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,7 +67,9 @@ 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 {
@ -71,7 +85,9 @@ impl Highlighter for TalcHelper {
};
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)
@ -103,66 +119,85 @@ impl Validator for TalcHelper {
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;
match k {
K::Eof => break,
K::LParen
| K::LBrack
| K::LBrace
| K::If
| K::While
| K::For
| K::Try
=> delims.push(token.kind),
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)]
@ -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)));

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
}
}
@ -150,10 +162,16 @@ 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::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)),
@ -163,8 +181,11 @@ impl std::fmt::Display for Instruction {
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,8 +1,8 @@
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;
@ -15,7 +15,9 @@ enum ResolveOutcome {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum VarKind {
Local(usize), Closed(usize), Global
Local(usize),
Closed(usize),
Global,
}
#[derive(Debug, Clone)]
@ -26,7 +28,9 @@ pub struct Var {
#[derive(Clone, Copy, PartialEq, Eq)]
enum CompilerMode {
Function, Repl, Module,
Function,
Repl,
Module,
}
struct Compiler<'a> {
@ -55,10 +59,13 @@ 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 {
scope.insert(
*SYM_SELF,
Var {
name: *SYM_SELF,
kind: VarKind::Local(0),
});
},
);
Self {
mode: CompilerMode::Function,
parent: None,
@ -76,7 +83,10 @@ 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()
};
@ -98,7 +108,10 @@ 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()
};
@ -120,7 +133,7 @@ impl<'a> Compiler<'a> {
// TODO closure
(
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()),
self.closes
self.closes,
)
}
@ -129,9 +142,10 @@ impl<'a> Compiler<'a> {
(
// 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()
self.scope
.into_iter()
.filter_map(|(_, v)| (v.kind == VarKind::Global).then_some(v.name))
.collect(),
)
}
@ -154,26 +168,32 @@ 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::Dup
| I::Const(_) | I::Int(_)
| I::Nil | I::Bool(_)
| I::Symbol(_) | I::LoadLocal(_)
| I::LoadClosedLocal(_)
| I::LoadUpvalue(_)
)
);
@ -181,11 +201,11 @@ impl<'a> Compiler<'a> {
// 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)));
@ -226,7 +246,6 @@ impl<'a> Compiler<'a> {
}
}
//
// variables
//
@ -272,7 +291,7 @@ impl<'a> Compiler<'a> {
fn declare_local(&mut self, name: Symbol) -> usize {
let local = Var {
name,
kind: VarKind::Local(self.local_count)
kind: VarKind::Local(self.local_count),
};
self.local_count += 1;
let shadowed = self.scope.insert(name, local);
@ -294,7 +313,7 @@ impl<'a> Compiler<'a> {
fn declare_global(&mut self, name: Symbol) {
let global = Var {
name,
kind: VarKind::Global
kind: VarKind::Global,
};
let shadowed = self.scope.insert(name, global);
self.shadowed.push((name, shadowed));
@ -331,8 +350,6 @@ impl<'a> Compiler<'a> {
}
}
//
// Expressions
//
@ -340,7 +357,9 @@ impl<'a> Compiler<'a> {
fn expr(&mut self, e: &Expr) {
let Expr { kind, .. } = e;
match kind {
ExprKind::Block(xs) if xs.is_empty() => { self.emit(I::Nil); },
ExprKind::Block(xs) if xs.is_empty() => {
self.emit(I::Nil);
}
ExprKind::Block(xs) => {
let scope = self.begin_scope();
for x in &xs[0..xs.len() - 1] {
@ -349,32 +368,32 @@ impl<'a> Compiler<'a> {
}
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),
}
@ -577,17 +594,18 @@ impl<'a> Compiler<'a> {
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"),
}
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
panic!("upvalue resolved to none or global")
}
}
}
@ -605,18 +623,22 @@ impl<'a> Compiler<'a> {
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,7 +88,9 @@ 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];
if (0..=0x7f).contains(&last) {
@ -77,9 +98,13 @@ fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
}
'case: {
if !is_continue(last) { break 'case }
if !is_continue(last) {
break 'case
}
if len < 2 { break 'case }
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]) {
@ -89,7 +114,9 @@ fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
break 'case
}
if len < 3 { break 'case }
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]) {
@ -99,7 +126,9 @@ fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result<char, u8>)> {
break 'case
}
if len < 4 { break 'case }
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]) {
@ -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> {
@ -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> {}
@ -646,7 +717,6 @@ impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> {
}
}
#[derive(Clone)]
pub struct Chars<'a>(LosslessChars<'a>);
@ -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())
}
}
@ -863,17 +943,20 @@ impl LStr {
}
pub fn strip_prefix(&self, prefix: &LStr) -> Option<&LStr> {
self.as_bytes().strip_prefix(prefix.as_bytes()).map(LStr::from_bytes)
self.as_bytes()
.strip_prefix(prefix.as_bytes())
.map(LStr::from_bytes)
}
pub fn strip_suffix(&self, suffix: &LStr) -> Option<&LStr> {
self.as_bytes().strip_suffix(suffix.as_bytes()).map(LStr::from_bytes)
self.as_bytes()
.strip_suffix(suffix.as_bytes())
.map(LStr::from_bytes)
}
pub fn is_identifier(&self) -> bool {
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,16 +6,35 @@ 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)]
@ -132,7 +151,7 @@ impl LValue {
writeln!(w, "index")?;
l.write_to(w, depth)?;
r.write_to(w, depth)
},
}
}
}
}
@ -153,12 +172,12 @@ impl Expr {
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:?}")?;
@ -167,15 +186,15 @@ impl Expr {
}
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())?;
@ -186,12 +205,12 @@ impl Expr {
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)?;
@ -199,7 +218,7 @@ impl Expr {
arg.write_to(w, depth)?;
}
Ok(())
},
}
ExprKind::AssocFnCall(d, f, a) => {
writeln!(w, "assoc call {}", f.name())?;
d.write_to(w, depth)?;
@ -207,26 +226,26 @@ impl Expr {
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 {
@ -234,7 +253,7 @@ impl Expr {
v.write_to(w, depth)?;
}
Ok(())
},
}
ExprKind::Return(e) => {
writeln!(w, "return")?;
e.write_to(w, depth)
@ -293,4 +312,3 @@ impl fmt::Display for Expr {
self.write_to(f, 0)
}
}

View file

@ -227,11 +227,15 @@ impl<'s> Lexer<'s> {
fn filter_char(&mut self, c: Option<char>, advance: bool) -> Result<char> {
match c {
Some(c) if c.is_control() && !matches!(c, '\n' | '\r' | '\t') => Err(self.invalid_char(c)),
Some(c) if c.is_control() && !matches!(c, '\n' | '\r' | '\t') => {
Err(self.invalid_char(c))
}
Some(c) => {
if advance { self.pos = self.pos.advance(c); }
if advance {
self.pos = self.pos.advance(c);
}
Ok(c)
},
}
None => Ok('\0'),
}
}
@ -252,7 +256,8 @@ impl<'s> Lexer<'s> {
}
fn and_peek(&mut self) -> Result<char> {
self.next()?; self.peek()
self.next()?;
self.peek()
}
fn emit(&self, kind: TokenKind) -> Result<Token<'s>> {
@ -344,10 +349,13 @@ impl<'s> Lexer<'s> {
self.next()?;
}
match self.peek()? {
'e' => { self.next()?; has_e = true }
'e' => {
self.next()?;
has_e = true;
}
'i' => return self.next_imag(),
c if is_xid_start(c) => return self.unexpected(),
_ => return self.emit(K::Float)
_ => return self.emit(K::Float),
}
}
if matches!(self.peek()?, '+' | '-') {
@ -359,21 +367,35 @@ impl<'s> Lexer<'s> {
match self.peek()? {
'i' => self.next_imag(),
c if is_xid_start(c) => self.unexpected(),
_ => self.emit(K::Float)
_ => self.emit(K::Float),
}
}
fn next_number(&mut self) -> Result<Token<'s>> {
if self.next()? == '0' {
while self.peek()? == '_' { self.next()?; }
while self.peek()? == '_' {
self.next()?;
}
match self.peek()? {
'x' => { self.next()?; return self.next_int_base(16) },
'o' => { self.next()?; return self.next_int_base(8) },
's' => { self.next()?; return self.next_int_base(6) },
'b' => { self.next()?; return self.next_int_base(2) },
'x' => {
self.next()?;
return self.next_int_base(16)
}
'o' => {
self.next()?;
return self.next_int_base(8)
}
's' => {
self.next()?;
return self.next_int_base(6)
}
'b' => {
self.next()?;
return self.next_int_base(2)
}
'0'..='9' | '.' | 'e' | 'i' => (),
c if is_xid_start(c) => return self.unexpected(),
_ => return self.emit(K::Integer)
_ => return self.emit(K::Integer),
}
}
while matches!(self.peek()?, '_' | '0'..='9') {
@ -381,7 +403,10 @@ impl<'s> Lexer<'s> {
}
match self.peek()? {
'r' => todo!("arbitrary radix integer literals"),
'e' => { self.next()?; self.next_float(true) },
'e' => {
self.next()?;
self.next_float(true)
}
'i' => self.next_imag(),
'.' => {
if self.peek_n(1) == Some('.') {
@ -390,9 +415,9 @@ impl<'s> Lexer<'s> {
self.next()?;
self.next_float(false)
}
},
}
c if is_xid_start(c) => self.unexpected(),
_ => self.emit(K::Integer)
_ => self.emit(K::Integer),
}
}
@ -403,7 +428,9 @@ impl<'s> Lexer<'s> {
'\0' => return self.unexpected(),
'"' if double_quote => break,
'\'' if !double_quote => break,
'\\' if double_quote => { self.next()?; },
'\\' if double_quote => {
self.next()?;
}
_ => (),
}
}
@ -450,7 +477,7 @@ impl<'s> Lexer<'s> {
self.next_token()
}
_ => self.emit(K::Backslash),
}
},
// arithmetic
'+' => match self.and_peek()? {
'+' => match self.and_peek()? {
@ -491,15 +518,15 @@ impl<'s> Lexer<'s> {
'&' => match self.and_peek()? {
'=' => self.and_emit(K::HashAmperEqual),
_ => self.emit(K::HashAmper),
}
},
'^' => match self.and_peek()? {
'=' => self.and_emit(K::HashCaretEqual),
_ => self.emit(K::HashCaret),
}
},
'|' => match self.and_peek()? {
'=' => self.and_emit(K::HashPipeEqual),
_ => self.emit(K::HashPipe),
}
},
'!' => self.line_comment(),
_ => self.unexpected(),
},
@ -545,7 +572,7 @@ impl<'s> Lexer<'s> {
':' => match self.and_peek()? {
c if is_xid_start(c) || c == '"' || c == '\'' => self.next_symbol(),
_ => self.emit(K::Colon),
}
},
'0'..='9' => self.next_number(),
c if is_xid_start(c) => self.next_ident(),
'"' | '\'' => self.next_string(),

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()),
}

View file

