| ... | @@ -48,31 +48,21 @@ pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!*Tree { | ... | @@ -48,31 +48,21 @@ pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!*Tree { |
| 48 | | 48 | |
| 49 | while (it.peek().?.id == .LineComment) _ = it.next(); | 49 | while (it.peek().?.id == .LineComment) _ = it.next(); |
| 50 | | 50 | |
| 51 | tree.root_node = parseRoot(arena, &it, tree) catch |err| blk: { | 51 | tree.root_node = try parseRoot(arena, &it, tree); |
| 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 | }; | | |
| 62 | | 52 | |
| 63 | return tree; | 53 | return tree; |
| 64 | } | 54 | } |
| 65 | | 55 | |
| 66 | /// Root <- skip ContainerMembers eof | 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 | const node = try arena.create(Node.Root); | 58 | const node = try arena.create(Node.Root); |
| 69 | node.* = .{ | 59 | node.* = .{ |
| 70 | .decls = try parseContainerMembers(arena, it, tree), | 60 | .decls = try parseContainerMembers(arena, it, tree), |
| 71 | .eof_token = eatToken(it, .Eof) orelse { | 61 | .eof_token = eatToken(it, .Eof) orelse blk: { |
| 72 | try tree.errors.push(.{ | 62 | try tree.errors.push(.{ |
| 73 | .ExpectedContainerMembers = .{ .token = it.index }, | 63 | .ExpectedContainerMembers = .{ .token = it.index }, |
| 74 | }); | 64 | }); |
| 75 | return error.ParseError; | 65 | break :blk undefined; |
| 76 | }, | 66 | }, |
| 77 | }; | 67 | }; |
| 78 | return node; | 68 | return node; |
| ... | @@ -85,7 +75,7 @@ fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!*Node.Roo | ... | @@ -85,7 +75,7 @@ fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!*Node.Roo |
| 85 | /// / KEYWORD_pub? ContainerField COMMA ContainerMembers | 75 | /// / KEYWORD_pub? ContainerField COMMA ContainerMembers |
| 86 | /// / KEYWORD_pub? ContainerField | 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 | var list = Node.Root.DeclList.init(arena); | 79 | var list = Node.Root.DeclList.init(arena); |
| 90 | | 80 | |
| 91 | var field_state: union(enum) { | 81 | var field_state: union(enum) { |
| ... | @@ -108,7 +98,13 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No | ... | @@ -108,7 +98,13 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No |
| 108 | | 98 | |
| 109 | const doc_comments = try parseDocComment(arena, it, tree); | 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 | if (field_state == .seen) { | 108 | if (field_state == .seen) { |
| 113 | field_state = .{ .end = node.firstToken() }; | 109 | field_state = .{ .end = node.firstToken() }; |
| 114 | } | 110 | } |
| ... | @@ -117,7 +113,13 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No | ... | @@ -117,7 +113,13 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No |
| 117 | continue; | 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 | if (field_state == .seen) { | 123 | if (field_state == .seen) { |
| 122 | field_state = .{ .end = node.firstToken() }; | 124 | field_state = .{ .end = node.firstToken() }; |
| 123 | } | 125 | } |
| ... | @@ -128,7 +130,15 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No | ... | @@ -128,7 +130,15 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No |
| 128 | | 130 | |
| 129 | const visib_token = eatToken(it, .Keyword_pub); | 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 | if (field_state == .seen) { | 142 | if (field_state == .seen) { |
| 133 | field_state = .{ .end = visib_token orelse node.firstToken() }; | 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,10 +173,17 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No |
| 163 | try tree.errors.push(.{ | 173 | try tree.errors.push(.{ |
| 164 | .ExpectedPubItem = .{ .token = it.index }, | 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 | switch (field_state) { | 187 | switch (field_state) { |
| 171 | .none => field_state = .seen, | 188 | .none => field_state = .seen, |
| 172 | .err, .seen => {}, | 189 | .err, .seen => {}, |
| ... | @@ -200,8 +217,39 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No | ... | @@ -200,8 +217,39 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No |
| 200 | return list; | 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 | /// Eat a multiline container doc comment | 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 | var lines = Node.DocComment.LineList.init(arena); | 253 | var lines = Node.DocComment.LineList.init(arena); |
| 206 | while (eatToken(it, .ContainerDocComment)) |line| { | 254 | while (eatToken(it, .ContainerDocComment)) |line| { |
| 207 | try lines.push(line); | 255 | try lines.push(line); |
| ... | @@ -687,8 +735,13 @@ fn parseLoopStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod | ... | @@ -687,8 +735,13 @@ fn parseLoopStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod |
| 687 | node.cast(Node.While).?.inline_token = inline_token; | 735 | node.cast(Node.While).?.inline_token = inline_token; |
| 688 | return node; | 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 | /// ForStatement | 747 | /// ForStatement |
| ... | @@ -2925,7 +2978,7 @@ fn parseDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.D | ... | @@ -2925,7 +2978,7 @@ fn parseDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.D |
| 2925 | } | 2978 | } |
| 2926 | | 2979 | |
| 2927 | /// Eat a single-line doc comment on the same line as another node | 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 | const comment_token = eatToken(it, .DocComment) orelse return null; | 2982 | const comment_token = eatToken(it, .DocComment) orelse return null; |
| 2930 | if (tree.tokensOnSameLine(after_token, comment_token)) { | 2983 | if (tree.tokensOnSameLine(after_token, comment_token)) { |
| 2931 | const node = try arena.create(Node.DocComment); | 2984 | const node = try arena.create(Node.DocComment); |