list and table interpolation
This commit is contained in:
parent
11b07c7b0d
commit
2a8a7b347e
6 changed files with 312 additions and 135 deletions
|
@ -134,8 +134,10 @@ pub enum Instruction {
|
||||||
|
|
||||||
NewList(u8),
|
NewList(u8),
|
||||||
GrowList(u8),
|
GrowList(u8),
|
||||||
|
ExtendList,
|
||||||
NewTable(u8),
|
NewTable(u8),
|
||||||
GrowTable(u8),
|
GrowTable(u8),
|
||||||
|
ExtendTable,
|
||||||
|
|
||||||
Index,
|
Index,
|
||||||
StoreIndex,
|
StoreIndex,
|
||||||
|
@ -197,8 +199,10 @@ impl std::fmt::Display for Instruction {
|
||||||
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::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::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::Jump(a) => write!(f, "jump @{}", usize::from(a)),
|
Self::Jump(a) => write!(f, "jump @{}", usize::from(a)),
|
||||||
|
|
|
@ -3,11 +3,12 @@ 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::parser::ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind};
|
use crate::parser::ast::{
|
||||||
|
BinaryOp, CatchBlock, Expr, ExprKind, LValue, LValueKind, ListItem, TableItem,
|
||||||
|
};
|
||||||
use crate::parser::Pos;
|
use crate::parser::Pos;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::symbol::{Symbol, SYM_REPL, SYM_SELF};
|
use crate::symbol::{Symbol, SYM_REPL, SYM_SELF};
|
||||||
use crate::throw;
|
|
||||||
use crate::value::function::{FuncAttrs, Function};
|
use crate::value::function::{FuncAttrs, Function};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
|
@ -460,41 +461,8 @@ impl<'a> Compiler<'a> {
|
||||||
self.declare_global(*name);
|
self.declare_global(*name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::List(xs) if xs.is_empty() => {
|
ExprKind::List(xs) => self.expr_list(xs)?,
|
||||||
self.emit(I::NewList(0));
|
ExprKind::Table(xs) => self.expr_table(xs)?,
|
||||||
}
|
|
||||||
ExprKind::List(xs) => {
|
|
||||||
let mut first = true;
|
|
||||||
for chunk in xs.chunks(16) {
|
|
||||||
for e in chunk {
|
|
||||||
self.expr(e)?;
|
|
||||||
}
|
|
||||||
if first {
|
|
||||||
self.emit(I::NewList(chunk.len() as u8));
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
self.emit(I::GrowList(chunk.len() as u8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ExprKind::Table(xs) if xs.is_empty() => {
|
|
||||||
self.emit(I::NewTable(0));
|
|
||||||
}
|
|
||||||
ExprKind::Table(xs) => {
|
|
||||||
let mut first = true;
|
|
||||||
for chunk in xs.chunks(8) {
|
|
||||||
for (k, v) in chunk {
|
|
||||||
self.expr(k)?;
|
|
||||||
self.expr(v)?;
|
|
||||||
}
|
|
||||||
if first {
|
|
||||||
self.emit(I::NewTable(chunk.len() as u8));
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
self.emit(I::GrowTable(chunk.len() as u8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ExprKind::Index(ct, idx) => {
|
ExprKind::Index(ct, idx) => {
|
||||||
self.expr(ct)?;
|
self.expr(ct)?;
|
||||||
self.expr(idx)?;
|
self.expr(idx)?;
|
||||||
|
@ -600,6 +568,83 @@ impl<'a> Compiler<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expr_list(&mut self, xs: &[ListItem]) -> Result<()> {
|
||||||
|
if xs.is_empty() {
|
||||||
|
self.emit(I::NewList(0));
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
fn finish_chunk(c: &mut Compiler, len: &mut usize, first: &mut bool) {
|
||||||
|
if *first {
|
||||||
|
c.emit(I::NewList(*len as u8));
|
||||||
|
*first = false;
|
||||||
|
} else {
|
||||||
|
if *len == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.emit(I::GrowList(*len as u8));
|
||||||
|
}
|
||||||
|
*len = 0;
|
||||||
|
}
|
||||||
|
let mut first = true;
|
||||||
|
let mut chlen = 0;
|
||||||
|
for (i, e) in xs.iter().enumerate() {
|
||||||
|
match e {
|
||||||
|
ListItem::Interpolate(e) => {
|
||||||
|
finish_chunk(self, &mut chlen, &mut first);
|
||||||
|
self.expr(e)?;
|
||||||
|
self.emit(I::ExtendList);
|
||||||
|
}
|
||||||
|
ListItem::Item(e) => {
|
||||||
|
self.expr(e)?;
|
||||||
|
chlen += 1;
|
||||||
|
if chlen >= 16 || i == xs.len() - 1 {
|
||||||
|
finish_chunk(self, &mut chlen, &mut first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_table(&mut self, xs: &[TableItem]) -> Result<()> {
|
||||||
|
if xs.is_empty() {
|
||||||
|
self.emit(I::NewTable(0));
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
fn finish_chunk(c: &mut Compiler, len: &mut usize, first: &mut bool) {
|
||||||
|
if *first {
|
||||||
|
c.emit(I::NewTable(*len as u8));
|
||||||
|
*first = false;
|
||||||
|
} else {
|
||||||
|
if *len == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.emit(I::GrowTable(*len as u8));
|
||||||
|
}
|
||||||
|
*len = 0;
|
||||||
|
}
|
||||||
|
let mut first = true;
|
||||||
|
let mut chlen = 0;
|
||||||
|
for (i, e) in xs.iter().enumerate() {
|
||||||
|
match e {
|
||||||
|
TableItem::Pair(k, v) => {
|
||||||
|
self.expr(k)?;
|
||||||
|
self.expr(v)?;
|
||||||
|
chlen += 1;
|
||||||
|
if chlen >= 8 || i == xs.len() - 1 {
|
||||||
|
finish_chunk(self, &mut chlen, &mut first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TableItem::Interpolate(ext) => {
|
||||||
|
finish_chunk(self, &mut chlen, &mut first);
|
||||||
|
self.expr(ext)?;
|
||||||
|
self.emit(I::ExtendTable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn expr_infinite_loop(&mut self, body: &Expr) -> Result<()> {
|
fn expr_infinite_loop(&mut self, body: &Expr) -> Result<()> {
|
||||||
let start = self.ip();
|
let start = self.ip();
|
||||||
self.begin_break_frame();
|
self.begin_break_frame();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use core::panic;
|
use core::panic;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parser::ast::{CatchBlock, Expr, ExprKind, LValue, LValueKind},
|
parser::ast::{CatchBlock, Expr, ExprKind, LValue, LValueKind, ListItem, TableItem},
|
||||||
value::Value,
|
value::Value,
|
||||||
vm,
|
vm,
|
||||||
};
|
};
|
||||||
|
@ -128,14 +128,24 @@ fn optimize_ex_with(expr: &mut Expr, state: OptState) {
|
||||||
}
|
}
|
||||||
ExprKind::List(xs) => {
|
ExprKind::List(xs) => {
|
||||||
for e in xs {
|
for e in xs {
|
||||||
optimize_ex(e);
|
match e {
|
||||||
|
ListItem::Item(e) => optimize_ex(e),
|
||||||
|
ListItem::Interpolate(e) => optimize_ex(e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::Table(t) => {
|
ExprKind::Table(t) => {
|
||||||
for (k, v) in t {
|
for e in t {
|
||||||
|
match e {
|
||||||
|
TableItem::Pair(k, v) => {
|
||||||
optimize_ex(k);
|
optimize_ex(k);
|
||||||
optimize_ex(v);
|
optimize_ex(v);
|
||||||
}
|
}
|
||||||
|
TableItem::Interpolate(ext) => {
|
||||||
|
optimize_ex(ext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ExprKind::Return(e) => {
|
ExprKind::Return(e) => {
|
||||||
if let Some(e) = e {
|
if let Some(e) = e {
|
||||||
|
|
|
@ -65,8 +65,8 @@ pub enum ExprKind {
|
||||||
Pipe(Box<Expr>, Box<Expr>),
|
Pipe(Box<Expr>, Box<Expr>),
|
||||||
|
|
||||||
Block(Vec<Expr>),
|
Block(Vec<Expr>),
|
||||||
List(Vec<Expr>),
|
List(Vec<ListItem>),
|
||||||
Table(Vec<(Expr, Expr)>),
|
Table(Vec<TableItem>),
|
||||||
|
|
||||||
Return(Option<Box<Expr>>),
|
Return(Option<Box<Expr>>),
|
||||||
Break(Option<Box<Expr>>),
|
Break(Option<Box<Expr>>),
|
||||||
|
@ -90,6 +90,18 @@ impl ExprKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TableItem {
|
||||||
|
Pair(Box<Expr>, Box<Expr>),
|
||||||
|
Interpolate(Box<Expr>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ListItem {
|
||||||
|
Item(Box<Expr>),
|
||||||
|
Interpolate(Box<Expr>),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CatchBlock {
|
pub struct CatchBlock {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
|
@ -258,16 +270,32 @@ impl Expr {
|
||||||
ExprKind::List(l) => {
|
ExprKind::List(l) => {
|
||||||
writeln!(w, "list")?;
|
writeln!(w, "list")?;
|
||||||
for e in l {
|
for e in l {
|
||||||
|
match e {
|
||||||
|
ListItem::Item(e) => {
|
||||||
e.write_to(w, depth)?;
|
e.write_to(w, depth)?;
|
||||||
}
|
}
|
||||||
|
ListItem::Interpolate(ext) => {
|
||||||
|
writeln!(w, "{0: >1$}interpolate", "", depth * 2)?;
|
||||||
|
ext.write_to(w, depth + 1)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
ExprKind::Table(t) => {
|
ExprKind::Table(t) => {
|
||||||
writeln!(w, "list")?;
|
writeln!(w, "table")?;
|
||||||
for (k, v) in t {
|
for e in t {
|
||||||
|
match e {
|
||||||
|
TableItem::Pair(k, v) => {
|
||||||
k.write_to(w, depth)?;
|
k.write_to(w, depth)?;
|
||||||
v.write_to(w, depth)?;
|
v.write_to(w, depth)?;
|
||||||
}
|
}
|
||||||
|
TableItem::Interpolate(ext) => {
|
||||||
|
writeln!(w, "{0: >1$}interpolate", "", depth * 2)?;
|
||||||
|
ext.write_to(w, depth + 1)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
ExprKind::Return(e) => {
|
ExprKind::Return(e) => {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
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, UnaryOp},
|
ast::{BinaryOp, CatchBlock, Expr, ExprKind, LValue, ListItem, UnaryOp},
|
||||||
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,
|
||||||
};
|
};
|
||||||
|
@ -204,21 +205,53 @@ impl<'s> Parser<'s> {
|
||||||
self.lexer.peek().unwrap().clone()
|
self.lexer.peek().unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_table_items(&mut self) -> Result<Vec<(Expr, Expr)>> {
|
fn parse_table_items(&mut self) -> Result<Vec<TableItem>> {
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
while self.peek()?.kind.expr_first() {
|
loop {
|
||||||
let key = if let Some(id) = try_next!(self, T::Identifier) {
|
let t = self.peek()?;
|
||||||
E::Literal(Symbol::get(id.content).into()).span(id.span)
|
match t.kind {
|
||||||
} else {
|
T::Identifier => {
|
||||||
self.parse_term_not_ident()?
|
self.next()?;
|
||||||
};
|
let key = E::Literal(Symbol::get(t.content).into()).span(t.span);
|
||||||
|
|
||||||
expect!(self, T::Equal);
|
expect!(self, T::Equal);
|
||||||
|
|
||||||
let value = self.parse_expr()?;
|
let value = self.parse_expr()?;
|
||||||
|
items.push(TableItem::Pair(b(key), b(value)));
|
||||||
|
}
|
||||||
|
T::DotDot => {
|
||||||
|
self.next()?;
|
||||||
|
let ext = self.parse_expr()?;
|
||||||
|
items.push(TableItem::Interpolate(b(ext)));
|
||||||
|
}
|
||||||
|
k if k.expr_first() => {
|
||||||
|
let key = self.parse_term_not_ident()?;
|
||||||
|
expect!(self, T::Equal);
|
||||||
|
let value = self.parse_expr()?;
|
||||||
|
items.push(TableItem::Pair(b(key), b(value)));
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
if try_next!(self, T::Comma).is_none() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(items)
|
||||||
|
}
|
||||||
|
|
||||||
items.push((key, value));
|
fn parse_list_items(&mut self) -> Result<Vec<ListItem>> {
|
||||||
|
let mut items = Vec::new();
|
||||||
|
loop {
|
||||||
|
let t = self.peek()?;
|
||||||
|
match t.kind {
|
||||||
|
T::DotDot => {
|
||||||
|
self.next()?;
|
||||||
|
let expr = self.parse_expr()?;
|
||||||
|
items.push(ListItem::Interpolate(b(expr)));
|
||||||
|
}
|
||||||
|
k if k.expr_first() => {
|
||||||
|
items.push(ListItem::Item(b(self.parse_expr()?)));
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
if try_next!(self, T::Comma).is_none() {
|
if try_next!(self, T::Comma).is_none() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -325,7 +358,7 @@ impl<'s> Parser<'s> {
|
||||||
Ok(e)
|
Ok(e)
|
||||||
}
|
}
|
||||||
T::LBrack => {
|
T::LBrack => {
|
||||||
let args = self.parse_expr_list()?;
|
let args = self.parse_list_items()?;
|
||||||
let end = expect!(self, T::RBrack);
|
let end = expect!(self, T::RBrack);
|
||||||
Ok(E::List(args).span(tok.span + end.span))
|
Ok(E::List(args).span(tok.span + end.span))
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
value::{
|
value::{
|
||||||
function::{FuncAttrs, Function, NativeFunc},
|
function::{FuncAttrs, Function, NativeFunc},
|
||||||
Value,
|
HashValue, Value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -240,6 +240,12 @@ impl Vm {
|
||||||
self.stack.pop().expect("temporary stack underflow")
|
self.stack.pop().expect("temporary stack underflow")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn take_n(&mut self, n: usize) -> Value {
|
||||||
|
let i = self.stack.len() - n - 1;
|
||||||
|
std::mem::take(&mut self.stack[i])
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pop_n(&mut self, n: usize) -> Vec<Value> {
|
fn pop_n(&mut self, n: usize) -> Vec<Value> {
|
||||||
let res = self.stack.split_off(self.stack.len() - n);
|
let res = self.stack.split_off(self.stack.len() - n);
|
||||||
|
@ -397,38 +403,86 @@ impl Vm {
|
||||||
list.borrow_mut().extend(ext);
|
list.borrow_mut().extend(ext);
|
||||||
self.push(Value::List(list));
|
self.push(Value::List(list));
|
||||||
}
|
}
|
||||||
|
// [l,e] -> [l ++ e]
|
||||||
|
I::ExtendList => {
|
||||||
|
let ext = self.pop();
|
||||||
|
let list = self.pop(); // must be a list
|
||||||
|
let Value::List(list) = list else {
|
||||||
|
panic!("not a list")
|
||||||
|
};
|
||||||
|
match ext {
|
||||||
|
Value::List(ext) => {
|
||||||
|
list.borrow_mut().extend_from_slice(ext.borrow().as_slice());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let mut list = list.borrow_mut();
|
||||||
|
let f = ext.to_iter_function()?;
|
||||||
|
loop {
|
||||||
|
self.push(f.clone());
|
||||||
|
self.instr_call(0, frame)?;
|
||||||
|
match self.pop().iter_unpack() {
|
||||||
|
None => break,
|
||||||
|
Some(v) => list.push(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.push(Value::List(list));
|
||||||
|
}
|
||||||
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
|
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
|
||||||
I::NewTable(n) => {
|
I::NewTable(n) => {
|
||||||
|
let n = n as usize;
|
||||||
let mut table = HashMap::new();
|
let mut table = HashMap::new();
|
||||||
for _ in 0..n {
|
for i in 0..n {
|
||||||
let v = self.pop();
|
let k: HashValue = self.take_n(2 * (n - i - 1) + 1).try_into()?;
|
||||||
let k = self.pop();
|
let v = self.take_n(2 * (n - i - 1));
|
||||||
if v == Value::Nil {
|
if v == Value::Nil {
|
||||||
table.remove(&k.try_into()?);
|
table.remove(&k);
|
||||||
} else {
|
} else {
|
||||||
table.insert(k.try_into()?, v);
|
table.insert(k, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.stack.truncate(self.stack.len() - 2 * n);
|
||||||
self.push(table.into());
|
self.push(table.into());
|
||||||
}
|
}
|
||||||
// [t,k0,v0...kn,vn] -> [t ++ {k0=v0...kn=vn}]
|
// [t,k0,v0...kn,vn] -> [t ++ {k0=v0...kn=vn}]
|
||||||
I::GrowTable(n) => {
|
I::GrowTable(n) => {
|
||||||
let mut ext = self.pop_n(2 * n as usize);
|
let n = n as usize;
|
||||||
let table = self.pop();
|
let table = self.take_n(2 * n);
|
||||||
let Value::Table(table) = table else {
|
let Value::Table(table) = table else {
|
||||||
panic!("not a table")
|
panic!("not a table")
|
||||||
};
|
};
|
||||||
let mut table_ref = table.borrow_mut();
|
let mut table_ref = table.borrow_mut();
|
||||||
for _ in 0..n {
|
for i in 0..n {
|
||||||
// can't panic: pop_n checked that ext would have len 2*n
|
let k: HashValue = self.take_n(2 * (n - i - 1) + 1).try_into()?;
|
||||||
let v = ext.pop().unwrap();
|
let v = self.take_n(2 * (n - i - 1));
|
||||||
let k = ext.pop().unwrap();
|
|
||||||
if v == Value::Nil {
|
if v == Value::Nil {
|
||||||
table_ref.remove(&k.try_into()?);
|
table_ref.remove(&k);
|
||||||
} else {
|
} else {
|
||||||
table_ref.insert(k.try_into()?, v);
|
table_ref.insert(k, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.stack.truncate(self.stack.len() - 2 * n - 1);
|
||||||
|
drop(table_ref);
|
||||||
|
self.push(Value::Table(table));
|
||||||
|
}
|
||||||
|
I::ExtendTable => {
|
||||||
|
let ext = self.pop();
|
||||||
|
let table = self.pop(); // must be a list
|
||||||
|
let Value::Table(table) = table else {
|
||||||
|
panic!("not a table")
|
||||||
|
};
|
||||||
|
let Value::Table(ext) = ext else {
|
||||||
|
throw!(
|
||||||
|
*SYM_TYPE_ERROR,
|
||||||
|
"cannot interpolate value {:#} into table",
|
||||||
|
ext
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let mut table_ref = table.borrow_mut();
|
||||||
|
for (k, v) in ext.borrow().iter() {
|
||||||
|
table_ref.insert(k.clone(), v.clone());
|
||||||
|
}
|
||||||
drop(table_ref);
|
drop(table_ref);
|
||||||
self.push(Value::Table(table));
|
self.push(Value::Table(table));
|
||||||
}
|
}
|
||||||
|
@ -493,58 +547,7 @@ impl Vm {
|
||||||
frame.try_frames.pop().expect("no try to pop");
|
frame.try_frames.pop().expect("no try to pop");
|
||||||
}
|
}
|
||||||
// [f,a0,a1...an] -> [f(a0,a1...an)]
|
// [f,a0,a1...an] -> [f(a0,a1...an)]
|
||||||
I::Call(n) => {
|
I::Call(n) => self.instr_call(n as usize, frame)?,
|
||||||
self.check_interrupt()?;
|
|
||||||
let n = usize::from(n);
|
|
||||||
|
|
||||||
let args = self.pop_n(n + 1);
|
|
||||||
|
|
||||||
let args = match get_call_outcome(args)? {
|
|
||||||
CallOutcome::Call(args) => args,
|
|
||||||
CallOutcome::Partial(v) => {
|
|
||||||
self.push(v);
|
|
||||||
return Ok(None)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Value::NativeFunc(nf) = &args[0] {
|
|
||||||
let nf = nf.clone();
|
|
||||||
|
|
||||||
// safety: frame is restored immediately
|
|
||||||
// after function call ends
|
|
||||||
// ~25% performance improvement in
|
|
||||||
// code heavy on native function calls
|
|
||||||
unsafe {
|
|
||||||
let f = std::ptr::read(frame);
|
|
||||||
self.call_stack.push(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = (nf.func)(self, args);
|
|
||||||
|
|
||||||
// safety: frame was referencing invalid memory due to
|
|
||||||
// previous unsafe block, write will fix that
|
|
||||||
unsafe {
|
|
||||||
let f = self.call_stack.pop().expect("no frame to pop");
|
|
||||||
std::ptr::write(frame, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure we restored the value of frame
|
|
||||||
// before propagating exceptions
|
|
||||||
let res = res?;
|
|
||||||
|
|
||||||
self.push(res);
|
|
||||||
} else if let Value::Function(func) = &args[0] {
|
|
||||||
if self.call_stack.len() + 1 >= self.stack_max {
|
|
||||||
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_frame = CallFrame::new(func.clone(), args);
|
|
||||||
let old_frame = std::mem::replace(frame, new_frame);
|
|
||||||
self.call_stack.push(old_frame);
|
|
||||||
} else {
|
|
||||||
unreachable!("already verified by calling get_call_type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// [f,a0,a1...an] -> [], return f(a0,a1...an)
|
// [f,a0,a1...an] -> [], return f(a0,a1...an)
|
||||||
I::Tail(n) => {
|
I::Tail(n) => {
|
||||||
self.check_interrupt()?;
|
self.check_interrupt()?;
|
||||||
|
@ -594,6 +597,60 @@ impl Vm {
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn instr_call(&mut self, n: usize, frame: &mut CallFrame) -> Result<()> {
|
||||||
|
self.check_interrupt()?;
|
||||||
|
|
||||||
|
let args = self.pop_n(n + 1);
|
||||||
|
|
||||||
|
let args = match get_call_outcome(args)? {
|
||||||
|
CallOutcome::Call(args) => args,
|
||||||
|
CallOutcome::Partial(v) => {
|
||||||
|
self.push(v);
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Value::NativeFunc(nf) = &args[0] {
|
||||||
|
let nf = nf.clone();
|
||||||
|
|
||||||
|
// safety: frame is restored immediately
|
||||||
|
// after function call ends
|
||||||
|
// ~25% performance improvement in
|
||||||
|
// code heavy on native function calls
|
||||||
|
unsafe {
|
||||||
|
let f = std::ptr::read(frame);
|
||||||
|
self.call_stack.push(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = (nf.func)(self, args);
|
||||||
|
|
||||||
|
// safety: frame was referencing invalid memory due to
|
||||||
|
// previous unsafe block, write will fix that
|
||||||
|
unsafe {
|
||||||
|
let f = self.call_stack.pop().expect("no frame to pop");
|
||||||
|
std::ptr::write(frame, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we restored the value of frame
|
||||||
|
// before propagating exceptions
|
||||||
|
let res = res?;
|
||||||
|
|
||||||
|
self.push(res);
|
||||||
|
} else if let Value::Function(func) = &args[0] {
|
||||||
|
if self.call_stack.len() + 1 >= self.stack_max {
|
||||||
|
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_frame = CallFrame::new(func.clone(), args);
|
||||||
|
let old_frame = std::mem::replace(frame, new_frame);
|
||||||
|
self.call_stack.push(old_frame);
|
||||||
|
} else {
|
||||||
|
unreachable!("already verified by calling get_call_type");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|
Loading…
Reference in a new issue