| 1 | //! For testing the core compiler with minimal feature and |
| 2 | //! additional debugging parameters. |
| 3 | |
| 4 | const config = @import("config"); |
| 5 | |
| 6 | const 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 = 't', .name = "--tests", .desc = "Build unit tests instead of the main function" }, |
| 12 | .{ .short = 'L', .name = "--no-leak-checks", .desc = "" }, |
| 13 | }, |
| 14 | .positionals = .single, |
| 15 | .help = .{ |
| 16 | .usage = "[options...] [entry file]", |
| 17 | }, |
| 18 | }; |
| 19 | |
| 20 | pub fn main() !u8 { |
| 21 | try tracer.init(); |
| 22 | defer tracer.deinit(); |
| 23 | try tracer.init_thread(null); |
| 24 | defer tracer.deinit_thread(); |
| 25 | |
| 26 | // unicode props is asserted to be initialized before core.Tokenizer is used |
| 27 | // this will result in a static page allocation, which is never cleaned up. |
| 28 | try core.unicode_props.init(); |
| 29 | |
| 30 | const use_gpa = builtin.mode == .Debug; |
| 31 | var gpa = if (use_gpa) |
| 32 | std.heap.GeneralPurposeAllocator(.{ |
| 33 | .stack_trace_frames = 100, |
| 34 | }){} |
| 35 | else |
| 36 | std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 37 | var no_leak_checks = false; |
| 38 | defer _ = if (use_gpa and !no_leak_checks) gpa.deinit(); |
| 39 | const alloc = gpa.allocator(); |
| 40 | |
| 41 | // Current working directory is required for CLI resolution. |
| 42 | var cwd_buf: [std.fs.max_path_bytes]u8 = undefined; |
| 43 | const cwd = try std.posix.getcwd(&cwd_buf); |
| 44 | |
| 45 | // Command line argument parsing. |
| 46 | const program_name = "zig build compiler --"; |
| 47 | _, const args = try Cli.programArgsAlloc(alloc, program_name); |
| 48 | defer Cli.programArgsFree(alloc, args); |
| 49 | const opts = arg_config.parse(program_name, args); |
| 50 | |
| 51 | if (opts.report_trace == true) Report.debug_show_report_trace = true; |
| 52 | |
| 53 | const file_path = try std.fs.path.resolve(alloc, &.{ |
| 54 | cwd, |
| 55 | opts.positionals orelse |
| 56 | arg_config.showHelpAndExit(program_name), |
| 57 | }); |
| 58 | defer alloc.free(file_path); |
| 59 | |
| 60 | no_leak_checks = opts.no_leak_checks orelse false; |
| 61 | |
| 62 | // Execute compiler |
| 63 | { |
| 64 | var js_backend = core.Js{}; |
| 65 | |
| 66 | var comp = try core.Comp.init(.{ |
| 67 | .gpa = tracer.tracingAllocator(&alloc), |
| 68 | .options = .{ |
| 69 | .dump_ir = opts.dump_ir orelse false, |
| 70 | }, |
| 71 | .backend = .{ .js = &js_backend }, |
| 72 | }); |
| 73 | defer comp.deinit(); |
| 74 | |
| 75 | defer if (opts.dump_state) |path| { |
| 76 | dumpState(&comp, path) catch |err| { |
| 77 | std.debug.print("Failed to dump compilation state: {}", .{err}); |
| 78 | }; |
| 79 | }; |
| 80 | |
| 81 | const main_file = try comp.getFile(file_path); |
| 82 | |
| 83 | var fail_report: Report = undefined; |
| 84 | const hir = main_file.getHir(&comp, &fail_report) catch |err| switch (err) { |
| 85 | error.TransformFail => { |
| 86 | std.debug.print("Transform failed:\n", .{}); |
| 87 | try fail_report.dump(.initComp(&comp, main_file), comp.out); |
| 88 | fail_report.deinit(comp.gpa); |
| 89 | return 1; |
| 90 | }, |
| 91 | error.ParseFail => { |
| 92 | // Already printed. |
| 93 | return 1; |
| 94 | }, |
| 95 | else => return err, |
| 96 | }; |
| 97 | |
| 98 | if (comp.options.dump_ir) { |
| 99 | const stderr = std.io.getStdErr().writer(); |
| 100 | try hir.dumpToWriter(stderr, null); |
| 101 | } |
| 102 | |
| 103 | const entry_point = try comp.enqueueEntryModule(main_file); |
| 104 | comp.runAllJobs() catch |err| switch (err) { |
| 105 | error.AnalysisFail => { |
| 106 | // error is already printed |
| 107 | return 1; |
| 108 | }, |
| 109 | else => return err, |
| 110 | }; |
| 111 | |
| 112 | { |
| 113 | // Output to standard out |
| 114 | const output = std.io.getStdOut().writer(); |
| 115 | var bufw = std.io.bufferedWriter(output); |
| 116 | const writer = bufw.writer(); |
| 117 | try js_backend.link(&comp, entry_point, writer.any()); |
| 118 | try bufw.flush(); |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | std.process.cleanExit(); |
| 123 | return 0; |
| 124 | } |
| 125 | |
| 126 | const core = @import("core"); |
| 127 | const Comp = core.Comp; |
| 128 | const Report = core.Report; |
| 129 | |
| 130 | const tracer = @import("tracer"); |
| 131 | pub const tracer_impl = @field(tracer, @tagName(@import("config").tracing_mode)); |
| 132 | |
| 133 | const std = @import("std"); |
| 134 | const builtin = @import("builtin"); |
| 135 | const Cli = @import("Cli"); |
| 136 | |
| 137 | fn dumpState(comp: *const Comp, dump_path: []const u8) !void { |
| 138 | const file = try std.fs.cwd().createFile(dump_path, .{}); |
| 139 | defer file.close(); |
| 140 | |
| 141 | var bufw = std.io.bufferedWriter(file.writer()); |
| 142 | var jw = std.json.writeStream(bufw.writer(), .{ |
| 143 | .whitespace = .indent_2, |
| 144 | }); |
| 145 | |
| 146 | try comp.jsonStringify(&jw); |
| 147 | try bufw.writer().writeAll("\n"); |
| 148 | try bufw.flush(); |
| 149 | } |