Files
sx/src/core.zig
agra dd14f1206b ir
2026-02-26 02:25:02 +02:00

129 lines
4.7 KiB
Zig

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 c_import = @import("c_import.zig");
const ir = @import("ir/ir.zig");
const Node = ast.Node;
pub const TargetConfig = codegen.TargetConfig;
pub const Compilation = struct {
allocator: std.mem.Allocator,
io: std.Io,
file_path: []const u8,
source: [:0]const u8,
diagnostics: errors.DiagnosticList,
target_config: TargetConfig,
// 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, target_config: TargetConfig) 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),
.target_config = target_config,
};
}
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;
// Store main file source in import_sources so error reporting can find it
self.import_sources.put(self.file_path, self.source) catch {};
// Wire import_sources to diagnostics for file-aware error rendering
self.diagnostics.import_sources = &self.import_sources;
// 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", self.target_config);
cg.diagnostics = &self.diagnostics;
cg.import_sources = &self.import_sources;
if (self.sema_result) |*sr| {
cg.sema_result = sr;
}
cg.generate(root) catch return error.CompileError;
self.cg = cg;
}
/// Collect C import source info from the resolved AST.
/// Called after generateCode() to compile C sources natively (not merged into LLVM module).
pub fn collectCImportSources(self: *Compilation) ![]c_import.CImportInfo {
const root = self.resolved_root orelse self.root orelse return &.{};
return c_import.collectCImportSources(self.allocator, root);
}
/// Lower the parsed AST to the sx IR module (shadow pipeline).
pub fn lowerToIR(self: *Compilation) ir.Module {
const root = self.resolved_root orelse self.root orelse return ir.Module.init(self.allocator);
var module = ir.Module.init(self.allocator);
var lowering = ir.Lowering.init(&module);
lowering.lowerRoot(root);
return module;
}
pub fn renderErrors(self: *const Compilation) void {
self.diagnostics.renderDebug();
}
};