rustfmt
This commit is contained in:
parent
801aaf7b78
commit
ba7db1e91b
37 changed files with 4123 additions and 3667 deletions
3
format.sh
Executable file
3
format.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
cargo +nightly fmt
|
10
rustfmt.toml
Normal file
10
rustfmt.toml
Normal 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
|
||||
|
|
@ -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::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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 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 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 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 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_local(&mut self, name: Symbol) -> usize {
|
||||
let n = self.declare_local(name);
|
||||
self.emit(I::NewLocal);
|
||||
n
|
||||
}
|
||||
fn assign_local(&mut self, name: Symbol) -> usize {
|
||||
let n = self.declare_local(name);
|
||||
self.emit(I::NewLocal);
|
||||
n
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>));
|
||||
|
||||
|
|
|
@ -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
|
@ -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
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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:#}")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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,29 +109,37 @@ 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) => {
|
||||
|
||||
|
@ -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:#}"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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:#}"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue