use crate::reduced_ir::{ReducedIR, Expression, Lookup, Function, FunctionDefinition, Statement, Literal}; use crate::symbol_table::{DefId}; use crate::util::ScopeStack; use crate::builtin::Builtin; use std::fmt::Write; use std::rc::Rc; use std::convert::From; type EvalResult = Result; #[derive(Debug)] pub struct State<'a> { environments: ScopeStack<'a, Memory, RuntimeValue>, } //TODO - eh, I dunno, maybe it doesn't matter exactly how memory works in the tree-walking //evaluator #[derive(Debug, PartialEq, Eq, Hash, Clone)] enum Memory { Index(u32) } // This is for function param lookups, and is a hack impl From for Memory { fn from(n: u8) -> Self { Memory::Index(4_000_000 + (n as u32)) } } impl From<&DefId> for Memory { fn from(id: &DefId) -> Self { Self::Index(id.as_u32()) } } #[derive(Debug)] struct RuntimeError { msg: String } impl From for RuntimeError { fn from(msg: String) -> Self { Self { msg } } } impl RuntimeError { fn get_msg(&self) -> String { format!("Runtime error: {}", self.msg) } } fn paren_wrapped(terms: impl Iterator) -> String { let mut buf = String::new(); write!(buf, "(").unwrap(); for term in terms.map(Some).intersperse(None) { match term { Some(e) => write!(buf, "{}", e).unwrap(), None => write!(buf, ", ").unwrap(), }; } write!(buf, ")").unwrap(); buf } #[derive(Debug)] enum RuntimeValue { Function(FunctionDefinition), Primitive(Primitive), } impl From for RuntimeValue { fn from(prim: Primitive) -> Self { Self::Primitive(prim) } } fn expr_to_repl(expr: &Expression) -> String { match expr { Expression::Literal(lit) => match lit { Literal::Nat(n) => format!("{}", n), Literal::Int(i) => format!("{}", i), Literal::Float(f) => format!("{}", f), Literal::Bool(b) => format!("{}", b), Literal::StringLit(s) => format!("\"{}\"", s), } Expression::Tuple(terms) => paren_wrapped(terms.iter().map(|x| expr_to_repl(x))), Expression::Assign { lval, rval } => { "".to_string() }, e => format!("Expression {:?} shouldn't be here", e), } } impl RuntimeValue { fn to_repl(&self) -> String { match self { RuntimeValue::Primitive(ref prim) => expr_to_repl(&prim.to_expr()), RuntimeValue::Function(ref expr) => "".to_string(), } } } /// A fully-reduced value #[derive(Debug, Clone)] enum Primitive { Tuple(Vec), Literal(Literal), Callable(Function), /* PrimObject { name: Rc, tag: usize, items: Vec, }, */ } impl From for Primitive { fn from(lit: Literal) -> Self { Primitive::Literal(lit) } } impl Primitive { fn to_expr(&self) -> Expression { match self { Primitive::Tuple(items) => Expression::Tuple(items.iter().map(|item| item.to_expr()).collect()), Primitive::Literal(lit) => Expression::Literal(lit.clone()), Primitive::Callable(function) => Expression::Callable(function.clone()), } } } impl<'a> State<'a> { pub fn new() -> Self { Self { environments: ScopeStack::new(Some("global".to_string())) } } pub fn evaluate(&mut self, reduced: ReducedIR, repl: bool) -> Vec> { let mut acc = vec![]; for (def_id, function) in reduced.functions.into_iter() { let mem = (&def_id).into(); self.environments.insert(mem, RuntimeValue::Function(function)); } for statement in reduced.entrypoint.into_iter() { match self.statement(statement) { Ok(Some(output)) if repl => { acc.push(Ok(output.to_repl())) }, Ok(_) => (), Err(error) => { acc.push(Err(error.into())); return acc; } } } acc } fn block(&mut self, statements: Vec) -> EvalResult { //TODO need to handle breaks, returns, etc. let mut ret = None; for stmt in statements.into_iter() { if let Some(RuntimeValue::Primitive(prim)) = self.statement(stmt)? { ret = Some(prim); } } Ok(if let Some(ret) = ret { ret } else { self.expression(Expression::unit())? }) } fn statement(&mut self, stmt: Statement) -> EvalResult> { match stmt { Statement::Binding { ref id, expr, constant } => { println!("eval() binding id: {}", id); let evaluated = self.expression(expr)?; self.environments.insert(id.into(), evaluated.into()); Ok(None) }, Statement::Expression(expr) => { let evaluated = self.expression(expr)?; Ok(Some(evaluated.into())) } } } fn expression(&mut self, expression: Expression) -> EvalResult { Ok(match expression { Expression::Literal(lit) => Primitive::Literal(lit), Expression::Tuple(items) => Primitive::Tuple(items.into_iter().map(|expr| self.expression(expr)).collect::>>()?), Expression::Lookup { ref id, kind } => { match kind { Lookup::Function => { let mem = id.into(); match self.environments.lookup(&mem) { // This just checks that the function exists in "memory" by ID, we don't // actually retrieve it until `apply_function()` Some(RuntimeValue::Function(_)) => Primitive::Callable(Function::UserDefined(id.clone())), x => return Err(format!("Function not found for id: {} : {:?}", id, x)), } }, Lookup::Param(n) => { let mem = n.into(); match self.environments.lookup(&mem) { Some(RuntimeValue::Primitive(prim)) => prim.clone(), e => return Err(format!("Param lookup error, got {:?}", e)), } }, kind @ Lookup::LocalVar | kind @ Lookup::GlobalVar => { let mem = id.into(); match self.environments.lookup(&mem) { Some(RuntimeValue::Primitive(expr)) => expr.clone(), _ => return Err(format!("Nothing found for variable lookup {} of kind {:?}", id, kind)), } }, } }, Expression::Assign { ref lval, box rval } => { let mem = lval.into(); let mut env = self.environments.lookup(&mem); return Err("Assign not implemented".to_string()); }, Expression::Call { box f, args } => self.call_expression(f, args)?, Expression::Callable(func) => Primitive::Callable(func), Expression::ReductionError(e) => return Err(e.into()), e => return Err(format!("Can't yet handle {:?}", e)), }) } fn call_expression(&mut self, f: Expression, args: Vec) -> EvalResult { let func = match self.expression(f)? { Primitive::Callable(func) => func, other => return Err(format!("Trying to call non-function value: {:?}", other)), }; match func { Function::Builtin(builtin) => self.apply_builtin(builtin, args), Function::UserDefined(def_id) => { let mem = (&def_id).into(); match self.environments.lookup(&mem) { Some(RuntimeValue::Function(FunctionDefinition { body })) => { let body = body.clone(); //TODO ideally this clone would not happen self.apply_function(body, args) }, e => Err(format!("Error looking up function with id {}: {:?}", def_id, e)) } }, Function::Lambda { arity, body } => { if arity as usize != args.len() { return Err(format!("Lambda expression requries {} arguments, only {} provided", arity, args.len())); } let body = body.clone(); //TODO again ideally, no cloning here self.apply_function(body, args) } } } fn apply_builtin(&mut self, builtin: Builtin, args: Vec) -> EvalResult { use Builtin::*; use Literal::*; use Expression::Literal as Lit; let evaled_args: EvalResult> = args.into_iter().map(|arg| self.expression(arg).map(|prim| prim.to_expr())).collect(); let evaled_args = evaled_args?; Ok(match (builtin, evaled_args.as_slice()) { (FieldAccess, /*&[Node::PrimObject { .. }]*/ _) => { return Err("Field access unimplemented".to_string()); } (binop, &[ref lhs, ref rhs]) => match (binop, lhs, rhs) { (Add, Lit(Nat(l)), Lit(Nat(r))) => Nat(l + r).into(), (Concatenate, Lit(StringLit(ref s1)), Lit(StringLit(ref s2))) => StringLit(Rc::new(format!("{}{}", s1, s2))).into(), (Subtract, Lit(Nat(l)), Lit(Nat(r))) => Nat(l - r).into(), (Multiply, Lit(Nat(l)), Lit(Nat(r))) => Nat(l * r).into(), (Divide, Lit(Nat(l)), Lit(Nat(r))) => Float((*l as f64)/ (*r as f64)).into(), (Quotient, Lit(Nat(l)), Lit(Nat(r))) => if *r == 0 { return Err("Divide-by-zero error".to_string()); } else { Nat(l / r).into() }, (Modulo, Lit(Nat(l)), Lit(Nat(r))) => Nat(l % r).into(), (Exponentiation, Lit(Nat(l)), Lit(Nat(r))) => Nat(l ^ r).into(), (BitwiseAnd, Lit(Nat(l)), Lit(Nat(r))) => Nat(l & r).into(), (BitwiseOr, Lit(Nat(l)), Lit(Nat(r))) => Nat(l | r).into(), /* comparisons */ (Equality, Lit(Nat(l)), Lit(Nat(r))) => Bool(l == r).into(), (Equality, Lit(Int(l)), Lit(Int(r))) => Bool(l == r).into(), (Equality, Lit(Float(l)), Lit(Float(r))) => Bool(l == r).into(), (Equality, Lit(Bool(l)), Lit(Bool(r))) => Bool(l == r).into(), (Equality, Lit(StringLit(ref l)), Lit(StringLit(ref r))) => Bool(l == r).into(), (LessThan, Lit(Nat(l)), Lit(Nat(r))) => Bool(l < r).into(), (LessThan, Lit(Int(l)), Lit(Int(r))) => Bool(l < r).into(), (LessThan, Lit(Float(l)), Lit(Float(r))) => Bool(l < r).into(), (LessThanOrEqual, Lit(Nat(l)), Lit(Nat(r))) => Bool(l <= r).into(), (LessThanOrEqual, Lit(Int(l)), Lit(Int(r))) => Bool(l <= r).into(), (LessThanOrEqual, Lit(Float(l)), Lit(Float(r))) => Bool(l <= r).into(), (GreaterThan, Lit(Nat(l)), Lit(Nat(r))) => Bool(l > r).into(), (GreaterThan, Lit(Int(l)), Lit(Int(r))) => Bool(l > r).into(), (GreaterThan, Lit(Float(l)), Lit(Float(r))) => Bool(l > r).into(), (GreaterThanOrEqual, Lit(Nat(l)), Lit(Nat(r))) => Bool(l >= r).into(), (GreaterThanOrEqual, Lit(Int(l)), Lit(Int(r))) => Bool(l >= r).into(), (GreaterThanOrEqual, Lit(Float(l)), Lit(Float(r))) => Bool(l >= r).into(), (binop, lhs, rhs) => return Err(format!("Invalid binop expression {:?} {:?} {:?}", lhs, binop, rhs)), }, (prefix, &[ref arg]) => match (prefix, arg) { (BooleanNot, Lit(Bool(true))) => Bool(false), (BooleanNot, Lit(Bool(false))) => Bool(true), (Negate, Lit(Nat(n))) => Int(-(*n as i64)), (Negate, Lit(Int(n))) => Int(-(*n as i64)), (Increment, Lit(Int(n))) => Int(*n), (Increment, Lit(Nat(n))) => Nat(*n), _ => return Err("No valid prefix op".to_string()) }.into(), /* builtin functions */ (IOPrint, &[ref anything]) => { print!("{}", expr_to_repl(anything)); Primitive::Tuple(vec![]) }, (IOPrintLn, &[ref anything]) => { println!("{}", expr_to_repl(anything)); Primitive::Tuple(vec![]) }, (IOGetLine, &[]) => { let mut buf = String::new(); std::io::stdin().read_line(&mut buf).expect("Error readling line in 'getline'"); StringLit(Rc::new(buf.trim().to_string())).into() }, (x, args) => return Err(format!("bad or unimplemented builtin {:?} | {:?}", x, args)), }) } fn apply_function(&mut self, body: Vec, args: Vec) -> EvalResult { let mut evaluated_args: Vec = vec![]; for arg in args.into_iter() { evaluated_args.push(self.expression(arg)?); } let mut frame_state = State { environments: self.environments.new_scope(None) }; for (n, evaled) in evaluated_args.into_iter().enumerate() { let n = n as u8; let mem = n.into(); frame_state.environments.insert(mem, RuntimeValue::Primitive(evaled)); } frame_state.block(body) } }