@ -1,12 +1,18 @@
use std::iter::Peekable;
use crate::{
symbol::{Symbol, SYM_DOLLAR_SIGN},
value::Value,
};
use crate::{symbol::{Symbol, SYM_DOLLAR_SIGN}, value::Value};
use super::{ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, UnaryOp}, parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span, SpanParserError, Token, TokenKind};
use super::{
ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, UnaryOp},
parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span, SpanParserError,
Token, TokenKind,
};
use num_complex::Complex64;
use TokenKind as T;
use ExprKind as E;
use TokenKind as T;
type Result<T> = std::result::Result<T, ParserError>;
@ -37,7 +43,6 @@ macro_rules! try_next {
match t.kind {
$pat => Some($self.next()?),
_ => None,
}
}};
}
@ -135,19 +140,13 @@ impl BinaryOp {
| BinaryOp::Le => (10, 10),
BinaryOp::Concat => (20, 25),
BinaryOp::Append => (30, 35),
BinaryOp::Range
| BinaryOp::RangeIncl => (40, 40),
BinaryOp::Range | BinaryOp::RangeIncl => (40, 40),
BinaryOp::BitOr => (50, 55),
BinaryOp::BitXor => (60, 65),
BinaryOp::BitAnd => (70, 75),
BinaryOp::Shl
| BinaryOp::Shr => (80, 85),
BinaryOp::Add
| BinaryOp::Sub => (90, 95),
BinaryOp::Mul
| BinaryOp::Div
| BinaryOp::IntDiv
| BinaryOp::Mod => (100, 105),
BinaryOp::Shl | BinaryOp::Shr => (80, 85),
BinaryOp::Add | BinaryOp::Sub => (90, 95),
BinaryOp::Mul | BinaryOp::Div | BinaryOp::IntDiv | BinaryOp::Mod => (100, 105),
BinaryOp::Pow => (125, 120),
}
}
@ -160,12 +159,9 @@ fn b<T>(t: T) -> Box<T> {
impl TokenKind {
fn expr_first(self) -> bool {
matches!(self,
| T::Return
| T::Var
matches!(self, |T::Return| T::Var
| T::Global
| T::Fn
| T::Not
| T::Fn | T::Not
| T::Backslash
| T::Colon
| T::Minus
@ -174,31 +170,28 @@ impl TokenKind {
| T::LBrack
| T::LBrace
| T::Dollar
| T::Do
| T::If
| T::Do | T::If
| T::While
| T::For
| T::Try
| T::For | T::Try
| T::Integer
| T::Float
| T::Imaginary
| T::String
| T::Symbol
| T::True
| T::False
| T::Nil
)
| T::True | T::False
| T::Nil)
}
}
struct Parser<'s> {
lexer: Peekable<Lexer<'s>>,
}
impl<'s> Parser<'s> {
fn new(src: &'s str) -> Self {
Self { lexer: Lexer::new(src).peekable() }
Self {
lexer: Lexer::new(src).peekable(),
}
}
fn next(&mut self) -> Result<Token<'s>> {
@ -209,7 +202,6 @@ impl<'s> Parser<'s> {
self.lexer.peek().unwrap().clone()
}
fn parse_table_items(&mut self) -> Result<Vec<(Expr, Expr)>> {
let mut items = Vec::new();
while self.peek()?.kind.expr_first() {
@ -270,11 +262,13 @@ impl<'s> Parser<'s> {
let mut outer_span = self.peek()?.span;
loop {
let tok = expect!(self, T::Catch | T::End);
if tok.kind == T::End { break }
if tok.kind == T::End {
break
}
let types = match try_next!(self, T::Star) {
Some(_) => None,
None => Some(self.parse_symbol_list()?)
None => Some(self.parse_symbol_list()?),
};
let name = match try_next!(self, T::In) {
@ -286,7 +280,12 @@ impl<'s> Parser<'s> {
let body = self.parse_block()?;
let span = tok.span + body.span;
blocks.push(CatchBlock { span, name, types, body });
blocks.push(CatchBlock {
span,
name,
types,
body,
});
outer_span += span;
}
Ok((blocks, outer_span))
@ -311,9 +310,8 @@ impl<'s> Parser<'s> {
let span = span + elif_body.span;
Ok(E::If(b(cond), b(body), Some(b(elif_body))).span(span))
}
_ => unreachable!("parse_if_stmt_chain: guaranteed by expect!")
_ => unreachable!("parse_if_stmt_chain: guaranteed by expect!"),
}
}
fn parse_term_not_ident(&mut self) -> Result<Expr> {
@ -323,23 +321,23 @@ impl<'s> Parser<'s> {
let e = self.parse_expr()?;
expect!(self, T::RParen);
Ok(e)
},
}
T::LBrack => {
let args = self.parse_expr_list()?;
let end = expect!(self, T::RBrack);
Ok(E::List(args).span(tok.span + end.span))
},
}
T::LBrace => {
let args = self.parse_table_items()?;
let end = expect!(self, T::RBrace);
Ok(E::Table(args).span(tok.span + end.span))
},
}
T::Dollar => Ok(E::Ident(*SYM_DOLLAR_SIGN).span(tok.span)),
T::Do => {
let b = self.parse_block()?;
expect!(self, T::End);
Ok(b)
},
}
T::If => self.parse_if_stmt_chain(),
T::While => {
let cond = self.parse_expr()?;
@ -348,7 +346,7 @@ impl<'s> Parser<'s> {
let end = expect!(self, T::End);
let span = cond.span + end.span;
Ok(E::While(b(cond), b(body)).span(span))
},
}
T::For => {
let var = expect!(self, T::Identifier);
expect!(self, T::In);
@ -358,27 +356,24 @@ impl<'s> Parser<'s> {
let end = expect!(self, T::End);
let span = var.span + end.span;
Ok(E::For(Symbol::get(var.content), b(iter), b(body)).span(span))
},
}
T::Try => {
let body = self.parse_block()?;
let (catch, span) = self.parse_catch_blocks()?;
Ok(E::Try(b(body), catch).span(tok.span + span))
},
}
T::Integer => {
let n = parse_int_literal(tok.content)
.span_err(tok.span)?;
let n = parse_int_literal(tok.content).span_err(tok.span)?;
Ok(E::Literal(n.into()).span(tok.span))
},
}
T::Float => {
let x = parse_float(tok.content)
.span_err(tok.span)?;
let x = parse_float(tok.content).span_err(tok.span)?;
Ok(E::Literal(x.into()).span(tok.span))
},
}
T::Imaginary => {
let x = parse_float(&tok.content[..tok.content.len()-1])
.span_err(tok.span)?;
let x = parse_float(&tok.content[..tok.content.len() - 1]).span_err(tok.span)?;
Ok(E::Literal(Complex64::new(0.0, x).into()).span(tok.span))
},
}
T::String => {
let inner = &tok.content[1..tok.content.len() - 1];
let s = if &tok.content[..1] == "\"" {
@ -387,23 +382,26 @@ impl<'s> Parser<'s> {
inner.into()
};
Ok(E::Literal(s.into()).span(tok.span))
},
}
T::Symbol => {
let inner = &tok.content[1..];
let s = match inner.chars().next() {
Some('\'') => Symbol::get(&inner[1..inner.len() - 1]),
Some('\"') => Symbol::get(
&parse_str_escapes(&inner[1..inner.len()-1])
.span_err(tok.span)?
&parse_str_escapes(&inner[1..inner.len() - 1]).span_err(tok.span)?,
),
_ => Symbol::get(inner),
};
Ok(E::Literal(s.into()).span(tok.span))
},
}
T::True => Ok(E::Literal(Value::Bool(true)).span(tok.span)),
T::False => Ok(E::Literal(Value::Bool(false)).span(tok.span)),
T::Nil => Ok(E::Literal(Value::Nil).span(tok.span)),
t => throw!(tok.span, "unexpected token {}, expected expression", t.name()),
t => throw!(
tok.span,
"unexpected token {}, expected expression",
t.name()
),
}
}
@ -489,8 +487,7 @@ impl<'s> Parser<'s> {
let rhs = self.parse_precedence(rp)?;
span += rhs.span;
lhs = E::BinaryOp(op, Box::new(lhs), Box::new(rhs))
.span(span);
lhs = E::BinaryOp(op, Box::new(lhs), Box::new(rhs)).span(span);
}
Ok(lhs)
@ -499,19 +496,27 @@ impl<'s> Parser<'s> {
fn parse_lambda(&mut self) -> Result<Expr> {
let tok = try_next!(self, T::Backslash | T::Colon);
match tok {
Some(Token { kind: T::Backslash, span, .. }) => {
Some(Token {
kind: T::Backslash,
span,
..
}) => {
let args = self.parse_ident_list()?;
expect!(self, T::Arrow);
let body = self.parse_lambda()?;
let body_span = body.span;
Ok(E::Lambda(args, b(body)).span(span + body_span))
},
Some(Token { kind: T::Colon, span, .. }) => {
}
Some(Token {
kind: T::Colon,
span,
..
}) => {
let args = vec![*SYM_DOLLAR_SIGN];
let body = self.parse_lambda()?;
let body_span = body.span;
Ok(E::Lambda(args, b(body)).span(span + body_span))
},
}
None => self.parse_precedence(0),
_ => unreachable!("parse_lambda: guaranteed by try_next!"),
}
@ -582,9 +587,12 @@ impl<'s> Parser<'s> {
expect!(self, T::Equal);
let val = self.parse_decl()?;
let val_span = val.span;
let kind = if first.kind == T::Global { E::AssignGlobal } else { E::AssignVar };
Ok(kind(Symbol::get(name.content), b(val))
.span(first.span + val_span))
let kind = if first.kind == T::Global {
E::AssignGlobal
} else {
E::AssignVar
};
Ok(kind(Symbol::get(name.content), b(val)).span(first.span + val_span))
}
fn parse_fn_decl(&mut self) -> Result<Expr> {
@ -598,16 +606,14 @@ impl<'s> Parser<'s> {
let content = self.parse_block()?;
let end = expect!(self, T::End);
Ok(E::FnDef(name, args, b(content)).span(tok_fn.span + end.span))
},
}
T::Equal => {
let content = self.parse_expr()?;
let span = tok_fn.span + content.span;
Ok(E::FnDef(name, args, b(content)).span(span))
},
}
_ => unreachable!("parse_fn_decl: guaranteed by try_next!"),
}
}
fn parse_decl(&mut self) -> Result<Expr> {

View file

@ -21,12 +21,18 @@ impl std::cmp::Ord for Pos {
impl Pos {
pub const fn new() -> Pos {
Pos { idx: 0, line: 1, col: 1 }
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)
let idx = self
.idx
.checked_add(c.len_utf8() as u32)
.expect("source file contains more than u32::MAX chars");
if c == '\n' {
Pos {
@ -59,7 +65,10 @@ pub struct Span {
impl Span {
pub const fn new(start: Pos, end: Pos) -> Self {
if end.idx < start.idx {
Self { start: end, end: start }
Self {
start: end,
end: start,
}
} else {
Self { start, end }
}

View file

@ -1,5 +1,5 @@
use core::fmt;
use std::num::{ParseIntError, ParseFloatError};
use std::num::{ParseFloatError, ParseIntError};
use thiserror::Error;
@ -27,7 +27,10 @@ pub trait SpanParserError {
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() })
self.map_err(|e| ParserError {
span,
msg: e.to_string(),
})
}
}
@ -56,7 +59,10 @@ 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),
@ -75,7 +81,7 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
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)),
}
}
@ -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,8 +1,8 @@
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 {
@ -25,7 +25,11 @@ impl Function {
}
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,66 +16,88 @@ impl Value {
if i >= 0 && (i as usize) < l.len() {
Ok(l[i as usize].clone())
} else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
throw!(
*SYM_INDEX_ERROR,
"index {i} out of bounds for list of length {}",
l.len()
)
}
}
},
(V::Range(r), V::Int(i)) => {
if i >= 0 && (
r.ty == RangeType::Endless
|| i < r.stop
|| (r.ty == RangeType::Closed && i == r.stop)
) {
if i >= 0
&& (r.ty == RangeType::Endless
|| i < r.stop || (r.ty == RangeType::Closed && i == r.stop))
{
Ok((r.start + i).into())
} else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
}
},
}
(V::Table(t), i) if i.hashable() => {
let t = t.borrow();
let i = i.try_into()?;
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
},
}
(V::String(s), V::Range(r)) => {
let slen = s.len();
match r.ty {
RangeType::Open => {
if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
if r.stop < 0 || r.stop > slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
Ok(s[r.start as usize..r.stop as usize].into())
},
}
RangeType::Closed => {
if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
if r.stop < 0 || r.stop >= slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
Ok(s[r.start as usize..=r.stop as usize].into())
},
}
RangeType::Endless => {
if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
throw!(
*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}",
r.stop,
slen
)
}
Ok(s[r.start as usize..].into())
},
}
},
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() {
}
}
(col, idx) => {
if let Ok(ii) = idx.clone().to_iter_function() {
let col = col.clone();
let func = move |vm: &mut Vm, _| {
match vmcalliter!(vm; ii.clone())? {
let func = move |vm: &mut Vm, _| match vmcalliter!(vm; ii.clone())? {
Some(i) => col.index(i),
None => Ok(Value::from(*SYM_END_ITERATION)),
}
};
Ok(NativeFunc::new(Box::new(func), 0, symbol!("index...")).into())
} else {
@ -78,6 +105,7 @@ impl Value {
}
}
}
}
pub fn store_index(&self, vm: &mut Vm, idx: Self, val: Self) -> Result<()> {
use Value as V;
@ -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() {
}
(col, idx) => {
if let Ok(ii) = idx.clone().to_iter_function() {
let val = val.to_iter_function()?;
while let Some(i) = vmcalliter!(vm; ii.clone())? {
let Some(v) = vmcalliter!(vm; val.clone())? else {
throw!(*SYM_INDEX_ERROR, "not enough values provided for store index")
throw!(
*SYM_INDEX_ERROR,
"not enough values provided for store index"
)
};
col.store_index(vm, i, v)?;
}
Ok(())
} else {
throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}")
},
}
}
}
}
}

View file

@ -7,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,7 +112,7 @@ impl Value {
write!(w, "{name:?}")?;
}
Ok(())
},
}
Self::Range(r) => match r.ty {
RangeType::Open => write!(w, "{}..{}", r.start, r.stop),
RangeType::Closed => write!(w, "{}..={}", r.start, r.stop),
@ -110,7 +127,7 @@ 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(...)")
@ -120,7 +137,7 @@ impl Value {
v.borrow().write_to_lstring(w, repr, recur)?;
recur.pop();
w.write_all(b")")
},
}
Self::Cell(v) => {
if recur.contains(&(v.as_ptr() as _)) {
return w.write_all(b"cell(...)")
@ -129,7 +146,7 @@ impl Value {
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()),
@ -147,7 +164,7 @@ impl Value {
}
recur.pop();
w.write_all(b"]")
},
}
Self::Table(t) => {
if recur.contains(&(t.as_ptr() as _)) {
return w.write_all(b"{...}")
@ -164,34 +181,56 @@ impl Value {
}
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)
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)
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)
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)
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)?;
@ -216,7 +255,8 @@ impl Value {
} else {
let mut s = LString::new();
let mut recur = Vec::new();
self.write_to_lstring(&mut s, false, &mut recur).expect("write_to_lstring failed");
self.write_to_lstring(&mut s, false, &mut recur)
.expect("write_to_lstring failed");
Cow::Owned(s)
}
}
@ -224,7 +264,8 @@ impl Value {
pub fn repr(&self) -> LString {
let mut s = LString::new();
let mut recur = Vec::new();
self.write_to_lstring(&mut s, true, &mut recur).expect("write_to_lstring failed");
self.write_to_lstring(&mut s, true, &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,8 +47,10 @@ impl Value {
Value::List(l) => l.borrow().len() > 0,
Value::Cell(v) => v.borrow().truthy(),
Value::Symbol(_) | Value::Table(_)
| Value::Function(_) | Value::NativeFunc(_)
Value::Symbol(_)
| Value::Table(_)
| Value::Function(_)
| Value::NativeFunc(_)
| Value::Native(_) => true,
}
}
@ -59,30 +75,32 @@ pub fn promote(a: Value, b: Value) -> (Value, Value) {
}
}
////////////////////////
// unary arithmetic //
////////////////////////
impl Neg for Value {
type Output = Result<Self>;
fn neg(self) -> Self::Output {
use Value as V;
match self {
V::Int(x) => if let Some(x) = x.checked_neg() {
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) {
}
}
V::Ratio(x) => {
if let Some(x) = Rational64::ZERO.checked_sub(&x) {
Ok(V::Ratio(x))
} else {
throw!(*SYM_VALUE_ERROR, "overflow when negating {self}")
}
}
V::Float(x) => Ok(V::Float(-x)),
V::Complex(x) => Ok(V::Complex(-x)),
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}")
a => throw!(*SYM_TYPE_ERROR, "cannot negate {a:#}"),
}
}
}
@ -91,29 +109,37 @@ impl Value {
pub fn abs(self) -> Result<Self> {
use Value as V;
match self {
V::Int(x) => if let Some(x) = x.checked_abs() {
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) {
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}")
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) => {
@ -155,16 +181,19 @@ impl Div<Value> for Value {
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) {
(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,7 +202,6 @@ 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() {
@ -194,29 +222,32 @@ fn ratio_checked_div_euclid(r1: &Rational64, r2: &Rational64) -> Option<Rational
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)?)
}
impl Value {
pub fn modulo(self, rhs: Value) -> Result<Self> {
use Value as V;
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(_), V::Int(0)) => throw!(*SYM_VALUE_ERROR, "integer modulo by 0"),
(V::Int(x), V::Int(y)) => if let Some(v) = x.checked_rem_euclid(*y) {
(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) {
(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)) => {
@ -224,7 +255,7 @@ impl Value {
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:#}")
(l, r) => throw!(*SYM_TYPE_ERROR, "cannot modulo {l:#} and {r:#}"),
}
}
@ -233,36 +264,45 @@ impl Value {
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) {
(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}")
},
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) {
(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}")
},
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:#}")
(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) {
@ -284,7 +324,7 @@ fn rpow(n: i64, d: i64, p: i64) -> Option<(i64, i64)> {
(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)?)),
}
@ -298,41 +338,50 @@ impl Value {
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}")
throw!(
*SYM_VALUE_ERROR,
"overflow when raising {self} to the power {rhs}"
)
};
return Ok(V::Ratio(v.into()))
}
let (a, b) = promote(self, rhs);
match (&a, &b) {
(V::Int(0), V::Int(0))
=> throw!(*SYM_VALUE_ERROR, "integer zero to integer zero power"),
(V::Int(x), V::Int(y @ 0..)) => if let Some(v) = ipow(*x, *y as u64) {
(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) {
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:#}")
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,21 +437,19 @@ 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,
@ -427,8 +474,9 @@ impl PartialEq for Value {
(V::Symbol(a), V::Symbol(b)) => a == b,
(V::Cell(a), V::Cell(b)) => *a.borrow() == *b.borrow(),
(V::Range(a), V::Range(b)) => match (a.ty, b.ty) {
(Rty::Open, Rty::Open)
| (Rty::Closed, Rty::Closed) => a.start == b.start && a.stop == b.stop,
(Rty::Open, Rty::Open) | (Rty::Closed, Rty::Closed) => {
a.start == b.start && a.stop == b.stop
}
(Rty::Open, Rty::Closed) => a.start == b.start && a.stop == b.stop - 1,
(Rty::Closed, Rty::Open) => a.start == b.start && a.stop - 1 == b.stop,
(Rty::Endless, Rty::Endless) => a.start == b.start,
@ -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 {
throw!(*SYM_TYPE_ERROR, "cannot create range between {self:#} and {other:#}")
RangeType::Open
};
Ok(Range {
start: *start,
stop: *stop,
ty,
}
.into())
} else {
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)]
@ -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>,
@ -91,7 +109,10 @@ fn get_call_outcome(args: Vec<Value>) -> Result<CallOutcome> {
vm.call_value(f.clone(), args)
};
let nf = NativeFunc {
attrs: FuncAttrs { arity: remaining, name: None },
attrs: FuncAttrs {
arity: remaining,
name: None,
},
func: Box::new(nf),
};
Ok(CallOutcome::Partial(nf.into()))
@ -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"),
},
}
}
@ -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,20 +263,20 @@ 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 {
@ -262,44 +284,46 @@ impl Vm {
};
let mut f = f.as_ref().clone();
let captured: Vec<_> = self.pop_n(f.state.len()).into_iter()
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();
})
.collect();
f.state = captured.into_boxed_slice();
self.push(f.into());
},
}
I::LoadUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
self.push(v.borrow().clone());
},
}
I::StoreUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
*v.borrow_mut() = self.pop();
},
}
I::ContinueUpvalue(n) => {
let v = frame.func.state[usize::from(n)].clone();
self.push(Value::Cell(v));
},
}
I::LoadClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to load from closed non-cell local");
};
self.push(c.borrow().clone());
},
}
I::StoreClosedLocal(n) => {
let Value::Cell(c) = &frame.locals[usize::from(n)] else {
panic!("attempt to store to closed non-cell local");
};
*c.borrow_mut() = self.pop();
},
}
// [] -> [consts[n]]
I::Const(n)
=> self.push(frame.func.chunk.consts[usize::from(n)].clone()),
I::Const(n) => self.push(frame.func.chunk.consts[usize::from(n)].clone()),
// [] -> [nil]
I::Nil => self.push(Value::Nil),
// [] -> [b]
@ -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() {
I::JumpTrue(n) => {
if self.pop().truthy() {
self.check_interrupt()?;
frame.ip = usize::from(n);
},
}
}
// [v] ->, [], if not v then ip = n
I::JumpFalse(n) => if !self.pop().truthy() {
I::JumpFalse(n) => {
if !self.pop().truthy() {
self.check_interrupt()?;
frame.ip = usize::from(n);
},
}
}
// [v] -> [iter(v)]
I::IterBegin => {
let iter = self.pop().to_iter_function()?;
self.push(iter);
},
}
// [i,cell(v)] -> [i,v]
// [i,nil] -> [], ip = n
I::IterTest(n) => {
@ -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

@ -1,6 +1,6 @@
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,
@ -13,7 +13,10 @@ impl Parse for NativeFuncArgs {
let t: Option<Token![,]> = input.parse()?;
if t.is_some() {
let name = input.parse()?;
Ok(Self { arity, name: Some(name) })
Ok(Self {
arity,
name: Some(name),
})
} else {
Ok(Self { arity, name: None })
}
@ -41,7 +44,10 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
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! {

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;
},
}
}
}
@ -140,7 +151,7 @@ fn insertion_by(vals: &mut [Value], by: &Value, vm: &mut Vm) -> Result<()> {
while j > 0 {
let ord = call_comparison(vm, by.clone(), vals[j - 1].clone(), vals[j].clone())?;
if ord <= 0 {
break;
break
}
vals.swap(j, j - 1);
j -= 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();

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;
@ -9,17 +14,18 @@ pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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"),
[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)

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,11 +253,12 @@ 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() {
}
Value::List(l) => {
for s in l.borrow().iter() {
let Value::Symbol(s) = s else {
throw!(*SYM_TYPE_ERROR, "invalid option for open")
};
@ -231,20 +267,21 @@ 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::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() {
let func = move |_: &mut Vm, _: Vec<Value>| match listener.borrow_mut().accept() {
Ok((stream, addr)) => match BufFile::from_raw_fd(stream.into_raw_fd(), true) {
Ok(bf) => Ok(vec![
ValueFile::from(bf).into(),
LString::from(addr.to_string()).into()
].into()),
LString::from(addr.to_string()).into(),
]
.into()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
},
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
};
NativeFunc::new(Box::new(func), 0, symbol!("inner[tcp_listen]")).into()
}
@ -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() {
}
Value::List(l) => {
for e in l.borrow().iter() {
let Value::String(e) = e else {
throw!(*SYM_TYPE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}")
throw!(
*SYM_TYPE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}"
)
};
proc.env_remove(e.to_os_str());
},
_ => throw!(*SYM_VALUE_ERROR,
"{fname} delenv option expected :all or list of strings, got {delenv:#}")
}
}
_ => throw!(
*SYM_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() {
Value::Table(t) => {
for (k, v) in t.borrow().iter() {
let Value::String(k) = k.inner() else {
throw!(*SYM_TYPE_ERROR,
"{fname} env option expected table with string keys, got {env:#}")
throw!(
*SYM_TYPE_ERROR,
"{fname} 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:#}")
}
}
_ => 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()
Ok(code) => Ok(code
.code()
.map(|c| Value::Int(c as i64))
.unwrap_or_default())
},
.unwrap_or_default()),
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
}
}
@ -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,6 +1,14 @@
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};
@ -36,7 +44,7 @@ enum FmtSign {
Plus,
Minus,
#[default]
None
None,
}
// [idx][:[align][sign][width][.prec][ty]]
@ -57,10 +65,7 @@ struct FmtParser<'p> {
impl<'p> FmtParser<'p> {
fn new(code: &'p LStr) -> Self {
Self {
code,
pos: 0
}
Self { code, pos: 0 }
}
fn next(&mut self) -> Option<char> {
@ -91,14 +96,18 @@ impl<'p> FmtParser<'p> {
fn next_usize(&mut self) -> Result<Option<usize>> {
let s = self.next_number_str();
if s.is_empty() { return Ok(None) }
if s.is_empty() {
return Ok(None)
}
let Ok(i) = parse_int(s, 10) else {
throw!(*SYM_FORMAT_ERROR,
"code {{{}}}: invalid integer", self.code)
throw!(*SYM_FORMAT_ERROR, "code {{{}}}: invalid integer", self.code)
};
if i < 0 {
throw!(*SYM_FORMAT_ERROR,
"code {{{}}}: integer may not be negative", self.code)
throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: integer may not be negative",
self.code
)
}
Ok(Some(i as usize))
}
@ -123,8 +132,11 @@ impl<'p> FmtParser<'p> {
};
self.next();
let Some(c) = self.next() else {
throw!(*SYM_FORMAT_ERROR,
"code {{{}}}: alignment without fill character", self.code)
throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: alignment without fill character",
self.code
)
};
Ok(Some((align, c)))
}
@ -148,8 +160,11 @@ impl<'p> FmtParser<'p> {
Some('b') => FmtType::Bin,
Some('e') => FmtType::Exp(false),
Some('E') => FmtType::Exp(true),
_ => throw!(*SYM_FORMAT_ERROR,
"code {{{}}}: invalid format type", self.code),
_ => throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: invalid format type",
self.code
),
};
self.next();
Ok(ty)
@ -157,9 +172,13 @@ impl<'p> FmtParser<'p> {
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(())
Some(c) => throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: expected end of code, found character '{}'",
self.code,
c
),
None => Ok(()),
}
}
@ -184,8 +203,7 @@ impl<'p> FmtParser<'p> {
}
}
fn get_arg<'a>(args: &'a [Value], n: Option<usize>, faidx: &mut usize)
-> Result<&'a Value> {
fn get_arg<'a>(args: &'a [Value], n: Option<usize>, faidx: &mut usize) -> Result<&'a Value> {
let i = match n {
Some(n) => n,
None => {
@ -195,7 +213,11 @@ fn get_arg<'a>(args: &'a [Value], n: Option<usize>, faidx: &mut usize)
}
};
if i >= args.len() {
throw!(*SYM_FORMAT_ERROR, "missing arguments: expected at least {}", i+1)
throw!(
*SYM_FORMAT_ERROR,
"missing arguments: expected at least {}",
i + 1
)
}
Ok(&args[i])
}
@ -207,30 +229,46 @@ fn get_val(args: &[Value], i: FmtIndex, faidx: &mut usize) -> Result<usize> {
FmtIndex::NextArg => get_arg(args, None, faidx)?,
};
let Value::Int(v) = v else {
throw!(*SYM_FORMAT_ERROR, "expected positive integer argument, found {v:#}")
throw!(
*SYM_FORMAT_ERROR,
"expected positive integer argument, found {v:#}"
)
};
if *v < 0 {
throw!(*SYM_FORMAT_ERROR, "expected positive integer argument, found {v}")
throw!(
*SYM_FORMAT_ERROR,
"expected positive integer argument, found {v}"
)
}
Ok(*v as usize)
}
fn format_float(f: f64, prec: Option<usize>, ty: FmtType, buf: &mut LString, ty_name: &str) -> Result<()> {
fn format_float(
f: f64,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
ty_name: &str,
) -> Result<()> {
let res = match (prec, ty) {
(None, FmtType::Str) => write!(buf, "{}", Value::Float(f)),
(None, FmtType::Repr) => write!(buf, "{:#}", Value::Float(f)),
(None, FmtType::Exp(true)) => write!(buf, "{:E}", f),
(None, FmtType::Exp(false)) => write!(buf, "{:e}", f),
(Some(p), FmtType::Str)
| (Some(p), FmtType::Repr) => write!(buf, "{0:.1$}", f, p),
(Some(p), FmtType::Str) | (Some(p), FmtType::Repr) => write!(buf, "{0:.1$}", f, p),
(Some(p), FmtType::Exp(true)) => write!(buf, "{0:.1$E}", f, p),
(Some(p), FmtType::Exp(false)) => write!(buf, "{0:.1$e}", f, p),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
};
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
}
fn format_complex(cx: Complex64, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
fn format_complex(
cx: Complex64,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
) -> Result<()> {
format_float(cx.re, prec, ty, buf, "complex")?;
if cx.im < 0.0 {
buf.push_char('-')
@ -241,7 +279,13 @@ fn format_complex(cx: Complex64, prec: Option<usize>, ty: FmtType, buf: &mut LSt
Ok(())
}
fn format_int(n: i64, prec: Option<usize>, ty: FmtType, buf: &mut LString, ty_name: &str) -> Result<()> {
fn format_int(
n: i64,
prec: Option<usize>,
ty: FmtType,
buf: &mut LString,
ty_name: &str,
) -> Result<()> {
if prec.is_some() {
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
}
@ -252,7 +296,7 @@ fn format_int(n: i64, prec: Option<usize>, ty: FmtType, buf: &mut LString, ty_na
FmtType::Oct => write!(buf, "{}", to_lstring_radix(n, 8, false)),
FmtType::Sex => write!(buf, "{}", to_lstring_radix(n, 6, false)),
FmtType::Bin => write!(buf, "{}", to_lstring_radix(n, 2, false)),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}"),
};
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
}
@ -271,7 +315,7 @@ fn format_value(v: &Value, prec: Option<usize>, ty: FmtType, buf: &mut LString)
let res = match ty {
FmtType::Str => write!(buf, "{}", v),
FmtType::Repr => write!(buf, "{:#}", v),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value")
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value"),
};
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
}
@ -281,9 +325,12 @@ fn format_string(mut s: &LStr, prec: Option<usize>, ty: FmtType, buf: &mut LStri
s = &s[..prec]
}
let res = match ty {
FmtType::Str => { buf.push_lstr(s); Ok(()) },
FmtType::Str => {
buf.push_lstr(s);
Ok(())
}
FmtType::Repr => write!(buf, "{:?}", s),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for string")
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for string"),
};
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
}
@ -294,10 +341,13 @@ fn pad_str(n: usize, c: char, buf: &mut LString) {
}
}
fn format_arg(args: &[Value], faidx: &mut usize, code: &LStr, res: &mut LString)
-> Result<()> {
fn format_arg(args: &[Value], faidx: &mut usize, code: &LStr, res: &mut LString) -> Result<()> {
if !code.is_utf8() {
throw!(*SYM_FORMAT_ERROR, "code {{{}}}: code contains invalid UTF-8", code)
throw!(
*SYM_FORMAT_ERROR,
"code {{{}}}: code contains invalid UTF-8",
code
)
}
let fmtcode = FmtParser::new(code).read_format()?;
@ -320,16 +370,15 @@ fn format_arg(args: &[Value], faidx: &mut usize, code: &LStr, res: &mut LString)
Some(b'+') => ("+", &buf[1..]),
Some(b'-') => ("-", &buf[1..]),
_ => ("+", &buf[..]),
}
},
FmtSign::Minus => match buf.byte_get(0) {
Some(b'-') => ("-", &buf[1..]),
_ => ("", &buf[..]),
}
FmtSign::None => ("", &buf[..])
},
FmtSign::None => ("", &buf[..]),
};
res.push_str(sign);
if let Some(w) = width_val {
let w = w.saturating_sub(final_buf.len() + sign.len());
match fmtcode.align {
@ -347,12 +396,11 @@ fn format_arg(args: &[Value], faidx: &mut usize, code: &LStr, res: &mut LString)
res.push_lstr(final_buf);
}
None => {
let c = if matches!(fmt_arg,
Value::Int(_)
| Value::Float(_)
| Value::Ratio(_)
| Value::Complex(_)
) && fmtcode.sign != FmtSign::None {
let c = if matches!(
fmt_arg,
Value::Int(_) | Value::Float(_) | Value::Ratio(_) | Value::Complex(_)
) && fmtcode.sign != FmtSign::None
{
'0'
} else {
' '
@ -372,7 +420,10 @@ fn format_arg(args: &[Value], faidx: &mut usize, code: &LStr, res: &mut LString)
pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, fstr, fargs] = unpack_args!(args);
let (Value::String(fstr), Value::List(fargs)) = (&fstr, &fargs) else {
throw!(*SYM_TYPE_ERROR, "format expected string and list, got {fstr:#} and {fargs:#}")
throw!(
*SYM_TYPE_ERROR,
"format expected string and list, got {fstr:#} and {fargs:#}"
)
};
let mut res = LString::new();
let mut faidx = 0;
@ -382,7 +433,11 @@ pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
i += 1;
if b == b'}' {
let Some(b'}') = fstr.byte_get(i) else {
throw!(*SYM_FORMAT_ERROR, "unmatched closing brace at byte {}", i-1)
throw!(
*SYM_FORMAT_ERROR,
"unmatched closing brace at byte {}",
i - 1
)
};
i += 1;
res.push_byte(b);
@ -392,7 +447,11 @@ pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
continue
}
if i >= fstr.len() {
throw!(*SYM_FORMAT_ERROR, "unclosed format specifier at byte {}", i-1)
throw!(
*SYM_FORMAT_ERROR,
"unclosed format specifier at byte {}",
i - 1
)
}
if let Some(b'{') = fstr.byte_get(i) {
i += 1;
@ -404,7 +463,11 @@ pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
i += 1;
}
if i == fstr.len() {
throw!(*SYM_FORMAT_ERROR, "unclosed format specifier at byte {}", start-1)
throw!(
*SYM_FORMAT_ERROR,
"unclosed format specifier at byte {}",
start - 1
)
}
let code = &fstr[start..i];
i += 1;
@ -416,4 +479,3 @@ pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn load(vm: &mut Vm) {
vm.set_global_name("fmt", fmt_().into());
}

View file

@ -1,6 +1,17 @@
use std::{io::{BufRead, Write}, os::unix::ffi::OsStrExt, sync::Mutex, time::{SystemTime, UNIX_EPOCH}};
use std::{
io::{BufRead, Write},
os::unix::ffi::OsStrExt,
sync::Mutex,
time::{SystemTime, UNIX_EPOCH},
};
use talc_lang::{exception::{throw, Result}, lstring::LString, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::Value, vmcall, Vm};
use talc_lang::{
exception::{throw, Result},
lstring::LString,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
value::Value,
vmcall, Vm,
};
use talc_macros::native_func;
use crate::{unpack_args, SYM_IO_ERROR};
@ -45,14 +56,22 @@ pub fn readln(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
#[native_func(0)]
pub fn time(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("time went backwards");
Ok(time.as_secs_f64().into())
}
#[native_func(0)]
pub fn time_ns(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
let time = SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards");
Ok(vec![(time.as_secs() as i64).into(), (time.subsec_nanos() as i64).into()].into())
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("time went backwards");
Ok(vec![
(time.as_secs() as i64).into(),
(time.subsec_nanos() as i64).into(),
]
.into())
}
#[native_func(1)]
@ -72,7 +91,10 @@ pub fn env(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "env expected string, got {key:#}")
};
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(*SYM_VALUE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
throw!(
*SYM_VALUE_ERROR,
"environment variable must not be empty or contain an equals sign or null byte"
)
}
let val = std::env::var_os(key.to_os_str());
match val {
@ -88,11 +110,17 @@ pub fn setenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "setenv expected string, got {key:#}")
};
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(*SYM_VALUE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
throw!(
*SYM_VALUE_ERROR,
"environment variable must not be empty or contain an equals sign or null byte"
)
}
let val = val.str();
if val.as_bytes().contains(&0) {
throw!(*SYM_VALUE_ERROR, "environment variable value must not contain a null byte")
throw!(
*SYM_VALUE_ERROR,
"environment variable value must not contain a null byte"
)
}
{
let Ok(guard) = ENV_LOCK.lock() else {
@ -111,7 +139,10 @@ pub fn delenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "delenv expected string, got {key:#}")
};
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
throw!(*SYM_VALUE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
throw!(
*SYM_VALUE_ERROR,
"environment variable must not be empty or contain an equals sign or null byte"
)
}
{
let Ok(guard) = ENV_LOCK.lock() else {
@ -123,7 +154,6 @@ pub fn delenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::Nil)
}
pub fn load(vm: &mut Vm) {
vm.set_global_name("print", print().into());
vm.set_global_name("println", println().into());

View file

@ -1,6 +1,12 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{exception::Result, symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{function::NativeFunc, ops::RatioExt, range::RangeType, HashValue, Value}, vmcall, vmcalliter, Vm};
use talc_lang::{
exception::Result,
symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{function::NativeFunc, ops::RatioExt, range::RangeType, HashValue, Value},
vmcall, vmcalliter, Vm,
};
use talc_macros::native_func;
use crate::unpack_args;
@ -95,9 +101,7 @@ pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let v = RefCell::new(Some(val));
let f = move |_: &mut Vm, _| {
Ok(Value::iter_pack(v.borrow_mut().take()))
};
let f = move |_: &mut Vm, _| Ok(Value::iter_pack(v.borrow_mut().take()));
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[once]")).into())
}
@ -105,9 +109,7 @@ pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let f = move |_: &mut Vm, _| {
Ok(val.clone())
};
let f = move |_: &mut Vm, _| Ok(val.clone());
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[forever]")).into())
}
@ -115,18 +117,14 @@ pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// chain iteration
//
#[native_func(2)]
pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, map, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| {
match vmcalliter!(vm; iter.clone())? {
let f = move |vm: &mut Vm, _| match vmcalliter!(vm; iter.clone())? {
Some(val) => Ok(vmcall!(vm; map.clone(), val)?),
None => Ok(Value::iter_pack(None)),
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[map]")).into())
}
@ -136,14 +134,12 @@ pub fn tee(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, tee, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| {
match vmcalliter!(vm; iter.clone())? {
let f = move |vm: &mut Vm, _| match vmcalliter!(vm; iter.clone())? {
Some(val) => {
vmcall!(vm; tee.clone(), val.clone())?;
Ok(val)
}
None => Ok(Value::iter_pack(None)),
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[tee]")).into())
}
@ -171,8 +167,7 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, filter, iter] = unpack_args!(args);
let iter = iter.to_iter_function()?;
let f = move |vm: &mut Vm, _| {
loop {
let f = move |vm: &mut Vm, _| loop {
let Some(next) = vmcalliter!(vm; iter.clone())? else {
return Ok(Value::iter_pack(None))
};
@ -180,7 +175,6 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
if res.truthy() {
return Ok(next)
}
}
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[filter]")).into())
}
@ -192,7 +186,10 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "take expected integer, got {count:#}")
};
let Ok(count) = count.try_into() else {
throw!(*SYM_VALUE_ERROR, "take expected nonnegative integer, got {count:#}")
throw!(
*SYM_VALUE_ERROR,
"take expected nonnegative integer, got {count:#}"
)
};
let iter = iter.to_iter_function()?;
@ -218,7 +215,10 @@ pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "count expected integer, got {count:#}")
};
let Ok(count) = count.try_into() else {
throw!(*SYM_VALUE_ERROR, "count expected nonnegative integer, got {count:#}")
throw!(
*SYM_VALUE_ERROR,
"count expected nonnegative integer, got {count:#}"
)
};
let iter = iter.to_iter_function()?;
@ -255,7 +255,6 @@ pub fn enumerate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[enumerate]")).into())
}
#[native_func(1)]
pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
@ -292,7 +291,10 @@ pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[derive(Clone, Copy, Default)]
enum Step {
First, Going, #[default] End,
First,
Going,
#[default]
End,
}
#[native_func(2)]
@ -303,7 +305,10 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "step expected integer, got {by:#}")
};
if by <= 0 {
throw!(*SYM_VALUE_ERROR, "step expected positive integer, got {by:#}")
throw!(
*SYM_VALUE_ERROR,
"step expected positive integer, got {by:#}"
)
}
let state = RefCell::new(Step::First);
@ -314,7 +319,7 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*state.borrow_mut() = Step::Going;
}
Ok(Value::iter_pack(res))
},
}
Step::Going => {
for _ in 0..(by - 1) {
if vmcall!(vm; iter.clone())? == Value::Nil {
@ -326,7 +331,7 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
};
*state.borrow_mut() = Step::Going;
Ok(x)
},
}
Step::End => Ok(Value::Nil),
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[step]")).into())
@ -347,17 +352,14 @@ pub fn rev(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*lst.borrow_mut() = Some(l);
}
Ok(Value::iter_pack(lst.borrow_mut().as_mut().unwrap().pop()))
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[rev]")).into())
}
//
// join
//
#[native_func(2)]
pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, i1, i2] = unpack_args!(args);
@ -386,7 +388,9 @@ pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "zipn expected list, got {args:#}")
};
let iters = args.borrow().iter()
let iters = args
.borrow()
.iter()
.map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?;
@ -414,7 +418,7 @@ pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let f = move |vm: &mut Vm, _| {
let mut s = state.borrow_mut();
if s.1 {
return Ok(Value::iter_pack(None));
return Ok(Value::iter_pack(None))
}
let n = s.0;
s.0 = !s.0;
@ -437,7 +441,9 @@ pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Value::List(args) = args else {
throw!(*SYM_TYPE_ERROR, "alternaten expected list, got {args:#}")
};
let iters = args.borrow().iter()
let iters = args
.borrow()
.iter()
.map(|i| i.clone().to_iter_function())
.collect::<Result<Vec<_>>>()?;
@ -445,7 +451,7 @@ pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let f = move |vm: &mut Vm, _| {
let mut s = state.borrow_mut();
if s.1 {
return Ok(Value::iter_pack(None));
return Ok(Value::iter_pack(None))
}
let n = s.0;
s.0 = (s.0 + 1) % iters.len();
@ -463,7 +469,11 @@ pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[derive(Default)]
enum Intersperse {
Init, Waiting, HasNext(Value), #[default] End
Init,
Waiting,
HasNext(Value),
#[default]
End,
}
#[native_func(2)]
@ -472,8 +482,7 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let iter = iter.to_iter_function()?;
let state = RefCell::new(Intersperse::Init);
let f = move |vm: &mut Vm, _| {
match state.take() {
let f = move |vm: &mut Vm, _| match state.take() {
Intersperse::Init => {
if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::Waiting;
@ -482,7 +491,7 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None))
}
},
}
Intersperse::Waiting => {
if let Some(v) = vmcalliter!(vm; iter.clone())? {
*state.borrow_mut() = Intersperse::HasNext(v);
@ -491,19 +500,17 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*state.borrow_mut() = Intersperse::End;
Ok(Value::iter_pack(None))
}
},
}
Intersperse::HasNext(v) => {
*state.borrow_mut() = Intersperse::Waiting;
Ok(v)
},
Intersperse::End => Ok(Value::iter_pack(None)),
}
Intersperse::End => Ok(Value::iter_pack(None)),
};
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[intersperse]")).into())
}
#[native_func(2)]
pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter1, iter2] = unpack_args!(args);
@ -591,14 +598,10 @@ pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cart_prod]")).into())
}
//
// end iteration
//
#[native_func(1)]
pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, iter] = unpack_args!(args);
@ -606,7 +609,7 @@ pub fn list(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut result = Vec::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? {
result.push(value);
};
}
Ok(result.into())
}
@ -617,16 +620,23 @@ pub fn table(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut result = HashMap::new();
while let Some(value) = vmcalliter!(vm; iter.clone())? {
let Value::List(l) = value else {
throw!(*SYM_TYPE_ERROR, "table expected iterator to yield list, got {value:#}")
throw!(
*SYM_TYPE_ERROR,
"table expected iterator to yield list, got {value:#}"
)
};
let mut l = Rc::unwrap_or_clone(l).take();
if l.len() != 2 {
throw!(*SYM_VALUE_ERROR, "table: iterator yielded list of length {} (expected 2)", l.len())
throw!(
*SYM_VALUE_ERROR,
"table: iterator yielded list of length {} (expected 2)",
l.len()
)
};
let v = l.pop().unwrap();
let k = l.pop().unwrap();
result.insert(k.try_into()?, v);
};
}
Ok(result.into())
}
@ -637,15 +647,16 @@ pub fn len(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::String(s) => return Ok((s.chars().count() as i64).into()),
Value::List(l) => return Ok((l.borrow().len() as i64).into()),
Value::Table(t) => return Ok((t.borrow().len() as i64).into()),
Value::Range(r) if r.ty != RangeType::Endless
=> return Ok((r.len().unwrap() as i64).into()),
Value::Range(r) if r.ty != RangeType::Endless => {
return Ok((r.len().unwrap() as i64).into())
}
_ => (),
}
let iter = value.to_iter_function()?;
let mut len = 0;
while vmcalliter!(vm; iter.clone())?.is_some() {
len += 1;
};
}
Ok(len.into())
}
@ -764,11 +775,13 @@ pub fn contains(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for _ in 0.. {
match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil),
Some(v) => if v == val {
Some(v) => {
if v == val {
return Ok(true.into())
}
}
}
}
Ok(false.into())
}
@ -780,11 +793,13 @@ pub fn index_of(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for i in 0.. {
match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil),
Some(v) => if v == val {
Some(v) => {
if v == val {
return Ok(i.into())
}
}
}
}
Ok(Value::Nil)
}
@ -796,11 +811,13 @@ pub fn index_if(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
for i in 0.. {
match vmcalliter!(vm; iter.clone())? {
None => return Ok(Value::Nil),
Some(v) => if vmcall!(vm; func.clone(), v)?.truthy() {
Some(v) => {
if vmcall!(vm; func.clone(), v)?.truthy() {
return Ok(i.into())
}
}
}
}
Ok(Value::Nil)
}
@ -884,7 +901,7 @@ pub fn stdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}")
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
})
}
@ -907,7 +924,7 @@ pub fn pstdev(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Float(f) => Value::Float(f.sqrt()),
Value::Ratio(r) => Value::Float(r.to_f64().sqrt()),
Value::Complex(c) => Value::Complex(c.sqrt()),
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}")
v => throw!(*SYM_TYPE_ERROR, "stdev: cannot square root {v:#}"),
})
}
@ -946,4 +963,3 @@ pub fn median(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
m.0 / Value::Int(1)
}
}

View file

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

View file

@ -1,7 +1,14 @@
use std::cmp::Ordering;
use lazy_static::lazy_static;
use talc_lang::{exception::Result, parser::{parse_int, to_lstring_radix}, symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{ops::RatioExt, Complex64, Value}, vmcalliter, Vm};
use talc_lang::{
exception::Result,
parser::{parse_int, to_lstring_radix},
symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{ops::RatioExt, Complex64, Value},
vmcalliter, Vm,
};
use talc_macros::native_func;
use crate::unpack_args;
@ -163,7 +170,10 @@ pub fn hex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, radix] = unpack_args!(args);
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
throw!(*SYM_TYPE_ERROR, "to_radix expected integer arguments, got {x:#} and {radix:#}")
throw!(
*SYM_TYPE_ERROR,
"to_radix expected integer arguments, got {x:#} and {radix:#}"
)
};
if *radix < 2 || *radix > 36 {
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 0..=36")
@ -175,10 +185,16 @@ pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x, radix] = unpack_args!(args);
let (Value::Int(x), Value::Int(radix)) = (&x, &radix) else {
throw!(*SYM_TYPE_ERROR, "to_radix_upper expected integer arguments, got {x:#} and {radix:#}")
throw!(
*SYM_TYPE_ERROR,
"to_radix_upper expected integer arguments, got {x:#} and {radix:#}"
)
};
if *radix < 2 || *radix > 36 {
throw!(*SYM_VALUE_ERROR, "to_radix_upper expected radix in range 0..=36")
throw!(
*SYM_VALUE_ERROR,
"to_radix_upper expected radix in range 0..=36"
)
}
Ok(to_lstring_radix(*x, *radix as u32, true).into())
}
@ -187,14 +203,23 @@ pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s, radix] = unpack_args!(args);
let (Value::String(s), Value::Int(radix)) = (&s, &radix) else {
throw!(*SYM_TYPE_ERROR, "from_radix expected string and integer arguments, got {s:#} and {radix:#}")
throw!(
*SYM_TYPE_ERROR,
"from_radix expected string and integer arguments, got {s:#} and {radix:#}"
)
};
if *radix < 2 || *radix > 36 {
throw!(*SYM_VALUE_ERROR, "from_radix expected radix in range 0..=36")
throw!(
*SYM_VALUE_ERROR,
"from_radix expected radix in range 0..=36"
)
}
match parse_int(s.as_ref(), *radix as u32) {
Ok(v) => Ok(v.into()),
Err(_) => throw!(*SYM_VALUE_ERROR, "string was not a valid integer in given radix"),
Err(_) => throw!(
*SYM_VALUE_ERROR,
"string was not a valid integer in given radix"
),
}
}
@ -204,7 +229,9 @@ pub fn from_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
fn isqrt_inner(mut n: i64) -> i64 {
assert!(n >= 0, "isqrt input should be nonnegative");
if n < 2 { return n }
if n < 2 {
return n
}
let mut c = 0;
let mut d = 1 << 62;
@ -217,8 +244,7 @@ fn isqrt_inner(mut n: i64) -> i64 {
if n >= c + d {
n -= c + d;
c = (c >> 1) + d;
}
else {
} else {
c >>= 1;
}
d >>= 2;
@ -247,18 +273,20 @@ pub fn gcd_inner(a: i64, b: i64) -> i64 {
}
b -= a;
if b == 0 {
return a << z;
return a << z
}
b >>= b.trailing_zeros();
}
}
#[native_func(1)]
pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "isqrt expected integer argument, got {x:#}")
throw!(
*SYM_TYPE_ERROR,
"isqrt expected integer argument, got {x:#}"
)
};
if x < 0 {
throw!(*SYM_VALUE_ERROR, "isqrt: argument must be positive")
@ -266,12 +294,14 @@ pub fn isqrt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(isqrt_inner(x).into())
}
#[native_func(1)]
pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "isprime expected integer argument, got {x:#}")
throw!(
*SYM_TYPE_ERROR,
"isprime expected integer argument, got {x:#}"
)
};
if x < 2 {
return Ok(false.into())
@ -287,8 +317,12 @@ pub fn isprime(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let lim = isqrt_inner(x);
let mut i = 12;
while i <= lim + 1 {
if x % (i - 1) == 0 { return Ok(false.into()) }
if x % (i + 1) == 0 { return Ok(false.into()) }
if x % (i - 1) == 0 {
return Ok(false.into())
}
if x % (i + 1) == 0 {
return Ok(false.into())
}
i += 6;
}
Ok(true.into())
@ -352,7 +386,9 @@ pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "lcmn: cannot take lcm with {a:#}")
};
let g = gcd_inner(l, a);
if g == 0 { return Ok(Value::from(0)) };
if g == 0 {
return Ok(Value::from(0))
};
let new_l = (Value::from(l).int_div(Value::from(g))? * Value::from(a))?;
let Value::Int(new_l) = new_l else {
unreachable!("int//int * int != int")
@ -367,7 +403,10 @@ pub fn lcmn(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn factors(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(mut x) = x else {
throw!(*SYM_TYPE_ERROR, "factors expected integer argument, got {x:#}")
throw!(
*SYM_TYPE_ERROR,
"factors expected integer argument, got {x:#}"
)
};
let mut factors = Vec::new();
if x <= 1 {
@ -417,14 +456,19 @@ fn totient_prime(n: &mut u64, p: u64) -> u64 {
pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "totient expected integer argument, got {x:#}")
throw!(
*SYM_TYPE_ERROR,
"totient expected integer argument, got {x:#}"
)
};
if x <= 1 {
return Ok(1.into())
}
let mut x = x as u64;
let mut totient = 1;
if x & 1 == 0 { x >>= 1; }
if x & 1 == 0 {
x >>= 1;
}
while x & 1 == 0 {
x >>= 1;
totient <<= 1;
@ -443,7 +487,6 @@ pub fn totient(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok((totient as i64).into())
}
//
// numeric operations
//
@ -533,7 +576,7 @@ pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ordering::Greater => Ok(Value::Ratio(1.into())),
Ordering::Less => Ok(Value::Ratio((-1).into())),
Ordering::Equal => Ok(Value::Ratio(0.into())),
}
},
x => throw!(*SYM_TYPE_ERROR, "sign expected real argument, got {x:#}"),
}
}
@ -542,7 +585,6 @@ pub fn sign(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// floating-point operations
//
#[native_func(1)]
pub fn signum(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
@ -557,7 +599,10 @@ pub fn classify(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let x = to_floaty(x);
let Value::Float(x) = x else {
throw!(*SYM_TYPE_ERROR, "classify expected real argument, got {x:#}")
throw!(
*SYM_TYPE_ERROR,
"classify expected real argument, got {x:#}"
)
};
Ok(match x.classify() {
std::num::FpCategory::Nan => *SYM_NAN,
@ -565,7 +610,8 @@ pub fn classify(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
std::num::FpCategory::Zero => *SYM_ZERO,
std::num::FpCategory::Subnormal => *SYM_SUBNORMAL,
std::num::FpCategory::Normal => *SYM_NORMAL,
}.into())
}
.into())
}
#[native_func(1)]
@ -575,7 +621,10 @@ pub fn isnan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
Value::Float(x) => Ok(x.is_nan().into()),
Value::Complex(z) => Ok(z.is_nan().into()),
v => throw!(*SYM_TYPE_ERROR, "isnan expected numeric argument, got {v:#}"),
v => throw!(
*SYM_TYPE_ERROR,
"isnan expected numeric argument, got {v:#}"
),
}
}
@ -586,7 +635,10 @@ pub fn isfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Int(_) | Value::Ratio(_) => Ok(true.into()),
Value::Float(x) => Ok(x.is_finite().into()),
Value::Complex(z) => Ok(z.is_finite().into()),
v => throw!(*SYM_TYPE_ERROR, "isfinite expected numeric argument, got {v:#}"),
v => throw!(
*SYM_TYPE_ERROR,
"isfinite expected numeric argument, got {v:#}"
),
}
}
@ -597,7 +649,10 @@ pub fn isinfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Int(_) | Value::Ratio(_) => Ok(false.into()),
Value::Float(x) => Ok(x.is_infinite().into()),
Value::Complex(z) => Ok(z.is_infinite().into()),
v => throw!(*SYM_TYPE_ERROR, "isinfinite expected numeric argument, got {v:#}"),
v => throw!(
*SYM_TYPE_ERROR,
"isinfinite expected numeric argument, got {v:#}"
),
}
}
@ -605,7 +660,10 @@ pub fn isinfinite(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::Float(f) = val else {
throw!(*SYM_TYPE_ERROR, "float_to_bits expected float argument, got {val:#}")
throw!(
*SYM_TYPE_ERROR,
"float_to_bits expected float argument, got {val:#}"
)
};
Ok(Value::Int(f.to_bits() as i64))
}
@ -614,25 +672,28 @@ pub fn float_to_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn float_of_bits(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::Int(i) = val else {
throw!(*SYM_TYPE_ERROR, "float_of_bits expected integer argument, got {val:#}")
throw!(
*SYM_TYPE_ERROR,
"float_of_bits expected integer argument, got {val:#}"
)
};
Ok(Value::Float(f64::from_bits(i as u64)))
}
//
// rational operations
//
#[native_func(1)]
pub fn numer(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args);
match v {
Value::Int(x) => Ok(Value::Int(x)),
Value::Ratio(x) => Ok(Value::Int(*x.numer())),
v => throw!(*SYM_TYPE_ERROR, "numer expected rational argument, got {v:#}"),
v => throw!(
*SYM_TYPE_ERROR,
"numer expected rational argument, got {v:#}"
),
}
}
@ -642,7 +703,10 @@ pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match v {
Value::Int(_) => Ok(Value::Int(1)),
Value::Ratio(x) => Ok(Value::Int(*x.denom())),
v => throw!(*SYM_TYPE_ERROR, "denom expected rational argument, got {v:#}"),
v => throw!(
*SYM_TYPE_ERROR,
"denom expected rational argument, got {v:#}"
),
}
}
@ -650,8 +714,6 @@ pub fn denom(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// complex operations
//
#[native_func(1)]
pub fn re(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, v] = unpack_args!(args);
@ -699,17 +761,17 @@ pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Value::Ratio(_) => x.clone() * x,
Value::Float(_) => x.clone() * x,
Value::Complex(x) => Ok(Value::Float(x.norm_sqr())),
x => throw!(*SYM_TYPE_ERROR, "abs_sq expected numeric argument, got {x:#}"),
x => throw!(
*SYM_TYPE_ERROR,
"abs_sq expected numeric argument, got {x:#}"
),
}
}
//
// continuous operations
//
macro_rules! float_func {
($name:ident) => {
#[native_func(1)]
@ -718,8 +780,11 @@ macro_rules! float_func {
match to_floaty(v) {
Value::Float(x) => Ok(Value::Float(x.$name())),
Value::Complex(z) => Ok(Value::Complex(z.$name())),
v => throw!(*SYM_TYPE_ERROR,
"{} expected numeric argument, got {v:#}", stringify!($name)),
v => throw!(
*SYM_TYPE_ERROR,
"{} expected numeric argument, got {v:#}",
stringify!($name)
),
}
}
};
@ -750,10 +815,10 @@ float_func!(atanh);
pub fn atan2(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, y, x] = unpack_args!(args);
match (to_floaty(y), to_floaty(x)) {
(Value::Float(y), Value::Float(x))
=> Ok(Value::Float(x.atan2(y))),
(y,x) => throw!(*SYM_TYPE_ERROR,
"atan2 expected real arguments, got {y:#} and {x:#}"),
(Value::Float(y), Value::Float(x)) => Ok(Value::Float(x.atan2(y))),
(y, x) => throw!(
*SYM_TYPE_ERROR,
"atan2 expected real arguments, got {y:#} and {x:#}"
),
}
}

