Compare commits

..

No commits in common. "151c6abf45ba4c84571c89103a2220376730a8f3" and "4e61ca90f02d2417067b9055553c69bd18a80662" have entirely different histories.

48 changed files with 2671 additions and 5658 deletions

733
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,23 +0,0 @@
#!/usr/bin/env talc
-- adapted from Crafting Interpreters 10.6
make_counter = \-> do
var i = 0
\-> do
i += 1
println(i)
end
end
var counter1 = make_counter()
counter1() -- 1
counter1() -- 2
counter1() -- 3
counter1() -- 4
var counter2 = make_counter()
counter2() -- 1
counter2() -- 2
counter1() -- 5
counter1() -- 6
counter2() -- 3

View file

@ -1,23 +0,0 @@
#!/usr/bin/env talc
var i = 0;
var outer = \n -> do
var inner = \-> do
i = i + n
end
end
var by3 = outer(3)
var by5 = outer(5)
by3()
println(i) -- 3
by3()
println(i) -- 6
by5()
println(i) -- 11
by5()
println(i) -- 16
by3()
println(i) -- 19

View file

@ -1,9 +0,0 @@
#!/usr/bin/env talc
totient = \n -> do
count(factors(n)) | pairs | map(\v -> do
v[0]^v[1] - v[0]^(v[1] - 1)
end) | prod
end
println(totient(6615)) -- 3024

View file

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

View file

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

View file

@ -11,5 +11,5 @@ path = "src/main.rs"
talc-lang = { path = "../talc-lang" }
talc-std = { path = "../talc-std" }
rustyline = "14.0"
clap = { version = "4.5", features = ["std", "help", "usage", "derive", "error-context"], default-features = false }
clap = { version = "4.5", features = ["derive"] }
ctrlc = "3.4"

View file

@ -1,25 +1,42 @@
use std::{borrow::Cow, cell::RefCell, rc::Rc};
use std::{borrow::Cow, cell::RefCell, collections::HashMap, rc::Rc};
use rustyline::{
completion::Completer,
highlight::Highlighter,
hint::Hinter,
validate::{ValidationContext, ValidationResult, Validator},
Helper, Result,
};
use talc_lang::{
lstring::LStr,
parser::{Lexer, Pos, Span, TokenKind},
Vm,
};
use rustyline::{completion::Completer, highlight::Highlighter, hint::Hinter, validate::{ValidationContext, ValidationResult, Validator}, Helper, Result};
use talc_lang::{lstring::LStr, Lexer, Vm};
#[derive(Clone, Copy)]
enum TokenType {
String, Symbol, Number, Literal
}
pub struct TalcHelper {
vm: Rc<RefCell<Vm>>,
lex: Lexer,
token_types: HashMap<usize, TokenType>,
}
macro_rules! load_tokens {
($token_types:expr, $lex:expr, {$($($tok:literal)|+ => $ty:expr,)*}) => {{
$($(
$token_types.insert($lex.lex($tok).next().unwrap().unwrap().1.0, $ty);
)*)*
}};
}
impl TalcHelper {
pub fn new(vm: Rc<RefCell<Vm>>) -> Self {
Self { vm }
let lex = Lexer::new();
let mut token_types = HashMap::new();
load_tokens!(token_types, lex, {
"\"\"" | "''" => TokenType::String,
":a" | ":''" | ":\"\"" => TokenType::Symbol,
"0" | "0.0" | "0x0" | "0b0" | "0o0" | "0s0" => TokenType::Number,
"true" | "false" | "nil" => TokenType::Literal,
});
Self {
vm,
lex,
token_types,
}
}
}
@ -43,11 +60,7 @@ impl Completer for TalcHelper {
}
}
let res: String = res.chars().rev().collect();
let mut keys = self
.vm
.borrow()
.globals()
.keys()
let mut keys = self.vm.borrow().globals().keys()
.map(|sym| sym.name())
.filter(|name| name.starts_with(LStr::from_str(&res)))
.map(LStr::to_string)
@ -63,33 +76,24 @@ impl Hinter for TalcHelper {
impl Highlighter for TalcHelper {
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
let mut lexer = Lexer::new(line);
let mut tokens = self.lex.lex(line).peekable();
let mut buf = String::new();
let mut last = Pos::new();
while let Some(Ok(token)) = lexer.next() {
if token.kind == TokenKind::Eof {
break
}
buf += Span::new(last, token.span.start).of(line);
last = token.span.end;
let format = match token.kind {
TokenKind::Nil
| TokenKind::True
| TokenKind::False
| TokenKind::Integer
| TokenKind::Float
| TokenKind::Imaginary => "\x1b[93m",
TokenKind::String => "\x1b[92m",
TokenKind::Symbol => "\x1b[96m",
_ => "",
let mut last = 0;
while let Some(Ok((l, tok, r))) = tokens.next() {
buf += &line[last..l];
last = r;
let tokty = self.token_types.get(&tok.0);
buf += match tokty {
Some(TokenType::Literal) => "\x1b[93m",
Some(TokenType::Number) => "\x1b[93m",
Some(TokenType::String) => "\x1b[92m",
Some(TokenType::Symbol) => "\x1b[96m",
None => "",
};
buf += format;
buf += token.content;
if !format.is_empty() {
buf += "\x1b[0m"
buf += tok.1;
if tokty.is_some() { buf += "\x1b[0m" }
}
}
buf += &line[(last.idx as usize)..];
buf += &line[last..];
Cow::Owned(buf)
}
@ -112,92 +116,62 @@ impl Highlighter for TalcHelper {
impl Validator for TalcHelper {
fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
use TokenKind as K;
let lexer = Lexer::new(ctx.input());
let tokens = self.lex.lex(ctx.input());
let mut delims = Vec::new();
let mut mismatch = None;
for token in lexer {
for token in tokens {
let token = match token {
Ok(t) => t,
Err(e) => return Ok(ValidationResult::Invalid(Some(format!(" {e}")))),
Err(e) => return Ok(ValidationResult::Invalid(
Some(e.to_string()))),
};
let k = token.kind;
let s = token.span;
match k {
K::Eof => break,
K::LParen | K::LBrack | K::LBrace | K::If | K::While | K::For | K::Try => {
delims.push(token.kind)
}
K::RParen => match delims.pop() {
Some(K::LParen) => (),
v => {
mismatch = Some((v, k, s));
break
}
let t = token.1.1;
match t {
"(" | "{" | "[" | "if" | "while" | "for" | "try"
=> delims.push(token.1.1),
")" => match delims.pop() {
Some("(") => (),
v => { mismatch = Some((v, t)); break }
},
K::RBrack => match delims.pop() {
Some(K::LBrack) => (),
v => {
mismatch = Some((v, k, s));
break
}
"}" => match delims.pop() {
Some("{") => (),
v => { mismatch = Some((v, t)); break }
},
K::RBrace => match delims.pop() {
Some(K::LBrace) => (),
v => {
mismatch = Some((v, k, s));
break
}
"]" => match delims.pop() {
Some("[") => (),
v => { mismatch = Some((v, t)); break }
},
K::Then => match delims.pop() {
Some(K::If | K::Elif) => delims.push(k),
v => {
mismatch = Some((v, k, s));
break
"then" => match delims.pop() {
Some("if") => delims.push(t),
v => { mismatch = Some((v, t)); break }
}
},
K::Catch => match delims.pop() {
Some(K::Try) => delims.push(k),
v => {
mismatch = Some((v, k, s));
break
"catch" => match delims.pop() {
Some("try") => delims.push(t),
v => { mismatch = Some((v, t)); break }
}
},
K::Do => match delims.last().copied() {
Some(K::While | K::For | K::Catch) => {
"do" => match delims.last().copied() {
Some("while" | "for" | "catch") => {
delims.pop();
delims.push(k);
}
_ => delims.push(k),
delims.push(t);
},
K::Elif | K::Else => match delims.pop() {
Some(K::Then) => delims.push(k),
v => {
mismatch = Some((v, k, s));
break
}
_ => delims.push(t)
},
K::End => match delims.pop() {
Some(K::Then | K::Else | K::Do | K::Try | K::Catch) => (),
v => {
mismatch = Some((v, k, s));
break
}
"elif" | "else" => match delims.pop() {
Some("then") => delims.push(t),
v => { mismatch = Some((v, t)); break }
},
"end" => match delims.pop() {
Some("then" | "elif" | "else" | "do" | "try") => (),
v => { mismatch = Some((v, t)); break }
},
_ => (),
}
}
match mismatch {
Some((None, b, s)) => {
return Ok(ValidationResult::Invalid(Some(format!(
" found unmatched {b} at {s}"
))))
}
Some((Some(a), b, s)) => {
return Ok(ValidationResult::Invalid(Some(format!(
" found {a} matched with {b} at {s}"
))))
}
Some((None, b)) => return Ok(ValidationResult::Invalid(Some(
format!(" found unmatched {b}")))),
Some((Some(a), b)) => return Ok(ValidationResult::Invalid(Some(
format!(" found {a} matched with {b}")))),
_ => (),
}
@ -206,5 +180,6 @@ impl Validator for TalcHelper {
} else {
Ok(ValidationResult::Incomplete)
}
}
}

View file

@ -1,11 +1,9 @@
use clap::{ColorChoice, Parser};
use talc_lang::{compiler::compile, value::function::disasm_recursive, Vm};
use std::{path::PathBuf, process::ExitCode, rc::Rc};
use talc_lang::{
compiler::compile, parser, symbol::Symbol, value::function::disasm_recursive, Vm,
};
mod helper;
mod repl;
mod helper;
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
@ -22,27 +20,28 @@ struct Args {
disasm: bool,
/// show disassembled bytecode
#[arg(short = 'H', long)]
#[arg(short='H', long)]
histfile: Option<PathBuf>,
/// enable or disable color
#[arg(short, long, default_value = "auto")]
#[arg(short, long, default_value="auto")]
color: ColorChoice,
}
fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
fn exec(src: &str, args: &Args) -> ExitCode {
let parser = talc_lang::Parser::new();
let mut vm = Vm::new(256);
talc_std::load_all(&mut vm);
let ex = match parser::parse(src) {
let ex = match parser.parse(src) {
Ok(ex) => ex,
Err(e) => {
eprintln!("Error: {e}");
return ExitCode::FAILURE
}
},
};
let func = Rc::new(compile(&ex, Some(name)));
let func = Rc::new(compile(&ex));
if args.disasm {
if let Err(e) = disasm_recursive(&func, &mut std::io::stderr()) {
@ -66,10 +65,8 @@ fn main() -> ExitCode {
return repl::repl(&args)
}
let file = args.file.as_ref().unwrap();
match std::fs::read_to_string(file) {
Ok(s) => exec(Symbol::get(file.as_os_str()), &s, &args),
match std::fs::read_to_string(args.file.as_ref().unwrap()) {
Ok(s) => exec(&s, &args),
Err(e) => {
eprintln!("Error: {e}");
ExitCode::FAILURE

View file

@ -1,18 +1,8 @@
use std::{cell::RefCell, io::IsTerminal, process::ExitCode, rc::Rc};
use clap::ColorChoice;
use rustyline::{
error::ReadlineError,
history::{FileHistory, History},
ColorMode, Config, Editor,
};
use talc_lang::{
compiler::compile_repl,
parser,
symbol::Symbol,
value::{function::disasm_recursive, Value},
Vm,
};
use rustyline::{error::ReadlineError, history::{FileHistory, History}, ColorMode, Config, Editor};
use talc_lang::{compiler::compile_repl, symbol::Symbol, value::{function::disasm_recursive, Value}, Vm};
use crate::{helper::TalcHelper, Args};
@ -40,6 +30,7 @@ impl ReplColors {
}
}
fn get_colmode(args: &Args) -> ColorMode {
match args.color {
ColorChoice::Auto => ColorMode::Enabled,
@ -54,8 +45,7 @@ pub fn init_rustyline(args: &Args) -> Result<Editor<TalcHelper, FileHistory>, Ex
.color_mode(get_colmode(args))
.check_cursor_position(true)
.completion_type(rustyline::CompletionType::List)
.max_history_size(4096)
.unwrap()
.max_history_size(4096).unwrap()
.build();
let mut hist = FileHistory::default();
if let Some(f) = &args.histfile {
@ -70,12 +60,12 @@ pub fn init_rustyline(args: &Args) -> Result<Editor<TalcHelper, FileHistory>, Ex
Err(ReadlineError::Io(e)) => {
eprintln!("Error creating repl: {e}");
Err(ExitCode::FAILURE)
}
},
Err(ReadlineError::Errno(e)) => {
eprintln!("Error creating repl: {e}");
Err(ExitCode::FAILURE)
}
Err(_) => Err(ExitCode::SUCCESS),
},
Err(_) => Err(ExitCode::SUCCESS)
}
}
@ -84,6 +74,7 @@ pub fn repl(args: &Args) -> ExitCode {
eprintln!("input disassembly enabled");
}
let parser = talc_lang::Parser::new();
let mut compiler_globals = Vec::new();
let mut vm = Vm::new(256);
talc_std::load_all(&mut vm);
@ -127,15 +118,15 @@ pub fn repl(args: &Args) -> ExitCode {
Err(e) => {
eprintln!("{}Error:{} {e}", c.error, c.reset);
continue
}
},
};
let ex = match parser::parse(&line) {
let ex = match parser.parse(&line) {
Ok(ex) => ex,
Err(e) => {
eprintln!("{}Error:{} {e}", c.error, c.reset);
continue
}
},
};
let (f, g) = compile_repl(&ex, &compiler_globals);
@ -164,3 +155,4 @@ pub fn repl(args: &Args) -> ExitCode {
}
}
}

View file

@ -4,11 +4,13 @@ version = "0.2.0"
edition = "2021"
[dependencies]
lalrpop-util = { version = "0.20", features = ["lexer", "unicode"] }
num-complex = "0.4"
num-rational = { version = "0.4", default-features = false, features = [] }
num-traits = "0.2"
thiserror = "1.0"
lazy_static = "1.5"
lazy_static = "1.4"
unicode-ident = "1.0"
[build-dependencies]
lalrpop = { version = "0.20", default_features = false, features = ["lexer", "unicode"]}

3
talc-lang/build.rs Normal file
View file

@ -0,0 +1,3 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
lalrpop::process_root()
}

59
talc-lang/src/ast.rs Normal file
View file

@ -0,0 +1,59 @@
use crate::{lstring::LStr, symbol::Symbol, value::Value};
#[derive(Clone, Copy, Debug)]
pub enum BinaryOp {
Add, Sub, Mul, Div, Mod, Pow, IntDiv,
Shr, Shl, BitAnd, BitXor, BitOr,
Eq, Ne, Gt, Lt, Ge, Le,
Concat, Append,
Range, RangeIncl,
}
#[derive(Clone, Copy, Debug)]
pub enum UnaryOp {
Neg, Not, RangeEndless,
}
#[derive(Debug)]
pub enum Expr<'s> {
Literal(Value),
Ident(&'s LStr),
UnaryOp(UnaryOp, Box<Expr<'s>>),
BinaryOp(BinaryOp, Box<Expr<'s>>, Box<Expr<'s>>),
Assign(Option<BinaryOp>, Box<LValue<'s>>, Box<Expr<'s>>),
AssignVar(&'s LStr, Box<Expr<'s>>),
AssignGlobal(&'s LStr, Box<Expr<'s>>),
Index(Box<Expr<'s>>, Box<Expr<'s>>),
FnCall(Box<Expr<'s>>, Vec<Expr<'s>>),
AssocFnCall(Box<Expr<'s>>, Symbol, Vec<Expr<'s>>),
Pipe(Box<Expr<'s>>, Box<Expr<'s>>),
Block(Vec<Expr<'s>>),
List(Vec<Expr<'s>>),
Table(Vec<(Expr<'s>, Expr<'s>)>),
Return(Box<Expr<'s>>),
And(Box<Expr<'s>>, Box<Expr<'s>>),
Or(Box<Expr<'s>>, Box<Expr<'s>>),
If(Box<Expr<'s>>, Box<Expr<'s>>, Option<Box<Expr<'s>>>),
While(Box<Expr<'s>>, Box<Expr<'s>>),
For(&'s LStr, Box<Expr<'s>>, Box<Expr<'s>>),
Lambda(Vec<&'s LStr>, Box<Expr<'s>>),
Try(Box<Expr<'s>>, Vec<CatchBlock<'s>>),
}
#[derive(Debug)]
pub struct CatchBlock<'s> {
pub name: Option<&'s LStr>,
pub types: Option<Vec<Symbol>>,
pub body: Expr<'s>,
}
#[derive(Debug)]
pub enum LValue<'s> {
Ident(&'s LStr),
Index(Box<Expr<'s>>, Box<Expr<'s>>),
}

View file

@ -1,8 +1,4 @@
use crate::{
parser::ast::{BinaryOp, UnaryOp},
symbol::Symbol,
value::Value,
};
use crate::{value::Value, ast::{UnaryOp, BinaryOp}, symbol::Symbol};
#[derive(Clone, Copy, Debug, Default)]
pub struct Arg24([u8; 3]);
@ -17,20 +13,14 @@ impl Arg24 {
#[inline]
pub fn from_i32(n: i32) -> Self {
assert!(
(-0x80_0000..=0x7f_ffff).contains(&n),
"value out of range for argument"
);
assert!((-0x80_0000..=0x7f_ffff).contains(&n), "value out of range for argument");
// can't panic: size of slice guaranteed
Self(n.to_le_bytes()[0..3].try_into().unwrap())
}
#[inline]
pub fn from_i64(n: i64) -> Self {
assert!(
(-0x80_0000..=0x7f_ffff).contains(&n),
"value out of range for argument"
);
assert!((-0x80_0000..=0x7f_ffff).contains(&n), "value out of range for argument");
// can't panic: size of slice guaranteed
Self(n.to_le_bytes()[0..3].try_into().unwrap())
}
@ -74,9 +64,7 @@ impl From<Arg24> for i32 {
fn from(v: Arg24) -> Self {
let mut n = u32::from(v);
// sign-extend
if n & 0x00_800000 != 0 {
n |= 0xff_000000;
}
if n & 0x00_800000 != 0 { n |= 0xff_000000; }
n as i32
}
}
@ -100,55 +88,38 @@ impl TryFrom<Arg24> for Symbol {
#[derive(Clone, Copy, Debug, Default)]
pub enum Instruction {
#[default]
Nop, // do nothing
Nop,
LoadLocal(Arg24), // push nth local onto stack
StoreLocal(Arg24), // pop stack into nth local
NewLocal, // pop stack into a new local
DropLocal(Arg24), // remove last n locals
LoadLocal(Arg24),
StoreLocal(Arg24),
NewLocal,
DropLocal(Arg24),
LoadGlobal(Arg24), // load global by id
StoreGlobal(Arg24), // store global by id
LoadGlobal(Arg24), StoreGlobal(Arg24),
CloseOver(Arg24), // load nth local and convert to cell, write back a copy
Closure(Arg24), // load constant function and fill state from stack
LoadUpvalue(Arg24), // load
StoreUpvalue(Arg24), // store a cell from closure state to new local
ContinueUpvalue(Arg24), //
LoadClosedLocal(Arg24), // load through cell in nth local
StoreClosedLocal(Arg24), // store through cell in nth local
Const(Arg24),
Int(Arg24),
Symbol(Arg24),
Bool(bool),
Nil,
Const(Arg24), // push nth constant
Int(Arg24), // push integer
Symbol(Arg24), // push symbol
Bool(bool), // push boolean
Nil, // push nil
Dup,
DupTwo,
Drop(Arg24),
Swap,
Dup, DupTwo, Drop(Arg24), Swap,
UnaryOp(UnaryOp),
BinaryOp(BinaryOp),
NewList(u8),
GrowList(u8),
NewTable(u8),
GrowTable(u8),
NewList(u8), GrowList(u8),
NewTable(u8), GrowTable(u8),
Index,
StoreIndex,
Index, StoreIndex,
Jump(Arg24),
JumpTrue(Arg24),
JumpFalse(Arg24),
IterBegin,
IterTest(Arg24),
IterBegin, IterTest(Arg24),
BeginTry(Arg24),
EndTry,
BeginTry(Arg24), EndTry,
Call(u8),
Return,
@ -162,30 +133,14 @@ impl std::fmt::Display for Instruction {
Self::StoreLocal(a) => write!(f, "store {}", usize::from(a)),
Self::NewLocal => write!(f, "newlocal"),
Self::DropLocal(n) => write!(f, "discardlocal {}", usize::from(n)),
Self::LoadGlobal(s) => write!(
f,
"loadglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()
),
Self::StoreGlobal(s) => write!(
f,
"storeglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()
),
Self::CloseOver(n) => write!(f, "closeover {}", usize::from(n)),
Self::Closure(c) => write!(f, "closure {}", usize::from(c)),
Self::LoadUpvalue(n) => write!(f, "load_upval {}", usize::from(n)),
Self::StoreUpvalue(n) => write!(f, "store_upval {}", usize::from(n)),
Self::ContinueUpvalue(n) => write!(f, "cont_upval {}", usize::from(n)),
Self::LoadClosedLocal(n) => write!(f, "load_closed {}", usize::from(n)),
Self::StoreClosedLocal(n) => write!(f, "store_closed {}", usize::from(n)),
Self::LoadGlobal(s) => write!(f, "loadglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()),
Self::StoreGlobal(s) => write!(f, "storeglobal {}",
Symbol::try_from(s).expect("symbol does not exist").name()),
Self::Const(c) => write!(f, "const {}", usize::from(c)),
Self::Int(i) => write!(f, "int {}", i64::from(i)),
Self::Symbol(s) => write!(
f,
"symbol {}",
Symbol::try_from(s).expect("symbol does not exist").name()
),
Self::Symbol(s) => write!(f, "symbol {}",
Symbol::try_from(s).expect("symbol does not exist").name()),
Self::Bool(b) => write!(f, "bool {b}"),
Self::Nil => write!(f, "nil"),
Self::Dup => write!(f, "dup"),
@ -238,28 +193,19 @@ impl Chunk {
}
pub fn add_const(&mut self, v: Value) -> usize {
assert!(
self.consts.len() < 0xff_ffff,
"too many constants in a chunk"
);
assert!(self.consts.len() < 0xff_ffff, "too many constants in a chunk");
self.consts.push(v);
self.consts.len() - 1
}
pub fn add_instr(&mut self, i: Instruction) -> usize {
assert!(
self.instrs.len() < 0xff_ffff,
"too many instructions in a chunk"
);
assert!(self.instrs.len() < 0xff_ffff, "too many instructions in a chunk");
self.instrs.push(i);
self.instrs.len() - 1
}
pub fn begin_try_table(&mut self, local_count: usize) -> (usize, TryTable) {
assert!(
self.try_tables.len() < 0xff_ffff,
"too many catch tables in a chunk"
);
assert!(self.try_tables.len() < 0xff_ffff, "too many catch tables in a chunk");
let table = TryTable {
catches: Vec::new(),
local_count,
@ -272,3 +218,5 @@ impl Chunk {
self.try_tables[idx] = table;
}
}

View file

@ -1,36 +1,22 @@
use std::collections::{BTreeMap, HashMap};
use std::rc::Rc;
use crate::chunk::{Arg24, Catch, Chunk, Instruction as I};
use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
use crate::symbol::{Symbol, SYM_SELF};
use crate::ast::{BinaryOp, Expr, LValue, CatchBlock};
use crate::chunk::{Instruction as I, Chunk, Arg24, Catch};
use crate::lstr;
use crate::lstring::LStr;
use crate::symbol::Symbol;
use crate::value::function::{FuncAttrs, Function};
use crate::value::Value;
enum ResolveOutcome {
Var(VarKind),
InParent,
None,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum VarKind {
Local(usize),
Closed(usize),
Global,
}
#[derive(Debug, Clone)]
pub struct Var {
name: Symbol,
kind: VarKind,
pub struct Local {
name: Rc<LStr>,
scope: usize,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum CompilerMode {
Function,
Repl,
Module,
Function, Repl, Module,
}
struct Compiler<'a> {
@ -38,19 +24,18 @@ struct Compiler<'a> {
parent: Option<&'a Compiler<'a>>,
chunk: Chunk,
attrs: FuncAttrs,
scope: HashMap<Symbol, Var>,
shadowed: Vec<(Symbol, Option<Var>)>,
closes: BTreeMap<Symbol, usize>,
local_count: usize,
scope: usize,
locals: Vec<Local>,
globals: Vec<Local>,
}
pub fn compile(expr: &Expr, name: Option<Symbol>) -> Function {
let mut comp = Compiler::new_module(name, None);
pub fn compile(expr: &Expr) -> Function {
let mut comp = Compiler::new_module(None);
comp.expr(expr);
comp.finish()
}
pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec<Symbol>) {
pub fn compile_repl(expr: &Expr, globals: &[Local]) -> (Function, Vec<Local>) {
let mut comp = Compiler::new_repl(globals);
comp.expr(expr);
comp.finish_repl()
@ -58,66 +43,52 @@ pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec<Symbol>)
impl<'a> Default for Compiler<'a> {
fn default() -> Self {
let mut scope = HashMap::new();
scope.insert(
*SYM_SELF,
Var {
name: *SYM_SELF,
kind: VarKind::Local(0),
},
);
let locals = vec![Local {
name: lstr!("self").into(),
scope: 0,
}];
Self {
mode: CompilerMode::Function,
parent: None,
chunk: Chunk::new(),
attrs: FuncAttrs::default(),
scope,
shadowed: Vec::new(),
local_count: 1,
closes: BTreeMap::new(),
attrs: FuncAttrs { arity: 0, variadic: false },
scope: 0,
locals,
globals: Vec::new(),
}
}
}
impl<'a> Compiler<'a> {
fn new_repl(globals: &[Symbol]) -> Self {
let mut new = Self {
fn new_repl(globals: &[Local]) -> Self {
Self {
mode: CompilerMode::Repl,
attrs: FuncAttrs {
arity: 0,
name: Some(Symbol::get("<repl>")),
},
globals: globals.to_vec(),
..Self::default()
};
for g in globals {
new.declare_global(*g);
}
new
}
fn new_module(name: Option<Symbol>, parent: Option<&'a Self>) -> Self {
fn new_module(parent: Option<&'a Self>) -> Self {
Self {
mode: CompilerMode::Module,
attrs: FuncAttrs { arity: 0, name },
parent,
..Self::default()
}
}
fn new_function(&'a self, name: Option<Symbol>, args: &[Symbol]) -> Self {
fn new_function(&'a self, args: &[&LStr]) -> Self {
let mut new = Self {
mode: CompilerMode::Function,
attrs: FuncAttrs {
arity: args.len(),
name,
},
parent: Some(self),
..Self::default()
};
new.attrs.arity = args.len();
for arg in args {
new.declare_local(*arg);
new.locals.push(Local {
name: (*arg).into(),
scope: 0,
});
}
new
@ -125,27 +96,14 @@ impl<'a> Compiler<'a> {
pub fn finish(mut self) -> Function {
self.emit(I::Return);
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len())
Function { chunk: Rc::new(self.chunk), attrs: self.attrs }
}
pub fn finish_inner(mut self) -> (Function, BTreeMap<Symbol, usize>) {
self.emit(I::Return);
// TODO closure
(
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
self.closes,
)
}
pub fn finish_repl(mut self) -> (Function, Vec<Symbol>) {
pub fn finish_repl(mut self) -> (Function, Vec<Local>) {
self.emit(I::Return);
(
// TODO closure
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
self.scope
.into_iter()
.filter_map(|(_, v)| (v.kind == VarKind::Global).then_some(v.name))
.collect(),
Function { chunk: Rc::new(self.chunk), attrs: self.attrs },
self.globals
)
}
@ -168,44 +126,36 @@ impl<'a> Compiler<'a> {
// dup followed by store: remove the dup
if instrs.len() >= 2
&& matches!(instrs.get(instrs.len() - 2), Some(I::Dup))
&& matches!(
instrs.last(),
Some(
I::NewLocal
| I::StoreLocal(_) | I::StoreGlobal(_)
| I::StoreClosedLocal(_)
| I::StoreUpvalue(_)
)
) {
&& matches!(instrs.last(), Some(
I::NewLocal | I::StoreLocal(_) | I::StoreGlobal(_)
))
{
// can't panic: checked that instrs.len() >= 2
let i = self.chunk.instrs.pop().unwrap();
self.chunk.instrs.pop().unwrap();
self.chunk.instrs.push(i);
n -= 1;
continue
continue;
}
// final side-effectless instruction
let poppable = matches!(
instrs.last(),
Some(
I::Dup
| I::Const(_) | I::Int(_)
| I::Nil | I::Bool(_)
| I::Symbol(_) | I::LoadLocal(_)
| I::LoadClosedLocal(_)
| I::LoadUpvalue(_)
I::Dup | I::Const(_) | I::Int(_)
| I::Nil | I::Bool(_) | I::Symbol(_)
| I::LoadLocal(_)
)
);
if poppable {
// can't panic: checked that instrs.last() was Some
instrs.pop().unwrap();
n -= 1;
continue
continue;
}
// no more optimizations possible
break
break;
}
if n > 0 {
self.emit(I::Drop(Arg24::from_usize(n)));
@ -220,181 +170,164 @@ impl<'a> Compiler<'a> {
self.chunk.instrs[n] = new;
}
#[must_use]
fn begin_scope(&mut self) -> usize {
self.shadowed.len()
fn begin_scope(&mut self) {
self.scope += 1;
}
fn end_scope(&mut self, scope: usize) {
let mut locals = 0;
while self.shadowed.len() > scope {
let (name, var) = self.shadowed.pop().expect("scope bad");
fn end_scope(&mut self) {
self.scope -= 1;
if let Some(var) = var {
if var.kind != VarKind::Global {
locals += 1;
// no need to clean up at bottom scope
if self.scope == 0 { return; }
for i in (0..self.globals.len()).rev() {
if self.globals[i].scope <= self.scope {
break;
}
self.scope.insert(name, var);
} else {
self.scope.remove(&name);
self.globals.pop();
}
let mut count = 0;
for i in (0..self.locals.len()).rev() {
if self.locals[i].scope <= self.scope {
break;
}
self.locals.pop();
count += 1;
}
if count > 0 && self.scope > 0 {
self.emit(I::DropLocal(Arg24::from_usize(count)));
}
}
if locals > 0 {
self.emit(I::DropLocal(Arg24::from_usize(locals)));
self.local_count -= locals;
}
}
//
// variables
//
fn resolve_name(&self, name: Symbol) -> ResolveOutcome {
if let Some(v) = self.scope.get(&name) {
return ResolveOutcome::Var(v.kind)
}
let Some(parent) = self.parent else {
return ResolveOutcome::None
};
if let ResolveOutcome::None = parent.resolve_name(name) {
return ResolveOutcome::None
}
ResolveOutcome::InParent
fn resolve_local(&mut self, name: &LStr) -> Option<usize> {
self.locals.iter().rev()
.position(|v| v.name.as_ref() == name)
.map(|x| self.locals.len() - x - 1)
}
fn load_var(&mut self, name: Symbol) {
match self.resolve_name(name) {
ResolveOutcome::Var(VarKind::Local(n)) => {
fn resolve_global(&mut self, name: &LStr) -> Option<usize> {
self.globals.iter().rev()
.position(|v| v.name.as_ref() == name)
.map(|x| self.globals.len() - x - 1)
}
fn load_var(&mut self, name: &LStr) {
match (self.resolve_local(name), self.resolve_global(name)) {
(Some(n), None) => {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
}
ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
}
ResolveOutcome::InParent => {
let n = match self.closes.get(&name) {
Some(n) => *n,
None => {
let n = self.closes.len();
self.closes.insert(name, n);
n
}
};
self.emit(I::LoadUpvalue(Arg24::from_usize(n)));
}
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
self.emit(I::LoadGlobal(Arg24::from_symbol(name)));
},
(Some(n), Some(m)) if n >= m => {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
},
_ => {
let sym = Symbol::get(name);
self.emit(I::LoadGlobal(Arg24::from_symbol(sym)));
}
}
}
fn declare_local(&mut self, name: Symbol) -> usize {
let local = Var {
name,
kind: VarKind::Local(self.local_count),
};
self.local_count += 1;
let shadowed = self.scope.insert(name, local);
self.shadowed.push((name, shadowed));
self.local_count - 1
fn declare_local(&mut self, name: &LStr) -> usize {
if let Some(i) = self.resolve_local(name) {
if self.locals[i].scope == self.scope {
self.emit(I::StoreLocal(Arg24::from_usize(i)));
return i;
}
}
fn assign_local(&mut self, name: Symbol) -> usize {
let n = self.declare_local(name);
self.locals.push(Local {
name: name.into(),
scope: self.scope,
});
let i = self.locals.len() - 1;
self.emit(I::NewLocal);
n
i
}
fn assign_global(&mut self, name: Symbol) {
self.declare_global(name);
self.store_var(name);
fn store_local(&mut self, i: usize) {
self.emit(I::StoreLocal(Arg24::from_usize(i)));
}
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_global(&mut self, name: &LStr) {
let sym = Symbol::get(name);
self.emit(I::StoreGlobal(Arg24::from_symbol(sym)));
if let Some(i) = self.resolve_global(name) {
if self.globals[i].scope == self.scope {
return
}
}
self.globals.push(Local {
name: name.into(),
scope: self.scope,
});
}
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 store_default(&mut self, name: &LStr) {
match (self.resolve_local(name), self.resolve_global(name)) {
(Some(n), None) => self.store_local(n),
(Some(n), Some(m)) if n >= m => self.store_local(n),
(_, Some(_)) => self.store_global(name),
(None, None) => {
if self.mode == CompilerMode::Repl && self.scope == 1 {
self.store_global(name);
} else {
self.declare_local(name);
}
}
}
}
//
// Expressions
//
fn expr(&mut self, e: &Expr) {
let Expr { kind, .. } = e;
match kind {
ExprKind::Block(xs) if xs.is_empty() => {
self.emit(I::Nil);
}
ExprKind::Block(xs) => {
let scope = self.begin_scope();
for x in &xs[0..xs.len() - 1] {
match e {
Expr::Block(xs) if xs.is_empty() => { self.emit(I::Nil); },
Expr::Block(xs) => {
self.begin_scope();
for x in &xs[0..xs.len()-1] {
self.expr(x);
self.emit_discard(1);
}
self.expr(&xs[xs.len() - 1]);
self.end_scope(scope);
}
ExprKind::Literal(v) => self.expr_literal(v),
ExprKind::Ident(ident) => self.load_var(*ident),
ExprKind::UnaryOp(o, a) => {
self.expr(&xs[xs.len()-1]);
self.end_scope();
},
Expr::Literal(v) => self.expr_literal(v),
Expr::Ident(ident) => self.load_var(ident),
Expr::UnaryOp(o, a) => {
self.expr(a);
self.emit(I::UnaryOp(*o));
}
ExprKind::BinaryOp(o, a, b) => {
},
Expr::BinaryOp(o, a, b) => {
self.expr(a);
self.expr(b);
self.emit(I::BinaryOp(*o));
}
ExprKind::Assign(o, lv, a) => self.expr_assign(*o, lv, a),
ExprKind::AssignVar(name, a) => {
},
Expr::Assign(o, lv, a) => self.expr_assign(*o, lv, a),
Expr::AssignVar(name, a) => {
self.expr(a);
self.emit(I::Dup);
self.assign_local(*name);
}
ExprKind::AssignGlobal(name, a) => {
self.declare_local(name);
},
Expr::AssignGlobal(name, a) => {
self.expr(a);
self.emit(I::Dup);
self.assign_global(*name);
}
ExprKind::List(xs) if xs.is_empty() => {
self.store_global(name);
},
Expr::List(xs) if xs.is_empty() => {
self.emit(I::NewList(0));
}
ExprKind::List(xs) => {
},
Expr::List(xs) => {
let mut first = true;
for chunk in xs.chunks(16) {
for e in chunk {
@ -407,11 +340,11 @@ impl<'a> Compiler<'a> {
self.emit(I::GrowList(chunk.len() as u8));
}
}
}
ExprKind::Table(xs) if xs.is_empty() => {
},
Expr::Table(xs) if xs.is_empty() => {
self.emit(I::NewTable(0));
}
ExprKind::Table(xs) => {
},
Expr::Table(xs) => {
let mut first = true;
for chunk in xs.chunks(8) {
for (k, v) in chunk {
@ -425,20 +358,20 @@ impl<'a> Compiler<'a> {
self.emit(I::GrowTable(chunk.len() as u8));
}
}
}
ExprKind::Index(ct, idx) => {
},
Expr::Index(ct, idx) => {
self.expr(ct);
self.expr(idx);
self.emit(I::Index);
}
ExprKind::FnCall(f, args) => {
},
Expr::FnCall(f, args) => {
self.expr(f);
for a in args {
self.expr(a);
}
self.emit(I::Call(args.len() as u8));
}
ExprKind::AssocFnCall(o, f, args) => {
},
Expr::AssocFnCall(o, f, args) => {
self.expr(o);
self.emit(I::Dup);
self.emit(I::Symbol(Arg24::from_symbol(*f)));
@ -448,36 +381,35 @@ impl<'a> Compiler<'a> {
self.expr(a);
}
self.emit(I::Call((args.len() + 1) as u8));
}
ExprKind::Return(e) => {
},
Expr::Return(e) => {
self.expr(e);
self.emit(I::Return);
}
ExprKind::Pipe(a, f) => {
},
Expr::Pipe(a, f) => {
self.expr(a);
self.expr(f);
self.emit(I::Swap);
self.emit(I::Call(1));
}
ExprKind::Lambda(args, body) => self.expr_fndef(None, args, body),
ExprKind::FnDef(name, args, body) => self.expr_fndef(*name, args, body),
ExprKind::And(a, b) => {
},
Expr::Lambda(args, body) => self.expr_lambda(args, body),
Expr::And(a, b) => {
self.expr(a);
self.emit(I::Dup);
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
self.emit_discard(1);
self.expr(b);
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
}
ExprKind::Or(a, b) => {
},
Expr::Or(a, b) => {
self.expr(a);
self.emit(I::Dup);
let j1 = self.emit(I::JumpTrue(Arg24::from_usize(0)));
self.emit_discard(1);
self.expr(b);
self.update_instr(j1, I::JumpTrue(Arg24::from_usize(self.ip())));
}
ExprKind::If(cond, b1, b2) => {
},
Expr::If(cond, b1, b2) => {
self.expr(cond);
let j1 = self.emit(I::JumpFalse(Arg24::from_usize(0)));
self.expr(b1);
@ -488,9 +420,11 @@ impl<'a> Compiler<'a> {
} else {
self.emit(I::Nil);
}
self.update_instr(j2, I::Jump(Arg24::from_usize(self.ip())));
}
ExprKind::While(cond, body) => {
self.update_instr(j2,
I::Jump(Arg24::from_usize(self.ip()))
);
},
Expr::While(cond, body) => {
let start = self.ip();
self.expr(cond);
@ -501,14 +435,14 @@ impl<'a> Compiler<'a> {
self.update_instr(j1, I::JumpFalse(Arg24::from_usize(self.ip())));
self.emit(I::Nil);
}
ExprKind::For(name, iter, body) => self.expr_for(*name, iter, body),
ExprKind::Try(body, catches) => self.expr_try(body, catches),
},
Expr::For(name, iter, body) => self.expr_for(name, iter, body),
Expr::Try(body, catches) => self.expr_try(body, catches),
}
}
fn expr_try(&mut self, body: &Expr, catch_blocks: &[CatchBlock]) {
let (idx, mut table) = self.chunk.begin_try_table(self.local_count);
let (idx, mut table) = self.chunk.begin_try_table(self.locals.len());
self.emit(I::BeginTry(Arg24::from_usize(idx)));
self.expr(body);
@ -522,16 +456,16 @@ impl<'a> Compiler<'a> {
types: catch_block.types.clone(),
});
let scope = self.begin_scope();
self.begin_scope();
if let Some(name) = catch_block.name {
self.assign_local(name);
self.declare_local(name);
} else {
self.emit_discard(1);
}
self.expr(&catch_block.body);
self.end_scope(scope);
self.end_scope();
let end_addr = self.emit(I::Jump(Arg24::from_usize(0)));
catch_end_addrs.push(end_addr);
@ -546,15 +480,15 @@ impl<'a> Compiler<'a> {
self.chunk.finish_catch_table(idx, table);
}
fn expr_for(&mut self, name: Symbol, iter: &Expr, body: &Expr) {
fn expr_for(&mut self, name: &LStr, iter: &Expr, body: &Expr) {
// load iterable and convert to iterator
self.expr(iter);
self.emit(I::IterBegin);
// declare loop variable
let scope = self.begin_scope();
self.begin_scope();
self.emit(I::Nil);
self.assign_local(name);
let local = self.declare_local(name);
// begin loop
let start = self.ip();
@ -563,7 +497,7 @@ impl<'a> Compiler<'a> {
self.emit(I::Dup);
self.emit(I::Call(0));
let j1 = self.emit(I::IterTest(Arg24::from_usize(0)));
self.store_var(name);
self.store_local(local);
// body
self.expr(body);
@ -573,96 +507,57 @@ impl<'a> Compiler<'a> {
self.emit(I::Jump(Arg24::from_usize(start)));
self.update_instr(j1, I::IterTest(Arg24::from_usize(self.ip())));
self.end_scope(scope);
self.end_scope();
self.emit(I::Nil);
}
fn expr_fndef(&mut self, name: Option<Symbol>, args: &[Symbol], body: &Expr) {
let mut inner = self.new_function(name, args);
fn expr_lambda(&mut self, args: &[&LStr], body: &Expr) {
let mut inner = self.new_function(args);
inner.parent = Some(self);
inner.expr(body);
let (func, closes) = inner.finish_inner();
let func_const = self.add_const(func.into());
let num_closed = closes.len();
for (name, _) in closes {
match self.resolve_name(name) {
ResolveOutcome::Var(VarKind::Local(n)) => {
self.emit(I::CloseOver(Arg24::from_usize(n)));
self.scope.entry(name).and_modify(|v| {
v.kind = VarKind::Closed(n);
});
}
ResolveOutcome::Var(VarKind::Closed(n)) => {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
}
ResolveOutcome::InParent => {
let n = self.closes.len();
self.closes.insert(name, n);
self.emit(I::ContinueUpvalue(Arg24::from_usize(n)));
}
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
panic!("upvalue resolved to none or global")
}
}
}
if num_closed == 0 {
self.emit(I::Const(Arg24::from_usize(func_const)));
} else {
self.emit(I::Closure(Arg24::from_usize(func_const)));
}
if let Some(name) = name {
self.emit(I::Dup);
self.store_var(name);
}
let func = inner.finish();
let n = self.add_const(func.into());
self.emit(I::Const(Arg24::from_usize(n)));
}
fn expr_literal(&mut self, val: &Value) {
match val {
Value::Nil => {
self.emit(I::Nil);
}
Value::Bool(b) => {
self.emit(I::Bool(*b));
}
Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i) => {
self.emit(I::Int(Arg24::from_i64(*i)));
}
Value::Symbol(s) => {
self.emit(I::Symbol(Arg24::from_symbol(*s)));
}
Value::Nil
=> { self.emit(I::Nil); },
Value::Bool(b)
=> { self.emit(I::Bool(*b)); },
Value::Int(i) if (-0x80_0000..=0x7f_ffff).contains(i)
=> { self.emit(I::Int(Arg24::from_i64(*i))); },
Value::Symbol(s)
=> { self.emit(I::Symbol(Arg24::from_symbol(*s))); },
_ => {
let n = self.add_const(val.clone());
self.emit(I::Const(Arg24::from_usize(n)));
}
},
}
}
fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) {
match (&lv.kind, o) {
(LValueKind::Ident(i), None) => {
match (lv, o) {
(LValue::Ident(i), None) => {
self.expr(a);
self.emit(I::Dup);
self.store_var(*i);
}
(LValueKind::Ident(i), Some(o)) => {
self.load_var(*i);
self.store_default(i);
},
(LValue::Ident(i), Some(o)) => {
self.load_var(i);
self.expr(a);
self.emit(I::BinaryOp(o));
self.emit(I::Dup);
self.store_var(*i);
}
(LValueKind::Index(ct, i), None) => {
self.store_default(i);
},
(LValue::Index(ct, i), None) => {
self.expr(ct);
self.expr(i);
self.expr(a);
self.emit(I::StoreIndex);
}
(LValueKind::Index(ct, i), Some(o)) => {
},
(LValue::Index(ct, i), Some(o)) => {
self.expr(ct);
self.expr(i);
self.emit(I::DupTwo);
@ -670,7 +565,8 @@ impl<'a> Compiler<'a> {
self.expr(a);
self.emit(I::BinaryOp(o));
self.emit(I::StoreIndex);
},
}
}
}
}

View file

@ -1,9 +1,5 @@
use crate::{
lstring::LStr,
symbol::{Symbol, SYM_DATA, SYM_MSG, SYM_TYPE},
value::{HashValue, Value},
};
use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc};
use std::{rc::Rc, collections::HashMap, cell::RefCell, fmt::Display};
use crate::{lstring::LStr, symbol::{Symbol, SYM_DATA, SYM_MSG, SYM_TYPE}, value::{HashValue, Value}};
pub type Result<T> = std::result::Result<T, Exception>;
@ -16,35 +12,19 @@ pub struct Exception {
impl Exception {
pub fn new(ty: Symbol) -> Self {
Self {
ty,
msg: None,
data: None,
}
Self { ty, msg: None, data: None }
}
pub fn new_with_msg(ty: Symbol, msg: Rc<LStr>) -> Self {
Self {
ty,
msg: Some(msg),
data: None,
}
Self { ty, msg: Some(msg), data: None }
}
pub fn new_with_data(ty: Symbol, data: Value) -> Self {
Self {
ty,
msg: None,
data: Some(data),
}
Self { ty, msg: None, data: Some(data) }
}
pub fn new_with_msg_data(ty: Symbol, msg: Rc<LStr>, data: Value) -> Self {
Self {
ty,
msg: Some(msg),
data: Some(data),
}
Self { ty, msg: Some(msg), data: Some(data) }
}
pub fn from_table(table: &Rc<RefCell<HashMap<HashValue, Value>>>) -> Option<Self> {
@ -96,11 +76,11 @@ impl Display for Exception {
#[macro_export]
macro_rules! exception {
($exc_ty:expr, $($t:tt)*) => {
($exc_ty:expr, $fstr:literal, $($arg:expr),*) => {
$crate::exception::Exception::new_with_msg(
$exc_ty,
$crate::lstring::LString::from(
format!($($t)*)
format!($fstr, $($arg),*)
).into()
)
};
@ -122,3 +102,6 @@ macro_rules! throw {
}
pub use throw;

View file

@ -1,14 +1,50 @@
#![allow(clippy::mutable_key_type)]
#![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::allow_attributes)]
#[rustfmt::skip]
#[allow(clippy::extra_unused_lifetimes)]
#[allow(clippy::needless_lifetimes)]
#[allow(clippy::let_unit_value)]
#[allow(clippy::just_underscores_and_digits)]
#[allow(clippy::pedantic)]
mod parser {
pub use __intern_token::new_builder as new_builder;
include!(concat!(env!("OUT_DIR"),"/parser.rs"));
}
mod parser_util;
mod vm;
pub mod symbol;
pub mod ast;
pub mod value;
pub mod exception;
pub mod chunk;
pub mod compiler;
pub mod exception;
pub mod lstring;
pub mod parser;
pub mod symbol;
pub mod value;
mod vm;
pub use parser::BlockParser as Parser;
pub use vm::Vm;
pub use parser_util::{parse_int, parse_float, parse_str_escapes};
type LexResult<'input> = Result<
(usize, Token<'input>, usize),
ParseError<usize, Token<'input>, parser_util::ParseError>
>;
use lalrpop_util::{ParseError, lexer::{Token, MatcherBuilder}};
pub struct Lexer {
builder: MatcherBuilder,
}
impl Default for Lexer {
fn default() -> Self { Self::new() }
}
impl Lexer {
pub fn new() -> Self {
Self { builder: crate::parser::new_builder() }
}
pub fn lex<'s>(&'s self, input: &'s str) -> impl Iterator<Item=LexResult<'s>> {
self.builder.matcher::<parser_util::ParseError>(input)
}
}

View file

@ -1,15 +1,4 @@
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, 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};
@ -39,9 +28,7 @@ fn is_continue(b: u8) -> bool {
#[inline]
fn calc_continue(mut ch: u32, bytes: &[u8]) -> Option<char> {
for b in bytes {
if !is_continue(*b) {
return None
}
if !is_continue(*b) { return None }
ch = (ch << 6) | (b & 0x3f) as u32;
}
char::from_u32(ch)
@ -53,29 +40,23 @@ fn next_codepoint(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
match init {
0..=0x7f => return Some((&bytes[1..], Ok(init as char))),
0xc0..=0xdf => 'case: {
if bytes.len() < 2 {
break 'case
}
if bytes.len() < 2 { break 'case }
let Some(ch) = calc_continue(init as u32 & 0x1f, &bytes[1..2]) else {
break 'case
break 'case;
};
return Some((&bytes[2..], Ok(ch)))
}
},
0xe0..=0xef => 'case: {
if bytes.len() < 3 {
break 'case
}
if bytes.len() < 3 { break 'case }
let Some(ch) = calc_continue(init as u32 & 0x0f, &bytes[1..3]) else {
break 'case
break 'case;
};
return Some((&bytes[3..], Ok(ch)))
}
},
0xf0..=0xf7 => 'case: {
if bytes.len() < 4 {
break 'case
}
if bytes.len() < 4 { break 'case }
let Some(ch) = calc_continue(init as u32 & 0x07, &bytes[1..4]) else {
break 'case
break 'case;
};
return Some((&bytes[4..], Ok(ch)))
}
@ -88,55 +69,45 @@ fn next_codepoint(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
#[inline]
fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
let len = bytes.len();
if len < 1 {
return None
}
if len < 1 { return None }
let last = bytes[len - 1];
let last = bytes[len-1];
if (0..=0x7f).contains(&last) {
return Some((&bytes[..len - 1], Ok(last as char)))
return Some((&bytes[..len-1], Ok(last as char)))
}
'case: {
if !is_continue(last) {
break 'case
}
if !is_continue(last) { break 'case }
if len < 2 {
break 'case
}
let b1 = bytes[len - 2];
if len < 2 { break 'case }
let b1 = bytes[len-2];
if 0xe0 & b1 == 0xc0 {
if let Some(ch) = calc_continue(b1 as u32 & 0x1f, &[last]) {
return Some((&bytes[..len - 2], Ok(ch)))
return Some((&bytes[..len-2], Ok(ch)))
};
} else if !is_continue(b1) {
break 'case
}
if len < 3 {
break 'case
}
let b2 = bytes[len - 3];
if len < 3 { break 'case }
let b2 = bytes[len-3];
if 0xf0 & b2 == 0xe0 {
if let Some(ch) = calc_continue(b2 as u32 & 0x0f, &[b1, last]) {
return Some((&bytes[..len - 3], Ok(ch)))
return Some((&bytes[..len-3], Ok(ch)))
};
} else if !is_continue(b2) {
break 'case
}
if len < 4 {
break 'case
}
let b3 = bytes[len - 4];
if len < 4 { break 'case }
let b3 = bytes[len-4];
if 0xf8 & b3 == 0xf0 {
if let Some(ch) = calc_continue(b3 as u32 & 0x07, &[b2, b1, last]) {
return Some((&bytes[..len - 4], Ok(ch)))
return Some((&bytes[..len-4], Ok(ch)))
};
}
}
Some((&bytes[..len - 1], Err(last)))
Some((&bytes[..len-1], Err(last)))
}
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -259,69 +230,32 @@ impl BorrowMut<LStr> for LString {
impl From<LString> for Vec<u8> {
#[inline]
fn from(value: LString) -> Self {
value.inner
}
fn from(value: LString) -> Self { value.inner }
}
impl From<Vec<u8>> for LString {
#[inline]
fn from(value: Vec<u8>) -> Self {
Self { inner: value }
}
fn from(value: Vec<u8>) -> Self { Self { inner: value } }
}
impl From<String> for LString {
#[inline]
fn from(value: String) -> Self {
Self {
inner: value.into_bytes(),
}
}
}
impl From<OsString> for LString {
#[inline]
fn from(value: OsString) -> Self {
Self {
inner: value.into_encoded_bytes(),
}
}
fn from(value: String) -> Self { Self { inner: value.into_bytes() } }
}
impl From<&LStr> for LString {
#[inline]
fn from(value: &LStr) -> Self {
value.to_owned()
}
fn from(value: &LStr) -> Self { value.to_owned() }
}
impl From<&str> for LString {
#[inline]
fn from(value: &str) -> Self {
value.to_owned().into()
}
fn from(value: &str) -> Self { value.to_owned().into() }
}
impl From<&[u8]> for LString {
#[inline]
fn from(value: &[u8]) -> Self {
value.to_owned().into()
}
}
impl<const N: usize> From<&[u8; N]> for LString {
#[inline]
fn from(value: &[u8; N]) -> Self {
value.as_slice().into()
}
}
impl<const N: usize> From<[u8; N]> for LString {
#[inline]
fn from(value: [u8; N]) -> Self {
value.as_slice().into()
}
fn from(value: &[u8]) -> Self { value.to_owned().into() }
}
impl From<Cow<'_, LStr>> for LString {
@ -329,7 +263,7 @@ impl From<Cow<'_, LStr>> for LString {
fn from(value: Cow<'_, LStr>) -> Self {
match value {
Cow::Borrowed(b) => b.to_owned(),
Cow::Owned(o) => o,
Cow::Owned(o) => o
}
}
}
@ -348,18 +282,9 @@ impl From<Cow<'_, [u8]>> for LString {
}
}
impl<const N: usize> From<Cow<'_, [u8; N]>> for LString {
#[inline]
fn from(value: Cow<'_, [u8; N]>) -> Self {
value.into_owned().into()
}
}
impl<'a> From<&'a LStr> for &'a [u8] {
#[inline]
fn from(value: &'a LStr) -> Self {
&value.inner
}
fn from(value: &'a LStr) -> Self { &value.inner }
}
impl<'a> From<&'a [u8]> for &'a LStr {
@ -369,13 +294,6 @@ impl<'a> From<&'a [u8]> for &'a LStr {
}
}
impl<'a, const N: usize> From<&'a [u8; N]> for &'a LStr {
#[inline]
fn from(value: &'a [u8; N]) -> Self {
LStr::from_bytes(value.as_slice())
}
}
impl<'a> From<&'a str> for &'a LStr {
#[inline]
fn from(value: &'a str) -> Self {
@ -383,18 +301,9 @@ impl<'a> From<&'a str> for &'a LStr {
}
}
impl<'a> From<&'a OsStr> for &'a LStr {
#[inline]
fn from(value: &'a OsStr) -> Self {
LStr::from_os_str(value)
}
}
impl<'a> From<&'a mut LStr> for &'a mut [u8] {
#[inline]
fn from(value: &'a mut LStr) -> Self {
&mut value.inner
}
fn from(value: &'a mut LStr) -> Self { &mut value.inner }
}
impl<'a> From<&'a mut [u8]> for &'a mut LStr {
@ -406,9 +315,7 @@ impl<'a> From<&'a mut [u8]> for &'a mut LStr {
impl<'a> From<&'a LString> for &'a LStr {
#[inline]
fn from(value: &'a LString) -> Self {
value
}
fn from(value: &'a LString) -> Self { value }
}
impl From<&LStr> for Rc<LStr> {
@ -469,35 +376,35 @@ impl From<u8> for LString {
impl FromIterator<char> for LString {
#[inline]
fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
fn from_iter<I: IntoIterator<Item=char>>(iter: I) -> Self {
String::from_iter(iter).into()
}
}
impl FromIterator<u8> for LString {
#[inline]
fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
fn from_iter<T: IntoIterator<Item=u8>>(iter: T) -> Self {
Vec::from_iter(iter).into()
}
}
impl Extend<u8> for LString {
#[inline]
fn extend<I: IntoIterator<Item = u8>>(&mut self, iter: I) {
fn extend<I: IntoIterator<Item=u8>>(&mut self, iter: I) {
self.inner.extend(iter);
}
}
impl<'a> Extend<&'a u8> for LString {
#[inline]
fn extend<I: IntoIterator<Item = &'a u8>>(&mut self, iter: I) {
fn extend<I: IntoIterator<Item=&'a u8>>(&mut self, iter: I) {
self.inner.extend(iter);
}
}
impl Extend<char> for LString {
#[inline]
fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
fn extend<I: IntoIterator<Item=char>>(&mut self, iter: I) {
let iter = iter.into_iter();
let (lo, _) = iter.size_hint();
self.reserve(lo);
@ -507,7 +414,7 @@ impl Extend<char> for LString {
impl<'a> Extend<&'a char> for LString {
#[inline]
fn extend<I: IntoIterator<Item = &'a char>>(&mut self, iter: I) {
fn extend<I: IntoIterator<Item=&'a char>>(&mut self, iter: I) {
let iter = iter.into_iter();
let (lo, _) = iter.size_hint();
self.reserve(lo);
@ -525,9 +432,7 @@ impl io::Write for LString {
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
fn flush(&mut self) -> io::Result<()> { Ok(()) }
}
//impl fmt::Write for LString {
@ -606,25 +511,15 @@ impl<'a> Iterator for Bytes<'a> {
type Item = u8;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn next(&mut self) -> Option<Self::Item> { self.0.next() }
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
#[inline]
fn count(self) -> usize {
self.0.count()
}
fn count(self) -> usize { self.0.count() }
#[inline]
fn last(self) -> Option<Self::Item> {
self.0.last()
}
fn last(self) -> Option<Self::Item> { self.0.last() }
#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.0.nth(n)
}
fn nth(&mut self, n: usize) -> Option<Self::Item> { self.0.nth(n) }
#[inline]
fn all<F: FnMut(Self::Item) -> bool>(&mut self, f: F) -> bool {
self.0.all(f)
@ -645,9 +540,7 @@ impl<'a> Iterator for Bytes<'a> {
impl<'a> ExactSizeIterator for Bytes<'a> {
#[inline]
fn len(&self) -> usize {
self.0.len()
}
fn len(&self) -> usize { self.0.len() }
}
impl<'a> FusedIterator for Bytes<'a> {}
@ -668,7 +561,7 @@ impl<'a> Iterator for LosslessChars<'a> {
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.0.len();
((len + 3) / 4, Some(len))
((len + 3)/4, Some(len))
}
}
@ -680,42 +573,6 @@ impl<'a> DoubleEndedIterator for LosslessChars<'a> {
}
}
#[derive(Clone)]
pub struct LosslessCharsIndices<'a>(&'a [u8], usize);
impl<'a> Iterator for LosslessCharsIndices<'a> {
type Item = (usize, Result<char, u8>);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let (new_bytes, res) = next_codepoint(self.0)?;
let index = self.1;
self.0 = new_bytes;
match res {
Ok(c) => self.1 += c.len_utf8(),
Err(_) => self.1 += 1,
}
Some((index, res))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.0.len();
((len + 3) / 4, Some(len))
}
}
impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
let (new_bytes, res) = next_codepoint_back(self.0)?;
self.0 = new_bytes;
match res {
Ok(c) => self.1 -= c.len_utf8(),
Err(_) => self.1 -= 1,
}
Some((self.1, res))
}
}
#[derive(Clone)]
pub struct Chars<'a>(LosslessChars<'a>);
@ -748,36 +605,6 @@ impl<'a> DoubleEndedIterator for Chars<'a> {
}
}
#[derive(Clone)]
pub struct CharsIndices<'a>(LosslessCharsIndices<'a>);
impl<'a> Iterator for CharsIndices<'a> {
type Item = (usize, char);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
loop {
if let (index, Ok(c)) = self.0.next()? {
return Some((index, c))
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<'a> DoubleEndedIterator for CharsIndices<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
if let (index, Ok(c)) = self.0.next_back()? {
return Some((index, c))
}
}
}
}
impl LStr {
#[inline]
@ -785,11 +612,6 @@ impl LStr {
Self::from_bytes(string.as_bytes())
}
#[inline]
pub fn from_os_str(os_string: &OsStr) -> &Self {
Self::from_bytes(os_string.as_encoded_bytes())
}
#[inline]
pub const fn from_bytes(bytes: &[u8]) -> &Self {
unsafe { &*(bytes as *const [u8] as *const LStr) }
@ -801,35 +623,19 @@ impl LStr {
}
#[inline]
pub const fn as_bytes(&self) -> &[u8] {
&self.inner
}
pub const fn as_bytes(&self) -> &[u8] { &self.inner }
#[inline]
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.inner
}
#[inline]
pub fn byte_at(&self, n: usize) -> u8 {
self.inner[n]
}
#[inline]
pub fn byte_get(&self, n: usize) -> Option<u8> {
self.inner.get(n).copied()
}
pub fn as_bytes_mut(&mut self) -> &mut [u8] { &mut self.inner }
#[inline]
pub fn bytes(&self) -> Bytes {
Bytes(self.as_bytes().iter().copied())
}
#[inline]
pub fn chars(&self) -> Chars {
Chars(self.chars_lossless())
}
#[inline]
pub fn chars_lossless(&self) -> LosslessChars {
LosslessChars(self.as_bytes())
@ -865,13 +671,11 @@ impl LStr {
#[inline]
pub fn to_os_str(&self) -> Cow<OsStr> {
#[cfg(unix)]
{
#[cfg(unix)] {
use std::os::unix::ffi::OsStrExt;
Cow::Borrowed(OsStr::from_bytes(self.as_bytes()))
}
#[cfg(not(unix))]
{
#[cfg(not(unix))] {
Cow::Owned(self.to_string().into())
}
}
@ -942,21 +746,10 @@ impl LStr {
self.as_bytes().ends_with(s.as_bytes())
}
pub fn strip_prefix(&self, prefix: &LStr) -> Option<&LStr> {
self.as_bytes()
.strip_prefix(prefix.as_bytes())
.map(LStr::from_bytes)
}
pub fn strip_suffix(&self, suffix: &LStr) -> Option<&LStr> {
self.as_bytes()
.strip_suffix(suffix.as_bytes())
.map(LStr::from_bytes)
}
pub fn is_identifier(&self) -> bool {
let mut chars = self.chars_lossless();
let first = chars.next().is_some_and(|ch| ch.is_ok_and(is_xid_start));
let first = chars.next()
.is_some_and(|ch| ch.is_ok_and(is_xid_start));
if !first {
return false
}
@ -982,15 +775,11 @@ impl AddAssign<&LStr> for LString {
}
impl Default for &LStr {
fn default() -> Self {
[].as_ref().into()
}
fn default() -> Self { [].as_ref().into() }
}
impl Default for &mut LStr {
fn default() -> Self {
[].as_mut().into()
}
fn default() -> Self { [].as_mut().into() }
}
macro_rules! impl_index {
@ -1032,3 +821,4 @@ impl_index!(std::ops::RangeInclusive<usize>);
impl_index!(std::ops::RangeTo<usize>);
impl_index!(std::ops::RangeToInclusive<usize>);
impl_index!((std::ops::Bound<usize>, std::ops::Bound<usize>));

View file

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

View file

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

View file

@ -1,602 +0,0 @@
use std::{fmt, iter::Peekable, str::Chars};
use unicode_ident::{is_xid_continue, is_xid_start};
use super::{ParserError, Pos, Span};
type Result<T> = std::result::Result<T, ParserError>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TokenKind {
Eof,
LineSeparator,
Bang,
BangEqual,
HashAmper,
HashAmperEqual,
HashCaret,
HashCaretEqual,
HashPipe,
HashPipeEqual,
Dollar,
Percent,
PercentEqual,
Amper,
AmperEqual,
Star,
StarEqual,
Plus,
PlusPlus,
PlusPlusEqual,
PlusEqual,
Comma,
Minus,
MinusEqual,
Arrow,
Dot,
DotDot,
DotDotStar,
DotDotEqual,
Slash,
SlashSlash,
SlashSlashEqual,
SlashEqual,
Colon,
Less,
LessLess,
LessLessEqual,
LessEqual,
Equal,
EqualEqual,
Greater,
GreaterEqual,
GreaterGreater,
GreaterGreaterEqual,
LParen,
RParen,
LBrack,
RBrack,
LBrace,
RBrace,
Backslash,
Caret,
CaretEqual,
Pipe,
Identifier,
Integer,
Float,
Imaginary,
String,
Symbol,
And,
Break,
Catch,
Continue,
Do,
Elif,
Else,
End,
False,
Fn,
For,
Global,
If,
In,
Nil,
Not,
Or,
Return,
Then,
True,
Try,
Var,
While,
}
use TokenKind as K;
impl TokenKind {
pub fn name(self) -> &'static str {
match self {
K::Eof => "end of file",
K::LineSeparator => "line separator",
K::Bang => "'!'",
K::BangEqual => "'!='",
K::HashAmper => "'#&'",
K::HashAmperEqual => "'#&='",
K::HashCaret => "'#^'",
K::HashCaretEqual => "'#^='",
K::HashPipe => "'#|'",
K::HashPipeEqual => "'#|='",
K::Dollar => "'$'",
K::Percent => "'%'",
K::PercentEqual => "'%='",
K::Amper => "'&'",
K::AmperEqual => "'&='",
K::Star => "'*'",
K::StarEqual => "'*='",
K::Plus => "'+'",
K::PlusPlus => "'++'",
K::PlusPlusEqual => "'++='",
K::PlusEqual => "'+='",
K::Comma => "','",
K::Minus => "'-'",
K::MinusEqual => "'-='",
K::Arrow => "'=>'",
K::Dot => "'.'",
K::DotDot => "'..'",
K::DotDotStar => "'..*'",
K::DotDotEqual => "'..='",
K::Slash => "'/'",
K::SlashSlash => "'//'",
K::SlashSlashEqual => "'//='",
K::SlashEqual => "'/='",
K::Colon => "':'",
K::Less => "'<'",
K::LessLess => "'<<'",
K::LessLessEqual => "'<<='",
K::LessEqual => "'<='",
K::Equal => "'='",
K::EqualEqual => "'=='",
K::Greater => "'>'",
K::GreaterEqual => "'>='",
K::GreaterGreater => "'>>'",
K::GreaterGreaterEqual => "'>>='",
K::LParen => "'('",
K::RParen => "')'",
K::LBrack => "'['",
K::RBrack => "']'",
K::LBrace => "'{'",
K::RBrace => "'}'",
K::Backslash => "'\\'",
K::Caret => "'^'",
K::CaretEqual => "'^='",
K::Pipe => "'|'",
K::Identifier => "identifier",
K::Integer => "integer",
K::Float => "float",
K::Imaginary => "imaginary",
K::String => "string",
K::Symbol => "symbol",
K::And => "'and'",
K::Break => "'break'",
K::Catch => "'catch'",
K::Continue => "'continue'",
K::Do => "'do'",
K::Elif => "'elif'",
K::Else => "'else'",
K::End => "'end'",
K::False => "'false'",
K::Fn => "'fn'",
K::For => "'for'",
K::Global => "'global'",
K::If => "'if'",
K::In => "'in'",
K::Nil => "'nil'",
K::Not => "'not'",
K::Or => "'or'",
K::Return => "'return'",
K::Then => "'then'",
K::True => "'true'",
K::Try => "'try'",
K::Var => "'var'",
K::While => "'while'",
}
}
}
impl fmt::Display for TokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Clone, Copy, Debug)]
pub struct Token<'s> {
pub span: Span,
pub content: &'s str,
pub kind: TokenKind,
}
pub struct Lexer<'s> {
src: &'s str,
chars: Peekable<Chars<'s>>,
start_pos: Pos,
pos: Pos,
}
impl<'s> Lexer<'s> {
pub fn new(src: &'s str) -> Self {
Self {
src,
chars: src.chars().peekable(),
start_pos: Pos::new(),
pos: Pos::new(),
}
}
fn invalid_char(&self, c: char) -> ParserError {
let span = Span::new(self.pos, self.pos.advance(c));
let msg = match c as u32 {
c @ 0x00..=0x7f => format!("invalid character (codepoint 0x{:2x})", c),
c => format!("invalid character (codepoint U+{:04x})", c),
};
ParserError { span, msg }
}
fn filter_char(&mut self, c: Option<char>, advance: bool) -> Result<char> {
match c {
Some(c) if c.is_control() && !matches!(c, '\n' | '\r' | '\t') => {
Err(self.invalid_char(c))
}
Some(c) => {
if advance {
self.pos = self.pos.advance(c);
}
Ok(c)
}
None => Ok('\0'),
}
}
fn peek(&mut self) -> Result<char> {
let c = self.chars.peek().copied();
self.filter_char(c, false)
}
fn peek_n(&mut self, n: usize) -> Option<char> {
let chars = &self.src[(self.pos.idx as usize)..];
chars.chars().nth(n)
}
fn next(&mut self) -> Result<char> {
let c = self.chars.next();
self.filter_char(c, true)
}
fn and_peek(&mut self) -> Result<char> {
self.next()?;
self.peek()
}
fn emit(&self, kind: TokenKind) -> Result<Token<'s>> {
let span = Span::new(self.start_pos, self.pos);
Ok(Token {
span,
content: span.of(self.src),
kind,
})
}
fn and_emit(&mut self, kind: TokenKind) -> Result<Token<'s>> {
self.next()?;
self.emit(kind)
}
fn unexpected(&mut self) -> Result<Token<'s>> {
let c = self.peek()?;
let span = Span::new(self.pos, self.pos.advance(c));
let msg = match c {
'\0' => "unexpected end of file".to_owned(),
'\n' => "unexpected newline character".to_owned(),
'\t' => "unexpected tab character".to_owned(),
'\r' => "unexpected return character".to_owned(),
c => format!("unexpected character {c}"),
};
Err(ParserError { span, msg })
}
fn next_ident(&mut self) -> Result<Token<'s>> {
self.next()?;
while is_xid_continue(self.peek()?) {
self.next()?;
}
let kind = match Span::new(self.start_pos, self.pos).of(self.src) {
"and" => K::And,
"break" => K::Break,
"catch" => K::Catch,
"continue" => K::Continue,
"do" => K::Do,
"elif" => K::Elif,
"else" => K::Else,
"end" => K::End,
"false" => K::False,
"fn" => K::Fn,
"for" => K::For,
"global" => K::Global,
"if" => K::If,
"in" => K::In,
"nil" => K::Nil,
"not" => K::Not,
"or" => K::Or,
"return" => K::Return,
"then" => K::Then,
"true" => K::True,
"try" => K::Try,
"var" => K::Var,
"while" => K::While,
_ => K::Identifier,
};
self.emit(kind)
}
fn next_int_base(&mut self, radix: u32) -> Result<Token<'s>> {
loop {
let c = self.peek()?;
if c == '_' || c.is_digit(radix) {
self.next()?;
} else if is_xid_start(c) {
return self.unexpected()
} else {
return self.emit(K::Integer)
}
}
}
fn next_imag(&mut self) -> Result<Token<'s>> {
self.next()?;
if is_xid_start(self.peek()?) {
self.unexpected()
} else {
self.emit(K::Imaginary)
}
}
fn next_float(&mut self, mut has_e: bool) -> Result<Token<'s>> {
while !has_e {
while matches!(self.peek()?, '_' | '0'..='9') {
self.next()?;
}
match self.peek()? {
'e' => {
self.next()?;
has_e = true;
}
'i' => return self.next_imag(),
c if is_xid_start(c) => return self.unexpected(),
_ => return self.emit(K::Float),
}
}
if matches!(self.peek()?, '+' | '-') {
self.next()?;
}
while matches!(self.peek()?, '_' | '0'..='9') {
self.next()?;
}
match self.peek()? {
'i' => self.next_imag(),
c if is_xid_start(c) => self.unexpected(),
_ => self.emit(K::Float),
}
}
fn next_number(&mut self) -> Result<Token<'s>> {
if self.next()? == '0' {
while self.peek()? == '_' {
self.next()?;
}
match self.peek()? {
'x' => {
self.next()?;
return self.next_int_base(16)
}
'o' => {
self.next()?;
return self.next_int_base(8)
}
's' => {
self.next()?;
return self.next_int_base(6)
}
'b' => {
self.next()?;
return self.next_int_base(2)
}
'0'..='9' | '.' | 'e' | 'i' => (),
c if is_xid_start(c) => return self.unexpected(),
_ => return self.emit(K::Integer),
}
}
while matches!(self.peek()?, '_' | '0'..='9') {
self.next()?;
}
match self.peek()? {
'r' => todo!("arbitrary radix integer literals"),
'e' => {
self.next()?;
self.next_float(true)
}
'i' => self.next_imag(),
'.' => {
if self.peek_n(1) == Some('.') {
self.emit(K::Integer)
} else {
self.next()?;
self.next_float(false)
}
}
c if is_xid_start(c) => self.unexpected(),
_ => self.emit(K::Integer),
}
}
fn next_string(&mut self) -> Result<Token<'s>> {
let double_quote = self.next()? == '"';
loop {
match self.next()? {
'\0' => return self.unexpected(),
'"' if double_quote => break,
'\'' if !double_quote => break,
'\\' if double_quote => {
self.next()?;
}
_ => (),
}
}
self.emit(K::String)
}
fn next_symbol(&mut self) -> Result<Token<'s>> {
if matches!(self.peek()?, '\'' | '"') {
self.next_string()?;
} else {
self.next_ident()?;
}
self.emit(K::Symbol)
}
fn line_comment(&mut self) -> Result<Token<'s>> {
while !matches!(self.peek()?, '\0' | '\n') {
self.next()?;
}
self.next_token()
}
fn next_token(&mut self) -> Result<Token<'s>> {
while matches!(self.peek()?, ' ' | '\t' | '\r') {
self.next()?;
}
self.start_pos = self.pos;
match self.peek()? {
// misc
'\0' => self.emit(K::Eof),
';' | '\n' => self.and_emit(K::LineSeparator),
'(' => self.and_emit(K::LParen),
')' => self.and_emit(K::RParen),
'{' => self.and_emit(K::LBrace),
'}' => self.and_emit(K::RBrace),
'[' => self.and_emit(K::LBrack),
']' => self.and_emit(K::RBrack),
',' => self.and_emit(K::Comma),
'$' => self.and_emit(K::Dollar),
'|' => self.and_emit(K::Pipe),
'\\' => match self.and_peek()? {
'\n' => {
self.next()?;
self.next_token()
}
_ => self.emit(K::Backslash),
},
// arithmetic
'+' => match self.and_peek()? {
'+' => match self.and_peek()? {
'=' => self.and_emit(K::PlusPlusEqual),
_ => self.emit(K::PlusPlus),
},
'=' => self.and_emit(K::PlusEqual),
_ => self.emit(K::Plus),
},
'-' => match self.and_peek()? {
'-' => self.line_comment(),
'=' => self.and_emit(K::MinusEqual),
'>' => self.and_emit(K::Arrow),
_ => self.emit(K::Minus),
},
'*' => match self.and_peek()? {
'=' => self.and_emit(K::StarEqual),
_ => self.emit(K::Star),
},
'/' => match self.and_peek()? {
'/' => match self.and_peek()? {
'=' => self.and_emit(K::SlashSlashEqual),
_ => self.emit(K::SlashSlash),
},
'=' => self.and_emit(K::SlashEqual),
_ => self.emit(K::Slash),
},
'%' => match self.and_peek()? {
'=' => self.and_emit(K::PercentEqual),
_ => self.emit(K::Percent),
},
'^' => match self.and_peek()? {
'=' => self.and_emit(K::CaretEqual),
_ => self.emit(K::Caret),
},
// logical
'#' => match self.and_peek()? {
'&' => match self.and_peek()? {
'=' => self.and_emit(K::HashAmperEqual),
_ => self.emit(K::HashAmper),
},
'^' => match self.and_peek()? {
'=' => self.and_emit(K::HashCaretEqual),
_ => self.emit(K::HashCaret),
},
'|' => match self.and_peek()? {
'=' => self.and_emit(K::HashPipeEqual),
_ => self.emit(K::HashPipe),
},
'!' => self.line_comment(),
_ => self.unexpected(),
},
// lists
'!' => match self.and_peek()? {
'=' => self.and_emit(K::BangEqual),
_ => self.emit(K::Bang),
},
'&' => match self.and_peek()? {
'=' => self.and_emit(K::AmperEqual),
_ => self.emit(K::Amper),
},
// comparison
'<' => match self.and_peek()? {
'<' => match self.and_peek()? {
'=' => self.and_emit(K::LessLessEqual),
_ => self.emit(K::LessLess),
},
'=' => self.and_emit(K::LessEqual),
_ => self.emit(K::Less),
},
'>' => match self.and_peek()? {
'>' => match self.and_peek()? {
'=' => self.and_emit(K::GreaterGreaterEqual),
_ => self.emit(K::GreaterGreater),
},
'=' => self.and_emit(K::GreaterEqual),
_ => self.emit(K::Greater),
},
'=' => match self.and_peek()? {
'=' => self.and_emit(K::EqualEqual),
_ => self.emit(K::Equal),
},
// range
'.' => match self.and_peek()? {
'.' => match self.and_peek()? {
'*' => self.and_emit(K::DotDotStar),
'=' => self.and_emit(K::DotDotEqual),
_ => self.emit(K::DotDot),
},
_ => self.emit(K::Dot),
},
':' => match self.and_peek()? {
c if is_xid_start(c) || c == '"' || c == '\'' => self.next_symbol(),
_ => self.emit(K::Colon),
},
'0'..='9' => self.next_number(),
c if is_xid_start(c) => self.next_ident(),
'"' | '\'' => self.next_string(),
_ => self.unexpected(),
}
}
pub fn tokens(mut self) -> Result<Vec<Token<'s>>> {
let mut res = Vec::new();
loop {
let t = self.next_token()?;
let k = t.kind;
res.push(t);
if k == TokenKind::Eof {
return Ok(res)
}
}
}
}
impl<'s> Iterator for Lexer<'s> {
type Item = Result<Token<'s>>;
fn next(&mut self) -> Option<Self::Item> {
Some(self.next_token())
}
}

View file

@ -1,14 +0,0 @@
pub mod ast;
mod lexer;
pub use lexer::*;
#[expect(clippy::module_inception)]
mod parser;
pub use parser::*;
mod pos;
pub use pos::*;
mod util;
pub use util::*;

View file

@ -1,663 +0,0 @@
use std::iter::Peekable;
use crate::{
symbol::{Symbol, SYM_DOLLAR_SIGN},
value::Value,
};
use super::{
ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, UnaryOp},
parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span,
SpanParserError, Token, TokenKind,
};
use num_complex::Complex64;
use ExprKind as E;
use TokenKind as T;
type Result<T> = std::result::Result<T, ParserError>;
macro_rules! expect {
($self:expr, $($t:tt)*) => {{
let t = $self.next()?;
match t.kind {
$($t)* => t,
e => return Err(ParserError { span: t.span, msg: expect_inner!(e, $($t)*) })
}
}};
}
macro_rules! expect_inner {
($e:expr, $($tok:path)|*) => {
{
let mut s = format!("unexpected token {}, expected ", $e.name())
+ $($tok.name() + ", " +)* "";
s.truncate(s.len() - 2);
s
}
};
}
macro_rules! try_next {
($self:expr, $pat:pat) => {{
let t = $self.peek()?;
match t.kind {
$pat => Some($self.next()?),
_ => None,
}
}};
}
macro_rules! throw {
($span:expr, $($t:tt)*) => {
return Err(ParserError { span: $span, msg: format!($($t)*) })
};
}
impl TokenKind {
pub fn assign_op(self) -> Option<Option<BinaryOp>> {
Some(match self {
T::PlusPlusEqual => Some(BinaryOp::Concat),
T::AmperEqual => Some(BinaryOp::Append),
T::HashPipeEqual => Some(BinaryOp::BitOr),
T::HashCaretEqual => Some(BinaryOp::BitXor),
T::HashAmperEqual => Some(BinaryOp::BitAnd),
T::LessLessEqual => Some(BinaryOp::Shl),
T::GreaterGreaterEqual => Some(BinaryOp::Shr),
T::PlusEqual => Some(BinaryOp::Add),
T::MinusEqual => Some(BinaryOp::Sub),
T::StarEqual => Some(BinaryOp::Mul),
T::SlashEqual => Some(BinaryOp::Div),
T::SlashSlashEqual => Some(BinaryOp::IntDiv),
T::PercentEqual => Some(BinaryOp::Mod),
T::CaretEqual => Some(BinaryOp::Pow),
T::Equal => None,
_ => return None,
})
}
pub fn binary_op(self) -> Option<BinaryOp> {
Some(match self {
T::EqualEqual => BinaryOp::Eq,
T::BangEqual => BinaryOp::Ne,
T::Greater => BinaryOp::Gt,
T::GreaterEqual => BinaryOp::Ge,
T::Less => BinaryOp::Lt,
T::LessEqual => BinaryOp::Le,
T::PlusPlus => BinaryOp::Concat,
T::Amper => BinaryOp::Append,
T::DotDot => BinaryOp::Range,
T::DotDotEqual => BinaryOp::RangeIncl,
T::HashPipe => BinaryOp::BitOr,
T::HashCaret => BinaryOp::BitXor,
T::HashAmper => BinaryOp::BitAnd,
T::LessLess => BinaryOp::Shl,
T::GreaterGreater => BinaryOp::Shr,
T::Plus => BinaryOp::Add,
T::Minus => BinaryOp::Sub,
T::Star => BinaryOp::Mul,
T::Slash => BinaryOp::Div,
T::SlashSlash => BinaryOp::IntDiv,
T::Percent => BinaryOp::Mod,
T::Caret => BinaryOp::Pow,
_ => return None,
})
}
pub fn unary_op(self) -> Option<UnaryOp> {
match self {
T::Minus => Some(UnaryOp::Neg),
T::Not => Some(UnaryOp::Not),
_ => None,
}
}
pub fn postfix_unary_op(self) -> Option<UnaryOp> {
match self {
T::DotDotStar => Some(UnaryOp::RangeEndless),
_ => None,
}
}
}
impl UnaryOp {
pub fn precedence(self) -> u8 {
match self {
UnaryOp::Not => 0,
UnaryOp::RangeEndless => 40,
UnaryOp::Neg => 110,
}
}
}
impl BinaryOp {
pub fn precedence(self) -> (u8, u8) {
match self {
BinaryOp::Eq
| BinaryOp::Ne
| BinaryOp::Gt
| BinaryOp::Ge
| BinaryOp::Lt
| BinaryOp::Le => (10, 10),
BinaryOp::Concat => (20, 25),
BinaryOp::Append => (30, 35),
BinaryOp::Range | BinaryOp::RangeIncl => (40, 40),
BinaryOp::BitOr => (50, 55),
BinaryOp::BitXor => (60, 65),
BinaryOp::BitAnd => (70, 75),
BinaryOp::Shl | BinaryOp::Shr => (80, 85),
BinaryOp::Add | BinaryOp::Sub => (90, 95),
BinaryOp::Mul | BinaryOp::Div | BinaryOp::IntDiv | BinaryOp::Mod => (100, 105),
BinaryOp::Pow => (125, 120),
}
}
}
#[inline(always)]
fn b<T>(t: T) -> Box<T> {
Box::new(t)
}
impl TokenKind {
fn expr_first(self) -> bool {
matches!(
self,
T::Return
| T::Var | T::Global
| T::Fn | T::Not
| T::Backslash
| T::Colon | T::Minus
| T::Identifier
| T::LParen | T::LBrack
| T::LBrace | T::Dollar
| T::Do | T::If
| T::While | T::For
| T::Try | T::Integer
| T::Float | T::Imaginary
| T::String | T::Symbol
| T::True | T::False
| T::Nil
)
}
}
struct Parser<'s> {
lexer: Peekable<Lexer<'s>>,
}
impl<'s> Parser<'s> {
fn new(src: &'s str) -> Self {
Self {
lexer: Lexer::new(src).peekable(),
}
}
fn next(&mut self) -> Result<Token<'s>> {
self.lexer.next().unwrap()
}
fn peek(&mut self) -> Result<Token<'s>> {
self.lexer.peek().unwrap().clone()
}
fn parse_table_items(&mut self) -> Result<Vec<(Expr, Expr)>> {
let mut items = Vec::new();
while self.peek()?.kind.expr_first() {
let key = if let Some(id) = try_next!(self, T::Identifier) {
E::Literal(Symbol::get(id.content).into()).span(id.span)
} else {
self.parse_term_not_ident()?
};
expect!(self, T::Equal);
let value = self.parse_expr()?;
items.push((key, value));
if try_next!(self, T::Comma).is_none() {
break
}
}
Ok(items)
}
fn parse_expr_list(&mut self) -> Result<Vec<Expr>> {
let mut exprs = Vec::new();
while self.peek()?.kind.expr_first() {
exprs.push(self.parse_expr()?);
if try_next!(self, T::Comma).is_none() {
break
}
}
Ok(exprs)
}
fn parse_ident_list(&mut self) -> Result<Vec<Symbol>> {
let mut idents = Vec::new();
while let Some(tok) = try_next!(self, T::Identifier) {
idents.push(Symbol::get(tok.content));
if try_next!(self, T::Comma).is_none() {
break
}
}
Ok(idents)
}
fn parse_symbol_list(&mut self) -> Result<Vec<Symbol>> {
let mut syms = Vec::new();
while let Some(tok) = try_next!(self, T::Symbol) {
syms.push(Symbol::get(tok.content));
if try_next!(self, T::Comma).is_none() {
break
}
}
Ok(syms)
}
fn parse_catch_blocks(&mut self) -> Result<(Vec<CatchBlock>, Span)> {
let mut blocks = Vec::new();
let mut outer_span = self.peek()?.span;
loop {
let tok = expect!(self, T::Catch | T::End);
if tok.kind == T::End {
break
}
let types = match try_next!(self, T::Star) {
Some(_) => None,
None => Some(self.parse_symbol_list()?),
};
let name = match try_next!(self, T::In) {
Some(_) => Some(Symbol::get(expect!(self, T::Identifier).content)),
None => None,
};
expect!(self, T::Do);
let body = self.parse_block()?;
let span = tok.span + body.span;
blocks.push(CatchBlock {
span,
name,
types,
body,
});
outer_span += span;
}
Ok((blocks, outer_span))
}
fn parse_if_stmt_chain(&mut self) -> Result<Expr> {
let cond = self.parse_expr()?;
expect!(self, T::Then);
let body = self.parse_block()?;
let tok = expect!(self, T::End | T::Else | T::Elif);
let span = cond.span + tok.span;
match tok.kind {
T::End => Ok(E::If(b(cond), b(body), None).span(span)),
T::Else => {
let else_body = self.parse_block()?;
expect!(self, T::End);
let span = span + else_body.span;
Ok(E::If(b(cond), b(body), Some(b(else_body))).span(span))
}
T::Elif => {
let elif_body = self.parse_if_stmt_chain()?;
let span = span + elif_body.span;
Ok(E::If(b(cond), b(body), Some(b(elif_body))).span(span))
}
_ => unreachable!("parse_if_stmt_chain: guaranteed by expect!"),
}
}
fn parse_term_not_ident(&mut self) -> Result<Expr> {
let tok = self.next()?;
match tok.kind {
T::LParen => {
let e = self.parse_expr()?;
expect!(self, T::RParen);
Ok(e)
}
T::LBrack => {
let args = self.parse_expr_list()?;
let end = expect!(self, T::RBrack);
Ok(E::List(args).span(tok.span + end.span))
}
T::LBrace => {
let args = self.parse_table_items()?;
let end = expect!(self, T::RBrace);
Ok(E::Table(args).span(tok.span + end.span))
}
T::Dollar => Ok(E::Ident(*SYM_DOLLAR_SIGN).span(tok.span)),
T::Do => {
let b = self.parse_block()?;
expect!(self, T::End);
Ok(b)
}
T::If => self.parse_if_stmt_chain(),
T::While => {
let cond = self.parse_expr()?;
expect!(self, T::Do);
let body = self.parse_block()?;
let end = expect!(self, T::End);
let span = cond.span + end.span;
Ok(E::While(b(cond), b(body)).span(span))
}
T::For => {
let var = expect!(self, T::Identifier);
expect!(self, T::In);
let iter = self.parse_expr()?;
expect!(self, T::Do);
let body = self.parse_block()?;
let end = expect!(self, T::End);
let span = var.span + end.span;
Ok(E::For(Symbol::get(var.content), b(iter), b(body)).span(span))
}
T::Try => {
let body = self.parse_block()?;
let (catch, span) = self.parse_catch_blocks()?;
Ok(E::Try(b(body), catch).span(tok.span + span))
}
T::Integer => {
let n = parse_int_literal(tok.content).span_err(tok.span)?;
Ok(E::Literal(n.into()).span(tok.span))
}
T::Float => {
let x = parse_float(tok.content).span_err(tok.span)?;
Ok(E::Literal(x.into()).span(tok.span))
}
T::Imaginary => {
let x = parse_float(&tok.content[..tok.content.len() - 1])
.span_err(tok.span)?;
Ok(E::Literal(Complex64::new(0.0, x).into()).span(tok.span))
}
T::String => {
let inner = &tok.content[1..tok.content.len() - 1];
let s = if &tok.content[..1] == "\"" {
parse_str_escapes(inner).span_err(tok.span)?
} else {
inner.into()
};
Ok(E::Literal(s.into()).span(tok.span))
}
T::Symbol => {
let inner = &tok.content[1..];
let s = match inner.chars().next() {
Some('\'') => Symbol::get(&inner[1..inner.len() - 1]),
Some('\"') => Symbol::get(
&parse_str_escapes(&inner[1..inner.len() - 1])
.span_err(tok.span)?,
),
_ => Symbol::get(inner),
};
Ok(E::Literal(s.into()).span(tok.span))
}
T::True => Ok(E::Literal(Value::Bool(true)).span(tok.span)),
T::False => Ok(E::Literal(Value::Bool(false)).span(tok.span)),
T::Nil => Ok(E::Literal(Value::Nil).span(tok.span)),
t => throw!(
tok.span,
"unexpected token {}, expected expression",
t.name()
),
}
}
fn parse_term(&mut self) -> Result<Expr> {
if let Some(tok) = try_next!(self, T::Identifier) {
Ok(E::Ident(Symbol::get(tok.content)).span(tok.span))
} else {
self.parse_term_not_ident()
}
}
fn parse_access(&mut self) -> Result<Expr> {
let mut lhs = self.parse_term()?;
loop {
let tok = try_next!(self, T::LParen | T::LBrack | T::Arrow | T::Dot);
match tok.map(|t| t.kind) {
Some(T::LParen) => {
let args = self.parse_expr_list()?;
let end = expect!(self, T::RParen);
let lhs_span = lhs.span;
lhs = E::FnCall(b(lhs), args).span(lhs_span + end.span);
}
Some(T::LBrack) => {
let idx = self.parse_expr()?;
let end = expect!(self, T::RBrack);
let lhs_span = lhs.span;
lhs = E::Index(b(lhs), b(idx)).span(lhs_span + end.span);
}
Some(T::Arrow) => {
let field = expect!(self, T::Identifier);
let symbol = Symbol::get(field.content);
expect!(self, T::LParen);
let args = self.parse_expr_list()?;
let end = expect!(self, T::RParen);
let lhs_span = lhs.span;
lhs = E::AssocFnCall(b(lhs), symbol, args).span(lhs_span + end.span);
}
Some(T::Dot) => {
let field = expect!(self, T::Identifier);
let symbol = Symbol::get(field.content);
let idx = E::Literal(symbol.into()).span(field.span);
let lhs_span = lhs.span;
lhs = E::Index(b(lhs), b(idx)).span(lhs_span + field.span);
}
None => break,
_ => unreachable!("parse_access: guaranteed by try_next!"),
}
}
Ok(lhs)
}
fn parse_precedence(&mut self, min_prec: u8) -> Result<Expr> {
let mut lhs = if let Some(op) = self.peek()?.kind.unary_op() {
let tok = self.next()?;
let rhs = self.parse_precedence(op.precedence())?;
let span = tok.span + rhs.span;
E::UnaryOp(op, b(rhs)).span(span)
} else {
self.parse_access()?
};
let mut span = lhs.span;
loop {
let tok = self.peek()?;
if let Some(op) = tok.kind.postfix_unary_op() {
if op.precedence() < min_prec {
break
}
self.next()?;
span += tok.span;
lhs = E::UnaryOp(op, b(lhs)).span(span);
continue
};
let Some(op) = tok.kind.binary_op() else {
break
};
let (lp, rp) = op.precedence();
if lp < min_prec {
break
}
self.next()?;
let rhs = self.parse_precedence(rp)?;
span += rhs.span;
lhs = E::BinaryOp(op, Box::new(lhs), Box::new(rhs)).span(span);
}
Ok(lhs)
}
fn parse_lambda(&mut self) -> Result<Expr> {
let tok = try_next!(self, T::Backslash | T::Colon);
match tok {
Some(Token {
kind: T::Backslash,
span,
..
}) => {
let args = self.parse_ident_list()?;
expect!(self, T::Arrow);
let body = self.parse_lambda()?;
let body_span = body.span;
Ok(E::Lambda(args, b(body)).span(span + body_span))
}
Some(Token {
kind: T::Colon,
span,
..
}) => {
let args = vec![*SYM_DOLLAR_SIGN];
let body = self.parse_lambda()?;
let body_span = body.span;
Ok(E::Lambda(args, b(body)).span(span + body_span))
}
None => self.parse_precedence(0),
_ => unreachable!("parse_lambda: guaranteed by try_next!"),
}
}
fn parse_pipeline(&mut self) -> Result<Expr> {
let mut lhs = self.parse_lambda()?;
let mut span = lhs.span;
while try_next!(self, T::Pipe).is_some() {
let rhs = self.parse_lambda()?;
span += rhs.span;
lhs = E::Pipe(b(lhs), b(rhs)).span(span);
}
Ok(lhs)
}
fn parse_not(&mut self) -> Result<Expr> {
if let Some(tok) = try_next!(self, T::Not) {
let expr = self.parse_not()?;
let span = tok.span + expr.span;
Ok(E::UnaryOp(UnaryOp::Not, b(expr)).span(span))
} else {
self.parse_pipeline()
}
}
fn parse_and(&mut self) -> Result<Expr> {
let mut lhs = self.parse_not()?;
let mut span = lhs.span;
while try_next!(self, T::And).is_some() {
let rhs = self.parse_not()?;
span += rhs.span;
lhs = E::And(b(lhs), b(rhs)).span(span);
}
Ok(lhs)
}
fn parse_or(&mut self) -> Result<Expr> {
let mut lhs = self.parse_and()?;
let mut span = lhs.span;
while try_next!(self, T::Or).is_some() {
let rhs = self.parse_and()?;
span += rhs.span;
lhs = E::Or(b(lhs), b(rhs)).span(span);
}
Ok(lhs)
}
fn parse_assign(&mut self) -> Result<Expr> {
let lhs = self.parse_or()?;
let lhs_span = lhs.span;
if let Some(op) = self.peek()?.kind.assign_op() {
let Some(lval) = LValue::from_expr(lhs) else {
throw!(lhs_span, "invalid lvalue for assingment")
};
self.next()?;
let rhs = self.parse_decl()?;
let rhs_span = rhs.span;
Ok(E::Assign(op, b(lval), b(rhs)).span(lhs_span + rhs_span))
} else {
Ok(lhs)
}
}
fn parse_var_decl(&mut self) -> Result<Expr> {
let first = expect!(self, T::Var | T::Global);
let name = expect!(self, T::Identifier);
expect!(self, T::Equal);
let val = self.parse_decl()?;
let val_span = val.span;
let kind = if first.kind == T::Global {
E::AssignGlobal
} else {
E::AssignVar
};
Ok(kind(Symbol::get(name.content), b(val)).span(first.span + val_span))
}
fn parse_fn_decl(&mut self) -> Result<Expr> {
let tok_fn = expect!(self, T::Fn);
let name = try_next!(self, T::Identifier).map(|t| Symbol::get(t.content));
expect!(self, T::LParen);
let args = self.parse_ident_list()?;
expect!(self, T::RParen);
match expect!(self, T::Do | T::Equal).kind {
T::Do => {
let content = self.parse_block()?;
let end = expect!(self, T::End);
Ok(E::FnDef(name, args, b(content)).span(tok_fn.span + end.span))
}
T::Equal => {
let content = self.parse_expr()?;
let span = tok_fn.span + content.span;
Ok(E::FnDef(name, args, b(content)).span(span))
}
_ => unreachable!("parse_fn_decl: guaranteed by try_next!"),
}
}
fn parse_decl(&mut self) -> Result<Expr> {
match self.peek()?.kind {
T::Var | T::Global => self.parse_var_decl(),
T::Fn => self.parse_fn_decl(),
_ => self.parse_assign(),
}
}
fn parse_expr(&mut self) -> Result<Expr> {
if let Some(tok) = try_next!(self, T::Return) {
let expr = self.parse_decl()?;
let span = expr.span;
Ok(E::Return(b(expr)).span(tok.span + span))
} else {
self.parse_decl()
}
}
fn parse_block(&mut self) -> Result<Expr> {
while try_next!(self, T::LineSeparator).is_some() {}
let mut span = self.peek()?.span;
let mut exprs = Vec::new();
while self.peek()?.kind.expr_first() {
let expr = self.parse_expr()?;
span += expr.span;
exprs.push(expr);
if try_next!(self, T::LineSeparator).is_none() {
break
}
while try_next!(self, T::LineSeparator).is_some() {}
}
Ok(E::Block(exprs).span(span))
}
fn parse(mut self) -> Result<Expr> {
let block = self.parse_block()?;
expect!(self, T::Eof);
Ok(block)
}
}
pub fn parse(src: &str) -> Result<Expr> {
Parser::new(src).parse()
}

View file

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

View file

@ -1,171 +0,0 @@
use core::fmt;
use std::num::{ParseFloatError, ParseIntError};
use thiserror::Error;
use crate::lstring::{LStr, LString};
use super::Span;
#[derive(Clone, Debug, Error)]
pub struct ParserError {
pub span: Span,
pub msg: String,
}
impl fmt::Display for ParserError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} | {}", self.span, self.msg)
}
}
pub trait SpanParserError {
type Output;
fn span_err(self, span: Span) -> Self::Output;
}
impl<T, E: std::error::Error> SpanParserError for Result<T, E> {
type Output = Result<T, ParserError>;
fn span_err(self, span: Span) -> Self::Output {
self.map_err(|e| ParserError {
span,
msg: e.to_string(),
})
}
}
#[derive(Clone, Copy, Debug, Error)]
pub enum StrEscapeError {
#[error("EOF in string escape")]
Eof,
#[error("EOF in string escape \\x")]
HexEof,
#[error("EOF in string escape \\u")]
UnicodeEof,
#[error("invalid string escape \\{0}")]
Invalid(char),
#[error("invalid hex digit '{0}' in string escape")]
InvalidHex(char),
#[error("missing brace after string escape \\u")]
MissingBrace,
#[error("invalid codepoint in string escape: {0:x}")]
InvalidCodepoint(u32),
#[error("codepoint in string escape too large")]
CodepointTooLarge,
}
pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
let mut s = LString::with_capacity(src.len());
let mut chars = src.chars();
while let Some(c) = chars.next() {
if c != '\\' {
s.push_char(c);
continue
}
let c = chars.next().ok_or(StrEscapeError::Eof)?;
match c {
'"' | '\'' | '\\' => s.push_char(c),
'0' => s.push_char('\0'),
'a' => s.push_char('\x07'),
'b' => s.push_char('\x08'),
't' => s.push_char('\t'),
'n' => s.push_char('\n'),
'v' => s.push_char('\x0b'),
'f' => s.push_char('\x0c'),
'r' => s.push_char('\r'),
'e' => s.push_char('\x1b'),
'x' => {
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
s.push_byte((n1 * 16 + n2) as u8);
}
'u' => {
let Some('{') = chars.next() else {
return Err(StrEscapeError::MissingBrace)
};
let mut n = 0_u32;
loop {
let Some(c) = chars.next() else {
return Err(StrEscapeError::UnicodeEof)
};
if c == '}' {
break
}
if n > 0x10ffff {
return Err(StrEscapeError::CodepointTooLarge)
}
n = n * 16 + c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
}
let ch = char::from_u32(n).ok_or(StrEscapeError::InvalidCodepoint(n))?;
s.push_char(ch);
}
c => return Err(StrEscapeError::Invalid(c)),
}
}
Ok(s)
}
pub fn parse_float<'a, S: Into<&'a LStr>>(f: S) -> Result<f64, ParseFloatError> {
let mut s = String::new();
for c in f.into().chars() {
if c != '_' {
s.push(c);
}
}
s.parse()
}
pub fn parse_int<'a, S: Into<&'a LStr>>(f: S, radix: u32) -> Result<i64, ParseIntError> {
let mut s = String::new();
for c in f.into().chars() {
if c != '_' {
s.push(c);
}
}
i64::from_str_radix(&s, radix)
}
pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result<i64, ParseIntError> {
let f = f.into();
match f.chars().nth(2) {
Some('x') => parse_int(&f[2..], 16),
Some('o') => parse_int(&f[2..], 8),
Some('s') => parse_int(&f[2..], 6),
Some('b') => parse_int(&f[2..], 2),
_ => parse_int(f, 10),
}
}
pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
let mut result = vec![];
let mut begin = 0;
let mut x;
if n < 0 {
result.push('-' as u32 as u8);
begin = 1;
x = (-n) as u64;
} else {
x = n as u64;
}
loop {
let m = x % (radix as u64);
x /= radix as u64;
let mut c = char::from_digit(m as u32, radix).unwrap();
if upper {
c.make_ascii_uppercase();
}
result.push(c as u8);
if x == 0 {
break
}
}
result[begin..].reverse();
LString::from(result)
}

View file

@ -0,0 +1,106 @@
use std::num::{ParseIntError, ParseFloatError};
use thiserror::Error;
use crate::lstring::{LStr, LString};
#[derive(Clone, Debug, Error)]
pub enum ParseError {
#[error("{0}")]
StrEscape(#[from] StrEscapeError),
#[error("{0}")]
Integer(#[from] ParseIntError),
#[error("{0}")]
Float(#[from] ParseFloatError),
}
#[derive(Clone, Copy, Debug, Error)]
pub enum StrEscapeError {
#[error("EOF in string escape")]
Eof,
#[error("EOF in string escape \\x")]
HexEof,
#[error("EOF in string escape \\u")]
UnicodeEof,
#[error("Invalid string escape \\{0}")]
Invalid(char),
#[error("Invalid hex digit '{0}' in string escape")]
InvalidHex(char),
#[error("Missing brace after string escape \\u")]
MissingBrace,
#[error("Invalid codepoint in string escape: {0:x}")]
InvalidCodepoint(u32),
#[error("Codepoint in string escape too large")]
CodepointTooLarge,
}
pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
let mut s = LString::with_capacity(src.len());
let mut chars = src.chars();
while let Some(c) = chars.next() {
if c != '\\' { s.push_char(c); continue }
let c = chars.next().ok_or(StrEscapeError::Eof)?;
match c {
'"' | '\'' | '\\' => s.push_char(c),
'0' => s.push_char('\0'),
'a' => s.push_char('\x07'),
'b' => s.push_char('\x08'),
't' => s.push_char('\t'),
'n' => s.push_char('\n'),
'v' => s.push_char('\x0b'),
'f' => s.push_char('\x0c'),
'r' => s.push_char('\r'),
'e' => s.push_char('\x1b'),
'x' => {
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
s.push_byte((n1 * 16 + n2) as u8);
},
'u' => {
let Some('{') = chars.next() else {
return Err(StrEscapeError::MissingBrace)
};
let mut n = 0u32;
loop {
let Some(c) = chars.next() else {
return Err(StrEscapeError::UnicodeEof)
};
if c == '}' { break }
if n >= 0x1000_0000u32 {
return Err(StrEscapeError::CodepointTooLarge)
}
n = n * 16 + c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
}
let ch = char::from_u32(n).ok_or(StrEscapeError::InvalidCodepoint(n))?;
s.push_char(ch);
},
c => return Err(StrEscapeError::Invalid(c)),
}
}
Ok(s)
}
pub fn parse_float<'a, S: Into<&'a LStr>>(f: S) -> Result<f64, ParseFloatError> {
let mut s = String::new();
for c in f.into().chars() {
if c != '_' {
s.push(c);
}
}
s.parse()
}
pub fn parse_int<'a, S: Into<&'a LStr>>(f: S, radix: u32) -> Result<i64, ParseIntError> {
let mut s = String::new();
for c in f.into().chars() {
if c != '_' {
s.push(c);
}
}
i64::from_str_radix(&s, radix)
}

View file

@ -1,19 +1,14 @@
use std::{
collections::HashMap,
sync::{Mutex, OnceLock},
};
use std::{collections::HashMap, sync::{Mutex, OnceLock}};
use lazy_static::lazy_static;
#[derive(Default)]
struct SymbolTable {
names: Vec<&'static LStr>,
values: HashMap<&'static LStr, Symbol>,
values: HashMap<&'static LStr, Symbol>
}
lazy_static! {
pub static ref SYM_SELF: Symbol = symbol!(self);
pub static ref SYM_DOLLAR_SIGN: Symbol = symbol!("$");
pub static ref SYM_NIL: Symbol = symbol!(nil);
pub static ref SYM_BOOL: Symbol = symbol!(bool);
pub static ref SYM_SYMBOL: Symbol = symbol!(symbol);
@ -28,12 +23,14 @@ lazy_static! {
pub static ref SYM_TABLE: Symbol = symbol!(table);
pub static ref SYM_FUNCTION: Symbol = symbol!(function);
pub static ref SYM_NATIVE_FUNC: Symbol = symbol!(native_func);
pub static ref SYM_END_ITERATION: Symbol = symbol!(end_iteration);
pub static ref SYM_TYPE: Symbol = symbol!(type);
pub static ref SYM_MSG: Symbol = symbol!(msg);
pub static ref SYM_DATA: Symbol = symbol!(data);
pub static ref SYM_TYPE_ERROR: Symbol = symbol!(type_error);
pub static ref SYM_VALUE_ERROR: Symbol = symbol!(value_error);
pub static ref SYM_NAME_ERROR: Symbol = symbol!(name_error);
pub static ref SYM_INDEX_ERROR: Symbol = symbol!(index_error);
pub static ref SYM_HASH_ERROR: Symbol = symbol!(hash_error);
@ -109,11 +106,7 @@ impl Symbol {
/// If the mutex storing the symbol table is poisoned
pub fn name(self) -> &'static LStr {
let table = get_table().lock().expect("couldn't lock symbol table");
table
.names
.get(self.0 as usize)
.copied()
.expect("symbol does not exist")
table.names.get(self.0 as usize).copied().expect("symbol does not exist")
}
}
@ -130,3 +123,4 @@ macro_rules! symbol {
pub use symbol;
use crate::lstring::{LStr, LString};

View file

@ -1,35 +1,27 @@
use std::{cell::RefCell, rc::Rc};
use std::rc::Rc;
use crate::{chunk::Chunk, exception::Result, symbol::Symbol, Vm};
use crate::{chunk::Chunk, Vm, exception::Result};
use super::{CellValue, Value};
use super::Value;
#[derive(Clone, Copy, Debug, Default)]
pub struct FuncAttrs {
pub arity: usize,
pub name: Option<Symbol>,
pub variadic: bool,
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct Function {
pub attrs: FuncAttrs,
pub chunk: Rc<Chunk>,
pub state: Box<[CellValue]>,
}
impl Function {
pub fn new(chunk: Rc<Chunk>, attrs: FuncAttrs, closes: usize) -> Self {
let v = Rc::new(RefCell::new(Value::Nil));
let state = std::iter::repeat(v).take(closes).collect();
Self::from_parts(chunk, attrs, state)
}
pub fn from_parts(chunk: Rc<Chunk>, attrs: FuncAttrs, state: Box<[CellValue]>) -> Self {
Self {
chunk,
attrs,
state,
pub fn new(chunk: Rc<Chunk>, arity: usize) -> Self {
Self { chunk, attrs: FuncAttrs { arity, variadic: false } }
}
pub fn new_variadic(chunk: Rc<Chunk>, arity: usize) -> Self {
Self { chunk, attrs: FuncAttrs { arity, variadic: true } }
}
}
@ -41,21 +33,11 @@ pub struct NativeFunc {
}
impl NativeFunc {
pub fn new(func: FnNative, arity: usize, name: Symbol) -> Self {
Self {
func,
attrs: FuncAttrs {
arity,
name: Some(name),
},
}
}
pub fn new_anon(func: FnNative, arity: usize) -> Self {
Self {
func,
attrs: FuncAttrs { arity, name: None },
pub fn new(func: FnNative, arity: usize) -> Self {
Self { func, attrs: FuncAttrs { arity, variadic: false } }
}
pub fn new_variadic(func: FnNative, arity: usize) -> Self {
Self { func, attrs: FuncAttrs { arity, variadic: true } }
}
}
@ -67,11 +49,11 @@ impl std::fmt::Debug for NativeFunc {
}
}
pub fn disasm_recursive(
f: &Rc<Function>,
w: &mut impl std::io::Write,
) -> std::io::Result<()> {
writeln!(w, "{} ({})", Value::Function(f.clone()), f.attrs.arity)?;
pub fn disasm_recursive(f: &Rc<Function>, w: &mut impl std::io::Write) -> std::io::Result<()> {
writeln!(w, "{} ({}{})",
Value::Function(f.clone()),
f.attrs.arity,
if f.attrs.variadic { ".." } else { "" })?;
if !f.chunk.consts.is_empty() {
writeln!(w, "constants")?;
for (i, c) in f.chunk.consts.iter().enumerate() {
@ -92,9 +74,7 @@ pub fn disasm_recursive(
if let Some(types) = &catch.types {
write!(w, "{:04} [", catch.addr)?;
for (i, ty) in types.iter().enumerate() {
if i != 0 {
write!(w, ", ")?;
}
if i != 0 { write!(w, ", ")?; }
write!(w, "{}", ty.name())?;
}
writeln!(w, "]")?;
@ -117,3 +97,4 @@ pub fn disasm_recursive(
}
Ok(())
}

View file

@ -1,11 +1,6 @@
use crate::{
exception::{throw, Result},
symbol::{symbol, SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR},
value::function::NativeFunc,
vmcalliter, Vm,
};
use crate::{symbol::{SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR}, value::function::NativeFunc, vmcalliter, Vm, exception::{Result, throw}};
use super::{range::RangeType, Value};
use super::{Value, range::RangeType};
impl Value {
pub fn index(&self, idx: Self) -> Result<Self> {
@ -16,96 +11,73 @@ impl Value {
if i >= 0 && (i as usize) < l.len() {
Ok(l[i as usize].clone())
} else {
throw!(
*SYM_INDEX_ERROR,
"index {i} out of bounds for list of length {}",
l.len()
)
}
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
}
},
(V::Range(r), V::Int(i)) => {
if i >= 0
&& (r.ty == RangeType::Endless
|| i < r.stop || (r.ty == RangeType::Closed && i == r.stop))
{
if i >= 0 && (
r.ty == RangeType::Endless
|| i < r.stop
|| (r.ty == RangeType::Closed && i == r.stop)
) {
Ok((r.start + i).into())
} else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
}
}
},
(V::Table(t), i) if i.hashable() => {
let t = t.borrow();
let i = i.try_into()?;
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
}
},
(V::String(s), V::Range(r)) => {
let slen = s.len();
match r.ty {
RangeType::Open => {
if r.start < 0 || r.start > slen as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
}
if r.stop < 0 || r.stop > slen as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
}
Ok(s[r.start as usize..r.stop as usize].into())
}
},
RangeType::Closed => {
if r.start < 0 || r.start > slen as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
}
if r.stop < 0 || r.stop >= slen as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
}
Ok(s[r.start as usize..=r.stop as usize].into())
}
},
RangeType::Endless => {
if r.start < 0 || r.start > slen as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
}
Ok(s[r.start as usize..].into())
},
}
}
}
(col, idx) => {
if let Ok(ii) = idx.clone().to_iter_function() {
},
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() {
let col = col.clone();
let func = move |vm: &mut Vm, _| match vmcalliter!(vm; ii.clone())? {
let func = move |vm: &mut Vm, _| {
match vmcalliter!(vm; ii.clone())? {
Some(i) => col.index(i),
None => Ok(Value::from(*SYM_END_ITERATION)),
}
};
Ok(NativeFunc::new(Box::new(func), 0, symbol!("index...")).into())
Ok(NativeFunc::new(Box::new(func), 0).into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
}
}
}
}
pub fn store_index(&self, vm: &mut Vm, idx: Self, val: Self) -> Result<()> {
use Value as V;
@ -116,19 +88,15 @@ impl Value {
l[i as usize] = val;
Ok(())
} else {
throw!(
*SYM_INDEX_ERROR,
"index {i} out of bounds for list of length {}",
l.len()
)
}
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
}
},
(V::Table(t), i) if i.hashable() => {
let mut t = t.borrow_mut();
let i = i.try_into()?;
t.insert(i, val);
Ok(())
}
},
(V::List(t), V::Range(r)) => {
let iter = val.to_iter_function()?;
let mut vals = Vec::new();
@ -139,78 +107,53 @@ impl Value {
match r.ty {
RangeType::Open => {
if r.start < 0 || r.start > tm.len() as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
}
if r.stop < 0 || r.stop > tm.len() as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
}
let end = tm.split_off(r.stop as usize);
tm.truncate(r.start as usize);
tm.extend(vals.into_iter().chain(end));
}
},
RangeType::Closed => {
if r.start < 0 || r.start > tm.len() as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
}
if r.stop < 0 || r.stop >= tm.len() as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
}
let end = tm.split_off(r.stop as usize + 1);
tm.truncate(r.start as usize);
tm.extend(vals.into_iter().chain(end));
}
},
RangeType::Endless => {
if r.start < 0 || r.start > tm.len() as i64 {
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}",
r.stop,
tm.len()
)
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
}
tm.truncate(r.start as usize);
tm.extend(vals);
}
},
}
Ok(())
}
(col, idx) => {
if let Ok(ii) = idx.clone().to_iter_function() {
},
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() {
let val = val.to_iter_function()?;
while let Some(i) = vmcalliter!(vm; ii.clone())? {
let Some(v) = vmcalliter!(vm; val.clone())? else {
throw!(
*SYM_INDEX_ERROR,
"not enough values provided for store index"
)
throw!(*SYM_INDEX_ERROR, "not enough values provided for store index")
};
col.store_index(vm, i, v)?;
}
Ok(())
} else {
throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}")
}
}
},
}
}
}

View file

@ -7,23 +7,19 @@ pub use num_complex::Complex64;
use num_complex::ComplexFloat;
pub use num_rational::Rational64;
use crate::exception::{throw, Exception};
use crate::lstring::{LStr, LString};
use crate::symbol::{Symbol, SYM_HASH_ERROR};
use crate::exception::{Exception, throw};
use self::{
function::{Function, NativeFunc},
range::{Range, RangeType},
};
use self::{range::{Range, RangeType}, function::{Function, NativeFunc}};
pub mod function;
pub mod index;
pub mod ops;
pub mod range;
pub mod index;
type RcList = Rc<RefCell<Vec<Value>>>;
type RcTable = Rc<RefCell<HashMap<HashValue, Value>>>;
pub type CellValue = Rc<RefCell<Value>>;
#[derive(Clone, Debug, Default)]
pub enum Value {
@ -52,12 +48,7 @@ pub trait NativeValue: std::fmt::Debug + Any {
fn get_type(&self) -> Symbol;
fn as_any(&self) -> &dyn Any;
fn to_lstring(
&self,
w: &mut LString,
_repr: bool,
_recur: &mut Vec<*const ()>,
) -> io::Result<()> {
fn to_lstring(&self, w: &mut LString, _repr: bool) -> io::Result<()> {
w.extend(b"<native value>");
Ok(())
}
@ -71,11 +62,7 @@ pub trait NativeValue: std::fmt::Debug + Any {
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = if f.alternate() {
Cow::Owned(self.repr())
} else {
self.str()
};
let s = if f.alternate() { Cow::Owned(self.repr()) } else { self.str() };
write!(f, "{s}")
}
}
@ -93,12 +80,7 @@ impl Value {
table.into()
}
pub fn write_to_lstring(
&self,
w: &mut LString,
repr: bool,
recur: &mut Vec<*const ()>,
) -> io::Result<()> {
pub fn write_to_lstring(&self, w: &mut LString, repr: bool) -> io::Result<()> {
use std::io::Write;
match self {
Self::Nil => write!(w, "nil"),
@ -112,7 +94,7 @@ impl Value {
write!(w, "{name:?}")?;
}
Ok(())
}
},
Self::Range(r) => match r.ty {
RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
@ -127,122 +109,64 @@ impl Value {
w.push_byte(b'+');
}
write!(w, "{:?}i", z.im())
}
},
Self::Cell(v) if repr => {
if recur.contains(&(v.as_ptr() as _)) {
return w.write_all(b"cell(...)")
}
w.write_all(b"cell(")?;
recur.push(v.as_ptr() as _);
v.borrow().write_to_lstring(w, repr, recur)?;
recur.pop();
v.borrow().write_to_lstring(w, repr)?;
w.write_all(b")")
}
Self::Cell(v) => {
if recur.contains(&(v.as_ptr() as _)) {
return w.write_all(b"cell(...)")
}
recur.push(v.as_ptr() as _);
v.borrow().write_to_lstring(w, true, recur)?;
recur.pop();
Ok(())
}
},
Self::Cell(v) => v.borrow().write_to_lstring(w, false),
Self::String(s) if repr => write!(w, "{s:?}"),
Self::String(s) => w.write_all(s.as_bytes()),
Self::List(l) => {
if recur.contains(&(l.as_ptr() as _)) {
return w.write_all(b"[...]")
}
w.write_all(b"[")?;
recur.push(l.as_ptr() as _);
for (i, item) in l.borrow().iter().enumerate() {
if i != 0 {
w.write_all(b", ")?;
}
item.write_to_lstring(w, true, recur)?;
item.write_to_lstring(w, true)?;
}
recur.pop();
w.write_all(b"]")
}
},
Self::Table(t) => {
if recur.contains(&(t.as_ptr() as _)) {
return w.write_all(b"{...}")
}
w.write_all(b"{ ")?;
recur.push(t.as_ptr() as _);
for (i, (k, v)) in t.borrow().iter().enumerate() {
if i != 0 {
w.write_all(b", ")?;
}
k.0.write_table_key_repr(w, recur)?;
k.0.write_table_key_repr(w)?;
w.write_all(b" = ")?;
v.write_to_lstring(w, true, recur)?;
v.write_to_lstring(w, true)?;
}
recur.pop();
w.write_all(b" }")
}
Self::Function(g) => {
if let Some(name) = g.attrs.name {
write!(
w,
"<function {}({}) @{:0>12x}>",
name.name(),
g.attrs.arity,
Rc::as_ptr(g) as usize
)
} else {
write!(
w,
"<anon function({}) @{:0>12x}>",
g.attrs.arity,
Rc::as_ptr(g) as usize
)
}
}
Self::NativeFunc(g) => {
if let Some(name) = g.attrs.name {
write!(
w,
"<native function {}({}) @{:0>12x}>",
name.name(),
g.attrs.arity,
Rc::as_ptr(g) as usize
)
} else {
write!(
w,
"<anon native function({}) @{:0>12x}>",
g.attrs.arity,
Rc::as_ptr(g) as usize
)
}
}
Self::Native(n) => n.to_lstring(w, repr, recur),
},
Self::Function(g)
=> write!(w, "<function {:?}>", Rc::as_ptr(g)),
Self::NativeFunc(g)
=> write!(w, "<native function {:?}>", Rc::as_ptr(g)),
Self::Native(n) => n.to_lstring(w, repr),
}
}
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) -> std::io::Result<()> {
match self {
Self::Nil | Self::Bool(_) | Self::Int(_) | Self::String(_) => {
self.write_to_lstring(w, true, recur)
}
Self::Nil | Self::Bool(_)
| Self::Int(_) | Self::String(_)
=> self.write_to_lstring(w, true),
Self::Symbol(s) => {
let name = s.name();
if name.is_identifier() {
w.push_lstr(name);
Ok(())
} else {
self.write_to_lstring(w, true, recur)
}
self.write_to_lstring(w, true)
}
},
_ => {
w.push_byte(b'(');
self.write_to_lstring(w, true, recur)?;
self.write_to_lstring(w, true)?;
w.push_byte(b')');
Ok(())
}
@ -254,18 +178,14 @@ impl Value {
Cow::Borrowed(s)
} else {
let mut s = LString::new();
let mut recur = Vec::new();
self.write_to_lstring(&mut s, false, &mut recur)
.expect("write_to_lstring failed");
self.write_to_lstring(&mut s, false).expect("write_to_lstring failed");
Cow::Owned(s)
}
}
pub fn repr(&self) -> LString {
let mut s = LString::new();
let mut recur = Vec::new();
self.write_to_lstring(&mut s, true, &mut recur)
.expect("write_to_lstring failed");
self.write_to_lstring(&mut s, true).expect("write_to_lstring failed");
s
}
@ -293,19 +213,15 @@ impl Value {
pub fn hashable(&self) -> bool {
matches!(
self,
Value::Nil
| Value::Bool(_)
| Value::Symbol(_)
| Value::Int(_)
| Value::Ratio(_)
| Value::String(_)
Value::Nil | Value::Bool(_) | Value::Symbol(_)
| Value::Int(_) | Value::Ratio(_) | Value::String(_)
)
}
pub fn downcast_native<T: NativeValue>(&self) -> Option<&T> {
match self {
Value::Native(n) => n.as_any().downcast_ref(),
_ => None,
_ => None
}
}
}
@ -326,12 +242,8 @@ impl TryFrom<Value> for HashValue {
}
impl HashValue {
pub fn into_inner(self) -> Value {
self.0
}
pub fn inner(&self) -> &Value {
&self.0
}
pub fn into_inner(self) -> Value { self.0 }
pub fn inner(&self) -> &Value { &self.0 }
}
impl Hash for HashValue {
@ -352,57 +264,39 @@ impl Hash for HashValue {
macro_rules! impl_from {
($ty:ty, $var:ident) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self {
Self::$var(value)
}
fn from(value: $ty) -> Self { Self::$var(value) }
}
};
($ty:ty, $var:ident, hash) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self {
Self::$var(value)
}
fn from(value: $ty) -> Self { Self::$var(value) }
}
impl From<$ty> for HashValue {
fn from(value: $ty) -> Self {
Self(Value::$var(value))
}
fn from(value: $ty) -> Self { Self(Value::$var(value)) }
}
};
($ty:ty, $var:ident, rc) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self {
Self::$var(Rc::new(value))
}
fn from(value: $ty) -> Self { Self::$var(Rc::new(value)) }
}
impl From<Rc<$ty>> for Value {
fn from(value: Rc<$ty>) -> Self {
Self::$var(value)
}
fn from(value: Rc<$ty>) -> Self { Self::$var(value) }
}
};
($ty:ty, $var:ident, rcref) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self {
Self::$var(Rc::new(RefCell::new(value)))
}
fn from(value: $ty) -> Self { Self::$var(Rc::new(RefCell::new(value))) }
}
impl From<RefCell<$ty>> for Value {
fn from(value: RefCell<$ty>) -> Self {
Self::$var(Rc::new(value))
}
fn from(value: RefCell<$ty>) -> Self { Self::$var(Rc::new(value)) }
}
impl From<Rc<RefCell<$ty>>> for Value {
fn from(value: Rc<RefCell<$ty>>) -> Self {
Self::$var(value)
}
fn from(value: Rc<RefCell<$ty>>) -> Self { Self::$var(value) }
}
};
($ty:ty, $var:ident, into) => {
impl From<$ty> for Value {
fn from(value: $ty) -> Self {
Self::$var(value.into())
}
fn from(value: $ty) -> Self { Self::$var(value.into()) }
}
};
}

View file

@ -1,27 +1,11 @@
use std::{
cell::RefCell,
cmp::Ordering,
ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Shl, Shr, Sub},
rc::Rc,
};
use std::{cell::RefCell, cmp::Ordering, ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Shl, Shr, Sub}, rc::Rc};
use num_complex::{Complex64, ComplexFloat};
use num_complex::Complex64;
use num_rational::Rational64;
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
use crate::{
exception::{throw, Result},
lstring::LString,
symbol::{symbol, SYM_END_ITERATION, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
value::range::RangeType,
Vm,
};
use crate::{exception::{throw, Result}, lstring::LString, symbol::{SYM_END_ITERATION, SYM_TYPE_ERROR}, value::range::RangeType, Vm};
use super::{
function::{FuncAttrs, NativeFunc},
range::Range,
HashValue, Value,
};
use super::{function::{FuncAttrs, NativeFunc}, range::Range, HashValue, Value};
pub trait RatioExt {
fn to_f64(&self) -> f64;
@ -47,15 +31,14 @@ impl Value {
Value::List(l) => l.borrow().len() > 0,
Value::Cell(v) => v.borrow().truthy(),
Value::Symbol(_)
| Value::Table(_)
| Value::Function(_)
| Value::NativeFunc(_)
Value::Symbol(_) | Value::Table(_)
| Value::Function(_) | Value::NativeFunc(_)
| Value::Native(_) => true,
}
}
}
#[allow(clippy::cast_precision_loss)]
pub fn promote(a: Value, b: Value) -> (Value, Value) {
use Value as V;
match (&a, &b) {
@ -75,313 +58,145 @@ pub fn promote(a: Value, b: Value) -> (Value, Value) {
}
}
////////////////////////
// unary arithmetic //
////////////////////////
impl Neg for Value {
type Output = Result<Self>;
fn neg(self) -> Self::Output {
use Value as V;
match self {
V::Int(x) => {
if let Some(x) = x.checked_neg() {
Ok(V::Int(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
}
}
V::Ratio(x) => {
if let Some(x) = Rational64::ZERO.checked_sub(&x) {
Ok(V::Ratio(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
}
}
V::Int(x) => Ok(V::Int(-x)),
V::Ratio(x) => Ok(V::Ratio(-x)),
V::Float(x) => Ok(V::Float(-x)),
V::Complex(x) => Ok(V::Complex(-x)),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}")
}
}
}
impl Value {
pub fn abs(self) -> Result<Self> {
use Value as V;
match self {
V::Int(x) => {
if let Some(x) = x.checked_abs() {
Ok(V::Int(x))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when finding absolute value of {self}"
)
}
}
V::Ratio(x) => {
if let Some((x, _)) = ratio_checked_absign(&x) {
Ok(V::Ratio(x))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when finding absolute value of {self}"
)
}
}
V::Float(x) => Ok(V::Float(x.abs())),
V::Complex(x) => Ok(V::Float(x.norm())),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
}
}
}
/////////////////////////
// binary arithmetic //
/////////////////////////
macro_rules! impl_value_arith {
($trait:ident, $name:ident, $checked:ident, $op:tt, $verb:literal) => {
impl $trait<Value> for Value {
impl Add<Value> for Value {
type Output = Result<Self>;
fn $name(self, rhs: Value) -> Self::Output {
fn add(self, rhs: Value) -> Self::Output {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(x), V::Int(y)) => if let Some(v) = x.$checked(y) {
Ok(V::Int(v))
} else {
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
},
(V::Ratio(x), V::Ratio(y)) => if let Some(v) = x.$checked(y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, concat!("overflow when ", $verb, "ing {} and {}"), a, b)
},
(V::Float(x), V::Float(y)) => Ok(V::Float(x $op y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x $op y)),
(l, r) => throw!(*SYM_TYPE_ERROR, concat!("cannot ", $verb, " {:#} and {:#}"), l, r)
match promote(self, rhs) {
(V::Int(x), V::Int(y)) => Ok(V::Int(x + y)),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x + y)),
(V::Float(x), V::Float(y)) => Ok(V::Float(x + y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x + y)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot add {l:#} and {r:#}")
}
}
}
};
}
impl_value_arith!(Add, add, checked_add, +, "add");
impl_value_arith!(Sub, sub, checked_sub, -, "subtract");
impl_value_arith!(Mul, mul, checked_mul, *, "multiply");
impl Sub<Value> for Value {
type Output = Result<Self>;
fn sub(self, rhs: Value) -> Self::Output {
use Value as V;
match promote(self, rhs) {
(V::Int(x), V::Int(y)) => Ok(V::Int(x - y)),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x - y)),
(V::Float(x), V::Float(y)) => Ok(V::Float(x - y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x - y)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot subtract {l:#} and {r:#}")
}
}
}
impl Mul<Value> for Value {
type Output = Result<Self>;
fn mul(self, rhs: Value) -> Self::Output {
use Value as V;
match promote(self, rhs) {
(V::Int(x), V::Int(y)) => Ok(V::Int(x * y)),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x * y)),
(V::Float(x), V::Float(y)) => Ok(V::Float(x * y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x * y)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot multiply {l:#} and {r:#}")
}
}
}
impl Div<Value> for Value {
type Output = Result<Self>;
fn div(self, rhs: Value) -> Self::Output {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer division by 0"),
(V::Int(x), V::Int(y)) => Ok(Value::Ratio((*x, *y).into())),
(V::Ratio(_), V::Ratio(r)) if r.is_zero() => {
throw!(*SYM_VALUE_ERROR, "rational division by 0")
}
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = x.checked_div(y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when dividing {a} and {b}")
}
}
match promote(self, rhs) {
(_, V::Int(0)) => throw!(*SYM_TYPE_ERROR, "integer division by 0"),
(_, V::Ratio(r)) if *r.numer() == 0 && *r.denom() != 0
=> throw!(*SYM_TYPE_ERROR, "integer division by 0"),
(V::Int(x), V::Int(y)) => Ok(V::Ratio(Rational64::new(x, y))),
(V::Ratio(x), V::Ratio(y)) => Ok(V::Ratio(x / y)),
(V::Float(x), V::Float(y)) => Ok(V::Float(x / y)),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x / y)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}"),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot divide {l:#} and {r:#}")
}
}
}
///////////////////////////////////
// modulo and integer division //
///////////////////////////////////
#[inline]
fn ratio_checked_absign(r: &Rational64) -> Option<(Rational64, Rational64)> {
let a = if r.is_negative() {
Rational64::ZERO.checked_sub(r)?
} else {
*r
};
Some((a, r.signum()))
#[allow(clippy::cast_sign_loss)]
fn ipow(n: i64, p: u64) -> Result<i64> {
match (n, p) {
(0, 0) => throw!(*SYM_TYPE_ERROR, "integer 0 raised to power 0"),
(0, _) => Ok(0),
(_, 0) => Ok(1),
(n, p) if p > u32::MAX as u64 => {
let (lo, hi) = (p as u32, (p >> 32) as u32);
let (a, b) = (n.pow(lo), n.pow(hi));
Ok(a * b.pow(0x1_0000).pow(0x1_0000))
}
(n, p) => Ok(n.pow(p as u32)),
}
}
#[inline]
fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
let (r2_abs, r2_sgn) = ratio_checked_absign(r2)?;
Some(r1.checked_div(&r2_abs)?.floor() * r2_sgn)
}
#[inline]
fn ratio_checked_rem_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational64> {
let q = ratio_checked_div_euclid(r1, r2)?;
r1.checked_sub(&r2.checked_mul(&q)?)
#[allow(clippy::cast_sign_loss)]
fn rpow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> {
Ok(match p {
0.. => (ipow(n, p as u64)?, ipow(d, p as u64)?),
_ => (ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?),
})
}
impl Value {
pub fn modulo(self, rhs: Value) -> Result<Self> {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer modulo by 0"),
(V::Int(x), V::Int(y)) => {
if let Some(v) = x.checked_rem_euclid(*y) {
Ok(V::Int(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
}
}
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "rational modulo by 0")
}
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = ratio_checked_rem_euclid(x, y) {
Ok(V::Ratio(v))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when calculating {a} modulo {b}")
}
}
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(*y))),
(V::Complex(x), V::Complex(y)) => {
let n = x / y;
let n = Complex64::new(n.re().floor(), n.im().floor());
Ok(Value::Complex(x - n * y))
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}"),
match promote(self, rhs) {
(V::Int(x), V::Int(y)) => Ok(V::Int(x.rem_euclid(y))),
(V::Ratio(_x), V::Ratio(_y)) => todo!("ratio modulo"),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.rem_euclid(y))),
(V::Complex(_x), V::Complex(_y)) => todo!("complex modulo"),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}")
}
}
pub fn int_div(self, rhs: Value) -> Result<Self> {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer divsion by 0"),
(V::Int(x), V::Int(y)) => {
if let Some(v) = x.checked_div_euclid(*y) {
Ok(V::Int(v))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when integer dividing {a} and {b}"
)
match promote(self, rhs) {
(V::Int(x), V::Int(y)) => Ok(V::Int(x.div_euclid(y))),
(V::Ratio(_x), V::Ratio(_y)) => todo!("ratio integer division"),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(y))),
(V::Complex(_x), V::Complex(_y)) => todo!("complex integer division"),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}")
}
}
(V::Ratio(_), V::Ratio(y)) if y.is_zero() => {
throw!(*SYM_VALUE_ERROR, "integer division by 0")
}
(V::Ratio(x), V::Ratio(y)) => {
if let Some(v) = ratio_checked_div_euclid(x, y) {
Ok(V::Ratio(v))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when integer dividing {a} and {b}"
)
}
}
(V::Float(x), V::Float(y)) => Ok(V::Float(x.div_euclid(*y))),
(V::Complex(x), V::Complex(y)) => {
let n = x / y;
Ok(V::from(Complex64::new(n.re().floor(), n.im().floor())))
}
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot integer divide {l:#} and {r:#}"),
}
}
}
//////////////////////
// exponentiation //
//////////////////////
#[inline]
fn ipow(n: i64, p: u64) -> Option<i64> {
match (n, p) {
(0, 0) => None,
(0, _) => Some(0),
(_, 0) => Some(1),
(1, _) => Some(1),
(-1, p) => (-1_i64).checked_pow((p % 2) as u32),
(_, p) if p > u32::MAX as u64 => None,
(n, p) => n.checked_pow(p as u32),
}
}
#[inline]
fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
match p {
i64::MIN => match (n, d) {
(0, _) => Some((0, 1)),
(1, 1) => Some((1, 1)),
(-1, 1) => Some((-1, 1)),
_ => None,
},
0.. => Some((ipow(n, p as u64)?, ipow(d, p as u64)?)),
_ => Some((ipow(d, (-p) as u64)?, ipow(n, (-p) as u64)?)),
}
}
impl Value {
pub fn pow(self, rhs: Value) -> Result<Self> {
use Value as V;
if let (V::Ratio(x), V::Int(y)) = (&self, &rhs) {
if x.is_zero() && *y == 0 {
throw!(*SYM_VALUE_ERROR, "rational zero to integer zero power")
return Ok(V::Ratio(rpow(*(*x).numer(), *(*x).denom(), *y)?.into()));
}
let Some(v) = rpow(*(*x).numer(), *(*x).denom(), *y) else {
throw!(
*SYM_VALUE_ERROR,
"overflow when raising {self} to the power {rhs}"
)
};
return Ok(V::Ratio(v.into()))
}
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(0), V::Int(0)) => {
throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power")
}
(V::Int(x), V::Int(y @ 0..)) => {
if let Some(v) = ipow(*x, *y as u64) {
Ok(V::Int(v))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when raising {a} to the power {b}"
)
}
}
(V::Int(x), V::Int(y)) => {
if let Some(v) = rpow(*x, 1, *y) {
Ok(V::Ratio(v.into()))
} else {
throw!(
*SYM_VALUE_ERROR,
"overflow when raising {a} to the power {b}"
)
}
}
(V::Ratio(x), V::Ratio(y)) => Ok(V::Float(x.to_f64().powf(y.to_f64()))),
(V::Float(x), V::Float(y)) => Ok(V::Float(x.powf(*y))),
(V::Complex(x), V::Complex(y)) => Ok(V::Complex(x.powc(*y))),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}"),
match promote(self, rhs) {
(V::Int(x), V::Int(y)) if y >= 0 => Ok(V::Int(ipow(x, y as u64)?)),
(V::Int(x), V::Int(y)) => Ok(V::Ratio(rpow(x, 1, y)?.into())),
(V::Float(x), V::Float(y))
=> Ok(V::Float(x.powf(y))),
(V::Ratio(x), V::Ratio(y))
=> Ok(V::Float(x.to_f64().powf(y.to_f64()))),
(V::Complex(x), V::Complex(y))
=> Ok(V::Complex(x.powc(y))),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot exponentiate {l:#} and {r:#}")
}
}
}
//////////////////////////
// Bitwise operations //
//////////////////////////
impl Shl<Value> for Value {
type Output = Result<Value>;
@ -389,7 +204,7 @@ impl Shl<Value> for Value {
use Value as V;
match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int(((a as u64) << b as u64) as i64)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} left by {r:#}"),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} left by {r:#}")
}
}
}
@ -401,7 +216,7 @@ impl Shr<Value> for Value {
use Value as V;
match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int((a as u64 >> b as u64) as i64)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}")
}
}
}
@ -413,7 +228,7 @@ impl BitAnd<Value> for Value {
use Value as V;
match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int(a & b)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}")
}
}
}
@ -425,7 +240,7 @@ impl BitXor<Value> for Value {
use Value as V;
match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int(a ^ b)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}")
}
}
}
@ -437,19 +252,16 @@ impl BitOr<Value> for Value {
use Value as V;
match (self, rhs) {
(V::Int(a), V::Int(b)) => Ok(Value::Int(a | b)),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}"),
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot shift {l:#} right by {r:#}")
}
}
}
/////////////////////////////
// Equality and ordering //
/////////////////////////////
impl PartialEq for Value {
#[allow(clippy::cast_precision_loss)]
fn eq(&self, other: &Self) -> bool {
use super::range::RangeType as Rty;
use Value as V;
use super::range::RangeType as Rty;
match (self, other) {
(V::Nil, V::Nil) => true,
(V::Bool(a), V::Bool(b)) => a == b,
@ -474,9 +286,8 @@ impl PartialEq for Value {
(V::Symbol(a), V::Symbol(b)) => a == b,
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
(Rty::Open, Rty::Open) | (Rty::Closed, Rty::Closed) => {
a.start == b.start && a.stop == b.stop
}
(Rty::Open, Rty::Open)
| (Rty::Closed, Rty::Closed) => a.start == b.start && a.stop == b.stop,
(Rty::Open, Rty::Closed) => a.start == b.start && a.stop == b.stop - 1,
(Rty::Closed, Rty::Open) => a.start == b.start && a.stop - 1 == b.stop,
(Rty::Endless, Rty::Endless) => a.start == b.start,
@ -490,6 +301,7 @@ impl PartialEq for Value {
}
impl PartialOrd for Value {
#[allow(clippy::cast_precision_loss)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
use Value as V;
match (self, other) {
@ -512,10 +324,6 @@ impl PartialOrd for Value {
}
}
////////////
// misc //
////////////
impl Value {
pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
match self.partial_cmp(other) {
@ -531,7 +339,7 @@ impl Value {
let mut l = l1.borrow().clone();
l.extend_from_slice(&l2.borrow());
Ok(l.into())
}
},
(V::String(s1), V::String(s2)) => {
let mut s: LString = s1.as_ref().to_owned();
s.push_lstr(s2);
@ -553,40 +361,23 @@ impl Value {
let mut l = list.borrow().clone();
l.push(val);
Ok(l.into())
}
},
lhs => throw!(*SYM_TYPE_ERROR, "cannot append to {lhs:#}"),
}
}
pub fn range(&self, other: &Self, closed: bool) -> Result<Self> {
if let (Value::Int(start), Value::Int(stop)) = (self, other) {
let ty = if closed {
RangeType::Closed
let ty = if closed { RangeType::Closed } else { RangeType::Open };
Ok(Range { start: *start, stop: *stop, ty }.into())
} else {
RangeType::Open
};
Ok(Range {
start: *start,
stop: *stop,
ty,
}
.into())
} else {
throw!(
*SYM_TYPE_ERROR,
"cannot create range between {self:#} and {other:#}"
)
throw!(*SYM_TYPE_ERROR, "cannot create range between {self:#} and {other:#}")
}
}
pub fn range_endless(&self) -> Result<Self> {
if let Value::Int(start) = self {
Ok(Range {
start: *start,
stop: 0,
ty: RangeType::Endless,
}
.into())
Ok(Range { start: *start, stop: 0, ty: RangeType::Endless }.into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot create endless range from {self:#}")
}
@ -606,7 +397,7 @@ impl Value {
pub fn iter_pack(v: Option<Self>) -> Self {
match v {
Some(v) => v,
None => Value::from(*SYM_END_ITERATION),
None => Value::from(*SYM_END_ITERATION)
}
}
@ -616,12 +407,11 @@ impl Value {
Self::Range(range) => {
let range_iter = RefCell::new(range.into_iter());
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
Ok(Value::iter_pack(
range_iter.borrow_mut().next().map(Value::from),
))
Ok(Value::iter_pack(range_iter.borrow_mut().next()
.map(Value::from)))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[range]")).into())
}
Ok(NativeFunc::new(Box::new(f), 0).into())
},
Self::String(s) => {
let byte_pos = RefCell::new(0);
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
@ -633,8 +423,8 @@ impl Value {
Ok(Value::from(*SYM_END_ITERATION))
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[string]")).into())
}
Ok(NativeFunc::new(Box::new(f), 0).into())
},
Self::List(list) => {
let idx = RefCell::new(0);
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
@ -646,18 +436,17 @@ impl Value {
Ok(Value::from(*SYM_END_ITERATION))
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[list]")).into())
}
Ok(NativeFunc::new(Box::new(f), 0).into())
},
Self::Table(table) => {
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
let keys = RefCell::new(keys.into_iter());
let f = move |_: &mut Vm, _: Vec<Value>| -> Result<Value> {
Ok(Value::iter_pack(
keys.borrow_mut().next().map(HashValue::into_inner),
))
Ok(Value::iter_pack(keys.borrow_mut().next()
.map(HashValue::into_inner)))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[table]")).into())
}
Ok(NativeFunc::new(Box::new(f), 0).into())
},
_ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"),
}
}

View file

@ -1,8 +1,7 @@
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RangeType {
Open,
Closed,
Endless,
Open, Closed, Endless,
}
#[derive(Clone, Copy, Debug)]
@ -32,7 +31,7 @@ impl Range {
impl IntoIterator for Range {
type Item = i64;
type IntoIter = Box<dyn Iterator<Item = i64>>;
type IntoIter = Box<dyn Iterator<Item=i64>>;
fn into_iter(self) -> Self::IntoIter {
match self.ty {
RangeType::Open => Box::new(self.start..self.stop),
@ -41,3 +40,5 @@ impl IntoIterator for Range {
}
}
}

View file

@ -1,28 +1,8 @@
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::{
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,
},
};
use crate::{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}};
struct TryFrame {
idx: usize,
stack_len: usize,
}
struct TryFrame { idx: usize, stack_len: usize }
struct CallFrame {
func: Rc<Function>,
@ -92,34 +72,43 @@ enum CallOutcome {
Partial(Value),
}
fn get_call_outcome(args: Vec<Value>) -> Result<CallOutcome> {
fn get_call_outcome(mut args: Vec<Value>) -> Result<CallOutcome> {
let f = &args[0];
let Some(attrs) = f.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "cannot call non-function {f:#}")
};
let argc = args.len() - 1;
match argc.cmp(&attrs.arity) {
Ordering::Equal => Ok(CallOutcome::Call(args)),
Ordering::Greater => throw!(*SYM_TYPE_ERROR, "too many arguments for function"),
Ordering::Less => {
if attrs.variadic && argc >= attrs.arity {
let vararg = args.split_off(attrs.arity + 1);
args.push(vararg.into());
Ok(CallOutcome::Call(args))
} else if argc == attrs.arity {
Ok(CallOutcome::Call(args))
} else if argc > attrs.arity {
throw!(*SYM_TYPE_ERROR, "too many arguments for function")
} else {
let remaining = attrs.arity - argc;
let f = f.clone();
let nf = move |vm: &mut Vm, inner_args: Vec<Value>| {
let mut ia = inner_args.into_iter();
ia.next();
let args: Vec<Value> = args.clone().into_iter().chain(ia).collect();
let mut args: Vec<Value> = args.clone().into_iter().chain(ia).collect();
if attrs.variadic {
let Value::List(varargs) = args.pop()
.expect("did not receive vararg") else {
panic!("did not receive vararg")
};
let varargs = Rc::unwrap_or_clone(varargs).take();
args.extend(varargs);
}
vm.call_value(f.clone(), args)
};
let nf = NativeFunc {
attrs: FuncAttrs {
arity: remaining,
name: None,
},
attrs: FuncAttrs { arity: remaining, variadic: attrs.variadic },
func: Box::new(nf),
};
Ok(CallOutcome::Partial(nf.into()))
}
}
}
impl Vm {
@ -142,9 +131,7 @@ impl Vm {
}
pub fn set_global_name<'a, S>(&mut self, name: S, val: Value)
where
S: Into<&'a LStr>,
{
where S: Into<&'a LStr> {
self.globals.insert(Symbol::get(name.into()), val);
}
@ -163,8 +150,8 @@ impl Vm {
CallOutcome::Call(args) => match value {
Value::Function(f) => self.run_function(f, args),
Value::NativeFunc(f) => (f.func)(self, args),
_ => unreachable!("already verified by calling get_call_type"),
},
_ => unreachable!("already verified by calling get_call_type")
}
}
}
@ -200,9 +187,7 @@ impl Vm {
while let Some(try_frame) = frame.try_frames.pop() {
let table = &frame.func.chunk.try_tables[try_frame.idx];
for catch in &table.catches {
if catch.types.is_none()
|| catch.types.as_ref().unwrap().contains(&exc.ty)
{
if catch.types.is_none() || catch.types.as_ref().unwrap().contains(&exc.ty) {
frame.ip = catch.addr;
frame.locals.truncate(table.local_count);
self.stack.truncate(try_frame.stack_len);
@ -236,33 +221,30 @@ impl Vm {
}
fn check_interrupt(&mut self) -> Result<()> {
if self
.interrupt
.fetch_and(false, std::sync::atomic::Ordering::Relaxed)
{
if self.interrupt.fetch_and(false, std::sync::atomic::Ordering::Relaxed) {
throw!(*SYM_INTERRUPTED)
}
Ok(())
}
fn run_instr(
&mut self,
frame: &mut CallFrame,
instr: Instruction,
) -> Result<Option<Value>> {
fn run_instr(&mut self, frame: &mut CallFrame, instr: Instruction) -> Result<Option<Value>> {
use Instruction as I;
match instr {
// do nothing
I::Nop => (),
// [] -> [locals[n]]
I::LoadLocal(n) => self.push(frame.locals[usize::from(n)].clone()),
I::LoadLocal(n)
=> self.push(frame.locals[usize::from(n)].clone()),
// [x] -> [], locals[n] = x
I::StoreLocal(n) => frame.locals[usize::from(n)] = self.pop(),
I::StoreLocal(n)
=> frame.locals[usize::from(n)] = self.pop(),
// [x] -> [], locals.push(x)
I::NewLocal => frame.locals.push(self.pop()),
I::NewLocal
=> frame.locals.push(self.pop()),
// locals.pop_n(n)
I::DropLocal(n) => frame.locals.truncate(frame.locals.len() - usize::from(n)),
I::DropLocal(n)
=> frame.locals.truncate(frame.locals.len() - usize::from(n)),
// [] -> [globals[s]]
I::LoadGlobal(s) => {
let sym = unsafe { s.to_symbol_unchecked() };
@ -271,67 +253,16 @@ impl Vm {
None => throw!(*SYM_NAME_ERROR, "undefined global {}", sym.name()),
};
self.push(v);
}
},
// [x] -> [], globals[s] = x
I::StoreGlobal(s) => {
let sym = unsafe { s.to_symbol_unchecked() };
let v = self.pop();
self.globals.insert(sym, v);
}
I::CloseOver(n) => {
let n = usize::from(n);
let v = std::mem::replace(&mut frame.locals[n], Value::Nil);
let v = v.to_cell();
frame.locals[n] = v.clone();
self.push(v);
}
I::Closure(n) => {
let f = frame.func.chunk.consts[usize::from(n)].clone();
let Value::Function(f) = f else {
panic!("attempt to build closure from non-closure constant")
};
let mut f = f.as_ref().clone();
let captured: Vec<_> = self
.pop_n(f.state.len())
.into_iter()
.map(|v| {
let Value::Cell(v) = v else {
panic!("attempt to build closure from non-cell local");
};
v
})
.collect();
f.state = captured.into_boxed_slice();
self.push(f.into());
}
I::LoadUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
self.push(v.borrow().clone());
}
I::StoreUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
*v.borrow_mut() = self.pop();
}
I::ContinueUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
self.push(Value::Cell(v));
}
I::LoadClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to load from closed non-cell local");
};
self.push(c.borrow().clone());
}
I::StoreClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to store to closed non-cell local");
};
*c.borrow_mut() = self.pop();
}
},
// [] -> [consts[n]]
I::Const(n) => self.push(frame.func.chunk.consts[usize::from(n)].clone()),
I::Const(n)
=> self.push(frame.func.chunk.consts[usize::from(n)].clone()),
// [] -> [nil]
I::Nil => self.push(Value::Nil),
// [] -> [b]
@ -340,7 +271,7 @@ impl Vm {
I::Symbol(s) => {
let sym = unsafe { Symbol::from_id_unchecked(u32::from(s)) };
self.push(Value::Symbol(sym));
}
},
// [] -> [n]
I::Int(n) => self.push(Value::Int(i64::from(n))),
// [x] -> [x,x]
@ -349,44 +280,38 @@ impl Vm {
I::DupTwo => {
self.push(self.stack[self.stack.len() - 2].clone());
self.push(self.stack[self.stack.len() - 2].clone());
}
},
// [a0,a1...an] -> []
I::Drop(n) => {
for _ in 0..u32::from(n) {
self.pop();
}
}
I::Drop(n) => for _ in 0..u32::from(n) { self.pop(); },
// [x,y] -> [y,x]
I::Swap => {
let len = self.stack.len();
self.stack.swap(len - 1, len - 2);
}
},
// [x,y] -> [y op x]
I::BinaryOp(op) => {
let b = self.pop();
let a = self.pop();
self.push(binary_op(op, a, b)?);
}
},
// [x] -> [op x]
I::UnaryOp(op) => {
let a = self.pop();
self.push(unary_op(op, a)?);
}
},
// [a0,a1...an] -.> [[a0,a1...an]]
I::NewList(n) => {
let list = self.pop_n(n as usize);
self.push(list.into());
}
},
// [l,a0,a1...an] -.> [l ++ [a0,a1...an]]
I::GrowList(n) => {
let ext = self.pop_n(n as usize);
let list = self.pop();
let Value::List(list) = list else {
panic!("not a list")
};
let Value::List(list) = list else { panic!("not a list") };
list.borrow_mut().extend(ext);
self.push(Value::List(list));
}
},
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
I::NewTable(n) => {
let mut table = HashMap::new();
@ -396,14 +321,12 @@ impl Vm {
table.insert(k.try_into()?, v);
}
self.push(table.into());
}
},
// [t,k0,v0...kn,vn] -> [t ++ {k0=v0...kn=vn}]
I::GrowTable(n) => {
let mut ext = self.pop_n(2 * n as usize);
let table = self.pop();
let Value::Table(table) = table else {
panic!("not a table")
};
let Value::Table(table) = table else { panic!("not a table") };
let mut table_ref = table.borrow_mut();
for _ in 0..n {
// can't panic: pop_n checked that ext would have len 2*n
@ -413,13 +336,13 @@ impl Vm {
}
drop(table_ref);
self.push(Value::Table(table));
}
},
// [ct, idx] -> [ct!idx]
I::Index => {
let idx = self.pop();
let ct = self.pop();
self.push(ct.index(idx)?);
}
},
// [ct, idx, v] -> [v], ct!idx = v
I::StoreIndex => {
let v = self.pop();
@ -427,31 +350,27 @@ impl Vm {
let ct = self.pop();
ct.store_index(self, idx, v.clone())?;
self.push(v);
}
},
// ip = n
I::Jump(n) => {
self.check_interrupt()?;
frame.ip = usize::from(n);
}
},
// [v] ->, [], if v then ip = n
I::JumpTrue(n) => {
if self.pop().truthy() {
I::JumpTrue(n) => if self.pop().truthy() {
self.check_interrupt()?;
frame.ip = usize::from(n);
}
}
},
// [v] ->, [], if not v then ip = n
I::JumpFalse(n) => {
if !self.pop().truthy() {
I::JumpFalse(n) => if !self.pop().truthy() {
self.check_interrupt()?;
frame.ip = usize::from(n);
}
}
},
// [v] -> [iter(v)]
I::IterBegin => {
let iter = self.pop().to_iter_function()?;
self.push(iter);
}
},
// [i,cell(v)] -> [i,v]
// [i,nil] -> [], ip = n
I::IterTest(n) => {
@ -461,19 +380,19 @@ impl Vm {
self.pop();
frame.ip = usize::from(n);
}
}
},
// try_frames.push(t, stack.len())
I::BeginTry(t) => {
let tryframe = TryFrame {
idx: usize::from(t),
stack_len: self.stack.len(),
stack_len: self.stack.len()
};
frame.try_frames.push(tryframe);
}
},
// try_frames.pop()
I::EndTry => {
frame.try_frames.pop().expect("no try to pop");
}
},
// [f,a0,a1...an] -> [f(a0,a1...an)]
I::Call(n) => {
let n = usize::from(n);
@ -513,8 +432,9 @@ impl Vm {
// before propagating exceptions
let res = res?;
self.push(res);
self.stack.push(res);
} else if let Value::Function(func) = &args[0] {
if self.call_stack.len() + 1 >= self.stack_max {
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
}
@ -525,18 +445,22 @@ impl Vm {
} else {
unreachable!("already verified by calling get_call_type");
}
}
},
// [v] -> [], return v
I::Return if frame.root => return Ok(Some(self.pop())),
I::Return if frame.root => {
return Ok(Some(self.pop()));
},
// [v] -> [], return v
I::Return => {
self.check_interrupt()?;
*frame = self.call_stack.pop().expect("no root frame");
}
},
}
Ok(None)
}
}
#[macro_export]

