| ... | ... | @@ -48,31 +48,21 @@ pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!*Tree { |
| 48 | 48 | |
| 49 | 49 | while (it.peek().?.id == .LineComment) _ = it.next(); |
| 50 | 50 | |
| 51 | | tree.root_node = parseRoot(arena, &it, tree) catch |err| blk: { |
| 52 | | switch (err) { |
| 53 | | error.ParseError => { |
| 54 | | assert(tree.errors.len != 0); |
| 55 | | break :blk undefined; |
| 56 | | }, |
| 57 | | error.OutOfMemory => { |
| 58 | | return error.OutOfMemory; |
| 59 | | }, |
| 60 | | } |
| 61 | | }; |
| 51 | tree.root_node = try parseRoot(arena, &it, tree); |
| 62 | 52 | |
| 63 | 53 | return tree; |
| 64 | 54 | } |
| 65 | 55 | |
| 66 | 56 | /// Root <- skip ContainerMembers eof |
| 67 | | fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!*Node.Root { |
| 57 | fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocator.Error!*Node.Root { |
| 68 | 58 | const node = try arena.create(Node.Root); |
| 69 | 59 | node.* = .{ |
| 70 | 60 | .decls = try parseContainerMembers(arena, it, tree), |
| 71 | | .eof_token = eatToken(it, .Eof) orelse { |
| 61 | .eof_token = eatToken(it, .Eof) orelse blk: { |
| 72 | 62 | try tree.errors.push(.{ |
| 73 | 63 | .ExpectedContainerMembers = .{ .token = it.index }, |
| 74 | 64 | }); |
| 75 | | return error.ParseError; |
| 65 | break :blk undefined; |
| 76 | 66 | }, |
| 77 | 67 | }; |
| 78 | 68 | return node; |
| ... | ... | @@ -85,7 +75,7 @@ fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!*Node.Roo |
| 85 | 75 | /// / KEYWORD_pub? ContainerField COMMA ContainerMembers |
| 86 | 76 | /// / KEYWORD_pub? ContainerField |
| 87 | 77 | /// / |
| 88 | | fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !Node.Root.DeclList { |
| 78 | fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocator.Error!Node.Root.DeclList { |
| 89 | 79 | var list = Node.Root.DeclList.init(arena); |
| 90 | 80 | |
| 91 | 81 | var field_state: union(enum) { |
| ... | ... | @@ -108,7 +98,13 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No |
| 108 | 98 | |
| 109 | 99 | const doc_comments = try parseDocComment(arena, it, tree); |
| 110 | 100 | |
| 111 | | if (try parseTestDecl(arena, it, tree)) |node| { |
| 101 | if (parseTestDecl(arena, it, tree) catch |err| switch (err) { |
| 102 | error.OutOfMemory => return error.OutOfMemory, |
| 103 | error.ParseError => { |
| 104 | findEndOfBlock(it); |
| 105 | continue; |
| 106 | }, |
| 107 | }) |node| { |
| 112 | 108 | if (field_state == .seen) { |
| 113 | 109 | field_state = .{ .end = node.firstToken() }; |
| 114 | 110 | } |
| ... | ... | @@ -117,7 +113,13 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No |
| 117 | 113 | continue; |
| 118 | 114 | } |
| 119 | 115 | |
| 120 | | if (try parseTopLevelComptime(arena, it, tree)) |node| { |
| 116 | if (parseTopLevelComptime(arena, it, tree) catch |err| switch (err) { |
| 117 | error.OutOfMemory => return error.OutOfMemory, |
| 118 | error.ParseError => { |
| 119 | findEndOfBlock(it); |
| 120 | continue; |
| 121 | }, |
| 122 | }) |node| { |
| 121 | 123 | if (field_state == .seen) { |
| 122 | 124 | field_state = .{ .end = node.firstToken() }; |
| 123 | 125 | } |
| ... | ... | @@ -128,7 +130,15 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No |
| 128 | 130 | |
| 129 | 131 | const visib_token = eatToken(it, .Keyword_pub); |
| 130 | 132 | |
| 131 | | if (try parseTopLevelDecl(arena, it, tree)) |node| { |
| 133 | if (parseTopLevelDecl(arena, it, tree) catch |err| switch (err) { |
| 134 | error.OutOfMemory => return error.OutOfMemory, |
| 135 | error.ParseError => { |
| 136 | // attempt to recover by finding a semicolon |
| 137 | // TODO if this was a function with a body we should use findEndOfBlock |
| 138 | findToken(it, .Semicolon); |
| 139 | continue; |
| 140 | }, |
| 141 | }) |node| { |
| 132 | 142 | if (field_state == .seen) { |
| 133 | 143 | field_state = .{ .end = visib_token orelse node.firstToken() }; |
| 134 | 144 | } |
| ... | ... | @@ -163,10 +173,17 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No |
| 163 | 173 | try tree.errors.push(.{ |
| 164 | 174 | .ExpectedPubItem = .{ .token = it.index }, |
| 165 | 175 | }); |
| 166 | | return error.ParseError; |
| 176 | // ignore this pub |
| 167 | 177 | } |
| 168 | 178 | |
| 169 | | if (try parseContainerField(arena, it, tree)) |node| { |
| 179 | if (parseContainerField(arena, it, tree) catch |err| switch (err) { |
| 180 | error.OutOfMemory => return error.OutOfMemory, |
| 181 | error.ParseError => { |
| 182 | // attempt to recover by finding a comma |
| 183 | findToken(it, .Comma); |
| 184 | continue; |
| 185 | }, |
| 186 | }) |node| { |
| 170 | 187 | switch (field_state) { |
| 171 | 188 | .none => field_state = .seen, |
| 172 | 189 | .err, .seen => {}, |
| ... | ... | @@ -200,8 +217,39 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No |
| 200 | 217 | return list; |
| 201 | 218 | } |
| 202 | 219 | |
| 220 | /// Attempts to find a closing brace, assumes the opening brace was found. |
| 221 | fn findEndOfBlock(it: *TokenIterator) void { |
| 222 | var count: u32 = 1; |
| 223 | while (it.next()) |tok| switch (tok.id) { |
| 224 | .LBrace => count += 1, |
| 225 | .RBrace => { |
| 226 | count -= 1; |
| 227 | if (count == 0) return; |
| 228 | }, |
| 229 | else => {}, |
| 230 | }; |
| 231 | } |
| 232 | |
| 233 | /// Attempts to find `wanted` token, keeps track of parentheses. |
| 234 | fn findToken(it: *TokenIterator, wanted: Token.Id) void { |
| 235 | var count: u32 = 0; |
| 236 | while (it.next()) |tok| switch (tok.id) { |
| 237 | .LParen, .LBracket, .LBrace => count += 1, |
| 238 | .RParen, .RBracket, .RBrace => { |
| 239 | if (count == 0) { |
| 240 | _ = it.prev(); |
| 241 | return; |
| 242 | } |
| 243 | count -= 1; |
| 244 | }, |
| 245 | else => { |
| 246 | if (tok.id == wanted and count == 0) return; |
| 247 | }, |
| 248 | }; |
| 249 | } |
| 250 | |
| 203 | 251 | /// Eat a multiline container doc comment |
| 204 | | fn parseContainerDocComments(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { |
| 252 | fn parseContainerDocComments(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocator.Error!?*Node { |
| 205 | 253 | var lines = Node.DocComment.LineList.init(arena); |
| 206 | 254 | while (eatToken(it, .ContainerDocComment)) |line| { |
| 207 | 255 | try lines.push(line); |
| ... | ... | @@ -687,8 +735,13 @@ fn parseLoopStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod |
| 687 | 735 | node.cast(Node.While).?.inline_token = inline_token; |
| 688 | 736 | return node; |
| 689 | 737 | } |
| 738 | if (inline_token == null) return null; |
| 690 | 739 | |
| 691 | | return null; |
| 740 | // If we've seen "inline", there should have been a "for" or "while" |
| 741 | try tree.errors.push(.{ |
| 742 | .ExpectedInlinable = .{ .token = it.index }, |
| 743 | }); |
| 744 | return error.ParseError; |
| 692 | 745 | } |
| 693 | 746 | |
| 694 | 747 | /// ForStatement |
| ... | ... | @@ -2925,7 +2978,7 @@ fn parseDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.D |
| 2925 | 2978 | } |
| 2926 | 2979 | |
| 2927 | 2980 | /// Eat a single-line doc comment on the same line as another node |
| 2928 | | fn parseAppendedDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree, after_token: TokenIndex) !?*Node.DocComment { |
| 2981 | fn parseAppendedDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree, after_token: TokenIndex) Allocator.Error!?*Node.DocComment { |
| 2929 | 2982 | const comment_token = eatToken(it, .DocComment) orelse return null; |
| 2930 | 2983 | if (tree.tokensOnSameLine(after_token, comment_token)) { |
| 2931 | 2984 | const node = try arena.create(Node.DocComment); |