todo: arbitrary ir

This commit is contained in:
2023-12-03 17:32:37 -08:00
parent 93cac44a99
commit 2c2268925a
16 changed files with 298 additions and 163 deletions

View File

@@ -1,11 +1,12 @@
use crate::eval::{EvalError, PrimitiveType, Value};
use crate::syntax::{ConstantType, Expression, Name, Program, Statement, TopLevel};
use crate::util::scoped_map::ScopedMap;
use internment::ArcIntern;
use crate::eval::{EvalEnvironment, EvalError, PrimitiveType, Value};
use crate::syntax::{ConstantType, Expression, Program, Statement, TopLevel};
use std::str::FromStr;
impl Program {
/// Evaluate the program, returning either an error or what it prints out when run.
/// Evaluate the program, returning either an error or a pair of the final value
/// produced and the output printed to the console.
///
/// Doing this evaluation is particularly useful for testing, to ensure that if we
/// modify a program in some way it does the same thing on both sides of the
@@ -15,36 +16,51 @@ impl Program {
/// Note that the errors here are slightly more strict that we enforce at runtime.
/// For example, we check for overflow and underflow errors during evaluation, and
/// we don't check for those in the compiled code.
pub fn eval(&self) -> Result<String, EvalError> {
let mut env = EvalEnvironment::empty();
pub fn eval(&self) -> Result<(Value<Expression>, String), EvalError<Expression>> {
let mut env = ScopedMap::new();
let mut stdout = String::new();
let mut last_result = Value::Void;
for stmt in self.items.iter() {
match stmt {
TopLevel::Function(_name, _arg_names, _body) => {
unimplemented!()
}
// at this point, evaluation is pretty simple. just walk through each
// statement, in order, and record printouts as we come to them.
TopLevel::Statement(Statement::Binding(_, name, value)) => {
let actual_value = value.eval(&env)?;
env = env.extend(name.clone().intern(), actual_value);
TopLevel::Function(name, arg_names, body) => {
last_result = Value::Closure(
name.clone().map(Name::intern),
env.clone(),
arg_names.iter().cloned().map(Name::intern).collect(),
body.clone(),
);
if let Some(name) = name {
env.insert(name.clone().intern(), last_result.clone());
}
}
TopLevel::Statement(Statement::Print(_, name)) => {
let value = env.lookup(name.clone().intern())?;
TopLevel::Statement(Statement::Binding(_, name, value)) => {
let actual_value = value.eval(&env)?;
env.insert(name.clone().intern(), actual_value);
last_result = Value::Void;
}
TopLevel::Statement(Statement::Print(loc, name)) => {
let value = env
.get(&name.clone().intern())
.ok_or_else(|| EvalError::LookupFailed(loc.clone(), name.name.clone()))?;
let line = format!("{} = {}\n", name, value);
stdout.push_str(&line);
last_result = Value::Void;
}
}
}
Ok(stdout)
Ok((last_result, stdout))
}
}
impl Expression {
fn eval(&self, env: &EvalEnvironment) -> Result<Value, EvalError> {
fn eval(
&self,
env: &ScopedMap<ArcIntern<String>, Value<Expression>>,
) -> Result<Value<Expression>, EvalError<Expression>> {
match self {
Expression::Value(_, v) => match v {
super::Value::Number(_, ty, v) => match ty {
@@ -61,7 +77,10 @@ impl Expression {
},
},
Expression::Reference(_, n) => Ok(env.lookup(ArcIntern::new(n.clone()))?),
Expression::Reference(loc, n) => env
.get(&ArcIntern::new(n.clone()))
.ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.clone()))
.cloned(),
Expression::Cast(_, target, expr) => {
let target_type = PrimitiveType::from_str(target)?;
@@ -86,13 +105,13 @@ impl Expression {
#[test]
fn two_plus_three() {
let input = Program::parse(0, "x = 2 + 3; print x;").expect("parse works");
let output = input.eval().expect("runs successfully");
let (_, output) = input.eval().expect("runs successfully");
assert_eq!("x = 5u64\n", &output);
}
#[test]
fn lotsa_math() {
let input = Program::parse(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works");
let output = input.eval().expect("runs successfully");
let (_, output) = input.eval().expect("runs successfully");
assert_eq!("x = 7u64\n", &output);
}