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;
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
%left '*' '/'
|
%left '*' '/'
|
||||||
%left '+' '-'
|
%left '+' '-'
|
||||||
|
|
||||||
|
%avoid_insert "NUM" "SYM" "STRING" "||" "&&" "<" ">" "<=" ">=" "!=" "==" "+" "-" "*" "/" "{"
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
Body -> Result<Vec<Stmt>>:
|
Body -> Result<Vec<Stmt>>:
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
Reference in New Issue
Block a user