fixes and destructure
All checks were successful
docs / test (push) Successful in 9s

This commit is contained in:
trimill 2024-12-30 23:56:53 -05:00
parent 825379a38b
commit 668e0712c8
14 changed files with 377 additions and 218 deletions

8
Cargo.lock generated
View file

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

View file

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

View file

@ -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<BinaryOp>, 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<BinaryOp>, 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(())

View file

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

View file

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

91
talc-lang/src/ops.rs Normal file
View file

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

View file

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

View file

@ -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<Expr>),
BinaryOp(BinaryOp, Box<Expr>, Box<Expr>),
Assign(Option<BinaryOp>, Box<LValue>, Box<Expr>),
AssignVar(Symbol, Option<Box<Expr>>),
AssignGlobal(Symbol, Option<Box<Expr>>),
FnDef(Option<Symbol>, Vec<Symbol>, Box<Expr>),
Index(Box<Expr>, Box<Expr>),
@ -119,7 +87,10 @@ pub struct LValue {
#[derive(Debug)]
pub enum LValueKind {
Ident(Symbol),
Var(Symbol),
Global(Symbol),
Index(Box<Expr>, Box<Expr>),
List(Vec<LValue>, Option<usize>),
}
impl LValueKind {
@ -128,17 +99,6 @@ impl LValueKind {
}
}
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)),
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())?;

View file

@ -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<LValue> {
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<Lexer<'s>>,
}
@ -448,10 +480,20 @@ impl<'s> Parser<'s> {
}
fn parse_term(&mut self) -> Result<Expr> {
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<Expr> {
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<Expr> {
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<Expr> {
match self.peek()?.kind {
T::Var | T::Global => self.parse_var_decl(),
T::Fn => self.parse_fn_decl(),
_ => self.parse_assign(),
}

View file

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

View file

@ -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) => {

View file

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

View file

@ -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<Option<FmtIndex>> {
@ -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}"))

View file

@ -92,11 +92,11 @@ pub fn as_(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
(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!(