diff --git a/Cargo.lock b/Cargo.lock index 3d0ac9f..e2e573d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 1809e70..3f42c6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,6 @@ members = [ "talc-lang", "talc-bin", "talc-std", + "talc-macros", ] resolver = "2" diff --git a/talc-bin/Cargo.toml b/talc-bin/Cargo.toml index a85f938..38ae9c4 100644 --- a/talc-bin/Cargo.toml +++ b/talc-bin/Cargo.toml @@ -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" diff --git a/talc-bin/src/main.rs b/talc-bin/src/main.rs index 0d01072..04696e2 100644 --- a/talc-bin/src/main.rs +++ b/talc-bin/src/main.rs @@ -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> { - let parser = Parser::new(); +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + file: Option, + + #[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> { 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> { 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> { 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 + } + } } diff --git a/talc-lang/Cargo.toml b/talc-lang/Cargo.toml index 07144db..aa4ea01 100644 --- a/talc-lang/Cargo.toml +++ b/talc-lang/Cargo.toml @@ -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"]} diff --git a/talc-lang/src/ast.rs b/talc-lang/src/ast.rs index 3fcc2a8..c2b6297 100644 --- a/talc-lang/src/ast.rs +++ b/talc-lang/src/ast.rs @@ -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>), Table(Vec<(Expr<'s>, Expr<'s>)>), + Return(Box>), And(Box>, Box>), Or(Box>, Box>), If(Box>, Box>, Option>>), diff --git a/talc-lang/src/chunk.rs b/talc-lang/src/chunk.rs index d16581f..2c9f1bf 100644 --- a/talc-lang/src/chunk.rs +++ b/talc-lang/src/chunk.rs @@ -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}"), diff --git a/talc-lang/src/compiler.rs b/talc-lang/src/compiler.rs index e127812..d55ddb0 100644 --- a/talc-lang/src/compiler.rs +++ b/talc-lang/src/compiler.rs @@ -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, globals: Vec, } -pub fn repl(expr: &Expr, globals: &[Local]) -> (Function, Vec) { - 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) { + 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) { + 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))); } diff --git a/talc-lang/src/lib.rs b/talc-lang/src/lib.rs index 54d9efc..8fda026 100644 --- a/talc-lang/src/lib.rs +++ b/talc-lang/src/lib.rs @@ -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), diff --git a/talc-lang/src/parser.lalrpop b/talc-lang/src/parser.lalrpop index 4c50924..98bdfa0 100644 --- a/talc-lang/src/parser.lalrpop +++ b/talc-lang/src/parser.lalrpop @@ -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: Box> = Assign; +Expr: Box> = { + "return" => Box::new(Expr::Return(e)), + Assign, +} // // assignment diff --git a/talc-lang/src/symbol.rs b/talc-lang/src/symbol.rs index ab1d377..c3a3d72 100644 --- a/talc-lang/src/symbol.rs +++ b/talc-lang/src/symbol.rs @@ -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, Symbol> } -static SYM_TABLE: OnceLock> = 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> = 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 { - 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; diff --git a/talc-lang/src/value/exception.rs b/talc-lang/src/value/exception.rs new file mode 100644 index 0000000..6e0b5e9 --- /dev/null +++ b/talc-lang/src/value/exception.rs @@ -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 = std::result::Result; + +#[derive(Clone, Debug)] +pub struct Exception { + pub ty: Symbol, + pub msg: Option>, + pub data: Option, +} + +impl Exception { + pub fn new(ty: Symbol) -> Self { + Self { ty, msg: None, data: None } + } + + pub fn new_with_msg(ty: Symbol, msg: Rc) -> Self { + Self { ty, msg: Some(msg), data: None } + } + + pub fn from_table(table: &Rc>>) -> Option { + 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; + + + diff --git a/talc-lang/src/value/function.rs b/talc-lang/src/value/function.rs new file mode 100644 index 0000000..36ff31f --- /dev/null +++ b/talc-lang/src/value/function.rs @@ -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) -> Result>, +} + +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, 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(()) +} + diff --git a/talc-lang/src/value/mod.rs b/talc-lang/src/value/mod.rs new file mode 100644 index 0000000..7a9791e --- /dev/null +++ b/talc-lang/src/value/mod.rs @@ -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>>; +type RcTable = Rc>>; + +#[derive(Clone, Debug)] +pub enum Value { + Nil, + Bool(bool), + Symbol(Symbol), + Range(Range), + + Int(i64), + Float(f64), + Ratio(Rational64), + Complex(Complex64), + + String(Rc), + List(RcList), + Table(RcTable), + Function(Rc), + NativeFunc(Rc), +} + +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, "", Rc::as_ptr(g)), + Value::NativeFunc(g) + => write!(f, "", Rc::as_ptr(g)), + } + } +} + + +#[derive(Clone, Debug, PartialEq)] +pub struct HashValue(Value); + +impl Eq for HashValue {} + +impl TryFrom for HashValue { + type Error = Exception; + fn try_from(value: Value) -> std::result::Result { + 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(&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> 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> for Value { + fn from(value: RefCell<$ty>) -> Self { Self::$var(Rc::new(value)) } + } + impl From>> for Value { + fn from(value: Rc>) -> 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, Table, rcref); +impl_from!(Vec, List, rcref); +impl_from!(Function, Function, rc); +impl_from!(NativeFunc, NativeFunc, rc); +impl_from!(Rc, String); +impl_from!(&str, String, into); +impl_from!(Box, String, into); +impl_from!(String, String, into); diff --git a/talc-lang/src/value.rs b/talc-lang/src/value/ops.rs similarity index 53% rename from talc-lang/src/value.rs rename to talc-lang/src/value/ops.rs index 1feb263..8d96ea9 100644 --- a/talc-lang/src/value.rs +++ b/talc-lang/src/value/ops.rs @@ -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>>; -type RcTable = Rc>>; - -#[derive(Clone, Debug)] -pub struct Exception { - pub ty: Symbol, - pub msg: Rc, - pub data: Option, -} - -impl Exception { - pub fn new(ty: Symbol, msg: Rc) -> Self { - Self { ty, msg, data: None } - } - - pub fn from_table(table: RcTable) -> Option { - 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 = std::result::Result; - -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, 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) -> Result>, -} - -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 for HashValue { - type Error = Exception; - fn try_from(value: Value) -> std::result::Result { - 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(&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 { - 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>; - 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), - List(RcList), - Table(RcTable), - Function(Rc), - NativeFunc(Rc), -} - -impl From for Value { - fn from(value: Symbol) -> Self { Self::Symbol(value) } -} -impl From 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, "", Rc::as_ptr(g)), - Value::NativeFunc(g) - => write!(f, "", 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 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 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 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 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 { 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 { 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 { + 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 { 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 { 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| -> Result { 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| -> Result { 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 = table.borrow().keys().cloned().collect(); let keys = RefCell::new(keys.into_iter()); - let f = move |_, _| { + let f = move |_: &mut Vm, _: Vec| -> Result { 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:#}"), } } } diff --git a/talc-lang/src/value/range.rs b/talc-lang/src/value/range.rs new file mode 100644 index 0000000..8597a6a --- /dev/null +++ b/talc-lang/src/value/range.rs @@ -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 { + 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>; + 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..), + } + } +} + diff --git a/talc-lang/src/vm.rs b/talc-lang/src/vm.rs index 8e55b42..1ec7071 100644 --- a/talc-lang/src/vm.rs +++ b/talc-lang/src/vm.rs @@ -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, locals: Vec) -> Self { Self { @@ -22,15 +21,6 @@ impl CallFrame { ..Self::default() } } - - fn new_root(func: Rc) -> 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, stack_max: usize, globals: HashMap, + interrupt: Arc, } pub fn binary_op(o: BinaryOp, a: Value, b: Value) -> Result { @@ -58,7 +49,7 @@ pub fn binary_op(o: BinaryOp, a: Value, b: Value) -> Result { 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 { + 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> { 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) -> Result { - assert!(func.arity == 0, "root function must not take arguments"); + pub fn call_value(&mut self, value: Value, args: Vec) -> Result { + 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) -> Result { + self.call(func.clone(), vec![func.into()]) + } + + pub fn call(&mut self, func: Rc, args: Vec) -> Result { + 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) => (), diff --git a/talc-macros/Cargo.toml b/talc-macros/Cargo.toml new file mode 100644 index 0000000..a7d44c4 --- /dev/null +++ b/talc-macros/Cargo.toml @@ -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" diff --git a/talc-macros/src/lib.rs b/talc-macros/src/lib.rs new file mode 100644 index 0000000..46c8bfc --- /dev/null +++ b/talc-macros/src/lib.rs @@ -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) +} diff --git a/talc-std/Cargo.toml b/talc-std/Cargo.toml index d2ed7c0..81e3fb8 100644 --- a/talc-std/Cargo.toml +++ b/talc-std/Cargo.toml @@ -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" diff --git a/talc-std/src/exception.rs b/talc-std/src/exception.rs new file mode 100644 index 0000000..1e5874c --- /dev/null +++ b/talc-std/src/exception.rs @@ -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) -> Result { + 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) -> Result { + 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()); +} diff --git a/talc-std/src/io.rs b/talc-std/src/io.rs new file mode 100644 index 0000000..7bdec38 --- /dev/null +++ b/talc-std/src/io.rs @@ -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) -> Result { + 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) -> Result { + 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) -> Result { + 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()); +} diff --git a/talc-std/src/iter.rs b/talc-std/src/iter.rs new file mode 100644 index 0000000..41b4c9e --- /dev/null +++ b/talc-std/src/iter.rs @@ -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) -> Result { + let [_, v] = args.try_into().expect("bypassed arity check"); + v.to_iter_function() +} + +#[native_func(2)] +pub fn map(_: &mut Vm, args: Vec) -> Result { + 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) -> Result { + 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()); +} diff --git a/talc-std/src/lib.rs b/talc-std/src/lib.rs index e69de29..2856dee 100644 --- a/talc-std/src/lib.rs +++ b/talc-std/src/lib.rs @@ -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); +} diff --git a/talc-std/src/num.rs b/talc-std/src/num.rs new file mode 100644 index 0000000..9e6fec5 --- /dev/null +++ b/talc-std/src/num.rs @@ -0,0 +1,5 @@ +use talc_lang::Vm; + +pub fn load(vm: &mut Vm) { + +}