87 lines
3.1 KiB
Rust
87 lines
3.1 KiB
Rust
use crate::eval::{EvalEnvironment, EvalError, Value};
|
|
use crate::ir::{Expression, Program, Statement};
|
|
|
|
use super::{Primitive, ValueOrRef};
|
|
|
|
impl Program {
|
|
/// Evaluate the program, returning either an error or a string containing everything
|
|
/// the program printed out.
|
|
///
|
|
/// The print outs will be newline separated, with one print out per line.
|
|
pub fn eval(&self) -> Result<String, EvalError> {
|
|
let mut env = EvalEnvironment::empty();
|
|
let mut stdout = String::new();
|
|
|
|
for stmt in self.statements.iter() {
|
|
match stmt {
|
|
Statement::Binding(_, name, value) => {
|
|
let actual_value = value.eval(&env)?;
|
|
env = env.extend(name.clone(), actual_value);
|
|
}
|
|
|
|
Statement::Print(_, name) => {
|
|
let value = env.lookup(name.clone())?;
|
|
let line = format!("{} = {}\n", name, value);
|
|
stdout.push_str(&line);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(stdout)
|
|
}
|
|
}
|
|
|
|
impl Expression {
|
|
fn eval(&self, env: &EvalEnvironment) -> Result<Value, EvalError> {
|
|
match self {
|
|
Expression::Value(_, v) => match v {
|
|
super::Value::Number(_, v) => Ok(Value::I64(*v)),
|
|
},
|
|
|
|
Expression::Reference(_, n) => Ok(env.lookup(n.clone())?),
|
|
|
|
Expression::Primitive(_, op, args) => {
|
|
let mut arg_values = Vec::with_capacity(args.len());
|
|
|
|
// we implement primitive operations by first evaluating each of the
|
|
// arguments to the function, and then gathering up all the values
|
|
// produced.
|
|
for arg in args.iter() {
|
|
match arg {
|
|
ValueOrRef::Ref(_, n) => arg_values.push(env.lookup(n.clone())?),
|
|
ValueOrRef::Value(_, super::Value::Number(_, v)) => {
|
|
arg_values.push(Value::I64(*v))
|
|
}
|
|
}
|
|
}
|
|
|
|
// and then finally we call `calculate` to run them. trust me, it's nice
|
|
// to not have to deal with all the nonsense hidden under `calculate`.
|
|
match op {
|
|
Primitive::Plus => Ok(Value::calculate("+", arg_values)?),
|
|
Primitive::Minus => Ok(Value::calculate("-", arg_values)?),
|
|
Primitive::Times => Ok(Value::calculate("*", arg_values)?),
|
|
Primitive::Divide => Ok(Value::calculate("/", arg_values)?),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn two_plus_three() {
|
|
let input = crate::syntax::Program::parse(0, "x = 2 + 3; print x;").expect("parse works");
|
|
let ir = Program::from(input.simplify());
|
|
let output = ir.eval().expect("runs successfully");
|
|
assert_eq!("x = 5i64\n", &output);
|
|
}
|
|
|
|
#[test]
|
|
fn lotsa_math() {
|
|
let input =
|
|
crate::syntax::Program::parse(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works");
|
|
let ir = Program::from(input.simplify());
|
|
let output = ir.eval().expect("runs successfully");
|
|
assert_eq!("x = 7i64\n", &output);
|
|
}
|