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

View file

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

View file

@ -1,8 +1,18 @@
use std::{cell::RefCell, io::IsTerminal, process::ExitCode, rc::Rc};
use clap::ColorChoice;
use rustyline::{error::ReadlineError, history::{FileHistory, History}, ColorMode, Config, Editor};
use talc_lang::{compiler::compile_repl, parser, symbol::Symbol, value::{function::disasm_recursive, Value}, Vm};
use rustyline::{
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};
@ -30,7 +40,6 @@ impl ReplColors {
}
}
fn get_colmode(args: &Args) -> ColorMode {
match args.color {
ColorChoice::Auto => ColorMode::Enabled,
@ -45,7 +54,8 @@ pub fn init_rustyline(args: &Args) -> Result<Editor<TalcHelper, FileHistory>, Ex
.color_mode(get_colmode(args))
.check_cursor_position(true)
.completion_type(rustyline::CompletionType::List)
.max_history_size(4096).unwrap()
.max_history_size(4096)
.unwrap()
.build();
let mut hist = FileHistory::default();
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)) => {
eprintln!("Error creating repl: {e}");
Err(ExitCode::FAILURE)
},
}
Err(ReadlineError::Errno(e)) => {
eprintln!("Error creating repl: {e}");
Err(ExitCode::FAILURE)
},
Err(_) => Err(ExitCode::SUCCESS)
}
Err(_) => Err(ExitCode::SUCCESS),
}
}
@ -117,7 +127,7 @@ pub fn repl(args: &Args) -> ExitCode {
Err(e) => {
eprintln!("{}Error:{} {e}", c.error, c.reset);
continue
},
}
};
let ex = match parser::parse(&line) {
@ -125,7 +135,7 @@ pub fn repl(args: &Args) -> ExitCode {
Err(e) => {
eprintln!("{}Error:{} {e}", c.error, c.reset);
continue
},
}
};
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)]
pub struct Arg24([u8; 3]);
@ -13,14 +17,20 @@ impl Arg24 {
#[inline]
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
Self(n.to_le_bytes()[0..3].try_into().unwrap())
}
#[inline]
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
Self(n.to_le_bytes()[0..3].try_into().unwrap())
}
@ -64,7 +74,9 @@ impl From<Arg24> for i32 {
fn from(v: Arg24) -> Self {
let mut n = u32::from(v);
// sign-extend
if n & 0x00_800000 != 0 { n |= 0xff_000000; }
if n & 0x00_800000 != 0 {
n |= 0xff_000000;
}
n as i32
}
}
@ -88,55 +100,55 @@ impl TryFrom<Arg24> for Symbol {
#[derive(Clone, Copy, Debug, Default)]
pub enum Instruction {
#[default]
Nop, // do nothing
Nop, // do nothing
LoadLocal(Arg24), // push nth local onto stack
StoreLocal(Arg24), // pop stack into nth local
NewLocal, // pop stack into a new local
DropLocal(Arg24), // remove last n locals
LoadLocal(Arg24), // push nth local onto stack
StoreLocal(Arg24), // pop stack into nth local
NewLocal, // pop stack into a new local
DropLocal(Arg24), // remove last n locals
LoadGlobal(Arg24), // load global by id
StoreGlobal(Arg24), // store global by id
LoadGlobal(Arg24), // load global by id
StoreGlobal(Arg24), // store global by id
CloseOver(Arg24), // load nth local and convert to cell, write back a copy
Closure(Arg24), // load constant function and fill state from stack
LoadUpvalue(Arg24), // load
StoreUpvalue(Arg24), // store a cell from closure state to new local
ContinueUpvalue(Arg24), //
LoadClosedLocal(Arg24), // load through cell in nth local
StoreClosedLocal(Arg24), // store through cell in nth local
CloseOver(Arg24), // load nth local and convert to cell, write back a copy
Closure(Arg24), // load constant function and fill state from stack
LoadUpvalue(Arg24), // load
StoreUpvalue(Arg24), // store a cell from closure state to new local
ContinueUpvalue(Arg24), //
LoadClosedLocal(Arg24), // load through cell in nth local
StoreClosedLocal(Arg24), // store through cell in nth local
Const(Arg24), // push nth constant
Int(Arg24), // push integer
Symbol(Arg24), // push symbol
Bool(bool), // push boolean
Nil, // push nil
Const(Arg24), // push nth constant
Int(Arg24), // push integer
Symbol(Arg24), // push symbol
Bool(bool), // push boolean
Nil, // push nil
Dup,
DupTwo,
Drop(Arg24),
Swap,
DupTwo,
Drop(Arg24),
Swap,
UnaryOp(UnaryOp),
BinaryOp(BinaryOp),
NewList(u8),
GrowList(u8),
GrowList(u8),
NewTable(u8),
GrowTable(u8),
GrowTable(u8),
Index,
StoreIndex,
StoreIndex,
Jump(Arg24),
JumpTrue(Arg24),
JumpFalse(Arg24),
IterBegin,
IterTest(Arg24),
IterTest(Arg24),
BeginTry(Arg24),
EndTry,
EndTry,
Call(u8),
Return,
@ -150,21 +162,30 @@ impl std::fmt::Display for Instruction {
Self::StoreLocal(a) => write!(f, "store {}", usize::from(a)),
Self::NewLocal => write!(f, "newlocal"),
Self::DropLocal(n) => write!(f, "discardlocal {}", usize::from(n)),
Self::LoadGlobal(s) => write!(f, "loadglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()),
Self::StoreGlobal(s) => write!(f, "storeglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()),
Self::CloseOver(n) => write!(f, "closeover {}", usize::from(n)),
Self::Closure(c) => write!(f, "closure {}", usize::from(c)),
Self::LoadUpvalue(n) => write!(f, "load_upval {}", usize::from(n)),
Self::StoreUpvalue(n) => write!(f, "store_upval {}", usize::from(n)),
Self::ContinueUpvalue(n) => write!(f, "cont_upval {}", usize::from(n)),
Self::LoadClosedLocal(n) => write!(f, "load_closed {}", usize::from(n)),
Self::StoreClosedLocal(n) => write!(f, "store_closed {}", usize::from(n)),
Self::LoadGlobal(s) => write!(
f,
"loadglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()
),
Self::StoreGlobal(s) => write!(
f,
"storeglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()
),
Self::CloseOver(n) => write!(f, "closeover {}", usize::from(n)),
Self::Closure(c) => write!(f, "closure {}", usize::from(c)),
Self::LoadUpvalue(n) => write!(f, "load_upval {}", usize::from(n)),
Self::StoreUpvalue(n) => write!(f, "store_upval {}", usize::from(n)),
Self::ContinueUpvalue(n) => write!(f, "cont_upval {}", usize::from(n)),
Self::LoadClosedLocal(n) => write!(f, "load_closed {}", usize::from(n)),
Self::StoreClosedLocal(n) => write!(f, "store_closed {}", usize::from(n)),
Self::Const(c) => write!(f, "const {}", usize::from(c)),
Self::Int(i) => write!(f, "int {}", i64::from(i)),
Self::Symbol(s) => write!(f, "symbol {}",
Symbol::try_from(s).expect("symbol does not exist").name()),
Self::Symbol(s) => write!(
f,
"symbol {}",
Symbol::try_from(s).expect("symbol does not exist").name()
),
Self::Bool(b) => write!(f, "bool {b}"),
Self::Nil => write!(f, "nil"),
Self::Dup => write!(f, "dup"),
@ -217,19 +238,28 @@ impl Chunk {
}
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.len() - 1
}
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.len() - 1
}
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 {
catches: Vec::new(),
local_count,
@ -242,5 +272,3 @@ impl Chunk {
self.try_tables[idx] = table;
}
}

View file

@ -1,32 +1,36 @@
use std::collections::{BTreeMap, HashMap};
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::chunk::{Instruction as I, Chunk, Arg24, Catch};
use crate::symbol::{Symbol, SYM_SELF};
use crate::value::function::{FuncAttrs, Function};
use crate::value::Value;
enum ResolveOutcome {
Var(VarKind),
InParent,
None,
Var(VarKind),
InParent,
None,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum VarKind {
Local(usize), Closed(usize), Global
Local(usize),
Closed(usize),
Global,
}
#[derive(Debug, Clone)]
pub struct Var {
name: Symbol,
kind: VarKind,
kind: VarKind,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum CompilerMode {
Function, Repl, Module,
Function,
Repl,
Module,
}
struct Compiler<'a> {
@ -34,19 +38,19 @@ struct Compiler<'a> {
parent: Option<&'a Compiler<'a>>,
chunk: Chunk,
attrs: FuncAttrs,
scope: HashMap<Symbol, Var>,
shadowed: Vec<(Symbol, Option<Var>)>,
closes: BTreeMap<Symbol, usize>,
local_count: usize,
scope: HashMap<Symbol, Var>,
shadowed: Vec<(Symbol, Option<Var>)>,
closes: BTreeMap<Symbol, usize>,
local_count: usize,
}
pub fn compile(expr: &Expr, name: Option<Symbol>) -> Function {
pub fn compile(expr: &Expr, name: Option<Symbol>) -> Function {
let mut comp = Compiler::new_module(name, None);
comp.expr(expr);
comp.finish()
}
pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec<Symbol>) {
pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec<Symbol>) {
let mut comp = Compiler::new_repl(globals);
comp.expr(expr);
comp.finish_repl()
@ -54,20 +58,23 @@ pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec<Symbol>)
impl<'a> Default for Compiler<'a> {
fn default() -> Self {
let mut scope = HashMap::new();
scope.insert(*SYM_SELF, Var {
name: *SYM_SELF,
kind: VarKind::Local(0),
});
let mut scope = HashMap::new();
scope.insert(
*SYM_SELF,
Var {
name: *SYM_SELF,
kind: VarKind::Local(0),
},
);
Self {
mode: CompilerMode::Function,
parent: None,
chunk: Chunk::new(),
attrs: FuncAttrs::default(),
scope,
shadowed: Vec::new(),
local_count: 1,
closes: BTreeMap::new(),
attrs: FuncAttrs::default(),
scope,
shadowed: Vec::new(),
local_count: 1,
closes: BTreeMap::new(),
}
}
}
@ -76,20 +83,23 @@ impl<'a> Compiler<'a> {
fn new_repl(globals: &[Symbol]) -> Self {
let mut new = Self {
mode: CompilerMode::Repl,
attrs: FuncAttrs { arity: 0, name: Some(Symbol::get("<repl>")) },
attrs: FuncAttrs {
arity: 0,
name: Some(Symbol::get("<repl>")),
},
..Self::default()
};
for g in globals {
new.declare_global(*g);
}
new
for g in globals {
new.declare_global(*g);
}
new
}
fn new_module(name: Option<Symbol>, parent: Option<&'a Self>) -> Self {
Self {
mode: CompilerMode::Module,
attrs: FuncAttrs { arity: 0, name },
attrs: FuncAttrs { arity: 0, name },
parent,
..Self::default()
}
@ -98,13 +108,16 @@ impl<'a> Compiler<'a> {
fn new_function(&'a self, name: Option<Symbol>, args: &[Symbol]) -> Self {
let mut new = Self {
mode: CompilerMode::Function,
attrs: FuncAttrs { arity: args.len(), name },
attrs: FuncAttrs {
arity: args.len(),
name,
},
parent: Some(self),
..Self::default()
};
for arg in args {
new.declare_local(*arg);
new.declare_local(*arg);
}
new
@ -112,26 +125,27 @@ impl<'a> Compiler<'a> {
pub fn finish(mut self) -> Function {
self.emit(I::Return);
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len())
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len())
}
pub fn finish_inner(mut self) -> (Function, BTreeMap<Symbol, usize>) {
self.emit(I::Return);
// TODO closure
(
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
self.closes
)
// TODO closure
(
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
self.closes,
)
}
pub fn finish_repl(mut self) -> (Function, Vec<Symbol>) {
self.emit(I::Return);
(
// TODO closure
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
self.scope.into_iter().filter_map(|(_,v)| {
(v.kind == VarKind::Global).then_some(v.name)
}).collect()
// TODO closure
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
self.scope
.into_iter()
.filter_map(|(_, v)| (v.kind == VarKind::Global).then_some(v.name))
.collect(),
)
}
@ -154,38 +168,44 @@ impl<'a> Compiler<'a> {
// dup followed by store: remove the dup
if instrs.len() >= 2
&& matches!(instrs.get(instrs.len() - 2), Some(I::Dup))
&& matches!(instrs.last(), Some(
I::NewLocal | I::StoreLocal(_) | I::StoreGlobal(_)
| I::StoreClosedLocal(_) | I::StoreUpvalue(_)
))
{
&& matches!(
instrs.last(),
Some(
I::NewLocal
| I::StoreLocal(_) | I::StoreGlobal(_)
| I::StoreClosedLocal(_)
| I::StoreUpvalue(_)
)
) {
// can't panic: checked that instrs.len() >= 2
let i = self.chunk.instrs.pop().unwrap();
self.chunk.instrs.pop().unwrap();
self.chunk.instrs.push(i);
n -= 1;
continue;
continue
}
// final side-effectless instruction
let poppable = matches!(
instrs.last(),
Some(
I::Dup | I::Const(_) | I::Int(_)
| I::Nil | I::Bool(_) | I::Symbol(_)
| I::LoadLocal(_) | I::LoadClosedLocal(_)
| I::LoadUpvalue(_)
I::Dup
| I::Const(_) | I::Int(_)
| I::Nil | I::Bool(_)
| I::Symbol(_) | I::LoadLocal(_)
| I::LoadClosedLocal(_)
| I::LoadUpvalue(_)
)
);
if poppable {
// can't panic: checked that instrs.last() was Some
instrs.pop().unwrap();
n -= 1;
continue;
continue
}
// no more optimizations possible
break;
break
}
if n > 0 {
self.emit(I::Drop(Arg24::from_usize(n)));
@ -200,181 +220,180 @@ impl<'a> Compiler<'a> {
self.chunk.instrs[n] = new;
}
#[must_use]
#[must_use]
fn begin_scope(&mut self) -> usize {
self.shadowed.len()
self.shadowed.len()
}
fn end_scope(&mut self, scope: usize) {
let mut locals = 0;
while self.shadowed.len() > scope {
let (name, var) = self.shadowed.pop().expect("scope bad");
let mut locals = 0;
while self.shadowed.len() > scope {
let (name, var) = self.shadowed.pop().expect("scope bad");
if let Some(var) = var {
if var.kind != VarKind::Global {
locals += 1;
}
self.scope.insert(name, var);
} else {
self.scope.remove(&name);
}
}
if let Some(var) = var {
if var.kind != VarKind::Global {
locals += 1;
}
self.scope.insert(name, var);
} else {
self.scope.remove(&name);
}
}
if locals > 0 {
self.emit(I::DropLocal(Arg24::from_usize(locals)));
self.local_count -= locals;
}
if locals > 0 {
self.emit(I::DropLocal(Arg24::from_usize(locals)));
self.local_count -= locals;
}
}
//
// variables
//
fn resolve_name(&self, name: Symbol) -> ResolveOutcome {
if let Some(v) = self.scope.get(&name) {
return ResolveOutcome::Var(v.kind)
}
let Some(parent) = self.parent else {
return ResolveOutcome::None
};
if let ResolveOutcome::None = parent.resolve_name(name) {
return ResolveOutcome::None
}
ResolveOutcome::InParent
}
fn load_var(&mut self, name: Symbol) {
match self.resolve_name(name) {
ResolveOutcome::Var(VarKind::Local(n)) => {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
}
ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
}
ResolveOutcome::InParent => {
let n = match self.closes.get(&name) {
Some(n) => *n,
None => {
let n = self.closes.len();
self.closes.insert(name, n);
n
}
};
self.emit(I::LoadUpvalue(Arg24::from_usize(n)));
}
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
self.emit(I::LoadGlobal(Arg24::from_symbol(name)));
}
}
}
fn resolve_name(&self, name: Symbol) -> ResolveOutcome {
if let Some(v) = self.scope.get(&name) {
return ResolveOutcome::Var(v.kind)
}
let Some(parent) = self.parent else {
return ResolveOutcome::None
};
if let ResolveOutcome::None = parent.resolve_name(name) {
return ResolveOutcome::None
}
ResolveOutcome::InParent
}
fn declare_local(&mut self, name: Symbol) -> usize {
let local = Var {
name,
kind: VarKind::Local(self.local_count)
};
self.local_count += 1;
let shadowed = self.scope.insert(name, local);
self.shadowed.push((name, shadowed));
self.local_count - 1
}
fn load_var(&mut self, name: Symbol) {
match self.resolve_name(name) {
ResolveOutcome::Var(VarKind::Local(n)) => {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
}
ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
}
ResolveOutcome::InParent => {
let n = match self.closes.get(&name) {
Some(n) => *n,
None => {
let n = self.closes.len();
self.closes.insert(name, n);
n
}
};
self.emit(I::LoadUpvalue(Arg24::from_usize(n)));
}
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
self.emit(I::LoadGlobal(Arg24::from_symbol(name)));
}
}
}
fn assign_local(&mut self, name: Symbol) -> usize {
let n = self.declare_local(name);
self.emit(I::NewLocal);
n
}
fn declare_local(&mut self, name: Symbol) -> usize {
let local = Var {
name,
kind: VarKind::Local(self.local_count),
};
self.local_count += 1;
let shadowed = self.scope.insert(name, local);
self.shadowed.push((name, shadowed));
self.local_count - 1
}
fn assign_global(&mut self, name: Symbol) {
self.declare_global(name);
self.store_var(name);
}
fn assign_local(&mut self, name: Symbol) -> usize {
let n = self.declare_local(name);
self.emit(I::NewLocal);
n
}
fn declare_global(&mut self, name: Symbol) {
let global = Var {
name,
kind: VarKind::Global
};
let shadowed = self.scope.insert(name, global);
self.shadowed.push((name, shadowed));
}
fn store_var(&mut self, name: Symbol) {
match self.resolve_name(name) {
ResolveOutcome::Var(VarKind::Local(n)) => {
self.emit(I::StoreLocal(Arg24::from_usize(n)));
}
ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::StoreClosedLocal(Arg24::from_usize(n)));
}
ResolveOutcome::InParent => {
let n = match self.closes.get(&name) {
Some(n) => *n,
None => {
let n = self.closes.len();
self.closes.insert(name, n);
n
}
};
self.emit(I::StoreUpvalue(Arg24::from_usize(n)));
}
ResolveOutcome::Var(VarKind::Global) => {
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
}
ResolveOutcome::None if self.mode == CompilerMode::Repl => {
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
}
ResolveOutcome::None => {
self.assign_local(name);
}
}
}
fn assign_global(&mut self, name: Symbol) {
self.declare_global(name);
self.store_var(name);
}
fn declare_global(&mut self, name: Symbol) {
let global = Var {
name,
kind: VarKind::Global,
};
let shadowed = self.scope.insert(name, global);
self.shadowed.push((name, shadowed));
}
fn store_var(&mut self, name: Symbol) {
match self.resolve_name(name) {
ResolveOutcome::Var(VarKind::Local(n)) => {
self.emit(I::StoreLocal(Arg24::from_usize(n)));
}
ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::StoreClosedLocal(Arg24::from_usize(n)));
}
ResolveOutcome::InParent => {
let n = match self.closes.get(&name) {
Some(n) => *n,
None => {
let n = self.closes.len();
self.closes.insert(name, n);
n
}
};
self.emit(I::StoreUpvalue(Arg24::from_usize(n)));
}
ResolveOutcome::Var(VarKind::Global) => {
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
}
ResolveOutcome::None if self.mode == CompilerMode::Repl => {
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
}
ResolveOutcome::None => {
self.assign_local(name);
}
}
}
//
// Expressions
//
fn expr(&mut self, e: &Expr) {
let Expr { kind, .. } = e;
let Expr { kind, .. } = e;
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) => {
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.emit_discard(1);
}
self.expr(&xs[xs.len()-1]);
self.expr(&xs[xs.len() - 1]);
self.end_scope(scope);
},
}
ExprKind::Literal(v) => self.expr_literal(v),
ExprKind::Ident(ident) => self.load_var(*ident),
ExprKind::UnaryOp(o, a) => {
self.expr(a);
self.emit(I::UnaryOp(*o));
},
}
ExprKind::BinaryOp(o, a, b) => {
self.expr(a);
self.expr(b);
self.emit(I::BinaryOp(*o));
},
}
ExprKind::Assign(o, lv, a) => self.expr_assign(*o, lv, a),
ExprKind::AssignVar(name, a) => {
self.expr(a);
self.emit(I::Dup);
self.assign_local(*name);
},
}
ExprKind::AssignGlobal(name, a) => {
self.expr(a);
self.emit(I::Dup);
self.assign_global(*name);
},
}
ExprKind::List(xs) if xs.is_empty() => {
self.emit(I::NewList(0));
},
}
ExprKind::List(xs) => {
let mut first = true;
for chunk in xs.chunks(16) {
@ -388,10 +407,10 @@ impl<'a> Compiler<'a> {
self.emit(I::GrowList(chunk.len() as u8));
}
}
},
}
ExprKind::Table(xs) if xs.is_empty() => {
self.emit(I::NewTable(0));
},
}
ExprKind::Table(xs) => {
let mut first = true;
for chunk in xs.chunks(8) {
@ -406,19 +425,19 @@ impl<'a> Compiler<'a> {
self.emit(I::GrowTable(chunk.len() as u8));
}
}
},
}
ExprKind::Index(ct, idx) => {
self.expr(ct);
self.expr(idx);
self.emit(I::Index);
},
}
ExprKind::FnCall(f, args) => {
self.expr(f);
for a in args {
self.expr(a);
}
self.emit(I::Call(args.len() as u8));
},
}
ExprKind::AssocFnCall(o, f, args) => {
self.expr(o);
self.emit(I::Dup);
@ -429,17 +448,17 @@ impl<'a> Compiler<'a> {
self.expr(a);
}
self.emit(I::Call((args.len() + 1) as u8));
},
}
ExprKind::Return(e) => {
self.expr(e);
self.emit(I::Return);
},
}
ExprKind::Pipe(a, f) => {
self.expr(a);
self.expr(f);
self.emit(I::Swap);
self.emit(I::Call(1));
},
}
ExprKind::Lambda(args, body) => self.expr_fndef(None, args, body),
ExprKind::FnDef(name, args, body) => self.expr_fndef(*name, args, body),
ExprKind::And(a, b) => {
@ -449,7 +468,7 @@ impl<'a> Compiler<'a> {
self.emit_discard(1);
self.expr(b);
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
},
}
ExprKind::Or(a, b) => {
self.expr(a);
self.emit(I::Dup);
@ -457,7 +476,7 @@ impl<'a> Compiler<'a> {
self.emit_discard(1);
self.expr(b);
self.update_instr(j1, I::JumpTrue(Arg24::from_usize(self.ip())));
},
}
ExprKind::If(cond, b1, b2) => {
self.expr(cond);
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
@ -469,10 +488,8 @@ impl<'a> Compiler<'a> {
} else {
self.emit(I::Nil);
}
self.update_instr(j2,
I::Jump(Arg24::from_usize(self.ip()))
);
},
self.update_instr(j2, I::Jump(Arg24::from_usize(self.ip())));
}
ExprKind::While(cond, body) => {
let start = self.ip();
self.expr(cond);
@ -484,7 +501,7 @@ impl<'a> Compiler<'a> {
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
self.emit(I::Nil);
},
}
ExprKind::For(name, iter, body) => self.expr_for(*name, iter, body),
ExprKind::Try(body, catches) => self.expr_try(body, catches),
}
@ -568,55 +585,60 @@ impl<'a> Compiler<'a> {
let (func, closes) = inner.finish_inner();
let func_const = self.add_const(func.into());
let num_closed = closes.len();
let num_closed = closes.len();
for (name, _) in closes {
match self.resolve_name(name) {
ResolveOutcome::Var(VarKind::Local(n)) => {
self.emit(I::CloseOver(Arg24::from_usize(n)));
self.scope.entry(name).and_modify(|v| {
v.kind = VarKind::Closed(n);
});
},
ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
},
ResolveOutcome::InParent => {
let n = self.closes.len();
self.closes.insert(name, n);
self.emit(I::ContinueUpvalue(Arg24::from_usize(n)));
},
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global)
=> panic!("upvalue resolved to none or global"),
}
}
for (name, _) in closes {
match self.resolve_name(name) {
ResolveOutcome::Var(VarKind::Local(n)) => {
self.emit(I::CloseOver(Arg24::from_usize(n)));
self.scope.entry(name).and_modify(|v| {
v.kind = VarKind::Closed(n);
});
}
ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
}
ResolveOutcome::InParent => {
let n = self.closes.len();
self.closes.insert(name, n);
self.emit(I::ContinueUpvalue(Arg24::from_usize(n)));
}
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
panic!("upvalue resolved to none or global")
}
}
}
if num_closed == 0 {
self.emit(I::Const(Arg24::from_usize(func_const)));
} else {
self.emit(I::Closure(Arg24::from_usize(func_const)));
}
if num_closed == 0 {
self.emit(I::Const(Arg24::from_usize(func_const)));
} else {
self.emit(I::Closure(Arg24::from_usize(func_const)));
}
if let Some(name) = name {
self.emit(I::Dup);
self.store_var(name);
}
if let Some(name) = name {
self.emit(I::Dup);
self.store_var(name);
}
}
fn expr_literal(&mut self, val: &Value) {
match val {
Value::Nil
=> { self.emit(I::Nil); },
Value::Bool(b)
=> { self.emit(I::Bool(*b)); },
Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i)
=> { self.emit(I::Int(Arg24::from_i64(*i))); },
Value::Symbol(s)
=> { self.emit(I::Symbol(Arg24::from_symbol(*s))); },
Value::Nil => {
self.emit(I::Nil);
}
Value::Bool(b) => {
self.emit(I::Bool(*b));
}
Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i) => {
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());
self.emit(I::Const(Arg24::from_usize(n)));
},
}
}
}
@ -626,20 +648,20 @@ impl<'a> Compiler<'a> {
self.expr(a);
self.emit(I::Dup);
self.store_var(*i);
},
}
(LValueKind::Ident(i), Some(o)) => {
self.load_var(*i);
self.expr(a);
self.emit(I::BinaryOp(o));
self.emit(I::Dup);
self.store_var(*i);
},
}
(LValueKind::Index(ct, i), None) => {
self.expr(ct);
self.expr(i);
self.expr(a);
self.emit(I::StoreIndex);
},
}
(LValueKind::Index(ct, i), Some(o)) => {
self.expr(ct);
self.expr(i);
@ -648,8 +670,7 @@ impl<'a> Compiler<'a> {
self.expr(a);
self.emit(I::BinaryOp(o));
self.emit(I::StoreIndex);
},
}
}
}
}

View file

@ -1,5 +1,9 @@
use std::{rc::Rc, collections::HashMap, cell::RefCell, fmt::Display};
use crate::{lstring::LStr, symbol::{Symbol, SYM_DATA, SYM_MSG, SYM_TYPE}, value::{HashValue, Value}};
use crate::{
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>;
@ -12,19 +16,35 @@ pub struct Exception {
impl Exception {
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 {
Self { ty, msg: Some(msg), data: None }
Self {
ty,
msg: Some(msg),
data: None,
}
}
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 {
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> {
@ -102,6 +122,3 @@ macro_rules! throw {
}
pub use throw;

View file

@ -2,13 +2,13 @@
#![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::allow_attributes)]
pub mod symbol;
pub mod parser;
pub mod value;
pub mod exception;
pub mod chunk;
pub mod compiler;
pub mod exception;
pub mod lstring;
pub mod parser;
pub mod symbol;
pub mod value;
mod 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};
@ -28,7 +39,9 @@ fn is_continue(b: u8) -> bool {
#[inline]
fn calc_continue(mut ch: u32, bytes: &[u8]) -> Option<char> {
for b in bytes {
if !is_continue(*b) { return None }
if !is_continue(*b) {
return None
}
ch = (ch << 6) | (b & 0x3f) as u32;
}
char::from_u32(ch)
@ -40,23 +53,29 @@ fn next_codepoint(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
match init {
0..=0x7f => return Some((&bytes[1..], Ok(init as char))),
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 {
break 'case;
break 'case
};
return Some((&bytes[2..], Ok(ch)))
},
}
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 {
break 'case;
break 'case
};
return Some((&bytes[3..], Ok(ch)))
},
}
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 {
break 'case;
break 'case
};
return Some((&bytes[4..], Ok(ch)))
}
@ -69,45 +88,55 @@ fn next_codepoint(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
#[inline]
fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
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) {
return Some((&bytes[..len-1], Ok(last as char)))
return Some((&bytes[..len - 1], Ok(last as char)))
}
'case: {
if !is_continue(last) { break 'case }
if !is_continue(last) {
break 'case
}
if len < 2 { break 'case }
let b1 = bytes[len-2];
if len < 2 {
break 'case
}
let b1 = bytes[len - 2];
if 0xe0 & b1 == 0xc0 {
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) {
break 'case
}
if len < 3 { break 'case }
let b2 = bytes[len-3];
if len < 3 {
break 'case
}
let b2 = bytes[len - 3];
if 0xf0 & b2 == 0xe0 {
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) {
break 'case
}
if len < 4 { break 'case }
let b3 = bytes[len-4];
if len < 4 {
break 'case
}
let b3 = bytes[len - 4];
if 0xf8 & b3 == 0xf0 {
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)]
@ -230,47 +259,69 @@ impl BorrowMut<LStr> for LString {
impl From<LString> for Vec<u8> {
#[inline]
fn from(value: LString) -> Self { value.inner }
fn from(value: LString) -> Self {
value.inner
}
}
impl From<Vec<u8>> for LString {
#[inline]
fn from(value: Vec<u8>) -> Self { Self { inner: value } }
fn from(value: Vec<u8>) -> Self {
Self { inner: value }
}
}
impl From<String> for LString {
#[inline]
fn from(value: String) -> Self { Self { inner: value.into_bytes() } }
fn from(value: String) -> Self {
Self {
inner: value.into_bytes(),
}
}
}
impl From<OsString> for LString {
#[inline]
fn from(value: OsString) -> Self { Self { inner: value.into_encoded_bytes() } }
fn from(value: OsString) -> Self {
Self {
inner: value.into_encoded_bytes(),
}
}
}
impl From<&LStr> for LString {
#[inline]
fn from(value: &LStr) -> Self { value.to_owned() }
fn from(value: &LStr) -> Self {
value.to_owned()
}
}
impl From<&str> for LString {
#[inline]
fn from(value: &str) -> Self { value.to_owned().into() }
fn from(value: &str) -> Self {
value.to_owned().into()
}
}
impl From<&[u8]> for LString {
#[inline]
fn from(value: &[u8]) -> Self { value.to_owned().into() }
fn from(value: &[u8]) -> Self {
value.to_owned().into()
}
}
impl<const N: usize> From<&[u8; N]> for LString {
#[inline]
fn from(value: &[u8; N]) -> Self { value.as_slice().into() }
fn from(value: &[u8; N]) -> Self {
value.as_slice().into()
}
}
impl<const N: usize> From<[u8; N]> for LString {
#[inline]
fn from(value: [u8; N]) -> Self { value.as_slice().into() }
fn from(value: [u8; N]) -> Self {
value.as_slice().into()
}
}
impl From<Cow<'_, LStr>> for LString {
@ -278,7 +329,7 @@ impl From<Cow<'_, LStr>> for LString {
fn from(value: Cow<'_, LStr>) -> Self {
match value {
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] {
#[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 {
@ -339,7 +392,9 @@ impl<'a> From<&'a OsStr> for &'a LStr {
impl<'a> From<&'a mut LStr> for &'a mut [u8] {
#[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 {
@ -351,7 +406,9 @@ impl<'a> From<&'a mut [u8]> for &'a mut LStr {
impl<'a> From<&'a LString> for &'a LStr {
#[inline]
fn from(value: &'a LString) -> Self { value }
fn from(value: &'a LString) -> Self {
value
}
}
impl From<&LStr> for Rc<LStr> {
@ -412,35 +469,35 @@ impl From<u8> for LString {
impl FromIterator<char> for LString {
#[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()
}
}
impl FromIterator<u8> for LString {
#[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()
}
}
impl Extend<u8> for LString {
#[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);
}
}
impl<'a> Extend<&'a u8> for LString {
#[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);
}
}
impl Extend<char> for LString {
#[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 (lo, _) = iter.size_hint();
self.reserve(lo);
@ -450,7 +507,7 @@ impl Extend<char> for LString {
impl<'a> Extend<&'a char> for LString {
#[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 (lo, _) = iter.size_hint();
self.reserve(lo);
@ -468,7 +525,9 @@ impl io::Write for LString {
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> { Ok(()) }
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
//impl fmt::Write for LString {
@ -547,15 +606,25 @@ impl<'a> Iterator for Bytes<'a> {
type Item = u8;
#[inline]
fn next(&mut self) -> Option<Self::Item> { self.0.next() }
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
#[inline]
fn count(self) -> usize { self.0.count() }
fn count(self) -> usize {
self.0.count()
}
#[inline]
fn last(self) -> Option<Self::Item> { self.0.last() }
fn last(self) -> Option<Self::Item> {
self.0.last()
}
#[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]
fn all<F: FnMut(Self::Item) -> bool>(&mut self, f: F) -> bool {
self.0.all(f)
@ -576,7 +645,9 @@ impl<'a> Iterator for Bytes<'a> {
impl<'a> ExactSizeIterator for Bytes<'a> {
#[inline]
fn len(&self) -> usize { self.0.len() }
fn len(&self) -> usize {
self.0.len()
}
}
impl<'a> FusedIterator for Bytes<'a> {}
@ -597,7 +668,7 @@ impl<'a> Iterator for LosslessChars<'a> {
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.0.len();
((len + 3)/4, Some(len))
((len + 3) / 4, Some(len))
}
}
@ -618,19 +689,19 @@ impl<'a> Iterator for LosslessCharsIndices<'a> {
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let (new_bytes, res) = next_codepoint(self.0)?;
let index = self.1;
let index = self.1;
self.0 = new_bytes;
match res {
Ok(c) => self.1 += c.len_utf8(),
Err(_) => self.1 += 1,
}
match res {
Ok(c) => self.1 += c.len_utf8(),
Err(_) => self.1 += 1,
}
Some((index, res))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.0.len();
((len + 3)/4, Some(len))
((len + 3) / 4, Some(len))
}
}
@ -638,15 +709,14 @@ impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
let (new_bytes, res) = next_codepoint_back(self.0)?;
self.0 = new_bytes;
match res {
Ok(c) => self.1 -= c.len_utf8(),
Err(_) => self.1 -= 1,
}
match res {
Ok(c) => self.1 -= c.len_utf8(),
Err(_) => self.1 -= 1,
}
Some((self.1, res))
}
}
#[derive(Clone)]
pub struct Chars<'a>(LosslessChars<'a>);
@ -702,7 +772,7 @@ impl<'a> Iterator for CharsIndices<'a> {
impl<'a> DoubleEndedIterator for CharsIndices<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
if let (index, Ok(c)) = self.0.next_back()? {
if let (index, Ok(c)) = self.0.next_back()? {
return Some((index, c))
}
}
@ -731,16 +801,24 @@ impl LStr {
}
#[inline]
pub const fn as_bytes(&self) -> &[u8] { &self.inner }
pub const fn as_bytes(&self) -> &[u8] {
&self.inner
}
#[inline]
pub fn as_bytes_mut(&mut self) -> &mut [u8] { &mut self.inner }
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.inner
}
#[inline]
pub fn byte_at(&self, n: usize) -> u8 { self.inner[n] }
pub fn byte_at(&self, n: usize) -> u8 {
self.inner[n]
}
#[inline]
pub fn byte_get(&self, n: usize) -> Option<u8> { self.inner.get(n).copied() }
pub fn byte_get(&self, n: usize) -> Option<u8> {
self.inner.get(n).copied()
}
#[inline]
pub fn bytes(&self) -> Bytes {
@ -787,11 +865,13 @@ impl LStr {
#[inline]
pub fn to_os_str(&self) -> Cow<OsStr> {
#[cfg(unix)] {
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
Cow::Borrowed(OsStr::from_bytes(self.as_bytes()))
}
#[cfg(not(unix))] {
#[cfg(not(unix))]
{
Cow::Owned(self.to_string().into())
}
}
@ -862,18 +942,21 @@ impl LStr {
self.as_bytes().ends_with(s.as_bytes())
}
pub fn strip_prefix(&self, prefix: &LStr) -> Option<&LStr> {
self.as_bytes().strip_prefix(prefix.as_bytes()).map(LStr::from_bytes)
}
pub fn strip_prefix(&self, prefix: &LStr) -> Option<&LStr> {
self.as_bytes()
.strip_prefix(prefix.as_bytes())
.map(LStr::from_bytes)
}
pub fn strip_suffix(&self, suffix: &LStr) -> Option<&LStr> {
self.as_bytes().strip_suffix(suffix.as_bytes()).map(LStr::from_bytes)
}
pub fn strip_suffix(&self, suffix: &LStr) -> Option<&LStr> {
self.as_bytes()
.strip_suffix(suffix.as_bytes())
.map(LStr::from_bytes)
}
pub fn is_identifier(&self) -> bool {
let mut chars = self.chars_lossless();
let first = chars.next()
.is_some_and(|ch| ch.is_ok_and(is_xid_start));
let first = chars.next().is_some_and(|ch| ch.is_ok_and(is_xid_start));
if !first {
return false
}
@ -899,11 +982,15 @@ impl AddAssign<&LStr> for LString {
}
impl Default for &LStr {
fn default() -> Self { [].as_ref().into() }
fn default() -> Self {
[].as_ref().into()
}
}
impl Default for &mut LStr {
fn default() -> Self { [].as_mut().into() }
fn default() -> Self {
[].as_mut().into()
}
}
macro_rules! impl_index {
@ -945,4 +1032,3 @@ impl_index!(std::ops::RangeInclusive<usize>);
impl_index!(std::ops::RangeTo<usize>);
impl_index!(std::ops::RangeToInclusive<usize>);
impl_index!((std::ops::Bound<usize>, std::ops::Bound<usize>));

View file

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

File diff suppressed because it is too large Load diff

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,5 +1,5 @@
use core::fmt;
use std::num::{ParseIntError, ParseFloatError};
use std::num::{ParseFloatError, ParseIntError};
use thiserror::Error;
@ -9,26 +9,29 @@ use super::Span;
#[derive(Clone, Debug, Error)]
pub struct ParserError {
pub span: Span,
pub msg: String,
pub span: Span,
pub msg: String,
}
impl fmt::Display for ParserError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} | {}", self.span, self.msg)
}
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} | {}", self.span, self.msg)
}
}
pub trait SpanParserError {
type Output;
fn span_err(self, span: Span) -> Self::Output;
type Output;
fn span_err(self, span: Span) -> Self::Output;
}
impl<T, E: std::error::Error> SpanParserError for Result<T, E> {
type Output = Result<T, ParserError>;
fn span_err(self, span: Span) -> Self::Output {
self.map_err(|e| ParserError { span, msg: e.to_string() })
}
type Output = Result<T, ParserError>;
fn span_err(self, span: Span) -> Self::Output {
self.map_err(|e| ParserError {
span,
msg: e.to_string(),
})
}
}
#[derive(Clone, Copy, Debug, Error)]
@ -56,26 +59,29 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
let mut chars = src.chars();
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)?;
match c {
'"' | '\'' | '\\' => s.push_char(c),
'0' => s.push_char('\0'),
'a' => s.push_char('\x07'),
'b' => s.push_char('\x08'),
't' => s.push_char('\t'),
'n' => s.push_char('\n'),
'v' => s.push_char('\x0b'),
'f' => s.push_char('\x0c'),
'r' => s.push_char('\r'),
'e' => s.push_char('\x1b'),
'0' => s.push_char('\0'),
'a' => s.push_char('\x07'),
'b' => s.push_char('\x08'),
't' => s.push_char('\t'),
'n' => s.push_char('\n'),
'v' => s.push_char('\x0b'),
'f' => s.push_char('\x0c'),
'r' => s.push_char('\r'),
'e' => s.push_char('\x1b'),
'x' => {
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
s.push_byte((n1 * 16 + n2) as u8);
},
}
'u' => {
let Some('{') = chars.next() else {
return Err(StrEscapeError::MissingBrace)
@ -85,7 +91,9 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
let Some(c) = chars.next() else {
return Err(StrEscapeError::UnicodeEof)
};
if c == '}' { break }
if c == '}' {
break
}
if n > 0x10ffff {
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))?;
s.push_char(ch);
},
}
c => return Err(StrEscapeError::Invalid(c)),
}
}
@ -123,14 +130,14 @@ pub fn parse_int<'a, S: Into<&'a LStr>>(f: S, radix: u32) -> Result<i64, ParseIn
}
pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result<i64, ParseIntError> {
let f = f.into();
match f.chars().nth(2) {
Some('x') => parse_int(&f[2..], 16),
Some('o') => parse_int(&f[2..], 8),
Some('s') => parse_int(&f[2..], 6),
Some('b') => parse_int(&f[2..], 2),
_ => parse_int(f, 10),
}
let f = f.into();
match f.chars().nth(2) {
Some('x') => parse_int(&f[2..], 16),
Some('o') => parse_int(&f[2..], 8),
Some('s') => parse_int(&f[2..], 6),
Some('b') => parse_int(&f[2..], 2),
_ => parse_int(f, 10),
}
}
pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
@ -151,14 +158,14 @@ pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
x /= radix as u64;
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);
if x == 0 {
break;
break
}
}
result[begin..].reverse();
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;
#[derive(Default)]
struct SymbolTable {
names: Vec<&'static LStr>,
values: HashMap<&'static LStr, Symbol>
values: HashMap<&'static LStr, Symbol>,
}
lazy_static! {
pub static ref SYM_SELF: Symbol = symbol!(self);
pub static ref SYM_DOLLAR_SIGN: Symbol = symbol!("$");
pub static ref SYM_NIL: Symbol = symbol!(nil);
pub static ref SYM_BOOL: Symbol = symbol!(bool);
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_FUNCTION: Symbol = symbol!(function);
pub static ref SYM_NATIVE_FUNC: Symbol = symbol!(native_func);
pub static ref SYM_END_ITERATION: Symbol = symbol!(end_iteration);
pub static ref SYM_TYPE: Symbol = symbol!(type);
pub static ref SYM_MSG: Symbol = symbol!(msg);
pub static ref SYM_DATA: Symbol = symbol!(data);
pub static ref SYM_TYPE_ERROR: Symbol = symbol!(type_error);
pub static ref SYM_VALUE_ERROR: Symbol = symbol!(value_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
pub fn name(self) -> &'static LStr {
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;
use crate::lstring::{LStr, LString};

View file

@ -1,31 +1,35 @@
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)]
pub struct FuncAttrs {
pub arity: usize,
pub name: Option<Symbol>,
pub name: Option<Symbol>,
}
#[derive(Debug, Clone)]
pub struct Function {
pub attrs: FuncAttrs,
pub chunk: Rc<Chunk>,
pub state: Box<[CellValue]>,
pub state: Box<[CellValue]>,
}
impl Function {
pub fn new(chunk: Rc<Chunk>, attrs: FuncAttrs, closes: usize) -> Self {
let v = Rc::new(RefCell::new(Value::Nil));
let state = std::iter::repeat(v).take(closes).collect();
Self::from_parts(chunk, attrs, state)
let v = Rc::new(RefCell::new(Value::Nil));
let state = std::iter::repeat(v).take(closes).collect();
Self::from_parts(chunk, attrs, state)
}
pub fn from_parts(chunk: Rc<Chunk>, attrs: FuncAttrs, state: Box<[CellValue]>) -> Self {
Self { chunk, attrs, state }
Self {
chunk,
attrs,
state,
}
}
}
@ -38,11 +42,20 @@ pub struct NativeFunc {
impl NativeFunc {
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 {
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<()> {
writeln!(w, "{} ({})",
Value::Function(f.clone()),
f.attrs.arity)?;
writeln!(w, "{} ({})", Value::Function(f.clone()), f.attrs.arity)?;
if !f.chunk.consts.is_empty() {
writeln!(w, "constants")?;
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 {
write!(w, "{:04} [", catch.addr)?;
for (i, ty) in types.iter().enumerate() {
if i != 0 { write!(w, ", ")?; }
if i != 0 {
write!(w, ", ")?;
}
write!(w, "{}", ty.name())?;
}
writeln!(w, "]")?;
@ -101,4 +114,3 @@ pub fn disasm_recursive(f: &Rc<Function>, w: &mut impl std::io::Write) -> std::i
}
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 {
pub fn index(&self, idx: Self) -> Result<Self> {
@ -11,70 +16,93 @@ impl Value {
if i >= 0 && (i as usize) < l.len() {
Ok(l[i as usize].clone())
} else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
throw!(
*SYM_INDEX_ERROR,
"index {i} out of bounds for list of length {}",
l.len()
)
}
},
}
(V::Range(r), V::Int(i)) => {
if i >= 0 && (
r.ty == RangeType::Endless
|| i < r.stop
|| (r.ty == RangeType::Closed && i == r.stop)
) {
if i >= 0
&& (r.ty == RangeType::Endless
|| i < r.stop || (r.ty == RangeType::Closed && i == r.stop))
{
Ok((r.start + i).into())
} else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
}
},
}
(V::Table(t), i) if i.hashable() => {
let t = t.borrow();
let i = i.try_into()?;
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
},
}
(V::String(s), V::Range(r)) => {
let slen = s.len();
match r.ty {
RangeType::Open => {
if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
if r.stop < 0 || r.stop > slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
throw!(
*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())
},
}
RangeType::Closed => {
if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
if r.stop < 0 || r.stop >= slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
throw!(
*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())
},
}
RangeType::Endless => {
if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
Ok(s[r.start as usize..].into())
},
}
}
},
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() {
let col = col.clone();
let func = move |vm: &mut Vm, _| {
match vmcalliter!(vm; ii.clone())? {
}
(col, idx) => {
if let Ok(ii) = idx.clone().to_iter_function() {
let col = col.clone();
let func = move |vm: &mut Vm, _| match vmcalliter!(vm; ii.clone())? {
Some(i) => col.index(i),
None => Ok(Value::from(*SYM_END_ITERATION)),
}
};
Ok(NativeFunc::new(Box::new(func), 0, symbol!("index...")).into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
};
Ok(NativeFunc::new(Box::new(func), 0, symbol!("index...")).into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
}
}
}
}
@ -88,15 +116,19 @@ impl Value {
l[i as usize] = val;
Ok(())
} 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() => {
let mut t = t.borrow_mut();
let i = i.try_into()?;
t.insert(i, val);
Ok(())
},
}
(V::List(t), V::Range(r)) => {
let iter = val.to_iter_function()?;
let mut vals = Vec::new();
@ -107,53 +139,78 @@ impl Value {
match r.ty {
RangeType::Open => {
if r.start < 0 || r.start > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
}
if r.stop < 0 || r.stop > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
}
let end = tm.split_off(r.stop as usize);
tm.truncate(r.start as usize);
tm.extend(vals.into_iter().chain(end));
},
}
RangeType::Closed => {
if r.start < 0 || r.start > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
}
if r.stop < 0 || r.stop >= tm.len() as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
}
let end = tm.split_off(r.stop as usize + 1);
tm.truncate(r.start as usize);
tm.extend(vals.into_iter().chain(end));
},
}
RangeType::Endless => {
if r.start < 0 || r.start > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
}
tm.truncate(r.start as usize);
tm.extend(vals);
},
}
}
Ok(())
},
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() {
let val = val.to_iter_function()?;
while let Some(i) = vmcalliter!(vm; ii.clone())? {
let Some(v) = vmcalliter!(vm; val.clone())? else {
throw!(*SYM_INDEX_ERROR, "not enough values provided for store index")
};
col.store_index(vm, i, v)?;
}
(col, idx) => {
if let Ok(ii) = idx.clone().to_iter_function() {
let val = val.to_iter_function()?;
while let Some(i) = vmcalliter!(vm; ii.clone())? {
let Some(v) = vmcalliter!(vm; val.clone())? else {
throw!(
*SYM_INDEX_ERROR,
"not enough values provided for store index"
)
};
col.store_index(vm, i, v)?;
}
Ok(())
} else {
throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}")
}
Ok(())
} else {
throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}")
},
}
}
}
}

