From 53f9ec2356ad9d066c1cf906dad47f2544729dad Mon Sep 17 00:00:00 2001 From: John Date: Fri, 5 Jan 2024 22:47:16 -0600 Subject: [PATCH] interpreter: use an explicit stack, rather than a cons list Also gets rid of some dead code --- libconlang/src/interpreter.rs | 169 ++++++++++++++-------------------- 1 file changed, 69 insertions(+), 100 deletions(-) diff --git a/libconlang/src/interpreter.rs b/libconlang/src/interpreter.rs index 894f6c4..8db2dc8 100644 --- a/libconlang/src/interpreter.rs +++ b/libconlang/src/interpreter.rs @@ -8,7 +8,7 @@ use temp_type_impl::ConValue; /// Callable types can be called from within a Conlang program pub trait Callable: std::fmt::Debug { - /// Calls this [Callable] in the provided [Interpreter], with [ConValue] args \ + /// Calls this [Callable] in the provided [Environment], with [ConValue] args \ /// The Callable is responsible for checking the argument count and validating types fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult; /// Returns the common name of this identifier. @@ -285,7 +285,7 @@ pub mod temp_type_impl { /// A work-in-progress tree walk interpreter for Conlang pub trait Interpret { - /// Interprets this thing using the given [`Interpreter`]. + /// Interprets this thing in the given [`Environment`]. /// /// Everything returns a value!™ fn interpret(&self, env: &mut Environment) -> IResult; @@ -353,25 +353,20 @@ impl Interpret for Assign { use operator::Assign; let math::Assign { target, operator, init } = self; let init = init.interpret(env)?; - let resolved = env.get_mut(&target.name)?; + let target = env.get_mut(&target.name)?; if let Assign::Assign = operator { use std::mem::discriminant as variant; // runtime typecheck - match resolved.as_mut() { - Some(value) if variant(value) == variant(&init) => { + match target { + value if variant(value) == variant(&init) => { *value = init; } - None => *resolved = Some(init), _ => Err(Error::TypeError)?, } return Ok(ConValue::Empty); } - let Some(target) = resolved.as_mut() else { - Err(Error::NotInitialized(target.name.to_owned()))? - }; - match operator { Assign::AddAssign => target.add_assign(init)?, Assign::SubAssign => target.sub_assign(init)?, @@ -494,7 +489,7 @@ impl Interpret for Primary { } impl Interpret for Identifier { fn interpret(&self, env: &mut Environment) -> IResult { - env.resolve(&self.name) + env.get(&self.name).cloned() } } impl Interpret for Literal { @@ -622,7 +617,7 @@ impl Interpret for Return { } impl Interpret for Break { fn interpret(&self, env: &mut Environment) -> IResult { - Err(Error::Return(self.expr.interpret(env)?)) + Err(Error::Break(self.expr.interpret(env)?)) } } @@ -634,42 +629,35 @@ pub mod function { #[derive(Clone, Debug)] pub struct Function { /// Stores the contents of the function declaration - declaration: Box, - // /// Stores the enclosing scope of the function - // TODO: Capture variables - //environment: Box, + decl: Box, + /// Stores the enclosing scope of the function + env: Box, } impl Function { - pub fn new(declaration: &FnDecl) -> Self { - Self { - declaration: declaration.clone().into(), - //environment: Box::new(Environment::new()), - } + pub fn new(decl: &FnDecl, env: Environment) -> Self { + Self { decl: decl.clone().into(), env: Box::new(env) } } } impl Callable for Function { fn name(&self) -> &str { - &self.declaration.name.symbol.name + &self.decl.name.symbol.name } - fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult { + fn call(&self, _env: &mut Environment, args: &[ConValue]) -> IResult { // Check arg mapping - if args.len() != self.declaration.args.len() { - return Err(Error::ArgNumber { - want: self.declaration.args.len(), - got: args.len(), - }); + if args.len() != self.decl.args.len() { + return Err(Error::ArgNumber { want: self.decl.args.len(), got: args.len() }); } // TODO: Isolate cross-function scopes! - - let mut frame = env.frame("function args"); + let mut env = self.env.clone(); + let mut frame = env.frame("fn args"); for (Name { symbol: Identifier { name, .. }, .. }, value) in - self.declaration.args.iter().zip(args) + self.decl.args.iter().zip(args) { frame.insert(name, Some(value.clone())); } - match self.declaration.body.interpret(&mut frame) { + match self.decl.body.interpret(&mut frame) { Err(Error::Return(value)) => Ok(value), Err(Error::Break(value)) => Err(Error::BadBreak(value)), result => result, @@ -679,6 +667,7 @@ pub mod function { } pub mod builtin { + //! Implementations of built-in functions mod builtin_imports { pub use crate::interpreter::{ env::Environment, error::IResult, temp_type_impl::ConValue, BuiltIn, Callable, @@ -759,34 +748,32 @@ pub mod env { /// Implements a nested lexical scope #[derive(Clone, Debug)] pub struct Environment { - outer: Option>, - vars: HashMap>, - name: &'static str, + frames: Vec<(HashMap>, &'static str)>, } impl Display for Environment { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "--- '{}' ---", self.name)?; - for (var, val) in &self.vars { - write!(f, "{var}: ")?; - match val { - Some(value) => writeln!(f, "{value}"), - None => writeln!(f, ""), - }? - } - if let Some(outer) = &self.outer { - outer.fmt(f)?; + for (frame, name) in self.frames.iter().rev() { + writeln!(f, "--- {name} ---")?; + for (var, val) in frame { + write!(f, "- {var}: ")?; + match val { + Some(value) => writeln!(f, "{value}"), + None => writeln!(f, ""), + }? + } } Ok(()) } } impl Default for Environment { fn default() -> Self { - let mut outer = Self::no_builtins("builtins"); + let mut builtins = HashMap::new(); for &builtin in DEFAULT_BUILTINS { - outer.insert(builtin.name(), Some(ConValue::BuiltIn(builtin))); + builtins.insert(builtin.name().into(), Some(ConValue::BuiltIn(builtin))); } - Self { outer: Some(Box::new(outer)), vars: Default::default(), name: "base" } + // FIXME: Temporary until modules are implemented + Self { frames: vec![(builtins, "builtins"), (HashMap::new(), "globals")] } } } @@ -794,13 +781,9 @@ pub mod env { pub fn new() -> Self { Self::default() } - fn no_builtins(name: &'static str) -> Self { - Self { outer: None, vars: Default::default(), name } - } - /// Temporary function - #[deprecated] - pub fn resolve(&mut self, name: &str) -> IResult { - self.get(name).cloned() + /// Creates an [Environment] with no [builtins](super::builtin) + pub fn no_builtins(name: &'static str) -> Self { + Self { frames: vec![(Default::default(), name)] } } pub fn eval(&mut self, node: &impl Interpret) -> IResult { @@ -810,7 +793,8 @@ pub mod env { /// Calls a function inside the interpreter's scope, /// and returns the result pub fn call(&mut self, name: &str, args: &[ConValue]) -> IResult { - let function = self.resolve(name)?; + // FIXME: Clone to satisfy the borrow checker + let function = self.get(name)?.clone(); function.call(self, args) } /// Enters a nested scope, returning a [`Frame`] stack-guard. @@ -822,36 +806,42 @@ pub mod env { /// Resolves a variable mutably /// /// Returns a mutable reference to the variable's record, if it exists - pub fn get_mut(&mut self, id: &str) -> IResult<&mut Option> { - match self.vars.get_mut(id) { - Some(var) => Ok(var), - None => self - .outer - .as_mut() - .ok_or_else(|| Error::NotDefined(id.into()))? - .get_mut(id), + pub fn get_mut(&mut self, id: &str) -> IResult<&mut ConValue> { + for (frame, _) in self.frames.iter_mut() { + match frame.get_mut(id) { + Some(Some(var)) => return Ok(var), + Some(None) => return Err(Error::NotInitialized(id.into())), + _ => (), + } } + Err(Error::NotDefined(id.into())) } /// Resolves a variable immutably pub fn get(&self, id: &str) -> IResult<&ConValue> { - match self.vars.get(id) { - Some(var) => var.as_ref().ok_or_else(|| Error::NotInitialized(id.into())), - None => self - .outer - .as_ref() - .ok_or_else(|| Error::NotDefined(id.into()))? - .get(id), + for (frame, _) in self.frames.iter() { + match frame.get(id) { + Some(Some(var)) => return Ok(var), + Some(None) => return Err(Error::NotInitialized(id.into())), + _ => (), + } } + Err(Error::NotDefined(id.into())) } + /// Inserts a new [ConValue] into this [Environment] pub fn insert(&mut self, id: &str, value: Option) { - self.vars.insert(id.to_string(), value); + if let Some((frame, _)) = self.frames.last_mut() { + frame.insert(id.into(), value); + } } /// A convenience function for registering a [FnDecl] as a [Function] pub fn insert_fn(&mut self, decl: &FnDecl) { - self.vars.insert( + let (name, function) = ( decl.name.symbol.name.clone(), - Some(Function::new(decl).into()), + Some(Function::new(decl, self.clone()).into()), ); + if let Some((frame, _)) = self.frames.last_mut() { + frame.insert(name, function); + } } } @@ -859,16 +849,15 @@ pub mod env { impl Environment { /// Enters a scope, creating a new namespace for variables fn enter(&mut self, name: &'static str) -> &mut Self { - let outer = std::mem::replace(self, Environment::no_builtins(name)); - self.outer = Some(outer.into()); + self.frames.push((Default::default(), name)); self } /// Exits the scope, destroying all local variables and /// returning the outer scope, if there is one fn exit(&mut self) -> &mut Self { - if let Some(outer) = std::mem::take(&mut self.outer) { - *self = *outer; + if self.frames.len() > 2 { + self.frames.pop(); } self } @@ -902,34 +891,14 @@ pub mod env { } } -pub mod module { - //! Implements non-lexical "global" scoping rules - -} - pub mod error { - //! The [Error] type represents any error thrown by the [Interpreter](super::Interpreter) + //! The [Error] type represents any error thrown by the [Environment](super::Environment) use super::temp_type_impl::ConValue; pub type IResult = Result; - /// Represents any error thrown by the [Interpreter](super::Interpreter) - impl Error { - /// Creates a [Return](Error::Return) error, with the given [value](ConValue) - pub fn ret(value: ConValue) -> Self { - Error::Return(value) - } - /// Creates a [Break](Error::Break) error, with the given [value](ConValue) - pub fn brk(value: ConValue) -> Self { - Error::Break(value) - } - /// Creates a [Continue](Error::Continue) error - pub fn cnt() -> Self { - Error::Continue - } - } - /// The reason for the [Error] + /// Represents any error thrown by the [Environment](super::Environment) #[derive(Clone, Debug)] pub enum Error { /// Propagate a Return value