# HG changeset patch # User Simon Heath # Date 1737066217 18000 # Thu Jan 16 17:23:37 2025 -0500 # Branch devel # Node ID c941fa06d4c5a2f07fd78597f8bec4b4a700987d # Parent 3b4c96fa40a9347f39bba4b613950fb53ddf55a5 Ok I'm still procrastinating about de bruijn indices. Not sure what to do about this. diff --git a/src/hir2/lower.rs b/src/hir2/lower.rs --- a/src/hir2/lower.rs +++ b/src/hir2/lower.rs @@ -686,10 +686,20 @@ } } +/// Ok this does the actual debruijn-ification of indices. +/// I'm putting way, way too much work into avoiding the traditional +/// shifting construction, so here's how it works. +/// +/// When it descends into an AST fragment, it counts how many variables are +/// declared inside it. It then saves that number and when it runs into +/// a var decl, uses that value as the db index and decrements it. +/// +/// This... still involves a lot of walking up and down the AST trees, but +/// fuck it. #[derive(Default)] struct IndexRenamer { next_var_idx: usize, - symtbl: crate::symtbl::Symtbl, + symtbl: crate::simptbl::Symtbl, } impl super::visit::Visit for IndexRenamer { @@ -706,7 +716,10 @@ } fn visit_let(&mut self, varname: Sym, typename: &Type, init: &ENode, mutable: bool) -> Expr { - todo!(); + let idx = self.next_var_idx; + self.next_var_idx -= 1; + self.symtbl.add_binding(varname, Idx(idx)); + super::visit::walk_let(self, varname, typename, init, mutable) } } @@ -715,7 +728,8 @@ /// all our Idx's have been filled in to something other than Idx::invalid(). /// /// TODO: Check to make sure they resolve to valid values in the symtbl, instead of -/// just +/// just not being invalid's +#[derive(Default)] struct IndexChecker {} impl super::visit::Visit for IndexChecker { @@ -740,9 +754,7 @@ lower_decl(&mut ir, d) } - let mut resolver = IndexResolver::default(); use super::visit::Visit; - let ir = resolver.visit(&ir); let mut checker = IndexChecker {}; checker.visit(&ir) } @@ -751,8 +763,11 @@ mod tests { use super::*; use crate::ast::Expr as A; + // AN = AST node use crate::ast::ExprNode as AN; + // EN = expr node use crate::hir2::ENode as EN; + // ...not sure why I called this I. use crate::hir2::Expr as I; /// Does `return;` turn into `return ();`? @@ -919,8 +934,10 @@ let ir = lower(&ast); let mut ctr = IndexCounter::default(); ctr.visit(&ir); + let mut checker = IndexChecker::default(); assert_eq!(ctr.vars, varcount); assert_eq!(ctr.type_vars, typevarcount); + checker.visit(&ir); } } diff --git a/src/lib.rs b/src/lib.rs --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ pub mod nbe; pub mod parser; pub mod passes; +pub mod simptbl; pub mod symtbl; pub mod typeck; pub mod types; diff --git a/src/symtbl.rs b/src/simptbl.rs copy from src/symtbl.rs copy to src/simptbl.rs --- a/src/symtbl.rs +++ b/src/simptbl.rs @@ -1,83 +1,58 @@ //! Symbol table. //! -//! Basically we need to make the symtable persistent, -//! so we only need to figure out all meta-info about it -//! once instead of needing to walk through scopes multiple -//! times and then throw the scope information away when -//! we're done with it. +//! This is a little jank right now 'cause it *started* as a way +//! to do dedicated alpha-renaming and not much else, and then I +//! found out that alpha-renaming sucks when doing substitution and +//! had to end up repurposing this for other things to. So I ripped its +//! guts out and made *the simplest damn thing* that could possibly work. +//! +//! Just use this as a piece to build other more complicated operations atop. +use im::OrdMap; use std::cell::RefCell; -use std::rc::Rc; -use anymap::Map; - -use crate::types::*; use crate::*; -/// A symbol that has been renamed to be globally unique. -/// So basically instead of `foo` being scoped, it gets -/// renamed to `foo_1` where every mention of `foo_1` -/// refers to the same value. -/// -/// TODO: The typeck code needs to construct/retrieve its own -/// UniqueSym's, which is why this is public. It's a bit of -/// a pickle though, since really the goal is to have these -/// make non-unique syms unrepresentable. -/// -/// But after the alpha-renaming pass all Sym's are unique already -/// anyway, soooooo... idk. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct UniqueSym(pub Sym); +#[derive(Clone, Debug)] +struct ScopeFrame { + contents: OrdMap, +} -impl Default for UniqueSym { +/// This can't be a derive 'cause the generic +/// make shit squirrelly. +impl Default for ScopeFrame { fn default() -> Self { - unreachable!( - "can't happen, but we want to be able to default-construct containers containing this" - ) + Self { + contents: OrdMap::default(), + } } } -#[derive(Clone, Default, Debug)] -struct ScopeFrame { - symbols: BTreeMap, - types: BTreeMap, -} - -/// A shortcut for a cloneable AnyMap -type CloneMap = Map; - -/// Basic symbol table that maps names to type ID's +/// Basic symbol table that maps names to random data ID's /// and manages scope. /// Looks ugly, works well. +/// +/// The im::OrdMap is probably unnecessary but it makes me happy. #[derive(Debug, Clone)] -pub struct Symtbl { - frames: Rc>>>, - /// A mapping from unique symbol names to whatever - /// we need to know about them. It's an AnyMap, so - /// we can just stuff whatever data we need into it. - unique_symbols: BTreeMap, - - /// Same as unique_symbols but for types. - unique_types: BTreeMap, +pub struct Symtbl { + frames: RefCell>>, } -impl Default for Symtbl { +impl Default for Symtbl { /// We start with an empty toplevel scope existing, /// then add some builtin's to it. fn default() -> Self { Self { - frames: Rc::new(RefCell::new(vec![ScopeFrame::default()])), - unique_symbols: Default::default(), - unique_types: Default::default(), + frames: RefCell::new(vec![ScopeFrame::default()]), } } } -pub struct ScopeGuard { - scope: Symtbl, +pub struct ScopeGuard { + scope: Symtbl, } -impl Drop for ScopeGuard { +impl Drop for ScopeGuard { fn drop(&mut self) { self.scope .frames @@ -87,60 +62,33 @@ } } -impl Symtbl { - fn add_builtins(&mut self) { - for builtin in &*builtins::BUILTINS { - let _new = self.new_unchanged_symbol(builtin.name); - } - // Add another builtin called "main" so the main function - // doesn't get renamed. - self.new_unchanged_symbol(Sym::new("main")); - } - - /// Creates a new symbol with the name of the given one - /// and a unique numerical suffix. - fn gensym(&mut self, base: Sym) -> UniqueSym { - let sym = crate::INT.gensym_sym(base); - UniqueSym(sym) - } - - pub fn push_scope(&self) -> ScopeGuard { +impl Symtbl +where + Val: Clone, +{ + pub fn push_scope(&self) -> ScopeGuard { self.frames.borrow_mut().push(ScopeFrame::default()); - // TODO: This clone is a little cursed 'cause - // it'll clone the whole `unique_symbols` map, - // We should probably just wrap the whole symtbl in - // a Rc> intead of just the scopeframe. + // TODO: This clones the whole Vec, we could + // probably do better with our persistant map. + // But it also probably doesn't matter since the vec is + // usually small. ScopeGuard { scope: self.clone(), } } - /// Returns the UniqueSym that the given Sym is *currently* - /// bound to, or None if DNE - fn get_binding(&self, sym: Sym) -> Option { - for scope in self.frames.borrow().iter().rev() { - let v = scope.symbols.get(&sym).cloned(); - if v.is_some() { - return v; - } - } - None + /// Get a reference to the symbol most recently bound + /// with that name, or panic if it does not exist + pub fn get_binding(&self, sym: Sym) -> Val { + let msg = format!("Symbol {} is not bound!", sym); + self.try_get_binding(sym).expect(&msg) } - /// Get a reference to the symbol most recently bound - /// with that name, or panic if it does not exist - /// - /// ...I kinda miss Elixir's convention of having - /// a function `foo` return a result and `foo!` panic - /// on error. - fn really_get_binding(&self, sym: Sym) -> UniqueSym { - let msg = format!("Symbol {} is not bound!", sym); - self.get_binding(sym).expect(&msg) - } - - fn get_type_binding(&self, sym: Sym) -> Option { + /// Returns the UniqueSym that the given Sym is *currently* + /// bound to, or None if DNE + pub fn try_get_binding(&self, sym: Sym) -> Option { for scope in self.frames.borrow().iter().rev() { - let v = scope.types.get(&sym).cloned(); + let v = scope.contents.get(&sym).cloned(); if v.is_some() { return v; } @@ -148,559 +96,19 @@ None } - fn really_get_type_binding(&self, sym: Sym) -> UniqueSym { - let msg = format!("Type {} is not bound!", sym); - self.get_type_binding(sym).expect(&msg) - } - - /// Get the specified info for the given UniqueSym, or - /// None if DNE - pub fn get_info(&self, sym: UniqueSym) -> Option<&T> - where - T: anymap::any::CloneAny, - { - self.unique_symbols - .get(&sym) - .and_then(|anymap| anymap.get::()) - } - - /// Adds the given info struct to the symbol, or - /// panics if it already exists. - pub fn put_info(&mut self, sym: UniqueSym, info: T) - where - T: anymap::any::CloneAny, - { - self.unique_symbols - .get_mut(&sym) - .and_then(|anymap| anymap.insert(info)); - } - - pub fn type_exists(&self, sym: Sym) -> bool { - if sym == "Tuple" { - return true; - } - let thing = self.unique_types.get(&UniqueSym(sym)); - // fuck combinators - match thing { - Some(_anymap) => true, - None => false, - } - } - - fn get_type_info(&self, sym: UniqueSym) -> Option<&T> - where - T: anymap::any::CloneAny, - { - // dbg!(&self.unique_types); - self.unique_types - .get(&sym) - .and_then(|anymap| anymap.get::()) - } - - /// BUGGO: See discussion on UniqueSym type - pub fn get_type_info2(&self, sym: Sym) -> Option<&T> - where - T: anymap::any::CloneAny, - { - self.get_type_info(UniqueSym(sym)) - } - - /// Adds the given type struct to the symbol, or - /// panics if it already exists. - fn put_type_info(&mut self, sym: UniqueSym, info: T) - where - T: anymap::any::CloneAny + std::fmt::Debug, - { - let anymap = self.unique_types.entry(sym).or_insert_with(CloneMap::new); - // slightly weird error message formatting 'cause we apparently can't - // clone `info` easily??? - let info_dbg = format!("{:?}", info); - if let Some(x) = anymap.insert(info) { - panic!( - "Type info for {} already exists: {:?}\nAttempting to replace it with: {}", - sym.0, x, info_dbg - ); - } - } - /// BUGGO: same as get_type_info2() - pub fn put_type_info2(&mut self, sym: Sym, info: T) - where - T: anymap::any::CloneAny + std::fmt::Debug, - { - self.put_type_info(UniqueSym(sym), info) - } - - fn binding_exists(&self, sym: Sym) -> bool { - self.get_binding(sym).is_some() - } - - fn _unique_exists(&self, sym: UniqueSym) -> bool { - self.unique_symbols.get(&sym).is_some() + pub fn binding_exists(&self, sym: Sym) -> bool { + self.try_get_binding(sym).is_some() } /// Takes a symbol, generates a new UniqueSym for it, /// and stuffs it into the topmost scope, overwriting /// any symbol of the same name already there. - fn bind_new_symbol(&mut self, sym: Sym) -> UniqueSym { - let newsym = self.gensym(sym); - self.frames - .borrow_mut() - .last_mut() - .expect("Scope stack underflow") - .symbols - .insert(sym, newsym); - self.unique_symbols.insert(newsym, Map::new()); - newsym - } - - /// Takes a name and generates a new type param name for it - pub(crate) fn bind_new_type(&mut self, sym: Sym) -> UniqueSym { - let newsym = self.gensym(sym); + pub fn add_binding(&mut self, sym: Sym, vl: Val) { self.frames .borrow_mut() .last_mut() .expect("Scope stack underflow") - .types - .insert(sym, newsym); - self.unique_types.insert(newsym, Map::new()); - newsym - } - - /// Introduce a new symbol, or panic if that name is already - /// defined (ie, it is a name conflict, like two functions with - /// the same name) - fn new_unique_symbol(&mut self, sym: Sym) -> UniqueSym { - if self.binding_exists(sym) { - panic!("Attempting to create duplicate symbol {}", sym); - } else { - self.bind_new_symbol(sym) - } - } - - /// Introduce a new symbol as long as it doesn't clash with one - /// currently in the top scope. - fn _new_local_symbol(&mut self, sym: Sym) -> UniqueSym { - // Does the symbol exist in the topmost scope? - if self - .frames - .borrow_mut() - .last_mut() - .expect("Scope stack underflow") - .symbols - .contains_key(&sym) - { - panic!("Attempting to create duplicate global symbol {}", sym) - } else { - self.bind_new_symbol(sym) - } - } - - /// Insert a new symbol that is actually identical to the given one, - fn new_unchanged_symbol(&mut self, sym: Sym) -> UniqueSym { - if self.binding_exists(sym) { - panic!("Attempting to create duplicate symbol {}", sym); - } else { - let new_sym = UniqueSym(sym); - self.frames - .borrow_mut() - .last_mut() - .expect("Scope stack underflow") - .symbols - .insert(sym, new_sym); - self.unique_symbols.insert(new_sym, Map::new()); - new_sym - } - } - - fn rename_types(&mut self, tys: Vec) -> Vec { - tys.into_iter().map(|t| self.rename_type(t)).collect() - } - - /// Replace any generic names in the type with unique ones - fn rename_type(&mut self, ty: Type) -> Type { - use crate::types::Type::*; - match ty { - Named(nm, type_params) if &*nm.val() == "Tuple" => { - let new_type_params = self.rename_types(type_params); - Named(nm, new_type_params) - } - Named(nm, type_params) => { - let nm = self - .get_type_binding(nm) - .unwrap_or_else(|| panic!("Could not find declared type {}", nm)) - .0; - let _guard = self.push_scope(); - let new_type_params = self.rename_types(type_params); - Named(nm, new_type_params) - } - Func(args, rettype, type_params) => { - let _guard = self.push_scope(); - let new_args = self.rename_types(args); - let new_rettype = Box::new(self.rename_type(*rettype)); - let new_type_params = self.rename_types(type_params); - Func(new_args, new_rettype, new_type_params) - } - Struct(body, type_params) => { - let _guard = self.push_scope(); - let new_type_params = self.rename_types(type_params); - let new_body = body - .into_iter() - .map(|(nm, ty)| (nm, self.rename_type(ty))) - .collect(); - Struct(new_body, new_type_params) - } - Sum(body, type_params) => { - let _guard = self.push_scope(); - let new_type_params = self.rename_types(type_params); - let new_body = body - .into_iter() - .map(|(nm, ty)| (nm, self.rename_type(ty))) - .collect(); - Sum(new_body, new_type_params) - } - Array(t, size) => Array(Box::new(self.rename_type(*t)), size), - Uniq(inner) => Uniq(Box::new(self.rename_type(*inner))), - - // all these have no subtypes - Prim(_) => ty, - Enum(_) => ty, - _ => todo!(), - } - } - - fn rename_exprs(&mut self, exprs: Vec) -> Vec { - trace!("Handling exprs"); - exprs.into_iter().map(|e| self.rename_expr(e)).collect() - } - - /// TODO: These can't *quite* be part of the Pass framework, - /// because we need to handle different parts of the expr - /// in different orders depending on what it is, pushing and - /// popping scopes in different places as we go. We *could* - /// probably make it fit if we tried hard enough but I'm not - /// sure it's worth it, so for now we'll just brute-force - /// it and see how it looks at the end. - /// - /// closure_convert::do_to_children() might be a - /// way of making it more generic? - fn rename_expr(&mut self, expr: hir::ExprNode) -> hir::ExprNode { - use hir::Expr::*; - let f = &mut |e| match e { - Lit { val } => Lit { val }, - Var { name } => { - let new_name = self.really_get_binding(name); - Var { name: new_name.0 } - } - BinOp { op, lhs, rhs } => BinOp { - op, - lhs: self.rename_expr(lhs), - rhs: self.rename_expr(rhs), - }, - UniOp { rhs, op } => UniOp { - op, - rhs: self.rename_expr(rhs), - }, - Block { body } => { - let _guard = self.push_scope(); - let new_body = self.rename_exprs(body); - Block { body: new_body } - } - Loop { body } => Loop { - body: self.rename_exprs(body), - }, - Funcall { - func, - params, - type_params, - } => { - let func = self.rename_expr(func); - let params = self.rename_exprs(params); - let type_params = self.rename_types(type_params); - Funcall { - func, - params, - type_params, - } - } - Let { - varname, - init, - typename, - mutable, - } => { - // init expression cannot refer to the same - // symbol name - let init = self.rename_expr(init); - let varname = self.bind_new_symbol(varname).0; - let typename = typename.map(|t| self.rename_type(t)); - Let { - varname, - init, - typename, - mutable, - } - } - If { cases } => { - let cases = cases - .into_iter() - .map(|(test, body)| { - // The test cannot introduce a new scope unless - // it contains some cursed structure like a block - // or fn that introduces a new scope anyway, so. - let t = self.rename_expr(test); - let _guard = self.push_scope(); - let b = self.rename_exprs(body); - (t, b) - }) - .collect(); - If { cases } - } - EnumCtor { - name, - variant, - value, - } => { - // Translate the type name - // No need to translate the variants 'cause - // they are namespaced anyway - let new_name = self.really_get_type_binding(name).0; - EnumCtor { - name: new_name, - variant, - value, - } - } - TupleCtor { body } => TupleCtor { - body: self.rename_exprs(body), - }, - TupleRef { expr, elt } => TupleRef { - expr: self.rename_expr(expr), - elt, - }, - StructCtor { body } => { - let body = body - .into_iter() - .map(|(nm, expr)| (nm, self.rename_expr(expr))) - .collect(); - StructCtor { body } - } - StructRef { expr, elt } => StructRef { - expr: self.rename_expr(expr), - elt, - }, - Assign { lhs, rhs } => Assign { - lhs: self.rename_expr(lhs), - rhs: self.rename_expr(rhs), - }, - Break => Break, - Lambda { signature, body } => { - let _scope = self.push_scope(); - // Here we DECLARE the types in the param - // and rettype, similar to in handle_decl() - let new_type_params = signature - .typeparams - .into_iter() - .map(|sym| self.bind_new_type(sym).0) - .collect(); - let new_rettype = self.rename_type(signature.rettype); - - // We have to do this AFTER adding the type params - // because the function args can mention - // type params. - let new_params = signature - .params - .into_iter() - .map(|(sym, typ)| (self.bind_new_symbol(sym).0, self.rename_type(typ))) - .collect(); - let new_sig = Signature { - params: new_params, - rettype: new_rettype, - typeparams: new_type_params, - }; - let new_body = self.rename_exprs(body); - Lambda { - signature: new_sig, - body: new_body, - } - } - Return { retval } => Return { - retval: self.rename_expr(retval), - }, - TypeCtor { - name, - type_params, - body, - } => { - // Translate the type name - let new_name = self.really_get_type_binding(name).0; - // Translate the type params it's called with - let new_params = self.rename_types(type_params); - let body = self.rename_expr(body); - TypeCtor { - name: new_name, - type_params: new_params, - body, - } - } - TypeUnwrap { expr } => TypeUnwrap { - expr: self.rename_expr(expr), - }, - SumCtor { - name, - variant, - body, - } => { - let new_name = self.really_get_type_binding(name).0; - SumCtor { - name: new_name, - variant, - body: self.rename_expr(body), - } - } - ArrayCtor { body } => ArrayCtor { - body: self.rename_exprs(body), - }, - ArrayRef { expr, idx } => ArrayRef { - expr: self.rename_expr(expr), - idx: self.rename_expr(idx), - }, - Typecast { .. } => { - todo!() - } - Ref { .. } => { - todo!() - } - Deref { .. } => { - todo!() - } - }; - expr.map(f) + .contents + .insert(sym, vl); } } - -pub(crate) fn predeclare_decls(symtbl: &mut Symtbl, decls: &[hir::Decl]) { - use hir::Decl::*; - for d in decls { - match d { - Function { - name, - signature: _, - body: _, - } => { - // Don't rename functions already declared as builtins, like "main" - // Little hacky but does what we want. - symtbl - .get_binding(*name) - .unwrap_or_else(|| symtbl.new_unique_symbol(*name)); - } - TypeDef { - name, - params: _, - typedecl: _, - } => { - // Let's rename all types, which I think doesn't - // quite matter yet but will in the future when we - // have more powerful modules and such. - symtbl.bind_new_type(*name); - } - Const { - name, - typ: _, - init: _, - } => { - symtbl.new_unique_symbol(*name); - } - Import { .. } => todo!(), - } - } -} - -pub fn rename_decl(symtbl: &mut Symtbl, decl: hir::Decl) -> hir::Decl { - use hir::Decl::*; - match decl { - Function { - name, - signature, - body, - } => { - // This has to happen before we push the scope, so - // if we have a function arg with the same name as the - // function it doesn't rename the function. - let fn_name = symtbl.really_get_binding(name).0; - - let _scope = symtbl.push_scope(); - // These must be handled first 'cause types in the - // function arguments can refer to them. - let new_type_params = signature - .typeparams - .into_iter() - .map(|sym| symtbl.bind_new_type(sym).0) - .collect(); - let new_params = signature - .params - .into_iter() - .map(|(sym, typ)| (symtbl.bind_new_symbol(sym).0, symtbl.rename_type(typ))) - .collect(); - let new_rettype = symtbl.rename_type(signature.rettype); - let new_sig = Signature { - params: new_params, - rettype: new_rettype, - typeparams: new_type_params, - }; - let new_body = symtbl.rename_exprs(body); - Function { - name: fn_name, - signature: new_sig, - body: new_body, - } - } - TypeDef { - name, - params, - typedecl, - } => { - // Ok this is exactly like resolving variables in a function. - // We bind the declared type params, check that all the types - // named in the body exist, and rename the ones in the params. - let ty_name = symtbl.really_get_type_binding(name).0; - let _scope = symtbl.push_scope(); - let new_params = params - .into_iter() - .map(|sym| (symtbl.bind_new_type(sym).0)) - .collect(); - TypeDef { - name: ty_name, - params: new_params, - typedecl: symtbl.rename_type(typedecl), - } - } - Const { name, typ, init } => { - let new_name = symtbl.really_get_binding(name).0; - let new_type = symtbl.rename_type(typ); - let new_body = symtbl.rename_expr(init); - Const { - name: new_name, - typ: new_type, - init: new_body, - } - } - Import { .. } => todo!(), - } -} - -/// Takes all value and type names and renames them all to -/// remove scope dependence. -/// -/// aka alpha-renaming -pub fn make_symbols_unique(ir: hir::Ir) -> (hir::Ir, Symtbl) { - let mut s = Symtbl::default(); - s.add_builtins(); - predeclare_decls(&mut s, &ir.decls); - let mut ir = ir; - ir.decls = ir - .decls - .into_iter() - .map(|d| rename_decl(&mut s, d)) - .collect(); - (ir, s) -} diff --git a/src/symtbl.rs b/src/symtbl.rs --- a/src/symtbl.rs +++ b/src/symtbl.rs @@ -5,6 +5,13 @@ //! once instead of needing to walk through scopes multiple //! times and then throw the scope information away when //! we're done with it. +//! +//! This is a little jank right now 'cause it *started* as a way +//! to do dedicated alpha-renaming and not much else, and then I +//! found out that alpha-renaming sucks when doing substitution and +//! had to end up repurposing this for other things to. +//! +//! TODO: This should probably be rewritten to use simptbl instead. use std::cell::RefCell; use std::rc::Rc; @@ -37,12 +44,23 @@ } } -#[derive(Clone, Default, Debug)] +#[derive(Clone, Debug)] struct ScopeFrame { symbols: BTreeMap, types: BTreeMap, } +/// This can't be a derive 'cause the generics-with-default-type +/// make shit squirrelly. +impl Default for ScopeFrame { + fn default() -> Self { + Self { + symbols: BTreeMap::default(), + types: BTreeMap::default(), + } + } +} + /// A shortcut for a cloneable AnyMap type CloneMap = Map; @@ -61,7 +79,7 @@ unique_types: BTreeMap, } -impl Default for Symtbl { +impl Default for Symtbl { /// We start with an empty toplevel scope existing, /// then add some builtin's to it. fn default() -> Self { @@ -73,11 +91,11 @@ } } -pub struct ScopeGuard { - scope: Symtbl, +pub struct ScopeGuard { + scope: Symtbl, } -impl Drop for ScopeGuard { +impl Drop for ScopeGuard { fn drop(&mut self) { self.scope .frames @@ -87,6 +105,97 @@ } } +impl Symtbl +where + T: Clone, +{ + pub fn push_scope(&self) -> ScopeGuard { + self.frames.borrow_mut().push(ScopeFrame::default()); + // TODO: This clone is a little cursed 'cause + // it'll clone the whole `unique_symbols` map, + // We should probably just wrap the whole symtbl in + // a Rc> intead of just the scopeframe. + ScopeGuard { + scope: self.clone(), + } + } + + /// Get a reference to the symbol most recently bound + /// with that name, or panic if it does not exist + /// + /// ...I kinda miss Elixir's convention of having + /// a function `foo` return a result and `foo!` panic + /// on error. + fn really_get_binding(&self, sym: Sym) -> T { + let msg = format!("Symbol {} is not bound!", sym); + self.get_binding(sym).expect(&msg) + } + + /// Returns the UniqueSym that the given Sym is *currently* + /// bound to, or None if DNE + fn get_binding(&self, sym: Sym) -> Option { + for scope in self.frames.borrow().iter().rev() { + let v = scope.symbols.get(&sym).cloned(); + if v.is_some() { + return v; + } + } + None + } + + fn get_type_binding(&self, sym: Sym) -> Option { + for scope in self.frames.borrow().iter().rev() { + let v = scope.types.get(&sym).cloned(); + if v.is_some() { + return v; + } + } + None + } + + fn really_get_type_binding(&self, sym: Sym) -> T { + let msg = format!("Type {} is not bound!", sym); + self.get_type_binding(sym).expect(&msg) + } + + /// Get the specified info for the given UniqueSym, or + /// None if DNE + pub fn get_info(&self, sym: UniqueSym) -> Option<&Info> + where + Info: anymap::any::CloneAny, + { + self.unique_symbols + .get(&sym) + .and_then(|anymap| anymap.get::()) + } + + /// Adds the given info struct to the symbol, or + /// panics if it already exists. + pub fn put_info(&mut self, sym: UniqueSym, info: Info) + where + Info: anymap::any::CloneAny, + { + self.unique_symbols + .get_mut(&sym) + .and_then(|anymap| anymap.insert(info)); + } + + pub fn type_exists(&self, sym: Sym) -> bool { + if sym == "Tuple" { + return true; + } + let thing = self.unique_types.get(&UniqueSym(sym)); + // fuck combinators + match thing { + Some(_anymap) => true, + None => false, + } + } + fn binding_exists(&self, sym: Sym) -> bool { + self.get_binding(sym).is_some() + } +} + impl Symtbl { fn add_builtins(&mut self) { for builtin in &*builtins::BUILTINS { @@ -104,89 +213,6 @@ UniqueSym(sym) } - pub fn push_scope(&self) -> ScopeGuard { - self.frames.borrow_mut().push(ScopeFrame::default()); - // TODO: This clone is a little cursed 'cause - // it'll clone the whole `unique_symbols` map, - // We should probably just wrap the whole symtbl in - // a Rc> intead of just the scopeframe. - ScopeGuard { - scope: self.clone(), - } - } - - /// Returns the UniqueSym that the given Sym is *currently* - /// bound to, or None if DNE - fn get_binding(&self, sym: Sym) -> Option { - for scope in self.frames.borrow().iter().rev() { - let v = scope.symbols.get(&sym).cloned(); - if v.is_some() { - return v; - } - } - None - } - - /// Get a reference to the symbol most recently bound - /// with that name, or panic if it does not exist - /// - /// ...I kinda miss Elixir's convention of having - /// a function `foo` return a result and `foo!` panic - /// on error. - fn really_get_binding(&self, sym: Sym) -> UniqueSym { - let msg = format!("Symbol {} is not bound!", sym); - self.get_binding(sym).expect(&msg) - } - - fn get_type_binding(&self, sym: Sym) -> Option { - for scope in self.frames.borrow().iter().rev() { - let v = scope.types.get(&sym).cloned(); - if v.is_some() { - return v; - } - } - None - } - - fn really_get_type_binding(&self, sym: Sym) -> UniqueSym { - let msg = format!("Type {} is not bound!", sym); - self.get_type_binding(sym).expect(&msg) - } - - /// Get the specified info for the given UniqueSym, or - /// None if DNE - pub fn get_info(&self, sym: UniqueSym) -> Option<&T> - where - T: anymap::any::CloneAny, - { - self.unique_symbols - .get(&sym) - .and_then(|anymap| anymap.get::()) - } - - /// Adds the given info struct to the symbol, or - /// panics if it already exists. - pub fn put_info(&mut self, sym: UniqueSym, info: T) - where - T: anymap::any::CloneAny, - { - self.unique_symbols - .get_mut(&sym) - .and_then(|anymap| anymap.insert(info)); - } - - pub fn type_exists(&self, sym: Sym) -> bool { - if sym == "Tuple" { - return true; - } - let thing = self.unique_types.get(&UniqueSym(sym)); - // fuck combinators - match thing { - Some(_anymap) => true, - None => false, - } - } - fn get_type_info(&self, sym: UniqueSym) -> Option<&T> where T: anymap::any::CloneAny, @@ -230,10 +256,6 @@ self.put_type_info(UniqueSym(sym), info) } - fn binding_exists(&self, sym: Sym) -> bool { - self.get_binding(sym).is_some() - } - fn _unique_exists(&self, sym: UniqueSym) -> bool { self.unique_symbols.get(&sym).is_some() }