plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
plum-checker/src/types.rs
use std::collections::BTreeMap;
#[derive(Debug, Clone, PartialEq)]
pub enum PlumType {
TInt,
TFloat,
TBool,
TStr,
TByte,
/// `[]Byte` — a fixed builtin, not a monomorphized generic. The checker
/// only ever produces this for the exact source spelling `[]Byte`
/// (see `plumTypeFromName`); there is no general `[]T` for other `T`.
TByteSlice,
TUnit,
TVar(String),
TFun(Vec<PlumType>, Box<PlumType>),
TNamed(String),
/// The type of a variadic parameter, e.g. `...Int` -> `TVariadic(TInt)`.
/// Appears in exactly two places: as the trailing entry of a `TFun`'s
/// param-types list (call-site arity/type checking), and as the type bound
/// to the param's name inside the function body. Its only legal use inside
/// a body is as a `for` loop's iterable — no other `unify`/`inferExpr` arm
/// handles it, so any other use is a type error by construction.
TVariadic(Box<PlumType>),
}
impl std::fmt::Display for PlumType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PlumType::TInt => write!(f, "Int"),
PlumType::TFloat => write!(f, "Float"),
PlumType::TBool => write!(f, "Bool"),
PlumType::TStr => write!(f, "Str"),
PlumType::TByte => write!(f, "Byte"),
PlumType::TByteSlice => write!(f, "[]Byte"),
PlumType::TUnit => write!(f, "Unit"),
PlumType::TVar(n) => write!(f, "{}", n),
PlumType::TFun(ps, r) => {
let ps_str: Vec<_> = ps.iter().map(|p| p.to_string()).collect();
write!(f, "({}) -> {}", ps_str.join(", "), r)
}
PlumType::TNamed(n) => write!(f, "{}", n),
PlumType::TVariadic(inner) => write!(f, "...{}", inner),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TypeScheme {
pub vars: Vec<String>,
pub body: Box<PlumType>,
}
impl TypeScheme {
pub fn mono(t: PlumType) -> Self {
TypeScheme { vars: vec![], body: Box::new(t) }
}
}
pub type TypeEnv = BTreeMap<String, TypeScheme>;
pub struct InferState {
pub counter: u64,
}
impl InferState {
pub fn new() -> Self {
InferState { counter: 0 }
}
pub fn freshVar(&mut self) -> String {
let name = format!("a{}", self.counter);
self.counter += 1;
name
}
pub fn freshType(&mut self) -> PlumType {
PlumType::TVar(self.freshVar())
}
}
#[derive(Debug, Clone)]
pub struct CheckError {
pub message: String,
}
impl std::fmt::Display for CheckError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
pub type CheckResult<T> = Result<T, Vec<CheckError>>;