add features

This commit is contained in:
trimill 2024-02-23 14:54:34 -05:00
parent b48c08cb62
commit 66abf928e9
Signed by: trimill
GPG key ID: 4F77A16E17E10BCB
25 changed files with 1052 additions and 495 deletions

112
Cargo.lock generated
View file

@ -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"

View file

@ -3,5 +3,6 @@ members = [
"talc-lang",
"talc-bin",
"talc-std",
"talc-macros",
]
resolver = "2"

View file

@ -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"

View file

@ -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);
Function::disasm_recursive(func.clone(), &mut std::io::stdout())?;
std::io::stdout().flush()?;
if args.disasm {
if let Err(e) = disasm_recursive(&func, &mut std::io::stderr()) {
eprintln!("Error: {e}");
return ExitCode::FAILURE
}
}
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
}
}
}

View file

@ -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"]}

View file

@ -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>>>),

View file

@ -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}"),

View file

@ -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>) {
pub fn compile(expr: &Expr) -> Function {
let mut comp = Compiler::new_module(None);
comp.expr(expr);
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,
}];
let mut comp = Compiler {
Self {
mode: CompilerMode::Function,
parent: None,
mode: CompilerMode::Repl,
func: Function { arity: 0, chunk: Chunk::new() },
scope: 0,
locals,
globals: globals.to_vec(),
};
comp.expr(expr);
comp.emit(I::Return);
(comp.func, comp.globals)
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)));
}

View file

@ -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),

View file

@ -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

View file

@ -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;

View 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;

View 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
View 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);

View file

@ -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),
})))
}.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:#}"),
}
}
}

View 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..),
}
}
}

View file

@ -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.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();
self.pop();
frame.ip = usize::from(n);
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
View 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
View 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)
}

View file

@ -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
View 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
View 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
View 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());
}

View file

@ -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
View file

@ -0,0 +1,5 @@
use talc_lang::Vm;
pub fn load(vm: &mut Vm) {
}