View file

@ -7,16 +7,19 @@ pub use num_complex::Complex64;
use num_complex::ComplexFloat;
pub use num_rational::Rational64;
use crate::exception::{throw, Exception};
use crate::lstring::{LStr, LString};
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 index;
pub mod ops;
pub mod range;
pub mod index;
type RcList = Rc<RefCell<Vec<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 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>");
Ok(())
}
@ -63,7 +71,11 @@ pub trait NativeValue: std::fmt::Debug + Any {
impl Display for Value {
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}")
}
}
@ -81,7 +93,12 @@ impl Value {
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;
match self {
Self::Nil => write!(w, "nil"),
@ -95,10 +112,10 @@ impl Value {
write!(w, "{name:?}")?;
}
Ok(())
},
}
Self::Range(r) => match r.ty {
RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
RangeType::Endless => write!(w, "{}..*", r.start),
},
Self::Int(i) => write!(w, "{i}"),
@ -110,50 +127,50 @@ impl Value {
w.push_byte(b'+');
}
write!(w, "{:?}i", z.im())
},
}
Self::Cell(v) if repr => {
if recur.contains(&(v.as_ptr() as _)) {
return w.write_all(b"cell(...)")
}
if recur.contains(&(v.as_ptr() as _)) {
return w.write_all(b"cell(...)")
}
w.write_all(b"cell(")?;
recur.push(v.as_ptr() as _);
recur.push(v.as_ptr() as _);
v.borrow().write_to_lstring(w, repr, recur)?;
recur.pop();
recur.pop();
w.write_all(b")")
},
}
Self::Cell(v) => {
if recur.contains(&(v.as_ptr() as _)) {
return w.write_all(b"cell(...)")
}
recur.push(v.as_ptr() as _);
v.borrow().write_to_lstring(w, true, recur)?;
recur.pop();
Ok(())
},
if recur.contains(&(v.as_ptr() as _)) {
return w.write_all(b"cell(...)")
}
recur.push(v.as_ptr() as _);
v.borrow().write_to_lstring(w, true, recur)?;
recur.pop();
Ok(())
}
Self::String(s) if repr => write!(w, "{s:?}"),
Self::String(s) => w.write_all(s.as_bytes()),
Self::List(l) => {
if recur.contains(&(l.as_ptr() as _)) {
return w.write_all(b"[...]")
}
if recur.contains(&(l.as_ptr() as _)) {
return w.write_all(b"[...]")
}
w.write_all(b"[")?;
recur.push(l.as_ptr() as _);
recur.push(l.as_ptr() as _);
for (i, item) in l.borrow().iter().enumerate() {
if i != 0 {
w.write_all(b", ")?;
}
item.write_to_lstring(w, true, recur)?;
}
recur.pop();
recur.pop();
w.write_all(b"]")
},
}
Self::Table(t) => {
if recur.contains(&(t.as_ptr() as _)) {
return w.write_all(b"{...}")
}
if recur.contains(&(t.as_ptr() as _)) {
return w.write_all(b"{...}")
}
w.write_all(b"{ ")?;
recur.push(t.as_ptr() as _);
recur.push(t.as_ptr() as _);
for (i, (k, v)) in t.borrow().iter().enumerate() {
if i != 0 {
w.write_all(b", ")?;
@ -162,36 +179,58 @@ impl Value {
w.write_all(b" = ")?;
v.write_to_lstring(w, true, recur)?;
}
recur.pop();
recur.pop();
w.write_all(b" }")
},
}
Self::Function(g) => {
if let Some(name) = g.attrs.name {
write!(w, "<function {}({}) @{:0>12x}>",
name.name(), g.attrs.arity, Rc::as_ptr(g) as usize)
} else {
write!(w, "<anon function({}) @{:0>12x}>",
g.attrs.arity, Rc::as_ptr(g) as usize)
}
}
if let Some(name) = g.attrs.name {
write!(
w,
"<function {}({}) @{:0>12x}>",
name.name(),
g.attrs.arity,
Rc::as_ptr(g) as usize
)
} else {
write!(
w,
"<anon function({}) @{:0>12x}>",
g.attrs.arity,
Rc::as_ptr(g) as usize
)
}
}
Self::NativeFunc(g) => {
if let Some(name) = g.attrs.name {
write!(w, "<native function {}({}) @{:0>12x}>",
name.name(), g.attrs.arity, Rc::as_ptr(g) as usize)
} else {
write!(w, "<anon native function({}) @{:0>12x}>",
g.attrs.arity, Rc::as_ptr(g) as usize)
}
}
if let Some(name) = g.attrs.name {
write!(
w,
"<native function {}({}) @{:0>12x}>",
name.name(),
g.attrs.arity,
Rc::as_ptr(g) as usize
)
} else {
write!(
w,
"<anon native function({}) @{:0>12x}>",
g.attrs.arity,
Rc::as_ptr(g) as usize
)
}
}
Self::Native(n) => n.to_lstring(w, repr, recur),
}
}
fn write_table_key_repr(&self, w: &mut LString, 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 {
Self::Nil | Self::Bool(_)
| Self::Int(_) | Self::String(_)
=> self.write_to_lstring(w, true, recur),
Self::Nil | Self::Bool(_) | Self::Int(_) | Self::String(_) => {
self.write_to_lstring(w, true, recur)
}
Self::Symbol(s) => {
let name = s.name();
if name.is_identifier() {
@ -200,7 +239,7 @@ impl Value {
} else {
self.write_to_lstring(w, true, recur)
}
},
}
_ => {
w.push_byte(b'(');
self.write_to_lstring(w, true, recur)?;
@ -215,16 +254,18 @@ impl Value {
Cow::Borrowed(s)
} else {
let mut s = LString::new();
let mut recur = Vec::new();
self.write_to_lstring(&mut s, false, &mut recur).expect("write_to_lstring failed");
let mut recur = Vec::new();
self.write_to_lstring(&mut s, false, &mut recur)
.expect("write_to_lstring failed");
Cow::Owned(s)
}
}
pub fn repr(&self) -> LString {
let mut s = LString::new();
let mut recur = Vec::new();
self.write_to_lstring(&mut s, true, &mut recur).expect("write_to_lstring failed");
let mut recur = Vec::new();
self.write_to_lstring(&mut s, true, &mut recur)
.expect("write_to_lstring failed");
s
}
@ -252,15 +293,19 @@ impl Value {
pub fn hashable(&self) -> bool {
matches!(
self,
Value::Nil | Value::Bool(_) | Value::Symbol(_)
| Value::Int(_) | Value::Ratio(_) | Value::String(_)
Value::Nil
| Value::Bool(_)
| Value::Symbol(_)
| Value::Int(_)
| Value::Ratio(_)
| Value::String(_)
)
}
pub fn downcast_native<T: NativeValue>(&self) -> Option<&T> {
match self {
Value::Native(n) => n.as_any().downcast_ref(),
_ => None
_ => None,
}
}
}
@ -281,8 +326,12 @@ impl TryFrom<Value> for HashValue {
}
impl HashValue {
pub fn into_inner(self) -> Value { self.0 }
pub fn inner(&self) -> &Value { &self.0 }
pub fn into_inner(self) -> Value {
self.0
}
pub fn inner(&self) -> &Value {
&self.0
}
}
impl Hash for HashValue {
@ -303,39 +352,57 @@ impl Hash for HashValue {
macro_rules! impl_from {
($ty:ty, $var:ident) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(value) }
fn from(value: $ty) -> Self {
Self::$var(value)
}
}
};
($ty:ty, $var:ident, hash) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(value) }
fn from(value: $ty) -> Self {
Self::$var(value)
}
}
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) => {
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 {
fn from(value: Rc<$ty>) -> Self { Self::$var(value) }
fn from(value: Rc<$ty>) -> Self {
Self::$var(value)
}
}
};
($ty:ty, $var:ident, rcref) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(Rc::new(RefCell::new(value))) }
fn from(value: $ty) -> Self {
Self::$var(Rc::new(RefCell::new(value)))
}
}
impl From<RefCell<$ty>> for Value {
fn from(value: RefCell<$ty>) -> Self { Self::$var(Rc::new(value)) }
fn from(value: RefCell<$ty>) -> Self {
Self::$var(Rc::new(value))
}
}
impl From<Rc<RefCell<$ty>>> for Value {
fn from(value: Rc<RefCell<$ty>>) -> Self { Self::$var(value) }
fn from(value: Rc<RefCell<$ty>>) -> Self {
Self::$var(value)
}
}
};
($ty:ty, $var:ident, into) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self { Self::$var(value.into()) }
fn from(value: $ty) -> Self {
Self::$var(value.into())
}
}
};
}

