Refactor map and record representation

Replaces the use of `BTreeMap` and `HashMap` for maps and records with
`Vec<(Keyword, Value)>` and `Vec<(Keyword, StaticType)>`. This
simplifies the data structure and allows for ordered iteration, which is
important for serialization and comparison.

Also updates the `unpack` function in `vm.rs` to handle records as well
as lists when destructuring.
This commit is contained in:
Michael Schimmel
2026-02-20 15:10:07 +01:00
parent 6ddc1c0a11
commit ca2c85a8a4
6 changed files with 60 additions and 37 deletions
+10 -10
View File
@@ -1,4 +1,4 @@
use std::collections::{HashMap, BTreeMap};
use std::collections::HashMap;
use std::rc::Rc;
use std::any::{TypeId};
use crate::ast::types::{Value, StaticType, Keyword, Signature};
@@ -74,7 +74,7 @@ impl TypeRegistry {
/// Helper to build the shadow record for an instance.
/// This is used inside `Scriptable::wrap`.
pub struct RecordBuilder {
fields: HashMap<Keyword, Value>,
fields: Vec<(Keyword, Value)>,
}
impl Default for RecordBuilder {
@@ -86,7 +86,7 @@ impl Default for RecordBuilder {
impl RecordBuilder {
pub fn new() -> Self {
Self {
fields: HashMap::new(),
fields: Vec::new(),
}
}
@@ -94,7 +94,7 @@ impl RecordBuilder {
where F: Fn(Vec<Value>) -> Value + 'static
{
let key = Keyword::intern(name);
self.fields.insert(key, Value::Function(Rc::new(func)));
self.fields.push((key, Value::Function(Rc::new(func))));
self
}
@@ -118,7 +118,7 @@ impl RecordBuilder {
/// Helper to build the StaticType definition.
pub struct TypeBuilder {
fields: BTreeMap<Keyword, StaticType>,
fields: Vec<(Keyword, StaticType)>,
}
impl Default for TypeBuilder {
@@ -130,14 +130,14 @@ impl Default for TypeBuilder {
impl TypeBuilder {
pub fn new() -> Self {
Self {
fields: BTreeMap::new(),
fields: Vec::new(),
}
}
pub fn method(mut self, name: &str, params: Vec<StaticType>, ret: StaticType) -> Self {
let key = Keyword::intern(name);
let sig = StaticType::Function(Box::new(Signature { params: StaticType::Tuple(params), ret }));
self.fields.insert(key, sig);
self.fields.push((key, sig));
self
}
@@ -189,14 +189,14 @@ mod tests {
// Check static type
let st = TypeRegistry::resolve_type::<Person>(&registry);
if let StaticType::Record(fields) = st {
assert!(fields.contains_key(&Keyword::intern("greet")));
assert!(fields.iter().any(|(k, _)| *k == Keyword::intern("greet")));
} else {
panic!("Expected Record type");
}
// Check runtime behavior
if let Value::Record(fields) = wrapped {
let greet_fn = fields.get(&Keyword::intern("greet")).unwrap();
let greet_fn = &fields.iter().find(|(k, _)| *k == Keyword::intern("greet")).unwrap().1;
if let Value::Function(f) = greet_fn {
let res = f(vec![]);
if let Value::Text(s) = res {
@@ -226,7 +226,7 @@ mod tests {
if let Value::Function(f) = factory_val {
let instance = f(vec![Value::Text("Bob".into()), Value::Int(40)]);
if let Value::Record(fields) = instance {
let greet_fn = fields.get(&Keyword::intern("greet")).unwrap();
let greet_fn = &fields.iter().find(|(k, _)| *k == Keyword::intern("greet")).unwrap().1;
if let Value::Function(gf) = greet_fn {
let res = gf(vec![]);
if let Value::Text(s) = res {