View file

@ -1,5 +1,11 @@
use rand::{seq::SliceRandom, Rng};
use talc_lang::{exception::Result, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{range::RangeType, Value}, vmcalliter, Vm};
use talc_lang::{
exception::Result,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{range::RangeType, Value},
vmcalliter, Vm,
};
use talc_macros::native_func;
use crate::unpack_args;
@ -25,7 +31,7 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_VALUE_ERROR, "rand_in: empty list")
};
Ok(v.clone())
},
}
Value::Table(t) => {
let t = t.borrow();
if t.is_empty() {
@ -34,16 +40,14 @@ pub fn rand_in(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let i = rand::thread_rng().gen_range(0..t.len());
let key = t.keys().nth(i).unwrap();
Ok(key.clone().into_inner())
},
}
Value::Range(r) => {
if r.is_empty() {
throw!(*SYM_VALUE_ERROR, "rand_in: empty range")
}
match r.ty {
RangeType::Open => Ok(Value::Int(
rand::thread_rng().gen_range(r.start..r.stop))),
RangeType::Closed => Ok(Value::Int(
rand::thread_rng().gen_range(r.start..=r.stop))),
RangeType::Open => Ok(Value::Int(rand::thread_rng().gen_range(r.start..r.stop))),
RangeType::Closed => Ok(Value::Int(rand::thread_rng().gen_range(r.start..=r.stop))),
RangeType::Endless => throw!(*SYM_VALUE_ERROR, "rand_in: endless range"),
}
}

View file

@ -1,9 +1,16 @@
use std::borrow::Cow;
use talc_lang::{exception::{exception, Result}, lstring::LString, symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{NativeValue, Value}, Vm};
use talc_macros::native_func;
use regex::{Captures, Match, Regex};
use lazy_static::lazy_static;
use regex::{Captures, Match, Regex};
use talc_lang::{
exception::{exception, Result},
lstring::LString,
symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{NativeValue, Value},
Vm,
};
use talc_macros::native_func;
use crate::unpack_args;
@ -18,17 +25,30 @@ lazy_static! {
pub struct ValueRegex(Regex);
impl From<Regex> for ValueRegex {
fn from(value: Regex) -> Self { Self(value) }
fn from(value: Regex) -> Self {
Self(value)
}
}
impl From<ValueRegex> for Regex {
fn from(value: ValueRegex) -> Self { value.0 }
fn from(value: ValueRegex) -> Self {
value.0
}
}
impl NativeValue for ValueRegex {
fn get_type(&self) -> Symbol { *SYM_STD_REGEX }
fn as_any(&self) -> &dyn std::any::Any { self }
fn to_lstring(&self, w: &mut LString, repr: bool, _recur: &mut Vec<*const ()>) -> std::io::Result<()> {
fn get_type(&self) -> Symbol {
*SYM_STD_REGEX
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn to_lstring(
&self,
w: &mut LString,
repr: bool,
_recur: &mut Vec<*const ()>,
) -> std::io::Result<()> {
use std::io::Write;
if repr {
write!(w, "/{}/", self.0)
@ -58,7 +78,10 @@ fn match_to_value(m: Match) -> Value {
Value::new_table(|t| {
t.insert((*SYM_START).into(), (m.start() as i64).into());
t.insert((*SYM_END).into(), (m.end() as i64).into());
t.insert((*SYM_STR).into(), LString::from(m.as_str().to_string()).into());
t.insert(
(*SYM_STR).into(),
LString::from(m.as_str().to_string()).into(),
);
})
}
@ -78,22 +101,28 @@ fn regex_from<'a>(v: &'a Value, name: &str) -> Result<Cow<'a, Regex>> {
Regex::new(s)
.map(Cow::Owned)
.map_err(|e| exception!(*SYM_VALUE_ERROR, "invalid regex: {e}"))
},
Value::Native(n) if n.get_type() == *SYM_STD_REGEX => {
n.as_any().downcast_ref::<ValueRegex>()
}
Value::Native(n) if n.get_type() == *SYM_STD_REGEX => n
.as_any()
.downcast_ref::<ValueRegex>()
.map(|vr| Cow::Borrowed(&vr.0))
.ok_or_else(|| exception!(
*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}"))
},
_ => throw!(*SYM_TYPE_ERROR, "{name} expected string or regex, got {v:#}")
.ok_or_else(|| {
exception!(
*SYM_TYPE_ERROR,
"{name} expected string or regex, got {v:#}"
)
}),
_ => throw!(
*SYM_TYPE_ERROR,
"{name} expected string or regex, got {v:#}"
),
}
}
#[native_func(1)]
pub fn _regex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, re] = unpack_args!(args);
regex_from(&re, "regex")
.map(|re| ValueRegex(re.into_owned()).into())
regex_from(&re, "regex").map(|re| ValueRegex(re.into_owned()).into())
}
#[native_func(2)]
@ -132,7 +161,11 @@ pub fn _match(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
};
let re = regex_from(&re, "match")?;
Ok(re.find_iter(s).map(match_to_value).collect::<Vec<Value>>().into())
Ok(re
.find_iter(s)
.map(match_to_value)
.collect::<Vec<Value>>()
.into())
}
#[native_func(2)]
@ -158,7 +191,11 @@ pub fn captures(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
};
let re = regex_from(&re, "captures")?;
Ok(re.captures_iter(s).map(captures_to_value).collect::<Vec<Value>>().into())
Ok(re
.captures_iter(s)
.map(captures_to_value)
.collect::<Vec<Value>>()
.into())
}
#[native_func(3)]
@ -168,7 +205,10 @@ pub fn replace_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "replace_once expected string, got {s:#}")
};
let Value::String(rep) = rep else {
throw!(*SYM_TYPE_ERROR, "replace_once expected string or function, got {rep:#}")
throw!(
*SYM_TYPE_ERROR,
"replace_once expected string or function, got {rep:#}"
)
};
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
@ -187,7 +227,10 @@ pub fn replace(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_TYPE_ERROR, "replace expected string, got {s:#}")
};
let Value::String(rep) = rep else {
throw!(*SYM_TYPE_ERROR, "replace expected string or function, got {rep:#}")
throw!(
*SYM_TYPE_ERROR,
"replace expected string or function, got {rep:#}"
)
};
let Ok(s) = s.to_str() else {
throw!(*SYM_VALUE_ERROR, "search string must be valid UTF-8")
@ -212,7 +255,7 @@ pub fn split_once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let mut parts = re.splitn(s, 2);
let (part1, part2) = (
LString::from(parts.next().unwrap_or_default()).into(),
LString::from(parts.next().unwrap_or_default()).into()
LString::from(parts.next().unwrap_or_default()).into(),
);
Ok(vec![part1, part2].into())
}
@ -227,9 +270,6 @@ pub fn split(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
throw!(*SYM_VALUE_ERROR, "string to split must be valid UTF-8")
};
let re = regex_from(&re, "split")?;
let parts: Vec<Value> = re.split(s)
.map(|s| LString::from(s).into())
.collect();
let parts: Vec<Value> = re.split(s).map(|s| LString::from(s).into()).collect();
Ok(parts.into())
}

