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 std::{borrow::Cow, cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use rustyline::{completion::Completer, highlight::Highlighter, hint::Hinter, validate::{ValidationContext, ValidationResult, Validator}, Helper, Result};
|
use rustyline::{
|
||||||
use talc_lang::{lstring::LStr, parser::{Lexer, Pos, Span, TokenKind}, Vm};
|
completion::Completer,
|
||||||
|
highlight::Highlighter,
|
||||||
|
hint::Hinter,
|
||||||
|
validate::{ValidationContext, ValidationResult, Validator},
|
||||||
|
Helper, Result,
|
||||||
|
};
|
||||||
|
use talc_lang::{
|
||||||
|
lstring::LStr,
|
||||||
|
parser::{Lexer, Pos, Span, TokenKind},
|
||||||
|
Vm,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct TalcHelper {
|
pub struct TalcHelper {
|
||||||
vm: Rc<RefCell<Vm>>,
|
vm: Rc<RefCell<Vm>>,
|
||||||
|
@ -9,9 +19,7 @@ pub struct TalcHelper {
|
||||||
|
|
||||||
impl TalcHelper {
|
impl TalcHelper {
|
||||||
pub fn new(vm: Rc<RefCell<Vm>>) -> Self {
|
pub fn new(vm: Rc<RefCell<Vm>>) -> Self {
|
||||||
Self {
|
Self { vm }
|
||||||
vm,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +43,11 @@ impl Completer for TalcHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let res: String = res.chars().rev().collect();
|
let res: String = res.chars().rev().collect();
|
||||||
let mut keys = self.vm.borrow().globals().keys()
|
let mut keys = self
|
||||||
|
.vm
|
||||||
|
.borrow()
|
||||||
|
.globals()
|
||||||
|
.keys()
|
||||||
.map(|sym| sym.name())
|
.map(|sym| sym.name())
|
||||||
.filter(|name| name.starts_with(LStr::from_str(&res)))
|
.filter(|name| name.starts_with(LStr::from_str(&res)))
|
||||||
.map(LStr::to_string)
|
.map(LStr::to_string)
|
||||||
|
@ -55,23 +67,27 @@ impl Highlighter for TalcHelper {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
let mut last = Pos::new();
|
let mut last = Pos::new();
|
||||||
while let Some(Ok(token)) = lexer.next() {
|
while let Some(Ok(token)) = lexer.next() {
|
||||||
if token.kind == TokenKind::Eof { break }
|
if token.kind == TokenKind::Eof {
|
||||||
|
break
|
||||||
|
}
|
||||||
buf += Span::new(last, token.span.start).of(line);
|
buf += Span::new(last, token.span.start).of(line);
|
||||||
last = token.span.end;
|
last = token.span.end;
|
||||||
let format = match token.kind {
|
let format = match token.kind {
|
||||||
TokenKind::Nil
|
TokenKind::Nil
|
||||||
| TokenKind::True
|
| TokenKind::True
|
||||||
| TokenKind::False
|
| TokenKind::False
|
||||||
| TokenKind::Integer
|
| TokenKind::Integer
|
||||||
| TokenKind::Float
|
| TokenKind::Float
|
||||||
| TokenKind::Imaginary => "\x1b[93m",
|
| TokenKind::Imaginary => "\x1b[93m",
|
||||||
TokenKind::String => "\x1b[92m",
|
TokenKind::String => "\x1b[92m",
|
||||||
TokenKind::Symbol => "\x1b[96m",
|
TokenKind::Symbol => "\x1b[96m",
|
||||||
_ => "",
|
_ => "",
|
||||||
};
|
};
|
||||||
buf += format;
|
buf += format;
|
||||||
buf += token.content;
|
buf += token.content;
|
||||||
if !format.is_empty() { buf += "\x1b[0m" }
|
if !format.is_empty() {
|
||||||
|
buf += "\x1b[0m"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buf += &line[(last.idx as usize)..];
|
buf += &line[(last.idx as usize)..];
|
||||||
Cow::Owned(buf)
|
Cow::Owned(buf)
|
||||||
|
@ -96,73 +112,92 @@ impl Highlighter for TalcHelper {
|
||||||
|
|
||||||
impl Validator for TalcHelper {
|
impl Validator for TalcHelper {
|
||||||
fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
|
fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
|
||||||
use TokenKind as K;
|
use TokenKind as K;
|
||||||
let lexer = Lexer::new(ctx.input());
|
let lexer = Lexer::new(ctx.input());
|
||||||
let mut delims = Vec::new();
|
let mut delims = Vec::new();
|
||||||
let mut mismatch = None;
|
let mut mismatch = None;
|
||||||
for token in lexer {
|
for token in lexer {
|
||||||
let token = match token {
|
let token = match token {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => return Ok(ValidationResult::Invalid(Some(format!(" {e}")))),
|
||||||
return Ok(ValidationResult::Invalid(
|
|
||||||
Some(format!(" {e}"))))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let k = token.kind;
|
let k = token.kind;
|
||||||
let s = token.span;
|
let s = token.span;
|
||||||
match k {
|
match k {
|
||||||
K::Eof => break,
|
K::Eof => break,
|
||||||
K::LParen
|
K::LParen | K::LBrack | K::LBrace | K::If | K::While | K::For | K::Try => {
|
||||||
| K::LBrack
|
delims.push(token.kind)
|
||||||
| K::LBrace
|
}
|
||||||
| K::If
|
|
||||||
| K::While
|
|
||||||
| K::For
|
|
||||||
| K::Try
|
|
||||||
=> delims.push(token.kind),
|
|
||||||
K::RParen => match delims.pop() {
|
K::RParen => match delims.pop() {
|
||||||
Some(K::LParen) => (),
|
Some(K::LParen) => (),
|
||||||
v => { mismatch = Some((v, k, s)); break }
|
v => {
|
||||||
|
mismatch = Some((v, k, s));
|
||||||
|
break
|
||||||
|
}
|
||||||
},
|
},
|
||||||
K::RBrack => match delims.pop() {
|
K::RBrack => match delims.pop() {
|
||||||
Some(K::LBrack) => (),
|
Some(K::LBrack) => (),
|
||||||
v => { mismatch = Some((v, k, s)); break }
|
v => {
|
||||||
|
mismatch = Some((v, k, s));
|
||||||
|
break
|
||||||
|
}
|
||||||
},
|
},
|
||||||
K::RBrace => match delims.pop() {
|
K::RBrace => match delims.pop() {
|
||||||
Some(K::LBrace) => (),
|
Some(K::LBrace) => (),
|
||||||
v => { mismatch = Some((v, k, s)); break }
|
v => {
|
||||||
|
mismatch = Some((v, k, s));
|
||||||
|
break
|
||||||
|
}
|
||||||
},
|
},
|
||||||
K::Then => match delims.pop() {
|
K::Then => match delims.pop() {
|
||||||
Some(K::If | K::Elif) => delims.push(k),
|
Some(K::If | K::Elif) => delims.push(k),
|
||||||
v => { mismatch = Some((v, k, s)); break }
|
v => {
|
||||||
}
|
mismatch = Some((v, k, s));
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
K::Catch => match delims.pop() {
|
K::Catch => match delims.pop() {
|
||||||
Some(K::Try) => delims.push(k),
|
Some(K::Try) => delims.push(k),
|
||||||
v => { mismatch = Some((v, k, s)); break }
|
v => {
|
||||||
}
|
mismatch = Some((v, k, s));
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
K::Do => match delims.last().copied() {
|
K::Do => match delims.last().copied() {
|
||||||
Some(K::While | K::For | K::Catch) => {
|
Some(K::While | K::For | K::Catch) => {
|
||||||
delims.pop();
|
delims.pop();
|
||||||
delims.push(k);
|
delims.push(k);
|
||||||
},
|
}
|
||||||
_ => delims.push(k)
|
_ => delims.push(k),
|
||||||
},
|
},
|
||||||
K::Elif | K::Else => match delims.pop() {
|
K::Elif | K::Else => match delims.pop() {
|
||||||
Some(K::Then) => delims.push(k),
|
Some(K::Then) => delims.push(k),
|
||||||
v => { mismatch = Some((v, k, s)); break }
|
v => {
|
||||||
|
mismatch = Some((v, k, s));
|
||||||
|
break
|
||||||
|
}
|
||||||
},
|
},
|
||||||
K::End => match delims.pop() {
|
K::End => match delims.pop() {
|
||||||
Some(K::Then | K::Else | K::Do | K::Try | K::Catch) => (),
|
Some(K::Then | K::Else | K::Do | K::Try | K::Catch) => (),
|
||||||
v => { mismatch = Some((v, k, s)); break }
|
v => {
|
||||||
|
mismatch = Some((v, k, s));
|
||||||
|
break
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match mismatch {
|
match mismatch {
|
||||||
Some((None, b, s)) => return Ok(ValidationResult::Invalid(Some(
|
Some((None, b, s)) => {
|
||||||
format!(" found unmatched {b} at {s}")))),
|
return Ok(ValidationResult::Invalid(Some(format!(
|
||||||
Some((Some(a), b, s)) => return Ok(ValidationResult::Invalid(Some(
|
" found unmatched {b} at {s}"
|
||||||
format!(" found {a} matched with {b} at {s}")))),
|
))))
|
||||||
|
}
|
||||||
|
Some((Some(a), b, s)) => {
|
||||||
|
return Ok(ValidationResult::Invalid(Some(format!(
|
||||||
|
" found {a} matched with {b} at {s}"
|
||||||
|
))))
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +206,5 @@ impl Validator for TalcHelper {
|
||||||
} else {
|
} else {
|
||||||
Ok(ValidationResult::Incomplete)
|
Ok(ValidationResult::Incomplete)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use clap::{ColorChoice, Parser};
|
use clap::{ColorChoice, Parser};
|
||||||
use talc_lang::{compiler::compile, parser, symbol::Symbol, value::function::disasm_recursive, Vm};
|
|
||||||
use std::{path::PathBuf, process::ExitCode, rc::Rc};
|
use std::{path::PathBuf, process::ExitCode, rc::Rc};
|
||||||
|
use talc_lang::{compiler::compile, parser, symbol::Symbol, value::function::disasm_recursive, Vm};
|
||||||
|
|
||||||
mod repl;
|
|
||||||
mod helper;
|
mod helper;
|
||||||
|
mod repl;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
|
@ -20,11 +20,11 @@ struct Args {
|
||||||
disasm: bool,
|
disasm: bool,
|
||||||
|
|
||||||
/// show disassembled bytecode
|
/// show disassembled bytecode
|
||||||
#[arg(short='H', long)]
|
#[arg(short = 'H', long)]
|
||||||
histfile: Option<PathBuf>,
|
histfile: Option<PathBuf>,
|
||||||
|
|
||||||
/// enable or disable color
|
/// enable or disable color
|
||||||
#[arg(short, long, default_value="auto")]
|
#[arg(short, long, default_value = "auto")]
|
||||||
color: ColorChoice,
|
color: ColorChoice,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error: {e}");
|
eprintln!("Error: {e}");
|
||||||
return ExitCode::FAILURE
|
return ExitCode::FAILURE
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let func = Rc::new(compile(&ex, Some(name)));
|
let func = Rc::new(compile(&ex, Some(name)));
|
||||||
|
@ -64,7 +64,7 @@ fn main() -> ExitCode {
|
||||||
return repl::repl(&args)
|
return repl::repl(&args)
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = args.file.as_ref().unwrap();
|
let file = args.file.as_ref().unwrap();
|
||||||
|
|
||||||
match std::fs::read_to_string(file) {
|
match std::fs::read_to_string(file) {
|
||||||
Ok(s) => exec(Symbol::get(file.as_os_str()), &s, &args),
|
Ok(s) => exec(Symbol::get(file.as_os_str()), &s, &args),
|
||||||
|
|
|
@ -1,8 +1,18 @@
|
||||||
use std::{cell::RefCell, io::IsTerminal, process::ExitCode, rc::Rc};
|
use std::{cell::RefCell, io::IsTerminal, process::ExitCode, rc::Rc};
|
||||||
|
|
||||||
use clap::ColorChoice;
|
use clap::ColorChoice;
|
||||||
use rustyline::{error::ReadlineError, history::{FileHistory, History}, ColorMode, Config, Editor};
|
use rustyline::{
|
||||||
use talc_lang::{compiler::compile_repl, parser, symbol::Symbol, value::{function::disasm_recursive, Value}, Vm};
|
error::ReadlineError,
|
||||||
|
history::{FileHistory, History},
|
||||||
|
ColorMode, Config, Editor,
|
||||||
|
};
|
||||||
|
use talc_lang::{
|
||||||
|
compiler::compile_repl,
|
||||||
|
parser,
|
||||||
|
symbol::Symbol,
|
||||||
|
value::{function::disasm_recursive, Value},
|
||||||
|
Vm,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{helper::TalcHelper, Args};
|
use crate::{helper::TalcHelper, Args};
|
||||||
|
|
||||||
|
@ -30,7 +40,6 @@ impl ReplColors {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn get_colmode(args: &Args) -> ColorMode {
|
fn get_colmode(args: &Args) -> ColorMode {
|
||||||
match args.color {
|
match args.color {
|
||||||
ColorChoice::Auto => ColorMode::Enabled,
|
ColorChoice::Auto => ColorMode::Enabled,
|
||||||
|
@ -45,7 +54,8 @@ pub fn init_rustyline(args: &Args) -> Result<Editor<TalcHelper, FileHistory>, Ex
|
||||||
.color_mode(get_colmode(args))
|
.color_mode(get_colmode(args))
|
||||||
.check_cursor_position(true)
|
.check_cursor_position(true)
|
||||||
.completion_type(rustyline::CompletionType::List)
|
.completion_type(rustyline::CompletionType::List)
|
||||||
.max_history_size(4096).unwrap()
|
.max_history_size(4096)
|
||||||
|
.unwrap()
|
||||||
.build();
|
.build();
|
||||||
let mut hist = FileHistory::default();
|
let mut hist = FileHistory::default();
|
||||||
if let Some(f) = &args.histfile {
|
if let Some(f) = &args.histfile {
|
||||||
|
@ -60,12 +70,12 @@ pub fn init_rustyline(args: &Args) -> Result<Editor<TalcHelper, FileHistory>, Ex
|
||||||
Err(ReadlineError::Io(e)) => {
|
Err(ReadlineError::Io(e)) => {
|
||||||
eprintln!("Error creating repl: {e}");
|
eprintln!("Error creating repl: {e}");
|
||||||
Err(ExitCode::FAILURE)
|
Err(ExitCode::FAILURE)
|
||||||
},
|
}
|
||||||
Err(ReadlineError::Errno(e)) => {
|
Err(ReadlineError::Errno(e)) => {
|
||||||
eprintln!("Error creating repl: {e}");
|
eprintln!("Error creating repl: {e}");
|
||||||
Err(ExitCode::FAILURE)
|
Err(ExitCode::FAILURE)
|
||||||
},
|
}
|
||||||
Err(_) => Err(ExitCode::SUCCESS)
|
Err(_) => Err(ExitCode::SUCCESS),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +127,7 @@ pub fn repl(args: &Args) -> ExitCode {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}Error:{} {e}", c.error, c.reset);
|
eprintln!("{}Error:{} {e}", c.error, c.reset);
|
||||||
continue
|
continue
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let ex = match parser::parse(&line) {
|
let ex = match parser::parse(&line) {
|
||||||
|
@ -125,7 +135,7 @@ pub fn repl(args: &Args) -> ExitCode {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}Error:{} {e}", c.error, c.reset);
|
eprintln!("{}Error:{} {e}", c.error, c.reset);
|
||||||
continue
|
continue
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (f, g) = compile_repl(&ex, &compiler_globals);
|
let (f, g) = compile_repl(&ex, &compiler_globals);
|
||||||
|
@ -154,4 +164,3 @@ pub fn repl(args: &Args) -> ExitCode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use crate::{value::Value, parser::ast::{UnaryOp, BinaryOp}, symbol::Symbol};
|
use crate::{
|
||||||
|
parser::ast::{BinaryOp, UnaryOp},
|
||||||
|
symbol::Symbol,
|
||||||
|
value::Value,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct Arg24([u8; 3]);
|
pub struct Arg24([u8; 3]);
|
||||||
|
@ -13,14 +17,20 @@ impl Arg24 {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_i32(n: i32) -> Self {
|
pub fn from_i32(n: i32) -> Self {
|
||||||
assert!((-0x80_0000..=0x7f_ffff).contains(&n), "value out of range for argument");
|
assert!(
|
||||||
|
(-0x80_0000..=0x7f_ffff).contains(&n),
|
||||||
|
"value out of range for argument"
|
||||||
|
);
|
||||||
// can't panic: size of slice guaranteed
|
// can't panic: size of slice guaranteed
|
||||||
Self(n.to_le_bytes()[0..3].try_into().unwrap())
|
Self(n.to_le_bytes()[0..3].try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_i64(n: i64) -> Self {
|
pub fn from_i64(n: i64) -> Self {
|
||||||
assert!((-0x80_0000..=0x7f_ffff).contains(&n), "value out of range for argument");
|
assert!(
|
||||||
|
(-0x80_0000..=0x7f_ffff).contains(&n),
|
||||||
|
"value out of range for argument"
|
||||||
|
);
|
||||||
// can't panic: size of slice guaranteed
|
// can't panic: size of slice guaranteed
|
||||||
Self(n.to_le_bytes()[0..3].try_into().unwrap())
|
Self(n.to_le_bytes()[0..3].try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
@ -64,7 +74,9 @@ impl From<Arg24> for i32 {
|
||||||
fn from(v: Arg24) -> Self {
|
fn from(v: Arg24) -> Self {
|
||||||
let mut n = u32::from(v);
|
let mut n = u32::from(v);
|
||||||
// sign-extend
|
// sign-extend
|
||||||
if n & 0x00_800000 != 0 { n |= 0xff_000000; }
|
if n & 0x00_800000 != 0 {
|
||||||
|
n |= 0xff_000000;
|
||||||
|
}
|
||||||
n as i32
|
n as i32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,55 +100,55 @@ impl TryFrom<Arg24> for Symbol {
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
#[default]
|
#[default]
|
||||||
Nop, // do nothing
|
Nop, // do nothing
|
||||||
|
|
||||||
LoadLocal(Arg24), // push nth local onto stack
|
LoadLocal(Arg24), // push nth local onto stack
|
||||||
StoreLocal(Arg24), // pop stack into nth local
|
StoreLocal(Arg24), // pop stack into nth local
|
||||||
NewLocal, // pop stack into a new local
|
NewLocal, // pop stack into a new local
|
||||||
DropLocal(Arg24), // remove last n locals
|
DropLocal(Arg24), // remove last n locals
|
||||||
|
|
||||||
LoadGlobal(Arg24), // load global by id
|
LoadGlobal(Arg24), // load global by id
|
||||||
StoreGlobal(Arg24), // store global by id
|
StoreGlobal(Arg24), // store global by id
|
||||||
|
|
||||||
CloseOver(Arg24), // load nth local and convert to cell, write back a copy
|
CloseOver(Arg24), // load nth local and convert to cell, write back a copy
|
||||||
Closure(Arg24), // load constant function and fill state from stack
|
Closure(Arg24), // load constant function and fill state from stack
|
||||||
LoadUpvalue(Arg24), // load
|
LoadUpvalue(Arg24), // load
|
||||||
StoreUpvalue(Arg24), // store a cell from closure state to new local
|
StoreUpvalue(Arg24), // store a cell from closure state to new local
|
||||||
ContinueUpvalue(Arg24), //
|
ContinueUpvalue(Arg24), //
|
||||||
LoadClosedLocal(Arg24), // load through cell in nth local
|
LoadClosedLocal(Arg24), // load through cell in nth local
|
||||||
StoreClosedLocal(Arg24), // store through cell in nth local
|
StoreClosedLocal(Arg24), // store through cell in nth local
|
||||||
|
|
||||||
Const(Arg24), // push nth constant
|
Const(Arg24), // push nth constant
|
||||||
Int(Arg24), // push integer
|
Int(Arg24), // push integer
|
||||||
Symbol(Arg24), // push symbol
|
Symbol(Arg24), // push symbol
|
||||||
Bool(bool), // push boolean
|
Bool(bool), // push boolean
|
||||||
Nil, // push nil
|
Nil, // push nil
|
||||||
|
|
||||||
Dup,
|
Dup,
|
||||||
DupTwo,
|
DupTwo,
|
||||||
Drop(Arg24),
|
Drop(Arg24),
|
||||||
Swap,
|
Swap,
|
||||||
|
|
||||||
UnaryOp(UnaryOp),
|
UnaryOp(UnaryOp),
|
||||||
BinaryOp(BinaryOp),
|
BinaryOp(BinaryOp),
|
||||||
|
|
||||||
NewList(u8),
|
NewList(u8),
|
||||||
GrowList(u8),
|
GrowList(u8),
|
||||||
NewTable(u8),
|
NewTable(u8),
|
||||||
GrowTable(u8),
|
GrowTable(u8),
|
||||||
|
|
||||||
Index,
|
Index,
|
||||||
StoreIndex,
|
StoreIndex,
|
||||||
|
|
||||||
Jump(Arg24),
|
Jump(Arg24),
|
||||||
JumpTrue(Arg24),
|
JumpTrue(Arg24),
|
||||||
JumpFalse(Arg24),
|
JumpFalse(Arg24),
|
||||||
|
|
||||||
IterBegin,
|
IterBegin,
|
||||||
IterTest(Arg24),
|
IterTest(Arg24),
|
||||||
|
|
||||||
BeginTry(Arg24),
|
BeginTry(Arg24),
|
||||||
EndTry,
|
EndTry,
|
||||||
|
|
||||||
Call(u8),
|
Call(u8),
|
||||||
Return,
|
Return,
|
||||||
|
@ -150,21 +162,30 @@ impl std::fmt::Display for Instruction {
|
||||||
Self::StoreLocal(a) => write!(f, "store {}", usize::from(a)),
|
Self::StoreLocal(a) => write!(f, "store {}", usize::from(a)),
|
||||||
Self::NewLocal => write!(f, "newlocal"),
|
Self::NewLocal => write!(f, "newlocal"),
|
||||||
Self::DropLocal(n) => write!(f, "discardlocal {}", usize::from(n)),
|
Self::DropLocal(n) => write!(f, "discardlocal {}", usize::from(n)),
|
||||||
Self::LoadGlobal(s) => write!(f, "loadglobal {}",
|
Self::LoadGlobal(s) => write!(
|
||||||
Symbol::try_from(s).expect("symbol does not exist").name()),
|
f,
|
||||||
Self::StoreGlobal(s) => write!(f, "storeglobal {}",
|
"loadglobal {}",
|
||||||
Symbol::try_from(s).expect("symbol does not exist").name()),
|
Symbol::try_from(s).expect("symbol does not exist").name()
|
||||||
Self::CloseOver(n) => write!(f, "closeover {}", usize::from(n)),
|
),
|
||||||
Self::Closure(c) => write!(f, "closure {}", usize::from(c)),
|
Self::StoreGlobal(s) => write!(
|
||||||
Self::LoadUpvalue(n) => write!(f, "load_upval {}", usize::from(n)),
|
f,
|
||||||
Self::StoreUpvalue(n) => write!(f, "store_upval {}", usize::from(n)),
|
"storeglobal {}",
|
||||||
Self::ContinueUpvalue(n) => write!(f, "cont_upval {}", usize::from(n)),
|
Symbol::try_from(s).expect("symbol does not exist").name()
|
||||||
Self::LoadClosedLocal(n) => write!(f, "load_closed {}", usize::from(n)),
|
),
|
||||||
Self::StoreClosedLocal(n) => write!(f, "store_closed {}", usize::from(n)),
|
Self::CloseOver(n) => write!(f, "closeover {}", usize::from(n)),
|
||||||
|
Self::Closure(c) => write!(f, "closure {}", usize::from(c)),
|
||||||
|
Self::LoadUpvalue(n) => write!(f, "load_upval {}", usize::from(n)),
|
||||||
|
Self::StoreUpvalue(n) => write!(f, "store_upval {}", usize::from(n)),
|
||||||
|
Self::ContinueUpvalue(n) => write!(f, "cont_upval {}", usize::from(n)),
|
||||||
|
Self::LoadClosedLocal(n) => write!(f, "load_closed {}", usize::from(n)),
|
||||||
|
Self::StoreClosedLocal(n) => write!(f, "store_closed {}", usize::from(n)),
|
||||||
Self::Const(c) => write!(f, "const {}", usize::from(c)),
|
Self::Const(c) => write!(f, "const {}", usize::from(c)),
|
||||||
Self::Int(i) => write!(f, "int {}", i64::from(i)),
|
Self::Int(i) => write!(f, "int {}", i64::from(i)),
|
||||||
Self::Symbol(s) => write!(f, "symbol {}",
|
Self::Symbol(s) => write!(
|
||||||
Symbol::try_from(s).expect("symbol does not exist").name()),
|
f,
|
||||||
|
"symbol {}",
|
||||||
|
Symbol::try_from(s).expect("symbol does not exist").name()
|
||||||
|
),
|
||||||
Self::Bool(b) => write!(f, "bool {b}"),
|
Self::Bool(b) => write!(f, "bool {b}"),
|
||||||
Self::Nil => write!(f, "nil"),
|
Self::Nil => write!(f, "nil"),
|
||||||
Self::Dup => write!(f, "dup"),
|
Self::Dup => write!(f, "dup"),
|
||||||
|
@ -217,19 +238,28 @@ impl Chunk {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_const(&mut self, v: Value) -> usize {
|
pub fn add_const(&mut self, v: Value) -> usize {
|
||||||
assert!(self.consts.len() < 0xff_ffff, "too many constants in a chunk");
|
assert!(
|
||||||
|
self.consts.len() < 0xff_ffff,
|
||||||
|
"too many constants in a chunk"
|
||||||
|
);
|
||||||
self.consts.push(v);
|
self.consts.push(v);
|
||||||
self.consts.len() - 1
|
self.consts.len() - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_instr(&mut self, i: Instruction) -> usize {
|
pub fn add_instr(&mut self, i: Instruction) -> usize {
|
||||||
assert!(self.instrs.len() < 0xff_ffff, "too many instructions in a chunk");
|
assert!(
|
||||||
|
self.instrs.len() < 0xff_ffff,
|
||||||
|
"too many instructions in a chunk"
|
||||||
|
);
|
||||||
self.instrs.push(i);
|
self.instrs.push(i);
|
||||||
self.instrs.len() - 1
|
self.instrs.len() - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_try_table(&mut self, local_count: usize) -> (usize, TryTable) {
|
pub fn begin_try_table(&mut self, local_count: usize) -> (usize, TryTable) {
|
||||||
assert!(self.try_tables.len() < 0xff_ffff, "too many catch tables in a chunk");
|
assert!(
|
||||||
|
self.try_tables.len() < 0xff_ffff,
|
||||||
|
"too many catch tables in a chunk"
|
||||||
|
);
|
||||||
let table = TryTable {
|
let table = TryTable {
|
||||||
catches: Vec::new(),
|
catches: Vec::new(),
|
||||||
local_count,
|
local_count,
|
||||||
|
@ -242,5 +272,3 @@ impl Chunk {
|
||||||
self.try_tables[idx] = table;
|
self.try_tables[idx] = table;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,32 +1,36 @@
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::chunk::{Arg24, Catch, Chunk, Instruction as I};
|
||||||
use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
|
use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
|
||||||
use crate::chunk::{Instruction as I, Chunk, Arg24, Catch};
|
|
||||||
use crate::symbol::{Symbol, SYM_SELF};
|
use crate::symbol::{Symbol, SYM_SELF};
|
||||||
use crate::value::function::{FuncAttrs, Function};
|
use crate::value::function::{FuncAttrs, Function};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
enum ResolveOutcome {
|
enum ResolveOutcome {
|
||||||
Var(VarKind),
|
Var(VarKind),
|
||||||
InParent,
|
InParent,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum VarKind {
|
pub enum VarKind {
|
||||||
Local(usize), Closed(usize), Global
|
Local(usize),
|
||||||
|
Closed(usize),
|
||||||
|
Global,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Var {
|
pub struct Var {
|
||||||
name: Symbol,
|
name: Symbol,
|
||||||
kind: VarKind,
|
kind: VarKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
enum CompilerMode {
|
enum CompilerMode {
|
||||||
Function, Repl, Module,
|
Function,
|
||||||
|
Repl,
|
||||||
|
Module,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Compiler<'a> {
|
struct Compiler<'a> {
|
||||||
|
@ -34,19 +38,19 @@ struct Compiler<'a> {
|
||||||
parent: Option<&'a Compiler<'a>>,
|
parent: Option<&'a Compiler<'a>>,
|
||||||
chunk: Chunk,
|
chunk: Chunk,
|
||||||
attrs: FuncAttrs,
|
attrs: FuncAttrs,
|
||||||
scope: HashMap<Symbol, Var>,
|
scope: HashMap<Symbol, Var>,
|
||||||
shadowed: Vec<(Symbol, Option<Var>)>,
|
shadowed: Vec<(Symbol, Option<Var>)>,
|
||||||
closes: BTreeMap<Symbol, usize>,
|
closes: BTreeMap<Symbol, usize>,
|
||||||
local_count: usize,
|
local_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(expr: &Expr, name: Option<Symbol>) -> Function {
|
pub fn compile(expr: &Expr, name: Option<Symbol>) -> Function {
|
||||||
let mut comp = Compiler::new_module(name, None);
|
let mut comp = Compiler::new_module(name, None);
|
||||||
comp.expr(expr);
|
comp.expr(expr);
|
||||||
comp.finish()
|
comp.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec<Symbol>) {
|
pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec<Symbol>) {
|
||||||
let mut comp = Compiler::new_repl(globals);
|
let mut comp = Compiler::new_repl(globals);
|
||||||
comp.expr(expr);
|
comp.expr(expr);
|
||||||
comp.finish_repl()
|
comp.finish_repl()
|
||||||
|
@ -54,20 +58,23 @@ pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec<Symbol>)
|
||||||
|
|
||||||
impl<'a> Default for Compiler<'a> {
|
impl<'a> Default for Compiler<'a> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let mut scope = HashMap::new();
|
let mut scope = HashMap::new();
|
||||||
scope.insert(*SYM_SELF, Var {
|
scope.insert(
|
||||||
name: *SYM_SELF,
|
*SYM_SELF,
|
||||||
kind: VarKind::Local(0),
|
Var {
|
||||||
});
|
name: *SYM_SELF,
|
||||||
|
kind: VarKind::Local(0),
|
||||||
|
},
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
mode: CompilerMode::Function,
|
mode: CompilerMode::Function,
|
||||||
parent: None,
|
parent: None,
|
||||||
chunk: Chunk::new(),
|
chunk: Chunk::new(),
|
||||||
attrs: FuncAttrs::default(),
|
attrs: FuncAttrs::default(),
|
||||||
scope,
|
scope,
|
||||||
shadowed: Vec::new(),
|
shadowed: Vec::new(),
|
||||||
local_count: 1,
|
local_count: 1,
|
||||||
closes: BTreeMap::new(),
|
closes: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,20 +83,23 @@ impl<'a> Compiler<'a> {
|
||||||
fn new_repl(globals: &[Symbol]) -> Self {
|
fn new_repl(globals: &[Symbol]) -> Self {
|
||||||
let mut new = Self {
|
let mut new = Self {
|
||||||
mode: CompilerMode::Repl,
|
mode: CompilerMode::Repl,
|
||||||
attrs: FuncAttrs { arity: 0, name: Some(Symbol::get("<repl>")) },
|
attrs: FuncAttrs {
|
||||||
|
arity: 0,
|
||||||
|
name: Some(Symbol::get("<repl>")),
|
||||||
|
},
|
||||||
..Self::default()
|
..Self::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
for g in globals {
|
for g in globals {
|
||||||
new.declare_global(*g);
|
new.declare_global(*g);
|
||||||
}
|
}
|
||||||
new
|
new
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_module(name: Option<Symbol>, parent: Option<&'a Self>) -> Self {
|
fn new_module(name: Option<Symbol>, parent: Option<&'a Self>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mode: CompilerMode::Module,
|
mode: CompilerMode::Module,
|
||||||
attrs: FuncAttrs { arity: 0, name },
|
attrs: FuncAttrs { arity: 0, name },
|
||||||
parent,
|
parent,
|
||||||
..Self::default()
|
..Self::default()
|
||||||
}
|
}
|
||||||
|
@ -98,13 +108,16 @@ impl<'a> Compiler<'a> {
|
||||||
fn new_function(&'a self, name: Option<Symbol>, args: &[Symbol]) -> Self {
|
fn new_function(&'a self, name: Option<Symbol>, args: &[Symbol]) -> Self {
|
||||||
let mut new = Self {
|
let mut new = Self {
|
||||||
mode: CompilerMode::Function,
|
mode: CompilerMode::Function,
|
||||||
attrs: FuncAttrs { arity: args.len(), name },
|
attrs: FuncAttrs {
|
||||||
|
arity: args.len(),
|
||||||
|
name,
|
||||||
|
},
|
||||||
parent: Some(self),
|
parent: Some(self),
|
||||||
..Self::default()
|
..Self::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
new.declare_local(*arg);
|
new.declare_local(*arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
new
|
new
|
||||||
|
@ -112,26 +125,27 @@ impl<'a> Compiler<'a> {
|
||||||
|
|
||||||
pub fn finish(mut self) -> Function {
|
pub fn finish(mut self) -> Function {
|
||||||
self.emit(I::Return);
|
self.emit(I::Return);
|
||||||
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len())
|
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_inner(mut self) -> (Function, BTreeMap<Symbol, usize>) {
|
pub fn finish_inner(mut self) -> (Function, BTreeMap<Symbol, usize>) {
|
||||||
self.emit(I::Return);
|
self.emit(I::Return);
|
||||||
// TODO closure
|
// TODO closure
|
||||||
(
|
(
|
||||||
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
|
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
|
||||||
self.closes
|
self.closes,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_repl(mut self) -> (Function, Vec<Symbol>) {
|
pub fn finish_repl(mut self) -> (Function, Vec<Symbol>) {
|
||||||
self.emit(I::Return);
|
self.emit(I::Return);
|
||||||
(
|
(
|
||||||
// TODO closure
|
// TODO closure
|
||||||
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
|
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
|
||||||
self.scope.into_iter().filter_map(|(_,v)| {
|
self.scope
|
||||||
(v.kind == VarKind::Global).then_some(v.name)
|
.into_iter()
|
||||||
}).collect()
|
.filter_map(|(_, v)| (v.kind == VarKind::Global).then_some(v.name))
|
||||||
|
.collect(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,38 +168,44 @@ impl<'a> Compiler<'a> {
|
||||||
// dup followed by store: remove the dup
|
// dup followed by store: remove the dup
|
||||||
if instrs.len() >= 2
|
if instrs.len() >= 2
|
||||||
&& matches!(instrs.get(instrs.len() - 2), Some(I::Dup))
|
&& matches!(instrs.get(instrs.len() - 2), Some(I::Dup))
|
||||||
&& matches!(instrs.last(), Some(
|
&& matches!(
|
||||||
I::NewLocal | I::StoreLocal(_) | I::StoreGlobal(_)
|
instrs.last(),
|
||||||
| I::StoreClosedLocal(_) | I::StoreUpvalue(_)
|
Some(
|
||||||
))
|
I::NewLocal
|
||||||
{
|
| I::StoreLocal(_) | I::StoreGlobal(_)
|
||||||
|
| I::StoreClosedLocal(_)
|
||||||
|
| I::StoreUpvalue(_)
|
||||||
|
)
|
||||||
|
) {
|
||||||
// can't panic: checked that instrs.len() >= 2
|
// can't panic: checked that instrs.len() >= 2
|
||||||
let i = self.chunk.instrs.pop().unwrap();
|
let i = self.chunk.instrs.pop().unwrap();
|
||||||
self.chunk.instrs.pop().unwrap();
|
self.chunk.instrs.pop().unwrap();
|
||||||
self.chunk.instrs.push(i);
|
self.chunk.instrs.push(i);
|
||||||
n -= 1;
|
n -= 1;
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// final side-effectless instruction
|
// final side-effectless instruction
|
||||||
let poppable = matches!(
|
let poppable = matches!(
|
||||||
instrs.last(),
|
instrs.last(),
|
||||||
Some(
|
Some(
|
||||||
I::Dup | I::Const(_) | I::Int(_)
|
I::Dup
|
||||||
| I::Nil | I::Bool(_) | I::Symbol(_)
|
| I::Const(_) | I::Int(_)
|
||||||
| I::LoadLocal(_) | I::LoadClosedLocal(_)
|
| I::Nil | I::Bool(_)
|
||||||
| I::LoadUpvalue(_)
|
| I::Symbol(_) | I::LoadLocal(_)
|
||||||
|
| I::LoadClosedLocal(_)
|
||||||
|
| I::LoadUpvalue(_)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if poppable {
|
if poppable {
|
||||||
// can't panic: checked that instrs.last() was Some
|
// can't panic: checked that instrs.last() was Some
|
||||||
instrs.pop().unwrap();
|
instrs.pop().unwrap();
|
||||||
n -= 1;
|
n -= 1;
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// no more optimizations possible
|
// no more optimizations possible
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
self.emit(I::Drop(Arg24::from_usize(n)));
|
self.emit(I::Drop(Arg24::from_usize(n)));
|
||||||
|
@ -200,181 +220,180 @@ impl<'a> Compiler<'a> {
|
||||||
self.chunk.instrs[n] = new;
|
self.chunk.instrs[n] = new;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn begin_scope(&mut self) -> usize {
|
fn begin_scope(&mut self) -> usize {
|
||||||
self.shadowed.len()
|
self.shadowed.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end_scope(&mut self, scope: usize) {
|
fn end_scope(&mut self, scope: usize) {
|
||||||
let mut locals = 0;
|
let mut locals = 0;
|
||||||
while self.shadowed.len() > scope {
|
while self.shadowed.len() > scope {
|
||||||
let (name, var) = self.shadowed.pop().expect("scope bad");
|
let (name, var) = self.shadowed.pop().expect("scope bad");
|
||||||
|
|
||||||
if let Some(var) = var {
|
if let Some(var) = var {
|
||||||
if var.kind != VarKind::Global {
|
if var.kind != VarKind::Global {
|
||||||
locals += 1;
|
locals += 1;
|
||||||
}
|
}
|
||||||
self.scope.insert(name, var);
|
self.scope.insert(name, var);
|
||||||
} else {
|
} else {
|
||||||
self.scope.remove(&name);
|
self.scope.remove(&name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if locals > 0 {
|
if locals > 0 {
|
||||||
self.emit(I::DropLocal(Arg24::from_usize(locals)));
|
self.emit(I::DropLocal(Arg24::from_usize(locals)));
|
||||||
self.local_count -= locals;
|
self.local_count -= locals;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// variables
|
// variables
|
||||||
//
|
//
|
||||||
|
|
||||||
fn resolve_name(&self, name: Symbol) -> ResolveOutcome {
|
|
||||||
if let Some(v) = self.scope.get(&name) {
|
|
||||||
return ResolveOutcome::Var(v.kind)
|
|
||||||
}
|
|
||||||
let Some(parent) = self.parent else {
|
|
||||||
return ResolveOutcome::None
|
|
||||||
};
|
|
||||||
if let ResolveOutcome::None = parent.resolve_name(name) {
|
|
||||||
return ResolveOutcome::None
|
|
||||||
}
|
|
||||||
ResolveOutcome::InParent
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_var(&mut self, name: Symbol) {
|
fn resolve_name(&self, name: Symbol) -> ResolveOutcome {
|
||||||
match self.resolve_name(name) {
|
if let Some(v) = self.scope.get(&name) {
|
||||||
ResolveOutcome::Var(VarKind::Local(n)) => {
|
return ResolveOutcome::Var(v.kind)
|
||||||
self.emit(I::LoadLocal(Arg24::from_usize(n)));
|
}
|
||||||
}
|
let Some(parent) = self.parent else {
|
||||||
ResolveOutcome::Var(VarKind::Closed(n)) => {
|
return ResolveOutcome::None
|
||||||
self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
|
};
|
||||||
}
|
if let ResolveOutcome::None = parent.resolve_name(name) {
|
||||||
ResolveOutcome::InParent => {
|
return ResolveOutcome::None
|
||||||
let n = match self.closes.get(&name) {
|
}
|
||||||
Some(n) => *n,
|
ResolveOutcome::InParent
|
||||||
None => {
|
}
|
||||||
let n = self.closes.len();
|
|
||||||
self.closes.insert(name, n);
|
|
||||||
n
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.emit(I::LoadUpvalue(Arg24::from_usize(n)));
|
|
||||||
}
|
|
||||||
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
|
|
||||||
self.emit(I::LoadGlobal(Arg24::from_symbol(name)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declare_local(&mut self, name: Symbol) -> usize {
|
fn load_var(&mut self, name: Symbol) {
|
||||||
let local = Var {
|
match self.resolve_name(name) {
|
||||||
name,
|
ResolveOutcome::Var(VarKind::Local(n)) => {
|
||||||
kind: VarKind::Local(self.local_count)
|
self.emit(I::LoadLocal(Arg24::from_usize(n)));
|
||||||
};
|
}
|
||||||
self.local_count += 1;
|
ResolveOutcome::Var(VarKind::Closed(n)) => {
|
||||||
let shadowed = self.scope.insert(name, local);
|
self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
|
||||||
self.shadowed.push((name, shadowed));
|
}
|
||||||
self.local_count - 1
|
ResolveOutcome::InParent => {
|
||||||
}
|
let n = match self.closes.get(&name) {
|
||||||
|
Some(n) => *n,
|
||||||
|
None => {
|
||||||
|
let n = self.closes.len();
|
||||||
|
self.closes.insert(name, n);
|
||||||
|
n
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.emit(I::LoadUpvalue(Arg24::from_usize(n)));
|
||||||
|
}
|
||||||
|
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
|
||||||
|
self.emit(I::LoadGlobal(Arg24::from_symbol(name)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn assign_local(&mut self, name: Symbol) -> usize {
|
fn declare_local(&mut self, name: Symbol) -> usize {
|
||||||
let n = self.declare_local(name);
|
let local = Var {
|
||||||
self.emit(I::NewLocal);
|
name,
|
||||||
n
|
kind: VarKind::Local(self.local_count),
|
||||||
}
|
};
|
||||||
|
self.local_count += 1;
|
||||||
|
let shadowed = self.scope.insert(name, local);
|
||||||
|
self.shadowed.push((name, shadowed));
|
||||||
|
self.local_count - 1
|
||||||
|
}
|
||||||
|
|
||||||
fn assign_global(&mut self, name: Symbol) {
|
fn assign_local(&mut self, name: Symbol) -> usize {
|
||||||
self.declare_global(name);
|
let n = self.declare_local(name);
|
||||||
self.store_var(name);
|
self.emit(I::NewLocal);
|
||||||
}
|
n
|
||||||
|
}
|
||||||
|
|
||||||
fn declare_global(&mut self, name: Symbol) {
|
fn assign_global(&mut self, name: Symbol) {
|
||||||
let global = Var {
|
self.declare_global(name);
|
||||||
name,
|
self.store_var(name);
|
||||||
kind: VarKind::Global
|
}
|
||||||
};
|
|
||||||
let shadowed = self.scope.insert(name, global);
|
|
||||||
self.shadowed.push((name, shadowed));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_var(&mut self, name: Symbol) {
|
|
||||||
match self.resolve_name(name) {
|
|
||||||
ResolveOutcome::Var(VarKind::Local(n)) => {
|
|
||||||
self.emit(I::StoreLocal(Arg24::from_usize(n)));
|
|
||||||
}
|
|
||||||
ResolveOutcome::Var(VarKind::Closed(n)) => {
|
|
||||||
self.emit(I::StoreClosedLocal(Arg24::from_usize(n)));
|
|
||||||
}
|
|
||||||
ResolveOutcome::InParent => {
|
|
||||||
let n = match self.closes.get(&name) {
|
|
||||||
Some(n) => *n,
|
|
||||||
None => {
|
|
||||||
let n = self.closes.len();
|
|
||||||
self.closes.insert(name, n);
|
|
||||||
n
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.emit(I::StoreUpvalue(Arg24::from_usize(n)));
|
|
||||||
}
|
|
||||||
ResolveOutcome::Var(VarKind::Global) => {
|
|
||||||
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
|
|
||||||
}
|
|
||||||
ResolveOutcome::None if self.mode == CompilerMode::Repl => {
|
|
||||||
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
|
|
||||||
}
|
|
||||||
ResolveOutcome::None => {
|
|
||||||
self.assign_local(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fn declare_global(&mut self, name: Symbol) {
|
||||||
|
let global = Var {
|
||||||
|
name,
|
||||||
|
kind: VarKind::Global,
|
||||||
|
};
|
||||||
|
let shadowed = self.scope.insert(name, global);
|
||||||
|
self.shadowed.push((name, shadowed));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store_var(&mut self, name: Symbol) {
|
||||||
|
match self.resolve_name(name) {
|
||||||
|
ResolveOutcome::Var(VarKind::Local(n)) => {
|
||||||
|
self.emit(I::StoreLocal(Arg24::from_usize(n)));
|
||||||
|
}
|
||||||
|
ResolveOutcome::Var(VarKind::Closed(n)) => {
|
||||||
|
self.emit(I::StoreClosedLocal(Arg24::from_usize(n)));
|
||||||
|
}
|
||||||
|
ResolveOutcome::InParent => {
|
||||||
|
let n = match self.closes.get(&name) {
|
||||||
|
Some(n) => *n,
|
||||||
|
None => {
|
||||||
|
let n = self.closes.len();
|
||||||
|
self.closes.insert(name, n);
|
||||||
|
n
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.emit(I::StoreUpvalue(Arg24::from_usize(n)));
|
||||||
|
}
|
||||||
|
ResolveOutcome::Var(VarKind::Global) => {
|
||||||
|
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
|
||||||
|
}
|
||||||
|
ResolveOutcome::None if self.mode == CompilerMode::Repl => {
|
||||||
|
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
|
||||||
|
}
|
||||||
|
ResolveOutcome::None => {
|
||||||
|
self.assign_local(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Expressions
|
// Expressions
|
||||||
//
|
//
|
||||||
|
|
||||||
fn expr(&mut self, e: &Expr) {
|
fn expr(&mut self, e: &Expr) {
|
||||||
let Expr { kind, .. } = e;
|
let Expr { kind, .. } = e;
|
||||||
match kind {
|
match kind {
|
||||||
ExprKind::Block(xs) if xs.is_empty() => { self.emit(I::Nil); },
|
ExprKind::Block(xs) if xs.is_empty() => {
|
||||||
|
self.emit(I::Nil);
|
||||||
|
}
|
||||||
ExprKind::Block(xs) => {
|
ExprKind::Block(xs) => {
|
||||||
let scope = self.begin_scope();
|
let scope = self.begin_scope();
|
||||||
for x in &xs[0..xs.len()-1] {
|
for x in &xs[0..xs.len() - 1] {
|
||||||
self.expr(x);
|
self.expr(x);
|
||||||
self.emit_discard(1);
|
self.emit_discard(1);
|
||||||
}
|
}
|
||||||
self.expr(&xs[xs.len()-1]);
|
self.expr(&xs[xs.len() - 1]);
|
||||||
self.end_scope(scope);
|
self.end_scope(scope);
|
||||||
},
|
}
|
||||||
ExprKind::Literal(v) => self.expr_literal(v),
|
ExprKind::Literal(v) => self.expr_literal(v),
|
||||||
ExprKind::Ident(ident) => self.load_var(*ident),
|
ExprKind::Ident(ident) => self.load_var(*ident),
|
||||||
ExprKind::UnaryOp(o, a) => {
|
ExprKind::UnaryOp(o, a) => {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::UnaryOp(*o));
|
self.emit(I::UnaryOp(*o));
|
||||||
},
|
}
|
||||||
ExprKind::BinaryOp(o, a, b) => {
|
ExprKind::BinaryOp(o, a, b) => {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.expr(b);
|
self.expr(b);
|
||||||
self.emit(I::BinaryOp(*o));
|
self.emit(I::BinaryOp(*o));
|
||||||
},
|
}
|
||||||
ExprKind::Assign(o, lv, a) => self.expr_assign(*o, lv, a),
|
ExprKind::Assign(o, lv, a) => self.expr_assign(*o, lv, a),
|
||||||
ExprKind::AssignVar(name, a) => {
|
ExprKind::AssignVar(name, a) => {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::Dup);
|
self.emit(I::Dup);
|
||||||
self.assign_local(*name);
|
self.assign_local(*name);
|
||||||
},
|
}
|
||||||
ExprKind::AssignGlobal(name, a) => {
|
ExprKind::AssignGlobal(name, a) => {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::Dup);
|
self.emit(I::Dup);
|
||||||
self.assign_global(*name);
|
self.assign_global(*name);
|
||||||
},
|
}
|
||||||
ExprKind::List(xs) if xs.is_empty() => {
|
ExprKind::List(xs) if xs.is_empty() => {
|
||||||
self.emit(I::NewList(0));
|
self.emit(I::NewList(0));
|
||||||
},
|
}
|
||||||
ExprKind::List(xs) => {
|
ExprKind::List(xs) => {
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for chunk in xs.chunks(16) {
|
for chunk in xs.chunks(16) {
|
||||||
|
@ -388,10 +407,10 @@ impl<'a> Compiler<'a> {
|
||||||
self.emit(I::GrowList(chunk.len() as u8));
|
self.emit(I::GrowList(chunk.len() as u8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
ExprKind::Table(xs) if xs.is_empty() => {
|
ExprKind::Table(xs) if xs.is_empty() => {
|
||||||
self.emit(I::NewTable(0));
|
self.emit(I::NewTable(0));
|
||||||
},
|
}
|
||||||
ExprKind::Table(xs) => {
|
ExprKind::Table(xs) => {
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for chunk in xs.chunks(8) {
|
for chunk in xs.chunks(8) {
|
||||||
|
@ -406,19 +425,19 @@ impl<'a> Compiler<'a> {
|
||||||
self.emit(I::GrowTable(chunk.len() as u8));
|
self.emit(I::GrowTable(chunk.len() as u8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
ExprKind::Index(ct, idx) => {
|
ExprKind::Index(ct, idx) => {
|
||||||
self.expr(ct);
|
self.expr(ct);
|
||||||
self.expr(idx);
|
self.expr(idx);
|
||||||
self.emit(I::Index);
|
self.emit(I::Index);
|
||||||
},
|
}
|
||||||
ExprKind::FnCall(f, args) => {
|
ExprKind::FnCall(f, args) => {
|
||||||
self.expr(f);
|
self.expr(f);
|
||||||
for a in args {
|
for a in args {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
}
|
}
|
||||||
self.emit(I::Call(args.len() as u8));
|
self.emit(I::Call(args.len() as u8));
|
||||||
},
|
}
|
||||||
ExprKind::AssocFnCall(o, f, args) => {
|
ExprKind::AssocFnCall(o, f, args) => {
|
||||||
self.expr(o);
|
self.expr(o);
|
||||||
self.emit(I::Dup);
|
self.emit(I::Dup);
|
||||||
|
@ -429,17 +448,17 @@ impl<'a> Compiler<'a> {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
}
|
}
|
||||||
self.emit(I::Call((args.len() + 1) as u8));
|
self.emit(I::Call((args.len() + 1) as u8));
|
||||||
},
|
}
|
||||||
ExprKind::Return(e) => {
|
ExprKind::Return(e) => {
|
||||||
self.expr(e);
|
self.expr(e);
|
||||||
self.emit(I::Return);
|
self.emit(I::Return);
|
||||||
},
|
}
|
||||||
ExprKind::Pipe(a, f) => {
|
ExprKind::Pipe(a, f) => {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.expr(f);
|
self.expr(f);
|
||||||
self.emit(I::Swap);
|
self.emit(I::Swap);
|
||||||
self.emit(I::Call(1));
|
self.emit(I::Call(1));
|
||||||
},
|
}
|
||||||
ExprKind::Lambda(args, body) => self.expr_fndef(None, args, body),
|
ExprKind::Lambda(args, body) => self.expr_fndef(None, args, body),
|
||||||
ExprKind::FnDef(name, args, body) => self.expr_fndef(*name, args, body),
|
ExprKind::FnDef(name, args, body) => self.expr_fndef(*name, args, body),
|
||||||
ExprKind::And(a, b) => {
|
ExprKind::And(a, b) => {
|
||||||
|
@ -449,7 +468,7 @@ impl<'a> Compiler<'a> {
|
||||||
self.emit_discard(1);
|
self.emit_discard(1);
|
||||||
self.expr(b);
|
self.expr(b);
|
||||||
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
|
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
|
||||||
},
|
}
|
||||||
ExprKind::Or(a, b) => {
|
ExprKind::Or(a, b) => {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::Dup);
|
self.emit(I::Dup);
|
||||||
|
@ -457,7 +476,7 @@ impl<'a> Compiler<'a> {
|
||||||
self.emit_discard(1);
|
self.emit_discard(1);
|
||||||
self.expr(b);
|
self.expr(b);
|
||||||
self.update_instr(j1, I::JumpTrue(Arg24::from_usize(self.ip())));
|
self.update_instr(j1, I::JumpTrue(Arg24::from_usize(self.ip())));
|
||||||
},
|
}
|
||||||
ExprKind::If(cond, b1, b2) => {
|
ExprKind::If(cond, b1, b2) => {
|
||||||
self.expr(cond);
|
self.expr(cond);
|
||||||
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
|
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
|
||||||
|
@ -469,10 +488,8 @@ impl<'a> Compiler<'a> {
|
||||||
} else {
|
} else {
|
||||||
self.emit(I::Nil);
|
self.emit(I::Nil);
|
||||||
}
|
}
|
||||||
self.update_instr(j2,
|
self.update_instr(j2, I::Jump(Arg24::from_usize(self.ip())));
|
||||||
I::Jump(Arg24::from_usize(self.ip()))
|
}
|
||||||
);
|
|
||||||
},
|
|
||||||
ExprKind::While(cond, body) => {
|
ExprKind::While(cond, body) => {
|
||||||
let start = self.ip();
|
let start = self.ip();
|
||||||
self.expr(cond);
|
self.expr(cond);
|
||||||
|
@ -484,7 +501,7 @@ impl<'a> Compiler<'a> {
|
||||||
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
|
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
|
||||||
|
|
||||||
self.emit(I::Nil);
|
self.emit(I::Nil);
|
||||||
},
|
}
|
||||||
ExprKind::For(name, iter, body) => self.expr_for(*name, iter, body),
|
ExprKind::For(name, iter, body) => self.expr_for(*name, iter, body),
|
||||||
ExprKind::Try(body, catches) => self.expr_try(body, catches),
|
ExprKind::Try(body, catches) => self.expr_try(body, catches),
|
||||||
}
|
}
|
||||||
|
@ -568,55 +585,60 @@ impl<'a> Compiler<'a> {
|
||||||
let (func, closes) = inner.finish_inner();
|
let (func, closes) = inner.finish_inner();
|
||||||
let func_const = self.add_const(func.into());
|
let func_const = self.add_const(func.into());
|
||||||
|
|
||||||
let num_closed = closes.len();
|
let num_closed = closes.len();
|
||||||
|
|
||||||
for (name, _) in closes {
|
for (name, _) in closes {
|
||||||
match self.resolve_name(name) {
|
match self.resolve_name(name) {
|
||||||
ResolveOutcome::Var(VarKind::Local(n)) => {
|
ResolveOutcome::Var(VarKind::Local(n)) => {
|
||||||
self.emit(I::CloseOver(Arg24::from_usize(n)));
|
self.emit(I::CloseOver(Arg24::from_usize(n)));
|
||||||
self.scope.entry(name).and_modify(|v| {
|
self.scope.entry(name).and_modify(|v| {
|
||||||
v.kind = VarKind::Closed(n);
|
v.kind = VarKind::Closed(n);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
ResolveOutcome::Var(VarKind::Closed(n)) => {
|
ResolveOutcome::Var(VarKind::Closed(n)) => {
|
||||||
self.emit(I::LoadLocal(Arg24::from_usize(n)));
|
self.emit(I::LoadLocal(Arg24::from_usize(n)));
|
||||||
},
|
}
|
||||||
ResolveOutcome::InParent => {
|
ResolveOutcome::InParent => {
|
||||||
let n = self.closes.len();
|
let n = self.closes.len();
|
||||||
self.closes.insert(name, n);
|
self.closes.insert(name, n);
|
||||||
self.emit(I::ContinueUpvalue(Arg24::from_usize(n)));
|
self.emit(I::ContinueUpvalue(Arg24::from_usize(n)));
|
||||||
},
|
}
|
||||||
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global)
|
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
|
||||||
=> panic!("upvalue resolved to none or global"),
|
panic!("upvalue resolved to none or global")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if num_closed == 0 {
|
if num_closed == 0 {
|
||||||
self.emit(I::Const(Arg24::from_usize(func_const)));
|
self.emit(I::Const(Arg24::from_usize(func_const)));
|
||||||
} else {
|
} else {
|
||||||
self.emit(I::Closure(Arg24::from_usize(func_const)));
|
self.emit(I::Closure(Arg24::from_usize(func_const)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
self.emit(I::Dup);
|
self.emit(I::Dup);
|
||||||
self.store_var(name);
|
self.store_var(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_literal(&mut self, val: &Value) {
|
fn expr_literal(&mut self, val: &Value) {
|
||||||
match val {
|
match val {
|
||||||
Value::Nil
|
Value::Nil => {
|
||||||
=> { self.emit(I::Nil); },
|
self.emit(I::Nil);
|
||||||
Value::Bool(b)
|
}
|
||||||
=> { self.emit(I::Bool(*b)); },
|
Value::Bool(b) => {
|
||||||
Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i)
|
self.emit(I::Bool(*b));
|
||||||
=> { self.emit(I::Int(Arg24::from_i64(*i))); },
|
}
|
||||||
Value::Symbol(s)
|
Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i) => {
|
||||||
=> { self.emit(I::Symbol(Arg24::from_symbol(*s))); },
|
self.emit(I::Int(Arg24::from_i64(*i)));
|
||||||
|
}
|
||||||
|
Value::Symbol(s) => {
|
||||||
|
self.emit(I::Symbol(Arg24::from_symbol(*s)));
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let n = self.add_const(val.clone());
|
let n = self.add_const(val.clone());
|
||||||
self.emit(I::Const(Arg24::from_usize(n)));
|
self.emit(I::Const(Arg24::from_usize(n)));
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,20 +648,20 @@ impl<'a> Compiler<'a> {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::Dup);
|
self.emit(I::Dup);
|
||||||
self.store_var(*i);
|
self.store_var(*i);
|
||||||
},
|
}
|
||||||
(LValueKind::Ident(i), Some(o)) => {
|
(LValueKind::Ident(i), Some(o)) => {
|
||||||
self.load_var(*i);
|
self.load_var(*i);
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::BinaryOp(o));
|
self.emit(I::BinaryOp(o));
|
||||||
self.emit(I::Dup);
|
self.emit(I::Dup);
|
||||||
self.store_var(*i);
|
self.store_var(*i);
|
||||||
},
|
}
|
||||||
(LValueKind::Index(ct, i), None) => {
|
(LValueKind::Index(ct, i), None) => {
|
||||||
self.expr(ct);
|
self.expr(ct);
|
||||||
self.expr(i);
|
self.expr(i);
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::StoreIndex);
|
self.emit(I::StoreIndex);
|
||||||
},
|
}
|
||||||
(LValueKind::Index(ct, i), Some(o)) => {
|
(LValueKind::Index(ct, i), Some(o)) => {
|
||||||
self.expr(ct);
|
self.expr(ct);
|
||||||
self.expr(i);
|
self.expr(i);
|
||||||
|
@ -648,8 +670,7 @@ impl<'a> Compiler<'a> {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::BinaryOp(o));
|
self.emit(I::BinaryOp(o));
|
||||||
self.emit(I::StoreIndex);
|
self.emit(I::StoreIndex);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
use std::{rc::Rc, collections::HashMap, cell::RefCell, fmt::Display};
|
use crate::{
|
||||||
use crate::{lstring::LStr, symbol::{Symbol, SYM_DATA, SYM_MSG, SYM_TYPE}, value::{HashValue, Value}};
|
lstring::LStr,
|
||||||
|
symbol::{Symbol, SYM_DATA, SYM_MSG, SYM_TYPE},
|
||||||
|
value::{HashValue, Value},
|
||||||
|
};
|
||||||
|
use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc};
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Exception>;
|
pub type Result<T> = std::result::Result<T, Exception>;
|
||||||
|
|
||||||
|
@ -12,19 +16,35 @@ pub struct Exception {
|
||||||
|
|
||||||
impl Exception {
|
impl Exception {
|
||||||
pub fn new(ty: Symbol) -> Self {
|
pub fn new(ty: Symbol) -> Self {
|
||||||
Self { ty, msg: None, data: None }
|
Self {
|
||||||
|
ty,
|
||||||
|
msg: None,
|
||||||
|
data: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_msg(ty: Symbol, msg: Rc<LStr>) -> Self {
|
pub fn new_with_msg(ty: Symbol, msg: Rc<LStr>) -> Self {
|
||||||
Self { ty, msg: Some(msg), data: None }
|
Self {
|
||||||
|
ty,
|
||||||
|
msg: Some(msg),
|
||||||
|
data: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_data(ty: Symbol, data: Value) -> Self {
|
pub fn new_with_data(ty: Symbol, data: Value) -> Self {
|
||||||
Self { ty, msg: None, data: Some(data) }
|
Self {
|
||||||
|
ty,
|
||||||
|
msg: None,
|
||||||
|
data: Some(data),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_msg_data(ty: Symbol, msg: Rc<LStr>, data: Value) -> Self {
|
pub fn new_with_msg_data(ty: Symbol, msg: Rc<LStr>, data: Value) -> Self {
|
||||||
Self { ty, msg: Some(msg), data: Some(data) }
|
Self {
|
||||||
|
ty,
|
||||||
|
msg: Some(msg),
|
||||||
|
data: Some(data),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_table(table: &Rc<RefCell<HashMap<HashValue, Value>>>) -> Option<Self> {
|
pub fn from_table(table: &Rc<RefCell<HashMap<HashValue, Value>>>) -> Option<Self> {
|
||||||
|
@ -102,6 +122,3 @@ macro_rules! throw {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use throw;
|
pub use throw;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
#![warn(clippy::semicolon_if_nothing_returned)]
|
#![warn(clippy::semicolon_if_nothing_returned)]
|
||||||
#![warn(clippy::allow_attributes)]
|
#![warn(clippy::allow_attributes)]
|
||||||
|
|
||||||
pub mod symbol;
|
|
||||||
pub mod parser;
|
|
||||||
pub mod value;
|
|
||||||
pub mod exception;
|
|
||||||
pub mod chunk;
|
pub mod chunk;
|
||||||
pub mod compiler;
|
pub mod compiler;
|
||||||
|
pub mod exception;
|
||||||
pub mod lstring;
|
pub mod lstring;
|
||||||
|
pub mod parser;
|
||||||
|
pub mod symbol;
|
||||||
|
pub mod value;
|
||||||
|
|
||||||
mod vm;
|
mod vm;
|
||||||
pub use vm::Vm;
|
pub use vm::Vm;
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
use std::{borrow::{Borrow, BorrowMut, Cow}, ffi::{OsStr, OsString}, fmt::{self, Write}, io, iter::{Copied, FusedIterator}, ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut}, rc::Rc, slice, str::Utf8Error, string::FromUtf8Error};
|
use std::{
|
||||||
|
borrow::{Borrow, BorrowMut, Cow},
|
||||||
|
ffi::{OsStr, OsString},
|
||||||
|
fmt::{self, Write},
|
||||||
|
io,
|
||||||
|
iter::{Copied, FusedIterator},
|
||||||
|
ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut},
|
||||||
|
rc::Rc,
|
||||||
|
slice,
|
||||||
|
str::Utf8Error,
|
||||||
|
string::FromUtf8Error,
|
||||||
|
};
|
||||||
|
|
||||||
use unicode_ident::{is_xid_continue, is_xid_start};
|
use unicode_ident::{is_xid_continue, is_xid_start};
|
||||||
|
|
||||||
|
@ -28,7 +39,9 @@ fn is_continue(b: u8) -> bool {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn calc_continue(mut ch: u32, bytes: &[u8]) -> Option<char> {
|
fn calc_continue(mut ch: u32, bytes: &[u8]) -> Option<char> {
|
||||||
for b in bytes {
|
for b in bytes {
|
||||||
if !is_continue(*b) { return None }
|
if !is_continue(*b) {
|
||||||
|
return None
|
||||||
|
}
|
||||||
ch = (ch << 6) | (b & 0x3f) as u32;
|
ch = (ch << 6) | (b & 0x3f) as u32;
|
||||||
}
|
}
|
||||||
char::from_u32(ch)
|
char::from_u32(ch)
|
||||||
|
@ -40,23 +53,29 @@ fn next_codepoint(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
|
||||||
match init {
|
match init {
|
||||||
0..=0x7f => return Some((&bytes[1..], Ok(init as char))),
|
0..=0x7f => return Some((&bytes[1..], Ok(init as char))),
|
||||||
0xc0..=0xdf => 'case: {
|
0xc0..=0xdf => 'case: {
|
||||||
if bytes.len() < 2 { break 'case }
|
if bytes.len() < 2 {
|
||||||
|
break 'case
|
||||||
|
}
|
||||||
let Some(ch) = calc_continue(init as u32 & 0x1f, &bytes[1..2]) else {
|
let Some(ch) = calc_continue(init as u32 & 0x1f, &bytes[1..2]) else {
|
||||||
break 'case;
|
break 'case
|
||||||
};
|
};
|
||||||
return Some((&bytes[2..], Ok(ch)))
|
return Some((&bytes[2..], Ok(ch)))
|
||||||
},
|
}
|
||||||
0xe0..=0xef => 'case: {
|
0xe0..=0xef => 'case: {
|
||||||
if bytes.len() < 3 { break 'case }
|
if bytes.len() < 3 {
|
||||||
|
break 'case
|
||||||
|
}
|
||||||
let Some(ch) = calc_continue(init as u32 & 0x0f, &bytes[1..3]) else {
|
let Some(ch) = calc_continue(init as u32 & 0x0f, &bytes[1..3]) else {
|
||||||
break 'case;
|
break 'case
|
||||||
};
|
};
|
||||||
return Some((&bytes[3..], Ok(ch)))
|
return Some((&bytes[3..], Ok(ch)))
|
||||||
},
|
}
|
||||||
0xf0..=0xf7 => 'case: {
|
0xf0..=0xf7 => 'case: {
|
||||||
if bytes.len() < 4 { break 'case }
|
if bytes.len() < 4 {
|
||||||
|
break 'case
|
||||||
|
}
|
||||||
let Some(ch) = calc_continue(init as u32 & 0x07, &bytes[1..4]) else {
|
let Some(ch) = calc_continue(init as u32 & 0x07, &bytes[1..4]) else {
|
||||||
break 'case;
|
break 'case
|
||||||
};
|
};
|
||||||
return Some((&bytes[4..], Ok(ch)))
|
return Some((&bytes[4..], Ok(ch)))
|
||||||
}
|
}
|
||||||
|
@ -69,45 +88,55 @@ fn next_codepoint(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
|
fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
|
||||||
let len = bytes.len();
|
let len = bytes.len();
|
||||||
if len < 1 { return None }
|
if len < 1 {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
let last = bytes[len-1];
|
let last = bytes[len - 1];
|
||||||
if (0..=0x7f).contains(&last) {
|
if (0..=0x7f).contains(&last) {
|
||||||
return Some((&bytes[..len-1], Ok(last as char)))
|
return Some((&bytes[..len - 1], Ok(last as char)))
|
||||||
}
|
}
|
||||||
|
|
||||||
'case: {
|
'case: {
|
||||||
if !is_continue(last) { break 'case }
|
if !is_continue(last) {
|
||||||
|
break 'case
|
||||||
|
}
|
||||||
|
|
||||||
if len < 2 { break 'case }
|
if len < 2 {
|
||||||
let b1 = bytes[len-2];
|
break 'case
|
||||||
|
}
|
||||||
|
let b1 = bytes[len - 2];
|
||||||
if 0xe0 & b1 == 0xc0 {
|
if 0xe0 & b1 == 0xc0 {
|
||||||
if let Some(ch) = calc_continue(b1 as u32 & 0x1f, &[last]) {
|
if let Some(ch) = calc_continue(b1 as u32 & 0x1f, &[last]) {
|
||||||
return Some((&bytes[..len-2], Ok(ch)))
|
return Some((&bytes[..len - 2], Ok(ch)))
|
||||||
};
|
};
|
||||||
} else if !is_continue(b1) {
|
} else if !is_continue(b1) {
|
||||||
break 'case
|
break 'case
|
||||||
}
|
}
|
||||||
|
|
||||||
if len < 3 { break 'case }
|
if len < 3 {
|
||||||
let b2 = bytes[len-3];
|
break 'case
|
||||||
|
}
|
||||||
|
let b2 = bytes[len - 3];
|
||||||
if 0xf0 & b2 == 0xe0 {
|
if 0xf0 & b2 == 0xe0 {
|
||||||
if let Some(ch) = calc_continue(b2 as u32 & 0x0f, &[b1, last]) {
|
if let Some(ch) = calc_continue(b2 as u32 & 0x0f, &[b1, last]) {
|
||||||
return Some((&bytes[..len-3], Ok(ch)))
|
return Some((&bytes[..len - 3], Ok(ch)))
|
||||||
};
|
};
|
||||||
} else if !is_continue(b2) {
|
} else if !is_continue(b2) {
|
||||||
break 'case
|
break 'case
|
||||||
}
|
}
|
||||||
|
|
||||||
if len < 4 { break 'case }
|
if len < 4 {
|
||||||
let b3 = bytes[len-4];
|
break 'case
|
||||||
|
}
|
||||||
|
let b3 = bytes[len - 4];
|
||||||
if 0xf8 & b3 == 0xf0 {
|
if 0xf8 & b3 == 0xf0 {
|
||||||
if let Some(ch) = calc_continue(b3 as u32 & 0x07, &[b2, b1, last]) {
|
if let Some(ch) = calc_continue(b3 as u32 & 0x07, &[b2, b1, last]) {
|
||||||
return Some((&bytes[..len-4], Ok(ch)))
|
return Some((&bytes[..len - 4], Ok(ch)))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some((&bytes[..len-1], Err(last)))
|
Some((&bytes[..len - 1], Err(last)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
@ -230,47 +259,69 @@ impl BorrowMut<LStr> for LString {
|
||||||
|
|
||||||
impl From<LString> for Vec<u8> {
|
impl From<LString> for Vec<u8> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: LString) -> Self { value.inner }
|
fn from(value: LString) -> Self {
|
||||||
|
value.inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<u8>> for LString {
|
impl From<Vec<u8>> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: Vec<u8>) -> Self { Self { inner: value } }
|
fn from(value: Vec<u8>) -> Self {
|
||||||
|
Self { inner: value }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for LString {
|
impl From<String> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: String) -> Self { Self { inner: value.into_bytes() } }
|
fn from(value: String) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: value.into_bytes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OsString> for LString {
|
impl From<OsString> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: OsString) -> Self { Self { inner: value.into_encoded_bytes() } }
|
fn from(value: OsString) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: value.into_encoded_bytes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&LStr> for LString {
|
impl From<&LStr> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: &LStr) -> Self { value.to_owned() }
|
fn from(value: &LStr) -> Self {
|
||||||
|
value.to_owned()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for LString {
|
impl From<&str> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: &str) -> Self { value.to_owned().into() }
|
fn from(value: &str) -> Self {
|
||||||
|
value.to_owned().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&[u8]> for LString {
|
impl From<&[u8]> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: &[u8]) -> Self { value.to_owned().into() }
|
fn from(value: &[u8]) -> Self {
|
||||||
|
value.to_owned().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> From<&[u8; N]> for LString {
|
impl<const N: usize> From<&[u8; N]> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: &[u8; N]) -> Self { value.as_slice().into() }
|
fn from(value: &[u8; N]) -> Self {
|
||||||
|
value.as_slice().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> From<[u8; N]> for LString {
|
impl<const N: usize> From<[u8; N]> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: [u8; N]) -> Self { value.as_slice().into() }
|
fn from(value: [u8; N]) -> Self {
|
||||||
|
value.as_slice().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Cow<'_, LStr>> for LString {
|
impl From<Cow<'_, LStr>> for LString {
|
||||||
|
@ -278,7 +329,7 @@ impl From<Cow<'_, LStr>> for LString {
|
||||||
fn from(value: Cow<'_, LStr>) -> Self {
|
fn from(value: Cow<'_, LStr>) -> Self {
|
||||||
match value {
|
match value {
|
||||||
Cow::Borrowed(b) => b.to_owned(),
|
Cow::Borrowed(b) => b.to_owned(),
|
||||||
Cow::Owned(o) => o
|
Cow::Owned(o) => o,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,7 +357,9 @@ impl<const N: usize> From<Cow<'_, [u8; N]>> for LString {
|
||||||
|
|
||||||
impl<'a> From<&'a LStr> for &'a [u8] {
|
impl<'a> From<&'a LStr> for &'a [u8] {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: &'a LStr) -> Self { &value.inner }
|
fn from(value: &'a LStr) -> Self {
|
||||||
|
&value.inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a [u8]> for &'a LStr {
|
impl<'a> From<&'a [u8]> for &'a LStr {
|
||||||
|
@ -339,7 +392,9 @@ impl<'a> From<&'a OsStr> for &'a LStr {
|
||||||
|
|
||||||
impl<'a> From<&'a mut LStr> for &'a mut [u8] {
|
impl<'a> From<&'a mut LStr> for &'a mut [u8] {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: &'a mut LStr) -> Self { &mut value.inner }
|
fn from(value: &'a mut LStr) -> Self {
|
||||||
|
&mut value.inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a mut [u8]> for &'a mut LStr {
|
impl<'a> From<&'a mut [u8]> for &'a mut LStr {
|
||||||
|
@ -351,7 +406,9 @@ impl<'a> From<&'a mut [u8]> for &'a mut LStr {
|
||||||
|
|
||||||
impl<'a> From<&'a LString> for &'a LStr {
|
impl<'a> From<&'a LString> for &'a LStr {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: &'a LString) -> Self { value }
|
fn from(value: &'a LString) -> Self {
|
||||||
|
value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&LStr> for Rc<LStr> {
|
impl From<&LStr> for Rc<LStr> {
|
||||||
|
@ -412,35 +469,35 @@ impl From<u8> for LString {
|
||||||
|
|
||||||
impl FromIterator<char> for LString {
|
impl FromIterator<char> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_iter<I: IntoIterator<Item=char>>(iter: I) -> Self {
|
fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
|
||||||
String::from_iter(iter).into()
|
String::from_iter(iter).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromIterator<u8> for LString {
|
impl FromIterator<u8> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_iter<T: IntoIterator<Item=u8>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
|
||||||
Vec::from_iter(iter).into()
|
Vec::from_iter(iter).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Extend<u8> for LString {
|
impl Extend<u8> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extend<I: IntoIterator<Item=u8>>(&mut self, iter: I) {
|
fn extend<I: IntoIterator<Item = u8>>(&mut self, iter: I) {
|
||||||
self.inner.extend(iter);
|
self.inner.extend(iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Extend<&'a u8> for LString {
|
impl<'a> Extend<&'a u8> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extend<I: IntoIterator<Item=&'a u8>>(&mut self, iter: I) {
|
fn extend<I: IntoIterator<Item = &'a u8>>(&mut self, iter: I) {
|
||||||
self.inner.extend(iter);
|
self.inner.extend(iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Extend<char> for LString {
|
impl Extend<char> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extend<I: IntoIterator<Item=char>>(&mut self, iter: I) {
|
fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
|
||||||
let iter = iter.into_iter();
|
let iter = iter.into_iter();
|
||||||
let (lo, _) = iter.size_hint();
|
let (lo, _) = iter.size_hint();
|
||||||
self.reserve(lo);
|
self.reserve(lo);
|
||||||
|
@ -450,7 +507,7 @@ impl Extend<char> for LString {
|
||||||
|
|
||||||
impl<'a> Extend<&'a char> for LString {
|
impl<'a> Extend<&'a char> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn extend<I: IntoIterator<Item=&'a char>>(&mut self, iter: I) {
|
fn extend<I: IntoIterator<Item = &'a char>>(&mut self, iter: I) {
|
||||||
let iter = iter.into_iter();
|
let iter = iter.into_iter();
|
||||||
let (lo, _) = iter.size_hint();
|
let (lo, _) = iter.size_hint();
|
||||||
self.reserve(lo);
|
self.reserve(lo);
|
||||||
|
@ -468,7 +525,9 @@ impl io::Write for LString {
|
||||||
Ok(buf.len())
|
Ok(buf.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//impl fmt::Write for LString {
|
//impl fmt::Write for LString {
|
||||||
|
@ -547,15 +606,25 @@ impl<'a> Iterator for Bytes<'a> {
|
||||||
type Item = u8;
|
type Item = u8;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<Self::Item> { self.0.next() }
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.0.next()
|
||||||
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
self.0.size_hint()
|
||||||
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn count(self) -> usize { self.0.count() }
|
fn count(self) -> usize {
|
||||||
|
self.0.count()
|
||||||
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn last(self) -> Option<Self::Item> { self.0.last() }
|
fn last(self) -> Option<Self::Item> {
|
||||||
|
self.0.last()
|
||||||
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn nth(&mut self, n: usize) -> Option<Self::Item> { self.0.nth(n) }
|
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
||||||
|
self.0.nth(n)
|
||||||
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn all<F: FnMut(Self::Item) -> bool>(&mut self, f: F) -> bool {
|
fn all<F: FnMut(Self::Item) -> bool>(&mut self, f: F) -> bool {
|
||||||
self.0.all(f)
|
self.0.all(f)
|
||||||
|
@ -576,7 +645,9 @@ impl<'a> Iterator for Bytes<'a> {
|
||||||
|
|
||||||
impl<'a> ExactSizeIterator for Bytes<'a> {
|
impl<'a> ExactSizeIterator for Bytes<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn len(&self) -> usize { self.0.len() }
|
fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FusedIterator for Bytes<'a> {}
|
impl<'a> FusedIterator for Bytes<'a> {}
|
||||||
|
@ -597,7 +668,7 @@ impl<'a> Iterator for LosslessChars<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
let len = self.0.len();
|
let len = self.0.len();
|
||||||
((len + 3)/4, Some(len))
|
((len + 3) / 4, Some(len))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -618,19 +689,19 @@ impl<'a> Iterator for LosslessCharsIndices<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let (new_bytes, res) = next_codepoint(self.0)?;
|
let (new_bytes, res) = next_codepoint(self.0)?;
|
||||||
let index = self.1;
|
let index = self.1;
|
||||||
self.0 = new_bytes;
|
self.0 = new_bytes;
|
||||||
match res {
|
match res {
|
||||||
Ok(c) => self.1 += c.len_utf8(),
|
Ok(c) => self.1 += c.len_utf8(),
|
||||||
Err(_) => self.1 += 1,
|
Err(_) => self.1 += 1,
|
||||||
}
|
}
|
||||||
Some((index, res))
|
Some((index, res))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
let len = self.0.len();
|
let len = self.0.len();
|
||||||
((len + 3)/4, Some(len))
|
((len + 3) / 4, Some(len))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -638,15 +709,14 @@ impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> {
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
let (new_bytes, res) = next_codepoint_back(self.0)?;
|
let (new_bytes, res) = next_codepoint_back(self.0)?;
|
||||||
self.0 = new_bytes;
|
self.0 = new_bytes;
|
||||||
match res {
|
match res {
|
||||||
Ok(c) => self.1 -= c.len_utf8(),
|
Ok(c) => self.1 -= c.len_utf8(),
|
||||||
Err(_) => self.1 -= 1,
|
Err(_) => self.1 -= 1,
|
||||||
}
|
}
|
||||||
Some((self.1, res))
|
Some((self.1, res))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Chars<'a>(LosslessChars<'a>);
|
pub struct Chars<'a>(LosslessChars<'a>);
|
||||||
|
|
||||||
|
@ -702,7 +772,7 @@ impl<'a> Iterator for CharsIndices<'a> {
|
||||||
impl<'a> DoubleEndedIterator for CharsIndices<'a> {
|
impl<'a> DoubleEndedIterator for CharsIndices<'a> {
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
loop {
|
loop {
|
||||||
if let (index, Ok(c)) = self.0.next_back()? {
|
if let (index, Ok(c)) = self.0.next_back()? {
|
||||||
return Some((index, c))
|
return Some((index, c))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -731,16 +801,24 @@ impl LStr {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn as_bytes(&self) -> &[u8] { &self.inner }
|
pub const fn as_bytes(&self) -> &[u8] {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_bytes_mut(&mut self) -> &mut [u8] { &mut self.inner }
|
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn byte_at(&self, n: usize) -> u8 { self.inner[n] }
|
pub fn byte_at(&self, n: usize) -> u8 {
|
||||||
|
self.inner[n]
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn byte_get(&self, n: usize) -> Option<u8> { self.inner.get(n).copied() }
|
pub fn byte_get(&self, n: usize) -> Option<u8> {
|
||||||
|
self.inner.get(n).copied()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn bytes(&self) -> Bytes {
|
pub fn bytes(&self) -> Bytes {
|
||||||
|
@ -787,11 +865,13 @@ impl LStr {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_os_str(&self) -> Cow<OsStr> {
|
pub fn to_os_str(&self) -> Cow<OsStr> {
|
||||||
#[cfg(unix)] {
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
Cow::Borrowed(OsStr::from_bytes(self.as_bytes()))
|
Cow::Borrowed(OsStr::from_bytes(self.as_bytes()))
|
||||||
}
|
}
|
||||||
#[cfg(not(unix))] {
|
#[cfg(not(unix))]
|
||||||
|
{
|
||||||
Cow::Owned(self.to_string().into())
|
Cow::Owned(self.to_string().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -862,18 +942,21 @@ impl LStr {
|
||||||
self.as_bytes().ends_with(s.as_bytes())
|
self.as_bytes().ends_with(s.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn strip_prefix(&self, prefix: &LStr) -> Option<&LStr> {
|
pub fn strip_prefix(&self, prefix: &LStr) -> Option<&LStr> {
|
||||||
self.as_bytes().strip_prefix(prefix.as_bytes()).map(LStr::from_bytes)
|
self.as_bytes()
|
||||||
}
|
.strip_prefix(prefix.as_bytes())
|
||||||
|
.map(LStr::from_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn strip_suffix(&self, suffix: &LStr) -> Option<&LStr> {
|
pub fn strip_suffix(&self, suffix: &LStr) -> Option<&LStr> {
|
||||||
self.as_bytes().strip_suffix(suffix.as_bytes()).map(LStr::from_bytes)
|
self.as_bytes()
|
||||||
}
|
.strip_suffix(suffix.as_bytes())
|
||||||
|
.map(LStr::from_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_identifier(&self) -> bool {
|
pub fn is_identifier(&self) -> bool {
|
||||||
let mut chars = self.chars_lossless();
|
let mut chars = self.chars_lossless();
|
||||||
let first = chars.next()
|
let first = chars.next().is_some_and(|ch| ch.is_ok_and(is_xid_start));
|
||||||
.is_some_and(|ch| ch.is_ok_and(is_xid_start));
|
|
||||||
if !first {
|
if !first {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -899,11 +982,15 @@ impl AddAssign<&LStr> for LString {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for &LStr {
|
impl Default for &LStr {
|
||||||
fn default() -> Self { [].as_ref().into() }
|
fn default() -> Self {
|
||||||
|
[].as_ref().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for &mut LStr {
|
impl Default for &mut LStr {
|
||||||
fn default() -> Self { [].as_mut().into() }
|
fn default() -> Self {
|
||||||
|
[].as_mut().into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_index {
|
macro_rules! impl_index {
|
||||||
|
@ -945,4 +1032,3 @@ impl_index!(std::ops::RangeInclusive<usize>);
|
||||||
impl_index!(std::ops::RangeTo<usize>);
|
impl_index!(std::ops::RangeTo<usize>);
|
||||||
impl_index!(std::ops::RangeToInclusive<usize>);
|
impl_index!(std::ops::RangeToInclusive<usize>);
|
||||||
impl_index!((std::ops::Bound<usize>, std::ops::Bound<usize>));
|
impl_index!((std::ops::Bound<usize>, std::ops::Bound<usize>));
|
||||||
|
|
||||||
|
|
|
@ -6,22 +6,41 @@ use super::Span;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum BinaryOp {
|
pub enum BinaryOp {
|
||||||
Add, Sub, Mul, Div, Mod, Pow, IntDiv,
|
Add,
|
||||||
Shr, Shl, BitAnd, BitXor, BitOr,
|
Sub,
|
||||||
Eq, Ne, Gt, Lt, Ge, Le,
|
Mul,
|
||||||
Concat, Append,
|
Div,
|
||||||
Range, RangeIncl,
|
Mod,
|
||||||
|
Pow,
|
||||||
|
IntDiv,
|
||||||
|
Shr,
|
||||||
|
Shl,
|
||||||
|
BitAnd,
|
||||||
|
BitXor,
|
||||||
|
BitOr,
|
||||||
|
Eq,
|
||||||
|
Ne,
|
||||||
|
Gt,
|
||||||
|
Lt,
|
||||||
|
Ge,
|
||||||
|
Le,
|
||||||
|
Concat,
|
||||||
|
Append,
|
||||||
|
Range,
|
||||||
|
RangeIncl,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum UnaryOp {
|
pub enum UnaryOp {
|
||||||
Neg, Not, RangeEndless,
|
Neg,
|
||||||
|
Not,
|
||||||
|
RangeEndless,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Expr {
|
pub struct Expr {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub kind: ExprKind,
|
pub kind: ExprKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -57,14 +76,14 @@ pub enum ExprKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExprKind {
|
impl ExprKind {
|
||||||
pub fn span(self, span: Span) -> Expr {
|
pub fn span(self, span: Span) -> Expr {
|
||||||
Expr { kind: self, span }
|
Expr { kind: self, span }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CatchBlock {
|
pub struct CatchBlock {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub name: Option<Symbol>,
|
pub name: Option<Symbol>,
|
||||||
pub types: Option<Vec<Symbol>>,
|
pub types: Option<Vec<Symbol>>,
|
||||||
pub body: Expr,
|
pub body: Expr,
|
||||||
|
@ -72,8 +91,8 @@ pub struct CatchBlock {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LValue {
|
pub struct LValue {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub kind: LValueKind,
|
pub kind: LValueKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -83,214 +102,213 @@ pub enum LValueKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LValueKind {
|
impl LValueKind {
|
||||||
pub fn span(self, span: Span) -> LValue {
|
pub fn span(self, span: Span) -> LValue {
|
||||||
LValue { kind: self, span }
|
LValue { kind: self, span }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LValue {
|
impl LValue {
|
||||||
pub fn from_expr(e: Expr) -> Option<LValue> {
|
pub fn from_expr(e: Expr) -> Option<LValue> {
|
||||||
let Expr { span, kind } = e;
|
let Expr { span, kind } = e;
|
||||||
match kind {
|
match kind {
|
||||||
ExprKind::Ident(i) => Some(LValueKind::Ident(i).span(span)),
|
ExprKind::Ident(i) => Some(LValueKind::Ident(i).span(span)),
|
||||||
ExprKind::Index(l, r) => Some(LValueKind::Index(l, r).span(span)),
|
ExprKind::Index(l, r) => Some(LValueKind::Index(l, r).span(span)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CatchBlock {
|
impl CatchBlock {
|
||||||
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
|
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
|
||||||
write!(w, "{0: >1$}catch", "", depth*2)?;
|
write!(w, "{0: >1$}catch", "", depth * 2)?;
|
||||||
if let Some(name) = self.name {
|
if let Some(name) = self.name {
|
||||||
write!(w, " ${}", name.name())?;
|
write!(w, " ${}", name.name())?;
|
||||||
}
|
}
|
||||||
if let Some(types) = &self.types {
|
if let Some(types) = &self.types {
|
||||||
write!(w, ":")?;
|
write!(w, ":")?;
|
||||||
for ty in types {
|
for ty in types {
|
||||||
write!(w, " {}", ty.name())?;
|
write!(w, " {}", ty.name())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writeln!(w)?;
|
writeln!(w)?;
|
||||||
self.body.write_to(w, depth + 1)
|
self.body.write_to(w, depth + 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CatchBlock {
|
impl fmt::Display for CatchBlock {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.write_to(f, 0)
|
self.write_to(f, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LValue {
|
impl LValue {
|
||||||
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
|
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
|
||||||
write!(w, "{0: >1$}", "", depth*2)?;
|
write!(w, "{0: >1$}", "", depth * 2)?;
|
||||||
let depth = depth + 1;
|
let depth = depth + 1;
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
LValueKind::Ident(n) => writeln!(w, "${}", n.name()),
|
LValueKind::Ident(n) => writeln!(w, "${}", n.name()),
|
||||||
LValueKind::Index(l, r) => {
|
LValueKind::Index(l, r) => {
|
||||||
writeln!(w, "index")?;
|
writeln!(w, "index")?;
|
||||||
l.write_to(w, depth)?;
|
l.write_to(w, depth)?;
|
||||||
r.write_to(w, depth)
|
r.write_to(w, depth)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for LValue {
|
impl fmt::Display for LValue {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.write_to(f, 0)
|
self.write_to(f, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
|
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
|
||||||
write!(w, "{0: >1$}", "", depth*2)?;
|
write!(w, "{0: >1$}", "", depth * 2)?;
|
||||||
let depth = depth + 1;
|
let depth = depth + 1;
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
ExprKind::Literal(val) => writeln!(w, "{val}"),
|
ExprKind::Literal(val) => writeln!(w, "{val}"),
|
||||||
ExprKind::Ident(n) => writeln!(w, "${}", n.name()),
|
ExprKind::Ident(n) => writeln!(w, "${}", n.name()),
|
||||||
ExprKind::UnaryOp(op, e) => {
|
ExprKind::UnaryOp(op, e) => {
|
||||||
writeln!(w, "uop {op:?}")?;
|
writeln!(w, "uop {op:?}")?;
|
||||||
e.write_to(w, depth)
|
e.write_to(w, depth)
|
||||||
},
|
}
|
||||||
ExprKind::BinaryOp(op, l, r) => {
|
ExprKind::BinaryOp(op, l, r) => {
|
||||||
writeln!(w, "bop {op:?}")?;
|
writeln!(w, "bop {op:?}")?;
|
||||||
l.write_to(w, depth)?;
|
l.write_to(w, depth)?;
|
||||||
r.write_to(w, depth)
|
r.write_to(w, depth)
|
||||||
},
|
}
|
||||||
ExprKind::Assign(op, l, r) => {
|
ExprKind::Assign(op, l, r) => {
|
||||||
if let Some(op) = op {
|
if let Some(op) = op {
|
||||||
writeln!(w, "asgn {op:?}")?;
|
writeln!(w, "asgn {op:?}")?;
|
||||||
} else {
|
} else {
|
||||||
writeln!(w, "asgn =")?;
|
writeln!(w, "asgn =")?;
|
||||||
}
|
}
|
||||||
l.write_to(w, depth)?;
|
l.write_to(w, depth)?;
|
||||||
r.write_to(w, depth)
|
r.write_to(w, depth)
|
||||||
},
|
}
|
||||||
ExprKind::AssignVar(l, r) => {
|
ExprKind::AssignVar(l, r) => {
|
||||||
writeln!(w, "var {}", l.name())?;
|
writeln!(w, "var {}", l.name())?;
|
||||||
r.write_to(w, depth)
|
r.write_to(w, depth)
|
||||||
},
|
}
|
||||||
ExprKind::AssignGlobal(l, r) => {
|
ExprKind::AssignGlobal(l, r) => {
|
||||||
writeln!(w, "global {}", l.name())?;
|
writeln!(w, "global {}", l.name())?;
|
||||||
r.write_to(w, depth)
|
r.write_to(w, depth)
|
||||||
},
|
}
|
||||||
ExprKind::FnDef(n, p, b) => {
|
ExprKind::FnDef(n, p, b) => {
|
||||||
if let Some(n) = n {
|
if let Some(n) = n {
|
||||||
writeln!(w, "fndef ${}", n.name())?;
|
writeln!(w, "fndef ${}", n.name())?;
|
||||||
} else {
|
} else {
|
||||||
writeln!(w, "fndef anon")?;
|
writeln!(w, "fndef anon")?;
|
||||||
}
|
}
|
||||||
for arg in p {
|
for arg in p {
|
||||||
writeln!(w, " ${}", arg.name())?;
|
writeln!(w, " ${}", arg.name())?;
|
||||||
}
|
}
|
||||||
b.write_to(w, depth)
|
b.write_to(w, depth)
|
||||||
},
|
}
|
||||||
ExprKind::Index(l, r) => {
|
ExprKind::Index(l, r) => {
|
||||||
writeln!(w, "index")?;
|
writeln!(w, "index")?;
|
||||||
l.write_to(w, depth)?;
|
l.write_to(w, depth)?;
|
||||||
r.write_to(w, depth)
|
r.write_to(w, depth)
|
||||||
},
|
}
|
||||||
ExprKind::FnCall(f, a) => {
|
ExprKind::FnCall(f, a) => {
|
||||||
writeln!(w, "call")?;
|
writeln!(w, "call")?;
|
||||||
f.write_to(w, depth)?;
|
f.write_to(w, depth)?;
|
||||||
for arg in a {
|
for arg in a {
|
||||||
arg.write_to(w, depth)?;
|
arg.write_to(w, depth)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
ExprKind::AssocFnCall(d, f, a) => {
|
ExprKind::AssocFnCall(d, f, a) => {
|
||||||
writeln!(w, "assoc call {}", f.name())?;
|
writeln!(w, "assoc call {}", f.name())?;
|
||||||
d.write_to(w, depth)?;
|
d.write_to(w, depth)?;
|
||||||
for arg in a {
|
for arg in a {
|
||||||
arg.write_to(w, depth)?;
|
arg.write_to(w, depth)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
ExprKind::Pipe(l, r) => {
|
ExprKind::Pipe(l, r) => {
|
||||||
writeln!(w, "pipe")?;
|
writeln!(w, "pipe")?;
|
||||||
l.write_to(w, depth)?;
|
l.write_to(w, depth)?;
|
||||||
r.write_to(w, depth)
|
r.write_to(w, depth)
|
||||||
},
|
}
|
||||||
ExprKind::Block(b) => {
|
ExprKind::Block(b) => {
|
||||||
writeln!(w, "block")?;
|
writeln!(w, "block")?;
|
||||||
for e in b {
|
for e in b {
|
||||||
e.write_to(w, depth)?;
|
e.write_to(w, depth)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
ExprKind::List(l) => {
|
ExprKind::List(l) => {
|
||||||
writeln!(w, "list")?;
|
writeln!(w, "list")?;
|
||||||
for e in l {
|
for e in l {
|
||||||
e.write_to(w, depth)?;
|
e.write_to(w, depth)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
ExprKind::Table(t) => {
|
ExprKind::Table(t) => {
|
||||||
writeln!(w, "list")?;
|
writeln!(w, "list")?;
|
||||||
for (k, v) in t {
|
for (k, v) in t {
|
||||||
k.write_to(w, depth)?;
|
k.write_to(w, depth)?;
|
||||||
v.write_to(w, depth)?;
|
v.write_to(w, depth)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
ExprKind::Return(e) => {
|
ExprKind::Return(e) => {
|
||||||
writeln!(w, "return")?;
|
writeln!(w, "return")?;
|
||||||
e.write_to(w, depth)
|
e.write_to(w, depth)
|
||||||
}
|
}
|
||||||
ExprKind::And(l, r) => {
|
ExprKind::And(l, r) => {
|
||||||
writeln!(w, "and")?;
|
writeln!(w, "and")?;
|
||||||
l.write_to(w, depth)?;
|
l.write_to(w, depth)?;
|
||||||
r.write_to(w, depth)
|
r.write_to(w, depth)
|
||||||
}
|
}
|
||||||
ExprKind::Or(l, r) => {
|
ExprKind::Or(l, r) => {
|
||||||
writeln!(w, "or")?;
|
writeln!(w, "or")?;
|
||||||
l.write_to(w, depth)?;
|
l.write_to(w, depth)?;
|
||||||
r.write_to(w, depth)
|
r.write_to(w, depth)
|
||||||
}
|
}
|
||||||
ExprKind::If(c, b, e) => {
|
ExprKind::If(c, b, e) => {
|
||||||
writeln!(w, "if")?;
|
writeln!(w, "if")?;
|
||||||
c.write_to(w, depth)?;
|
c.write_to(w, depth)?;
|
||||||
b.write_to(w, depth)?;
|
b.write_to(w, depth)?;
|
||||||
if let Some(e) = e {
|
if let Some(e) = e {
|
||||||
e.write_to(w, depth)?;
|
e.write_to(w, depth)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
ExprKind::While(c, b) => {
|
ExprKind::While(c, b) => {
|
||||||
writeln!(w, "while")?;
|
writeln!(w, "while")?;
|
||||||
c.write_to(w, depth)?;
|
c.write_to(w, depth)?;
|
||||||
b.write_to(w, depth)
|
b.write_to(w, depth)
|
||||||
}
|
}
|
||||||
ExprKind::For(v, i, b) => {
|
ExprKind::For(v, i, b) => {
|
||||||
writeln!(w, "for {}", v.name())?;
|
writeln!(w, "for {}", v.name())?;
|
||||||
i.write_to(w, depth)?;
|
i.write_to(w, depth)?;
|
||||||
b.write_to(w, depth)
|
b.write_to(w, depth)
|
||||||
}
|
}
|
||||||
ExprKind::Lambda(a, b) => {
|
ExprKind::Lambda(a, b) => {
|
||||||
write!(w, "lambda")?;
|
write!(w, "lambda")?;
|
||||||
for arg in a {
|
for arg in a {
|
||||||
write!(w, " {}", arg.name())?;
|
write!(w, " {}", arg.name())?;
|
||||||
}
|
}
|
||||||
writeln!(w)?;
|
writeln!(w)?;
|
||||||
b.write_to(w, depth)
|
b.write_to(w, depth)
|
||||||
}
|
}
|
||||||
ExprKind::Try(t, c) => {
|
ExprKind::Try(t, c) => {
|
||||||
write!(w, "try")?;
|
write!(w, "try")?;
|
||||||
t.write_to(w, depth)?;
|
t.write_to(w, depth)?;
|
||||||
for catch in c {
|
for catch in c {
|
||||||
catch.write_to(w, depth)?;
|
catch.write_to(w, depth)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Expr {
|
impl fmt::Display for Expr {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.write_to(f, 0)
|
self.write_to(f, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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)]
|
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
|
||||||
pub struct Pos {
|
pub struct Pos {
|
||||||
pub idx: u32,
|
pub idx: u32,
|
||||||
pub line: u32,
|
pub line: u32,
|
||||||
pub col: u32,
|
pub col: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::cmp::PartialOrd for Pos {
|
impl std::cmp::PartialOrd for Pos {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::cmp::Ord for Pos {
|
impl std::cmp::Ord for Pos {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
self.idx.cmp(&other.idx)
|
self.idx.cmp(&other.idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pos {
|
impl Pos {
|
||||||
pub const fn new() -> Pos {
|
pub const fn new() -> Pos {
|
||||||
Pos { idx: 0, line: 1, col: 1 }
|
Pos {
|
||||||
}
|
idx: 0,
|
||||||
|
line: 1,
|
||||||
|
col: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn advance(self, c: char) -> Pos {
|
pub fn advance(self, c: char) -> Pos {
|
||||||
let idx = self.idx.checked_add(c.len_utf8() as u32)
|
let idx = self
|
||||||
.expect("source file contains more than u32::MAX chars");
|
.idx
|
||||||
if c == '\n' {
|
.checked_add(c.len_utf8() as u32)
|
||||||
Pos {
|
.expect("source file contains more than u32::MAX chars");
|
||||||
idx,
|
if c == '\n' {
|
||||||
line: self.line + 1,
|
Pos {
|
||||||
col: 1,
|
idx,
|
||||||
}
|
line: self.line + 1,
|
||||||
} else {
|
col: 1,
|
||||||
Pos {
|
}
|
||||||
idx,
|
} else {
|
||||||
line: self.line,
|
Pos {
|
||||||
col: self.col + 1,
|
idx,
|
||||||
}
|
line: self.line,
|
||||||
}
|
col: self.col + 1,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Pos {
|
impl fmt::Display for Pos {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}:{}", self.line, self.col)
|
write!(f, "{}:{}", self.line, self.col)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Span {
|
pub struct Span {
|
||||||
pub start: Pos,
|
pub start: Pos,
|
||||||
pub end: Pos,
|
pub end: Pos,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Span {
|
impl Span {
|
||||||
pub const fn new(start: Pos, end: Pos) -> Self {
|
pub const fn new(start: Pos, end: Pos) -> Self {
|
||||||
if end.idx < start.idx {
|
if end.idx < start.idx {
|
||||||
Self { start: end, end: start }
|
Self {
|
||||||
} else {
|
start: end,
|
||||||
Self { start, end }
|
end: start,
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
Self { start, end }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn of<'a>(&self, s: &'a str) -> &'a str {
|
pub fn of<'a>(&self, s: &'a str) -> &'a str {
|
||||||
&s[(self.start.idx as usize)..(self.end.idx as usize)]
|
&s[(self.start.idx as usize)..(self.end.idx as usize)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Add for Span {
|
impl std::ops::Add for Span {
|
||||||
type Output = Span;
|
type Output = Span;
|
||||||
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
let start = self.start.min(rhs.start);
|
let start = self.start.min(rhs.start);
|
||||||
let end = self.end.max(rhs.end);
|
let end = self.end.max(rhs.end);
|
||||||
Self::new(start, end)
|
Self::new(start, end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::AddAssign for Span {
|
impl std::ops::AddAssign for Span {
|
||||||
fn add_assign(&mut self, rhs: Self) {
|
fn add_assign(&mut self, rhs: Self) {
|
||||||
self.start = self.start.min(rhs.start);
|
self.start = self.start.min(rhs.start);
|
||||||
self.end = self.end.max(rhs.end);
|
self.end = self.end.max(rhs.end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(Pos, Pos)> for Span {
|
impl From<(Pos, Pos)> for Span {
|
||||||
fn from((start, end): (Pos, Pos)) -> Self {
|
fn from((start, end): (Pos, Pos)) -> Self {
|
||||||
Self { start, end }
|
Self { start, end }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Span {
|
impl fmt::Display for Span {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}-{}", self.start, self.end)
|
write!(f, "{}-{}", self.start, self.end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use std::num::{ParseIntError, ParseFloatError};
|
use std::num::{ParseFloatError, ParseIntError};
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -9,26 +9,29 @@ use super::Span;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Error)]
|
#[derive(Clone, Debug, Error)]
|
||||||
pub struct ParserError {
|
pub struct ParserError {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub msg: String,
|
pub msg: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ParserError {
|
impl fmt::Display for ParserError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{} | {}", self.span, self.msg)
|
write!(f, "{} | {}", self.span, self.msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SpanParserError {
|
pub trait SpanParserError {
|
||||||
type Output;
|
type Output;
|
||||||
fn span_err(self, span: Span) -> Self::Output;
|
fn span_err(self, span: Span) -> Self::Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, E: std::error::Error> SpanParserError for Result<T, E> {
|
impl<T, E: std::error::Error> SpanParserError for Result<T, E> {
|
||||||
type Output = Result<T, ParserError>;
|
type Output = Result<T, ParserError>;
|
||||||
fn span_err(self, span: Span) -> Self::Output {
|
fn span_err(self, span: Span) -> Self::Output {
|
||||||
self.map_err(|e| ParserError { span, msg: e.to_string() })
|
self.map_err(|e| ParserError {
|
||||||
}
|
span,
|
||||||
|
msg: e.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Error)]
|
#[derive(Clone, Copy, Debug, Error)]
|
||||||
|
@ -56,26 +59,29 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
|
||||||
let mut chars = src.chars();
|
let mut chars = src.chars();
|
||||||
|
|
||||||
while let Some(c) = chars.next() {
|
while let Some(c) = chars.next() {
|
||||||
if c != '\\' { s.push_char(c); continue }
|
if c != '\\' {
|
||||||
|
s.push_char(c);
|
||||||
|
continue
|
||||||
|
}
|
||||||
let c = chars.next().ok_or(StrEscapeError::Eof)?;
|
let c = chars.next().ok_or(StrEscapeError::Eof)?;
|
||||||
match c {
|
match c {
|
||||||
'"' | '\'' | '\\' => s.push_char(c),
|
'"' | '\'' | '\\' => s.push_char(c),
|
||||||
'0' => s.push_char('\0'),
|
'0' => s.push_char('\0'),
|
||||||
'a' => s.push_char('\x07'),
|
'a' => s.push_char('\x07'),
|
||||||
'b' => s.push_char('\x08'),
|
'b' => s.push_char('\x08'),
|
||||||
't' => s.push_char('\t'),
|
't' => s.push_char('\t'),
|
||||||
'n' => s.push_char('\n'),
|
'n' => s.push_char('\n'),
|
||||||
'v' => s.push_char('\x0b'),
|
'v' => s.push_char('\x0b'),
|
||||||
'f' => s.push_char('\x0c'),
|
'f' => s.push_char('\x0c'),
|
||||||
'r' => s.push_char('\r'),
|
'r' => s.push_char('\r'),
|
||||||
'e' => s.push_char('\x1b'),
|
'e' => s.push_char('\x1b'),
|
||||||
'x' => {
|
'x' => {
|
||||||
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
|
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
|
||||||
let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
|
let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
|
||||||
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
|
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
|
||||||
let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
|
let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
|
||||||
s.push_byte((n1 * 16 + n2) as u8);
|
s.push_byte((n1 * 16 + n2) as u8);
|
||||||
},
|
}
|
||||||
'u' => {
|
'u' => {
|
||||||
let Some('{') = chars.next() else {
|
let Some('{') = chars.next() else {
|
||||||
return Err(StrEscapeError::MissingBrace)
|
return Err(StrEscapeError::MissingBrace)
|
||||||
|
@ -85,7 +91,9 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
|
||||||
let Some(c) = chars.next() else {
|
let Some(c) = chars.next() else {
|
||||||
return Err(StrEscapeError::UnicodeEof)
|
return Err(StrEscapeError::UnicodeEof)
|
||||||
};
|
};
|
||||||
if c == '}' { break }
|
if c == '}' {
|
||||||
|
break
|
||||||
|
}
|
||||||
if n > 0x10ffff {
|
if n > 0x10ffff {
|
||||||
return Err(StrEscapeError::CodepointTooLarge)
|
return Err(StrEscapeError::CodepointTooLarge)
|
||||||
}
|
}
|
||||||
|
@ -93,8 +101,7 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
|
||||||
}
|
}
|
||||||
let ch = char::from_u32(n).ok_or(StrEscapeError::InvalidCodepoint(n))?;
|
let ch = char::from_u32(n).ok_or(StrEscapeError::InvalidCodepoint(n))?;
|
||||||
s.push_char(ch);
|
s.push_char(ch);
|
||||||
|
}
|
||||||
},
|
|
||||||
c => return Err(StrEscapeError::Invalid(c)),
|
c => return Err(StrEscapeError::Invalid(c)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,14 +130,14 @@ pub fn parse_int<'a, S: Into<&'a LStr>>(f: S, radix: u32) -> Result<i64, ParseIn
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result<i64, ParseIntError> {
|
pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result<i64, ParseIntError> {
|
||||||
let f = f.into();
|
let f = f.into();
|
||||||
match f.chars().nth(2) {
|
match f.chars().nth(2) {
|
||||||
Some('x') => parse_int(&f[2..], 16),
|
Some('x') => parse_int(&f[2..], 16),
|
||||||
Some('o') => parse_int(&f[2..], 8),
|
Some('o') => parse_int(&f[2..], 8),
|
||||||
Some('s') => parse_int(&f[2..], 6),
|
Some('s') => parse_int(&f[2..], 6),
|
||||||
Some('b') => parse_int(&f[2..], 2),
|
Some('b') => parse_int(&f[2..], 2),
|
||||||
_ => parse_int(f, 10),
|
_ => parse_int(f, 10),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
|
pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
|
||||||
|
@ -151,14 +158,14 @@ pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
|
||||||
x /= radix as u64;
|
x /= radix as u64;
|
||||||
|
|
||||||
let mut c = char::from_digit(m as u32, radix).unwrap();
|
let mut c = char::from_digit(m as u32, radix).unwrap();
|
||||||
if upper { c.make_ascii_uppercase(); }
|
if upper {
|
||||||
|
c.make_ascii_uppercase();
|
||||||
|
}
|
||||||
result.push(c as u8);
|
result.push(c as u8);
|
||||||
if x == 0 {
|
if x == 0 {
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result[begin..].reverse();
|
result[begin..].reverse();
|
||||||
LString::from(result)
|
LString::from(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
use std::{collections::HashMap, sync::{Mutex, OnceLock}};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Mutex, OnceLock},
|
||||||
|
};
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct SymbolTable {
|
struct SymbolTable {
|
||||||
names: Vec<&'static LStr>,
|
names: Vec<&'static LStr>,
|
||||||
values: HashMap<&'static LStr, Symbol>
|
values: HashMap<&'static LStr, Symbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref SYM_SELF: Symbol = symbol!(self);
|
pub static ref SYM_SELF: Symbol = symbol!(self);
|
||||||
pub static ref SYM_DOLLAR_SIGN: Symbol = symbol!("$");
|
pub static ref SYM_DOLLAR_SIGN: Symbol = symbol!("$");
|
||||||
|
|
||||||
pub static ref SYM_NIL: Symbol = symbol!(nil);
|
pub static ref SYM_NIL: Symbol = symbol!(nil);
|
||||||
pub static ref SYM_BOOL: Symbol = symbol!(bool);
|
pub static ref SYM_BOOL: Symbol = symbol!(bool);
|
||||||
pub static ref SYM_SYMBOL: Symbol = symbol!(symbol);
|
pub static ref SYM_SYMBOL: Symbol = symbol!(symbol);
|
||||||
|
@ -26,13 +28,10 @@ lazy_static! {
|
||||||
pub static ref SYM_TABLE: Symbol = symbol!(table);
|
pub static ref SYM_TABLE: Symbol = symbol!(table);
|
||||||
pub static ref SYM_FUNCTION: Symbol = symbol!(function);
|
pub static ref SYM_FUNCTION: Symbol = symbol!(function);
|
||||||
pub static ref SYM_NATIVE_FUNC: Symbol = symbol!(native_func);
|
pub static ref SYM_NATIVE_FUNC: Symbol = symbol!(native_func);
|
||||||
|
|
||||||
pub static ref SYM_END_ITERATION: Symbol = symbol!(end_iteration);
|
pub static ref SYM_END_ITERATION: Symbol = symbol!(end_iteration);
|
||||||
|
|
||||||
pub static ref SYM_TYPE: Symbol = symbol!(type);
|
pub static ref SYM_TYPE: Symbol = symbol!(type);
|
||||||
pub static ref SYM_MSG: Symbol = symbol!(msg);
|
pub static ref SYM_MSG: Symbol = symbol!(msg);
|
||||||
pub static ref SYM_DATA: Symbol = symbol!(data);
|
pub static ref SYM_DATA: Symbol = symbol!(data);
|
||||||
|
|
||||||
pub static ref SYM_TYPE_ERROR: Symbol = symbol!(type_error);
|
pub static ref SYM_TYPE_ERROR: Symbol = symbol!(type_error);
|
||||||
pub static ref SYM_VALUE_ERROR: Symbol = symbol!(value_error);
|
pub static ref SYM_VALUE_ERROR: Symbol = symbol!(value_error);
|
||||||
pub static ref SYM_NAME_ERROR: Symbol = symbol!(name_error);
|
pub static ref SYM_NAME_ERROR: Symbol = symbol!(name_error);
|
||||||
|
@ -110,7 +109,11 @@ impl Symbol {
|
||||||
/// If the mutex storing the symbol table is poisoned
|
/// If the mutex storing the symbol table is poisoned
|
||||||
pub fn name(self) -> &'static LStr {
|
pub fn name(self) -> &'static LStr {
|
||||||
let table = get_table().lock().expect("couldn't lock symbol table");
|
let table = get_table().lock().expect("couldn't lock symbol table");
|
||||||
table.names.get(self.0 as usize).copied().expect("symbol does not exist")
|
table
|
||||||
|
.names
|
||||||
|
.get(self.0 as usize)
|
||||||
|
.copied()
|
||||||
|
.expect("symbol does not exist")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,4 +130,3 @@ macro_rules! symbol {
|
||||||
pub use symbol;
|
pub use symbol;
|
||||||
|
|
||||||
use crate::lstring::{LStr, LString};
|
use crate::lstring::{LStr, LString};
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,35 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use crate::{chunk::Chunk, Vm, exception::Result, symbol::Symbol};
|
use crate::{chunk::Chunk, exception::Result, symbol::Symbol, Vm};
|
||||||
|
|
||||||
use super::{Value, CellValue};
|
use super::{CellValue, Value};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct FuncAttrs {
|
pub struct FuncAttrs {
|
||||||
pub arity: usize,
|
pub arity: usize,
|
||||||
pub name: Option<Symbol>,
|
pub name: Option<Symbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
pub attrs: FuncAttrs,
|
pub attrs: FuncAttrs,
|
||||||
pub chunk: Rc<Chunk>,
|
pub chunk: Rc<Chunk>,
|
||||||
pub state: Box<[CellValue]>,
|
pub state: Box<[CellValue]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn new(chunk: Rc<Chunk>, attrs: FuncAttrs, closes: usize) -> Self {
|
pub fn new(chunk: Rc<Chunk>, attrs: FuncAttrs, closes: usize) -> Self {
|
||||||
let v = Rc::new(RefCell::new(Value::Nil));
|
let v = Rc::new(RefCell::new(Value::Nil));
|
||||||
let state = std::iter::repeat(v).take(closes).collect();
|
let state = std::iter::repeat(v).take(closes).collect();
|
||||||
Self::from_parts(chunk, attrs, state)
|
Self::from_parts(chunk, attrs, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_parts(chunk: Rc<Chunk>, attrs: FuncAttrs, state: Box<[CellValue]>) -> Self {
|
pub fn from_parts(chunk: Rc<Chunk>, attrs: FuncAttrs, state: Box<[CellValue]>) -> Self {
|
||||||
Self { chunk, attrs, state }
|
Self {
|
||||||
|
chunk,
|
||||||
|
attrs,
|
||||||
|
state,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,11 +42,20 @@ pub struct NativeFunc {
|
||||||
|
|
||||||
impl NativeFunc {
|
impl NativeFunc {
|
||||||
pub fn new(func: FnNative, arity: usize, name: Symbol) -> Self {
|
pub fn new(func: FnNative, arity: usize, name: Symbol) -> Self {
|
||||||
Self { func, attrs: FuncAttrs { arity, name: Some(name) } }
|
Self {
|
||||||
|
func,
|
||||||
|
attrs: FuncAttrs {
|
||||||
|
arity,
|
||||||
|
name: Some(name),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_anon(func: FnNative, arity: usize) -> Self {
|
pub fn new_anon(func: FnNative, arity: usize) -> Self {
|
||||||
Self { func, attrs: FuncAttrs { arity, name: None } }
|
Self {
|
||||||
|
func,
|
||||||
|
attrs: FuncAttrs { arity, name: None },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,9 +68,7 @@ impl std::fmt::Debug for NativeFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disasm_recursive(f: &Rc<Function>, w: &mut impl std::io::Write) -> std::io::Result<()> {
|
pub fn disasm_recursive(f: &Rc<Function>, w: &mut impl std::io::Write) -> std::io::Result<()> {
|
||||||
writeln!(w, "{} ({})",
|
writeln!(w, "{} ({})", Value::Function(f.clone()), f.attrs.arity)?;
|
||||||
Value::Function(f.clone()),
|
|
||||||
f.attrs.arity)?;
|
|
||||||
if !f.chunk.consts.is_empty() {
|
if !f.chunk.consts.is_empty() {
|
||||||
writeln!(w, "constants")?;
|
writeln!(w, "constants")?;
|
||||||
for (i, c) in f.chunk.consts.iter().enumerate() {
|
for (i, c) in f.chunk.consts.iter().enumerate() {
|
||||||
|
@ -78,7 +89,9 @@ pub fn disasm_recursive(f: &Rc<Function>, w: &mut impl std::io::Write) -> std::i
|
||||||
if let Some(types) = &catch.types {
|
if let Some(types) = &catch.types {
|
||||||
write!(w, "{:04} [", catch.addr)?;
|
write!(w, "{:04} [", catch.addr)?;
|
||||||
for (i, ty) in types.iter().enumerate() {
|
for (i, ty) in types.iter().enumerate() {
|
||||||
if i != 0 { write!(w, ", ")?; }
|
if i != 0 {
|
||||||
|
write!(w, ", ")?;
|
||||||
|
}
|
||||||
write!(w, "{}", ty.name())?;
|
write!(w, "{}", ty.name())?;
|
||||||
}
|
}
|
||||||
writeln!(w, "]")?;
|
writeln!(w, "]")?;
|
||||||
|
@ -101,4 +114,3 @@ pub fn disasm_recursive(f: &Rc<Function>, w: &mut impl std::io::Write) -> std::i
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
use crate::{exception::{throw, Result}, symbol::{symbol, SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR}, value::function::NativeFunc, vmcalliter, Vm};
|
use crate::{
|
||||||
|
exception::{throw, Result},
|
||||||
|
symbol::{symbol, SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR},
|
||||||
|
value::function::NativeFunc,
|
||||||
|
vmcalliter, Vm,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{Value, range::RangeType};
|
use super::{range::RangeType, Value};
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn index(&self, idx: Self) -> Result<Self> {
|
pub fn index(&self, idx: Self) -> Result<Self> {
|
||||||
|
@ -11,70 +16,93 @@ impl Value {
|
||||||
if i >= 0 && (i as usize) < l.len() {
|
if i >= 0 && (i as usize) < l.len() {
|
||||||
Ok(l[i as usize].clone())
|
Ok(l[i as usize].clone())
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
|
throw!(
|
||||||
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {i} out of bounds for list of length {}",
|
||||||
|
l.len()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
(V::Range(r), V::Int(i)) => {
|
(V::Range(r), V::Int(i)) => {
|
||||||
if i >= 0 && (
|
if i >= 0
|
||||||
r.ty == RangeType::Endless
|
&& (r.ty == RangeType::Endless
|
||||||
|| i < r.stop
|
|| i < r.stop || (r.ty == RangeType::Closed && i == r.stop))
|
||||||
|| (r.ty == RangeType::Closed && i == r.stop)
|
{
|
||||||
) {
|
|
||||||
Ok((r.start + i).into())
|
Ok((r.start + i).into())
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
|
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
(V::Table(t), i) if i.hashable() => {
|
(V::Table(t), i) if i.hashable() => {
|
||||||
let t = t.borrow();
|
let t = t.borrow();
|
||||||
let i = i.try_into()?;
|
let i = i.try_into()?;
|
||||||
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
|
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
|
||||||
},
|
}
|
||||||
(V::String(s), V::Range(r)) => {
|
(V::String(s), V::Range(r)) => {
|
||||||
let slen = s.len();
|
let slen = s.len();
|
||||||
match r.ty {
|
match r.ty {
|
||||||
RangeType::Open => {
|
RangeType::Open => {
|
||||||
if r.start < 0 || r.start > slen as i64 {
|
if r.start < 0 || r.start > slen as i64 {
|
||||||
throw!(*SYM_INDEX_ERROR,
|
throw!(
|
||||||
"index {} out of bounds for string of length {}", r.stop, slen)
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {} out of bounds for string of length {}",
|
||||||
|
r.stop,
|
||||||
|
slen
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if r.stop < 0 || r.stop > slen as i64 {
|
if r.stop < 0 || r.stop > slen as i64 {
|
||||||
throw!(*SYM_INDEX_ERROR,
|
throw!(
|
||||||
"index {} out of bounds for string of length {}", r.stop, slen)
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {} out of bounds for string of length {}",
|
||||||
|
r.stop,
|
||||||
|
slen
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Ok(s[r.start as usize..r.stop as usize].into())
|
Ok(s[r.start as usize..r.stop as usize].into())
|
||||||
},
|
}
|
||||||
RangeType::Closed => {
|
RangeType::Closed => {
|
||||||
if r.start < 0 || r.start > slen as i64 {
|
if r.start < 0 || r.start > slen as i64 {
|
||||||
throw!(*SYM_INDEX_ERROR,
|
throw!(
|
||||||
"index {} out of bounds for string of length {}", r.stop, slen)
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {} out of bounds for string of length {}",
|
||||||
|
r.stop,
|
||||||
|
slen
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if r.stop < 0 || r.stop >= slen as i64 {
|
if r.stop < 0 || r.stop >= slen as i64 {
|
||||||
throw!(*SYM_INDEX_ERROR,
|
throw!(
|
||||||
"index {} out of bounds for string of length {}", r.stop, slen)
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {} out of bounds for string of length {}",
|
||||||
|
r.stop,
|
||||||
|
slen
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Ok(s[r.start as usize..=r.stop as usize].into())
|
Ok(s[r.start as usize..=r.stop as usize].into())
|
||||||
},
|
}
|
||||||
RangeType::Endless => {
|
RangeType::Endless => {
|
||||||
if r.start < 0 || r.start > slen as i64 {
|
if r.start < 0 || r.start > slen as i64 {
|
||||||
throw!(*SYM_INDEX_ERROR,
|
throw!(
|
||||||
"index {} out of bounds for string of length {}", r.stop, slen)
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {} out of bounds for string of length {}",
|
||||||
|
r.stop,
|
||||||
|
slen
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Ok(s[r.start as usize..].into())
|
Ok(s[r.start as usize..].into())
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() {
|
(col, idx) => {
|
||||||
let col = col.clone();
|
if let Ok(ii) = idx.clone().to_iter_function() {
|
||||||
let func = move |vm: &mut Vm, _| {
|
let col = col.clone();
|
||||||
match vmcalliter!(vm; ii.clone())? {
|
let func = move |vm: &mut Vm, _| match vmcalliter!(vm; ii.clone())? {
|
||||||
Some(i) => col.index(i),
|
Some(i) => col.index(i),
|
||||||
None => Ok(Value::from(*SYM_END_ITERATION)),
|
None => Ok(Value::from(*SYM_END_ITERATION)),
|
||||||
}
|
};
|
||||||
};
|
Ok(NativeFunc::new(Box::new(func), 0, symbol!("index...")).into())
|
||||||
Ok(NativeFunc::new(Box::new(func), 0, symbol!("index...")).into())
|
} else {
|
||||||
} else {
|
throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
|
||||||
throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,15 +116,19 @@ impl Value {
|
||||||
l[i as usize] = val;
|
l[i as usize] = val;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
|
throw!(
|
||||||
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {i} out of bounds for list of length {}",
|
||||||
|
l.len()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
(V::Table(t), i) if i.hashable() => {
|
(V::Table(t), i) if i.hashable() => {
|
||||||
let mut t = t.borrow_mut();
|
let mut t = t.borrow_mut();
|
||||||
let i = i.try_into()?;
|
let i = i.try_into()?;
|
||||||
t.insert(i, val);
|
t.insert(i, val);
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
(V::List(t), V::Range(r)) => {
|
(V::List(t), V::Range(r)) => {
|
||||||
let iter = val.to_iter_function()?;
|
let iter = val.to_iter_function()?;
|
||||||
let mut vals = Vec::new();
|
let mut vals = Vec::new();
|
||||||
|
@ -107,53 +139,78 @@ impl Value {
|
||||||
match r.ty {
|
match r.ty {
|
||||||
RangeType::Open => {
|
RangeType::Open => {
|
||||||
if r.start < 0 || r.start > tm.len() as i64 {
|
if r.start < 0 || r.start > tm.len() as i64 {
|
||||||
throw!(*SYM_INDEX_ERROR,
|
throw!(
|
||||||
"index {} out of bounds for list of length {}", r.stop, tm.len())
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {} out of bounds for list of length {}",
|
||||||
|
r.stop,
|
||||||
|
tm.len()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if r.stop < 0 || r.stop > tm.len() as i64 {
|
if r.stop < 0 || r.stop > tm.len() as i64 {
|
||||||
throw!(*SYM_INDEX_ERROR,
|
throw!(
|
||||||
"index {} out of bounds for list of length {}", r.stop, tm.len())
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {} out of bounds for list of length {}",
|
||||||
|
r.stop,
|
||||||
|
tm.len()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
let end = tm.split_off(r.stop as usize);
|
let end = tm.split_off(r.stop as usize);
|
||||||
tm.truncate(r.start as usize);
|
tm.truncate(r.start as usize);
|
||||||
tm.extend(vals.into_iter().chain(end));
|
tm.extend(vals.into_iter().chain(end));
|
||||||
},
|
}
|
||||||
RangeType::Closed => {
|
RangeType::Closed => {
|
||||||
if r.start < 0 || r.start > tm.len() as i64 {
|
if r.start < 0 || r.start > tm.len() as i64 {
|
||||||
throw!(*SYM_INDEX_ERROR,
|
throw!(
|
||||||
"index {} out of bounds for list of length {}", r.stop, tm.len())
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {} out of bounds for list of length {}",
|
||||||
|
r.stop,
|
||||||
|
tm.len()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if r.stop < 0 || r.stop >= tm.len() as i64 {
|
if r.stop < 0 || r.stop >= tm.len() as i64 {
|
||||||
throw!(*SYM_INDEX_ERROR,
|
throw!(
|
||||||
"index {} out of bounds for list of length {}", r.stop, tm.len())
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {} out of bounds for list of length {}",
|
||||||
|
r.stop,
|
||||||
|
tm.len()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
let end = tm.split_off(r.stop as usize + 1);
|
let end = tm.split_off(r.stop as usize + 1);
|
||||||
tm.truncate(r.start as usize);
|
tm.truncate(r.start as usize);
|
||||||
tm.extend(vals.into_iter().chain(end));
|
tm.extend(vals.into_iter().chain(end));
|
||||||
},
|
}
|
||||||
RangeType::Endless => {
|
RangeType::Endless => {
|
||||||
if r.start < 0 || r.start > tm.len() as i64 {
|
if r.start < 0 || r.start > tm.len() as i64 {
|
||||||
throw!(*SYM_INDEX_ERROR,
|
throw!(
|
||||||
"index {} out of bounds for list of length {}", r.stop, tm.len())
|
*SYM_INDEX_ERROR,
|
||||||
|
"index {} out of bounds for list of length {}",
|
||||||
|
r.stop,
|
||||||
|
tm.len()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
tm.truncate(r.start as usize);
|
tm.truncate(r.start as usize);
|
||||||
tm.extend(vals);
|
tm.extend(vals);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() {
|
(col, idx) => {
|
||||||
let val = val.to_iter_function()?;
|
if let Ok(ii) = idx.clone().to_iter_function() {
|
||||||
while let Some(i) = vmcalliter!(vm; ii.clone())? {
|
let val = val.to_iter_function()?;
|
||||||
let Some(v) = vmcalliter!(vm; val.clone())? else {
|
while let Some(i) = vmcalliter!(vm; ii.clone())? {
|
||||||
throw!(*SYM_INDEX_ERROR, "not enough values provided for store index")
|
let Some(v) = vmcalliter!(vm; val.clone())? else {
|
||||||
};
|
throw!(
|
||||||
col.store_index(vm, i, v)?;
|
*SYM_INDEX_ERROR,
|
||||||
|
"not enough values provided for store index"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
col.store_index(vm, i, v)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}")
|
||||||
}
|
}
|
||||||
Ok(())
|
}
|
||||||
} else {
|
|
||||||
throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}")
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,19 @@ pub use num_complex::Complex64;
|
||||||
use num_complex::ComplexFloat;
|
use num_complex::ComplexFloat;
|
||||||
pub use num_rational::Rational64;
|
pub use num_rational::Rational64;
|
||||||
|
|
||||||
|
use crate::exception::{throw, Exception};
|
||||||
use crate::lstring::{LStr, LString};
|
use crate::lstring::{LStr, LString};
|
||||||
use crate::symbol::{Symbol, SYM_HASH_ERROR};
|
use crate::symbol::{Symbol, SYM_HASH_ERROR};
|
||||||
use crate::exception::{Exception, throw};
|
|
||||||
|
|
||||||
use self::{range::{Range, RangeType}, function::{Function, NativeFunc}};
|
use self::{
|
||||||
|
function::{Function, NativeFunc},
|
||||||
|
range::{Range, RangeType},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod function;
|
pub mod function;
|
||||||
|
pub mod index;
|
||||||
pub mod ops;
|
pub mod ops;
|
||||||
pub mod range;
|
pub mod range;
|
||||||
pub mod index;
|
|
||||||
|
|
||||||
type RcList = Rc<RefCell<Vec<Value>>>;
|
type RcList = Rc<RefCell<Vec<Value>>>;
|
||||||
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
|
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
|
||||||
|
@ -49,7 +52,12 @@ pub trait NativeValue: std::fmt::Debug + Any {
|
||||||
fn get_type(&self) -> Symbol;
|
fn get_type(&self) -> Symbol;
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
|
||||||
fn to_lstring(&self, w: &mut LString, _repr: bool, _recur: &mut Vec<*const ()>) -> io::Result<()> {
|
fn to_lstring(
|
||||||
|
&self,
|
||||||
|
w: &mut LString,
|
||||||
|
_repr: bool,
|
||||||
|
_recur: &mut Vec<*const ()>,
|
||||||
|
) -> io::Result<()> {
|
||||||
w.extend(b"<native value>");
|
w.extend(b"<native value>");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -63,7 +71,11 @@ pub trait NativeValue: std::fmt::Debug + Any {
|
||||||
|
|
||||||
impl Display for Value {
|
impl Display for Value {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let s = if f.alternate() { Cow::Owned(self.repr()) } else { self.str() };
|
let s = if f.alternate() {
|
||||||
|
Cow::Owned(self.repr())
|
||||||
|
} else {
|
||||||
|
self.str()
|
||||||
|
};
|
||||||
write!(f, "{s}")
|
write!(f, "{s}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +93,12 @@ impl Value {
|
||||||
table.into()
|
table.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_to_lstring(&self, w: &mut LString, repr: bool, recur: &mut Vec<*const ()>) -> io::Result<()> {
|
pub fn write_to_lstring(
|
||||||
|
&self,
|
||||||
|
w: &mut LString,
|
||||||
|
repr: bool,
|
||||||
|
recur: &mut Vec<*const ()>,
|
||||||
|
) -> io::Result<()> {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
match self {
|
match self {
|
||||||
Self::Nil => write!(w, "nil"),
|
Self::Nil => write!(w, "nil"),
|
||||||
|
@ -95,10 +112,10 @@ impl Value {
|
||||||
write!(w, "{name:?}")?;
|
write!(w, "{name:?}")?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
Self::Range(r) => match r.ty {
|
Self::Range(r) => match r.ty {
|
||||||
RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
|
RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
|
||||||
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
|
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
|
||||||
RangeType::Endless => write!(w, "{}..*", r.start),
|
RangeType::Endless => write!(w, "{}..*", r.start),
|
||||||
},
|
},
|
||||||
Self::Int(i) => write!(w, "{i}"),
|
Self::Int(i) => write!(w, "{i}"),
|
||||||
|
@ -110,50 +127,50 @@ impl Value {
|
||||||
w.push_byte(b'+');
|
w.push_byte(b'+');
|
||||||
}
|
}
|
||||||
write!(w, "{:?}i", z.im())
|
write!(w, "{:?}i", z.im())
|
||||||
},
|
}
|
||||||
Self::Cell(v) if repr => {
|
Self::Cell(v) if repr => {
|
||||||
if recur.contains(&(v.as_ptr() as _)) {
|
if recur.contains(&(v.as_ptr() as _)) {
|
||||||
return w.write_all(b"cell(...)")
|
return w.write_all(b"cell(...)")
|
||||||
}
|
}
|
||||||
w.write_all(b"cell(")?;
|
w.write_all(b"cell(")?;
|
||||||
recur.push(v.as_ptr() as _);
|
recur.push(v.as_ptr() as _);
|
||||||
v.borrow().write_to_lstring(w, repr, recur)?;
|
v.borrow().write_to_lstring(w, repr, recur)?;
|
||||||
recur.pop();
|
recur.pop();
|
||||||
w.write_all(b")")
|
w.write_all(b")")
|
||||||
},
|
}
|
||||||
Self::Cell(v) => {
|
Self::Cell(v) => {
|
||||||
if recur.contains(&(v.as_ptr() as _)) {
|
if recur.contains(&(v.as_ptr() as _)) {
|
||||||
return w.write_all(b"cell(...)")
|
return w.write_all(b"cell(...)")
|
||||||
}
|
}
|
||||||
recur.push(v.as_ptr() as _);
|
recur.push(v.as_ptr() as _);
|
||||||
v.borrow().write_to_lstring(w, true, recur)?;
|
v.borrow().write_to_lstring(w, true, recur)?;
|
||||||
recur.pop();
|
recur.pop();
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
Self::String(s) if repr => write!(w, "{s:?}"),
|
Self::String(s) if repr => write!(w, "{s:?}"),
|
||||||
Self::String(s) => w.write_all(s.as_bytes()),
|
Self::String(s) => w.write_all(s.as_bytes()),
|
||||||
|
|
||||||
Self::List(l) => {
|
Self::List(l) => {
|
||||||
if recur.contains(&(l.as_ptr() as _)) {
|
if recur.contains(&(l.as_ptr() as _)) {
|
||||||
return w.write_all(b"[...]")
|
return w.write_all(b"[...]")
|
||||||
}
|
}
|
||||||
w.write_all(b"[")?;
|
w.write_all(b"[")?;
|
||||||
recur.push(l.as_ptr() as _);
|
recur.push(l.as_ptr() as _);
|
||||||
for (i, item) in l.borrow().iter().enumerate() {
|
for (i, item) in l.borrow().iter().enumerate() {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
w.write_all(b", ")?;
|
w.write_all(b", ")?;
|
||||||
}
|
}
|
||||||
item.write_to_lstring(w, true, recur)?;
|
item.write_to_lstring(w, true, recur)?;
|
||||||
}
|
}
|
||||||
recur.pop();
|
recur.pop();
|
||||||
w.write_all(b"]")
|
w.write_all(b"]")
|
||||||
},
|
}
|
||||||
Self::Table(t) => {
|
Self::Table(t) => {
|
||||||
if recur.contains(&(t.as_ptr() as _)) {
|
if recur.contains(&(t.as_ptr() as _)) {
|
||||||
return w.write_all(b"{...}")
|
return w.write_all(b"{...}")
|
||||||
}
|
}
|
||||||
w.write_all(b"{ ")?;
|
w.write_all(b"{ ")?;
|
||||||
recur.push(t.as_ptr() as _);
|
recur.push(t.as_ptr() as _);
|
||||||
for (i, (k, v)) in t.borrow().iter().enumerate() {
|
for (i, (k, v)) in t.borrow().iter().enumerate() {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
w.write_all(b", ")?;
|
w.write_all(b", ")?;
|
||||||
|
@ -162,36 +179,58 @@ impl Value {
|
||||||
w.write_all(b" = ")?;
|
w.write_all(b" = ")?;
|
||||||
v.write_to_lstring(w, true, recur)?;
|
v.write_to_lstring(w, true, recur)?;
|
||||||
}
|
}
|
||||||
recur.pop();
|
recur.pop();
|
||||||
w.write_all(b" }")
|
w.write_all(b" }")
|
||||||
},
|
}
|
||||||
Self::Function(g) => {
|
Self::Function(g) => {
|
||||||
if let Some(name) = g.attrs.name {
|
if let Some(name) = g.attrs.name {
|
||||||
write!(w, "<function {}({}) @{:0>12x}>",
|
write!(
|
||||||
name.name(), g.attrs.arity, Rc::as_ptr(g) as usize)
|
w,
|
||||||
} else {
|
"<function {}({}) @{:0>12x}>",
|
||||||
write!(w, "<anon function({}) @{:0>12x}>",
|
name.name(),
|
||||||
g.attrs.arity, Rc::as_ptr(g) as usize)
|
g.attrs.arity,
|
||||||
}
|
Rc::as_ptr(g) as usize
|
||||||
}
|
)
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
"<anon function({}) @{:0>12x}>",
|
||||||
|
g.attrs.arity,
|
||||||
|
Rc::as_ptr(g) as usize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Self::NativeFunc(g) => {
|
Self::NativeFunc(g) => {
|
||||||
if let Some(name) = g.attrs.name {
|
if let Some(name) = g.attrs.name {
|
||||||
write!(w, "<native function {}({}) @{:0>12x}>",
|
write!(
|
||||||
name.name(), g.attrs.arity, Rc::as_ptr(g) as usize)
|
w,
|
||||||
} else {
|
"<native function {}({}) @{:0>12x}>",
|
||||||
write!(w, "<anon native function({}) @{:0>12x}>",
|
name.name(),
|
||||||
g.attrs.arity, Rc::as_ptr(g) as usize)
|
g.attrs.arity,
|
||||||
}
|
Rc::as_ptr(g) as usize
|
||||||
}
|
)
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
w,
|
||||||
|
"<anon native function({}) @{:0>12x}>",
|
||||||
|
g.attrs.arity,
|
||||||
|
Rc::as_ptr(g) as usize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Self::Native(n) => n.to_lstring(w, repr, recur),
|
Self::Native(n) => n.to_lstring(w, repr, recur),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_table_key_repr(&self, w: &mut LString, recur: &mut Vec<*const ()>) -> std::io::Result<()> {
|
fn write_table_key_repr(
|
||||||
|
&self,
|
||||||
|
w: &mut LString,
|
||||||
|
recur: &mut Vec<*const ()>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::Nil | Self::Bool(_)
|
Self::Nil | Self::Bool(_) | Self::Int(_) | Self::String(_) => {
|
||||||
| Self::Int(_) | Self::String(_)
|
self.write_to_lstring(w, true, recur)
|
||||||
=> self.write_to_lstring(w, true, recur),
|
}
|
||||||
Self::Symbol(s) => {
|
Self::Symbol(s) => {
|
||||||
let name = s.name();
|
let name = s.name();
|
||||||
if name.is_identifier() {
|
if name.is_identifier() {
|
||||||
|
@ -200,7 +239,7 @@ impl Value {
|
||||||
} else {
|
} else {
|
||||||
self.write_to_lstring(w, true, recur)
|
self.write_to_lstring(w, true, recur)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
w.push_byte(b'(');
|
w.push_byte(b'(');
|
||||||
self.write_to_lstring(w, true, recur)?;
|
self.write_to_lstring(w, true, recur)?;
|
||||||
|
@ -215,16 +254,18 @@ impl Value {
|
||||||
Cow::Borrowed(s)
|
Cow::Borrowed(s)
|
||||||
} else {
|
} else {
|
||||||
let mut s = LString::new();
|
let mut s = LString::new();
|
||||||
let mut recur = Vec::new();
|
let mut recur = Vec::new();
|
||||||
self.write_to_lstring(&mut s, false, &mut recur).expect("write_to_lstring failed");
|
self.write_to_lstring(&mut s, false, &mut recur)
|
||||||
|
.expect("write_to_lstring failed");
|
||||||
Cow::Owned(s)
|
Cow::Owned(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repr(&self) -> LString {
|
pub fn repr(&self) -> LString {
|
||||||
let mut s = LString::new();
|
let mut s = LString::new();
|
||||||
let mut recur = Vec::new();
|
let mut recur = Vec::new();
|
||||||
self.write_to_lstring(&mut s, true, &mut recur).expect("write_to_lstring failed");
|
self.write_to_lstring(&mut s, true, &mut recur)
|
||||||
|
.expect("write_to_lstring failed");
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,15 +293,19 @@ impl Value {
|
||||||
pub fn hashable(&self) -> bool {
|
pub fn hashable(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
Value::Nil | Value::Bool(_) | Value::Symbol(_)
|
Value::Nil
|
||||||
| Value::Int(_) | Value::Ratio(_) | Value::String(_)
|
| Value::Bool(_)
|
||||||
|
| Value::Symbol(_)
|
||||||
|
| Value::Int(_)
|
||||||
|
| Value::Ratio(_)
|
||||||
|
| Value::String(_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn downcast_native<T: NativeValue>(&self) -> Option<&T> {
|
pub fn downcast_native<T: NativeValue>(&self) -> Option<&T> {
|
||||||
match self {
|
match self {
|
||||||
Value::Native(n) => n.as_any().downcast_ref(),
|
Value::Native(n) => n.as_any().downcast_ref(),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,8 +326,12 @@ impl TryFrom<Value> for HashValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HashValue {
|
impl HashValue {
|
||||||
pub fn into_inner(self) -> Value { self.0 }
|
pub fn into_inner(self) -> Value {
|
||||||
pub fn inner(&self) -> &Value { &self.0 }
|
self.0
|
||||||
|
}
|
||||||
|
pub fn inner(&self) -> &Value {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for HashValue {
|
impl Hash for HashValue {
|
||||||
|
@ -303,39 +352,57 @@ impl Hash for HashValue {
|
||||||
macro_rules! impl_from {
|
macro_rules! impl_from {
|
||||||
($ty:ty, $var:ident) => {
|
($ty:ty, $var:ident) => {
|
||||||
impl From<$ty> for Value {
|
impl From<$ty> for Value {
|
||||||
fn from(value: $ty) -> Self { Self::$var(value) }
|
fn from(value: $ty) -> Self {
|
||||||
|
Self::$var(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($ty:ty, $var:ident, hash) => {
|
($ty:ty, $var:ident, hash) => {
|
||||||
impl From<$ty> for Value {
|
impl From<$ty> for Value {
|
||||||
fn from(value: $ty) -> Self { Self::$var(value) }
|
fn from(value: $ty) -> Self {
|
||||||
|
Self::$var(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl From<$ty> for HashValue {
|
impl From<$ty> for HashValue {
|
||||||
fn from(value: $ty) -> Self { Self(Value::$var(value)) }
|
fn from(value: $ty) -> Self {
|
||||||
|
Self(Value::$var(value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($ty:ty, $var:ident, rc) => {
|
($ty:ty, $var:ident, rc) => {
|
||||||
impl From<$ty> for Value {
|
impl From<$ty> for Value {
|
||||||
fn from(value: $ty) -> Self { Self::$var(Rc::new(value)) }
|
fn from(value: $ty) -> Self {
|
||||||
|
Self::$var(Rc::new(value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl From<Rc<$ty>> for Value {
|
impl From<Rc<$ty>> for Value {
|
||||||
fn from(value: Rc<$ty>) -> Self { Self::$var(value) }
|
fn from(value: Rc<$ty>) -> Self {
|
||||||
|
Self::$var(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($ty:ty, $var:ident, rcref) => {
|
($ty:ty, $var:ident, rcref) => {
|
||||||
impl From<$ty> for Value {
|
impl From<$ty> for Value {
|
||||||
fn from(value: $ty) -> Self { Self::$var(Rc::new(RefCell::new(value))) }
|
fn from(value: $ty) -> Self {
|
||||||
|
Self::$var(Rc::new(RefCell::new(value)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl From<RefCell<$ty>> for Value {
|
impl From<RefCell<$ty>> for Value {
|
||||||
fn from(value: RefCell<$ty>) -> Self { Self::$var(Rc::new(value)) }
|
fn from(value: RefCell<$ty>) -> Self {
|
||||||
|
Self::$var(Rc::new(value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl From<Rc<RefCell<$ty>>> for Value {
|
impl From<Rc<RefCell<$ty>>> for Value {
|
||||||
fn from(value: Rc<RefCell<$ty>>) -> Self { Self::$var(value) }
|
fn from(value: Rc<RefCell<$ty>>) -> Self {
|
||||||
|
Self::$var(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($ty:ty, $var:ident, into) => {
|
($ty:ty, $var:ident, into) => {
|
||||||
impl From<$ty> for Value {
|
impl From<$ty> for Value {
|
||||||
fn from(value: $ty) -> Self { Self::$var(value.into()) }
|
fn from(value: $ty) -> Self {
|
||||||
|
Self::$var(value.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,27 @@
|
||||||
|
use std::{
|
||||||
use std::{cell::RefCell, cmp::Ordering, ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Shl, Shr, Sub}, rc::Rc};
|
cell::RefCell,
|
||||||
|
cmp::Ordering,
|
||||||
|
ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Shl, Shr, Sub},
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
use num_complex::{Complex64, ComplexFloat};
|
use num_complex::{Complex64, ComplexFloat};
|
||||||
use num_rational::Rational64;
|
use num_rational::Rational64;
|
||||||
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
|
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
|
||||||
|
|
||||||
use crate::{exception::{throw, Result}, lstring::LString, symbol::{symbol, SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::range::RangeType, Vm};
|
use crate::{
|
||||||
|
exception::{throw, Result},
|
||||||
|
lstring::LString,
|
||||||
|
symbol::{symbol, SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
|
value::range::RangeType,
|
||||||
|
Vm,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value};
|
use super::{
|
||||||
|
function::{FuncAttrs, NativeFunc},
|
||||||
|
range::Range,
|
||||||
|
HashValue, Value,
|
||||||
|
};
|
||||||
|
|
||||||
pub trait RatioExt {
|
pub trait RatioExt {
|
||||||
fn to_f64(&self) -> f64;
|
fn to_f64(&self) -> f64;
|
||||||
|
@ -33,9 +47,11 @@ impl Value {
|
||||||
Value::List(l) => l.borrow().len() > 0,
|
Value::List(l) => l.borrow().len() > 0,
|
||||||
Value::Cell(v) => v.borrow().truthy(),
|
Value::Cell(v) => v.borrow().truthy(),
|
||||||
|
|
||||||
Value::Symbol(_) | Value::Table(_)
|
Value::Symbol(_)
|
||||||
| Value::Function(_) | Value::NativeFunc(_)
|
| Value::Table(_)
|
||||||
| Value::Native(_) => true,
|
| Value::Function(_)
|
||||||
|
| Value::NativeFunc(_)
|
||||||
|
| Value::Native(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,46 +59,48 @@ impl Value {
|
||||||
pub fn promote(a: Value, b: Value) -> (Value, Value) {
|
pub fn promote(a: Value, b: Value) -> (Value, Value) {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match (&a, &b) {
|
match (&a, &b) {
|
||||||
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
|
(V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
|
||||||
(V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
|
(V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
|
||||||
(V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
|
(V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
|
||||||
(V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64()), b),
|
(V::Ratio(x), V::Float(..)) => (V::Float(x.to_f64()), b),
|
||||||
(V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64().into()), b),
|
(V::Ratio(x), V::Complex(..)) => (V::Complex(x.to_f64().into()), b),
|
||||||
(V::Float(x), V::Complex(..)) => (V::Complex(x.into()), b),
|
(V::Float(x), V::Complex(..)) => (V::Complex(x.into()), b),
|
||||||
(V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
|
(V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
|
||||||
(V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
|
(V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
|
||||||
(V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
|
(V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
|
||||||
(V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64())),
|
(V::Float(..), V::Ratio(y)) => (a, V::Float(y.to_f64())),
|
||||||
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64().into())),
|
(V::Complex(..), V::Ratio(y)) => (a, V::Complex(y.to_f64().into())),
|
||||||
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
|
(V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
|
||||||
_ => (a, b),
|
_ => (a, b),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// unary arithmetic //
|
// unary arithmetic //
|
||||||
////////////////////////
|
////////////////////////
|
||||||
|
|
||||||
|
|
||||||
impl Neg for Value {
|
impl Neg for Value {
|
||||||
type Output = Result<Self>;
|
type Output = Result<Self>;
|
||||||
fn neg(self) -> Self::Output {
|
fn neg(self) -> Self::Output {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match self {
|
match self {
|
||||||
V::Int(x) => if let Some(x) = x.checked_neg() {
|
V::Int(x) => {
|
||||||
Ok(V::Int(x))
|
if let Some(x) = x.checked_neg() {
|
||||||
} else {
|
Ok(V::Int(x))
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
|
} else {
|
||||||
},
|
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
|
||||||
V::Ratio(x) => if let Some(x) = Rational64::ZERO.checked_sub(&x) {
|
}
|
||||||
Ok(V::Ratio(x))
|
}
|
||||||
} else {
|
V::Ratio(x) => {
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
|
if let Some(x) = Rational64::ZERO.checked_sub(&x) {
|
||||||
}
|
Ok(V::Ratio(x))
|
||||||
|
} else {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
|
||||||
|
}
|
||||||
|
}
|
||||||
V::Float(x) => Ok(V::Float(-x)),
|
V::Float(x) => Ok(V::Float(-x)),
|
||||||
V::Complex(x) => Ok(V::Complex(-x)),
|
V::Complex(x) => Ok(V::Complex(-x)),
|
||||||
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}")
|
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,32 +109,40 @@ impl Value {
|
||||||
pub fn abs(self) -> Result<Self> {
|
pub fn abs(self) -> Result<Self> {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match self {
|
match self {
|
||||||
V::Int(x) => if let Some(x) = x.checked_abs() {
|
V::Int(x) => {
|
||||||
Ok(V::Int(x))
|
if let Some(x) = x.checked_abs() {
|
||||||
} else {
|
Ok(V::Int(x))
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when finding absolute value of {self}")
|
} else {
|
||||||
},
|
throw!(
|
||||||
V::Ratio(x) => if let Some((x, _)) = ratio_checked_absign(&x) {
|
*SYM_VALUE_ERROR,
|
||||||
Ok(V::Ratio(x))
|
"overflow when finding absolute value of {self}"
|
||||||
} else {
|
)
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when finding absolute value of {self}")
|
}
|
||||||
}
|
}
|
||||||
|
V::Ratio(x) => {
|
||||||
|
if let Some((x, _)) = ratio_checked_absign(&x) {
|
||||||
|
Ok(V::Ratio(x))
|
||||||
|
} else {
|
||||||
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"overflow when finding absolute value of {self}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
V::Float(x) => Ok(V::Float(x.abs())),
|
V::Float(x) => Ok(V::Float(x.abs())),
|
||||||
V::Complex(x) => Ok(V::Float(x.norm())),
|
V::Complex(x) => Ok(V::Float(x.norm())),
|
||||||
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}")
|
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
// binary arithmetic //
|
// binary arithmetic //
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
|
|
||||||
|
|
||||||
macro_rules! impl_value_arith {
|
macro_rules! impl_value_arith {
|
||||||
($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => {
|
($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => {
|
||||||
|
|
||||||
impl $trait<Value> for Value {
|
impl $trait<Value> for Value {
|
||||||
type Output = Result<Self>;
|
type Output = Result<Self>;
|
||||||
fn $name(self, rhs: Value) -> Self::Output {
|
fn $name(self, rhs: Value) -> Self::Output {
|
||||||
|
@ -151,20 +177,23 @@ impl Div<Value> for Value {
|
||||||
type Output = Result<Self>;
|
type Output = Result<Self>;
|
||||||
fn div(self, rhs: Value) -> Self::Output {
|
fn div(self, rhs: Value) -> Self::Output {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
let (a, b) = promote(self, rhs);
|
let (a, b) = promote(self, rhs);
|
||||||
match (&a, &b) {
|
match (&a, &b) {
|
||||||
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer division by 0"),
|
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer division by 0"),
|
||||||
(V::Int(x), V::Int(y)) => Ok(Value::Ratio((*x,*y).into())),
|
(V::Int(x), V::Int(y)) => Ok(Value::Ratio((*x, *y).into())),
|
||||||
(V::Ratio(_), V::Ratio(r)) if r.is_zero()
|
(V::Ratio(_), V::Ratio(r)) if r.is_zero() => {
|
||||||
=> throw!(*SYM_VALUE_ERROR, "rational division by 0"),
|
throw!(*SYM_VALUE_ERROR, "rational division by 0")
|
||||||
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.checked_div(y) {
|
}
|
||||||
Ok(V::Ratio(v))
|
(V::Ratio(x), V::Ratio(y)) => {
|
||||||
} else {
|
if let Some(v) = x.checked_div(y) {
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when dividing {a} and {b}")
|
Ok(V::Ratio(v))
|
||||||
},
|
} else {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "overflow when dividing {a} and {b}")
|
||||||
|
}
|
||||||
|
}
|
||||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
|
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
|
||||||
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
|
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}")
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,104 +202,115 @@ impl Div<Value> for Value {
|
||||||
// modulo and integer division //
|
// modulo and integer division //
|
||||||
///////////////////////////////////
|
///////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> {
|
fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> {
|
||||||
let a = if r.is_negative() {
|
let a = if r.is_negative() {
|
||||||
Rational64::ZERO.checked_sub(r)?
|
Rational64::ZERO.checked_sub(r)?
|
||||||
} else {
|
} else {
|
||||||
*r
|
*r
|
||||||
};
|
};
|
||||||
Some((a, r.signum()))
|
Some((a, r.signum()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
|
fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
|
||||||
let (r2_abs, r2_sgn) = ratio_checked_absign(r2)?;
|
let (r2_abs, r2_sgn) = ratio_checked_absign(r2)?;
|
||||||
Some(r1.checked_div(&r2_abs)?.floor() * r2_sgn)
|
Some(r1.checked_div(&r2_abs)?.floor() * r2_sgn)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
|
fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
|
||||||
let q = ratio_checked_div_euclid(r1, r2)?;
|
let q = ratio_checked_div_euclid(r1, r2)?;
|
||||||
r1.checked_sub(&r2.checked_mul(&q)?)
|
r1.checked_sub(&r2.checked_mul(&q)?)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn modulo(self, rhs: Value) -> Result<Self> {
|
pub fn modulo(self, rhs: Value) -> Result<Self> {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
let (a, b) = promote(self, rhs);
|
let (a, b) = promote(self, rhs);
|
||||||
match (&a, &b) {
|
match (&a, &b) {
|
||||||
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer modulo by 0"),
|
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer modulo by 0"),
|
||||||
(V::Int(x), V::Int(y)) => if let Some(v) = x.checked_rem_euclid(*y) {
|
(V::Int(x), V::Int(y)) => {
|
||||||
Ok(V::Int(v))
|
if let Some(v) = x.checked_rem_euclid(*y) {
|
||||||
} else {
|
Ok(V::Int(v))
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
|
} else {
|
||||||
},
|
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(V::Ratio(_), V::Ratio(y)) if y.is_zero()
|
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
|
||||||
=> throw!(*SYM_VALUE_ERROR, "rational modulo by 0"),
|
throw!(*SYM_VALUE_ERROR, "rational modulo by 0")
|
||||||
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = ratio_checked_rem_euclid(x, y) {
|
}
|
||||||
Ok(V::Ratio(v))
|
(V::Ratio(x), V::Ratio(y)) => {
|
||||||
} else {
|
if let Some(v) = ratio_checked_rem_euclid(x, y) {
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
|
Ok(V::Ratio(v))
|
||||||
}
|
} else {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(*y))),
|
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(*y))),
|
||||||
(V::Complex(x), V::Complex(y)) => {
|
(V::Complex(x), V::Complex(y)) => {
|
||||||
let n = x / y;
|
let n = x / y;
|
||||||
let n = Complex64::new(n.re().floor(), n.im().floor());
|
let n = Complex64::new(n.re().floor(), n.im().floor());
|
||||||
Ok(Value::Complex(x - n * y))
|
Ok(Value::Complex(x - n * y))
|
||||||
}
|
}
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}")
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn int_div(self, rhs: Value) -> Result<Self> {
|
pub fn int_div(self, rhs: Value) -> Result<Self> {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
let (a, b) = promote(self, rhs);
|
let (a, b) = promote(self, rhs);
|
||||||
match (&a, &b) {
|
match (&a, &b) {
|
||||||
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer divsion by 0"),
|
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer divsion by 0"),
|
||||||
(V::Int(x), V::Int(y)) => if let Some(v) = x.checked_div_euclid(*y) {
|
(V::Int(x), V::Int(y)) => {
|
||||||
Ok(V::Int(v))
|
if let Some(v) = x.checked_div_euclid(*y) {
|
||||||
} else {
|
Ok(V::Int(v))
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when integer dividing {a} and {b}")
|
} else {
|
||||||
},
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"overflow when integer dividing {a} and {b}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(V::Ratio(_), V::Ratio(y)) if y.is_zero()
|
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
|
||||||
=> throw!(*SYM_VALUE_ERROR, "integer division by 0"),
|
throw!(*SYM_VALUE_ERROR, "integer division by 0")
|
||||||
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = ratio_checked_div_euclid(x, y) {
|
}
|
||||||
Ok(V::Ratio(v))
|
(V::Ratio(x), V::Ratio(y)) => {
|
||||||
} else {
|
if let Some(v) = ratio_checked_div_euclid(x, y) {
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when integer dividing {a} and {b}")
|
Ok(V::Ratio(v))
|
||||||
},
|
} else {
|
||||||
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"overflow when integer dividing {a} and {b}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(*y))),
|
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(*y))),
|
||||||
(V::Complex(x), V::Complex(y)) => {
|
(V::Complex(x), V::Complex(y)) => {
|
||||||
let n = x / y;
|
let n = x / y;
|
||||||
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
|
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
|
||||||
}
|
}
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}")
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////
|
//////////////////////
|
||||||
// exponentiation //
|
// exponentiation //
|
||||||
//////////////////////
|
//////////////////////
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ipow(n: i64, p: u64) -> Option<i64> {
|
fn ipow(n: i64, p: u64) -> Option<i64> {
|
||||||
match (n, p) {
|
match (n, p) {
|
||||||
(0, 0) => None,
|
(0, 0) => None,
|
||||||
(0, _) => Some(0),
|
(0, _) => Some(0),
|
||||||
(_, 0) => Some(1),
|
(_, 0) => Some(1),
|
||||||
(1, _) => Some(1),
|
(1, _) => Some(1),
|
||||||
(-1, p) => (-1_i64).checked_pow((p % 2) as u32),
|
(-1, p) => (-1_i64).checked_pow((p % 2) as u32),
|
||||||
(_, p) if p > u32::MAX as u64 => None,
|
(_, p) if p > u32::MAX as u64 => None,
|
||||||
(n, p) => n.checked_pow(p as u32),
|
(n, p) => n.checked_pow(p as u32),
|
||||||
}
|
}
|
||||||
|
@ -279,12 +319,12 @@ fn ipow(n: i64, p: u64) -> Option<i64> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
|
fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
|
||||||
match p {
|
match p {
|
||||||
i64::MIN => match (n, d) {
|
i64::MIN => match (n, d) {
|
||||||
(0, _) => Some((0, 1)),
|
(0, _) => Some((0, 1)),
|
||||||
(1, 1) => Some((1, 1)),
|
(1, 1) => Some((1, 1)),
|
||||||
(-1, 1) => Some((-1, 1)),
|
(-1, 1) => Some((-1, 1)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
},
|
||||||
0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)),
|
0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)),
|
||||||
_ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)),
|
_ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)),
|
||||||
}
|
}
|
||||||
|
@ -294,45 +334,54 @@ impl Value {
|
||||||
pub fn pow(self, rhs: Value) -> Result<Self> {
|
pub fn pow(self, rhs: Value) -> Result<Self> {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
|
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
|
||||||
if x.is_zero() && *y == 0 {
|
if x.is_zero() && *y == 0 {
|
||||||
throw!(*SYM_VALUE_ERROR, "rational zero to integer zero power")
|
throw!(*SYM_VALUE_ERROR, "rational zero to integer zero power")
|
||||||
}
|
}
|
||||||
let Some(v) = rpow(*(*x).numer(), *(*x).denom(), *y) else {
|
let Some(v) = rpow(*(*x).numer(), *(*x).denom(), *y) else {
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when raising {self} to the power {rhs}")
|
throw!(
|
||||||
};
|
*SYM_VALUE_ERROR,
|
||||||
|
"overflow when raising {self} to the power {rhs}"
|
||||||
|
)
|
||||||
|
};
|
||||||
return Ok(V::Ratio(v.into()))
|
return Ok(V::Ratio(v.into()))
|
||||||
}
|
}
|
||||||
let (a, b) = promote(self, rhs);
|
let (a, b) = promote(self, rhs);
|
||||||
match (&a, &b) {
|
match (&a, &b) {
|
||||||
(V::Int(0), V::Int(0))
|
(V::Int(0), V::Int(0)) => {
|
||||||
=> throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power"),
|
throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power")
|
||||||
(V::Int(x), V::Int(y @ 0..)) => if let Some(v) = ipow(*x, *y as u64) {
|
}
|
||||||
Ok(V::Int(v))
|
(V::Int(x), V::Int(y @ 0..)) => {
|
||||||
} else {
|
if let Some(v) = ipow(*x, *y as u64) {
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when raising {a} to the power {b}")
|
Ok(V::Int(v))
|
||||||
},
|
} else {
|
||||||
(V::Int(x), V::Int(y)) => if let Some(v) = rpow(*x, 1, *y) {
|
throw!(
|
||||||
Ok(V::Ratio(v.into()))
|
*SYM_VALUE_ERROR,
|
||||||
} else {
|
"overflow when raising {a} to the power {b}"
|
||||||
throw!(*SYM_VALUE_ERROR, "overflow when raising {a} to the power {b}")
|
)
|
||||||
},
|
}
|
||||||
(V::Ratio(x), V::Ratio(y))
|
}
|
||||||
=> Ok(V::Float(x.to_f64().powf(y.to_f64()))),
|
(V::Int(x), V::Int(y)) => {
|
||||||
(V::Float(x), V::Float(y))
|
if let Some(v) = rpow(*x, 1, *y) {
|
||||||
=> Ok(V::Float(x.powf(*y))),
|
Ok(V::Ratio(v.into()))
|
||||||
(V::Complex(x), V::Complex(y))
|
} else {
|
||||||
=> Ok(V::Complex(x.powc(*y))),
|
throw!(
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}")
|
*SYM_VALUE_ERROR,
|
||||||
|
"overflow when raising {a} to the power {b}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(V::Ratio(x), V::Ratio(y)) => Ok(V::Float(x.to_f64().powf(y.to_f64()))),
|
||||||
|
(V::Float(x), V::Float(y)) => Ok(V::Float(x.powf(*y))),
|
||||||
|
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x.powc(*y))),
|
||||||
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
// Bitwise operations //
|
// Bitwise operations //
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
||||||
|
|
||||||
impl Shl<Value> for Value {
|
impl Shl<Value> for Value {
|
||||||
type Output = Result<Value>;
|
type Output = Result<Value>;
|
||||||
|
|
||||||
|
@ -340,7 +389,7 @@ impl Shl<Value> for Value {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(V::Int(a), V::Int(b)) => Ok(Value::Int(((a as u64) << b as u64) as i64)),
|
(V::Int(a), V::Int(b)) => Ok(Value::Int(((a as u64) << b as u64) as i64)),
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} left by {r:#}")
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} left by {r:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -352,7 +401,7 @@ impl Shr<Value> for Value {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(V::Int(a), V::Int(b)) => Ok(Value::Int((a as u64 >> b as u64) as i64)),
|
(V::Int(a), V::Int(b)) => Ok(Value::Int((a as u64 >> b as u64) as i64)),
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}")
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,7 +413,7 @@ impl BitAnd<Value> for Value {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(V::Int(a), V::Int(b)) => Ok(Value::Int(a & b)),
|
(V::Int(a), V::Int(b)) => Ok(Value::Int(a & b)),
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}")
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -376,7 +425,7 @@ impl BitXor<Value> for Value {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(V::Int(a), V::Int(b)) => Ok(Value::Int(a ^ b)),
|
(V::Int(a), V::Int(b)) => Ok(Value::Int(a ^ b)),
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}")
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -388,47 +437,46 @@ impl BitOr<Value> for Value {
|
||||||
use Value as V;
|
use Value as V;
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(V::Int(a), V::Int(b)) => Ok(Value::Int(a | b)),
|
(V::Int(a), V::Int(b)) => Ok(Value::Int(a | b)),
|
||||||
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}")
|
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// Equality and ordering //
|
// Equality and ordering //
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
impl PartialEq for Value {
|
impl PartialEq for Value {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
use Value as V;
|
|
||||||
use super::range::RangeType as Rty;
|
use super::range::RangeType as Rty;
|
||||||
|
use Value as V;
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(V::Nil, V::Nil) => true,
|
(V::Nil, V::Nil) => true,
|
||||||
(V::Bool(a), V::Bool(b)) => a == b,
|
(V::Bool(a), V::Bool(b)) => a == b,
|
||||||
(V::Int(a), V::Int(b)) => *a == *b,
|
(V::Int(a), V::Int(b)) => *a == *b,
|
||||||
(V::Ratio(a), V::Ratio(b)) => *a == *b,
|
(V::Ratio(a), V::Ratio(b)) => *a == *b,
|
||||||
(V::Float(a), V::Float(b)) => *a == *b,
|
(V::Float(a), V::Float(b)) => *a == *b,
|
||||||
(V::Complex(a), V::Complex(b)) => *a == *b,
|
(V::Complex(a), V::Complex(b)) => *a == *b,
|
||||||
(V::Int(a), V::Ratio(b)) => Rational64::from(*a) == *b,
|
(V::Int(a), V::Ratio(b)) => Rational64::from(*a) == *b,
|
||||||
(V::Ratio(a), V::Int(b)) => *a == Rational64::from(*b),
|
(V::Ratio(a), V::Int(b)) => *a == Rational64::from(*b),
|
||||||
(V::Int(a), V::Float(b)) => *a as f64 == *b,
|
(V::Int(a), V::Float(b)) => *a as f64 == *b,
|
||||||
(V::Float(a), V::Int(b)) => *a == *b as f64,
|
(V::Float(a), V::Int(b)) => *a == *b as f64,
|
||||||
(V::Int(a), V::Complex(b)) => Complex64::from(*a as f64) == *b,
|
(V::Int(a), V::Complex(b)) => Complex64::from(*a as f64) == *b,
|
||||||
(V::Complex(a), V::Int(b)) => *a == Complex64::from(*b as f64),
|
(V::Complex(a), V::Int(b)) => *a == Complex64::from(*b as f64),
|
||||||
(V::Ratio(a), V::Float(b)) => a.to_f64() == *b,
|
(V::Ratio(a), V::Float(b)) => a.to_f64() == *b,
|
||||||
(V::Float(a), V::Ratio(b)) => *a == b.to_f64(),
|
(V::Float(a), V::Ratio(b)) => *a == b.to_f64(),
|
||||||
(V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64()) == *b,
|
(V::Ratio(a), V::Complex(b)) => Complex64::from(a.to_f64()) == *b,
|
||||||
(V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64()),
|
(V::Complex(a), V::Ratio(b)) => *a == Complex64::from(b.to_f64()),
|
||||||
(V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b,
|
(V::Float(a), V::Complex(b)) => Complex64::from(*a) == *b,
|
||||||
(V::Complex(a), V::Float(b)) => *a == Complex64::from(*b),
|
(V::Complex(a), V::Float(b)) => *a == Complex64::from(*b),
|
||||||
(V::String(a), V::String(b)) => *a == *b,
|
(V::String(a), V::String(b)) => *a == *b,
|
||||||
(V::List(a), V::List(b)) => *a.borrow() == *b.borrow(),
|
(V::List(a), V::List(b)) => *a.borrow() == *b.borrow(),
|
||||||
(V::Symbol(a), V::Symbol(b)) => a == b,
|
(V::Symbol(a), V::Symbol(b)) => a == b,
|
||||||
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
|
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
|
||||||
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
|
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
|
||||||
(Rty::Open, Rty::Open)
|
(Rty::Open, Rty::Open) | (Rty::Closed, Rty::Closed) => {
|
||||||
| (Rty::Closed, Rty::Closed) => a.start == b.start && a.stop == b.stop,
|
a.start == b.start && a.stop == b.stop
|
||||||
|
}
|
||||||
(Rty::Open, Rty::Closed) => a.start == b.start && a.stop == b.stop - 1,
|
(Rty::Open, Rty::Closed) => a.start == b.start && a.stop == b.stop - 1,
|
||||||
(Rty::Closed, Rty::Open) => a.start == b.start && a.stop - 1 == b.stop,
|
(Rty::Closed, Rty::Open) => a.start == b.start && a.stop - 1 == b.stop,
|
||||||
(Rty::Endless, Rty::Endless) => a.start == b.start,
|
(Rty::Endless, Rty::Endless) => a.start == b.start,
|
||||||
|
@ -464,12 +512,10 @@ impl PartialOrd for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////
|
////////////
|
||||||
// misc //
|
// misc //
|
||||||
////////////
|
////////////
|
||||||
|
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
|
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
|
||||||
match self.partial_cmp(other) {
|
match self.partial_cmp(other) {
|
||||||
|
@ -485,7 +531,7 @@ impl Value {
|
||||||
let mut l = l1.borrow().clone();
|
let mut l = l1.borrow().clone();
|
||||||
l.extend_from_slice(&l2.borrow());
|
l.extend_from_slice(&l2.borrow());
|
||||||
Ok(l.into())
|
Ok(l.into())
|
||||||
},
|
}
|
||||||
(V::String(s1), V::String(s2)) => {
|
(V::String(s1), V::String(s2)) => {
|
||||||
let mut s: LString = s1.as_ref().to_owned();
|
let mut s: LString = s1.as_ref().to_owned();
|
||||||
s.push_lstr(s2);
|
s.push_lstr(s2);
|
||||||
|
@ -507,23 +553,40 @@ impl Value {
|
||||||
let mut l = list.borrow().clone();
|
let mut l = list.borrow().clone();
|
||||||
l.push(val);
|
l.push(val);
|
||||||
Ok(l.into())
|
Ok(l.into())
|
||||||
},
|
}
|
||||||
lhs => throw!(*SYM_TYPE_ERROR, "cannot append to {lhs:#}"),
|
lhs => throw!(*SYM_TYPE_ERROR, "cannot append to {lhs:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range(&self, other: &Self, closed: bool) -> Result<Self> {
|
pub fn range(&self, other: &Self, closed: bool) -> Result<Self> {
|
||||||
if let (Value::Int(start), Value::Int(stop)) = (self, other) {
|
if let (Value::Int(start), Value::Int(stop)) = (self, other) {
|
||||||
let ty = if closed { RangeType::Closed } else { RangeType::Open };
|
let ty = if closed {
|
||||||
Ok(Range { start: *start, stop: *stop, ty }.into())
|
RangeType::Closed
|
||||||
|
} else {
|
||||||
|
RangeType::Open
|
||||||
|
};
|
||||||
|
Ok(Range {
|
||||||
|
start: *start,
|
||||||
|
stop: *stop,
|
||||||
|
ty,
|
||||||
|
}
|
||||||
|
.into())
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_TYPE_ERROR, "cannot create range between {self:#} and {other:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"cannot create range between {self:#} and {other:#}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range_endless(&self) -> Result<Self> {
|
pub fn range_endless(&self) -> Result<Self> {
|
||||||
if let Value::Int(start) = self {
|
if let Value::Int(start) = self {
|
||||||
Ok(Range { start: *start, stop: 0, ty: RangeType::Endless }.into())
|
Ok(Range {
|
||||||
|
start: *start,
|
||||||
|
stop: 0,
|
||||||
|
ty: RangeType::Endless,
|
||||||
|
}
|
||||||
|
.into())
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}")
|
throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}")
|
||||||
}
|
}
|
||||||
|
@ -543,7 +606,7 @@ impl Value {
|
||||||
pub fn iter_pack(v: Option<Self>) -> Self {
|
pub fn iter_pack(v: Option<Self>) -> Self {
|
||||||
match v {
|
match v {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => Value::from(*SYM_END_ITERATION)
|
None => Value::from(*SYM_END_ITERATION),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,11 +616,12 @@ impl Value {
|
||||||
Self::Range(range) => {
|
Self::Range(range) => {
|
||||||
let range_iter = RefCell::new(range.into_iter());
|
let range_iter = RefCell::new(range.into_iter());
|
||||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||||
Ok(Value::iter_pack(range_iter.borrow_mut().next()
|
Ok(Value::iter_pack(
|
||||||
.map(Value::from)))
|
range_iter.borrow_mut().next().map(Value::from),
|
||||||
|
))
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[range]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[range]")).into())
|
||||||
},
|
}
|
||||||
Self::String(s) => {
|
Self::String(s) => {
|
||||||
let byte_pos = RefCell::new(0);
|
let byte_pos = RefCell::new(0);
|
||||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||||
|
@ -570,7 +634,7 @@ impl Value {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[string]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[string]")).into())
|
||||||
},
|
}
|
||||||
Self::List(list) => {
|
Self::List(list) => {
|
||||||
let idx = RefCell::new(0);
|
let idx = RefCell::new(0);
|
||||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||||
|
@ -583,16 +647,17 @@ impl Value {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[list]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[list]")).into())
|
||||||
},
|
}
|
||||||
Self::Table(table) => {
|
Self::Table(table) => {
|
||||||
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
|
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
|
||||||
let keys = RefCell::new(keys.into_iter());
|
let keys = RefCell::new(keys.into_iter());
|
||||||
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
|
||||||
Ok(Value::iter_pack(keys.borrow_mut().next()
|
Ok(Value::iter_pack(
|
||||||
.map(HashValue::into_inner)))
|
keys.borrow_mut().next().map(HashValue::into_inner),
|
||||||
|
))
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[table]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[table]")).into())
|
||||||
},
|
}
|
||||||
_ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"),
|
_ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum RangeType {
|
pub enum RangeType {
|
||||||
Open, Closed, Endless,
|
Open,
|
||||||
|
Closed,
|
||||||
|
Endless,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
@ -31,7 +32,7 @@ impl Range {
|
||||||
|
|
||||||
impl IntoIterator for Range {
|
impl IntoIterator for Range {
|
||||||
type Item = i64;
|
type Item = i64;
|
||||||
type IntoIter = Box<dyn Iterator<Item=i64>>;
|
type IntoIter = Box<dyn Iterator<Item = i64>>;
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
match self.ty {
|
match self.ty {
|
||||||
RangeType::Open => Box::new(self.start..self.stop),
|
RangeType::Open => Box::new(self.start..self.stop),
|
||||||
|
@ -40,5 +41,3 @@ impl IntoIterator for Range {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,26 @@
|
||||||
use std::{cmp::Ordering, collections::HashMap, rc::Rc, sync::{atomic::AtomicBool, Arc}};
|
use std::{
|
||||||
|
cmp::Ordering,
|
||||||
|
collections::HashMap,
|
||||||
|
rc::Rc,
|
||||||
|
sync::{atomic::AtomicBool, Arc},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{parser::ast::{BinaryOp, UnaryOp}, chunk::Instruction, exception::{throw, Exception, Result}, lstring::LStr, symbol::{Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR}, value::{function::{FuncAttrs, Function, NativeFunc}, Value}};
|
use crate::{
|
||||||
|
chunk::Instruction,
|
||||||
|
exception::{throw, Exception, Result},
|
||||||
|
lstring::LStr,
|
||||||
|
parser::ast::{BinaryOp, UnaryOp},
|
||||||
|
symbol::{Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR},
|
||||||
|
value::{
|
||||||
|
function::{FuncAttrs, Function, NativeFunc},
|
||||||
|
Value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
struct TryFrame { idx: usize, stack_len: usize }
|
struct TryFrame {
|
||||||
|
idx: usize,
|
||||||
|
stack_len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
struct CallFrame {
|
struct CallFrame {
|
||||||
func: Rc<Function>,
|
func: Rc<Function>,
|
||||||
|
@ -78,25 +96,28 @@ fn get_call_outcome(args: Vec<Value>) -> Result<CallOutcome> {
|
||||||
throw!(*SYM_TYPE_ERROR, "cannot call non-function {f:#}")
|
throw!(*SYM_TYPE_ERROR, "cannot call non-function {f:#}")
|
||||||
};
|
};
|
||||||
let argc = args.len() - 1;
|
let argc = args.len() - 1;
|
||||||
match argc.cmp(&attrs.arity) {
|
match argc.cmp(&attrs.arity) {
|
||||||
Ordering::Equal => Ok(CallOutcome::Call(args)),
|
Ordering::Equal => Ok(CallOutcome::Call(args)),
|
||||||
Ordering::Greater => throw!(*SYM_TYPE_ERROR, "too many arguments for function"),
|
Ordering::Greater => throw!(*SYM_TYPE_ERROR, "too many arguments for function"),
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
let remaining = attrs.arity - argc;
|
let remaining = attrs.arity - argc;
|
||||||
let f = f.clone();
|
let f = f.clone();
|
||||||
let nf = move |vm: &mut Vm, inner_args: Vec<Value>| {
|
let nf = move |vm: &mut Vm, inner_args: Vec<Value>| {
|
||||||
let mut ia = inner_args.into_iter();
|
let mut ia = inner_args.into_iter();
|
||||||
ia.next();
|
ia.next();
|
||||||
let args: Vec<Value> = args.clone().into_iter().chain(ia).collect();
|
let args: Vec<Value> = args.clone().into_iter().chain(ia).collect();
|
||||||
vm.call_value(f.clone(), args)
|
vm.call_value(f.clone(), args)
|
||||||
};
|
};
|
||||||
let nf = NativeFunc {
|
let nf = NativeFunc {
|
||||||
attrs: FuncAttrs { arity: remaining, name: None },
|
attrs: FuncAttrs {
|
||||||
func: Box::new(nf),
|
arity: remaining,
|
||||||
};
|
name: None,
|
||||||
Ok(CallOutcome::Partial(nf.into()))
|
},
|
||||||
}
|
func: Box::new(nf),
|
||||||
}
|
};
|
||||||
|
Ok(CallOutcome::Partial(nf.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vm {
|
impl Vm {
|
||||||
|
@ -119,7 +140,9 @@ impl Vm {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_global_name<'a, S>(&mut self, name: S, val: Value)
|
pub fn set_global_name<'a, S>(&mut self, name: S, val: Value)
|
||||||
where S: Into<&'a LStr> {
|
where
|
||||||
|
S: Into<&'a LStr>,
|
||||||
|
{
|
||||||
self.globals.insert(Symbol::get(name.into()), val);
|
self.globals.insert(Symbol::get(name.into()), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,8 +161,8 @@ impl Vm {
|
||||||
CallOutcome::Call(args) => match value {
|
CallOutcome::Call(args) => match value {
|
||||||
Value::Function(f) => self.run_function(f, args),
|
Value::Function(f) => self.run_function(f, args),
|
||||||
Value::NativeFunc(f) => (f.func)(self, args),
|
Value::NativeFunc(f) => (f.func)(self, args),
|
||||||
_ => unreachable!("already verified by calling get_call_type")
|
_ => unreachable!("already verified by calling get_call_type"),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +198,7 @@ impl Vm {
|
||||||
while let Some(try_frame) = frame.try_frames.pop() {
|
while let Some(try_frame) = frame.try_frames.pop() {
|
||||||
let table = &frame.func.chunk.try_tables[try_frame.idx];
|
let table = &frame.func.chunk.try_tables[try_frame.idx];
|
||||||
for catch in &table.catches {
|
for catch in &table.catches {
|
||||||
if catch.types.is_none() || catch.types.as_ref().unwrap().contains(&exc.ty) {
|
if catch.types.is_none() || catch.types.as_ref().unwrap().contains(&exc.ty) {
|
||||||
frame.ip = catch.addr;
|
frame.ip = catch.addr;
|
||||||
frame.locals.truncate(table.local_count);
|
frame.locals.truncate(table.local_count);
|
||||||
self.stack.truncate(try_frame.stack_len);
|
self.stack.truncate(try_frame.stack_len);
|
||||||
|
@ -209,7 +232,10 @@ impl Vm {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_interrupt(&mut self) -> Result<()> {
|
fn check_interrupt(&mut self) -> Result<()> {
|
||||||
if self.interrupt.fetch_and(false, std::sync::atomic::Ordering::Relaxed) {
|
if self
|
||||||
|
.interrupt
|
||||||
|
.fetch_and(false, std::sync::atomic::Ordering::Relaxed)
|
||||||
|
{
|
||||||
throw!(*SYM_INTERRUPTED)
|
throw!(*SYM_INTERRUPTED)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -222,17 +248,13 @@ impl Vm {
|
||||||
// do nothing
|
// do nothing
|
||||||
I::Nop => (),
|
I::Nop => (),
|
||||||
// [] -> [locals[n]]
|
// [] -> [locals[n]]
|
||||||
I::LoadLocal(n)
|
I::LoadLocal(n) => self.push(frame.locals[usize::from(n)].clone()),
|
||||||
=> self.push(frame.locals[usize::from(n)].clone()),
|
|
||||||
// [x] -> [], locals[n] = x
|
// [x] -> [], locals[n] = x
|
||||||
I::StoreLocal(n)
|
I::StoreLocal(n) => frame.locals[usize::from(n)] = self.pop(),
|
||||||
=> frame.locals[usize::from(n)] = self.pop(),
|
|
||||||
// [x] -> [], locals.push(x)
|
// [x] -> [], locals.push(x)
|
||||||
I::NewLocal
|
I::NewLocal => frame.locals.push(self.pop()),
|
||||||
=> frame.locals.push(self.pop()),
|
|
||||||
// locals.pop_n(n)
|
// locals.pop_n(n)
|
||||||
I::DropLocal(n)
|
I::DropLocal(n) => frame.locals.truncate(frame.locals.len() - usize::from(n)),
|
||||||
=> frame.locals.truncate(frame.locals.len() - usize::from(n)),
|
|
||||||
// [] -> [globals[s]]
|
// [] -> [globals[s]]
|
||||||
I::LoadGlobal(s) => {
|
I::LoadGlobal(s) => {
|
||||||
let sym = unsafe { s.to_symbol_unchecked() };
|
let sym = unsafe { s.to_symbol_unchecked() };
|
||||||
|
@ -241,65 +263,67 @@ impl Vm {
|
||||||
None => throw!(*SYM_NAME_ERROR, "undefined global {}", sym.name()),
|
None => throw!(*SYM_NAME_ERROR, "undefined global {}", sym.name()),
|
||||||
};
|
};
|
||||||
self.push(v);
|
self.push(v);
|
||||||
},
|
}
|
||||||
// [x] -> [], globals[s] = x
|
// [x] -> [], globals[s] = x
|
||||||
I::StoreGlobal(s) => {
|
I::StoreGlobal(s) => {
|
||||||
let sym = unsafe { s.to_symbol_unchecked() };
|
let sym = unsafe { s.to_symbol_unchecked() };
|
||||||
let v = self.pop();
|
let v = self.pop();
|
||||||
self.globals.insert(sym, v);
|
self.globals.insert(sym, v);
|
||||||
},
|
}
|
||||||
I::CloseOver(n) => {
|
I::CloseOver(n) => {
|
||||||
let n = usize::from(n);
|
let n = usize::from(n);
|
||||||
let v = std::mem::replace(&mut frame.locals[n], Value::Nil);
|
let v = std::mem::replace(&mut frame.locals[n], Value::Nil);
|
||||||
let v = v.to_cell();
|
let v = v.to_cell();
|
||||||
frame.locals[n] = v.clone();
|
frame.locals[n] = v.clone();
|
||||||
self.push(v);
|
self.push(v);
|
||||||
},
|
}
|
||||||
I::Closure(n) => {
|
I::Closure(n) => {
|
||||||
let f = frame.func.chunk.consts[usize::from(n)].clone();
|
let f = frame.func.chunk.consts[usize::from(n)].clone();
|
||||||
let Value::Function(f) = f else {
|
let Value::Function(f) = f else {
|
||||||
panic!("attempt to build closure from non-closure constant")
|
panic!("attempt to build closure from non-closure constant")
|
||||||
};
|
};
|
||||||
let mut f = f.as_ref().clone();
|
let mut f = f.as_ref().clone();
|
||||||
|
|
||||||
let captured: Vec<_> = self.pop_n(f.state.len()).into_iter()
|
let captured: Vec<_> = self
|
||||||
.map(|v| {
|
.pop_n(f.state.len())
|
||||||
let Value::Cell(v) = v else {
|
.into_iter()
|
||||||
panic!("attempt to build closure from non-cell local");
|
.map(|v| {
|
||||||
};
|
let Value::Cell(v) = v else {
|
||||||
v
|
panic!("attempt to build closure from non-cell local");
|
||||||
}).collect();
|
};
|
||||||
|
v
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
f.state = captured.into_boxed_slice();
|
f.state = captured.into_boxed_slice();
|
||||||
self.push(f.into());
|
self.push(f.into());
|
||||||
},
|
}
|
||||||
I::LoadUpvalue(n) => {
|
I::LoadUpvalue(n) => {
|
||||||
let v = frame.func.state[usize::from(n)].clone();
|
let v = frame.func.state[usize::from(n)].clone();
|
||||||
self.push(v.borrow().clone());
|
self.push(v.borrow().clone());
|
||||||
},
|
}
|
||||||
I::StoreUpvalue(n) => {
|
I::StoreUpvalue(n) => {
|
||||||
let v = frame.func.state[usize::from(n)].clone();
|
let v = frame.func.state[usize::from(n)].clone();
|
||||||
*v.borrow_mut() = self.pop();
|
*v.borrow_mut() = self.pop();
|
||||||
},
|
}
|
||||||
I::ContinueUpvalue(n) => {
|
I::ContinueUpvalue(n) => {
|
||||||
let v = frame.func.state[usize::from(n)].clone();
|
let v = frame.func.state[usize::from(n)].clone();
|
||||||
self.push(Value::Cell(v));
|
self.push(Value::Cell(v));
|
||||||
},
|
}
|
||||||
I::LoadClosedLocal(n) => {
|
I::LoadClosedLocal(n) => {
|
||||||
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
|
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
|
||||||
panic!("attempt to load from closed non-cell local");
|
panic!("attempt to load from closed non-cell local");
|
||||||
};
|
};
|
||||||
self.push(c.borrow().clone());
|
self.push(c.borrow().clone());
|
||||||
},
|
}
|
||||||
I::StoreClosedLocal(n) => {
|
I::StoreClosedLocal(n) => {
|
||||||
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
|
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
|
||||||
panic!("attempt to store to closed non-cell local");
|
panic!("attempt to store to closed non-cell local");
|
||||||
};
|
};
|
||||||
*c.borrow_mut() = self.pop();
|
*c.borrow_mut() = self.pop();
|
||||||
},
|
}
|
||||||
// [] -> [consts[n]]
|
// [] -> [consts[n]]
|
||||||
I::Const(n)
|
I::Const(n) => self.push(frame.func.chunk.consts[usize::from(n)].clone()),
|
||||||
=> self.push(frame.func.chunk.consts[usize::from(n)].clone()),
|
|
||||||
// [] -> [nil]
|
// [] -> [nil]
|
||||||
I::Nil => self.push(Value::Nil),
|
I::Nil => self.push(Value::Nil),
|
||||||
// [] -> [b]
|
// [] -> [b]
|
||||||
|
@ -308,7 +332,7 @@ impl Vm {
|
||||||
I::Symbol(s) => {
|
I::Symbol(s) => {
|
||||||
let sym = unsafe { Symbol::from_id_unchecked(u32::from(s)) };
|
let sym = unsafe { Symbol::from_id_unchecked(u32::from(s)) };
|
||||||
self.push(Value::Symbol(sym));
|
self.push(Value::Symbol(sym));
|
||||||
},
|
}
|
||||||
// [] -> [n]
|
// [] -> [n]
|
||||||
I::Int(n) => self.push(Value::Int(i64::from(n))),
|
I::Int(n) => self.push(Value::Int(i64::from(n))),
|
||||||
// [x] -> [x,x]
|
// [x] -> [x,x]
|
||||||
|
@ -317,38 +341,44 @@ impl Vm {
|
||||||
I::DupTwo => {
|
I::DupTwo => {
|
||||||
self.push(self.stack[self.stack.len() - 2].clone());
|
self.push(self.stack[self.stack.len() - 2].clone());
|
||||||
self.push(self.stack[self.stack.len() - 2].clone());
|
self.push(self.stack[self.stack.len() - 2].clone());
|
||||||
},
|
}
|
||||||
// [a0,a1...an] -> []
|
// [a0,a1...an] -> []
|
||||||
I::Drop(n) => for _ in 0..u32::from(n) { self.pop(); },
|
I::Drop(n) => {
|
||||||
|
for _ in 0..u32::from(n) {
|
||||||
|
self.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
// [x,y] -> [y,x]
|
// [x,y] -> [y,x]
|
||||||
I::Swap => {
|
I::Swap => {
|
||||||
let len = self.stack.len();
|
let len = self.stack.len();
|
||||||
self.stack.swap(len - 1, len - 2);
|
self.stack.swap(len - 1, len - 2);
|
||||||
},
|
}
|
||||||
// [x,y] -> [y op x]
|
// [x,y] -> [y op x]
|
||||||
I::BinaryOp(op) => {
|
I::BinaryOp(op) => {
|
||||||
let b = self.pop();
|
let b = self.pop();
|
||||||
let a = self.pop();
|
let a = self.pop();
|
||||||
self.push(binary_op(op, a, b)?);
|
self.push(binary_op(op, a, b)?);
|
||||||
},
|
}
|
||||||
// [x] -> [op x]
|
// [x] -> [op x]
|
||||||
I::UnaryOp(op) => {
|
I::UnaryOp(op) => {
|
||||||
let a = self.pop();
|
let a = self.pop();
|
||||||
self.push(unary_op(op, a)?);
|
self.push(unary_op(op, a)?);
|
||||||
},
|
}
|
||||||
// [a0,a1...an] -.> [[a0,a1...an]]
|
// [a0,a1...an] -.> [[a0,a1...an]]
|
||||||
I::NewList(n) => {
|
I::NewList(n) => {
|
||||||
let list = self.pop_n(n as usize);
|
let list = self.pop_n(n as usize);
|
||||||
self.push(list.into());
|
self.push(list.into());
|
||||||
},
|
}
|
||||||
// [l,a0,a1...an] -.> [l ++ [a0,a1...an]]
|
// [l,a0,a1...an] -.> [l ++ [a0,a1...an]]
|
||||||
I::GrowList(n) => {
|
I::GrowList(n) => {
|
||||||
let ext = self.pop_n(n as usize);
|
let ext = self.pop_n(n as usize);
|
||||||
let list = self.pop();
|
let list = self.pop();
|
||||||
let Value::List(list) = list else { panic!("not a list") };
|
let Value::List(list) = list else {
|
||||||
|
panic!("not a list")
|
||||||
|
};
|
||||||
list.borrow_mut().extend(ext);
|
list.borrow_mut().extend(ext);
|
||||||
self.push(Value::List(list));
|
self.push(Value::List(list));
|
||||||
},
|
}
|
||||||
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
|
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
|
||||||
I::NewTable(n) => {
|
I::NewTable(n) => {
|
||||||
let mut table = HashMap::new();
|
let mut table = HashMap::new();
|
||||||
|
@ -358,12 +388,14 @@ impl Vm {
|
||||||
table.insert(k.try_into()?, v);
|
table.insert(k.try_into()?, v);
|
||||||
}
|
}
|
||||||
self.push(table.into());
|
self.push(table.into());
|
||||||
},
|
}
|
||||||
// [t,k0,v0...kn,vn] -> [t ++ {k0=v0...kn=vn}]
|
// [t,k0,v0...kn,vn] -> [t ++ {k0=v0...kn=vn}]
|
||||||
I::GrowTable(n) => {
|
I::GrowTable(n) => {
|
||||||
let mut ext = self.pop_n(2 * n as usize);
|
let mut ext = self.pop_n(2 * n as usize);
|
||||||
let table = self.pop();
|
let table = self.pop();
|
||||||
let Value::Table(table) = table else { panic!("not a table") };
|
let Value::Table(table) = table else {
|
||||||
|
panic!("not a table")
|
||||||
|
};
|
||||||
let mut table_ref = table.borrow_mut();
|
let mut table_ref = table.borrow_mut();
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
// can't panic: pop_n checked that ext would have len 2*n
|
// can't panic: pop_n checked that ext would have len 2*n
|
||||||
|
@ -373,13 +405,13 @@ impl Vm {
|
||||||
}
|
}
|
||||||
drop(table_ref);
|
drop(table_ref);
|
||||||
self.push(Value::Table(table));
|
self.push(Value::Table(table));
|
||||||
},
|
}
|
||||||
// [ct, idx] -> [ct!idx]
|
// [ct, idx] -> [ct!idx]
|
||||||
I::Index => {
|
I::Index => {
|
||||||
let idx = self.pop();
|
let idx = self.pop();
|
||||||
let ct = self.pop();
|
let ct = self.pop();
|
||||||
self.push(ct.index(idx)?);
|
self.push(ct.index(idx)?);
|
||||||
},
|
}
|
||||||
// [ct, idx, v] -> [v], ct!idx = v
|
// [ct, idx, v] -> [v], ct!idx = v
|
||||||
I::StoreIndex => {
|
I::StoreIndex => {
|
||||||
let v = self.pop();
|
let v = self.pop();
|
||||||
|
@ -387,27 +419,31 @@ impl Vm {
|
||||||
let ct = self.pop();
|
let ct = self.pop();
|
||||||
ct.store_index(self, idx, v.clone())?;
|
ct.store_index(self, idx, v.clone())?;
|
||||||
self.push(v);
|
self.push(v);
|
||||||
},
|
}
|
||||||
// ip = n
|
// ip = n
|
||||||
I::Jump(n) => {
|
I::Jump(n) => {
|
||||||
self.check_interrupt()?;
|
self.check_interrupt()?;
|
||||||
frame.ip = usize::from(n);
|
frame.ip = usize::from(n);
|
||||||
},
|
}
|
||||||
// [v] ->, [], if v then ip = n
|
// [v] ->, [], if v then ip = n
|
||||||
I::JumpTrue(n) => if self.pop().truthy() {
|
I::JumpTrue(n) => {
|
||||||
self.check_interrupt()?;
|
if self.pop().truthy() {
|
||||||
frame.ip = usize::from(n);
|
self.check_interrupt()?;
|
||||||
},
|
frame.ip = usize::from(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
// [v] ->, [], if not v then ip = n
|
// [v] ->, [], if not v then ip = n
|
||||||
I::JumpFalse(n) => if !self.pop().truthy() {
|
I::JumpFalse(n) => {
|
||||||
self.check_interrupt()?;
|
if !self.pop().truthy() {
|
||||||
frame.ip = usize::from(n);
|
self.check_interrupt()?;
|
||||||
},
|
frame.ip = usize::from(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
// [v] -> [iter(v)]
|
// [v] -> [iter(v)]
|
||||||
I::IterBegin => {
|
I::IterBegin => {
|
||||||
let iter = self.pop().to_iter_function()?;
|
let iter = self.pop().to_iter_function()?;
|
||||||
self.push(iter);
|
self.push(iter);
|
||||||
},
|
}
|
||||||
// [i,cell(v)] -> [i,v]
|
// [i,cell(v)] -> [i,v]
|
||||||
// [i,nil] -> [], ip = n
|
// [i,nil] -> [], ip = n
|
||||||
I::IterTest(n) => {
|
I::IterTest(n) => {
|
||||||
|
@ -417,19 +453,19 @@ impl Vm {
|
||||||
self.pop();
|
self.pop();
|
||||||
frame.ip = usize::from(n);
|
frame.ip = usize::from(n);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// try_frames.push(t, stack.len())
|
// try_frames.push(t, stack.len())
|
||||||
I::BeginTry(t) => {
|
I::BeginTry(t) => {
|
||||||
let tryframe = TryFrame {
|
let tryframe = TryFrame {
|
||||||
idx: usize::from(t),
|
idx: usize::from(t),
|
||||||
stack_len: self.stack.len()
|
stack_len: self.stack.len(),
|
||||||
};
|
};
|
||||||
frame.try_frames.push(tryframe);
|
frame.try_frames.push(tryframe);
|
||||||
},
|
}
|
||||||
// try_frames.pop()
|
// try_frames.pop()
|
||||||
I::EndTry => {
|
I::EndTry => {
|
||||||
frame.try_frames.pop().expect("no try to pop");
|
frame.try_frames.pop().expect("no try to pop");
|
||||||
},
|
}
|
||||||
// [f,a0,a1...an] -> [f(a0,a1...an)]
|
// [f,a0,a1...an] -> [f(a0,a1...an)]
|
||||||
I::Call(n) => {
|
I::Call(n) => {
|
||||||
let n = usize::from(n);
|
let n = usize::from(n);
|
||||||
|
@ -471,7 +507,6 @@ impl Vm {
|
||||||
|
|
||||||
self.push(res);
|
self.push(res);
|
||||||
} else if let Value::Function(func) = &args[0] {
|
} else if let Value::Function(func) = &args[0] {
|
||||||
|
|
||||||
if self.call_stack.len() + 1 >= self.stack_max {
|
if self.call_stack.len() + 1 >= self.stack_max {
|
||||||
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
|
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
|
||||||
}
|
}
|
||||||
|
@ -482,22 +517,18 @@ impl Vm {
|
||||||
} else {
|
} else {
|
||||||
unreachable!("already verified by calling get_call_type");
|
unreachable!("already verified by calling get_call_type");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// [v] -> [], return v
|
// [v] -> [], return v
|
||||||
I::Return if frame.root => {
|
I::Return if frame.root => return Ok(Some(self.pop())),
|
||||||
return Ok(Some(self.pop()));
|
|
||||||
},
|
|
||||||
// [v] -> [], return v
|
// [v] -> [], return v
|
||||||
I::Return => {
|
I::Return => {
|
||||||
self.check_interrupt()?;
|
self.check_interrupt()?;
|
||||||
*frame = self.call_stack.pop().expect("no root frame");
|
*frame = self.call_stack.pop().expect("no root frame");
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|
|
@ -4,5 +4,5 @@ mod native_func;
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream {
|
pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream {
|
||||||
native_func::native_func(input, annotated_item)
|
native_func::native_func(input, annotated_item)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use syn::{parse::Parse, parse_macro_input, Token};
|
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
|
use syn::{parse::Parse, parse_macro_input, Token};
|
||||||
|
|
||||||
struct NativeFuncArgs {
|
struct NativeFuncArgs {
|
||||||
arity: syn::LitInt,
|
arity: syn::LitInt,
|
||||||
name: Option<syn::LitStr>,
|
name: Option<syn::LitStr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for NativeFuncArgs {
|
impl Parse for NativeFuncArgs {
|
||||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
let arity = input.parse()?;
|
let arity = input.parse()?;
|
||||||
let t: Option<Token![,]> = input.parse()?;
|
let t: Option<Token![,]> = input.parse()?;
|
||||||
if t.is_some() {
|
if t.is_some() {
|
||||||
let name = input.parse()?;
|
let name = input.parse()?;
|
||||||
Ok(Self { arity, name: Some(name) })
|
Ok(Self {
|
||||||
} else {
|
arity,
|
||||||
Ok(Self { arity, name: None })
|
name: Some(name),
|
||||||
}
|
})
|
||||||
|
} else {
|
||||||
|
Ok(Self { arity, name: None })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,14 +37,17 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
|
||||||
let arity = args.arity;
|
let arity = args.arity;
|
||||||
|
|
||||||
let talc_name = match args.name {
|
let talc_name = match args.name {
|
||||||
Some(n) => n.to_token_stream(),
|
Some(n) => n.to_token_stream(),
|
||||||
None => name.to_token_stream(),
|
None => name.to_token_stream(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(itemfn.sig.constness.is_none(), "item must not be const");
|
assert!(itemfn.sig.constness.is_none(), "item must not be const");
|
||||||
assert!(itemfn.sig.asyncness.is_none(), "item must not be async");
|
assert!(itemfn.sig.asyncness.is_none(), "item must not be async");
|
||||||
assert!(itemfn.sig.unsafety.is_none(), "item must not be unsafe");
|
assert!(itemfn.sig.unsafety.is_none(), "item must not be unsafe");
|
||||||
assert!(itemfn.sig.abi.is_none(), "item must not contain an ABI specifier");
|
assert!(
|
||||||
|
itemfn.sig.abi.is_none(),
|
||||||
|
"item must not contain an ABI specifier"
|
||||||
|
);
|
||||||
assert!(itemfn.sig.variadic.is_none(), "item must not be variadic");
|
assert!(itemfn.sig.variadic.is_none(), "item must not be variadic");
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
|
@ -49,7 +55,7 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
|
||||||
::talc_lang::value::function::NativeFunc {
|
::talc_lang::value::function::NativeFunc {
|
||||||
attrs: ::talc_lang::value::function::FuncAttrs{
|
attrs: ::talc_lang::value::function::FuncAttrs{
|
||||||
arity: #arity,
|
arity: #arity,
|
||||||
name: Some(::talc_lang::symbol::symbol!(#talc_name))
|
name: Some(::talc_lang::symbol::symbol!(#talc_name))
|
||||||
},
|
},
|
||||||
func: Box::new(|#inputs| #output #block)
|
func: Box::new(|#inputs| #output #block)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use talc_lang::{exception::{exception, Result}, symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{function::NativeFunc, Value}, vmcall, Vm};
|
use talc_lang::{
|
||||||
|
exception::{exception, Result},
|
||||||
|
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
|
throw,
|
||||||
|
value::{function::NativeFunc, Value},
|
||||||
|
vmcall, Vm,
|
||||||
|
};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
|
||||||
|
|
||||||
pub fn load(vm: &mut Vm) {
|
pub fn load(vm: &mut Vm) {
|
||||||
vm.set_global_name("push", push().into());
|
vm.set_global_name("push", push().into());
|
||||||
vm.set_global_name("pop", pop().into());
|
vm.set_global_name("pop", pop().into());
|
||||||
|
@ -55,7 +60,10 @@ pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
match &col {
|
match &col {
|
||||||
Value::List(list) => list.borrow_mut().clear(),
|
Value::List(list) => list.borrow_mut().clear(),
|
||||||
Value::Table(table) => table.borrow_mut().clear(),
|
Value::Table(table) => table.borrow_mut().clear(),
|
||||||
_ => throw!(*SYM_TYPE_ERROR, "clear expected list or table, found {col:#}")
|
_ => throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"clear expected list or table, found {col:#}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
Ok(col)
|
Ok(col)
|
||||||
}
|
}
|
||||||
|
@ -63,7 +71,10 @@ pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<i64> {
|
fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<i64> {
|
||||||
let ord = vmcall!(vm; by, l, r)?;
|
let ord = vmcall!(vm; by, l, r)?;
|
||||||
let Value::Int(ord) = ord else {
|
let Value::Int(ord) = ord else {
|
||||||
throw!(*SYM_TYPE_ERROR, "comparison function should return an integer")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"comparison function should return an integer"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
Ok(ord)
|
Ok(ord)
|
||||||
}
|
}
|
||||||
|
@ -82,14 +93,14 @@ fn partition(vals: &mut [Value]) -> (usize, usize) {
|
||||||
vals.swap(eq, lt);
|
vals.swap(eq, lt);
|
||||||
lt += 1;
|
lt += 1;
|
||||||
eq += 1;
|
eq += 1;
|
||||||
},
|
}
|
||||||
Some(Ordering::Greater) => {
|
Some(Ordering::Greater) => {
|
||||||
vals.swap(eq, gt);
|
vals.swap(eq, gt);
|
||||||
gt -= 1;
|
gt -= 1;
|
||||||
},
|
}
|
||||||
Some(Ordering::Equal) | None => {
|
Some(Ordering::Equal) | None => {
|
||||||
eq += 1;
|
eq += 1;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,14 +121,14 @@ fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, u
|
||||||
vals.swap(eq, lt);
|
vals.swap(eq, lt);
|
||||||
lt += 1;
|
lt += 1;
|
||||||
eq += 1;
|
eq += 1;
|
||||||
},
|
}
|
||||||
1.. => {
|
1.. => {
|
||||||
vals.swap(eq, gt);
|
vals.swap(eq, gt);
|
||||||
gt -= 1;
|
gt -= 1;
|
||||||
},
|
}
|
||||||
0 => {
|
0 => {
|
||||||
eq += 1;
|
eq += 1;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,8 +138,8 @@ fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, u
|
||||||
fn insertion(vals: &mut [Value]) {
|
fn insertion(vals: &mut [Value]) {
|
||||||
for i in 0..vals.len() {
|
for i in 0..vals.len() {
|
||||||
let mut j = i;
|
let mut j = i;
|
||||||
while j > 0 && vals[j-1] > vals[j] {
|
while j > 0 && vals[j - 1] > vals[j] {
|
||||||
vals.swap(j, j-1);
|
vals.swap(j, j - 1);
|
||||||
j -= 1;
|
j -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,11 +149,11 @@ fn insertion_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<()> {
|
||||||
for i in 0..vals.len() {
|
for i in 0..vals.len() {
|
||||||
let mut j = i;
|
let mut j = i;
|
||||||
while j > 0 {
|
while j > 0 {
|
||||||
let ord = call_comparison(vm, by.clone(), vals[j-1].clone(), vals[j].clone())?;
|
let ord = call_comparison(vm, by.clone(), vals[j - 1].clone(), vals[j].clone())?;
|
||||||
if ord <= 0 {
|
if ord <= 0 {
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
vals.swap(j, j-1);
|
vals.swap(j, j - 1);
|
||||||
j -= 1;
|
j -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,7 +176,7 @@ fn sort_inner(vals: &mut [Value], by: Option<&Value>, vm: &mut Vm) -> Result<()>
|
||||||
None => partition(vals),
|
None => partition(vals),
|
||||||
};
|
};
|
||||||
sort_inner(&mut vals[..lt], by, vm)?;
|
sort_inner(&mut vals[..lt], by, vm)?;
|
||||||
sort_inner(&mut vals[(gt+1)..], by, vm)
|
sort_inner(&mut vals[(gt + 1)..], by, vm)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -202,7 +213,10 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Some(Ordering::Greater) => Ok(Value::Int(1)),
|
Some(Ordering::Greater) => Ok(Value::Int(1)),
|
||||||
Some(Ordering::Equal) => Ok(Value::Int(0)),
|
Some(Ordering::Equal) => Ok(Value::Int(0)),
|
||||||
Some(Ordering::Less) => Ok(Value::Int(-1)),
|
Some(Ordering::Less) => Ok(Value::Int(-1)),
|
||||||
None => throw!(*SYM_VALUE_ERROR, "values returned from sort key were incomparable"),
|
None => throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"values returned from sort key were incomparable"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let nf = NativeFunc::new(Box::new(f), 2, symbol!("inner[sort_key]")).into();
|
let nf = NativeFunc::new(Box::new(f), 2, symbol!("inner[sort_key]")).into();
|
||||||
|
@ -212,32 +226,32 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
pub fn pair(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn pair(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x, y] = unpack_args!(args);
|
let [_, x, y] = unpack_args!(args);
|
||||||
Ok(vec![x, y].into())
|
Ok(vec![x, y].into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn fst(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn fst(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, l] = unpack_args!(args);
|
let [_, l] = unpack_args!(args);
|
||||||
let Value::List(l) = l else {
|
let Value::List(l) = l else {
|
||||||
throw!(*SYM_TYPE_ERROR, "fst: expected list")
|
throw!(*SYM_TYPE_ERROR, "fst: expected list")
|
||||||
};
|
};
|
||||||
let l = l.borrow();
|
let l = l.borrow();
|
||||||
let [x, _] = l.as_slice() else {
|
let [x, _] = l.as_slice() else {
|
||||||
throw!(*SYM_VALUE_ERROR, "fst: list must have length 2")
|
throw!(*SYM_VALUE_ERROR, "fst: list must have length 2")
|
||||||
};
|
};
|
||||||
Ok(x.clone())
|
Ok(x.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn snd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn snd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, l] = unpack_args!(args);
|
let [_, l] = unpack_args!(args);
|
||||||
let Value::List(l) = l else {
|
let Value::List(l) = l else {
|
||||||
throw!(*SYM_TYPE_ERROR, "snd: expected list")
|
throw!(*SYM_TYPE_ERROR, "snd: expected list")
|
||||||
};
|
};
|
||||||
let l = l.borrow();
|
let l = l.borrow();
|
||||||
let [_, y] = l.as_slice() else {
|
let [_, y] = l.as_slice() else {
|
||||||
throw!(*SYM_VALUE_ERROR, "snd: list must have length 2")
|
throw!(*SYM_VALUE_ERROR, "snd: list must have length 2")
|
||||||
};
|
};
|
||||||
Ok(y.clone())
|
Ok(y.clone())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
use talc_lang::{exception::{throw, Exception, Result}, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::Value, Vm};
|
use talc_lang::{
|
||||||
|
exception::{throw, Exception, Result},
|
||||||
|
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
|
value::Value,
|
||||||
|
Vm,
|
||||||
|
};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
@ -6,23 +11,24 @@ use crate::unpack_args;
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, arg] = unpack_args!(args);
|
let [_, arg] = unpack_args!(args);
|
||||||
let exc = match arg {
|
let exc = match arg {
|
||||||
Value::Symbol(ty) => Exception::new(ty),
|
Value::Symbol(ty) => Exception::new(ty),
|
||||||
Value::List(l) => match l.borrow().as_slice() {
|
Value::List(l) => match l.borrow().as_slice() {
|
||||||
[Value::Symbol(ty)] | [Value::Symbol(ty), Value::Nil]
|
[Value::Symbol(ty)] | [Value::Symbol(ty), Value::Nil] => Exception::new(*ty),
|
||||||
=> Exception::new(*ty),
|
[Value::Symbol(ty), Value::Nil, data] => Exception::new_with_data(*ty, data.clone()),
|
||||||
[Value::Symbol(ty), Value::Nil, data]
|
[Value::Symbol(ty), Value::String(s)] => Exception::new_with_msg(*ty, s.clone()),
|
||||||
=> Exception::new_with_data(*ty, data.clone()),
|
[Value::Symbol(ty), Value::String(s), data] => {
|
||||||
[Value::Symbol(ty), Value::String(s)]
|
Exception::new_with_msg_data(*ty, s.clone(), data.clone())
|
||||||
=> Exception::new_with_msg(*ty, s.clone()),
|
}
|
||||||
[Value::Symbol(ty), Value::String(s), data]
|
[] | [_] | [_, _] | [_, _, _] => throw!(*SYM_TYPE_ERROR, "wrong argument for throw"),
|
||||||
=> Exception::new_with_msg_data(*ty, s.clone(), data.clone()),
|
[_, _, _, _, ..] => throw!(
|
||||||
[] | [_] | [_,_] | [_,_,_] => throw!(*SYM_TYPE_ERROR, "wrong argument for throw"),
|
*SYM_VALUE_ERROR,
|
||||||
[_,_,_,_,..] => throw!(*SYM_VALUE_ERROR, "too many elements in list argument for throw"),
|
"too many elements in list argument for throw"
|
||||||
}
|
),
|
||||||
_ => throw!(*SYM_TYPE_ERROR, "throw expected symbol or list"),
|
},
|
||||||
};
|
_ => throw!(*SYM_TYPE_ERROR, "throw expected symbol or list"),
|
||||||
Err(exc)
|
};
|
||||||
|
Err(exc)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -32,7 +38,7 @@ pub fn rethrow(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
if let Some(e) = Exception::from_table(&table) {
|
if let Some(e) = Exception::from_table(&table) {
|
||||||
return Err(e)
|
return Err(e)
|
||||||
}
|
}
|
||||||
throw!(*SYM_VALUE_ERROR, "argument not a valid exception")
|
throw!(*SYM_VALUE_ERROR, "argument not a valid exception")
|
||||||
}
|
}
|
||||||
throw!(*SYM_TYPE_ERROR, "argument not a valid exception")
|
throw!(*SYM_TYPE_ERROR, "argument not a valid exception")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,24 @@
|
||||||
use std::{cell::RefCell, collections::HashMap, fs::{File, OpenOptions}, io::{BufRead, BufReader, BufWriter, ErrorKind, IntoInnerError, Read, Write}, net::{TcpListener, TcpStream, ToSocketAddrs}, os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd}, process::{Child, Command, Stdio}, time::Duration};
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
collections::HashMap,
|
||||||
|
fs::{File, OpenOptions},
|
||||||
|
io::{BufRead, BufReader, BufWriter, ErrorKind, IntoInnerError, Read, Write},
|
||||||
|
net::{TcpListener, TcpStream, ToSocketAddrs},
|
||||||
|
os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd},
|
||||||
|
process::{Child, Command, Stdio},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use talc_lang::{exception::Result, lstring::LString, symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{function::NativeFunc, HashValue, NativeValue, Value}, Vm};
|
|
||||||
use talc_macros::native_func;
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use talc_lang::{
|
||||||
|
exception::Result,
|
||||||
|
lstring::LString,
|
||||||
|
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
|
throw,
|
||||||
|
value::{function::NativeFunc, HashValue, NativeValue, Value},
|
||||||
|
Vm,
|
||||||
|
};
|
||||||
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::{unpack_args, SYM_IO_ERROR};
|
use crate::{unpack_args, SYM_IO_ERROR};
|
||||||
|
|
||||||
|
@ -10,7 +26,6 @@ type OpenOptFn = for<'a> fn(&'a mut OpenOptions, bool) -> &'a mut OpenOptions;
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref SYM_STD_FILE: Symbol = Symbol::get("std.file");
|
static ref SYM_STD_FILE: Symbol = Symbol::get("std.file");
|
||||||
static ref SYM_STD_PROCESS: Symbol = Symbol::get("std.process");
|
static ref SYM_STD_PROCESS: Symbol = Symbol::get("std.process");
|
||||||
|
|
||||||
static ref SYM_R: Symbol = Symbol::get("r");
|
static ref SYM_R: Symbol = Symbol::get("r");
|
||||||
static ref SYM_W: Symbol = Symbol::get("w");
|
static ref SYM_W: Symbol = Symbol::get("w");
|
||||||
static ref SYM_A: Symbol = Symbol::get("a");
|
static ref SYM_A: Symbol = Symbol::get("a");
|
||||||
|
@ -18,7 +33,6 @@ lazy_static! {
|
||||||
static ref SYM_C: Symbol = Symbol::get("c");
|
static ref SYM_C: Symbol = Symbol::get("c");
|
||||||
static ref SYM_N: Symbol = Symbol::get("n");
|
static ref SYM_N: Symbol = Symbol::get("n");
|
||||||
static ref SYM_U: Symbol = Symbol::get("u");
|
static ref SYM_U: Symbol = Symbol::get("u");
|
||||||
|
|
||||||
static ref OPEN_OPT_MAP: HashMap<Symbol, OpenOptFn> = {
|
static ref OPEN_OPT_MAP: HashMap<Symbol, OpenOptFn> = {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert(*SYM_R, OpenOptions::read as OpenOptFn);
|
map.insert(*SYM_R, OpenOptions::read as OpenOptFn);
|
||||||
|
@ -29,21 +43,17 @@ lazy_static! {
|
||||||
map.insert(*SYM_N, OpenOptions::create_new);
|
map.insert(*SYM_N, OpenOptions::create_new);
|
||||||
map
|
map
|
||||||
};
|
};
|
||||||
|
|
||||||
static ref SYM_STDIN: Symbol = Symbol::get("stdin");
|
static ref SYM_STDIN: Symbol = Symbol::get("stdin");
|
||||||
static ref SYM_STDOUT: Symbol = Symbol::get("stdout");
|
static ref SYM_STDOUT: Symbol = Symbol::get("stdout");
|
||||||
static ref SYM_STDERR: Symbol = Symbol::get("stderr");
|
static ref SYM_STDERR: Symbol = Symbol::get("stderr");
|
||||||
static ref SYM_CD: Symbol = Symbol::get("cd");
|
static ref SYM_CD: Symbol = Symbol::get("cd");
|
||||||
static ref SYM_DELENV: Symbol = Symbol::get("delenv");
|
static ref SYM_DELENV: Symbol = Symbol::get("delenv");
|
||||||
static ref SYM_ENV: Symbol = Symbol::get("env");
|
static ref SYM_ENV: Symbol = Symbol::get("env");
|
||||||
|
|
||||||
static ref SYM_PROCESS: Symbol = Symbol::get("process");
|
static ref SYM_PROCESS: Symbol = Symbol::get("process");
|
||||||
|
|
||||||
static ref SYM_INHERIT: Symbol = Symbol::get("inherit");
|
static ref SYM_INHERIT: Symbol = Symbol::get("inherit");
|
||||||
static ref SYM_PIPED: Symbol = Symbol::get("piped");
|
static ref SYM_PIPED: Symbol = Symbol::get("piped");
|
||||||
static ref SYM_NULL: Symbol = Symbol::get("null");
|
static ref SYM_NULL: Symbol = Symbol::get("null");
|
||||||
static ref SYM_ALL: Symbol = Symbol::get("all");
|
static ref SYM_ALL: Symbol = Symbol::get("all");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
|
@ -69,15 +79,23 @@ thread_local! {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum BufFile {
|
enum BufFile {
|
||||||
Buffered { r: BufReader<File>, w: BufWriter<File> },
|
Buffered {
|
||||||
Unbuffered { f: File },
|
r: BufReader<File>,
|
||||||
|
w: BufWriter<File>,
|
||||||
|
},
|
||||||
|
Unbuffered {
|
||||||
|
f: File,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufFile {
|
impl BufFile {
|
||||||
fn new(file: File, buffered: bool) -> std::io::Result<Self> {
|
fn new(file: File, buffered: bool) -> std::io::Result<Self> {
|
||||||
if buffered {
|
if buffered {
|
||||||
let file2 = file.try_clone()?;
|
let file2 = file.try_clone()?;
|
||||||
Ok(Self::Buffered { r: BufReader::new(file), w: BufWriter::new(file2) })
|
Ok(Self::Buffered {
|
||||||
|
r: BufReader::new(file),
|
||||||
|
w: BufWriter::new(file2),
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Ok(Self::Unbuffered { f: file })
|
Ok(Self::Unbuffered { f: file })
|
||||||
}
|
}
|
||||||
|
@ -92,8 +110,8 @@ impl BufFile {
|
||||||
Self::Buffered { r, .. } => {
|
Self::Buffered { r, .. } => {
|
||||||
let file = r.get_ref().try_clone()?;
|
let file = r.get_ref().try_clone()?;
|
||||||
Self::new(file, true)
|
Self::new(file, true)
|
||||||
},
|
}
|
||||||
Self::Unbuffered { f } => Ok(Self::Unbuffered { f: f.try_clone()? })
|
Self::Unbuffered { f } => Ok(Self::Unbuffered { f: f.try_clone()? }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,9 +170,18 @@ impl From<BufFile> for ValueFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NativeValue for ValueFile {
|
impl NativeValue for ValueFile {
|
||||||
fn get_type(&self) -> Symbol { *SYM_STD_FILE }
|
fn get_type(&self) -> Symbol {
|
||||||
fn as_any(&self) -> &dyn std::any::Any { self }
|
*SYM_STD_FILE
|
||||||
fn to_lstring(&self, w: &mut LString, _repr: bool, _recur: &mut Vec<*const ()>) -> std::io::Result<()> {
|
}
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn to_lstring(
|
||||||
|
&self,
|
||||||
|
w: &mut LString,
|
||||||
|
_repr: bool,
|
||||||
|
_recur: &mut Vec<*const ()>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
w.push_str("<file>");
|
w.push_str("<file>");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -170,9 +197,18 @@ impl From<Child> for ValueProcess {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NativeValue for ValueProcess {
|
impl NativeValue for ValueProcess {
|
||||||
fn get_type(&self) -> Symbol { *SYM_STD_PROCESS }
|
fn get_type(&self) -> Symbol {
|
||||||
fn as_any(&self) -> &dyn std::any::Any { self }
|
*SYM_STD_PROCESS
|
||||||
fn to_lstring(&self, w: &mut LString, _repr: bool, _recur: &mut Vec<*const ()>) -> std::io::Result<()> {
|
}
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn to_lstring(
|
||||||
|
&self,
|
||||||
|
w: &mut LString,
|
||||||
|
_repr: bool,
|
||||||
|
_recur: &mut Vec<*const ()>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
let id = self.0.borrow().id();
|
let id = self.0.borrow().id();
|
||||||
write!(w, "<process {id}>")
|
write!(w, "<process {id}>")
|
||||||
}
|
}
|
||||||
|
@ -201,7 +237,6 @@ pub fn load(vm: &mut Vm) {
|
||||||
vm.set_global_name("process_id", process_id().into());
|
vm.set_global_name("process_id", process_id().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, path, opts] = unpack_args!(args);
|
let [_, path, opts] = unpack_args!(args);
|
||||||
|
@ -218,33 +253,35 @@ pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
} else {
|
} else {
|
||||||
match OPEN_OPT_MAP.get(&s) {
|
match OPEN_OPT_MAP.get(&s) {
|
||||||
Some(f) => f(&mut oo, true),
|
Some(f) => f(&mut oo, true),
|
||||||
None => throw!(*SYM_VALUE_ERROR, "invalid option for open")
|
None => throw!(*SYM_VALUE_ERROR, "invalid option for open"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Value::List(l) => for s in l.borrow().iter() {
|
Value::List(l) => {
|
||||||
let Value::Symbol(s) = s else {
|
for s in l.borrow().iter() {
|
||||||
throw!(*SYM_TYPE_ERROR, "invalid option for open")
|
let Value::Symbol(s) = s else {
|
||||||
};
|
throw!(*SYM_TYPE_ERROR, "invalid option for open")
|
||||||
if *s == *SYM_U {
|
|
||||||
buffered = false;
|
|
||||||
} else {
|
|
||||||
match OPEN_OPT_MAP.get(s) {
|
|
||||||
Some(f) => f(&mut oo, true),
|
|
||||||
None => throw!(*SYM_VALUE_ERROR, "invalid option for open")
|
|
||||||
};
|
};
|
||||||
|
if *s == *SYM_U {
|
||||||
|
buffered = false;
|
||||||
|
} else {
|
||||||
|
match OPEN_OPT_MAP.get(s) {
|
||||||
|
Some(f) => f(&mut oo, true),
|
||||||
|
None => throw!(*SYM_VALUE_ERROR, "invalid option for open"),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Value::Nil => {
|
Value::Nil => {
|
||||||
oo.read(true).write(true);
|
oo.read(true).write(true);
|
||||||
},
|
}
|
||||||
_ => throw!(*SYM_TYPE_ERROR, "invalid option for open")
|
_ => throw!(*SYM_TYPE_ERROR, "invalid option for open"),
|
||||||
}
|
}
|
||||||
match oo.open(path.to_os_str()) {
|
match oo.open(path.to_os_str()) {
|
||||||
Ok(f) => match BufFile::new(f, buffered) {
|
Ok(f) => match BufFile::new(f, buffered) {
|
||||||
Ok(bf) => Ok(ValueFile::from(bf).into()),
|
Ok(bf) => Ok(ValueFile::from(bf).into()),
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
},
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,7 +293,10 @@ pub fn read(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "read expected integer, got {nbytes:#}")
|
throw!(*SYM_TYPE_ERROR, "read expected integer, got {nbytes:#}")
|
||||||
};
|
};
|
||||||
let Ok(nbytes) = usize::try_from(nbytes) else {
|
let Ok(nbytes) = usize::try_from(nbytes) else {
|
||||||
throw!(*SYM_VALUE_ERROR, "number of bytes to read must be nonnegative")
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"number of bytes to read must be nonnegative"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let Some(file): Option<&ValueFile> = file.downcast_native() else {
|
let Some(file): Option<&ValueFile> = file.downcast_native() else {
|
||||||
throw!(*SYM_TYPE_ERROR, "read expected file, got {file:#}")
|
throw!(*SYM_TYPE_ERROR, "read expected file, got {file:#}")
|
||||||
|
@ -317,7 +357,7 @@ pub fn read_until(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
match read_until_impl(r, end.as_bytes()) {
|
match read_until_impl(r, end.as_bytes()) {
|
||||||
Ok(s) if s.is_empty() => Ok(Value::Nil),
|
Ok(s) if s.is_empty() => Ok(Value::Nil),
|
||||||
Ok(s) => Ok(s.into()),
|
Ok(s) => Ok(s.into()),
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}")
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_TYPE_ERROR, "read_until: file must be buffered")
|
throw!(*SYM_TYPE_ERROR, "read_until: file must be buffered")
|
||||||
|
@ -336,7 +376,7 @@ pub fn read_line(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
match r.read_until(b'\n', &mut buf) {
|
match r.read_until(b'\n', &mut buf) {
|
||||||
Ok(0) => Ok(Value::Nil),
|
Ok(0) => Ok(Value::Nil),
|
||||||
Ok(_) => Ok(LString::from(buf).into()),
|
Ok(_) => Ok(LString::from(buf).into()),
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}")
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_VALUE_ERROR, "read_line: file must be buffered")
|
throw!(*SYM_VALUE_ERROR, "read_line: file must be buffered")
|
||||||
|
@ -404,7 +444,7 @@ pub fn tcp_connect(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(stream) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
|
Ok(stream) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
|
||||||
Ok(bf) => Ok(ValueFile::from(bf).into()),
|
Ok(bf) => Ok(ValueFile::from(bf).into()),
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
},
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -413,16 +453,26 @@ pub fn tcp_connect(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn tcp_connect_timeout(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn tcp_connect_timeout(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, addr, timeout] = unpack_args!(args);
|
let [_, addr, timeout] = unpack_args!(args);
|
||||||
let Value::String(addr) = addr else {
|
let Value::String(addr) = addr else {
|
||||||
throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout expected string, got {addr:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"tcp_connect_timeout expected string, got {addr:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let Ok(addr) = addr.to_str() else {
|
let Ok(addr) = addr.to_str() else {
|
||||||
throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
|
throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
|
||||||
};
|
};
|
||||||
let timeout = match timeout {
|
let timeout = match timeout {
|
||||||
Value::Int(n) if n >= 0 => Duration::from_secs(n as u64),
|
Value::Int(n) if n >= 0 => Duration::from_secs(n as u64),
|
||||||
Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => Duration::from_secs_f64(n),
|
Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => {
|
||||||
Value::Int(_) | Value::Float(_) => throw!(*SYM_VALUE_ERROR, "tcp_connect_timeout: invalid timeout"),
|
Duration::from_secs_f64(n)
|
||||||
_ => throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout expected int or float, got {timeout:#}")
|
}
|
||||||
|
Value::Int(_) | Value::Float(_) => {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "tcp_connect_timeout: invalid timeout")
|
||||||
|
}
|
||||||
|
_ => throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"tcp_connect_timeout expected int or float, got {timeout:#}"
|
||||||
|
),
|
||||||
};
|
};
|
||||||
let mut addrs = match addr.to_socket_addrs() {
|
let mut addrs = match addr.to_socket_addrs() {
|
||||||
Ok(addrs) => addrs,
|
Ok(addrs) => addrs,
|
||||||
|
@ -435,24 +485,23 @@ pub fn tcp_connect_timeout(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(stream) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
|
Ok(stream) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
|
||||||
Ok(bf) => Ok(ValueFile::from(bf).into()),
|
Ok(bf) => Ok(ValueFile::from(bf).into()),
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
},
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tcp_listen_inner(listener: TcpListener) -> Value {
|
fn tcp_listen_inner(listener: TcpListener) -> Value {
|
||||||
let listener = RefCell::new(listener);
|
let listener = RefCell::new(listener);
|
||||||
let func = move |_: &mut Vm, _: Vec<Value>| {
|
let func = move |_: &mut Vm, _: Vec<Value>| match listener.borrow_mut().accept() {
|
||||||
match listener.borrow_mut().accept() {
|
Ok((stream, addr)) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
|
||||||
Ok((stream, addr)) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
|
Ok(bf) => Ok(vec![
|
||||||
Ok(bf) => Ok(vec![
|
ValueFile::from(bf).into(),
|
||||||
ValueFile::from(bf).into(),
|
LString::from(addr.to_string()).into(),
|
||||||
LString::from(addr.to_string()).into()
|
]
|
||||||
].into()),
|
.into()),
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
|
||||||
}
|
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
},
|
||||||
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
};
|
};
|
||||||
NativeFunc::new(Box::new(func), 0, symbol!("inner[tcp_listen]")).into()
|
NativeFunc::new(Box::new(func), 0, symbol!("inner[tcp_listen]")).into()
|
||||||
}
|
}
|
||||||
|
@ -468,14 +517,12 @@ pub fn tcp_listen(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
};
|
};
|
||||||
match TcpListener::bind(addr) {
|
match TcpListener::bind(addr) {
|
||||||
Ok(listener) => {
|
Ok(listener) => {
|
||||||
let addr = listener.local_addr()
|
let addr = listener
|
||||||
|
.local_addr()
|
||||||
.map(|a| LString::from(a.to_string()).into())
|
.map(|a| LString::from(a.to_string()).into())
|
||||||
.unwrap_or(Value::Nil);
|
.unwrap_or(Value::Nil);
|
||||||
Ok(vec![
|
Ok(vec![tcp_listen_inner(listener), addr].into())
|
||||||
tcp_listen_inner(listener),
|
}
|
||||||
addr,
|
|
||||||
].into())
|
|
||||||
},
|
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -499,24 +546,32 @@ fn spawn_opt_stdio(
|
||||||
let f: &ValueFile = opt.downcast_native().unwrap();
|
let f: &ValueFile = opt.downcast_native().unwrap();
|
||||||
let bf = match f.0.borrow().try_clone() {
|
let bf = match f.0.borrow().try_clone() {
|
||||||
Ok(bf) => bf,
|
Ok(bf) => bf,
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}")
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
};
|
};
|
||||||
let fd = match bf.try_into_raw_fd() {
|
let fd = match bf.try_into_raw_fd() {
|
||||||
Ok(fd) => fd,
|
Ok(fd) => fd,
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}")
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
};
|
};
|
||||||
let stdio = unsafe { Stdio::from_raw_fd(fd) };
|
let stdio = unsafe { Stdio::from_raw_fd(fd) };
|
||||||
func(proc, stdio)
|
func(proc, stdio)
|
||||||
},
|
}
|
||||||
_ => throw!(*SYM_VALUE_ERROR, "{fname} stdio option expected :inherit, :piped, :null, or file, got {opt:#}")
|
_ => throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"{fname} stdio option expected :inherit, :piped, :null, or file, got {opt:#}"
|
||||||
|
),
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_opt_cd(fname: &str, proc: &mut Command, cd: &Value) -> Result<()> {
|
fn spawn_opt_cd(fname: &str, proc: &mut Command, cd: &Value) -> Result<()> {
|
||||||
if let Value::Nil = cd { return Ok(()) }
|
if let Value::Nil = cd {
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
let Value::String(cd) = cd else {
|
let Value::String(cd) = cd else {
|
||||||
throw!(*SYM_TYPE_ERROR, "{fname} cd option expected string, got {cd:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"{fname} cd option expected string, got {cd:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
proc.current_dir(cd.to_os_str());
|
proc.current_dir(cd.to_os_str());
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -527,16 +582,22 @@ fn spawn_opt_delenv(fname: &str, proc: &mut Command, delenv: &Value) -> Result<(
|
||||||
Value::Nil => (),
|
Value::Nil => (),
|
||||||
Value::Symbol(s) if *s == *SYM_ALL => {
|
Value::Symbol(s) if *s == *SYM_ALL => {
|
||||||
proc.env_clear();
|
proc.env_clear();
|
||||||
},
|
}
|
||||||
Value::List(l) => for e in l.borrow().iter() {
|
Value::List(l) => {
|
||||||
let Value::String(e) = e else {
|
for e in l.borrow().iter() {
|
||||||
throw!(*SYM_TYPE_ERROR,
|
let Value::String(e) = e else {
|
||||||
"{fname} delenv option expected :all or list of strings, got {delenv:#}")
|
throw!(
|
||||||
};
|
*SYM_TYPE_ERROR,
|
||||||
proc.env_remove(e.to_os_str());
|
"{fname} delenv option expected :all or list of strings, got {delenv:#}"
|
||||||
},
|
)
|
||||||
_ => throw!(*SYM_VALUE_ERROR,
|
};
|
||||||
"{fname} delenv option expected :all or list of strings, got {delenv:#}")
|
proc.env_remove(e.to_os_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"{fname} delenv option expected :all or list of strings, got {delenv:#}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -544,15 +605,21 @@ fn spawn_opt_delenv(fname: &str, proc: &mut Command, delenv: &Value) -> Result<(
|
||||||
fn spawn_opt_env(fname: &str, proc: &mut Command, env: &Value) -> Result<()> {
|
fn spawn_opt_env(fname: &str, proc: &mut Command, env: &Value) -> Result<()> {
|
||||||
match env {
|
match env {
|
||||||
Value::Nil => (),
|
Value::Nil => (),
|
||||||
Value::Table(t) => for (k, v) in t.borrow().iter() {
|
Value::Table(t) => {
|
||||||
let Value::String(k) = k.inner() else {
|
for (k, v) in t.borrow().iter() {
|
||||||
throw!(*SYM_TYPE_ERROR,
|
let Value::String(k) = k.inner() else {
|
||||||
"{fname} env option expected table with string keys, got {env:#}")
|
throw!(
|
||||||
};
|
*SYM_TYPE_ERROR,
|
||||||
proc.env(k.to_os_str(), v.str().to_os_str());
|
"{fname} env option expected table with string keys, got {env:#}"
|
||||||
},
|
)
|
||||||
_ => throw!(*SYM_TYPE_ERROR,
|
};
|
||||||
"{fname} env option expected table with string keys, got {env:#}")
|
proc.env(k.to_os_str(), v.str().to_os_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"{fname} env option expected table with string keys, got {env:#}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -560,7 +627,10 @@ fn spawn_opt_env(fname: &str, proc: &mut Command, env: &Value) -> Result<()> {
|
||||||
fn spawn_inner(fname: &str, proc: &mut Command, opts: Value) -> Result<Value> {
|
fn spawn_inner(fname: &str, proc: &mut Command, opts: Value) -> Result<Value> {
|
||||||
let (i, o, e) = match opts {
|
let (i, o, e) = match opts {
|
||||||
Value::Native(ref n) if n.get_type() == *SYM_STD_FILE => {
|
Value::Native(ref n) if n.get_type() == *SYM_STD_FILE => {
|
||||||
throw!(*SYM_TYPE_ERROR, "{fname} options expected :inherit, :piped, :null, or table, got {opts:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"{fname} options expected :inherit, :piped, :null, or table, got {opts:#}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Value::Table(t) => {
|
Value::Table(t) => {
|
||||||
let t = t.borrow();
|
let t = t.borrow();
|
||||||
|
@ -577,17 +647,20 @@ fn spawn_inner(fname: &str, proc: &mut Command, opts: Value) -> Result<Value> {
|
||||||
spawn_opt_env(fname, proc, env)?;
|
spawn_opt_env(fname, proc, env)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let i = t.get(&HashValue::from(*SYM_STDIN))
|
let i = t
|
||||||
|
.get(&HashValue::from(*SYM_STDIN))
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| Value::from(*SYM_INHERIT));
|
.unwrap_or_else(|| Value::from(*SYM_INHERIT));
|
||||||
let o = t.get(&HashValue::from(*SYM_STDOUT))
|
let o = t
|
||||||
|
.get(&HashValue::from(*SYM_STDOUT))
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| Value::from(*SYM_INHERIT));
|
.unwrap_or_else(|| Value::from(*SYM_INHERIT));
|
||||||
let e = t.get(&HashValue::from(*SYM_STDERR))
|
let e = t
|
||||||
|
.get(&HashValue::from(*SYM_STDERR))
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| Value::from(*SYM_INHERIT));
|
.unwrap_or_else(|| Value::from(*SYM_INHERIT));
|
||||||
(i, o, e)
|
(i, o, e)
|
||||||
},
|
}
|
||||||
v => (v.clone(), v.clone(), v),
|
v => (v.clone(), v.clone(), v),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -621,7 +694,10 @@ fn spawn_inner(fname: &str, proc: &mut Command, opts: Value) -> Result<Value> {
|
||||||
};
|
};
|
||||||
table.insert(HashValue::from(*SYM_STDERR), ValueFile::from(bf).into());
|
table.insert(HashValue::from(*SYM_STDERR), ValueFile::from(bf).into());
|
||||||
}
|
}
|
||||||
table.insert(HashValue::from(*SYM_PROCESS), ValueProcess::from(child).into());
|
table.insert(
|
||||||
|
HashValue::from(*SYM_PROCESS),
|
||||||
|
ValueProcess::from(child).into(),
|
||||||
|
);
|
||||||
Ok(table.into())
|
Ok(table.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,12 +708,18 @@ pub fn spawn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "spawn expected string, got {cmd:#}")
|
throw!(*SYM_TYPE_ERROR, "spawn expected string, got {cmd:#}")
|
||||||
};
|
};
|
||||||
let Value::List(args) = args else {
|
let Value::List(args) = args else {
|
||||||
throw!(*SYM_TYPE_ERROR, "spawn expected list of strings, got {args:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"spawn expected list of strings, got {args:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let mut proc = Command::new(cmd.to_os_str());
|
let mut proc = Command::new(cmd.to_os_str());
|
||||||
for arg in args.borrow().iter() {
|
for arg in args.borrow().iter() {
|
||||||
let Value::String(arg) = arg else {
|
let Value::String(arg) = arg else {
|
||||||
throw!(*SYM_TYPE_ERROR, "spawn expected list of strings, got list containing {arg:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"spawn expected list of strings, got list containing {arg:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
proc.arg(arg.to_os_str());
|
proc.arg(arg.to_os_str());
|
||||||
}
|
}
|
||||||
|
@ -653,12 +735,10 @@ pub fn system(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let mut proc;
|
let mut proc;
|
||||||
if cfg!(target_os = "windows") {
|
if cfg!(target_os = "windows") {
|
||||||
proc = Command::new("cmd");
|
proc = Command::new("cmd");
|
||||||
proc.arg("/C")
|
proc.arg("/C").arg(cmd.to_os_str())
|
||||||
.arg(cmd.to_os_str())
|
|
||||||
} else {
|
} else {
|
||||||
proc = Command::new("sh");
|
proc = Command::new("sh");
|
||||||
proc.arg("-c")
|
proc.arg("-c").arg(cmd.to_os_str())
|
||||||
.arg(cmd.to_os_str())
|
|
||||||
};
|
};
|
||||||
spawn_inner("system", &mut proc, opts)
|
spawn_inner("system", &mut proc, opts)
|
||||||
}
|
}
|
||||||
|
@ -671,11 +751,10 @@ pub fn exit_code(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
};
|
};
|
||||||
let mut proc = proc.0.borrow_mut();
|
let mut proc = proc.0.borrow_mut();
|
||||||
match proc.wait() {
|
match proc.wait() {
|
||||||
Ok(code) => {
|
Ok(code) => Ok(code
|
||||||
Ok(code.code()
|
.code()
|
||||||
.map(|c| Value::Int(c as i64))
|
.map(|c| Value::Int(c as i64))
|
||||||
.unwrap_or_default())
|
.unwrap_or_default()),
|
||||||
},
|
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -689,4 +768,3 @@ pub fn process_id(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let proc = proc.0.borrow();
|
let proc = proc.0.borrow();
|
||||||
Ok((proc.id() as i64).into())
|
Ok((proc.id() as i64).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,414 +1,477 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use talc_lang::{exception::{exception, Result}, lstring::{LStr, LString}, parser::{parse_int, to_lstring_radix}, symbol::SYM_TYPE_ERROR, throw, value::{Complex64, Rational64, Value}, Vm};
|
use talc_lang::{
|
||||||
|
exception::{exception, Result},
|
||||||
|
lstring::{LStr, LString},
|
||||||
|
parser::{parse_int, to_lstring_radix},
|
||||||
|
symbol::SYM_TYPE_ERROR,
|
||||||
|
throw,
|
||||||
|
value::{Complex64, Rational64, Value},
|
||||||
|
Vm,
|
||||||
|
};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::{unpack_args, SYM_FORMAT_ERROR};
|
use crate::{unpack_args, SYM_FORMAT_ERROR};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
enum FmtIndex {
|
enum FmtIndex {
|
||||||
Imm(usize),
|
Imm(usize),
|
||||||
Arg(usize),
|
Arg(usize),
|
||||||
NextArg,
|
NextArg,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
enum Align {
|
enum Align {
|
||||||
Left,
|
Left,
|
||||||
Center,
|
Center,
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
enum FmtType {
|
enum FmtType {
|
||||||
#[default]
|
#[default]
|
||||||
Str,
|
Str,
|
||||||
Repr,
|
Repr,
|
||||||
Hex(bool),
|
Hex(bool),
|
||||||
Oct,
|
Oct,
|
||||||
Sex,
|
Sex,
|
||||||
Bin,
|
Bin,
|
||||||
Exp(bool),
|
Exp(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
enum FmtSign {
|
enum FmtSign {
|
||||||
Plus,
|
Plus,
|
||||||
Minus,
|
Minus,
|
||||||
#[default]
|
#[default]
|
||||||
None
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
// [idx][:[align][sign][width][.prec][ty]]
|
// [idx][:[align][sign][width][.prec][ty]]
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
struct FmtCode {
|
struct FmtCode {
|
||||||
arg_idx: Option<usize>,
|
arg_idx: Option<usize>,
|
||||||
align: Option<(Align, char)>,
|
align: Option<(Align, char)>,
|
||||||
sign: FmtSign,
|
sign: FmtSign,
|
||||||
width: Option<FmtIndex>,
|
width: Option<FmtIndex>,
|
||||||
prec: Option<FmtIndex>,
|
prec: Option<FmtIndex>,
|
||||||
ty: FmtType,
|
ty: FmtType,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FmtParser<'p> {
|
struct FmtParser<'p> {
|
||||||
code: &'p LStr,
|
code: &'p LStr,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'p> FmtParser<'p> {
|
impl<'p> FmtParser<'p> {
|
||||||
fn new(code: &'p LStr) -> Self {
|
fn new(code: &'p LStr) -> Self {
|
||||||
Self {
|
Self { code, pos: 0 }
|
||||||
code,
|
}
|
||||||
pos: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<char> {
|
fn next(&mut self) -> Option<char> {
|
||||||
let c = &self.code[self.pos..].chars().next()?;
|
let c = &self.code[self.pos..].chars().next()?;
|
||||||
self.pos += c.len_utf8();
|
self.pos += c.len_utf8();
|
||||||
Some(*c)
|
Some(*c)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&mut self) -> Option<char> {
|
fn peek(&mut self) -> Option<char> {
|
||||||
self.code[self.pos..].chars().next()
|
self.code[self.pos..].chars().next()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_next(&mut self, chs: &[char]) -> Option<char> {
|
fn try_next(&mut self, chs: &[char]) -> Option<char> {
|
||||||
if self.peek().is_some_and(|c| chs.contains(&c)) {
|
if self.peek().is_some_and(|c| chs.contains(&c)) {
|
||||||
self.next()
|
self.next()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_number_str(&mut self) -> &LStr {
|
fn next_number_str(&mut self) -> &LStr {
|
||||||
let start = self.pos;
|
let start = self.pos;
|
||||||
while matches!(self.peek(), Some('0'..='9' | '_')) {
|
while matches!(self.peek(), Some('0'..='9' | '_')) {
|
||||||
self.next();
|
self.next();
|
||||||
}
|
}
|
||||||
&self.code[start..self.pos]
|
&self.code[start..self.pos]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_usize(&mut self) -> Result<Option<usize>> {
|
fn next_usize(&mut self) -> Result<Option<usize>> {
|
||||||
let s = self.next_number_str();
|
let s = self.next_number_str();
|
||||||
if s.is_empty() { return Ok(None) }
|
if s.is_empty() {
|
||||||
let Ok(i) = parse_int(s, 10) else {
|
return Ok(None)
|
||||||
throw!(*SYM_FORMAT_ERROR,
|
}
|
||||||
"code {{{}}}: invalid integer", self.code)
|
let Ok(i) = parse_int(s, 10) else {
|
||||||
};
|
throw!(*SYM_FORMAT_ERROR, "code {{{}}}: invalid integer", self.code)
|
||||||
if i < 0 {
|
};
|
||||||
throw!(*SYM_FORMAT_ERROR,
|
if i < 0 {
|
||||||
"code {{{}}}: integer may not be negative", self.code)
|
throw!(
|
||||||
}
|
*SYM_FORMAT_ERROR,
|
||||||
Ok(Some(i as usize))
|
"code {{{}}}: integer may not be negative",
|
||||||
}
|
self.code
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Ok(Some(i as usize))
|
||||||
|
}
|
||||||
|
|
||||||
fn next_fmt_index(&mut self) -> Result<Option<FmtIndex>> {
|
fn next_fmt_index(&mut self) -> Result<Option<FmtIndex>> {
|
||||||
let dollar = self.try_next(&['$']).is_some();
|
let dollar = self.try_next(&['$']).is_some();
|
||||||
let num = self.next_usize()?;
|
let num = self.next_usize()?;
|
||||||
match (dollar, num) {
|
match (dollar, num) {
|
||||||
(true, None) => Ok(Some(FmtIndex::NextArg)),
|
(true, None) => Ok(Some(FmtIndex::NextArg)),
|
||||||
(true, Some(n)) => Ok(Some(FmtIndex::Arg(n))),
|
(true, Some(n)) => Ok(Some(FmtIndex::Arg(n))),
|
||||||
(false, None) => Ok(None),
|
(false, None) => Ok(None),
|
||||||
(false, Some(n)) => Ok(Some(FmtIndex::Imm(n))),
|
(false, Some(n)) => Ok(Some(FmtIndex::Imm(n))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_align(&mut self) -> Result<Option<(Align, char)>> {
|
fn next_align(&mut self) -> Result<Option<(Align, char)>> {
|
||||||
let align = match self.peek() {
|
let align = match self.peek() {
|
||||||
Some('<') => Align::Left,
|
Some('<') => Align::Left,
|
||||||
Some('^') => Align::Center,
|
Some('^') => Align::Center,
|
||||||
Some('>') => Align::Right,
|
Some('>') => Align::Right,
|
||||||
_ => return Ok(None),
|
_ => return Ok(None),
|
||||||
};
|
};
|
||||||
self.next();
|
self.next();
|
||||||
let Some(c) = self.next() else {
|
let Some(c) = self.next() else {
|
||||||
throw!(*SYM_FORMAT_ERROR,
|
throw!(
|
||||||
"code {{{}}}: alignment without fill character", self.code)
|
*SYM_FORMAT_ERROR,
|
||||||
};
|
"code {{{}}}: alignment without fill character",
|
||||||
Ok(Some((align, c)))
|
self.code
|
||||||
}
|
)
|
||||||
|
};
|
||||||
|
Ok(Some((align, c)))
|
||||||
|
}
|
||||||
|
|
||||||
fn next_sign(&mut self) -> FmtSign {
|
fn next_sign(&mut self) -> FmtSign {
|
||||||
match self.try_next(&['+', '-']) {
|
match self.try_next(&['+', '-']) {
|
||||||
Some('+') => FmtSign::Plus,
|
Some('+') => FmtSign::Plus,
|
||||||
Some('-') => FmtSign::Minus,
|
Some('-') => FmtSign::Minus,
|
||||||
_ => FmtSign::None,
|
_ => FmtSign::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_type(&mut self) -> Result<FmtType> {
|
fn next_type(&mut self) -> Result<FmtType> {
|
||||||
let ty = match self.peek() {
|
let ty = match self.peek() {
|
||||||
None => return Ok(FmtType::Str),
|
None => return Ok(FmtType::Str),
|
||||||
Some('?') => FmtType::Repr,
|
Some('?') => FmtType::Repr,
|
||||||
Some('x') => FmtType::Hex(false),
|
Some('x') => FmtType::Hex(false),
|
||||||
Some('X') => FmtType::Hex(true),
|
Some('X') => FmtType::Hex(true),
|
||||||
Some('o') => FmtType::Oct,
|
Some('o') => FmtType::Oct,
|
||||||
Some('s') => FmtType::Sex,
|
Some('s') => FmtType::Sex,
|
||||||
Some('b') => FmtType::Bin,
|
Some('b') => FmtType::Bin,
|
||||||
Some('e') => FmtType::Exp(false),
|
Some('e') => FmtType::Exp(false),
|
||||||
Some('E') => FmtType::Exp(true),
|
Some('E') => FmtType::Exp(true),
|
||||||
_ => throw!(*SYM_FORMAT_ERROR,
|
_ => throw!(
|
||||||
"code {{{}}}: invalid format type", self.code),
|
*SYM_FORMAT_ERROR,
|
||||||
};
|
"code {{{}}}: invalid format type",
|
||||||
self.next();
|
self.code
|
||||||
Ok(ty)
|
),
|
||||||
}
|
};
|
||||||
|
self.next();
|
||||||
|
Ok(ty)
|
||||||
|
}
|
||||||
|
|
||||||
fn next_code_end(&mut self) -> Result<()> {
|
fn next_code_end(&mut self) -> Result<()> {
|
||||||
match self.peek() {
|
match self.peek() {
|
||||||
Some(c) => throw!(*SYM_FORMAT_ERROR,
|
Some(c) => throw!(
|
||||||
"code {{{}}}: expected end of code, found character '{}'", self.code, c),
|
*SYM_FORMAT_ERROR,
|
||||||
None => Ok(())
|
"code {{{}}}: expected end of code, found character '{}'",
|
||||||
}
|
self.code,
|
||||||
}
|
c
|
||||||
|
),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn read_format(&mut self) -> Result<FmtCode> {
|
fn read_format(&mut self) -> Result<FmtCode> {
|
||||||
let mut code = FmtCode {
|
let mut code = FmtCode {
|
||||||
arg_idx: self.next_usize()?,
|
arg_idx: self.next_usize()?,
|
||||||
..FmtCode::default()
|
..FmtCode::default()
|
||||||
};
|
};
|
||||||
if self.try_next(&[':']).is_none() {
|
if self.try_next(&[':']).is_none() {
|
||||||
self.next_code_end()?;
|
self.next_code_end()?;
|
||||||
return Ok(code)
|
return Ok(code)
|
||||||
}
|
}
|
||||||
code.align = self.next_align()?;
|
code.align = self.next_align()?;
|
||||||
code.sign = self.next_sign();
|
code.sign = self.next_sign();
|
||||||
code.width = self.next_fmt_index()?;
|
code.width = self.next_fmt_index()?;
|
||||||
if self.try_next(&['.']).is_some() {
|
if self.try_next(&['.']).is_some() {
|
||||||
code.prec = self.next_fmt_index()?;
|
code.prec = self.next_fmt_index()?;
|
||||||
}
|
}
|
||||||
code.ty = self.next_type()?;
|
code.ty = self.next_type()?;
|
||||||
self.next_code_end()?;
|
self.next_code_end()?;
|
||||||
Ok(code)
|
Ok(code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_arg<'a>(args: &'a [Value], n: Option<usize>, faidx: &mut usize)
|
fn get_arg<'a>(args: &'a [Value], n: Option<usize>, faidx: &mut usize) -> Result<&'a Value> {
|
||||||
-> Result<&'a Value> {
|
let i = match n {
|
||||||
let i = match n {
|
Some(n) => n,
|
||||||
Some(n) => n,
|
None => {
|
||||||
None => {
|
let i = *faidx;
|
||||||
let i = *faidx;
|
*faidx += 1;
|
||||||
*faidx += 1;
|
i
|
||||||
i
|
}
|
||||||
}
|
};
|
||||||
};
|
if i >= args.len() {
|
||||||
if i >= args.len() {
|
throw!(
|
||||||
throw!(*SYM_FORMAT_ERROR, "missing arguments: expected at least {}", i+1)
|
*SYM_FORMAT_ERROR,
|
||||||
}
|
"missing arguments: expected at least {}",
|
||||||
Ok(&args[i])
|
i + 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Ok(&args[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_val(args: &[Value], i: FmtIndex, faidx: &mut usize) -> Result<usize> {
|
fn get_val(args: &[Value], i: FmtIndex, faidx: &mut usize) -> Result<usize> {
|
||||||
let v = match i {
|
let v = match i {
|
||||||
FmtIndex::Imm(n) => return Ok(n),
|
FmtIndex::Imm(n) => return Ok(n),
|
||||||
FmtIndex::Arg(n) => get_arg(args, Some(n), faidx)?,
|
FmtIndex::Arg(n) => get_arg(args, Some(n), faidx)?,
|
||||||
FmtIndex::NextArg => get_arg(args, None, faidx)?,
|
FmtIndex::NextArg => get_arg(args, None, faidx)?,
|
||||||
};
|
};
|
||||||
let Value::Int(v) = v else {
|
let Value::Int(v) = v else {
|
||||||
throw!(*SYM_FORMAT_ERROR, "expected positive integer argument, found {v:#}")
|
throw!(
|
||||||
};
|
*SYM_FORMAT_ERROR,
|
||||||
if *v < 0 {
|
"expected positive integer argument, found {v:#}"
|
||||||
throw!(*SYM_FORMAT_ERROR, "expected positive integer argument, found {v}")
|
)
|
||||||
}
|
};
|
||||||
Ok(*v as usize)
|
if *v < 0 {
|
||||||
|
throw!(
|
||||||
|
*SYM_FORMAT_ERROR,
|
||||||
|
"expected positive integer argument, found {v}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Ok(*v as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_float(f: f64, prec: Option<usize>, ty: FmtType, buf: &mut LString, ty_name: &str) -> Result<()> {
|
fn format_float(
|
||||||
let res = match (prec, ty) {
|
f: f64,
|
||||||
(None, FmtType::Str) => write!(buf, "{}", Value::Float(f)),
|
prec: Option<usize>,
|
||||||
(None, FmtType::Repr) => write!(buf, "{:#}", Value::Float(f)),
|
ty: FmtType,
|
||||||
(None, FmtType::Exp(true)) => write!(buf, "{:E}", f),
|
buf: &mut LString,
|
||||||
(None, FmtType::Exp(false)) => write!(buf, "{:e}", f),
|
ty_name: &str,
|
||||||
(Some(p), FmtType::Str)
|
) -> Result<()> {
|
||||||
| (Some(p), FmtType::Repr) => write!(buf, "{0:.1$}", f, p),
|
let res = match (prec, ty) {
|
||||||
(Some(p), FmtType::Exp(true)) => write!(buf, "{0:.1$E}", f, p),
|
(None, FmtType::Str) => write!(buf, "{}", Value::Float(f)),
|
||||||
(Some(p), FmtType::Exp(false)) => write!(buf, "{0:.1$e}", f, p),
|
(None, FmtType::Repr) => write!(buf, "{:#}", Value::Float(f)),
|
||||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
(None, FmtType::Exp(true)) => write!(buf, "{:E}", f),
|
||||||
};
|
(None, FmtType::Exp(false)) => write!(buf, "{:e}", f),
|
||||||
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
(Some(p), FmtType::Str) | (Some(p), FmtType::Repr) => write!(buf, "{0:.1$}", f, p),
|
||||||
|
(Some(p), FmtType::Exp(true)) => write!(buf, "{0:.1$E}", f, p),
|
||||||
|
(Some(p), FmtType::Exp(false)) => write!(buf, "{0:.1$e}", f, p),
|
||||||
|
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
|
||||||
|
};
|
||||||
|
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_complex(cx: Complex64, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
fn format_complex(
|
||||||
format_float(cx.re, prec, ty, buf, "complex")?;
|
cx: Complex64,
|
||||||
if cx.im < 0.0 {
|
prec: Option<usize>,
|
||||||
buf.push_char('-')
|
ty: FmtType,
|
||||||
} else {
|
buf: &mut LString,
|
||||||
buf.push_char('+')
|
) -> Result<()> {
|
||||||
}
|
format_float(cx.re, prec, ty, buf, "complex")?;
|
||||||
format_float(cx.im.abs(), prec, ty, buf, "complex")?;
|
if cx.im < 0.0 {
|
||||||
Ok(())
|
buf.push_char('-')
|
||||||
|
} else {
|
||||||
|
buf.push_char('+')
|
||||||
|
}
|
||||||
|
format_float(cx.im.abs(), prec, ty, buf, "complex")?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_int(n: i64, prec: Option<usize>, ty: FmtType, buf: &mut LString, ty_name: &str) -> Result<()> {
|
fn format_int(
|
||||||
if prec.is_some() {
|
n: i64,
|
||||||
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
prec: Option<usize>,
|
||||||
}
|
ty: FmtType,
|
||||||
let res = match ty {
|
buf: &mut LString,
|
||||||
FmtType::Str => write!(buf, "{}", Value::Int(n)),
|
ty_name: &str,
|
||||||
FmtType::Repr => write!(buf, "{:#}", Value::Int(n)),
|
) -> Result<()> {
|
||||||
FmtType::Hex(caps) => write!(buf, "{}", to_lstring_radix(n, 16, caps)),
|
if prec.is_some() {
|
||||||
FmtType::Oct => write!(buf, "{}", to_lstring_radix(n, 8, false)),
|
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
||||||
FmtType::Sex => write!(buf, "{}", to_lstring_radix(n, 6, false)),
|
}
|
||||||
FmtType::Bin => write!(buf, "{}", to_lstring_radix(n, 2, false)),
|
let res = match ty {
|
||||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
FmtType::Str => write!(buf, "{}", Value::Int(n)),
|
||||||
};
|
FmtType::Repr => write!(buf, "{:#}", Value::Int(n)),
|
||||||
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
FmtType::Hex(caps) => write!(buf, "{}", to_lstring_radix(n, 16, caps)),
|
||||||
|
FmtType::Oct => write!(buf, "{}", to_lstring_radix(n, 8, false)),
|
||||||
|
FmtType::Sex => write!(buf, "{}", to_lstring_radix(n, 6, false)),
|
||||||
|
FmtType::Bin => write!(buf, "{}", to_lstring_radix(n, 2, false)),
|
||||||
|
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
|
||||||
|
};
|
||||||
|
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_ratio(n: Rational64, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
fn format_ratio(n: Rational64, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
||||||
format_int(*n.numer(), prec, ty, buf, "ratio")?;
|
format_int(*n.numer(), prec, ty, buf, "ratio")?;
|
||||||
buf.push_char('/');
|
buf.push_char('/');
|
||||||
format_int(*n.denom(), prec, ty, buf, "ratio")?;
|
format_int(*n.denom(), prec, ty, buf, "ratio")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_value(v: &Value, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
fn format_value(v: &Value, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
||||||
if prec.is_some() {
|
if prec.is_some() {
|
||||||
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value")
|
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value")
|
||||||
}
|
}
|
||||||
let res = match ty {
|
let res = match ty {
|
||||||
FmtType::Str => write!(buf, "{}", v),
|
FmtType::Str => write!(buf, "{}", v),
|
||||||
FmtType::Repr => write!(buf, "{:#}", v),
|
FmtType::Repr => write!(buf, "{:#}", v),
|
||||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value")
|
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value"),
|
||||||
};
|
};
|
||||||
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_string(mut s: &LStr, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
fn format_string(mut s: &LStr, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
||||||
if let Some(prec) = prec {
|
if let Some(prec) = prec {
|
||||||
s = &s[..prec]
|
s = &s[..prec]
|
||||||
}
|
}
|
||||||
let res = match ty {
|
let res = match ty {
|
||||||
FmtType::Str => { buf.push_lstr(s); Ok(()) },
|
FmtType::Str => {
|
||||||
FmtType::Repr => write!(buf, "{:?}", s),
|
buf.push_lstr(s);
|
||||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for string")
|
Ok(())
|
||||||
};
|
}
|
||||||
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
FmtType::Repr => write!(buf, "{:?}", s),
|
||||||
|
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for string"),
|
||||||
|
};
|
||||||
|
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pad_str(n: usize, c: char, buf: &mut LString) {
|
fn pad_str(n: usize, c: char, buf: &mut LString) {
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
buf.push_char(c)
|
buf.push_char(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_arg(args: &[Value], faidx: &mut usize, code: &LStr, res: &mut LString)
|
fn format_arg(args: &[Value], faidx: &mut usize, code: &LStr, res: &mut LString) -> Result<()> {
|
||||||
-> Result<()> {
|
if !code.is_utf8() {
|
||||||
if !code.is_utf8() {
|
throw!(
|
||||||
throw!(*SYM_FORMAT_ERROR, "code {{{}}}: code contains invalid UTF-8", code)
|
*SYM_FORMAT_ERROR,
|
||||||
}
|
"code {{{}}}: code contains invalid UTF-8",
|
||||||
let fmtcode = FmtParser::new(code).read_format()?;
|
code
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let fmtcode = FmtParser::new(code).read_format()?;
|
||||||
|
|
||||||
let mut buf = LString::new();
|
let mut buf = LString::new();
|
||||||
let fmt_arg = get_arg(args, fmtcode.arg_idx, faidx)?;
|
let fmt_arg = get_arg(args, fmtcode.arg_idx, faidx)?;
|
||||||
let prec_val = fmtcode.prec.map(|i| get_val(args, i, faidx)).transpose()?;
|
let prec_val = fmtcode.prec.map(|i| get_val(args, i, faidx)).transpose()?;
|
||||||
let width_val = fmtcode.width.map(|i| get_val(args, i, faidx)).transpose()?;
|
let width_val = fmtcode.width.map(|i| get_val(args, i, faidx)).transpose()?;
|
||||||
|
|
||||||
match fmt_arg {
|
match fmt_arg {
|
||||||
Value::Int(n) => format_int(*n, prec_val, fmtcode.ty, &mut buf, "integer")?,
|
Value::Int(n) => format_int(*n, prec_val, fmtcode.ty, &mut buf, "integer")?,
|
||||||
Value::Ratio(n) => format_ratio(*n, prec_val, fmtcode.ty, &mut buf)?,
|
Value::Ratio(n) => format_ratio(*n, prec_val, fmtcode.ty, &mut buf)?,
|
||||||
Value::Float(f) => format_float(*f, prec_val, fmtcode.ty, &mut buf, "float")?,
|
Value::Float(f) => format_float(*f, prec_val, fmtcode.ty, &mut buf, "float")?,
|
||||||
Value::Complex(n) => format_complex(*n, prec_val, fmtcode.ty, &mut buf)?,
|
Value::Complex(n) => format_complex(*n, prec_val, fmtcode.ty, &mut buf)?,
|
||||||
Value::String(s) => format_string(s, prec_val, fmtcode.ty, &mut buf)?,
|
Value::String(s) => format_string(s, prec_val, fmtcode.ty, &mut buf)?,
|
||||||
v => format_value(v, prec_val, fmtcode.ty, &mut buf)?,
|
v => format_value(v, prec_val, fmtcode.ty, &mut buf)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
let (sign, final_buf) = match fmtcode.sign {
|
let (sign, final_buf) = match fmtcode.sign {
|
||||||
FmtSign::Plus => match buf.byte_get(0) {
|
FmtSign::Plus => match buf.byte_get(0) {
|
||||||
Some(b'+') => ("+", &buf[1..]),
|
Some(b'+') => ("+", &buf[1..]),
|
||||||
Some(b'-') => ("-", &buf[1..]),
|
Some(b'-') => ("-", &buf[1..]),
|
||||||
_ => ("+", &buf[..]),
|
_ => ("+", &buf[..]),
|
||||||
}
|
},
|
||||||
FmtSign::Minus => match buf.byte_get(0) {
|
FmtSign::Minus => match buf.byte_get(0) {
|
||||||
Some(b'-') => ("-", &buf[1..]),
|
Some(b'-') => ("-", &buf[1..]),
|
||||||
_ => ("", &buf[..]),
|
_ => ("", &buf[..]),
|
||||||
}
|
},
|
||||||
FmtSign::None => ("", &buf[..])
|
FmtSign::None => ("", &buf[..]),
|
||||||
};
|
};
|
||||||
res.push_str(sign);
|
res.push_str(sign);
|
||||||
|
|
||||||
|
if let Some(w) = width_val {
|
||||||
|
let w = w.saturating_sub(final_buf.len() + sign.len());
|
||||||
|
match fmtcode.align {
|
||||||
|
Some((Align::Left, c)) => {
|
||||||
|
res.push_lstr(final_buf);
|
||||||
|
pad_str(w, c, res);
|
||||||
|
}
|
||||||
|
Some((Align::Center, c)) => {
|
||||||
|
pad_str((w + 1) / 2, c, res);
|
||||||
|
res.push_lstr(final_buf);
|
||||||
|
pad_str(w / 2, c, res);
|
||||||
|
}
|
||||||
|
Some((Align::Right, c)) => {
|
||||||
|
pad_str(w, c, res);
|
||||||
|
res.push_lstr(final_buf);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let c = if matches!(
|
||||||
|
fmt_arg,
|
||||||
|
Value::Int(_) | Value::Float(_) | Value::Ratio(_) | Value::Complex(_)
|
||||||
|
) && fmtcode.sign != FmtSign::None
|
||||||
|
{
|
||||||
|
'0'
|
||||||
|
} else {
|
||||||
|
' '
|
||||||
|
};
|
||||||
|
pad_str(w, c, res);
|
||||||
|
res.push_lstr(final_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.push_lstr(final_buf);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(w) = width_val {
|
Ok(())
|
||||||
let w = w.saturating_sub(final_buf.len() + sign.len());
|
|
||||||
match fmtcode.align {
|
|
||||||
Some((Align::Left, c)) => {
|
|
||||||
res.push_lstr(final_buf);
|
|
||||||
pad_str(w, c, res);
|
|
||||||
}
|
|
||||||
Some((Align::Center, c)) => {
|
|
||||||
pad_str((w+1)/2, c, res);
|
|
||||||
res.push_lstr(final_buf);
|
|
||||||
pad_str(w/2, c, res);
|
|
||||||
}
|
|
||||||
Some((Align::Right, c)) => {
|
|
||||||
pad_str(w, c, res);
|
|
||||||
res.push_lstr(final_buf);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let c = if matches!(fmt_arg,
|
|
||||||
Value::Int(_)
|
|
||||||
| Value::Float(_)
|
|
||||||
| Value::Ratio(_)
|
|
||||||
| Value::Complex(_)
|
|
||||||
) && fmtcode.sign != FmtSign::None {
|
|
||||||
'0'
|
|
||||||
} else {
|
|
||||||
' '
|
|
||||||
};
|
|
||||||
pad_str(w, c, res);
|
|
||||||
res.push_lstr(final_buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.push_lstr(final_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2, "fmt")]
|
#[native_func(2, "fmt")]
|
||||||
pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, fstr, fargs] = unpack_args!(args);
|
let [_, fstr, fargs] = unpack_args!(args);
|
||||||
let (Value::String(fstr), Value::List(fargs)) = (&fstr, &fargs) else {
|
let (Value::String(fstr), Value::List(fargs)) = (&fstr, &fargs) else {
|
||||||
throw!(*SYM_TYPE_ERROR, "format expected string and list, got {fstr:#} and {fargs:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"format expected string and list, got {fstr:#} and {fargs:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let mut res = LString::new();
|
let mut res = LString::new();
|
||||||
let mut faidx = 0;
|
let mut faidx = 0;
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < fstr.len() {
|
while i < fstr.len() {
|
||||||
let b = fstr.byte_at(i);
|
let b = fstr.byte_at(i);
|
||||||
i += 1;
|
i += 1;
|
||||||
if b == b'}' {
|
if b == b'}' {
|
||||||
let Some(b'}') = fstr.byte_get(i) else {
|
let Some(b'}') = fstr.byte_get(i) else {
|
||||||
throw!(*SYM_FORMAT_ERROR, "unmatched closing brace at byte {}", i-1)
|
throw!(
|
||||||
};
|
*SYM_FORMAT_ERROR,
|
||||||
i += 1;
|
"unmatched closing brace at byte {}",
|
||||||
res.push_byte(b);
|
i - 1
|
||||||
}
|
)
|
||||||
|
};
|
||||||
|
i += 1;
|
||||||
|
res.push_byte(b);
|
||||||
|
}
|
||||||
if b != b'{' {
|
if b != b'{' {
|
||||||
res.push_byte(b);
|
res.push_byte(b);
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if i >= fstr.len() {
|
if i >= fstr.len() {
|
||||||
throw!(*SYM_FORMAT_ERROR, "unclosed format specifier at byte {}", i-1)
|
throw!(
|
||||||
}
|
*SYM_FORMAT_ERROR,
|
||||||
if let Some(b'{') = fstr.byte_get(i) {
|
"unclosed format specifier at byte {}",
|
||||||
i += 1;
|
i - 1
|
||||||
res.push_byte(b);
|
)
|
||||||
continue
|
}
|
||||||
}
|
if let Some(b'{') = fstr.byte_get(i) {
|
||||||
let start = i;
|
i += 1;
|
||||||
while i < fstr.len() && fstr.byte_at(i) != b'}' {
|
res.push_byte(b);
|
||||||
i += 1;
|
continue
|
||||||
}
|
}
|
||||||
if i == fstr.len() {
|
let start = i;
|
||||||
throw!(*SYM_FORMAT_ERROR, "unclosed format specifier at byte {}", start-1)
|
while i < fstr.len() && fstr.byte_at(i) != b'}' {
|
||||||
}
|
i += 1;
|
||||||
let code = &fstr[start..i];
|
}
|
||||||
i += 1;
|
if i == fstr.len() {
|
||||||
format_arg(&fargs.borrow(), &mut faidx, code, &mut res)?;
|
throw!(
|
||||||
|
*SYM_FORMAT_ERROR,
|
||||||
|
"unclosed format specifier at byte {}",
|
||||||
|
start - 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let code = &fstr[start..i];
|
||||||
|
i += 1;
|
||||||
|
format_arg(&fargs.borrow(), &mut faidx, code, &mut res)?;
|
||||||
}
|
}
|
||||||
Ok(res.into())
|
Ok(res.into())
|
||||||
}
|
}
|
||||||
|
@ -416,4 +479,3 @@ pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn load(vm: &mut Vm) {
|
pub fn load(vm: &mut Vm) {
|
||||||
vm.set_global_name("fmt", fmt_().into());
|
vm.set_global_name("fmt", fmt_().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,17 @@
|
||||||
use std::{io::{BufRead, Write}, os::unix::ffi::OsStrExt, sync::Mutex, time::{SystemTime, UNIX_EPOCH}};
|
use std::{
|
||||||
|
io::{BufRead, Write},
|
||||||
|
os::unix::ffi::OsStrExt,
|
||||||
|
sync::Mutex,
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
|
||||||
use talc_lang::{exception::{throw, Result}, lstring::LString, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::Value, vmcall, Vm};
|
use talc_lang::{
|
||||||
|
exception::{throw, Result},
|
||||||
|
lstring::LString,
|
||||||
|
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
|
value::Value,
|
||||||
|
vmcall, Vm,
|
||||||
|
};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::{unpack_args, SYM_IO_ERROR};
|
use crate::{unpack_args, SYM_IO_ERROR};
|
||||||
|
@ -45,14 +56,22 @@ pub fn readln(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
|
||||||
|
|
||||||
#[native_func(0)]
|
#[native_func(0)]
|
||||||
pub fn time(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
|
pub fn time(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
|
||||||
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
|
let time = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("time went backwards");
|
||||||
Ok(time.as_secs_f64().into())
|
Ok(time.as_secs_f64().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(0)]
|
#[native_func(0)]
|
||||||
pub fn time_ns(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
|
pub fn time_ns(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
|
||||||
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
|
let time = SystemTime::now()
|
||||||
Ok(vec![(time.as_secs() as i64).into(), (time.subsec_nanos() as i64).into()].into())
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("time went backwards");
|
||||||
|
Ok(vec![
|
||||||
|
(time.as_secs() as i64).into(),
|
||||||
|
(time.subsec_nanos() as i64).into(),
|
||||||
|
]
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -72,7 +91,10 @@ pub fn env(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "env expected string, got {key:#}")
|
throw!(*SYM_TYPE_ERROR, "env expected string, got {key:#}")
|
||||||
};
|
};
|
||||||
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
|
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
|
||||||
throw!(*SYM_VALUE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"environment variable must not be empty or contain an equals sign or null byte"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
let val = std::env::var_os(key.to_os_str());
|
let val = std::env::var_os(key.to_os_str());
|
||||||
match val {
|
match val {
|
||||||
|
@ -88,19 +110,25 @@ pub fn setenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "setenv expected string, got {key:#}")
|
throw!(*SYM_TYPE_ERROR, "setenv expected string, got {key:#}")
|
||||||
};
|
};
|
||||||
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
|
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
|
||||||
throw!(*SYM_VALUE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"environment variable must not be empty or contain an equals sign or null byte"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
let val = val.str();
|
let val = val.str();
|
||||||
if val.as_bytes().contains(&0) {
|
if val.as_bytes().contains(&0) {
|
||||||
throw!(*SYM_VALUE_ERROR, "environment variable value must not contain a null byte")
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"environment variable value must not contain a null byte"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let Ok(guard) = ENV_LOCK.lock() else {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "failed to lock environemnt variables")
|
||||||
|
};
|
||||||
|
unsafe { std::env::set_var(key.to_os_str(), val.to_os_str()) };
|
||||||
|
drop(guard);
|
||||||
}
|
}
|
||||||
{
|
|
||||||
let Ok(guard) = ENV_LOCK.lock() else {
|
|
||||||
throw!(*SYM_VALUE_ERROR, "failed to lock environemnt variables")
|
|
||||||
};
|
|
||||||
unsafe { std::env::set_var(key.to_os_str(), val.to_os_str()) };
|
|
||||||
drop(guard);
|
|
||||||
}
|
|
||||||
Ok(Value::Nil)
|
Ok(Value::Nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,19 +139,21 @@ pub fn delenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "delenv expected string, got {key:#}")
|
throw!(*SYM_TYPE_ERROR, "delenv expected string, got {key:#}")
|
||||||
};
|
};
|
||||||
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
|
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
|
||||||
throw!(*SYM_VALUE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"environment variable must not be empty or contain an equals sign or null byte"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let Ok(guard) = ENV_LOCK.lock() else {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "failed to lock environemnt variables")
|
||||||
|
};
|
||||||
|
unsafe { std::env::remove_var(key.to_os_str()) };
|
||||||
|
drop(guard);
|
||||||
}
|
}
|
||||||
{
|
|
||||||
let Ok(guard) = ENV_LOCK.lock() else {
|
|
||||||
throw!(*SYM_VALUE_ERROR, "failed to lock environemnt variables")
|
|
||||||
};
|
|
||||||
unsafe { std::env::remove_var(key.to_os_str()) };
|
|
||||||
drop(guard);
|
|
||||||
}
|
|
||||||
Ok(Value::Nil)
|
Ok(Value::Nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn load(vm: &mut Vm) {
|
pub fn load(vm: &mut Vm) {
|
||||||
vm.set_global_name("print", print().into());
|
vm.set_global_name("print", print().into());
|
||||||
vm.set_global_name("println", println().into());
|
vm.set_global_name("println", println().into());
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use talc_lang::{exception::Result, symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{function::NativeFunc, ops::RatioExt, range::RangeType, HashValue, Value}, vmcall, vmcalliter, Vm};
|
use talc_lang::{
|
||||||
|
exception::Result,
|
||||||
|
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
|
throw,
|
||||||
|
value::{function::NativeFunc, ops::RatioExt, range::RangeType, HashValue, Value},
|
||||||
|
vmcall, vmcalliter, Vm,
|
||||||
|
};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
@ -95,9 +101,7 @@ pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, val] = unpack_args!(args);
|
let [_, val] = unpack_args!(args);
|
||||||
|
|
||||||
let v = RefCell::new(Some(val));
|
let v = RefCell::new(Some(val));
|
||||||
let f = move |_: &mut Vm, _| {
|
let f = move |_: &mut Vm, _| Ok(Value::iter_pack(v.borrow_mut().take()));
|
||||||
Ok(Value::iter_pack(v.borrow_mut().take()))
|
|
||||||
};
|
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[once]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[once]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,9 +109,7 @@ pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, val] = unpack_args!(args);
|
let [_, val] = unpack_args!(args);
|
||||||
|
|
||||||
let f = move |_: &mut Vm, _| {
|
let f = move |_: &mut Vm, _| Ok(val.clone());
|
||||||
Ok(val.clone())
|
|
||||||
};
|
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[forever]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[forever]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,18 +117,14 @@ pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
// chain iteration
|
// chain iteration
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, map, iter] = unpack_args!(args);
|
let [_, map, iter] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let f = move |vm: &mut Vm, _| {
|
let f = move |vm: &mut Vm, _| match vmcalliter!(vm; iter.clone())? {
|
||||||
match vmcalliter!(vm; iter.clone())? {
|
Some(val) => Ok(vmcall!(vm; map.clone(), val)?),
|
||||||
Some(val) => Ok(vmcall!(vm; map.clone(), val)?),
|
None => Ok(Value::iter_pack(None)),
|
||||||
None => Ok(Value::iter_pack(None)),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[map]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[map]")).into())
|
||||||
}
|
}
|
||||||
|
@ -136,14 +134,12 @@ pub fn tee(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, tee, iter] = unpack_args!(args);
|
let [_, tee, iter] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let f = move |vm: &mut Vm, _| {
|
let f = move |vm: &mut Vm, _| match vmcalliter!(vm; iter.clone())? {
|
||||||
match vmcalliter!(vm; iter.clone())? {
|
Some(val) => {
|
||||||
Some(val) => {
|
vmcall!(vm; tee.clone(), val.clone())?;
|
||||||
vmcall!(vm; tee.clone(), val.clone())?;
|
Ok(val)
|
||||||
Ok(val)
|
|
||||||
}
|
|
||||||
None => Ok(Value::iter_pack(None)),
|
|
||||||
}
|
}
|
||||||
|
None => Ok(Value::iter_pack(None)),
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[tee]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[tee]")).into())
|
||||||
}
|
}
|
||||||
|
@ -171,15 +167,13 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, filter, iter] = unpack_args!(args);
|
let [_, filter, iter] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let f = move |vm: &mut Vm, _| {
|
let f = move |vm: &mut Vm, _| loop {
|
||||||
loop {
|
let Some(next) = vmcalliter!(vm; iter.clone())? else {
|
||||||
let Some(next) = vmcalliter!(vm; iter.clone())? else {
|
return Ok(Value::iter_pack(None))
|
||||||
return Ok(Value::iter_pack(None))
|
};
|
||||||
};
|
let res = vmcall!(vm; filter.clone(), next.clone())?;
|
||||||
let res = vmcall!(vm; filter.clone(), next.clone())?;
|
if res.truthy() {
|
||||||
if res.truthy() {
|
return Ok(next)
|
||||||
return Ok(next)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[filter]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[filter]")).into())
|
||||||
|
@ -192,7 +186,10 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}")
|
throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}")
|
||||||
};
|
};
|
||||||
let Ok(count) = count.try_into() else {
|
let Ok(count) = count.try_into() else {
|
||||||
throw!(*SYM_VALUE_ERROR, "take expected nonnegative integer, got {count:#}")
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"take expected nonnegative integer, got {count:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
|
@ -218,7 +215,10 @@ pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}")
|
throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}")
|
||||||
};
|
};
|
||||||
let Ok(count) = count.try_into() else {
|
let Ok(count) = count.try_into() else {
|
||||||
throw!(*SYM_VALUE_ERROR, "count expected nonnegative integer, got {count:#}")
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"count expected nonnegative integer, got {count:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
|
@ -255,7 +255,6 @@ pub fn enumerate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[enumerate]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[enumerate]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, iter] = unpack_args!(args);
|
let [_, iter] = unpack_args!(args);
|
||||||
|
@ -292,7 +291,10 @@ pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
enum Step {
|
enum Step {
|
||||||
First, Going, #[default] End,
|
First,
|
||||||
|
Going,
|
||||||
|
#[default]
|
||||||
|
End,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -303,7 +305,10 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}")
|
throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}")
|
||||||
};
|
};
|
||||||
if by <= 0 {
|
if by <= 0 {
|
||||||
throw!(*SYM_VALUE_ERROR, "step expected positive integer, got {by:#}")
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"step expected positive integer, got {by:#}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = RefCell::new(Step::First);
|
let state = RefCell::new(Step::First);
|
||||||
|
@ -314,9 +319,9 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
*state.borrow_mut() = Step::Going;
|
*state.borrow_mut() = Step::Going;
|
||||||
}
|
}
|
||||||
Ok(Value::iter_pack(res))
|
Ok(Value::iter_pack(res))
|
||||||
},
|
}
|
||||||
Step::Going => {
|
Step::Going => {
|
||||||
for _ in 0..(by-1) {
|
for _ in 0..(by - 1) {
|
||||||
if vmcall!(vm; iter.clone())? == Value::Nil {
|
if vmcall!(vm; iter.clone())? == Value::Nil {
|
||||||
return Ok(Value::iter_pack(None))
|
return Ok(Value::iter_pack(None))
|
||||||
}
|
}
|
||||||
|
@ -326,7 +331,7 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
};
|
};
|
||||||
*state.borrow_mut() = Step::Going;
|
*state.borrow_mut() = Step::Going;
|
||||||
Ok(x)
|
Ok(x)
|
||||||
},
|
}
|
||||||
Step::End => Ok(Value::Nil),
|
Step::End => Ok(Value::Nil),
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[step]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[step]")).into())
|
||||||
|
@ -347,33 +352,30 @@ pub fn rev(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
*lst.borrow_mut() = Some(l);
|
*lst.borrow_mut() = Some(l);
|
||||||
}
|
}
|
||||||
Ok(Value::iter_pack(lst.borrow_mut().as_mut().unwrap().pop()))
|
Ok(Value::iter_pack(lst.borrow_mut().as_mut().unwrap().pop()))
|
||||||
|
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[rev]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[rev]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// join
|
// join
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, i1, i2] = unpack_args!(args);
|
let [_, i1, i2] = unpack_args!(args);
|
||||||
let i1 = i1.to_iter_function()?;
|
let i1 = i1.to_iter_function()?;
|
||||||
let i2 = i2.to_iter_function()?;
|
let i2 = i2.to_iter_function()?;
|
||||||
|
|
||||||
let f = move |vm: &mut Vm, _| {
|
let f = move |vm: &mut Vm, _| {
|
||||||
let mut res = Vec::with_capacity(2);
|
let mut res = Vec::with_capacity(2);
|
||||||
match vmcalliter!(vm; i1.clone())? {
|
match vmcalliter!(vm; i1.clone())? {
|
||||||
Some(v) => res.push(v),
|
Some(v) => res.push(v),
|
||||||
None => return Ok(Value::iter_pack(None)),
|
None => return Ok(Value::iter_pack(None)),
|
||||||
};
|
};
|
||||||
match vmcalliter!(vm; i2.clone())? {
|
match vmcalliter!(vm; i2.clone())? {
|
||||||
Some(v) => res.push(v),
|
Some(v) => res.push(v),
|
||||||
None => return Ok(Value::iter_pack(None)),
|
None => return Ok(Value::iter_pack(None)),
|
||||||
};
|
};
|
||||||
Ok(Value::from(res))
|
Ok(Value::from(res))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -383,12 +385,14 @@ pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, args] = unpack_args!(args);
|
let [_, args] = unpack_args!(args);
|
||||||
let Value::List(args) = args else {
|
let Value::List(args) = args else {
|
||||||
throw!(*SYM_TYPE_ERROR, "zipn expected list, got {args:#}")
|
throw!(*SYM_TYPE_ERROR, "zipn expected list, got {args:#}")
|
||||||
};
|
};
|
||||||
let iters = args.borrow().iter()
|
let iters = args
|
||||||
.map(|i| i.clone().to_iter_function())
|
.borrow()
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.iter()
|
||||||
|
.map(|i| i.clone().to_iter_function())
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
let f = move |vm: &mut Vm, _| {
|
let f = move |vm: &mut Vm, _| {
|
||||||
let mut res = Vec::with_capacity(iters.len());
|
let mut res = Vec::with_capacity(iters.len());
|
||||||
|
@ -407,19 +411,19 @@ pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, i1, i2] = unpack_args!(args);
|
let [_, i1, i2] = unpack_args!(args);
|
||||||
let i1 = i1.to_iter_function()?;
|
let i1 = i1.to_iter_function()?;
|
||||||
let i2 = i2.to_iter_function()?;
|
let i2 = i2.to_iter_function()?;
|
||||||
|
|
||||||
let state = RefCell::new((false, false));
|
let state = RefCell::new((false, false));
|
||||||
let f = move |vm: &mut Vm, _| {
|
let f = move |vm: &mut Vm, _| {
|
||||||
let mut s = state.borrow_mut();
|
let mut s = state.borrow_mut();
|
||||||
if s.1 {
|
if s.1 {
|
||||||
return Ok(Value::iter_pack(None));
|
return Ok(Value::iter_pack(None))
|
||||||
}
|
}
|
||||||
let n = s.0;
|
let n = s.0;
|
||||||
s.0 = !s.0;
|
s.0 = !s.0;
|
||||||
drop(s);
|
drop(s);
|
||||||
let iter = if n { i1.clone() } else { i2.clone() };
|
let iter = if n { i1.clone() } else { i2.clone() };
|
||||||
if let Some(v) = vmcalliter!(vm; iter)? {
|
if let Some(v) = vmcalliter!(vm; iter)? {
|
||||||
Ok(v)
|
Ok(v)
|
||||||
} else {
|
} else {
|
||||||
|
@ -434,18 +438,20 @@ pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, args] = unpack_args!(args);
|
let [_, args] = unpack_args!(args);
|
||||||
let Value::List(args) = args else {
|
let Value::List(args) = args else {
|
||||||
throw!(*SYM_TYPE_ERROR, "alternaten expected list, got {args:#}")
|
throw!(*SYM_TYPE_ERROR, "alternaten expected list, got {args:#}")
|
||||||
};
|
};
|
||||||
let iters = args.borrow().iter()
|
let iters = args
|
||||||
.map(|i| i.clone().to_iter_function())
|
.borrow()
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.iter()
|
||||||
|
.map(|i| i.clone().to_iter_function())
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
let state = RefCell::new((0, false));
|
let state = RefCell::new((0, false));
|
||||||
let f = move |vm: &mut Vm, _| {
|
let f = move |vm: &mut Vm, _| {
|
||||||
let mut s = state.borrow_mut();
|
let mut s = state.borrow_mut();
|
||||||
if s.1 {
|
if s.1 {
|
||||||
return Ok(Value::iter_pack(None));
|
return Ok(Value::iter_pack(None))
|
||||||
}
|
}
|
||||||
let n = s.0;
|
let n = s.0;
|
||||||
s.0 = (s.0 + 1) % iters.len();
|
s.0 = (s.0 + 1) % iters.len();
|
||||||
|
@ -463,7 +469,11 @@ pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
enum Intersperse {
|
enum Intersperse {
|
||||||
Init, Waiting, HasNext(Value), #[default] End
|
Init,
|
||||||
|
Waiting,
|
||||||
|
HasNext(Value),
|
||||||
|
#[default]
|
||||||
|
End,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -472,38 +482,35 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let state = RefCell::new(Intersperse::Init);
|
let state = RefCell::new(Intersperse::Init);
|
||||||
let f = move |vm: &mut Vm, _| {
|
let f = move |vm: &mut Vm, _| match state.take() {
|
||||||
match state.take() {
|
Intersperse::Init => {
|
||||||
Intersperse::Init => {
|
if let Some(v) = vmcalliter!(vm; iter.clone())? {
|
||||||
if let Some(v) = vmcalliter!(vm; iter.clone())? {
|
|
||||||
*state.borrow_mut() = Intersperse::Waiting;
|
|
||||||
Ok(v)
|
|
||||||
} else {
|
|
||||||
*state.borrow_mut() = Intersperse::End;
|
|
||||||
Ok(Value::iter_pack(None))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Intersperse::Waiting => {
|
|
||||||
if let Some(v) = vmcalliter!(vm; iter.clone())? {
|
|
||||||
*state.borrow_mut() = Intersperse::HasNext(v);
|
|
||||||
Ok(val.clone())
|
|
||||||
} else {
|
|
||||||
*state.borrow_mut() = Intersperse::End;
|
|
||||||
Ok(Value::iter_pack(None))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Intersperse::HasNext(v) => {
|
|
||||||
*state.borrow_mut() = Intersperse::Waiting;
|
*state.borrow_mut() = Intersperse::Waiting;
|
||||||
Ok(v)
|
Ok(v)
|
||||||
},
|
} else {
|
||||||
Intersperse::End => Ok(Value::iter_pack(None)),
|
*state.borrow_mut() = Intersperse::End;
|
||||||
|
Ok(Value::iter_pack(None))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Intersperse::Waiting => {
|
||||||
|
if let Some(v) = vmcalliter!(vm; iter.clone())? {
|
||||||
|
*state.borrow_mut() = Intersperse::HasNext(v);
|
||||||
|
Ok(val.clone())
|
||||||
|
} else {
|
||||||
|
*state.borrow_mut() = Intersperse::End;
|
||||||
|
Ok(Value::iter_pack(None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Intersperse::HasNext(v) => {
|
||||||
|
*state.borrow_mut() = Intersperse::Waiting;
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
Intersperse::End => Ok(Value::iter_pack(None)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[intersperse]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[intersperse]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, iter1, iter2] = unpack_args!(args);
|
let [_, iter1, iter2] = unpack_args!(args);
|
||||||
|
@ -591,14 +598,10 @@ pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cart_prod]")).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cart_prod]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// end iteration
|
// end iteration
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, iter] = unpack_args!(args);
|
let [_, iter] = unpack_args!(args);
|
||||||
|
@ -606,7 +609,7 @@ pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||||
result.push(value);
|
result.push(value);
|
||||||
};
|
}
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -617,16 +620,23 @@ pub fn table(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let mut result = HashMap::new();
|
let mut result = HashMap::new();
|
||||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||||
let Value::List(l) = value else {
|
let Value::List(l) = value else {
|
||||||
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list, got {value:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"table expected iterator to yield list, got {value:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let mut l = Rc::unwrap_or_clone(l).take();
|
let mut l = Rc::unwrap_or_clone(l).take();
|
||||||
if l.len() != 2 {
|
if l.len() != 2 {
|
||||||
throw!(*SYM_VALUE_ERROR, "table: iterator yielded list of length {} (expected 2)", l.len())
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"table: iterator yielded list of length {} (expected 2)",
|
||||||
|
l.len()
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let v = l.pop().unwrap();
|
let v = l.pop().unwrap();
|
||||||
let k = l.pop().unwrap();
|
let k = l.pop().unwrap();
|
||||||
result.insert(k.try_into()?, v);
|
result.insert(k.try_into()?, v);
|
||||||
};
|
}
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,15 +647,16 @@ pub fn len(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Value::String(s) => return Ok((s.chars().count() as i64).into()),
|
Value::String(s) => return Ok((s.chars().count() as i64).into()),
|
||||||
Value::List(l) => return Ok((l.borrow().len() as i64).into()),
|
Value::List(l) => return Ok((l.borrow().len() as i64).into()),
|
||||||
Value::Table(t) => return Ok((t.borrow().len() as i64).into()),
|
Value::Table(t) => return Ok((t.borrow().len() as i64).into()),
|
||||||
Value::Range(r) if r.ty != RangeType::Endless
|
Value::Range(r) if r.ty != RangeType::Endless => {
|
||||||
=> return Ok((r.len().unwrap() as i64).into()),
|
return Ok((r.len().unwrap() as i64).into())
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
let iter = value.to_iter_function()?;
|
let iter = value.to_iter_function()?;
|
||||||
let mut len = 0;
|
let mut len = 0;
|
||||||
while vmcalliter!(vm; iter.clone())?.is_some() {
|
while vmcalliter!(vm; iter.clone())?.is_some() {
|
||||||
len += 1;
|
len += 1;
|
||||||
};
|
}
|
||||||
Ok(len.into())
|
Ok(len.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -764,8 +775,10 @@ pub fn contains(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
for _ in 0.. {
|
for _ in 0.. {
|
||||||
match vmcalliter!(vm; iter.clone())? {
|
match vmcalliter!(vm; iter.clone())? {
|
||||||
None => return Ok(Value::Nil),
|
None => return Ok(Value::Nil),
|
||||||
Some(v) => if v == val {
|
Some(v) => {
|
||||||
return Ok(true.into())
|
if v == val {
|
||||||
|
return Ok(true.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -780,8 +793,10 @@ pub fn index_of(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
for i in 0.. {
|
for i in 0.. {
|
||||||
match vmcalliter!(vm; iter.clone())? {
|
match vmcalliter!(vm; iter.clone())? {
|
||||||
None => return Ok(Value::Nil),
|
None => return Ok(Value::Nil),
|
||||||
Some(v) => if v == val {
|
Some(v) => {
|
||||||
return Ok(i.into())
|
if v == val {
|
||||||
|
return Ok(i.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -796,8 +811,10 @@ pub fn index_if(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
for i in 0.. {
|
for i in 0.. {
|
||||||
match vmcalliter!(vm; iter.clone())? {
|
match vmcalliter!(vm; iter.clone())? {
|
||||||
None => return Ok(Value::Nil),
|
None => return Ok(Value::Nil),
|
||||||
Some(v) => if vmcall!(vm; func.clone(), v)?.truthy() {
|
Some(v) => {
|
||||||
return Ok(i.into())
|
if vmcall!(vm; func.clone(), v)?.truthy() {
|
||||||
|
return Ok(i.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -844,25 +861,25 @@ pub fn mean(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let mut sum = Value::Float(0.0);
|
let mut sum = Value::Float(0.0);
|
||||||
let mut count = Value::Int(0);
|
let mut count = Value::Int(0);
|
||||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||||
sum = (sum + value)?;
|
sum = (sum + value)?;
|
||||||
count = (count + Value::from(1))?;
|
count = (count + Value::from(1))?;
|
||||||
}
|
}
|
||||||
sum / count
|
sum / count
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> {
|
fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> {
|
||||||
let mut m = Value::Float(0.0);
|
let mut m = Value::Float(0.0);
|
||||||
let mut s = Value::Float(0.0);
|
let mut s = Value::Float(0.0);
|
||||||
let mut k = 1;
|
let mut k = 1;
|
||||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||||
let old_m = m.clone();
|
let old_m = m.clone();
|
||||||
m = (m.clone() + ((value.clone() - m.clone())?/Value::Int(k))?)?;
|
m = (m.clone() + ((value.clone() - m.clone())? / Value::Int(k))?)?;
|
||||||
s = (s + ((value.clone() - m.clone())?*(value - old_m)?)?)?;
|
s = (s + ((value.clone() - m.clone())? * (value - old_m)?)?)?;
|
||||||
k += 1;
|
k += 1;
|
||||||
}
|
}
|
||||||
s / Value::Int(k - if pop { 1 } else { 2 })
|
s / Value::Int(k - if pop { 1 } else { 2 })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -870,7 +887,7 @@ pub fn variance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, iter] = unpack_args!(args);
|
let [_, iter] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
variance_inner(vm, iter, false)
|
variance_inner(vm, iter, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -878,14 +895,14 @@ pub fn stdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, iter] = unpack_args!(args);
|
let [_, iter] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let v = variance_inner(vm, iter, false)?;
|
let v = variance_inner(vm, iter, false)?;
|
||||||
Ok(match v {
|
Ok(match v {
|
||||||
Value::Int(n) => Value::Float((n as f64).sqrt()),
|
Value::Int(n) => Value::Float((n as f64).sqrt()),
|
||||||
Value::Float(f) => Value::Float(f.sqrt()),
|
Value::Float(f) => Value::Float(f.sqrt()),
|
||||||
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
|
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
|
||||||
Value::Complex(c) => Value::Complex(c.sqrt()),
|
Value::Complex(c) => Value::Complex(c.sqrt()),
|
||||||
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}")
|
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -893,7 +910,7 @@ pub fn pvariance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, iter] = unpack_args!(args);
|
let [_, iter] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
variance_inner(vm, iter, true)
|
variance_inner(vm, iter, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -901,14 +918,14 @@ pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, iter] = unpack_args!(args);
|
let [_, iter] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let v = variance_inner(vm, iter, true)?;
|
let v = variance_inner(vm, iter, true)?;
|
||||||
Ok(match v {
|
Ok(match v {
|
||||||
Value::Int(n) => Value::Float((n as f64).sqrt()),
|
Value::Int(n) => Value::Float((n as f64).sqrt()),
|
||||||
Value::Float(f) => Value::Float(f.sqrt()),
|
Value::Float(f) => Value::Float(f.sqrt()),
|
||||||
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
|
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
|
||||||
Value::Complex(c) => Value::Complex(c.sqrt()),
|
Value::Complex(c) => Value::Complex(c.sqrt()),
|
||||||
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}")
|
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, PartialOrd)]
|
#[derive(PartialEq, PartialOrd)]
|
||||||
|
@ -917,9 +934,9 @@ impl std::cmp::Eq for OrdValue {}
|
||||||
|
|
||||||
#[allow(clippy::derive_ord_xor_partial_ord)]
|
#[allow(clippy::derive_ord_xor_partial_ord)]
|
||||||
impl std::cmp::Ord for OrdValue {
|
impl std::cmp::Ord for OrdValue {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Less)
|
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Less)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -927,23 +944,22 @@ pub fn median(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, iter] = unpack_args!(args);
|
let [_, iter] = unpack_args!(args);
|
||||||
let iter = iter.to_iter_function()?;
|
let iter = iter.to_iter_function()?;
|
||||||
|
|
||||||
let mut vals = Vec::new();
|
let mut vals = Vec::new();
|
||||||
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
while let Some(value) = vmcalliter!(vm; iter.clone())? {
|
||||||
vals.push(OrdValue(value));
|
vals.push(OrdValue(value));
|
||||||
|
}
|
||||||
|
let count = vals.len();
|
||||||
|
if count == 0 {
|
||||||
|
Ok(Value::Nil)
|
||||||
|
} else if count % 2 == 0 {
|
||||||
|
let (_, _, hi) = vals.select_nth_unstable(count / 2 - 1);
|
||||||
|
let (_, _, _) = hi.select_nth_unstable(0);
|
||||||
|
let m2 = vals.swap_remove(count / 2);
|
||||||
|
let m1 = vals.swap_remove(count / 2 - 1);
|
||||||
|
(m1.0 + m2.0)? / Value::Int(2)
|
||||||
|
} else {
|
||||||
|
let (_, _, _) = vals.select_nth_unstable(count / 2);
|
||||||
|
let m = vals.swap_remove(count / 2);
|
||||||
|
m.0 / Value::Int(1)
|
||||||
}
|
}
|
||||||
let count = vals.len();
|
|
||||||
if count == 0 {
|
|
||||||
Ok(Value::Nil)
|
|
||||||
} else if count % 2 == 0 {
|
|
||||||
let (_, _, hi) = vals.select_nth_unstable(count/2 - 1);
|
|
||||||
let (_, _, _) = hi.select_nth_unstable(0);
|
|
||||||
let m2 = vals.swap_remove(count/2);
|
|
||||||
let m1 = vals.swap_remove(count/2 - 1);
|
|
||||||
(m1.0 + m2.0)? / Value::Int(2)
|
|
||||||
} else {
|
|
||||||
let (_, _, _) = vals.select_nth_unstable(count/2);
|
|
||||||
let m = vals.swap_remove(count/2);
|
|
||||||
m.0 / Value::Int(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,22 @@
|
||||||
#![allow(clippy::mutable_key_type)]
|
#![allow(clippy::mutable_key_type)]
|
||||||
|
|
||||||
use talc_lang::{symbol::{symbol, Symbol}, Vm};
|
use talc_lang::{
|
||||||
|
symbol::{symbol, Symbol},
|
||||||
|
Vm,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod value;
|
pub mod collection;
|
||||||
|
pub mod exception;
|
||||||
|
pub mod file;
|
||||||
|
pub mod format;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod iter;
|
pub mod iter;
|
||||||
pub mod exception;
|
|
||||||
pub mod num;
|
pub mod num;
|
||||||
pub mod collection;
|
#[cfg(feature = "random")]
|
||||||
pub mod string;
|
|
||||||
pub mod format;
|
|
||||||
pub mod file;
|
|
||||||
pub mod regex;
|
|
||||||
#[cfg(feature="random")]
|
|
||||||
pub mod random;
|
pub mod random;
|
||||||
|
pub mod regex;
|
||||||
|
pub mod string;
|
||||||
|
pub mod value;
|
||||||
|
|
||||||
pub fn load_all(vm: &mut Vm) {
|
pub fn load_all(vm: &mut Vm) {
|
||||||
value::load(vm);
|
value::load(vm);
|
||||||
|
@ -26,7 +29,7 @@ pub fn load_all(vm: &mut Vm) {
|
||||||
format::load(vm);
|
format::load(vm);
|
||||||
regex::load(vm);
|
regex::load(vm);
|
||||||
file::load(vm);
|
file::load(vm);
|
||||||
#[cfg(feature="random")]
|
#[cfg(feature = "random")]
|
||||||
random::load(vm);
|
random::load(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +38,6 @@ lazy_static::lazy_static! {
|
||||||
pub static ref SYM_FORMAT_ERROR: Symbol = symbol!(format_error);
|
pub static ref SYM_FORMAT_ERROR: Symbol = symbol!(format_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
macro_rules! unpack_args {
|
macro_rules! unpack_args {
|
||||||
($e:expr) => {
|
($e:expr) => {
|
||||||
($e).try_into().expect("bypassed arity check")
|
($e).try_into().expect("bypassed arity check")
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use talc_lang::{exception::Result, parser::{parse_int, to_lstring_radix}, symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{ops::RatioExt, Complex64, Value}, vmcalliter, Vm};
|
use talc_lang::{
|
||||||
|
exception::Result,
|
||||||
|
parser::{parse_int, to_lstring_radix},
|
||||||
|
symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
|
throw,
|
||||||
|
value::{ops::RatioExt, Complex64, Value},
|
||||||
|
vmcalliter, Vm,
|
||||||
|
};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
@ -49,8 +56,8 @@ pub fn load(vm: &mut Vm) {
|
||||||
|
|
||||||
vm.set_global_name("inf", (f64::INFINITY).into());
|
vm.set_global_name("inf", (f64::INFINITY).into());
|
||||||
vm.set_global_name("NaN", (f64::NAN).into());
|
vm.set_global_name("NaN", (f64::NAN).into());
|
||||||
vm.set_global_name("infi", Complex64::new(0.0,f64::INFINITY).into());
|
vm.set_global_name("infi", Complex64::new(0.0, f64::INFINITY).into());
|
||||||
vm.set_global_name("NaNi", Complex64::new(0.0,f64::NAN).into());
|
vm.set_global_name("NaNi", Complex64::new(0.0, f64::NAN).into());
|
||||||
|
|
||||||
vm.set_global_name("bin", bin().into());
|
vm.set_global_name("bin", bin().into());
|
||||||
vm.set_global_name("sex", sex().into());
|
vm.set_global_name("sex", sex().into());
|
||||||
|
@ -163,7 +170,10 @@ pub fn hex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x, radix] = unpack_args!(args);
|
let [_, x, radix] = unpack_args!(args);
|
||||||
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
|
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
|
||||||
throw!(*SYM_TYPE_ERROR, "to_radix expected integer arguments, got {x:#} and {radix:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"to_radix expected integer arguments, got {x:#} and {radix:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
if *radix < 2 || *radix > 36 {
|
if *radix < 2 || *radix > 36 {
|
||||||
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 0..=36")
|
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 0..=36")
|
||||||
|
@ -175,10 +185,16 @@ pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x, radix] = unpack_args!(args);
|
let [_, x, radix] = unpack_args!(args);
|
||||||
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
|
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
|
||||||
throw!(*SYM_TYPE_ERROR, "to_radix_upper expected integer arguments, got {x:#} and {radix:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"to_radix_upper expected integer arguments, got {x:#} and {radix:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
if *radix < 2 || *radix > 36 {
|
if *radix < 2 || *radix > 36 {
|
||||||
throw!(*SYM_VALUE_ERROR, "to_radix_upper expected radix in range 0..=36")
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"to_radix_upper expected radix in range 0..=36"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Ok(to_lstring_radix(*x, *radix as u32, true).into())
|
Ok(to_lstring_radix(*x, *radix as u32, true).into())
|
||||||
}
|
}
|
||||||
|
@ -187,14 +203,23 @@ pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, s, radix] = unpack_args!(args);
|
let [_, s, radix] = unpack_args!(args);
|
||||||
let (Value::String(s), Value::Int(radix)) = (&s, &radix) else {
|
let (Value::String(s), Value::Int(radix)) = (&s, &radix) else {
|
||||||
throw!(*SYM_TYPE_ERROR, "from_radix expected string and integer arguments, got {s:#} and {radix:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"from_radix expected string and integer arguments, got {s:#} and {radix:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
if *radix < 2 || *radix > 36 {
|
if *radix < 2 || *radix > 36 {
|
||||||
throw!(*SYM_VALUE_ERROR, "from_radix expected radix in range 0..=36")
|
throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"from_radix expected radix in range 0..=36"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
match parse_int(s.as_ref(), *radix as u32) {
|
match parse_int(s.as_ref(), *radix as u32) {
|
||||||
Ok(v) => Ok(v.into()),
|
Ok(v) => Ok(v.into()),
|
||||||
Err(_) => throw!(*SYM_VALUE_ERROR, "string was not a valid integer in given radix"),
|
Err(_) => throw!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"string was not a valid integer in given radix"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +229,9 @@ pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
|
||||||
fn isqrt_inner(mut n: i64) -> i64 {
|
fn isqrt_inner(mut n: i64) -> i64 {
|
||||||
assert!(n >= 0, "isqrt input should be nonnegative");
|
assert!(n >= 0, "isqrt input should be nonnegative");
|
||||||
if n < 2 { return n }
|
if n < 2 {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
let mut c = 0;
|
let mut c = 0;
|
||||||
let mut d = 1 << 62;
|
let mut d = 1 << 62;
|
||||||
|
@ -217,8 +244,7 @@ fn isqrt_inner(mut n: i64) -> i64 {
|
||||||
if n >= c + d {
|
if n >= c + d {
|
||||||
n -= c + d;
|
n -= c + d;
|
||||||
c = (c >> 1) + d;
|
c = (c >> 1) + d;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
c >>= 1;
|
c >>= 1;
|
||||||
}
|
}
|
||||||
d >>= 2;
|
d >>= 2;
|
||||||
|
@ -242,23 +268,25 @@ pub fn gcd_inner(a: i64, b: i64) -> i64 {
|
||||||
let z = az.min(bz);
|
let z = az.min(bz);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if a > b {
|
if a > b {
|
||||||
std::mem::swap(&mut a, &mut b);
|
std::mem::swap(&mut a, &mut b);
|
||||||
}
|
}
|
||||||
b -= a;
|
b -= a;
|
||||||
if b == 0 {
|
if b == 0 {
|
||||||
return a << z;
|
return a << z
|
||||||
}
|
}
|
||||||
b >>= b.trailing_zeros();
|
b >>= b.trailing_zeros();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x] = unpack_args!(args);
|
let [_, x] = unpack_args!(args);
|
||||||
let Value::Int(x) = x else {
|
let Value::Int(x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "isqrt expected integer argument, got {x:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"isqrt expected integer argument, got {x:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
if x < 0 {
|
if x < 0 {
|
||||||
throw!(*SYM_VALUE_ERROR, "isqrt: argument must be positive")
|
throw!(*SYM_VALUE_ERROR, "isqrt: argument must be positive")
|
||||||
|
@ -266,12 +294,14 @@ pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(isqrt_inner(x).into())
|
Ok(isqrt_inner(x).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x] = unpack_args!(args);
|
let [_, x] = unpack_args!(args);
|
||||||
let Value::Int(x) = x else {
|
let Value::Int(x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "isprime expected integer argument, got {x:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"isprime expected integer argument, got {x:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
if x < 2 {
|
if x < 2 {
|
||||||
return Ok(false.into())
|
return Ok(false.into())
|
||||||
|
@ -286,9 +316,13 @@ pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
}
|
}
|
||||||
let lim = isqrt_inner(x);
|
let lim = isqrt_inner(x);
|
||||||
let mut i = 12;
|
let mut i = 12;
|
||||||
while i <= lim+1 {
|
while i <= lim + 1 {
|
||||||
if x % (i - 1) == 0 { return Ok(false.into()) }
|
if x % (i - 1) == 0 {
|
||||||
if x % (i + 1) == 0 { return Ok(false.into()) }
|
return Ok(false.into())
|
||||||
|
}
|
||||||
|
if x % (i + 1) == 0 {
|
||||||
|
return Ok(false.into())
|
||||||
|
}
|
||||||
i += 6;
|
i += 6;
|
||||||
}
|
}
|
||||||
Ok(true.into())
|
Ok(true.into())
|
||||||
|
@ -309,11 +343,11 @@ pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, args] = unpack_args!(args);
|
let [_, args] = unpack_args!(args);
|
||||||
let args = args.to_iter_function()?;
|
let args = args.to_iter_function()?;
|
||||||
|
|
||||||
let mut g = 0;
|
let mut g = 0;
|
||||||
|
|
||||||
while let Some(a) = vmcalliter!(vm; args.clone())? {
|
while let Some(a) = vmcalliter!(vm; args.clone())? {
|
||||||
let Value::Int(a) = a else {
|
let Value::Int(a) = a else {
|
||||||
throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}")
|
throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}")
|
||||||
};
|
};
|
||||||
|
@ -333,41 +367,46 @@ pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}")
|
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}")
|
||||||
};
|
};
|
||||||
let g = gcd_inner(x, y);
|
let g = gcd_inner(x, y);
|
||||||
if g == 0 {
|
if g == 0 {
|
||||||
Ok(Value::from(0))
|
Ok(Value::from(0))
|
||||||
} else {
|
} else {
|
||||||
(Value::from(x)/Value::from(g))? * Value::from(y)
|
(Value::from(x) / Value::from(g))? * Value::from(y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, args] = unpack_args!(args);
|
let [_, args] = unpack_args!(args);
|
||||||
let args = args.to_iter_function()?;
|
let args = args.to_iter_function()?;
|
||||||
|
|
||||||
let mut l = 1;
|
let mut l = 1;
|
||||||
|
|
||||||
while let Some(a) = vmcalliter!(vm; args.clone())? {
|
while let Some(a) = vmcalliter!(vm; args.clone())? {
|
||||||
let Value::Int(a) = a else {
|
let Value::Int(a) = a else {
|
||||||
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
|
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
|
||||||
};
|
};
|
||||||
let g = gcd_inner(l, a);
|
let g = gcd_inner(l, a);
|
||||||
if g == 0 { return Ok(Value::from(0)) };
|
if g == 0 {
|
||||||
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?;
|
return Ok(Value::from(0))
|
||||||
let Value::Int(new_l) = new_l else {
|
};
|
||||||
|
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?;
|
||||||
|
let Value::Int(new_l) = new_l else {
|
||||||
unreachable!("int//int * int != int")
|
unreachable!("int//int * int != int")
|
||||||
};
|
};
|
||||||
l = new_l;
|
l = new_l;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::from(l))
|
Ok(Value::from(l))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x] = unpack_args!(args);
|
let [_, x] = unpack_args!(args);
|
||||||
let Value::Int(mut x) = x else {
|
let Value::Int(mut x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "factors expected integer argument, got {x:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"factors expected integer argument, got {x:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let mut factors = Vec::new();
|
let mut factors = Vec::new();
|
||||||
if x <= 1 {
|
if x <= 1 {
|
||||||
|
@ -382,7 +421,7 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
factors.push(Value::Int(3));
|
factors.push(Value::Int(3));
|
||||||
}
|
}
|
||||||
let mut i = 5;
|
let mut i = 5;
|
||||||
while x >= i*i {
|
while x >= i * i {
|
||||||
while x % i == 0 {
|
while x % i == 0 {
|
||||||
x /= i;
|
x /= i;
|
||||||
factors.push(Value::Int(i));
|
factors.push(Value::Int(i));
|
||||||
|
@ -401,49 +440,53 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn totient_prime(n: &mut u64, p: u64) -> u64 {
|
fn totient_prime(n: &mut u64, p: u64) -> u64 {
|
||||||
if *n % p != 0 {
|
if *n % p != 0 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
*n /= p;
|
*n /= p;
|
||||||
let mut v = p - 1;
|
let mut v = p - 1;
|
||||||
while *n % p == 0 {
|
while *n % p == 0 {
|
||||||
*n /= p;
|
*n /= p;
|
||||||
v *= p;
|
v *= p;
|
||||||
}
|
}
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x] = unpack_args!(args);
|
let [_, x] = unpack_args!(args);
|
||||||
let Value::Int(x) = x else {
|
let Value::Int(x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "totient expected integer argument, got {x:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"totient expected integer argument, got {x:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
if x <= 1 {
|
if x <= 1 {
|
||||||
return Ok(1.into())
|
return Ok(1.into())
|
||||||
}
|
}
|
||||||
let mut x = x as u64;
|
let mut x = x as u64;
|
||||||
let mut totient = 1;
|
let mut totient = 1;
|
||||||
if x & 1 == 0 { x >>= 1; }
|
if x & 1 == 0 {
|
||||||
|
x >>= 1;
|
||||||
|
}
|
||||||
while x & 1 == 0 {
|
while x & 1 == 0 {
|
||||||
x >>= 1;
|
x >>= 1;
|
||||||
totient <<= 1;
|
totient <<= 1;
|
||||||
}
|
}
|
||||||
totient *= totient_prime(&mut x, 3);
|
totient *= totient_prime(&mut x, 3);
|
||||||
let mut i = 5;
|
let mut i = 5;
|
||||||
while x >= i*i {
|
while x >= i * i {
|
||||||
totient *= totient_prime(&mut x, i);
|
totient *= totient_prime(&mut x, i);
|
||||||
i += 2;
|
i += 2;
|
||||||
totient *= totient_prime(&mut x, i);
|
totient *= totient_prime(&mut x, i);
|
||||||
i += 4;
|
i += 4;
|
||||||
}
|
}
|
||||||
if x > 1 {
|
if x > 1 {
|
||||||
totient *= x - 1;
|
totient *= x - 1;
|
||||||
}
|
}
|
||||||
Ok((totient as i64).into())
|
Ok((totient as i64).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// numeric operations
|
// numeric operations
|
||||||
//
|
//
|
||||||
|
@ -451,21 +494,21 @@ pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
pub fn min(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn min(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x, y] = unpack_args!(args);
|
let [_, x, y] = unpack_args!(args);
|
||||||
if y < x {
|
if y < x {
|
||||||
Ok(y)
|
Ok(y)
|
||||||
} else {
|
} else {
|
||||||
Ok(x)
|
Ok(x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
pub fn max(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn max(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x, y] = unpack_args!(args);
|
let [_, x, y] = unpack_args!(args);
|
||||||
if y > x {
|
if y > x {
|
||||||
Ok(y)
|
Ok(y)
|
||||||
} else {
|
} else {
|
||||||
Ok(x)
|
Ok(x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -533,7 +576,7 @@ pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ordering::Greater => Ok(Value::Ratio(1.into())),
|
Ordering::Greater => Ok(Value::Ratio(1.into())),
|
||||||
Ordering::Less => Ok(Value::Ratio((-1).into())),
|
Ordering::Less => Ok(Value::Ratio((-1).into())),
|
||||||
Ordering::Equal => Ok(Value::Ratio(0.into())),
|
Ordering::Equal => Ok(Value::Ratio(0.into())),
|
||||||
}
|
},
|
||||||
x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"),
|
x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -542,7 +585,6 @@ pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
// floating-point operations
|
// floating-point operations
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn signum(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn signum(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x] = unpack_args!(args);
|
let [_, x] = unpack_args!(args);
|
||||||
|
@ -557,7 +599,10 @@ pub fn classify(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x] = unpack_args!(args);
|
let [_, x] = unpack_args!(args);
|
||||||
let x = to_floaty(x);
|
let x = to_floaty(x);
|
||||||
let Value::Float(x) = x else {
|
let Value::Float(x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "classify expected real argument, got {x:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"classify expected real argument, got {x:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
Ok(match x.classify() {
|
Ok(match x.classify() {
|
||||||
std::num::FpCategory::Nan => *SYM_NAN,
|
std::num::FpCategory::Nan => *SYM_NAN,
|
||||||
|
@ -565,7 +610,8 @@ pub fn classify(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
std::num::FpCategory::Zero => *SYM_ZERO,
|
std::num::FpCategory::Zero => *SYM_ZERO,
|
||||||
std::num::FpCategory::Subnormal => *SYM_SUBNORMAL,
|
std::num::FpCategory::Subnormal => *SYM_SUBNORMAL,
|
||||||
std::num::FpCategory::Normal => *SYM_NORMAL,
|
std::num::FpCategory::Normal => *SYM_NORMAL,
|
||||||
}.into())
|
}
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -575,7 +621,10 @@ pub fn isnan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
|
Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
|
||||||
Value::Float(x) => Ok(x.is_nan().into()),
|
Value::Float(x) => Ok(x.is_nan().into()),
|
||||||
Value::Complex(z) => Ok(z.is_nan().into()),
|
Value::Complex(z) => Ok(z.is_nan().into()),
|
||||||
v => throw!(*SYM_TYPE_ERROR, "isnan expected numeric argument, got {v:#}"),
|
v => throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"isnan expected numeric argument, got {v:#}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,7 +635,10 @@ pub fn isfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Value::Int(_) | Value::Ratio(_) => Ok(true.into()),
|
Value::Int(_) | Value::Ratio(_) => Ok(true.into()),
|
||||||
Value::Float(x) => Ok(x.is_finite().into()),
|
Value::Float(x) => Ok(x.is_finite().into()),
|
||||||
Value::Complex(z) => Ok(z.is_finite().into()),
|
Value::Complex(z) => Ok(z.is_finite().into()),
|
||||||
v => throw!(*SYM_TYPE_ERROR, "isfinite expected numeric argument, got {v:#}"),
|
v => throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"isfinite expected numeric argument, got {v:#}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,7 +649,10 @@ pub fn isinfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
|
Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
|
||||||
Value::Float(x) => Ok(x.is_infinite().into()),
|
Value::Float(x) => Ok(x.is_infinite().into()),
|
||||||
Value::Complex(z) => Ok(z.is_infinite().into()),
|
Value::Complex(z) => Ok(z.is_infinite().into()),
|
||||||
v => throw!(*SYM_TYPE_ERROR, "isinfinite expected numeric argument, got {v:#}"),
|
v => throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"isinfinite expected numeric argument, got {v:#}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,7 +660,10 @@ pub fn isinfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, val] = unpack_args!(args);
|
let [_, val] = unpack_args!(args);
|
||||||
let Value::Float(f) = val else {
|
let Value::Float(f) = val else {
|
||||||
throw!(*SYM_TYPE_ERROR, "float_to_bits expected float argument, got {val:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"float_to_bits expected float argument, got {val:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
Ok(Value::Int(f.to_bits() as i64))
|
Ok(Value::Int(f.to_bits() as i64))
|
||||||
}
|
}
|
||||||
|
@ -614,25 +672,28 @@ pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn float_of_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn float_of_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, val] = unpack_args!(args);
|
let [_, val] = unpack_args!(args);
|
||||||
let Value::Int(i) = val else {
|
let Value::Int(i) = val else {
|
||||||
throw!(*SYM_TYPE_ERROR, "float_of_bits expected integer argument, got {val:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"float_of_bits expected integer argument, got {val:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
Ok(Value::Float(f64::from_bits(i as u64)))
|
Ok(Value::Float(f64::from_bits(i as u64)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// rational operations
|
// rational operations
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, v] = unpack_args!(args);
|
let [_, v] = unpack_args!(args);
|
||||||
match v {
|
match v {
|
||||||
Value::Int(x) => Ok(Value::Int(x)),
|
Value::Int(x) => Ok(Value::Int(x)),
|
||||||
Value::Ratio(x) => Ok(Value::Int(*x.numer())),
|
Value::Ratio(x) => Ok(Value::Int(*x.numer())),
|
||||||
v => throw!(*SYM_TYPE_ERROR, "numer expected rational argument, got {v:#}"),
|
v => throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"numer expected rational argument, got {v:#}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,7 +703,10 @@ pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
match v {
|
match v {
|
||||||
Value::Int(_) => Ok(Value::Int(1)),
|
Value::Int(_) => Ok(Value::Int(1)),
|
||||||
Value::Ratio(x) => Ok(Value::Int(*x.denom())),
|
Value::Ratio(x) => Ok(Value::Int(*x.denom())),
|
||||||
v => throw!(*SYM_TYPE_ERROR, "denom expected rational argument, got {v:#}"),
|
v => throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"denom expected rational argument, got {v:#}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,8 +714,6 @@ pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
// complex operations
|
// complex operations
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn re(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn re(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, v] = unpack_args!(args);
|
let [_, v] = unpack_args!(args);
|
||||||
|
@ -688,7 +750,7 @@ pub fn arg(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x] = unpack_args!(args);
|
let [_, x] = unpack_args!(args);
|
||||||
x.abs()
|
x.abs()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -699,17 +761,17 @@ pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Value::Ratio(_) => x.clone() * x,
|
Value::Ratio(_) => x.clone() * x,
|
||||||
Value::Float(_) => x.clone() * x,
|
Value::Float(_) => x.clone() * x,
|
||||||
Value::Complex(x) => Ok(Value::Float(x.norm_sqr())),
|
Value::Complex(x) => Ok(Value::Float(x.norm_sqr())),
|
||||||
x => throw!(*SYM_TYPE_ERROR, "abs_sq expected numeric argument, got {x:#}"),
|
x => throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"abs_sq expected numeric argument, got {x:#}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// continuous operations
|
// continuous operations
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
macro_rules! float_func {
|
macro_rules! float_func {
|
||||||
($name:ident) => {
|
($name:ident) => {
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -718,8 +780,11 @@ macro_rules! float_func {
|
||||||
match to_floaty(v) {
|
match to_floaty(v) {
|
||||||
Value::Float(x) => Ok(Value::Float(x.$name())),
|
Value::Float(x) => Ok(Value::Float(x.$name())),
|
||||||
Value::Complex(z) => Ok(Value::Complex(z.$name())),
|
Value::Complex(z) => Ok(Value::Complex(z.$name())),
|
||||||
v => throw!(*SYM_TYPE_ERROR,
|
v => throw!(
|
||||||
"{} expected numeric argument, got {v:#}", stringify!($name)),
|
*SYM_TYPE_ERROR,
|
||||||
|
"{} expected numeric argument, got {v:#}",
|
||||||
|
stringify!($name)
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -750,10 +815,10 @@ float_func!(atanh);
|
||||||
pub fn atan2(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn atan2(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, y, x] = unpack_args!(args);
|
let [_, y, x] = unpack_args!(args);
|
||||||
match (to_floaty(y), to_floaty(x)) {
|
match (to_floaty(y), to_floaty(x)) {
|
||||||
(Value::Float(y), Value::Float(x))
|
(Value::Float(y), Value::Float(x)) => Ok(Value::Float(x.atan2(y))),
|
||||||
=> Ok(Value::Float(x.atan2(y))),
|
(y, x) => throw!(
|
||||||
(y,x) => throw!(*SYM_TYPE_ERROR,
|
*SYM_TYPE_ERROR,
|
||||||
"atan2 expected real arguments, got {y:#} and {x:#}"),
|
"atan2 expected real arguments, got {y:#} and {x:#}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
use rand::{seq::SliceRandom, Rng};
|
use rand::{seq::SliceRandom, Rng};
|
||||||
use talc_lang::{exception::Result, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{range::RangeType, Value}, vmcalliter, Vm};
|
use talc_lang::{
|
||||||
|
exception::Result,
|
||||||
|
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
|
throw,
|
||||||
|
value::{range::RangeType, Value},
|
||||||
|
vmcalliter, Vm,
|
||||||
|
};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
@ -21,11 +27,11 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
match col {
|
match col {
|
||||||
Value::List(l) => {
|
Value::List(l) => {
|
||||||
let l = l.borrow();
|
let l = l.borrow();
|
||||||
let Some(v) = l.choose(&mut rand::thread_rng()) else {
|
let Some(v) = l.choose(&mut rand::thread_rng()) else {
|
||||||
throw!(*SYM_VALUE_ERROR, "rand_in: empty list")
|
throw!(*SYM_VALUE_ERROR, "rand_in: empty list")
|
||||||
};
|
};
|
||||||
Ok(v.clone())
|
Ok(v.clone())
|
||||||
},
|
}
|
||||||
Value::Table(t) => {
|
Value::Table(t) => {
|
||||||
let t = t.borrow();
|
let t = t.borrow();
|
||||||
if t.is_empty() {
|
if t.is_empty() {
|
||||||
|
@ -34,16 +40,14 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let i = rand::thread_rng().gen_range(0..t.len());
|
let i = rand::thread_rng().gen_range(0..t.len());
|
||||||
let key = t.keys().nth(i).unwrap();
|
let key = t.keys().nth(i).unwrap();
|
||||||
Ok(key.clone().into_inner())
|
Ok(key.clone().into_inner())
|
||||||
},
|
}
|
||||||
Value::Range(r) => {
|
Value::Range(r) => {
|
||||||
if r.is_empty() {
|
if r.is_empty() {
|
||||||
throw!(*SYM_VALUE_ERROR, "rand_in: empty range")
|
throw!(*SYM_VALUE_ERROR, "rand_in: empty range")
|
||||||
}
|
}
|
||||||
match r.ty {
|
match r.ty {
|
||||||
RangeType::Open => Ok(Value::Int(
|
RangeType::Open => Ok(Value::Int(rand::thread_rng().gen_range(r.start..r.stop))),
|
||||||
rand::thread_rng().gen_range(r.start..r.stop))),
|
RangeType::Closed => Ok(Value::Int(rand::thread_rng().gen_range(r.start..=r.stop))),
|
||||||
RangeType::Closed => Ok(Value::Int(
|
|
||||||
rand::thread_rng().gen_range(r.start..=r.stop))),
|
|
||||||
RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"),
|
RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use talc_lang::{exception::{exception, Result}, lstring::LString, symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{NativeValue, Value}, Vm};
|
|
||||||
use talc_macros::native_func;
|
|
||||||
use regex::{Captures, Match, Regex};
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use regex::{Captures, Match, Regex};
|
||||||
|
use talc_lang::{
|
||||||
|
exception::{exception, Result},
|
||||||
|
lstring::LString,
|
||||||
|
symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
|
throw,
|
||||||
|
value::{NativeValue, Value},
|
||||||
|
Vm,
|
||||||
|
};
|
||||||
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
|
||||||
|
@ -18,17 +25,30 @@ lazy_static! {
|
||||||
pub struct ValueRegex(Regex);
|
pub struct ValueRegex(Regex);
|
||||||
|
|
||||||
impl From<Regex> for ValueRegex {
|
impl From<Regex> for ValueRegex {
|
||||||
fn from(value: Regex) -> Self { Self(value) }
|
fn from(value: Regex) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ValueRegex> for Regex {
|
impl From<ValueRegex> for Regex {
|
||||||
fn from(value: ValueRegex) -> Self { value.0 }
|
fn from(value: ValueRegex) -> Self {
|
||||||
|
value.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NativeValue for ValueRegex {
|
impl NativeValue for ValueRegex {
|
||||||
fn get_type(&self) -> Symbol { *SYM_STD_REGEX }
|
fn get_type(&self) -> Symbol {
|
||||||
fn as_any(&self) -> &dyn std::any::Any { self }
|
*SYM_STD_REGEX
|
||||||
fn to_lstring(&self, w: &mut LString, repr: bool, _recur: &mut Vec<*const ()>) -> std::io::Result<()> {
|
}
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn to_lstring(
|
||||||
|
&self,
|
||||||
|
w: &mut LString,
|
||||||
|
repr: bool,
|
||||||
|
_recur: &mut Vec<*const ()>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
if repr {
|
if repr {
|
||||||
write!(w, "/{}/", self.0)
|
write!(w, "/{}/", self.0)
|
||||||
|
@ -58,7 +78,10 @@ fn match_to_value(m: Match) -> Value {
|
||||||
Value::new_table(|t| {
|
Value::new_table(|t| {
|
||||||
t.insert((*SYM_START).into(), (m.start() as i64).into());
|
t.insert((*SYM_START).into(), (m.start() as i64).into());
|
||||||
t.insert((*SYM_END).into(), (m.end() as i64).into());
|
t.insert((*SYM_END).into(), (m.end() as i64).into());
|
||||||
t.insert((*SYM_STR).into(), LString::from(m.as_str().to_string()).into());
|
t.insert(
|
||||||
|
(*SYM_STR).into(),
|
||||||
|
LString::from(m.as_str().to_string()).into(),
|
||||||
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,22 +101,28 @@ fn regex_from<'a>(v: &'a Value, name: &str) -> Result<Cow<'a, Regex>> {
|
||||||
Regex::new(s)
|
Regex::new(s)
|
||||||
.map(Cow::Owned)
|
.map(Cow::Owned)
|
||||||
.map_err(|e| exception!(*SYM_VALUE_ERROR, "invalid regex: {e}"))
|
.map_err(|e| exception!(*SYM_VALUE_ERROR, "invalid regex: {e}"))
|
||||||
},
|
}
|
||||||
Value::Native(n) if n.get_type() == *SYM_STD_REGEX => {
|
Value::Native(n) if n.get_type() == *SYM_STD_REGEX => n
|
||||||
n.as_any().downcast_ref::<ValueRegex>()
|
.as_any()
|
||||||
.map(|vr| Cow::Borrowed(&vr.0))
|
.downcast_ref::<ValueRegex>()
|
||||||
.ok_or_else(|| exception!(
|
.map(|vr| Cow::Borrowed(&vr.0))
|
||||||
*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}"))
|
.ok_or_else(|| {
|
||||||
},
|
exception!(
|
||||||
_ => throw!(*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}")
|
*SYM_TYPE_ERROR,
|
||||||
|
"{name} expected string or regex, got {v:#}"
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
_ => throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"{name} expected string or regex, got {v:#}"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn _regex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn _regex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, re] = unpack_args!(args);
|
let [_, re] = unpack_args!(args);
|
||||||
regex_from(&re, "regex")
|
regex_from(&re, "regex").map(|re| ValueRegex(re.into_owned()).into())
|
||||||
.map(|re| ValueRegex(re.into_owned()).into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -132,7 +161,11 @@ pub fn _match(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
|
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
|
||||||
};
|
};
|
||||||
let re = regex_from(&re, "match")?;
|
let re = regex_from(&re, "match")?;
|
||||||
Ok(re.find_iter(s).map(match_to_value).collect::<Vec<Value>>().into())
|
Ok(re
|
||||||
|
.find_iter(s)
|
||||||
|
.map(match_to_value)
|
||||||
|
.collect::<Vec<Value>>()
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -158,7 +191,11 @@ pub fn captures(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
|
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
|
||||||
};
|
};
|
||||||
let re = regex_from(&re, "captures")?;
|
let re = regex_from(&re, "captures")?;
|
||||||
Ok(re.captures_iter(s).map(captures_to_value).collect::<Vec<Value>>().into())
|
Ok(re
|
||||||
|
.captures_iter(s)
|
||||||
|
.map(captures_to_value)
|
||||||
|
.collect::<Vec<Value>>()
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(3)]
|
#[native_func(3)]
|
||||||
|
@ -168,7 +205,10 @@ pub fn replace_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "replace_once expected string, got {s:#}")
|
throw!(*SYM_TYPE_ERROR, "replace_once expected string, got {s:#}")
|
||||||
};
|
};
|
||||||
let Value::String(rep) = rep else {
|
let Value::String(rep) = rep else {
|
||||||
throw!(*SYM_TYPE_ERROR, "replace_once expected string or function, got {rep:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"replace_once expected string or function, got {rep:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let Ok(s) = s.to_str() else {
|
let Ok(s) = s.to_str() else {
|
||||||
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
|
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
|
||||||
|
@ -187,7 +227,10 @@ pub fn replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_TYPE_ERROR, "replace expected string, got {s:#}")
|
throw!(*SYM_TYPE_ERROR, "replace expected string, got {s:#}")
|
||||||
};
|
};
|
||||||
let Value::String(rep) = rep else {
|
let Value::String(rep) = rep else {
|
||||||
throw!(*SYM_TYPE_ERROR, "replace expected string or function, got {rep:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"replace expected string or function, got {rep:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let Ok(s) = s.to_str() else {
|
let Ok(s) = s.to_str() else {
|
||||||
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
|
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
|
||||||
|
@ -212,7 +255,7 @@ pub fn split_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let mut parts = re.splitn(s, 2);
|
let mut parts = re.splitn(s, 2);
|
||||||
let (part1, part2) = (
|
let (part1, part2) = (
|
||||||
LString::from(parts.next().unwrap_or_default()).into(),
|
LString::from(parts.next().unwrap_or_default()).into(),
|
||||||
LString::from(parts.next().unwrap_or_default()).into()
|
LString::from(parts.next().unwrap_or_default()).into(),
|
||||||
);
|
);
|
||||||
Ok(vec![part1, part2].into())
|
Ok(vec![part1, part2].into())
|
||||||
}
|
}
|
||||||
|
@ -227,9 +270,6 @@ pub fn split(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
throw!(*SYM_VALUE_ERROR, "string to split must be valid UTF-8")
|
throw!(*SYM_VALUE_ERROR, "string to split must be valid UTF-8")
|
||||||
};
|
};
|
||||||
let re = regex_from(&re, "split")?;
|
let re = regex_from(&re, "split")?;
|
||||||
let parts: Vec<Value> = re.split(s)
|
let parts: Vec<Value> = re.split(s).map(|s| LString::from(s).into()).collect();
|
||||||
.map(|s| LString::from(s).into())
|
|
||||||
.collect();
|
|
||||||
Ok(parts.into())
|
Ok(parts.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
use talc_lang::{exception::Result, lstring::LString, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::Value, Vm};
|
use talc_lang::{
|
||||||
|
exception::Result,
|
||||||
|
lstring::LString,
|
||||||
|
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
|
throw,
|
||||||
|
value::Value,
|
||||||
|
Vm,
|
||||||
|
};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
@ -54,7 +61,10 @@ pub fn chr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn len_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn len_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, s] = unpack_args!(args);
|
let [_, s] = unpack_args!(args);
|
||||||
let Value::String(s) = s else {
|
let Value::String(s) = s else {
|
||||||
throw!(*SYM_TYPE_ERROR, "len_bytes expected string argument, got {s:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"len_bytes expected string argument, got {s:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
Ok(Value::Int(s.len() as i64))
|
Ok(Value::Int(s.len() as i64))
|
||||||
}
|
}
|
||||||
|
@ -92,8 +102,10 @@ pub fn starts_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let res = match (d, pre) {
|
let res = match (d, pre) {
|
||||||
(Value::List(d), Value::List(pre)) => d.borrow().starts_with(&pre.borrow()),
|
(Value::List(d), Value::List(pre)) => d.borrow().starts_with(&pre.borrow()),
|
||||||
(Value::String(d), Value::String(pre)) => d.starts_with(&pre),
|
(Value::String(d), Value::String(pre)) => d.starts_with(&pre),
|
||||||
(d, pre) => throw!(*SYM_TYPE_ERROR,
|
(d, pre) => throw!(
|
||||||
"starts_with expected two lists or strings, got {d:#} and {pre:#}")
|
*SYM_TYPE_ERROR,
|
||||||
|
"starts_with expected two lists or strings, got {d:#} and {pre:#}"
|
||||||
|
),
|
||||||
};
|
};
|
||||||
Ok(res.into())
|
Ok(res.into())
|
||||||
}
|
}
|
||||||
|
@ -104,8 +116,10 @@ pub fn ends_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let res = match (d, suf) {
|
let res = match (d, suf) {
|
||||||
(Value::List(d), Value::List(suf)) => d.borrow().ends_with(&suf.borrow()),
|
(Value::List(d), Value::List(suf)) => d.borrow().ends_with(&suf.borrow()),
|
||||||
(Value::String(d), Value::String(suf)) => d.ends_with(&suf),
|
(Value::String(d), Value::String(suf)) => d.ends_with(&suf),
|
||||||
(d, suf) => throw!(*SYM_TYPE_ERROR,
|
(d, suf) => throw!(
|
||||||
"ends_with expected two lists or strings, got {d:#} and {suf:#}")
|
*SYM_TYPE_ERROR,
|
||||||
|
"ends_with expected two lists or strings, got {d:#} and {suf:#}"
|
||||||
|
),
|
||||||
};
|
};
|
||||||
Ok(res.into())
|
Ok(res.into())
|
||||||
}
|
}
|
||||||
|
@ -114,7 +128,10 @@ pub fn ends_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn is_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn is_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, s] = unpack_args!(args);
|
let [_, s] = unpack_args!(args);
|
||||||
let Value::String(s) = s else {
|
let Value::String(s) = s else {
|
||||||
throw!(*SYM_TYPE_ERROR, "is_utf8 expected string argument, got {s:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"is_utf8 expected string argument, got {s:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
Ok(s.is_utf8().into())
|
Ok(s.is_utf8().into())
|
||||||
}
|
}
|
||||||
|
@ -123,7 +140,10 @@ pub fn is_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn to_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn to_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, s] = unpack_args!(args);
|
let [_, s] = unpack_args!(args);
|
||||||
let Value::String(s) = s else {
|
let Value::String(s) = s else {
|
||||||
throw!(*SYM_TYPE_ERROR, "is_utf8 expected string argument, got {s:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"is_utf8 expected string argument, got {s:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
if s.is_utf8() {
|
if s.is_utf8() {
|
||||||
Ok(s.into())
|
Ok(s.into())
|
||||||
|
@ -136,7 +156,10 @@ pub fn to_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn to_utf8_lossy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn to_utf8_lossy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, s] = unpack_args!(args);
|
let [_, s] = unpack_args!(args);
|
||||||
let Value::String(s) = s else {
|
let Value::String(s) = s else {
|
||||||
throw!(*SYM_TYPE_ERROR, "is_utf8 expected string argument, got {s:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"is_utf8 expected string argument, got {s:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
Ok(s.to_utf8_lossy().into())
|
Ok(s.to_utf8_lossy().into())
|
||||||
}
|
}
|
||||||
|
@ -145,25 +168,37 @@ pub fn to_utf8_lossy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
pub fn str_to_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn str_to_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, s] = unpack_args!(args);
|
let [_, s] = unpack_args!(args);
|
||||||
let Value::String(s) = s else {
|
let Value::String(s) = s else {
|
||||||
throw!(*SYM_TYPE_ERROR, "str_to_bytes expected string argument, got {s:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"str_to_bytes expected string argument, got {s:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
Ok(s.as_bytes()
|
Ok(s.as_bytes()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| (*v as i64).into())
|
.map(|v| (*v as i64).into())
|
||||||
.collect::<Vec<Value>>()
|
.collect::<Vec<Value>>()
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, b] = unpack_args!(args);
|
let [_, b] = unpack_args!(args);
|
||||||
let Value::List(b) = b else {
|
let Value::List(b) = b else {
|
||||||
throw!(*SYM_TYPE_ERROR, "str_of_bytes expected list argument, got {b:#}")
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"str_of_bytes expected list argument, got {b:#}"
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let bytes: Vec<u8> = b.borrow().iter()
|
let bytes: Vec<u8> = b
|
||||||
|
.borrow()
|
||||||
|
.iter()
|
||||||
.map(|v| match v {
|
.map(|v| match v {
|
||||||
Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8),
|
Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8),
|
||||||
_ => throw!(*SYM_VALUE_ERROR, "str_of_bytes expected list of integers in 0..=255"),
|
_ => throw!(
|
||||||
}).collect::<Result<Vec<u8>>>()?;
|
*SYM_VALUE_ERROR,
|
||||||
|
"str_of_bytes expected list of integers in 0..=255"
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<u8>>>()?;
|
||||||
Ok(LString::from(bytes).into())
|
Ok(LString::from(bytes).into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use talc_lang::{exception::{exception, Result}, lformat, parser::{parse_float, parse_int}, symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{ops::RatioExt, HashValue, Rational64, Value}, Vm};
|
use talc_lang::{
|
||||||
|
exception::{exception, Result},
|
||||||
|
lformat,
|
||||||
|
parser::{parse_float, parse_int},
|
||||||
|
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
|
||||||
|
throw,
|
||||||
|
value::{ops::RatioExt, HashValue, Rational64, Value},
|
||||||
|
Vm,
|
||||||
|
};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
@ -34,7 +42,6 @@ pub fn load(vm: &mut Vm) {
|
||||||
// types
|
// types
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1, "type")]
|
#[native_func(1, "type")]
|
||||||
pub fn type_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn type_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, val] = unpack_args!(args);
|
let [_, val] = unpack_args!(args);
|
||||||
|
@ -74,59 +81,75 @@ pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
(Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64().into())),
|
(Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64().into())),
|
||||||
(Value::Float(x), b"int") => Ok(Value::Int(x as i64)),
|
(Value::Float(x), b"int") => Ok(Value::Int(x as i64)),
|
||||||
(Value::Float(x), b"ratio") => {
|
(Value::Float(x), b"ratio") => {
|
||||||
let r = Rational64::approximate_float(x)
|
let r = Rational64::approximate_float(x).ok_or_else(|| {
|
||||||
.ok_or_else(|| exception!(*SYM_VALUE_ERROR, "float {x:?} could not be converted to ratio"))?;
|
exception!(
|
||||||
|
*SYM_VALUE_ERROR,
|
||||||
|
"float {x:?} could not be converted to ratio"
|
||||||
|
)
|
||||||
|
})?;
|
||||||
Ok(Value::Ratio(r))
|
Ok(Value::Ratio(r))
|
||||||
}
|
}
|
||||||
(Value::Float(x), b"complex") => Ok(Value::Complex(x.into())),
|
(Value::Float(x), b"complex") => Ok(Value::Complex(x.into())),
|
||||||
|
|
||||||
(Value::String(s), b"int")
|
(Value::String(s), b"int") => parse_int(s.as_ref(), 10)
|
||||||
=> parse_int(s.as_ref(), 10)
|
|
||||||
.map(i64::into)
|
.map(i64::into)
|
||||||
.map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as integer")),
|
.map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as integer")),
|
||||||
|
|
||||||
(Value::String(s), b"float")
|
(Value::String(s), b"float") => parse_float(s.as_ref())
|
||||||
=> parse_float(s.as_ref())
|
|
||||||
.map(f64::into)
|
.map(f64::into)
|
||||||
.map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as float")),
|
.map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as float")),
|
||||||
|
|
||||||
(v, _) => throw!(*SYM_TYPE_ERROR,
|
(v, _) => throw!(
|
||||||
"cannot convert value of type {} to type {}", v.get_type().name(), ty.name())
|
*SYM_TYPE_ERROR,
|
||||||
|
"cannot convert value of type {} to type {}",
|
||||||
|
v.get_type().name(),
|
||||||
|
ty.name()
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_inner(value: Value) -> Result<Value> {
|
pub fn copy_inner(value: Value) -> Result<Value> {
|
||||||
match value {
|
match value {
|
||||||
Value::Nil | Value::Bool(_) | Value::Symbol(_)
|
Value::Nil
|
||||||
| Value::Int(_) | Value::Ratio(_) | Value::Float(_)
|
| Value::Bool(_)
|
||||||
| Value::Complex(_) | Value::Range(_) | Value::String(_)
|
| Value::Symbol(_)
|
||||||
=> Ok(value),
|
| Value::Int(_)
|
||||||
|
| Value::Ratio(_)
|
||||||
|
| Value::Float(_)
|
||||||
|
| Value::Complex(_)
|
||||||
|
| Value::Range(_)
|
||||||
|
| Value::String(_) => Ok(value),
|
||||||
Value::Cell(c) => {
|
Value::Cell(c) => {
|
||||||
let c = Rc::unwrap_or_clone(c).take();
|
let c = Rc::unwrap_or_clone(c).take();
|
||||||
let c = copy_inner(c)?;
|
let c = copy_inner(c)?;
|
||||||
Ok(RefCell::new(c).into())
|
Ok(RefCell::new(c).into())
|
||||||
},
|
}
|
||||||
Value::List(l) => {
|
Value::List(l) => {
|
||||||
let l = Rc::unwrap_or_clone(l).take();
|
let l = Rc::unwrap_or_clone(l).take();
|
||||||
let v: Result<Vec<Value>> = l.into_iter()
|
let v: Result<Vec<Value>> = l.into_iter().map(copy_inner).collect();
|
||||||
.map(copy_inner)
|
|
||||||
.collect();
|
|
||||||
Ok(v?.into())
|
Ok(v?.into())
|
||||||
},
|
}
|
||||||
Value::Table(t) => {
|
Value::Table(t) => {
|
||||||
let t = Rc::unwrap_or_clone(t).take();
|
let t = Rc::unwrap_or_clone(t).take();
|
||||||
let v: Result<HashMap<HashValue, Value>> = t.into_iter()
|
let v: Result<HashMap<HashValue, Value>> = t
|
||||||
|
.into_iter()
|
||||||
.map(|(k, v)| copy_inner(v).map(|v| (k, v)))
|
.map(|(k, v)| copy_inner(v).map(|v| (k, v)))
|
||||||
.collect();
|
.collect();
|
||||||
Ok(v?.into())
|
Ok(v?.into())
|
||||||
},
|
}
|
||||||
Value::Native(ref n) => match n.copy_value()? {
|
Value::Native(ref n) => match n.copy_value()? {
|
||||||
Some(x) => Ok(x),
|
Some(x) => Ok(x),
|
||||||
None => throw!(*SYM_TYPE_ERROR,
|
None => throw!(
|
||||||
"cannot copy value of type {}", value.get_type().name())
|
*SYM_TYPE_ERROR,
|
||||||
}
|
"cannot copy value of type {}",
|
||||||
_ => throw!(*SYM_TYPE_ERROR,
|
value.get_type().name()
|
||||||
"cannot copy value of type {}", value.get_type().name())
|
),
|
||||||
|
},
|
||||||
|
_ => throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"cannot copy value of type {}",
|
||||||
|
value.get_type().name()
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +163,6 @@ pub fn copy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
// strings
|
// strings
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1, "str")]
|
#[native_func(1, "str")]
|
||||||
pub fn str_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn str_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, val] = unpack_args!(args);
|
let [_, val] = unpack_args!(args);
|
||||||
|
@ -160,38 +182,37 @@ pub fn repr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn symbol_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn symbol_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, val] = unpack_args!(args);
|
let [_, val] = unpack_args!(args);
|
||||||
let Value::Symbol(s) = val else {
|
let Value::Symbol(s) = val else {
|
||||||
throw!(*SYM_TYPE_ERROR, "symbol_name: expected symbol")
|
throw!(*SYM_TYPE_ERROR, "symbol_name: expected symbol")
|
||||||
};
|
};
|
||||||
Ok(s.name().into())
|
Ok(s.name().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn symbol_of(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn symbol_of(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, val] = unpack_args!(args);
|
let [_, val] = unpack_args!(args);
|
||||||
let Value::String(s) = val else {
|
let Value::String(s) = val else {
|
||||||
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
|
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
|
||||||
};
|
};
|
||||||
Ok(Symbol::get(s.as_ref()).into())
|
Ok(Symbol::get(s.as_ref()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn symbol_exists(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn symbol_exists(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, val] = unpack_args!(args);
|
let [_, val] = unpack_args!(args);
|
||||||
let Value::String(s) = val else {
|
let Value::String(s) = val else {
|
||||||
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
|
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
|
||||||
};
|
};
|
||||||
match Symbol::try_get(s.as_ref()) {
|
match Symbol::try_get(s.as_ref()) {
|
||||||
Some(s) => Ok(s.into()),
|
Some(s) => Ok(s.into()),
|
||||||
None => Ok(Value::Nil),
|
None => Ok(Value::Nil),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// cells
|
// cells
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn cell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn cell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, value] = unpack_args!(args);
|
let [_, value] = unpack_args!(args);
|
||||||
|
@ -232,66 +253,78 @@ pub fn cell_take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn func_state(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn func_state(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, func] = unpack_args!(args);
|
let [_, func] = unpack_args!(args);
|
||||||
match func {
|
match func {
|
||||||
Value::NativeFunc(_) => Ok(Value::Nil),
|
Value::NativeFunc(_) => Ok(Value::Nil),
|
||||||
Value::Function(f) => {
|
Value::Function(f) => {
|
||||||
let l: Vec<Value> = f.state.iter()
|
let l: Vec<Value> = f.state.iter().map(|v| Value::Cell(v.clone())).collect();
|
||||||
.map(|v| Value::Cell(v.clone()))
|
Ok(l.into())
|
||||||
.collect();
|
}
|
||||||
Ok(l.into())
|
_ => throw!(
|
||||||
}
|
*SYM_TYPE_ERROR,
|
||||||
_ => throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a talc function")
|
"closure_state: {func:#} is not a talc function"
|
||||||
}
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn func_arity(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn func_arity(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, func] = unpack_args!(args);
|
let [_, func] = unpack_args!(args);
|
||||||
let Some(attrs) = func.func_attrs() else {
|
let Some(attrs) = func.func_attrs() else {
|
||||||
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
|
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
|
||||||
};
|
};
|
||||||
Ok((attrs.arity as i64).into())
|
Ok((attrs.arity as i64).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn func_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn func_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, func] = unpack_args!(args);
|
let [_, func] = unpack_args!(args);
|
||||||
let Some(attrs) = func.func_attrs() else {
|
let Some(attrs) = func.func_attrs() else {
|
||||||
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
|
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
|
||||||
};
|
};
|
||||||
if let Some(name) = attrs.name {
|
if let Some(name) = attrs.name {
|
||||||
Ok(name.into())
|
Ok(name.into())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nil)
|
Ok(Value::Nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
pub fn apply(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn apply(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, func, lst] = unpack_args!(args);
|
let [_, func, lst] = unpack_args!(args);
|
||||||
if func.func_attrs().is_none() {
|
if func.func_attrs().is_none() {
|
||||||
throw!(*SYM_TYPE_ERROR, "apply: first argument must be a function, found {func:#}")
|
throw!(
|
||||||
}
|
*SYM_TYPE_ERROR,
|
||||||
let Value::List(l) = lst else {
|
"apply: first argument must be a function, found {func:#}"
|
||||||
throw!(*SYM_TYPE_ERROR, "apply: second argument must be a list, found {lst:#}")
|
)
|
||||||
};
|
}
|
||||||
let mut args = l.borrow().clone();
|
let Value::List(l) = lst else {
|
||||||
args.insert(0, func.clone());
|
throw!(
|
||||||
vm.call_value(func, args)
|
*SYM_TYPE_ERROR,
|
||||||
|
"apply: second argument must be a list, found {lst:#}"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let mut args = l.borrow().clone();
|
||||||
|
args.insert(0, func.clone());
|
||||||
|
vm.call_value(func, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn compile(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn compile(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, src] = unpack_args!(args);
|
let [_, src] = unpack_args!(args);
|
||||||
let Value::String(src) = src else {
|
let Value::String(src) = src else {
|
||||||
throw!(*SYM_TYPE_ERROR, "compile: argument must be a string, found {src:#}")
|
throw!(
|
||||||
};
|
*SYM_TYPE_ERROR,
|
||||||
let src = src.to_str()
|
"compile: argument must be a string, found {src:#}"
|
||||||
.map_err(|e| exception!(*SYM_VALUE_ERROR, "compile: argument must be valid unicode ({e})"))?;
|
)
|
||||||
let ast = talc_lang::parser::parse(src)
|
};
|
||||||
.map_err(|e| exception!(symbol!("parse_error"), "{e}"))?;
|
let src = src.to_str().map_err(|e| {
|
||||||
let func = talc_lang::compiler::compile(&ast, None);
|
exception!(
|
||||||
Ok(func.into())
|
*SYM_VALUE_ERROR,
|
||||||
|
"compile: argument must be valid unicode ({e})"
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let ast =
|
||||||
|
talc_lang::parser::parse(src).map_err(|e| exception!(symbol!("parse_error"), "{e}"))?;
|
||||||
|
let func = talc_lang::compiler::compile(&ast, None);
|
||||||
|
Ok(func.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue