add dict splats

This commit is contained in:
Scott Richmond 2025-06-05 13:24:32 -04:00
parent b557c487cc
commit f3bf55fe72
3 changed files with 71 additions and 34 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;