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:
@@ -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<Sym, Name>,
|
||||
scope: Vec<BTreeMap<Sym, Name>>,
|
||||
globals: Locals,
|
||||
scope: Vec<Locals>,
|
||||
names: Vec<Sym>,
|
||||
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<Name> {
|
||||
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<BTreeMap<Sym, Name>> {
|
||||
pub(crate) fn pop_scope_layer(&mut self) -> Option<Locals> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user