View file

@ -1,4 +1,11 @@
use talc_lang::{exception::Result, lstring::LString, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::Value, Vm};
use talc_lang::{
exception::Result,
lstring::LString,
symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::Value,
Vm,
};
use talc_macros::native_func;
use crate::unpack_args;
@ -54,7 +61,10 @@ pub fn chr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn len_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args);
let Value::String(s) = s else {
throw!(*SYM_TYPE_ERROR, "len_bytes expected string argument, got {s:#}")
throw!(
*SYM_TYPE_ERROR,
"len_bytes expected string argument, got {s:#}"
)
};
Ok(Value::Int(s.len() as i64))
}
@ -92,8 +102,10 @@ pub fn starts_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let res = match (d, pre) {
(Value::List(d), Value::List(pre)) => d.borrow().starts_with(&pre.borrow()),
(Value::String(d), Value::String(pre)) => d.starts_with(&pre),
(d, pre) => throw!(*SYM_TYPE_ERROR,
"starts_with expected two lists or strings, got {d:#} and {pre:#}")
(d, pre) => throw!(
*SYM_TYPE_ERROR,
"starts_with expected two lists or strings, got {d:#} and {pre:#}"
),
};
Ok(res.into())
}
@ -104,8 +116,10 @@ pub fn ends_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let res = match (d, suf) {
(Value::List(d), Value::List(suf)) => d.borrow().ends_with(&suf.borrow()),
(Value::String(d), Value::String(suf)) => d.ends_with(&suf),
(d, suf) => throw!(*SYM_TYPE_ERROR,
"ends_with expected two lists or strings, got {d:#} and {suf:#}")
(d, suf) => throw!(
*SYM_TYPE_ERROR,
"ends_with expected two lists or strings, got {d:#} and {suf:#}"
),
};
Ok(res.into())
}
@ -114,7 +128,10 @@ pub fn ends_with(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn is_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args);
let Value::String(s) = s else {
throw!(*SYM_TYPE_ERROR, "is_utf8 expected string argument, got {s:#}")
throw!(
*SYM_TYPE_ERROR,
"is_utf8 expected string argument, got {s:#}"
)
};
Ok(s.is_utf8().into())
}
@ -123,7 +140,10 @@ pub fn is_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn to_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args);
let Value::String(s) = s else {
throw!(*SYM_TYPE_ERROR, "is_utf8 expected string argument, got {s:#}")
throw!(
*SYM_TYPE_ERROR,
"is_utf8 expected string argument, got {s:#}"
)
};
if s.is_utf8() {
Ok(s.into())
@ -136,7 +156,10 @@ pub fn to_utf8(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn to_utf8_lossy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args);
let Value::String(s) = s else {
throw!(*SYM_TYPE_ERROR, "is_utf8 expected string argument, got {s:#}")
throw!(
*SYM_TYPE_ERROR,
"is_utf8 expected string argument, got {s:#}"
)
};
Ok(s.to_utf8_lossy().into())
}
@ -145,7 +168,10 @@ pub fn to_utf8_lossy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn str_to_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, s] = unpack_args!(args);
let Value::String(s) = s else {
throw!(*SYM_TYPE_ERROR, "str_to_bytes expected string argument, got {s:#}")
throw!(
*SYM_TYPE_ERROR,
"str_to_bytes expected string argument, got {s:#}"
)
};
Ok(s.as_bytes()
.iter()
@ -158,12 +184,21 @@ pub fn str_to_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, b] = unpack_args!(args);
let Value::List(b) = b else {
throw!(*SYM_TYPE_ERROR, "str_of_bytes expected list argument, got {b:#}")
throw!(
*SYM_TYPE_ERROR,
"str_of_bytes expected list argument, got {b:#}"
)
};
let bytes: Vec<u8> = b.borrow().iter()
let bytes: Vec<u8> = b
.borrow()
.iter()
.map(|v| match v {
Value::Int(i) if (0..=255).contains(i) => Ok(*i as u8),
_ => throw!(*SYM_VALUE_ERROR, "str_of_bytes expected list of integers in 0..=255"),
}).collect::<Result<Vec<u8>>>()?;
_ => throw!(
*SYM_VALUE_ERROR,
"str_of_bytes expected list of integers in 0..=255"
),
})
.collect::<Result<Vec<u8>>>()?;
Ok(LString::from(bytes).into())
}

View file

@ -1,6 +1,14 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use talc_lang::{exception::{exception, Result}, lformat, parser::{parse_float, parse_int}, symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{ops::RatioExt, HashValue, Rational64, Value}, Vm};
use talc_lang::{
exception::{exception, Result},
lformat,
parser::{parse_float, parse_int},
symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR},
throw,
value::{ops::RatioExt, HashValue, Rational64, Value},
Vm,
};
use talc_macros::native_func;
use crate::unpack_args;
@ -34,7 +42,6 @@ pub fn load(vm: &mut Vm) {
// types
//
#[native_func(1, "type")]
pub fn type_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
@ -74,59 +81,75 @@ pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
(Value::Ratio(x), b"complex") => Ok(Value::Complex(x.to_f64().into())),
(Value::Float(x), b"int") => Ok(Value::Int(x as i64)),
(Value::Float(x), b"ratio") => {
let r = Rational64::approximate_float(x)
.ok_or_else(|| exception!(*SYM_VALUE_ERROR, "float {x:?} could not be converted to ratio"))?;
let r = Rational64::approximate_float(x).ok_or_else(|| {
exception!(
*SYM_VALUE_ERROR,
"float {x:?} could not be converted to ratio"
)
})?;
Ok(Value::Ratio(r))
}
(Value::Float(x), b"complex") => Ok(Value::Complex(x.into())),
(Value::String(s), b"int")
=> parse_int(s.as_ref(), 10)
(Value::String(s), b"int") => parse_int(s.as_ref(), 10)
.map(i64::into)
.map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as integer")),
(Value::String(s), b"float")
=> parse_float(s.as_ref())
(Value::String(s), b"float") => parse_float(s.as_ref())
.map(f64::into)
.map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as float")),
(v, _) => throw!(*SYM_TYPE_ERROR,
"cannot convert value of type {} to type {}", v.get_type().name(), ty.name())
(v, _) => throw!(
*SYM_TYPE_ERROR,
"cannot convert value of type {} to type {}",
v.get_type().name(),
ty.name()
),
}
}
pub fn copy_inner(value: Value) -> Result<Value> {
match value {
Value::Nil | Value::Bool(_) | Value::Symbol(_)
| Value::Int(_) | Value::Ratio(_) | Value::Float(_)
| Value::Complex(_) | Value::Range(_) | Value::String(_)
=> Ok(value),
Value::Nil
| Value::Bool(_)
| Value::Symbol(_)
| Value::Int(_)
| Value::Ratio(_)
| Value::Float(_)
| Value::Complex(_)
| Value::Range(_)
| Value::String(_) => Ok(value),
Value::Cell(c) => {
let c = Rc::unwrap_or_clone(c).take();
let c = copy_inner(c)?;
Ok(RefCell::new(c).into())
},
}
Value::List(l) => {
let l = Rc::unwrap_or_clone(l).take();
let v: Result<Vec<Value>> = l.into_iter()
.map(copy_inner)
.collect();
let v: Result<Vec<Value>> = l.into_iter().map(copy_inner).collect();
Ok(v?.into())
},
}
Value::Table(t) => {
let t = Rc::unwrap_or_clone(t).take();
let v: Result<HashMap<HashValue, Value>> = t.into_iter()
let v: Result<HashMap<HashValue, Value>> = t
.into_iter()
.map(|(k, v)| copy_inner(v).map(|v| (k, v)))
.collect();
Ok(v?.into())
},
}
Value::Native(ref n) => match n.copy_value()? {
Some(x) => Ok(x),
None => throw!(*SYM_TYPE_ERROR,
"cannot copy value of type {}", value.get_type().name())
}
_ => throw!(*SYM_TYPE_ERROR,
"cannot copy value of type {}", value.get_type().name())
None => throw!(
*SYM_TYPE_ERROR,
"cannot copy value of type {}",
value.get_type().name()
),
},
_ => throw!(
*SYM_TYPE_ERROR,
"cannot copy value of type {}",
value.get_type().name()
),
}
}
@ -140,7 +163,6 @@ pub fn copy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// strings
//
#[native_func(1, "str")]
pub fn str_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
@ -191,7 +213,6 @@ pub fn symbol_exists(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
// cells
//
#[native_func(1)]
pub fn cell(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, value] = unpack_args!(args);
@ -235,16 +256,16 @@ pub fn func_state(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
match func {
Value::NativeFunc(_) => Ok(Value::Nil),
Value::Function(f) => {
let l: Vec<Value> = f.state.iter()
.map(|v| Value::Cell(v.clone()))
.collect();
let l: Vec<Value> = f.state.iter().map(|v| Value::Cell(v.clone())).collect();
Ok(l.into())
}
_ => throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a talc function")
_ => throw!(
*SYM_TYPE_ERROR,
"closure_state: {func:#} is not a talc function"
),
}
}
#[native_func(1)]
pub fn func_arity(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
@ -271,10 +292,16 @@ pub fn func_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn apply(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func, lst] = unpack_args!(args);
if func.func_attrs().is_none() {
throw!(*SYM_TYPE_ERROR, "apply: first argument must be a function, found {func:#}")
throw!(
*SYM_TYPE_ERROR,
"apply: first argument must be a function, found {func:#}"
)
}
let Value::List(l) = lst else {
throw!(*SYM_TYPE_ERROR, "apply: second argument must be a list, found {lst:#}")
throw!(
*SYM_TYPE_ERROR,
"apply: second argument must be a list, found {lst:#}"
)
};
let mut args = l.borrow().clone();
args.insert(0, func.clone());
@ -285,13 +312,19 @@ pub fn apply(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn compile(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, src] = unpack_args!(args);
let Value::String(src) = src else {
throw!(*SYM_TYPE_ERROR, "compile: argument must be a string, found {src:#}")
throw!(
*SYM_TYPE_ERROR,
"compile: argument must be a string, found {src:#}"
)
};
let src = src.to_str()
.map_err(|e| exception!(*SYM_VALUE_ERROR, "compile: argument must be valid unicode ({e})"))?;
let ast = talc_lang::parser::parse(src)
.map_err(|e| exception!(symbol!("parse_error"), "{e}"))?;
let src = src.to_str().map_err(|e| {
exception!(
*SYM_VALUE_ERROR,
"compile: argument must be valid unicode ({e})"
)
})?;
let ast =
talc_lang::parser::parse(src).map_err(|e| exception!(symbol!("parse_error"), "{e}"))?;
let func = talc_lang::compiler::compile(&ast, None);
Ok(func.into())
}