This commit is contained in:
trimill 2024-11-04 13:25:31 -05:00
parent 801aaf7b78
commit ba7db1e91b
37 changed files with 4123 additions and 3667 deletions

3
format.sh Executable file
View file

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

10
rustfmt.toml Normal file
View file

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

View file

@ -1,7 +1,17 @@
use std::{borrow::Cow, cell::RefCell, 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, parser::{Lexer, Pos, Span, TokenKind}, Vm}; completion::Completer,
highlight::Highlighter,
hint::Hinter,
validate::{ValidationContext, ValidationResult, Validator},
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>>,
@ -9,9 +19,7 @@ pub struct TalcHelper {
impl TalcHelper { impl TalcHelper {
pub fn new(vm: Rc<RefCell<Vm>>) -> Self { pub fn new(vm: Rc<RefCell<Vm>>) -> Self {
Self { Self { vm }
vm,
}
} }
} }
@ -35,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)
@ -55,23 +67,27 @@ impl Highlighter for TalcHelper {
let mut buf = String::new(); let mut buf = String::new();
let mut last = Pos::new(); let mut last = Pos::new();
while let Some(Ok(token)) = lexer.next() { while let Some(Ok(token)) = lexer.next() {
if token.kind == TokenKind::Eof { break } if token.kind == TokenKind::Eof {
break
}
buf += Span::new(last, token.span.start).of(line); buf += Span::new(last, token.span.start).of(line);
last = token.span.end; last = token.span.end;
let format = match token.kind { let format = match token.kind {
TokenKind::Nil TokenKind::Nil
| TokenKind::True | TokenKind::True
| TokenKind::False | TokenKind::False
| TokenKind::Integer | TokenKind::Integer
| TokenKind::Float | TokenKind::Float
| TokenKind::Imaginary => "\x1b[93m", | TokenKind::Imaginary => "\x1b[93m",
TokenKind::String => "\x1b[92m", TokenKind::String => "\x1b[92m",
TokenKind::Symbol => "\x1b[96m", TokenKind::Symbol => "\x1b[96m",
_ => "", _ => "",
}; };
buf += format; buf += format;
buf += token.content; buf += token.content;
if !format.is_empty() { buf += "\x1b[0m" } if !format.is_empty() {
buf += "\x1b[0m"
}
} }
buf += &line[(last.idx as usize)..]; buf += &line[(last.idx as usize)..];
Cow::Owned(buf) Cow::Owned(buf)
@ -96,73 +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> {
use TokenKind as K; use TokenKind as K;
let lexer = Lexer::new(ctx.input()); 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 lexer { for token in lexer {
let token = match token { let token = match token {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => return Ok(ValidationResult::Invalid(Some(format!(" {e}")))),
return Ok(ValidationResult::Invalid(
Some(format!(" {e}"))))
}
}; };
let k = token.kind; let k = token.kind;
let s = token.span; let s = token.span;
match k { match k {
K::Eof => break, K::Eof => break,
K::LParen K::LParen | K::LBrack | K::LBrace | K::If | K::While | K::For | K::Try => {
| K::LBrack delims.push(token.kind)
| K::LBrace }
| K::If
| K::While
| K::For
| K::Try
=> delims.push(token.kind),
K::RParen => match delims.pop() { K::RParen => match delims.pop() {
Some(K::LParen) => (), Some(K::LParen) => (),
v => { mismatch = Some((v, k, s)); break } v => {
mismatch = Some((v, k, s));
break
}
}, },
K::RBrack => match delims.pop() { K::RBrack => match delims.pop() {
Some(K::LBrack) => (), Some(K::LBrack) => (),
v => { mismatch = Some((v, k, s)); break } v => {
mismatch = Some((v, k, s));
break
}
}, },
K::RBrace => match delims.pop() { K::RBrace => match delims.pop() {
Some(K::LBrace) => (), Some(K::LBrace) => (),
v => { mismatch = Some((v, k, s)); break } v => {
mismatch = Some((v, k, s));
break
}
}, },
K::Then => match delims.pop() { K::Then => match delims.pop() {
Some(K::If | K::Elif) => delims.push(k), Some(K::If | K::Elif) => delims.push(k),
v => { mismatch = Some((v, k, s)); break } v => {
} mismatch = Some((v, k, s));
break
}
},
K::Catch => match delims.pop() { K::Catch => match delims.pop() {
Some(K::Try) => delims.push(k), Some(K::Try) => delims.push(k),
v => { mismatch = Some((v, k, s)); break } v => {
} mismatch = Some((v, k, s));
break
}
},
K::Do => match delims.last().copied() { K::Do => match delims.last().copied() {
Some(K::While | K::For | K::Catch) => { Some(K::While | K::For | K::Catch) => {
delims.pop(); delims.pop();
delims.push(k); delims.push(k);
}, }
_ => delims.push(k) _ => delims.push(k),
}, },
K::Elif | K::Else => match delims.pop() { K::Elif | K::Else => match delims.pop() {
Some(K::Then) => delims.push(k), Some(K::Then) => delims.push(k),
v => { mismatch = Some((v, k, s)); break } v => {
mismatch = Some((v, k, s));
break
}
}, },
K::End => match delims.pop() { K::End => match delims.pop() {
Some(K::Then | K::Else | K::Do | K::Try | K::Catch) => (), Some(K::Then | K::Else | K::Do | K::Try | K::Catch) => (),
v => { mismatch = Some((v, k, s)); break } v => {
mismatch = Some((v, k, s));
break
}
}, },
_ => (), _ => (),
} }
} }
match mismatch { match mismatch {
Some((None, b, s)) => return Ok(ValidationResult::Invalid(Some( Some((None, b, s)) => {
format!(" found unmatched {b} at {s}")))), return Ok(ValidationResult::Invalid(Some(format!(
Some((Some(a), b, s)) => return Ok(ValidationResult::Invalid(Some( " found unmatched {b} at {s}"
format!(" found {a} matched with {b} at {s}")))), ))))
}
Some((Some(a), b, s)) => {
return Ok(ValidationResult::Invalid(Some(format!(
" found {a} matched with {b} at {s}"
))))
}
_ => (), _ => (),
} }
@ -171,6 +206,5 @@ impl Validator for TalcHelper {
} else { } else {
Ok(ValidationResult::Incomplete) Ok(ValidationResult::Incomplete)
} }
} }
} }

View file

@ -1,9 +1,9 @@
use clap::{ColorChoice, Parser}; use clap::{ColorChoice, Parser};
use talc_lang::{compiler::compile, parser, symbol::Symbol, 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)]
@ -20,11 +20,11 @@ struct Args {
disasm: bool, disasm: bool,
/// show disassembled bytecode /// show disassembled bytecode
#[arg(short='H', long)] #[arg(short = 'H', long)]
histfile: Option<PathBuf>, histfile: Option<PathBuf>,
/// enable or disable color /// enable or disable color
#[arg(short, long, default_value="auto")] #[arg(short, long, default_value = "auto")]
color: ColorChoice, color: ColorChoice,
} }
@ -37,7 +37,7 @@ fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
Err(e) => { Err(e) => {
eprintln!("Error: {e}"); eprintln!("Error: {e}");
return ExitCode::FAILURE return ExitCode::FAILURE
}, }
}; };
let func = Rc::new(compile(&ex, Some(name))); let func = Rc::new(compile(&ex, Some(name)));
@ -64,7 +64,7 @@ fn main() -> ExitCode {
return repl::repl(&args) return repl::repl(&args)
} }
let file = args.file.as_ref().unwrap(); let file = args.file.as_ref().unwrap();
match std::fs::read_to_string(file) { match std::fs::read_to_string(file) {
Ok(s) => exec(Symbol::get(file.as_os_str()), &s, &args), Ok(s) => exec(Symbol::get(file.as_os_str()), &s, &args),

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, parser, 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),
} }
} }
@ -117,7 +127,7 @@ 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) {
@ -125,7 +135,7 @@ 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 (f, g) = compile_repl(&ex, &compiler_globals); let (f, g) = compile_repl(&ex, &compiler_globals);
@ -154,4 +164,3 @@ pub fn repl(args: &Args) -> ExitCode {
} }
} }
} }

View file

@ -1,4 +1,8 @@
use crate::{value::Value, parser::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,55 +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, // do nothing Nop, // do nothing
LoadLocal(Arg24), // push nth local onto stack LoadLocal(Arg24), // push nth local onto stack
StoreLocal(Arg24), // pop stack into nth local StoreLocal(Arg24), // pop stack into nth local
NewLocal, // pop stack into a new local NewLocal, // pop stack into a new local
DropLocal(Arg24), // remove last n locals DropLocal(Arg24), // remove last n locals
LoadGlobal(Arg24), // load global by id LoadGlobal(Arg24), // load global by id
StoreGlobal(Arg24), // store global by id StoreGlobal(Arg24), // store global by id
CloseOver(Arg24), // load nth local and convert to cell, write back a copy CloseOver(Arg24), // load nth local and convert to cell, write back a copy
Closure(Arg24), // load constant function and fill state from stack Closure(Arg24), // load constant function and fill state from stack
LoadUpvalue(Arg24), // load LoadUpvalue(Arg24), // load
StoreUpvalue(Arg24), // store a cell from closure state to new local StoreUpvalue(Arg24), // store a cell from closure state to new local
ContinueUpvalue(Arg24), // ContinueUpvalue(Arg24), //
LoadClosedLocal(Arg24), // load through cell in nth local LoadClosedLocal(Arg24), // load through cell in nth local
StoreClosedLocal(Arg24), // store through cell in nth local StoreClosedLocal(Arg24), // store through cell in nth local
Const(Arg24), // push nth constant Const(Arg24), // push nth constant
Int(Arg24), // push integer Int(Arg24), // push integer
Symbol(Arg24), // push symbol Symbol(Arg24), // push symbol
Bool(bool), // push boolean Bool(bool), // push boolean
Nil, // push nil Nil, // push nil
Dup, Dup,
DupTwo, DupTwo,
Drop(Arg24), Drop(Arg24),
Swap, Swap,
UnaryOp(UnaryOp), UnaryOp(UnaryOp),
BinaryOp(BinaryOp), BinaryOp(BinaryOp),
NewList(u8), NewList(u8),
GrowList(u8), GrowList(u8),
NewTable(u8), NewTable(u8),
GrowTable(u8), GrowTable(u8),
Index, Index,
StoreIndex, StoreIndex,
Jump(Arg24), Jump(Arg24),
JumpTrue(Arg24), JumpTrue(Arg24),
JumpFalse(Arg24), JumpFalse(Arg24),
IterBegin, IterBegin,
IterTest(Arg24), IterTest(Arg24),
BeginTry(Arg24), BeginTry(Arg24),
EndTry, EndTry,
Call(u8), Call(u8),
Return, Return,
@ -150,21 +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::CloseOver(n) => write!(f, "closeover {}", usize::from(n)), ),
Self::Closure(c) => write!(f, "closure {}", usize::from(c)), Self::StoreGlobal(s) => write!(
Self::LoadUpvalue(n) => write!(f, "load_upval {}", usize::from(n)), f,
Self::StoreUpvalue(n) => write!(f, "store_upval {}", usize::from(n)), "storeglobal {}",
Self::ContinueUpvalue(n) => write!(f, "cont_upval {}", usize::from(n)), Symbol::try_from(s).expect("symbol does not exist").name()
Self::LoadClosedLocal(n) => write!(f, "load_closed {}", usize::from(n)), ),
Self::StoreClosedLocal(n) => write!(f, "store_closed {}", usize::from(n)), 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"),
@ -217,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,
@ -242,5 +272,3 @@ impl Chunk {
self.try_tables[idx] = table; self.try_tables[idx] = table;
} }
} }

View file

@ -1,32 +1,36 @@
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::rc::Rc; use std::rc::Rc;
use crate::chunk::{Arg24, Catch, Chunk, Instruction as I};
use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind}; use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
use crate::chunk::{Instruction as I, Chunk, Arg24, Catch};
use crate::symbol::{Symbol, SYM_SELF}; use crate::symbol::{Symbol, SYM_SELF};
use crate::value::function::{FuncAttrs, Function}; use crate::value::function::{FuncAttrs, Function};
use crate::value::Value; use crate::value::Value;
enum ResolveOutcome { enum ResolveOutcome {
Var(VarKind), Var(VarKind),
InParent, InParent,
None, None,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum VarKind { pub enum VarKind {
Local(usize), Closed(usize), Global Local(usize),
Closed(usize),
Global,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Var { pub struct Var {
name: Symbol, name: Symbol,
kind: VarKind, 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> {
@ -34,19 +38,19 @@ struct Compiler<'a> {
parent: Option<&'a Compiler<'a>>, parent: Option<&'a Compiler<'a>>,
chunk: Chunk, chunk: Chunk,
attrs: FuncAttrs, attrs: FuncAttrs,
scope: HashMap<Symbol, Var>, scope: HashMap<Symbol, Var>,
shadowed: Vec<(Symbol, Option<Var>)>, shadowed: Vec<(Symbol, Option<Var>)>,
closes: BTreeMap<Symbol, usize>, closes: BTreeMap<Symbol, usize>,
local_count: usize, local_count: usize,
} }
pub fn compile(expr: &Expr, name: Option<Symbol>) -> Function { pub fn compile(expr: &Expr, name: Option<Symbol>) -> Function {
let mut comp = Compiler::new_module(name, 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: &[Symbol]) -> (Function, Vec<Symbol>) { 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()
@ -54,20 +58,23 @@ pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec<Symbol>)
impl<'a> Default for Compiler<'a> { impl<'a> Default for Compiler<'a> {
fn default() -> Self { fn default() -> Self {
let mut scope = HashMap::new(); let mut scope = HashMap::new();
scope.insert(*SYM_SELF, Var { scope.insert(
name: *SYM_SELF, *SYM_SELF,
kind: VarKind::Local(0), 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::default(), attrs: FuncAttrs::default(),
scope, scope,
shadowed: Vec::new(), shadowed: Vec::new(),
local_count: 1, local_count: 1,
closes: BTreeMap::new(), closes: BTreeMap::new(),
} }
} }
} }
@ -76,20 +83,23 @@ impl<'a> Compiler<'a> {
fn new_repl(globals: &[Symbol]) -> Self { fn new_repl(globals: &[Symbol]) -> Self {
let mut new = Self { let mut new = Self {
mode: CompilerMode::Repl, mode: CompilerMode::Repl,
attrs: FuncAttrs { arity: 0, name: Some(Symbol::get("<repl>")) }, attrs: FuncAttrs {
arity: 0,
name: Some(Symbol::get("<repl>")),
},
..Self::default() ..Self::default()
}; };
for g in globals { for g in globals {
new.declare_global(*g); new.declare_global(*g);
} }
new new
} }
fn new_module(name: Option<Symbol>, 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 }, attrs: FuncAttrs { arity: 0, name },
parent, parent,
..Self::default() ..Self::default()
} }
@ -98,13 +108,16 @@ impl<'a> Compiler<'a> {
fn new_function(&'a self, name: Option<Symbol>, args: &[Symbol]) -> 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 }, attrs: FuncAttrs {
arity: args.len(),
name,
},
parent: Some(self), parent: Some(self),
..Self::default() ..Self::default()
}; };
for arg in args { for arg in args {
new.declare_local(*arg); new.declare_local(*arg);
} }
new new
@ -112,26 +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::new(Rc::new(self.chunk), self.attrs, self.closes.len()) Function::new(Rc::new(self.chunk), self.attrs, self.closes.len())
} }
pub fn finish_inner(mut self) -> (Function, BTreeMap<Symbol, usize>) { pub fn finish_inner(mut self) -> (Function, BTreeMap<Symbol, usize>) {
self.emit(I::Return); self.emit(I::Return);
// TODO closure // TODO closure
( (
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()), Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
self.closes self.closes,
) )
} }
pub fn finish_repl(mut self) -> (Function, Vec<Symbol>) { pub fn finish_repl(mut self) -> (Function, Vec<Symbol>) {
self.emit(I::Return); self.emit(I::Return);
( (
// TODO closure // TODO closure
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()), Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
self.scope.into_iter().filter_map(|(_,v)| { self.scope
(v.kind == VarKind::Global).then_some(v.name) .into_iter()
}).collect() .filter_map(|(_, v)| (v.kind == VarKind::Global).then_some(v.name))
.collect(),
) )
} }
@ -154,38 +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(),
| I::StoreClosedLocal(_) | I::StoreUpvalue(_) 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::LoadClosedLocal(_) | I::Nil | I::Bool(_)
| I::LoadUpvalue(_) | 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)));
@ -200,181 +220,180 @@ impl<'a> Compiler<'a> {
self.chunk.instrs[n] = new; self.chunk.instrs[n] = new;
} }
#[must_use] #[must_use]
fn begin_scope(&mut self) -> usize { fn begin_scope(&mut self) -> usize {
self.shadowed.len() self.shadowed.len()
} }
fn end_scope(&mut self, scope: usize) { fn end_scope(&mut self, scope: usize) {
let mut locals = 0; let mut locals = 0;
while self.shadowed.len() > scope { while self.shadowed.len() > scope {
let (name, var) = self.shadowed.pop().expect("scope bad"); let (name, var) = self.shadowed.pop().expect("scope bad");
if let Some(var) = var { if let Some(var) = var {
if var.kind != VarKind::Global { if var.kind != VarKind::Global {
locals += 1; locals += 1;
} }
self.scope.insert(name, var); self.scope.insert(name, var);
} else { } else {
self.scope.remove(&name); self.scope.remove(&name);
} }
} }
if locals > 0 { if locals > 0 {
self.emit(I::DropLocal(Arg24::from_usize(locals))); self.emit(I::DropLocal(Arg24::from_usize(locals)));
self.local_count -= locals; self.local_count -= locals;
} }
} }
// //
// variables // variables
// //
fn resolve_name(&self, name: Symbol) -> ResolveOutcome {
if let Some(v) = self.scope.get(&name) {
return ResolveOutcome::Var(v.kind)
}
let Some(parent) = self.parent else {
return ResolveOutcome::None
};
if let ResolveOutcome::None = parent.resolve_name(name) {
return ResolveOutcome::None
}
ResolveOutcome::InParent
}
fn load_var(&mut self, name: Symbol) { fn resolve_name(&self, name: Symbol) -> ResolveOutcome {
match self.resolve_name(name) { if let Some(v) = self.scope.get(&name) {
ResolveOutcome::Var(VarKind::Local(n)) => { return ResolveOutcome::Var(v.kind)
self.emit(I::LoadLocal(Arg24::from_usize(n))); }
} let Some(parent) = self.parent else {
ResolveOutcome::Var(VarKind::Closed(n)) => { return ResolveOutcome::None
self.emit(I::LoadClosedLocal(Arg24::from_usize(n))); };
} if let ResolveOutcome::None = parent.resolve_name(name) {
ResolveOutcome::InParent => { return ResolveOutcome::None
let n = match self.closes.get(&name) { }
Some(n) => *n, ResolveOutcome::InParent
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: Symbol) -> usize { fn load_var(&mut self, name: Symbol) {
let local = Var { match self.resolve_name(name) {
name, ResolveOutcome::Var(VarKind::Local(n)) => {
kind: VarKind::Local(self.local_count) self.emit(I::LoadLocal(Arg24::from_usize(n)));
}; }
self.local_count += 1; ResolveOutcome::Var(VarKind::Closed(n)) => {
let shadowed = self.scope.insert(name, local); self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
self.shadowed.push((name, shadowed)); }
self.local_count - 1 ResolveOutcome::InParent => {
} 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::LoadUpvalue(Arg24::from_usize(n)));
}
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
self.emit(I::LoadGlobal(Arg24::from_symbol(name)));
}
}
}
fn assign_local(&mut self, name: Symbol) -> usize { fn declare_local(&mut self, name: Symbol) -> usize {
let n = self.declare_local(name); let local = Var {
self.emit(I::NewLocal); name,
n kind: VarKind::Local(self.local_count),
} };
self.local_count += 1;
let shadowed = self.scope.insert(name, local);
self.shadowed.push((name, shadowed));
self.local_count - 1
}
fn assign_global(&mut self, name: Symbol) { fn assign_local(&mut self, name: Symbol) -> usize {
self.declare_global(name); let n = self.declare_local(name);
self.store_var(name); self.emit(I::NewLocal);
} n
}
fn declare_global(&mut self, name: Symbol) { fn assign_global(&mut self, name: Symbol) {
let global = Var { self.declare_global(name);
name, self.store_var(name);
kind: VarKind::Global }
};
let shadowed = self.scope.insert(name, global);
self.shadowed.push((name, shadowed));
}
fn store_var(&mut self, name: Symbol) {
match self.resolve_name(name) {
ResolveOutcome::Var(VarKind::Local(n)) => {
self.emit(I::StoreLocal(Arg24::from_usize(n)));
}
ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::StoreClosedLocal(Arg24::from_usize(n)));
}
ResolveOutcome::InParent => {
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);
}
}
}
fn declare_global(&mut self, name: Symbol) {
let global = Var {
name,
kind: VarKind::Global,
};
let shadowed = self.scope.insert(name, global);
self.shadowed.push((name, shadowed));
}
fn store_var(&mut self, name: Symbol) {
match self.resolve_name(name) {
ResolveOutcome::Var(VarKind::Local(n)) => {
self.emit(I::StoreLocal(Arg24::from_usize(n)));
}
ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::StoreClosedLocal(Arg24::from_usize(n)));
}
ResolveOutcome::InParent => {
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) {
let Expr { kind, .. } = e; let Expr { kind, .. } = e;
match kind { match kind {
ExprKind::Block(xs) if xs.is_empty() => { self.emit(I::Nil); }, ExprKind::Block(xs) if xs.is_empty() => {
self.emit(I::Nil);
}
ExprKind::Block(xs) => { ExprKind::Block(xs) => {
let scope = self.begin_scope(); 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(scope); self.end_scope(scope);
}, }
ExprKind::Literal(v) => self.expr_literal(v), ExprKind::Literal(v) => self.expr_literal(v),
ExprKind::Ident(ident) => self.load_var(*ident), ExprKind::Ident(ident) => self.load_var(*ident),
ExprKind::UnaryOp(o, a) => { ExprKind::UnaryOp(o, a) => {
self.expr(a); self.expr(a);
self.emit(I::UnaryOp(*o)); self.emit(I::UnaryOp(*o));
}, }
ExprKind::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));
}, }
ExprKind::Assign(o, lv, a) => self.expr_assign(*o, lv, a), ExprKind::Assign(o, lv, a) => self.expr_assign(*o, lv, a),
ExprKind::AssignVar(name, a) => { ExprKind::AssignVar(name, a) => {
self.expr(a); self.expr(a);
self.emit(I::Dup); self.emit(I::Dup);
self.assign_local(*name); self.assign_local(*name);
}, }
ExprKind::AssignGlobal(name, a) => { ExprKind::AssignGlobal(name, a) => {
self.expr(a); self.expr(a);
self.emit(I::Dup); self.emit(I::Dup);
self.assign_global(*name); self.assign_global(*name);
}, }
ExprKind::List(xs) if xs.is_empty() => { ExprKind::List(xs) if xs.is_empty() => {
self.emit(I::NewList(0)); self.emit(I::NewList(0));
}, }
ExprKind::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) {
@ -388,10 +407,10 @@ impl<'a> Compiler<'a> {
self.emit(I::GrowList(chunk.len() as u8)); self.emit(I::GrowList(chunk.len() as u8));
} }
} }
}, }
ExprKind::Table(xs) if xs.is_empty() => { ExprKind::Table(xs) if xs.is_empty() => {
self.emit(I::NewTable(0)); self.emit(I::NewTable(0));
}, }
ExprKind::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) {
@ -406,19 +425,19 @@ impl<'a> Compiler<'a> {
self.emit(I::GrowTable(chunk.len() as u8)); self.emit(I::GrowTable(chunk.len() as u8));
} }
} }
}, }
ExprKind::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);
}, }
ExprKind::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));
}, }
ExprKind::AssocFnCall(o, f, args) => { ExprKind::AssocFnCall(o, f, args) => {
self.expr(o); self.expr(o);
self.emit(I::Dup); self.emit(I::Dup);
@ -429,17 +448,17 @@ 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));
}, }
ExprKind::Return(e) => { ExprKind::Return(e) => {
self.expr(e); self.expr(e);
self.emit(I::Return); self.emit(I::Return);
}, }
ExprKind::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));
}, }
ExprKind::Lambda(args, body) => self.expr_fndef(None, args, body), ExprKind::Lambda(args, body) => self.expr_fndef(None, args, body),
ExprKind::FnDef(name, args, body) => self.expr_fndef(*name, args, body), ExprKind::FnDef(name, args, body) => self.expr_fndef(*name, args, body),
ExprKind::And(a, b) => { ExprKind::And(a, b) => {
@ -449,7 +468,7 @@ impl<'a> Compiler<'a> {
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())));
}, }
ExprKind::Or(a, b) => { ExprKind::Or(a, b) => {
self.expr(a); self.expr(a);
self.emit(I::Dup); self.emit(I::Dup);
@ -457,7 +476,7 @@ impl<'a> Compiler<'a> {
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())));
}, }
ExprKind::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)));
@ -469,10 +488,8 @@ 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) => { ExprKind::While(cond, body) => {
let start = self.ip(); let start = self.ip();
self.expr(cond); self.expr(cond);
@ -484,7 +501,7 @@ 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);
}, }
ExprKind::For(name, iter, body) => self.expr_for(*name, iter, body), ExprKind::For(name, iter, body) => self.expr_for(*name, iter, body),
ExprKind::Try(body, catches) => self.expr_try(body, catches), ExprKind::Try(body, catches) => self.expr_try(body, catches),
} }
@ -568,55 +585,60 @@ impl<'a> Compiler<'a> {
let (func, closes) = inner.finish_inner(); let (func, closes) = inner.finish_inner();
let func_const = self.add_const(func.into()); let func_const = self.add_const(func.into());
let num_closed = closes.len(); let num_closed = closes.len();
for (name, _) in closes { for (name, _) in closes {
match self.resolve_name(name) { match self.resolve_name(name) {
ResolveOutcome::Var(VarKind::Local(n)) => { ResolveOutcome::Var(VarKind::Local(n)) => {
self.emit(I::CloseOver(Arg24::from_usize(n))); self.emit(I::CloseOver(Arg24::from_usize(n)));
self.scope.entry(name).and_modify(|v| { self.scope.entry(name).and_modify(|v| {
v.kind = VarKind::Closed(n); v.kind = VarKind::Closed(n);
}); });
}, }
ResolveOutcome::Var(VarKind::Closed(n)) => { ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::LoadLocal(Arg24::from_usize(n))); self.emit(I::LoadLocal(Arg24::from_usize(n)));
}, }
ResolveOutcome::InParent => { ResolveOutcome::InParent => {
let n = self.closes.len(); let n = self.closes.len();
self.closes.insert(name, n); self.closes.insert(name, n);
self.emit(I::ContinueUpvalue(Arg24::from_usize(n))); self.emit(I::ContinueUpvalue(Arg24::from_usize(n)));
}, }
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
=> panic!("upvalue resolved to none or global"), panic!("upvalue resolved to none or global")
} }
} }
}
if num_closed == 0 { if num_closed == 0 {
self.emit(I::Const(Arg24::from_usize(func_const))); self.emit(I::Const(Arg24::from_usize(func_const)));
} else { } else {
self.emit(I::Closure(Arg24::from_usize(func_const))); self.emit(I::Closure(Arg24::from_usize(func_const)));
} }
if let Some(name) = name { if let Some(name) = name {
self.emit(I::Dup); self.emit(I::Dup);
self.store_var(name); 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)));
}, }
} }
} }
@ -626,20 +648,20 @@ impl<'a> Compiler<'a> {
self.expr(a); self.expr(a);
self.emit(I::Dup); self.emit(I::Dup);
self.store_var(*i); self.store_var(*i);
}, }
(LValueKind::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_var(*i); self.store_var(*i);
}, }
(LValueKind::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);
}, }
(LValueKind::Index(ct, i), Some(o)) => { (LValueKind::Index(ct, i), Some(o)) => {
self.expr(ct); self.expr(ct);
self.expr(i); self.expr(i);
@ -648,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> {
@ -102,6 +122,3 @@ macro_rules! throw {
} }
pub use throw; pub use throw;

View file

@ -2,13 +2,13 @@
#![warn(clippy::semicolon_if_nothing_returned)] #![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::allow_attributes)] #![warn(clippy::allow_attributes)]
pub mod symbol;
pub mod parser;
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;
mod vm; mod vm;
pub use vm::Vm; pub use vm::Vm;

View file

@ -1,4 +1,15 @@
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 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,45 +88,55 @@ 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) {
return Some((&bytes[..len-1], Ok(last as char))) return Some((&bytes[..len - 1], Ok(last as char)))
} }
'case: { 'case: {
if !is_continue(last) { break 'case } if !is_continue(last) {
break 'case
}
if len < 2 { break 'case } if len < 2 {
let b1 = bytes[len-2]; break 'case
}
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]) {
return Some((&bytes[..len-2], Ok(ch))) return Some((&bytes[..len - 2], Ok(ch)))
}; };
} else if !is_continue(b1) { } else if !is_continue(b1) {
break 'case break 'case
} }
if len < 3 { break 'case } if len < 3 {
let b2 = bytes[len-3]; break 'case
}
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]) {
return Some((&bytes[..len-3], Ok(ch))) return Some((&bytes[..len - 3], Ok(ch)))
}; };
} else if !is_continue(b2) { } else if !is_continue(b2) {
break 'case break 'case
} }
if len < 4 { break 'case } if len < 4 {
let b3 = bytes[len-4]; break 'case
}
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]) {
return Some((&bytes[..len-4], Ok(ch))) return Some((&bytes[..len - 4], Ok(ch)))
}; };
} }
} }
Some((&bytes[..len-1], Err(last))) Some((&bytes[..len - 1], Err(last)))
} }
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -230,47 +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 { impl From<OsString> for LString {
#[inline] #[inline]
fn from(value: OsString) -> Self { Self { inner: value.into_encoded_bytes() } } 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 { impl<const N: usize> From<&[u8; N]> for LString {
#[inline] #[inline]
fn from(value: &[u8; N]) -> Self { value.as_slice().into() } fn from(value: &[u8; N]) -> Self {
value.as_slice().into()
}
} }
impl<const N: usize> From<[u8; N]> for LString { impl<const N: usize> From<[u8; N]> for LString {
#[inline] #[inline]
fn from(value: [u8; N]) -> Self { value.as_slice().into() } fn from(value: [u8; N]) -> Self {
value.as_slice().into()
}
} }
impl From<Cow<'_, LStr>> for LString { impl From<Cow<'_, LStr>> for LString {
@ -278,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,
} }
} }
} }
@ -306,7 +357,9 @@ impl<const N: usize> From<Cow<'_, [u8; N]>> for LString {
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 {
@ -339,7 +392,9 @@ impl<'a> From<&'a OsStr> for &'a LStr {
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 {
@ -351,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> {
@ -412,35 +469,35 @@ impl From<u8> for LString {
impl FromIterator<char> for LString { impl FromIterator<char> for LString {
#[inline] #[inline]
fn from_iter<I: IntoIterator<Item=char>>(iter: I) -> Self { fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
String::from_iter(iter).into() String::from_iter(iter).into()
} }
} }
impl FromIterator<u8> for LString { impl FromIterator<u8> for LString {
#[inline] #[inline]
fn from_iter<T: IntoIterator<Item=u8>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
Vec::from_iter(iter).into() Vec::from_iter(iter).into()
} }
} }
impl Extend<u8> for LString { impl Extend<u8> for LString {
#[inline] #[inline]
fn extend<I: IntoIterator<Item=u8>>(&mut self, iter: I) { fn extend<I: IntoIterator<Item = u8>>(&mut self, iter: I) {
self.inner.extend(iter); self.inner.extend(iter);
} }
} }
impl<'a> Extend<&'a u8> for LString { impl<'a> Extend<&'a u8> for LString {
#[inline] #[inline]
fn extend<I: IntoIterator<Item=&'a u8>>(&mut self, iter: I) { fn extend<I: IntoIterator<Item = &'a u8>>(&mut self, iter: I) {
self.inner.extend(iter); self.inner.extend(iter);
} }
} }
impl Extend<char> for LString { impl Extend<char> for LString {
#[inline] #[inline]
fn extend<I: IntoIterator<Item=char>>(&mut self, iter: I) { fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
let iter = iter.into_iter(); let iter = iter.into_iter();
let (lo, _) = iter.size_hint(); let (lo, _) = iter.size_hint();
self.reserve(lo); self.reserve(lo);
@ -450,7 +507,7 @@ impl Extend<char> for LString {
impl<'a> Extend<&'a char> for LString { impl<'a> Extend<&'a char> for LString {
#[inline] #[inline]
fn extend<I: IntoIterator<Item=&'a char>>(&mut self, iter: I) { fn extend<I: IntoIterator<Item = &'a char>>(&mut self, iter: I) {
let iter = iter.into_iter(); let iter = iter.into_iter();
let (lo, _) = iter.size_hint(); let (lo, _) = iter.size_hint();
self.reserve(lo); self.reserve(lo);
@ -468,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 {
@ -547,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)
@ -576,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> {}
@ -597,7 +668,7 @@ impl<'a> Iterator for LosslessChars<'a> {
#[inline] #[inline]
fn size_hint(&self) -> (usize, Option<usize>) { fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.0.len(); let len = self.0.len();
((len + 3)/4, Some(len)) ((len + 3) / 4, Some(len))
} }
} }
@ -618,19 +689,19 @@ impl<'a> Iterator for LosslessCharsIndices<'a> {
#[inline] #[inline]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let (new_bytes, res) = next_codepoint(self.0)?; let (new_bytes, res) = next_codepoint(self.0)?;
let index = self.1; let index = self.1;
self.0 = new_bytes; self.0 = new_bytes;
match res { match res {
Ok(c) => self.1 += c.len_utf8(), Ok(c) => self.1 += c.len_utf8(),
Err(_) => self.1 += 1, Err(_) => self.1 += 1,
} }
Some((index, res)) Some((index, res))
} }
#[inline] #[inline]
fn size_hint(&self) -> (usize, Option<usize>) { fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.0.len(); let len = self.0.len();
((len + 3)/4, Some(len)) ((len + 3) / 4, Some(len))
} }
} }
@ -638,15 +709,14 @@ impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> {
fn next_back(&mut self) -> Option<Self::Item> { fn next_back(&mut self) -> Option<Self::Item> {
let (new_bytes, res) = next_codepoint_back(self.0)?; let (new_bytes, res) = next_codepoint_back(self.0)?;
self.0 = new_bytes; self.0 = new_bytes;
match res { match res {
Ok(c) => self.1 -= c.len_utf8(), Ok(c) => self.1 -= c.len_utf8(),
Err(_) => self.1 -= 1, Err(_) => self.1 -= 1,
} }
Some((self.1, res)) Some((self.1, res))
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Chars<'a>(LosslessChars<'a>); pub struct Chars<'a>(LosslessChars<'a>);
@ -702,7 +772,7 @@ impl<'a> Iterator for CharsIndices<'a> {
impl<'a> DoubleEndedIterator for CharsIndices<'a> { impl<'a> DoubleEndedIterator for CharsIndices<'a> {
fn next_back(&mut self) -> Option<Self::Item> { fn next_back(&mut self) -> Option<Self::Item> {
loop { loop {
if let (index, Ok(c)) = self.0.next_back()? { if let (index, Ok(c)) = self.0.next_back()? {
return Some((index, c)) return Some((index, c))
} }
} }
@ -731,16 +801,24 @@ 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] #[inline]
pub fn byte_at(&self, n: usize) -> u8 { self.inner[n] } pub fn byte_at(&self, n: usize) -> u8 {
self.inner[n]
}
#[inline] #[inline]
pub fn byte_get(&self, n: usize) -> Option<u8> { self.inner.get(n).copied() } 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 {
@ -787,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())
} }
} }
@ -862,18 +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> { pub fn strip_prefix(&self, prefix: &LStr) -> Option<&LStr> {
self.as_bytes().strip_prefix(prefix.as_bytes()).map(LStr::from_bytes) self.as_bytes()
} .strip_prefix(prefix.as_bytes())
.map(LStr::from_bytes)
}
pub fn strip_suffix(&self, suffix: &LStr) -> Option<&LStr> { pub fn strip_suffix(&self, suffix: &LStr) -> Option<&LStr> {
self.as_bytes().strip_suffix(suffix.as_bytes()).map(LStr::from_bytes) 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
} }
@ -899,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 {
@ -945,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

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

File diff suppressed because it is too large Load diff

View file

@ -1,443 +0,0 @@
// vim: set syn=rust:
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()),
}

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,5 +1,5 @@
use core::fmt; use core::fmt;
use std::num::{ParseIntError, ParseFloatError}; use std::num::{ParseFloatError, ParseIntError};
use thiserror::Error; use thiserror::Error;
@ -9,26 +9,29 @@ use super::Span;
#[derive(Clone, Debug, Error)] #[derive(Clone, Debug, Error)]
pub struct ParserError { pub struct ParserError {
pub span: Span, pub span: Span,
pub msg: String, pub msg: String,
} }
impl fmt::Display for ParserError { impl fmt::Display for ParserError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} | {}", self.span, self.msg) write!(f, "{} | {}", self.span, self.msg)
} }
} }
pub trait SpanParserError { pub trait SpanParserError {
type Output; type Output;
fn span_err(self, span: Span) -> Self::Output; fn span_err(self, span: Span) -> Self::Output;
} }
impl<T, E: std::error::Error> SpanParserError for Result<T, E> { impl<T, E: std::error::Error> SpanParserError for Result<T, E> {
type Output = Result<T, ParserError>; type Output = Result<T, ParserError>;
fn span_err(self, span: Span) -> Self::Output { fn span_err(self, span: Span) -> Self::Output {
self.map_err(|e| ParserError { span, msg: e.to_string() }) self.map_err(|e| ParserError {
} span,
msg: e.to_string(),
})
}
} }
#[derive(Clone, Copy, Debug, Error)] #[derive(Clone, Copy, Debug, Error)]
@ -56,26 +59,29 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
let mut chars = src.chars(); let mut chars = src.chars();
while let Some(c) = chars.next() { while let Some(c) = chars.next() {
if c != '\\' { s.push_char(c); continue } if c != '\\' {
s.push_char(c);
continue
}
let c = chars.next().ok_or(StrEscapeError::Eof)?; let c = chars.next().ok_or(StrEscapeError::Eof)?;
match c { match c {
'"' | '\'' | '\\' => s.push_char(c), '"' | '\'' | '\\' => s.push_char(c),
'0' => s.push_char('\0'), '0' => s.push_char('\0'),
'a' => s.push_char('\x07'), 'a' => s.push_char('\x07'),
'b' => s.push_char('\x08'), 'b' => s.push_char('\x08'),
't' => s.push_char('\t'), 't' => s.push_char('\t'),
'n' => s.push_char('\n'), 'n' => s.push_char('\n'),
'v' => s.push_char('\x0b'), 'v' => s.push_char('\x0b'),
'f' => s.push_char('\x0c'), 'f' => s.push_char('\x0c'),
'r' => s.push_char('\r'), 'r' => s.push_char('\r'),
'e' => s.push_char('\x1b'), 'e' => s.push_char('\x1b'),
'x' => { 'x' => {
let c = chars.next().ok_or(StrEscapeError::HexEof)?; let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?; let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
let c = chars.next().ok_or(StrEscapeError::HexEof)?; let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?; let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
s.push_byte((n1 * 16 + n2) as u8); s.push_byte((n1 * 16 + n2) as u8);
}, }
'u' => { 'u' => {
let Some('{') = chars.next() else { let Some('{') = chars.next() else {
return Err(StrEscapeError::MissingBrace) return Err(StrEscapeError::MissingBrace)
@ -85,7 +91,9 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
let Some(c) = chars.next() else { let Some(c) = chars.next() else {
return Err(StrEscapeError::UnicodeEof) return Err(StrEscapeError::UnicodeEof)
}; };
if c == '}' { break } if c == '}' {
break
}
if n > 0x10ffff { if n > 0x10ffff {
return Err(StrEscapeError::CodepointTooLarge) return Err(StrEscapeError::CodepointTooLarge)
} }
@ -93,8 +101,7 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
} }
let ch = char::from_u32(n).ok_or(StrEscapeError::InvalidCodepoint(n))?; let ch = char::from_u32(n).ok_or(StrEscapeError::InvalidCodepoint(n))?;
s.push_char(ch); s.push_char(ch);
}
},
c => return Err(StrEscapeError::Invalid(c)), c => return Err(StrEscapeError::Invalid(c)),
} }
} }
@ -123,14 +130,14 @@ pub fn parse_int<'a, S: Into<&'a LStr>>(f: S, radix: u32) -> Result<i64, ParseIn
} }
pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result<i64, ParseIntError> { pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result<i64, ParseIntError> {
let f = f.into(); let f = f.into();
match f.chars().nth(2) { match f.chars().nth(2) {
Some('x') => parse_int(&f[2..], 16), Some('x') => parse_int(&f[2..], 16),
Some('o') => parse_int(&f[2..], 8), Some('o') => parse_int(&f[2..], 8),
Some('s') => parse_int(&f[2..], 6), Some('s') => parse_int(&f[2..], 6),
Some('b') => parse_int(&f[2..], 2), Some('b') => parse_int(&f[2..], 2),
_ => parse_int(f, 10), _ => parse_int(f, 10),
} }
} }
pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString { pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
@ -151,14 +158,14 @@ pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
x /= radix as u64; x /= radix as u64;
let mut c = char::from_digit(m as u32, radix).unwrap(); let mut c = char::from_digit(m as u32, radix).unwrap();
if upper { c.make_ascii_uppercase(); } if upper {
c.make_ascii_uppercase();
}
result.push(c as u8); result.push(c as u8);
if x == 0 { if x == 0 {
break; break
} }
} }
result[begin..].reverse(); result[begin..].reverse();
LString::from(result) LString::from(result)
} }

