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:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
;; Complex Data Structure Test
|
;; Complex Data Structure Test
|
||||||
;; Benchmark: 51.4us
|
;; Benchmark: 51.4us
|
||||||
;; Output: {:input 10, :name "Fibonacci", :output 55}
|
;; Output: {:name "Fibonacci", :input 10, :output 55}
|
||||||
(do
|
(do
|
||||||
(def fib (fn [n]
|
(def fib (fn [n]
|
||||||
(if (< n 2)
|
(if (< n 2)
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
;; Record to Tuple Mapping Test
|
||||||
|
;; Output: 30
|
||||||
|
(do
|
||||||
|
(def rec {:x 10 :y 20})
|
||||||
|
|
||||||
|
;; Map record to tuple pattern
|
||||||
|
(def add-tuple (fn [[a b]] (+ a b)))
|
||||||
|
|
||||||
|
(add-tuple rec)
|
||||||
|
)
|
||||||
@@ -309,13 +309,13 @@ impl TypeChecker {
|
|||||||
|
|
||||||
BoundKind::Map { entries } => {
|
BoundKind::Map { entries } => {
|
||||||
let mut typed_entries = Vec::new();
|
let mut typed_entries = Vec::new();
|
||||||
let mut fields = std::collections::BTreeMap::new();
|
let mut fields = Vec::new();
|
||||||
for (k, v) in entries {
|
for (k, v) in entries {
|
||||||
let kt = self.check_node(k, ctx)?;
|
let kt = self.check_node(k, ctx)?;
|
||||||
let vt = self.check_node(v, ctx)?;
|
let vt = self.check_node(v, ctx)?;
|
||||||
|
|
||||||
if let BoundKind::Constant(crate::ast::types::Value::Keyword(kw)) = &kt.kind {
|
if let BoundKind::Constant(crate::ast::types::Value::Keyword(kw)) = &kt.kind {
|
||||||
fields.insert(*kw, vt.ty.clone());
|
fields.push((*kw, vt.ty.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
typed_entries.push((kt, vt));
|
typed_entries.push((kt, vt));
|
||||||
@@ -484,8 +484,8 @@ mod tests {
|
|||||||
let ty = get_ret_type(&typed);
|
let ty = get_ret_type(&typed);
|
||||||
if let StaticType::Record(fields) = ty {
|
if let StaticType::Record(fields) = ty {
|
||||||
assert_eq!(fields.len(), 2);
|
assert_eq!(fields.len(), 2);
|
||||||
assert_eq!(fields.get(&Keyword::intern("x")), Some(&StaticType::Int));
|
assert_eq!(fields[0], (Keyword::intern("x"), StaticType::Int));
|
||||||
assert_eq!(fields.get(&Keyword::intern("y")), Some(&StaticType::Float));
|
assert_eq!(fields[1], (Keyword::intern("y"), StaticType::Float));
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected Record, got {:?}", ty);
|
panic!("Expected Record, got {:?}", ty);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::collections::{HashMap, BTreeMap};
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::any::{TypeId};
|
use std::any::{TypeId};
|
||||||
use crate::ast::types::{Value, StaticType, Keyword, Signature};
|
use crate::ast::types::{Value, StaticType, Keyword, Signature};
|
||||||
@@ -74,7 +74,7 @@ impl TypeRegistry {
|
|||||||
/// Helper to build the shadow record for an instance.
|
/// Helper to build the shadow record for an instance.
|
||||||
/// This is used inside `Scriptable::wrap`.
|
/// This is used inside `Scriptable::wrap`.
|
||||||
pub struct RecordBuilder {
|
pub struct RecordBuilder {
|
||||||
fields: HashMap<Keyword, Value>,
|
fields: Vec<(Keyword, Value)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RecordBuilder {
|
impl Default for RecordBuilder {
|
||||||
@@ -86,7 +86,7 @@ impl Default for RecordBuilder {
|
|||||||
impl RecordBuilder {
|
impl RecordBuilder {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
fields: HashMap::new(),
|
fields: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ impl RecordBuilder {
|
|||||||
where F: Fn(Vec<Value>) -> Value + 'static
|
where F: Fn(Vec<Value>) -> Value + 'static
|
||||||
{
|
{
|
||||||
let key = Keyword::intern(name);
|
let key = Keyword::intern(name);
|
||||||
self.fields.insert(key, Value::Function(Rc::new(func)));
|
self.fields.push((key, Value::Function(Rc::new(func))));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ impl RecordBuilder {
|
|||||||
|
|
||||||
/// Helper to build the StaticType definition.
|
/// Helper to build the StaticType definition.
|
||||||
pub struct TypeBuilder {
|
pub struct TypeBuilder {
|
||||||
fields: BTreeMap<Keyword, StaticType>,
|
fields: Vec<(Keyword, StaticType)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TypeBuilder {
|
impl Default for TypeBuilder {
|
||||||
@@ -130,14 +130,14 @@ impl Default for TypeBuilder {
|
|||||||
impl TypeBuilder {
|
impl TypeBuilder {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
fields: BTreeMap::new(),
|
fields: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn method(mut self, name: &str, params: Vec<StaticType>, ret: StaticType) -> Self {
|
pub fn method(mut self, name: &str, params: Vec<StaticType>, ret: StaticType) -> Self {
|
||||||
let key = Keyword::intern(name);
|
let key = Keyword::intern(name);
|
||||||
let sig = StaticType::Function(Box::new(Signature { params: StaticType::Tuple(params), ret }));
|
let sig = StaticType::Function(Box::new(Signature { params: StaticType::Tuple(params), ret }));
|
||||||
self.fields.insert(key, sig);
|
self.fields.push((key, sig));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,14 +189,14 @@ mod tests {
|
|||||||
// Check static type
|
// Check static type
|
||||||
let st = TypeRegistry::resolve_type::<Person>(®istry);
|
let st = TypeRegistry::resolve_type::<Person>(®istry);
|
||||||
if let StaticType::Record(fields) = st {
|
if let StaticType::Record(fields) = st {
|
||||||
assert!(fields.contains_key(&Keyword::intern("greet")));
|
assert!(fields.iter().any(|(k, _)| *k == Keyword::intern("greet")));
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected Record type");
|
panic!("Expected Record type");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check runtime behavior
|
// Check runtime behavior
|
||||||
if let Value::Record(fields) = wrapped {
|
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 {
|
if let Value::Function(f) = greet_fn {
|
||||||
let res = f(vec![]);
|
let res = f(vec![]);
|
||||||
if let Value::Text(s) = res {
|
if let Value::Text(s) = res {
|
||||||
@@ -226,7 +226,7 @@ mod tests {
|
|||||||
if let Value::Function(f) = factory_val {
|
if let Value::Function(f) = factory_val {
|
||||||
let instance = f(vec![Value::Text("Bob".into()), Value::Int(40)]);
|
let instance = f(vec![Value::Text("Bob".into()), Value::Int(40)]);
|
||||||
if let Value::Record(fields) = instance {
|
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 {
|
if let Value::Function(gf) = greet_fn {
|
||||||
let res = gf(vec![]);
|
let res = gf(vec![]);
|
||||||
if let Value::Text(s) = res {
|
if let Value::Text(s) = res {
|
||||||
|
|||||||
+6
-8
@@ -1,4 +1,4 @@
|
|||||||
use std::collections::{HashMap, BTreeMap};
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -66,7 +66,7 @@ pub enum Value {
|
|||||||
Text(Rc<str>),
|
Text(Rc<str>),
|
||||||
Keyword(Keyword),
|
Keyword(Keyword),
|
||||||
List(Rc<Vec<Value>>),
|
List(Rc<Vec<Value>>),
|
||||||
Record(Rc<HashMap<Keyword, Value>>),
|
Record(Rc<Vec<(Keyword, Value)>>),
|
||||||
Function(Rc<dyn Fn(Vec<Value>) -> Value>),
|
Function(Rc<dyn Fn(Vec<Value>) -> Value>),
|
||||||
Object(Rc<dyn Object>), // For compiled Closures and other opaque types
|
Object(Rc<dyn Object>), // For compiled Closures and other opaque types
|
||||||
Cell(Rc<RefCell<Value>>), // Boxed value for captures
|
Cell(Rc<RefCell<Value>>), // Boxed value for captures
|
||||||
@@ -93,7 +93,7 @@ pub enum StaticType {
|
|||||||
Tuple(Vec<StaticType>), // Heterogeneous fixed-size
|
Tuple(Vec<StaticType>), // Heterogeneous fixed-size
|
||||||
Vector(Box<StaticType>, usize), // Homogeneous fixed-size
|
Vector(Box<StaticType>, usize), // Homogeneous fixed-size
|
||||||
Matrix(Box<StaticType>, Vec<usize>), // Multi-dimensional homogeneous
|
Matrix(Box<StaticType>, Vec<usize>), // Multi-dimensional homogeneous
|
||||||
Record(Rc<BTreeMap<Keyword, StaticType>>),
|
Record(Rc<Vec<(Keyword, StaticType)>>),
|
||||||
Function(Box<Signature>),
|
Function(Box<Signature>),
|
||||||
FunctionOverloads(Vec<Signature>),
|
FunctionOverloads(Vec<Signature>),
|
||||||
Object(&'static str),
|
Object(&'static str),
|
||||||
@@ -255,9 +255,9 @@ impl Value {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Value::Record(r) => {
|
Value::Record(r) => {
|
||||||
let mut fields = BTreeMap::new();
|
let mut fields = Vec::with_capacity(r.len());
|
||||||
for (k, v) in r.iter() {
|
for (k, v) in r.iter() {
|
||||||
fields.insert(*k, v.static_type());
|
fields.push((*k, v.static_type()));
|
||||||
}
|
}
|
||||||
StaticType::Record(Rc::new(fields))
|
StaticType::Record(Rc::new(fields))
|
||||||
},
|
},
|
||||||
@@ -293,10 +293,8 @@ impl fmt::Display for Value {
|
|||||||
write!(f, "]")
|
write!(f, "]")
|
||||||
},
|
},
|
||||||
Value::Record(r) => {
|
Value::Record(r) => {
|
||||||
let mut entries: Vec<_> = r.iter().collect();
|
|
||||||
entries.sort_by_key(|(k, _)| k.name());
|
|
||||||
write!(f, "{{")?;
|
write!(f, "{{")?;
|
||||||
for (i, (k, v)) in entries.iter().enumerate() {
|
for (i, (k, v)) in r.iter().enumerate() {
|
||||||
if i > 0 { write!(f, ", ")?; }
|
if i > 0 { write!(f, ", ")?; }
|
||||||
write!(f, ":{} {}", k.name(), v)?;
|
write!(f, ":{} {}", k.name(), v)?;
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-14
@@ -1,6 +1,5 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind, TypedNode};
|
use crate::ast::compiler::bound_nodes::{Address, BoundKind, TypedNode};
|
||||||
use crate::ast::types::{Value, Object};
|
use crate::ast::types::{Value, Object};
|
||||||
@@ -318,13 +317,13 @@ macro_rules! dispatch_eval {
|
|||||||
},
|
},
|
||||||
|
|
||||||
BoundKind::Map { entries } => {
|
BoundKind::Map { entries } => {
|
||||||
let mut map = HashMap::new();
|
let mut map = Vec::with_capacity(entries.len());
|
||||||
for (k, v) in entries {
|
for (k, v) in entries {
|
||||||
let key = $self.$eval_method($($observer,)? k)?;
|
let key = $self.$eval_method($($observer,)? k)?;
|
||||||
let val = $self.$eval_method($($observer,)? v)?;
|
let val = $self.$eval_method($($observer,)? v)?;
|
||||||
|
|
||||||
if let Value::Keyword(kw) = key {
|
if let Value::Keyword(kw) = key {
|
||||||
map.insert(kw, val);
|
map.push((kw, val));
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("Map key must be keyword, got {}", key));
|
return Err(format!("Map key must be keyword, got {}", key));
|
||||||
}
|
}
|
||||||
@@ -678,19 +677,35 @@ impl VM {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
BoundKind::Tuple { elements } => {
|
BoundKind::Tuple { elements } => {
|
||||||
// If the current value at offset is a List, we dive into it.
|
// If the current value at offset is a List or Record, we dive into it.
|
||||||
// Otherwise, we assume the list was already flattened (e.g. by Specializer).
|
// Otherwise, we assume the sequence was already flattened (e.g. by Specializer).
|
||||||
if let Some(Value::List(l)) = values.get(*offset) {
|
if let Some(val) = values.get(*offset) {
|
||||||
*offset += 1;
|
match val {
|
||||||
let mut sub_offset = 0;
|
Value::List(l) => {
|
||||||
for el in elements {
|
*offset += 1;
|
||||||
self.unpack(el, l, &mut sub_offset)?;
|
let mut sub_offset = 0;
|
||||||
}
|
for el in elements {
|
||||||
} else {
|
self.unpack(el, l, &mut sub_offset)?;
|
||||||
for el in elements {
|
}
|
||||||
self.unpack(el, values, offset)?;
|
return Ok(());
|
||||||
|
}
|
||||||
|
Value::Record(r) => {
|
||||||
|
*offset += 1;
|
||||||
|
let mut sub_offset = 0;
|
||||||
|
// Treat record values as a tuple sequence in definition order
|
||||||
|
let record_values: Vec<_> = r.iter().map(|(_, v)| v.clone()).collect();
|
||||||
|
for el in elements {
|
||||||
|
self.unpack(el, &record_values, &mut sub_offset)?;
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for el in elements {
|
||||||
|
self.unpack(el, values, offset)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err("Invalid node in parameter pattern".to_string()),
|
_ => Err("Invalid node in parameter pattern".to_string()),
|
||||||
|
|||||||
Reference in New Issue
Block a user