From 276f0b1031afbf118e608a777f94e369b134db0e Mon Sep 17 00:00:00 2001 From: John Date: Fri, 1 Mar 2024 02:38:00 -0600 Subject: [PATCH] "libconlang": Remove libconlang --- Cargo.toml | 1 - libconlang/.gitignore | 1 - libconlang/Cargo.toml | 18 - libconlang/src/lib.rs | 5 - libconlang/src/resolver.rs | 911 ------------------------------------- 5 files changed, 936 deletions(-) delete mode 100644 libconlang/.gitignore delete mode 100644 libconlang/Cargo.toml delete mode 100644 libconlang/src/lib.rs delete mode 100644 libconlang/src/resolver.rs diff --git a/Cargo.toml b/Cargo.toml index 59fe784..07574ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,5 @@ [workspace] members = [ - "libconlang", "cl-repl", "cl-interpret", "cl-structures", diff --git a/libconlang/.gitignore b/libconlang/.gitignore deleted file mode 100644 index ea8c4bf..0000000 --- a/libconlang/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/libconlang/Cargo.toml b/libconlang/Cargo.toml deleted file mode 100644 index 143c33a..0000000 --- a/libconlang/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "conlang" -description = "The Conlang Programming Language" -keywords = ["interpreter", "programming", "language"] -authors.workspace = true -version.workspace = true -edition.workspace = true -license.workspace = true -publish.workspace = true -repository.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -unicode-xid = "0.2.4" -cl-ast = { path = "../cl-ast" } -cl-token = { path = "../cl-token" } -cl-structures = { path = "../cl-structures" } diff --git a/libconlang/src/lib.rs b/libconlang/src/lib.rs deleted file mode 100644 index 72396da..0000000 --- a/libconlang/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Conlang is an expression-based programming language with similarities to Rust and Python -#![warn(clippy::all)] -#![feature(decl_macro)] - -pub mod resolver; diff --git a/libconlang/src/resolver.rs b/libconlang/src/resolver.rs deleted file mode 100644 index 03401f5..0000000 --- a/libconlang/src/resolver.rs +++ /dev/null @@ -1,911 +0,0 @@ -//! Extremely early WIP of a static type-checker/resolver -//! -//! This will hopefully become a fully fledged static resolution pass in the future -use std::collections::HashMap; - -use scopeguard::Scoped; -pub mod scopeguard { - //! Implements a generic RAII scope-guard - - use std::ops::{Deref, DerefMut}; - - pub trait Scoped: Sized { - fn frame(&mut self) -> Guard { - Guard::new(self) - } - /// - fn enter_scope(&mut self); - fn exit_scope(&mut self); - } - - pub struct Guard<'scope, T: Scoped> { - inner: &'scope mut T, - } - impl<'scope, T: Scoped> Guard<'scope, T> { - pub fn new(inner: &'scope mut T) -> Self { - inner.enter_scope(); - Self { inner } - } - } - impl<'scope, T: Scoped> Deref for Guard<'scope, T> { - type Target = T; - fn deref(&self) -> &Self::Target { - self.inner - } - } - impl<'scope, T: Scoped> DerefMut for Guard<'scope, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.inner - } - } - impl<'scope, T: Scoped> Drop for Guard<'scope, T> { - fn drop(&mut self) { - self.inner.exit_scope() - } - } -} - -/// Prints like [println] if `debug_assertions` are enabled -macro debugln($($t:tt)*) { - if cfg!(debug_assertions) { - println!($($t)*); - } -} -macro debug($($t:tt)*) { - if cfg!(debug_assertions) { - print!($($t)*); - } -} - -use ty::Type; -pub mod ty { - //! Describes the type of a [Variable](super::Variable) - use std::fmt::Display; - - /// Describes the type of a [Variable](super::Variable) - #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] - pub enum Type { - #[default] - Empty, - Int, - Bool, - Char, - String, - Float, - Fn { - args: Vec, - ret: Box, - }, - Range(Box), - Tuple(Vec), - Never, - /// [Inferred](Type::Inferred) is for error messages. DO NOT CONSTRUCT - Inferred, - Generic(String), - /// A function with a single parameter of [Type::ManyInferred] - /// is assumed to always be correct. - ManyInferred, - } - impl Type { - fn is_empty(&self) -> bool { - self == &Type::Empty - } - } - impl Display for Type { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Type::Empty => "Empty".fmt(f), - Type::Int => "integer".fmt(f), - Type::Bool => "bool".fmt(f), - Type::Char => "char".fmt(f), - Type::String => "String".fmt(f), - Type::Float => "float".fmt(f), - // TODO: clean this up - Type::Fn { args, ret } => { - "fn (".fmt(f)?; - let mut args = args.iter(); - if let Some(arg) = args.next() { - arg.fmt(f)?; - } - for arg in args { - write!(f, ", {arg}")? - } - ")".fmt(f)?; - if !ret.is_empty() { - write!(f, " -> {ret}")?; - } - Ok(()) - } - Type::Range(t) => write!(f, "{t}..{t}"), - Type::Tuple(t) => { - "(".fmt(f)?; - for (idx, ty) in t.iter().enumerate() { - if idx > 0 { - ", ".fmt(f)?; - } - ty.fmt(f)?; - } - ")".fmt(f) - } - Type::Never => "!".fmt(f), - Type::Inferred => "_".fmt(f), - Type::Generic(name) => write!(f, "<{name}>"), - Type::ManyInferred => "..".fmt(f), - } - } - } -} - -/// Describes the life-cycle of a [Variable]: Whether it's bound, typed, or initialized -#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] -pub enum Status { - #[default] - Bound, - Uninitialized(Type), - Initialized(Type), -} -impl Status { - /// Performs type-checking for a [Variable] assignment - pub fn assign(&mut self, ty: &Type) -> TyResult<()> { - match self { - // Variable uninitialized: initialize it - Status::Bound => { - *self = Status::Initialized(ty.clone()); - Ok(()) - } - // Typecheck ok! Reuse the allocation for t - Status::Uninitialized(t) if t == ty => { - *self = Status::Initialized(std::mem::take(t)); - Ok(()) - } - Status::Initialized(t) if t == ty => Ok(()), - // Typecheck not ok. - Status::Uninitialized(e) | Status::Initialized(e) => { - Err(Error::TypeMismatch { want: ty.clone(), got: e.clone() }) - } - } - } -} -#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] -pub struct Variable { - /// The unique, global index of this variable - pub index: usize, - /// The [Status] of this variable - pub status: Status, - /// The mutability qualifier of this variable - pub mutable: bool, -} -impl Variable { - /// Constructs a new variable with the provided index and mutability - pub fn new(index: usize, mutable: bool) -> Self { - Self { index, mutable, ..Default::default() } - } - /// Performs a type-checked assignment on self - pub fn assign(&mut self, name: &str, ty: &Type) -> TyResult<()> { - let Variable { index, status, mutable } = self; - debug!("Typecheck for {name} #{index}: "); - let out = match (status, mutable) { - // Variable uninitialized: initialize it - (Status::Bound, _) => { - self.status = Status::Initialized(ty.clone()); - Ok(()) - } - // Typecheck ok! Reuse the allocation for t - (Status::Uninitialized(t), _) if t == ty => { - self.status = Status::Initialized(std::mem::take(t)); - Ok(()) - } - // Reassignment of mutable variable is ok - (Status::Initialized(t), true) if t == ty => Ok(()), - // Reassignment of immutable variable is not ok - (Status::Initialized(_), false) => Err(Error::ImmutableAssign(name.into(), *index)), - // Typecheck not ok. - (Status::Uninitialized(e) | Status::Initialized(e), _) => { - Err(Error::TypeMismatch { want: ty.clone(), got: e.clone() }) - } - }; - match &out { - Ok(_) => debugln!("Ok! ({ty})"), - Err(e) => debugln!("Error: {e:?}"), - } - out - } - /// Performs the type-checking for a modifying assignment - pub fn modify_assign(&self, name: &str, ty: &Type) -> TyResult<()> { - let Variable { index, status, mutable } = &self; - match (status, mutable) { - (Status::Initialized(t), true) if t == ty => Ok(()), - (Status::Initialized(t), true) => { - Err(Error::TypeMismatch { want: t.clone(), got: ty.clone() }) - } - (Status::Initialized(_), false) => Err(Error::ImmutableAssign(name.into(), *index)), - (..) => Err(Error::Uninitialized(name.into(), *index)), - } - } -} - -/* - THE BIG IDEA: - - Each `let` statement binds a *different* variable. - - Shadowing is a FEATURE - - Traversing the tree before program launch allows the Resolver to assign - an index to each variable usage in the scope-tree - - These indices allow constant-time variable lookup in the interpreter!!! - - The added type-checking means fewer type errors! - - REQUIREMENTS FOR FULL TYPE-CHECKING: - - Meaningful type expressions in function declarations - - NECESSARY CONSIDERATIONS: - - Variable binding happens AFTER the initialization expression is run. - - If a variable is *entirely* unbound before being referenced, - it'll still error. - - This is *intentional*, and ALLOWS shadowing previous variables. - - In my experience, this is almost NEVER an error :P -*/ - -#[derive(Clone, Debug, Default)] -pub struct Scope { - /// A monotonically increasing counter of variable declarations - count: usize, - /// A dictionary keeping track of type and lifetime information - vars: HashMap, -} -impl Scope { - /// Bind a [Variable] in Scope - pub fn insert(&mut self, name: &str, index: usize, mutable: bool) { - self.count += 1; - self.vars - .insert(name.to_string(), Variable::new(index, mutable)); - } - /// Returns a reference to a [Variable], if `name` is bound - pub fn get(&self, name: &str) -> Option<&Variable> { - self.vars.get(name) - } - /// Returns a mutable reference to a [Variable], if `name` is bound - pub fn get_mut(&mut self, name: &str) -> Option<&mut Variable> { - self.vars.get_mut(name) - } -} -/// Implements a dynamically scoped namespace -#[derive(Clone, Debug, Default)] -pub struct Module { - modules: HashMap, - vars: HashMap, -} -impl Module { - pub fn insert_var(&mut self, name: &str, index: usize, mutable: bool) -> TyResult<()> { - if self - .vars - .insert(name.into(), Variable::new(index, mutable)) - .is_some() - { - Err(Error::NonUniqueInModule(name.into()))?; - } - Ok(()) - } - pub fn insert_module(&mut self, name: String, module: Module) -> TyResult<()> { - if self.modules.insert(name.clone(), module).is_some() { - Err(Error::NonUniqueInModule(name + "(module)"))? - } - Ok(()) - } - /// Returns a reference to a [Variable] in this Module, if `name` is bound - pub fn get(&self, name: &str) -> Option<&Variable> { - self.vars.get(name) - } - /// Returns a mutable reference to a [Variable] in this Module, if `name` is bound - pub fn get_mut(&mut self, name: &str) -> Option<&mut Variable> { - self.vars.get_mut(name) - } - - pub fn resolve_get(&self, name: &str, path: &[String]) -> Option<&Variable> { - if path.is_empty() { - return self.get(name); - } - let module = self.modules.get(&path[0])?; - module - .resolve_get(name, &path[1..]) - .or_else(|| self.get(name)) - } - // Returns a reference to the module at a specified path - pub fn resolve(&self, path: &[String]) -> TyResult<&Module> { - if path.is_empty() { - return Ok(self); - } - let module = self - .modules - .get(&path[0]) - .ok_or_else(|| Error::Unbound(path[0].clone()))?; - module.resolve(&path[1..]) - } - /// Returns a mutable reference to a Module if one is bound - pub fn resolve_mut(&mut self, path: &[String]) -> TyResult<&mut Module> { - if path.is_empty() { - return Ok(self); - } - let module = self - .modules - .get_mut(&path[0]) - .ok_or_else(|| Error::Unbound(path[0].clone()))?; - module.resolve_mut(&path[1..]) - } -} - -#[derive(Clone, Debug)] -pub struct Resolver { - /// A monotonically increasing counter of variable declarations - count: usize, - /// A stack of nested scopes *inside* a function - scopes: Vec, - /// A stack of nested scopes *outside* a function - // TODO: Record the name of the module, and keep a stack of the current module - // for name resolution - modules: Module, - /// Describes the current path - module: Vec, - /// A stack of types - types: Vec, -} - -impl Scoped for Resolver { - fn enter_scope(&mut self) { - self.enter_scope(); - } - fn exit_scope(&mut self) { - self.exit_scope(); - } -} - -impl Default for Resolver { - fn default() -> Self { - let mut new = Self { - count: Default::default(), - scopes: vec![Default::default()], - modules: Default::default(), - module: Default::default(), - types: Default::default(), - }; - new.register_builtin("print", &[], &[Type::ManyInferred], Type::Empty) - .expect("print should not be bound in new Resolver"); - new - } -} - -impl Resolver { - pub fn new() -> Self { - Default::default() - } - /// Register a built-in function into the top-level module - pub fn register_builtin( - &mut self, - name: &str, - path: &[String], - args: &[Type], - ret: Type, - ) -> TyResult<()> { - let module = self.modules.resolve_mut(path)?; - module.vars.insert( - name.into(), - Variable { - index: 0, - status: Status::Initialized(Type::Fn { args: args.into(), ret: ret.into() }), - mutable: false, - }, - ); - Ok(()) - } - /// Enters a Module Scope - pub fn enter_module(&mut self, name: &str) -> TyResult<()> { - let module = self.modules.resolve_mut(&self.module)?; - module.insert_module(name.into(), Default::default())?; - self.module.push(name.into()); - Ok(()) - } - /// Exits a Module Scope - pub fn exit_module(&mut self) -> Option { - // Modules stay registered - self.module.pop() - } - /// Enters a Block Scope - pub fn enter_scope(&mut self) { - self.scopes.push(Default::default()); - } - /// Exits a Block Scope, returning the value - pub fn exit_scope(&mut self) -> Option { - self.scopes.pop().map(|scope| scope.count) - } - /// Resolves a name to a [Variable] - pub fn get(&self, name: &str) -> TyResult<&Variable> { - if let Some(var) = self.scopes.iter().rev().find_map(|s| s.get(name)) { - return Ok(var); - } - self.modules - .resolve_get(name, &self.module) - .ok_or_else(|| Error::Unbound(name.into())) - } - /// Mutably resolves a name to a [Variable] - pub fn get_mut(&mut self, name: &str) -> TyResult<&mut Variable> { - if let Some(var) = self.scopes.iter_mut().rev().find_map(|s| s.get_mut(name)) { - return Ok(var); - } - self.modules - .resolve_mut(&self.module)? - .get_mut(name) - .ok_or_else(|| Error::Unbound(name.into())) - } - /// Binds a name in the current lexical scope - pub fn insert_scope(&mut self, name: &str, mutable: bool) -> TyResult { - self.count += 1; - self.scopes - .last_mut() - .ok_or_else(|| panic!("Stack underflow in resolver?"))? - .insert(name, self.count, mutable); - Ok(self.count) - } - /// Binds a name in the current module - pub fn insert_module(&mut self, name: &str, mutable: bool) -> TyResult { - self.count += 1; - self.modules - .resolve_mut(&self.module)? - .insert_var(name, self.count, mutable)?; - Ok(self.count) - } - /// Performs scoped type-checking of variables - pub fn assign(&mut self, name: &str, ty: &Type) -> TyResult<()> { - self.get_mut(name)?.assign(name, ty) - } -} -#[allow(unused_macros)] -/// Manages a module scope -/// ```rust,ignore -/// macro module(self, name: &str, inner: {...}) -> Result<_, Error> -/// ``` -macro module($self:ident, $name:tt, $inner:tt) {{ - $self.enter_module($name)?; - #[allow(clippy::redundant_closure_call)] - let scope = (|| $inner)(); // This is pretty gross, but hey, try {} syntax is unstable too - $self.exit_module(); - scope -}} - -impl Resolver { - pub fn visit_empty(&mut self) -> TyResult<()> { - debugln!("Got Empty"); - self.types.push(Type::Empty); - Ok(()) - } -} - -pub trait Resolve { - /// Performs variable resolution on self, and returns the type of self. - /// - /// For expressions, this is the type of the expression. - /// - /// For declarations, this is Empty. - fn resolve(&mut self, _resolver: &mut Resolver) -> TyResult { - Ok(Type::Empty) - } -} -mod ast1 { - // #![allow(deprecated)] - // use super::*; - // use crate::ast::preamble::*; - // impl Resolve for Start { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let Self(program) = self; - // program.resolve(resolver) - // } - // } - // impl Resolve for Program { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let Self(module) = self; - // for decl in module { - // decl.resolve(resolver)?; - // } - // // TODO: record the number of module-level assignments into the AST - // Ok(Type::Empty) - // } - // } - // impl Resolve for Stmt { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // match self { - // Stmt::Let(value) => value.resolve(resolver), - // Stmt::Fn(value) => value.resolve(resolver), - // Stmt::Expr(value) => value.resolve(resolver), - // } - // } - // } - // impl Resolve for Let { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let Let { name: Name { symbol: Identifier { name, index }, mutable, ty: _ }, init } = - // self; - // debugln!("ty> let {name} ..."); - // if let Some(init) = init { - // let ty = init.resolve(resolver)?; - // *index = Some(resolver.insert_scope(name, *mutable)?); - // resolver.get_mut(name)?.assign(name, &ty)?; - // } else { - // resolver.insert_scope(name, *mutable)?; - // } - // Ok(Type::Empty) - // } - // } - // impl Resolve for FnDecl { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let FnDecl { name: Name { symbol: Identifier { name, index }, .. }, args, body } = - // self; debugln!("ty> fn {name} ..."); - // // register the name at module scope - // *index = Some(resolver.insert_module(name, false)?); - // // create a new lexical scope - // let scopes = std::mem::take(&mut resolver.scopes); - // // type-check the function body - // let out = { - // let mut resolver = resolver.frame(); - // let mut evaluated_args = vec![]; - // for arg in args { - // evaluated_args.push(arg.resolve(&mut resolver)?) - // } - // let fn_decl = Type::Fn { args: evaluated_args.clone(), ret: Box::new(Type::Empty) - // }; resolver.get_mut(name)?.assign(name, &fn_decl)?; - // module!(resolver, name, { body.resolve(&mut resolver) }) - // }; - // let _ = std::mem::replace(&mut resolver.scopes, scopes); - // out - // } - // } - // impl Resolve for Name { - // fn resolve(&mut self, _resolver: &mut Resolver) -> TyResult { - // Ok(Type::Empty) - // } - // } - // impl Resolve for Block { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let Block { let_count: _, statements, expr } = self; - // let mut resolver = resolver.frame(); - // for stmt in statements { - // stmt.resolve(&mut resolver)?; - // } - // expr.resolve(&mut resolver) - // } - // } - // impl Resolve for Expr { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let Expr(expr) = self; - // expr.resolve(resolver) - // } - // } - - // impl Resolve for Operation { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // match self { - // Operation::Assign(value) => value.resolve(resolver), - // Operation::Binary(value) => value.resolve(resolver), - // Operation::Unary(value) => value.resolve(resolver), - // Operation::Call(value) => value.resolve(resolver), - // } - // } - // } - // impl Resolve for Assign { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let Assign { target, operator, init } = self; - // // Evaluate the initializer expression - // let ty = init.resolve(resolver)?; - // // Resolve the variable - // match (operator, resolver.get_mut(&target.name)?) { - // ( - // operator::Assign::Assign, - // Variable { status: Status::Initialized(_), mutable: false, index }, - // ) => Err(Error::ImmutableAssign(target.name.clone(), *index)), - // // TODO: make typing more expressive for modifying assignment - // (_, variable) => variable - // .modify_assign(&target.name, &ty) - // .map(|_| Type::Empty), - // } - // } - // } - - // impl Resolve for Binary { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let Binary { first, other } = self; - - // let mut first = first.resolve(resolver)?; - // for (op, other) in other { - // let other = other.resolve(resolver)?; - // first = resolver.resolve_binary_operator(first, other, op)?; - // } - // Ok(first) - // } - // } - // impl Resolve for Unary { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let Unary { operators, operand } = self; - // let mut operand = operand.resolve(resolver)?; - // for op in operators { - // operand = resolver.resolve_unary_operator(operand, op)?; - // } - // Ok(operand) - // } - // } - // /// Resolve [operator]s - // impl Resolver { - // fn resolve_binary_operator( - // &mut self, - // first: Type, - // other: Type, - // op: &operator::Binary, - // ) -> TyResult { - // // TODO: check type compatibility for binary ops - // // TODO: desugar binary ops into function calls, when member functions are a thing - // eprintln!("Resolve binary operators {first} {op:?} {other}"); - // if first != other { - // Err(Error::TypeMismatch { want: first, got: other }) - // } else { - // Ok(first) - // } - // } - // fn resolve_unary_operator( - // &mut self, - // operand: Type, - // op: &operator::Unary, - // ) -> TyResult { - // // TODO: Allow more expressive unary operator type conversions - // todo!("Resolve unary operators {op:?} {operand}") - // } - // } - // impl Resolve for Call { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // match self { - // Call::FnCall(value) => value.resolve(resolver), - // Call::Primary(value) => value.resolve(resolver), - // } - // } - // } - // impl Resolve for FnCall { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let FnCall { callee, args } = self; - // let mut callee = callee.resolve(resolver)?; - // for argset in args { - // // arguments should always be a tuple here - // let arguments = argset.resolve(resolver)?; - // let Type::Tuple(arguments) = arguments else { - // Err(Error::TypeMismatch { - // want: Type::Tuple(vec![Type::ManyInferred]), - // got: arguments, - // })? - // }; - // // Verify that the callee is a function, and the arguments match. - // // We need the arguments - // let Type::Fn { args, ret } = callee else { - // return Err(Error::TypeMismatch { - // want: Type::Fn { args: arguments, ret: Type::Inferred.into() }, - // got: callee, - // })?; - // }; - // for (want, got) in args.iter().zip(&arguments) { - // // TODO: verify generics - // if let Type::Generic(_) = want { - // continue; - // } - // if want != got { - // return Err(Error::TypeMismatch { - // want: Type::Fn { args: arguments, ret: Type::Inferred.into() }, - // got: Type::Fn { args, ret }, - // })?; - // } - // } - // callee = *ret; - // } - // Ok(callee) - // } - // } - // impl Resolve for Primary { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // match self { - // Primary::Identifier(value) => value.resolve(resolver), - // Primary::Literal(value) => value.resolve(resolver), - // Primary::Block(value) => value.resolve(resolver), - // Primary::Group(value) => value.resolve(resolver), - // Primary::Branch(value) => value.resolve(resolver), - // } - // } - // } - - // impl Resolve for Group { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // match self { - // Group::Tuple(tuple) => tuple.resolve(resolver), - // Group::Single(expr) => expr.resolve(resolver), - // Group::Empty => Ok(Type::Empty), - // } - // } - // } - - // impl Resolve for Tuple { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let Tuple { elements } = self; - // let mut types = vec![]; - // for expr in elements.iter_mut() { - // types.push(expr.resolve(resolver)?); - // } - // Ok(Type::Tuple(types)) - // } - // } - - // impl Resolve for Identifier { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let Identifier { name, index: id_index } = self; - // let Variable { index, status, .. } = resolver.get(name)?; - // *id_index = Some(*index); - // let ty = match status { - // Status::Initialized(t) => t, - // _ => Err(Error::Uninitialized(name.to_owned(), *index))?, - // }; - // debugln!("ty> Resolved {} #{index}: {ty}", name); - // Ok(ty.to_owned()) - // } - // } - // impl Resolve for Literal { - // fn resolve(&mut self, _resolver: &mut Resolver) -> TyResult { - // Ok(match self { - // Literal::String(_) => Type::String, - // Literal::Char(_) => Type::Char, - // Literal::Bool(_) => Type::Bool, - // Literal::Float(_) => Type::Float, - // Literal::Int(_) => Type::Int, - // }) - // } - // } - - // impl Resolve for Flow { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // // TODO: Finish this - // match self { - // Flow::While(value) => value.resolve(resolver), - // Flow::If(value) => value.resolve(resolver), - // Flow::For(value) => value.resolve(resolver), - // Flow::Continue(value) => value.resolve(resolver), - // Flow::Return(value) => value.resolve(resolver), - // Flow::Break(value) => value.resolve(resolver), - // } - // } - // } - // impl Resolve for While { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // // TODO: Finish this - // // Visit else first, save that to a break-pattern stack in the Resolver, - // // and check it inside Break::resolve() - // let While { cond, body, else_ } = self; - // cond.resolve(resolver)?; // must be Type::Bool - // body.resolve(resolver)?; // discard - // else_.resolve(resolver) // compare with returns inside body - // } - // } - // impl Resolve for If { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let If { cond, body, else_ } = self; - // let cond = cond.resolve(resolver)?; - // if Type::Bool != cond { - // return Err(Error::TypeMismatch { want: Type::Bool, got: cond }); - // } - // let body_ty = body.resolve(resolver)?; - // let else_ty = else_.resolve(resolver)?; - // if body_ty == else_ty { - // Ok(body_ty) - // } else { - // Err(Error::TypeMismatch { want: body_ty, got: else_ty }) - // } - // } - // } - - // impl Resolve for For { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let For { var: Identifier { name, index }, iter, body, else_ } = self; - // debugln!("> for {name} in ..."); - // // Visit the iter expression and get its type - // let range = iter.resolve(resolver)?; - // let ty = match range { - // Type::Range(t) => t, - // got => Err(Error::TypeMismatch { want: Type::Range(Type::Inferred.into()), got - // })?, }; - // let body_ty = { - // let mut resolver = resolver.frame(); - // // bind the variable in the loop scope - // *index = Some(resolver.insert_scope(name, false)?); - // resolver.get_mut(name)?.assign(name, &ty)?; - // body.resolve(&mut resolver) - // }?; - // // visit the else block - // let else_ty = else_.resolve(resolver)?; - // if body_ty != else_ty { - // Err(Error::TypeMismatch { want: body_ty, got: else_ty }) - // } else { - // Ok(body_ty) - // } - // } - // } - // impl Resolve for Else { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // let Else { expr } = self; - // expr.resolve(resolver) - // } - // } - - // impl Resolve for Continue { - // fn resolve(&mut self, _resolver: &mut Resolver) -> TyResult { - // // TODO: Finish control flow - // Ok(Type::Never) - // } - // } - // impl Resolve for Break { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // // TODO: Finish control flow - // let Break { expr } = self; - // expr.resolve(resolver) - // } - // } - // impl Resolve for Return { - // fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - // // TODO: Finish control flow - // let Return { expr } = self; - // expr.resolve(resolver) - // } - // } -} - -// heakc yea man, generics -impl Resolve for Option { - fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - match self { - Some(t) => t.resolve(resolver), - None => Ok(Type::Empty), - } - } -} -impl Resolve for Box { - fn resolve(&mut self, resolver: &mut Resolver) -> TyResult { - self.as_mut().resolve(resolver) - } -} - -use error::{Error, TyResult}; -pub mod error { - use super::Type; - use std::fmt::Display; - - pub type TyResult = Result; - - impl std::error::Error for Error {} - #[derive(Clone, Debug)] - pub enum Error { - StackUnderflow, - // types - TypeMismatch { want: Type, got: Type }, - // modules - NonUniqueInModule(String), - // lifetimes - Uninitialized(String, usize), - ImmutableAssign(String, usize), - Unbound(String), - } - impl Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Error::StackUnderflow => "Stack underflow in Resolver".fmt(f), - Error::TypeMismatch { want, got } => { - write!(f, "Type error: {want} != {got}") - } - Error::ImmutableAssign(name, index) => { - write!(f, "Cannot mutate immutable variable {name}(#{index})") - } - Error::Uninitialized(name, index) => { - write!(f, "{name}(#{index}) was accessed before initialization") - } - Error::Unbound(name) => write!(f, "{name} not bound before use."), - Error::NonUniqueInModule(name) => { - write!(f, "Name {name} not unique at module scope!") - } - } - } - } -}