View file

@ -1,17 +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_SELF: Symbol = symbol!(self);
pub static ref SYM_DOLLAR_SIGN: Symbol = symbol!("$"); 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);
@ -26,13 +28,10 @@ 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_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);
@ -110,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")
} }
} }
@ -127,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,31 +1,35 @@
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use crate::{chunk::Chunk, Vm, exception::Result, symbol::Symbol}; use crate::{chunk::Chunk, exception::Result, symbol::Symbol, Vm};
use super::{Value, CellValue}; 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 name: Option<Symbol>, pub name: Option<Symbol>,
} }
#[derive(Debug, Clone)] #[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]>, pub state: Box<[CellValue]>,
} }
impl Function { impl Function {
pub fn new(chunk: Rc<Chunk>, attrs: FuncAttrs, closes: usize) -> Self { pub fn new(chunk: Rc<Chunk>, attrs: FuncAttrs, closes: usize) -> Self {
let v = Rc::new(RefCell::new(Value::Nil)); let v = Rc::new(RefCell::new(Value::Nil));
let state = std::iter::repeat(v).take(closes).collect(); let state = std::iter::repeat(v).take(closes).collect();
Self::from_parts(chunk, attrs, state) Self::from_parts(chunk, attrs, state)
} }
pub fn from_parts(chunk: Rc<Chunk>, attrs: FuncAttrs, state: Box<[CellValue]>) -> Self { pub fn from_parts(chunk: Rc<Chunk>, attrs: FuncAttrs, state: Box<[CellValue]>) -> Self {
Self { chunk, attrs, state } Self {
chunk,
attrs,
state,
}
} }
} }
@ -38,11 +42,20 @@ pub struct NativeFunc {
impl NativeFunc { impl NativeFunc {
pub fn new(func: FnNative, arity: usize, name: Symbol) -> Self { pub fn new(func: FnNative, arity: usize, name: Symbol) -> Self {
Self { func, attrs: FuncAttrs { arity, name: Some(name) } } Self {
func,
attrs: FuncAttrs {
arity,
name: Some(name),
},
}
} }
pub fn new_anon(func: FnNative, arity: usize) -> Self { pub fn new_anon(func: FnNative, arity: usize) -> Self {
Self { func, attrs: FuncAttrs { arity, name: None } } Self {
func,
attrs: FuncAttrs { arity, name: None },
}
} }
} }
@ -55,9 +68,7 @@ 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(f: &Rc<Function>, w: &mut impl std::io::Write) -> std::io::Result<()> {
writeln!(w, "{} ({})", writeln!(w, "{} ({})", Value::Function(f.clone()), f.attrs.arity)?;
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() {
@ -78,7 +89,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, "]")?;
@ -101,4 +114,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::{exception::{throw, Result}, symbol::{symbol, SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR}, value::function::NativeFunc, vmcalliter, Vm}; 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,70 +16,93 @@ 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) => {
let col = col.clone(); if let Ok(ii) = idx.clone().to_iter_function() {
let func = move |vm: &mut Vm, _| { let col = col.clone();
match vmcalliter!(vm; ii.clone())? { let func = move |vm: &mut Vm, _| 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, symbol!("index...")).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:#}") }
} }
} }
} }
@ -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) => {
let val = val.to_iter_function()?; if let Ok(ii) = idx.clone().to_iter_function() {
while let Some(i) = vmcalliter!(vm; ii.clone())? { let val = val.to_iter_function()?;
let Some(v) = vmcalliter!(vm; val.clone())? else { while let Some(i) = vmcalliter!(vm; ii.clone())? {
throw!(*SYM_INDEX_ERROR, "not enough values provided for store index") let Some(v) = vmcalliter!(vm; val.clone())? else {
}; throw!(
col.store_index(vm, i, v)?; *SYM_INDEX_ERROR,
"not enough values provided for store index"
)
};
col.store_index(vm, i, v)?;
}
Ok(())
} else {
throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}")
} }
Ok(()) }
} else {
throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}")
},
} }
} }
} }

View file

@ -7,16 +7,19 @@ 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>>>;
@ -49,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, _recur: &mut Vec<*const ()>) -> 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(())
} }
@ -63,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}")
} }
} }
@ -81,7 +93,12 @@ impl Value {
table.into() table.into()
} }
pub fn write_to_lstring(&self, w: &mut LString, repr: bool, recur: &mut Vec<*const ()>) -> 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"),
@ -95,10 +112,10 @@ 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),
RangeType::Endless => write!(w, "{}..*", r.start), RangeType::Endless => write!(w, "{}..*", r.start),
}, },
Self::Int(i) => write!(w, "{i}"), Self::Int(i) => write!(w, "{i}"),
@ -110,50 +127,50 @@ 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 _)) { if recur.contains(&(v.as_ptr() as _)) {
return w.write_all(b"cell(...)") return w.write_all(b"cell(...)")
} }
w.write_all(b"cell(")?; w.write_all(b"cell(")?;
recur.push(v.as_ptr() as _); recur.push(v.as_ptr() as _);
v.borrow().write_to_lstring(w, repr, recur)?; v.borrow().write_to_lstring(w, repr, recur)?;
recur.pop(); recur.pop();
w.write_all(b")") w.write_all(b")")
}, }
Self::Cell(v) => { Self::Cell(v) => {
if recur.contains(&(v.as_ptr() as _)) { if recur.contains(&(v.as_ptr() as _)) {
return w.write_all(b"cell(...)") return w.write_all(b"cell(...)")
} }
recur.push(v.as_ptr() as _); recur.push(v.as_ptr() as _);
v.borrow().write_to_lstring(w, true, recur)?; v.borrow().write_to_lstring(w, true, recur)?;
recur.pop(); recur.pop();
Ok(()) 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 _)) { if recur.contains(&(l.as_ptr() as _)) {
return w.write_all(b"[...]") return w.write_all(b"[...]")
} }
w.write_all(b"[")?; w.write_all(b"[")?;
recur.push(l.as_ptr() as _); 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, recur)?; item.write_to_lstring(w, true, recur)?;
} }
recur.pop(); recur.pop();
w.write_all(b"]") w.write_all(b"]")
}, }
Self::Table(t) => { Self::Table(t) => {
if recur.contains(&(t.as_ptr() as _)) { if recur.contains(&(t.as_ptr() as _)) {
return w.write_all(b"{...}") return w.write_all(b"{...}")
} }
w.write_all(b"{ ")?; w.write_all(b"{ ")?;
recur.push(t.as_ptr() as _); 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", ")?;
@ -162,36 +179,58 @@ impl Value {
w.write_all(b" = ")?; w.write_all(b" = ")?;
v.write_to_lstring(w, true, recur)?; v.write_to_lstring(w, true, recur)?;
} }
recur.pop(); recur.pop();
w.write_all(b" }") w.write_all(b" }")
}, }
Self::Function(g) => { Self::Function(g) => {
if let Some(name) = g.attrs.name { if let Some(name) = g.attrs.name {
write!(w, "<function {}({}) @{:0>12x}>", write!(
name.name(), g.attrs.arity, Rc::as_ptr(g) as usize) w,
} else { "<function {}({}) @{:0>12x}>",
write!(w, "<anon function({}) @{:0>12x}>", name.name(),
g.attrs.arity, Rc::as_ptr(g) as usize) 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) => { Self::NativeFunc(g) => {
if let Some(name) = g.attrs.name { if let Some(name) = g.attrs.name {
write!(w, "<native function {}({}) @{:0>12x}>", write!(
name.name(), g.attrs.arity, Rc::as_ptr(g) as usize) w,
} else { "<native function {}({}) @{:0>12x}>",
write!(w, "<anon native function({}) @{:0>12x}>", name.name(),
g.attrs.arity, Rc::as_ptr(g) as usize) 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), Self::Native(n) => n.to_lstring(w, repr, recur),
} }
} }
fn write_table_key_repr(&self, w: &mut LString, recur: &mut Vec<*const ()>) -> 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, recur), }
Self::Symbol(s) => { Self::Symbol(s) => {
let name = s.name(); let name = s.name();
if name.is_identifier() { if name.is_identifier() {
@ -200,7 +239,7 @@ impl Value {
} else { } else {
self.write_to_lstring(w, true, recur) self.write_to_lstring(w, true, recur)
} }
}, }
_ => { _ => {
w.push_byte(b'('); w.push_byte(b'(');
self.write_to_lstring(w, true, recur)?; self.write_to_lstring(w, true, recur)?;
@ -215,16 +254,18 @@ impl Value {
Cow::Borrowed(s) Cow::Borrowed(s)
} else { } else {
let mut s = LString::new(); let mut s = LString::new();
let mut recur = Vec::new(); let mut recur = Vec::new();
self.write_to_lstring(&mut s, false, &mut recur).expect("write_to_lstring failed"); 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();
let mut recur = Vec::new(); let mut recur = Vec::new();
self.write_to_lstring(&mut s, true, &mut recur).expect("write_to_lstring failed"); self.write_to_lstring(&mut s, true, &mut recur)
.expect("write_to_lstring failed");
s s
} }
@ -252,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,
} }
} }
} }
@ -281,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 {
@ -303,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,13 +1,27 @@
use std::{
use std::{cell::RefCell, cmp::Ordering, ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Shl, Shr, Sub}, rc::Rc}; cell::RefCell,
cmp::Ordering,
ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Shl, Shr, Sub},
rc::Rc,
};
use num_complex::{Complex64, ComplexFloat}; use num_complex::{Complex64, ComplexFloat};
use num_rational::Rational64; use num_rational::Rational64;
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero}; use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
use crate::{exception::{throw, Result}, lstring::LString, symbol::{symbol, SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_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;
@ -33,9 +47,11 @@ 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::Native(_) => true, | Value::Function(_)
| Value::NativeFunc(_)
| Value::Native(_) => true,
} }
} }
} }
@ -43,46 +59,48 @@ impl Value {
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) {
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b), (V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
(V::Int(x), V::Float(..)) => (V::Float(*x as f64), b), (V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
(V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b), (V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
(V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64()), b), (V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64()), b),
(V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64().into()), b), (V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64().into()), b),
(V::Float(x), V::Complex(..)) => (V::Complex(x.into()), b), (V::Float(x), V::Complex(..)) => (V::Complex(x.into()), b),
(V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())), (V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
(V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)), (V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
(V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())), (V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
(V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64())), (V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64())),
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64().into())), (V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64().into())),
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())), (V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
_ => (a, b), _ => (a, b),
} }
} }
//////////////////////// ////////////////////////
// unary arithmetic // // 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) => if let Some(x) = x.checked_neg() { V::Int(x) => {
Ok(V::Int(x)) if let Some(x) = x.checked_neg() {
} else { Ok(V::Int(x))
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}") } 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 { V::Ratio(x) => {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}") 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:#}"),
} }
} }
} }
@ -91,32 +109,40 @@ impl Value {
pub fn abs(self) -> Result<Self> { pub fn abs(self) -> Result<Self> {
use Value as V; use Value as V;
match self { match self {
V::Int(x) => if let Some(x) = x.checked_abs() { V::Int(x) => {
Ok(V::Int(x)) if let Some(x) = x.checked_abs() {
} else { Ok(V::Int(x))
throw!(*SYM_VALUE_ERROR, "overflow when finding absolute value of {self}") } else {
}, throw!(
V::Ratio(x) => if let Some((x, _)) = ratio_checked_absign(&x) { *SYM_VALUE_ERROR,
Ok(V::Ratio(x)) "overflow when finding absolute value of {self}"
} else { )
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::Float(x) => Ok(V::Float(x.abs())),
V::Complex(x) => Ok(V::Float(x.norm())), V::Complex(x) => Ok(V::Float(x.norm())),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}") a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
} }
} }
} }
///////////////////////// /////////////////////////
// binary arithmetic // // binary arithmetic //
///////////////////////// /////////////////////////
macro_rules! impl_value_arith { macro_rules! impl_value_arith {
($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => { ($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => {
impl $trait<Value> for Value { impl $trait<Value> for Value {
type Output = Result<Self>; type Output = Result<Self>;
fn $name(self, rhs: Value) -> Self::Output { fn $name(self, rhs: Value) -> Self::Output {
@ -151,20 +177,23 @@ 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;
let (a, b) = promote(self, rhs); let (a, b) = promote(self, rhs);
match (&a, &b) { match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer division by 0"), (V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer division by 0"),
(V::Int(x), V::Int(y)) => Ok(Value::Ratio((*x,*y).into())), (V::Int(x), V::Int(y)) => Ok(Value::Ratio((*x, *y).into())),
(V::Ratio(_), V::Ratio(r)) if r.is_zero() (V::Ratio(_), V::Ratio(r)) if r.is_zero() => {
=> throw!(*SYM_VALUE_ERROR, "rational division by 0"), 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)) (V::Ratio(x), V::Ratio(y)) => {
} else { if let Some(v) = x.checked_div(y) {
throw!(*SYM_VALUE_ERROR, "overflow when dividing {a} and {b}") 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:#}"),
} }
} }
} }
@ -173,104 +202,115 @@ impl Div<Value> for Value {
// modulo and integer division // // modulo and integer division //
/////////////////////////////////// ///////////////////////////////////
#[inline] #[inline]
fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> { fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> {
let a = if r.is_negative() { let a = if r.is_negative() {
Rational64::ZERO.checked_sub(r)? Rational64::ZERO.checked_sub(r)?
} else { } else {
*r *r
}; };
Some((a, r.signum())) Some((a, r.signum()))
} }
#[inline] #[inline]
fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> { fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
let (r2_abs, r2_sgn) = ratio_checked_absign(r2)?; let (r2_abs, r2_sgn) = ratio_checked_absign(r2)?;
Some(r1.checked_div(&r2_abs)?.floor() * r2_sgn) Some(r1.checked_div(&r2_abs)?.floor() * r2_sgn)
} }
#[inline] #[inline]
fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> { fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
let q = ratio_checked_div_euclid(r1, r2)?; let q = ratio_checked_div_euclid(r1, r2)?;
r1.checked_sub(&r2.checked_mul(&q)?) 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;
let (a, b) = promote(self, rhs); let (a, b) = promote(self, rhs);
match (&a, &b) { match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer modulo by 0"), (V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer modulo by 0"),
(V::Int(x), V::Int(y)) => if let Some(v) = x.checked_rem_euclid(*y) { (V::Int(x), V::Int(y)) => {
Ok(V::Int(v)) if let Some(v) = x.checked_rem_euclid(*y) {
} else { Ok(V::Int(v))
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}") } else {
}, throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
}
}
(V::Ratio(_), V::Ratio(y)) if y.is_zero() (V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
=> throw!(*SYM_VALUE_ERROR, "rational modulo by 0"), 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)) (V::Ratio(x), V::Ratio(y)) => {
} else { if let Some(v) = ratio_checked_rem_euclid(x, y) {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}") 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::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(*y))),
(V::Complex(x), V::Complex(y)) => { (V::Complex(x), V::Complex(y)) => {
let n = x / y; let n = x / y;
let n = Complex64::new(n.re().floor(), n.im().floor()); let n = Complex64::new(n.re().floor(), n.im().floor());
Ok(Value::Complex(x - n * y)) Ok(Value::Complex(x - n * y))
} }
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}") (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;
let (a, b) = promote(self, rhs); let (a, b) = promote(self, rhs);
match (&a, &b) { match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer divsion by 0"), (V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer divsion by 0"),
(V::Int(x), V::Int(y)) => if let Some(v) = x.checked_div_euclid(*y) { (V::Int(x), V::Int(y)) => {
Ok(V::Int(v)) if let Some(v) = x.checked_div_euclid(*y) {
} else { Ok(V::Int(v))
throw!(*SYM_VALUE_ERROR, "overflow when integer dividing {a} and {b}") } else {
}, throw!(
*SYM_VALUE_ERROR,
"overflow when integer dividing {a} and {b}"
)
}
}
(V::Ratio(_), V::Ratio(y)) if y.is_zero() (V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
=> throw!(*SYM_VALUE_ERROR, "integer division by 0"), 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)) (V::Ratio(x), V::Ratio(y)) => {
} else { if let Some(v) = ratio_checked_div_euclid(x, y) {
throw!(*SYM_VALUE_ERROR, "overflow when integer dividing {a} and {b}") 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::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(*y))),
(V::Complex(x), V::Complex(y)) => { (V::Complex(x), V::Complex(y)) => {
let n = x / y; let n = x / y;
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor()))) Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
} }
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}") (l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}"),
} }
} }
} }
////////////////////// //////////////////////
// exponentiation // // exponentiation //
////////////////////// //////////////////////
#[inline] #[inline]
fn ipow(n: i64, p: u64) -> Option<i64> { fn ipow(n: i64, p: u64) -> Option<i64> {
match (n, p) { match (n, p) {
(0, 0) => None, (0, 0) => None,
(0, _) => Some(0), (0, _) => Some(0),
(_, 0) => Some(1), (_, 0) => Some(1),
(1, _) => Some(1), (1, _) => Some(1),
(-1, p) => (-1_i64).checked_pow((p % 2) as u32), (-1, p) => (-1_i64).checked_pow((p % 2) as u32),
(_, p) if p > u32::MAX as u64 => None, (_, p) if p > u32::MAX as u64 => None,
(n, p) => n.checked_pow(p as u32), (n, p) => n.checked_pow(p as u32),
} }
@ -279,12 +319,12 @@ fn ipow(n: i64, p: u64) -> Option<i64> {
#[inline] #[inline]
fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> { fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
match p { match p {
i64::MIN => match (n, d) { i64::MIN => match (n, d) {
(0, _) => Some((0, 1)), (0, _) => Some((0, 1)),
(1, 1) => Some((1, 1)), (1, 1) => Some((1, 1)),
(-1, 1) => Some((-1, 1)), (-1, 1) => Some((-1, 1)),
_ => None, _ => None,
} },
0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)), 0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)),
_ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)), _ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)),
} }
@ -294,45 +334,54 @@ 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) {
if x.is_zero() && *y == 0 { if x.is_zero() && *y == 0 {
throw!(*SYM_VALUE_ERROR, "rational zero to integer zero power") throw!(*SYM_VALUE_ERROR, "rational zero to integer zero power")
} }
let Some(v) = rpow(*(*x).numer(), *(*x).denom(), *y) else { let Some(v) = rpow(*(*x).numer(), *(*x).denom(), *y) else {
throw!(*SYM_VALUE_ERROR, "overflow when raising {self} to the power {rhs}") throw!(
}; *SYM_VALUE_ERROR,
"overflow when raising {self} to the power {rhs}"
)
};
return Ok(V::Ratio(v.into())) return Ok(V::Ratio(v.into()))
} }
let (a, b) = promote(self, rhs); let (a, b) = promote(self, rhs);
match (&a, &b) { match (&a, &b) {
(V::Int(0), V::Int(0)) (V::Int(0), V::Int(0)) => {
=> throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power"), 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)) (V::Int(x), V::Int(y @ 0..)) => {
} else { if let Some(v) = ipow(*x, *y as u64) {
throw!(*SYM_VALUE_ERROR, "overflow when raising {a} to the power {b}") Ok(V::Int(v))
}, } else {
(V::Int(x), V::Int(y)) => if let Some(v) = rpow(*x, 1, *y) { throw!(
Ok(V::Ratio(v.into())) *SYM_VALUE_ERROR,
} else { "overflow when raising {a} to the power {b}"
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::Int(x), V::Int(y)) => {
(V::Float(x), V::Float(y)) if let Some(v) = rpow(*x, 1, *y) {
=> Ok(V::Float(x.powf(*y))), Ok(V::Ratio(v.into()))
(V::Complex(x), V::Complex(y)) } else {
=> Ok(V::Complex(x.powc(*y))), throw!(
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}") *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 // // Bitwise operations //
////////////////////////// //////////////////////////
impl Shl<Value> for Value { impl Shl<Value> for Value {
type Output = Result<Value>; type Output = Result<Value>;
@ -340,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:#}"),
} }
} }
} }
@ -352,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:#}"),
} }
} }
} }
@ -364,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:#}"),
} }
} }
} }
@ -376,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:#}"),
} }
} }
} }
@ -388,47 +437,46 @@ 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 // // Equality and ordering //
///////////////////////////// /////////////////////////////
impl PartialEq for Value { impl PartialEq for Value {
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,
(V::Int(a), V::Int(b)) => *a == *b, (V::Int(a), V::Int(b)) => *a == *b,
(V::Ratio(a), V::Ratio(b)) => *a == *b, (V::Ratio(a), V::Ratio(b)) => *a == *b,
(V::Float(a), V::Float(b)) => *a == *b, (V::Float(a), V::Float(b)) => *a == *b,
(V::Complex(a), V::Complex(b)) => *a == *b, (V::Complex(a), V::Complex(b)) => *a == *b,
(V::Int(a), V::Ratio(b)) => Rational64::from(*a) == *b, (V::Int(a), V::Ratio(b)) => Rational64::from(*a) == *b,
(V::Ratio(a), V::Int(b)) => *a == Rational64::from(*b), (V::Ratio(a), V::Int(b)) => *a == Rational64::from(*b),
(V::Int(a), V::Float(b)) => *a as f64 == *b, (V::Int(a), V::Float(b)) => *a as f64 == *b,
(V::Float(a), V::Int(b)) => *a == *b as f64, (V::Float(a), V::Int(b)) => *a == *b as f64,
(V::Int(a), V::Complex(b)) => Complex64::from(*a as f64) == *b, (V::Int(a), V::Complex(b)) => Complex64::from(*a as f64) == *b,
(V::Complex(a), V::Int(b)) => *a == Complex64::from(*b as f64), (V::Complex(a), V::Int(b)) => *a == Complex64::from(*b as f64),
(V::Ratio(a), V::Float(b)) => a.to_f64() == *b, (V::Ratio(a), V::Float(b)) => a.to_f64() == *b,
(V::Float(a), V::Ratio(b)) => *a == b.to_f64(), (V::Float(a), V::Ratio(b)) => *a == b.to_f64(),
(V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64()) == *b, (V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64()) == *b,
(V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64()), (V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64()),
(V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b, (V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b,
(V::Complex(a), V::Float(b)) => *a == Complex64::from(*b), (V::Complex(a), V::Float(b)) => *a == Complex64::from(*b),
(V::String(a), V::String(b)) => *a == *b, (V::String(a), V::String(b)) => *a == *b,
(V::List(a), V::List(b)) => *a.borrow() == *b.borrow(), (V::List(a), V::List(b)) => *a.borrow() == *b.borrow(),
(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,
@ -464,12 +512,10 @@ impl PartialOrd for Value {
} }
} }
//////////// ////////////
// misc // // 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) {
@ -485,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);
@ -507,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 {
RangeType::Open
};
Ok(Range {
start: *start,
stop: *stop,
ty,
}
.into())
} else { } else {
throw!(*SYM_TYPE_ERROR, "cannot create range between {self:#} and {other:#}") throw!(
*SYM_TYPE_ERROR,
"cannot create range between {self:#} and {other:#}"
)
} }
} }
pub fn range_endless(&self) -> Result<Self> { 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:#}")
} }
@ -543,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),
} }
} }
@ -553,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, symbol!("iter[range]")).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> {
@ -570,7 +634,7 @@ impl Value {
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[string]")).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> {
@ -583,16 +647,17 @@ impl Value {
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[list]")).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, symbol!("iter[table]")).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)]
@ -31,7 +32,7 @@ impl Range {
impl IntoIterator for Range { impl IntoIterator for Range {
type Item = i64; type Item = i64;
type IntoIter = Box<dyn Iterator<Item=i64>>; type IntoIter = Box<dyn Iterator<Item = i64>>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
match self.ty { match self.ty {
RangeType::Open => Box::new(self.start..self.stop), RangeType::Open => Box::new(self.start..self.stop),
@ -40,5 +41,3 @@ impl IntoIterator for Range {
} }
} }
} }

View file

@ -1,8 +1,26 @@
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::{parser::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>,
@ -78,25 +96,28 @@ fn get_call_outcome(args: Vec<Value>) -> Result<CallOutcome> {
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;
match argc.cmp(&attrs.arity) { match argc.cmp(&attrs.arity) {
Ordering::Equal => Ok(CallOutcome::Call(args)), Ordering::Equal => Ok(CallOutcome::Call(args)),
Ordering::Greater => throw!(*SYM_TYPE_ERROR, "too many arguments for function"), Ordering::Greater => throw!(*SYM_TYPE_ERROR, "too many arguments for function"),
Ordering::Less => { Ordering::Less => {
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 args: Vec<Value> = args.clone().into_iter().chain(ia).collect(); let args: Vec<Value> = args.clone().into_iter().chain(ia).collect();
vm.call_value(f.clone(), args) vm.call_value(f.clone(), args)
}; };
let nf = NativeFunc { let nf = NativeFunc {
attrs: FuncAttrs { arity: remaining, name: None }, attrs: FuncAttrs {
func: Box::new(nf), arity: remaining,
}; name: None,
Ok(CallOutcome::Partial(nf.into())) },
} func: Box::new(nf),
} };
Ok(CallOutcome::Partial(nf.into()))
}
}
} }
impl Vm { impl Vm {
@ -119,7 +140,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);
} }
@ -138,8 +161,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"),
} },
} }
} }
@ -175,7 +198,7 @@ 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);
@ -209,7 +232,10 @@ 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(())
@ -222,17 +248,13 @@ impl Vm {
// 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() };
@ -241,65 +263,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) => { I::CloseOver(n) => {
let n = usize::from(n); let n = usize::from(n);
let v = std::mem::replace(&mut frame.locals[n], Value::Nil); let v = std::mem::replace(&mut frame.locals[n], Value::Nil);
let v = v.to_cell(); let v = v.to_cell();
frame.locals[n] = v.clone(); frame.locals[n] = v.clone();
self.push(v); self.push(v);
}, }
I::Closure(n) => { I::Closure(n) => {
let f = frame.func.chunk.consts[usize::from(n)].clone(); let f = frame.func.chunk.consts[usize::from(n)].clone();
let Value::Function(f) = f else { let Value::Function(f) = f else {
panic!("attempt to build closure from non-closure constant") panic!("attempt to build closure from non-closure constant")
}; };
let mut f = f.as_ref().clone(); let mut f = f.as_ref().clone();
let captured: Vec<_> = self.pop_n(f.state.len()).into_iter() let captured: Vec<_> = self
.map(|v| { .pop_n(f.state.len())
let Value::Cell(v) = v else { .into_iter()
panic!("attempt to build closure from non-cell local"); .map(|v| {
}; let Value::Cell(v) = v else {
v panic!("attempt to build closure from non-cell local");
}).collect(); };
v
})
.collect();
f.state = captured.into_boxed_slice(); f.state = captured.into_boxed_slice();
self.push(f.into()); self.push(f.into());
}, }
I::LoadUpvalue(n) => { I::LoadUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone(); let v = frame.func.state[usize::from(n)].clone();
self.push(v.borrow().clone()); self.push(v.borrow().clone());
}, }
I::StoreUpvalue(n) => { I::StoreUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone(); let v = frame.func.state[usize::from(n)].clone();
*v.borrow_mut() = self.pop(); *v.borrow_mut() = self.pop();
}, }
I::ContinueUpvalue(n) => { I::ContinueUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone(); let v = frame.func.state[usize::from(n)].clone();
self.push(Value::Cell(v)); self.push(Value::Cell(v));
}, }
I::LoadClosedLocal(n) => { I::LoadClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else { let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to load from closed non-cell local"); panic!("attempt to load from closed non-cell local");
}; };
self.push(c.borrow().clone()); self.push(c.borrow().clone());
}, }
I::StoreClosedLocal(n) => { I::StoreClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else { let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to store to closed non-cell local"); panic!("attempt to store to closed non-cell local");
}; };
*c.borrow_mut() = self.pop(); *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]
@ -308,7 +332,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]
@ -317,38 +341,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();
@ -358,12 +388,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
@ -373,13 +405,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();
@ -387,27 +419,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) => {
self.check_interrupt()?; if self.pop().truthy() {
frame.ip = usize::from(n); self.check_interrupt()?;
}, 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) => {
self.check_interrupt()?; if !self.pop().truthy() {
frame.ip = usize::from(n); self.check_interrupt()?;
}, 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) => {
@ -417,19 +453,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);
@ -471,7 +507,6 @@ impl Vm {
self.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")
} }
@ -482,22 +517,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

@ -4,5 +4,5 @@ mod native_func;
#[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 {
native_func::native_func(input, annotated_item) native_func::native_func(input, annotated_item)
} }

View file

@ -1,22 +1,25 @@
use proc_macro::TokenStream; use proc_macro::TokenStream;
use syn::{parse::Parse, parse_macro_input, Token};
use quote::{quote, ToTokens}; use quote::{quote, ToTokens};
use syn::{parse::Parse, parse_macro_input, Token};
struct NativeFuncArgs { struct NativeFuncArgs {
arity: syn::LitInt, arity: syn::LitInt,
name: Option<syn::LitStr>, name: Option<syn::LitStr>,
} }
impl Parse for NativeFuncArgs { impl Parse for NativeFuncArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let arity = input.parse()?; let arity = input.parse()?;
let t: Option<Token![,]> = input.parse()?; let t: Option<Token![,]> = input.parse()?;
if t.is_some() { if t.is_some() {
let name = input.parse()?; let name = input.parse()?;
Ok(Self { arity, name: Some(name) }) Ok(Self {
} else { arity,
Ok(Self { arity, name: None }) name: Some(name),
} })
} else {
Ok(Self { arity, name: None })
}
} }
} }
@ -34,14 +37,17 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
let arity = args.arity; let arity = args.arity;
let talc_name = match args.name { let talc_name = match args.name {
Some(n) => n.to_token_stream(), Some(n) => n.to_token_stream(),
None => name.to_token_stream(), None => name.to_token_stream(),
}; };
assert!(itemfn.sig.constness.is_none(), "item must not be const"); 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.asyncness.is_none(), "item must not be async");
assert!(itemfn.sig.unsafety.is_none(), "item must not be unsafe"); 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.abi.is_none(),
"item must not contain an ABI specifier"
);
assert!(itemfn.sig.variadic.is_none(), "item must not be variadic"); assert!(itemfn.sig.variadic.is_none(), "item must not be variadic");
let expanded = quote! { let expanded = quote! {
@ -49,7 +55,7 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
::talc_lang::value::function::NativeFunc { ::talc_lang::value::function::NativeFunc {
attrs: ::talc_lang::value::function::FuncAttrs{ attrs: ::talc_lang::value::function::FuncAttrs{
arity: #arity, arity: #arity,
name: Some(::talc_lang::symbol::symbol!(#talc_name)) name: Some(::talc_lang::symbol::symbol!(#talc_name))
}, },
func: Box::new(|#inputs| #output #block) func: Box::new(|#inputs| #output #block)
} }

View file

@ -1,11 +1,16 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use talc_lang::{exception::{exception, Result}, symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_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());
@ -55,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)
} }
@ -63,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)
} }
@ -82,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;
}, }
} }
} }
@ -110,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;
}, }
} }
} }
@ -127,8 +138,8 @@ fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, u
fn insertion(vals: &mut [Value]) { fn insertion(vals: &mut [Value]) {
for i in 0..vals.len() { for i in 0..vals.len() {
let mut j = i; let mut j = i;
while j > 0 && vals[j-1] > vals[j] { while j > 0 && vals[j - 1] > vals[j] {
vals.swap(j, j-1); vals.swap(j, j - 1);
j -= 1; j -= 1;
} }
} }
@ -138,11 +149,11 @@ 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;
} }
} }
@ -165,7 +176,7 @@ fn sort_inner(vals: &mut [Value], by: Option<&Value>, vm: &mut Vm) -> Result<()>
None => partition(vals), None => partition(vals),
}; };
sort_inner(&mut vals[..lt], by, vm)?; sort_inner(&mut vals[..lt], by, vm)?;
sort_inner(&mut vals[(gt+1)..], by, vm) sort_inner(&mut vals[(gt + 1)..], by, vm)
} }
#[native_func(1)] #[native_func(1)]
@ -202,7 +213,10 @@ 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_VALUE_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, symbol!("inner[sort_key]")).into(); let nf = NativeFunc::new(Box::new(f), 2, symbol!("inner[sort_key]")).into();
@ -212,32 +226,32 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(2)] #[native_func(2)]
pub fn pair(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn pair(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args); let [_, x, y] = unpack_args!(args);
Ok(vec![x, y].into()) Ok(vec![x, y].into())
} }
#[native_func(1)] #[native_func(1)]
pub fn fst(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn fst(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, l] = unpack_args!(args); let [_, l] = unpack_args!(args);
let Value::List(l) = l else { let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "fst: expected list") throw!(*SYM_TYPE_ERROR, "fst: expected list")
}; };
let l = l.borrow(); let l = l.borrow();
let [x, _] = l.as_slice() else { let [x, _] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "fst: list must have length 2") throw!(*SYM_VALUE_ERROR, "fst: list must have length 2")
}; };
Ok(x.clone()) Ok(x.clone())
} }
#[native_func(1)] #[native_func(1)]
pub fn snd(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn snd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, l] = unpack_args!(args); let [_, l] = unpack_args!(args);
let Value::List(l) = l else { let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "snd: expected list") throw!(*SYM_TYPE_ERROR, "snd: expected list")
}; };
let l = l.borrow(); let l = l.borrow();
let [_, y] = l.as_slice() else { let [_, y] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "snd: list must have length 2") throw!(*SYM_VALUE_ERROR, "snd: list must have length 2")
}; };
Ok(y.clone()) Ok(y.clone())
} }

View file

@ -1,4 +1,9 @@
use talc_lang::{exception::{throw, Exception, Result}, symbol::{SYM_TYPE_ERROR, SYM_VALUE_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; use crate::unpack_args;
@ -6,23 +11,24 @@ 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 [_, arg] = unpack_args!(args); let [_, arg] = unpack_args!(args);
let exc = match arg { let exc = match arg {
Value::Symbol(ty) => Exception::new(ty), Value::Symbol(ty) => Exception::new(ty),
Value::List(l) => match l.borrow().as_slice() { Value::List(l) => match l.borrow().as_slice() {
[Value::Symbol(ty)] | [Value::Symbol(ty), Value::Nil] [Value::Symbol(ty)] | [Value::Symbol(ty), Value::Nil] => Exception::new(*ty),
=> Exception::new(*ty), [Value::Symbol(ty), Value::Nil, data] => Exception::new_with_data(*ty, data.clone()),
[Value::Symbol(ty), Value::Nil, data] [Value::Symbol(ty), Value::String(s)] => Exception::new_with_msg(*ty, s.clone()),
=> Exception::new_with_data(*ty, data.clone()), [Value::Symbol(ty), Value::String(s), data] => {
[Value::Symbol(ty), Value::String(s)] Exception::new_with_msg_data(*ty, s.clone(), data.clone())
=> Exception::new_with_msg(*ty, s.clone()), }
[Value::Symbol(ty), Value::String(s), data] [] | [_] | [_, _] | [_, _, _] => throw!(*SYM_TYPE_ERROR, "wrong argument for throw"),
=> Exception::new_with_msg_data(*ty, s.clone(), data.clone()), [_, _, _, _, ..] => throw!(
[] | [_] | [_,_] | [_,_,_] => throw!(*SYM_TYPE_ERROR, "wrong argument for throw"), *SYM_VALUE_ERROR,
[_,_,_,_,..] => throw!(*SYM_VALUE_ERROR, "too many elements in list argument for throw"), "too many elements in list argument for throw"
} ),
_ => throw!(*SYM_TYPE_ERROR, "throw expected symbol or list"), },
}; _ => throw!(*SYM_TYPE_ERROR, "throw expected symbol or list"),
Err(exc) };
Err(exc)
} }
#[native_func(1)] #[native_func(1)]
@ -32,7 +38,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_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, Symbol, SYM_TYPE_ERROR, SYM_VALUE_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,8 +110,8 @@ 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()? }),
} }
} }
@ -152,9 +170,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, _recur: &mut Vec<*const ()>) -> 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 +197,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, _recur: &mut Vec<*const ()>) -> 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 +237,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,33 +253,35 @@ 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_VALUE_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) => {
let Value::Symbol(s) = s else { for s in l.borrow().iter() {
throw!(*SYM_TYPE_ERROR, "invalid option for open") let Value::Symbol(s) = s else {
}; throw!(*SYM_TYPE_ERROR, "invalid option for open")
if *s == *SYM_U {
buffered = false;
} else {
match OPEN_OPT_MAP.get(s) {
Some(f) => f(&mut oo, true),
None => throw!(*SYM_VALUE_ERROR, "invalid option for open")
}; };
if *s == *SYM_U {
buffered = false;
} else {
match OPEN_OPT_MAP.get(s) {
Some(f) => f(&mut oo, true),
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 +293,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_VALUE_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:#}")
@ -317,7 +357,7 @@ pub fn read_until(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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,7 +376,7 @@ 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_VALUE_ERROR, "read_line: file must be buffered") throw!(*SYM_VALUE_ERROR, "read_line: file must be buffered")
@ -404,7 +444,7 @@ pub fn tcp_connect(_: &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}"),
} }
} }
@ -413,16 +453,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_VALUE_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_VALUE_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,24 +485,23 @@ 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, symbol!("inner[tcp_listen]")).into() NativeFunc::new(Box::new(func), 0, symbol!("inner[tcp_listen]")).into()
} }
@ -468,14 +517,12 @@ pub fn tcp_listen(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}; };
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 +546,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_VALUE_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 +582,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) => {
let Value::String(e) = e else { for e in l.borrow().iter() {
throw!(*SYM_TYPE_ERROR, let Value::String(e) = e else {
"{fname} delenv option expected :all or list of strings, got {delenv:#}") throw!(
}; *SYM_TYPE_ERROR,
proc.env_remove(e.to_os_str()); "{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:#}") proc.env_remove(e.to_os_str());
}
}
_ => throw!(
*SYM_VALUE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}"
),
} }
Ok(()) Ok(())
} }
@ -544,15 +605,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) => {
let Value::String(k) = k.inner() else { for (k, v) in t.borrow().iter() {
throw!(*SYM_TYPE_ERROR, let Value::String(k) = k.inner() else {
"{fname} env option expected table with string keys, got {env:#}") throw!(
}; *SYM_TYPE_ERROR,
proc.env(k.to_os_str(), v.str().to_os_str()); "{fname} env option expected table with string keys, got {env:#}"
}, )
_ => throw!(*SYM_TYPE_ERROR, };
"{fname} env option expected table with string keys, got {env:#}") 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:#}"
),
} }
Ok(()) Ok(())
} }
@ -560,7 +627,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 +647,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 +694,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 +708,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 +735,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 +751,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 +768,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())
} }

