ERR/E0.3: parser test consolidation — Phase E0 complete

Fills the E0.3 coverage gaps E0.1/E0.2's inline tests hadn't hit and adds
an end-to-end integration parse. Test-only; no production code change.

- `try` in statement position (`try must_init();`).
- `try` over a parenthesized or-chain (`try (foo() or boo())`) — distinct
  from `try foo() or try boo()`.
- `or` value-terminator (`parse(s) or 0`).
- Integration: a full `parse :: (s) -> (s32, !ParseErr) { onfail / try / or /
  catch / if { raise } / return }` — asserts the trailing `!ParseErr`, the
  five body statement kinds, and that `in_onfail_body` is correctly scoped
  (the later if-block `raise` is allowed).

Tests stay inline in parser.zig (consistent with the existing 24 + E0.1/E0.2
inline tests). 37 ERR parser tests total; every new AST node has a round-trip
test. zig build test (268) and 254/254 examples green.
This commit is contained in:
agra
2026-05-31 17:14:02 +03:00
parent 1b777dd6ab
commit fdeab0efd4

View File

@@ -4336,3 +4336,68 @@ test "E0.2 round-trip print: catch match-body form" {
const v = try e02FirstValue(a, "f :: () { v := foo() catch e == { case .Empty: 0; else: 1; }; }");
try e02ExpectPrints(a, v, "foo() catch e == { case .Empty: 0; else: 1; }");
}
// ── ERR step E0.3 — coverage consolidation (gaps + integration) ──
test "E0.3 try in statement position (propagate, discard value)" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const s = try e02FirstStmt(arena.allocator(), "f :: () { try must_init(); }");
try std.testing.expect(s.data == .try_expr);
try std.testing.expect(s.data.try_expr.operand.data == .call);
}
test "E0.3 try over a parenthesized or-chain: try (foo() or boo())" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const v = try e02FirstValue(arena.allocator(), "f :: () { v := try (foo() or boo()); }");
// Distinct from `try foo() or try boo()`: here `try` wraps the whole chain.
try std.testing.expect(v.data == .try_expr);
try std.testing.expect(v.data.try_expr.operand.data == .binary_op);
try std.testing.expect(v.data.try_expr.operand.data.binary_op.op == .or_op);
}
test "E0.3 or value-terminator: parse(s) or 0" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const v = try e02FirstValue(arena.allocator(), "f :: () { v := parse(s) or 0; }");
try std.testing.expect(v.data == .binary_op and v.data.binary_op.op == .or_op);
try std.testing.expect(v.data.binary_op.lhs.data == .call);
try std.testing.expect(v.data.binary_op.rhs.data == .int_literal);
}
test "E0.3 full failable function parses end-to-end (all E0 forms)" {
const source =
\\parse :: (s: string) -> (s32, !ParseErr) {
\\ onfail e { cleanup(s); }
\\ v := try inner(s) or 0;
\\ w := other(s) catch e2 { return 0; };
\\ if bad(s) { raise error.BadDigit; }
\\ return v;
\\}
;
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
var parser = Parser.init(arena.allocator(), source);
const root = try parser.parse();
const decl = root.data.root.decls[0];
try std.testing.expect(decl.data == .fn_decl);
try std.testing.expectEqualStrings("parse", decl.data.fn_decl.name);
// return type is a multi-value result list ending in `!ParseErr`
const rt = decl.data.fn_decl.return_type.?;
try std.testing.expect(rt.data == .tuple_type_expr);
const fields = rt.data.tuple_type_expr.field_types;
try std.testing.expect(fields[fields.len - 1].data == .error_type_expr);
try std.testing.expectEqualStrings("ParseErr", fields[fields.len - 1].data.error_type_expr.name.?);
// body statement kinds
const stmts = decl.data.fn_decl.body.data.block.stmts;
try std.testing.expectEqual(@as(usize, 5), stmts.len);
try std.testing.expect(stmts[0].data == .onfail_stmt);
try std.testing.expect(stmts[1].data == .var_decl and stmts[1].data.var_decl.value.?.data == .binary_op);
try std.testing.expect(stmts[2].data == .var_decl and stmts[2].data.var_decl.value.?.data == .catch_expr);
try std.testing.expect(stmts[3].data == .if_expr);
try std.testing.expect(stmts[4].data == .return_stmt);
// the onfail flag was restored: the raise inside the (separate) if-block is allowed
const then_block = stmts[3].data.if_expr.then_branch;
try std.testing.expect(then_block.data.block.stmts[0].data == .raise_stmt);
}