From 45d2e7e7422e68ae323b0af67fd1954419711673 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Sun, 25 May 2025 19:09:21 -0400 Subject: [PATCH] rough, not yet working, draft of dict patterns --- src/compiler.rs | 81 +++++++++++++++++++++++++++++++++++++------------ src/main.rs | 3 +- src/vm.rs | 21 +++++++++++++ 3 files changed, 85 insertions(+), 20 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 9a84d6f..aac2dd1 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -35,6 +35,8 @@ pub enum Op { LoadList, PushList, PushDict, + LoadDictValue, + MatchDict, PushBox, GetKey, PanicNoWhen, @@ -80,6 +82,8 @@ impl std::fmt::Display for Op { 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", @@ -128,9 +132,9 @@ impl Chunk { let value = &self.constants[next as usize].show(self); println!("{i:04}: {:16} {next:04}: {value}", op.to_string()); } - PushBinding | MatchTuple | MatchList | PushTuple | PushDict | PushList | PushBox - | Jump | JumpIfFalse | JumpIfNoMatch | JumpIfMatch | JumpBack | JumpIfZero - | MatchDepth | PopN => { + PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple + | PushDict | PushList | PushBox | Jump | JumpIfFalse | JumpIfNoMatch | JumpIfMatch + | JumpBack | JumpIfZero | MatchDepth | PopN => { let next = self.bytecode[i + 1]; println!("{i:04}: {:16} {next:04}", op.to_string()); } @@ -229,6 +233,18 @@ impl Compiler { self.span = root_span; } + fn emit_keyword(&mut self, s: &'static str) { + 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.emit_constant(Value::Keyword(kw_index)); + } + fn emit_constant(&mut self, val: Value) { let constant_index = self.chunk.constants.len(); if constant_index > u8::MAX as usize { @@ -344,17 +360,7 @@ impl Compiler { self.chunk.strings.push(s); self.emit_constant(Value::Interned(str_index)); } - Keyword(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.emit_constant(Value::Keyword(kw_index)); - } + Keyword(s) => self.emit_keyword(s), Block(lines) => { self.scope_depth += 1; let stack_depth = self.stack_depth; @@ -546,6 +552,45 @@ impl Compiler { 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; + for pair in pairs { + let (PairPattern(key, pattern), _) = pair else { + unreachable!() + }; + // algo: + // push the keyword onto the stack + self.emit_keyword(key); + // pull the value out of the dict + self.emit_op(Op::LoadDictValue); + self.emit_byte(dict_stack_pos); + // visit the pattern + self.visit(pattern); + // jump if no match + 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); @@ -840,9 +885,7 @@ impl Compiler { | Splat(..) | InterpolatedPattern(..) | AsPattern(..) - | Splattern(..) - | PairPattern(..) - | DictPattern(..) => todo!(), + | Splattern(..) => todo!(), } } @@ -866,8 +909,8 @@ impl Compiler { println!("{i:04}: {:16} {next:04}: {value}", op.to_string()); } PushBinding | MatchTuple | MatchList | PushTuple | PushDict | PushList - | PushBox | Jump | JumpIfFalse | JumpIfNoMatch | JumpIfMatch | JumpBack - | JumpIfZero | MatchDepth | PopN => { + | MatchDict | LoadDictValue | PushBox | Jump | JumpIfFalse | JumpIfNoMatch + | JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN => { let (_, next) = codes.next().unwrap(); println!("{i:04}: {:16} {next:04}", op.to_string()); } diff --git a/src/main.rs b/src/main.rs index bd6d4eb..135752a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,7 +74,8 @@ pub fn run(src: &'static str) { pub fn main() { env::set_var("RUST_BACKTRACE", "1"); let src = " -#{:a 1, :b 2, :c 3} +let #{:a x} = #{:a 1} +x "; run(src); } diff --git a/src/vm.rs b/src/vm.rs index f730d2c..30c1a7f 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -303,6 +303,27 @@ impl<'a> Vm<'a> { self.stack.push(Value::Dict(Box::new(dict))); self.ip += 2; } + LoadDictValue => { + let dict_idx = self.chunk.bytecode[self.ip + 1] as usize; + let Value::Dict(dict) = self.stack[dict_idx].clone() else { + unreachable!("expected dict, got something else") + }; + let Value::Keyword(key) = self.pop() else { + unreachable!("expected keyword, got something else") + }; + let value = dict.get(&key).unwrap_or(&Value::Nil); + self.push(value.clone()); + } + MatchDict => { + let idx = self.stack.len() - self.match_depth as usize - 1; + let dict_len = self.chunk.bytecode[self.ip + 1]; + let scrutinee = self.stack[idx].clone(); + match scrutinee { + Value::Dict(members) => self.matches = members.len() == dict_len as usize, + _ => self.matches = false, + }; + self.ip += 2; + } PushBox => { let val = self.pop(); self.stack.push(Value::Box(Rc::new(RefCell::new(val))));