mul/src/evaluate.rs

202 lines
6.7 KiB
Rust

//! Tree-walk interpreter for syntax.
use std::fmt::Display;
use thiserror::Error;
use crate::builtins::Builtin;
use crate::dictionary::Dictionary;
use crate::syntax::{Block, Expression, InteractiveEntry, Item, Name, Program};
/// Runtime values.
#[derive(Debug, Clone)]
pub enum Value {
Builtin(Builtin),
Closure {
environment: Dictionary<Value>,
parameters: Vec<Name>,
result: Expression,
},
Boolean(bool),
Integer(i64),
Unit,
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Builtin(b) => write!(f, "<builtin:{b:?}>"),
Value::Closure { parameters, .. } => write!(f, "<closure({})>", parameters.join(", ")),
Value::Boolean(b) => write!(f, "{b}"),
Value::Integer(i) => write!(f, "{i}"),
Value::Unit => write!(f, "()"),
}
}
}
/// Errors that may occur during evaluation.
#[derive(Debug, Error)]
pub enum Error {
#[error("The name '{0} was not bound to any value in this context")]
UnboundName(Name),
#[error("Tried to call a term which was not a function")]
NotAFunction,
#[error("Mismatched number of function parameters and arguments: expected {0}, got {1}")]
ArgumentsMismatch(usize, usize),
#[error("No function called main to run")]
MissingMainFunction,
}
type Result<T> = std::result::Result<T, Error>;
/// AST walking interpreter.
#[derive(Debug, Clone)]
pub struct Interpreter {
environment: Dictionary<Value>,
}
macro_rules! bind {
($interpreter:expr, $name:expr, $term:expr, to: $nested:expr) => {{
let value = $interpreter.evaluate($term)?;
$nested.environment.insert($name, value.clone());
Ok(value)
}};
($interpreter:expr, $name:expr, $term:expr) => {
bind!($interpreter, $name, $term, to: $interpreter)
};
}
impl Interpreter {
/// Create a fresh interpreter populated with builtins.
pub fn new() -> Self {
Interpreter {
environment: Builtin::dictionary(),
}
}
/// Create a nested interpreter given an existing environment.
fn nested(environment: Dictionary<Value>) -> Self {
Interpreter { environment }
}
/// Run a program. Expects a main function to exist.
pub fn run(&mut self, program: &Program) -> Result<Value> {
for item in program {
self.interpret_item(item)?;
}
if let Some(main) = self.environment.get("main").cloned() {
self.apply_call(&main, &vec![])
} else {
Err(Error::MissingMainFunction)
}
}
fn interpret_item(&mut self, item: &Item) -> Result<Value> {
match item {
Item::Fn {
name,
parameters,
body,
..
} => {
let closure = Value::Closure {
environment: self.environment.clone(),
parameters: parameters.iter().map(|p| p.name.clone()).collect(),
result: Expression::Block(Box::new(body.clone())),
};
self.environment.insert(name.clone(), closure.clone());
Ok(closure)
}
}
}
/// Evaluate a code fragment from an interactive context.
pub fn evaluate_interactive_entry(&mut self, entry: &InteractiveEntry) -> Result<Value> {
match entry {
InteractiveEntry::Item(item) => self.interpret_item(item),
InteractiveEntry::Binding(binding) => {
bind!(self, binding.name.clone(), &binding.expression)
}
InteractiveEntry::Expression(term) => self.evaluate(term),
}
}
fn evaluate(&mut self, term: &Expression) -> Result<Value> {
match term {
Expression::Variable(name) => self
.environment
.get(name)
.ok_or(Error::UnboundName(name.clone()))
.cloned(),
Expression::Block(b) => self.evaluate_block(b.as_ref()),
Expression::Lambda {
parameters, result, ..
} => Ok(Value::Closure {
environment: self.environment.clone(),
parameters: parameters.iter().map(|p| p.name.clone()).collect(),
result: *result.clone(),
}),
Expression::Call { callee, arguments } => {
let callee = self.evaluate(callee.as_ref())?;
self.apply_call(&callee, arguments)
}
Expression::Annotation { expression, .. } => self.evaluate(expression.as_ref()),
Expression::Boolean(b) => Ok(Value::Boolean(*b)),
Expression::Integer(i) => Ok(Value::Integer(*i)),
Expression::Unit => Ok(Value::Unit),
}
}
fn apply_call(&mut self, callee: &Value, arguments: &Vec<Expression>) -> Result<Value> {
match callee {
Value::Closure {
environment,
parameters,
result,
} => {
if parameters.len() != arguments.len() {
return Err(Error::ArgumentsMismatch(parameters.len(), arguments.len()));
}
let mut nested = Interpreter::nested(environment.clone());
for (name, argument) in parameters.iter().zip(arguments) {
// we don't want arguments to refer to each other, so use the parent interpreter
bind!(self, name.clone(), argument, to: nested)?;
}
nested.evaluate(result)
}
Value::Builtin(b) => {
if arguments.len() < b.min_parameters() {
return Err(Error::ArgumentsMismatch(
b.min_parameters(),
arguments.len(),
));
} else if let Some(max) = b.max_parameters() {
if arguments.len() > max {
return Err(Error::ArgumentsMismatch(max, arguments.len()));
}
}
let arguments = arguments
.iter()
.map(|term| self.evaluate(term))
.collect::<Result<Vec<_>>>()?;
Ok(b.call(arguments))
}
_ => Err(Error::NotAFunction),
}
}
fn evaluate_block(&mut self, block: &Block) -> Result<Value> {
let mut nested = self.clone();
for binding in &block.bindings {
bind!(nested, binding.name.clone(), &binding.expression)?;
}
nested.evaluate(&block.result)
}
}
impl Default for Interpreter {
fn default() -> Self {
Self::new()
}
}