const std = @import("std"); const ast = @import("ast.zig"); const parser = @import("parser.zig"); const imports = @import("imports.zig"); const sema = @import("sema.zig"); const codegen = @import("codegen.zig"); const errors = @import("errors.zig"); const Node = ast.Node; pub const Compilation = struct { allocator: std.mem.Allocator, io: std.Io, file_path: []const u8, source: [:0]const u8, diagnostics: errors.DiagnosticList, // Pipeline results root: ?*Node = null, resolved_root: ?*Node = null, import_sources: std.StringHashMap([:0]const u8), sema_result: ?sema.SemaResult = null, cg: ?codegen.CodeGen = null, pub fn init(allocator: std.mem.Allocator, io: std.Io, file_path: []const u8, source: [:0]const u8) Compilation { return .{ .allocator = allocator, .io = io, .file_path = file_path, .source = source, .diagnostics = errors.DiagnosticList.init(allocator, source, file_path), .import_sources = std.StringHashMap([:0]const u8).init(allocator), }; } pub fn deinit(self: *Compilation) void { if (self.cg) |*cg| cg.deinit(); self.diagnostics.deinit(); } pub fn parse(self: *Compilation) !void { var p = parser.Parser.init(self.allocator, self.source); p.diagnostics = &self.diagnostics; self.root = p.parse() catch return error.CompileError; } pub fn resolveImports(self: *Compilation) !void { const root = self.root orelse return error.CompileError; var chain = std.StringHashMap(void).init(self.allocator); var cache = imports.ModuleCache.init(self.allocator); const base_dir = imports.dirName(self.file_path); const mod = imports.resolveImports( self.allocator, self.io, root, base_dir, self.file_path, &chain, &cache, &self.import_sources, &self.diagnostics, ) catch return error.CompileError; // Build a root node from the resolved module's decls const new_root = try self.allocator.create(Node); new_root.* = .{ .span = root.span, .data = .{ .root = .{ .decls = mod.decls } }, }; self.resolved_root = new_root; } pub fn analyze(self: *Compilation) !void { const root = self.resolved_root orelse self.root orelse return error.CompileError; var analyzer = sema.Analyzer.init(self.allocator); self.sema_result = analyzer.analyze(root) catch return error.CompileError; // Merge sema diagnostics into our list if (self.sema_result) |sr| { for (sr.diagnostics) |d| { self.diagnostics.add(d.level, d.message, d.span); } } } pub fn generateCode(self: *Compilation) !void { const root = self.resolved_root orelse self.root orelse return error.CompileError; var cg = codegen.CodeGen.init(self.allocator, "sx_module"); cg.diagnostics = &self.diagnostics; if (self.sema_result) |*sr| { cg.sema_result = sr; } cg.generate(root) catch return error.CompileError; self.cg = cg; } pub fn renderErrors(self: *const Compilation) void { for (self.diagnostics.items.items) |d| { const level_str = switch (d.level) { .err => "error", .warn => "warning", .note => "note", }; if (d.span) |span| { const loc = errors.SourceLoc.compute(self.source, span.start); std.debug.print("{s}:{d}:{d}: {s}: {s}\n", .{ self.file_path, loc.line, loc.col, level_str, d.message }); } else { std.debug.print("{s}: {s}: {s}\n", .{ self.file_path, level_str, d.message }); } } } };