@@ -10,4 +10,3 @@ pub enum Error {
|
||||
}
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
use crate::{compile::Compile, obj::prelude::*, syn::{ast::*, visit::*}};
|
||||
use crate::{
|
||||
compile::Compile,
|
||||
obj::prelude::*,
|
||||
syn::{ast::*, visit::*},
|
||||
};
|
||||
|
||||
/// Collects local names from the given tree.
|
||||
///
|
||||
@@ -30,10 +34,18 @@ impl<'c> CollectLocals<'c> {
|
||||
impl Visit for CollectLocals<'_> {
|
||||
type Out = ();
|
||||
|
||||
fn visit_body(&mut self, body: &Body) -> Self::Out { DefaultAccept::default_accept(body, self); }
|
||||
fn visit_stmt(&mut self, stmt: &Stmt) -> Self::Out { DefaultAccept::default_accept(stmt, self); }
|
||||
fn visit_assign_stmt(&mut self, assign: &AssignStmt) -> Self::Out { DefaultAccept::default_accept(assign, self); }
|
||||
fn visit_return_stmt(&mut self, ret: &ReturnStmt) -> Self::Out { DefaultAccept::default_accept(ret, self); }
|
||||
fn visit_body(&mut self, body: &Body) -> Self::Out {
|
||||
DefaultAccept::default_accept(body, self);
|
||||
}
|
||||
fn visit_stmt(&mut self, stmt: &Stmt) -> Self::Out {
|
||||
DefaultAccept::default_accept(stmt, self);
|
||||
}
|
||||
fn visit_assign_stmt(&mut self, assign: &AssignStmt) -> Self::Out {
|
||||
DefaultAccept::default_accept(assign, self);
|
||||
}
|
||||
fn visit_return_stmt(&mut self, ret: &ReturnStmt) -> Self::Out {
|
||||
DefaultAccept::default_accept(ret, self);
|
||||
}
|
||||
fn visit_lhs_expr(&mut self, lhs_expr: &LhsExpr) -> Self::Out {
|
||||
match lhs_expr {
|
||||
LhsExpr::Name(name) => {
|
||||
@@ -43,14 +55,28 @@ impl Visit for CollectLocals<'_> {
|
||||
_ => { /* no op */ }
|
||||
}
|
||||
}
|
||||
fn visit_expr(&mut self, expr: &Expr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
|
||||
fn visit_bin_expr(&mut self, expr: &BinExpr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
|
||||
fn visit_un_expr(&mut self, expr: &UnExpr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
|
||||
fn visit_call_expr(&mut self, expr: &CallExpr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
|
||||
fn visit_index_expr(&mut self, expr: &IndexExpr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
|
||||
fn visit_access_expr(&mut self, expr: &AccessExpr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
|
||||
fn visit_expr(&mut self, expr: &Expr) -> Self::Out {
|
||||
DefaultAccept::default_accept(expr, self);
|
||||
}
|
||||
fn visit_bin_expr(&mut self, expr: &BinExpr) -> Self::Out {
|
||||
DefaultAccept::default_accept(expr, self);
|
||||
}
|
||||
fn visit_un_expr(&mut self, expr: &UnExpr) -> Self::Out {
|
||||
DefaultAccept::default_accept(expr, self);
|
||||
}
|
||||
fn visit_call_expr(&mut self, expr: &CallExpr) -> Self::Out {
|
||||
DefaultAccept::default_accept(expr, self);
|
||||
}
|
||||
fn visit_index_expr(&mut self, expr: &IndexExpr) -> Self::Out {
|
||||
DefaultAccept::default_accept(expr, self);
|
||||
}
|
||||
fn visit_access_expr(&mut self, expr: &AccessExpr) -> Self::Out {
|
||||
DefaultAccept::default_accept(expr, self);
|
||||
}
|
||||
fn visit_fun_expr(&mut self, _expr: &FunExpr) -> Self::Out {
|
||||
// Do not collect names for function expressions, since they have their own scope.
|
||||
}
|
||||
fn visit_atom(&mut self, atom: &Atom) -> Self::Out { DefaultAccept::default_accept(atom, self); }
|
||||
fn visit_atom(&mut self, atom: &Atom) -> Self::Out {
|
||||
DefaultAccept::default_accept(atom, self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ mod locals;
|
||||
mod scope;
|
||||
pub mod thunk;
|
||||
|
||||
use crate::{syn::ast::Body, obj::prelude::*, vm::consts::*};
|
||||
use crate::{obj::prelude::*, syn::ast::Body, vm::consts::*};
|
||||
use scope::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -29,20 +29,28 @@ impl Compile {
|
||||
.to_vec();
|
||||
// XXX TODO(compile)
|
||||
// remove this when we get returns implemented
|
||||
main.push(crate::vm::inst::Inst::PushSym(crate::obj::reserved::NIL_NAME.sym));
|
||||
main.push(crate::vm::inst::Inst::PushSym(
|
||||
crate::obj::reserved::NIL_NAME.sym,
|
||||
));
|
||||
main.push(crate::vm::inst::Inst::Return);
|
||||
let globals_syms: std::collections::BTreeMap<_, _> = self.pop_scope_layer().unwrap()
|
||||
let globals_syms: std::collections::BTreeMap<_, _> = self
|
||||
.pop_scope_layer()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|(sym, name)| (name, sym))
|
||||
.collect();
|
||||
let globals = globals_syms.into_iter()
|
||||
let globals = globals_syms
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, (name, sym))| {
|
||||
assert_eq!(index, name.index());
|
||||
sym
|
||||
})
|
||||
.collect();
|
||||
Ok((self.const_data.const_pool, UserFun::new_obj(main, globals, 0)))
|
||||
Ok((
|
||||
self.const_data.const_pool,
|
||||
UserFun::new_obj(main, globals, 0),
|
||||
))
|
||||
}
|
||||
|
||||
/// Gets the constant data that is interned in this compile session.
|
||||
|
||||
@@ -11,9 +11,7 @@ pub struct Scope {
|
||||
|
||||
impl Scope {
|
||||
pub fn lookup_local(&self, sym: Sym) -> Option<Name> {
|
||||
self.locals()
|
||||
.and_then(|locals| locals.get(&sym))
|
||||
.copied()
|
||||
self.locals().and_then(|locals| locals.get(&sym)).copied()
|
||||
}
|
||||
|
||||
pub fn lookup_global(&self, sym: Sym) -> Option<Name> {
|
||||
@@ -23,8 +21,7 @@ impl Scope {
|
||||
}
|
||||
|
||||
pub fn create_local(&mut self, sym: Sym) -> Name {
|
||||
let locals = self.locals_mut()
|
||||
.expect("locals");
|
||||
let locals = self.locals_mut().expect("locals");
|
||||
if let Some(local) = locals.get(&sym).copied() {
|
||||
local
|
||||
} else {
|
||||
@@ -35,8 +32,7 @@ impl Scope {
|
||||
}
|
||||
|
||||
pub fn create_global(&mut self, sym: Sym) -> Name {
|
||||
let globals = self.globals_mut()
|
||||
.expect("globals");
|
||||
let globals = self.globals_mut().expect("globals");
|
||||
if let Some(global) = globals.get(&sym).copied() {
|
||||
global
|
||||
} else {
|
||||
|
||||
@@ -106,8 +106,7 @@ impl Thunk {
|
||||
}
|
||||
|
||||
pub fn flatten(self) -> BasicBlockList {
|
||||
Flatten::default()
|
||||
.flatten(self)
|
||||
Flatten::default().flatten(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,10 +157,13 @@ impl Flatten {
|
||||
match thunk {
|
||||
Thunk::Body(thunk) => {
|
||||
let this_block = self.this_block();
|
||||
let prev = self.blocks.insert(this_block, BasicBlock::Block {
|
||||
exit: next_block,
|
||||
block: thunk,
|
||||
});
|
||||
let prev = self.blocks.insert(
|
||||
this_block,
|
||||
BasicBlock::Block {
|
||||
exit: next_block,
|
||||
block: thunk,
|
||||
},
|
||||
);
|
||||
assert!(prev.is_none());
|
||||
}
|
||||
Thunk::List(thunks) => {
|
||||
@@ -172,14 +174,20 @@ impl Flatten {
|
||||
}
|
||||
assert_eq!(next_block, self.this_block());
|
||||
}
|
||||
Thunk::Branch { thunk_true, thunk_false, } => {
|
||||
Thunk::Branch {
|
||||
thunk_true,
|
||||
thunk_false,
|
||||
} => {
|
||||
let branch_block = self.this_block();
|
||||
let block_true = self.this_block() + 1;
|
||||
let block_false = block_true + thunk_true.basic_block_count();
|
||||
self.blocks.insert(branch_block, BasicBlock::Branch {
|
||||
block_true,
|
||||
block_false,
|
||||
});
|
||||
self.blocks.insert(
|
||||
branch_block,
|
||||
BasicBlock::Branch {
|
||||
block_true,
|
||||
block_false,
|
||||
},
|
||||
);
|
||||
self.flatten_next(next_block, *thunk_true);
|
||||
self.flatten_next(next_block, *thunk_false);
|
||||
assert_eq!(self.this_block(), next_block);
|
||||
@@ -278,7 +286,10 @@ impl Visit for CompileBody<'_> {
|
||||
if let Some(local) = self.compile.lookup_local(sym) {
|
||||
thunk = Inst::PopLocal(Some(local)).into();
|
||||
} else {
|
||||
let global = self.compile.lookup_global(sym).expect("name expected to exist someplace(?)");
|
||||
let global = self
|
||||
.compile
|
||||
.lookup_global(sym)
|
||||
.expect("name expected to exist someplace(?)");
|
||||
thunk = Inst::PopGlobal(Some(global)).into();
|
||||
}
|
||||
}
|
||||
@@ -368,7 +379,7 @@ impl Visit for CompileBody<'_> {
|
||||
// - 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();
|
||||
@@ -378,9 +389,7 @@ impl Visit for CompileBody<'_> {
|
||||
}
|
||||
|
||||
// Compile function body
|
||||
let mut code = self.visit_body(&expr.body)?
|
||||
.flatten()
|
||||
.to_vec();
|
||||
let mut code = self.visit_body(&expr.body)?.flatten().to_vec();
|
||||
|
||||
// If the last instruction is not a return, or if there are no instructions, then return
|
||||
// :nil value.
|
||||
@@ -390,7 +399,9 @@ impl Visit for CompileBody<'_> {
|
||||
}
|
||||
|
||||
// remap (Sym -> Name) to be (Name -> Sym) and make sure it's all in order.
|
||||
let scope_locals: BTreeMap<_, _> = self.compile.pop_scope_layer()
|
||||
let scope_locals: BTreeMap<_, _> = self
|
||||
.compile
|
||||
.pop_scope_layer()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|(sym, name)| (name, sym))
|
||||
@@ -399,7 +410,8 @@ impl Visit for CompileBody<'_> {
|
||||
// this should be in numeric order since:
|
||||
// 1. locals are created exactly once or looked up
|
||||
// 2. scope_locals is a btreemap, keyed by names, which are in order from 0..N
|
||||
let locals: FunLocals = scope_locals.into_iter()
|
||||
let locals: FunLocals = scope_locals
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, (name, sym))| {
|
||||
assert_eq!(index, name.index());
|
||||
@@ -407,7 +419,9 @@ impl Visit for CompileBody<'_> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let (hdl, _fun) = self.compile.push_const(UserFun::new_obj(code, locals, expr.params.len()));
|
||||
let (hdl, _fun) =
|
||||
self.compile
|
||||
.push_const(UserFun::new_obj(code, locals, expr.params.len()));
|
||||
|
||||
// TODO(compile) : determine return value at the end of the body (preferably at parse-time)
|
||||
|
||||
@@ -427,7 +441,10 @@ impl Visit for CompileBody<'_> {
|
||||
// This checks to make sure that it's both a local variable and that there's more
|
||||
// than one scope layer.
|
||||
let sym = global_sym(ident.to_string());
|
||||
if let (true, Some(local)) = (self.compile.scope().layers_len() > 1, self.compile.lookup_local(sym)) {
|
||||
if let (true, Some(local)) = (
|
||||
self.compile.scope().layers_len() > 1,
|
||||
self.compile.lookup_local(sym),
|
||||
) {
|
||||
// get local
|
||||
Inst::LoadLocal(local).into()
|
||||
} else {
|
||||
@@ -466,25 +483,20 @@ fn test_flatten_thunk() {
|
||||
let init_body = vec![
|
||||
Inst::PushSym(Sym::new(0)),
|
||||
Inst::PushSym(Sym::new(1)),
|
||||
Inst::Call(1)
|
||||
Inst::Call(1),
|
||||
];
|
||||
let true_body = vec![Inst::PushSym(Sym::new(2))];
|
||||
let false_body = vec![Inst::PushSym(Sym::new(3))];
|
||||
let end_body = vec![
|
||||
Inst::PushSym(Sym::new(1)),
|
||||
Inst::Call(1)
|
||||
];
|
||||
let end_body = vec![Inst::PushSym(Sym::new(1)), Inst::Call(1)];
|
||||
|
||||
let thunk = Thunk::List(vec![
|
||||
// do something before
|
||||
Thunk::Body(init_body.clone()),
|
||||
|
||||
// branch
|
||||
Thunk::Branch {
|
||||
thunk_true: Thunk::Body(true_body.clone()).into(),
|
||||
thunk_false: Thunk::Body(false_body.clone()).into(),
|
||||
},
|
||||
|
||||
// do something after
|
||||
Thunk::Body(end_body.clone()),
|
||||
]);
|
||||
@@ -495,10 +507,55 @@ fn test_flatten_thunk() {
|
||||
assert_eq!(blocks.len(), block_count);
|
||||
|
||||
let mut iter = blocks.into_iter();
|
||||
assert_eq!(iter.next().unwrap(), (0, BasicBlock::Block { exit: 1, block: init_body, }));
|
||||
assert_eq!(iter.next().unwrap(), (1, BasicBlock::Branch { block_true: 2, block_false: 3, }));
|
||||
assert_eq!(iter.next().unwrap(), (2, BasicBlock::Block { exit: 4, block: true_body, }));
|
||||
assert_eq!(iter.next().unwrap(), (3, BasicBlock::Block { exit: 4, block: false_body, }));
|
||||
assert_eq!(iter.next().unwrap(), (4, BasicBlock::Block { exit: 5, block: end_body, }));
|
||||
assert_eq!(
|
||||
iter.next().unwrap(),
|
||||
(
|
||||
0,
|
||||
BasicBlock::Block {
|
||||
exit: 1,
|
||||
block: init_body,
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next().unwrap(),
|
||||
(
|
||||
1,
|
||||
BasicBlock::Branch {
|
||||
block_true: 2,
|
||||
block_false: 3,
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next().unwrap(),
|
||||
(
|
||||
2,
|
||||
BasicBlock::Block {
|
||||
exit: 4,
|
||||
block: true_body,
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next().unwrap(),
|
||||
(
|
||||
3,
|
||||
BasicBlock::Block {
|
||||
exit: 4,
|
||||
block: false_body,
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next().unwrap(),
|
||||
(
|
||||
4,
|
||||
BasicBlock::Block {
|
||||
exit: 5,
|
||||
block: end_body,
|
||||
}
|
||||
)
|
||||
);
|
||||
assert!(iter.next().is_none());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user