switch to symbols, update formatting

This commit is contained in:
trimill 2024-11-04 12:59:05 -05:00
parent 59f3e320e3
commit 801aaf7b78
23 changed files with 998 additions and 292 deletions

View file

@ -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

View file

@ -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<LStr>,
name: Symbol,
kind: VarKind,
}
@ -36,19 +34,19 @@ struct Compiler<'a> {
parent: Option<&'a Compiler<'a>>,
chunk: Chunk,
attrs: FuncAttrs,
scope: HashMap<Rc<LStr>, Var>,
shadowed: Vec<(Rc<LStr>, Option<Var>)>,
closes: BTreeMap<Rc<LStr>, usize>,
scope: HashMap<Symbol, Var>,
shadowed: Vec<(Symbol, Option<Var>)>,
closes: BTreeMap<Symbol, usize>,
local_count: usize,
}
pub fn compile(expr: &Expr) -> Function {
let mut comp = Compiler::new_module(None);
pub fn compile(expr: &Expr, name: Option<Symbol>) -> Function {
let mut comp = Compiler::new_module(name, None);
comp.expr(expr);
comp.finish()
}
pub fn compile_repl(expr: &Expr, globals: &[Rc<LStr>]) -> (Function, Vec<Rc<LStr>>) {
pub fn compile_repl(expr: &Expr, globals: &[Symbol]) -> (Function, Vec<Symbol>) {
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<LStr>]) -> (Function, Vec<Rc<LStr
impl<'a> Default for Compiler<'a> {
fn default() -> Self {
let mut scope = HashMap::new();
let self_name: Rc<LStr> = 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<LStr>]) -> Self {
fn new_repl(globals: &[Symbol]) -> Self {
let mut new = Self {
mode: CompilerMode::Repl,
attrs: FuncAttrs { arity: 0, name: Some(Symbol::get("<repl>")) },
..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<Symbol>, 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<Symbol>, 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<Rc<LStr>, usize>) {
pub fn finish_inner(mut self) -> (Function, BTreeMap<Symbol, usize>) {
self.emit(I::Return);
// 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);
(
// 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<LStr> = 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<LStr> = 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<Symbol>, 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);

View file

@ -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<String> for LString {
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 {
#[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<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 {
#[inline]
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] {
#[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<char, u8>);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let (new_bytes, res) = next_codepoint(self.0)?;
let index = self.1;
self.0 = new_bytes;
match res {
Ok(c) => self.1 += c.len_utf8(),
Err(_) => self.1 += 1,
}
Some((index, res))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.0.len();
((len + 3)/4, Some(len))
}
}
impl<'a> DoubleEndedIterator for LosslessCharsIndices<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
let (new_bytes, res) = next_codepoint_back(self.0)?;
self.0 = new_bytes;
match res {
Ok(c) => self.1 -= c.len_utf8(),
Err(_) => self.1 -= 1,
}
Some((self.1, res))
}
}
#[derive(Clone)]
pub struct Chars<'a>(LosslessChars<'a>);
@ -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 {
#[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<u8> { 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()

View file

@ -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<Expr<'s>>),
BinaryOp(BinaryOp, Box<Expr<'s>>, Box<Expr<'s>>),
UnaryOp(UnaryOp, Box<Expr>),
BinaryOp(BinaryOp, Box<Expr>, Box<Expr>),
Assign(Option<BinaryOp>, Box<LValue<'s>>, Box<Expr<'s>>),
AssignVar(&'s LStr, Box<Expr<'s>>),
AssignGlobal(&'s LStr, Box<Expr<'s>>),
Assign(Option<BinaryOp>, Box<LValue>, Box<Expr>),
AssignVar(Symbol, Box<Expr>),
AssignGlobal(Symbol, Box<Expr>),
FnDef(Option<Symbol>, Vec<Symbol>, Box<Expr>),
Index(Box<Expr<'s>>, Box<Expr<'s>>),
FnCall(Box<Expr<'s>>, Vec<Expr<'s>>),
AssocFnCall(Box<Expr<'s>>, Symbol, Vec<Expr<'s>>),
Pipe(Box<Expr<'s>>, Box<Expr<'s>>),
Index(Box<Expr>, Box<Expr>),
FnCall(Box<Expr>, Vec<Expr>),
AssocFnCall(Box<Expr>, Symbol, Vec<Expr>),
Pipe(Box<Expr>, Box<Expr>),
Block(Vec<Expr<'s>>),
List(Vec<Expr<'s>>),
Table(Vec<(Expr<'s>, Expr<'s>)>),
Block(Vec<Expr>),
List(Vec<Expr>),
Table(Vec<(Expr, Expr)>),
Return(Box<Expr<'s>>),
And(Box<Expr<'s>>, Box<Expr<'s>>),
Or(Box<Expr<'s>>, Box<Expr<'s>>),
If(Box<Expr<'s>>, Box<Expr<'s>>, Option<Box<Expr<'s>>>),
While(Box<Expr<'s>>, Box<Expr<'s>>),
For(&'s LStr, Box<Expr<'s>>, Box<Expr<'s>>),
Lambda(Vec<&'s LStr>, Box<Expr<'s>>),
Try(Box<Expr<'s>>, Vec<CatchBlock<'s>>),
Return(Box<Expr>),
And(Box<Expr>, Box<Expr>),
Or(Box<Expr>, Box<Expr>),
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
While(Box<Expr>, Box<Expr>),
For(Symbol, Box<Expr>, Box<Expr>),
Lambda(Vec<Symbol>, Box<Expr>),
Try(Box<Expr>, Vec<CatchBlock>),
}
impl<'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<Symbol>,
pub types: Option<Vec<Symbol>>,
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<Expr<'s>>, Box<Expr<'s>>),
pub enum LValueKind {
Ident(Symbol),
Index(Box<Expr>, Box<Expr>),
}
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<LValue<'s>> {
impl LValue {
pub fn from_expr(e: Expr) -> Option<LValue> {
let Expr { span, kind } = e;
match kind {
ExprKind::Ident(i) => Some(LValueKind::Ident(i).span(span)),
@ -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)
}

View file

@ -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,

View file

@ -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<Vec<(Expr<'s>, Expr<'s>)>> {
fn parse_table_items(&mut self) -> Result<Vec<(Expr, Expr)>> {
let mut items = Vec::new();
while self.peek()?.kind.expr_first() {
let key = if let Some(id) = try_next!(self, T::Identifier) {
@ -231,7 +232,7 @@ impl<'s> Parser<'s> {
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();
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<Vec<&'s LStr>> {
fn parse_ident_list(&mut self) -> Result<Vec<Symbol>> {
let mut idents = Vec::new();
while let Some(tok) = try_next!(self, T::Identifier) {
idents.push(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<CatchBlock<'s>>, Span)> {
fn parse_catch_blocks(&mut self) -> Result<(Vec<CatchBlock>, 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<Expr<'s>> {
fn parse_if_stmt_chain(&mut self) -> Result<Expr> {
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<Expr<'s>> {
fn parse_term_not_ident(&mut self) -> Result<Expr> {
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<Expr<'s>> {
fn parse_term(&mut self) -> Result<Expr> {
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<Expr<'s>> {
fn parse_access(&mut self) -> Result<Expr> {
let mut lhs = self.parse_term()?;
loop {
let tok = try_next!(self, T::LParen | T::LBrack | T::Arrow | T::Dot);
@ -454,7 +455,7 @@ impl<'s> Parser<'s> {
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 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<Expr<'s>> {
fn parse_lambda(&mut self) -> Result<Expr> {
let tok = try_next!(self, T::Backslash | T::Colon);
match tok {
Some(Token { kind: T::Backslash, span, .. }) => {
@ -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<Expr<'s>> {
fn parse_pipeline(&mut self) -> Result<Expr> {
let mut lhs = self.parse_lambda()?;
let mut span = lhs.span;
while try_next!(self, T::Pipe).is_some() {
@ -527,7 +528,7 @@ impl<'s> Parser<'s> {
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) {
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<Expr<'s>> {
fn parse_and(&mut self) -> Result<Expr> {
let mut lhs = self.parse_not()?;
let mut span = lhs.span;
while try_next!(self, T::And).is_some() {
@ -548,7 +549,7 @@ impl<'s> Parser<'s> {
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 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<Expr<'s>> {
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<Expr> {
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<Expr<'s>> {
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<Expr> {
let first = expect!(self, T::Var | T::Global);
let name = expect!(self, T::Identifier);
expect!(self, T::Equal);
let val = self.parse_decl()?;
let val_span = val.span;
let kind = if first.kind == T::Global { E::AssignGlobal } else { E::AssignVar };
Ok(kind(Symbol::get(name.content), b(val))
.span(first.span + val_span))
}
fn parse_fn_decl(&mut self) -> Result<Expr> {
let tok_fn = expect!(self, T::Fn);
let name = try_next!(self, T::Identifier).map(|t| Symbol::get(t.content));
expect!(self, T::LParen);
let args = self.parse_ident_list()?;
expect!(self, T::RParen);
match expect!(self, T::Do | T::Equal).kind {
T::Do => {
let content = self.parse_block()?;
let end = expect!(self, T::End);
Ok(E::FnDef(name, args, b(content)).span(tok_fn.span + end.span))
},
T::Equal => {
let content = self.parse_expr()?;
let span = tok_fn.span + content.span;
Ok(E::FnDef(name, args, b(content)).span(span))
},
_ => unreachable!("parse_fn_decl: guaranteed by try_next!"),
}
}
fn parse_decl(&mut self) -> Result<Expr> {
match self.peek()?.kind {
T::Var | T::Global => self.parse_var_decl(),
T::Fn => self.parse_fn_decl(),
_ => self.parse_assign(),
}
}
fn parse_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() {}
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<Expr<'s>> {
fn parse(mut self) -> Result<Expr> {
let block = self.parse_block()?;
expect!(self, T::Eof);
Ok(block)

View file

@ -132,3 +132,33 @@ pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result<i64, ParseIntErr
_ => parse_int(f, 10),
}
}
pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString {
let mut result = vec![];
let mut begin = 0;
let mut x;
if n < 0 {
result.push('-' as u32 as u8);
begin = 1;
x = (-n) as u64;
} else {
x = n as u64;
}
loop {
let m = x % (radix as u64);
x /= radix as u64;
let mut c = char::from_digit(m as u32, radix).unwrap();
if upper { c.make_ascii_uppercase(); }
result.push(c as u8);
if x == 0 {
break;
}
}
result[begin..].reverse();
LString::from(result)
}

View file

@ -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);

View file

@ -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<Symbol>,
}
#[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 } }
}
}

View file

@ -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:#}")
}

View file

@ -166,14 +166,23 @@ impl Value {
w.write_all(b" }")
},
Self::Function(g) => {
if g.state.is_empty() {
write!(w, "<function({}) {:?}>", g.attrs.arity, Rc::as_ptr(g))
if let Some(name) = g.attrs.name {
write!(w, "<function {}({}) @{:0>12x}>",
name.name(), g.attrs.arity, Rc::as_ptr(g) as usize)
} else {
write!(w, "<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),
}
}

View file

@ -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<HashValue> = 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:#}"),
}

View file

@ -91,7 +91,7 @@ fn get_call_outcome(args: Vec<Value>) -> Result<CallOutcome> {
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()))

View file

@ -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<syn::LitStr>,
}
impl Parse for NativeFuncArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
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 {
let Ok(itemfn) = syn::parse::<ItemFn>(annotated_item.clone()) else {
let Ok(itemfn) = syn::parse::<syn::ItemFn>(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)
}

View file

@ -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<Value>) -> Result<Value> {
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())
}

View file

@ -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)]

419
talc-std/src/format.rs Normal file
View 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());
}

View file

@ -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<Value>) -> Result<Value> {
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) {
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<Value>) -> Result<Value> {
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)
}

View file

@ -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<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
*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<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)]
@ -208,7 +208,7 @@ pub fn take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
*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<Value>) -> Result<Value> {
}
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<Value>) -> Result<Value> {
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)]
@ -329,7 +329,7 @@ pub fn step(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
},
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<Value>) -> Result<Value> {
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(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<Value>) -> Result<Value> {
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<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)]
@ -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)]
@ -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)]
@ -588,7 +588,7 @@ pub fn cartprod(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(Value::from(vec![a_res, b_res]))
};
Ok(NativeFunc::new(Box::new(f), 0).into())
Ok(NativeFunc::new(Box::new(f), 0, symbol!("iter[cart_prod]")).into())
}

View file

@ -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")]

View file

@ -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<Value>) -> Result<Value> {
let [_, x] = unpack_args!(args);
let Value::Int(x) = x else {
throw!(*SYM_TYPE_ERROR, "bin expected integer argument, got {x:#}")
};
Ok(to_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<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
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)]

View file

@ -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<Value>) -> Result<Value> {
}).collect::<Result<Vec<u8>>>()?;
Ok(LString::from(bytes).into())
}
#[native_func(2)]
pub fn _format(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, fstr, fargs] = unpack_args!(args);
let (Value::String(fstr), Value::List(fargs)) = (&fstr, &fargs) else {
throw!(*SYM_TYPE_ERROR, "format expected string and list, got {fstr:#} and {fargs:#}")
};
let mut res = LString::new();
let mut bytes = fstr.bytes();
let mut faidx = 0;
while let Some(b) = bytes.next() {
if b == b'%' {
match bytes.next() {
Some(b'%') => res.push_byte(b'%'),
Some(b @ (b'#' | b'?')) => {
let fargs = fargs.borrow();
let Some(a) = fargs.get(faidx) else {
throw!(*SYM_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())
}

View file

@ -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<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
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())
}
#[native_func(2)]
#[native_func(2, "as")]
pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val, ty] = unpack_args!(args);
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> {
let [_, val] = unpack_args!(args);
Ok(lformat!("{val}").into())
@ -145,6 +153,40 @@ pub fn repr(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(lformat!("{val:#}").into())
}
//
// symbols
//
#[native_func(1)]
pub fn symbol_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::Symbol(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_name: expected symbol")
};
Ok(s.name().into())
}
#[native_func(1)]
pub fn symbol_of(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::String(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
};
Ok(Symbol::get(s.as_ref()).into())
}
#[native_func(1)]
pub fn symbol_exists(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, val] = unpack_args!(args);
let Value::String(s) = val else {
throw!(*SYM_TYPE_ERROR, "symbol_of: expected string")
};
match Symbol::try_get(s.as_ref()) {
Some(s) => Ok(s.into()),
None => Ok(Value::Nil),
}
}
//
// cells
//
@ -183,13 +225,73 @@ pub fn cell_take(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
Ok(cell.replace(Value::Nil))
}
//
// functions
//
#[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 Value::Function(func) = func else {
throw!(*SYM_TYPE_ERROR, "closure_state: value is not a function")
};
let l: Vec<Value> = 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<Value> = f.state.iter()
.map(|v| Value::Cell(v.clone()))
.collect();
Ok(l.into())
}
_ => throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a talc function")
}
}
#[native_func(1)]
pub fn func_arity(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
let Some(attrs) = func.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
};
Ok((attrs.arity as i64).into())
}
#[native_func(1)]
pub fn func_name(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func] = unpack_args!(args);
let Some(attrs) = func.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
};
if let Some(name) = attrs.name {
Ok(name.into())
} else {
Ok(Value::Nil)
}
}
#[native_func(2)]
pub fn apply(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, func, lst] = unpack_args!(args);
if func.func_attrs().is_none() {
throw!(*SYM_TYPE_ERROR, "apply: first argument must be a function, found {func:#}")
}
let Value::List(l) = lst else {
throw!(*SYM_TYPE_ERROR, "apply: second argument must be a list, found {lst:#}")
};
let mut args = l.borrow().clone();
args.insert(0, func.clone());
vm.call_value(func, args)
}
#[native_func(1)]
pub fn compile(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, src] = unpack_args!(args);
let Value::String(src) = src else {
throw!(*SYM_TYPE_ERROR, "compile: argument must be a string, found {src:#}")
};
let src = src.to_str()
.map_err(|e| exception!(*SYM_VALUE_ERROR, "compile: argument must be valid unicode ({e})"))?;
let ast = talc_lang::parser::parse(src)
.map_err(|e| exception!(symbol!("parse_error"), "{e}"))?;
let func = talc_lang::compiler::compile(&ast, None);
Ok(func.into())
}