feat: add built-in Map type with String keys
Add Map<String, V> as a first-class built-in type for key-value storage, needed for self-hosting the compiler (parser/typechecker/interpreter all rely heavily on hashmaps). - types.rs: Type::Map(K,V) variant, all match arms (unify, apply, etc.) - interpreter.rs: Value::Map, 12 BuiltinFn variants (new/set/get/contains/ remove/keys/values/size/isEmpty/fromList/toList/merge), immutable semantics - typechecker.rs: Map<K,V> resolution in resolve_type - js_backend.rs: Map as JS Map with emit_map_operation() - c_backend.rs: LuxMap struct (linear-scan), runtime fns, emit_map_operation() - main.rs: 12 tests covering all Map operations - validate.sh: now checks all projects/ directories too Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
95
src/types.rs
95
src/types.rs
@@ -47,6 +47,8 @@ pub enum Type {
|
||||
List(Box<Type>),
|
||||
/// Option type (sugar for App(Option, [T]))
|
||||
Option(Box<Type>),
|
||||
/// Map type (sugar for App(Map, [K, V]))
|
||||
Map(Box<Type>, Box<Type>),
|
||||
/// Versioned type (e.g., User @v2)
|
||||
Versioned {
|
||||
base: Box<Type>,
|
||||
@@ -119,6 +121,7 @@ impl Type {
|
||||
Type::Tuple(elements) => elements.iter().any(|e| e.contains_var(var)),
|
||||
Type::Record(fields) => fields.iter().any(|(_, t)| t.contains_var(var)),
|
||||
Type::List(inner) | Type::Option(inner) => inner.contains_var(var),
|
||||
Type::Map(k, v) => k.contains_var(var) || v.contains_var(var),
|
||||
Type::Versioned { base, .. } => base.contains_var(var),
|
||||
_ => false,
|
||||
}
|
||||
@@ -158,6 +161,7 @@ impl Type {
|
||||
),
|
||||
Type::List(inner) => Type::List(Box::new(inner.apply(subst))),
|
||||
Type::Option(inner) => Type::Option(Box::new(inner.apply(subst))),
|
||||
Type::Map(k, v) => Type::Map(Box::new(k.apply(subst)), Box::new(v.apply(subst))),
|
||||
Type::Versioned { base, version } => Type::Versioned {
|
||||
base: Box::new(base.apply(subst)),
|
||||
version: version.clone(),
|
||||
@@ -208,6 +212,11 @@ impl Type {
|
||||
vars
|
||||
}
|
||||
Type::List(inner) | Type::Option(inner) => inner.free_vars(),
|
||||
Type::Map(k, v) => {
|
||||
let mut vars = k.free_vars();
|
||||
vars.extend(v.free_vars());
|
||||
vars
|
||||
}
|
||||
Type::Versioned { base, .. } => base.free_vars(),
|
||||
_ => HashSet::new(),
|
||||
}
|
||||
@@ -279,6 +288,7 @@ impl fmt::Display for Type {
|
||||
}
|
||||
Type::List(inner) => write!(f, "List<{}>", inner),
|
||||
Type::Option(inner) => write!(f, "Option<{}>", inner),
|
||||
Type::Map(k, v) => write!(f, "Map<{}, {}>", k, v),
|
||||
Type::Versioned { base, version } => {
|
||||
write!(f, "{} {}", base, version)
|
||||
}
|
||||
@@ -1775,6 +1785,73 @@ impl TypeEnv {
|
||||
]);
|
||||
env.bind("Option", TypeScheme::mono(option_module_type));
|
||||
|
||||
// Map module
|
||||
let map_v = || Type::var();
|
||||
let map_type = || Type::Map(Box::new(Type::String), Box::new(Type::var()));
|
||||
let map_module_type = Type::Record(vec![
|
||||
(
|
||||
"new".to_string(),
|
||||
Type::function(vec![], map_type()),
|
||||
),
|
||||
(
|
||||
"set".to_string(),
|
||||
Type::function(
|
||||
vec![map_type(), Type::String, map_v()],
|
||||
map_type(),
|
||||
),
|
||||
),
|
||||
(
|
||||
"get".to_string(),
|
||||
Type::function(
|
||||
vec![map_type(), Type::String],
|
||||
Type::Option(Box::new(map_v())),
|
||||
),
|
||||
),
|
||||
(
|
||||
"contains".to_string(),
|
||||
Type::function(vec![map_type(), Type::String], Type::Bool),
|
||||
),
|
||||
(
|
||||
"remove".to_string(),
|
||||
Type::function(vec![map_type(), Type::String], map_type()),
|
||||
),
|
||||
(
|
||||
"keys".to_string(),
|
||||
Type::function(vec![map_type()], Type::List(Box::new(Type::String))),
|
||||
),
|
||||
(
|
||||
"values".to_string(),
|
||||
Type::function(vec![map_type()], Type::List(Box::new(map_v()))),
|
||||
),
|
||||
(
|
||||
"size".to_string(),
|
||||
Type::function(vec![map_type()], Type::Int),
|
||||
),
|
||||
(
|
||||
"isEmpty".to_string(),
|
||||
Type::function(vec![map_type()], Type::Bool),
|
||||
),
|
||||
(
|
||||
"fromList".to_string(),
|
||||
Type::function(
|
||||
vec![Type::List(Box::new(Type::Tuple(vec![Type::String, map_v()])))],
|
||||
map_type(),
|
||||
),
|
||||
),
|
||||
(
|
||||
"toList".to_string(),
|
||||
Type::function(
|
||||
vec![map_type()],
|
||||
Type::List(Box::new(Type::Tuple(vec![Type::String, map_v()]))),
|
||||
),
|
||||
),
|
||||
(
|
||||
"merge".to_string(),
|
||||
Type::function(vec![map_type(), map_type()], map_type()),
|
||||
),
|
||||
]);
|
||||
env.bind("Map", TypeScheme::mono(map_module_type));
|
||||
|
||||
// Result module
|
||||
let result_type = Type::App {
|
||||
constructor: Box::new(Type::Named("Result".to_string())),
|
||||
@@ -1908,6 +1985,10 @@ impl TypeEnv {
|
||||
"toString".to_string(),
|
||||
Type::function(vec![Type::Int], Type::String),
|
||||
),
|
||||
(
|
||||
"toFloat".to_string(),
|
||||
Type::function(vec![Type::Int], Type::Float),
|
||||
),
|
||||
]);
|
||||
env.bind("Int", TypeScheme::mono(int_module_type));
|
||||
|
||||
@@ -1917,6 +1998,10 @@ impl TypeEnv {
|
||||
"toString".to_string(),
|
||||
Type::function(vec![Type::Float], Type::String),
|
||||
),
|
||||
(
|
||||
"toInt".to_string(),
|
||||
Type::function(vec![Type::Float], Type::Int),
|
||||
),
|
||||
]);
|
||||
env.bind("Float", TypeScheme::mono(float_module_type));
|
||||
|
||||
@@ -2003,6 +2088,9 @@ impl TypeEnv {
|
||||
Type::Option(inner) => {
|
||||
Type::Option(Box::new(self.expand_type_alias(inner)))
|
||||
}
|
||||
Type::Map(k, v) => {
|
||||
Type::Map(Box::new(self.expand_type_alias(k)), Box::new(self.expand_type_alias(v)))
|
||||
}
|
||||
Type::Versioned { base, version } => {
|
||||
Type::Versioned {
|
||||
base: Box::new(self.expand_type_alias(base)),
|
||||
@@ -2163,6 +2251,13 @@ pub fn unify(t1: &Type, t2: &Type) -> Result<Substitution, String> {
|
||||
// Option
|
||||
(Type::Option(a), Type::Option(b)) => unify(a, b),
|
||||
|
||||
// Map
|
||||
(Type::Map(k1, v1), Type::Map(k2, v2)) => {
|
||||
let s1 = unify(k1, k2)?;
|
||||
let s2 = unify(&v1.apply(&s1), &v2.apply(&s1))?;
|
||||
Ok(s1.compose(&s2))
|
||||
}
|
||||
|
||||
// Versioned types
|
||||
(
|
||||
Type::Versioned {
|
||||
|
||||
Reference in New Issue
Block a user