rough, not yet working, draft of dict patterns

This commit is contained in:
Scott Richmond 2025-05-25 19:09:21 -04:00
parent cf51822128
commit 45d2e7e742
3 changed files with 85 additions and 20 deletions

View File

@ -35,6 +35,8 @@ pub enum Op {
LoadList, LoadList,
PushList, PushList,
PushDict, PushDict,
LoadDictValue,
MatchDict,
PushBox, PushBox,
GetKey, GetKey,
PanicNoWhen, PanicNoWhen,
@ -80,6 +82,8 @@ impl std::fmt::Display for Op {
LoadList => "load_list", LoadList => "load_list",
PushList => "push_list", PushList => "push_list",
PushDict => "push_dict", PushDict => "push_dict",
LoadDictValue => "load_dict_value",
MatchDict => "match_dict",
PushBox => "push_box", PushBox => "push_box",
GetKey => "get_key", GetKey => "get_key",
PanicNoWhen => "panic_no_when", PanicNoWhen => "panic_no_when",
@ -128,9 +132,9 @@ impl Chunk {
let value = &self.constants[next as usize].show(self); let value = &self.constants[next as usize].show(self);
println!("{i:04}: {:16} {next:04}: {value}", op.to_string()); println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
} }
PushBinding | MatchTuple | MatchList | PushTuple | PushDict | PushList | PushBox PushBinding | MatchTuple | MatchList | MatchDict | LoadDictValue | PushTuple
| Jump | JumpIfFalse | JumpIfNoMatch | JumpIfMatch | JumpBack | JumpIfZero | PushDict | PushList | PushBox | Jump | JumpIfFalse | JumpIfNoMatch | JumpIfMatch
| MatchDepth | PopN => { | JumpBack | JumpIfZero | MatchDepth | PopN => {
let next = self.bytecode[i + 1]; let next = self.bytecode[i + 1];
println!("{i:04}: {:16} {next:04}", op.to_string()); println!("{i:04}: {:16} {next:04}", op.to_string());
} }
@ -229,6 +233,18 @@ impl Compiler {
self.span = root_span; 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) { fn emit_constant(&mut self, val: Value) {
let constant_index = self.chunk.constants.len(); let constant_index = self.chunk.constants.len();
if constant_index > u8::MAX as usize { if constant_index > u8::MAX as usize {
@ -344,17 +360,7 @@ impl Compiler {
self.chunk.strings.push(s); self.chunk.strings.push(s);
self.emit_constant(Value::Interned(str_index)); self.emit_constant(Value::Interned(str_index));
} }
Keyword(s) => { Keyword(s) => self.emit_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));
}
Block(lines) => { Block(lines) => {
self.scope_depth += 1; self.scope_depth += 1;
let stack_depth = self.stack_depth; let stack_depth = self.stack_depth;
@ -546,6 +552,45 @@ impl Compiler {
self.len() as u8 - before_load_list_idx as u8 - 1; 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; 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) => { Tuple(members) => {
for member in members { for member in members {
self.visit(member); self.visit(member);
@ -840,9 +885,7 @@ impl Compiler {
| Splat(..) | Splat(..)
| InterpolatedPattern(..) | InterpolatedPattern(..)
| AsPattern(..) | AsPattern(..)
| Splattern(..) | Splattern(..) => todo!(),
| PairPattern(..)
| DictPattern(..) => todo!(),
} }
} }
@ -866,8 +909,8 @@ impl Compiler {
println!("{i:04}: {:16} {next:04}: {value}", op.to_string()); println!("{i:04}: {:16} {next:04}: {value}", op.to_string());
} }
PushBinding | MatchTuple | MatchList | PushTuple | PushDict | PushList PushBinding | MatchTuple | MatchList | PushTuple | PushDict | PushList
| PushBox | Jump | JumpIfFalse | JumpIfNoMatch | JumpIfMatch | JumpBack | MatchDict | LoadDictValue | PushBox | Jump | JumpIfFalse | JumpIfNoMatch
| JumpIfZero | MatchDepth | PopN => { | JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN => {
let (_, next) = codes.next().unwrap(); let (_, next) = codes.next().unwrap();
println!("{i:04}: {:16} {next:04}", op.to_string()); println!("{i:04}: {:16} {next:04}", op.to_string());
} }

View File

@ -74,7 +74,8 @@ pub fn run(src: &'static str) {
pub fn main() { pub fn main() {
env::set_var("RUST_BACKTRACE", "1"); env::set_var("RUST_BACKTRACE", "1");
let src = " let src = "
#{:a 1, :b 2, :c 3} let #{:a x} = #{:a 1}
x
"; ";
run(src); run(src);
} }

View File

@ -303,6 +303,27 @@ impl<'a> Vm<'a> {
self.stack.push(Value::Dict(Box::new(dict))); self.stack.push(Value::Dict(Box::new(dict)));
self.ip += 2; 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 => { PushBox => {
let val = self.pop(); let val = self.pop();
self.stack.push(Value::Box(Rc::new(RefCell::new(val)))); self.stack.push(Value::Box(Rc::new(RefCell::new(val))));