View file

@ -1,8 +1,52 @@
use proc_macro::TokenStream;
use syn::{parse::Parse, parse_macro_input, ItemFn, LitInt, Token};
use quote::quote;
mod native_func;
struct NativeFuncArgs {
arity: LitInt,
variadic: Option<Token![..]>,
}
impl Parse for NativeFuncArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let arity = input.parse()?;
let variadic = input.parse()?;
Ok(Self { arity, variadic })
}
}
#[proc_macro_attribute]
pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream {
native_func::native_func(input, annotated_item)
let Ok(itemfn) = syn::parse::<ItemFn>(annotated_item.clone()) else {
return annotated_item
};
let args: NativeFuncArgs = parse_macro_input!(input as NativeFuncArgs);
let visibility = itemfn.vis;
let block = itemfn.block;
let name = itemfn.sig.ident;
let inputs = itemfn.sig.inputs;
let output = itemfn.sig.output;
let arity = args.arity;
let variadic = args.variadic.is_some();
assert!(itemfn.sig.constness.is_none(), "item must not be const");
assert!(itemfn.sig.asyncness.is_none(), "item must not be async");
assert!(itemfn.sig.unsafety.is_none(), "item must not be unsafe");
assert!(itemfn.sig.abi.is_none(), "item must not contain an ABI specifier");
assert!(itemfn.sig.variadic.is_none(), "item must not be variadic");
let expanded = quote! {
#visibility fn #name() -> ::talc_lang::value::function::NativeFunc {
::talc_lang::value::function::NativeFunc {
attrs: ::talc_lang::value::function::FuncAttrs{
arity: #arity,
variadic: #variadic,
},
func: Box::new(|#inputs| #output #block)
}
}
};
TokenStream::from(expanded)
}

