feat: error message for unknown global variables
This commit is contained in:
parent
c940374f27
commit
47351cd6f9
6 changed files with 184 additions and 89 deletions
126
src/AstGen.zig
126
src/AstGen.zig
|
|
@ -128,7 +128,12 @@ fn nullTerminatedString(astgen: *AstGen, str: Ir.NullTerminatedString) [:0]const
|
||||||
return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
|
return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stringFromBytes(astgen: *AstGen, bytes: []const u8) error{OutOfMemory}!Ir.NullTerminatedString {
|
const IndexSlice = struct {
|
||||||
|
index: Ir.NullTerminatedString,
|
||||||
|
len: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn strFromSlice(astgen: *AstGen, bytes: []const u8) error{OutOfMemory}!IndexSlice {
|
||||||
const gpa = astgen.gpa;
|
const gpa = astgen.gpa;
|
||||||
const string_bytes = &astgen.string_bytes;
|
const string_bytes = &astgen.string_bytes;
|
||||||
const str_index: u32 = @intCast(string_bytes.items.len);
|
const str_index: u32 = @intCast(string_bytes.items.len);
|
||||||
|
|
@ -142,17 +147,23 @@ fn stringFromBytes(astgen: *AstGen, bytes: []const u8) error{OutOfMemory}!Ir.Nul
|
||||||
});
|
});
|
||||||
if (gop.found_existing) {
|
if (gop.found_existing) {
|
||||||
string_bytes.shrinkRetainingCapacity(str_index);
|
string_bytes.shrinkRetainingCapacity(str_index);
|
||||||
return @enumFromInt(gop.key_ptr.*);
|
return .{
|
||||||
|
.index = @enumFromInt(gop.key_ptr.*),
|
||||||
|
.len = @intCast(key.len),
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
gop.key_ptr.* = str_index;
|
gop.key_ptr.* = str_index;
|
||||||
try string_bytes.append(gpa, 0);
|
try string_bytes.append(gpa, 0);
|
||||||
return @enumFromInt(str_index);
|
return .{
|
||||||
|
.index = @enumFromInt(str_index),
|
||||||
|
.len = @intCast(key.len),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stringFromNode(astgen: *AstGen, node: *const Ast.Node) !Ir.NullTerminatedString {
|
fn strFromNode(astgen: *AstGen, node: *const Ast.Node) !IndexSlice {
|
||||||
const name_bytes = astgen.tree.nodeSlice(node);
|
const name_bytes = astgen.tree.nodeSlice(node);
|
||||||
return astgen.stringFromBytes(name_bytes);
|
return astgen.strFromSlice(name_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn qualifiedString(
|
fn qualifiedString(
|
||||||
|
|
@ -332,14 +343,8 @@ const GenIr = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addInt(gi: *GenIr, value: u64) !Ir.Inst.Ref {
|
fn addInt(gi: *GenIr, value: u64) !Ir.Inst.Ref {
|
||||||
return add(gi, .{ .tag = .integer, .data = .{
|
return add(gi, .{ .tag = .int, .data = .{
|
||||||
.integer = .{ .value = value },
|
.int = value,
|
||||||
} });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn addStr(gi: *GenIr, tag: Ir.Inst.Tag, str: Ir.NullTerminatedString) !Ir.Inst.Ref {
|
|
||||||
return add(gi, .{ .tag = tag, .data = .{
|
|
||||||
.string = .{ .start = str },
|
|
||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,14 +365,33 @@ const GenIr = struct {
|
||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addDeclRef(gi: *GenIr, decl_ref: Ir.NullTerminatedString) !Ir.Inst.Ref {
|
fn addStr(
|
||||||
return add(gi, .{ .tag = .decl_ref, .data = .{
|
gi: *GenIr,
|
||||||
.string = .{
|
str: Ir.NullTerminatedString,
|
||||||
.start = decl_ref,
|
str_len: usize,
|
||||||
},
|
) !Ir.Inst.Ref {
|
||||||
|
assert(str_len <= std.math.maxInt(u32));
|
||||||
|
return add(gi, .{ .tag = .str, .data = .{
|
||||||
|
.str = .{ .start = str, .len = @intCast(str_len) },
|
||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn addStrTok(
|
||||||
|
block: *GenIr,
|
||||||
|
tag: Ir.Inst.Tag,
|
||||||
|
str_index: Ir.NullTerminatedString,
|
||||||
|
byte_offset: usize,
|
||||||
|
) !Ir.Inst.Ref {
|
||||||
|
assert(byte_offset <= std.math.maxInt(u32));
|
||||||
|
return block.add(.{
|
||||||
|
.tag = tag,
|
||||||
|
.data = .{ .str_tok = .{
|
||||||
|
.start = str_index,
|
||||||
|
.src_offset = @intCast(byte_offset),
|
||||||
|
} },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn addPayloadNode(gi: *GenIr, tag: Ir.Inst.Tag, extra: anytype) !Ir.Inst.Ref {
|
fn addPayloadNode(gi: *GenIr, tag: Ir.Inst.Tag, extra: anytype) !Ir.Inst.Ref {
|
||||||
const gpa = gi.astgen.gpa;
|
const gpa = gi.astgen.gpa;
|
||||||
try gi.instructions.ensureUnusedCapacity(gpa, 1);
|
try gi.instructions.ensureUnusedCapacity(gpa, 1);
|
||||||
|
|
@ -675,13 +699,8 @@ fn numberLiteral(block: *GenIr, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stringLiteral(gi: *GenIr, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
fn stringLiteral(gi: *GenIr, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
||||||
const str = try gi.astgen.stringFromNode(node);
|
const str = try gi.astgen.strFromNode(node);
|
||||||
return gi.add(.{
|
return gi.addStr(str.index, str.len);
|
||||||
.tag = .string,
|
|
||||||
.data = .{ .string = .{
|
|
||||||
.start = str,
|
|
||||||
} },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stringExpr(gen: *GenIr, expr_node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
fn stringExpr(gen: *GenIr, expr_node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
||||||
|
|
@ -689,13 +708,16 @@ fn stringExpr(gen: *GenIr, expr_node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
||||||
return stringLiteral(gen, first_node);
|
return stringLiteral(gen, first_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identifier(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
fn identifier(
|
||||||
const astgen = gi.astgen;
|
block: *GenIr,
|
||||||
const str = try astgen.stringFromNode(node);
|
scope: *Scope,
|
||||||
if (scope.lookup(str)) |decl| {
|
node: *const Ast.Node,
|
||||||
return gi.addUnaryNode(.load, decl.inst_index.toRef());
|
) InnerError!Ir.Inst.Ref {
|
||||||
|
const str = try block.astgen.strFromNode(node);
|
||||||
|
if (scope.lookup(str.index)) |decl| {
|
||||||
|
return block.addUnaryNode(.load, decl.inst_index.toRef());
|
||||||
}
|
}
|
||||||
return gi.addDeclRef(str);
|
return block.addStrTok(.decl_ref, str.index, node.loc.start);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr(gi: *GenIr, scope: *Scope, optional_expr: ?*const Ast.Node) InnerError!Ir.Inst.Ref {
|
fn expr(gi: *GenIr, scope: *Scope, optional_expr: ?*const Ast.Node) InnerError!Ir.Inst.Ref {
|
||||||
|
|
@ -1014,10 +1036,10 @@ fn assignStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!void
|
||||||
const astgen = gi.astgen;
|
const astgen = gi.astgen;
|
||||||
const identifier_node = node.data.bin.lhs.?;
|
const identifier_node = node.data.bin.lhs.?;
|
||||||
const expr_node = node.data.bin.rhs.?;
|
const expr_node = node.data.bin.rhs.?;
|
||||||
const name_ref = try astgen.stringFromNode(identifier_node);
|
const name_str = try astgen.strFromNode(identifier_node);
|
||||||
|
|
||||||
// TODO: Support globals as well
|
// TODO: Support globals as well
|
||||||
if (scope.lookup(name_ref)) |decl| {
|
if (scope.lookup(name_str.index)) |decl| {
|
||||||
const expr_result = try expr(gi, scope, expr_node);
|
const expr_result = try expr(gi, scope, expr_node);
|
||||||
_ = try gi.addBinaryNode(.store, decl.inst_index.toRef(), expr_result);
|
_ = try gi.addBinaryNode(.store, decl.inst_index.toRef(), expr_result);
|
||||||
return;
|
return;
|
||||||
|
|
@ -1112,12 +1134,12 @@ fn fieldAccess(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.I
|
||||||
const data = node.data.bin;
|
const data = node.data.bin;
|
||||||
|
|
||||||
assert(data.rhs.?.tag == .identifier);
|
assert(data.rhs.?.tag == .identifier);
|
||||||
const field_str = try gi.astgen.stringFromNode(data.rhs.?);
|
const field_str = try gi.astgen.strFromNode(data.rhs.?);
|
||||||
const lhs = try expr(gi, scope, data.lhs.?);
|
const lhs = try expr(gi, scope, data.lhs.?);
|
||||||
|
|
||||||
return gi.addPayloadNode(.field_ptr, Ir.Inst.Field{
|
return gi.addPayloadNode(.field_ptr, Ir.Inst.Field{
|
||||||
.lhs = lhs,
|
.lhs = lhs,
|
||||||
.field_name_start = field_str,
|
.field_name_start = field_str.index,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1133,10 +1155,10 @@ fn calleeExpr(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Calle
|
||||||
const call_target = data.rhs.?;
|
const call_target = data.rhs.?;
|
||||||
assert(call_target.tag == .identifier);
|
assert(call_target.tag == .identifier);
|
||||||
|
|
||||||
const field_str = try gi.astgen.stringFromNode(call_target);
|
const field_str = try gi.astgen.strFromNode(call_target);
|
||||||
const lhs = try expr(gi, scope, data.lhs.?);
|
const lhs = try expr(gi, scope, data.lhs.?);
|
||||||
return .{
|
return .{
|
||||||
.field = .{ .obj_ptr = lhs, .field_name_start = field_str },
|
.field = .{ .obj_ptr = lhs, .field_name_start = field_str.index },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
.identifier => {
|
.identifier => {
|
||||||
|
|
@ -1265,9 +1287,9 @@ fn tempDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
|
||||||
const astgen = gi.astgen;
|
const astgen = gi.astgen;
|
||||||
const identifier_node = decl_node.data.bin.lhs.?;
|
const identifier_node = decl_node.data.bin.lhs.?;
|
||||||
const expr_node = decl_node.data.bin.rhs.?;
|
const expr_node = decl_node.data.bin.rhs.?;
|
||||||
const name_ref = try astgen.stringFromNode(identifier_node);
|
const name_str = try astgen.strFromNode(identifier_node);
|
||||||
|
|
||||||
if (scope.lookup(name_ref)) |_| {
|
if (scope.lookup(name_str.index)) |_| {
|
||||||
return fail(astgen, decl_node, "duplicate identifier", .{});
|
return fail(astgen, decl_node, "duplicate identifier", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1275,7 +1297,7 @@ fn tempDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
|
||||||
const expr_result = try expr(gi, scope, expr_node);
|
const expr_result = try expr(gi, scope, expr_node);
|
||||||
_ = try gi.addBinaryNode(.store, alloc_inst, expr_result);
|
_ = try gi.addBinaryNode(.store, alloc_inst, expr_result);
|
||||||
|
|
||||||
return scope.insert(name_ref, .{
|
return scope.insert(name_str.index, .{
|
||||||
.decl_node = decl_node,
|
.decl_node = decl_node,
|
||||||
.inst_index = alloc_inst.toIndex().?,
|
.inst_index = alloc_inst.toIndex().?,
|
||||||
});
|
});
|
||||||
|
|
@ -1297,9 +1319,11 @@ fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
|
||||||
|
|
||||||
_ = try expr(&decl_block, scope, expr_node);
|
_ = try expr(&decl_block, scope, expr_node);
|
||||||
const var_inst = try decl_block.addVar();
|
const var_inst = try decl_block.addVar();
|
||||||
|
|
||||||
|
const name_str = try astgen.strFromNode(identifier_node);
|
||||||
try setDeclaration(decl_inst.toIndex().?, .{
|
try setDeclaration(decl_inst.toIndex().?, .{
|
||||||
.tag = .variable,
|
.tag = .variable,
|
||||||
.name = try astgen.stringFromNode(identifier_node),
|
.name = name_str.index,
|
||||||
.ref = var_inst,
|
.ref = var_inst,
|
||||||
.decl_node = decl_node,
|
.decl_node = decl_node,
|
||||||
.body_block = &decl_block,
|
.body_block = &decl_block,
|
||||||
|
|
@ -1353,10 +1377,11 @@ fn defaultBlock(
|
||||||
const block_stmts = body_node.data.list.items.?;
|
const block_stmts = body_node.data.list.items.?;
|
||||||
try blockInner(&decl_scope, scope, block_stmts);
|
try blockInner(&decl_scope, scope, block_stmts);
|
||||||
|
|
||||||
|
const name_str = try astgen.strFromSlice(Story.default_knot_name);
|
||||||
const knot_inst = try decl_scope.addKnot();
|
const knot_inst = try decl_scope.addKnot();
|
||||||
try setDeclaration(decl_inst, .{
|
try setDeclaration(decl_inst, .{
|
||||||
.tag = .knot,
|
.tag = .knot,
|
||||||
.name = try astgen.stringFromBytes(Story.default_knot_name),
|
.name = name_str.index,
|
||||||
.ref = knot_inst,
|
.ref = knot_inst,
|
||||||
.decl_node = body_node,
|
.decl_node = body_node,
|
||||||
.body_block = &decl_scope,
|
.body_block = &decl_scope,
|
||||||
|
|
@ -1387,11 +1412,11 @@ fn stitchDeclInner(
|
||||||
const args_list = args_node.data.list.items.?;
|
const args_list = args_node.data.list.items.?;
|
||||||
for (args_list) |arg| {
|
for (args_list) |arg| {
|
||||||
assert(arg.tag == .parameter_decl);
|
assert(arg.tag == .parameter_decl);
|
||||||
const arg_str = try astgen.stringFromNode(arg);
|
const arg_str = try astgen.strFromNode(arg);
|
||||||
const arg_inst = try decl_block.addStr(.param, arg_str);
|
const arg_inst = try decl_block.addStrTok(.param, arg_str.index, arg.loc.start);
|
||||||
|
|
||||||
// TODO: Maybe make decl accept a ref?
|
// TODO: Maybe make decl accept a ref?
|
||||||
try scope.insert(arg_str, .{
|
try scope.insert(arg_str.index, .{
|
||||||
.decl_node = arg,
|
.decl_node = arg,
|
||||||
.inst_index = arg_inst.toIndex().?,
|
.inst_index = arg_inst.toIndex().?,
|
||||||
});
|
});
|
||||||
|
|
@ -1403,12 +1428,11 @@ fn stitchDeclInner(
|
||||||
_ = try decl_block.addUnaryNode(.implicit_ret, .none);
|
_ = try decl_block.addUnaryNode(.implicit_ret, .none);
|
||||||
}
|
}
|
||||||
|
|
||||||
const knot_inst = try decl_block.addKnot();
|
const name_str = try astgen.strFromNode(identifier_node);
|
||||||
const name_str = try astgen.stringFromNode(identifier_node);
|
|
||||||
try setDeclaration(decl_inst, .{
|
try setDeclaration(decl_inst, .{
|
||||||
.tag = .knot,
|
.tag = .knot,
|
||||||
.name = try astgen.qualifiedString(scope.namespace_prefix, name_str),
|
.name = try astgen.qualifiedString(scope.namespace_prefix, name_str.index),
|
||||||
.ref = knot_inst,
|
.ref = try decl_block.addKnot(),
|
||||||
.decl_node = decl_node,
|
.decl_node = decl_node,
|
||||||
.body_block = &decl_block,
|
.body_block = &decl_block,
|
||||||
});
|
});
|
||||||
|
|
@ -1444,8 +1468,8 @@ fn knotDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) InnerE
|
||||||
try stitchDeclInner(gi, &decl_scope, decl_node, prototype_node, null);
|
try stitchDeclInner(gi, &decl_scope, decl_node, prototype_node, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
const name_str = try gi.astgen.stringFromNode(identifier_node);
|
const name_str = try gi.astgen.strFromNode(identifier_node);
|
||||||
try decl_scope.setNamespacePrefix(name_str);
|
try decl_scope.setNamespacePrefix(name_str.index);
|
||||||
|
|
||||||
for (nested_decls_list[start_index..]) |nested_decl_node| {
|
for (nested_decls_list[start_index..]) |nested_decl_node| {
|
||||||
switch (nested_decl_node.tag) {
|
switch (nested_decl_node.tag) {
|
||||||
|
|
|
||||||
31
src/Ir.zig
31
src/Ir.zig
|
|
@ -166,16 +166,24 @@ pub const Inst = struct {
|
||||||
declaration,
|
declaration,
|
||||||
decl_knot,
|
decl_knot,
|
||||||
decl_var,
|
decl_var,
|
||||||
|
/// Uses the `str_tok` union field.
|
||||||
decl_ref,
|
decl_ref,
|
||||||
alloc,
|
alloc,
|
||||||
load,
|
load,
|
||||||
store,
|
store,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
add,
|
add,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
sub,
|
sub,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
mul,
|
mul,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
div,
|
div,
|
||||||
|
/// Uses the `bin` union field.
|
||||||
mod,
|
mod,
|
||||||
|
/// Uses the `un` union field.
|
||||||
neg,
|
neg,
|
||||||
|
/// Uses the `un` union field.
|
||||||
not,
|
not,
|
||||||
cmp_eq,
|
cmp_eq,
|
||||||
cmp_neq,
|
cmp_neq,
|
||||||
|
|
@ -183,8 +191,10 @@ pub const Inst = struct {
|
||||||
cmp_gte,
|
cmp_gte,
|
||||||
cmp_lt,
|
cmp_lt,
|
||||||
cmp_lte,
|
cmp_lte,
|
||||||
integer,
|
/// Uses the `int` union field.
|
||||||
string,
|
int,
|
||||||
|
/// Uses the `str` union field.
|
||||||
|
str,
|
||||||
block,
|
block,
|
||||||
condbr,
|
condbr,
|
||||||
@"break",
|
@"break",
|
||||||
|
|
@ -212,17 +222,22 @@ pub const Inst = struct {
|
||||||
lhs: Ref,
|
lhs: Ref,
|
||||||
rhs: Ref,
|
rhs: Ref,
|
||||||
},
|
},
|
||||||
integer: struct {
|
int: u64,
|
||||||
value: u64,
|
str: struct {
|
||||||
},
|
|
||||||
string: struct {
|
|
||||||
/// Offset into `string_bytes`.
|
/// Offset into `string_bytes`.
|
||||||
start: NullTerminatedString,
|
start: NullTerminatedString,
|
||||||
|
/// Number of bytes in the string.
|
||||||
|
len: u32,
|
||||||
|
|
||||||
pub fn get(self: @This(), ir: Ir) []const u8 {
|
pub fn get(self: @This(), code: Ir) []const u8 {
|
||||||
return nullTerminatedString(ir, self.start);
|
return code.string_bytes[@intFromEnum(self.start)..][0..self.len];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
str_tok: struct {
|
||||||
|
/// Offset into `string_bytes`.
|
||||||
|
start: NullTerminatedString,
|
||||||
|
src_offset: u32,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Declaration = struct {
|
pub const Declaration = struct {
|
||||||
|
|
|
||||||
63
src/Sema.zig
63
src/Sema.zig
|
|
@ -1,19 +1,25 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const Ast = @import("Ast.zig");
|
||||||
const Ir = @import("Ir.zig");
|
const Ir = @import("Ir.zig");
|
||||||
const Story = @import("Story.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 assert = std.debug.assert;
|
||||||
const Sema = @This();
|
const Sema = @This();
|
||||||
|
|
||||||
gpa: std.mem.Allocator,
|
gpa: std.mem.Allocator,
|
||||||
ir: *const Ir,
|
arena: std.mem.Allocator,
|
||||||
|
tree: Ast,
|
||||||
|
ir: Ir,
|
||||||
constants: std.ArrayListUnmanaged(Compilation.Constant) = .empty,
|
constants: std.ArrayListUnmanaged(Compilation.Constant) = .empty,
|
||||||
constant_map: std.AutoHashMapUnmanaged(Compilation.Constant, u32) = .empty,
|
constant_map: std.AutoHashMapUnmanaged(Compilation.Constant, u32) = .empty,
|
||||||
knots: std.ArrayListUnmanaged(Compilation.Knot) = .empty,
|
knots: std.ArrayListUnmanaged(Compilation.Knot) = .empty,
|
||||||
globals: std.ArrayListUnmanaged(u32) = .empty,
|
globals: std.ArrayListUnmanaged(u32) = .empty,
|
||||||
|
errors: *std.ArrayListUnmanaged(Compilation.Error),
|
||||||
|
|
||||||
const InnerError = error{
|
const InnerError = error{
|
||||||
OutOfMemory,
|
OutOfMemory,
|
||||||
|
AnalysisFail,
|
||||||
TooManyConstants,
|
TooManyConstants,
|
||||||
InvalidJump,
|
InvalidJump,
|
||||||
};
|
};
|
||||||
|
|
@ -37,8 +43,26 @@ pub fn deinit(sema: *Sema) void {
|
||||||
sema.* = undefined;
|
sema.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fail(_: *Sema, message: []const u8) InnerError {
|
pub const SrcLoc = struct {
|
||||||
@panic(message);
|
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 {
|
fn getConstant(sema: *Sema, data: Compilation.Constant) !Ref {
|
||||||
|
|
@ -61,23 +85,23 @@ fn addGlobal(sema: *Sema, name: Ir.NullTerminatedString) !Ref {
|
||||||
return .{ .global = interned.constant };
|
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 });
|
const interned = try sema.getConstant(.{ .string = name });
|
||||||
for (sema.ir.globals) |global| {
|
for (sema.ir.globals) |global| {
|
||||||
if (global.name == name) {
|
if (global.name == name) {
|
||||||
return .{ .global = interned.constant };
|
return .{ .global = interned.constant };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fail(sema, "unknown global variable");
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn irInteger(sema: *Sema, inst: Ir.Inst.Index) InnerError!Ref {
|
fn irInteger(sema: *Sema, inst: Ir.Inst.Index) InnerError!Ref {
|
||||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.integer;
|
const value = sema.ir.instructions[@intFromEnum(inst)].data.int;
|
||||||
return sema.getConstant(.{ .integer = data.value });
|
return sema.getConstant(.{ .integer = value });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn irString(sema: *Sema, inst: Ir.Inst.Index) InnerError!Ref {
|
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 });
|
return sema.getConstant(.{ .string = data.start });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -308,9 +332,20 @@ fn irImplicitRet(_: *Sema, chunk: *Chunk, _: Ir.Inst.Index) InnerError!Ref {
|
||||||
return chunk.addByteOp(.exit);
|
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 {
|
fn irDeclRef(sema: *Sema, _: *Chunk, inst: Ir.Inst.Index) InnerError!Ref {
|
||||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.string;
|
const data = sema.ir.instructions[@intFromEnum(inst)].data.str_tok;
|
||||||
return sema.getGlobal(data.start);
|
return resolveGlobal(sema, data.src_offset, data.start);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn irDeclVar(
|
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_gt => try irBinary(sema, chunk, inst, .cmp_gt),
|
||||||
.cmp_gte => try irBinary(sema, chunk, inst, .cmp_gte),
|
.cmp_gte => try irBinary(sema, chunk, inst, .cmp_gte),
|
||||||
.decl_ref => try irDeclRef(sema, chunk, inst),
|
.decl_ref => try irDeclRef(sema, chunk, inst),
|
||||||
.integer => try irInteger(sema, inst),
|
.int => try irInteger(sema, inst),
|
||||||
.string => try irString(sema, inst),
|
.str => try irString(sema, inst),
|
||||||
.condbr => try irCondBr(sema, chunk, inst),
|
.condbr => try irCondBr(sema, chunk, inst),
|
||||||
.@"break" => {
|
.@"break" => {
|
||||||
try irBreak(sema, inst);
|
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 data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||||
const extra = sema.ir.extraData(Ir.Inst.Block, data.payload_index);
|
const extra = sema.ir.extraData(Ir.Inst.Block, data.payload_index);
|
||||||
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ pub const Compilation = struct {
|
||||||
try w.splatByteAll(' ', column - 1);
|
try w.splatByteAll(' ', column - 1);
|
||||||
}
|
}
|
||||||
try w.writeAll("^\n");
|
try w.writeAll("^\n");
|
||||||
|
return w.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const CompileOptions = struct {
|
pub const CompileOptions = struct {
|
||||||
|
|
@ -78,7 +79,10 @@ pub const Compilation = struct {
|
||||||
|
|
||||||
var sema: Sema = .{
|
var sema: Sema = .{
|
||||||
.gpa = gpa,
|
.gpa = gpa,
|
||||||
.ir = &ir,
|
.arena = arena,
|
||||||
|
.tree = ast,
|
||||||
|
.ir = ir,
|
||||||
|
.errors = &errors,
|
||||||
};
|
};
|
||||||
defer sema.deinit();
|
defer sema.deinit();
|
||||||
|
|
||||||
|
|
@ -119,7 +123,12 @@ pub const Compilation = struct {
|
||||||
}
|
}
|
||||||
break :fatal true;
|
break :fatal true;
|
||||||
} else fatal: {
|
} else fatal: {
|
||||||
try sema.analyzeFile(.file_inst);
|
sema.analyzeFile(.file_inst) catch |err| switch (err) {
|
||||||
|
error.OutOfMemory => return error.OutOfMemory,
|
||||||
|
error.AnalysisFail => break :fatal true,
|
||||||
|
// TODO: These errors should be handled...
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
break :fatal false;
|
break :fatal false;
|
||||||
};
|
};
|
||||||
return .{
|
return .{
|
||||||
|
|
@ -195,13 +204,13 @@ pub const Compilation = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Loc = struct {
|
pub const Loc = struct {
|
||||||
line: usize,
|
line: usize,
|
||||||
column: usize,
|
column: usize,
|
||||||
source_line: []const u8,
|
source_line: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn findLineColumn(source: []const u8, byte_offset: usize) Loc {
|
pub fn findLineColumn(source: []const u8, byte_offset: usize) Loc {
|
||||||
var line: usize = 0;
|
var line: usize = 0;
|
||||||
var column: usize = 0;
|
var column: usize = 0;
|
||||||
var line_start: usize = 0;
|
var line_start: usize = 0;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,18 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Compilation = @import("compile.zig").Compilation;
|
const Compilation = @import("compile.zig").Compilation;
|
||||||
|
|
||||||
|
test "compiler: unknown global variable" {
|
||||||
|
try testEqual(
|
||||||
|
\\{a}
|
||||||
|
,
|
||||||
|
\\<STDIN>:1:2: error: unknown global variable
|
||||||
|
\\1 | {a}
|
||||||
|
\\ | ^
|
||||||
|
\\
|
||||||
|
,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
test "compiler: global variable shadowing" {
|
test "compiler: global variable shadowing" {
|
||||||
try testEqual(
|
try testEqual(
|
||||||
\\VAR a = 0
|
\\VAR a = 0
|
||||||
|
|
|
||||||
|
|
@ -49,12 +49,17 @@ pub const Writer = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeIntegerInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
fn writeIntegerInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
||||||
const data = self.code.instructions[@intFromEnum(inst)].data.integer;
|
const data = self.code.instructions[@intFromEnum(inst)].data.int;
|
||||||
try w.print("{d}", .{data.value});
|
try w.print("{d}", .{data});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeStringInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
fn writeStringInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
||||||
const data = self.code.instructions[@intFromEnum(inst)].data.string;
|
const data = self.code.instructions[@intFromEnum(inst)].data.str;
|
||||||
|
try self.writeStringRef(w, data.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeStrTokInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
||||||
|
const data = self.code.instructions[@intFromEnum(inst)].data.str_tok;
|
||||||
try self.writeStringRef(w, data.start);
|
try self.writeStringRef(w, data.start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,11 +128,6 @@ pub const Writer = struct {
|
||||||
try w.writeAll("]");
|
try w.writeAll("]");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeParamInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
|
||||||
const data = self.code.instructions[@intFromEnum(inst)].data.string;
|
|
||||||
try self.writeStringRef(w, data.start);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn writeBreakInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
fn writeBreakInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
||||||
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
|
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
|
||||||
const extra = self.code.extraData(Ir.Inst.Break, data.payload_index);
|
const extra = self.code.extraData(Ir.Inst.Break, data.payload_index);
|
||||||
|
|
@ -274,7 +274,7 @@ pub const Writer = struct {
|
||||||
.declaration => try self.writeDeclarationInst(w, inst),
|
.declaration => try self.writeDeclarationInst(w, inst),
|
||||||
.decl_knot => try self.writeKnotDeclInst(w, inst),
|
.decl_knot => try self.writeKnotDeclInst(w, inst),
|
||||||
.decl_var => try self.writeVarDeclInst(w, inst),
|
.decl_var => try self.writeVarDeclInst(w, inst),
|
||||||
.decl_ref => try self.writeStringInst(w, inst),
|
.decl_ref => try self.writeStrTokInst(w, inst),
|
||||||
.condbr => try self.writeCondbrInst(w, inst),
|
.condbr => try self.writeCondbrInst(w, inst),
|
||||||
.@"break" => try self.writeBreakInst(w, inst),
|
.@"break" => try self.writeBreakInst(w, inst),
|
||||||
.switch_br => try self.writeSwitchBrInst(w, inst),
|
.switch_br => try self.writeSwitchBrInst(w, inst),
|
||||||
|
|
@ -295,8 +295,8 @@ pub const Writer = struct {
|
||||||
.cmp_gte => try self.writeBinaryInst(w, inst),
|
.cmp_gte => try self.writeBinaryInst(w, inst),
|
||||||
.cmp_lt => try self.writeBinaryInst(w, inst),
|
.cmp_lt => try self.writeBinaryInst(w, inst),
|
||||||
.cmp_lte => try self.writeBinaryInst(w, inst),
|
.cmp_lte => try self.writeBinaryInst(w, inst),
|
||||||
.integer => try self.writeIntegerInst(w, inst),
|
.int => try self.writeIntegerInst(w, inst),
|
||||||
.string => try self.writeStringInst(w, inst),
|
.str => try self.writeStringInst(w, inst),
|
||||||
.content_push => try self.writeUnaryInst(w, inst),
|
.content_push => try self.writeUnaryInst(w, inst),
|
||||||
.content_flush => try self.writeUnaryInst(w, inst),
|
.content_flush => try self.writeUnaryInst(w, inst),
|
||||||
.choice_br => try self.writeChoiceBrInst(w, inst),
|
.choice_br => try self.writeChoiceBrInst(w, inst),
|
||||||
|
|
@ -306,7 +306,7 @@ pub const Writer = struct {
|
||||||
.field_call => try self.writeCallInst(w, inst, .field),
|
.field_call => try self.writeCallInst(w, inst, .field),
|
||||||
.field_divert => try self.writeCallInst(w, inst, .field),
|
.field_divert => try self.writeCallInst(w, inst, .field),
|
||||||
.field_ptr => try self.writeFieldPtrInst(w, inst),
|
.field_ptr => try self.writeFieldPtrInst(w, inst),
|
||||||
.param => try self.writeParamInst(w, inst),
|
.param => try self.writeStrTokInst(w, inst),
|
||||||
}
|
}
|
||||||
try w.writeAll(")");
|
try w.writeAll(")");
|
||||||
try w.writeAll("\n");
|
try w.writeAll("\n");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue