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 <alekratz@gmail.com>
This commit is contained in:
2020-09-26 18:31:23 -07:00
parent 3976b2135a
commit 4848a342f0
7 changed files with 53 additions and 21 deletions

View File

@@ -4,13 +4,13 @@ mod locals;
pub mod thunk; pub mod thunk;
use crate::{syn::ast::Body, obj::prelude::*, vm::{consts::*, package::Package}}; use crate::{syn::ast::Body, obj::prelude::*, vm::{consts::*, package::Package}};
use std::collections::{BTreeMap, HashMap}; use std::collections::HashMap;
#[derive(Default)] #[derive(Default)]
pub struct Compile { pub struct Compile {
const_data: ConstData, const_data: ConstData,
globals: BTreeMap<Sym, Name>, globals: Locals,
scope: Vec<BTreeMap<Sym, Name>>, scope: Vec<Locals>,
names: Vec<Sym>, names: Vec<Sym>,
next_local: Name, next_local: Name,
} }
@@ -50,10 +50,12 @@ impl Compile {
self.const_data_mut().const_str(s) 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. /// 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<Name> { pub fn lookup_scope(&mut self, sym: Sym) -> Option<Name> {
self.scope self.scope
.iter() .iter()
@@ -109,7 +111,7 @@ impl Compile {
} }
/// Pops a scope layer of local variables, if any are available. /// Pops a scope layer of local variables, if any are available.
pub(crate) fn pop_scope_layer(&mut self) -> Option<BTreeMap<Sym, Name>> { pub(crate) fn pop_scope_layer(&mut self) -> Option<Locals> {
self.scope.pop() self.scope.pop()
} }
@@ -136,7 +138,7 @@ impl ConstData {
pair.clone() pair.clone()
} else { } else {
let obj = Int::new_obj(int); 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())); self.ints.insert(int, (hdl, obj.clone()));
(hdl, obj) (hdl, obj)
} }
@@ -149,9 +151,18 @@ impl ConstData {
pair.clone() pair.clone()
} else { } else {
let obj = Str::new_obj(s.to_string()); 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())); self.strs.insert(s.to_string(), (hdl, obj.clone()));
(hdl, obj) (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)
}
} }

View File

@@ -345,9 +345,28 @@ impl Visit for CompileBody<'_> {
Ok(thunk) Ok(thunk)
} }
fn visit_fun_expr(&mut self, _expr: &FunExpr) -> Self::Out { fn visit_fun_expr(&mut self, expr: &FunExpr) -> Self::Out {
// TODO : fun exprs should be compiled as constants and put into the constant pool // - push const
todo!() // (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 { fn visit_atom(&mut self, atom: &Atom) -> Self::Out {

View File

@@ -18,11 +18,11 @@ pub struct UserFun {
code: Vec<Inst>, code: Vec<Inst>,
// Safe because this is just an interner that points to symbols, which aren't GC'd // Safe because this is just an interner that points to symbols, which aren't GC'd
#[shredder(unsafe_skip)] #[shredder(unsafe_skip)]
locals: Names, locals: Locals,
} }
impl UserFun { impl UserFun {
pub fn new_obj(code: Vec<Inst>, locals: Names) -> UserFunRef { pub fn new_obj(code: Vec<Inst>, locals: Locals) -> UserFunRef {
let obj_ref = ObjRef::new(UserFun { let obj_ref = ObjRef::new(UserFun {
vtable: Default::default(), // this is a placeholder for the real vtable vtable: Default::default(), // this is a placeholder for the real vtable
attrs: Default::default(), attrs: Default::default(),
@@ -47,7 +47,7 @@ impl UserFun {
&self.code &self.code
} }
pub fn locals(&self) -> &Names { pub fn locals(&self) -> &Locals {
&self.locals &self.locals
} }
} }

View File

@@ -1,6 +1,7 @@
use crate::obj::ObjRef; use crate::obj::prelude::*;
use std::collections::BTreeMap; use std::collections::BTreeMap;
handle_type!(Name); handle_type!(Name);
pub type Names = BTreeMap<Name, ObjRef>; pub type Names = BTreeMap<Name, ObjRef>;
pub type Locals = BTreeMap<Sym, Name>;

View File

@@ -66,8 +66,7 @@ pub static SYM_VTABLE: Lazy<Vtable> = Lazy::new(|| vtable! {});
// //
// global interned symbol obj table // global interned symbol obj table
// //
pub(crate) static SYM_REFS: Lazy<Mutex<BTreeMap<Sym, SymRef>>> = pub(crate) static SYM_REFS: Lazy<Mutex<BTreeMap<Sym, SymRef>>> = Lazy::new(|| Default::default());
Lazy::new(|| Mutex::new(Default::default()));
/// Access or insert a globally interned symbol object. /// Access or insert a globally interned symbol object.
/// ///

View File

@@ -6,6 +6,8 @@
%left '*' '/' %left '*' '/'
%left '+' '-' %left '+' '-'
%avoid_insert "NUM" "SYM" "STRING" "||" "&&" "<" ">" "<=" ">=" "!=" "==" "+" "-" "*" "/" "{"
%% %%
Body -> Result<Vec<Stmt>>: Body -> Result<Vec<Stmt>>:

View File

@@ -143,16 +143,16 @@ impl<'p> Vm<'p> {
fn begin_call(&mut self, caller: ObjRef, args: Vec<ObjRef>) { fn begin_call(&mut self, caller: ObjRef, args: Vec<ObjRef>) {
// create stack frame // create stack frame
let stack_frame = if let Some(user_fun) = std::any::Any::downcast_ref::<UserFunRef>(&caller) { let stack_frame = if let Some(user_fun) = std::any::Any::downcast_ref::<UserFunRef>(&caller) {
let locals = { let names: Names = {
read_obj!(let fun_ref = user_fun); read_obj!(let fun_ref = user_fun);
fun_ref.locals() fun_ref.locals()
.iter() .iter()
.zip(args.into_iter()) .zip(args.into_iter())
.map(|((k, _), v)| (*k, v)) .map(|((_, v), arg)| (*v, arg.clone()))
.collect() .collect()
}; };
Frame::new(locals, FrameKind::User { Frame::new(names, FrameKind::User {
last_pc: self.pc(), last_pc: self.pc(),
stack_base: self.stack().len(), stack_base: self.stack().len(),
fun: user_fun.clone(), fun: user_fun.clone(),