switch to symbols, update formatting
This commit is contained in:
parent
59f3e320e3
commit
801aaf7b78
23 changed files with 998 additions and 292 deletions
|
@ -1,5 +1,5 @@
|
|||
use clap::{ColorChoice, Parser};
|
||||
use 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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 } }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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:#}")
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:#}"),
|
||||
}
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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
419
talc-std/src/format.rs
Normal file
|
@ -0,0 +1,419 @@
|
|||
use std::io::Write;
|
||||
|
||||
use talc_lang::{exception::{exception, Result}, lstring::{LStr, LString}, parser::{parse_int, to_lstring_radix}, symbol::SYM_TYPE_ERROR, throw, value::{Complex64, Rational64, Value}, Vm};
|
||||
use talc_macros::native_func;
|
||||
|
||||
use crate::{unpack_args, SYM_FORMAT_ERROR};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum FmtIndex {
|
||||
Imm(usize),
|
||||
Arg(usize),
|
||||
NextArg,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Align {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
enum FmtType {
|
||||
#[default]
|
||||
Str,
|
||||
Repr,
|
||||
Hex(bool),
|
||||
Oct,
|
||||
Sex,
|
||||
Bin,
|
||||
Exp(bool),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
enum FmtSign {
|
||||
Plus,
|
||||
Minus,
|
||||
#[default]
|
||||
None
|
||||
}
|
||||
|
||||
// [idx][:[align][sign][width][.prec][ty]]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct FmtCode {
|
||||
arg_idx: Option<usize>,
|
||||
align: Option<(Align, char)>,
|
||||
sign: FmtSign,
|
||||
width: Option<FmtIndex>,
|
||||
prec: Option<FmtIndex>,
|
||||
ty: FmtType,
|
||||
}
|
||||
|
||||
struct FmtParser<'p> {
|
||||
code: &'p LStr,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl<'p> FmtParser<'p> {
|
||||
fn new(code: &'p LStr) -> Self {
|
||||
Self {
|
||||
code,
|
||||
pos: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn next(&mut self) -> Option<char> {
|
||||
let c = &self.code[self.pos..].chars().next()?;
|
||||
self.pos += c.len_utf8();
|
||||
Some(*c)
|
||||
}
|
||||
|
||||
fn peek(&mut self) -> Option<char> {
|
||||
self.code[self.pos..].chars().next()
|
||||
}
|
||||
|
||||
fn try_next(&mut self, chs: &[char]) -> Option<char> {
|
||||
if self.peek().is_some_and(|c| chs.contains(&c)) {
|
||||
self.next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn next_number_str(&mut self) -> &LStr {
|
||||
let start = self.pos;
|
||||
while matches!(self.peek(), Some('0'..='9' | '_')) {
|
||||
self.next();
|
||||
}
|
||||
&self.code[start..self.pos]
|
||||
}
|
||||
|
||||
fn next_usize(&mut self) -> Result<Option<usize>> {
|
||||
let s = self.next_number_str();
|
||||
if s.is_empty() { return Ok(None) }
|
||||
let Ok(i) = parse_int(s, 10) else {
|
||||
throw!(*SYM_FORMAT_ERROR,
|
||||
"code {{{}}}: invalid integer", self.code)
|
||||
};
|
||||
if i < 0 {
|
||||
throw!(*SYM_FORMAT_ERROR,
|
||||
"code {{{}}}: integer may not be negative", self.code)
|
||||
}
|
||||
Ok(Some(i as usize))
|
||||
}
|
||||
|
||||
fn next_fmt_index(&mut self) -> Result<Option<FmtIndex>> {
|
||||
let dollar = self.try_next(&['$']).is_some();
|
||||
let num = self.next_usize()?;
|
||||
match (dollar, num) {
|
||||
(true, None) => Ok(Some(FmtIndex::NextArg)),
|
||||
(true, Some(n)) => Ok(Some(FmtIndex::Arg(n))),
|
||||
(false, None) => Ok(None),
|
||||
(false, Some(n)) => Ok(Some(FmtIndex::Imm(n))),
|
||||
}
|
||||
}
|
||||
|
||||
fn next_align(&mut self) -> Result<Option<(Align, char)>> {
|
||||
let align = match self.peek() {
|
||||
Some('<') => Align::Left,
|
||||
Some('^') => Align::Center,
|
||||
Some('>') => Align::Right,
|
||||
_ => return Ok(None),
|
||||
};
|
||||
self.next();
|
||||
let Some(c) = self.next() else {
|
||||
throw!(*SYM_FORMAT_ERROR,
|
||||
"code {{{}}}: alignment without fill character", self.code)
|
||||
};
|
||||
Ok(Some((align, c)))
|
||||
}
|
||||
|
||||
fn next_sign(&mut self) -> FmtSign {
|
||||
match self.try_next(&['+', '-']) {
|
||||
Some('+') => FmtSign::Plus,
|
||||
Some('-') => FmtSign::Minus,
|
||||
_ => FmtSign::None,
|
||||
}
|
||||
}
|
||||
|
||||
fn next_type(&mut self) -> Result<FmtType> {
|
||||
let ty = match self.peek() {
|
||||
None => return Ok(FmtType::Str),
|
||||
Some('?') => FmtType::Repr,
|
||||
Some('x') => FmtType::Hex(false),
|
||||
Some('X') => FmtType::Hex(true),
|
||||
Some('o') => FmtType::Oct,
|
||||
Some('s') => FmtType::Sex,
|
||||
Some('b') => FmtType::Bin,
|
||||
Some('e') => FmtType::Exp(false),
|
||||
Some('E') => FmtType::Exp(true),
|
||||
_ => throw!(*SYM_FORMAT_ERROR,
|
||||
"code {{{}}}: invalid format type", self.code),
|
||||
};
|
||||
self.next();
|
||||
Ok(ty)
|
||||
}
|
||||
|
||||
fn next_code_end(&mut self) -> Result<()> {
|
||||
match self.peek() {
|
||||
Some(c) => throw!(*SYM_FORMAT_ERROR,
|
||||
"code {{{}}}: expected end of code, found character '{}'", self.code, c),
|
||||
None => Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_format(&mut self) -> Result<FmtCode> {
|
||||
let mut code = FmtCode {
|
||||
arg_idx: self.next_usize()?,
|
||||
..FmtCode::default()
|
||||
};
|
||||
if self.try_next(&[':']).is_none() {
|
||||
self.next_code_end()?;
|
||||
return Ok(code)
|
||||
}
|
||||
code.align = self.next_align()?;
|
||||
code.sign = self.next_sign();
|
||||
code.width = self.next_fmt_index()?;
|
||||
if self.try_next(&['.']).is_some() {
|
||||
code.prec = self.next_fmt_index()?;
|
||||
}
|
||||
code.ty = self.next_type()?;
|
||||
self.next_code_end()?;
|
||||
Ok(code)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_arg<'a>(args: &'a [Value], n: Option<usize>, faidx: &mut usize)
|
||||
-> Result<&'a Value> {
|
||||
let i = match n {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
let i = *faidx;
|
||||
*faidx += 1;
|
||||
i
|
||||
}
|
||||
};
|
||||
if i >= args.len() {
|
||||
throw!(*SYM_FORMAT_ERROR, "missing arguments: expected at least {}", i+1)
|
||||
}
|
||||
Ok(&args[i])
|
||||
}
|
||||
|
||||
fn get_val(args: &[Value], i: FmtIndex, faidx: &mut usize) -> Result<usize> {
|
||||
let v = match i {
|
||||
FmtIndex::Imm(n) => return Ok(n),
|
||||
FmtIndex::Arg(n) => get_arg(args, Some(n), faidx)?,
|
||||
FmtIndex::NextArg => get_arg(args, None, faidx)?,
|
||||
};
|
||||
let Value::Int(v) = v else {
|
||||
throw!(*SYM_FORMAT_ERROR, "expected positive integer argument, found {v:#}")
|
||||
};
|
||||
if *v < 0 {
|
||||
throw!(*SYM_FORMAT_ERROR, "expected positive integer argument, found {v}")
|
||||
}
|
||||
Ok(*v as usize)
|
||||
}
|
||||
|
||||
fn format_float(f: f64, prec: Option<usize>, ty: FmtType, buf: &mut LString, ty_name: &str) -> Result<()> {
|
||||
let res = match (prec, ty) {
|
||||
(None, FmtType::Str) => write!(buf, "{}", Value::Float(f)),
|
||||
(None, FmtType::Repr) => write!(buf, "{:#}", Value::Float(f)),
|
||||
(None, FmtType::Exp(true)) => write!(buf, "{:E}", f),
|
||||
(None, FmtType::Exp(false)) => write!(buf, "{:e}", f),
|
||||
(Some(p), FmtType::Str)
|
||||
| (Some(p), FmtType::Repr) => write!(buf, "{0:.1$}", f, p),
|
||||
(Some(p), FmtType::Exp(true)) => write!(buf, "{0:.1$E}", f, p),
|
||||
(Some(p), FmtType::Exp(false)) => write!(buf, "{0:.1$e}", f, p),
|
||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
||||
};
|
||||
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||
}
|
||||
|
||||
fn format_complex(cx: Complex64, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
||||
format_float(cx.re, prec, ty, buf, "complex")?;
|
||||
if cx.im < 0.0 {
|
||||
buf.push_char('-')
|
||||
} else {
|
||||
buf.push_char('+')
|
||||
}
|
||||
format_float(cx.im.abs(), prec, ty, buf, "complex")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_int(n: i64, prec: Option<usize>, ty: FmtType, buf: &mut LString, ty_name: &str) -> Result<()> {
|
||||
if prec.is_some() {
|
||||
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
||||
}
|
||||
let res = match ty {
|
||||
FmtType::Str => write!(buf, "{}", Value::Int(n)),
|
||||
FmtType::Repr => write!(buf, "{:#}", Value::Int(n)),
|
||||
FmtType::Hex(caps) => write!(buf, "{}", to_lstring_radix(n, 16, caps)),
|
||||
FmtType::Oct => write!(buf, "{}", to_lstring_radix(n, 8, false)),
|
||||
FmtType::Sex => write!(buf, "{}", to_lstring_radix(n, 6, false)),
|
||||
FmtType::Bin => write!(buf, "{}", to_lstring_radix(n, 2, false)),
|
||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for {ty_name}")
|
||||
};
|
||||
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||
}
|
||||
|
||||
fn format_ratio(n: Rational64, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
||||
format_int(*n.numer(), prec, ty, buf, "ratio")?;
|
||||
buf.push_char('/');
|
||||
format_int(*n.denom(), prec, ty, buf, "ratio")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_value(v: &Value, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
||||
if prec.is_some() {
|
||||
throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value")
|
||||
}
|
||||
let res = match ty {
|
||||
FmtType::Str => write!(buf, "{}", v),
|
||||
FmtType::Repr => write!(buf, "{:#}", v),
|
||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for value")
|
||||
};
|
||||
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||
}
|
||||
|
||||
fn format_string(mut s: &LStr, prec: Option<usize>, ty: FmtType, buf: &mut LString) -> Result<()> {
|
||||
if let Some(prec) = prec {
|
||||
s = &s[..prec]
|
||||
}
|
||||
let res = match ty {
|
||||
FmtType::Str => { buf.push_lstr(s); Ok(()) },
|
||||
FmtType::Repr => write!(buf, "{:?}", s),
|
||||
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for string")
|
||||
};
|
||||
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}"))
|
||||
}
|
||||
|
||||
fn pad_str(n: usize, c: char, buf: &mut LString) {
|
||||
for _ in 0..n {
|
||||
buf.push_char(c)
|
||||
}
|
||||
}
|
||||
|
||||
fn format_arg(args: &[Value], faidx: &mut usize, code: &LStr, res: &mut LString)
|
||||
-> Result<()> {
|
||||
if !code.is_utf8() {
|
||||
throw!(*SYM_FORMAT_ERROR, "code {{{}}}: code contains invalid UTF-8", code)
|
||||
}
|
||||
let fmtcode = FmtParser::new(code).read_format()?;
|
||||
|
||||
let mut buf = LString::new();
|
||||
let fmt_arg = get_arg(args, fmtcode.arg_idx, faidx)?;
|
||||
let prec_val = fmtcode.prec.map(|i| get_val(args, i, faidx)).transpose()?;
|
||||
let width_val = fmtcode.width.map(|i| get_val(args, i, faidx)).transpose()?;
|
||||
|
||||
match fmt_arg {
|
||||
Value::Int(n) => format_int(*n, prec_val, fmtcode.ty, &mut buf, "integer")?,
|
||||
Value::Ratio(n) => format_ratio(*n, prec_val, fmtcode.ty, &mut buf)?,
|
||||
Value::Float(f) => format_float(*f, prec_val, fmtcode.ty, &mut buf, "float")?,
|
||||
Value::Complex(n) => format_complex(*n, prec_val, fmtcode.ty, &mut buf)?,
|
||||
Value::String(s) => format_string(s, prec_val, fmtcode.ty, &mut buf)?,
|
||||
v => format_value(v, prec_val, fmtcode.ty, &mut buf)?,
|
||||
}
|
||||
|
||||
let (sign, final_buf) = match fmtcode.sign {
|
||||
FmtSign::Plus => match buf.byte_get(0) {
|
||||
Some(b'+') => ("+", &buf[1..]),
|
||||
Some(b'-') => ("-", &buf[1..]),
|
||||
_ => ("+", &buf[..]),
|
||||
}
|
||||
FmtSign::Minus => match buf.byte_get(0) {
|
||||
Some(b'-') => ("-", &buf[1..]),
|
||||
_ => ("", &buf[..]),
|
||||
}
|
||||
FmtSign::None => ("", &buf[..])
|
||||
};
|
||||
res.push_str(sign);
|
||||
|
||||
|
||||
if let Some(w) = width_val {
|
||||
let w = w.saturating_sub(final_buf.len() + sign.len());
|
||||
match fmtcode.align {
|
||||
Some((Align::Left, c)) => {
|
||||
res.push_lstr(final_buf);
|
||||
pad_str(w, c, res);
|
||||
}
|
||||
Some((Align::Center, c)) => {
|
||||
pad_str((w+1)/2, c, res);
|
||||
res.push_lstr(final_buf);
|
||||
pad_str(w/2, c, res);
|
||||
}
|
||||
Some((Align::Right, c)) => {
|
||||
pad_str(w, c, res);
|
||||
res.push_lstr(final_buf);
|
||||
}
|
||||
None => {
|
||||
let c = if matches!(fmt_arg,
|
||||
Value::Int(_)
|
||||
| Value::Float(_)
|
||||
| Value::Ratio(_)
|
||||
| Value::Complex(_)
|
||||
) && fmtcode.sign != FmtSign::None {
|
||||
'0'
|
||||
} else {
|
||||
' '
|
||||
};
|
||||
pad_str(w, c, res);
|
||||
res.push_lstr(final_buf);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res.push_lstr(final_buf);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[native_func(2, "fmt")]
|
||||
pub fn fmt_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
|
||||
let [_, fstr, fargs] = unpack_args!(args);
|
||||
let (Value::String(fstr), Value::List(fargs)) = (&fstr, &fargs) else {
|
||||
throw!(*SYM_TYPE_ERROR, "format expected string and list, got {fstr:#} and {fargs:#}")
|
||||
};
|
||||
let mut res = LString::new();
|
||||
let mut faidx = 0;
|
||||
let mut i = 0;
|
||||
while i < fstr.len() {
|
||||
let b = fstr.byte_at(i);
|
||||
i += 1;
|
||||
if b == b'}' {
|
||||
let Some(b'}') = fstr.byte_get(i) else {
|
||||
throw!(*SYM_FORMAT_ERROR, "unmatched closing brace at byte {}", i-1)
|
||||
};
|
||||
i += 1;
|
||||
res.push_byte(b);
|
||||
}
|
||||
if b != b'{' {
|
||||
res.push_byte(b);
|
||||
continue
|
||||
}
|
||||
if i >= fstr.len() {
|
||||
throw!(*SYM_FORMAT_ERROR, "unclosed format specifier at byte {}", i-1)
|
||||
}
|
||||
if let Some(b'{') = fstr.byte_get(i) {
|
||||
i += 1;
|
||||
res.push_byte(b);
|
||||
continue
|
||||
}
|
||||
let start = i;
|
||||
while i < fstr.len() && fstr.byte_at(i) != b'}' {
|
||||
i += 1;
|
||||
}
|
||||
if i == fstr.len() {
|
||||
throw!(*SYM_FORMAT_ERROR, "unclosed format specifier at byte {}", start-1)
|
||||
}
|
||||
let code = &fstr[start..i];
|
||||
i += 1;
|
||||
format_arg(&fargs.borrow(), &mut faidx, code, &mut res)?;
|
||||
}
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
pub fn load(vm: &mut Vm) {
|
||||
vm.set_global_name("fmt", fmt_().into());
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
use std::{io::{BufRead, Write}, os::unix::ffi::OsStrExt, time::{SystemTime, UNIX_EPOCH}};
|
||||
use std::{io::{BufRead, Write}, os::unix::ffi::OsStrExt, sync::Mutex, time::{SystemTime, UNIX_EPOCH}};
|
||||
|
||||
use talc_lang::{exception::{throw, Result}, lstring::LString, symbol::{SYM_TYPE_ERROR, SYM_VALUE_ERROR}, value::Value, vmcall, Vm};
|
||||
use talc_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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue