Skip to content

t27o: a Zig front end (zig_convert.t27) -- 36,495 Zig fns on the roadmap repos, 10-38 % in a first subset, tests as the oracle (stage 5) #10597

Open

Description

@gHashTag

Goal

A third t27o front end, specs/tools/t27o/zig_convert.t27 beside rust_convert.t27 and spice_convert.t27, run as tri t27o <file.zig>: hand-written Zig on the roadmap becomes .t27 specs, judged by vectors recorded from the file's own test blocks. Proposed by the front-end census of #10522 (see the census section of docs/nona-03-manifest/T27O-COMPETITORS-AND-DOGFOOD.md); roadmap stage 5 (#4547) says "hand-written Zig becomes generated Zig".

Why this matters (census 2026-10-19, coarse: regex and brace matching, no type check)

Language on the five roadmap repos Distinct fns In a strict first subset
Zig 36,495 3,797 (10.4 %); 7,767 (21.3 %) with same-file named types; 13,900 (38.1 %) with *T self
Rust (today's front end) 16,775 1,667 converted after #10593
Python (stdlib ast) 6,468 237 (3.7 %)
TypeScript (compiler API, syntactic) 9,746 243 (2.5 %); 26 % of fns are async

Zig is the largest pool, its grammar is closest to t27's (both derive from the same expression and statement forms), and 1,947 of 2,644 Zig files already hold test blocks (19,594 expectEqual) that can serve as the oracle. First refusals of the coarse subset: error union 41.8 %, pointer 29.9 %, named type 16.8 %, std/allocator 6.9 %.

Deliverable (three steps, each its own PR with tests and a negative control)

  1. Scalar and slice leaf fns: every fn the strict subset admits converts, or is refused with a named kind; equivalence on vectors recorded from the file's test blocks.
  2. Same-file struct / enum and *T self methods become t27 structs and free fns; a method that writes through self is refused, with a negative control. Re-measure the pool with a real parse first (the census is regex-grade).
  3. Error unions: !T, try, catch, error.X convert to the form the rest of the corpus uses, and the error-union refusals (41.8 % of first refusals) drop.

Done when

Step 1 lands with the before/after count on trinity src and trios crates at named commits; tri t27o-sweep (#10543) counts .zig files beside .rs.

Boundary

Risks

The census was coarse; the real parse (comment below) moves the pool down by about 41 % for step 1. Generated Zig (402 parsed files carry a generator marker in their first 20 lines; most Generated from lines name .tri specs, only 12 name .vibee -- an earlier version of this issue said "341 files naming .vibee", which the real parse refuted) must be excluded or traced to its source spec, never converted twice; the rule keys on the generator marker, not on .vibee.

Links

#10522, #4547, #8995 (Python, ranked third), #10543.

User Scenarios

  • Given a Zig file in the roadmap repos containing only scalar and slice leaf functions with test blocks, when tri t27o <file.zig> runs, then the tool emits a .t27 spec that passes equivalence on vectors recorded from the file's own test blocks.
  • Given a Zig file containing same-file struct/enum definitions and *T self methods, when tri t27o <file.zig> runs, then the tool emits a .t27 spec with t27 structs and free functions, and refuses any method that writes through self with a named refusal kind.
  • Given a Zig file containing error unions (!T, try, catch, error.X), when tri t27o <file.zig> runs, then the tool converts them to the form used by the rest of the corpus and the error-union refusal rate drops from the census baseline.

Requirements

FR-001: The tool specs/tools/t27o/zig_convert.t27 MUST exist beside rust_convert.t27 and spice_convert.t27 and be invokable as tri t27o <file.zig>.
FR-002: For every function admitted by the strict subset (scalar and slice leaf fns), the converter MUST either produce a .t27 spec or refuse with a named refusal kind, and equivalence MUST hold on vectors recorded from the file's test blocks.
FR-003: Same-file struct/enum and *T self methods MUST become t27 structs and free functions; a method that writes through self MUST be refused with a named kind and a negative control test.
FR-004: Error unions (!T, try, catch, error.X) MUST convert to the form used by the rest of the corpus, reducing the error-union refusal rate from the census baseline of 41.8%.
FR-005: Generated Zig files (identified by .vibee spec headers) MUST be excluded or traced to their source spec and never converted twice.
FR-006: The extension dispatch in cmd_t27o MUST be updated for Zig with glue only; any added hand-written line MUST be paid for by a deleted one per the own-language check.
FR-007: rust_convert.t27 and spice_convert.t27 MUST NOT be modified.

Activity

  1. gHashTag commented on Oct 18, 2026

    @gHashTag
    OwnerAuthor

    Real-parse census (std.zig.Ast, Zig 0.16.0; throw-away code, not committed) -- 2026-10-19

    Same five repositories and commits as the coarse census. Bodies deduplicated by sha256; "hand" = at least one copy outside a file whose first 20 lines carry a generator marker.

    Tier Coarse (regex) Real parse, distinct bodies Real parse, hand-written distinct
    1 scalar + slice only 3,797 (10.4 %) 2,619 (9.1 %) 2,243 (8.6 %)
    2 + same-file named types 7,767 (21.3 %) 4,921 (17.1 %) 4,343 (16.7 %)
    3 + *T self 13,900 (38.1 %) 8,014 (27.8 %) 7,285 (28.1 %)
    • Pool: 80,970 fn decls in tracked files, 36,113 in distinct parsed blobs, 28,855 distinct bodies; 36 blobs (64 tracked files) do not parse -- all look like genuinely invalid code (e.g. a Rust path in specs/igla/coder/training.zig), unverified against the 0.15.2 the repos pin.
    • The regex extraction itself was accurate (36,099 vs 36,113 fns); the drop comes from resolving names (mutable file-level vars, @import aliases, nested fns), duplication across trinity / trinity-fpga, and generated copies over-represented in tier 1.
    • First disqualifiers match the coarse ones (error union 41.8 %, pointer 30.1 %, named type 16.7 %, std / allocator 6.9 %), but an error union is the ONLY blocker for just 518 fns (3.8 %): step 3 alone unlocks little.
    • Generated Zig: 402 of 2,608 parsed files carry a generator marker (most name .tri specs; 12 name .vibee). The earlier "341 files naming .vibee" in this issue was wrong and is corrected above.
    • Oracle: of the 2,243 tier-1 hand-written bodies, 1,201 (53.5 %) are called unambiguously from a test block in the same file; 187 sit inside expectEqual* / expectApproxEq* (value assertions, the strongest).

    Honest target for the first PR: about 1,052 tier-1 hand-written distinct fns that call only builtins and std.math / std.ascii / std.mem.eql and have an unambiguous same-file test call (874 of them in trinity); 842 with no calls at all. Step 1 must carry file-level scalar const declarations (627 need them) and allow std.math. Baselines: trinity src/ 1,590 tier-1 hand-written (978 with a test call), trios crates/ 546 (206).

    Uncertain: test runnability under 0.15.2 (under 0.16.0, 19 of 40 sampled test files type-check; most failures are std API drift); many assertions are properties (expect(x > 0)), not values; write-through-self detection sees assignments only.

  2. gHashTag commented on Oct 19, 2026

    @gHashTag
    OwnerAuthor

    Owner decision 2026-10-19 (translated): "continue autonomously, options A and B" -- this proposal (option B of #10522) is approved; the roadmap label is on. Generated Zig, the open question above: the default taken is that a file with a generator marker in its first 20 lines is refused as a whole ("generated; convert its source spec instead") and never converted twice; tracing it to its .tri / .vibee spec is a later step. Step 1 is #10765 (in progress).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions