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