diff --git a/Cargo.lock b/Cargo.lock index 293058c..a72479b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -326,9 +326,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -445,9 +445,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" -version = "2.0.91" +version = "2.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" +checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" dependencies = [ "proc-macro2", "quote", diff --git a/talc-lang/src/chunk.rs b/talc-lang/src/chunk.rs index 5f994e5..107e115 100644 --- a/talc-lang/src/chunk.rs +++ b/talc-lang/src/chunk.rs @@ -1,5 +1,5 @@ use crate::{ - parser::ast::{BinaryOp, UnaryOp}, + ops::{BinaryOp, UnaryOp}, symbol::Symbol, value::Value, }; @@ -139,8 +139,11 @@ pub enum Instruction { GrowTable(u8), ExtendTable, + ListDestructure(u8, u8, bool), + Index, StoreIndex, + StoreIndexOp(BinaryOp), Jump(Arg24), JumpTrue(Arg24), @@ -195,16 +198,20 @@ impl std::fmt::Display for Instruction { Self::DupTwo => write!(f, "duptwo"), Self::Drop => write!(f, "drop"), Self::Swap => write!(f, "swap"), - Self::UnaryOp(o) => write!(f, "unary {o:?}"), - Self::BinaryOp(o) => write!(f, "binary {o:?}"), + Self::UnaryOp(o) => write!(f, "unary {o}"), + Self::BinaryOp(o) => write!(f, "binary {o}"), Self::NewList(n) => write!(f, "newlist #{n}"), Self::GrowList(n) => write!(f, "growlist #{n}"), Self::ExtendList => write!(f, "extendlist"), Self::NewTable(n) => write!(f, "newtable #{n}"), Self::GrowTable(n) => write!(f, "growtable #{n}"), + Self::ListDestructure(l, r, interp) => { + write!(f, "listdestructure #{l},#{r},{interp}") + } Self::ExtendTable => write!(f, "extendtable"), Self::Index => write!(f, "index"), Self::StoreIndex => write!(f, "storeindex"), + Self::StoreIndexOp(o) => write!(f, "storeindexop {o}"), Self::Jump(a) => write!(f, "jump @{}", usize::from(a)), Self::JumpTrue(a) => write!(f, "jumptrue @{}", usize::from(a)), Self::JumpFalse(a) => write!(f, "jumpfalse @{}", usize::from(a)), diff --git a/talc-lang/src/compiler.rs b/talc-lang/src/compiler.rs index 7399882..1972d7d 100644 --- a/talc-lang/src/compiler.rs +++ b/talc-lang/src/compiler.rs @@ -3,14 +3,15 @@ use std::collections::HashMap; use std::rc::Rc; use crate::chunk::{Arg24, Catch, Chunk, Instruction as I}; +use crate::ops::BinaryOp; use crate::parser::ast::{ - BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind, ListItem, TableItem, + CatchBlock, Expr, ExprKind, LValue, LValueKind, ListItem, TableItem, }; use crate::parser::Pos; -use crate::prelude::*; use crate::symbol::{Symbol, SYM_REPL, SYM_SELF}; use crate::value::function::{FuncAttrs, Function}; use crate::value::Value; +use crate::{prelude::*, throw}; #[derive(Clone, Debug)] pub struct CompileError { @@ -222,6 +223,7 @@ impl<'a> Compiler<'a> { ) => { // final side-effectless instruction instrs.pop().unwrap(); + return } Some( I::NewLocal @@ -236,12 +238,12 @@ impl<'a> Compiler<'a> { let i = self.chunk.instrs.pop().unwrap(); self.chunk.instrs.pop().unwrap(); self.chunk.instrs.push(i); + return } } - _ => { - self.emit(I::Drop); - } + _ => (), } + self.emit(I::Drop); } fn ip(&self) -> usize { @@ -370,6 +372,10 @@ impl<'a> Compiler<'a> { Ok(()) } + fn load_global(&mut self, name: Symbol) { + self.emit(I::LoadGlobal(Arg24::from_symbol(name))); + } + fn declare_local(&mut self, name: Symbol) -> usize { let local = VarKind::Local(self.local_count); self.local_count += 1; @@ -452,6 +458,11 @@ impl<'a> Compiler<'a> { } ExprKind::Literal(v) => self.expr_literal(v), ExprKind::Ident(ident) => self.load_var(*ident)?, + ExprKind::Var(_) => throw!( + span.start, + "identifer qualified with var can only be used as an lvalue" + ), + ExprKind::Global(ident) => self.load_global(*ident), ExprKind::UnaryOp(o, a) => { self.expr(a)?; self.emit(I::UnaryOp(*o)); @@ -462,24 +473,6 @@ impl<'a> Compiler<'a> { self.emit(I::BinaryOp(*o)); } ExprKind::Assign(o, lv, a) => self.expr_assign(*o, lv, a)?, - ExprKind::AssignVar(name, a) => { - if let Some(a) = a { - self.expr(a)?; - } else { - self.emit(I::Nil); - } - self.emit(I::Dup); - self.assign_local(*name); - } - ExprKind::AssignGlobal(name, a) => { - if let Some(a) = a { - self.expr(a)?; - self.emit(I::Dup); - self.assign_global(*name); - } else { - self.declare_global(*name); - } - } ExprKind::List(xs) => self.expr_list(xs)?, ExprKind::Table(xs) => self.expr_table(xs)?, ExprKind::Index(ct, idx) => { @@ -855,34 +848,84 @@ impl<'a> Compiler<'a> { self.emit(I::Const(Arg24::from_usize(n))); } - fn expr_assign(&mut self, o: Option, lv: &LValue, a: &Expr) -> Result<()> { - match (&lv.kind, o) { - (LValueKind::Ident(i), None) => { - self.expr(a)?; + fn lvalue(&mut self, lv: &LValue) -> Result<()> { + match &lv.kind { + LValueKind::Ident(i) => { self.emit(I::Dup); self.store_var(*i); } - (LValueKind::Ident(i), Some(o)) => { + LValueKind::Var(i) => { + self.emit(I::Dup); + self.assign_local(*i); + } + LValueKind::Global(i) => { + self.emit(I::Dup); + self.assign_global(*i); + } + LValueKind::Index(ct, i) => { + self.expr(ct)?; + self.expr(i)?; + self.emit(I::StoreIndex); + } + LValueKind::List(ls, i) => { + let Ok(len) = u8::try_from(ls.len()) else { + throw!(lv.span.start, "list destructure is too large"); + }; + + self.emit(I::Dup); + + let i = i.map(|n| u8::try_from(n).expect("interp index past end of list?")); + + self.emit(I::ListDestructure( + i.unwrap_or(len), + i.map(|n| len - n - 1).unwrap_or(0), + i.is_some(), + )); + + for lv in ls { + self.lvalue(lv)?; + self.emit_discard(); + } + } + } + Ok(()) + } + + fn expr_assign(&mut self, o: Option, lv: &LValue, a: &Expr) -> Result<()> { + let Some(o) = o else { + self.expr(a)?; + self.lvalue(lv)?; + return Ok(()) + }; + match &lv.kind { + LValueKind::Ident(i) => { self.load_var(*i)?; self.expr(a)?; self.emit(I::BinaryOp(o)); self.emit(I::Dup); self.store_var(*i); } - (LValueKind::Index(ct, i), None) => { - self.expr(ct)?; - self.expr(i)?; - self.expr(a)?; - self.emit(I::StoreIndex); + LValueKind::Var(_) => { + throw!(lv.span.start, "cannot compound assign to explicit local") } - (LValueKind::Index(ct, i), Some(o)) => { - self.expr(ct)?; - self.expr(i)?; - self.emit(I::DupTwo); - self.emit(I::Index); + LValueKind::Global(i) => { + self.load_global(*i); self.expr(a)?; self.emit(I::BinaryOp(o)); - self.emit(I::StoreIndex); + self.emit(I::Dup); + self.assign_global(*i); + } + LValueKind::Index(ct, i) => { + self.expr(a)?; + self.expr(ct)?; + self.expr(i)?; + self.emit(I::StoreIndexOp(o)); + } + LValueKind::List(..) => { + throw!( + lv.span.start, + "cannot compound assign to a list destructure" + ) } } Ok(()) diff --git a/talc-lang/src/lib.rs b/talc-lang/src/lib.rs index e044fbf..7f76322 100644 --- a/talc-lang/src/lib.rs +++ b/talc-lang/src/lib.rs @@ -9,6 +9,7 @@ pub mod compiler; pub mod exception; pub mod lstring; pub mod number; +pub mod ops; pub mod optimize; pub mod parser; pub mod serial; diff --git a/talc-lang/src/lstring.rs b/talc-lang/src/lstring.rs index 493b96b..98b8403 100644 --- a/talc-lang/src/lstring.rs +++ b/talc-lang/src/lstring.rs @@ -151,30 +151,40 @@ pub struct LStr { inner: [u8], } -impl fmt::Debug for LStr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_char('"')?; +impl LStr { + pub fn to_escaped(&self) -> String { + let mut s = String::new(); + s += "\""; let mut bytes = &self.inner; while let Some((new_bytes, res)) = next_codepoint(bytes) { bytes = new_bytes; match res { - Ok('"') => f.write_str("\\\"")?, - Ok('\\') => f.write_str("\\\\")?, - Ok('\x00') => f.write_str("\\0")?, - Ok('\x07') => f.write_str("\\a")?, - Ok('\x08') => f.write_str("\\b")?, - Ok('\x09') => f.write_str("\\t")?, - Ok('\x0a') => f.write_str("\\n")?, - Ok('\x0b') => f.write_str("\\v")?, - Ok('\x0c') => f.write_str("\\f")?, - Ok('\x0d') => f.write_str("\\r")?, - Ok('\x1b') => f.write_str("\\e")?, - Ok(c) if c.is_control() => write!(f, "\\u{{{:x}}}", c as u32)?, - Ok(c) => f.write_char(c)?, - Err(b) => write!(f, "\\x{b:02x}")?, + Ok('"') => s += "\\\"", + Ok('\\') => s += "\\\\", + Ok('\x00') => s += "\\0", + Ok('\x07') => s += "\\a", + Ok('\x08') => s += "\\b", + Ok('\x09') => s += "\\t", + Ok('\x0a') => s += "\\n", + Ok('\x0b') => s += "\\v", + Ok('\x0c') => s += "\\f", + Ok('\x0d') => s += "\\r", + Ok('\x1b') => s += "\\e", + Ok(c) if c.is_control() => { + write!(s, "\\u{{{:x}}}", c as u32).expect("failed to write to string"); + } + Ok(c) => s.push(c), + Err(b) => write!(s, "\\x{b:02x}").expect("failed to write to string"), } } - f.write_char('"') + s += "\""; + s + } +} + +impl fmt::Debug for LStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_escaped()) } } diff --git a/talc-lang/src/ops.rs b/talc-lang/src/ops.rs new file mode 100644 index 0000000..c6729c8 --- /dev/null +++ b/talc-lang/src/ops.rs @@ -0,0 +1,91 @@ +use std::fmt; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BinaryOp { + Add, + Sub, + Mul, + Div, + Mod, + Pow, + IntDiv, + Shr, + Shl, + BitAnd, + BitXor, + BitOr, + Eq, + Ne, + Gt, + Lt, + Ge, + Le, + Concat, + Append, + Range, + RangeIncl, +} + +impl BinaryOp { + pub fn name(self) -> &'static str { + match self { + Self::Add => "add", + Self::Sub => "sub", + Self::Mul => "mul", + Self::Div => "div", + Self::Mod => "mod", + Self::Pow => "pow", + Self::IntDiv => "int_div", + Self::Shr => "shr", + Self::Shl => "shl", + Self::BitAnd => "bit_and", + Self::BitXor => "bit_xor", + Self::BitOr => "bit_or", + Self::Eq => "eq", + Self::Ne => "ne", + Self::Gt => "gt", + Self::Lt => "lt", + Self::Ge => "ge", + Self::Le => "le", + Self::Concat => "concat", + Self::Append => "append", + Self::Range => "range", + Self::RangeIncl => "range_incl", + } + } +} + +impl fmt::Display for BinaryOp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum UnaryOp { + Neg, + Not, + BitNot, + RangeFrom, + RangeTo, + RangeToIncl, +} + +impl UnaryOp { + pub fn name(self) -> &'static str { + match self { + UnaryOp::Neg => "neg", + UnaryOp::Not => "not", + UnaryOp::BitNot => "bit_not", + UnaryOp::RangeFrom => "range_from", + UnaryOp::RangeTo => "range_to", + UnaryOp::RangeToIncl => "range_to_incl", + } + } +} + +impl fmt::Display for UnaryOp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } +} diff --git a/talc-lang/src/optimize.rs b/talc-lang/src/optimize.rs index d417c83..2e767cd 100644 --- a/talc-lang/src/optimize.rs +++ b/talc-lang/src/optimize.rs @@ -52,6 +52,8 @@ fn optimize_ex_with(expr: &mut Expr, state: OptState) { match &mut expr.kind { ExprKind::Literal(_) => (), ExprKind::Ident(_) => (), + ExprKind::Var(_) => (), + ExprKind::Global(_) => (), ExprKind::UnaryOp(o, e) => { optimize_ex(e); if let Some(a) = e.value() { @@ -73,16 +75,6 @@ fn optimize_ex_with(expr: &mut Expr, state: OptState) { optimize_lv(l); optimize_ex(r); } - ExprKind::AssignVar(_, e) => { - if let Some(e) = e { - optimize_ex(e); - } - } - ExprKind::AssignGlobal(_, e) => { - if let Some(e) = e { - optimize_ex(e); - } - } ExprKind::FnDef(_, _, e) => { optimize_ex_with(e, OptState::ret(true)); } @@ -227,10 +219,17 @@ fn optimize_ex_with(expr: &mut Expr, state: OptState) { fn optimize_lv(e: &mut LValue) { match &mut e.kind { LValueKind::Ident(_) => (), + LValueKind::Var(_) => (), + LValueKind::Global(_) => (), LValueKind::Index(l, r) => { optimize_ex(l); optimize_ex(r); } + LValueKind::List(ls, _) => { + for l in ls { + optimize_lv(l); + } + } } } diff --git a/talc-lang/src/parser/ast.rs b/talc-lang/src/parser/ast.rs index b6522d1..d48fc6d 100644 --- a/talc-lang/src/parser/ast.rs +++ b/talc-lang/src/parser/ast.rs @@ -1,45 +1,13 @@ use core::fmt; -use crate::{symbol::Symbol, value::Value}; +use crate::{ + ops::{BinaryOp, UnaryOp}, + symbol::Symbol, + value::Value, +}; use super::Span; -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum BinaryOp { - Add, - Sub, - Mul, - Div, - Mod, - Pow, - IntDiv, - Shr, - Shl, - BitAnd, - BitXor, - BitOr, - Eq, - Ne, - Gt, - Lt, - Ge, - Le, - Concat, - Append, - Range, - RangeIncl, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum UnaryOp { - Neg, - Not, - BitNot, - RangeFrom, - RangeTo, - RangeToIncl, -} - #[derive(Debug)] pub struct Expr { pub span: Span, @@ -50,13 +18,13 @@ pub struct Expr { pub enum ExprKind { Literal(Value), Ident(Symbol), + Var(Symbol), + Global(Symbol), UnaryOp(UnaryOp, Box), BinaryOp(BinaryOp, Box, Box), Assign(Option, Box, Box), - AssignVar(Symbol, Option>), - AssignGlobal(Symbol, Option>), FnDef(Option, Vec, Box), Index(Box, Box), @@ -119,7 +87,10 @@ pub struct LValue { #[derive(Debug)] pub enum LValueKind { Ident(Symbol), + Var(Symbol), + Global(Symbol), Index(Box, Box), + List(Vec, Option), } impl LValueKind { @@ -128,17 +99,6 @@ impl LValueKind { } } -impl LValue { - pub fn from_expr(e: Expr) -> Option { - let Expr { span, kind } = e; - match kind { - ExprKind::Ident(i) => Some(LValueKind::Ident(i).span(span)), - ExprKind::Index(l, r) => Some(LValueKind::Index(l, r).span(span)), - _ => None, - } - } -} - impl CatchBlock { pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result { write!(w, "{0: >1$}catch", "", depth * 2)?; @@ -168,11 +128,23 @@ impl LValue { let depth = depth + 1; match &self.kind { LValueKind::Ident(n) => writeln!(w, "${}", n.name()), + LValueKind::Var(n) => writeln!(w, "var ${}", n.name()), + LValueKind::Global(n) => writeln!(w, "global ${}", n.name()), LValueKind::Index(l, r) => { writeln!(w, "index")?; l.write_to(w, depth)?; r.write_to(w, depth) } + LValueKind::List(ls, i) => { + match i { + Some(n) => writeln!(w, "list ..#{n}")?, + None => writeln!(w, "list")?, + } + for item in ls { + item.write_to(w, depth)?; + } + Ok(()) + } } } } @@ -190,38 +162,26 @@ impl Expr { match &self.kind { ExprKind::Literal(val) => writeln!(w, "{val}"), ExprKind::Ident(n) => writeln!(w, "${}", n.name()), + ExprKind::Var(n) => writeln!(w, "var ${}", n.name()), + ExprKind::Global(n) => writeln!(w, "global ${}", n.name()), ExprKind::UnaryOp(op, e) => { - writeln!(w, "uop {op:?}")?; + writeln!(w, "uop {op}")?; e.write_to(w, depth) } ExprKind::BinaryOp(op, l, r) => { - writeln!(w, "bop {op:?}")?; + writeln!(w, "bop {op}")?; l.write_to(w, depth)?; r.write_to(w, depth) } ExprKind::Assign(op, l, r) => { if let Some(op) = op { - writeln!(w, "asgn {op:?}")?; + writeln!(w, "asgn {op}")?; } else { writeln!(w, "asgn =")?; } l.write_to(w, depth)?; r.write_to(w, depth) } - ExprKind::AssignVar(l, r) => { - writeln!(w, "var {}", l.name())?; - if let Some(r) = r { - r.write_to(w, depth)?; - } - Ok(()) - } - ExprKind::AssignGlobal(l, r) => { - writeln!(w, "global {}", l.name())?; - if let Some(r) = r { - r.write_to(w, depth)?; - } - Ok(()) - } ExprKind::FnDef(n, p, b) => { if let Some(n) = n { write!(w, "fndef ${}", n.name())?; diff --git a/talc-lang/src/parser/parser.rs b/talc-lang/src/parser/parser.rs index d6a8164..fe2d501 100644 --- a/talc-lang/src/parser/parser.rs +++ b/talc-lang/src/parser/parser.rs @@ -1,13 +1,14 @@ use std::iter::Peekable; use crate::{ + ops::{BinaryOp, UnaryOp}, parser::ast::TableItem, symbol::{Symbol, SYM_DOLLAR_SIGN}, value::Value, }; use super::{ - ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, ListItem, UnaryOp}, + ast::{CatchBlock, Expr, ExprKind, LValue, LValueKind, ListItem}, parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span, SpanParserError, Token, TokenKind, }; @@ -186,6 +187,37 @@ impl TokenKind { } } +impl LValue { + pub fn from_expr(e: Expr) -> Result { + let Expr { span, kind } = e; + match kind { + ExprKind::Ident(i) => Ok(LValueKind::Ident(i).span(span)), + ExprKind::Var(i) => Ok(LValueKind::Var(i).span(span)), + ExprKind::Global(i) => Ok(LValueKind::Global(i).span(span)), + ExprKind::Index(l, r) => Ok(LValueKind::Index(l, r).span(span)), + ExprKind::List(items) => { + let mut lvals = Vec::new(); + let mut interp = None; + for (i, item) in items.into_iter().enumerate() { + match item { + ListItem::Item(expr) => lvals.push(LValue::from_expr(*expr)?), + ListItem::Interpolate(expr) if interp.is_some() => throw!( + expr.span, + "list destructure with multiple interpolations" + ), + ListItem::Interpolate(expr) => { + interp = Some(i); + lvals.push(LValue::from_expr(*expr)?); + } + } + } + Ok(LValueKind::List(lvals, interp).span(span)) + } + _ => throw!(span, "invalid lvalue for assignment"), + } + } +} + struct Parser<'s> { lexer: Peekable>, } @@ -448,10 +480,20 @@ impl<'s> Parser<'s> { } fn parse_term(&mut self) -> Result { - if let Some(tok) = try_next!(self, T::Identifier) { - Ok(E::Ident(Symbol::get(tok.content)).span(tok.span)) - } else { - self.parse_term_not_ident() + let Some(tok) = try_next!(self, T::Identifier | T::Var | T::Global) else { + return self.parse_term_not_ident() + }; + match tok.kind { + T::Identifier => Ok(E::Ident(Symbol::get(tok.content)).span(tok.span)), + T::Global => { + let ident = expect!(self, T::Identifier); + Ok(E::Global(Symbol::get(ident.content)).span(tok.span + ident.span)) + } + T::Var => { + let ident = expect!(self, T::Identifier); + Ok(E::Var(Symbol::get(ident.content)).span(tok.span + ident.span)) + } + _ => unreachable!("guarenteed by try_next"), } } @@ -601,9 +643,7 @@ impl<'s> Parser<'s> { let lhs = self.parse_pipeline()?; let lhs_span = lhs.span; if let Some(op) = self.peek()?.kind.assign_op() { - let Some(lval) = LValue::from_expr(lhs) else { - throw!(lhs_span, "invalid lvalue for assignment") - }; + let lval = LValue::from_expr(lhs)?; self.next()?; let rhs = self.parse_decl()?; let rhs_span = rhs.span; @@ -613,23 +653,6 @@ impl<'s> Parser<'s> { } } - fn parse_var_decl(&mut self) -> Result { - let first = expect!(self, T::Var | T::Global); - let kind = if first.kind == T::Global { - E::AssignGlobal - } else { - E::AssignVar - }; - let name = expect!(self, T::Identifier); - if try_next!(self, T::Equal).is_some() { - let val = self.parse_decl()?; - let val_span = val.span; - Ok(kind(Symbol::get(name.content), Some(b(val))).span(first.span + val_span)) - } else { - Ok(kind(Symbol::get(name.content), None).span(first.span + name.span)) - } - } - fn parse_fn_decl(&mut self) -> Result { let tok_fn = expect!(self, T::Fn); let name = try_next!(self, T::Identifier).map(|t| Symbol::get(t.content)); @@ -653,7 +676,6 @@ impl<'s> Parser<'s> { fn parse_decl(&mut self) -> Result { match self.peek()?.kind { - T::Var | T::Global => self.parse_var_decl(), T::Fn => self.parse_fn_decl(), _ => self.parse_assign(), } diff --git a/talc-lang/src/parser/util.rs b/talc-lang/src/parser/util.rs index 576dacb..ce2dccd 100644 --- a/talc-lang/src/parser/util.rs +++ b/talc-lang/src/parser/util.rs @@ -1,7 +1,12 @@ use core::fmt; -use std::num::{ParseFloatError, ParseIntError}; +use std::num::ParseFloatError; -use crate::lstring::{LStr, LString}; +use num::{bigint::ParseBigIntError, Num}; + +use crate::{ + lstring::{LStr, LString}, + number::Int, +}; use super::Span; @@ -136,17 +141,17 @@ pub fn parse_float<'a, S: Into<&'a LStr>>(f: S) -> Result s.parse() } -pub fn parse_int<'a, S: Into<&'a LStr>>(f: S, radix: u32) -> Result { +pub fn parse_int<'a, S: Into<&'a LStr>>(f: S, radix: u32) -> Result { let mut s = String::new(); for c in f.into().chars() { if c != '_' { s.push(c); } } - i64::from_str_radix(&s, radix) + Int::from_str_radix(&s, radix) } -pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result { +pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result { let f = f.into(); match f.chars().nth(1) { Some('x') => parse_int(&f[2..], 16), @@ -156,33 +161,3 @@ pub fn parse_int_literal<'a, S: Into<&'a LStr>>(f: S) -> Result parse_int(f, 10), } } - -pub fn to_lstring_radix(n: i64, radix: u32, upper: bool) -> LString { - let mut result = vec![]; - let mut begin = 0; - - let mut x; - if n < 0 { - result.push('-' as u32 as u8); - begin = 1; - x = (-n) as u64; - } else { - x = n as u64; - } - - loop { - let m = x % (radix as u64); - x /= radix as u64; - - let mut c = char::from_digit(m as u32, radix).unwrap(); - if upper { - c.make_ascii_uppercase(); - } - result.push(c as u8); - if x == 0 { - break - } - } - result[begin..].reverse(); - LString::from(result) -} diff --git a/talc-lang/src/value/mod.rs b/talc-lang/src/value/mod.rs index 30b4917..f8733d3 100644 --- a/talc-lang/src/value/mod.rs +++ b/talc-lang/src/value/mod.rs @@ -96,6 +96,15 @@ impl Value { recur: &mut Vec<*const ()>, ) -> io::Result<()> { use std::io::Write; + + fn write_float(w: &mut LString, f: f64) -> io::Result<()> { + write!(w, "{f:.}")?; + if f.fract() == 0.0 { + write!(w, ".0")?; + } + Ok(()) + } + match self { Self::Nil => write!(w, "nil"), Self::Bool(b) => write!(w, "{b}"), @@ -105,20 +114,24 @@ impl Value { if name.is_identifier() { w.extend(name.as_bytes()); } else { - write!(w, "{name:?}")?; + write!(w, "{}", name.to_escaped())?; } Ok(()) } Self::Range(r) => write!(w, "{r}"), Self::Int(i) => write!(w, "{i}"), - Self::Float(x) => write!(w, "{x:?}"), + Self::Float(x) => write_float(w, *x), Self::Ratio(r) => write!(w, "{}/{}", r.numer(), r.denom()), Self::Complex(z) => { - write!(w, "{:?}", z.re())?; - if z.im() >= 0.0 || z.im.is_nan() { - w.push_byte(b'+'); + write_float(w, z.re())?; + if z.im().signum() > 0.0 || z.im.is_nan() { + w.push_bytes(b" + "); + } else { + w.push_bytes(b" - "); } - write!(w, "{:?}i", z.im()) + write_float(w, z.im().abs())?; + w.push_byte(b'i'); + Ok(()) } Self::Cell(v) if repr => { if recur.contains(&(v.as_ptr() as _)) { @@ -139,7 +152,7 @@ impl Value { recur.pop(); Ok(()) } - Self::String(s) if repr => write!(w, "{s:?}"), + Self::String(s) if repr => write!(w, "{}", s.to_escaped()), Self::String(s) => w.write_all(s.as_bytes()), Self::List(l) => { diff --git a/talc-lang/src/vm.rs b/talc-lang/src/vm.rs index 2a463af..d291e12 100644 --- a/talc-lang/src/vm.rs +++ b/talc-lang/src/vm.rs @@ -10,9 +10,10 @@ use crate::{ chunk::Instruction, exception::{throw, Exception, Result}, lstring::{LStr, LString}, - parser::ast::{BinaryOp, UnaryOp}, + ops::{BinaryOp, UnaryOp}, symbol::{ Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR, + SYM_VALUE_ERROR, }, value::{ function::{FuncAttrs, Function, NativeFunc}, @@ -486,20 +487,57 @@ impl Vm { drop(table_ref); self.push(Value::Table(table)); } + I::ListDestructure(l, r, interp) => { + let lst = self.pop(); + let Value::List(lst) = lst else { + throw!(*SYM_TYPE_ERROR, "cannot destructure {lst:#} into list") + }; + let lst_ref = lst.borrow(); + let lst_len = lst_ref.len(); + + let (l, r) = (l as usize, r as usize); + + if lst_len < l + r || (lst_len != l + r && !interp) { + throw!(*SYM_VALUE_ERROR, "wrong number of items to destructure"); + } + + for i in ((lst_len - r)..lst_len).rev() { + self.push(lst_ref[i].clone()); + } + + if interp { + let slice = &lst_ref[l..(lst_len - r)]; + self.push(Value::from(slice.to_vec())); + } + + for i in (0..l).rev() { + self.push(lst_ref[i].clone()); + } + } // [ct, idx] -> [ct[idx]] I::Index => { let idx = self.pop(); let ct = self.pop(); self.push(ct.index(idx)?); } - // [ct, idx, v] -> [v], ct[idx] = v + // [v, ct, idx] -> [v], ct[idx] = v I::StoreIndex => { - let v = self.pop(); let idx = self.pop(); let ct = self.pop(); + let v = self.pop(); ct.store_index(self, idx, v.clone())?; self.push(v); } + // [v, ct, idx] -> [ct[idx] op v], ct[idx] op= v + I::StoreIndexOp(op) => { + let idx = self.pop(); + let ct = self.pop(); + let v = self.pop(); + let w = ct.index(idx.clone())?; + let o = binary_op(op, w, v)?; + ct.store_index(self, idx, o.clone())?; + self.push(o); + } // ip = n I::Jump(n) => { self.check_interrupt()?; diff --git a/talc-std/src/format.rs b/talc-std/src/format.rs index c4f98e9..e91368d 100644 --- a/talc-std/src/format.rs +++ b/talc-std/src/format.rs @@ -104,14 +104,14 @@ impl<'p> FmtParser<'p> { let Ok(i) = parse_int(s, 10) else { throw!(*SYM_FORMAT_ERROR, "code {{{}}}: invalid integer", self.code) }; - if i < 0 { + let Some(n) = i.to_usize() else { throw!( *SYM_FORMAT_ERROR, "code {{{}}}: integer may not be negative", self.code ) - } - Ok(Some(i as usize)) + }; + Ok(Some(n)) } fn next_fmt_index(&mut self) -> Result> { @@ -351,7 +351,7 @@ fn format_string( buf.push_lstr(s); Ok(()) } - FmtType::Repr => write!(buf, "{:?}", s), + FmtType::Repr => write!(buf, "{}", s.to_escaped()), _ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for string"), }; res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}")) diff --git a/talc-std/src/value.rs b/talc-std/src/value.rs index 91e0b25..b6ce95f 100644 --- a/talc-std/src/value.rs +++ b/talc-std/src/value.rs @@ -92,11 +92,11 @@ pub fn as_(_: &mut Vm, args: Vec) -> Result { (Value::Float(x), b"complex") => Ok(Value::Complex(x.into())), (Value::String(s), b"int") => parse_int(s.as_ref(), 10) - .map(i64::into) + .map(Value::from) .map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as integer")), (Value::String(s), b"float") => parse_float(s.as_ref()) - .map(f64::into) + .map(Value::from) .map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as float")), (v, _) => throw!(