View file

@ -1,13 +1,27 @@
use std::{cell::RefCell, cmp::Ordering, ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Shl, Shr, Sub}, rc::Rc};
use std::{
cell::RefCell,
cmp::Ordering,
ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Shl, Shr, Sub},
rc::Rc,
};
use num_complex::{Complex64, ComplexFloat};
use num_rational::Rational64;
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 {
fn to_f64(&self) -> f64;
@ -33,9 +47,11 @@ impl Value {
Value::List(l) => l.borrow().len() > 0,
Value::Cell(v) => v.borrow().truthy(),
Value::Symbol(_) | Value::Table(_)
| Value::Function(_) | Value::NativeFunc(_)
| Value::Native(_) => true,
Value::Symbol(_)
| Value::Table(_)
| Value::Function(_)
| Value::NativeFunc(_)
| Value::Native(_) => true,
}
}
}
@ -43,46 +59,48 @@ impl Value {
pub fn promote(a: Value, b: Value) -> (Value, Value) {
use Value as V;
match (&a, &b) {
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
(V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
(V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
(V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64()), b),
(V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64().into()), b),
(V::Float(x), V::Complex(..)) => (V::Complex(x.into()), b),
(V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
(V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
(V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
(V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64())),
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64().into())),
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
(V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
(V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
(V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64()), b),
(V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64().into()), b),
(V::Float(x), V::Complex(..)) => (V::Complex(x.into()), b),
(V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
(V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
(V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
(V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64())),
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64().into())),
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
_ => (a, b),
}
}
////////////////////////
// unary arithmetic //
////////////////////////
impl Neg for Value {
type Output = Result<Self>;
fn neg(self) -> Self::Output {
use Value as V;
match self {
V::Int(x) => if let Some(x) = x.checked_neg() {
Ok(V::Int(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
},
V::Ratio(x) => if let Some(x) = Rational64::ZERO.checked_sub(&x) {
Ok(V::Ratio(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
}
V::Int(x) => {
if let Some(x) = x.checked_neg() {
Ok(V::Int(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
}
}
V::Ratio(x) => {
if let Some(x) = Rational64::ZERO.checked_sub(&x) {
Ok(V::Ratio(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
}
}
V::Float(x) => Ok(V::Float(-x)),
V::Complex(x) => Ok(V::Complex(-x)),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}")
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
}
}
}
@ -91,32 +109,40 @@ impl Value {
pub fn abs(self) -> Result<Self> {
use Value as V;
match self {
V::Int(x) => if let Some(x) = x.checked_abs() {
Ok(V::Int(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when finding absolute value of {self}")
},
V::Ratio(x) => if let Some((x, _)) = ratio_checked_absign(&x) {
Ok(V::Ratio(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when finding absolute value of {self}")
}
V::Int(x) => {
if let Some(x) = x.checked_abs() {
Ok(V::Int(x))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when finding absolute value of {self}"
)
}
}
V::Ratio(x) => {
if let Some((x, _)) = ratio_checked_absign(&x) {
Ok(V::Ratio(x))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when finding absolute value of {self}"
)
}
}
V::Float(x) => Ok(V::Float(x.abs())),
V::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 //
/////////////////////////
macro_rules! impl_value_arith {
($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => {
impl $trait<Value> for Value {
type Output = Result<Self>;
fn $name(self, rhs: Value) -> Self::Output {
@ -151,20 +177,23 @@ impl Div<Value> for Value {
type Output = Result<Self>;
fn div(self, rhs: Value) -> Self::Output {
use Value as V;
let (a, b) = promote(self, rhs);
let (a, b) = promote(self, rhs);
match (&a, &b) {
(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::Ratio(_), V::Ratio(r)) if r.is_zero()
=> throw!(*SYM_VALUE_ERROR, "rational division by 0"),
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.checked_div(y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when dividing {a} and {b}")
},
(V::Int(x), V::Int(y)) => Ok(Value::Ratio((*x, *y).into())),
(V::Ratio(_), V::Ratio(r)) if r.is_zero() => {
throw!(*SYM_VALUE_ERROR, "rational division by 0")
}
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = x.checked_div(y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when dividing {a} and {b}")
}
}
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}")
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}"),
}
}
}
@ -173,104 +202,115 @@ impl Div<Value> for Value {
// modulo and integer division //
///////////////////////////////////
#[inline]
fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> {
let a = if r.is_negative() {
Rational64::ZERO.checked_sub(r)?
} else {
*r
};
Some((a, r.signum()))
let a = if r.is_negative() {
Rational64::ZERO.checked_sub(r)?
} else {
*r
};
Some((a, r.signum()))
}
#[inline]
fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
let (r2_abs, r2_sgn) = ratio_checked_absign(r2)?;
Some(r1.checked_div(&r2_abs)?.floor() * r2_sgn)
let (r2_abs, r2_sgn) = ratio_checked_absign(r2)?;
Some(r1.checked_div(&r2_abs)?.floor() * r2_sgn)
}
#[inline]
fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
let q = ratio_checked_div_euclid(r1, r2)?;
r1.checked_sub(&r2.checked_mul(&q)?)
let q = ratio_checked_div_euclid(r1, r2)?;
r1.checked_sub(&r2.checked_mul(&q)?)
}
impl Value {
pub fn modulo(self, rhs: Value) -> Result<Self> {
use Value as V;
let (a, b) = promote(self, rhs);
let (a, b) = promote(self, rhs);
match (&a, &b) {
(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) {
Ok(V::Int(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
},
(V::Int(x), V::Int(y)) => {
if let Some(v) = x.checked_rem_euclid(*y) {
Ok(V::Int(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
}
}
(V::Ratio(_), V::Ratio(y)) if y.is_zero()
=> throw!(*SYM_VALUE_ERROR, "rational modulo by 0"),
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = ratio_checked_rem_euclid(x, y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
}
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "rational modulo by 0")
}
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = ratio_checked_rem_euclid(x, y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
}
}
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(*y))),
(V::Complex(x), V::Complex(y)) => {
let n = x / y;
let n = Complex64::new(n.re().floor(), n.im().floor());
Ok(Value::Complex(x - n * y))
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}")
let n = x / y;
let n = Complex64::new(n.re().floor(), n.im().floor());
Ok(Value::Complex(x - n * y))
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}"),
}
}
pub fn int_div(self, rhs: Value) -> Result<Self> {
use Value as V;
let (a, b) = promote(self, rhs);
let (a, b) = promote(self, rhs);
match (&a, &b) {
(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) {
Ok(V::Int(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when integer dividing {a} and {b}")
},
(V::Int(x), V::Int(y)) => {
if let Some(v) = x.checked_div_euclid(*y) {
Ok(V::Int(v))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when integer dividing {a} and {b}"
)
}
}
(V::Ratio(_), V::Ratio(y)) if y.is_zero()
=> throw!(*SYM_VALUE_ERROR, "integer division by 0"),
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = ratio_checked_div_euclid(x, y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when integer dividing {a} and {b}")
},
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "integer division by 0")
}
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = ratio_checked_div_euclid(x, y) {
Ok(V::Ratio(v))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when integer dividing {a} and {b}"
)
}
}
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(*y))),
(V::Complex(x), V::Complex(y)) => {
let n = x / y;
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}")
let n = x / y;
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}"),
}
}
}
//////////////////////
// exponentiation //
//////////////////////
#[inline]
fn ipow(n: i64, p: u64) -> Option<i64> {
match (n, p) {
(0, 0) => None,
(0, _) => Some(0),
(_, 0) => Some(1),
(1, _) => Some(1),
(-1, p) => (-1_i64).checked_pow((p % 2) as u32),
(1, _) => Some(1),
(-1, p) => (-1_i64).checked_pow((p % 2) as u32),
(_, p) if p > u32::MAX as u64 => None,
(n, p) => n.checked_pow(p as u32),
}
@ -279,12 +319,12 @@ fn ipow(n: i64, p: u64) -> Option<i64> {
#[inline]
fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
match p {
i64::MIN => match (n, d) {
(0, _) => Some((0, 1)),
(1, 1) => Some((1, 1)),
(-1, 1) => Some((-1, 1)),
_ => None,
}
i64::MIN => match (n, d) {
(0, _) => Some((0, 1)),
(1, 1) => Some((1, 1)),
(-1, 1) => Some((-1, 1)),
_ => None,
},
0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)),
_ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)),
}
@ -294,45 +334,54 @@ impl Value {
pub fn pow(self, rhs: Value) -> Result<Self> {
use Value as V;
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
if x.is_zero() && *y == 0 {
throw!(*SYM_VALUE_ERROR, "rational zero to integer zero power")
}
let Some(v) = rpow(*(*x).numer(), *(*x).denom(), *y) else {
throw!(*SYM_VALUE_ERROR, "overflow when raising {self} to the power {rhs}")
};
if x.is_zero() && *y == 0 {
throw!(*SYM_VALUE_ERROR, "rational zero to integer zero power")
}
let Some(v) = rpow(*(*x).numer(), *(*x).denom(), *y) else {
throw!(
*SYM_VALUE_ERROR,
"overflow when raising {self} to the power {rhs}"
)
};
return Ok(V::Ratio(v.into()))
}
let (a, b) = promote(self, rhs);
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(0), V::Int(0))
=> throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power"),
(V::Int(x), V::Int(y @ 0..)) => if let Some(v) = ipow(*x, *y as u64) {
Ok(V::Int(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when raising {a} to the power {b}")
},
(V::Int(x), V::Int(y)) => if let Some(v) = rpow(*x, 1, *y) {
Ok(V::Ratio(v.into()))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when raising {a} to the power {b}")
},
(V::Ratio(x), V::Ratio(y))
=> Ok(V::Float(x.to_f64().powf(y.to_f64()))),
(V::Float(x), V::Float(y))
=> Ok(V::Float(x.powf(*y))),
(V::Complex(x), V::Complex(y))
=> Ok(V::Complex(x.powc(*y))),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}")
(V::Int(0), V::Int(0)) => {
throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power")
}
(V::Int(x), V::Int(y @ 0..)) => {
if let Some(v) = ipow(*x, *y as u64) {
Ok(V::Int(v))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when raising {a} to the power {b}"
)
}
}
(V::Int(x), V::Int(y)) => {
if let Some(v) = rpow(*x, 1, *y) {
Ok(V::Ratio(v.into()))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when raising {a} to the power {b}"
)
}
}
(V::Ratio(x), V::Ratio(y)) => Ok(V::Float(x.to_f64().powf(y.to_f64()))),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.powf(*y))),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x.powc(*y))),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}"),
}
}
}
//////////////////////////
// Bitwise operations //
//////////////////////////
impl Shl<Value> for Value {
type Output = Result<Value>;
@ -340,7 +389,7 @@ impl Shl<Value> for Value {
use Value as V;
match (self, rhs) {
(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;
match (self, rhs) {
(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;
match (self, rhs) {
(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;
match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int(a ^ b)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}")
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
}
}
}
@ -388,47 +437,46 @@ impl BitOr<Value> for Value {
use Value as V;
match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int(a | b)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}")
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
}
}
}
/////////////////////////////
// Equality and ordering //
/////////////////////////////
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
use Value as V;
use super::range::RangeType as Rty;
use Value as V;
match (self, other) {
(V::Nil, V::Nil) => true,
(V::Bool(a), V::Bool(b)) => a == b,
(V::Int(a), V::Int(b)) => *a == *b,
(V::Ratio(a), V::Ratio(b)) => *a == *b,
(V::Float(a), V::Float(b)) => *a == *b,
(V::Nil, V::Nil) => true,
(V::Bool(a), V::Bool(b)) => a == b,
(V::Int(a), V::Int(b)) => *a == *b,
(V::Ratio(a), V::Ratio(b)) => *a == *b,
(V::Float(a), V::Float(b)) => *a == *b,
(V::Complex(a), V::Complex(b)) => *a == *b,
(V::Int(a), V::Ratio(b)) => Rational64::from(*a) == *b,
(V::Ratio(a), V::Int(b)) => *a == Rational64::from(*b),
(V::Int(a), V::Float(b)) => *a as f64 == *b,
(V::Float(a), V::Int(b)) => *a == *b as f64,
(V::Int(a), V::Complex(b)) => Complex64::from(*a as f64) == *b,
(V::Int(a), V::Ratio(b)) => Rational64::from(*a) == *b,
(V::Ratio(a), V::Int(b)) => *a == Rational64::from(*b),
(V::Int(a), V::Float(b)) => *a as f64 == *b,
(V::Float(a), V::Int(b)) => *a == *b as f64,
(V::Int(a), V::Complex(b)) => Complex64::from(*a as f64) == *b,
(V::Complex(a), V::Int(b)) => *a == Complex64::from(*b as f64),
(V::Ratio(a), V::Float(b)) => a.to_f64() == *b,
(V::Float(a), V::Ratio(b)) => *a == b.to_f64(),
(V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64()) == *b,
(V::Ratio(a), V::Float(b)) => a.to_f64() == *b,
(V::Float(a), V::Ratio(b)) => *a == b.to_f64(),
(V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64()) == *b,
(V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64()),
(V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b,
(V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b,
(V::Complex(a), V::Float(b)) => *a == Complex64::from(*b),
(V::String(a), V::String(b)) => *a == *b,
(V::List(a), V::List(b)) => *a.borrow() == *b.borrow(),
(V::Symbol(a), V::Symbol(b)) => a == b,
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
(Rty::Open, Rty::Open)
| (Rty::Closed, Rty::Closed) => a.start == b.start && a.stop == b.stop,
(V::String(a), V::String(b)) => *a == *b,
(V::List(a), V::List(b)) => *a.borrow() == *b.borrow(),
(V::Symbol(a), V::Symbol(b)) => a == b,
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
(Rty::Open, Rty::Open) | (Rty::Closed, Rty::Closed) => {
a.start == b.start && a.stop == b.stop
}
(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::Endless, Rty::Endless) => a.start == b.start,
@ -464,12 +512,10 @@ impl PartialOrd for Value {
}
}
////////////
// misc //
////////////
impl Value {
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
match self.partial_cmp(other) {
@ -485,7 +531,7 @@ impl Value {
let mut l = l1.borrow().clone();
l.extend_from_slice(&l2.borrow());
Ok(l.into())
},
}
(V::String(s1), V::String(s2)) => {
let mut s: LString = s1.as_ref().to_owned();
s.push_lstr(s2);
@ -507,23 +553,40 @@ impl Value {
let mut l = list.borrow().clone();
l.push(val);
Ok(l.into())
},
}
lhs => throw!(*SYM_TYPE_ERROR, "cannot append to {lhs:#}"),
}
}
pub fn range(&self, other: &Self, closed: bool) -> Result<Self> {
if let (Value::Int(start), Value::Int(stop)) = (self, other) {
let ty = if closed { RangeType::Closed } else { RangeType::Open };
Ok(Range { start: *start, stop: *stop, ty }.into())
let ty = if closed {
RangeType::Closed
} else {
RangeType::Open
};
Ok(Range {
start: *start,
stop: *stop,
ty,
}
.into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot create range between {self:#} and {other:#}")
throw!(
*SYM_TYPE_ERROR,
"cannot create range between {self:#} and {other:#}"
)
}
}
pub fn range_endless(&self) -> Result<Self> {
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 {
throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}")
}
@ -543,7 +606,7 @@ impl Value {
pub fn iter_pack(v: Option<Self>) -> Self {
match 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) => {
let range_iter = RefCell::new(range.into_iter());
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
Ok(Value::iter_pack(range_iter.borrow_mut().next()
.map(Value::from)))
Ok(Value::iter_pack(
range_iter.borrow_mut().next().map(Value::from),
))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[range]")).into())
},
}
Self::String(s) => {
let byte_pos = RefCell::new(0);
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())
},
}
Self::List(list) => {
let idx = RefCell::new(0);
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())
},
}
Self::Table(table) => {
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
let keys = RefCell::new(keys.into_iter());
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
Ok(Value::iter_pack(keys.borrow_mut().next()
.map(HashValue::into_inner)))
Ok(Value::iter_pack(
keys.borrow_mut().next().map(HashValue::into_inner),
))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[table]")).into())
},
}
_ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"),
}
}

