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,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(())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
92
src/obj/boolean.rs
Normal 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)
|
||||
})
|
||||
});
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
});
|
||||
|
||||
@@ -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 {}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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); }
|
||||
|
||||
*/
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user