diff --git a/src/cli.zig b/src/cli.zig
index fd981159f46ab157af9a83a7a403f460203ea21c..92b3ac8c447191afa673912d7e915f0461f25114 100644
--- a/src/cli.zig
+++ b/src/cli.zig
@@ -8,7 +8,7 @@ options: []const Option = &.{},
positionals: Positionals = .none,
help: HelpConfig = .{},
-const Option = struct {
+pub const Option = struct {
/// Should be formatted as the flag name: '--dump-ir'
name: []const u8,
/// Displayed in generated help menu
@@ -32,6 +32,10 @@ const Option = struct {
}
};
+ pub fn optional(comptime cond: bool, opts: []const Option) []const Option {
+ return if (cond) opts else &.{};
+ }
+
const help: Option = .{
.name = "--help",
.short = 'h',
diff --git a/src/cli_compiler.zig b/src/cli_compiler.zig
new file mode 100644
index 0000000000000000000000000000000000000000..9f5fbd66dc895351e25461f0fe33bf29e7506942
--- /dev/null
+++ b/src/cli_compiler.zig
@@ -0,0 +1,145 @@
+//! For testing the core compiler with minimal feature and
+//! additional debugging parameters.
+
+const config = @import("config");
+
+const arg_config: Cli = .{
+ .options = &.{
+ .{ .short = 'i', .name = "--dump-ir", .desc = "Dump intermediate representations" },
+ .{ .name = "--dump-state", .desc = "Dump compiler state to JSON", .kind = .string },
+ .{ .short = 'r', .name = "--report-trace", .desc = "Print stack traces to errors" },
+ .{ .short = 'L', .name = "--no-leak-checks", .desc = "" },
+ },
+ .positionals = .single,
+ .help = .{
+ .usage = "[options...] [entry file]",
+ },
+};
+
+pub fn main() !u8 {
+ try tracer.init();
+ defer tracer.deinit();
+ try tracer.init_thread(null);
+ defer tracer.deinit_thread();
+
+ // unicode props is asserted to be initialized before core.Tokenizer is used
+ // this will result in a static page allocation, which is never cleaned up.
+ try core.unicode_props.init();
+
+ const use_gpa = builtin.mode == .Debug;
+ var gpa = if (use_gpa)
+ std.heap.GeneralPurposeAllocator(.{
+ .stack_trace_frames = 100,
+ }){}
+ else
+ std.heap.ArenaAllocator.init(std.heap.page_allocator);
+ var no_leak_checks = false;
+ defer _ = if (use_gpa and !no_leak_checks) gpa.deinit();
+ const alloc = gpa.allocator();
+
+ // Current working directory is required for CLI resolution.
+ var cwd_buf: [std.fs.max_path_bytes]u8 = undefined;
+ const cwd = try std.posix.getcwd(&cwd_buf);
+
+ // Command line argument parsing.
+ const program_name = "zig build compiler --";
+ _, const args = try Cli.programArgsAlloc(alloc, program_name);
+ const opts = arg_config.parse(program_name, args);
+
+ const file_path = try std.fs.path.resolve(alloc, &.{
+ cwd,
+ opts.positionals orelse
+ arg_config.showHelpAndExit(program_name),
+ });
+ defer alloc.free(file_path);
+
+ no_leak_checks = opts.no_leak_checks orelse false;
+
+ // Execute compiler
+ {
+ var js_backend = core.Js{};
+
+ var comp = try core.Comp.init(.{
+ .gpa = tracer.tracingAllocator(&alloc),
+ .options = .{
+ .dump_ir = opts.dump_ir orelse false,
+ },
+ .backend = .{ .js = &js_backend },
+ });
+ defer comp.deinit();
+
+ defer if (opts.dump_state) |path| {
+ dumpState(&comp, path) catch |err| {
+ std.debug.print("Failed to dump compilation state: {}", .{err});
+ };
+ };
+
+ const main_file = try comp.getFile(file_path);
+
+ var fail_report: Report = undefined;
+ const teg = main_file.getTeg(&comp, &fail_report) catch |err| switch (err) {
+ error.TransformFail => {
+ std.debug.print("Transform failed:\n", .{});
+ try fail_report.dump(.initComp(&comp, main_file), comp.out);
+ fail_report.deinit(comp.gpa);
+ return 1;
+ },
+ error.ParseFail => {
+ // Already printed.
+ return 1;
+ },
+ else => return err,
+ };
+
+ if (comp.options.dump_ir) {
+ const stderr = std.io.getStdErr().writer();
+ try teg.dumpToWriter(stderr, null);
+ }
+
+ const entry_point = try comp.enqueueEntryModule(main_file);
+ comp.runAllJobs() catch |err| switch (err) {
+ error.AnalysisFail => {
+ // error is already printed
+ return 1;
+ },
+ else => return err,
+ };
+
+ {
+ // Output to standard out
+ const output = std.io.getStdOut().writer();
+ var bufw = std.io.bufferedWriter(output);
+ const writer = bufw.writer();
+ try js_backend.link(&comp, entry_point, writer.any());
+ try bufw.flush();
+ }
+ }
+
+ std.process.cleanExit();
+ return 0;
+}
+
+const core = @import("core");
+const Comp = core.Comp;
+const Report = core.Report;
+
+const tracer = @import("tracer");
+pub const tracer_impl = @field(tracer, @tagName(@import("config").tracing_mode));
+
+const std = @import("std");
+const builtin = @import("builtin");
+const Cli = @import("Cli.zig");
+
+fn dumpState(comp: *const Comp, dump_path: []const u8) !void {
+ const file = try std.fs.cwd().createFile(dump_path, .{});
+ defer file.close();
+
+ var bufw = std.io.bufferedWriter(file.writer());
+ var jw = std.json.writeStream(bufw.writer(), .{
+ .whitespace = .indent_2,
+ });
+
+ try comp.jsonStringify(&jw);
+ try bufw.writer().writeAll("\n");
+ try bufw.flush();
+}
diff --git a/src/cli_transform.zig b/src/cli_transform.zig
index 44920293e9b2d00e4fbde53476d3b694c332b957..76200d6457a22fefd34076def4e44cfe3c58c97e 100644
--- a/src/cli_transform.zig
+++ b/src/cli_transform.zig
@@ -25,8 +25,7 @@ pub fn main() !u8 {
const program_name, const raw_args = try Cli.programArgsAlloc(alloc, "transform");
defer Cli.programArgsFree(alloc, raw_args);
- const args = arg_config.parse(raw_args) catch
- arg_config.showHelpAndExit(program_name);
+ const args = arg_config.parse(program_name, raw_args);
const file_path = args.positionals orelse
arg_config.fatalWithHelp(program_name, "missing filepath argument\n", .{});
diff --git a/src/main.zig b/src/main.zig
index ebbe1e8800327ce7b1a243bec9b8b4780c79dd1d..797569df9ccb3d4ba6dbd65fa0fbe92bd1ace20d 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -4,9 +4,18 @@
//! for example, test the `core` module's compilation features without
//! needing to compile the formatter.
-const have_debug_features = builtin.mode == .Debug;
const config = @import("config");
+const arg_config: Cli = .{
+ .options = .{
+ .{ .short = 'i', .name = "--dump-ir", .desc = "Dump intermediate representations" },
+ .{ .name = "--dump-state", .desc = "Dump compiler state to JSON", .kind = .string },
+ } ++ Cli.Option.optional(config.have_embedded_quickjs, &.{
+ .{ .name = "--run", .desc = "Execute with QuickJS" },
+ }),
+ .positionals = .single,
+};
+
pub fn main() !u8 {
try tracer.init();
defer tracer.deinit();
@@ -24,8 +33,7 @@ pub fn main() !u8 {
}){}
else
std.heap.ArenaAllocator.init(std.heap.page_allocator);
- var no_leak_checks = false;
- defer _ = if (use_gpa and !no_leak_checks) gpa.deinit();
+ defer _ = if (use_gpa) gpa.deinit();
const alloc = gpa.allocator();
// Current working directory is required for CLI resolution.
@@ -33,95 +41,16 @@ pub fn main() !u8 {
const cwd = try std.posix.getcwd(&cwd_buf);
// Command line argument parsing.
- var maybe_file_path: ?[]const u8 = null;
- defer if (maybe_file_path) |path| alloc.free(path);
- var exe_name: ?[]const u8 = null;
- defer if (exe_name) |path| alloc.free(path);
- var dump_ir = false;
- var dump_state: ?[]const u8 = null;
- var run = false;
- var has_errors = false;
- {
- var args = try std.process.argsWithAllocator(alloc);
- defer args.deinit();
-
- exe_name = try alloc.dupe(u8, std.fs.path.basename(args.next() orelse "cli"));
-
- while (args.next()) |arg| {
- if (std.mem.startsWith(u8, arg, "-")) {
- if (have_debug_features) {
- if (std.mem.eql(u8, arg, "--report-trace")) {
- core.Report.debug_show_report_trace = true;
- continue;
- }
- if (std.mem.eql(u8, arg, "--no-leak-checks")) {
- no_leak_checks = true;
- continue;
- }
- }
-
- if (std.mem.eql(u8, arg, "--dump-ir")) {
- dump_ir = true;
- continue;
- }
-
- if (std.mem.startsWith(u8, arg, "--dump-state=")) {
- dump_state = arg["--dump-state=".len..];
- continue;
- }
-
- if (config.have_embedded_quickjs and std.mem.eql(u8, arg, "--run")) {
- run = true;
- continue;
- }
-
- std.debug.print("error: unknown flag '{s}'\n", .{arg});
- has_errors = true;
- break;
- }
-
- if (maybe_file_path == null) {
- maybe_file_path = try std.fs.path.resolve(alloc, &.{ cwd, arg });
- } else {
- std.debug.print("error: multiple files specified\n", .{});
- maybe_file_path = null;
- break;
- }
- }
- }
-
- if (maybe_file_path == null) has_errors = true;
- if (has_errors) {
- std.debug.print("Usage: {s} [flags] \n", .{exe_name.?});
- {
- std.debug.print(
- // | 60 chars wrap point|
- \\
- \\Flags:
- \\ --dump-ir Dump intermediate representations
- \\ --dump-state= Dump compiler state to JSON
- \\
- , .{});
- }
- if (config.have_embedded_quickjs) {
- std.debug.print(
- \\ --run Run program using QuickJS
- \\
- , .{});
- }
- if (have_debug_features) {
- std.debug.print(
- \\
- \\Debug Mode Flags:
- \\ --report-trace Print stack traces per error
- \\ --no-leak-checks
- \\
- , .{});
- }
- std.debug.print("\nLanguage SDK v" ++ config.version_string ++ "\n", .{});
- return 1;
- }
- const file_path = maybe_file_path.?;
+ const program_name = "zig build compiler --";
+ _, const args = try Cli.programArgsAlloc(alloc, program_name);
+ const opts = arg_config.parse(program_name, args);
+
+ const file_path = try std.fs.path.resolve(alloc, &.{
+ cwd,
+ opts.positionals orelse
+ arg_config.showHelpAndExit(program_name),
+ });
+ defer alloc.free(file_path);
// Execute compiler
{
@@ -130,13 +59,13 @@ pub fn main() !u8 {
var comp = try core.Comp.init(.{
.gpa = tracer.tracingAllocator(&alloc),
.options = .{
- .dump_ir = dump_ir,
+ .dump_ir = opts.dump_ir orelse false,
},
.backend = .{ .js = &js_backend },
});
defer comp.deinit();
- defer if (dump_state) |path| {
+ defer if (opts.dump_state) |path| {
dumpState(&comp, path) catch |err| {
std.debug.print("Failed to dump compilation state: {}", .{err});
};
@@ -173,7 +102,7 @@ pub fn main() !u8 {
else => return err,
};
- if (config.have_embedded_quickjs and run) {
+ if (config.have_embedded_quickjs and (opts.run orelse false)) {
// Execute with QuickJS
var out: std.ArrayListUnmanaged(u8) = .{};
defer out.deinit(alloc);
@@ -244,6 +173,7 @@ pub const tracer_impl = @field(tracer, @tagName(@import("config").tracing_mode))
const std = @import("std");
const builtin = @import("builtin");
+const Cli = @import("Cli.zig");
fn dumpState(comp: *const Comp, dump_path: []const u8) !void {
const file = try std.fs.cwd().createFile(dump_path, .{});