diff --git a/src/ast/compiler/binder.rs b/src/ast/compiler/binder.rs index 42d79e5..b62badd 100644 --- a/src/ast/compiler/binder.rs +++ b/src/ast/compiler/binder.rs @@ -253,12 +253,12 @@ impl Binder { Ok(self.make_node(node.identity.clone(), BoundKind::Tuple { elements: bound_elems })) }, - UntypedKind::Map { entries } => { - let mut bound_entries = Vec::new(); - for (k, v) in entries { - bound_entries.push((self.bind(k)?, self.bind(v)?)); + UntypedKind::Record { fields } => { + let mut bound_fields = Vec::new(); + for (k, v) in fields { + bound_fields.push((self.bind(k)?, self.bind(v)?)); } - Ok(self.make_node(node.identity.clone(), BoundKind::Map { entries: bound_entries })) + Ok(self.make_node(node.identity.clone(), BoundKind::Record { fields: bound_fields })) }, UntypedKind::Expansion { call, expanded } => { diff --git a/src/ast/compiler/bound_nodes.rs b/src/ast/compiler/bound_nodes.rs index 7da9ba8..f72a473 100644 --- a/src/ast/compiler/bound_nodes.rs +++ b/src/ast/compiler/bound_nodes.rs @@ -98,8 +98,8 @@ pub enum BoundKind { elements: Vec>, }, - Map { - entries: Vec>, + Record { + fields: Vec>, }, /// An expanded macro call, preserving the original call for debugging and UI. @@ -114,8 +114,8 @@ pub enum BoundKind { Extension(Box>), } -/// A single entry in a Map literal (Key-Value pair) -pub type MapEntry = (BoundNode, BoundNode); +/// A single field in a Record literal (Key-Value pair) +pub type RecordField = (BoundNode, BoundNode); impl BoundKind { pub fn display_name(&self) -> String { @@ -139,7 +139,7 @@ impl BoundKind { BoundKind::TailCall { .. } => "T-CALL".to_string(), BoundKind::Block { .. } => "BLOCK".to_string(), BoundKind::Tuple { elements } => format!("TUPLE({})", elements.len()), - BoundKind::Map { entries } => format!("MAP({})", entries.len()), + BoundKind::Record { fields } => format!("RECORD({})", fields.len()), BoundKind::Expansion { .. } => "EXPANSION".to_string(), BoundKind::Extension(ext) => ext.display_name(), } diff --git a/src/ast/compiler/dumper.rs b/src/ast/compiler/dumper.rs index 65165eb..8215bb8 100644 --- a/src/ast/compiler/dumper.rs +++ b/src/ast/compiler/dumper.rs @@ -185,10 +185,10 @@ impl Dumper { self.indent -= 1; } - BoundKind::Map { entries } => { - self.log("Map", node); + BoundKind::Record { fields } => { + self.log("Record", node); self.indent += 1; - for (k, v) in entries { + for (k, v) in fields { self.visit(k); self.visit(v); } diff --git a/src/ast/compiler/lambda_collector.rs b/src/ast/compiler/lambda_collector.rs index ee3b80d..4fff011 100644 --- a/src/ast/compiler/lambda_collector.rs +++ b/src/ast/compiler/lambda_collector.rs @@ -69,8 +69,8 @@ impl<'a> LambdaCollector<'a> { } } - BoundKind::Map { entries } => { - for (k, v) in entries { + BoundKind::Record { fields } => { + for (k, v) in fields { self.visit(k); self.visit(v); } diff --git a/src/ast/compiler/macros.rs b/src/ast/compiler/macros.rs index 3e48b21..96904bf 100644 --- a/src/ast/compiler/macros.rs +++ b/src/ast/compiler/macros.rs @@ -193,14 +193,14 @@ impl MacroExpander { }) } - UntypedKind::Map { entries } => { - let mut expanded_entries = Vec::new(); - for (k, v) in entries { - expanded_entries.push((self.expand_recursive(k)?, self.expand_recursive(v)?)); + UntypedKind::Record { fields } => { + let mut expanded_fields = Vec::new(); + for (k, v) in fields { + expanded_fields.push((self.expand_recursive(k)?, self.expand_recursive(v)?)); } Ok(Node { identity: node.identity, - kind: UntypedKind::Map { entries: expanded_entries }, + kind: UntypedKind::Record { fields: expanded_fields }, ty: (), }) } @@ -368,14 +368,14 @@ impl MacroExpander { }) } - UntypedKind::Map { entries } => { - let mut new_entries = Vec::new(); - for (k, v) in entries { - new_entries.push((self.expand_template(k, state)?, self.expand_template(v, state)?)); + UntypedKind::Record { fields } => { + let mut new_fields = Vec::new(); + for (k, v) in fields { + new_fields.push((self.expand_template(k, state)?, self.expand_template(v, state)?)); } Ok(Node { identity: node.identity, - kind: UntypedKind::Map { entries: new_entries }, + kind: UntypedKind::Record { fields: new_fields }, ty: (), }) } diff --git a/src/ast/compiler/specializer.rs b/src/ast/compiler/specializer.rs index 3da884d..643fefc 100644 --- a/src/ast/compiler/specializer.rs +++ b/src/ast/compiler/specializer.rs @@ -90,10 +90,11 @@ impl Specializer { let elements = elements.into_iter().map(|e| self.visit_node(e)).collect(); (BoundKind::Tuple { elements }, node.ty) }, - BoundKind::Map { entries } => { - let entries = entries.into_iter().map(|(k, v)| (self.visit_node(k), self.visit_node(v))).collect(); - (BoundKind::Map { entries }, node.ty) - }, + BoundKind::Record { fields } => { + let fields = fields.into_iter().map(|(k, v)| (self.visit_node(k), self.visit_node(v))).collect(); + (BoundKind::Record { fields }, node.ty) + } + , BoundKind::Expansion { original_call, bound_expanded } => { let bound_expanded = Box::new(self.visit_node(*bound_expanded)); (BoundKind::Expansion { original_call, bound_expanded }, node.ty) diff --git a/src/ast/compiler/tco.rs b/src/ast/compiler/tco.rs index 066ef4f..103577f 100644 --- a/src/ast/compiler/tco.rs +++ b/src/ast/compiler/tco.rs @@ -108,12 +108,12 @@ impl TCO { ..node } }, - BoundKind::Map { entries } => { - let new_entries = entries.into_iter().map(|(k, v)| { + BoundKind::Record { fields } => { + let new_fields = fields.into_iter().map(|(k, v)| { (Self::transform(k, false), Self::transform(v, false)) }).collect(); Node { - kind: BoundKind::Map { entries: new_entries }, + kind: BoundKind::Record { fields: new_fields }, ..node } }, diff --git a/src/ast/compiler/type_checker.rs b/src/ast/compiler/type_checker.rs index 4add772..b6df9c3 100644 --- a/src/ast/compiler/type_checker.rs +++ b/src/ast/compiler/type_checker.rs @@ -307,20 +307,20 @@ impl TypeChecker { (BoundKind::Tuple { elements: typed_elements }, ty) }, - BoundKind::Map { entries } => { - let mut typed_entries = Vec::new(); - let mut fields = Vec::new(); - for (k, v) in entries { + BoundKind::Record { fields } => { + let mut typed_fields = Vec::with_capacity(fields.len()); + let mut fields_ty = Vec::with_capacity(fields.len()); + for (k, v) in fields { let kt = self.check_node(k, ctx)?; let vt = self.check_node(v, ctx)?; if let BoundKind::Constant(crate::ast::types::Value::Keyword(kw)) = &kt.kind { - fields.push((*kw, vt.ty.clone())); + fields_ty.push((*kw, vt.ty.clone())); } - typed_entries.push((kt, vt)); + typed_fields.push((kt, vt)); } - (BoundKind::Map { entries: typed_entries }, StaticType::Record(Rc::new(fields))) + (BoundKind::Record { fields: typed_fields }, StaticType::Record(Rc::new(fields_ty))) }, BoundKind::Expansion { original_call, bound_expanded } => { diff --git a/src/ast/compiler/upvalues.rs b/src/ast/compiler/upvalues.rs index ba5039e..2f7db14 100644 --- a/src/ast/compiler/upvalues.rs +++ b/src/ast/compiler/upvalues.rs @@ -103,8 +103,8 @@ impl UpvalueAnalyzer { Self::visit(el, scopes, capture_map, current_lambda.clone()); } } - UntypedKind::Map { entries } => { - for (k, v) in entries { + UntypedKind::Record { fields } => { + for (k, v) in fields { Self::visit(k, scopes, capture_map, current_lambda.clone()); Self::visit(v, scopes, capture_map, current_lambda.clone()); } diff --git a/src/ast/nodes.rs b/src/ast/nodes.rs index bd70aaf..3db5ccd 100644 --- a/src/ast/nodes.rs +++ b/src/ast/nodes.rs @@ -86,8 +86,8 @@ pub enum UntypedKind { Tuple { elements: Vec>, }, - Map { - entries: Vec<(Node, Node)>, + Record { + fields: Vec<(Node, Node)>, }, /// A macro declaration that can be expanded at compile time. MacroDecl { @@ -99,7 +99,7 @@ pub enum UntypedKind { Template(Box>), /// A placeholder inside a template to be replaced by a node or value. (Unquote) Placeholder(Box>), - /// A placeholder that flattens a sequence or merges a map into its surroundings. (Splice) + /// A placeholder that flattens a sequence or merges a record into its surroundings. (Splice) Splice(Box>), /// Represents an expanded macro call, preserving the original call for debugging. Expansion { diff --git a/src/ast/parser.rs b/src/ast/parser.rs index a0d7402..1bed25b 100644 --- a/src/ast/parser.rs +++ b/src/ast/parser.rs @@ -31,7 +31,7 @@ impl<'a> Parser<'a> { match self.peek() { TokenKind::LeftParen => self.parse_list(), TokenKind::LeftBracket => self.parse_vector_literal(), - TokenKind::LeftBrace => self.parse_map_literal(), + TokenKind::LeftBrace => self.parse_record_literal(), TokenKind::Quote => { self.advance()?; // consume ' let expr = self.parse_expression()?; @@ -292,13 +292,13 @@ impl<'a> Parser<'a> { }) } - fn parse_map_literal(&mut self) -> Result, String> { + fn parse_record_literal(&mut self) -> Result, String> { let token = self.advance()?; - let mut entries = Vec::new(); + let mut fields = Vec::new(); while *self.peek() != TokenKind::RightBrace { if *self.peek() == TokenKind::EOF { - return Err("Unexpected EOF in map literal".to_string()); + return Err("Unexpected EOF in record literal".to_string()); } let key_node = self.parse_expression()?; @@ -307,21 +307,21 @@ impl<'a> Parser<'a> { // Delphi enforces keywords. We can do minimal check here. match &key_node.kind { UntypedKind::Constant(Value::Keyword(_)) => {}, - _ => return Err("Map keys must be keywords (syntactically)".to_string()), + _ => return Err("Record keys must be keywords (syntactically)".to_string()), } if *self.peek() == TokenKind::RightBrace { - return Err("Map literal must have even number of forms".to_string()); + return Err("Record literal must have even number of forms".to_string()); } let val_node = self.parse_expression()?; - entries.push((key_node, val_node)); + fields.push((key_node, val_node)); } self.expect(TokenKind::RightBrace)?; Ok(Node { identity: Rc::new(NodeIdentity { location: token.location }), - kind: UntypedKind::Map { entries }, + kind: UntypedKind::Record { fields }, ty: (), }) } diff --git a/src/ast/rtl/type_registry.rs b/src/ast/rtl/type_registry.rs index 26ff2d9..b1ed235 100644 --- a/src/ast/rtl/type_registry.rs +++ b/src/ast/rtl/type_registry.rs @@ -201,26 +201,22 @@ mod tests { } // Check runtime behavior - if let Value::Tuple(t) = wrapped { - if let Some(keys) = &t.keys { - let idx = keys.iter().position(|k| *k == Keyword::intern("greet")).unwrap(); - let greet_fn = &t.values[idx]; - - if let Value::Function(f) = greet_fn { - let res = f(vec![]); - if let Value::Text(s) = res { - assert_eq!(&*s, "Hello Alice"); - } else { - panic!("Expected Text result"); - } + 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::Function(f) = greet_fn { + let res = f(vec![]); + if let Value::Text(s) = res { + assert_eq!(&*s, "Hello Alice"); } else { - panic!("Expected Function value for method"); + panic!("Expected Text result"); } } else { - panic!("Expected Record keys"); + panic!("Expected Function value for method"); } } else { - panic!("Expected Tuple value"); + panic!("Expected Record value"); } } @@ -237,19 +233,17 @@ mod tests { if let Value::Function(f) = factory_val { let instance = f(vec![Value::Text("Bob".into()), Value::Int(40)]); - if let Value::Tuple(t) = instance { - if let Some(keys) = &t.keys { - let idx = keys.iter().position(|k| *k == Keyword::intern("greet")).unwrap(); - let greet_fn = &t.values[idx]; - - if let Value::Function(gf) = greet_fn { - let res = gf(vec![]); - if let Value::Text(s) = res { - assert_eq!(&*s, "Hello Bob"); - } else { panic!("Wrong return type"); } - } - } else { panic!("Expected keys"); } - } else { panic!("Factory should return Tuple/Record"); } + 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::Function(gf) = greet_fn { + let res = gf(vec![]); + if let Value::Text(s) = res { + assert_eq!(&*s, "Hello Bob"); + } else { panic!("Wrong return type"); } + } + } else { panic!("Factory should return Record"); } } else { panic!("Factory is not a function"); } } } diff --git a/src/ast/types.rs b/src/ast/types.rs index 2aef55d..c898cff 100644 --- a/src/ast/types.rs +++ b/src/ast/types.rs @@ -55,12 +55,12 @@ pub trait Object: fmt::Debug { fn as_any(&self) -> &dyn Any; } -/// Internal storage for Tuples (and Records) to allow sharing schema (keys) between instances. +/// Internal storage for Records to allow sharing schema (keys) between instances. #[derive(Debug, Clone)] -pub struct TupleData { +pub struct RecordData { + /// Names for slots. + pub keys: Rc>, pub values: Vec, - /// Optional names for slots. If present, this is semantically a Record. - pub keys: Option>>, } /// Core data value in Myc Script (similar to TDataValue) @@ -73,7 +73,8 @@ pub enum Value { DateTime(i64), Text(Rc), Keyword(Keyword), - Tuple(Rc), // Replaces List and Record + Tuple(Rc>), + Record(Rc), Function(Rc) -> Value>), Object(Rc), // For compiled Closures and other opaque types Cell(Rc>), // Boxed value for captures @@ -224,12 +225,12 @@ impl Value { } } - pub fn make_list(values: Vec) -> Self { - Value::Tuple(Rc::new(TupleData { values, keys: None })) + pub fn make_tuple(values: Vec) -> Self { + Value::Tuple(Rc::new(values)) } pub fn make_record(keys: Vec, values: Vec) -> Self { - Value::Tuple(Rc::new(TupleData { values, keys: Some(Rc::new(keys)) })) + Value::Record(Rc::new(RecordData { keys: Rc::new(keys), values })) } pub fn static_type(&self) -> StaticType { @@ -241,44 +242,40 @@ impl Value { Value::DateTime(_) => StaticType::DateTime, Value::Text(_) => StaticType::Text, Value::Keyword(_) => StaticType::Keyword, - Value::Tuple(t) => { - if let Some(keys) = &t.keys { - // It's a Record - let mut fields = Vec::with_capacity(t.values.len()); - for (i, v) in t.values.iter().enumerate() { - fields.push((keys[i], v.static_type())); - } - StaticType::Record(Rc::new(fields)) - } else { - // It's a List/Tuple - let l = &t.values; - if l.is_empty() { - return StaticType::Vector(Box::new(StaticType::Any), 0); - } - - let element_types: Vec<_> = l.iter().map(|v| v.static_type()).collect(); - - // Check for Homogeneity (Vector) - let first_ty = &element_types[0]; - let all_same = element_types.iter().all(|t| t == first_ty); - - if all_same { - match first_ty { - StaticType::Vector(inner, len) => { - // Possible Matrix - StaticType::Matrix(inner.clone(), vec![l.len(), *len]) - }, - StaticType::Matrix(inner, shape) => { - let mut new_shape = vec![l.len()]; - new_shape.extend(shape); - StaticType::Matrix(inner.clone(), new_shape) - }, - _ => StaticType::Vector(Box::new(first_ty.clone()), l.len()) - } - } else { - StaticType::Tuple(element_types) - } + Value::Tuple(values) => { + if values.is_empty() { + return StaticType::Vector(Box::new(StaticType::Any), 0); } + + let element_types: Vec<_> = values.iter().map(|v| v.static_type()).collect(); + + // Check for Homogeneity (Vector) + let first_ty = &element_types[0]; + let all_same = element_types.iter().all(|t| t == first_ty); + + if all_same { + match first_ty { + StaticType::Vector(inner, len) => { + // Possible Matrix + StaticType::Matrix(inner.clone(), vec![values.len(), *len]) + }, + StaticType::Matrix(inner, shape) => { + let mut new_shape = vec![values.len()]; + new_shape.extend(shape); + StaticType::Matrix(inner.clone(), new_shape) + }, + _ => StaticType::Vector(Box::new(first_ty.clone()), values.len()) + } + } else { + StaticType::Tuple(element_types) + } + }, + Value::Record(r) => { + let mut fields = Vec::with_capacity(r.values.len()); + for (i, v) in r.values.iter().enumerate() { + fields.push((r.keys[i], v.static_type())); + } + StaticType::Record(Rc::new(fields)) }, Value::Function(_) => StaticType::Any, // Dynamic function Value::Object(o) => StaticType::Object(o.type_name()), @@ -303,22 +300,21 @@ impl fmt::Display for Value { }, Value::Text(t) => write!(f, "\"{}\"", t), Value::Keyword(k) => write!(f, ":{}", k.name()), - Value::Tuple(t) => { - if let Some(keys) = &t.keys { - write!(f, "{{")?; - for i in 0..t.values.len() { - if i > 0 { write!(f, ", ")?; } - write!(f, ":{} {}", keys[i].name(), t.values[i])?; - } - write!(f, "}}") - } else { - write!(f, "[")?; - for (i, val) in t.values.iter().enumerate() { - if i > 0 { write!(f, " ")?; } - write!(f, "{}", val)?; - } - write!(f, "]") + Value::Tuple(values) => { + write!(f, "[")?; + for (i, val) in values.iter().enumerate() { + if i > 0 { write!(f, " ")?; } + write!(f, "{}", val)?; } + write!(f, "]") + }, + Value::Record(r) => { + write!(f, "{{")?; + for i in 0..r.values.len() { + if i > 0 { write!(f, ", ")?; } + write!(f, ":{} {}", r.keys[i].name(), r.values[i])?; + } + write!(f, "}}") }, Value::Function(_) => write!(f, ""), Value::Object(o) => write!(f, "<{}>", o.type_name()), diff --git a/src/ast/vm.rs b/src/ast/vm.rs index e99e127..378d08e 100644 --- a/src/ast/vm.rs +++ b/src/ast/vm.rs @@ -313,13 +313,13 @@ macro_rules! dispatch_eval { for e in elements { vals.push($self.$eval_method($($observer,)? e)?); } - Ok(Value::make_list(vals)) + Ok(Value::make_tuple(vals)) }, - BoundKind::Map { entries } => { - let mut keys = Vec::with_capacity(entries.len()); - let mut values = Vec::with_capacity(entries.len()); - for (k, v) in entries { + BoundKind::Record { fields } => { + let mut keys = Vec::with_capacity(fields.len()); + let mut values = Vec::with_capacity(fields.len()); + for (k, v) in fields { let key = $self.$eval_method($($observer,)? k)?; let val = $self.$eval_method($($observer,)? v)?; @@ -327,7 +327,7 @@ macro_rules! dispatch_eval { keys.push(kw); values.push(val); } else { - return Err(format!("Map key must be keyword, got {}", key)); + return Err(format!("Record key must be keyword, got {}", key)); } } Ok(Value::make_record(keys, values)) @@ -601,12 +601,18 @@ impl VM { } fn flatten_value(val: Value, into: &mut Vec) { - if let Value::Tuple(t) = val { - for item in t.values.iter() { - Self::flatten_value(item.clone(), into); + match val { + Value::Tuple(values) => { + for item in values.iter() { + Self::flatten_value(item.clone(), into); + } } - } else { - into.push(val); + Value::Record(r) => { + for item in r.values.iter() { + Self::flatten_value(item.clone(), into); + } + } + _ => into.push(val), } } @@ -681,13 +687,24 @@ impl VM { BoundKind::Tuple { elements } => { // 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(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)?; + match values.get(*offset) { + Some(Value::Tuple(t)) => { + *offset += 1; + let mut sub_offset = 0; + for el in elements { + self.unpack(el, t, &mut sub_offset)?; + } + return Ok(()); } - return Ok(()); + Some(Value::Record(r)) => { + *offset += 1; + let mut sub_offset = 0; + for el in elements { + self.unpack(el, &r.values, &mut sub_offset)?; + } + return Ok(()); + } + _ => {} } for el in elements {