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
+3
View File
@@ -366,6 +366,9 @@ fn register_comparison(env: &Environment) {
(Value::Bool(a), Value::Bool(b)) => Value::Bool(a == b),
(Value::Keyword(a), Value::Keyword(b)) => Value::Bool(a == b),
(Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a == b),
(Value::Record(la, va), Value::Record(lb, vb)) => {
Value::Bool(std::sync::Arc::ptr_eq(la, lb) && va == vb)
}
(Value::Void, Value::Void) => Value::Bool(true),
_ => Value::Bool(false),
}
+9 -18
View File
@@ -1,7 +1,6 @@
use crate::ast::types::{Keyword, Purity, Signature, StaticType, Value};
use std::any::TypeId;
use std::collections::HashMap;
use std::rc::Rc;
/// Represents a Rust type that can be exposed to the script environment.
pub trait Scriptable: 'static + Sized {
@@ -149,7 +148,7 @@ impl TypeBuilder {
}
pub fn build(self) -> StaticType {
StaticType::Record(Rc::new(self.fields))
StaticType::Record(crate::ast::types::RecordLayout::get_or_create(self.fields))
}
}
@@ -202,20 +201,16 @@ mod tests {
// Check static type
let st = TypeRegistry::resolve_type::<Person>(&registry);
if let StaticType::Record(fields) = st {
assert!(fields.iter().any(|(k, _)| *k == Keyword::intern("greet")));
if let StaticType::Record(layout) = st {
assert!(layout.fields.iter().any(|(k, _)| *k == Keyword::intern("greet")));
} else {
panic!("Expected Record type");
}
// Check runtime behavior
if let Value::Record(r) = wrapped {
let idx = r
.keys
.iter()
.position(|k| *k == Keyword::intern("greet"))
.unwrap();
let greet_fn = &r.values[idx];
if let Value::Record(layout, values) = wrapped {
let idx = layout.index_of(Keyword::intern("greet")).unwrap();
let greet_fn = &values[idx];
if let Value::Function(f) = greet_fn {
let res = (f.func)(vec![]);
@@ -251,13 +246,9 @@ mod tests {
if let Value::Function(f) = factory_val {
let instance = (f.func)(vec![Value::Text("Bob".into()), Value::Int(40)]);
if let Value::Record(r) = instance {
let idx = r
.keys
.iter()
.position(|k| *k == Keyword::intern("greet"))
.unwrap();
let greet_fn = &r.values[idx];
if let Value::Record(layout, values) = instance {
let idx = layout.index_of(Keyword::intern("greet")).unwrap();
let greet_fn = &values[idx];
if let Value::Function(gf) = greet_fn {
let res = (gf.func)(vec![]);