View file

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

View file

@ -6,8 +6,8 @@ edition = "2021"
[dependencies]
talc-lang = { path = "../talc-lang" }
talc-macros = { path = "../talc-macros" }
lazy_static = "1.5"
regex = "1.11"
lazy_static = "1.4"
regex = "1.10"
rand = { version = "0.8", optional = true }
[features]

View file

@ -1,16 +1,11 @@
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::SYM_TYPE_ERROR, throw, value::{function::NativeFunc, Value}, vmcall, Vm};
use talc_macros::native_func;
use crate::unpack_args;
pub fn load(vm: &mut Vm) {
vm.set_global_name("push", push().into());
vm.set_global_name("pop", pop().into());
@ -19,9 +14,6 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("sort", sort().into());
vm.set_global_name("sort_by", sort_by().into());
vm.set_global_name("sort_key", sort_key().into());
vm.set_global_name("pair", pair().into());
vm.set_global_name("fst", fst().into());
vm.set_global_name("snd", snd().into());
}
#[native_func(2)]
@ -41,7 +33,7 @@ pub fn pop(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "pop expected list, found {list:#}")
};
let v = list.borrow_mut().pop();
v.ok_or_else(|| exception!(*SYM_VALUE_ERROR, "attempt to pop empty list"))
v.ok_or_else(|| exception!(*SYM_TYPE_ERROR, "attempt to pop empty list"))
}
#[native_func(1)]
@ -60,10 +52,7 @@ pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match &col {
Value::List(list) => list.borrow_mut().clear(),
Value::Table(table) => table.borrow_mut().clear(),
_ => throw!(
*SYM_TYPE_ERROR,
"clear expected list or table, found {col:#}"
),
_ => throw!(*SYM_TYPE_ERROR, "clear expected list or table, found {col:#}")
}
Ok(col)
}
@ -71,10 +60,7 @@ pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<i64> {
let ord = vmcall!(vm; by, l, r)?;
let Value::Int(ord) = ord else {
throw!(
*SYM_TYPE_ERROR,
"comparison function should return an integer"
)
throw!(*SYM_TYPE_ERROR, "comparison function should return an integer")
};
Ok(ord)
}
@ -93,14 +79,14 @@ fn partition(vals: &mut [Value]) -> (usize, usize) {
vals.swap(eq, lt);
lt += 1;
eq += 1;
}
},
Some(Ordering::Greater) => {
vals.swap(eq, gt);
gt -= 1;
}
},
Some(Ordering::Equal) | None => {
eq += 1;
}
},
}
}
@ -121,14 +107,14 @@ fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, u
vals.swap(eq, lt);
lt += 1;
eq += 1;
}
},
1.. => {
vals.swap(eq, gt);
gt -= 1;
}
},
0 => {
eq += 1;
}
},
}
}
@ -138,8 +124,8 @@ fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, u
fn insertion(vals: &mut [Value]) {
for i in 0..vals.len() {
let mut j = i;
while j > 0 && vals[j - 1] > vals[j] {
vals.swap(j, j - 1);
while j > 0 && vals[j-1] > vals[j] {
vals.swap(j, j-1);
j -= 1;
}
}
@ -149,12 +135,11 @@ fn insertion_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<()> {
for i in 0..vals.len() {
let mut j = i;
while j > 0 {
let ord =
call_comparison(vm, by.clone(), vals[j - 1].clone(), vals[j].clone())?;
let ord = call_comparison(vm, by.clone(), vals[j-1].clone(), vals[j].clone())?;
if ord <= 0 {
break
break;
}
vals.swap(j, j - 1);
vals.swap(j, j-1);
j -= 1;
}
}
@ -177,7 +162,7 @@ fn sort_inner(vals: &mut [Value], by: Option<&Value>, vm: &mut Vm) -> Result<()>
None => partition(vals),
};
sort_inner(&mut vals[..lt], by, vm)?;
sort_inner(&mut vals[(gt + 1)..], by, vm)
sort_inner(&mut vals[(gt+1)..], by, vm)
}
#[native_func(1)]
@ -214,45 +199,10 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Some(Ordering::Greater) => Ok(Value::Int(1)),
Some(Ordering::Equal) => Ok(Value::Int(0)),
Some(Ordering::Less) => Ok(Value::Int(-1)),
None => throw!(
*SYM_VALUE_ERROR,
"values returned from sort key were incomparable"
),
None => throw!(*SYM_TYPE_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).into();
sort_inner(&mut list.borrow_mut(), Some(&nf), vm)?;
Ok(list.into())
}
#[native_func(2)]
pub fn pair(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
Ok(vec![x, y].into())
}
#[native_func(1)]
pub fn fst(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "fst: expected list")
};
let l = l.borrow();
let [x, _] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "fst: list must have length 2")
};
Ok(x.clone())
}
#[native_func(1)]
pub fn snd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "snd: expected list")
};
let l = l.borrow();
let [_, y] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "snd: list must have length 2")
};
Ok(y.clone())
}