View file

@ -1,7 +1,8 @@
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RangeType {
Open, Closed, Endless,
Open,
Closed,
Endless,
}
#[derive(Clone, Copy, Debug)]
@ -31,7 +32,7 @@ impl Range {
impl IntoIterator for Range {
type Item = i64;
type IntoIter = Box<dyn Iterator<Item=i64>>;
type IntoIter = Box<dyn Iterator<Item = i64>>;
fn into_iter(self) -> Self::IntoIter {
match self.ty {
RangeType::Open => Box::new(self.start..self.stop),
@ -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 {
func: Rc<Function>,
@ -78,25 +96,28 @@ fn get_call_outcome(args: Vec<Value>) -> Result<CallOutcome> {
throw!(*SYM_TYPE_ERROR, "cannot call non-function {f:#}")
};
let argc = args.len() - 1;
match argc.cmp(&attrs.arity) {
Ordering::Equal => Ok(CallOutcome::Call(args)),
Ordering::Greater => throw!(*SYM_TYPE_ERROR, "too many arguments for function"),
Ordering::Less => {
let remaining = attrs.arity - argc;
let f = f.clone();
let nf = move |vm: &mut Vm, inner_args: Vec<Value>| {
let mut ia = inner_args.into_iter();
ia.next();
let args: Vec<Value> = args.clone().into_iter().chain(ia).collect();
vm.call_value(f.clone(), args)
};
let nf = NativeFunc {
attrs: FuncAttrs { arity: remaining, name: None },
func: Box::new(nf),
};
Ok(CallOutcome::Partial(nf.into()))
}
}
match argc.cmp(&attrs.arity) {
Ordering::Equal => Ok(CallOutcome::Call(args)),
Ordering::Greater => throw!(*SYM_TYPE_ERROR, "too many arguments for function"),
Ordering::Less => {
let remaining = attrs.arity - argc;
let f = f.clone();
let nf = move |vm: &mut Vm, inner_args: Vec<Value>| {
let mut ia = inner_args.into_iter();
ia.next();
let args: Vec<Value> = args.clone().into_iter().chain(ia).collect();
vm.call_value(f.clone(), args)
};
let nf = NativeFunc {
attrs: FuncAttrs {
arity: remaining,
name: None,
},
func: Box::new(nf),
};
Ok(CallOutcome::Partial(nf.into()))
}
}
}
impl Vm {
@ -119,7 +140,9 @@ impl Vm {
}
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);
}
@ -138,8 +161,8 @@ impl Vm {
CallOutcome::Call(args) => match value {
Value::Function(f) => self.run_function(f, args),
Value::NativeFunc(f) => (f.func)(self, args),
_ => unreachable!("already verified by calling get_call_type")
}
_ => unreachable!("already verified by calling get_call_type"),
},
}
}
@ -175,7 +198,7 @@ impl Vm {
while let Some(try_frame) = frame.try_frames.pop() {
let table = &frame.func.chunk.try_tables[try_frame.idx];
for catch in &table.catches {
if catch.types.is_none() || catch.types.as_ref().unwrap().contains(&exc.ty) {
if catch.types.is_none() || catch.types.as_ref().unwrap().contains(&exc.ty) {
frame.ip = catch.addr;
frame.locals.truncate(table.local_count);
self.stack.truncate(try_frame.stack_len);
@ -209,7 +232,10 @@ impl Vm {
}
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)
}
Ok(())
@ -222,17 +248,13 @@ impl Vm {
// do nothing
I::Nop => (),
// [] -> [locals[n]]
I::LoadLocal(n)
=> self.push(frame.locals[usize::from(n)].clone()),
I::LoadLocal(n) => self.push(frame.locals[usize::from(n)].clone()),
// [x] -> [], locals[n] = x
I::StoreLocal(n)
=> frame.locals[usize::from(n)] = self.pop(),
I::StoreLocal(n) => frame.locals[usize::from(n)] = self.pop(),
// [x] -> [], locals.push(x)
I::NewLocal
=> frame.locals.push(self.pop()),
I::NewLocal => frame.locals.push(self.pop()),
// locals.pop_n(n)
I::DropLocal(n)
=> frame.locals.truncate(frame.locals.len() - usize::from(n)),
I::DropLocal(n) => frame.locals.truncate(frame.locals.len() - usize::from(n)),
// [] -> [globals[s]]
I::LoadGlobal(s) => {
let sym = unsafe { s.to_symbol_unchecked() };
@ -241,65 +263,67 @@ impl Vm {
None => throw!(*SYM_NAME_ERROR, "undefined global {}", sym.name()),
};
self.push(v);
},
}
// [x] -> [], globals[s] = x
I::StoreGlobal(s) => {
let sym = unsafe { s.to_symbol_unchecked() };
let v = self.pop();
self.globals.insert(sym, v);
},
I::CloseOver(n) => {
let n = usize::from(n);
let v = std::mem::replace(&mut frame.locals[n], Value::Nil);
let v = v.to_cell();
frame.locals[n] = v.clone();
self.push(v);
},
I::Closure(n) => {
let f = frame.func.chunk.consts[usize::from(n)].clone();
let Value::Function(f) = f else {
panic!("attempt to build closure from non-closure constant")
};
let mut f = f.as_ref().clone();
}
I::CloseOver(n) => {
let n = usize::from(n);
let v = std::mem::replace(&mut frame.locals[n], Value::Nil);
let v = v.to_cell();
frame.locals[n] = v.clone();
self.push(v);
}
I::Closure(n) => {
let f = frame.func.chunk.consts[usize::from(n)].clone();
let Value::Function(f) = f else {
panic!("attempt to build closure from non-closure constant")
};
let mut f = f.as_ref().clone();
let captured: Vec<_> = self.pop_n(f.state.len()).into_iter()
.map(|v| {
let Value::Cell(v) = v else {
panic!("attempt to build closure from non-cell local");
};
v
}).collect();
let captured: Vec<_> = self
.pop_n(f.state.len())
.into_iter()
.map(|v| {
let Value::Cell(v) = v else {
panic!("attempt to build closure from non-cell local");
};
v
})
.collect();
f.state = captured.into_boxed_slice();
self.push(f.into());
},
I::LoadUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
self.push(v.borrow().clone());
},
I::StoreUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
*v.borrow_mut() = self.pop();
},
I::ContinueUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
self.push(Value::Cell(v));
},
I::LoadClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to load from closed non-cell local");
};
f.state = captured.into_boxed_slice();
self.push(f.into());
}
I::LoadUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
self.push(v.borrow().clone());
}
I::StoreUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
*v.borrow_mut() = self.pop();
}
I::ContinueUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
self.push(Value::Cell(v));
}
I::LoadClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to load from closed non-cell local");
};
self.push(c.borrow().clone());
},
I::StoreClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to store to closed non-cell local");
};
*c.borrow_mut() = self.pop();
},
}
I::StoreClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to store to closed non-cell local");
};
*c.borrow_mut() = self.pop();
}
// [] -> [consts[n]]
I::Const(n)
=> self.push(frame.func.chunk.consts[usize::from(n)].clone()),
I::Const(n) => self.push(frame.func.chunk.consts[usize::from(n)].clone()),
// [] -> [nil]
I::Nil => self.push(Value::Nil),
// [] -> [b]
@ -308,7 +332,7 @@ impl Vm {
I::Symbol(s) => {
let sym = unsafe { Symbol::from_id_unchecked(u32::from(s)) };
self.push(Value::Symbol(sym));
},
}
// [] -> [n]
I::Int(n) => self.push(Value::Int(i64::from(n))),
// [x] -> [x,x]
@ -317,38 +341,44 @@ impl Vm {
I::DupTwo => {
self.push(self.stack[self.stack.len() - 2].clone());
self.push(self.stack[self.stack.len() - 2].clone());
},
}
// [a0,a1...an] -> []
I::Drop(n) => for _ in 0..u32::from(n) { self.pop(); },
I::Drop(n) => {
for _ in 0..u32::from(n) {
self.pop();
}
}
// [x,y] -> [y,x]
I::Swap => {
let len = self.stack.len();
self.stack.swap(len - 1, len - 2);
},
}
// [x,y] -> [y op x]
I::BinaryOp(op) => {
let b = self.pop();
let a = self.pop();
self.push(binary_op(op, a, b)?);
},
}
// [x] -> [op x]
I::UnaryOp(op) => {
let a = self.pop();
self.push(unary_op(op, a)?);
},
}
// [a0,a1...an] -.> [[a0,a1...an]]
I::NewList(n) => {
let list = self.pop_n(n as usize);
self.push(list.into());
},
}
// [l,a0,a1...an] -.> [l ++ [a0,a1...an]]
I::GrowList(n) => {
let ext = self.pop_n(n as usize);
let list = self.pop();
let Value::List(list) = list else { panic!("not a list") };
let Value::List(list) = list else {
panic!("not a list")
};
list.borrow_mut().extend(ext);
self.push(Value::List(list));
},
}
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
I::NewTable(n) => {
let mut table = HashMap::new();
@ -358,12 +388,14 @@ impl Vm {
table.insert(k.try_into()?, v);
}
self.push(table.into());
},
}
// [t,k0,v0...kn,vn] -> [t ++ {k0=v0...kn=vn}]
I::GrowTable(n) => {
let mut ext = self.pop_n(2 * n as usize);
let table = self.pop();
let Value::Table(table) = table else { panic!("not a table") };
let Value::Table(table) = table else {
panic!("not a table")
};
let mut table_ref = table.borrow_mut();
for _ in 0..n {
// can't panic: pop_n checked that ext would have len 2*n
@ -373,13 +405,13 @@ impl Vm {
}
drop(table_ref);
self.push(Value::Table(table));
},
}
// [ct, idx] -> [ct!idx]
I::Index => {
let idx = self.pop();
let ct = self.pop();
self.push(ct.index(idx)?);
},
}
// [ct, idx, v] -> [v], ct!idx = v
I::StoreIndex => {
let v = self.pop();
@ -387,27 +419,31 @@ impl Vm {
let ct = self.pop();
ct.store_index(self, idx, v.clone())?;
self.push(v);
},
}
// ip = n
I::Jump(n) => {
self.check_interrupt()?;
frame.ip = usize::from(n);
},
}
// [v] ->, [], if v then ip = n
I::JumpTrue(n) => if self.pop().truthy() {
self.check_interrupt()?;
frame.ip = usize::from(n);
},
I::JumpTrue(n) => {
if self.pop().truthy() {
self.check_interrupt()?;
frame.ip = usize::from(n);
}
}
// [v] ->, [], if not v then ip = n
I::JumpFalse(n) => if !self.pop().truthy() {
self.check_interrupt()?;
frame.ip = usize::from(n);
},
I::JumpFalse(n) => {
if !self.pop().truthy() {
self.check_interrupt()?;
frame.ip = usize::from(n);
}
}
// [v] -> [iter(v)]
I::IterBegin => {
let iter = self.pop().to_iter_function()?;
self.push(iter);
},
}
// [i,cell(v)] -> [i,v]
// [i,nil] -> [], ip = n
I::IterTest(n) => {
@ -417,19 +453,19 @@ impl Vm {
self.pop();
frame.ip = usize::from(n);
}
},
}
// try_frames.push(t, stack.len())
I::BeginTry(t) => {
let tryframe = TryFrame {
idx: usize::from(t),
stack_len: self.stack.len()
stack_len: self.stack.len(),
};
frame.try_frames.push(tryframe);
},
}
// try_frames.pop()
I::EndTry => {
frame.try_frames.pop().expect("no try to pop");
},
}
// [f,a0,a1...an] -> [f(a0,a1...an)]
I::Call(n) => {
let n = usize::from(n);
@ -471,7 +507,6 @@ impl Vm {
self.push(res);
} else if let Value::Function(func) = &args[0] {
if self.call_stack.len() + 1 >= self.stack_max {
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
}
@ -482,22 +517,18 @@ impl Vm {
} else {
unreachable!("already verified by calling get_call_type");
}
},
}
// [v] -> [], return v
I::Return if frame.root => {
return Ok(Some(self.pop()));
},
I::Return if frame.root => return Ok(Some(self.pop())),
// [v] -> [], return v
I::Return => {
self.check_interrupt()?;
*frame = self.call_stack.pop().expect("no root frame");
},
}
}
Ok(None)
}
}
#[macro_export]

View file

@ -4,5 +4,5 @@ mod native_func;
#[proc_macro_attribute]
pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream {
native_func::native_func(input, annotated_item)
native_func::native_func(input, annotated_item)
}

View file

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

View file

@ -1,11 +1,16 @@
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 crate::unpack_args;
pub fn load(vm: &mut Vm) {
vm.set_global_name("push", push().into());
vm.set_global_name("pop", pop().into());
@ -55,7 +60,10 @@ pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match &col {
Value::List(list) => list.borrow_mut().clear(),
Value::Table(table) => table.borrow_mut().clear(),
_ => throw!(*SYM_TYPE_ERROR, "clear expected list or table, found {col:#}")
_ => throw!(
*SYM_TYPE_ERROR,
"clear expected list or table, found {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> {
let ord = vmcall!(vm; by, l, r)?;
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)
}
@ -82,14 +93,14 @@ fn partition(vals: &mut [Value]) -> (usize, usize) {
vals.swap(eq, lt);
lt += 1;
eq += 1;
},
}
Some(Ordering::Greater) => {
vals.swap(eq, gt);
gt -= 1;
},
}
Some(Ordering::Equal) | None => {
eq += 1;
},
}
}
}
@ -110,14 +121,14 @@ fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, u
vals.swap(eq, lt);
lt += 1;
eq += 1;
},
}
1.. => {
vals.swap(eq, gt);
gt -= 1;
},
}
0 => {
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]) {
for i in 0..vals.len() {
let mut j = i;
while j > 0 && vals[j-1] > vals[j] {
vals.swap(j, j-1);
while j > 0 && vals[j - 1] > vals[j] {
vals.swap(j, 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() {
let mut j = i;
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 {
break;
break
}
vals.swap(j, j-1);
vals.swap(j, j - 1);
j -= 1;
}
}
@ -165,7 +176,7 @@ fn sort_inner(vals: &mut [Value], by: Option<&Value>, vm: &mut Vm) -> Result<()>
None => partition(vals),
};
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)]
@ -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::Equal) => Ok(Value::Int(0)),
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();
@ -212,32 +226,32 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(2)]
pub fn pair(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
Ok(vec![x, y].into())
let [_, x, y] = unpack_args!(args);
Ok(vec![x, y].into())
}
#[native_func(1)]
pub fn fst(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "fst: expected list")
};
let l = l.borrow();
let [x, _] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "fst: list must have length 2")
};
Ok(x.clone())
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "fst: expected list")
};
let l = l.borrow();
let [x, _] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "fst: list must have length 2")
};
Ok(x.clone())
}
#[native_func(1)]
pub fn snd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "snd: expected list")
};
let l = l.borrow();
let [_, y] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "snd: list must have length 2")
};
Ok(y.clone())
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "snd: expected list")
};
let l = l.borrow();
let [_, y] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "snd: list must have length 2")
};
Ok(y.clone())
}

View file

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

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 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};
@ -10,7 +26,6 @@ type OpenOptFn = for<'a> fn(&'a mut OpenOptions, bool) -> &'a mut OpenOptions;
lazy_static! {
static ref SYM_STD_FILE: Symbol = Symbol::get("std.file");
static ref SYM_STD_PROCESS: Symbol = Symbol::get("std.process");
static ref SYM_R: Symbol = Symbol::get("r");
static ref SYM_W: Symbol = Symbol::get("w");
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_N: Symbol = Symbol::get("n");
static ref SYM_U: Symbol = Symbol::get("u");
static ref OPEN_OPT_MAP: HashMap<Symbol, OpenOptFn> = {
let mut map = HashMap::new();
map.insert(*SYM_R, OpenOptions::read as OpenOptFn);
@ -29,21 +43,17 @@ lazy_static! {
map.insert(*SYM_N, OpenOptions::create_new);
map
};
static ref SYM_STDIN: Symbol = Symbol::get("stdin");
static ref SYM_STDOUT: Symbol = Symbol::get("stdout");
static ref SYM_STDERR: Symbol = Symbol::get("stderr");
static ref SYM_CD: Symbol = Symbol::get("cd");
static ref SYM_DELENV: Symbol = Symbol::get("delenv");
static ref SYM_ENV: Symbol = Symbol::get("env");
static ref SYM_PROCESS: Symbol = Symbol::get("process");
static ref SYM_INHERIT: Symbol = Symbol::get("inherit");
static ref SYM_PIPED: Symbol = Symbol::get("piped");
static ref SYM_NULL: Symbol = Symbol::get("null");
static ref SYM_ALL: Symbol = Symbol::get("all");
}
thread_local! {
@ -69,15 +79,23 @@ thread_local! {
#[derive(Debug)]
enum BufFile {
Buffered { r: BufReader<File>, w: BufWriter<File> },
Unbuffered { f: File },
Buffered {
r: BufReader<File>,
w: BufWriter<File>,
},
Unbuffered {
f: File,
},
}
impl BufFile {
fn new(file: File, buffered: bool) -> std::io::Result<Self> {
if buffered {
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 {
Ok(Self::Unbuffered { f: file })
}
@ -92,8 +110,8 @@ impl BufFile {
Self::Buffered { r, .. } => {
let file = r.get_ref().try_clone()?;
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 {
fn get_type(&self) -> Symbol { *SYM_STD_FILE }
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<()> {
fn get_type(&self) -> Symbol {
*SYM_STD_FILE
}
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>");
Ok(())
}
@ -170,9 +197,18 @@ impl From<Child> for ValueProcess {
}
impl NativeValue for ValueProcess {
fn get_type(&self) -> Symbol { *SYM_STD_PROCESS }
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<()> {
fn get_type(&self) -> Symbol {
*SYM_STD_PROCESS
}
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();
write!(w, "<process {id}>")
}
@ -201,7 +237,6 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("process_id", process_id().into());
}
#[native_func(2)]
pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, path, opts] = unpack_args!(args);
@ -218,33 +253,35 @@ pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} else {
match OPEN_OPT_MAP.get(&s) {
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() {
let Value::Symbol(s) = s else {
throw!(*SYM_TYPE_ERROR, "invalid option for open")
};
if *s == *SYM_U {
buffered = false;
} else {
match OPEN_OPT_MAP.get(s) {
Some(f) => f(&mut oo, true),
None => throw!(*SYM_VALUE_ERROR, "invalid option for open")
}
Value::List(l) => {
for s in l.borrow().iter() {
let Value::Symbol(s) = s else {
throw!(*SYM_TYPE_ERROR, "invalid option for open")
};
if *s == *SYM_U {
buffered = false;
} else {
match OPEN_OPT_MAP.get(s) {
Some(f) => f(&mut oo, true),
None => throw!(*SYM_VALUE_ERROR, "invalid option for open"),
};
}
}
},
}
Value::Nil => {
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()) {
Ok(f) => match BufFile::new(f, buffered) {
Ok(bf) => Ok(ValueFile::from(bf).into()),
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:#}")
};
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 {
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()) {
Ok(s) if s.is_empty() => Ok(Value::Nil),
Ok(s) => Ok(s.into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}")
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
} else {
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) {
Ok(0) => Ok(Value::Nil),
Ok(_) => Ok(LString::from(buf).into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}")
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
} else {
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(bf) => Ok(ValueFile::from(bf).into()),
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> {
let [_, addr, timeout] = unpack_args!(args);
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 {
throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
};
let timeout = match timeout {
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::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:#}")
Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => {
Duration::from_secs_f64(n)
}
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() {
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(bf) => Ok(ValueFile::from(bf).into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
},
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
}
fn tcp_listen_inner(listener: TcpListener) -> Value {
let listener = RefCell::new(listener);
let func = move |_: &mut Vm, _: Vec<Value>| {
match listener.borrow_mut().accept() {
Ok((stream, addr)) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
Ok(bf) => Ok(vec![
ValueFile::from(bf).into(),
LString::from(addr.to_string()).into()
].into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
let func = move |_: &mut Vm, _: Vec<Value>| match listener.borrow_mut().accept() {
Ok((stream, addr)) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
Ok(bf) => Ok(vec![
ValueFile::from(bf).into(),
LString::from(addr.to_string()).into(),
]
.into()),
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()
}
@ -468,14 +517,12 @@ pub fn tcp_listen(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
};
match TcpListener::bind(addr) {
Ok(listener) => {
let addr = listener.local_addr()
let addr = listener
.local_addr()
.map(|a| LString::from(a.to_string()).into())
.unwrap_or(Value::Nil);
Ok(vec![
tcp_listen_inner(listener),
addr,
].into())
},
Ok(vec![tcp_listen_inner(listener), addr].into())
}
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
}
@ -499,24 +546,32 @@ fn spawn_opt_stdio(
let f: &ValueFile = opt.downcast_native().unwrap();
let bf = match f.0.borrow().try_clone() {
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() {
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) };
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(())
}
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 {
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());
Ok(())
@ -527,16 +582,22 @@ fn spawn_opt_delenv(fname: &str, proc: &mut Command, delenv: &Value) -> Result<(
Value::Nil => (),
Value::Symbol(s) if *s == *SYM_ALL => {
proc.env_clear();
},
Value::List(l) => for e in l.borrow().iter() {
let Value::String(e) = e else {
throw!(*SYM_TYPE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}")
};
proc.env_remove(e.to_os_str());
},
_ => throw!(*SYM_VALUE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}")
}
Value::List(l) => {
for e in l.borrow().iter() {
let Value::String(e) = e else {
throw!(
*SYM_TYPE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}"
)
};
proc.env_remove(e.to_os_str());
}
}
_ => throw!(
*SYM_VALUE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}"
),
}
Ok(())
}
@ -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<()> {
match env {
Value::Nil => (),
Value::Table(t) => for (k, v) in t.borrow().iter() {
let Value::String(k) = k.inner() else {
throw!(*SYM_TYPE_ERROR,
"{fname} env option expected table with string keys, got {env:#}")
};
proc.env(k.to_os_str(), v.str().to_os_str());
},
_ => throw!(*SYM_TYPE_ERROR,
"{fname} env option expected table with string keys, got {env:#}")
Value::Table(t) => {
for (k, v) in t.borrow().iter() {
let Value::String(k) = k.inner() else {
throw!(
*SYM_TYPE_ERROR,
"{fname} env option expected table with string keys, got {env:#}"
)
};
proc.env(k.to_os_str(), v.str().to_os_str());
}
}
_ => throw!(
*SYM_TYPE_ERROR,
"{fname} env option expected table with string keys, got {env:#}"
),
}
Ok(())
}
@ -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> {
let (i, o, e) = match opts {
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) => {
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)?;
}
let i = t.get(&HashValue::from(*SYM_STDIN))
let i = t
.get(&HashValue::from(*SYM_STDIN))
.cloned()
.unwrap_or_else(|| Value::from(*SYM_INHERIT));
let o = t.get(&HashValue::from(*SYM_STDOUT))
let o = t
.get(&HashValue::from(*SYM_STDOUT))
.cloned()
.unwrap_or_else(|| Value::from(*SYM_INHERIT));
let e = t.get(&HashValue::from(*SYM_STDERR))
let e = t
.get(&HashValue::from(*SYM_STDERR))
.cloned()
.unwrap_or_else(|| Value::from(*SYM_INHERIT));
(i, o, e)
},
}
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_PROCESS), ValueProcess::from(child).into());
table.insert(
HashValue::from(*SYM_PROCESS),
ValueProcess::from(child).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:#}")
};
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());
for arg in args.borrow().iter() {
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());
}
@ -653,12 +735,10 @@ pub fn system(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut proc;
if cfg!(target_os = "windows") {
proc = Command::new("cmd");
proc.arg("/C")
.arg(cmd.to_os_str())
proc.arg("/C").arg(cmd.to_os_str())
} else {
proc = Command::new("sh");
proc.arg("-c")
.arg(cmd.to_os_str())
proc.arg("-c").arg(cmd.to_os_str())
};
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();
match proc.wait() {
Ok(code) => {
Ok(code.code()
.map(|c| Value::Int(c as i64))
.unwrap_or_default())
},
Ok(code) => Ok(code
.code()
.map(|c| Value::Int(c as i64))
.unwrap_or_default()),
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();
Ok((proc.id() as i64).into())
}

View file

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

View file

@ -1,6 +1,12 @@
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 crate::unpack_args;
@ -95,9 +101,7 @@ pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let v = RefCell::new(Some(val));
let f = move |_: &mut Vm, _| {
Ok(Value::iter_pack(v.borrow_mut().take()))
};
let f = move |_: &mut Vm, _| Ok(Value::iter_pack(v.borrow_mut().take()));
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> {
let [_, val] = unpack_args!(args);
let f = move |_: &mut Vm, _| {
Ok(val.clone())
};
let f = move |_: &mut Vm, _| Ok(val.clone());
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
//
#[native_func(2)]
pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, map, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| {
match vmcalliter!(vm; iter.clone())? {
Some(val) => Ok(vmcall!(vm; map.clone(), val)?),
None => Ok(Value::iter_pack(None)),
}
let f = move |vm: &mut Vm, _| match vmcalliter!(vm; iter.clone())? {
Some(val) => Ok(vmcall!(vm; map.clone(), val)?),
None => Ok(Value::iter_pack(None)),
};
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 iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| {
match vmcalliter!(vm; iter.clone())? {
Some(val) => {
vmcall!(vm; tee.clone(), val.clone())?;
Ok(val)
}
None => Ok(Value::iter_pack(None)),
let f = move |vm: &mut Vm, _| match vmcalliter!(vm; iter.clone())? {
Some(val) => {
vmcall!(vm; tee.clone(), val.clone())?;
Ok(val)
}
None => Ok(Value::iter_pack(None)),
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[tee]")).into())
}
@ -171,15 +167,13 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, filter, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| {
loop {
let Some(next) = vmcalliter!(vm; iter.clone())? else {
return Ok(Value::iter_pack(None))
};
let res = vmcall!(vm; filter.clone(), next.clone())?;
if res.truthy() {
return Ok(next)
}
let f = move |vm: &mut Vm, _| loop {
let Some(next) = vmcalliter!(vm; iter.clone())? else {
return Ok(Value::iter_pack(None))
};
let res = vmcall!(vm; filter.clone(), next.clone())?;
if res.truthy() {
return Ok(next)
}
};
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:#}")
};
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()?;
@ -218,7 +215,10 @@ pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}")
};
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()?;
@ -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())
}
#[native_func(1)]
pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
@ -292,7 +291,10 @@ pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[derive(Clone, Copy, Default)]
enum Step {
First, Going, #[default] End,
First,
Going,
#[default]
End,
}
#[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:#}")
};
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);
@ -314,9 +319,9 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*state.borrow_mut() = Step::Going;
}
Ok(Value::iter_pack(res))
},
}
Step::Going => {
for _ in 0..(by-1) {
for _ in 0..(by - 1) {
if vmcall!(vm; iter.clone())? == Value::Nil {
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;
Ok(x)
},
}
Step::End => Ok(Value::Nil),
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[step]")).into())
@ -347,33 +352,30 @@ pub fn rev(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*lst.borrow_mut() = Some(l);
}
Ok(Value::iter_pack(lst.borrow_mut().as_mut().unwrap().pop()))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[rev]")).into())
}
//
// join
//
#[native_func(2)]
pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, i1, i2] = unpack_args!(args);
let i1 = i1.to_iter_function()?;
let i2 = i2.to_iter_function()?;
let i1 = i1.to_iter_function()?;
let i2 = i2.to_iter_function()?;
let f = move |vm: &mut Vm, _| {
let mut res = Vec::with_capacity(2);
match vmcalliter!(vm; i1.clone())? {
Some(v) => res.push(v),
None => return Ok(Value::iter_pack(None)),
};
match vmcalliter!(vm; i2.clone())? {
Some(v) => res.push(v),
None => return Ok(Value::iter_pack(None)),
};
match vmcalliter!(vm; i1.clone())? {
Some(v) => res.push(v),
None => return Ok(Value::iter_pack(None)),
};
match vmcalliter!(vm; i2.clone())? {
Some(v) => res.push(v),
None => return Ok(Value::iter_pack(None)),
};
Ok(Value::from(res))
};
@ -383,12 +385,14 @@ pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)]
pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let Value::List(args) = args else {
let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "zipn expected list, got {args:#}")
};
let iters = args.borrow().iter()
.map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?;
};
let iters = args
.borrow()
.iter()
.map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?;
let f = move |vm: &mut Vm, _| {
let mut res = Vec::with_capacity(iters.len());
@ -407,19 +411,19 @@ pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(2)]
pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, i1, i2] = unpack_args!(args);
let i1 = i1.to_iter_function()?;
let i2 = i2.to_iter_function()?;
let i1 = i1.to_iter_function()?;
let i2 = i2.to_iter_function()?;
let state = RefCell::new((false, false));
let f = move |vm: &mut Vm, _| {
let mut s = state.borrow_mut();
if s.1 {
return Ok(Value::iter_pack(None));
return Ok(Value::iter_pack(None))
}
let n = s.0;
s.0 = !s.0;
drop(s);
let iter = if n { i1.clone() } else { i2.clone() };
let iter = if n { i1.clone() } else { i2.clone() };
if let Some(v) = vmcalliter!(vm; iter)? {
Ok(v)
} else {
@ -434,18 +438,20 @@ pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)]
pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let Value::List(args) = args else {
let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "alternaten expected list, got {args:#}")
};
let iters = args.borrow().iter()
.map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?;
};
let iters = args
.borrow()
.iter()
.map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?;
let state = RefCell::new((0, false));
let f = move |vm: &mut Vm, _| {
let mut s = state.borrow_mut();
if s.1 {
return Ok(Value::iter_pack(None));
return Ok(Value::iter_pack(None))
}
let n = s.0;
s.0 = (s.0 + 1) % iters.len();
@ -463,7 +469,11 @@ pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[derive(Default)]
enum Intersperse {
Init, Waiting, HasNext(Value), #[default] End
Init,
Waiting,
HasNext(Value),
#[default]
End,
}
#[native_func(2)]
@ -472,38 +482,35 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter = iter.to_iter_function()?;
let state = RefCell::new(Intersperse::Init);
let f = move |vm: &mut Vm, _| {
match state.take() {
Intersperse::Init => {
if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::Waiting;
Ok(v)
} else {
*state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None))
}
},
Intersperse::Waiting => {
if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::HasNext(v);
Ok(val.clone())
} else {
*state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None))
}
},
Intersperse::HasNext(v) => {
let f = move |vm: &mut Vm, _| match state.take() {
Intersperse::Init => {
if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::Waiting;
Ok(v)
},
Intersperse::End => Ok(Value::iter_pack(None)),
} else {
*state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None))
}
}
Intersperse::Waiting => {
if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::HasNext(v);
Ok(val.clone())
} else {
*state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None))
}
}
Intersperse::HasNext(v) => {
*state.borrow_mut() = Intersperse::Waiting;
Ok(v)
}
Intersperse::End => Ok(Value::iter_pack(None)),
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[intersperse]")).into())
}
#[native_func(2)]
pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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())
}
//
// end iteration
//
#[native_func(1)]
pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
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();
while let Some(value) = vmcalliter!(vm; iter.clone())? {
result.push(value);
};
}
Ok(result.into())
}
@ -617,16 +620,23 @@ pub fn table(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut result = HashMap::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? {
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();
if l.len() != 2 {
throw!(*SYM_VALUE_ERROR, "table: iterator yielded list of length {} (expected 2)", l.len())
if l.len() != 2 {
throw!(
*SYM_VALUE_ERROR,
"table: iterator yielded list of length {} (expected 2)",
l.len()
)
};
let v = l.pop().unwrap();
let k = l.pop().unwrap();
let v = l.pop().unwrap();
let k = l.pop().unwrap();
result.insert(k.try_into()?, v);
};
}
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::List(l) => return Ok((l.borrow().len() as i64).into()),
Value::Table(t) => return Ok((t.borrow().len() as i64).into()),
Value::Range(r) if r.ty != RangeType::Endless
=> return Ok((r.len().unwrap() as i64).into()),
Value::Range(r) if r.ty != RangeType::Endless => {
return Ok((r.len().unwrap() as i64).into())
}
_ => (),
}
let iter = value.to_iter_function()?;
let mut len = 0;
while vmcalliter!(vm; iter.clone())?.is_some() {
len += 1;
};
}
Ok(len.into())
}
@ -764,8 +775,10 @@ pub fn contains(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for _ in 0.. {
match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil),
Some(v) => if v == val {
return Ok(true.into())
Some(v) => {
if v == val {
return Ok(true.into())
}
}
}
}
@ -780,8 +793,10 @@ pub fn index_of(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for i in 0.. {
match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil),
Some(v) => if v == val {
return Ok(i.into())
Some(v) => {
if v == val {
return Ok(i.into())
}
}
}
}
@ -796,8 +811,10 @@ pub fn index_if(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for i in 0.. {
match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil),
Some(v) => if vmcall!(vm; func.clone(), v)?.truthy() {
return Ok(i.into())
Some(v) => {
if vmcall!(vm; func.clone(), v)?.truthy() {
return Ok(i.into())
}
}
}
}
@ -844,25 +861,25 @@ pub fn mean(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter = iter.to_iter_function()?;
let mut sum = Value::Float(0.0);
let mut count = Value::Int(0);
let mut count = Value::Int(0);
while let Some(value) = vmcalliter!(vm; iter.clone())? {
sum = (sum + value)?;
count = (count + Value::from(1))?;
count = (count + Value::from(1))?;
}
sum / count
}
fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> {
let mut m = Value::Float(0.0);
let mut s = Value::Float(0.0);
let mut k = 1;
let mut s = Value::Float(0.0);
let mut k = 1;
while let Some(value) = vmcalliter!(vm; iter.clone())? {
let old_m = m.clone();
m = (m.clone() + ((value.clone() - m.clone())?/Value::Int(k))?)?;
s = (s + ((value.clone() - m.clone())?*(value - old_m)?)?)?;
k += 1;
let old_m = m.clone();
m = (m.clone() + ((value.clone() - m.clone())? / Value::Int(k))?)?;
s = (s + ((value.clone() - m.clone())? * (value - old_m)?)?)?;
k += 1;
}
s / Value::Int(k - if pop { 1 } else { 2 })
s / Value::Int(k - if pop { 1 } else { 2 })
}
#[native_func(1)]
@ -870,7 +887,7 @@ pub fn variance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
variance_inner(vm, iter, false)
variance_inner(vm, iter, false)
}
#[native_func(1)]
@ -878,14 +895,14 @@ pub fn stdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let v = variance_inner(vm, iter, false)?;
Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}")
})
let v = variance_inner(vm, iter, false)?;
Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
})
}
#[native_func(1)]
@ -893,7 +910,7 @@ pub fn pvariance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
variance_inner(vm, iter, true)
variance_inner(vm, iter, true)
}
#[native_func(1)]
@ -901,14 +918,14 @@ pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let v = variance_inner(vm, iter, true)?;
Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}")
})
let v = variance_inner(vm, iter, true)?;
Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
})
}
#[derive(PartialEq, PartialOrd)]
@ -917,9 +934,9 @@ impl std::cmp::Eq for OrdValue {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl std::cmp::Ord for OrdValue {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Less)
}
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Less)
}
}
#[native_func(1)]
@ -927,23 +944,22 @@ pub fn median(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let mut vals = Vec::new();
let mut vals = Vec::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? {
vals.push(OrdValue(value));
vals.push(OrdValue(value));
}
let count = vals.len();
if count == 0 {
Ok(Value::Nil)
} else if count % 2 == 0 {
let (_, _, hi) = vals.select_nth_unstable(count / 2 - 1);
let (_, _, _) = hi.select_nth_unstable(0);
let m2 = vals.swap_remove(count / 2);
let m1 = vals.swap_remove(count / 2 - 1);
(m1.0 + m2.0)? / Value::Int(2)
} else {
let (_, _, _) = vals.select_nth_unstable(count / 2);
let m = vals.swap_remove(count / 2);
m.0 / Value::Int(1)
}
let count = vals.len();
if count == 0 {
Ok(Value::Nil)
} else if count % 2 == 0 {
let (_, _, hi) = vals.select_nth_unstable(count/2 - 1);
let (_, _, _) = hi.select_nth_unstable(0);
let m2 = vals.swap_remove(count/2);
let m1 = vals.swap_remove(count/2 - 1);
(m1.0 + m2.0)? / Value::Int(2)
} else {
let (_, _, _) = vals.select_nth_unstable(count/2);
let m = vals.swap_remove(count/2);
m.0 / Value::Int(1)
}
}

View file

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

View file

@ -1,7 +1,14 @@
use std::cmp::Ordering;
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 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("NaN", (f64::NAN).into());
vm.set_global_name("infi", Complex64::new(0.0,f64::INFINITY).into());
vm.set_global_name("NaNi", Complex64::new(0.0,f64::NAN).into());
vm.set_global_name("infi", Complex64::new(0.0, f64::INFINITY).into());
vm.set_global_name("NaNi", Complex64::new(0.0, f64::NAN).into());
vm.set_global_name("bin", bin().into());
vm.set_global_name("sex", sex().into());
@ -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> {
let [_, x, radix] = unpack_args!(args);
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
throw!(*SYM_TYPE_ERROR, "to_radix expected integer arguments, got {x:#} and {radix:#}")
throw!(
*SYM_TYPE_ERROR,
"to_radix expected integer arguments, got {x:#} and {radix:#}"
)
};
if *radix < 2 || *radix > 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> {
let [_, x, radix] = unpack_args!(args);
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 {
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())
}
@ -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> {
let [_, s, radix] = unpack_args!(args);
let (Value::String(s), Value::Int(radix)) = (&s, &radix) else {
throw!(*SYM_TYPE_ERROR, "from_radix expected string and integer arguments, got {s:#} and {radix:#}")
throw!(
*SYM_TYPE_ERROR,
"from_radix expected string and integer arguments, got {s:#} and {radix:#}"
)
};
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) {
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 {
assert!(n >= 0, "isqrt input should be nonnegative");
if n < 2 { return n }
if n < 2 {
return n
}
let mut c = 0;
let mut d = 1 << 62;
@ -217,8 +244,7 @@ fn isqrt_inner(mut n: i64) -> i64 {
if n >= c + d {
n -= c + d;
c = (c >> 1) + d;
}
else {
} else {
c >>= 1;
}
d >>= 2;
@ -242,23 +268,25 @@ pub fn gcd_inner(a: i64, b: i64) -> i64 {
let z = az.min(bz);
loop {
if a > b {
if a > b {
std::mem::swap(&mut a, &mut b);
}
b -= a;
if b == 0 {
return a << z;
return a << z
}
b >>= b.trailing_zeros();
}
}
#[native_func(1)]
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "isqrt expected integer argument, got {x:#}")
throw!(
*SYM_TYPE_ERROR,
"isqrt expected integer argument, got {x:#}"
)
};
if x < 0 {
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())
}
#[native_func(1)]
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "isprime expected integer argument, got {x:#}")
throw!(
*SYM_TYPE_ERROR,
"isprime expected integer argument, got {x:#}"
)
};
if x < 2 {
return Ok(false.into())
@ -286,9 +316,13 @@ pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
let lim = isqrt_inner(x);
let mut i = 12;
while i <= lim+1 {
if x % (i - 1) == 0 { return Ok(false.into()) }
if x % (i + 1) == 0 { return Ok(false.into()) }
while i <= lim + 1 {
if x % (i - 1) == 0 {
return Ok(false.into())
}
if x % (i + 1) == 0 {
return Ok(false.into())
}
i += 6;
}
Ok(true.into())
@ -309,11 +343,11 @@ pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)]
pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?;
let args = args.to_iter_function()?;
let mut g = 0;
let mut g = 0;
while let Some(a) = vmcalliter!(vm; args.clone())? {
while let Some(a) = vmcalliter!(vm; args.clone())? {
let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}")
};
@ -333,41 +367,46 @@ pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}")
};
let g = gcd_inner(x, y);
if g == 0 {
Ok(Value::from(0))
} else {
(Value::from(x)/Value::from(g))? * Value::from(y)
}
if g == 0 {
Ok(Value::from(0))
} else {
(Value::from(x) / Value::from(g))? * Value::from(y)
}
}
#[native_func(1)]
pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?;
let args = args.to_iter_function()?;
let mut l = 1;
let mut l = 1;
while let Some(a) = vmcalliter!(vm; args.clone())? {
while let Some(a) = vmcalliter!(vm; args.clone())? {
let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
};
let g = gcd_inner(l, a);
if g == 0 { return Ok(Value::from(0)) };
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?;
let Value::Int(new_l) = new_l else {
if g == 0 {
return Ok(Value::from(0))
};
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?;
let Value::Int(new_l) = new_l else {
unreachable!("int//int * int != int")
};
l = new_l;
};
l = new_l;
}
Ok(Value::from(l))
Ok(Value::from(l))
}
#[native_func(1)]
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(mut x) = x else {
throw!(*SYM_TYPE_ERROR, "factors expected integer argument, got {x:#}")
throw!(
*SYM_TYPE_ERROR,
"factors expected integer argument, got {x:#}"
)
};
let mut factors = Vec::new();
if x <= 1 {
@ -382,7 +421,7 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
factors.push(Value::Int(3));
}
let mut i = 5;
while x >= i*i {
while x >= i * i {
while x % i == 0 {
x /= i;
factors.push(Value::Int(i));
@ -401,49 +440,53 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
fn totient_prime(n: &mut u64, p: u64) -> u64 {
if *n % p != 0 {
return 1
}
*n /= p;
let mut v = p - 1;
while *n % p == 0 {
*n /= p;
v *= p;
}
v
if *n % p != 0 {
return 1
}
*n /= p;
let mut v = p - 1;
while *n % p == 0 {
*n /= p;
v *= p;
}
v
}
#[native_func(1)]
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "totient expected integer argument, got {x:#}")
throw!(
*SYM_TYPE_ERROR,
"totient expected integer argument, got {x:#}"
)
};
if x <= 1 {
return Ok(1.into())
}
let mut x = x as u64;
let mut x = x as u64;
let mut totient = 1;
if x & 1 == 0 { x >>= 1; }
if x & 1 == 0 {
x >>= 1;
}
while x & 1 == 0 {
x >>= 1;
totient <<= 1;
totient <<= 1;
}
totient *= totient_prime(&mut x, 3);
totient *= totient_prime(&mut x, 3);
let mut i = 5;
while x >= i*i {
totient *= totient_prime(&mut x, i);
while x >= i * i {
totient *= totient_prime(&mut x, i);
i += 2;
totient *= totient_prime(&mut x, i);
totient *= totient_prime(&mut x, i);
i += 4;
}
if x > 1 {
totient *= x - 1;
totient *= x - 1;
}
Ok((totient as i64).into())
}
//
// numeric operations
//
@ -451,21 +494,21 @@ pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(2)]
pub fn min(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
if y < x {
Ok(y)
} else {
Ok(x)
}
if y < x {
Ok(y)
} else {
Ok(x)
}
}
#[native_func(2)]
pub fn max(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
if y > x {
Ok(y)
} else {
Ok(x)
}
if y > x {
Ok(y)
} else {
Ok(x)
}
}
#[native_func(1)]
@ -533,7 +576,7 @@ pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ordering::Greater => Ok(Value::Ratio(1.into())),
Ordering::Less => Ok(Value::Ratio((-1).into())),
Ordering::Equal => Ok(Value::Ratio(0.into())),
}
},
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
//
#[native_func(1)]
pub fn signum(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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 = to_floaty(x);
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() {
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::Subnormal => *SYM_SUBNORMAL,
std::num::FpCategory::Normal => *SYM_NORMAL,
}.into())
}
.into())
}
#[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::Float(x) => Ok(x.is_nan().into()),
Value::Complex(z) => Ok(z.is_nan().into()),
v => throw!(*SYM_TYPE_ERROR, "isnan expected numeric argument, got {v:#}"),
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::Float(x) => Ok(x.is_finite().into()),
Value::Complex(z) => Ok(z.is_finite().into()),
v => throw!(*SYM_TYPE_ERROR, "isfinite expected numeric argument, got {v:#}"),
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::Float(x) => Ok(x.is_infinite().into()),
Value::Complex(z) => Ok(z.is_infinite().into()),
v => throw!(*SYM_TYPE_ERROR, "isinfinite expected numeric argument, got {v:#}"),
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> {
let [_, val] = unpack_args!(args);
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))
}
@ -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> {
let [_, val] = unpack_args!(args);
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)))
}
//
// rational operations
//
#[native_func(1)]
pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args);
match v {
Value::Int(x) => Ok(Value::Int(x)),
Value::Ratio(x) => Ok(Value::Int(*x.numer())),
v => throw!(*SYM_TYPE_ERROR, "numer expected rational argument, got {v:#}"),
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 {
Value::Int(_) => Ok(Value::Int(1)),
Value::Ratio(x) => Ok(Value::Int(*x.denom())),
v => throw!(*SYM_TYPE_ERROR, "denom expected rational argument, got {v:#}"),
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
//
#[native_func(1)]
pub fn re(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args);
@ -688,7 +750,7 @@ pub fn arg(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)]
pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
x.abs()
x.abs()
}
#[native_func(1)]
@ -699,17 +761,17 @@ pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Ratio(_) => x.clone() * x,
Value::Float(_) => x.clone() * x,
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
//
macro_rules! float_func {
($name:ident) => {
#[native_func(1)]
@ -718,8 +780,11 @@ macro_rules! float_func {
match to_floaty(v) {
Value::Float(x) => Ok(Value::Float(x.$name())),
Value::Complex(z) => Ok(Value::Complex(z.$name())),
v => throw!(*SYM_TYPE_ERROR,
"{} expected numeric argument, got {v:#}", stringify!($name)),
v => throw!(
*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> {
let [_, y, x] = unpack_args!(args);
match (to_floaty(y), to_floaty(x)) {
(Value::Float(y), Value::Float(x))
=> Ok(Value::Float(x.atan2(y))),
(y,x) => throw!(*SYM_TYPE_ERROR,
"atan2 expected real arguments, got {y:#} and {x:#}"),
(Value::Float(y), Value::Float(x)) => Ok(Value::Float(x.atan2(y))),
(y, x) => throw!(
*SYM_TYPE_ERROR,
"atan2 expected real arguments, got {y:#} and {x:#}"
),
}
}

View file

@ -1,5 +1,11 @@
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 crate::unpack_args;
@ -21,11 +27,11 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
match col {
Value::List(l) => {
let l = l.borrow();
let Some(v) = l.choose(&mut rand::thread_rng()) else {
let Some(v) = l.choose(&mut rand::thread_rng()) else {
throw!(*SYM_VALUE_ERROR, "rand_in: empty list")
};
Ok(v.clone())
},
}
Value::Table(t) => {
let t = t.borrow();
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 key = t.keys().nth(i).unwrap();
Ok(key.clone().into_inner())
},
}
Value::Range(r) => {
if r.is_empty() {
throw!(*SYM_VALUE_ERROR, "rand_in: empty range")
}
match r.ty {
RangeType::Open => Ok(Value::Int(
rand::thread_rng().gen_range(r.start..r.stop))),
RangeType::Closed => Ok(Value::Int(
rand::thread_rng().gen_range(r.start..=r.stop))),
RangeType::Open => Ok(Value::Int(rand::thread_rng().gen_range(r.start..r.stop))),
RangeType::Closed => Ok(Value::Int(rand::thread_rng().gen_range(r.start..=r.stop))),
RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"),
}
}

View file

@ -1,9 +1,16 @@
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 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;
@ -18,17 +25,30 @@ lazy_static! {
pub struct ValueRegex(Regex);
impl From<Regex> for ValueRegex {
fn from(value: Regex) -> Self { Self(value) }
fn from(value: Regex) -> Self {
Self(value)
}
}
impl From<ValueRegex> for Regex {
fn from(value: ValueRegex) -> Self { value.0 }
fn from(value: ValueRegex) -> Self {
value.0
}
}
impl NativeValue for ValueRegex {
fn get_type(&self) -> Symbol { *SYM_STD_REGEX }
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<()> {
fn get_type(&self) -> Symbol {
*SYM_STD_REGEX
}
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;
if repr {
write!(w, "/{}/", self.0)
@ -58,7 +78,10 @@ fn match_to_value(m: Match) -> Value {
Value::new_table(|t| {
t.insert((*SYM_START).into(), (m.start() 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)
.map(Cow::Owned)
.map_err(|e| exception!(*SYM_VALUE_ERROR, "invalid regex: {e}"))
},
Value::Native(n) if n.get_type() == *SYM_STD_REGEX => {
n.as_any().downcast_ref::<ValueRegex>()
.map(|vr| Cow::Borrowed(&vr.0))
.ok_or_else(|| exception!(
*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}"))
},
_ => throw!(*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}")
}
Value::Native(n) if n.get_type() == *SYM_STD_REGEX => n
.as_any()
.downcast_ref::<ValueRegex>()
.map(|vr| Cow::Borrowed(&vr.0))
.ok_or_else(|| {
exception!(
*SYM_TYPE_ERROR,
"{name} expected string or regex, got {v:#}"
)
}),
_ => throw!(
*SYM_TYPE_ERROR,
"{name} expected string or regex, got {v:#}"
),
}
}
#[native_func(1)]
pub fn _regex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, re] = unpack_args!(args);
regex_from(&re, "regex")
.map(|re| ValueRegex(re.into_owned()).into())
regex_from(&re, "regex").map(|re| ValueRegex(re.into_owned()).into())
}
#[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")
};
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)]
@ -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")
};
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)]
@ -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:#}")
};
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 {
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:#}")
};
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 {
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 (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(),
);
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")
};
let re = regex_from(&re, "split")?;
let parts: Vec<Value> = re.split(s)
.map(|s| LString::from(s).into())
.collect();
let parts: Vec<Value> = re.split(s).map(|s| LString::from(s).into()).collect();
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 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> {
let [_, s] = unpack_args!(args);
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))
}
@ -92,8 +102,10 @@ pub fn starts_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let res = match (d, pre) {
(Value::List(d), Value::List(pre)) => d.borrow().starts_with(&pre.borrow()),
(Value::String(d), Value::String(pre)) => d.starts_with(&pre),
(d, pre) => throw!(*SYM_TYPE_ERROR,
"starts_with expected two lists or strings, got {d:#} and {pre:#}")
(d, pre) => throw!(
*SYM_TYPE_ERROR,
"starts_with expected two lists or strings, got {d:#} and {pre:#}"
),
};
Ok(res.into())
}
@ -104,8 +116,10 @@ pub fn ends_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let res = match (d, suf) {
(Value::List(d), Value::List(suf)) => d.borrow().ends_with(&suf.borrow()),
(Value::String(d), Value::String(suf)) => d.ends_with(&suf),
(d, suf) => throw!(*SYM_TYPE_ERROR,
"ends_with expected two lists or strings, got {d:#} and {suf:#}")
(d, suf) => throw!(
*SYM_TYPE_ERROR,
"ends_with expected two lists or strings, got {d:#} and {suf:#}"
),
};
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> {
let [_, s] = unpack_args!(args);
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())
}
@ -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> {
let [_, s] = unpack_args!(args);
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() {
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> {
let [_, s] = unpack_args!(args);
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())
}
@ -145,25 +168,37 @@ pub fn to_utf8_lossy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn str_to_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args);
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()
.iter()
.map(|v| (*v as i64).into())
.collect::<Vec<Value>>()
.into())
.iter()
.map(|v| (*v as i64).into())
.collect::<Vec<Value>>()
.into())
}
#[native_func(1)]
pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, b] = unpack_args!(args);
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 {
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"),
}).collect::<Result<Vec<u8>>>()?;
_ => throw!(
*SYM_VALUE_ERROR,
"str_of_bytes expected list of integers in 0..=255"
),
})
.collect::<Result<Vec<u8>>>()?;
Ok(LString::from(bytes).into())
}

