rudus/src/compiler.rs

1442 lines
54 KiB
Rust
Raw Normal View History

use crate::chunk::{Chunk, StrPattern};
use crate::op::Op;
use crate::parser::Ast;
2025-05-30 15:44:32 +00:00
use crate::parser::StringPart;
use crate::spans::Spanned;
use crate::value::*;
use chumsky::prelude::SimpleSpan;
use regex::Regex;
2025-06-04 21:53:38 +00:00
use std::cell::RefCell;
use std::collections::HashMap;
2024-12-24 17:35:44 +00:00
use std::rc::Rc;
#[derive(Clone, Debug, PartialEq)]
2024-12-18 04:45:39 +00:00
pub struct Binding {
2024-12-16 04:28:57 +00:00
name: &'static str,
2024-12-18 04:45:39 +00:00
depth: isize,
stack_pos: usize,
2024-12-16 04:28:57 +00:00
}
impl std::fmt::Display for Binding {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}@{}//{}", self.name, self.stack_pos, self.depth)
}
}
2025-06-04 21:53:38 +00:00
#[derive(Clone, Debug, PartialEq)]
pub struct Upvalue {
name: &'static str,
stack_pos: usize,
}
#[derive(Debug, Clone, PartialEq)]
struct LoopInfo {
start: usize,
stack_root: usize,
}
impl LoopInfo {
fn new(start: usize, stack_root: usize) -> LoopInfo {
LoopInfo { start, stack_root }
}
}
fn get_builtin(name: &str, arity: usize) -> Option<Op> {
// match (name, arity) {
// ("type", 1) => Some(Op::TypeOf),
// ("eq?", 2) => Some(Op::Eq),
// ("add", 2) => Some(Op::Add),
// ("sub", 2) => Some(Op::Sub),
// ("mult", 2) => Some(Op::Mult),
// ("div", 2) => Some(Op::Div),
// ("unbox", 1) => Some(Op::Unbox),
// ("store!", 2) => Some(Op::BoxStore),
// ("assert!", 1) => Some(Op::Assert),
// ("get", 2) => Some(Op::Get),
// ("at", 2) => Some(Op::At),
// ("not", 1) => Some(Op::Not),
// ("print!", 1) => Some(Op::Print),
2025-05-28 20:37:25 +00:00
// _ => None,
// }
None
}
#[derive(Debug, Clone)]
pub struct Compiler {
2024-12-27 00:03:09 +00:00
pub chunk: Chunk,
pub bindings: Vec<Binding>,
2025-06-03 19:48:13 +00:00
pub scope_depth: isize,
pub match_depth: usize,
pub stack_depth: usize,
2024-12-27 00:03:09 +00:00
pub spans: Vec<SimpleSpan>,
pub nodes: Vec<&'static Ast>,
pub ast: &'static Ast,
pub span: SimpleSpan,
pub src: &'static str,
pub name: &'static str,
pub depth: usize,
pub upvalues: Vec<&'static str>,
loop_info: Vec<LoopInfo>,
tail_pos: bool,
debug: bool,
}
2025-06-05 16:04:02 +00:00
fn is_binding(expr: &Spanned<Ast>) -> bool {
2024-12-18 06:28:23 +00:00
let (ast, _) = expr;
use Ast::*;
2025-06-05 16:04:02 +00:00
match ast {
Let(..) | LBox(..) | FnDeclaration(..) => true,
2025-06-05 16:04:02 +00:00
Fn(name, ..) => !name.is_empty(),
_ => false,
}
2024-12-18 06:28:23 +00:00
}
fn has_placeholder(args: &[Spanned<Ast>]) -> bool {
args.iter().any(|arg| matches!(arg, (Ast::Placeholder, _)))
}
impl Compiler {
2025-06-04 21:53:38 +00:00
pub fn new(
ast: &'static Spanned<Ast>,
name: &'static str,
src: &'static str,
depth: usize,
env: imbl::HashMap<&'static str, Value>,
debug: bool,
) -> Compiler {
2024-12-27 00:03:09 +00:00
let chunk = Chunk {
2024-12-16 04:28:57 +00:00
constants: vec![],
bytecode: vec![],
keywords: vec![],
string_patterns: vec![],
env,
msgs: vec![],
2024-12-27 00:03:09 +00:00
};
Compiler {
chunk,
bindings: vec![],
depth,
2024-12-27 00:03:09 +00:00
scope_depth: -1,
match_depth: 0,
stack_depth: 0,
2024-12-27 00:03:09 +00:00
spans: vec![],
2024-12-16 04:28:57 +00:00
nodes: vec![],
ast: &ast.0,
span: ast.1,
loop_info: vec![],
2025-06-04 21:53:38 +00:00
upvalues: vec![],
2024-12-16 04:28:57 +00:00
src,
name,
tail_pos: false,
debug,
2024-12-16 04:28:57 +00:00
}
}
pub fn visit(&mut self, node: &'static Spanned<Ast>) {
let root_node = self.ast;
let root_span = self.span;
let (ast, span) = node;
self.ast = ast;
self.span = *span;
self.compile();
self.ast = root_node;
self.span = root_span;
}
2025-06-18 17:15:57 +00:00
fn jump(&mut self, op: Op, len: usize) {
let low = len as u8;
let high = (len >> 8) as u8;
self.emit_op(op);
self.chunk.bytecode.push(high);
self.chunk.bytecode.push(low);
}
fn stub_jump(&mut self, op: Op) -> usize {
use Op::*;
match op {
JumpIfFalse | JumpIfTrue | JumpIfZero => self.stack_depth -= 1,
_ => (),
}
let out = self.chunk.bytecode.len();
self.emit_op(op);
self.emit_byte(0xff);
self.emit_byte(0xff);
out
}
fn patch_jump(&mut self, i: usize, len: usize) {
let low = len as u8;
let high = (len >> 8) as u8;
self.chunk.bytecode[i + 1] = high;
self.chunk.bytecode[i + 2] = low;
}
2025-06-20 16:49:31 +00:00
pub fn emit_constant(&mut self, val: Value) {
let const_idx = if let Some(idx) = self.chunk.constants.iter().position(|v| *v == val) {
idx
} else {
self.chunk.constants.push(val);
self.chunk.constants.len() - 1
};
2025-06-20 19:56:13 +00:00
if const_idx > u16::MAX as usize {
panic!(
"internal Ludus compiler error: too many constants in chunk:{}:: {}",
self.span, self.ast
)
}
2025-05-30 18:57:51 +00:00
self.emit_op(Op::Constant);
2025-06-20 19:56:13 +00:00
let low = const_idx as u8;
let high = (const_idx >> 8) as u8;
self.chunk.bytecode.push(high);
self.chunk.bytecode.push(low);
self.stack_depth += 1;
}
fn reset_match(&mut self) {
self.emit_op(Op::ResetMatch);
self.match_depth = 0;
}
2024-12-23 00:07:42 +00:00
fn match_constant(&mut self, val: Value) {
2025-06-20 19:56:13 +00:00
let const_idx = match self.chunk.constants.iter().position(|v| *v == val) {
2024-12-23 00:07:42 +00:00
Some(idx) => idx,
None => {
self.chunk.constants.push(val);
self.chunk.constants.len() - 1
}
2024-12-23 00:07:42 +00:00
};
2025-06-20 19:56:13 +00:00
if const_idx > u16::MAX as usize {
2024-12-23 00:07:42 +00:00
panic!(
"internal Ludus compiler error: too many constants in chunk:{}:: {}",
self.span, self.ast
)
}
2025-05-30 18:57:51 +00:00
self.emit_op(Op::MatchConstant);
2025-06-20 19:56:13 +00:00
let low = const_idx as u8;
let high = (const_idx >> 8) as u8;
self.chunk.bytecode.push(high);
self.chunk.bytecode.push(low);
2024-12-23 00:07:42 +00:00
}
fn emit_op(&mut self, op: Op) {
2024-12-27 00:03:09 +00:00
self.chunk.bytecode.push(op as u8);
self.spans.push(self.span);
}
2024-12-27 04:46:06 +00:00
fn emit_byte(&mut self, byte: usize) {
self.chunk.bytecode.push(byte as u8);
self.spans.push(self.span);
}
fn len(&self) -> usize {
self.chunk.bytecode.len()
}
2025-06-20 16:49:31 +00:00
pub fn bind(&mut self, name: &'static str) {
self.msg(format!("binding `{name}` in {}", self.name));
self.msg(format!(
"stack depth: {}; match depth: {}",
self.stack_depth, self.match_depth
));
self.msg(format!(
"at stack index: {}",
self.stack_depth - self.match_depth - 1
));
let binding = Binding {
2024-12-18 04:45:39 +00:00
name,
depth: self.scope_depth,
stack_pos: self.stack_depth - self.match_depth - 1,
};
self.bindings.push(binding);
self.msg(format!(
"new locals: {}",
self.bindings
.iter()
.map(|binding| format!("{binding}"))
.collect::<Vec<_>>()
.join("|")
));
}
2025-06-04 21:53:38 +00:00
fn resolve_local(&self, name: &'static str) -> Option<usize> {
for binding in self.bindings.iter() {
if binding.name == name {
return Some(binding.stack_pos);
}
}
None
}
fn resolve_upvalue(&self, name: &'static str) -> Option<usize> {
self.upvalues.iter().position(|uv| *uv == name)
2025-06-04 21:53:38 +00:00
}
fn resolve_binding(&mut self, name: &'static str) {
self.msg(format!(
"resolving binding `{name}` in {}\nlocals: {}",
self.name,
self.bindings
.iter()
.map(|binding| format!("{binding}"))
.collect::<Vec<_>>()
.join("|")
));
if let Some(pos) = self.resolve_local(name) {
self.msg(format!("at locals position {pos}"));
self.emit_op(Op::PushBinding);
self.emit_byte(pos);
self.stack_depth += 1;
return;
}
if let Some(pos) = self.resolve_upvalue(name) {
self.msg(format!("as upvalue {pos}"));
self.emit_op(Op::GetUpvalue);
self.emit_byte(pos);
self.stack_depth += 1;
return;
}
if self.depth == 0 {
self.msg("as global".to_string());
self.emit_constant(Value::Keyword(name));
self.emit_op(Op::PushGlobal);
} else {
self.msg(format!("as enclosing upvalue {}", self.upvalues.len()));
self.emit_op(Op::GetUpvalue);
self.emit_byte(self.upvalues.len());
self.upvalues.push(name);
self.stack_depth += 1;
}
}
2025-06-23 00:26:08 +00:00
fn duplicate(&mut self) {
self.emit_op(Op::Duplicate);
self.stack_depth += 1;
}
fn pop(&mut self) {
self.emit_op(Op::Pop);
self.stack_depth -= 1;
}
fn pop_n(&mut self, n: usize) {
match n {
0 => (),
1 => self.pop(),
n => {
self.emit_op(Op::PopN);
self.emit_byte(n);
self.stack_depth -= n;
}
}
2024-12-18 04:45:39 +00:00
}
fn store(&mut self) {
self.emit_op(Op::Store);
self.stack_depth -= 1;
}
fn store_n(&mut self, n: usize) {
self.emit_op(Op::StoreN);
self.emit_byte(n);
self.stack_depth -= n;
}
fn load(&mut self) {
self.emit_op(Op::Load);
self.stack_depth += 1;
}
fn load_n(&mut self, n: usize) {
self.emit_op(Op::LoadN);
self.emit_byte(n);
self.stack_depth += n;
}
fn enter_scope(&mut self) {
self.scope_depth += 1;
}
fn leave_scope(&mut self) {
self.msg(format!("leaving scope {}", self.scope_depth));
while let Some(binding) = self.bindings.last() {
if binding.depth == self.scope_depth {
let unbound = self.bindings.pop();
self.msg(format!("releasing binding {}", unbound.unwrap()));
} else {
break;
}
}
self.scope_depth -= 1;
}
fn enter_loop(&mut self, arity: usize) {
self.loop_info
.push(LoopInfo::new(self.len(), self.stack_depth - arity));
2024-12-27 05:22:01 +00:00
}
fn leave_loop(&mut self) {
self.loop_info.pop();
}
2025-05-26 15:03:37 +00:00
fn loop_info(&self) -> LoopInfo {
self.loop_info.last().unwrap().clone()
2024-12-27 05:22:01 +00:00
}
2025-05-26 15:03:37 +00:00
fn loop_idx(&self) -> usize {
self.loop_info.last().unwrap().start
}
2025-05-26 15:03:37 +00:00
fn loop_root(&self) -> usize {
self.loop_info.last().unwrap().stack_root
2024-12-27 05:22:01 +00:00
}
fn msg(&mut self, str: String) {
if self.debug {
self.emit_op(Op::Msg);
self.emit_byte(self.chunk.msgs.len());
println!("{str}");
self.chunk.msgs.push(str);
}
}
fn report_depth(&mut self, label: &'static str) {
self.msg(format!("***{label} stack depth: {}", self.stack_depth));
}
fn report_depth_str(&mut self, label: String) {
self.msg(format!("***{label} stack depth: {}", self.stack_depth));
}
fn report_ast(&mut self, label: String, node: &'static Spanned<Ast>) {
self.msg(format!("***{label}: {}", node.0.show()))
}
2024-12-16 04:28:57 +00:00
pub fn compile(&mut self) {
use Ast::*;
match self.ast {
Error => unreachable!(),
Nil => {
self.emit_op(Op::Nil);
self.stack_depth += 1;
}
2024-12-16 04:28:57 +00:00
Number(n) => self.emit_constant(Value::Number(*n)),
Boolean(b) => {
self.emit_op(if *b { Op::True } else { Op::False });
self.stack_depth += 1;
}
2024-12-16 04:28:57 +00:00
String(s) => {
self.emit_constant(Value::Interned(s));
}
Keyword(s) => self.emit_constant(Value::Keyword(s)),
2024-12-16 04:28:57 +00:00
Block(lines) => {
let tail_pos = self.tail_pos;
self.tail_pos = false;
// increase the scope
self.enter_scope();
// stash the stack depth
let stack_depth = self.stack_depth;
// evaluate all the lines but the last
2024-12-18 06:28:23 +00:00
for expr in lines.iter().take(lines.len() - 1) {
// evaluate the expression
self.visit(expr);
// if it doesn't bind a name, pop the result from the stack
if !is_binding(expr) {
self.pop();
2024-12-18 06:28:23 +00:00
}
}
// now, evaluate the last expression in the block
let last_expr = lines.last().unwrap();
match last_expr {
// if the last expression is a let form,
// return the evaluated rhs instead of whatever is last on the stack
// we do this by pretending it's a binding
(Let(patt, expr), _) => {
// self.match_depth = 0;
self.visit(expr);
let expr_pos = self.stack_depth - 1;
self.report_ast("let binding: matching".to_string(), patt);
self.reset_match();
self.visit(patt);
self.emit_op(Op::PanicIfNoMatch);
self.emit_op(Op::PushBinding);
self.emit_byte(expr_pos);
self.stack_depth += 1;
}
// otherwise, just evaluate it and leave the value on the stack
_ => {
self.tail_pos = tail_pos;
self.visit(last_expr);
}
}
// store the value in the return register
self.store();
// reset the scope
self.leave_scope();
// reset the stack
self.report_depth("leaving block before pop");
self.msg(format!(
"popping back from {} to {}",
self.stack_depth, stack_depth,
));
2025-06-23 00:43:51 +00:00
self.pop_n(self.stack_depth - stack_depth);
// load the value from the return register
self.load();
2024-12-16 04:28:57 +00:00
}
If(cond, then, r#else) => {
let tail_pos = self.tail_pos;
self.tail_pos = false;
2024-12-16 04:28:57 +00:00
self.visit(cond);
self.report_depth("after condition");
let jif_idx = self.stub_jump(Op::JumpIfFalse);
// self.stack_depth -= 1;
self.tail_pos = tail_pos;
2024-12-16 04:28:57 +00:00
self.visit(then);
self.report_depth("after consequent");
let jump_idx = self.stub_jump(Op::Jump);
self.stack_depth -= 1;
self.visit(r#else);
self.report_depth("after alternative");
// self.stack_depth += 1;
2024-12-27 04:46:06 +00:00
let end_idx = self.len();
2024-12-16 04:28:57 +00:00
let jif_offset = jump_idx - jif_idx;
let jump_offset = end_idx - jump_idx - 3;
self.patch_jump(jif_idx, jif_offset);
self.patch_jump(jump_idx, jump_offset);
}
2024-12-18 04:45:39 +00:00
Let(patt, expr) => {
self.report_depth("before let binding");
// self.match_depth = 0;
// self.emit_op(Op::ResetMatch);
2024-12-18 04:45:39 +00:00
self.visit(expr);
self.report_depth("after let expr");
self.report_ast("let binding: matching".to_string(), patt);
self.reset_match();
2024-12-18 04:45:39 +00:00
self.visit(patt);
2024-12-23 00:07:42 +00:00
self.emit_op(Op::PanicIfNoMatch);
self.report_depth("after let binding");
2024-12-18 04:45:39 +00:00
}
WordPattern(name) => {
self.emit_op(Op::UnconditionalMatch);
2024-12-18 04:45:39 +00:00
self.bind(name);
}
Word(name) | Splat(name) => self.resolve_binding(name),
2024-12-18 04:45:39 +00:00
PlaceholderPattern => {
self.emit_op(Op::UnconditionalMatch);
2024-12-18 04:45:39 +00:00
}
2024-12-23 00:07:42 +00:00
NilPattern => {
self.emit_op(Op::MatchNil);
}
BooleanPattern(b) => {
if *b {
self.emit_op(Op::MatchTrue);
} else {
self.emit_op(Op::MatchFalse);
}
}
NumberPattern(n) => {
self.match_constant(Value::Number(*n));
}
KeywordPattern(s) => {
2024-12-27 00:03:09 +00:00
let existing_kw = self.chunk.keywords.iter().position(|kw| kw == s);
2024-12-23 00:07:42 +00:00
let kw_index = match existing_kw {
Some(index) => index,
2024-12-27 00:03:09 +00:00
None => self.chunk.keywords.len(),
2024-12-23 00:07:42 +00:00
};
2024-12-27 00:03:09 +00:00
if kw_index == self.chunk.keywords.len() {
self.chunk.keywords.push(s);
2024-12-23 00:07:42 +00:00
}
self.match_constant(Value::Keyword(s));
2024-12-23 00:07:42 +00:00
}
2025-05-30 18:57:51 +00:00
AsPattern(word, typ) => {
self.emit_constant(Value::Keyword(typ));
2025-05-30 18:57:51 +00:00
self.emit_op(Op::MatchType);
self.stack_depth -= 1;
self.bind(word);
}
2024-12-23 00:07:42 +00:00
StringPattern(s) => {
self.match_constant(Value::Interned(s));
2024-12-23 00:07:42 +00:00
}
TuplePattern(members) => {
// first, test the tuple against length
2025-06-19 16:37:29 +00:00
// check if we're splatted
// different opcodes w/ splats, but same logic
let mut is_splatted = false;
if let Some((Splattern(_), _)) = members.last() {
is_splatted = true;
self.emit_op(Op::MatchSplattedTuple);
} else {
self.emit_op(Op::MatchTuple);
}
self.emit_byte(members.len());
// skip everything if tuple lengths don't match
let before_load_tup_idx = self.stub_jump(Op::JumpIfNoMatch);
// set up the per-member conditional logic
let mut jump_idxes = vec![];
// stash match_depth, and set it to the tuple len
let match_depth = self.match_depth;
self.match_depth = members.len();
// load the tuple and update the stack len
2025-06-19 16:37:29 +00:00
if is_splatted {
self.emit_op(Op::LoadSplattedTuple);
self.emit_byte(members.len());
} else {
self.emit_op(Op::LoadTuple);
}
self.stack_depth += members.len();
// visit each member
for member in members {
// reduce the match depth to start
self.match_depth -= 1;
self.emit_op(Op::MatchDepth);
self.emit_byte(self.match_depth);
// visit the pattern member
self.visit(member);
// and jump if there's no match
jump_idxes.push(self.stub_jump(Op::JumpIfNoMatch));
}
// if we get here--not having jumped on no match--we're matched; jump the "no match" code
let jump_idx = self.stub_jump(Op::Jump);
// patch up the previous no match jumps to jump to clean-up code
for idx in jump_idxes {
self.patch_jump(idx, self.len() - idx - 3)
}
// pop everything that was pushed
// don't change the compiler stack representation, tho
// we need this as cleanup code with no matches
// the compiler should still have access to the bindings in this pattern
self.emit_op(Op::PopN);
self.emit_byte(members.len());
// patch up the tuple length match jump
self.patch_jump(before_load_tup_idx, self.len() - before_load_tup_idx - 3);
// patch up the yes-matches unconditional jump
self.patch_jump(jump_idx, self.len() - jump_idx - 3);
// finally, for any further matches (e.g. nested lists/tuples)
// add increase the match depth, since we've added a bunch
// of bindings to the stack
self.match_depth = match_depth + members.len();
}
ListPattern(members) => {
2025-06-19 16:06:47 +00:00
let mut is_splatted = false;
2025-06-19 16:18:09 +00:00
if let Some((Splattern(_), _)) = members.last() {
2025-06-19 16:06:47 +00:00
is_splatted = true;
2025-06-19 15:54:26 +00:00
self.emit_op(Op::MatchSplattedList)
} else {
self.emit_op(Op::MatchList);
}
// TODO: lists must be able to be longer than 256 elements; fix this
self.emit_byte(members.len());
let before_load_tup_idx = self.stub_jump(Op::JumpIfNoMatch);
let mut jump_idxes = vec![];
let match_depth = self.match_depth;
self.match_depth = members.len();
2025-06-19 16:06:47 +00:00
if is_splatted {
self.emit_op(Op::LoadSplattedList);
2025-06-19 16:18:09 +00:00
self.emit_byte(members.len());
2025-06-19 16:06:47 +00:00
} else {
self.emit_op(Op::LoadList);
}
self.stack_depth += members.len();
for member in members {
self.match_depth -= 1;
self.emit_op(Op::MatchDepth);
self.emit_byte(self.match_depth);
self.visit(member);
jump_idxes.push(self.stub_jump(Op::JumpIfNoMatch));
}
let jump_idx = self.stub_jump(Op::Jump);
for idx in jump_idxes {
self.patch_jump(idx, self.len() - idx - 3)
}
self.emit_op(Op::PopN);
self.emit_byte(members.len());
self.patch_jump(before_load_tup_idx, self.len() - before_load_tup_idx - 3);
self.patch_jump(jump_idx, self.len() - jump_idx - 3);
self.match_depth = match_depth + members.len();
}
DictPattern(pairs) => {
2025-06-19 16:44:29 +00:00
// here's an algorithm for dealing with splatted dicts
// check len to see it's at least as long as the pattern
// then, match against all the values
// then push the dict itself as last value
// and then emit an opcode and constant/keyword to OMIT that key from the dict
2025-06-19 19:56:23 +00:00
let mut is_splatted = false;
if let Some((Splattern(_), _)) = pairs.last() {
is_splatted = true;
self.emit_op(Op::MatchSplattedDict);
} else {
self.emit_op(Op::MatchDict);
}
self.emit_byte(pairs.len());
let before_load_dict_idx = self.stub_jump(Op::JumpIfNoMatch);
2025-06-18 19:00:46 +00:00
let mut jump_idxes = vec![];
2025-06-19 19:56:23 +00:00
let dict_stack_pos = self.stack_depth - self.match_depth - 1;
let mut splattern = None;
let mut pairs_len = pairs.len();
if is_splatted {
splattern = pairs.last();
pairs_len -= 1;
}
2025-06-19 22:26:44 +00:00
let match_depth = self.match_depth;
2025-06-19 19:56:23 +00:00
self.match_depth = 0;
for pair in pairs.iter().take(pairs_len) {
let (PairPattern(key, pattern), _) = pair else {
unreachable!()
};
self.emit_constant(Value::Keyword(key));
self.emit_op(Op::LoadDictValue);
self.emit_byte(dict_stack_pos);
self.visit(pattern);
jump_idxes.push(self.stub_jump(Op::JumpIfNoMatch));
}
2025-06-19 19:56:23 +00:00
if is_splatted {
// pull the dict out of the stack
// drop every value in the pattern
self.emit_op(Op::PushBinding);
self.emit_byte(dict_stack_pos);
self.stack_depth += 1;
2025-06-19 19:56:23 +00:00
for pair in pairs.iter().take(pairs_len) {
let (PairPattern(key, _), _) = pair else {
unreachable!()
};
self.emit_constant(Value::Keyword(key));
self.emit_op(Op::DropDictEntry);
2025-06-23 21:33:45 +00:00
self.stack_depth -= 1;
2025-06-19 19:56:23 +00:00
}
if let Some(splatt) = splattern {
self.visit(splatt);
}
}
self.match_depth = match_depth + pairs.len();
let jump_idx = self.stub_jump(Op::Jump);
2025-06-18 19:00:46 +00:00
for idx in jump_idxes {
self.patch_jump(idx, self.len() - idx - 3);
}
2025-06-18 19:00:46 +00:00
// do this explicitly to keep compiler & vm stacks in sync
self.emit_op(Op::Pop);
self.emit_byte(pairs.len());
2025-06-18 19:00:46 +00:00
self.patch_jump(before_load_dict_idx, self.len() - before_load_dict_idx - 3);
self.patch_jump(jump_idx, self.len() - jump_idx - 3);
}
2025-06-19 22:26:44 +00:00
Splattern(patt) => self.visit(patt),
InterpolatedPattern(parts, _) => {
// println!("An interpolated pattern of {} parts", parts.len());
let mut pattern = "".to_string();
let mut words = vec![];
for (part, _) in parts {
match part {
StringPart::Word(word) => {
// println!("wordpart: {word}");
words.push(word.clone());
pattern.push_str("(.*)");
}
StringPart::Data(data) => {
// println!("datapart: {data}");
let data = regex::escape(data);
pattern.push_str(data.as_str());
}
StringPart::Inline(..) => unreachable!(),
}
}
let re = Regex::new(pattern.as_str()).unwrap();
let moar_words = words.clone();
let string_pattern = StrPattern { words, re };
let pattern_idx = self.chunk.string_patterns.len();
self.chunk.string_patterns.push(string_pattern);
self.emit_op(Op::MatchString);
self.emit_byte(pattern_idx);
let jnm_idx = self.stub_jump(Op::JumpIfNoMatch);
self.emit_op(Op::PushStringMatches);
self.emit_byte(pattern_idx);
for word in moar_words {
let name: &'static str = std::string::String::leak(word);
let binding = Binding {
name,
depth: self.scope_depth,
stack_pos: self.stack_depth,
};
self.bindings.push(binding);
self.stack_depth += 1;
}
self.patch_jump(jnm_idx, self.len() - jnm_idx - 3);
}
PairPattern(_, _) => unreachable!(),
2024-12-23 00:07:42 +00:00
Tuple(members) => {
2025-06-23 04:27:50 +00:00
self.tail_pos = false;
2024-12-23 00:07:42 +00:00
for member in members {
self.visit(member);
}
self.emit_op(Op::PushTuple);
2024-12-27 04:46:06 +00:00
self.emit_byte(members.len());
self.stack_depth = self.stack_depth + 1 - members.len();
2024-12-23 00:07:42 +00:00
}
List(members) => {
2025-06-23 04:27:50 +00:00
self.tail_pos = false;
2025-06-05 17:05:07 +00:00
self.emit_op(Op::PushList);
self.stack_depth += 1;
2024-12-23 00:07:42 +00:00
for member in members {
self.visit(member);
2025-06-05 17:05:07 +00:00
if matches!(member, (Splat(..), _)) {
self.emit_op(Op::ConcatList);
} else {
self.emit_op(Op::AppendList);
}
self.stack_depth -= 1;
2024-12-23 00:07:42 +00:00
}
}
LBox(name, expr) => {
2025-06-23 04:27:50 +00:00
self.tail_pos = false;
2024-12-23 00:07:42 +00:00
self.visit(expr);
self.emit_op(Op::PushBox);
self.bind(name);
}
Dict(pairs) => {
2025-06-23 04:27:50 +00:00
self.tail_pos = false;
2025-06-05 17:24:32 +00:00
self.emit_op(Op::PushDict);
self.stack_depth += 1;
for pair in pairs.iter().rev() {
2024-12-23 00:07:42 +00:00
self.visit(pair);
2025-06-05 17:24:32 +00:00
if matches!(pair, (Splat(..), _)) {
self.emit_op(Op::ConcatDict);
self.stack_depth -= 1;
} else {
self.emit_op(Op::AppendDict);
self.stack_depth -= 2;
}
2024-12-23 00:07:42 +00:00
}
}
Pair(key, value) => {
2025-06-23 04:27:50 +00:00
self.tail_pos = false;
self.emit_constant(Value::Keyword(key));
2024-12-23 00:07:42 +00:00
self.visit(value);
}
// TODO: thread tail position through this
2024-12-23 00:07:42 +00:00
Synthetic(first, second, rest) => {
let tail_pos = self.tail_pos;
self.tail_pos = false;
2024-12-23 00:07:42 +00:00
match (&first.0, &second.0) {
(Word(name), Keyword(key)) => {
self.report_depth_str(format!("accessing keyword: {name} :{key}"));
2024-12-23 00:07:42 +00:00
self.visit(first);
self.visit(second);
self.emit_op(Op::GetKey);
self.stack_depth -= 1;
self.report_depth("after keyword access");
2024-12-23 00:07:42 +00:00
}
(Keyword(_), Arguments(args)) => {
self.visit(&args[0]);
self.visit(first);
self.emit_op(Op::GetKey);
self.stack_depth -= 1;
2024-12-23 00:07:42 +00:00
}
2025-05-28 20:37:25 +00:00
(Or, Arguments(args)) => {
let mut jump_idxes = vec![];
if !args.is_empty() {
2025-06-23 00:26:08 +00:00
let mut args = args.iter().rev();
let last = args.next().unwrap();
for arg in args.rev() {
2025-05-28 20:37:25 +00:00
self.visit(arg);
2025-06-23 00:26:08 +00:00
self.duplicate();
jump_idxes.push(self.stub_jump(Op::JumpIfTrue));
2025-06-23 00:26:08 +00:00
self.pop();
2025-05-28 20:37:25 +00:00
}
2025-06-23 00:26:08 +00:00
self.visit(last);
2025-05-28 20:37:25 +00:00
for idx in jump_idxes {
2025-06-23 00:26:08 +00:00
self.patch_jump(idx, self.len() - idx - 3);
2025-05-28 20:37:25 +00:00
}
} else {
self.emit_op(Op::False);
2025-06-23 00:26:08 +00:00
self.stack_depth += 1;
2025-05-28 20:37:25 +00:00
}
}
(And, Arguments(args)) => {
let mut jump_idxes = vec![];
if !args.is_empty() {
2025-06-23 00:26:08 +00:00
let mut args = args.iter().rev();
let last = args.next().unwrap();
for arg in args.rev() {
2025-05-28 20:37:25 +00:00
self.visit(arg);
2025-06-23 00:26:08 +00:00
self.duplicate();
jump_idxes.push(self.stub_jump(Op::JumpIfFalse));
2025-06-23 00:26:08 +00:00
self.pop();
2025-05-28 20:37:25 +00:00
}
2025-06-23 00:26:08 +00:00
self.visit(last);
2025-05-28 20:37:25 +00:00
for idx in jump_idxes {
2025-06-23 00:26:08 +00:00
self.patch_jump(idx, self.len() - idx - 3);
2025-05-28 20:37:25 +00:00
}
} else {
self.emit_op(Op::True);
2025-06-23 00:26:08 +00:00
self.stack_depth += 1;
2025-05-28 20:37:25 +00:00
}
}
(Word(fn_name), Arguments(args)) => {
self.report_depth_str(format!("calling function {fn_name}"));
if has_placeholder(args) {
let arity = args.len();
2025-06-03 19:48:13 +00:00
for arg in args {
2025-05-28 20:37:25 +00:00
self.visit(arg);
}
self.resolve_binding(fn_name);
self.emit_op(Op::Partial);
self.emit_byte(arity);
self.stack_depth -= args.len() - 1;
} else {
match get_builtin(fn_name, args.len()) {
Some(code) => {
for arg in args {
self.visit(arg);
}
self.emit_op(code);
self.stack_depth -= args.len() - 1;
}
None => {
let arity = args.len();
for arg in args {
self.visit(arg);
}
self.resolve_binding(fn_name);
// if we're in tail position AND there aren't any rest args, this should be a tail call (I think)
self.report_depth_str(format!("after {arity} args"));
if rest.is_empty() && tail_pos {
self.emit_op(Op::TailCall);
} else {
self.emit_op(Op::Call);
}
self.emit_byte(arity);
self.stack_depth -= arity;
2025-05-28 20:37:25 +00:00
}
}
}
}
2024-12-23 00:07:42 +00:00
_ => unreachable!(),
}
// the last term in rest should be in tail position if we are in tail position
let num_rest_terms = rest.len();
for (i, (term, _)) in rest.iter().enumerate() {
match term {
Keyword(str) => {
self.emit_constant(Value::Keyword(str));
self.emit_op(Op::GetKey);
self.stack_depth -= 1;
}
Arguments(args) => {
self.store();
let arity = args.len();
for arg in args {
self.visit(arg);
}
self.load();
if tail_pos && i == num_rest_terms - 1 {
self.emit_op(Op::TailCall)
} else {
self.emit_op(Op::Call);
}
self.emit_byte(arity);
self.stack_depth -= arity;
}
_ => unreachable!(),
}
2024-12-23 00:07:42 +00:00
}
self.tail_pos = tail_pos;
2024-12-23 00:07:42 +00:00
}
When(clauses) => {
let tail_pos = self.tail_pos;
2024-12-23 00:07:42 +00:00
let mut jump_idxes = vec![];
let mut clauses = clauses.iter();
while let Some((WhenClause(cond, body), _)) = clauses.next() {
self.tail_pos = false;
2024-12-23 00:07:42 +00:00
self.visit(cond.as_ref());
let jif_jump_idx = self.stub_jump(Op::JumpIfFalse);
self.tail_pos = tail_pos;
2024-12-23 00:07:42 +00:00
self.visit(body);
self.stack_depth -= 1;
jump_idxes.push(self.stub_jump(Op::Jump));
self.patch_jump(jif_jump_idx, self.len() - jif_jump_idx - 3);
2024-12-23 00:07:42 +00:00
}
self.emit_op(Op::PanicNoWhen);
for idx in jump_idxes {
self.patch_jump(idx, self.len() - idx - 3);
2024-12-23 00:07:42 +00:00
}
2025-05-23 21:59:09 +00:00
self.stack_depth += 1;
2024-12-23 00:07:42 +00:00
}
WhenClause(..) => unreachable!(),
2024-12-23 00:33:59 +00:00
Match(scrutinee, clauses) => {
let tail_pos = self.tail_pos;
self.tail_pos = false;
2024-12-23 00:33:59 +00:00
self.visit(scrutinee.as_ref());
let stack_depth = self.stack_depth;
2024-12-23 00:33:59 +00:00
let mut jump_idxes = vec![];
let mut clauses = clauses.iter();
2024-12-27 04:46:06 +00:00
while let Some((MatchClause(pattern, guard, body), _)) = clauses.next() {
self.tail_pos = false;
2025-06-04 22:51:07 +00:00
let mut no_match_jumps = vec![];
self.report_ast("match clause: ".to_string(), pattern);
// self.scope_depth += 1;
self.enter_scope();
self.match_depth = 0;
2024-12-23 00:33:59 +00:00
self.visit(pattern);
no_match_jumps.push(self.stub_jump(Op::JumpIfNoMatch));
2025-06-04 22:51:07 +00:00
if guard.is_some() {
let guard_expr: &'static Spanned<Ast> =
Box::leak(Box::new(guard.clone().unwrap()));
self.visit(guard_expr);
no_match_jumps.push(self.stub_jump(Op::JumpIfFalse));
2025-06-04 22:51:07 +00:00
}
self.tail_pos = tail_pos;
2025-06-04 22:51:07 +00:00
self.visit(body);
self.store();
self.leave_scope();
self.pop_n(self.stack_depth - stack_depth);
jump_idxes.push(self.stub_jump(Op::Jump));
2025-06-04 22:51:07 +00:00
for idx in no_match_jumps {
self.patch_jump(idx, self.len() - idx - 3);
2025-06-04 22:51:07 +00:00
}
2024-12-23 00:33:59 +00:00
}
self.emit_op(Op::PanicNoMatch);
2024-12-23 00:33:59 +00:00
for idx in jump_idxes {
self.patch_jump(idx, self.len() - idx - 3);
}
self.pop_n(self.stack_depth - stack_depth);
self.emit_op(Op::Load);
self.stack_depth += 1;
2024-12-23 00:33:59 +00:00
}
MatchClause(..) => unreachable!(),
2024-12-24 17:35:44 +00:00
Fn(name, body, doc) => {
let is_anon = name.is_empty();
let mut name = name;
if !is_anon {
let declared = self.chunk.constants.iter().any(|val| match val {
2025-06-20 19:35:09 +00:00
Value::Fn(lfn) => {
if matches!(lfn.as_ref(), LFn::Declared { .. }) {
lfn.name() == *name
} else {
false
}
}
_ => false,
});
if !declared {
let declaration = Value::Fn(Rc::new(LFn::Declared { name }));
self.emit_constant(declaration);
self.bind(name);
}
} else {
name = &"_anon";
}
2025-06-18 21:50:30 +00:00
2025-05-28 20:37:25 +00:00
let FnBody(fn_body) = &body.as_ref().0 else {
unreachable!()
};
2025-06-19 22:26:44 +00:00
let mut compilers: HashMap<u8, Compiler> = HashMap::new();
2025-05-28 20:37:25 +00:00
2025-06-04 21:53:38 +00:00
let mut upvalues = vec![];
2025-06-19 22:26:44 +00:00
let mut has_splat = false;
2025-05-28 20:37:25 +00:00
for clause in fn_body {
2025-06-04 22:55:40 +00:00
let MatchClause(pattern, guard, clause_body) = &clause.0 else {
2025-05-28 20:37:25 +00:00
unreachable!()
};
let full_pattern = pattern;
2025-05-28 20:37:25 +00:00
let TuplePattern(pattern) = &pattern.0 else {
unreachable!()
};
2025-06-19 22:26:44 +00:00
if matches!(pattern.last(), Some((Splattern(_), _))) {
has_splat = true;
};
let arity = pattern.len() as u8;
let compiler = match compilers.get_mut(&arity) {
Some(compiler) => compiler,
None => {
let mut compiler = Compiler::new(
clause,
2025-06-21 22:33:14 +00:00
name,
self.src,
self.depth + 1,
self.chunk.env.clone(),
self.debug,
);
compiler.reset_match();
// compiler.emit_op(Op::ResetMatch);
compilers.insert(arity, compiler);
compilers.get_mut(&arity).unwrap()
}
};
compiler.tail_pos = false;
2025-06-19 22:26:44 +00:00
compiler.stack_depth += arity as usize;
compiler.scope_depth += 1;
2025-06-19 22:26:44 +00:00
compiler.match_depth = arity as usize;
2025-06-04 21:53:38 +00:00
std::mem::swap(&mut upvalues, &mut compiler.upvalues);
let mut tup_jump_idxes = vec![];
compiler.report_ast("function clause matching: ".to_string(), full_pattern);
for member in pattern {
compiler.match_depth -= 1;
compiler.emit_op(Op::MatchDepth);
compiler.emit_byte(compiler.match_depth);
compiler.visit(member);
2025-06-18 21:50:30 +00:00
tup_jump_idxes.push(compiler.stub_jump(Op::JumpIfNoMatch));
}
if pattern.is_empty() {
compiler.emit_op(Op::UnconditionalMatch);
}
2025-06-18 21:50:30 +00:00
let jump_idx = compiler.stub_jump(Op::Jump);
for idx in tup_jump_idxes {
2025-06-18 21:50:30 +00:00
compiler.patch_jump(idx, compiler.len() - idx - 3);
}
// compiler.emit_op(Op::PopN);
// compiler.emit_byte(arity as usize);
2025-06-18 21:50:30 +00:00
compiler.patch_jump(jump_idx, compiler.len() - jump_idx - 3);
2025-06-04 22:55:40 +00:00
let mut no_match_jumps = vec![];
2025-06-18 21:50:30 +00:00
no_match_jumps.push(compiler.stub_jump(Op::JumpIfNoMatch));
2025-06-04 22:55:40 +00:00
if guard.is_some() {
let guard_expr: &'static Spanned<Ast> =
Box::leak(Box::new(guard.clone().unwrap()));
compiler.visit(guard_expr);
2025-06-18 21:50:30 +00:00
no_match_jumps.push(compiler.stub_jump(Op::JumpIfFalse));
2025-06-04 22:55:40 +00:00
compiler.stack_depth -= 1;
}
compiler.tail_pos = true;
compiler.visit(clause_body);
compiler.store();
compiler.scope_depth -= 1;
while let Some(binding) = compiler.bindings.last() {
if binding.depth > compiler.scope_depth {
compiler.bindings.pop();
} else {
break;
}
}
2025-06-18 21:50:30 +00:00
compiler.pop_n(compiler.stack_depth);
compiler.stack_depth = 0;
compiler.emit_op(Op::Return);
2025-06-04 22:55:40 +00:00
for idx in no_match_jumps {
2025-06-18 21:50:30 +00:00
compiler.patch_jump(idx, compiler.len() - idx - 3);
2025-06-04 22:55:40 +00:00
}
// compiler.scope_depth -= 1;
2025-06-04 21:53:38 +00:00
std::mem::swap(&mut compiler.upvalues, &mut upvalues);
2025-05-28 20:37:25 +00:00
}
2025-06-19 22:26:44 +00:00
let mut compilers = compilers.into_iter().collect::<Vec<_>>();
compilers.sort_by(|(a, _), (b, _)| a.cmp(b));
2025-06-19 22:26:44 +00:00
let mut arities = vec![];
let mut chunks = vec![];
for (arity, mut compiler) in compilers {
compiler.emit_op(Op::PanicNoMatch);
let chunk = compiler.chunk;
if self.debug {
println!("=== function chuncktion: {name}/{arity} ===");
chunk.dissasemble();
}
2025-06-19 22:26:44 +00:00
arities.push(arity);
chunks.push(chunk);
2024-12-24 17:35:44 +00:00
}
2024-12-26 23:41:54 +00:00
2025-06-19 22:26:44 +00:00
let splat = if has_splat {
arities.iter().fold(0, |max, curr| max.max(*curr))
} else {
0
};
2025-06-04 21:53:38 +00:00
let lfn = crate::value::LFn::Defined {
2024-12-24 17:35:44 +00:00
name,
doc: *doc,
2025-06-19 22:26:44 +00:00
arities,
chunks,
splat,
2025-06-04 21:53:38 +00:00
closed: RefCell::new(vec![]),
2024-12-24 17:35:44 +00:00
};
2024-12-26 23:41:54 +00:00
2025-06-20 04:56:43 +00:00
let the_fn = Value::Fn(Rc::new(lfn));
// self.emit_constant(the_fn);
// self.bind(name);
if !is_anon {
let declaration_idx = self
.chunk
.constants
.iter()
.position(|val| match val {
2025-06-20 19:35:09 +00:00
Value::Fn(lfn) => {
if matches!(lfn.as_ref(), LFn::Declared { .. }) {
lfn.name() == *name
} else {
false
}
}
_ => false,
})
.unwrap();
self.chunk.constants[declaration_idx] = the_fn;
// if the function been forward-declared, bring it to the top of the stack
if declaration_idx < self.chunk.constants.len() - 1 {
self.resolve_binding(name);
}
} else {
self.emit_constant(the_fn)
}
2025-06-04 21:53:38 +00:00
for upvalue in upvalues {
self.resolve_binding(upvalue);
2025-06-04 21:53:38 +00:00
self.emit_op(Op::SetUpvalue);
self.stack_depth -= 1;
2025-06-04 21:53:38 +00:00
}
2024-12-24 17:35:44 +00:00
}
FnDeclaration(name) => {
2025-06-04 21:53:38 +00:00
let lfn = Value::Fn(Rc::new(LFn::Declared { name }));
2024-12-26 23:41:54 +00:00
self.emit_constant(lfn);
self.bind(name);
2024-12-24 17:35:44 +00:00
}
FnBody(_) => unreachable!(),
Repeat(times, body) => {
let tail_pos = self.tail_pos;
self.tail_pos = false;
self.visit(times);
self.emit_op(Op::ToInt);
// skip the decrement the first time
self.emit_op(Op::Jump);
2025-06-18 21:50:30 +00:00
self.emit_byte(0);
2024-12-27 04:46:06 +00:00
self.emit_byte(1);
// begin repeat
self.emit_op(Op::Decrement);
2024-12-27 04:46:06 +00:00
let repeat_begin = self.len();
2025-06-23 21:37:46 +00:00
self.duplicate();
2025-06-18 21:50:30 +00:00
let jiz_idx = self.stub_jump(Op::JumpIfZero);
// compile the body
self.visit(body);
// pop whatever value the body returns
self.pop();
2025-06-18 21:50:30 +00:00
let jump_back = self.stub_jump(Op::JumpBack);
// set jump points
2025-06-18 21:50:30 +00:00
self.patch_jump(jump_back, self.len() - repeat_begin - 2);
self.patch_jump(jiz_idx, self.len() - repeat_begin - 4);
self.pop();
self.emit_constant(Value::Nil);
self.tail_pos = tail_pos;
}
2024-12-27 05:22:01 +00:00
Loop(value, clauses) => {
self.report_depth("entering loop");
let tail_pos = self.tail_pos;
self.tail_pos = false;
2024-12-27 05:22:01 +00:00
//algo:
//first, put the values on the stack
let (Ast::Tuple(members), _) = value.as_ref() else {
unreachable!()
};
for member in members {
2024-12-27 05:22:01 +00:00
self.visit(member);
}
self.report_depth("after loop args");
2024-12-27 05:22:01 +00:00
let arity = members.len();
// self.emit_op(Op::StoreN);
// self.emit_byte(members.len());
self.store_n(arity);
2025-05-26 15:03:37 +00:00
let stack_depth = self.stack_depth;
self.report_depth("loop: after store");
2024-12-27 05:22:01 +00:00
//then, save the beginning of the loop
// self.emit_op(Op::Load);
self.load_n(arity);
self.enter_loop(arity);
// self.stack_depth += arity;
2024-12-27 05:22:01 +00:00
//next, compile each clause:
let mut clauses = clauses.iter();
let mut jump_idxes = vec![];
while let Some((Ast::MatchClause(pattern, guard, body), _)) = clauses.next() {
self.tail_pos = false;
self.report_depth("loop: after load");
self.reset_match();
// self.emit_op(Op::ResetMatch);
self.enter_scope();
// self.scope_depth += 1;
2024-12-27 05:22:01 +00:00
let (Ast::TuplePattern(members), _) = pattern.as_ref() else {
unreachable!()
};
2025-05-26 15:03:37 +00:00
self.match_depth = arity;
let mut jnm_idxes = vec![];
self.report_ast("loop clause matching: ".to_string(), pattern);
2025-05-26 15:03:37 +00:00
for member in members {
self.match_depth -= 1;
self.emit_op(Op::MatchDepth);
2025-05-26 15:03:37 +00:00
self.emit_byte(self.match_depth);
self.visit(member);
jnm_idxes.push(self.stub_jump(Op::JumpIfNoMatch));
2025-05-26 15:03:37 +00:00
}
if guard.is_some() {
let guard_expr: &'static Spanned<Ast> =
Box::leak(Box::new(guard.clone().unwrap()));
self.visit(guard_expr);
jnm_idxes.push(self.stub_jump(Op::JumpIfFalse));
2025-05-26 15:03:37 +00:00
}
self.tail_pos = tail_pos;
self.report_depth("loop: before body");
self.visit(body);
self.report_depth("loop: after body, before store");
// self.emit_op(Op::Store);
self.store();
self.report_depth("loop: after body, after store");
self.leave_scope();
self.report_depth_str(format!(
"resetting the stack after loop from {} to {stack_depth}",
self.stack_depth,
));
self.pop_n(self.stack_depth - stack_depth);
// while self.stack_depth > stack_depth {
// self.pop();
// }
jump_idxes.push(self.stub_jump(Op::Jump));
for idx in jnm_idxes {
self.patch_jump(idx, self.len() - idx - 3);
}
self.stack_depth += arity;
2024-12-27 05:22:01 +00:00
}
self.emit_op(Op::PanicNoMatch);
2025-05-26 15:03:37 +00:00
for idx in jump_idxes {
self.patch_jump(idx, self.len() - idx - 3);
2025-05-26 15:03:37 +00:00
}
self.report_depth("before loop arity adjustment");
2025-05-26 15:03:37 +00:00
self.stack_depth -= arity;
// pop back to the original depth before load
// i.e. clear loop args
// self.pop();
// self.emit_op(Op::Load);
self.load();
// self.stack_depth += 1;
self.leave_loop();
self.report_depth("at very end of loop after load");
}
2025-05-26 13:16:47 +00:00
Recur(args) => {
// self.emit_op(Op::Nothing);
self.report_depth("recur: before args");
let tail_pos = self.tail_pos;
self.tail_pos = false;
let mut argnum = 0;
for arg in args {
self.msg(format!("recur arg: {argnum}"));
argnum += 1;
2025-05-26 15:03:37 +00:00
self.visit(arg);
}
self.report_depth("recur: after args");
self.store_n(args.len());
self.report_depth("recur: after store");
self.msg(format!("loop root depth: {}", self.loop_root()));
self.pop_n(self.stack_depth - self.loop_root());
self.report_depth("recur: after stack reset");
self.load_n(args.len());
self.report_depth("recur: after load, end of compilation");
self.jump(Op::JumpBack, self.len() - self.loop_idx());
self.tail_pos = tail_pos;
2024-12-27 05:22:01 +00:00
}
2025-05-30 15:44:32 +00:00
Panic(msg) => {
self.visit(msg);
self.emit_op(Op::Panic);
}
Interpolated(parts) => {
self.emit_op(Op::EmptyString);
self.stack_depth += 1;
for part in parts {
let str = &part.0;
match str {
StringPart::Inline(_) => unreachable!(),
StringPart::Data(str) => {
let allocated = Value::String(Rc::new(str.clone()));
self.emit_constant(allocated);
self.emit_op(Op::ConcatStrings);
}
StringPart::Word(word) => {
self.resolve_binding(word);
2025-05-30 15:44:32 +00:00
self.emit_op(Op::Stringify);
self.emit_op(Op::ConcatStrings);
}
}
self.stack_depth -= 1;
2025-05-30 15:44:32 +00:00
}
}
2025-06-05 16:15:49 +00:00
Do(terms) => {
let mut terms = terms.iter();
let first = terms.next().unwrap();
let mut terms = terms.rev();
let last = terms.next().unwrap();
let terms = terms.rev();
2025-06-05 16:15:49 +00:00
// put the first value on the stack
let tail_pos = self.tail_pos;
self.tail_pos = false;
2025-06-05 16:15:49 +00:00
self.visit(first);
for term in terms {
self.visit(term);
self.emit_op(Op::Call);
self.emit_byte(1);
self.stack_depth -= 1;
}
self.visit(last);
if tail_pos {
self.emit_op(Op::TailCall)
} else {
self.emit_op(Op::Call);
}
self.emit_byte(1);
self.tail_pos = tail_pos;
self.stack_depth -= 1;
2025-06-05 16:15:49 +00:00
}
Placeholder => {
self.emit_op(Op::Nothing);
}
And | Or | Arguments(..) => unreachable!(),
}
}
2024-12-16 04:28:57 +00:00
pub fn disassemble(&self) {
println!("=== chunk: {} ===", self.name);
self.chunk.dissasemble();
}
}