View file

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

View file

@ -1,24 +1,8 @@
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 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_lang::{exception::Result, lstring::LString, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{function::NativeFunc, HashValue, NativeValue, Value}, Vm};
use talc_macros::native_func;
use lazy_static::lazy_static;
use crate::{unpack_args, SYM_IO_ERROR};
@ -26,6 +10,7 @@ type OpenOptFn = for<'a> fn(&'a mut OpenOptions, bool) -> &'a mut OpenOptions;
lazy_static! {
static ref SYM_STD_FILE: Symbol = Symbol::get("std.file");
static ref SYM_STD_PROCESS: Symbol = Symbol::get("std.process");
static ref SYM_R: Symbol = Symbol::get("r");
static ref SYM_W: Symbol = Symbol::get("w");
static ref SYM_A: Symbol = Symbol::get("a");
@ -33,6 +18,7 @@ lazy_static! {
static ref SYM_C: Symbol = Symbol::get("c");
static ref SYM_N: Symbol = Symbol::get("n");
static ref SYM_U: Symbol = Symbol::get("u");
static ref OPEN_OPT_MAP: HashMap<Symbol, OpenOptFn> = {
let mut map = HashMap::new();
map.insert(*SYM_R, OpenOptions::read as OpenOptFn);
@ -43,17 +29,21 @@ lazy_static! {
map.insert(*SYM_N, OpenOptions::create_new);
map
};
static ref SYM_STDIN: Symbol = Symbol::get("stdin");
static ref SYM_STDOUT: Symbol = Symbol::get("stdout");
static ref SYM_STDERR: Symbol = Symbol::get("stderr");
static ref SYM_CD: Symbol = Symbol::get("cd");
static ref SYM_DELENV: Symbol = Symbol::get("delenv");
static ref SYM_ENV: Symbol = Symbol::get("env");
static ref SYM_PROCESS: Symbol = Symbol::get("process");
static ref SYM_INHERIT: Symbol = Symbol::get("inherit");
static ref SYM_PIPED: Symbol = Symbol::get("piped");
static ref SYM_NULL: Symbol = Symbol::get("null");
static ref SYM_ALL: Symbol = Symbol::get("all");
}
thread_local! {
@ -79,23 +69,15 @@ thread_local! {
#[derive(Debug)]
enum BufFile {
Buffered {
r: BufReader<File>,
w: BufWriter<File>,
},
Unbuffered {
f: File,
},
Buffered { r: BufReader<File>, w: BufWriter<File> },
Unbuffered { f: File },
}
impl BufFile {
fn new(file: File, buffered: bool) -> std::io::Result<Self> {
if buffered {
let file2 = file.try_clone()?;
Ok(Self::Buffered {
r: BufReader::new(file),
w: BufWriter::new(file2),
})
Ok(Self::Buffered { r: BufReader::new(file), w: BufWriter::new(file2) })
} else {
Ok(Self::Unbuffered { f: file })
}
@ -110,17 +92,15 @@ impl BufFile {
Self::Buffered { r, .. } => {
let file = r.get_ref().try_clone()?;
Self::new(file, true)
}
Self::Unbuffered { f } => Ok(Self::Unbuffered { f: f.try_clone()? }),
},
Self::Unbuffered { f } => Ok(Self::Unbuffered { f: f.try_clone()? })
}
}
fn try_into_raw_fd(
self,
) -> std::result::Result<RawFd, IntoInnerError<BufWriter<File>>> {
fn try_into_raw_fd(self) -> std::result::Result<RawFd, IntoInnerError<BufWriter<File>>> {
match self {
BufFile::Buffered { r, w } => {
let _ = w.into_inner()?.into_raw_fd();
w.into_inner()?.into_raw_fd();
Ok(r.into_inner().into_raw_fd())
}
BufFile::Unbuffered { f } => Ok(f.into_raw_fd()),
@ -172,18 +152,9 @@ impl From<BufFile> for ValueFile {
}
impl NativeValue for ValueFile {
fn get_type(&self) -> Symbol {
*SYM_STD_FILE
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn to_lstring(
&self,
w: &mut LString,
_repr: bool,
_recur: &mut Vec<*const ()>,
) -> std::io::Result<()> {
fn get_type(&self) -> Symbol { *SYM_STD_FILE }
fn as_any(&self) -> &dyn std::any::Any { self }
fn to_lstring(&self, w: &mut LString, _repr: bool) -> std::io::Result<()> {
w.push_str("<file>");
Ok(())
}
@ -199,18 +170,9 @@ impl From<Child> for ValueProcess {
}
impl NativeValue for ValueProcess {
fn get_type(&self) -> Symbol {
*SYM_STD_PROCESS
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn to_lstring(
&self,
w: &mut LString,
_repr: bool,
_recur: &mut Vec<*const ()>,
) -> std::io::Result<()> {
fn get_type(&self) -> Symbol { *SYM_STD_PROCESS }
fn as_any(&self) -> &dyn std::any::Any { self }
fn to_lstring(&self, w: &mut LString, _repr: bool) -> std::io::Result<()> {
let id = self.0.borrow().id();
write!(w, "<process {id}>")
}
@ -239,6 +201,7 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("process_id", process_id().into());
}
#[native_func(2)]
pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, path, opts] = unpack_args!(args);
@ -255,12 +218,11 @@ pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} else {
match OPEN_OPT_MAP.get(&s) {
Some(f) => f(&mut oo, true),
None => throw!(*SYM_VALUE_ERROR, "invalid option for open"),
None => throw!(*SYM_TYPE_ERROR, "invalid option for open")
};
}
}
Value::List(l) => {
for s in l.borrow().iter() {
},
Value::List(l) => for s in l.borrow().iter() {
let Value::Symbol(s) = s else {
throw!(*SYM_TYPE_ERROR, "invalid option for open")
};
@ -269,21 +231,20 @@ pub fn open(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} else {
match OPEN_OPT_MAP.get(s) {
Some(f) => f(&mut oo, true),
None => throw!(*SYM_VALUE_ERROR, "invalid option for open"),
None => throw!(*SYM_TYPE_ERROR, "invalid option for open")
};
}
}
}
},
Value::Nil => {
oo.read(true).write(true);
}
_ => throw!(*SYM_TYPE_ERROR, "invalid option for open"),
},
_ => throw!(*SYM_TYPE_ERROR, "invalid option for open")
}
match oo.open(path.to_os_str()) {
Ok(f) => match BufFile::new(f, buffered) {
Ok(bf) => Ok(ValueFile::from(bf).into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
},
}
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
}
@ -295,10 +256,7 @@ pub fn read(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "read expected integer, got {nbytes:#}")
};
let Ok(nbytes) = usize::try_from(nbytes) else {
throw!(
*SYM_VALUE_ERROR,
"number of bytes to read must be nonnegative"
)
throw!(*SYM_TYPE_ERROR, "number of bytes to read must be nonnegative")
};
let Some(file): Option<&ValueFile> = file.downcast_native() else {
throw!(*SYM_TYPE_ERROR, "read expected file, got {file:#}")
@ -352,14 +310,14 @@ pub fn read_until(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "read_until expected string, got {end:#}")
};
if end.is_empty() {
throw!(*SYM_VALUE_ERROR, "read_until: end string must not be empty")
throw!(*SYM_TYPE_ERROR, "read_until: end string must not be empty")
}
let mut file = file.0.borrow_mut();
if let BufFile::Buffered { r, .. } = &mut *file {
match read_until_impl(r, end.as_bytes()) {
Ok(s) if s.is_empty() => Ok(Value::Nil),
Ok(s) => Ok(s.into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
Err(e) => throw!(*SYM_IO_ERROR, "{e}")
}
} else {
throw!(*SYM_TYPE_ERROR, "read_until: file must be buffered")
@ -378,10 +336,10 @@ pub fn read_line(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match r.read_until(b'\n', &mut buf) {
Ok(0) => Ok(Value::Nil),
Ok(_) => Ok(LString::from(buf).into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
Err(e) => throw!(*SYM_IO_ERROR, "{e}")
}
} else {
throw!(*SYM_VALUE_ERROR, "read_line: file must be buffered")
throw!(*SYM_TYPE_ERROR, "read_line: file must be buffered")
}
}
@ -440,13 +398,13 @@ pub fn tcp_connect(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "tcp_connect expected string, got {addr:#}")
};
let Ok(addr) = addr.to_str() else {
throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "address must be valid UTF-8")
};
match TcpStream::connect(addr) {
Ok(stream) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
Ok(bf) => Ok(ValueFile::from(bf).into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
},
}
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
}
@ -455,26 +413,16 @@ pub fn tcp_connect(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn tcp_connect_timeout(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, addr, timeout] = unpack_args!(args);
let Value::String(addr) = addr else {
throw!(
*SYM_TYPE_ERROR,
"tcp_connect_timeout expected string, got {addr:#}"
)
throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout expected string, got {addr:#}")
};
let Ok(addr) = addr.to_str() else {
throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "address must be valid UTF-8")
};
let timeout = match timeout {
Value::Int(n) if n >= 0 => Duration::from_secs(n as u64),
Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => {
Duration::from_secs_f64(n)
}
Value::Int(_) | Value::Float(_) => {
throw!(*SYM_VALUE_ERROR, "tcp_connect_timeout: invalid timeout")
}
_ => throw!(
*SYM_TYPE_ERROR,
"tcp_connect_timeout expected int or float, got {timeout:#}"
),
Value::Float(n) if n >= 0.0 && n <= Duration::MAX.as_secs_f64() => Duration::from_secs_f64(n),
Value::Int(_) | Value::Float(_) => throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout: invalid timeout"),
_ => throw!(*SYM_TYPE_ERROR, "tcp_connect_timeout expected int or float, got {timeout:#}")
};
let mut addrs = match addr.to_socket_addrs() {
Ok(addrs) => addrs,
@ -487,25 +435,26 @@ pub fn tcp_connect_timeout(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(stream) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
Ok(bf) => Ok(ValueFile::from(bf).into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
},
}
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
}
fn tcp_listen_inner(listener: TcpListener) -> Value {
let listener = RefCell::new(listener);
let func = move |_: &mut Vm, _: Vec<Value>| match listener.borrow_mut().accept() {
let func = move |_: &mut Vm, _: Vec<Value>| {
match listener.borrow_mut().accept() {
Ok((stream, addr)) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
Ok(bf) => Ok(vec![
ValueFile::from(bf).into(),
LString::from(addr.to_string()).into(),
]
.into()),
LString::from(addr.to_string()).into()
].into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
},
}
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
};
NativeFunc::new(Box::new(func), 0, symbol!("inner[tcp_listen]")).into()
NativeFunc::new(Box::new(func), 0).into()
}
#[native_func(1)]
@ -515,16 +464,18 @@ pub fn tcp_listen(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "tcp_connect expected string, got {addr:#}")
};
let Ok(addr) = addr.to_str() else {
throw!(*SYM_VALUE_ERROR, "address must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "address must be valid UTF-8")
};
match TcpListener::bind(addr) {
Ok(listener) => {
let addr = listener
.local_addr()
let addr = listener.local_addr()
.map(|a| LString::from(a.to_string()).into())
.unwrap_or(Value::Nil);
Ok(vec![tcp_listen_inner(listener), addr].into())
}
Ok(vec![
tcp_listen_inner(listener),
addr,
].into())
},
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
}
@ -548,32 +499,24 @@ fn spawn_opt_stdio(
let f: &ValueFile = opt.downcast_native().unwrap();
let bf = match f.0.borrow().try_clone() {
Ok(bf) => bf,
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
Err(e) => throw!(*SYM_IO_ERROR, "{e}")
};
let fd = match bf.try_into_raw_fd() {
Ok(fd) => fd,
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
Err(e) => throw!(*SYM_IO_ERROR, "{e}")
};
let stdio = unsafe { Stdio::from_raw_fd(fd) };
func(proc, stdio)
}
_ => throw!(
*SYM_VALUE_ERROR,
"{fname} stdio option expected :inherit, :piped, :null, or file, got {opt:#}"
),
},
_ => throw!(*SYM_TYPE_ERROR, "{fname} stdio option expected :inherit, :piped, :null, or file, got {opt:#}")
};
Ok(())
}
fn spawn_opt_cd(fname: &str, proc: &mut Command, cd: &Value) -> Result<()> {
if let Value::Nil = cd {
return Ok(())
}
if let Value::Nil = cd { return Ok(()) }
let Value::String(cd) = cd else {
throw!(
*SYM_TYPE_ERROR,
"{fname} cd option expected string, got {cd:#}"
)
throw!(*SYM_TYPE_ERROR, "{fname} cd option expected string, got {cd:#}")
};
proc.current_dir(cd.to_os_str());
Ok(())
@ -584,22 +527,16 @@ fn spawn_opt_delenv(fname: &str, proc: &mut Command, delenv: &Value) -> Result<(
Value::Nil => (),
Value::Symbol(s) if *s == *SYM_ALL => {
proc.env_clear();
}
Value::List(l) => {
for e in l.borrow().iter() {
},
Value::List(l) => for e in l.borrow().iter() {
let Value::String(e) = e else {
throw!(
*SYM_TYPE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}"
)
throw!(*SYM_TYPE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}")
};
proc.env_remove(e.to_os_str());
}
}
_ => throw!(
*SYM_VALUE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}"
),
},
_ => throw!(*SYM_TYPE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}")
}
Ok(())
}
@ -607,21 +544,15 @@ fn spawn_opt_delenv(fname: &str, proc: &mut Command, delenv: &Value) -> Result<(
fn spawn_opt_env(fname: &str, proc: &mut Command, env: &Value) -> Result<()> {
match env {
Value::Nil => (),
Value::Table(t) => {
for (k, v) in t.borrow().iter() {
Value::Table(t) => for (k, v) in t.borrow().iter() {
let Value::String(k) = k.inner() else {
throw!(
*SYM_TYPE_ERROR,
"{fname} env option expected table with string keys, got {env:#}"
)
throw!(*SYM_TYPE_ERROR,
"{fname} efromnv 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:#}"
),
},
_ => throw!(*SYM_TYPE_ERROR,
"{fname} env option expected table with string keys, got {env:#}")
}
Ok(())
}
@ -629,10 +560,7 @@ fn spawn_opt_env(fname: &str, proc: &mut Command, env: &Value) -> Result<()> {
fn spawn_inner(fname: &str, proc: &mut Command, opts: Value) -> Result<Value> {
let (i, o, e) = match opts {
Value::Native(ref n) if n.get_type() == *SYM_STD_FILE => {
throw!(
*SYM_TYPE_ERROR,
"{fname} options expected :inherit, :piped, :null, or table, got {opts:#}"
)
throw!(*SYM_TYPE_ERROR, "{fname} options expected :inherit, :piped, :null, or table, got {opts:#}")
}
Value::Table(t) => {
let t = t.borrow();
@ -649,20 +577,17 @@ fn spawn_inner(fname: &str, proc: &mut Command, opts: Value) -> Result<Value> {
spawn_opt_env(fname, proc, env)?;
}
let i = t
.get(&HashValue::from(*SYM_STDIN))
let i = t.get(&HashValue::from(*SYM_STDIN))
.cloned()
.unwrap_or_else(|| Value::from(*SYM_INHERIT));
let o = t
.get(&HashValue::from(*SYM_STDOUT))
let o = t.get(&HashValue::from(*SYM_STDOUT))
.cloned()
.unwrap_or_else(|| Value::from(*SYM_INHERIT));
let e = t
.get(&HashValue::from(*SYM_STDERR))
let e = t.get(&HashValue::from(*SYM_STDERR))
.cloned()
.unwrap_or_else(|| Value::from(*SYM_INHERIT));
(i, o, e)
}
},
v => (v.clone(), v.clone(), v),
};
@ -696,10 +621,7 @@ fn spawn_inner(fname: &str, proc: &mut Command, opts: Value) -> Result<Value> {
};
table.insert(HashValue::from(*SYM_STDERR), ValueFile::from(bf).into());
}
table.insert(
HashValue::from(*SYM_PROCESS),
ValueProcess::from(child).into(),
);
table.insert(HashValue::from(*SYM_PROCESS), ValueProcess::from(child).into());
Ok(table.into())
}
@ -710,18 +632,12 @@ pub fn spawn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "spawn expected string, got {cmd:#}")
};
let Value::List(args) = args else {
throw!(
*SYM_TYPE_ERROR,
"spawn expected list of strings, got {args:#}"
)
throw!(*SYM_TYPE_ERROR, "spawn expected list of strings, got {args:#}")
};
let mut proc = Command::new(cmd.to_os_str());
for arg in args.borrow().iter() {
let Value::String(arg) = arg else {
throw!(
*SYM_TYPE_ERROR,
"spawn expected list of strings, got list containing {arg:#}"
)
throw!(*SYM_TYPE_ERROR, "spawn expected list of strings, got list containing {arg:#}")
};
proc.arg(arg.to_os_str());
}
@ -737,10 +653,12 @@ pub fn system(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut proc;
if cfg!(target_os = "windows") {
proc = Command::new("cmd");
proc.arg("/C").arg(cmd.to_os_str())
proc.arg("/C")
.arg(cmd.to_os_str())
} else {
proc = Command::new("sh");
proc.arg("-c").arg(cmd.to_os_str())
proc.arg("-c")
.arg(cmd.to_os_str())
};
spawn_inner("system", &mut proc, opts)
}
@ -753,10 +671,11 @@ pub fn exit_code(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
};
let mut proc = proc.0.borrow_mut();
match proc.wait() {
Ok(code) => Ok(code
.code()
Ok(code) => {
Ok(code.code()
.map(|c| Value::Int(c as i64))
.unwrap_or_default()),
.unwrap_or_default())
},
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
}
@ -770,3 +689,4 @@ pub fn process_id(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let proc = proc.0.borrow();
Ok((proc.id() as i64).into())
}

View file

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

View file

@ -1,23 +1,10 @@
use std::{
io::{BufRead, Write},
os::unix::ffi::OsStrExt,
sync::Mutex,
time::{SystemTime, UNIX_EPOCH},
};
use std::{io::{BufRead, Write}, os::unix::ffi::OsStrExt, 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, value::Value, vmcall, Vm};
use talc_macros::native_func;
use crate::{unpack_args, SYM_IO_ERROR};
static ENV_LOCK: Mutex<()> = Mutex::new(());
#[native_func(1)]
pub fn print(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let res = match &args[1] {
@ -56,22 +43,14 @@ pub fn readln(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
#[native_func(0)]
pub fn time(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("time went backwards");
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
Ok(time.as_secs_f64().into())
}
#[native_func(0)]
pub fn time_ns(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("time went backwards");
Ok(vec![
(time.as_secs() as i64).into(),
(time.subsec_nanos() as i64).into(),
]
.into())
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
Ok(vec![(time.as_secs() as i64).into(), (time.subsec_nanos() as i64).into()].into())
}
#[native_func(1)]
@ -91,10 +70,7 @@ pub fn env(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "env expected string, got {key:#}")
};
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(
*SYM_VALUE_ERROR,
"environment variable must not be empty or contain an equals sign or null byte"
)
throw!(*SYM_TYPE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
}
let val = std::env::var_os(key.to_os_str());
match val {
@ -110,25 +86,13 @@ pub fn setenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "setenv expected string, got {key:#}")
};
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(
*SYM_VALUE_ERROR,
"environment variable must not be empty or contain an equals sign or null byte"
)
throw!(*SYM_TYPE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
}
let val = val.str();
if val.as_bytes().contains(&0) {
throw!(
*SYM_VALUE_ERROR,
"environment variable value must not contain a null byte"
)
}
{
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);
throw!(*SYM_TYPE_ERROR, "environment variable value must not contain a null byte")
}
std::env::set_var(key.to_os_str(), val.to_os_str());
Ok(Value::Nil)
}
@ -139,21 +103,13 @@ pub fn delenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "delenv expected string, got {key:#}")
};
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(
*SYM_VALUE_ERROR,
"environment variable must not be empty or contain an equals sign or null byte"
)
}
{
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);
throw!(*SYM_TYPE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
}
std::env::remove_var(key.to_os_str());
Ok(Value::Nil)
}
pub fn load(vm: &mut Vm) {
vm.set_global_name("print", print().into());
vm.set_global_name("println", println().into());

View file

@ -1,15 +1,9 @@
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::{symbol::SYM_TYPE_ERROR, exception::Result, throw, value::{function::NativeFunc, range::RangeType, HashValue, Value}, vmcall, vmcalliter, Vm};
use talc_macros::native_func;
use crate::unpack_args;
use crate::{unpack_args, unpack_varargs};
pub fn load(vm: &mut Vm) {
// begin
@ -32,9 +26,7 @@ pub fn load(vm: &mut Vm) {
// join
vm.set_global_name("zip", zip().into());
vm.set_global_name("zipn", zipn().into());
vm.set_global_name("alternate", alternate().into());
vm.set_global_name("alternaten", alternaten().into());
vm.set_global_name("intersperse", intersperse().into());
vm.set_global_name("cartprod", cartprod().into());
vm.set_global_name("chain", chain().into());
@ -56,12 +48,6 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("index_if", index_if().into());
vm.set_global_name("find", find().into());
vm.set_global_name("count", count().into());
vm.set_global_name("mean", mean().into());
vm.set_global_name("variance", variance().into());
vm.set_global_name("stdev", stdev().into());
vm.set_global_name("pvariance", pvariance().into());
vm.set_global_name("pstdev", pstdev().into());
vm.set_global_name("median", median().into());
}
//
@ -93,7 +79,7 @@ pub fn pairs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::iter_pack(None))
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[pairs]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(1)]
@ -101,32 +87,40 @@ pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let v = RefCell::new(Some(val));
let f = move |_: &mut Vm, _| Ok(Value::iter_pack(v.borrow_mut().take()));
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[once]")).into())
let f = move |_: &mut Vm, _| {
Ok(Value::iter_pack(v.borrow_mut().take()))
};
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(1)]
pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let f = move |_: &mut Vm, _| Ok(val.clone());
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[forever]")).into())
let f = move |_: &mut Vm, _| {
Ok(val.clone())
};
Ok(NativeFunc::new(Box::new(f), 0).into())
}
//
// chain iteration
//
#[native_func(2)]
pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, map, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| match vmcalliter!(vm; iter.clone())? {
let f = move |vm: &mut Vm, _| {
match vmcalliter!(vm; iter.clone())? {
Some(val) => Ok(vmcall!(vm; map.clone(), val)?),
None => Ok(Value::iter_pack(None)),
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[map]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(2)]
@ -134,14 +128,16 @@ pub fn tee(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, tee, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| match vmcalliter!(vm; iter.clone())? {
let f = move |vm: &mut Vm, _| {
match vmcalliter!(vm; iter.clone())? {
Some(val) => {
vmcall!(vm; tee.clone(), val.clone())?;
Ok(val)
}
None => Ok(Value::iter_pack(None)),
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[tee]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(3)]
@ -159,7 +155,7 @@ pub fn scan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*result.borrow_mut() = r.clone();
Ok(r)
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[scan]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(2)]
@ -167,7 +163,8 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, filter, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| loop {
let f = move |vm: &mut Vm, _| {
loop {
let Some(next) = vmcalliter!(vm; iter.clone())? else {
return Ok(Value::iter_pack(None))
};
@ -175,21 +172,19 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
if res.truthy() {
return Ok(next)
}
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[filter]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(2)]
pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, count, iter] = unpack_args!(args);
let Value::Int(count) = count else {
throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}")
throw!(*SYM_TYPE_ERROR, "take expected integer")
};
let Ok(count) = count.try_into() else {
throw!(
*SYM_VALUE_ERROR,
"take expected nonnegative integer, got {count:#}"
)
throw!(*SYM_TYPE_ERROR, "take expected nonnegative integer")
};
let iter = iter.to_iter_function()?;
@ -205,20 +200,17 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*taken.borrow_mut() += 1;
Ok(next)
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[take]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(2)]
pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, count, iter] = unpack_args!(args);
let Value::Int(count) = count else {
throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}")
throw!(*SYM_TYPE_ERROR, "count expected integer")
};
let Ok(count) = count.try_into() else {
throw!(
*SYM_VALUE_ERROR,
"count expected nonnegative integer, got {count:#}"
)
throw!(*SYM_TYPE_ERROR, "count expected nonnegative integer")
};
let iter = iter.to_iter_function()?;
@ -233,7 +225,7 @@ pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
vmcall!(vm; iter.clone())
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[skip]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(1)]
@ -252,9 +244,10 @@ pub fn enumerate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::from(vec![n.into(), next]))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[enumerate]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(1)]
pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
@ -286,15 +279,12 @@ pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cycle]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[derive(Clone, Copy, Default)]
enum Step {
First,
Going,
#[default]
End,
First, Going, #[default] End,
}
#[native_func(2)]
@ -302,13 +292,10 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, by, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let Value::Int(by) = by else {
throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}")
throw!(*SYM_TYPE_ERROR, "step expected integer")
};
if by <= 0 {
throw!(
*SYM_VALUE_ERROR,
"step expected positive integer, got {by:#}"
)
throw!(*SYM_TYPE_ERROR, "step expected positive integer")
}
let state = RefCell::new(Step::First);
@ -319,9 +306,9 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*state.borrow_mut() = Step::Going;
}
Ok(Value::iter_pack(res))
}
},
Step::Going => {
for _ in 0..(by - 1) {
for _ in 0..(by-1) {
if vmcall!(vm; iter.clone())? == Value::Nil {
return Ok(Value::iter_pack(None))
}
@ -331,10 +318,10 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
};
*state.borrow_mut() = Step::Going;
Ok(x)
}
},
Step::End => Ok(Value::Nil),
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[step]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(1)]
@ -352,47 +339,24 @@ pub fn rev(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*lst.borrow_mut() = Some(l);
}
Ok(Value::iter_pack(lst.borrow_mut().as_mut().unwrap().pop()))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[rev]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
//
// join
//
#[native_func(2)]
#[native_func(2..)]
pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, i1, i2] = unpack_args!(args);
let i1 = i1.to_iter_function()?;
let i2 = i2.to_iter_function()?;
let f = move |vm: &mut Vm, _| {
let mut res = Vec::with_capacity(2);
match vmcalliter!(vm; i1.clone())? {
Some(v) => res.push(v),
None => return Ok(Value::iter_pack(None)),
};
match vmcalliter!(vm; i2.clone())? {
Some(v) => res.push(v),
None => return Ok(Value::iter_pack(None)),
};
Ok(Value::from(res))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[zip]")).into())
}
#[native_func(1)]
pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "zipn expected list, got {args:#}")
};
let iters = args
.borrow()
.iter()
.map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?;
let ([_, i1, i2], rest) = unpack_varargs!(args);
let mut iters = Vec::with_capacity(2 + rest.len());
for i in [i1, i2].into_iter().chain(rest.into_iter()) {
iters.push(i.to_iter_function()?);
}
let f = move |vm: &mut Vm, _| {
let mut res = Vec::with_capacity(iters.len());
@ -405,53 +369,22 @@ pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::from(res))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[zipn]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[native_func(2)]
#[native_func(2..)]
pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, i1, i2] = unpack_args!(args);
let i1 = i1.to_iter_function()?;
let i2 = i2.to_iter_function()?;
let state = RefCell::new((false, false));
let f = move |vm: &mut Vm, _| {
let mut s = state.borrow_mut();
if s.1 {
return Ok(Value::iter_pack(None))
let ([_, i1, i2], rest) = unpack_varargs!(args);
let mut iters = Vec::with_capacity(2 + rest.len());
for i in [i1, i2].into_iter().chain(rest.into_iter()) {
iters.push(i.to_iter_function()?);
}
let n = s.0;
s.0 = !s.0;
drop(s);
let iter = if n { i1.clone() } else { i2.clone() };
if let Some(v) = vmcalliter!(vm; iter)? {
Ok(v)
} else {
state.borrow_mut().1 = true;
Ok(Value::iter_pack(None))
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[alternate]")).into())
}
#[native_func(1)]
pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "alternaten expected list, got {args:#}")
};
let iters = args
.borrow()
.iter()
.map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?;
let state = RefCell::new((0, false));
let f = move |vm: &mut Vm, _| {
let mut s = state.borrow_mut();
if s.1 {
return Ok(Value::iter_pack(None))
return Ok(Value::iter_pack(None));
}
let n = s.0;
s.0 = (s.0 + 1) % iters.len();
@ -464,16 +397,12 @@ pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[alternaten]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[derive(Default)]
enum Intersperse {
Init,
Waiting,
HasNext(Value),
#[default]
End,
Init, Waiting, HasNext(Value), #[default] End
}
#[native_func(2)]
@ -482,7 +411,8 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter = iter.to_iter_function()?;
let state = RefCell::new(Intersperse::Init);
let f = move |vm: &mut Vm, _| match state.take() {
let f = move |vm: &mut Vm, _| {
match state.take() {
Intersperse::Init => {
if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::Waiting;
@ -491,7 +421,7 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*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);
@ -500,17 +430,19 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*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).into())
}
#[native_func(2)]
pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter1, iter2] = unpack_args!(args);
@ -530,7 +462,7 @@ pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[chain]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
#[derive(Default, Debug)]
@ -595,13 +527,17 @@ pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::from(vec![a_res, b_res]))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cart_prod]")).into())
Ok(NativeFunc::new(Box::new(f), 0).into())
}
//
// end iteration
//
#[native_func(1)]
pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
@ -609,7 +545,7 @@ pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut result = Vec::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? {
result.push(value);
}
};
Ok(result.into())
}
@ -620,23 +556,14 @@ pub fn table(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut result = HashMap::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? {
let Value::List(l) = value else {
throw!(
*SYM_TYPE_ERROR,
"table expected iterator to yield list, got {value:#}"
)
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list")
};
let mut l = Rc::unwrap_or_clone(l).take();
if l.len() != 2 {
throw!(
*SYM_VALUE_ERROR,
"table: iterator yielded list of length {} (expected 2)",
l.len()
)
let l = Rc::unwrap_or_clone(l).take();
let Ok([k, v]): std::result::Result<[Value; 2], Vec<Value>> = l.try_into() else {
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list of length 2")
};
let v = l.pop().unwrap();
let k = l.pop().unwrap();
result.insert(k.try_into()?, v);
}
};
Ok(result.into())
}
@ -647,16 +574,15 @@ pub fn len(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::String(s) => return Ok((s.chars().count() as i64).into()),
Value::List(l) => return Ok((l.borrow().len() as i64).into()),
Value::Table(t) => return Ok((t.borrow().len() as i64).into()),
Value::Range(r) if r.ty != RangeType::Endless => {
return Ok((r.len().unwrap() as i64).into())
}
Value::Range(r) if r.ty != RangeType::Endless
=> return Ok((r.len().unwrap() as i64).into()),
_ => (),
}
let iter = value.to_iter_function()?;
let mut len = 0;
while vmcalliter!(vm; iter.clone())?.is_some() {
len += 1;
}
};
Ok(len.into())
}
@ -775,13 +701,11 @@ pub fn contains(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for _ in 0.. {
match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil),
Some(v) => {
if v == val {
Some(v) => if v == val {
return Ok(true.into())
}
}
}
}
Ok(false.into())
}
@ -793,13 +717,11 @@ pub fn index_of(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for i in 0.. {
match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil),
Some(v) => {
if v == val {
Some(v) => if v == val {
return Ok(i.into())
}
}
}
}
Ok(Value::Nil)
}
@ -811,13 +733,11 @@ pub fn index_if(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for i in 0.. {
match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil),
Some(v) => {
if vmcall!(vm; func.clone(), v)?.truthy() {
Some(v) => if vmcall!(vm; func.clone(), v)?.truthy() {
return Ok(i.into())
}
}
}
}
Ok(Value::Nil)
}
@ -855,111 +775,3 @@ pub fn count(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(map.into())
}
#[native_func(1)]
pub fn mean(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let mut sum = Value::Float(0.0);
let mut count = Value::Int(0);
while let Some(value) = vmcalliter!(vm; iter.clone())? {
sum = (sum + value)?;
count = (count + Value::from(1))?;
}
sum / count
}
fn variance_inner(vm: &mut Vm, iter: Value, pop: bool) -> Result<Value> {
let mut m = Value::Float(0.0);
let mut s = Value::Float(0.0);
let mut k = 1;
while let Some(value) = vmcalliter!(vm; iter.clone())? {
let old_m = m.clone();
m = (m.clone() + ((value.clone() - m.clone())? / Value::Int(k))?)?;
s = (s + ((value.clone() - m.clone())? * (value - old_m)?)?)?;
k += 1;
}
s / Value::Int(k - if pop { 1 } else { 2 })
}
#[native_func(1)]
pub fn variance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
variance_inner(vm, iter, false)
}
#[native_func(1)]
pub fn stdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let v = variance_inner(vm, iter, false)?;
Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
})
}
#[native_func(1)]
pub fn pvariance(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
variance_inner(vm, iter, true)
}
#[native_func(1)]
pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let v = variance_inner(vm, iter, true)?;
Ok(match v {
Value::Int(n) => Value::Float((n as f64).sqrt()),
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
})
}
#[derive(PartialEq, PartialOrd)]
struct OrdValue(Value);
impl std::cmp::Eq for OrdValue {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl std::cmp::Ord for OrdValue {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap_or(std::cmp::Ordering::Less)
}
}
#[native_func(1)]
pub fn median(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let mut vals = Vec::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? {
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)
}
}

View file

@ -1,22 +1,16 @@
#![allow(clippy::mutable_key_type)]
use talc_lang::{symbol::{symbol, Symbol}, Vm};
use talc_lang::{
symbol::{symbol, Symbol},
Vm,
};
pub mod collection;
pub mod exception;
pub mod file;
pub mod format;
pub mod value;
pub mod io;
pub mod iter;
pub mod exception;
pub mod num;
#[cfg(feature = "random")]
pub mod random;
pub mod regex;
pub mod collection;
pub mod string;
pub mod value;
pub mod file;
pub mod regex;
#[cfg(feature="random")]
pub mod random;
pub fn load_all(vm: &mut Vm) {
value::load(vm);
@ -26,18 +20,17 @@ pub fn load_all(vm: &mut Vm) {
num::load(vm);
io::load(vm);
string::load(vm);
format::load(vm);
regex::load(vm);
file::load(vm);
#[cfg(feature = "random")]
#[cfg(feature="random")]
random::load(vm);
}
lazy_static::lazy_static! {
pub static ref SYM_IO_ERROR: Symbol = symbol!(io_error);
pub static ref SYM_FORMAT_ERROR: Symbol = symbol!(format_error);
}
macro_rules! unpack_args {
($e:expr) => {
($e).try_into().expect("bypassed arity check")
@ -45,3 +38,16 @@ macro_rules! unpack_args {
}
pub(crate) use unpack_args;
macro_rules! unpack_varargs {
($e:expr) => {{
let mut args = $e;
let Value::List(varargs) = args.pop().expect("bypassed arity check") else {
panic!("bypassed arity check")
};
let varargs = ::std::rc::Rc::unwrap_or_clone(varargs).into_inner();
(args.try_into().expect("bypassed arity check"), varargs)
}};
}
pub(crate) use unpack_varargs;

View file

@ -1,17 +1,10 @@
use std::cmp::Ordering;
use lazy_static::lazy_static;
use talc_lang::{
exception::Result,
parser::{parse_int, to_lstring_radix},
symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{ops::RatioExt, Complex64, Value},
vmcalliter, Vm,
};
use talc_lang::{exception::Result, lstring::LString, parse_int, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{ops::RatioExt, Complex64, Value}, Vm};
use talc_macros::native_func;
use crate::unpack_args;
use crate::{unpack_args, unpack_varargs};
lazy_static! {
static ref SYM_NAN: Symbol = Symbol::get("nan");
@ -56,8 +49,8 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("inf", (f64::INFINITY).into());
vm.set_global_name("NaN", (f64::NAN).into());
vm.set_global_name("infi", Complex64::new(0.0, f64::INFINITY).into());
vm.set_global_name("NaNi", Complex64::new(0.0, f64::NAN).into());
vm.set_global_name("infi", Complex64::new(0.0,f64::INFINITY).into());
vm.set_global_name("NaNi", Complex64::new(0.0,f64::NAN).into());
vm.set_global_name("bin", bin().into());
vm.set_global_name("sex", sex().into());
@ -68,13 +61,10 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("from_radix", from_radix().into());
vm.set_global_name("gcd", gcd().into());
vm.set_global_name("gcdn", gcdn().into());
vm.set_global_name("lcm", lcm().into());
vm.set_global_name("lcmn", lcmn().into());
vm.set_global_name("isqrt", isqrt().into());
vm.set_global_name("isprime", isprime().into());
vm.set_global_name("factors", factors().into());
vm.set_global_name("totient", totient().into());
vm.set_global_name("min", min().into());
vm.set_global_name("max", max().into());
@ -106,7 +96,6 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("cbrt", cbrt().into());
vm.set_global_name("ln", ln().into());
vm.set_global_name("log2", log2().into());
vm.set_global_name("log10", log10().into());
vm.set_global_name("exp", exp().into());
vm.set_global_name("exp2", exp2().into());
@ -130,13 +119,42 @@ pub fn load(vm: &mut Vm) {
// base conversions
//
fn to_radix_inner(n: i64, radix: u32, upper: bool) -> LString {
let mut result = vec![];
let mut begin = 0;
let mut x;
if n < 0 {
result.push('-' as u32 as u8);
begin = 1;
x = (-n) as u64;
} else {
x = n as u64;
}
loop {
let m = x % (radix as u64);
x /= radix as u64;
let mut c = char::from_digit(m as u32, radix).unwrap();
if upper { c.make_ascii_uppercase(); }
result.push(c as u8);
if x == 0 {
break;
}
}
result[begin..].reverse();
LString::from(result)
}
#[native_func(1)]
pub fn bin(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}")
};
Ok(to_lstring_radix(x, 2, false).into())
Ok(to_radix_inner(x, 2, false).into())
}
#[native_func(1)]
@ -145,7 +163,7 @@ pub fn sex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "sex expected integer argument, got {x:#}")
};
Ok(to_lstring_radix(x, 6, false).into())
Ok(to_radix_inner(x, 6, false).into())
}
#[native_func(1)]
@ -154,7 +172,7 @@ pub fn oct(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "oct expected integer argument, got {x:#}")
};
Ok(to_lstring_radix(x, 8, false).into())
Ok(to_radix_inner(x, 8, false).into())
}
#[native_func(1)]
@ -163,63 +181,45 @@ pub fn hex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "hex expected integer argument, got {x:#}")
};
Ok(to_lstring_radix(x, 16, false).into())
Ok(to_radix_inner(x, 16, false).into())
}
#[native_func(2)]
pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, radix] = unpack_args!(args);
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
throw!(
*SYM_TYPE_ERROR,
"to_radix expected integer arguments, got {x:#} and {radix:#}"
)
throw!(*SYM_TYPE_ERROR, "to_radix expected integer arguments, got {x:#} and {radix:#}")
};
if *radix < 2 || *radix > 36 {
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 0..=36")
throw!(*SYM_TYPE_ERROR, "to_radix expected radix in range 0..=36")
}
Ok(to_lstring_radix(*x, *radix as u32, false).into())
Ok(to_radix_inner(*x, *radix as u32, false).into())
}
#[native_func(2)]
pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, radix] = unpack_args!(args);
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
throw!(
*SYM_TYPE_ERROR,
"to_radix_upper expected integer arguments, got {x:#} and {radix:#}"
)
throw!(*SYM_TYPE_ERROR, "to_radix_upper expected integer arguments, got {x:#} and {radix:#}")
};
if *radix < 2 || *radix > 36 {
throw!(
*SYM_VALUE_ERROR,
"to_radix_upper expected radix in range 0..=36"
)
throw!(*SYM_TYPE_ERROR, "to_radix_upper expected radix in range 0..=36")
}
Ok(to_lstring_radix(*x, *radix as u32, true).into())
Ok(to_radix_inner(*x, *radix as u32, true).into())
}
#[native_func(2)]
pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s, radix] = unpack_args!(args);
let (Value::String(s), Value::Int(radix)) = (&s, &radix) else {
throw!(
*SYM_TYPE_ERROR,
"from_radix expected string and integer arguments, got {s:#} and {radix:#}"
)
throw!(*SYM_TYPE_ERROR, "from_radix expected string and integer arguments, got {s:#} and {radix:#}")
};
if *radix < 2 || *radix > 36 {
throw!(
*SYM_VALUE_ERROR,
"from_radix expected radix in range 0..=36"
)
throw!(*SYM_TYPE_ERROR, "from_radix expected radix in range 0..=36")
}
match parse_int(s.as_ref(), *radix as u32) {
Ok(v) => Ok(v.into()),
Err(_) => throw!(
*SYM_VALUE_ERROR,
"string was not a valid integer in given radix"
),
Err(_) => throw!(*SYM_TYPE_ERROR, "string was not a valid integer in given radix"),
}
}
@ -229,9 +229,7 @@ pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
fn isqrt_inner(mut n: i64) -> i64 {
assert!(n >= 0, "isqrt input should be nonnegative");
if n < 2 {
return n
}
if n < 2 { return n }
let mut c = 0;
let mut d = 1 << 62;
@ -244,7 +242,8 @@ fn isqrt_inner(mut n: i64) -> i64 {
if n >= c + d {
n -= c + d;
c = (c >> 1) + d;
} else {
}
else {
c >>= 1;
}
d >>= 2;
@ -273,35 +272,31 @@ pub fn gcd_inner(a: i64, b: i64) -> i64 {
}
b -= a;
if b == 0 {
return a << z
return a << z;
}
b >>= b.trailing_zeros();
}
}
#[native_func(1)]
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(
*SYM_TYPE_ERROR,
"isqrt expected integer argument, got {x:#}"
)
throw!(*SYM_TYPE_ERROR, "isqrt expected integer argument, got {x:#}")
};
if x < 0 {
throw!(*SYM_VALUE_ERROR, "isqrt: argument must be positive")
throw!(*SYM_TYPE_ERROR, "isqrt: argument must be positive")
}
Ok(isqrt_inner(x).into())
}
#[native_func(1)]
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(
*SYM_TYPE_ERROR,
"isprime expected integer argument, got {x:#}"
)
throw!(*SYM_TYPE_ERROR, "isprime expected integer argument, got {x:#}")
};
if x < 2 {
return Ok(false.into())
@ -316,97 +311,59 @@ pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
}
let lim = isqrt_inner(x);
let mut i = 12;
while i <= lim + 1 {
if x % (i - 1) == 0 {
return Ok(false.into())
}
if x % (i + 1) == 0 {
return Ok(false.into())
}
while i <= lim+1 {
if x % (i - 1) == 0 { return Ok(false.into()) }
if x % (i + 1) == 0 { return Ok(false.into()) }
i += 6;
}
Ok(true.into())
}
#[native_func(2)]
#[native_func(2..)]
pub fn gcd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
let ([_, x, y], rest) = unpack_varargs!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
};
let Value::Int(y) = y else {
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {y:#}")
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {x:#}")
};
Ok(gcd_inner(x, y).into())
}
#[native_func(1)]
pub fn gcdn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?;
let mut g = 0;
while let Some(a) = vmcalliter!(vm; args.clone())? {
let mut g = gcd_inner(x, y);
for a in rest {
let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "gcdn: cannot take gcd with {a:#}")
throw!(*SYM_TYPE_ERROR, "gcd expected integer argument, got {a:#}")
};
g = gcd_inner(g, a);
}
Ok(g.into())
}
#[native_func(2)]
#[native_func(2..)]
pub fn lcm(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
let ([_, x, y], rest) = unpack_varargs!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
};
let Value::Int(y) = y else {
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {y:#}")
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {x:#}")
};
let g = gcd_inner(x, y);
if g == 0 {
Ok(Value::from(0))
} else {
(Value::from(x) / Value::from(g))? * Value::from(y)
}
}
#[native_func(1)]
pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, args] = unpack_args!(args);
let args = args.to_iter_function()?;
let mut l = 1;
while let Some(a) = vmcalliter!(vm; args.clone())? {
let mut g = gcd_inner(x, y);
let mut prod = y;
for a in rest {
let Value::Int(a) = a else {
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
throw!(*SYM_TYPE_ERROR, "lcm expected integer argument, got {a:#}")
};
let g = gcd_inner(l, a);
if g == 0 {
return Ok(Value::from(0))
};
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?;
let Value::Int(new_l) = new_l else {
unreachable!("int//int * int != int")
};
l = new_l;
prod *= a;
g = gcd_inner(g, a);
}
Ok(Value::from(l))
Ok((x/g * prod).into())
}
#[native_func(1)]
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(mut x) = x else {
throw!(
*SYM_TYPE_ERROR,
"factors expected integer argument, got {x:#}"
)
throw!(*SYM_TYPE_ERROR, "factords expected integer argument, got {x:#}")
};
let mut factors = Vec::new();
if x <= 1 {
@ -421,7 +378,7 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
factors.push(Value::Int(3));
}
let mut i = 5;
while x >= i * i {
while x >= i*i {
while x % i == 0 {
x /= i;
factors.push(Value::Int(i));
@ -439,76 +396,31 @@ pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(factors.into())
}
fn totient_prime(n: &mut u64, p: u64) -> u64 {
if *n % p != 0 {
return 1
}
*n /= p;
let mut v = p - 1;
while *n % p == 0 {
*n /= p;
v *= p;
}
v
}
#[native_func(1)]
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(
*SYM_TYPE_ERROR,
"totient expected integer argument, got {x:#}"
)
};
if x <= 1 {
return Ok(1.into())
}
let mut x = x as u64;
let mut totient = 1;
if x & 1 == 0 {
x >>= 1;
}
while x & 1 == 0 {
x >>= 1;
totient <<= 1;
}
totient *= totient_prime(&mut x, 3);
let mut i = 5;
while x >= i * i {
totient *= totient_prime(&mut x, i);
i += 2;
totient *= totient_prime(&mut x, i);
i += 4;
}
if x > 1 {
totient *= x - 1;
}
Ok((totient as i64).into())
}
//
// numeric operations
//
#[native_func(2)]
#[native_func(1..)]
pub fn min(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
if y < x {
Ok(y)
} else {
Ok(x)
let ([_, mut x], rest) = unpack_varargs!(args);
for val in rest {
if val < x {
x = val;
}
}
Ok(x)
}
#[native_func(2)]
#[native_func(1..)]
pub fn max(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
if y > x {
Ok(y)
} else {
Ok(x)
let ([_, mut x], rest) = unpack_varargs!(args);
for val in rest {
if val > x {
x = val;
}
}
Ok(x)
}
#[native_func(1)]
@ -576,7 +488,7 @@ pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ordering::Greater => Ok(Value::Ratio(1.into())),
Ordering::Less => Ok(Value::Ratio((-1).into())),
Ordering::Equal => Ok(Value::Ratio(0.into())),
},
}
x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"),
}
}
@ -585,6 +497,7 @@ pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// floating-point operations
//
#[native_func(1)]
pub fn signum(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
@ -599,10 +512,7 @@ pub fn classify(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let x = to_floaty(x);
let Value::Float(x) = x else {
throw!(
*SYM_TYPE_ERROR,
"classify expected real argument, got {x:#}"
)
throw!(*SYM_TYPE_ERROR, "classify expected real argument, got {x:#}")
};
Ok(match x.classify() {
std::num::FpCategory::Nan => *SYM_NAN,
@ -610,8 +520,7 @@ pub fn classify(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
std::num::FpCategory::Zero => *SYM_ZERO,
std::num::FpCategory::Subnormal => *SYM_SUBNORMAL,
std::num::FpCategory::Normal => *SYM_NORMAL,
}
.into())
}.into())
}
#[native_func(1)]
@ -621,10 +530,7 @@ pub fn isnan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
Value::Float(x) => Ok(x.is_nan().into()),
Value::Complex(z) => Ok(z.is_nan().into()),
v => throw!(
*SYM_TYPE_ERROR,
"isnan expected numeric argument, got {v:#}"
),
v => throw!(*SYM_TYPE_ERROR, "isnan expected numeric argument, got {v:#}"),
}
}
@ -635,10 +541,7 @@ pub fn isfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Int(_) | Value::Ratio(_) => Ok(true.into()),
Value::Float(x) => Ok(x.is_finite().into()),
Value::Complex(z) => Ok(z.is_finite().into()),
v => throw!(
*SYM_TYPE_ERROR,
"isfinite expected numeric argument, got {v:#}"
),
v => throw!(*SYM_TYPE_ERROR, "isfinite expected numeric argument, got {v:#}"),
}
}
@ -649,10 +552,7 @@ pub fn isinfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
Value::Float(x) => Ok(x.is_infinite().into()),
Value::Complex(z) => Ok(z.is_infinite().into()),
v => throw!(
*SYM_TYPE_ERROR,
"isinfinite expected numeric argument, got {v:#}"
),
v => throw!(*SYM_TYPE_ERROR, "isinfinite expected numeric argument, got {v:#}"),
}
}
@ -660,10 +560,7 @@ pub fn isinfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::Float(f) = val else {
throw!(
*SYM_TYPE_ERROR,
"float_to_bits expected float argument, got {val:#}"
)
throw!(*SYM_TYPE_ERROR, "float_to_bits expected float argument, got {val:#}")
};
Ok(Value::Int(f.to_bits() as i64))
}
@ -672,28 +569,25 @@ pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn float_of_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::Int(i) = val else {
throw!(
*SYM_TYPE_ERROR,
"float_of_bits expected integer argument, got {val:#}"
)
throw!(*SYM_TYPE_ERROR, "float_of_bits expected integer argument, got {val:#}")
};
Ok(Value::Float(f64::from_bits(i as u64)))
}
//
// rational operations
//
#[native_func(1)]
pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args);
match v {
Value::Int(x) => Ok(Value::Int(x)),
Value::Ratio(x) => Ok(Value::Int(*x.numer())),
v => throw!(
*SYM_TYPE_ERROR,
"numer expected rational argument, got {v:#}"
),
v => throw!(*SYM_TYPE_ERROR, "numer expected rational argument, got {v:#}"),
}
}
@ -703,10 +597,7 @@ pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match v {
Value::Int(_) => Ok(Value::Int(1)),
Value::Ratio(x) => Ok(Value::Int(*x.denom())),
v => throw!(
*SYM_TYPE_ERROR,
"denom expected rational argument, got {v:#}"
),
v => throw!(*SYM_TYPE_ERROR, "denom expected rational argument, got {v:#}"),
}
}
@ -714,6 +605,8 @@ pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// complex operations
//
#[native_func(1)]
pub fn re(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args);
@ -750,28 +643,34 @@ pub fn arg(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(1)]
pub fn abs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
x.abs()
match x {
Value::Int(x) => Ok(Value::Int(x.abs())),
Value::Ratio(x) => Ok(Value::Ratio(if x < 0.into() { -x } else { x })),
Value::Float(x) => Ok(Value::Float(x.abs())),
Value::Complex(x) => Ok(Value::Float(x.norm())),
x => throw!(*SYM_TYPE_ERROR, "abs expected numeric argument, got {x:#}"),
}
}
#[native_func(1)]
pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
match x {
Value::Int(_) => x.clone() * x,
Value::Ratio(_) => x.clone() * x,
Value::Float(_) => x.clone() * x,
Value::Int(x) => Ok(Value::Int(x * x)),
Value::Ratio(x) => Ok(Value::Ratio(x * x)),
Value::Float(x) => Ok(Value::Float(x * x)),
Value::Complex(x) => Ok(Value::Float(x.norm_sqr())),
x => throw!(
*SYM_TYPE_ERROR,
"abs_sq expected numeric argument, got {x:#}"
),
x => throw!(*SYM_TYPE_ERROR, "abs_sq expected numeric argument, got {x:#}"),
}
}
//
// continuous operations
//
macro_rules! float_func {
($name:ident) => {
#[native_func(1)]
@ -780,11 +679,8 @@ macro_rules! float_func {
match to_floaty(v) {
Value::Float(x) => Ok(Value::Float(x.$name())),
Value::Complex(z) => Ok(Value::Complex(z.$name())),
v => throw!(
*SYM_TYPE_ERROR,
"{} expected numeric argument, got {v:#}",
stringify!($name)
),
v => throw!(*SYM_TYPE_ERROR,
"{} expected numeric argument, got {v:#}", stringify!($name)),
}
}
};
@ -794,7 +690,6 @@ float_func!(sqrt);
float_func!(cbrt);
float_func!(ln);
float_func!(log2);
float_func!(log10);
float_func!(exp);
float_func!(exp2);
@ -815,10 +710,10 @@ float_func!(atanh);
pub fn atan2(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, y, x] = unpack_args!(args);
match (to_floaty(y), to_floaty(x)) {
(Value::Float(y), Value::Float(x)) => Ok(Value::Float(x.atan2(y))),
(y, x) => throw!(
*SYM_TYPE_ERROR,
"atan2 expected real arguments, got {y:#} and {x:#}"
),
(Value::Float(y), Value::Float(x))
=> Ok(Value::Float(x.atan2(y))),
(y,x) => throw!(*SYM_TYPE_ERROR,
"atan2 expected real arguments, got {y:#} and {x:#}"),
}
}

View file

@ -1,11 +1,5 @@
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::{symbol::SYM_TYPE_ERROR, throw, value::{range::RangeType, Value}, vmcalliter, Vm, exception::Result};
use talc_macros::native_func;
use crate::unpack_args;
@ -28,31 +22,29 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::List(l) => {
let l = l.borrow();
let Some(v) = l.choose(&mut rand::thread_rng()) else {
throw!(*SYM_VALUE_ERROR, "rand_in: empty list")
throw!(*SYM_TYPE_ERROR, "rand_in: empty list")
};
Ok(v.clone())
}
},
Value::Table(t) => {
let t = t.borrow();
if t.is_empty() {
throw!(*SYM_VALUE_ERROR, "rand_in: empty table")
throw!(*SYM_TYPE_ERROR, "rand_in: empty table")
};
let i = rand::thread_rng().gen_range(0..t.len());
let key = t.keys().nth(i).unwrap();
Ok(key.clone().into_inner())
}
},
Value::Range(r) => {
if r.is_empty() {
throw!(*SYM_VALUE_ERROR, "rand_in: empty range")
throw!(*SYM_TYPE_ERROR, "rand_in: empty range")
}
match r.ty {
RangeType::Open => {
Ok(Value::Int(rand::thread_rng().gen_range(r.start..r.stop)))
}
RangeType::Closed => {
Ok(Value::Int(rand::thread_rng().gen_range(r.start..=r.stop)))
}
RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"),
RangeType::Open => Ok(Value::Int(
rand::thread_rng().gen_range(r.start..r.stop))),
RangeType::Closed => Ok(Value::Int(
rand::thread_rng().gen_range(r.start..=r.stop))),
RangeType::Endless => throw!(*SYM_TYPE_ERROR, "rand_in: endless range"),
}
}
col => {
@ -62,7 +54,7 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
values.push(v);
}
if values.is_empty() {
throw!(*SYM_VALUE_ERROR, "rand_in: empty iterator")
throw!(*SYM_TYPE_ERROR, "rand_in: empty iterator")
}
let i = rand::thread_rng().gen_range(0..values.len());
let v = values.swap_remove(i);

