From 801aaf7b78b60c3b1f2931570eb00b23974f2720 Mon Sep 17 00:00:00 2001 From: trimill Date: Mon, 4 Nov 2024 12:59:05 -0500 Subject: [PATCH] switch to symbols, update formatting --- talc-bin/src/main.rs | 12 +- talc-lang/src/compiler.rs | 117 +++++---- talc-lang/src/lstring.rs | 126 +++++++++- talc-lang/src/parser/ast.rs | 116 +++++---- talc-lang/src/parser/lexer.rs | 3 + talc-lang/src/parser/parser.rs | 116 +++++---- talc-lang/src/parser/util.rs | 30 +++ talc-lang/src/symbol.rs | 3 + talc-lang/src/value/function.rs | 11 +- talc-lang/src/value/index.rs | 4 +- talc-lang/src/value/mod.rs | 19 +- talc-lang/src/value/ops.rs | 10 +- talc-lang/src/vm.rs | 2 +- talc-macros/src/native_func.rs | 23 +- talc-std/src/collection.rs | 4 +- talc-std/src/file.rs | 4 +- talc-std/src/format.rs | 419 ++++++++++++++++++++++++++++++++ talc-std/src/io.rs | 20 +- talc-std/src/iter.rs | 42 ++-- talc-std/src/lib.rs | 2 + talc-std/src/num.rs | 43 +--- talc-std/src/string.rs | 40 +-- talc-std/src/value.rs | 124 +++++++++- 23 files changed, 998 insertions(+), 292 deletions(-) create mode 100644 talc-std/src/format.rs diff --git a/talc-bin/src/main.rs b/talc-bin/src/main.rs index 2333265..50cc371 100644 --- a/talc-bin/src/main.rs +++ b/talc-bin/src/main.rs @@ -1,5 +1,5 @@ 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}; mod repl; @@ -28,7 +28,7 @@ struct Args { color: ColorChoice, } -fn exec(src: &str, args: &Args) -> ExitCode { +fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode { let mut vm = Vm::new(256); 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 let Err(e) = disasm_recursive(&func, &mut std::io::stderr()) { @@ -64,8 +64,10 @@ fn main() -> ExitCode { return repl::repl(&args) } - match std::fs::read_to_string(args.file.as_ref().unwrap()) { - Ok(s) => exec(&s, &args), + let file = args.file.as_ref().unwrap(); + + match std::fs::read_to_string(file) { + Ok(s) => exec(Symbol::get(file.as_os_str()), &s, &args), Err(e) => { eprintln!("Error: {e}"); ExitCode::FAILURE diff --git a/talc-lang/src/compiler.rs b/talc-lang/src/compiler.rs index 5166673..c6aaaee 100644 --- a/talc-lang/src/compiler.rs +++ b/talc-lang/src/compiler.rs @@ -3,9 +3,7 @@ use std::rc::Rc; use crate::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind}; use crate::chunk::{Instruction as I, Chunk, Arg24, Catch}; -use crate::lstr; -use crate::lstring::LStr; -use crate::symbol::Symbol; +use crate::symbol::{Symbol, SYM_SELF}; use crate::value::function::{FuncAttrs, Function}; use crate::value::Value; @@ -22,7 +20,7 @@ pub enum VarKind { #[derive(Debug, Clone)] pub struct Var { - name: Rc, + name: Symbol, kind: VarKind, } @@ -36,19 +34,19 @@ struct Compiler<'a> { parent: Option<&'a Compiler<'a>>, chunk: Chunk, attrs: FuncAttrs, - scope: HashMap, Var>, - shadowed: Vec<(Rc, Option)>, - closes: BTreeMap, usize>, + scope: HashMap, + shadowed: Vec<(Symbol, Option)>, + closes: BTreeMap, local_count: usize, } -pub fn compile(expr: &Expr) -> Function { - let mut comp = Compiler::new_module(None); +pub fn compile(expr: &Expr, name: Option) -> Function { + let mut comp = Compiler::new_module(name, None); comp.expr(expr); comp.finish() } -pub fn compile_repl(expr: &Expr, globals: &[Rc]) -> (Function, Vec>) { +pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec) { let mut comp = Compiler::new_repl(globals); comp.expr(expr); comp.finish_repl() @@ -57,9 +55,8 @@ pub fn compile_repl(expr: &Expr, globals: &[Rc]) -> (Function, Vec Default for Compiler<'a> { fn default() -> Self { let mut scope = HashMap::new(); - let self_name: Rc = lstr!("self").into(); - scope.insert(self_name.clone(), Var { - name: self_name, + scope.insert(*SYM_SELF, Var { + name: *SYM_SELF, kind: VarKind::Local(0), }); Self { @@ -76,36 +73,38 @@ impl<'a> Default for Compiler<'a> { } impl<'a> Compiler<'a> { - fn new_repl(globals: &'a [Rc]) -> Self { + fn new_repl(globals: &[Symbol]) -> Self { let mut new = Self { mode: CompilerMode::Repl, + attrs: FuncAttrs { arity: 0, name: Some(Symbol::get("")) }, ..Self::default() }; + for g in globals { - new.declare_global(g); + new.declare_global(*g); } new } - fn new_module(parent: Option<&'a Self>) -> Self { + fn new_module(name: Option, parent: Option<&'a Self>) -> Self { Self { mode: CompilerMode::Module, + attrs: FuncAttrs { arity: 0, name }, parent, ..Self::default() } } - fn new_function(&'a self, args: &[&LStr]) -> Self { + fn new_function(&'a self, name: Option, args: &[Symbol]) -> Self { let mut new = Self { mode: CompilerMode::Function, + attrs: FuncAttrs { arity: args.len(), name }, parent: Some(self), ..Self::default() }; - new.attrs.arity = args.len(); - for arg in args { - new.declare_local(arg); + new.declare_local(*arg); } new @@ -116,7 +115,7 @@ impl<'a> Compiler<'a> { Function::new(Rc::new(self.chunk), self.attrs, self.closes.len()) } - pub fn finish_inner(mut self) -> (Function, BTreeMap, usize>) { + pub fn finish_inner(mut self) -> (Function, BTreeMap) { self.emit(I::Return); // TODO closure ( @@ -125,7 +124,7 @@ impl<'a> Compiler<'a> { ) } - pub fn finish_repl(mut self) -> (Function, Vec>) { + pub fn finish_repl(mut self) -> (Function, Vec) { self.emit(I::Return); ( // TODO closure @@ -232,8 +231,8 @@ impl<'a> Compiler<'a> { // variables // - fn resolve_name(&self, name: &LStr) -> ResolveOutcome { - if let Some(v) = self.scope.get(name) { + fn resolve_name(&self, name: Symbol) -> ResolveOutcome { + if let Some(v) = self.scope.get(&name) { return ResolveOutcome::Var(v.kind) } let Some(parent) = self.parent else { @@ -245,7 +244,7 @@ impl<'a> Compiler<'a> { ResolveOutcome::InParent } - fn load_var(&mut self, name: &LStr) { + fn load_var(&mut self, name: Symbol) { match self.resolve_name(name) { ResolveOutcome::Var(VarKind::Local(n)) => { self.emit(I::LoadLocal(Arg24::from_usize(n))); @@ -254,57 +253,54 @@ impl<'a> Compiler<'a> { self.emit(I::LoadClosedLocal(Arg24::from_usize(n))); } ResolveOutcome::InParent => { - let n = match self.closes.get(name) { + let n = match self.closes.get(&name) { Some(n) => *n, None => { let n = self.closes.len(); - self.closes.insert(name.into(), n); + self.closes.insert(name, n); n } }; self.emit(I::LoadUpvalue(Arg24::from_usize(n))); } ResolveOutcome::None | ResolveOutcome::Var(VarKind::Global) => { - let s = Symbol::get(name); - self.emit(I::LoadGlobal(Arg24::from_symbol(s))); + self.emit(I::LoadGlobal(Arg24::from_symbol(name))); } } } - fn declare_local(&mut self, name: &LStr) -> usize { - let name: Rc = name.into(); + fn declare_local(&mut self, name: Symbol) -> usize { let local = Var { - name: name.clone(), + name, kind: VarKind::Local(self.local_count) }; 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.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); self.emit(I::NewLocal); n } - fn assign_global(&mut self, name: &LStr) { + fn assign_global(&mut self, name: Symbol) { self.declare_global(name); self.store_var(name); } - fn declare_global(&mut self, name: &LStr) { - let name: Rc = name.into(); + fn declare_global(&mut self, name: Symbol) { let global = Var { - name: name.clone(), + name, kind: VarKind::Global }; - let shadowed = self.scope.insert(name.clone(), global); + let shadowed = self.scope.insert(name, global); self.shadowed.push((name, shadowed)); } - fn store_var(&mut self, name: &LStr) { + fn store_var(&mut self, name: Symbol) { match self.resolve_name(name) { ResolveOutcome::Var(VarKind::Local(n)) => { self.emit(I::StoreLocal(Arg24::from_usize(n))); @@ -313,23 +309,21 @@ impl<'a> Compiler<'a> { self.emit(I::StoreClosedLocal(Arg24::from_usize(n))); } ResolveOutcome::InParent => { - let n = match self.closes.get(name) { + let n = match self.closes.get(&name) { Some(n) => *n, None => { let n = self.closes.len(); - self.closes.insert(name.into(), n); + self.closes.insert(name, n); n } }; self.emit(I::StoreUpvalue(Arg24::from_usize(n))); } ResolveOutcome::Var(VarKind::Global) => { - let s = Symbol::get(name); - self.emit(I::StoreGlobal(Arg24::from_symbol(s))); + self.emit(I::StoreGlobal(Arg24::from_symbol(name))); } ResolveOutcome::None if self.mode == CompilerMode::Repl => { - let s = Symbol::get(name); - self.emit(I::StoreGlobal(Arg24::from_symbol(s))); + self.emit(I::StoreGlobal(Arg24::from_symbol(name))); } ResolveOutcome::None => { self.assign_local(name); @@ -357,7 +351,7 @@ impl<'a> Compiler<'a> { self.end_scope(scope); }, 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) => { self.expr(a); self.emit(I::UnaryOp(*o)); @@ -371,12 +365,12 @@ impl<'a> Compiler<'a> { ExprKind::AssignVar(name, a) => { self.expr(a); self.emit(I::Dup); - self.assign_local(name); + self.assign_local(*name); }, ExprKind::AssignGlobal(name, a) => { self.expr(a); self.emit(I::Dup); - self.assign_global(name); + self.assign_global(*name); }, ExprKind::List(xs) if xs.is_empty() => { self.emit(I::NewList(0)); @@ -446,7 +440,8 @@ impl<'a> Compiler<'a> { self.emit(I::Swap); 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) => { self.expr(a); self.emit(I::Dup); @@ -490,7 +485,7 @@ impl<'a> Compiler<'a> { 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), } } @@ -534,7 +529,7 @@ impl<'a> Compiler<'a> { 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 self.expr(iter); self.emit(I::IterBegin); @@ -565,19 +560,18 @@ impl<'a> Compiler<'a> { self.emit(I::Nil); } - fn expr_lambda(&mut self, args: &[&LStr], body: &Expr) { - let mut inner = self.new_function(args); + fn expr_fndef(&mut self, name: Option, args: &[Symbol], body: &Expr) { + let mut inner = self.new_function(name, args); inner.parent = Some(self); inner.expr(body); - let (func, closes) = inner.finish_inner(); let func_const = self.add_const(func.into()); let num_closed = closes.len(); for (name, _) in closes { - match self.resolve_name(&name) { + match self.resolve_name(name) { ResolveOutcome::Var(VarKind::Local(n)) => { self.emit(I::CloseOver(Arg24::from_usize(n))); self.scope.entry(name).and_modify(|v| { @@ -602,6 +596,11 @@ impl<'a> Compiler<'a> { } else { 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) { @@ -626,14 +625,14 @@ impl<'a> Compiler<'a> { (LValueKind::Ident(i), None) => { self.expr(a); self.emit(I::Dup); - self.store_var(i); + self.store_var(*i); }, (LValueKind::Ident(i), Some(o)) => { - self.load_var(i); + self.load_var(*i); self.expr(a); self.emit(I::BinaryOp(o)); self.emit(I::Dup); - self.store_var(i); + self.store_var(*i); }, (LValueKind::Index(ct, i), None) => { self.expr(ct); diff --git a/talc-lang/src/lstring.rs b/talc-lang/src/lstring.rs index 2ffafa8..7398053 100644 --- a/talc-lang/src/lstring.rs +++ b/talc-lang/src/lstring.rs @@ -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}; @@ -243,6 +243,11 @@ impl From for LString { fn from(value: String) -> Self { Self { inner: value.into_bytes() } } } +impl From for LString { + #[inline] + fn from(value: OsString) -> Self { Self { inner: value.into_encoded_bytes() } } +} + impl From<&LStr> for LString { #[inline] fn from(value: &LStr) -> Self { value.to_owned() } @@ -258,6 +263,16 @@ impl From<&[u8]> for LString { fn from(value: &[u8]) -> Self { value.to_owned().into() } } +impl From<&[u8; N]> for LString { + #[inline] + fn from(value: &[u8; N]) -> Self { value.as_slice().into() } +} + +impl From<[u8; N]> for LString { + #[inline] + fn from(value: [u8; N]) -> Self { value.as_slice().into() } +} + impl From> for LString { #[inline] fn from(value: Cow<'_, LStr>) -> Self { @@ -282,6 +297,13 @@ impl From> for LString { } } +impl From> for LString { + #[inline] + fn from(value: Cow<'_, [u8; N]>) -> Self { + value.into_owned().into() + } +} + impl<'a> From<&'a LStr> for &'a [u8] { #[inline] fn from(value: &'a LStr) -> Self { &value.inner } @@ -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 { #[inline] 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] { #[inline] 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); + + #[inline] + fn next(&mut self) -> Option { + 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) { + let len = self.0.len(); + ((len + 3)/4, Some(len)) + } +} + +impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> { + fn next_back(&mut self) -> Option { + let (new_bytes, res) = next_codepoint_back(self.0)?; + self.0 = new_bytes; + match res { + Ok(c) => self.1 -= c.len_utf8(), + Err(_) => self.1 -= 1, + } + Some((self.1, res)) + } +} + #[derive(Clone)] pub struct Chars<'a>(LosslessChars<'a>); @@ -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 { + loop { + if let (index, Ok(c)) = self.0.next()? { + return Some((index, c)) + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl<'a> DoubleEndedIterator for CharsIndices<'a> { + fn next_back(&mut self) -> Option { + loop { + if let (index, Ok(c)) = self.0.next_back()? { + return Some((index, c)) + } + } + } +} impl LStr { #[inline] @@ -612,6 +715,11 @@ impl LStr { Self::from_bytes(string.as_bytes()) } + #[inline] + pub fn from_os_str(os_string: &OsStr) -> &Self { + Self::from_bytes(os_string.as_encoded_bytes()) + } + #[inline] pub const fn from_bytes(bytes: &[u8]) -> &Self { unsafe { &*(bytes as *const [u8] as *const LStr) } @@ -628,14 +736,22 @@ impl LStr { #[inline] pub fn as_bytes_mut(&mut self) -> &mut [u8] { &mut self.inner } + #[inline] + pub fn byte_at(&self, n: usize) -> u8 { self.inner[n] } + + #[inline] + pub fn byte_get(&self, n: usize) -> Option { self.inner.get(n).copied() } + #[inline] pub fn bytes(&self) -> Bytes { Bytes(self.as_bytes().iter().copied()) } + #[inline] pub fn chars(&self) -> Chars { Chars(self.chars_lossless()) } + #[inline] pub fn chars_lossless(&self) -> LosslessChars { LosslessChars(self.as_bytes()) @@ -746,6 +862,14 @@ impl LStr { self.as_bytes().ends_with(s.as_bytes()) } + pub fn strip_prefix(&self, prefix: &LStr) -> Option<&LStr> { + self.as_bytes().strip_prefix(prefix.as_bytes()).map(LStr::from_bytes) + } + + pub fn strip_suffix(&self, suffix: &LStr) -> Option<&LStr> { + self.as_bytes().strip_suffix(suffix.as_bytes()).map(LStr::from_bytes) + } + pub fn is_identifier(&self) -> bool { let mut chars = self.chars_lossless(); let first = chars.next() diff --git a/talc-lang/src/parser/ast.rs b/talc-lang/src/parser/ast.rs index 6d22784..3c9561f 100644 --- a/talc-lang/src/parser/ast.rs +++ b/talc-lang/src/parser/ast.rs @@ -1,6 +1,6 @@ use core::fmt; -use crate::{lstring::LStr, symbol::Symbol, value::Value}; +use crate::{symbol::Symbol, value::Value}; use super::Span; @@ -19,76 +19,77 @@ pub enum UnaryOp { } #[derive(Debug)] -pub struct Expr<'s> { +pub struct Expr { pub span: Span, - pub kind: ExprKind<'s>, + pub kind: ExprKind, } #[derive(Debug)] -pub enum ExprKind<'s> { +pub enum ExprKind { Literal(Value), - Ident(&'s LStr), + Ident(Symbol), - UnaryOp(UnaryOp, Box>), - BinaryOp(BinaryOp, Box>, Box>), + UnaryOp(UnaryOp, Box), + BinaryOp(BinaryOp, Box, Box), - Assign(Option, Box>, Box>), - AssignVar(&'s LStr, Box>), - AssignGlobal(&'s LStr, Box>), + Assign(Option, Box, Box), + AssignVar(Symbol, Box), + AssignGlobal(Symbol, Box), + FnDef(Option, Vec, Box), - Index(Box>, Box>), - FnCall(Box>, Vec>), - AssocFnCall(Box>, Symbol, Vec>), - Pipe(Box>, Box>), + Index(Box, Box), + FnCall(Box, Vec), + AssocFnCall(Box, Symbol, Vec), + Pipe(Box, Box), - Block(Vec>), - List(Vec>), - Table(Vec<(Expr<'s>, Expr<'s>)>), + Block(Vec), + List(Vec), + Table(Vec<(Expr, Expr)>), - Return(Box>), - And(Box>, Box>), - Or(Box>, Box>), - If(Box>, Box>, Option>>), - While(Box>, Box>), - For(&'s LStr, Box>, Box>), - Lambda(Vec<&'s LStr>, Box>), - Try(Box>, Vec>), + Return(Box), + And(Box, Box), + Or(Box, Box), + If(Box, Box, Option>), + While(Box, Box), + For(Symbol, Box, Box), + Lambda(Vec, Box), + Try(Box, Vec), } -impl<'s> ExprKind<'s> { - pub fn span(self, span: Span) -> Expr<'s> { +impl ExprKind { + pub fn span(self, span: Span) -> Expr { Expr { kind: self, span } } } #[derive(Debug)] -pub struct CatchBlock<'s> { +pub struct CatchBlock { pub span: Span, - pub name: Option<&'s LStr>, + pub name: Option, pub types: Option>, - pub body: Expr<'s>, + pub body: Expr, } #[derive(Debug)] -pub struct LValue<'s> { +pub struct LValue { pub span: Span, - pub kind: LValueKind<'s>, + pub kind: LValueKind, } #[derive(Debug)] -pub enum LValueKind<'s> { - Ident(&'s LStr), - Index(Box>, Box>), +pub enum LValueKind { + Ident(Symbol), + Index(Box, Box), } -impl<'s> LValueKind<'s> { - pub fn span(self, span: Span) -> LValue<'s> { +impl LValueKind { + pub fn span(self, span: Span) -> LValue { LValue { kind: self, span } } } -impl<'s> LValue<'s> { - pub fn from_expr(e: Expr<'s>) -> Option> { +impl LValue { + pub fn from_expr(e: Expr) -> Option { let Expr { span, kind } = e; match kind { 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 { write!(w, "{0: >1$}catch", "", depth*2)?; if let Some(name) = self.name { - write!(w, " ${name}")?; + write!(w, " ${}", name.name())?; } if let Some(types) = &self.types { 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 { 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 { write!(w, "{0: >1$}", "", depth*2)?; let depth = depth + 1; match &self.kind { - LValueKind::Ident(n) => writeln!(w, "${n}"), + LValueKind::Ident(n) => writeln!(w, "${}", n.name()), LValueKind::Index(l, r) => { writeln!(w, "index")?; 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 { 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 { write!(w, "{0: >1$}", "", depth*2)?; let depth = depth + 1; match &self.kind { ExprKind::Literal(val) => writeln!(w, "{val}"), - ExprKind::Ident(n) => writeln!(w, "${n}"), + ExprKind::Ident(n) => writeln!(w, "${}", n.name()), ExprKind::UnaryOp(op, e) => { writeln!(w, "uop {op:?}")?; e.write_to(w, depth) @@ -168,13 +169,24 @@ impl<'s> Expr<'s> { r.write_to(w, depth) }, ExprKind::AssignVar(l, r) => { - writeln!(w, "var {l}")?; + writeln!(w, "var {}", l.name())?; r.write_to(w, depth) }, ExprKind::AssignGlobal(l, r) => { - writeln!(w, "global {l}")?; + writeln!(w, "global {}", l.name())?; r.write_to(w, depth) }, + ExprKind::FnDef(n, p, b) => { + if let Some(n) = n { + writeln!(w, "fndef ${}", n.name())?; + } else { + writeln!(w, "fndef anon")?; + } + for arg in p { + writeln!(w, " ${}", arg.name())?; + } + b.write_to(w, depth) + }, ExprKind::Index(l, r) => { writeln!(w, "index")?; l.write_to(w, depth)?; @@ -252,14 +264,14 @@ impl<'s> Expr<'s> { b.write_to(w, depth) } ExprKind::For(v, i, b) => { - writeln!(w, "for {v}")?; + writeln!(w, "for {}", v.name())?; i.write_to(w, depth)?; b.write_to(w, depth) } ExprKind::Lambda(a, b) => { write!(w, "lambda")?; for arg in a { - write!(w, " {arg}")?; + write!(w, " {}", arg.name())?; } writeln!(w)?; 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 { self.write_to(f, 0) } diff --git a/talc-lang/src/parser/lexer.rs b/talc-lang/src/parser/lexer.rs index d4596ae..6f63021 100644 --- a/talc-lang/src/parser/lexer.rs +++ b/talc-lang/src/parser/lexer.rs @@ -79,6 +79,7 @@ pub enum TokenKind { Else, End, False, + Fn, For, Global, If, @@ -167,6 +168,7 @@ impl TokenKind { K::Else => "'else'", K::End => "'end'", K::False => "'false'", + K::Fn => "'fn'", K::For => "'for'", K::Global => "'global'", K::If => "'if'", @@ -295,6 +297,7 @@ impl<'s> Lexer<'s> { "else" => K::Else, "end" => K::End, "false" => K::False, + "fn" => K::Fn, "for" => K::For, "global" => K::Global, "if" => K::If, diff --git a/talc-lang/src/parser/parser.rs b/talc-lang/src/parser/parser.rs index c270e68..2e23211 100644 --- a/talc-lang/src/parser/parser.rs +++ b/talc-lang/src/parser/parser.rs @@ -1,7 +1,7 @@ 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 num_complex::Complex64; @@ -164,6 +164,7 @@ impl TokenKind { | T::Return | T::Var | T::Global + | T::Fn | T::Not | T::Backslash | T::Colon @@ -209,7 +210,7 @@ impl<'s> Parser<'s> { } - fn parse_table_items(&mut self) -> Result, Expr<'s>)>> { + fn parse_table_items(&mut self) -> Result> { let mut items = Vec::new(); while self.peek()?.kind.expr_first() { let key = if let Some(id) = try_next!(self, T::Identifier) { @@ -231,7 +232,7 @@ impl<'s> Parser<'s> { Ok(items) } - fn parse_expr_list(&mut self) -> Result>> { + fn parse_expr_list(&mut self) -> Result> { let mut exprs = Vec::new(); while self.peek()?.kind.expr_first() { exprs.push(self.parse_expr()?); @@ -242,10 +243,10 @@ impl<'s> Parser<'s> { Ok(exprs) } - fn parse_ident_list(&mut self) -> Result> { + fn parse_ident_list(&mut self) -> Result> { let mut idents = Vec::new(); 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() { break } @@ -264,7 +265,7 @@ impl<'s> Parser<'s> { Ok(syms) } - fn parse_catch_blocks(&mut self) -> Result<(Vec>, Span)> { + fn parse_catch_blocks(&mut self) -> Result<(Vec, Span)> { let mut blocks = Vec::new(); let mut outer_span = self.peek()?.span; loop { @@ -277,7 +278,7 @@ impl<'s> Parser<'s> { }; 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, }; @@ -291,7 +292,7 @@ impl<'s> Parser<'s> { Ok((blocks, outer_span)) } - fn parse_if_stmt_chain(&mut self) -> Result> { + fn parse_if_stmt_chain(&mut self) -> Result { let cond = self.parse_expr()?; expect!(self, T::Then); let body = self.parse_block()?; @@ -315,7 +316,7 @@ impl<'s> Parser<'s> { } - fn parse_term_not_ident(&mut self) -> Result> { + fn parse_term_not_ident(&mut self) -> Result { let tok = self.next()?; match tok.kind { T::LParen => { @@ -333,7 +334,7 @@ impl<'s> Parser<'s> { let end = expect!(self, T::RBrace); 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 => { let b = self.parse_block()?; expect!(self, T::End); @@ -356,7 +357,7 @@ impl<'s> Parser<'s> { let body = self.parse_block()?; let end = expect!(self, T::End); 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 => { let body = self.parse_block()?; @@ -406,15 +407,15 @@ impl<'s> Parser<'s> { } } - fn parse_term(&mut self) -> Result> { + fn parse_term(&mut self) -> Result { 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 { self.parse_term_not_ident() } } - fn parse_access(&mut self) -> Result> { + fn parse_access(&mut self) -> Result { let mut lhs = self.parse_term()?; loop { let tok = try_next!(self, T::LParen | T::LBrack | T::Arrow | T::Dot); @@ -454,7 +455,7 @@ impl<'s> Parser<'s> { Ok(lhs) } - fn parse_precedence(&mut self, min_prec: u8) -> Result> { + fn parse_precedence(&mut self, min_prec: u8) -> Result { let mut lhs = if let Some(op) = self.peek()?.kind.unary_op() { let tok = self.next()?; let rhs = self.parse_precedence(op.precedence())?; @@ -495,7 +496,7 @@ impl<'s> Parser<'s> { Ok(lhs) } - fn parse_lambda(&mut self) -> Result> { + fn parse_lambda(&mut self) -> Result { let tok = try_next!(self, T::Backslash | T::Colon); match tok { Some(Token { kind: T::Backslash, span, .. }) => { @@ -506,7 +507,7 @@ impl<'s> Parser<'s> { Ok(E::Lambda(args, b(body)).span(span + body_span)) }, Some(Token { kind: T::Colon, span, .. }) => { - let args = vec![lstr!("$")]; + let args = vec![*SYM_DOLLAR_SIGN]; let body = self.parse_lambda()?; let body_span = body.span; Ok(E::Lambda(args, b(body)).span(span + body_span)) @@ -516,7 +517,7 @@ impl<'s> Parser<'s> { } } - fn parse_pipeline(&mut self) -> Result> { + fn parse_pipeline(&mut self) -> Result { let mut lhs = self.parse_lambda()?; let mut span = lhs.span; while try_next!(self, T::Pipe).is_some() { @@ -527,7 +528,7 @@ impl<'s> Parser<'s> { Ok(lhs) } - fn parse_not(&mut self) -> Result> { + fn parse_not(&mut self) -> Result { if let Some(tok) = try_next!(self, T::Not) { let expr = self.parse_not()?; let span = tok.span + expr.span; @@ -537,7 +538,7 @@ impl<'s> Parser<'s> { } } - fn parse_and(&mut self) -> Result> { + fn parse_and(&mut self) -> Result { let mut lhs = self.parse_not()?; let mut span = lhs.span; while try_next!(self, T::And).is_some() { @@ -548,7 +549,7 @@ impl<'s> Parser<'s> { Ok(lhs) } - fn parse_or(&mut self) -> Result> { + fn parse_or(&mut self) -> Result { let mut lhs = self.parse_and()?; let mut span = lhs.span; while try_next!(self, T::Or).is_some() { @@ -559,16 +560,7 @@ impl<'s> Parser<'s> { Ok(lhs) } - fn parse_assign(&mut self) -> Result> { - 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)) - } + fn parse_assign(&mut self) -> Result { let lhs = self.parse_or()?; let lhs_span = lhs.span; if let Some(op) = self.peek()?.kind.assign_op() { @@ -576,7 +568,7 @@ impl<'s> Parser<'s> { throw!(lhs_span, "invalid lvalue for assingment") }; self.next()?; - let rhs = self.parse_assign()?; + let rhs = self.parse_decl()?; let rhs_span = rhs.span; Ok(E::Assign(op, b(lval), b(rhs)).span(lhs_span + rhs_span)) } else { @@ -584,17 +576,59 @@ impl<'s> Parser<'s> { } } - fn parse_expr(&mut self) -> Result> { - if let Some(tok) = try_next!(self, T::Return) { - let expr = self.parse_assign()?; - let span = expr.span; - Ok(E::Return(b(expr)).span(tok.span + span)) - } else { - self.parse_assign() + fn parse_var_decl(&mut self) -> Result { + let first = expect!(self, T::Var | T::Global); + let name = expect!(self, T::Identifier); + expect!(self, T::Equal); + let val = self.parse_decl()?; + let val_span = val.span; + let kind = if first.kind == T::Global { E::AssignGlobal } else { E::AssignVar }; + Ok(kind(Symbol::get(name.content), b(val)) + .span(first.span + val_span)) + } + + fn parse_fn_decl(&mut self) -> Result { + 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 { + 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> { + fn parse_expr(&mut self) -> Result { + 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 { while try_next!(self, T::LineSeparator).is_some() {} let mut span = self.peek()?.span; @@ -612,7 +646,7 @@ impl<'s> Parser<'s> { Ok(E::Block(exprs).span(span)) } - fn parse(mut self) -> Result> { + fn parse(mut self) -> Result { let block = self.parse_block()?; expect!(self, T::Eof); Ok(block) diff --git a/talc-lang/src/parser/util.rs b/talc-lang/src/parser/util.rs index e03da70..b0dd8c5 100644 --- a/talc-lang/src/parser/util.rs +++ b/talc-lang/src/parser/util.rs @@ -132,3 +132,33 @@ pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result 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) +} + + diff --git a/talc-lang/src/symbol.rs b/talc-lang/src/symbol.rs index 0c21de3..4dca299 100644 --- a/talc-lang/src/symbol.rs +++ b/talc-lang/src/symbol.rs @@ -9,6 +9,9 @@ struct SymbolTable { } lazy_static! { + pub static ref SYM_SELF: Symbol = symbol!(self); + pub static ref SYM_DOLLAR_SIGN: Symbol = symbol!("$"); + pub static ref SYM_NIL: Symbol = symbol!(nil); pub static ref SYM_BOOL: Symbol = symbol!(bool); pub static ref SYM_SYMBOL: Symbol = symbol!(symbol); diff --git a/talc-lang/src/value/function.rs b/talc-lang/src/value/function.rs index 68c65de..7ba0ae4 100644 --- a/talc-lang/src/value/function.rs +++ b/talc-lang/src/value/function.rs @@ -1,12 +1,13 @@ 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}; #[derive(Clone, Copy, Debug, Default)] pub struct FuncAttrs { pub arity: usize, + pub name: Option, } #[derive(Debug, Clone)] @@ -36,8 +37,12 @@ pub struct NativeFunc { } impl NativeFunc { - pub fn new(func: FnNative, arity: usize) -> Self { - Self { func, attrs: FuncAttrs { arity } } + pub fn new(func: FnNative, arity: usize, name: Symbol) -> Self { + Self { func, attrs: FuncAttrs { arity, name: Some(name) } } + } + + pub fn new_anon(func: FnNative, arity: usize) -> Self { + Self { func, attrs: FuncAttrs { arity, name: None } } } } diff --git a/talc-lang/src/value/index.rs b/talc-lang/src/value/index.rs index 4abc138..6204806 100644 --- a/talc-lang/src/value/index.rs +++ b/talc-lang/src/value/index.rs @@ -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}; @@ -72,7 +72,7 @@ impl Value { 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 { throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}") } diff --git a/talc-lang/src/value/mod.rs b/talc-lang/src/value/mod.rs index f84bf04..895d855 100644 --- a/talc-lang/src/value/mod.rs +++ b/talc-lang/src/value/mod.rs @@ -166,14 +166,23 @@ impl Value { w.write_all(b" }") }, Self::Function(g) => { - if g.state.is_empty() { - write!(w, "", g.attrs.arity, Rc::as_ptr(g)) + if let Some(name) = g.attrs.name { + write!(w, "12x}>", + name.name(), g.attrs.arity, Rc::as_ptr(g) as usize) } else { - write!(w, "", g.attrs.arity, Rc::as_ptr(g)) + write!(w, "12x}>", + g.attrs.arity, Rc::as_ptr(g) as usize) + } + } + Self::NativeFunc(g) => { + if let Some(name) = g.attrs.name { + write!(w, "12x}>", + name.name(), g.attrs.arity, Rc::as_ptr(g) as usize) + } else { + write!(w, "12x}>", + g.attrs.arity, Rc::as_ptr(g) as usize) } } - Self::NativeFunc(g) - => write!(w, "", g.attrs.arity, Rc::as_ptr(g)), Self::Native(n) => n.to_lstring(w, repr, recur), } } diff --git a/talc-lang/src/value/ops.rs b/talc-lang/src/value/ops.rs index 11679cb..1af9597 100644 --- a/talc-lang/src/value/ops.rs +++ b/talc-lang/src/value/ops.rs @@ -5,7 +5,7 @@ use num_complex::{Complex64, ComplexFloat}; use num_rational::Rational64; use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Signed, Zero}; -use crate::{exception::{throw, Result}, lstring::LString, symbol::{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}; @@ -556,7 +556,7 @@ impl Value { Ok(Value::iter_pack(range_iter.borrow_mut().next() .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) => { let byte_pos = RefCell::new(0); @@ -569,7 +569,7 @@ impl Value { 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) => { let idx = RefCell::new(0); @@ -582,7 +582,7 @@ impl Value { 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) => { let keys: Vec = table.borrow().keys().cloned().collect(); @@ -591,7 +591,7 @@ impl Value { Ok(Value::iter_pack(keys.borrow_mut().next() .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:#}"), } diff --git a/talc-lang/src/vm.rs b/talc-lang/src/vm.rs index 38df830..c4ebfc6 100644 --- a/talc-lang/src/vm.rs +++ b/talc-lang/src/vm.rs @@ -91,7 +91,7 @@ fn get_call_outcome(args: Vec) -> Result { vm.call_value(f.clone(), args) }; let nf = NativeFunc { - attrs: FuncAttrs { arity: remaining }, + attrs: FuncAttrs { arity: remaining, name: None }, func: Box::new(nf), }; Ok(CallOutcome::Partial(nf.into())) diff --git a/talc-macros/src/native_func.rs b/talc-macros/src/native_func.rs index 186b31c..f3506f4 100644 --- a/talc-macros/src/native_func.rs +++ b/talc-macros/src/native_func.rs @@ -1,20 +1,27 @@ use proc_macro::TokenStream; -use syn::{parse::Parse, parse_macro_input, ItemFn, LitInt}; -use quote::quote; +use syn::{parse::Parse, parse_macro_input, Token}; +use quote::{quote, ToTokens}; struct NativeFuncArgs { - arity: LitInt, + arity: syn::LitInt, + name: Option, } impl Parse for NativeFuncArgs { fn parse(input: syn::parse::ParseStream) -> syn::Result { let arity = input.parse()?; - Ok(Self { arity }) + let t: Option = input.parse()?; + if t.is_some() { + let name = input.parse()?; + Ok(Self { arity, name: Some(name) }) + } else { + Ok(Self { arity, name: None }) + } } } pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream { - let Ok(itemfn) = syn::parse::(annotated_item.clone()) else { + let Ok(itemfn) = syn::parse::(annotated_item.clone()) else { return annotated_item }; 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 arity = args.arity; + let talc_name = match args.name { + Some(n) => n.to_token_stream(), + None => name.to_token_stream(), + }; + assert!(itemfn.sig.constness.is_none(), "item must not be const"); assert!(itemfn.sig.asyncness.is_none(), "item must not be async"); assert!(itemfn.sig.unsafety.is_none(), "item must not be unsafe"); @@ -37,6 +49,7 @@ pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStre ::talc_lang::value::function::NativeFunc { attrs: ::talc_lang::value::function::FuncAttrs{ arity: #arity, + name: Some(::talc_lang::symbol::symbol!(#talc_name)) }, func: Box::new(|#inputs| #output #block) } diff --git a/talc-std/src/collection.rs b/talc-std/src/collection.rs index 6716df9..ff86fa4 100644 --- a/talc-std/src/collection.rs +++ b/talc-std/src/collection.rs @@ -1,6 +1,6 @@ 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 crate::unpack_args; @@ -205,7 +205,7 @@ pub fn sort_key(vm: &mut Vm, args: Vec) -> Result { 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)?; Ok(list.into()) } diff --git a/talc-std/src/file.rs b/talc-std/src/file.rs index 8257247..93843b5 100644 --- a/talc-std/src/file.rs +++ b/talc-std/src/file.rs @@ -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 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 lazy_static::lazy_static; @@ -454,7 +454,7 @@ fn tcp_listen_inner(listener: TcpListener) -> Value { 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)] diff --git a/talc-std/src/format.rs b/talc-std/src/format.rs new file mode 100644 index 0000000..83687b6 --- /dev/null +++ b/talc-std/src/format.rs @@ -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, + align: Option<(Align, char)>, + sign: FmtSign, + width: Option, + prec: Option, + 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 { + let c = &self.code[self.pos..].chars().next()?; + self.pos += c.len_utf8(); + Some(*c) + } + + fn peek(&mut self) -> Option { + self.code[self.pos..].chars().next() + } + + fn try_next(&mut self, chs: &[char]) -> Option { + 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> { + 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> { + 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> { + 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 { + 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 { + 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, 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 { + 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, 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, 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, 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, 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, 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, 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) -> Result { + 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()); +} + diff --git a/talc-std/src/io.rs b/talc-std/src/io.rs index 791fbd3..b303a1c 100644 --- a/talc-std/src/io.rs +++ b/talc-std/src/io.rs @@ -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_macros::native_func; use crate::{unpack_args, SYM_IO_ERROR}; +static ENV_LOCK: Mutex<()> = Mutex::new(()); + #[native_func(1)] pub fn print(_: &mut Vm, args: Vec) -> Result { let res = match &args[1] { @@ -92,7 +94,13 @@ pub fn setenv(_: &mut Vm, args: Vec) -> Result { if val.as_bytes().contains(&0) { 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) } @@ -105,7 +113,13 @@ pub fn delenv(_: &mut Vm, args: Vec) -> Result { 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") } - 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) } diff --git a/talc-std/src/iter.rs b/talc-std/src/iter.rs index a899df1..68ed425 100644 --- a/talc-std/src/iter.rs +++ b/talc-std/src/iter.rs @@ -1,6 +1,6 @@ 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 crate::unpack_args; @@ -87,7 +87,7 @@ pub fn pairs(_: &mut Vm, args: Vec) -> Result { 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)] @@ -98,7 +98,7 @@ pub fn once(_: &mut Vm, args: Vec) -> Result { let f = move |_: &mut Vm, _| { 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)] @@ -108,7 +108,7 @@ pub fn forever(_: &mut Vm, args: Vec) -> Result { let f = move |_: &mut Vm, _| { 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) -> Result { 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)] @@ -145,7 +145,7 @@ pub fn tee(_: &mut Vm, args: Vec) -> Result { 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)] @@ -163,7 +163,7 @@ pub fn scan(_: &mut Vm, args: Vec) -> Result { *result.borrow_mut() = r.clone(); Ok(r) }; - Ok(NativeFunc::new(Box::new(f), 0).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[scan]")).into()) } #[native_func(2)] @@ -182,7 +182,7 @@ pub fn filter(_: &mut Vm, args: Vec) -> Result { } } }; - Ok(NativeFunc::new(Box::new(f), 0).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[filter]")).into()) } #[native_func(2)] @@ -208,7 +208,7 @@ pub fn take(_: &mut Vm, args: Vec) -> Result { *taken.borrow_mut() += 1; Ok(next) }; - Ok(NativeFunc::new(Box::new(f), 0).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[take]")).into()) } #[native_func(2)] @@ -233,7 +233,7 @@ pub fn skip(_: &mut Vm, args: Vec) -> Result { } 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)] @@ -252,7 +252,7 @@ pub fn enumerate(_: &mut Vm, args: Vec) -> Result { 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) -> Result { } }; - Ok(NativeFunc::new(Box::new(f), 0).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cycle]")).into()) } #[derive(Clone, Copy, Default)] @@ -329,7 +329,7 @@ pub fn step(_: &mut Vm, args: Vec) -> Result { }, 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)] @@ -349,7 +349,7 @@ pub fn rev(_: &mut Vm, args: Vec) -> Result { 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) -> Result { 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)] @@ -401,7 +401,7 @@ pub fn zipn(_: &mut Vm, args: Vec) -> Result { 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)] @@ -428,7 +428,7 @@ pub fn alternate(_: &mut Vm, args: Vec) -> Result { } }; - Ok(NativeFunc::new(Box::new(f), 0).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[alternate]")).into()) } #[native_func(1)] @@ -458,7 +458,7 @@ pub fn alternaten(_: &mut Vm, args: Vec) -> Result { } }; - Ok(NativeFunc::new(Box::new(f), 0).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[alternaten]")).into()) } #[derive(Default)] @@ -500,7 +500,7 @@ pub fn intersperse(_: &mut Vm, args: Vec) -> Result { } }; - 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) -> Result { } }; - Ok(NativeFunc::new(Box::new(f), 0).into()) + Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[chain]")).into()) } #[derive(Default, Debug)] @@ -588,7 +588,7 @@ pub fn cartprod(_: &mut Vm, args: Vec) -> Result { 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()) } diff --git a/talc-std/src/lib.rs b/talc-std/src/lib.rs index 8708d71..cf90b5f 100644 --- a/talc-std/src/lib.rs +++ b/talc-std/src/lib.rs @@ -9,6 +9,7 @@ pub mod exception; pub mod num; pub mod collection; pub mod string; +pub mod format; pub mod file; pub mod regex; #[cfg(feature="random")] @@ -22,6 +23,7 @@ pub fn load_all(vm: &mut Vm) { num::load(vm); io::load(vm); string::load(vm); + format::load(vm); regex::load(vm); file::load(vm); #[cfg(feature="random")] diff --git a/talc-std/src/num.rs b/talc-std/src/num.rs index 076841e..d7d1cca 100644 --- a/talc-std/src/num.rs +++ b/talc-std/src/num.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; 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 crate::unpack_args; @@ -123,42 +123,13 @@ pub fn load(vm: &mut Vm) { // base conversions // -fn to_radix_inner(n: i64, radix: u32, upper: bool) -> LString { - let mut result = vec![]; - let mut begin = 0; - - let mut x; - if n < 0 { - result.push('-' as u32 as u8); - begin = 1; - x = (-n) as u64; - } else { - x = n as u64; - } - - loop { - let m = x % (radix as u64); - x /= radix as u64; - - let mut c = char::from_digit(m as u32, radix).unwrap(); - if upper { c.make_ascii_uppercase(); } - result.push(c as u8); - if x == 0 { - break; - } - } - result[begin..].reverse(); - LString::from(result) -} - - #[native_func(1)] pub fn bin(_: &mut Vm, args: Vec) -> Result { let [_, x] = unpack_args!(args); let Value::Int(x) = x else { 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)] @@ -167,7 +138,7 @@ pub fn sex(_: &mut Vm, args: Vec) -> Result { let Value::Int(x) = x else { 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)] @@ -176,7 +147,7 @@ pub fn oct(_: &mut Vm, args: Vec) -> Result { let Value::Int(x) = x else { 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)] @@ -185,7 +156,7 @@ pub fn hex(_: &mut Vm, args: Vec) -> Result { let Value::Int(x) = x else { 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)] @@ -197,7 +168,7 @@ pub fn to_radix(_: &mut Vm, args: Vec) -> Result { if *radix < 2 || *radix > 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)] @@ -209,7 +180,7 @@ pub fn to_radix_upper(_: &mut Vm, args: Vec) -> Result { if *radix < 2 || *radix > 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)] diff --git a/talc-std/src/string.rs b/talc-std/src/string.rs index a06d122..49f0ac3 100644 --- a/talc-std/src/string.rs +++ b/talc-std/src/string.rs @@ -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 crate::{unpack_args, SYM_FORMAT_ERROR}; +use crate::unpack_args; pub fn load(vm: &mut Vm) { 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("str_to_bytes", str_to_bytes().into()); vm.set_global_name("str_of_bytes", str_of_bytes().into()); - vm.set_global_name("format", _format().into()); } #[native_func(1)] @@ -168,38 +167,3 @@ pub fn str_of_bytes(_: &mut Vm, args: Vec) -> Result { }).collect::>>()?; Ok(LString::from(bytes).into()) } - -#[native_func(2)] -pub fn _format(_: &mut Vm, args: Vec) -> Result { - 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()) -} diff --git a/talc-std/src/value.rs b/talc-std/src/value.rs index b8a614e..50b5a8e 100644 --- a/talc-std/src/value.rs +++ b/talc-std/src/value.rs @@ -1,6 +1,6 @@ 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 crate::unpack_args; @@ -14,12 +14,20 @@ pub fn load(vm: &mut Vm) { vm.set_global_name("str", str_().into()); vm.set_global_name("repr", repr().into()); + vm.set_global_name("symbol_name", symbol_name().into()); + vm.set_global_name("symbol_of", symbol_of().into()); + vm.set_global_name("symbol_exists", symbol_exists().into()); + vm.set_global_name("cell", cell().into()); vm.set_global_name("uncell", uncell().into()); vm.set_global_name("cell_replace", cell_replace().into()); vm.set_global_name("cell_take", cell_take().into()); - vm.set_global_name("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) -> Result { let [_, val] = unpack_args!(args); Ok(val.get_type().into()) @@ -42,7 +50,7 @@ pub fn is(_: &mut Vm, args: Vec) -> Result { Ok((val.get_type() == ty).into()) } -#[native_func(2)] +#[native_func(2, "as")] pub fn as_(_: &mut Vm, args: Vec) -> Result { let [_, val, ty] = unpack_args!(args); let Value::Symbol(ty) = ty else { @@ -133,7 +141,7 @@ pub fn copy(_: &mut Vm, args: Vec) -> Result { // -#[native_func(1)] +#[native_func(1, "str")] pub fn str_(_: &mut Vm, args: Vec) -> Result { let [_, val] = unpack_args!(args); Ok(lformat!("{val}").into()) @@ -145,6 +153,40 @@ pub fn repr(_: &mut Vm, args: Vec) -> Result { Ok(lformat!("{val:#}").into()) } +// +// symbols +// + +#[native_func(1)] +pub fn symbol_name(_: &mut Vm, args: Vec) -> Result { + 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) -> Result { + 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) -> Result { + 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 // @@ -183,13 +225,73 @@ pub fn cell_take(_: &mut Vm, args: Vec) -> Result { Ok(cell.replace(Value::Nil)) } +// +// functions +// + #[native_func(1)] -pub fn closure_state(_: &mut Vm, args: Vec) -> Result { +pub fn func_state(_: &mut Vm, args: Vec) -> Result { let [_, func] = unpack_args!(args); - let Value::Function(func) = func else { - throw!(*SYM_TYPE_ERROR, "closure_state: value is not a function") - }; - let l: Vec = func.state.iter().map(|v| Value::Cell(v.clone())).collect(); - Ok(l.into()) + match func { + Value::NativeFunc(_) => Ok(Value::Nil), + Value::Function(f) => { + let l: Vec = f.state.iter() + .map(|v| Value::Cell(v.clone())) + .collect(); + Ok(l.into()) + } + _ => throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a talc function") + } +} + + +#[native_func(1)] +pub fn func_arity(_: &mut Vm, args: Vec) -> Result { + 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) -> Result { + 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) -> Result { + 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) -> Result { + 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()) }