Add base branch logic
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -2,3 +2,27 @@ println(123);
|
|||||||
println(None);
|
println(None);
|
||||||
message = "hell world";
|
message = "hell world";
|
||||||
println(message);
|
println(message);
|
||||||
|
|
||||||
|
if 1 {
|
||||||
|
println("OK");
|
||||||
|
} else if 0 {
|
||||||
|
println("1 - fail?");
|
||||||
|
} else {
|
||||||
|
println("2 - fail?");
|
||||||
|
};
|
||||||
|
|
||||||
|
if 0 {
|
||||||
|
println("3 - fail?");
|
||||||
|
} else if 1 {
|
||||||
|
println("OK");
|
||||||
|
} else {
|
||||||
|
println("4 - fail?");
|
||||||
|
};
|
||||||
|
|
||||||
|
if 0 {
|
||||||
|
println("5 - fail?");
|
||||||
|
} else if 0 {
|
||||||
|
println("6 - fail?");
|
||||||
|
} else {
|
||||||
|
println("OK");
|
||||||
|
};
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use crate::compile::{scope::GlobalScope, thunk::Thunk};
|
use crate::compile::{scope::GlobalScope, thunk::Thunk};
|
||||||
use crate::obj::prelude::*;
|
use crate::obj::prelude::*;
|
||||||
use crate::obj::ObjPtr;
|
use crate::obj::ObjPtr;
|
||||||
use crate::syn::ast::*;
|
use crate::syn::ast::*;
|
||||||
use crate::vm::inst::Inst;
|
use crate::vm::inst::*;
|
||||||
|
|
||||||
pub struct Compiler {
|
pub struct Compiler {
|
||||||
scope: GlobalScope,
|
scope: GlobalScope,
|
||||||
@@ -28,20 +30,28 @@ impl Compiler {
|
|||||||
/// Compile the given list of AST statements, returning the body.
|
/// Compile the given list of AST statements, returning the body.
|
||||||
pub fn compile_stmts(&mut self, stmts: &Vec<SpStmt>) -> Vec<Inst> {
|
pub fn compile_stmts(&mut self, stmts: &Vec<SpStmt>) -> Vec<Inst> {
|
||||||
self.gather_names(stmts);
|
self.gather_names(stmts);
|
||||||
let mut thunks = Vec::new();
|
let mut thunks = VecDeque::new();
|
||||||
for stmt in stmts {
|
for stmt in stmts {
|
||||||
thunks.push(self.emit_stmt(stmt));
|
thunks.push_back(self.emit_stmt(stmt));
|
||||||
}
|
}
|
||||||
Thunk::List(thunks).flatten()
|
Thunk::List(thunks).flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn emit_body(&mut self, body: &SpBody) -> Thunk {
|
||||||
|
let mut thunks = VecDeque::new();
|
||||||
|
for stmt in body.inner() {
|
||||||
|
thunks.push_back(self.emit_stmt(stmt));
|
||||||
|
}
|
||||||
|
Thunk::List(thunks)
|
||||||
|
}
|
||||||
|
|
||||||
fn emit_stmt(&mut self, stmt: &SpStmt) -> Thunk {
|
fn emit_stmt(&mut self, stmt: &SpStmt) -> Thunk {
|
||||||
match stmt.inner() {
|
match stmt.inner() {
|
||||||
Stmt::Assign(lhs_expr, expr) => match lhs_expr.inner() {
|
Stmt::Assign(lhs_expr, expr) => match lhs_expr.inner() {
|
||||||
AssignLhs::Name(n) => {
|
AssignLhs::Name(n) => {
|
||||||
let name = self.scope().lookup_scoped(n).unwrap();
|
let name = self.scope().lookup_scoped(n).unwrap();
|
||||||
let mut thunk = self.emit_expr(expr);
|
let mut thunk = self.emit_expr(expr);
|
||||||
thunk.push(Inst::Store(name));
|
thunk.push_back(Inst::Store(name));
|
||||||
thunk
|
thunk
|
||||||
}
|
}
|
||||||
AssignLhs::Complex(_, _) => {
|
AssignLhs::Complex(_, _) => {
|
||||||
@@ -50,10 +60,80 @@ impl Compiler {
|
|||||||
},
|
},
|
||||||
Stmt::Expr(expr) => {
|
Stmt::Expr(expr) => {
|
||||||
let mut thunk = self.emit_expr(expr);
|
let mut thunk = self.emit_expr(expr);
|
||||||
thunk.push(Inst::Pop);
|
thunk.push_back(Inst::Pop);
|
||||||
thunk
|
thunk
|
||||||
}
|
}
|
||||||
Stmt::If { .. } => todo!(),
|
Stmt::If {
|
||||||
|
if_true,
|
||||||
|
elseif,
|
||||||
|
else_body,
|
||||||
|
} => {
|
||||||
|
// compile the "else" thunk first - it doesn't need to jump
|
||||||
|
let mut else_thunk = if let Some(else_body) = else_body {
|
||||||
|
self.emit_body(else_body)
|
||||||
|
} else {
|
||||||
|
Thunk::empty()
|
||||||
|
};
|
||||||
|
else_thunk.push_front(Inst::Comment("else body".to_string()));
|
||||||
|
|
||||||
|
// compile the "else if" thunks next. These need to jump forward N
|
||||||
|
// instructions at the very end
|
||||||
|
let mut jump_forward: isize = (else_thunk.len() + 1).try_into().unwrap();
|
||||||
|
let mut thunks = VecDeque::with_capacity(
|
||||||
|
1 + // else thunk
|
||||||
|
elseif.len() + // elseif thunks
|
||||||
|
1, // if thunk
|
||||||
|
);
|
||||||
|
thunks.push_front(else_thunk);
|
||||||
|
|
||||||
|
for cond_block in elseif.iter().rev() {
|
||||||
|
// do block, then condition
|
||||||
|
let mut body_thunk = self.emit_body(&cond_block.inner().body);
|
||||||
|
// jump forward by the previous jump forward distance
|
||||||
|
body_thunk.push_front(Inst::Comment("elseif body".to_string()));
|
||||||
|
body_thunk.push_back(Inst::JumpRelative(jump_forward, JumpCondition::Always));
|
||||||
|
|
||||||
|
let mut cond_thunk = self.emit_expr(&cond_block.inner().cond);
|
||||||
|
cond_thunk.push_front(Inst::Comment("elseif compare ".to_string()));
|
||||||
|
cond_thunk.push_back(Inst::Compare);
|
||||||
|
// jump forward by the length of the body
|
||||||
|
cond_thunk.push_back(Inst::JumpRelative(
|
||||||
|
isize::try_from(body_thunk.len()).unwrap() + 1,
|
||||||
|
JumpCondition::False,
|
||||||
|
));
|
||||||
|
|
||||||
|
jump_forward +=
|
||||||
|
isize::try_from(body_thunk.len() + cond_thunk.len()).unwrap() + 1;
|
||||||
|
|
||||||
|
thunks.push_front(body_thunk);
|
||||||
|
thunks.push_front(cond_thunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
// same as above: do block, then condition
|
||||||
|
let mut body_thunk = self.emit_body(&if_true.inner().body);
|
||||||
|
body_thunk.push_front(Inst::Comment("if body".to_string()));
|
||||||
|
body_thunk.push_back(Inst::JumpRelative(jump_forward, JumpCondition::Always));
|
||||||
|
|
||||||
|
let mut cond_thunk = self.emit_expr(&if_true.inner().cond);
|
||||||
|
cond_thunk.push_front(Inst::Comment("if compare".to_string()));
|
||||||
|
cond_thunk.push_back(Inst::Compare);
|
||||||
|
cond_thunk.push_back(Inst::JumpRelative(
|
||||||
|
// + 1 because otherwise we won't end up on the next instruction
|
||||||
|
isize::try_from(body_thunk.len()).unwrap() + 1,
|
||||||
|
JumpCondition::False,
|
||||||
|
));
|
||||||
|
|
||||||
|
// don't need to add this
|
||||||
|
//jump_forward += isize::try_from(body_thunk.len() + cond_thunk.len()).unwrap();
|
||||||
|
|
||||||
|
thunks.push_front(body_thunk);
|
||||||
|
thunks.push_front(cond_thunk);
|
||||||
|
thunks.push_back(Thunk::Block(
|
||||||
|
vec![Inst::Comment("end of if block".to_string())].into(),
|
||||||
|
));
|
||||||
|
|
||||||
|
Thunk::List(thunks)
|
||||||
|
}
|
||||||
Stmt::Def { .. } => todo!(),
|
Stmt::Def { .. } => todo!(),
|
||||||
Stmt::Import { .. } => todo!(),
|
Stmt::Import { .. } => todo!(),
|
||||||
}
|
}
|
||||||
@@ -62,16 +142,16 @@ impl Compiler {
|
|||||||
fn emit_expr(&mut self, expr: &SpExpr) -> Thunk {
|
fn emit_expr(&mut self, expr: &SpExpr) -> Thunk {
|
||||||
match expr.inner() {
|
match expr.inner() {
|
||||||
Expr::Call(expr, args) => {
|
Expr::Call(expr, args) => {
|
||||||
let mut thunk = Vec::with_capacity(args.len() + 1);
|
let mut thunk = VecDeque::with_capacity(args.len() + 1);
|
||||||
thunk.push(self.emit_expr(expr));
|
thunk.push_back(self.emit_expr(expr));
|
||||||
thunk.extend(args.iter().map(|arg| self.emit_expr(arg)));
|
thunk.extend(args.iter().map(|arg| self.emit_expr(arg)));
|
||||||
let mut thunk = Thunk::List(thunk);
|
let mut thunk = Thunk::List(thunk);
|
||||||
thunk.push(Inst::Call(args.len()));
|
thunk.push_back(Inst::Call(args.len()));
|
||||||
thunk
|
thunk
|
||||||
}
|
}
|
||||||
Expr::Get(expr, name) => {
|
Expr::Get(expr, name) => {
|
||||||
let mut thunk = self.emit_expr(expr);
|
let mut thunk = self.emit_expr(expr);
|
||||||
thunk.push(Inst::GetAttr(name.clone()));
|
thunk.push_back(Inst::GetAttr(name.clone()));
|
||||||
thunk
|
thunk
|
||||||
}
|
}
|
||||||
Expr::Atom(atom) => self.emit_atom(atom),
|
Expr::Atom(atom) => self.emit_atom(atom),
|
||||||
@@ -80,19 +160,19 @@ impl Compiler {
|
|||||||
|
|
||||||
fn emit_atom(&mut self, atom: &SpAtom) -> Thunk {
|
fn emit_atom(&mut self, atom: &SpAtom) -> Thunk {
|
||||||
match atom.inner() {
|
match atom.inner() {
|
||||||
Atom::Int(i) => Thunk::Block(vec![Inst::Push(ObjPtr::new(Int::new(*i)))]),
|
Atom::Int(i) => Thunk::Block(vec![Inst::Push(ObjPtr::new(Int::new(*i)))].into()),
|
||||||
Atom::Float(_) => todo!(), // Thunk::Block(vec![Inst::Push(Gc::new(Float::new(*f)))]),
|
Atom::Float(_) => todo!(), // Thunk::Block(vec![Inst::Push(Gc::new(Float::new(*f)))]),
|
||||||
Atom::Str(s) => Thunk::Block(vec![Inst::Push(ObjPtr::new(Str::new(s.clone())))]),
|
Atom::Str(s) => Thunk::Block(vec![Inst::Push(ObjPtr::new(Str::new(s.clone())))].into()),
|
||||||
Atom::Sym(_) => todo!(),
|
Atom::Sym(_) => todo!(),
|
||||||
Atom::Name(n) => {
|
Atom::Name(n) => {
|
||||||
// Look up the symbol first. It may already exist.
|
// Look up the symbol first. It may already exist.
|
||||||
// If it doesn't exist, create it. It may be created
|
// If it doesn't exist, create it. It may be created
|
||||||
// dynamically.
|
// Dynamically.
|
||||||
let name = self
|
let name = self
|
||||||
.scope()
|
.scope()
|
||||||
.lookup_scoped(n)
|
.lookup_scoped(n)
|
||||||
.unwrap_or_else(|| self.scope_mut().insert_local(n));
|
.unwrap_or_else(|| self.scope_mut().insert_local(n));
|
||||||
Thunk::Block(vec![Inst::Load(name)])
|
Thunk::Block(vec![Inst::Load(name)].into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,8 +194,22 @@ impl Compiler {
|
|||||||
// Get the names of the variables used in this expr
|
// Get the names of the variables used in this expr
|
||||||
self.gather_expr_names(expr.inner());
|
self.gather_expr_names(expr.inner());
|
||||||
}
|
}
|
||||||
Stmt::If { .. } => {
|
Stmt::If {
|
||||||
todo!()
|
if_true,
|
||||||
|
elseif,
|
||||||
|
else_body,
|
||||||
|
} => {
|
||||||
|
self.gather_expr_names(if_true.inner().cond.inner());
|
||||||
|
self.gather_names(if_true.inner().body.inner());
|
||||||
|
|
||||||
|
for cond_body in elseif {
|
||||||
|
self.gather_expr_names(cond_body.inner().cond.inner());
|
||||||
|
self.gather_names(cond_body.inner().body.inner());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(else_body) = else_body {
|
||||||
|
self.gather_names(else_body.inner());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Stmt::Import { .. } => {
|
Stmt::Import { .. } => {
|
||||||
todo!()
|
todo!()
|
||||||
|
|||||||
@@ -1,52 +1,62 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use crate::vm::inst::Inst;
|
use crate::vm::inst::Inst;
|
||||||
|
|
||||||
pub enum Thunk {
|
pub enum Thunk {
|
||||||
Block(Vec<Inst>),
|
Block(VecDeque<Inst>),
|
||||||
List(Vec<Thunk>),
|
List(VecDeque<Thunk>),
|
||||||
/*
|
|
||||||
IfElse {
|
|
||||||
cond: Box<Thunk>,
|
|
||||||
if_true: Box<Thunk>,
|
|
||||||
if_false: Box<Thunk>,
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Thunk {
|
impl Thunk {
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Thunk::Block(Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn flatten(self) -> Vec<Inst> {
|
pub fn flatten(self) -> Vec<Inst> {
|
||||||
use Thunk::*;
|
use Thunk::*;
|
||||||
match self {
|
match self {
|
||||||
Block(block) => block,
|
Block(block) => block.into(),
|
||||||
List(list) => list.into_iter().flat_map(|thunk| thunk.flatten()).collect(),
|
List(list) => list.into_iter().flat_map(|thunk| thunk.flatten()).collect(),
|
||||||
/*
|
|
||||||
IfElse { .. } => {
|
|
||||||
todo!("flatten if/else")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, inst: Inst) {
|
pub fn push_back(&mut self, inst: Inst) {
|
||||||
use Thunk::*;
|
use Thunk::*;
|
||||||
match self {
|
match self {
|
||||||
Block(block) => {
|
Block(block) => {
|
||||||
block.push(inst);
|
block.push_back(inst);
|
||||||
}
|
}
|
||||||
List(list) => {
|
List(list) => {
|
||||||
if let Some(Block(last)) = list.last_mut() {
|
if let Some(Block(last)) = list.back_mut() {
|
||||||
// if this is a block, then push the inst to the end of the
|
// if this is a block, then push the inst to the end of the
|
||||||
// block instead of creating a new element
|
// block instead of creating a new element
|
||||||
last.push(inst);
|
last.push_back(inst);
|
||||||
} else {
|
} else {
|
||||||
// otherwise, push a new list block
|
// otherwise, push a new list block
|
||||||
list.push(Block(vec![inst]));
|
list.push_back(Block(vec![inst].into()));
|
||||||
}
|
}
|
||||||
} /*
|
|
||||||
IfElse { .. } => {
|
|
||||||
// replace this with a List type, with *self as the first element
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
*/
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_front(&mut self, inst: Inst) {
|
||||||
|
use Thunk::*;
|
||||||
|
match self {
|
||||||
|
Block(block) => block.push_front(inst),
|
||||||
|
List(list) => {
|
||||||
|
if let Some(Block(first)) = list.front_mut() {
|
||||||
|
first.push_front(inst);
|
||||||
|
} else {
|
||||||
|
list.push_front(Block(vec![inst].into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Thunk::Block(block) => block.len(),
|
||||||
|
Thunk::List(list) => list.iter().map(Thunk::len).sum(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/main.rs
16
src/main.rs
@@ -20,6 +20,19 @@ struct Opt {
|
|||||||
|
|
||||||
type Result<T = (), E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
type Result<T = (), E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
fn disassemble(insts: &Vec<Inst>) {
|
||||||
|
// I could do some annoying casting shit, or I could just do string length
|
||||||
|
let max_digits = insts.len().to_string().len();
|
||||||
|
for (addr, inst) in insts.iter().enumerate() {
|
||||||
|
println!(
|
||||||
|
"{:0width$} {}",
|
||||||
|
addr,
|
||||||
|
inst.disassemble_string(),
|
||||||
|
width = max_digits
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result {
|
fn main() -> Result {
|
||||||
let opt = Opt::from_args();
|
let opt = Opt::from_args();
|
||||||
|
|
||||||
@@ -59,7 +72,8 @@ fn main() -> Result {
|
|||||||
exec_body.push(Inst::Push(ObjPtr::new(Int::new(0))));
|
exec_body.push(Inst::Push(ObjPtr::new(Int::new(0))));
|
||||||
exec_body.push(Inst::Return);
|
exec_body.push(Inst::Return);
|
||||||
|
|
||||||
//println!("{:#?}", exec_body);
|
//disassemble(&exec_body);
|
||||||
|
//return Ok(());
|
||||||
|
|
||||||
let locals: Vec<_> = scope.keys().copied().collect();
|
let locals: Vec<_> = scope.keys().copied().collect();
|
||||||
let main_fun: ObjPtr<_> = UserFun::new(None, vec![], locals, exec_body).into();
|
let main_fun: ObjPtr<_> = UserFun::new(None, vec![], locals, exec_body).into();
|
||||||
|
|||||||
66
src/obj/boolean.rs
Normal file
66
src/obj/boolean.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
use crate::obj::prelude::*;
|
||||||
|
use std::{any::Any, sync::LazyLock};
|
||||||
|
|
||||||
|
pub type BooleanValue = bool;
|
||||||
|
|
||||||
|
pub static BOOL_TRUE_INST: LazyLock<ObjPtr> = LazyLock::new(|| ObjPtr::new(Boolean::new(true)));
|
||||||
|
pub static BOOL_FALSE_INST: LazyLock<ObjPtr> = LazyLock::new(|| ObjPtr::new(Boolean::new(false)));
|
||||||
|
pub static BOOL_INSTS: &[&LazyLock<ObjPtr>] = &[&BOOL_FALSE_INST, &BOOL_TRUE_INST];
|
||||||
|
|
||||||
|
static BOOL_ATTRS: LazyLock<AttrsPtr> = LazyLock::new(|| {
|
||||||
|
attrs_ptr! {
|
||||||
|
//
|
||||||
|
"__str__" => builtin_fun_ptr!("__str__", |_vm, args, _state| {
|
||||||
|
if args.len() != 1 {
|
||||||
|
todo!("Throw exception: arity error");
|
||||||
|
}
|
||||||
|
let arg_any = &args[0].as_any();
|
||||||
|
let obj = arg_any.downcast_ref::<Boolean>().expect("Boolean.__str__ did not receive Boolean?");
|
||||||
|
|
||||||
|
BuiltinFunResult::Return(Some(ObjPtr::new(Str::new(obj.value().to_string()))))
|
||||||
|
}),
|
||||||
|
"__bool__" => builtin_fun_ptr!("__bool__", |_vm, args, _state| {
|
||||||
|
let arg_any = &args[0].as_any();
|
||||||
|
let obj = arg_any.downcast_ref::<Boolean>().expect("Boolean.__str__ did not receive Boolean?");
|
||||||
|
|
||||||
|
BuiltinFunResult::Return(Some(ObjPtr::new(Boolean::new(obj.is_truthy()))))
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Boolean {
|
||||||
|
attrs: AttrsPtr,
|
||||||
|
value: BooleanValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Boolean {
|
||||||
|
fn new(value: BooleanValue) -> Self {
|
||||||
|
let attrs = AttrsPtr::clone(&BOOL_ATTRS);
|
||||||
|
Boolean { attrs, value }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> BooleanValue {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Obj for Boolean {
|
||||||
|
fn attrs(&self) -> AttrsPtr {
|
||||||
|
AttrsPtr::clone(&self.attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &(dyn Any + 'static) {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_truthy(&self) -> bool {
|
||||||
|
self.value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for Boolean {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
format!("{}", self.value())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -134,4 +134,8 @@ impl Obj for BuiltinFun {
|
|||||||
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
|
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_truthy(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
//! Builtin functions and objects.
|
//! Builtin functions and objects.
|
||||||
use crate::obj::prelude::*;
|
use crate::obj::prelude::*;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Builtin functions
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
pub const BUILTIN_FUNS: &[(&str, BuiltinFunPtr)] = &[
|
pub const BUILTIN_FUNS: &[(&str, BuiltinFunPtr)] = &[
|
||||||
//
|
//
|
||||||
("println", println),
|
("println", println),
|
||||||
("print", print),
|
("print", print),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Shared functionality between builtins
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
fn println(vm: BuiltinFunVm, args: Vec<ObjPtr>, state: BuiltinFunState) -> BuiltinFunResult {
|
fn println(vm: BuiltinFunVm, args: Vec<ObjPtr>, state: BuiltinFunState) -> BuiltinFunResult {
|
||||||
do_print(vm, args, state, "\n")
|
do_print(vm, args, state, "\n")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ static INT_ATTRS: LazyLock<AttrsPtr> = LazyLock::new(|| {
|
|||||||
|
|
||||||
BuiltinFunResult::Return(Some(ObjPtr::new(Str::new(obj.value().to_string()))))
|
BuiltinFunResult::Return(Some(ObjPtr::new(Str::new(obj.value().to_string()))))
|
||||||
}),
|
}),
|
||||||
|
"__bool__" => builtin_fun_ptr!("__bool__", |_vm, args, _state| {
|
||||||
|
let arg_any = &args[0].as_any();
|
||||||
|
let obj = arg_any.downcast_ref::<Int>().expect("Int.__bool__ did not receive Int?");
|
||||||
|
let result = ObjPtr::clone(&BOOL_INSTS[obj.is_truthy() as usize]);
|
||||||
|
BuiltinFunResult::Return(Some(result))
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -44,6 +50,10 @@ impl Obj for Int {
|
|||||||
fn as_any(&self) -> &(dyn Any + 'static) {
|
fn as_any(&self) -> &(dyn Any + 'static) {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_truthy(&self) -> bool {
|
||||||
|
self.value() != 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Int {
|
impl ToString for Int {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pub mod boolean;
|
||||||
pub mod builtin_fun;
|
pub mod builtin_fun;
|
||||||
pub mod builtins;
|
pub mod builtins;
|
||||||
pub mod int;
|
pub mod int;
|
||||||
@@ -7,7 +8,7 @@ pub mod user_fun;
|
|||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
// Module types
|
// Module types
|
||||||
pub use super::{builtin_fun::*, int::*, none::*, str::*, user_fun::*};
|
pub use super::{boolean::*, builtin_fun::*, int::*, none::*, str::*, user_fun::*};
|
||||||
// Local types
|
// Local types
|
||||||
pub use super::{Attrs, AttrsPtr, Obj, ObjPtr};
|
pub use super::{Attrs, AttrsPtr, Obj, ObjPtr};
|
||||||
// Macros
|
// Macros
|
||||||
@@ -70,4 +71,10 @@ pub trait Obj: Debug + Sync + Send {
|
|||||||
let mut attrs = attrs_ptr.write().unwrap();
|
let mut attrs = attrs_ptr.write().unwrap();
|
||||||
attrs.insert(key, value)
|
attrs.insert(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_truthy(&self) -> bool;
|
||||||
|
|
||||||
|
fn is_falsey(&self) -> bool {
|
||||||
|
!self.is_truthy()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,13 @@ static NONE_ATTRS: LazyLock<AttrsPtr> = LazyLock::new(|| {
|
|||||||
}
|
}
|
||||||
BuiltinFunResult::Return(Some(ObjPtr::clone(&NONE_STR)))
|
BuiltinFunResult::Return(Some(ObjPtr::clone(&NONE_STR)))
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
"__bool__" => builtin_fun_ptr!("__bool__", |_vm, args, _state| {
|
||||||
|
let arg_any = &args[0].as_any();
|
||||||
|
let obj = arg_any.downcast_ref::<Int>().expect("None.__bool__ did not receive Int?");
|
||||||
|
let result = ObjPtr::clone(&BOOL_INSTS[obj.is_truthy() as usize]);
|
||||||
|
BuiltinFunResult::Return(Some(result))
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -36,4 +43,8 @@ impl Obj for None {
|
|||||||
fn as_any(&self) -> &(dyn Any + 'static) {
|
fn as_any(&self) -> &(dyn Any + 'static) {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_truthy(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ static STR_ATTRS: LazyLock<AttrsPtr> = LazyLock::new(|| {
|
|||||||
}
|
}
|
||||||
BuiltinFunResult::Return(Some(args.pop().unwrap()))
|
BuiltinFunResult::Return(Some(args.pop().unwrap()))
|
||||||
}),
|
}),
|
||||||
|
"__bool__" => builtin_fun_ptr!("__bool__", |_vm, args, _state| {
|
||||||
|
let arg_any = &args[0].as_any();
|
||||||
|
let obj = arg_any.downcast_ref::<Int>().expect("Str.__bool__ did not receive Int?");
|
||||||
|
let result = ObjPtr::clone(&BOOL_INSTS[obj.is_truthy() as usize]);
|
||||||
|
BuiltinFunResult::Return(Some(result))
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -44,6 +50,10 @@ impl Obj for Str {
|
|||||||
fn as_any(&self) -> &(dyn Any + 'static) {
|
fn as_any(&self) -> &(dyn Any + 'static) {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_truthy(&self) -> bool {
|
||||||
|
self.value().len() > 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Str {
|
impl ToString for Str {
|
||||||
|
|||||||
@@ -47,4 +47,8 @@ impl Obj for UserFun {
|
|||||||
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
|
fn as_any(&self) -> &(dyn std::any::Any + 'static) {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_truthy(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
use crate::syn::Spanned;
|
use crate::syn::Spanned;
|
||||||
|
|
||||||
|
pub type SpBody = Spanned<Body>;
|
||||||
|
pub type Body = Vec<SpStmt>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
Assign(SpAssignLhs, SpExpr),
|
Assign(SpAssignLhs, SpExpr),
|
||||||
Expr(SpExpr),
|
Expr(SpExpr),
|
||||||
If {
|
If {
|
||||||
if_true: SpCondExpr,
|
if_true: SpCondBody,
|
||||||
elseif: Vec<SpCondExpr>,
|
elseif: Vec<SpCondBody>,
|
||||||
else_expr: Option<SpExpr>,
|
else_body: Option<SpBody>,
|
||||||
},
|
},
|
||||||
Def {
|
Def {
|
||||||
name: String,
|
name: String,
|
||||||
@@ -38,12 +41,12 @@ pub enum AssignLhs {
|
|||||||
pub type SpStmt = Spanned<Stmt>;
|
pub type SpStmt = Spanned<Stmt>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CondExpr {
|
pub struct CondBody {
|
||||||
pub cond: SpExpr,
|
pub cond: SpExpr,
|
||||||
pub expr: SpExpr,
|
pub body: SpBody,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type SpCondExpr = Spanned<CondExpr>;
|
pub type SpCondBody = Spanned<CondBody>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Param {
|
pub struct Param {
|
||||||
|
|||||||
@@ -9,7 +9,21 @@ Spanned<T>: Spanned<T> = {
|
|||||||
<start:@L> <inner:T> <end:@R> => Spanned::new(Span::new(start, end), inner),
|
<start:@L> <inner:T> <end:@R> => Spanned::new(Span::new(start, end), inner),
|
||||||
};
|
};
|
||||||
|
|
||||||
pub File: Vec<SpStmt> = {
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// File, block, body
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
pub File: Body = {
|
||||||
|
<Body> => <>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub SpBlock = Spanned<Block>;
|
||||||
|
|
||||||
|
pub Block: Body = {
|
||||||
|
"{" <Body> "}" => <>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub Body: Body = {
|
||||||
<mut head:(<SpStmt> ";")*> <tail:SpStmt?> => {
|
<mut head:(<SpStmt> ";")*> <tail:SpStmt?> => {
|
||||||
if let Some(tail) = tail {
|
if let Some(tail) = tail {
|
||||||
head.push(tail);
|
head.push(tail);
|
||||||
@@ -18,11 +32,16 @@ pub File: Vec<SpStmt> = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Statements, assignment
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
pub SpStmt = Spanned<Stmt>;
|
pub SpStmt = Spanned<Stmt>;
|
||||||
|
|
||||||
pub Stmt: Stmt = {
|
pub Stmt: Stmt = {
|
||||||
<SpExpr> => Stmt::Expr(<>),
|
<SpExpr> => Stmt::Expr(<>),
|
||||||
<lhs:SpAssignLhs> "=" <expr:SpExpr> => Stmt::Assign(lhs, expr),
|
<lhs:SpAssignLhs> "=" <expr:SpExpr> => Stmt::Assign(lhs, expr),
|
||||||
|
<IfStmt> => <>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub SpAssignLhs = Spanned<AssignLhs>;
|
pub SpAssignLhs = Spanned<AssignLhs>;
|
||||||
@@ -31,6 +50,46 @@ pub AssignLhs: AssignLhs = {
|
|||||||
<Name> => { AssignLhs::Name(<>) }
|
<Name> => { AssignLhs::Name(<>) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Branch, condition blocks
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
pub IfStmt: Stmt = {
|
||||||
|
<if_start:@L>
|
||||||
|
"if" <if_condition:SpExpr> <if_block:SpBlock>
|
||||||
|
<if_end:@R>
|
||||||
|
<elif_start:@L>
|
||||||
|
<elif_block:(<@L> "else" "if" <SpExpr> <SpBlock> <@R>)*>
|
||||||
|
<elif_end:@R>
|
||||||
|
<else_start:@L>
|
||||||
|
<else_block:("else" <SpBlock>)?>
|
||||||
|
<else_end:@R> => {
|
||||||
|
// Make if statement cond body
|
||||||
|
let if_true = CondBody {
|
||||||
|
cond: if_condition,
|
||||||
|
body: if_block,
|
||||||
|
};
|
||||||
|
// Make elif statement cond body
|
||||||
|
let mut elseif = Vec::with_capacity(elif_block.len());
|
||||||
|
for (start, expr, block, end) in elif_block {
|
||||||
|
let elseif_cond_body = CondBody {
|
||||||
|
cond: expr,
|
||||||
|
body: block
|
||||||
|
};
|
||||||
|
elseif.push(Spanned::new(Span::new(start, end), elseif_cond_body));
|
||||||
|
}
|
||||||
|
Stmt::If {
|
||||||
|
if_true: Spanned::new(Span::new(if_start, if_end), if_true),
|
||||||
|
elseif,
|
||||||
|
else_body: else_block,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Expressions
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
pub SpExpr = Spanned<Expr>;
|
pub SpExpr = Spanned<Expr>;
|
||||||
|
|
||||||
pub Expr: Expr = {
|
pub Expr: Expr = {
|
||||||
@@ -48,6 +107,9 @@ pub Expr: Expr = {
|
|||||||
"(" <expr:Expr> ")" => expr,
|
"(" <expr:Expr> ")" => expr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Atoms
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
pub SpAtom = Spanned<Atom>;
|
pub SpAtom = Spanned<Atom>;
|
||||||
|
|
||||||
|
|||||||
25252
src/syn/parser.rs
25252
src/syn/parser.rs
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,13 @@
|
|||||||
use crate::obj::ObjPtr;
|
use crate::obj::ObjPtr;
|
||||||
use crate::vm::name::Name;
|
use crate::vm::name::Name;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum JumpCondition {
|
||||||
|
False,
|
||||||
|
True,
|
||||||
|
Always,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Inst {
|
pub enum Inst {
|
||||||
/// Pushes an object to the stack.
|
/// Pushes an object to the stack.
|
||||||
@@ -29,4 +36,37 @@ pub enum Inst {
|
|||||||
/// resize the stack to its last size (pre-call), and push the return value
|
/// resize the stack to its last size (pre-call), and push the return value
|
||||||
/// back to the top of the stack.
|
/// back to the top of the stack.
|
||||||
Return,
|
Return,
|
||||||
|
|
||||||
|
/// Adds the given value to the current program counter (positive or
|
||||||
|
/// negative) when the given condition holds..
|
||||||
|
///
|
||||||
|
/// `Inst::JumpRelative(1)` functions as a no-op.
|
||||||
|
/// `Inst::JumpRelative(0)` functions as a "hang"
|
||||||
|
/// `Inst::JumpRelative(-1)` jumps to the previous instruction.
|
||||||
|
JumpRelative(isize, JumpCondition),
|
||||||
|
|
||||||
|
/// Pops the top value off of the stack and sets the condition flag to
|
||||||
|
/// "true" or "false".
|
||||||
|
Compare,
|
||||||
|
|
||||||
|
Comment(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inst {
|
||||||
|
pub fn disassemble_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Inst::Push(ptr) => format!("push <{:#x}>", (ptr as *const _ as usize)),
|
||||||
|
Inst::Load(name) => format!("load {name:?}"),
|
||||||
|
Inst::Store(name) => format!("store {name:?}"),
|
||||||
|
Inst::GetAttr(attr) => format!("getattr {attr}"),
|
||||||
|
Inst::Pop => format!("pop"),
|
||||||
|
Inst::Call(argc) => format!("call {argc}"),
|
||||||
|
Inst::Return => format!("return"),
|
||||||
|
Inst::JumpRelative(distance, condition) => {
|
||||||
|
format!("jump {:+} (when condition is {:?})", distance, condition)
|
||||||
|
}
|
||||||
|
Inst::Compare => format!("compare"),
|
||||||
|
Inst::Comment(comment) => format!("# {comment}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ pub mod name;
|
|||||||
|
|
||||||
use crate::obj::prelude::*;
|
use crate::obj::prelude::*;
|
||||||
use crate::vm::frame::{BuiltinFrame, UserFrame};
|
use crate::vm::frame::{BuiltinFrame, UserFrame};
|
||||||
use crate::vm::{frame::Frame, inst::Inst, name::Name};
|
use crate::vm::{frame::Frame, inst::*, name::Name};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum State {
|
pub enum State {
|
||||||
@@ -18,6 +18,7 @@ pub struct Vm {
|
|||||||
frames: Vec<Frame>,
|
frames: Vec<Frame>,
|
||||||
state: State,
|
state: State,
|
||||||
stack: Vec<ObjPtr>,
|
stack: Vec<ObjPtr>,
|
||||||
|
condition: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vm {
|
impl Vm {
|
||||||
@@ -26,6 +27,7 @@ impl Vm {
|
|||||||
frames: Default::default(),
|
frames: Default::default(),
|
||||||
state: State::Stopped,
|
state: State::Stopped,
|
||||||
stack: Default::default(),
|
stack: Default::default(),
|
||||||
|
condition: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +75,14 @@ impl Vm {
|
|||||||
self.stack_mut().pop()
|
self.stack_mut().pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn condition(&self) -> bool {
|
||||||
|
self.condition
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_condition(&mut self, condition: bool) {
|
||||||
|
self.condition = condition;
|
||||||
|
}
|
||||||
|
|
||||||
/// Loads a given name in the currently executing context.
|
/// Loads a given name in the currently executing context.
|
||||||
pub fn load(&self, name: Name) -> Option<ObjPtr> {
|
pub fn load(&self, name: Name) -> Option<ObjPtr> {
|
||||||
// Search up the stack frame for local variable with the given name
|
// Search up the stack frame for local variable with the given name
|
||||||
@@ -122,6 +132,8 @@ impl Vm {
|
|||||||
///
|
///
|
||||||
/// Returns the address of the next instruction to load.
|
/// Returns the address of the next instruction to load.
|
||||||
fn exec_inst(&mut self) {
|
fn exec_inst(&mut self) {
|
||||||
|
let self_condition = self.condition();
|
||||||
|
|
||||||
// don't like this, but this is the consequence of not using a pipeline I think
|
// don't like this, but this is the consequence of not using a pipeline I think
|
||||||
let frame = match self.active_frame_mut() {
|
let frame = match self.active_frame_mut() {
|
||||||
Some(Frame::User(frame)) => frame,
|
Some(Frame::User(frame)) => frame,
|
||||||
@@ -129,7 +141,7 @@ impl Vm {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let inst = frame.get_inst();
|
let inst = frame.get_inst();
|
||||||
let pc = frame.pc() + 1;
|
let mut pc = frame.pc() + 1;
|
||||||
let mut next_frame: Option<Frame> = None;
|
let mut next_frame: Option<Frame> = None;
|
||||||
let mut pop_frame = false;
|
let mut pop_frame = false;
|
||||||
|
|
||||||
@@ -151,11 +163,34 @@ impl Vm {
|
|||||||
let value = self.pop_stack().expect("Stack underflow");
|
let value = self.pop_stack().expect("Stack underflow");
|
||||||
self.store(name, Some(value));
|
self.store(name, Some(value));
|
||||||
}
|
}
|
||||||
Inst::GetAttr(_attr) => todo!(),
|
Inst::GetAttr(attr) => {
|
||||||
Inst::Pop => {
|
// XXX
|
||||||
self
|
// there's an annoying thing with this. We can't use
|
||||||
|
// self.pop_stack() and then use "attr" afterwards because they
|
||||||
|
// belong to the same owner (self).
|
||||||
|
// ALSO:
|
||||||
|
// We can't just do peek_stack() either, because since `attr`
|
||||||
|
// above was borrowed while `self` is mutable. If you want to
|
||||||
|
// borrow `self` immutably, you have to drop the reference above
|
||||||
|
// as well.
|
||||||
|
// So either we have to do some unsafe stuff (not a bad option
|
||||||
|
// here, since nothing is going to move) or clone(). Maybe a
|
||||||
|
// `get_attr()` function is in order, I don't know.
|
||||||
|
// For now we're cloning.
|
||||||
|
let attr = attr.clone();
|
||||||
|
let obj = self
|
||||||
.pop_stack()
|
.pop_stack()
|
||||||
.expect("Inst::Pop executed, but there was no value on top of the stack. This is most likely a bug.");
|
.expect("Inst::GetAttr executed, but there was no value on top of the stack.");
|
||||||
|
let value = obj.get(&attr);
|
||||||
|
if let Some(value) = value {
|
||||||
|
self.push_stack(value);
|
||||||
|
} else {
|
||||||
|
todo!("Throw exception: Could not get attr {attr:?} on object {obj:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Inst::Pop => {
|
||||||
|
self.pop_stack()
|
||||||
|
.expect("Inst::Pop executed, but there was no value on top of the stack.");
|
||||||
}
|
}
|
||||||
Inst::Call(argc) => {
|
Inst::Call(argc) => {
|
||||||
let argc = *argc;
|
let argc = *argc;
|
||||||
@@ -182,6 +217,29 @@ impl Vm {
|
|||||||
self.push_stack(return_value);
|
self.push_stack(return_value);
|
||||||
pop_frame = true;
|
pop_frame = true;
|
||||||
}
|
}
|
||||||
|
Inst::JumpRelative(amount, condition) => {
|
||||||
|
match (self_condition, condition) {
|
||||||
|
(true, JumpCondition::True)
|
||||||
|
| (false, JumpCondition::False)
|
||||||
|
| (_, JumpCondition::Always) => {
|
||||||
|
// jump taken
|
||||||
|
pc = (pc as isize + amount - 1) as usize;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// jump not taken, do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Inst::Compare => {
|
||||||
|
let top = self.pop_stack().expect(
|
||||||
|
"Inst::Compare expected a value on top of the stack, but it was empty.",
|
||||||
|
);
|
||||||
|
self.set_condition(top.is_truthy());
|
||||||
|
}
|
||||||
|
Inst::Comment(comment) => {
|
||||||
|
/* no-op */
|
||||||
|
//println!("COMMENT: {comment}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same as the top - this time update the program counter
|
// Same as the top - this time update the program counter
|
||||||
|
|||||||
Reference in New Issue
Block a user