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:
2020-11-06 16:57:25 -08:00
parent 692bb521ec
commit be9a659159
17 changed files with 435 additions and 78 deletions

View File

@@ -1,5 +1,6 @@
use not_python::{compile::Compile, syn::ast, vm::Vm};
use shredder::run_with_gc_cleanup;
use std::{fs, path::PathBuf};
use structopt::StructOpt;
@@ -23,42 +24,43 @@ struct Options {
}
fn main() -> Result<()> {
let opt = Options::from_args();
let text = fs::read_to_string(&opt.input)?;
let action = &opt.action.to_lowercase();
match action.as_str() {
"run" | "parse" | "dump" => {}
_ => eprintln!("WARNING: unknown action {}", action),
}
let ast = match ast::parse(text.as_str()) {
Ok(ast) => ast,
Err(errs) => {
for err in errs {
eprintln!("{}", err);
}
return Err("errors reported, exiting".into());
run_with_gc_cleanup(|| {
let opt = Options::from_args();
let text = fs::read_to_string(&opt.input)?;
let action = &opt.action.to_lowercase();
match action.as_str() {
"run" | "parse" | "dump" => {}
_ => eprintln!("WARNING: unknown action {}", action),
}
}
.unwrap();
if action == "parse" {
println!("{:#?}", ast);
return Ok(());
}
let (const_pool, main) = Compile::new().compile(&ast)?;
let ast = match ast::parse(text.as_str()) {
Ok(ast) => ast,
Err(errs) => {
for err in errs {
eprintln!("{}", err);
}
return Err("errors reported, exiting".into());
}
}
.unwrap();
if action == "parse" {
println!("{:#?}", ast);
return Ok(());
}
if action == "dump" {
let mut stdout = std::io::stdout();
not_python::read_obj!(let main_obj = main);
main_obj.dump(&mut stdout, &const_pool, main_obj.locals())?;
return Ok(());
}
let (const_pool, main) = Compile::new().compile(&ast)?;
if action == "run" {
let mut vm = Vm::new(&const_pool);
vm.call(main, vec![])?;
}
if action == "dump" {
let mut stdout = std::io::stdout();
not_python::read_obj!(let main_obj = main);
main_obj.dump(&mut stdout, &const_pool, main_obj.locals())?;
return Ok(());
}
Ok(())
if action == "run" {
let mut vm = Vm::new(&const_pool);
vm.call(main, vec![])?;
}
Ok(())
})
}

View File

@@ -76,6 +76,12 @@ impl Visit for CollectLocals<'_> {
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_if_expr(&mut self, expr: &IfExpr) -> Self::Out {
DefaultAccept::default_accept(expr, self);
}
fn visit_cond_body(&mut self, cond_body: &CondBody) -> Self::Out {
DefaultAccept::default_accept(cond_body, self);
}
fn visit_atom(&mut self, atom: &Atom) -> Self::Out {
DefaultAccept::default_accept(atom, self);
}

View File

@@ -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());
}

92
src/obj/boolean.rs Normal file
View File

@@ -0,0 +1,92 @@
use crate::{
obj::{prelude::*, reserved::*},
vm::error::*,
};
use once_cell::sync::Lazy;
use shredder::Scan;
use std::fmt::{self, Debug, Formatter};
pub type BoolRef = ObjRef<Bool>;
#[derive(Scan)]
pub struct Bool {
value: bool,
vtable: Vtable,
attrs: Attrs,
}
impl Bool {
/// Gets the value of this boolean.
pub fn value(&self) -> bool {
self.value
}
}
impl From<bool> for BoolRef {
fn from(value: bool) -> Self {
if value {
BOOL_TRUE.clone()
} else {
BOOL_FALSE.clone()
}
}
}
impl_obj_readonly!(Bool);
impl Debug for Bool {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
fmt.debug_struct("Bool").field("value", &self.value).finish()
}
}
/// A true boolean value.
///
/// This value should be the only instance of a "true" boolean.
pub static BOOL_TRUE: Lazy<BoolRef> = Lazy::new(||
self_referring_obj! {
Bool {
value: true,
vtable: Default::default(),
attrs: Default::default(),
},
vtable: |obj_ref: ObjRef| vtable! {
BOOL_MEMBER_NAME.sym => Method::new_obj(obj_ref.clone(), BOOL_BOOL_FUN.clone()),
}
}
);
/// A false boolean value.
///
/// This value should be the only instance of a "false" boolean.
pub static BOOL_FALSE: Lazy<BoolRef> = Lazy::new(||
self_referring_obj! {
Bool {
value: false,
vtable: Default::default(),
attrs: Default::default(),
},
vtable: |obj_ref: ObjRef| vtable! {
BOOL_MEMBER_NAME.sym => Method::new_obj(obj_ref.clone(), BOOL_BOOL_FUN.clone()),
}
}
);
////////////////////////////////////////////////////////////////////////////////
// Builtin Bool functions
////////////////////////////////////////////////////////////////////////////////
/// Bool.__bool__(self) impl
static BOOL_BOOL_FUN: Lazy<NativeFunRef> = Lazy::new(|| {
NativeFun::new_obj(1, |_callee, vm, args| {
{
read_obj_downcast!(let value: Option<&Bool> = &args[0]);
value.ok_or_else(|| Error::ValueError {
error: "expected Bool value".to_string(),
})?;
}
// just push a copy of the "self" value
vm.push(args[0].clone());
Ok(crate::vm::signal::Signal::Return)
})
});

