From 0a790a38f311cb8b70e96de5db095924242bce72 Mon Sep 17 00:00:00 2001 From: Ali Chraghi Date: Tue, 7 Mar 2023 18:34:57 +0330 Subject: [PATCH] dusk: cover analyser errors and recursivly fetch type alias type --- libs/dusk/src/Analyse.zig | 99 +++++++++++++++++++++++----------- libs/dusk/src/main.zig | 4 +- libs/dusk/test/main.zig | 109 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 32 deletions(-) diff --git a/libs/dusk/src/Analyse.zig b/libs/dusk/src/Analyse.zig index 8dd29d32..b9fdd88d 100644 --- a/libs/dusk/src/Analyse.zig +++ b/libs/dusk/src/Analyse.zig @@ -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)); for (member_list, 0..) |member_node, i| { try self.checkRedeclaration(member_list[i + 1 ..], 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_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)) { - .scalar_type, - .vector_type, - .matrix_type, - .atomic_type, - => {}, - .array_type => { - if (self.tree.nodeRHS(member_type_node) == Ast.null_index and - i != member_list.len - 1) - { + var inner_type = member_type_node; + while (true) { + switch (self.tree.nodeTag(inner_type)) { + .scalar_type, + .vector_type, + .matrix_type, + .atomic_type, + => {}, + .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( 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, ); - } - }, - .user_type => { - _ = self.findDeclNode(parent_scope, member_name) orelse { - try self.addError( - member_loc, // TODO - "use of undeclared identifier '{s}'", - .{member_name}, - null, - ); - continue; - }; - }, + }, + } + break; + } + } +} + +/// UNUSED +/// returns the actual type of a type alias +pub fn fetchTypeAliasType(self: *Analyse, node: Ast.Index) void { + 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 => { try self.addError( - member_loc, - "invalid struct member type '{s}'", - .{member_name}, + ref_loc, + "'{s}' is neither an struct or type alias", + .{name}, 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 { diff --git a/libs/dusk/src/main.zig b/libs/dusk/src/main.zig index f4b16791..f9153a80 100644 --- a/libs/dusk/src/main.zig +++ b/libs/dusk/src/main.zig @@ -15,10 +15,10 @@ pub const Extension = enum { pub const ErrorMsg = struct { loc: Token.Loc, msg: []const u8, - note: ?Note, + note: ?Note = null, pub const Note = struct { - loc: ?Token.Loc, + loc: ?Token.Loc = null, msg: []const u8, pub fn create( diff --git a/libs/dusk/test/main.zig b/libs/dusk/test/main.zig index 3eb9b885..e1bafaf8 100644 --- a/libs/dusk/test/main.zig +++ b/libs/dusk/test/main.zig @@ -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" { const source = ""; var tree = try expectTree(source); @@ -168,3 +222,58 @@ test "variable & expressions" { const @"7" = tree.nodeRHS(@"6 >> 7"); 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, 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 } }, + }); + } +}