From 4848a342f0dab186b87fec62a0a31d3b84637a1f Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Sat, 26 Sep 2020 18:31:23 -0700 Subject: [PATCH] Add function compilation Functions are compiled in the most naiive way right now. I want to fix up how scope lookups are done before it becomes too much to update. Signed-off-by: Alek Ratzloff --- src/compile/mod.rs | 29 ++++++++++++++++++++--------- src/compile/thunk.rs | 25 ++++++++++++++++++++++--- src/obj/fun.rs | 6 +++--- src/obj/names.rs | 3 ++- src/obj/sym.rs | 3 +-- src/syn/parser.y | 2 ++ src/vm/mod.rs | 6 +++--- 7 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/compile/mod.rs b/src/compile/mod.rs index ac7d800..ebd4c53 100644 --- a/src/compile/mod.rs +++ b/src/compile/mod.rs @@ -4,13 +4,13 @@ mod locals; pub mod thunk; use crate::{syn::ast::Body, obj::prelude::*, vm::{consts::*, package::Package}}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; #[derive(Default)] pub struct Compile { const_data: ConstData, - globals: BTreeMap, - scope: Vec>, + globals: Locals, + scope: Vec, names: Vec, next_local: Name, } @@ -50,10 +50,12 @@ impl Compile { self.const_data_mut().const_str(s) } + /// Inserts a constant object without deduplication. + pub fn push_const(&mut self, obj: ObjRef) -> (ConstHandle, ObjRef) { + self.const_data_mut().push(obj) + } + /// Looks up a variable name. - /// - /// This will search up the locals stack until the given name is found, ultimately ending with - /// a global name lookup. pub fn lookup_scope(&mut self, sym: Sym) -> Option { self.scope .iter() @@ -109,7 +111,7 @@ impl Compile { } /// Pops a scope layer of local variables, if any are available. - pub(crate) fn pop_scope_layer(&mut self) -> Option> { + pub(crate) fn pop_scope_layer(&mut self) -> Option { self.scope.pop() } @@ -136,7 +138,7 @@ impl ConstData { pair.clone() } else { let obj = Int::new_obj(int); - let hdl = self.const_pool.push(obj.clone()); + let (hdl, _) = self.push(obj.clone()); self.ints.insert(int, (hdl, obj.clone())); (hdl, obj) } @@ -149,9 +151,18 @@ impl ConstData { pair.clone() } else { let obj = Str::new_obj(s.to_string()); - let hdl = self.const_pool.push(obj.clone()); + let (hdl, _) = self.push(obj.clone()); self.strs.insert(s.to_string(), (hdl, obj.clone())); (hdl, obj) } } + + /// Inserts a user-defined function constant object. + /// + /// This function provides no de-duplication. Use `const_str` or `const_int` for objects that + /// may be repeated. + pub fn push(&mut self, obj: ObjRef) -> (ConstHandle, ObjRef) { + let hdl = self.const_pool.push(obj.clone()); + (hdl, obj) + } } diff --git a/src/compile/thunk.rs b/src/compile/thunk.rs index a0df7cb..175374a 100644 --- a/src/compile/thunk.rs +++ b/src/compile/thunk.rs @@ -345,9 +345,28 @@ impl Visit for CompileBody<'_> { Ok(thunk) } - fn visit_fun_expr(&mut self, _expr: &FunExpr) -> Self::Out { - // TODO : fun exprs should be compiled as constants and put into the constant pool - todo!() + fn visit_fun_expr(&mut self, expr: &FunExpr) -> Self::Out { + // - push const + // (functions are unique const values so a new function will be created for every literal + // function defined in code) + + // This is pretty much the only place where a new scope layer gets pushed beyond the start + // of the program + self.compile.push_scope_layer(); + for param in expr.params.iter() { + let sym = global_sym(param.to_string()); + self.compile.create_local(sym); + } + let locals = self.compile.pop_scope_layer().unwrap(); + let code = self.visit_body(&expr.body)? + .flatten() + .to_vec(); + let (hdl, _fun) = self.compile.push_const(UserFun::new_obj(code, locals)); + + // TODO(compile) : determine return value at the end of the body (preferably at parse-time) + + // oh yeah, we were compiling a function body weren't we + Ok(Inst::PushConst(hdl).into()) } fn visit_atom(&mut self, atom: &Atom) -> Self::Out { diff --git a/src/obj/fun.rs b/src/obj/fun.rs index 11157ff..1a9d869 100644 --- a/src/obj/fun.rs +++ b/src/obj/fun.rs @@ -18,11 +18,11 @@ pub struct UserFun { code: Vec, // Safe because this is just an interner that points to symbols, which aren't GC'd #[shredder(unsafe_skip)] - locals: Names, + locals: Locals, } impl UserFun { - pub fn new_obj(code: Vec, locals: Names) -> UserFunRef { + pub fn new_obj(code: Vec, locals: Locals) -> UserFunRef { let obj_ref = ObjRef::new(UserFun { vtable: Default::default(), // this is a placeholder for the real vtable attrs: Default::default(), @@ -47,7 +47,7 @@ impl UserFun { &self.code } - pub fn locals(&self) -> &Names { + pub fn locals(&self) -> &Locals { &self.locals } } diff --git a/src/obj/names.rs b/src/obj/names.rs index 596ee18..186132a 100644 --- a/src/obj/names.rs +++ b/src/obj/names.rs @@ -1,6 +1,7 @@ -use crate::obj::ObjRef; +use crate::obj::prelude::*; use std::collections::BTreeMap; handle_type!(Name); pub type Names = BTreeMap; +pub type Locals = BTreeMap; diff --git a/src/obj/sym.rs b/src/obj/sym.rs index c0e8610..0eed22d 100644 --- a/src/obj/sym.rs +++ b/src/obj/sym.rs @@ -66,8 +66,7 @@ pub static SYM_VTABLE: Lazy = Lazy::new(|| vtable! {}); // // global interned symbol obj table // -pub(crate) static SYM_REFS: Lazy>> = - Lazy::new(|| Mutex::new(Default::default())); +pub(crate) static SYM_REFS: Lazy>> = Lazy::new(|| Default::default()); /// Access or insert a globally interned symbol object. /// diff --git a/src/syn/parser.y b/src/syn/parser.y index d4cd754..f9f9529 100644 --- a/src/syn/parser.y +++ b/src/syn/parser.y @@ -6,6 +6,8 @@ %left '*' '/' %left '+' '-' +%avoid_insert "NUM" "SYM" "STRING" "||" "&&" "<" ">" "<=" ">=" "!=" "==" "+" "-" "*" "/" "{" + %% Body -> Result>: diff --git a/src/vm/mod.rs b/src/vm/mod.rs index ae4ad62..84af19f 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -143,16 +143,16 @@ impl<'p> Vm<'p> { fn begin_call(&mut self, caller: ObjRef, args: Vec) { // create stack frame let stack_frame = if let Some(user_fun) = std::any::Any::downcast_ref::(&caller) { - let locals = { + let names: Names = { read_obj!(let fun_ref = user_fun); fun_ref.locals() .iter() .zip(args.into_iter()) - .map(|((k, _), v)| (*k, v)) + .map(|((_, v), arg)| (*v, arg.clone())) .collect() }; - Frame::new(locals, FrameKind::User { + Frame::new(names, FrameKind::User { last_pc: self.pc(), stack_base: self.stack().len(), fun: user_fun.clone(),