From 7282a38a0866c1f1cdfbec80209926d0d952e01b Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Sun, 24 Oct 2021 17:57:56 -0700 Subject: [PATCH] Function application working again --- schala-lang/language/src/reduced_ir/mod.rs | 19 +++++- schala-lang/language/src/reduced_ir/types.rs | 15 +++-- .../language/src/symbol_table/resolver.rs | 2 +- .../language/src/tree_walk_eval/mod.rs | 64 +++++++++++++++++-- 4 files changed, 84 insertions(+), 16 deletions(-) diff --git a/schala-lang/language/src/reduced_ir/mod.rs b/schala-lang/language/src/reduced_ir/mod.rs index 9da00ee..9fa1bfd 100644 --- a/schala-lang/language/src/reduced_ir/mod.rs +++ b/schala-lang/language/src/reduced_ir/mod.rs @@ -118,7 +118,13 @@ impl<'a> Reducer<'a> { 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 } => Unimplemented, // self.reduce_call_expression(f, arguments), + 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, @@ -133,6 +139,15 @@ impl<'a> Reducer<'a> { } } + 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() } @@ -201,7 +216,7 @@ impl<'a> Reducer<'a> { Func(_) => Expression::Lookup { id: def_id.clone(), kind: Lookup::Function }, GlobalBinding => Expression::Lookup { id: def_id.clone(), kind: Lookup::GlobalVar }, LocalVariable => Expression::Lookup { id: def_id.clone(), kind: Lookup::LocalVar }, - FunctionParam(_) => Expression::Lookup { id: def_id.clone(), kind: Lookup::Param }, + FunctionParam(n) => Expression::Lookup { id: def_id.clone(), kind: Lookup::Param(*n) }, DataConstructor { index, arity, .. } => { Expression::Unimplemented }, diff --git a/schala-lang/language/src/reduced_ir/types.rs b/schala-lang/language/src/reduced_ir/types.rs index ea6f8a8..d2af782 100644 --- a/schala-lang/language/src/reduced_ir/types.rs +++ b/schala-lang/language/src/reduced_ir/types.rs @@ -2,7 +2,10 @@ use std::collections::HashMap; use std::rc::Rc; use crate::builtin::Builtin; -use crate::symbol_table::{DefId, Symbol, SymbolSpec, SymbolTable}; +use crate::symbol_table::{DefId, SymbolTable}; + +//TODO most of these Clone impls only exist to support function application, because the +//tree-walking evaluator moves the reduced IR members. /// The reduced intermediate representation consists of a list of function definitions, and a block /// of entrypoint statements. In a repl or script context this can be an arbitrary list of @@ -14,7 +17,7 @@ pub struct ReducedIR { } impl ReducedIR { - fn debug(&self, symbol_table: &SymbolTable) { + pub fn debug(&self, symbol_table: &SymbolTable) { println!("Reduced IR:"); println!("Functions:"); println!("-----------"); @@ -32,7 +35,7 @@ impl ReducedIR { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Statement { Expression(Expression), Binding { @@ -42,7 +45,7 @@ pub enum Statement { }, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Expression { Literal(Literal), Tuple(Vec), @@ -80,12 +83,12 @@ pub enum Function { UserDefined(DefId) } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Lookup { LocalVar, GlobalVar, Function, - Param, + Param(u8), } #[derive(Debug, Clone)] diff --git a/schala-lang/language/src/symbol_table/resolver.rs b/schala-lang/language/src/symbol_table/resolver.rs index 7b53ec0..11fffca 100644 --- a/schala-lang/language/src/symbol_table/resolver.rs +++ b/schala-lang/language/src/symbol_table/resolver.rs @@ -1,7 +1,7 @@ use std::rc::Rc; use crate::ast::*; -use crate::symbol_table::{Fqsn, Scope, SymbolTable, Symbol, SymbolSpec, DefId}; +use crate::symbol_table::{Fqsn, Scope, SymbolTable, SymbolSpec}; use crate::util::ScopeStack; type FqsnPrefix = Vec; diff --git a/schala-lang/language/src/tree_walk_eval/mod.rs b/schala-lang/language/src/tree_walk_eval/mod.rs index 785e0b3..2114189 100644 --- a/schala-lang/language/src/tree_walk_eval/mod.rs +++ b/schala-lang/language/src/tree_walk_eval/mod.rs @@ -21,6 +21,13 @@ enum Memory { Index(u32) } +// This is for function param lookups, and is a hack +impl From for Memory { + fn from(n: u8) -> Self { + Memory::Index(4_000_000 + (n as u32)) + } +} + impl From<&DefId> for Memory { fn from(id: &DefId) -> Self { Self::Index(id.as_u32()) @@ -159,6 +166,21 @@ impl<'a> State<'a> { acc } + fn block(&mut self, statements: Vec) -> EvalResult { + //TODO need to handle breaks, returns, etc. + let mut ret = None; + for stmt in statements.into_iter() { + if let Some(RuntimeValue::Primitive(prim)) = self.statement(stmt)? { + ret = Some(prim); + } + } + Ok(if let Some(ret) = ret { + ret + } else { + self.expression(Expression::unit())? + }) + } + fn statement(&mut self, stmt: Statement) -> EvalResult> { match stmt { Statement::Binding { ref id, expr, constant } => { @@ -179,14 +201,25 @@ impl<'a> State<'a> { Expression::Literal(lit) => Primitive::Literal(lit), Expression::Tuple(items) => Primitive::Tuple(items.into_iter().map(|expr| self.expression(expr)).collect::>>()?), Expression::Lookup { ref id, kind } => { - let mem = id.into(); match kind { - Lookup::Function => match self.environments.lookup(&mem) { - //TODO is this right? not sure - Some(RuntimeValue::Primitive(prim)) => prim.clone(), - _ => return Err(format!("Function not found for id: {}", id)), + Lookup::Function => { + let mem = id.into(); + match self.environments.lookup(&mem) { + // This just checks that the function exists in "memory" by ID, we don't + // actually retrieve it until `apply_function()` + Some(RuntimeValue::Function(_)) => Primitive::Callable(Function::UserDefined(id.clone())), + x => return Err(format!("Function not found for id: {} : {:?}", id, x)), + } }, - kind @ Lookup::LocalVar | kind @ Lookup::GlobalVar | kind @ Lookup::Param => { + Lookup::Param(n) => { + let mem = n.into(); + match self.environments.lookup(&mem) { + Some(RuntimeValue::Primitive(prim)) => prim.clone(), + e => return Err(format!("Param lookup error, got {:?}", e)), + } + }, + kind @ Lookup::LocalVar | kind @ Lookup::GlobalVar => { + let mem = id.into(); match self.environments.lookup(&mem) { Some(RuntimeValue::Primitive(expr)) => expr.clone(), _ => return Err(format!("Nothing found for variable lookup {} of kind {:?}", id, kind)), @@ -302,7 +335,24 @@ impl<'a> State<'a> { let mem = (&def_id).into(); Ok(match self.environments.lookup(&mem) { Some(RuntimeValue::Function(FunctionDefinition { body })) => { - return Err("unimplemented apply-function".to_string()); + let body = body.clone(); //TODO ideally this clone would not happen + + let mut evaluated_args: Vec = vec![]; + for arg in args.into_iter() { + evaluated_args.push(self.expression(arg)?); + } + + let mut frame_state = State { + environments: self.environments.new_scope(None) + }; + + for (n, evaled) in evaluated_args.into_iter().enumerate() { + let n = n as u8; + let mem = n.into(); + frame_state.environments.insert(mem, RuntimeValue::Primitive(evaled)); + } + + frame_state.block(body)? }, e => return Err(format!("Error looking up function with id {}: {:?}", def_id, e)), })