use crate::ast; use crate::symbol_table::{DefId, SymbolSpec, SymbolTable}; use crate::builtin::Builtin; use std::str::FromStr; use std::collections::HashMap; mod types; mod test; pub use types::*; pub fn reduce(ast: &ast::AST, symbol_table: &SymbolTable) -> ReducedIR { let reducer = Reducer::new(symbol_table); reducer.reduce(ast) } struct Reducer<'a> { symbol_table: &'a SymbolTable, functions: HashMap, } impl<'a> Reducer<'a> { fn new(symbol_table: &'a SymbolTable) -> Self { Self { symbol_table, functions: HashMap::new(), } } fn reduce(mut self, ast: &ast::AST) -> ReducedIR { // First reduce all functions // TODO once this works, maybe rewrite it using the Visitor for statement in ast.statements.iter() { self.top_level_statement(&statement); } // Then compute the entrypoint statements (which may reference previously-computed // functions by ID) let mut entrypoint = vec![]; for statement in ast.statements.iter() { let ast::Statement { id: item_id, kind, .. } = statement; match &kind { ast::StatementKind::Expression(expr) => { entrypoint.push(Statement::Expression(self.expression(&expr))); }, ast::StatementKind::Declaration(ast::Declaration::Binding { name: _, constant, expr, ..}) => { let symbol = self.symbol_table.lookup_symbol(item_id).unwrap(); let def_id = symbol.def_id().unwrap(); entrypoint.push(Statement::Binding { id: def_id, constant: *constant, expr: self.expression(&expr) }); }, _ => () } } ReducedIR { functions: self.functions, entrypoint, } } fn top_level_statement(&mut self, statement: &ast::Statement) { let ast::Statement { id: item_id, kind, .. } = statement; match kind { ast::StatementKind::Expression(_expr) => { //TODO expressions can in principle contain definitions, but I won't worry //about it now () }, ast::StatementKind::Declaration(decl) => match decl { ast::Declaration::FuncDecl(_, statements) => { self.insert_function_definition(item_id, statements); }, _ => () }, ast::StatementKind::Import(..) => (), ast::StatementKind::Module(_modspec) => { //TODO handle modules () } } } fn function_internal_statement(&mut self, statement: &ast::Statement) -> Option { let ast::Statement { id: item_id, kind, .. } = statement; match kind { ast::StatementKind::Expression(expr) => { Some(Statement::Expression(self.expression(expr))) }, ast::StatementKind::Declaration(decl) => match decl { ast::Declaration::FuncDecl(_, statements) => { self.insert_function_definition(item_id, statements); None }, ast::Declaration::Binding { constant, expr, ..} => { let symbol = self.symbol_table.lookup_symbol(item_id).unwrap(); let def_id = symbol.def_id().unwrap(); Some(Statement::Binding { id: def_id, constant: *constant, expr: self.expression(&expr) }) }, _ => None }, _ => None } } fn insert_function_definition(&mut self, item_id: &ast::ItemId, statements: &ast::Block) { let symbol = self.symbol_table.lookup_symbol(item_id).unwrap(); let def_id = symbol.def_id().unwrap(); let function_def = FunctionDefinition { body: self.function(statements) }; self.functions.insert(def_id, function_def); } fn expression(&mut self, expr: &ast::Expression) -> Expression { use crate::ast::ExpressionKind::*; match &expr.kind { NatLiteral(n) => Expression::Literal(Literal::Nat(*n)), FloatLiteral(f) => Expression::Literal(Literal::Float(*f)), StringLiteral(s) => Expression::Literal(Literal::StringLit(s.clone())), BoolLiteral(b) => Expression::Literal(Literal::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 } => Expression::Call { f: Box::new(self.expression(f)), args: arguments .iter() .map(|arg| self.invocation_argument(arg)) .collect(), }, TupleLiteral(exprs) => Expression::Tuple(exprs.iter().map(|e| self.expression(e)).collect()), IfExpression { discriminator, body, } => self.reduce_if_expression(discriminator.as_ref().map(|x| x.as_ref()), body), Lambda { params, body, .. } => { Expression::Callable(Function::Lambda { arity: params.len() as u8, body: self.function(body), }) }, NamedStruct { .. } => Expression::ReductionError("NamedStruct not implemented".to_string()), //self.reduce_named_struct(name, fields), Index { .. } => Expression::ReductionError("Index expr not implemented".to_string()), WhileExpression { .. } => Expression::ReductionError("While expr not implemented".to_string()), ForExpression { .. } => Expression::ReductionError("For expr not implemented".to_string()), ListLiteral { .. } => Expression::ReductionError("ListLiteral expr not implemented".to_string()), } } fn reduce_if_expression(&mut self, _discriminator: Option<&ast::Expression>, _body: &ast::IfExpressionBody) -> Expression { Expression::ReductionError("if expr".to_string()) } fn invocation_argument(&mut self, invoc: &ast::InvocationArgument) -> Expression { use crate::ast::InvocationArgument::*; match invoc { Positional(ex) => self.expression(ex), Keyword { .. } => Expression::ReductionError("Keyword arguments not supported".to_string()), Ignored => Expression::ReductionError("Ignored arguments not supported".to_string()), } } fn function(&mut self, statements: &ast::Block) -> Vec { statements.iter().filter_map(|stmt| self.function_internal_statement(stmt)).collect() } fn prefix(&mut self, prefix: &ast::PrefixOp, arg: &ast::Expression) -> Expression { let builtin: Option = TryFrom::try_from(prefix).ok(); match builtin { Some(op) => { Expression::Call { f: Box::new(Expression::Callable(Function::Builtin(op))), args: vec![self.expression(arg)], } } None => { //TODO need this for custom prefix ops Expression::ReductionError("User-defined prefix ops not supported".to_string()) } } } fn binop(&mut self, binop: &ast::BinOp, lhs: &ast::Expression, rhs: &ast::Expression) -> Expression { use Expression::*; let operation = Builtin::from_str(binop.sigil()).ok(); match operation { Some(Builtin::Assignment) => { let lval = match &lhs.kind { ast::ExpressionKind::Value(qualified_name) => { if let Some(symbol) = self.symbol_table.lookup_symbol(&qualified_name.id) { symbol.def_id().unwrap() } else { return ReductionError(format!("Couldn't look up name: {:?}", qualified_name)); } }, _ => return ReductionError("Trying to assign to a non-name".to_string()), }; Assign { lval, rval: Box::new(self.expression(rhs)), } }, Some(op) => { Expression::Call { f: Box::new(Expression::Callable(Function::Builtin(op))), args: vec![self.expression(lhs), self.expression(rhs)], } } None => { //TODO handle a user-defined operation ReductionError("User-defined operations not supported".to_string()) } } } fn value(&mut self, qualified_name: &ast::QualifiedName) -> Expression { use SymbolSpec::*; let ast::QualifiedName { id: _, components, .. } = qualified_name; let _ = components; let symbol = match self.symbol_table.lookup_symbol(&qualified_name.id) { Some(s) => s, None => return Expression::ReductionError(format!("No symbol found for name: {:?}", qualified_name)) }; let def_id = symbol.def_id(); match symbol.spec() { Func(_) => Expression::Lookup(Lookup::Function(def_id.unwrap())), GlobalBinding => Expression::Lookup(Lookup::GlobalVar(def_id.unwrap())), LocalVariable => Expression::Lookup(Lookup::LocalVar(def_id.unwrap())), FunctionParam(n) => Expression::Lookup(Lookup::Param(n)), DataConstructor { .. } => { Expression::ReductionError("DataConstructor not supported".to_string()) }, RecordConstructor { .. } => { Expression::ReductionError(format!("The symbol for value {:?} is unexpectdly a RecordConstructor", qualified_name)) }, } } }