Skip to content

Commit 1b6e0fe

Browse files
Feat: Add missing dict OpCodes.
1 parent 9d117ed commit 1b6e0fe

5 files changed

Lines changed: 82 additions & 2 deletions

File tree

compiler/src/modules/vm/handlers/attr.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ impl<'a> VM<'a> {
3333
("list", "remove") => BuiltinMethodId::ListRemove,
3434
("list", "index") => BuiltinMethodId::ListIndex,
3535
("list", "count") => BuiltinMethodId::ListCount,
36+
("dict", "get") => BuiltinMethodId::DictGet,
37+
("dict", "update") => BuiltinMethodId::DictUpdate,
38+
("dict", "pop") => BuiltinMethodId::DictPop,
3639
(ty, attr) => {
3740
return Err(attr_not_found(ty, attr));
3841
}

compiler/src/modules/vm/handlers/function.rs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ impl<'a> VM<'a> {
512512
self.push(Val::int(idx));
513513
Ok(())
514514
}
515-
ListCount => {
515+
ListCount => {
516516
if positional.len() != 1 {
517517
return Err(VmErr::Type("count() takes exactly one argument"));
518518
}
@@ -526,6 +526,62 @@ impl<'a> VM<'a> {
526526
self.push(Val::int(n));
527527
Ok(())
528528
}
529+
DictGet => {
530+
if positional.is_empty() || positional.len() > 2 {
531+
return Err(VmErr::Type("get() takes 1 or 2 arguments"));
532+
}
533+
let key = positional[0];
534+
let default = if positional.len() == 2 { positional[1] } else { Val::none() };
535+
let result = match self.heap.get(recv) {
536+
HeapObj::Dict(rc) => rc.borrow().get(&key).copied().unwrap_or(default),
537+
_ => return Err(VmErr::Type("get: receiver is not a dict")),
538+
};
539+
self.push(result);
540+
Ok(())
541+
}
542+
DictUpdate => {
543+
if positional.len() != 1 {
544+
return Err(VmErr::Type("update() takes exactly one argument"));
545+
}
546+
let pairs = match self.heap.get(positional[0]) {
547+
HeapObj::Dict(rc) => rc.borrow().entries.clone(),
548+
_ => return Err(VmErr::Type("update() argument must be a dict")),
549+
};
550+
match self.heap.get_mut(recv) {
551+
HeapObj::Dict(rc) => {
552+
let mut b = rc.borrow_mut();
553+
for (k, v) in pairs { b.insert(k, v); }
554+
}
555+
_ => return Err(VmErr::Type("update: receiver is not a dict")),
556+
}
557+
self.mark_impure();
558+
self.push(Val::none());
559+
Ok(())
560+
}
561+
DictPop => {
562+
if positional.is_empty() || positional.len() > 2 {
563+
return Err(VmErr::Type("pop() takes 1 or 2 arguments"));
564+
}
565+
let key = positional[0];
566+
let default = if positional.len() == 2 { Some(positional[1]) } else { None };
567+
let result = match self.heap.get_mut(recv) {
568+
HeapObj::Dict(rc) => {
569+
let mut b = rc.borrow_mut();
570+
if let Some(val) = b.remove(&key) {
571+
val
572+
} else {
573+
match default {
574+
Some(d) => d,
575+
None => return Err(VmErr::Value("key not found")),
576+
}
577+
}
578+
}
579+
_ => return Err(VmErr::Type("pop: receiver is not a dict")),
580+
};
581+
self.mark_impure();
582+
self.push(result);
583+
Ok(())
584+
}
529585
}
530586
}
531587

compiler/src/modules/vm/ops.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ impl<'a> VM<'a> {
143143
BuiltinMethodId::ListRemove => "<built-in method remove>".into(),
144144
BuiltinMethodId::ListIndex => "<built-in method index>".into(),
145145
BuiltinMethodId::ListCount => "<built-in method count>".into(),
146+
BuiltinMethodId::DictGet => "<built-in method get>".into(),
147+
BuiltinMethodId::DictUpdate => "<built-in method update>".into(),
148+
BuiltinMethodId::DictPop => "<built-in method pop>".into(),
146149
},
147150
HeapObj::Slice(s, e, st) => format!("slice({}, {}, {})",
148151
self.display(*s), self.display(*e), self.display(*st)),

compiler/src/modules/vm/types.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,9 @@ pub enum BuiltinMethodId {
486486
ListRemove,
487487
ListIndex,
488488
ListCount,
489+
DictGet,
490+
DictUpdate,
491+
DictPop,
489492
}
490493

491494
/*
@@ -546,6 +549,17 @@ impl Default for DictMap {
546549

547550
impl DictMap {
548551
pub fn new() -> Self { Self { entries: Vec::new(), index: HashMap::default() } }
552+
553+
pub fn remove(&mut self, key: &Val) -> Option<Val> {
554+
let idx = *self.index.get(key)?;
555+
let val = self.entries[idx].1;
556+
self.entries.remove(idx);
557+
self.index.clear();
558+
for (i, (k, _)) in self.entries.iter().enumerate() {
559+
self.index.insert(*k, i);
560+
}
561+
Some(val)
562+
}
549563
}
550564

551565
/*

compiler/tests/cases/vm.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,5 +366,9 @@
366366
{"src": "x = [1,2,3]\nx.insert(1, 99)\nprint(x)", "output": ["[1, 99, 2, 3]"], "result": "None"},
367367
{"src": "x = [1,2,2,3]\nx.remove(2)\nprint(x)", "output": ["[1, 2, 3]"], "result": "None"},
368368
{"src": "print([1,2,3].index(2))", "output": ["1"], "result": "None"},
369-
{"src": "print([1,2,2,3].count(2))", "output": ["2"], "result": "None"}
369+
{"src": "print([1,2,2,3].count(2))", "output": ["2"], "result": "None"},
370+
{"src": "d = {'a':1}\nprint(d.get('a'))", "output": ["1"], "result": "None"},
371+
{"src": "d = {'a':1}\nprint(d.get('z', 0))", "output": ["0"], "result": "None"},
372+
{"src": "d = {'a':1,'b':2}\nd.update({'b':9,'c':3})\nprint(d)", "output": ["{'a': 1, 'b': 9, 'c': 3}"], "result": "None"},
373+
{"src": "d = {'a':1}\nd.pop('a')\nprint(d)", "output": ["{}"], "result": "None"}
370374
]

0 commit comments

Comments
 (0)