use crate::parser::Ast; use crate::parser::StringPart; use crate::spans::Spanned; use crate::value::*; use chumsky::prelude::SimpleSpan; use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::FromPrimitive; use std::borrow::Borrow; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; #[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)] pub enum Op { Noop, Nil, True, False, Constant, Jump, JumpIfFalse, JumpIfTrue, Pop, PopN, PushBinding, Store, StoreAt, Stash, Load, ResetMatch, Match, MatchNil, MatchTrue, MatchFalse, PanicIfNoMatch, MatchConstant, MatchType, MatchTuple, PushTuple, LoadTuple, MatchList, LoadList, PushList, PushDict, LoadDictValue, MatchDict, PushBox, GetKey, PanicNoWhen, JumpIfNoMatch, JumpIfMatch, PanicNoMatch, TypeOf, JumpBack, JumpIfZero, Duplicate, Decrement, Truncate, MatchDepth, Panic, EmptyString, ConcatStrings, Stringify, Call, Return, Eq, Add, Sub, Mult, Div, Unbox, BoxStore, Assert, Get, At, Not, Print, SetUpvalue, GetUpvalue, // 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 } impl std::fmt::Display for Op { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { use Op::*; let rep = match self { Noop => "noop", Nil => "nil", True => "true", False => "false", Constant => "constant", Jump => "jump", JumpIfFalse => "jump_if_false", JumpIfTrue => "jump_if_true", Pop => "pop", PopN => "pop_n", PushBinding => "push_binding", Store => "store", StoreAt => "store_at", Stash => "stash", Load => "load", Match => "match", MatchNil => "match_nil", MatchTrue => "match_true", MatchFalse => "match_false", ResetMatch => "reset_match", PanicIfNoMatch => "panic_if_no_match", MatchConstant => "match_constant", MatchType => "match_type", MatchTuple => "match_tuple", PushTuple => "push_tuple", LoadTuple => "load_tuple", MatchList => "match_list", LoadList => "load_list", PushList => "push_list", PushDict => "push_dict", LoadDictValue => "load_dict_value", MatchDict => "match_dict", PushBox => "push_box", GetKey => "get_key", PanicNoWhen => "panic_no_when", JumpIfNoMatch => "jump_if_no_match", JumpIfMatch => "jump_if_match", PanicNoMatch => "panic_no_match", TypeOf => "type_of", JumpBack => "jump_back", JumpIfZero => "jump_if_zero", Decrement => "decrement", Truncate => "truncate", Duplicate => "duplicate", MatchDepth => "match_depth", Panic => "panic", EmptyString => "empty_string", ConcatStrings => "concat_strings", Stringify => "stringify", Print => "print", Eq => "eq", Add => "add", Sub => "sub", Mult => "mult", Div => "div", Unbox => "unbox", BoxStore => "box_store", Assert => "assert", Get => "get", At => "at", Not => "not", Call => "call", Return => "return", SetUpvalue => "set_upvalue", GetUpvalue => "get_upvalue", }; write!(f, "{rep}") } } #[derive(Clone, Debug, PartialEq)] pub struct Binding { name: &'static str, depth: isize, stack_pos: usize, } #[derive(Clone, Debug, PartialEq)] pub struct Upvalue { name: &'static str, stack_pos: usize, } #[derive(Clone, Debug, PartialEq)] pub struct Chunk { pub constants: Vec, pub bytecode: Vec, pub strings: Vec<&'static str>, pub keywords: Vec<&'static str>, } impl Chunk { pub fn dissasemble_instr(&self, i: &mut usize) { let op = Op::from_u8(self.bytecode[*i]).unwrap(); use Op::*; match op { Pop | Store | Stash | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse | 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 | ConcatStrings | Stringify | MatchType | Return | Match | Print => { println!("{i:04}: {op}") } Constant | MatchConstant => { let next = self.bytecode[*i + 1]; let value = &self.constants[next as usize].show(); println!("{i:04}: {:16} {next:04}: {value}", op.to_string()); *i += 1; } PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple | PushDict | PushList | PushBox | Jump | JumpIfFalse | JumpIfTrue | JumpIfNoMatch | JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt | Call | SetUpvalue | GetUpvalue => { let next = self.bytecode[*i + 1]; println!("{i:04}: {:16} {next:04}", op.to_string()); *i += 1; } } } pub fn dissasemble(&self) { println!("IDX | CODE | INFO"); let mut i = 0; while i < self.bytecode.len() { self.dissasemble_instr(&mut i); i += 1; } } // pub fn kw_from(&self, kw: &str) -> Option { // self.kw_index_from(kw).map(Value::Keyword) // } // pub fn kw_index_from(&self, kw: &str) -> Option { // self.keywords.iter().position(|s| *s == kw) // } } #[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 { 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), _ => None, } } #[derive(Debug, Clone, PartialEq)] pub struct Compiler<'a> { pub chunk: Chunk, pub bindings: Vec, pub scope_depth: isize, pub match_depth: usize, pub stack_depth: usize, pub spans: Vec, pub nodes: Vec<&'static Ast>, pub ast: &'static Ast, pub span: SimpleSpan, pub src: &'static str, pub name: &'static str, pub enclosing: Option<&'a Compiler<'a>>, pub upvalues: Vec, loop_info: Vec, } fn is_let(expr: &Spanned) -> bool { let (ast, _) = expr; use Ast::*; matches!(ast, Let(..)) } impl<'a> Compiler<'a> { pub fn new( ast: &'static Spanned, name: &'static str, src: &'static str, enclosing: Option<&'a Compiler>, ) -> Compiler<'a> { let chunk = Chunk { constants: vec![], bytecode: vec![], strings: vec![], keywords: vec![ "nil", "bool", "number", "keyword", "string", "tuple", "list", "dict", "box", "fn", ], }; Compiler { chunk, bindings: vec![], scope_depth: -1, match_depth: 0, stack_depth: 0, spans: vec![], nodes: vec![], ast: &ast.0, span: ast.1, loop_info: vec![], enclosing, upvalues: vec![], src, name, } } pub fn visit(&mut self, node: &'static Spanned) { 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; } 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 }; if const_idx > u8::MAX as usize { panic!( "internal Ludus compiler error: too many constants in chunk:{}:: {}", self.span, self.ast ) } self.emit_op(Op::Constant); self.emit_byte(const_idx); self.stack_depth += 1; } fn match_constant(&mut self, val: Value) { let constant_index = match self.chunk.constants.iter().position(|v| *v == val) { Some(idx) => idx, None => { self.chunk.constants.push(val); self.chunk.constants.len() - 1 } }; if constant_index > u8::MAX as usize { panic!( "internal Ludus compiler error: too many constants in chunk:{}:: {}", self.span, self.ast ) } self.emit_op(Op::MatchConstant); self.emit_byte(constant_index); } fn emit_op(&mut self, op: Op) { self.chunk.bytecode.push(op as u8); self.spans.push(self.span); } 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() } fn bind(&mut self, name: &'static str) { let binding = Binding { name, depth: self.scope_depth, stack_pos: self.stack_depth - self.match_depth - 1, }; println!("{:?}", binding); println!("stack: {}; match: {}", self.stack_depth, self.match_depth); self.bindings.push(binding); } fn resolve_local(&self, name: &'static str) -> Option { for binding in self.bindings.iter() { if binding.name == name { return Some(binding.stack_pos); } } None } fn resolve_upvalue(&self, name: &'static str) -> Option { 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), } } fn resolve_binding(&self, name: &'static str) -> usize { if let Some(pos) = self.resolve_local(name) { return pos; } match self.enclosing { Some(compiler) => compiler.resolve_binding(name), None => unreachable!(), } } fn pop(&mut self) { self.emit_op(Op::Pop); self.stack_depth -= 1; } fn pop_n(&mut self, n: usize) { self.emit_op(Op::PopN); self.emit_byte(n); self.stack_depth -= n; } fn enter_loop(&mut self) { self.loop_info .push(LoopInfo::new(self.len(), self.stack_depth)); } fn leave_loop(&mut self) { self.loop_info.pop(); } fn loop_info(&self) -> LoopInfo { self.loop_info.last().unwrap().clone() } fn loop_idx(&self) -> usize { self.loop_info.last().unwrap().start } fn loop_root(&self) -> usize { self.loop_info.last().unwrap().stack_root } pub fn compile(&mut self) { use Ast::*; match self.ast { Error => unreachable!(), Nil => { self.emit_op(Op::Nil); self.stack_depth += 1; } Number(n) => self.emit_constant(Value::Number(*n)), Boolean(b) => { self.emit_op(if *b { Op::True } else { Op::False }); self.stack_depth += 1; } String(s) => { self.emit_constant(Value::Interned(s)); } Keyword(s) => self.emit_constant(Value::Keyword(s)), Block(lines) => { self.scope_depth += 1; let stack_depth = self.stack_depth; for expr in lines.iter().take(lines.len() - 1) { if is_let(expr) { self.visit(expr); } else { self.visit(expr); self.pop(); } } let last_expr = lines.last().unwrap(); match last_expr { (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; } _ => { self.visit(last_expr); } } self.stack_depth += 1; 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; } } // reset stack while self.stack_depth > stack_depth + 1 { self.pop(); } self.emit_op(Op::Load); } If(cond, then, r#else) => { self.visit(cond); let jif_idx = self.len(); self.emit_op(Op::JumpIfFalse); self.emit_byte(0xff); self.stack_depth -= 1; self.visit(then); let jump_idx = self.len(); self.emit_op(Op::Jump); self.emit_byte(0xff); self.visit(r#else); self.stack_depth -= 1; let end_idx = self.len(); let jif_offset = jump_idx - jif_idx; let jump_offset = end_idx - jump_idx - 2; self.chunk.bytecode[jif_idx + 1] = jif_offset as u8; self.chunk.bytecode[jump_idx + 1] = jump_offset as u8; } Let(patt, expr) => { self.match_depth = 0; self.emit_op(Op::ResetMatch); self.visit(expr); self.visit(patt); self.emit_op(Op::PanicIfNoMatch); } WordPattern(name) => { self.emit_op(Op::Match); self.bind(name); } Word(name) => 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) => { println!("resolved upvalue: {name}"); 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); self.emit_byte(upvalue.stack_pos); self.upvalues.push(upvalue); dbg!(&self.upvalues); self.stack_depth += 1; } }, }, PlaceholderPattern => { self.emit_op(Op::Match); } 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) => { let existing_kw = self.chunk.keywords.iter().position(|kw| kw == s); let kw_index = match existing_kw { Some(index) => index, None => self.chunk.keywords.len(), }; if kw_index == self.chunk.keywords.len() { self.chunk.keywords.push(s); } self.match_constant(Value::Keyword(s)); } AsPattern(word, typ) => { self.emit_constant(Value::Keyword(typ)); self.emit_op(Op::MatchType); self.stack_depth -= 1; self.bind(word); } StringPattern(s) => { self.match_constant(Value::Interned(s)); } TuplePattern(members) => { self.emit_op(Op::MatchTuple); self.emit_byte(members.len()); self.emit_op(Op::JumpIfNoMatch); let before_load_tup_idx = self.len(); self.emit_byte(0xff); let mut jump_idxes = vec![]; self.match_depth += members.len(); self.emit_op(Op::LoadTuple); 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); self.emit_op(Op::JumpIfNoMatch); jump_idxes.push(self.len()); self.emit_byte(0xff); } self.emit_op(Op::Jump); let jump_idx = self.len(); self.emit_byte(0xff); for idx in jump_idxes { self.chunk.bytecode[idx] = self.len() as u8 - idx as u8 - 1; } for _ in 0..members.len() { self.emit_op(Op::Pop); } self.chunk.bytecode[before_load_tup_idx] = self.len() as u8 - before_load_tup_idx as u8 - 1; self.chunk.bytecode[jump_idx] = self.len() as u8 - jump_idx as u8 - 1; } ListPattern(members) => { self.emit_op(Op::MatchList); self.emit_byte(members.len()); self.emit_op(Op::JumpIfNoMatch); let before_load_list_idx = self.len(); self.emit_byte(0xff); let mut jump_idxes = vec![]; self.match_depth += members.len(); 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); self.emit_op(Op::JumpIfNoMatch); jump_idxes.push(self.len()); self.emit_byte(0xff); } self.emit_op(Op::Jump); let jump_idx = self.len(); self.emit_byte(0xff); for idx in jump_idxes { self.chunk.bytecode[idx] = self.len() as u8 - idx as u8 - 1; } for _ in 0..members.len() { self.emit_op(Op::Pop); } self.chunk.bytecode[before_load_list_idx] = self.len() as u8 - before_load_list_idx as u8 - 1; self.chunk.bytecode[jump_idx] = self.len() as u8 - jump_idx as u8 - 1; } DictPattern(pairs) => { self.emit_op(Op::MatchDict); self.emit_byte(pairs.len()); self.emit_op(Op::JumpIfNoMatch); let before_load_dict_idx = self.len(); self.emit_byte(0xff); let mut jump_idxes = vec![]; let dict_stack_pos = self.stack_depth - 1; for pair in pairs { 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); self.emit_op(Op::JumpIfNoMatch); jump_idxes.push(self.len()); self.emit_byte(0xff); } self.emit_op(Op::Jump); let jump_idx = self.len(); self.emit_byte(0xff); for idx in jump_idxes { self.chunk.bytecode[idx] = self.len() as u8 - idx as u8 - 1; } for _ in 0..pairs.len() { self.emit_op(Op::Pop); } self.chunk.bytecode[before_load_dict_idx] = self.len() as u8 - before_load_dict_idx as u8 - 1; self.chunk.bytecode[jump_idx] = self.len() as u8 - jump_idx as u8 - 1; } PairPattern(_, _) => unreachable!(), Tuple(members) => { for member in members { self.visit(member); } self.emit_op(Op::PushTuple); self.emit_byte(members.len()); self.stack_depth = self.stack_depth + 1 - members.len(); } List(members) => { for member in members { self.visit(member); } self.emit_op(Op::PushList); self.emit_byte(members.len()); self.stack_depth = self.stack_depth + 1 - members.len(); } LBox(name, expr) => { self.visit(expr); self.emit_op(Op::PushBox); self.bind(name); } Dict(pairs) => { for pair in pairs { self.visit(pair); } self.emit_op(Op::PushDict); self.emit_byte(pairs.len()); self.stack_depth = self.stack_depth + 1 - (pairs.len() * 2); } Pair(key, value) => { self.emit_constant(Value::Keyword(key)); self.visit(value); } Synthetic(first, second, rest) => { match (&first.0, &second.0) { (Word(_), Keyword(_)) => { self.visit(first); self.visit(second); self.emit_op(Op::GetKey); self.stack_depth -= 1; } (Keyword(_), Arguments(args)) => { self.visit(&args[0]); self.visit(first); self.emit_op(Op::GetKey); self.stack_depth -= 1; } (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); self.emit_op(Op::JumpIfTrue); jump_idxes.push(self.len()); self.emit_byte(0xff); } for idx in jump_idxes { self.chunk.bytecode[idx] = (self.len() - idx + 2) as u8; } 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); self.emit_op(Op::JumpIfFalse); jump_idxes.push(self.len()); self.emit_byte(0xff); } for idx in jump_idxes { self.chunk.bytecode[idx] = (self.len() - idx + 2) as u8; } self.emit_op(Op::Load); } else { self.emit_op(Op::True); } self.stack_depth = stack_depth + 1; } (Word(fn_name), Arguments(args)) => 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.emit_op(Op::PushBinding); self.stack_depth += 1; let biter = self.bindings.iter().rev(); for binding in biter { if binding.name == *fn_name { self.emit_byte(binding.stack_pos); break; } } self.emit_op(Op::Call); self.emit_byte(arity); self.stack_depth -= arity; } }, _ => unreachable!(), } for (term, _) in rest { 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); self.emit_op(Op::Call); self.emit_byte(arity); self.stack_depth -= arity; } _ => unreachable!(), } } } When(clauses) => { let mut jump_idxes = vec![]; let mut clauses = clauses.iter(); while let Some((WhenClause(cond, body), _)) = clauses.next() { self.visit(cond.as_ref()); self.emit_op(Op::JumpIfFalse); let jif_jump_idx = self.len(); self.emit_byte(0xff); self.stack_depth -= 1; self.visit(body); self.stack_depth -= 1; self.emit_op(Op::Jump); jump_idxes.push(self.len()); self.emit_byte(0xff); self.chunk.bytecode[jif_jump_idx] = self.len() as u8 - jif_jump_idx as u8 - 1; } self.emit_op(Op::PanicNoWhen); for idx in jump_idxes { self.chunk.bytecode[idx] = self.len() as u8 - idx as u8 - 1; } self.stack_depth += 1; } WhenClause(..) => unreachable!(), Match(scrutinee, clauses) => { self.visit(scrutinee.as_ref()); let stack_depth = self.stack_depth; let mut jump_idxes = vec![]; let mut clauses = clauses.iter(); while let Some((MatchClause(pattern, guard, body), _)) = clauses.next() { let mut no_match_jumps = vec![]; self.scope_depth += 1; self.match_depth = 0; self.visit(pattern); self.emit_op(Op::JumpIfNoMatch); no_match_jumps.push(self.len()); self.emit_byte(0xff); if guard.is_some() { let guard_expr: &'static Spanned = Box::leak(Box::new(guard.clone().unwrap())); self.visit(guard_expr); self.emit_op(Op::JumpIfFalse); no_match_jumps.push(self.len()); self.emit_byte(0xff); self.stack_depth -= 1; } 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; } } while self.stack_depth > stack_depth { self.pop(); } self.emit_op(Op::Jump); jump_idxes.push(self.len()); self.emit_byte(0xff); for idx in no_match_jumps { self.chunk.bytecode[idx] = self.len() as u8 - idx as u8 - 1; } } self.emit_op(Op::PanicNoMatch); for idx in jump_idxes { self.chunk.bytecode[idx] = self.len() as u8 - idx as u8 - 1; } while self.stack_depth > stack_depth { self.pop(); } self.emit_op(Op::Load); self.stack_depth += 1; } MatchClause(..) => unreachable!(), Fn(name, body, doc) => { let name = if name.is_empty() { "anonymous" } else { name }; // first, declare the function // TODO: or, check if the function has already been declared! let FnBody(fn_body) = &body.as_ref().0 else { unreachable!() }; let mut compilers: HashMap = HashMap::new(); let mut upvalues = vec![]; for clause in fn_body { let MatchClause(pattern, guard, clause_body) = &clause.0 else { unreachable!() }; let TuplePattern(pattern) = &pattern.0 else { unreachable!() }; let arity = pattern.len(); let compiler = match compilers.get_mut(&arity) { Some(compiler) => compiler, None => { let mut compiler = Compiler::new(clause, self.name, self.src, Some(self)); compiler.emit_op(Op::ResetMatch); compilers.insert(arity, compiler); compilers.get_mut(&arity).unwrap() } }; compiler.stack_depth += arity; compiler.scope_depth += 1; compiler.match_depth = arity; std::mem::swap(&mut upvalues, &mut compiler.upvalues); 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); compiler.emit_op(Op::JumpIfNoMatch); tup_jump_idxes.push(compiler.len()); compiler.emit_byte(0xff); } if pattern.is_empty() { compiler.emit_op(Op::Match); } compiler.emit_op(Op::Jump); let jump_idx = compiler.len(); compiler.emit_byte(0xff); for idx in tup_jump_idxes { compiler.chunk.bytecode[idx] = compiler.len() as u8 - idx as u8 - 2; } for _ in 0..arity { compiler.emit_op(Op::Pop); } compiler.chunk.bytecode[jump_idx] = compiler.len() as u8 - jump_idx as u8 - 1; let mut no_match_jumps = vec![]; compiler.emit_op(Op::JumpIfNoMatch); // let jnm_idx = compiler.len(); no_match_jumps.push(compiler.len()); compiler.emit_byte(0xff); if guard.is_some() { let guard_expr: &'static Spanned = Box::leak(Box::new(guard.clone().unwrap())); compiler.visit(guard_expr); compiler.emit_op(Op::JumpIfFalse); no_match_jumps.push(self.len()); compiler.emit_byte(0xff); compiler.stack_depth -= 1; } 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; } } while compiler.stack_depth > 0 { compiler.pop(); } compiler.stack_depth = 0; compiler.emit_op(Op::Return); for idx in no_match_jumps { compiler.chunk.bytecode[idx] = compiler.len() as u8 - idx as u8 - 1; } // compiler.chunk.bytecode[jnm_idx] = compiler.len() as u8 - jnm_idx as u8 - 1; compiler.scope_depth -= 1; std::mem::swap(&mut compiler.upvalues, &mut upvalues); } let mut the_chunks = vec![]; for (arity, mut compiler) in compilers.into_iter() { compiler.emit_op(Op::PanicNoMatch); let chunk = compiler.chunk; if crate::DEBUG_COMPILE { println!("=== function chuncktion: {name}/{arity} ==="); chunk.dissasemble(); } the_chunks.push((arity as u8, chunk)); } let lfn = crate::value::LFn::Defined { name, doc: *doc, chunks: the_chunks, closed: RefCell::new(vec![]), }; // TODO: check if the function is already declared, and pull out the relevant OnceCell if need be let init_val = Value::Fn(Rc::new(lfn)); self.emit_constant(init_val); self.bind(name); for upvalue in upvalues { self.emit_op(Op::SetUpvalue); self.emit_byte(upvalue.stack_pos); } } FnDeclaration(name) => { let lfn = Value::Fn(Rc::new(LFn::Declared { name })); self.emit_constant(lfn); self.bind(name); } FnBody(_) => unreachable!(), // TODO: add a check to make sure times >= 0 // TODO: fix this so that the algorithm is actually correct // * truncate // * check that it's 0 or more, panic if not // * jump if 0 to end of loop // * body // * pop // * decrement counter (which is now at the top of the stack) // * jump back to jump if 0 instruction Repeat(times, body) => { self.visit(times); self.emit_op(Op::Truncate); // skip the decrement the first time self.emit_op(Op::Jump); self.emit_byte(1); // begin repeat self.emit_op(Op::Decrement); let repeat_begin = self.len(); self.emit_op(Op::Duplicate); // self.stack_depth += 1; self.emit_op(Op::JumpIfZero); self.emit_byte(0xff); // compile the body self.visit(body); // pop whatever value the body returns self.pop(); // self.emit_op(Op::Pop); self.emit_op(Op::JumpBack); // set jump points let repeat_end = self.len(); self.emit_byte(repeat_end - repeat_begin); self.chunk.bytecode[repeat_begin + 2] = (repeat_end - repeat_begin - 2) as u8; // pop the counter // self.pop(); // self.emit_op(Op::Pop); // and emit nil self.emit_constant(Value::Nil); } Loop(value, clauses) => { //algo: //first, put the values on the stack let (Ast::Tuple(members), _) = value.as_ref() else { unreachable!() }; for (i, member) in members.iter().enumerate() { self.visit(member); self.emit_op(Op::StoreAt); self.emit_byte(i); } let arity = members.len(); let stack_depth = self.stack_depth; //then, save the beginning of the loop self.emit_op(Op::Load); self.enter_loop(); self.stack_depth += arity; //next, compile each clause: let mut clauses = clauses.iter(); let mut jump_idxes = vec![]; while let Some((Ast::MatchClause(pattern, _, body), _)) = clauses.next() { self.emit_op(Op::ResetMatch); self.scope_depth += 1; let (Ast::TuplePattern(members), _) = pattern.as_ref() else { unreachable!() }; self.match_depth = arity; let mut tup_jump_idxes = vec![]; for member in members { self.match_depth -= 1; self.emit_op(Op::MatchDepth); self.emit_byte(self.match_depth); self.visit(member); self.emit_op(Op::JumpIfNoMatch); tup_jump_idxes.push(self.len()); self.emit_byte(0xff); } self.emit_op(Op::Jump); let jump_idx = self.len(); self.emit_byte(0xff); for idx in tup_jump_idxes { self.chunk.bytecode[idx] = self.len() as u8 - idx as u8 - 2; } for _ in 0..arity { self.emit_op(Op::Pop); } self.chunk.bytecode[jump_idx] = self.len() as u8 - jump_idx as u8 - 1; self.emit_op(Op::JumpIfNoMatch); let jnm_idx = self.len(); self.emit_byte(0xff); 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; } } while self.stack_depth > stack_depth + arity { self.pop(); } self.stack_depth -= arity; self.emit_op(Op::Jump); jump_idxes.push(self.len()); self.emit_byte(0xff); self.chunk.bytecode[jnm_idx] = self.len() as u8 - jnm_idx as u8; self.scope_depth -= 1; } self.emit_op(Op::PanicNoMatch); for idx in jump_idxes { self.chunk.bytecode[idx] = self.len() as u8 - idx as u8 - 1; } self.emit_op(Op::PopN); self.emit_byte(arity); self.stack_depth -= arity; self.emit_op(Op::Load); self.stack_depth += 1; self.leave_loop(); } Recur(args) => { 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()); self.emit_op(Op::JumpBack); self.emit_byte(self.len() - self.loop_idx()); } 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) => { self.emit_op(Op::PushBinding); let biter = self.bindings.iter().rev(); for binding in biter { if binding.name == word.as_str() { self.emit_byte(binding.stack_pos); break; } } self.emit_op(Op::Stringify); self.emit_op(Op::ConcatStrings); } } } } Arguments(..) | Placeholder | Do(..) | Splat(..) | InterpolatedPattern(..) | Splattern(..) => todo!(), And | Or => unreachable!(), } } pub fn disassemble(&self) { println!("=== chunk: {} ===", self.name); self.chunk.dissasemble(); } }