todo: arbitrary ir
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user