Refactor Record and List to use TupleData

The `Value::List` and `Value::Record` variants have been consolidated
into a single `Value::Tuple` variant. This new variant uses a
`TupleData` struct to store values and an optional `Rc<Vec<Keyword>>`
for keys, allowing it to represent both ordered lists/tuples and
key-value records.

This change simplifies the internal representation and improves
performance by allowing schema sharing for records. It also includes
updates to the compiler, runtime, and tests to reflect the new
structure.
This commit is contained in:
Michael Schimmel
2026-02-20 16:13:38 +01:00
parent ca2c85a8a4
commit 87259584ee
6 changed files with 148 additions and 96 deletions
+15 -28
View File
@@ -313,22 +313,24 @@ macro_rules! dispatch_eval {
for e in elements {
vals.push($self.$eval_method($($observer,)? e)?);
}
Ok(Value::List(Rc::new(vals)))
Ok(Value::make_list(vals))
},
BoundKind::Map { entries } => {
let mut map = Vec::with_capacity(entries.len());
let mut keys = Vec::with_capacity(entries.len());
let mut values = Vec::with_capacity(entries.len());
for (k, v) in entries {
let key = $self.$eval_method($($observer,)? k)?;
let val = $self.$eval_method($($observer,)? v)?;
if let Value::Keyword(kw) = key {
map.push((kw, val));
keys.push(kw);
values.push(val);
} else {
return Err(format!("Map key must be keyword, got {}", key));
}
}
Ok(Value::Record(Rc::new(map)))
Ok(Value::make_record(keys, values))
},
BoundKind::Expansion { original_call: _, bound_expanded } => {
@@ -599,8 +601,8 @@ impl VM {
}
fn flatten_value(val: Value, into: &mut Vec<Value>) {
if let Value::List(l) = val {
for item in l.iter() {
if let Value::Tuple(t) = val {
for item in t.values.iter() {
Self::flatten_value(item.clone(), into);
}
} else {
@@ -677,30 +679,15 @@ impl VM {
Ok(())
}
BoundKind::Tuple { elements } => {
// If the current value at offset is a List or Record, we dive into it.
// If the current value at offset is a Tuple (List or Record), we dive into it.
// Otherwise, we assume the sequence was already flattened (e.g. by Specializer).
if let Some(val) = values.get(*offset) {
match val {
Value::List(l) => {
*offset += 1;
let mut sub_offset = 0;
for el in elements {
self.unpack(el, l, &mut sub_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(());
}
_ => {}
if let Some(Value::Tuple(t)) = values.get(*offset) {
*offset += 1;
let mut sub_offset = 0;
for el in elements {
self.unpack(el, &t.values, &mut sub_offset)?;
}
return Ok(());
}
for el in elements {