2024-12-15 21:37:51 +00:00
use crate ::parser ::Ast ;
2025-05-23 04:09:35 +00:00
use crate ::pattern ::Pattern ;
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 ;
2024-12-26 23:41:54 +00:00
use std ::cell ::OnceCell ;
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 ,
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 ,
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 ,
Store ,
Load ,
2024-12-23 00:07:42 +00:00
ResetMatch ,
MatchNil ,
MatchTrue ,
MatchFalse ,
MatchWord ,
PanicIfNoMatch ,
MatchConstant ,
MatchTuple ,
PushTuple ,
2025-05-23 04:09:35 +00:00
LoadTuple ,
2024-12-23 00:07:42 +00:00
PushList ,
PushDict ,
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 ,
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 " ,
2024-12-23 00:07:42 +00:00
Nil = > " nil " ,
True = > " true " ,
False = > " false " ,
Constant = > " constant " ,
Jump = > " jump " ,
JumpIfFalse = > " jump_if_false " ,
Pop = > " pop " ,
2025-05-23 04:09:35 +00:00
PopN = > " pop_n " ,
2024-12-23 00:07:42 +00:00
PushBinding = > " push_binding " ,
Store = > " store " ,
Load = > " load " ,
MatchNil = > " match_nil " ,
MatchTrue = > " match_true " ,
MatchFalse = > " match_false " ,
MatchWord = > " match_word " ,
ResetMatch = > " reset_match " ,
PanicIfNoMatch = > " panic_if_no_match " ,
MatchConstant = > " match_constant " ,
MatchTuple = > " match_tuple " ,
PushTuple = > " push_tuple " ,
2025-05-23 04:09:35 +00:00
LoadTuple = > " load_tuple " ,
2024-12-23 00:07:42 +00:00
PushList = > " push_list " ,
PushDict = > " push_dict " ,
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 " ,
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
}
#[ derive(Clone, Debug, PartialEq) ]
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 strings : Vec < & 'static str > ,
pub keywords : Vec < & 'static str > ,
2024-12-27 00:03:09 +00:00
}
impl Chunk {
pub fn dissasemble_instr ( & self , i : usize ) {
let op = Op ::from_u8 ( self . bytecode [ i ] ) . unwrap ( ) ;
use Op ::* ;
match op {
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
2024-12-27 04:33:57 +00:00
| PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch
2025-05-23 04:09:35 +00:00
| TypeOf | Duplicate | Decrement | Truncate | Noop | LoadTuple = > {
2024-12-27 00:03:09 +00:00
println! ( " {i:04} : {op} " )
}
Constant | MatchConstant = > {
let next = self . bytecode [ i + 1 ] ;
let value = & self . constants [ next as usize ] . show ( self ) ;
println! ( " {i:04} : {:16} {next:04} : {value} " , op . to_string ( ) ) ;
}
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump
2025-05-23 04:09:35 +00:00
| JumpIfFalse | JumpIfNoMatch | JumpIfMatch | JumpBack | JumpIfZero | MatchDepth
| PopN = > {
2024-12-27 00:03:09 +00:00
let next = self . bytecode [ i + 1 ] ;
println! ( " {i:04} : {:16} {next:04} " , op . to_string ( ) ) ;
}
}
}
2024-12-27 04:33:57 +00:00
pub fn kw_from ( & self , kw : & str ) -> Option < Value > {
self . kw_index_from ( kw ) . map ( Value ::Keyword )
}
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 }
}
}
#[ derive(Debug, Clone, PartialEq) ]
2024-12-27 00:03:09 +00:00
pub struct Compiler {
pub chunk : Chunk ,
pub bindings : Vec < Binding > ,
scope_depth : isize ,
2025-05-23 04:09:35 +00:00
match_depth : usize ,
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-05-23 04:09:35 +00:00
loop_info : Vec < LoopInfo > ,
2024-12-15 21:37:51 +00:00
}
2024-12-18 06:28:23 +00:00
fn is_binding ( expr : & Spanned < Ast > ) -> bool {
let ( ast , _ ) = expr ;
use Ast ::* ;
match ast {
Let ( .. ) | LBox ( .. ) = > true ,
2024-12-24 17:35:44 +00:00
Fn ( name , .. ) = > ! name . is_empty ( ) ,
2024-12-18 06:28:23 +00:00
_ = > false ,
}
}
2024-12-27 00:03:09 +00:00
impl Compiler {
pub fn new ( ast : & 'static Spanned < Ast > , name : & 'static str , src : & 'static str ) -> Compiler {
let chunk = Chunk {
2024-12-16 04:28:57 +00:00
constants : vec ! [ ] ,
bytecode : vec ! [ ] ,
strings : vec ! [ ] ,
keywords : vec ! [
2024-12-16 04:49:27 +00:00
" nil " , " bool " , " number " , " keyword " , " string " , " tuple " , " list " , " dict " , " box " , " fn " ,
2024-12-16 04:28:57 +00:00
] ,
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 ! [ ] ,
2024-12-16 04:28:57 +00:00
src ,
name ,
}
}
pub fn kw_from ( & self , kw : & str ) -> Option < Value > {
2024-12-23 00:07:42 +00:00
self . kw_index_from ( kw ) . map ( Value ::Keyword )
}
pub fn kw_index_from ( & self , kw : & str ) -> Option < usize > {
2024-12-27 00:03:09 +00:00
self . chunk . keywords . iter ( ) . position ( | s | * s = = kw )
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 ;
}
2024-12-16 04:28:57 +00:00
fn emit_constant ( & mut self , val : Value ) {
2024-12-27 00:03:09 +00:00
let constant_index = self . chunk . constants . len ( ) ;
2024-12-15 21:37:51 +00:00
if constant_index > u8 ::MAX as usize {
panic! (
" internal Ludus compiler error: too many constants in chunk:{}:: {} " ,
self . span , self . ast
)
}
2024-12-27 00:03:09 +00:00
self . chunk . constants . push ( val ) ;
self . chunk . bytecode . push ( Op ::Constant as u8 ) ;
2024-12-15 21:37:51 +00:00
self . spans . push ( self . span ) ;
2024-12-27 00:03:09 +00:00
self . chunk . bytecode . push ( constant_index as u8 ) ;
2024-12-15 21:37:51 +00:00
self . spans . push ( self . span ) ;
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 ,
2024-12-27 00:03:09 +00:00
None = > self . chunk . constants . len ( ) ,
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
)
}
2024-12-27 00:03:09 +00:00
if constant_index = = self . chunk . constants . len ( ) {
self . chunk . constants . push ( val ) ;
2024-12-23 00:07:42 +00:00
}
2024-12-27 00:03:09 +00:00
self . chunk . bytecode . push ( Op ::MatchConstant as u8 ) ;
2024-12-23 00:07:42 +00:00
self . spans . push ( self . span ) ;
2024-12-27 00:03:09 +00:00
self . chunk . bytecode . push ( constant_index as u8 ) ;
2024-12-23 00:07:42 +00:00
self . spans . push ( self . span ) ;
2025-05-23 04:09:35 +00:00
// self.bind("");
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 ( )
}
2024-12-18 04:45:39 +00:00
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 ) ;
}
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 ;
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
. push ( LoopInfo ::new ( self . len ( ) , self . bindings . len ( ) ) ) ;
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 ( ) ;
}
fn loop_info ( & mut self ) -> LoopInfo {
self . loop_info . last ( ) . unwrap ( ) . clone ( )
2024-12-27 05:22:01 +00:00
}
fn loop_idx ( & mut self ) -> usize {
2025-05-23 04:09:35 +00:00
self . loop_info . last ( ) . unwrap ( ) . start
}
fn loop_root ( & mut self ) -> usize {
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 ) = > {
2024-12-27 00:03:09 +00:00
let existing_str = self . chunk . strings . iter ( ) . position ( | e | e = = s ) ;
2024-12-23 00:07:42 +00:00
let str_index = match existing_str {
Some ( idx ) = > idx ,
2024-12-27 00:03:09 +00:00
None = > self . chunk . strings . len ( ) ,
2024-12-23 00:07:42 +00:00
} ;
2024-12-27 00:03:09 +00:00
self . chunk . strings . push ( s ) ;
2024-12-16 04:28:57 +00:00
self . emit_constant ( Value ::Interned ( str_index ) ) ;
2024-12-15 21:37:51 +00:00
}
2024-12-16 04:28:57 +00:00
Keyword ( s ) = > {
2024-12-27 00:03:09 +00:00
let existing_kw = self . chunk . keywords . iter ( ) . position ( | kw | kw = = s ) ;
2024-12-16 04:28:57 +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-16 04:28:57 +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
}
2024-12-16 04:28:57 +00:00
self . emit_constant ( Value ::Keyword ( kw_index ) ) ;
}
Block ( lines ) = > {
self . scope_depth + = 1 ;
2025-05-23 17:42:29 +00:00
let stack_depth = self . stack_depth ;
2024-12-18 06:28:23 +00:00
for expr in lines . iter ( ) . take ( lines . len ( ) - 1 ) {
if is_binding ( expr ) {
2024-12-27 04:33:57 +00:00
self . visit ( expr ) ;
2024-12-18 06:28:23 +00:00
} else {
self . visit ( expr ) ;
2025-05-23 04:09:35 +00:00
self . pop ( ) ;
// self.emit_op(Op::Pop);
2024-12-18 06:28:23 +00:00
}
2024-12-15 21:37:51 +00:00
}
2024-12-27 04:33:57 +00:00
let last_expr = lines . last ( ) . unwrap ( ) ;
2025-05-23 17:42:29 +00:00
// TODO: make sure this actually returns the RHS of the expression
2024-12-27 04:33:57 +00:00
if is_binding ( last_expr ) {
self . visit ( last_expr ) ;
self . emit_op ( Op ::Duplicate ) ;
2025-05-23 04:09:35 +00:00
self . stack_depth + = 1 ;
2024-12-27 04:33:57 +00:00
} else {
self . visit ( last_expr ) ;
2025-05-23 04:09:35 +00:00
self . stack_depth + = 1 ;
2024-12-27 04:33:57 +00:00
}
2024-12-18 04:45:39 +00:00
self . emit_op ( Op ::Store ) ;
2024-12-16 04:28:57 +00:00
self . scope_depth - = 1 ;
2025-05-23 17:42:29 +00:00
// reset bindings
2024-12-18 04:45:39 +00:00
while let Some ( binding ) = self . bindings . last ( ) {
if binding . depth > self . scope_depth {
2025-05-23 17:42:29 +00:00
let binding = self . bindings . pop ( ) . unwrap ( ) ;
println! ( " popping: {:?} " , binding ) ;
2024-12-18 04:45:39 +00:00
} else {
break ;
}
}
2025-05-23 17:42:29 +00:00
// reset stack
while self . stack_depth > stack_depth + 1 {
self . pop ( ) ;
}
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 ) = > {
self . visit ( cond ) ;
2024-12-27 04:46:06 +00:00
let jif_idx = self . len ( ) ;
2024-12-16 04:28:57 +00:00
self . emit_op ( Op ::JumpIfFalse ) ;
2024-12-27 04:46:06 +00:00
self . emit_byte ( 0xff ) ;
2025-05-23 04:09:35 +00:00
self . stack_depth - = 1 ;
2024-12-16 04:28:57 +00:00
self . visit ( then ) ;
2024-12-27 04:46:06 +00:00
let jump_idx = self . len ( ) ;
2024-12-16 04:28:57 +00:00
self . emit_op ( Op ::Jump ) ;
2024-12-27 04:46:06 +00:00
self . emit_byte ( 0xff ) ;
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 ;
2024-12-27 04:33:57 +00:00
let jump_offset = end_idx - jump_idx - 2 ;
2024-12-27 00:03:09 +00:00
self . chunk . bytecode [ jif_idx + 1 ] = jif_offset as u8 ;
self . chunk . bytecode [ jump_idx + 1 ] = jump_offset as u8 ;
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 ) = > {
2024-12-23 00:07:42 +00:00
self . emit_op ( Op ::MatchWord ) ;
2024-12-18 04:45:39 +00:00
self . bind ( name ) ;
}
Word ( name ) = > {
self . emit_op ( Op ::PushBinding ) ;
2025-05-23 04:09:35 +00:00
self . stack_depth + = 1 ;
let biter = self . bindings . iter ( ) . rev ( ) ;
for binding in biter {
2024-12-18 04:45:39 +00:00
if binding . name = = * name {
2025-05-23 04:09:35 +00:00
self . emit_byte ( binding . stack_pos ) ;
2024-12-18 04:45:39 +00:00
break ;
}
}
}
PlaceholderPattern = > {
2024-12-23 00:07:42 +00:00
self . emit_op ( Op ::MatchWord ) ;
2025-05-23 04:09:35 +00:00
// self.bind("");
2024-12-18 04:45:39 +00:00
}
2024-12-23 00:07:42 +00:00
NilPattern = > {
self . emit_op ( Op ::MatchNil ) ;
2025-05-23 04:09:35 +00:00
// self.bind("");
2024-12-23 00:07:42 +00:00
}
BooleanPattern ( b ) = > {
if * b {
self . emit_op ( Op ::MatchTrue ) ;
2025-05-23 04:09:35 +00:00
// self.bind("");
2024-12-23 00:07:42 +00:00
} else {
self . emit_op ( Op ::MatchFalse ) ;
2025-05-23 04:09:35 +00:00
// self.bind("");
2024-12-23 00:07:42 +00:00
}
}
NumberPattern ( n ) = > {
self . match_constant ( Value ::Number ( * n ) ) ;
}
KeywordPattern ( s ) = > {
2024-12-27 00:03:09 +00:00
let existing_kw = self . chunk . keywords . iter ( ) . position ( | kw | kw = = s ) ;
2024-12-23 00:07:42 +00:00
let kw_index = match existing_kw {
Some ( index ) = > index ,
2024-12-27 00:03:09 +00:00
None = > self . chunk . keywords . len ( ) ,
2024-12-23 00:07:42 +00:00
} ;
2024-12-27 00:03:09 +00:00
if kw_index = = self . chunk . keywords . len ( ) {
self . chunk . keywords . push ( s ) ;
2024-12-23 00:07:42 +00:00
}
self . match_constant ( Value ::Keyword ( kw_index ) ) ;
}
StringPattern ( s ) = > {
2024-12-27 00:03:09 +00:00
let existing_str = self . chunk . strings . iter ( ) . position ( | e | e = = s ) ;
2024-12-23 00:07:42 +00:00
let str_index = match existing_str {
Some ( idx ) = > idx ,
2024-12-27 00:03:09 +00:00
None = > self . chunk . strings . len ( ) ,
2024-12-23 00:07:42 +00:00
} ;
2024-12-27 00:03:09 +00:00
if str_index = = self . chunk . strings . len ( ) {
self . chunk . strings . push ( s )
2024-12-23 00:07:42 +00:00
}
self . match_constant ( Value ::Interned ( str_index ) ) ;
}
2025-05-23 04:09:35 +00:00
// TODO: finish this work
// What's going on:
// Currently, bindings are made in lockstep with the stack.
// And the index of the binding in the bindings array in the compiler gets converted to a u8 as the index into the stack
// I suspect this will have to change when we get stack frames
// But what's happening here is that nested tuple bindings are out of order
// When a tuple gets unfolded at the end of the stack, the binding that matches is now not where you'd expect
// The whole "match depth" construct, while working, is what allows for out-of-order matching/binding
// So either:
// - Bindings need to work differently, where there's some way of representing them that's not just the index of where the name is
// - Or we need a way of working with nested tuples that ensure bindings continue to be orederly
// My sense is that the former is the correct approach.
// It introduces some complexity: a binding will have to be a `(name, offset)` tuple or some such
// Working with nested tuples could perhaps be solved by representing tuples on the stack, but then you're going to run into similar problems with list and dict patterns
// And so, the thing to, I think, is to get clever with an offset
// But to do that, probably the compiler needs to model the stack? Ugh.
// SO, THEN:
// [x] 1. we need a stack counter, that increases any time anything gets pushed to the stack, and decreases any time anything gets popped
// [x] 2. We need to change the representation of bindings to be a tuple (name, index), where index is `stack size - match depth`
// [x] 3. This means we get to remove the "silent" bindings where all patterns add a `""` binding
// [x] 4. Question: given that we need both of these things, should I model this as methods rather than explicit `emit_op` calls? Probably.
// Currently: there's still an off by one error with the current test code with nested tuples, but I can prolly fix this?
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.pop();
// this only runs if there's no match
// so don't change the representation of the stack
// contingencies will be handled by the binding forms
// thus: emit Op::Pop directly
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 ;
}
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 ) = > {
for member in members {
self . visit ( member ) ;
}
self . emit_op ( Op ::PushList ) ;
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
}
LBox ( name , expr ) = > {
self . visit ( expr ) ;
self . emit_op ( Op ::PushBox ) ;
2025-05-23 04:09:35 +00:00
self . stack_depth + = 1 ;
2024-12-23 00:07:42 +00:00
self . bind ( name ) ;
}
Dict ( pairs ) = > {
for pair in pairs {
self . visit ( pair ) ;
}
self . emit_op ( Op ::PushDict ) ;
2024-12-27 04:46:06 +00:00
self . emit_byte ( pairs . len ( ) ) ;
2025-05-23 04:09:35 +00:00
self . stack_depth = self . stack_depth + 1 - pairs . len ( ) ;
2024-12-23 00:07:42 +00:00
}
Pair ( key , value ) = > {
2024-12-27 00:03:09 +00:00
let existing_kw = self . chunk . keywords . iter ( ) . position ( | kw | kw = = key ) ;
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 ( key ) ;
2024-12-23 00:07:42 +00:00
}
self . emit_constant ( Value ::Keyword ( kw_index ) ) ;
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 ) ;
}
( Keyword ( _ ) , Arguments ( args ) ) = > {
self . visit ( & args [ 0 ] ) ;
self . visit ( first ) ;
self . emit_op ( Op ::GetKey ) ;
}
( Word ( _ ) , Arguments ( _ ) ) = > {
todo! ( )
}
_ = > unreachable! ( ) ,
}
2024-12-24 17:35:44 +00:00
// TODO: implement longer synthetic expressions
2024-12-23 00:07:42 +00:00
for term in rest {
todo! ( )
}
}
2025-05-23 04:09:35 +00:00
// TODO: Keep track of the stack in
// WHEN and MATCH:
// each needs to just hold onto the stack depth
// before each clause, and reset it after each
2024-12-23 00:07:42 +00:00
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 ) ;
2024-12-27 04:46:06 +00:00
let jif_jump_idx = self . len ( ) ;
self . emit_byte ( 0xff ) ;
2025-05-23 21:59:09 +00:00
self . stack_depth - = 1 ;
2024-12-23 00:07:42 +00:00
self . visit ( body ) ;
self . emit_op ( Op ::Jump ) ;
2024-12-27 04:46:06 +00:00
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 ;
2025-05-23 21:59:09 +00:00
self . stack_depth - = 1 ;
2024-12-23 00:07:42 +00:00
}
self . emit_op ( Op ::PanicNoWhen ) ;
for idx in jump_idxes {
2025-05-23 21:59:09 +00:00
self . chunk . bytecode [ idx ] = self . len ( ) as u8 - idx as u8 - 1 ;
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 ) = > {
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 ( ) {
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 ) ;
self . emit_op ( Op ::JumpIfNoMatch ) ;
2024-12-27 04:46:06 +00:00
let jnm_jump_idx = self . len ( ) ;
self . emit_byte ( 0xff ) ;
2024-12-27 04:48:38 +00:00
// conditional compilation of guards
// hard to DRY out
match guard . as_ref ( ) {
2025-05-23 17:42:29 +00:00
Some ( _ ) = > todo! ( ) ,
// Some(expr) => {
// self.visit(expr);
// self.emit_op(Op::JumpIfFalse);
// let jif_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.pop();
// // self.emit_op(Op::Pop);
// self.bindings.pop();
// } else {
// break;
// }
// }
// self.emit_op(Op::Jump);
// jump_idxes.push(self.len());
// self.emit_byte(0xff);
// self.chunk.bytecode[jnm_jump_idx] =
// self.len() as u8 - jnm_jump_idx as u8 - 1;
// self.chunk.bytecode[jif_idx] = self.len() as u8 - jif_idx as u8 - 1;
// }
2024-12-27 04:48:38 +00:00
None = > {
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
}
2025-05-23 17:42:29 +00:00
while self . stack_depth > stack_depth {
self . pop ( ) ;
}
2024-12-27 04:48:38 +00:00
self . emit_op ( Op ::Jump ) ;
jump_idxes . push ( self . len ( ) ) ;
self . emit_byte ( 0xff ) ;
self . chunk . bytecode [ jnm_jump_idx ] =
self . len ( ) as u8 - jnm_jump_idx as u8 - 1 ;
2024-12-27 04:46:06 +00:00
}
2024-12-23 00:33:59 +00:00
}
}
self . emit_op ( Op ::PanicNoMatch ) ;
for idx in jump_idxes {
2025-05-23 04:09:35 +00:00
self . chunk . bytecode [ idx ] = self . len ( ) as u8 - idx as u8 - 1 ;
2024-12-23 00:33:59 +00:00
}
2025-05-23 17:42:29 +00:00
while self . stack_depth > stack_depth {
self . pop ( ) ;
}
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 ) = > {
2024-12-26 23:41:54 +00:00
// first, declare the function
// TODO: or, check if the function has already been declared!
let init_val = Value ::Fn ( Rc ::new ( OnceCell ::new ( ) ) ) ;
self . emit_constant ( init_val ) ;
self . bind ( name ) ;
// compile the function
2024-12-27 00:03:09 +00:00
let mut compiler = Compiler ::new ( body , self . name , self . src ) ;
compiler . compile ( ) ;
2024-12-24 17:35:44 +00:00
if crate ::DEBUG_COMPILE {
println! ( " ==function: {name} == " ) ;
2024-12-27 00:03:09 +00:00
compiler . disassemble ( ) ;
2024-12-24 17:35:44 +00:00
}
2024-12-26 23:41:54 +00:00
2024-12-24 17:35:44 +00:00
let lfn = crate ::value ::LFn {
name ,
doc : * doc ,
2024-12-27 00:03:09 +00:00
chunk : compiler . chunk ,
2024-12-26 23:41:54 +00:00
closed : vec ! [ ] ,
2024-12-24 17:35:44 +00:00
} ;
2024-12-26 23:41:54 +00:00
// TODO: close over everything accessed in the function
// TODO: pull the function off the stack, and set the OnceCell.
2024-12-24 17:35:44 +00:00
}
FnDeclaration ( name ) = > {
2024-12-26 23:41:54 +00:00
let lfn = Value ::Fn ( Rc ::new ( OnceCell ::new ( ) ) ) ;
self . emit_constant ( lfn ) ;
self . bind ( name ) ;
2024-12-24 17:35:44 +00:00
}
FnBody ( clauses ) = > {
self . emit_op ( Op ::ResetMatch ) ;
2025-05-23 04:09:35 +00:00
todo! ( ) ;
2024-12-23 15:55:28 +00:00
}
2025-05-23 04:09:35 +00:00
// TODO: add stack-tracking to this
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 ) ;
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-05-23 04:09:35 +00:00
self . stack_depth + = 1 ;
2024-12-27 04:33:57 +00:00
self . emit_op ( Op ::JumpIfZero ) ;
2024-12-27 04:46:06 +00:00
self . emit_byte ( 0xff ) ;
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 ( ) ;
// self.emit_op(Op::Pop);
2024-12-27 04:33:57 +00:00
self . emit_op ( Op ::JumpBack ) ;
// set jump points
2024-12-27 04:46:06 +00:00
let repeat_end = self . len ( ) ;
self . emit_byte ( repeat_end - repeat_begin ) ;
2024-12-27 04:33:57 +00:00
self . chunk . bytecode [ repeat_begin + 2 ] = ( repeat_end - repeat_begin - 2 ) as u8 ;
// pop the counter
2025-05-23 04:09:35 +00:00
self . pop ( ) ;
// self.emit_op(Op::Pop);
2024-12-27 04:33:57 +00:00
// and emit nil
self . emit_constant ( Value ::Nil ) ;
}
2024-12-27 05:22:01 +00:00
Loop ( value , clauses ) = > {
2025-05-23 04:09:35 +00:00
todo! ( ) ;
2024-12-27 05:22:01 +00:00
//algo:
//first, put the values on the stack
let ( Ast ::Tuple ( members ) , _ ) = value . as_ref ( ) else {
unreachable! ( )
} ;
for member in members {
self . visit ( member ) ;
}
let arity = members . len ( ) ;
//then, save the beginning of the loop
self . enter_loop ( ) ;
self . emit_op ( Op ::ResetMatch ) ;
//next, compile each clause:
let mut clauses = clauses . iter ( ) ;
2025-05-23 04:09:35 +00:00
let mut jump_idxes = vec! [ ] ;
2024-12-27 05:22:01 +00:00
while let Some ( ( Ast ::MatchClause ( pattern , _ , body ) , _ ) ) = clauses . next ( ) {
self . scope_depth + = 1 ;
let ( Ast ::TuplePattern ( members ) , _ ) = pattern . as_ref ( ) else {
unreachable! ( )
} ;
2025-05-23 04:09:35 +00:00
let mut match_depth = arity ;
let mut members = members . iter ( ) ;
while match_depth > 0 {
self . emit_op ( Op ::MatchDepth ) ;
self . emit_byte ( match_depth - 1 ) ;
self . visit ( members . next ( ) . unwrap ( ) ) ;
match_depth - = 1 ;
}
self . emit_op ( Op ::JumpIfNoMatch ) ;
let jnm_idx = self . len ( ) ;
self . emit_byte ( 0xff ) ;
self . visit ( body ) ;
self . emit_op ( Op ::Jump ) ;
self . emit_byte ( 0xff ) ;
jump_idxes . push ( self . len ( ) ) ;
self . chunk . bytecode [ jnm_idx ] = self . len ( ) as u8 - jnm_idx as u8 ;
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 ) ;
// TODO: fix up jump indexes a la match
self . leave_loop ( ) ;
}
Recur ( _ ) = > {
// algo
// visit each member of the arguments
// then store those in the return register
// then pop back to loop stack root
// then jump to loop start
2024-12-27 05:22:01 +00:00
}
2024-12-27 04:33:57 +00:00
Interpolated ( .. )
| Arguments ( .. )
| Placeholder
| Panic ( .. )
| Do ( .. )
| Splat ( .. )
| InterpolatedPattern ( .. )
| AsPattern ( .. )
| Splattern ( .. )
| ListPattern ( .. )
| PairPattern ( .. )
| DictPattern ( .. ) = > todo! ( ) ,
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 ) ;
2024-12-16 04:28:57 +00:00
println! ( " IDX | CODE | INFO " ) ;
2024-12-27 00:03:09 +00:00
let mut codes = self . chunk . bytecode . iter ( ) . enumerate ( ) ;
2024-12-15 21:37:51 +00:00
while let Some ( ( i , byte ) ) = codes . next ( ) {
2024-12-16 04:28:57 +00:00
let op = Op ::from_u8 ( * byte ) . unwrap ( ) ;
use Op ::* ;
2024-12-15 21:37:51 +00:00
match op {
2025-05-23 04:09:35 +00:00
Noop | Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue
| MatchFalse | MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen
| PanicNoMatch | TypeOf | Duplicate | Truncate | Decrement | LoadTuple = > {
2024-12-23 00:07:42 +00:00
println! ( " {i:04} : {op} " )
}
Constant | MatchConstant = > {
2024-12-16 04:28:57 +00:00
let ( _ , next ) = codes . next ( ) . unwrap ( ) ;
2024-12-27 00:03:09 +00:00
let value = & self . chunk . constants [ * next as usize ] . show ( & self . chunk ) ;
2024-12-16 04:28:57 +00:00
println! ( " {i:04} : {:16} {next:04} : {value} " , op . to_string ( ) ) ;
}
2024-12-23 00:33:59 +00:00
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump
2025-05-23 04:09:35 +00:00
| JumpIfFalse | JumpIfNoMatch | JumpIfMatch | JumpBack | JumpIfZero
| MatchDepth | PopN = > {
2024-12-18 04:45:39 +00:00
let ( _ , next ) = codes . next ( ) . unwrap ( ) ;
println! ( " {i:04} : {:16} {next:04} " , op . to_string ( ) ) ;
}
2024-12-15 21:37:51 +00:00
}
}
}
}