dusk: cover analyser errors and recursivly fetch type alias type
This commit is contained in:
parent
96ad6503d9
commit
0a790a38f3
3 changed files with 180 additions and 32 deletions
|
|
@ -38,50 +38,89 @@ pub fn structDecl(self: *Analyse, parent_scope: []const Ast.Index, node: Ast.Ind
|
||||||
const member_list = self.tree.spanToList(self.tree.nodeLHS(node));
|
const member_list = self.tree.spanToList(self.tree.nodeLHS(node));
|
||||||
for (member_list, 0..) |member_node, i| {
|
for (member_list, 0..) |member_node, i| {
|
||||||
try self.checkRedeclaration(member_list[i + 1 ..], member_node);
|
try self.checkRedeclaration(member_list[i + 1 ..], member_node);
|
||||||
|
|
||||||
const member_loc = self.tree.tokenLoc(self.tree.nodeToken(member_node));
|
const member_loc = self.tree.tokenLoc(self.tree.nodeToken(member_node));
|
||||||
const member_name = member_loc.slice(self.tree.source);
|
|
||||||
const member_type_node = self.tree.nodeRHS(member_node);
|
const member_type_node = self.tree.nodeRHS(member_node);
|
||||||
|
const member_type_loc = self.tree.tokenLoc(self.tree.nodeToken(member_type_node));
|
||||||
|
const member_type_name = member_type_loc.slice(self.tree.source);
|
||||||
|
|
||||||
switch (self.tree.nodeTag(member_type_node)) {
|
var inner_type = member_type_node;
|
||||||
.scalar_type,
|
while (true) {
|
||||||
.vector_type,
|
switch (self.tree.nodeTag(inner_type)) {
|
||||||
.matrix_type,
|
.scalar_type,
|
||||||
.atomic_type,
|
.vector_type,
|
||||||
=> {},
|
.matrix_type,
|
||||||
.array_type => {
|
.atomic_type,
|
||||||
if (self.tree.nodeRHS(member_type_node) == Ast.null_index and
|
=> {},
|
||||||
i != member_list.len - 1)
|
.array_type => {
|
||||||
{
|
if (self.tree.nodeRHS(member_type_node) == Ast.null_index and
|
||||||
|
i != member_list.len - 1)
|
||||||
|
{
|
||||||
|
try self.addError(
|
||||||
|
member_loc,
|
||||||
|
"struct member with runtime-sized array type, must be the last member of the structure",
|
||||||
|
.{},
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.user_type => {
|
||||||
|
const decl_node = try self.expectFindTypeAliasOrStructDeclNode(member_type_loc, parent_scope, member_type_name) orelse break;
|
||||||
|
if (self.tree.nodeTag(decl_node) == .type_alias) {
|
||||||
|
inner_type = self.tree.nodeLHS(decl_node);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {
|
||||||
try self.addError(
|
try self.addError(
|
||||||
member_loc,
|
member_loc,
|
||||||
"struct member with runtime-sized array type, must be the last member of the structure",
|
"invalid struct member type '{s}'",
|
||||||
.{},
|
.{member_type_name},
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
},
|
}
|
||||||
.user_type => {
|
break;
|
||||||
_ = self.findDeclNode(parent_scope, member_name) orelse {
|
}
|
||||||
try self.addError(
|
}
|
||||||
member_loc, // TODO
|
}
|
||||||
"use of undeclared identifier '{s}'",
|
|
||||||
.{member_name},
|
/// UNUSED
|
||||||
null,
|
/// returns the actual type of a type alias
|
||||||
);
|
pub fn fetchTypeAliasType(self: *Analyse, node: Ast.Index) void {
|
||||||
continue;
|
std.debug.assert(self.tree.nodeTag(node) == .type_alias);
|
||||||
};
|
if (self.tree.nodeTag(node) == .type_alias) {
|
||||||
},
|
return self.fetchTypeAliasType(self.tree.nodeLHS(node));
|
||||||
|
}
|
||||||
|
return self.tree.nodeLHS(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expectFindTypeAliasOrStructDeclNode(self: *Analyse, ref_loc: Token.Loc, scope_items: []const Ast.Index, name: []const u8) !?Ast.Index {
|
||||||
|
if (try self.expectFindDeclNode(ref_loc, scope_items, name)) |decl_node| {
|
||||||
|
switch (self.tree.nodeTag(decl_node)) {
|
||||||
|
.struct_decl, .type_alias => return decl_node,
|
||||||
else => {
|
else => {
|
||||||
try self.addError(
|
try self.addError(
|
||||||
member_loc,
|
ref_loc,
|
||||||
"invalid struct member type '{s}'",
|
"'{s}' is neither an struct or type alias",
|
||||||
.{member_name},
|
.{name},
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expectFindDeclNode(self: *Analyse, ref_loc: Token.Loc, scope_items: []const Ast.Index, name: []const u8) !?Ast.Index {
|
||||||
|
return self.findDeclNode(scope_items, name) orelse {
|
||||||
|
try self.addError(
|
||||||
|
ref_loc,
|
||||||
|
"use of undeclared identifier '{s}'",
|
||||||
|
.{name},
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn findDeclNode(self: *Analyse, scope_items: []const Ast.Index, name: []const u8) ?Ast.Index {
|
pub fn findDeclNode(self: *Analyse, scope_items: []const Ast.Index, name: []const u8) ?Ast.Index {
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ pub const Extension = enum {
|
||||||
pub const ErrorMsg = struct {
|
pub const ErrorMsg = struct {
|
||||||
loc: Token.Loc,
|
loc: Token.Loc,
|
||||||
msg: []const u8,
|
msg: []const u8,
|
||||||
note: ?Note,
|
note: ?Note = null,
|
||||||
|
|
||||||
pub const Note = struct {
|
pub const Note = struct {
|
||||||
loc: ?Token.Loc,
|
loc: ?Token.Loc = null,
|
||||||
msg: []const u8,
|
msg: []const u8,
|
||||||
|
|
||||||
pub fn create(
|
pub fn create(
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,60 @@ fn expectTree(source: [:0]const u8) !dusk.Ast {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expectError(source: [:0]const u8, err: dusk.ErrorMsg) !void {
|
||||||
|
var res = try dusk.Ast.parse(allocator, source);
|
||||||
|
const err_list = switch (res) {
|
||||||
|
.tree => |*tree| blk: {
|
||||||
|
defer tree.deinit(allocator);
|
||||||
|
if (try tree.analyse(allocator)) |errors| {
|
||||||
|
break :blk errors;
|
||||||
|
}
|
||||||
|
return error.ExpectedError;
|
||||||
|
},
|
||||||
|
.errors => |err_msgs| err_msgs,
|
||||||
|
};
|
||||||
|
defer {
|
||||||
|
for (err_list) |*err_msg| err_msg.deinit(allocator);
|
||||||
|
allocator.free(err_list);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
errdefer {
|
||||||
|
std.debug.print(
|
||||||
|
"\n\x1b[31mexpected error({d}..{d}):\n{s}\n\x1b[32mactual error({d}..{d}):\n{s}\n\x1b[0m",
|
||||||
|
.{
|
||||||
|
err.loc.start, err.loc.end, err.msg,
|
||||||
|
err_list[0].loc.start, err_list[0].loc.end, err_list[0].msg,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try expect(std.mem.eql(u8, err.msg, err_list[0].msg));
|
||||||
|
try expect(err_list[0].loc.start == err.loc.start);
|
||||||
|
try expect(err_list[0].loc.end == err.loc.end);
|
||||||
|
}
|
||||||
|
if (err_list[0].note) |_| {
|
||||||
|
errdefer {
|
||||||
|
std.debug.print(
|
||||||
|
"\n\x1b[31mexpected note msg:\n{s}\n\x1b[32mactual note msg:\n{s}\n\x1b[0m",
|
||||||
|
.{ err.note.?.msg, err_list[0].note.?.msg },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try expect(std.mem.eql(u8, err.note.?.msg, err_list[0].note.?.msg));
|
||||||
|
if (err_list[0].note.?.loc) |_| {
|
||||||
|
errdefer {
|
||||||
|
std.debug.print(
|
||||||
|
"\n\x1b[31mexpected note loc: {d}..{d}\n\x1b[32mactual note loc: {d}..{d}\n\x1b[0m",
|
||||||
|
.{
|
||||||
|
err.note.?.loc.?.start, err.note.?.loc.?.end,
|
||||||
|
err_list[0].note.?.loc.?.start, err_list[0].note.?.loc.?.end,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try expect(err_list[0].note.?.loc.?.start == err.note.?.loc.?.start);
|
||||||
|
try expect(err_list[0].note.?.loc.?.end == err.note.?.loc.?.end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "empty" {
|
test "empty" {
|
||||||
const source = "";
|
const source = "";
|
||||||
var tree = try expectTree(source);
|
var tree = try expectTree(source);
|
||||||
|
|
@ -168,3 +222,58 @@ test "variable & expressions" {
|
||||||
const @"7" = tree.nodeRHS(@"6 >> 7");
|
const @"7" = tree.nodeRHS(@"6 >> 7");
|
||||||
try expect(tree.nodeTag(@"7") == .number_literal);
|
try expect(tree.nodeTag(@"7") == .number_literal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "analyser errors" {
|
||||||
|
{
|
||||||
|
const source = "^";
|
||||||
|
try expectError(source, .{
|
||||||
|
.msg = "expected global declaration, found '^'",
|
||||||
|
.loc = .{ .start = 0, .end = 1 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const source = "struct S { m0: array<f32>, m1: f32 }";
|
||||||
|
try expectError(source, .{
|
||||||
|
.msg = "struct member with runtime-sized array type, must be the last member of the structure",
|
||||||
|
.loc = .{ .start = 11, .end = 13 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const source = "struct S0 { m: S1 }";
|
||||||
|
try expectError(source, .{
|
||||||
|
.msg = "use of undeclared identifier 'S1'",
|
||||||
|
.loc = .{ .start = 15, .end = 17 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const source =
|
||||||
|
\\var S1 = 0;
|
||||||
|
\\struct S0 { m: S1 }
|
||||||
|
;
|
||||||
|
try expectError(source, .{
|
||||||
|
.msg = "'S1' is neither an struct or type alias",
|
||||||
|
.loc = .{ .start = 27, .end = 29 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const source =
|
||||||
|
\\type T = sampler;
|
||||||
|
\\struct S0 { m: T }
|
||||||
|
;
|
||||||
|
try expectError(source, .{
|
||||||
|
.msg = "invalid struct member type 'T'",
|
||||||
|
.loc = .{ .start = 30, .end = 31 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const source =
|
||||||
|
\\var d1 = 0;
|
||||||
|
\\var d1 = 0;
|
||||||
|
;
|
||||||
|
try expectError(source, .{
|
||||||
|
.msg = "redeclaration of 'd1'",
|
||||||
|
.loc = .{ .start = 16, .end = 18 },
|
||||||
|
.note = .{ .msg = "other declaration here", .loc = .{ .start = 4, .end = 6 } },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue