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,7 +67,9 @@ 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 {
@ -71,7 +85,9 @@ impl Highlighter for TalcHelper {
}; };
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)
@ -103,66 +119,85 @@ impl Validator for TalcHelper {
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)));

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
} }
} }
@ -150,10 +162,16 @@ impl std::fmt::Display for Instruction {
Self::StoreLocal(a) => write!(f, "store {}", usize::from(a)), Self::StoreLocal(a) => write!(f, "store {}", usize::from(a)),
Self::NewLocal => write!(f, "newlocal"), Self::NewLocal => write!(f, "newlocal"),
Self::DropLocal(n) => write!(f, "discardlocal {}", usize::from(n)), Self::DropLocal(n) => write!(f, "discardlocal {}", usize::from(n)),
Self::LoadGlobal(s) => write!(f, "loadglobal {}", Self::LoadGlobal(s) => write!(
Symbol::try_from(s).expect("symbol does not exist").name()), f,
Self::StoreGlobal(s) => write!(f, "storeglobal {}", "loadglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()), Symbol::try_from(s).expect("symbol does not exist").name()
),
Self::StoreGlobal(s) => write!(
f,
"storeglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()
),
Self::CloseOver(n) => write!(f, "closeover {}", usize::from(n)), Self::CloseOver(n) => write!(f, "closeover {}", usize::from(n)),
Self::Closure(c) => write!(f, "closure {}", usize::from(c)), Self::Closure(c) => write!(f, "closure {}", usize::from(c)),
Self::LoadUpvalue(n) => write!(f, "load_upval {}", usize::from(n)), Self::LoadUpvalue(n) => write!(f, "load_upval {}", usize::from(n)),
@ -163,8 +181,11 @@ impl std::fmt::Display for Instruction {
Self::StoreClosedLocal(n) => write!(f, "store_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,8 +1,8 @@
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;
@ -15,7 +15,9 @@ enum ResolveOutcome {
#[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)]
@ -26,7 +28,9 @@ pub struct Var {
#[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> {
@ -55,10 +59,13 @@ 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(
*SYM_SELF,
Var {
name: *SYM_SELF, name: *SYM_SELF,
kind: VarKind::Local(0), kind: VarKind::Local(0),
}); },
);
Self { Self {
mode: CompilerMode::Function, mode: CompilerMode::Function,
parent: None, parent: None,
@ -76,7 +83,10 @@ 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()
}; };
@ -98,7 +108,10 @@ 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()
}; };
@ -120,7 +133,7 @@ impl<'a> Compiler<'a> {
// 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,
) )
} }
@ -129,9 +142,10 @@ impl<'a> Compiler<'a> {
( (
// 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,26 +168,32 @@ 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::Symbol(_) | I::LoadLocal(_)
| I::LoadClosedLocal(_)
| I::LoadUpvalue(_) | I::LoadUpvalue(_)
) )
); );
@ -181,11 +201,11 @@ impl<'a> Compiler<'a> {
// 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)));
@ -226,7 +246,6 @@ impl<'a> Compiler<'a> {
} }
} }
// //
// variables // variables
// //
@ -272,7 +291,7 @@ impl<'a> Compiler<'a> {
fn declare_local(&mut self, name: Symbol) -> usize { fn declare_local(&mut self, name: Symbol) -> usize {
let local = Var { let local = Var {
name, name,
kind: VarKind::Local(self.local_count) kind: VarKind::Local(self.local_count),
}; };
self.local_count += 1; self.local_count += 1;
let shadowed = self.scope.insert(name, local); let shadowed = self.scope.insert(name, local);
@ -294,7 +313,7 @@ impl<'a> Compiler<'a> {
fn declare_global(&mut self, name: Symbol) { fn declare_global(&mut self, name: Symbol) {
let global = Var { let global = Var {
name, name,
kind: VarKind::Global kind: VarKind::Global,
}; };
let shadowed = self.scope.insert(name, global); let shadowed = self.scope.insert(name, global);
self.shadowed.push((name, shadowed)); self.shadowed.push((name, shadowed));
@ -331,8 +350,6 @@ impl<'a> Compiler<'a> {
} }
} }
// //
// Expressions // Expressions
// //
@ -340,41 +357,43 @@ impl<'a> Compiler<'a> {
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),
} }
@ -577,17 +594,18 @@ impl<'a> Compiler<'a> {
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")
}
} }
} }
@ -605,18 +623,22 @@ impl<'a> Compiler<'a> {
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))
} }
} }
@ -630,7 +701,7 @@ impl<'a> Iterator for LosslessCharsIndices<'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))
} }
} }
@ -646,7 +717,6 @@ impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> {
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Chars<'a>(LosslessChars<'a>); pub struct Chars<'a>(LosslessChars<'a>);
@ -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())
} }
} }
@ -863,17 +943,20 @@ impl LStr {
} }
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,16 +6,35 @@ 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)]
@ -101,7 +120,7 @@ impl LValue {
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())?;
} }
@ -124,7 +143,7 @@ impl fmt::Display for CatchBlock {
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()),
@ -132,7 +151,7 @@ impl LValue {
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)
}, }
} }
} }
} }
@ -145,7 +164,7 @@ impl fmt::Display for LValue {
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}"),
@ -153,12 +172,12 @@ impl Expr {
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:?}")?;
@ -167,15 +186,15 @@ impl Expr {
} }
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())?;
@ -186,12 +205,12 @@ impl Expr {
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)?;
@ -199,7 +218,7 @@ impl Expr {
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)?;
@ -207,26 +226,26 @@ impl Expr {
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 {
@ -234,7 +253,7 @@ impl Expr {
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)
@ -293,4 +312,3 @@ impl fmt::Display for Expr {
self.write_to(f, 0) self.write_to(f, 0)
} }
} }

View file

@ -227,11 +227,15 @@ impl<'s> Lexer<'s> {
fn filter_char(&mut self, c: Option<char>, advance: bool) -> Result<char> { fn filter_char(&mut self, c: Option<char>, advance: bool) -> Result<char> {
match c { match c {
Some(c) if c.is_control() && !matches!(c, '\n' | '\r' | '\t') => Err(self.invalid_char(c)), Some(c) if c.is_control() && !matches!(c, '\n' | '\r' | '\t') => {
Err(self.invalid_char(c))
}
Some(c) => { Some(c) => {
if advance { self.pos = self.pos.advance(c); } if advance {
self.pos = self.pos.advance(c);
}
Ok(c) Ok(c)
}, }
None => Ok('\0'), None => Ok('\0'),
} }
} }
@ -252,7 +256,8 @@ impl<'s> Lexer<'s> {
} }
fn and_peek(&mut self) -> Result<char> { fn and_peek(&mut self) -> Result<char> {
self.next()?; self.peek() self.next()?;
self.peek()
} }
fn emit(&self, kind: TokenKind) -> Result<Token<'s>> { fn emit(&self, kind: TokenKind) -> Result<Token<'s>> {
@ -344,10 +349,13 @@ impl<'s> Lexer<'s> {
self.next()?; self.next()?;
} }
match self.peek()? { match self.peek()? {
'e' => { self.next()?; has_e = true } 'e' => {
self.next()?;
has_e = true;
}
'i' => return self.next_imag(), 'i' => return self.next_imag(),
c if is_xid_start(c) => return self.unexpected(), c if is_xid_start(c) => return self.unexpected(),
_ => return self.emit(K::Float) _ => return self.emit(K::Float),
} }
} }
if matches!(self.peek()?, '+' | '-') { if matches!(self.peek()?, '+' | '-') {
@ -359,21 +367,35 @@ impl<'s> Lexer<'s> {
match self.peek()? { match self.peek()? {
'i' => self.next_imag(), 'i' => self.next_imag(),
c if is_xid_start(c) => self.unexpected(), c if is_xid_start(c) => self.unexpected(),
_ => self.emit(K::Float) _ => self.emit(K::Float),
} }
} }
fn next_number(&mut self) -> Result<Token<'s>> { fn next_number(&mut self) -> Result<Token<'s>> {
if self.next()? == '0' { if self.next()? == '0' {
while self.peek()? == '_' { self.next()?; } while self.peek()? == '_' {
self.next()?;
}
match self.peek()? { match self.peek()? {
'x' => { self.next()?; return self.next_int_base(16) }, 'x' => {
'o' => { self.next()?; return self.next_int_base(8) }, self.next()?;
's' => { self.next()?; return self.next_int_base(6) }, return self.next_int_base(16)
'b' => { self.next()?; return self.next_int_base(2) }, }
'o' => {
self.next()?;
return self.next_int_base(8)
}
's' => {
self.next()?;
return self.next_int_base(6)
}
'b' => {
self.next()?;
return self.next_int_base(2)
}
'0'..='9' | '.' | 'e' | 'i' => (), '0'..='9' | '.' | 'e' | 'i' => (),
c if is_xid_start(c) => return self.unexpected(), c if is_xid_start(c) => return self.unexpected(),
_ => return self.emit(K::Integer) _ => return self.emit(K::Integer),
} }
} }
while matches!(self.peek()?, '_' | '0'..='9') { while matches!(self.peek()?, '_' | '0'..='9') {
@ -381,7 +403,10 @@ impl<'s> Lexer<'s> {
} }
match self.peek()? { match self.peek()? {
'r' => todo!("arbitrary radix integer literals"), 'r' => todo!("arbitrary radix integer literals"),
'e' => { self.next()?; self.next_float(true) }, 'e' => {
self.next()?;
self.next_float(true)
}
'i' => self.next_imag(), 'i' => self.next_imag(),
'.' => { '.' => {
if self.peek_n(1) == Some('.') { if self.peek_n(1) == Some('.') {
@ -390,9 +415,9 @@ impl<'s> Lexer<'s> {
self.next()?; self.next()?;
self.next_float(false) self.next_float(false)
} }
}, }
c if is_xid_start(c) => self.unexpected(), c if is_xid_start(c) => self.unexpected(),
_ => self.emit(K::Integer) _ => self.emit(K::Integer),
} }
} }
@ -403,7 +428,9 @@ impl<'s> Lexer<'s> {
'\0' => return self.unexpected(), '\0' => return self.unexpected(),
'"' if double_quote => break, '"' if double_quote => break,
'\'' if !double_quote => break, '\'' if !double_quote => break,
'\\' if double_quote => { self.next()?; }, '\\' if double_quote => {
self.next()?;
}
_ => (), _ => (),
} }
} }
@ -450,7 +477,7 @@ impl<'s> Lexer<'s> {
self.next_token() self.next_token()
} }
_ => self.emit(K::Backslash), _ => self.emit(K::Backslash),
} },
// arithmetic // arithmetic
'+' => match self.and_peek()? { '+' => match self.and_peek()? {
'+' => match self.and_peek()? { '+' => match self.and_peek()? {
@ -491,15 +518,15 @@ impl<'s> Lexer<'s> {
'&' => match self.and_peek()? { '&' => match self.and_peek()? {
'=' => self.and_emit(K::HashAmperEqual), '=' => self.and_emit(K::HashAmperEqual),
_ => self.emit(K::HashAmper), _ => self.emit(K::HashAmper),
} },
'^' => match self.and_peek()? { '^' => match self.and_peek()? {
'=' => self.and_emit(K::HashCaretEqual), '=' => self.and_emit(K::HashCaretEqual),
_ => self.emit(K::HashCaret), _ => self.emit(K::HashCaret),
} },
'|' => match self.and_peek()? { '|' => match self.and_peek()? {
'=' => self.and_emit(K::HashPipeEqual), '=' => self.and_emit(K::HashPipeEqual),
_ => self.emit(K::HashPipe), _ => self.emit(K::HashPipe),
} },
'!' => self.line_comment(), '!' => self.line_comment(),
_ => self.unexpected(), _ => self.unexpected(),
}, },
@ -545,7 +572,7 @@ impl<'s> Lexer<'s> {
':' => match self.and_peek()? { ':' => match self.and_peek()? {
c if is_xid_start(c) || c == '"' || c == '\'' => self.next_symbol(), c if is_xid_start(c) || c == '"' || c == '\'' => self.next_symbol(),
_ => self.emit(K::Colon), _ => self.emit(K::Colon),
} },
'0'..='9' => self.next_number(), '0'..='9' => self.next_number(),
c if is_xid_start(c) => self.next_ident(), c if is_xid_start(c) => self.next_ident(),
'"' | '\'' => self.next_string(), '"' | '\'' => self.next_string(),

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()),
}

View file

@ -1,12 +1,18 @@
use std::iter::Peekable; use std::iter::Peekable;
use crate::{
symbol::{Symbol, SYM_DOLLAR_SIGN},
value::Value,
};
use crate::{symbol::{Symbol, SYM_DOLLAR_SIGN}, value::Value}; use super::{
ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, UnaryOp},
use super::{ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, UnaryOp}, parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span, SpanParserError, Token, TokenKind}; parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span, SpanParserError,
Token, TokenKind,
};
use num_complex::Complex64; use num_complex::Complex64;
use TokenKind as T;
use ExprKind as E; use ExprKind as E;
use TokenKind as T;
type Result<T> = std::result::Result<T, ParserError>; type Result<T> = std::result::Result<T, ParserError>;
@ -37,7 +43,6 @@ macro_rules! try_next {
match t.kind { match t.kind {
$pat => Some($self.next()?), $pat => Some($self.next()?),
_ => None, _ => None,
} }
}}; }};
} }
@ -135,19 +140,13 @@ impl BinaryOp {
| BinaryOp::Le => (10, 10), | BinaryOp::Le => (10, 10),
BinaryOp::Concat => (20, 25), BinaryOp::Concat => (20, 25),
BinaryOp::Append => (30, 35), BinaryOp::Append => (30, 35),
BinaryOp::Range BinaryOp::Range | BinaryOp::RangeIncl => (40, 40),
| BinaryOp::RangeIncl => (40, 40),
BinaryOp::BitOr => (50, 55), BinaryOp::BitOr => (50, 55),
BinaryOp::BitXor => (60, 65), BinaryOp::BitXor => (60, 65),
BinaryOp::BitAnd => (70, 75), BinaryOp::BitAnd => (70, 75),
BinaryOp::Shl BinaryOp::Shl | BinaryOp::Shr => (80, 85),
| BinaryOp::Shr => (80, 85), BinaryOp::Add | BinaryOp::Sub => (90, 95),
BinaryOp::Add BinaryOp::Mul | BinaryOp::Div | BinaryOp::IntDiv | BinaryOp::Mod => (100, 105),
| BinaryOp::Sub => (90, 95),
BinaryOp::Mul
| BinaryOp::Div
| BinaryOp::IntDiv
| BinaryOp::Mod => (100, 105),
BinaryOp::Pow => (125, 120), BinaryOp::Pow => (125, 120),
} }
} }
@ -160,12 +159,9 @@ fn b<T>(t: T) -> Box<T> {
impl TokenKind { impl TokenKind {
fn expr_first(self) -> bool { fn expr_first(self) -> bool {
matches!(self, matches!(self, |T::Return| T::Var
| T::Return
| T::Var
| T::Global | T::Global
| T::Fn | T::Fn | T::Not
| T::Not
| T::Backslash | T::Backslash
| T::Colon | T::Colon
| T::Minus | T::Minus
@ -174,31 +170,28 @@ impl TokenKind {
| T::LBrack | T::LBrack
| T::LBrace | T::LBrace
| T::Dollar | T::Dollar
| T::Do | T::Do | T::If
| T::If
| T::While | T::While
| T::For | T::For | T::Try
| T::Try
| T::Integer | T::Integer
| T::Float | T::Float
| T::Imaginary | T::Imaginary
| T::String | T::String
| T::Symbol | T::Symbol
| T::True | T::True | T::False
| T::False | T::Nil)
| T::Nil
)
} }
} }
struct Parser<'s> { struct Parser<'s> {
lexer: Peekable<Lexer<'s>>, lexer: Peekable<Lexer<'s>>,
} }
impl<'s> Parser<'s> { impl<'s> Parser<'s> {
fn new(src: &'s str) -> Self { fn new(src: &'s str) -> Self {
Self { lexer: Lexer::new(src).peekable() } Self {
lexer: Lexer::new(src).peekable(),
}
} }
fn next(&mut self) -> Result<Token<'s>> { fn next(&mut self) -> Result<Token<'s>> {
@ -209,7 +202,6 @@ impl<'s> Parser<'s> {
self.lexer.peek().unwrap().clone() self.lexer.peek().unwrap().clone()
} }
fn parse_table_items(&mut self) -> Result<Vec<(Expr, Expr)>> { fn parse_table_items(&mut self) -> Result<Vec<(Expr, Expr)>> {
let mut items = Vec::new(); let mut items = Vec::new();
while self.peek()?.kind.expr_first() { while self.peek()?.kind.expr_first() {
@ -270,11 +262,13 @@ impl<'s> Parser<'s> {
let mut outer_span = self.peek()?.span; let mut outer_span = self.peek()?.span;
loop { loop {
let tok = expect!(self, T::Catch | T::End); let tok = expect!(self, T::Catch | T::End);
if tok.kind == T::End { break } if tok.kind == T::End {
break
}
let types = match try_next!(self, T::Star) { let types = match try_next!(self, T::Star) {
Some(_) => None, Some(_) => None,
None => Some(self.parse_symbol_list()?) None => Some(self.parse_symbol_list()?),
}; };
let name = match try_next!(self, T::In) { let name = match try_next!(self, T::In) {
@ -286,7 +280,12 @@ impl<'s> Parser<'s> {
let body = self.parse_block()?; let body = self.parse_block()?;
let span = tok.span + body.span; let span = tok.span + body.span;
blocks.push(CatchBlock { span, name, types, body }); blocks.push(CatchBlock {
span,
name,
types,
body,
});
outer_span += span; outer_span += span;
} }
Ok((blocks, outer_span)) Ok((blocks, outer_span))
@ -311,9 +310,8 @@ impl<'s> Parser<'s> {
let span = span + elif_body.span; let span = span + elif_body.span;
Ok(E::If(b(cond), b(body), Some(b(elif_body))).span(span)) Ok(E::If(b(cond), b(body), Some(b(elif_body))).span(span))
} }
_ => unreachable!("parse_if_stmt_chain: guaranteed by expect!") _ => unreachable!("parse_if_stmt_chain: guaranteed by expect!"),
} }
} }
fn parse_term_not_ident(&mut self) -> Result<Expr> { fn parse_term_not_ident(&mut self) -> Result<Expr> {
@ -323,23 +321,23 @@ impl<'s> Parser<'s> {
let e = self.parse_expr()?; let e = self.parse_expr()?;
expect!(self, T::RParen); expect!(self, T::RParen);
Ok(e) Ok(e)
}, }
T::LBrack => { T::LBrack => {
let args = self.parse_expr_list()?; let args = self.parse_expr_list()?;
let end = expect!(self, T::RBrack); let end = expect!(self, T::RBrack);
Ok(E::List(args).span(tok.span + end.span)) Ok(E::List(args).span(tok.span + end.span))
}, }
T::LBrace => { T::LBrace => {
let args = self.parse_table_items()?; let args = self.parse_table_items()?;
let end = expect!(self, T::RBrace); let end = expect!(self, T::RBrace);
Ok(E::Table(args).span(tok.span + end.span)) Ok(E::Table(args).span(tok.span + end.span))
}, }
T::Dollar => Ok(E::Ident(*SYM_DOLLAR_SIGN).span(tok.span)), T::Dollar => Ok(E::Ident(*SYM_DOLLAR_SIGN).span(tok.span)),
T::Do => { T::Do => {
let b = self.parse_block()?; let b = self.parse_block()?;
expect!(self, T::End); expect!(self, T::End);
Ok(b) Ok(b)
}, }
T::If => self.parse_if_stmt_chain(), T::If => self.parse_if_stmt_chain(),
T::While => { T::While => {
let cond = self.parse_expr()?; let cond = self.parse_expr()?;
@ -348,7 +346,7 @@ impl<'s> Parser<'s> {
let end = expect!(self, T::End); let end = expect!(self, T::End);
let span = cond.span + end.span; let span = cond.span + end.span;
Ok(E::While(b(cond), b(body)).span(span)) Ok(E::While(b(cond), b(body)).span(span))
}, }
T::For => { T::For => {
let var = expect!(self, T::Identifier); let var = expect!(self, T::Identifier);
expect!(self, T::In); expect!(self, T::In);
@ -358,52 +356,52 @@ impl<'s> Parser<'s> {
let end = expect!(self, T::End); let end = expect!(self, T::End);
let span = var.span + end.span; let span = var.span + end.span;
Ok(E::For(Symbol::get(var.content), b(iter), b(body)).span(span)) Ok(E::For(Symbol::get(var.content), b(iter), b(body)).span(span))
}, }
T::Try => { T::Try => {
let body = self.parse_block()?; let body = self.parse_block()?;
let (catch, span) = self.parse_catch_blocks()?; let (catch, span) = self.parse_catch_blocks()?;
Ok(E::Try(b(body), catch).span(tok.span + span)) Ok(E::Try(b(body), catch).span(tok.span + span))
}, }
T::Integer => { T::Integer => {
let n = parse_int_literal(tok.content) let n = parse_int_literal(tok.content).span_err(tok.span)?;
.span_err(tok.span)?;
Ok(E::Literal(n.into()).span(tok.span)) Ok(E::Literal(n.into()).span(tok.span))
}, }
T::Float => { T::Float => {
let x = parse_float(tok.content) let x = parse_float(tok.content).span_err(tok.span)?;
.span_err(tok.span)?;
Ok(E::Literal(x.into()).span(tok.span)) Ok(E::Literal(x.into()).span(tok.span))
}, }
T::Imaginary => { T::Imaginary => {
let x = parse_float(&tok.content[..tok.content.len()-1]) let x = parse_float(&tok.content[..tok.content.len() - 1]).span_err(tok.span)?;
.span_err(tok.span)?;
Ok(E::Literal(Complex64::new(0.0, x).into()).span(tok.span)) Ok(E::Literal(Complex64::new(0.0, x).into()).span(tok.span))
}, }
T::String => { T::String => {
let inner = &tok.content[1..tok.content.len()-1]; let inner = &tok.content[1..tok.content.len() - 1];
let s = if &tok.content[..1] == "\"" { let s = if &tok.content[..1] == "\"" {
parse_str_escapes(inner).span_err(tok.span)? parse_str_escapes(inner).span_err(tok.span)?
} else { } else {
inner.into() inner.into()
}; };
Ok(E::Literal(s.into()).span(tok.span)) Ok(E::Literal(s.into()).span(tok.span))
}, }
T::Symbol => { T::Symbol => {
let inner = &tok.content[1..]; let inner = &tok.content[1..];
let s = match inner.chars().next() { let s = match inner.chars().next() {
Some('\'') => Symbol::get(&inner[1..inner.len()-1]), Some('\'') => Symbol::get(&inner[1..inner.len() - 1]),
Some('\"') => Symbol::get( Some('\"') => Symbol::get(
&parse_str_escapes(&inner[1..inner.len()-1]) &parse_str_escapes(&inner[1..inner.len() - 1]).span_err(tok.span)?,
.span_err(tok.span)?
), ),
_ => Symbol::get(inner), _ => Symbol::get(inner),
}; };
Ok(E::Literal(s.into()).span(tok.span)) Ok(E::Literal(s.into()).span(tok.span))
}, }
T::True => Ok(E::Literal(Value::Bool(true)).span(tok.span)), T::True => Ok(E::Literal(Value::Bool(true)).span(tok.span)),
T::False => Ok(E::Literal(Value::Bool(false)).span(tok.span)), T::False => Ok(E::Literal(Value::Bool(false)).span(tok.span)),
T::Nil => Ok(E::Literal(Value::Nil).span(tok.span)), T::Nil => Ok(E::Literal(Value::Nil).span(tok.span)),
t => throw!(tok.span, "unexpected token {}, expected expression", t.name()), t => throw!(
tok.span,
"unexpected token {}, expected expression",
t.name()
),
} }
} }
@ -489,8 +487,7 @@ impl<'s> Parser<'s> {
let rhs = self.parse_precedence(rp)?; let rhs = self.parse_precedence(rp)?;
span += rhs.span; span += rhs.span;
lhs = E::BinaryOp(op, Box::new(lhs), Box::new(rhs)) lhs = E::BinaryOp(op, Box::new(lhs), Box::new(rhs)).span(span);
.span(span);
} }
Ok(lhs) Ok(lhs)
@ -499,19 +496,27 @@ impl<'s> Parser<'s> {
fn parse_lambda(&mut self) -> Result<Expr> { fn parse_lambda(&mut self) -> Result<Expr> {
let tok = try_next!(self, T::Backslash | T::Colon); let tok = try_next!(self, T::Backslash | T::Colon);
match tok { match tok {
Some(Token { kind: T::Backslash, span, .. }) => { Some(Token {
kind: T::Backslash,
span,
..
}) => {
let args = self.parse_ident_list()?; let args = self.parse_ident_list()?;
expect!(self, T::Arrow); expect!(self, T::Arrow);
let body = self.parse_lambda()?; let body = self.parse_lambda()?;
let body_span = body.span; let body_span = body.span;
Ok(E::Lambda(args, b(body)).span(span + body_span)) Ok(E::Lambda(args, b(body)).span(span + body_span))
}, }
Some(Token { kind: T::Colon, span, .. }) => { Some(Token {
kind: T::Colon,
span,
..
}) => {
let args = vec![*SYM_DOLLAR_SIGN]; let args = vec![*SYM_DOLLAR_SIGN];
let body = self.parse_lambda()?; let body = self.parse_lambda()?;
let body_span = body.span; let body_span = body.span;
Ok(E::Lambda(args, b(body)).span(span + body_span)) Ok(E::Lambda(args, b(body)).span(span + body_span))
}, }
None => self.parse_precedence(0), None => self.parse_precedence(0),
_ => unreachable!("parse_lambda: guaranteed by try_next!"), _ => unreachable!("parse_lambda: guaranteed by try_next!"),
} }
@ -582,9 +587,12 @@ impl<'s> Parser<'s> {
expect!(self, T::Equal); expect!(self, T::Equal);
let val = self.parse_decl()?; let val = self.parse_decl()?;
let val_span = val.span; let val_span = val.span;
let kind = if first.kind == T::Global { E::AssignGlobal } else { E::AssignVar }; let kind = if first.kind == T::Global {
Ok(kind(Symbol::get(name.content), b(val)) E::AssignGlobal
.span(first.span + val_span)) } else {
E::AssignVar
};
Ok(kind(Symbol::get(name.content), b(val)).span(first.span + val_span))
} }
fn parse_fn_decl(&mut self) -> Result<Expr> { fn parse_fn_decl(&mut self) -> Result<Expr> {
@ -598,16 +606,14 @@ impl<'s> Parser<'s> {
let content = self.parse_block()?; let content = self.parse_block()?;
let end = expect!(self, T::End); let end = expect!(self, T::End);
Ok(E::FnDef(name, args, b(content)).span(tok_fn.span + end.span)) Ok(E::FnDef(name, args, b(content)).span(tok_fn.span + end.span))
}, }
T::Equal => { T::Equal => {
let content = self.parse_expr()?; let content = self.parse_expr()?;
let span = tok_fn.span + content.span; let span = tok_fn.span + content.span;
Ok(E::FnDef(name, args, b(content)).span(span)) Ok(E::FnDef(name, args, b(content)).span(span))
}, }
_ => unreachable!("parse_fn_decl: guaranteed by try_next!"), _ => unreachable!("parse_fn_decl: guaranteed by try_next!"),
} }
} }
fn parse_decl(&mut self) -> Result<Expr> { fn parse_decl(&mut self) -> Result<Expr> {

View file

@ -21,12 +21,18 @@ impl std::cmp::Ord for Pos {
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
.idx
.checked_add(c.len_utf8() as u32)
.expect("source file contains more than u32::MAX chars"); .expect("source file contains more than u32::MAX chars");
if c == '\n' { if c == '\n' {
Pos { Pos {
@ -59,7 +65,10 @@ pub struct Span {
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 {
start: end,
end: start,
}
} else { } else {
Self { start, end } Self { start, 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;
@ -27,7 +27,10 @@ pub trait SpanParserError {
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(),
})
} }
} }
@ -56,7 +59,10 @@ 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),
@ -75,7 +81,7 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
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)),
} }
} }
@ -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,8 +1,8 @@
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 {
@ -25,7 +25,11 @@ impl Function {
} }
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,66 +16,88 @@ impl Value {
if i >= 0 && (i as usize) < l.len() { if i >= 0 && (i as usize) < l.len() {
Ok(l[i as usize].clone()) Ok(l[i as usize].clone())
} else { } else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len()) throw!(
*SYM_INDEX_ERROR,
"index {i} out of bounds for list of length {}",
l.len()
)
}
} }
},
(V::Range(r), V::Int(i)) => { (V::Range(r), V::Int(i)) => {
if i >= 0 && ( if i >= 0
r.ty == RangeType::Endless && (r.ty == RangeType::Endless
|| i < r.stop || i < r.stop || (r.ty == RangeType::Closed && i == r.stop))
|| (r.ty == RangeType::Closed && i == r.stop) {
) {
Ok((r.start + i).into()) Ok((r.start + i).into())
} else { } else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}") throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
} }
}, }
(V::Table(t), i) if i.hashable() => { (V::Table(t), i) if i.hashable() => {
let t = t.borrow(); let t = t.borrow();
let i = i.try_into()?; let i = i.try_into()?;
Ok(t.get(&i).cloned().unwrap_or(Value::Nil)) Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
}, }
(V::String(s), V::Range(r)) => { (V::String(s), V::Range(r)) => {
let slen = s.len(); let slen = s.len();
match r.ty { match r.ty {
RangeType::Open => { RangeType::Open => {
if r.start < 0 || r.start > slen as i64 { if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for string of length {}", r.stop, slen) *SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
} }
if r.stop < 0 || r.stop > slen as i64 { if r.stop < 0 || r.stop > slen as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for string of length {}", r.stop, slen) *SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
} }
Ok(s[r.start as usize..r.stop as usize].into()) Ok(s[r.start as usize..r.stop as usize].into())
}, }
RangeType::Closed => { RangeType::Closed => {
if r.start < 0 || r.start > slen as i64 { if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for string of length {}", r.stop, slen) *SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
} }
if r.stop < 0 || r.stop >= slen as i64 { if r.stop < 0 || r.stop >= slen as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for string of length {}", r.stop, slen) *SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
} }
Ok(s[r.start as usize..=r.stop as usize].into()) Ok(s[r.start as usize..=r.stop as usize].into())
}, }
RangeType::Endless => { RangeType::Endless => {
if r.start < 0 || r.start > slen as i64 { if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for string of length {}", r.stop, slen) *SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
} }
Ok(s[r.start as usize..].into()) Ok(s[r.start as usize..].into())
},
} }
}, }
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() { }
(col, idx) => {
if let Ok(ii) = idx.clone().to_iter_function() {
let col = col.clone(); let col = col.clone();
let func = move |vm: &mut Vm, _| { let func = move |vm: &mut Vm, _| match vmcalliter!(vm; ii.clone())? {
match vmcalliter!(vm; ii.clone())? {
Some(i) => col.index(i), Some(i) => col.index(i),
None => Ok(Value::from(*SYM_END_ITERATION)), None => Ok(Value::from(*SYM_END_ITERATION)),
}
}; };
Ok(NativeFunc::new(Box::new(func), 0, symbol!("index...")).into()) Ok(NativeFunc::new(Box::new(func), 0, symbol!("index...")).into())
} else { } else {
@ -78,6 +105,7 @@ impl Value {
} }
} }
} }
}
pub fn store_index(&self, vm: &mut Vm, idx: Self, val: Self) -> Result<()> { pub fn store_index(&self, vm: &mut Vm, idx: Self, val: Self) -> Result<()> {
use Value as V; use Value as V;
@ -88,15 +116,19 @@ impl Value {
l[i as usize] = val; l[i as usize] = val;
Ok(()) Ok(())
} else { } else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len()) throw!(
*SYM_INDEX_ERROR,
"index {i} out of bounds for list of length {}",
l.len()
)
}
} }
},
(V::Table(t), i) if i.hashable() => { (V::Table(t), i) if i.hashable() => {
let mut t = t.borrow_mut(); let mut t = t.borrow_mut();
let i = i.try_into()?; let i = i.try_into()?;
t.insert(i, val); t.insert(i, val);
Ok(()) Ok(())
}, }
(V::List(t), V::Range(r)) => { (V::List(t), V::Range(r)) => {
let iter = val.to_iter_function()?; let iter = val.to_iter_function()?;
let mut vals = Vec::new(); let mut vals = Vec::new();
@ -107,53 +139,78 @@ impl Value {
match r.ty { match r.ty {
RangeType::Open => { RangeType::Open => {
if r.start < 0 || r.start > tm.len() as i64 { if r.start < 0 || r.start > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for list of length {}", r.stop, tm.len()) *SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
} }
if r.stop < 0 || r.stop > tm.len() as i64 { if r.stop < 0 || r.stop > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for list of length {}", r.stop, tm.len()) *SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
} }
let end = tm.split_off(r.stop as usize); let end = tm.split_off(r.stop as usize);
tm.truncate(r.start as usize); tm.truncate(r.start as usize);
tm.extend(vals.into_iter().chain(end)); tm.extend(vals.into_iter().chain(end));
}, }
RangeType::Closed => { RangeType::Closed => {
if r.start < 0 || r.start > tm.len() as i64 { if r.start < 0 || r.start > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for list of length {}", r.stop, tm.len()) *SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
} }
if r.stop < 0 || r.stop >= tm.len() as i64 { if r.stop < 0 || r.stop >= tm.len() as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for list of length {}", r.stop, tm.len()) *SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
} }
let end = tm.split_off(r.stop as usize + 1); let end = tm.split_off(r.stop as usize + 1);
tm.truncate(r.start as usize); tm.truncate(r.start as usize);
tm.extend(vals.into_iter().chain(end)); tm.extend(vals.into_iter().chain(end));
}, }
RangeType::Endless => { RangeType::Endless => {
if r.start < 0 || r.start > tm.len() as i64 { if r.start < 0 || r.start > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR, throw!(
"index {} out of bounds for list of length {}", r.stop, tm.len()) *SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
} }
tm.truncate(r.start as usize); tm.truncate(r.start as usize);
tm.extend(vals); tm.extend(vals);
}, }
} }
Ok(()) Ok(())
}, }
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() { (col, idx) => {
if let Ok(ii) = idx.clone().to_iter_function() {
let val = val.to_iter_function()?; let val = val.to_iter_function()?;
while let Some(i) = vmcalliter!(vm; ii.clone())? { while let Some(i) = vmcalliter!(vm; ii.clone())? {
let Some(v) = vmcalliter!(vm; val.clone())? else { let Some(v) = vmcalliter!(vm; val.clone())? else {
throw!(*SYM_INDEX_ERROR, "not enough values provided for store index") throw!(
*SYM_INDEX_ERROR,
"not enough values provided for store index"
)
}; };
col.store_index(vm, i, v)?; col.store_index(vm, i, v)?;
} }
Ok(()) Ok(())
} else { } else {
throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}") throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}")
}, }
}
} }
} }
} }

View file

@ -7,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,7 +112,7 @@ impl Value {
write!(w, "{name:?}")?; write!(w, "{name:?}")?;
} }
Ok(()) Ok(())
}, }
Self::Range(r) => match r.ty { Self::Range(r) => match r.ty {
RangeType::Open => write!(w, "{}..{}", r.start, r.stop), RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop), RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
@ -110,7 +127,7 @@ 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(...)")
@ -120,7 +137,7 @@ impl Value {
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(...)")
@ -129,7 +146,7 @@ impl Value {
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()),
@ -147,7 +164,7 @@ impl Value {
} }
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"{...}")
@ -164,34 +181,56 @@ impl Value {
} }
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,
"<function {}({}) @{:0>12x}>",
name.name(),
g.attrs.arity,
Rc::as_ptr(g) as usize
)
} else { } else {
write!(w, "<anon function({}) @{:0>12x}>", write!(
g.attrs.arity, Rc::as_ptr(g) as usize) 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,
"<native function {}({}) @{:0>12x}>",
name.name(),
g.attrs.arity,
Rc::as_ptr(g) as usize
)
} else { } else {
write!(w, "<anon native function({}) @{:0>12x}>", write!(
g.attrs.arity, Rc::as_ptr(g) as usize) 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)?;
@ -216,7 +255,8 @@ impl Value {
} 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)
} }
} }
@ -224,7 +264,8 @@ impl Value {
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,8 +47,10 @@ impl Value {
Value::List(l) => l.borrow().len() > 0, Value::List(l) => l.borrow().len() > 0,
Value::Cell(v) => v.borrow().truthy(), Value::Cell(v) => v.borrow().truthy(),
Value::Symbol(_) | Value::Table(_) Value::Symbol(_)
| Value::Function(_) | Value::NativeFunc(_) | Value::Table(_)
| Value::Function(_)
| Value::NativeFunc(_)
| Value::Native(_) => true, | Value::Native(_) => true,
} }
} }
@ -59,30 +75,32 @@ pub fn promote(a: Value, b: Value) -> (Value, Value) {
} }
} }
//////////////////////// ////////////////////////
// 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) => {
if let Some(x) = x.checked_neg() {
Ok(V::Int(x)) Ok(V::Int(x))
} else { } else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}") throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
}, }
V::Ratio(x) => if let Some(x) = Rational64::ZERO.checked_sub(&x) { }
V::Ratio(x) => {
if let Some(x) = Rational64::ZERO.checked_sub(&x) {
Ok(V::Ratio(x)) Ok(V::Ratio(x))
} else { } else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}") 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,29 +109,37 @@ 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) => {
if let Some(x) = x.checked_abs() {
Ok(V::Int(x)) Ok(V::Int(x))
} else { } else {
throw!(*SYM_VALUE_ERROR, "overflow when finding absolute value of {self}") throw!(
}, *SYM_VALUE_ERROR,
V::Ratio(x) => if let Some((x, _)) = ratio_checked_absign(&x) { "overflow when finding absolute value of {self}"
)
}
}
V::Ratio(x) => {
if let Some((x, _)) = ratio_checked_absign(&x) {
Ok(V::Ratio(x)) Ok(V::Ratio(x))
} else { } else {
throw!(*SYM_VALUE_ERROR, "overflow when finding absolute value of {self}") 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) => {
@ -154,17 +180,20 @@ impl Div<Value> for Value {
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) { }
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = x.checked_div(y) {
Ok(V::Ratio(v)) Ok(V::Ratio(v))
} else { } else {
throw!(*SYM_VALUE_ERROR, "overflow when dividing {a} and {b}") 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,7 +202,6 @@ 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() {
@ -194,29 +222,32 @@ fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational
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)) => {
if let Some(v) = x.checked_rem_euclid(*y) {
Ok(V::Int(v)) Ok(V::Int(v))
} else { } else {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}") 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) { }
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = ratio_checked_rem_euclid(x, y) {
Ok(V::Ratio(v)) Ok(V::Ratio(v))
} else { } else {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}") 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)) => {
@ -224,7 +255,7 @@ impl Value {
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:#}"),
} }
} }
@ -233,36 +264,45 @@ impl Value {
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)) => {
if let Some(v) = x.checked_div_euclid(*y) {
Ok(V::Int(v)) Ok(V::Int(v))
} else { } else {
throw!(*SYM_VALUE_ERROR, "overflow when integer dividing {a} and {b}") 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) { }
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = ratio_checked_div_euclid(x, y) {
Ok(V::Ratio(v)) Ok(V::Ratio(v))
} else { } else {
throw!(*SYM_VALUE_ERROR, "overflow when integer dividing {a} and {b}") 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) {
@ -284,7 +324,7 @@ fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
(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)?)),
} }
@ -298,41 +338,50 @@ impl Value {
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) { }
(V::Int(x), V::Int(y @ 0..)) => {
if let Some(v) = ipow(*x, *y as u64) {
Ok(V::Int(v)) Ok(V::Int(v))
} else { } else {
throw!(*SYM_VALUE_ERROR, "overflow when raising {a} to the power {b}") throw!(
}, *SYM_VALUE_ERROR,
(V::Int(x), V::Int(y)) => if let Some(v) = rpow(*x, 1, *y) { "overflow when raising {a} to the power {b}"
)
}
}
(V::Int(x), V::Int(y)) => {
if let Some(v) = rpow(*x, 1, *y) {
Ok(V::Ratio(v.into())) Ok(V::Ratio(v.into()))
} else { } else {
throw!(*SYM_VALUE_ERROR, "overflow when raising {a} to the power {b}") throw!(
}, *SYM_VALUE_ERROR,
(V::Ratio(x), V::Ratio(y)) "overflow when raising {a} to the power {b}"
=> 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)) (V::Ratio(x), V::Ratio(y)) => Ok(V::Float(x.to_f64().powf(y.to_f64()))),
=> Ok(V::Complex(x.powc(*y))), (V::Float(x), V::Float(y)) => Ok(V::Float(x.powf(*y))),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}") (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,21 +437,19 @@ impl BitOr<Value> for Value {
use Value as V; use Value as V;
match (self, rhs) { match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int(a | b)), (V::Int(a), V::Int(b)) => Ok(Value::Int(a | b)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}") (l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
} }
} }
} }
///////////////////////////// /////////////////////////////
// Equality and ordering // // 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,
@ -427,8 +474,9 @@ impl PartialEq for Value {
(V::Symbol(a), V::Symbol(b)) => a == b, (V::Symbol(a), V::Symbol(b)) => a == b,
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(), (V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) { (V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
(Rty::Open, Rty::Open) (Rty::Open, Rty::Open) | (Rty::Closed, Rty::Closed) => {
| (Rty::Closed, Rty::Closed) => a.start == b.start && a.stop == b.stop, a.start == b.start && a.stop == b.stop
}
(Rty::Open, Rty::Closed) => a.start == b.start && a.stop == b.stop - 1, (Rty::Open, Rty::Closed) => a.start == b.start && a.stop == b.stop - 1,
(Rty::Closed, Rty::Open) => a.start == b.start && a.stop - 1 == b.stop, (Rty::Closed, Rty::Open) => a.start == b.start && a.stop - 1 == b.stop,
(Rty::Endless, Rty::Endless) => a.start == b.start, (Rty::Endless, Rty::Endless) => a.start == b.start,
@ -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 { } else {
throw!(*SYM_TYPE_ERROR, "cannot create range between {self:#} and {other:#}") RangeType::Open
};
Ok(Range {
start: *start,
stop: *stop,
ty,
}
.into())
} else {
throw!(
*SYM_TYPE_ERROR,
"cannot create range between {self:#} and {other:#}"
)
} }
} }
pub fn range_endless(&self) -> Result<Self> { pub fn range_endless(&self) -> Result<Self> {
if let Value::Int(start) = self { if let Value::Int(start) = self {
Ok(Range { start: *start, stop: 0, ty: RangeType::Endless }.into()) Ok(Range {
start: *start,
stop: 0,
ty: RangeType::Endless,
}
.into())
} else { } else {
throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}") throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}")
} }
@ -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>,
@ -91,7 +109,10 @@ fn get_call_outcome(args: Vec<Value>) -> Result<CallOutcome> {
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 {
arity: remaining,
name: None,
},
func: Box::new(nf), func: Box::new(nf),
}; };
Ok(CallOutcome::Partial(nf.into())) Ok(CallOutcome::Partial(nf.into()))
@ -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"),
} },
} }
} }
@ -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,20 +263,20 @@ 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 {
@ -262,44 +284,46 @@ impl Vm {
}; };
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
.pop_n(f.state.len())
.into_iter()
.map(|v| { .map(|v| {
let Value::Cell(v) = v else { let Value::Cell(v) = v else {
panic!("attempt to build closure from non-cell local"); panic!("attempt to build closure from non-cell local");
}; };
v v
}).collect(); })
.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) => {
if self.pop().truthy() {
self.check_interrupt()?; self.check_interrupt()?;
frame.ip = usize::from(n); frame.ip = usize::from(n);
}, }
}
// [v] ->, [], if not v then ip = n // [v] ->, [], if not v then ip = n
I::JumpFalse(n) => if !self.pop().truthy() { I::JumpFalse(n) => {
if !self.pop().truthy() {
self.check_interrupt()?; self.check_interrupt()?;
frame.ip = usize::from(n); frame.ip = usize::from(n);
}, }
}
// [v] -> [iter(v)] // [v] -> [iter(v)]
I::IterBegin => { I::IterBegin => {
let iter = self.pop().to_iter_function()?; let iter = self.pop().to_iter_function()?;
self.push(iter); self.push(iter);
}, }
// [i,cell(v)] -> [i,v] // [i,cell(v)] -> [i,v]
// [i,nil] -> [], ip = n // [i,nil] -> [], ip = n
I::IterTest(n) => { I::IterTest(n) => {
@ -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

@ -1,6 +1,6 @@
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,
@ -13,7 +13,10 @@ impl Parse for NativeFuncArgs {
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 {
arity,
name: Some(name),
})
} else { } else {
Ok(Self { arity, name: None }) Ok(Self { arity, name: None })
} }
@ -41,7 +44,10 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
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! {

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

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;
@ -9,17 +14,18 @@ pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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]
=> Exception::new_with_msg_data(*ty, s.clone(), data.clone()),
[] | [_] | [_,_] | [_,_,_] => throw!(*SYM_TYPE_ERROR, "wrong argument for throw"),
[_,_,_,_,..] => throw!(*SYM_VALUE_ERROR, "too many elements in list argument for throw"),
} }
[] | [_] | [_, _] | [_, _, _] => throw!(*SYM_TYPE_ERROR, "wrong argument for throw"),
[_, _, _, _, ..] => throw!(
*SYM_VALUE_ERROR,
"too many elements in list argument for throw"
),
},
_ => throw!(*SYM_TYPE_ERROR, "throw expected symbol or list"), _ => throw!(*SYM_TYPE_ERROR, "throw expected symbol or list"),
}; };
Err(exc) Err(exc)

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,11 +253,12 @@ pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} else { } else {
match OPEN_OPT_MAP.get(&s) { match OPEN_OPT_MAP.get(&s) {
Some(f) => f(&mut oo, true), Some(f) => f(&mut oo, true),
None => throw!(*SYM_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) => {
for s in l.borrow().iter() {
let Value::Symbol(s) = s else { let Value::Symbol(s) = s else {
throw!(*SYM_TYPE_ERROR, "invalid option for open") throw!(*SYM_TYPE_ERROR, "invalid option for open")
}; };
@ -231,20 +267,21 @@ pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} else { } else {
match OPEN_OPT_MAP.get(s) { match OPEN_OPT_MAP.get(s) {
Some(f) => f(&mut oo, true), Some(f) => f(&mut oo, true),
None => throw!(*SYM_VALUE_ERROR, "invalid option for open") None => throw!(*SYM_VALUE_ERROR, "invalid option for open"),
}; };
} }
}, }
}
Value::Nil => { Value::Nil => {
oo.read(true).write(true); oo.read(true).write(true);
}, }
_ => throw!(*SYM_TYPE_ERROR, "invalid option for open") _ => throw!(*SYM_TYPE_ERROR, "invalid option for open"),
} }
match oo.open(path.to_os_str()) { match oo.open(path.to_os_str()) {
Ok(f) => match BufFile::new(f, buffered) { Ok(f) => match BufFile::new(f, buffered) {
Ok(bf) => Ok(ValueFile::from(bf).into()), Ok(bf) => Ok(ValueFile::from(bf).into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"), Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} },
Err(e) => throw!(*SYM_IO_ERROR, "{e}"), Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
} }
} }
@ -256,7 +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) => {
for e in l.borrow().iter() {
let Value::String(e) = e else { let Value::String(e) = e else {
throw!(*SYM_TYPE_ERROR, throw!(
"{fname} delenv option expected :all or list of strings, got {delenv:#}") *SYM_TYPE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}"
)
}; };
proc.env_remove(e.to_os_str()); proc.env_remove(e.to_os_str());
}, }
_ => throw!(*SYM_VALUE_ERROR, }
"{fname} delenv option expected :all or list of strings, got {delenv:#}") _ => throw!(
*SYM_VALUE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}"
),
} }
Ok(()) Ok(())
} }
@ -544,15 +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) => {
for (k, v) in t.borrow().iter() {
let Value::String(k) = k.inner() else { let Value::String(k) = k.inner() else {
throw!(*SYM_TYPE_ERROR, throw!(
"{fname} env option expected table with string keys, got {env:#}") *SYM_TYPE_ERROR,
"{fname} env option expected table with string keys, got {env:#}"
)
}; };
proc.env(k.to_os_str(), v.str().to_os_str()); proc.env(k.to_os_str(), v.str().to_os_str());
}, }
_ => throw!(*SYM_TYPE_ERROR, }
"{fname} env option expected table with string keys, got {env:#}") _ => throw!(
*SYM_TYPE_ERROR,
"{fname} env option expected table with string keys, got {env:#}"
),
} }
Ok(()) Ok(())
} }
@ -560,7 +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,6 +1,14 @@
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};
@ -36,7 +44,7 @@ enum FmtSign {
Plus, Plus,
Minus, Minus,
#[default] #[default]
None None,
} }
// [idx][:[align][sign][width][.prec][ty]] // [idx][:[align][sign][width][.prec][ty]]
@ -57,10 +65,7 @@ struct FmtParser<'p> {
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> {
@ -91,14 +96,18 @@ impl<'p> FmtParser<'p> {
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() {
return Ok(None)
}
let Ok(i) = parse_int(s, 10) else { let Ok(i) = parse_int(s, 10) else {
throw!(*SYM_FORMAT_ERROR, throw!(*SYM_FORMAT_ERROR, "code {{{}}}: invalid integer", self.code)
"code {{{}}}: invalid integer", self.code)
}; };
if i < 0 { if i < 0 {
throw!(*SYM_FORMAT_ERROR, throw!(
"code {{{}}}: integer may not be negative", self.code) *SYM_FORMAT_ERROR,
"code {{{}}}: integer may not be negative",
self.code
)
} }
Ok(Some(i as usize)) Ok(Some(i as usize))
} }
@ -123,8 +132,11 @@ impl<'p> FmtParser<'p> {
}; };
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",
self.code
)
}; };
Ok(Some((align, c))) Ok(Some((align, c)))
} }
@ -148,8 +160,11 @@ impl<'p> FmtParser<'p> {
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.code
),
}; };
self.next(); self.next();
Ok(ty) Ok(ty)
@ -157,9 +172,13 @@ impl<'p> FmtParser<'p> {
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(()),
} }
} }
@ -184,8 +203,7 @@ impl<'p> FmtParser<'p> {
} }
} }
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 => {
@ -195,7 +213,11 @@ fn get_arg<'a>(args: &'a [Value], n: Option<usize>, faidx: &mut usize)
} }
}; };
if i >= args.len() { if i >= args.len() {
throw!(*SYM_FORMAT_ERROR, "missing arguments: expected at least {}", i+1) throw!(
*SYM_FORMAT_ERROR,
"missing arguments: expected at least {}",
i + 1
)
} }
Ok(&args[i]) Ok(&args[i])
} }
@ -207,30 +229,46 @@ fn get_val(args: &[Value], i: FmtIndex, faidx: &mut usize) -> Result<usize> {
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,
"expected positive integer argument, found {v:#}"
)
}; };
if *v < 0 { if *v < 0 {
throw!(*SYM_FORMAT_ERROR, "expected positive integer argument, found {v}") throw!(
*SYM_FORMAT_ERROR,
"expected positive integer argument, found {v}"
)
} }
Ok(*v as usize) Ok(*v as usize)
} }
fn format_float(f: f64, prec: Option<usize>, ty: FmtType, buf: &mut LString, ty_name: &str) -> Result<()> { fn format_float(
f: f64,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
ty_name: &str,
) -> Result<()> {
let res = match (prec, ty) { let res = match (prec, ty) {
(None, FmtType::Str) => write!(buf, "{}", Value::Float(f)), (None, FmtType::Str) => write!(buf, "{}", Value::Float(f)),
(None, FmtType::Repr) => write!(buf, "{:#}", Value::Float(f)), (None, FmtType::Repr) => write!(buf, "{:#}", Value::Float(f)),
(None, FmtType::Exp(true)) => write!(buf, "{:E}", f), (None, FmtType::Exp(true)) => write!(buf, "{:E}", f),
(None, FmtType::Exp(false)) => write!(buf, "{:e}", f), (None, FmtType::Exp(false)) => write!(buf, "{:e}", f),
(Some(p), FmtType::Str) (Some(p), FmtType::Str) | (Some(p), FmtType::Repr) => write!(buf, "{0:.1$}", f, p),
| (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(true)) => write!(buf, "{0:.1$E}", f, p),
(Some(p), FmtType::Exp(false)) => 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}") _ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
}; };
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}")) 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(
cx: Complex64,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
) -> Result<()> {
format_float(cx.re, prec, ty, buf, "complex")?; format_float(cx.re, prec, ty, buf, "complex")?;
if cx.im < 0.0 { if cx.im < 0.0 {
buf.push_char('-') buf.push_char('-')
@ -241,7 +279,13 @@ fn format_complex(cx: Complex64, prec: Option<usize>, ty: FmtType, buf: &mut LSt
Ok(()) Ok(())
} }
fn format_int(n: i64, prec: Option<usize>, ty: FmtType, buf: &mut LString, ty_name: &str) -> Result<()> { fn format_int(
n: i64,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
ty_name: &str,
) -> Result<()> {
if prec.is_some() { if prec.is_some() {
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}") throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
} }
@ -252,7 +296,7 @@ fn format_int(n: i64, prec: Option<usize>, ty: FmtType, buf: &mut LString, ty_na
FmtType::Oct => write!(buf, "{}", to_lstring_radix(n, 8, false)), FmtType::Oct => write!(buf, "{}", to_lstring_radix(n, 8, false)),
FmtType::Sex => write!(buf, "{}", to_lstring_radix(n, 6, false)), FmtType::Sex => write!(buf, "{}", to_lstring_radix(n, 6, false)),
FmtType::Bin => write!(buf, "{}", to_lstring_radix(n, 2, false)), FmtType::Bin => write!(buf, "{}", to_lstring_radix(n, 2, false)),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}") _ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
}; };
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}")) res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
} }
@ -271,7 +315,7 @@ fn format_value(v: &Value, prec: Option<usize>, ty: FmtType, buf: &mut LString)
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}"))
} }
@ -281,9 +325,12 @@ fn format_string(mut s: &LStr, prec: Option<usize>, ty: FmtType, buf: &mut LStri
s = &s[..prec] s = &s[..prec]
} }
let res = match ty { let res = match ty {
FmtType::Str => { buf.push_lstr(s); Ok(()) }, FmtType::Str => {
buf.push_lstr(s);
Ok(())
}
FmtType::Repr => write!(buf, "{:?}", s), FmtType::Repr => write!(buf, "{:?}", s),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for string") _ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for string"),
}; };
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}")) res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
} }
@ -294,10 +341,13 @@ fn pad_str(n: usize, c: char, buf: &mut LString) {
} }
} }
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!(*SYM_FORMAT_ERROR, "code {{{}}}: code contains invalid UTF-8", code) throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: code contains invalid UTF-8",
code
)
} }
let fmtcode = FmtParser::new(code).read_format()?; let fmtcode = FmtParser::new(code).read_format()?;
@ -320,16 +370,15 @@ fn format_arg(args: &[Value], faidx: &mut usize, code: &LStr, res: &mut LString)
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 { if let Some(w) = width_val {
let w = w.saturating_sub(final_buf.len() + sign.len()); let w = w.saturating_sub(final_buf.len() + sign.len());
match fmtcode.align { match fmtcode.align {
@ -338,21 +387,20 @@ fn format_arg(args: &[Value], faidx: &mut usize, code: &LStr, res: &mut LString)
pad_str(w, c, res); pad_str(w, c, res);
} }
Some((Align::Center, c)) => { Some((Align::Center, c)) => {
pad_str((w+1)/2, c, res); pad_str((w + 1) / 2, c, res);
res.push_lstr(final_buf); res.push_lstr(final_buf);
pad_str(w/2, c, res); pad_str(w / 2, c, res);
} }
Some((Align::Right, c)) => { Some((Align::Right, c)) => {
pad_str(w, c, res); pad_str(w, c, res);
res.push_lstr(final_buf); res.push_lstr(final_buf);
} }
None => { None => {
let c = if matches!(fmt_arg, let c = if matches!(
Value::Int(_) fmt_arg,
| Value::Float(_) Value::Int(_) | Value::Float(_) | Value::Ratio(_) | Value::Complex(_)
| Value::Ratio(_) ) && fmtcode.sign != FmtSign::None
| Value::Complex(_) {
) && fmtcode.sign != FmtSign::None {
'0' '0'
} else { } else {
' ' ' '
@ -372,7 +420,10 @@ fn format_arg(args: &[Value], faidx: &mut usize, code: &LStr, res: &mut LString)
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;
@ -382,7 +433,11 @@ pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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,
"unmatched closing brace at byte {}",
i - 1
)
}; };
i += 1; i += 1;
res.push_byte(b); res.push_byte(b);
@ -392,7 +447,11 @@ pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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,
"unclosed format specifier at byte {}",
i - 1
)
} }
if let Some(b'{') = fstr.byte_get(i) { if let Some(b'{') = fstr.byte_get(i) {
i += 1; i += 1;
@ -404,7 +463,11 @@ pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
i += 1; i += 1;
} }
if i == fstr.len() { if i == fstr.len() {
throw!(*SYM_FORMAT_ERROR, "unclosed format specifier at byte {}", start-1) throw!(
*SYM_FORMAT_ERROR,
"unclosed format specifier at byte {}",
start - 1
)
} }
let code = &fstr[start..i]; let code = &fstr[start..i];
i += 1; i += 1;
@ -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,11 +110,17 @@ 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 { let Ok(guard) = ENV_LOCK.lock() else {
@ -111,7 +139,10 @@ 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 { let Ok(guard) = ENV_LOCK.lock() else {
@ -123,7 +154,6 @@ pub fn delenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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,8 +167,7 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, filter, iter] = unpack_args!(args); let [_, filter, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| loop {
loop {
let Some(next) = vmcalliter!(vm; iter.clone())? else { let Some(next) = vmcalliter!(vm; iter.clone())? else {
return Ok(Value::iter_pack(None)) return Ok(Value::iter_pack(None))
}; };
@ -180,7 +175,6 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
if res.truthy() { if res.truthy() {
return Ok(next) return Ok(next)
} }
}
}; };
Ok(NativeFunc::new(Box::new(f), 0, 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,17 +352,14 @@ 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);
@ -386,7 +388,9 @@ pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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
.borrow()
.iter()
.map(|i| i.clone().to_iter_function()) .map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
@ -414,7 +418,7 @@ pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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;
@ -437,7 +441,9 @@ pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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
.borrow()
.iter()
.map(|i| i.clone().to_iter_function()) .map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
@ -445,7 +451,7 @@ pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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,8 +482,7 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter = iter.to_iter_function()?; let iter = iter.to_iter_function()?;
let state = RefCell::new(Intersperse::Init); let state = RefCell::new(Intersperse::Init);
let f = move |vm: &mut Vm, _| { let f = move |vm: &mut Vm, _| match state.take() {
match state.take() {
Intersperse::Init => { Intersperse::Init => {
if let Some(v) = vmcalliter!(vm; iter.clone())? { if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::Waiting; *state.borrow_mut() = Intersperse::Waiting;
@ -482,7 +491,7 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*state.borrow_mut() = Intersperse::End; *state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None)) Ok(Value::iter_pack(None))
} }
}, }
Intersperse::Waiting => { Intersperse::Waiting => {
if let Some(v) = vmcalliter!(vm; iter.clone())? { if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::HasNext(v); *state.borrow_mut() = Intersperse::HasNext(v);
@ -491,19 +500,17 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*state.borrow_mut() = Intersperse::End; *state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None)) Ok(Value::iter_pack(None))
} }
}, }
Intersperse::HasNext(v) => { Intersperse::HasNext(v) => {
*state.borrow_mut() = Intersperse::Waiting; *state.borrow_mut() = Intersperse::Waiting;
Ok(v) Ok(v)
},
Intersperse::End => Ok(Value::iter_pack(None)),
} }
Intersperse::End => Ok(Value::iter_pack(None)),
}; };
Ok(NativeFunc::new(Box::new(f), 0, 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,11 +775,13 @@ pub fn contains(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for _ in 0.. { for _ in 0.. {
match vmcalliter!(vm; iter.clone())? { match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil), None => return Ok(Value::Nil),
Some(v) => if v == val { Some(v) => {
if v == val {
return Ok(true.into()) return Ok(true.into())
} }
} }
} }
}
Ok(false.into()) Ok(false.into())
} }
@ -780,11 +793,13 @@ pub fn index_of(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for i in 0.. { for i in 0.. {
match vmcalliter!(vm; iter.clone())? { match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil), None => return Ok(Value::Nil),
Some(v) => if v == val { Some(v) => {
if v == val {
return Ok(i.into()) return Ok(i.into())
} }
} }
} }
}
Ok(Value::Nil) Ok(Value::Nil)
} }
@ -796,11 +811,13 @@ pub fn index_if(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for i in 0.. { for i in 0.. {
match vmcalliter!(vm; iter.clone())? { match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil), None => return Ok(Value::Nil),
Some(v) => if vmcall!(vm; func.clone(), v)?.truthy() { Some(v) => {
if vmcall!(vm; func.clone(), v)?.truthy() {
return Ok(i.into()) return Ok(i.into())
} }
} }
} }
}
Ok(Value::Nil) Ok(Value::Nil)
} }
@ -858,8 +875,8 @@ fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> {
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 })
@ -884,7 +901,7 @@ pub fn stdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
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:#}"),
}) })
} }
@ -907,7 +924,7 @@ pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
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:#}"),
}) })
} }
@ -935,15 +952,14 @@ pub fn median(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
if count == 0 { if count == 0 {
Ok(Value::Nil) Ok(Value::Nil)
} else if count % 2 == 0 { } else if count % 2 == 0 {
let (_, _, hi) = vals.select_nth_unstable(count/2 - 1); let (_, _, hi) = vals.select_nth_unstable(count / 2 - 1);
let (_, _, _) = hi.select_nth_unstable(0); let (_, _, _) = hi.select_nth_unstable(0);
let m2 = vals.swap_remove(count/2); let m2 = vals.swap_remove(count / 2);
let m1 = vals.swap_remove(count/2 - 1); let m1 = vals.swap_remove(count / 2 - 1);
(m1.0 + m2.0)? / Value::Int(2) (m1.0 + m2.0)? / Value::Int(2)
} else { } else {
let (_, _, _) = vals.select_nth_unstable(count/2); let (_, _, _) = vals.select_nth_unstable(count / 2);
let m = vals.swap_remove(count/2); let m = vals.swap_remove(count / 2);
m.0 / Value::Int(1) 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;
@ -247,18 +273,20 @@ pub fn gcd_inner(a: i64, b: i64) -> i64 {
} }
b -= a; b -= a;
if b == 0 { if b == 0 {
return a << z; return a << z
} }
b >>= b.trailing_zeros(); b >>= b.trailing_zeros();
} }
} }
#[native_func(1)] #[native_func(1)]
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args); let [_, x] = unpack_args!(args);
let Value::Int(x) = x else { let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "isqrt expected integer argument, got {x:#}") throw!(
*SYM_TYPE_ERROR,
"isqrt expected integer argument, got {x:#}"
)
}; };
if x < 0 { if x < 0 {
throw!(*SYM_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())
@ -336,7 +370,7 @@ pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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)
} }
} }
@ -352,7 +386,9 @@ pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
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 {
return Ok(Value::from(0))
};
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?; let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?;
let Value::Int(new_l) = new_l else { let Value::Int(new_l) = new_l else {
unreachable!("int//int * int != int") unreachable!("int//int * int != int")
@ -367,7 +403,10 @@ pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
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));
@ -417,21 +456,26 @@ fn totient_prime(n: &mut u64, p: u64) -> u64 {
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);
@ -443,7 +487,6 @@ pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok((totient as i64).into()) Ok((totient as i64).into())
} }
// //
// numeric operations // numeric operations
// //
@ -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);
@ -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;
@ -25,7 +31,7 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
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()
.downcast_ref::<ValueRegex>()
.map(|vr| Cow::Borrowed(&vr.0)) .map(|vr| Cow::Borrowed(&vr.0))
.ok_or_else(|| exception!( .ok_or_else(|| {
*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}")) exception!(
}, *SYM_TYPE_ERROR,
_ => throw!(*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}") "{name} expected string or regex, got {v:#}"
)
}),
_ => throw!(
*SYM_TYPE_ERROR,
"{name} expected string or regex, got {v:#}"
),
} }
} }
#[native_func(1)] #[native_func(1)]
pub fn _regex(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn _regex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, re] = unpack_args!(args); let [_, re] = unpack_args!(args);
regex_from(&re, "regex") regex_from(&re, "regex").map(|re| ValueRegex(re.into_owned()).into())
.map(|re| ValueRegex(re.into_owned()).into())
} }
#[native_func(2)] #[native_func(2)]
@ -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,7 +168,10 @@ pub fn to_utf8_lossy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn str_to_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn str_to_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args); let [_, s] = unpack_args!(args);
let Value::String(s) = s else { let Value::String(s) = s else {
throw!(*SYM_TYPE_ERROR, "str_to_bytes expected string argument, got {s:#}") throw!(
*SYM_TYPE_ERROR,
"str_to_bytes expected string argument, got {s:#}"
)
}; };
Ok(s.as_bytes() Ok(s.as_bytes()
.iter() .iter()
@ -158,12 +184,21 @@ pub fn str_to_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, b] = unpack_args!(args); let [_, b] = unpack_args!(args);
let Value::List(b) = b else { let Value::List(b) = b else {
throw!(*SYM_TYPE_ERROR, "str_of_bytes expected list argument, got {b:#}") throw!(
*SYM_TYPE_ERROR,
"str_of_bytes expected list argument, got {b:#}"
)
}; };
let bytes: Vec<u8> = b.borrow().iter() let bytes: Vec<u8> = b
.borrow()
.iter()
.map(|v| match v { .map(|v| match v {
Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8), Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8),
_ => throw!(*SYM_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);
@ -191,7 +213,6 @@ pub fn symbol_exists(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// 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);
@ -235,16 +256,16 @@ pub fn func_state(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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()))
.collect();
Ok(l.into()) Ok(l.into())
} }
_ => throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a talc function") _ => throw!(
*SYM_TYPE_ERROR,
"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);
@ -271,10 +292,16 @@ pub fn func_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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,
"apply: first argument must be a function, found {func:#}"
)
} }
let Value::List(l) = lst else { let Value::List(l) = lst else {
throw!(*SYM_TYPE_ERROR, "apply: second argument must be a list, found {lst:#}") throw!(
*SYM_TYPE_ERROR,
"apply: second argument must be a list, found {lst:#}"
)
}; };
let mut args = l.borrow().clone(); let mut args = l.borrow().clone();
args.insert(0, func.clone()); args.insert(0, func.clone());
@ -285,13 +312,19 @@ pub fn apply(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
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,
"compile: argument must be a string, found {src:#}"
)
}; };
let src = src.to_str() let src = src.to_str().map_err(|e| {
.map_err(|e| exception!(*SYM_VALUE_ERROR, "compile: argument must be valid unicode ({e})"))?; exception!(
let ast = talc_lang::parser::parse(src) *SYM_VALUE_ERROR,
.map_err(|e| exception!(symbol!("parse_error"), "{e}"))?; "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); let func = talc_lang::compiler::compile(&ast, None);
Ok(func.into()) Ok(func.into())
} }