Compare commits
No commits in common. "45d2e7e7422e68ae323b0af67fd1954419711673" and "4f7ba56d1f7c683129aace6ab1516a6e277e0b3d" have entirely different histories.
45d2e7e742
...
4f7ba56d1f
176
src/compiler.rs
176
src/compiler.rs
|
@ -1,4 +1,5 @@
|
||||||
use crate::parser::Ast;
|
use crate::parser::Ast;
|
||||||
|
use crate::pattern::Pattern;
|
||||||
use crate::spans::Spanned;
|
use crate::spans::Spanned;
|
||||||
use crate::value::*;
|
use crate::value::*;
|
||||||
use chumsky::prelude::SimpleSpan;
|
use chumsky::prelude::SimpleSpan;
|
||||||
|
@ -31,12 +32,8 @@ pub enum Op {
|
||||||
MatchTuple,
|
MatchTuple,
|
||||||
PushTuple,
|
PushTuple,
|
||||||
LoadTuple,
|
LoadTuple,
|
||||||
MatchList,
|
|
||||||
LoadList,
|
|
||||||
PushList,
|
PushList,
|
||||||
PushDict,
|
PushDict,
|
||||||
LoadDictValue,
|
|
||||||
MatchDict,
|
|
||||||
PushBox,
|
PushBox,
|
||||||
GetKey,
|
GetKey,
|
||||||
PanicNoWhen,
|
PanicNoWhen,
|
||||||
|
@ -78,12 +75,8 @@ impl std::fmt::Display for Op {
|
||||||
MatchTuple => "match_tuple",
|
MatchTuple => "match_tuple",
|
||||||
PushTuple => "push_tuple",
|
PushTuple => "push_tuple",
|
||||||
LoadTuple => "load_tuple",
|
LoadTuple => "load_tuple",
|
||||||
MatchList => "match_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",
|
||||||
|
@ -124,7 +117,7 @@ impl Chunk {
|
||||||
match op {
|
match op {
|
||||||
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue | MatchFalse
|
||||||
| PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch
|
| PanicIfNoMatch | MatchWord | ResetMatch | GetKey | PanicNoWhen | PanicNoMatch
|
||||||
| TypeOf | Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList => {
|
| TypeOf | Duplicate | Decrement | Truncate | Noop | LoadTuple => {
|
||||||
println!("{i:04}: {op}")
|
println!("{i:04}: {op}")
|
||||||
}
|
}
|
||||||
Constant | MatchConstant => {
|
Constant | MatchConstant => {
|
||||||
|
@ -132,9 +125,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 | MatchDict | LoadDictValue | PushTuple
|
PushBinding | MatchTuple | PushTuple | PushDict | PushList | PushBox | Jump
|
||||||
| PushDict | PushList | PushBox | Jump | JumpIfFalse | JumpIfNoMatch | JumpIfMatch
|
| JumpIfFalse | JumpIfNoMatch | JumpIfMatch | JumpBack | JumpIfZero | MatchDepth
|
||||||
| JumpBack | JumpIfZero | MatchDepth | PopN => {
|
| 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());
|
||||||
}
|
}
|
||||||
|
@ -233,18 +226,6 @@ 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 {
|
||||||
|
@ -279,6 +260,7 @@ impl Compiler {
|
||||||
self.spans.push(self.span);
|
self.spans.push(self.span);
|
||||||
self.chunk.bytecode.push(constant_index as u8);
|
self.chunk.bytecode.push(constant_index as u8);
|
||||||
self.spans.push(self.span);
|
self.spans.push(self.span);
|
||||||
|
// self.bind("");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_op(&mut self, op: Op) {
|
fn emit_op(&mut self, op: Op) {
|
||||||
|
@ -360,7 +342,17 @@ 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) => self.emit_keyword(s),
|
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;
|
||||||
|
@ -442,15 +434,19 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
PlaceholderPattern => {
|
PlaceholderPattern => {
|
||||||
self.emit_op(Op::MatchWord);
|
self.emit_op(Op::MatchWord);
|
||||||
|
// self.bind("");
|
||||||
}
|
}
|
||||||
NilPattern => {
|
NilPattern => {
|
||||||
self.emit_op(Op::MatchNil);
|
self.emit_op(Op::MatchNil);
|
||||||
|
// self.bind("");
|
||||||
}
|
}
|
||||||
BooleanPattern(b) => {
|
BooleanPattern(b) => {
|
||||||
if *b {
|
if *b {
|
||||||
self.emit_op(Op::MatchTrue);
|
self.emit_op(Op::MatchTrue);
|
||||||
|
// self.bind("");
|
||||||
} else {
|
} else {
|
||||||
self.emit_op(Op::MatchFalse);
|
self.emit_op(Op::MatchFalse);
|
||||||
|
// self.bind("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NumberPattern(n) => {
|
NumberPattern(n) => {
|
||||||
|
@ -478,6 +474,28 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
self.match_constant(Value::Interned(str_index));
|
self.match_constant(Value::Interned(str_index));
|
||||||
}
|
}
|
||||||
|
// 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) => {
|
TuplePattern(members) => {
|
||||||
self.emit_op(Op::MatchTuple);
|
self.emit_op(Op::MatchTuple);
|
||||||
self.emit_byte(members.len());
|
self.emit_byte(members.len());
|
||||||
|
@ -504,10 +522,10 @@ impl Compiler {
|
||||||
self.chunk.bytecode[idx] = self.len() as u8 - idx as u8 - 1;
|
self.chunk.bytecode[idx] = self.len() as u8 - idx as u8 - 1;
|
||||||
}
|
}
|
||||||
for _ in 0..members.len() {
|
for _ in 0..members.len() {
|
||||||
|
// self.pop();
|
||||||
// this only runs if there's no match
|
// this only runs if there's no match
|
||||||
// so don't change the representation of the stack
|
// so don't change the representation of the stack
|
||||||
// contingencies will be handled by the binding forms
|
// contingencies will be handled by the binding forms
|
||||||
// (i.e., `let` and `match`)
|
|
||||||
// thus: emit Op::Pop directly
|
// thus: emit Op::Pop directly
|
||||||
self.emit_op(Op::Pop);
|
self.emit_op(Op::Pop);
|
||||||
}
|
}
|
||||||
|
@ -515,82 +533,6 @@ impl Compiler {
|
||||||
self.len() as u8 - before_load_tup_idx as u8 - 1;
|
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;
|
self.chunk.bytecode[jump_idx] = self.len() as u8 - jump_idx as u8 - 1;
|
||||||
}
|
}
|
||||||
ListPattern(members) => {
|
|
||||||
self.emit_op(Op::MatchList);
|
|
||||||
self.emit_byte(members.len());
|
|
||||||
self.emit_op(Op::JumpIfNoMatch);
|
|
||||||
let before_load_list_idx = self.len();
|
|
||||||
self.emit_byte(0xff);
|
|
||||||
let mut jump_idxes = vec![];
|
|
||||||
self.match_depth += members.len();
|
|
||||||
self.emit_op(Op::LoadList);
|
|
||||||
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() {
|
|
||||||
// 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
|
|
||||||
// (i.e., `let` and `match`)
|
|
||||||
// thus: emit Op::Pop directly
|
|
||||||
self.emit_op(Op::Pop);
|
|
||||||
}
|
|
||||||
self.chunk.bytecode[before_load_list_idx] =
|
|
||||||
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) => {
|
Tuple(members) => {
|
||||||
for member in members {
|
for member in members {
|
||||||
self.visit(member);
|
self.visit(member);
|
||||||
|
@ -614,20 +556,12 @@ impl Compiler {
|
||||||
self.bind(name);
|
self.bind(name);
|
||||||
}
|
}
|
||||||
Dict(pairs) => {
|
Dict(pairs) => {
|
||||||
println!("Compiling dict of len {}", pairs.len());
|
|
||||||
println!("Stack len: {}", self.stack_depth);
|
|
||||||
for pair in pairs {
|
for pair in pairs {
|
||||||
self.visit(pair);
|
self.visit(pair);
|
||||||
println!("Visited pair {:?}", pair);
|
|
||||||
println!("Current stack len: {}", self.stack_depth);
|
|
||||||
}
|
}
|
||||||
self.emit_op(Op::PushDict);
|
self.emit_op(Op::PushDict);
|
||||||
self.emit_byte(pairs.len());
|
self.emit_byte(pairs.len());
|
||||||
self.stack_depth = self.stack_depth + 1 - (pairs.len() * 2);
|
self.stack_depth = self.stack_depth + 1 - pairs.len();
|
||||||
println!(
|
|
||||||
"Finished compiling dict, stack len now: {}",
|
|
||||||
self.stack_depth
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Pair(key, value) => {
|
Pair(key, value) => {
|
||||||
let existing_kw = self.chunk.keywords.iter().position(|kw| kw == key);
|
let existing_kw = self.chunk.keywords.iter().position(|kw| kw == key);
|
||||||
|
@ -663,6 +597,10 @@ impl Compiler {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 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
|
||||||
When(clauses) => {
|
When(clauses) => {
|
||||||
let mut jump_idxes = vec![];
|
let mut jump_idxes = vec![];
|
||||||
let mut clauses = clauses.iter();
|
let mut clauses = clauses.iter();
|
||||||
|
@ -673,11 +611,11 @@ impl Compiler {
|
||||||
self.emit_byte(0xff);
|
self.emit_byte(0xff);
|
||||||
self.stack_depth -= 1;
|
self.stack_depth -= 1;
|
||||||
self.visit(body);
|
self.visit(body);
|
||||||
self.stack_depth -= 1;
|
|
||||||
self.emit_op(Op::Jump);
|
self.emit_op(Op::Jump);
|
||||||
jump_idxes.push(self.len());
|
jump_idxes.push(self.len());
|
||||||
self.emit_byte(0xff);
|
self.emit_byte(0xff);
|
||||||
self.chunk.bytecode[jif_jump_idx] = self.len() as u8 - jif_jump_idx as u8 - 1;
|
self.chunk.bytecode[jif_jump_idx] = self.len() as u8 - jif_jump_idx as u8 - 1;
|
||||||
|
self.stack_depth -= 1;
|
||||||
}
|
}
|
||||||
self.emit_op(Op::PanicNoWhen);
|
self.emit_op(Op::PanicNoWhen);
|
||||||
for idx in jump_idxes {
|
for idx in jump_idxes {
|
||||||
|
@ -885,7 +823,10 @@ impl Compiler {
|
||||||
| Splat(..)
|
| Splat(..)
|
||||||
| InterpolatedPattern(..)
|
| InterpolatedPattern(..)
|
||||||
| AsPattern(..)
|
| AsPattern(..)
|
||||||
| Splattern(..) => todo!(),
|
| Splattern(..)
|
||||||
|
| ListPattern(..)
|
||||||
|
| PairPattern(..)
|
||||||
|
| DictPattern(..) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -899,8 +840,7 @@ impl Compiler {
|
||||||
match op {
|
match op {
|
||||||
Noop | Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue
|
Noop | Pop | Store | Load | Nil | True | False | MatchNil | MatchTrue
|
||||||
| MatchFalse | MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen
|
| MatchFalse | MatchWord | ResetMatch | PanicIfNoMatch | GetKey | PanicNoWhen
|
||||||
| PanicNoMatch | TypeOf | Duplicate | Truncate | Decrement | LoadTuple
|
| PanicNoMatch | TypeOf | Duplicate | Truncate | Decrement | LoadTuple => {
|
||||||
| LoadList => {
|
|
||||||
println!("{i:04}: {op}")
|
println!("{i:04}: {op}")
|
||||||
}
|
}
|
||||||
Constant | MatchConstant => {
|
Constant | MatchConstant => {
|
||||||
|
@ -908,9 +848,9 @@ impl Compiler {
|
||||||
let value = &self.chunk.constants[*next as usize].show(&self.chunk);
|
let value = &self.chunk.constants[*next as usize].show(&self.chunk);
|
||||||
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 | PushTuple | PushDict | PushList | PushBox | Jump
|
||||||
| MatchDict | LoadDictValue | PushBox | Jump | JumpIfFalse | JumpIfNoMatch
|
| JumpIfFalse | JumpIfNoMatch | JumpIfMatch | JumpBack | JumpIfZero
|
||||||
| JumpIfMatch | JumpBack | JumpIfZero | MatchDepth | PopN => {
|
| 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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,8 +74,7 @@ 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 = "
|
||||||
let #{:a x} = #{:a 1}
|
|
||||||
x
|
|
||||||
";
|
";
|
||||||
run(src);
|
run(src);
|
||||||
}
|
}
|
||||||
|
|
44
src/vm.rs
44
src/vm.rs
|
@ -265,29 +265,6 @@ impl<'a> Vm<'a> {
|
||||||
self.stack.push(list);
|
self.stack.push(list);
|
||||||
self.ip += 2;
|
self.ip += 2;
|
||||||
}
|
}
|
||||||
MatchList => {
|
|
||||||
let idx = self.stack.len() - self.match_depth as usize - 1;
|
|
||||||
let tuple_len = self.chunk.bytecode[self.ip + 1];
|
|
||||||
let scrutinee = self.stack[idx].clone();
|
|
||||||
match scrutinee {
|
|
||||||
Value::List(members) => self.matches = members.len() == tuple_len as usize,
|
|
||||||
_ => self.matches = false,
|
|
||||||
};
|
|
||||||
self.ip += 2;
|
|
||||||
}
|
|
||||||
LoadList => {
|
|
||||||
let idx = self.stack.len() - self.match_depth as usize - 1;
|
|
||||||
let tuple = self.stack[idx].clone();
|
|
||||||
match tuple {
|
|
||||||
Value::List(members) => {
|
|
||||||
for member in members.iter() {
|
|
||||||
self.push(member.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => self.panic("internal error: expected tuple"),
|
|
||||||
};
|
|
||||||
self.ip += 1;
|
|
||||||
}
|
|
||||||
PushDict => {
|
PushDict => {
|
||||||
let dict_len = self.chunk.bytecode[self.ip + 1] as usize * 2;
|
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 dict_members = self.stack.split_off(self.stack.len() - dict_len);
|
||||||
|
@ -303,27 +280,6 @@ 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))));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user