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:
118
src/main.rs
118
src/main.rs
@@ -5441,4 +5441,122 @@ c")"#;
|
||||
check_file("projects/rest-api/main.lux").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// === Map type tests ===
|
||||
|
||||
#[test]
|
||||
fn test_map_new_and_size() {
|
||||
let source = r#"
|
||||
let m = Map.new()
|
||||
let result = Map.size(m)
|
||||
"#;
|
||||
assert_eq!(eval(source).unwrap(), "0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_set_and_get() {
|
||||
let source = r#"
|
||||
let m = Map.new()
|
||||
let m2 = Map.set(m, "name", "Alice")
|
||||
let result = Map.get(m2, "name")
|
||||
"#;
|
||||
assert_eq!(eval(source).unwrap(), "Some(\"Alice\")");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_get_missing() {
|
||||
let source = r#"
|
||||
let m = Map.new()
|
||||
let result = Map.get(m, "missing")
|
||||
"#;
|
||||
assert_eq!(eval(source).unwrap(), "None");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_contains() {
|
||||
let source = r#"
|
||||
let m = Map.set(Map.new(), "x", 1)
|
||||
let result = (Map.contains(m, "x"), Map.contains(m, "y"))
|
||||
"#;
|
||||
assert_eq!(eval(source).unwrap(), "(true, false)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_remove() {
|
||||
let source = r#"
|
||||
let m = Map.set(Map.set(Map.new(), "a", 1), "b", 2)
|
||||
let m2 = Map.remove(m, "a")
|
||||
let result = (Map.size(m2), Map.contains(m2, "a"), Map.contains(m2, "b"))
|
||||
"#;
|
||||
assert_eq!(eval(source).unwrap(), "(1, false, true)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_keys_and_values() {
|
||||
let source = r#"
|
||||
let m = Map.set(Map.set(Map.new(), "b", 2), "a", 1)
|
||||
let result = Map.keys(m)
|
||||
"#;
|
||||
assert_eq!(eval(source).unwrap(), "[\"a\", \"b\"]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_from_list() {
|
||||
let source = r#"
|
||||
let m = Map.fromList([("x", 10), ("y", 20)])
|
||||
let result = (Map.get(m, "x"), Map.size(m))
|
||||
"#;
|
||||
assert_eq!(eval(source).unwrap(), "(Some(10), 2)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_to_list() {
|
||||
let source = r#"
|
||||
let m = Map.set(Map.set(Map.new(), "b", 2), "a", 1)
|
||||
let result = Map.toList(m)
|
||||
"#;
|
||||
assert_eq!(eval(source).unwrap(), "[(\"a\", 1), (\"b\", 2)]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_merge() {
|
||||
let source = r#"
|
||||
let m1 = Map.fromList([("a", 1), ("b", 2)])
|
||||
let m2 = Map.fromList([("b", 3), ("c", 4)])
|
||||
let merged = Map.merge(m1, m2)
|
||||
let result = (Map.get(merged, "a"), Map.get(merged, "b"), Map.get(merged, "c"))
|
||||
"#;
|
||||
assert_eq!(eval(source).unwrap(), "(Some(1), Some(3), Some(4))");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_immutability() {
|
||||
let source = r#"
|
||||
let m1 = Map.fromList([("a", 1)])
|
||||
let m2 = Map.set(m1, "b", 2)
|
||||
let result = (Map.size(m1), Map.size(m2))
|
||||
"#;
|
||||
assert_eq!(eval(source).unwrap(), "(1, 2)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_is_empty() {
|
||||
let source = r#"
|
||||
let m1 = Map.new()
|
||||
let m2 = Map.set(m1, "x", 1)
|
||||
let result = (Map.isEmpty(m1), Map.isEmpty(m2))
|
||||
"#;
|
||||
assert_eq!(eval(source).unwrap(), "(true, false)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_type_annotation() {
|
||||
let source = r#"
|
||||
fn lookup(m: Map<String, Int>, key: String): Option<Int> =
|
||||
Map.get(m, key)
|
||||
let m = Map.fromList([("age", 30)])
|
||||
let result = lookup(m, "age")
|
||||
"#;
|
||||
assert_eq!(eval(source).unwrap(), "Some(30)");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user