Add Bool builtin type, branches, and some more stuff
* obj::Bool builtin type is used for truthiness and decision-making * Branches are compiled and seem to be working for basic integer comparison * Updated version of Shredder to what is current as of writing * CheckTruth VM instruction that will explicitly set the condition flag * Probably some other stuff Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
compile::{basic_block::*, error::*, Compile},
|
||||
obj::prelude::*,
|
||||
obj::{prelude::*, reserved::*},
|
||||
syn::{ast::*, visit::*},
|
||||
vm::inst::*,
|
||||
};
|
||||
@@ -25,10 +25,7 @@ pub enum Thunk {
|
||||
///
|
||||
/// Only one of these thunks will be executed. At the end of either thunk, the program will
|
||||
/// continue at the address following this branch.
|
||||
Branch {
|
||||
thunk_true: Box<Thunk>,
|
||||
thunk_false: Box<Thunk>,
|
||||
},
|
||||
Branch(ThunkBranch),
|
||||
|
||||
/// Based on the conditional flag in the VM, code for this loop will continue to execute.
|
||||
///
|
||||
@@ -94,11 +91,11 @@ impl Thunk {
|
||||
Thunk::List(thunks) => thunks
|
||||
.iter()
|
||||
.fold(0, |n, thunk| n + thunk.basic_block_count()),
|
||||
Thunk::Branch {
|
||||
Thunk::Branch(ThunkBranch {
|
||||
preamble,
|
||||
thunk_true,
|
||||
thunk_false,
|
||||
// length is true + false block count, + 1 for the branch basic block at the start
|
||||
} => thunk_true.basic_block_count() + thunk_false.basic_block_count() + 1,
|
||||
}) => preamble.basic_block_count() + thunk_true.basic_block_count() + thunk_false.basic_block_count() + 1,
|
||||
// length is thunk, + 1 for branch at the start of the loop
|
||||
Thunk::Loop(thunk) => thunk.basic_block_count() + 1,
|
||||
Thunk::Nop => 0,
|
||||
@@ -128,6 +125,16 @@ impl From<Vec<Thunk>> for Thunk {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// struct ThunkBranch
|
||||
//
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ThunkBranch {
|
||||
preamble: Box<Thunk>,
|
||||
thunk_true: Box<Thunk>,
|
||||
thunk_false: Box<Thunk>,
|
||||
}
|
||||
|
||||
//
|
||||
// struct Flatten
|
||||
//
|
||||
@@ -150,6 +157,9 @@ impl Flatten {
|
||||
let last_block = thunk.basic_block_count();
|
||||
self.flatten_next(last_block, thunk);
|
||||
assert_eq!(self.blocks.len(), last_block);
|
||||
// push an extra null block at the very end so that anything pointing to `last_block` will
|
||||
// have a valid block index
|
||||
self.blocks.insert(last_block, BasicBlock::Block { exit: last_block + 1, block: Default::default() });
|
||||
self.blocks
|
||||
}
|
||||
|
||||
@@ -174,13 +184,17 @@ impl Flatten {
|
||||
}
|
||||
assert_eq!(next_block, self.this_block());
|
||||
}
|
||||
Thunk::Branch {
|
||||
Thunk::Branch(ThunkBranch {
|
||||
preamble,
|
||||
thunk_true,
|
||||
thunk_false,
|
||||
} => {
|
||||
let branch_block = self.this_block();
|
||||
let block_true = self.this_block() + 1;
|
||||
}) => {
|
||||
let preamble_block = self.this_block();
|
||||
let branch_block = preamble_block + preamble.basic_block_count();
|
||||
let block_true = branch_block + 1;
|
||||
let block_false = block_true + thunk_true.basic_block_count();
|
||||
self.flatten_next(branch_block, *preamble);
|
||||
assert_eq!(self.this_block(), branch_block);
|
||||
self.blocks.insert(
|
||||
branch_block,
|
||||
BasicBlock::Branch {
|
||||
@@ -188,7 +202,9 @@ impl Flatten {
|
||||
block_false,
|
||||
},
|
||||
);
|
||||
assert_eq!(self.this_block(), block_true);
|
||||
self.flatten_next(next_block, *thunk_true);
|
||||
assert_eq!(self.this_block(), block_false);
|
||||
self.flatten_next(next_block, *thunk_false);
|
||||
assert_eq!(self.this_block(), next_block);
|
||||
}
|
||||
@@ -263,7 +279,7 @@ impl Visit for CompileBody<'_> {
|
||||
let mut thunk = if let Some(expr) = stmt.expr.as_ref() {
|
||||
self.visit_expr(expr)?
|
||||
} else {
|
||||
Inst::PushSym(crate::obj::reserved::NIL_NAME.sym).into()
|
||||
Inst::PushSym(NIL_NAME.sym).into()
|
||||
};
|
||||
thunk.push(Inst::Return);
|
||||
|
||||
@@ -394,7 +410,7 @@ impl Visit for CompileBody<'_> {
|
||||
// If the last instruction is not a return, or if there are no instructions, then return
|
||||
// :nil value.
|
||||
if !matches!(code.last(), Some(Inst::Return)) {
|
||||
code.push(Inst::PushSym(crate::obj::reserved::NIL_NAME.sym));
|
||||
code.push(Inst::PushSym(NIL_NAME.sym));
|
||||
code.push(Inst::Return);
|
||||
}
|
||||
|
||||
@@ -429,6 +445,46 @@ impl Visit for CompileBody<'_> {
|
||||
Ok(Inst::PushConst(hdl).into())
|
||||
}
|
||||
|
||||
fn visit_if_expr(&mut self, expr: &IfExpr) -> Self::Out {
|
||||
// base if condition
|
||||
let mut thunk = self.visit_cond_body(&expr.cond_body)?;
|
||||
{
|
||||
// elif branches
|
||||
let mut prev_thunk: &mut Thunk = &mut thunk;
|
||||
for elif_cond_body in expr.elif.iter() {
|
||||
let elif_thunk = self.visit_cond_body(elif_cond_body)?;
|
||||
if let Thunk::Branch(thunk_branch) = prev_thunk {
|
||||
thunk_branch.thunk_false = Box::new(elif_thunk);
|
||||
prev_thunk = &mut thunk_branch.thunk_false;
|
||||
} else {
|
||||
unreachable!("accidentally found a non-branch thunk in elif expression")
|
||||
}
|
||||
}
|
||||
|
||||
// el branch
|
||||
if let (Some(el_body), Thunk::Branch(thunk_branch)) = (&expr.el, prev_thunk) {
|
||||
thunk_branch.thunk_false = Box::new(self.visit_body(el_body)?);
|
||||
//prev_thunk = &mut thunk_branch.thunk_false;
|
||||
}
|
||||
}
|
||||
Ok(thunk)
|
||||
}
|
||||
|
||||
fn visit_cond_body(&mut self, cond_body: &CondBody) -> Self::Out {
|
||||
let mut preamble = self.visit_expr(&cond_body.cond)?;
|
||||
// Attempt to call the __bool__ function on this object which leaves a value on the stack
|
||||
preamble.push_thunk(vec![
|
||||
Inst::GetAttr(BOOL_MEMBER_NAME.sym),
|
||||
Inst::Call(0),
|
||||
Inst::CheckTruth,
|
||||
]);
|
||||
Ok(Thunk::Branch(ThunkBranch {
|
||||
preamble: preamble.into(),
|
||||
thunk_true: self.visit_body(&cond_body.body)?.into(),
|
||||
thunk_false: Box::new(Thunk::Nop),
|
||||
}))
|
||||
}
|
||||
|
||||
fn visit_atom(&mut self, atom: &Atom) -> Self::Out {
|
||||
let thunk = match atom {
|
||||
Atom::Ident(ident) => {
|
||||
@@ -490,13 +546,12 @@ fn test_flatten_thunk() {
|
||||
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::Branch(ThunkBranch {
|
||||
preamble: Thunk::Body(init_body.clone()).into(),
|
||||
thunk_true: Thunk::Body(true_body.clone()).into(),
|
||||
thunk_false: Thunk::Body(false_body.clone()).into(),
|
||||
},
|
||||
}),
|
||||
// do something after
|
||||
Thunk::Body(end_body.clone()),
|
||||
]);
|
||||
@@ -504,7 +559,7 @@ fn test_flatten_thunk() {
|
||||
let block_count = thunk.basic_block_count();
|
||||
|
||||
let blocks = thunk.flatten();
|
||||
assert_eq!(blocks.len(), block_count);
|
||||
assert_eq!(blocks.len(), block_count + 1);
|
||||
|
||||
let mut iter = blocks.into_iter();
|
||||
assert_eq!(
|
||||
@@ -557,5 +612,16 @@ fn test_flatten_thunk() {
|
||||
}
|
||||
)
|
||||
);
|
||||
// empty block
|
||||
assert_eq!(
|
||||
iter.next().unwrap(),
|
||||
(
|
||||
5,
|
||||
BasicBlock::Block {
|
||||
exit: 6,
|
||||
block: vec![],
|
||||
}
|
||||
)
|
||||
);
|
||||
assert!(iter.next().is_none());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user