From f3bf55fe7252ba63eb4e61664aaf78fa62b87efa Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Thu, 5 Jun 2025 13:24:32 -0400 Subject: [PATCH] add dict splats --- src/compiler.rs | 36 +++++++++++++++++++++-------- src/main.rs | 9 ++++---- src/vm.rs | 60 ++++++++++++++++++++++++++++++++----------------- 3 files changed, 71 insertions(+), 34 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index bbaa21d..59d03dc 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -44,6 +44,8 @@ pub enum Op { AppendList, ConcatList, PushDict, + AppendDict, + ConcatDict, LoadDictValue, MatchDict, PushBox, @@ -158,6 +160,8 @@ impl std::fmt::Display for Op { AppendList => "append_list", ConcatList => "concat_list", PushDict => "push_dict", + AppendDict => "append_dict", + ConcatDict => "concat_dict", LoadDictValue => "load_dict_value", MatchDict => "match_dict", PushBox => "push_box", @@ -233,7 +237,7 @@ impl Chunk { | 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 | AppendList - | ConcatList | PushList => { + | ConcatList | PushList | PushDict | AppendDict | ConcatDict => { println!("{i:04}: {op}") } Constant | MatchConstant => { @@ -243,9 +247,9 @@ impl Chunk { *i += 1; } PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple - | PushDict | PushBox | Jump | JumpIfFalse | JumpIfTrue | JumpIfNoMatch - | JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN | StoreAt | Call - | SetUpvalue | GetUpvalue => { + | 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; @@ -780,13 +784,27 @@ impl<'a> Compiler<'a> { 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); + self.stack_depth += 1; + for pair in pairs.iter().rev() { + self.visit(pair); + if matches!(pair, (Splat(..), _)) { + self.emit_op(Op::ConcatDict); + self.stack_depth -= 1; + } else { + self.emit_op(Op::AppendDict); + self.stack_depth -= 2; + } + } } + // 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); diff --git a/src/main.rs b/src/main.rs index 45f310c..4b19dfd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,11 +76,10 @@ pub fn run(src: &'static str) { pub fn main() { env::set_var("RUST_BACKTRACE", "1"); let src = " -let x = [] -let y = [1, 2, 3] -let z = [...y, 4, ...x] - -z +let x = #{:a 1, :b 2} +let y = #{x} +let z = #{...x, y} +z :y :x :a "; run(src); diff --git a/src/vm.rs b/src/vm.rs index 29604ac..b123c35 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -368,13 +368,6 @@ impl Vm { }; self.ip += 1; } - // PushList => { - // let list_len = self.chunk().bytecode[self.ip + 1]; - // let list_members = self.stack.split_off(self.stack.len() - list_len as usize); - // let list = Value::List(Box::new(Vector::from(list_members))); - // self.push(list); - // self.ip += 2; - // } PushList => { self.push(Value::List(Box::new(Vector::new()))); self.ip += 1; @@ -425,20 +418,47 @@ impl Vm { }; self.ip += 1; } + // PushDict => { + // let dict_len = self.chunk().bytecode[self.ip + 1] as usize * 2; + // let dict_members = self.stack.split_off(self.stack.len() - dict_len); + // let mut dict = HashMap::new(); + // let mut dict_iter = dict_members.iter(); + // while let Some(kw) = dict_iter.next() { + // let Value::Keyword(key) = kw else { + // unreachable!() + // }; + // let value = dict_iter.next().unwrap(); + // dict.insert(*key, value.clone()); + // } + // self.push(Value::Dict(Box::new(dict))); + // self.ip += 2; + // } PushDict => { - let dict_len = self.chunk().bytecode[self.ip + 1] as usize * 2; - let dict_members = self.stack.split_off(self.stack.len() - dict_len); - let mut dict = HashMap::new(); - let mut dict_iter = dict_members.iter(); - while let Some(kw) = dict_iter.next() { - let Value::Keyword(key) = kw else { - unreachable!() - }; - let value = dict_iter.next().unwrap(); - dict.insert(*key, value.clone()); - } - self.push(Value::Dict(Box::new(dict))); - self.ip += 2; + self.push(Value::Dict(Box::new(HashMap::new()))); + self.ip += 1; + } + AppendDict => { + let value = self.pop(); + let Value::Keyword(key) = self.pop() else { + unreachable!() + }; + let Value::Dict(mut dict) = self.pop() else { + unreachable!() + }; + dict.insert(key, value); + self.push(Value::Dict(dict)); + self.ip += 1; + } + ConcatDict => { + let Value::Dict(splatted) = self.pop() else { + return self.panic("only dicts may be splatted into dicts"); + }; + let Value::Dict(target) = self.pop() else { + unreachable!() + }; + let union = splatted.union(*target); + self.push(Value::Dict(Box::new(union))); + self.ip += 1; } LoadDictValue => { let dict_idx = self.chunk().bytecode[self.ip + 1] as usize;