interpreter: use an explicit stack, rather than a cons list
Also gets rid of some dead code
This commit is contained in:
parent
54bae6a9c5
commit
53f9ec2356
@ -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 {
|
||||||
|
write!(f, "- {var}: ")?;
|
||||||
match val {
|
match val {
|
||||||
Some(value) => writeln!(f, "{value}"),
|
Some(value) => writeln!(f, "{value}"),
|
||||||
None => writeln!(f, "<undefined>"),
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user