const std = @import("std"); const Parser = @import("parser.zig"); const Lexer = @import("lexer.zig").Lexer; const ast = @import("ast.zig"); const Errors = @import("error.zig").Errors; const Interpreter = @import("Interpreter.zig"); const Prelude = @import("Prelude.zig"); const Modules = @import("Modules.zig"); const TypeContext = @import("TypeContext.zig"); const common = @import("common.zig"); const Str = common.Str; const Args = @import("Args.zig"); const Module = @import("Module.zig"); const mono = @import("mono.zig"); const VM = @import("mono/bytecode.zig"); const Bytecode = VM.Mono; const C = @import("mono/c.zig"); pub fn main() !void { // SETUP var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const al = gpa.allocator(); defer { const deinit_status = gpa.deinit(); // this prints all the leaks std.debug.print("gpa deinit status = {}\n", .{deinit_status}); } // global allocator for STUFF var arena = std.heap.ArenaAllocator.init(al); defer arena.deinit(); const aa = arena.allocator(); // PARSE ARGS const opts = try Args.parse(std.process.args(), aa); const compilationStartTime = try std.time.Instant.now(); var modules = try preloadModules(&opts, aa); const fileonly = std.fs.path.basename(opts.filename); _ = try compileFile(&modules, fileonly); const compilationTime = std.time.Instant.since(try std.time.Instant.now(), compilationStartTime) / std.time.ns_per_ms; std.debug.print("=== compilation time: {}ms ===\n", .{compilationTime}); // context setup var fakeNewline: bool = undefined; const fakeHackCtx = ast.Ctx.init(&fakeNewline, modules.typeContext); fakeNewline = false; // SIKE (but obv. temporary) if (!opts.hideErrors) { for (modules.errors.items) |err| { err.err.print(fakeHackCtx, err.module); } } else { if (modules.errors.items.len > 0) { std.debug.print("Hidden {} errors.\n", .{modules.errors.items.len}); } } const moduleAST = modules.getAST(); if (modules.errors.items.len > 0) return; // go and interpret if (opts.backend) |backend| { switch (backend) { .c => { // mono it // var backend = Bytecode.Backend.init(aa, modules.typeContext); var cbackend = C.init(aa, modules.typeContext); try C.Mono.mono(moduleAST, modules.getRoots(), &modules.prelude.?, modules.typeContext, &cbackend, aa, true); const outname = opts.exeName orelse std.fs.path.stem(opts.filename); var stdoutbuf = std.io.bufferedWriter(std.io.getStdOut().writer()); const stdout = stdoutbuf.writer(); const ccomp = try compileC(aa, &cbackend, outname, null, stdout); try stdoutbuf.flush(); if (opts.printAST or opts.printRootAST) { try cbackend.writeTo(stdout); } try stdoutbuf.flush(); std.debug.print("=== writing and compiling (C) time: {}ms ===\n", .{ccomp.time}); if (ccomp.succeeded and !opts.dontCompile and !opts.dontRun) { const term = try runExe(aa, ccomp.exeFilename, opts.programArgs); switch (term) { .Exited => |code| { std.debug.print("program exited with code {}\n", .{code}); }, else => |code| { std.debug.print("unexpected STOP ({})\n", .{code}); }, } } }, } } else { if (!opts.dontRun) { const interpretStartTime = try std.time.Instant.now(); // how would I handle a partially declared Prelude? or should I even do it? it may be useful? const ret = try Interpreter.run(moduleAST, modules.prelude.?, modules.typeContext, opts.programArgs, aa, al); const interpretTime = std.time.Instant.since(try std.time.Instant.now(), interpretStartTime) / std.time.ns_per_ms; std.debug.print("=== return value: {} ===\n", .{ret}); std.debug.print("=== interpret time: {}ms ===\n", .{interpretTime}); return; } } // backend.print(fakeHackCtx); // const retVal = try VM.exec(&backend.cur, al); // std.debug.print("VM: {}\n", .{retVal}); } pub const CompilationStuff = struct { prelude: Prelude, ast: []ast, modules: Modules, // compilationTimeMS: u64, }; pub fn preloadModules(opts: *const Args, aa: std.mem.Allocator) !Modules { const stdRoot = std.process.getEnvVarOwned(aa, "KKC_STD") catch |e| switch (e) { error.EnvironmentVariableNotFound => b: { std.debug.print("KKC_STD env var not set. Defaulting to 'std/'.\n", .{}); break :b "std/"; }, else => return e, }; // -|| MODULES ||- const errors = try common.allocOne(aa, Errors.init(aa)); const typeContext = try common.allocOne(aa, try TypeContext.init(aa, errors)); const root = std.fs.path.dirname(opts.filename) orelse ""; var modules = try Modules.init(aa, errors, typeContext, root, stdRoot, opts); if (!opts.noImplicitPrelude) { const prelude = try modules.loadPrelude(); typeContext.prelude = prelude; if (!opts.noDefaultImports) { _ = try modules.loadConverged(); } } return modules; } pub fn compileC(aa: std.mem.Allocator, cbackend: *const C, outName: Str, mdir: ?Str, out: anytype) !struct { time: u64, exeFilename: [:0]const u8, succeeded: bool, } { const cWritingCompilingStartTime = try std.time.Instant.now(); var c_filename = try std.mem.concat(aa, u8, &.{ outName, ".c" }); if (mdir) |dir| { c_filename = try std.mem.concat(aa, u8, &.{ dir, "/", c_filename }); } var outname = outName; if (mdir) |dir| { outname = try std.mem.concat(aa, u8, &.{ dir, "/", outname }); } const file = try std.fs.cwd().createFile(c_filename, .{}); defer file.close(); const writer = file.writer(); try cbackend.writeTo(writer); var copts = std.ArrayList([]const u8).init(aa); try copts.appendSlice(&.{ "cc", c_filename, "-o", outname }); var prog_c_opts = cbackend.coptions.iterator(); while (prog_c_opts.next()) |copt| { try copts.append(copt.*); } const res = try std.process.Child.run(.{ .allocator = aa, .argv = copts.items }); try out.writeAll(res.stdout); try out.writeAll(res.stderr); const cWritingCompilingTime = std.time.Instant.since(try std.time.Instant.now(), cWritingCompilingStartTime) / std.time.ns_per_ms; const exeFilename = try aa.dupeZ(u8, outname); switch (res.term) { .Exited => |code| { if (code == 0) { return .{ .time = cWritingCompilingTime, .exeFilename = exeFilename, .succeeded = true, }; } }, else => {}, } return .{ .time = cWritingCompilingTime, .exeFilename = exeFilename, .succeeded = false, }; } pub fn runExe(aa: std.mem.Allocator, outName: Str, args: []const [*:0]const u8) !std.process.Child.Term { const exe_name = if (outName[0] != '/') try std.mem.concat(aa, u8, &.{ "./", outName }) else outName; // prepare prog with args. var proc_params = std.ArrayList([]const u8).init(aa); try proc_params.append(exe_name); for (args[1..]) |ztArg| { var arg: []const u8 = undefined; arg.ptr = ztArg; arg.len = std.mem.len(ztArg); try proc_params.append(arg); } var child = std.process.Child.init(proc_params.items, aa); child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; child.stderr_behavior = .Inherit; try child.spawn(); const term = try child.wait(); return term; } pub fn compileFile(modules: *Modules, filename: Str) !Module { return try modules.initialModule(&filename); }