Refactor type inference for collections
The type inference for collections (tuples, vectors, matrices, and records) has been significantly refactored. This includes: - Introducing distinct `StaticType` variants for `Tuple`, `Vector`, and `Matrix`. - Implementing more robust type checking for these collections, allowing for homogeneous and heterogeneous structures. - Enhancing the `is_assignable_from` method to handle type compatibility between these collection types. - Updating `Value::static_type()` to correctly infer the types of literal collections. - Improving the type inference for map literals to create `Record` types. - Adding comprehensive unit tests for tuple, vector, matrix, and record type inference.
This commit is contained in:
+87
-5
@@ -89,7 +89,10 @@ pub enum StaticType {
|
||||
DateTime,
|
||||
Text,
|
||||
Keyword,
|
||||
List(Box<StaticType>),
|
||||
List(Box<StaticType>), // Legacy / Dynamic list
|
||||
Tuple(Vec<StaticType>), // Heterogeneous fixed-size
|
||||
Vector(Box<StaticType>, usize), // Homogeneous fixed-size
|
||||
Matrix(Box<StaticType>, Vec<usize>), // Multi-dimensional homogeneous
|
||||
Record(Rc<BTreeMap<Keyword, StaticType>>),
|
||||
Function(Box<Signature>),
|
||||
FunctionOverloads(Vec<Signature>),
|
||||
@@ -108,6 +111,23 @@ impl fmt::Display for StaticType {
|
||||
StaticType::Text => write!(f, "text"),
|
||||
StaticType::Keyword => write!(f, "keyword"),
|
||||
StaticType::List(inner) => write!(f, "[{}]", inner),
|
||||
StaticType::Tuple(elements) => {
|
||||
write!(f, "[")?;
|
||||
for (i, el) in elements.iter().enumerate() {
|
||||
if i > 0 { write!(f, " ")?; }
|
||||
write!(f, "{}", el)?;
|
||||
}
|
||||
write!(f, "]")
|
||||
},
|
||||
StaticType::Vector(inner, len) => write!(f, "vector<{}, {}>", inner, len),
|
||||
StaticType::Matrix(inner, shape) => {
|
||||
write!(f, "matrix<{}, [", inner)?;
|
||||
for (i, s) in shape.iter().enumerate() {
|
||||
if i > 0 { write!(f, " ")?; }
|
||||
write!(f, "{}", s)?;
|
||||
}
|
||||
write!(f, "]>")
|
||||
},
|
||||
StaticType::Record(fields) => {
|
||||
write!(f, "{{")?;
|
||||
for (i, (k, v)) in fields.iter().enumerate() {
|
||||
@@ -138,8 +158,26 @@ impl StaticType {
|
||||
if self == other || matches!(self, StaticType::Any) || matches!(other, StaticType::Any) {
|
||||
return true;
|
||||
}
|
||||
// Future: Numeric promotion (Int -> Float)
|
||||
false
|
||||
|
||||
match (self, other) {
|
||||
// A Vector is a Tuple
|
||||
(StaticType::Tuple(elements), StaticType::Vector(inner, len)) => {
|
||||
if elements.len() != *len { return false; }
|
||||
elements.iter().all(|e| e.is_assignable_from(inner))
|
||||
},
|
||||
// A Matrix is a Vector (of Vectors/Matrices)
|
||||
(StaticType::Vector(inner, len), StaticType::Matrix(m_inner, shape)) => {
|
||||
if shape.is_empty() || shape[0] != *len { return false; }
|
||||
if shape.len() == 1 {
|
||||
inner.is_assignable_from(m_inner)
|
||||
} else {
|
||||
// It's a matrix of higher dimension, so inner must be assignable from a sub-matrix
|
||||
let sub_shape = shape[1..].to_vec();
|
||||
inner.is_assignable_from(&StaticType::Matrix(m_inner.clone(), sub_shape))
|
||||
}
|
||||
},
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to resolve a call with the given argument types and returns the return type.
|
||||
@@ -168,6 +206,17 @@ impl StaticType {
|
||||
}
|
||||
expected.iter().zip(actual).all(|(e, a)| e.is_assignable_from(a))
|
||||
}
|
||||
|
||||
/// Returns true if this type and all its recursive elements are scalars (Int, Float, etc.)
|
||||
pub fn is_scalar_pure(&self) -> bool {
|
||||
match self {
|
||||
StaticType::Int | StaticType::Float | StaticType::Bool | StaticType::DateTime => true,
|
||||
StaticType::Tuple(elements) => elements.iter().all(|e| e.is_scalar_pure()),
|
||||
StaticType::Vector(inner, _) => inner.is_scalar_pure(),
|
||||
StaticType::Matrix(inner, _) => inner.is_scalar_pure(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
@@ -189,8 +238,41 @@ impl Value {
|
||||
Value::DateTime(_) => StaticType::DateTime,
|
||||
Value::Text(_) => StaticType::Text,
|
||||
Value::Keyword(_) => StaticType::Keyword,
|
||||
Value::List(_) => StaticType::List(Box::new(StaticType::Any)),
|
||||
Value::Record(_) => StaticType::Record(Rc::new(BTreeMap::new())), // Empty for now
|
||||
Value::List(l) => {
|
||||
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::Record(r) => {
|
||||
let mut fields = BTreeMap::new();
|
||||
for (k, v) in r.iter() {
|
||||
fields.insert(*k, v.static_type());
|
||||
}
|
||||
StaticType::Record(Rc::new(fields))
|
||||
},
|
||||
Value::Function(_) => StaticType::Any, // Dynamic function
|
||||
Value::Object(o) => StaticType::Object(o.type_name()),
|
||||
Value::Cell(c) => c.borrow().static_type(),
|
||||
|
||||
Reference in New Issue
Block a user