View file

@ -1,6 +1,14 @@
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 crate::unpack_args;
@ -34,7 +42,6 @@ pub fn load(vm: &mut Vm) {
// types
//
#[native_func(1, "type")]
pub fn type_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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::Float(x), b"int") => Ok(Value::Int(x as i64)),
(Value::Float(x), b"ratio") => {
let r = Rational64::approximate_float(x)
.ok_or_else(|| exception!(*SYM_VALUE_ERROR, "float {x:?} could not be converted to ratio"))?;
let r = Rational64::approximate_float(x).ok_or_else(|| {
exception!(
*SYM_VALUE_ERROR,
"float {x:?} could not be converted to ratio"
)
})?;
Ok(Value::Ratio(r))
}
(Value::Float(x), b"complex") => Ok(Value::Complex(x.into())),
(Value::String(s), b"int")
=> parse_int(s.as_ref(), 10)
(Value::String(s), b"int") => parse_int(s.as_ref(), 10)
.map(i64::into)
.map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as integer")),
(Value::String(s), b"float")
=> parse_float(s.as_ref())
(Value::String(s), b"float") => parse_float(s.as_ref())
.map(f64::into)
.map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as float")),
(v, _) => throw!(*SYM_TYPE_ERROR,
"cannot convert value of type {} to type {}", v.get_type().name(), ty.name())
(v, _) => throw!(
*SYM_TYPE_ERROR,
"cannot convert value of type {} to type {}",
v.get_type().name(),
ty.name()
),
}
}
pub fn copy_inner(value: Value) -> Result<Value> {
match value {
Value::Nil | Value::Bool(_) | Value::Symbol(_)
| Value::Int(_) | Value::Ratio(_) | Value::Float(_)
| Value::Complex(_) | Value::Range(_) | Value::String(_)
=> Ok(value),
Value::Nil
| Value::Bool(_)
| Value::Symbol(_)
| Value::Int(_)
| Value::Ratio(_)
| Value::Float(_)
| Value::Complex(_)
| Value::Range(_)
| Value::String(_) => Ok(value),
Value::Cell(c) => {
let c = Rc::unwrap_or_clone(c).take();
let c = copy_inner(c)?;
Ok(RefCell::new(c).into())
},
}
Value::List(l) => {
let l = Rc::unwrap_or_clone(l).take();
let v: Result<Vec<Value>> = l.into_iter()
.map(copy_inner)
.collect();
let v: Result<Vec<Value>> = l.into_iter().map(copy_inner).collect();
Ok(v?.into())
},
}
Value::Table(t) => {
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)))
.collect();
Ok(v?.into())
},
}
Value::Native(ref n) => match n.copy_value()? {
Some(x) => Ok(x),
None => throw!(*SYM_TYPE_ERROR,
"cannot copy value of type {}", value.get_type().name())
}
_ => throw!(*SYM_TYPE_ERROR,
"cannot copy value of type {}", value.get_type().name())
None => throw!(
*SYM_TYPE_ERROR,
"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
//
#[native_func(1, "str")]
pub fn str_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
@ -160,38 +182,37 @@ pub fn repr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)]
pub fn symbol_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::Symbol(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_name: expected symbol")
};
let Value::Symbol(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_name: expected symbol")
};
Ok(s.name().into())
}
#[native_func(1)]
pub fn symbol_of(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::String(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
};
let Value::String(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
};
Ok(Symbol::get(s.as_ref()).into())
}
#[native_func(1)]
pub fn symbol_exists(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::String(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
};
let Value::String(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
};
match Symbol::try_get(s.as_ref()) {
Some(s) => Ok(s.into()),
None => Ok(Value::Nil),
}
Some(s) => Ok(s.into()),
None => Ok(Value::Nil),
}
}
//
// cells
//
#[native_func(1)]
pub fn cell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, value] = unpack_args!(args);
@ -232,66 +253,78 @@ pub fn cell_take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)]
pub fn func_state(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
match func {
Value::NativeFunc(_) => Ok(Value::Nil),
Value::Function(f) => {
let l: Vec<Value> = f.state.iter()
.map(|v| Value::Cell(v.clone()))
.collect();
Ok(l.into())
}
_ => throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a talc function")
}
match func {
Value::NativeFunc(_) => Ok(Value::Nil),
Value::Function(f) => {
let l: Vec<Value> = f.state.iter().map(|v| Value::Cell(v.clone())).collect();
Ok(l.into())
}
_ => throw!(
*SYM_TYPE_ERROR,
"closure_state: {func:#} is not a talc function"
),
}
}
#[native_func(1)]
pub fn func_arity(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
let Some(attrs) = func.func_attrs() else {
let Some(attrs) = func.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
};
Ok((attrs.arity as i64).into())
Ok((attrs.arity as i64).into())
}
#[native_func(1)]
pub fn func_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
let Some(attrs) = func.func_attrs() else {
let Some(attrs) = func.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
};
if let Some(name) = attrs.name {
Ok(name.into())
} else {
Ok(Value::Nil)
}
if let Some(name) = attrs.name {
Ok(name.into())
} else {
Ok(Value::Nil)
}
}
#[native_func(2)]
pub fn apply(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func, lst] = unpack_args!(args);
if func.func_attrs().is_none() {
throw!(*SYM_TYPE_ERROR, "apply: first argument must be a function, found {func:#}")
}
let Value::List(l) = lst else {
throw!(*SYM_TYPE_ERROR, "apply: second argument must be a list, found {lst:#}")
};
let mut args = l.borrow().clone();
args.insert(0, func.clone());
vm.call_value(func, args)
if func.func_attrs().is_none() {
throw!(
*SYM_TYPE_ERROR,
"apply: first argument must be a function, found {func:#}"
)
}
let Value::List(l) = lst else {
throw!(
*SYM_TYPE_ERROR,
"apply: second argument must be a list, found {lst:#}"
)
};
let mut args = l.borrow().clone();
args.insert(0, func.clone());
vm.call_value(func, args)
}
#[native_func(1)]
pub fn compile(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, src] = unpack_args!(args);
let Value::String(src) = src else {
throw!(*SYM_TYPE_ERROR, "compile: argument must be a string, found {src:#}")
};
let src = src.to_str()
.map_err(|e| exception!(*SYM_VALUE_ERROR, "compile: argument must be valid unicode ({e})"))?;
let ast = talc_lang::parser::parse(src)
.map_err(|e| exception!(symbol!("parse_error"), "{e}"))?;
let func = talc_lang::compiler::compile(&ast, None);
Ok(func.into())
let Value::String(src) = src else {
throw!(
*SYM_TYPE_ERROR,
"compile: argument must be a string, found {src:#}"
)
};
let src = src.to_str().map_err(|e| {
exception!(
*SYM_VALUE_ERROR,
"compile: argument must be valid unicode ({e})"
)
})?;
let ast =
talc_lang::parser::parse(src).map_err(|e| exception!(symbol!("parse_error"), "{e}"))?;
let func = talc_lang::compiler::compile(&ast, None);
Ok(func.into())
}