This commit is contained in:
parent
825379a38b
commit
668e0712c8
14 changed files with 377 additions and 218 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
91
talc-lang/src/ops.rs
Normal 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())
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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())?;
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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()?;
|
||||
|
|
|
@ -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}"))
|
||||
|
|
|
@ -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!(
|
||||
|
|
Loading…
Add table
Reference in a new issue