Introduce zir.h/zir.c with ZIR instruction types (269 tags, 56 extended opcodes, 8-byte Data union) ported from lib/std/zig/Zir.zig, and astgen.h/astgen.c implementing the empty-container fast path that produces correct ZIR for empty source files. The test infrastructure in astgen_test.zig compares C astGen() output field-by-field against Zig's std.zig.AstGen.generate() using tag-based dispatch, avoiding raw byte comparison since Zig's Data union has no guaranteed in-memory layout. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
123 lines
3.9 KiB
Zig
123 lines
3.9 KiB
Zig
const std = @import("std");
|
|
const Ast = std.zig.Ast;
|
|
const Zir = std.zig.Zir;
|
|
const AstGen = std.zig.AstGen;
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
const c = @cImport({
|
|
@cInclude("astgen.h");
|
|
});
|
|
|
|
test "astgen: empty source" {
|
|
const gpa = std.testing.allocator;
|
|
|
|
const source: [:0]const u8 = "";
|
|
|
|
// Reference: parse and generate ZIR with Zig.
|
|
var tree = try Ast.parse(gpa, source, .zig);
|
|
defer tree.deinit(gpa);
|
|
var ref_zir = try AstGen.generate(gpa, tree);
|
|
defer ref_zir.deinit(gpa);
|
|
|
|
// Test: parse and generate ZIR with C.
|
|
var c_ast = c.astParse(source.ptr, @intCast(source.len));
|
|
defer c.astDeinit(&c_ast);
|
|
var c_zir = c.astGen(&c_ast);
|
|
defer c.zirDeinit(&c_zir);
|
|
|
|
try expectEqualZir(ref_zir, c_zir);
|
|
}
|
|
|
|
fn expectEqualZir(ref: Zir, got: c.Zir) !void {
|
|
// Compare instruction count.
|
|
const ref_len: u32 = @intCast(ref.instructions.len);
|
|
try std.testing.expectEqual(ref_len, got.inst_len);
|
|
|
|
// Compare instructions (tag + data) field-by-field.
|
|
const ref_tags = ref.instructions.items(.tag);
|
|
const ref_datas = ref.instructions.items(.data);
|
|
for (0..ref_len) |i| {
|
|
const ref_tag: u8 = @intFromEnum(ref_tags[i]);
|
|
const got_tag: u8 = @intCast(got.inst_tags[i]);
|
|
if (ref_tag != got_tag) {
|
|
std.debug.print(
|
|
"inst_tags[{d}] mismatch: ref={d} got={d}\n",
|
|
.{ i, ref_tag, got_tag },
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
try expectEqualData(i, ref_tags[i], ref_datas[i], got.inst_datas[i]);
|
|
}
|
|
|
|
// Compare extra data.
|
|
const ref_extra_len: u32 = @intCast(ref.extra.len);
|
|
try std.testing.expectEqual(ref_extra_len, got.extra_len);
|
|
for (0..ref_extra_len) |i| {
|
|
if (ref.extra[i] != got.extra[i]) {
|
|
std.debug.print(
|
|
"extra[{d}] mismatch: ref=0x{x:0>8} got=0x{x:0>8}\n",
|
|
.{ i, ref.extra[i], got.extra[i] },
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
}
|
|
|
|
// Compare string bytes.
|
|
const ref_sb_len: u32 = @intCast(ref.string_bytes.len);
|
|
try std.testing.expectEqual(ref_sb_len, got.string_bytes_len);
|
|
for (0..ref_sb_len) |i| {
|
|
if (ref.string_bytes[i] != got.string_bytes[i]) {
|
|
std.debug.print(
|
|
"string_bytes[{d}] mismatch: ref=0x{x:0>2} got=0x{x:0>2}\n",
|
|
.{ i, ref.string_bytes[i], got.string_bytes[i] },
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Compare a single instruction's data, dispatching by tag.
|
|
/// Zig's Data union has no guaranteed in-memory layout, so we
|
|
/// compare each variant's fields individually.
|
|
fn expectEqualData(
|
|
idx: usize,
|
|
tag: Zir.Inst.Tag,
|
|
ref: Zir.Inst.Data,
|
|
got: c.ZirInstData,
|
|
) !void {
|
|
switch (tag) {
|
|
.extended => {
|
|
const r = ref.extended;
|
|
const g = got.extended;
|
|
if (@intFromEnum(r.opcode) != g.opcode or
|
|
r.small != g.small or
|
|
r.operand != g.operand)
|
|
{
|
|
std.debug.print(
|
|
"inst_datas[{d}] (extended) mismatch:\n" ++
|
|
" ref: opcode={d} small=0x{x:0>4} operand={d}\n" ++
|
|
" got: opcode={d} small=0x{x:0>4} operand={d}\n",
|
|
.{
|
|
idx,
|
|
@intFromEnum(r.opcode),
|
|
r.small,
|
|
r.operand,
|
|
g.opcode,
|
|
g.small,
|
|
g.operand,
|
|
},
|
|
);
|
|
return error.TestExpectedEqual;
|
|
}
|
|
},
|
|
// Add more tag handlers as AstGen implementation grows.
|
|
else => {
|
|
std.debug.print(
|
|
"inst_datas[{d}]: unhandled tag {d} in comparison\n",
|
|
.{ idx, @intFromEnum(tag) },
|
|
);
|
|
return error.TestUnexpectedResult;
|
|
},
|
|
}
|
|
}
|