@@ -1,4 +1,4 @@
|
||||
use not_python::{syn::ast, compile::Compile, vm::Vm};
|
||||
use not_python::{compile::Compile, syn::ast, vm::Vm};
|
||||
|
||||
use std::{fs, path::PathBuf};
|
||||
use structopt::StructOpt;
|
||||
@@ -18,7 +18,6 @@ struct Options {
|
||||
#[structopt(short = "d", long)]
|
||||
disassemble: bool,
|
||||
*/
|
||||
|
||||
#[structopt(long, default_value = "run")]
|
||||
action: String,
|
||||
}
|
||||
@@ -28,8 +27,8 @@ fn main() -> Result<()> {
|
||||
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)
|
||||
"run" | "parse" | "dump" => {}
|
||||
_ => eprintln!("WARNING: unknown action {}", action),
|
||||
}
|
||||
|
||||
let ast = match ast::parse(text.as_str()) {
|
||||
@@ -40,7 +39,8 @@ fn main() -> Result<()> {
|
||||
}
|
||||
return Err("errors reported, exiting".into());
|
||||
}
|
||||
}.unwrap();
|
||||
}
|
||||
.unwrap();
|
||||
if action == "parse" {
|
||||
println!("{:#?}", ast);
|
||||
return Ok(());
|
||||
|
||||
@@ -10,4 +10,3 @@ pub enum Error {
|
||||
}
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
use crate::{compile::Compile, obj::prelude::*, syn::{ast::*, visit::*}};
|
||||
use crate::{
|
||||
compile::Compile,
|
||||
obj::prelude::*,
|
||||
syn::{ast::*, visit::*},
|
||||
};
|
||||
|
||||
/// Collects local names from the given tree.
|
||||
///
|
||||
@@ -30,10 +34,18 @@ impl<'c> CollectLocals<'c> {
|
||||
impl Visit for CollectLocals<'_> {
|
||||
type Out = ();
|
||||
|
||||
fn visit_body(&mut self, body: &Body) -> Self::Out { DefaultAccept::default_accept(body, self); }
|
||||
fn visit_stmt(&mut self, stmt: &Stmt) -> Self::Out { DefaultAccept::default_accept(stmt, self); }
|
||||
fn visit_assign_stmt(&mut self, assign: &AssignStmt) -> Self::Out { DefaultAccept::default_accept(assign, self); }
|
||||
fn visit_return_stmt(&mut self, ret: &ReturnStmt) -> Self::Out { DefaultAccept::default_accept(ret, self); }
|
||||
fn visit_body(&mut self, body: &Body) -> Self::Out {
|
||||
DefaultAccept::default_accept(body, self);
|
||||
}
|
||||
fn visit_stmt(&mut self, stmt: &Stmt) -> Self::Out {
|
||||
DefaultAccept::default_accept(stmt, self);
|
||||
}
|
||||
fn visit_assign_stmt(&mut self, assign: &AssignStmt) -> Self::Out {
|
||||
DefaultAccept::default_accept(assign, self);
|
||||
}
|
||||
fn visit_return_stmt(&mut self, ret: &ReturnStmt) -> Self::Out {
|
||||
DefaultAccept::default_accept(ret, self);
|
||||
}
|
||||
fn visit_lhs_expr(&mut self, lhs_expr: &LhsExpr) -> Self::Out {
|
||||
match lhs_expr {
|
||||
LhsExpr::Name(name) => {
|
||||
@@ -43,14 +55,28 @@ impl Visit for CollectLocals<'_> {
|
||||
_ => { /* no op */ }
|
||||
}
|
||||
}
|
||||
fn visit_expr(&mut self, expr: &Expr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
|
||||
fn visit_bin_expr(&mut self, expr: &BinExpr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
|
||||
fn visit_un_expr(&mut self, expr: &UnExpr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
|
||||
fn visit_call_expr(&mut self, expr: &CallExpr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
|
||||
fn visit_index_expr(&mut self, expr: &IndexExpr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
|
||||
fn visit_access_expr(&mut self, expr: &AccessExpr) -> Self::Out { DefaultAccept::default_accept(expr, self); }
|
||||
fn visit_expr(&mut self, expr: &Expr) -> Self::Out {
|
||||
DefaultAccept::default_accept(expr, self);
|
||||
}
|
||||
fn visit_bin_expr(&mut self, expr: &BinExpr) -> Self::Out {
|
||||
DefaultAccept::default_accept(expr, self);
|
||||
}
|
||||
fn visit_un_expr(&mut self, expr: &UnExpr) -> Self::Out {
|
||||
DefaultAccept::default_accept(expr, self);
|
||||
}
|
||||
fn visit_call_expr(&mut self, expr: &CallExpr) -> Self::Out {
|
||||
DefaultAccept::default_accept(expr, self);
|
||||
}
|
||||
fn visit_index_expr(&mut self, expr: &IndexExpr) -> Self::Out {
|
||||
DefaultAccept::default_accept(expr, self);
|
||||
}
|
||||
fn visit_access_expr(&mut self, expr: &AccessExpr) -> Self::Out {
|
||||
DefaultAccept::default_accept(expr, self);
|
||||
}
|
||||
fn visit_fun_expr(&mut self, _expr: &FunExpr) -> Self::Out {
|
||||
// Do not collect names for function expressions, since they have their own scope.
|
||||
}
|
||||
fn visit_atom(&mut self, atom: &Atom) -> Self::Out { DefaultAccept::default_accept(atom, self); }
|
||||
fn visit_atom(&mut self, atom: &Atom) -> Self::Out {
|
||||
DefaultAccept::default_accept(atom, self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ mod locals;
|
||||
mod scope;
|
||||
pub mod thunk;
|
||||
|
||||
use crate::{syn::ast::Body, obj::prelude::*, vm::consts::*};
|
||||
use crate::{obj::prelude::*, syn::ast::Body, vm::consts::*};
|
||||
use scope::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -29,20 +29,28 @@ impl Compile {
|
||||
.to_vec();
|
||||
// XXX TODO(compile)
|
||||
// remove this when we get returns implemented
|
||||
main.push(crate::vm::inst::Inst::PushSym(crate::obj::reserved::NIL_NAME.sym));
|
||||
main.push(crate::vm::inst::Inst::PushSym(
|
||||
crate::obj::reserved::NIL_NAME.sym,
|
||||
));
|
||||
main.push(crate::vm::inst::Inst::Return);
|
||||
let globals_syms: std::collections::BTreeMap<_, _> = self.pop_scope_layer().unwrap()
|
||||
let globals_syms: std::collections::BTreeMap<_, _> = self
|
||||
.pop_scope_layer()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|(sym, name)| (name, sym))
|
||||
.collect();
|
||||
let globals = globals_syms.into_iter()
|
||||
let globals = globals_syms
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, (name, sym))| {
|
||||
assert_eq!(index, name.index());
|
||||
sym
|
||||
})
|
||||
.collect();
|
||||
Ok((self.const_data.const_pool, UserFun::new_obj(main, globals, 0)))
|
||||
Ok((
|
||||
self.const_data.const_pool,
|
||||
UserFun::new_obj(main, globals, 0),
|
||||
))
|
||||
}
|
||||
|
||||
/// Gets the constant data that is interned in this compile session.
|
||||
|
||||
@@ -11,9 +11,7 @@ pub struct Scope {
|
||||
|
||||
impl Scope {
|
||||
pub fn lookup_local(&self, sym: Sym) -> Option<Name> {
|
||||
self.locals()
|
||||
.and_then(|locals| locals.get(&sym))
|
||||
.copied()
|
||||
self.locals().and_then(|locals| locals.get(&sym)).copied()
|
||||
}
|
||||
|
||||
pub fn lookup_global(&self, sym: Sym) -> Option<Name> {
|
||||
@@ -23,8 +21,7 @@ impl Scope {
|
||||
}
|
||||
|
||||
pub fn create_local(&mut self, sym: Sym) -> Name {
|
||||
let locals = self.locals_mut()
|
||||
.expect("locals");
|
||||
let locals = self.locals_mut().expect("locals");
|
||||
if let Some(local) = locals.get(&sym).copied() {
|
||||
local
|
||||
} else {
|
||||
@@ -35,8 +32,7 @@ impl Scope {
|
||||
}
|
||||
|
||||
pub fn create_global(&mut self, sym: Sym) -> Name {
|
||||
let globals = self.globals_mut()
|
||||
.expect("globals");
|
||||
let globals = self.globals_mut().expect("globals");
|
||||
if let Some(global) = globals.get(&sym).copied() {
|
||||
global
|
||||
} else {
|
||||
|
||||
@@ -106,8 +106,7 @@ impl Thunk {
|
||||
}
|
||||
|
||||
pub fn flatten(self) -> BasicBlockList {
|
||||
Flatten::default()
|
||||
.flatten(self)
|
||||
Flatten::default().flatten(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,10 +157,13 @@ impl Flatten {
|
||||
match thunk {
|
||||
Thunk::Body(thunk) => {
|
||||
let this_block = self.this_block();
|
||||
let prev = self.blocks.insert(this_block, BasicBlock::Block {
|
||||
exit: next_block,
|
||||
block: thunk,
|
||||
});
|
||||
let prev = self.blocks.insert(
|
||||
this_block,
|
||||
BasicBlock::Block {
|
||||
exit: next_block,
|
||||
block: thunk,
|
||||
},
|
||||
);
|
||||
assert!(prev.is_none());
|
||||
}
|
||||
Thunk::List(thunks) => {
|
||||
@@ -172,14 +174,20 @@ impl Flatten {
|
||||
}
|
||||
assert_eq!(next_block, self.this_block());
|
||||
}
|
||||
Thunk::Branch { thunk_true, thunk_false, } => {
|
||||
Thunk::Branch {
|
||||
thunk_true,
|
||||
thunk_false,
|
||||
} => {
|
||||
let branch_block = self.this_block();
|
||||
let block_true = self.this_block() + 1;
|
||||
let block_false = block_true + thunk_true.basic_block_count();
|
||||
self.blocks.insert(branch_block, BasicBlock::Branch {
|
||||
block_true,
|
||||
block_false,
|
||||
});
|
||||
self.blocks.insert(
|
||||
branch_block,
|
||||
BasicBlock::Branch {
|
||||
block_true,
|
||||
block_false,
|
||||
},
|
||||
);
|
||||
self.flatten_next(next_block, *thunk_true);
|
||||
self.flatten_next(next_block, *thunk_false);
|
||||
assert_eq!(self.this_block(), next_block);
|
||||
@@ -278,7 +286,10 @@ impl Visit for CompileBody<'_> {
|
||||
if let Some(local) = self.compile.lookup_local(sym) {
|
||||
thunk = Inst::PopLocal(Some(local)).into();
|
||||
} else {
|
||||
let global = self.compile.lookup_global(sym).expect("name expected to exist someplace(?)");
|
||||
let global = self
|
||||
.compile
|
||||
.lookup_global(sym)
|
||||
.expect("name expected to exist someplace(?)");
|
||||
thunk = Inst::PopGlobal(Some(global)).into();
|
||||
}
|
||||
}
|
||||
@@ -368,7 +379,7 @@ impl Visit for CompileBody<'_> {
|
||||
// - push const
|
||||
// (functions are unique const values so a new function will be created for every literal
|
||||
// function defined in code)
|
||||
|
||||
|
||||
// This is pretty much the only place where a new scope layer gets pushed beyond the start
|
||||
// of the program
|
||||
self.compile.push_scope_layer();
|
||||
@@ -378,9 +389,7 @@ impl Visit for CompileBody<'_> {
|
||||
}
|
||||
|
||||
// Compile function body
|
||||
let mut code = self.visit_body(&expr.body)?
|
||||
.flatten()
|
||||
.to_vec();
|
||||
let mut code = self.visit_body(&expr.body)?.flatten().to_vec();
|
||||
|
||||
// If the last instruction is not a return, or if there are no instructions, then return
|
||||
// :nil value.
|
||||
@@ -390,7 +399,9 @@ impl Visit for CompileBody<'_> {
|
||||
}
|
||||
|
||||
// remap (Sym -> Name) to be (Name -> Sym) and make sure it's all in order.
|
||||
let scope_locals: BTreeMap<_, _> = self.compile.pop_scope_layer()
|
||||
let scope_locals: BTreeMap<_, _> = self
|
||||
.compile
|
||||
.pop_scope_layer()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|(sym, name)| (name, sym))
|
||||
@@ -399,7 +410,8 @@ impl Visit for CompileBody<'_> {
|
||||
// this should be in numeric order since:
|
||||
// 1. locals are created exactly once or looked up
|
||||
// 2. scope_locals is a btreemap, keyed by names, which are in order from 0..N
|
||||
let locals: FunLocals = scope_locals.into_iter()
|
||||
let locals: FunLocals = scope_locals
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, (name, sym))| {
|
||||
assert_eq!(index, name.index());
|
||||
@@ -407,7 +419,9 @@ impl Visit for CompileBody<'_> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let (hdl, _fun) = self.compile.push_const(UserFun::new_obj(code, locals, expr.params.len()));
|
||||
let (hdl, _fun) =
|
||||
self.compile
|
||||
.push_const(UserFun::new_obj(code, locals, expr.params.len()));
|
||||
|
||||
// TODO(compile) : determine return value at the end of the body (preferably at parse-time)
|
||||
|
||||
@@ -427,7 +441,10 @@ impl Visit for CompileBody<'_> {
|
||||
// This checks to make sure that it's both a local variable and that there's more
|
||||
// than one scope layer.
|
||||
let sym = global_sym(ident.to_string());
|
||||
if let (true, Some(local)) = (self.compile.scope().layers_len() > 1, self.compile.lookup_local(sym)) {
|
||||
if let (true, Some(local)) = (
|
||||
self.compile.scope().layers_len() > 1,
|
||||
self.compile.lookup_local(sym),
|
||||
) {
|
||||
// get local
|
||||
Inst::LoadLocal(local).into()
|
||||
} else {
|
||||
@@ -466,25 +483,20 @@ fn test_flatten_thunk() {
|
||||
let init_body = vec![
|
||||
Inst::PushSym(Sym::new(0)),
|
||||
Inst::PushSym(Sym::new(1)),
|
||||
Inst::Call(1)
|
||||
Inst::Call(1),
|
||||
];
|
||||
let true_body = vec![Inst::PushSym(Sym::new(2))];
|
||||
let false_body = vec![Inst::PushSym(Sym::new(3))];
|
||||
let end_body = vec![
|
||||
Inst::PushSym(Sym::new(1)),
|
||||
Inst::Call(1)
|
||||
];
|
||||
let end_body = vec![Inst::PushSym(Sym::new(1)), Inst::Call(1)];
|
||||
|
||||
let thunk = Thunk::List(vec![
|
||||
// do something before
|
||||
Thunk::Body(init_body.clone()),
|
||||
|
||||
// branch
|
||||
Thunk::Branch {
|
||||
thunk_true: Thunk::Body(true_body.clone()).into(),
|
||||
thunk_false: Thunk::Body(false_body.clone()).into(),
|
||||
},
|
||||
|
||||
// do something after
|
||||
Thunk::Body(end_body.clone()),
|
||||
]);
|
||||
@@ -495,10 +507,55 @@ fn test_flatten_thunk() {
|
||||
assert_eq!(blocks.len(), block_count);
|
||||
|
||||
let mut iter = blocks.into_iter();
|
||||
assert_eq!(iter.next().unwrap(), (0, BasicBlock::Block { exit: 1, block: init_body, }));
|
||||
assert_eq!(iter.next().unwrap(), (1, BasicBlock::Branch { block_true: 2, block_false: 3, }));
|
||||
assert_eq!(iter.next().unwrap(), (2, BasicBlock::Block { exit: 4, block: true_body, }));
|
||||
assert_eq!(iter.next().unwrap(), (3, BasicBlock::Block { exit: 4, block: false_body, }));
|
||||
assert_eq!(iter.next().unwrap(), (4, BasicBlock::Block { exit: 5, block: end_body, }));
|
||||
assert_eq!(
|
||||
iter.next().unwrap(),
|
||||
(
|
||||
0,
|
||||
BasicBlock::Block {
|
||||
exit: 1,
|
||||
block: init_body,
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next().unwrap(),
|
||||
(
|
||||
1,
|
||||
BasicBlock::Branch {
|
||||
block_true: 2,
|
||||
block_false: 3,
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next().unwrap(),
|
||||
(
|
||||
2,
|
||||
BasicBlock::Block {
|
||||
exit: 4,
|
||||
block: true_body,
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next().unwrap(),
|
||||
(
|
||||
3,
|
||||
BasicBlock::Block {
|
||||
exit: 4,
|
||||
block: false_body,
|
||||
}
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next().unwrap(),
|
||||
(
|
||||
4,
|
||||
BasicBlock::Block {
|
||||
exit: 5,
|
||||
block: end_body,
|
||||
}
|
||||
)
|
||||
);
|
||||
assert!(iter.next().is_none());
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
|
||||
#[macro_use]
|
||||
pub mod obj;
|
||||
pub mod syn;
|
||||
pub mod compile;
|
||||
pub mod syn;
|
||||
pub mod vm;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use crate::obj::{ObjRef, sym::Sym};
|
||||
use crate::obj::{sym::Sym, ObjRef};
|
||||
use shredder::{Gc, Scan};
|
||||
use std::{collections::BTreeMap, ops::{Deref, DerefMut}};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
pub type Attrs = BTreeMap<Sym, ObjRef>;
|
||||
pub type VtableAttrs = Gc<Attrs>;
|
||||
@@ -12,7 +15,9 @@ pub struct Vtable {
|
||||
|
||||
impl Vtable {
|
||||
pub fn new(attrs: Attrs) -> Self {
|
||||
Self { attrs: Gc::new(attrs), }
|
||||
Self {
|
||||
attrs: Gc::new(attrs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +1,72 @@
|
||||
use crate::{obj::{prelude::*, reserved::*}, vm::{error::*, signal::*}};
|
||||
use crate::{
|
||||
obj::{prelude::*, reserved::*},
|
||||
vm::{error::*, signal::*},
|
||||
};
|
||||
use maplit::btreemap;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub static PRINTLN_BUILTIN_FUN: Lazy<NativeFunRef> = Lazy::new(|| NativeFun::new_obj(1, |_, vm, args| {
|
||||
// TODO : use __get_attr__ when it gets added
|
||||
let to_string = {
|
||||
let obj_ref = &args[0];
|
||||
read_obj!(let obj = obj_ref);
|
||||
obj.get_attr(STR_MEMBER_NAME.sym)
|
||||
.ok_or(Error::MissingAttr { attr: STR_MEMBER_NAME.sym })?
|
||||
};
|
||||
let return_value = vm.call(to_string, vec![])?;
|
||||
{
|
||||
read_obj!(let str_obj = return_value);
|
||||
let str_obj: &Str = str_obj.as_any().downcast_ref()
|
||||
.ok_or_else(|| Error::ValueError { error: "expected str value".to_string(), })?;
|
||||
println!("{}", str_obj.value());
|
||||
}
|
||||
pub static PRINTLN_BUILTIN_FUN: Lazy<NativeFunRef> = Lazy::new(|| {
|
||||
NativeFun::new_obj(1, |_, vm, args| {
|
||||
// TODO : use __get_attr__ when it gets added
|
||||
let to_string = {
|
||||
let obj_ref = &args[0];
|
||||
read_obj!(let obj = obj_ref);
|
||||
obj.get_attr(STR_MEMBER_NAME.sym)
|
||||
.ok_or(Error::MissingAttr {
|
||||
attr: STR_MEMBER_NAME.sym,
|
||||
})?
|
||||
};
|
||||
let return_value = vm.call(to_string, vec![])?;
|
||||
{
|
||||
read_obj!(let str_obj = return_value);
|
||||
let str_obj: &Str =
|
||||
str_obj
|
||||
.as_any()
|
||||
.downcast_ref()
|
||||
.ok_or_else(|| Error::ValueError {
|
||||
error: "expected str value".to_string(),
|
||||
})?;
|
||||
println!("{}", str_obj.value());
|
||||
}
|
||||
|
||||
vm.push(NIL_NAME.sym_ref());
|
||||
Ok(Signal::Return)
|
||||
}));
|
||||
|
||||
pub static PRINT_BUILTIN_FUN: Lazy<NativeFunRef> = Lazy::new(|| NativeFun::new_obj(1, |_, vm, args| {
|
||||
// TODO : use __get_attr__ when it gets added
|
||||
let to_string = {
|
||||
let obj_ref = &args[0];
|
||||
read_obj!(let obj = obj_ref);
|
||||
obj.get_attr(STR_MEMBER_NAME.sym)
|
||||
.ok_or(Error::MissingAttr { attr: STR_MEMBER_NAME.sym })?
|
||||
};
|
||||
let return_value = vm.call(to_string, vec![])?;
|
||||
{
|
||||
read_obj!(let str_obj = return_value);
|
||||
let str_obj: &Str = str_obj.as_any().downcast_ref()
|
||||
.ok_or_else(|| Error::ValueError { error: "expected str value".to_string(), })?;
|
||||
print!("{}", str_obj.value());
|
||||
}
|
||||
|
||||
vm.push(NIL_NAME.sym_ref());
|
||||
Ok(Signal::Return)
|
||||
}));
|
||||
|
||||
pub static BUILTIN_OBJS: Lazy<BTreeMap<Sym, ObjRef>> = Lazy::new(|| btreemap! {
|
||||
PRINTLN_BUILTIN_NAME.sym => PRINTLN_BUILTIN_FUN.clone() as _,
|
||||
PRINT_BUILTIN_NAME.sym => PRINT_BUILTIN_FUN.clone() as _,
|
||||
vm.push(NIL_NAME.sym_ref());
|
||||
Ok(Signal::Return)
|
||||
})
|
||||
});
|
||||
|
||||
pub static PRINT_BUILTIN_FUN: Lazy<NativeFunRef> = Lazy::new(|| {
|
||||
NativeFun::new_obj(1, |_, vm, args| {
|
||||
// TODO : use __get_attr__ when it gets added
|
||||
let to_string = {
|
||||
let obj_ref = &args[0];
|
||||
read_obj!(let obj = obj_ref);
|
||||
obj.get_attr(STR_MEMBER_NAME.sym)
|
||||
.ok_or(Error::MissingAttr {
|
||||
attr: STR_MEMBER_NAME.sym,
|
||||
})?
|
||||
};
|
||||
let return_value = vm.call(to_string, vec![])?;
|
||||
{
|
||||
read_obj!(let str_obj = return_value);
|
||||
let str_obj: &Str =
|
||||
str_obj
|
||||
.as_any()
|
||||
.downcast_ref()
|
||||
.ok_or_else(|| Error::ValueError {
|
||||
error: "expected str value".to_string(),
|
||||
})?;
|
||||
print!("{}", str_obj.value());
|
||||
}
|
||||
|
||||
vm.push(NIL_NAME.sym_ref());
|
||||
Ok(Signal::Return)
|
||||
})
|
||||
});
|
||||
|
||||
pub static BUILTIN_OBJS: Lazy<BTreeMap<Sym, ObjRef>> = Lazy::new(|| {
|
||||
btreemap! {
|
||||
PRINTLN_BUILTIN_NAME.sym => PRINTLN_BUILTIN_FUN.clone() as _,
|
||||
PRINT_BUILTIN_NAME.sym => PRINT_BUILTIN_FUN.clone() as _,
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
use crate::{obj::{reserved::*, prelude::*}, vm::{consts::ConstPool, error::*, frame::*, inst::Inst, signal::*, Vm}};
|
||||
use crate::{
|
||||
obj::{prelude::*, reserved::*},
|
||||
vm::{consts::ConstPool, error::*, frame::*, inst::Inst, signal::*, Vm},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use shredder::{GcSafeWrapper, Scan};
|
||||
use std::{fmt::{Debug, Formatter, self}, io::{self, Write}, sync::Arc};
|
||||
use std::{
|
||||
fmt::{self, Debug, Formatter},
|
||||
io::{self, Write},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
pub type FunLocals = Vec<Sym>;
|
||||
|
||||
@@ -49,9 +56,13 @@ impl Obj for Method {
|
||||
Some(&mut self.attrs)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any { self }
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_fun(&self) -> Option<&dyn Fun> { Some(self) }
|
||||
fn as_fun(&self) -> Option<&dyn Fun> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Fun for Method {
|
||||
@@ -110,7 +121,12 @@ impl UserFun {
|
||||
&self.locals
|
||||
}
|
||||
|
||||
pub fn dump(&self, writer: &mut dyn Write, const_pool: &ConstPool, globals: &FunLocals) -> io::Result<()> {
|
||||
pub fn dump(
|
||||
&self,
|
||||
writer: &mut dyn Write,
|
||||
const_pool: &ConstPool,
|
||||
globals: &FunLocals,
|
||||
) -> io::Result<()> {
|
||||
fn num_digits(n: usize, radix: usize) -> usize {
|
||||
((n as f64) + 1.0).log(radix as f64).ceil() as usize
|
||||
}
|
||||
@@ -126,21 +142,22 @@ impl UserFun {
|
||||
sym.index().to_string(),
|
||||
global_sym_lookup(*sym).unwrap().to_string(),
|
||||
),
|
||||
Inst::PushConst(hdl) => (
|
||||
hdl.index().to_string(),
|
||||
{
|
||||
let obj_ref = const_pool.get(*hdl);
|
||||
read_obj!(let obj = obj_ref);
|
||||
format!("{:?}", obj)
|
||||
},
|
||||
),
|
||||
Inst::PushConst(hdl) => (hdl.index().to_string(), {
|
||||
let obj_ref = const_pool.get(*hdl);
|
||||
read_obj!(let obj = obj_ref);
|
||||
format!("{:?}", obj)
|
||||
}),
|
||||
Inst::LoadLocal(local) => (
|
||||
local.index().to_string(),
|
||||
global_sym_lookup(self.locals()[local.index()]).unwrap().to_string(),
|
||||
global_sym_lookup(self.locals()[local.index()])
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
),
|
||||
Inst::LoadGlobal(global) => (
|
||||
global.index().to_string(),
|
||||
global_sym_lookup(globals[global.index()]).unwrap().to_string(),
|
||||
global_sym_lookup(globals[global.index()])
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
),
|
||||
Inst::PopLocal(local) => {
|
||||
if let Some(local) = local {
|
||||
@@ -153,7 +170,7 @@ impl UserFun {
|
||||
}
|
||||
}
|
||||
Inst::PopGlobal(global) => {
|
||||
if let Some(global) = global{
|
||||
if let Some(global) = global {
|
||||
let index = global.index();
|
||||
let sym = globals[index];
|
||||
let name = global_sym_lookup(sym).unwrap().to_string();
|
||||
@@ -162,14 +179,8 @@ impl UserFun {
|
||||
("(discarded)".to_string(), String::new())
|
||||
}
|
||||
}
|
||||
Inst::Jump(addr) | Inst::JumpTrue(addr) => (
|
||||
addr.to_string(),
|
||||
String::new(),
|
||||
),
|
||||
Inst::Call(argc) => (
|
||||
argc.to_string(),
|
||||
String::new(),
|
||||
),
|
||||
Inst::Jump(addr) | Inst::JumpTrue(addr) => (addr.to_string(), String::new()),
|
||||
Inst::Call(argc) => (argc.to_string(), String::new()),
|
||||
_ => (String::new(), String::new()),
|
||||
};
|
||||
|
||||
@@ -224,12 +235,13 @@ impl Fun for UserFun {
|
||||
got: args.len(),
|
||||
});
|
||||
}
|
||||
let bindings: FrameBindings = args.into_iter()
|
||||
.enumerate()
|
||||
.collect();
|
||||
Ok(Frame::User(
|
||||
UserFrame::new(callee, bindings, vm.pc(), Arc::clone(&self.code))
|
||||
))
|
||||
let bindings: FrameBindings = args.into_iter().enumerate().collect();
|
||||
Ok(Frame::User(UserFrame::new(
|
||||
callee,
|
||||
bindings,
|
||||
vm.pc(),
|
||||
Arc::clone(&self.code),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +306,10 @@ impl NativeFun {
|
||||
impl Debug for NativeFun {
|
||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("NativeFun")
|
||||
.field("fun", &format!("(function at {:x})", &self.fun as *const _ as usize))
|
||||
.field(
|
||||
"fun",
|
||||
&format!("(function at {:x})", &self.fun as *const _ as usize),
|
||||
)
|
||||
.field("attrs", &self.attrs)
|
||||
.finish()
|
||||
}
|
||||
@@ -330,9 +345,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, args)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use crate::{obj::{prelude::*, reserved::*}, vm::error::*};
|
||||
use crate::{
|
||||
obj::{prelude::*, reserved::*},
|
||||
vm::error::*,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use shredder::Scan;
|
||||
use std::fmt::{Debug, Formatter, self};
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
pub type IntRef = ObjRef<Int>;
|
||||
pub type IntValue = i128;
|
||||
@@ -43,9 +46,7 @@ impl_obj_readonly!(Int);
|
||||
|
||||
impl Debug for Int {
|
||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Int")
|
||||
.field("value", &self.value)
|
||||
.finish()
|
||||
fmt.debug_struct("Int").field("value", &self.value).finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,12 @@ pub struct Interner<T: Hash + Eq> {
|
||||
|
||||
impl<T: Hash + Eq> Interner<T> {
|
||||
pub fn new(forward: Vec<Arc<T>>) -> Self {
|
||||
let reverse = forward.iter()
|
||||
let reverse = forward
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, s)| (Arc::clone(s), Sym::new(i)))
|
||||
.collect();
|
||||
Self {
|
||||
forward,
|
||||
reverse,
|
||||
}
|
||||
Self { forward, reverse }
|
||||
}
|
||||
|
||||
pub fn forward(&self) -> &Vec<Arc<T>> {
|
||||
@@ -41,18 +39,19 @@ impl<T: Hash + Eq> Interner<T> {
|
||||
}
|
||||
|
||||
pub fn lookup(&self, sym: Sym) -> Option<Arc<T>> {
|
||||
self.forward.get(sym.index())
|
||||
.map(Arc::clone)
|
||||
self.forward.get(sym.index()).map(Arc::clone)
|
||||
}
|
||||
|
||||
pub fn lookup_sym(&self, value: impl std::borrow::Borrow<T>) -> Option<Sym> {
|
||||
self.reverse.get(value.borrow())
|
||||
.copied()
|
||||
self.reverse.get(value.borrow()).copied()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash + Eq> Default for Interner<T> {
|
||||
fn default() -> Self {
|
||||
Interner { forward: Default::default(), reverse: Default::default(), }
|
||||
Interner {
|
||||
forward: Default::default(),
|
||||
reverse: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ macro_rules! impl_obj {
|
||||
fn attrs_mut(&mut self) -> Option<&mut $crate::obj::attrs::Attrs> {
|
||||
Some(&mut self.$attrs)
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
@@ -23,7 +23,7 @@ macro_rules! impl_obj {
|
||||
|
||||
($ty:ty) => {
|
||||
impl_obj!($ty, vtable, attrs);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
||||
@@ -40,7 +40,7 @@ macro_rules! obj_attr {
|
||||
fn $name(&self) -> Option<ObjRef> {
|
||||
self.get_attr($attr.sym)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Obj: Scan + Debug {
|
||||
@@ -58,7 +58,9 @@ pub trait Obj: Scan + Debug {
|
||||
fn as_any(&self) -> &dyn std::any::Any;
|
||||
|
||||
/// Gets this object as a `dyn Fun` reference.
|
||||
fn as_fun(&self) -> Option<&dyn Fun> { None }
|
||||
fn as_fun(&self) -> Option<&dyn Fun> {
|
||||
None
|
||||
}
|
||||
|
||||
obj_attr!(get_ty, TY_MEMBER_NAME);
|
||||
obj_attr!(get_call, CALL_MEMBER_NAME);
|
||||
@@ -140,7 +142,8 @@ where
|
||||
// impl Debug for ObjRef
|
||||
//
|
||||
impl<T> Debug for ObjRef<T>
|
||||
where T: Obj + ?Sized + Send + Sync + 'static,
|
||||
where
|
||||
T: Obj + ?Sized + Send + Sync + 'static,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||
read_obj!(let obj: &T = self);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Reserved names for object members.
|
||||
|
||||
use crate::obj::sym::{Sym, SymRef, global_sym, global_sym_ref};
|
||||
use crate::obj::sym::{global_sym, global_sym_ref, Sym, SymRef};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
macro_rules! name {
|
||||
@@ -8,9 +8,9 @@ macro_rules! name {
|
||||
pub static $name: Lazy<NameInfo> = Lazy::new(|| {
|
||||
let name = $text;
|
||||
let sym = global_sym(name.to_string());
|
||||
NameInfo { name, sym, }
|
||||
NameInfo { name, sym }
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct NameInfo {
|
||||
@@ -79,4 +79,3 @@ name!(PLUS_OP_NAME, "__add__");
|
||||
name!(MINUS_OP_NAME, "__sub__");
|
||||
name!(TIMES_OP_NAME, "__mul__");
|
||||
name!(DIV_OP_NAME, "__div__");
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use crate::{obj::{prelude::*, reserved::*}, vm::error::*};
|
||||
use crate::{
|
||||
obj::{prelude::*, reserved::*},
|
||||
vm::error::*,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use shredder::Scan;
|
||||
use std::fmt::{Debug, Formatter, self};
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
pub type StrRef = ObjRef<Str>;
|
||||
|
||||
@@ -37,9 +40,7 @@ impl_obj_readonly!(Str);
|
||||
|
||||
impl Debug for Str {
|
||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Str")
|
||||
.field("value", &self.value)
|
||||
.finish()
|
||||
fmt.debug_struct("Str").field("value", &self.value).finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +87,9 @@ static STR_INT_FUN: Lazy<NativeFunRef> = Lazy::new(|| {
|
||||
error: "expected Str value".to_string(),
|
||||
})?;
|
||||
|
||||
let int: IntValue = str_obj.value.parse()
|
||||
.map_err(|_| Error::ValueError { error: "invalid Int value".to_string(), })?;
|
||||
let int: IntValue = str_obj.value.parse().map_err(|_| Error::ValueError {
|
||||
error: "invalid Int value".to_string(),
|
||||
})?;
|
||||
vm.push(Int::new_obj(int));
|
||||
|
||||
Ok(crate::vm::signal::Signal::Return)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use crate::obj::{intern::Interner, reserved::*, prelude::*};
|
||||
use crate::obj::{intern::Interner, prelude::*, reserved::*};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{collections::BTreeMap, sync::{Arc, Mutex}};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
//
|
||||
// struct Sym
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::obj::{reserved::*, prelude::*};
|
||||
use crate::obj::{prelude::*, reserved::*};
|
||||
use once_cell::sync::Lazy;
|
||||
use shredder::Scan;
|
||||
|
||||
|
||||
@@ -13,10 +13,9 @@ impl<V: Visit> Accept<V> for Body {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> for Body {
|
||||
impl<V: Visit<Out = ()>> DefaultAccept<V> for Body {
|
||||
fn default_accept(&self, visitor: &mut V) -> V::Out {
|
||||
self.iter()
|
||||
.for_each(|stmt| visitor.visit_stmt(stmt));
|
||||
self.iter().for_each(|stmt| visitor.visit_stmt(stmt));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +66,7 @@ impl<V: Visit> Accept<V> for AssignStmt {
|
||||
//
|
||||
// impl DefaultAccept for AssignStmt
|
||||
//
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> for AssignStmt {
|
||||
impl<V: Visit<Out = ()>> DefaultAccept<V> for AssignStmt {
|
||||
fn default_accept(&self, visitor: &mut V) -> V::Out {
|
||||
self.lhs.accept(visitor);
|
||||
self.rhs.accept(visitor);
|
||||
@@ -92,11 +91,11 @@ impl<V: Visit> Accept<V> for LhsExpr {
|
||||
//
|
||||
// impl DefaultAccept for LhsExpr
|
||||
//
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> for LhsExpr {
|
||||
impl<V: Visit<Out = ()>> DefaultAccept<V> for LhsExpr {
|
||||
fn default_accept(&self, visitor: &mut V) -> V::Out {
|
||||
match self {
|
||||
LhsExpr::SetAttr(a) => a.accept(visitor),
|
||||
LhsExpr::Name(_) => {},
|
||||
LhsExpr::Name(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,7 +114,7 @@ impl<V: Visit> Accept<V> for ReturnStmt {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> for ReturnStmt {
|
||||
impl<V: Visit<Out = ()>> DefaultAccept<V> for ReturnStmt {
|
||||
fn default_accept(&self, visitor: &mut V) -> V::Out {
|
||||
if let Some(expr) = self.expr.as_ref() {
|
||||
expr.accept(visitor)
|
||||
@@ -123,7 +122,6 @@ impl<V: Visit<Out=()>> DefaultAccept<V> for ReturnStmt {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// struct Expr
|
||||
//
|
||||
@@ -237,7 +235,7 @@ pub struct BinExpr {
|
||||
|
||||
impl BinExpr {
|
||||
pub fn new_expr(lhs: Expr, op: BinOp, rhs: Expr) -> Expr {
|
||||
Self { lhs, op, rhs, }.into()
|
||||
Self { lhs, op, rhs }.into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +251,7 @@ impl<V: Visit> Accept<V> for BinExpr {
|
||||
//
|
||||
// impl DefaultAccept for BinExpr
|
||||
//
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> for BinExpr {
|
||||
impl<V: Visit<Out = ()>> DefaultAccept<V> for BinExpr {
|
||||
fn default_accept(&self, visitor: &mut V) -> V::Out {
|
||||
visitor.visit_expr(&self.lhs);
|
||||
visitor.visit_expr(&self.rhs);
|
||||
@@ -326,11 +324,10 @@ impl<V: Visit> Accept<V> for CallExpr {
|
||||
//
|
||||
// impl DefaultAccept for CallExpr
|
||||
//
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> for CallExpr {
|
||||
impl<V: Visit<Out = ()>> DefaultAccept<V> for CallExpr {
|
||||
fn default_accept(&self, visitor: &mut V) -> V::Out {
|
||||
visitor.visit_expr(&self.expr);
|
||||
self.args.iter()
|
||||
.for_each(|arg| visitor.visit_expr(arg));
|
||||
self.args.iter().for_each(|arg| visitor.visit_expr(arg));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,7 +342,7 @@ pub struct IndexExpr {
|
||||
|
||||
impl IndexExpr {
|
||||
pub fn new_expr(expr: Expr, index: Expr) -> Expr {
|
||||
Self { expr, index, }.into()
|
||||
Self { expr, index }.into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,7 +358,7 @@ impl<V: Visit> Accept<V> for IndexExpr {
|
||||
//
|
||||
// impl DefaultAccept for IndexExpr
|
||||
//
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> for IndexExpr {
|
||||
impl<V: Visit<Out = ()>> DefaultAccept<V> for IndexExpr {
|
||||
fn default_accept(&self, visitor: &mut V) -> V::Out {
|
||||
visitor.visit_expr(&self.expr);
|
||||
visitor.visit_expr(&self.index);
|
||||
@@ -379,7 +376,7 @@ pub struct AccessExpr {
|
||||
|
||||
impl AccessExpr {
|
||||
pub fn new_expr(expr: Expr, access: String) -> Expr {
|
||||
Self { expr, access, }.into()
|
||||
Self { expr, access }.into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,7 +412,7 @@ pub struct FunExpr {
|
||||
//
|
||||
impl FunExpr {
|
||||
pub fn new_expr(params: Vec<String>, body: Body) -> Expr {
|
||||
Self { params, body, }.into()
|
||||
Self { params, body }.into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,8 +459,8 @@ impl<V: Visit> Accept<V> for Atom {
|
||||
// impl DefaultAccept for Atom
|
||||
//
|
||||
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> for Atom {
|
||||
fn default_accept(&self, _visitor: &mut V) -> V::Out { }
|
||||
impl<V: Visit<Out = ()>> DefaultAccept<V> for Atom {
|
||||
fn default_accept(&self, _visitor: &mut V) -> V::Out {}
|
||||
}
|
||||
|
||||
pub fn parse(text: &str) -> Result<Option<Vec<Stmt>>, Vec<String>> {
|
||||
@@ -473,9 +470,10 @@ pub fn parse(text: &str) -> Result<Option<Vec<Stmt>>, Vec<String>> {
|
||||
let (res, errors) = parser::parse(&lexer);
|
||||
|
||||
if !errors.is_empty() {
|
||||
return Err(errors.into_iter()
|
||||
return Err(errors
|
||||
.into_iter()
|
||||
.map(|e| e.pp(&lexer, &parser::token_epp))
|
||||
.collect())
|
||||
.collect());
|
||||
}
|
||||
|
||||
Ok(res.transpose().unwrap())
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
#![macro_use] mod macros;
|
||||
#![macro_use]
|
||||
pub mod ast;
|
||||
mod macros;
|
||||
pub mod visit;
|
||||
|
||||
pub mod lexer {
|
||||
lrlex_mod!("syn/lexer.l");
|
||||
use lrlex::lrlex_mod;
|
||||
pub use self::lexer_l::*;
|
||||
|
||||
use lrlex::lrlex_mod;
|
||||
}
|
||||
|
||||
pub mod parser {
|
||||
lrpar_mod!("syn/parser.y");
|
||||
use lrpar::lrpar_mod;
|
||||
pub use self::parser_y::*;
|
||||
use lrpar::lrpar_mod;
|
||||
}
|
||||
|
||||
//pub use lexer_l as lexer;
|
||||
|
||||
@@ -8,7 +8,7 @@ pub struct ConstPool {
|
||||
}
|
||||
|
||||
// Constant pools are constant, and live for the lifetime of the entire program.
|
||||
impl shredder::EmptyScan for ConstPool { }
|
||||
impl shredder::EmptyScan for ConstPool {}
|
||||
|
||||
impl ConstPool {
|
||||
pub fn new() -> Self {
|
||||
|
||||
@@ -4,20 +4,17 @@ use snafu::Snafu;
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum Error {
|
||||
#[snafu(display("missing attribute: {}", global_sym_lookup(*attr).unwrap()))]
|
||||
MissingAttr {
|
||||
attr: Sym,
|
||||
},
|
||||
MissingAttr { attr: Sym },
|
||||
|
||||
#[snafu(display("{}", error))]
|
||||
ValueError {
|
||||
error: String,
|
||||
},
|
||||
ValueError { error: String },
|
||||
|
||||
#[snafu(display("incorrect function arity; expected {} but got {} instead", expected, got))]
|
||||
ArityError {
|
||||
expected: usize,
|
||||
got: usize,
|
||||
},
|
||||
#[snafu(display(
|
||||
"incorrect function arity; expected {} but got {} instead",
|
||||
expected,
|
||||
got
|
||||
))]
|
||||
ArityError { expected: usize, got: usize },
|
||||
}
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{obj::prelude::*, vm::inst::Inst};
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
use std::fmt::{self, Formatter, Debug};
|
||||
|
||||
pub type FrameBindings = BTreeMap<usize, ObjRef>;
|
||||
|
||||
@@ -53,8 +53,18 @@ pub struct UserFrame {
|
||||
}
|
||||
|
||||
impl UserFrame {
|
||||
pub fn new(callee: ObjRef, bindings: FrameBindings, last_pc: usize, code: Arc<Vec<Inst>>) -> Self {
|
||||
Self { callee, bindings, last_pc, code, }
|
||||
pub fn new(
|
||||
callee: ObjRef,
|
||||
bindings: FrameBindings,
|
||||
last_pc: usize,
|
||||
code: Arc<Vec<Inst>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
callee,
|
||||
bindings,
|
||||
last_pc,
|
||||
code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bindings(&self) -> &FrameBindings {
|
||||
@@ -86,7 +96,11 @@ pub struct NativeFrame {
|
||||
|
||||
impl NativeFrame {
|
||||
pub fn new(callee: ObjRef, fun_ptr: NativeFunPtr, args: Vec<ObjRef>) -> Self {
|
||||
Self { callee, fun_ptr, args }
|
||||
Self {
|
||||
callee,
|
||||
fun_ptr,
|
||||
args,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fun_ptr(&self) -> &NativeFunPtr {
|
||||
@@ -105,7 +119,10 @@ impl NativeFrame {
|
||||
impl Debug for NativeFrame {
|
||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("NativeFrame")
|
||||
.field("fun_ptr", &format!("{:#x}", &self.fun_ptr as *const _ as usize))
|
||||
.field(
|
||||
"fun_ptr",
|
||||
&format!("{:#x}", &self.fun_ptr as *const _ as usize),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,4 +154,4 @@ impl Inst {
|
||||
}
|
||||
}
|
||||
|
||||
impl EmptyScan for Inst { }
|
||||
impl EmptyScan for Inst {}
|
||||
|
||||
@@ -5,7 +5,7 @@ pub mod inst;
|
||||
pub mod signal;
|
||||
|
||||
use crate::{
|
||||
obj::{builtin::BUILTIN_OBJS, reserved::*, prelude::*},
|
||||
obj::{builtin::BUILTIN_OBJS, prelude::*, reserved::*},
|
||||
vm::{consts::ConstPool, error::*, frame::*, inst::*, signal::*},
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ pub struct Vm<'c> {
|
||||
}
|
||||
|
||||
impl<'c> Vm<'c> {
|
||||
pub fn new(const_pool: &'c ConstPool,) -> Self {
|
||||
pub fn new(const_pool: &'c ConstPool) -> Self {
|
||||
Self {
|
||||
stack: Default::default(),
|
||||
frames: vec![],
|
||||
@@ -122,9 +122,7 @@ impl<'c> Vm<'c> {
|
||||
let args = fun.args().clone();
|
||||
(*fun.fun_ptr())(callee, self, args)?
|
||||
}
|
||||
Frame::User(_) => {
|
||||
self.resume_user_fun()
|
||||
}
|
||||
Frame::User(_) => self.resume_user_fun(),
|
||||
};
|
||||
self.handle_signal(signal)?;
|
||||
}
|
||||
@@ -148,8 +146,11 @@ impl<'c> Vm<'c> {
|
||||
match signal {
|
||||
Signal::Call(callee, args) => {
|
||||
read_obj!(let callee_obj = callee);
|
||||
let frame = callee_obj.as_fun()
|
||||
.ok_or_else(|| Error::ValueError { error: "cannot call this object".to_string() })?
|
||||
let frame = callee_obj
|
||||
.as_fun()
|
||||
.ok_or_else(|| Error::ValueError {
|
||||
error: "cannot call this object".to_string(),
|
||||
})?
|
||||
.create_frame(callee.clone(), self, args)?;
|
||||
// Jump to the first address of the new function call if it's a user function
|
||||
if let Frame::User(_) = &frame {
|
||||
@@ -207,19 +208,20 @@ impl<'c> Vm<'c> {
|
||||
self.push(obj_ref);
|
||||
}
|
||||
Inst::LoadLocal(local) => {
|
||||
let value = self.get_local(local)
|
||||
let value = self
|
||||
.get_local(local)
|
||||
.expect("TODO: throw error for missing local");
|
||||
self.push(value);
|
||||
}
|
||||
Inst::LoadGlobal(global) => {
|
||||
let value = self.get_global(global)
|
||||
let value = self
|
||||
.get_global(global)
|
||||
.or_else(|| self.get_builtin(global))
|
||||
.expect("TODO: throw error for missing global");
|
||||
self.push(value);
|
||||
}
|
||||
Inst::PopLocal(name) => {
|
||||
let tos = self.pop()
|
||||
.expect("stack underflow");
|
||||
let tos = self.pop().expect("stack underflow");
|
||||
// pop into name
|
||||
if let Some(name) = name {
|
||||
self.set_local(name, tos);
|
||||
@@ -227,8 +229,7 @@ impl<'c> Vm<'c> {
|
||||
// else discard
|
||||
}
|
||||
Inst::PopGlobal(name) => {
|
||||
let tos = self.pop()
|
||||
.expect("stack underflow");
|
||||
let tos = self.pop().expect("stack underflow");
|
||||
// pop into name
|
||||
if let Some(name) = name {
|
||||
self.set_global(name, tos);
|
||||
@@ -244,10 +245,8 @@ impl<'c> Vm<'c> {
|
||||
self.push(attr);
|
||||
}
|
||||
Inst::SetAttr(sym) => {
|
||||
let target = self.pop()
|
||||
.expect("no target available for SetAttr");
|
||||
let source = self.pop()
|
||||
.expect("no source available for SetAttr");
|
||||
let target = self.pop().expect("no target available for SetAttr");
|
||||
let source = self.pop().expect("no source available for SetAttr");
|
||||
write_obj!(let target = target);
|
||||
if let Some(attrs) = target.attrs_mut() {
|
||||
attrs.insert(sym, source);
|
||||
@@ -266,15 +265,17 @@ impl<'c> Vm<'c> {
|
||||
Inst::Call(argc) => {
|
||||
let stack_top = self.stack.len() - argc;
|
||||
let args = self.stack.split_off(stack_top);
|
||||
let tos = self.pop()
|
||||
.expect("stack underflow");
|
||||
let tos = self.pop().expect("stack underflow");
|
||||
read_obj!(let tos = tos);
|
||||
let callee = tos.get_call()
|
||||
let callee = tos
|
||||
.get_call()
|
||||
.expect("TODO: throw an error for missing __call__ attr");
|
||||
signal = Some(Signal::Call(callee, args));
|
||||
}
|
||||
Inst::Index => todo!(),
|
||||
Inst::Return => { signal = Some(Signal::Return); }
|
||||
Inst::Return => {
|
||||
signal = Some(Signal::Return);
|
||||
}
|
||||
Inst::UnNeg => todo!(),
|
||||
Inst::UnPos => todo!(),
|
||||
Inst::BinPlus => {
|
||||
@@ -282,7 +283,8 @@ impl<'c> Vm<'c> {
|
||||
let lhs = self.pop().unwrap();
|
||||
let fun = {
|
||||
read_obj!(let lhs = lhs);
|
||||
lhs.get_plus().expect("TODO: throw an error for missing __add__ attr")
|
||||
lhs.get_plus()
|
||||
.expect("TODO: throw an error for missing __add__ attr")
|
||||
};
|
||||
signal = Some(Signal::Call(fun, vec![rhs]));
|
||||
}
|
||||
@@ -291,7 +293,8 @@ impl<'c> Vm<'c> {
|
||||
let lhs = self.pop().unwrap();
|
||||
let fun = {
|
||||
read_obj!(let lhs = lhs);
|
||||
lhs.get_minus().expect("TODO: throw an error for missing __sub__ attr")
|
||||
lhs.get_minus()
|
||||
.expect("TODO: throw an error for missing __sub__ attr")
|
||||
};
|
||||
signal = Some(Signal::Call(fun, vec![rhs]));
|
||||
}
|
||||
@@ -300,7 +303,8 @@ impl<'c> Vm<'c> {
|
||||
let lhs = self.pop().unwrap();
|
||||
let fun = {
|
||||
read_obj!(let lhs = lhs);
|
||||
lhs.get_mul().expect("TODO: throw an error for missing __mul__ attr")
|
||||
lhs.get_mul()
|
||||
.expect("TODO: throw an error for missing __mul__ attr")
|
||||
};
|
||||
signal = Some(Signal::Call(fun, vec![rhs]));
|
||||
}
|
||||
@@ -309,7 +313,8 @@ impl<'c> Vm<'c> {
|
||||
let lhs = self.pop().unwrap();
|
||||
let fun = {
|
||||
read_obj!(let lhs = lhs);
|
||||
lhs.get_div().expect("TODO: throw an error for missing __div__ attr")
|
||||
lhs.get_div()
|
||||
.expect("TODO: throw an error for missing __div__ attr")
|
||||
};
|
||||
signal = Some(Signal::Call(fun, vec![rhs]));
|
||||
}
|
||||
@@ -318,7 +323,8 @@ impl<'c> Vm<'c> {
|
||||
let lhs = self.pop().unwrap();
|
||||
let fun = {
|
||||
read_obj!(let lhs = lhs);
|
||||
lhs.get_eq().expect("TODO: throw an error for missing __eq__ attr")
|
||||
lhs.get_eq()
|
||||
.expect("TODO: throw an error for missing __eq__ attr")
|
||||
};
|
||||
signal = Some(Signal::Call(fun, vec![rhs]));
|
||||
}
|
||||
@@ -342,7 +348,7 @@ impl<'c> Vm<'c> {
|
||||
.get(&name.index())
|
||||
.cloned()
|
||||
}
|
||||
|
||||
|
||||
fn set_local(&mut self, name: Name, value: ObjRef) {
|
||||
let frame = self
|
||||
.frame_mut()
|
||||
@@ -363,7 +369,8 @@ impl<'c> Vm<'c> {
|
||||
}
|
||||
|
||||
fn set_global(&mut self, name: Name, value: ObjRef) {
|
||||
let frame = self.frames_mut()
|
||||
let frame = self
|
||||
.frames_mut()
|
||||
.first_mut()
|
||||
.expect("global stack frame")
|
||||
.user_frame_mut()
|
||||
|
||||
@@ -6,4 +6,3 @@ pub enum Signal {
|
||||
Call(ObjRef, Vec<ObjRef>),
|
||||
Return,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user