Replace the count-only check with a faithful textual comparison, analogous to how expectEqualZir compares AstGen output: - Export Zcu from test_exports so tests can construct a PerThread - Parse Zig verbose_air output into per-function sections keyed by FQN - For each C function Air, render it as text via air.write() using the Zig PerThread (InternPool indices must match between C and Zig for the same source), then compare against the Zig reference text For the current corpus (codecs.zig, no functions), both sides produce zero entries so the comparison loop is empty. When zirFunc is ported and a corpus file with functions is added, this will exercise real per-function Air matching. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
About
zig0 aspires to be an interpreter of zig 0.15.2 written in C.
This is written with help from LLM:
- Lexer:
- Datastructures 100% human.
- Helper functions 100% human.
- Lexing functions 50/50 human/bot.
- Parser:
- Datastructures 100% human.
- Helper functions 50/50.
- Parser functions 5/95 human/bot.
- AstGen: TBD.
Testing
Quick test:
./zig-out/bin/zig build fmt-zig0 test-zig0
Full test and static analysis with all supported compilers and valgrind (run before commit, takes a while):
./zig-out/bin/zig build all-zig0 -Dvalgrind
Debugging tips
Test runs infinitely? Build the test program executable:
$ ./zig-out/bin/zig build test-zig0 -Dzig0-no-exec
And then run it, capturing the stack trace:
gdb -batch \
-ex "python import threading; threading.Timer(1.0, lambda: gdb.post_event(lambda: gdb.execute('interrupt'))).start()" \
-ex run \
-ex "bt full" \
-ex quit \
zig-out/bin/test
You are welcome to replace -ex "bt full" with anything other of interest.
Float handling
Float literals are parsed with strtold() (C11 standard, portable). On
x86-64 Linux, long double is 80-bit extended precision (63 fraction bits).
When a float doesn't round-trip through f64, it's emitted as f128 (ZIR
float128 instruction). The 80-bit extended value is converted to IEEE 754
binary128 encoding by bit manipulation — both formats share the same 15-bit
exponent with bias 16383. The top 63 of binary128's 112 fraction bits come
from the 80-bit value; the bottom 49 are zero-padded.
This means float128 literals lose ~49 bits of precision compared to the
upstream Zig implementation (which uses native f128). This is acceptable
because stage0 is a bootstrap tool — the real Zig compiler re-parses all
source with full f128 precision in later stages. The test comparison mask
in astgen_test.zig skips float128 payloads to account for this.
Previous approach used __float128/strtof128 (GCC/glibc extensions) for
full precision, but these are not portable to TCC and other C11 compilers.