2024-12-15 21:37:51 +00:00
|
|
|
use crate::parser::Ast;
|
2025-05-30 15:44:32 +00:00
|
|
|
use crate::parser::StringPart;
|
2024-12-15 21:37:51 +00:00
|
|
|
use crate::spans::Spanned;
|
|
|
|
use crate::value::*;
|
|
|
|
use chumsky::prelude::SimpleSpan;
|
|
|
|
use num_derive::{FromPrimitive, ToPrimitive};
|
|
|
|
use num_traits::FromPrimitive;
|
2025-06-06 03:26:42 +00:00
|
|
|
use regex::Regex;
|
2025-06-04 21:53:38 +00:00
|
|
|
use std::cell::RefCell;
|
2025-05-30 21:02:55 +00:00
|
|
|
use std::collections::HashMap;
|
2024-12-24 17:35:44 +00:00
|
|
|
use std::rc::Rc;
|
2024-12-15 21:37:51 +00:00
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
2024-12-16 04:28:57 +00:00
|
|
|
pub enum Op {
|
2025-05-23 04:09:35 +00:00
|
|
|
Noop,
|
2025-06-05 20:10:40 +00:00
|
|
|
Nothing,
|
2024-12-23 00:07:42 +00:00
|
|
|
Nil,
|
|
|
|
True,
|
|
|
|
False,
|
2024-12-15 21:37:51 +00:00
|
|
|
Constant,
|
2024-12-16 04:28:57 +00:00
|
|
|
Jump,
|
|
|
|
JumpIfFalse,
|
2025-05-28 20:37:25 +00:00
|
|
|
JumpIfTrue,
|
2024-12-18 04:45:39 +00:00
|
|
|
Pop,
|
2025-05-23 04:09:35 +00:00
|
|
|
PopN,
|
2024-12-18 04:45:39 +00:00
|
|
|
PushBinding,
|
2025-06-20 18:29:31 +00:00
|
|
|
PushGlobal,
|
2024-12-18 04:45:39 +00:00
|
|
|
Store,
|
2025-05-26 15:03:37 +00:00
|
|
|
StoreAt,
|
2025-05-28 20:37:25 +00:00
|
|
|
Stash,
|
2024-12-18 04:45:39 +00:00
|
|
|
Load,
|
2024-12-23 00:07:42 +00:00
|
|
|
ResetMatch,
|
2025-06-03 21:30:00 +00:00
|
|
|
Match,
|
2024-12-23 00:07:42 +00:00
|
|
|
MatchNil,
|
|
|
|
MatchTrue,
|
|
|
|
MatchFalse,
|
|
|
|
PanicIfNoMatch,
|
|
|
|
MatchConstant,
|
2025-06-06 03:26:42 +00:00
|
|
|
MatchString,
|
|
|
|
PushStringMatches,
|
2025-05-30 18:57:51 +00:00
|
|
|
MatchType,
|
2024-12-23 00:07:42 +00:00
|
|
|
MatchTuple,
|
2025-06-19 16:37:29 +00:00
|
|
|
MatchSplattedTuple,
|
2024-12-23 00:07:42 +00:00
|
|
|
PushTuple,
|
2025-05-23 04:09:35 +00:00
|
|
|
LoadTuple,
|
2025-06-19 16:37:29 +00:00
|
|
|
LoadSplattedTuple,
|
2025-05-25 20:30:20 +00:00
|
|
|
MatchList,
|
2025-06-19 15:54:26 +00:00
|
|
|
MatchSplattedList,
|
2025-05-25 20:30:20 +00:00
|
|
|
LoadList,
|
2025-06-19 16:06:47 +00:00
|
|
|
LoadSplattedList,
|
2024-12-23 00:07:42 +00:00
|
|
|
PushList,
|
2025-06-05 17:05:07 +00:00
|
|
|
AppendList,
|
|
|
|
ConcatList,
|
2024-12-23 00:07:42 +00:00
|
|
|
PushDict,
|
2025-06-05 17:24:32 +00:00
|
|
|
AppendDict,
|
|
|
|
ConcatDict,
|
2025-05-25 23:09:21 +00:00
|
|
|
LoadDictValue,
|
|
|
|
MatchDict,
|
2025-06-19 19:56:23 +00:00
|
|
|
MatchSplattedDict,
|
|
|
|
DropDictEntry,
|
2024-12-23 00:07:42 +00:00
|
|
|
PushBox,
|
|
|
|
GetKey,
|
|
|
|
PanicNoWhen,
|
2024-12-23 00:33:59 +00:00
|
|
|
JumpIfNoMatch,
|
2025-05-23 04:09:35 +00:00
|
|
|
JumpIfMatch,
|
2024-12-23 00:33:59 +00:00
|
|
|
PanicNoMatch,
|
2024-12-27 04:33:57 +00:00
|
|
|
TypeOf,
|
|
|
|
JumpBack,
|
|
|
|
JumpIfZero,
|
|
|
|
Duplicate,
|
|
|
|
Decrement,
|
|
|
|
Truncate,
|
2024-12-27 05:47:22 +00:00
|
|
|
MatchDepth,
|
2025-05-30 15:44:32 +00:00
|
|
|
Panic,
|
|
|
|
EmptyString,
|
|
|
|
ConcatStrings,
|
|
|
|
Stringify,
|
2025-05-27 18:15:12 +00:00
|
|
|
|
2025-05-28 20:37:25 +00:00
|
|
|
Call,
|
2025-06-20 00:26:16 +00:00
|
|
|
TailCall,
|
2025-06-02 00:01:42 +00:00
|
|
|
Return,
|
2025-06-05 20:10:40 +00:00
|
|
|
Partial,
|
2025-05-28 20:37:25 +00:00
|
|
|
|
2025-05-27 18:15:12 +00:00
|
|
|
Eq,
|
|
|
|
Add,
|
|
|
|
Sub,
|
|
|
|
Mult,
|
|
|
|
Div,
|
|
|
|
Unbox,
|
|
|
|
BoxStore,
|
|
|
|
Assert,
|
|
|
|
Get,
|
|
|
|
At,
|
2025-05-28 20:37:25 +00:00
|
|
|
|
|
|
|
Not,
|
2025-06-03 23:26:01 +00:00
|
|
|
Print,
|
2025-06-04 21:53:38 +00:00
|
|
|
SetUpvalue,
|
|
|
|
GetUpvalue,
|
2025-05-28 20:37:25 +00:00
|
|
|
// Inc,
|
|
|
|
// Dec,
|
|
|
|
// Gt,
|
|
|
|
// Gte,
|
|
|
|
// Lt,
|
|
|
|
// Lte,
|
|
|
|
// Mod,
|
|
|
|
// Round,
|
|
|
|
// Ceil,
|
|
|
|
// Floor,
|
|
|
|
// Random,
|
|
|
|
// Sqrt,
|
|
|
|
|
|
|
|
// Assoc,
|
|
|
|
// Concat,
|
|
|
|
// Conj,
|
|
|
|
// Count,
|
|
|
|
// Disj,
|
|
|
|
// Dissoc,
|
|
|
|
// Range,
|
|
|
|
// Rest,
|
|
|
|
// Slice,
|
|
|
|
|
|
|
|
// "atan_2" math/atan2
|
|
|
|
// "chars" chars
|
|
|
|
// "cos" math/cos
|
|
|
|
// "doc" doc
|
|
|
|
// "downcase" string/ascii-lower
|
|
|
|
// "pi" math/pi
|
|
|
|
// "show" show
|
|
|
|
// "sin" math/sin
|
|
|
|
// "split" string/split
|
|
|
|
// "str_slice" string/slice
|
|
|
|
// "tan" math/tan
|
|
|
|
// "trim" string/trim
|
|
|
|
// "triml" string/triml
|
|
|
|
// "trimr" string/trimr
|
|
|
|
// "upcase" string/ascii-upper
|
2024-12-16 04:28:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Display for Op {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
use Op::*;
|
2024-12-23 00:07:42 +00:00
|
|
|
let rep = match self {
|
2025-05-23 04:09:35 +00:00
|
|
|
Noop => "noop",
|
2025-06-05 20:10:40 +00:00
|
|
|
Nothing => "nothing",
|
2024-12-23 00:07:42 +00:00
|
|
|
Nil => "nil",
|
|
|
|
True => "true",
|
|
|
|
False => "false",
|
|
|
|
Constant => "constant",
|
|
|
|
Jump => "jump",
|
|
|
|
JumpIfFalse => "jump_if_false",
|
2025-05-28 20:37:25 +00:00
|
|
|
JumpIfTrue => "jump_if_true",
|
2024-12-23 00:07:42 +00:00
|
|
|
Pop => "pop",
|
2025-05-23 04:09:35 +00:00
|
|
|
PopN => "pop_n",
|
2024-12-23 00:07:42 +00:00
|
|
|
PushBinding => "push_binding",
|
2025-06-20 18:29:31 +00:00
|
|
|
PushGlobal => "push_global",
|
2024-12-23 00:07:42 +00:00
|
|
|
Store => "store",
|
2025-05-26 15:03:37 +00:00
|
|
|
StoreAt => "store_at",
|
2025-05-28 20:37:25 +00:00
|
|
|
Stash => "stash",
|
2024-12-23 00:07:42 +00:00
|
|
|
Load => "load",
|
2025-06-03 21:30:00 +00:00
|
|
|
Match => "match",
|
2024-12-23 00:07:42 +00:00
|
|
|
MatchNil => "match_nil",
|
|
|
|
MatchTrue => "match_true",
|
|
|
|
MatchFalse => "match_false",
|
|
|
|
ResetMatch => "reset_match",
|
|
|
|
PanicIfNoMatch => "panic_if_no_match",
|
|
|
|
MatchConstant => "match_constant",
|
2025-06-06 03:26:42 +00:00
|
|
|
MatchString => "match_string",
|
|
|
|
PushStringMatches => "push_string_matches",
|
2025-05-30 18:57:51 +00:00
|
|
|
MatchType => "match_type",
|
2024-12-23 00:07:42 +00:00
|
|
|
MatchTuple => "match_tuple",
|
2025-06-19 16:37:29 +00:00
|
|
|
MatchSplattedTuple => "match_splatted_tuple",
|
2024-12-23 00:07:42 +00:00
|
|
|
PushTuple => "push_tuple",
|
2025-05-23 04:09:35 +00:00
|
|
|
LoadTuple => "load_tuple",
|
2025-06-19 16:37:29 +00:00
|
|
|
LoadSplattedTuple => "load_splatted_tuple",
|
2025-05-25 20:30:20 +00:00
|
|
|
MatchList => "match_list",
|
2025-06-19 15:54:26 +00:00
|
|
|
MatchSplattedList => "match_splatted_list",
|
2025-05-25 20:30:20 +00:00
|
|
|
LoadList => "load_list",
|
2025-06-19 16:06:47 +00:00
|
|
|
LoadSplattedList => "load_splatted_list",
|
2024-12-23 00:07:42 +00:00
|
|
|
PushList => "push_list",
|
2025-06-05 17:05:07 +00:00
|
|
|
AppendList => "append_list",
|
|
|
|
ConcatList => "concat_list",
|
2024-12-23 00:07:42 +00:00
|
|
|
PushDict => "push_dict",
|
2025-06-05 17:24:32 +00:00
|
|
|
AppendDict => "append_dict",
|
|
|
|
ConcatDict => "concat_dict",
|
2025-05-25 23:09:21 +00:00
|
|
|
LoadDictValue => "load_dict_value",
|
|
|
|
MatchDict => "match_dict",
|
2025-06-19 19:56:23 +00:00
|
|
|
MatchSplattedDict => "match_splatted_dict",
|
|
|
|
DropDictEntry => "drop_dict_entry",
|
2024-12-23 00:07:42 +00:00
|
|
|
PushBox => "push_box",
|
|
|
|
GetKey => "get_key",
|
|
|
|
PanicNoWhen => "panic_no_when",
|
2024-12-23 00:33:59 +00:00
|
|
|
JumpIfNoMatch => "jump_if_no_match",
|
2025-05-23 04:09:35 +00:00
|
|
|
JumpIfMatch => "jump_if_match",
|
2024-12-23 00:33:59 +00:00
|
|
|
PanicNoMatch => "panic_no_match",
|
2024-12-27 04:33:57 +00:00
|
|
|
TypeOf => "type_of",
|
|
|
|
JumpBack => "jump_back",
|
|
|
|
JumpIfZero => "jump_if_zero",
|
|
|
|
Decrement => "decrement",
|
|
|
|
Truncate => "truncate",
|
|
|
|
Duplicate => "duplicate",
|
2024-12-27 05:47:22 +00:00
|
|
|
MatchDepth => "match_depth",
|
2025-05-30 15:44:32 +00:00
|
|
|
Panic => "panic",
|
|
|
|
EmptyString => "empty_string",
|
|
|
|
ConcatStrings => "concat_strings",
|
|
|
|
Stringify => "stringify",
|
2025-06-03 23:26:01 +00:00
|
|
|
Print => "print",
|
2025-05-27 18:15:12 +00:00
|
|
|
|
|
|
|
Eq => "eq",
|
|
|
|
Add => "add",
|
|
|
|
Sub => "sub",
|
|
|
|
Mult => "mult",
|
|
|
|
Div => "div",
|
|
|
|
Unbox => "unbox",
|
|
|
|
BoxStore => "box_store",
|
|
|
|
Assert => "assert",
|
|
|
|
Get => "get",
|
|
|
|
At => "at",
|
2025-05-28 20:37:25 +00:00
|
|
|
|
|
|
|
Not => "not",
|
|
|
|
|
|
|
|
Call => "call",
|
2025-06-02 00:01:42 +00:00
|
|
|
Return => "return",
|
2025-06-05 20:10:40 +00:00
|
|
|
Partial => "partial",
|
2025-06-20 00:26:16 +00:00
|
|
|
TailCall => "tail_call",
|
2025-06-04 21:53:38 +00:00
|
|
|
|
|
|
|
SetUpvalue => "set_upvalue",
|
|
|
|
GetUpvalue => "get_upvalue",
|
2024-12-23 00:07:42 +00:00
|
|
|
};
|
|
|
|
write!(f, "{rep}")
|
2024-12-16 04:28:57 +00:00
|
|
|
}
|
2024-12-15 21:37:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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,
|
2025-05-23 04:09:35 +00:00
|
|
|
stack_pos: usize,
|
2024-12-16 04:28:57 +00:00
|
|
|
}
|
|
|
|
|
2025-06-04 21:53:38 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
pub struct Upvalue {
|
|
|
|
name: &'static str,
|
|
|
|
stack_pos: usize,
|
|
|
|
}
|
|
|
|
|
2025-06-06 03:26:42 +00:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct StrPattern {
|
|
|
|
pub words: Vec<String>,
|
|
|
|
pub re: Regex,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
2024-12-23 00:51:02 +00:00
|
|
|
pub struct Chunk {
|
2024-12-16 04:28:57 +00:00
|
|
|
pub constants: Vec<Value>,
|
2024-12-15 21:37:51 +00:00
|
|
|
pub bytecode: Vec<u8>,
|
|
|
|
pub keywords: Vec<&'static str>,
|
2025-06-06 03:26:42 +00:00
|
|
|
pub string_patterns: Vec<StrPattern>,
|
2025-06-20 18:29:31 +00:00
|
|
|
pub env: imbl::HashMap<&'static str, Value>,
|
2024-12-27 00:03:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Chunk {
|
2025-06-02 00:01:42 +00:00
|
|
|
pub fn dissasemble_instr(&self, i: &mut usize) {
|
|
|
|
let op = Op::from_u8(self.bytecode[*i]).unwrap();
|
2024-12-27 00:03:09 +00:00
|
|
|
use Op::*;
|
|
|
|
match op {
|
2025-05-28 20:37:25 +00:00
|
|
|
Pop | Store | Stash | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
2025-06-03 21:30:00 +00:00
|
|
|
| PanicIfNoMatch | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch | TypeOf
|
|
|
|
| Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq | Add | Sub
|
|
|
|
| Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString
|
2025-06-05 17:05:07 +00:00
|
|
|
| ConcatStrings | Stringify | MatchType | Return | Match | Print | AppendList
|
2025-06-20 18:29:31 +00:00
|
|
|
| ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing | PushGlobal => {
|
2024-12-27 00:03:09 +00:00
|
|
|
println!("{i:04}: {op}")
|
|
|
|
}
|
|
|
|
Constant | MatchConstant => {
|
2025-06-02 00:01:42 +00:00
|
|
|
let next = self.bytecode[*i + 1];
|
2025-06-03 20:23:37 +00:00
|
|
|
let value = &self.constants[next as usize].show();
|
2025-06-05 20:10:40 +00:00
|
|
|
println!("{i:04}: {:16} {next:03}: {value}", op.to_string());
|
2025-06-02 00:01:42 +00:00
|
|
|
*i += 1;
|
2024-12-27 00:03:09 +00:00
|
|
|
}
|
2025-06-19 16:37:29 +00:00
|
|
|
PushBinding | MatchTuple | MatchSplattedTuple | LoadSplattedTuple | MatchList
|
2025-06-19 19:56:23 +00:00
|
|
|
| MatchSplattedList | LoadSplattedList | MatchDict | MatchSplattedDict
|
|
|
|
| DropDictEntry | LoadDictValue | PushTuple | PushBox | MatchDepth | PopN | StoreAt
|
2025-06-20 00:26:16 +00:00
|
|
|
| Call | SetUpvalue | GetUpvalue | Partial | MatchString | PushStringMatches
|
|
|
|
| TailCall => {
|
2025-06-02 00:01:42 +00:00
|
|
|
let next = self.bytecode[*i + 1];
|
2025-06-05 20:10:40 +00:00
|
|
|
println!("{i:04}: {:16} {next:03}", op.to_string());
|
2025-06-02 00:01:42 +00:00
|
|
|
*i += 1;
|
2024-12-27 00:03:09 +00:00
|
|
|
}
|
2025-06-18 17:15:57 +00:00
|
|
|
Jump | JumpIfFalse | JumpIfTrue | JumpIfNoMatch | JumpIfMatch | JumpBack
|
|
|
|
| JumpIfZero => {
|
|
|
|
let high = self.bytecode[*i + 1];
|
|
|
|
let low = self.bytecode[*i + 2];
|
2025-06-18 18:47:00 +00:00
|
|
|
let len = ((high as u16) << 8) + low as u16;
|
|
|
|
println!("{i:04}: {:16} {len:05}", op.to_string());
|
2025-06-18 17:15:57 +00:00
|
|
|
*i += 2;
|
|
|
|
}
|
2024-12-27 00:03:09 +00:00
|
|
|
}
|
|
|
|
}
|
2024-12-27 04:33:57 +00:00
|
|
|
|
2025-06-02 00:01:42 +00:00
|
|
|
pub fn dissasemble(&self) {
|
|
|
|
println!("IDX | CODE | INFO");
|
|
|
|
let mut i = 0;
|
|
|
|
while i < self.bytecode.len() {
|
|
|
|
self.dissasemble_instr(&mut i);
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-06-03 20:23:37 +00:00
|
|
|
// pub fn kw_from(&self, kw: &str) -> Option<Value> {
|
|
|
|
// self.kw_index_from(kw).map(Value::Keyword)
|
|
|
|
// }
|
2024-12-27 04:33:57 +00:00
|
|
|
|
2025-06-04 21:53:38 +00:00
|
|
|
// pub fn kw_index_from(&self, kw: &str) -> Option<usize> {
|
|
|
|
// self.keywords.iter().position(|s| *s == kw)
|
|
|
|
// }
|
2024-12-27 00:03:09 +00:00
|
|
|
}
|
|
|
|
|
2025-05-23 04:09:35 +00:00
|
|
|
#[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 }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-05-27 18:15:12 +00:00
|
|
|
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),
|
2025-05-28 20:37:25 +00:00
|
|
|
("not", 1) => Some(Op::Not),
|
2025-06-03 23:26:01 +00:00
|
|
|
("print!", 1) => Some(Op::Print),
|
2025-05-28 20:37:25 +00:00
|
|
|
|
2025-05-27 18:15:12 +00:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-06-06 03:26:42 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2025-06-04 21:53:38 +00:00
|
|
|
pub struct Compiler<'a> {
|
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>,
|
2024-12-23 00:51:02 +00:00
|
|
|
pub nodes: Vec<&'static Ast>,
|
|
|
|
pub ast: &'static Ast,
|
2024-12-15 21:37:51 +00:00
|
|
|
pub span: SimpleSpan,
|
|
|
|
pub src: &'static str,
|
|
|
|
pub name: &'static str,
|
2025-06-04 21:53:38 +00:00
|
|
|
pub enclosing: Option<&'a Compiler<'a>>,
|
|
|
|
pub upvalues: Vec<Upvalue>,
|
2025-05-23 04:09:35 +00:00
|
|
|
loop_info: Vec<LoopInfo>,
|
2025-06-20 00:26:16 +00:00
|
|
|
tail_pos: bool,
|
2024-12-15 21:37:51 +00:00
|
|
|
}
|
|
|
|
|
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 {
|
2025-06-20 16:32:15 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2025-06-05 20:10:40 +00:00
|
|
|
fn has_placeholder(args: &[Spanned<Ast>]) -> bool {
|
|
|
|
args.iter().any(|arg| matches!(arg, (Ast::Placeholder, _)))
|
|
|
|
}
|
|
|
|
|
2025-06-04 21:53:38 +00:00
|
|
|
impl<'a> Compiler<'a> {
|
|
|
|
pub fn new(
|
|
|
|
ast: &'static Spanned<Ast>,
|
|
|
|
name: &'static str,
|
|
|
|
src: &'static str,
|
|
|
|
enclosing: Option<&'a Compiler>,
|
2025-06-20 18:29:31 +00:00
|
|
|
env: imbl::HashMap<&'static str, Value>,
|
2025-06-04 21:53:38 +00:00
|
|
|
) -> Compiler<'a> {
|
2024-12-27 00:03:09 +00:00
|
|
|
let chunk = Chunk {
|
2024-12-16 04:28:57 +00:00
|
|
|
constants: vec![],
|
|
|
|
bytecode: vec![],
|
2025-06-06 03:26:42 +00:00
|
|
|
keywords: vec![],
|
|
|
|
string_patterns: vec![],
|
2025-06-20 18:29:31 +00:00
|
|
|
env,
|
2024-12-27 00:03:09 +00:00
|
|
|
};
|
|
|
|
Compiler {
|
|
|
|
chunk,
|
|
|
|
bindings: vec![],
|
|
|
|
scope_depth: -1,
|
2025-05-23 04:09:35 +00:00
|
|
|
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,
|
2025-05-23 04:09:35 +00:00
|
|
|
loop_info: vec![],
|
2025-06-04 21:53:38 +00:00
|
|
|
enclosing,
|
|
|
|
upvalues: vec![],
|
2024-12-16 04:28:57 +00:00
|
|
|
src,
|
|
|
|
name,
|
2025-06-20 00:26:16 +00:00
|
|
|
tail_pos: false,
|
2024-12-16 04:28:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-23 00:51:02 +00:00
|
|
|
pub fn visit(&mut self, node: &'static Spanned<Ast>) {
|
2024-12-15 21:37:51 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2025-06-18 19:24:30 +00:00
|
|
|
fn stub_jump(&mut self, op: Op) -> usize {
|
2025-06-18 18:47:00 +00:00
|
|
|
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) {
|
2025-06-03 20:23:37 +00:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
|
|
|
if const_idx > u8::MAX as usize {
|
2024-12-15 21:37:51 +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::Constant);
|
2025-06-03 20:23:37 +00:00
|
|
|
self.emit_byte(const_idx);
|
2025-05-23 04:09:35 +00:00
|
|
|
self.stack_depth += 1;
|
2024-12-15 21:37:51 +00:00
|
|
|
}
|
|
|
|
|
2024-12-23 00:07:42 +00:00
|
|
|
fn match_constant(&mut self, val: Value) {
|
2024-12-27 00:03:09 +00:00
|
|
|
let constant_index = match self.chunk.constants.iter().position(|v| *v == val) {
|
2024-12-23 00:07:42 +00:00
|
|
|
Some(idx) => idx,
|
2025-06-03 20:23:37 +00:00
|
|
|
None => {
|
|
|
|
self.chunk.constants.push(val);
|
|
|
|
self.chunk.constants.len() - 1
|
|
|
|
}
|
2024-12-23 00:07:42 +00:00
|
|
|
};
|
|
|
|
if constant_index > u8::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::MatchConstant);
|
|
|
|
self.emit_byte(constant_index);
|
2024-12-23 00:07:42 +00:00
|
|
|
}
|
|
|
|
|
2024-12-15 21:37:51 +00:00
|
|
|
fn emit_op(&mut self, op: Op) {
|
2024-12-27 00:03:09 +00:00
|
|
|
self.chunk.bytecode.push(op as u8);
|
2024-12-15 21:37:51 +00:00
|
|
|
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) {
|
2025-05-23 04:09:35 +00:00
|
|
|
let binding = Binding {
|
2024-12-18 04:45:39 +00:00
|
|
|
name,
|
|
|
|
depth: self.scope_depth,
|
2025-05-23 04:09:35 +00:00
|
|
|
stack_pos: self.stack_depth - self.match_depth - 1,
|
|
|
|
};
|
|
|
|
println!("{:?}", binding);
|
|
|
|
println!("stack: {}; match: {}", self.stack_depth, self.match_depth);
|
|
|
|
self.bindings.push(binding);
|
|
|
|
}
|
|
|
|
|
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 == name)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_upvalue(&self, name: &'static str) -> Upvalue {
|
|
|
|
let local = self.bindings.iter().find(|b| b.name == name);
|
|
|
|
match local {
|
|
|
|
Some(binding) => Upvalue {
|
|
|
|
name,
|
|
|
|
stack_pos: binding.stack_pos,
|
|
|
|
},
|
|
|
|
None => self.enclosing.unwrap().get_upvalue(name),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-06-05 20:10:40 +00:00
|
|
|
fn resolve_binding(&mut self, name: &'static str) {
|
2025-06-20 18:29:31 +00:00
|
|
|
if let Some(pos) = self.resolve_local(name) {
|
|
|
|
self.emit_op(Op::PushBinding);
|
|
|
|
self.emit_byte(pos);
|
|
|
|
self.stack_depth += 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if let Some(pos) = self.resolve_upvalue(name) {
|
|
|
|
self.emit_op(Op::GetUpvalue);
|
|
|
|
self.emit_byte(pos);
|
|
|
|
self.stack_depth += 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if self.chunk.env.contains_key(name) {
|
|
|
|
self.emit_constant(Value::Keyword(name));
|
|
|
|
self.emit_op(Op::PushGlobal);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let upvalue = self.get_upvalue(name);
|
|
|
|
self.emit_op(Op::GetUpvalue);
|
|
|
|
self.emit_byte(self.upvalues.len());
|
|
|
|
self.upvalues.push(upvalue);
|
|
|
|
self.stack_depth += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_binding_old(&mut self, name: &'static str) {
|
2025-06-05 20:10:40 +00:00
|
|
|
match self.resolve_local(name) {
|
|
|
|
Some(position) => {
|
|
|
|
self.emit_op(Op::PushBinding);
|
|
|
|
self.emit_byte(position);
|
|
|
|
self.stack_depth += 1;
|
|
|
|
}
|
|
|
|
None => match self.resolve_upvalue(name) {
|
|
|
|
Some(position) => {
|
2025-06-20 00:26:16 +00:00
|
|
|
println!("resolved upvalue: {name} at {position}");
|
2025-06-05 20:10:40 +00:00
|
|
|
self.emit_op(Op::GetUpvalue);
|
|
|
|
self.emit_byte(position);
|
|
|
|
self.stack_depth += 1;
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
println!("setting upvalue: {name}");
|
|
|
|
let upvalue = self.get_upvalue(name);
|
|
|
|
self.emit_op(Op::GetUpvalue);
|
2025-06-20 00:26:16 +00:00
|
|
|
self.emit_byte(self.upvalues.len());
|
2025-06-05 20:10:40 +00:00
|
|
|
self.upvalues.push(upvalue);
|
|
|
|
dbg!(&self.upvalues);
|
|
|
|
self.stack_depth += 1;
|
|
|
|
}
|
|
|
|
},
|
2025-06-04 21:53:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-05-23 04:09:35 +00:00
|
|
|
fn pop(&mut self) {
|
|
|
|
self.emit_op(Op::Pop);
|
|
|
|
self.stack_depth -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pop_n(&mut self, n: usize) {
|
2025-06-18 19:24:30 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2024-12-27 05:22:01 +00:00
|
|
|
fn enter_loop(&mut self) {
|
2025-05-23 04:09:35 +00:00
|
|
|
self.loop_info
|
2025-05-26 15:03:37 +00:00
|
|
|
.push(LoopInfo::new(self.len(), self.stack_depth));
|
2024-12-27 05:22:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn leave_loop(&mut self) {
|
2025-05-23 04:09:35 +00:00
|
|
|
self.loop_info.pop();
|
|
|
|
}
|
|
|
|
|
2025-05-26 15:03:37 +00:00
|
|
|
fn loop_info(&self) -> LoopInfo {
|
2025-05-23 04:09:35 +00:00
|
|
|
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 {
|
2025-05-23 04:09:35 +00:00
|
|
|
self.loop_info.last().unwrap().start
|
|
|
|
}
|
|
|
|
|
2025-05-26 15:03:37 +00:00
|
|
|
fn loop_root(&self) -> usize {
|
2025-05-23 04:09:35 +00:00
|
|
|
self.loop_info.last().unwrap().stack_root
|
2024-12-27 05:22:01 +00:00
|
|
|
}
|
|
|
|
|
2024-12-16 04:28:57 +00:00
|
|
|
pub fn compile(&mut self) {
|
|
|
|
use Ast::*;
|
2024-12-15 21:37:51 +00:00
|
|
|
match self.ast {
|
2024-12-27 04:33:57 +00:00
|
|
|
Error => unreachable!(),
|
2025-05-23 04:09:35 +00:00
|
|
|
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)),
|
2025-05-23 04:09:35 +00:00
|
|
|
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) => {
|
2025-06-03 20:23:37 +00:00
|
|
|
self.emit_constant(Value::Interned(s));
|
2024-12-15 21:37:51 +00:00
|
|
|
}
|
2025-06-03 20:23:37 +00:00
|
|
|
Keyword(s) => self.emit_constant(Value::Keyword(s)),
|
2024-12-16 04:28:57 +00:00
|
|
|
Block(lines) => {
|
2025-06-20 00:26:16 +00:00
|
|
|
let tail_pos = self.tail_pos;
|
|
|
|
self.tail_pos = false;
|
2025-06-18 18:47:00 +00:00
|
|
|
// increase the scope
|
2024-12-16 04:28:57 +00:00
|
|
|
self.scope_depth += 1;
|
2025-06-18 18:47:00 +00:00
|
|
|
// stash the stack depth
|
2025-05-23 17:42:29 +00:00
|
|
|
let stack_depth = self.stack_depth;
|
2025-06-18 18:47:00 +00:00
|
|
|
// evaluate all the lines but the last
|
2024-12-18 06:28:23 +00:00
|
|
|
for expr in lines.iter().take(lines.len() - 1) {
|
2025-06-18 18:47:00 +00:00
|
|
|
// evaluate the expression
|
|
|
|
self.visit(expr);
|
|
|
|
// if it doesn't bind a name, pop the result from the stack
|
|
|
|
if !is_binding(expr) {
|
2025-05-23 04:09:35 +00:00
|
|
|
self.pop();
|
2024-12-18 06:28:23 +00:00
|
|
|
}
|
2024-12-15 21:37:51 +00:00
|
|
|
}
|
2025-06-18 18:47:00 +00:00
|
|
|
|
|
|
|
// now, evaluate the last expression in the block
|
2024-12-27 04:33:57 +00:00
|
|
|
let last_expr = lines.last().unwrap();
|
2025-06-04 22:27:17 +00:00
|
|
|
match last_expr {
|
2025-06-18 18:47:00 +00:00
|
|
|
// 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
|
2025-06-04 22:27:17 +00:00
|
|
|
(Let(patt, expr), _) => {
|
|
|
|
self.match_depth = 0;
|
|
|
|
self.emit_op(Op::ResetMatch);
|
|
|
|
self.visit(expr);
|
|
|
|
let expr_pos = self.stack_depth - 1;
|
|
|
|
self.visit(patt);
|
|
|
|
self.emit_op(Op::PanicIfNoMatch);
|
|
|
|
self.emit_op(Op::PushBinding);
|
|
|
|
self.emit_byte(expr_pos);
|
|
|
|
self.stack_depth += 1;
|
|
|
|
}
|
2025-06-18 18:47:00 +00:00
|
|
|
// otherwise, just evaluate it and leave the value on the stack
|
2025-06-04 22:27:17 +00:00
|
|
|
_ => {
|
2025-06-20 00:26:16 +00:00
|
|
|
self.tail_pos = tail_pos;
|
2025-06-04 22:27:17 +00:00
|
|
|
self.visit(last_expr);
|
|
|
|
}
|
2024-12-27 04:33:57 +00:00
|
|
|
}
|
2025-06-18 18:47:00 +00:00
|
|
|
|
|
|
|
// we've made a new value, so increase the stack level in the compiler
|
2025-06-04 22:27:17 +00:00
|
|
|
self.stack_depth += 1;
|
2025-06-18 18:47:00 +00:00
|
|
|
|
|
|
|
// store the value in the return register
|
2024-12-18 04:45:39 +00:00
|
|
|
self.emit_op(Op::Store);
|
2025-06-04 22:27:17 +00:00
|
|
|
|
2025-06-18 18:47:00 +00:00
|
|
|
// reset the scope
|
|
|
|
self.scope_depth -= 1;
|
2024-12-18 04:45:39 +00:00
|
|
|
while let Some(binding) = self.bindings.last() {
|
|
|
|
if binding.depth > self.scope_depth {
|
2025-06-04 22:27:17 +00:00
|
|
|
self.bindings.pop();
|
2024-12-18 04:45:39 +00:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2025-06-18 18:47:00 +00:00
|
|
|
// reset the stack
|
|
|
|
self.pop_n(self.stack_depth - stack_depth - 1);
|
|
|
|
// load the value from the return register
|
2024-12-18 04:45:39 +00:00
|
|
|
self.emit_op(Op::Load);
|
2024-12-16 04:28:57 +00:00
|
|
|
}
|
|
|
|
If(cond, then, r#else) => {
|
2025-06-20 00:26:16 +00:00
|
|
|
let tail_pos = self.tail_pos;
|
|
|
|
self.tail_pos = false;
|
2024-12-16 04:28:57 +00:00
|
|
|
self.visit(cond);
|
2025-06-18 19:24:30 +00:00
|
|
|
let jif_idx = self.stub_jump(Op::JumpIfFalse);
|
2025-05-23 04:09:35 +00:00
|
|
|
self.stack_depth -= 1;
|
2025-06-20 00:26:16 +00:00
|
|
|
self.tail_pos = tail_pos;
|
2024-12-16 04:28:57 +00:00
|
|
|
self.visit(then);
|
2025-06-18 19:24:30 +00:00
|
|
|
let jump_idx = self.stub_jump(Op::Jump);
|
2024-12-16 04:28:57 +00:00
|
|
|
self.visit(r#else);
|
2025-05-23 04:09:35 +00:00
|
|
|
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;
|
2025-06-18 18:47:00 +00:00
|
|
|
let jump_offset = end_idx - jump_idx - 1;
|
|
|
|
self.patch_jump(jif_idx, jif_offset);
|
|
|
|
self.patch_jump(jump_idx, jump_offset);
|
2024-12-15 21:37:51 +00:00
|
|
|
}
|
2024-12-18 04:45:39 +00:00
|
|
|
Let(patt, expr) => {
|
2025-05-23 04:09:35 +00:00
|
|
|
self.match_depth = 0;
|
2024-12-23 00:07:42 +00:00
|
|
|
self.emit_op(Op::ResetMatch);
|
2024-12-18 04:45:39 +00:00
|
|
|
self.visit(expr);
|
|
|
|
self.visit(patt);
|
2024-12-23 00:07:42 +00:00
|
|
|
self.emit_op(Op::PanicIfNoMatch);
|
2024-12-18 04:45:39 +00:00
|
|
|
}
|
|
|
|
WordPattern(name) => {
|
2025-06-03 21:30:00 +00:00
|
|
|
self.emit_op(Op::Match);
|
2024-12-18 04:45:39 +00:00
|
|
|
self.bind(name);
|
|
|
|
}
|
2025-06-05 20:10:40 +00:00
|
|
|
Word(name) | Splat(name) => self.resolve_binding(name),
|
2024-12-18 04:45:39 +00:00
|
|
|
PlaceholderPattern => {
|
2025-06-03 21:30:00 +00:00
|
|
|
self.emit_op(Op::Match);
|
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
|
|
|
}
|
2025-06-03 20:23:37 +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) => {
|
2025-06-03 20:23:37 +00:00
|
|
|
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) => {
|
2025-06-03 20:23:37 +00:00
|
|
|
self.match_constant(Value::Interned(s));
|
2024-12-23 00:07:42 +00:00
|
|
|
}
|
2025-05-23 04:09:35 +00:00
|
|
|
TuplePattern(members) => {
|
2025-06-18 18:47:00 +00:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2025-05-23 04:09:35 +00:00
|
|
|
self.emit_byte(members.len());
|
2025-06-18 18:47:00 +00:00
|
|
|
// skip everything if tuple lengths don't match
|
2025-06-18 19:24:30 +00:00
|
|
|
let before_load_tup_idx = self.stub_jump(Op::JumpIfNoMatch);
|
2025-06-18 18:47:00 +00:00
|
|
|
|
|
|
|
// set up the per-member conditional logic
|
2025-05-23 04:09:35 +00:00
|
|
|
let mut jump_idxes = vec![];
|
2025-06-18 18:47:00 +00:00
|
|
|
// 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);
|
|
|
|
}
|
2025-05-23 04:09:35 +00:00
|
|
|
self.stack_depth += members.len();
|
2025-06-18 18:47:00 +00:00
|
|
|
|
|
|
|
// visit each member
|
2025-05-23 04:09:35 +00:00
|
|
|
for member in members {
|
2025-06-18 18:47:00 +00:00
|
|
|
// reduce the match depth to start
|
2025-05-23 04:09:35 +00:00
|
|
|
self.match_depth -= 1;
|
|
|
|
self.emit_op(Op::MatchDepth);
|
|
|
|
self.emit_byte(self.match_depth);
|
2025-06-18 18:47:00 +00:00
|
|
|
// visit the pattern member
|
2025-05-23 04:09:35 +00:00
|
|
|
self.visit(member);
|
2025-06-18 18:47:00 +00:00
|
|
|
// and jump if there's no match
|
2025-06-18 19:24:30 +00:00
|
|
|
jump_idxes.push(self.stub_jump(Op::JumpIfNoMatch));
|
2025-05-23 04:09:35 +00:00
|
|
|
}
|
2025-06-18 18:47:00 +00:00
|
|
|
|
|
|
|
// if we get here--not having jumped on no match--we're matched; jump the "no match" code
|
2025-06-18 19:24:30 +00:00
|
|
|
let jump_idx = self.stub_jump(Op::Jump);
|
2025-06-18 18:47:00 +00:00
|
|
|
|
|
|
|
// patch up the previous no match jumps to jump to clean-up code
|
2025-05-23 04:09:35 +00:00
|
|
|
for idx in jump_idxes {
|
2025-06-18 18:47:00 +00:00
|
|
|
self.patch_jump(idx, self.len() - idx - 2)
|
2025-05-23 04:09:35 +00:00
|
|
|
}
|
2025-06-18 18:47:00 +00:00
|
|
|
// 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);
|
2025-06-18 18:52:15 +00:00
|
|
|
|
|
|
|
// 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();
|
2025-05-23 04:09:35 +00:00
|
|
|
}
|
2025-05-25 20:30:20 +00:00
|
|
|
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
|
2025-05-25 20:30:20 +00:00
|
|
|
self.emit_byte(members.len());
|
2025-06-18 19:24:30 +00:00
|
|
|
let before_load_tup_idx = self.stub_jump(Op::JumpIfNoMatch);
|
2025-06-18 18:52:15 +00:00
|
|
|
|
2025-05-25 20:30:20 +00:00
|
|
|
let mut jump_idxes = vec![];
|
2025-06-18 18:52:15 +00:00
|
|
|
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);
|
|
|
|
}
|
2025-05-25 20:30:20 +00:00
|
|
|
self.stack_depth += members.len();
|
2025-06-18 18:52:15 +00:00
|
|
|
|
2025-05-25 20:30:20 +00:00
|
|
|
for member in members {
|
|
|
|
self.match_depth -= 1;
|
|
|
|
self.emit_op(Op::MatchDepth);
|
|
|
|
self.emit_byte(self.match_depth);
|
|
|
|
self.visit(member);
|
2025-06-18 19:24:30 +00:00
|
|
|
jump_idxes.push(self.stub_jump(Op::JumpIfNoMatch));
|
2025-05-25 20:30:20 +00:00
|
|
|
}
|
2025-06-18 18:52:15 +00:00
|
|
|
|
2025-06-18 19:24:30 +00:00
|
|
|
let jump_idx = self.stub_jump(Op::Jump);
|
2025-06-18 18:52:15 +00:00
|
|
|
|
2025-05-25 20:30:20 +00:00
|
|
|
for idx in jump_idxes {
|
2025-06-18 18:52:15 +00:00
|
|
|
self.patch_jump(idx, self.len() - idx - 2)
|
2025-05-25 20:30:20 +00:00
|
|
|
}
|
2025-06-18 18:52:15 +00:00
|
|
|
|
|
|
|
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();
|
2025-05-25 20:30:20 +00:00
|
|
|
}
|
2025-05-25 23:09:21 +00:00
|
|
|
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);
|
|
|
|
}
|
2025-05-25 23:09:21 +00:00
|
|
|
self.emit_byte(pairs.len());
|
2025-06-18 19:24:30 +00:00
|
|
|
let before_load_dict_idx = self.stub_jump(Op::JumpIfNoMatch);
|
2025-06-18 19:00:46 +00:00
|
|
|
|
2025-05-25 23:09:21 +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) {
|
2025-05-25 23:09:21 +00:00
|
|
|
let (PairPattern(key, pattern), _) = pair else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
2025-06-03 20:23:37 +00:00
|
|
|
self.emit_constant(Value::Keyword(key));
|
2025-05-25 23:09:21 +00:00
|
|
|
self.emit_op(Op::LoadDictValue);
|
|
|
|
self.emit_byte(dict_stack_pos);
|
|
|
|
self.visit(pattern);
|
2025-06-18 19:24:30 +00:00
|
|
|
jump_idxes.push(self.stub_jump(Op::JumpIfNoMatch));
|
2025-05-25 23:09:21 +00:00
|
|
|
}
|
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);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(splatt) = splattern {
|
|
|
|
self.visit(splatt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.match_depth = match_depth + pairs.len();
|
|
|
|
|
2025-06-18 19:24:30 +00:00
|
|
|
let jump_idx = self.stub_jump(Op::Jump);
|
2025-06-18 19:00:46 +00:00
|
|
|
|
2025-05-25 23:09:21 +00:00
|
|
|
for idx in jump_idxes {
|
2025-06-18 19:00:46 +00:00
|
|
|
self.patch_jump(idx, self.len() - idx - 2);
|
2025-05-25 23:09:21 +00:00
|
|
|
}
|
2025-06-18 19:00:46 +00:00
|
|
|
|
|
|
|
self.emit_op(Op::PopN);
|
|
|
|
self.emit_byte(pairs.len());
|
|
|
|
|
|
|
|
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-06 03:26:42 +00:00
|
|
|
}
|
2025-06-19 22:26:44 +00:00
|
|
|
Splattern(patt) => self.visit(patt),
|
2025-06-06 03:26:42 +00:00
|
|
|
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);
|
|
|
|
|
2025-06-18 19:24:30 +00:00
|
|
|
let jnm_idx = self.stub_jump(Op::JumpIfNoMatch);
|
2025-06-06 03:26:42 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2025-06-18 19:24:30 +00:00
|
|
|
self.patch_jump(jnm_idx, self.len() - jnm_idx - 3);
|
2025-05-25 23:09:21 +00:00
|
|
|
}
|
|
|
|
PairPattern(_, _) => unreachable!(),
|
2024-12-23 00:07:42 +00:00
|
|
|
Tuple(members) => {
|
|
|
|
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());
|
2025-05-23 04:09:35 +00:00
|
|
|
self.stack_depth = self.stack_depth + 1 - members.len();
|
2024-12-23 00:07:42 +00:00
|
|
|
}
|
|
|
|
List(members) => {
|
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) => {
|
|
|
|
self.visit(expr);
|
|
|
|
self.emit_op(Op::PushBox);
|
|
|
|
self.bind(name);
|
|
|
|
}
|
|
|
|
Dict(pairs) => {
|
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-03 20:23:37 +00:00
|
|
|
self.emit_constant(Value::Keyword(key));
|
2024-12-23 00:07:42 +00:00
|
|
|
self.visit(value);
|
|
|
|
}
|
2025-06-20 00:26:16 +00:00
|
|
|
// TODO: thread tail position through this
|
2024-12-23 00:07:42 +00:00
|
|
|
Synthetic(first, second, rest) => {
|
2025-06-20 00:26:16 +00:00
|
|
|
let tail_pos = self.tail_pos;
|
|
|
|
self.tail_pos = false;
|
2024-12-23 00:07:42 +00:00
|
|
|
match (&first.0, &second.0) {
|
|
|
|
(Word(_), Keyword(_)) => {
|
|
|
|
self.visit(first);
|
|
|
|
self.visit(second);
|
|
|
|
self.emit_op(Op::GetKey);
|
2025-05-27 18:15:12 +00:00
|
|
|
self.stack_depth -= 1;
|
2024-12-23 00:07:42 +00:00
|
|
|
}
|
|
|
|
(Keyword(_), Arguments(args)) => {
|
|
|
|
self.visit(&args[0]);
|
|
|
|
self.visit(first);
|
|
|
|
self.emit_op(Op::GetKey);
|
2025-05-27 18:15:12 +00:00
|
|
|
self.stack_depth -= 1;
|
2024-12-23 00:07:42 +00:00
|
|
|
}
|
2025-05-28 20:37:25 +00:00
|
|
|
(Or, Arguments(args)) => {
|
|
|
|
let stack_depth = self.stack_depth;
|
|
|
|
let mut jump_idxes = vec![];
|
|
|
|
if !args.is_empty() {
|
|
|
|
for arg in args {
|
|
|
|
self.visit(arg);
|
|
|
|
self.emit_op(Op::Stash);
|
2025-06-18 19:24:30 +00:00
|
|
|
jump_idxes.push(self.stub_jump(Op::JumpIfTrue));
|
2025-05-28 20:37:25 +00:00
|
|
|
}
|
|
|
|
for idx in jump_idxes {
|
2025-06-18 19:24:30 +00:00
|
|
|
self.patch_jump(idx, self.len() - idx);
|
2025-05-28 20:37:25 +00:00
|
|
|
}
|
|
|
|
self.emit_op(Op::Load);
|
|
|
|
} else {
|
|
|
|
self.emit_op(Op::False);
|
|
|
|
}
|
|
|
|
self.stack_depth = stack_depth + 1;
|
|
|
|
}
|
|
|
|
(And, Arguments(args)) => {
|
|
|
|
let stack_depth = self.stack_depth;
|
|
|
|
let mut jump_idxes = vec![];
|
|
|
|
if !args.is_empty() {
|
|
|
|
for arg in args {
|
|
|
|
self.visit(arg);
|
|
|
|
self.emit_op(Op::Stash);
|
2025-06-18 19:24:30 +00:00
|
|
|
jump_idxes.push(self.stub_jump(Op::JumpIfFalse));
|
2025-05-28 20:37:25 +00:00
|
|
|
}
|
|
|
|
for idx in jump_idxes {
|
2025-06-18 19:24:30 +00:00
|
|
|
self.patch_jump(idx, self.len() - idx);
|
2025-05-28 20:37:25 +00:00
|
|
|
}
|
|
|
|
self.emit_op(Op::Load);
|
|
|
|
} else {
|
|
|
|
self.emit_op(Op::True);
|
|
|
|
}
|
|
|
|
self.stack_depth = stack_depth + 1;
|
|
|
|
}
|
2025-06-05 20:10:40 +00:00
|
|
|
(Word(fn_name), Arguments(args)) => {
|
|
|
|
if has_placeholder(args) {
|
2025-06-03 21:30:00 +00:00
|
|
|
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);
|
|
|
|
}
|
2025-06-05 20:10:40 +00:00
|
|
|
self.resolve_binding(fn_name);
|
|
|
|
self.emit_op(Op::Partial);
|
|
|
|
self.emit_byte(arity);
|
|
|
|
self.stack_depth -= 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);
|
2025-06-20 00:26:16 +00:00
|
|
|
// if we're in tail position AND there aren't any rest args, this should be a tail call (I think)
|
2025-06-20 01:47:58 +00:00
|
|
|
if rest.is_empty() && tail_pos {
|
2025-06-20 00:26:16 +00:00
|
|
|
self.emit_op(Op::TailCall);
|
|
|
|
} else {
|
|
|
|
self.emit_op(Op::Call);
|
|
|
|
}
|
2025-06-05 20:10:40 +00:00
|
|
|
self.emit_byte(arity);
|
|
|
|
self.stack_depth -= arity;
|
2025-05-28 20:37:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-06-05 20:10:40 +00:00
|
|
|
}
|
2024-12-23 00:07:42 +00:00
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2025-06-20 00:26:16 +00:00
|
|
|
// the last term in rest should be in tail position if we are in tail position
|
2025-06-20 01:47:58 +00:00
|
|
|
let num_rest_terms = rest.len();
|
|
|
|
for (i, (term, _)) in rest.iter().enumerate() {
|
2025-06-03 23:13:40 +00:00
|
|
|
match term {
|
|
|
|
Keyword(str) => {
|
|
|
|
self.emit_constant(Value::Keyword(str));
|
|
|
|
self.emit_op(Op::GetKey);
|
|
|
|
self.stack_depth -= 1;
|
|
|
|
}
|
|
|
|
Arguments(args) => {
|
|
|
|
self.emit_op(Op::Stash);
|
|
|
|
self.pop();
|
|
|
|
let arity = args.len();
|
|
|
|
for arg in args {
|
|
|
|
self.visit(arg);
|
|
|
|
}
|
|
|
|
self.emit_op(Op::Load);
|
2025-06-20 01:47:58 +00:00
|
|
|
if tail_pos && i == num_rest_terms - 1 {
|
|
|
|
self.emit_op(Op::TailCall)
|
|
|
|
} else {
|
|
|
|
self.emit_op(Op::Call);
|
|
|
|
}
|
2025-06-03 23:13:40 +00:00
|
|
|
self.emit_byte(arity);
|
|
|
|
self.stack_depth -= arity;
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2024-12-23 00:07:42 +00:00
|
|
|
}
|
2025-06-20 01:47:58 +00:00
|
|
|
self.tail_pos = tail_pos;
|
2024-12-23 00:07:42 +00:00
|
|
|
}
|
|
|
|
When(clauses) => {
|
2025-06-20 00:26:16 +00:00
|
|
|
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() {
|
2025-06-20 00:26:16 +00:00
|
|
|
self.tail_pos = false;
|
2024-12-23 00:07:42 +00:00
|
|
|
self.visit(cond.as_ref());
|
2025-06-18 19:24:30 +00:00
|
|
|
let jif_jump_idx = self.stub_jump(Op::JumpIfFalse);
|
2025-05-23 21:59:09 +00:00
|
|
|
self.stack_depth -= 1;
|
2025-06-20 00:26:16 +00:00
|
|
|
self.tail_pos = tail_pos;
|
2024-12-23 00:07:42 +00:00
|
|
|
self.visit(body);
|
2025-05-25 20:30:20 +00:00
|
|
|
self.stack_depth -= 1;
|
2025-06-18 19:24:30 +00:00
|
|
|
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 {
|
2025-06-18 19:24:30 +00:00
|
|
|
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
|
|
|
}
|
2024-12-27 04:33:57 +00:00
|
|
|
WhenClause(..) => unreachable!(),
|
2024-12-23 00:33:59 +00:00
|
|
|
Match(scrutinee, clauses) => {
|
2025-06-20 00:26:16 +00:00
|
|
|
let tail_pos = self.tail_pos;
|
|
|
|
self.tail_pos = false;
|
2024-12-23 00:33:59 +00:00
|
|
|
self.visit(scrutinee.as_ref());
|
2025-05-23 04:09:35 +00:00
|
|
|
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() {
|
2025-06-20 00:26:16 +00:00
|
|
|
self.tail_pos = false;
|
2025-06-04 22:51:07 +00:00
|
|
|
let mut no_match_jumps = vec![];
|
2024-12-23 00:33:59 +00:00
|
|
|
self.scope_depth += 1;
|
2025-05-23 04:09:35 +00:00
|
|
|
self.match_depth = 0;
|
2024-12-23 00:33:59 +00:00
|
|
|
self.visit(pattern);
|
2025-06-18 19:24:30 +00:00
|
|
|
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);
|
2025-06-18 19:24:30 +00:00
|
|
|
no_match_jumps.push(self.stub_jump(Op::JumpIfFalse));
|
2025-06-04 22:51:07 +00:00
|
|
|
self.stack_depth -= 1;
|
|
|
|
}
|
2025-06-20 00:26:16 +00:00
|
|
|
self.tail_pos = tail_pos;
|
2025-06-04 22:51:07 +00:00
|
|
|
self.visit(body);
|
|
|
|
self.emit_op(Op::Store);
|
|
|
|
self.scope_depth -= 1;
|
|
|
|
while let Some(binding) = self.bindings.last() {
|
|
|
|
if binding.depth > self.scope_depth {
|
|
|
|
self.bindings.pop();
|
|
|
|
} else {
|
|
|
|
break;
|
2024-12-27 04:46:06 +00:00
|
|
|
}
|
2024-12-23 00:33:59 +00:00
|
|
|
}
|
2025-06-18 19:24:30 +00:00
|
|
|
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 {
|
2025-06-18 19:24:30 +00:00
|
|
|
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);
|
2025-06-18 19:24:30 +00:00
|
|
|
|
2024-12-23 00:33:59 +00:00
|
|
|
for idx in jump_idxes {
|
2025-06-18 19:24:30 +00:00
|
|
|
self.patch_jump(idx, self.len() - idx - 3);
|
2025-05-23 17:42:29 +00:00
|
|
|
}
|
2025-06-18 19:24:30 +00:00
|
|
|
self.pop_n(self.stack_depth - stack_depth);
|
2025-05-23 04:09:35 +00:00
|
|
|
self.emit_op(Op::Load);
|
|
|
|
self.stack_depth += 1;
|
2024-12-23 00:33:59 +00:00
|
|
|
}
|
2024-12-27 04:33:57 +00:00
|
|
|
MatchClause(..) => unreachable!(),
|
2024-12-24 17:35:44 +00:00
|
|
|
Fn(name, body, doc) => {
|
2025-06-20 16:32:15 +00:00
|
|
|
let is_anon = name.is_empty();
|
|
|
|
|
|
|
|
if !is_anon {
|
|
|
|
let declared = self.chunk.constants.iter().any(|val| match val {
|
|
|
|
Value::Fn(lfn) => lfn.name() == *name,
|
|
|
|
_ => false,
|
|
|
|
});
|
|
|
|
if !declared {
|
|
|
|
let declaration = Value::Fn(Rc::new(LFn::Declared { name }));
|
|
|
|
self.emit_constant(declaration);
|
|
|
|
self.bind(name);
|
|
|
|
}
|
|
|
|
}
|
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 TuplePattern(pattern) = &pattern.0 else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
2025-05-30 21:02:55 +00:00
|
|
|
|
2025-06-19 22:26:44 +00:00
|
|
|
if matches!(pattern.last(), Some((Splattern(_), _))) {
|
|
|
|
has_splat = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
let arity = pattern.len() as u8;
|
2025-05-30 21:02:55 +00:00
|
|
|
|
|
|
|
let compiler = match compilers.get_mut(&arity) {
|
|
|
|
Some(compiler) => compiler,
|
|
|
|
None => {
|
2025-06-20 18:29:31 +00:00
|
|
|
let mut compiler = Compiler::new(
|
|
|
|
clause,
|
|
|
|
self.name,
|
|
|
|
self.src,
|
|
|
|
Some(self),
|
|
|
|
self.chunk.env.clone(),
|
|
|
|
);
|
2025-05-30 21:02:55 +00:00
|
|
|
compiler.emit_op(Op::ResetMatch);
|
|
|
|
compilers.insert(arity, compiler);
|
|
|
|
compilers.get_mut(&arity).unwrap()
|
|
|
|
}
|
|
|
|
};
|
2025-06-20 00:26:16 +00:00
|
|
|
compiler.tail_pos = false;
|
2025-05-30 21:02:55 +00:00
|
|
|
|
2025-06-19 22:26:44 +00:00
|
|
|
compiler.stack_depth += arity as usize;
|
2025-06-03 21:30:00 +00:00
|
|
|
compiler.scope_depth += 1;
|
2025-06-19 22:26:44 +00:00
|
|
|
compiler.match_depth = arity as usize;
|
2025-06-03 21:30:00 +00:00
|
|
|
|
2025-06-04 21:53:38 +00:00
|
|
|
std::mem::swap(&mut upvalues, &mut compiler.upvalues);
|
|
|
|
|
2025-05-30 21:02:55 +00:00
|
|
|
let mut tup_jump_idxes = vec![];
|
|
|
|
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));
|
2025-05-30 21:02:55 +00:00
|
|
|
}
|
2025-06-03 21:30:00 +00:00
|
|
|
if pattern.is_empty() {
|
|
|
|
compiler.emit_op(Op::Match);
|
|
|
|
}
|
2025-06-18 21:50:30 +00:00
|
|
|
let jump_idx = compiler.stub_jump(Op::Jump);
|
2025-05-30 21:02:55 +00:00
|
|
|
for idx in tup_jump_idxes {
|
2025-06-18 21:50:30 +00:00
|
|
|
compiler.patch_jump(idx, compiler.len() - idx - 3);
|
2025-05-30 21:02:55 +00:00
|
|
|
}
|
2025-06-20 16:32:15 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2025-06-20 00:26:16 +00:00
|
|
|
compiler.tail_pos = true;
|
2025-05-30 21:02:55 +00:00
|
|
|
compiler.visit(clause_body);
|
|
|
|
compiler.emit_op(Op::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);
|
2025-05-30 21:02:55 +00:00
|
|
|
compiler.stack_depth = 0;
|
2025-06-02 00:01:42 +00:00
|
|
|
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
|
|
|
}
|
2025-05-30 21:02:55 +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-05-30 21:02:55 +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-05-30 21:02:55 +00:00
|
|
|
|
2025-06-19 22:26:44 +00:00
|
|
|
let mut arities = vec![];
|
|
|
|
let mut chunks = vec![];
|
|
|
|
|
|
|
|
for (arity, mut compiler) in compilers {
|
2025-06-02 00:01:42 +00:00
|
|
|
compiler.emit_op(Op::PanicNoMatch);
|
2025-06-03 21:30:00 +00:00
|
|
|
let chunk = compiler.chunk;
|
2025-06-02 00:01:42 +00:00
|
|
|
if crate::DEBUG_COMPILE {
|
|
|
|
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-04 21:53:38 +00:00
|
|
|
// TODO: check if the function is already declared, and pull out the relevant OnceCell if need be
|
2025-06-20 04:56:43 +00:00
|
|
|
let the_fn = Value::Fn(Rc::new(lfn));
|
2025-06-20 16:32:15 +00:00
|
|
|
// self.emit_constant(the_fn);
|
|
|
|
// self.bind(name);
|
|
|
|
|
|
|
|
if !is_anon {
|
|
|
|
let declaration_idx = self
|
|
|
|
.chunk
|
|
|
|
.constants
|
|
|
|
.iter()
|
|
|
|
.position(|val| match val {
|
|
|
|
Value::Fn(declaration) => declaration.name() == *name,
|
|
|
|
_ => false,
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
self.chunk.constants[declaration_idx] = the_fn;
|
|
|
|
} else {
|
|
|
|
self.emit_constant(the_fn)
|
|
|
|
}
|
2025-06-03 22:54:33 +00:00
|
|
|
|
2025-06-04 21:53:38 +00:00
|
|
|
for upvalue in upvalues {
|
|
|
|
self.emit_op(Op::SetUpvalue);
|
|
|
|
self.emit_byte(upvalue.stack_pos);
|
|
|
|
}
|
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
|
|
|
}
|
2025-06-02 00:01:42 +00:00
|
|
|
FnBody(_) => unreachable!(),
|
2024-12-27 04:33:57 +00:00
|
|
|
Repeat(times, body) => {
|
|
|
|
self.visit(times);
|
|
|
|
self.emit_op(Op::Truncate);
|
|
|
|
// 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);
|
2024-12-27 04:33:57 +00:00
|
|
|
// begin repeat
|
|
|
|
self.emit_op(Op::Decrement);
|
2024-12-27 04:46:06 +00:00
|
|
|
let repeat_begin = self.len();
|
2024-12-27 04:33:57 +00:00
|
|
|
self.emit_op(Op::Duplicate);
|
2025-06-18 21:50:30 +00:00
|
|
|
let jiz_idx = self.stub_jump(Op::JumpIfZero);
|
2024-12-27 04:33:57 +00:00
|
|
|
// compile the body
|
|
|
|
self.visit(body);
|
|
|
|
// pop whatever value the body returns
|
2025-05-23 04:09:35 +00:00
|
|
|
self.pop();
|
2025-06-18 21:50:30 +00:00
|
|
|
let jump_back = self.stub_jump(Op::JumpBack);
|
2024-12-27 04:33:57 +00:00
|
|
|
// 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();
|
2024-12-27 04:33:57 +00:00
|
|
|
self.emit_constant(Value::Nil);
|
|
|
|
}
|
2024-12-27 05:22:01 +00:00
|
|
|
Loop(value, clauses) => {
|
2025-06-20 00:26:16 +00:00
|
|
|
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!()
|
|
|
|
};
|
2025-05-26 15:03:37 +00:00
|
|
|
for (i, member) in members.iter().enumerate() {
|
2024-12-27 05:22:01 +00:00
|
|
|
self.visit(member);
|
2025-05-26 15:03:37 +00:00
|
|
|
self.emit_op(Op::StoreAt);
|
|
|
|
self.emit_byte(i);
|
2024-12-27 05:22:01 +00:00
|
|
|
}
|
|
|
|
let arity = members.len();
|
2025-05-26 15:03:37 +00:00
|
|
|
let stack_depth = self.stack_depth;
|
2024-12-27 05:22:01 +00:00
|
|
|
//then, save the beginning of the loop
|
2025-05-26 15:03:37 +00:00
|
|
|
self.emit_op(Op::Load);
|
2024-12-27 05:22:01 +00:00
|
|
|
self.enter_loop();
|
2025-05-26 15:03:37 +00:00
|
|
|
self.stack_depth += arity;
|
2024-12-27 05:22:01 +00:00
|
|
|
//next, compile each clause:
|
|
|
|
let mut clauses = clauses.iter();
|
2025-05-23 04:09:35 +00:00
|
|
|
let mut jump_idxes = vec![];
|
2025-06-18 23:03:45 +00:00
|
|
|
while let Some((Ast::MatchClause(pattern, guard, body), _)) = clauses.next() {
|
2025-06-20 00:26:16 +00:00
|
|
|
self.tail_pos = false;
|
2025-05-26 15:03:37 +00:00
|
|
|
self.emit_op(Op::ResetMatch);
|
2024-12-27 05:22:01 +00:00
|
|
|
self.scope_depth += 1;
|
|
|
|
let (Ast::TuplePattern(members), _) = pattern.as_ref() else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
2025-05-26 15:03:37 +00:00
|
|
|
self.match_depth = arity;
|
2025-06-18 23:03:45 +00:00
|
|
|
let mut jnm_idxes = vec![];
|
2025-05-26 15:03:37 +00:00
|
|
|
for member in members {
|
|
|
|
self.match_depth -= 1;
|
2025-05-23 04:09:35 +00:00
|
|
|
self.emit_op(Op::MatchDepth);
|
2025-05-26 15:03:37 +00:00
|
|
|
self.emit_byte(self.match_depth);
|
|
|
|
self.visit(member);
|
2025-06-18 23:03:45 +00:00
|
|
|
jnm_idxes.push(self.stub_jump(Op::JumpIfNoMatch));
|
2025-05-26 15:03:37 +00:00
|
|
|
}
|
2025-06-18 23:03:45 +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));
|
|
|
|
self.stack_depth -= 1;
|
2025-05-26 15:03:37 +00:00
|
|
|
}
|
2025-06-20 00:26:16 +00:00
|
|
|
self.tail_pos = tail_pos;
|
2025-05-23 04:09:35 +00:00
|
|
|
self.visit(body);
|
2025-05-26 15:03:37 +00:00
|
|
|
self.emit_op(Op::Store);
|
|
|
|
self.scope_depth -= 1;
|
|
|
|
while let Some(binding) = self.bindings.last() {
|
|
|
|
if binding.depth > self.scope_depth {
|
|
|
|
self.bindings.pop();
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2025-06-18 23:03:45 +00:00
|
|
|
while self.stack_depth > stack_depth {
|
2025-05-26 15:03:37 +00:00
|
|
|
self.pop();
|
|
|
|
}
|
2025-06-18 23:03:45 +00:00
|
|
|
jump_idxes.push(self.stub_jump(Op::Jump));
|
|
|
|
for idx in jnm_idxes {
|
|
|
|
self.patch_jump(idx, self.len() - idx - 3);
|
|
|
|
}
|
2025-05-23 04:09:35 +00:00
|
|
|
self.scope_depth -= 1;
|
2024-12-27 05:22:01 +00:00
|
|
|
}
|
2025-05-23 04:09:35 +00:00
|
|
|
self.emit_op(Op::PanicNoMatch);
|
2025-05-26 15:03:37 +00:00
|
|
|
for idx in jump_idxes {
|
2025-06-18 23:03:45 +00:00
|
|
|
self.patch_jump(idx, self.len() - idx - 3);
|
2025-05-26 15:03:37 +00:00
|
|
|
}
|
|
|
|
self.stack_depth -= arity;
|
|
|
|
self.emit_op(Op::Load);
|
|
|
|
self.stack_depth += 1;
|
2025-05-23 04:09:35 +00:00
|
|
|
self.leave_loop();
|
|
|
|
}
|
2025-05-26 13:16:47 +00:00
|
|
|
Recur(args) => {
|
2025-05-26 15:03:37 +00:00
|
|
|
for (i, arg) in args.iter().enumerate() {
|
|
|
|
self.visit(arg);
|
|
|
|
self.emit_op(Op::StoreAt);
|
|
|
|
self.emit_byte(i);
|
|
|
|
}
|
|
|
|
self.emit_op(Op::PopN);
|
|
|
|
self.emit_byte(self.loop_root());
|
2025-06-18 23:03:45 +00:00
|
|
|
self.emit_op(Op::Load);
|
|
|
|
self.jump(Op::JumpBack, self.len() - self.loop_idx());
|
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);
|
|
|
|
self.stack_depth -= 1;
|
|
|
|
}
|
|
|
|
StringPart::Word(word) => {
|
2025-06-05 20:10:40 +00:00
|
|
|
self.resolve_binding(word);
|
2025-05-30 15:44:32 +00:00
|
|
|
self.emit_op(Op::Stringify);
|
|
|
|
self.emit_op(Op::ConcatStrings);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-06-05 16:15:49 +00:00
|
|
|
Do(terms) => {
|
|
|
|
let mut terms = terms.iter();
|
|
|
|
let first = terms.next().unwrap();
|
2025-06-20 00:26:16 +00:00
|
|
|
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
|
2025-06-20 00:26:16 +00:00
|
|
|
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;
|
|
|
|
}
|
2025-06-20 00:26:16 +00:00
|
|
|
self.visit(last);
|
2025-06-20 01:47:58 +00:00
|
|
|
if tail_pos {
|
|
|
|
self.emit_op(Op::TailCall)
|
|
|
|
} else {
|
|
|
|
self.emit_op(Op::Call);
|
|
|
|
}
|
2025-06-20 00:26:16 +00:00
|
|
|
self.emit_byte(1);
|
2025-06-20 01:47:58 +00:00
|
|
|
self.tail_pos = tail_pos;
|
2025-06-20 00:26:16 +00:00
|
|
|
self.stack_depth -= 1;
|
2025-06-05 16:15:49 +00:00
|
|
|
}
|
2025-06-05 20:10:40 +00:00
|
|
|
Placeholder => {
|
|
|
|
self.emit_op(Op::Nothing);
|
|
|
|
}
|
2025-06-06 03:26:42 +00:00
|
|
|
And | Or | Arguments(..) => unreachable!(),
|
2024-12-15 21:37:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-16 04:28:57 +00:00
|
|
|
pub fn disassemble(&self) {
|
2024-12-15 21:37:51 +00:00
|
|
|
println!("=== chunk: {} ===", self.name);
|
2025-06-02 00:01:42 +00:00
|
|
|
self.chunk.dissasemble();
|
2024-12-15 21:37:51 +00:00
|
|
|
}
|
|
|
|
}
|