Refactor: Implement Record Layouts and Optimized Field Access

Introduces a new `RecordLayout` system for efficient and type-safe
record handling.

Key changes:
- `RecordLayout` struct with `O(1)` field lookup using FMap
  optimization.
- Field accessors (`.name`) are now first-class callable values.
- Parser recognizes dot-prefixed identifiers as field accessors.
- Binder and TypeChecker handle field accessors.
- Optimizer transforms field accessor calls into specialized `GetField`
  nodes.
- VM executes `GetField` efficiently by directly accessing record
  values.
- Adds a new `records.myc` example showcasing record features.
- Improves comparison logic for records to use pointer equality for
  layout.
This commit is contained in:
Michael Schimmel
2026-02-26 21:38:20 +01:00
parent 2ad2eb5d43
commit bf74795e01
20 changed files with 548 additions and 63 deletions
+62
View File
@@ -294,6 +294,22 @@ impl VM {
Ok(val)
}
BoundKind::Get { addr, .. } => self.get_value(*addr),
BoundKind::FieldAccessor(k) => Ok(Value::FieldAccessor(*k)),
BoundKind::GetField { rec, field } => {
let rec_val = self.eval_internal(obs, rec)?;
if let Value::Record(layout, values) = rec_val {
if let Some(idx) = layout.index_of(*field) {
Ok(values[idx].clone())
} else {
Err(format!("Record does not have field :{}", field.name()))
}
} else {
Err(format!("Attempt to access field on non-record: {}", rec_val))
}
}
BoundKind::Set { addr, value } => {
let val = self.eval_internal(obs, value)?;
self.set_value(*addr, val.clone())?;
@@ -368,6 +384,29 @@ impl VM {
return Ok(Value::TailCallRequest(Box::new((obj, arg_vals))));
}
Value::Function(f) => return Ok((f.func)(arg_vals)),
Value::FieldAccessor(k) => {
if arg_vals.len() != 1 {
return Err(format!(
"Field accessor .{} expects exactly 1 argument, got {}",
k.name(),
arg_vals.len()
));
}
let rec = &arg_vals[0];
if let Value::Record(layout, values) = rec {
if let Some(idx) = layout.index_of(k) {
return Ok(values[idx].clone());
} else {
return Err(format!("Record does not have field :{}", k.name()));
}
} else {
return Err(format!(
"Field accessor .{} expects a record, got {}",
k.name(),
rec
));
}
}
_ => {
return Err(format!(
"Tail call target is not a function: {}",
@@ -380,6 +419,29 @@ impl VM {
loop {
match func_val {
Value::Function(f) => break Ok((f.func)(arg_vals)),
Value::FieldAccessor(k) => {
if arg_vals.len() != 1 {
break Err(format!(
"Field accessor .{} expects exactly 1 argument, got {}",
k.name(),
arg_vals.len()
));
}
let rec = &arg_vals[0];
if let Value::Record(layout, values) = rec {
if let Some(idx) = layout.index_of(k) {
break Ok(values[idx].clone());
} else {
break Err(format!("Record does not have field :{}", k.name()));
}
} else {
break Err(format!(
"Field accessor .{} expects a record, got {}",
k.name(),
rec
));
}
}
Value::Object(obj) => {
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
let old_stack_top = self.stack.len();