improvements and such
This commit is contained in:
parent
66abf928e9
commit
9996ca1574
28 changed files with 3384 additions and 834 deletions
53
Cargo.lock
generated
53
Cargo.lock
generated
|
@ -138,7 +138,7 @@ dependencies = [
|
|||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.50",
|
||||
"syn 2.0.51",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -244,9 +244,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "3.0.0"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc"
|
||||
checksum = "26a147e1a6641a55d994b3e4e9fa4d9b180c8d652c09b363af8c9bf1b8e04139"
|
||||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
|
@ -290,9 +290,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.6"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd"
|
||||
checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
|
@ -527,6 +527,12 @@ dependencies = [
|
|||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "precomputed-hash"
|
||||
version = "0.1.1"
|
||||
|
@ -561,6 +567,36 @@ dependencies = [
|
|||
"nibble_vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
|
@ -707,9 +743,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.50"
|
||||
version = "2.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb"
|
||||
checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -752,6 +788,7 @@ name = "talc-std"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"rand",
|
||||
"talc-lang",
|
||||
"talc-macros",
|
||||
]
|
||||
|
@ -784,7 +821,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.50",
|
||||
"syn 2.0.51",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
185
talc-bin/src/helper.rs
Normal file
185
talc-bin/src/helper.rs
Normal file
|
@ -0,0 +1,185 @@
|
|||
use std::{borrow::Cow, cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use rustyline::{completion::Completer, highlight::Highlighter, hint::Hinter, validate::{ValidationContext, ValidationResult, Validator}, Helper, Result};
|
||||
use talc_lang::{Lexer, Vm};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum TokenType {
|
||||
String, Symbol, Number, Literal
|
||||
}
|
||||
|
||||
pub struct TalcHelper {
|
||||
vm: Rc<RefCell<Vm>>,
|
||||
lex: Lexer,
|
||||
token_types: HashMap<usize, TokenType>,
|
||||
}
|
||||
|
||||
macro_rules! load_tokens {
|
||||
($token_types:expr, $lex:expr, {$($($tok:literal)|+ => $ty:expr,)*}) => {{
|
||||
$($(
|
||||
$token_types.insert($lex.lex($tok).next().unwrap().unwrap().1.0, $ty);
|
||||
)*)*
|
||||
}};
|
||||
}
|
||||
|
||||
impl TalcHelper {
|
||||
pub fn new(vm: Rc<RefCell<Vm>>) -> Self {
|
||||
let lex = Lexer::new();
|
||||
let mut token_types = HashMap::new();
|
||||
load_tokens!(token_types, lex, {
|
||||
"\"\"" | "''" => TokenType::String,
|
||||
":a" | ":''" | ":\"\"" => TokenType::Symbol,
|
||||
"0" | "0.0" | "0x0" | "0b0" | "0o0" | "0s0" => TokenType::Number,
|
||||
"true" | "false" | "nil" => TokenType::Literal,
|
||||
});
|
||||
Self {
|
||||
vm,
|
||||
lex,
|
||||
token_types,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Helper for TalcHelper {}
|
||||
|
||||
impl Completer for TalcHelper {
|
||||
type Candidate = String;
|
||||
|
||||
fn complete(
|
||||
&self,
|
||||
line: &str,
|
||||
pos: usize,
|
||||
_ctx: &rustyline::Context<'_>,
|
||||
) -> Result<(usize, Vec<Self::Candidate>)> {
|
||||
let mut res = String::new();
|
||||
for ch in line[..pos].chars().rev() {
|
||||
if matches!(ch, '0'..='9' | 'a'..='z' | 'A'..='Z' | '_') {
|
||||
res.push(ch);
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
let res: String = res.chars().rev().collect();
|
||||
let mut keys = self.vm.borrow().globals().keys()
|
||||
.map(|sym| sym.name())
|
||||
.filter(|name| name.starts_with(&res))
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
keys.sort();
|
||||
Ok((pos - res.as_bytes().len(), keys))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hinter for TalcHelper {
|
||||
type Hint = String;
|
||||
}
|
||||
|
||||
impl Highlighter for TalcHelper {
|
||||
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
|
||||
let mut tokens = self.lex.lex(line).peekable();
|
||||
let mut buf = String::new();
|
||||
let mut last = 0;
|
||||
while let Some(Ok((l, tok, r))) = tokens.next() {
|
||||
buf += &line[last..l];
|
||||
last = r;
|
||||
let tokty = self.token_types.get(&tok.0);
|
||||
buf += match tokty {
|
||||
Some(TokenType::Literal) => "\x1b[93m",
|
||||
Some(TokenType::Number) => "\x1b[93m",
|
||||
Some(TokenType::String) => "\x1b[92m",
|
||||
Some(TokenType::Symbol) => "\x1b[96m",
|
||||
None => "",
|
||||
};
|
||||
buf += tok.1;
|
||||
if tokty.is_some() { buf += "\x1b[0m" }
|
||||
}
|
||||
buf += &line[last..];
|
||||
Cow::Owned(buf)
|
||||
}
|
||||
|
||||
fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
|
||||
&'s self,
|
||||
prompt: &'p str,
|
||||
_default: bool,
|
||||
) -> Cow<'b, str> {
|
||||
Cow::Owned(format!("\x1b[94m{prompt}\x1b[0m"))
|
||||
}
|
||||
|
||||
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
|
||||
Cow::Owned(format!("\x1b[37m{}\x1b[0m", hint))
|
||||
}
|
||||
|
||||
fn highlight_char(&self, line: &str, _: usize, forced: bool) -> bool {
|
||||
forced || !line.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for TalcHelper {
|
||||
fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
|
||||
let tokens = self.lex.lex(ctx.input());
|
||||
let mut delims = Vec::new();
|
||||
let mut mismatch = None;
|
||||
for token in tokens {
|
||||
let token = match token {
|
||||
Ok(t) => t,
|
||||
Err(e) => return Ok(ValidationResult::Invalid(
|
||||
Some(e.to_string()))),
|
||||
};
|
||||
let t = token.1.1;
|
||||
match t {
|
||||
"(" | "{" | "[" | "if" | "while" | "for" | "try"
|
||||
=> delims.push(token.1.1),
|
||||
")" => match delims.pop() {
|
||||
Some("(") => (),
|
||||
v => { mismatch = Some((v, t)); break }
|
||||
},
|
||||
"}" => match delims.pop() {
|
||||
Some("{") => (),
|
||||
v => { mismatch = Some((v, t)); break }
|
||||
},
|
||||
"]" => match delims.pop() {
|
||||
Some("[") => (),
|
||||
v => { mismatch = Some((v, t)); break }
|
||||
},
|
||||
"then" => match delims.pop() {
|
||||
Some("if") => delims.push(t),
|
||||
v => { mismatch = Some((v, t)); break }
|
||||
}
|
||||
"catch" => match delims.pop() {
|
||||
Some("try") => delims.push(t),
|
||||
v => { mismatch = Some((v, t)); break }
|
||||
}
|
||||
"do" => match delims.last().copied() {
|
||||
Some("while") | Some("for") | Some("catch") => {
|
||||
delims.pop();
|
||||
delims.push(t);
|
||||
},
|
||||
_ => delims.push(t)
|
||||
},
|
||||
"elif" | "else" => match delims.pop() {
|
||||
Some("then") => delims.push(t),
|
||||
v => { mismatch = Some((v, t)); break }
|
||||
},
|
||||
"end" => match delims.pop() {
|
||||
Some("then" | "elif" | "else" | "do" | "try") => (),
|
||||
v => { mismatch = Some((v, t)); break }
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
match mismatch {
|
||||
Some((None, b)) => return Ok(ValidationResult::Invalid(Some(
|
||||
format!(" found unmatched {b}")))),
|
||||
Some((Some(a), b)) => return Ok(ValidationResult::Invalid(Some(
|
||||
format!(" found {a} matched with {b}")))),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if delims.is_empty() {
|
||||
Ok(ValidationResult::Valid(None))
|
||||
} else {
|
||||
Ok(ValidationResult::Incomplete)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,136 +1,71 @@
|
|||
use clap::Parser;
|
||||
use rustyline::error::ReadlineError;
|
||||
use talc_lang::{compiler::{compile, compile_repl}, value::{function::disasm_recursive, Value}, symbol::Symbol, Vm};
|
||||
use std::{rc::Rc, path::PathBuf, process::ExitCode};
|
||||
use clap::{ColorChoice, Parser};
|
||||
use talc_lang::{compiler::compile, value::function::disasm_recursive, Vm};
|
||||
use std::{path::PathBuf, process::ExitCode, rc::Rc};
|
||||
|
||||
mod repl;
|
||||
mod helper;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Args {
|
||||
file: Option<PathBuf>,
|
||||
/// file to run
|
||||
file: Option<PathBuf>,
|
||||
|
||||
#[arg(short, long)]
|
||||
repl: bool,
|
||||
/// start the repl
|
||||
#[arg(short, long)]
|
||||
repl: bool,
|
||||
|
||||
#[arg(short, long)]
|
||||
disasm: bool,
|
||||
/// show disassembled bytecode
|
||||
#[arg(short, long)]
|
||||
disasm: bool,
|
||||
|
||||
/// enable or disable color
|
||||
#[arg(short, long, default_value="auto")]
|
||||
color: ColorChoice,
|
||||
}
|
||||
|
||||
fn exec(src: String, _args: &Args) -> ExitCode {
|
||||
fn exec(src: String, args: &Args) -> ExitCode {
|
||||
let parser = talc_lang::Parser::new();
|
||||
let mut vm = Vm::new(256);
|
||||
talc_std::load_all(&mut vm);
|
||||
talc_std::load_all(&mut vm);
|
||||
|
||||
let ex = match parser.parse(&src) {
|
||||
Ok(ex) => ex,
|
||||
Err(e) => {
|
||||
eprintln!("Error: {e}");
|
||||
return ExitCode::FAILURE
|
||||
},
|
||||
};
|
||||
let ex = match parser.parse(&src) {
|
||||
Ok(ex) => ex,
|
||||
Err(e) => {
|
||||
eprintln!("Error: {e}");
|
||||
return ExitCode::FAILURE
|
||||
},
|
||||
};
|
||||
|
||||
let func = Rc::new(compile(&ex));
|
||||
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("__");
|
||||
let prev3_sym = Symbol::get("___");
|
||||
|
||||
vm.set_global(prev1_sym, Value::Nil);
|
||||
vm.set_global(prev2_sym, Value::Nil);
|
||||
vm.set_global(prev3_sym, Value::Nil);
|
||||
|
||||
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) => return ExitCode::SUCCESS,
|
||||
Err(ReadlineError::Interrupted) => continue,
|
||||
Err(e) => {
|
||||
eprintln!("Error: {e}");
|
||||
continue
|
||||
},
|
||||
};
|
||||
|
||||
let ex = match parser.parse(&line) {
|
||||
Ok(ex) => ex,
|
||||
Err(e) => { eprintln!("Error: {e}"); continue },
|
||||
};
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
vm.set_global(prev1_sym, v.clone());
|
||||
if v != Value::Nil {
|
||||
println!("{v:#}");
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!("{e}"),
|
||||
if args.disasm {
|
||||
if let Err(e) = disasm_recursive(&func, &mut std::io::stderr()) {
|
||||
eprintln!("Error: {e}");
|
||||
return ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = vm.run_function(func.clone(), vec![func.into()]) {
|
||||
eprintln!("{e}");
|
||||
ExitCode::FAILURE
|
||||
} else {
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args = Args::parse();
|
||||
let args = Args::parse();
|
||||
|
||||
if args.repl || args.file.is_none() {
|
||||
return repl(&args)
|
||||
}
|
||||
if args.repl || args.file.is_none() {
|
||||
return repl::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
|
||||
}
|
||||
}
|
||||
match std::fs::read_to_string(args.file.as_ref().unwrap()) {
|
||||
Ok(s) => exec(s, &args),
|
||||
Err(e) => {
|
||||
eprintln!("Error: {e}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
146
talc-bin/src/repl.rs
Normal file
146
talc-bin/src/repl.rs
Normal file
|
@ -0,0 +1,146 @@
|
|||
use std::{cell::RefCell, io::IsTerminal, process::ExitCode, rc::Rc};
|
||||
|
||||
use clap::ColorChoice;
|
||||
use rustyline::{error::ReadlineError, history::MemHistory, ColorMode, Config, Editor};
|
||||
use talc_lang::{compiler::compile_repl, symbol::Symbol, value::{function::disasm_recursive, Value}, Vm};
|
||||
|
||||
use crate::{helper::TalcHelper, Args};
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct ReplColors {
|
||||
pub reset: &'static str,
|
||||
pub error: &'static str,
|
||||
}
|
||||
|
||||
impl ReplColors {
|
||||
fn new(colors: ColorChoice) -> Self {
|
||||
let colors = match colors {
|
||||
ColorChoice::Always => true,
|
||||
ColorChoice::Never => false,
|
||||
ColorChoice::Auto => std::io::stdout().is_terminal(),
|
||||
};
|
||||
if !colors {
|
||||
return Self::default()
|
||||
}
|
||||
|
||||
Self {
|
||||
reset: "\x1b[0m",
|
||||
error: "\x1b[91m",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn get_colmode(args: &Args) -> ColorMode {
|
||||
match args.color {
|
||||
ColorChoice::Auto => ColorMode::Enabled,
|
||||
ColorChoice::Always => ColorMode::Forced,
|
||||
ColorChoice::Never => ColorMode::Disabled,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_rustyline(args: &Args) -> Result<Editor<TalcHelper, MemHistory>, ExitCode> {
|
||||
let config = Config::builder()
|
||||
.auto_add_history(true)
|
||||
.color_mode(get_colmode(args))
|
||||
.check_cursor_position(true)
|
||||
.completion_type(rustyline::CompletionType::List)
|
||||
.build();
|
||||
match rustyline::Editor::with_history(config, MemHistory::default()) {
|
||||
Ok(rl) => Ok(rl),
|
||||
Err(ReadlineError::Io(e)) => {
|
||||
eprintln!("Error: {e}");
|
||||
Err(ExitCode::FAILURE)
|
||||
},
|
||||
Err(ReadlineError::Errno(e)) => {
|
||||
eprintln!("Error: {e}");
|
||||
Err(ExitCode::FAILURE)
|
||||
},
|
||||
Err(_) => Err(ExitCode::SUCCESS)
|
||||
}
|
||||
}
|
||||
|
||||
pub 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("__");
|
||||
let prev3_sym = Symbol::get("___");
|
||||
|
||||
vm.set_global(prev1_sym, Value::Nil);
|
||||
vm.set_global(prev2_sym, Value::Nil);
|
||||
vm.set_global(prev3_sym, Value::Nil);
|
||||
|
||||
let c = ReplColors::new(args.color);
|
||||
|
||||
let mut rl = match init_rustyline(args) {
|
||||
Ok(rl) => rl,
|
||||
Err(e) => return e,
|
||||
};
|
||||
|
||||
let vm = Rc::new(RefCell::new(vm));
|
||||
|
||||
rl.set_helper(Some(TalcHelper::new(vm.clone())));
|
||||
|
||||
loop {
|
||||
let line = rl.readline(">> ");
|
||||
let line = match line {
|
||||
Ok(line) => line,
|
||||
Err(ReadlineError::Eof) => return ExitCode::SUCCESS,
|
||||
Err(ReadlineError::Interrupted) => continue,
|
||||
Err(e) => {
|
||||
eprintln!("{}Error:{} {e}", c.error, c.reset);
|
||||
continue
|
||||
},
|
||||
};
|
||||
|
||||
let ex = match parser.parse(&line) {
|
||||
Ok(ex) => ex,
|
||||
Err(e) => {
|
||||
eprintln!("{}Error:{} {e}", c.error, c.reset);
|
||||
continue
|
||||
},
|
||||
};
|
||||
|
||||
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}", c.error, c.reset);
|
||||
}
|
||||
}
|
||||
|
||||
let mut vm = vm.borrow_mut();
|
||||
match vm.run_function(func.clone(), vec![func.into()]) {
|
||||
Ok(v) => {
|
||||
let prev2 = vm.get_global(prev2_sym).unwrap().clone();
|
||||
vm.set_global(prev3_sym, prev2);
|
||||
let prev1 = vm.get_global(prev1_sym).unwrap().clone();
|
||||
vm.set_global(prev2_sym, prev1);
|
||||
vm.set_global(prev1_sym, v.clone());
|
||||
if v != Value::Nil {
|
||||
println!("{v:#}");
|
||||
}
|
||||
}
|
||||
Err(e) => eprintln!("{}Error:{} {e}", c.error, c.reset),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ pub enum Expr<'s> {
|
|||
List(Vec<Expr<'s>>),
|
||||
Table(Vec<(Expr<'s>, Expr<'s>)>),
|
||||
|
||||
Return(Box<Expr<'s>>),
|
||||
Return(Box<Expr<'s>>),
|
||||
And(Box<Expr<'s>>, Box<Expr<'s>>),
|
||||
Or(Box<Expr<'s>>, Box<Expr<'s>>),
|
||||
If(Box<Expr<'s>>, Box<Expr<'s>>, Option<Box<Expr<'s>>>),
|
||||
|
|
|
@ -117,7 +117,7 @@ pub enum Instruction {
|
|||
JumpTrue(Arg24),
|
||||
JumpFalse(Arg24),
|
||||
|
||||
IterBegin, IterNext(Arg24),
|
||||
IterBegin, IterTest(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::IterNext(a) => write!(f, "iternext {}", usize::from(a)),
|
||||
Self::IterTest(a) => write!(f, "itertest {}", usize::from(a)),
|
||||
Self::BeginTry(t) => write!(f, "begintry {}", usize::from(t)),
|
||||
Self::EndTry => write!(f, "endtry"),
|
||||
Self::Call(n) => write!(f, "call {n}"),
|
||||
|
|
|
@ -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::Function;
|
||||
use crate::value::function::{FuncAttrs, Function};
|
||||
use crate::value::Value;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -20,69 +20,67 @@ enum CompilerMode {
|
|||
struct Compiler<'a> {
|
||||
mode: CompilerMode,
|
||||
parent: Option<&'a Compiler<'a>>,
|
||||
func: Function,
|
||||
chunk: Chunk,
|
||||
attrs: FuncAttrs,
|
||||
scope: usize,
|
||||
locals: Vec<Local>,
|
||||
globals: Vec<Local>,
|
||||
}
|
||||
|
||||
pub fn compile(expr: &Expr) -> Function {
|
||||
let mut comp = Compiler::new_module(None);
|
||||
let mut comp = Compiler::new_module(None);
|
||||
comp.expr(expr);
|
||||
comp.finish()
|
||||
comp.finish()
|
||||
}
|
||||
|
||||
pub fn compile_repl(expr: &Expr, globals: &[Local]) -> (Function, Vec<Local>) {
|
||||
let mut comp = Compiler::new_repl(globals);
|
||||
let mut comp = Compiler::new_repl(globals);
|
||||
comp.expr(expr);
|
||||
comp.finish_repl()
|
||||
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(),
|
||||
}
|
||||
}
|
||||
fn default() -> Self {
|
||||
let locals = vec![Local {
|
||||
name: "self".into(),
|
||||
scope: 0,
|
||||
}];
|
||||
Self {
|
||||
mode: CompilerMode::Function,
|
||||
parent: None,
|
||||
chunk: Chunk::new(),
|
||||
attrs: FuncAttrs { arity: 0, variadic: false },
|
||||
scope: 0,
|
||||
locals,
|
||||
globals: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Compiler<'a> {
|
||||
fn new_repl(globals: &[Local]) -> Self {
|
||||
Self {
|
||||
mode: CompilerMode::Repl,
|
||||
globals: globals.to_vec(),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
Self {
|
||||
mode: CompilerMode::Repl,
|
||||
globals: globals.to_vec(),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn new_module(parent: Option<&'a Self>) -> Self {
|
||||
Self {
|
||||
mode: CompilerMode::Module,
|
||||
parent,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
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 {
|
||||
let mut new = Self {
|
||||
mode: CompilerMode::Function,
|
||||
parent: Some(self),
|
||||
func,
|
||||
..Self::default()
|
||||
};
|
||||
..Self::default()
|
||||
};
|
||||
new.attrs.arity = args.len();
|
||||
|
||||
for arg in args {
|
||||
new.locals.push(Local {
|
||||
|
@ -96,12 +94,15 @@ impl<'a> Compiler<'a> {
|
|||
|
||||
pub fn finish(mut self) -> Function {
|
||||
self.emit(I::Return);
|
||||
self.func
|
||||
Function { chunk: Rc::new(self.chunk), attrs: self.attrs }
|
||||
}
|
||||
|
||||
pub fn finish_repl(mut self) -> (Function, Vec<Local>) {
|
||||
self.emit(I::Return);
|
||||
(self.func, self.globals)
|
||||
(
|
||||
Function { chunk: Rc::new(self.chunk), attrs: self.attrs },
|
||||
self.globals
|
||||
)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -109,16 +110,16 @@ impl<'a> Compiler<'a> {
|
|||
//
|
||||
|
||||
fn add_const(&mut self, val: Value) -> usize {
|
||||
self.func.chunk.add_const(val)
|
||||
self.chunk.add_const(val)
|
||||
}
|
||||
|
||||
fn emit(&mut self, instr: I) -> usize {
|
||||
self.func.chunk.add_instr(instr)
|
||||
self.chunk.add_instr(instr)
|
||||
}
|
||||
|
||||
fn emit_discard(&mut self, mut n: usize) {
|
||||
while n > 0 {
|
||||
let instrs = &mut self.func.chunk.instrs;
|
||||
let instrs = &mut self.chunk.instrs;
|
||||
|
||||
// dup followed by store: remove the dup
|
||||
if instrs.len() >= 2
|
||||
|
@ -128,9 +129,9 @@ impl<'a> Compiler<'a> {
|
|||
))
|
||||
{
|
||||
// can't panic: checked that instrs.len() >= 2
|
||||
let i = self.func.chunk.instrs.pop().unwrap();
|
||||
self.func.chunk.instrs.pop().unwrap();
|
||||
self.func.chunk.instrs.push(i);
|
||||
let i = self.chunk.instrs.pop().unwrap();
|
||||
self.chunk.instrs.pop().unwrap();
|
||||
self.chunk.instrs.push(i);
|
||||
n -= 1;
|
||||
continue;
|
||||
}
|
||||
|
@ -160,11 +161,11 @@ impl<'a> Compiler<'a> {
|
|||
}
|
||||
|
||||
fn ip(&self) -> usize {
|
||||
self.func.chunk.instrs.len()
|
||||
self.chunk.instrs.len()
|
||||
}
|
||||
|
||||
fn update_instr(&mut self, n: usize, new: I) {
|
||||
self.func.chunk.instrs[n] = new;
|
||||
self.chunk.instrs[n] = new;
|
||||
}
|
||||
|
||||
fn begin_scope(&mut self) {
|
||||
|
@ -368,10 +369,10 @@ impl<'a> Compiler<'a> {
|
|||
}
|
||||
self.emit(I::Call(args.len() as u8));
|
||||
},
|
||||
Expr::Return(e) => {
|
||||
self.expr(e);
|
||||
self.emit(I::Return);
|
||||
},
|
||||
Expr::Return(e) => {
|
||||
self.expr(e);
|
||||
self.emit(I::Return);
|
||||
},
|
||||
Expr::Pipe(a, f) => {
|
||||
self.expr(a);
|
||||
self.expr(f);
|
||||
|
@ -428,7 +429,7 @@ impl<'a> Compiler<'a> {
|
|||
}
|
||||
|
||||
fn expr_try(&mut self, body: &Expr, catch_blocks: &[CatchBlock]) {
|
||||
let (idx, mut table) = self.func.chunk.begin_try_table(self.locals.len());
|
||||
let (idx, mut table) = self.chunk.begin_try_table(self.locals.len());
|
||||
|
||||
self.emit(I::BeginTry(Arg24::from_usize(idx)));
|
||||
self.expr(body);
|
||||
|
@ -463,7 +464,7 @@ impl<'a> Compiler<'a> {
|
|||
self.update_instr(addr, I::Jump(ip));
|
||||
}
|
||||
|
||||
self.func.chunk.finish_catch_table(idx, table);
|
||||
self.chunk.finish_catch_table(idx, table);
|
||||
}
|
||||
|
||||
fn expr_for(&mut self, name: &str, iter: &Expr, body: &Expr) {
|
||||
|
@ -480,7 +481,9 @@ impl<'a> Compiler<'a> {
|
|||
let start = self.ip();
|
||||
|
||||
// call iterator and jump if nil, otherwise store
|
||||
let j1 = self.emit(I::IterNext(Arg24::from_usize(0)));
|
||||
self.emit(I::Dup);
|
||||
self.emit(I::Call(0));
|
||||
let j1 = self.emit(I::IterTest(Arg24::from_usize(0)));
|
||||
self.store_local(local);
|
||||
|
||||
// body
|
||||
|
@ -490,7 +493,7 @@ impl<'a> Compiler<'a> {
|
|||
// end loop
|
||||
self.emit(I::Jump(Arg24::from_usize(start)));
|
||||
|
||||
self.update_instr(j1, I::IterNext(Arg24::from_usize(self.ip())));
|
||||
self.update_instr(j1, I::IterTest(Arg24::from_usize(self.ip())));
|
||||
self.end_scope();
|
||||
self.emit(I::Nil);
|
||||
}
|
||||
|
|
|
@ -15,13 +15,14 @@ pub mod symbol;
|
|||
|
||||
pub mod ast;
|
||||
pub mod value;
|
||||
pub mod gc;
|
||||
pub mod chunk;
|
||||
pub mod compiler;
|
||||
|
||||
pub use parser::BlockParser as Parser;
|
||||
pub use vm::Vm;
|
||||
|
||||
pub use parser_util::{parse_int, parse_float, parse_str_escapes};
|
||||
|
||||
type LexResult<'input> = Result<
|
||||
(usize, Token<'input>, usize),
|
||||
ParseError<usize, Token<'input>, parser_util::ParseError>
|
||||
|
|
|
@ -26,7 +26,6 @@ match {
|
|||
"false",
|
||||
"nil",
|
||||
// kw variables
|
||||
"const",
|
||||
"global",
|
||||
"var",
|
||||
// kw logic
|
||||
|
@ -117,9 +116,8 @@ AssignOp: Option<BinaryOp> = {
|
|||
"&=" => Some(BinaryOp::Append),
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// operations
|
||||
// logical ops
|
||||
//
|
||||
|
||||
// or
|
||||
|
@ -140,12 +138,29 @@ UnaryNot: Box<Expr<'input>> = {
|
|||
Pipe,
|
||||
}
|
||||
|
||||
// |
|
||||
//
|
||||
// pipe
|
||||
//
|
||||
|
||||
Pipe: Box<Expr<'input>> = {
|
||||
<l:Pipe> "|" <r:BinaryCompare> => Box::new(Expr::Pipe(l, r)),
|
||||
<l:Pipe> "|" <r:Lambda> => Box::new(Expr::Pipe(l, r)),
|
||||
Lambda,
|
||||
}
|
||||
|
||||
//
|
||||
// lambda
|
||||
//
|
||||
|
||||
Lambda: Box<Expr<'input>> = {
|
||||
"\\" <xs:IdentList> "->" <e:BinaryCompare> => Box::new(Expr::Lambda(xs, e)),
|
||||
":" <e:BinaryCompare> => Box::new(Expr::Lambda(vec!["$"], e)),
|
||||
"::" <e:BinaryCompare> => Box::new(Expr::Lambda(vec!["$", "$$"], e)),
|
||||
BinaryCompare,
|
||||
}
|
||||
|
||||
//
|
||||
// operations
|
||||
//
|
||||
|
||||
// == != > < >= <=
|
||||
BinaryCompare: Box<Expr<'input>> = {
|
||||
|
@ -250,7 +265,7 @@ UnaryMinus2: Box<Expr<'input>> = {
|
|||
|
||||
// function call
|
||||
FunctionCall: Box<Expr<'input>> = {
|
||||
<l:FieldAccess> "(" <r:ExprList> ")" => Box::new(Expr::FnCall(l, r)),
|
||||
<l:FunctionCall> "(" <r:ExprList> ")" => Box::new(Expr::FnCall(l, r)),
|
||||
FieldAccess,
|
||||
}
|
||||
|
||||
|
@ -277,8 +292,7 @@ TermNotIdent: Box<Expr<'input>> = {
|
|||
"[" <ExprList> "]" => Box::new(Expr::List(<>)),
|
||||
"{" <TableItems> "}" => Box::new(Expr::Table(<>)),
|
||||
"$" => Box::new(Expr::Ident("$")),
|
||||
":" "(" <e:Block> ")" => Box::new(Expr::Lambda(vec!["$"], e)),
|
||||
"\\" <xs:IdentList> "->" <e:Term> => Box::new(Expr::Lambda(xs, e)),
|
||||
"$$" => Box::new(Expr::Ident("$$")),
|
||||
|
||||
"do" <Block> "end" => <>,
|
||||
"if" <IfStmtChain> => <>,
|
||||
|
|
|
@ -9,17 +9,31 @@ struct SymbolTable {
|
|||
}
|
||||
|
||||
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_NIL: Symbol = symbol!(nil);
|
||||
pub static ref SYM_BOOL: Symbol = symbol!(bool);
|
||||
pub static ref SYM_SYMBOL: Symbol = symbol!(symbol);
|
||||
pub static ref SYM_INT: Symbol = symbol!(int);
|
||||
pub static ref SYM_RATIO: Symbol = symbol!(ratio);
|
||||
pub static ref SYM_FLOAT: Symbol = symbol!(float);
|
||||
pub static ref SYM_COMPLEX: Symbol = symbol!(complex);
|
||||
pub static ref SYM_RANGE: Symbol = symbol!(range);
|
||||
pub static ref SYM_CELL: Symbol = symbol!(cell);
|
||||
pub static ref SYM_STRING: Symbol = symbol!(string);
|
||||
pub static ref SYM_LIST: Symbol = symbol!(list);
|
||||
pub static ref SYM_TABLE: Symbol = symbol!(table);
|
||||
pub static ref SYM_FUNCTION: Symbol = symbol!(function);
|
||||
pub static ref SYM_NATIVE_FUNC: Symbol = symbol!(native_func);
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
static TABLE: OnceLock<Mutex<SymbolTable>> = OnceLock::new();
|
||||
|
@ -93,12 +107,12 @@ impl Symbol {
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! symbol {
|
||||
($sym:ident) => {
|
||||
$crate::symbol::Symbol::get(stringify!($sym))
|
||||
};
|
||||
($sym:literal) => {
|
||||
$crate::symbol::Symbol::get($sym)
|
||||
};
|
||||
($sym:ident) => {
|
||||
$crate::symbol::Symbol::get(stringify!($sym))
|
||||
};
|
||||
($sym:literal) => {
|
||||
$crate::symbol::Symbol::get($sym)
|
||||
};
|
||||
}
|
||||
|
||||
pub use symbol;
|
||||
|
|
|
@ -20,25 +20,33 @@ impl Exception {
|
|||
Self { ty, msg: Some(msg), data: None }
|
||||
}
|
||||
|
||||
pub fn new_with_data(ty: Symbol, data: Value) -> Self {
|
||||
Self { ty, msg: None, data: Some(data) }
|
||||
}
|
||||
|
||||
pub fn new_with_msg_data(ty: Symbol, msg: Rc<str>, data: Value) -> Self {
|
||||
Self { ty, msg: Some(msg), data: Some(data) }
|
||||
}
|
||||
|
||||
pub fn from_table(table: &Rc<RefCell<HashMap<HashValue, Value>>>) -> Option<Self> {
|
||||
let table = table.borrow();
|
||||
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,
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -59,11 +67,11 @@ impl Exception {
|
|||
|
||||
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())
|
||||
}
|
||||
if let Some(msg) = &self.msg {
|
||||
write!(f, "{}: {}", self.ty.name(), msg)
|
||||
} else {
|
||||
write!(f, "{}", self.ty.name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,7 +96,7 @@ pub use exception;
|
|||
#[macro_export]
|
||||
macro_rules! throw {
|
||||
($($args:tt)*) => {
|
||||
return Err($crate::value::exception::exception!($($args)*))
|
||||
return Err($crate::value::exception::exception!($($args)*))
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -4,69 +4,97 @@ use crate::{chunk::Chunk, Vm};
|
|||
|
||||
use super::{Value, exception::Result};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Function {
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct FuncAttrs {
|
||||
pub arity: usize,
|
||||
pub chunk: Chunk,
|
||||
pub variadic: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Function {
|
||||
pub attrs: FuncAttrs,
|
||||
pub chunk: Rc<Chunk>,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn new(chunk: Rc<Chunk>, arity: usize) -> Self {
|
||||
Self { chunk, attrs: FuncAttrs { arity, variadic: false } }
|
||||
}
|
||||
pub fn new_variadic(chunk: Rc<Chunk>, arity: usize) -> Self {
|
||||
Self { chunk, attrs: FuncAttrs { arity, variadic: true } }
|
||||
}
|
||||
}
|
||||
|
||||
type FnNative = Box<dyn Fn(&mut Vm, Vec<Value>) -> Result<Value>>;
|
||||
|
||||
pub struct NativeFunc {
|
||||
pub arity: usize,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub f: Box<dyn Fn(&mut Vm, Vec<Value>) -> Result<Value>>,
|
||||
pub attrs: FuncAttrs,
|
||||
pub func: FnNative,
|
||||
}
|
||||
|
||||
impl NativeFunc {
|
||||
pub fn new(func: FnNative, arity: usize) -> Self {
|
||||
Self { func, attrs: FuncAttrs { arity, variadic: false } }
|
||||
}
|
||||
pub fn new_variadic(func: FnNative, arity: usize) -> Self {
|
||||
Self { func, attrs: FuncAttrs { arity, variadic: true } }
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
.field("attrs", &self.attrs)
|
||||
.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)?;
|
||||
writeln!(w, "{} ({}{})",
|
||||
Value::Function(f.clone()),
|
||||
f.attrs.arity,
|
||||
if f.attrs.variadic { ".." } else { "" })?;
|
||||
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(())
|
||||
for c in &f.chunk.consts {
|
||||
if let Value::Function(f) = c {
|
||||
disasm_recursive(f, w)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
125
talc-lang/src/value/index.rs
Normal file
125
talc-lang/src/value/index.rs
Normal file
|
@ -0,0 +1,125 @@
|
|||
use crate::{symbol::{SYM_INDEX_ERROR, SYM_TYPE_ERROR}, value::{cell_take, function::NativeFunc}, vmcalliter, Vm};
|
||||
|
||||
use super::{Value, exception::{Result, throw}, range::RangeType};
|
||||
|
||||
impl Value {
|
||||
pub fn index(&self, idx: Self) -> Result<Self> {
|
||||
use Value as V;
|
||||
match (self, idx) {
|
||||
(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!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
|
||||
}
|
||||
},
|
||||
(V::Range(r), V::Int(i)) => {
|
||||
if i >= 0 && (
|
||||
r.ty == RangeType::Endless
|
||||
|| i < r.stop
|
||||
|| (r.ty == RangeType::Closed && i == r.stop)
|
||||
) {
|
||||
Ok((r.start + i).into())
|
||||
} else {
|
||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
|
||||
}
|
||||
},
|
||||
(V::Table(t), i) if i.hashable() => {
|
||||
let t = t.borrow();
|
||||
let i = i.try_into()?;
|
||||
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
|
||||
},
|
||||
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() {
|
||||
let col = col.clone();
|
||||
let func = move |vm: &mut Vm, _| {
|
||||
match vmcalliter!(vm; ii.clone())? {
|
||||
Some(i) => Ok(col.index(cell_take(i))?.to_cell()),
|
||||
None => Ok(Value::Nil)
|
||||
}
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(func), 0).into())
|
||||
} else {
|
||||
throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store_index(&self, vm: &mut Vm, idx: Self, val: Self) -> Result<()> {
|
||||
use Value as V;
|
||||
match (self, idx) {
|
||||
(V::List(l), V::Int(i)) => {
|
||||
let mut l = l.borrow_mut();
|
||||
if i >= 0 && (i as usize) < l.len() {
|
||||
l[i as usize] = val;
|
||||
Ok(())
|
||||
} else {
|
||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
|
||||
}
|
||||
},
|
||||
(V::Table(t), i) if i.hashable() => {
|
||||
let mut t = t.borrow_mut();
|
||||
let i = i.try_into()?;
|
||||
t.insert(i, val);
|
||||
Ok(())
|
||||
},
|
||||
(V::List(t), V::Range(r)) => {
|
||||
let iter = val.to_iter_function()?;
|
||||
let mut vals = Vec::new();
|
||||
while let Some(v) = vmcalliter!(vm; iter.clone())? {
|
||||
vals.push(cell_take(v));
|
||||
}
|
||||
let mut tm = t.borrow_mut();
|
||||
match r.ty {
|
||||
RangeType::Open => {
|
||||
if r.start < 0 || r.start > tm.len() as i64 {
|
||||
throw!(*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for list of length {}", r.stop, tm.len())
|
||||
}
|
||||
if r.stop < 0 || r.stop > tm.len() as i64 {
|
||||
throw!(*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for list of length {}", r.stop, tm.len())
|
||||
}
|
||||
let end = tm.split_off(r.stop as usize);
|
||||
tm.truncate(r.start as usize);
|
||||
tm.extend(vals.into_iter().chain(end))
|
||||
},
|
||||
RangeType::Closed => {
|
||||
if r.start < 0 || r.start > tm.len() as i64 {
|
||||
throw!(*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for list of length {}", r.stop, tm.len())
|
||||
}
|
||||
if r.stop < 0 || r.stop >= tm.len() as i64 {
|
||||
throw!(*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for list of length {}", r.stop, tm.len())
|
||||
}
|
||||
let end = tm.split_off(r.stop as usize + 1);
|
||||
tm.truncate(r.start as usize);
|
||||
tm.extend(vals.into_iter().chain(end))
|
||||
},
|
||||
RangeType::Endless => {
|
||||
if r.start < 0 || r.start > tm.len() as i64 {
|
||||
throw!(*SYM_INDEX_ERROR,
|
||||
"index {} out of bounds for list of length {}", r.stop, tm.len())
|
||||
}
|
||||
tm.truncate(r.start as usize);
|
||||
tm.extend(vals)
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() {
|
||||
let val = val.to_iter_function()?;
|
||||
while let Some(i) = vmcalliter!(vm; ii.clone())? {
|
||||
let Some(v) = vmcalliter!(vm; val.clone())? else {
|
||||
throw!(*SYM_INDEX_ERROR, "not enough values provided for store index")
|
||||
};
|
||||
col.store_index(vm, cell_take(i), cell_take(v))?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
use std::{rc::Rc, cell::RefCell, collections::HashMap, hash::Hash, fmt::Display};
|
||||
use std::{cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, rc::Rc};
|
||||
|
||||
use num_complex::Complex64;
|
||||
use num_rational::Rational64;
|
||||
pub use num_complex::Complex64;
|
||||
pub use num_rational::Rational64;
|
||||
|
||||
use crate::symbol::{SYM_HASH_ERROR, Symbol};
|
||||
use crate::symbol::{Symbol, SYM_HASH_ERROR};
|
||||
|
||||
use self::{range::{Range, RangeType}, function::{Function, NativeFunc}, exception::{Exception, throw}};
|
||||
|
||||
|
@ -11,12 +11,14 @@ pub mod exception;
|
|||
pub mod function;
|
||||
pub mod ops;
|
||||
pub mod range;
|
||||
pub mod index;
|
||||
|
||||
type RcList = Rc<RefCell<Vec<Value>>>;
|
||||
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub enum Value {
|
||||
#[default]
|
||||
Nil,
|
||||
Bool(bool),
|
||||
Symbol(Symbol),
|
||||
|
@ -27,6 +29,7 @@ pub enum Value {
|
|||
Ratio(Rational64),
|
||||
Complex(Complex64),
|
||||
|
||||
Cell(Rc<RefCell<Value>>),
|
||||
String(Rc<str>),
|
||||
List(RcList),
|
||||
Table(RcTable),
|
||||
|
@ -37,62 +40,82 @@ pub enum Value {
|
|||
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 {
|
||||
Self::Nil => write!(f, "nil"),
|
||||
Self::Bool(b) => write!(f, "{b}"),
|
||||
Self::Symbol(s) => write!(f, ":{}", s.name()),
|
||||
Self::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}"),
|
||||
Self::Int(n) => write!(f, "{n}"),
|
||||
Self::Float(x) => write!(f, "{x:?}"),
|
||||
Self::Ratio(r) => write!(f, "{}/{}", r.numer(), r.denom()),
|
||||
Self::Complex(z) => write!(f, "{z}"),
|
||||
|
||||
Value::String(s) => {
|
||||
if f.alternate() {
|
||||
write!(f, "{s:?}")
|
||||
} else {
|
||||
write!(f, "{s}")
|
||||
}
|
||||
},
|
||||
Value::List(l) => {
|
||||
Self::Cell(v) if f.alternate() => write!(f, "cell({:#})", v.borrow()),
|
||||
Self::Cell(v) => write!(f, "{})", v.borrow()),
|
||||
|
||||
Self::String(s) if f.alternate() => write!(f, "{s:?}"),
|
||||
Self::String(s) => write!(f, "{s}"),
|
||||
|
||||
Self::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, "{item:#}")?;
|
||||
}
|
||||
write!(f, "]")
|
||||
},
|
||||
Value::Table(t) => {
|
||||
Self::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, "({:#}) = {v:#}", k.0)?;
|
||||
}
|
||||
write!(f, " }}")
|
||||
},
|
||||
Value::Function(g)
|
||||
Self::Function(g)
|
||||
=> write!(f, "<function {:?}>", Rc::as_ptr(g)),
|
||||
Value::NativeFunc(g)
|
||||
Self::NativeFunc(g)
|
||||
=> write!(f, "<native function {:?}>", Rc::as_ptr(g)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn get_type(&self) -> Symbol {
|
||||
use crate::symbol::*;
|
||||
match self {
|
||||
Value::Nil => *SYM_NIL,
|
||||
Value::Bool(_) => *SYM_BOOL,
|
||||
Value::Symbol(_) => *SYM_SYMBOL,
|
||||
Value::Range(_) => *SYM_RANGE,
|
||||
Value::Int(_) => *SYM_INT,
|
||||
Value::Float(_) => *SYM_FLOAT,
|
||||
Value::Ratio(_) => *SYM_RATIO,
|
||||
Value::Complex(_) => *SYM_COMPLEX,
|
||||
Value::Cell(_) => *SYM_CELL,
|
||||
Value::String(_) => *SYM_STRING,
|
||||
Value::List(_) => *SYM_LIST,
|
||||
Value::Table(_) => *SYM_TABLE,
|
||||
Value::Function(_) => *SYM_FUNCTION,
|
||||
Value::NativeFunc(_) => *SYM_NATIVE_FUNC,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hashable(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Value::Nil | Value::Bool(_) | Value::Symbol(_)
|
||||
| Value::Int(_) | Value::Ratio(_) | Value::String(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct HashValue(Value);
|
||||
|
@ -102,15 +125,10 @@ 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"),
|
||||
if !value.hashable() {
|
||||
throw!(*SYM_HASH_ERROR, "value {value:#} cannot be hashed")
|
||||
}
|
||||
Ok(Self(value))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,43 +152,43 @@ impl Hash for HashValue {
|
|||
}
|
||||
|
||||
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()) }
|
||||
}
|
||||
};
|
||||
($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);
|
||||
|
@ -185,6 +203,12 @@ impl_from!(Vec<Value>, List, rcref);
|
|||
impl_from!(Function, Function, rc);
|
||||
impl_from!(NativeFunc, NativeFunc, rc);
|
||||
impl_from!(Rc<str>, String);
|
||||
impl_from!(String, String, into);
|
||||
impl_from!(&str, String, into);
|
||||
impl_from!(Box<str>, String, into);
|
||||
impl_from!(String, String, into);
|
||||
impl_from!(RefCell<Value>, Cell, rc);
|
||||
|
||||
#[inline]
|
||||
pub fn cell_take<T: Clone>(cell: Rc<RefCell<T>>) -> T {
|
||||
Rc::unwrap_or_clone(cell).into_inner()
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::{ops::{Neg, Add, Sub, Mul, Div}, cmp::Ordering, cell::RefCell};
|
||||
use std::{cell::RefCell, cmp::Ordering, ops::{Add, Div, Mul, Neg, Sub}, rc::Rc};
|
||||
|
||||
use num_complex::Complex64;
|
||||
use num_rational::Rational64;
|
||||
|
||||
use crate::{symbol::{SYM_INDEX_ERROR, SYM_STOP_ITERATOR, SYM_TYPE_ERROR}, value::range::RangeType, Vm};
|
||||
use crate::{symbol::SYM_TYPE_ERROR, value::range::RangeType, Vm};
|
||||
|
||||
use super::{Value, exception::{Result, throw}, range::Range, function::NativeFunc, HashValue};
|
||||
use super::{exception::{throw, Result}, function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value};
|
||||
|
||||
impl Value {
|
||||
pub fn truthy(&self) -> bool {
|
||||
|
@ -19,6 +19,7 @@ impl Value {
|
|||
Value::Complex(c) => !(c.re == 0.0 && c.im == 0.0 || c.is_nan()),
|
||||
Value::String(s) => s.len() > 0,
|
||||
Value::List(l) => l.borrow().len() > 0,
|
||||
Value::Cell(v) => v.borrow().truthy(),
|
||||
|
||||
Value::Symbol(_) | Value::Table(_)
|
||||
| Value::Function(_) | Value::NativeFunc(_) => true,
|
||||
|
@ -32,7 +33,7 @@ fn ratio_to_f64(r: Rational64) -> f64 {
|
|||
}
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
fn promote(a: Value, b: Value) -> (Value, Value) {
|
||||
pub fn promote(a: Value, b: Value) -> (Value, Value) {
|
||||
use Value as V;
|
||||
match (&a, &b) {
|
||||
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
|
||||
|
@ -113,6 +114,9 @@ impl Div<Value> for Value {
|
|||
fn div(self, rhs: Value) -> Self::Output {
|
||||
use Value as V;
|
||||
match promote(self, rhs) {
|
||||
(_, V::Int(0)) => throw!(*SYM_TYPE_ERROR, "integer division by 0"),
|
||||
(_, V::Ratio(r)) if *r.numer() == 0 && *r.denom() != 0
|
||||
=> throw!(*SYM_TYPE_ERROR, "integer division by 0"),
|
||||
(V::Int(x), V::Int(y)) => Ok(V::Ratio(Rational64::new(x, y))),
|
||||
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x / y)),
|
||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
|
||||
|
@ -123,26 +127,26 @@ impl Div<Value> for Value {
|
|||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
const fn ipow(n: i64, p: u64) -> i64 {
|
||||
fn ipow(n: i64, p: u64) -> Result<i64> {
|
||||
match (n, p) {
|
||||
(0, 0) => panic!("power 0^0"),
|
||||
(0, _) => 0,
|
||||
(_, 0) => 1,
|
||||
(0, 0) => throw!(*SYM_TYPE_ERROR, "integer 0 raised to power 0"),
|
||||
(0, _) => Ok(0),
|
||||
(_, 0) => Ok(1),
|
||||
(n, p) if p > u32::MAX as u64 => {
|
||||
let (lo, hi) = (p as u32, (p >> 32) as u32);
|
||||
let (a, b) = (n.pow(lo), n.pow(hi));
|
||||
a * b.pow(0x1_0000).pow(0x1_0000)
|
||||
Ok(a * b.pow(0x1_0000).pow(0x1_0000))
|
||||
}
|
||||
(n, p) => n.pow(p as u32),
|
||||
(n, p) => Ok(n.pow(p as u32)),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
const fn rpow(n: i64, d: i64, p: i64) -> (i64, i64) {
|
||||
match p {
|
||||
0.. => (ipow(n, p as u64), ipow(d, p as u64)),
|
||||
_ => (ipow(d, (-p) as u64), ipow(n, (-p) as u64)),
|
||||
}
|
||||
fn rpow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> {
|
||||
Ok(match p {
|
||||
0.. => (ipow(n, p as u64)?, ipow(d, p as u64)?),
|
||||
_ => (ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?),
|
||||
})
|
||||
}
|
||||
|
||||
impl Value {
|
||||
|
@ -171,11 +175,11 @@ impl Value {
|
|||
pub fn pow(self, rhs: Value) -> Result<Self> {
|
||||
use Value as V;
|
||||
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
|
||||
return Ok(V::Ratio(rpow(*(*x).numer(), *(*x).denom(), *y).into()));
|
||||
return Ok(V::Ratio(rpow(*(*x).numer(), *(*x).denom(), *y)?.into()));
|
||||
}
|
||||
match promote(self, rhs) {
|
||||
(V::Int(x), V::Int(y))
|
||||
=> Ok(V::Ratio(rpow(x, 1, y).into())),
|
||||
=> Ok(V::Ratio(rpow(x, 1, y)?.into())),
|
||||
(V::Float(x), V::Float(y))
|
||||
=> Ok(V::Float(x.powf(y))),
|
||||
(V::Ratio(x), V::Ratio(y))
|
||||
|
@ -214,6 +218,7 @@ impl PartialEq for Value {
|
|||
(V::String(a), V::String(b)) => *a == *b,
|
||||
(V::List(a), V::List(b)) => *a.borrow() == *b.borrow(),
|
||||
(V::Symbol(a), V::Symbol(b)) => a == b,
|
||||
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
|
||||
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
|
||||
(Rty::Open, Rty::Open) => a.start == b.start && a.stop == b.stop,
|
||||
(Rty::Closed, Rty::Closed) => a.start == b.start && a.stop == b.stop,
|
||||
|
@ -259,59 +264,6 @@ impl Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn index(&self, idx: Self) -> Result<Self> {
|
||||
use Value as V;
|
||||
match (self, idx) {
|
||||
(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!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
|
||||
}
|
||||
},
|
||||
(V::Range(r), V::Int(i)) => {
|
||||
if i >= 0 && (
|
||||
r.ty == RangeType::Endless
|
||||
|| i < r.stop
|
||||
|| (r.ty == RangeType::Closed && i == r.stop)
|
||||
) {
|
||||
Ok((r.start + i).into())
|
||||
} else {
|
||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
|
||||
}
|
||||
},
|
||||
(V::Table(t), i) => {
|
||||
let t = t.borrow();
|
||||
let i = i.try_into()?;
|
||||
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
|
||||
},
|
||||
(col, idx) => throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store_index(&self, idx: Self, val: Self) -> Result<()> {
|
||||
use Value as V;
|
||||
match (self, idx) {
|
||||
(V::List(l), V::Int(i)) => {
|
||||
let mut l = l.borrow_mut();
|
||||
if i >= 0 && (i as usize) < l.len() {
|
||||
l[i as usize] = val;
|
||||
Ok(())
|
||||
} else {
|
||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
|
||||
}
|
||||
},
|
||||
(V::Table(t), i) => {
|
||||
let mut t = t.borrow_mut();
|
||||
let i = i.try_into()?;
|
||||
t.insert(i, val);
|
||||
Ok(())
|
||||
},
|
||||
(col, idx) => throw!(*SYM_INDEX_ERROR, "cannot index {col:#} with {idx:#}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn concat(&self, other: &Self) -> Result<Self> {
|
||||
use Value as V;
|
||||
match (self, other) {
|
||||
|
@ -334,7 +286,7 @@ impl Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn append(&self, val: Self) -> Result<Self> {
|
||||
pub fn append(&self, val: Self) -> Result<Self> {
|
||||
use Value as V;
|
||||
match self {
|
||||
V::List(list) => {
|
||||
|
@ -343,8 +295,8 @@ impl Value {
|
|||
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) {
|
||||
|
@ -363,6 +315,25 @@ impl Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_cell(self) -> Self {
|
||||
Value::Cell(Rc::new(RefCell::new(self)))
|
||||
}
|
||||
|
||||
pub fn iter_unpack(self) -> Result<Option<Rc<RefCell<Self>>>> {
|
||||
match self {
|
||||
Self::Nil => Ok(None),
|
||||
Self::Cell(v) => Ok(Some(v)),
|
||||
_ => throw!(*SYM_TYPE_ERROR, "expected iterator to return cell or nil")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_pack(v: Option<Self>) -> Self {
|
||||
match v {
|
||||
Some(v) => v.to_cell(),
|
||||
None => Value::Nil,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_iter_function(self) -> Result<Self> {
|
||||
match self {
|
||||
Self::Function(_) | Self::NativeFunc(_) => Ok(self),
|
||||
|
@ -370,15 +341,25 @@ impl Value {
|
|||
let range_iter = RefCell::new(range.into_iter());
|
||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||
if let Some(v) = range_iter.borrow_mut().next() {
|
||||
Ok(v.into())
|
||||
Ok(Value::from(v).to_cell())
|
||||
} else {
|
||||
throw!(*SYM_STOP_ITERATOR)
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
};
|
||||
Ok(NativeFunc {
|
||||
arity: 0,
|
||||
f: Box::new(f),
|
||||
}.into())
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
},
|
||||
Self::String(s) => {
|
||||
let byte_pos = RefCell::new(0);
|
||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||
let pos = *byte_pos.borrow();
|
||||
if let Some(v) = &s[pos..].chars().next() {
|
||||
*byte_pos.borrow_mut() += v.len_utf8();
|
||||
Ok(Value::from(v.to_string()).to_cell())
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
},
|
||||
Self::List(list) => {
|
||||
let idx = RefCell::new(0);
|
||||
|
@ -386,32 +367,34 @@ impl Value {
|
|||
let i = *idx.borrow();
|
||||
if let Some(v) = list.borrow().get(i) {
|
||||
*idx.borrow_mut() += 1;
|
||||
Ok(v.clone())
|
||||
Ok(v.clone().to_cell())
|
||||
} else {
|
||||
throw!(*SYM_STOP_ITERATOR)
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
};
|
||||
Ok(NativeFunc {
|
||||
arity: 0,
|
||||
f: Box::new(f),
|
||||
}.into())
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
},
|
||||
Self::Table(table) => {
|
||||
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
|
||||
let keys = RefCell::new(keys.into_iter());
|
||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||
if let Some(v) = keys.borrow_mut().next() {
|
||||
Ok(v.into_inner())
|
||||
Ok(v.into_inner().to_cell())
|
||||
} else {
|
||||
throw!(*SYM_STOP_ITERATOR)
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
};
|
||||
Ok(NativeFunc {
|
||||
arity: 0,
|
||||
f: Box::new(f),
|
||||
}.into())
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
},
|
||||
_ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn func_attrs(&self) -> Option<FuncAttrs> {
|
||||
match self {
|
||||
Value::Function(f) => Some(f.attrs),
|
||||
Value::NativeFunc(f) => Some(f.attrs),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,3 +41,4 @@ impl IntoIterator for Range {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{cmp::Ordering, collections::HashMap, rc::Rc, sync::{atomic::AtomicBool, Arc}};
|
||||
|
||||
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}};
|
||||
use crate::{ast::{BinaryOp, UnaryOp}, chunk::Instruction, symbol::{Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR}, value::{cell_take, exception::{throw, Exception, Result}, function::{FuncAttrs, Function, NativeFunc}, Value}};
|
||||
|
||||
struct TryFrame { idx: usize, stack_len: usize }
|
||||
|
||||
|
@ -28,7 +28,7 @@ pub struct Vm {
|
|||
call_stack: Vec<CallFrame>,
|
||||
stack_max: usize,
|
||||
globals: HashMap<Symbol, Value>,
|
||||
interrupt: Arc<AtomicBool>,
|
||||
interrupt: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
pub fn binary_op(o: BinaryOp, a: Value, b: Value) -> Result<Value> {
|
||||
|
@ -61,6 +61,49 @@ pub fn unary_op(o: UnaryOp, a: Value) -> Result<Value> {
|
|||
}
|
||||
}
|
||||
|
||||
enum CallOutcome {
|
||||
Call(Vec<Value>),
|
||||
Partial(Value),
|
||||
}
|
||||
|
||||
fn get_call_outcome(mut args: Vec<Value>) -> Result<CallOutcome> {
|
||||
let f = &args[0];
|
||||
let Some(attrs) = f.func_attrs() else {
|
||||
throw!(*SYM_TYPE_ERROR, "cannot call non-function {f:#}")
|
||||
};
|
||||
let argc = args.len() - 1;
|
||||
if attrs.variadic && argc >= attrs.arity {
|
||||
let vararg = args.split_off(attrs.arity + 1);
|
||||
args.push(vararg.into());
|
||||
Ok(CallOutcome::Call(args))
|
||||
} else if argc == attrs.arity {
|
||||
Ok(CallOutcome::Call(args))
|
||||
} else if argc > attrs.arity {
|
||||
throw!(*SYM_TYPE_ERROR, "too many arguments for function")
|
||||
} else {
|
||||
let remaining = attrs.arity - argc;
|
||||
let f = f.clone();
|
||||
let nf = move |vm: &mut Vm, inner_args: Vec<Value>| {
|
||||
let mut ia = inner_args.into_iter();
|
||||
ia.next();
|
||||
let mut args: Vec<Value> = args.clone().into_iter().chain(ia).collect();
|
||||
if attrs.variadic {
|
||||
let Value::List(varargs) = args.pop()
|
||||
.expect("did not receive vararg") else {
|
||||
panic!("did not receive vararg")
|
||||
};
|
||||
args.extend(cell_take(varargs));
|
||||
}
|
||||
vm.call_value(f.clone(), args)
|
||||
};
|
||||
let nf = NativeFunc {
|
||||
attrs: FuncAttrs { arity: remaining, variadic: attrs.variadic },
|
||||
func: Box::new(nf),
|
||||
};
|
||||
Ok(CallOutcome::Partial(nf.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
pub fn new(stack_max: usize) -> Self {
|
||||
Self {
|
||||
|
@ -68,13 +111,13 @@ impl Vm {
|
|||
call_stack: Vec::with_capacity(16),
|
||||
globals: HashMap::with_capacity(16),
|
||||
stack_max,
|
||||
interrupt: Arc::new(AtomicBool::new(false)),
|
||||
interrupt: Arc::new(AtomicBool::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_interrupt(&self) -> Arc<AtomicBool> {
|
||||
self.interrupt.clone()
|
||||
}
|
||||
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);
|
||||
|
@ -88,246 +131,47 @@ impl Vm {
|
|||
self.globals.get(&name)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn push(&mut self, v: Value) {
|
||||
self.stack.push(v);
|
||||
pub fn globals(&self) -> &HashMap<Symbol, Value> {
|
||||
&self.globals
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pop(&mut self) -> Value {
|
||||
self.stack.pop().expect("temporary stack underflow")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pop_n(&mut self, n: usize) -> Vec<Value> {
|
||||
let res = self.stack.split_off(self.stack.len() - n);
|
||||
assert!(res.len() == n, "temporary stack underflow");
|
||||
res
|
||||
}
|
||||
|
||||
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!(*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)),
|
||||
// [] -> [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(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();
|
||||
let Value::List(list) = list else { panic!("not a list") };
|
||||
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 {
|
||||
let v = self.pop();
|
||||
let k = self.pop();
|
||||
table.insert(k.try_into()?, v);
|
||||
}
|
||||
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();
|
||||
let Value::Table(table) = table else { panic!("not a table") };
|
||||
let mut table_ref = table.borrow_mut();
|
||||
for _ in 0..n {
|
||||
// can't panic: pop_n checked that ext would have len 2*n
|
||||
let v = ext.pop().unwrap();
|
||||
let k = ext.pop().unwrap();
|
||||
table_ref.insert(k.try_into()?, v);
|
||||
}
|
||||
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();
|
||||
let ct = self.pop();
|
||||
ct.store_index(idx, v.clone())?;
|
||||
self.push(v);
|
||||
},
|
||||
// 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] -> if i() succeeds then [i, i()], otherwise [] and ip = n
|
||||
I::IterNext(n) => {
|
||||
let v = &self.stack[self.stack.len() - 1];
|
||||
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),
|
||||
stack_len: self.stack.len()
|
||||
};
|
||||
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 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!(*SYM_TYPE_ERROR, "function call with wrong argument count");
|
||||
}
|
||||
|
||||
self.call_stack.push(std::mem::take(frame));
|
||||
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!(*SYM_TYPE_ERROR, "function call with wrong argument count");
|
||||
}
|
||||
|
||||
if self.call_stack.len() + 1 >= self.stack_max {
|
||||
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
|
||||
}
|
||||
self.call_stack.push(std::mem::take(frame));
|
||||
|
||||
let func = func.clone();
|
||||
*frame = CallFrame::new(func, args);
|
||||
} else {
|
||||
throw!(*SYM_TYPE_ERROR, "attempt to call non-function {func}");
|
||||
}
|
||||
},
|
||||
// [v] -> [], return v
|
||||
I::Return if frame.root => {
|
||||
return Ok(Some(self.pop()));
|
||||
},
|
||||
// [v] -> [], return v
|
||||
I::Return => {
|
||||
*frame = self.call_stack.pop().expect("no root frame");
|
||||
},
|
||||
pub fn call_value(&mut self, value: Value, args: Vec<Value>) -> Result<Value> {
|
||||
self.check_interrupt()?;
|
||||
match get_call_outcome(args)? {
|
||||
CallOutcome::Partial(v) => Ok(v),
|
||||
CallOutcome::Call(args) => match value {
|
||||
Value::Function(f) => self.run_function(f, args),
|
||||
Value::NativeFunc(f) => (f.func)(self, args),
|
||||
_ => unreachable!("already verified by calling get_call_type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
pub fn run_function(&mut self, func: Rc<Function>, args: Vec<Value>) -> Result<Value> {
|
||||
if func.attrs.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(func, args);
|
||||
frame.root = true;
|
||||
|
||||
loop {
|
||||
let instr = frame.func.chunk.instrs[frame.ip];
|
||||
frame.ip += 1;
|
||||
match self.run_instr(&mut frame, instr) {
|
||||
Ok(None) => (),
|
||||
Ok(Some(v)) => {
|
||||
self.stack.truncate(init_stack_len);
|
||||
return Ok(v)
|
||||
}
|
||||
Err(e) => {
|
||||
if let Err(e) = self.handle_exception(&mut frame, e) {
|
||||
self.stack.truncate(init_stack_len);
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_exception(&mut self, frame: &mut CallFrame, exc: Exception) -> Result<()> {
|
||||
|
@ -351,49 +195,265 @@ impl Vm {
|
|||
}
|
||||
}
|
||||
|
||||
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(func, args);
|
||||
frame.root = true;
|
||||
|
||||
loop {
|
||||
let instr = frame.func.chunk.instrs[frame.ip];
|
||||
frame.ip += 1;
|
||||
match self.run_instr(&mut frame, instr) {
|
||||
Ok(None) => (),
|
||||
Ok(Some(v)) => {
|
||||
self.stack.truncate(init_stack_len);
|
||||
return Ok(v)
|
||||
}
|
||||
Err(e) => {
|
||||
if let Err(e) = self.handle_exception(&mut frame, e) {
|
||||
self.stack.truncate(init_stack_len);
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn push(&mut self, v: Value) {
|
||||
self.stack.push(v);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pop(&mut self) -> Value {
|
||||
self.stack.pop().expect("temporary stack underflow")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pop_n(&mut self, n: usize) -> Vec<Value> {
|
||||
let res = self.stack.split_off(self.stack.len() - n);
|
||||
assert!(res.len() == n, "temporary stack underflow");
|
||||
res
|
||||
}
|
||||
|
||||
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!(*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)),
|
||||
// [] -> [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(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();
|
||||
let Value::List(list) = list else { panic!("not a list") };
|
||||
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 {
|
||||
let v = self.pop();
|
||||
let k = self.pop();
|
||||
table.insert(k.try_into()?, v);
|
||||
}
|
||||
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();
|
||||
let Value::Table(table) = table else { panic!("not a table") };
|
||||
let mut table_ref = table.borrow_mut();
|
||||
for _ in 0..n {
|
||||
// can't panic: pop_n checked that ext would have len 2*n
|
||||
let v = ext.pop().unwrap();
|
||||
let k = ext.pop().unwrap();
|
||||
table_ref.insert(k.try_into()?, v);
|
||||
}
|
||||
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();
|
||||
let ct = self.pop();
|
||||
ct.store_index(self, idx, v.clone())?;
|
||||
self.push(v);
|
||||
},
|
||||
// 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,cell(v)] -> [i,v]
|
||||
// [i,nil] -> [], ip = n
|
||||
I::IterTest(n) => {
|
||||
match self.pop() {
|
||||
Value::Cell(c) => self.push(cell_take(c)),
|
||||
Value::Nil => {
|
||||
self.pop();
|
||||
frame.ip = usize::from(n)
|
||||
},
|
||||
v => throw!(*SYM_TYPE_ERROR, "iterator returned invalid value {v}")
|
||||
}
|
||||
},
|
||||
// try_frames.push(t, stack.len())
|
||||
I::BeginTry(t) => {
|
||||
let tryframe = TryFrame {
|
||||
idx: usize::from(t),
|
||||
stack_len: self.stack.len()
|
||||
};
|
||||
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) => {
|
||||
let n = usize::from(n);
|
||||
|
||||
let args = self.pop_n(n + 1);
|
||||
|
||||
let args = match get_call_outcome(args)? {
|
||||
CallOutcome::Call(args) => args,
|
||||
CallOutcome::Partial(v) => {
|
||||
self.push(v);
|
||||
return Ok(None)
|
||||
}
|
||||
};
|
||||
|
||||
if let Value::NativeFunc(nf) = &args[0] {
|
||||
let nf = nf.clone();
|
||||
|
||||
self.call_stack.push(std::mem::take(frame));
|
||||
let res = (nf.func)(self, args)?;
|
||||
*frame = self.call_stack.pop().expect("no frame to pop");
|
||||
|
||||
self.stack.push(res);
|
||||
} else if let Value::Function(func) = &args[0] {
|
||||
|
||||
if self.call_stack.len() + 1 >= self.stack_max {
|
||||
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
|
||||
}
|
||||
self.call_stack.push(std::mem::take(frame));
|
||||
|
||||
let func = func.clone();
|
||||
*frame = CallFrame::new(func, args);
|
||||
} else {
|
||||
unreachable!("already verified by calling get_call_type");
|
||||
}
|
||||
},
|
||||
// [v] -> [], return v
|
||||
I::Return if frame.root => {
|
||||
return Ok(Some(self.pop()));
|
||||
},
|
||||
// [v] -> [], return v
|
||||
I::Return => {
|
||||
self.check_interrupt()?;
|
||||
*frame = self.call_stack.pop().expect("no root frame");
|
||||
},
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! vmcall {
|
||||
($vm:expr; $func:expr, $($arg:expr),*) => {{
|
||||
let f = $func;
|
||||
$vm.call_value(f.clone(), vec![f, $($arg),*])
|
||||
}};
|
||||
($vm:expr; $func:expr) => {{
|
||||
let f = $func;
|
||||
$vm.call_value(f.clone(), vec![f])
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! vmcalliter {
|
||||
($($input:tt)*) => {
|
||||
$crate::vmcall!($($input)*).and_then(|v| v.iter_unpack())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +1,52 @@
|
|||
use proc_macro::TokenStream;
|
||||
use syn::{LitInt, ItemFn};
|
||||
use syn::{parse::Parse, parse_macro_input, ItemFn, LitInt, Token};
|
||||
use quote::quote;
|
||||
|
||||
struct NativeFuncArgs {
|
||||
arity: LitInt,
|
||||
variadic: Option<Token![..]>,
|
||||
}
|
||||
|
||||
impl Parse for NativeFuncArgs {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let arity = input.parse()?;
|
||||
let variadic = input.parse()?;
|
||||
Ok(Self { arity, variadic })
|
||||
}
|
||||
}
|
||||
|
||||
#[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 Ok(itemfn) = syn::parse::<ItemFn>(annotated_item.clone()) else {
|
||||
return annotated_item
|
||||
};
|
||||
let args: NativeFuncArgs = parse_macro_input!(input as NativeFuncArgs);
|
||||
|
||||
let visibility = itemfn.vis;
|
||||
let block = itemfn.block;
|
||||
let name = itemfn.sig.ident;
|
||||
let inputs = itemfn.sig.inputs;
|
||||
let output = itemfn.sig.output;
|
||||
let visibility = itemfn.vis;
|
||||
let block = itemfn.block;
|
||||
let name = itemfn.sig.ident;
|
||||
let inputs = itemfn.sig.inputs;
|
||||
let output = itemfn.sig.output;
|
||||
let arity = args.arity;
|
||||
let variadic = args.variadic.is_some();
|
||||
|
||||
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");
|
||||
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)
|
||||
let expanded = quote! {
|
||||
#visibility fn #name() -> ::talc_lang::value::function::NativeFunc {
|
||||
::talc_lang::value::function::NativeFunc {
|
||||
attrs: ::talc_lang::value::function::FuncAttrs{
|
||||
arity: #arity,
|
||||
variadic: #variadic,
|
||||
},
|
||||
func: Box::new(|#inputs| #output #block)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
|
|
@ -7,3 +7,8 @@ edition = "2021"
|
|||
talc-lang = { path = "../talc-lang" }
|
||||
talc-macros = { path = "../talc-macros" }
|
||||
lazy_static = "1.4"
|
||||
rand = { version = "0.8", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["random"]
|
||||
random = ["dep:rand"]
|
||||
|
|
208
talc-std/src/collection.rs
Normal file
208
talc-std/src/collection.rs
Normal file
|
@ -0,0 +1,208 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use talc_lang::{exception, symbol::SYM_TYPE_ERROR, throw, value::{exception::Result, function::NativeFunc, Value}, vmcall, Vm};
|
||||
use talc_macros::native_func;
|
||||
|
||||
use crate::unpack_args;
|
||||
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
vm.set_global_name("push", push().into());
|
||||
vm.set_global_name("pop", pop().into());
|
||||
vm.set_global_name("reverse", reverse().into());
|
||||
vm.set_global_name("clear", clear().into());
|
||||
vm.set_global_name("sort", sort().into());
|
||||
vm.set_global_name("sort_by", sort_by().into());
|
||||
vm.set_global_name("sort_key", sort_key().into());
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn push(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, list, item] = unpack_args!(args);
|
||||
let Value::List(list) = list else {
|
||||
throw!(*SYM_TYPE_ERROR, "push expected list, found {list:#}")
|
||||
};
|
||||
list.borrow_mut().push(item);
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn pop(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, list] = unpack_args!(args);
|
||||
let Value::List(list) = list else {
|
||||
throw!(*SYM_TYPE_ERROR, "pop expected list, found {list:#}")
|
||||
};
|
||||
let v = list.borrow_mut().pop();
|
||||
v.ok_or_else(|| exception!(*SYM_TYPE_ERROR, "attempt to pop empty list"))
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn reverse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, list] = unpack_args!(args);
|
||||
let Value::List(list) = list else {
|
||||
throw!(*SYM_TYPE_ERROR, "reversed expected list, found {list:#}")
|
||||
};
|
||||
list.borrow_mut().reverse();
|
||||
Ok(Value::List(list))
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, col] = unpack_args!(args);
|
||||
match &col {
|
||||
Value::List(list) => list.borrow_mut().clear(),
|
||||
Value::Table(table) => table.borrow_mut().clear(),
|
||||
_ => throw!(*SYM_TYPE_ERROR, "clear expected list or table, found {col:#}")
|
||||
}
|
||||
Ok(col)
|
||||
}
|
||||
|
||||
fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<i64> {
|
||||
let ord = vmcall!(vm; by, l, r)?;
|
||||
let Value::Int(ord) = ord else {
|
||||
throw!(*SYM_TYPE_ERROR, "comparison function should return an integer")
|
||||
};
|
||||
Ok(ord)
|
||||
}
|
||||
|
||||
fn partition(vals: &mut [Value]) -> (usize, usize) {
|
||||
let pivot = vals[vals.len() / 2].clone();
|
||||
|
||||
let mut lt = 0;
|
||||
let mut eq = 0;
|
||||
let mut gt = vals.len() - 1;
|
||||
|
||||
while eq <= gt {
|
||||
let ord = vals[eq].partial_cmp(&pivot);
|
||||
match ord {
|
||||
Some(Ordering::Less) => {
|
||||
vals.swap(eq, lt);
|
||||
lt += 1;
|
||||
eq += 1;
|
||||
},
|
||||
Some(Ordering::Greater) => {
|
||||
vals.swap(eq, gt);
|
||||
gt -= 1;
|
||||
},
|
||||
Some(Ordering::Equal) | None => {
|
||||
eq += 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
(lt, gt)
|
||||
}
|
||||
|
||||
fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, usize)> {
|
||||
let pivot = vals[vals.len() / 2].clone();
|
||||
|
||||
let mut lt = 0;
|
||||
let mut eq = 0;
|
||||
let mut gt = vals.len() - 1;
|
||||
|
||||
while eq <= gt {
|
||||
let ord = call_comparison(vm, by.clone(), vals[eq].clone(), pivot.clone())?;
|
||||
match ord {
|
||||
..=-1 => {
|
||||
vals.swap(eq, lt);
|
||||
lt += 1;
|
||||
eq += 1;
|
||||
},
|
||||
1.. => {
|
||||
vals.swap(eq, gt);
|
||||
gt -= 1;
|
||||
},
|
||||
0 => {
|
||||
eq += 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok((lt, gt))
|
||||
}
|
||||
|
||||
fn insertion(vals: &mut [Value]) {
|
||||
for i in 0..vals.len() {
|
||||
let mut j = i;
|
||||
while j > 0 && vals[j-1] > vals[j] {
|
||||
vals.swap(j, j-1);
|
||||
j -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn insertion_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<()> {
|
||||
for i in 0..vals.len() {
|
||||
let mut j = i;
|
||||
while j > 0 {
|
||||
let ord = call_comparison(vm, by.clone(), vals[j-1].clone(), vals[j].clone())?;
|
||||
if ord <= 0 {
|
||||
break;
|
||||
}
|
||||
vals.swap(j, j-1);
|
||||
j -= 1;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sort_inner(vals: &mut [Value], by: Option<&Value>, vm: &mut Vm) -> Result<()> {
|
||||
if vals.len() <= 1 {
|
||||
return Ok(())
|
||||
}
|
||||
if vals.len() <= 8 {
|
||||
match by {
|
||||
Some(by) => insertion_by(vals, by, vm)?,
|
||||
None => insertion(vals),
|
||||
}
|
||||
return Ok(())
|
||||
}
|
||||
let (lt, gt) = match by {
|
||||
Some(by) => partition_by(vals, by, vm)?,
|
||||
None => partition(vals),
|
||||
};
|
||||
sort_inner(&mut vals[..lt], by, vm)?;
|
||||
sort_inner(&mut vals[(gt+1)..], by, vm)
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn sort(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, list] = unpack_args!(args);
|
||||
let Value::List(list) = list else {
|
||||
throw!(*SYM_TYPE_ERROR, "sort expected list, found {list:#}")
|
||||
};
|
||||
sort_inner(&mut list.borrow_mut(), None, vm)?;
|
||||
Ok(list.into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn sort_by(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, by, list] = unpack_args!(args);
|
||||
let Value::List(list) = list else {
|
||||
throw!(*SYM_TYPE_ERROR, "sort expected list, found {list:#}")
|
||||
};
|
||||
sort_inner(&mut list.borrow_mut(), Some(&by), vm)?;
|
||||
Ok(list.into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, key, list] = unpack_args!(args);
|
||||
let Value::List(list) = list else {
|
||||
throw!(*SYM_TYPE_ERROR, "sort expected list, found {list:#}")
|
||||
};
|
||||
let f = move |vm: &mut Vm, args: Vec<Value>| {
|
||||
let [_, a, b] = unpack_args!(args);
|
||||
let a = vmcall!(vm; key.clone(), a)?;
|
||||
let b = vmcall!(vm; key.clone(), b)?;
|
||||
match a.partial_cmp(&b) {
|
||||
Some(Ordering::Greater) => Ok(Value::Int(1)),
|
||||
Some(Ordering::Equal) => Ok(Value::Int(0)),
|
||||
Some(Ordering::Less) => Ok(Value::Int(-1)),
|
||||
None => throw!(*SYM_TYPE_ERROR, "values returned from sort key were incomparable"),
|
||||
}
|
||||
};
|
||||
let nf = NativeFunc::new(Box::new(f), 2).into();
|
||||
sort_inner(&mut list.borrow_mut(), Some(&nf), vm)?;
|
||||
Ok(list.into())
|
||||
}
|
|
@ -1,31 +1,40 @@
|
|||
use talc_lang::{symbol::SYM_TYPE_ERROR, value::{exception::{throw, Exception, Result}, Value}, Vm};
|
||||
use talc_macros::native_func;
|
||||
|
||||
#[native_func(2)]
|
||||
use crate::{unpack_args, unpack_varargs};
|
||||
|
||||
#[native_func(1..)]
|
||||
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")
|
||||
}
|
||||
let ([_, ty], varargs) = unpack_varargs!(args);
|
||||
let Value::Symbol(ty) = ty else {
|
||||
throw!(*SYM_TYPE_ERROR, "exception type must be a symbol")
|
||||
};
|
||||
Err(match &*varargs {
|
||||
[] | [Value::Nil]
|
||||
=> Exception::new(ty),
|
||||
[Value::Nil, data]
|
||||
=> Exception::new_with_data(ty, data.clone()),
|
||||
[Value::String(s)]
|
||||
=> Exception::new_with_msg(ty, s.clone()),
|
||||
[Value::String(s), data]
|
||||
=> Exception::new_with_msg_data(ty, s.clone(), data.clone()),
|
||||
[_] | [_,_] => throw!(*SYM_TYPE_ERROR, "wrong arguments for throw"),
|
||||
[_,_,_,..] => throw!(*SYM_TYPE_ERROR, "too many arguments for throw"),
|
||||
})
|
||||
}
|
||||
|
||||
#[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")
|
||||
let [_, table] = unpack_args!(args);
|
||||
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());
|
||||
vm.set_global_name("throw", throw().into());
|
||||
vm.set_global_name("rethrow", rethrow().into());
|
||||
}
|
||||
|
|
|
@ -1,37 +1,70 @@
|
|||
use std::io::Write;
|
||||
use std::{io::Write, time::{SystemTime, UNIX_EPOCH}};
|
||||
|
||||
use talc_lang::{value::{exception::{throw, Result}, Value}, Vm};
|
||||
use talc_lang::{value::{exception::{throw, Result}, Value}, vmcall, Vm};
|
||||
use talc_macros::native_func;
|
||||
|
||||
use crate::SYM_IO_ERROR;
|
||||
use crate::{unpack_args, 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)
|
||||
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)
|
||||
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())
|
||||
let mut buf = String::new();
|
||||
if let Err(e) = std::io::stdin().read_line(&mut buf) {
|
||||
throw!(*SYM_IO_ERROR, "{e}")
|
||||
}
|
||||
Ok(buf.into())
|
||||
}
|
||||
|
||||
#[native_func(0)]
|
||||
pub fn time(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
|
||||
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
|
||||
Ok((time.as_secs() as i64).into())
|
||||
}
|
||||
|
||||
#[native_func(0)]
|
||||
pub fn time_ms(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
|
||||
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
|
||||
Ok((time.as_millis() as i64).into())
|
||||
}
|
||||
|
||||
#[native_func(0)]
|
||||
pub fn time_ns(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
|
||||
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
|
||||
Ok(vec![(time.as_secs() as i64).into(), (time.subsec_nanos() as i64).into()].into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn time_fn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, func] = unpack_args!(args);
|
||||
let t0 = SystemTime::now();
|
||||
vmcall!(vm; func)?;
|
||||
let tf = SystemTime::now();
|
||||
let time = tf.duration_since(t0).expect("time went backwards");
|
||||
Ok((time.as_nanos() as i64).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());
|
||||
vm.set_global_name("print", print().into());
|
||||
vm.set_global_name("println", println().into());
|
||||
vm.set_global_name("readln", readln().into());
|
||||
|
||||
vm.set_global_name("time", time().into());
|
||||
vm.set_global_name("time_ms", time_ms().into());
|
||||
vm.set_global_name("time_ns", time_ns().into());
|
||||
vm.set_global_name("time_fn", time_fn().into());
|
||||
}
|
||||
|
|
|
@ -1,42 +1,829 @@
|
|||
use talc_lang::{symbol::SYM_STOP_ITERATOR, value::{exception::Result, function::NativeFunc, Value}, Vm};
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use talc_lang::{symbol::SYM_TYPE_ERROR, throw, value::{cell_take, exception::Result, function::NativeFunc, range::RangeType, HashValue, Value}, vmcall, vmcalliter, Vm};
|
||||
use talc_macros::native_func;
|
||||
|
||||
use crate::{unpack_args, unpack_varargs};
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
vm.set_global_name("iter", iter().into());
|
||||
vm.set_global_name("pairs", pairs().into());
|
||||
vm.set_global_name("once", once().into());
|
||||
vm.set_global_name("forever", forever().into());
|
||||
vm.set_global_name("do_forever", forever().into());
|
||||
|
||||
vm.set_global_name("map", map().into());
|
||||
vm.set_global_name("scan", scan().into());
|
||||
vm.set_global_name("tee", tee().into());
|
||||
vm.set_global_name("filter", filter().into());
|
||||
vm.set_global_name("filter_map", filter_map().into());
|
||||
vm.set_global_name("take", take().into());
|
||||
vm.set_global_name("skip", skip().into());
|
||||
vm.set_global_name("enumerate", enumerate().into());
|
||||
vm.set_global_name("zip", zip().into());
|
||||
vm.set_global_name("alternate", alternate().into());
|
||||
vm.set_global_name("intersperse", intersperse().into());
|
||||
vm.set_global_name("cartprod", cartprod().into());
|
||||
vm.set_global_name("cycle", cycle().into());
|
||||
vm.set_global_name("chain", chain().into());
|
||||
vm.set_global_name("step", step().into());
|
||||
vm.set_global_name("rev", rev().into());
|
||||
|
||||
vm.set_global_name("list", list().into());
|
||||
vm.set_global_name("table", table().into());
|
||||
vm.set_global_name("len", len().into());
|
||||
vm.set_global_name("fold", fold().into());
|
||||
vm.set_global_name("foldi", foldi().into());
|
||||
vm.set_global_name("sum", sum().into());
|
||||
vm.set_global_name("prod", prod().into());
|
||||
vm.set_global_name("any", any().into());
|
||||
vm.set_global_name("all", all().into());
|
||||
vm.set_global_name("last", last().into());
|
||||
vm.set_global_name("nth", nth().into());
|
||||
vm.set_global_name("contains", contains().into());
|
||||
vm.set_global_name("index_of", index_of().into());
|
||||
vm.set_global_name("index_if", index_if().into());
|
||||
vm.set_global_name("find", find().into());
|
||||
vm.set_global_name("count", count().into());
|
||||
}
|
||||
|
||||
//
|
||||
// begin iteration
|
||||
//
|
||||
|
||||
#[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()
|
||||
}
|
||||
let [_, v] = unpack_args!(args);
|
||||
|
||||
#[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())
|
||||
v.to_iter_function()
|
||||
}
|
||||
|
||||
#[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 pairs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, table] = unpack_args!(args);
|
||||
let Value::Table(table) = table else {
|
||||
throw!(*SYM_TYPE_ERROR, "pairs expected table, found {table:#}")
|
||||
};
|
||||
|
||||
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
|
||||
let keys = RefCell::new(keys.into_iter());
|
||||
|
||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||
if let Some(k) = keys.borrow_mut().next() {
|
||||
let v = table.borrow().get(&k).cloned().unwrap_or(Value::Nil);
|
||||
Ok(Value::from(vec![k.into_inner(), v]).to_cell())
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
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());
|
||||
#[native_func(1)]
|
||||
pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, val] = unpack_args!(args);
|
||||
|
||||
let v = RefCell::new(Some(val));
|
||||
let f = move |_: &mut Vm, _| {
|
||||
if let Some(v) = v.borrow_mut().take() {
|
||||
Ok(v.to_cell())
|
||||
} else {
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, val] = unpack_args!(args);
|
||||
|
||||
let f = move |_: &mut Vm, _| {
|
||||
Ok(val.clone().to_cell())
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn do_forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, func] = unpack_args!(args);
|
||||
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
Ok(vmcall!(vm; func.clone())?.to_cell())
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
//
|
||||
// chain iteration
|
||||
//
|
||||
|
||||
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, map, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
Some(val) => Ok(vmcall!(vm; map.clone(), cell_take(val))?.to_cell()),
|
||||
None => Ok(Value::Nil),
|
||||
}
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn tee(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, tee, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
Some(val) => {
|
||||
vmcall!(vm; tee.clone(), cell_take(val.clone()))?;
|
||||
Ok(val.into())
|
||||
}
|
||||
None => Ok(Value::Nil),
|
||||
}
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(3)]
|
||||
pub fn scan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, init, func, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let result = RefCell::new(init);
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
Some(val) => {
|
||||
let val = cell_take(val);
|
||||
let r = vmcall!(vm; func.clone(), result.take(), val)?;
|
||||
*result.borrow_mut() = r.clone();
|
||||
Ok(r.to_cell())
|
||||
},
|
||||
None => {
|
||||
result.take();
|
||||
Ok(Value::Nil)
|
||||
},
|
||||
}
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, filter, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
loop {
|
||||
let next = match vmcalliter!(vm; iter.clone())? {
|
||||
Some(next) => next,
|
||||
None => return Ok(Value::Nil),
|
||||
};
|
||||
let res = vmcall!(vm; filter.clone(), cell_take(next.clone()))?;
|
||||
if res.truthy() {
|
||||
return Ok(next.into())
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn filter_map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, fmap, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
loop {
|
||||
let next = match vmcalliter!(vm; iter.clone())? {
|
||||
Some(next) => next,
|
||||
None => return Ok(Value::Nil),
|
||||
};
|
||||
let res = vmcall!(vm; fmap.clone(), cell_take(next))?;
|
||||
if let Value::Cell(_) = res {
|
||||
return Ok(res)
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, count, iter] = unpack_args!(args);
|
||||
let Value::Int(count) = count else {
|
||||
throw!(*SYM_TYPE_ERROR, "take expected integer")
|
||||
};
|
||||
let Ok(count) = count.try_into() else {
|
||||
throw!(*SYM_TYPE_ERROR, "take expected nonnegative integer")
|
||||
};
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let taken = RefCell::new(0);
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
if *taken.borrow() >= count {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
let next = match vmcalliter!(vm; iter.clone())? {
|
||||
Some(next) => next,
|
||||
None => {
|
||||
*taken.borrow_mut() = count;
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
};
|
||||
*taken.borrow_mut() += 1;
|
||||
Ok(next.into())
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, count, iter] = unpack_args!(args);
|
||||
let Value::Int(count) = count else {
|
||||
throw!(*SYM_TYPE_ERROR, "count expected integer")
|
||||
};
|
||||
let Ok(count) = count.try_into() else {
|
||||
throw!(*SYM_TYPE_ERROR, "count expected nonnegative integer")
|
||||
};
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let skipped = RefCell::new(false);
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
if *skipped.borrow() {
|
||||
return vmcall!(vm; iter.clone())
|
||||
}
|
||||
*skipped.borrow_mut() = true;
|
||||
for _ in 0..count {
|
||||
vmcall!(vm; iter.clone())?;
|
||||
}
|
||||
vmcall!(vm; iter.clone())
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn enumerate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let counter = RefCell::new(0);
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
let next = match vmcalliter!(vm; iter.clone())? {
|
||||
Some(next) => cell_take(next),
|
||||
None => return Ok(Value::Nil),
|
||||
};
|
||||
let mut c = counter.borrow_mut();
|
||||
let n = *c;
|
||||
*c += 1;
|
||||
Ok(Value::from(vec![n.into(), next]).to_cell())
|
||||
};
|
||||
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(2..)]
|
||||
pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let ([_, i1, i2], rest) = unpack_varargs!(args);
|
||||
let mut iters = Vec::with_capacity(2 + rest.len());
|
||||
for i in [i1, i2].into_iter().chain(rest.into_iter()) {
|
||||
iters.push(i.to_iter_function()?);
|
||||
}
|
||||
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
let mut res = Vec::with_capacity(iters.len());
|
||||
for i in iters.iter() {
|
||||
match vmcalliter!(vm; i.clone())? {
|
||||
Some(v) => res.push(cell_take(v)),
|
||||
None => return Ok(Value::Nil),
|
||||
};
|
||||
}
|
||||
Ok(Value::from(res).to_cell())
|
||||
};
|
||||
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(2..)]
|
||||
pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let ([_, i1, i2], rest) = unpack_varargs!(args);
|
||||
let mut iters = Vec::with_capacity(2 + rest.len());
|
||||
for i in [i1, i2].into_iter().chain(rest.into_iter()) {
|
||||
iters.push(i.to_iter_function()?);
|
||||
}
|
||||
|
||||
let state = RefCell::new((0, false));
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
let mut s = state.borrow_mut();
|
||||
if s.1 {
|
||||
return Ok(Value::Nil);
|
||||
}
|
||||
let n = s.0;
|
||||
s.0 = (s.0 + 1) % iters.len();
|
||||
drop(s);
|
||||
match vmcalliter!(vm; iters[n].clone())? {
|
||||
Some(v) => Ok(v.into()),
|
||||
None => {
|
||||
state.borrow_mut().1 = true;
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
enum Intersperse {
|
||||
Init, Waiting, HasNext(Rc<RefCell<Value>>), #[default] End
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, val, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
let val = val.to_cell();
|
||||
|
||||
let state = RefCell::new(Intersperse::Init);
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
match state.take() {
|
||||
Intersperse::Init => match vmcalliter!(vm; iter.clone())? {
|
||||
Some(v) => {
|
||||
*state.borrow_mut() = Intersperse::Waiting;
|
||||
Ok(v.into())
|
||||
},
|
||||
None => {
|
||||
*state.borrow_mut() = Intersperse::End;
|
||||
Ok(Value::Nil)
|
||||
},
|
||||
},
|
||||
Intersperse::Waiting => match vmcalliter!(vm; iter.clone())? {
|
||||
Some(v) => {
|
||||
*state.borrow_mut() = Intersperse::HasNext(v);
|
||||
Ok(val.clone())
|
||||
},
|
||||
None => {
|
||||
*state.borrow_mut() = Intersperse::End;
|
||||
Ok(Value::Nil)
|
||||
},
|
||||
},
|
||||
Intersperse::HasNext(v) => {
|
||||
*state.borrow_mut() = Intersperse::Waiting;
|
||||
Ok(v.into())
|
||||
},
|
||||
Intersperse::End => Ok(Value::Nil),
|
||||
}
|
||||
};
|
||||
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter1, iter2] = unpack_args!(args);
|
||||
let iter1 = iter1.to_iter_function()?;
|
||||
let iter2 = iter2.to_iter_function()?;
|
||||
|
||||
let done_first = RefCell::new(false);
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
if *done_first.borrow() {
|
||||
return vmcall!(vm; iter2.clone())
|
||||
}
|
||||
match vmcalliter!(vm; iter1.clone())? {
|
||||
Some(v) => Ok(v.into()),
|
||||
None => {
|
||||
*done_first.borrow_mut() = true;
|
||||
vmcall!(vm; iter2.clone())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct CartProd {
|
||||
a_val: Value,
|
||||
b_data: Vec<Value>,
|
||||
b_idx: usize,
|
||||
b_done: bool,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, a, b] = unpack_args!(args);
|
||||
let a = a.to_iter_function()?;
|
||||
let b = b.to_iter_function()?;
|
||||
|
||||
let state = RefCell::new(CartProd::default());
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
if state.borrow().done {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
|
||||
if state.borrow().b_idx >= state.borrow().b_data.len() {
|
||||
if !state.borrow().b_done {
|
||||
let v = vmcalliter!(vm; b.clone())?;
|
||||
let mut s = state.borrow_mut();
|
||||
match v {
|
||||
Some(x) => s.b_data.push(cell_take(x)),
|
||||
None => {
|
||||
s.b_done = true;
|
||||
if s.b_idx == 0 {
|
||||
s.done = true;
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
s.b_idx = 0;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
if state.borrow().b_idx == 0 {
|
||||
state.borrow_mut().done = true;
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
state.borrow_mut().b_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let b_res = state.borrow().b_data[state.borrow().b_idx].clone();
|
||||
|
||||
if state.borrow().b_idx == 0 {
|
||||
match vmcalliter!(vm; a.clone())? {
|
||||
Some(v) => state.borrow_mut().a_val = cell_take(v),
|
||||
None => {
|
||||
state.borrow_mut().done = true;
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let a_res = state.borrow().a_val.clone();
|
||||
|
||||
state.borrow_mut().b_idx += 1;
|
||||
|
||||
Ok(Value::from(vec![a_res, b_res]).to_cell())
|
||||
};
|
||||
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let record = RefCell::new((Vec::<Rc<RefCell<Value>>>::new(), false, 0));
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
if record.borrow().1 {
|
||||
let mut r = record.borrow_mut();
|
||||
if r.0.is_empty() {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
let val = r.0[r.2].clone();
|
||||
r.2 = (r.2 + 1) % r.0.len();
|
||||
return Ok(val.into())
|
||||
}
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
Some(v) => {
|
||||
record.borrow_mut().0.push(v.clone());
|
||||
Ok(v.into())
|
||||
},
|
||||
None => {
|
||||
let mut r = record.borrow_mut();
|
||||
r.1 = true;
|
||||
if r.0.is_empty() {
|
||||
Ok(Value::Nil)
|
||||
} else {
|
||||
r.2 = (r.2 + 1) % r.0.len();
|
||||
Ok(r.0[0].clone().into())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
enum Step {
|
||||
First, Going, #[default] End,
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, by, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
let Value::Int(by) = by else {
|
||||
throw!(*SYM_TYPE_ERROR, "step expected integer")
|
||||
};
|
||||
if by <= 0 {
|
||||
throw!(*SYM_TYPE_ERROR, "step expected positive integer")
|
||||
}
|
||||
|
||||
let state = RefCell::new(Step::First);
|
||||
let f = move |vm: &mut Vm, _| match state.take() {
|
||||
Step::First => {
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
Some(x) => {
|
||||
*state.borrow_mut() = Step::Going;
|
||||
Ok(x.into())
|
||||
},
|
||||
None => Ok(Value::Nil),
|
||||
}
|
||||
},
|
||||
Step::Going => {
|
||||
for _ in 0..(by-1) {
|
||||
if vmcall!(vm; iter.clone())? == Value::Nil {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
let Some(x) = vmcalliter!(vm; iter.clone())? else {
|
||||
return Ok(Value::Nil)
|
||||
};
|
||||
*state.borrow_mut() = Step::Going;
|
||||
Ok(x.into())
|
||||
},
|
||||
Step::End => Ok(Value::Nil),
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn rev(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let lst = RefCell::new(None);
|
||||
let f = move |vm: &mut Vm, _| {
|
||||
if lst.borrow().is_none() {
|
||||
let mut l = Vec::new();
|
||||
while let Some(x) = vmcalliter!(vm; iter.clone())? {
|
||||
l.push(cell_take(x))
|
||||
}
|
||||
l.reverse();
|
||||
*lst.borrow_mut() = Some(l.into_iter());
|
||||
}
|
||||
match lst.borrow_mut().as_mut().unwrap().next() {
|
||||
Some(v) => Ok(v.to_cell()),
|
||||
None => Ok(Value::Nil),
|
||||
}
|
||||
|
||||
};
|
||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// end iteration
|
||||
//
|
||||
|
||||
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
let mut result = Vec::new();
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
result.push(cell_take(value));
|
||||
};
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn table(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
let mut result = HashMap::new();
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
let Value::List(l) = cell_take(value) else {
|
||||
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list")
|
||||
};
|
||||
let Ok([k, v]): std::result::Result<[Value; 2], Vec<Value>> = cell_take(l).try_into() else {
|
||||
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list of length 2")
|
||||
};
|
||||
result.insert(k.try_into()?, v);
|
||||
};
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn len(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, value] = unpack_args!(args);
|
||||
match value {
|
||||
Value::String(s) => return Ok((s.len() as i64).into()),
|
||||
Value::List(l) => return Ok((l.borrow().len() as i64).into()),
|
||||
Value::Table(t) => return Ok((t.borrow().len() as i64).into()),
|
||||
Value::Range(r) if r.ty != RangeType::Endless
|
||||
=> return Ok((r.len().unwrap() as i64).into()),
|
||||
_ => (),
|
||||
}
|
||||
let iter = value.to_iter_function()?;
|
||||
let mut len = 0;
|
||||
while vmcalliter!(vm; iter.clone())?.is_some() {
|
||||
len += 1;
|
||||
};
|
||||
Ok(len.into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn fold(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, func, iter] = unpack_args!(args);
|
||||
let iter: Value = iter.to_iter_function()?;
|
||||
|
||||
let mut result = match vmcalliter!(vm; iter.clone())? {
|
||||
Some(v) => cell_take(v),
|
||||
None => return Ok(Value::Nil),
|
||||
};
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
result = vmcall!(vm; func.clone(), result, cell_take(value))?;
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[native_func(3)]
|
||||
pub fn foldi(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, init, func, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let mut result = init;
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
result = vmcall!(vm; func.clone(), result, cell_take(value))?;
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn sum(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let mut result = Value::Int(0);
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
result = (result + cell_take(value))?
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn prod(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let mut result = Value::Int(1);
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
result = (result * cell_take(value))?
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn any(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
if RefCell::borrow(&value).truthy() {
|
||||
return Ok(true.into())
|
||||
}
|
||||
}
|
||||
Ok(false.into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn all(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
if !RefCell::borrow(&value).truthy() {
|
||||
return Ok(false.into())
|
||||
}
|
||||
}
|
||||
Ok(true.into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn last(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let mut last = Rc::new(RefCell::new(Value::Nil));
|
||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||
last = value;
|
||||
}
|
||||
Ok(cell_take(last))
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn nth(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, n, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
let Value::Int(n) = n else {
|
||||
throw!(*SYM_TYPE_ERROR, "nth expected integer")
|
||||
};
|
||||
|
||||
for _ in 0..n {
|
||||
if vmcalliter!(vm; iter.clone())?.is_none() {
|
||||
return Ok(Value::Nil)
|
||||
}
|
||||
}
|
||||
match vmcalliter!(vm; iter)? {
|
||||
Some(v) => Ok(cell_take(v)),
|
||||
None => Ok(Value::Nil),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn contains(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, val, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
for _ in 0.. {
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
None => return Ok(Value::Nil),
|
||||
Some(v) => if *RefCell::borrow(&v) == val {
|
||||
return Ok(true.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false.into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn index_of(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, val, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
for i in 0.. {
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
None => return Ok(Value::Nil),
|
||||
Some(v) => if *RefCell::borrow(&v) == val {
|
||||
return Ok(i.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn index_if(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, func, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
for i in 0.. {
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
None => return Ok(Value::Nil),
|
||||
Some(v) => if vmcall!(vm; func.clone(), cell_take(v))?.truthy() {
|
||||
return Ok(i.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Value::Nil)
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn find(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, func, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
loop {
|
||||
match vmcalliter!(vm; iter.clone())? {
|
||||
None => return Ok(Value::Nil),
|
||||
Some(v) => {
|
||||
let v = cell_take(v);
|
||||
if vmcall!(vm; func.clone(), v.clone())?.truthy() {
|
||||
return Ok(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn count(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, iter] = unpack_args!(args);
|
||||
let iter = iter.to_iter_function()?;
|
||||
|
||||
let mut map = HashMap::new();
|
||||
|
||||
while let Some(v) = vmcalliter!(vm; iter.clone())? {
|
||||
let v = cell_take(v);
|
||||
let hv = v.try_into()?;
|
||||
map.entry(hv)
|
||||
.and_modify(|v: &mut Value| *v = (v.clone() + Value::Int(1)).unwrap())
|
||||
.or_insert(Value::Int(1));
|
||||
}
|
||||
|
||||
Ok(map.into())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,47 @@
|
|||
use talc_lang::{symbol::{symbol, Symbol}, Vm};
|
||||
|
||||
pub mod num;
|
||||
pub mod value;
|
||||
pub mod io;
|
||||
pub mod iter;
|
||||
pub mod exception;
|
||||
pub mod num;
|
||||
pub mod collection;
|
||||
#[cfg(feature="random")]
|
||||
pub mod random;
|
||||
|
||||
pub fn load_all(vm: &mut Vm) {
|
||||
num::load(vm);
|
||||
io::load(vm);
|
||||
iter::load(vm);
|
||||
exception::load(vm);
|
||||
value::load(vm);
|
||||
exception::load(vm);
|
||||
iter::load(vm);
|
||||
collection::load(vm);
|
||||
num::load(vm);
|
||||
io::load(vm);
|
||||
#[cfg(feature="random")]
|
||||
random::load(vm);
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref SYM_IO_ERROR: Symbol = symbol!(io_error);
|
||||
pub static ref SYM_IO_ERROR: Symbol = symbol!(io_error);
|
||||
}
|
||||
|
||||
|
||||
macro_rules! unpack_args {
|
||||
($e:expr) => {
|
||||
($e).try_into().expect("bypassed arity check")
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use unpack_args;
|
||||
|
||||
macro_rules! unpack_varargs {
|
||||
($e:expr) => {{
|
||||
let mut args = $e;
|
||||
let Value::List(varargs) = args.pop().expect("bypassed arity check") else {
|
||||
panic!("bypassed arity check")
|
||||
};
|
||||
let varargs = ::std::rc::Rc::unwrap_or_clone(varargs).into_inner();
|
||||
(args.try_into().expect("bypassed arity check"), varargs)
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) use unpack_varargs;
|
||||
|
|
|
@ -1,5 +1,679 @@
|
|||
use talc_lang::Vm;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use talc_lang::{parse_int, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{exception::Result, Complex64, Value}, Vm};
|
||||
use talc_macros::native_func;
|
||||
|
||||
use crate::{unpack_args, unpack_varargs};
|
||||
|
||||
lazy_static! {
|
||||
static ref SYM_NAN: Symbol = Symbol::get("nan");
|
||||
static ref SYM_INFINITE: Symbol = Symbol::get("infinite");
|
||||
static ref SYM_ZERO: Symbol = Symbol::get("zero");
|
||||
static ref SYM_SUBNORMAL: Symbol = Symbol::get("subnormal");
|
||||
static ref SYM_NORMAL: Symbol = Symbol::get("normal");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_floaty(v: Value) -> Value {
|
||||
match v {
|
||||
Value::Int(v) => Value::Float(v as f64),
|
||||
Value::Ratio(v) => Value::Float(*v.numer() as f64 / *v.denom() as f64),
|
||||
Value::Float(v) => Value::Float(v),
|
||||
Value::Complex(v) => Value::Complex(v),
|
||||
v => v,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_complex(v: Value) -> Value {
|
||||
match v {
|
||||
Value::Int(v) => Value::Complex((v as f64).into()),
|
||||
Value::Ratio(v) => Value::Complex((*v.numer() as f64 / *v.denom() as f64).into()),
|
||||
Value::Float(v) => Value::Complex(v.into()),
|
||||
Value::Complex(v) => Value::Complex(v),
|
||||
v => v,
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// load
|
||||
//
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
vm.set_global_name("e", std::f64::consts::E.into());
|
||||
vm.set_global_name("tau", std::f64::consts::TAU.into());
|
||||
vm.set_global_name("pi", std::f64::consts::PI.into());
|
||||
vm.set_global_name("egamma", 0.5772156649015329.into());
|
||||
vm.set_global_name("phi", 1.618033988749895.into());
|
||||
|
||||
vm.set_global_name("inf", (f64::INFINITY).into());
|
||||
vm.set_global_name("NaN", (f64::NAN).into());
|
||||
vm.set_global_name("infi", Complex64::new(0.0,f64::INFINITY).into());
|
||||
vm.set_global_name("NaNi", Complex64::new(0.0,f64::NAN).into());
|
||||
|
||||
vm.set_global_name("bin", bin().into());
|
||||
vm.set_global_name("sex", sex().into());
|
||||
vm.set_global_name("oct", oct().into());
|
||||
vm.set_global_name("hex", hex().into());
|
||||
vm.set_global_name("to_radix", to_radix().into());
|
||||
vm.set_global_name("from_radix", from_radix().into());
|
||||
|
||||
vm.set_global_name("gcd", gcd().into());
|
||||
vm.set_global_name("lcm", lcm().into());
|
||||
vm.set_global_name("isqrt", isqrt().into());
|
||||
vm.set_global_name("isprime", isprime().into());
|
||||
vm.set_global_name("factors", factors().into());
|
||||
|
||||
vm.set_global_name("min", min().into());
|
||||
vm.set_global_name("max", max().into());
|
||||
vm.set_global_name("floor", floor().into());
|
||||
vm.set_global_name("ceil", ceil().into());
|
||||
vm.set_global_name("round", round().into());
|
||||
vm.set_global_name("trunc", trunc().into());
|
||||
vm.set_global_name("fract", fract().into());
|
||||
vm.set_global_name("sign", sign().into());
|
||||
|
||||
vm.set_global_name("signum", signum().into());
|
||||
vm.set_global_name("classify", classify().into());
|
||||
vm.set_global_name("isnan", isnan().into());
|
||||
vm.set_global_name("isfinite", isfinite().into());
|
||||
vm.set_global_name("isinfinite", isinfinite().into());
|
||||
|
||||
vm.set_global_name("numer", numer().into());
|
||||
vm.set_global_name("denom", denom().into());
|
||||
|
||||
vm.set_global_name("re", re().into());
|
||||
vm.set_global_name("im", im().into());
|
||||
vm.set_global_name("arg", arg().into());
|
||||
vm.set_global_name("abs", abs().into());
|
||||
vm.set_global_name("abs_sq", abs_sq().into());
|
||||
|
||||
vm.set_global_name("sqrt", sqrt().into());
|
||||
vm.set_global_name("cbrt", cbrt().into());
|
||||
vm.set_global_name("ln", cbrt().into());
|
||||
vm.set_global_name("log2", cbrt().into());
|
||||
vm.set_global_name("exp", cbrt().into());
|
||||
vm.set_global_name("exp2", cbrt().into());
|
||||
|
||||
vm.set_global_name("sin", sin().into());
|
||||
vm.set_global_name("cos", cos().into());
|
||||
vm.set_global_name("tan", tan().into());
|
||||
vm.set_global_name("asin", asin().into());
|
||||
vm.set_global_name("acos", acos().into());
|
||||
vm.set_global_name("atan", atan().into());
|
||||
vm.set_global_name("sinh", sinh().into());
|
||||
vm.set_global_name("cosh", cosh().into());
|
||||
vm.set_global_name("tanh", tanh().into());
|
||||
vm.set_global_name("asinh", asinh().into());
|
||||
vm.set_global_name("acosh", acosh().into());
|
||||
vm.set_global_name("atanh", atanh().into());
|
||||
vm.set_global_name("atanh", atanh().into());
|
||||
vm.set_global_name("atan2", atan2().into());
|
||||
}
|
||||
|
||||
//
|
||||
// base conversions
|
||||
//
|
||||
|
||||
fn to_radix_inner(n: i64, radix: u32) -> String {
|
||||
let mut result = vec![];
|
||||
let mut begin = 0;
|
||||
|
||||
let mut x;
|
||||
if n < 0 {
|
||||
result.push('-' as u32 as u8);
|
||||
begin = 1;
|
||||
x = (-n) as u64
|
||||
} else {
|
||||
x = n as u64
|
||||
};
|
||||
|
||||
loop {
|
||||
let m = x % (radix as u64);
|
||||
x /= radix as u64;
|
||||
|
||||
result.push(char::from_digit(m as u32, radix).unwrap() as u8);
|
||||
if x == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result[begin..].reverse();
|
||||
String::from_utf8(result).expect("string was not valid utf8")
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn bin(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}")
|
||||
};
|
||||
Ok(to_radix_inner(x, 2).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn sex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "sex expected integer argument, got {x:#}")
|
||||
};
|
||||
Ok(to_radix_inner(x, 6).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn oct(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "oct expected integer argument, got {x:#}")
|
||||
};
|
||||
Ok(to_radix_inner(x, 8).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn hex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "hex expected integer argument, got {x:#}")
|
||||
};
|
||||
Ok(to_radix_inner(x, 16).into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x, radix] = unpack_args!(args);
|
||||
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
|
||||
throw!(*SYM_TYPE_ERROR, "to_radix expected integer arguments, got {x:#} and {radix:#}")
|
||||
};
|
||||
if *radix < 2 || *radix > 36 {
|
||||
throw!(*SYM_TYPE_ERROR, "to_radix expected radix in range 0..=36")
|
||||
}
|
||||
Ok(to_radix_inner(*x, *radix as u32).into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, s, radix] = unpack_args!(args);
|
||||
let (Value::String(s), Value::Int(radix)) = (&s, &radix) else {
|
||||
throw!(*SYM_TYPE_ERROR, "from_radix expected string and integer arguments, got {s:#} and {radix:#}")
|
||||
};
|
||||
if *radix < 2 || *radix > 36 {
|
||||
throw!(*SYM_TYPE_ERROR, "from_radix expected radix in range 0..=36")
|
||||
}
|
||||
match parse_int(s, *radix as u32) {
|
||||
Ok(v) => Ok(v.into()),
|
||||
Err(_) => throw!(*SYM_TYPE_ERROR, "string was not a valid integer in given radix"),
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// integer operations
|
||||
//
|
||||
|
||||
fn isqrt_inner(mut n: i64) -> i64 {
|
||||
assert!(n >= 0, "isqrt input should be nonnegative");
|
||||
if n < 2 { return n }
|
||||
|
||||
let mut c = 0;
|
||||
let mut d = 1 << 62;
|
||||
|
||||
while d > n {
|
||||
d >>= 2;
|
||||
}
|
||||
|
||||
while d != 0 {
|
||||
if n >= c + d {
|
||||
n -= c + d;
|
||||
c = (c >> 1) + d;
|
||||
}
|
||||
else {
|
||||
c >>= 1;
|
||||
}
|
||||
d >>= 2;
|
||||
}
|
||||
c
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "isqrt expected integer argument, got {x:#}")
|
||||
};
|
||||
if x < 0 {
|
||||
throw!(*SYM_TYPE_ERROR, "isqrt: argument must be positive")
|
||||
}
|
||||
Ok(isqrt_inner(x).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "isprime expected integer argument, got {x:#}")
|
||||
};
|
||||
if x < 2 {
|
||||
return Ok(false.into())
|
||||
}
|
||||
for p in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53] {
|
||||
if x % p == 0 {
|
||||
return Ok((x == p).into())
|
||||
}
|
||||
}
|
||||
if x < 59 {
|
||||
return Ok(false.into())
|
||||
}
|
||||
let lim = isqrt_inner(x);
|
||||
let mut i = 54;
|
||||
while i <= lim {
|
||||
if x % (i + 1) == 0 { return Ok(false.into()) }
|
||||
if x % (i + 5) == 0 { return Ok(false.into()) }
|
||||
i += 6;
|
||||
}
|
||||
Ok(true.into())
|
||||
}
|
||||
|
||||
fn gcd_inner(a: i64, b: i64) -> i64 {
|
||||
let (mut a, mut b) = (a.abs(), b.abs());
|
||||
if a == 0 {
|
||||
return b
|
||||
}
|
||||
if b == 0 {
|
||||
return a
|
||||
}
|
||||
|
||||
let az = a.trailing_zeros();
|
||||
a >>= az;
|
||||
let bz = b.trailing_zeros();
|
||||
b >>= bz;
|
||||
let z = az.min(bz);
|
||||
|
||||
loop {
|
||||
if a > b {
|
||||
std::mem::swap(&mut a, &mut b);
|
||||
}
|
||||
b -= a;
|
||||
if b == 0 {
|
||||
return a << z;
|
||||
}
|
||||
b >>= b.trailing_zeros();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[native_func(2..)]
|
||||
pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let ([_, x, y], rest) = unpack_varargs!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
|
||||
};
|
||||
let Value::Int(y) = y else {
|
||||
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
|
||||
};
|
||||
let mut g = gcd_inner(x, y);
|
||||
for a in rest {
|
||||
let Value::Int(a) = a else {
|
||||
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {a:#}")
|
||||
};
|
||||
g = gcd_inner(g, a);
|
||||
}
|
||||
Ok(g.into())
|
||||
}
|
||||
|
||||
#[native_func(2..)]
|
||||
pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let ([_, x, y], rest) = unpack_varargs!(args);
|
||||
let Value::Int(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
|
||||
};
|
||||
let Value::Int(y) = y else {
|
||||
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
|
||||
};
|
||||
let mut g = gcd_inner(x, y);
|
||||
let mut prod = y;
|
||||
for a in rest {
|
||||
let Value::Int(a) = a else {
|
||||
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {a:#}")
|
||||
};
|
||||
prod *= a;
|
||||
g = gcd_inner(g, a);
|
||||
}
|
||||
Ok((x/g * prod).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
let Value::Int(mut x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "factords expected integer argument, got {x:#}")
|
||||
};
|
||||
let mut factors = Vec::new();
|
||||
if x <= 1 {
|
||||
return Ok(factors.into())
|
||||
}
|
||||
while x & 1 == 0 {
|
||||
x >>= 1;
|
||||
factors.push(Value::Int(2));
|
||||
}
|
||||
while x % 3 == 0 {
|
||||
x /= 3;
|
||||
factors.push(Value::Int(3));
|
||||
}
|
||||
//let lim = isqrt_inner(x);
|
||||
let mut i = 5;
|
||||
while x > 1 {
|
||||
while x % i == 0 {
|
||||
x /= i;
|
||||
factors.push(Value::Int(i));
|
||||
}
|
||||
i += 2;
|
||||
while x % i == 0 {
|
||||
x /= i;
|
||||
factors.push(Value::Int(i));
|
||||
}
|
||||
i += 4;
|
||||
}
|
||||
Ok(factors.into())
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// numeric operations
|
||||
//
|
||||
|
||||
#[native_func(1..)]
|
||||
pub fn min(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let ([_, mut x], rest) = unpack_varargs!(args);
|
||||
for val in rest {
|
||||
if val < x {
|
||||
x = val;
|
||||
}
|
||||
}
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
#[native_func(1..)]
|
||||
pub fn max(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let ([_, mut x], rest) = unpack_varargs!(args);
|
||||
for val in rest {
|
||||
if val > x {
|
||||
x = val;
|
||||
}
|
||||
}
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn floor(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
match x {
|
||||
Value::Int(x) => Ok(Value::Int(x)),
|
||||
Value::Float(x) => Ok(Value::Float(x.floor())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.floor())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "floor expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn ceil(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
match x {
|
||||
Value::Int(x) => Ok(Value::Int(x)),
|
||||
Value::Float(x) => Ok(Value::Float(x.ceil())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.ceil())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "ceil expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn round(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
match x {
|
||||
Value::Int(x) => Ok(Value::Int(x)),
|
||||
Value::Float(x) => Ok(Value::Float(x.round())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.round())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "round expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn trunc(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
match x {
|
||||
Value::Int(x) => Ok(Value::Int(x)),
|
||||
Value::Float(x) => Ok(Value::Float(x.trunc())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.trunc())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "trunc expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn fract(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
match x {
|
||||
Value::Int(_) => Ok(Value::Int(0)),
|
||||
Value::Float(x) => Ok(Value::Float(x.fract())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x.fract())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "fract expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
match x {
|
||||
Value::Int(x) => Ok(Value::Int(x.signum())),
|
||||
Value::Float(x) => Ok(Value::Float(if x == 0.0 { 0.0 } else { x.signum() })),
|
||||
Value::Ratio(x) => match x.cmp(&0.into()) {
|
||||
Ordering::Greater => Ok(Value::Ratio(1.into())),
|
||||
Ordering::Less => Ok(Value::Ratio((-1).into())),
|
||||
Ordering::Equal => Ok(Value::Ratio(0.into())),
|
||||
}
|
||||
x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// floating-point operations
|
||||
//
|
||||
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn signum(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
match to_floaty(x) {
|
||||
Value::Float(x) => Ok(Value::Float(x.signum())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "signum expected real argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn classify(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
let x = to_floaty(x);
|
||||
let Value::Float(x) = x else {
|
||||
throw!(*SYM_TYPE_ERROR, "classify expected real argument, got {x:#}")
|
||||
};
|
||||
Ok(match x.classify() {
|
||||
std::num::FpCategory::Nan => *SYM_NAN,
|
||||
std::num::FpCategory::Infinite => *SYM_INFINITE,
|
||||
std::num::FpCategory::Zero => *SYM_ZERO,
|
||||
std::num::FpCategory::Subnormal => *SYM_SUBNORMAL,
|
||||
std::num::FpCategory::Normal => *SYM_NORMAL,
|
||||
}.into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn isnan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, v] = unpack_args!(args);
|
||||
match v {
|
||||
Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
|
||||
Value::Float(x) => Ok(x.is_nan().into()),
|
||||
Value::Complex(z) => Ok(z.is_nan().into()),
|
||||
v => throw!(*SYM_TYPE_ERROR, "isnan expected numeric argument, got {v:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn isfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, v] = unpack_args!(args);
|
||||
match v {
|
||||
Value::Int(_) | Value::Ratio(_) => Ok(true.into()),
|
||||
Value::Float(x) => Ok(x.is_finite().into()),
|
||||
Value::Complex(z) => Ok(z.is_finite().into()),
|
||||
v => throw!(*SYM_TYPE_ERROR, "isfinite expected numeric argument, got {v:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn isinfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, v] = unpack_args!(args);
|
||||
match v {
|
||||
Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
|
||||
Value::Float(x) => Ok(x.is_infinite().into()),
|
||||
Value::Complex(z) => Ok(z.is_infinite().into()),
|
||||
v => throw!(*SYM_TYPE_ERROR, "isinfinite expected numeric argument, got {v:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// rational operations
|
||||
//
|
||||
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, v] = unpack_args!(args);
|
||||
match v {
|
||||
Value::Int(x) => Ok(Value::Int(x)),
|
||||
Value::Ratio(x) => Ok(Value::Int(*x.numer())),
|
||||
v => throw!(*SYM_TYPE_ERROR, "numer expected rational argument, got {v:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, v] = unpack_args!(args);
|
||||
match v {
|
||||
Value::Int(_) => Ok(Value::Int(1)),
|
||||
Value::Ratio(x) => Ok(Value::Int(*x.denom())),
|
||||
v => throw!(*SYM_TYPE_ERROR, "denom expected rational argument, got {v:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// complex operations
|
||||
//
|
||||
|
||||
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn re(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, v] = unpack_args!(args);
|
||||
match v {
|
||||
Value::Int(x) => Ok(Value::Int(x)),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x)),
|
||||
Value::Float(x) => Ok(Value::Float(x)),
|
||||
Value::Complex(z) => Ok(Value::Float(z.re)),
|
||||
v => throw!(*SYM_TYPE_ERROR, "re expected numeric argument, got {v:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn im(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, v] = unpack_args!(args);
|
||||
match to_complex(v) {
|
||||
Value::Int(_) => Ok(Value::Int(0)),
|
||||
Value::Ratio(_) => Ok(Value::Ratio(0.into())),
|
||||
Value::Float(_) => Ok(Value::Float(0.0)),
|
||||
Value::Complex(z) => Ok(Value::Float(z.im)),
|
||||
v => throw!(*SYM_TYPE_ERROR, "im expected numeric argument, got {v:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn arg(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, v] = unpack_args!(args);
|
||||
match to_complex(v) {
|
||||
Value::Complex(z) => Ok(Value::Float(z.arg())),
|
||||
v => throw!(*SYM_TYPE_ERROR, "im expected numeric argument, got {v:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
match x {
|
||||
Value::Int(x) => Ok(Value::Int(x.abs())),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(if x < 0.into() { -x } else { x })),
|
||||
Value::Float(x) => Ok(Value::Float(x.abs())),
|
||||
Value::Complex(x) => Ok(Value::Float(x.norm())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "abs expected numeric argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, x] = unpack_args!(args);
|
||||
match x {
|
||||
Value::Int(x) => Ok(Value::Int(x * x)),
|
||||
Value::Ratio(x) => Ok(Value::Ratio(x * x)),
|
||||
Value::Float(x) => Ok(Value::Float(x * x)),
|
||||
Value::Complex(x) => Ok(Value::Float(x.norm_sqr())),
|
||||
x => throw!(*SYM_TYPE_ERROR, "abs_sq expected numeric argument, got {x:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// continuous operations
|
||||
//
|
||||
|
||||
|
||||
macro_rules! float_func {
|
||||
($name:ident) => {
|
||||
#[native_func(1)]
|
||||
pub fn $name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, v] = unpack_args!(args);
|
||||
match to_floaty(v) {
|
||||
Value::Float(x) => Ok(Value::Float(x.$name())),
|
||||
Value::Complex(z) => Ok(Value::Complex(z.$name())),
|
||||
v => throw!(*SYM_TYPE_ERROR,
|
||||
"{} expected numeric argument, got {v:#}", stringify!($name)),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
float_func!(sqrt);
|
||||
float_func!(cbrt);
|
||||
float_func!(ln);
|
||||
float_func!(log2);
|
||||
float_func!(exp);
|
||||
float_func!(exp2);
|
||||
|
||||
float_func!(sin);
|
||||
float_func!(cos);
|
||||
float_func!(tan);
|
||||
float_func!(asin);
|
||||
float_func!(acos);
|
||||
float_func!(atan);
|
||||
float_func!(sinh);
|
||||
float_func!(cosh);
|
||||
float_func!(tanh);
|
||||
float_func!(asinh);
|
||||
float_func!(acosh);
|
||||
float_func!(atanh);
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn atan2(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, y, x] = unpack_args!(args);
|
||||
match (to_floaty(y), to_floaty(x)) {
|
||||
(Value::Float(y), Value::Float(x))
|
||||
=> Ok(Value::Float(x.atan2(y))),
|
||||
(y,x) => throw!(*SYM_TYPE_ERROR,
|
||||
"atan2 expected real arguments, got {y:#} and {x:#}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
50
talc-std/src/random.rs
Normal file
50
talc-std/src/random.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use rand::Rng;
|
||||
use talc_lang::{symbol::SYM_TYPE_ERROR, throw, value::{cell_take, exception::Result, range::RangeType, Value}, vmcalliter, Vm};
|
||||
use talc_macros::native_func;
|
||||
|
||||
use crate::unpack_args;
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
vm.set_global_name("rand", rand().into());
|
||||
vm.set_global_name("rand_in", rand_in().into());
|
||||
}
|
||||
|
||||
#[native_func(0)]
|
||||
pub fn rand(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
|
||||
Ok(Value::Float(rand::thread_rng().gen()))
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, col] = unpack_args!(args);
|
||||
match col {
|
||||
Value::List(l) => {
|
||||
let l = l.borrow();
|
||||
let i = rand::thread_rng().gen_range(0..l.len());
|
||||
Ok(l[i].clone())
|
||||
},
|
||||
Value::Table(t) => {
|
||||
let t = t.borrow();
|
||||
let i = rand::thread_rng().gen_range(0..t.len());
|
||||
let key = t.keys().nth(i).unwrap();
|
||||
Ok(key.clone().into_inner())
|
||||
},
|
||||
Value::Range(r) => match r.ty {
|
||||
RangeType::Open => Ok(Value::Int(
|
||||
rand::thread_rng().gen_range(r.start..r.stop))),
|
||||
RangeType::Closed => Ok(Value::Int(
|
||||
rand::thread_rng().gen_range(r.start..=r.stop))),
|
||||
RangeType::Endless => throw!(*SYM_TYPE_ERROR, "cannot select random element of endless range"),
|
||||
},
|
||||
col => {
|
||||
let iter = col.to_iter_function()?;
|
||||
let mut values = Vec::new();
|
||||
while let Some(v) = vmcalliter!(vm; iter.clone())? {
|
||||
values.push(v);
|
||||
}
|
||||
let i = rand::thread_rng().gen_range(0..values.len());
|
||||
let v = values.swap_remove(i);
|
||||
Ok(cell_take(v))
|
||||
}
|
||||
}
|
||||
}
|
170
talc-std/src/value.rs
Normal file
170
talc-std/src/value.rs
Normal file
|
@ -0,0 +1,170 @@
|
|||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use talc_lang::{exception, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, value::{exception::Result, HashValue, Value}, Vm};
|
||||
use talc_macros::native_func;
|
||||
|
||||
use crate::unpack_args;
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
vm.set_global_name("type", type_().into());
|
||||
vm.set_global_name("is", is().into());
|
||||
vm.set_global_name("as", as_().into());
|
||||
vm.set_global_name("copy", copy().into());
|
||||
|
||||
vm.set_global_name("str", str_().into());
|
||||
vm.set_global_name("repr", repr().into());
|
||||
|
||||
vm.set_global_name("cell", cell().into());
|
||||
vm.set_global_name("uncell", uncell().into());
|
||||
vm.set_global_name("cell_replace", cell_replace().into());
|
||||
vm.set_global_name("cell_take", cell_replace().into());
|
||||
}
|
||||
|
||||
//
|
||||
// types
|
||||
//
|
||||
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn type_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, val] = unpack_args!(args);
|
||||
Ok(val.get_type().into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn is(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, val, ty] = unpack_args!(args);
|
||||
let Value::Symbol(ty) = ty else {
|
||||
throw!(*SYM_TYPE_ERROR, "type expected symbol, got {ty:#}")
|
||||
};
|
||||
Ok((val.get_type() == ty).into())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, val, ty] = unpack_args!(args);
|
||||
let Value::Symbol(ty) = ty else {
|
||||
throw!(*SYM_TYPE_ERROR, "type expected symbol, got {ty:#}")
|
||||
};
|
||||
if val.get_type() == ty {
|
||||
return Ok(val)
|
||||
}
|
||||
match (val, ty.name().as_ref()) {
|
||||
(_, "nil") => Ok(Value::Nil),
|
||||
(v, "string") => Ok(Value::String(v.to_string().into())),
|
||||
(v, "bool") => Ok(Value::Bool(v.truthy())),
|
||||
|
||||
(Value::Symbol(s), "int") => Ok(Value::Int(s.id() as i64)),
|
||||
|
||||
(Value::Int(x), "ratio") => Ok(Value::Ratio(x.into())),
|
||||
(Value::Int(x), "float") => Ok(Value::Float(x as f64)),
|
||||
(Value::Int(x), "complex") => Ok(Value::Complex((x as f64).into())),
|
||||
(Value::Ratio(x), "float") => Ok(Value::Float(*x.numer() as f64 / *x.denom() as f64)),
|
||||
(Value::Ratio(x), "complex") => Ok(Value::Complex((*x.numer() as f64 / *x.denom() as f64).into())),
|
||||
(Value::Float(x), "complex") => Ok(Value::Complex(x.into())),
|
||||
|
||||
(Value::String(s), "int")
|
||||
=> parse_int(&s, 10)
|
||||
.map(|v| v.into())
|
||||
.map_err(|_| exception!(*SYM_TYPE_ERROR, "could not parse {s:#} as integer")),
|
||||
|
||||
(Value::String(s), "float")
|
||||
=> parse_float(&s)
|
||||
.map(|v| v.into())
|
||||
.map_err(|_| exception!(*SYM_TYPE_ERROR, "could not parse {s:#} as float")),
|
||||
|
||||
(v, t) => throw!(*SYM_TYPE_ERROR,
|
||||
"cannot convert value of type {} to type {t}", v.get_type().name())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_inner(value: Value) -> Result<Value> {
|
||||
match value {
|
||||
Value::Nil | Value::Bool(_) | Value::Symbol(_)
|
||||
| Value::Int(_) | Value::Ratio(_) | Value::Float(_)
|
||||
| Value::Complex(_) | Value::Range(_) | Value::String(_)
|
||||
=> Ok(value),
|
||||
Value::Cell(c) => {
|
||||
let c = copy_inner(talc_lang::value::cell_take(c))?;
|
||||
Ok(RefCell::new(c).into())
|
||||
},
|
||||
Value::List(l) => {
|
||||
let l = talc_lang::value::cell_take(l);
|
||||
let v: Result<Vec<Value>> = l.into_iter()
|
||||
.map(copy_inner)
|
||||
.collect();
|
||||
Ok(v?.into())
|
||||
},
|
||||
Value::Table(t) => {
|
||||
let t = talc_lang::value::cell_take(t);
|
||||
let v: Result<HashMap<HashValue, Value>> = t.into_iter()
|
||||
.map(|(k, v)| copy_inner(v).map(|v| (k, v)))
|
||||
.collect();
|
||||
Ok(v?.into())
|
||||
},
|
||||
_ => throw!(*SYM_TYPE_ERROR,
|
||||
"cannot copy value of type {}", value.get_type().name())
|
||||
}
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn copy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, val] = unpack_args!(args);
|
||||
copy_inner(val)
|
||||
}
|
||||
|
||||
//
|
||||
// strings
|
||||
//
|
||||
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn str_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, val] = unpack_args!(args);
|
||||
Ok(val.to_string().into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn repr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, val] = unpack_args!(args);
|
||||
Ok(format!("{val:#}").into())
|
||||
}
|
||||
|
||||
//
|
||||
// cells
|
||||
//
|
||||
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn cell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, value] = unpack_args!(args);
|
||||
Ok(RefCell::new(value).into())
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn uncell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, cell] = unpack_args!(args);
|
||||
let Value::Cell(cell) = cell else {
|
||||
throw!(*SYM_TYPE_ERROR, "value is not a cell")
|
||||
};
|
||||
Ok(Rc::unwrap_or_clone(cell).into_inner())
|
||||
}
|
||||
|
||||
#[native_func(2)]
|
||||
pub fn cell_replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, cell, value] = unpack_args!(args);
|
||||
let Value::Cell(cell) = cell else {
|
||||
throw!(*SYM_TYPE_ERROR, "value is not a cell")
|
||||
};
|
||||
Ok(cell.replace(value))
|
||||
}
|
||||
|
||||
#[native_func(1)]
|
||||
pub fn cell_take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, cell] = unpack_args!(args);
|
||||
let Value::Cell(cell) = cell else {
|
||||
throw!(*SYM_TYPE_ERROR, "value is not a cell")
|
||||
};
|
||||
Ok(cell.replace(Value::Nil))
|
||||
}
|
||||
|
Loading…
Reference in a new issue