diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..36327c7 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,7 @@ + +max_width = 110 +use_small_heuristics = "max" +imports_indent = "block" +imports_granularity = "crate" +group_imports = "stdexternalcrate" + diff --git a/schala-lang/language/src/tree_walk_eval/mod.rs b/schala-lang/language/src/tree_walk_eval/mod.rs index 42cdebe..5923c54 100644 --- a/schala-lang/language/src/tree_walk_eval/mod.rs +++ b/schala-lang/language/src/tree_walk_eval/mod.rs @@ -1,12 +1,14 @@ -use crate::reduced_ir::{ReducedIR, Expression, Lookup, Callable, FunctionDefinition, Statement, Literal, Alternative, Pattern}; -use crate::symbol_table::{DefId}; -use crate::util::ScopeStack; -use crate::builtin::Builtin; -use crate::typechecking::TypeId; +use std::{convert::From, fmt::Write, rc::Rc}; -use std::fmt::Write; -use std::rc::Rc; -use std::convert::From; +use crate::{ + builtin::Builtin, + reduced_ir::{ + Alternative, Callable, Expression, FunctionDefinition, Literal, Lookup, Pattern, ReducedIR, Statement, + }, + symbol_table::DefId, + typechecking::TypeId, + util::ScopeStack, +}; mod test; @@ -14,14 +16,14 @@ type EvalResult = Result; #[derive(Debug)] pub struct State<'a> { - environments: ScopeStack<'a, Memory, MemoryValue>, + environments: ScopeStack<'a, Memory, MemoryValue>, } //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) + Index(u32), } // This is for function param lookups, and is a hack @@ -39,22 +41,18 @@ impl From<&DefId> for Memory { #[derive(Debug)] struct RuntimeError { - msg: String + msg: String, } impl From for RuntimeError { fn from(msg: String) -> Self { - Self { - msg - } + Self { msg } } } impl From<&str> for RuntimeError { fn from(msg: &str) -> Self { - Self { - msg: msg.to_string(), - } + Self { msg: msg.to_string() } } } @@ -65,17 +63,17 @@ impl RuntimeError { } } -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 +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 } /// Anything that can be stored in memory; that is, a function definition, or a fully-evaluated @@ -92,7 +90,6 @@ impl From for MemoryValue { } } - impl MemoryValue { fn to_repl(&self) -> String { match self { @@ -126,26 +123,23 @@ enum Primitive { Tuple(Vec), Literal(Literal), Callable(Callable), - Object { - type_id: TypeId, - tag: u32, - items: Vec - }, + Object { type_id: TypeId, tag: u32, items: Vec }, } impl Primitive { fn to_repl(&self) -> String { match self { Primitive::Object { type_id, items, .. } if items.is_empty() => type_id.local_name().to_string(), - Primitive::Object { type_id, items, .. } => - format!("{}{}", type_id.local_name(), paren_wrapped(items.iter().map(|item| item.to_repl()))), + Primitive::Object { type_id, items, .. } => { + format!("{}{}", type_id.local_name(), paren_wrapped(items.iter().map(|item| item.to_repl()))) + } Primitive::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), - } + }, Primitive::Tuple(terms) => paren_wrapped(terms.iter().map(|x| x.to_repl())), Primitive::Callable(..) => "".to_string(), } @@ -164,9 +158,7 @@ impl From for Primitive { impl<'a> State<'a> { pub fn new() -> Self { - Self { - environments: ScopeStack::new(Some("global".to_string())) - } + Self { environments: ScopeStack::new(Some("global".to_string())) } } pub fn evaluate(&mut self, reduced: ReducedIR, repl: bool) -> Vec> { @@ -179,9 +171,7 @@ impl<'a> State<'a> { for statement in reduced.entrypoint.into_iter() { match self.statement(statement) { - Ok(Some(output)) if repl => { - acc.push(Ok(output.to_repl())) - }, + Ok(Some(output)) if repl => acc.push(Ok(output.to_repl())), Ok(_) => (), Err(error) => { acc.push(Err(error.msg)); @@ -200,11 +190,7 @@ impl<'a> State<'a> { ret = Some(prim); } } - Ok(if let Some(ret) = ret { - ret - } else { - self.expression(Expression::unit())? - }) + Ok(if let Some(ret) = ret { ret } else { self.expression(Expression::unit())? }) } fn statement(&mut self, stmt: Statement) -> EvalResult> { @@ -214,7 +200,7 @@ impl<'a> State<'a> { 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())) @@ -222,252 +208,286 @@ impl<'a> State<'a> { } } - 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.clone())), - 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 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.clone())) + } + 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(&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)?; + 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.environments.new_scope(None); - if matches(&cond, &alt.pattern, &mut new_scope) { - let mut new_state = State { - environments: new_scope - }; + 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()) - } + 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()); - } + 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!() - } - } - } + 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; + 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?; + 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(), + 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(), + /* 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(), + (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(), + (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(), + (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(), + (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()), - }) - } + (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)?); - } + 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) - }; + 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)); - } + 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) - } + frame_state.block(body) + } } - diff --git a/schala-lang/language/src/tree_walk_eval/test.rs b/schala-lang/language/src/tree_walk_eval/test.rs index 3a434b6..2fe2912 100644 --- a/schala-lang/language/src/tree_walk_eval/test.rs +++ b/schala-lang/language/src/tree_walk_eval/test.rs @@ -1,20 +1,19 @@ #![cfg(test)] use test_case::test_case; -use crate::symbol_table::SymbolTable; -use crate::tree_walk_eval::State; +use crate::{symbol_table::SymbolTable, tree_walk_eval::State}; 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 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); - outputs.pop().unwrap() + let ast = crate::util::quick_ast(input); + let mut symbol_table = SymbolTable::new(); + symbol_table.process_ast(&ast).unwrap(); + 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); + outputs.pop().unwrap() } fn eval_assert(input: &str, expected: &str) { @@ -23,26 +22,26 @@ fn eval_assert(input: &str, expected: &str) { #[test] fn test_basic_eval() { - eval_assert("1 + 2", "3"); - eval_assert("let mut a = 1; a = 2", "()"); - eval_assert("let mut a = 1; a = a + 2; a", "3"); + eval_assert("1 + 2", "3"); + eval_assert("let mut a = 1; a = 2", "()"); + eval_assert("let mut a = 1; a = a + 2; a", "3"); } #[test] fn op_eval() { - eval_assert("- 13", "-13"); - eval_assert("10 - 2", "8"); + eval_assert("- 13", "-13"); + eval_assert("10 - 2", "8"); } #[test] fn function_eval() { - eval_assert("fn oi(x) { x + 1 }; oi(4)", "5"); - eval_assert("fn oi(x) { x + 1 }; oi(1+2)", "4"); + eval_assert("fn oi(x) { x + 1 }; oi(4)", "5"); + eval_assert("fn oi(x) { x + 1 }; oi(1+2)", "4"); } #[test] fn scopes() { - let scope_ok = r#" + let scope_ok = r#" let a = 20 fn haha() { let something = 38 @@ -79,7 +78,7 @@ let b = Option::Some(10) #[test] fn basic_if_statement() { - let source = r#" + let source = r#" let a = 10 let b = 10 if a == b then { 69 } else { 420 } @@ -89,7 +88,7 @@ fn basic_if_statement() { #[test] fn basic_patterns_1() { - let source =r#" + let source = r#" let x = 10 let a = if x is 10 then { 255 } else { 256 } let b = if 23 is 99 then { 255 } else { 256 } @@ -106,14 +105,16 @@ let d = if "xxx" is "yyy" then { 20 } else { 30 } #[test_case("cyrus", "4")] fn basic_patterns_2(input: &str, expected: &str) { let mut source = format!(r#"let x = "{}""#, input); - source.push_str(r#" + source.push_str( + r#" if x { is "sanchez" then 1 is "mouri" then 2 is "hella" then 3 is _ then 4 } -"#); +"#, + ); eval_assert(&source, expected); } @@ -121,31 +122,33 @@ if x { #[test_case(r#"(99, "panda", false, -2.45)"#, r#""maybe""#)] fn tuple_patterns(input: &str, expected: &str) { let mut source = format!("let x = {}", input); - source.push_str(r#" + source.push_str( + r#" if x { is (45, "pablo", _, 28.4) then "no" is (_, "panda", _, 2.2) then "yes" is _ then "maybe" -}"#); +}"#, + ); eval_assert(&source, expected); } #[test] fn if_is_patterns() { - let source = r#" + let source = r#" type Option = Some(T) | None let q = "a string" let x = Option::Some(9); if x is Option::Some(q) then { q } else { 0 }"#; - eval_assert(source, "9"); + eval_assert(source, "9"); - let source = r#" + let source = r#" type Option = Some(T) | None let q = "a string" let outer = 2 let x = Option::None; if x is Option::Some(q) then { q } else { -2 + outer }"#; - eval_assert(source, "0"); + eval_assert(source, "0"); } #[test] @@ -189,7 +192,7 @@ if a { is "foo" then "x", is _ then "y" } #[test] fn boolean_pattern() { - let source = r#" + let source = r#" let a = true if a { is true then "x", @@ -201,7 +204,7 @@ if a { #[test] fn boolean_pattern_2() { - let source = r#" + let source = r#" let a = false if a { is true then "x", is false then "y" } "#; @@ -210,7 +213,7 @@ if a { is true then "x", is false then "y" } #[test] fn ignore_pattern() { - let source = r#" + let source = r#" type Option = Some(T) | None if Option::Some(10) { is _ then "hella" @@ -221,7 +224,7 @@ if Option::Some(10) { #[test] fn tuple_pattern() { - let source = r#" + let source = r#" if (1, 2) { is (1, x) then x, is _ then 99 @@ -230,10 +233,9 @@ if (1, 2) { eval_assert(source, "2"); } - #[test] fn tuple_pattern_2() { - let source = r#" + let source = r#" if (1, 2) { is (10, x) then x, is (y, x) then x + y @@ -244,7 +246,7 @@ if (1, 2) { #[test] fn tuple_pattern_3() { - let source = r#" + let source = r#" if (1, 5) { is (10, x) then x, is (1, x) then x @@ -255,7 +257,7 @@ if (1, 5) { #[test] fn tuple_pattern_4() { - let source = r#" + let source = r#" if (1, 5) { is (10, x) then x, is (1, x) then x, @@ -264,10 +266,9 @@ if (1, 5) { eval_assert(source, "5"); } - #[test] -fn prim_obj_pattern() { - let source = r#" +fn prim_obj_pattern() { + let source = r#" type Stuff = Mulch(Nat) | Jugs(Nat, String) | Mardok let a = Stuff::Mulch(20) let b = Stuff::Jugs(1, "haha") @@ -305,26 +306,26 @@ let y = \(m, n, o) { m + n + o }(1,2,3) (x, y) "#; -eval_assert(source, r"(10, 6)"); + eval_assert(source, r"(10, 6)"); } #[test] fn basic_lambda_evaluation_2() { - let source = r#" + let source = r#" fn milta() { \(x) { x + 33 } } milta()(10) "#; - eval_assert(source, "43"); - } + eval_assert(source, "43"); +} #[test] fn import_all() { - let source = r#" + let source = r#" type Option = Some(T) | None import Option::* let x = Some(9); if x is Some(q) then { q } else { 0 }"#; - eval_assert(source, "9"); + eval_assert(source, "9"); }