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]] [[package]]
name = "quote" name = "quote"
version = "1.0.37" version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -445,9 +445,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.91" version = "2.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
parser::ast::{BinaryOp, UnaryOp}, ops::{BinaryOp, UnaryOp},
symbol::Symbol, symbol::Symbol,
value::Value, value::Value,
}; };
@ -139,8 +139,11 @@ pub enum Instruction {
GrowTable(u8), GrowTable(u8),
ExtendTable, ExtendTable,
ListDestructure(u8, u8, bool),
Index, Index,
StoreIndex, StoreIndex,
StoreIndexOp(BinaryOp),
Jump(Arg24), Jump(Arg24),
JumpTrue(Arg24), JumpTrue(Arg24),
@ -195,16 +198,20 @@ impl std::fmt::Display for Instruction {
Self::DupTwo => write!(f, "duptwo"), Self::DupTwo => write!(f, "duptwo"),
Self::Drop => write!(f, "drop"), Self::Drop => write!(f, "drop"),
Self::Swap => write!(f, "swap"), Self::Swap => write!(f, "swap"),
Self::UnaryOp(o) => write!(f, "unary {o:?}"), Self::UnaryOp(o) => write!(f, "unary {o}"),
Self::BinaryOp(o) => write!(f, "binary {o:?}"), Self::BinaryOp(o) => write!(f, "binary {o}"),
Self::NewList(n) => write!(f, "newlist #{n}"), Self::NewList(n) => write!(f, "newlist #{n}"),
Self::GrowList(n) => write!(f, "growlist #{n}"), Self::GrowList(n) => write!(f, "growlist #{n}"),
Self::ExtendList => write!(f, "extendlist"), Self::ExtendList => write!(f, "extendlist"),
Self::NewTable(n) => write!(f, "newtable #{n}"), Self::NewTable(n) => write!(f, "newtable #{n}"),
Self::GrowTable(n) => write!(f, "growtable #{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::ExtendTable => write!(f, "extendtable"),
Self::Index => write!(f, "index"), Self::Index => write!(f, "index"),
Self::StoreIndex => write!(f, "storeindex"), Self::StoreIndex => write!(f, "storeindex"),
Self::StoreIndexOp(o) => write!(f, "storeindexop {o}"),
Self::Jump(a) => write!(f, "jump @{}", usize::from(a)), Self::Jump(a) => write!(f, "jump @{}", usize::from(a)),
Self::JumpTrue(a) => write!(f, "jumptrue @{}", usize::from(a)), Self::JumpTrue(a) => write!(f, "jumptrue @{}", usize::from(a)),
Self::JumpFalse(a) => write!(f, "jumpfalse @{}", 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 std::rc::Rc;
use crate::chunk::{Arg24, Catch, Chunk, Instruction as I}; use crate::chunk::{Arg24, Catch, Chunk, Instruction as I};
use crate::ops::BinaryOp;
use crate::parser::ast::{ 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::parser::Pos;
use crate::prelude::*;
use crate::symbol::{Symbol, SYM_REPL, SYM_SELF}; use crate::symbol::{Symbol, SYM_REPL, SYM_SELF};
use crate::value::function::{FuncAttrs, Function}; use crate::value::function::{FuncAttrs, Function};
use crate::value::Value; use crate::value::Value;
use crate::{prelude::*, throw};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CompileError { pub struct CompileError {
@ -222,6 +223,7 @@ impl<'a> Compiler<'a> {
) => { ) => {
// final side-effectless instruction // final side-effectless instruction
instrs.pop().unwrap(); instrs.pop().unwrap();
return
} }
Some( Some(
I::NewLocal I::NewLocal
@ -236,13 +238,13 @@ impl<'a> Compiler<'a> {
let i = self.chunk.instrs.pop().unwrap(); let i = self.chunk.instrs.pop().unwrap();
self.chunk.instrs.pop().unwrap(); self.chunk.instrs.pop().unwrap();
self.chunk.instrs.push(i); self.chunk.instrs.push(i);
return
} }
} }
_ => { _ => (),
}
self.emit(I::Drop); self.emit(I::Drop);
} }
}
}
fn ip(&self) -> usize { fn ip(&self) -> usize {
self.chunk.instrs.len() self.chunk.instrs.len()
@ -370,6 +372,10 @@ impl<'a> Compiler<'a> {
Ok(()) Ok(())
} }
fn load_global(&mut self, name: Symbol) {
self.emit(I::LoadGlobal(Arg24::from_symbol(name)));
}
fn declare_local(&mut self, name: Symbol) -> usize { fn declare_local(&mut self, name: Symbol) -> usize {
let local = VarKind::Local(self.local_count); let local = VarKind::Local(self.local_count);
self.local_count += 1; self.local_count += 1;
@ -452,6 +458,11 @@ impl<'a> Compiler<'a> {
} }
ExprKind::Literal(v) => self.expr_literal(v), ExprKind::Literal(v) => self.expr_literal(v),
ExprKind::Ident(ident) => self.load_var(*ident)?, 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) => { ExprKind::UnaryOp(o, a) => {
self.expr(a)?; self.expr(a)?;
self.emit(I::UnaryOp(*o)); self.emit(I::UnaryOp(*o));
@ -462,24 +473,6 @@ impl<'a> Compiler<'a> {
self.emit(I::BinaryOp(*o)); self.emit(I::BinaryOp(*o));
} }
ExprKind::Assign(o, lv, a) => self.expr_assign(*o, lv, a)?, 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::List(xs) => self.expr_list(xs)?,
ExprKind::Table(xs) => self.expr_table(xs)?, ExprKind::Table(xs) => self.expr_table(xs)?,
ExprKind::Index(ct, idx) => { ExprKind::Index(ct, idx) => {
@ -855,34 +848,84 @@ impl<'a> Compiler<'a> {
self.emit(I::Const(Arg24::from_usize(n))); self.emit(I::Const(Arg24::from_usize(n)));
} }
fn expr_assign(&mut self, o: Option<BinaryOp>, lv: &LValue, a: &Expr) -> Result<()> { fn lvalue(&mut self, lv: &LValue) -> Result<()> {
match (&lv.kind, o) { match &lv.kind {
(LValueKind::Ident(i), None) => { LValueKind::Ident(i) => {
self.expr(a)?;
self.emit(I::Dup); self.emit(I::Dup);
self.store_var(*i); 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.load_var(*i)?;
self.expr(a)?; self.expr(a)?;
self.emit(I::BinaryOp(o)); self.emit(I::BinaryOp(o));
self.emit(I::Dup); self.emit(I::Dup);
self.store_var(*i); self.store_var(*i);
} }
(LValueKind::Index(ct, i), None) => { LValueKind::Var(_) => {
self.expr(ct)?; throw!(lv.span.start, "cannot compound assign to explicit local")
self.expr(i)?;
self.expr(a)?;
self.emit(I::StoreIndex);
} }
(LValueKind::Index(ct, i), Some(o)) => { LValueKind::Global(i) => {
self.expr(ct)?; self.load_global(*i);
self.expr(i)?;
self.emit(I::DupTwo);
self.emit(I::Index);
self.expr(a)?; self.expr(a)?;
self.emit(I::BinaryOp(o)); 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(()) Ok(())

View file

@ -9,6 +9,7 @@ pub mod compiler;
pub mod exception; pub mod exception;
pub mod lstring; pub mod lstring;
pub mod number; pub mod number;
pub mod ops;
pub mod optimize; pub mod optimize;
pub mod parser; pub mod parser;
pub mod serial; pub mod serial;

View file

@ -151,30 +151,40 @@ pub struct LStr {
inner: [u8], inner: [u8],
} }
impl fmt::Debug for LStr { impl LStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { pub fn to_escaped(&self) -> String {
f.write_char('"')?; let mut s = String::new();
s += "\"";
let mut bytes = &self.inner; let mut bytes = &self.inner;
while let Some((new_bytes, res)) = next_codepoint(bytes) { while let Some((new_bytes, res)) = next_codepoint(bytes) {
bytes = new_bytes; bytes = new_bytes;
match res { match res {
Ok('"') => f.write_str("\\\"")?, Ok('"') => s += "\\\"",
Ok('\\') => f.write_str("\\\\")?, Ok('\\') => s += "\\\\",
Ok('\x00') => f.write_str("\\0")?, Ok('\x00') => s += "\\0",
Ok('\x07') => f.write_str("\\a")?, Ok('\x07') => s += "\\a",
Ok('\x08') => f.write_str("\\b")?, Ok('\x08') => s += "\\b",
Ok('\x09') => f.write_str("\\t")?, Ok('\x09') => s += "\\t",
Ok('\x0a') => f.write_str("\\n")?, Ok('\x0a') => s += "\\n",
Ok('\x0b') => f.write_str("\\v")?, Ok('\x0b') => s += "\\v",
Ok('\x0c') => f.write_str("\\f")?, Ok('\x0c') => s += "\\f",
Ok('\x0d') => f.write_str("\\r")?, Ok('\x0d') => s += "\\r",
Ok('\x1b') => f.write_str("\\e")?, Ok('\x1b') => s += "\\e",
Ok(c) if c.is_control() => write!(f, "\\u{{{:x}}}", c as u32)?, Ok(c) if c.is_control() => {
Ok(c) => f.write_char(c)?, write!(s, "\\u{{{:x}}}", c as u32).expect("failed to write to string");
Err(b) => write!(f, "\\x{b:02x}")?, }
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 { match &mut expr.kind {
ExprKind::Literal(_) => (), ExprKind::Literal(_) => (),
ExprKind::Ident(_) => (), ExprKind::Ident(_) => (),
ExprKind::Var(_) => (),
ExprKind::Global(_) => (),
ExprKind::UnaryOp(o, e) => { ExprKind::UnaryOp(o, e) => {
optimize_ex(e); optimize_ex(e);
if let Some(a) = e.value() { if let Some(a) = e.value() {
@ -73,16 +75,6 @@ fn optimize_ex_with(expr: &mut Expr, state: OptState) {
optimize_lv(l); optimize_lv(l);
optimize_ex(r); 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) => { ExprKind::FnDef(_, _, e) => {
optimize_ex_with(e, OptState::ret(true)); 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) { fn optimize_lv(e: &mut LValue) {
match &mut e.kind { match &mut e.kind {
LValueKind::Ident(_) => (), LValueKind::Ident(_) => (),
LValueKind::Var(_) => (),
LValueKind::Global(_) => (),
LValueKind::Index(l, r) => { LValueKind::Index(l, r) => {
optimize_ex(l); optimize_ex(l);
optimize_ex(r); optimize_ex(r);
} }
LValueKind::List(ls, _) => {
for l in ls {
optimize_lv(l);
}
}
} }
} }

View file

@ -1,45 +1,13 @@
use core::fmt; use core::fmt;
use crate::{symbol::Symbol, value::Value}; use crate::{
ops::{BinaryOp, UnaryOp},
symbol::Symbol,
value::Value,
};
use super::Span; 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)] #[derive(Debug)]
pub struct Expr { pub struct Expr {
pub span: Span, pub span: Span,
@ -50,13 +18,13 @@ pub struct Expr {
pub enum ExprKind { pub enum ExprKind {
Literal(Value), Literal(Value),
Ident(Symbol), Ident(Symbol),
Var(Symbol),
Global(Symbol),
UnaryOp(UnaryOp, Box<Expr>), UnaryOp(UnaryOp, Box<Expr>),
BinaryOp(BinaryOp, Box<Expr>, Box<Expr>), BinaryOp(BinaryOp, Box<Expr>, Box<Expr>),
Assign(Option<BinaryOp>, Box<LValue>, 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>), FnDef(Option<Symbol>, Vec<Symbol>, Box<Expr>),
Index(Box<Expr>, Box<Expr>), Index(Box<Expr>, Box<Expr>),
@ -119,7 +87,10 @@ pub struct LValue {
#[derive(Debug)] #[derive(Debug)]
pub enum LValueKind { pub enum LValueKind {
Ident(Symbol), Ident(Symbol),
Var(Symbol),
Global(Symbol),
Index(Box<Expr>, Box<Expr>), Index(Box<Expr>, Box<Expr>),
List(Vec<LValue>, Option<usize>),
} }
impl LValueKind { 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 { impl CatchBlock {
pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result { pub fn write_to(&self, w: &mut impl fmt::Write, depth: usize) -> fmt::Result {
write!(w, "{0: >1$}catch", "", depth * 2)?; write!(w, "{0: >1$}catch", "", depth * 2)?;
@ -168,11 +128,23 @@ impl LValue {
let depth = depth + 1; let depth = depth + 1;
match &self.kind { match &self.kind {
LValueKind::Ident(n) => writeln!(w, "${}", n.name()), 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) => { LValueKind::Index(l, r) => {
writeln!(w, "index")?; writeln!(w, "index")?;
l.write_to(w, depth)?; l.write_to(w, depth)?;
r.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 { match &self.kind {
ExprKind::Literal(val) => writeln!(w, "{val}"), ExprKind::Literal(val) => writeln!(w, "{val}"),
ExprKind::Ident(n) => writeln!(w, "${}", n.name()), 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) => { ExprKind::UnaryOp(op, e) => {
writeln!(w, "uop {op:?}")?; writeln!(w, "uop {op}")?;
e.write_to(w, depth) e.write_to(w, depth)
} }
ExprKind::BinaryOp(op, l, r) => { ExprKind::BinaryOp(op, l, r) => {
writeln!(w, "bop {op:?}")?; writeln!(w, "bop {op}")?;
l.write_to(w, depth)?; l.write_to(w, depth)?;
r.write_to(w, depth) r.write_to(w, depth)
} }
ExprKind::Assign(op, l, r) => { ExprKind::Assign(op, l, r) => {
if let Some(op) = op { if let Some(op) = op {
writeln!(w, "asgn {op:?}")?; writeln!(w, "asgn {op}")?;
} else { } else {
writeln!(w, "asgn =")?; writeln!(w, "asgn =")?;
} }
l.write_to(w, depth)?; l.write_to(w, depth)?;
r.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) => { ExprKind::FnDef(n, p, b) => {
if let Some(n) = n { if let Some(n) = n {
write!(w, "fndef ${}", n.name())?; write!(w, "fndef ${}", n.name())?;

View file

@ -1,13 +1,14 @@
use std::iter::Peekable; use std::iter::Peekable;
use crate::{ use crate::{
ops::{BinaryOp, UnaryOp},
parser::ast::TableItem, parser::ast::TableItem,
symbol::{Symbol, SYM_DOLLAR_SIGN}, symbol::{Symbol, SYM_DOLLAR_SIGN},
value::Value, value::Value,
}; };
use super::{ 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, parse_float, parse_int_literal, parse_str_escapes, Lexer, ParserError, Span,
SpanParserError, Token, TokenKind, 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> { struct Parser<'s> {
lexer: Peekable<Lexer<'s>>, lexer: Peekable<Lexer<'s>>,
} }
@ -448,10 +480,20 @@ impl<'s> Parser<'s> {
} }
fn parse_term(&mut self) -> Result<Expr> { fn parse_term(&mut self) -> Result<Expr> {
if let Some(tok) = try_next!(self, T::Identifier) { let Some(tok) = try_next!(self, T::Identifier | T::Var | T::Global) else {
Ok(E::Ident(Symbol::get(tok.content)).span(tok.span)) return self.parse_term_not_ident()
} else { };
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 = self.parse_pipeline()?;
let lhs_span = lhs.span; let lhs_span = lhs.span;
if let Some(op) = self.peek()?.kind.assign_op() { if let Some(op) = self.peek()?.kind.assign_op() {
let Some(lval) = LValue::from_expr(lhs) else { let lval = LValue::from_expr(lhs)?;
throw!(lhs_span, "invalid lvalue for assignment")
};
self.next()?; self.next()?;
let rhs = self.parse_decl()?; let rhs = self.parse_decl()?;
let rhs_span = rhs.span; 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> { fn parse_fn_decl(&mut self) -> Result<Expr> {
let tok_fn = expect!(self, T::Fn); let tok_fn = expect!(self, T::Fn);
let name = try_next!(self, T::Identifier).map(|t| Symbol::get(t.content)); 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> { fn parse_decl(&mut self) -> Result<Expr> {
match self.peek()?.kind { match self.peek()?.kind {
T::Var | T::Global => self.parse_var_decl(),
T::Fn => self.parse_fn_decl(), T::Fn => self.parse_fn_decl(),
_ => self.parse_assign(), _ => self.parse_assign(),
} }

View file

@ -1,7 +1,12 @@
use core::fmt; 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; use super::Span;
@ -136,17 +141,17 @@ pub fn parse_float<'a, S: Into<&'a LStr>>(f: S) -> Result<f64, ParseFloatError>
s.parse() 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(); let mut s = String::new();
for c in f.into().chars() { for c in f.into().chars() {
if c != '_' { if c != '_' {
s.push(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(); let f = f.into();
match f.chars().nth(1) { match f.chars().nth(1) {
Some('x') => parse_int(&f[2..], 16), 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), _ => 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 ()>, recur: &mut Vec<*const ()>,
) -> io::Result<()> { ) -> io::Result<()> {
use std::io::Write; 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 { match self {
Self::Nil => write!(w, "nil"), Self::Nil => write!(w, "nil"),
Self::Bool(b) => write!(w, "{b}"), Self::Bool(b) => write!(w, "{b}"),
@ -105,20 +114,24 @@ impl Value {
if name.is_identifier() { if name.is_identifier() {
w.extend(name.as_bytes()); w.extend(name.as_bytes());
} else { } else {
write!(w, "{name:?}")?; write!(w, "{}", name.to_escaped())?;
} }
Ok(()) Ok(())
} }
Self::Range(r) => write!(w, "{r}"), Self::Range(r) => write!(w, "{r}"),
Self::Int(i) => write!(w, "{i}"), 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::Ratio(r) => write!(w, "{}/{}", r.numer(), r.denom()),
Self::Complex(z) => { Self::Complex(z) => {
write!(w, "{:?}", z.re())?; write_float(w, z.re())?;
if z.im() >= 0.0 || z.im.is_nan() { if z.im().signum() > 0.0 || z.im.is_nan() {
w.push_byte(b'+'); 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 => { Self::Cell(v) if repr => {
if recur.contains(&(v.as_ptr() as _)) { if recur.contains(&(v.as_ptr() as _)) {
@ -139,7 +152,7 @@ impl Value {
recur.pop(); recur.pop();
Ok(()) 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::String(s) => w.write_all(s.as_bytes()),
Self::List(l) => { Self::List(l) => {

View file

@ -10,9 +10,10 @@ use crate::{
chunk::Instruction, chunk::Instruction,
exception::{throw, Exception, Result}, exception::{throw, Exception, Result},
lstring::{LStr, LString}, lstring::{LStr, LString},
parser::ast::{BinaryOp, UnaryOp}, ops::{BinaryOp, UnaryOp},
symbol::{ symbol::{
Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR, Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR,
SYM_VALUE_ERROR,
}, },
value::{ value::{
function::{FuncAttrs, Function, NativeFunc}, function::{FuncAttrs, Function, NativeFunc},
@ -486,20 +487,57 @@ impl Vm {
drop(table_ref); drop(table_ref);
self.push(Value::Table(table)); 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]] // [ct, idx] -> [ct[idx]]
I::Index => { I::Index => {
let idx = self.pop(); let idx = self.pop();
let ct = self.pop(); let ct = self.pop();
self.push(ct.index(idx)?); self.push(ct.index(idx)?);
} }
// [ct, idx, v] -> [v], ct[idx] = v // [v, ct, idx] -> [v], ct[idx] = v
I::StoreIndex => { I::StoreIndex => {
let v = self.pop();
let idx = self.pop(); let idx = self.pop();
let ct = self.pop(); let ct = self.pop();
let v = self.pop();
ct.store_index(self, idx, v.clone())?; ct.store_index(self, idx, v.clone())?;
self.push(v); 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 // ip = n
I::Jump(n) => { I::Jump(n) => {
self.check_interrupt()?; self.check_interrupt()?;

View file

@ -104,14 +104,14 @@ impl<'p> FmtParser<'p> {
let Ok(i) = parse_int(s, 10) else { let Ok(i) = parse_int(s, 10) else {
throw!(*SYM_FORMAT_ERROR, "code {{{}}}: invalid integer", self.code) throw!(*SYM_FORMAT_ERROR, "code {{{}}}: invalid integer", self.code)
}; };
if i < 0 { let Some(n) = i.to_usize() else {
throw!( throw!(
*SYM_FORMAT_ERROR, *SYM_FORMAT_ERROR,
"code {{{}}}: integer may not be negative", "code {{{}}}: integer may not be negative",
self.code self.code
) )
} };
Ok(Some(i as usize)) Ok(Some(n))
} }
fn next_fmt_index(&mut self) -> Result<Option<FmtIndex>> { fn next_fmt_index(&mut self) -> Result<Option<FmtIndex>> {
@ -351,7 +351,7 @@ fn format_string(
buf.push_lstr(s); buf.push_lstr(s);
Ok(()) Ok(())
} }
FmtType::Repr => write!(buf, "{:?}", s), FmtType::Repr => write!(buf, "{}", s.to_escaped()),
_ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for string"), _ => throw!(*SYM_FORMAT_ERROR, "invalid format specifier for string"),
}; };
res.map_err(|e| exception!(*SYM_FORMAT_ERROR, "{e}")) 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::Float(x), b"complex") => Ok(Value::Complex(x.into())),
(Value::String(s), b"int") => parse_int(s.as_ref(), 10) (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")), .map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as integer")),
(Value::String(s), b"float") => parse_float(s.as_ref()) (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")), .map_err(|_| exception!(*SYM_VALUE_ERROR, "could not parse {s:#} as float")),
(v, _) => throw!( (v, _) => throw!(