View File

@@ -3,7 +3,7 @@ use crate::{
vm::{consts::ConstPool, error::*, frame::*, inst::Inst, signal::*, Vm},
};
use once_cell::sync::Lazy;
use shredder::{GcSafeWrapper, Scan};
use shredder::Scan;
use std::{
fmt::{self, Debug, Formatter},
io::{self, Write},
@@ -88,10 +88,10 @@ pub struct UserFun {
vtable: Vtable,
attrs: Attrs,
// Safe because Vec<Inst> doesn't need to be scanned
#[shredder(unsafe_skip)]
#[shredder(unsafe_skip_gc_safe)]
code: Arc<Vec<Inst>>,
// Safe because this is just an interner that points to symbols, which aren't GC'd
#[shredder(unsafe_skip)]
#[shredder(unsafe_skip_gc_safe)]
locals: FunLocals,
arity: usize,
}
@@ -179,7 +179,7 @@ impl UserFun {
("(discarded)".to_string(), String::new())
}
}
Inst::Jump(addr) | Inst::JumpTrue(addr) => (addr.to_string(), String::new()),
Inst::Jump(addr) | Inst::JumpTrue(addr) => (format!("{:0addr_w$x}", addr, addr_w = addr_w), String::new()),
Inst::Call(argc) => (argc.to_string(), String::new()),
_ => (String::new(), String::new()),
};
@@ -261,15 +261,32 @@ pub static USER_FUN_TY: Lazy<ObjRef<Ty>> = Lazy::new(|| Ty::new_obj(USER_FUN_NAM
// struct NativeFun
//
pub type NativeFunPtr = fn(ObjRef, &mut Vm, Vec<ObjRef>) -> Result<Signal>;
pub type FunPtr = fn(ObjRef, &mut Vm, Vec<ObjRef>) -> Result<Signal>;
pub struct NativeFunPtr(pub FunPtr);
impl Clone for NativeFunPtr {
fn clone(&self) -> Self {
NativeFunPtr(self.0)
}
}
impl std::ops::Deref for NativeFunPtr {
type Target = FunPtr;
fn deref(&self) -> &Self::Target {
&self.0
}
}
unsafe impl shredder::marker::GcDrop for NativeFunPtr { }
pub type NativeFunRef = ObjRef<NativeFun>;
#[derive(Scan)]
pub struct NativeFun {
vtable: Vtable,
attrs: Attrs,
#[shredder(skip)]
fun: GcSafeWrapper<NativeFunPtr>,
#[shredder(unsafe_skip_gc_safe)]
fun: NativeFunPtr,
arity: usize,
}
@@ -278,12 +295,12 @@ pub struct NativeFun {
//
impl NativeFun {
pub fn new_obj(arity: usize, fun: NativeFunPtr) -> ObjRef<Self> {
pub fn new_obj(arity: usize, fun: FunPtr) -> ObjRef<Self> {
self_referring_obj! (
Self {
vtable: Default::default(),
attrs: Default::default(),
fun: GcSafeWrapper::new(fun),
fun: NativeFunPtr(fun),
arity,
},
vtable: |obj_ref: ObjRef| vtable! {
@@ -345,7 +362,7 @@ impl Fun for NativeFun {
got: args.len(),
});
}
Ok(Frame::Native(NativeFrame::new(callee, *self.fun, args)))
Ok(Frame::Native(NativeFrame::new(callee, self.fun.clone(), args)))
}
}

View File

@@ -26,6 +26,7 @@ impl Int {
attrs: Default::default(),
},
vtable: |obj_ref: ObjRef| vtable! {
BOOL_MEMBER_NAME.sym => Method::new_obj(obj_ref.clone(), INT_BOOL_FUN.clone()),
REPR_MEMBER_NAME.sym => Method::new_obj(obj_ref.clone(), INT_STR_FUN.clone()),
STR_MEMBER_NAME.sym => Method::new_obj(obj_ref.clone(), INT_STR_FUN.clone()),
INT_MEMBER_NAME.sym => Method::new_obj(obj_ref.clone(), INT_INT_FUN.clone()),
@@ -33,6 +34,8 @@ impl Int {
MINUS_OP_NAME.sym => Method::new_obj(obj_ref.clone(), INT_SUB_FUN.clone()),
TIMES_OP_NAME.sym => Method::new_obj(obj_ref.clone(), INT_MUL_FUN.clone()),
DIV_OP_NAME.sym => Method::new_obj(obj_ref.clone(), INT_DIV_FUN.clone()),
EQ_OP_NAME.sym => Method::new_obj(obj_ref.clone(), INT_EQ_FUN.clone()),
NE_OP_NAME.sym => Method::new_obj(obj_ref.clone(), INT_NE_FUN.clone()),
}
}
}
@@ -54,6 +57,18 @@ impl Debug for Int {
// Builtin Int functions
////////////////////////////////////////////////////////////////////////////////
/// Int.__bool__(self) impl
static INT_BOOL_FUN: Lazy<NativeFunRef> = Lazy::new(|| {
NativeFun::new_obj(1, |_callee, vm, args| {
read_obj_downcast!(let int_obj: Option<&Int> = &args[0]);
let int_obj = int_obj.ok_or_else(|| Error::ValueError {
error: "expected Int value".to_string(),
})?;
vm.push(BoolRef::from(int_obj.value != 0));
Ok(crate::vm::signal::Signal::Return)
})
});
/// Int.__str__(self) impl
static INT_STR_FUN: Lazy<NativeFunRef> = Lazy::new(|| {
NativeFun::new_obj(1, |_callee, vm, args| {
@@ -157,3 +172,41 @@ static INT_DIV_FUN: Lazy<NativeFunRef> = Lazy::new(|| {
Ok(crate::vm::signal::Signal::Return)
})
});
/// Int.__eq__(self, Int) impl
static INT_EQ_FUN: Lazy<NativeFunRef> = Lazy::new(|| {
NativeFun::new_obj(2, |_callee, vm, args| {
read_obj_downcast!(let lhs: Option<&Int> = &args[0]);
let lhs = lhs.ok_or_else(|| Error::ValueError {
error: "expected int for LHS".to_string(),
})?;
read_obj_downcast!(let rhs: Option<&Int> = &args[1]);
let rhs = rhs.ok_or_else(|| Error::ValueError {
error: "expected int for RHS".to_string(),
})?;
let result = lhs.value == rhs.value;
vm.push(BoolRef::from(result));
Ok(crate::vm::signal::Signal::Return)
})
});
/// Int.__ne__(self, Int) impl
static INT_NE_FUN: Lazy<NativeFunRef> = Lazy::new(|| {
NativeFun::new_obj(2, |_callee, vm, args| {
read_obj_downcast!(let lhs: Option<&Int> = &args[0]);
let lhs = lhs.ok_or_else(|| Error::ValueError {
error: "expected int for LHS".to_string(),
})?;
read_obj_downcast!(let rhs: Option<&Int> = &args[1]);
let rhs = rhs.ok_or_else(|| Error::ValueError {
error: "expected int for RHS".to_string(),
})?;
let result = lhs.value != rhs.value;
vm.push(BoolRef::from(result));
Ok(crate::vm::signal::Signal::Return)
})
});

View File

@@ -108,7 +108,7 @@ macro_rules! vtable {
#[macro_export]
macro_rules! handle_type {
($name:ident) => {
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, shredder::Scan)]
pub struct $name(usize);
impl $name {
@@ -128,7 +128,6 @@ macro_rules! handle_type {
}
}
impl shredder::EmptyScan for $name {}
};
}

View File

@@ -2,6 +2,7 @@
mod macros;
pub mod attrs;
pub mod boolean;
pub mod builtin;
pub mod fun;
pub mod int;
@@ -14,11 +15,11 @@ pub mod ty;
pub mod prelude {
pub use crate::obj::{
attrs::*, fun::*, int::*, intern::*, name::*, str::*, sym::*, ty::*, Obj, ObjRef,
attrs::*, boolean::*, fun::*, int::*, intern::*, name::*, str::*, sym::*, ty::*, Obj, ObjRef,
};
}
use shredder::{Gc, Scan};
use shredder::{marker::GcDrop, Gc, Scan};
use std::{
fmt::{self, Debug, Formatter},
marker::Unsize,
@@ -43,7 +44,7 @@ macro_rules! obj_attr {
};
}
pub trait Obj: Scan + Debug {
pub trait Obj: Scan + GcDrop + Debug {
fn vtable(&self) -> &Vtable;
fn attrs(&self) -> &Attrs;
fn attrs_mut(&mut self) -> Option<&mut Attrs>;

View File

@@ -46,6 +46,7 @@ name!(FUNC_MEMBER_NAME, "__func__");
name!(REPR_MEMBER_NAME, "__repr__");
name!(STR_MEMBER_NAME, "__str__");
name!(INT_MEMBER_NAME, "__int__");
name!(BOOL_MEMBER_NAME, "__bool__");
//
// Predefined VM-aware symbols
@@ -64,8 +65,6 @@ name!(PRINT_BUILTIN_NAME, "print");
//
// Builtin constants
//
name!(TRUE_NAME, "true");
name!(FALSE_NAME, "false");
name!(NIL_NAME, "nil");
// Operator function names

View File

@@ -133,6 +133,7 @@ pub enum Expr {
Index(Box<IndexExpr>),
Access(Box<AccessExpr>),
Fun(Box<FunExpr>),
If(Box<IfExpr>),
Atom(Atom),
}
@@ -157,6 +158,7 @@ impl<V: Visit> DefaultAccept<V> for Expr {
Expr::Index(i) => i.accept(visitor),
Expr::Access(a) => a.accept(visitor),
Expr::Fun(f) => f.accept(visitor),
Expr::If(i) => i.accept(visitor),
Expr::Atom(a) => a.accept(visitor),
}
}
@@ -207,6 +209,12 @@ impl From<FunExpr> for Expr {
}
}
impl From<IfExpr> for Expr {
fn from(other: IfExpr) -> Self {
Expr::If(other.into())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BinOp {
Plus,
@@ -434,6 +442,78 @@ impl<V: Visit> DefaultAccept<V> for FunExpr {
}
}
//
// struct IfExpr
//
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IfExpr {
pub cond_body: CondBody,
pub elif: Vec<CondBody>,
pub el: Option<Body>,
}
impl IfExpr {
pub fn new_expr(cond_body: CondBody, elif: Vec<CondBody>, el: Option<Body>) -> Expr {
Self {
cond_body,
elif,
el,
}.into()
}
}
//
// impl Accept for IfExpr
//
impl<V: Visit> Accept<V> for IfExpr {
fn accept(&self, visitor: &mut V) -> V::Out {
visitor.visit_if_expr(self)
}
}
//
// impl DefaultAccept for IfExpr
//
impl<V: Visit<Out=()>> DefaultAccept<V> for IfExpr {
fn default_accept(&self, visitor: &mut V) -> V::Out {
visitor.visit_cond_body(&self.cond_body);
for elif in self.elif.iter() {
visitor.visit_cond_body(elif);
}
if let Some(el) = &self.el {
visitor.visit_body(&el);
}
}
}
//
// struct CondBody
//
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CondBody {
pub cond: Expr,
pub body: Body,
}
//
// impl Accept for CondBody
//
impl<V: Visit> Accept<V> for CondBody {
fn accept(&self, visitor: &mut V) -> V::Out {
visitor.visit_cond_body(self)
}
}
//
// impl DefaultAccept for CondBody
//
impl<V: Visit<Out=()>> DefaultAccept<V> for CondBody {
fn default_accept(&self, visitor: &mut V) -> V::Out {
visitor.visit_expr(&self.cond);
visitor.visit_body(&self.body);
}
}
//
// enum Atom
//

View File

@@ -1,6 +1,9 @@
%%
fn "fn"
return "return"
if "if"
elif "elif"
el "el"
[\r\n;]+ "EOL"
[a-zA-Z_][a-zA-Z0-9_]* "IDENT"

View File

@@ -104,7 +104,7 @@ AccessExpr -> Result<Expr>:
;
FunExpr -> Result<Expr>:
AtomExpr { $1 }
IfExpr { $1 }
| 'fn' '(' FunParams ')' '{' Body '}' { Ok(FunExpr::new_expr($3?, $6?)) }
;
@@ -118,6 +118,31 @@ FunParamsTail -> Result<Vec<String>>:
| Ident { Ok(vec![$1?]) }
;
IfExpr -> Result<Expr>:
AtomExpr { $1 }
| 'if' Expr '{' Body '}' Elif El {
Ok(IfExpr::new_expr(
CondBody { cond: $2?, body: $4?, },
$6?,
$7?,
))
}
;
Elif -> Result<Vec<CondBody>>:
Elif 'elif' Expr '{' Body '}' {
let mut head = $1?;
head.push(CondBody { cond: $3?, body: $5? });
Ok(head)
}
| { Ok(Vec::new()) }
;
El -> Result<Option<Body>>:
'el' '{' Body '}' { $3.map(Some) }
| { Ok(None) }
;
AtomExpr -> Result<Expr>:
Atom { $1.map(Expr::Atom) }
| '(' Expr ')' { $2 }

View File

@@ -23,6 +23,8 @@ pub trait Visit {
fn visit_index_expr(&mut self, expr: &IndexExpr) -> Self::Out;
fn visit_access_expr(&mut self, expr: &AccessExpr) -> Self::Out;
fn visit_fun_expr(&mut self, expr: &FunExpr) -> Self::Out;
fn visit_if_expr(&mut self, expr: &IfExpr) -> Self::Out;
fn visit_cond_body(&mut self, cond_body: &CondBody) -> Self::Out;
fn visit_atom(&mut self, atom: &Atom) -> Self::Out;
}
@@ -41,6 +43,8 @@ copy/paste of default_accepts
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 { DefaultAccept::default_accept(expr, self); }
fn visit_if_expr(&mut self, expr: &IfExpr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
fn visit_cond_body(&mut self, cond_body: &CondBody) -> Self::Out { DefaultAccept::default_accept(cond_body, self); }
fn visit_atom(&mut self, atom: &Atom) -> Self::Out { DefaultAccept(atom, self); }
*/

View File

@@ -7,9 +7,6 @@ pub struct ConstPool {
pool: Vec<ObjRef>,
}
// Constant pools are constant, and live for the lifetime of the entire program.
impl shredder::EmptyScan for ConstPool {}
impl ConstPool {
pub fn new() -> Self {
Default::default()

View File

@@ -1,5 +1,4 @@
use crate::{obj::prelude::*, vm::consts::*};
use shredder::EmptyScan;
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Inst {
@@ -36,6 +35,9 @@ pub enum Inst {
///
SetAttr(Sym),
/// Pops the top value off of the stack and checks if it is a `Bool` of a true value.
CheckTruth,
/// Jump to a given address in the current function unconditionally.
Jump(usize),
@@ -131,6 +133,7 @@ impl Inst {
Inst::PopGlobal(_) => "POP_GLOBAL",
Inst::GetAttr(_) => "GET_ATTR",
Inst::SetAttr(_) => "SET_ATTR",
Inst::CheckTruth => "CHECK_TRUTH",
Inst::Jump(_) => "JUMP",
Inst::JumpTrue(_) => "JUMP_TRUE",
Inst::Call(_) => "CALL",
@@ -154,4 +157,4 @@ impl Inst {
}
}
impl EmptyScan for Inst {}
unsafe impl shredder::marker::GcDrop for Inst {}

View File

@@ -254,6 +254,15 @@ impl<'c> Vm<'c> {
todo!("TODO: throw an error for attributes that can't be set");
}
}
Inst::CheckTruth => {
let source = self.pop().expect("no source available for CheckTruth");
read_obj_downcast!(let obj: Option<&Bool> = &source);
if let Some(obj) = obj {
self.condition = obj.value();
} else {
unreachable!("Expected a boolean for CheckTruth but got {:?} instead", source);
}
}
Inst::Jump(addr) => {
next_pc = addr;
}
@@ -272,12 +281,12 @@ impl<'c> Vm<'c> {
.expect("TODO: throw an error for missing __call__ attr");
signal = Some(Signal::Call(callee, args));
}
Inst::Index => todo!(),
Inst::Index => todo!("Inst::Index"),
Inst::Return => {
signal = Some(Signal::Return);
}
Inst::UnNeg => todo!(),
Inst::UnPos => todo!(),
Inst::UnNeg => todo!("Inst::UnNeg"),
Inst::UnPos => todo!("Inst::UnPos"),
Inst::BinPlus => {
let rhs = self.pop().unwrap();
let lhs = self.pop().unwrap();