add features
This commit is contained in:
parent
b48c08cb62
commit
66abf928e9
25 changed files with 1052 additions and 495 deletions
112
Cargo.lock
generated
112
Cargo.lock
generated
|
@ -114,6 +114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -128,6 +129,18 @@ dependencies = [
|
|||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.50",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.0"
|
||||
|
@ -155,6 +168,16 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
|
@ -259,6 +282,12 @@ version = "0.14.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.6"
|
||||
|
@ -335,6 +364,12 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
|
@ -659,6 +694,17 @@ version = "0.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.50"
|
||||
|
@ -675,8 +721,10 @@ name = "talc-bin"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"ctrlc",
|
||||
"rustyline",
|
||||
"talc-lang",
|
||||
"talc-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -685,14 +733,28 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"lazy_static",
|
||||
"num-complex",
|
||||
"num-rational",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "talc-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "talc-std"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"talc-lang",
|
||||
"talc-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
|
@ -722,7 +784,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.50",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -798,7 +860,7 @@ version = "0.52.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
"windows-targets 0.52.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -818,17 +880,17 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.0",
|
||||
"windows_aarch64_msvc 0.52.0",
|
||||
"windows_i686_gnu 0.52.0",
|
||||
"windows_i686_msvc 0.52.0",
|
||||
"windows_x86_64_gnu 0.52.0",
|
||||
"windows_x86_64_gnullvm 0.52.0",
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
"windows_aarch64_gnullvm 0.52.3",
|
||||
"windows_aarch64_msvc 0.52.3",
|
||||
"windows_i686_gnu 0.52.3",
|
||||
"windows_i686_msvc 0.52.3",
|
||||
"windows_x86_64_gnu 0.52.3",
|
||||
"windows_x86_64_gnullvm 0.52.3",
|
||||
"windows_x86_64_msvc 0.52.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -839,9 +901,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
|
@ -851,9 +913,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
|
@ -863,9 +925,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
|||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
|
@ -875,9 +937,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
|||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
|
@ -887,9 +949,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
|
@ -899,9 +961,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
|
@ -911,6 +973,6 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
|||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
version = "0.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6"
|
||||
|
|
|
@ -3,5 +3,6 @@ members = [
|
|||
"talc-lang",
|
||||
"talc-bin",
|
||||
"talc-std",
|
||||
"talc-macros",
|
||||
]
|
||||
resolver = "2"
|
||||
|
|
|
@ -5,5 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
talc-lang = { path = "../talc-lang" }
|
||||
talc-std = { path = "../talc-std" }
|
||||
rustyline = "13.0"
|
||||
clap = "4.5"
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
ctrlc = "3.4"
|
||||
|
|
|
@ -1,11 +1,60 @@
|
|||
use clap::Parser;
|
||||
use rustyline::error::ReadlineError;
|
||||
use talc_lang::{Parser, Vm, Symbol, value::{Value, Function}, compiler::repl};
|
||||
use std::{rc::Rc, io::Write};
|
||||
use talc_lang::{compiler::{compile, compile_repl}, value::{function::disasm_recursive, Value}, symbol::Symbol, Vm};
|
||||
use std::{rc::Rc, path::PathBuf, process::ExitCode};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let parser = Parser::new();
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Args {
|
||||
file: Option<PathBuf>,
|
||||
|
||||
#[arg(short, long)]
|
||||
repl: bool,
|
||||
|
||||
#[arg(short, long)]
|
||||
disasm: bool,
|
||||
}
|
||||
|
||||
fn exec(src: String, _args: &Args) -> ExitCode {
|
||||
let parser = talc_lang::Parser::new();
|
||||
let mut vm = Vm::new(256);
|
||||
let mut globals = Vec::new();
|
||||
talc_std::load_all(&mut vm);
|
||||
|
||||
let ex = match parser.parse(&src) {
|
||||
Ok(ex) => ex,
|
||||
Err(e) => {
|
||||
eprintln!("Error: {e}");
|
||||
return ExitCode::FAILURE
|
||||
},
|
||||
};
|
||||
|
||||
let func = Rc::new(compile(&ex));
|
||||
|
||||
if let Err(e) = vm.call_no_args(func.clone()) {
|
||||
eprintln!("{e}");
|
||||
ExitCode::FAILURE
|
||||
} else {
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
fn repl(args: &Args) -> ExitCode {
|
||||
if args.disasm {
|
||||
eprintln!("input disassembly enabled");
|
||||
}
|
||||
|
||||
let parser = talc_lang::Parser::new();
|
||||
let mut compiler_globals = Vec::new();
|
||||
let mut vm = Vm::new(256);
|
||||
talc_std::load_all(&mut vm);
|
||||
|
||||
let interrupt = vm.get_interrupt();
|
||||
let ctrlc_res = ctrlc::set_handler(move || {
|
||||
interrupt.fetch_or(true, std::sync::atomic::Ordering::Relaxed);
|
||||
});
|
||||
if let Err(e) = ctrlc_res {
|
||||
eprintln!("Warn: couldn't set ctrl+c handler: {e}")
|
||||
}
|
||||
|
||||
let prev1_sym = Symbol::get("_");
|
||||
let prev2_sym = Symbol::get("__");
|
||||
|
@ -15,13 +64,24 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
vm.set_global(prev2_sym, Value::Nil);
|
||||
vm.set_global(prev3_sym, Value::Nil);
|
||||
|
||||
let mut rl = rustyline::DefaultEditor::new()?;
|
||||
let mut rl = match rustyline::DefaultEditor::new() {
|
||||
Ok(rl) => rl,
|
||||
Err(ReadlineError::Io(e)) => {
|
||||
eprintln!("Error: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
},
|
||||
Err(ReadlineError::Errno(e)) => {
|
||||
eprintln!("Error: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
},
|
||||
Err(_) => return ExitCode::SUCCESS,
|
||||
};
|
||||
|
||||
loop {
|
||||
let line = rl.readline(">> ");
|
||||
let line = match line {
|
||||
Ok(line) => line,
|
||||
Err(ReadlineError::Eof) => break,
|
||||
Err(ReadlineError::Eof) => return ExitCode::SUCCESS,
|
||||
Err(ReadlineError::Interrupted) => continue,
|
||||
Err(e) => {
|
||||
eprintln!("Error: {e}");
|
||||
|
@ -34,14 +94,18 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
Err(e) => { eprintln!("Error: {e}"); continue },
|
||||
};
|
||||
|
||||
let (f, g) = repl(&ex, &globals);
|
||||
globals = g;
|
||||
let (f, g) = compile_repl(&ex, &compiler_globals);
|
||||
compiler_globals = g;
|
||||
let func = Rc::new(f);
|
||||
|
||||
if args.disasm {
|
||||
if let Err(e) = disasm_recursive(&func, &mut std::io::stderr()) {
|
||||
eprintln!("Error: {e}");
|
||||
return ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
|
||||
Function::disasm_recursive(func.clone(), &mut std::io::stdout())?;
|
||||
std::io::stdout().flush()?;
|
||||
|
||||
match vm.run(func) {
|
||||
match vm.call_no_args(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());
|
||||
|
@ -53,6 +117,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
Err(e) => eprintln!("{e}"),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args = Args::parse();
|
||||
|
||||
if args.repl || args.file.is_none() {
|
||||
return repl(&args)
|
||||
}
|
||||
|
||||
match std::fs::read_to_string(args.file.as_ref().unwrap()) {
|
||||
Ok(s) => exec(s, &args),
|
||||
Err(e) => {
|
||||
eprintln!("Error: {e}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ lalrpop-util = { version = "0.20", features = ["lexer", "unicode"] }
|
|||
num-complex = "0.4"
|
||||
num-rational = { version = "0.4", default-features = false, features = [] }
|
||||
thiserror = "1.0"
|
||||
lazy_static = "1.4"
|
||||
|
||||
[build-dependencies]
|
||||
lalrpop = { version = "0.20", default_features = false, features = ["lexer", "unicode"]}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{value::Value, Symbol};
|
||||
use crate::{value::Value, symbol::Symbol};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum BinaryOp {
|
||||
|
@ -33,6 +33,7 @@ pub enum Expr<'s> {
|
|||
List(Vec<Expr<'s>>),
|
||||
Table(Vec<(Expr<'s>, Expr<'s>)>),
|
||||
|
||||
Return(Box<Expr<'s>>),
|
||||
And(Box<Expr<'s>>, Box<Expr<'s>>),
|
||||
Or(Box<Expr<'s>>, Box<Expr<'s>>),
|
||||
If(Box<Expr<'s>>, Box<Expr<'s>>, Option<Box<Expr<'s>>>),
|
||||
|
|
|
@ -117,7 +117,7 @@ pub enum Instruction {
|
|||
JumpTrue(Arg24),
|
||||
JumpFalse(Arg24),
|
||||
|
||||
IterBegin, IterTest(Arg24),
|
||||
IterBegin, IterNext(Arg24),
|
||||
|
||||
BeginTry(Arg24), EndTry,
|
||||
|
||||
|
@ -159,7 +159,7 @@ impl std::fmt::Display for Instruction {
|
|||
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::IterNext(a) => write!(f, "iternext {}", usize::from(a)),
|
||||
Self::BeginTry(t) => write!(f, "begintry {}", usize::from(t)),
|
||||
Self::EndTry => write!(f, "endtry"),
|
||||
Self::Call(n) => write!(f, "call {n}"),
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::rc::Rc;
|
|||
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::function::Function;
|
||||
use crate::value::Value;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -14,51 +14,75 @@ pub struct Local {
|
|||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum CompilerMode {
|
||||
Function, Repl, // Module,
|
||||
Function, Repl, Module,
|
||||
}
|
||||
|
||||
struct Compiler<'a> {
|
||||
parent: Option<&'a Compiler<'a>>,
|
||||
mode: CompilerMode,
|
||||
parent: Option<&'a Compiler<'a>>,
|
||||
func: Function,
|
||||
scope: usize,
|
||||
locals: Vec<Local>,
|
||||
globals: Vec<Local>,
|
||||
}
|
||||
|
||||
pub fn repl(expr: &Expr, globals: &[Local]) -> (Function, Vec<Local>) {
|
||||
let locals = vec![Local {
|
||||
name: "self".into(),
|
||||
scope: 0,
|
||||
}];
|
||||
let mut comp = Compiler {
|
||||
parent: None,
|
||||
mode: CompilerMode::Repl,
|
||||
func: Function { arity: 0, chunk: Chunk::new() },
|
||||
scope: 0,
|
||||
locals,
|
||||
globals: globals.to_vec(),
|
||||
};
|
||||
pub fn compile(expr: &Expr) -> Function {
|
||||
let mut comp = Compiler::new_module(None);
|
||||
comp.expr(expr);
|
||||
comp.emit(I::Return);
|
||||
(comp.func, comp.globals)
|
||||
comp.finish()
|
||||
}
|
||||
|
||||
pub fn compile_repl(expr: &Expr, globals: &[Local]) -> (Function, Vec<Local>) {
|
||||
let mut comp = Compiler::new_repl(globals);
|
||||
comp.expr(expr);
|
||||
comp.finish_repl()
|
||||
}
|
||||
|
||||
impl<'a> Default for Compiler<'a> {
|
||||
fn default() -> Self {
|
||||
let locals = vec![Local {
|
||||
name: "self".into(),
|
||||
scope: 0,
|
||||
}];
|
||||
Self {
|
||||
mode: CompilerMode::Function,
|
||||
parent: None,
|
||||
func: Function { arity: 0, chunk: Chunk::new() },
|
||||
scope: 0,
|
||||
locals,
|
||||
globals: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Compiler<'a> {
|
||||
fn new_function(&'a self, func: Function, args: &[&str]) -> Self {
|
||||
let mut new = Self {
|
||||
parent: Some(self),
|
||||
mode: CompilerMode::Function,
|
||||
func,
|
||||
scope: 0,
|
||||
locals: Vec::new(),
|
||||
globals: Vec::new(),
|
||||
};
|
||||
fn new_repl(globals: &[Local]) -> Self {
|
||||
Self {
|
||||
mode: CompilerMode::Repl,
|
||||
globals: globals.to_vec(),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
new.locals.push(Local {
|
||||
name: "self".into(),
|
||||
scope: 0,
|
||||
});
|
||||
fn new_module(parent: Option<&'a Self>) -> Self {
|
||||
Self {
|
||||
mode: CompilerMode::Module,
|
||||
parent,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn new_function(&'a self, args: &[&str]) -> Self {
|
||||
let func = Function {
|
||||
arity: args.len(),
|
||||
chunk: Chunk::new(),
|
||||
};
|
||||
let mut new = Self {
|
||||
mode: CompilerMode::Function,
|
||||
parent: Some(self),
|
||||
func,
|
||||
..Self::default()
|
||||
};
|
||||
|
||||
for arg in args {
|
||||
new.locals.push(Local {
|
||||
|
@ -75,6 +99,11 @@ impl<'a> Compiler<'a> {
|
|||
self.func
|
||||
}
|
||||
|
||||
pub fn finish_repl(mut self) -> (Function, Vec<Local>) {
|
||||
self.emit(I::Return);
|
||||
(self.func, self.globals)
|
||||
}
|
||||
|
||||
//
|
||||
// Utility
|
||||
//
|
||||
|
@ -339,6 +368,10 @@ impl<'a> Compiler<'a> {
|
|||
}
|
||||
self.emit(I::Call(args.len() as u8));
|
||||
},
|
||||
Expr::Return(e) => {
|
||||
self.expr(e);
|
||||
self.emit(I::Return);
|
||||
},
|
||||
Expr::Pipe(a, f) => {
|
||||
self.expr(a);
|
||||
self.expr(f);
|
||||
|
@ -447,9 +480,7 @@ impl<'a> Compiler<'a> {
|
|||
let start = self.ip();
|
||||
|
||||
// call iterator and jump if nil, otherwise store
|
||||
self.emit(I::Dup);
|
||||
self.emit(I::Call(0));
|
||||
let j1 = self.emit(I::IterTest(Arg24::from_usize(0)));
|
||||
let j1 = self.emit(I::IterNext(Arg24::from_usize(0)));
|
||||
self.store_local(local);
|
||||
|
||||
// body
|
||||
|
@ -459,21 +490,17 @@ impl<'a> Compiler<'a> {
|
|||
// end loop
|
||||
self.emit(I::Jump(Arg24::from_usize(start)));
|
||||
|
||||
self.update_instr(j1, I::IterTest(Arg24::from_usize(self.ip())));
|
||||
self.update_instr(j1, I::IterNext(Arg24::from_usize(self.ip())));
|
||||
self.end_scope();
|
||||
self.emit(I::Nil);
|
||||
}
|
||||
|
||||
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);
|
||||
let mut inner = self.new_function(args);
|
||||
inner.parent = Some(self);
|
||||
inner.expr(body);
|
||||
let func = inner.finish();
|
||||
let n = self.add_const(Value::Function(Rc::new(func)));
|
||||
let n = self.add_const(func.into());
|
||||
self.emit(I::Const(Arg24::from_usize(n)));
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ mod parser {
|
|||
}
|
||||
mod parser_util;
|
||||
mod vm;
|
||||
mod symbol;
|
||||
pub mod symbol;
|
||||
|
||||
pub mod ast;
|
||||
pub mod value;
|
||||
|
@ -21,8 +21,6 @@ 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),
|
||||
|
|
|
@ -48,6 +48,7 @@ match {
|
|||
"break",
|
||||
"try",
|
||||
"catch",
|
||||
"return",
|
||||
} else {
|
||||
// identifiers
|
||||
r"[a-zA-Z_][a-zA-Z0-9_]*" => TokIdentifier,
|
||||
|
@ -81,7 +82,10 @@ pub Block: Box<Expr<'input>> = {
|
|||
}
|
||||
|
||||
|
||||
Expr: Box<Expr<'input>> = Assign;
|
||||
Expr: Box<Expr<'input>> = {
|
||||
"return" <e:Expr> => Box::new(Expr::Return(e)),
|
||||
Assign,
|
||||
}
|
||||
|
||||
//
|
||||
// assignment
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use std::{sync::{OnceLock, Arc, Mutex}, collections::HashMap};
|
||||
use std::{collections::HashMap, sync::{Arc, Mutex, OnceLock}};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
#[derive(Default)]
|
||||
struct SymbolTable {
|
||||
|
@ -6,7 +8,21 @@ struct SymbolTable {
|
|||
values: HashMap<Arc<str>, Symbol>
|
||||
}
|
||||
|
||||
static SYM_TABLE: OnceLock<Mutex<SymbolTable>> = OnceLock::new();
|
||||
lazy_static! {
|
||||
pub static ref SYM_TYPE: Symbol = symbol!(type);
|
||||
pub static ref SYM_MSG: Symbol = symbol!(msg);
|
||||
pub static ref SYM_DATA: Symbol = symbol!(data);
|
||||
|
||||
pub static ref SYM_TYPE_ERROR: Symbol = symbol!(type_error);
|
||||
pub static ref SYM_NAME_ERROR: Symbol = symbol!(name_error);
|
||||
pub static ref SYM_INDEX_ERROR: Symbol = symbol!(index_error);
|
||||
pub static ref SYM_HASH_ERROR: Symbol = symbol!(hash_error);
|
||||
pub static ref SYM_CALL_STACK_OVERFLOW: Symbol = symbol!(call_stack_overflow);
|
||||
pub static ref SYM_INTERRUPTED: Symbol = symbol!(interrupted);
|
||||
pub static ref SYM_STOP_ITERATOR: Symbol = symbol!(stop_iterator);
|
||||
}
|
||||
|
||||
static TABLE: OnceLock<Mutex<SymbolTable>> = OnceLock::new();
|
||||
|
||||
const MAX_SYMBOL: usize = 0xff_ffff;
|
||||
|
||||
|
@ -14,7 +30,7 @@ const MAX_SYMBOL: usize = 0xff_ffff;
|
|||
pub struct Symbol(u32);
|
||||
|
||||
fn get_table() -> &'static Mutex<SymbolTable> {
|
||||
SYM_TABLE.get_or_init(|| Mutex::new(SymbolTable::default()))
|
||||
TABLE.get_or_init(|| Mutex::new(SymbolTable::default()))
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
|
@ -74,3 +90,15 @@ impl Symbol {
|
|||
table.names.get(self.0 as usize).cloned().expect("symbol does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! symbol {
|
||||
($sym:ident) => {
|
||||
$crate::symbol::Symbol::get(stringify!($sym))
|
||||
};
|
||||
($sym:literal) => {
|
||||
$crate::symbol::Symbol::get($sym)
|
||||
};
|
||||
}
|
||||
|
||||
pub use symbol;
|
||||
|
|
98
talc-lang/src/value/exception.rs
Normal file
98
talc-lang/src/value/exception.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
use std::{rc::Rc, collections::HashMap, cell::RefCell, fmt::Display};
|
||||
use super::{HashValue, RcTable, Value};
|
||||
use crate::{symbol::{SYM_DATA, SYM_MSG, SYM_TYPE}, symbol::Symbol};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Exception>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Exception {
|
||||
pub ty: Symbol,
|
||||
pub msg: Option<Rc<str>>,
|
||||
pub data: Option<Value>,
|
||||
}
|
||||
|
||||
impl Exception {
|
||||
pub fn new(ty: Symbol) -> Self {
|
||||
Self { ty, msg: None, data: None }
|
||||
}
|
||||
|
||||
pub fn new_with_msg(ty: Symbol, msg: Rc<str>) -> Self {
|
||||
Self { ty, msg: Some(msg), data: None }
|
||||
}
|
||||
|
||||
pub fn from_table(table: &Rc<RefCell<HashMap<HashValue, Value>>>) -> Option<Self> {
|
||||
let table = table.borrow();
|
||||
let ty = table.get(&(*SYM_TYPE).into())?;
|
||||
if let Value::Symbol(ty) = ty {
|
||||
let data = table.get(&(*SYM_DATA).into()).cloned();
|
||||
let msg = table.get(&(*SYM_MSG).into());
|
||||
match msg {
|
||||
None => Some(Self {
|
||||
ty: *ty,
|
||||
data,
|
||||
msg: None,
|
||||
}),
|
||||
Some(Value::String(msg)) => Some(Self {
|
||||
ty: *ty,
|
||||
data,
|
||||
msg: Some(msg.clone()),
|
||||
}),
|
||||
Some(_) => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_table(self) -> RcTable {
|
||||
let mut table = HashMap::new();
|
||||
table.insert((*SYM_TYPE).into(), self.ty.into());
|
||||
if let Some(msg) = self.msg {
|
||||
table.insert((*SYM_MSG).into(), Value::String(msg));
|
||||
}
|
||||
if let Some(data) = self.data {
|
||||
table.insert((*SYM_DATA).into(), data);
|
||||
}
|
||||
Rc::new(RefCell::new(table))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Exception {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if let Some(msg) = &self.msg {
|
||||
write!(f, "{}: {}", self.ty.name(), msg)
|
||||
} else {
|
||||
write!(f, "{}", self.ty.name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! exception {
|
||||
($exc_ty:expr, $fstr:literal, $($arg:expr),*) => {
|
||||
$crate::value::exception::Exception::new_with_msg(
|
||||
$exc_ty,
|
||||
format!($fstr, $($arg),*).into()
|
||||
)
|
||||
};
|
||||
($exc_ty:expr, $fstr:literal) => {
|
||||
$crate::value::exception::exception!($exc_ty, $fstr,)
|
||||
};
|
||||
($exc_ty:expr) => {
|
||||
$crate::value::exception::Exception::new($exc_ty)
|
||||
};
|
||||
}
|
||||
|
||||
pub use exception;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! throw {
|
||||
($($args:tt)*) => {
|
||||
return Err($crate::value::exception::exception!($($args)*))
|
||||
};
|
||||
}
|
||||
|
||||
pub use throw;
|
||||
|
||||
|
||||
|
72
talc-lang/src/value/function.rs
Normal file
72
talc-lang/src/value/function.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use crate::{chunk::Chunk, Vm};
|
||||
|
||||
use super::{Value, exception::Result};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Function {
|
||||
pub arity: usize,
|
||||
pub chunk: Chunk,
|
||||
}
|
||||
|
||||
pub struct NativeFunc {
|
||||
pub arity: usize,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub f: Box<dyn Fn(&mut Vm, Vec<Value>) -> Result<Value>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for NativeFunc {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("NativeFunc")
|
||||
.field("arity", &self.arity)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
disasm_recursive(f, w)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
190
talc-lang/src/value/mod.rs
Normal file
190
talc-lang/src/value/mod.rs
Normal file
|
@ -0,0 +1,190 @@
|
|||
use std::{rc::Rc, cell::RefCell, collections::HashMap, hash::Hash, fmt::Display};
|
||||
|
||||
use num_complex::Complex64;
|
||||
use num_rational::Rational64;
|
||||
|
||||
use crate::symbol::{SYM_HASH_ERROR, Symbol};
|
||||
|
||||
use self::{range::{Range, RangeType}, function::{Function, NativeFunc}, exception::{Exception, throw}};
|
||||
|
||||
pub mod exception;
|
||||
pub mod function;
|
||||
pub mod ops;
|
||||
pub mod range;
|
||||
|
||||
type RcList = Rc<RefCell<Vec<Value>>>;
|
||||
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Value {
|
||||
Nil,
|
||||
Bool(bool),
|
||||
Symbol(Symbol),
|
||||
Range(Range),
|
||||
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Ratio(Rational64),
|
||||
Complex(Complex64),
|
||||
|
||||
String(Rc<str>),
|
||||
List(RcList),
|
||||
Table(RcTable),
|
||||
Function(Rc<Function>),
|
||||
NativeFunc(Rc<NativeFunc>),
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Value::Nil => write!(f, "nil"),
|
||||
Value::Bool(b) => write!(f, "{b}"),
|
||||
Value::Symbol(s) => write!(f, ":{}", s.name()),
|
||||
Value::Range(r) => match r.ty {
|
||||
RangeType::Open => write!(f, "{}..{}", r.start, r.stop),
|
||||
RangeType::Closed => write!(f, "{}..={}", r.start, r.stop),
|
||||
RangeType::Endless => write!(f, "{}..*", r.start),
|
||||
},
|
||||
Value::Int(n) => write!(f, "{n}"),
|
||||
Value::Float(x) => write!(f, "{x:?}"),
|
||||
Value::Ratio(r) => write!(f, "{}/{}", r.numer(), r.denom()),
|
||||
Value::Complex(z) => write!(f, "{z}"),
|
||||
|
||||
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) => {
|
||||
write!(f, "{{ ")?;
|
||||
for (i, (k, v)) in t.borrow().iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
if f.alternate() {
|
||||
write!(f, "({:#}) = {v:#}", k.0)?;
|
||||
} else {
|
||||
write!(f, "({}) = {v}", k.0)?;
|
||||
}
|
||||
}
|
||||
write!(f, " }}")
|
||||
},
|
||||
Value::Function(g)
|
||||
=> write!(f, "<function {:?}>", Rc::as_ptr(g)),
|
||||
Value::NativeFunc(g)
|
||||
=> write!(f, "<native function {:?}>", Rc::as_ptr(g)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct HashValue(Value);
|
||||
|
||||
impl Eq for HashValue {}
|
||||
|
||||
impl TryFrom<Value> for HashValue {
|
||||
type Error = Exception;
|
||||
fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
|
||||
match value {
|
||||
Value::Nil
|
||||
| Value::Bool(_)
|
||||
| Value::Symbol(_)
|
||||
| Value::Int(_)
|
||||
| Value::Ratio(_)
|
||||
| Value::String(_) => Ok(Self(value)),
|
||||
_ => throw!(*SYM_HASH_ERROR, "value {value:#} cannot be hashed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HashValue {
|
||||
pub fn into_inner(self) -> Value { self.0 }
|
||||
}
|
||||
|
||||
impl Hash for HashValue {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
std::mem::discriminant(&self.0).hash(state);
|
||||
match &self.0 {
|
||||
Value::Nil => (),
|
||||
Value::Bool(b) => b.hash(state),
|
||||
Value::Symbol(s) => s.hash(state),
|
||||
Value::Int(n) => n.hash(state),
|
||||
Value::Ratio(r) => r.hash(state),
|
||||
Value::String(s) => s.hash(state),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_from {
|
||||
($ty:ty, $var:ident) => {
|
||||
impl From<$ty> for Value {
|
||||
fn from(value: $ty) -> Self { Self::$var(value) }
|
||||
}
|
||||
};
|
||||
($ty:ty, $var:ident, hash) => {
|
||||
impl From<$ty> for Value {
|
||||
fn from(value: $ty) -> Self { Self::$var(value) }
|
||||
}
|
||||
impl From<$ty> for HashValue {
|
||||
fn from(value: $ty) -> Self { Self(Value::$var(value)) }
|
||||
}
|
||||
};
|
||||
($ty:ty, $var:ident, rc) => {
|
||||
impl From<$ty> for Value {
|
||||
fn from(value: $ty) -> Self { Self::$var(Rc::new(value)) }
|
||||
}
|
||||
impl From<Rc<$ty>> for Value {
|
||||
fn from(value: Rc<$ty>) -> Self { Self::$var(value) }
|
||||
}
|
||||
};
|
||||
($ty:ty, $var:ident, rcref) => {
|
||||
impl From<$ty> for Value {
|
||||
fn from(value: $ty) -> Self { Self::$var(Rc::new(RefCell::new(value))) }
|
||||
}
|
||||
impl From<RefCell<$ty>> for Value {
|
||||
fn from(value: RefCell<$ty>) -> Self { Self::$var(Rc::new(value)) }
|
||||
}
|
||||
impl From<Rc<RefCell<$ty>>> for Value {
|
||||
fn from(value: Rc<RefCell<$ty>>) -> Self { Self::$var(value) }
|
||||
}
|
||||
};
|
||||
($ty:ty, $var:ident, into) => {
|
||||
impl From<$ty> for Value {
|
||||
fn from(value: $ty) -> Self { Self::$var(value.into()) }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_from!(bool, Bool, hash);
|
||||
impl_from!(Symbol, Symbol, hash);
|
||||
impl_from!(Range, Range);
|
||||
impl_from!(i64, Int, hash);
|
||||
impl_from!(f64, Float);
|
||||
impl_from!(Rational64, Ratio, hash);
|
||||
impl_from!(Complex64, Complex);
|
||||
impl_from!(HashMap<HashValue, Value>, Table, rcref);
|
||||
impl_from!(Vec<Value>, List, rcref);
|
||||
impl_from!(Function, Function, rc);
|
||||
impl_from!(NativeFunc, NativeFunc, rc);
|
||||
impl_from!(Rc<str>, String);
|
||||
impl_from!(&str, String, into);
|
||||
impl_from!(Box<str>, String, into);
|
||||
impl_from!(String, String, into);
|
|
@ -1,321 +1,11 @@
|
|||
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 num_rational::Rational64;
|
||||
use std::{ops::{Neg, Add, Sub, Mul, Div}, cmp::Ordering, cell::RefCell};
|
||||
|
||||
use num_complex::Complex64;
|
||||
use num_rational::Rational64;
|
||||
|
||||
use crate::chunk::Chunk;
|
||||
use crate::symbol::Symbol;
|
||||
use crate::{symbol::{SYM_INDEX_ERROR, SYM_STOP_ITERATOR, SYM_TYPE_ERROR}, value::range::RangeType, Vm};
|
||||
|
||||
type RcList = Rc<RefCell<Vec<Value>>>;
|
||||
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
|
||||
|
||||
#[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>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for NativeFunc {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("NativeFunc")
|
||||
.field("arity", &self.arity)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct HashValue(Value);
|
||||
|
||||
impl Eq for HashValue {}
|
||||
|
||||
impl TryFrom<Value> for HashValue {
|
||||
type Error = Exception;
|
||||
fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
|
||||
match value {
|
||||
Value::Nil
|
||||
| Value::Bool(_)
|
||||
| Value::Symbol(_)
|
||||
| Value::Int(_)
|
||||
| Value::Ratio(_)
|
||||
| Value::String(_) => Ok(Self(value)),
|
||||
_ => throw!(hash, "value {value:#} cannot be hashed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HashValue {
|
||||
pub fn into_inner(self) -> Value { self.0 }
|
||||
}
|
||||
|
||||
impl Hash for HashValue {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
std::mem::discriminant(&self.0).hash(state);
|
||||
match &self.0 {
|
||||
Value::Nil => (),
|
||||
Value::Bool(b) => b.hash(state),
|
||||
Value::Symbol(s) => s.hash(state),
|
||||
Value::Int(n) => n.hash(state),
|
||||
Value::Ratio(r) => r.hash(state),
|
||||
Value::String(s) => s.hash(state),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum RangeType {
|
||||
Open, Closed, Endless,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Range {
|
||||
start: i64,
|
||||
stop: i64,
|
||||
ty: RangeType,
|
||||
}
|
||||
|
||||
impl Range {
|
||||
pub fn len(&self) -> Option<usize> {
|
||||
match self.ty {
|
||||
RangeType::Open => Some((self.stop - self.start).max(0) as usize),
|
||||
RangeType::Closed => Some((self.stop - self.start + 1).max(0) as usize),
|
||||
RangeType::Endless => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self.ty {
|
||||
RangeType::Open => self.stop - self.start <= 0,
|
||||
RangeType::Closed => self.stop - self.start < 0,
|
||||
RangeType::Endless => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Range {
|
||||
type Item = i64;
|
||||
type IntoIter = Box<dyn Iterator<Item=i64>>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self.ty {
|
||||
RangeType::Open => Box::new(self.start..self.stop),
|
||||
RangeType::Closed => Box::new(self.start..=self.stop),
|
||||
RangeType::Endless => Box::new(self.start..),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Value {
|
||||
Nil,
|
||||
Bool(bool),
|
||||
Symbol(Symbol),
|
||||
Range(Range),
|
||||
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Ratio(Rational64),
|
||||
Complex(Complex64),
|
||||
|
||||
String(Rc<str>),
|
||||
List(RcList),
|
||||
Table(RcTable),
|
||||
Function(Rc<Function>),
|
||||
NativeFunc(Rc<NativeFunc>),
|
||||
}
|
||||
|
||||
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"),
|
||||
Value::Bool(b) => write!(f, "{b}"),
|
||||
Value::Symbol(s) => write!(f, ":{}", s.name()),
|
||||
Value::Range(r) => match r.ty {
|
||||
RangeType::Open => write!(f, "{}..{}", r.start, r.stop),
|
||||
RangeType::Closed => write!(f, "{}..={}", r.start, r.stop),
|
||||
RangeType::Endless => write!(f, "{}..*", r.start),
|
||||
},
|
||||
Value::Int(n) => write!(f, "{n}"),
|
||||
Value::Float(x) => write!(f, "{x:?}"),
|
||||
Value::Ratio(r) => write!(f, "{}/{}", r.numer(), r.denom()),
|
||||
Value::Complex(z) => write!(f, "{z}"),
|
||||
|
||||
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) => {
|
||||
write!(f, "{{ ")?;
|
||||
for (i, (k, v)) in t.borrow().iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
if f.alternate() {
|
||||
write!(f, "({:#}) = {v:#}", k.0)?;
|
||||
} else {
|
||||
write!(f, "({}) = {v}", k.0)?;
|
||||
}
|
||||
}
|
||||
write!(f, " }}")
|
||||
},
|
||||
Value::Function(g)
|
||||
=> write!(f, "<function {:?}>", Rc::as_ptr(g)),
|
||||
Value::NativeFunc(g)
|
||||
=> write!(f, "<native function {:?}>", Rc::as_ptr(g)),
|
||||
}
|
||||
}
|
||||
}
|
||||
use super::{Value, exception::{Result, throw}, range::Range, function::NativeFunc, HashValue};
|
||||
|
||||
impl Value {
|
||||
pub fn truthy(&self) -> bool {
|
||||
|
@ -371,7 +61,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 => throw!(type_error, "cannot negate {a:#}")
|
||||
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -385,7 +75,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) => throw!(type_error, "cannot add {l:#} and {r:#}")
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot add {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -399,7 +89,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) => throw!(type_error, "cannot subtract {l:#} and {r:#}")
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot subtract {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -413,7 +103,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) => throw!(type_error, "cannot multiply {l:#} and {r:#}")
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot multiply {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -427,7 +117,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) => throw!(type_error, "cannot divide {l:#} and {r:#}")
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -463,7 +153,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) => throw!(type_error, "cannot modulo {l:#} and {r:#}")
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -474,7 +164,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) => throw!(type_error, "cannot integer divide {l:#} and {r:#}")
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -492,7 +182,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) => throw!(type_error, "cannot exponentiate {l:#} and {r:#}")
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -501,7 +191,7 @@ impl PartialEq for Value {
|
|||
#[allow(clippy::cast_precision_loss)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
use Value as V;
|
||||
use RangeType as Rty;
|
||||
use super::range::RangeType as Rty;
|
||||
match (self, other) {
|
||||
(V::Nil, V::Nil) => true,
|
||||
(V::Bool(a), V::Bool(b)) => a == b,
|
||||
|
@ -565,21 +255,19 @@ impl Value {
|
|||
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
|
||||
match self.partial_cmp(other) {
|
||||
Some(o) => Ok(o),
|
||||
None => throw!(type_error, "cannot compare {self:#} and {other:#}"),
|
||||
None => throw!(*SYM_TYPE_ERROR, "cannot compare {self:#} and {other:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index(&self, idx: Self) -> Result<Self> {
|
||||
use Value as V;
|
||||
match (self, idx) {
|
||||
(_lhs, V::List(_l)) => todo!("assign index with list"),
|
||||
(_lhs, V::Range(_r)) => todo!("assign index with range"),
|
||||
(V::List(l), V::Int(i)) => {
|
||||
let l = l.borrow();
|
||||
if i >= 0 && (i as usize) < l.len() {
|
||||
Ok(l[i as usize].clone())
|
||||
} else {
|
||||
throw!(index_error, "index {i} out of bounds for list of length {}", l.len())
|
||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
|
||||
}
|
||||
},
|
||||
(V::Range(r), V::Int(i)) => {
|
||||
|
@ -588,9 +276,9 @@ impl Value {
|
|||
|| i < r.stop
|
||||
|| (r.ty == RangeType::Closed && i == r.stop)
|
||||
) {
|
||||
Ok(Value::Int(r.start + i))
|
||||
Ok((r.start + i).into())
|
||||
} else {
|
||||
throw!(index_error, "index {i} out of bounds for range {self}")
|
||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
|
||||
}
|
||||
},
|
||||
(V::Table(t), i) => {
|
||||
|
@ -598,7 +286,7 @@ impl Value {
|
|||
let i = i.try_into()?;
|
||||
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
|
||||
},
|
||||
(col, idx) => throw!(type_error, "cannot index {col:#} with {idx:#}")
|
||||
(col, idx) => throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -611,7 +299,7 @@ impl Value {
|
|||
l[i as usize] = val;
|
||||
Ok(())
|
||||
} else {
|
||||
throw!(index_error, "index {i} out of bounds for list of length {}", l.len())
|
||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
|
||||
}
|
||||
},
|
||||
(V::Table(t), i) => {
|
||||
|
@ -620,7 +308,7 @@ impl Value {
|
|||
t.insert(i, val);
|
||||
Ok(())
|
||||
},
|
||||
(col, idx) => throw!(index_error, "cannot index {col:#} with {idx:#}")
|
||||
(col, idx) => throw!(*SYM_INDEX_ERROR, "cannot index {col:#} with {idx:#}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -630,31 +318,48 @@ impl Value {
|
|||
(V::List(l1), V::List(l2)) => {
|
||||
let mut l = l1.borrow().clone();
|
||||
l.extend_from_slice(&l2.borrow());
|
||||
Ok(V::List(Rc::new(RefCell::new(l))))
|
||||
Ok(l.into())
|
||||
},
|
||||
(V::String(s1), V::String(s2)) => {
|
||||
let mut s = s1.as_ref().to_owned();
|
||||
s.push_str(s2);
|
||||
Ok(V::String(s.into()))
|
||||
}
|
||||
(l, r) => throw!(type_error, "cannot concatenate {l:#} and {r:#}"),
|
||||
(V::Table(t1), V::Table(t2)) => {
|
||||
let mut t = t1.borrow().clone();
|
||||
t.extend(t2.borrow().iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||
Ok(t.into())
|
||||
}
|
||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot concatenate {l:#} and {r:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append(&self, val: Self) -> Result<Self> {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::List(list) => {
|
||||
let mut l = list.borrow().clone();
|
||||
l.push(val);
|
||||
Ok(l.into())
|
||||
},
|
||||
lhs => throw!(*SYM_TYPE_ERROR, "cannot append to {lhs:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn range(&self, other: &Self, closed: bool) -> Result<Self> {
|
||||
if let (Value::Int(start), Value::Int(stop)) = (self, other) {
|
||||
let ty = if closed { RangeType::Closed } else { RangeType::Open };
|
||||
Ok(Value::Range(Range { start: *start, stop: *stop, ty }))
|
||||
Ok(Range { start: *start, stop: *stop, ty }.into())
|
||||
} else {
|
||||
throw!(type_error, "cannot create range between {self:#} and {other:#}")
|
||||
throw!(*SYM_TYPE_ERROR, "cannot create range between {self:#} and {other:#}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn range_endless(&self) -> Result<Self> {
|
||||
if let Value::Int(start) = self {
|
||||
Ok(Value::Range(Range { start: *start, stop: 0, ty: RangeType::Endless }))
|
||||
Ok(Range { start: *start, stop: 0, ty: RangeType::Endless }.into())
|
||||
} else {
|
||||
throw!(type_error, "cannot create endless range from {self:#}")
|
||||
throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -663,50 +368,50 @@ impl Value {
|
|||
Self::Function(_) | Self::NativeFunc(_) => Ok(self),
|
||||
Self::Range(range) => {
|
||||
let range_iter = RefCell::new(range.into_iter());
|
||||
let f = move |_, _| {
|
||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||
if let Some(v) = range_iter.borrow_mut().next() {
|
||||
Ok(Value::Int(v))
|
||||
Ok(v.into())
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
throw!(*SYM_STOP_ITERATOR)
|
||||
}
|
||||
};
|
||||
Ok(Value::NativeFunc(Rc::new(NativeFunc {
|
||||
Ok(NativeFunc {
|
||||
arity: 0,
|
||||
f: Box::new(f),
|
||||
})))
|
||||
f: Box::new(f),
|
||||
}.into())
|
||||
},
|
||||
Self::List(list) => {
|
||||
let idx = RefCell::new(0);
|
||||
let f = move |_, _| {
|
||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||
let i = *idx.borrow();
|
||||
if let Some(v) = list.borrow().get(i) {
|
||||
*idx.borrow_mut() += 1;
|
||||
Ok(v.clone())
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
throw!(*SYM_STOP_ITERATOR)
|
||||
}
|
||||
};
|
||||
Ok(Value::NativeFunc(Rc::new(NativeFunc {
|
||||
Ok(NativeFunc {
|
||||
arity: 0,
|
||||
f: Box::new(f),
|
||||
})))
|
||||
}.into())
|
||||
},
|
||||
Self::Table(table) => {
|
||||
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
|
||||
let keys = RefCell::new(keys.into_iter());
|
||||
let f = move |_, _| {
|
||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||
if let Some(v) = keys.borrow_mut().next() {
|
||||
Ok(v.into_inner())
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
throw!(*SYM_STOP_ITERATOR)
|
||||
}
|
||||
};
|
||||
Ok(Value::NativeFunc(Rc::new(NativeFunc {
|
||||
Ok(NativeFunc {
|
||||
arity: 0,
|
||||
f: Box::new(f),
|
||||
})))
|
||||
}.into())
|
||||
},
|
||||
_ => throw!(type_error, "cannot iterate {self:#}"),
|
||||
_ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"),
|
||||
}
|
||||
}
|
||||
}
|
43
talc-lang/src/value/range.rs
Normal file
43
talc-lang/src/value/range.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum RangeType {
|
||||
Open, Closed, Endless,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Range {
|
||||
pub start: i64,
|
||||
pub stop: i64,
|
||||
pub ty: RangeType,
|
||||
}
|
||||
|
||||
impl Range {
|
||||
pub fn len(&self) -> Option<usize> {
|
||||
match self.ty {
|
||||
RangeType::Open => Some((self.stop - self.start).max(0) as usize),
|
||||
RangeType::Closed => Some((self.stop - self.start + 1).max(0) as usize),
|
||||
RangeType::Endless => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self.ty {
|
||||
RangeType::Open => self.stop - self.start <= 0,
|
||||
RangeType::Closed => self.stop - self.start < 0,
|
||||
RangeType::Endless => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Range {
|
||||
type Item = i64;
|
||||
type IntoIter = Box<dyn Iterator<Item=i64>>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self.ty {
|
||||
RangeType::Open => Box::new(self.start..self.stop),
|
||||
RangeType::Closed => Box::new(self.start..=self.stop),
|
||||
RangeType::Endless => Box::new(self.start..),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{rc::Rc, cmp::Ordering, cell::RefCell, collections::HashMap};
|
||||
use std::{cmp::Ordering, collections::HashMap, rc::Rc, sync::{atomic::AtomicBool, Arc}};
|
||||
|
||||
use crate::{chunk::Instruction, value::{Value, Function, Result, throw, Exception}, ast::{BinaryOp, UnaryOp}, symbol::Symbol};
|
||||
use crate::{ast::{BinaryOp, UnaryOp}, chunk::Instruction, symbol::{Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR}, value::{exception::{throw, Exception, Result}, function::Function, Value}};
|
||||
|
||||
struct TryFrame { idx: usize, stack_len: usize }
|
||||
|
||||
|
@ -13,7 +13,6 @@ struct CallFrame {
|
|||
root: bool,
|
||||
}
|
||||
|
||||
|
||||
impl CallFrame {
|
||||
fn new(func: Rc<Function>, locals: Vec<Value>) -> Self {
|
||||
Self {
|
||||
|
@ -22,15 +21,6 @@ impl CallFrame {
|
|||
..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 {
|
||||
|
@ -38,6 +28,7 @@ pub struct Vm {
|
|||
call_stack: Vec<CallFrame>,
|
||||
stack_max: usize,
|
||||
globals: HashMap<Symbol, Value>,
|
||||
interrupt: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
pub fn binary_op(o: BinaryOp, a: Value, b: Value) -> Result<Value> {
|
||||
|
@ -58,7 +49,7 @@ pub fn binary_op(o: BinaryOp, a: Value, b: Value) -> Result<Value> {
|
|||
BinaryOp::Range => a.range(&b, false),
|
||||
BinaryOp::RangeIncl => a.range(&b, true),
|
||||
BinaryOp::Concat => a.concat(&b),
|
||||
BinaryOp::Append => todo!("append"),
|
||||
BinaryOp::Append => a.append(b),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,13 +68,22 @@ impl Vm {
|
|||
call_stack: Vec::with_capacity(16),
|
||||
globals: HashMap::with_capacity(16),
|
||||
stack_max,
|
||||
interrupt: Arc::new(AtomicBool::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_interrupt(&self) -> Arc<AtomicBool> {
|
||||
self.interrupt.clone()
|
||||
}
|
||||
|
||||
pub fn set_global(&mut self, name: Symbol, val: Value) {
|
||||
self.globals.insert(name, val);
|
||||
}
|
||||
|
||||
pub fn set_global_name(&mut self, name: &str, val: Value) {
|
||||
self.globals.insert(Symbol::get(name), val);
|
||||
}
|
||||
|
||||
pub fn get_global(&self, name: Symbol) -> Option<&Value> {
|
||||
self.globals.get(&name)
|
||||
}
|
||||
|
@ -105,68 +105,91 @@ impl Vm {
|
|||
res
|
||||
}
|
||||
|
||||
fn debug_instr(&self, frame: &CallFrame, instr: Instruction) {
|
||||
let framecode = (Rc::as_ptr(&frame.func) as usize >> 4) & 0xffff;
|
||||
println!("({:04x}) {:04}: {instr}", framecode, frame.ip);
|
||||
}
|
||||
fn check_interrupt(&mut self) -> Result<()> {
|
||||
if self.interrupt.fetch_and(false, std::sync::atomic::Ordering::Relaxed) {
|
||||
throw!(*SYM_INTERRUPTED)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_instr(&mut self, frame: &mut CallFrame, instr: Instruction) -> Result<Option<Value>> {
|
||||
use Instruction as I;
|
||||
|
||||
match instr {
|
||||
// do nothing
|
||||
I::Nop => (),
|
||||
// [] -> [locals[n]]
|
||||
I::LoadLocal(n)
|
||||
=> self.push(frame.locals[usize::from(n)].clone()),
|
||||
// [x] -> [], locals[n] = x
|
||||
I::StoreLocal(n)
|
||||
=> frame.locals[usize::from(n)] = self.pop(),
|
||||
// [x] -> [], locals.push(x)
|
||||
I::NewLocal
|
||||
=> frame.locals.push(self.pop()),
|
||||
// locals.pop_n(n)
|
||||
I::DropLocal(n)
|
||||
=> frame.locals.truncate(frame.locals.len() - usize::from(n)),
|
||||
// [] -> [globals[s]]
|
||||
I::LoadGlobal(s) => {
|
||||
let sym = unsafe { s.to_symbol_unchecked() };
|
||||
let v = match self.globals.get(&sym) {
|
||||
Some(v) => v.clone(),
|
||||
None => throw!(name_error, "undefined global {}", sym.name()),
|
||||
None => throw!(*SYM_NAME_ERROR, "undefined global {}", sym.name()),
|
||||
};
|
||||
self.push(v);
|
||||
},
|
||||
// [x] -> [], globals[s] = x
|
||||
I::StoreGlobal(s) => {
|
||||
let sym = unsafe { s.to_symbol_unchecked() };
|
||||
let v = self.pop();
|
||||
self.globals.insert(sym, v);
|
||||
},
|
||||
// [] -> [consts[n]]
|
||||
I::Const(n)
|
||||
=> self.push(frame.func.chunk.consts[usize::from(n)].clone()),
|
||||
// [] -> [nil]
|
||||
I::Nil => self.push(Value::Nil),
|
||||
// [] -> [b]
|
||||
I::Bool(b) => self.push(Value::Bool(b)),
|
||||
I::Symbol(n) => {
|
||||
let sym = unsafe { Symbol::from_id_unchecked(u32::from(n)) };
|
||||
// [] -> [s]
|
||||
I::Symbol(s) => {
|
||||
let sym = unsafe { Symbol::from_id_unchecked(u32::from(s)) };
|
||||
self.push(Value::Symbol(sym));
|
||||
},
|
||||
// [] -> [n]
|
||||
I::Int(n) => self.push(Value::Int(i64::from(n))),
|
||||
// [x] -> [x,x]
|
||||
I::Dup => self.push(self.stack[self.stack.len() - 1].clone()),
|
||||
// [x,y] -> [x,y,x,y]
|
||||
I::DupTwo => {
|
||||
self.push(self.stack[self.stack.len() - 2].clone());
|
||||
self.push(self.stack[self.stack.len() - 2].clone());
|
||||
},
|
||||
// [a0,a1...an] -> []
|
||||
I::Drop(n) => for _ in 0..u32::from(n) { self.pop(); },
|
||||
// [x,y] -> [y,x]
|
||||
I::Swap => {
|
||||
let len = self.stack.len();
|
||||
self.stack.swap(len - 1, len - 2);
|
||||
},
|
||||
// [x,y] -> [y op x]
|
||||
I::BinaryOp(op) => {
|
||||
let b = self.pop();
|
||||
let a = self.pop();
|
||||
self.push(binary_op(op, a, b)?);
|
||||
},
|
||||
// [x] -> [op x]
|
||||
I::UnaryOp(op) => {
|
||||
let a = self.pop();
|
||||
self.push(unary_op(op, a)?);
|
||||
},
|
||||
// [a0,a1...an] -.> [[a0,a1...an]]
|
||||
I::NewList(n) => {
|
||||
let list = self.pop_n(n as usize);
|
||||
self.push(Value::List(Rc::new(RefCell::new(list))));
|
||||
self.push(list.into())
|
||||
},
|
||||
// [l,a0,a1...an] -.> [l ++ [a0,a1...an]]
|
||||
I::GrowList(n) => {
|
||||
let ext = self.pop_n(n as usize);
|
||||
let list = self.pop();
|
||||
|
@ -174,6 +197,7 @@ impl Vm {
|
|||
list.borrow_mut().extend(ext);
|
||||
self.push(Value::List(list));
|
||||
},
|
||||
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
|
||||
I::NewTable(n) => {
|
||||
let mut table = HashMap::new();
|
||||
for _ in 0..n {
|
||||
|
@ -181,8 +205,9 @@ impl Vm {
|
|||
let k = self.pop();
|
||||
table.insert(k.try_into()?, v);
|
||||
}
|
||||
self.push(Value::Table(Rc::new(RefCell::new(table))));
|
||||
self.push(table.into())
|
||||
},
|
||||
// [t,k0,v0...kn,vn] -> [t ++ {k0=v0...kn=vn}]
|
||||
I::GrowTable(n) => {
|
||||
let mut ext = self.pop_n(2 * n as usize);
|
||||
let table = self.pop();
|
||||
|
@ -197,11 +222,13 @@ impl Vm {
|
|||
drop(table_ref);
|
||||
self.push(Value::Table(table));
|
||||
},
|
||||
// [ct, idx] -> [ct!idx]
|
||||
I::Index => {
|
||||
let idx = self.pop();
|
||||
let ct = self.pop();
|
||||
self.push(ct.index(idx)?);
|
||||
},
|
||||
// [ct, idx, v] -> [v], ct!idx = v
|
||||
I::StoreIndex => {
|
||||
let v = self.pop();
|
||||
let idx = self.pop();
|
||||
|
@ -209,21 +236,43 @@ impl Vm {
|
|||
ct.store_index(idx, v.clone())?;
|
||||
self.push(v);
|
||||
},
|
||||
I::Jump(n) => frame.ip = usize::from(n),
|
||||
I::JumpTrue(n) => if self.pop().truthy() { frame.ip = usize::from(n) },
|
||||
I::JumpFalse(n) => if !self.pop().truthy() { frame.ip = usize::from(n) },
|
||||
// ip = n
|
||||
I::Jump(n) => {
|
||||
self.check_interrupt()?;
|
||||
frame.ip = usize::from(n)
|
||||
},
|
||||
// [v] ->, [], if v then ip = n
|
||||
I::JumpTrue(n) => if self.pop().truthy() {
|
||||
self.check_interrupt()?;
|
||||
frame.ip = usize::from(n)
|
||||
},
|
||||
// [v] ->, [], if not v then ip = n
|
||||
I::JumpFalse(n) => if !self.pop().truthy() {
|
||||
self.check_interrupt()?;
|
||||
frame.ip = usize::from(n)
|
||||
},
|
||||
// [v] -> [iter(v)]
|
||||
I::IterBegin => {
|
||||
let iter = self.pop().to_iter_function()?;
|
||||
self.push(iter);
|
||||
},
|
||||
I::IterTest(n) => {
|
||||
// [i] -> if i() succeeds then [i, i()], otherwise [] and ip = n
|
||||
I::IterNext(n) => {
|
||||
let v = &self.stack[self.stack.len() - 1];
|
||||
if v == &Value::Nil {
|
||||
self.pop();
|
||||
self.pop();
|
||||
frame.ip = usize::from(n);
|
||||
}
|
||||
self.call_stack.push(std::mem::take(frame));
|
||||
let res = self.call_value(v.clone(), vec![v.clone()]);
|
||||
*frame = self.call_stack.pop().expect("no frame to pop");
|
||||
match res {
|
||||
Ok(res) => self.push(res),
|
||||
Err(e) => if e.ty == Symbol::get("stop_iterator") {
|
||||
self.pop();
|
||||
frame.ip = usize::from(n)
|
||||
} else {
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
},
|
||||
// try_frames.push(t, stack.len())
|
||||
I::BeginTry(t) => {
|
||||
let tryframe = TryFrame {
|
||||
idx: usize::from(t),
|
||||
|
@ -231,48 +280,48 @@ impl Vm {
|
|||
};
|
||||
frame.try_frames.push(tryframe);
|
||||
},
|
||||
// try_frames.pop()
|
||||
I::EndTry => {
|
||||
frame.try_frames.pop().expect("no try to pop");
|
||||
},
|
||||
// [f,a0,a1...an] -> [f(a0,a1...an)]
|
||||
I::Call(n) => {
|
||||
self.check_interrupt()?;
|
||||
let n = usize::from(n);
|
||||
let func = &self.stack[self.stack.len() - n - 1];
|
||||
let args = self.pop_n(n + 1);
|
||||
let func = &args[0];
|
||||
if let Value::NativeFunc(nf) = func {
|
||||
let nf = nf.clone();
|
||||
if nf.arity != n {
|
||||
throw!(type_error, "function call with wrong argument count");
|
||||
throw!(*SYM_TYPE_ERROR, "function call with wrong argument count");
|
||||
}
|
||||
|
||||
let args = self.pop_n(n);
|
||||
let func = self.pop();
|
||||
|
||||
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");
|
||||
let res = (nf.f)(self, args)?;
|
||||
*frame = self.call_stack.pop().expect("no frame to pop");
|
||||
|
||||
self.stack.push(res);
|
||||
} else if let Value::Function(func) = func {
|
||||
if func.arity != n {
|
||||
throw!(type_error, "function call with wrong argument count");
|
||||
throw!(*SYM_TYPE_ERROR, "function call with wrong argument count");
|
||||
}
|
||||
|
||||
if self.call_stack.len() + 1 >= self.stack_max {
|
||||
throw!(call_stack_overflow, "call stack overflow")
|
||||
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
|
||||
}
|
||||
self.call_stack.push(std::mem::take(frame));
|
||||
|
||||
let func = func.clone();
|
||||
let args = self.pop_n(n + 1);
|
||||
*frame = CallFrame::new(func, args);
|
||||
} else {
|
||||
throw!(type_error, "attempt to call non-function {func}");
|
||||
throw!(*SYM_TYPE_ERROR, "attempt to call non-function {func}");
|
||||
}
|
||||
},
|
||||
// [v] -> [], return v
|
||||
I::Return if frame.root => {
|
||||
let v = self.pop();
|
||||
self.stack.clear();
|
||||
return Ok(Some(v));
|
||||
return Ok(Some(self.pop()));
|
||||
},
|
||||
// [v] -> [], return v
|
||||
I::Return => {
|
||||
*frame = self.call_stack.pop().expect("no root frame");
|
||||
},
|
||||
|
@ -302,14 +351,34 @@ impl Vm {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, func: Rc<Function>) -> Result<Value> {
|
||||
assert!(func.arity == 0, "root function must not take arguments");
|
||||
pub fn call_value(&mut self, value: Value, args: Vec<Value>) -> Result<Value> {
|
||||
match value {
|
||||
Value::Function(f) => self.call(f, args),
|
||||
Value::NativeFunc(f) => {
|
||||
if f.arity + 1 != args.len() {
|
||||
throw!(*SYM_TYPE_ERROR, "function call with wrong argument count");
|
||||
}
|
||||
(f.f)(self, args)
|
||||
},
|
||||
_ => throw!(*SYM_TYPE_ERROR, "not a function"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn call_no_args(&mut self, func: Rc<Function>) -> Result<Value> {
|
||||
self.call(func.clone(), vec![func.into()])
|
||||
}
|
||||
|
||||
pub fn call(&mut self, func: Rc<Function>, args: Vec<Value>) -> Result<Value> {
|
||||
if func.arity + 1 != args.len() {
|
||||
throw!(*SYM_TYPE_ERROR, "function call with wrong argument count");
|
||||
}
|
||||
let init_stack_len = self.stack.len();
|
||||
let mut frame = CallFrame::new_root(func.clone());
|
||||
let mut frame = CallFrame::new(func, args);
|
||||
frame.root = true;
|
||||
|
||||
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) => (),
|
||||
|
|
11
talc-macros/Cargo.toml
Normal file
11
talc-macros/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "talc-macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
quote = "1.0"
|
32
talc-macros/src/lib.rs
Normal file
32
talc-macros/src/lib.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use proc_macro::TokenStream;
|
||||
use syn::{LitInt, ItemFn};
|
||||
use quote::quote;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream {
|
||||
let itemfn: ItemFn = syn::parse(annotated_item).unwrap();
|
||||
let arity: LitInt = syn::parse(input).unwrap();
|
||||
|
||||
let visibility = itemfn.vis;
|
||||
let block = itemfn.block;
|
||||
let name = itemfn.sig.ident;
|
||||
let inputs = itemfn.sig.inputs;
|
||||
let output = itemfn.sig.output;
|
||||
|
||||
assert!(itemfn.sig.constness.is_none(), "item must not be const");
|
||||
assert!(itemfn.sig.asyncness.is_none(), "item must not be async");
|
||||
assert!(itemfn.sig.unsafety.is_none(), "item must not be unsafe");
|
||||
assert!(itemfn.sig.abi.is_none(), "item must not contain an ABI specifier");
|
||||
assert!(itemfn.sig.variadic.is_none(), "item must not be variadic");
|
||||
|
||||
let expanded = quote! {
|
||||
#visibility fn #name() -> ::talc_lang::value::function::NativeFunc {
|
||||
::talc_lang::value::function::NativeFunc {
|
||||
arity: #arity,
|
||||
f: Box::new(|#inputs| #output #block)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
|
@ -4,3 +4,6 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
talc-lang = { path = "../talc-lang" }
|
||||
talc-macros = { path = "../talc-macros" }
|
||||
lazy_static = "1.4"
|
||||
|
|
31
talc-std/src/exception.rs
Normal file
31
talc-std/src/exception.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use talc_lang::{symbol::SYM_TYPE_ERROR, value::{exception::{throw, Exception, Result}, Value}, Vm};
|
||||
use talc_macros::native_func;
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, ty, msg] = args.try_into().expect("bypassed arity check");
|
||||
let Value::Symbol(ty) = ty else {
|
||||
throw!(*SYM_TYPE_ERROR, "exception type must be a symbol")
|
||||
};
|
||||
match msg {
|
||||
Value::String(msg) => Err(Exception::new_with_msg(ty, msg)),
|
||||
Value::Nil => Err(Exception::new(ty)),
|
||||
_ => throw!(*SYM_TYPE_ERROR, "exception message must be a string")
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn rethrow(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, table] = args.try_into().expect("bypassed arity check");
|
||||
if let Value::Table(table) = table {
|
||||
if let Some(e) = Exception::from_table(&table) {
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
throw!(*SYM_TYPE_ERROR, "argument not a valid exception")
|
||||
}
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
vm.set_global_name("throw", throw().into());
|
||||
vm.set_global_name("rethrow", rethrow().into());
|
||||
}
|
37
talc-std/src/io.rs
Normal file
37
talc-std/src/io.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use std::io::Write;
|
||||
|
||||
use talc_lang::{value::{exception::{throw, Result}, Value}, Vm};
|
||||
use talc_macros::native_func;
|
||||
|
||||
use crate::SYM_IO_ERROR;
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn print(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
if let Err(e) = write!(std::io::stdout(), "{}", args[1]) {
|
||||
throw!(*SYM_IO_ERROR, "{e}")
|
||||
}
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn println(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
if let Err(e) = writeln!(std::io::stdout(), "{}", args[1]) {
|
||||
throw!(*SYM_IO_ERROR, "{e}")
|
||||
}
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
#[native_func(0)]
|
||||
pub fn readln(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
|
||||
let mut buf = String::new();
|
||||
if let Err(e) = std::io::stdin().read_line(&mut buf) {
|
||||
throw!(*SYM_IO_ERROR, "{e}")
|
||||
}
|
||||
Ok(buf.into())
|
||||
}
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
vm.set_global_name("print", print().into());
|
||||
vm.set_global_name("println", println().into());
|
||||
vm.set_global_name("readln", readln().into());
|
||||
}
|
42
talc-std/src/iter.rs
Normal file
42
talc-std/src/iter.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use talc_lang::{symbol::SYM_STOP_ITERATOR, value::{exception::Result, function::NativeFunc, Value}, Vm};
|
||||
use talc_macros::native_func;
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn iter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, v] = args.try_into().expect("bypassed arity check");
|
||||
v.to_iter_function()
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, func, iter] = args.try_into().expect("bypassed arity check");
|
||||
let iter = iter.to_iter_function()?;
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
let next = vm.call_value(iter.clone(), vec![iter.clone()])?;
|
||||
vm.call_value(func.clone(), vec![func.clone(), next])
|
||||
};
|
||||
Ok(NativeFunc { arity: 0, f: Box::new(f) }.into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = args.try_into().expect("bypassed arity check");
|
||||
let iter = iter.to_iter_function()?;
|
||||
let mut result = Vec::new();
|
||||
loop {
|
||||
match vm.call_value(iter.clone(), vec![iter.clone()]) {
|
||||
Ok(v) => result.push(v),
|
||||
Err(e) => if e.ty == *SYM_STOP_ITERATOR {
|
||||
return Ok(result.into())
|
||||
} else {
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
vm.set_global_name("iter", iter().into());
|
||||
vm.set_global_name("map", map().into());
|
||||
vm.set_global_name("list", list().into());
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
use talc_lang::{symbol::{symbol, Symbol}, Vm};
|
||||
|
||||
pub mod num;
|
||||
pub mod io;
|
||||
pub mod iter;
|
||||
pub mod exception;
|
||||
|
||||
pub fn load_all(vm: &mut Vm) {
|
||||
num::load(vm);
|
||||
io::load(vm);
|
||||
iter::load(vm);
|
||||
exception::load(vm);
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref SYM_IO_ERROR: Symbol = symbol!(io_error);
|
||||
}
|
5
talc-std/src/num.rs
Normal file
5
talc-std/src/num.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
use talc_lang::Vm;
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
|
||||
}
|
Loading…
Reference in a new issue