View file

@ -1,16 +1,9 @@
use std::borrow::Cow;
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_lang::{exception::{exception, Result}, lstring::LString, symbol::{Symbol, SYM_TYPE_ERROR}, throw, value::{NativeValue, Value}, Vm};
use talc_macros::native_func;
use regex::{Captures, Match, Regex};
use lazy_static::lazy_static;
use crate::unpack_args;
@ -25,30 +18,17 @@ lazy_static! {
pub struct ValueRegex(Regex);
impl From<Regex> for ValueRegex {
fn from(value: Regex) -> Self {
Self(value)
}
fn from(value: Regex) -> Self { Self(value) }
}
impl From<ValueRegex> for Regex {
fn from(value: ValueRegex) -> Self {
value.0
}
fn from(value: ValueRegex) -> Self { value.0 }
}
impl NativeValue for ValueRegex {
fn get_type(&self) -> Symbol {
*SYM_STD_REGEX
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn to_lstring(
&self,
w: &mut LString,
repr: bool,
_recur: &mut Vec<*const ()>,
) -> std::io::Result<()> {
fn get_type(&self) -> Symbol { *SYM_STD_REGEX }
fn as_any(&self) -> &dyn std::any::Any { self }
fn to_lstring(&self, w: &mut LString, repr: bool) -> std::io::Result<()> {
use std::io::Write;
if repr {
write!(w, "/{}/", self.0)
@ -78,10 +58,7 @@ fn match_to_value(m: Match) -> Value {
Value::new_table(|t| {
t.insert((*SYM_START).into(), (m.start() as i64).into());
t.insert((*SYM_END).into(), (m.end() as i64).into());
t.insert(
(*SYM_STR).into(),
LString::from(m.as_str().to_string()).into(),
);
t.insert((*SYM_STR).into(), LString::from(m.as_str().to_string()).into());
})
}
@ -96,33 +73,27 @@ fn regex_from<'a>(v: &'a Value, name: &str) -> Result<Cow<'a, Regex>> {
match v {
Value::String(s) => {
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "regex must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "regex must be valid UTF-8")
};
Regex::new(s)
.map(Cow::Owned)
.map_err(|e| exception!(*SYM_VALUE_ERROR, "invalid regex: {e}"))
}
Value::Native(n) if n.get_type() == *SYM_STD_REGEX => n
.as_any()
.downcast_ref::<ValueRegex>()
.map_err(|e| exception!(*SYM_TYPE_ERROR, "invalid regex: {e}"))
},
Value::Native(n) if n.get_type() == *SYM_STD_REGEX => {
n.as_any().downcast_ref::<ValueRegex>()
.map(|vr| Cow::Borrowed(&vr.0))
.ok_or_else(|| {
exception!(
*SYM_TYPE_ERROR,
"{name} expected string or regex, got {v:#}"
)
}),
_ => throw!(
*SYM_TYPE_ERROR,
"{name} expected string or regex, got {v:#}"
),
.ok_or_else(|| exception!(
*SYM_TYPE_ERROR, "BEES {name} expected string or regex, got {v:#}"))
},
_ => throw!(*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}")
}
}
#[native_func(1)]
pub fn _regex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, re] = unpack_args!(args);
regex_from(&re, "regex").map(|re| ValueRegex(re.into_owned()).into())
regex_from(&re, "regex")
.map(|re| ValueRegex(re.into_owned()).into())
}
#[native_func(2)]
@ -132,7 +103,7 @@ pub fn matches(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "matches expected string, got {s:#}")
};
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8")
};
let re = regex_from(&re, "matches")?;
Ok(re.is_match(s).into())
@ -145,7 +116,7 @@ pub fn match_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "match_once expected string, got {s:#}")
};
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8")
};
let re = regex_from(&re, "match_once")?;
Ok(re.find(s).map_or(Value::Nil, match_to_value))
@ -158,14 +129,10 @@ pub fn _match(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "match expected string, got {s:#}")
};
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8")
};
let re = regex_from(&re, "match")?;
Ok(re
.find_iter(s)
.map(match_to_value)
.collect::<Vec<Value>>()
.into())
Ok(re.find_iter(s).map(match_to_value).collect::<Vec<Value>>().into())
}
#[native_func(2)]
@ -175,7 +142,7 @@ pub fn captures_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "captures_once expected string, got {s:#}")
};
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8")
};
let re = regex_from(&re, "captures_once")?;
Ok(re.captures(s).map_or(Value::Nil, captures_to_value))
@ -188,14 +155,10 @@ pub fn captures(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "captures expected string, got {s:#}")
};
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8")
};
let re = regex_from(&re, "captures")?;
Ok(re
.captures_iter(s)
.map(captures_to_value)
.collect::<Vec<Value>>()
.into())
Ok(re.captures_iter(s).map(captures_to_value).collect::<Vec<Value>>().into())
}
#[native_func(3)]
@ -205,16 +168,13 @@ pub fn replace_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "replace_once expected string, got {s:#}")
};
let Value::String(rep) = rep else {
throw!(
*SYM_TYPE_ERROR,
"replace_once expected string or function, got {rep:#}"
)
throw!(*SYM_TYPE_ERROR, "replace_once expected string or function, got {rep:#}")
};
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8")
};
let Ok(rep) = rep.to_str() else {
throw!(*SYM_VALUE_ERROR, "replacement string must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "replacement string must be valid UTF-8")
};
let re = regex_from(&re, "replace_once")?;
Ok(LString::from(re.replace(s, rep)).into())
@ -227,16 +187,13 @@ pub fn replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "replace expected string, got {s:#}")
};
let Value::String(rep) = rep else {
throw!(
*SYM_TYPE_ERROR,
"replace expected string or function, got {rep:#}"
)
throw!(*SYM_TYPE_ERROR, "replace expected string or function, got {rep:#}")
};
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "search string must be valid UTF-8")
};
let Ok(rep) = rep.to_str() else {
throw!(*SYM_VALUE_ERROR, "replacement string must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "replacement string must be valid UTF-8")
};
let re = regex_from(&re, "replace")?;
Ok(LString::from(re.replace_all(s, rep)).into())
@ -249,13 +206,13 @@ pub fn split_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "split_once expected string, got {s:#}")
};
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "string to split must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "string to split must be valid UTF-8")
};
let re = regex_from(&re, "split_once")?;
let mut parts = re.splitn(s, 2);
let (part1, part2) = (
LString::from(parts.next().unwrap_or_default()).into(),
LString::from(parts.next().unwrap_or_default()).into(),
LString::from(parts.next().unwrap_or_default()).into()
);
Ok(vec![part1, part2].into())
}
@ -267,9 +224,12 @@ pub fn split(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "split expected string, got {s:#}")
};
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "string to split must be valid UTF-8")
throw!(*SYM_TYPE_ERROR, "string to split must be valid UTF-8")
};
let re = regex_from(&re, "split")?;
let parts: Vec<Value> = re.split(s).map(|s| LString::from(s).into()).collect();
let parts: Vec<Value> = re.split(s)
.map(|s| LString::from(s).into())
.collect();
Ok(parts.into())
}

View file

@ -1,11 +1,4 @@
use talc_lang::{
exception::Result,
lstring::LString,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::Value,
Vm,
};
use talc_lang::{exception::Result, lformat, lstring::LString, symbol::SYM_TYPE_ERROR, throw, value::Value, Vm};
use talc_macros::native_func;
use crate::unpack_args;
@ -24,6 +17,7 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("to_utf8_lossy", to_utf8_lossy().into());
vm.set_global_name("str_to_bytes", str_to_bytes().into());
vm.set_global_name("str_of_bytes", str_of_bytes().into());
vm.set_global_name("format", _format().into());
}
#[native_func(1)]
@ -34,10 +28,10 @@ pub fn ord(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
};
let mut chars = s.chars();
let Some(c) = chars.next() else {
throw!(*SYM_VALUE_ERROR, "argument to ord must have length 1")
throw!(*SYM_TYPE_ERROR, "argument to ord must have length 1")
};
if chars.next().is_some() {
throw!(*SYM_VALUE_ERROR, "argument to ord must have length 1")
throw!(*SYM_TYPE_ERROR, "argument to ord must have length 1")
};
Ok(Value::Int(c as u32 as i64))
}
@ -49,10 +43,10 @@ pub fn chr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "chr expected integer argument, got {i:#}")
};
let Ok(i) = u32::try_from(i) else {
throw!(*SYM_VALUE_ERROR, "argument to chr is not a valid codepoint")
throw!(*SYM_TYPE_ERROR, "argument to chr is not a valid codepoint")
};
let Some(c) = char::from_u32(i) else {
throw!(*SYM_VALUE_ERROR, "argument to chr is not a valid codepoint")
throw!(*SYM_TYPE_ERROR, "argument to chr is not a valid codepoint")
};
Ok(Value::String(LString::from(c).into()))
}
@ -61,10 +55,7 @@ pub fn chr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn len_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args);
let Value::String(s) = s else {
throw!(
*SYM_TYPE_ERROR,
"len_bytes expected string argument, got {s:#}"
)
throw!(*SYM_TYPE_ERROR, "len_bytes expected string argument, got {s:#}")
};
Ok(Value::Int(s.len() as i64))
}
@ -102,10 +93,8 @@ pub fn starts_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let res = match (d, pre) {
(Value::List(d), Value::List(pre)) => d.borrow().starts_with(&pre.borrow()),
(Value::String(d), Value::String(pre)) => d.starts_with(&pre),
(d, pre) => throw!(
*SYM_TYPE_ERROR,
"starts_with expected two lists or strings, got {d:#} and {pre:#}"
),
(d, pre) => throw!(*SYM_TYPE_ERROR,
"starts_with expected two lists or strings, got {d:#} and {pre:#}")
};
Ok(res.into())
}
@ -116,10 +105,8 @@ pub fn ends_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let res = match (d, suf) {
(Value::List(d), Value::List(suf)) => d.borrow().ends_with(&suf.borrow()),
(Value::String(d), Value::String(suf)) => d.ends_with(&suf),
(d, suf) => throw!(
*SYM_TYPE_ERROR,
"ends_with expected two lists or strings, got {d:#} and {suf:#}"
),
(d, suf) => throw!(*SYM_TYPE_ERROR,
"ends_with expected two lists or strings, got {d:#} and {suf:#}")
};
Ok(res.into())
}
@ -128,10 +115,7 @@ pub fn ends_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn is_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args);
let Value::String(s) = s else {
throw!(
*SYM_TYPE_ERROR,
"is_utf8 expected string argument, got {s:#}"
)
throw!(*SYM_TYPE_ERROR, "is_utf8 expected string argument, got {s:#}")
};
Ok(s.is_utf8().into())
}
@ -140,10 +124,7 @@ pub fn is_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn to_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args);
let Value::String(s) = s else {
throw!(
*SYM_TYPE_ERROR,
"is_utf8 expected string argument, got {s:#}"
)
throw!(*SYM_TYPE_ERROR, "is_utf8 expected string argument, got {s:#}")
};
if s.is_utf8() {
Ok(s.into())
@ -156,10 +137,7 @@ pub fn to_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn to_utf8_lossy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args);
let Value::String(s) = s else {
throw!(
*SYM_TYPE_ERROR,
"is_utf8 expected string argument, got {s:#}"
)
throw!(*SYM_TYPE_ERROR, "is_utf8 expected string argument, got {s:#}")
};
Ok(s.to_utf8_lossy().into())
}
@ -168,10 +146,7 @@ pub fn to_utf8_lossy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn str_to_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args);
let Value::String(s) = s else {
throw!(
*SYM_TYPE_ERROR,
"str_to_bytes expected string argument, got {s:#}"
)
throw!(*SYM_TYPE_ERROR, "str_to_bytes expected string argument, got {s:#}")
};
Ok(s.as_bytes()
.iter()
@ -184,21 +159,47 @@ pub fn str_to_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 Value::List(b) = b else {
throw!(
*SYM_TYPE_ERROR,
"str_of_bytes expected list argument, got {b:#}"
)
throw!(*SYM_TYPE_ERROR, "str_of_bytes expected list argument, got {b:#}")
};
let bytes: Vec<u8> = b
.borrow()
.iter()
let bytes: Vec<u8> = b.borrow().iter()
.map(|v| match v {
Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8),
_ => throw!(
*SYM_VALUE_ERROR,
"str_of_bytes expected list of integers in 0..=255"
),
})
.collect::<Result<Vec<u8>>>()?;
_ => throw!(*SYM_TYPE_ERROR, "str_of_bytes expected list of integers in 0..=255"),
}).collect::<Result<Vec<u8>>>()?;
Ok(LString::from(bytes).into())
}
#[native_func(2)]
pub fn _format(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, fstr, fargs] = unpack_args!(args);
let (Value::String(fstr), Value::List(fargs)) = (&fstr, &fargs) else {
throw!(*SYM_TYPE_ERROR, "format expected string and list, got {fstr:#} and {fargs:#}")
};
let mut res = LString::new();
let mut bytes = fstr.bytes();
let mut faidx = 0;
while let Some(b) = bytes.next() {
if b == b'%' {
match bytes.next() {
Some(b'%') => res.push_byte(b'%'),
Some(b @ (b'#' | b'?')) => {
let fargs = fargs.borrow();
let Some(a) = fargs.get(faidx) else {
throw!(*SYM_TYPE_ERROR, "not enough args for format string")
};
faidx += 1;
if b == b'?' {
res += &lformat!("{a:#}");
} else {
res += &lformat!("{a}");
}
},
_ => throw!(*SYM_TYPE_ERROR, "invalid format code")
}
} else {
res.push_byte(b);
}
}
Ok(res.into())
}

View file

@ -1,14 +1,6 @@
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, parse_float, parse_int, symbol::SYM_TYPE_ERROR, throw, value::{ops::RatioExt, HashValue, Rational64, Value}, Vm};
use talc_macros::native_func;
use crate::unpack_args;
@ -22,27 +14,18 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("str", str_().into());
vm.set_global_name("repr", repr().into());
vm.set_global_name("symbol_name", symbol_name().into());
vm.set_global_name("symbol_of", symbol_of().into());
vm.set_global_name("symbol_exists", symbol_exists().into());
vm.set_global_name("cell", cell().into());
vm.set_global_name("uncell", uncell().into());
vm.set_global_name("cell_replace", cell_replace().into());
vm.set_global_name("cell_take", cell_take().into());
vm.set_global_name("func_state", func_state().into());
vm.set_global_name("func_arity", func_arity().into());
vm.set_global_name("func_name", func_name().into());
vm.set_global_name("apply", apply().into());
vm.set_global_name("compile", compile().into());
vm.set_global_name("cell_take", cell_replace().into());
}
//
// types
//
#[native_func(1, "type")]
#[native_func(1)]
pub fn type_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
Ok(val.get_type().into())
@ -57,7 +40,7 @@ pub fn is(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok((val.get_type() == ty).into())
}
#[native_func(2, "as")]
#[native_func(2)]
pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val, ty] = unpack_args!(args);
let Value::Symbol(ty) = ty else {
@ -81,75 +64,59 @@ pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
(Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64().into())),
(Value::Float(x), b"int") => Ok(Value::Int(x as i64)),
(Value::Float(x), b"ratio") => {
let r = Rational64::approximate_float(x).ok_or_else(|| {
exception!(
*SYM_VALUE_ERROR,
"float {x:?} could not be converted to ratio"
)
})?;
let r = Rational64::approximate_float(x)
.ok_or_else(|| exception!(*SYM_TYPE_ERROR, "float {x:?} could not be converted to ratio"))?;
Ok(Value::Ratio(r))
}
(Value::Float(x), b"complex") => Ok(Value::Complex(x.into())),
(Value::String(s), b"int") => parse_int(s.as_ref(), 10)
(Value::String(s), b"int")
=> parse_int(s.as_ref(), 10)
.map(i64::into)
.map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as integer")),
.map_err(|_| exception!(*SYM_TYPE_ERROR, "could not parse {s:#} as integer")),
(Value::String(s), b"float") => parse_float(s.as_ref())
(Value::String(s), b"float")
=> parse_float(s.as_ref())
.map(f64::into)
.map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as float")),
.map_err(|_| exception!(*SYM_TYPE_ERROR, "could not parse {s:#} as float")),
(v, _) => throw!(
*SYM_TYPE_ERROR,
"cannot convert value of type {} to type {}",
v.get_type().name(),
ty.name()
),
(v, _) => throw!(*SYM_TYPE_ERROR,
"cannot convert value of type {} to type {}", v.get_type().name(), ty.name())
}
}
pub fn copy_inner(value: Value) -> Result<Value> {
match value {
Value::Nil
| Value::Bool(_)
| Value::Symbol(_)
| Value::Int(_)
| Value::Ratio(_)
| Value::Float(_)
| Value::Complex(_)
| Value::Range(_)
| Value::String(_) => Ok(value),
Value::Nil | Value::Bool(_) | Value::Symbol(_)
| Value::Int(_) | Value::Ratio(_) | Value::Float(_)
| Value::Complex(_) | Value::Range(_) | Value::String(_)
=> Ok(value),
Value::Cell(c) => {
let c = Rc::unwrap_or_clone(c).take();
let c = copy_inner(c)?;
Ok(RefCell::new(c).into())
}
},
Value::List(l) => {
let l = Rc::unwrap_or_clone(l).take();
let v: Result<Vec<Value>> = l.into_iter().map(copy_inner).collect();
let v: Result<Vec<Value>> = l.into_iter()
.map(copy_inner)
.collect();
Ok(v?.into())
}
},
Value::Table(t) => {
let t = Rc::unwrap_or_clone(t).take();
let v: Result<HashMap<HashValue, Value>> = t
.into_iter()
let v: Result<HashMap<HashValue, Value>> = t.into_iter()
.map(|(k, v)| copy_inner(v).map(|v| (k, v)))
.collect();
Ok(v?.into())
}
},
Value::Native(ref n) => match n.copy_value()? {
Some(x) => Ok(x),
None => throw!(
*SYM_TYPE_ERROR,
"cannot copy value of type {}",
value.get_type().name()
),
},
_ => throw!(
*SYM_TYPE_ERROR,
"cannot copy value of type {}",
value.get_type().name()
),
None => throw!(*SYM_TYPE_ERROR,
"cannot copy value of type {}", value.get_type().name())
}
_ => throw!(*SYM_TYPE_ERROR,
"cannot copy value of type {}", value.get_type().name())
}
}
@ -163,7 +130,8 @@ pub fn copy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// strings
//
#[native_func(1, "str")]
#[native_func(1)]
pub fn str_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
Ok(lformat!("{val}").into())
@ -175,44 +143,11 @@ pub fn repr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(lformat!("{val:#}").into())
}
//
// symbols
//
#[native_func(1)]
pub fn symbol_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::Symbol(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_name: expected symbol")
};
Ok(s.name().into())
}
#[native_func(1)]
pub fn symbol_of(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::String(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
};
Ok(Symbol::get(s.as_ref()).into())
}
#[native_func(1)]
pub fn symbol_exists(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::String(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
};
match Symbol::try_get(s.as_ref()) {
Some(s) => Ok(s.into()),
None => Ok(Value::Nil),
}
}
//
// cells
//
#[native_func(1)]
pub fn cell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, value] = unpack_args!(args);
@ -223,7 +158,7 @@ pub fn cell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn uncell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, cell] = unpack_args!(args);
let Value::Cell(cell) = cell else {
throw!(*SYM_TYPE_ERROR, "uncell: value is not a cell")
throw!(*SYM_TYPE_ERROR, "value is not a cell")
};
Ok(Rc::unwrap_or_clone(cell).into_inner())
}
@ -232,7 +167,7 @@ pub fn uncell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn cell_replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, cell, value] = unpack_args!(args);
let Value::Cell(cell) = cell else {
throw!(*SYM_TYPE_ERROR, "cell_replace: value is not a cell")
throw!(*SYM_TYPE_ERROR, "value is not a cell")
};
Ok(cell.replace(value))
}
@ -241,90 +176,8 @@ pub fn cell_replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn cell_take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, cell] = unpack_args!(args);
let Value::Cell(cell) = cell else {
throw!(*SYM_TYPE_ERROR, "cell_take: value is not a cell")
throw!(*SYM_TYPE_ERROR, "value is not a cell")
};
Ok(cell.replace(Value::Nil))
}
//
// functions
//
#[native_func(1)]
pub fn func_state(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
match func {
Value::NativeFunc(_) => Ok(Value::Nil),
Value::Function(f) => {
let l: Vec<Value> = f.state.iter().map(|v| Value::Cell(v.clone())).collect();
Ok(l.into())
}
_ => throw!(
*SYM_TYPE_ERROR,
"closure_state: {func:#} is not a talc function"
),
}
}
#[native_func(1)]
pub fn func_arity(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
let Some(attrs) = func.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
};
Ok((attrs.arity as i64).into())
}
#[native_func(1)]
pub fn func_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
let Some(attrs) = func.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
};
if let Some(name) = attrs.name {
Ok(name.into())
} else {
Ok(Value::Nil)
}
}
#[native_func(2)]
pub fn apply(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func, lst] = unpack_args!(args);
if func.func_attrs().is_none() {
throw!(
*SYM_TYPE_ERROR,
"apply: first argument must be a function, found {func:#}"
)
}
let Value::List(l) = lst else {
throw!(
*SYM_TYPE_ERROR,
"apply: second argument must be a list, found {lst:#}"
)
};
let mut args = l.borrow().clone();
args.insert(0, func.clone());
vm.call_value(func, args)
}
#[native_func(1)]
pub fn compile(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, src] = unpack_args!(args);
let Value::String(src) = src else {
throw!(
*SYM_TYPE_ERROR,
"compile: argument must be a string, found {src:#}"
)
};
let src = src.to_str().map_err(|e| {
exception!(
*SYM_VALUE_ERROR,
"compile: argument must be valid unicode ({e})"
)
})?;
let ast = talc_lang::parser::parse(src)
.map_err(|e| exception!(symbol!("parse_error"), "{e}"))?;
let func = talc_lang::compiler::compile(&ast, None);
Ok(func.into())
}