switch to symbols, update formatting
This commit is contained in:
parent
59f3e320e3
commit
801aaf7b78
23 changed files with 998 additions and 292 deletions
|
@ -1,5 +1,5 @@
|
||||||
use clap::{ColorChoice, Parser};
|
use clap::{ColorChoice, Parser};
|
||||||
use talc_lang::{compiler::compile, parser, value::function::disasm_recursive, Vm};
|
use talc_lang::{compiler::compile, parser, symbol::Symbol, value::function::disasm_recursive, Vm};
|
||||||
use std::{path::PathBuf, process::ExitCode, rc::Rc};
|
use std::{path::PathBuf, process::ExitCode, rc::Rc};
|
||||||
|
|
||||||
mod repl;
|
mod repl;
|
||||||
|
@ -28,7 +28,7 @@ struct Args {
|
||||||
color: ColorChoice,
|
color: ColorChoice,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(src: &str, args: &Args) -> ExitCode {
|
fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
|
||||||
let mut vm = Vm::new(256);
|
let mut vm = Vm::new(256);
|
||||||
talc_std::load_all(&mut vm);
|
talc_std::load_all(&mut vm);
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ fn exec(src: &str, args: &Args) -> ExitCode {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let func = Rc::new(compile(&ex));
|
let func = Rc::new(compile(&ex, Some(name)));
|
||||||
|
|
||||||
if args.disasm {
|
if args.disasm {
|
||||||
if let Err(e) = disasm_recursive(&func, &mut std::io::stderr()) {
|
if let Err(e) = disasm_recursive(&func, &mut std::io::stderr()) {
|
||||||
|
@ -64,8 +64,10 @@ fn main() -> ExitCode {
|
||||||
return repl::repl(&args)
|
return repl::repl(&args)
|
||||||
}
|
}
|
||||||
|
|
||||||
match std::fs::read_to_string(args.file.as_ref().unwrap()) {
|
let file = args.file.as_ref().unwrap();
|
||||||
Ok(s) => exec(&s, &args),
|
|
||||||
|
match std::fs::read_to_string(file) {
|
||||||
|
Ok(s) => exec(Symbol::get(file.as_os_str()), &s, &args),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error: {e}");
|
eprintln!("Error: {e}");
|
||||||
ExitCode::FAILURE
|
ExitCode::FAILURE
|
||||||
|
|
|
@ -3,9 +3,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
|
use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
|
||||||
use crate::chunk::{Instruction as I, Chunk, Arg24, Catch};
|
use crate::chunk::{Instruction as I, Chunk, Arg24, Catch};
|
||||||
use crate::lstr;
|
use crate::symbol::{Symbol, SYM_SELF};
|
||||||
use crate::lstring::LStr;
|
|
||||||
use crate::symbol::Symbol;
|
|
||||||
use crate::value::function::{FuncAttrs, Function};
|
use crate::value::function::{FuncAttrs, Function};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
|
@ -22,7 +20,7 @@ pub enum VarKind {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Var {
|
pub struct Var {
|
||||||
name: Rc<LStr>,
|
name: Symbol,
|
||||||
kind: VarKind,
|
kind: VarKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,19 +34,19 @@ struct Compiler<'a> {
|
||||||
parent: Option<&'a Compiler<'a>>,
|
parent: Option<&'a Compiler<'a>>,
|
||||||
chunk: Chunk,
|
chunk: Chunk,
|
||||||
attrs: FuncAttrs,
|
attrs: FuncAttrs,
|
||||||
scope: HashMap<Rc<LStr>, Var>,
|
scope: HashMap<Symbol, Var>,
|
||||||
shadowed: Vec<(Rc<LStr>, Option<Var>)>,
|
shadowed: Vec<(Symbol, Option<Var>)>,
|
||||||
closes: BTreeMap<Rc<LStr>, usize>,
|
closes: BTreeMap<Symbol, usize>,
|
||||||
local_count: usize,
|
local_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(expr: &Expr) -> Function {
|
pub fn compile(expr: &Expr, name: Option<Symbol>) -> Function {
|
||||||
let mut comp = Compiler::new_module(None);
|
let mut comp = Compiler::new_module(name, None);
|
||||||
comp.expr(expr);
|
comp.expr(expr);
|
||||||
comp.finish()
|
comp.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_repl(expr: &Expr, globals: &[Rc<LStr>]) -> (Function, Vec<Rc<LStr>>) {
|
pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec<Symbol>) {
|
||||||
let mut comp = Compiler::new_repl(globals);
|
let mut comp = Compiler::new_repl(globals);
|
||||||
comp.expr(expr);
|
comp.expr(expr);
|
||||||
comp.finish_repl()
|
comp.finish_repl()
|
||||||
|
@ -57,9 +55,8 @@ pub fn compile_repl(expr: &Expr, globals: &[Rc<LStr>]) -> (Function, Vec<Rc<LStr
|
||||||
impl<'a> Default for Compiler<'a> {
|
impl<'a> Default for Compiler<'a> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let mut scope = HashMap::new();
|
let mut scope = HashMap::new();
|
||||||
let self_name: Rc<LStr> = lstr!("self").into();
|
scope.insert(*SYM_SELF, Var {
|
||||||
scope.insert(self_name.clone(), Var {
|
name: *SYM_SELF,
|
||||||
name: self_name,
|
|
||||||
kind: VarKind::Local(0),
|
kind: VarKind::Local(0),
|
||||||
});
|
});
|
||||||
Self {
|
Self {
|
||||||
|
@ -76,36 +73,38 @@ impl<'a> Default for Compiler<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Compiler<'a> {
|
impl<'a> Compiler<'a> {
|
||||||
fn new_repl(globals: &'a [Rc<LStr>]) -> Self {
|
fn new_repl(globals: &[Symbol]) -> Self {
|
||||||
let mut new = Self {
|
let mut new = Self {
|
||||||
mode: CompilerMode::Repl,
|
mode: CompilerMode::Repl,
|
||||||
|
attrs: FuncAttrs { arity: 0, name: Some(Symbol::get("<repl>")) },
|
||||||
..Self::default()
|
..Self::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
for g in globals {
|
for g in globals {
|
||||||
new.declare_global(g);
|
new.declare_global(*g);
|
||||||
}
|
}
|
||||||
new
|
new
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_module(parent: Option<&'a Self>) -> Self {
|
fn new_module(name: Option<Symbol>, parent: Option<&'a Self>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mode: CompilerMode::Module,
|
mode: CompilerMode::Module,
|
||||||
|
attrs: FuncAttrs { arity: 0, name },
|
||||||
parent,
|
parent,
|
||||||
..Self::default()
|
..Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_function(&'a self, args: &[&LStr]) -> Self {
|
fn new_function(&'a self, name: Option<Symbol>, args: &[Symbol]) -> Self {
|
||||||
let mut new = Self {
|
let mut new = Self {
|
||||||
mode: CompilerMode::Function,
|
mode: CompilerMode::Function,
|
||||||
|
attrs: FuncAttrs { arity: args.len(), name },
|
||||||
parent: Some(self),
|
parent: Some(self),
|
||||||
..Self::default()
|
..Self::default()
|
||||||
};
|
};
|
||||||
new.attrs.arity = args.len();
|
|
||||||
|
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
new.declare_local(arg);
|
new.declare_local(*arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
new
|
new
|
||||||
|
@ -116,7 +115,7 @@ impl<'a> Compiler<'a> {
|
||||||
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len())
|
Function::new(Rc::new(self.chunk), self.attrs, self.closes.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_inner(mut self) -> (Function, BTreeMap<Rc<LStr>, usize>) {
|
pub fn finish_inner(mut self) -> (Function, BTreeMap<Symbol, usize>) {
|
||||||
self.emit(I::Return);
|
self.emit(I::Return);
|
||||||
// TODO closure
|
// TODO closure
|
||||||
(
|
(
|
||||||
|
@ -125,7 +124,7 @@ impl<'a> Compiler<'a> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_repl(mut self) -> (Function, Vec<Rc<LStr>>) {
|
pub fn finish_repl(mut self) -> (Function, Vec<Symbol>) {
|
||||||
self.emit(I::Return);
|
self.emit(I::Return);
|
||||||
(
|
(
|
||||||
// TODO closure
|
// TODO closure
|
||||||
|
@ -232,8 +231,8 @@ impl<'a> Compiler<'a> {
|
||||||
// variables
|
// variables
|
||||||
//
|
//
|
||||||
|
|
||||||
fn resolve_name(&self, name: &LStr) -> ResolveOutcome {
|
fn resolve_name(&self, name: Symbol) -> ResolveOutcome {
|
||||||
if let Some(v) = self.scope.get(name) {
|
if let Some(v) = self.scope.get(&name) {
|
||||||
return ResolveOutcome::Var(v.kind)
|
return ResolveOutcome::Var(v.kind)
|
||||||
}
|
}
|
||||||
let Some(parent) = self.parent else {
|
let Some(parent) = self.parent else {
|
||||||
|
@ -245,7 +244,7 @@ impl<'a> Compiler<'a> {
|
||||||
ResolveOutcome::InParent
|
ResolveOutcome::InParent
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_var(&mut self, name: &LStr) {
|
fn load_var(&mut self, name: Symbol) {
|
||||||
match self.resolve_name(name) {
|
match self.resolve_name(name) {
|
||||||
ResolveOutcome::Var(VarKind::Local(n)) => {
|
ResolveOutcome::Var(VarKind::Local(n)) => {
|
||||||
self.emit(I::LoadLocal(Arg24::from_usize(n)));
|
self.emit(I::LoadLocal(Arg24::from_usize(n)));
|
||||||
|
@ -254,57 +253,54 @@ impl<'a> Compiler<'a> {
|
||||||
self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
|
self.emit(I::LoadClosedLocal(Arg24::from_usize(n)));
|
||||||
}
|
}
|
||||||
ResolveOutcome::InParent => {
|
ResolveOutcome::InParent => {
|
||||||
let n = match self.closes.get(name) {
|
let n = match self.closes.get(&name) {
|
||||||
Some(n) => *n,
|
Some(n) => *n,
|
||||||
None => {
|
None => {
|
||||||
let n = self.closes.len();
|
let n = self.closes.len();
|
||||||
self.closes.insert(name.into(), n);
|
self.closes.insert(name, n);
|
||||||
n
|
n
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.emit(I::LoadUpvalue(Arg24::from_usize(n)));
|
self.emit(I::LoadUpvalue(Arg24::from_usize(n)));
|
||||||
}
|
}
|
||||||
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
|
ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => {
|
||||||
let s = Symbol::get(name);
|
self.emit(I::LoadGlobal(Arg24::from_symbol(name)));
|
||||||
self.emit(I::LoadGlobal(Arg24::from_symbol(s)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_local(&mut self, name: &LStr) -> usize {
|
fn declare_local(&mut self, name: Symbol) -> usize {
|
||||||
let name: Rc<LStr> = name.into();
|
|
||||||
let local = Var {
|
let local = Var {
|
||||||
name: name.clone(),
|
name,
|
||||||
kind: VarKind::Local(self.local_count)
|
kind: VarKind::Local(self.local_count)
|
||||||
};
|
};
|
||||||
self.local_count += 1;
|
self.local_count += 1;
|
||||||
let shadowed = self.scope.insert(name.clone(), local);
|
let shadowed = self.scope.insert(name, local);
|
||||||
self.shadowed.push((name, shadowed));
|
self.shadowed.push((name, shadowed));
|
||||||
self.local_count - 1
|
self.local_count - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_local(&mut self, name: &LStr) -> usize {
|
fn assign_local(&mut self, name: Symbol) -> usize {
|
||||||
let n = self.declare_local(name);
|
let n = self.declare_local(name);
|
||||||
self.emit(I::NewLocal);
|
self.emit(I::NewLocal);
|
||||||
n
|
n
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assign_global(&mut self, name: &LStr) {
|
fn assign_global(&mut self, name: Symbol) {
|
||||||
self.declare_global(name);
|
self.declare_global(name);
|
||||||
self.store_var(name);
|
self.store_var(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_global(&mut self, name: &LStr) {
|
fn declare_global(&mut self, name: Symbol) {
|
||||||
let name: Rc<LStr> = name.into();
|
|
||||||
let global = Var {
|
let global = Var {
|
||||||
name: name.clone(),
|
name,
|
||||||
kind: VarKind::Global
|
kind: VarKind::Global
|
||||||
};
|
};
|
||||||
let shadowed = self.scope.insert(name.clone(), global);
|
let shadowed = self.scope.insert(name, global);
|
||||||
self.shadowed.push((name, shadowed));
|
self.shadowed.push((name, shadowed));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_var(&mut self, name: &LStr) {
|
fn store_var(&mut self, name: Symbol) {
|
||||||
match self.resolve_name(name) {
|
match self.resolve_name(name) {
|
||||||
ResolveOutcome::Var(VarKind::Local(n)) => {
|
ResolveOutcome::Var(VarKind::Local(n)) => {
|
||||||
self.emit(I::StoreLocal(Arg24::from_usize(n)));
|
self.emit(I::StoreLocal(Arg24::from_usize(n)));
|
||||||
|
@ -313,23 +309,21 @@ impl<'a> Compiler<'a> {
|
||||||
self.emit(I::StoreClosedLocal(Arg24::from_usize(n)));
|
self.emit(I::StoreClosedLocal(Arg24::from_usize(n)));
|
||||||
}
|
}
|
||||||
ResolveOutcome::InParent => {
|
ResolveOutcome::InParent => {
|
||||||
let n = match self.closes.get(name) {
|
let n = match self.closes.get(&name) {
|
||||||
Some(n) => *n,
|
Some(n) => *n,
|
||||||
None => {
|
None => {
|
||||||
let n = self.closes.len();
|
let n = self.closes.len();
|
||||||
self.closes.insert(name.into(), n);
|
self.closes.insert(name, n);
|
||||||
n
|
n
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.emit(I::StoreUpvalue(Arg24::from_usize(n)));
|
self.emit(I::StoreUpvalue(Arg24::from_usize(n)));
|
||||||
}
|
}
|
||||||
ResolveOutcome::Var(VarKind::Global) => {
|
ResolveOutcome::Var(VarKind::Global) => {
|
||||||
let s = Symbol::get(name);
|
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
|
||||||
self.emit(I::StoreGlobal(Arg24::from_symbol(s)));
|
|
||||||
}
|
}
|
||||||
ResolveOutcome::None if self.mode == CompilerMode::Repl => {
|
ResolveOutcome::None if self.mode == CompilerMode::Repl => {
|
||||||
let s = Symbol::get(name);
|
self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
|
||||||
self.emit(I::StoreGlobal(Arg24::from_symbol(s)));
|
|
||||||
}
|
}
|
||||||
ResolveOutcome::None => {
|
ResolveOutcome::None => {
|
||||||
self.assign_local(name);
|
self.assign_local(name);
|
||||||
|
@ -357,7 +351,7 @@ impl<'a> Compiler<'a> {
|
||||||
self.end_scope(scope);
|
self.end_scope(scope);
|
||||||
},
|
},
|
||||||
ExprKind::Literal(v) => self.expr_literal(v),
|
ExprKind::Literal(v) => self.expr_literal(v),
|
||||||
ExprKind::Ident(ident) => self.load_var(ident),
|
ExprKind::Ident(ident) => self.load_var(*ident),
|
||||||
ExprKind::UnaryOp(o, a) => {
|
ExprKind::UnaryOp(o, a) => {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::UnaryOp(*o));
|
self.emit(I::UnaryOp(*o));
|
||||||
|
@ -371,12 +365,12 @@ impl<'a> Compiler<'a> {
|
||||||
ExprKind::AssignVar(name, a) => {
|
ExprKind::AssignVar(name, a) => {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::Dup);
|
self.emit(I::Dup);
|
||||||
self.assign_local(name);
|
self.assign_local(*name);
|
||||||
},
|
},
|
||||||
ExprKind::AssignGlobal(name, a) => {
|
ExprKind::AssignGlobal(name, a) => {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::Dup);
|
self.emit(I::Dup);
|
||||||
self.assign_global(name);
|
self.assign_global(*name);
|
||||||
},
|
},
|
||||||
ExprKind::List(xs) if xs.is_empty() => {
|
ExprKind::List(xs) if xs.is_empty() => {
|
||||||
self.emit(I::NewList(0));
|
self.emit(I::NewList(0));
|
||||||
|
@ -446,7 +440,8 @@ impl<'a> Compiler<'a> {
|
||||||
self.emit(I::Swap);
|
self.emit(I::Swap);
|
||||||
self.emit(I::Call(1));
|
self.emit(I::Call(1));
|
||||||
},
|
},
|
||||||
ExprKind::Lambda(args, body) => self.expr_lambda(args, body),
|
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) => {
|
ExprKind::And(a, b) => {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::Dup);
|
self.emit(I::Dup);
|
||||||
|
@ -490,7 +485,7 @@ impl<'a> Compiler<'a> {
|
||||||
|
|
||||||
self.emit(I::Nil);
|
self.emit(I::Nil);
|
||||||
},
|
},
|
||||||
ExprKind::For(name, iter, body) => self.expr_for(name, iter, body),
|
ExprKind::For(name, iter, body) => self.expr_for(*name, iter, body),
|
||||||
ExprKind::Try(body, catches) => self.expr_try(body, catches),
|
ExprKind::Try(body, catches) => self.expr_try(body, catches),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -534,7 +529,7 @@ impl<'a> Compiler<'a> {
|
||||||
self.chunk.finish_catch_table(idx, table);
|
self.chunk.finish_catch_table(idx, table);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_for(&mut self, name: &LStr, iter: &Expr, body: &Expr) {
|
fn expr_for(&mut self, name: Symbol, iter: &Expr, body: &Expr) {
|
||||||
// load iterable and convert to iterator
|
// load iterable and convert to iterator
|
||||||
self.expr(iter);
|
self.expr(iter);
|
||||||
self.emit(I::IterBegin);
|
self.emit(I::IterBegin);
|
||||||
|
@ -565,19 +560,18 @@ impl<'a> Compiler<'a> {
|
||||||
self.emit(I::Nil);
|
self.emit(I::Nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_lambda(&mut self, args: &[&LStr], body: &Expr) {
|
fn expr_fndef(&mut self, name: Option<Symbol>, args: &[Symbol], body: &Expr) {
|
||||||
let mut inner = self.new_function(args);
|
let mut inner = self.new_function(name, args);
|
||||||
inner.parent = Some(self);
|
inner.parent = Some(self);
|
||||||
inner.expr(body);
|
inner.expr(body);
|
||||||
|
|
||||||
|
|
||||||
let (func, closes) = inner.finish_inner();
|
let (func, closes) = inner.finish_inner();
|
||||||
let func_const = self.add_const(func.into());
|
let func_const = self.add_const(func.into());
|
||||||
|
|
||||||
let num_closed = closes.len();
|
let num_closed = closes.len();
|
||||||
|
|
||||||
for (name, _) in closes {
|
for (name, _) in closes {
|
||||||
match self.resolve_name(&name) {
|
match self.resolve_name(name) {
|
||||||
ResolveOutcome::Var(VarKind::Local(n)) => {
|
ResolveOutcome::Var(VarKind::Local(n)) => {
|
||||||
self.emit(I::CloseOver(Arg24::from_usize(n)));
|
self.emit(I::CloseOver(Arg24::from_usize(n)));
|
||||||
self.scope.entry(name).and_modify(|v| {
|
self.scope.entry(name).and_modify(|v| {
|
||||||
|
@ -602,6 +596,11 @@ impl<'a> Compiler<'a> {
|
||||||
} else {
|
} else {
|
||||||
self.emit(I::Closure(Arg24::from_usize(func_const)));
|
self.emit(I::Closure(Arg24::from_usize(func_const)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(name) = name {
|
||||||
|
self.emit(I::Dup);
|
||||||
|
self.store_var(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_literal(&mut self, val: &Value) {
|
fn expr_literal(&mut self, val: &Value) {
|
||||||
|
@ -626,14 +625,14 @@ impl<'a> Compiler<'a> {
|
||||||
(LValueKind::Ident(i), None) => {
|
(LValueKind::Ident(i), None) => {
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::Dup);
|
self.emit(I::Dup);
|
||||||
self.store_var(i);
|
self.store_var(*i);
|
||||||
},
|
},
|
||||||
(LValueKind::Ident(i), Some(o)) => {
|
(LValueKind::Ident(i), Some(o)) => {
|
||||||
self.load_var(i);
|
self.load_var(*i);
|
||||||
self.expr(a);
|
self.expr(a);
|
||||||
self.emit(I::BinaryOp(o));
|
self.emit(I::BinaryOp(o));
|
||||||
self.emit(I::Dup);
|
self.emit(I::Dup);
|
||||||
self.store_var(i);
|
self.store_var(*i);
|
||||||
},
|
},
|
||||||
(LValueKind::Index(ct, i), None) => {
|
(LValueKind::Index(ct, i), None) => {
|
||||||
self.expr(ct);
|
self.expr(ct);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{borrow::{Borrow, BorrowMut, Cow}, ffi::OsStr, fmt::{self, Write}, io, iter::{Copied, FusedIterator}, ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut}, rc::Rc, slice, str::Utf8Error, string::FromUtf8Error};
|
use std::{borrow::{Borrow, BorrowMut, Cow}, ffi::{OsStr, OsString}, fmt::{self, Write}, io, iter::{Copied, FusedIterator}, ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut}, rc::Rc, slice, str::Utf8Error, string::FromUtf8Error};
|
||||||
|
|
||||||
use unicode_ident::{is_xid_continue, is_xid_start};
|
use unicode_ident::{is_xid_continue, is_xid_start};
|
||||||
|
|
||||||
|
@ -243,6 +243,11 @@ impl From<String> for LString {
|
||||||
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() } }
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&LStr> for LString {
|
impl From<&LStr> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: &LStr) -> Self { value.to_owned() }
|
fn from(value: &LStr) -> Self { value.to_owned() }
|
||||||
|
@ -258,6 +263,16 @@ impl From<&[u8]> for LString {
|
||||||
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() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> From<[u8; N]> for LString {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: [u8; N]) -> Self { value.as_slice().into() }
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Cow<'_, LStr>> for LString {
|
impl From<Cow<'_, LStr>> for LString {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: Cow<'_, LStr>) -> Self {
|
fn from(value: Cow<'_, LStr>) -> Self {
|
||||||
|
@ -282,6 +297,13 @@ impl From<Cow<'_, [u8]>> for LString {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> From<Cow<'_, [u8; N]>> for LString {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: Cow<'_, [u8; N]>) -> Self {
|
||||||
|
value.into_owned().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a LStr> for &'a [u8] {
|
impl<'a> From<&'a LStr> for &'a [u8] {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: &'a LStr) -> Self { &value.inner }
|
fn from(value: &'a LStr) -> Self { &value.inner }
|
||||||
|
@ -294,6 +316,13 @@ impl<'a> From<&'a [u8]> for &'a LStr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, const N: usize> From<&'a [u8; N]> for &'a LStr {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: &'a [u8; N]) -> Self {
|
||||||
|
LStr::from_bytes(value.as_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a str> for &'a LStr {
|
impl<'a> From<&'a str> for &'a LStr {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: &'a str) -> Self {
|
fn from(value: &'a str) -> Self {
|
||||||
|
@ -301,6 +330,13 @@ impl<'a> From<&'a str> for &'a LStr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a OsStr> for &'a LStr {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: &'a OsStr) -> Self {
|
||||||
|
LStr::from_os_str(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a mut LStr> for &'a mut [u8] {
|
impl<'a> From<&'a mut LStr> for &'a mut [u8] {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: &'a mut LStr) -> Self { &mut value.inner }
|
fn from(value: &'a mut LStr) -> Self { &mut value.inner }
|
||||||
|
@ -573,6 +609,43 @@ impl<'a> DoubleEndedIterator for LosslessChars<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LosslessCharsIndices<'a>(&'a [u8], usize);
|
||||||
|
|
||||||
|
impl<'a> Iterator for LosslessCharsIndices<'a> {
|
||||||
|
type Item = (usize, Result<char, u8>);
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let (new_bytes, res) = next_codepoint(self.0)?;
|
||||||
|
let index = self.1;
|
||||||
|
self.0 = new_bytes;
|
||||||
|
match res {
|
||||||
|
Ok(c) => self.1 += c.len_utf8(),
|
||||||
|
Err(_) => self.1 += 1,
|
||||||
|
}
|
||||||
|
Some((index, res))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
let len = self.0.len();
|
||||||
|
((len + 3)/4, Some(len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> {
|
||||||
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
|
let (new_bytes, res) = next_codepoint_back(self.0)?;
|
||||||
|
self.0 = new_bytes;
|
||||||
|
match res {
|
||||||
|
Ok(c) => self.1 -= c.len_utf8(),
|
||||||
|
Err(_) => self.1 -= 1,
|
||||||
|
}
|
||||||
|
Some((self.1, res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Chars<'a>(LosslessChars<'a>);
|
pub struct Chars<'a>(LosslessChars<'a>);
|
||||||
|
@ -605,6 +678,36 @@ impl<'a> DoubleEndedIterator for Chars<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CharsIndices<'a>(LosslessCharsIndices<'a>);
|
||||||
|
|
||||||
|
impl<'a> Iterator for CharsIndices<'a> {
|
||||||
|
type Item = (usize, char);
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
if let (index, Ok(c)) = self.0.next()? {
|
||||||
|
return Some((index, c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
self.0.size_hint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DoubleEndedIterator for CharsIndices<'a> {
|
||||||
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
if let (index, Ok(c)) = self.0.next_back()? {
|
||||||
|
return Some((index, c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl LStr {
|
impl LStr {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -612,6 +715,11 @@ impl LStr {
|
||||||
Self::from_bytes(string.as_bytes())
|
Self::from_bytes(string.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_os_str(os_string: &OsStr) -> &Self {
|
||||||
|
Self::from_bytes(os_string.as_encoded_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn from_bytes(bytes: &[u8]) -> &Self {
|
pub const fn from_bytes(bytes: &[u8]) -> &Self {
|
||||||
unsafe { &*(bytes as *const [u8] as *const LStr) }
|
unsafe { &*(bytes as *const [u8] as *const LStr) }
|
||||||
|
@ -628,14 +736,22 @@ impl LStr {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_bytes_mut(&mut self) -> &mut [u8] { &mut self.inner }
|
pub fn as_bytes_mut(&mut self) -> &mut [u8] { &mut self.inner }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
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() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn bytes(&self) -> Bytes {
|
pub fn bytes(&self) -> Bytes {
|
||||||
Bytes(self.as_bytes().iter().copied())
|
Bytes(self.as_bytes().iter().copied())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn chars(&self) -> Chars {
|
pub fn chars(&self) -> Chars {
|
||||||
Chars(self.chars_lossless())
|
Chars(self.chars_lossless())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn chars_lossless(&self) -> LosslessChars {
|
pub fn chars_lossless(&self) -> LosslessChars {
|
||||||
LosslessChars(self.as_bytes())
|
LosslessChars(self.as_bytes())
|
||||||
|
@ -746,6 +862,14 @@ impl LStr {
|
||||||
self.as_bytes().ends_with(s.as_bytes())
|
self.as_bytes().ends_with(s.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn strip_prefix(&self, prefix: &LStr) -> Option<&LStr> {
|
||||||
|
self.as_bytes().strip_prefix(prefix.as_bytes()).map(LStr::from_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn strip_suffix(&self, suffix: &LStr) -> Option<&LStr> {
|
||||||
|
self.as_bytes().strip_suffix(suffix.as_bytes()).map(LStr::from_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_identifier(&self) -> bool {
|
pub fn is_identifier(&self) -> bool {
|
||||||
let mut chars = self.chars_lossless();
|
let mut chars = self.chars_lossless();
|
||||||
let first = chars.next()
|
let first = chars.next()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
use crate::{lstring::LStr, symbol::Symbol, value::Value};
|
use crate::{symbol::Symbol, value::Value};
|
||||||
|
|
||||||
use super::Span;
|
use super::Span;
|
||||||
|
|
||||||
|
@ -19,76 +19,77 @@ pub enum UnaryOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Expr<'s> {
|
pub struct Expr {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub kind: ExprKind<'s>,
|
pub kind: ExprKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ExprKind<'s> {
|
pub enum ExprKind {
|
||||||
Literal(Value),
|
Literal(Value),
|
||||||
Ident(&'s LStr),
|
Ident(Symbol),
|
||||||
|
|
||||||
UnaryOp(UnaryOp, Box<Expr<'s>>),
|
UnaryOp(UnaryOp, Box<Expr>),
|
||||||
BinaryOp(BinaryOp, Box<Expr<'s>>, Box<Expr<'s>>),
|
BinaryOp(BinaryOp, Box<Expr>, Box<Expr>),
|
||||||
|
|
||||||
Assign(Option<BinaryOp>, Box<LValue<'s>>, Box<Expr<'s>>),
|
Assign(Option<BinaryOp>, Box<LValue>, Box<Expr>),
|
||||||
AssignVar(&'s LStr, Box<Expr<'s>>),
|
AssignVar(Symbol, Box<Expr>),
|
||||||
AssignGlobal(&'s LStr, Box<Expr<'s>>),
|
AssignGlobal(Symbol, Box<Expr>),
|
||||||
|
FnDef(Option<Symbol>, Vec<Symbol>, Box<Expr>),
|
||||||
|
|
||||||
Index(Box<Expr<'s>>, Box<Expr<'s>>),
|
Index(Box<Expr>, Box<Expr>),
|
||||||
FnCall(Box<Expr<'s>>, Vec<Expr<'s>>),
|
FnCall(Box<Expr>, Vec<Expr>),
|
||||||
AssocFnCall(Box<Expr<'s>>, Symbol, Vec<Expr<'s>>),
|
AssocFnCall(Box<Expr>, Symbol, Vec<Expr>),
|
||||||
Pipe(Box<Expr<'s>>, Box<Expr<'s>>),
|
Pipe(Box<Expr>, Box<Expr>),
|
||||||
|
|
||||||
Block(Vec<Expr<'s>>),
|
Block(Vec<Expr>),
|
||||||
List(Vec<Expr<'s>>),
|
List(Vec<Expr>),
|
||||||
Table(Vec<(Expr<'s>, Expr<'s>)>),
|
Table(Vec<(Expr, Expr)>),
|
||||||
|
|
||||||
Return(Box<Expr<'s>>),
|
Return(Box<Expr>),
|
||||||
And(Box<Expr<'s>>, Box<Expr<'s>>),
|
And(Box<Expr>, Box<Expr>),
|
||||||
Or(Box<Expr<'s>>, Box<Expr<'s>>),
|
Or(Box<Expr>, Box<Expr>),
|
||||||
If(Box<Expr<'s>>, Box<Expr<'s>>, Option<Box<Expr<'s>>>),
|
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
|
||||||
While(Box<Expr<'s>>, Box<Expr<'s>>),
|
While(Box<Expr>, Box<Expr>),
|
||||||
For(&'s LStr, Box<Expr<'s>>, Box<Expr<'s>>),
|
For(Symbol, Box<Expr>, Box<Expr>),
|
||||||
Lambda(Vec<&'s LStr>, Box<Expr<'s>>),
|
Lambda(Vec<Symbol>, Box<Expr>),
|
||||||
Try(Box<Expr<'s>>, Vec<CatchBlock<'s>>),
|
Try(Box<Expr>, Vec<CatchBlock>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> ExprKind<'s> {
|
impl ExprKind {
|
||||||
pub fn span(self, span: Span) -> Expr<'s> {
|
pub fn span(self, span: Span) -> Expr {
|
||||||
Expr { kind: self, span }
|
Expr { kind: self, span }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CatchBlock<'s> {
|
pub struct CatchBlock {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub name: Option<&'s LStr>,
|
pub name: Option<Symbol>,
|
||||||
pub types: Option<Vec<Symbol>>,
|
pub types: Option<Vec<Symbol>>,
|
||||||
pub body: Expr<'s>,
|
pub body: Expr,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LValue<'s> {
|
pub struct LValue {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub kind: LValueKind<'s>,
|
pub kind: LValueKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum LValueKind<'s> {
|
pub enum LValueKind {
|
||||||
Ident(&'s LStr),
|
Ident(Symbol),
|
||||||
Index(Box<Expr<'s>>, Box<Expr<'s>>),
|
Index(Box<Expr>, Box<Expr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> LValueKind<'s> {
|
impl LValueKind {
|
||||||
pub fn span(self, span: Span) -> LValue<'s> {
|
pub fn span(self, span: Span) -> LValue {
|
||||||
LValue { kind: self, span }
|
LValue { kind: self, span }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> LValue<'s> {
|
impl LValue {
|
||||||
pub fn from_expr(e: Expr<'s>) -> Option<LValue<'s>> {
|
pub fn from_expr(e: Expr) -> Option<LValue> {
|
||||||
let Expr { span, kind } = e;
|
let Expr { span, kind } = e;
|
||||||
match kind {
|
match kind {
|
||||||
ExprKind::Ident(i) => Some(LValueKind::Ident(i).span(span)),
|
ExprKind::Ident(i) => Some(LValueKind::Ident(i).span(span)),
|
||||||
|
@ -98,11 +99,11 @@ impl<'s> LValue<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> CatchBlock<'s> {
|
impl CatchBlock {
|
||||||
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
|
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
|
||||||
write!(w, "{0: >1$}catch", "", depth*2)?;
|
write!(w, "{0: >1$}catch", "", depth*2)?;
|
||||||
if let Some(name) = self.name {
|
if let Some(name) = self.name {
|
||||||
write!(w, " ${name}")?;
|
write!(w, " ${}", name.name())?;
|
||||||
}
|
}
|
||||||
if let Some(types) = &self.types {
|
if let Some(types) = &self.types {
|
||||||
write!(w, ":")?;
|
write!(w, ":")?;
|
||||||
|
@ -115,18 +116,18 @@ impl<'s> CatchBlock<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CatchBlock<'_> {
|
impl fmt::Display for CatchBlock {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.write_to(f, 0)
|
self.write_to(f, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> LValue<'s> {
|
impl LValue {
|
||||||
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
|
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
|
||||||
write!(w, "{0: >1$}", "", depth*2)?;
|
write!(w, "{0: >1$}", "", depth*2)?;
|
||||||
let depth = depth + 1;
|
let depth = depth + 1;
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
LValueKind::Ident(n) => writeln!(w, "${n}"),
|
LValueKind::Ident(n) => writeln!(w, "${}", n.name()),
|
||||||
LValueKind::Index(l, r) => {
|
LValueKind::Index(l, r) => {
|
||||||
writeln!(w, "index")?;
|
writeln!(w, "index")?;
|
||||||
l.write_to(w, depth)?;
|
l.write_to(w, depth)?;
|
||||||
|
@ -136,19 +137,19 @@ impl<'s> LValue<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for LValue<'_> {
|
impl fmt::Display for LValue {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.write_to(f, 0)
|
self.write_to(f, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Expr<'s> {
|
impl Expr {
|
||||||
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
|
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
|
||||||
write!(w, "{0: >1$}", "", depth*2)?;
|
write!(w, "{0: >1$}", "", depth*2)?;
|
||||||
let depth = depth + 1;
|
let depth = depth + 1;
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
ExprKind::Literal(val) => writeln!(w, "{val}"),
|
ExprKind::Literal(val) => writeln!(w, "{val}"),
|
||||||
ExprKind::Ident(n) => writeln!(w, "${n}"),
|
ExprKind::Ident(n) => writeln!(w, "${}", n.name()),
|
||||||
ExprKind::UnaryOp(op, e) => {
|
ExprKind::UnaryOp(op, e) => {
|
||||||
writeln!(w, "uop {op:?}")?;
|
writeln!(w, "uop {op:?}")?;
|
||||||
e.write_to(w, depth)
|
e.write_to(w, depth)
|
||||||
|
@ -168,13 +169,24 @@ impl<'s> Expr<'s> {
|
||||||
r.write_to(w, depth)
|
r.write_to(w, depth)
|
||||||
},
|
},
|
||||||
ExprKind::AssignVar(l, r) => {
|
ExprKind::AssignVar(l, r) => {
|
||||||
writeln!(w, "var {l}")?;
|
writeln!(w, "var {}", l.name())?;
|
||||||
r.write_to(w, depth)
|
r.write_to(w, depth)
|
||||||
},
|
},
|
||||||
ExprKind::AssignGlobal(l, r) => {
|
ExprKind::AssignGlobal(l, r) => {
|
||||||
writeln!(w, "global {l}")?;
|
writeln!(w, "global {}", l.name())?;
|
||||||
r.write_to(w, depth)
|
r.write_to(w, depth)
|
||||||
},
|
},
|
||||||
|
ExprKind::FnDef(n, p, b) => {
|
||||||
|
if let Some(n) = n {
|
||||||
|
writeln!(w, "fndef ${}", n.name())?;
|
||||||
|
} else {
|
||||||
|
writeln!(w, "fndef anon")?;
|
||||||
|
}
|
||||||
|
for arg in p {
|
||||||
|
writeln!(w, " ${}", arg.name())?;
|
||||||
|
}
|
||||||
|
b.write_to(w, depth)
|
||||||
|
},
|
||||||
ExprKind::Index(l, r) => {
|
ExprKind::Index(l, r) => {
|
||||||
writeln!(w, "index")?;
|
writeln!(w, "index")?;
|
||||||
l.write_to(w, depth)?;
|
l.write_to(w, depth)?;
|
||||||
|
@ -252,14 +264,14 @@ impl<'s> Expr<'s> {
|
||||||
b.write_to(w, depth)
|
b.write_to(w, depth)
|
||||||
}
|
}
|
||||||
ExprKind::For(v, i, b) => {
|
ExprKind::For(v, i, b) => {
|
||||||
writeln!(w, "for {v}")?;
|
writeln!(w, "for {}", v.name())?;
|
||||||
i.write_to(w, depth)?;
|
i.write_to(w, depth)?;
|
||||||
b.write_to(w, depth)
|
b.write_to(w, depth)
|
||||||
}
|
}
|
||||||
ExprKind::Lambda(a, b) => {
|
ExprKind::Lambda(a, b) => {
|
||||||
write!(w, "lambda")?;
|
write!(w, "lambda")?;
|
||||||
for arg in a {
|
for arg in a {
|
||||||
write!(w, " {arg}")?;
|
write!(w, " {}", arg.name())?;
|
||||||
}
|
}
|
||||||
writeln!(w)?;
|
writeln!(w)?;
|
||||||
b.write_to(w, depth)
|
b.write_to(w, depth)
|
||||||
|
@ -276,7 +288,7 @@ impl<'s> Expr<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Expr<'_> {
|
impl fmt::Display for Expr {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.write_to(f, 0)
|
self.write_to(f, 0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,7 @@ pub enum TokenKind {
|
||||||
Else,
|
Else,
|
||||||
End,
|
End,
|
||||||
False,
|
False,
|
||||||
|
Fn,
|
||||||
For,
|
For,
|
||||||
Global,
|
Global,
|
||||||
If,
|
If,
|
||||||
|
@ -167,6 +168,7 @@ impl TokenKind {
|
||||||
K::Else => "'else'",
|
K::Else => "'else'",
|
||||||
K::End => "'end'",
|
K::End => "'end'",
|
||||||
K::False => "'false'",
|
K::False => "'false'",
|
||||||
|
K::Fn => "'fn'",
|
||||||
K::For => "'for'",
|
K::For => "'for'",
|
||||||
K::Global => "'global'",
|
K::Global => "'global'",
|
||||||
K::If => "'if'",
|
K::If => "'if'",
|
||||||
|
@ -295,6 +297,7 @@ impl<'s> Lexer<'s> {
|
||||||
"else" => K::Else,
|
"else" => K::Else,
|
||||||
"end" => K::End,
|
"end" => K::End,
|
||||||
"false" => K::False,
|
"false" => K::False,
|
||||||
|
"fn" => K::Fn,
|
||||||
"for" => K::For,
|
"for" => K::For,
|
||||||
"global" => K::Global,
|
"global" => K::Global,
|
||||||
"if" => K::If,
|
"if" => K::If,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
|
|
||||||
use crate::{lstr, lstring::LStr, symbol::Symbol, 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 num_complex::Complex64;
|
||||||
|
@ -164,6 +164,7 @@ impl TokenKind {
|
||||||
| T::Return
|
| T::Return
|
||||||
| T::Var
|
| T::Var
|
||||||
| T::Global
|
| T::Global
|
||||||
|
| T::Fn
|
||||||
| T::Not
|
| T::Not
|
||||||
| T::Backslash
|
| T::Backslash
|
||||||
| T::Colon
|
| T::Colon
|
||||||
|
@ -209,7 +210,7 @@ impl<'s> Parser<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn parse_table_items(&mut self) -> Result<Vec<(Expr<'s>, Expr<'s>)>> {
|
fn parse_table_items(&mut self) -> Result<Vec<(Expr, Expr)>> {
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
while self.peek()?.kind.expr_first() {
|
while self.peek()?.kind.expr_first() {
|
||||||
let key = if let Some(id) = try_next!(self, T::Identifier) {
|
let key = if let Some(id) = try_next!(self, T::Identifier) {
|
||||||
|
@ -231,7 +232,7 @@ impl<'s> Parser<'s> {
|
||||||
Ok(items)
|
Ok(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expr_list(&mut self) -> Result<Vec<Expr<'s>>> {
|
fn parse_expr_list(&mut self) -> Result<Vec<Expr>> {
|
||||||
let mut exprs = Vec::new();
|
let mut exprs = Vec::new();
|
||||||
while self.peek()?.kind.expr_first() {
|
while self.peek()?.kind.expr_first() {
|
||||||
exprs.push(self.parse_expr()?);
|
exprs.push(self.parse_expr()?);
|
||||||
|
@ -242,10 +243,10 @@ impl<'s> Parser<'s> {
|
||||||
Ok(exprs)
|
Ok(exprs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ident_list(&mut self) -> Result<Vec<&'s LStr>> {
|
fn parse_ident_list(&mut self) -> Result<Vec<Symbol>> {
|
||||||
let mut idents = Vec::new();
|
let mut idents = Vec::new();
|
||||||
while let Some(tok) = try_next!(self, T::Identifier) {
|
while let Some(tok) = try_next!(self, T::Identifier) {
|
||||||
idents.push(tok.content.into());
|
idents.push(Symbol::get(tok.content));
|
||||||
if try_next!(self, T::Comma).is_none() {
|
if try_next!(self, T::Comma).is_none() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -264,7 +265,7 @@ impl<'s> Parser<'s> {
|
||||||
Ok(syms)
|
Ok(syms)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_catch_blocks(&mut self) -> Result<(Vec<CatchBlock<'s>>, Span)> {
|
fn parse_catch_blocks(&mut self) -> Result<(Vec<CatchBlock>, Span)> {
|
||||||
let mut blocks = Vec::new();
|
let mut blocks = Vec::new();
|
||||||
let mut outer_span = self.peek()?.span;
|
let mut outer_span = self.peek()?.span;
|
||||||
loop {
|
loop {
|
||||||
|
@ -277,7 +278,7 @@ impl<'s> Parser<'s> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = match try_next!(self, T::In) {
|
let name = match try_next!(self, T::In) {
|
||||||
Some(_) => Some(expect!(self, T::Identifier).content.into()),
|
Some(_) => Some(Symbol::get(expect!(self, T::Identifier).content)),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -291,7 +292,7 @@ impl<'s> Parser<'s> {
|
||||||
Ok((blocks, outer_span))
|
Ok((blocks, outer_span))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_if_stmt_chain(&mut self) -> Result<Expr<'s>> {
|
fn parse_if_stmt_chain(&mut self) -> Result<Expr> {
|
||||||
let cond = self.parse_expr()?;
|
let cond = self.parse_expr()?;
|
||||||
expect!(self, T::Then);
|
expect!(self, T::Then);
|
||||||
let body = self.parse_block()?;
|
let body = self.parse_block()?;
|
||||||
|
@ -315,7 +316,7 @@ impl<'s> Parser<'s> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_term_not_ident(&mut self) -> Result<Expr<'s>> {
|
fn parse_term_not_ident(&mut self) -> Result<Expr> {
|
||||||
let tok = self.next()?;
|
let tok = self.next()?;
|
||||||
match tok.kind {
|
match tok.kind {
|
||||||
T::LParen => {
|
T::LParen => {
|
||||||
|
@ -333,7 +334,7 @@ impl<'s> Parser<'s> {
|
||||||
let end = expect!(self, T::RBrace);
|
let end = expect!(self, T::RBrace);
|
||||||
Ok(E::Table(args).span(tok.span + end.span))
|
Ok(E::Table(args).span(tok.span + end.span))
|
||||||
},
|
},
|
||||||
T::Dollar => Ok(E::Ident("$".into()).span(tok.span)),
|
T::Dollar => Ok(E::Ident(*SYM_DOLLAR_SIGN).span(tok.span)),
|
||||||
T::Do => {
|
T::Do => {
|
||||||
let b = self.parse_block()?;
|
let b = self.parse_block()?;
|
||||||
expect!(self, T::End);
|
expect!(self, T::End);
|
||||||
|
@ -356,7 +357,7 @@ impl<'s> Parser<'s> {
|
||||||
let body = self.parse_block()?;
|
let body = self.parse_block()?;
|
||||||
let end = expect!(self, T::End);
|
let end = expect!(self, T::End);
|
||||||
let span = var.span + end.span;
|
let span = var.span + end.span;
|
||||||
Ok(E::For(var.content.into(), b(iter), b(body)).span(span))
|
Ok(E::For(Symbol::get(var.content), b(iter), b(body)).span(span))
|
||||||
},
|
},
|
||||||
T::Try => {
|
T::Try => {
|
||||||
let body = self.parse_block()?;
|
let body = self.parse_block()?;
|
||||||
|
@ -406,15 +407,15 @@ impl<'s> Parser<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_term(&mut self) -> Result<Expr<'s>> {
|
fn parse_term(&mut self) -> Result<Expr> {
|
||||||
if let Some(tok) = try_next!(self, T::Identifier) {
|
if let Some(tok) = try_next!(self, T::Identifier) {
|
||||||
Ok(E::Ident(tok.content.into()).span(tok.span))
|
Ok(E::Ident(Symbol::get(tok.content)).span(tok.span))
|
||||||
} else {
|
} else {
|
||||||
self.parse_term_not_ident()
|
self.parse_term_not_ident()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_access(&mut self) -> Result<Expr<'s>> {
|
fn parse_access(&mut self) -> Result<Expr> {
|
||||||
let mut lhs = self.parse_term()?;
|
let mut lhs = self.parse_term()?;
|
||||||
loop {
|
loop {
|
||||||
let tok = try_next!(self, T::LParen | T::LBrack | T::Arrow | T::Dot);
|
let tok = try_next!(self, T::LParen | T::LBrack | T::Arrow | T::Dot);
|
||||||
|
@ -454,7 +455,7 @@ impl<'s> Parser<'s> {
|
||||||
Ok(lhs)
|
Ok(lhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_precedence(&mut self, min_prec: u8) -> Result<Expr<'s>> {
|
fn parse_precedence(&mut self, min_prec: u8) -> Result<Expr> {
|
||||||
let mut lhs = if let Some(op) = self.peek()?.kind.unary_op() {
|
let mut lhs = if let Some(op) = self.peek()?.kind.unary_op() {
|
||||||
let tok = self.next()?;
|
let tok = self.next()?;
|
||||||
let rhs = self.parse_precedence(op.precedence())?;
|
let rhs = self.parse_precedence(op.precedence())?;
|
||||||
|
@ -495,7 +496,7 @@ impl<'s> Parser<'s> {
|
||||||
Ok(lhs)
|
Ok(lhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_lambda(&mut self) -> Result<Expr<'s>> {
|
fn parse_lambda(&mut self) -> Result<Expr> {
|
||||||
let tok = try_next!(self, T::Backslash | T::Colon);
|
let tok = try_next!(self, T::Backslash | T::Colon);
|
||||||
match tok {
|
match tok {
|
||||||
Some(Token { kind: T::Backslash, span, .. }) => {
|
Some(Token { kind: T::Backslash, span, .. }) => {
|
||||||
|
@ -506,7 +507,7 @@ impl<'s> Parser<'s> {
|
||||||
Ok(E::Lambda(args, b(body)).span(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![lstr!("$")];
|
let args = vec![*SYM_DOLLAR_SIGN];
|
||||||
let body = self.parse_lambda()?;
|
let body = self.parse_lambda()?;
|
||||||
let body_span = body.span;
|
let body_span = body.span;
|
||||||
Ok(E::Lambda(args, b(body)).span(span + body_span))
|
Ok(E::Lambda(args, b(body)).span(span + body_span))
|
||||||
|
@ -516,7 +517,7 @@ impl<'s> Parser<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_pipeline(&mut self) -> Result<Expr<'s>> {
|
fn parse_pipeline(&mut self) -> Result<Expr> {
|
||||||
let mut lhs = self.parse_lambda()?;
|
let mut lhs = self.parse_lambda()?;
|
||||||
let mut span = lhs.span;
|
let mut span = lhs.span;
|
||||||
while try_next!(self, T::Pipe).is_some() {
|
while try_next!(self, T::Pipe).is_some() {
|
||||||
|
@ -527,7 +528,7 @@ impl<'s> Parser<'s> {
|
||||||
Ok(lhs)
|
Ok(lhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_not(&mut self) -> Result<Expr<'s>> {
|
fn parse_not(&mut self) -> Result<Expr> {
|
||||||
if let Some(tok) = try_next!(self, T::Not) {
|
if let Some(tok) = try_next!(self, T::Not) {
|
||||||
let expr = self.parse_not()?;
|
let expr = self.parse_not()?;
|
||||||
let span = tok.span + expr.span;
|
let span = tok.span + expr.span;
|
||||||
|
@ -537,7 +538,7 @@ impl<'s> Parser<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_and(&mut self) -> Result<Expr<'s>> {
|
fn parse_and(&mut self) -> Result<Expr> {
|
||||||
let mut lhs = self.parse_not()?;
|
let mut lhs = self.parse_not()?;
|
||||||
let mut span = lhs.span;
|
let mut span = lhs.span;
|
||||||
while try_next!(self, T::And).is_some() {
|
while try_next!(self, T::And).is_some() {
|
||||||
|
@ -548,7 +549,7 @@ impl<'s> Parser<'s> {
|
||||||
Ok(lhs)
|
Ok(lhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_or(&mut self) -> Result<Expr<'s>> {
|
fn parse_or(&mut self) -> Result<Expr> {
|
||||||
let mut lhs = self.parse_and()?;
|
let mut lhs = self.parse_and()?;
|
||||||
let mut span = lhs.span;
|
let mut span = lhs.span;
|
||||||
while try_next!(self, T::Or).is_some() {
|
while try_next!(self, T::Or).is_some() {
|
||||||
|
@ -559,16 +560,7 @@ impl<'s> Parser<'s> {
|
||||||
Ok(lhs)
|
Ok(lhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_assign(&mut self) -> Result<Expr<'s>> {
|
fn parse_assign(&mut self) -> Result<Expr> {
|
||||||
if let Some(tok) = try_next!(self, T::Global | T::Var) {
|
|
||||||
let name = expect!(self, T::Identifier);
|
|
||||||
expect!(self, T::Equal);
|
|
||||||
let val = self.parse_or()?;
|
|
||||||
let val_span = val.span;
|
|
||||||
let kind = if tok.kind == T::Global { E::AssignGlobal } else { E::AssignVar };
|
|
||||||
return Ok(kind(name.content.into(), b(val))
|
|
||||||
.span(tok.span + val_span))
|
|
||||||
}
|
|
||||||
let lhs = self.parse_or()?;
|
let lhs = self.parse_or()?;
|
||||||
let lhs_span = lhs.span;
|
let lhs_span = lhs.span;
|
||||||
if let Some(op) = self.peek()?.kind.assign_op() {
|
if let Some(op) = self.peek()?.kind.assign_op() {
|
||||||
|
@ -576,7 +568,7 @@ impl<'s> Parser<'s> {
|
||||||
throw!(lhs_span, "invalid lvalue for assingment")
|
throw!(lhs_span, "invalid lvalue for assingment")
|
||||||
};
|
};
|
||||||
self.next()?;
|
self.next()?;
|
||||||
let rhs = self.parse_assign()?;
|
let rhs = self.parse_decl()?;
|
||||||
let rhs_span = rhs.span;
|
let rhs_span = rhs.span;
|
||||||
Ok(E::Assign(op, b(lval), b(rhs)).span(lhs_span + rhs_span))
|
Ok(E::Assign(op, b(lval), b(rhs)).span(lhs_span + rhs_span))
|
||||||
} else {
|
} else {
|
||||||
|
@ -584,17 +576,59 @@ impl<'s> Parser<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expr(&mut self) -> Result<Expr<'s>> {
|
fn parse_var_decl(&mut self) -> Result<Expr> {
|
||||||
if let Some(tok) = try_next!(self, T::Return) {
|
let first = expect!(self, T::Var | T::Global);
|
||||||
let expr = self.parse_assign()?;
|
let name = expect!(self, T::Identifier);
|
||||||
let span = expr.span;
|
expect!(self, T::Equal);
|
||||||
Ok(E::Return(b(expr)).span(tok.span + span))
|
let val = self.parse_decl()?;
|
||||||
} else {
|
let val_span = val.span;
|
||||||
self.parse_assign()
|
let kind = if first.kind == T::Global { E::AssignGlobal } else { E::AssignVar };
|
||||||
|
Ok(kind(Symbol::get(name.content), b(val))
|
||||||
|
.span(first.span + val_span))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_fn_decl(&mut self) -> Result<Expr> {
|
||||||
|
let tok_fn = expect!(self, T::Fn);
|
||||||
|
let name = try_next!(self, T::Identifier).map(|t| Symbol::get(t.content));
|
||||||
|
expect!(self, T::LParen);
|
||||||
|
let args = self.parse_ident_list()?;
|
||||||
|
expect!(self, T::RParen);
|
||||||
|
match expect!(self, T::Do | T::Equal).kind {
|
||||||
|
T::Do => {
|
||||||
|
let content = self.parse_block()?;
|
||||||
|
let end = expect!(self, T::End);
|
||||||
|
Ok(E::FnDef(name, args, b(content)).span(tok_fn.span + end.span))
|
||||||
|
},
|
||||||
|
T::Equal => {
|
||||||
|
let content = self.parse_expr()?;
|
||||||
|
let span = tok_fn.span + content.span;
|
||||||
|
Ok(E::FnDef(name, args, b(content)).span(span))
|
||||||
|
},
|
||||||
|
_ => unreachable!("parse_fn_decl: guaranteed by try_next!"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_decl(&mut self) -> Result<Expr> {
|
||||||
|
match self.peek()?.kind {
|
||||||
|
T::Var | T::Global => self.parse_var_decl(),
|
||||||
|
T::Fn => self.parse_fn_decl(),
|
||||||
|
_ => self.parse_assign(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_block(&mut self) -> Result<Expr<'s>> {
|
fn parse_expr(&mut self) -> Result<Expr> {
|
||||||
|
if let Some(tok) = try_next!(self, T::Return) {
|
||||||
|
let expr = self.parse_decl()?;
|
||||||
|
let span = expr.span;
|
||||||
|
Ok(E::Return(b(expr)).span(tok.span + span))
|
||||||
|
} else {
|
||||||
|
self.parse_decl()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_block(&mut self) -> Result<Expr> {
|
||||||
while try_next!(self, T::LineSeparator).is_some() {}
|
while try_next!(self, T::LineSeparator).is_some() {}
|
||||||
|
|
||||||
let mut span = self.peek()?.span;
|
let mut span = self.peek()?.span;
|
||||||
|
@ -612,7 +646,7 @@ impl<'s> Parser<'s> {
|
||||||
Ok(E::Block(exprs).span(span))
|
Ok(E::Block(exprs).span(span))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(mut self) -> Result<Expr<'s>> {
|
fn parse(mut self) -> Result<Expr> {
|
||||||
let block = self.parse_block()?;
|
let block = self.parse_block()?;
|
||||||
expect!(self, T::Eof);
|
expect!(self, T::Eof);
|
||||||
Ok(block)
|
Ok(block)
|
||||||
|
|
|
@ -132,3 +132,33 @@ pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result<i64, ParseIntErr
|
||||||
_ => parse_int(f, 10),
|
_ => parse_int(f, 10),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
|
||||||
|
let mut result = vec![];
|
||||||
|
let mut begin = 0;
|
||||||
|
|
||||||
|
let mut x;
|
||||||
|
if n < 0 {
|
||||||
|
result.push('-' as u32 as u8);
|
||||||
|
begin = 1;
|
||||||
|
x = (-n) as u64;
|
||||||
|
} else {
|
||||||
|
x = n as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let m = x % (radix as u64);
|
||||||
|
x /= radix as u64;
|
||||||
|
|
||||||
|
let mut c = char::from_digit(m as u32, radix).unwrap();
|
||||||
|
if upper { c.make_ascii_uppercase(); }
|
||||||
|
result.push(c as u8);
|
||||||
|
if x == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result[begin..].reverse();
|
||||||
|
LString::from(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,9 @@ struct SymbolTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
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_NIL: Symbol = symbol!(nil);
|
||||||
pub static ref SYM_BOOL: Symbol = symbol!(bool);
|
pub static ref SYM_BOOL: Symbol = symbol!(bool);
|
||||||
pub static ref SYM_SYMBOL: Symbol = symbol!(symbol);
|
pub static ref SYM_SYMBOL: Symbol = symbol!(symbol);
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use crate::{chunk::Chunk, Vm, exception::Result};
|
use crate::{chunk::Chunk, Vm, exception::Result, symbol::Symbol};
|
||||||
|
|
||||||
use super::{Value, CellValue};
|
use super::{Value, CellValue};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct FuncAttrs {
|
pub struct FuncAttrs {
|
||||||
pub arity: usize,
|
pub arity: usize,
|
||||||
|
pub name: Option<Symbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -36,8 +37,12 @@ pub struct NativeFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NativeFunc {
|
impl NativeFunc {
|
||||||
pub fn new(func: FnNative, arity: usize) -> Self {
|
pub fn new(func: FnNative, arity: usize, name: Symbol) -> Self {
|
||||||
Self { func, attrs: FuncAttrs { arity } }
|
Self { func, attrs: FuncAttrs { arity, name: Some(name) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_anon(func: FnNative, arity: usize) -> Self {
|
||||||
|
Self { func, attrs: FuncAttrs { arity, name: None } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{symbol::{SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR}, value::function::NativeFunc, vmcalliter, Vm, exception::{Result, throw}};
|
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::{Value, range::RangeType};
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ impl Value {
|
||||||
None => Ok(Value::from(*SYM_END_ITERATION)),
|
None => Ok(Value::from(*SYM_END_ITERATION)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(func), 0).into())
|
Ok(NativeFunc::new(Box::new(func), 0, symbol!("index...")).into())
|
||||||
} else {
|
} else {
|
||||||
throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
|
throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,14 +166,23 @@ impl Value {
|
||||||
w.write_all(b" }")
|
w.write_all(b" }")
|
||||||
},
|
},
|
||||||
Self::Function(g) => {
|
Self::Function(g) => {
|
||||||
if g.state.is_empty() {
|
if let Some(name) = g.attrs.name {
|
||||||
write!(w, "<function({}) {:?}>", g.attrs.arity, Rc::as_ptr(g))
|
write!(w, "<function {}({}) @{:0>12x}>",
|
||||||
|
name.name(), g.attrs.arity, Rc::as_ptr(g) as usize)
|
||||||
} else {
|
} else {
|
||||||
write!(w, "<closure({}) {:?}>", g.attrs.arity, Rc::as_ptr(g))
|
write!(w, "<anon function({}) @{:0>12x}>",
|
||||||
|
g.attrs.arity, Rc::as_ptr(g) as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::NativeFunc(g) => {
|
||||||
|
if let Some(name) = g.attrs.name {
|
||||||
|
write!(w, "<native function {}({}) @{:0>12x}>",
|
||||||
|
name.name(), g.attrs.arity, Rc::as_ptr(g) as usize)
|
||||||
|
} else {
|
||||||
|
write!(w, "<anon native function({}) @{:0>12x}>",
|
||||||
|
g.attrs.arity, Rc::as_ptr(g) as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::NativeFunc(g)
|
|
||||||
=> write!(w, "<native function({}) {:?}>", g.attrs.arity, Rc::as_ptr(g)),
|
|
||||||
Self::Native(n) => n.to_lstring(w, repr, recur),
|
Self::Native(n) => n.to_lstring(w, repr, recur),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use num_complex::{Complex64, ComplexFloat};
|
||||||
use num_rational::Rational64;
|
use num_rational::Rational64;
|
||||||
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
|
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero};
|
||||||
|
|
||||||
use crate::{exception::{throw, Result}, lstring::LString, symbol::{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};
|
||||||
|
|
||||||
|
@ -556,7 +556,7 @@ impl Value {
|
||||||
Ok(Value::iter_pack(range_iter.borrow_mut().next()
|
Ok(Value::iter_pack(range_iter.borrow_mut().next()
|
||||||
.map(Value::from)))
|
.map(Value::from)))
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[range]")).into())
|
||||||
},
|
},
|
||||||
Self::String(s) => {
|
Self::String(s) => {
|
||||||
let byte_pos = RefCell::new(0);
|
let byte_pos = RefCell::new(0);
|
||||||
|
@ -569,7 +569,7 @@ impl Value {
|
||||||
Ok(Value::from(*SYM_END_ITERATION))
|
Ok(Value::from(*SYM_END_ITERATION))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[string]")).into())
|
||||||
},
|
},
|
||||||
Self::List(list) => {
|
Self::List(list) => {
|
||||||
let idx = RefCell::new(0);
|
let idx = RefCell::new(0);
|
||||||
|
@ -582,7 +582,7 @@ impl Value {
|
||||||
Ok(Value::from(*SYM_END_ITERATION))
|
Ok(Value::from(*SYM_END_ITERATION))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[list]")).into())
|
||||||
},
|
},
|
||||||
Self::Table(table) => {
|
Self::Table(table) => {
|
||||||
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
|
let keys: Vec<HashValue> = table.borrow().keys().cloned().collect();
|
||||||
|
@ -591,7 +591,7 @@ impl Value {
|
||||||
Ok(Value::iter_pack(keys.borrow_mut().next()
|
Ok(Value::iter_pack(keys.borrow_mut().next()
|
||||||
.map(HashValue::into_inner)))
|
.map(HashValue::into_inner)))
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[table]")).into())
|
||||||
},
|
},
|
||||||
_ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"),
|
_ => throw!(*SYM_TYPE_ERROR, "cannot iterate {self:#}"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ fn get_call_outcome(args: Vec<Value>) -> Result<CallOutcome> {
|
||||||
vm.call_value(f.clone(), args)
|
vm.call_value(f.clone(), args)
|
||||||
};
|
};
|
||||||
let nf = NativeFunc {
|
let nf = NativeFunc {
|
||||||
attrs: FuncAttrs { arity: remaining },
|
attrs: FuncAttrs { arity: remaining, name: None },
|
||||||
func: Box::new(nf),
|
func: Box::new(nf),
|
||||||
};
|
};
|
||||||
Ok(CallOutcome::Partial(nf.into()))
|
Ok(CallOutcome::Partial(nf.into()))
|
||||||
|
|
|
@ -1,20 +1,27 @@
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use syn::{parse::Parse, parse_macro_input, ItemFn, LitInt};
|
use syn::{parse::Parse, parse_macro_input, Token};
|
||||||
use quote::quote;
|
use quote::{quote, ToTokens};
|
||||||
|
|
||||||
struct NativeFuncArgs {
|
struct NativeFuncArgs {
|
||||||
arity: LitInt,
|
arity: syn::LitInt,
|
||||||
|
name: Option<syn::LitStr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for NativeFuncArgs {
|
impl Parse for NativeFuncArgs {
|
||||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
let arity = input.parse()?;
|
let arity = input.parse()?;
|
||||||
Ok(Self { arity })
|
let t: Option<Token![,]> = input.parse()?;
|
||||||
|
if t.is_some() {
|
||||||
|
let name = input.parse()?;
|
||||||
|
Ok(Self { arity, name: Some(name) })
|
||||||
|
} else {
|
||||||
|
Ok(Self { arity, name: None })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream {
|
pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream {
|
||||||
let Ok(itemfn) = syn::parse::<ItemFn>(annotated_item.clone()) else {
|
let Ok(itemfn) = syn::parse::<syn::ItemFn>(annotated_item.clone()) else {
|
||||||
return annotated_item
|
return annotated_item
|
||||||
};
|
};
|
||||||
let args: NativeFuncArgs = parse_macro_input!(input as NativeFuncArgs);
|
let args: NativeFuncArgs = parse_macro_input!(input as NativeFuncArgs);
|
||||||
|
@ -26,6 +33,11 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
|
||||||
let output = itemfn.sig.output;
|
let output = itemfn.sig.output;
|
||||||
let arity = args.arity;
|
let arity = args.arity;
|
||||||
|
|
||||||
|
let talc_name = match args.name {
|
||||||
|
Some(n) => n.to_token_stream(),
|
||||||
|
None => name.to_token_stream(),
|
||||||
|
};
|
||||||
|
|
||||||
assert!(itemfn.sig.constness.is_none(), "item must not be const");
|
assert!(itemfn.sig.constness.is_none(), "item must not be const");
|
||||||
assert!(itemfn.sig.asyncness.is_none(), "item must not be async");
|
assert!(itemfn.sig.asyncness.is_none(), "item must not be async");
|
||||||
assert!(itemfn.sig.unsafety.is_none(), "item must not be unsafe");
|
assert!(itemfn.sig.unsafety.is_none(), "item must not be unsafe");
|
||||||
|
@ -37,6 +49,7 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre
|
||||||
::talc_lang::value::function::NativeFunc {
|
::talc_lang::value::function::NativeFunc {
|
||||||
attrs: ::talc_lang::value::function::FuncAttrs{
|
attrs: ::talc_lang::value::function::FuncAttrs{
|
||||||
arity: #arity,
|
arity: #arity,
|
||||||
|
name: Some(::talc_lang::symbol::symbol!(#talc_name))
|
||||||
},
|
},
|
||||||
func: Box::new(|#inputs| #output #block)
|
func: Box::new(|#inputs| #output #block)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use talc_lang::{exception::{exception, Result}, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{function::NativeFunc, Value}, vmcall, Vm};
|
use talc_lang::{exception::{exception, Result}, symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{function::NativeFunc, Value}, vmcall, Vm};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
@ -205,7 +205,7 @@ pub fn sort_key(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
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).into();
|
let nf = NativeFunc::new(Box::new(f), 2, symbol!("inner[sort_key]")).into();
|
||||||
sort_inner(&mut list.borrow_mut(), Some(&nf), vm)?;
|
sort_inner(&mut list.borrow_mut(), Some(&nf), vm)?;
|
||||||
Ok(list.into())
|
Ok(list.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
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, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{function::NativeFunc, HashValue, NativeValue, Value}, Vm};
|
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 talc_macros::native_func;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
@ -454,7 +454,7 @@ fn tcp_listen_inner(listener: TcpListener) -> Value {
|
||||||
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
Err(e) => throw!(*SYM_IO_ERROR, "{e}"),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
NativeFunc::new(Box::new(func), 0).into()
|
NativeFunc::new(Box::new(func), 0, symbol!("inner[tcp_listen]")).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
|
419
talc-std/src/format.rs
Normal file
419
talc-std/src/format.rs
Normal file
|
@ -0,0 +1,419 @@
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use talc_lang::{exception::{exception, Result}, lstring::{LStr, LString}, parser::{parse_int, to_lstring_radix}, symbol::SYM_TYPE_ERROR, throw, value::{Complex64, Rational64, Value}, Vm};
|
||||||
|
use talc_macros::native_func;
|
||||||
|
|
||||||
|
use crate::{unpack_args, SYM_FORMAT_ERROR};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum FmtIndex {
|
||||||
|
Imm(usize),
|
||||||
|
Arg(usize),
|
||||||
|
NextArg,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum Align {
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
enum FmtType {
|
||||||
|
#[default]
|
||||||
|
Str,
|
||||||
|
Repr,
|
||||||
|
Hex(bool),
|
||||||
|
Oct,
|
||||||
|
Sex,
|
||||||
|
Bin,
|
||||||
|
Exp(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
|
enum FmtSign {
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
#[default]
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// [idx][:[align][sign][width][.prec][ty]]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
struct FmtCode {
|
||||||
|
arg_idx: Option<usize>,
|
||||||
|
align: Option<(Align, char)>,
|
||||||
|
sign: FmtSign,
|
||||||
|
width: Option<FmtIndex>,
|
||||||
|
prec: Option<FmtIndex>,
|
||||||
|
ty: FmtType,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FmtParser<'p> {
|
||||||
|
code: &'p LStr,
|
||||||
|
pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'p> FmtParser<'p> {
|
||||||
|
fn new(code: &'p LStr) -> Self {
|
||||||
|
Self {
|
||||||
|
code,
|
||||||
|
pos: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<char> {
|
||||||
|
let c = &self.code[self.pos..].chars().next()?;
|
||||||
|
self.pos += c.len_utf8();
|
||||||
|
Some(*c)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek(&mut self) -> Option<char> {
|
||||||
|
self.code[self.pos..].chars().next()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_next(&mut self, chs: &[char]) -> Option<char> {
|
||||||
|
if self.peek().is_some_and(|c| chs.contains(&c)) {
|
||||||
|
self.next()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_number_str(&mut self) -> &LStr {
|
||||||
|
let start = self.pos;
|
||||||
|
while matches!(self.peek(), Some('0'..='9' | '_')) {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
&self.code[start..self.pos]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_usize(&mut self) -> Result<Option<usize>> {
|
||||||
|
let s = self.next_number_str();
|
||||||
|
if s.is_empty() { return Ok(None) }
|
||||||
|
let Ok(i) = parse_int(s, 10) else {
|
||||||
|
throw!(*SYM_FORMAT_ERROR,
|
||||||
|
"code {{{}}}: invalid integer", self.code)
|
||||||
|
};
|
||||||
|
if i < 0 {
|
||||||
|
throw!(*SYM_FORMAT_ERROR,
|
||||||
|
"code {{{}}}: integer may not be negative", self.code)
|
||||||
|
}
|
||||||
|
Ok(Some(i as usize))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_fmt_index(&mut self) -> Result<Option<FmtIndex>> {
|
||||||
|
let dollar = self.try_next(&['$']).is_some();
|
||||||
|
let num = self.next_usize()?;
|
||||||
|
match (dollar, num) {
|
||||||
|
(true, None) => Ok(Some(FmtIndex::NextArg)),
|
||||||
|
(true, Some(n)) => Ok(Some(FmtIndex::Arg(n))),
|
||||||
|
(false, None) => Ok(None),
|
||||||
|
(false, Some(n)) => Ok(Some(FmtIndex::Imm(n))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_align(&mut self) -> Result<Option<(Align, char)>> {
|
||||||
|
let align = match self.peek() {
|
||||||
|
Some('<') => Align::Left,
|
||||||
|
Some('^') => Align::Center,
|
||||||
|
Some('>') => Align::Right,
|
||||||
|
_ => return Ok(None),
|
||||||
|
};
|
||||||
|
self.next();
|
||||||
|
let Some(c) = self.next() else {
|
||||||
|
throw!(*SYM_FORMAT_ERROR,
|
||||||
|
"code {{{}}}: alignment without fill character", self.code)
|
||||||
|
};
|
||||||
|
Ok(Some((align, c)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_sign(&mut self) -> FmtSign {
|
||||||
|
match self.try_next(&['+', '-']) {
|
||||||
|
Some('+') => FmtSign::Plus,
|
||||||
|
Some('-') => FmtSign::Minus,
|
||||||
|
_ => FmtSign::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_type(&mut self) -> Result<FmtType> {
|
||||||
|
let ty = match self.peek() {
|
||||||
|
None => return Ok(FmtType::Str),
|
||||||
|
Some('?') => FmtType::Repr,
|
||||||
|
Some('x') => FmtType::Hex(false),
|
||||||
|
Some('X') => FmtType::Hex(true),
|
||||||
|
Some('o') => FmtType::Oct,
|
||||||
|
Some('s') => FmtType::Sex,
|
||||||
|
Some('b') => FmtType::Bin,
|
||||||
|
Some('e') => FmtType::Exp(false),
|
||||||
|
Some('E') => FmtType::Exp(true),
|
||||||
|
_ => throw!(*SYM_FORMAT_ERROR,
|
||||||
|
"code {{{}}}: invalid format type", self.code),
|
||||||
|
};
|
||||||
|
self.next();
|
||||||
|
Ok(ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_code_end(&mut self) -> Result<()> {
|
||||||
|
match self.peek() {
|
||||||
|
Some(c) => throw!(*SYM_FORMAT_ERROR,
|
||||||
|
"code {{{}}}: expected end of code, found character '{}'", self.code, c),
|
||||||
|
None => Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_format(&mut self) -> Result<FmtCode> {
|
||||||
|
let mut code = FmtCode {
|
||||||
|
arg_idx: self.next_usize()?,
|
||||||
|
..FmtCode::default()
|
||||||
|
};
|
||||||
|
if self.try_next(&[':']).is_none() {
|
||||||
|
self.next_code_end()?;
|
||||||
|
return Ok(code)
|
||||||
|
}
|
||||||
|
code.align = self.next_align()?;
|
||||||
|
code.sign = self.next_sign();
|
||||||
|
code.width = self.next_fmt_index()?;
|
||||||
|
if self.try_next(&['.']).is_some() {
|
||||||
|
code.prec = self.next_fmt_index()?;
|
||||||
|
}
|
||||||
|
code.ty = self.next_type()?;
|
||||||
|
self.next_code_end()?;
|
||||||
|
Ok(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_arg<'a>(args: &'a [Value], n: Option<usize>, faidx: &mut usize)
|
||||||
|
-> Result<&'a Value> {
|
||||||
|
let i = match n {
|
||||||
|
Some(n) => n,
|
||||||
|
None => {
|
||||||
|
let i = *faidx;
|
||||||
|
*faidx += 1;
|
||||||
|
i
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if i >= args.len() {
|
||||||
|
throw!(*SYM_FORMAT_ERROR, "missing arguments: expected at least {}", i+1)
|
||||||
|
}
|
||||||
|
Ok(&args[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_val(args: &[Value], i: FmtIndex, faidx: &mut usize) -> Result<usize> {
|
||||||
|
let v = match i {
|
||||||
|
FmtIndex::Imm(n) => return Ok(n),
|
||||||
|
FmtIndex::Arg(n) => get_arg(args, Some(n), faidx)?,
|
||||||
|
FmtIndex::NextArg => get_arg(args, None, faidx)?,
|
||||||
|
};
|
||||||
|
let Value::Int(v) = v else {
|
||||||
|
throw!(*SYM_FORMAT_ERROR, "expected positive integer argument, found {v:#}")
|
||||||
|
};
|
||||||
|
if *v < 0 {
|
||||||
|
throw!(*SYM_FORMAT_ERROR, "expected positive integer argument, found {v}")
|
||||||
|
}
|
||||||
|
Ok(*v as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_float(f: f64, prec: Option<usize>, ty: FmtType, buf: &mut LString, ty_name: &str) -> Result<()> {
|
||||||
|
let res = match (prec, ty) {
|
||||||
|
(None, FmtType::Str) => write!(buf, "{}", Value::Float(f)),
|
||||||
|
(None, FmtType::Repr) => write!(buf, "{:#}", Value::Float(f)),
|
||||||
|
(None, FmtType::Exp(true)) => write!(buf, "{:E}", f),
|
||||||
|
(None, FmtType::Exp(false)) => write!(buf, "{:e}", f),
|
||||||
|
(Some(p), FmtType::Str)
|
||||||
|
| (Some(p), FmtType::Repr) => write!(buf, "{0:.1$}", f, p),
|
||||||
|
(Some(p), FmtType::Exp(true)) => write!(buf, "{0:.1$E}", f, p),
|
||||||
|
(Some(p), FmtType::Exp(false)) => write!(buf, "{0:.1$e}", f, p),
|
||||||
|
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
||||||
|
};
|
||||||
|
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_complex(cx: Complex64, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
||||||
|
format_float(cx.re, prec, ty, buf, "complex")?;
|
||||||
|
if cx.im < 0.0 {
|
||||||
|
buf.push_char('-')
|
||||||
|
} else {
|
||||||
|
buf.push_char('+')
|
||||||
|
}
|
||||||
|
format_float(cx.im.abs(), prec, ty, buf, "complex")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_int(n: i64, prec: Option<usize>, ty: FmtType, buf: &mut LString, ty_name: &str) -> Result<()> {
|
||||||
|
if prec.is_some() {
|
||||||
|
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
||||||
|
}
|
||||||
|
let res = match ty {
|
||||||
|
FmtType::Str => write!(buf, "{}", Value::Int(n)),
|
||||||
|
FmtType::Repr => write!(buf, "{:#}", Value::Int(n)),
|
||||||
|
FmtType::Hex(caps) => write!(buf, "{}", to_lstring_radix(n, 16, caps)),
|
||||||
|
FmtType::Oct => write!(buf, "{}", to_lstring_radix(n, 8, false)),
|
||||||
|
FmtType::Sex => write!(buf, "{}", to_lstring_radix(n, 6, false)),
|
||||||
|
FmtType::Bin => write!(buf, "{}", to_lstring_radix(n, 2, false)),
|
||||||
|
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
||||||
|
};
|
||||||
|
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_ratio(n: Rational64, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
||||||
|
format_int(*n.numer(), prec, ty, buf, "ratio")?;
|
||||||
|
buf.push_char('/');
|
||||||
|
format_int(*n.denom(), prec, ty, buf, "ratio")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_value(v: &Value, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
||||||
|
if prec.is_some() {
|
||||||
|
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value")
|
||||||
|
}
|
||||||
|
let res = match ty {
|
||||||
|
FmtType::Str => write!(buf, "{}", v),
|
||||||
|
FmtType::Repr => write!(buf, "{:#}", v),
|
||||||
|
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value")
|
||||||
|
};
|
||||||
|
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_string(mut s: &LStr, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
||||||
|
if let Some(prec) = prec {
|
||||||
|
s = &s[..prec]
|
||||||
|
}
|
||||||
|
let res = match ty {
|
||||||
|
FmtType::Str => { buf.push_lstr(s); Ok(()) },
|
||||||
|
FmtType::Repr => write!(buf, "{:?}", s),
|
||||||
|
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for string")
|
||||||
|
};
|
||||||
|
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_str(n: usize, c: char, buf: &mut LString) {
|
||||||
|
for _ in 0..n {
|
||||||
|
buf.push_char(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_arg(args: &[Value], faidx: &mut usize, code: &LStr, res: &mut LString)
|
||||||
|
-> Result<()> {
|
||||||
|
if !code.is_utf8() {
|
||||||
|
throw!(*SYM_FORMAT_ERROR, "code {{{}}}: code contains invalid UTF-8", code)
|
||||||
|
}
|
||||||
|
let fmtcode = FmtParser::new(code).read_format()?;
|
||||||
|
|
||||||
|
let mut buf = LString::new();
|
||||||
|
let fmt_arg = get_arg(args, fmtcode.arg_idx, faidx)?;
|
||||||
|
let prec_val = fmtcode.prec.map(|i| get_val(args, i, faidx)).transpose()?;
|
||||||
|
let width_val = fmtcode.width.map(|i| get_val(args, i, faidx)).transpose()?;
|
||||||
|
|
||||||
|
match fmt_arg {
|
||||||
|
Value::Int(n) => format_int(*n, prec_val, fmtcode.ty, &mut buf, "integer")?,
|
||||||
|
Value::Ratio(n) => format_ratio(*n, prec_val, fmtcode.ty, &mut buf)?,
|
||||||
|
Value::Float(f) => format_float(*f, prec_val, fmtcode.ty, &mut buf, "float")?,
|
||||||
|
Value::Complex(n) => format_complex(*n, prec_val, fmtcode.ty, &mut buf)?,
|
||||||
|
Value::String(s) => format_string(s, prec_val, fmtcode.ty, &mut buf)?,
|
||||||
|
v => format_value(v, prec_val, fmtcode.ty, &mut buf)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
let (sign, final_buf) = match fmtcode.sign {
|
||||||
|
FmtSign::Plus => match buf.byte_get(0) {
|
||||||
|
Some(b'+') => ("+", &buf[1..]),
|
||||||
|
Some(b'-') => ("-", &buf[1..]),
|
||||||
|
_ => ("+", &buf[..]),
|
||||||
|
}
|
||||||
|
FmtSign::Minus => match buf.byte_get(0) {
|
||||||
|
Some(b'-') => ("-", &buf[1..]),
|
||||||
|
_ => ("", &buf[..]),
|
||||||
|
}
|
||||||
|
FmtSign::None => ("", &buf[..])
|
||||||
|
};
|
||||||
|
res.push_str(sign);
|
||||||
|
|
||||||
|
|
||||||
|
if let Some(w) = width_val {
|
||||||
|
let w = w.saturating_sub(final_buf.len() + sign.len());
|
||||||
|
match fmtcode.align {
|
||||||
|
Some((Align::Left, c)) => {
|
||||||
|
res.push_lstr(final_buf);
|
||||||
|
pad_str(w, c, res);
|
||||||
|
}
|
||||||
|
Some((Align::Center, c)) => {
|
||||||
|
pad_str((w+1)/2, c, res);
|
||||||
|
res.push_lstr(final_buf);
|
||||||
|
pad_str(w/2, c, res);
|
||||||
|
}
|
||||||
|
Some((Align::Right, c)) => {
|
||||||
|
pad_str(w, c, res);
|
||||||
|
res.push_lstr(final_buf);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let c = if matches!(fmt_arg,
|
||||||
|
Value::Int(_)
|
||||||
|
| Value::Float(_)
|
||||||
|
| Value::Ratio(_)
|
||||||
|
| Value::Complex(_)
|
||||||
|
) && fmtcode.sign != FmtSign::None {
|
||||||
|
'0'
|
||||||
|
} else {
|
||||||
|
' '
|
||||||
|
};
|
||||||
|
pad_str(w, c, res);
|
||||||
|
res.push_lstr(final_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.push_lstr(final_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(2, "fmt")]
|
||||||
|
pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, fstr, fargs] = unpack_args!(args);
|
||||||
|
let (Value::String(fstr), Value::List(fargs)) = (&fstr, &fargs) else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "format expected string and list, got {fstr:#} and {fargs:#}")
|
||||||
|
};
|
||||||
|
let mut res = LString::new();
|
||||||
|
let mut faidx = 0;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < fstr.len() {
|
||||||
|
let b = fstr.byte_at(i);
|
||||||
|
i += 1;
|
||||||
|
if b == b'}' {
|
||||||
|
let Some(b'}') = fstr.byte_get(i) else {
|
||||||
|
throw!(*SYM_FORMAT_ERROR, "unmatched closing brace at byte {}", i-1)
|
||||||
|
};
|
||||||
|
i += 1;
|
||||||
|
res.push_byte(b);
|
||||||
|
}
|
||||||
|
if b != b'{' {
|
||||||
|
res.push_byte(b);
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i >= fstr.len() {
|
||||||
|
throw!(*SYM_FORMAT_ERROR, "unclosed format specifier at byte {}", i-1)
|
||||||
|
}
|
||||||
|
if let Some(b'{') = fstr.byte_get(i) {
|
||||||
|
i += 1;
|
||||||
|
res.push_byte(b);
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let start = i;
|
||||||
|
while i < fstr.len() && fstr.byte_at(i) != b'}' {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
if i == fstr.len() {
|
||||||
|
throw!(*SYM_FORMAT_ERROR, "unclosed format specifier at byte {}", start-1)
|
||||||
|
}
|
||||||
|
let code = &fstr[start..i];
|
||||||
|
i += 1;
|
||||||
|
format_arg(&fargs.borrow(), &mut faidx, code, &mut res)?;
|
||||||
|
}
|
||||||
|
Ok(res.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(vm: &mut Vm) {
|
||||||
|
vm.set_global_name("fmt", fmt_().into());
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use std::{io::{BufRead, Write}, os::unix::ffi::OsStrExt, time::{SystemTime, UNIX_EPOCH}};
|
use std::{io::{BufRead, Write}, os::unix::ffi::OsStrExt, sync::Mutex, time::{SystemTime, UNIX_EPOCH}};
|
||||||
|
|
||||||
use talc_lang::{exception::{throw, Result}, lstring::LString, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::Value, vmcall, Vm};
|
use talc_lang::{exception::{throw, Result}, lstring::LString, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::Value, vmcall, Vm};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::{unpack_args, SYM_IO_ERROR};
|
use crate::{unpack_args, SYM_IO_ERROR};
|
||||||
|
|
||||||
|
static ENV_LOCK: Mutex<()> = Mutex::new(());
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn print(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn print(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let res = match &args[1] {
|
let res = match &args[1] {
|
||||||
|
@ -92,7 +94,13 @@ pub fn setenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
if val.as_bytes().contains(&0) {
|
if val.as_bytes().contains(&0) {
|
||||||
throw!(*SYM_VALUE_ERROR, "environment variable value must not contain a null byte")
|
throw!(*SYM_VALUE_ERROR, "environment variable value must not contain a null byte")
|
||||||
}
|
}
|
||||||
std::env::set_var(key.to_os_str(), val.to_os_str());
|
{
|
||||||
|
let Ok(guard) = ENV_LOCK.lock() else {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "failed to lock environemnt variables")
|
||||||
|
};
|
||||||
|
unsafe { std::env::set_var(key.to_os_str(), val.to_os_str()) };
|
||||||
|
drop(guard);
|
||||||
|
}
|
||||||
Ok(Value::Nil)
|
Ok(Value::Nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +113,13 @@ pub fn delenv(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
|
if key.is_empty() || key.as_bytes().contains(&b'=') || key.as_bytes().contains(&0) {
|
||||||
throw!(*SYM_VALUE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
|
throw!(*SYM_VALUE_ERROR, "environment variable must not be empty or contain an equals sign or null byte")
|
||||||
}
|
}
|
||||||
std::env::remove_var(key.to_os_str());
|
{
|
||||||
|
let Ok(guard) = ENV_LOCK.lock() else {
|
||||||
|
throw!(*SYM_VALUE_ERROR, "failed to lock environemnt variables")
|
||||||
|
};
|
||||||
|
unsafe { std::env::remove_var(key.to_os_str()) };
|
||||||
|
drop(guard);
|
||||||
|
}
|
||||||
Ok(Value::Nil)
|
Ok(Value::Nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use talc_lang::{exception::Result, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{function::NativeFunc, ops::RatioExt, range::RangeType, HashValue, Value}, vmcall, vmcalliter, Vm};
|
use talc_lang::{exception::Result, symbol::{symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{function::NativeFunc, ops::RatioExt, range::RangeType, HashValue, Value}, vmcall, vmcalliter, Vm};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
@ -87,7 +87,7 @@ pub fn pairs(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(Value::iter_pack(None))
|
Ok(Value::iter_pack(None))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[pairs]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -98,7 +98,7 @@ pub fn once(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let f = move |_: &mut Vm, _| {
|
let f = move |_: &mut Vm, _| {
|
||||||
Ok(Value::iter_pack(v.borrow_mut().take()))
|
Ok(Value::iter_pack(v.borrow_mut().take()))
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[once]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -108,7 +108,7 @@ pub fn forever(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let f = move |_: &mut Vm, _| {
|
let f = move |_: &mut Vm, _| {
|
||||||
Ok(val.clone())
|
Ok(val.clone())
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[forever]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -128,7 +128,7 @@ pub fn map(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
None => Ok(Value::iter_pack(None)),
|
None => Ok(Value::iter_pack(None)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[map]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -145,7 +145,7 @@ pub fn tee(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
None => Ok(Value::iter_pack(None)),
|
None => Ok(Value::iter_pack(None)),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[tee]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(3)]
|
#[native_func(3)]
|
||||||
|
@ -163,7 +163,7 @@ pub fn scan(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
*result.borrow_mut() = r.clone();
|
*result.borrow_mut() = r.clone();
|
||||||
Ok(r)
|
Ok(r)
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[scan]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -182,7 +182,7 @@ pub fn filter(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[filter]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -208,7 +208,7 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
*taken.borrow_mut() += 1;
|
*taken.borrow_mut() += 1;
|
||||||
Ok(next)
|
Ok(next)
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[take]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -233,7 +233,7 @@ pub fn skip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
}
|
}
|
||||||
vmcall!(vm; iter.clone())
|
vmcall!(vm; iter.clone())
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[skip]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -252,7 +252,7 @@ pub fn enumerate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(Value::from(vec![n.into(), next]))
|
Ok(Value::from(vec![n.into(), next]))
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[enumerate]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -287,7 +287,7 @@ pub fn cycle(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cycle]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
|
@ -329,7 +329,7 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
},
|
},
|
||||||
Step::End => Ok(Value::Nil),
|
Step::End => Ok(Value::Nil),
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[step]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -349,7 +349,7 @@ pub fn rev(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(Value::iter_pack(lst.borrow_mut().as_mut().unwrap().pop()))
|
Ok(Value::iter_pack(lst.borrow_mut().as_mut().unwrap().pop()))
|
||||||
|
|
||||||
};
|
};
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[rev]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -377,7 +377,7 @@ pub fn zip(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(Value::from(res))
|
Ok(Value::from(res))
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[zip]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -401,7 +401,7 @@ pub fn zipn(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(Value::from(res))
|
Ok(Value::from(res))
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[zipn]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -428,7 +428,7 @@ pub fn alternate(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[alternate]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -458,7 +458,7 @@ pub fn alternaten(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[alternaten]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -500,7 +500,7 @@ pub fn intersperse(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[intersperse]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -523,7 +523,7 @@ pub fn chain(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[chain]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
@ -588,7 +588,7 @@ pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(Value::from(vec![a_res, b_res]))
|
Ok(Value::from(vec![a_res, b_res]))
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(NativeFunc::new(Box::new(f), 0).into())
|
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cart_prod]")).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ pub mod exception;
|
||||||
pub mod num;
|
pub mod num;
|
||||||
pub mod collection;
|
pub mod collection;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
|
pub mod format;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod regex;
|
pub mod regex;
|
||||||
#[cfg(feature="random")]
|
#[cfg(feature="random")]
|
||||||
|
@ -22,6 +23,7 @@ pub fn load_all(vm: &mut Vm) {
|
||||||
num::load(vm);
|
num::load(vm);
|
||||||
io::load(vm);
|
io::load(vm);
|
||||||
string::load(vm);
|
string::load(vm);
|
||||||
|
format::load(vm);
|
||||||
regex::load(vm);
|
regex::load(vm);
|
||||||
file::load(vm);
|
file::load(vm);
|
||||||
#[cfg(feature="random")]
|
#[cfg(feature="random")]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use talc_lang::{exception::Result, lstring::LString, parser::parse_int, symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{ops::RatioExt, Complex64, Value}, vmcalliter, Vm};
|
use talc_lang::{exception::Result, parser::{parse_int, to_lstring_radix}, symbol::{Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{ops::RatioExt, Complex64, Value}, vmcalliter, Vm};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
@ -123,42 +123,13 @@ pub fn load(vm: &mut Vm) {
|
||||||
// base conversions
|
// base conversions
|
||||||
//
|
//
|
||||||
|
|
||||||
fn to_radix_inner(n: i64, radix: u32, upper: bool) -> LString {
|
|
||||||
let mut result = vec![];
|
|
||||||
let mut begin = 0;
|
|
||||||
|
|
||||||
let mut x;
|
|
||||||
if n < 0 {
|
|
||||||
result.push('-' as u32 as u8);
|
|
||||||
begin = 1;
|
|
||||||
x = (-n) as u64;
|
|
||||||
} else {
|
|
||||||
x = n as u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let m = x % (radix as u64);
|
|
||||||
x /= radix as u64;
|
|
||||||
|
|
||||||
let mut c = char::from_digit(m as u32, radix).unwrap();
|
|
||||||
if upper { c.make_ascii_uppercase(); }
|
|
||||||
result.push(c as u8);
|
|
||||||
if x == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result[begin..].reverse();
|
|
||||||
LString::from(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn bin(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn bin(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, x] = unpack_args!(args);
|
let [_, x] = unpack_args!(args);
|
||||||
let Value::Int(x) = x else {
|
let Value::Int(x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}")
|
throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}")
|
||||||
};
|
};
|
||||||
Ok(to_radix_inner(x, 2, false).into())
|
Ok(to_lstring_radix(x, 2, false).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -167,7 +138,7 @@ pub fn sex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let Value::Int(x) = x else {
|
let Value::Int(x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "sex expected integer argument, got {x:#}")
|
throw!(*SYM_TYPE_ERROR, "sex expected integer argument, got {x:#}")
|
||||||
};
|
};
|
||||||
Ok(to_radix_inner(x, 6, false).into())
|
Ok(to_lstring_radix(x, 6, false).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -176,7 +147,7 @@ pub fn oct(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let Value::Int(x) = x else {
|
let Value::Int(x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "oct expected integer argument, got {x:#}")
|
throw!(*SYM_TYPE_ERROR, "oct expected integer argument, got {x:#}")
|
||||||
};
|
};
|
||||||
Ok(to_radix_inner(x, 8, false).into())
|
Ok(to_lstring_radix(x, 8, false).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -185,7 +156,7 @@ pub fn hex(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let Value::Int(x) = x else {
|
let Value::Int(x) = x else {
|
||||||
throw!(*SYM_TYPE_ERROR, "hex expected integer argument, got {x:#}")
|
throw!(*SYM_TYPE_ERROR, "hex expected integer argument, got {x:#}")
|
||||||
};
|
};
|
||||||
Ok(to_radix_inner(x, 16, false).into())
|
Ok(to_lstring_radix(x, 16, false).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -197,7 +168,7 @@ pub fn to_radix(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
if *radix < 2 || *radix > 36 {
|
if *radix < 2 || *radix > 36 {
|
||||||
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 0..=36")
|
throw!(*SYM_VALUE_ERROR, "to_radix expected radix in range 0..=36")
|
||||||
}
|
}
|
||||||
Ok(to_radix_inner(*x, *radix as u32, false).into())
|
Ok(to_lstring_radix(*x, *radix as u32, false).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
@ -209,7 +180,7 @@ pub fn to_radix_upper(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
if *radix < 2 || *radix > 36 {
|
if *radix < 2 || *radix > 36 {
|
||||||
throw!(*SYM_VALUE_ERROR, "to_radix_upper expected radix in range 0..=36")
|
throw!(*SYM_VALUE_ERROR, "to_radix_upper expected radix in range 0..=36")
|
||||||
}
|
}
|
||||||
Ok(to_radix_inner(*x, *radix as u32, true).into())
|
Ok(to_lstring_radix(*x, *radix as u32, true).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2)]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use talc_lang::{exception::Result, lformat, lstring::LString, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::Value, Vm};
|
use talc_lang::{exception::Result, lstring::LString, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::Value, Vm};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::{unpack_args, SYM_FORMAT_ERROR};
|
use crate::unpack_args;
|
||||||
|
|
||||||
pub fn load(vm: &mut Vm) {
|
pub fn load(vm: &mut Vm) {
|
||||||
vm.set_global_name("ord", ord().into());
|
vm.set_global_name("ord", ord().into());
|
||||||
|
@ -17,7 +17,6 @@ pub fn load(vm: &mut Vm) {
|
||||||
vm.set_global_name("to_utf8_lossy", to_utf8_lossy().into());
|
vm.set_global_name("to_utf8_lossy", to_utf8_lossy().into());
|
||||||
vm.set_global_name("str_to_bytes", str_to_bytes().into());
|
vm.set_global_name("str_to_bytes", str_to_bytes().into());
|
||||||
vm.set_global_name("str_of_bytes", str_of_bytes().into());
|
vm.set_global_name("str_of_bytes", str_of_bytes().into());
|
||||||
vm.set_global_name("format", _format().into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
|
@ -168,38 +167,3 @@ pub fn str_of_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
}).collect::<Result<Vec<u8>>>()?;
|
}).collect::<Result<Vec<u8>>>()?;
|
||||||
Ok(LString::from(bytes).into())
|
Ok(LString::from(bytes).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
|
||||||
pub fn _format(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
|
||||||
let [_, fstr, fargs] = unpack_args!(args);
|
|
||||||
let (Value::String(fstr), Value::List(fargs)) = (&fstr, &fargs) else {
|
|
||||||
throw!(*SYM_TYPE_ERROR, "format expected string and list, got {fstr:#} and {fargs:#}")
|
|
||||||
};
|
|
||||||
let mut res = LString::new();
|
|
||||||
let mut bytes = fstr.bytes();
|
|
||||||
let mut faidx = 0;
|
|
||||||
while let Some(b) = bytes.next() {
|
|
||||||
if b == b'%' {
|
|
||||||
match bytes.next() {
|
|
||||||
Some(b'%') => res.push_byte(b'%'),
|
|
||||||
Some(b @ (b'#' | b'?')) => {
|
|
||||||
let fargs = fargs.borrow();
|
|
||||||
let Some(a) = fargs.get(faidx) else {
|
|
||||||
throw!(*SYM_FORMAT_ERROR, "not enough args for format string")
|
|
||||||
};
|
|
||||||
faidx += 1;
|
|
||||||
if b == b'?' {
|
|
||||||
res += &lformat!("{a:#}");
|
|
||||||
} else {
|
|
||||||
res += &lformat!("{a}");
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format code")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.push_byte(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(res.into())
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use talc_lang::{exception::{exception, Result}, lformat, parser::{parse_float, parse_int}, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{ops::RatioExt, HashValue, Rational64, Value}, Vm};
|
use talc_lang::{exception::{exception, Result}, lformat, parser::{parse_float, parse_int}, symbol::{symbol, Symbol, SYM_TYPE_ERROR, SYM_VALUE_ERROR}, throw, value::{ops::RatioExt, HashValue, Rational64, Value}, Vm};
|
||||||
use talc_macros::native_func;
|
use talc_macros::native_func;
|
||||||
|
|
||||||
use crate::unpack_args;
|
use crate::unpack_args;
|
||||||
|
@ -14,12 +14,20 @@ pub fn load(vm: &mut Vm) {
|
||||||
vm.set_global_name("str", str_().into());
|
vm.set_global_name("str", str_().into());
|
||||||
vm.set_global_name("repr", repr().into());
|
vm.set_global_name("repr", repr().into());
|
||||||
|
|
||||||
|
vm.set_global_name("symbol_name", symbol_name().into());
|
||||||
|
vm.set_global_name("symbol_of", symbol_of().into());
|
||||||
|
vm.set_global_name("symbol_exists", symbol_exists().into());
|
||||||
|
|
||||||
vm.set_global_name("cell", cell().into());
|
vm.set_global_name("cell", cell().into());
|
||||||
vm.set_global_name("uncell", uncell().into());
|
vm.set_global_name("uncell", uncell().into());
|
||||||
vm.set_global_name("cell_replace", cell_replace().into());
|
vm.set_global_name("cell_replace", cell_replace().into());
|
||||||
vm.set_global_name("cell_take", cell_take().into());
|
vm.set_global_name("cell_take", cell_take().into());
|
||||||
|
|
||||||
vm.set_global_name("closure_state", closure_state().into());
|
vm.set_global_name("func_state", func_state().into());
|
||||||
|
vm.set_global_name("func_arity", func_arity().into());
|
||||||
|
vm.set_global_name("func_name", func_name().into());
|
||||||
|
vm.set_global_name("apply", apply().into());
|
||||||
|
vm.set_global_name("compile", compile().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -27,7 +35,7 @@ pub fn load(vm: &mut Vm) {
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1, "type")]
|
||||||
pub fn type_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn type_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, val] = unpack_args!(args);
|
let [_, val] = unpack_args!(args);
|
||||||
Ok(val.get_type().into())
|
Ok(val.get_type().into())
|
||||||
|
@ -42,7 +50,7 @@ pub fn is(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok((val.get_type() == ty).into())
|
Ok((val.get_type() == ty).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[native_func(2)]
|
#[native_func(2, "as")]
|
||||||
pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, val, ty] = unpack_args!(args);
|
let [_, val, ty] = unpack_args!(args);
|
||||||
let Value::Symbol(ty) = ty else {
|
let Value::Symbol(ty) = ty else {
|
||||||
|
@ -133,7 +141,7 @@ pub fn copy(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1, "str")]
|
||||||
pub fn str_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn str_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, val] = unpack_args!(args);
|
let [_, val] = unpack_args!(args);
|
||||||
Ok(lformat!("{val}").into())
|
Ok(lformat!("{val}").into())
|
||||||
|
@ -145,6 +153,40 @@ pub fn repr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(lformat!("{val:#}").into())
|
Ok(lformat!("{val:#}").into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// symbols
|
||||||
|
//
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn symbol_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, val] = unpack_args!(args);
|
||||||
|
let Value::Symbol(s) = val else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "symbol_name: expected symbol")
|
||||||
|
};
|
||||||
|
Ok(s.name().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn symbol_of(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, val] = unpack_args!(args);
|
||||||
|
let Value::String(s) = val else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
|
||||||
|
};
|
||||||
|
Ok(Symbol::get(s.as_ref()).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn symbol_exists(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, val] = unpack_args!(args);
|
||||||
|
let Value::String(s) = val else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
|
||||||
|
};
|
||||||
|
match Symbol::try_get(s.as_ref()) {
|
||||||
|
Some(s) => Ok(s.into()),
|
||||||
|
None => Ok(Value::Nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// cells
|
// cells
|
||||||
//
|
//
|
||||||
|
@ -183,13 +225,73 @@ pub fn cell_take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
Ok(cell.replace(Value::Nil))
|
Ok(cell.replace(Value::Nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// functions
|
||||||
|
//
|
||||||
|
|
||||||
#[native_func(1)]
|
#[native_func(1)]
|
||||||
pub fn closure_state(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
pub fn func_state(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
let [_, func] = unpack_args!(args);
|
let [_, func] = unpack_args!(args);
|
||||||
let Value::Function(func) = func else {
|
match func {
|
||||||
throw!(*SYM_TYPE_ERROR, "closure_state: value is not a function")
|
Value::NativeFunc(_) => Ok(Value::Nil),
|
||||||
};
|
Value::Function(f) => {
|
||||||
let l: Vec<Value> = func.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())
|
Ok(l.into())
|
||||||
|
}
|
||||||
|
_ => throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a talc function")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn func_arity(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, func] = unpack_args!(args);
|
||||||
|
let Some(attrs) = func.func_attrs() else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
|
||||||
|
};
|
||||||
|
Ok((attrs.arity as i64).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn func_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, func] = unpack_args!(args);
|
||||||
|
let Some(attrs) = func.func_attrs() else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
|
||||||
|
};
|
||||||
|
if let Some(name) = attrs.name {
|
||||||
|
Ok(name.into())
|
||||||
|
} else {
|
||||||
|
Ok(Value::Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(2)]
|
||||||
|
pub fn apply(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, func, lst] = unpack_args!(args);
|
||||||
|
if func.func_attrs().is_none() {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "apply: first argument must be a function, found {func:#}")
|
||||||
|
}
|
||||||
|
let Value::List(l) = lst else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "apply: second argument must be a list, found {lst:#}")
|
||||||
|
};
|
||||||
|
let mut args = l.borrow().clone();
|
||||||
|
args.insert(0, func.clone());
|
||||||
|
vm.call_value(func, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[native_func(1)]
|
||||||
|
pub fn compile(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||||
|
let [_, src] = unpack_args!(args);
|
||||||
|
let Value::String(src) = src else {
|
||||||
|
throw!(*SYM_TYPE_ERROR, "compile: argument must be a string, found {src:#}")
|
||||||
|
};
|
||||||
|
let src = src.to_str()
|
||||||
|
.map_err(|e| exception!(*SYM_VALUE_ERROR, "compile: argument must be valid unicode ({e})"))?;
|
||||||
|
let ast = talc_lang::parser::parse(src)
|
||||||
|
.map_err(|e| exception!(symbol!("parse_error"), "{e}"))?;
|
||||||
|
let func = talc_lang::compiler::compile(&ast, None);
|
||||||
|
Ok(func.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue