Refactor: Rename map to record
This commit renames `Map` to `Record` and updates all related AST nodes, binders, type checkers, and runtime values to reflect this change. This is a semantic change to better align with common programming language terminology.
This commit is contained in:
@@ -253,12 +253,12 @@ impl Binder {
|
|||||||
Ok(self.make_node(node.identity.clone(), BoundKind::Tuple { elements: bound_elems }))
|
Ok(self.make_node(node.identity.clone(), BoundKind::Tuple { elements: bound_elems }))
|
||||||
},
|
},
|
||||||
|
|
||||||
UntypedKind::Map { entries } => {
|
UntypedKind::Record { fields } => {
|
||||||
let mut bound_entries = Vec::new();
|
let mut bound_fields = Vec::new();
|
||||||
for (k, v) in entries {
|
for (k, v) in fields {
|
||||||
bound_entries.push((self.bind(k)?, self.bind(v)?));
|
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 } => {
|
UntypedKind::Expansion { call, expanded } => {
|
||||||
|
|||||||
@@ -98,8 +98,8 @@ pub enum BoundKind<T = ()> {
|
|||||||
elements: Vec<BoundNode<T>>,
|
elements: Vec<BoundNode<T>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
Map {
|
Record {
|
||||||
entries: Vec<MapEntry<T>>,
|
fields: Vec<RecordField<T>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// An expanded macro call, preserving the original call for debugging and UI.
|
/// An expanded macro call, preserving the original call for debugging and UI.
|
||||||
@@ -114,8 +114,8 @@ pub enum BoundKind<T = ()> {
|
|||||||
Extension(Box<dyn BoundExtension<T>>),
|
Extension(Box<dyn BoundExtension<T>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single entry in a Map literal (Key-Value pair)
|
/// A single field in a Record literal (Key-Value pair)
|
||||||
pub type MapEntry<T> = (BoundNode<T>, BoundNode<T>);
|
pub type RecordField<T> = (BoundNode<T>, BoundNode<T>);
|
||||||
|
|
||||||
impl<T> BoundKind<T> {
|
impl<T> BoundKind<T> {
|
||||||
pub fn display_name(&self) -> String {
|
pub fn display_name(&self) -> String {
|
||||||
@@ -139,7 +139,7 @@ impl<T> BoundKind<T> {
|
|||||||
BoundKind::TailCall { .. } => "T-CALL".to_string(),
|
BoundKind::TailCall { .. } => "T-CALL".to_string(),
|
||||||
BoundKind::Block { .. } => "BLOCK".to_string(),
|
BoundKind::Block { .. } => "BLOCK".to_string(),
|
||||||
BoundKind::Tuple { elements } => format!("TUPLE({})", elements.len()),
|
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::Expansion { .. } => "EXPANSION".to_string(),
|
||||||
BoundKind::Extension(ext) => ext.display_name(),
|
BoundKind::Extension(ext) => ext.display_name(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,10 +185,10 @@ impl Dumper {
|
|||||||
self.indent -= 1;
|
self.indent -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundKind::Map { entries } => {
|
BoundKind::Record { fields } => {
|
||||||
self.log("Map", node);
|
self.log("Record", node);
|
||||||
self.indent += 1;
|
self.indent += 1;
|
||||||
for (k, v) in entries {
|
for (k, v) in fields {
|
||||||
self.visit(k);
|
self.visit(k);
|
||||||
self.visit(v);
|
self.visit(v);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ impl<'a> LambdaCollector<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundKind::Map { entries } => {
|
BoundKind::Record { fields } => {
|
||||||
for (k, v) in entries {
|
for (k, v) in fields {
|
||||||
self.visit(k);
|
self.visit(k);
|
||||||
self.visit(v);
|
self.visit(v);
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -193,14 +193,14 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
UntypedKind::Map { entries } => {
|
UntypedKind::Record { fields } => {
|
||||||
let mut expanded_entries = Vec::new();
|
let mut expanded_fields = Vec::new();
|
||||||
for (k, v) in entries {
|
for (k, v) in fields {
|
||||||
expanded_entries.push((self.expand_recursive(k)?, self.expand_recursive(v)?));
|
expanded_fields.push((self.expand_recursive(k)?, self.expand_recursive(v)?));
|
||||||
}
|
}
|
||||||
Ok(Node {
|
Ok(Node {
|
||||||
identity: node.identity,
|
identity: node.identity,
|
||||||
kind: UntypedKind::Map { entries: expanded_entries },
|
kind: UntypedKind::Record { fields: expanded_fields },
|
||||||
ty: (),
|
ty: (),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -368,14 +368,14 @@ impl<E: MacroEvaluator> MacroExpander<E> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
UntypedKind::Map { entries } => {
|
UntypedKind::Record { fields } => {
|
||||||
let mut new_entries = Vec::new();
|
let mut new_fields = Vec::new();
|
||||||
for (k, v) in entries {
|
for (k, v) in fields {
|
||||||
new_entries.push((self.expand_template(k, state)?, self.expand_template(v, state)?));
|
new_fields.push((self.expand_template(k, state)?, self.expand_template(v, state)?));
|
||||||
}
|
}
|
||||||
Ok(Node {
|
Ok(Node {
|
||||||
identity: node.identity,
|
identity: node.identity,
|
||||||
kind: UntypedKind::Map { entries: new_entries },
|
kind: UntypedKind::Record { fields: new_fields },
|
||||||
ty: (),
|
ty: (),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,10 +90,11 @@ impl Specializer {
|
|||||||
let elements = elements.into_iter().map(|e| self.visit_node(e)).collect();
|
let elements = elements.into_iter().map(|e| self.visit_node(e)).collect();
|
||||||
(BoundKind::Tuple { elements }, node.ty)
|
(BoundKind::Tuple { elements }, node.ty)
|
||||||
},
|
},
|
||||||
BoundKind::Map { entries } => {
|
BoundKind::Record { fields } => {
|
||||||
let entries = entries.into_iter().map(|(k, v)| (self.visit_node(k), self.visit_node(v))).collect();
|
let fields = fields.into_iter().map(|(k, v)| (self.visit_node(k), self.visit_node(v))).collect();
|
||||||
(BoundKind::Map { entries }, node.ty)
|
(BoundKind::Record { fields }, node.ty)
|
||||||
},
|
}
|
||||||
|
,
|
||||||
BoundKind::Expansion { original_call, bound_expanded } => {
|
BoundKind::Expansion { original_call, bound_expanded } => {
|
||||||
let bound_expanded = Box::new(self.visit_node(*bound_expanded));
|
let bound_expanded = Box::new(self.visit_node(*bound_expanded));
|
||||||
(BoundKind::Expansion { original_call, bound_expanded }, node.ty)
|
(BoundKind::Expansion { original_call, bound_expanded }, node.ty)
|
||||||
|
|||||||
@@ -108,12 +108,12 @@ impl TCO {
|
|||||||
..node
|
..node
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BoundKind::Map { entries } => {
|
BoundKind::Record { fields } => {
|
||||||
let new_entries = entries.into_iter().map(|(k, v)| {
|
let new_fields = fields.into_iter().map(|(k, v)| {
|
||||||
(Self::transform(k, false), Self::transform(v, false))
|
(Self::transform(k, false), Self::transform(v, false))
|
||||||
}).collect();
|
}).collect();
|
||||||
Node {
|
Node {
|
||||||
kind: BoundKind::Map { entries: new_entries },
|
kind: BoundKind::Record { fields: new_fields },
|
||||||
..node
|
..node
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -307,20 +307,20 @@ impl TypeChecker {
|
|||||||
(BoundKind::Tuple { elements: typed_elements }, ty)
|
(BoundKind::Tuple { elements: typed_elements }, ty)
|
||||||
},
|
},
|
||||||
|
|
||||||
BoundKind::Map { entries } => {
|
BoundKind::Record { fields } => {
|
||||||
let mut typed_entries = Vec::new();
|
let mut typed_fields = Vec::with_capacity(fields.len());
|
||||||
let mut fields = Vec::new();
|
let mut fields_ty = Vec::with_capacity(fields.len());
|
||||||
for (k, v) in entries {
|
for (k, v) in fields {
|
||||||
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.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 } => {
|
BoundKind::Expansion { original_call, bound_expanded } => {
|
||||||
|
|||||||
@@ -103,8 +103,8 @@ impl UpvalueAnalyzer {
|
|||||||
Self::visit(el, scopes, capture_map, current_lambda.clone());
|
Self::visit(el, scopes, capture_map, current_lambda.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UntypedKind::Map { entries } => {
|
UntypedKind::Record { fields } => {
|
||||||
for (k, v) in entries {
|
for (k, v) in fields {
|
||||||
Self::visit(k, scopes, capture_map, current_lambda.clone());
|
Self::visit(k, scopes, capture_map, current_lambda.clone());
|
||||||
Self::visit(v, scopes, capture_map, current_lambda.clone());
|
Self::visit(v, scopes, capture_map, current_lambda.clone());
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -86,8 +86,8 @@ pub enum UntypedKind {
|
|||||||
Tuple {
|
Tuple {
|
||||||
elements: Vec<Node<UntypedKind>>,
|
elements: Vec<Node<UntypedKind>>,
|
||||||
},
|
},
|
||||||
Map {
|
Record {
|
||||||
entries: Vec<(Node<UntypedKind>, Node<UntypedKind>)>,
|
fields: Vec<(Node<UntypedKind>, Node<UntypedKind>)>,
|
||||||
},
|
},
|
||||||
/// A macro declaration that can be expanded at compile time.
|
/// A macro declaration that can be expanded at compile time.
|
||||||
MacroDecl {
|
MacroDecl {
|
||||||
@@ -99,7 +99,7 @@ pub enum UntypedKind {
|
|||||||
Template(Box<Node<UntypedKind>>),
|
Template(Box<Node<UntypedKind>>),
|
||||||
/// A placeholder inside a template to be replaced by a node or value. (Unquote)
|
/// A placeholder inside a template to be replaced by a node or value. (Unquote)
|
||||||
Placeholder(Box<Node<UntypedKind>>),
|
Placeholder(Box<Node<UntypedKind>>),
|
||||||
/// 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<Node<UntypedKind>>),
|
Splice(Box<Node<UntypedKind>>),
|
||||||
/// Represents an expanded macro call, preserving the original call for debugging.
|
/// Represents an expanded macro call, preserving the original call for debugging.
|
||||||
Expansion {
|
Expansion {
|
||||||
|
|||||||
+8
-8
@@ -31,7 +31,7 @@ impl<'a> Parser<'a> {
|
|||||||
match self.peek() {
|
match self.peek() {
|
||||||
TokenKind::LeftParen => self.parse_list(),
|
TokenKind::LeftParen => self.parse_list(),
|
||||||
TokenKind::LeftBracket => self.parse_vector_literal(),
|
TokenKind::LeftBracket => self.parse_vector_literal(),
|
||||||
TokenKind::LeftBrace => self.parse_map_literal(),
|
TokenKind::LeftBrace => self.parse_record_literal(),
|
||||||
TokenKind::Quote => {
|
TokenKind::Quote => {
|
||||||
self.advance()?; // consume '
|
self.advance()?; // consume '
|
||||||
let expr = self.parse_expression()?;
|
let expr = self.parse_expression()?;
|
||||||
@@ -292,13 +292,13 @@ impl<'a> Parser<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_map_literal(&mut self) -> Result<Node<UntypedKind>, String> {
|
fn parse_record_literal(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||||
let token = self.advance()?;
|
let token = self.advance()?;
|
||||||
let mut entries = Vec::new();
|
let mut fields = Vec::new();
|
||||||
|
|
||||||
while *self.peek() != TokenKind::RightBrace {
|
while *self.peek() != TokenKind::RightBrace {
|
||||||
if *self.peek() == TokenKind::EOF {
|
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()?;
|
let key_node = self.parse_expression()?;
|
||||||
@@ -307,21 +307,21 @@ impl<'a> Parser<'a> {
|
|||||||
// Delphi enforces keywords. We can do minimal check here.
|
// Delphi enforces keywords. We can do minimal check here.
|
||||||
match &key_node.kind {
|
match &key_node.kind {
|
||||||
UntypedKind::Constant(Value::Keyword(_)) => {},
|
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 {
|
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()?;
|
let val_node = self.parse_expression()?;
|
||||||
|
|
||||||
entries.push((key_node, val_node));
|
fields.push((key_node, val_node));
|
||||||
}
|
}
|
||||||
self.expect(TokenKind::RightBrace)?;
|
self.expect(TokenKind::RightBrace)?;
|
||||||
|
|
||||||
Ok(Node {
|
Ok(Node {
|
||||||
identity: Rc::new(NodeIdentity { location: token.location }),
|
identity: Rc::new(NodeIdentity { location: token.location }),
|
||||||
kind: UntypedKind::Map { entries },
|
kind: UntypedKind::Record { fields },
|
||||||
ty: (),
|
ty: (),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,26 +201,22 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check runtime behavior
|
// Check runtime behavior
|
||||||
if let Value::Tuple(t) = wrapped {
|
if let Value::Record(r) = wrapped {
|
||||||
if let Some(keys) = &t.keys {
|
let idx = r.keys.iter().position(|k| *k == Keyword::intern("greet")).unwrap();
|
||||||
let idx = keys.iter().position(|k| *k == Keyword::intern("greet")).unwrap();
|
let greet_fn = &r.values[idx];
|
||||||
let greet_fn = &t.values[idx];
|
|
||||||
|
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 {
|
assert_eq!(&*s, "Hello Alice");
|
||||||
assert_eq!(&*s, "Hello Alice");
|
|
||||||
} else {
|
|
||||||
panic!("Expected Text result");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected Function value for method");
|
panic!("Expected Text result");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected Record keys");
|
panic!("Expected Function value for method");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected Tuple value");
|
panic!("Expected Record value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,19 +233,17 @@ 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::Tuple(t) = instance {
|
if let Value::Record(r) = instance {
|
||||||
if let Some(keys) = &t.keys {
|
let idx = r.keys.iter().position(|k| *k == Keyword::intern("greet")).unwrap();
|
||||||
let idx = keys.iter().position(|k| *k == Keyword::intern("greet")).unwrap();
|
let greet_fn = &r.values[idx];
|
||||||
let greet_fn = &t.values[idx];
|
|
||||||
|
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 {
|
assert_eq!(&*s, "Hello Bob");
|
||||||
assert_eq!(&*s, "Hello Bob");
|
} else { panic!("Wrong return type"); }
|
||||||
} else { panic!("Wrong return type"); }
|
}
|
||||||
}
|
} else { panic!("Factory should return Record"); }
|
||||||
} else { panic!("Expected keys"); }
|
|
||||||
} else { panic!("Factory should return Tuple/Record"); }
|
|
||||||
} else { panic!("Factory is not a function"); }
|
} else { panic!("Factory is not a function"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+56
-60
@@ -55,12 +55,12 @@ pub trait Object: fmt::Debug {
|
|||||||
fn as_any(&self) -> &dyn Any;
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TupleData {
|
pub struct RecordData {
|
||||||
|
/// Names for slots.
|
||||||
|
pub keys: Rc<Vec<Keyword>>,
|
||||||
pub values: Vec<Value>,
|
pub values: Vec<Value>,
|
||||||
/// Optional names for slots. If present, this is semantically a Record.
|
|
||||||
pub keys: Option<Rc<Vec<Keyword>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Core data value in Myc Script (similar to TDataValue)
|
/// Core data value in Myc Script (similar to TDataValue)
|
||||||
@@ -73,7 +73,8 @@ pub enum Value {
|
|||||||
DateTime(i64),
|
DateTime(i64),
|
||||||
Text(Rc<str>),
|
Text(Rc<str>),
|
||||||
Keyword(Keyword),
|
Keyword(Keyword),
|
||||||
Tuple(Rc<TupleData>), // Replaces List and Record
|
Tuple(Rc<Vec<Value>>),
|
||||||
|
Record(Rc<RecordData>),
|
||||||
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
|
||||||
@@ -224,12 +225,12 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_list(values: Vec<Value>) -> Self {
|
pub fn make_tuple(values: Vec<Value>) -> Self {
|
||||||
Value::Tuple(Rc::new(TupleData { values, keys: None }))
|
Value::Tuple(Rc::new(values))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_record(keys: Vec<Keyword>, values: Vec<Value>) -> Self {
|
pub fn make_record(keys: Vec<Keyword>, values: Vec<Value>) -> 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 {
|
pub fn static_type(&self) -> StaticType {
|
||||||
@@ -241,44 +242,40 @@ impl Value {
|
|||||||
Value::DateTime(_) => StaticType::DateTime,
|
Value::DateTime(_) => StaticType::DateTime,
|
||||||
Value::Text(_) => StaticType::Text,
|
Value::Text(_) => StaticType::Text,
|
||||||
Value::Keyword(_) => StaticType::Keyword,
|
Value::Keyword(_) => StaticType::Keyword,
|
||||||
Value::Tuple(t) => {
|
Value::Tuple(values) => {
|
||||||
if let Some(keys) = &t.keys {
|
if values.is_empty() {
|
||||||
// It's a Record
|
return StaticType::Vector(Box::new(StaticType::Any), 0);
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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::Function(_) => StaticType::Any, // Dynamic function
|
||||||
Value::Object(o) => StaticType::Object(o.type_name()),
|
Value::Object(o) => StaticType::Object(o.type_name()),
|
||||||
@@ -303,22 +300,21 @@ impl fmt::Display for Value {
|
|||||||
},
|
},
|
||||||
Value::Text(t) => write!(f, "\"{}\"", t),
|
Value::Text(t) => write!(f, "\"{}\"", t),
|
||||||
Value::Keyword(k) => write!(f, ":{}", k.name()),
|
Value::Keyword(k) => write!(f, ":{}", k.name()),
|
||||||
Value::Tuple(t) => {
|
Value::Tuple(values) => {
|
||||||
if let Some(keys) = &t.keys {
|
write!(f, "[")?;
|
||||||
write!(f, "{{")?;
|
for (i, val) in values.iter().enumerate() {
|
||||||
for i in 0..t.values.len() {
|
if i > 0 { write!(f, " ")?; }
|
||||||
if i > 0 { write!(f, ", ")?; }
|
write!(f, "{}", val)?;
|
||||||
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, "]")
|
|
||||||
}
|
}
|
||||||
|
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, "<native fn>"),
|
Value::Function(_) => write!(f, "<native fn>"),
|
||||||
Value::Object(o) => write!(f, "<{}>", o.type_name()),
|
Value::Object(o) => write!(f, "<{}>", o.type_name()),
|
||||||
|
|||||||
+34
-17
@@ -313,13 +313,13 @@ macro_rules! dispatch_eval {
|
|||||||
for e in elements {
|
for e in elements {
|
||||||
vals.push($self.$eval_method($($observer,)? e)?);
|
vals.push($self.$eval_method($($observer,)? e)?);
|
||||||
}
|
}
|
||||||
Ok(Value::make_list(vals))
|
Ok(Value::make_tuple(vals))
|
||||||
},
|
},
|
||||||
|
|
||||||
BoundKind::Map { entries } => {
|
BoundKind::Record { fields } => {
|
||||||
let mut keys = Vec::with_capacity(entries.len());
|
let mut keys = Vec::with_capacity(fields.len());
|
||||||
let mut values = Vec::with_capacity(entries.len());
|
let mut values = Vec::with_capacity(fields.len());
|
||||||
for (k, v) in entries {
|
for (k, v) in fields {
|
||||||
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)?;
|
||||||
|
|
||||||
@@ -327,7 +327,7 @@ macro_rules! dispatch_eval {
|
|||||||
keys.push(kw);
|
keys.push(kw);
|
||||||
values.push(val);
|
values.push(val);
|
||||||
} else {
|
} 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))
|
Ok(Value::make_record(keys, values))
|
||||||
@@ -601,12 +601,18 @@ impl VM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn flatten_value(val: Value, into: &mut Vec<Value>) {
|
fn flatten_value(val: Value, into: &mut Vec<Value>) {
|
||||||
if let Value::Tuple(t) = val {
|
match val {
|
||||||
for item in t.values.iter() {
|
Value::Tuple(values) => {
|
||||||
Self::flatten_value(item.clone(), into);
|
for item in values.iter() {
|
||||||
|
Self::flatten_value(item.clone(), into);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
Value::Record(r) => {
|
||||||
into.push(val);
|
for item in r.values.iter() {
|
||||||
|
Self::flatten_value(item.clone(), into);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => into.push(val),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,13 +687,24 @@ impl VM {
|
|||||||
BoundKind::Tuple { elements } => {
|
BoundKind::Tuple { elements } => {
|
||||||
// If the current value at offset is a Tuple (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).
|
// Otherwise, we assume the sequence was already flattened (e.g. by Specializer).
|
||||||
if let Some(Value::Tuple(t)) = values.get(*offset) {
|
match values.get(*offset) {
|
||||||
*offset += 1;
|
Some(Value::Tuple(t)) => {
|
||||||
let mut sub_offset = 0;
|
*offset += 1;
|
||||||
for el in elements {
|
let mut sub_offset = 0;
|
||||||
self.unpack(el, &t.values, &mut sub_offset)?;
|
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 {
|
for el in elements {
|
||||||
|
|||||||
Reference in New Issue
Block a user