authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-09-14 20:21:19-04:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-09-14 20:21:19-04:00
loge52a575edb42a6339b121ed77629b3874d1fe8f4
tree61799ec08d57af7449ca48b4361e49686dfa1f91
parent03d5aa6a575bcd5237473b8f87d273504e6ed4b9
signature Commit is signed but in an unrecognized format.

chore: rework some CLI programs to use Cli.zig


4 files changed, 176 insertions(+), 98 deletions(-)

src/cli.zig+5-1
......@@ -8,7 +8,7 @@ options: []const Option = &.{},
88positionals: Positionals = .none,
99help: HelpConfig = .{},
1010
11const Option = struct {
11pub const Option = struct {
1212 /// Should be formatted as the flag name: '--dump-ir'
1313 name: []const u8,
1414 /// Displayed in generated help menu
......@@ -32,6 +32,10 @@ const Option = struct {
3232 }
3333 };
3434
35 pub fn optional(comptime cond: bool, opts: []const Option) []const Option {
36 return if (cond) opts else &.{};
37 }
38
3539 const help: Option = .{
3640 .name = "--help",
3741 .short = 'h',
src/cli_compiler.zig created+145
......@@ -0,0 +1,145 @@
1//! For testing the core compiler with minimal feature and
2//! additional debugging parameters.
3
4const config = @import("config");
5
6const arg_config: Cli = .{
7 .options = &.{
8 .{ .short = 'i', .name = "--dump-ir", .desc = "Dump intermediate representations" },
9 .{ .name = "--dump-state", .desc = "Dump compiler state to JSON", .kind = .string },
10 .{ .short = 'r', .name = "--report-trace", .desc = "Print stack traces to errors" },
11 .{ .short = 'L', .name = "--no-leak-checks", .desc = "" },
12 },
13 .positionals = .single,
14 .help = .{
15 .usage = "[options...] [entry file]",
16 },
17};
18
19pub fn main() !u8 {
20 try tracer.init();
21 defer tracer.deinit();
22 try tracer.init_thread(null);
23 defer tracer.deinit_thread();
24
25 // unicode props is asserted to be initialized before core.Tokenizer is used
26 // this will result in a static page allocation, which is never cleaned up.
27 try core.unicode_props.init();
28
29 const use_gpa = builtin.mode == .Debug;
30 var gpa = if (use_gpa)
31 std.heap.GeneralPurposeAllocator(.{
32 .stack_trace_frames = 100,
33 }){}
34 else
35 std.heap.ArenaAllocator.init(std.heap.page_allocator);
36 var no_leak_checks = false;
37 defer _ = if (use_gpa and !no_leak_checks) gpa.deinit();
38 const alloc = gpa.allocator();
39
40 // Current working directory is required for CLI resolution.
41 var cwd_buf: [std.fs.max_path_bytes]u8 = undefined;
42 const cwd = try std.posix.getcwd(&cwd_buf);
43
44 // Command line argument parsing.
45 const program_name = "zig build compiler --";
46 _, const args = try Cli.programArgsAlloc(alloc, program_name);
47 const opts = arg_config.parse(program_name, args);
48
49 const file_path = try std.fs.path.resolve(alloc, &.{
50 cwd,
51 opts.positionals orelse
52 arg_config.showHelpAndExit(program_name),
53 });
54 defer alloc.free(file_path);
55
56 no_leak_checks = opts.no_leak_checks orelse false;
57
58 // Execute compiler
59 {
60 var js_backend = core.Js{};
61
62 var comp = try core.Comp.init(.{
63 .gpa = tracer.tracingAllocator(&alloc),
64 .options = .{
65 .dump_ir = opts.dump_ir orelse false,
66 },
67 .backend = .{ .js = &js_backend },
68 });
69 defer comp.deinit();
70
71 defer if (opts.dump_state) |path| {
72 dumpState(&comp, path) catch |err| {
73 std.debug.print("Failed to dump compilation state: {}", .{err});
74 };
75 };
76
77 const main_file = try comp.getFile(file_path);
78
79 var fail_report: Report = undefined;
80 const teg = main_file.getTeg(&comp, &fail_report) catch |err| switch (err) {
81 error.TransformFail => {
82 std.debug.print("Transform failed:\n", .{});
83 try fail_report.dump(.initComp(&comp, main_file), comp.out);
84 fail_report.deinit(comp.gpa);
85 return 1;
86 },
87 error.ParseFail => {
88 // Already printed.
89 return 1;
90 },
91 else => return err,
92 };
93
94 if (comp.options.dump_ir) {
95 const stderr = std.io.getStdErr().writer();
96 try teg.dumpToWriter(stderr, null);
97 }
98
99 const entry_point = try comp.enqueueEntryModule(main_file);
100 comp.runAllJobs() catch |err| switch (err) {
101 error.AnalysisFail => {
102 // error is already printed
103 return 1;
104 },
105 else => return err,
106 };
107
108 {
109 // Output to standard out
110 const output = std.io.getStdOut().writer();
111 var bufw = std.io.bufferedWriter(output);
112 const writer = bufw.writer();
113 try js_backend.link(&comp, entry_point, writer.any());
114 try bufw.flush();
115 }
116 }
117
118 std.process.cleanExit();
119 return 0;
120}
121
122const core = @import("core");
123const Comp = core.Comp;
124const Report = core.Report;
125
126const tracer = @import("tracer");
127pub const tracer_impl = @field(tracer, @tagName(@import("config").tracing_mode));
128
129const std = @import("std");
130const builtin = @import("builtin");
131const Cli = @import("Cli.zig");
132
133fn dumpState(comp: *const Comp, dump_path: []const u8) !void {
134 const file = try std.fs.cwd().createFile(dump_path, .{});
135 defer file.close();
136
137 var bufw = std.io.bufferedWriter(file.writer());
138 var jw = std.json.writeStream(bufw.writer(), .{
139 .whitespace = .indent_2,
140 });
141
142 try comp.jsonStringify(&jw);
143 try bufw.writer().writeAll("\n");
144 try bufw.flush();
145}
src/cli_transform.zig+1-2
......@@ -25,8 +25,7 @@ pub fn main() !u8 {
2525 const program_name, const raw_args = try Cli.programArgsAlloc(alloc, "transform");
2626 defer Cli.programArgsFree(alloc, raw_args);
2727
28 const args = arg_config.parse(raw_args) catch
29 arg_config.showHelpAndExit(program_name);
28 const args = arg_config.parse(program_name, raw_args);
3029
3130 const file_path = args.positionals orelse
3231 arg_config.fatalWithHelp(program_name, "missing filepath argument\n", .{});
src/main.zig+25-95
......@@ -4,9 +4,18 @@
44//! for example, test the `core` module's compilation features without
55//! needing to compile the formatter.
66
7const have_debug_features = builtin.mode == .Debug;
87const config = @import("config");
98
9const arg_config: Cli = .{
10 .options = .{
11 .{ .short = 'i', .name = "--dump-ir", .desc = "Dump intermediate representations" },
12 .{ .name = "--dump-state", .desc = "Dump compiler state to JSON", .kind = .string },
13 } ++ Cli.Option.optional(config.have_embedded_quickjs, &.{
14 .{ .name = "--run", .desc = "Execute with QuickJS" },
15 }),
16 .positionals = .single,
17};
18
1019pub fn main() !u8 {
1120 try tracer.init();
1221 defer tracer.deinit();
......@@ -24,8 +33,7 @@ pub fn main() !u8 {
2433 }){}
2534 else
2635 std.heap.ArenaAllocator.init(std.heap.page_allocator);
27 var no_leak_checks = false;
28 defer _ = if (use_gpa and !no_leak_checks) gpa.deinit();
36 defer _ = if (use_gpa) gpa.deinit();
2937 const alloc = gpa.allocator();
3038
3139 // Current working directory is required for CLI resolution.
......@@ -33,95 +41,16 @@ pub fn main() !u8 {
3341 const cwd = try std.posix.getcwd(&cwd_buf);
3442
3543 // Command line argument parsing.
36 var maybe_file_path: ?[]const u8 = null;
37 defer if (maybe_file_path) |path| alloc.free(path);
38 var exe_name: ?[]const u8 = null;
39 defer if (exe_name) |path| alloc.free(path);
40 var dump_ir = false;
41 var dump_state: ?[]const u8 = null;
42 var run = false;
43 var has_errors = false;
44 {
45 var args = try std.process.argsWithAllocator(alloc);
46 defer args.deinit();
47
48 exe_name = try alloc.dupe(u8, std.fs.path.basename(args.next() orelse "cli"));
49
50 while (args.next()) |arg| {
51 if (std.mem.startsWith(u8, arg, "-")) {
52 if (have_debug_features) {
53 if (std.mem.eql(u8, arg, "--report-trace")) {
54 core.Report.debug_show_report_trace = true;
55 continue;
56 }
57 if (std.mem.eql(u8, arg, "--no-leak-checks")) {
58 no_leak_checks = true;
59 continue;
60 }
61 }
62
63 if (std.mem.eql(u8, arg, "--dump-ir")) {
64 dump_ir = true;
65 continue;
66 }
67
68 if (std.mem.startsWith(u8, arg, "--dump-state=")) {
69 dump_state = arg["--dump-state=".len..];
70 continue;
71 }
72
73 if (config.have_embedded_quickjs and std.mem.eql(u8, arg, "--run")) {
74 run = true;
75 continue;
76 }
77
78 std.debug.print("error: unknown flag '{s}'\n", .{arg});
79 has_errors = true;
80 break;
81 }
82
83 if (maybe_file_path == null) {
84 maybe_file_path = try std.fs.path.resolve(alloc, &.{ cwd, arg });
85 } else {
86 std.debug.print("error: multiple files specified\n", .{});
87 maybe_file_path = null;
88 break;
89 }
90 }
91 }
92
93 if (maybe_file_path == null) has_errors = true;
94 if (has_errors) {
95 std.debug.print("Usage: {s} [flags] <input file>\n", .{exe_name.?});
96 {
97 std.debug.print(
98 // | 60 chars wrap point|
99 \\
100 \\Flags:
101 \\ --dump-ir Dump intermediate representations
102 \\ --dump-state=<path> Dump compiler state to JSON
103 \\
104 , .{});
105 }
106 if (config.have_embedded_quickjs) {
107 std.debug.print(
108 \\ --run Run program using QuickJS
109 \\
110 , .{});
111 }
112 if (have_debug_features) {
113 std.debug.print(
114 \\
115 \\Debug Mode Flags:
116 \\ --report-trace Print stack traces per error
117 \\ --no-leak-checks
118 \\
119 , .{});
120 }
121 std.debug.print("\nLanguage SDK v" ++ config.version_string ++ "\n", .{});
122 return 1;
123 }
124 const file_path = maybe_file_path.?;
44 const program_name = "zig build compiler --";
45 _, const args = try Cli.programArgsAlloc(alloc, program_name);
46 const opts = arg_config.parse(program_name, args);
47
48 const file_path = try std.fs.path.resolve(alloc, &.{
49 cwd,
50 opts.positionals orelse
51 arg_config.showHelpAndExit(program_name),
52 });
53 defer alloc.free(file_path);
12554
12655 // Execute compiler
12756 {
......@@ -130,13 +59,13 @@ pub fn main() !u8 {
13059 var comp = try core.Comp.init(.{
13160 .gpa = tracer.tracingAllocator(&alloc),
13261 .options = .{
133 .dump_ir = dump_ir,
62 .dump_ir = opts.dump_ir orelse false,
13463 },
13564 .backend = .{ .js = &js_backend },
13665 });
13766 defer comp.deinit();
13867
139 defer if (dump_state) |path| {
68 defer if (opts.dump_state) |path| {
14069 dumpState(&comp, path) catch |err| {
14170 std.debug.print("Failed to dump compilation state: {}", .{err});
14271 };
......@@ -173,7 +102,7 @@ pub fn main() !u8 {
173102 else => return err,
174103 };
175104
176 if (config.have_embedded_quickjs and run) {
105 if (config.have_embedded_quickjs and (opts.run orelse false)) {
177106 // Execute with QuickJS
178107 var out: std.ArrayListUnmanaged(u8) = .{};
179108 defer out.deinit(alloc);
......@@ -244,6 +173,7 @@ pub const tracer_impl = @field(tracer, @tagName(@import("config").tracing_mode))
244173
245174const std = @import("std");
246175const builtin = @import("builtin");
176const Cli = @import("Cli.zig");
247177
248178fn dumpState(comp: *const Comp, dump_path: []const u8) !void {
249179 const file = try std.fs.cwd().createFile(dump_path, .{});