diff --git a/build.zig b/build.zig index 1e59d84658b370ad64a9b6db381ce4f137ea0155..385d4d5b7e0288d79a07c4c2c314cb7e6bc30c5d 100644 --- a/build.zig +++ b/build.zig @@ -720,18 +720,27 @@ pub fn build(b: *std.Build) !void { check_mingw_step.dependOn(&check_mingw_run.step); { - const gpo_step = b.step("gen-parser-oracle", "Regenerate lib/std/zig/parser_generated_oracle.zig from doc/langref/grammar.peg"); - const gpo_exe = b.addExecutable(.{ + const gen_oracle_exe = b.addExecutable(.{ .name = "gen_parser_oracle", .root_module = b.createModule(.{ .root_source_file = b.path("tools/gen_parser_oracle.zig"), .target = b.graph.host, }), }); - const gpo_run = b.addRunArtifact(gpo_exe); - gpo_run.addFileArg(b.path("doc/langref/grammar.peg")); - gpo_run.addFileArg(b.path("lib/std/zig/parser_generated_oracle.zig")); - gpo_step.dependOn(&gpo_run.step); + + const gen_oracle_step = b.step("gen-parser-oracle", "Regenerate lib/std/zig/parser_generated_oracle.zig from doc/langref/grammar.peg"); + const gen_oracle_run = b.addRunArtifact(gen_oracle_exe); + gen_oracle_run.addFileArg(b.path("doc/langref/grammar.peg")); + gen_oracle_run.addFileArg(b.path("lib/std/zig/parser_generated_oracle.zig")); + gen_oracle_step.dependOn(&gen_oracle_run.step); + + const check_oracle_step = b.step("check-parser-oracle", "Check if doc/langref/grammar.peg was modified without regenerating the oracle"); + const check_oracle_run = b.addRunArtifact(gen_oracle_exe); + check_oracle_run.addFileArg(b.path("doc/langref/grammar.peg")); + check_oracle_run.addFileArg(b.path("lib/std/zig/parser_generated_oracle.zig")); + check_oracle_run.addArg("--check"); + check_oracle_step.dependOn(&check_oracle_run.step); + test_step.dependOn(check_oracle_step); } const test_incremental_step = b.step("test-incremental", "Run the incremental compilation test cases"); diff --git a/tools/gen_parser_oracle.zig b/tools/gen_parser_oracle.zig index a5bad1238ec74748d654e92b962ef7fa360da609..fafed31eb9eeea1fa0cf69fe9a75374a7bd78b6d 100644 --- a/tools/gen_parser_oracle.zig +++ b/tools/gen_parser_oracle.zig @@ -1,5 +1,6 @@ //! Example usage: //! zig build gen-parser-oracle +//! zig build check-parser-oracle // This program implements a subset of the PEG grammar definition // in the peg(1) man page. @@ -22,6 +23,7 @@ pub fn main(init: std.process.Init) !void { const grammar_path = args[1]; const out_path = args[2]; + const check = args.len > 3 and mem.eql(u8, args[3], "--check"); const grammar = try Io.Dir.cwd().readFileAlloc(io, grammar_path, gpa, .unlimited); defer gpa.free(grammar); @@ -53,13 +55,27 @@ pub fn main(init: std.process.Init) !void { return error.ParseError; } - var out_file = try Io.Dir.cwd().createFile(io, out_path, .{}); - var out_buffer: [4096]u8 = undefined; - var out_writer = out_file.writer(io, &out_buffer); - const out = &out_writer.interface; + if (check) { + const current = try Io.Dir.cwd().readFileAlloc(io, out_path, gpa, .unlimited); + defer gpa.free(current); + var aw: Io.Writer.Allocating = .init(gpa); + defer aw.deinit(); + try tree.render(gpa, &aw.writer, .{}); + if (!mem.eql(u8, current, aw.written())) { + std.log.err("grammar.peg modified without regenerating oracle", .{}); + std.log.info("Run zig build gen-parser-oracle to regenerate", .{}); + std.process.exit(1); + } + } else { + var out_file = try Io.Dir.cwd().createFile(io, out_path, .{}); + defer out_file.close(io); + var out_buffer: [4096]u8 = undefined; + var out_writer = out_file.writer(io, &out_buffer); + const out = &out_writer.interface; - try tree.render(gpa, out, .{}); - try out.flush(); + try tree.render(gpa, out, .{}); + try out.flush(); + } } const Generator = struct {