This commit is contained in:
trimill 2024-11-04 13:25:31 -05:00
parent 801aaf7b78
commit ba7db1e91b
37 changed files with 4123 additions and 3667 deletions

3
format.sh Executable file
View file

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

10
rustfmt.toml Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,9 @@
use std::{rc::Rc, collections::HashMap, cell::RefCell, fmt::Display};
use crate::{lstring::LStr, symbol::{Symbol, SYM_DATA, SYM_MSG, SYM_TYPE}, value::{HashValue, Value}};
use crate::{
lstring::LStr,
symbol::{Symbol, SYM_DATA, SYM_MSG, SYM_TYPE},
value::{HashValue, Value},
};
use std::{cell::RefCell, collections::HashMap, fmt::Display, rc::Rc};
pub type Result<T> = std::result::Result<T, Exception>;
@ -12,19 +16,35 @@ pub struct Exception {
impl Exception {
pub fn new(ty: Symbol) -> Self {
Self { ty, msg: None, data: None }
Self {
ty,
msg: None,
data: None,
}
}
pub fn new_with_msg(ty: Symbol, msg: Rc<LStr>) -> Self {
Self { ty, msg: Some(msg), data: None }
Self {
ty,
msg: Some(msg),
data: None,
}
}
pub fn new_with_data(ty: Symbol, data: Value) -> Self {
Self { ty, msg: None, data: Some(data) }
Self {
ty,
msg: None,
data: Some(data),
}
}
pub fn new_with_msg_data(ty: Symbol, msg: Rc<LStr>, data: Value) -> Self {
Self { ty, msg: Some(msg), data: Some(data) }
Self {
ty,
msg: Some(msg),
data: Some(data),
}
}
pub fn from_table(table: &Rc<RefCell<HashMap<HashValue, Value>>>) -> Option<Self> {
@ -102,6 +122,3 @@ macro_rules! throw {
}
pub use throw;

View file

@ -2,13 +2,13 @@
#![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::allow_attributes)]
pub mod symbol;
pub mod parser;
pub mod value;
pub mod exception;
pub mod chunk;
pub mod compiler;
pub mod exception;
pub mod lstring;
pub mod parser;
pub mod symbol;
pub mod value;
mod vm;
pub use vm::Vm;

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,5 +1,5 @@
use core::fmt;
use std::num::{ParseIntError, ParseFloatError};
use std::num::{ParseFloatError, ParseIntError};
use thiserror::Error;
@ -9,26 +9,29 @@ use super::Span;
#[derive(Clone, Debug, Error)]
pub struct ParserError {
pub span: Span,
pub msg: String,
pub span: Span,
pub msg: String,
}
impl fmt::Display for ParserError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} | {}", self.span, self.msg)
}
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} | {}", self.span, self.msg)
}
}
pub trait SpanParserError {
type Output;
fn span_err(self, span: Span) -> Self::Output;
type Output;
fn span_err(self, span: Span) -> Self::Output;
}
impl<T, E: std::error::Error> SpanParserError for Result<T, E> {
type Output = Result<T, ParserError>;
fn span_err(self, span: Span) -> Self::Output {
self.map_err(|e| ParserError { span, msg: e.to_string() })
}
type Output = Result<T, ParserError>;
fn span_err(self, span: Span) -> Self::Output {
self.map_err(|e| ParserError {
span,
msg: e.to_string(),
})
}
}
#[derive(Clone, Copy, Debug, Error)]
@ -56,26 +59,29 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
let mut chars = src.chars();
while let Some(c) = chars.next() {
if c != '\\' { s.push_char(c); continue }
if c != '\\' {
s.push_char(c);
continue
}
let c = chars.next().ok_or(StrEscapeError::Eof)?;
match c {
'"' | '\'' | '\\' => s.push_char(c),
'0' => s.push_char('\0'),
'a' => s.push_char('\x07'),
'b' => s.push_char('\x08'),
't' => s.push_char('\t'),
'n' => s.push_char('\n'),
'v' => s.push_char('\x0b'),
'f' => s.push_char('\x0c'),
'r' => s.push_char('\r'),
'e' => s.push_char('\x1b'),
'0' => s.push_char('\0'),
'a' => s.push_char('\x07'),
'b' => s.push_char('\x08'),
't' => s.push_char('\t'),
'n' => s.push_char('\n'),
'v' => s.push_char('\x0b'),
'f' => s.push_char('\x0c'),
'r' => s.push_char('\r'),
'e' => s.push_char('\x1b'),
'x' => {
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
s.push_byte((n1 * 16 + n2) as u8);
},
}
'u' => {
let Some('{') = chars.next() else {
return Err(StrEscapeError::MissingBrace)
@ -85,7 +91,9 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
let Some(c) = chars.next() else {
return Err(StrEscapeError::UnicodeEof)
};
if c == '}' { break }
if c == '}' {
break
}
if n > 0x10ffff {
return Err(StrEscapeError::CodepointTooLarge)
}
@ -93,8 +101,7 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
}
let ch = char::from_u32(n).ok_or(StrEscapeError::InvalidCodepoint(n))?;
s.push_char(ch);
},
}
c => return Err(StrEscapeError::Invalid(c)),
}
}
@ -123,14 +130,14 @@ pub fn parse_int<'a, S: Into<&'a LStr>>(f: S, radix: u32) -> Result<i64, ParseIn
}
pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result<i64, ParseIntError> {
let f = f.into();
match f.chars().nth(2) {
Some('x') => parse_int(&f[2..], 16),
Some('o') => parse_int(&f[2..], 8),
Some('s') => parse_int(&f[2..], 6),
Some('b') => parse_int(&f[2..], 2),
_ => parse_int(f, 10),
}
let f = f.into();
match f.chars().nth(2) {
Some('x') => parse_int(&f[2..], 16),
Some('o') => parse_int(&f[2..], 8),
Some('s') => parse_int(&f[2..], 6),
Some('b') => parse_int(&f[2..], 2),
_ => parse_int(f, 10),
}
}
pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
@ -151,14 +158,14 @@ pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
x /= radix as u64;
let mut c = char::from_digit(m as u32, radix).unwrap();
if upper { c.make_ascii_uppercase(); }
if upper {
c.make_ascii_uppercase();
}
result.push(c as u8);
if x == 0 {
break;
break
}
}
result[begin..].reverse();
LString::from(result)
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,11 +1,16 @@
use std::cmp::Ordering;
use talc_lang::{exception::{exception, Result}, symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{function::NativeFunc, Value}, vmcall, Vm};
use talc_lang::{
exception::{exception, Result},
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{function::NativeFunc, Value},
vmcall, Vm,
};
use talc_macros::native_func;
use crate::unpack_args;
pub fn load(vm: &mut Vm) {
vm.set_global_name("push", push().into());
vm.set_global_name("pop", pop().into());
@ -55,7 +60,10 @@ pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match &col {
Value::List(list) => list.borrow_mut().clear(),
Value::Table(table) => table.borrow_mut().clear(),
_ => throw!(*SYM_TYPE_ERROR, "clear expected list or table, found {col:#}")
_ => throw!(
*SYM_TYPE_ERROR,
"clear expected list or table, found {col:#}"
),
}
Ok(col)
}
@ -63,7 +71,10 @@ pub fn clear(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
fn call_comparison(vm: &mut Vm, by: Value, l: Value, r: Value) -> Result<i64> {
let ord = vmcall!(vm; by, l, r)?;
let Value::Int(ord) = ord else {
throw!(*SYM_TYPE_ERROR, "comparison function should return an integer")
throw!(
*SYM_TYPE_ERROR,
"comparison function should return an integer"
)
};
Ok(ord)
}
@ -82,14 +93,14 @@ fn partition(vals: &mut [Value]) -> (usize, usize) {
vals.swap(eq, lt);
lt += 1;
eq += 1;
},
}
Some(Ordering::Greater) => {
vals.swap(eq, gt);
gt -= 1;
},
}
Some(Ordering::Equal) | None => {
eq += 1;
},
}
}
}
@ -110,14 +121,14 @@ fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, u
vals.swap(eq, lt);
lt += 1;
eq += 1;
},
}
1.. => {
vals.swap(eq, gt);
gt -= 1;
},
}
0 => {
eq += 1;
},
}
}
}
@ -127,8 +138,8 @@ fn partition_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<(usize, u
fn insertion(vals: &mut [Value]) {
for i in 0..vals.len() {
let mut j = i;
while j > 0 && vals[j-1] > vals[j] {
vals.swap(j, j-1);
while j > 0 && vals[j - 1] > vals[j] {
vals.swap(j, j - 1);
j -= 1;
}
}
@ -138,11 +149,11 @@ fn insertion_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<()> {
for i in 0..vals.len() {
let mut j = i;
while j > 0 {
let ord = call_comparison(vm, by.clone(), vals[j-1].clone(), vals[j].clone())?;
let ord = call_comparison(vm, by.clone(), vals[j - 1].clone(), vals[j].clone())?;
if ord <= 0 {
break;
break
}
vals.swap(j, j-1);
vals.swap(j, j - 1);
j -= 1;
}
}
@ -165,7 +176,7 @@ fn sort_inner(vals: &mut [Value], by: Option<&Value>, vm: &mut Vm) -> Result<()>
None => partition(vals),
};
sort_inner(&mut vals[..lt], by, vm)?;
sort_inner(&mut vals[(gt+1)..], by, vm)
sort_inner(&mut vals[(gt + 1)..], by, vm)
}
#[native_func(1)]
@ -202,7 +213,10 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Some(Ordering::Greater) => Ok(Value::Int(1)),
Some(Ordering::Equal) => Ok(Value::Int(0)),
Some(Ordering::Less) => Ok(Value::Int(-1)),
None => throw!(*SYM_VALUE_ERROR, "values returned from sort key were incomparable"),
None => throw!(
*SYM_VALUE_ERROR,
"values returned from sort key were incomparable"
),
}
};
let nf = NativeFunc::new(Box::new(f), 2, symbol!("inner[sort_key]")).into();
@ -212,32 +226,32 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(2)]
pub fn pair(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, y] = unpack_args!(args);
Ok(vec![x, y].into())
let [_, x, y] = unpack_args!(args);
Ok(vec![x, y].into())
}
#[native_func(1)]
pub fn fst(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "fst: expected list")
};
let l = l.borrow();
let [x, _] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "fst: list must have length 2")
};
Ok(x.clone())
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "fst: expected list")
};
let l = l.borrow();
let [x, _] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "fst: list must have length 2")
};
Ok(x.clone())
}
#[native_func(1)]
pub fn snd(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "snd: expected list")
};
let l = l.borrow();
let [_, y] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "snd: list must have length 2")
};
Ok(y.clone())
let [_, l] = unpack_args!(args);
let Value::List(l) = l else {
throw!(*SYM_TYPE_ERROR, "snd: expected list")
};
let l = l.borrow();
let [_, y] = l.as_slice() else {
throw!(*SYM_VALUE_ERROR, "snd: list must have length 2")
};
Ok(y.clone())
}

View file

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

View file

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

View file

@ -1,414 +1,477 @@
use std::io::Write;
use talc_lang::{exception::{exception, Result}, lstring::{LStr, LString}, parser::{parse_int, to_lstring_radix}, symbol::SYM_TYPE_ERROR, throw, value::{Complex64, Rational64, Value}, Vm};
use talc_lang::{
exception::{exception, Result},
lstring::{LStr, LString},
parser::{parse_int, to_lstring_radix},
symbol::SYM_TYPE_ERROR,
throw,
value::{Complex64, Rational64, Value},
Vm,
};
use talc_macros::native_func;
use crate::{unpack_args, SYM_FORMAT_ERROR};
#[derive(Clone, Copy, Debug)]
enum FmtIndex {
Imm(usize),
Arg(usize),
NextArg,
Imm(usize),
Arg(usize),
NextArg,
}
#[derive(Clone, Copy, Debug)]
enum Align {
Left,
Center,
Right,
Left,
Center,
Right,
}
#[derive(Clone, Copy, Debug, Default)]
enum FmtType {
#[default]
Str,
Repr,
Hex(bool),
Oct,
Sex,
Bin,
Exp(bool),
#[default]
Str,
Repr,
Hex(bool),
Oct,
Sex,
Bin,
Exp(bool),
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
enum FmtSign {
Plus,
Minus,
#[default]
None
Plus,
Minus,
#[default]
None,
}
// [idx][:[align][sign][width][.prec][ty]]
#[derive(Clone, Copy, Debug, Default)]
struct FmtCode {
arg_idx: Option<usize>,
align: Option<(Align, char)>,
sign: FmtSign,
width: Option<FmtIndex>,
prec: Option<FmtIndex>,
ty: FmtType,
arg_idx: Option<usize>,
align: Option<(Align, char)>,
sign: FmtSign,
width: Option<FmtIndex>,
prec: Option<FmtIndex>,
ty: FmtType,
}
struct FmtParser<'p> {
code: &'p LStr,
pos: usize,
code: &'p LStr,
pos: usize,
}
impl<'p> FmtParser<'p> {
fn new(code: &'p LStr) -> Self {
Self {
code,
pos: 0
}
}
fn new(code: &'p LStr) -> Self {
Self { code, pos: 0 }
}
fn next(&mut self) -> Option<char> {
let c = &self.code[self.pos..].chars().next()?;
self.pos += c.len_utf8();
Some(*c)
}
fn next(&mut self) -> Option<char> {
let c = &self.code[self.pos..].chars().next()?;
self.pos += c.len_utf8();
Some(*c)
}
fn peek(&mut self) -> Option<char> {
self.code[self.pos..].chars().next()
}
fn peek(&mut self) -> Option<char> {
self.code[self.pos..].chars().next()
}
fn try_next(&mut self, chs: &[char]) -> Option<char> {
if self.peek().is_some_and(|c| chs.contains(&c)) {
self.next()
} else {
None
}
}
fn try_next(&mut self, chs: &[char]) -> Option<char> {
if self.peek().is_some_and(|c| chs.contains(&c)) {
self.next()
} else {
None
}
}
fn next_number_str(&mut self) -> &LStr {
let start = self.pos;
while matches!(self.peek(), Some('0'..='9' | '_')) {
self.next();
}
&self.code[start..self.pos]
}
fn next_number_str(&mut self) -> &LStr {
let start = self.pos;
while matches!(self.peek(), Some('0'..='9' | '_')) {
self.next();
}
&self.code[start..self.pos]
}
fn next_usize(&mut self) -> Result<Option<usize>> {
let s = self.next_number_str();
if s.is_empty() { return Ok(None) }
let Ok(i) = parse_int(s, 10) else {
throw!(*SYM_FORMAT_ERROR,
"code {{{}}}: invalid integer", self.code)
};
if i < 0 {
throw!(*SYM_FORMAT_ERROR,
"code {{{}}}: integer may not be negative", self.code)
}
Ok(Some(i as usize))
}
fn next_usize(&mut self) -> Result<Option<usize>> {
let s = self.next_number_str();
if s.is_empty() {
return Ok(None)
}
let Ok(i) = parse_int(s, 10) else {
throw!(*SYM_FORMAT_ERROR, "code {{{}}}: invalid integer", self.code)
};
if i < 0 {
throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: integer may not be negative",
self.code
)
}
Ok(Some(i as usize))
}
fn next_fmt_index(&mut self) -> Result<Option<FmtIndex>> {
let dollar = self.try_next(&['$']).is_some();
let num = self.next_usize()?;
match (dollar, num) {
(true, None) => Ok(Some(FmtIndex::NextArg)),
(true, Some(n)) => Ok(Some(FmtIndex::Arg(n))),
(false, None) => Ok(None),
(false, Some(n)) => Ok(Some(FmtIndex::Imm(n))),
}
}
fn next_fmt_index(&mut self) -> Result<Option<FmtIndex>> {
let dollar = self.try_next(&['$']).is_some();
let num = self.next_usize()?;
match (dollar, num) {
(true, None) => Ok(Some(FmtIndex::NextArg)),
(true, Some(n)) => Ok(Some(FmtIndex::Arg(n))),
(false, None) => Ok(None),
(false, Some(n)) => Ok(Some(FmtIndex::Imm(n))),
}
}
fn next_align(&mut self) -> Result<Option<(Align, char)>> {
let align = match self.peek() {
Some('<') => Align::Left,
Some('^') => Align::Center,
Some('>') => Align::Right,
_ => return Ok(None),
};
self.next();
let Some(c) = self.next() else {
throw!(*SYM_FORMAT_ERROR,
"code {{{}}}: alignment without fill character", self.code)
};
Ok(Some((align, c)))
}
fn next_align(&mut self) -> Result<Option<(Align, char)>> {
let align = match self.peek() {
Some('<') => Align::Left,
Some('^') => Align::Center,
Some('>') => Align::Right,
_ => return Ok(None),
};
self.next();
let Some(c) = self.next() else {
throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: alignment without fill character",
self.code
)
};
Ok(Some((align, c)))
}
fn next_sign(&mut self) -> FmtSign {
match self.try_next(&['+', '-']) {
Some('+') => FmtSign::Plus,
Some('-') => FmtSign::Minus,
_ => FmtSign::None,
}
}
fn next_sign(&mut self) -> FmtSign {
match self.try_next(&['+', '-']) {
Some('+') => FmtSign::Plus,
Some('-') => FmtSign::Minus,
_ => FmtSign::None,
}
}
fn next_type(&mut self) -> Result<FmtType> {
let ty = match self.peek() {
None => return Ok(FmtType::Str),
Some('?') => FmtType::Repr,
Some('x') => FmtType::Hex(false),
Some('X') => FmtType::Hex(true),
Some('o') => FmtType::Oct,
Some('s') => FmtType::Sex,
Some('b') => FmtType::Bin,
Some('e') => FmtType::Exp(false),
Some('E') => FmtType::Exp(true),
_ => throw!(*SYM_FORMAT_ERROR,
"code {{{}}}: invalid format type", self.code),
};
self.next();
Ok(ty)
}
fn next_type(&mut self) -> Result<FmtType> {
let ty = match self.peek() {
None => return Ok(FmtType::Str),
Some('?') => FmtType::Repr,
Some('x') => FmtType::Hex(false),
Some('X') => FmtType::Hex(true),
Some('o') => FmtType::Oct,
Some('s') => FmtType::Sex,
Some('b') => FmtType::Bin,
Some('e') => FmtType::Exp(false),
Some('E') => FmtType::Exp(true),
_ => throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: invalid format type",
self.code
),
};
self.next();
Ok(ty)
}
fn next_code_end(&mut self) -> Result<()> {
match self.peek() {
Some(c) => throw!(*SYM_FORMAT_ERROR,
"code {{{}}}: expected end of code, found character '{}'", self.code, c),
None => Ok(())
}
}
fn next_code_end(&mut self) -> Result<()> {
match self.peek() {
Some(c) => throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: expected end of code, found character '{}'",
self.code,
c
),
None => Ok(()),
}
}
fn read_format(&mut self) -> Result<FmtCode> {
let mut code = FmtCode {
arg_idx: self.next_usize()?,
..FmtCode::default()
};
if self.try_next(&[':']).is_none() {
self.next_code_end()?;
return Ok(code)
}
code.align = self.next_align()?;
code.sign = self.next_sign();
code.width = self.next_fmt_index()?;
if self.try_next(&['.']).is_some() {
code.prec = self.next_fmt_index()?;
}
code.ty = self.next_type()?;
self.next_code_end()?;
Ok(code)
}
fn read_format(&mut self) -> Result<FmtCode> {
let mut code = FmtCode {
arg_idx: self.next_usize()?,
..FmtCode::default()
};
if self.try_next(&[':']).is_none() {
self.next_code_end()?;
return Ok(code)
}
code.align = self.next_align()?;
code.sign = self.next_sign();
code.width = self.next_fmt_index()?;
if self.try_next(&['.']).is_some() {
code.prec = self.next_fmt_index()?;
}
code.ty = self.next_type()?;
self.next_code_end()?;
Ok(code)
}
}
fn get_arg<'a>(args: &'a [Value], n: Option<usize>, faidx: &mut usize)
-> Result<&'a Value> {
let i = match n {
Some(n) => n,
None => {
let i = *faidx;
*faidx += 1;
i
}
};
if i >= args.len() {
throw!(*SYM_FORMAT_ERROR, "missing arguments: expected at least {}", i+1)
}
Ok(&args[i])
fn get_arg<'a>(args: &'a [Value], n: Option<usize>, faidx: &mut usize) -> Result<&'a Value> {
let i = match n {