Compare commits

...

8 commits

Author SHA1 Message Date
151c6abf45 rustfmt fixes 2024-11-04 13:31:53 -05:00
ba7db1e91b rustfmt 2024-11-04 13:25:31 -05:00
801aaf7b78 switch to symbols, update formatting 2024-11-04 12:59:05 -05:00
59f3e320e3 fix imaginary, refactor 2024-11-03 14:55:49 -05:00
0560847753 rewrite parser, checked arithmetic 2024-11-03 12:50:36 -05:00
d04f98dedc add closures 2024-10-31 17:20:20 -04:00
754fbf6c2c removed variadics, prepared for closures 2024-10-31 12:36:53 -04:00
e4b4c981d2 update packages 2024-10-28 22:42:21 -04:00
48 changed files with 5660 additions and 2673 deletions

719
Cargo.lock generated

File diff suppressed because it is too large Load diff

23
examples/closures.talc Executable file
View file

@ -0,0 +1,23 @@
#!/usr/bin/env talc
-- adapted from Crafting Interpreters 10.6
make_counter = \-> do
var i = 0
\-> do
i += 1
println(i)
end
end
var counter1 = make_counter()
counter1() -- 1
counter1() -- 2
counter1() -- 3
counter1() -- 4
var counter2 = make_counter()
counter2() -- 1
counter2() -- 2
counter1() -- 5
counter1() -- 6
counter2() -- 3

23
examples/closures2.talc Executable file
View file

@ -0,0 +1,23 @@
#!/usr/bin/env talc
var i = 0;
var outer = \n -> do
var inner = \-> do
i = i + n
end
end
var by3 = outer(3)
var by5 = outer(5)
by3()
println(i) -- 3
by3()
println(i) -- 6
by5()
println(i) -- 11
by5()
println(i) -- 16
by3()
println(i) -- 19

9
examples/totient.talc Executable file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env talc
totient = \n -> do
count(factors(n)) | pairs | map(\v -> do
v[0]^v[1] - v[0]^(v[1] - 1)
end) | prod
end
println(totient(6615)) -- 3024

3
format.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
cargo +nightly fmt

11
rustfmt.toml Normal file
View file

@ -0,0 +1,11 @@
unstable_features = true
hard_tabs = true
tab_spaces = 4
max_width = 92
trailing_semicolon = false
trailing_comma = "Vertical"
use_field_init_shorthand = true

View file

@ -11,5 +11,5 @@ path = "src/main.rs"
talc-lang = { path = "../talc-lang" } talc-lang = { path = "../talc-lang" }
talc-std = { path = "../talc-std" } talc-std = { path = "../talc-std" }
rustyline = "14.0" rustyline = "14.0"
clap = { version = "4.5", features = ["derive"] } clap = { version = "4.5", features = ["std", "help", "usage", "derive", "error-context"], default-features = false }
ctrlc = "3.4" ctrlc = "3.4"

View file

@ -1,42 +1,25 @@
use std::{borrow::Cow, cell::RefCell, collections::HashMap, rc::Rc}; use std::{borrow::Cow, cell::RefCell, rc::Rc};
use rustyline::{completion::Completer, highlight::Highlighter, hint::Hinter, validate::{ValidationContext, ValidationResult, Validator}, Helper, Result}; use rustyline::{
use talc_lang::{lstring::LStr, Lexer, Vm}; completion::Completer,
highlight::Highlighter,
#[derive(Clone, Copy)] hint::Hinter,
enum TokenType { validate::{ValidationContext, ValidationResult, Validator},
String, Symbol, Number, Literal Helper, Result,
} };
use talc_lang::{
lstring::LStr,
parser::{Lexer, Pos, Span, TokenKind},
Vm,
};
pub struct TalcHelper { pub struct TalcHelper {
vm: Rc<RefCell<Vm>>, 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 { impl TalcHelper {
pub fn new(vm: Rc<RefCell<Vm>>) -> Self { pub fn new(vm: Rc<RefCell<Vm>>) -> Self {
let lex = Lexer::new(); Self { vm }
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,
}
} }
} }
@ -60,7 +43,11 @@ impl Completer for TalcHelper {
} }
} }
let res: String = res.chars().rev().collect(); let res: String = res.chars().rev().collect();
let mut keys = self.vm.borrow().globals().keys() let mut keys = self
.vm
.borrow()
.globals()
.keys()
.map(|sym| sym.name()) .map(|sym| sym.name())
.filter(|name| name.starts_with(LStr::from_str(&res))) .filter(|name| name.starts_with(LStr::from_str(&res)))
.map(LStr::to_string) .map(LStr::to_string)
@ -76,24 +63,33 @@ impl Hinter for TalcHelper {
impl Highlighter for TalcHelper { impl Highlighter for TalcHelper {
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
let mut tokens = self.lex.lex(line).peekable(); let mut lexer = Lexer::new(line);
let mut buf = String::new(); let mut buf = String::new();
let mut last = 0; let mut last = Pos::new();
while let Some(Ok((l, tok, r))) = tokens.next() { while let Some(Ok(token)) = lexer.next() {
buf += &line[last..l]; if token.kind == TokenKind::Eof {
last = r; break
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..]; buf += Span::new(last, token.span.start).of(line);
last = token.span.end;
let format = match token.kind {
TokenKind::Nil
| TokenKind::True
| TokenKind::False
| TokenKind::Integer
| TokenKind::Float
| TokenKind::Imaginary => "\x1b[93m",
TokenKind::String => "\x1b[92m",
TokenKind::Symbol => "\x1b[96m",
_ => "",
};
buf += format;
buf += token.content;
if !format.is_empty() {
buf += "\x1b[0m"
}
}
buf += &line[(last.idx as usize)..];
Cow::Owned(buf) Cow::Owned(buf)
} }
@ -116,62 +112,92 @@ impl Highlighter for TalcHelper {
impl Validator for TalcHelper { impl Validator for TalcHelper {
fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> { fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
let tokens = self.lex.lex(ctx.input()); use TokenKind as K;
let lexer = Lexer::new(ctx.input());
let mut delims = Vec::new(); let mut delims = Vec::new();
let mut mismatch = None; let mut mismatch = None;
for token in tokens { for token in lexer {
let token = match token { let token = match token {
Ok(t) => t, Ok(t) => t,
Err(e) => return Ok(ValidationResult::Invalid( Err(e) => return Ok(ValidationResult::Invalid(Some(format!(" {e}")))),
Some(e.to_string()))),
}; };
let t = token.1.1; let k = token.kind;
match t { let s = token.span;
"(" | "{" | "[" | "if" | "while" | "for" | "try" match k {
=> delims.push(token.1.1), K::Eof => break,
")" => match delims.pop() { K::LParen | K::LBrack | K::LBrace | K::If | K::While | K::For | K::Try => {
Some("(") => (), delims.push(token.kind)
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() { K::RParen => match delims.pop() {
Some("try") => delims.push(t), Some(K::LParen) => (),
v => { mismatch = Some((v, t)); break } v => {
mismatch = Some((v, k, s));
break
} }
"do" => match delims.last().copied() { },
Some("while" | "for" | "catch") => { K::RBrack => match delims.pop() {
Some(K::LBrack) => (),
v => {
mismatch = Some((v, k, s));
break
}
},
K::RBrace => match delims.pop() {
Some(K::LBrace) => (),
v => {
mismatch = Some((v, k, s));
break
}
},
K::Then => match delims.pop() {
Some(K::If | K::Elif) => delims.push(k),
v => {
mismatch = Some((v, k, s));
break
}
},
K::Catch => match delims.pop() {
Some(K::Try) => delims.push(k),
v => {
mismatch = Some((v, k, s));
break
}
},
K::Do => match delims.last().copied() {
Some(K::While | K::For | K::Catch) => {
delims.pop(); delims.pop();
delims.push(t); delims.push(k);
}
_ => delims.push(k),
}, },
_ => delims.push(t) K::Elif | K::Else => match delims.pop() {
Some(K::Then) => delims.push(k),
v => {
mismatch = Some((v, k, s));
break
}
}, },
"elif" | "else" => match delims.pop() { K::End => match delims.pop() {
Some("then") => delims.push(t), Some(K::Then | K::Else | K::Do | K::Try | K::Catch) => (),
v => { mismatch = Some((v, t)); break } v => {
}, mismatch = Some((v, k, s));
"end" => match delims.pop() { break
Some("then" | "elif" | "else" | "do" | "try") => (), }
v => { mismatch = Some((v, t)); break }
}, },
_ => (), _ => (),
} }
} }
match mismatch { match mismatch {
Some((None, b)) => return Ok(ValidationResult::Invalid(Some( Some((None, b, s)) => {
format!(" found unmatched {b}")))), return Ok(ValidationResult::Invalid(Some(format!(
Some((Some(a), b)) => return Ok(ValidationResult::Invalid(Some( " found unmatched {b} at {s}"
format!(" found {a} matched with {b}")))), ))))
}
Some((Some(a), b, s)) => {
return Ok(ValidationResult::Invalid(Some(format!(
" found {a} matched with {b} at {s}"
))))
}
_ => (), _ => (),
} }
@ -180,6 +206,5 @@ impl Validator for TalcHelper {
} else { } else {
Ok(ValidationResult::Incomplete) Ok(ValidationResult::Incomplete)
} }
} }
} }

View file

@ -1,9 +1,11 @@
use clap::{ColorChoice, Parser}; use clap::{ColorChoice, Parser};
use talc_lang::{compiler::compile, value::function::disasm_recursive, Vm};
use std::{path::PathBuf, process::ExitCode, rc::Rc}; use std::{path::PathBuf, process::ExitCode, rc::Rc};
use talc_lang::{
compiler::compile, parser, symbol::Symbol, value::function::disasm_recursive, Vm,
};
mod repl;
mod helper; mod helper;
mod repl;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
@ -28,20 +30,19 @@ struct Args {
color: ColorChoice, color: ColorChoice,
} }
fn exec(src: &str, args: &Args) -> ExitCode { fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
let parser = talc_lang::Parser::new();
let mut vm = Vm::new(256); let mut vm = Vm::new(256);
talc_std::load_all(&mut vm); talc_std::load_all(&mut vm);
let ex = match parser.parse(src) { let ex = match parser::parse(src) {
Ok(ex) => ex, Ok(ex) => ex,
Err(e) => { Err(e) => {
eprintln!("Error: {e}"); eprintln!("Error: {e}");
return ExitCode::FAILURE return ExitCode::FAILURE
}, }
}; };
let func = Rc::new(compile(&ex)); let func = Rc::new(compile(&ex, Some(name)));
if args.disasm { if args.disasm {
if let Err(e) = disasm_recursive(&func, &mut std::io::stderr()) { if let Err(e) = disasm_recursive(&func, &mut std::io::stderr()) {
@ -65,8 +66,10 @@ fn main() -> ExitCode {
return repl::repl(&args) return repl::repl(&args)
} }
match std::fs::read_to_string(args.file.as_ref().unwrap()) { let file = args.file.as_ref().unwrap();
Ok(s) => exec(&s, &args),
match std::fs::read_to_string(file) {
Ok(s) => exec(Symbol::get(file.as_os_str()), &s, &args),
Err(e) => { Err(e) => {
eprintln!("Error: {e}"); eprintln!("Error: {e}");
ExitCode::FAILURE ExitCode::FAILURE

View file

@ -1,8 +1,18 @@
use std::{cell::RefCell, io::IsTerminal, process::ExitCode, rc::Rc}; use std::{cell::RefCell, io::IsTerminal, process::ExitCode, rc::Rc};
use clap::ColorChoice; use clap::ColorChoice;
use rustyline::{error::ReadlineError, history::{FileHistory, History}, ColorMode, Config, Editor}; use rustyline::{
use talc_lang::{compiler::compile_repl, symbol::Symbol, value::{function::disasm_recursive, Value}, Vm}; error::ReadlineError,
history::{FileHistory, History},
ColorMode, Config, Editor,
};
use talc_lang::{
compiler::compile_repl,
parser,
symbol::Symbol,
value::{function::disasm_recursive, Value},
Vm,
};
use crate::{helper::TalcHelper, Args}; use crate::{helper::TalcHelper, Args};
@ -30,7 +40,6 @@ impl ReplColors {
} }
} }
fn get_colmode(args: &Args) -> ColorMode { fn get_colmode(args: &Args) -> ColorMode {
match args.color { match args.color {
ColorChoice::Auto => ColorMode::Enabled, ColorChoice::Auto => ColorMode::Enabled,
@ -45,7 +54,8 @@ pub fn init_rustyline(args: &Args) -> Result<Editor<TalcHelper, FileHistory>, Ex
.color_mode(get_colmode(args)) .color_mode(get_colmode(args))
.check_cursor_position(true) .check_cursor_position(true)
.completion_type(rustyline::CompletionType::List) .completion_type(rustyline::CompletionType::List)
.max_history_size(4096).unwrap() .max_history_size(4096)
.unwrap()
.build(); .build();
let mut hist = FileHistory::default(); let mut hist = FileHistory::default();
if let Some(f) = &args.histfile { if let Some(f) = &args.histfile {
@ -60,12 +70,12 @@ pub fn init_rustyline(args: &Args) -> Result<Editor<TalcHelper, FileHistory>, Ex
Err(ReadlineError::Io(e)) => { Err(ReadlineError::Io(e)) => {
eprintln!("Error creating repl: {e}"); eprintln!("Error creating repl: {e}");
Err(ExitCode::FAILURE) Err(ExitCode::FAILURE)
}, }
Err(ReadlineError::Errno(e)) => { Err(ReadlineError::Errno(e)) => {
eprintln!("Error creating repl: {e}"); eprintln!("Error creating repl: {e}");
Err(ExitCode::FAILURE) Err(ExitCode::FAILURE)
}, }
Err(_) => Err(ExitCode::SUCCESS) Err(_) => Err(ExitCode::SUCCESS),
} }
} }
@ -74,7 +84,6 @@ pub fn repl(args: &Args) -> ExitCode {
eprintln!("input disassembly enabled"); eprintln!("input disassembly enabled");
} }
let parser = talc_lang::Parser::new();
let mut compiler_globals = Vec::new(); let mut compiler_globals = Vec::new();
let mut vm = Vm::new(256); let mut vm = Vm::new(256);
talc_std::load_all(&mut vm); talc_std::load_all(&mut vm);
@ -118,15 +127,15 @@ pub fn repl(args: &Args) -> ExitCode {
Err(e) => { Err(e) => {
eprintln!("{}Error:{} {e}", c.error, c.reset); eprintln!("{}Error:{} {e}", c.error, c.reset);
continue continue
}, }
}; };
let ex = match parser.parse(&line) { let ex = match parser::parse(&line) {
Ok(ex) => ex, Ok(ex) => ex,
Err(e) => { Err(e) => {
eprintln!("{}Error:{} {e}", c.error, c.reset); eprintln!("{}Error:{} {e}", c.error, c.reset);
continue continue
}, }
}; };
let (f, g) = compile_repl(&ex, &compiler_globals); let (f, g) = compile_repl(&ex, &compiler_globals);
@ -155,4 +164,3 @@ pub fn repl(args: &Args) -> ExitCode {
} }
} }
} }

View file

@ -4,13 +4,11 @@ version = "0.2.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
lalrpop-util = { version = "0.20", features = ["lexer", "unicode"] }
num-complex = "0.4" num-complex = "0.4"
num-rational = { version = "0.4", default-features = false, features = [] } num-rational = { version = "0.4", default-features = false, features = [] }
num-traits = "0.2" num-traits = "0.2"
thiserror = "1.0" thiserror = "1.0"
lazy_static = "1.4" lazy_static = "1.5"
unicode-ident = "1.0" unicode-ident = "1.0"
[build-dependencies] [build-dependencies]
lalrpop = { version = "0.20", default_features = false, features = ["lexer", "unicode"]}

View file

@ -1,3 +0,0 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
lalrpop::process_root()
}

View file

@ -1,59 +0,0 @@
use crate::{lstring::LStr, symbol::Symbol, value::Value};
#[derive(Clone, Copy, Debug)]
pub enum BinaryOp {
Add, Sub, Mul, Div, Mod, Pow, IntDiv,
Shr, Shl, BitAnd, BitXor, BitOr,
Eq, Ne, Gt, Lt, Ge, Le,
Concat, Append,
Range, RangeIncl,
}
#[derive(Clone, Copy, Debug)]
pub enum UnaryOp {
Neg, Not, RangeEndless,
}
#[derive(Debug)]
pub enum Expr<'s> {
Literal(Value),
Ident(&'s LStr),
UnaryOp(UnaryOp, Box<Expr<'s>>),
BinaryOp(BinaryOp, Box<Expr<'s>>, Box<Expr<'s>>),
Assign(Option<BinaryOp>, Box<LValue<'s>>, Box<Expr<'s>>),
AssignVar(&'s LStr, Box<Expr<'s>>),
AssignGlobal(&'s LStr, Box<Expr<'s>>),
Index(Box<Expr<'s>>, Box<Expr<'s>>),
FnCall(Box<Expr<'s>>, Vec<Expr<'s>>),
AssocFnCall(Box<Expr<'s>>, Symbol, Vec<Expr<'s>>),
Pipe(Box<Expr<'s>>, Box<Expr<'s>>),
Block(Vec<Expr<'s>>),
List(Vec<Expr<'s>>),
Table(Vec<(Expr<'s>, Expr<'s>)>),
Return(Box<Expr<'s>>),
And(Box<Expr<'s>>, Box<Expr<'s>>),
Or(Box<Expr<'s>>, Box<Expr<'s>>),
If(Box<Expr<'s>>, Box<Expr<'s>>, Option<Box<Expr<'s>>>),
While(Box<Expr<'s>>, Box<Expr<'s>>),
For(&'s LStr, Box<Expr<'s>>, Box<Expr<'s>>),
Lambda(Vec<&'s LStr>, Box<Expr<'s>>),
Try(Box<Expr<'s>>, Vec<CatchBlock<'s>>),
}
#[derive(Debug)]
pub struct CatchBlock<'s> {
pub name: Option<&'s LStr>,
pub types: Option<Vec<Symbol>>,
pub body: Expr<'s>,
}
#[derive(Debug)]
pub enum LValue<'s> {
Ident(&'s LStr),
Index(Box<Expr<'s>>, Box<Expr<'s>>),
}

View file

@ -1,4 +1,8 @@
use crate::{value::Value, ast::{UnaryOp, BinaryOp}, symbol::Symbol}; use crate::{
parser::ast::{BinaryOp, UnaryOp},
symbol::Symbol,
value::Value,
};
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct Arg24([u8; 3]); pub struct Arg24([u8; 3]);
@ -13,14 +17,20 @@ impl Arg24 {
#[inline] #[inline]
pub fn from_i32(n: i32) -> Self { pub fn from_i32(n: i32) -> Self {
assert!((-0x80_0000..=0x7f_ffff).contains(&n), "value out of range for argument"); assert!(
(-0x80_0000..=0x7f_ffff).contains(&n),
"value out of range for argument"
);
// can't panic: size of slice guaranteed // can't panic: size of slice guaranteed
Self(n.to_le_bytes()[0..3].try_into().unwrap()) Self(n.to_le_bytes()[0..3].try_into().unwrap())
} }
#[inline] #[inline]
pub fn from_i64(n: i64) -> Self { pub fn from_i64(n: i64) -> Self {
assert!((-0x80_0000..=0x7f_ffff).contains(&n), "value out of range for argument"); assert!(
(-0x80_0000..=0x7f_ffff).contains(&n),
"value out of range for argument"
);
// can't panic: size of slice guaranteed // can't panic: size of slice guaranteed
Self(n.to_le_bytes()[0..3].try_into().unwrap()) Self(n.to_le_bytes()[0..3].try_into().unwrap())
} }
@ -64,7 +74,9 @@ impl From<Arg24> for i32 {
fn from(v: Arg24) -> Self { fn from(v: Arg24) -> Self {
let mut n = u32::from(v); let mut n = u32::from(v);
// sign-extend // sign-extend
if n & 0x00_800000 != 0 { n |= 0xff_000000; } if n & 0x00_800000 != 0 {
n |= 0xff_000000;
}
n as i32 n as i32
} }
} }
@ -88,38 +100,55 @@ impl TryFrom<Arg24> for Symbol {
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub enum Instruction { pub enum Instruction {
#[default] #[default]
Nop, Nop, // do nothing
LoadLocal(Arg24), LoadLocal(Arg24), // push nth local onto stack
StoreLocal(Arg24), StoreLocal(Arg24), // pop stack into nth local
NewLocal, NewLocal, // pop stack into a new local
DropLocal(Arg24), DropLocal(Arg24), // remove last n locals
LoadGlobal(Arg24), StoreGlobal(Arg24), LoadGlobal(Arg24), // load global by id
StoreGlobal(Arg24), // store global by id
Const(Arg24), CloseOver(Arg24), // load nth local and convert to cell, write back a copy
Int(Arg24), Closure(Arg24), // load constant function and fill state from stack
Symbol(Arg24), LoadUpvalue(Arg24), // load
Bool(bool), StoreUpvalue(Arg24), // store a cell from closure state to new local
Nil, ContinueUpvalue(Arg24), //
LoadClosedLocal(Arg24), // load through cell in nth local
StoreClosedLocal(Arg24), // store through cell in nth local
Dup, DupTwo, Drop(Arg24), Swap, Const(Arg24), // push nth constant
Int(Arg24), // push integer
Symbol(Arg24), // push symbol
Bool(bool), // push boolean
Nil, // push nil
Dup,
DupTwo,
Drop(Arg24),
Swap,
UnaryOp(UnaryOp), UnaryOp(UnaryOp),
BinaryOp(BinaryOp), BinaryOp(BinaryOp),
NewList(u8), GrowList(u8), NewList(u8),
NewTable(u8), GrowTable(u8), GrowList(u8),
NewTable(u8),
GrowTable(u8),
Index, StoreIndex, Index,
StoreIndex,
Jump(Arg24), Jump(Arg24),
JumpTrue(Arg24), JumpTrue(Arg24),
JumpFalse(Arg24), JumpFalse(Arg24),
IterBegin, IterTest(Arg24), IterBegin,
IterTest(Arg24),
BeginTry(Arg24), EndTry, BeginTry(Arg24),
EndTry,
Call(u8), Call(u8),
Return, Return,
@ -133,14 +162,30 @@ impl std::fmt::Display for Instruction {
Self::StoreLocal(a) => write!(f, "store {}", usize::from(a)), Self::StoreLocal(a) => write!(f, "store {}", usize::from(a)),
Self::NewLocal => write!(f, "newlocal"), Self::NewLocal => write!(f, "newlocal"),
Self::DropLocal(n) => write!(f, "discardlocal {}", usize::from(n)), Self::DropLocal(n) => write!(f, "discardlocal {}", usize::from(n)),
Self::LoadGlobal(s) => write!(f, "loadglobal {}", Self::LoadGlobal(s) => write!(
Symbol::try_from(s).expect("symbol does not exist").name()), f,
Self::StoreGlobal(s) => write!(f, "storeglobal {}", "loadglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()), Symbol::try_from(s).expect("symbol does not exist").name()
),
Self::StoreGlobal(s) => write!(
f,
"storeglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()
),
Self::CloseOver(n) => write!(f, "closeover {}", usize::from(n)),
Self::Closure(c) => write!(f, "closure {}", usize::from(c)),
Self::LoadUpvalue(n) => write!(f, "load_upval {}", usize::from(n)),
Self::StoreUpvalue(n) => write!(f, "store_upval {}", usize::from(n)),
Self::ContinueUpvalue(n) => write!(f, "cont_upval {}", usize::from(n)),
Self::LoadClosedLocal(n) => write!(f, "load_closed {}", usize::from(n)),
Self::StoreClosedLocal(n) => write!(f, "store_closed {}", usize::from(n)),
Self::Const(c) => write!(f, "const {}", usize::from(c)), Self::Const(c) => write!(f, "const {}", usize::from(c)),
Self::Int(i) => write!(f, "int {}", i64::from(i)), Self::Int(i) => write!(f, "int {}", i64::from(i)),
Self::Symbol(s) => write!(f, "symbol {}", Self::Symbol(s) => write!(
Symbol::try_from(s).expect("symbol does not exist").name()), f,
"symbol {}",
Symbol::try_from(s).expect("symbol does not exist").name()
),
Self::Bool(b) => write!(f, "bool {b}"), Self::Bool(b) => write!(f, "bool {b}"),
Self::Nil => write!(f, "nil"), Self::Nil => write!(f, "nil"),
Self::Dup => write!(f, "dup"), Self::Dup => write!(f, "dup"),
@ -193,19 +238,28 @@ impl Chunk {
} }
pub fn add_const(&mut self, v: Value) -> usize { pub fn add_const(&mut self, v: Value) -> usize {
assert!(self.consts.len() < 0xff_ffff, "too many constants in a chunk"); assert!(
self.consts.len() < 0xff_ffff,
"too many constants in a chunk"
);
self.consts.push(v); self.consts.push(v);
self.consts.len() - 1 self.consts.len() - 1
} }
pub fn add_instr(&mut self, i: Instruction) -> usize { pub fn add_instr(&mut self, i: Instruction) -> usize {
assert!(self.instrs.len() < 0xff_ffff, "too many instructions in a chunk"); assert!(
self.instrs.len() < 0xff_ffff,
"too many instructions in a chunk"
);
self.instrs.push(i); self.instrs.push(i);
self.instrs.len() - 1 self.instrs.len() - 1
} }
pub fn begin_try_table(&mut self, local_count: usize) -> (usize, TryTable) { pub fn begin_try_table(&mut self, local_count: usize) -> (usize, TryTable) {
assert!(self.try_tables.len() < 0xff_ffff, "too many catch tables in a chunk"); assert!(
self.try_tables.len() < 0xff_ffff,
"too many catch tables in a chunk"
);
let table = TryTable { let table = TryTable {
catches: Vec::new(), catches: Vec::new(),
local_count, local_count,
@ -218,5 +272,3 @@ impl Chunk {
self.try_tables[idx] = table; self.try_tables[idx] = table;
} }
} }

View file

@ -1,22 +1,36 @@
use std::collections::{BTreeMap, HashMap};
use std::rc::Rc; use std::rc::Rc;
use crate::ast::{BinaryOp, Expr, LValue, CatchBlock}; use crate::chunk::{Arg24, Catch, Chunk, Instruction as I};
use crate::chunk::{Instruction as I, Chunk, Arg24, Catch}; use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
use crate::lstr; use crate::symbol::{Symbol, SYM_SELF};
use crate::lstring::LStr;
use crate::symbol::Symbol;
use crate::value::function::{FuncAttrs, Function}; use crate::value::function::{FuncAttrs, Function};
use crate::value::Value; use crate::value::Value;
enum ResolveOutcome {
Var(VarKind),
InParent,
None,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum VarKind {
Local(usize),
Closed(usize),
Global,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Local { pub struct Var {
name: Rc<LStr>, name: Symbol,
scope: usize, kind: VarKind,
} }
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
enum CompilerMode { enum CompilerMode {
Function, Repl, Module, Function,
Repl,
Module,
} }
struct Compiler<'a> { struct Compiler<'a> {
@ -24,18 +38,19 @@ struct Compiler<'a> {
parent: Option<&'a Compiler<'a>>, parent: Option<&'a Compiler<'a>>,
chunk: Chunk, chunk: Chunk,
attrs: FuncAttrs, attrs: FuncAttrs,
scope: usize, scope: HashMap<Symbol, Var>,
locals: Vec<Local>, shadowed: Vec<(Symbol, Option<Var>)>,
globals: Vec<Local>, closes: BTreeMap<Symbol, usize>,
local_count: usize,
} }
pub fn compile(expr: &Expr) -> Function { pub fn compile(expr: &Expr, name: Option<Symbol>) -> Function {
let mut comp = Compiler::new_module(None); let mut comp = Compiler::new_module(name, None);
comp.expr(expr); comp.expr(expr);
comp.finish() comp.finish()
} }
pub fn compile_repl(expr: &Expr, globals: &[Local]) -> (Function, Vec<Local>) { pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec<Symbol>) {
let mut comp = Compiler::new_repl(globals); let mut comp = Compiler::new_repl(globals);
comp.expr(expr); comp.expr(expr);
comp.finish_repl() comp.finish_repl()
@ -43,52 +58,66 @@ pub fn compile_repl(expr: &Expr, globals: &[Local]) -> (Function, Vec<Local>) {
impl<'a> Default for Compiler<'a> { impl<'a> Default for Compiler<'a> {
fn default() -> Self { fn default() -> Self {
let locals = vec![Local { let mut scope = HashMap::new();
name: lstr!("self").into(), scope.insert(
scope: 0, *SYM_SELF,
}]; Var {
name: *SYM_SELF,
kind: VarKind::Local(0),
},
);
Self { Self {
mode: CompilerMode::Function, mode: CompilerMode::Function,
parent: None, parent: None,
chunk: Chunk::new(), chunk: Chunk::new(),
attrs: FuncAttrs { arity: 0, variadic: false }, attrs: FuncAttrs::default(),
scope: 0, scope,
locals, shadowed: Vec::new(),
globals: Vec::new(), local_count: 1,
closes: BTreeMap::new(),
} }
} }
} }
impl<'a> Compiler<'a> { impl<'a> Compiler<'a> {
fn new_repl(globals: &[Local]) -> Self { fn new_repl(globals: &[Symbol]) -> Self {
Self { let mut new = Self {
mode: CompilerMode::Repl, mode: CompilerMode::Repl,
globals: globals.to_vec(), attrs: FuncAttrs {
arity: 0,
name: Some(Symbol::get("<repl>")),
},
..Self::default() ..Self::default()
};
for g in globals {
new.declare_global(*g);
} }
new
} }
fn new_module(parent: Option<&'a Self>) -> Self { fn new_module(name: Option<Symbol>, parent: Option<&'a Self>) -> Self {
Self { Self {
mode: CompilerMode::Module, mode: CompilerMode::Module,
attrs: FuncAttrs { arity: 0, name },
parent, parent,
..Self::default() ..Self::default()
} }
} }
fn new_function(&'a self, args: &[&LStr]) -> Self { fn new_function(&'a self, name: Option<Symbol>, args: &[Symbol]) -> Self {
let mut new = Self { let mut new = Self {
mode: CompilerMode::Function, mode: CompilerMode::Function,
attrs: FuncAttrs {
arity: args.len(),
name,
},
parent: Some(self), parent: Some(self),
..Self::default() ..Self::default()
}; };
new.attrs.arity = args.len();
for arg in args { for arg in args {
new.locals.push(Local { new.declare_local(*arg);
name: (*arg).into(),
scope: 0,
});
} }
new new
@ -96,14 +125,27 @@ impl<'a> Compiler<'a> {
pub fn finish(mut self) -> Function { pub fn finish(mut self) -> Function {
self.emit(I::Return); self.emit(I::Return);
Function { chunk: Rc::new(self.chunk), attrs: self.attrs } Function::new(Rc::new(self.chunk), self.attrs, self.closes.len())
} }
pub fn finish_repl(mut self) -> (Function, Vec<Local>) { pub fn finish_inner(mut self) -> (Function, BTreeMap<Symbol, usize>) {
self.emit(I::Return);
// TODO closure
(
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
self.closes,
)
}
pub fn finish_repl(mut self) -> (Function, Vec<Symbol>) {
self.emit(I::Return); self.emit(I::Return);
( (
Function { chunk: Rc::new(self.chunk), attrs: self.attrs }, // TODO closure
self.globals Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
self.scope
.into_iter()
.filter_map(|(_, v)| (v.kind == VarKind::Global).then_some(v.name))
.collect(),
) )
} }
@ -126,36 +168,44 @@ impl<'a> Compiler<'a> {
// dup followed by store: remove the dup // dup followed by store: remove the dup
if instrs.len() >= 2 if instrs.len() >= 2
&& matches!(instrs.get(instrs.len() - 2), Some(I::Dup)) && matches!(instrs.get(instrs.len() - 2), Some(I::Dup))
&& matches!(instrs.last(), Some( && matches!(
I::NewLocal | I::StoreLocal(_) | I::StoreGlobal(_) instrs.last(),
)) Some(
{ I::NewLocal
| I::StoreLocal(_) | I::StoreGlobal(_)
| I::StoreClosedLocal(_)
| I::StoreUpvalue(_)
)
) {
// can't panic: checked that instrs.len() >= 2 // can't panic: checked that instrs.len() >= 2
let i = self.chunk.instrs.pop().unwrap(); let i = self.chunk.instrs.pop().unwrap();
self.chunk.instrs.pop().unwrap(); self.chunk.instrs.pop().unwrap();
self.chunk.instrs.push(i); self.chunk.instrs.push(i);
n -= 1; n -= 1;
continue; continue
} }
// final side-effectless instruction // final side-effectless instruction
let poppable = matches!( let poppable = matches!(
instrs.last(), instrs.last(),
Some( Some(
I::Dup | I::Const(_) | I::Int(_) I::Dup
| I::Nil | I::Bool(_) | I::Symbol(_) | I::Const(_) | I::Int(_)
| I::LoadLocal(_) | I::Nil | I::Bool(_)
| I::Symbol(_) | I::LoadLocal(_)
| I::LoadClosedLocal(_)
| I::LoadUpvalue(_)
) )
); );
if poppable { if poppable {
// can't panic: checked that instrs.last() was Some // can't panic: checked that instrs.last() was Some
instrs.pop().unwrap(); instrs.pop().unwrap();
n -= 1; n -= 1;
continue; continue
} }
// no more optimizations possible // no more optimizations possible
break; break
} }
if n > 0 { if n > 0 {
self.emit(I::Drop(Arg24::from_usize(n))); self.emit(I::Drop(Arg24::from_usize(n)));
@ -170,164 +220,181 @@ impl<'a> Compiler<'a> {
self.chunk.instrs[n] = new; self.chunk.instrs[n] = new;
} }
fn begin_scope(&mut self) { #[must_use]
self.scope += 1; fn begin_scope(&mut self) -> usize {
self.shadowed.len()
} }
fn end_scope(&mut self) { fn end_scope(&mut self, scope: usize) {
self.scope -= 1; let mut locals = 0;
while self.shadowed.len() > scope {
let (name, var) = self.shadowed.pop().expect("scope bad");
// no need to clean up at bottom scope if let Some(var) = var {
if self.scope == 0 { return; } if var.kind != VarKind::Global {
locals += 1;
for i in (0..self.globals.len()).rev() {
if self.globals[i].scope <= self.scope {
break;
} }
self.globals.pop(); self.scope.insert(name, var);
} } else {
self.scope.remove(&name);
let mut count = 0;
for i in (0..self.locals.len()).rev() {
if self.locals[i].scope <= self.scope {
break;
}
self.locals.pop();
count += 1;
}
if count > 0 && self.scope > 0 {
self.emit(I::DropLocal(Arg24::from_usize(count)));
} }
} }
if locals > 0 {
self.emit(I::DropLocal(Arg24::from_usize(locals)));
self.local_count -= locals;
}
}
// //
// variables // variables
// //
fn resolve_local(&mut self, name: &LStr) -> Option<usize> { fn resolve_name(&self, name: Symbol) -> ResolveOutcome {
self.locals.iter().rev() if let Some(v) = self.scope.get(&name) {
.position(|v| v.name.as_ref() == name) return ResolveOutcome::Var(v.kind)
.map(|x| self.locals.len() - x - 1) }
let Some(parent) = self.parent else {
return ResolveOutcome::None
};
if let ResolveOutcome::None = parent.resolve_name(name) {
return ResolveOutcome::None
}
ResolveOutcome::InParent
} }
fn resolve_global(&mut self, name: &LStr) -> Option<usize> { fn load_var(&mut self, name: Symbol) {
self.globals.iter().rev() match self.resolve_name(name) {
.position(|v| v.name.as_ref() == name) ResolveOutcome::Var(VarKind::Local(n)) => {
.map(|x| self.globals.len() - x - 1)
}
fn load_var(&mut self, name: &LStr) {
match (self.resolve_local(name), self.resolve_global(name)) {
(Some(n), None) => {
self.emit(I::LoadLocal(Arg24::from_usize(n))); self.emit(I::LoadLocal(Arg24::from_usize(n)));
}, }
(Some(n), Some(m)) if n >= m => { ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::LoadLocal(Arg24::from_usize(n))); self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
}, }
_ => { ResolveOutcome::InParent => {
let sym = Symbol::get(name); let n = match self.closes.get(&name) {
self.emit(I::LoadGlobal(Arg24::from_symbol(sym))); Some(n) => *n,
None => {
let n = self.closes.len();
self.closes.insert(name, n);
n
}
};
self.emit(I::LoadUpvalue(Arg24::from_usize(n)));
}
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
self.emit(I::LoadGlobal(Arg24::from_symbol(name)));
} }
} }
} }
fn declare_local(&mut self, name: &LStr) -> usize { fn declare_local(&mut self, name: Symbol) -> usize {
if let Some(i) = self.resolve_local(name) { let local = Var {
if self.locals[i].scope == self.scope { name,
self.emit(I::StoreLocal(Arg24::from_usize(i))); kind: VarKind::Local(self.local_count),
return i; };
} self.local_count += 1;
let shadowed = self.scope.insert(name, local);
self.shadowed.push((name, shadowed));
self.local_count - 1
} }
self.locals.push(Local { fn assign_local(&mut self, name: Symbol) -> usize {
name: name.into(), let n = self.declare_local(name);
scope: self.scope,
});
let i = self.locals.len() - 1;
self.emit(I::NewLocal); self.emit(I::NewLocal);
i n
} }
fn store_local(&mut self, i: usize) { fn assign_global(&mut self, name: Symbol) {
self.emit(I::StoreLocal(Arg24::from_usize(i))); self.declare_global(name);
self.store_var(name);
} }
fn store_global(&mut self, name: &LStr) { fn declare_global(&mut self, name: Symbol) {
let sym = Symbol::get(name); let global = Var {
self.emit(I::StoreGlobal(Arg24::from_symbol(sym))); name,
if let Some(i) = self.resolve_global(name) { kind: VarKind::Global,
if self.globals[i].scope == self.scope { };
return let shadowed = self.scope.insert(name, global);
} self.shadowed.push((name, shadowed));
}
self.globals.push(Local {
name: name.into(),
scope: self.scope,
});
} }
fn store_default(&mut self, name: &LStr) { fn store_var(&mut self, name: Symbol) {
match (self.resolve_local(name), self.resolve_global(name)) { match self.resolve_name(name) {
(Some(n), None) => self.store_local(n), ResolveOutcome::Var(VarKind::Local(n)) => {
(Some(n), Some(m)) if n >= m => self.store_local(n), self.emit(I::StoreLocal(Arg24::from_usize(n)));
(_, Some(_)) => self.store_global(name), }
(None, None) => { ResolveOutcome::Var(VarKind::Closed(n)) => {
if self.mode == CompilerMode::Repl && self.scope == 1 { self.emit(I::StoreClosedLocal(Arg24::from_usize(n)));
self.store_global(name); }
} else { ResolveOutcome::InParent => {
self.declare_local(name); let n = match self.closes.get(&name) {
Some(n) => *n,
None => {
let n = self.closes.len();
self.closes.insert(name, n);
n
}
};
self.emit(I::StoreUpvalue(Arg24::from_usize(n)));
}
ResolveOutcome::Var(VarKind::Global) => {
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
}
ResolveOutcome::None if self.mode == CompilerMode::Repl => {
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
}
ResolveOutcome::None => {
self.assign_local(name);
} }
} }
} }
}
// //
// Expressions // Expressions
// //
fn expr(&mut self, e: &Expr) { fn expr(&mut self, e: &Expr) {
match e { let Expr { kind, .. } = e;
Expr::Block(xs) if xs.is_empty() => { self.emit(I::Nil); }, match kind {
Expr::Block(xs) => { ExprKind::Block(xs) if xs.is_empty() => {
self.begin_scope(); self.emit(I::Nil);
}
ExprKind::Block(xs) => {
let scope = self.begin_scope();
for x in &xs[0..xs.len() - 1] { for x in &xs[0..xs.len() - 1] {
self.expr(x); self.expr(x);
self.emit_discard(1); self.emit_discard(1);
} }
self.expr(&xs[xs.len() - 1]); self.expr(&xs[xs.len() - 1]);
self.end_scope(); self.end_scope(scope);
}, }
Expr::Literal(v) => self.expr_literal(v), ExprKind::Literal(v) => self.expr_literal(v),
Expr::Ident(ident) => self.load_var(ident), ExprKind::Ident(ident) => self.load_var(*ident),
Expr::UnaryOp(o, a) => { ExprKind::UnaryOp(o, a) => {
self.expr(a); self.expr(a);
self.emit(I::UnaryOp(*o)); self.emit(I::UnaryOp(*o));
}, }
Expr::BinaryOp(o, a, b) => { ExprKind::BinaryOp(o, a, b) => {
self.expr(a); self.expr(a);
self.expr(b); self.expr(b);
self.emit(I::BinaryOp(*o)); self.emit(I::BinaryOp(*o));
}, }
Expr::Assign(o, lv, a) => self.expr_assign(*o, lv, a), ExprKind::Assign(o, lv, a) => self.expr_assign(*o, lv, a),
Expr::AssignVar(name, a) => { ExprKind::AssignVar(name, a) => {
self.expr(a); self.expr(a);
self.emit(I::Dup); self.emit(I::Dup);
self.declare_local(name); self.assign_local(*name);
}, }
Expr::AssignGlobal(name, a) => { ExprKind::AssignGlobal(name, a) => {
self.expr(a); self.expr(a);
self.emit(I::Dup); self.emit(I::Dup);
self.store_global(name); self.assign_global(*name);
}, }
Expr::List(xs) if xs.is_empty() => { ExprKind::List(xs) if xs.is_empty() => {
self.emit(I::NewList(0)); self.emit(I::NewList(0));
}, }
Expr::List(xs) => { ExprKind::List(xs) => {
let mut first = true; let mut first = true;
for chunk in xs.chunks(16) { for chunk in xs.chunks(16) {
for e in chunk { for e in chunk {
@ -340,11 +407,11 @@ impl<'a> Compiler<'a> {
self.emit(I::GrowList(chunk.len() as u8)); self.emit(I::GrowList(chunk.len() as u8));
} }
} }
}, }
Expr::Table(xs) if xs.is_empty() => { ExprKind::Table(xs) if xs.is_empty() => {
self.emit(I::NewTable(0)); self.emit(I::NewTable(0));
}, }
Expr::Table(xs) => { ExprKind::Table(xs) => {
let mut first = true; let mut first = true;
for chunk in xs.chunks(8) { for chunk in xs.chunks(8) {
for (k, v) in chunk { for (k, v) in chunk {
@ -358,20 +425,20 @@ impl<'a> Compiler<'a> {
self.emit(I::GrowTable(chunk.len() as u8)); self.emit(I::GrowTable(chunk.len() as u8));
} }
} }
}, }
Expr::Index(ct, idx) => { ExprKind::Index(ct, idx) => {
self.expr(ct); self.expr(ct);
self.expr(idx); self.expr(idx);
self.emit(I::Index); self.emit(I::Index);
}, }
Expr::FnCall(f, args) => { ExprKind::FnCall(f, args) => {
self.expr(f); self.expr(f);
for a in args { for a in args {
self.expr(a); self.expr(a);
} }
self.emit(I::Call(args.len() as u8)); self.emit(I::Call(args.len() as u8));
}, }
Expr::AssocFnCall(o, f, args) => { ExprKind::AssocFnCall(o, f, args) => {
self.expr(o); self.expr(o);
self.emit(I::Dup); self.emit(I::Dup);
self.emit(I::Symbol(Arg24::from_symbol(*f))); self.emit(I::Symbol(Arg24::from_symbol(*f)));
@ -381,35 +448,36 @@ impl<'a> Compiler<'a> {
self.expr(a); self.expr(a);
} }
self.emit(I::Call((args.len() + 1) as u8)); self.emit(I::Call((args.len() + 1) as u8));
}, }
Expr::Return(e) => { ExprKind::Return(e) => {
self.expr(e); self.expr(e);
self.emit(I::Return); self.emit(I::Return);
}, }
Expr::Pipe(a, f) => { ExprKind::Pipe(a, f) => {
self.expr(a); self.expr(a);
self.expr(f); self.expr(f);
self.emit(I::Swap); self.emit(I::Swap);
self.emit(I::Call(1)); self.emit(I::Call(1));
}, }
Expr::Lambda(args, body) => self.expr_lambda(args, body), ExprKind::Lambda(args, body) => self.expr_fndef(None, args, body),
Expr::And(a, b) => { ExprKind::FnDef(name, args, body) => self.expr_fndef(*name, args, body),
ExprKind::And(a, b) => {
self.expr(a); self.expr(a);
self.emit(I::Dup); self.emit(I::Dup);
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0))); let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
self.emit_discard(1); self.emit_discard(1);
self.expr(b); self.expr(b);
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip()))); self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
}, }
Expr::Or(a, b) => { ExprKind::Or(a, b) => {
self.expr(a); self.expr(a);
self.emit(I::Dup); self.emit(I::Dup);
let j1 = self.emit(I::JumpTrue(Arg24::from_usize(0))); let j1 = self.emit(I::JumpTrue(Arg24::from_usize(0)));
self.emit_discard(1); self.emit_discard(1);
self.expr(b); self.expr(b);
self.update_instr(j1, I::JumpTrue(Arg24::from_usize(self.ip()))); self.update_instr(j1, I::JumpTrue(Arg24::from_usize(self.ip())));
}, }
Expr::If(cond, b1, b2) => { ExprKind::If(cond, b1, b2) => {
self.expr(cond); self.expr(cond);
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0))); let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
self.expr(b1); self.expr(b1);
@ -420,11 +488,9 @@ impl<'a> Compiler<'a> {
} else { } else {
self.emit(I::Nil); self.emit(I::Nil);
} }
self.update_instr(j2, self.update_instr(j2, I::Jump(Arg24::from_usize(self.ip())));
I::Jump(Arg24::from_usize(self.ip())) }
); ExprKind::While(cond, body) => {
},
Expr::While(cond, body) => {
let start = self.ip(); let start = self.ip();
self.expr(cond); self.expr(cond);
@ -435,14 +501,14 @@ impl<'a> Compiler<'a> {
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip()))); self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
self.emit(I::Nil); self.emit(I::Nil);
}, }
Expr::For(name, iter, body) => self.expr_for(name, iter, body), ExprKind::For(name, iter, body) => self.expr_for(*name, iter, body),
Expr::Try(body, catches) => self.expr_try(body, catches), ExprKind::Try(body, catches) => self.expr_try(body, catches),
} }
} }
fn expr_try(&mut self, body: &Expr, catch_blocks: &[CatchBlock]) { fn expr_try(&mut self, body: &Expr, catch_blocks: &[CatchBlock]) {
let (idx, mut table) = self.chunk.begin_try_table(self.locals.len()); let (idx, mut table) = self.chunk.begin_try_table(self.local_count);
self.emit(I::BeginTry(Arg24::from_usize(idx))); self.emit(I::BeginTry(Arg24::from_usize(idx)));
self.expr(body); self.expr(body);
@ -456,16 +522,16 @@ impl<'a> Compiler<'a> {
types: catch_block.types.clone(), types: catch_block.types.clone(),
}); });
self.begin_scope(); let scope = self.begin_scope();
if let Some(name) = catch_block.name { if let Some(name) = catch_block.name {
self.declare_local(name); self.assign_local(name);
} else { } else {
self.emit_discard(1); self.emit_discard(1);
} }
self.expr(&catch_block.body); self.expr(&catch_block.body);
self.end_scope(); self.end_scope(scope);
let end_addr = self.emit(I::Jump(Arg24::from_usize(0))); let end_addr = self.emit(I::Jump(Arg24::from_usize(0)));
catch_end_addrs.push(end_addr); catch_end_addrs.push(end_addr);
@ -480,15 +546,15 @@ impl<'a> Compiler<'a> {
self.chunk.finish_catch_table(idx, table); self.chunk.finish_catch_table(idx, table);
} }
fn expr_for(&mut self, name: &LStr, iter: &Expr, body: &Expr) { fn expr_for(&mut self, name: Symbol, iter: &Expr, body: &Expr) {
// load iterable and convert to iterator // load iterable and convert to iterator
self.expr(iter); self.expr(iter);
self.emit(I::IterBegin); self.emit(I::IterBegin);
// declare loop variable // declare loop variable
self.begin_scope(); let scope = self.begin_scope();
self.emit(I::Nil); self.emit(I::Nil);
let local = self.declare_local(name); self.assign_local(name);
// begin loop // begin loop
let start = self.ip(); let start = self.ip();
@ -497,7 +563,7 @@ impl<'a> Compiler<'a> {
self.emit(I::Dup); self.emit(I::Dup);
self.emit(I::Call(0)); self.emit(I::Call(0));
let j1 = self.emit(I::IterTest(Arg24::from_usize(0))); let j1 = self.emit(I::IterTest(Arg24::from_usize(0)));
self.store_local(local); self.store_var(name);
// body // body
self.expr(body); self.expr(body);
@ -507,57 +573,96 @@ impl<'a> Compiler<'a> {
self.emit(I::Jump(Arg24::from_usize(start))); self.emit(I::Jump(Arg24::from_usize(start)));
self.update_instr(j1, I::IterTest(Arg24::from_usize(self.ip()))); self.update_instr(j1, I::IterTest(Arg24::from_usize(self.ip())));
self.end_scope(); self.end_scope(scope);
self.emit(I::Nil); self.emit(I::Nil);
} }
fn expr_lambda(&mut self, args: &[&LStr], body: &Expr) { fn expr_fndef(&mut self, name: Option<Symbol>, args: &[Symbol], body: &Expr) {
let mut inner = self.new_function(args); let mut inner = self.new_function(name, args);
inner.parent = Some(self); inner.parent = Some(self);
inner.expr(body); inner.expr(body);
let func = inner.finish();
let n = self.add_const(func.into()); let (func, closes) = inner.finish_inner();
self.emit(I::Const(Arg24::from_usize(n))); let func_const = self.add_const(func.into());
let num_closed = closes.len();
for (name, _) in closes {
match self.resolve_name(name) {
ResolveOutcome::Var(VarKind::Local(n)) => {
self.emit(I::CloseOver(Arg24::from_usize(n)));
self.scope.entry(name).and_modify(|v| {
v.kind = VarKind::Closed(n);
});
}
ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
}
ResolveOutcome::InParent => {
let n = self.closes.len();
self.closes.insert(name, n);
self.emit(I::ContinueUpvalue(Arg24::from_usize(n)));
}
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
panic!("upvalue resolved to none or global")
}
}
}
if num_closed == 0 {
self.emit(I::Const(Arg24::from_usize(func_const)));
} else {
self.emit(I::Closure(Arg24::from_usize(func_const)));
}
if let Some(name) = name {
self.emit(I::Dup);
self.store_var(name);
}
} }
fn expr_literal(&mut self, val: &Value) { fn expr_literal(&mut self, val: &Value) {
match val { match val {
Value::Nil Value::Nil => {
=> { self.emit(I::Nil); }, self.emit(I::Nil);
Value::Bool(b) }
=> { self.emit(I::Bool(*b)); }, Value::Bool(b) => {
Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i) self.emit(I::Bool(*b));
=> { self.emit(I::Int(Arg24::from_i64(*i))); }, }
Value::Symbol(s) Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i) => {
=> { self.emit(I::Symbol(Arg24::from_symbol(*s))); }, self.emit(I::Int(Arg24::from_i64(*i)));
}
Value::Symbol(s) => {
self.emit(I::Symbol(Arg24::from_symbol(*s)));
}
_ => { _ => {
let n = self.add_const(val.clone()); let n = self.add_const(val.clone());
self.emit(I::Const(Arg24::from_usize(n))); self.emit(I::Const(Arg24::from_usize(n)));
}, }
} }
} }
fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) { fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) {
match (lv, o) { match (&lv.kind, o) {
(LValue::Ident(i), None) => { (LValueKind::Ident(i), None) => {
self.expr(a); self.expr(a);
self.emit(I::Dup); self.emit(I::Dup);
self.store_default(i); self.store_var(*i);
}, }
(LValue::Ident(i), Some(o)) => { (LValueKind::Ident(i), Some(o)) => {
self.load_var(i); self.load_var(*i);
self.expr(a); self.expr(a);
self.emit(I::BinaryOp(o)); self.emit(I::BinaryOp(o));
self.emit(I::Dup); self.emit(I::Dup);
self.store_default(i); self.store_var(*i);
}, }
(LValue::Index(ct, i), None) => { (LValueKind::Index(ct, i), None) => {
self.expr(ct); self.expr(ct);
self.expr(i); self.expr(i);
self.expr(a); self.expr(a);
self.emit(I::StoreIndex); self.emit(I::StoreIndex);
}, }
(LValue::Index(ct, i), Some(o)) => { (LValueKind::Index(ct, i), Some(o)) => {
self.expr(ct); self.expr(ct);
self.expr(i); self.expr(i);
self.emit(I::DupTwo); self.emit(I::DupTwo);
@ -565,8 +670,7 @@ impl<'a> Compiler<'a> {
self.expr(a); self.expr(a);
self.emit(I::BinaryOp(o)); self.emit(I::BinaryOp(o));
self.emit(I::StoreIndex); self.emit(I::StoreIndex);
},
} }
} }
}
} }

View file

@ -1,5 +1,9 @@
use std::{rc::Rc, collections::HashMap, cell::RefCell, fmt::Display}; use crate::{
use crate::{lstring::LStr, symbol::{Symbol, SYM_DATA, SYM_MSG, SYM_TYPE}, value::{HashValue, Value}}; lstring::LStr,
symbol::{Symbol, SYM_DATA, SYM_MSG, SYM_TYPE},
value::{HashValue, Value},
};
use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc};
pub type Result<T> = std::result::Result<T, Exception>; pub type Result<T> = std::result::Result<T, Exception>;
@ -12,19 +16,35 @@ pub struct Exception {
impl Exception { impl Exception {
pub fn new(ty: Symbol) -> Self { pub fn new(ty: Symbol) -> Self {
Self { ty, msg: None, data: None } Self {
ty,
msg: None,
data: None,
}
} }
pub fn new_with_msg(ty: Symbol, msg: Rc<LStr>) -> Self { pub fn new_with_msg(ty: Symbol, msg: Rc<LStr>) -> Self {
Self { ty, msg: Some(msg), data: None } Self {
ty,
msg: Some(msg),
data: None,
}
} }
pub fn new_with_data(ty: Symbol, data: Value) -> Self { pub fn new_with_data(ty: Symbol, data: Value) -> Self {
Self { ty, msg: None, data: Some(data) } Self {
ty,
msg: None,
data: Some(data),
}
} }
pub fn new_with_msg_data(ty: Symbol, msg: Rc<LStr>, data: Value) -> Self { pub fn new_with_msg_data(ty: Symbol, msg: Rc<LStr>, data: Value) -> Self {
Self { ty, msg: Some(msg), data: Some(data) } Self {
ty,
msg: Some(msg),
data: Some(data),
}
} }
pub fn from_table(table: &Rc<RefCell<HashMap<HashValue, Value>>>) -> Option<Self> { pub fn from_table(table: &Rc<RefCell<HashMap<HashValue, Value>>>) -> Option<Self> {
@ -76,11 +96,11 @@ impl Display for Exception {
#[macro_export] #[macro_export]
macro_rules! exception { macro_rules! exception {
($exc_ty:expr, $fstr:literal, $($arg:expr),*) => { ($exc_ty:expr, $($t:tt)*) => {
$crate::exception::Exception::new_with_msg( $crate::exception::Exception::new_with_msg(
$exc_ty, $exc_ty,
$crate::lstring::LString::from( $crate::lstring::LString::from(
format!($fstr, $($arg),*) format!($($t)*)
).into() ).into()
) )
}; };
@ -102,6 +122,3 @@ macro_rules! throw {
} }
pub use throw; pub use throw;

View file

@ -1,50 +1,14 @@
#![allow(clippy::mutable_key_type)]
#![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::allow_attributes)]
#[rustfmt::skip]
#[allow(clippy::extra_unused_lifetimes)]
#[allow(clippy::needless_lifetimes)]
#[allow(clippy::let_unit_value)]
#[allow(clippy::just_underscores_and_digits)]
#[allow(clippy::pedantic)]
mod parser {
pub use __intern_token::new_builder as new_builder;
include!(concat!(env!("OUT_DIR"),"/parser.rs"));
}
mod parser_util;
mod vm;
pub mod symbol;
pub mod ast;
pub mod value;
pub mod exception;
pub mod chunk; pub mod chunk;
pub mod compiler; pub mod compiler;
pub mod exception;
pub mod lstring; pub mod lstring;
pub mod parser;
pub mod symbol;
pub mod value;
pub use parser::BlockParser as Parser; mod vm;
pub use vm::Vm; 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>
>;
use lalrpop_util::{ParseError, lexer::{Token, MatcherBuilder}};
pub struct Lexer {
builder: MatcherBuilder,
}
impl Default for Lexer {
fn default() -> Self { Self::new() }
}
impl Lexer {
pub fn new() -> Self {
Self { builder: crate::parser::new_builder() }
}
pub fn lex<'s>(&'s self, input: &'s str) -> impl Iterator<Item=LexResult<'s>> {
self.builder.matcher::<parser_util::ParseError>(input)
}
}

View file

@ -1,4 +1,15 @@
use std::{borrow::{Borrow, BorrowMut, Cow}, ffi::OsStr, fmt::{self, Write}, io, iter::{Copied, FusedIterator}, ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut}, rc::Rc, slice, str::Utf8Error, string::FromUtf8Error}; use std::{
borrow::{Borrow, BorrowMut, Cow},
ffi::{OsStr, OsString},
fmt::{self, Write},
io,
iter::{Copied, FusedIterator},
ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut},
rc::Rc,
slice,
str::Utf8Error,
string::FromUtf8Error,
};
use unicode_ident::{is_xid_continue, is_xid_start}; use unicode_ident::{is_xid_continue, is_xid_start};
@ -28,7 +39,9 @@ fn is_continue(b: u8) -> bool {
#[inline] #[inline]
fn calc_continue(mut ch: u32, bytes: &[u8]) -> Option<char> { fn calc_continue(mut ch: u32, bytes: &[u8]) -> Option<char> {
for b in bytes { for b in bytes {
if !is_continue(*b) { return None } if !is_continue(*b) {
return None
}
ch = (ch << 6) | (b & 0x3f) as u32; ch = (ch << 6) | (b & 0x3f) as u32;
} }
char::from_u32(ch) char::from_u32(ch)
@ -40,23 +53,29 @@ fn next_codepoint(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
match init { match init {
0..=0x7f => return Some((&bytes[1..], Ok(init as char))), 0..=0x7f => return Some((&bytes[1..], Ok(init as char))),
0xc0..=0xdf => 'case: { 0xc0..=0xdf => 'case: {
if bytes.len() < 2 { break 'case } if bytes.len() < 2 {
break 'case
}
let Some(ch) = calc_continue(init as u32 & 0x1f, &bytes[1..2]) else { let Some(ch) = calc_continue(init as u32 & 0x1f, &bytes[1..2]) else {
break 'case; break 'case
}; };
return Some((&bytes[2..], Ok(ch))) return Some((&bytes[2..], Ok(ch)))
}, }
0xe0..=0xef => 'case: { 0xe0..=0xef => 'case: {
if bytes.len() < 3 { break 'case } if bytes.len() < 3 {
break 'case
}
let Some(ch) = calc_continue(init as u32 & 0x0f, &bytes[1..3]) else { let Some(ch) = calc_continue(init as u32 & 0x0f, &bytes[1..3]) else {
break 'case; break 'case
}; };
return Some((&bytes[3..], Ok(ch))) return Some((&bytes[3..], Ok(ch)))
}, }
0xf0..=0xf7 => 'case: { 0xf0..=0xf7 => 'case: {
if bytes.len() < 4 { break 'case } if bytes.len() < 4 {
break 'case
}
let Some(ch) = calc_continue(init as u32 & 0x07, &bytes[1..4]) else { let Some(ch) = calc_continue(init as u32 & 0x07, &bytes[1..4]) else {
break 'case; break 'case
}; };
return Some((&bytes[4..], Ok(ch))) return Some((&bytes[4..], Ok(ch)))
} }
@ -69,7 +88,9 @@ fn next_codepoint(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
#[inline] #[inline]
fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> { fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
let len = bytes.len(); let len = bytes.len();
if len < 1 { return None } if len < 1 {
return None
}
let last = bytes[len - 1]; let last = bytes[len - 1];
if (0..=0x7f).contains(&last) { if (0..=0x7f).contains(&last) {
@ -77,9 +98,13 @@ fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
} }
'case: { 'case: {
if !is_continue(last) { break 'case } if !is_continue(last) {
break 'case
}
if len < 2 { break 'case } if len < 2 {
break 'case
}
let b1 = bytes[len - 2]; let b1 = bytes[len - 2];
if 0xe0 & b1 == 0xc0 { if 0xe0 & b1 == 0xc0 {
if let Some(ch) = calc_continue(b1 as u32 & 0x1f, &[last]) { if let Some(ch) = calc_continue(b1 as u32 & 0x1f, &[last]) {
@ -89,7 +114,9 @@ fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
break 'case break 'case
} }
if len < 3 { break 'case } if len < 3 {
break 'case
}
let b2 = bytes[len - 3]; let b2 = bytes[len - 3];
if 0xf0 & b2 == 0xe0 { if 0xf0 & b2 == 0xe0 {
if let Some(ch) = calc_continue(b2 as u32 & 0x0f, &[b1, last]) { if let Some(ch) = calc_continue(b2 as u32 & 0x0f, &[b1, last]) {
@ -99,7 +126,9 @@ fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
break 'case break 'case
} }
if len < 4 { break 'case } if len < 4 {
break 'case
}
let b3 = bytes[len - 4]; let b3 = bytes[len - 4];
if 0xf8 & b3 == 0xf0 { if 0xf8 & b3 == 0xf0 {
if let Some(ch) = calc_continue(b3 as u32 & 0x07, &[b2, b1, last]) { if let Some(ch) = calc_continue(b3 as u32 & 0x07, &[b2, b1, last]) {
@ -230,32 +259,69 @@ impl BorrowMut<LStr> for LString {
impl From<LString> for Vec<u8> { impl From<LString> for Vec<u8> {
#[inline] #[inline]
fn from(value: LString) -> Self { value.inner } fn from(value: LString) -> Self {
value.inner
}
} }
impl From<Vec<u8>> for LString { impl From<Vec<u8>> for LString {
#[inline] #[inline]
fn from(value: Vec<u8>) -> Self { Self { inner: value } } fn from(value: Vec<u8>) -> Self {
Self { inner: value }
}
} }
impl From<String> for LString { impl From<String> for LString {
#[inline] #[inline]
fn from(value: String) -> Self { Self { inner: value.into_bytes() } } fn from(value: String) -> Self {
Self {
inner: value.into_bytes(),
}
}
}
impl From<OsString> for LString {
#[inline]
fn from(value: OsString) -> Self {
Self {
inner: value.into_encoded_bytes(),
}
}
} }
impl From<&LStr> for LString { impl From<&LStr> for LString {
#[inline] #[inline]
fn from(value: &LStr) -> Self { value.to_owned() } fn from(value: &LStr) -> Self {
value.to_owned()
}
} }
impl From<&str> for LString { impl From<&str> for LString {
#[inline] #[inline]
fn from(value: &str) -> Self { value.to_owned().into() } fn from(value: &str) -> Self {
value.to_owned().into()
}
} }
impl From<&[u8]> for LString { impl From<&[u8]> for LString {
#[inline] #[inline]
fn from(value: &[u8]) -> Self { value.to_owned().into() } fn from(value: &[u8]) -> Self {
value.to_owned().into()
}
}
impl<const N: usize> From<&[u8; N]> for LString {
#[inline]
fn from(value: &[u8; N]) -> Self {
value.as_slice().into()
}
}
impl<const N: usize> From<[u8; N]> for LString {
#[inline]
fn from(value: [u8; N]) -> Self {
value.as_slice().into()
}
} }
impl From<Cow<'_, LStr>> for LString { impl From<Cow<'_, LStr>> for LString {
@ -263,7 +329,7 @@ impl From<Cow<'_, LStr>> for LString {
fn from(value: Cow<'_, LStr>) -> Self { fn from(value: Cow<'_, LStr>) -> Self {
match value { match value {
Cow::Borrowed(b) => b.to_owned(), Cow::Borrowed(b) => b.to_owned(),
Cow::Owned(o) => o Cow::Owned(o) => o,
} }
} }
} }
@ -282,9 +348,18 @@ impl From<Cow<'_, [u8]>> for LString {
} }
} }
impl<const N: usize> From<Cow<'_, [u8; N]>> for LString {
#[inline]
fn from(value: Cow<'_, [u8; N]>) -> Self {
value.into_owned().into()
}
}
impl<'a> From<&'a LStr> for &'a [u8] { impl<'a> From<&'a LStr> for &'a [u8] {
#[inline] #[inline]
fn from(value: &'a LStr) -> Self { &value.inner } fn from(value: &'a LStr) -> Self {
&value.inner
}
} }
impl<'a> From<&'a [u8]> for &'a LStr { impl<'a> From<&'a [u8]> for &'a LStr {
@ -294,6 +369,13 @@ impl<'a> From<&'a [u8]> for &'a LStr {
} }
} }
impl<'a, const N: usize> From<&'a [u8; N]> for &'a LStr {
#[inline]
fn from(value: &'a [u8; N]) -> Self {
LStr::from_bytes(value.as_slice())
}
}
impl<'a> From<&'a str> for &'a LStr { impl<'a> From<&'a str> for &'a LStr {
#[inline] #[inline]
fn from(value: &'a str) -> Self { fn from(value: &'a str) -> Self {
@ -301,9 +383,18 @@ impl<'a> From<&'a str> for &'a LStr {
} }
} }
impl<'a> From<&'a OsStr> for &'a LStr {
#[inline]
fn from(value: &'a OsStr) -> Self {
LStr::from_os_str(value)
}
}
impl<'a> From<&'a mut LStr> for &'a mut [u8] { impl<'a> From<&'a mut LStr> for &'a mut [u8] {
#[inline] #[inline]
fn from(value: &'a mut LStr) -> Self { &mut value.inner } fn from(value: &'a mut LStr) -> Self {
&mut value.inner
}
} }
impl<'a> From<&'a mut [u8]> for &'a mut LStr { impl<'a> From<&'a mut [u8]> for &'a mut LStr {
@ -315,7 +406,9 @@ impl<'a> From<&'a mut [u8]> for &'a mut LStr {
impl<'a> From<&'a LString> for &'a LStr { impl<'a> From<&'a LString> for &'a LStr {
#[inline] #[inline]
fn from(value: &'a LString) -> Self { value } fn from(value: &'a LString) -> Self {
value
}
} }
impl From<&LStr> for Rc<LStr> { impl From<&LStr> for Rc<LStr> {
@ -432,7 +525,9 @@ impl io::Write for LString {
Ok(buf.len()) Ok(buf.len())
} }
fn flush(&mut self) -> io::Result<()> { Ok(()) } fn flush(&mut self) -> io::Result<()> {
Ok(())
}
} }
//impl fmt::Write for LString { //impl fmt::Write for LString {
@ -511,15 +606,25 @@ impl<'a> Iterator for Bytes<'a> {
type Item = u8; type Item = u8;
#[inline] #[inline]
fn next(&mut self) -> Option<Self::Item> { self.0.next() } fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
#[inline] #[inline]
fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() } fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
#[inline] #[inline]
fn count(self) -> usize { self.0.count() } fn count(self) -> usize {
self.0.count()
}
#[inline] #[inline]
fn last(self) -> Option<Self::Item> { self.0.last() } fn last(self) -> Option<Self::Item> {
self.0.last()
}
#[inline] #[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> { self.0.nth(n) } fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.0.nth(n)
}
#[inline] #[inline]
fn all<F: FnMut(Self::Item) -> bool>(&mut self, f: F) -> bool { fn all<F: FnMut(Self::Item) -> bool>(&mut self, f: F) -> bool {
self.0.all(f) self.0.all(f)
@ -540,7 +645,9 @@ impl<'a> Iterator for Bytes<'a> {
impl<'a> ExactSizeIterator for Bytes<'a> { impl<'a> ExactSizeIterator for Bytes<'a> {
#[inline] #[inline]
fn len(&self) -> usize { self.0.len() } fn len(&self) -> usize {
self.0.len()
}
} }
impl<'a> FusedIterator for Bytes<'a> {} impl<'a> FusedIterator for Bytes<'a> {}
@ -573,6 +680,42 @@ impl<'a> DoubleEndedIterator for LosslessChars<'a> {
} }
} }
#[derive(Clone)]
pub struct LosslessCharsIndices<'a>(&'a [u8], usize);
impl<'a> Iterator for LosslessCharsIndices<'a> {
type Item = (usize, Result<char, u8>);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let (new_bytes, res) = next_codepoint(self.0)?;
let index = self.1;
self.0 = new_bytes;
match res {
Ok(c) => self.1 += c.len_utf8(),
Err(_) => self.1 += 1,
}
Some((index, res))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.0.len();
((len + 3) / 4, Some(len))
}
}
impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
let (new_bytes, res) = next_codepoint_back(self.0)?;
self.0 = new_bytes;
match res {
Ok(c) => self.1 -= c.len_utf8(),
Err(_) => self.1 -= 1,
}
Some((self.1, res))
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct Chars<'a>(LosslessChars<'a>); pub struct Chars<'a>(LosslessChars<'a>);
@ -605,6 +748,36 @@ impl<'a> DoubleEndedIterator for Chars<'a> {
} }
} }
#[derive(Clone)]
pub struct CharsIndices<'a>(LosslessCharsIndices<'a>);
impl<'a> Iterator for CharsIndices<'a> {
type Item = (usize, char);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
loop {
if let (index, Ok(c)) = self.0.next()? {
return Some((index, c))
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<'a> DoubleEndedIterator for CharsIndices<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
if let (index, Ok(c)) = self.0.next_back()? {
return Some((index, c))
}
}
}
}
impl LStr { impl LStr {
#[inline] #[inline]
@ -612,6 +785,11 @@ impl LStr {
Self::from_bytes(string.as_bytes()) Self::from_bytes(string.as_bytes())
} }
#[inline]
pub fn from_os_str(os_string: &OsStr) -> &Self {
Self::from_bytes(os_string.as_encoded_bytes())
}
#[inline] #[inline]
pub const fn from_bytes(bytes: &[u8]) -> &Self { pub const fn from_bytes(bytes: &[u8]) -> &Self {
unsafe { &*(bytes as *const [u8] as *const LStr) } unsafe { &*(bytes as *const [u8] as *const LStr) }
@ -623,19 +801,35 @@ impl LStr {
} }
#[inline] #[inline]
pub const fn as_bytes(&self) -> &[u8] { &self.inner } pub const fn as_bytes(&self) -> &[u8] {
&self.inner
}
#[inline] #[inline]
pub fn as_bytes_mut(&mut self) -> &mut [u8] { &mut self.inner } pub fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.inner
}
#[inline]
pub fn byte_at(&self, n: usize) -> u8 {
self.inner[n]
}
#[inline]
pub fn byte_get(&self, n: usize) -> Option<u8> {
self.inner.get(n).copied()
}
#[inline] #[inline]
pub fn bytes(&self) -> Bytes { pub fn bytes(&self) -> Bytes {
Bytes(self.as_bytes().iter().copied()) Bytes(self.as_bytes().iter().copied())
} }
#[inline] #[inline]
pub fn chars(&self) -> Chars { pub fn chars(&self) -> Chars {
Chars(self.chars_lossless()) Chars(self.chars_lossless())
} }
#[inline] #[inline]
pub fn chars_lossless(&self) -> LosslessChars { pub fn chars_lossless(&self) -> LosslessChars {
LosslessChars(self.as_bytes()) LosslessChars(self.as_bytes())
@ -671,11 +865,13 @@ impl LStr {
#[inline] #[inline]
pub fn to_os_str(&self) -> Cow<OsStr> { pub fn to_os_str(&self) -> Cow<OsStr> {
#[cfg(unix)] { #[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
Cow::Borrowed(OsStr::from_bytes(self.as_bytes())) Cow::Borrowed(OsStr::from_bytes(self.as_bytes()))
} }
#[cfg(not(unix))] { #[cfg(not(unix))]
{
Cow::Owned(self.to_string().into()) Cow::Owned(self.to_string().into())
} }
} }
@ -746,10 +942,21 @@ impl LStr {
self.as_bytes().ends_with(s.as_bytes()) self.as_bytes().ends_with(s.as_bytes())
} }
pub fn strip_prefix(&self, prefix: &LStr) -> Option<&LStr> {
self.as_bytes()
.strip_prefix(prefix.as_bytes())
.map(LStr::from_bytes)
}
pub fn strip_suffix(&self, suffix: &LStr) -> Option<&LStr> {
self.as_bytes()
.strip_suffix(suffix.as_bytes())
.map(LStr::from_bytes)
}
pub fn is_identifier(&self) -> bool { pub fn is_identifier(&self) -> bool {
let mut chars = self.chars_lossless(); let mut chars = self.chars_lossless();
let first = chars.next() let first = chars.next().is_some_and(|ch| ch.is_ok_and(is_xid_start));
.is_some_and(|ch| ch.is_ok_and(is_xid_start));
if !first { if !first {
return false return false
} }
@ -775,11 +982,15 @@ impl AddAssign<&LStr> for LString {
} }
impl Default for &LStr { impl Default for &LStr {
fn default() -> Self { [].as_ref().into() } fn default() -> Self {
[].as_ref().into()
}
} }
impl Default for &mut LStr { impl Default for &mut LStr {
fn default() -> Self { [].as_mut().into() } fn default() -> Self {
[].as_mut().into()
}
} }
macro_rules! impl_index { macro_rules! impl_index {
@ -821,4 +1032,3 @@ impl_index!(std::ops::RangeInclusive<usize>);
impl_index!(std::ops::RangeTo<usize>); impl_index!(std::ops::RangeTo<usize>);
impl_index!(std::ops::RangeToInclusive<usize>); impl_index!(std::ops::RangeToInclusive<usize>);
impl_index!((std::ops::Bound<usize>, std::ops::Bound<usize>)); impl_index!((std::ops::Bound<usize>, std::ops::Bound<usize>));

View file

@ -1,442 +0,0 @@
use std::rc::Rc;
use crate::ast::*;
use crate::value::Value;
use crate::symbol::Symbol;
use crate::parser_util::*;
use crate::lstring::LStr;
use crate::lstr;
use num_complex::Complex64;
grammar;
extern {
type Error = ParseError;
}
match {
// line separator (use \ to escape newlines)
r";|\n" => LineSeparator,
// whitespace
r"[ \t\r]*" => {},
r"\\\r?\n" => {},
r"--[^\n]*" => {},
// kw literals
"true",
"false",
"nil",
// kw variables
"global",
"var",
// kw logic
"and",
"or",
"not",
// kw control flow
"begin",
"end",
"if",
"then",
"elif",
"else",
"while",
"for",
"do",
"in",
"continue",
"break",
"try",
"catch",
"return",
} else {
// identifiers
r"[a-zA-Z_][a-zA-Z0-9_]*" => TokIdentifier,
// literals
r"0x[0-9A-Fa-f][0-9A-Fa-f_]*" => TokHexInteger,
r"[0-9][0-9_]*" => TokDecInteger,
r"0o[0-7][0-7]*" => TokOctInteger,
r"0s[0-5][0-5]*" => TokSexInteger,
r"0b[01][01_]*" => TokBinInteger,
r"[0-9][0-9_]*([eE][-+]?[0-9_]*[0-9][0-9_]*i?|i)|([0-9][0-9_]*)?\.[0-9_]+([eE]_*[-+]?[0-9_]*[0-9][0-9_]*)?i?" => TokFloat,
r#""([^\\"]|\\.)*""# => TokStringDouble,
r#"'[^']*'"# => TokStringSingle,
r#":[a-zA-Z_][a-zA-Z0-9_]*"# => TokSymbolNone,
r#":'[^']*'"# => TokSymbolSingle,
r#":"([^\\"]|\\.)*""# => TokSymbolDouble,
} else {
// everything else
_
}
pub Block: Box<Expr<'input>> = {
LineSeparator* <xs:(<Expr> LineSeparator+)*> <x:Expr> LineSeparator* => {
let v = xs.into_iter().chain(std::iter::once(x)).map(|x| *x).collect();
return Box::new(Expr::Block(v));
},
LineSeparator* => Box::new(Expr::Block(Vec::new())),
}
Expr: Box<Expr<'input>> = {
"return" <e:Expr> => Box::new(Expr::Return(e)),
Assign,
}
//
// assignment
//
Assign: Box<Expr<'input>> = {
<l:LValue> <o:AssignOp> <r:Assign> => Box::new(Expr::Assign(o, l, r)),
"var" <i:Identifier> "=" <r:Assign> => Box::new(Expr::AssignVar(i, r)),
"global" <i:Identifier> "=" <r:Assign> => Box::new(Expr::AssignGlobal(i, r)),
Or,
}
LValue: Box<LValue<'input>> = {
<Identifier> => Box::new(LValue::Ident(<>)),
<l:BinaryIndex> "!" <r:CallOrAccess> => Box::new(LValue::Index(l, r)),
<l:CallOrAccess> "." <r:Identifier> => Box::new(LValue::Index(l, Box::new(Expr::Literal(Value::Symbol(Symbol::get(r)))))),
}
AssignOp: Option<BinaryOp> = {
"=" => None,
"+=" => Some(BinaryOp::Add),
"-=" => Some(BinaryOp::Sub),
"*=" => Some(BinaryOp::Mul),
"/=" => Some(BinaryOp::Div),
"%=" => Some(BinaryOp::Mod),
"^=" => Some(BinaryOp::Pow),
"++=" => Some(BinaryOp::Concat),
"&=" => Some(BinaryOp::Append),
}
//
// logical ops
//
// or
Or: Box<Expr<'input>> = {
<l:Or> "or" <r:And> => Box::new(Expr::Or(l, r)),
And,
}
// and
And: Box<Expr<'input>> = {
<l:And> "and" <r:UnaryNot> => Box::new(Expr::And(l, r)),
UnaryNot,
}
// not
UnaryNot: Box<Expr<'input>> = {
"not" <r:Pipe> => Box::new(Expr::UnaryOp(UnaryOp::Not, r)),
Pipe,
}
//
// pipe
//
Pipe: Box<Expr<'input>> = {
<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![lstr!("$")], e)),
"::" <e:BinaryCompare> => Box::new(Expr::Lambda(vec![lstr!("$"), lstr!("$$")], e)),
BinaryCompare,
}
//
// operations
//
// == != > < >= <=
BinaryCompare: Box<Expr<'input>> = {
<l:BinaryConcat> <o:CompareOp> <r:BinaryConcat> => Box::new(Expr::BinaryOp(o, l, r)),
BinaryConcat,
}
CompareOp: BinaryOp = {
"==" => BinaryOp::Eq,
"!=" => BinaryOp::Ne,
">" => BinaryOp::Gt,
"<" => BinaryOp::Lt,
">=" => BinaryOp::Ge,
"<=" => BinaryOp::Le,
}
// concat (++)
BinaryConcat: Box<Expr<'input>> = {
<l:BinaryConcat> "++" <r:BinaryAppend> => Box::new(Expr::BinaryOp(BinaryOp::Concat, l, r)),
BinaryAppend,
}
// append ( & )
BinaryAppend: Box<Expr<'input>> = {
<l:BinaryAppend> "&" <r:BinaryRange> => Box::new(Expr::BinaryOp(BinaryOp::Append, l, r)),
BinaryRange,
}
// .. ..= ..*
BinaryRange: Box<Expr<'input>> = {
<l:BinaryBitOr> <o:RangeOp> <r:BinaryBitOr> => Box::new(Expr::BinaryOp(o, l, r)),
<l:BinaryBitOr> "..*" => Box::new(Expr::UnaryOp(UnaryOp::RangeEndless, l)),
BinaryBitOr,
}
RangeOp: BinaryOp = {
".." => BinaryOp::Range,
"..=" => BinaryOp::RangeIncl,
}
// #|
BinaryBitOr: Box<Expr<'input>> = {
<l:BinaryBitOr> "#|" <r:BinaryBitXor> => Box::new(Expr::BinaryOp(BinaryOp::BitOr, l, r)),
BinaryBitXor,
}
// #^
BinaryBitXor: Box<Expr<'input>> = {
<l:BinaryBitXor> "#^" <r:BinaryBitAnd> => Box::new(Expr::BinaryOp(BinaryOp::BitXor, l, r)),
BinaryBitAnd,
}
// #&
BinaryBitAnd: Box<Expr<'input>> = {
<l:BinaryBitAnd> "#&" <r:BinaryShift> => Box::new(Expr::BinaryOp(BinaryOp::BitAnd, l, r)),
BinaryShift,
}
// >> <<
BinaryShift: Box<Expr<'input>> = {
<l:BinaryShift> <o:ShiftOp> <r:BinaryAdd> => Box::new(Expr::BinaryOp(o, l, r)),
BinaryAdd,
}
ShiftOp: BinaryOp = {
">>" => BinaryOp::Shr,
"<<" => BinaryOp::Shl,
}
// + -
BinaryAdd: Box<Expr<'input>> = {
<l:BinaryAdd> <o:AddOp> <r:BinaryMul> => Box::new(Expr::BinaryOp(o, l, r)),
BinaryMul,
}
AddOp: BinaryOp = {
"+" => BinaryOp::Add,
"-" => BinaryOp::Sub,
}
// * / %
BinaryMul: Box<Expr<'input>> = {
<l:BinaryMul> <o:MulOp> <r:UnaryMinus> => Box::new(Expr::BinaryOp(o, l, r)),
UnaryMinus,
}
MulOp: BinaryOp = {
"*" => BinaryOp::Mul,
"/" => BinaryOp::Div,
"//" => BinaryOp::IntDiv,
"%" => BinaryOp::Mod,
}
// unary-
UnaryMinus: Box<Expr<'input>> = {
"-" <r:BinaryPow> => Box::new(Expr::UnaryOp(UnaryOp::Neg, r)),
BinaryPow,
}
// power ( ^ )
BinaryPow: Box<Expr<'input>> = {
<l:BinaryIndex> "^" <r:UnaryMinus> => Box::new(Expr::BinaryOp(BinaryOp::Pow, l, r)),
BinaryIndex,
}
// index ( ! )
BinaryIndex: Box<Expr<'input>> = {
<l:BinaryIndex> "!" <r:UnaryMinus2> => Box::new(Expr::Index(l, r)),
CallOrAccess,
}
// unary-
UnaryMinus2: Box<Expr<'input>> = {
"-" <r:UnaryMinus2> => Box::new(Expr::UnaryOp(UnaryOp::Neg, r)),
CallOrAccess,
}
//
// things
//
// function call
CallOrAccess: Box<Expr<'input>> = {
<l:CallOrAccess> "(" <args:ExprList> ")" => Box::new(Expr::FnCall(l, args)),
<l:CallOrAccess> "->" <r:Identifier> "(" <args:ExprList> ")" => Box::new(Expr::AssocFnCall(l, Symbol::get(r), args)),
<l:CallOrAccess> "." <r:Identifier> => Box::new(Expr::Index(l, Box::new(Expr::Literal(Value::Symbol(Symbol::get(r)))))),
Term,
}
//
// base
//
Term: Box<Expr<'input>> = {
Identifier => Box::new(Expr::Ident(<>)),
TermNotIdent,
}
TermNotIdent: Box<Expr<'input>> = {
"(" <Expr> ")" => <>,
"[" <ExprList> "]" => Box::new(Expr::List(<>)),
"{" <TableItems> "}" => Box::new(Expr::Table(<>)),
"$" => Box::new(Expr::Ident(lstr!("$"))),
"$$" => Box::new(Expr::Ident(lstr!("$$"))),
"do" <Block> "end" => <>,
"if" <IfStmtChain> => <>,
"while" <a:Expr> "do" <b:Block> "end"
=> Box::new(Expr::While(a, b)),
"for" <v:Identifier> "in" <a:Expr> "do" <b:Block> "end"
=> Box::new(Expr::For(v, a, b)),
"try" <b:Block> <mut ch:CatchChain> => {
ch.reverse();
Box::new(Expr::Try(b, ch))
},
Literal => Box::new(Expr::Literal(<>)),
}
IfStmtChain: Box<Expr<'input>> = {
<a:Expr> "then" <b:Block> "end" => Box::new(Expr::If(a, b, None)),
<a:Expr> "then" <b:Block> "else" <c:Block> "end" => Box::new(Expr::If(a, b, Some(c))),
<a:Expr> "then" <b:Block> "elif" <c:IfStmtChain> => Box::new(Expr::If(a, b, Some(c))),
}
CatchChain: Vec<CatchBlock<'input>> = {
"catch" <types:SymbolList> <name:("in" <Identifier>)?> "do" <b:Block> <mut ch:CatchChain> => {
ch.push(CatchBlock { name, types: Some(types), body: *b });
ch
},
"catch" "*" <name:("in" <Identifier>)?> "do" <b:Block> <mut ch:CatchChain> => {
ch.push(CatchBlock { name, types: None, body: *b });
ch
},
"end" => Vec::new(),
}
SymbolList: Vec<Symbol> = {
<mut xs:(<SymbolLiteral> ",")*> <x:SymbolLiteral?> => {
if let Some(x) = x { xs.push(x) };
xs
},
}
ExprList: Vec<Expr<'input>> = {
<xs:(<Expr> ",")*> <x:Expr?> => {
let mut xs: Vec<_> = xs.into_iter().map(|x| *x).collect();
if let Some(x) = x { xs.push(*x) };
xs
}
}
TableItems: Vec<(Expr<'input>, Expr<'input>)> = {
<mut xs:(<TableItem> ",")*> <x:TableItem?> => {
if let Some(x) = x { xs.push(x) };
xs
}
}
TableItem: (Expr<'input>, Expr<'input>) = {
<k:TableKey> "=" <v:Expr> => (k, *v),
}
TableKey: Expr<'input> = {
Identifier => Expr::Literal(Value::Symbol(Symbol::get(<>))),
TermNotIdent => *<>,
}
IdentList: Vec<&'input LStr> = {
<mut xs:(<Identifier> ",")*> <x:Identifier?>
=> { if let Some(x) = x { xs.push(x) }; xs }
}
Identifier: &'input LStr = TokIdentifier => <>.into();
//
// literals
//
Literal: Value = {
TokDecInteger =>? parse_int(<>, 10)
.map(Value::Int)
.map_err(|e| ParseError::from(e).into()),
TokHexInteger =>? parse_int(&<>[2..], 16)
.map(Value::Int)
.map_err(|e| ParseError::from(e).into()),
TokOctInteger =>? parse_int(&<>[2..], 8)
.map(Value::Int)
.map_err(|e| ParseError::from(e).into()),
TokSexInteger =>? parse_int(&<>[2..], 6)
.map(Value::Int)
.map_err(|e| ParseError::from(e).into()),
TokBinInteger =>? parse_int(&<>[2..], 2)
.map(Value::Int)
.map_err(|e| ParseError::from(e).into()),
<f:TokFloat> =>? {
if let Some(f) = f.strip_suffix('i') {
parse_float(f)
.map(|im| Value::Complex(Complex64::new(0.0, im)))
.map_err(|e| ParseError::from(e).into())
} else {
parse_float(f)
.map(Value::Float)
.map_err(|e| ParseError::from(e).into())
}
},
StringLiteral => Value::String(<>),
SymbolLiteral => Value::Symbol(<>),
"true" => Value::Bool(true),
"false" => Value::Bool(false),
"nil" => Value::Nil,
}
StringLiteral: Rc<LStr> = {
TokStringSingle => LStr::from_str(&<>[1..<>.len()-1]).into(),
TokStringDouble =>? parse_str_escapes(&<>[1..<>.len()-1])
.map(|s| s.into())
.map_err(|e| ParseError::from(e).into()),
}
SymbolLiteral: Symbol = {
TokSymbolNone => Symbol::get(&<>[1..]),
TokSymbolSingle => Symbol::get(&<>[2..<>.len()-1]),
TokSymbolDouble =>? parse_str_escapes(&<>[2..<>.len()-1])
.map(|s| Symbol::get(&s))
.map_err(|e| ParseError::from(e).into()),
}

314
talc-lang/src/parser/ast.rs Normal file
View file

@ -0,0 +1,314 @@
use core::fmt;
use crate::{symbol::Symbol, value::Value};
use super::Span;
#[derive(Clone, Copy, Debug)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Mod,
Pow,
IntDiv,
Shr,
Shl,
BitAnd,
BitXor,
BitOr,
Eq,
Ne,
Gt,
Lt,
Ge,
Le,
Concat,
Append,
Range,
RangeIncl,
}
#[derive(Clone, Copy, Debug)]
pub enum UnaryOp {
Neg,
Not,
RangeEndless,
}
#[derive(Debug)]
pub struct Expr {
pub span: Span,
pub kind: ExprKind,
}
#[derive(Debug)]
pub enum ExprKind {
Literal(Value),
Ident(Symbol),
UnaryOp(UnaryOp, Box<Expr>),
BinaryOp(BinaryOp, Box<Expr>, Box<Expr>),
Assign(Option<BinaryOp>, Box<LValue>, Box<Expr>),
AssignVar(Symbol, Box<Expr>),
AssignGlobal(Symbol, Box<Expr>),
FnDef(Option<Symbol>, Vec<Symbol>, Box<Expr>),
Index(Box<Expr>, Box<Expr>),
FnCall(Box<Expr>, Vec<Expr>),
AssocFnCall(Box<Expr>, Symbol, Vec<Expr>),
Pipe(Box<Expr>, Box<Expr>),
Block(Vec<Expr>),
List(Vec<Expr>),
Table(Vec<(Expr, Expr)>),
Return(Box<Expr>),
And(Box<Expr>, Box<Expr>),
Or(Box<Expr>, Box<Expr>),
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
While(Box<Expr>, Box<Expr>),
For(Symbol, Box<Expr>, Box<Expr>),
Lambda(Vec<Symbol>, Box<Expr>),
Try(Box<Expr>, Vec<CatchBlock>),
}
impl ExprKind {
pub fn span(self, span: Span) -> Expr {
Expr { kind: self, span }
}
}
#[derive(Debug)]
pub struct CatchBlock {
pub span: Span,
pub name: Option<Symbol>,
pub types: Option<Vec<Symbol>>,
pub body: Expr,
}
#[derive(Debug)]
pub struct LValue {
pub span: Span,
pub kind: LValueKind,
}
#[derive(Debug)]
pub enum LValueKind {
Ident(Symbol),
Index(Box<Expr>, Box<Expr>),
}
impl LValueKind {
pub fn span(self, span: Span) -> LValue {
LValue { kind: self, span }
}
}
impl LValue {
pub fn from_expr(e: Expr) -> Option<LValue> {
let Expr { span, kind } = e;
match kind {
ExprKind::Ident(i) => Some(LValueKind::Ident(i).span(span)),
ExprKind::Index(l, r) => Some(LValueKind::Index(l, r).span(span)),
_ => None,
}
}
}
impl CatchBlock {
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
write!(w, "{0: >1$}catch", "", depth * 2)?;
if let Some(name) = self.name {
write!(w, " ${}", name.name())?;
}
if let Some(types) = &self.types {
write!(w, ":")?;
for ty in types {
write!(w, " {}", ty.name())?;
}
}
writeln!(w)?;
self.body.write_to(w, depth + 1)
}
}
impl fmt::Display for CatchBlock {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write_to(f, 0)
}
}
impl LValue {
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
write!(w, "{0: >1$}", "", depth * 2)?;
let depth = depth + 1;
match &self.kind {
LValueKind::Ident(n) => writeln!(w, "${}", n.name()),
LValueKind::Index(l, r) => {
writeln!(w, "index")?;
l.write_to(w, depth)?;
r.write_to(w, depth)
}
}
}
}
impl fmt::Display for LValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write_to(f, 0)
}
}
impl Expr {
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
write!(w, "{0: >1$}", "", depth * 2)?;
let depth = depth + 1;
match &self.kind {
ExprKind::Literal(val) => writeln!(w, "{val}"),
ExprKind::Ident(n) => writeln!(w, "${}", n.name()),
ExprKind::UnaryOp(op, e) => {
writeln!(w, "uop {op:?}")?;
e.write_to(w, depth)
}
ExprKind::BinaryOp(op, l, r) => {
writeln!(w, "bop {op:?}")?;
l.write_to(w, depth)?;
r.write_to(w, depth)
}
ExprKind::Assign(op, l, r) => {
if let Some(op) = op {
writeln!(w, "asgn {op:?}")?;
} else {
writeln!(w, "asgn =")?;
}
l.write_to(w, depth)?;
r.write_to(w, depth)
}
ExprKind::AssignVar(l, r) => {
writeln!(w, "var {}", l.name())?;
r.write_to(w, depth)
}
ExprKind::AssignGlobal(l, r) => {
writeln!(w, "global {}", l.name())?;
r.write_to(w, depth)
}
ExprKind::FnDef(n, p, b) => {
if let Some(n) = n {
writeln!(w, "fndef ${}", n.name())?;
} else {
writeln!(w, "fndef anon")?;
}
for arg in p {
writeln!(w, " ${}", arg.name())?;
}
b.write_to(w, depth)
}
ExprKind::Index(l, r) => {
writeln!(w, "index")?;
l.write_to(w, depth)?;
r.write_to(w, depth)
}
ExprKind::FnCall(f, a) => {
writeln!(w, "call")?;
f.write_to(w, depth)?;
for arg in a {
arg.write_to(w, depth)?;
}
Ok(())
}
ExprKind::AssocFnCall(d, f, a) => {
writeln!(w, "assoc call {}", f.name())?;
d.write_to(w, depth)?;
for arg in a {
arg.write_to(w, depth)?;
}
Ok(())
}
ExprKind::Pipe(l, r) => {
writeln!(w, "pipe")?;
l.write_to(w, depth)?;
r.write_to(w, depth)
}
ExprKind::Block(b) => {
writeln!(w, "block")?;
for e in b {
e.write_to(w, depth)?;
}
Ok(())
}
ExprKind::List(l) => {
writeln!(w, "list")?;
for e in l {
e.write_to(w, depth)?;
}
Ok(())
}
ExprKind::Table(t) => {
writeln!(w, "list")?;
for (k, v) in t {
k.write_to(w, depth)?;
v.write_to(w, depth)?;
}
Ok(())
}
ExprKind::Return(e) => {
writeln!(w, "return")?;
e.write_to(w, depth)
}
ExprKind::And(l, r) => {
writeln!(w, "and")?;
l.write_to(w, depth)?;
r.write_to(w, depth)
}
ExprKind::Or(l, r) => {
writeln!(w, "or")?;
l.write_to(w, depth)?;
r.write_to(w, depth)
}
ExprKind::If(c, b, e) => {
writeln!(w, "if")?;
c.write_to(w, depth)?;
b.write_to(w, depth)?;
if let Some(e) = e {
e.write_to(w, depth)?;
}
Ok(())
}
ExprKind::While(c, b) => {
writeln!(w, "while")?;
c.write_to(w, depth)?;
b.write_to(w, depth)
}
ExprKind::For(v, i, b) => {
writeln!(w, "for {}", v.name())?;
i.write_to(w, depth)?;
b.write_to(w, depth)
}
ExprKind::Lambda(a, b) => {
write!(w, "lambda")?;
for arg in a {
write!(w, " {}", arg.name())?;
}
writeln!(w)?;
b.write_to(w, depth)
}
ExprKind::Try(t, c) => {
write!(w, "try")?;
t.write_to(w, depth)?;
for catch in c {
catch.write_to(w, depth)?;
}
Ok(())
}
}
}
}
impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write_to(f, 0)
}
}

View file

@ -0,0 +1,602 @@
use std::{fmt, iter::Peekable, str::Chars};
use unicode_ident::{is_xid_continue, is_xid_start};
use super::{ParserError, Pos, Span};
type Result<T> = std::result::Result<T, ParserError>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TokenKind {
Eof,
LineSeparator,
Bang,
BangEqual,
HashAmper,
HashAmperEqual,
HashCaret,
HashCaretEqual,
HashPipe,
HashPipeEqual,
Dollar,
Percent,
PercentEqual,
Amper,
AmperEqual,
Star,
StarEqual,
Plus,
PlusPlus,
PlusPlusEqual,
PlusEqual,
Comma,
Minus,
MinusEqual,
Arrow,
Dot,
DotDot,
DotDotStar,
DotDotEqual,
Slash,
SlashSlash,
SlashSlashEqual,
SlashEqual,
Colon,
Less,
LessLess,
LessLessEqual,
LessEqual,
Equal,
EqualEqual,
Greater,
GreaterEqual,
GreaterGreater,
GreaterGreaterEqual,
LParen,
RParen,
LBrack,
RBrack,
LBrace,
RBrace,
Backslash,
Caret,
CaretEqual,
Pipe,
Identifier,
Integer,
Float,
Imaginary,
String,
Symbol,
And,
Break,
Catch,
Continue,
Do,
Elif,
Else,
End,
False,
Fn,
For,
Global,
If,
In,
Nil,
Not,
Or,
Return,
Then,
True,
Try,
Var,
While,
}
use TokenKind as K;
impl TokenKind {
pub fn name(self) -> &'static str {
match self {
K::Eof => "end of file",
K::LineSeparator => "line separator",
K::Bang => "'!'",
K::BangEqual => "'!='",
K::HashAmper => "'#&'",
K::HashAmperEqual => "'#&='",
K::HashCaret => "'#^'",
K::HashCaretEqual => "'#^='",
K::HashPipe => "'#|'",
K::HashPipeEqual => "'#|='",
K::Dollar => "'$'",
K::Percent => "'%'",
K::PercentEqual => "'%='",
K::Amper => "'&'",
K::AmperEqual => "'&='",
K::Star => "'*'",
K::StarEqual => "'*='",
K::Plus => "'+'",
K::PlusPlus => "'++'",
K::PlusPlusEqual => "'++='",
K::PlusEqual => "'+='",
K::Comma => "','",
K::Minus => "'-'",
K::MinusEqual => "'-='",
K::Arrow => "'=>'",
K::Dot => "'.'",
K::DotDot => "'..'",
K::DotDotStar => "'..*'",
K::DotDotEqual => "'..='",
K::Slash => "'/'",
K::SlashSlash => "'//'",
K::SlashSlashEqual => "'//='",
K::SlashEqual => "'/='",
K::Colon => "':'",
K::Less => "'<'",
K::LessLess => "'<<'",
K::LessLessEqual => "'<<='",
K::LessEqual => "'<='",
K::Equal => "'='",
K::EqualEqual => "'=='",
K::Greater => "'>'",
K::GreaterEqual => "'>='",
K::GreaterGreater => "'>>'",
K::GreaterGreaterEqual => "'>>='",
K::LParen => "'('",
K::RParen => "')'",
K::LBrack => "'['",
K::RBrack => "']'",
K::LBrace => "'{'",
K::RBrace => "'}'",
K::Backslash => "'\\'",
K::Caret => "'^'",
K::CaretEqual => "'^='",
K::Pipe => "'|'",
K::Identifier => "identifier",
K::Integer => "integer",
K::Float => "float",
K::Imaginary => "imaginary",
K::String => "string",
K::Symbol => "symbol",
K::And => "'and'",
K::Break => "'break'",
K::Catch => "'catch'",
K::Continue => "'continue'",
K::Do => "'do'",
K::Elif => "'elif'",
K::Else => "'else'",
K::End => "'end'",
K::False => "'false'",
K::Fn => "'fn'",
K::For => "'for'",
K::Global => "'global'",
K::If => "'if'",
K::In => "'in'",
K::Nil => "'nil'",
K::Not => "'not'",
K::Or => "'or'",
K::Return => "'return'",
K::Then => "'then'",
K::True => "'true'",
K::Try => "'try'",
K::Var => "'var'",
K::While => "'while'",
}
}
}
impl fmt::Display for TokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Clone, Copy, Debug)]
pub struct Token<'s> {
pub span: Span,
pub content: &'s str,
pub kind: TokenKind,
}
pub struct Lexer<'s> {
src: &'s str,
chars: Peekable<Chars<'s>>,
start_pos: Pos,
pos: Pos,
}
impl<'s> Lexer<'s> {
pub fn new(src: &'s str) -> Self {
Self {
src,
chars: src.chars().peekable(),
start_pos: Pos::new(),
pos: Pos::new(),
}
}
fn invalid_char(&self, c: char) -> ParserError {
let span = Span::new(self.pos, self.pos.advance(c));
let msg = match c as u32 {
c @ 0x00..=0x7f => format!("invalid character (codepoint 0x{:2x})", c),
c => format!("invalid character (codepoint U+{:04x})", c),
};
ParserError { span, msg }
}
fn filter_char(&mut self, c: Option<char>, advance: bool) -> Result<char> {
match c {
Some(c) if c.is_control() && !matches!(c, '\n' | '\r' | '\t') => {
Err(self.invalid_char(c))
}
Some(c) => {
if advance {
self.pos = self.pos.advance(c);
}
Ok(c)
}
None => Ok('\0'),
}
}
fn peek(&mut self) -> Result<char> {
let c = self.chars.peek().copied();
self.filter_char(c, false)
}
fn peek_n(&mut self, n: usize) -> Option<char> {
let chars = &self.src[(self.pos.idx as usize)..];
chars.chars().nth(n)
}
fn next(&mut self) -> Result<char> {
let c = self.chars.next();
self.filter_char(c, true)
}
fn and_peek(&mut self) -> Result<char> {
self.next()?;
self.peek()
}
fn emit(&self, kind: TokenKind) -> Result<Token<'s>> {
let span = Span::new(self.start_pos, self.pos);
Ok(Token {
span,
content: span.of(self.src),
kind,
})
}
fn and_emit(&mut self, kind: TokenKind) -> Result<Token<'s>> {
self.next()?;
self.emit(kind)
}
fn unexpected(&mut self) -> Result<Token<'s>> {
let c = self.peek()?;
let span = Span::new(self.pos, self.pos.advance(c));
let msg = match c {
'\0' => "unexpected end of file".to_owned(),
'\n' => "unexpected newline character".to_owned(),
'\t' => "unexpected tab character".to_owned(),
'\r' => "unexpected return character".to_owned(),
c => format!("unexpected character {c}"),
};
Err(ParserError { span, msg })
}
fn next_ident(&mut self) -> Result<Token<'s>> {
self.next()?;
while is_xid_continue(self.peek()?) {
self.next()?;
}
let kind = match Span::new(self.start_pos, self.pos).of(self.src) {
"and" => K::And,
"break" => K::Break,
"catch" => K::Catch,
"continue" => K::Continue,
"do" => K::Do,
"elif" => K::Elif,
"else" => K::Else,
"end" => K::End,
"false" => K::False,
"fn" => K::Fn,
"for" => K::For,
"global" => K::Global,
"if" => K::If,
"in" => K::In,
"nil" => K::Nil,
"not" => K::Not,
"or" => K::Or,
"return" => K::Return,
"then" => K::Then,
"true" => K::True,
"try" => K::Try,
"var" => K::Var,
"while" => K::While,
_ => K::Identifier,
};
self.emit(kind)
}
fn next_int_base(&mut self, radix: u32) -> Result<Token<'s>> {
loop {
let c = self.peek()?;
if c == '_' || c.is_digit(radix) {
self.next()?;
} else if is_xid_start(c) {
return self.unexpected()
} else {
return self.emit(K::Integer)
}
}
}
fn next_imag(&mut self) -> Result<Token<'s>> {
self.next()?;
if is_xid_start(self.peek()?) {
self.unexpected()
} else {
self.emit(K::Imaginary)
}
}
fn next_float(&mut self, mut has_e: bool) -> Result<Token<'s>> {
while !has_e {
while matches!(self.peek()?, '_' | '0'..='9') {
self.next()?;
}
match self.peek()? {
'e' => {
self.next()?;
has_e = true;
}
'i' => return self.next_imag(),
c if is_xid_start(c) => return self.unexpected(),
_ => return self.emit(K::Float),
}
}
if matches!(self.peek()?, '+' | '-') {
self.next()?;
}
while matches!(self.peek()?, '_' | '0'..='9') {
self.next()?;
}
match self.peek()? {
'i' => self.next_imag(),
c if is_xid_start(c) => self.unexpected(),
_ => self.emit(K::Float),
}
}
fn next_number(&mut self) -> Result<Token<'s>> {
if self.next()? == '0' {
while self.peek()? == '_' {
self.next()?;
}
match self.peek()? {
'x' => {
self.next()?;
return self.next_int_base(16)
}
'o' => {
self.next()?;
return self.next_int_base(8)
}
's' => {
self.next()?;
return self.next_int_base(6)
}
'b' => {
self.next()?;
return self.next_int_base(2)
}
'0'..='9' | '.' | 'e' | 'i' => (),
c if is_xid_start(c) => return self.unexpected(),
_ => return self.emit(K::Integer),
}
}
while matches!(self.peek()?, '_' | '0'..='9') {
self.next()?;
}
match self.peek()? {
'r' => todo!("arbitrary radix integer literals"),
'e' => {
self.next()?;
self.next_float(true)
}
'i' => self.next_imag(),
'.' => {
if self.peek_n(1) == Some('.') {
self.emit(K::Integer)
} else {
self.next()?;
self.next_float(false)
}
}
c if is_xid_start(c) => self.unexpected(),
_ => self.emit(K::Integer),
}
}
fn next_string(&mut self) -> Result<Token<'s>> {
let double_quote = self.next()? == '"';
loop {
match self.next()? {
'\0' => return self.unexpected(),
'"' if double_quote => break,
'\'' if !double_quote => break,
'\\' if double_quote => {
self.next()?;
}
_ => (),
}
}
self.emit(K::String)
}
fn next_symbol(&mut self) -> Result<Token<'s>> {
if matches!(self.peek()?, '\'' | '"') {
self.next_string()?;
} else {
self.next_ident()?;
}
self.emit(K::Symbol)
}
fn line_comment(&mut self) -> Result<Token<'s>> {
while !matches!(self.peek()?, '\0' | '\n') {
self.next()?;
}
self.next_token()
}
fn next_token(&mut self) -> Result<Token<'s>> {
while matches!(self.peek()?, ' ' | '\t' | '\r') {
self.next()?;
}
self.start_pos = self.pos;
match self.peek()? {
// misc
'\0' => self.emit(K::Eof),
';' | '\n' => self.and_emit(K::LineSeparator),
'(' => self.and_emit(K::LParen),
')' => self.and_emit(K::RParen),
'{' => self.and_emit(K::LBrace),
'}' => self.and_emit(K::RBrace),
'[' => self.and_emit(K::LBrack),
']' => self.and_emit(K::RBrack),
',' => self.and_emit(K::Comma),
'$' => self.and_emit(K::Dollar),
'|' => self.and_emit(K::Pipe),
'\\' => match self.and_peek()? {
'\n' => {
self.next()?;
self.next_token()
}
_ => self.emit(K::Backslash),
},
// arithmetic
'+' => match self.and_peek()? {
'+' => match self.and_peek()? {
'=' => self.and_emit(K::PlusPlusEqual),
_ => self.emit(K::PlusPlus),
},
'=' => self.and_emit(K::PlusEqual),
_ => self.emit(K::Plus),
},
'-' => match self.and_peek()? {
'-' => self.line_comment(),
'=' => self.and_emit(K::MinusEqual),
'>' => self.and_emit(K::Arrow),
_ => self.emit(K::Minus),
},
'*' => match self.and_peek()? {
'=' => self.and_emit(K::StarEqual),
_ => self.emit(K::Star),
},
'/' => match self.and_peek()? {
'/' => match self.and_peek()? {
'=' => self.and_emit(K::SlashSlashEqual),
_ => self.emit(K::SlashSlash),
},
'=' => self.and_emit(K::SlashEqual),
_ => self.emit(K::Slash),
},
'%' => match self.and_peek()? {
'=' => self.and_emit(K::PercentEqual),
_ => self.emit(K::Percent),
},
'^' => match self.and_peek()? {
'=' => self.and_emit(K::CaretEqual),
_ => self.emit(K::Caret),
},
// logical
'#' => match self.and_peek()? {
'&' => match self.and_peek()? {
'=' => self.and_emit(K::HashAmperEqual),
_ => self.emit(K::HashAmper),
},
'^' => match self.and_peek()? {
'=' => self.and_emit(K::HashCaretEqual),
_ => self.emit(K::HashCaret),
},
'|' => match self.and_peek()? {
'=' => self.and_emit(K::HashPipeEqual),
_ => self.emit(K::HashPipe),
},
'!' => self.line_comment(),
_ => self.unexpected(),
},
// lists
'!' => match self.and_peek()? {
'=' => self.and_emit(K::BangEqual),
_ => self.emit(K::Bang),
},
'&' => match self.and_peek()? {
'=' => self.and_emit(K::AmperEqual),
_ => self.emit(K::Amper),
},
// comparison
'<' => match self.and_peek()? {
'<' => match self.and_peek()? {
'=' => self.and_emit(K::LessLessEqual),
_ => self.emit(K::LessLess),
},
'=' => self.and_emit(K::LessEqual),
_ => self.emit(K::Less),
},
'>' => match self.and_peek()? {
'>' => match self.and_peek()? {
'=' => self.and_emit(K::GreaterGreaterEqual),
_ => self.emit(K::GreaterGreater),
},
'=' => self.and_emit(K::GreaterEqual),
_ => self.emit(K::Greater),
},
'=' => match self.and_peek()? {
'=' => self.and_emit(K::EqualEqual),
_ => self.emit(K::Equal),
},
// range
'.' => match self.and_peek()? {
'.' => match self.and_peek()? {
'*' => self.and_emit(K::DotDotStar),
'=' => self.and_emit(K::DotDotEqual),
_ => self.emit(K::DotDot),
},
_ => self.emit(K::Dot),
},
':' => match self.and_peek()? {
c if is_xid_start(c) || c == '"' || c == '\'' => self.next_symbol(),
_ => self.emit(K::Colon),
},
'0'..='9' => self.next_number(),
c if is_xid_start(c) => self.next_ident(),
'"' | '\'' => self.next_string(),
_ => self.unexpected(),
}
}
pub fn tokens(mut self) -> Result<Vec<Token<'s>>> {
let mut res = Vec::new();
loop {
let t = self.next_token()?;
let k = t.kind;
res.push(t);
if k == TokenKind::Eof {
return Ok(res)
}
}
}
}
impl<'s> Iterator for Lexer<'s> {
type Item = Result<Token<'s>>;
fn next(&mut self) -> Option<Self::Item> {
Some(self.next_token())
}
}

View file

@ -0,0 +1,14 @@
pub mod ast;
mod lexer;
pub use lexer::*;
#[expect(clippy::module_inception)]
mod parser;
pub use parser::*;
mod pos;
pub use pos::*;
mod util;
pub use util::*;

View file

@ -0,0 +1,663 @@
use std::iter::Peekable;
use crate::{
symbol::{Symbol, SYM_DOLLAR_SIGN},
value::Value,
};
use super::{
ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, UnaryOp},
parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span,
SpanParserError, Token, TokenKind,
};
use num_complex::Complex64;
use ExprKind as E;
use TokenKind as T;
type Result<T> = std::result::Result<T, ParserError>;
macro_rules! expect {
($self:expr, $($t:tt)*) => {{
let t = $self.next()?;
match t.kind {
$($t)* => t,
e => return Err(ParserError { span: t.span, msg: expect_inner!(e, $($t)*) })
}
}};
}
macro_rules! expect_inner {
($e:expr, $($tok:path)|*) => {
{
let mut s = format!("unexpected token {}, expected ", $e.name())
+ $($tok.name() + ", " +)* "";
s.truncate(s.len() - 2);
s
}
};
}
macro_rules! try_next {
($self:expr, $pat:pat) => {{
let t = $self.peek()?;
match t.kind {
$pat => Some($self.next()?),
_ => None,
}
}};
}
macro_rules! throw {
($span:expr, $($t:tt)*) => {
return Err(ParserError { span: $span, msg: format!($($t)*) })
};
}
impl TokenKind {
pub fn assign_op(self) -> Option<Option<BinaryOp>> {
Some(match self {
T::PlusPlusEqual => Some(BinaryOp::Concat),
T::AmperEqual => Some(BinaryOp::Append),
T::HashPipeEqual => Some(BinaryOp::BitOr),
T::HashCaretEqual => Some(BinaryOp::BitXor),
T::HashAmperEqual => Some(BinaryOp::BitAnd),
T::LessLessEqual => Some(BinaryOp::Shl),
T::GreaterGreaterEqual => Some(BinaryOp::Shr),
T::PlusEqual => Some(BinaryOp::Add),
T::MinusEqual => Some(BinaryOp::Sub),
T::StarEqual => Some(BinaryOp::Mul),
T::SlashEqual => Some(BinaryOp::Div),
T::SlashSlashEqual => Some(BinaryOp::IntDiv),
T::PercentEqual => Some(BinaryOp::Mod),
T::CaretEqual => Some(BinaryOp::Pow),
T::Equal => None,
_ => return None,
})
}
pub fn binary_op(self) -> Option<BinaryOp> {
Some(match self {
T::EqualEqual => BinaryOp::Eq,
T::BangEqual => BinaryOp::Ne,
T::Greater => BinaryOp::Gt,
T::GreaterEqual => BinaryOp::Ge,
T::Less => BinaryOp::Lt,
T::LessEqual => BinaryOp::Le,
T::PlusPlus => BinaryOp::Concat,
T::Amper => BinaryOp::Append,
T::DotDot => BinaryOp::Range,
T::DotDotEqual => BinaryOp::RangeIncl,
T::HashPipe => BinaryOp::BitOr,
T::HashCaret => BinaryOp::BitXor,
T::HashAmper => BinaryOp::BitAnd,
T::LessLess => BinaryOp::Shl,
T::GreaterGreater => BinaryOp::Shr,
T::Plus => BinaryOp::Add,
T::Minus => BinaryOp::Sub,
T::Star => BinaryOp::Mul,
T::Slash => BinaryOp::Div,
T::SlashSlash => BinaryOp::IntDiv,
T::Percent => BinaryOp::Mod,
T::Caret => BinaryOp::Pow,
_ => return None,
})
}
pub fn unary_op(self) -> Option<UnaryOp> {
match self {
T::Minus => Some(UnaryOp::Neg),
T::Not => Some(UnaryOp::Not),
_ => None,
}
}
pub fn postfix_unary_op(self) -> Option<UnaryOp> {
match self {
T::DotDotStar => Some(UnaryOp::RangeEndless),
_ => None,
}
}
}
impl UnaryOp {
pub fn precedence(self) -> u8 {
match self {
UnaryOp::Not => 0,
UnaryOp::RangeEndless => 40,
UnaryOp::Neg => 110,
}
}
}
impl BinaryOp {
pub fn precedence(self) -> (u8, u8) {
match self {
BinaryOp::Eq
| BinaryOp::Ne
| BinaryOp::Gt
| BinaryOp::Ge
| BinaryOp::Lt
| BinaryOp::Le => (10, 10),
BinaryOp::Concat => (20, 25),
BinaryOp::Append => (30, 35),
BinaryOp::Range | BinaryOp::RangeIncl => (40, 40),
BinaryOp::BitOr => (50, 55),
BinaryOp::BitXor => (60, 65),
BinaryOp::BitAnd => (70, 75),
BinaryOp::Shl | BinaryOp::Shr => (80, 85),
BinaryOp::Add | BinaryOp::Sub => (90, 95),
BinaryOp::Mul | BinaryOp::Div | BinaryOp::IntDiv | BinaryOp::Mod => (100, 105),
BinaryOp::Pow => (125, 120),
}
}
}
#[inline(always)]
fn b<T>(t: T) -> Box<T> {
Box::new(t)
}
impl TokenKind {
fn expr_first(self) -> bool {
matches!(
self,
T::Return
| T::Var | T::Global
| T::Fn | T::Not
| T::Backslash
| T::Colon | T::Minus
| T::Identifier
| T::LParen | T::LBrack
| T::LBrace | T::Dollar
| T::Do | T::If
| T::While | T::For
| T::Try | T::Integer
| T::Float | T::Imaginary
| T::String | T::Symbol
| T::True | T::False
| T::Nil
)
}
}
struct Parser<'s> {
lexer: Peekable<Lexer<'s>>,
}
impl<'s> Parser<'s> {
fn new(src: &'s str) -> Self {
Self {
lexer: Lexer::new(src).peekable(),
}
}
fn next(&mut self) -> Result<Token<'s>> {
self.lexer.next().unwrap()
}
fn peek(&mut self) -> Result<Token<'s>> {
self.lexer.peek().unwrap().clone()
}
fn parse_table_items(&mut self) -> Result<Vec<(Expr, Expr)>> {
let mut items = Vec::new();
while self.peek()?.kind.expr_first() {
let key = if let Some(id) = try_next!(self, T::Identifier) {
E::Literal(Symbol::get(id.content).into()).span(id.span)
} else {
self.parse_term_not_ident()?
};
expect!(self, T::Equal);
let value = self.parse_expr()?;
items.push((key, value));
if try_next!(self, T::Comma).is_none() {
break
}
}
Ok(items)
}
fn parse_expr_list(&mut self) -> Result<Vec<Expr>> {
let mut exprs = Vec::new();
while self.peek()?.kind.expr_first() {
exprs.push(self.parse_expr()?);
if try_next!(self, T::Comma).is_none() {
break
}
}
Ok(exprs)
}
fn parse_ident_list(&mut self) -> Result<Vec<Symbol>> {
let mut idents = Vec::new();
while let Some(tok) = try_next!(self, T::Identifier) {
idents.push(Symbol::get(tok.content));
if try_next!(self, T::Comma).is_none() {
break
}
}
Ok(idents)
}
fn parse_symbol_list(&mut self) -> Result<Vec<Symbol>> {
let mut syms = Vec::new();
while let Some(tok) = try_next!(self, T::Symbol) {
syms.push(Symbol::get(tok.content));
if try_next!(self, T::Comma).is_none() {
break
}
}
Ok(syms)
}
fn parse_catch_blocks(&mut self) -> Result<(Vec<CatchBlock>, Span)> {
let mut blocks = Vec::new();
let mut outer_span = self.peek()?.span;
loop {
let tok = expect!(self, T::Catch | T::End);
if tok.kind == T::End {
break
}
let types = match try_next!(self, T::Star) {
Some(_) => None,
None => Some(self.parse_symbol_list()?),
};
let name = match try_next!(self, T::In) {
Some(_) => Some(Symbol::get(expect!(self, T::Identifier).content)),
None => None,
};
expect!(self, T::Do);
let body = self.parse_block()?;
let span = tok.span + body.span;
blocks.push(CatchBlock {
span,
name,
types,
body,
});
outer_span += span;
}
Ok((blocks, outer_span))
}
fn parse_if_stmt_chain(&mut self) -> Result<Expr> {
let cond = self.parse_expr()?;
expect!(self, T::Then);
let body = self.parse_block()?;
let tok = expect!(self, T::End | T::Else | T::Elif);
let span = cond.span + tok.span;
match tok.kind {
T::End => Ok(E::If(b(cond), b(body), None).span(span)),
T::Else => {
let else_body = self.parse_block()?;
expect!(self, T::End);
let span = span + else_body.span;
Ok(E::If(b(cond), b(body), Some(b(else_body))).span(span))
}
T::Elif => {
let elif_body = self.parse_if_stmt_chain()?;
let span = span + elif_body.span;
Ok(E::If(b(cond), b(body), Some(b(elif_body))).span(span))
}
_ => unreachable!("parse_if_stmt_chain: guaranteed by expect!"),
}
}
fn parse_term_not_ident(&mut self) -> Result<Expr> {
let tok = self.next()?;
match tok.kind {
T::LParen => {
let e = self.parse_expr()?;
expect!(self, T::RParen);
Ok(e)
}
T::LBrack => {
let args = self.parse_expr_list()?;
let end = expect!(self, T::RBrack);
Ok(E::List(args).span(tok.span + end.span))
}
T::LBrace => {
let args = self.parse_table_items()?;
let end = expect!(self, T::RBrace);
Ok(E::Table(args).span(tok.span + end.span))
}
T::Dollar => Ok(E::Ident(*SYM_DOLLAR_SIGN).span(tok.span)),
T::Do => {
let b = self.parse_block()?;
expect!(self, T::End);
Ok(b)
}
T::If => self.parse_if_stmt_chain(),
T::While => {
let cond = self.parse_expr()?;
expect!(self, T::Do);
let body = self.parse_block()?;
let end = expect!(self, T::End);
let span = cond.span + end.span;
Ok(E::While(b(cond), b(body)).span(span))
}
T::For => {
let var = expect!(self, T::Identifier);
expect!(self, T::In);
let iter = self.parse_expr()?;
expect!(self, T::Do);
let body = self.parse_block()?;
let end = expect!(self, T::End);
let span = var.span + end.span;
Ok(E::For(Symbol::get(var.content), b(iter), b(body)).span(span))
}
T::Try => {
let body = self.parse_block()?;
let (catch, span) = self.parse_catch_blocks()?;
Ok(E::Try(b(body), catch).span(tok.span + span))
}
T::Integer => {
let n = parse_int_literal(tok.content).span_err(tok.span)?;
Ok(E::Literal(n.into()).span(tok.span))
}
T::Float => {
let x = parse_float(tok.content).span_err(tok.span)?;
Ok(E::Literal(x.into()).span(tok.span))
}
T::Imaginary => {
let x = parse_float(&tok.content[..tok.content.len() - 1])
.span_err(tok.span)?;
Ok(E::Literal(Complex64::new(0.0, x).into()).span(tok.span))
}
T::String => {
let inner = &tok.content[1..tok.content.len() - 1];
let s = if &tok.content[..1] == "\"" {
parse_str_escapes(inner).span_err(tok.span)?
} else {
inner.into()
};
Ok(E::Literal(s.into()).span(tok.span))
}
T::Symbol => {
let inner = &tok.content[1..];
let s = match inner.chars().next() {
Some('\'') => Symbol::get(&inner[1..inner.len() - 1]),
Some('\"') => Symbol::get(
&parse_str_escapes(&inner[1..inner.len() - 1])
.span_err(tok.span)?,
),
_ => Symbol::get(inner),
};
Ok(E::Literal(s.into()).span(tok.span))
}
T::True => Ok(E::Literal(Value::Bool(true)).span(tok.span)),
T::False => Ok(E::Literal(Value::Bool(false)).span(tok.span)),
T::Nil => Ok(E::Literal(Value::Nil).span(tok.span)),
t => throw!(
tok.span,
"unexpected token {}, expected expression",
t.name()
),
}
}
fn parse_term(&mut self) -> Result<Expr> {
if let Some(tok) = try_next!(self, T::Identifier) {
Ok(E::Ident(Symbol::get(tok.content)).span(tok.span))
} else {
self.parse_term_not_ident()
}
}
fn parse_access(&mut self) -> Result<Expr> {
let mut lhs = self.parse_term()?;
loop {
let tok = try_next!(self, T::LParen | T::LBrack | T::Arrow | T::Dot);
match tok.map(|t| t.kind) {
Some(T::LParen) => {
let args = self.parse_expr_list()?;
let end = expect!(self, T::RParen);
let lhs_span = lhs.span;
lhs = E::FnCall(b(lhs), args).span(lhs_span + end.span);
}
Some(T::LBrack) => {
let idx = self.parse_expr()?;
let end = expect!(self, T::RBrack);
let lhs_span = lhs.span;
lhs = E::Index(b(lhs), b(idx)).span(lhs_span + end.span);
}
Some(T::Arrow) => {
let field = expect!(self, T::Identifier);
let symbol = Symbol::get(field.content);
expect!(self, T::LParen);
let args = self.parse_expr_list()?;
let end = expect!(self, T::RParen);
let lhs_span = lhs.span;
lhs = E::AssocFnCall(b(lhs), symbol, args).span(lhs_span + end.span);
}
Some(T::Dot) => {
let field = expect!(self, T::Identifier);
let symbol = Symbol::get(field.content);
let idx = E::Literal(symbol.into()).span(field.span);
let lhs_span = lhs.span;
lhs = E::Index(b(lhs), b(idx)).span(lhs_span + field.span);
}
None => break,
_ => unreachable!("parse_access: guaranteed by try_next!"),
}
}
Ok(lhs)
}
fn parse_precedence(&mut self, min_prec: u8) -> Result<Expr> {
let mut lhs = if let Some(op) = self.peek()?.kind.unary_op() {
let tok = self.next()?;
let rhs = self.parse_precedence(op.precedence())?;
let span = tok.span + rhs.span;
E::UnaryOp(op, b(rhs)).span(span)
} else {
self.parse_access()?
};
let mut span = lhs.span;
loop {
let tok = self.peek()?;
if let Some(op) = tok.kind.postfix_unary_op() {
if op.precedence() < min_prec {
break
}
self.next()?;
span += tok.span;
lhs = E::UnaryOp(op, b(lhs)).span(span);
continue
};
let Some(op) = tok.kind.binary_op() else {
break
};
let (lp, rp) = op.precedence();
if lp < min_prec {
break
}
self.next()?;
let rhs = self.parse_precedence(rp)?;
span += rhs.span;
lhs = E::BinaryOp(op, Box::new(lhs), Box::new(rhs)).span(span);
}
Ok(lhs)
}
fn parse_lambda(&mut self) -> Result<Expr> {
let tok = try_next!(self, T::Backslash | T::Colon);
match tok {
Some(Token {
kind: T::Backslash,
span,
..
}) => {
let args = self.parse_ident_list()?;
expect!(self, T::Arrow);
let body = self.parse_lambda()?;
let body_span = body.span;
Ok(E::Lambda(args, b(body)).span(span + body_span))
}
Some(Token {
kind: T::Colon,
span,
..
}) => {
let args = vec![*SYM_DOLLAR_SIGN];
let body = self.parse_lambda()?;
let body_span = body.span;
Ok(E::Lambda(args, b(body)).span(span + body_span))
}
None => self.parse_precedence(0),
_ => unreachable!("parse_lambda: guaranteed by try_next!"),
}
}
fn parse_pipeline(&mut self) -> Result<Expr> {
let mut lhs = self.parse_lambda()?;
let mut span = lhs.span;
while try_next!(self, T::Pipe).is_some() {
let rhs = self.parse_lambda()?;
span += rhs.span;
lhs = E::Pipe(b(lhs), b(rhs)).span(span);
}
Ok(lhs)
}
fn parse_not(&mut self) -> Result<Expr> {
if let Some(tok) = try_next!(self, T::Not) {
let expr = self.parse_not()?;
let span = tok.span + expr.span;
Ok(E::UnaryOp(UnaryOp::Not, b(expr)).span(span))
} else {
self.parse_pipeline()
}
}
fn parse_and(&mut self) -> Result<Expr> {
let mut lhs = self.parse_not()?;
let mut span = lhs.span;
while try_next!(self, T::And).is_some() {
let rhs = self.parse_not()?;
span += rhs.span;
lhs = E::And(b(lhs), b(rhs)).span(span);
}
Ok(lhs)
}
fn parse_or(&mut self) -> Result<Expr> {
let mut lhs = self.parse_and()?;
let mut span = lhs.span;
while try_next!(self, T::Or).is_some() {
let rhs = self.parse_and()?;
span += rhs.span;
lhs = E::Or(b(lhs), b(rhs)).span(span);
}
Ok(lhs)
}
fn parse_assign(&mut self) -> Result<Expr> {
let lhs = self.parse_or()?;
let lhs_span = lhs.span;
if let Some(op) = self.peek()?.kind.assign_op() {
let Some(lval) = LValue::from_expr(lhs) else {
throw!(lhs_span, "invalid lvalue for assingment")
};
self.next()?;
let rhs = self.parse_decl()?;
let rhs_span = rhs.span;
Ok(E::Assign(op, b(lval), b(rhs)).span(lhs_span + rhs_span))
} else {
Ok(lhs)
}
}
fn parse_var_decl(&mut self) -> Result<Expr> {
let first = expect!(self, T::Var | T::Global);
let name = expect!(self, T::Identifier);
expect!(self, T::Equal);
let val = self.parse_decl()?;
let val_span = val.span;
let kind = if first.kind == T::Global {
E::AssignGlobal
} else {
E::AssignVar
};
Ok(kind(Symbol::get(name.content), b(val)).span(first.span + val_span))
}
fn parse_fn_decl(&mut self) -> Result<Expr> {
let tok_fn = expect!(self, T::Fn);
let name = try_next!(self, T::Identifier).map(|t| Symbol::get(t.content));
expect!(self, T::LParen);
let args = self.parse_ident_list()?;
expect!(self, T::RParen);
match expect!(self, T::Do | T::Equal).kind {
T::Do => {
let content = self.parse_block()?;
let end = expect!(self, T::End);
Ok(E::FnDef(name, args, b(content)).span(tok_fn.span + end.span))
}
T::Equal => {
let content = self.parse_expr()?;
let span = tok_fn.span + content.span;
Ok(E::FnDef(name, args, b(content)).span(span))
}
_ => unreachable!("parse_fn_decl: guaranteed by try_next!"),
}
}
fn parse_decl(&mut self) -> Result<Expr> {
match self.peek()?.kind {
T::Var | T::Global => self.parse_var_decl(),
T::Fn => self.parse_fn_decl(),
_ => self.parse_assign(),
}
}
fn parse_expr(&mut self) -> Result<Expr> {
if let Some(tok) = try_next!(self, T::Return) {
let expr = self.parse_decl()?;
let span = expr.span;
Ok(E::Return(b(expr)).span(tok.span + span))
} else {
self.parse_decl()
}
}
fn parse_block(&mut self) -> Result<Expr> {
while try_next!(self, T::LineSeparator).is_some() {}
let mut span = self.peek()?.span;
let mut exprs = Vec::new();
while self.peek()?.kind.expr_first() {
let expr = self.parse_expr()?;
span += expr.span;
exprs.push(expr);
if try_next!(self, T::LineSeparator).is_none() {
break
}
while try_next!(self, T::LineSeparator).is_some() {}
}
Ok(E::Block(exprs).span(span))
}
fn parse(mut self) -> Result<Expr> {
let block = self.parse_block()?;
expect!(self, T::Eof);
Ok(block)
}
}
pub fn parse(src: &str) -> Result<Expr> {
Parser::new(src).parse()
}

109
talc-lang/src/parser/pos.rs Normal file
View file

@ -0,0 +1,109 @@
use std::fmt;
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
pub struct Pos {
pub idx: u32,
pub line: u32,
pub col: u32,
}
impl std::cmp::PartialOrd for Pos {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl std::cmp::Ord for Pos {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.idx.cmp(&other.idx)
}
}
impl Pos {
pub const fn new() -> Pos {
Pos {
idx: 0,
line: 1,
col: 1,
}
}
#[must_use]
pub fn advance(self, c: char) -> Pos {
let idx = self
.idx
.checked_add(c.len_utf8() as u32)
.expect("source file contains more than u32::MAX chars");
if c == '\n' {
Pos {
idx,
line: self.line + 1,
col: 1,
}
} else {
Pos {
idx,
line: self.line,
col: self.col + 1,
}
}
}
}
impl fmt::Display for Pos {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.line, self.col)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Span {
pub start: Pos,
pub end: Pos,
}
impl Span {
pub const fn new(start: Pos, end: Pos) -> Self {
if end.idx < start.idx {
Self {
start: end,
end: start,
}
} else {
Self { start, end }
}
}
pub fn of<'a>(&self, s: &'a str) -> &'a str {
&s[(self.start.idx as usize)..(self.end.idx as usize)]
}
}
impl std::ops::Add for Span {
type Output = Span;
fn add(self, rhs: Self) -> Self::Output {
let start = self.start.min(rhs.start);
let end = self.end.max(rhs.end);
Self::new(start, end)
}
}
impl std::ops::AddAssign for Span {
fn add_assign(&mut self, rhs: Self) {
self.start = self.start.min(rhs.start);
self.end = self.end.max(rhs.end);
}
}
impl From<(Pos, Pos)> for Span {
fn from((start, end): (Pos, Pos)) -> Self {
Self { start, end }
}
}
impl fmt::Display for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}-{}", self.start, self.end)
}
}

View file

@ -0,0 +1,171 @@
use core::fmt;
use std::num::{ParseFloatError, ParseIntError};
use thiserror::Error;
use crate::lstring::{LStr, LString};
use super::Span;
#[derive(Clone, Debug, Error)]
pub struct ParserError {
pub span: Span,
pub msg: String,
}
impl fmt::Display for ParserError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} | {}", self.span, self.msg)
}
}
pub trait SpanParserError {
type Output;
fn span_err(self, span: Span) -> Self::Output;
}
impl<T, E: std::error::Error> SpanParserError for Result<T, E> {
type Output = Result<T, ParserError>;
fn span_err(self, span: Span) -> Self::Output {
self.map_err(|e| ParserError {
span,
msg: e.to_string(),
})
}
}
#[derive(Clone, Copy, Debug, Error)]
pub enum StrEscapeError {
#[error("EOF in string escape")]
Eof,
#[error("EOF in string escape \\x")]
HexEof,
#[error("EOF in string escape \\u")]
UnicodeEof,
#[error("invalid string escape \\{0}")]
Invalid(char),
#[error("invalid hex digit '{0}' in string escape")]
InvalidHex(char),
#[error("missing brace after string escape \\u")]
MissingBrace,
#[error("invalid codepoint in string escape: {0:x}")]
InvalidCodepoint(u32),
#[error("codepoint in string escape too large")]
CodepointTooLarge,
}
pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
let mut s = LString::with_capacity(src.len());
let mut chars = src.chars();
while let Some(c) = chars.next() {
if c != '\\' {
s.push_char(c);
continue
}
let c = chars.next().ok_or(StrEscapeError::Eof)?;
match c {
'"' | '\'' | '\\' => s.push_char(c),
'0' => s.push_char('\0'),
'a' => s.push_char('\x07'),
'b' => s.push_char('\x08'),
't' => s.push_char('\t'),
'n' => s.push_char('\n'),
'v' => s.push_char('\x0b'),
'f' => s.push_char('\x0c'),
'r' => s.push_char('\r'),
'e' => s.push_char('\x1b'),
'x' => {
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
s.push_byte((n1 * 16 + n2) as u8);
}
'u' => {
let Some('{') = chars.next() else {
return Err(StrEscapeError::MissingBrace)
};
let mut n = 0_u32;
loop {
let Some(c) = chars.next() else {
return Err(StrEscapeError::UnicodeEof)
};
if c == '}' {
break
}
if n > 0x10ffff {
return Err(StrEscapeError::CodepointTooLarge)
}
n = n * 16 + c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
}
let ch = char::from_u32(n).ok_or(StrEscapeError::InvalidCodepoint(n))?;
s.push_char(ch);
}
c => return Err(StrEscapeError::Invalid(c)),
}
}
Ok(s)
}
pub fn parse_float<'a, S: Into<&'a LStr>>(f: S) -> Result<f64, ParseFloatError> {
let mut s = String::new();
for c in f.into().chars() {
if c != '_' {
s.push(c);
}
}
s.parse()
}
pub fn parse_int<'a, S: Into<&'a LStr>>(f: S, radix: u32) -> Result<i64, ParseIntError> {
let mut s = String::new();
for c in f.into().chars() {
if c != '_' {
s.push(c);
}
}
i64::from_str_radix(&s, radix)
}
pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result<i64, ParseIntError> {
let f = f.into();
match f.chars().nth(2) {
Some('x') => parse_int(&f[2..], 16),
Some('o') => parse_int(&f[2..], 8),
Some('s') => parse_int(&f[2..], 6),
Some('b') => parse_int(&f[2..], 2),
_ => parse_int(f, 10),
}
}
pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
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;
let mut c = char::from_digit(m as u32, radix).unwrap();
if upper {
c.make_ascii_uppercase();
}
result.push(c as u8);
if x == 0 {
break
}
}
result[begin..].reverse();
LString::from(result)
}

View file

@ -1,106 +0,0 @@
use std::num::{ParseIntError, ParseFloatError};
use thiserror::Error;
use crate::lstring::{LStr, LString};
#[derive(Clone, Debug, Error)]
pub enum ParseError {
#[error("{0}")]
StrEscape(#[from] StrEscapeError),
#[error("{0}")]
Integer(#[from] ParseIntError),
#[error("{0}")]
Float(#[from] ParseFloatError),
}
#[derive(Clone, Copy, Debug, Error)]
pub enum StrEscapeError {
#[error("EOF in string escape")]
Eof,
#[error("EOF in string escape \\x")]
HexEof,
#[error("EOF in string escape \\u")]
UnicodeEof,
#[error("Invalid string escape \\{0}")]
Invalid(char),
#[error("Invalid hex digit '{0}' in string escape")]
InvalidHex(char),
#[error("Missing brace after string escape \\u")]
MissingBrace,
#[error("Invalid codepoint in string escape: {0:x}")]
InvalidCodepoint(u32),
#[error("Codepoint in string escape too large")]
CodepointTooLarge,
}
pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
let mut s = LString::with_capacity(src.len());
let mut chars = src.chars();
while let Some(c) = chars.next() {
if c != '\\' { s.push_char(c); continue }
let c = chars.next().ok_or(StrEscapeError::Eof)?;
match c {
'"' | '\'' | '\\' => s.push_char(c),
'0' => s.push_char('\0'),
'a' => s.push_char('\x07'),
'b' => s.push_char('\x08'),
't' => s.push_char('\t'),
'n' => s.push_char('\n'),
'v' => s.push_char('\x0b'),
'f' => s.push_char('\x0c'),
'r' => s.push_char('\r'),
'e' => s.push_char('\x1b'),
'x' => {
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
s.push_byte((n1 * 16 + n2) as u8);
},
'u' => {
let Some('{') = chars.next() else {
return Err(StrEscapeError::MissingBrace)
};
let mut n = 0u32;
loop {
let Some(c) = chars.next() else {
return Err(StrEscapeError::UnicodeEof)
};
if c == '}' { break }
if n >= 0x1000_0000u32 {
return Err(StrEscapeError::CodepointTooLarge)
}
n = n * 16 + c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
}
let ch = char::from_u32(n).ok_or(StrEscapeError::InvalidCodepoint(n))?;
s.push_char(ch);
},
c => return Err(StrEscapeError::Invalid(c)),
}
}
Ok(s)
}
pub fn parse_float<'a, S: Into<&'a LStr>>(f: S) -> Result<f64, ParseFloatError> {
let mut s = String::new();
for c in f.into().chars() {
if c != '_' {
s.push(c);
}
}
s.parse()
}
pub fn parse_int<'a, S: Into<&'a LStr>>(f: S, radix: u32) -> Result<i64, ParseIntError> {
let mut s = String::new();
for c in f.into().chars() {
if c != '_' {
s.push(c);
}
}
i64::from_str_radix(&s, radix)
}

View file

@ -1,14 +1,19 @@
use std::{collections::HashMap, sync::{Mutex, OnceLock}}; use std::{
collections::HashMap,
sync::{Mutex, OnceLock},
};
use lazy_static::lazy_static; use lazy_static::lazy_static;
#[derive(Default)] #[derive(Default)]
struct SymbolTable { struct SymbolTable {
names: Vec<&'static LStr>, names: Vec<&'static LStr>,
values: HashMap<&'static LStr, Symbol> values: HashMap<&'static LStr, Symbol>,
} }
lazy_static! { lazy_static! {
pub static ref SYM_SELF: Symbol = symbol!(self);
pub static ref SYM_DOLLAR_SIGN: Symbol = symbol!("$");
pub static ref SYM_NIL: Symbol = symbol!(nil); pub static ref SYM_NIL: Symbol = symbol!(nil);
pub static ref SYM_BOOL: Symbol = symbol!(bool); pub static ref SYM_BOOL: Symbol = symbol!(bool);
pub static ref SYM_SYMBOL: Symbol = symbol!(symbol); pub static ref SYM_SYMBOL: Symbol = symbol!(symbol);
@ -23,14 +28,12 @@ lazy_static! {
pub static ref SYM_TABLE: Symbol = symbol!(table); pub static ref SYM_TABLE: Symbol = symbol!(table);
pub static ref SYM_FUNCTION: Symbol = symbol!(function); pub static ref SYM_FUNCTION: Symbol = symbol!(function);
pub static ref SYM_NATIVE_FUNC: Symbol = symbol!(native_func); pub static ref SYM_NATIVE_FUNC: Symbol = symbol!(native_func);
pub static ref SYM_END_ITERATION: Symbol = symbol!(end_iteration); pub static ref SYM_END_ITERATION: Symbol = symbol!(end_iteration);
pub static ref SYM_TYPE: Symbol = symbol!(type); pub static ref SYM_TYPE: Symbol = symbol!(type);
pub static ref SYM_MSG: Symbol = symbol!(msg); pub static ref SYM_MSG: Symbol = symbol!(msg);
pub static ref SYM_DATA: Symbol = symbol!(data); pub static ref SYM_DATA: Symbol = symbol!(data);
pub static ref SYM_TYPE_ERROR: Symbol = symbol!(type_error); pub static ref SYM_TYPE_ERROR: Symbol = symbol!(type_error);
pub static ref SYM_VALUE_ERROR: Symbol = symbol!(value_error);
pub static ref SYM_NAME_ERROR: Symbol = symbol!(name_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_INDEX_ERROR: Symbol = symbol!(index_error);
pub static ref SYM_HASH_ERROR: Symbol = symbol!(hash_error); pub static ref SYM_HASH_ERROR: Symbol = symbol!(hash_error);
@ -106,7 +109,11 @@ impl Symbol {
/// If the mutex storing the symbol table is poisoned /// If the mutex storing the symbol table is poisoned
pub fn name(self) -> &'static LStr { pub fn name(self) -> &'static LStr {
let table = get_table().lock().expect("couldn't lock symbol table"); let table = get_table().lock().expect("couldn't lock symbol table");
table.names.get(self.0 as usize).copied().expect("symbol does not exist") table
.names
.get(self.0 as usize)
.copied()
.expect("symbol does not exist")
} }
} }
@ -123,4 +130,3 @@ macro_rules! symbol {
pub use symbol; pub use symbol;
use crate::lstring::{LStr, LString}; use crate::lstring::{LStr, LString};

View file

@ -1,27 +1,35 @@
use std::rc::Rc; use std::{cell::RefCell, rc::Rc};
use crate::{chunk::Chunk, Vm, exception::Result}; use crate::{chunk::Chunk, exception::Result, symbol::Symbol, Vm};
use super::Value; use super::{CellValue, Value};
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct FuncAttrs { pub struct FuncAttrs {
pub arity: usize, pub arity: usize,
pub variadic: bool, pub name: Option<Symbol>,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Function { pub struct Function {
pub attrs: FuncAttrs, pub attrs: FuncAttrs,
pub chunk: Rc<Chunk>, pub chunk: Rc<Chunk>,
pub state: Box<[CellValue]>,
} }
impl Function { impl Function {
pub fn new(chunk: Rc<Chunk>, arity: usize) -> Self { pub fn new(chunk: Rc<Chunk>, attrs: FuncAttrs, closes: usize) -> Self {
Self { chunk, attrs: FuncAttrs { arity, variadic: false } } let v = Rc::new(RefCell::new(Value::Nil));
let state = std::iter::repeat(v).take(closes).collect();
Self::from_parts(chunk, attrs, state)
}
pub fn from_parts(chunk: Rc<Chunk>, attrs: FuncAttrs, state: Box<[CellValue]>) -> Self {
Self {
chunk,
attrs,
state,
} }
pub fn new_variadic(chunk: Rc<Chunk>, arity: usize) -> Self {
Self { chunk, attrs: FuncAttrs { arity, variadic: true } }
} }
} }
@ -33,11 +41,21 @@ pub struct NativeFunc {
} }
impl NativeFunc { impl NativeFunc {
pub fn new(func: FnNative, arity: usize) -> Self { pub fn new(func: FnNative, arity: usize, name: Symbol) -> Self {
Self { func, attrs: FuncAttrs { arity, variadic: false } } Self {
func,
attrs: FuncAttrs {
arity,
name: Some(name),
},
}
}
pub fn new_anon(func: FnNative, arity: usize) -> Self {
Self {
func,
attrs: FuncAttrs { arity, name: None },
} }
pub fn new_variadic(func: FnNative, arity: usize) -> Self {
Self { func, attrs: FuncAttrs { arity, variadic: true } }
} }
} }
@ -49,11 +67,11 @@ impl std::fmt::Debug for NativeFunc {
} }
} }
pub fn disasm_recursive(f: &Rc<Function>, w: &mut impl std::io::Write) -> std::io::Result<()> { pub fn disasm_recursive(
writeln!(w, "{} ({}{})", f: &Rc<Function>,
Value::Function(f.clone()), w: &mut impl std::io::Write,
f.attrs.arity, ) -> std::io::Result<()> {
if f.attrs.variadic { ".." } else { "" })?; writeln!(w, "{} ({})", Value::Function(f.clone()), f.attrs.arity)?;
if !f.chunk.consts.is_empty() { if !f.chunk.consts.is_empty() {
writeln!(w, "constants")?; writeln!(w, "constants")?;
for (i, c) in f.chunk.consts.iter().enumerate() { for (i, c) in f.chunk.consts.iter().enumerate() {
@ -74,7 +92,9 @@ pub fn disasm_recursive(f: &Rc<Function>, w: &mut impl std::io::Write) -> std::i
if let Some(types) = &catch.types { if let Some(types) = &catch.types {
write!(w, "{:04} [", catch.addr)?; write!(w, "{:04} [", catch.addr)?;
for (i, ty) in types.iter().enumerate() { for (i, ty) in types.iter().enumerate() {
if i != 0 { write!(w, ", ")?; } if i != 0 {
write!(w, ", ")?;
}
write!(w, "{}", ty.name())?; write!(w, "{}", ty.name())?;
} }
writeln!(w, "]")?; writeln!(w, "]")?;
@ -97,4 +117,3 @@ pub fn disasm_recursive(f: &Rc<Function>, w: &mut impl std::io::Write) -> std::i
} }
Ok(()) Ok(())
} }

View file

@ -1,6 +1,11 @@
use crate::{symbol::{SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR}, value::function::NativeFunc, vmcalliter, Vm, exception::{Result, throw}}; use crate::{
exception::{throw, Result},
symbol::{symbol, SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR},
value::function::NativeFunc,
vmcalliter, Vm,
};
use super::{Value, range::RangeType}; use super::{range::RangeType, Value};
impl Value { impl Value {
pub fn index(&self, idx: Self) -> Result<Self> { pub fn index(&self, idx: Self) -> Result<Self> {
@ -11,73 +16,96 @@ impl Value {
if i >= 0 && (i as usize) < l.len() { if i >= 0 && (i as usize) < l.len() {
Ok(l[i as usize].clone()) Ok(l[i as usize].clone())
} else { } else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len()) throw!(
*SYM_INDEX_ERROR,
"index {i} out of bounds for list of length {}",
l.len()
)
}
} }
},
(V::Range(r), V::Int(i)) => { (V::Range(r), V::Int(i)) => {
if i >= 0 && ( if i >= 0
r.ty == RangeType::Endless && (r.ty == RangeType::Endless
|| i < r.stop || i < r.stop || (r.ty == RangeType::Closed && i == r.stop))
|| (r.ty == RangeType::Closed && i == r.stop) {
) {
Ok((r.start + i).into()) Ok((r.start + i).into())
} else { } else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}") throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
} }
}, }
(V::Table(t), i) if i.hashable() => { (V::Table(t), i) if i.hashable() => {
let t = t.borrow(); let t = t.borrow();
let i = i.try_into()?; let i = i.try_into()?;
Ok(t.get(&i).cloned().unwrap_or(Value::Nil)) Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
}, }
(V::String(s), V::Range(r)) => { (V::String(s), V::Range(r)) => {
let slen = s.len(); let slen = s.len();
match r.ty { match r.ty {
RangeType::Open => { RangeType::Open => {
if r.start < 0 || r.start > slen as i64 { if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for string of length {}", r.stop, slen) *SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
} }
if r.stop < 0 || r.stop > slen as i64 { if r.stop < 0 || r.stop > slen as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for string of length {}", r.stop, slen) *SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
} }
Ok(s[r.start as usize..r.stop as usize].into()) Ok(s[r.start as usize..r.stop as usize].into())
}, }
RangeType::Closed => { RangeType::Closed => {
if r.start < 0 || r.start > slen as i64 { if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for string of length {}", r.stop, slen) *SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
} }
if r.stop < 0 || r.stop >= slen as i64 { if r.stop < 0 || r.stop >= slen as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for string of length {}", r.stop, slen) *SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
} }
Ok(s[r.start as usize..=r.stop as usize].into()) Ok(s[r.start as usize..=r.stop as usize].into())
}, }
RangeType::Endless => { RangeType::Endless => {
if r.start < 0 || r.start > slen as i64 { if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for string of length {}", r.stop, slen) *SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
} }
Ok(s[r.start as usize..].into()) Ok(s[r.start as usize..].into())
},
} }
}, }
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() { }
(col, idx) => {
if let Ok(ii) = idx.clone().to_iter_function() {
let col = col.clone(); let col = col.clone();
let func = move |vm: &mut Vm, _| { let func = move |vm: &mut Vm, _| match vmcalliter!(vm; ii.clone())? {
match vmcalliter!(vm; ii.clone())? {
Some(i) => col.index(i), Some(i) => col.index(i),
None => Ok(Value::from(*SYM_END_ITERATION)), None => Ok(Value::from(*SYM_END_ITERATION)),
}
}; };
Ok(NativeFunc::new(Box::new(func), 0).into()) Ok(NativeFunc::new(Box::new(func), 0, symbol!("index...")).into())
} else { } else {
throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}") throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
} }
} }
} }
}
pub fn store_index(&self, vm: &mut Vm, idx: Self, val: Self) -> Result<()> { pub fn store_index(&self, vm: &mut Vm, idx: Self, val: Self) -> Result<()> {
use Value as V; use Value as V;
@ -88,15 +116,19 @@ impl Value {
l[i as usize] = val; l[i as usize] = val;
Ok(()) Ok(())
} else { } else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len()) throw!(
*SYM_INDEX_ERROR,
"index {i} out of bounds for list of length {}",
l.len()
)
}
} }
},
(V::Table(t), i) if i.hashable() => { (V::Table(t), i) if i.hashable() => {
let mut t = t.borrow_mut(); let mut t = t.borrow_mut();
let i = i.try_into()?; let i = i.try_into()?;
t.insert(i, val); t.insert(i, val);
Ok(()) Ok(())
}, }
(V::List(t), V::Range(r)) => { (V::List(t), V::Range(r)) => {
let iter = val.to_iter_function()?; let iter = val.to_iter_function()?;
let mut vals = Vec::new(); let mut vals = Vec::new();
@ -107,53 +139,78 @@ impl Value {
match r.ty { match r.ty {
RangeType::Open => { RangeType::Open => {
if r.start < 0 || r.start > tm.len() as i64 { if r.start < 0 || r.start > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for list of length {}", r.stop, tm.len()) *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 { if r.stop < 0 || r.stop > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for list of length {}", r.stop, tm.len()) *SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
} }
let end = tm.split_off(r.stop as usize); let end = tm.split_off(r.stop as usize);
tm.truncate(r.start as usize); tm.truncate(r.start as usize);
tm.extend(vals.into_iter().chain(end)); tm.extend(vals.into_iter().chain(end));
}, }
RangeType::Closed => { RangeType::Closed => {
if r.start < 0 || r.start > tm.len() as i64 { if r.start < 0 || r.start > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for list of length {}", r.stop, tm.len()) *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 { if r.stop < 0 || r.stop >= tm.len() as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for list of length {}", r.stop, tm.len()) *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); let end = tm.split_off(r.stop as usize + 1);
tm.truncate(r.start as usize); tm.truncate(r.start as usize);
tm.extend(vals.into_iter().chain(end)); tm.extend(vals.into_iter().chain(end));
}, }
RangeType::Endless => { RangeType::Endless => {
if r.start < 0 || r.start > tm.len() as i64 { if r.start < 0 || r.start > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for list of length {}", r.stop, tm.len()) *SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
} }
tm.truncate(r.start as usize); tm.truncate(r.start as usize);
tm.extend(vals); tm.extend(vals);
}, }
} }
Ok(()) Ok(())
}, }
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() { (col, idx) => {
if let Ok(ii) = idx.clone().to_iter_function() {
let val = val.to_iter_function()?; let val = val.to_iter_function()?;
while let Some(i) = vmcalliter!(vm; ii.clone())? { while let Some(i) = vmcalliter!(vm; ii.clone())? {
let Some(v) = vmcalliter!(vm; val.clone())? else { let Some(v) = vmcalliter!(vm; val.clone())? else {
throw!(*SYM_INDEX_ERROR, "not enough values provided for store index") throw!(
*SYM_INDEX_ERROR,
"not enough values provided for store index"
)
}; };
col.store_index(vm, i, v)?; col.store_index(vm, i, v)?;
} }
Ok(()) Ok(())
} else { } else {
throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}") throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}")
}, }
}
} }
} }
} }

View file

@ -7,19 +7,23 @@ pub use num_complex::Complex64;
use num_complex::ComplexFloat; use num_complex::ComplexFloat;
pub use num_rational::Rational64; pub use num_rational::Rational64;
use crate::exception::{throw, Exception};
use crate::lstring::{LStr, LString}; use crate::lstring::{LStr, LString};
use crate::symbol::{Symbol, SYM_HASH_ERROR}; use crate::symbol::{Symbol, SYM_HASH_ERROR};
use crate::exception::{Exception, throw};
use self::{range::{Range, RangeType}, function::{Function, NativeFunc}}; use self::{
function::{Function, NativeFunc},
range::{Range, RangeType},
};
pub mod function; pub mod function;
pub mod index;
pub mod ops; pub mod ops;
pub mod range; pub mod range;
pub mod index;
type RcList = Rc<RefCell<Vec<Value>>>; type RcList = Rc<RefCell<Vec<Value>>>;
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>; type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
pub type CellValue = Rc<RefCell<Value>>;
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub enum Value { pub enum Value {
@ -48,7 +52,12 @@ pub trait NativeValue: std::fmt::Debug + Any {
fn get_type(&self) -> Symbol; fn get_type(&self) -> Symbol;
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
fn to_lstring(&self, w: &mut LString, _repr: bool) -> io::Result<()> { fn to_lstring(
&self,
w: &mut LString,
_repr: bool,
_recur: &mut Vec<*const ()>,
) -> io::Result<()> {
w.extend(b"<native value>"); w.extend(b"<native value>");
Ok(()) Ok(())
} }
@ -62,7 +71,11 @@ pub trait NativeValue: std::fmt::Debug + Any {
impl Display for Value { impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = if f.alternate() { Cow::Owned(self.repr()) } else { self.str() }; let s = if f.alternate() {
Cow::Owned(self.repr())
} else {
self.str()
};
write!(f, "{s}") write!(f, "{s}")
} }
} }
@ -80,7 +93,12 @@ impl Value {
table.into() table.into()
} }
pub fn write_to_lstring(&self, w: &mut LString, repr: bool) -> io::Result<()> { pub fn write_to_lstring(
&self,
w: &mut LString,
repr: bool,
recur: &mut Vec<*const ()>,
) -> io::Result<()> {
use std::io::Write; use std::io::Write;
match self { match self {
Self::Nil => write!(w, "nil"), Self::Nil => write!(w, "nil"),
@ -94,7 +112,7 @@ impl Value {
write!(w, "{name:?}")?; write!(w, "{name:?}")?;
} }
Ok(()) Ok(())
}, }
Self::Range(r) => match r.ty { Self::Range(r) => match r.ty {
RangeType::Open => write!(w, "{}..{}", r.start, r.stop), RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop), RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
@ -109,64 +127,122 @@ impl Value {
w.push_byte(b'+'); w.push_byte(b'+');
} }
write!(w, "{:?}i", z.im()) write!(w, "{:?}i", z.im())
}, }
Self::Cell(v) if repr => { Self::Cell(v) if repr => {
if recur.contains(&(v.as_ptr() as _)) {
return w.write_all(b"cell(...)")
}
w.write_all(b"cell(")?; w.write_all(b"cell(")?;
v.borrow().write_to_lstring(w, repr)?; recur.push(v.as_ptr() as _);
v.borrow().write_to_lstring(w, repr, recur)?;
recur.pop();
w.write_all(b")") w.write_all(b")")
}, }
Self::Cell(v) => v.borrow().write_to_lstring(w, false), Self::Cell(v) => {
if recur.contains(&(v.as_ptr() as _)) {
return w.write_all(b"cell(...)")
}
recur.push(v.as_ptr() as _);
v.borrow().write_to_lstring(w, true, recur)?;
recur.pop();
Ok(())
}
Self::String(s) if repr => write!(w, "{s:?}"), Self::String(s) if repr => write!(w, "{s:?}"),
Self::String(s) => w.write_all(s.as_bytes()), Self::String(s) => w.write_all(s.as_bytes()),
Self::List(l) => { Self::List(l) => {
if recur.contains(&(l.as_ptr() as _)) {
return w.write_all(b"[...]")
}
w.write_all(b"[")?; w.write_all(b"[")?;
recur.push(l.as_ptr() as _);
for (i, item) in l.borrow().iter().enumerate() { for (i, item) in l.borrow().iter().enumerate() {
if i != 0 { if i != 0 {
w.write_all(b", ")?; w.write_all(b", ")?;
} }
item.write_to_lstring(w, true)?; item.write_to_lstring(w, true, recur)?;
} }
recur.pop();
w.write_all(b"]") w.write_all(b"]")
}, }
Self::Table(t) => { Self::Table(t) => {
if recur.contains(&(t.as_ptr() as _)) {
return w.write_all(b"{...}")
}
w.write_all(b"{ ")?; w.write_all(b"{ ")?;
recur.push(t.as_ptr() as _);
for (i, (k, v)) in t.borrow().iter().enumerate() { for (i, (k, v)) in t.borrow().iter().enumerate() {
if i != 0 { if i != 0 {
w.write_all(b", ")?; w.write_all(b", ")?;
} }
k.0.write_table_key_repr(w)?; k.0.write_table_key_repr(w, recur)?;
w.write_all(b" = ")?; w.write_all(b" = ")?;
v.write_to_lstring(w, true)?; v.write_to_lstring(w, true, recur)?;
} }
recur.pop();
w.write_all(b" }") w.write_all(b" }")
}, }
Self::Function(g) Self::Function(g) => {
=> write!(w, "<function {:?}>", Rc::as_ptr(g)), if let Some(name) = g.attrs.name {
Self::NativeFunc(g) write!(
=> write!(w, "<native function {:?}>", Rc::as_ptr(g)), w,
Self::Native(n) => n.to_lstring(w, repr), "<function {}({}) @{:0>12x}>",
name.name(),
g.attrs.arity,
Rc::as_ptr(g) as usize
)
} else {
write!(
w,
"<anon function({}) @{:0>12x}>",
g.attrs.arity,
Rc::as_ptr(g) as usize
)
}
}
Self::NativeFunc(g) => {
if let Some(name) = g.attrs.name {
write!(
w,
"<native function {}({}) @{:0>12x}>",
name.name(),
g.attrs.arity,
Rc::as_ptr(g) as usize
)
} else {
write!(
w,
"<anon native function({}) @{:0>12x}>",
g.attrs.arity,
Rc::as_ptr(g) as usize
)
}
}
Self::Native(n) => n.to_lstring(w, repr, recur),
} }
} }
fn write_table_key_repr(&self, w: &mut LString) -> std::io::Result<()> { fn write_table_key_repr(
&self,
w: &mut LString,
recur: &mut Vec<*const ()>,
) -> std::io::Result<()> {
match self { match self {
Self::Nil | Self::Bool(_) Self::Nil | Self::Bool(_) | Self::Int(_) | Self::String(_) => {
| Self::Int(_) | Self::String(_) self.write_to_lstring(w, true, recur)
=> self.write_to_lstring(w, true), }
Self::Symbol(s) => { Self::Symbol(s) => {
let name = s.name(); let name = s.name();
if name.is_identifier() { if name.is_identifier() {
w.push_lstr(name); w.push_lstr(name);
Ok(()) Ok(())
} else { } else {
self.write_to_lstring(w, true) self.write_to_lstring(w, true, recur)
}
} }
},
_ => { _ => {
w.push_byte(b'('); w.push_byte(b'(');
self.write_to_lstring(w, true)?; self.write_to_lstring(w, true, recur)?;
w.push_byte(b')'); w.push_byte(b')');
Ok(()) Ok(())
} }
@ -178,14 +254,18 @@ impl Value {
Cow::Borrowed(s) Cow::Borrowed(s)
} else { } else {
let mut s = LString::new(); let mut s = LString::new();
self.write_to_lstring(&mut s, false).expect("write_to_lstring failed"); let mut recur = Vec::new();
self.write_to_lstring(&mut s, false, &mut recur)
.expect("write_to_lstring failed");
Cow::Owned(s) Cow::Owned(s)
} }
} }
pub fn repr(&self) -> LString { pub fn repr(&self) -> LString {
let mut s = LString::new(); let mut s = LString::new();
self.write_to_lstring(&mut s, true).expect("write_to_lstring failed"); let mut recur = Vec::new();
self.write_to_lstring(&mut s, true, &mut recur)
.expect("write_to_lstring failed");
s s
} }
@ -213,15 +293,19 @@ impl Value {
pub fn hashable(&self) -> bool { pub fn hashable(&self) -> bool {
matches!( matches!(
self, self,
Value::Nil | Value::Bool(_) | Value::Symbol(_) Value::Nil
| Value::Int(_) | Value::Ratio(_) | Value::String(_) | Value::Bool(_)
| Value::Symbol(_)
| Value::Int(_)
| Value::Ratio(_)
| Value::String(_)
) )
} }
pub fn downcast_native<T: NativeValue>(&self) -> Option<&T> { pub fn downcast_native<T: NativeValue>(&self) -> Option<&T> {
match self { match self {
Value::Native(n) => n.as_any().downcast_ref(), Value::Native(n) => n.as_any().downcast_ref(),
_ => None _ => None,
} }
} }
} }
@ -242,8 +326,12 @@ impl TryFrom<Value> for HashValue {
} }
impl HashValue { impl HashValue {
pub fn into_inner(self) -> Value { self.0 } pub fn into_inner(self) -> Value {
pub fn inner(&self) -> &Value { &self.0 } self.0
}
pub fn inner(&self) -> &Value {
&self.0
}
} }
impl Hash for HashValue { impl Hash for HashValue {
@ -264,39 +352,57 @@ impl Hash for HashValue {
macro_rules! impl_from { macro_rules! impl_from {
($ty:ty, $var:ident) => { ($ty:ty, $var:ident) => {
impl From<$ty> for Value { impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(value) } fn from(value: $ty) -> Self {
Self::$var(value)
}
} }
}; };
($ty:ty, $var:ident, hash) => { ($ty:ty, $var:ident, hash) => {
impl From<$ty> for Value { impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(value) } fn from(value: $ty) -> Self {
Self::$var(value)
}
} }
impl From<$ty> for HashValue { impl From<$ty> for HashValue {
fn from(value: $ty) -> Self { Self(Value::$var(value)) } fn from(value: $ty) -> Self {
Self(Value::$var(value))
}
} }
}; };
($ty:ty, $var:ident, rc) => { ($ty:ty, $var:ident, rc) => {
impl From<$ty> for Value { impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(Rc::new(value)) } fn from(value: $ty) -> Self {
Self::$var(Rc::new(value))
}
} }
impl From<Rc<$ty>> for Value { impl From<Rc<$ty>> for Value {
fn from(value: Rc<$ty>) -> Self { Self::$var(value) } fn from(value: Rc<$ty>) -> Self {
Self::$var(value)
}
} }
}; };
($ty:ty, $var:ident, rcref) => { ($ty:ty, $var:ident, rcref) => {
impl From<$ty> for Value { impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(Rc::new(RefCell::new(value))) } fn from(value: $ty) -> Self {
Self::$var(Rc::new(RefCell::new(value)))
}
} }
impl From<RefCell<$ty>> for Value { impl From<RefCell<$ty>> for Value {
fn from(value: RefCell<$ty>) -> Self { Self::$var(Rc::new(value)) } fn from(value: RefCell<$ty>) -> Self {
Self::$var(Rc::new(value))
}
} }
impl From<Rc<RefCell<$ty>>> for Value { impl From<Rc<RefCell<$ty>>> for Value {
fn from(value: Rc<RefCell<$ty>>) -> Self { Self::$var(value) } fn from(value: Rc<RefCell<$ty>>) -> Self {
Self::$var(value)
}
} }
}; };
($ty:ty, $var:ident, into) => { ($ty:ty, $var:ident, into) => {
impl From<$ty> for Value { impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(value.into()) } fn from(value: $ty) -> Self {
Self::$var(value.into())
}
} }
}; };
} }

View file

@ -1,11 +1,27 @@
use std::{cell::RefCell, cmp::Ordering, ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Shl, Shr, Sub}, rc::Rc}; use std::{
cell::RefCell,
cmp::Ordering,
ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Shl, Shr, Sub},
rc::Rc,
};
use num_complex::Complex64; use num_complex::{Complex64, ComplexFloat};
use num_rational::Rational64; use num_rational::Rational64;
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
use crate::{exception::{throw, Result}, lstring::LString, symbol::{SYM_END_ITERATION, SYM_TYPE_ERROR}, value::range::RangeType, Vm}; use crate::{
exception::{throw, Result},
lstring::LString,
symbol::{symbol, SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
value::range::RangeType,
Vm,
};
use super::{function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value}; use super::{
function::{FuncAttrs, NativeFunc},
range::Range,
HashValue, Value,
};
pub trait RatioExt { pub trait RatioExt {
fn to_f64(&self) -> f64; fn to_f64(&self) -> f64;
@ -31,14 +47,15 @@ impl Value {
Value::List(l) => l.borrow().len() > 0, Value::List(l) => l.borrow().len() > 0,
Value::Cell(v) => v.borrow().truthy(), Value::Cell(v) => v.borrow().truthy(),
Value::Symbol(_) | Value::Table(_) Value::Symbol(_)
| Value::Function(_) | Value::NativeFunc(_) | Value::Table(_)
| Value::Function(_)
| Value::NativeFunc(_)
| Value::Native(_) => true, | Value::Native(_) => true,
} }
} }
} }
#[allow(clippy::cast_precision_loss)]
pub fn promote(a: Value, b: Value) -> (Value, Value) { pub fn promote(a: Value, b: Value) -> (Value, Value) {
use Value as V; use Value as V;
match (&a, &b) { match (&a, &b) {
@ -58,145 +75,313 @@ pub fn promote(a: Value, b: Value) -> (Value, Value) {
} }
} }
////////////////////////
// unary arithmetic //
////////////////////////
impl Neg for Value { impl Neg for Value {
type Output = Result<Self>; type Output = Result<Self>;
fn neg(self) -> Self::Output { fn neg(self) -> Self::Output {
use Value as V; use Value as V;
match self { match self {
V::Int(x) => Ok(V::Int(-x)), V::Int(x) => {
V::Ratio(x) => Ok(V::Ratio(-x)), if let Some(x) = x.checked_neg() {
Ok(V::Int(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
}
}
V::Ratio(x) => {
if let Some(x) = Rational64::ZERO.checked_sub(&x) {
Ok(V::Ratio(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
}
}
V::Float(x) => Ok(V::Float(-x)), V::Float(x) => Ok(V::Float(-x)),
V::Complex(x) => Ok(V::Complex(-x)), V::Complex(x) => Ok(V::Complex(-x)),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}") a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
} }
} }
} }
impl Add<Value> for Value { impl Value {
type Output = Result<Self>; pub fn abs(self) -> Result<Self> {
fn add(self, rhs: Value) -> Self::Output {
use Value as V; use Value as V;
match promote(self, rhs) { match self {
(V::Int(x), V::Int(y)) => Ok(V::Int(x + y)), V::Int(x) => {
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x + y)), if let Some(x) = x.checked_abs() {
(V::Float(x), V::Float(y)) => Ok(V::Float(x + y)), Ok(V::Int(x))
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x + y)), } else {
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot add {l:#} and {r:#}") throw!(
*SYM_VALUE_ERROR,
"overflow when finding absolute value of {self}"
)
}
}
V::Ratio(x) => {
if let Some((x, _)) = ratio_checked_absign(&x) {
Ok(V::Ratio(x))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when finding absolute value of {self}"
)
}
}
V::Float(x) => Ok(V::Float(x.abs())),
V::Complex(x) => Ok(V::Float(x.norm())),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
} }
} }
} }
impl Sub<Value> for Value { /////////////////////////
// binary arithmetic //
/////////////////////////
macro_rules! impl_value_arith {
($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => {
impl $trait<Value> for Value {
type Output = Result<Self>; type Output = Result<Self>;
fn sub(self, rhs: Value) -> Self::Output { fn $name(self, rhs: Value) -> Self::Output {
use Value as V; use Value as V;
match promote(self, rhs) { let (a, b) = promote(self, rhs);
(V::Int(x), V::Int(y)) => Ok(V::Int(x - y)), match (&a, &b) {
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x - y)), (V::Int(x), V::Int(y)) => if let Some(v) = x.$checked(y) {
(V::Float(x), V::Float(y)) => Ok(V::Float(x - y)), Ok(V::Int(v))
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x - y)), } else {
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot subtract {l:#} and {r:#}") throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
},
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.$checked(y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
},
(V::Float(x), V::Float(y)) => Ok(V::Float(x $op y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x $op y)),
(l, r) => throw!(*SYM_TYPE_ERROR, concat!("cannot ", $verb, " {:#} and {:#}"), l, r)
} }
} }
} }
impl Mul<Value> for Value { };
type Output = Result<Self>;
fn mul(self, rhs: Value) -> Self::Output {
use Value as V;
match promote(self, rhs) {
(V::Int(x), V::Int(y)) => Ok(V::Int(x * y)),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x * y)),
(V::Float(x), V::Float(y)) => Ok(V::Float(x * y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x * y)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot multiply {l:#} and {r:#}")
}
}
} }
impl_value_arith!(Add, add, checked_add, +, "add");
impl_value_arith!(Sub, sub, checked_sub, -, "subtract");
impl_value_arith!(Mul, mul, checked_mul, *, "multiply");
impl Div<Value> for Value { impl Div<Value> for Value {
type Output = Result<Self>; type Output = Result<Self>;
fn div(self, rhs: Value) -> Self::Output { fn div(self, rhs: Value) -> Self::Output {
use Value as V; use Value as V;
match promote(self, rhs) { let (a, b) = promote(self, rhs);
(_, V::Int(0)) => throw!(*SYM_TYPE_ERROR, "integer division by 0"), match (&a, &b) {
(_, V::Ratio(r)) if *r.numer() == 0 && *r.denom() != 0 (V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer division by 0"),
=> throw!(*SYM_TYPE_ERROR, "integer division by 0"), (V::Int(x), V::Int(y)) => Ok(Value::Ratio((*x, *y).into())),
(V::Int(x), V::Int(y)) => Ok(V::Ratio(Rational64::new(x, y))), (V::Ratio(_), V::Ratio(r)) if r.is_zero() => {
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x / y)), throw!(*SYM_VALUE_ERROR, "rational division by 0")
}
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = x.checked_div(y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when dividing {a} and {b}")
}
}
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)), (V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)), (V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}") (l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}"),
} }
} }
} }
#[allow(clippy::cast_sign_loss)] ///////////////////////////////////
fn ipow(n: i64, p: u64) -> Result<i64> { // modulo and integer division //
match (n, p) { ///////////////////////////////////
(0, 0) => throw!(*SYM_TYPE_ERROR, "integer 0 raised to power 0"),
(0, _) => Ok(0), #[inline]
(_, 0) => Ok(1), fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> {
(n, p) if p > u32::MAX as u64 => { let a = if r.is_negative() {
let (lo, hi) = (p as u32, (p >> 32) as u32); Rational64::ZERO.checked_sub(r)?
let (a, b) = (n.pow(lo), n.pow(hi)); } else {
Ok(a * b.pow(0x1_0000).pow(0x1_0000)) *r
} };
(n, p) => Ok(n.pow(p as u32)), Some((a, r.signum()))
}
} }
#[allow(clippy::cast_sign_loss)] #[inline]
fn rpow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> { fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
Ok(match p { let (r2_abs, r2_sgn) = ratio_checked_absign(r2)?;
0.. => (ipow(n, p as u64)?, ipow(d, p as u64)?), Some(r1.checked_div(&r2_abs)?.floor() * r2_sgn)
_ => (ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?), }
})
#[inline]
fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
let q = ratio_checked_div_euclid(r1, r2)?;
r1.checked_sub(&r2.checked_mul(&q)?)
} }
impl Value { impl Value {
pub fn modulo(self, rhs: Value) -> Result<Self> { pub fn modulo(self, rhs: Value) -> Result<Self> {
use Value as V; use Value as V;
match promote(self, rhs) { let (a, b) = promote(self, rhs);
(V::Int(x), V::Int(y)) => Ok(V::Int(x.rem_euclid(y))), match (&a, &b) {
(V::Ratio(_x), V::Ratio(_y)) => todo!("ratio modulo"), (V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer modulo by 0"),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(y))), (V::Int(x), V::Int(y)) => {
(V::Complex(_x), V::Complex(_y)) => todo!("complex modulo"), if let Some(v) = x.checked_rem_euclid(*y) {
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}") Ok(V::Int(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
}
}
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "rational modulo by 0")
}
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = ratio_checked_rem_euclid(x, y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
}
}
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(*y))),
(V::Complex(x), V::Complex(y)) => {
let n = x / y;
let n = Complex64::new(n.re().floor(), n.im().floor());
Ok(Value::Complex(x - n * y))
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}"),
} }
} }
pub fn int_div(self, rhs: Value) -> Result<Self> { pub fn int_div(self, rhs: Value) -> Result<Self> {
use Value as V; use Value as V;
match promote(self, rhs) { let (a, b) = promote(self, rhs);
(V::Int(x), V::Int(y)) => Ok(V::Int(x.div_euclid(y))), match (&a, &b) {
(V::Ratio(_x), V::Ratio(_y)) => todo!("ratio integer division"), (V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer divsion by 0"),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(y))), (V::Int(x), V::Int(y)) => {
(V::Complex(_x), V::Complex(_y)) => todo!("complex integer division"), if let Some(v) = x.checked_div_euclid(*y) {
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}") Ok(V::Int(v))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when integer dividing {a} and {b}"
)
} }
} }
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "integer division by 0")
}
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = ratio_checked_div_euclid(x, y) {
Ok(V::Ratio(v))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when integer dividing {a} and {b}"
)
}
}
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(*y))),
(V::Complex(x), V::Complex(y)) => {
let n = x / y;
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}"),
}
}
}
//////////////////////
// exponentiation //
//////////////////////
#[inline]
fn ipow(n: i64, p: u64) -> Option<i64> {
match (n, p) {
(0, 0) => None,
(0, _) => Some(0),
(_, 0) => Some(1),
(1, _) => Some(1),
(-1, p) => (-1_i64).checked_pow((p % 2) as u32),
(_, p) if p > u32::MAX as u64 => None,
(n, p) => n.checked_pow(p as u32),
}
}
#[inline]
fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
match p {
i64::MIN => match (n, d) {
(0, _) => Some((0, 1)),
(1, 1) => Some((1, 1)),
(-1, 1) => Some((-1, 1)),
_ => None,
},
0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)),
_ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)),
}
}
impl Value {
pub fn pow(self, rhs: Value) -> Result<Self> { pub fn pow(self, rhs: Value) -> Result<Self> {
use Value as V; use Value as V;
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) { if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
return Ok(V::Ratio(rpow(*(*x).numer(), *(*x).denom(), *y)?.into())); if x.is_zero() && *y == 0 {
throw!(*SYM_VALUE_ERROR, "rational zero to integer zero power")
} }
match promote(self, rhs) { let Some(v) = rpow(*(*x).numer(), *(*x).denom(), *y) else {
(V::Int(x), V::Int(y)) if y >= 0 => Ok(V::Int(ipow(x, y as u64)?)), throw!(
(V::Int(x), V::Int(y)) => Ok(V::Ratio(rpow(x, 1, y)?.into())), *SYM_VALUE_ERROR,
(V::Float(x), V::Float(y)) "overflow when raising {self} to the power {rhs}"
=> Ok(V::Float(x.powf(y))), )
(V::Ratio(x), V::Ratio(y)) };
=> Ok(V::Float(x.to_f64().powf(y.to_f64()))), return Ok(V::Ratio(v.into()))
(V::Complex(x), V::Complex(y)) }
=> Ok(V::Complex(x.powc(y))), let (a, b) = promote(self, rhs);
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}") match (&a, &b) {
(V::Int(0), V::Int(0)) => {
throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power")
}
(V::Int(x), V::Int(y @ 0..)) => {
if let Some(v) = ipow(*x, *y as u64) {
Ok(V::Int(v))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when raising {a} to the power {b}"
)
}
}
(V::Int(x), V::Int(y)) => {
if let Some(v) = rpow(*x, 1, *y) {
Ok(V::Ratio(v.into()))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when raising {a} to the power {b}"
)
}
}
(V::Ratio(x), V::Ratio(y)) => Ok(V::Float(x.to_f64().powf(y.to_f64()))),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.powf(*y))),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x.powc(*y))),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}"),
} }
} }
} }
//////////////////////////
// Bitwise operations //
//////////////////////////
impl Shl<Value> for Value { impl Shl<Value> for Value {
type Output = Result<Value>; type Output = Result<Value>;
@ -204,7 +389,7 @@ impl Shl<Value> for Value {
use Value as V; use Value as V;
match (self, rhs) { match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int(((a as u64) << b as u64) as i64)), (V::Int(a), V::Int(b)) => Ok(Value::Int(((a as u64) << b as u64) as i64)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} left by {r:#}") (l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} left by {r:#}"),
} }
} }
} }
@ -216,7 +401,7 @@ impl Shr<Value> for Value {
use Value as V; use Value as V;
match (self, rhs) { match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int((a as u64 >> b as u64) as i64)), (V::Int(a), V::Int(b)) => Ok(Value::Int((a as u64 >> b as u64) as i64)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}") (l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
} }
} }
} }
@ -228,7 +413,7 @@ impl BitAnd<Value> for Value {
use Value as V; use Value as V;
match (self, rhs) { match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int(a & b)), (V::Int(a), V::Int(b)) => Ok(Value::Int(a & b)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}") (l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
} }
} }
} }
@ -240,7 +425,7 @@ impl BitXor<Value> for Value {
use Value as V; use Value as V;
match (self, rhs) { match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int(a ^ b)), (V::Int(a), V::Int(b)) => Ok(Value::Int(a ^ b)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}") (l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
} }
} }
} }
@ -252,16 +437,19 @@ impl BitOr<Value> for Value {
use Value as V; use Value as V;
match (self, rhs) { match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int(a | b)), (V::Int(a), V::Int(b)) => Ok(Value::Int(a | b)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}") (l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
} }
} }
} }
/////////////////////////////
// Equality and ordering //
/////////////////////////////
impl PartialEq for Value { impl PartialEq for Value {
#[allow(clippy::cast_precision_loss)]
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
use Value as V;
use super::range::RangeType as Rty; use super::range::RangeType as Rty;
use Value as V;
match (self, other) { match (self, other) {
(V::Nil, V::Nil) => true, (V::Nil, V::Nil) => true,
(V::Bool(a), V::Bool(b)) => a == b, (V::Bool(a), V::Bool(b)) => a == b,
@ -286,8 +474,9 @@ impl PartialEq for Value {
(V::Symbol(a), V::Symbol(b)) => a == b, (V::Symbol(a), V::Symbol(b)) => a == b,
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(), (V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) { (V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
(Rty::Open, Rty::Open) (Rty::Open, Rty::Open) | (Rty::Closed, Rty::Closed) => {
| (Rty::Closed, Rty::Closed) => a.start == b.start && a.stop == b.stop, a.start == b.start && a.stop == b.stop
}
(Rty::Open, Rty::Closed) => a.start == b.start && a.stop == b.stop - 1, (Rty::Open, Rty::Closed) => a.start == b.start && a.stop == b.stop - 1,
(Rty::Closed, Rty::Open) => a.start == b.start && a.stop - 1 == b.stop, (Rty::Closed, Rty::Open) => a.start == b.start && a.stop - 1 == b.stop,
(Rty::Endless, Rty::Endless) => a.start == b.start, (Rty::Endless, Rty::Endless) => a.start == b.start,
@ -301,7 +490,6 @@ impl PartialEq for Value {
} }
impl PartialOrd for Value { impl PartialOrd for Value {
#[allow(clippy::cast_precision_loss)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
use Value as V; use Value as V;
match (self, other) { match (self, other) {
@ -324,6 +512,10 @@ impl PartialOrd for Value {
} }
} }
////////////
// misc //
////////////
impl Value { impl Value {
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> { pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
match self.partial_cmp(other) { match self.partial_cmp(other) {
@ -339,7 +531,7 @@ impl Value {
let mut l = l1.borrow().clone(); let mut l = l1.borrow().clone();
l.extend_from_slice(&l2.borrow()); l.extend_from_slice(&l2.borrow());
Ok(l.into()) Ok(l.into())
}, }
(V::String(s1), V::String(s2)) => { (V::String(s1), V::String(s2)) => {
let mut s: LString = s1.as_ref().to_owned(); let mut s: LString = s1.as_ref().to_owned();
s.push_lstr(s2); s.push_lstr(s2);
@ -361,23 +553,40 @@ impl Value {
let mut l = list.borrow().clone(); let mut l = list.borrow().clone();
l.push(val); l.push(val);
Ok(l.into()) Ok(l.into())
}, }
lhs => throw!(*SYM_TYPE_ERROR, "cannot append to {lhs:#}"), lhs => throw!(*SYM_TYPE_ERROR, "cannot append to {lhs:#}"),
} }
} }
pub fn range(&self, other: &Self, closed: bool) -> Result<Self> { pub fn range(&self, other: &Self, closed: bool) -> Result<Self> {
if let (Value::Int(start), Value::Int(stop)) = (self, other) { if let (Value::Int(start), Value::Int(stop)) = (self, other) {
let ty = if closed { RangeType::Closed } else { RangeType::Open }; let ty = if closed {
Ok(Range { start: *start, stop: *stop, ty }.into()) RangeType::Closed
} else { } else {
throw!(*SYM_TYPE_ERROR, "cannot create range between {self:#} and {other:#}") RangeType::Open
};
Ok(Range {
start: *start,
stop: *stop,
ty,
}
.into())
} else {
throw!(
*SYM_TYPE_ERROR,
"cannot create range between {self:#} and {other:#}"
)
} }
} }
pub fn range_endless(&self) -> Result<Self> { pub fn range_endless(&self) -> Result<Self> {
if let Value::Int(start) = self { if let Value::Int(start) = self {
Ok(Range { start: *start, stop: 0, ty: RangeType::Endless }.into()) Ok(Range {
start: *start,
stop: 0,
ty: RangeType::Endless,
}
.into())
} else { } else {
throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}") throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}")
} }
@ -397,7 +606,7 @@ impl Value {
pub fn iter_pack(v: Option<Self>) -> Self { pub fn iter_pack(v: Option<Self>) -> Self {
match v { match v {
Some(v) => v, Some(v) => v,
None => Value::from(*SYM_END_ITERATION) None => Value::from(*SYM_END_ITERATION),
} }
} }
@ -407,11 +616,12 @@ impl Value {
Self::Range(range) => { Self::Range(range) => {
let range_iter = RefCell::new(range.into_iter()); let range_iter = RefCell::new(range.into_iter());
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> { let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
Ok(Value::iter_pack(range_iter.borrow_mut().next() Ok(Value::iter_pack(
.map(Value::from))) range_iter.borrow_mut().next().map(Value::from),
))
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[range]")).into())
}, }
Self::String(s) => { Self::String(s) => {
let byte_pos = RefCell::new(0); let byte_pos = RefCell::new(0);
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> { let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
@ -423,8 +633,8 @@ impl Value {
Ok(Value::from(*SYM_END_ITERATION)) Ok(Value::from(*SYM_END_ITERATION))
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[string]")).into())
}, }
Self::List(list) => { Self::List(list) => {
let idx = RefCell::new(0); let idx = RefCell::new(0);
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> { let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
@ -436,17 +646,18 @@ impl Value {
Ok(Value::from(*SYM_END_ITERATION)) Ok(Value::from(*SYM_END_ITERATION))
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[list]")).into())
}, }
Self::Table(table) => { Self::Table(table) => {
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect(); let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
let keys = RefCell::new(keys.into_iter()); let keys = RefCell::new(keys.into_iter());
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> { let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
Ok(Value::iter_pack(keys.borrow_mut().next() Ok(Value::iter_pack(
.map(HashValue::into_inner))) keys.borrow_mut().next().map(HashValue::into_inner),
))
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[table]")).into())
}, }
_ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"), _ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"),
} }
} }

View file

@ -1,7 +1,8 @@
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RangeType { pub enum RangeType {
Open, Closed, Endless, Open,
Closed,
Endless,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@ -40,5 +41,3 @@ impl IntoIterator for Range {
} }
} }
} }

View file

@ -1,8 +1,28 @@
use std::{cmp::Ordering, collections::HashMap, rc::Rc, sync::{atomic::AtomicBool, Arc}}; use std::{
cmp::Ordering,
collections::HashMap,
rc::Rc,
sync::{atomic::AtomicBool, Arc},
};
use crate::{ast::{BinaryOp, UnaryOp}, chunk::Instruction, exception::{throw, Exception, Result}, lstring::LStr, symbol::{Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR}, value::{function::{FuncAttrs, Function, NativeFunc}, Value}}; use crate::{
chunk::Instruction,
exception::{throw, Exception, Result},
lstring::LStr,
parser::ast::{BinaryOp, UnaryOp},
symbol::{
Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR,
},
value::{
function::{FuncAttrs, Function, NativeFunc},
Value,
},
};
struct TryFrame { idx: usize, stack_len: usize } struct TryFrame {
idx: usize,
stack_len: usize,
}
struct CallFrame { struct CallFrame {
func: Rc<Function>, func: Rc<Function>,
@ -72,44 +92,35 @@ enum CallOutcome {
Partial(Value), Partial(Value),
} }
fn get_call_outcome(mut args: Vec<Value>) -> Result<CallOutcome> { fn get_call_outcome(args: Vec<Value>) -> Result<CallOutcome> {
let f = &args[0]; let f = &args[0];
let Some(attrs) = f.func_attrs() else { let Some(attrs) = f.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "cannot call non-function {f:#}") throw!(*SYM_TYPE_ERROR, "cannot call non-function {f:#}")
}; };
let argc = args.len() - 1; let argc = args.len() - 1;
if attrs.variadic && argc >= attrs.arity { match argc.cmp(&attrs.arity) {
let vararg = args.split_off(attrs.arity + 1); Ordering::Equal => Ok(CallOutcome::Call(args)),
args.push(vararg.into()); Ordering::Greater => throw!(*SYM_TYPE_ERROR, "too many arguments for function"),
Ok(CallOutcome::Call(args)) Ordering::Less => {
} 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 remaining = attrs.arity - argc;
let f = f.clone(); let f = f.clone();
let nf = move |vm: &mut Vm, inner_args: Vec<Value>| { let nf = move |vm: &mut Vm, inner_args: Vec<Value>| {
let mut ia = inner_args.into_iter(); let mut ia = inner_args.into_iter();
ia.next(); ia.next();
let mut args: Vec<Value> = args.clone().into_iter().chain(ia).collect(); let 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")
};
let varargs = Rc::unwrap_or_clone(varargs).take();
args.extend(varargs);
}
vm.call_value(f.clone(), args) vm.call_value(f.clone(), args)
}; };
let nf = NativeFunc { let nf = NativeFunc {
attrs: FuncAttrs { arity: remaining, variadic: attrs.variadic }, attrs: FuncAttrs {
arity: remaining,
name: None,
},
func: Box::new(nf), func: Box::new(nf),
}; };
Ok(CallOutcome::Partial(nf.into())) Ok(CallOutcome::Partial(nf.into()))
} }
} }
}
impl Vm { impl Vm {
pub fn new(stack_max: usize) -> Self { pub fn new(stack_max: usize) -> Self {
@ -131,7 +142,9 @@ impl Vm {
} }
pub fn set_global_name<'a, S>(&mut self, name: S, val: Value) pub fn set_global_name<'a, S>(&mut self, name: S, val: Value)
where S: Into<&'a LStr> { where
S: Into<&'a LStr>,
{
self.globals.insert(Symbol::get(name.into()), val); self.globals.insert(Symbol::get(name.into()), val);
} }
@ -150,8 +163,8 @@ impl Vm {
CallOutcome::Call(args) => match value { CallOutcome::Call(args) => match value {
Value::Function(f) => self.run_function(f, args), Value::Function(f) => self.run_function(f, args),
Value::NativeFunc(f) => (f.func)(self, args), Value::NativeFunc(f) => (f.func)(self, args),
_ => unreachable!("already verified by calling get_call_type") _ => unreachable!("already verified by calling get_call_type"),
} },
} }
} }
@ -187,7 +200,9 @@ impl Vm {
while let Some(try_frame) = frame.try_frames.pop() { while let Some(try_frame) = frame.try_frames.pop() {
let table = &frame.func.chunk.try_tables[try_frame.idx]; let table = &frame.func.chunk.try_tables[try_frame.idx];
for catch in &table.catches { for catch in &table.catches {
if catch.types.is_none() || catch.types.as_ref().unwrap().contains(&exc.ty) { if catch.types.is_none()
|| catch.types.as_ref().unwrap().contains(&exc.ty)
{
frame.ip = catch.addr; frame.ip = catch.addr;
frame.locals.truncate(table.local_count); frame.locals.truncate(table.local_count);
self.stack.truncate(try_frame.stack_len); self.stack.truncate(try_frame.stack_len);
@ -221,30 +236,33 @@ impl Vm {
} }
fn check_interrupt(&mut self) -> Result<()> { fn check_interrupt(&mut self) -> Result<()> {
if self.interrupt.fetch_and(false, std::sync::atomic::Ordering::Relaxed) { if self
.interrupt
.fetch_and(false, std::sync::atomic::Ordering::Relaxed)
{
throw!(*SYM_INTERRUPTED) throw!(*SYM_INTERRUPTED)
} }
Ok(()) Ok(())
} }
fn run_instr(&mut self, frame: &mut CallFrame, instr: Instruction) -> Result<Option<Value>> { fn run_instr(
&mut self,
frame: &mut CallFrame,
instr: Instruction,
) -> Result<Option<Value>> {
use Instruction as I; use Instruction as I;
match instr { match instr {
// do nothing // do nothing
I::Nop => (), I::Nop => (),
// [] -> [locals[n]] // [] -> [locals[n]]
I::LoadLocal(n) I::LoadLocal(n) => self.push(frame.locals[usize::from(n)].clone()),
=> self.push(frame.locals[usize::from(n)].clone()),
// [x] -> [], locals[n] = x // [x] -> [], locals[n] = x
I::StoreLocal(n) I::StoreLocal(n) => frame.locals[usize::from(n)] = self.pop(),
=> frame.locals[usize::from(n)] = self.pop(),
// [x] -> [], locals.push(x) // [x] -> [], locals.push(x)
I::NewLocal I::NewLocal => frame.locals.push(self.pop()),
=> frame.locals.push(self.pop()),
// locals.pop_n(n) // locals.pop_n(n)
I::DropLocal(n) I::DropLocal(n) => frame.locals.truncate(frame.locals.len() - usize::from(n)),
=> frame.locals.truncate(frame.locals.len() - usize::from(n)),
// [] -> [globals[s]] // [] -> [globals[s]]
I::LoadGlobal(s) => { I::LoadGlobal(s) => {
let sym = unsafe { s.to_symbol_unchecked() }; let sym = unsafe { s.to_symbol_unchecked() };
@ -253,16 +271,67 @@ impl Vm {
None => throw!(*SYM_NAME_ERROR, "undefined global {}", sym.name()), None => throw!(*SYM_NAME_ERROR, "undefined global {}", sym.name()),
}; };
self.push(v); self.push(v);
}, }
// [x] -> [], globals[s] = x // [x] -> [], globals[s] = x
I::StoreGlobal(s) => { I::StoreGlobal(s) => {
let sym = unsafe { s.to_symbol_unchecked() }; let sym = unsafe { s.to_symbol_unchecked() };
let v = self.pop(); let v = self.pop();
self.globals.insert(sym, v); self.globals.insert(sym, v);
}, }
I::CloseOver(n) => {
let n = usize::from(n);
let v = std::mem::replace(&mut frame.locals[n], Value::Nil);
let v = v.to_cell();
frame.locals[n] = v.clone();
self.push(v);
}
I::Closure(n) => {
let f = frame.func.chunk.consts[usize::from(n)].clone();
let Value::Function(f) = f else {
panic!("attempt to build closure from non-closure constant")
};
let mut f = f.as_ref().clone();
let captured: Vec<_> = self
.pop_n(f.state.len())
.into_iter()
.map(|v| {
let Value::Cell(v) = v else {
panic!("attempt to build closure from non-cell local");
};
v
})
.collect();
f.state = captured.into_boxed_slice();
self.push(f.into());
}
I::LoadUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
self.push(v.borrow().clone());
}
I::StoreUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
*v.borrow_mut() = self.pop();
}
I::ContinueUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
self.push(Value::Cell(v));
}
I::LoadClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to load from closed non-cell local");
};
self.push(c.borrow().clone());
}
I::StoreClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to store to closed non-cell local");
};
*c.borrow_mut() = self.pop();
}
// [] -> [consts[n]] // [] -> [consts[n]]
I::Const(n) I::Const(n) => self.push(frame.func.chunk.consts[usize::from(n)].clone()),
=> self.push(frame.func.chunk.consts[usize::from(n)].clone()),
// [] -> [nil] // [] -> [nil]
I::Nil => self.push(Value::Nil), I::Nil => self.push(Value::Nil),
// [] -> [b] // [] -> [b]
@ -271,7 +340,7 @@ impl Vm {
I::Symbol(s) => { I::Symbol(s) => {
let sym = unsafe { Symbol::from_id_unchecked(u32::from(s)) }; let sym = unsafe { Symbol::from_id_unchecked(u32::from(s)) };
self.push(Value::Symbol(sym)); self.push(Value::Symbol(sym));
}, }
// [] -> [n] // [] -> [n]
I::Int(n) => self.push(Value::Int(i64::from(n))), I::Int(n) => self.push(Value::Int(i64::from(n))),
// [x] -> [x,x] // [x] -> [x,x]
@ -280,38 +349,44 @@ impl Vm {
I::DupTwo => { I::DupTwo => {
self.push(self.stack[self.stack.len() - 2].clone()); self.push(self.stack[self.stack.len() - 2].clone());
self.push(self.stack[self.stack.len() - 2].clone()); self.push(self.stack[self.stack.len() - 2].clone());
}, }
// [a0,a1...an] -> [] // [a0,a1...an] -> []
I::Drop(n) => for _ in 0..u32::from(n) { self.pop(); }, I::Drop(n) => {
for _ in 0..u32::from(n) {
self.pop();
}
}
// [x,y] -> [y,x] // [x,y] -> [y,x]
I::Swap => { I::Swap => {
let len = self.stack.len(); let len = self.stack.len();
self.stack.swap(len - 1, len - 2); self.stack.swap(len - 1, len - 2);
}, }
// [x,y] -> [y op x] // [x,y] -> [y op x]
I::BinaryOp(op) => { I::BinaryOp(op) => {
let b = self.pop(); let b = self.pop();
let a = self.pop(); let a = self.pop();
self.push(binary_op(op, a, b)?); self.push(binary_op(op, a, b)?);
}, }
// [x] -> [op x] // [x] -> [op x]
I::UnaryOp(op) => { I::UnaryOp(op) => {
let a = self.pop(); let a = self.pop();
self.push(unary_op(op, a)?); self.push(unary_op(op, a)?);
}, }
// [a0,a1...an] -.> [[a0,a1...an]] // [a0,a1...an] -.> [[a0,a1...an]]
I::NewList(n) => { I::NewList(n) => {
let list = self.pop_n(n as usize); let list = self.pop_n(n as usize);
self.push(list.into()); self.push(list.into());
}, }
// [l,a0,a1...an] -.> [l ++ [a0,a1...an]] // [l,a0,a1...an] -.> [l ++ [a0,a1...an]]
I::GrowList(n) => { I::GrowList(n) => {
let ext = self.pop_n(n as usize); let ext = self.pop_n(n as usize);
let list = self.pop(); let list = self.pop();
let Value::List(list) = list else { panic!("not a list") }; let Value::List(list) = list else {
panic!("not a list")
};
list.borrow_mut().extend(ext); list.borrow_mut().extend(ext);
self.push(Value::List(list)); self.push(Value::List(list));
}, }
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}] // [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
I::NewTable(n) => { I::NewTable(n) => {
let mut table = HashMap::new(); let mut table = HashMap::new();
@ -321,12 +396,14 @@ impl Vm {
table.insert(k.try_into()?, v); table.insert(k.try_into()?, v);
} }
self.push(table.into()); self.push(table.into());
}, }
// [t,k0,v0...kn,vn] -> [t ++ {k0=v0...kn=vn}] // [t,k0,v0...kn,vn] -> [t ++ {k0=v0...kn=vn}]
I::GrowTable(n) => { I::GrowTable(n) => {
let mut ext = self.pop_n(2 * n as usize); let mut ext = self.pop_n(2 * n as usize);
let table = self.pop(); let table = self.pop();
let Value::Table(table) = table else { panic!("not a table") }; let Value::Table(table) = table else {
panic!("not a table")
};
let mut table_ref = table.borrow_mut(); let mut table_ref = table.borrow_mut();
for _ in 0..n { for _ in 0..n {
// can't panic: pop_n checked that ext would have len 2*n // can't panic: pop_n checked that ext would have len 2*n
@ -336,13 +413,13 @@ impl Vm {
} }
drop(table_ref); drop(table_ref);
self.push(Value::Table(table)); self.push(Value::Table(table));
}, }
// [ct, idx] -> [ct!idx] // [ct, idx] -> [ct!idx]
I::Index => { I::Index => {
let idx = self.pop(); let idx = self.pop();
let ct = self.pop(); let ct = self.pop();
self.push(ct.index(idx)?); self.push(ct.index(idx)?);
}, }
// [ct, idx, v] -> [v], ct!idx = v // [ct, idx, v] -> [v], ct!idx = v
I::StoreIndex => { I::StoreIndex => {
let v = self.pop(); let v = self.pop();
@ -350,27 +427,31 @@ impl Vm {
let ct = self.pop(); let ct = self.pop();
ct.store_index(self, idx, v.clone())?; ct.store_index(self, idx, v.clone())?;
self.push(v); self.push(v);
}, }
// ip = n // ip = n
I::Jump(n) => { I::Jump(n) => {
self.check_interrupt()?; self.check_interrupt()?;
frame.ip = usize::from(n); frame.ip = usize::from(n);
}, }
// [v] ->, [], if v then ip = n // [v] ->, [], if v then ip = n
I::JumpTrue(n) => if self.pop().truthy() { I::JumpTrue(n) => {
if self.pop().truthy() {
self.check_interrupt()?; self.check_interrupt()?;
frame.ip = usize::from(n); frame.ip = usize::from(n);
}, }
}
// [v] ->, [], if not v then ip = n // [v] ->, [], if not v then ip = n
I::JumpFalse(n) => if !self.pop().truthy() { I::JumpFalse(n) => {
if !self.pop().truthy() {
self.check_interrupt()?; self.check_interrupt()?;
frame.ip = usize::from(n); frame.ip = usize::from(n);
}, }
}
// [v] -> [iter(v)] // [v] -> [iter(v)]
I::IterBegin => { I::IterBegin => {
let iter = self.pop().to_iter_function()?; let iter = self.pop().to_iter_function()?;
self.push(iter); self.push(iter);
}, }
// [i,cell(v)] -> [i,v] // [i,cell(v)] -> [i,v]
// [i,nil] -> [], ip = n // [i,nil] -> [], ip = n
I::IterTest(n) => { I::IterTest(n) => {
@ -380,19 +461,19 @@ impl Vm {
self.pop(); self.pop();
frame.ip = usize::from(n); frame.ip = usize::from(n);
} }
}, }
// try_frames.push(t, stack.len()) // try_frames.push(t, stack.len())
I::BeginTry(t) => { I::BeginTry(t) => {
let tryframe = TryFrame { let tryframe = TryFrame {
idx: usize::from(t), idx: usize::from(t),
stack_len: self.stack.len() stack_len: self.stack.len(),
}; };
frame.try_frames.push(tryframe); frame.try_frames.push(tryframe);
}, }
// try_frames.pop() // try_frames.pop()
I::EndTry => { I::EndTry => {
frame.try_frames.pop().expect("no try to pop"); frame.try_frames.pop().expect("no try to pop");
}, }
// [f,a0,a1...an] -> [f(a0,a1...an)] // [f,a0,a1...an] -> [f(a0,a1...an)]
I::Call(n) => { I::Call(n) => {
let n = usize::from(n); let n = usize::from(n);
@ -432,9 +513,8 @@ impl Vm {
// before propagating exceptions // before propagating exceptions
let res = res?; let res = res?;
self.stack.push(res); self.push(res);
} else if let Value::Function(func) = &args[0] { } else if let Value::Function(func) = &args[0] {
if self.call_stack.len() + 1 >= self.stack_max { if self.call_stack.len() + 1 >= self.stack_max {
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow") throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
} }
@ -445,22 +525,18 @@ impl Vm {
} else { } else {
unreachable!("already verified by calling get_call_type"); unreachable!("already verified by calling get_call_type");
} }
}, }
// [v] -> [], return v // [v] -> [], return v
I::Return if frame.root => { I::Return if frame.root => return Ok(Some(self.pop())),
return Ok(Some(self.pop()));
},
// [v] -> [], return v // [v] -> [], return v
I::Return => { I::Return => {
self.check_interrupt()?; self.check_interrupt()?;
*frame = self.call_stack.pop().expect("no root frame"); *frame = self.call_stack.pop().expect("no root frame");
}, }
} }
Ok(None) Ok(None)
} }
} }
#[macro_export] #[macro_export]

View file

@ -1,52 +1,8 @@
use proc_macro::TokenStream; use proc_macro::TokenStream;
use syn::{parse::Parse, parse_macro_input, ItemFn, LitInt, Token};
use quote::quote;
struct NativeFuncArgs { mod native_func;
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] #[proc_macro_attribute]
pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream { pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream {
let Ok(itemfn) = syn::parse::<ItemFn>(annotated_item.clone()) else { native_func::native_func(input, annotated_item)
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 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");
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)
} }

View file

@ -0,0 +1,66 @@
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse::Parse, parse_macro_input, Token};
struct NativeFuncArgs {
arity: syn::LitInt,
name: Option<syn::LitStr>,
}
impl Parse for NativeFuncArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let arity = input.parse()?;
let t: Option<Token![,]> = input.parse()?;
if t.is_some() {
let name = input.parse()?;
Ok(Self {
arity,
name: Some(name),
})
} else {
Ok(Self { arity, name: None })
}
}
}
pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream {
let Ok(itemfn) = syn::parse::<syn::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 arity = args.arity;
let talc_name = match args.name {
Some(n) => n.to_token_stream(),
None => name.to_token_stream(),
};
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 {
attrs: ::talc_lang::value::function::FuncAttrs{
arity: #arity,
name: Some(::talc_lang::symbol::symbol!(#talc_name))
},
func: Box::new(|#inputs| #output #block)
}
}
};
TokenStream::from(expanded)
}

View file

@ -6,8 +6,8 @@ edition = "2021"
[dependencies] [dependencies]
talc-lang = { path = "../talc-lang" } talc-lang = { path = "../talc-lang" }
talc-macros = { path = "../talc-macros" } talc-macros = { path = "../talc-macros" }
lazy_static = "1.4" lazy_static = "1.5"
regex = "1.10" regex = "1.11"
rand = { version = "0.8", optional = true } rand = { version = "0.8", optional = true }
[features] [features]

View file

@ -1,11 +1,16 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use talc_lang::{exception::{exception, Result}, symbol::SYM_TYPE_ERROR, throw, value::{function::NativeFunc, Value}, vmcall, Vm}; use talc_lang::{
exception::{exception, Result},
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{function::NativeFunc, Value},
vmcall, Vm,
};
use talc_macros::native_func; use talc_macros::native_func;
use crate::unpack_args; use crate::unpack_args;
pub fn load(vm: &mut Vm) { pub fn load(vm: &mut Vm) {
vm.set_global_name("push", push().into()); vm.set_global_name("push", push().into());
vm.set_global_name("pop", pop().into()); vm.set_global_name("pop", pop().into());
@ -14,6 +19,9 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("sort", sort().into()); vm.set_global_name("sort", sort().into());
vm.set_global_name("sort_by", sort_by().into()); vm.set_global_name("sort_by", sort_by().into());
vm.set_global_name("sort_key", sort_key().into()); vm.set_global_name("sort_key", sort_key().into());
vm.set_global_name("pair", pair().into());
vm.set_global_name("fst", fst().into());
vm.set_global_name("snd", snd().into());
} }
#[native_func(2)] #[native_func(2)]
@ -33,7 +41,7 @@ pub fn pop(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "pop expected list, found {list:#}") throw!(*SYM_TYPE_ERROR, "pop expected list, found {list:#}")
}; };
let v = list.borrow_mut().pop(); let v = list.borrow_mut().pop();
v.ok_or_else(|| exception!(*SYM_TYPE_ERROR, "attempt to pop empty list")) v.ok_or_else(|| exception!(*SYM_VALUE_ERROR, "attempt to pop empty list"))
} }
#[native_func(1)] #[native_func(1)]
@ -52,7 +60,10 @@ pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match &col { match &col {
Value::List(list) => list.borrow_mut().clear(), Value::List(list) => list.borrow_mut().clear(),
Value::Table(table) => table.borrow_mut().clear(), Value::Table(table) => table.borrow_mut().clear(),
_ => throw!(*SYM_TYPE_ERROR, "clear expected list or table, found {col:#}") _ => throw!(
*SYM_TYPE_ERROR,
"clear expected list or table, found {col:#}"
),
} }
Ok(col) Ok(col)
} }
@ -60,7 +71,10 @@ pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<i64> { fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<i64> {
let ord = vmcall!(vm; by, l, r)?; let ord = vmcall!(vm; by, l, r)?;
let Value::Int(ord) = ord else { let Value::Int(ord) = ord else {
throw!(*SYM_TYPE_ERROR, "comparison function should return an integer") throw!(
*SYM_TYPE_ERROR,
"comparison function should return an integer"
)
}; };
Ok(ord) Ok(ord)
} }
@ -79,14 +93,14 @@ fn partition(vals: &mut [Value]) -> (usize, usize) {
vals.swap(eq, lt); vals.swap(eq, lt);
lt += 1; lt += 1;
eq += 1; eq += 1;
}, }
Some(Ordering::Greater) => { Some(Ordering::Greater) => {
vals.swap(eq, gt); vals.swap(eq, gt);
gt -= 1; gt -= 1;
}, }
Some(Ordering::Equal) | None => { Some(Ordering::Equal) | None => {
eq += 1; eq += 1;
}, }
} }
} }
@ -107,14 +121,14 @@ fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, u
vals.swap(eq, lt); vals.swap(eq, lt);
lt += 1; lt += 1;
eq += 1; eq += 1;
}, }
1.. => { 1.. => {
vals.swap(eq, gt); vals.swap(eq, gt);
gt -= 1; gt -= 1;
}, }
0 => { 0 => {
eq += 1; eq += 1;
}, }
} }
} }
@ -135,9 +149,10 @@ fn insertion_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<()> {
for i in 0..vals.len() { for i in 0..vals.len() {
let mut j = i; let mut j = i;
while j > 0 { while j > 0 {
let ord = call_comparison(vm, by.clone(), vals[j-1].clone(), vals[j].clone())?; let ord =
call_comparison(vm, by.clone(), vals[j - 1].clone(), vals[j].clone())?;
if ord <= 0 { if ord <= 0 {
break; break
} }
vals.swap(j, j - 1); vals.swap(j, j - 1);
j -= 1; j -= 1;
@ -199,10 +214,45 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Some(Ordering::Greater) => Ok(Value::Int(1)), Some(Ordering::Greater) => Ok(Value::Int(1)),
Some(Ordering::Equal) => Ok(Value::Int(0)), Some(Ordering::Equal) => Ok(Value::Int(0)),
Some(Ordering::Less) => Ok(Value::Int(-1)), Some(Ordering::Less) => Ok(Value::Int(-1)),
None => throw!(*SYM_TYPE_ERROR, "values returned from sort key were incomparable"), None => throw!(
*SYM_VALUE_ERROR,
"values returned from sort key were incomparable"
),
} }
}; };
let nf = NativeFunc::new(Box::new(f), 2).into(); let nf = NativeFunc::new(Box::new(f), 2, symbol!("inner[sort_key]")).into();
sort_inner(&mut list.borrow_mut(), Some(&nf), vm)?; sort_inner(&mut list.borrow_mut(), Some(&nf), vm)?;
Ok(list.into()) Ok(list.into())
} }
#[native_func(2)]
pub fn pair(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
Ok(vec![x, y].into())
}
#[native_func(1)]
pub fn fst(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "fst: expected list")
};
let l = l.borrow();
let [x, _] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "fst: list must have length 2")
};
Ok(x.clone())
}
#[native_func(1)]
pub fn snd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "snd: expected list")
};
let l = l.borrow();
let [_, y] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "snd: list must have length 2")
};
Ok(y.clone())
}

View file

@ -1,26 +1,40 @@
use talc_lang::{exception::{throw, Exception, Result}, symbol::SYM_TYPE_ERROR, value::Value, Vm}; use talc_lang::{
exception::{throw, Exception, Result},
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
value::Value,
Vm,
};
use talc_macros::native_func; use talc_macros::native_func;
use crate::{unpack_args, unpack_varargs}; use crate::unpack_args;
#[native_func(1..)] #[native_func(1)]
pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, ty], varargs) = unpack_varargs!(args); let [_, arg] = unpack_args!(args);
let Value::Symbol(ty) = ty else { let exc = match arg {
throw!(*SYM_TYPE_ERROR, "exception type must be a symbol") Value::Symbol(ty) => Exception::new(ty),
Value::List(l) => match l.borrow().as_slice() {
[Value::Symbol(ty)] | [Value::Symbol(ty), Value::Nil] => Exception::new(*ty),
[Value::Symbol(ty), Value::Nil, data] => {
Exception::new_with_data(*ty, data.clone())
}
[Value::Symbol(ty), Value::String(s)] => {
Exception::new_with_msg(*ty, s.clone())
}
[Value::Symbol(ty), Value::String(s), data] => {
Exception::new_with_msg_data(*ty, s.clone(), data.clone())
}
[] | [_] | [_, _] | [_, _, _] => {
throw!(*SYM_TYPE_ERROR, "wrong argument for throw")
}
[_, _, _, _, ..] => throw!(
*SYM_VALUE_ERROR,
"too many elements in list argument for throw"
),
},
_ => throw!(*SYM_TYPE_ERROR, "throw expected symbol or list"),
}; };
Err(match &*varargs { Err(exc)
[] | [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)] #[native_func(1)]
@ -30,6 +44,7 @@ pub fn rethrow(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
if let Some(e) = Exception::from_table(&table) { if let Some(e) = Exception::from_table(&table) {
return Err(e) return Err(e)
} }
throw!(*SYM_VALUE_ERROR, "argument not a valid exception")
} }
throw!(*SYM_TYPE_ERROR, "argument not a valid exception") throw!(*SYM_TYPE_ERROR, "argument not a valid exception")
} }

View file

@ -1,8 +1,24 @@
use std::{cell::RefCell, collections::HashMap, fs::{File, OpenOptions}, io::{BufRead, BufReader, BufWriter, ErrorKind, IntoInnerError, Read, Write}, net::{TcpListener, TcpStream, ToSocketAddrs}, os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd}, process::{Child, Command, Stdio}, time::Duration}; use std::{
cell::RefCell,
collections::HashMap,
fs::{File, OpenOptions},
io::{BufRead, BufReader, BufWriter, ErrorKind, IntoInnerError, Read, Write},
net::{TcpListener, TcpStream, ToSocketAddrs},
os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd},
process::{Child, Command, Stdio},
time::Duration,
};
use talc_lang::{exception::Result, lstring::LString, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{function::NativeFunc, HashValue, NativeValue, Value}, Vm};
use talc_macros::native_func;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use talc_lang::{
exception::Result,
lstring::LString,
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{function::NativeFunc, HashValue, NativeValue, Value},
Vm,
};
use talc_macros::native_func;
use crate::{unpack_args, SYM_IO_ERROR}; use crate::{unpack_args, SYM_IO_ERROR};
@ -10,7 +26,6 @@ type OpenOptFn = for<'a> fn(&'a mut OpenOptions, bool) -> &'a mut OpenOptions;
lazy_static! { lazy_static! {
static ref SYM_STD_FILE: Symbol = Symbol::get("std.file"); static ref SYM_STD_FILE: Symbol = Symbol::get("std.file");
static ref SYM_STD_PROCESS: Symbol = Symbol::get("std.process"); static ref SYM_STD_PROCESS: Symbol = Symbol::get("std.process");
static ref SYM_R: Symbol = Symbol::get("r"); static ref SYM_R: Symbol = Symbol::get("r");
static ref SYM_W: Symbol = Symbol::get("w"); static ref SYM_W: Symbol = Symbol::get("w");
static ref SYM_A: Symbol = Symbol::get("a"); static ref SYM_A: Symbol = Symbol::get("a");
@ -18,7 +33,6 @@ lazy_static! {
static ref SYM_C: Symbol = Symbol::get("c"); static ref SYM_C: Symbol = Symbol::get("c");
static ref SYM_N: Symbol = Symbol::get("n"); static ref SYM_N: Symbol = Symbol::get("n");
static ref SYM_U: Symbol = Symbol::get("u"); static ref SYM_U: Symbol = Symbol::get("u");
static ref OPEN_OPT_MAP: HashMap<Symbol, OpenOptFn> = { static ref OPEN_OPT_MAP: HashMap<Symbol, OpenOptFn> = {
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert(*SYM_R, OpenOptions::read as OpenOptFn); map.insert(*SYM_R, OpenOptions::read as OpenOptFn);
@ -29,21 +43,17 @@ lazy_static! {
map.insert(*SYM_N, OpenOptions::create_new); map.insert(*SYM_N, OpenOptions::create_new);
map map
}; };
static ref SYM_STDIN: Symbol = Symbol::get("stdin"); static ref SYM_STDIN: Symbol = Symbol::get("stdin");
static ref SYM_STDOUT: Symbol = Symbol::get("stdout"); static ref SYM_STDOUT: Symbol = Symbol::get("stdout");
static ref SYM_STDERR: Symbol = Symbol::get("stderr"); static ref SYM_STDERR: Symbol = Symbol::get("stderr");
static ref SYM_CD: Symbol = Symbol::get("cd"); static ref SYM_CD: Symbol = Symbol::get("cd");
static ref SYM_DELENV: Symbol = Symbol::get("delenv"); static ref SYM_DELENV: Symbol = Symbol::get("delenv");
static ref SYM_ENV: Symbol = Symbol::get("env"); static ref SYM_ENV: Symbol = Symbol::get("env");
static ref SYM_PROCESS: Symbol = Symbol::get("process"); static ref SYM_PROCESS: Symbol = Symbol::get("process");
static ref SYM_INHERIT: Symbol = Symbol::get("inherit"); static ref SYM_INHERIT: Symbol = Symbol::get("inherit");
static ref SYM_PIPED: Symbol = Symbol::get("piped"); static ref SYM_PIPED: Symbol = Symbol::get("piped");
static ref SYM_NULL: Symbol = Symbol::get("null"); static ref SYM_NULL: Symbol = Symbol::get("null");
static ref SYM_ALL: Symbol = Symbol::get("all"); static ref SYM_ALL: Symbol = Symbol::get("all");
} }
thread_local! { thread_local! {
@ -69,15 +79,23 @@ thread_local! {
#[derive(Debug)] #[derive(Debug)]
enum BufFile { enum BufFile {
Buffered { r: BufReader<File>, w: BufWriter<File> }, Buffered {
Unbuffered { f: File }, r: BufReader<File>,
w: BufWriter<File>,
},
Unbuffered {
f: File,
},
} }
impl BufFile { impl BufFile {
fn new(file: File, buffered: bool) -> std::io::Result<Self> { fn new(file: File, buffered: bool) -> std::io::Result<Self> {
if buffered { if buffered {
let file2 = file.try_clone()?; let file2 = file.try_clone()?;
Ok(Self::Buffered { r: BufReader::new(file), w: BufWriter::new(file2) }) Ok(Self::Buffered {
r: BufReader::new(file),
w: BufWriter::new(file2),
})
} else { } else {
Ok(Self::Unbuffered { f: file }) Ok(Self::Unbuffered { f: file })
} }
@ -92,15 +110,17 @@ impl BufFile {
Self::Buffered { r, .. } => { Self::Buffered { r, .. } => {
let file = r.get_ref().try_clone()?; let file = r.get_ref().try_clone()?;
Self::new(file, true) Self::new(file, true)
}, }
Self::Unbuffered { f } => Ok(Self::Unbuffered { f: f.try_clone()? }) Self::Unbuffered { f } => Ok(Self::Unbuffered { f: f.try_clone()? }),
} }
} }
fn try_into_raw_fd(self) -> std::result::Result<RawFd, IntoInnerError<BufWriter<File>>> { fn try_into_raw_fd(
self,
) -> std::result::Result<RawFd, IntoInnerError<BufWriter<File>>> {
match self { match self {
BufFile::Buffered { r, w } => { BufFile::Buffered { r, w } => {
w.into_inner()?.into_raw_fd(); let _ = w.into_inner()?.into_raw_fd();
Ok(r.into_inner().into_raw_fd()) Ok(r.into_inner().into_raw_fd())
} }
BufFile::Unbuffered { f } => Ok(f.into_raw_fd()), BufFile::Unbuffered { f } => Ok(f.into_raw_fd()),
@ -152,9 +172,18 @@ impl From<BufFile> for ValueFile {
} }
impl NativeValue for ValueFile { impl NativeValue for ValueFile {
fn get_type(&self) -> Symbol { *SYM_STD_FILE } fn get_type(&self) -> Symbol {
fn as_any(&self) -> &dyn std::any::Any { self } *SYM_STD_FILE
fn to_lstring(&self, w: &mut LString, _repr: bool) -> std::io::Result<()> { }
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn to_lstring(
&self,
w: &mut LString,
_repr: bool,
_recur: &mut Vec<*const ()>,
) -> std::io::Result<()> {
w.push_str("<file>"); w.push_str("<file>");
Ok(()) Ok(())
} }
@ -170,9 +199,18 @@ impl From<Child> for ValueProcess {
} }
impl NativeValue for ValueProcess { impl NativeValue for ValueProcess {
fn get_type(&self) -> Symbol { *SYM_STD_PROCESS } fn get_type(&self) -> Symbol {
fn as_any(&self) -> &dyn std::any::Any { self } *SYM_STD_PROCESS
fn to_lstring(&self, w: &mut LString, _repr: bool) -> std::io::Result<()> { }
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn to_lstring(
&self,
w: &mut LString,
_repr: bool,
_recur: &mut Vec<*const ()>,
) -> std::io::Result<()> {
let id = self.0.borrow().id(); let id = self.0.borrow().id();
write!(w, "<process {id}>") write!(w, "<process {id}>")
} }
@ -201,7 +239,6 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("process_id", process_id().into()); vm.set_global_name("process_id", process_id().into());
} }
#[native_func(2)] #[native_func(2)]
pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, path, opts] = unpack_args!(args); let [_, path, opts] = unpack_args!(args);
@ -218,11 +255,12 @@ pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} else { } else {
match OPEN_OPT_MAP.get(&s) { match OPEN_OPT_MAP.get(&s) {
Some(f) => f(&mut oo, true), Some(f) => f(&mut oo, true),
None => throw!(*SYM_TYPE_ERROR, "invalid option for open") None => throw!(*SYM_VALUE_ERROR, "invalid option for open"),
}; };
} }
}, }
Value::List(l) => for s in l.borrow().iter() { Value::List(l) => {
for s in l.borrow().iter() {
let Value::Symbol(s) = s else { let Value::Symbol(s) = s else {
throw!(*SYM_TYPE_ERROR, "invalid option for open") throw!(*SYM_TYPE_ERROR, "invalid option for open")
}; };
@ -231,20 +269,21 @@ pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} else { } else {
match OPEN_OPT_MAP.get(s) { match OPEN_OPT_MAP.get(s) {
Some(f) => f(&mut oo, true), Some(f) => f(&mut oo, true),
None => throw!(*SYM_TYPE_ERROR, "invalid option for open") None => throw!(*SYM_VALUE_ERROR, "invalid option for open"),
}; };
} }
}, }
}
Value::Nil => { Value::Nil => {
oo.read(true).write(true); oo.read(true).write(true);
}, }
_ => throw!(*SYM_TYPE_ERROR, "invalid option for open") _ => throw!(*SYM_TYPE_ERROR, "invalid option for open"),
} }
match oo.open(path.to_os_str()) { match oo.open(path.to_os_str()) {
Ok(f) => match BufFile::new(f, buffered) { Ok(f) => match BufFile::new(f, buffered) {
Ok(bf) => Ok(ValueFile::from(bf).into()), Ok(bf) => Ok(ValueFile::from(bf).into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"), Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} },
Err(e) => throw!(*SYM_IO_ERROR, "{e}"), Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} }
} }
@ -256,7 +295,10 @@ pub fn read(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "read expected integer, got {nbytes:#}") throw!(*SYM_TYPE_ERROR, "read expected integer, got {nbytes:#}")
}; };
let Ok(nbytes) = usize::try_from(nbytes) else { let Ok(nbytes) = usize::try_from(nbytes) else {
throw!(*SYM_TYPE_ERROR, "number of bytes to read must be nonnegative") throw!(
*SYM_VALUE_ERROR,
"number of bytes to read must be nonnegative"
)
}; };
let Some(file): Option<&ValueFile> = file.downcast_native() else { let Some(file): Option<&ValueFile> = file.downcast_native() else {
throw!(*SYM_TYPE_ERROR, "read expected file, got {file:#}") throw!(*SYM_TYPE_ERROR, "read expected file, got {file:#}")
@ -310,14 +352,14 @@ pub fn read_until(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "read_until expected string, got {end:#}") throw!(*SYM_TYPE_ERROR, "read_until expected string, got {end:#}")
}; };
if end.is_empty() { if end.is_empty() {
throw!(*SYM_TYPE_ERROR, "read_until: end string must not be empty") throw!(*SYM_VALUE_ERROR, "read_until: end string must not be empty")
} }
let mut file = file.0.borrow_mut(); let mut file = file.0.borrow_mut();
if let BufFile::Buffered { r, .. } = &mut *file { if let BufFile::Buffered { r, .. } = &mut *file {
match read_until_impl(r, end.as_bytes()) { match read_until_impl(r, end.as_bytes()) {
Ok(s) if s.is_empty() => Ok(Value::Nil), Ok(s) if s.is_empty() => Ok(Value::Nil),
Ok(s) => Ok(s.into()), Ok(s) => Ok(s.into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}") Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} }
} else { } else {
throw!(*SYM_TYPE_ERROR, "read_until: file must be buffered") throw!(*SYM_TYPE_ERROR, "read_until: file must be buffered")
@ -336,10 +378,10 @@ pub fn read_line(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match r.read_until(b'\n', &mut buf) { match r.read_until(b'\n', &mut buf) {
Ok(0) => Ok(Value::Nil), Ok(0) => Ok(Value::Nil),
Ok(_) => Ok(LString::from(buf).into()), Ok(_) => Ok(LString::from(buf).into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}") Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} }
} else { } else {
throw!(*SYM_TYPE_ERROR, "read_line: file must be buffered") throw!(*SYM_VALUE_ERROR, "read_line: file must be buffered")
} }
} }
@ -398,13 +440,13 @@ pub fn tcp_connect(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "tcp_connect expected string, got {addr:#}") throw!(*SYM_TYPE_ERROR, "tcp_connect expected string, got {addr:#}")
}; };
let Ok(addr) = addr.to_str() else { let Ok(addr) = addr.to_str() else {
throw!(*SYM_TYPE_ERROR, "address must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
}; };
match TcpStream::connect(addr) { match TcpStream::connect(addr) {
Ok(stream) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) { Ok(stream) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
Ok(bf) => Ok(ValueFile::from(bf).into()), Ok(bf) => Ok(ValueFile::from(bf).into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"), Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} },
Err(e) => throw!(*SYM_IO_ERROR, "{e}"), Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} }
} }
@ -413,16 +455,26 @@ pub fn tcp_connect(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn tcp_connect_timeout(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn tcp_connect_timeout(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, addr, timeout] = unpack_args!(args); let [_, addr, timeout] = unpack_args!(args);
let Value::String(addr) = addr else { let Value::String(addr) = addr else {
throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout expected string, got {addr:#}") throw!(
*SYM_TYPE_ERROR,
"tcp_connect_timeout expected string, got {addr:#}"
)
}; };
let Ok(addr) = addr.to_str() else { let Ok(addr) = addr.to_str() else {
throw!(*SYM_TYPE_ERROR, "address must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
}; };
let timeout = match timeout { let timeout = match timeout {
Value::Int(n) if n >= 0 => Duration::from_secs(n as u64), Value::Int(n) if n >= 0 => Duration::from_secs(n as u64),
Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => Duration::from_secs_f64(n), Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => {
Value::Int(_) | Value::Float(_) => throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout: invalid timeout"), Duration::from_secs_f64(n)
_ => throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout expected int or float, got {timeout:#}") }
Value::Int(_) | Value::Float(_) => {
throw!(*SYM_VALUE_ERROR, "tcp_connect_timeout: invalid timeout")
}
_ => throw!(
*SYM_TYPE_ERROR,
"tcp_connect_timeout expected int or float, got {timeout:#}"
),
}; };
let mut addrs = match addr.to_socket_addrs() { let mut addrs = match addr.to_socket_addrs() {
Ok(addrs) => addrs, Ok(addrs) => addrs,
@ -435,26 +487,25 @@ pub fn tcp_connect_timeout(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(stream) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) { Ok(stream) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
Ok(bf) => Ok(ValueFile::from(bf).into()), Ok(bf) => Ok(ValueFile::from(bf).into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"), Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} },
Err(e) => throw!(*SYM_IO_ERROR, "{e}"), Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} }
} }
fn tcp_listen_inner(listener: TcpListener) -> Value { fn tcp_listen_inner(listener: TcpListener) -> Value {
let listener = RefCell::new(listener); let listener = RefCell::new(listener);
let func = move |_: &mut Vm, _: Vec<Value>| { let func = move |_: &mut Vm, _: Vec<Value>| match listener.borrow_mut().accept() {
match listener.borrow_mut().accept() {
Ok((stream, addr)) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) { Ok((stream, addr)) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
Ok(bf) => Ok(vec![ Ok(bf) => Ok(vec![
ValueFile::from(bf).into(), ValueFile::from(bf).into(),
LString::from(addr.to_string()).into() LString::from(addr.to_string()).into(),
].into()), ]
.into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"), Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} },
Err(e) => throw!(*SYM_IO_ERROR, "{e}"), Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
}; };
NativeFunc::new(Box::new(func), 0).into() NativeFunc::new(Box::new(func), 0, symbol!("inner[tcp_listen]")).into()
} }
#[native_func(1)] #[native_func(1)]
@ -464,18 +515,16 @@ pub fn tcp_listen(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "tcp_connect expected string, got {addr:#}") throw!(*SYM_TYPE_ERROR, "tcp_connect expected string, got {addr:#}")
}; };
let Ok(addr) = addr.to_str() else { let Ok(addr) = addr.to_str() else {
throw!(*SYM_TYPE_ERROR, "address must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
}; };
match TcpListener::bind(addr) { match TcpListener::bind(addr) {
Ok(listener) => { Ok(listener) => {
let addr = listener.local_addr() let addr = listener
.local_addr()
.map(|a| LString::from(a.to_string()).into()) .map(|a| LString::from(a.to_string()).into())
.unwrap_or(Value::Nil); .unwrap_or(Value::Nil);
Ok(vec![ Ok(vec![tcp_listen_inner(listener), addr].into())
tcp_listen_inner(listener), }
addr,
].into())
},
Err(e) => throw!(*SYM_IO_ERROR, "{e}"), Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} }
} }
@ -499,24 +548,32 @@ fn spawn_opt_stdio(
let f: &ValueFile = opt.downcast_native().unwrap(); let f: &ValueFile = opt.downcast_native().unwrap();
let bf = match f.0.borrow().try_clone() { let bf = match f.0.borrow().try_clone() {
Ok(bf) => bf, Ok(bf) => bf,
Err(e) => throw!(*SYM_IO_ERROR, "{e}") Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}; };
let fd = match bf.try_into_raw_fd() { let fd = match bf.try_into_raw_fd() {
Ok(fd) => fd, Ok(fd) => fd,
Err(e) => throw!(*SYM_IO_ERROR, "{e}") Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}; };
let stdio = unsafe { Stdio::from_raw_fd(fd) }; let stdio = unsafe { Stdio::from_raw_fd(fd) };
func(proc, stdio) func(proc, stdio)
}, }
_ => throw!(*SYM_TYPE_ERROR, "{fname} stdio option expected :inherit, :piped, :null, or file, got {opt:#}") _ => throw!(
*SYM_VALUE_ERROR,
"{fname} stdio option expected :inherit, :piped, :null, or file, got {opt:#}"
),
}; };
Ok(()) Ok(())
} }
fn spawn_opt_cd(fname: &str, proc: &mut Command, cd: &Value) -> Result<()> { fn spawn_opt_cd(fname: &str, proc: &mut Command, cd: &Value) -> Result<()> {
if let Value::Nil = cd { return Ok(()) } if let Value::Nil = cd {
return Ok(())
}
let Value::String(cd) = cd else { let Value::String(cd) = cd else {
throw!(*SYM_TYPE_ERROR, "{fname} cd option expected string, got {cd:#}") throw!(
*SYM_TYPE_ERROR,
"{fname} cd option expected string, got {cd:#}"
)
}; };
proc.current_dir(cd.to_os_str()); proc.current_dir(cd.to_os_str());
Ok(()) Ok(())
@ -527,16 +584,22 @@ fn spawn_opt_delenv(fname: &str, proc: &mut Command, delenv: &Value) -> Result<(
Value::Nil => (), Value::Nil => (),
Value::Symbol(s) if *s == *SYM_ALL => { Value::Symbol(s) if *s == *SYM_ALL => {
proc.env_clear(); proc.env_clear();
}, }
Value::List(l) => for e in l.borrow().iter() { Value::List(l) => {
for e in l.borrow().iter() {
let Value::String(e) = e else { let Value::String(e) = e else {
throw!(*SYM_TYPE_ERROR, throw!(
"{fname} delenv option expected :all or list of strings, got {delenv:#}") *SYM_TYPE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}"
)
}; };
proc.env_remove(e.to_os_str()); proc.env_remove(e.to_os_str());
}, }
_ => throw!(*SYM_TYPE_ERROR, }
"{fname} delenv option expected :all or list of strings, got {delenv:#}") _ => throw!(
*SYM_VALUE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}"
),
} }
Ok(()) Ok(())
} }
@ -544,15 +607,21 @@ fn spawn_opt_delenv(fname: &str, proc: &mut Command, delenv: &Value) -> Result<(
fn spawn_opt_env(fname: &str, proc: &mut Command, env: &Value) -> Result<()> { fn spawn_opt_env(fname: &str, proc: &mut Command, env: &Value) -> Result<()> {
match env { match env {
Value::Nil => (), Value::Nil => (),
Value::Table(t) => for (k, v) in t.borrow().iter() { Value::Table(t) => {
for (k, v) in t.borrow().iter() {
let Value::String(k) = k.inner() else { let Value::String(k) = k.inner() else {
throw!(*SYM_TYPE_ERROR, throw!(
"{fname} efromnv option expected table with string keys, got {env:#}") *SYM_TYPE_ERROR,
"{fname} env option expected table with string keys, got {env:#}"
)
}; };
proc.env(k.to_os_str(), v.str().to_os_str()); proc.env(k.to_os_str(), v.str().to_os_str());
}, }
_ => throw!(*SYM_TYPE_ERROR, }
"{fname} env option expected table with string keys, got {env:#}") _ => throw!(
*SYM_TYPE_ERROR,
"{fname} env option expected table with string keys, got {env:#}"
),
} }
Ok(()) Ok(())
} }
@ -560,7 +629,10 @@ fn spawn_opt_env(fname: &str, proc: &mut Command, env: &Value) -> Result<()> {
fn spawn_inner(fname: &str, proc: &mut Command, opts: Value) -> Result<Value> { fn spawn_inner(fname: &str, proc: &mut Command, opts: Value) -> Result<Value> {
let (i, o, e) = match opts { let (i, o, e) = match opts {
Value::Native(ref n) if n.get_type() == *SYM_STD_FILE => { Value::Native(ref n) if n.get_type() == *SYM_STD_FILE => {
throw!(*SYM_TYPE_ERROR, "{fname} options expected :inherit, :piped, :null, or table, got {opts:#}") throw!(
*SYM_TYPE_ERROR,
"{fname} options expected :inherit, :piped, :null, or table, got {opts:#}"
)
} }
Value::Table(t) => { Value::Table(t) => {
let t = t.borrow(); let t = t.borrow();
@ -577,17 +649,20 @@ fn spawn_inner(fname: &str, proc: &mut Command, opts: Value) -> Result<Value> {
spawn_opt_env(fname, proc, env)?; spawn_opt_env(fname, proc, env)?;
} }
let i = t.get(&HashValue::from(*SYM_STDIN)) let i = t
.get(&HashValue::from(*SYM_STDIN))
.cloned() .cloned()
.unwrap_or_else(|| Value::from(*SYM_INHERIT)); .unwrap_or_else(|| Value::from(*SYM_INHERIT));
let o = t.get(&HashValue::from(*SYM_STDOUT)) let o = t
.get(&HashValue::from(*SYM_STDOUT))
.cloned() .cloned()
.unwrap_or_else(|| Value::from(*SYM_INHERIT)); .unwrap_or_else(|| Value::from(*SYM_INHERIT));
let e = t.get(&HashValue::from(*SYM_STDERR)) let e = t
.get(&HashValue::from(*SYM_STDERR))
.cloned() .cloned()
.unwrap_or_else(|| Value::from(*SYM_INHERIT)); .unwrap_or_else(|| Value::from(*SYM_INHERIT));
(i, o, e) (i, o, e)
}, }
v => (v.clone(), v.clone(), v), v => (v.clone(), v.clone(), v),
}; };
@ -621,7 +696,10 @@ fn spawn_inner(fname: &str, proc: &mut Command, opts: Value) -> Result<Value> {
}; };
table.insert(HashValue::from(*SYM_STDERR), ValueFile::from(bf).into()); table.insert(HashValue::from(*SYM_STDERR), ValueFile::from(bf).into());
} }
table.insert(HashValue::from(*SYM_PROCESS), ValueProcess::from(child).into()); table.insert(
HashValue::from(*SYM_PROCESS),
ValueProcess::from(child).into(),
);
Ok(table.into()) Ok(table.into())
} }
@ -632,12 +710,18 @@ pub fn spawn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "spawn expected string, got {cmd:#}") throw!(*SYM_TYPE_ERROR, "spawn expected string, got {cmd:#}")
}; };
let Value::List(args) = args else { let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "spawn expected list of strings, got {args:#}") throw!(
*SYM_TYPE_ERROR,
"spawn expected list of strings, got {args:#}"
)
}; };
let mut proc = Command::new(cmd.to_os_str()); let mut proc = Command::new(cmd.to_os_str());
for arg in args.borrow().iter() { for arg in args.borrow().iter() {
let Value::String(arg) = arg else { let Value::String(arg) = arg else {
throw!(*SYM_TYPE_ERROR, "spawn expected list of strings, got list containing {arg:#}") throw!(
*SYM_TYPE_ERROR,
"spawn expected list of strings, got list containing {arg:#}"
)
}; };
proc.arg(arg.to_os_str()); proc.arg(arg.to_os_str());
} }
@ -653,12 +737,10 @@ pub fn system(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut proc; let mut proc;
if cfg!(target_os = "windows") { if cfg!(target_os = "windows") {
proc = Command::new("cmd"); proc = Command::new("cmd");
proc.arg("/C") proc.arg("/C").arg(cmd.to_os_str())
.arg(cmd.to_os_str())
} else { } else {
proc = Command::new("sh"); proc = Command::new("sh");
proc.arg("-c") proc.arg("-c").arg(cmd.to_os_str())
.arg(cmd.to_os_str())
}; };
spawn_inner("system", &mut proc, opts) spawn_inner("system", &mut proc, opts)
} }
@ -671,11 +753,10 @@ pub fn exit_code(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}; };
let mut proc = proc.0.borrow_mut(); let mut proc = proc.0.borrow_mut();
match proc.wait() { match proc.wait() {
Ok(code) => { Ok(code) => Ok(code
Ok(code.code() .code()
.map(|c| Value::Int(c as i64)) .map(|c| Value::Int(c as i64))
.unwrap_or_default()) .unwrap_or_default()),
},
Err(e) => throw!(*SYM_IO_ERROR, "{e}"), Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} }
} }
@ -689,4 +770,3 @@ pub fn process_id(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let proc = proc.0.borrow(); let proc = proc.0.borrow();
Ok((proc.id() as i64).into()) Ok((proc.id() as i64).into())
} }

507
talc-std/src/format.rs Normal file
View file

@ -0,0 +1,507 @@
use std::io::Write;
use talc_lang::{
exception::{exception, Result},
lstring::{LStr, LString},
parser::{parse_int, to_lstring_radix},
symbol::SYM_TYPE_ERROR,
throw,
value::{Complex64, Rational64, Value},
Vm,
};
use talc_macros::native_func;
use crate::{unpack_args, SYM_FORMAT_ERROR};
#[derive(Clone, Copy, Debug)]
enum FmtIndex {
Imm(usize),
Arg(usize),
NextArg,
}
#[derive(Clone, Copy, Debug)]
enum Align {
Left,
Center,
Right,
}
#[derive(Clone, Copy, Debug, Default)]
enum FmtType {
#[default]
Str,
Repr,
Hex(bool),
Oct,
Sex,
Bin,
Exp(bool),
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
enum FmtSign {
Plus,
Minus,
#[default]
None,
}
// [idx][:[align][sign][width][.prec][ty]]
#[derive(Clone, Copy, Debug, Default)]
struct FmtCode {
arg_idx: Option<usize>,
align: Option<(Align, char)>,
sign: FmtSign,
width: Option<FmtIndex>,
prec: Option<FmtIndex>,
ty: FmtType,
}
struct FmtParser<'p> {
code: &'p LStr,
pos: usize,
}
impl<'p> FmtParser<'p> {
fn new(code: &'p LStr) -> Self {
Self { code, pos: 0 }
}
fn next(&mut self) -> Option<char> {
let c = &self.code[self.pos..].chars().next()?;
self.pos += c.len_utf8();
Some(*c)
}
fn peek(&mut self) -> Option<char> {
self.code[self.pos..].chars().next()
}
fn try_next(&mut self, chs: &[char]) -> Option<char> {
if self.peek().is_some_and(|c| chs.contains(&c)) {
self.next()
} else {
None
}
}
fn next_number_str(&mut self) -> &LStr {
let start = self.pos;
while matches!(self.peek(), Some('0'..='9' | '_')) {
self.next();
}
&self.code[start..self.pos]
}
fn next_usize(&mut self) -> Result<Option<usize>> {
let s = self.next_number_str();
if s.is_empty() {
return Ok(None)
}
let Ok(i) = parse_int(s, 10) else {
throw!(*SYM_FORMAT_ERROR, "code {{{}}}: invalid integer", self.code)
};
if i < 0 {
throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: integer may not be negative",
self.code
)
}
Ok(Some(i as usize))
}
fn next_fmt_index(&mut self) -> Result<Option<FmtIndex>> {
let dollar = self.try_next(&['$']).is_some();
let num = self.next_usize()?;
match (dollar, num) {
(true, None) => Ok(Some(FmtIndex::NextArg)),
(true, Some(n)) => Ok(Some(FmtIndex::Arg(n))),
(false, None) => Ok(None),
(false, Some(n)) => Ok(Some(FmtIndex::Imm(n))),
}
}
fn next_align(&mut self) -> Result<Option<(Align, char)>> {
let align = match self.peek() {
Some('<') => Align::Left,
Some('^') => Align::Center,
Some('>') => Align::Right,
_ => return Ok(None),
};
self.next();
let Some(c) = self.next() else {
throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: alignment without fill character",
self.code
)
};
Ok(Some((align, c)))
}
fn next_sign(&mut self) -> FmtSign {
match self.try_next(&['+', '-']) {
Some('+') => FmtSign::Plus,
Some('-') => FmtSign::Minus,
_ => FmtSign::None,
}
}
fn next_type(&mut self) -> Result<FmtType> {
let ty = match self.peek() {
None => return Ok(FmtType::Str),
Some('?') => FmtType::Repr,
Some('x') => FmtType::Hex(false),
Some('X') => FmtType::Hex(true),
Some('o') => FmtType::Oct,
Some('s') => FmtType::Sex,
Some('b') => FmtType::Bin,
Some('e') => FmtType::Exp(false),
Some('E') => FmtType::Exp(true),
_ => throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: invalid format type",
self.code
),
};
self.next();
Ok(ty)
}
fn next_code_end(&mut self) -> Result<()> {
match self.peek() {
Some(c) => throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: expected end of code, found character '{}'",
self.code,
c
),
None => Ok(()),
}
}
fn read_format(&mut self) -> Result<FmtCode> {
let mut code = FmtCode {
arg_idx: self.next_usize()?,
..FmtCode::default()
};
if self.try_next(&[':']).is_none() {
self.next_code_end()?;
return Ok(code)
}
code.align = self.next_align()?;
code.sign = self.next_sign();
code.width = self.next_fmt_index()?;
if self.try_next(&['.']).is_some() {
code.prec = self.next_fmt_index()?;
}
code.ty = self.next_type()?;
self.next_code_end()?;
Ok(code)
}
}
fn get_arg<'a>(
args: &'a [Value],
n: Option<usize>,
faidx: &mut usize,
) -> Result<&'a Value> {
let i = match n {
Some(n) => n,
None => {
let i = *faidx;
*faidx += 1;
i
}
};
if i >= args.len() {
throw!(
*SYM_FORMAT_ERROR,
"missing arguments: expected at least {}",
i + 1
)
}
Ok(&args[i])
}
fn get_val(args: &[Value], i: FmtIndex, faidx: &mut usize) -> Result<usize> {
let v = match i {
FmtIndex::Imm(n) => return Ok(n),
FmtIndex::Arg(n) => get_arg(args, Some(n), faidx)?,
FmtIndex::NextArg => get_arg(args, None, faidx)?,
};
let Value::Int(v) = v else {
throw!(
*SYM_FORMAT_ERROR,
"expected positive integer argument, found {v:#}"
)
};
if *v < 0 {
throw!(
*SYM_FORMAT_ERROR,
"expected positive integer argument, found {v}"
)
}
Ok(*v as usize)
}
fn format_float(
f: f64,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
ty_name: &str,
) -> Result<()> {
let res = match (prec, ty) {
(None, FmtType::Str) => write!(buf, "{}", Value::Float(f)),
(None, FmtType::Repr) => write!(buf, "{:#}", Value::Float(f)),
(None, FmtType::Exp(true)) => write!(buf, "{:E}", f),
(None, FmtType::Exp(false)) => write!(buf, "{:e}", f),
(Some(p), FmtType::Str) | (Some(p), FmtType::Repr) => {
write!(buf, "{0:.1$}", f, p)
}
(Some(p), FmtType::Exp(true)) => write!(buf, "{0:.1$E}", f, p),
(Some(p), FmtType::Exp(false)) => write!(buf, "{0:.1$e}", f, p),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
};
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
}
fn format_complex(
cx: Complex64,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
) -> Result<()> {
format_float(cx.re, prec, ty, buf, "complex")?;
if cx.im < 0.0 {
buf.push_char('-')
} else {
buf.push_char('+')
}
format_float(cx.im.abs(), prec, ty, buf, "complex")?;
Ok(())
}
fn format_int(
n: i64,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
ty_name: &str,
) -> Result<()> {
if prec.is_some() {
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
}
let res = match ty {
FmtType::Str => write!(buf, "{}", Value::Int(n)),
FmtType::Repr => write!(buf, "{:#}", Value::Int(n)),
FmtType::Hex(caps) => write!(buf, "{}", to_lstring_radix(n, 16, caps)),
FmtType::Oct => write!(buf, "{}", to_lstring_radix(n, 8, false)),
FmtType::Sex => write!(buf, "{}", to_lstring_radix(n, 6, false)),
FmtType::Bin => write!(buf, "{}", to_lstring_radix(n, 2, false)),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
};
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
}
fn format_ratio(
n: Rational64,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
) -> Result<()> {
format_int(*n.numer(), prec, ty, buf, "ratio")?;
buf.push_char('/');
format_int(*n.denom(), prec, ty, buf, "ratio")?;
Ok(())
}
fn format_value(
v: &Value,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
) -> Result<()> {
if prec.is_some() {
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value")
}
let res = match ty {
FmtType::Str => write!(buf, "{}", v),
FmtType::Repr => write!(buf, "{:#}", v),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value"),
};
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
}
fn format_string(
mut s: &LStr,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
) -> Result<()> {
if let Some(prec) = prec {
s = &s[..prec]
}
let res = match ty {
FmtType::Str => {
buf.push_lstr(s);
Ok(())
}
FmtType::Repr => write!(buf, "{:?}", s),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for string"),
};
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
}
fn pad_str(n: usize, c: char, buf: &mut LString) {
for _ in 0..n {
buf.push_char(c)
}
}
fn format_arg(
args: &[Value],
faidx: &mut usize,
code: &LStr,
res: &mut LString,
) -> Result<()> {
if !code.is_utf8() {
throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: code contains invalid UTF-8",
code
)
}
let fmtcode = FmtParser::new(code).read_format()?;
let mut buf = LString::new();
let fmt_arg = get_arg(args, fmtcode.arg_idx, faidx)?;
let prec_val = fmtcode.prec.map(|i| get_val(args, i, faidx)).transpose()?;
let width_val = fmtcode.width.map(|i| get_val(args, i, faidx)).transpose()?;
match fmt_arg {
Value::Int(n) => format_int(*n, prec_val, fmtcode.ty, &mut buf, "integer")?,
Value::Ratio(n) => format_ratio(*n, prec_val, fmtcode.ty, &mut buf)?,
Value::Float(f) => format_float(*f, prec_val, fmtcode.ty, &mut buf, "float")?,
Value::Complex(n) => format_complex(*n, prec_val, fmtcode.ty, &mut buf)?,
Value::String(s) => format_string(s, prec_val, fmtcode.ty, &mut buf)?,
v => format_value(v, prec_val, fmtcode.ty, &mut buf)?,
}
let (sign, final_buf) = match fmtcode.sign {
FmtSign::Plus => match buf.byte_get(0) {
Some(b'+') => ("+", &buf[1..]),
Some(b'-') => ("-", &buf[1..]),
_ => ("+", &buf[..]),
},
FmtSign::Minus => match buf.byte_get(0) {
Some(b'-') => ("-", &buf[1..]),
_ => ("", &buf[..]),
},
FmtSign::None => ("", &buf[..]),
};
res.push_str(sign);
if let Some(w) = width_val {
let w = w.saturating_sub(final_buf.len() + sign.len());
match fmtcode.align {
Some((Align::Left, c)) => {
res.push_lstr(final_buf);
pad_str(w, c, res);
}
Some((Align::Center, c)) => {
pad_str((w + 1) / 2, c, res);
res.push_lstr(final_buf);
pad_str(w / 2, c, res);
}
Some((Align::Right, c)) => {
pad_str(w, c, res);
res.push_lstr(final_buf);
}
None => {
let c = if matches!(
fmt_arg,
Value::Int(_) | Value::Float(_) | Value::Ratio(_) | Value::Complex(_)
) && fmtcode.sign != FmtSign::None
{
'0'
} else {
' '
};
pad_str(w, c, res);
res.push_lstr(final_buf);
}
}
} else {
res.push_lstr(final_buf);
}
Ok(())
}
#[native_func(2, "fmt")]
pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, fstr, fargs] = unpack_args!(args);
let (Value::String(fstr), Value::List(fargs)) = (&fstr, &fargs) else {
throw!(
*SYM_TYPE_ERROR,
"format expected string and list, got {fstr:#} and {fargs:#}"
)
};
let mut res = LString::new();
let mut faidx = 0;
let mut i = 0;
while i < fstr.len() {
let b = fstr.byte_at(i);
i += 1;
if b == b'}' {
let Some(b'}') = fstr.byte_get(i) else {
throw!(
*SYM_FORMAT_ERROR,
"unmatched closing brace at byte {}",
i - 1
)
};
i += 1;
res.push_byte(b);
}
if b != b'{' {
res.push_byte(b);
continue
}
if i >= fstr.len() {
throw!(
*SYM_FORMAT_ERROR,
"unclosed format specifier at byte {}",
i - 1
)
}
if let Some(b'{') = fstr.byte_get(i) {
i += 1;
res.push_byte(b);
continue
}
let start = i;
while i < fstr.len() && fstr.byte_at(i) != b'}' {
i += 1;
}
if i == fstr.len() {
throw!(
*SYM_FORMAT_ERROR,
"unclosed format specifier at byte {}",
start - 1
)
}
let code = &fstr[start..i];
i += 1;
format_arg(&fargs.borrow(), &mut faidx, code, &mut res)?;
}
Ok(res.into())
}
pub fn load(vm: &mut Vm) {
vm.set_global_name("fmt", fmt_().into());
}

View file

@ -1,10 +1,23 @@
use std::{io::{BufRead, Write}, os::unix::ffi::OsStrExt, time::{SystemTime, UNIX_EPOCH}}; use std::{
io::{BufRead, Write},
os::unix::ffi::OsStrExt,
sync::Mutex,
time::{SystemTime, UNIX_EPOCH},
};
use talc_lang::{exception::{throw, Result}, lstring::LString, symbol::SYM_TYPE_ERROR, value::Value, vmcall, Vm}; use talc_lang::{
exception::{throw, Result},
lstring::LString,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
value::Value,
vmcall, Vm,
};
use talc_macros::native_func; use talc_macros::native_func;
use crate::{unpack_args, SYM_IO_ERROR}; use crate::{unpack_args, SYM_IO_ERROR};
static ENV_LOCK: Mutex<()> = Mutex::new(());
#[native_func(1)] #[native_func(1)]
pub fn print(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn print(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let res = match &args[1] { let res = match &args[1] {
@ -43,14 +56,22 @@ pub fn readln(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
#[native_func(0)] #[native_func(0)]
pub fn time(_: &mut Vm, _: Vec<Value>) -> Result<Value> { pub fn time(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards"); let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("time went backwards");
Ok(time.as_secs_f64().into()) Ok(time.as_secs_f64().into())
} }
#[native_func(0)] #[native_func(0)]
pub fn time_ns(_: &mut Vm, _: Vec<Value>) -> Result<Value> { pub fn time_ns(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards"); let time = SystemTime::now()
Ok(vec![(time.as_secs() as i64).into(), (time.subsec_nanos() as i64).into()].into()) .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)] #[native_func(1)]
@ -70,7 +91,10 @@ pub fn env(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "env expected string, got {key:#}") throw!(*SYM_TYPE_ERROR, "env expected string, got {key:#}")
}; };
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) { if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(*SYM_TYPE_ERROR, "environment variable must not be empty or contain an equals sign or null byte") throw!(
*SYM_VALUE_ERROR,
"environment variable must not be empty or contain an equals sign or null byte"
)
} }
let val = std::env::var_os(key.to_os_str()); let val = std::env::var_os(key.to_os_str());
match val { match val {
@ -86,13 +110,25 @@ pub fn setenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "setenv expected string, got {key:#}") throw!(*SYM_TYPE_ERROR, "setenv expected string, got {key:#}")
}; };
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) { if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(*SYM_TYPE_ERROR, "environment variable must not be empty or contain an equals sign or null byte") throw!(
*SYM_VALUE_ERROR,
"environment variable must not be empty or contain an equals sign or null byte"
)
} }
let val = val.str(); let val = val.str();
if val.as_bytes().contains(&0) { if val.as_bytes().contains(&0) {
throw!(*SYM_TYPE_ERROR, "environment variable value must not contain a null byte") throw!(
*SYM_VALUE_ERROR,
"environment variable value must not contain a null byte"
)
}
{
let Ok(guard) = ENV_LOCK.lock() else {
throw!(*SYM_VALUE_ERROR, "failed to lock environemnt variables")
};
unsafe { std::env::set_var(key.to_os_str(), val.to_os_str()) };
drop(guard);
} }
std::env::set_var(key.to_os_str(), val.to_os_str());
Ok(Value::Nil) Ok(Value::Nil)
} }
@ -103,13 +139,21 @@ pub fn delenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "delenv expected string, got {key:#}") throw!(*SYM_TYPE_ERROR, "delenv expected string, got {key:#}")
}; };
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) { if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(*SYM_TYPE_ERROR, "environment variable must not be empty or contain an equals sign or null byte") throw!(
*SYM_VALUE_ERROR,
"environment variable must not be empty or contain an equals sign or null byte"
)
}
{
let Ok(guard) = ENV_LOCK.lock() else {
throw!(*SYM_VALUE_ERROR, "failed to lock environemnt variables")
};
unsafe { std::env::remove_var(key.to_os_str()) };
drop(guard);
} }
std::env::remove_var(key.to_os_str());
Ok(Value::Nil) Ok(Value::Nil)
} }
pub fn load(vm: &mut Vm) { pub fn load(vm: &mut Vm) {
vm.set_global_name("print", print().into()); vm.set_global_name("print", print().into());
vm.set_global_name("println", println().into()); vm.set_global_name("println", println().into());

View file

@ -1,9 +1,15 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{symbol::SYM_TYPE_ERROR, exception::Result, throw, value::{function::NativeFunc, range::RangeType, HashValue, Value}, vmcall, vmcalliter, Vm}; use talc_lang::{
exception::Result,
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{function::NativeFunc, ops::RatioExt, range::RangeType, HashValue, Value},
vmcall, vmcalliter, Vm,
};
use talc_macros::native_func; use talc_macros::native_func;
use crate::{unpack_args, unpack_varargs}; use crate::unpack_args;
pub fn load(vm: &mut Vm) { pub fn load(vm: &mut Vm) {
// begin // begin
@ -26,7 +32,9 @@ pub fn load(vm: &mut Vm) {
// join // join
vm.set_global_name("zip", zip().into()); vm.set_global_name("zip", zip().into());
vm.set_global_name("zipn", zipn().into());
vm.set_global_name("alternate", alternate().into()); vm.set_global_name("alternate", alternate().into());
vm.set_global_name("alternaten", alternaten().into());
vm.set_global_name("intersperse", intersperse().into()); vm.set_global_name("intersperse", intersperse().into());
vm.set_global_name("cartprod", cartprod().into()); vm.set_global_name("cartprod", cartprod().into());
vm.set_global_name("chain", chain().into()); vm.set_global_name("chain", chain().into());
@ -48,6 +56,12 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("index_if", index_if().into()); vm.set_global_name("index_if", index_if().into());
vm.set_global_name("find", find().into()); vm.set_global_name("find", find().into());
vm.set_global_name("count", count().into()); vm.set_global_name("count", count().into());
vm.set_global_name("mean", mean().into());
vm.set_global_name("variance", variance().into());
vm.set_global_name("stdev", stdev().into());
vm.set_global_name("pvariance", pvariance().into());
vm.set_global_name("pstdev", pstdev().into());
vm.set_global_name("median", median().into());
} }
// //
@ -79,7 +93,7 @@ pub fn pairs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::iter_pack(None)) Ok(Value::iter_pack(None))
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[pairs]")).into())
} }
#[native_func(1)] #[native_func(1)]
@ -87,40 +101,32 @@ pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args); let [_, val] = unpack_args!(args);
let v = RefCell::new(Some(val)); let v = RefCell::new(Some(val));
let f = move |_: &mut Vm, _| { let f = move |_: &mut Vm, _| Ok(Value::iter_pack(v.borrow_mut().take()));
Ok(Value::iter_pack(v.borrow_mut().take())) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[once]")).into())
};
Ok(NativeFunc::new(Box::new(f), 0).into())
} }
#[native_func(1)] #[native_func(1)]
pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args); let [_, val] = unpack_args!(args);
let f = move |_: &mut Vm, _| { let f = move |_: &mut Vm, _| Ok(val.clone());
Ok(val.clone()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[forever]")).into())
};
Ok(NativeFunc::new(Box::new(f), 0).into())
} }
// //
// chain iteration // chain iteration
// //
#[native_func(2)] #[native_func(2)]
pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, map, iter] = unpack_args!(args); let [_, map, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| match vmcalliter!(vm; iter.clone())? {
match vmcalliter!(vm; iter.clone())? {
Some(val) => Ok(vmcall!(vm; map.clone(), val)?), Some(val) => Ok(vmcall!(vm; map.clone(), val)?),
None => Ok(Value::iter_pack(None)), None => Ok(Value::iter_pack(None)),
}
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[map]")).into())
} }
#[native_func(2)] #[native_func(2)]
@ -128,16 +134,14 @@ pub fn tee(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, tee, iter] = unpack_args!(args); let [_, tee, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| match vmcalliter!(vm; iter.clone())? {
match vmcalliter!(vm; iter.clone())? {
Some(val) => { Some(val) => {
vmcall!(vm; tee.clone(), val.clone())?; vmcall!(vm; tee.clone(), val.clone())?;
Ok(val) Ok(val)
} }
None => Ok(Value::iter_pack(None)), None => Ok(Value::iter_pack(None)),
}
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[tee]")).into())
} }
#[native_func(3)] #[native_func(3)]
@ -155,7 +159,7 @@ pub fn scan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*result.borrow_mut() = r.clone(); *result.borrow_mut() = r.clone();
Ok(r) Ok(r)
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[scan]")).into())
} }
#[native_func(2)] #[native_func(2)]
@ -163,8 +167,7 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, filter, iter] = unpack_args!(args); let [_, filter, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| loop {
loop {
let Some(next) = vmcalliter!(vm; iter.clone())? else { let Some(next) = vmcalliter!(vm; iter.clone())? else {
return Ok(Value::iter_pack(None)) return Ok(Value::iter_pack(None))
}; };
@ -172,19 +175,21 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
if res.truthy() { if res.truthy() {
return Ok(next) return Ok(next)
} }
}
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[filter]")).into())
} }
#[native_func(2)] #[native_func(2)]
pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, count, iter] = unpack_args!(args); let [_, count, iter] = unpack_args!(args);
let Value::Int(count) = count else { let Value::Int(count) = count else {
throw!(*SYM_TYPE_ERROR, "take expected integer") throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}")
}; };
let Ok(count) = count.try_into() else { let Ok(count) = count.try_into() else {
throw!(*SYM_TYPE_ERROR, "take expected nonnegative integer") throw!(
*SYM_VALUE_ERROR,
"take expected nonnegative integer, got {count:#}"
)
}; };
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
@ -200,17 +205,20 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*taken.borrow_mut() += 1; *taken.borrow_mut() += 1;
Ok(next) Ok(next)
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[take]")).into())
} }
#[native_func(2)] #[native_func(2)]
pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, count, iter] = unpack_args!(args); let [_, count, iter] = unpack_args!(args);
let Value::Int(count) = count else { let Value::Int(count) = count else {
throw!(*SYM_TYPE_ERROR, "count expected integer") throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}")
}; };
let Ok(count) = count.try_into() else { let Ok(count) = count.try_into() else {
throw!(*SYM_TYPE_ERROR, "count expected nonnegative integer") throw!(
*SYM_VALUE_ERROR,
"count expected nonnegative integer, got {count:#}"
)
}; };
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
@ -225,7 +233,7 @@ pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} }
vmcall!(vm; iter.clone()) vmcall!(vm; iter.clone())
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[skip]")).into())
} }
#[native_func(1)] #[native_func(1)]
@ -244,10 +252,9 @@ pub fn enumerate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::from(vec![n.into(), next])) Ok(Value::from(vec![n.into(), next]))
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[enumerate]")).into())
} }
#[native_func(1)] #[native_func(1)]
pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args); let [_, iter] = unpack_args!(args);
@ -279,12 +286,15 @@ pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cycle]")).into())
} }
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, Default)]
enum Step { enum Step {
First, Going, #[default] End, First,
Going,
#[default]
End,
} }
#[native_func(2)] #[native_func(2)]
@ -292,10 +302,13 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, by, iter] = unpack_args!(args); let [_, by, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let Value::Int(by) = by else { let Value::Int(by) = by else {
throw!(*SYM_TYPE_ERROR, "step expected integer") throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}")
}; };
if by <= 0 { if by <= 0 {
throw!(*SYM_TYPE_ERROR, "step expected positive integer") throw!(
*SYM_VALUE_ERROR,
"step expected positive integer, got {by:#}"
)
} }
let state = RefCell::new(Step::First); let state = RefCell::new(Step::First);
@ -306,7 +319,7 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*state.borrow_mut() = Step::Going; *state.borrow_mut() = Step::Going;
} }
Ok(Value::iter_pack(res)) Ok(Value::iter_pack(res))
}, }
Step::Going => { Step::Going => {
for _ in 0..(by - 1) { for _ in 0..(by - 1) {
if vmcall!(vm; iter.clone())? == Value::Nil { if vmcall!(vm; iter.clone())? == Value::Nil {
@ -318,10 +331,10 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}; };
*state.borrow_mut() = Step::Going; *state.borrow_mut() = Step::Going;
Ok(x) Ok(x)
}, }
Step::End => Ok(Value::Nil), Step::End => Ok(Value::Nil),
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[step]")).into())
} }
#[native_func(1)] #[native_func(1)]
@ -339,25 +352,48 @@ pub fn rev(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*lst.borrow_mut() = Some(l); *lst.borrow_mut() = Some(l);
} }
Ok(Value::iter_pack(lst.borrow_mut().as_mut().unwrap().pop())) Ok(Value::iter_pack(lst.borrow_mut().as_mut().unwrap().pop()))
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[rev]")).into())
} }
// //
// join // join
// //
#[native_func(2)]
#[native_func(2..)]
pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, i1, i2], rest) = unpack_varargs!(args); let [_, i1, i2] = unpack_args!(args);
let mut iters = Vec::with_capacity(2 + rest.len()); let i1 = i1.to_iter_function()?;
for i in [i1, i2].into_iter().chain(rest.into_iter()) { let i2 = i2.to_iter_function()?;
iters.push(i.to_iter_function()?);
let f = move |vm: &mut Vm, _| {
let mut res = Vec::with_capacity(2);
match vmcalliter!(vm; i1.clone())? {
Some(v) => res.push(v),
None => return Ok(Value::iter_pack(None)),
};
match vmcalliter!(vm; i2.clone())? {
Some(v) => res.push(v),
None => return Ok(Value::iter_pack(None)),
};
Ok(Value::from(res))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[zip]")).into())
} }
#[native_func(1)]
pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "zipn expected list, got {args:#}")
};
let iters = args
.borrow()
.iter()
.map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?;
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
let mut res = Vec::with_capacity(iters.len()); let mut res = Vec::with_capacity(iters.len());
for i in &iters { for i in &iters {
@ -369,22 +405,53 @@ pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::from(res)) Ok(Value::from(res))
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[zipn]")).into())
} }
#[native_func(2..)] #[native_func(2)]
pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, i1, i2], rest) = unpack_varargs!(args); let [_, i1, i2] = unpack_args!(args);
let mut iters = Vec::with_capacity(2 + rest.len()); let i1 = i1.to_iter_function()?;
for i in [i1, i2].into_iter().chain(rest.into_iter()) { let i2 = i2.to_iter_function()?;
iters.push(i.to_iter_function()?);
let state = RefCell::new((false, false));
let f = move |vm: &mut Vm, _| {
let mut s = state.borrow_mut();
if s.1 {
return Ok(Value::iter_pack(None))
} }
let n = s.0;
s.0 = !s.0;
drop(s);
let iter = if n { i1.clone() } else { i2.clone() };
if let Some(v) = vmcalliter!(vm; iter)? {
Ok(v)
} else {
state.borrow_mut().1 = true;
Ok(Value::iter_pack(None))
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[alternate]")).into())
}
#[native_func(1)]
pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "alternaten expected list, got {args:#}")
};
let iters = args
.borrow()
.iter()
.map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?;
let state = RefCell::new((0, false)); let state = RefCell::new((0, false));
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
let mut s = state.borrow_mut(); let mut s = state.borrow_mut();
if s.1 { if s.1 {
return Ok(Value::iter_pack(None)); return Ok(Value::iter_pack(None))
} }
let n = s.0; let n = s.0;
s.0 = (s.0 + 1) % iters.len(); s.0 = (s.0 + 1) % iters.len();
@ -397,12 +464,16 @@ pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[alternaten]")).into())
} }
#[derive(Default)] #[derive(Default)]
enum Intersperse { enum Intersperse {
Init, Waiting, HasNext(Value), #[default] End Init,
Waiting,
HasNext(Value),
#[default]
End,
} }
#[native_func(2)] #[native_func(2)]
@ -411,8 +482,7 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let state = RefCell::new(Intersperse::Init); let state = RefCell::new(Intersperse::Init);
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| match state.take() {
match state.take() {
Intersperse::Init => { Intersperse::Init => {
if let Some(v) = vmcalliter!(vm; iter.clone())? { if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::Waiting; *state.borrow_mut() = Intersperse::Waiting;
@ -421,7 +491,7 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*state.borrow_mut() = Intersperse::End; *state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None)) Ok(Value::iter_pack(None))
} }
}, }
Intersperse::Waiting => { Intersperse::Waiting => {
if let Some(v) = vmcalliter!(vm; iter.clone())? { if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::HasNext(v); *state.borrow_mut() = Intersperse::HasNext(v);
@ -430,19 +500,17 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*state.borrow_mut() = Intersperse::End; *state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None)) Ok(Value::iter_pack(None))
} }
}, }
Intersperse::HasNext(v) => { Intersperse::HasNext(v) => {
*state.borrow_mut() = Intersperse::Waiting; *state.borrow_mut() = Intersperse::Waiting;
Ok(v) Ok(v)
},
Intersperse::End => Ok(Value::iter_pack(None)),
} }
Intersperse::End => Ok(Value::iter_pack(None)),
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[intersperse]")).into())
} }
#[native_func(2)] #[native_func(2)]
pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter1, iter2] = unpack_args!(args); let [_, iter1, iter2] = unpack_args!(args);
@ -462,7 +530,7 @@ pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[chain]")).into())
} }
#[derive(Default, Debug)] #[derive(Default, Debug)]
@ -527,17 +595,13 @@ pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::from(vec![a_res, b_res])) Ok(Value::from(vec![a_res, b_res]))
}; };
Ok(NativeFunc::new(Box::new(f), 0).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cart_prod]")).into())
} }
// //
// end iteration // end iteration
// //
#[native_func(1)] #[native_func(1)]
pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args); let [_, iter] = unpack_args!(args);
@ -545,7 +609,7 @@ pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut result = Vec::new(); let mut result = Vec::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
result.push(value); result.push(value);
}; }
Ok(result.into()) Ok(result.into())
} }
@ -556,14 +620,23 @@ pub fn table(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut result = HashMap::new(); let mut result = HashMap::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
let Value::List(l) = value else { let Value::List(l) = value else {
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list") throw!(
*SYM_TYPE_ERROR,
"table expected iterator to yield list, got {value:#}"
)
}; };
let l = Rc::unwrap_or_clone(l).take(); let mut l = Rc::unwrap_or_clone(l).take();
let Ok([k, v]): std::result::Result<[Value; 2], Vec<Value>> = l.try_into() else { if l.len() != 2 {
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list of length 2") throw!(
*SYM_VALUE_ERROR,
"table: iterator yielded list of length {} (expected 2)",
l.len()
)
}; };
let v = l.pop().unwrap();
let k = l.pop().unwrap();
result.insert(k.try_into()?, v); result.insert(k.try_into()?, v);
}; }
Ok(result.into()) Ok(result.into())
} }
@ -574,15 +647,16 @@ pub fn len(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::String(s) => return Ok((s.chars().count() as i64).into()), Value::String(s) => return Ok((s.chars().count() as i64).into()),
Value::List(l) => return Ok((l.borrow().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::Table(t) => return Ok((t.borrow().len() as i64).into()),
Value::Range(r) if r.ty != RangeType::Endless Value::Range(r) if r.ty != RangeType::Endless => {
=> return Ok((r.len().unwrap() as i64).into()), return Ok((r.len().unwrap() as i64).into())
}
_ => (), _ => (),
} }
let iter = value.to_iter_function()?; let iter = value.to_iter_function()?;
let mut len = 0; let mut len = 0;
while vmcalliter!(vm; iter.clone())?.is_some() { while vmcalliter!(vm; iter.clone())?.is_some() {
len += 1; len += 1;
}; }
Ok(len.into()) Ok(len.into())
} }
@ -701,11 +775,13 @@ pub fn contains(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for _ in 0.. { for _ in 0.. {
match vmcalliter!(vm; iter.clone())? { match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil), None => return Ok(Value::Nil),
Some(v) => if v == val { Some(v) => {
if v == val {
return Ok(true.into()) return Ok(true.into())
} }
} }
} }
}
Ok(false.into()) Ok(false.into())
} }
@ -717,11 +793,13 @@ pub fn index_of(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for i in 0.. { for i in 0.. {
match vmcalliter!(vm; iter.clone())? { match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil), None => return Ok(Value::Nil),
Some(v) => if v == val { Some(v) => {
if v == val {
return Ok(i.into()) return Ok(i.into())
} }
} }
} }
}
Ok(Value::Nil) Ok(Value::Nil)
} }
@ -733,11 +811,13 @@ pub fn index_if(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for i in 0.. { for i in 0.. {
match vmcalliter!(vm; iter.clone())? { match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil), None => return Ok(Value::Nil),
Some(v) => if vmcall!(vm; func.clone(), v)?.truthy() { Some(v) => {
if vmcall!(vm; func.clone(), v)?.truthy() {
return Ok(i.into()) return Ok(i.into())
} }
} }
} }
}
Ok(Value::Nil) Ok(Value::Nil)
} }
@ -775,3 +855,111 @@ pub fn count(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(map.into()) Ok(map.into())
} }
#[native_func(1)]
pub fn mean(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let mut sum = Value::Float(0.0);
let mut count = Value::Int(0);
while let Some(value) = vmcalliter!(vm; iter.clone())? {
sum = (sum + value)?;
count = (count + Value::from(1))?;
}
sum / count
}
fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> {
let mut m = Value::Float(0.0);
let mut s = Value::Float(0.0);
let mut k = 1;
while let Some(value) = vmcalliter!(vm; iter.clone())? {
let old_m = m.clone();
m = (m.clone() + ((value.clone() - m.clone())? / Value::Int(k))?)?;
s = (s + ((value.clone() - m.clone())? * (value - old_m)?)?)?;
k += 1;
}
s / Value::Int(k - if pop { 1 } else { 2 })
}
#[native_func(1)]
pub fn variance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
variance_inner(vm, iter, false)
}
#[native_func(1)]
pub fn stdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let v = variance_inner(vm, iter, false)?;
Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
})
}
#[native_func(1)]
pub fn pvariance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
variance_inner(vm, iter, true)
}
#[native_func(1)]
pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let v = variance_inner(vm, iter, true)?;
Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
})
}
#[derive(PartialEq, PartialOrd)]
struct OrdValue(Value);
impl std::cmp::Eq for OrdValue {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl std::cmp::Ord for OrdValue {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Less)
}
}
#[native_func(1)]
pub fn median(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let mut vals = Vec::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? {
vals.push(OrdValue(value));
}
let count = vals.len();
if count == 0 {
Ok(Value::Nil)
} else if count % 2 == 0 {
let (_, _, hi) = vals.select_nth_unstable(count / 2 - 1);
let (_, _, _) = hi.select_nth_unstable(0);
let m2 = vals.swap_remove(count / 2);
let m1 = vals.swap_remove(count / 2 - 1);
(m1.0 + m2.0)? / Value::Int(2)
} else {
let (_, _, _) = vals.select_nth_unstable(count / 2);
let m = vals.swap_remove(count / 2);
m.0 / Value::Int(1)
}
}

View file

@ -1,16 +1,22 @@
use talc_lang::{symbol::{symbol, Symbol}, Vm}; #![allow(clippy::mutable_key_type)]
pub mod value; use talc_lang::{
symbol::{symbol, Symbol},
Vm,
};
pub mod collection;
pub mod exception;
pub mod file;
pub mod format;
pub mod io; pub mod io;
pub mod iter; pub mod iter;
pub mod exception;
pub mod num; pub mod num;
pub mod collection;
pub mod string;
pub mod file;
pub mod regex;
#[cfg(feature = "random")] #[cfg(feature = "random")]
pub mod random; pub mod random;
pub mod regex;
pub mod string;
pub mod value;
pub fn load_all(vm: &mut Vm) { pub fn load_all(vm: &mut Vm) {
value::load(vm); value::load(vm);
@ -20,6 +26,7 @@ pub fn load_all(vm: &mut Vm) {
num::load(vm); num::load(vm);
io::load(vm); io::load(vm);
string::load(vm); string::load(vm);
format::load(vm);
regex::load(vm); regex::load(vm);
file::load(vm); file::load(vm);
#[cfg(feature = "random")] #[cfg(feature = "random")]
@ -28,9 +35,9 @@ pub fn load_all(vm: &mut Vm) {
lazy_static::lazy_static! { lazy_static::lazy_static! {
pub static ref SYM_IO_ERROR: Symbol = symbol!(io_error); pub static ref SYM_IO_ERROR: Symbol = symbol!(io_error);
pub static ref SYM_FORMAT_ERROR: Symbol = symbol!(format_error);
} }
macro_rules! unpack_args { macro_rules! unpack_args {
($e:expr) => { ($e:expr) => {
($e).try_into().expect("bypassed arity check") ($e).try_into().expect("bypassed arity check")
@ -38,16 +45,3 @@ macro_rules! unpack_args {
} }
pub(crate) use unpack_args; 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;

View file

@ -1,10 +1,17 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use talc_lang::{exception::Result, lstring::LString, parse_int, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{ops::RatioExt, Complex64, Value}, Vm}; use talc_lang::{
exception::Result,
parser::{parse_int, to_lstring_radix},
symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{ops::RatioExt, Complex64, Value},
vmcalliter, Vm,
};
use talc_macros::native_func; use talc_macros::native_func;
use crate::{unpack_args, unpack_varargs}; use crate::unpack_args;
lazy_static! { lazy_static! {
static ref SYM_NAN: Symbol = Symbol::get("nan"); static ref SYM_NAN: Symbol = Symbol::get("nan");
@ -61,10 +68,13 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("from_radix", from_radix().into()); vm.set_global_name("from_radix", from_radix().into());
vm.set_global_name("gcd", gcd().into()); vm.set_global_name("gcd", gcd().into());
vm.set_global_name("gcdn", gcdn().into());
vm.set_global_name("lcm", lcm().into()); vm.set_global_name("lcm", lcm().into());
vm.set_global_name("lcmn", lcmn().into());
vm.set_global_name("isqrt", isqrt().into()); vm.set_global_name("isqrt", isqrt().into());
vm.set_global_name("isprime", isprime().into()); vm.set_global_name("isprime", isprime().into());
vm.set_global_name("factors", factors().into()); vm.set_global_name("factors", factors().into());
vm.set_global_name("totient", totient().into());
vm.set_global_name("min", min().into()); vm.set_global_name("min", min().into());
vm.set_global_name("max", max().into()); vm.set_global_name("max", max().into());
@ -96,6 +106,7 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("cbrt", cbrt().into()); vm.set_global_name("cbrt", cbrt().into());
vm.set_global_name("ln", ln().into()); vm.set_global_name("ln", ln().into());
vm.set_global_name("log2", log2().into()); vm.set_global_name("log2", log2().into());
vm.set_global_name("log10", log10().into());
vm.set_global_name("exp", exp().into()); vm.set_global_name("exp", exp().into());
vm.set_global_name("exp2", exp2().into()); vm.set_global_name("exp2", exp2().into());
@ -119,42 +130,13 @@ pub fn load(vm: &mut Vm) {
// base conversions // base conversions
// //
fn to_radix_inner(n: i64, radix: u32, upper: bool) -> LString {
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;
let mut c = char::from_digit(m as u32, radix).unwrap();
if upper { c.make_ascii_uppercase(); }
result.push(c as u8);
if x == 0 {
break;
}
}
result[begin..].reverse();
LString::from(result)
}
#[native_func(1)] #[native_func(1)]
pub fn bin(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn bin(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}")
}; };
Ok(to_radix_inner(x, 2, false).into()) Ok(to_lstring_radix(x, 2, false).into())
} }
#[native_func(1)] #[native_func(1)]
@ -163,7 +145,7 @@ pub fn sex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "sex expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "sex expected integer argument, got {x:#}")
}; };
Ok(to_radix_inner(x, 6, false).into()) Ok(to_lstring_radix(x, 6, false).into())
} }
#[native_func(1)] #[native_func(1)]
@ -172,7 +154,7 @@ pub fn oct(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "oct expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "oct expected integer argument, got {x:#}")
}; };
Ok(to_radix_inner(x, 8, false).into()) Ok(to_lstring_radix(x, 8, false).into())
} }
#[native_func(1)] #[native_func(1)]
@ -181,45 +163,63 @@ pub fn hex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "hex expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "hex expected integer argument, got {x:#}")
}; };
Ok(to_radix_inner(x, 16, false).into()) Ok(to_lstring_radix(x, 16, false).into())
} }
#[native_func(2)] #[native_func(2)]
pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, radix] = unpack_args!(args); let [_, x, radix] = unpack_args!(args);
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else { let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
throw!(*SYM_TYPE_ERROR, "to_radix expected integer arguments, got {x:#} and {radix:#}") throw!(
*SYM_TYPE_ERROR,
"to_radix expected integer arguments, got {x:#} and {radix:#}"
)
}; };
if *radix < 2 || *radix > 36 { if *radix < 2 || *radix > 36 {
throw!(*SYM_TYPE_ERROR, "to_radix expected radix in range 0..=36") throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 0..=36")
} }
Ok(to_radix_inner(*x, *radix as u32, false).into()) Ok(to_lstring_radix(*x, *radix as u32, false).into())
} }
#[native_func(2)] #[native_func(2)]
pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, radix] = unpack_args!(args); let [_, x, radix] = unpack_args!(args);
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else { let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
throw!(*SYM_TYPE_ERROR, "to_radix_upper expected integer arguments, got {x:#} and {radix:#}") throw!(
*SYM_TYPE_ERROR,
"to_radix_upper expected integer arguments, got {x:#} and {radix:#}"
)
}; };
if *radix < 2 || *radix > 36 { if *radix < 2 || *radix > 36 {
throw!(*SYM_TYPE_ERROR, "to_radix_upper expected radix in range 0..=36") throw!(
*SYM_VALUE_ERROR,
"to_radix_upper expected radix in range 0..=36"
)
} }
Ok(to_radix_inner(*x, *radix as u32, true).into()) Ok(to_lstring_radix(*x, *radix as u32, true).into())
} }
#[native_func(2)] #[native_func(2)]
pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s, radix] = unpack_args!(args); let [_, s, radix] = unpack_args!(args);
let (Value::String(s), Value::Int(radix)) = (&s, &radix) else { 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:#}") throw!(
*SYM_TYPE_ERROR,
"from_radix expected string and integer arguments, got {s:#} and {radix:#}"
)
}; };
if *radix < 2 || *radix > 36 { if *radix < 2 || *radix > 36 {
throw!(*SYM_TYPE_ERROR, "from_radix expected radix in range 0..=36") throw!(
*SYM_VALUE_ERROR,
"from_radix expected radix in range 0..=36"
)
} }
match parse_int(s.as_ref(), *radix as u32) { match parse_int(s.as_ref(), *radix as u32) {
Ok(v) => Ok(v.into()), Ok(v) => Ok(v.into()),
Err(_) => throw!(*SYM_TYPE_ERROR, "string was not a valid integer in given radix"), Err(_) => throw!(
*SYM_VALUE_ERROR,
"string was not a valid integer in given radix"
),
} }
} }
@ -229,7 +229,9 @@ pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
fn isqrt_inner(mut n: i64) -> i64 { fn isqrt_inner(mut n: i64) -> i64 {
assert!(n >= 0, "isqrt input should be nonnegative"); assert!(n >= 0, "isqrt input should be nonnegative");
if n < 2 { return n } if n < 2 {
return n
}
let mut c = 0; let mut c = 0;
let mut d = 1 << 62; let mut d = 1 << 62;
@ -242,8 +244,7 @@ fn isqrt_inner(mut n: i64) -> i64 {
if n >= c + d { if n >= c + d {
n -= c + d; n -= c + d;
c = (c >> 1) + d; c = (c >> 1) + d;
} } else {
else {
c >>= 1; c >>= 1;
} }
d >>= 2; d >>= 2;
@ -272,31 +273,35 @@ pub fn gcd_inner(a: i64, b: i64) -> i64 {
} }
b -= a; b -= a;
if b == 0 { if b == 0 {
return a << z; return a << z
} }
b >>= b.trailing_zeros(); b >>= b.trailing_zeros();
} }
} }
#[native_func(1)] #[native_func(1)]
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "isqrt expected integer argument, got {x:#}") throw!(
*SYM_TYPE_ERROR,
"isqrt expected integer argument, got {x:#}"
)
}; };
if x < 0 { if x < 0 {
throw!(*SYM_TYPE_ERROR, "isqrt: argument must be positive") throw!(*SYM_VALUE_ERROR, "isqrt: argument must be positive")
} }
Ok(isqrt_inner(x).into()) Ok(isqrt_inner(x).into())
} }
#[native_func(1)] #[native_func(1)]
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "isprime expected integer argument, got {x:#}") throw!(
*SYM_TYPE_ERROR,
"isprime expected integer argument, got {x:#}"
)
}; };
if x < 2 { if x < 2 {
return Ok(false.into()) return Ok(false.into())
@ -312,58 +317,96 @@ pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let lim = isqrt_inner(x); let lim = isqrt_inner(x);
let mut i = 12; let mut i = 12;
while i <= lim + 1 { while i <= lim + 1 {
if x % (i - 1) == 0 { return Ok(false.into()) } if x % (i - 1) == 0 {
if x % (i + 1) == 0 { return Ok(false.into()) } return Ok(false.into())
}
if x % (i + 1) == 0 {
return Ok(false.into())
}
i += 6; i += 6;
} }
Ok(true.into()) Ok(true.into())
} }
#[native_func(2..)] #[native_func(2)]
pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, x, y], rest) = unpack_varargs!(args); let [_, x, y] = unpack_args!(args);
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
}; };
let Value::Int(y) = y else { let Value::Int(y) = y else {
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {y:#}")
}; };
let mut g = gcd_inner(x, y); Ok(gcd_inner(x, y).into())
for a in rest { }
#[native_func(1)]
pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?;
let mut g = 0;
while let Some(a) = vmcalliter!(vm; args.clone())? {
let Value::Int(a) = a else { let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {a:#}") throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}")
}; };
g = gcd_inner(g, a); g = gcd_inner(g, a);
} }
Ok(g.into()) Ok(g.into())
} }
#[native_func(2..)] #[native_func(2)]
pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, x, y], rest) = unpack_varargs!(args); let [_, x, y] = unpack_args!(args);
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
}; };
let Value::Int(y) = y else { let Value::Int(y) = y else {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}") throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}")
}; };
let mut g = gcd_inner(x, y); let g = gcd_inner(x, y);
let mut prod = y; if g == 0 {
for a in rest { Ok(Value::from(0))
let Value::Int(a) = a else { } else {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {a:#}") (Value::from(x) / Value::from(g))? * Value::from(y)
};
prod *= a;
g = gcd_inner(g, a);
} }
Ok((x/g * prod).into()) }
#[native_func(1)]
pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?;
let mut l = 1;
while let Some(a) = vmcalliter!(vm; args.clone())? {
let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
};
let g = gcd_inner(l, a);
if g == 0 {
return Ok(Value::from(0))
};
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?;
let Value::Int(new_l) = new_l else {
unreachable!("int//int * int != int")
};
l = new_l;
}
Ok(Value::from(l))
} }
#[native_func(1)] #[native_func(1)]
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
let Value::Int(mut x) = x else { let Value::Int(mut x) = x else {
throw!(*SYM_TYPE_ERROR, "factords expected integer argument, got {x:#}") throw!(
*SYM_TYPE_ERROR,
"factors expected integer argument, got {x:#}"
)
}; };
let mut factors = Vec::new(); let mut factors = Vec::new();
if x <= 1 { if x <= 1 {
@ -396,32 +439,77 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(factors.into()) Ok(factors.into())
} }
fn totient_prime(n: &mut u64, p: u64) -> u64 {
if *n % p != 0 {
return 1
}
*n /= p;
let mut v = p - 1;
while *n % p == 0 {
*n /= p;
v *= p;
}
v
}
#[native_func(1)]
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(
*SYM_TYPE_ERROR,
"totient expected integer argument, got {x:#}"
)
};
if x <= 1 {
return Ok(1.into())
}
let mut x = x as u64;
let mut totient = 1;
if x & 1 == 0 {
x >>= 1;
}
while x & 1 == 0 {
x >>= 1;
totient <<= 1;
}
totient *= totient_prime(&mut x, 3);
let mut i = 5;
while x >= i * i {
totient *= totient_prime(&mut x, i);
i += 2;
totient *= totient_prime(&mut x, i);
i += 4;
}
if x > 1 {
totient *= x - 1;
}
Ok((totient as i64).into())
}
// //
// numeric operations // numeric operations
// //
#[native_func(1..)] #[native_func(2)]
pub fn min(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn min(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, mut x], rest) = unpack_varargs!(args); let [_, x, y] = unpack_args!(args);
for val in rest { if y < x {
if val < x { Ok(y)
x = val; } else {
}
}
Ok(x) Ok(x)
} }
}
#[native_func(1..)] #[native_func(2)]
pub fn max(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn max(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let ([_, mut x], rest) = unpack_varargs!(args); let [_, x, y] = unpack_args!(args);
for val in rest { if y > x {
if val > x { Ok(y)
x = val; } else {
}
}
Ok(x) Ok(x)
} }
}
#[native_func(1)] #[native_func(1)]
pub fn floor(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn floor(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
@ -488,7 +576,7 @@ pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ordering::Greater => Ok(Value::Ratio(1.into())), Ordering::Greater => Ok(Value::Ratio(1.into())),
Ordering::Less => Ok(Value::Ratio((-1).into())), Ordering::Less => Ok(Value::Ratio((-1).into())),
Ordering::Equal => Ok(Value::Ratio(0.into())), Ordering::Equal => Ok(Value::Ratio(0.into())),
} },
x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"), x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"),
} }
} }
@ -497,7 +585,6 @@ pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// floating-point operations // floating-point operations
// //
#[native_func(1)] #[native_func(1)]
pub fn signum(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn signum(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
@ -512,7 +599,10 @@ pub fn classify(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
let x = to_floaty(x); let x = to_floaty(x);
let Value::Float(x) = x else { let Value::Float(x) = x else {
throw!(*SYM_TYPE_ERROR, "classify expected real argument, got {x:#}") throw!(
*SYM_TYPE_ERROR,
"classify expected real argument, got {x:#}"
)
}; };
Ok(match x.classify() { Ok(match x.classify() {
std::num::FpCategory::Nan => *SYM_NAN, std::num::FpCategory::Nan => *SYM_NAN,
@ -520,7 +610,8 @@ pub fn classify(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
std::num::FpCategory::Zero => *SYM_ZERO, std::num::FpCategory::Zero => *SYM_ZERO,
std::num::FpCategory::Subnormal => *SYM_SUBNORMAL, std::num::FpCategory::Subnormal => *SYM_SUBNORMAL,
std::num::FpCategory::Normal => *SYM_NORMAL, std::num::FpCategory::Normal => *SYM_NORMAL,
}.into()) }
.into())
} }
#[native_func(1)] #[native_func(1)]
@ -530,7 +621,10 @@ pub fn isnan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Int(_) | Value::Ratio(_) => Ok(false.into()), Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
Value::Float(x) => Ok(x.is_nan().into()), Value::Float(x) => Ok(x.is_nan().into()),
Value::Complex(z) => Ok(z.is_nan().into()), Value::Complex(z) => Ok(z.is_nan().into()),
v => throw!(*SYM_TYPE_ERROR, "isnan expected numeric argument, got {v:#}"), v => throw!(
*SYM_TYPE_ERROR,
"isnan expected numeric argument, got {v:#}"
),
} }
} }
@ -541,7 +635,10 @@ pub fn isfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Int(_) | Value::Ratio(_) => Ok(true.into()), Value::Int(_) | Value::Ratio(_) => Ok(true.into()),
Value::Float(x) => Ok(x.is_finite().into()), Value::Float(x) => Ok(x.is_finite().into()),
Value::Complex(z) => Ok(z.is_finite().into()), Value::Complex(z) => Ok(z.is_finite().into()),
v => throw!(*SYM_TYPE_ERROR, "isfinite expected numeric argument, got {v:#}"), v => throw!(
*SYM_TYPE_ERROR,
"isfinite expected numeric argument, got {v:#}"
),
} }
} }
@ -552,7 +649,10 @@ pub fn isinfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Int(_) | Value::Ratio(_) => Ok(false.into()), Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
Value::Float(x) => Ok(x.is_infinite().into()), Value::Float(x) => Ok(x.is_infinite().into()),
Value::Complex(z) => Ok(z.is_infinite().into()), Value::Complex(z) => Ok(z.is_infinite().into()),
v => throw!(*SYM_TYPE_ERROR, "isinfinite expected numeric argument, got {v:#}"), v => throw!(
*SYM_TYPE_ERROR,
"isinfinite expected numeric argument, got {v:#}"
),
} }
} }
@ -560,7 +660,10 @@ pub fn isinfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args); let [_, val] = unpack_args!(args);
let Value::Float(f) = val else { let Value::Float(f) = val else {
throw!(*SYM_TYPE_ERROR, "float_to_bits expected float argument, got {val:#}") throw!(
*SYM_TYPE_ERROR,
"float_to_bits expected float argument, got {val:#}"
)
}; };
Ok(Value::Int(f.to_bits() as i64)) Ok(Value::Int(f.to_bits() as i64))
} }
@ -569,25 +672,28 @@ pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn float_of_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn float_of_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args); let [_, val] = unpack_args!(args);
let Value::Int(i) = val else { let Value::Int(i) = val else {
throw!(*SYM_TYPE_ERROR, "float_of_bits expected integer argument, got {val:#}") throw!(
*SYM_TYPE_ERROR,
"float_of_bits expected integer argument, got {val:#}"
)
}; };
Ok(Value::Float(f64::from_bits(i as u64))) Ok(Value::Float(f64::from_bits(i as u64)))
} }
// //
// rational operations // rational operations
// //
#[native_func(1)] #[native_func(1)]
pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args); let [_, v] = unpack_args!(args);
match v { match v {
Value::Int(x) => Ok(Value::Int(x)), Value::Int(x) => Ok(Value::Int(x)),
Value::Ratio(x) => Ok(Value::Int(*x.numer())), Value::Ratio(x) => Ok(Value::Int(*x.numer())),
v => throw!(*SYM_TYPE_ERROR, "numer expected rational argument, got {v:#}"), v => throw!(
*SYM_TYPE_ERROR,
"numer expected rational argument, got {v:#}"
),
} }
} }
@ -597,7 +703,10 @@ pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match v { match v {
Value::Int(_) => Ok(Value::Int(1)), Value::Int(_) => Ok(Value::Int(1)),
Value::Ratio(x) => Ok(Value::Int(*x.denom())), Value::Ratio(x) => Ok(Value::Int(*x.denom())),
v => throw!(*SYM_TYPE_ERROR, "denom expected rational argument, got {v:#}"), v => throw!(
*SYM_TYPE_ERROR,
"denom expected rational argument, got {v:#}"
),
} }
} }
@ -605,8 +714,6 @@ pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// complex operations // complex operations
// //
#[native_func(1)] #[native_func(1)]
pub fn re(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn re(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args); let [_, v] = unpack_args!(args);
@ -643,34 +750,28 @@ pub fn arg(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)] #[native_func(1)]
pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
match x { x.abs()
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)] #[native_func(1)]
pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
match x { match x {
Value::Int(x) => Ok(Value::Int(x * x)), Value::Int(_) => x.clone() * x,
Value::Ratio(x) => Ok(Value::Ratio(x * x)), Value::Ratio(_) => x.clone() * x,
Value::Float(x) => Ok(Value::Float(x * x)), Value::Float(_) => x.clone() * x,
Value::Complex(x) => Ok(Value::Float(x.norm_sqr())), Value::Complex(x) => Ok(Value::Float(x.norm_sqr())),
x => throw!(*SYM_TYPE_ERROR, "abs_sq expected numeric argument, got {x:#}"), x => throw!(
*SYM_TYPE_ERROR,
"abs_sq expected numeric argument, got {x:#}"
),
} }
} }
// //
// continuous operations // continuous operations
// //
macro_rules! float_func { macro_rules! float_func {
($name:ident) => { ($name:ident) => {
#[native_func(1)] #[native_func(1)]
@ -679,8 +780,11 @@ macro_rules! float_func {
match to_floaty(v) { match to_floaty(v) {
Value::Float(x) => Ok(Value::Float(x.$name())), Value::Float(x) => Ok(Value::Float(x.$name())),
Value::Complex(z) => Ok(Value::Complex(z.$name())), Value::Complex(z) => Ok(Value::Complex(z.$name())),
v => throw!(*SYM_TYPE_ERROR, v => throw!(
"{} expected numeric argument, got {v:#}", stringify!($name)), *SYM_TYPE_ERROR,
"{} expected numeric argument, got {v:#}",
stringify!($name)
),
} }
} }
}; };
@ -690,6 +794,7 @@ float_func!(sqrt);
float_func!(cbrt); float_func!(cbrt);
float_func!(ln); float_func!(ln);
float_func!(log2); float_func!(log2);
float_func!(log10);
float_func!(exp); float_func!(exp);
float_func!(exp2); float_func!(exp2);
@ -710,10 +815,10 @@ float_func!(atanh);
pub fn atan2(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn atan2(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, y, x] = unpack_args!(args); let [_, y, x] = unpack_args!(args);
match (to_floaty(y), to_floaty(x)) { match (to_floaty(y), to_floaty(x)) {
(Value::Float(y), Value::Float(x)) (Value::Float(y), Value::Float(x)) => Ok(Value::Float(x.atan2(y))),
=> Ok(Value::Float(x.atan2(y))), (y, x) => throw!(
(y,x) => throw!(*SYM_TYPE_ERROR, *SYM_TYPE_ERROR,
"atan2 expected real arguments, got {y:#} and {x:#}"), "atan2 expected real arguments, got {y:#} and {x:#}"
),
} }
} }

View file

@ -1,5 +1,11 @@
use rand::{seq::SliceRandom, Rng}; use rand::{seq::SliceRandom, Rng};
use talc_lang::{symbol::SYM_TYPE_ERROR, throw, value::{range::RangeType, Value}, vmcalliter, Vm, exception::Result}; use talc_lang::{
exception::Result,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{range::RangeType, Value},
vmcalliter, Vm,
};
use talc_macros::native_func; use talc_macros::native_func;
use crate::unpack_args; use crate::unpack_args;
@ -22,29 +28,31 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::List(l) => { Value::List(l) => {
let l = l.borrow(); let l = l.borrow();
let Some(v) = l.choose(&mut rand::thread_rng()) else { let Some(v) = l.choose(&mut rand::thread_rng()) else {
throw!(*SYM_TYPE_ERROR, "rand_in: empty list") throw!(*SYM_VALUE_ERROR, "rand_in: empty list")
}; };
Ok(v.clone()) Ok(v.clone())
}, }
Value::Table(t) => { Value::Table(t) => {
let t = t.borrow(); let t = t.borrow();
if t.is_empty() { if t.is_empty() {
throw!(*SYM_TYPE_ERROR, "rand_in: empty table") throw!(*SYM_VALUE_ERROR, "rand_in: empty table")
}; };
let i = rand::thread_rng().gen_range(0..t.len()); let i = rand::thread_rng().gen_range(0..t.len());
let key = t.keys().nth(i).unwrap(); let key = t.keys().nth(i).unwrap();
Ok(key.clone().into_inner()) Ok(key.clone().into_inner())
}, }
Value::Range(r) => { Value::Range(r) => {
if r.is_empty() { if r.is_empty() {
throw!(*SYM_TYPE_ERROR, "rand_in: empty range") throw!(*SYM_VALUE_ERROR, "rand_in: empty range")
} }
match r.ty { match r.ty {
RangeType::Open => Ok(Value::Int( RangeType::Open => {
rand::thread_rng().gen_range(r.start..r.stop))), 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::Closed => {
RangeType::Endless => throw!(*SYM_TYPE_ERROR, "rand_in: endless range"), Ok(Value::Int(rand::thread_rng().gen_range(r.start..=r.stop)))
}
RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"),
} }
} }
col => { col => {
@ -54,7 +62,7 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
values.push(v); values.push(v);
} }
if values.is_empty() { if values.is_empty() {
throw!(*SYM_TYPE_ERROR, "rand_in: empty iterator") throw!(*SYM_VALUE_ERROR, "rand_in: empty iterator")
} }
let i = rand::thread_rng().gen_range(0..values.len()); let i = rand::thread_rng().gen_range(0..values.len());
let v = values.swap_remove(i); let v = values.swap_remove(i);

View file

@ -1,9 +1,16 @@
use std::borrow::Cow; use std::borrow::Cow;
use talc_lang::{exception::{exception, Result}, lstring::LString, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{NativeValue, Value}, Vm};
use talc_macros::native_func;
use regex::{Captures, Match, Regex};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use regex::{Captures, Match, Regex};
use talc_lang::{
exception::{exception, Result},
lstring::LString,
symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{NativeValue, Value},
Vm,
};
use talc_macros::native_func;
use crate::unpack_args; use crate::unpack_args;
@ -18,17 +25,30 @@ lazy_static! {
pub struct ValueRegex(Regex); pub struct ValueRegex(Regex);
impl From<Regex> for ValueRegex { impl From<Regex> for ValueRegex {
fn from(value: Regex) -> Self { Self(value) } fn from(value: Regex) -> Self {
Self(value)
}
} }
impl From<ValueRegex> for Regex { impl From<ValueRegex> for Regex {
fn from(value: ValueRegex) -> Self { value.0 } fn from(value: ValueRegex) -> Self {
value.0
}
} }
impl NativeValue for ValueRegex { impl NativeValue for ValueRegex {
fn get_type(&self) -> Symbol { *SYM_STD_REGEX } fn get_type(&self) -> Symbol {
fn as_any(&self) -> &dyn std::any::Any { self } *SYM_STD_REGEX
fn to_lstring(&self, w: &mut LString, repr: bool) -> std::io::Result<()> { }
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn to_lstring(
&self,
w: &mut LString,
repr: bool,
_recur: &mut Vec<*const ()>,
) -> std::io::Result<()> {
use std::io::Write; use std::io::Write;
if repr { if repr {
write!(w, "/{}/", self.0) write!(w, "/{}/", self.0)
@ -58,7 +78,10 @@ fn match_to_value(m: Match) -> Value {
Value::new_table(|t| { Value::new_table(|t| {
t.insert((*SYM_START).into(), (m.start() as i64).into()); t.insert((*SYM_START).into(), (m.start() as i64).into());
t.insert((*SYM_END).into(), (m.end() as i64).into()); t.insert((*SYM_END).into(), (m.end() as i64).into());
t.insert((*SYM_STR).into(), LString::from(m.as_str().to_string()).into()); t.insert(
(*SYM_STR).into(),
LString::from(m.as_str().to_string()).into(),
);
}) })
} }
@ -73,27 +96,33 @@ fn regex_from<'a>(v: &'a Value, name: &str) -> Result<Cow<'a, Regex>> {
match v { match v {
Value::String(s) => { Value::String(s) => {
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "regex must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "regex must be valid UTF-8")
}; };
Regex::new(s) Regex::new(s)
.map(Cow::Owned) .map(Cow::Owned)
.map_err(|e| exception!(*SYM_TYPE_ERROR, "invalid regex: {e}")) .map_err(|e| exception!(*SYM_VALUE_ERROR, "invalid regex: {e}"))
}, }
Value::Native(n) if n.get_type() == *SYM_STD_REGEX => { Value::Native(n) if n.get_type() == *SYM_STD_REGEX => n
n.as_any().downcast_ref::<ValueRegex>() .as_any()
.downcast_ref::<ValueRegex>()
.map(|vr| Cow::Borrowed(&vr.0)) .map(|vr| Cow::Borrowed(&vr.0))
.ok_or_else(|| exception!( .ok_or_else(|| {
*SYM_TYPE_ERROR, "BEES {name} expected string or regex, got {v:#}")) exception!(
}, *SYM_TYPE_ERROR,
_ => throw!(*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}") "{name} expected string or regex, got {v:#}"
)
}),
_ => throw!(
*SYM_TYPE_ERROR,
"{name} expected string or regex, got {v:#}"
),
} }
} }
#[native_func(1)] #[native_func(1)]
pub fn _regex(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn _regex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, re] = unpack_args!(args); let [_, re] = unpack_args!(args);
regex_from(&re, "regex") regex_from(&re, "regex").map(|re| ValueRegex(re.into_owned()).into())
.map(|re| ValueRegex(re.into_owned()).into())
} }
#[native_func(2)] #[native_func(2)]
@ -103,7 +132,7 @@ pub fn matches(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "matches expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "matches expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let re = regex_from(&re, "matches")?; let re = regex_from(&re, "matches")?;
Ok(re.is_match(s).into()) Ok(re.is_match(s).into())
@ -116,7 +145,7 @@ pub fn match_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "match_once expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "match_once expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let re = regex_from(&re, "match_once")?; let re = regex_from(&re, "match_once")?;
Ok(re.find(s).map_or(Value::Nil, match_to_value)) Ok(re.find(s).map_or(Value::Nil, match_to_value))
@ -129,10 +158,14 @@ pub fn _match(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "match expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "match expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let re = regex_from(&re, "match")?; let re = regex_from(&re, "match")?;
Ok(re.find_iter(s).map(match_to_value).collect::<Vec<Value>>().into()) Ok(re
.find_iter(s)
.map(match_to_value)
.collect::<Vec<Value>>()
.into())
} }
#[native_func(2)] #[native_func(2)]
@ -142,7 +175,7 @@ pub fn captures_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "captures_once expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "captures_once expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let re = regex_from(&re, "captures_once")?; let re = regex_from(&re, "captures_once")?;
Ok(re.captures(s).map_or(Value::Nil, captures_to_value)) Ok(re.captures(s).map_or(Value::Nil, captures_to_value))
@ -155,10 +188,14 @@ pub fn captures(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "captures expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "captures expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let re = regex_from(&re, "captures")?; let re = regex_from(&re, "captures")?;
Ok(re.captures_iter(s).map(captures_to_value).collect::<Vec<Value>>().into()) Ok(re
.captures_iter(s)
.map(captures_to_value)
.collect::<Vec<Value>>()
.into())
} }
#[native_func(3)] #[native_func(3)]
@ -168,13 +205,16 @@ pub fn replace_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "replace_once expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "replace_once expected string, got {s:#}")
}; };
let Value::String(rep) = rep else { let Value::String(rep) = rep else {
throw!(*SYM_TYPE_ERROR, "replace_once expected string or function, got {rep:#}") throw!(
*SYM_TYPE_ERROR,
"replace_once expected string or function, got {rep:#}"
)
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let Ok(rep) = rep.to_str() else { let Ok(rep) = rep.to_str() else {
throw!(*SYM_TYPE_ERROR, "replacement string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "replacement string must be valid UTF-8")
}; };
let re = regex_from(&re, "replace_once")?; let re = regex_from(&re, "replace_once")?;
Ok(LString::from(re.replace(s, rep)).into()) Ok(LString::from(re.replace(s, rep)).into())
@ -187,13 +227,16 @@ pub fn replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "replace expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "replace expected string, got {s:#}")
}; };
let Value::String(rep) = rep else { let Value::String(rep) = rep else {
throw!(*SYM_TYPE_ERROR, "replace expected string or function, got {rep:#}") throw!(
*SYM_TYPE_ERROR,
"replace expected string or function, got {rep:#}"
)
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
}; };
let Ok(rep) = rep.to_str() else { let Ok(rep) = rep.to_str() else {
throw!(*SYM_TYPE_ERROR, "replacement string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "replacement string must be valid UTF-8")
}; };
let re = regex_from(&re, "replace")?; let re = regex_from(&re, "replace")?;
Ok(LString::from(re.replace_all(s, rep)).into()) Ok(LString::from(re.replace_all(s, rep)).into())
@ -206,13 +249,13 @@ pub fn split_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "split_once expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "split_once expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "string to split must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "string to split must be valid UTF-8")
}; };
let re = regex_from(&re, "split_once")?; let re = regex_from(&re, "split_once")?;
let mut parts = re.splitn(s, 2); let mut parts = re.splitn(s, 2);
let (part1, part2) = ( let (part1, part2) = (
LString::from(parts.next().unwrap_or_default()).into(), LString::from(parts.next().unwrap_or_default()).into(),
LString::from(parts.next().unwrap_or_default()).into() LString::from(parts.next().unwrap_or_default()).into(),
); );
Ok(vec![part1, part2].into()) Ok(vec![part1, part2].into())
} }
@ -224,12 +267,9 @@ pub fn split(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "split expected string, got {s:#}") throw!(*SYM_TYPE_ERROR, "split expected string, got {s:#}")
}; };
let Ok(s) = s.to_str() else { let Ok(s) = s.to_str() else {
throw!(*SYM_TYPE_ERROR, "string to split must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "string to split must be valid UTF-8")
}; };
let re = regex_from(&re, "split")?; let re = regex_from(&re, "split")?;
let parts: Vec<Value> = re.split(s) let parts: Vec<Value> = re.split(s).map(|s| LString::from(s).into()).collect();
.map(|s| LString::from(s).into())
.collect();
Ok(parts.into()) Ok(parts.into())
} }

View file

@ -1,4 +1,11 @@
use talc_lang::{exception::Result, lformat, lstring::LString, symbol::SYM_TYPE_ERROR, throw, value::Value, Vm}; use talc_lang::{
exception::Result,
lstring::LString,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::Value,
Vm,
};
use talc_macros::native_func; use talc_macros::native_func;
use crate::unpack_args; use crate::unpack_args;
@ -17,7 +24,6 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("to_utf8_lossy", to_utf8_lossy().into()); vm.set_global_name("to_utf8_lossy", to_utf8_lossy().into());
vm.set_global_name("str_to_bytes", str_to_bytes().into()); vm.set_global_name("str_to_bytes", str_to_bytes().into());
vm.set_global_name("str_of_bytes", str_of_bytes().into()); vm.set_global_name("str_of_bytes", str_of_bytes().into());
vm.set_global_name("format", _format().into());
} }
#[native_func(1)] #[native_func(1)]
@ -28,10 +34,10 @@ pub fn ord(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}; };
let mut chars = s.chars(); let mut chars = s.chars();
let Some(c) = chars.next() else { let Some(c) = chars.next() else {
throw!(*SYM_TYPE_ERROR, "argument to ord must have length 1") throw!(*SYM_VALUE_ERROR, "argument to ord must have length 1")
}; };
if chars.next().is_some() { if chars.next().is_some() {
throw!(*SYM_TYPE_ERROR, "argument to ord must have length 1") throw!(*SYM_VALUE_ERROR, "argument to ord must have length 1")
}; };
Ok(Value::Int(c as u32 as i64)) Ok(Value::Int(c as u32 as i64))
} }
@ -43,10 +49,10 @@ pub fn chr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "chr expected integer argument, got {i:#}") throw!(*SYM_TYPE_ERROR, "chr expected integer argument, got {i:#}")
}; };
let Ok(i) = u32::try_from(i) else { let Ok(i) = u32::try_from(i) else {
throw!(*SYM_TYPE_ERROR, "argument to chr is not a valid codepoint") throw!(*SYM_VALUE_ERROR, "argument to chr is not a valid codepoint")
}; };
let Some(c) = char::from_u32(i) else { let Some(c) = char::from_u32(i) else {
throw!(*SYM_TYPE_ERROR, "argument to chr is not a valid codepoint") throw!(*SYM_VALUE_ERROR, "argument to chr is not a valid codepoint")
}; };
Ok(Value::String(LString::from(c).into())) Ok(Value::String(LString::from(c).into()))
} }
@ -55,7 +61,10 @@ pub fn chr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn len_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn len_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args); let [_, s] = unpack_args!(args);
let Value::String(s) = s else { let Value::String(s) = s else {
throw!(*SYM_TYPE_ERROR, "len_bytes expected string argument, got {s:#}") throw!(
*SYM_TYPE_ERROR,
"len_bytes expected string argument, got {s:#}"
)
}; };
Ok(Value::Int(s.len() as i64)) Ok(Value::Int(s.len() as i64))
} }
@ -93,8 +102,10 @@ pub fn starts_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let res = match (d, pre) { let res = match (d, pre) {
(Value::List(d), Value::List(pre)) => d.borrow().starts_with(&pre.borrow()), (Value::List(d), Value::List(pre)) => d.borrow().starts_with(&pre.borrow()),
(Value::String(d), Value::String(pre)) => d.starts_with(&pre), (Value::String(d), Value::String(pre)) => d.starts_with(&pre),
(d, pre) => throw!(*SYM_TYPE_ERROR, (d, pre) => throw!(
"starts_with expected two lists or strings, got {d:#} and {pre:#}") *SYM_TYPE_ERROR,
"starts_with expected two lists or strings, got {d:#} and {pre:#}"
),
}; };
Ok(res.into()) Ok(res.into())
} }
@ -105,8 +116,10 @@ pub fn ends_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let res = match (d, suf) { let res = match (d, suf) {
(Value::List(d), Value::List(suf)) => d.borrow().ends_with(&suf.borrow()), (Value::List(d), Value::List(suf)) => d.borrow().ends_with(&suf.borrow()),
(Value::String(d), Value::String(suf)) => d.ends_with(&suf), (Value::String(d), Value::String(suf)) => d.ends_with(&suf),
(d, suf) => throw!(*SYM_TYPE_ERROR, (d, suf) => throw!(
"ends_with expected two lists or strings, got {d:#} and {suf:#}") *SYM_TYPE_ERROR,
"ends_with expected two lists or strings, got {d:#} and {suf:#}"
),
}; };
Ok(res.into()) Ok(res.into())
} }
@ -115,7 +128,10 @@ pub fn ends_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn is_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn is_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args); let [_, s] = unpack_args!(args);
let Value::String(s) = s else { let Value::String(s) = s else {
throw!(*SYM_TYPE_ERROR, "is_utf8 expected string argument, got {s:#}") throw!(
*SYM_TYPE_ERROR,
"is_utf8 expected string argument, got {s:#}"
)
}; };
Ok(s.is_utf8().into()) Ok(s.is_utf8().into())
} }
@ -124,7 +140,10 @@ pub fn is_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn to_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn to_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args); let [_, s] = unpack_args!(args);
let Value::String(s) = s else { let Value::String(s) = s else {
throw!(*SYM_TYPE_ERROR, "is_utf8 expected string argument, got {s:#}") throw!(
*SYM_TYPE_ERROR,
"is_utf8 expected string argument, got {s:#}"
)
}; };
if s.is_utf8() { if s.is_utf8() {
Ok(s.into()) Ok(s.into())
@ -137,7 +156,10 @@ pub fn to_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn to_utf8_lossy(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn to_utf8_lossy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args); let [_, s] = unpack_args!(args);
let Value::String(s) = s else { let Value::String(s) = s else {
throw!(*SYM_TYPE_ERROR, "is_utf8 expected string argument, got {s:#}") throw!(
*SYM_TYPE_ERROR,
"is_utf8 expected string argument, got {s:#}"
)
}; };
Ok(s.to_utf8_lossy().into()) Ok(s.to_utf8_lossy().into())
} }
@ -146,7 +168,10 @@ pub fn to_utf8_lossy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn str_to_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn str_to_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args); let [_, s] = unpack_args!(args);
let Value::String(s) = s else { let Value::String(s) = s else {
throw!(*SYM_TYPE_ERROR, "str_to_bytes expected string argument, got {s:#}") throw!(
*SYM_TYPE_ERROR,
"str_to_bytes expected string argument, got {s:#}"
)
}; };
Ok(s.as_bytes() Ok(s.as_bytes()
.iter() .iter()
@ -159,47 +184,21 @@ pub fn str_to_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, b] = unpack_args!(args); let [_, b] = unpack_args!(args);
let Value::List(b) = b else { let Value::List(b) = b else {
throw!(*SYM_TYPE_ERROR, "str_of_bytes expected list argument, got {b:#}") throw!(
*SYM_TYPE_ERROR,
"str_of_bytes expected list argument, got {b:#}"
)
}; };
let bytes: Vec<u8> = b.borrow().iter() let bytes: Vec<u8> = b
.borrow()
.iter()
.map(|v| match v { .map(|v| match v {
Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8), Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8),
_ => throw!(*SYM_TYPE_ERROR, "str_of_bytes expected list of integers in 0..=255"), _ => throw!(
}).collect::<Result<Vec<u8>>>()?; *SYM_VALUE_ERROR,
"str_of_bytes expected list of integers in 0..=255"
),
})
.collect::<Result<Vec<u8>>>()?;
Ok(LString::from(bytes).into()) Ok(LString::from(bytes).into())
} }
#[native_func(2)]
pub fn _format(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, fstr, fargs] = unpack_args!(args);
let (Value::String(fstr), Value::List(fargs)) = (&fstr, &fargs) else {
throw!(*SYM_TYPE_ERROR, "format expected string and list, got {fstr:#} and {fargs:#}")
};
let mut res = LString::new();
let mut bytes = fstr.bytes();
let mut faidx = 0;
while let Some(b) = bytes.next() {
if b == b'%' {
match bytes.next() {
Some(b'%') => res.push_byte(b'%'),
Some(b @ (b'#' | b'?')) => {
let fargs = fargs.borrow();
let Some(a) = fargs.get(faidx) else {
throw!(*SYM_TYPE_ERROR, "not enough args for format string")
};
faidx += 1;
if b == b'?' {
res += &lformat!("{a:#}");
} else {
res += &lformat!("{a}");
}
},
_ => throw!(*SYM_TYPE_ERROR, "invalid format code")
}
} else {
res.push_byte(b);
}
}
Ok(res.into())
}

View file

@ -1,6 +1,14 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{exception::{exception, Result}, lformat, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, value::{ops::RatioExt, HashValue, Rational64, Value}, Vm}; use talc_lang::{
exception::{exception, Result},
lformat,
parser::{parse_float, parse_int},
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{ops::RatioExt, HashValue, Rational64, Value},
Vm,
};
use talc_macros::native_func; use talc_macros::native_func;
use crate::unpack_args; use crate::unpack_args;
@ -14,18 +22,27 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("str", str_().into()); vm.set_global_name("str", str_().into());
vm.set_global_name("repr", repr().into()); vm.set_global_name("repr", repr().into());
vm.set_global_name("symbol_name", symbol_name().into());
vm.set_global_name("symbol_of", symbol_of().into());
vm.set_global_name("symbol_exists", symbol_exists().into());
vm.set_global_name("cell", cell().into()); vm.set_global_name("cell", cell().into());
vm.set_global_name("uncell", uncell().into()); vm.set_global_name("uncell", uncell().into());
vm.set_global_name("cell_replace", cell_replace().into()); vm.set_global_name("cell_replace", cell_replace().into());
vm.set_global_name("cell_take", cell_replace().into()); vm.set_global_name("cell_take", cell_take().into());
vm.set_global_name("func_state", func_state().into());
vm.set_global_name("func_arity", func_arity().into());
vm.set_global_name("func_name", func_name().into());
vm.set_global_name("apply", apply().into());
vm.set_global_name("compile", compile().into());
} }
// //
// types // types
// //
#[native_func(1, "type")]
#[native_func(1)]
pub fn type_(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn type_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args); let [_, val] = unpack_args!(args);
Ok(val.get_type().into()) Ok(val.get_type().into())
@ -40,7 +57,7 @@ pub fn is(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok((val.get_type() == ty).into()) Ok((val.get_type() == ty).into())
} }
#[native_func(2)] #[native_func(2, "as")]
pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val, ty] = unpack_args!(args); let [_, val, ty] = unpack_args!(args);
let Value::Symbol(ty) = ty else { let Value::Symbol(ty) = ty else {
@ -64,59 +81,75 @@ pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
(Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64().into())), (Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64().into())),
(Value::Float(x), b"int") => Ok(Value::Int(x as i64)), (Value::Float(x), b"int") => Ok(Value::Int(x as i64)),
(Value::Float(x), b"ratio") => { (Value::Float(x), b"ratio") => {
let r = Rational64::approximate_float(x) let r = Rational64::approximate_float(x).ok_or_else(|| {
.ok_or_else(|| exception!(*SYM_TYPE_ERROR, "float {x:?} could not be converted to ratio"))?; exception!(
*SYM_VALUE_ERROR,
"float {x:?} could not be converted to ratio"
)
})?;
Ok(Value::Ratio(r)) Ok(Value::Ratio(r))
} }
(Value::Float(x), b"complex") => Ok(Value::Complex(x.into())), (Value::Float(x), b"complex") => Ok(Value::Complex(x.into())),
(Value::String(s), b"int") (Value::String(s), b"int") => parse_int(s.as_ref(), 10)
=> parse_int(s.as_ref(), 10)
.map(i64::into) .map(i64::into)
.map_err(|_| exception!(*SYM_TYPE_ERROR, "could not parse {s:#} as integer")), .map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as integer")),
(Value::String(s), b"float") (Value::String(s), b"float") => parse_float(s.as_ref())
=> parse_float(s.as_ref())
.map(f64::into) .map(f64::into)
.map_err(|_| exception!(*SYM_TYPE_ERROR, "could not parse {s:#} as float")), .map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as float")),
(v, _) => throw!(*SYM_TYPE_ERROR, (v, _) => throw!(
"cannot convert value of type {} to type {}", v.get_type().name(), ty.name()) *SYM_TYPE_ERROR,
"cannot convert value of type {} to type {}",
v.get_type().name(),
ty.name()
),
} }
} }
pub fn copy_inner(value: Value) -> Result<Value> { pub fn copy_inner(value: Value) -> Result<Value> {
match value { match value {
Value::Nil | Value::Bool(_) | Value::Symbol(_) Value::Nil
| Value::Int(_) | Value::Ratio(_) | Value::Float(_) | Value::Bool(_)
| Value::Complex(_) | Value::Range(_) | Value::String(_) | Value::Symbol(_)
=> Ok(value), | Value::Int(_)
| Value::Ratio(_)
| Value::Float(_)
| Value::Complex(_)
| Value::Range(_)
| Value::String(_) => Ok(value),
Value::Cell(c) => { Value::Cell(c) => {
let c = Rc::unwrap_or_clone(c).take(); let c = Rc::unwrap_or_clone(c).take();
let c = copy_inner(c)?; let c = copy_inner(c)?;
Ok(RefCell::new(c).into()) Ok(RefCell::new(c).into())
}, }
Value::List(l) => { Value::List(l) => {
let l = Rc::unwrap_or_clone(l).take(); let l = Rc::unwrap_or_clone(l).take();
let v: Result<Vec<Value>> = l.into_iter() let v: Result<Vec<Value>> = l.into_iter().map(copy_inner).collect();
.map(copy_inner)
.collect();
Ok(v?.into()) Ok(v?.into())
}, }
Value::Table(t) => { Value::Table(t) => {
let t = Rc::unwrap_or_clone(t).take(); let t = Rc::unwrap_or_clone(t).take();
let v: Result<HashMap<HashValue, Value>> = t.into_iter() let v: Result<HashMap<HashValue, Value>> = t
.into_iter()
.map(|(k, v)| copy_inner(v).map(|v| (k, v))) .map(|(k, v)| copy_inner(v).map(|v| (k, v)))
.collect(); .collect();
Ok(v?.into()) Ok(v?.into())
}, }
Value::Native(ref n) => match n.copy_value()? { Value::Native(ref n) => match n.copy_value()? {
Some(x) => Ok(x), Some(x) => Ok(x),
None => throw!(*SYM_TYPE_ERROR, None => throw!(
"cannot copy value of type {}", value.get_type().name()) *SYM_TYPE_ERROR,
} "cannot copy value of type {}",
_ => throw!(*SYM_TYPE_ERROR, value.get_type().name()
"cannot copy value of type {}", value.get_type().name()) ),
},
_ => throw!(
*SYM_TYPE_ERROR,
"cannot copy value of type {}",
value.get_type().name()
),
} }
} }
@ -130,8 +163,7 @@ pub fn copy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// strings // strings
// //
#[native_func(1, "str")]
#[native_func(1)]
pub fn str_(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn str_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args); let [_, val] = unpack_args!(args);
Ok(lformat!("{val}").into()) Ok(lformat!("{val}").into())
@ -144,9 +176,42 @@ pub fn repr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} }
// //
// cells // symbols
// //
#[native_func(1)]
pub fn symbol_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::Symbol(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_name: expected symbol")
};
Ok(s.name().into())
}
#[native_func(1)]
pub fn symbol_of(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::String(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
};
Ok(Symbol::get(s.as_ref()).into())
}
#[native_func(1)]
pub fn symbol_exists(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::String(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
};
match Symbol::try_get(s.as_ref()) {
Some(s) => Ok(s.into()),
None => Ok(Value::Nil),
}
}
//
// cells
//
#[native_func(1)] #[native_func(1)]
pub fn cell(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn cell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
@ -158,7 +223,7 @@ pub fn cell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn uncell(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn uncell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, cell] = unpack_args!(args); let [_, cell] = unpack_args!(args);
let Value::Cell(cell) = cell else { let Value::Cell(cell) = cell else {
throw!(*SYM_TYPE_ERROR, "value is not a cell") throw!(*SYM_TYPE_ERROR, "uncell: value is not a cell")
}; };
Ok(Rc::unwrap_or_clone(cell).into_inner()) Ok(Rc::unwrap_or_clone(cell).into_inner())
} }
@ -167,7 +232,7 @@ pub fn uncell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn cell_replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn cell_replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, cell, value] = unpack_args!(args); let [_, cell, value] = unpack_args!(args);
let Value::Cell(cell) = cell else { let Value::Cell(cell) = cell else {
throw!(*SYM_TYPE_ERROR, "value is not a cell") throw!(*SYM_TYPE_ERROR, "cell_replace: value is not a cell")
}; };
Ok(cell.replace(value)) Ok(cell.replace(value))
} }
@ -176,8 +241,90 @@ pub fn cell_replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn cell_take(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn cell_take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, cell] = unpack_args!(args); let [_, cell] = unpack_args!(args);
let Value::Cell(cell) = cell else { let Value::Cell(cell) = cell else {
throw!(*SYM_TYPE_ERROR, "value is not a cell") throw!(*SYM_TYPE_ERROR, "cell_take: value is not a cell")
}; };
Ok(cell.replace(Value::Nil)) Ok(cell.replace(Value::Nil))
} }
//
// functions
//
#[native_func(1)]
pub fn func_state(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
match func {
Value::NativeFunc(_) => Ok(Value::Nil),
Value::Function(f) => {
let l: Vec<Value> = f.state.iter().map(|v| Value::Cell(v.clone())).collect();
Ok(l.into())
}
_ => throw!(
*SYM_TYPE_ERROR,
"closure_state: {func:#} is not a talc function"
),
}
}
#[native_func(1)]
pub fn func_arity(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
let Some(attrs) = func.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
};
Ok((attrs.arity as i64).into())
}
#[native_func(1)]
pub fn func_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
let Some(attrs) = func.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
};
if let Some(name) = attrs.name {
Ok(name.into())
} else {
Ok(Value::Nil)
}
}
#[native_func(2)]
pub fn apply(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func, lst] = unpack_args!(args);
if func.func_attrs().is_none() {
throw!(
*SYM_TYPE_ERROR,
"apply: first argument must be a function, found {func:#}"
)
}
let Value::List(l) = lst else {
throw!(
*SYM_TYPE_ERROR,
"apply: second argument must be a list, found {lst:#}"
)
};
let mut args = l.borrow().clone();
args.insert(0, func.clone());
vm.call_value(func, args)
}
#[native_func(1)]
pub fn compile(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, src] = unpack_args!(args);
let Value::String(src) = src else {
throw!(
*SYM_TYPE_ERROR,
"compile: argument must be a string, found {src:#}"
)
};
let src = src.to_str().map_err(|e| {
exception!(
*SYM_VALUE_ERROR,
"compile: argument must be valid unicode ({e})"
)
})?;
let ast = talc_lang::parser::parse(src)
.map_err(|e| exception!(symbol!("parse_error"), "{e}"))?;
let func = talc_lang::compiler::compile(&ast, None);
Ok(func.into())
}