exceptions
This commit is contained in:
parent
7aca8b423c
commit
b48c08cb62
11 changed files with 907 additions and 354 deletions
231
Cargo.lock
generated
231
Cargo.lock
generated
|
@ -12,10 +12,52 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.80"
|
||||
name = "anstream"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
|
||||
checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ascii-canvas"
|
||||
|
@ -65,6 +107,48 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12f9a0700e0127ba15d1d52dd742097f821cd9c65939303a44d970465040a297"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
|
@ -113,12 +197,45 @@ dependencies = [
|
|||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "endian-type"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc"
|
||||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
version = "4.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
|
@ -148,6 +265,15 @@ version = "0.3.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.3"
|
||||
|
@ -226,6 +352,12 @@ dependencies = [
|
|||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
|
@ -254,6 +386,26 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||
|
||||
[[package]]
|
||||
name = "nibble_vec"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.5"
|
||||
|
@ -364,6 +516,16 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radix_trie"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
|
||||
dependencies = [
|
||||
"endian-type",
|
||||
"nibble_vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
|
@ -419,12 +581,47 @@ version = "0.8.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||
|
||||
[[package]]
|
||||
name = "rustyline"
|
||||
version = "13.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"cfg-if",
|
||||
"clipboard-win",
|
||||
"fd-lock",
|
||||
"home",
|
||||
"libc",
|
||||
"log",
|
||||
"memchr",
|
||||
"nix",
|
||||
"radix_trie",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"utf8parse",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
|
@ -456,6 +653,12 @@ dependencies = [
|
|||
"precomputed-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.50"
|
||||
|
@ -471,7 +674,8 @@ dependencies = [
|
|||
name = "talc-bin"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"rustyline",
|
||||
"talc-lang",
|
||||
]
|
||||
|
||||
|
@ -479,7 +683,6 @@ dependencies = [
|
|||
name = "talc-lang"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"num-complex",
|
||||
|
@ -537,12 +740,30 @@ version = "1.0.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
|
|
@ -5,4 +5,5 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
talc-lang = { path = "../talc-lang" }
|
||||
anyhow = "1.0"
|
||||
rustyline = "13.0"
|
||||
clap = "4.5"
|
||||
|
|
|
@ -1,23 +1,9 @@
|
|||
use talc_lang::{Parser, Vm, Symbol, value::{Value, NativeFunc}, compiler::repl};
|
||||
use std::{io::Write, rc::Rc, cell::RefCell};
|
||||
use rustyline::error::ReadlineError;
|
||||
use talc_lang::{Parser, Vm, Symbol, value::{Value, Function}, compiler::repl};
|
||||
use std::{rc::Rc, io::Write};
|
||||
|
||||
fn do_thing() -> impl Fn(Value, Vec<Value>) -> anyhow::Result<Value> {
|
||||
let x = RefCell::new(0);
|
||||
move |_, _| {
|
||||
let v = *x.borrow();
|
||||
if v > 5 {
|
||||
Ok(Value::Nil)
|
||||
} else {
|
||||
*x.borrow_mut() += 1;
|
||||
Ok(Value::Int(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let parser = Parser::new();
|
||||
let mut lines = std::io::stdin().lines().map_while(Result::ok);
|
||||
let mut vm = Vm::new(256);
|
||||
let mut globals = Vec::new();
|
||||
|
||||
|
@ -29,36 +15,44 @@ fn main() {
|
|||
vm.set_global(prev2_sym, Value::Nil);
|
||||
vm.set_global(prev3_sym, Value::Nil);
|
||||
|
||||
vm.set_global(Symbol::get("test"), Value::NativeFunc(NativeFunc {
|
||||
arity: 0,
|
||||
f: Box::new(do_thing()),
|
||||
}.into()));
|
||||
let mut rl = rustyline::DefaultEditor::new()?;
|
||||
|
||||
loop {
|
||||
print!(">> ");
|
||||
std::io::stdout().flush().expect("could not flush stdout");
|
||||
let line = lines.next().expect("could not get next line");
|
||||
let line = rl.readline(">> ");
|
||||
let line = match line {
|
||||
Ok(line) => line,
|
||||
Err(ReadlineError::Eof) => break,
|
||||
Err(ReadlineError::Interrupted) => continue,
|
||||
Err(e) => {
|
||||
eprintln!("Error: {e}");
|
||||
continue
|
||||
},
|
||||
};
|
||||
|
||||
let ex = match parser.parse(&line) {
|
||||
Ok(ex) => ex,
|
||||
Err(e) => { println!("Error: {e}"); continue },
|
||||
Err(e) => { eprintln!("Error: {e}"); continue },
|
||||
};
|
||||
|
||||
let func = match repl(&ex, &globals) {
|
||||
Ok((f, g)) => { globals = g; f },
|
||||
Err(e) => { println!("Error: {e}"); continue },
|
||||
};
|
||||
let (f, g) = repl(&ex, &globals);
|
||||
globals = g;
|
||||
let func = Rc::new(f);
|
||||
|
||||
match vm.run(Rc::new(func)) {
|
||||
Function::disasm_recursive(func.clone(), &mut std::io::stdout())?;
|
||||
std::io::stdout().flush()?;
|
||||
|
||||
match vm.run(func) {
|
||||
Ok(v) => {
|
||||
vm.set_global(prev3_sym, vm.get_global(prev2_sym).unwrap().clone());
|
||||
vm.set_global(prev2_sym, vm.get_global(prev1_sym).unwrap().clone());
|
||||
vm.set_global(prev1_sym, v.clone());
|
||||
if v != Value::Nil {
|
||||
println!("{v}");
|
||||
println!("{v:#}");
|
||||
}
|
||||
}
|
||||
Err(e) => println!("Error: {e}"),
|
||||
Err(e) => eprintln!("{e}"),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ edition = "2021"
|
|||
lalrpop-util = { version = "0.20", features = ["lexer", "unicode"] }
|
||||
num-complex = "0.4"
|
||||
num-rational = { version = "0.4", default-features = false, features = [] }
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
|
||||
[build-dependencies]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::value::Value;
|
||||
use crate::{value::Value, Symbol};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum BinaryOp {
|
||||
|
@ -39,6 +39,14 @@ pub enum Expr<'s> {
|
|||
While(Box<Expr<'s>>, Box<Expr<'s>>),
|
||||
For(&'s str, Box<Expr<'s>>, Box<Expr<'s>>),
|
||||
Lambda(Vec<&'s str>, Box<Expr<'s>>),
|
||||
Try(Box<Expr<'s>>, Vec<CatchBlock<'s>>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CatchBlock<'s> {
|
||||
pub name: Option<&'s str>,
|
||||
pub types: Option<Vec<Symbol>>,
|
||||
pub body: Expr<'s>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -119,56 +119,72 @@ pub enum Instruction {
|
|||
|
||||
IterBegin, IterTest(Arg24),
|
||||
|
||||
BeginTry(Arg24), EndTry,
|
||||
|
||||
Call(u8),
|
||||
Return,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Instruction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use Instruction as I;
|
||||
match *self {
|
||||
I::Nop => write!(f, "nop"),
|
||||
I::LoadLocal(a) => write!(f, "load {}", usize::from(a)),
|
||||
I::StoreLocal(a) => write!(f, "store {}", usize::from(a)),
|
||||
I::NewLocal => write!(f, "newlocal"),
|
||||
I::DropLocal(n) => write!(f, "discardlocal {}", usize::from(n)),
|
||||
I::LoadGlobal(s) => write!(f, "loadglobal {}",
|
||||
Self::Nop => write!(f, "nop"),
|
||||
Self::LoadLocal(a) => write!(f, "load {}", usize::from(a)),
|
||||
Self::StoreLocal(a) => write!(f, "store {}", usize::from(a)),
|
||||
Self::NewLocal => write!(f, "newlocal"),
|
||||
Self::DropLocal(n) => write!(f, "discardlocal {}", usize::from(n)),
|
||||
Self::LoadGlobal(s) => write!(f, "loadglobal {}",
|
||||
Symbol::try_from(s).expect("symbol does not exist").name()),
|
||||
I::StoreGlobal(s) => write!(f, "storeglobal {}",
|
||||
Self::StoreGlobal(s) => write!(f, "storeglobal {}",
|
||||
Symbol::try_from(s).expect("symbol does not exist").name()),
|
||||
I::Const(c) => write!(f, "const {}", usize::from(c)),
|
||||
I::Int(i) => write!(f, "int {}", i64::from(i)),
|
||||
I::Symbol(s) => write!(f, "symbol {}",
|
||||
Self::Const(c) => write!(f, "const {}", usize::from(c)),
|
||||
Self::Int(i) => write!(f, "int {}", i64::from(i)),
|
||||
Self::Symbol(s) => write!(f, "symbol {}",
|
||||
Symbol::try_from(s).expect("symbol does not exist").name()),
|
||||
I::Bool(b) => write!(f, "bool {b}"),
|
||||
I::Nil => write!(f, "nil"),
|
||||
I::Dup => write!(f, "dup"),
|
||||
I::DupTwo => write!(f, "duptwo"),
|
||||
I::Drop(n) => write!(f, "discard {}", usize::from(n)),
|
||||
I::Swap => write!(f, "swap"),
|
||||
I::UnaryOp(o) => write!(f, "unary {o:?}"),
|
||||
I::BinaryOp(o) => write!(f, "binary {o:?}"),
|
||||
I::NewList(n) => write!(f, "newlist {n}"),
|
||||
I::GrowList(n) => write!(f, "growlist {n}"),
|
||||
I::NewTable(n) => write!(f, "newtable {n}"),
|
||||
I::GrowTable(n) => write!(f, "growtable {n}"),
|
||||
I::Index => write!(f, "index"),
|
||||
I::StoreIndex => write!(f, "storeindex"),
|
||||
I::Jump(a) => write!(f, "jump {}", usize::from(a)),
|
||||
I::JumpTrue(a) => write!(f, "jumptrue {}", usize::from(a)),
|
||||
I::JumpFalse(a) => write!(f, "jumpfalse {}", usize::from(a)),
|
||||
I::IterBegin => write!(f, "iterbegin"),
|
||||
I::IterTest(a) => write!(f, "itertest {}", usize::from(a)),
|
||||
I::Call(n) => write!(f, "call {n}"),
|
||||
I::Return => write!(f, "return"),
|
||||
Self::Bool(b) => write!(f, "bool {b}"),
|
||||
Self::Nil => write!(f, "nil"),
|
||||
Self::Dup => write!(f, "dup"),
|
||||
Self::DupTwo => write!(f, "duptwo"),
|
||||
Self::Drop(n) => write!(f, "discard {}", usize::from(n)),
|
||||
Self::Swap => write!(f, "swap"),
|
||||
Self::UnaryOp(o) => write!(f, "unary {o:?}"),
|
||||
Self::BinaryOp(o) => write!(f, "binary {o:?}"),
|
||||
Self::NewList(n) => write!(f, "newlist {n}"),
|
||||
Self::GrowList(n) => write!(f, "growlist {n}"),
|
||||
Self::NewTable(n) => write!(f, "newtable {n}"),
|
||||
Self::GrowTable(n) => write!(f, "growtable {n}"),
|
||||
Self::Index => write!(f, "index"),
|
||||
Self::StoreIndex => write!(f, "storeindex"),
|
||||
Self::Jump(a) => write!(f, "jump {}", usize::from(a)),
|
||||
Self::JumpTrue(a) => write!(f, "jumptrue {}", usize::from(a)),
|
||||
Self::JumpFalse(a) => write!(f, "jumpfalse {}", usize::from(a)),
|
||||
Self::IterBegin => write!(f, "iterbegin"),
|
||||
Self::IterTest(a) => write!(f, "itertest {}", usize::from(a)),
|
||||
Self::BeginTry(t) => write!(f, "begintry {}", usize::from(t)),
|
||||
Self::EndTry => write!(f, "endtry"),
|
||||
Self::Call(n) => write!(f, "call {n}"),
|
||||
Self::Return => write!(f, "return"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Catch {
|
||||
pub addr: usize,
|
||||
pub types: Option<Vec<Symbol>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct TryTable {
|
||||
pub catches: Vec<Catch>,
|
||||
pub local_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Chunk {
|
||||
pub consts: Vec<Value>,
|
||||
pub instrs: Vec<Instruction>,
|
||||
pub try_tables: Vec<TryTable>,
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
|
@ -187,19 +203,19 @@ impl Chunk {
|
|||
self.instrs.push(i);
|
||||
self.instrs.len() - 1
|
||||
}
|
||||
|
||||
pub fn begin_try_table(&mut self, local_count: usize) -> (usize, TryTable) {
|
||||
assert!(self.try_tables.len() < 0xff_ffff, "too many catch tables in a chunk");
|
||||
let table = TryTable {
|
||||
catches: Vec::new(),
|
||||
local_count,
|
||||
};
|
||||
self.try_tables.push(table.clone());
|
||||
(self.try_tables.len() - 1, table)
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Chunk {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "constants")?;
|
||||
for (i, c) in self.consts.iter().enumerate() {
|
||||
writeln!(f, " {i:04}: {c}")?;
|
||||
}
|
||||
writeln!(f, "instructions")?;
|
||||
for (i, n) in self.instrs.iter().enumerate() {
|
||||
writeln!(f, " {i:04}: {n}")?;
|
||||
}
|
||||
Ok(())
|
||||
pub fn finish_catch_table(&mut self, idx: usize, table: TryTable) {
|
||||
self.try_tables[idx] = table;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use crate::ast::{BinaryOp, Expr, LValue};
|
||||
use crate::chunk::{Instruction as I, Chunk, Arg24};
|
||||
use crate::ast::{BinaryOp, Expr, LValue, CatchBlock};
|
||||
use crate::chunk::{Instruction as I, Chunk, Arg24, Catch};
|
||||
use crate::symbol::Symbol;
|
||||
use crate::value::Function;
|
||||
use crate::value::Value;
|
||||
use anyhow::Result;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Local {
|
||||
|
@ -27,7 +26,7 @@ struct Compiler<'a> {
|
|||
globals: Vec<Local>,
|
||||
}
|
||||
|
||||
pub fn repl(expr: &Expr, globals: &[Local]) -> Result<(Function, Vec<Local>)> {
|
||||
pub fn repl(expr: &Expr, globals: &[Local]) -> (Function, Vec<Local>) {
|
||||
let locals = vec![Local {
|
||||
name: "self".into(),
|
||||
scope: 0,
|
||||
|
@ -40,9 +39,9 @@ pub fn repl(expr: &Expr, globals: &[Local]) -> Result<(Function, Vec<Local>)> {
|
|||
locals,
|
||||
globals: globals.to_vec(),
|
||||
};
|
||||
comp.expr(expr)?;
|
||||
comp.expr(expr);
|
||||
comp.emit(I::Return);
|
||||
Ok((comp.func, comp.globals))
|
||||
(comp.func, comp.globals)
|
||||
}
|
||||
|
||||
impl<'a> Compiler<'a> {
|
||||
|
@ -259,45 +258,48 @@ impl<'a> Compiler<'a> {
|
|||
// Expressions
|
||||
//
|
||||
|
||||
fn expr(&mut self, e: &Expr) -> Result<()> {
|
||||
fn expr(&mut self, e: &Expr) {
|
||||
match e {
|
||||
Expr::Block(xs) if xs.is_empty() => { self.emit(I::Nil); },
|
||||
Expr::Block(xs) => {
|
||||
self.begin_scope();
|
||||
for x in &xs[0..xs.len()-1] {
|
||||
self.expr(x)?;
|
||||
self.expr(x);
|
||||
self.emit_discard(1);
|
||||
}
|
||||
self.expr(&xs[xs.len()-1])?;
|
||||
self.expr(&xs[xs.len()-1]);
|
||||
self.end_scope();
|
||||
},
|
||||
Expr::Literal(v) => self.expr_literal(v),
|
||||
Expr::Ident(ident) => self.load_var(ident),
|
||||
Expr::UnaryOp(o, a) => {
|
||||
self.expr(a)?;
|
||||
self.expr(a);
|
||||
self.emit(I::UnaryOp(*o));
|
||||
},
|
||||
Expr::BinaryOp(o, a, b) => {
|
||||
self.expr(a)?;
|
||||
self.expr(b)?;
|
||||
self.expr(a);
|
||||
self.expr(b);
|
||||
self.emit(I::BinaryOp(*o));
|
||||
},
|
||||
Expr::Assign(o, lv, a) => self.expr_assign(*o, lv, a)?,
|
||||
Expr::Assign(o, lv, a) => self.expr_assign(*o, lv, a),
|
||||
Expr::AssignVar(name, a) => {
|
||||
self.expr(a)?;
|
||||
self.expr(a);
|
||||
self.emit(I::Dup);
|
||||
self.declare_local(name);
|
||||
},
|
||||
Expr::AssignGlobal(name, a) => {
|
||||
self.expr(a)?;
|
||||
self.expr(a);
|
||||
self.emit(I::Dup);
|
||||
self.store_global(name);
|
||||
},
|
||||
Expr::List(xs) if xs.is_empty() => {
|
||||
self.emit(I::NewList(0));
|
||||
},
|
||||
Expr::List(xs) => {
|
||||
let mut first = true;
|
||||
for chunk in xs.chunks(16) {
|
||||
for e in chunk {
|
||||
self.expr(e)?;
|
||||
self.expr(e);
|
||||
}
|
||||
if first {
|
||||
self.emit(I::NewList(chunk.len() as u8));
|
||||
|
@ -307,12 +309,15 @@ impl<'a> Compiler<'a> {
|
|||
}
|
||||
}
|
||||
},
|
||||
Expr::Table(xs) if xs.is_empty() => {
|
||||
self.emit(I::NewTable(0));
|
||||
},
|
||||
Expr::Table(xs) => {
|
||||
let mut first = true;
|
||||
for chunk in xs.chunks(8) {
|
||||
for (k, v) in chunk {
|
||||
self.expr(k)?;
|
||||
self.expr(v)?;
|
||||
self.expr(k);
|
||||
self.expr(v);
|
||||
}
|
||||
if first {
|
||||
self.emit(I::NewTable(chunk.len() as u8));
|
||||
|
@ -323,47 +328,48 @@ impl<'a> Compiler<'a> {
|
|||
}
|
||||
},
|
||||
Expr::Index(ct, idx) => {
|
||||
self.expr(ct)?;
|
||||
self.expr(idx)?;
|
||||
self.expr(ct);
|
||||
self.expr(idx);
|
||||
self.emit(I::Index);
|
||||
},
|
||||
Expr::FnCall(f, args) => {
|
||||
self.expr(f)?;
|
||||
self.expr(f);
|
||||
for a in args {
|
||||
self.expr(a)?;
|
||||
self.expr(a);
|
||||
}
|
||||
self.emit(I::Call(args.len() as u8));
|
||||
},
|
||||
Expr::Pipe(a, f) => {
|
||||
self.expr(a)?;
|
||||
self.expr(f)?;
|
||||
self.expr(a);
|
||||
self.expr(f);
|
||||
self.emit(I::Swap);
|
||||
self.emit(I::Call(1));
|
||||
},
|
||||
Expr::Lambda(args, body) => self.expr_lambda(args, body),
|
||||
Expr::And(a, b) => {
|
||||
self.expr(a)?;
|
||||
self.expr(a);
|
||||
self.emit(I::Dup);
|
||||
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
|
||||
self.emit_discard(1);
|
||||
self.expr(b)?;
|
||||
self.expr(b);
|
||||
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
|
||||
},
|
||||
Expr::Or(a, b) => {
|
||||
self.expr(a)?;
|
||||
self.expr(a);
|
||||
self.emit(I::Dup);
|
||||
let j1 = self.emit(I::JumpTrue(Arg24::from_usize(0)));
|
||||
self.emit_discard(1);
|
||||
self.expr(b)?;
|
||||
self.expr(b);
|
||||
self.update_instr(j1, I::JumpTrue(Arg24::from_usize(self.ip())));
|
||||
},
|
||||
Expr::If(cond, b1, b2) => {
|
||||
self.expr(cond)?;
|
||||
self.expr(cond);
|
||||
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
|
||||
self.expr(b1)?;
|
||||
self.expr(b1);
|
||||
let j2 = self.emit(I::Jump(Arg24::from_usize(0)));
|
||||
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
|
||||
if let Some(b2) = b2 {
|
||||
self.expr(b2)?;
|
||||
self.expr(b2);
|
||||
} else {
|
||||
self.emit(I::Nil);
|
||||
}
|
||||
|
@ -373,19 +379,63 @@ impl<'a> Compiler<'a> {
|
|||
},
|
||||
Expr::While(cond, body) => {
|
||||
let start = self.ip();
|
||||
self.expr(cond)?;
|
||||
self.expr(cond);
|
||||
|
||||
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
|
||||
self.expr(body)?;
|
||||
self.expr(body);
|
||||
self.emit_discard(1);
|
||||
self.emit(I::Jump(Arg24::from_usize(start)));
|
||||
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
|
||||
|
||||
self.emit(I::Nil);
|
||||
},
|
||||
Expr::For(name, iter, body) => {
|
||||
Expr::For(name, iter, body) => self.expr_for(name, iter, body),
|
||||
Expr::Try(body, catches) => self.expr_try(body, catches),
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_try(&mut self, body: &Expr, catch_blocks: &[CatchBlock]) {
|
||||
let (idx, mut table) = self.func.chunk.begin_try_table(self.locals.len());
|
||||
|
||||
self.emit(I::BeginTry(Arg24::from_usize(idx)));
|
||||
self.expr(body);
|
||||
self.emit(I::EndTry);
|
||||
let body_end_addr = self.emit(I::Jump(Arg24::from_usize(0)));
|
||||
let mut catch_end_addrs = Vec::new();
|
||||
|
||||
for catch_block in catch_blocks {
|
||||
table.catches.push(Catch {
|
||||
addr: self.ip(),
|
||||
types: catch_block.types.clone(),
|
||||
});
|
||||
|
||||
self.begin_scope();
|
||||
|
||||
if let Some(name) = catch_block.name {
|
||||
self.declare_local(name);
|
||||
} else {
|
||||
self.emit_discard(1);
|
||||
}
|
||||
|
||||
self.expr(&catch_block.body);
|
||||
self.end_scope();
|
||||
|
||||
let end_addr = self.emit(I::Jump(Arg24::from_usize(0)));
|
||||
catch_end_addrs.push(end_addr);
|
||||
}
|
||||
|
||||
let ip = Arg24::from_usize(self.ip());
|
||||
self.update_instr(body_end_addr, I::Jump(ip));
|
||||
for addr in catch_end_addrs {
|
||||
self.update_instr(addr, I::Jump(ip));
|
||||
}
|
||||
|
||||
self.func.chunk.finish_catch_table(idx, table);
|
||||
}
|
||||
|
||||
fn expr_for(&mut self, name: &str, iter: &Expr, body: &Expr) {
|
||||
// load iterable and convert to iterator
|
||||
self.expr(iter)?;
|
||||
self.expr(iter);
|
||||
self.emit(I::IterBegin);
|
||||
|
||||
// declare loop variable
|
||||
|
@ -403,7 +453,7 @@ impl<'a> Compiler<'a> {
|
|||
self.store_local(local);
|
||||
|
||||
// body
|
||||
self.expr(body)?;
|
||||
self.expr(body);
|
||||
self.emit_discard(1);
|
||||
|
||||
// end loop
|
||||
|
@ -412,24 +462,19 @@ impl<'a> Compiler<'a> {
|
|||
self.update_instr(j1, I::IterTest(Arg24::from_usize(self.ip())));
|
||||
self.end_scope();
|
||||
self.emit(I::Nil);
|
||||
},
|
||||
Expr::Lambda(args, body) => self.expr_lambda(args, body)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expr_lambda(&mut self, args: &[&str], body: &Expr) -> Result<()> {
|
||||
fn expr_lambda(&mut self, args: &[&str], body: &Expr) {
|
||||
let func = Function {
|
||||
arity: args.len(),
|
||||
chunk: Chunk::new()
|
||||
};
|
||||
let mut inner = self.new_function(func, args);
|
||||
inner.parent = Some(self);
|
||||
inner.expr(body)?;
|
||||
inner.expr(body);
|
||||
let func = inner.finish();
|
||||
let n = self.add_const(Value::Function(Rc::new(func)));
|
||||
self.emit(I::Const(Arg24::from_usize(n)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expr_literal(&mut self, val: &Value) {
|
||||
|
@ -449,37 +494,36 @@ impl<'a> Compiler<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) -> Result<()> {
|
||||
fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) {
|
||||
match (lv, o) {
|
||||
(LValue::Ident(i), None) => {
|
||||
self.expr(a)?;
|
||||
self.expr(a);
|
||||
self.emit(I::Dup);
|
||||
self.store_default(i);
|
||||
},
|
||||
(LValue::Ident(i), Some(o)) => {
|
||||
self.load_var(i);
|
||||
self.expr(a)?;
|
||||
self.expr(a);
|
||||
self.emit(I::BinaryOp(o));
|
||||
self.emit(I::Dup);
|
||||
self.store_default(i);
|
||||
},
|
||||
(LValue::Index(ct, i), None) => {
|
||||
self.expr(ct)?;
|
||||
self.expr(i)?;
|
||||
self.expr(a)?;
|
||||
self.expr(ct);
|
||||
self.expr(i);
|
||||
self.expr(a);
|
||||
self.emit(I::StoreIndex);
|
||||
},
|
||||
(LValue::Index(ct, i), Some(o)) => {
|
||||
self.expr(ct)?;
|
||||
self.expr(i)?;
|
||||
self.expr(ct);
|
||||
self.expr(i);
|
||||
self.emit(I::DupTwo);
|
||||
self.emit(I::Index);
|
||||
self.expr(a)?;
|
||||
self.expr(a);
|
||||
self.emit(I::BinaryOp(o));
|
||||
self.emit(I::StoreIndex);
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#[allow(clippy::just_underscores_and_digits)]
|
||||
#[allow(clippy::pedantic)]
|
||||
mod parser {
|
||||
pub use __intern_token::new_builder as new_builder;
|
||||
include!(concat!(env!("OUT_DIR"),"/parser.rs"));
|
||||
}
|
||||
mod parser_util;
|
||||
|
@ -21,3 +22,28 @@ pub mod compiler;
|
|||
pub use parser::BlockParser as Parser;
|
||||
pub use vm::Vm;
|
||||
pub use symbol::Symbol;
|
||||
|
||||
|
||||
type LexResult<'input> = Result<
|
||||
(usize, Token<'input>, usize),
|
||||
ParseError<usize, Token<'input>, parser_util::ParseError>
|
||||
>;
|
||||
|
||||
use lalrpop_util::{ParseError, lexer::{Token, MatcherBuilder}};
|
||||
|
||||
pub struct Lexer {
|
||||
builder: MatcherBuilder,
|
||||
}
|
||||
|
||||
impl Default for Lexer {
|
||||
fn default() -> Self { Self::new() }
|
||||
}
|
||||
|
||||
impl Lexer {
|
||||
pub fn new() -> Self {
|
||||
Self { builder: crate::parser::new_builder() }
|
||||
}
|
||||
pub fn lex<'s>(&'s self, input: &'s str) -> impl Iterator<Item=LexResult<'s>> {
|
||||
self.builder.matcher::<parser_util::ParseError>(input)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::rc::Rc;
|
||||
use crate::ast::*;
|
||||
use crate::value::*;
|
||||
use crate::value::Value;
|
||||
use crate::symbol::Symbol;
|
||||
use crate::parser_util::*;
|
||||
use num_complex::Complex64;
|
||||
|
@ -9,6 +9,7 @@ grammar;
|
|||
|
||||
extern {
|
||||
type Error = ParseError;
|
||||
|
||||
}
|
||||
|
||||
match {
|
||||
|
@ -45,6 +46,8 @@ match {
|
|||
"in",
|
||||
"continue",
|
||||
"break",
|
||||
"try",
|
||||
"catch",
|
||||
} else {
|
||||
// identifiers
|
||||
r"[a-zA-Z_][a-zA-Z0-9_]*" => TokIdentifier,
|
||||
|
@ -56,7 +59,7 @@ match {
|
|||
r"0s[0-5][0-5]*" => TokSexInteger,
|
||||
r"0b[01][01_]*" => TokBinInteger,
|
||||
|
||||
r"\d[0-9_]*([eE][-+]?[0-9_]*\d[0-9_]*i?|i)|(\d[0-9_]*)?\.[0-9_]+([eE]_*[-+]?[0-9_]*\d[0-9_]*)?i?" => TokFloat,
|
||||
r"[0-9][0-9_]*([eE][-+]?[0-9_]*[0-9][0-9_]*i?|i)|([0-9][0-9_]*)?\.[0-9_]+([eE]_*[-+]?[0-9_]*[0-9][0-9_]*)?i?" => TokFloat,
|
||||
|
||||
r#""([^\\"]|\\.)*""# => TokStringDouble,
|
||||
r#"'[^']*'"# => TokStringSingle,
|
||||
|
@ -272,10 +275,18 @@ TermNotIdent: Box<Expr<'input>> = {
|
|||
"$" => Box::new(Expr::Ident("$")),
|
||||
":" "(" <e:Block> ")" => Box::new(Expr::Lambda(vec!["$"], e)),
|
||||
"\\" <xs:IdentList> "->" <e:Term> => Box::new(Expr::Lambda(xs, e)),
|
||||
|
||||
"do" <Block> "end" => <>,
|
||||
"if" <IfStmtChain> => <>,
|
||||
"while" <a:Expr> "do" <b:Block> "end" => Box::new(Expr::While(a, b)),
|
||||
"for" <v:Identifier> "in" <a:Expr> "do" <b:Block> "end" => Box::new(Expr::For(v, a, b)),
|
||||
"while" <a:Expr> "do" <b:Block> "end"
|
||||
=> Box::new(Expr::While(a, b)),
|
||||
"for" <v:Identifier> "in" <a:Expr> "do" <b:Block> "end"
|
||||
=> Box::new(Expr::For(v, a, b)),
|
||||
"try" <b:Block> <mut ch:CatchChain> => {
|
||||
ch.reverse();
|
||||
Box::new(Expr::Try(b, ch))
|
||||
},
|
||||
|
||||
Literal => Box::new(Expr::Literal(<>)),
|
||||
}
|
||||
|
||||
|
@ -285,6 +296,26 @@ IfStmtChain: Box<Expr<'input>> = {
|
|||
<a:Expr> "then" <b:Block> "elif" <c:IfStmtChain> => Box::new(Expr::If(a, b, Some(c))),
|
||||
}
|
||||
|
||||
CatchChain: Vec<CatchBlock<'input>> = {
|
||||
"catch" <types:SymbolList> <name:("in" <Identifier>)?> "do" <b:Block> <mut ch:CatchChain> => {
|
||||
|
||||
ch.push(CatchBlock { name, types: Some(types), body: *b });
|
||||
ch
|
||||
},
|
||||
"catch" "*" <name:("in" <Identifier>)?> "do" <b:Block> <mut ch:CatchChain> => {
|
||||
ch.push(CatchBlock { name, types: None, body: *b });
|
||||
ch
|
||||
},
|
||||
"end" => Vec::new(),
|
||||
}
|
||||
|
||||
SymbolList: Vec<Symbol> = {
|
||||
<mut xs:(<SymbolLiteral> ",")*> <x:SymbolLiteral?> => {
|
||||
if let Some(x) = x { xs.push(x) };
|
||||
xs
|
||||
},
|
||||
}
|
||||
|
||||
ExprList: Vec<Expr<'input>> = {
|
||||
<xs:(<Expr> ",")*> <x:Expr?> => {
|
||||
let mut xs: Vec<_> = xs.into_iter().map(|x| *x).collect();
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::ops::{Add, Sub, Mul, Div, Neg};
|
||||
use std::cmp::Ordering;
|
||||
use std::rc::Rc;
|
||||
use std::hash::Hash;
|
||||
use anyhow::bail;
|
||||
use num_rational::Rational64;
|
||||
use num_complex::Complex64;
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
use crate::chunk::Chunk;
|
||||
use crate::symbol::Symbol;
|
||||
|
@ -15,12 +14,130 @@ use crate::symbol::Symbol;
|
|||
type RcList = Rc<RefCell<Vec<Value>>>;
|
||||
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Exception {
|
||||
pub ty: Symbol,
|
||||
pub msg: Rc<str>,
|
||||
pub data: Option<Value>,
|
||||
}
|
||||
|
||||
impl Exception {
|
||||
pub fn new(ty: Symbol, msg: Rc<str>) -> Self {
|
||||
Self { ty, msg, data: None }
|
||||
}
|
||||
|
||||
pub fn from_table(table: RcTable) -> Option<Self> {
|
||||
let table = table.borrow();
|
||||
let ty = table.get(&Symbol::get("type").into())?;
|
||||
let msg = table.get(&Symbol::get("msg").into())?;
|
||||
if let (Value::Symbol(ty), Value::String(msg)) = (ty, msg) {
|
||||
let data = table.get(&Symbol::get("data").into());
|
||||
Some(Self {
|
||||
ty: *ty,
|
||||
msg: msg.clone(),
|
||||
data: data.cloned(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_table(self) -> RcTable {
|
||||
let mut table = HashMap::new();
|
||||
table.insert(Symbol::get("type").into(), self.ty.into());
|
||||
table.insert(Symbol::get("msg").into(), Value::String(self.msg));
|
||||
if let Some(data) = self.data {
|
||||
table.insert(Symbol::get("data").into(), data);
|
||||
}
|
||||
Rc::new(RefCell::new(table))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Exception {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}: {}", self.ty.name(), self.msg)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Exception>;
|
||||
|
||||
macro_rules! exception {
|
||||
($exc_ty:ident, $fstr:literal, $($arg:expr),*) => {{
|
||||
$crate::value::Exception::new(
|
||||
$crate::Symbol::get(stringify!($exc_ty)),
|
||||
format!($fstr, $($arg),*).into()
|
||||
)
|
||||
}};
|
||||
($exc_ty:ident, $fstr:literal) => {{
|
||||
$crate::value::exception!($exc_ty, $fstr,)
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) use exception;
|
||||
|
||||
macro_rules! throw {
|
||||
($($args:tt)*) => {
|
||||
return Err($crate::value::exception!($($args)*))
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use throw;
|
||||
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Function {
|
||||
pub arity: usize,
|
||||
pub chunk: Chunk,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn disasm_recursive(f: Rc<Function>, w: &mut impl std::io::Write) -> std::io::Result<()> {
|
||||
writeln!(w, "{} (argc={})", Value::Function(f.clone()), f.arity)?;
|
||||
if !f.chunk.consts.is_empty() {
|
||||
writeln!(w, "constants")?;
|
||||
for (i, c) in f.chunk.consts.iter().enumerate() {
|
||||
writeln!(w, " {i:04}: {c}")?;
|
||||
}
|
||||
}
|
||||
if !f.chunk.try_tables.is_empty() {
|
||||
writeln!(w, "catch tables")?;
|
||||
for (i, n) in f.chunk.try_tables.iter().enumerate() {
|
||||
write!(w, " {i:04}: ")?;
|
||||
if n.catches.is_empty() {
|
||||
writeln!(w)?;
|
||||
}
|
||||
for (i, catch) in n.catches.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(w, " : ")?;
|
||||
}
|
||||
if let Some(types) = &catch.types {
|
||||
write!(w, "{:04} [", catch.addr)?;
|
||||
for (i, ty) in types.iter().enumerate() {
|
||||
if i != 0 { write!(w, ", ")?; }
|
||||
write!(w, "{}", ty.name())?;
|
||||
}
|
||||
writeln!(w, "]")?;
|
||||
} else {
|
||||
writeln!(w, "{:04} *", catch.addr)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
writeln!(w, "instructions")?;
|
||||
for (i, n) in f.chunk.instrs.iter().enumerate() {
|
||||
writeln!(w, " {i:04}: {n}")?;
|
||||
}
|
||||
writeln!(w)?;
|
||||
|
||||
for c in &f.chunk.consts {
|
||||
if let Value::Function(f) = c {
|
||||
Function::disasm_recursive(f.clone(), w)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NativeFunc {
|
||||
pub arity: usize,
|
||||
pub f: Box<dyn Fn(Value, Vec<Value>) -> Result<Value>>,
|
||||
|
@ -40,7 +157,7 @@ pub struct HashValue(Value);
|
|||
impl Eq for HashValue {}
|
||||
|
||||
impl TryFrom<Value> for HashValue {
|
||||
type Error = anyhow::Error;
|
||||
type Error = Exception;
|
||||
fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
|
||||
match value {
|
||||
Value::Nil
|
||||
|
@ -49,7 +166,7 @@ impl TryFrom<Value> for HashValue {
|
|||
| Value::Int(_)
|
||||
| Value::Ratio(_)
|
||||
| Value::String(_) => Ok(Self(value)),
|
||||
_ => bail!("value {} cannot be hashed", value),
|
||||
_ => throw!(hash, "value {value:#} cannot be hashed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +251,14 @@ pub enum Value {
|
|||
NativeFunc(Rc<NativeFunc>),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Value {
|
||||
impl From<Symbol> for Value {
|
||||
fn from(value: Symbol) -> Self { Self::Symbol(value) }
|
||||
}
|
||||
impl From<Symbol> for HashValue {
|
||||
fn from(value: Symbol) -> Self { Self(value.into()) }
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Value::Nil => write!(f, "nil"),
|
||||
|
@ -150,15 +274,25 @@ impl std::fmt::Display for Value {
|
|||
Value::Ratio(r) => write!(f, "{}/{}", r.numer(), r.denom()),
|
||||
Value::Complex(z) => write!(f, "{z}"),
|
||||
|
||||
Value::String(s) => write!(f, "{s}"),
|
||||
Value::String(s) => {
|
||||
if f.alternate() {
|
||||
write!(f, "{s:?}")
|
||||
} else {
|
||||
write!(f, "{s}")
|
||||
}
|
||||
},
|
||||
Value::List(l) => {
|
||||
write!(f, "[")?;
|
||||
for (i, item) in l.borrow().iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
if f.alternate() {
|
||||
write!(f, "{item:#}")?;
|
||||
} else {
|
||||
write!(f, "{item}")?;
|
||||
}
|
||||
}
|
||||
write!(f, "]")
|
||||
},
|
||||
Value::Table(t) => {
|
||||
|
@ -167,12 +301,18 @@ impl std::fmt::Display for Value {
|
|||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
if f.alternate() {
|
||||
write!(f, "({:#}) = {v:#}", k.0)?;
|
||||
} else {
|
||||
write!(f, "({}) = {v}", k.0)?;
|
||||
}
|
||||
}
|
||||
write!(f, " }}")
|
||||
},
|
||||
Value::Function(_) => write!(f, "<function>"),
|
||||
Value::NativeFunc(_) => write!(f, "<native function>"),
|
||||
Value::Function(g)
|
||||
=> write!(f, "<function {:?}>", Rc::as_ptr(g)),
|
||||
Value::NativeFunc(g)
|
||||
=> write!(f, "<native function {:?}>", Rc::as_ptr(g)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -231,7 +371,7 @@ impl Neg for Value {
|
|||
V::Ratio(x) => Ok(V::Ratio(-x)),
|
||||
V::Float(x) => Ok(V::Float(-x)),
|
||||
V::Complex(x) => Ok(V::Complex(-x)),
|
||||
a => Err(anyhow!("cannot negate {a}"))
|
||||
a => throw!(type_error, "cannot negate {a:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,7 +385,7 @@ impl Add<Value> for Value {
|
|||
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x + y)),
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x + y)),
|
||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x + y)),
|
||||
(l, r) => Err(anyhow!("cannot add {l:?} and {r:?}"))
|
||||
(l, r) => throw!(type_error, "cannot add {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -259,7 +399,7 @@ impl Sub<Value> for Value {
|
|||
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x - y)),
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x - y)),
|
||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x - y)),
|
||||
(l, r) => Err(anyhow!("cannot subtract {l:?} and {r:?}"))
|
||||
(l, r) => throw!(type_error, "cannot subtract {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -273,7 +413,7 @@ impl Mul<Value> for Value {
|
|||
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x * y)),
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x * y)),
|
||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x * y)),
|
||||
(l, r) => Err(anyhow!("cannot multiply {l:?} and {r:?}"))
|
||||
(l, r) => throw!(type_error, "cannot multiply {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -287,7 +427,7 @@ impl Div<Value> for Value {
|
|||
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x / y)),
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
|
||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
|
||||
(l, r) => Err(anyhow!("cannot divide {l:?} and {r:?}"))
|
||||
(l, r) => throw!(type_error, "cannot divide {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -323,7 +463,7 @@ impl Value {
|
|||
(V::Ratio(_x), V::Ratio(_y)) => todo!("ratio modulo"),
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(y))),
|
||||
(V::Complex(_x), V::Complex(_y)) => todo!("complex modulo"),
|
||||
(l, r) => Err(anyhow!("cannot modulo {l:?} and {r:?}"))
|
||||
(l, r) => throw!(type_error, "cannot modulo {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -334,7 +474,7 @@ impl Value {
|
|||
(V::Ratio(_x), V::Ratio(_y)) => todo!("ratio integer division"),
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(y))),
|
||||
(V::Complex(_x), V::Complex(_y)) => todo!("complex integer division"),
|
||||
(l, r) => Err(anyhow!("cannot integer divide {l:?} and {r:?}"))
|
||||
(l, r) => throw!(type_error, "cannot integer divide {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,7 +492,7 @@ impl Value {
|
|||
=> Ok(V::Float(ratio_to_f64(x).powf(ratio_to_f64(y)))),
|
||||
(V::Complex(x), V::Complex(y))
|
||||
=> Ok(V::Complex(x.powc(y))),
|
||||
(l, r) => Err(anyhow!("cannot exponentiate {l:?} and {r:?}"))
|
||||
(l, r) => throw!(type_error, "cannot exponentiate {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -422,10 +562,10 @@ impl PartialOrd for Value {
|
|||
}
|
||||
|
||||
impl Value {
|
||||
pub fn val_cmp(&self, other: &Self) -> anyhow::Result<Ordering> {
|
||||
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
|
||||
match self.partial_cmp(other) {
|
||||
Some(o) => Ok(o),
|
||||
None => Err(anyhow!("cannot compare {self:?} and {other:?}")),
|
||||
None => throw!(type_error, "cannot compare {self:#} and {other:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -439,7 +579,7 @@ impl Value {
|
|||
if i >= 0 && (i as usize) < l.len() {
|
||||
Ok(l[i as usize].clone())
|
||||
} else {
|
||||
Err(anyhow!("index {i} out of bounds for list of length {}", l.len()))
|
||||
throw!(index_error, "index {i} out of bounds for list of length {}", l.len())
|
||||
}
|
||||
},
|
||||
(V::Range(r), V::Int(i)) => {
|
||||
|
@ -450,7 +590,7 @@ impl Value {
|
|||
) {
|
||||
Ok(Value::Int(r.start + i))
|
||||
} else {
|
||||
Err(anyhow!("index {i} out of bounds for range {}", self))
|
||||
throw!(index_error, "index {i} out of bounds for range {self}")
|
||||
}
|
||||
},
|
||||
(V::Table(t), i) => {
|
||||
|
@ -458,7 +598,7 @@ impl Value {
|
|||
let i = i.try_into()?;
|
||||
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
|
||||
},
|
||||
(lhs, rhs) => Err(anyhow!("cannot assign to index {lhs} with {rhs}"))
|
||||
(col, idx) => throw!(type_error, "cannot index {col:#} with {idx:#}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -471,7 +611,7 @@ impl Value {
|
|||
l[i as usize] = val;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!("index {i} out of bounds for list of length {}", l.len()))
|
||||
throw!(index_error, "index {i} out of bounds for list of length {}", l.len())
|
||||
}
|
||||
},
|
||||
(V::Table(t), i) => {
|
||||
|
@ -480,7 +620,7 @@ impl Value {
|
|||
t.insert(i, val);
|
||||
Ok(())
|
||||
},
|
||||
(l, r) => Err(anyhow!("cannot index {l:?} with {r:?}"))
|
||||
(col, idx) => throw!(index_error, "cannot index {col:#} with {idx:#}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -497,7 +637,7 @@ impl Value {
|
|||
s.push_str(s2);
|
||||
Ok(V::String(s.into()))
|
||||
}
|
||||
(l, r) => Err(anyhow!("cannot concatenate {l:?} and {r:?}"))
|
||||
(l, r) => throw!(type_error, "cannot concatenate {l:#} and {r:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -506,7 +646,7 @@ impl Value {
|
|||
let ty = if closed { RangeType::Closed } else { RangeType::Open };
|
||||
Ok(Value::Range(Range { start: *start, stop: *stop, ty }))
|
||||
} else {
|
||||
bail!("cannot create range between {self} and {other}")
|
||||
throw!(type_error, "cannot create range between {self:#} and {other:#}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,7 +654,7 @@ impl Value {
|
|||
if let Value::Int(start) = self {
|
||||
Ok(Value::Range(Range { start: *start, stop: 0, ty: RangeType::Endless }))
|
||||
} else {
|
||||
bail!("cannot create endless range from {self}")
|
||||
throw!(type_error, "cannot create endless range from {self:#}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -566,7 +706,7 @@ impl Value {
|
|||
f: Box::new(f),
|
||||
})))
|
||||
},
|
||||
_ => bail!("cannot iterate {self}"),
|
||||
_ => throw!(type_error, "cannot iterate {self:#}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,36 @@
|
|||
use std::{rc::Rc, cmp::Ordering, cell::RefCell, collections::HashMap};
|
||||
|
||||
use crate::{chunk::Instruction, value::{Value, Function}, ast::{BinaryOp, UnaryOp}, symbol::Symbol};
|
||||
use crate::{chunk::Instruction, value::{Value, Function, Result, throw, Exception}, ast::{BinaryOp, UnaryOp}, symbol::Symbol};
|
||||
|
||||
use anyhow::{Result, anyhow, bail};
|
||||
struct TryFrame { idx: usize, stack_len: usize }
|
||||
|
||||
#[derive(Default)]
|
||||
struct CallFrame {
|
||||
locals: Vec<Value>,
|
||||
func: Rc<Function>,
|
||||
locals: Vec<Value>,
|
||||
try_frames: Vec<TryFrame>,
|
||||
ip: usize,
|
||||
root: bool,
|
||||
}
|
||||
|
||||
|
||||
impl CallFrame {
|
||||
fn new(func: Rc<Function>, locals: Vec<Value>) -> Self {
|
||||
Self {
|
||||
func,
|
||||
locals,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn new_root(func: Rc<Function>) -> Self {
|
||||
Self {
|
||||
func: func.clone(),
|
||||
locals: vec![Value::Function(func)],
|
||||
root: true,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Vm {
|
||||
|
@ -87,21 +110,8 @@ impl Vm {
|
|||
println!("({:04x}) {:04}: {instr}", framecode, frame.ip);
|
||||
}
|
||||
|
||||
pub fn run(&mut self, func: Rc<Function>) -> Result<Value> {
|
||||
fn run_instr(&mut self, frame: &mut CallFrame, instr: Instruction) -> Result<Option<Value>> {
|
||||
use Instruction as I;
|
||||
|
||||
assert!(func.arity == 0, "root function must not take arguments");
|
||||
let mut frame = CallFrame {
|
||||
func: func.clone(),
|
||||
locals: Vec::with_capacity(16),
|
||||
ip: 0,
|
||||
};
|
||||
frame.locals.push(Value::Function(func));
|
||||
|
||||
loop {
|
||||
let instr = frame.func.chunk.instrs[frame.ip];
|
||||
self.debug_instr(&frame, instr);
|
||||
frame.ip += 1;
|
||||
match instr {
|
||||
I::Nop => (),
|
||||
I::LoadLocal(n)
|
||||
|
@ -114,8 +124,10 @@ impl Vm {
|
|||
=> frame.locals.truncate(frame.locals.len() - usize::from(n)),
|
||||
I::LoadGlobal(s) => {
|
||||
let sym = unsafe { s.to_symbol_unchecked() };
|
||||
let v = self.globals.get(&sym)
|
||||
.ok_or_else(|| anyhow!("global not defined"))?.clone();
|
||||
let v = match self.globals.get(&sym) {
|
||||
Some(v) => v.clone(),
|
||||
None => throw!(name_error, "undefined global {}", sym.name()),
|
||||
};
|
||||
self.push(v);
|
||||
},
|
||||
I::StoreGlobal(s) => {
|
||||
|
@ -166,8 +178,8 @@ impl Vm {
|
|||
let mut table = HashMap::new();
|
||||
for _ in 0..n {
|
||||
let v = self.pop();
|
||||
let k = self.pop().try_into()?;
|
||||
table.insert(k, v);
|
||||
let k = self.pop();
|
||||
table.insert(k.try_into()?, v);
|
||||
}
|
||||
self.push(Value::Table(Rc::new(RefCell::new(table))));
|
||||
},
|
||||
|
@ -175,12 +187,14 @@ impl Vm {
|
|||
let mut ext = self.pop_n(2 * n as usize);
|
||||
let table = self.pop();
|
||||
let Value::Table(table) = table else { panic!("not a table") };
|
||||
let mut table_ref = table.borrow_mut();
|
||||
for _ in 0..n {
|
||||
// can't panic:
|
||||
// can't panic: pop_n checked that ext would have len 2*n
|
||||
let v = ext.pop().unwrap();
|
||||
let k = ext.pop().unwrap().try_into()?;
|
||||
table.borrow_mut().insert(k, v);
|
||||
let k = ext.pop().unwrap();
|
||||
table_ref.insert(k.try_into()?, v);
|
||||
}
|
||||
drop(table_ref);
|
||||
self.push(Value::Table(table));
|
||||
},
|
||||
I::Index => {
|
||||
|
@ -199,8 +213,8 @@ impl Vm {
|
|||
I::JumpTrue(n) => if self.pop().truthy() { frame.ip = usize::from(n) },
|
||||
I::JumpFalse(n) => if !self.pop().truthy() { frame.ip = usize::from(n) },
|
||||
I::IterBegin => {
|
||||
let v = self.pop().to_iter_function()?;
|
||||
self.push(v);
|
||||
let iter = self.pop().to_iter_function()?;
|
||||
self.push(iter);
|
||||
},
|
||||
I::IterTest(n) => {
|
||||
let v = &self.stack[self.stack.len() - 1];
|
||||
|
@ -210,47 +224,106 @@ impl Vm {
|
|||
frame.ip = usize::from(n);
|
||||
}
|
||||
},
|
||||
I::BeginTry(t) => {
|
||||
let tryframe = TryFrame {
|
||||
idx: usize::from(t),
|
||||
stack_len: self.stack.len()
|
||||
};
|
||||
frame.try_frames.push(tryframe);
|
||||
},
|
||||
I::EndTry => {
|
||||
frame.try_frames.pop().expect("no try to pop");
|
||||
},
|
||||
I::Call(n) => {
|
||||
let n = usize::from(n);
|
||||
let func = &self.stack[self.stack.len() - n - 1];
|
||||
if let Value::NativeFunc(nf) = func {
|
||||
let nf = nf.clone();
|
||||
if nf.arity != n {
|
||||
throw!(type_error, "function call with wrong argument count");
|
||||
}
|
||||
|
||||
let args = self.pop_n(n);
|
||||
let func = self.pop();
|
||||
if nf.arity != n {
|
||||
bail!("function call with wrong argument count");
|
||||
}
|
||||
|
||||
self.call_stack.push(std::mem::take(frame));
|
||||
let res = (nf.f)(func, args)?;
|
||||
*frame = self.call_stack.pop().expect("no frame left on stack");
|
||||
|
||||
self.stack.push(res);
|
||||
} else if let Value::Function(func) = func {
|
||||
if func.arity != n {
|
||||
bail!("function call with wrong argument count");
|
||||
throw!(type_error, "function call with wrong argument count");
|
||||
}
|
||||
|
||||
if self.call_stack.len() + 1 >= self.stack_max {
|
||||
bail!("call stack overflow")
|
||||
throw!(call_stack_overflow, "call stack overflow")
|
||||
}
|
||||
self.call_stack.push(frame);
|
||||
self.call_stack.push(std::mem::take(frame));
|
||||
|
||||
let func = func.clone();
|
||||
let args = self.pop_n(n + 1);
|
||||
frame = CallFrame {
|
||||
func,
|
||||
ip: 0,
|
||||
locals: args,
|
||||
};
|
||||
*frame = CallFrame::new(func, args);
|
||||
} else {
|
||||
bail!("attempt to call non-function {}", func);
|
||||
throw!(type_error, "attempt to call non-function {func}");
|
||||
}
|
||||
},
|
||||
I::Return if self.call_stack.is_empty() => {
|
||||
I::Return if frame.root => {
|
||||
let v = self.pop();
|
||||
self.stack.clear();
|
||||
return Ok(v);
|
||||
return Ok(Some(v));
|
||||
},
|
||||
I::Return => {
|
||||
// can't panic: checked that call_stack wasn't empty
|
||||
frame = self.call_stack.pop().unwrap();
|
||||
*frame = self.call_stack.pop().expect("no root frame");
|
||||
},
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn handle_exception(&mut self, frame: &mut CallFrame, exc: Exception) -> Result<()> {
|
||||
loop {
|
||||
while let Some(try_frame) = frame.try_frames.pop() {
|
||||
let table = &frame.func.chunk.try_tables[try_frame.idx];
|
||||
for catch in &table.catches {
|
||||
if catch.types.is_none() || catch.types.as_ref().unwrap().contains(&exc.ty) {
|
||||
frame.ip = catch.addr;
|
||||
frame.locals.truncate(table.local_count);
|
||||
self.stack.truncate(try_frame.stack_len);
|
||||
self.stack.push(Value::Table(exc.to_table()));
|
||||
return Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
if frame.root {
|
||||
return Err(exc)
|
||||
}
|
||||
*frame = self.call_stack.pop().expect("no root frame");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, func: Rc<Function>) -> Result<Value> {
|
||||
assert!(func.arity == 0, "root function must not take arguments");
|
||||
let init_stack_len = self.stack.len();
|
||||
let mut frame = CallFrame::new_root(func.clone());
|
||||
|
||||
loop {
|
||||
let instr = frame.func.chunk.instrs[frame.ip];
|
||||
self.debug_instr(&frame, instr);
|
||||
frame.ip += 1;
|
||||
match self.run_instr(&mut frame, instr) {
|
||||
Ok(None) => (),
|
||||
Ok(Some(v)) => {
|
||||
self.stack.truncate(init_stack_len);
|
||||
return Ok(v)
|
||||
}
|
||||
Err(e) => {
|
||||
if let Err(e) = self.handle_exception(&mut frame, e) {
|
||||
self.stack.truncate(init_stack_len);
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue