feat: error message for unknown global variables

This commit is contained in:
Brett Broadhurst 2026-03-18 19:21:56 -06:00
parent c940374f27
commit 47351cd6f9
Failed to generate hash of commit
6 changed files with 184 additions and 89 deletions

View file

@ -1,19 +1,25 @@
const std = @import("std");
const Ast = @import("Ast.zig");
const Ir = @import("Ir.zig");
const Story = @import("Story.zig");
const Compilation = @import("compile.zig").Compilation;
const compile = @import("compile.zig");
const Compilation = compile.Compilation;
const assert = std.debug.assert;
const Sema = @This();
gpa: std.mem.Allocator,
ir: *const Ir,
arena: std.mem.Allocator,
tree: Ast,
ir: Ir,
constants: std.ArrayListUnmanaged(Compilation.Constant) = .empty,
constant_map: std.AutoHashMapUnmanaged(Compilation.Constant, u32) = .empty,
knots: std.ArrayListUnmanaged(Compilation.Knot) = .empty,
globals: std.ArrayListUnmanaged(u32) = .empty,
errors: *std.ArrayListUnmanaged(Compilation.Error),
const InnerError = error{
OutOfMemory,
AnalysisFail,
TooManyConstants,
InvalidJump,
};
@ -37,8 +43,26 @@ pub fn deinit(sema: *Sema) void {
sema.* = undefined;
}
fn fail(_: *Sema, message: []const u8) InnerError {
@panic(message);
pub const SrcLoc = struct {
byte_offset: u32,
};
fn fail(
sema: *Sema,
src: SrcLoc,
comptime format: []const u8,
args: anytype,
) error{ OutOfMemory, AnalysisFail } {
// TODO: Revisit this
const message = try std.fmt.allocPrint(sema.arena, format, args);
const loc = compile.findLineColumn(sema.tree.source, src.byte_offset);
try sema.errors.append(sema.gpa, .{
.line = loc.line,
.column = loc.column,
.snippet = loc.source_line,
.message = message,
});
return error.AnalysisFail;
}
fn getConstant(sema: *Sema, data: Compilation.Constant) !Ref {
@ -61,23 +85,23 @@ fn addGlobal(sema: *Sema, name: Ir.NullTerminatedString) !Ref {
return .{ .global = interned.constant };
}
fn getGlobal(sema: *Sema, name: Ir.NullTerminatedString) !Ref {
fn getGlobal(sema: *Sema, name: Ir.NullTerminatedString) !?Ref {
const interned = try sema.getConstant(.{ .string = name });
for (sema.ir.globals) |global| {
if (global.name == name) {
return .{ .global = interned.constant };
}
}
return fail(sema, "unknown global variable");
return null;
}
fn irInteger(sema: *Sema, inst: Ir.Inst.Index) InnerError!Ref {
const data = sema.ir.instructions[@intFromEnum(inst)].data.integer;
return sema.getConstant(.{ .integer = data.value });
const value = sema.ir.instructions[@intFromEnum(inst)].data.int;
return sema.getConstant(.{ .integer = value });
}
fn irString(sema: *Sema, inst: Ir.Inst.Index) InnerError!Ref {
const data = sema.ir.instructions[@intFromEnum(inst)].data.string;
const data = sema.ir.instructions[@intFromEnum(inst)].data.str;
return sema.getConstant(.{ .string = data.start });
}
@ -308,9 +332,20 @@ fn irImplicitRet(_: *Sema, chunk: *Chunk, _: Ir.Inst.Index) InnerError!Ref {
return chunk.addByteOp(.exit);
}
fn resolveGlobal(
sema: *Sema,
byte_offset: u32,
global_name: Ir.NullTerminatedString,
) !Ref {
if (try sema.getGlobal(global_name)) |global| {
return global;
}
return fail(sema, .{ .byte_offset = byte_offset }, "unknown global variable", .{});
}
fn irDeclRef(sema: *Sema, _: *Chunk, inst: Ir.Inst.Index) InnerError!Ref {
const data = sema.ir.instructions[@intFromEnum(inst)].data.string;
return sema.getGlobal(data.start);
const data = sema.ir.instructions[@intFromEnum(inst)].data.str_tok;
return resolveGlobal(sema, data.src_offset, data.start);
}
fn irDeclVar(
@ -470,8 +505,8 @@ fn blockBodyInner(sema: *Sema, chunk: *Chunk, body: []const Ir.Inst.Index) Inner
.cmp_gt => try irBinary(sema, chunk, inst, .cmp_gt),
.cmp_gte => try irBinary(sema, chunk, inst, .cmp_gte),
.decl_ref => try irDeclRef(sema, chunk, inst),
.integer => try irInteger(sema, inst),
.string => try irString(sema, inst),
.int => try irInteger(sema, inst),
.str => try irString(sema, inst),
.condbr => try irCondBr(sema, chunk, inst),
.@"break" => {
try irBreak(sema, inst);
@ -505,7 +540,7 @@ fn blockBodyInner(sema: *Sema, chunk: *Chunk, body: []const Ir.Inst.Index) Inner
}
}
pub fn analyzeFile(sema: *Sema, inst: Ir.Inst.Index) !void {
pub fn analyzeFile(sema: *Sema, inst: Ir.Inst.Index) InnerError!void {
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
const extra = sema.ir.extraData(Ir.Inst.Block, data.payload_index);
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);