View file

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

View file

@ -1,6 +1,17 @@
use std::{io::{BufRead, Write}, os::unix::ffi::OsStrExt, sync::Mutex, 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, SYM_VALUE_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};
@ -45,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)]
@ -72,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_VALUE_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 {
@ -88,19 +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_VALUE_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_VALUE_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);
} }
{
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);
}
Ok(Value::Nil) Ok(Value::Nil)
} }
@ -111,19 +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_VALUE_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);
} }
{
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);
}
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,6 +1,12 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
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_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; use crate::unpack_args;
@ -95,9 +101,7 @@ 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, symbol!("iter[once]")).into())
} }
@ -105,9 +109,7 @@ pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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, symbol!("iter[forever]")).into())
} }
@ -115,18 +117,14 @@ pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// 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, symbol!("iter[map]")).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[map]")).into())
} }
@ -136,14 +134,12 @@ 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, symbol!("iter[tee]")).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[tee]")).into())
} }
@ -171,15 +167,13 @@ 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)) };
}; let res = vmcall!(vm; filter.clone(), next.clone())?;
let res = vmcall!(vm; filter.clone(), next.clone())?; if res.truthy() {
if res.truthy() { return Ok(next)
return Ok(next)
}
} }
}; };
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[filter]")).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[filter]")).into())
@ -192,7 +186,10 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}") 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_VALUE_ERROR, "take expected nonnegative integer, got {count:#}") throw!(
*SYM_VALUE_ERROR,
"take expected nonnegative integer, got {count:#}"
)
}; };
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
@ -218,7 +215,10 @@ pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}") 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_VALUE_ERROR, "count expected nonnegative integer, got {count:#}") throw!(
*SYM_VALUE_ERROR,
"count expected nonnegative integer, got {count:#}"
)
}; };
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
@ -255,7 +255,6 @@ pub fn enumerate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[enumerate]")).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);
@ -292,7 +291,10 @@ pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[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)]
@ -303,7 +305,10 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}") throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}")
}; };
if by <= 0 { if by <= 0 {
throw!(*SYM_VALUE_ERROR, "step expected positive integer, got {by:#}") throw!(
*SYM_VALUE_ERROR,
"step expected positive integer, got {by:#}"
)
} }
let state = RefCell::new(Step::First); let state = RefCell::new(Step::First);
@ -314,9 +319,9 @@ 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 {
return Ok(Value::iter_pack(None)) return Ok(Value::iter_pack(None))
} }
@ -326,7 +331,7 @@ 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, symbol!("iter[step]")).into()) Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[step]")).into())
@ -347,33 +352,30 @@ 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, symbol!("iter[rev]")).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] = unpack_args!(args); let [_, i1, i2] = unpack_args!(args);
let i1 = i1.to_iter_function()?; let i1 = i1.to_iter_function()?;
let i2 = i2.to_iter_function()?; let i2 = i2.to_iter_function()?;
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| {
let mut res = Vec::with_capacity(2); let mut res = Vec::with_capacity(2);
match vmcalliter!(vm; i1.clone())? { match vmcalliter!(vm; i1.clone())? {
Some(v) => res.push(v), Some(v) => res.push(v),
None => return Ok(Value::iter_pack(None)), None => return Ok(Value::iter_pack(None)),
}; };
match vmcalliter!(vm; i2.clone())? { match vmcalliter!(vm; i2.clone())? {
Some(v) => res.push(v), Some(v) => res.push(v),
None => return Ok(Value::iter_pack(None)), None => return Ok(Value::iter_pack(None)),
}; };
Ok(Value::from(res)) Ok(Value::from(res))
}; };
@ -383,12 +385,14 @@ pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)] #[native_func(1)]
pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args); let [_, args] = unpack_args!(args);
let Value::List(args) = args else { let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "zipn expected list, got {args:#}") throw!(*SYM_TYPE_ERROR, "zipn expected list, got {args:#}")
}; };
let iters = args.borrow().iter() let iters = args
.map(|i| i.clone().to_iter_function()) .borrow()
.collect::<Result<Vec<_>>>()?; .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());
@ -407,19 +411,19 @@ pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[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] = unpack_args!(args); let [_, i1, i2] = unpack_args!(args);
let i1 = i1.to_iter_function()?; let i1 = i1.to_iter_function()?;
let i2 = i2.to_iter_function()?; let i2 = i2.to_iter_function()?;
let state = RefCell::new((false, false)); let state = RefCell::new((false, 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; s.0 = !s.0;
drop(s); drop(s);
let iter = if n { i1.clone() } else { i2.clone() }; let iter = if n { i1.clone() } else { i2.clone() };
if let Some(v) = vmcalliter!(vm; iter)? { if let Some(v) = vmcalliter!(vm; iter)? {
Ok(v) Ok(v)
} else { } else {
@ -434,18 +438,20 @@ pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)] #[native_func(1)]
pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args); let [_, args] = unpack_args!(args);
let Value::List(args) = args else { let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "alternaten expected list, got {args:#}") throw!(*SYM_TYPE_ERROR, "alternaten expected list, got {args:#}")
}; };
let iters = args.borrow().iter() let iters = args
.map(|i| i.clone().to_iter_function()) .borrow()
.collect::<Result<Vec<_>>>()?; .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();
@ -463,7 +469,11 @@ pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[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)]
@ -472,38 +482,35 @@ 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;
Ok(v)
} else {
*state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None))
}
},
Intersperse::Waiting => {
if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::HasNext(v);
Ok(val.clone())
} else {
*state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None))
}
},
Intersperse::HasNext(v) => {
*state.borrow_mut() = Intersperse::Waiting; *state.borrow_mut() = Intersperse::Waiting;
Ok(v) Ok(v)
}, } else {
Intersperse::End => Ok(Value::iter_pack(None)), *state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None))
}
} }
Intersperse::Waiting => {
if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::HasNext(v);
Ok(val.clone())
} else {
*state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None))
}
}
Intersperse::HasNext(v) => {
*state.borrow_mut() = Intersperse::Waiting;
Ok(v)
}
Intersperse::End => Ok(Value::iter_pack(None)),
}; };
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[intersperse]")).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);
@ -591,14 +598,10 @@ pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cart_prod]")).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);
@ -606,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())
} }
@ -617,16 +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, got {value:#}") throw!(
*SYM_TYPE_ERROR,
"table expected iterator to yield list, got {value:#}"
)
}; };
let mut l = Rc::unwrap_or_clone(l).take(); let mut l = Rc::unwrap_or_clone(l).take();
if l.len() != 2 { if l.len() != 2 {
throw!(*SYM_VALUE_ERROR, "table: iterator yielded list of length {} (expected 2)", l.len()) throw!(
*SYM_VALUE_ERROR,
"table: iterator yielded list of length {} (expected 2)",
l.len()
)
}; };
let v = l.pop().unwrap(); let v = l.pop().unwrap();
let k = 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())
} }
@ -637,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())
} }
@ -764,8 +775,10 @@ 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) => {
return Ok(true.into()) if v == val {
return Ok(true.into())
}
} }
} }
} }
@ -780,8 +793,10 @@ 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) => {
return Ok(i.into()) if v == val {
return Ok(i.into())
}
} }
} }
} }
@ -796,8 +811,10 @@ 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) => {
return Ok(i.into()) if vmcall!(vm; func.clone(), v)?.truthy() {
return Ok(i.into())
}
} }
} }
} }
@ -844,25 +861,25 @@ pub fn mean(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let mut sum = Value::Float(0.0); let mut sum = Value::Float(0.0);
let mut count = Value::Int(0); let mut count = Value::Int(0);
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
sum = (sum + value)?; sum = (sum + value)?;
count = (count + Value::from(1))?; count = (count + Value::from(1))?;
} }
sum / count sum / count
} }
fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> { fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> {
let mut m = Value::Float(0.0); let mut m = Value::Float(0.0);
let mut s = Value::Float(0.0); let mut s = Value::Float(0.0);
let mut k = 1; let mut k = 1;
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
let old_m = m.clone(); let old_m = m.clone();
m = (m.clone() + ((value.clone() - m.clone())?/Value::Int(k))?)?; m = (m.clone() + ((value.clone() - m.clone())? / Value::Int(k))?)?;
s = (s + ((value.clone() - m.clone())?*(value - old_m)?)?)?; s = (s + ((value.clone() - m.clone())? * (value - old_m)?)?)?;
k += 1; k += 1;
} }
s / Value::Int(k - if pop { 1 } else { 2 }) s / Value::Int(k - if pop { 1 } else { 2 })
} }
#[native_func(1)] #[native_func(1)]
@ -870,7 +887,7 @@ pub fn variance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args); let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
variance_inner(vm, iter, false) variance_inner(vm, iter, false)
} }
#[native_func(1)] #[native_func(1)]
@ -878,14 +895,14 @@ pub fn stdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args); let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let v = variance_inner(vm, iter, false)?; let v = variance_inner(vm, iter, false)?;
Ok(match v { Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()), Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Float(f) => Value::Float(f.sqrt()), Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()), Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()), Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}") v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
}) })
} }
#[native_func(1)] #[native_func(1)]
@ -893,7 +910,7 @@ pub fn pvariance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args); let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
variance_inner(vm, iter, true) variance_inner(vm, iter, true)
} }
#[native_func(1)] #[native_func(1)]
@ -901,14 +918,14 @@ pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args); let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let v = variance_inner(vm, iter, true)?; let v = variance_inner(vm, iter, true)?;
Ok(match v { Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()), Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Float(f) => Value::Float(f.sqrt()), Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()), Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()), Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}") v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
}) })
} }
#[derive(PartialEq, PartialOrd)] #[derive(PartialEq, PartialOrd)]
@ -917,9 +934,9 @@ impl std::cmp::Eq for OrdValue {}
#[allow(clippy::derive_ord_xor_partial_ord)] #[allow(clippy::derive_ord_xor_partial_ord)]
impl std::cmp::Ord for OrdValue { impl std::cmp::Ord for OrdValue {
fn cmp(&self, other: &Self) -> std::cmp::Ordering { fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Less) self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Less)
} }
} }
#[native_func(1)] #[native_func(1)]
@ -927,23 +944,22 @@ pub fn median(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args); let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let mut vals = Vec::new(); let mut vals = Vec::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? { while let Some(value) = vmcalliter!(vm; iter.clone())? {
vals.push(OrdValue(value)); 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)
} }
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,19 +1,22 @@
#![allow(clippy::mutable_key_type)] #![allow(clippy::mutable_key_type)]
use talc_lang::{symbol::{symbol, Symbol}, Vm}; use talc_lang::{
symbol::{symbol, Symbol},
Vm,
};
pub mod value; 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; #[cfg(feature = "random")]
pub mod string;
pub mod format;
pub mod file;
pub mod regex;
#[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);
@ -26,7 +29,7 @@ pub fn load_all(vm: &mut Vm) {
format::load(vm); format::load(vm);
regex::load(vm); regex::load(vm);
file::load(vm); file::load(vm);
#[cfg(feature="random")] #[cfg(feature = "random")]
random::load(vm); random::load(vm);
} }
@ -35,7 +38,6 @@ lazy_static::lazy_static! {
pub static ref SYM_FORMAT_ERROR: Symbol = symbol!(format_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")

View file

@ -1,7 +1,14 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use lazy_static::lazy_static; use lazy_static::lazy_static;
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_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; use crate::unpack_args;
@ -49,8 +56,8 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("inf", (f64::INFINITY).into()); vm.set_global_name("inf", (f64::INFINITY).into());
vm.set_global_name("NaN", (f64::NAN).into()); vm.set_global_name("NaN", (f64::NAN).into());
vm.set_global_name("infi", Complex64::new(0.0,f64::INFINITY).into()); vm.set_global_name("infi", Complex64::new(0.0, f64::INFINITY).into());
vm.set_global_name("NaNi", Complex64::new(0.0,f64::NAN).into()); vm.set_global_name("NaNi", Complex64::new(0.0, f64::NAN).into());
vm.set_global_name("bin", bin().into()); vm.set_global_name("bin", bin().into());
vm.set_global_name("sex", sex().into()); vm.set_global_name("sex", sex().into());
@ -163,7 +170,10 @@ pub fn hex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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_VALUE_ERROR, "to_radix expected radix in range 0..=36") throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 0..=36")
@ -175,10 +185,16 @@ pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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_VALUE_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_lstring_radix(*x, *radix as u32, true).into()) Ok(to_lstring_radix(*x, *radix as u32, true).into())
} }
@ -187,14 +203,23 @@ pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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_VALUE_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_VALUE_ERROR, "string was not a valid integer in given radix"), Err(_) => throw!(
*SYM_VALUE_ERROR,
"string was not a valid integer in given radix"
),
} }
} }
@ -204,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;
@ -217,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;
@ -242,23 +268,25 @@ pub fn gcd_inner(a: i64, b: i64) -> i64 {
let z = az.min(bz); let z = az.min(bz);
loop { loop {
if a > b { if a > b {
std::mem::swap(&mut a, &mut b); std::mem::swap(&mut a, &mut b);
} }
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_VALUE_ERROR, "isqrt: argument must be positive") throw!(*SYM_VALUE_ERROR, "isqrt: argument must be positive")
@ -266,12 +294,14 @@ pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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())
@ -286,9 +316,13 @@ 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())
@ -309,11 +343,11 @@ pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)] #[native_func(1)]
pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args); let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?; let args = args.to_iter_function()?;
let mut g = 0; let mut g = 0;
while let Some(a) = vmcalliter!(vm; args.clone())? { while let Some(a) = vmcalliter!(vm; args.clone())? {
let Value::Int(a) = a else { let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}") throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}")
}; };
@ -333,41 +367,46 @@ pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}") throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}")
}; };
let g = gcd_inner(x, y); let g = gcd_inner(x, y);
if g == 0 { if g == 0 {
Ok(Value::from(0)) Ok(Value::from(0))
} else { } else {
(Value::from(x)/Value::from(g))? * Value::from(y) (Value::from(x) / Value::from(g))? * Value::from(y)
} }
} }
#[native_func(1)] #[native_func(1)]
pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args); let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?; let args = args.to_iter_function()?;
let mut l = 1; let mut l = 1;
while let Some(a) = vmcalliter!(vm; args.clone())? { while let Some(a) = vmcalliter!(vm; args.clone())? {
let Value::Int(a) = a else { let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}") throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
}; };
let g = gcd_inner(l, a); let g = gcd_inner(l, a);
if g == 0 { return Ok(Value::from(0)) }; if g == 0 {
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?; return Ok(Value::from(0))
let Value::Int(new_l) = new_l else { };
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") unreachable!("int//int * int != int")
}; };
l = new_l; l = new_l;
} }
Ok(Value::from(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, "factors 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 {
@ -382,7 +421,7 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
factors.push(Value::Int(3)); factors.push(Value::Int(3));
} }
let mut i = 5; let mut i = 5;
while x >= i*i { while x >= i * i {
while x % i == 0 { while x % i == 0 {
x /= i; x /= i;
factors.push(Value::Int(i)); factors.push(Value::Int(i));
@ -401,49 +440,53 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} }
fn totient_prime(n: &mut u64, p: u64) -> u64 { fn totient_prime(n: &mut u64, p: u64) -> u64 {
if *n % p != 0 { if *n % p != 0 {
return 1 return 1
} }
*n /= p; *n /= p;
let mut v = p - 1; let mut v = p - 1;
while *n % p == 0 { while *n % p == 0 {
*n /= p; *n /= p;
v *= p; v *= p;
} }
v v
} }
#[native_func(1)] #[native_func(1)]
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn totient(_: &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, "totient expected integer argument, got {x:#}") throw!(
*SYM_TYPE_ERROR,
"totient expected integer argument, got {x:#}"
)
}; };
if x <= 1 { if x <= 1 {
return Ok(1.into()) return Ok(1.into())
} }
let mut x = x as u64; let mut x = x as u64;
let mut totient = 1; let mut totient = 1;
if x & 1 == 0 { x >>= 1; } if x & 1 == 0 {
x >>= 1;
}
while x & 1 == 0 { while x & 1 == 0 {
x >>= 1; x >>= 1;
totient <<= 1; totient <<= 1;
} }
totient *= totient_prime(&mut x, 3); totient *= totient_prime(&mut x, 3);
let mut i = 5; let mut i = 5;
while x >= i*i { while x >= i * i {
totient *= totient_prime(&mut x, i); totient *= totient_prime(&mut x, i);
i += 2; i += 2;
totient *= totient_prime(&mut x, i); totient *= totient_prime(&mut x, i);
i += 4; i += 4;
} }
if x > 1 { if x > 1 {
totient *= x - 1; totient *= x - 1;
} }
Ok((totient as i64).into()) Ok((totient as i64).into())
} }
// //
// numeric operations // numeric operations
// //
@ -451,21 +494,21 @@ pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(2)] #[native_func(2)]
pub fn min(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn min(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args); let [_, x, y] = unpack_args!(args);
if y < x { if y < x {
Ok(y) Ok(y)
} else { } else {
Ok(x) Ok(x)
} }
} }
#[native_func(2)] #[native_func(2)]
pub fn max(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn max(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args); let [_, x, y] = unpack_args!(args);
if y > x { if y > x {
Ok(y) Ok(y)
} else { } else {
Ok(x) Ok(x)
} }
} }
#[native_func(1)] #[native_func(1)]
@ -533,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:#}"),
} }
} }
@ -542,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);
@ -557,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,
@ -565,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)]
@ -575,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:#}"
),
} }
} }
@ -586,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:#}"
),
} }
} }
@ -597,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:#}"
),
} }
} }
@ -605,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))
} }
@ -614,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:#}"
),
} }
} }
@ -642,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:#}"
),
} }
} }
@ -650,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);
@ -688,7 +750,7 @@ 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);
x.abs() x.abs()
} }
#[native_func(1)] #[native_func(1)]
@ -699,17 +761,17 @@ pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Ratio(_) => x.clone() * x, Value::Ratio(_) => x.clone() * x,
Value::Float(_) => x.clone() * 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)]
@ -718,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)
),
} }
} }
}; };
@ -750,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::{exception::Result, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{range::RangeType, Value}, vmcalliter, Vm}; 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;
@ -21,11 +27,11 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
match col { match col {
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_VALUE_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() {
@ -34,16 +40,14 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
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_VALUE_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 => Ok(Value::Int(rand::thread_rng().gen_range(r.start..r.stop))),
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 => Ok(Value::Int(
rand::thread_rng().gen_range(r.start..=r.stop))),
RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"), RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"),
} }
} }

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, SYM_VALUE_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, _recur: &mut Vec<*const ()>) -> 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(),
);
}) })
} }
@ -78,22 +101,28 @@ fn regex_from<'a>(v: &'a Value, name: &str) -> Result<Cow<'a, Regex>> {
Regex::new(s) Regex::new(s)
.map(Cow::Owned) .map(Cow::Owned)
.map_err(|e| exception!(*SYM_VALUE_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()
.map(|vr| Cow::Borrowed(&vr.0)) .downcast_ref::<ValueRegex>()
.ok_or_else(|| exception!( .map(|vr| Cow::Borrowed(&vr.0))
*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}")) .ok_or_else(|| {
}, exception!(
_ => throw!(*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}") *SYM_TYPE_ERROR,
"{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)]
@ -132,7 +161,11 @@ pub fn _match(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_VALUE_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)]
@ -158,7 +191,11 @@ pub fn captures(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_VALUE_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,7 +205,10 @@ 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_VALUE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
@ -187,7 +227,10 @@ 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_VALUE_ERROR, "search string must be valid UTF-8") throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
@ -212,7 +255,7 @@ pub fn split_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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())
} }
@ -227,9 +270,6 @@ pub fn split(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_VALUE_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, lstring::LString, symbol::{SYM_TYPE_ERROR, SYM_VALUE_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;
@ -54,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))
} }
@ -92,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())
} }
@ -104,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())
} }
@ -114,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())
} }
@ -123,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())
@ -136,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())
} }
@ -145,25 +168,37 @@ 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()
.map(|v| (*v as i64).into()) .map(|v| (*v as i64).into())
.collect::<Vec<Value>>() .collect::<Vec<Value>>()
.into()) .into())
} }
#[native_func(1)] #[native_func(1)]
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_VALUE_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())
} }

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, parser::{parse_float, parse_int}, symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_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;
@ -34,7 +42,6 @@ pub fn load(vm: &mut Vm) {
// types // types
// //
#[native_func(1, "type")] #[native_func(1, "type")]
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);
@ -74,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_VALUE_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_VALUE_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_VALUE_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()
),
} }
} }
@ -140,7 +163,6 @@ pub fn copy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// strings // strings
// //
#[native_func(1, "str")] #[native_func(1, "str")]
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);
@ -160,38 +182,37 @@ pub fn repr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)] #[native_func(1)]
pub fn symbol_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn symbol_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args); let [_, val] = unpack_args!(args);
let Value::Symbol(s) = val else { let Value::Symbol(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_name: expected symbol") throw!(*SYM_TYPE_ERROR, "symbol_name: expected symbol")
}; };
Ok(s.name().into()) Ok(s.name().into())
} }
#[native_func(1)] #[native_func(1)]
pub fn symbol_of(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn symbol_of(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args); let [_, val] = unpack_args!(args);
let Value::String(s) = val else { let Value::String(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string") throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
}; };
Ok(Symbol::get(s.as_ref()).into()) Ok(Symbol::get(s.as_ref()).into())
} }
#[native_func(1)] #[native_func(1)]
pub fn symbol_exists(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn symbol_exists(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args); let [_, val] = unpack_args!(args);
let Value::String(s) = val else { let Value::String(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string") throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
}; };
match Symbol::try_get(s.as_ref()) { match Symbol::try_get(s.as_ref()) {
Some(s) => Ok(s.into()), Some(s) => Ok(s.into()),
None => Ok(Value::Nil), None => Ok(Value::Nil),
} }
} }
// //
// cells // 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> {
let [_, value] = unpack_args!(args); let [_, value] = unpack_args!(args);
@ -232,66 +253,78 @@ pub fn cell_take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)] #[native_func(1)]
pub fn func_state(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn func_state(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args); let [_, func] = unpack_args!(args);
match func { match func {
Value::NativeFunc(_) => Ok(Value::Nil), Value::NativeFunc(_) => Ok(Value::Nil),
Value::Function(f) => { Value::Function(f) => {
let l: Vec<Value> = f.state.iter() let l: Vec<Value> = f.state.iter().map(|v| Value::Cell(v.clone())).collect();
.map(|v| Value::Cell(v.clone())) Ok(l.into())
.collect(); }
Ok(l.into()) _ => throw!(
} *SYM_TYPE_ERROR,
_ => throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a talc function") "closure_state: {func:#} is not a talc function"
} ),
}
} }
#[native_func(1)] #[native_func(1)]
pub fn func_arity(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn func_arity(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args); let [_, func] = unpack_args!(args);
let Some(attrs) = func.func_attrs() else { let Some(attrs) = func.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function") throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
}; };
Ok((attrs.arity as i64).into()) Ok((attrs.arity as i64).into())
} }
#[native_func(1)] #[native_func(1)]
pub fn func_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn func_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args); let [_, func] = unpack_args!(args);
let Some(attrs) = func.func_attrs() else { let Some(attrs) = func.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function") throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
}; };
if let Some(name) = attrs.name { if let Some(name) = attrs.name {
Ok(name.into()) Ok(name.into())
} else { } else {
Ok(Value::Nil) Ok(Value::Nil)
} }
} }
#[native_func(2)] #[native_func(2)]
pub fn apply(vm: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn apply(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func, lst] = unpack_args!(args); let [_, func, lst] = unpack_args!(args);
if func.func_attrs().is_none() { if func.func_attrs().is_none() {
throw!(*SYM_TYPE_ERROR, "apply: first argument must be a function, found {func:#}") throw!(
} *SYM_TYPE_ERROR,
let Value::List(l) = lst else { "apply: first argument must be a function, found {func:#}"
throw!(*SYM_TYPE_ERROR, "apply: second argument must be a list, found {lst:#}") )
}; }
let mut args = l.borrow().clone(); let Value::List(l) = lst else {
args.insert(0, func.clone()); throw!(
vm.call_value(func, args) *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)] #[native_func(1)]
pub fn compile(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn compile(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, src] = unpack_args!(args); let [_, src] = unpack_args!(args);
let Value::String(src) = src else { let Value::String(src) = src else {
throw!(*SYM_TYPE_ERROR, "compile: argument must be a string, found {src:#}") throw!(
}; *SYM_TYPE_ERROR,
let src = src.to_str() "compile: argument must be a string, found {src:#}"
.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 src = src.to_str().map_err(|e| {
let func = talc_lang::compiler::compile(&ast, None); exception!(
Ok(func.into()) *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())
} }