Update some tests, delete obsolete ones

This commit is contained in:
Richard Feldman 2025-11-21 11:20:41 -05:00
parent bd4006ce3a
commit d4e4b58dfd
No known key found for this signature in database
4 changed files with 42 additions and 133 deletions

View file

@ -66,44 +66,15 @@ test "polymorphic empty list" {
}
test "polymorphic cons function" {
// This test is skipped because these features are missing:
// - Spread operator `..` in list literals [fails at parse stage - syntax not recognized]
// TODO: Enable when spread operator is implemented in the parser
const source =
\\{
\\ cons = |x, xs| [x, ..xs]
\\ cons = |x, xs| List.concat([x], xs)
\\ list1 = cons(1, [2, 3])
\\ list2 = cons("a", ["b", "c"])
\\ { list1, list2 }
\\}
;
try typeCheck(source, "TODO");
}
test "polymorphic map function" {
// This test is skipped because these features are missing:
// - If-then-else expressions [fails at parse stage - syntax not recognized]
// - Recursive function calls [would fail at canonicalize stage - self-references not resolved]
// - List slicing `xs[1..]` [fails at parse stage - range syntax not recognized]
// - Spread operator `[x, ..xs]` [fails at parse stage - syntax not recognized]
// - List equality comparison `xs == []` [may fail at type-check stage]
// Note: List indexing `xs[0]` does parse and canonicalize but may have type issues
// TODO: Enable when conditional expressions, recursion, and list operations are implemented
const source =
\\{
\\ map = |f, xs|
\\ if xs == [] then
\\ []
\\ else
\\ [f(xs[0]), ..map(f, xs[1..])]
\\ double = |x| x * 2
\\ nums = map(double, [1, 2, 3])
\\ { nums }
\\}
;
try typeCheck(source, "TODO");
try typeCheck(source, "{ list1: List(item), list2: List(Str) } where [_a.from_num_literal : _arg -> _ret]");
}
test "polymorphic record constructor" {
@ -174,32 +145,6 @@ test "polymorphic swap function" {
try typeCheck(source, "{ swapped1: { first: Str, second: _field }, swapped2: { first: _field2, second: [True]_others } } where [_a.from_num_literal : _arg -> _ret, _b.from_num_literal : _arg2 -> _ret2]");
}
test "polymorphic fold function" {
// This test is skipped because these features are missing:
// - If-then-else expressions [fails at parse stage - syntax not recognized]
// - Recursive function calls [would fail at canonicalize stage - self-references not resolved]
// - List equality comparison `xs == []` [may fail at type-check stage]
// - String concatenation operator `++` [fails at parse or canonicalize stage]
// - List slicing `xs[1..]` [fails at parse stage - range syntax not recognized]
// Even if parsing succeeded, the canonicalizer doesn't support recursive
// let-bindings, and the type checker doesn't handle recursive polymorphic functions.
// TODO: Enable when conditional expressions, recursion, and list/string operations are implemented
const source =
\\{
\\ fold = |f, acc, xs|
\\ if xs == [] then
\\ acc
\\ else
\\ fold(f, f(acc, xs[0]), xs[1..])
\\ sum = fold(|a, b| a + b, 0, [1, 2, 3])
\\ concat = fold(|a, b| a ++ b, "", ["a", "b", "c"])
\\ { sum, concat }
\\}
;
try typeCheck(source, "TODO");
}
test "polymorphic option type simulation" {
const source =
\\{
@ -228,33 +173,6 @@ test "polymorphic const function" {
try typeCheck(source, "{ num: _field, str: Str } where [_a.from_num_literal : _arg -> _ret]");
}
test "shadowing of polymorphic values" {
// This test is skipped because these features are missing:
// - Type checking for nested block expressions that return values
// [parses and canonicalizes successfully, fails at type-check stage]
// The inner block `{ id = ...; b = ...; b }` should return `b` as its value.
// The type checker fails to properly handle the combination of:
// 1. A nested block that shadows a polymorphic identifier
// 2. The block returning a value (the final `b` expression)
// 3. Continuing to use the original polymorphic `id` after the block
// TODO: Enable when nested block expressions with value returns are fully supported
const source =
\\{
\\ id = |x| x
\\ a = id(1)
\\ inner = {
\\ id = |x| x + 1 // shadows outer id, now monomorphic
\\ b = id(2)
\\ b
\\ }
\\ c = id("test") // uses outer polymorphic id
\\ { a, inner, c }
\\}
;
try typeCheck(source, "TODO");
}
test "polymorphic pipe function" {
const source =
\\{

View file

@ -138,8 +138,8 @@ test "ModuleEnv.Serialized roundtrip" {
try testing.expectEqual(@as(usize, 2), original.imports.imports.len()); // Should have 2 unique imports
// First verify that the CommonEnv data was preserved after deserialization
// Should have same 10 identifiers as original: hello, world, TestModule + 7 well-known identifiers from ModuleEnv.init()
try testing.expectEqual(@as(u32, 10), env.common.idents.interner.entry_count);
// Should have same 21 identifiers as original: hello, world, TestModule + 18 well-known identifiers from ModuleEnv.init()
try testing.expectEqual(@as(u32, 21), env.common.idents.interner.entry_count);
try testing.expectEqual(@as(usize, 1), env.common.exposed_items.count());
try testing.expectEqual(@as(?u16, 42), env.common.exposed_items.getNodeIndexById(gpa, @as(u32, @bitCast(hello_idx))));

View file

@ -45,42 +45,6 @@ test "eval simple number" {
try runExpectInt("-1234", -1234, .no_trace);
}
test "eval boolean literals" {
try runExpectBool("True", true, .no_trace);
try runExpectBool("False", false, .no_trace);
// Note: Qualified tags like Bool.True and Bool.False don't work yet
// See QUALIFIED_TAGS.md for details
}
test "eval unary not operator" {
try runExpectBool("!True", false, .no_trace);
try runExpectBool("!False", true, .no_trace);
}
test "eval double negation" {
try runExpectBool("!!True", true, .no_trace);
try runExpectBool("!!False", false, .no_trace);
try runExpectBool("!!!True", false, .no_trace);
try runExpectBool("!!!False", true, .no_trace);
}
test "eval boolean in lambda expressions" {
try runExpectBool("(|x| !x)(True)", false, .no_trace);
try runExpectBool("(|x| !x)(False)", true, .no_trace);
// Not implemented yet -- the closure return type is still flex var
// try runExpectBool("(|x, y| x and y)(True, False)", false, .no_trace);
// try runExpectBool("(|x, y| x or y)(False, True)", true, .no_trace);
// try runExpectBool("(|x| x and !x)(True)", false, .no_trace);
// try runExpectBool("(|x| x or !x)(False)", true, .no_trace);
}
test "eval unary not in conditional expressions" {
try runExpectInt("if !True 42 else 99", 99, .no_trace);
try runExpectInt("if !False 42 else 99", 42, .no_trace);
try runExpectInt("if !!True 42 else 99", 42, .no_trace);
try runExpectInt("if !!False 42 else 99", 99, .no_trace);
}
test "if-else" {
try runExpectInt("if (1 == 1) 42 else 99", 42, .no_trace);
try runExpectInt("if (1 == 2) 42 else 99", 99, .no_trace);
@ -142,17 +106,6 @@ test "comparison binops" {
try runExpectInt("if 5 != 5 100 else 200", 200, .no_trace);
}
test "logical binops" {
try runExpectInt("if True and True 1 else 0", 1, .no_trace);
try runExpectInt("if True and False 1 else 0", 0, .no_trace);
try runExpectInt("if False and True 1 else 0", 0, .no_trace);
try runExpectInt("if False and False 1 else 0", 0, .no_trace);
try runExpectInt("if True or True 1 else 0", 1, .no_trace);
try runExpectInt("if True or False 1 else 0", 1, .no_trace);
try runExpectInt("if False or True 1 else 0", 1, .no_trace);
try runExpectInt("if False or False 1 else 0", 0, .no_trace);
}
test "unary minus" {
try runExpectInt("-5", -5, .no_trace);
try runExpectInt("-(-10)", 10, .no_trace);

View file

@ -107,6 +107,44 @@ pub fn runExpectInt(src: []const u8, expected_int: i128, should_trace: enum { tr
try std.testing.expectEqual(expected_int, int_value);
}
/// Helper function to run an expression and expect a boolean result.
pub fn runExpectBool(src: []const u8, expected_bool: bool, should_trace: enum { trace, no_trace }) !void {
const resources = try parseAndCanonicalizeExpr(test_allocator, src);
defer cleanupParseAndCanonical(test_allocator, resources);
var test_env_instance = TestEnv.init(test_allocator);
defer test_env_instance.deinit();
const builtin_types = BuiltinTypes.init(resources.builtin_indices, resources.builtin_module.env, resources.builtin_module.env, resources.builtin_module.env);
var interpreter = try Interpreter.init(test_allocator, resources.module_env, builtin_types, resources.builtin_module.env, &[_]*const can.ModuleEnv{});
defer interpreter.deinit();
const enable_trace = should_trace == .trace;
if (enable_trace) {
interpreter.startTrace();
}
defer if (enable_trace) interpreter.endTrace();
const ops = test_env_instance.get_ops();
const result = try interpreter.evalMinimal(resources.expr_idx, ops);
const layout_cache = &interpreter.runtime_layout_store;
defer result.decref(layout_cache, ops);
// For boolean results, read the underlying byte value
if (result.layout.tag == .scalar and result.layout.data.scalar.tag == .int) {
// Boolean represented as integer (discriminant)
const int_val = result.asI128();
const bool_val = int_val != 0;
try std.testing.expectEqual(expected_bool, bool_val);
} else {
// Try reading as raw byte (for boolean tag values)
std.debug.assert(result.ptr != null);
const bool_ptr: *const u8 = @ptrCast(@alignCast(result.ptr.?));
const bool_val = bool_ptr.* != 0;
try std.testing.expectEqual(expected_bool, bool_val);
}
}
/// Helper function to run an expression and expect an f32 result (with epsilon tolerance).
pub fn runExpectF32(src: []const u8, expected_f32: f32, should_trace: enum { trace, no_trace }) !void {
const resources = try parseAndCanonicalizeExpr(test_allocator, src);