refactor: new direction for error reporting
This commit is contained in:
parent
72b686d750
commit
c940374f27
11 changed files with 758 additions and 582 deletions
528
src/AstGen.zig
528
src/AstGen.zig
|
|
@ -16,7 +16,7 @@ globals: std.ArrayListUnmanaged(Ir.Global) = .empty,
|
|||
global_ref_table: std.AutoHashMapUnmanaged(Ir.NullTerminatedString, usize) = .empty,
|
||||
extra: std.ArrayListUnmanaged(u32) = .empty,
|
||||
scratch: std.ArrayListUnmanaged(u32) = .empty,
|
||||
errors: std.ArrayListUnmanaged(Ast.Error) = .empty,
|
||||
compile_errors: std.ArrayListUnmanaged(Ir.Inst.CompileErrors.Item) = .empty,
|
||||
|
||||
pub const InnerError = error{
|
||||
OutOfMemory,
|
||||
|
|
@ -25,7 +25,239 @@ pub const InnerError = error{
|
|||
Overflow,
|
||||
};
|
||||
|
||||
pub fn deinit(astgen: *AstGen) void {
|
||||
/// Splat an IR data struct into the `extra` array.
|
||||
fn addExtra(astgen: *AstGen, extra: anytype) !u32 {
|
||||
const fields = std.meta.fields(@TypeOf(extra));
|
||||
try astgen.extra.ensureUnusedCapacity(astgen.gpa, fields.len);
|
||||
return addExtraAssumeCapacity(astgen, extra);
|
||||
}
|
||||
|
||||
/// Splat an IR data struct into the `extra` array.
|
||||
fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
|
||||
const fields = std.meta.fields(@TypeOf(extra));
|
||||
const extra_index: u32 = @intCast(astgen.extra.items.len);
|
||||
astgen.extra.items.len += fields.len;
|
||||
setExtra(astgen, extra_index, extra);
|
||||
return extra_index;
|
||||
}
|
||||
|
||||
fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
|
||||
const fields = std.meta.fields(@TypeOf(extra));
|
||||
var i = index;
|
||||
inline for (fields) |field| {
|
||||
astgen.extra.items[i] = switch (field.type) {
|
||||
u32 => @field(extra, field.name),
|
||||
Ir.Inst.Index => @intFromEnum(@field(extra, field.name)),
|
||||
Ir.Inst.Ref => @intFromEnum(@field(extra, field.name)),
|
||||
Ir.NullTerminatedString => @intFromEnum(@field(extra, field.name)),
|
||||
else => @compileError("bad field type"),
|
||||
};
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn appendBlockBody(astgen: *AstGen, body: []const Ir.Inst.Index) void {
|
||||
return appendBlockBodyArrayList(astgen, &astgen.extra, body);
|
||||
}
|
||||
|
||||
fn appendBlockBodyArrayList(
|
||||
_: *AstGen,
|
||||
list: *std.ArrayListUnmanaged(u32),
|
||||
body: []const Ir.Inst.Index,
|
||||
) void {
|
||||
for (body) |inst_index| {
|
||||
list.appendAssumeCapacity(@intFromEnum(inst_index));
|
||||
}
|
||||
}
|
||||
fn appendErrorNode(
|
||||
astgen: *AstGen,
|
||||
node: *const Ast.Node,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) error{OutOfMemory}!void {
|
||||
return appendErrorToken(astgen, @intCast(node.loc.start), format, args);
|
||||
}
|
||||
|
||||
fn appendErrorToken(
|
||||
astgen: *AstGen,
|
||||
byte_offset: u32,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) error{OutOfMemory}!void {
|
||||
const gpa = astgen.gpa;
|
||||
const string_bytes = &astgen.string_bytes;
|
||||
const msg: Ir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
|
||||
try string_bytes.print(gpa, format ++ "\x00", args);
|
||||
|
||||
try astgen.compile_errors.append(gpa, .{
|
||||
.msg = msg,
|
||||
.byte_offset = byte_offset,
|
||||
});
|
||||
}
|
||||
|
||||
fn fail(
|
||||
astgen: *AstGen,
|
||||
node: *const Ast.Node,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) error{ SemanticError, OutOfMemory } {
|
||||
try appendErrorNode(astgen, node, format, args);
|
||||
return error.SemanticError;
|
||||
}
|
||||
|
||||
fn lowerAstErrors(astgen: *AstGen) error{OutOfMemory}!void {
|
||||
const gpa = astgen.gpa;
|
||||
var msg: std.Io.Writer.Allocating = .init(gpa);
|
||||
defer msg.deinit();
|
||||
const w = &msg.writer;
|
||||
|
||||
for (astgen.tree.errors) |err| {
|
||||
astgen.tree.renderError(w, err) catch return error.OutOfMemory;
|
||||
try appendErrorToken(
|
||||
astgen,
|
||||
@intCast(err.loc.start),
|
||||
"{s}",
|
||||
.{msg.written()},
|
||||
);
|
||||
msg.clearRetainingCapacity();
|
||||
}
|
||||
}
|
||||
|
||||
fn nullTerminatedString(astgen: *AstGen, str: Ir.NullTerminatedString) [:0]const u8 {
|
||||
const slice = astgen.string_bytes.items[@intFromEnum(str)..];
|
||||
return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
|
||||
}
|
||||
|
||||
fn stringFromBytes(astgen: *AstGen, bytes: []const u8) error{OutOfMemory}!Ir.NullTerminatedString {
|
||||
const gpa = astgen.gpa;
|
||||
const string_bytes = &astgen.string_bytes;
|
||||
const str_index: u32 = @intCast(string_bytes.items.len);
|
||||
try string_bytes.appendSlice(gpa, bytes);
|
||||
|
||||
const key: []const u8 = string_bytes.items[str_index..];
|
||||
const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
|
||||
.bytes = string_bytes,
|
||||
}, StringIndexContext{
|
||||
.bytes = string_bytes,
|
||||
});
|
||||
if (gop.found_existing) {
|
||||
string_bytes.shrinkRetainingCapacity(str_index);
|
||||
return @enumFromInt(gop.key_ptr.*);
|
||||
} else {
|
||||
gop.key_ptr.* = str_index;
|
||||
try string_bytes.append(gpa, 0);
|
||||
return @enumFromInt(str_index);
|
||||
}
|
||||
}
|
||||
|
||||
fn stringFromNode(astgen: *AstGen, node: *const Ast.Node) !Ir.NullTerminatedString {
|
||||
const name_bytes = astgen.tree.nodeSlice(node);
|
||||
return astgen.stringFromBytes(name_bytes);
|
||||
}
|
||||
|
||||
fn qualifiedString(
|
||||
astgen: *AstGen,
|
||||
prefix: Ir.NullTerminatedString,
|
||||
relative: Ir.NullTerminatedString,
|
||||
) !Ir.NullTerminatedString {
|
||||
const gpa = astgen.gpa;
|
||||
const string_bytes = &astgen.string_bytes;
|
||||
const string_table = &astgen.string_table;
|
||||
const str_index: u32 = @intCast(string_bytes.items.len);
|
||||
|
||||
switch (prefix) {
|
||||
.empty => return relative,
|
||||
else => |prev| {
|
||||
try string_bytes.appendSlice(gpa, nullTerminatedString(astgen, prev));
|
||||
try string_bytes.append(gpa, '.');
|
||||
try string_bytes.appendSlice(gpa, nullTerminatedString(astgen, relative));
|
||||
|
||||
const key: []const u8 = string_bytes.items[str_index..];
|
||||
const gop = try string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
|
||||
.bytes = string_bytes,
|
||||
}, StringIndexContext{
|
||||
.bytes = string_bytes,
|
||||
});
|
||||
if (gop.found_existing) {
|
||||
string_bytes.shrinkRetainingCapacity(str_index);
|
||||
return @enumFromInt(gop.key_ptr.*);
|
||||
} else {
|
||||
gop.key_ptr.* = str_index;
|
||||
try string_bytes.append(gpa, 0);
|
||||
return @enumFromInt(str_index);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform IR code generation via tree-walk.
|
||||
pub fn generate(gpa: std.mem.Allocator, tree: *const Ast) !Ir {
|
||||
var astgen: AstGen = .{
|
||||
.gpa = gpa,
|
||||
.tree = tree,
|
||||
};
|
||||
defer astgen.deinit();
|
||||
|
||||
// First entry is reserved for Ir.NullTerminatedString.empty.
|
||||
try astgen.string_bytes.append(gpa, 0);
|
||||
|
||||
var instructions: std.ArrayListUnmanaged(Ir.Inst.Index) = .empty;
|
||||
defer instructions.deinit(gpa);
|
||||
var file_scope: Scope = .{
|
||||
.parent = null,
|
||||
.decls = .empty,
|
||||
.namespace_prefix = .empty,
|
||||
.astgen = &astgen,
|
||||
};
|
||||
var block: GenIr = .{
|
||||
.astgen = &astgen,
|
||||
.instructions = &instructions,
|
||||
.instructions_top = 0,
|
||||
};
|
||||
defer block.unstack();
|
||||
|
||||
const reserved_extra_count = @typeInfo(Ir.ExtraIndex).@"enum".fields.len;
|
||||
try astgen.extra.ensureTotalCapacity(gpa, reserved_extra_count);
|
||||
astgen.extra.items.len += reserved_extra_count;
|
||||
|
||||
const fatal = if (tree.errors.len == 0) fatal: {
|
||||
// TODO: Make sure this is never null.
|
||||
const root_node = tree.root.?;
|
||||
file(&block, &file_scope, root_node) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
error.SemanticError => break :fatal true,
|
||||
else => |e| return e,
|
||||
};
|
||||
break :fatal false;
|
||||
} else fatal: {
|
||||
try lowerAstErrors(&astgen);
|
||||
break :fatal true;
|
||||
};
|
||||
|
||||
const err_index = @intFromEnum(Ir.ExtraIndex.compile_errors);
|
||||
if (astgen.compile_errors.items.len == 0) {
|
||||
astgen.extra.items[err_index] = 0;
|
||||
} else {
|
||||
const extra_len = 1 + astgen.compile_errors.items.len *
|
||||
@typeInfo(Ir.Inst.CompileErrors.Item).@"struct".fields.len;
|
||||
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
||||
|
||||
astgen.extra.items[err_index] = astgen.addExtraAssumeCapacity(Ir.Inst.CompileErrors{
|
||||
.items_len = @intCast(astgen.compile_errors.items.len),
|
||||
});
|
||||
for (astgen.compile_errors.items) |item| {
|
||||
_ = astgen.addExtraAssumeCapacity(item);
|
||||
}
|
||||
}
|
||||
return .{
|
||||
.instructions = if (fatal) &.{} else try astgen.instructions.toOwnedSlice(gpa),
|
||||
.string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
|
||||
.globals = try astgen.globals.toOwnedSlice(gpa),
|
||||
.extra = try astgen.extra.toOwnedSlice(gpa),
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(astgen: *AstGen) void {
|
||||
const gpa = astgen.gpa;
|
||||
astgen.string_table.deinit(gpa);
|
||||
astgen.string_bytes.deinit(gpa);
|
||||
|
|
@ -34,54 +266,9 @@ pub fn deinit(astgen: *AstGen) void {
|
|||
astgen.instructions.deinit(gpa);
|
||||
astgen.extra.deinit(gpa);
|
||||
astgen.scratch.deinit(gpa);
|
||||
astgen.errors.deinit(gpa);
|
||||
astgen.compile_errors.deinit(gpa);
|
||||
}
|
||||
|
||||
const Scope = struct {
|
||||
parent: ?*Scope,
|
||||
astgen: *AstGen,
|
||||
namespace_prefix: Ir.NullTerminatedString,
|
||||
decls: std.AutoHashMapUnmanaged(Ir.NullTerminatedString, Decl),
|
||||
|
||||
const Decl = struct {
|
||||
decl_node: *const Ast.Node,
|
||||
inst_index: Ir.Inst.Index,
|
||||
};
|
||||
|
||||
fn deinit(self: *Scope) void {
|
||||
const gpa = self.astgen.gpa;
|
||||
self.decls.deinit(gpa);
|
||||
}
|
||||
|
||||
fn makeChild(parent_scope: *Scope) Scope {
|
||||
return .{
|
||||
.parent = parent_scope,
|
||||
.astgen = parent_scope.astgen,
|
||||
.namespace_prefix = parent_scope.namespace_prefix,
|
||||
.decls = .empty,
|
||||
};
|
||||
}
|
||||
|
||||
fn setNamespacePrefix(scope: *Scope, relative: Ir.NullTerminatedString) !void {
|
||||
const astgen = scope.astgen;
|
||||
scope.namespace_prefix = try astgen.qualifiedString(scope.namespace_prefix, relative);
|
||||
}
|
||||
|
||||
fn insert(self: *Scope, ref: Ir.NullTerminatedString, decl: Decl) !void {
|
||||
const gpa = self.astgen.gpa;
|
||||
return self.decls.put(gpa, ref, decl);
|
||||
}
|
||||
|
||||
fn lookup(self: *Scope, ref: Ir.NullTerminatedString) ?Decl {
|
||||
var current_scope: ?*Scope = self;
|
||||
while (current_scope) |scope| : (current_scope = scope.parent) {
|
||||
const result = scope.decls.get(ref);
|
||||
if (result) |symbol| return symbol;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const GenIr = struct {
|
||||
astgen: *AstGen,
|
||||
instructions: *std.ArrayListUnmanaged(Ir.Inst.Index),
|
||||
|
|
@ -121,14 +308,6 @@ const GenIr = struct {
|
|||
self.instructions.items[self.instructions_top..];
|
||||
}
|
||||
|
||||
fn fail(
|
||||
self: *GenIr,
|
||||
tag: Ast.Error.Tag,
|
||||
node: *const Ast.Node,
|
||||
) error{ SemanticError, OutOfMemory } {
|
||||
return self.astgen.fail(tag, node);
|
||||
}
|
||||
|
||||
fn makeSubBlock(self: *GenIr) GenIr {
|
||||
return .{
|
||||
.astgen = self.astgen,
|
||||
|
|
@ -326,50 +505,50 @@ const GenIr = struct {
|
|||
}
|
||||
};
|
||||
|
||||
/// Splat an IR data struct into the `extra` array.
|
||||
fn addExtra(astgen: *AstGen, extra: anytype) !u32 {
|
||||
const fields = std.meta.fields(@TypeOf(extra));
|
||||
try astgen.extra.ensureUnusedCapacity(astgen.gpa, fields.len);
|
||||
return addExtraAssumeCapacity(astgen, extra);
|
||||
}
|
||||
const Scope = struct {
|
||||
parent: ?*Scope,
|
||||
astgen: *AstGen,
|
||||
namespace_prefix: Ir.NullTerminatedString,
|
||||
decls: std.AutoHashMapUnmanaged(Ir.NullTerminatedString, Decl),
|
||||
|
||||
/// Splat an IR data struct into the `extra` array.
|
||||
fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
|
||||
const fields = std.meta.fields(@TypeOf(extra));
|
||||
const extra_index: u32 = @intCast(astgen.extra.items.len);
|
||||
astgen.extra.items.len += fields.len;
|
||||
setExtra(astgen, extra_index, extra);
|
||||
return extra_index;
|
||||
}
|
||||
const Decl = struct {
|
||||
decl_node: *const Ast.Node,
|
||||
inst_index: Ir.Inst.Index,
|
||||
};
|
||||
|
||||
fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
|
||||
const fields = std.meta.fields(@TypeOf(extra));
|
||||
var i = index;
|
||||
inline for (fields) |field| {
|
||||
astgen.extra.items[i] = switch (field.type) {
|
||||
u32 => @field(extra, field.name),
|
||||
Ir.Inst.Index => @intFromEnum(@field(extra, field.name)),
|
||||
Ir.Inst.Ref => @intFromEnum(@field(extra, field.name)),
|
||||
Ir.NullTerminatedString => @intFromEnum(@field(extra, field.name)),
|
||||
else => @compileError("bad field type"),
|
||||
fn deinit(self: *Scope) void {
|
||||
const gpa = self.astgen.gpa;
|
||||
self.decls.deinit(gpa);
|
||||
}
|
||||
|
||||
fn makeChild(parent_scope: *Scope) Scope {
|
||||
return .{
|
||||
.parent = parent_scope,
|
||||
.astgen = parent_scope.astgen,
|
||||
.namespace_prefix = parent_scope.namespace_prefix,
|
||||
.decls = .empty,
|
||||
};
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn appendBlockBody(astgen: *AstGen, body: []const Ir.Inst.Index) void {
|
||||
return appendBlockBodyArrayList(astgen, &astgen.extra, body);
|
||||
}
|
||||
|
||||
fn appendBlockBodyArrayList(
|
||||
_: *AstGen,
|
||||
list: *std.ArrayListUnmanaged(u32),
|
||||
body: []const Ir.Inst.Index,
|
||||
) void {
|
||||
for (body) |inst_index| {
|
||||
list.appendAssumeCapacity(@intFromEnum(inst_index));
|
||||
fn setNamespacePrefix(scope: *Scope, relative: Ir.NullTerminatedString) !void {
|
||||
const astgen = scope.astgen;
|
||||
scope.namespace_prefix = try astgen.qualifiedString(scope.namespace_prefix, relative);
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(self: *Scope, ref: Ir.NullTerminatedString, decl: Decl) !void {
|
||||
const gpa = self.astgen.gpa;
|
||||
return self.decls.put(gpa, ref, decl);
|
||||
}
|
||||
|
||||
fn lookup(self: *Scope, ref: Ir.NullTerminatedString) ?Decl {
|
||||
var current_scope: ?*Scope = self;
|
||||
while (current_scope) |scope| : (current_scope = scope.parent) {
|
||||
const result = scope.decls.get(ref);
|
||||
if (result) |symbol| return symbol;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
fn setDeclaration(
|
||||
decl_index: Ir.Inst.Index,
|
||||
|
|
@ -392,7 +571,7 @@ fn setDeclaration(
|
|||
try astgen.global_ref_table.ensureUnusedCapacity(gpa, 1);
|
||||
|
||||
if (astgen.global_ref_table.get(args.name)) |_| {
|
||||
return astgen.fail(.redefined_identifier, args.decl_node);
|
||||
return fail(astgen, args.decl_node, "redefined identifier", .{});
|
||||
}
|
||||
|
||||
const inst_data = &astgen.instructions.items[@intFromEnum(decl_index)].data;
|
||||
|
|
@ -439,98 +618,6 @@ fn setCondBrPayload(
|
|||
astgen.appendBlockBody(else_body);
|
||||
}
|
||||
|
||||
fn fail(
|
||||
self: *AstGen,
|
||||
tag: Ast.Error.Tag,
|
||||
source_node: *const Ast.Node,
|
||||
) error{ SemanticError, OutOfMemory } {
|
||||
const gpa = self.gpa;
|
||||
const err: Ast.Error = .{
|
||||
.tag = tag,
|
||||
.loc = .{
|
||||
.start = source_node.loc.start,
|
||||
.end = source_node.loc.end,
|
||||
},
|
||||
};
|
||||
|
||||
try self.errors.append(gpa, err);
|
||||
return error.SemanticError;
|
||||
}
|
||||
|
||||
fn sliceFromNode(astgen: *const AstGen, node: *const Ast.Node) []const u8 {
|
||||
assert(node.loc.start <= node.loc.end);
|
||||
const source_bytes = astgen.tree.source;
|
||||
return source_bytes[node.loc.start..node.loc.end];
|
||||
}
|
||||
|
||||
fn stringFromBytes(astgen: *AstGen, bytes: []const u8) error{OutOfMemory}!Ir.NullTerminatedString {
|
||||
const gpa = astgen.gpa;
|
||||
const string_bytes = &astgen.string_bytes;
|
||||
const str_index: u32 = @intCast(string_bytes.items.len);
|
||||
try string_bytes.appendSlice(gpa, bytes);
|
||||
|
||||
const key: []const u8 = string_bytes.items[str_index..];
|
||||
const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
|
||||
.bytes = string_bytes,
|
||||
}, StringIndexContext{
|
||||
.bytes = string_bytes,
|
||||
});
|
||||
if (gop.found_existing) {
|
||||
string_bytes.shrinkRetainingCapacity(str_index);
|
||||
return @enumFromInt(gop.key_ptr.*);
|
||||
} else {
|
||||
gop.key_ptr.* = str_index;
|
||||
try string_bytes.append(gpa, 0);
|
||||
return @enumFromInt(str_index);
|
||||
}
|
||||
}
|
||||
|
||||
fn stringFromNode(astgen: *AstGen, node: *const Ast.Node) !Ir.NullTerminatedString {
|
||||
const name_bytes = sliceFromNode(astgen, node);
|
||||
assert(name_bytes.len > 0);
|
||||
return astgen.stringFromBytes(name_bytes);
|
||||
}
|
||||
|
||||
fn nullTerminatedString(astgen: *AstGen, str: Ir.NullTerminatedString) [:0]const u8 {
|
||||
const slice = astgen.string_bytes.items[@intFromEnum(str)..];
|
||||
return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
|
||||
}
|
||||
|
||||
fn qualifiedString(
|
||||
astgen: *AstGen,
|
||||
prefix: Ir.NullTerminatedString,
|
||||
relative: Ir.NullTerminatedString,
|
||||
) !Ir.NullTerminatedString {
|
||||
const gpa = astgen.gpa;
|
||||
const string_bytes = &astgen.string_bytes;
|
||||
const string_table = &astgen.string_table;
|
||||
const str_index: u32 = @intCast(string_bytes.items.len);
|
||||
|
||||
switch (prefix) {
|
||||
.empty => return relative,
|
||||
else => |prev| {
|
||||
try string_bytes.appendSlice(gpa, nullTerminatedString(astgen, prev));
|
||||
try string_bytes.append(gpa, '.');
|
||||
try string_bytes.appendSlice(gpa, nullTerminatedString(astgen, relative));
|
||||
|
||||
const key: []const u8 = string_bytes.items[str_index..];
|
||||
const gop = try string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
|
||||
.bytes = string_bytes,
|
||||
}, StringIndexContext{
|
||||
.bytes = string_bytes,
|
||||
});
|
||||
if (gop.found_existing) {
|
||||
string_bytes.shrinkRetainingCapacity(str_index);
|
||||
return @enumFromInt(gop.key_ptr.*);
|
||||
} else {
|
||||
gop.key_ptr.* = str_index;
|
||||
try string_bytes.append(gpa, 0);
|
||||
return @enumFromInt(str_index);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn unaryOp(
|
||||
gi: *GenIr,
|
||||
scope: *Scope,
|
||||
|
|
@ -581,10 +668,10 @@ fn logicalOp(
|
|||
gen.setLabel(else_label);
|
||||
}
|
||||
|
||||
fn numberLiteral(gen: *GenIr, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
||||
const lexeme = sliceFromNode(gen.astgen, node);
|
||||
fn numberLiteral(block: *GenIr, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
||||
const lexeme = block.astgen.tree.nodeSlice(node);
|
||||
const int_value = try std.fmt.parseUnsigned(u64, lexeme, 10);
|
||||
return gen.addInt(int_value);
|
||||
return block.addInt(int_value);
|
||||
}
|
||||
|
||||
fn stringLiteral(gi: *GenIr, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
||||
|
|
@ -694,6 +781,7 @@ fn inlineLogicExpr(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!
|
|||
}
|
||||
|
||||
fn validateSwitchProngs(gen: *GenIr, stmt_node: *const Ast.Node) InnerError!void {
|
||||
const astgen = gen.astgen;
|
||||
var stmt_has_block: bool = false;
|
||||
var stmt_has_else: bool = false;
|
||||
const case_list = stmt_node.data.switch_stmt.cases;
|
||||
|
|
@ -708,10 +796,10 @@ fn validateSwitchProngs(gen: *GenIr, stmt_node: *const Ast.Node) InnerError!void
|
|||
},
|
||||
.else_branch => {
|
||||
if (case_stmt != last_prong) {
|
||||
return gen.fail(.invalid_else_stmt, case_stmt);
|
||||
return fail(astgen, case_stmt, "invalid else stmt", .{});
|
||||
}
|
||||
if (stmt_has_else) {
|
||||
return gen.fail(.unexpected_else_stmt, case_stmt);
|
||||
return fail(astgen, case_stmt, "duplicate else stmt", .{});
|
||||
}
|
||||
stmt_has_else = true;
|
||||
},
|
||||
|
|
@ -845,7 +933,7 @@ fn switchStmt(
|
|||
.number_literal => try numberLiteral(parent_block, case_expr),
|
||||
.true_literal => .bool_true,
|
||||
.false_literal => .bool_false,
|
||||
else => return parent_block.fail(.invalid_switch_case, case_stmt),
|
||||
else => return fail(astgen, case_expr, "invalid switch case", .{}),
|
||||
};
|
||||
var case_block = parent_block.makeSubBlock();
|
||||
defer case_block.unstack();
|
||||
|
|
@ -934,7 +1022,7 @@ fn assignStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!void
|
|||
_ = try gi.addBinaryNode(.store, decl.inst_index.toRef(), expr_result);
|
||||
return;
|
||||
}
|
||||
return gi.fail(.unknown_identifier, identifier_node);
|
||||
return fail(astgen, identifier_node, "unknown identifier", .{});
|
||||
}
|
||||
|
||||
fn choiceStarStmt(gi: *GenIr, _: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
||||
|
|
@ -1174,12 +1262,13 @@ fn divertStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
|
|||
}
|
||||
|
||||
fn tempDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
|
||||
const astgen = gi.astgen;
|
||||
const identifier_node = decl_node.data.bin.lhs.?;
|
||||
const expr_node = decl_node.data.bin.rhs.?;
|
||||
const name_ref = try gi.astgen.stringFromNode(identifier_node);
|
||||
const name_ref = try astgen.stringFromNode(identifier_node);
|
||||
|
||||
if (scope.lookup(name_ref)) |_| {
|
||||
return gi.fail(.redefined_identifier, decl_node);
|
||||
return fail(astgen, decl_node, "duplicate identifier", .{});
|
||||
}
|
||||
|
||||
const alloc_inst = try gi.add(.{ .tag = .alloc, .data = undefined });
|
||||
|
|
@ -1371,9 +1460,7 @@ fn file(gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!void {
|
|||
const file_inst = try gi.addAsIndex(.{
|
||||
.tag = .file,
|
||||
.data = .{
|
||||
.payload = .{
|
||||
.payload_index = undefined,
|
||||
},
|
||||
.payload = .{ .payload_index = undefined },
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -1403,46 +1490,3 @@ fn file(gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!void {
|
|||
}
|
||||
return file_scope.setBlockBody(file_inst);
|
||||
}
|
||||
|
||||
/// Perform code generation via tree-walk.
|
||||
pub fn generate(gpa: std.mem.Allocator, tree: *const Ast) !Ir {
|
||||
var astgen: AstGen = .{
|
||||
.gpa = gpa,
|
||||
.tree = tree,
|
||||
};
|
||||
defer astgen.deinit();
|
||||
|
||||
// First entry is reserved for Ir.NullTerminatedString.empty.
|
||||
try astgen.string_bytes.append(gpa, 0);
|
||||
|
||||
var instructions: std.ArrayListUnmanaged(Ir.Inst.Index) = .empty;
|
||||
defer instructions.deinit(gpa);
|
||||
|
||||
var file_scope: Scope = .{
|
||||
.parent = null,
|
||||
.decls = .empty,
|
||||
.namespace_prefix = .empty,
|
||||
.astgen = &astgen,
|
||||
};
|
||||
var gen: GenIr = .{
|
||||
.astgen = &astgen,
|
||||
.instructions = &instructions,
|
||||
.instructions_top = 0,
|
||||
};
|
||||
defer gen.unstack();
|
||||
|
||||
// TODO: Make sure this is never null.
|
||||
const root_node = tree.root.?;
|
||||
file(&gen, &file_scope, root_node) catch |err| switch (err) {
|
||||
error.SemanticError => {},
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
return .{
|
||||
.string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
|
||||
.instructions = try astgen.instructions.toOwnedSlice(gpa),
|
||||
.globals = try astgen.globals.toOwnedSlice(gpa),
|
||||
.extra = try astgen.extra.toOwnedSlice(gpa),
|
||||
.errors = try astgen.errors.toOwnedSlice(gpa),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue