diff --git a/schala-lang/language/src/schala.rs b/schala-lang/language/src/schala.rs index 8001c8e..6e8ed37 100644 --- a/schala-lang/language/src/schala.rs +++ b/schala-lang/language/src/schala.rs @@ -91,7 +91,7 @@ impl<'a> Schala<'a> { let reduced_ir = reduced_ir::reduce(&ast, &self.symbol_table); - let evaluation_outputs = self.eval_state.evaluate(reduced_ir, true); + let evaluation_outputs = self.eval_state.evaluate(reduced_ir, &self.type_context, true); let text_output: Result, String> = evaluation_outputs.into_iter().collect(); let text_output: Result, SchalaError> = diff --git a/schala-lang/language/src/tree_walk_eval/evaluator.rs b/schala-lang/language/src/tree_walk_eval/evaluator.rs new file mode 100644 index 0000000..3aa35a4 --- /dev/null +++ b/schala-lang/language/src/tree_walk_eval/evaluator.rs @@ -0,0 +1,338 @@ +use std::rc::Rc; + +use super::{EvalResult, Memory, MemoryValue, Primitive, State}; +use crate::{ + builtin::Builtin, + reduced_ir::{ + Alternative, Callable, Expression, FunctionDefinition, Literal, Lookup, Pattern, ReducedIR, Statement, + }, + type_inference::TypeContext, + util::ScopeStack, +}; + +pub struct Evaluator<'a, 'b> { + pub type_context: &'b TypeContext, + pub state: &'b mut State<'a>, +} + +impl<'a, 'b> Evaluator<'a, 'b> { + 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.state.environments.insert(mem, MemoryValue::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.msg)); + 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(MemoryValue::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.state.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(kind) => match kind { + Lookup::Function(ref id) => { + let mem = id.into(); + match self.state.environments.lookup(&mem) { + // This just checks that the function exists in "memory" by ID, we don't + // actually retrieve it until `apply_function()` + Some(MemoryValue::Function(_)) => Primitive::Callable(Callable::UserDefined(*id)), + x => return Err(format!("Function not found for id: {} : {:?}", id, x).into()), + } + } + Lookup::Param(n) => { + let mem = n.into(); + match self.state.environments.lookup(&mem) { + Some(MemoryValue::Primitive(prim)) => prim.clone(), + e => return Err(format!("Param lookup error, got {:?}", e).into()), + } + } + Lookup::LocalVar(ref id) | Lookup::GlobalVar(ref id) => { + let mem = id.into(); + match self.state.environments.lookup(&mem) { + Some(MemoryValue::Primitive(expr)) => expr.clone(), + _ => + return Err( + format!("Nothing found for local/gloval variable lookup {}", id).into() + ), + } + } + }, + Expression::Assign { ref lval, box rval } => { + let mem = lval.into(); + let evaluated = self.expression(rval)?; + self.state.environments.insert(mem, MemoryValue::Primitive(evaluated)); + Primitive::unit() + } + Expression::Call { box f, args } => self.call_expression(f, args)?, + Expression::Callable(Callable::DataConstructor { type_id, arity, tag }) if arity == 0 => + Primitive::Object { type_id, tag, items: vec![] }, + Expression::Callable(func) => Primitive::Callable(func), + Expression::Conditional { box cond, then_clause, else_clause } => { + let cond = self.expression(cond)?; + match cond { + Primitive::Literal(Literal::Bool(true)) => self.block(then_clause)?, + Primitive::Literal(Literal::Bool(false)) => self.block(else_clause)?, + v => return Err(format!("Non-boolean value {:?} in if-statement", v).into()), + } + } + Expression::CaseMatch { box cond, alternatives } => + self.case_match_expression(cond, alternatives)?, + Expression::ReductionError(e) => return Err(e.into()), + }) + } + + fn case_match_expression( + &mut self, + cond: Expression, + alternatives: Vec, + ) -> EvalResult { + fn matches(scrut: &Primitive, pat: &Pattern, scope: &mut ScopeStack) -> bool { + match pat { + Pattern::Ignored => true, + Pattern::Binding(ref def_id) => { + let mem = def_id.into(); + scope.insert(mem, MemoryValue::Primitive(scrut.clone())); //TODO make sure this doesn't cause problems with nesting + true + } + Pattern::Literal(pat_literal) => + if let Primitive::Literal(scrut_literal) = scrut { + pat_literal == scrut_literal + } else { + false + }, + Pattern::Tuple { subpatterns, tag } => match tag { + None => match scrut { + Primitive::Tuple(items) if items.len() == subpatterns.len() => items + .iter() + .zip(subpatterns.iter()) + .all(|(item, subpat)| matches(item, subpat, scope)), + _ => false, //TODO should be a type error + }, + Some(pattern_tag) => match scrut { + //TODO should test type_ids for runtime type checking, once those work + Primitive::Object { tag, items, .. } + if tag == pattern_tag && items.len() == subpatterns.len() => + items + .iter() + .zip(subpatterns.iter()) + .all(|(item, subpat)| matches(item, subpat, scope)), + _ => false, + }, + }, + } + } + let cond = self.expression(cond)?; + + for alt in alternatives.into_iter() { + let mut new_scope = self.state.environments.new_scope(None); + if matches(&cond, &alt.pattern, &mut new_scope) { + let mut new_state = State { environments: new_scope }; + let mut evaluator = Evaluator { state: &mut new_state, type_context: self.type_context }; + + return evaluator.block(alt.item); + } + } + Err("No valid match in match expression".into()) + } + + 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).into()), + }; + match func { + Callable::Builtin(builtin) => self.apply_builtin(builtin, args), + Callable::UserDefined(def_id) => { + let mem = (&def_id).into(); + match self.state.environments.lookup(&mem) { + Some(MemoryValue::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).into()), + } + } + Callable::Lambda { arity, body } => { + if arity as usize != args.len() { + return Err(format!( + "Lambda expression requries {} arguments, only {} provided", + arity, + args.len() + ) + .into()); + } + self.apply_function(body, args) + } + Callable::DataConstructor { type_id, arity, tag } => { + if arity as usize != args.len() { + return Err(format!( + "Constructor expression requries {} arguments, only {} provided", + arity, + args.len() + ) + .into()); + } + + let mut evaluated_args: Vec = vec![]; + for arg in args.into_iter() { + evaluated_args.push(self.expression(arg)?); + } + Ok(Primitive::Object { type_id, tag, items: evaluated_args }) + } + Callable::RecordConstructor { type_id: _, tag: _ } => { + unimplemented!() + } + } + } + + fn apply_builtin(&mut self, builtin: Builtin, args: Vec) -> EvalResult { + use Builtin::*; + use Literal::*; + use Primitive::Literal as Lit; + + let evaled_args: EvalResult> = + args.into_iter().map(|arg| self.expression(arg)).collect(); + let evaled_args = evaled_args?; + + Ok(match (builtin, evaled_args.as_slice()) { + (FieldAccess, /*&[Node::PrimObject { .. }]*/ _) => { + return Err("Field access unimplemented".into()); + } + /* builtin functions */ + (IOPrint, &[ref anything]) => { + print!("{}", anything.to_repl()); + Primitive::Tuple(vec![]) + } + (IOPrintLn, &[ref anything]) => { + print!("{}", anything.to_repl()); + 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() + } + /* Binops */ + (binop, &[ref lhs, ref rhs]) => match (binop, lhs, rhs) { + // TODO need a better way of handling these literals + (Add, Lit(Nat(l)), Lit(Nat(r))) => Nat(l + r).into(), + (Add, Lit(Int(l)), Lit(Int(r))) => Int(l + r).into(), + (Add, Lit(Nat(l)), Lit(Int(r))) => Int((*l as i64) + (*r as i64)).into(), + (Add, Lit(Int(l)), Lit(Nat(r))) => Int((*l as i64) + (*r as i64)).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".into()); + } 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).into()), + }, + (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)), + (Negate, Lit(Float(f))) => Float(-(*f as f64)), + (Increment, Lit(Int(n))) => Int(*n), + (Increment, Lit(Nat(n))) => Nat(*n), + _ => return Err("No valid prefix op".into()), + } + .into(), + (x, args) => return Err(format!("bad or unimplemented builtin {:?} | {:?}", x, args).into()), + }) + } + + 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.state.environments.new_scope(None) }; + let mut evaluator = Evaluator { state: &mut frame_state, type_context: self.type_context }; + + for (n, evaled) in evaluated_args.into_iter().enumerate() { + let n = n as u8; + let mem = n.into(); + evaluator.state.environments.insert(mem, MemoryValue::Primitive(evaled)); + } + + evaluator.block(body) + } +} diff --git a/schala-lang/language/src/tree_walk_eval/mod.rs b/schala-lang/language/src/tree_walk_eval/mod.rs index f7c322d..72709ef 100644 --- a/schala-lang/language/src/tree_walk_eval/mod.rs +++ b/schala-lang/language/src/tree_walk_eval/mod.rs @@ -1,15 +1,13 @@ -use std::{convert::From, fmt::Write, rc::Rc}; +use std::{convert::From, fmt::Write}; use crate::{ - builtin::Builtin, - reduced_ir::{ - Alternative, Callable, Expression, FunctionDefinition, Literal, Lookup, Pattern, ReducedIR, Statement, - }, + reduced_ir::{Callable, Expression, FunctionDefinition, Literal, ReducedIR}, symbol_table::DefId, - type_inference::TypeId, + type_inference::{TypeContext, TypeId}, util::ScopeStack, }; +mod evaluator; mod test; type EvalResult = Result; @@ -161,333 +159,13 @@ impl<'a> State<'a> { 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, MemoryValue::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.msg)); - 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(MemoryValue::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(kind) => match kind { - Lookup::Function(ref id) => { - 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(MemoryValue::Function(_)) => { - Primitive::Callable(Callable::UserDefined(*id)) - } - x => return Err(format!("Function not found for id: {} : {:?}", id, x).into()), - } - } - Lookup::Param(n) => { - let mem = n.into(); - match self.environments.lookup(&mem) { - Some(MemoryValue::Primitive(prim)) => prim.clone(), - e => return Err(format!("Param lookup error, got {:?}", e).into()), - } - } - Lookup::LocalVar(ref id) | Lookup::GlobalVar(ref id) => { - let mem = id.into(); - match self.environments.lookup(&mem) { - Some(MemoryValue::Primitive(expr)) => expr.clone(), - _ => { - return Err( - format!("Nothing found for local/gloval variable lookup {}", id).into() - ) - } - } - } - }, - Expression::Assign { ref lval, box rval } => { - let mem = lval.into(); - let evaluated = self.expression(rval)?; - self.environments.insert(mem, MemoryValue::Primitive(evaluated)); - Primitive::unit() - } - Expression::Call { box f, args } => self.call_expression(f, args)?, - Expression::Callable(Callable::DataConstructor { type_id, arity, tag }) if arity == 0 => { - Primitive::Object { type_id, tag, items: vec![] } - } - Expression::Callable(func) => Primitive::Callable(func), - Expression::Conditional { box cond, then_clause, else_clause } => { - let cond = self.expression(cond)?; - match cond { - Primitive::Literal(Literal::Bool(true)) => self.block(then_clause)?, - Primitive::Literal(Literal::Bool(false)) => self.block(else_clause)?, - v => return Err(format!("Non-boolean value {:?} in if-statement", v).into()), - } - } - Expression::CaseMatch { box cond, alternatives } => { - self.case_match_expression(cond, alternatives)? - } - Expression::ReductionError(e) => return Err(e.into()), - }) - } - - fn case_match_expression( + pub fn evaluate( &mut self, - cond: Expression, - alternatives: Vec, - ) -> EvalResult { - fn matches(scrut: &Primitive, pat: &Pattern, scope: &mut ScopeStack) -> bool { - match pat { - Pattern::Ignored => true, - Pattern::Binding(ref def_id) => { - let mem = def_id.into(); - scope.insert(mem, MemoryValue::Primitive(scrut.clone())); //TODO make sure this doesn't cause problems with nesting - true - } - Pattern::Literal(pat_literal) => { - if let Primitive::Literal(scrut_literal) = scrut { - pat_literal == scrut_literal - } else { - false - } - } - Pattern::Tuple { subpatterns, tag } => match tag { - None => match scrut { - Primitive::Tuple(items) if items.len() == subpatterns.len() => items - .iter() - .zip(subpatterns.iter()) - .all(|(item, subpat)| matches(item, subpat, scope)), - _ => false, //TODO should be a type error - }, - Some(pattern_tag) => match scrut { - //TODO should test type_ids for runtime type checking, once those work - Primitive::Object { tag, items, .. } - if tag == pattern_tag && items.len() == subpatterns.len() => - { - items - .iter() - .zip(subpatterns.iter()) - .all(|(item, subpat)| matches(item, subpat, scope)) - } - _ => false, - }, - }, - } - } - let cond = self.expression(cond)?; - - for alt in alternatives.into_iter() { - let mut new_scope = self.environments.new_scope(None); - if matches(&cond, &alt.pattern, &mut new_scope) { - let mut new_state = State { environments: new_scope }; - - return new_state.block(alt.item); - } - } - Err("No valid match in match expression".into()) - } - - 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).into()), - }; - match func { - Callable::Builtin(builtin) => self.apply_builtin(builtin, args), - Callable::UserDefined(def_id) => { - let mem = (&def_id).into(); - match self.environments.lookup(&mem) { - Some(MemoryValue::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).into()), - } - } - Callable::Lambda { arity, body } => { - if arity as usize != args.len() { - return Err(format!( - "Lambda expression requries {} arguments, only {} provided", - arity, - args.len() - ) - .into()); - } - self.apply_function(body, args) - } - Callable::DataConstructor { type_id, arity, tag } => { - if arity as usize != args.len() { - return Err(format!( - "Constructor expression requries {} arguments, only {} provided", - arity, - args.len() - ) - .into()); - } - - let mut evaluated_args: Vec = vec![]; - for arg in args.into_iter() { - evaluated_args.push(self.expression(arg)?); - } - Ok(Primitive::Object { type_id, tag, items: evaluated_args }) - } - Callable::RecordConstructor { type_id: _, tag: _ } => { - unimplemented!() - } - } - } - - fn apply_builtin(&mut self, builtin: Builtin, args: Vec) -> EvalResult { - use Builtin::*; - use Literal::*; - use Primitive::Literal as Lit; - - let evaled_args: EvalResult> = - args.into_iter().map(|arg| self.expression(arg)).collect(); - let evaled_args = evaled_args?; - - Ok(match (builtin, evaled_args.as_slice()) { - (FieldAccess, /*&[Node::PrimObject { .. }]*/ _) => { - return Err("Field access unimplemented".into()); - } - /* builtin functions */ - (IOPrint, &[ref anything]) => { - print!("{}", anything.to_repl()); - Primitive::Tuple(vec![]) - } - (IOPrintLn, &[ref anything]) => { - print!("{}", anything.to_repl()); - 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() - } - /* Binops */ - (binop, &[ref lhs, ref rhs]) => match (binop, lhs, rhs) { - // TODO need a better way of handling these literals - (Add, Lit(Nat(l)), Lit(Nat(r))) => Nat(l + r).into(), - (Add, Lit(Int(l)), Lit(Int(r))) => Int(l + r).into(), - (Add, Lit(Nat(l)), Lit(Int(r))) => Int((*l as i64) + (*r as i64)).into(), - (Add, Lit(Int(l)), Lit(Nat(r))) => Int((*l as i64) + (*r as i64)).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".into()); - } 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).into()) - } - }, - (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)), - (Negate, Lit(Float(f))) => Float(-(*f as f64)), - (Increment, Lit(Int(n))) => Int(*n), - (Increment, Lit(Nat(n))) => Nat(*n), - _ => return Err("No valid prefix op".into()), - } - .into(), - (x, args) => return Err(format!("bad or unimplemented builtin {:?} | {:?}", x, args).into()), - }) - } - - 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, MemoryValue::Primitive(evaled)); - } - - frame_state.block(body) + reduced: ReducedIR, + type_context: &TypeContext, + repl: bool, + ) -> Vec> { + let mut evaluator = evaluator::Evaluator { state: self, type_context }; + evaluator.evaluate(reduced, repl) } } diff --git a/schala-lang/language/src/tree_walk_eval/test.rs b/schala-lang/language/src/tree_walk_eval/test.rs index 2fe2912..3671332 100644 --- a/schala-lang/language/src/tree_walk_eval/test.rs +++ b/schala-lang/language/src/tree_walk_eval/test.rs @@ -1,18 +1,27 @@ #![cfg(test)] use test_case::test_case; -use crate::{symbol_table::SymbolTable, tree_walk_eval::State}; +use crate::{ + symbol_table::SymbolTable, + tree_walk_eval::{evaluator::Evaluator, State}, + type_inference::TypeContext, +}; fn evaluate_input(input: &str) -> Result { let ast = crate::util::quick_ast(input); let mut symbol_table = SymbolTable::new(); symbol_table.process_ast(&ast).unwrap(); + + let type_context = TypeContext::new(); + let reduced_ir = crate::reduced_ir::reduce(&ast, &symbol_table); reduced_ir.debug(&symbol_table); println!("========"); symbol_table.debug(); + let mut state = State::new(); - let mut outputs = state.evaluate(reduced_ir, true); + let mut evaluator = Evaluator { state: &mut state, type_context: &type_context }; + let mut outputs = evaluator.evaluate(reduced_ir, true); outputs.pop().unwrap() }