diff --git a/schala-lang/language/src/eval/mod.rs b/schala-lang/language/src/eval/mod.rs deleted file mode 100644 index 16d537d..0000000 --- a/schala-lang/language/src/eval/mod.rs +++ /dev/null @@ -1,444 +0,0 @@ -use std::rc::Rc; -use std::fmt::Write; -use std::io; - -use crate::util::ScopeStack; -use crate::reduced_ast::{BoundVars, ReducedAST, Stmt, Expr, Lit, Func, Alternative, Pattern}; -use crate::builtin::Builtin; - -mod test; - -pub struct State<'a> { - values: ScopeStack<'a, Rc, ValueEntry>, -} - -impl<'a> State<'a> { - pub fn new() -> State<'a> { - let values = ScopeStack::new(Some("global".to_string())); - State { values } - } - - #[allow(clippy::ptr_arg)] - fn new_frame(&'a self, items: &'a [Node], bound_vars: &BoundVars) -> State<'a> { - let mut inner_state = State { - values: self.values.new_scope(None), - }; - for (bound_var, val) in bound_vars.iter().zip(items.iter()) { - if let Some(bv) = bound_var.as_ref() { - inner_state.values.insert(bv.clone(), ValueEntry::Binding { constant: true, val: val.clone() }); - } - } - inner_state - } -} - -#[derive(Debug, Clone)] -enum Node { - Expr(Expr), - PrimObject { - name: Rc, - tag: usize, - items: Vec, - }, - PrimTuple { - items: Vec - } -} - -fn paren_wrapped_vec(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 -} - - -impl Node { - fn to_repl(&self) -> String { - match self { - Node::Expr(e) => e.to_repl(), - Node::PrimObject { name, items, .. } if items.is_empty() => format!("{}", name), - Node::PrimObject { name, items, .. } => format!("{}{}", name, paren_wrapped_vec(items.iter().map(|x| x.to_repl()))), - Node::PrimTuple { items } => paren_wrapped_vec(items.iter().map(|x| x.to_repl())), - } - } - fn is_true(&self) -> bool { - matches!(self, Node::Expr(Expr::Lit(crate::reduced_ast::Lit::Bool(true)))) - } -} - -#[derive(Debug)] -enum ValueEntry { - Binding { - constant: bool, - val: /*FullyEvaluatedExpr*/ Node, //TODO make this use a subtype to represent fully evaluatedness - } -} - -type EvalResult = Result; - -impl Expr { - #[allow(clippy::wrong_self_convention)] - fn to_node(self) -> Node { - Node::Expr(self) - } - fn to_repl(&self) -> String { - use self::Lit::*; - use self::Func::*; - - match self { - Expr::Lit(ref l) => match l { - Nat(n) => format!("{}", n), - Int(i) => format!("{}", i), - Float(f) => format!("{}", f), - Bool(b) => format!("{}", b), - StringLit(s) => format!("\"{}\"", s), - }, - Expr::Func(f) => match f { - BuiltIn(builtin) => format!("", builtin), - UserDefined { name: None, .. } => "".to_string(), - UserDefined { name: Some(name), .. } => format!("", name), - }, - Expr::Constructor { type_name, arity, .. } => { - format!("", type_name, arity) - }, - Expr::Tuple(exprs) => paren_wrapped_vec(exprs.iter().map(|x| x.to_repl())), - _ => format!("{:?}", self), - } - } - - fn replace_conditional_target_sigil(self, replacement: &Expr) -> Expr { - use self::Expr::*; - - match self { - ConditionalTargetSigilValue => replacement.clone(), - Lit(_) | Func(_) | Sym(_) | Constructor { .. } | - CaseMatch { .. } | UnimplementedSigilValue | ReductionError(_) => self, - Tuple(exprs) => Tuple(exprs.into_iter().map(|e| e.replace_conditional_target_sigil(replacement)).collect()), - Call { f, args } => { - let new_args = args.into_iter().map(|e| e.replace_conditional_target_sigil(replacement)).collect(); - Call { f, args: new_args } - }, - Conditional { .. } => panic!("Dunno if I need this, but if so implement"), - Assign { .. } => panic!("I'm pretty sure I don't need this"), - } - } -} - -impl<'a> State<'a> { - pub fn evaluate(&mut self, ast: ReducedAST, repl: bool) -> Vec> { - let mut acc = vec![]; - - // handle prebindings - for statement in ast.0.iter() { - self.prebinding(statement); - } - - for statement in ast.0 { - match self.statement(statement) { - Ok(Some(ref output)) if repl => { - acc.push(Ok(output.to_repl())) - }, - Ok(_) => (), - Err(error) => { - acc.push(Err(format!("Runtime error: {}", error))); - return acc; - }, - } - } - acc - } - - fn prebinding(&mut self, stmt: &Stmt) { - match stmt { - Stmt::PreBinding { name, func } => { - let v_entry = ValueEntry::Binding { constant: true, val: Node::Expr(Expr::Func(func.clone())) }; - self.values.insert(name.clone(), v_entry); - }, - Stmt::Expr(_expr) => { - //TODO have this support things like nested function defs - - }, - _ => () - } - } - - fn statement(&mut self, stmt: Stmt) -> EvalResult> { - match stmt { - Stmt::Binding { name, constant, expr } => { - let val = self.expression(Node::Expr(expr))?; - self.values.insert(name, ValueEntry::Binding { constant, val }); - Ok(None) - }, - Stmt::Expr(expr) => Ok(Some(self.expression(expr.to_node())?)), - Stmt::PreBinding {..} | Stmt::Noop => Ok(None), - } - } - - fn block(&mut self, stmts: Vec) -> EvalResult { - let mut ret = None; - for stmt in stmts { - ret = self.statement(stmt)?; - } - Ok(ret.unwrap_or_else(|| Node::Expr(Expr::unit()))) - } - - fn expression(&mut self, node: Node) -> EvalResult { - use self::Expr::*; - match node { - t @ Node::PrimTuple { .. } => Ok(t), - obj @ Node::PrimObject { .. } => Ok(obj), - Node::Expr(expr) => match expr { - literal @ Lit(_) => Ok(Node::Expr(literal)), - Call { box f, args } => self.call_expression(f, args), - Sym(name) => Ok(match self.values.lookup(&name) { - Some(ValueEntry::Binding { val, .. }) => val.clone(), - None => return Err(format!("Could not look up symbol {}", name)) - }), - Constructor { arity, ref name, tag, .. } if arity == 0 => Ok(Node::PrimObject { name: name.clone(), tag, items: vec![] }), - constructor @ Constructor { .. } => Ok(Node::Expr(constructor)), - func @ Func(_) => Ok(Node::Expr(func)), - Tuple(exprs) => { - let nodes = exprs.into_iter().map(|expr| self.expression(Node::Expr(expr))).collect::,_>>()?; - Ok(Node::PrimTuple { items: nodes }) - }, - Conditional { box cond, then_clause, else_clause } => self.conditional(cond, then_clause, else_clause), - Assign { box val, box expr } => self.assign_expression(val, expr), - CaseMatch { box cond, alternatives } => self.case_match_expression(cond, alternatives), - ConditionalTargetSigilValue => Ok(Node::Expr(ConditionalTargetSigilValue)), - UnimplementedSigilValue => Err("Sigil value eval not implemented".to_string()), - ReductionError(err) => Err(format!("Reduction error: {}", err)), - } - } - } - - fn call_expression(&mut self, f: Expr, args: Vec) -> EvalResult { - use self::Expr::*; - match self.expression(Node::Expr(f))? { - Node::Expr(Constructor { type_name, name, tag, arity }) => self.apply_data_constructor(type_name, name, tag, arity, args), - Node::Expr(Func(f)) => self.apply_function(f, args), - other => return Err(format!("Tried to call {:?} which is not a function or data constructor", other)), - } - } - - fn apply_data_constructor(&mut self, _type_name: Rc, name: Rc, tag: usize, arity: usize, args: Vec) -> EvalResult { - if arity != args.len() { - return Err(format!("Data constructor {} requires {} arg(s)", name, arity)); - } - - let evaled_args = args.into_iter().map(|expr| self.expression(Node::Expr(expr))).collect::,_>>()?; - //let evaled_args = vec![]; - Ok(Node::PrimObject { - name, - items: evaled_args, - tag - }) - } - - fn apply_function(&mut self, f: Func, args: Vec) -> EvalResult { - match f { - Func::BuiltIn(builtin) => Ok(self.apply_builtin(builtin, args)?), - Func::UserDefined { params, body, name } => { - - if params.len() != args.len() { - return Err(format!("calling a {}-argument function with {} args", params.len(), args.len())) - } - let mut func_state = State { - values: self.values.new_scope(name.map(|n| format!("{}", n))), - }; - for (param, val) in params.into_iter().zip(args.into_iter()) { - let val = func_state.expression(Node::Expr(val))?; - func_state.values.insert(param, ValueEntry::Binding { constant: true, val }); - } - // TODO figure out function return semantics - func_state.block(body) - } - } - } - - fn apply_builtin(&mut self, builtin: Builtin, args: Vec) -> EvalResult { - use self::Expr::*; - use self::Lit::*; - use Builtin::*; - - let evaled_args: Result, String> = args.into_iter().map(|arg| self.expression(arg.to_node())) - .collect(); - let evaled_args = evaled_args?; - - Ok(match (builtin, evaled_args.as_slice()) { - (FieldAccess, &[Node::PrimObject { .. }]) => { - //TODO implement field access - unimplemented!() - }, - (binop, &[Node::Expr(ref lhs), Node::Expr(ref rhs)]) => match (binop, lhs, rhs) { - /* binops */ - (Add, Lit(Nat(l)), Lit(Nat(r))) => Lit(Nat(l + r)), - (Concatenate, Lit(StringLit(ref s1)), Lit(StringLit(ref s2))) => Lit(StringLit(Rc::new(format!("{}{}", s1, s2)))), - (Subtract, Lit(Nat(l)), Lit(Nat(r))) => Lit(Nat(l - r)), - (Multiply, Lit(Nat(l)), Lit(Nat(r))) => Lit(Nat(l * r)), - (Divide, Lit(Nat(l)), Lit(Nat(r))) => Lit(Float((*l as f64)/ (*r as f64))), - (Quotient, Lit(Nat(l)), Lit(Nat(r))) => if *r == 0 { - return Err("Divide-by-zero error".to_string()); - } else { - Lit(Nat(l / r)) - }, - (Modulo, Lit(Nat(l)), Lit(Nat(r))) => Lit(Nat(l % r)), - (Exponentiation, Lit(Nat(l)), Lit(Nat(r))) => Lit(Nat(l ^ r)), - (BitwiseAnd, Lit(Nat(l)), Lit(Nat(r))) => Lit(Nat(l & r)), - (BitwiseOr, Lit(Nat(l)), Lit(Nat(r))) => Lit(Nat(l | r)), - - /* comparisons */ - (Equality, Lit(Nat(l)), Lit(Nat(r))) => Lit(Bool(l == r)), - (Equality, Lit(Int(l)), Lit(Int(r))) => Lit(Bool(l == r)), - (Equality, Lit(Float(l)), Lit(Float(r))) => Lit(Bool(l == r)), - (Equality, Lit(Bool(l)), Lit(Bool(r))) => Lit(Bool(l == r)), - (Equality, Lit(StringLit(ref l)), Lit(StringLit(ref r))) => Lit(Bool(l == r)), - - (LessThan, Lit(Nat(l)), Lit(Nat(r))) => Lit(Bool(l < r)), - (LessThan, Lit(Int(l)), Lit(Int(r))) => Lit(Bool(l < r)), - (LessThan, Lit(Float(l)), Lit(Float(r))) => Lit(Bool(l < r)), - - (LessThanOrEqual, Lit(Nat(l)), Lit(Nat(r))) => Lit(Bool(l <= r)), - (LessThanOrEqual, Lit(Int(l)), Lit(Int(r))) => Lit(Bool(l <= r)), - (LessThanOrEqual, Lit(Float(l)), Lit(Float(r))) => Lit(Bool(l <= r)), - - (GreaterThan, Lit(Nat(l)), Lit(Nat(r))) => Lit(Bool(l > r)), - (GreaterThan, Lit(Int(l)), Lit(Int(r))) => Lit(Bool(l > r)), - (GreaterThan, Lit(Float(l)), Lit(Float(r))) => Lit(Bool(l > r)), - - (GreaterThanOrEqual, Lit(Nat(l)), Lit(Nat(r))) => Lit(Bool(l >= r)), - (GreaterThanOrEqual, Lit(Int(l)), Lit(Int(r))) => Lit(Bool(l >= r)), - (GreaterThanOrEqual, Lit(Float(l)), Lit(Float(r))) => Lit(Bool(l >= r)), - _ => return Err("No valid binop".to_string()) - }.to_node(), - (prefix, &[Node::Expr(ref arg)]) => match (prefix, arg) { - (BooleanNot, Lit(Bool(true))) => Lit(Bool(false)), - (BooleanNot, Lit(Bool(false))) => Lit(Bool(true)), - (Negate, Lit(Nat(n))) => Lit(Int(-(*n as i64))), - (Negate, Lit(Int(n))) => Lit(Int(-(*n as i64))), - (Increment, Lit(Int(n))) => Lit(Int(*n)), - (Increment, Lit(Nat(n))) => Lit(Nat(*n)), - _ => return Err("No valid prefix op".to_string()) - }.to_node(), - - /* builtin functions */ - (IOPrint, &[ref anything]) => { - print!("{}", anything.to_repl()); - Expr::unit().to_node() - }, - (IOPrintLn, &[ref anything]) => { - println!("{}", anything.to_repl()); - Expr::unit().to_node() - }, - (IOGetLine, &[]) => { - let mut buf = String::new(); - io::stdin().read_line(&mut buf).expect("Error readling line in 'getline'"); - Lit(StringLit(Rc::new(buf.trim().to_string()))).to_node() - }, - (x, args) => return Err(format!("bad or unimplemented builtin {:?} | {:?}", x, args)), - }) - } - - fn conditional(&mut self, cond: Expr, then_clause: Vec, else_clause: Vec) -> EvalResult { - let cond = self.expression(Node::Expr(cond))?; - Ok(match cond { - Node::Expr(Expr::Lit(Lit::Bool(true))) => self.block(then_clause)?, - Node::Expr(Expr::Lit(Lit::Bool(false))) => self.block(else_clause)?, - _ => return Err("Conditional with non-boolean condition".to_string()) - }) - } - - fn assign_expression(&mut self, val: Expr, expr: Expr) -> EvalResult { - let name = match val { - Expr::Sym(name) => name, - _ => return Err("Trying to assign to a non-value".to_string()), - }; - - let constant = match self.values.lookup(&name) { - None => return Err(format!("Constant {} is undefined", name)), - Some(ValueEntry::Binding { constant, .. }) => *constant, - }; - if constant { - return Err(format!("trying to update {}, a non-mutable binding", name)); - } - let val = self.expression(Node::Expr(expr))?; - self.values.insert(name, ValueEntry::Binding { constant: false, val }); - Ok(Node::Expr(Expr::unit())) - } - - fn guard_passes(&mut self, guard: &Option, cond: &Node) -> EvalResult { - if let Some(ref guard_expr) = guard { - let guard_expr = match cond { - Node::Expr(ref e) => guard_expr.clone().replace_conditional_target_sigil(e), - _ => guard_expr.clone() - }; - Ok(self.expression(guard_expr.to_node())?.is_true()) - } else { - Ok(true) - } - } - - fn case_match_expression(&mut self, cond: Expr, alternatives: Vec) -> EvalResult { - - //TODO need to handle recursive subpatterns - let all_subpatterns_pass = |state: &mut State, subpatterns: &Vec>, items: &Vec| -> EvalResult { - if subpatterns.is_empty() { - return Ok(true) - } - - if items.len() != subpatterns.len() { - return Err(format!("Subpattern length isn't correct items {} subpatterns {}", items.len(), subpatterns.len())); - } - - for (maybe_subp, cond) in subpatterns.iter().zip(items.iter()) { - if let Some(subp) = maybe_subp { - if !state.guard_passes(&subp.guard, cond)? { - return Ok(false) - } - } - } - Ok(true) - }; - - let cond = self.expression(Node::Expr(cond))?; - for alt in alternatives { - // no matter what type of condition we have, ignore alternative if the guard evaluates false - if !self.guard_passes(&alt.matchable.guard, &cond)? { - continue; - } - - match cond { - Node::PrimObject { ref tag, ref items, .. } => { - if alt.matchable.tag.map(|t| t == *tag).unwrap_or(true) { - let mut inner_state = self.new_frame(items, &alt.matchable.bound_vars); - if all_subpatterns_pass(&mut inner_state, &alt.matchable.subpatterns, items)? { - return inner_state.block(alt.item); - } else { - continue; - } - } - }, - Node::PrimTuple { ref items } => { - let mut inner_state = self.new_frame(items, &alt.matchable.bound_vars); - if all_subpatterns_pass(&mut inner_state, &alt.matchable.subpatterns, items)? { - return inner_state.block(alt.item); - } else { - continue; - } - }, - Node::Expr(ref _e) => { - if alt.matchable.tag.is_none() { - return self.block(alt.item) - } - } - } - } - Err(format!("{:?} failed pattern match", cond)) - } -} diff --git a/schala-lang/language/src/eval/test.rs b/schala-lang/language/src/eval/test.rs deleted file mode 100644 index d2b667e..0000000 --- a/schala-lang/language/src/eval/test.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![cfg(test)] - -use crate::symbol_table::SymbolTable; -use crate::reduced_ast::reduce; -use crate::eval::State; - -fn evaluate_all_outputs(input: &str) -> Vec> { - let ast = crate::util::quick_ast(input); - - let mut symbol_table = SymbolTable::new(); - symbol_table.process_ast(&ast).unwrap(); - - let reduced = reduce(&ast, &symbol_table); - - let mut state = State::new(); - - state.evaluate(reduced, true) -} - -macro_rules! test_in_fresh_env { - ($string:expr, $correct:expr) => { - { - let all_output = evaluate_all_outputs($string); - let output = &all_output.last().unwrap(); - assert_eq!(**output, Ok($correct.to_string())); - } - } -} - - - - diff --git a/schala-lang/language/src/reduced_ast/mod.rs b/schala-lang/language/src/reduced_ast/mod.rs deleted file mode 100644 index 107e29b..0000000 --- a/schala-lang/language/src/reduced_ast/mod.rs +++ /dev/null @@ -1,514 +0,0 @@ -//! # Reduced AST -//! The reduced AST is a minimal AST designed to be built from the full AST after all possible -//! static checks have been done. Consequently, the AST reduction phase does very little error -//! checking itself - any errors should ideally be caught either by an earlier phase, or are -//! runtime errors that the evaluator should handle. That said, becuase it does do table lookups -//! that can in principle fail [especially at the moment with most static analysis not yet complete], -//! there is an Expr variant `ReductionError` to handle these cases. -//! -//! A design decision to make - should the ReducedAST types contain all information about -//! type/layout necessary for the evaluator to work? If so, then the evaluator should not -//! have access to the symbol table at all and ReducedAST should carry that information. If not, -//! then ReducedAST shouldn't be duplicating information that can be queried at runtime from the -//! symbol table. But I think the former might make sense since ultimately the bytecode will be -//! built from the ReducedAST. - -use std::convert::TryFrom; -use std::rc::Rc; -use std::str::FromStr; - -use crate::ast::*; -use crate::builtin::Builtin; -use crate::symbol_table::{Symbol, SymbolSpec, SymbolTable}; -use crate::util::deref_optional_box; - -mod types; -pub use types::*; - -pub fn reduce(ast: &AST, symbol_table: &SymbolTable) -> ReducedAST { - let mut reducer = Reducer { symbol_table }; - reducer.ast(ast) -} - -struct Reducer<'a> { - symbol_table: &'a SymbolTable, -} - -impl<'a> Reducer<'a> { - fn ast(&mut self, ast: &AST) -> ReducedAST { - let mut output = vec![]; - for statement in ast.statements.iter() { - output.push(self.statement(statement)); - } - ReducedAST(output) - } - - fn statement(&mut self, stmt: &Statement) -> Stmt { - match &stmt.kind { - StatementKind::Expression(expr) => Stmt::Expr(self.expression(expr)), - StatementKind::Declaration(decl) => self.declaration(decl), - StatementKind::Import(_) => Stmt::Noop, - StatementKind::Module(modspec) => { - for statement in modspec.contents.iter() { - self.statement(statement); - } - Stmt::Noop - } - } - } - - #[allow(clippy::ptr_arg)] - fn block(&mut self, block: &Block) -> Vec { - block.iter().map(|stmt| self.statement(stmt)).collect() - } - - fn invocation_argument(&mut self, invoc: &InvocationArgument) -> Expr { - use crate::ast::InvocationArgument::*; - match invoc { - Positional(ex) => self.expression(ex), - Keyword { .. } => Expr::UnimplementedSigilValue, - Ignored => Expr::UnimplementedSigilValue, - } - } - - fn expression(&mut self, expr: &Expression) -> Expr { - use crate::ast::ExpressionKind::*; - let input = &expr.kind; - match input { - NatLiteral(n) => Expr::Lit(Lit::Nat(*n)), - FloatLiteral(f) => Expr::Lit(Lit::Float(*f)), - StringLiteral(s) => Expr::Lit(Lit::StringLit(s.clone())), - BoolLiteral(b) => Expr::Lit(Lit::Bool(*b)), - BinExp(binop, lhs, rhs) => self.binop(binop, lhs, rhs), - PrefixExp(op, arg) => self.prefix(op, arg), - Value(qualified_name) => self.value(qualified_name), - Call { f, arguments } => self.reduce_call_expression(f, arguments), - TupleLiteral(exprs) => Expr::Tuple(exprs.iter().map(|e| self.expression(e)).collect()), - IfExpression { - discriminator, - body, - } => self.reduce_if_expression(deref_optional_box(discriminator), body), - Lambda { params, body, .. } => self.reduce_lambda(params, body), - NamedStruct { name, fields } => self.reduce_named_struct(name, fields), - Index { .. } => Expr::UnimplementedSigilValue, - WhileExpression { .. } => Expr::UnimplementedSigilValue, - ForExpression { .. } => Expr::UnimplementedSigilValue, - ListLiteral { .. } => Expr::UnimplementedSigilValue, - } - } - - fn value(&mut self, qualified_name: &QualifiedName) -> Expr { - let Symbol { - local_name, spec, .. - } = match self.symbol_table.lookup_symbol(&qualified_name.id) { - Some(s) => s, - //TODO this causes several evaluation tests to fail, figure out what's going on here - //None => return Expr::ReductionError(format!("Symbol {:?} not found", sym_name)), - None => { - let name = qualified_name.components.last().unwrap().clone(); - return Expr::Sym(name); - } - }; - - match spec { - SymbolSpec::RecordConstructor { .. } => Expr::ReductionError( - "AST reducer doesn't expect a RecordConstructor here".to_string(), - ), - SymbolSpec::DataConstructor { - index, - arity, - type_name, - } => Expr::Constructor { - type_name: type_name.clone(), - name: local_name.clone(), - tag: *index, - arity: *arity, - }, - SymbolSpec::Func(_) => Expr::Sym(local_name.clone()), - SymbolSpec::GlobalBinding => Expr::Sym(local_name.clone()), //TODO not sure if this is right, probably needs to eventually be fqsn - _ => Expr::UnimplementedSigilValue, - } - } - - #[allow(clippy::ptr_arg)] - fn reduce_lambda(&mut self, params: &[FormalParam], body: &Block) -> Expr { - Expr::Func(Func::UserDefined { - name: None, - params: params.iter().map(|param| param.name.clone()).collect(), - body: self.block(body), - }) - } - - fn reduce_named_struct( - &mut self, - name: &QualifiedName, - fields: &[(Rc, Expression)], - ) -> Expr { - let symbol = match self.symbol_table.lookup_symbol(&name.id) { - Some(fqsn) => fqsn, - None => return Expr::ReductionError(format!("FQSN lookup for name {:?} failed", name)), - }; - - let (type_name, index, members_from_table) = match &symbol.spec { - SymbolSpec::RecordConstructor { - members, - type_name, - index, - } => (type_name.clone(), index, members), - _ => return Expr::ReductionError("Not a record constructor".to_string()), - }; - - let arity = members_from_table.len(); - - let mut args: Vec<(Rc, Expr)> = fields - .iter() - .map(|(name, expr)| (name.clone(), self.expression(expr))) - .collect(); - - args.as_mut_slice() - .sort_unstable_by(|(name1, _), (name2, _)| name1.cmp(name2)); //arbitrary - sorting by alphabetical order - - let args = args.into_iter().map(|(_, expr)| expr).collect(); - - //TODO make sure this sorting actually works - let f = box Expr::Constructor { - type_name, - name: symbol.local_name.clone(), - tag: *index, - arity, - }; - Expr::Call { f, args } - } - - fn reduce_call_expression( - &mut self, - func: &Expression, - arguments: &[InvocationArgument], - ) -> Expr { - Expr::Call { - f: Box::new(self.expression(func)), - args: arguments - .iter() - .map(|arg| self.invocation_argument(arg)) - .collect(), - } - } - - fn reduce_if_expression( - &mut self, - discriminator: Option<&Expression>, - body: &IfExpressionBody, - ) -> Expr { - let cond = Box::new(match discriminator { - Some(expr) => self.expression(expr), - None => return Expr::ReductionError("blank cond if-expr not supported".to_string()), - }); - - match body { - IfExpressionBody::SimpleConditional { - then_case, - else_case, - } => { - let then_clause = self.block(then_case); - let else_clause = match else_case.as_ref() { - None => vec![], - Some(stmts) => self.block(stmts), - }; - Expr::Conditional { - cond, - then_clause, - else_clause, - } - } - IfExpressionBody::SimplePatternMatch { - pattern, - then_case, - else_case, - } => { - let then_clause = self.block(then_case); - let else_clause = match else_case.as_ref() { - None => vec![], - Some(stmts) => self.block(stmts), - }; - - let alternatives = vec![ - pattern.to_alternative(then_clause, self.symbol_table), - Alternative { - matchable: Subpattern { - tag: None, - subpatterns: vec![], - bound_vars: vec![], - guard: None, - }, - item: else_clause, - }, - ]; - Expression::CaseMatch { cond, alternatives } - }, - IfExpressionBody::CondList(ref condition_arms) => { - let mut alternatives = vec![]; - for arm in condition_arms { - match arm.condition { - Condition::Expression(ref _expr) => return Expr::UnimplementedSigilValue, - Condition::Pattern(ref p) => { - let item = self.block(&arm.body); - let alt = p.to_alternative(item, self.symbol_table); - alternatives.push(alt); - } - Condition::TruncatedOp(_, _) => return Expr::UnimplementedSigilValue, - Condition::Else => return Expr::UnimplementedSigilValue, - } - } - Expr::CaseMatch { cond, alternatives } - } - } - } - - fn binop(&mut self, binop: &BinOp, lhs: &Expression, rhs: &Expression) -> Expr { - let operation = Builtin::from_str(binop.sigil()).ok(); - match operation { - Some(Builtin::Assignment) => Expr::Assign { - val: Box::new(self.expression(&*lhs)), - expr: Box::new(self.expression(&*rhs)), - }, - Some(op) => { - let f = Box::new(Expr::Func(Func::BuiltIn(op))); - Expr::Call { - f, - args: vec![self.expression(&*lhs), self.expression(&*rhs)], - } - } - None => { - //TODO handle a user-defined operation - Expr::UnimplementedSigilValue - } - } - } - - fn prefix(&mut self, prefix: &PrefixOp, arg: &Expression) -> Expr { - let builtin: Option = TryFrom::try_from(prefix).ok(); - match builtin { - Some(op) => { - let f = Box::new(Expr::Func(Func::BuiltIn(op))); - Expr::Call { - f, - args: vec![self.expression(arg)], - } - } - None => { - //TODO need this for custom prefix ops - Expr::UnimplementedSigilValue - } - } - } - - fn declaration(&mut self, declaration: &Declaration) -> Stmt { - use self::Declaration::*; - match declaration { - Binding { - name, - constant, - expr, - .. - } => Stmt::Binding { - name: name.clone(), - constant: *constant, - expr: self.expression(expr), - }, - FuncDecl(Signature { name, params, .. }, statements) => Stmt::PreBinding { - name: name.clone(), - func: Func::UserDefined { - name: Some(name.clone()), - params: params.iter().map(|param| param.name.clone()).collect(), - body: self.block(statements), - }, - }, - TypeDecl { .. } => Stmt::Noop, - TypeAlias { .. } => Stmt::Noop, - Interface { .. } => Stmt::Noop, - Impl { .. } => Stmt::Expr(Expr::UnimplementedSigilValue), - Annotation { .. } => Stmt::Noop, - _ => Stmt::Expr(Expr::UnimplementedSigilValue), - } - } -} - -fn handle_symbol( - symbol: Option<&Symbol>, - inner_patterns: &[Pattern], - symbol_table: &SymbolTable, -) -> Subpattern { - use self::Pattern::*; - let tag = symbol.map(|symbol| match symbol.spec { - SymbolSpec::DataConstructor { index, .. } => index, - _ => { - panic!("Symbol is not a data constructor - this should've been caught in type-checking") - } - }); - let bound_vars = inner_patterns - .iter() - .map(|p| match p { - VarOrName(qualified_name) => { - let symbol_exists = symbol_table.lookup_symbol(&qualified_name.id).is_some(); - if symbol_exists { - None - } else { - let QualifiedName { components, .. } = qualified_name; - if components.len() == 1 { - Some(components[0].clone()) - } else { - panic!("Bad variable name in pattern"); - } - } - } - _ => None, - }) - .collect(); - - let subpatterns = inner_patterns - .iter() - .map(|p| match p { - Ignored => None, - VarOrName(_) => None, - Literal(other) => Some(other.to_subpattern(symbol_table)), - tp @ TuplePattern(_) => Some(tp.to_subpattern(symbol_table)), - ts @ TupleStruct(_, _) => Some(ts.to_subpattern(symbol_table)), - Record(..) => unimplemented!(), - }) - .collect(); - - let guard = None; - /* - let guard_equality_exprs: Vec = subpatterns.iter().map(|p| match p { - Literal(lit) => match lit { - _ => unimplemented!() - }, - _ => unimplemented!() - }).collect(); - */ - - Subpattern { - tag, - subpatterns, - guard, - bound_vars, - } -} - -impl Pattern { - fn to_alternative(&self, item: Vec, symbol_table: &SymbolTable) -> Alternative { - let s = self.to_subpattern(symbol_table); - Alternative { - matchable: Subpattern { - tag: s.tag, - subpatterns: s.subpatterns, - bound_vars: s.bound_vars, - guard: s.guard, - }, - item, - } - } - - fn to_subpattern(&self, symbol_table: &SymbolTable) -> Subpattern { - use self::Pattern::*; - match self { - TupleStruct(QualifiedName { components, id }, inner_patterns) => { - match symbol_table.lookup_symbol(id) { - Some(symbol) => handle_symbol(Some(symbol), inner_patterns, symbol_table), - None => panic!("Symbol {:?} not found", components), - } - } - TuplePattern(inner_patterns) => handle_symbol(None, inner_patterns, symbol_table), - Record(_name, _pairs) => { - unimplemented!() - } - Ignored => Subpattern { - tag: None, - subpatterns: vec![], - guard: None, - bound_vars: vec![], - }, - Literal(lit) => lit.to_subpattern(symbol_table), - VarOrName(QualifiedName { components, id }) => { - // if symbol is Some, treat this as a symbol pattern. If it's None, treat it - // as a variable. - match symbol_table.lookup_symbol(id) { - Some(symbol) => handle_symbol(Some(symbol), &[], symbol_table), - None => { - println!("Components: {:?}", components); - let name = if components.len() == 1 { - components[0].clone() - } else { - panic!("check this line of code yo"); - }; - Subpattern { - tag: None, - subpatterns: vec![], - guard: None, - bound_vars: vec![Some(name)], - } - } - } - } - } - } -} - -impl PatternLiteral { - fn to_subpattern(&self, _symbol_table: &SymbolTable) -> Subpattern { - use self::PatternLiteral::*; - match self { - NumPattern { neg, num } => { - let comparison = Expr::Lit(match (neg, num) { - (false, ExpressionKind::NatLiteral(n)) => Lit::Nat(*n), - (false, ExpressionKind::FloatLiteral(f)) => Lit::Float(*f), - (true, ExpressionKind::NatLiteral(n)) => Lit::Int(-(*n as i64)), - (true, ExpressionKind::FloatLiteral(f)) => Lit::Float(-f), - _ => panic!("This should never happen"), - }); - let guard = Some(Expr::Call { - f: Box::new(Expr::Func(Func::BuiltIn(Builtin::Equality))), - args: vec![comparison, Expr::ConditionalTargetSigilValue], - }); - Subpattern { - tag: None, - subpatterns: vec![], - guard, - bound_vars: vec![], - } - } - StringPattern(s) => { - let guard = Some(Expr::Call { - f: Box::new(Expr::Func(Func::BuiltIn(Builtin::Equality))), - args: vec![ - Expr::Lit(Lit::StringLit(s.clone())), - Expr::ConditionalTargetSigilValue, - ], - }); - - Subpattern { - tag: None, - subpatterns: vec![], - guard, - bound_vars: vec![], - } - } - BoolPattern(b) => { - let guard = Some(if *b { - Expr::ConditionalTargetSigilValue - } else { - Expr::Call { - f: Box::new(Expr::Func(Func::BuiltIn(Builtin::BooleanNot))), - args: vec![Expr::ConditionalTargetSigilValue], - } - }); - Subpattern { - tag: None, - subpatterns: vec![], - guard, - bound_vars: vec![], - } - } - } - } -} diff --git a/schala-lang/language/src/reduced_ast/types.rs b/schala-lang/language/src/reduced_ast/types.rs deleted file mode 100644 index d6aef6e..0000000 --- a/schala-lang/language/src/reduced_ast/types.rs +++ /dev/null @@ -1,98 +0,0 @@ -#![allow(clippy::enum_variant_names)] - -use crate::builtin::Builtin; -use std::rc::Rc; - -#[derive(Debug)] -pub struct ReducedAST(pub Vec); - -#[derive(Debug, Clone)] -pub enum Stmt { - PreBinding { - name: Rc, - func: Func, - }, - Binding { - name: Rc, - constant: bool, - expr: Expr, - }, - Expr(Expr), - Noop, -} - -#[derive(Debug, Clone)] -pub enum Expr { - Lit(Lit), - Sym(Rc), //a Sym is anything that can be looked up by name at runtime - i.e. a function or variable address - Tuple(Vec), - Func(Func), - Constructor { - type_name: Rc, - name: Rc, - tag: usize, - arity: usize, // n.b. arity here is always the value from the symbol table - if it doesn't match what it's being called with, that's an eval error, eval will handle it - }, - Call { - f: Box, - args: Vec, - }, - Assign { - val: Box, //TODO this probably can't be a val - expr: Box, - }, - Conditional { - cond: Box, - then_clause: Vec, - else_clause: Vec, - }, - ConditionalTargetSigilValue, - CaseMatch { - cond: Box, - alternatives: Vec, - }, - UnimplementedSigilValue, - ReductionError(String), -} - -impl Expr { - // The unit value is an empty tuple - pub fn unit() -> Expr { - Expr::Tuple(vec![]) - } -} - -pub type BoundVars = Vec>>; //remember that order matters here - -#[derive(Debug, Clone)] -pub struct Alternative { - pub matchable: Subpattern, - pub item: Vec, -} - -#[derive(Debug, Clone)] -pub struct Subpattern { - pub tag: Option, - pub subpatterns: Vec>, - pub bound_vars: BoundVars, - pub guard: Option, -} - -#[derive(Debug, Clone)] -pub enum Lit { - Nat(u64), - Int(i64), - Float(f64), - Bool(bool), - StringLit(Rc), -} - -#[derive(Debug, Clone)] -pub enum Func { - BuiltIn(Builtin), - UserDefined { - name: Option>, - params: Vec>, - body: Vec, - }, -}