interpreter: use an explicit stack, rather than a cons list

Also gets rid of some dead code
This commit is contained in:
John 2024-01-05 22:47:16 -06:00
parent 54bae6a9c5
commit 53f9ec2356

View File

@ -8,7 +8,7 @@ use temp_type_impl::ConValue;
/// Callable types can be called from within a Conlang program /// Callable types can be called from within a Conlang program
pub trait Callable: std::fmt::Debug { 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 /// The Callable is responsible for checking the argument count and validating types
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue>; fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue>;
/// Returns the common name of this identifier. /// 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 /// A work-in-progress tree walk interpreter for Conlang
pub trait Interpret { pub trait Interpret {
/// Interprets this thing using the given [`Interpreter`]. /// Interprets this thing in the given [`Environment`].
/// ///
/// Everything returns a value!™ /// Everything returns a value!™
fn interpret(&self, env: &mut Environment) -> IResult<ConValue>; fn interpret(&self, env: &mut Environment) -> IResult<ConValue>;
@ -353,25 +353,20 @@ impl Interpret for Assign {
use operator::Assign; use operator::Assign;
let math::Assign { target, operator, init } = self; let math::Assign { target, operator, init } = self;
let init = init.interpret(env)?; let init = init.interpret(env)?;
let resolved = env.get_mut(&target.name)?; let target = env.get_mut(&target.name)?;
if let Assign::Assign = operator { if let Assign::Assign = operator {
use std::mem::discriminant as variant; use std::mem::discriminant as variant;
// runtime typecheck // runtime typecheck
match resolved.as_mut() { match target {
Some(value) if variant(value) == variant(&init) => { value if variant(value) == variant(&init) => {
*value = init; *value = init;
} }
None => *resolved = Some(init),
_ => Err(Error::TypeError)?, _ => Err(Error::TypeError)?,
} }
return Ok(ConValue::Empty); return Ok(ConValue::Empty);
} }
let Some(target) = resolved.as_mut() else {
Err(Error::NotInitialized(target.name.to_owned()))?
};
match operator { match operator {
Assign::AddAssign => target.add_assign(init)?, Assign::AddAssign => target.add_assign(init)?,
Assign::SubAssign => target.sub_assign(init)?, Assign::SubAssign => target.sub_assign(init)?,
@ -494,7 +489,7 @@ impl Interpret for Primary {
} }
impl Interpret for Identifier { impl Interpret for Identifier {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
env.resolve(&self.name) env.get(&self.name).cloned()
} }
} }
impl Interpret for Literal { impl Interpret for Literal {
@ -622,7 +617,7 @@ impl Interpret for Return {
} }
impl Interpret for Break { impl Interpret for Break {
fn interpret(&self, env: &mut Environment) -> IResult<ConValue> { fn interpret(&self, env: &mut Environment) -> IResult<ConValue> {
Err(Error::Return(self.expr.interpret(env)?)) Err(Error::Break(self.expr.interpret(env)?))
} }
} }
@ -634,42 +629,35 @@ pub mod function {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Function { pub struct Function {
/// Stores the contents of the function declaration /// Stores the contents of the function declaration
declaration: Box<FnDecl>, decl: Box<FnDecl>,
// /// Stores the enclosing scope of the function /// Stores the enclosing scope of the function
// TODO: Capture variables env: Box<Environment>,
//environment: Box<Environment>,
} }
impl Function { impl Function {
pub fn new(declaration: &FnDecl) -> Self { pub fn new(decl: &FnDecl, env: Environment) -> Self {
Self { Self { decl: decl.clone().into(), env: Box::new(env) }
declaration: declaration.clone().into(),
//environment: Box::new(Environment::new()),
}
} }
} }
impl Callable for Function { impl Callable for Function {
fn name(&self) -> &str { fn name(&self) -> &str {
&self.declaration.name.symbol.name &self.decl.name.symbol.name
} }
fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> { fn call(&self, _env: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
// Check arg mapping // Check arg mapping
if args.len() != self.declaration.args.len() { if args.len() != self.decl.args.len() {
return Err(Error::ArgNumber { return Err(Error::ArgNumber { want: self.decl.args.len(), got: args.len() });
want: self.declaration.args.len(),
got: args.len(),
});
} }
// TODO: Isolate cross-function scopes! // TODO: Isolate cross-function scopes!
let mut env = self.env.clone();
let mut frame = env.frame("function args"); let mut frame = env.frame("fn args");
for (Name { symbol: Identifier { name, .. }, .. }, value) in 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())); 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::Return(value)) => Ok(value),
Err(Error::Break(value)) => Err(Error::BadBreak(value)), Err(Error::Break(value)) => Err(Error::BadBreak(value)),
result => result, result => result,
@ -679,6 +667,7 @@ pub mod function {
} }
pub mod builtin { pub mod builtin {
//! Implementations of built-in functions
mod builtin_imports { mod builtin_imports {
pub use crate::interpreter::{ pub use crate::interpreter::{
env::Environment, error::IResult, temp_type_impl::ConValue, BuiltIn, Callable, env::Environment, error::IResult, temp_type_impl::ConValue, BuiltIn, Callable,
@ -759,34 +748,32 @@ pub mod env {
/// Implements a nested lexical scope /// Implements a nested lexical scope
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Environment { pub struct Environment {
outer: Option<Box<Self>>, frames: Vec<(HashMap<String, Option<ConValue>>, &'static str)>,
vars: HashMap<String, Option<ConValue>>,
name: &'static str,
} }
impl Display for Environment { impl Display for Environment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "--- '{}' ---", self.name)?; for (frame, name) in self.frames.iter().rev() {
for (var, val) in &self.vars { writeln!(f, "--- {name} ---")?;
write!(f, "{var}: ")?; for (var, val) in frame {
match val { write!(f, "- {var}: ")?;
Some(value) => writeln!(f, "{value}"), match val {
None => writeln!(f, "<undefined>"), Some(value) => writeln!(f, "{value}"),
}? None => writeln!(f, "<undefined>"),
} }?
if let Some(outer) = &self.outer { }
outer.fmt(f)?;
} }
Ok(()) Ok(())
} }
} }
impl Default for Environment { impl Default for Environment {
fn default() -> Self { fn default() -> Self {
let mut outer = Self::no_builtins("builtins"); let mut builtins = HashMap::new();
for &builtin in DEFAULT_BUILTINS { 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 { pub fn new() -> Self {
Self::default() Self::default()
} }
fn no_builtins(name: &'static str) -> Self { /// Creates an [Environment] with no [builtins](super::builtin)
Self { outer: None, vars: Default::default(), name } pub fn no_builtins(name: &'static str) -> Self {
} Self { frames: vec![(Default::default(), name)] }
/// Temporary function
#[deprecated]
pub fn resolve(&mut self, name: &str) -> IResult<ConValue> {
self.get(name).cloned()
} }
pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> { pub fn eval(&mut self, node: &impl Interpret) -> IResult<ConValue> {
@ -810,7 +793,8 @@ pub mod env {
/// Calls a function inside the interpreter's scope, /// Calls a function inside the interpreter's scope,
/// and returns the result /// and returns the result
pub fn call(&mut self, name: &str, args: &[ConValue]) -> IResult<ConValue> { pub fn call(&mut self, name: &str, args: &[ConValue]) -> IResult<ConValue> {
let function = self.resolve(name)?; // FIXME: Clone to satisfy the borrow checker
let function = self.get(name)?.clone();
function.call(self, args) function.call(self, args)
} }
/// Enters a nested scope, returning a [`Frame`] stack-guard. /// Enters a nested scope, returning a [`Frame`] stack-guard.
@ -822,36 +806,42 @@ pub mod env {
/// Resolves a variable mutably /// Resolves a variable mutably
/// ///
/// Returns a mutable reference to the variable's record, if it exists /// Returns a mutable reference to the variable's record, if it exists
pub fn get_mut(&mut self, id: &str) -> IResult<&mut Option<ConValue>> { pub fn get_mut(&mut self, id: &str) -> IResult<&mut ConValue> {
match self.vars.get_mut(id) { for (frame, _) in self.frames.iter_mut() {
Some(var) => Ok(var), match frame.get_mut(id) {
None => self Some(Some(var)) => return Ok(var),
.outer Some(None) => return Err(Error::NotInitialized(id.into())),
.as_mut() _ => (),
.ok_or_else(|| Error::NotDefined(id.into()))? }
.get_mut(id),
} }
Err(Error::NotDefined(id.into()))
} }
/// Resolves a variable immutably /// Resolves a variable immutably
pub fn get(&self, id: &str) -> IResult<&ConValue> { pub fn get(&self, id: &str) -> IResult<&ConValue> {
match self.vars.get(id) { for (frame, _) in self.frames.iter() {
Some(var) => var.as_ref().ok_or_else(|| Error::NotInitialized(id.into())), match frame.get(id) {
None => self Some(Some(var)) => return Ok(var),
.outer Some(None) => return Err(Error::NotInitialized(id.into())),
.as_ref() _ => (),
.ok_or_else(|| Error::NotDefined(id.into()))? }
.get(id),
} }
Err(Error::NotDefined(id.into()))
} }
/// Inserts a new [ConValue] into this [Environment]
pub fn insert(&mut self, id: &str, value: Option<ConValue>) { pub fn insert(&mut self, id: &str, value: Option<ConValue>) {
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] /// A convenience function for registering a [FnDecl] as a [Function]
pub fn insert_fn(&mut self, decl: &FnDecl) { pub fn insert_fn(&mut self, decl: &FnDecl) {
self.vars.insert( let (name, function) = (
decl.name.symbol.name.clone(), 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 { impl Environment {
/// Enters a scope, creating a new namespace for variables /// Enters a scope, creating a new namespace for variables
fn enter(&mut self, name: &'static str) -> &mut Self { fn enter(&mut self, name: &'static str) -> &mut Self {
let outer = std::mem::replace(self, Environment::no_builtins(name)); self.frames.push((Default::default(), name));
self.outer = Some(outer.into());
self self
} }
/// Exits the scope, destroying all local variables and /// Exits the scope, destroying all local variables and
/// returning the outer scope, if there is one /// returning the outer scope, if there is one
fn exit(&mut self) -> &mut Self { fn exit(&mut self) -> &mut Self {
if let Some(outer) = std::mem::take(&mut self.outer) { if self.frames.len() > 2 {
*self = *outer; self.frames.pop();
} }
self self
} }
@ -902,34 +891,14 @@ pub mod env {
} }
} }
pub mod module {
//! Implements non-lexical "global" scoping rules
}
pub mod error { 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; use super::temp_type_impl::ConValue;
pub type IResult<T> = Result<T, Error>; pub type IResult<T> = Result<T, Error>;
/// 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)] #[derive(Clone, Debug)]
pub enum Error { pub enum Error {
/// Propagate a Return value /// Propagate a Return value