| ... | @@ -7,3303 +7,3349 @@ const Tree = ast.Tree; | ... | @@ -7,3303 +7,3349 @@ const Tree = ast.Tree; |
| 7 | const AstError = ast.Error; | 7 | const AstError = ast.Error; |
| 8 | const TokenIndex = ast.TokenIndex; | 8 | const TokenIndex = ast.TokenIndex; |
| 9 | const Token = std.zig.Token; | 9 | const Token = std.zig.Token; |
| 10 | const TokenIterator = Tree.TokenList.Iterator; | | |
| 11 | | 10 | |
| 12 | pub const Error = error{ParseError} || Allocator.Error; | 11 | pub const Error = error{ParseError} || Allocator.Error; |
| 13 | | 12 | |
| 14 | /// Result should be freed with tree.deinit() when there are | 13 | /// Result should be freed with tree.deinit() when there are |
| 15 | /// no more references to any of the tokens or nodes. | 14 | /// no more references to any of the tokens or nodes. |
| 16 | pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!*Tree { | 15 | pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!*Tree { |
| 17 | const tree = blk: { | 16 | // TODO optimization idea: ensureCapacity on the tokens list and |
| 18 | // This block looks unnecessary, but is a "foot-shield" to prevent the SegmentedLists | 17 | // then appendAssumeCapacity inside the loop. |
| 19 | // from being initialized with a pointer to this `arena`, which is created on | 18 | var tokens = std.ArrayList(Token).init(gpa); |
| 20 | // the stack. Following code should instead refer to `&tree.arena_allocator`, a | 19 | defer tokens.deinit(); |
| 21 | // pointer to data which lives safely on the heap and will outlive `parse`. See: | | |
| 22 | // https://github.com/ziglang/zig/commit/cb4fb14b6e66bd213575f69eec9598be8394fae6 | | |
| 23 | var arena = std.heap.ArenaAllocator.init(allocator); | | |
| 24 | errdefer arena.deinit(); | | |
| 25 | const tree = try arena.allocator.create(ast.Tree); | | |
| 26 | tree.* = .{ | | |
| 27 | .source = source, | | |
| 28 | .root_node = undefined, | | |
| 29 | .arena_allocator = arena, | | |
| 30 | .tokens = undefined, | | |
| 31 | .errors = undefined, | | |
| 32 | }; | | |
| 33 | break :blk tree; | | |
| 34 | }; | | |
| 35 | errdefer tree.deinit(); | | |
| 36 | const arena = &tree.arena_allocator.allocator; | | |
| 37 | | | |
| 38 | tree.tokens = ast.Tree.TokenList.init(arena); | | |
| 39 | tree.errors = ast.Tree.ErrorList.init(arena); | | |
| 40 | | 20 | |
| 41 | var tokenizer = std.zig.Tokenizer.init(source); | 21 | var tokenizer = std.zig.Tokenizer.init(source); |
| 42 | while (true) { | 22 | while (true) { |
| 43 | const tree_token = try tree.tokens.addOne(); | 23 | const tree_token = try tokens.addOne(); |
| 44 | tree_token.* = tokenizer.next(); | 24 | tree_token.* = tokenizer.next(); |
| 45 | if (tree_token.id == .Eof) break; | 25 | if (tree_token.id == .Eof) break; |
| 46 | } | 26 | } |
| 47 | var it = tree.tokens.iterator(0); | | |
| 48 | | 27 | |
| 49 | while (it.peek().?.id == .LineComment) _ = it.next(); | 28 | var parser: Parser = .{ |
| | 29 | .source = source, |
| | 30 | .arena = std.heap.ArenaAllocator.init(gpa), |
| | 31 | .gpa = gpa, |
| | 32 | .tokens = tokens.items, |
| | 33 | .errors = .{}, |
| | 34 | .tok_i = 0, |
| | 35 | }; |
| | 36 | defer parser.errors.deinit(gpa); |
| | 37 | errdefer parser.arena.deinit(); |
| 50 | | 38 | |
| 51 | tree.root_node = try parseRoot(arena, &it, tree); | 39 | while (tokens.items[parser.tok_i].id == .LineComment) parser.tok_i += 1; |
| 52 | | 40 | |
| 53 | return tree; | 41 | const root_node = try parser.parseRoot(); |
| 54 | } | | |
| 55 | | 42 | |
| 56 | /// Root <- skip ContainerMembers eof | 43 | const tree = try parser.arena.allocator.create(Tree); |
| 57 | fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocator.Error!*Node.Root { | 44 | tree.* = .{ |
| 58 | const node = try arena.create(Node.Root); | 45 | .gpa = gpa, |
| 59 | node.* = .{ | 46 | .source = source, |
| 60 | .decls = try parseContainerMembers(arena, it, tree, true), | 47 | .tokens = tokens.toOwnedSlice(), |
| 61 | // parseContainerMembers will try to skip as much | 48 | .errors = parser.errors.toOwnedSlice(gpa), |
| 62 | // invalid tokens as it can so this can only be the EOF | 49 | .root_node = root_node, |
| 63 | .eof_token = eatToken(it, .Eof).?, | 50 | .arena = parser.arena.state, |
| 64 | }; | 51 | }; |
| 65 | return node; | 52 | return tree; |
| 66 | } | 53 | } |
| 67 | | 54 | |
| 68 | /// ContainerMembers | 55 | /// Represents in-progress parsing, will be converted to an ast.Tree after completion. |
| 69 | /// <- TestDecl ContainerMembers | 56 | const Parser = struct { |
| 70 | /// / TopLevelComptime ContainerMembers | 57 | arena: std.heap.ArenaAllocator, |
| 71 | /// / KEYWORD_pub? TopLevelDecl ContainerMembers | 58 | gpa: *Allocator, |
| 72 | /// / ContainerField COMMA ContainerMembers | 59 | source: []const u8, |
| 73 | /// / ContainerField | 60 | tokens: []const Token, |
| 74 | /// / | 61 | tok_i: TokenIndex, |
| 75 | fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree, top_level: bool) !Node.Root.DeclList { | 62 | errors: std.ArrayListUnmanaged(AstError), |
| 76 | var list = Node.Root.DeclList.init(arena); | | |
| 77 | | | |
| 78 | var field_state: union(enum) { | | |
| 79 | /// no fields have been seen | | |
| 80 | none, | | |
| 81 | /// currently parsing fields | | |
| 82 | seen, | | |
| 83 | /// saw fields and then a declaration after them. | | |
| 84 | /// payload is first token of previous declaration. | | |
| 85 | end: TokenIndex, | | |
| 86 | /// ther was a declaration between fields, don't report more errors | | |
| 87 | err, | | |
| 88 | } = .none; | | |
| 89 | | 63 | |
| 90 | while (true) { | 64 | /// Root <- skip ContainerMembers eof |
| 91 | if (try parseContainerDocComments(arena, it, tree)) |node| { | 65 | fn parseRoot(p: *Parser) Allocator.Error!*Node.Root { |
| 92 | try list.push(node); | 66 | const node = try p.arena.allocator.create(Node.Root); |
| 93 | continue; | 67 | node.* = .{ |
| 94 | } | 68 | .decls = try parseContainerMembers(p, true), |
| | 69 | // parseContainerMembers will try to skip as much |
| | 70 | // invalid tokens as it can so this can only be the EOF |
| | 71 | .eof_token = p.eatToken(.Eof).?, |
| | 72 | }; |
| | 73 | return node; |
| | 74 | } |
| 95 | | 75 | |
| 96 | const doc_comments = try parseDocComment(arena, it, tree); | 76 | fn llpush( |
| | 77 | p: *Parser, |
| | 78 | comptime T: type, |
| | 79 | it: *?*std.SinglyLinkedList(T).Node, |
| | 80 | data: T, |
| | 81 | ) !*?*std.SinglyLinkedList(T).Node { |
| | 82 | const llnode = try p.arena.allocator.create(std.SinglyLinkedList(T).Node); |
| | 83 | llnode.* = .{ .data = data }; |
| | 84 | it.* = llnode; |
| | 85 | return &llnode.next; |
| | 86 | } |
| | 87 | |
| | 88 | /// ContainerMembers |
| | 89 | /// <- TestDecl ContainerMembers |
| | 90 | /// / TopLevelComptime ContainerMembers |
| | 91 | /// / KEYWORD_pub? TopLevelDecl ContainerMembers |
| | 92 | /// / ContainerField COMMA ContainerMembers |
| | 93 | /// / ContainerField |
| | 94 | /// / |
| | 95 | fn parseContainerMembers(p: *Parser, top_level: bool) !Node.Root.DeclList { |
| | 96 | var list = Node.Root.DeclList{}; |
| | 97 | var list_it = &list.first; |
| | 98 | |
| | 99 | var field_state: union(enum) { |
| | 100 | /// no fields have been seen |
| | 101 | none, |
| | 102 | /// currently parsing fields |
| | 103 | seen, |
| | 104 | /// saw fields and then a declaration after them. |
| | 105 | /// payload is first token of previous declaration. |
| | 106 | end: TokenIndex, |
| | 107 | /// ther was a declaration between fields, don't report more errors |
| | 108 | err, |
| | 109 | } = .none; |
| 97 | | 110 | |
| 98 | if (parseTestDecl(arena, it, tree) catch |err| switch (err) { | 111 | while (true) { |
| 99 | error.OutOfMemory => return error.OutOfMemory, | 112 | if (try p.parseContainerDocComments()) |node| { |
| 100 | error.ParseError => { | 113 | list_it = try p.llpush(*Node, list_it, node); |
| 101 | findNextContainerMember(it); | | |
| 102 | continue; | 114 | continue; |
| 103 | }, | | |
| 104 | }) |node| { | | |
| 105 | if (field_state == .seen) { | | |
| 106 | field_state = .{ .end = node.firstToken() }; | | |
| 107 | } | 115 | } |
| 108 | node.cast(Node.TestDecl).?.doc_comments = doc_comments; | | |
| 109 | try list.push(node); | | |
| 110 | continue; | | |
| 111 | } | | |
| 112 | | 116 | |
| 113 | if (parseTopLevelComptime(arena, it, tree) catch |err| switch (err) { | 117 | const doc_comments = try p.parseDocComment(); |
| 114 | error.OutOfMemory => return error.OutOfMemory, | 118 | |
| 115 | error.ParseError => { | 119 | if (p.parseTestDecl() catch |err| switch (err) { |
| 116 | findNextContainerMember(it); | 120 | error.OutOfMemory => return error.OutOfMemory, |
| | 121 | error.ParseError => { |
| | 122 | p.findNextContainerMember(); |
| | 123 | continue; |
| | 124 | }, |
| | 125 | }) |node| { |
| | 126 | if (field_state == .seen) { |
| | 127 | field_state = .{ .end = node.firstToken() }; |
| | 128 | } |
| | 129 | node.cast(Node.TestDecl).?.doc_comments = doc_comments; |
| | 130 | list_it = try p.llpush(*Node, list_it, node); |
| 117 | continue; | 131 | continue; |
| 118 | }, | | |
| 119 | }) |node| { | | |
| 120 | if (field_state == .seen) { | | |
| 121 | field_state = .{ .end = node.firstToken() }; | | |
| 122 | } | 132 | } |
| 123 | node.cast(Node.Comptime).?.doc_comments = doc_comments; | | |
| 124 | try list.push(node); | | |
| 125 | continue; | | |
| 126 | } | | |
| 127 | | | |
| 128 | const visib_token = eatToken(it, .Keyword_pub); | | |
| 129 | | 133 | |
| 130 | if (parseTopLevelDecl(arena, it, tree) catch |err| switch (err) { | 134 | if (p.parseTopLevelComptime() catch |err| switch (err) { |
| 131 | error.OutOfMemory => return error.OutOfMemory, | 135 | error.OutOfMemory => return error.OutOfMemory, |
| 132 | error.ParseError => { | 136 | error.ParseError => { |
| 133 | findNextContainerMember(it); | 137 | p.findNextContainerMember(); |
| | 138 | continue; |
| | 139 | }, |
| | 140 | }) |node| { |
| | 141 | if (field_state == .seen) { |
| | 142 | field_state = .{ .end = node.firstToken() }; |
| | 143 | } |
| | 144 | node.cast(Node.Comptime).?.doc_comments = doc_comments; |
| | 145 | list_it = try p.llpush(*Node, list_it, node); |
| 134 | continue; | 146 | continue; |
| 135 | }, | | |
| 136 | }) |node| { | | |
| 137 | if (field_state == .seen) { | | |
| 138 | field_state = .{ .end = visib_token orelse node.firstToken() }; | | |
| 139 | } | 147 | } |
| 140 | switch (node.id) { | 148 | |
| 141 | .FnProto => { | 149 | const visib_token = p.eatToken(.Keyword_pub); |
| 142 | node.cast(Node.FnProto).?.doc_comments = doc_comments; | 150 | |
| 143 | node.cast(Node.FnProto).?.visib_token = visib_token; | 151 | if (p.parseTopLevelDecl() catch |err| switch (err) { |
| 144 | }, | 152 | error.OutOfMemory => return error.OutOfMemory, |
| 145 | .VarDecl => { | 153 | error.ParseError => { |
| 146 | node.cast(Node.VarDecl).?.doc_comments = doc_comments; | 154 | p.findNextContainerMember(); |
| 147 | node.cast(Node.VarDecl).?.visib_token = visib_token; | 155 | continue; |
| 148 | }, | | |
| 149 | .Use => { | | |
| 150 | node.cast(Node.Use).?.doc_comments = doc_comments; | | |
| 151 | node.cast(Node.Use).?.visib_token = visib_token; | | |
| 152 | }, | 156 | }, |
| 153 | else => unreachable, | 157 | }) |node| { |
| 154 | } | 158 | if (field_state == .seen) { |
| 155 | try list.push(node); | 159 | field_state = .{ .end = visib_token orelse node.firstToken() }; |
| 156 | if (try parseAppendedDocComment(arena, it, tree, node.lastToken())) |appended_comment| { | 160 | } |
| 157 | switch (node.id) { | 161 | switch (node.id) { |
| 158 | .FnProto => {}, | 162 | .FnProto => { |
| 159 | .VarDecl => node.cast(Node.VarDecl).?.doc_comments = appended_comment, | 163 | node.cast(Node.FnProto).?.doc_comments = doc_comments; |
| 160 | .Use => node.cast(Node.Use).?.doc_comments = appended_comment, | 164 | node.cast(Node.FnProto).?.visib_token = visib_token; |
| | 165 | }, |
| | 166 | .VarDecl => { |
| | 167 | node.cast(Node.VarDecl).?.doc_comments = doc_comments; |
| | 168 | node.cast(Node.VarDecl).?.visib_token = visib_token; |
| | 169 | }, |
| | 170 | .Use => { |
| | 171 | node.cast(Node.Use).?.doc_comments = doc_comments; |
| | 172 | node.cast(Node.Use).?.visib_token = visib_token; |
| | 173 | }, |
| 161 | else => unreachable, | 174 | else => unreachable, |
| 162 | } | 175 | } |
| | 176 | list_it = try p.llpush(*Node, list_it, node); |
| | 177 | if (try p.parseAppendedDocComment(node.lastToken())) |appended_comment| { |
| | 178 | switch (node.id) { |
| | 179 | .FnProto => {}, |
| | 180 | .VarDecl => node.cast(Node.VarDecl).?.doc_comments = appended_comment, |
| | 181 | .Use => node.cast(Node.Use).?.doc_comments = appended_comment, |
| | 182 | else => unreachable, |
| | 183 | } |
| | 184 | } |
| | 185 | continue; |
| 163 | } | 186 | } |
| 164 | continue; | | |
| 165 | } | | |
| 166 | | | |
| 167 | if (visib_token != null) { | | |
| 168 | try tree.errors.push(.{ | | |
| 169 | .ExpectedPubItem = .{ .token = it.index }, | | |
| 170 | }); | | |
| 171 | // ignore this pub | | |
| 172 | continue; | | |
| 173 | } | | |
| 174 | | 187 | |
| 175 | if (parseContainerField(arena, it, tree) catch |err| switch (err) { | 188 | if (visib_token != null) { |
| 176 | error.OutOfMemory => return error.OutOfMemory, | 189 | try p.errors.append(p.gpa, .{ |
| 177 | error.ParseError => { | 190 | .ExpectedPubItem = .{ .token = p.tok_i }, |
| 178 | // attempt to recover | 191 | }); |
| 179 | findNextContainerMember(it); | 192 | // ignore this pub |
| 180 | continue; | 193 | continue; |
| 181 | }, | | |
| 182 | }) |node| { | | |
| 183 | switch (field_state) { | | |
| 184 | .none => field_state = .seen, | | |
| 185 | .err, .seen => {}, | | |
| 186 | .end => |tok| { | | |
| 187 | try tree.errors.push(.{ | | |
| 188 | .DeclBetweenFields = .{ .token = tok }, | | |
| 189 | }); | | |
| 190 | // continue parsing, error will be reported later | | |
| 191 | field_state = .err; | | |
| 192 | }, | | |
| 193 | } | 194 | } |
| 194 | | 195 | |
| 195 | const field = node.cast(Node.ContainerField).?; | 196 | if (p.parseContainerField() catch |err| switch (err) { |
| 196 | field.doc_comments = doc_comments; | 197 | error.OutOfMemory => return error.OutOfMemory, |
| 197 | try list.push(node); | 198 | error.ParseError => { |
| 198 | const comma = eatToken(it, .Comma) orelse { | 199 | // attempt to recover |
| 199 | // try to continue parsing | 200 | p.findNextContainerMember(); |
| 200 | const index = it.index; | 201 | continue; |
| 201 | findNextContainerMember(it); | 202 | }, |
| 202 | const next = it.peek().?.id; | 203 | }) |node| { |
| 203 | switch (next) { | 204 | switch (field_state) { |
| 204 | .Eof => break, | 205 | .none => field_state = .seen, |
| 205 | else => { | 206 | .err, .seen => {}, |
| 206 | if (next == .RBrace) { | 207 | .end => |tok| { |
| 207 | if (!top_level) break; | 208 | try p.errors.append(p.gpa, .{ |
| 208 | _ = nextToken(it); | 209 | .DeclBetweenFields = .{ .token = tok }, |
| 209 | } | | |
| 210 | | | |
| 211 | // add error and continue | | |
| 212 | try tree.errors.push(.{ | | |
| 213 | .ExpectedToken = .{ .token = index, .expected_id = .Comma }, | | |
| 214 | }); | 210 | }); |
| 215 | continue; | 211 | // continue parsing, error will be reported later |
| | 212 | field_state = .err; |
| 216 | }, | 213 | }, |
| 217 | } | 214 | } |
| 218 | }; | | |
| 219 | if (try parseAppendedDocComment(arena, it, tree, comma)) |appended_comment| | | |
| 220 | field.doc_comments = appended_comment; | | |
| 221 | continue; | | |
| 222 | } | | |
| 223 | | 215 | |
| 224 | // Dangling doc comment | 216 | const field = node.cast(Node.ContainerField).?; |
| 225 | if (doc_comments != null) { | 217 | field.doc_comments = doc_comments; |
| 226 | try tree.errors.push(.{ | 218 | list_it = try p.llpush(*Node, list_it, node); |
| 227 | .UnattachedDocComment = .{ .token = doc_comments.?.firstToken() }, | 219 | const comma = p.eatToken(.Comma) orelse { |
| 228 | }); | 220 | // try to continue parsing |
| 229 | } | 221 | const index = p.tok_i; |
| | 222 | p.findNextContainerMember(); |
| | 223 | const next = p.tokens[p.tok_i].id; |
| | 224 | switch (next) { |
| | 225 | .Eof => break, |
| | 226 | else => { |
| | 227 | if (next == .RBrace) { |
| | 228 | if (!top_level) break; |
| | 229 | _ = p.nextToken(); |
| | 230 | } |
| | 231 | |
| | 232 | // add error and continue |
| | 233 | try p.errors.append(p.gpa, .{ |
| | 234 | .ExpectedToken = .{ .token = index, .expected_id = .Comma }, |
| | 235 | }); |
| | 236 | continue; |
| | 237 | }, |
| | 238 | } |
| | 239 | }; |
| | 240 | if (try p.parseAppendedDocComment(comma)) |appended_comment| |
| | 241 | field.doc_comments = appended_comment; |
| | 242 | continue; |
| | 243 | } |
| 230 | | 244 | |
| 231 | const next = it.peek().?.id; | 245 | // Dangling doc comment |
| 232 | switch (next) { | 246 | if (doc_comments != null) { |
| 233 | .Eof => break, | 247 | try p.errors.append(p.gpa, .{ |
| 234 | .Keyword_comptime => { | 248 | .UnattachedDocComment = .{ .token = doc_comments.?.firstToken() }, |
| 235 | _ = nextToken(it); | | |
| 236 | try tree.errors.push(.{ | | |
| 237 | .ExpectedBlockOrField = .{ .token = it.index }, | | |
| 238 | }); | 249 | }); |
| 239 | }, | 250 | } |
| 240 | else => { | | |
| 241 | const index = it.index; | | |
| 242 | if (next == .RBrace) { | | |
| 243 | if (!top_level) break; | | |
| 244 | _ = nextToken(it); | | |
| 245 | } | | |
| 246 | | 251 | |
| 247 | // this was likely not supposed to end yet, | 252 | const next = p.tokens[p.tok_i].id; |
| 248 | // try to find the next declaration | 253 | switch (next) { |
| 249 | findNextContainerMember(it); | 254 | .Eof => break, |
| 250 | try tree.errors.push(.{ | 255 | .Keyword_comptime => { |
| 251 | .ExpectedContainerMembers = .{ .token = index }, | 256 | _ = p.nextToken(); |
| 252 | }); | 257 | try p.errors.append(p.gpa, .{ |
| 253 | }, | 258 | .ExpectedBlockOrField = .{ .token = p.tok_i }, |
| | 259 | }); |
| | 260 | }, |
| | 261 | else => { |
| | 262 | const index = p.tok_i; |
| | 263 | if (next == .RBrace) { |
| | 264 | if (!top_level) break; |
| | 265 | _ = p.nextToken(); |
| | 266 | } |
| | 267 | |
| | 268 | // this was likely not supposed to end yet, |
| | 269 | // try to find the next declaration |
| | 270 | p.findNextContainerMember(); |
| | 271 | try p.errors.append(p.gpa, .{ |
| | 272 | .ExpectedContainerMembers = .{ .token = index }, |
| | 273 | }); |
| | 274 | }, |
| | 275 | } |
| 254 | } | 276 | } |
| 255 | } | | |
| 256 | | 277 | |
| 257 | return list; | 278 | return list; |
| 258 | } | 279 | } |
| 259 | | 280 | |
| 260 | /// Attempts to find next container member by searching for certain tokens | 281 | /// Attempts to find next container member by searching for certain tokens |
| 261 | fn findNextContainerMember(it: *TokenIterator) void { | 282 | fn findNextContainerMember(p: *Parser) void { |
| 262 | var level: u32 = 0; | 283 | var level: u32 = 0; |
| 263 | while (true) { | 284 | while (true) { |
| 264 | const tok = nextToken(it); | 285 | const tok = p.nextToken(); |
| 265 | switch (tok.ptr.id) { | 286 | switch (tok.ptr.id) { |
| 266 | // any of these can start a new top level declaration | 287 | // any of these can start a new top level declaration |
| 267 | .Keyword_test, | 288 | .Keyword_test, |
| 268 | .Keyword_comptime, | 289 | .Keyword_comptime, |
| 269 | .Keyword_pub, | 290 | .Keyword_pub, |
| 270 | .Keyword_export, | 291 | .Keyword_export, |
| 271 | .Keyword_extern, | 292 | .Keyword_extern, |
| 272 | .Keyword_inline, | 293 | .Keyword_inline, |
| 273 | .Keyword_noinline, | 294 | .Keyword_noinline, |
| 274 | .Keyword_usingnamespace, | 295 | .Keyword_usingnamespace, |
| 275 | .Keyword_threadlocal, | 296 | .Keyword_threadlocal, |
| 276 | .Keyword_const, | 297 | .Keyword_const, |
| 277 | .Keyword_var, | 298 | .Keyword_var, |
| 278 | .Keyword_fn, | 299 | .Keyword_fn, |
| 279 | .Identifier, | 300 | .Identifier, |
| 280 | => { | 301 | => { |
| 281 | if (level == 0) { | 302 | if (level == 0) { |
| 282 | putBackToken(it, tok.index); | 303 | p.putBackToken(tok.index); |
| 283 | return; | 304 | return; |
| 284 | } | 305 | } |
| 285 | }, | 306 | }, |
| 286 | .Comma, .Semicolon => { | 307 | .Comma, .Semicolon => { |
| 287 | // this decl was likely meant to end here | 308 | // this decl was likely meant to end here |
| 288 | if (level == 0) { | 309 | if (level == 0) { |
| 289 | return; | 310 | return; |
| 290 | } | 311 | } |
| 291 | }, | 312 | }, |
| 292 | .LParen, .LBracket, .LBrace => level += 1, | 313 | .LParen, .LBracket, .LBrace => level += 1, |
| 293 | .RParen, .RBracket => { | 314 | .RParen, .RBracket => { |
| 294 | if (level != 0) level -= 1; | 315 | if (level != 0) level -= 1; |
| 295 | }, | 316 | }, |
| 296 | .RBrace => { | 317 | .RBrace => { |
| 297 | if (level == 0) { | 318 | if (level == 0) { |
| 298 | // end of container, exit | 319 | // end of container, exit |
| 299 | putBackToken(it, tok.index); | 320 | p.putBackToken(tok.index); |
| | 321 | return; |
| | 322 | } |
| | 323 | level -= 1; |
| | 324 | }, |
| | 325 | .Eof => { |
| | 326 | p.putBackToken(tok.index); |
| 300 | return; | 327 | return; |
| 301 | } | 328 | }, |
| 302 | level -= 1; | 329 | else => {}, |
| 303 | }, | 330 | } |
| 304 | .Eof => { | | |
| 305 | putBackToken(it, tok.index); | | |
| 306 | return; | | |
| 307 | }, | | |
| 308 | else => {}, | | |
| 309 | } | 331 | } |
| 310 | } | 332 | } |
| 311 | } | | |
| 312 | | 333 | |
| 313 | /// Attempts to find the next statement by searching for a semicolon | 334 | /// Attempts to find the next statement by searching for a semicolon |
| 314 | fn findNextStmt(it: *TokenIterator) void { | 335 | fn findNextStmt(p: *Parser) void { |
| 315 | var level: u32 = 0; | 336 | var level: u32 = 0; |
| 316 | while (true) { | 337 | while (true) { |
| 317 | const tok = nextToken(it); | 338 | const tok = p.nextToken(); |
| 318 | switch (tok.ptr.id) { | 339 | switch (tok.ptr.id) { |
| 319 | .LBrace => level += 1, | 340 | .LBrace => level += 1, |
| 320 | .RBrace => { | 341 | .RBrace => { |
| 321 | if (level == 0) { | 342 | if (level == 0) { |
| 322 | putBackToken(it, tok.index); | 343 | p.putBackToken(tok.index); |
| 323 | return; | 344 | return; |
| 324 | } | 345 | } |
| 325 | level -= 1; | 346 | level -= 1; |
| 326 | }, | 347 | }, |
| 327 | .Semicolon => { | 348 | .Semicolon => { |
| 328 | if (level == 0) { | 349 | if (level == 0) { |
| | 350 | return; |
| | 351 | } |
| | 352 | }, |
| | 353 | .Eof => { |
| | 354 | p.putBackToken(tok.index); |
| 329 | return; | 355 | return; |
| 330 | } | 356 | }, |
| 331 | }, | 357 | else => {}, |
| 332 | .Eof => { | 358 | } |
| 333 | putBackToken(it, tok.index); | | |
| 334 | return; | | |
| 335 | }, | | |
| 336 | else => {}, | | |
| 337 | } | 359 | } |
| 338 | } | 360 | } |
| 339 | } | | |
| 340 | | | |
| 341 | /// Eat a multiline container doc comment | | |
| 342 | fn parseContainerDocComments(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 343 | var lines = Node.DocComment.LineList.init(arena); | | |
| 344 | while (eatToken(it, .ContainerDocComment)) |line| { | | |
| 345 | try lines.push(line); | | |
| 346 | } | | |
| 347 | | | |
| 348 | if (lines.len == 0) return null; | | |
| 349 | | | |
| 350 | const node = try arena.create(Node.DocComment); | | |
| 351 | node.* = .{ | | |
| 352 | .lines = lines, | | |
| 353 | }; | | |
| 354 | return &node.base; | | |
| 355 | } | | |
| 356 | | | |
| 357 | /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block | | |
| 358 | fn parseTestDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 359 | const test_token = eatToken(it, .Keyword_test) orelse return null; | | |
| 360 | const name_node = try expectNode(arena, it, tree, parseStringLiteralSingle, .{ | | |
| 361 | .ExpectedStringLiteral = .{ .token = it.index }, | | |
| 362 | }); | | |
| 363 | const block_node = try expectNode(arena, it, tree, parseBlock, .{ | | |
| 364 | .ExpectedLBrace = .{ .token = it.index }, | | |
| 365 | }); | | |
| 366 | | | |
| 367 | const test_node = try arena.create(Node.TestDecl); | | |
| 368 | test_node.* = .{ | | |
| 369 | .doc_comments = null, | | |
| 370 | .test_token = test_token, | | |
| 371 | .name = name_node, | | |
| 372 | .body_node = block_node, | | |
| 373 | }; | | |
| 374 | return &test_node.base; | | |
| 375 | } | | |
| 376 | | | |
| 377 | /// TopLevelComptime <- KEYWORD_comptime BlockExpr | | |
| 378 | fn parseTopLevelComptime(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 379 | const tok = eatToken(it, .Keyword_comptime) orelse return null; | | |
| 380 | const lbrace = eatToken(it, .LBrace) orelse { | | |
| 381 | putBackToken(it, tok); | | |
| 382 | return null; | | |
| 383 | }; | | |
| 384 | putBackToken(it, lbrace); | | |
| 385 | const block_node = try expectNode(arena, it, tree, parseBlockExpr, .{ | | |
| 386 | .ExpectedLabelOrLBrace = .{ .token = it.index }, | | |
| 387 | }); | | |
| 388 | | | |
| 389 | const comptime_node = try arena.create(Node.Comptime); | | |
| 390 | comptime_node.* = .{ | | |
| 391 | .doc_comments = null, | | |
| 392 | .comptime_token = tok, | | |
| 393 | .expr = block_node, | | |
| 394 | }; | | |
| 395 | return &comptime_node.base; | | |
| 396 | } | | |
| 397 | | 361 | |
| 398 | /// TopLevelDecl | 362 | /// Eat a multiline container doc comment |
| 399 | /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) | 363 | fn parseContainerDocComments(p: *Parser) !?*Node { |
| 400 | /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl | 364 | var lines = Node.DocComment.LineList{}; |
| 401 | /// / KEYWORD_usingnamespace Expr SEMICOLON | 365 | var lines_it: *?*Node.DocComment.LineList.Node = &lines.first; |
| 402 | fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 403 | var lib_name: ?*Node = null; | | |
| 404 | const extern_export_inline_token = blk: { | | |
| 405 | if (eatToken(it, .Keyword_export)) |token| break :blk token; | | |
| 406 | if (eatToken(it, .Keyword_extern)) |token| { | | |
| 407 | lib_name = try parseStringLiteralSingle(arena, it, tree); | | |
| 408 | break :blk token; | | |
| 409 | } | | |
| 410 | if (eatToken(it, .Keyword_inline)) |token| break :blk token; | | |
| 411 | if (eatToken(it, .Keyword_noinline)) |token| break :blk token; | | |
| 412 | break :blk null; | | |
| 413 | }; | | |
| 414 | | | |
| 415 | if (try parseFnProto(arena, it, tree)) |node| { | | |
| 416 | const fn_node = node.cast(Node.FnProto).?; | | |
| 417 | fn_node.*.extern_export_inline_token = extern_export_inline_token; | | |
| 418 | fn_node.*.lib_name = lib_name; | | |
| 419 | if (eatToken(it, .Semicolon)) |_| return node; | | |
| 420 | | | |
| 421 | if (try expectNodeRecoverable(arena, it, tree, parseBlock, .{ | | |
| 422 | // since parseBlock only return error.ParseError on | | |
| 423 | // a missing '}' we can assume this function was | | |
| 424 | // supposed to end here. | | |
| 425 | .ExpectedSemiOrLBrace = .{ .token = it.index }, | | |
| 426 | })) |body_node| { | | |
| 427 | fn_node.body_node = body_node; | | |
| 428 | } | | |
| 429 | return node; | | |
| 430 | } | | |
| 431 | | 366 | |
| 432 | if (extern_export_inline_token) |token| { | 367 | while (p.eatToken(.ContainerDocComment)) |line| { |
| 433 | if (tree.tokens.at(token).id == .Keyword_inline or | 368 | lines_it = try p.llpush(TokenIndex, lines_it, line); |
| 434 | tree.tokens.at(token).id == .Keyword_noinline) | | |
| 435 | { | | |
| 436 | try tree.errors.push(.{ | | |
| 437 | .ExpectedFn = .{ .token = it.index }, | | |
| 438 | }); | | |
| 439 | return error.ParseError; | | |
| 440 | } | 369 | } |
| 441 | } | | |
| 442 | | 370 | |
| 443 | const thread_local_token = eatToken(it, .Keyword_threadlocal); | 371 | if (lines.first == null) return null; |
| 444 | | 372 | |
| 445 | if (try parseVarDecl(arena, it, tree)) |node| { | 373 | const node = try p.arena.allocator.create(Node.DocComment); |
| 446 | var var_decl = node.cast(Node.VarDecl).?; | 374 | node.* = .{ |
| 447 | var_decl.*.thread_local_token = thread_local_token; | 375 | .lines = lines, |
| 448 | var_decl.*.comptime_token = null; | 376 | }; |
| 449 | var_decl.*.extern_export_token = extern_export_inline_token; | 377 | return &node.base; |
| 450 | var_decl.*.lib_name = lib_name; | | |
| 451 | return node; | | |
| 452 | } | 378 | } |
| 453 | | 379 | |
| 454 | if (thread_local_token != null) { | 380 | /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block |
| 455 | try tree.errors.push(.{ | 381 | fn parseTestDecl(p: *Parser) !?*Node { |
| 456 | .ExpectedVarDecl = .{ .token = it.index }, | 382 | const test_token = p.eatToken(.Keyword_test) orelse return null; |
| | 383 | const name_node = try p.expectNode(parseStringLiteralSingle, .{ |
| | 384 | .ExpectedStringLiteral = .{ .token = p.tok_i }, |
| 457 | }); | 385 | }); |
| 458 | // ignore this and try again; | 386 | const block_node = try p.expectNode(parseBlock, .{ |
| 459 | return error.ParseError; | 387 | .ExpectedLBrace = .{ .token = p.tok_i }, |
| 460 | } | | |
| 461 | | | |
| 462 | if (extern_export_inline_token) |token| { | | |
| 463 | try tree.errors.push(.{ | | |
| 464 | .ExpectedVarDeclOrFn = .{ .token = it.index }, | | |
| 465 | }); | 388 | }); |
| 466 | // ignore this and try again; | 389 | |
| 467 | return error.ParseError; | 390 | const test_node = try p.arena.allocator.create(Node.TestDecl); |
| | 391 | test_node.* = .{ |
| | 392 | .doc_comments = null, |
| | 393 | .test_token = test_token, |
| | 394 | .name = name_node, |
| | 395 | .body_node = block_node, |
| | 396 | }; |
| | 397 | return &test_node.base; |
| 468 | } | 398 | } |
| 469 | | 399 | |
| 470 | return try parseUse(arena, it, tree); | 400 | /// TopLevelComptime <- KEYWORD_comptime BlockExpr |
| 471 | } | 401 | fn parseTopLevelComptime(p: *Parser) !?*Node { |
| | 402 | const tok = p.eatToken(.Keyword_comptime) orelse return null; |
| | 403 | const lbrace = p.eatToken(.LBrace) orelse { |
| | 404 | p.putBackToken(tok); |
| | 405 | return null; |
| | 406 | }; |
| | 407 | p.putBackToken(lbrace); |
| | 408 | const block_node = try p.expectNode(parseBlockExpr, .{ |
| | 409 | .ExpectedLabelOrLBrace = .{ .token = p.tok_i }, |
| | 410 | }); |
| 472 | | 411 | |
| 473 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr) | 412 | const comptime_node = try p.arena.allocator.create(Node.Comptime); |
| 474 | fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 413 | comptime_node.* = .{ |
| 475 | // TODO: Remove once extern/async fn rewriting is | 414 | .doc_comments = null, |
| 476 | var is_async = false; | 415 | .comptime_token = tok, |
| 477 | var is_extern = false; | 416 | .expr = block_node, |
| 478 | const cc_token: ?usize = blk: { | 417 | }; |
| 479 | if (eatToken(it, .Keyword_extern)) |token| { | 418 | return &comptime_node.base; |
| 480 | is_extern = true; | 419 | } |
| 481 | break :blk token; | 420 | |
| 482 | } | 421 | /// TopLevelDecl |
| 483 | if (eatToken(it, .Keyword_async)) |token| { | 422 | /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) |
| 484 | is_async = true; | 423 | /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl |
| 485 | break :blk token; | 424 | /// / KEYWORD_usingnamespace Expr SEMICOLON |
| 486 | } | 425 | fn parseTopLevelDecl(p: *Parser) !?*Node { |
| 487 | break :blk null; | 426 | var lib_name: ?*Node = null; |
| 488 | }; | 427 | const extern_export_inline_token = blk: { |
| 489 | const fn_token = eatToken(it, .Keyword_fn) orelse { | 428 | if (p.eatToken(.Keyword_export)) |token| break :blk token; |
| 490 | if (cc_token) |token| | 429 | if (p.eatToken(.Keyword_extern)) |token| { |
| 491 | putBackToken(it, token); | 430 | lib_name = try p.parseStringLiteralSingle(); |
| 492 | return null; | 431 | break :blk token; |
| 493 | }; | 432 | } |
| 494 | const name_token = eatToken(it, .Identifier); | 433 | if (p.eatToken(.Keyword_inline)) |token| break :blk token; |
| 495 | const lparen = try expectToken(it, tree, .LParen); | 434 | if (p.eatToken(.Keyword_noinline)) |token| break :blk token; |
| 496 | const params = try parseParamDeclList(arena, it, tree); | 435 | break :blk null; |
| 497 | const rparen = try expectToken(it, tree, .RParen); | 436 | }; |
| 498 | const align_expr = try parseByteAlign(arena, it, tree); | | |
| 499 | const section_expr = try parseLinkSection(arena, it, tree); | | |
| 500 | const callconv_expr = try parseCallconv(arena, it, tree); | | |
| 501 | const exclamation_token = eatToken(it, .Bang); | | |
| 502 | | | |
| 503 | const return_type_expr = (try parseVarType(arena, it, tree)) orelse | | |
| 504 | try expectNodeRecoverable(arena, it, tree, parseTypeExpr, .{ | | |
| 505 | // most likely the user forgot to specify the return type. | | |
| 506 | // Mark return type as invalid and try to continue. | | |
| 507 | .ExpectedReturnType = .{ .token = it.index }, | | |
| 508 | }); | | |
| 509 | | | |
| 510 | // TODO https://github.com/ziglang/zig/issues/3750 | | |
| 511 | const R = Node.FnProto.ReturnType; | | |
| 512 | const return_type = if (return_type_expr == null) | | |
| 513 | R{ .Invalid = rparen } | | |
| 514 | else if (exclamation_token != null) | | |
| 515 | R{ .InferErrorSet = return_type_expr.? } | | |
| 516 | else | | |
| 517 | R{ .Explicit = return_type_expr.? }; | | |
| 518 | | | |
| 519 | const var_args_token = if (params.len > 0) blk: { | | |
| 520 | const param_type = params.at(params.len - 1).*.cast(Node.ParamDecl).?.param_type; | | |
| 521 | break :blk if (param_type == .var_args) param_type.var_args else null; | | |
| 522 | } else | | |
| 523 | null; | | |
| 524 | | | |
| 525 | const fn_proto_node = try arena.create(Node.FnProto); | | |
| 526 | fn_proto_node.* = .{ | | |
| 527 | .doc_comments = null, | | |
| 528 | .visib_token = null, | | |
| 529 | .fn_token = fn_token, | | |
| 530 | .name_token = name_token, | | |
| 531 | .params = params, | | |
| 532 | .return_type = return_type, | | |
| 533 | .var_args_token = var_args_token, | | |
| 534 | .extern_export_inline_token = null, | | |
| 535 | .body_node = null, | | |
| 536 | .lib_name = null, | | |
| 537 | .align_expr = align_expr, | | |
| 538 | .section_expr = section_expr, | | |
| 539 | .callconv_expr = callconv_expr, | | |
| 540 | .is_extern_prototype = is_extern, | | |
| 541 | .is_async = is_async, | | |
| 542 | }; | | |
| 543 | | 437 | |
| 544 | return &fn_proto_node.base; | 438 | if (try p.parseFnProto()) |node| { |
| 545 | } | 439 | const fn_node = node.cast(Node.FnProto).?; |
| | 440 | fn_node.*.extern_export_inline_token = extern_export_inline_token; |
| | 441 | fn_node.*.lib_name = lib_name; |
| | 442 | if (p.eatToken(.Semicolon)) |_| return node; |
| | 443 | |
| | 444 | if (try p.expectNodeRecoverable(parseBlock, .{ |
| | 445 | // since parseBlock only return error.ParseError on |
| | 446 | // a missing '}' we can assume this function was |
| | 447 | // supposed to end here. |
| | 448 | .ExpectedSemiOrLBrace = .{ .token = p.tok_i }, |
| | 449 | })) |body_node| { |
| | 450 | fn_node.body_node = body_node; |
| | 451 | } |
| | 452 | return node; |
| | 453 | } |
| 546 | | 454 | |
| 547 | /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON | 455 | if (extern_export_inline_token) |token| { |
| 548 | fn parseVarDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 456 | if (p.tokens[token].id == .Keyword_inline or |
| 549 | const mut_token = eatToken(it, .Keyword_const) orelse | 457 | p.tokens[token].id == .Keyword_noinline) |
| 550 | eatToken(it, .Keyword_var) orelse | 458 | { |
| 551 | return null; | 459 | try p.errors.append(p.gpa, .{ |
| | 460 | .ExpectedFn = .{ .token = p.tok_i }, |
| | 461 | }); |
| | 462 | return error.ParseError; |
| | 463 | } |
| | 464 | } |
| 552 | | 465 | |
| 553 | const name_token = try expectToken(it, tree, .Identifier); | 466 | const thread_local_token = p.eatToken(.Keyword_threadlocal); |
| 554 | const type_node = if (eatToken(it, .Colon) != null) | | |
| 555 | try expectNode(arena, it, tree, parseTypeExpr, .{ | | |
| 556 | .ExpectedTypeExpr = .{ .token = it.index }, | | |
| 557 | }) | | |
| 558 | else | | |
| 559 | null; | | |
| 560 | const align_node = try parseByteAlign(arena, it, tree); | | |
| 561 | const section_node = try parseLinkSection(arena, it, tree); | | |
| 562 | const eq_token = eatToken(it, .Equal); | | |
| 563 | const init_node = if (eq_token != null) blk: { | | |
| 564 | break :blk try expectNode(arena, it, tree, parseExpr, .{ | | |
| 565 | .ExpectedExpr = .{ .token = it.index }, | | |
| 566 | }); | | |
| 567 | } else null; | | |
| 568 | const semicolon_token = try expectToken(it, tree, .Semicolon); | | |
| 569 | | | |
| 570 | const node = try arena.create(Node.VarDecl); | | |
| 571 | node.* = .{ | | |
| 572 | .doc_comments = null, | | |
| 573 | .visib_token = null, | | |
| 574 | .thread_local_token = null, | | |
| 575 | .name_token = name_token, | | |
| 576 | .eq_token = eq_token, | | |
| 577 | .mut_token = mut_token, | | |
| 578 | .comptime_token = null, | | |
| 579 | .extern_export_token = null, | | |
| 580 | .lib_name = null, | | |
| 581 | .type_node = type_node, | | |
| 582 | .align_node = align_node, | | |
| 583 | .section_node = section_node, | | |
| 584 | .init_node = init_node, | | |
| 585 | .semicolon_token = semicolon_token, | | |
| 586 | }; | | |
| 587 | return &node.base; | | |
| 588 | } | | |
| 589 | | 467 | |
| 590 | /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? | 468 | if (try p.parseVarDecl()) |node| { |
| 591 | fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 469 | var var_decl = node.cast(Node.VarDecl).?; |
| 592 | const comptime_token = eatToken(it, .Keyword_comptime); | 470 | var_decl.*.thread_local_token = thread_local_token; |
| 593 | const name_token = eatToken(it, .Identifier) orelse { | 471 | var_decl.*.comptime_token = null; |
| 594 | if (comptime_token) |t| putBackToken(it, t); | 472 | var_decl.*.extern_export_token = extern_export_inline_token; |
| 595 | return null; | 473 | var_decl.*.lib_name = lib_name; |
| 596 | }; | 474 | return node; |
| | 475 | } |
| 597 | | 476 | |
| 598 | var align_expr: ?*Node = null; | 477 | if (thread_local_token != null) { |
| 599 | var type_expr: ?*Node = null; | 478 | try p.errors.append(p.gpa, .{ |
| 600 | if (eatToken(it, .Colon)) |_| { | 479 | .ExpectedVarDecl = .{ .token = p.tok_i }, |
| 601 | if (eatToken(it, .Keyword_var)) |var_tok| { | | |
| 602 | const node = try arena.create(ast.Node.VarType); | | |
| 603 | node.* = .{ .token = var_tok }; | | |
| 604 | type_expr = &node.base; | | |
| 605 | } else { | | |
| 606 | type_expr = try expectNode(arena, it, tree, parseTypeExpr, .{ | | |
| 607 | .ExpectedTypeExpr = .{ .token = it.index }, | | |
| 608 | }); | 480 | }); |
| 609 | align_expr = try parseByteAlign(arena, it, tree); | 481 | // ignore this and try again; |
| | 482 | return error.ParseError; |
| 610 | } | 483 | } |
| 611 | } | | |
| 612 | | | |
| 613 | const value_expr = if (eatToken(it, .Equal)) |_| | | |
| 614 | try expectNode(arena, it, tree, parseExpr, .{ | | |
| 615 | .ExpectedExpr = .{ .token = it.index }, | | |
| 616 | }) | | |
| 617 | else | | |
| 618 | null; | | |
| 619 | | 484 | |
| 620 | const node = try arena.create(Node.ContainerField); | 485 | if (extern_export_inline_token) |token| { |
| 621 | node.* = .{ | 486 | try p.errors.append(p.gpa, .{ |
| 622 | .doc_comments = null, | 487 | .ExpectedVarDeclOrFn = .{ .token = p.tok_i }, |
| 623 | .comptime_token = comptime_token, | 488 | }); |
| 624 | .name_token = name_token, | 489 | // ignore this and try again; |
| 625 | .type_expr = type_expr, | 490 | return error.ParseError; |
| 626 | .value_expr = value_expr, | 491 | } |
| 627 | .align_expr = align_expr, | | |
| 628 | }; | | |
| 629 | return &node.base; | | |
| 630 | } | | |
| 631 | | 492 | |
| 632 | /// Statement | 493 | return p.parseUse(); |
| 633 | /// <- KEYWORD_comptime? VarDecl | | |
| 634 | /// / KEYWORD_comptime BlockExprStatement | | |
| 635 | /// / KEYWORD_nosuspend BlockExprStatement | | |
| 636 | /// / KEYWORD_suspend (SEMICOLON / BlockExprStatement) | | |
| 637 | /// / KEYWORD_defer BlockExprStatement | | |
| 638 | /// / KEYWORD_errdefer Payload? BlockExprStatement | | |
| 639 | /// / IfStatement | | |
| 640 | /// / LabeledStatement | | |
| 641 | /// / SwitchExpr | | |
| 642 | /// / AssignExpr SEMICOLON | | |
| 643 | fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*Node { | | |
| 644 | const comptime_token = eatToken(it, .Keyword_comptime); | | |
| 645 | | | |
| 646 | const var_decl_node = try parseVarDecl(arena, it, tree); | | |
| 647 | if (var_decl_node) |node| { | | |
| 648 | const var_decl = node.cast(Node.VarDecl).?; | | |
| 649 | var_decl.comptime_token = comptime_token; | | |
| 650 | return node; | | |
| 651 | } | 494 | } |
| 652 | | 495 | |
| 653 | if (comptime_token) |token| { | 496 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr) |
| 654 | const block_expr = try expectNode(arena, it, tree, parseBlockExprStatement, .{ | 497 | fn parseFnProto(p: *Parser) !?*Node { |
| 655 | .ExpectedBlockOrAssignment = .{ .token = it.index }, | 498 | // TODO: Remove once extern/async fn rewriting is |
| | 499 | var is_async = false; |
| | 500 | var is_extern = false; |
| | 501 | const cc_token: ?TokenIndex = blk: { |
| | 502 | if (p.eatToken(.Keyword_extern)) |token| { |
| | 503 | is_extern = true; |
| | 504 | break :blk token; |
| | 505 | } |
| | 506 | if (p.eatToken(.Keyword_async)) |token| { |
| | 507 | is_async = true; |
| | 508 | break :blk token; |
| | 509 | } |
| | 510 | break :blk null; |
| | 511 | }; |
| | 512 | const fn_token = p.eatToken(.Keyword_fn) orelse { |
| | 513 | if (cc_token) |token| |
| | 514 | p.putBackToken(token); |
| | 515 | return null; |
| | 516 | }; |
| | 517 | var var_args_token: ?TokenIndex = null; |
| | 518 | const name_token = p.eatToken(.Identifier); |
| | 519 | const lparen = try p.expectToken(.LParen); |
| | 520 | const params = try p.parseParamDeclList(&var_args_token); |
| | 521 | const rparen = try p.expectToken(.RParen); |
| | 522 | const align_expr = try p.parseByteAlign(); |
| | 523 | const section_expr = try p.parseLinkSection(); |
| | 524 | const callconv_expr = try p.parseCallconv(); |
| | 525 | const exclamation_token = p.eatToken(.Bang); |
| | 526 | |
| | 527 | const return_type_expr = (try p.parseVarType()) orelse |
| | 528 | try p.expectNodeRecoverable(parseTypeExpr, .{ |
| | 529 | // most likely the user forgot to specify the return type. |
| | 530 | // Mark return type as invalid and try to continue. |
| | 531 | .ExpectedReturnType = .{ .token = p.tok_i }, |
| 656 | }); | 532 | }); |
| 657 | | 533 | |
| 658 | const node = try arena.create(Node.Comptime); | 534 | // TODO https://github.com/ziglang/zig/issues/3750 |
| 659 | node.* = .{ | 535 | const R = Node.FnProto.ReturnType; |
| | 536 | const return_type = if (return_type_expr == null) |
| | 537 | R{ .Invalid = rparen } |
| | 538 | else if (exclamation_token != null) |
| | 539 | R{ .InferErrorSet = return_type_expr.? } |
| | 540 | else |
| | 541 | R{ .Explicit = return_type_expr.? }; |
| | 542 | |
| | 543 | const fn_proto_node = try p.arena.allocator.create(Node.FnProto); |
| | 544 | fn_proto_node.* = .{ |
| 660 | .doc_comments = null, | 545 | .doc_comments = null, |
| 661 | .comptime_token = token, | 546 | .visib_token = null, |
| 662 | .expr = block_expr, | 547 | .fn_token = fn_token, |
| | 548 | .name_token = name_token, |
| | 549 | .params = params, |
| | 550 | .return_type = return_type, |
| | 551 | .var_args_token = var_args_token, |
| | 552 | .extern_export_inline_token = null, |
| | 553 | .body_node = null, |
| | 554 | .lib_name = null, |
| | 555 | .align_expr = align_expr, |
| | 556 | .section_expr = section_expr, |
| | 557 | .callconv_expr = callconv_expr, |
| | 558 | .is_extern_prototype = is_extern, |
| | 559 | .is_async = is_async, |
| 663 | }; | 560 | }; |
| 664 | return &node.base; | | |
| 665 | } | | |
| 666 | | | |
| 667 | if (eatToken(it, .Keyword_nosuspend)) |nosuspend_token| { | | |
| 668 | const block_expr = try expectNode(arena, it, tree, parseBlockExprStatement, .{ | | |
| 669 | .ExpectedBlockOrAssignment = .{ .token = it.index }, | | |
| 670 | }); | | |
| 671 | | 561 | |
| 672 | const node = try arena.create(Node.Nosuspend); | 562 | return &fn_proto_node.base; |
| 673 | node.* = .{ | | |
| 674 | .nosuspend_token = nosuspend_token, | | |
| 675 | .expr = block_expr, | | |
| 676 | }; | | |
| 677 | return &node.base; | | |
| 678 | } | 563 | } |
| 679 | | 564 | |
| 680 | if (eatToken(it, .Keyword_suspend)) |suspend_token| { | 565 | /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON |
| 681 | const semicolon = eatToken(it, .Semicolon); | 566 | fn parseVarDecl(p: *Parser) !?*Node { |
| | 567 | const mut_token = p.eatToken(.Keyword_const) orelse |
| | 568 | p.eatToken(.Keyword_var) orelse |
| | 569 | return null; |
| 682 | | 570 | |
| 683 | const body_node = if (semicolon == null) blk: { | 571 | const name_token = try p.expectToken(.Identifier); |
| 684 | break :blk try expectNode(arena, it, tree, parseBlockExprStatement, .{ | 572 | const type_node = if (p.eatToken(.Colon) != null) |
| 685 | .ExpectedBlockOrExpression = .{ .token = it.index }, | 573 | try p.expectNode(parseTypeExpr, .{ |
| | 574 | .ExpectedTypeExpr = .{ .token = p.tok_i }, |
| | 575 | }) |
| | 576 | else |
| | 577 | null; |
| | 578 | const align_node = try p.parseByteAlign(); |
| | 579 | const section_node = try p.parseLinkSection(); |
| | 580 | const eq_token = p.eatToken(.Equal); |
| | 581 | const init_node = if (eq_token != null) blk: { |
| | 582 | break :blk try p.expectNode(parseExpr, .{ |
| | 583 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 686 | }); | 584 | }); |
| 687 | } else null; | 585 | } else null; |
| | 586 | const semicolon_token = try p.expectToken(.Semicolon); |
| 688 | | 587 | |
| 689 | const node = try arena.create(Node.Suspend); | 588 | const node = try p.arena.allocator.create(Node.VarDecl); |
| 690 | node.* = .{ | 589 | node.* = .{ |
| 691 | .suspend_token = suspend_token, | 590 | .doc_comments = null, |
| 692 | .body = body_node, | 591 | .visib_token = null, |
| | 592 | .thread_local_token = null, |
| | 593 | .name_token = name_token, |
| | 594 | .eq_token = eq_token, |
| | 595 | .mut_token = mut_token, |
| | 596 | .comptime_token = null, |
| | 597 | .extern_export_token = null, |
| | 598 | .lib_name = null, |
| | 599 | .type_node = type_node, |
| | 600 | .align_node = align_node, |
| | 601 | .section_node = section_node, |
| | 602 | .init_node = init_node, |
| | 603 | .semicolon_token = semicolon_token, |
| 693 | }; | 604 | }; |
| 694 | return &node.base; | 605 | return &node.base; |
| 695 | } | 606 | } |
| 696 | | 607 | |
| 697 | const defer_token = eatToken(it, .Keyword_defer) orelse eatToken(it, .Keyword_errdefer); | 608 | /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? |
| 698 | if (defer_token) |token| { | 609 | fn parseContainerField(p: *Parser) !?*Node { |
| 699 | const payload = if (tree.tokens.at(token).id == .Keyword_errdefer) | 610 | const comptime_token = p.eatToken(.Keyword_comptime); |
| 700 | try parsePayload(arena, it, tree) | 611 | const name_token = p.eatToken(.Identifier) orelse { |
| | 612 | if (comptime_token) |t| p.putBackToken(t); |
| | 613 | return null; |
| | 614 | }; |
| | 615 | |
| | 616 | var align_expr: ?*Node = null; |
| | 617 | var type_expr: ?*Node = null; |
| | 618 | if (p.eatToken(.Colon)) |_| { |
| | 619 | if (p.eatToken(.Keyword_var)) |var_tok| { |
| | 620 | const node = try p.arena.allocator.create(ast.Node.VarType); |
| | 621 | node.* = .{ .token = var_tok }; |
| | 622 | type_expr = &node.base; |
| | 623 | } else { |
| | 624 | type_expr = try p.expectNode(parseTypeExpr, .{ |
| | 625 | .ExpectedTypeExpr = .{ .token = p.tok_i }, |
| | 626 | }); |
| | 627 | align_expr = try p.parseByteAlign(); |
| | 628 | } |
| | 629 | } |
| | 630 | |
| | 631 | const value_expr = if (p.eatToken(.Equal)) |_| |
| | 632 | try p.expectNode(parseExpr, .{ |
| | 633 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 634 | }) |
| 701 | else | 635 | else |
| 702 | null; | 636 | null; |
| 703 | const expr_node = try expectNode(arena, it, tree, parseBlockExprStatement, .{ | 637 | |
| 704 | .ExpectedBlockOrExpression = .{ .token = it.index }, | 638 | const node = try p.arena.allocator.create(Node.ContainerField); |
| 705 | }); | | |
| 706 | const node = try arena.create(Node.Defer); | | |
| 707 | node.* = .{ | 639 | node.* = .{ |
| 708 | .defer_token = token, | 640 | .doc_comments = null, |
| 709 | .expr = expr_node, | 641 | .comptime_token = comptime_token, |
| 710 | .payload = payload, | 642 | .name_token = name_token, |
| | 643 | .type_expr = type_expr, |
| | 644 | .value_expr = value_expr, |
| | 645 | .align_expr = align_expr, |
| 711 | }; | 646 | }; |
| 712 | return &node.base; | 647 | return &node.base; |
| 713 | } | 648 | } |
| 714 | | 649 | |
| 715 | if (try parseIfStatement(arena, it, tree)) |node| return node; | 650 | /// Statement |
| 716 | if (try parseLabeledStatement(arena, it, tree)) |node| return node; | 651 | /// <- KEYWORD_comptime? VarDecl |
| 717 | if (try parseSwitchExpr(arena, it, tree)) |node| return node; | 652 | /// / KEYWORD_comptime BlockExprStatement |
| 718 | if (try parseAssignExpr(arena, it, tree)) |node| { | 653 | /// / KEYWORD_nosuspend BlockExprStatement |
| 719 | _ = try expectTokenRecoverable(it, tree, .Semicolon); | 654 | /// / KEYWORD_suspend (SEMICOLON / BlockExprStatement) |
| 720 | return node; | 655 | /// / KEYWORD_defer BlockExprStatement |
| 721 | } | 656 | /// / KEYWORD_errdefer Payload? BlockExprStatement |
| 722 | | 657 | /// / IfStatement |
| 723 | return null; | 658 | /// / LabeledStatement |
| 724 | } | 659 | /// / SwitchExpr |
| 725 | | 660 | /// / AssignExpr SEMICOLON |
| 726 | /// IfStatement | 661 | fn parseStatement(p: *Parser) Error!?*Node { |
| 727 | /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? | 662 | const comptime_token = p.eatToken(.Keyword_comptime); |
| 728 | /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | 663 | |
| 729 | fn parseIfStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 664 | const var_decl_node = try p.parseVarDecl(); |
| 730 | const if_node = (try parseIfPrefix(arena, it, tree)) orelse return null; | 665 | if (var_decl_node) |node| { |
| 731 | const if_prefix = if_node.cast(Node.If).?; | 666 | const var_decl = node.cast(Node.VarDecl).?; |
| 732 | | 667 | var_decl.comptime_token = comptime_token; |
| 733 | const block_expr = (try parseBlockExpr(arena, it, tree)); | 668 | return node; |
| 734 | const assign_expr = if (block_expr == null) | 669 | } |
| 735 | try expectNode(arena, it, tree, parseAssignExpr, .{ | | |
| 736 | .ExpectedBlockOrAssignment = .{ .token = it.index }, | | |
| 737 | }) | | |
| 738 | else | | |
| 739 | null; | | |
| 740 | | | |
| 741 | const semicolon = if (assign_expr != null) eatToken(it, .Semicolon) else null; | | |
| 742 | | | |
| 743 | const else_node = if (semicolon == null) blk: { | | |
| 744 | const else_token = eatToken(it, .Keyword_else) orelse break :blk null; | | |
| 745 | const payload = try parsePayload(arena, it, tree); | | |
| 746 | const else_body = try expectNode(arena, it, tree, parseStatement, .{ | | |
| 747 | .InvalidToken = .{ .token = it.index }, | | |
| 748 | }); | | |
| 749 | | 670 | |
| 750 | const node = try arena.create(Node.Else); | 671 | if (comptime_token) |token| { |
| 751 | node.* = .{ | 672 | const block_expr = try p.expectNode(parseBlockExprStatement, .{ |
| 752 | .else_token = else_token, | 673 | .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, |
| 753 | .payload = payload, | 674 | }); |
| 754 | .body = else_body, | | |
| 755 | }; | | |
| 756 | | 675 | |
| 757 | break :blk node; | 676 | const node = try p.arena.allocator.create(Node.Comptime); |
| 758 | } else null; | 677 | node.* = .{ |
| | 678 | .doc_comments = null, |
| | 679 | .comptime_token = token, |
| | 680 | .expr = block_expr, |
| | 681 | }; |
| | 682 | return &node.base; |
| | 683 | } |
| 759 | | 684 | |
| 760 | if (block_expr) |body| { | 685 | if (p.eatToken(.Keyword_nosuspend)) |nosuspend_token| { |
| 761 | if_prefix.body = body; | 686 | const block_expr = try p.expectNode(parseBlockExprStatement, .{ |
| 762 | if_prefix.@"else" = else_node; | 687 | .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, |
| 763 | return if_node; | 688 | }); |
| 764 | } | | |
| 765 | | 689 | |
| 766 | if (assign_expr) |body| { | 690 | const node = try p.arena.allocator.create(Node.Nosuspend); |
| 767 | if_prefix.body = body; | 691 | node.* = .{ |
| 768 | if (semicolon != null) return if_node; | 692 | .nosuspend_token = nosuspend_token, |
| 769 | if (else_node != null) { | 693 | .expr = block_expr, |
| 770 | if_prefix.@"else" = else_node; | 694 | }; |
| 771 | return if_node; | 695 | return &node.base; |
| 772 | } | 696 | } |
| 773 | try tree.errors.push(.{ | | |
| 774 | .ExpectedSemiOrElse = .{ .token = it.index }, | | |
| 775 | }); | | |
| 776 | } | | |
| 777 | | | |
| 778 | return if_node; | | |
| 779 | } | | |
| 780 | | 697 | |
| 781 | /// LabeledStatement <- BlockLabel? (Block / LoopStatement) | 698 | if (p.eatToken(.Keyword_suspend)) |suspend_token| { |
| 782 | fn parseLabeledStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 699 | const semicolon = p.eatToken(.Semicolon); |
| 783 | var colon: TokenIndex = undefined; | | |
| 784 | const label_token = parseBlockLabel(arena, it, tree, &colon); | | |
| 785 | | 700 | |
| 786 | if (try parseBlock(arena, it, tree)) |node| { | 701 | const body_node = if (semicolon == null) blk: { |
| 787 | node.cast(Node.Block).?.label = label_token; | 702 | break :blk try p.expectNode(parseBlockExprStatement, .{ |
| 788 | return node; | 703 | .ExpectedBlockOrExpression = .{ .token = p.tok_i }, |
| 789 | } | 704 | }); |
| | 705 | } else null; |
| 790 | | 706 | |
| 791 | if (try parseLoopStatement(arena, it, tree)) |node| { | 707 | const node = try p.arena.allocator.create(Node.Suspend); |
| 792 | if (node.cast(Node.For)) |for_node| { | 708 | node.* = .{ |
| 793 | for_node.label = label_token; | 709 | .suspend_token = suspend_token, |
| 794 | } else if (node.cast(Node.While)) |while_node| { | 710 | .body = body_node, |
| 795 | while_node.label = label_token; | 711 | }; |
| 796 | } else unreachable; | 712 | return &node.base; |
| 797 | return node; | 713 | } |
| 798 | } | | |
| 799 | | | |
| 800 | if (label_token != null) { | | |
| 801 | try tree.errors.push(.{ | | |
| 802 | .ExpectedLabelable = .{ .token = it.index }, | | |
| 803 | }); | | |
| 804 | return error.ParseError; | | |
| 805 | } | | |
| 806 | | | |
| 807 | return null; | | |
| 808 | } | | |
| 809 | | 714 | |
| 810 | /// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) | 715 | const defer_token = p.eatToken(.Keyword_defer) orelse p.eatToken(.Keyword_errdefer); |
| 811 | fn parseLoopStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 716 | if (defer_token) |token| { |
| 812 | const inline_token = eatToken(it, .Keyword_inline); | 717 | const payload = if (p.tokens[token].id == .Keyword_errdefer) |
| | 718 | try p.parsePayload() |
| | 719 | else |
| | 720 | null; |
| | 721 | const expr_node = try p.expectNode(parseBlockExprStatement, .{ |
| | 722 | .ExpectedBlockOrExpression = .{ .token = p.tok_i }, |
| | 723 | }); |
| | 724 | const node = try p.arena.allocator.create(Node.Defer); |
| | 725 | node.* = .{ |
| | 726 | .defer_token = token, |
| | 727 | .expr = expr_node, |
| | 728 | .payload = payload, |
| | 729 | }; |
| | 730 | return &node.base; |
| | 731 | } |
| 813 | | 732 | |
| 814 | if (try parseForStatement(arena, it, tree)) |node| { | 733 | if (try p.parseIfStatement()) |node| return node; |
| 815 | node.cast(Node.For).?.inline_token = inline_token; | 734 | if (try p.parseLabeledStatement()) |node| return node; |
| 816 | return node; | 735 | if (try p.parseSwitchExpr()) |node| return node; |
| 817 | } | 736 | if (try p.parseAssignExpr()) |node| { |
| | 737 | _ = try p.expectTokenRecoverable(.Semicolon); |
| | 738 | return node; |
| | 739 | } |
| 818 | | 740 | |
| 819 | if (try parseWhileStatement(arena, it, tree)) |node| { | 741 | return null; |
| 820 | node.cast(Node.While).?.inline_token = inline_token; | | |
| 821 | return node; | | |
| 822 | } | 742 | } |
| 823 | if (inline_token == null) return null; | | |
| 824 | | 743 | |
| 825 | // If we've seen "inline", there should have been a "for" or "while" | 744 | /// IfStatement |
| 826 | try tree.errors.push(.{ | 745 | /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? |
| 827 | .ExpectedInlinable = .{ .token = it.index }, | 746 | /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) |
| 828 | }); | 747 | fn parseIfStatement(p: *Parser) !?*Node { |
| 829 | return error.ParseError; | 748 | const if_node = (try p.parseIfPrefix()) orelse return null; |
| 830 | } | 749 | const if_prefix = if_node.cast(Node.If).?; |
| 831 | | 750 | |
| 832 | /// ForStatement | 751 | const block_expr = (try p.parseBlockExpr()); |
| 833 | /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? | 752 | const assign_expr = if (block_expr == null) |
| 834 | /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) | 753 | try p.expectNode(parseAssignExpr, .{ |
| 835 | fn parseForStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 754 | .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, |
| 836 | const node = (try parseForPrefix(arena, it, tree)) orelse return null; | 755 | }) |
| 837 | const for_prefix = node.cast(Node.For).?; | 756 | else |
| | 757 | null; |
| 838 | | 758 | |
| 839 | if (try parseBlockExpr(arena, it, tree)) |block_expr_node| { | 759 | const semicolon = if (assign_expr != null) p.eatToken(.Semicolon) else null; |
| 840 | for_prefix.body = block_expr_node; | | |
| 841 | | 760 | |
| 842 | if (eatToken(it, .Keyword_else)) |else_token| { | 761 | const else_node = if (semicolon == null) blk: { |
| 843 | const statement_node = try expectNode(arena, it, tree, parseStatement, .{ | 762 | const else_token = p.eatToken(.Keyword_else) orelse break :blk null; |
| 844 | .InvalidToken = .{ .token = it.index }, | 763 | const payload = try p.parsePayload(); |
| | 764 | const else_body = try p.expectNode(parseStatement, .{ |
| | 765 | .InvalidToken = .{ .token = p.tok_i }, |
| 845 | }); | 766 | }); |
| 846 | | 767 | |
| 847 | const else_node = try arena.create(Node.Else); | 768 | const node = try p.arena.allocator.create(Node.Else); |
| 848 | else_node.* = .{ | 769 | node.* = .{ |
| 849 | .else_token = else_token, | 770 | .else_token = else_token, |
| 850 | .payload = null, | 771 | .payload = payload, |
| 851 | .body = statement_node, | 772 | .body = else_body, |
| 852 | }; | 773 | }; |
| 853 | for_prefix.@"else" = else_node; | | |
| 854 | | 774 | |
| 855 | return node; | 775 | break :blk node; |
| | 776 | } else null; |
| | 777 | |
| | 778 | if (block_expr) |body| { |
| | 779 | if_prefix.body = body; |
| | 780 | if_prefix.@"else" = else_node; |
| | 781 | return if_node; |
| 856 | } | 782 | } |
| 857 | | 783 | |
| 858 | return node; | 784 | if (assign_expr) |body| { |
| | 785 | if_prefix.body = body; |
| | 786 | if (semicolon != null) return if_node; |
| | 787 | if (else_node != null) { |
| | 788 | if_prefix.@"else" = else_node; |
| | 789 | return if_node; |
| | 790 | } |
| | 791 | try p.errors.append(p.gpa, .{ |
| | 792 | .ExpectedSemiOrElse = .{ .token = p.tok_i }, |
| | 793 | }); |
| | 794 | } |
| | 795 | |
| | 796 | return if_node; |
| 859 | } | 797 | } |
| 860 | | 798 | |
| 861 | if (try parseAssignExpr(arena, it, tree)) |assign_expr| { | 799 | /// LabeledStatement <- BlockLabel? (Block / LoopStatement) |
| 862 | for_prefix.body = assign_expr; | 800 | fn parseLabeledStatement(p: *Parser) !?*Node { |
| | 801 | var colon: TokenIndex = undefined; |
| | 802 | const label_token = p.parseBlockLabel(&colon); |
| | 803 | |
| | 804 | if (try p.parseBlock()) |node| { |
| | 805 | node.cast(Node.Block).?.label = label_token; |
| | 806 | return node; |
| | 807 | } |
| 863 | | 808 | |
| 864 | if (eatToken(it, .Semicolon) != null) return node; | 809 | if (try p.parseLoopStatement()) |node| { |
| | 810 | if (node.cast(Node.For)) |for_node| { |
| | 811 | for_node.label = label_token; |
| | 812 | } else if (node.cast(Node.While)) |while_node| { |
| | 813 | while_node.label = label_token; |
| | 814 | } else unreachable; |
| | 815 | return node; |
| | 816 | } |
| 865 | | 817 | |
| 866 | if (eatToken(it, .Keyword_else)) |else_token| { | 818 | if (label_token != null) { |
| 867 | const statement_node = try expectNode(arena, it, tree, parseStatement, .{ | 819 | try p.errors.append(p.gpa, .{ |
| 868 | .ExpectedStatement = .{ .token = it.index }, | 820 | .ExpectedLabelable = .{ .token = p.tok_i }, |
| 869 | }); | 821 | }); |
| | 822 | return error.ParseError; |
| | 823 | } |
| 870 | | 824 | |
| 871 | const else_node = try arena.create(Node.Else); | 825 | return null; |
| 872 | else_node.* = .{ | 826 | } |
| 873 | .else_token = else_token, | 827 | |
| 874 | .payload = null, | 828 | /// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) |
| 875 | .body = statement_node, | 829 | fn parseLoopStatement(p: *Parser) !?*Node { |
| 876 | }; | 830 | const inline_token = p.eatToken(.Keyword_inline); |
| 877 | for_prefix.@"else" = else_node; | 831 | |
| | 832 | if (try p.parseForStatement()) |node| { |
| | 833 | node.cast(Node.For).?.inline_token = inline_token; |
| 878 | return node; | 834 | return node; |
| 879 | } | 835 | } |
| 880 | | 836 | |
| 881 | try tree.errors.push(.{ | 837 | if (try p.parseWhileStatement()) |node| { |
| 882 | .ExpectedSemiOrElse = .{ .token = it.index }, | 838 | node.cast(Node.While).?.inline_token = inline_token; |
| 883 | }); | 839 | return node; |
| | 840 | } |
| | 841 | if (inline_token == null) return null; |
| 884 | | 842 | |
| 885 | return node; | 843 | // If we've seen "inline", there should have been a "for" or "while" |
| | 844 | try p.errors.append(p.gpa, .{ |
| | 845 | .ExpectedInlinable = .{ .token = p.tok_i }, |
| | 846 | }); |
| | 847 | return error.ParseError; |
| 886 | } | 848 | } |
| 887 | | 849 | |
| 888 | return null; | 850 | /// ForStatement |
| 889 | } | 851 | /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? |
| 890 | | 852 | /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) |
| 891 | /// WhileStatement | 853 | fn parseForStatement(p: *Parser) !?*Node { |
| 892 | /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? | 854 | const node = (try p.parseForPrefix()) orelse return null; |
| 893 | /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | 855 | const for_prefix = node.cast(Node.For).?; |
| 894 | fn parseWhileStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 895 | const node = (try parseWhilePrefix(arena, it, tree)) orelse return null; | | |
| 896 | const while_prefix = node.cast(Node.While).?; | | |
| 897 | | 856 | |
| 898 | if (try parseBlockExpr(arena, it, tree)) |block_expr_node| { | 857 | if (try p.parseBlockExpr()) |block_expr_node| { |
| 899 | while_prefix.body = block_expr_node; | 858 | for_prefix.body = block_expr_node; |
| 900 | | 859 | |
| 901 | if (eatToken(it, .Keyword_else)) |else_token| { | 860 | if (p.eatToken(.Keyword_else)) |else_token| { |
| 902 | const payload = try parsePayload(arena, it, tree); | 861 | const statement_node = try p.expectNode(parseStatement, .{ |
| | 862 | .InvalidToken = .{ .token = p.tok_i }, |
| | 863 | }); |
| 903 | | 864 | |
| 904 | const statement_node = try expectNode(arena, it, tree, parseStatement, .{ | 865 | const else_node = try p.arena.allocator.create(Node.Else); |
| 905 | .InvalidToken = .{ .token = it.index }, | 866 | else_node.* = .{ |
| 906 | }); | 867 | .else_token = else_token, |
| | 868 | .payload = null, |
| | 869 | .body = statement_node, |
| | 870 | }; |
| | 871 | for_prefix.@"else" = else_node; |
| 907 | | 872 | |
| 908 | const else_node = try arena.create(Node.Else); | 873 | return node; |
| 909 | else_node.* = .{ | 874 | } |
| 910 | .else_token = else_token, | | |
| 911 | .payload = payload, | | |
| 912 | .body = statement_node, | | |
| 913 | }; | | |
| 914 | while_prefix.@"else" = else_node; | | |
| 915 | | 875 | |
| 916 | return node; | 876 | return node; |
| 917 | } | 877 | } |
| 918 | | 878 | |
| 919 | return node; | 879 | if (try p.parseAssignExpr()) |assign_expr| { |
| 920 | } | 880 | for_prefix.body = assign_expr; |
| 921 | | 881 | |
| 922 | if (try parseAssignExpr(arena, it, tree)) |assign_expr_node| { | 882 | if (p.eatToken(.Semicolon) != null) return node; |
| 923 | while_prefix.body = assign_expr_node; | | |
| 924 | | 883 | |
| 925 | if (eatToken(it, .Semicolon) != null) return node; | 884 | if (p.eatToken(.Keyword_else)) |else_token| { |
| | 885 | const statement_node = try p.expectNode(parseStatement, .{ |
| | 886 | .ExpectedStatement = .{ .token = p.tok_i }, |
| | 887 | }); |
| 926 | | 888 | |
| 927 | if (eatToken(it, .Keyword_else)) |else_token| { | 889 | const else_node = try p.arena.allocator.create(Node.Else); |
| 928 | const payload = try parsePayload(arena, it, tree); | 890 | else_node.* = .{ |
| | 891 | .else_token = else_token, |
| | 892 | .payload = null, |
| | 893 | .body = statement_node, |
| | 894 | }; |
| | 895 | for_prefix.@"else" = else_node; |
| | 896 | return node; |
| | 897 | } |
| 929 | | 898 | |
| 930 | const statement_node = try expectNode(arena, it, tree, parseStatement, .{ | 899 | try p.errors.append(p.gpa, .{ |
| 931 | .ExpectedStatement = .{ .token = it.index }, | 900 | .ExpectedSemiOrElse = .{ .token = p.tok_i }, |
| 932 | }); | 901 | }); |
| 933 | | 902 | |
| 934 | const else_node = try arena.create(Node.Else); | | |
| 935 | else_node.* = .{ | | |
| 936 | .else_token = else_token, | | |
| 937 | .payload = payload, | | |
| 938 | .body = statement_node, | | |
| 939 | }; | | |
| 940 | while_prefix.@"else" = else_node; | | |
| 941 | return node; | 903 | return node; |
| 942 | } | 904 | } |
| 943 | | 905 | |
| 944 | try tree.errors.push(.{ | 906 | return null; |
| 945 | .ExpectedSemiOrElse = .{ .token = it.index }, | | |
| 946 | }); | | |
| 947 | | | |
| 948 | return node; | | |
| 949 | } | 907 | } |
| 950 | | 908 | |
| 951 | return null; | 909 | /// WhileStatement |
| 952 | } | 910 | /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? |
| | 911 | /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) |
| | 912 | fn parseWhileStatement(p: *Parser) !?*Node { |
| | 913 | const node = (try p.parseWhilePrefix()) orelse return null; |
| | 914 | const while_prefix = node.cast(Node.While).?; |
| 953 | | 915 | |
| 954 | /// BlockExprStatement | 916 | if (try p.parseBlockExpr()) |block_expr_node| { |
| 955 | /// <- BlockExpr | 917 | while_prefix.body = block_expr_node; |
| 956 | /// / AssignExpr SEMICOLON | | |
| 957 | fn parseBlockExprStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 958 | if (try parseBlockExpr(arena, it, tree)) |node| return node; | | |
| 959 | if (try parseAssignExpr(arena, it, tree)) |node| { | | |
| 960 | _ = try expectTokenRecoverable(it, tree, .Semicolon); | | |
| 961 | return node; | | |
| 962 | } | | |
| 963 | return null; | | |
| 964 | } | | |
| 965 | | 918 | |
| 966 | /// BlockExpr <- BlockLabel? Block | 919 | if (p.eatToken(.Keyword_else)) |else_token| { |
| 967 | fn parseBlockExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*Node { | 920 | const payload = try p.parsePayload(); |
| 968 | var colon: TokenIndex = undefined; | | |
| 969 | const label_token = parseBlockLabel(arena, it, tree, &colon); | | |
| 970 | const block_node = (try parseBlock(arena, it, tree)) orelse { | | |
| 971 | if (label_token) |label| { | | |
| 972 | putBackToken(it, label + 1); // ":" | | |
| 973 | putBackToken(it, label); // IDENTIFIER | | |
| 974 | } | | |
| 975 | return null; | | |
| 976 | }; | | |
| 977 | block_node.cast(Node.Block).?.label = label_token; | | |
| 978 | return block_node; | | |
| 979 | } | | |
| 980 | | 921 | |
| 981 | /// AssignExpr <- Expr (AssignOp Expr)? | 922 | const statement_node = try p.expectNode(parseStatement, .{ |
| 982 | fn parseAssignExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 923 | .InvalidToken = .{ .token = p.tok_i }, |
| 983 | return parseBinOpExpr(arena, it, tree, parseAssignOp, parseExpr, .Once); | 924 | }); |
| 984 | } | | |
| 985 | | 925 | |
| 986 | /// Expr <- KEYWORD_try* BoolOrExpr | 926 | const else_node = try p.arena.allocator.create(Node.Else); |
| 987 | fn parseExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*Node { | 927 | else_node.* = .{ |
| 988 | return parsePrefixOpExpr(arena, it, tree, parseTry, parseBoolOrExpr); | 928 | .else_token = else_token, |
| 989 | } | 929 | .payload = payload, |
| | 930 | .body = statement_node, |
| | 931 | }; |
| | 932 | while_prefix.@"else" = else_node; |
| 990 | | 933 | |
| 991 | /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)* | 934 | return node; |
| 992 | fn parseBoolOrExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 935 | } |
| 993 | return parseBinOpExpr( | | |
| 994 | arena, | | |
| 995 | it, | | |
| 996 | tree, | | |
| 997 | SimpleBinOpParseFn(.Keyword_or, Node.InfixOp.Op.BoolOr), | | |
| 998 | parseBoolAndExpr, | | |
| 999 | .Infinitely, | | |
| 1000 | ); | | |
| 1001 | } | | |
| 1002 | | 936 | |
| 1003 | /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)* | 937 | return node; |
| 1004 | fn parseBoolAndExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 938 | } |
| 1005 | return parseBinOpExpr( | | |
| 1006 | arena, | | |
| 1007 | it, | | |
| 1008 | tree, | | |
| 1009 | SimpleBinOpParseFn(.Keyword_and, .BoolAnd), | | |
| 1010 | parseCompareExpr, | | |
| 1011 | .Infinitely, | | |
| 1012 | ); | | |
| 1013 | } | | |
| 1014 | | 939 | |
| 1015 | /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? | 940 | if (try p.parseAssignExpr()) |assign_expr_node| { |
| 1016 | fn parseCompareExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 941 | while_prefix.body = assign_expr_node; |
| 1017 | return parseBinOpExpr(arena, it, tree, parseCompareOp, parseBitwiseExpr, .Once); | | |
| 1018 | } | | |
| 1019 | | 942 | |
| 1020 | /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* | 943 | if (p.eatToken(.Semicolon) != null) return node; |
| 1021 | fn parseBitwiseExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 1022 | return parseBinOpExpr(arena, it, tree, parseBitwiseOp, parseBitShiftExpr, .Infinitely); | | |
| 1023 | } | | |
| 1024 | | 944 | |
| 1025 | /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* | 945 | if (p.eatToken(.Keyword_else)) |else_token| { |
| 1026 | fn parseBitShiftExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 946 | const payload = try p.parsePayload(); |
| 1027 | return parseBinOpExpr(arena, it, tree, parseBitShiftOp, parseAdditionExpr, .Infinitely); | | |
| 1028 | } | | |
| 1029 | | 947 | |
| 1030 | /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* | 948 | const statement_node = try p.expectNode(parseStatement, .{ |
| 1031 | fn parseAdditionExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 949 | .ExpectedStatement = .{ .token = p.tok_i }, |
| 1032 | return parseBinOpExpr(arena, it, tree, parseAdditionOp, parseMultiplyExpr, .Infinitely); | 950 | }); |
| 1033 | } | | |
| 1034 | | 951 | |
| 1035 | /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* | 952 | const else_node = try p.arena.allocator.create(Node.Else); |
| 1036 | fn parseMultiplyExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 953 | else_node.* = .{ |
| 1037 | return parseBinOpExpr(arena, it, tree, parseMultiplyOp, parsePrefixExpr, .Infinitely); | 954 | .else_token = else_token, |
| 1038 | } | 955 | .payload = payload, |
| | 956 | .body = statement_node, |
| | 957 | }; |
| | 958 | while_prefix.@"else" = else_node; |
| | 959 | return node; |
| | 960 | } |
| 1039 | | 961 | |
| 1040 | /// PrefixExpr <- PrefixOp* PrimaryExpr | 962 | try p.errors.append(p.gpa, .{ |
| 1041 | fn parsePrefixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 963 | .ExpectedSemiOrElse = .{ .token = p.tok_i }, |
| 1042 | return parsePrefixOpExpr(arena, it, tree, parsePrefixOp, parsePrimaryExpr); | 964 | }); |
| 1043 | } | | |
| 1044 | | 965 | |
| 1045 | /// PrimaryExpr | 966 | return node; |
| 1046 | /// <- AsmExpr | 967 | } |
| 1047 | /// / IfExpr | | |
| 1048 | /// / KEYWORD_break BreakLabel? Expr? | | |
| 1049 | /// / KEYWORD_comptime Expr | | |
| 1050 | /// / KEYWORD_nosuspend Expr | | |
| 1051 | /// / KEYWORD_continue BreakLabel? | | |
| 1052 | /// / KEYWORD_resume Expr | | |
| 1053 | /// / KEYWORD_return Expr? | | |
| 1054 | /// / BlockLabel? LoopExpr | | |
| 1055 | /// / Block | | |
| 1056 | /// / CurlySuffixExpr | | |
| 1057 | fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 1058 | if (try parseAsmExpr(arena, it, tree)) |node| return node; | | |
| 1059 | if (try parseIfExpr(arena, it, tree)) |node| return node; | | |
| 1060 | | | |
| 1061 | if (eatToken(it, .Keyword_break)) |token| { | | |
| 1062 | const label = try parseBreakLabel(arena, it, tree); | | |
| 1063 | const expr_node = try parseExpr(arena, it, tree); | | |
| 1064 | const node = try arena.create(Node.ControlFlowExpression); | | |
| 1065 | node.* = .{ | | |
| 1066 | .ltoken = token, | | |
| 1067 | .kind = .{ .Break = label }, | | |
| 1068 | .rhs = expr_node, | | |
| 1069 | }; | | |
| 1070 | return &node.base; | | |
| 1071 | } | | |
| 1072 | | 968 | |
| 1073 | if (eatToken(it, .Keyword_comptime)) |token| { | 969 | return null; |
| 1074 | const expr_node = try expectNode(arena, it, tree, parseExpr, .{ | | |
| 1075 | .ExpectedExpr = .{ .token = it.index }, | | |
| 1076 | }); | | |
| 1077 | const node = try arena.create(Node.Comptime); | | |
| 1078 | node.* = .{ | | |
| 1079 | .doc_comments = null, | | |
| 1080 | .comptime_token = token, | | |
| 1081 | .expr = expr_node, | | |
| 1082 | }; | | |
| 1083 | return &node.base; | | |
| 1084 | } | 970 | } |
| 1085 | | 971 | |
| 1086 | if (eatToken(it, .Keyword_nosuspend)) |token| { | 972 | /// BlockExprStatement |
| 1087 | const expr_node = try expectNode(arena, it, tree, parseExpr, .{ | 973 | /// <- BlockExpr |
| 1088 | .ExpectedExpr = .{ .token = it.index }, | 974 | /// / AssignExpr SEMICOLON |
| 1089 | }); | 975 | fn parseBlockExprStatement(p: *Parser) !?*Node { |
| 1090 | const node = try arena.create(Node.Nosuspend); | 976 | if (try p.parseBlockExpr()) |node| return node; |
| 1091 | node.* = .{ | 977 | if (try p.parseAssignExpr()) |node| { |
| 1092 | .nosuspend_token = token, | 978 | _ = try p.expectTokenRecoverable(.Semicolon); |
| 1093 | .expr = expr_node, | 979 | return node; |
| 1094 | }; | 980 | } |
| 1095 | return &node.base; | 981 | return null; |
| 1096 | } | 982 | } |
| 1097 | | 983 | |
| 1098 | if (eatToken(it, .Keyword_continue)) |token| { | 984 | /// BlockExpr <- BlockLabel? Block |
| 1099 | const label = try parseBreakLabel(arena, it, tree); | 985 | fn parseBlockExpr(p: *Parser) Error!?*Node { |
| 1100 | const node = try arena.create(Node.ControlFlowExpression); | 986 | var colon: TokenIndex = undefined; |
| 1101 | node.* = .{ | 987 | const label_token = p.parseBlockLabel(&colon); |
| 1102 | .ltoken = token, | 988 | const block_node = (try p.parseBlock()) orelse { |
| 1103 | .kind = .{ .Continue = label }, | 989 | if (label_token) |label| { |
| 1104 | .rhs = null, | 990 | p.putBackToken(label + 1); // ":" |
| | 991 | p.putBackToken(label); // IDENTIFIER |
| | 992 | } |
| | 993 | return null; |
| 1105 | }; | 994 | }; |
| 1106 | return &node.base; | 995 | block_node.cast(Node.Block).?.label = label_token; |
| | 996 | return block_node; |
| 1107 | } | 997 | } |
| 1108 | | 998 | |
| 1109 | if (eatToken(it, .Keyword_resume)) |token| { | 999 | /// AssignExpr <- Expr (AssignOp Expr)? |
| 1110 | const expr_node = try expectNode(arena, it, tree, parseExpr, .{ | 1000 | fn parseAssignExpr(p: *Parser) !?*Node { |
| 1111 | .ExpectedExpr = .{ .token = it.index }, | 1001 | return p.parseBinOpExpr(parseAssignOp, parseExpr, .Once); |
| 1112 | }); | | |
| 1113 | const node = try arena.create(Node.PrefixOp); | | |
| 1114 | node.* = .{ | | |
| 1115 | .op_token = token, | | |
| 1116 | .op = .Resume, | | |
| 1117 | .rhs = expr_node, | | |
| 1118 | }; | | |
| 1119 | return &node.base; | | |
| 1120 | } | 1002 | } |
| 1121 | | 1003 | |
| 1122 | if (eatToken(it, .Keyword_return)) |token| { | 1004 | /// Expr <- KEYWORD_try* BoolOrExpr |
| 1123 | const expr_node = try parseExpr(arena, it, tree); | 1005 | fn parseExpr(p: *Parser) Error!?*Node { |
| 1124 | const node = try arena.create(Node.ControlFlowExpression); | 1006 | return p.parsePrefixOpExpr(parseTry, parseBoolOrExpr); |
| 1125 | node.* = .{ | | |
| 1126 | .ltoken = token, | | |
| 1127 | .kind = .Return, | | |
| 1128 | .rhs = expr_node, | | |
| 1129 | }; | | |
| 1130 | return &node.base; | | |
| 1131 | } | 1007 | } |
| 1132 | | 1008 | |
| 1133 | var colon: TokenIndex = undefined; | 1009 | /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)* |
| 1134 | const label = parseBlockLabel(arena, it, tree, &colon); | 1010 | fn parseBoolOrExpr(p: *Parser) !?*Node { |
| 1135 | if (try parseLoopExpr(arena, it, tree)) |node| { | 1011 | return p.parseBinOpExpr( |
| 1136 | if (node.cast(Node.For)) |for_node| { | 1012 | SimpleBinOpParseFn(.Keyword_or, Node.InfixOp.Op.BoolOr), |
| 1137 | for_node.label = label; | 1013 | parseBoolAndExpr, |
| 1138 | } else if (node.cast(Node.While)) |while_node| { | 1014 | .Infinitely, |
| 1139 | while_node.label = label; | 1015 | ); |
| 1140 | } else unreachable; | | |
| 1141 | return node; | | |
| 1142 | } | | |
| 1143 | if (label) |token| { | | |
| 1144 | putBackToken(it, token + 1); // ":" | | |
| 1145 | putBackToken(it, token); // IDENTIFIER | | |
| 1146 | } | 1016 | } |
| 1147 | | 1017 | |
| 1148 | if (try parseBlock(arena, it, tree)) |node| return node; | 1018 | /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)* |
| 1149 | if (try parseCurlySuffixExpr(arena, it, tree)) |node| return node; | 1019 | fn parseBoolAndExpr(p: *Parser) !?*Node { |
| 1150 | | 1020 | return p.parseBinOpExpr( |
| 1151 | return null; | 1021 | SimpleBinOpParseFn(.Keyword_and, .BoolAnd), |
| 1152 | } | 1022 | parseCompareExpr, |
| | 1023 | .Infinitely, |
| | 1024 | ); |
| | 1025 | } |
| 1153 | | 1026 | |
| 1154 | /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? | 1027 | /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? |
| 1155 | fn parseIfExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1028 | fn parseCompareExpr(p: *Parser) !?*Node { |
| 1156 | return parseIf(arena, it, tree, parseExpr); | 1029 | return p.parseBinOpExpr(parseCompareOp, parseBitwiseExpr, .Once); |
| 1157 | } | 1030 | } |
| 1158 | | 1031 | |
| 1159 | /// Block <- LBRACE Statement* RBRACE | 1032 | /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* |
| 1160 | fn parseBlock(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1033 | fn parseBitwiseExpr(p: *Parser) !?*Node { |
| 1161 | const lbrace = eatToken(it, .LBrace) orelse return null; | 1034 | return p.parseBinOpExpr(parseBitwiseOp, parseBitShiftExpr, .Infinitely); |
| | 1035 | } |
| 1162 | | 1036 | |
| 1163 | var statements = Node.Block.StatementList.init(arena); | 1037 | /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* |
| 1164 | while (true) { | 1038 | fn parseBitShiftExpr(p: *Parser) !?*Node { |
| 1165 | const statement = (parseStatement(arena, it, tree) catch |err| switch (err) { | 1039 | return p.parseBinOpExpr(parseBitShiftOp, parseAdditionExpr, .Infinitely); |
| 1166 | error.OutOfMemory => return error.OutOfMemory, | | |
| 1167 | error.ParseError => { | | |
| 1168 | // try to skip to the next statement | | |
| 1169 | findNextStmt(it); | | |
| 1170 | continue; | | |
| 1171 | }, | | |
| 1172 | }) orelse break; | | |
| 1173 | try statements.push(statement); | | |
| 1174 | } | 1040 | } |
| 1175 | | 1041 | |
| 1176 | const rbrace = try expectToken(it, tree, .RBrace); | 1042 | /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* |
| | 1043 | fn parseAdditionExpr(p: *Parser) !?*Node { |
| | 1044 | return p.parseBinOpExpr(parseAdditionOp, parseMultiplyExpr, .Infinitely); |
| | 1045 | } |
| 1177 | | 1046 | |
| 1178 | const block_node = try arena.create(Node.Block); | 1047 | /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* |
| 1179 | block_node.* = .{ | 1048 | fn parseMultiplyExpr(p: *Parser) !?*Node { |
| 1180 | .label = null, | 1049 | return p.parseBinOpExpr(parseMultiplyOp, parsePrefixExpr, .Infinitely); |
| 1181 | .lbrace = lbrace, | 1050 | } |
| 1182 | .statements = statements, | | |
| 1183 | .rbrace = rbrace, | | |
| 1184 | }; | | |
| 1185 | | 1051 | |
| 1186 | return &block_node.base; | 1052 | /// PrefixExpr <- PrefixOp* PrimaryExpr |
| 1187 | } | 1053 | fn parsePrefixExpr(p: *Parser) !?*Node { |
| | 1054 | return p.parsePrefixOpExpr(parsePrefixOp, parsePrimaryExpr); |
| | 1055 | } |
| 1188 | | 1056 | |
| 1189 | /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) | 1057 | /// PrimaryExpr |
| 1190 | fn parseLoopExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1058 | /// <- AsmExpr |
| 1191 | const inline_token = eatToken(it, .Keyword_inline); | 1059 | /// / IfExpr |
| | 1060 | /// / KEYWORD_break BreakLabel? Expr? |
| | 1061 | /// / KEYWORD_comptime Expr |
| | 1062 | /// / KEYWORD_nosuspend Expr |
| | 1063 | /// / KEYWORD_continue BreakLabel? |
| | 1064 | /// / KEYWORD_resume Expr |
| | 1065 | /// / KEYWORD_return Expr? |
| | 1066 | /// / BlockLabel? LoopExpr |
| | 1067 | /// / Block |
| | 1068 | /// / CurlySuffixExpr |
| | 1069 | fn parsePrimaryExpr(p: *Parser) !?*Node { |
| | 1070 | if (try p.parseAsmExpr()) |node| return node; |
| | 1071 | if (try p.parseIfExpr()) |node| return node; |
| 1192 | | 1072 | |
| 1193 | if (try parseForExpr(arena, it, tree)) |node| { | 1073 | if (p.eatToken(.Keyword_break)) |token| { |
| 1194 | node.cast(Node.For).?.inline_token = inline_token; | 1074 | const label = try p.parseBreakLabel(); |
| 1195 | return node; | 1075 | const expr_node = try p.parseExpr(); |
| 1196 | } | 1076 | const node = try p.arena.allocator.create(Node.ControlFlowExpression); |
| | 1077 | node.* = .{ |
| | 1078 | .ltoken = token, |
| | 1079 | .kind = .{ .Break = label }, |
| | 1080 | .rhs = expr_node, |
| | 1081 | }; |
| | 1082 | return &node.base; |
| | 1083 | } |
| 1197 | | 1084 | |
| 1198 | if (try parseWhileExpr(arena, it, tree)) |node| { | 1085 | if (p.eatToken(.Keyword_comptime)) |token| { |
| 1199 | node.cast(Node.While).?.inline_token = inline_token; | 1086 | const expr_node = try p.expectNode(parseExpr, .{ |
| 1200 | return node; | 1087 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 1201 | } | 1088 | }); |
| | 1089 | const node = try p.arena.allocator.create(Node.Comptime); |
| | 1090 | node.* = .{ |
| | 1091 | .doc_comments = null, |
| | 1092 | .comptime_token = token, |
| | 1093 | .expr = expr_node, |
| | 1094 | }; |
| | 1095 | return &node.base; |
| | 1096 | } |
| 1202 | | 1097 | |
| 1203 | if (inline_token == null) return null; | 1098 | if (p.eatToken(.Keyword_nosuspend)) |token| { |
| | 1099 | const expr_node = try p.expectNode(parseExpr, .{ |
| | 1100 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 1101 | }); |
| | 1102 | const node = try p.arena.allocator.create(Node.Nosuspend); |
| | 1103 | node.* = .{ |
| | 1104 | .nosuspend_token = token, |
| | 1105 | .expr = expr_node, |
| | 1106 | }; |
| | 1107 | return &node.base; |
| | 1108 | } |
| 1204 | | 1109 | |
| 1205 | // If we've seen "inline", there should have been a "for" or "while" | 1110 | if (p.eatToken(.Keyword_continue)) |token| { |
| 1206 | try tree.errors.push(.{ | 1111 | const label = try p.parseBreakLabel(); |
| 1207 | .ExpectedInlinable = .{ .token = it.index }, | 1112 | const node = try p.arena.allocator.create(Node.ControlFlowExpression); |
| 1208 | }); | 1113 | node.* = .{ |
| 1209 | return error.ParseError; | 1114 | .ltoken = token, |
| 1210 | } | 1115 | .kind = .{ .Continue = label }, |
| | 1116 | .rhs = null, |
| | 1117 | }; |
| | 1118 | return &node.base; |
| | 1119 | } |
| 1211 | | 1120 | |
| 1212 | /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? | 1121 | if (p.eatToken(.Keyword_resume)) |token| { |
| 1213 | fn parseForExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1122 | const expr_node = try p.expectNode(parseExpr, .{ |
| 1214 | const node = (try parseForPrefix(arena, it, tree)) orelse return null; | 1123 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 1215 | const for_prefix = node.cast(Node.For).?; | 1124 | }); |
| | 1125 | const node = try p.arena.allocator.create(Node.PrefixOp); |
| | 1126 | node.* = .{ |
| | 1127 | .op_token = token, |
| | 1128 | .op = .Resume, |
| | 1129 | .rhs = expr_node, |
| | 1130 | }; |
| | 1131 | return &node.base; |
| | 1132 | } |
| 1216 | | 1133 | |
| 1217 | const body_node = try expectNode(arena, it, tree, parseExpr, .{ | 1134 | if (p.eatToken(.Keyword_return)) |token| { |
| 1218 | .ExpectedExpr = .{ .token = it.index }, | 1135 | const expr_node = try p.parseExpr(); |
| 1219 | }); | 1136 | const node = try p.arena.allocator.create(Node.ControlFlowExpression); |
| 1220 | for_prefix.body = body_node; | 1137 | node.* = .{ |
| | 1138 | .ltoken = token, |
| | 1139 | .kind = .Return, |
| | 1140 | .rhs = expr_node, |
| | 1141 | }; |
| | 1142 | return &node.base; |
| | 1143 | } |
| 1221 | | 1144 | |
| 1222 | if (eatToken(it, .Keyword_else)) |else_token| { | 1145 | var colon: TokenIndex = undefined; |
| 1223 | const body = try expectNode(arena, it, tree, parseExpr, .{ | 1146 | const label = p.parseBlockLabel(&colon); |
| 1224 | .ExpectedExpr = .{ .token = it.index }, | 1147 | if (try p.parseLoopExpr()) |node| { |
| 1225 | }); | 1148 | if (node.cast(Node.For)) |for_node| { |
| | 1149 | for_node.label = label; |
| | 1150 | } else if (node.cast(Node.While)) |while_node| { |
| | 1151 | while_node.label = label; |
| | 1152 | } else unreachable; |
| | 1153 | return node; |
| | 1154 | } |
| | 1155 | if (label) |token| { |
| | 1156 | p.putBackToken(token + 1); // ":" |
| | 1157 | p.putBackToken(token); // IDENTIFIER |
| | 1158 | } |
| 1226 | | 1159 | |
| 1227 | const else_node = try arena.create(Node.Else); | 1160 | if (try p.parseBlock()) |node| return node; |
| 1228 | else_node.* = .{ | 1161 | if (try p.parseCurlySuffixExpr()) |node| return node; |
| 1229 | .else_token = else_token, | | |
| 1230 | .payload = null, | | |
| 1231 | .body = body, | | |
| 1232 | }; | | |
| 1233 | | 1162 | |
| 1234 | for_prefix.@"else" = else_node; | 1163 | return null; |
| 1235 | } | 1164 | } |
| 1236 | | 1165 | |
| 1237 | return node; | 1166 | /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? |
| 1238 | } | 1167 | fn parseIfExpr(p: *Parser) !?*Node { |
| | 1168 | return p.parseIf(parseExpr); |
| | 1169 | } |
| 1239 | | 1170 | |
| 1240 | /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? | 1171 | /// Block <- LBRACE Statement* RBRACE |
| 1241 | fn parseWhileExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1172 | fn parseBlock(p: *Parser) !?*Node { |
| 1242 | const node = (try parseWhilePrefix(arena, it, tree)) orelse return null; | 1173 | const lbrace = p.eatToken(.LBrace) orelse return null; |
| 1243 | const while_prefix = node.cast(Node.While).?; | | |
| 1244 | | 1174 | |
| 1245 | const body_node = try expectNode(arena, it, tree, parseExpr, .{ | 1175 | var statements = Node.Block.StatementList{}; |
| 1246 | .ExpectedExpr = .{ .token = it.index }, | 1176 | var statements_it = &statements.first; |
| 1247 | }); | 1177 | while (true) { |
| 1248 | while_prefix.body = body_node; | 1178 | const statement = (p.parseStatement() catch |err| switch (err) { |
| | 1179 | error.OutOfMemory => return error.OutOfMemory, |
| | 1180 | error.ParseError => { |
| | 1181 | // try to skip to the next statement |
| | 1182 | p.findNextStmt(); |
| | 1183 | continue; |
| | 1184 | }, |
| | 1185 | }) orelse break; |
| | 1186 | statements_it = try p.llpush(*Node, statements_it, statement); |
| | 1187 | } |
| 1249 | | 1188 | |
| 1250 | if (eatToken(it, .Keyword_else)) |else_token| { | 1189 | const rbrace = try p.expectToken(.RBrace); |
| 1251 | const payload = try parsePayload(arena, it, tree); | | |
| 1252 | const body = try expectNode(arena, it, tree, parseExpr, .{ | | |
| 1253 | .ExpectedExpr = .{ .token = it.index }, | | |
| 1254 | }); | | |
| 1255 | | 1190 | |
| 1256 | const else_node = try arena.create(Node.Else); | 1191 | const block_node = try p.arena.allocator.create(Node.Block); |
| 1257 | else_node.* = .{ | 1192 | block_node.* = .{ |
| 1258 | .else_token = else_token, | 1193 | .label = null, |
| 1259 | .payload = payload, | 1194 | .lbrace = lbrace, |
| 1260 | .body = body, | 1195 | .statements = statements, |
| | 1196 | .rbrace = rbrace, |
| 1261 | }; | 1197 | }; |
| 1262 | | 1198 | |
| 1263 | while_prefix.@"else" = else_node; | 1199 | return &block_node.base; |
| 1264 | } | 1200 | } |
| 1265 | | 1201 | |
| 1266 | return node; | 1202 | /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) |
| 1267 | } | 1203 | fn parseLoopExpr(p: *Parser) !?*Node { |
| 1268 | | 1204 | const inline_token = p.eatToken(.Keyword_inline); |
| 1269 | /// CurlySuffixExpr <- TypeExpr InitList? | | |
| 1270 | fn parseCurlySuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 1271 | const type_expr = (try parseTypeExpr(arena, it, tree)) orelse return null; | | |
| 1272 | const suffix_op = (try parseInitList(arena, it, tree)) orelse return type_expr; | | |
| 1273 | suffix_op.lhs.node = type_expr; | | |
| 1274 | return &suffix_op.base; | | |
| 1275 | } | | |
| 1276 | | 1205 | |
| 1277 | /// InitList | 1206 | if (try p.parseForExpr()) |node| { |
| 1278 | /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE | 1207 | node.cast(Node.For).?.inline_token = inline_token; |
| 1279 | /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE | 1208 | return node; |
| 1280 | /// / LBRACE RBRACE | | |
| 1281 | fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.SuffixOp { | | |
| 1282 | const lbrace = eatToken(it, .LBrace) orelse return null; | | |
| 1283 | var init_list = Node.SuffixOp.Op.InitList.init(arena); | | |
| 1284 | | | |
| 1285 | const op: Node.SuffixOp.Op = blk: { | | |
| 1286 | if (try parseFieldInit(arena, it, tree)) |field_init| { | | |
| 1287 | try init_list.push(field_init); | | |
| 1288 | while (eatToken(it, .Comma)) |_| { | | |
| 1289 | const next = (try parseFieldInit(arena, it, tree)) orelse break; | | |
| 1290 | try init_list.push(next); | | |
| 1291 | } | | |
| 1292 | break :blk .{ .StructInitializer = init_list }; | | |
| 1293 | } | 1209 | } |
| 1294 | | 1210 | |
| 1295 | if (try parseExpr(arena, it, tree)) |expr| { | 1211 | if (try p.parseWhileExpr()) |node| { |
| 1296 | try init_list.push(expr); | 1212 | node.cast(Node.While).?.inline_token = inline_token; |
| 1297 | while (eatToken(it, .Comma)) |_| { | 1213 | return node; |
| 1298 | const next = (try parseExpr(arena, it, tree)) orelse break; | | |
| 1299 | try init_list.push(next); | | |
| 1300 | } | | |
| 1301 | break :blk .{ .ArrayInitializer = init_list }; | | |
| 1302 | } | 1214 | } |
| 1303 | | 1215 | |
| 1304 | break :blk .{ .StructInitializer = init_list }; | 1216 | if (inline_token == null) return null; |
| 1305 | }; | | |
| 1306 | | | |
| 1307 | const node = try arena.create(Node.SuffixOp); | | |
| 1308 | node.* = .{ | | |
| 1309 | .lhs = .{ .node = undefined }, // set by caller | | |
| 1310 | .op = op, | | |
| 1311 | .rtoken = try expectToken(it, tree, .RBrace), | | |
| 1312 | }; | | |
| 1313 | return node; | | |
| 1314 | } | | |
| 1315 | | | |
| 1316 | /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr | | |
| 1317 | fn parseTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*Node { | | |
| 1318 | return parsePrefixOpExpr(arena, it, tree, parsePrefixTypeOp, parseErrorUnionExpr); | | |
| 1319 | } | | |
| 1320 | | | |
| 1321 | /// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? | | |
| 1322 | fn parseErrorUnionExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 1323 | const suffix_expr = (try parseSuffixExpr(arena, it, tree)) orelse return null; | | |
| 1324 | | 1217 | |
| 1325 | if (try SimpleBinOpParseFn(.Bang, Node.InfixOp.Op.ErrorUnion)(arena, it, tree)) |node| { | 1218 | // If we've seen "inline", there should have been a "for" or "while" |
| 1326 | const error_union = node.cast(Node.InfixOp).?; | 1219 | try p.errors.append(p.gpa, .{ |
| 1327 | const type_expr = try expectNode(arena, it, tree, parseTypeExpr, .{ | 1220 | .ExpectedInlinable = .{ .token = p.tok_i }, |
| 1328 | .ExpectedTypeExpr = .{ .token = it.index }, | | |
| 1329 | }); | 1221 | }); |
| 1330 | error_union.lhs = suffix_expr; | 1222 | return error.ParseError; |
| 1331 | error_union.rhs = type_expr; | | |
| 1332 | return node; | | |
| 1333 | } | 1223 | } |
| 1334 | | 1224 | |
| 1335 | return suffix_expr; | 1225 | /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? |
| 1336 | } | 1226 | fn parseForExpr(p: *Parser) !?*Node { |
| | 1227 | const node = (try p.parseForPrefix()) orelse return null; |
| | 1228 | const for_prefix = node.cast(Node.For).?; |
| 1337 | | 1229 | |
| 1338 | /// SuffixExpr | 1230 | const body_node = try p.expectNode(parseExpr, .{ |
| 1339 | /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments | 1231 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 1340 | /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* | | |
| 1341 | fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 1342 | const maybe_async = eatToken(it, .Keyword_async); | | |
| 1343 | if (maybe_async) |async_token| { | | |
| 1344 | const token_fn = eatToken(it, .Keyword_fn); | | |
| 1345 | if (token_fn != null) { | | |
| 1346 | // TODO: remove this hack when async fn rewriting is | | |
| 1347 | // HACK: If we see the keyword `fn`, then we assume that | | |
| 1348 | // we are parsing an async fn proto, and not a call. | | |
| 1349 | // We therefore put back all tokens consumed by the async | | |
| 1350 | // prefix... | | |
| 1351 | putBackToken(it, token_fn.?); | | |
| 1352 | putBackToken(it, async_token); | | |
| 1353 | return parsePrimaryTypeExpr(arena, it, tree); | | |
| 1354 | } | | |
| 1355 | var res = try expectNode(arena, it, tree, parsePrimaryTypeExpr, .{ | | |
| 1356 | .ExpectedPrimaryTypeExpr = .{ .token = it.index }, | | |
| 1357 | }); | 1232 | }); |
| | 1233 | for_prefix.body = body_node; |
| 1358 | | 1234 | |
| 1359 | while (try parseSuffixOp(arena, it, tree)) |node| { | 1235 | if (p.eatToken(.Keyword_else)) |else_token| { |
| 1360 | switch (node.id) { | 1236 | const body = try p.expectNode(parseExpr, .{ |
| 1361 | .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{ .node = res }, | 1237 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 1362 | .InfixOp => node.cast(Node.InfixOp).?.lhs = res, | 1238 | }); |
| 1363 | else => unreachable, | 1239 | |
| 1364 | } | 1240 | const else_node = try p.arena.allocator.create(Node.Else); |
| 1365 | res = node; | 1241 | else_node.* = .{ |
| | 1242 | .else_token = else_token, |
| | 1243 | .payload = null, |
| | 1244 | .body = body, |
| | 1245 | }; |
| | 1246 | |
| | 1247 | for_prefix.@"else" = else_node; |
| 1366 | } | 1248 | } |
| 1367 | | 1249 | |
| 1368 | const params = (try parseFnCallArguments(arena, it, tree)) orelse { | 1250 | return node; |
| 1369 | try tree.errors.push(.{ | | |
| 1370 | .ExpectedParamList = .{ .token = it.index }, | | |
| 1371 | }); | | |
| 1372 | // ignore this, continue parsing | | |
| 1373 | return res; | | |
| 1374 | }; | | |
| 1375 | const node = try arena.create(Node.SuffixOp); | | |
| 1376 | node.* = .{ | | |
| 1377 | .lhs = .{ .node = res }, | | |
| 1378 | .op = .{ | | |
| 1379 | .Call = .{ | | |
| 1380 | .params = params.list, | | |
| 1381 | .async_token = async_token, | | |
| 1382 | }, | | |
| 1383 | }, | | |
| 1384 | .rtoken = params.rparen, | | |
| 1385 | }; | | |
| 1386 | return &node.base; | | |
| 1387 | } | 1251 | } |
| 1388 | if (try parsePrimaryTypeExpr(arena, it, tree)) |expr| { | | |
| 1389 | var res = expr; | | |
| 1390 | | 1252 | |
| 1391 | while (true) { | 1253 | /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? |
| 1392 | if (try parseSuffixOp(arena, it, tree)) |node| { | 1254 | fn parseWhileExpr(p: *Parser) !?*Node { |
| | 1255 | const node = (try p.parseWhilePrefix()) orelse return null; |
| | 1256 | const while_prefix = node.cast(Node.While).?; |
| | 1257 | |
| | 1258 | const body_node = try p.expectNode(parseExpr, .{ |
| | 1259 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 1260 | }); |
| | 1261 | while_prefix.body = body_node; |
| | 1262 | |
| | 1263 | if (p.eatToken(.Keyword_else)) |else_token| { |
| | 1264 | const payload = try p.parsePayload(); |
| | 1265 | const body = try p.expectNode(parseExpr, .{ |
| | 1266 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 1267 | }); |
| | 1268 | |
| | 1269 | const else_node = try p.arena.allocator.create(Node.Else); |
| | 1270 | else_node.* = .{ |
| | 1271 | .else_token = else_token, |
| | 1272 | .payload = payload, |
| | 1273 | .body = body, |
| | 1274 | }; |
| | 1275 | |
| | 1276 | while_prefix.@"else" = else_node; |
| | 1277 | } |
| | 1278 | |
| | 1279 | return node; |
| | 1280 | } |
| | 1281 | |
| | 1282 | /// CurlySuffixExpr <- TypeExpr InitList? |
| | 1283 | fn parseCurlySuffixExpr(p: *Parser) !?*Node { |
| | 1284 | const type_expr = (try p.parseTypeExpr()) orelse return null; |
| | 1285 | const suffix_op = (try p.parseInitList()) orelse return type_expr; |
| | 1286 | suffix_op.lhs.node = type_expr; |
| | 1287 | return &suffix_op.base; |
| | 1288 | } |
| | 1289 | |
| | 1290 | /// InitList |
| | 1291 | /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE |
| | 1292 | /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE |
| | 1293 | /// / LBRACE RBRACE |
| | 1294 | fn parseInitList(p: *Parser) !?*Node.SuffixOp { |
| | 1295 | const lbrace = p.eatToken(.LBrace) orelse return null; |
| | 1296 | var init_list = Node.SuffixOp.Op.InitList{}; |
| | 1297 | var init_list_it = &init_list.first; |
| | 1298 | |
| | 1299 | const op: Node.SuffixOp.Op = blk: { |
| | 1300 | if (try p.parseFieldInit()) |field_init| { |
| | 1301 | init_list_it = try p.llpush(*Node, init_list_it, field_init); |
| | 1302 | while (p.eatToken(.Comma)) |_| { |
| | 1303 | const next = (try p.parseFieldInit()) orelse break; |
| | 1304 | init_list_it = try p.llpush(*Node, init_list_it, next); |
| | 1305 | } |
| | 1306 | break :blk .{ .StructInitializer = init_list }; |
| | 1307 | } |
| | 1308 | |
| | 1309 | if (try p.parseExpr()) |expr| { |
| | 1310 | init_list_it = try p.llpush(*Node, init_list_it, expr); |
| | 1311 | while (p.eatToken(.Comma)) |_| { |
| | 1312 | const next = (try p.parseExpr()) orelse break; |
| | 1313 | init_list_it = try p.llpush(*Node, init_list_it, next); |
| | 1314 | } |
| | 1315 | break :blk .{ .ArrayInitializer = init_list }; |
| | 1316 | } |
| | 1317 | |
| | 1318 | break :blk .{ .StructInitializer = init_list }; |
| | 1319 | }; |
| | 1320 | |
| | 1321 | const node = try p.arena.allocator.create(Node.SuffixOp); |
| | 1322 | node.* = .{ |
| | 1323 | .lhs = .{ .node = undefined }, // set by caller |
| | 1324 | .op = op, |
| | 1325 | .rtoken = try p.expectToken(.RBrace), |
| | 1326 | }; |
| | 1327 | return node; |
| | 1328 | } |
| | 1329 | |
| | 1330 | /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr |
| | 1331 | fn parseTypeExpr(p: *Parser) Error!?*Node { |
| | 1332 | return p.parsePrefixOpExpr(parsePrefixTypeOp, parseErrorUnionExpr); |
| | 1333 | } |
| | 1334 | |
| | 1335 | /// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? |
| | 1336 | fn parseErrorUnionExpr(p: *Parser) !?*Node { |
| | 1337 | const suffix_expr = (try p.parseSuffixExpr()) orelse return null; |
| | 1338 | |
| | 1339 | if (try SimpleBinOpParseFn(.Bang, Node.InfixOp.Op.ErrorUnion)(p)) |node| { |
| | 1340 | const error_union = node.cast(Node.InfixOp).?; |
| | 1341 | const type_expr = try p.expectNode(parseTypeExpr, .{ |
| | 1342 | .ExpectedTypeExpr = .{ .token = p.tok_i }, |
| | 1343 | }); |
| | 1344 | error_union.lhs = suffix_expr; |
| | 1345 | error_union.rhs = type_expr; |
| | 1346 | return node; |
| | 1347 | } |
| | 1348 | |
| | 1349 | return suffix_expr; |
| | 1350 | } |
| | 1351 | |
| | 1352 | /// SuffixExpr |
| | 1353 | /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments |
| | 1354 | /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* |
| | 1355 | fn parseSuffixExpr(p: *Parser) !?*Node { |
| | 1356 | const maybe_async = p.eatToken(.Keyword_async); |
| | 1357 | if (maybe_async) |async_token| { |
| | 1358 | const token_fn = p.eatToken(.Keyword_fn); |
| | 1359 | if (token_fn != null) { |
| | 1360 | // TODO: remove this hack when async fn rewriting is |
| | 1361 | // HACK: If we see the keyword `fn`, then we assume that |
| | 1362 | // we are parsing an async fn proto, and not a call. |
| | 1363 | // We therefore put back all tokens consumed by the async |
| | 1364 | // prefix... |
| | 1365 | p.putBackToken(token_fn.?); |
| | 1366 | p.putBackToken(async_token); |
| | 1367 | return p.parsePrimaryTypeExpr(); |
| | 1368 | } |
| | 1369 | var res = try p.expectNode(parsePrimaryTypeExpr, .{ |
| | 1370 | .ExpectedPrimaryTypeExpr = .{ .token = p.tok_i }, |
| | 1371 | }); |
| | 1372 | |
| | 1373 | while (try p.parseSuffixOp()) |node| { |
| 1393 | switch (node.id) { | 1374 | switch (node.id) { |
| 1394 | .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{ .node = res }, | 1375 | .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{ .node = res }, |
| 1395 | .InfixOp => node.cast(Node.InfixOp).?.lhs = res, | 1376 | .InfixOp => node.cast(Node.InfixOp).?.lhs = res, |
| 1396 | else => unreachable, | 1377 | else => unreachable, |
| 1397 | } | 1378 | } |
| 1398 | res = node; | 1379 | res = node; |
| 1399 | continue; | | |
| 1400 | } | 1380 | } |
| 1401 | if (try parseFnCallArguments(arena, it, tree)) |params| { | 1381 | |
| 1402 | const call = try arena.create(Node.SuffixOp); | 1382 | const params = (try p.parseFnCallArguments()) orelse { |
| 1403 | call.* = .{ | 1383 | try p.errors.append(p.gpa, .{ |
| 1404 | .lhs = .{ .node = res }, | 1384 | .ExpectedParamList = .{ .token = p.tok_i }, |
| 1405 | .op = .{ | 1385 | }); |
| 1406 | .Call = .{ | 1386 | // ignore this, continue parsing |
| 1407 | .params = params.list, | 1387 | return res; |
| 1408 | .async_token = null, | 1388 | }; |
| 1409 | }, | 1389 | const node = try p.arena.allocator.create(Node.SuffixOp); |
| | 1390 | node.* = .{ |
| | 1391 | .lhs = .{ .node = res }, |
| | 1392 | .op = .{ |
| | 1393 | .Call = .{ |
| | 1394 | .params = params.list, |
| | 1395 | .async_token = async_token, |
| 1410 | }, | 1396 | }, |
| 1411 | .rtoken = params.rparen, | 1397 | }, |
| 1412 | }; | 1398 | .rtoken = params.rparen, |
| 1413 | res = &call.base; | 1399 | }; |
| 1414 | continue; | 1400 | return &node.base; |
| | 1401 | } |
| | 1402 | if (try p.parsePrimaryTypeExpr()) |expr| { |
| | 1403 | var res = expr; |
| | 1404 | |
| | 1405 | while (true) { |
| | 1406 | if (try p.parseSuffixOp()) |node| { |
| | 1407 | switch (node.id) { |
| | 1408 | .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{ .node = res }, |
| | 1409 | .InfixOp => node.cast(Node.InfixOp).?.lhs = res, |
| | 1410 | else => unreachable, |
| | 1411 | } |
| | 1412 | res = node; |
| | 1413 | continue; |
| | 1414 | } |
| | 1415 | if (try p.parseFnCallArguments()) |params| { |
| | 1416 | const call = try p.arena.allocator.create(Node.SuffixOp); |
| | 1417 | call.* = .{ |
| | 1418 | .lhs = .{ .node = res }, |
| | 1419 | .op = .{ |
| | 1420 | .Call = .{ |
| | 1421 | .params = params.list, |
| | 1422 | .async_token = null, |
| | 1423 | }, |
| | 1424 | }, |
| | 1425 | .rtoken = params.rparen, |
| | 1426 | }; |
| | 1427 | res = &call.base; |
| | 1428 | continue; |
| | 1429 | } |
| | 1430 | break; |
| 1415 | } | 1431 | } |
| 1416 | break; | 1432 | return res; |
| 1417 | } | 1433 | } |
| 1418 | return res; | 1434 | |
| | 1435 | return null; |
| 1419 | } | 1436 | } |
| 1420 | | 1437 | |
| 1421 | return null; | 1438 | /// PrimaryTypeExpr |
| 1422 | } | 1439 | /// <- BUILTINIDENTIFIER FnCallArguments |
| | 1440 | /// / CHAR_LITERAL |
| | 1441 | /// / ContainerDecl |
| | 1442 | /// / DOT IDENTIFIER |
| | 1443 | /// / ErrorSetDecl |
| | 1444 | /// / FLOAT |
| | 1445 | /// / FnProto |
| | 1446 | /// / GroupedExpr |
| | 1447 | /// / LabeledTypeExpr |
| | 1448 | /// / IDENTIFIER |
| | 1449 | /// / IfTypeExpr |
| | 1450 | /// / INTEGER |
| | 1451 | /// / KEYWORD_comptime TypeExpr |
| | 1452 | /// / KEYWORD_error DOT IDENTIFIER |
| | 1453 | /// / KEYWORD_false |
| | 1454 | /// / KEYWORD_null |
| | 1455 | /// / KEYWORD_anyframe |
| | 1456 | /// / KEYWORD_true |
| | 1457 | /// / KEYWORD_undefined |
| | 1458 | /// / KEYWORD_unreachable |
| | 1459 | /// / STRINGLITERAL |
| | 1460 | /// / SwitchExpr |
| | 1461 | fn parsePrimaryTypeExpr(p: *Parser) !?*Node { |
| | 1462 | if (try p.parseBuiltinCall()) |node| return node; |
| | 1463 | if (p.eatToken(.CharLiteral)) |token| { |
| | 1464 | const node = try p.arena.allocator.create(Node.CharLiteral); |
| | 1465 | node.* = .{ |
| | 1466 | .token = token, |
| | 1467 | }; |
| | 1468 | return &node.base; |
| | 1469 | } |
| | 1470 | if (try p.parseContainerDecl()) |node| return node; |
| | 1471 | if (try p.parseAnonLiteral()) |node| return node; |
| | 1472 | if (try p.parseErrorSetDecl()) |node| return node; |
| | 1473 | if (try p.parseFloatLiteral()) |node| return node; |
| | 1474 | if (try p.parseFnProto()) |node| return node; |
| | 1475 | if (try p.parseGroupedExpr()) |node| return node; |
| | 1476 | if (try p.parseLabeledTypeExpr()) |node| return node; |
| | 1477 | if (try p.parseIdentifier()) |node| return node; |
| | 1478 | if (try p.parseIfTypeExpr()) |node| return node; |
| | 1479 | if (try p.parseIntegerLiteral()) |node| return node; |
| | 1480 | if (p.eatToken(.Keyword_comptime)) |token| { |
| | 1481 | const expr = (try p.parseTypeExpr()) orelse return null; |
| | 1482 | const node = try p.arena.allocator.create(Node.Comptime); |
| | 1483 | node.* = .{ |
| | 1484 | .doc_comments = null, |
| | 1485 | .comptime_token = token, |
| | 1486 | .expr = expr, |
| | 1487 | }; |
| | 1488 | return &node.base; |
| | 1489 | } |
| | 1490 | if (p.eatToken(.Keyword_error)) |token| { |
| | 1491 | const period = try p.expectTokenRecoverable(.Period); |
| | 1492 | const identifier = try p.expectNodeRecoverable(parseIdentifier, .{ |
| | 1493 | .ExpectedIdentifier = .{ .token = p.tok_i }, |
| | 1494 | }); |
| | 1495 | const global_error_set = try p.createLiteral(Node.ErrorType, token); |
| | 1496 | if (period == null or identifier == null) return global_error_set; |
| 1423 | | 1497 | |
| 1424 | /// PrimaryTypeExpr | 1498 | const node = try p.arena.allocator.create(Node.InfixOp); |
| 1425 | /// <- BUILTINIDENTIFIER FnCallArguments | 1499 | node.* = .{ |
| 1426 | /// / CHAR_LITERAL | 1500 | .op_token = period.?, |
| 1427 | /// / ContainerDecl | 1501 | .lhs = global_error_set, |
| 1428 | /// / DOT IDENTIFIER | 1502 | .op = .Period, |
| 1429 | /// / ErrorSetDecl | 1503 | .rhs = identifier.?, |
| 1430 | /// / FLOAT | 1504 | }; |
| 1431 | /// / FnProto | 1505 | return &node.base; |
| 1432 | /// / GroupedExpr | 1506 | } |
| 1433 | /// / LabeledTypeExpr | 1507 | if (p.eatToken(.Keyword_false)) |token| return p.createLiteral(Node.BoolLiteral, token); |
| 1434 | /// / IDENTIFIER | 1508 | if (p.eatToken(.Keyword_null)) |token| return p.createLiteral(Node.NullLiteral, token); |
| 1435 | /// / IfTypeExpr | 1509 | if (p.eatToken(.Keyword_anyframe)) |token| { |
| 1436 | /// / INTEGER | 1510 | const node = try p.arena.allocator.create(Node.AnyFrameType); |
| 1437 | /// / KEYWORD_comptime TypeExpr | 1511 | node.* = .{ |
| 1438 | /// / KEYWORD_error DOT IDENTIFIER | 1512 | .anyframe_token = token, |
| 1439 | /// / KEYWORD_false | 1513 | .result = null, |
| 1440 | /// / KEYWORD_null | 1514 | }; |
| 1441 | /// / KEYWORD_anyframe | 1515 | return &node.base; |
| 1442 | /// / KEYWORD_true | 1516 | } |
| 1443 | /// / KEYWORD_undefined | 1517 | if (p.eatToken(.Keyword_true)) |token| return p.createLiteral(Node.BoolLiteral, token); |
| 1444 | /// / KEYWORD_unreachable | 1518 | if (p.eatToken(.Keyword_undefined)) |token| return p.createLiteral(Node.UndefinedLiteral, token); |
| 1445 | /// / STRINGLITERAL | 1519 | if (p.eatToken(.Keyword_unreachable)) |token| return p.createLiteral(Node.Unreachable, token); |
| 1446 | /// / SwitchExpr | 1520 | if (try p.parseStringLiteral()) |node| return node; |
| 1447 | fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1521 | if (try p.parseSwitchExpr()) |node| return node; |
| 1448 | if (try parseBuiltinCall(arena, it, tree)) |node| return node; | 1522 | |
| 1449 | if (eatToken(it, .CharLiteral)) |token| { | 1523 | return null; |
| 1450 | const node = try arena.create(Node.CharLiteral); | 1524 | } |
| 1451 | node.* = .{ | 1525 | |
| 1452 | .token = token, | 1526 | /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto |
| | 1527 | fn parseContainerDecl(p: *Parser) !?*Node { |
| | 1528 | const layout_token = p.eatToken(.Keyword_extern) orelse |
| | 1529 | p.eatToken(.Keyword_packed); |
| | 1530 | |
| | 1531 | const node = (try p.parseContainerDeclAuto()) orelse { |
| | 1532 | if (layout_token) |token| |
| | 1533 | p.putBackToken(token); |
| | 1534 | return null; |
| 1453 | }; | 1535 | }; |
| 1454 | return &node.base; | 1536 | node.cast(Node.ContainerDecl).?.*.layout_token = layout_token; |
| | 1537 | return node; |
| 1455 | } | 1538 | } |
| 1456 | if (try parseContainerDecl(arena, it, tree)) |node| return node; | 1539 | |
| 1457 | if (try parseAnonLiteral(arena, it, tree)) |node| return node; | 1540 | /// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE |
| 1458 | if (try parseErrorSetDecl(arena, it, tree)) |node| return node; | 1541 | fn parseErrorSetDecl(p: *Parser) !?*Node { |
| 1459 | if (try parseFloatLiteral(arena, it, tree)) |node| return node; | 1542 | const error_token = p.eatToken(.Keyword_error) orelse return null; |
| 1460 | if (try parseFnProto(arena, it, tree)) |node| return node; | 1543 | if (p.eatToken(.LBrace) == null) { |
| 1461 | if (try parseGroupedExpr(arena, it, tree)) |node| return node; | 1544 | // Might parse as `KEYWORD_error DOT IDENTIFIER` later in PrimaryTypeExpr, so don't error |
| 1462 | if (try parseLabeledTypeExpr(arena, it, tree)) |node| return node; | 1545 | p.putBackToken(error_token); |
| 1463 | if (try parseIdentifier(arena, it, tree)) |node| return node; | 1546 | return null; |
| 1464 | if (try parseIfTypeExpr(arena, it, tree)) |node| return node; | 1547 | } |
| 1465 | if (try parseIntegerLiteral(arena, it, tree)) |node| return node; | 1548 | const decls = try p.parseErrorTagList(); |
| 1466 | if (eatToken(it, .Keyword_comptime)) |token| { | 1549 | const rbrace = try p.expectToken(.RBrace); |
| 1467 | const expr = (try parseTypeExpr(arena, it, tree)) orelse return null; | 1550 | |
| 1468 | const node = try arena.create(Node.Comptime); | 1551 | const node = try p.arena.allocator.create(Node.ErrorSetDecl); |
| 1469 | node.* = .{ | 1552 | node.* = .{ |
| 1470 | .doc_comments = null, | 1553 | .error_token = error_token, |
| 1471 | .comptime_token = token, | 1554 | .decls = decls, |
| 1472 | .expr = expr, | 1555 | .rbrace_token = rbrace, |
| 1473 | }; | 1556 | }; |
| 1474 | return &node.base; | 1557 | return &node.base; |
| 1475 | } | 1558 | } |
| 1476 | if (eatToken(it, .Keyword_error)) |token| { | 1559 | |
| 1477 | const period = try expectTokenRecoverable(it, tree, .Period); | 1560 | /// GroupedExpr <- LPAREN Expr RPAREN |
| 1478 | const identifier = try expectNodeRecoverable(arena, it, tree, parseIdentifier, .{ | 1561 | fn parseGroupedExpr(p: *Parser) !?*Node { |
| 1479 | .ExpectedIdentifier = .{ .token = it.index }, | 1562 | const lparen = p.eatToken(.LParen) orelse return null; |
| | 1563 | const expr = try p.expectNode(parseExpr, .{ |
| | 1564 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 1480 | }); | 1565 | }); |
| 1481 | const global_error_set = try createLiteral(arena, Node.ErrorType, token); | 1566 | const rparen = try p.expectToken(.RParen); |
| 1482 | if (period == null or identifier == null) return global_error_set; | | |
| 1483 | | 1567 | |
| 1484 | const node = try arena.create(Node.InfixOp); | 1568 | const node = try p.arena.allocator.create(Node.GroupedExpression); |
| 1485 | node.* = .{ | 1569 | node.* = .{ |
| 1486 | .op_token = period.?, | 1570 | .lparen = lparen, |
| 1487 | .lhs = global_error_set, | 1571 | .expr = expr, |
| 1488 | .op = .Period, | 1572 | .rparen = rparen, |
| 1489 | .rhs = identifier.?, | | |
| 1490 | }; | 1573 | }; |
| 1491 | return &node.base; | 1574 | return &node.base; |
| 1492 | } | 1575 | } |
| 1493 | if (eatToken(it, .Keyword_false)) |token| return createLiteral(arena, Node.BoolLiteral, token); | 1576 | |
| 1494 | if (eatToken(it, .Keyword_null)) |token| return createLiteral(arena, Node.NullLiteral, token); | 1577 | /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? |
| 1495 | if (eatToken(it, .Keyword_anyframe)) |token| { | 1578 | fn parseIfTypeExpr(p: *Parser) !?*Node { |
| 1496 | const node = try arena.create(Node.AnyFrameType); | 1579 | return p.parseIf(parseTypeExpr); |
| 1497 | node.* = .{ | | |
| 1498 | .anyframe_token = token, | | |
| 1499 | .result = null, | | |
| 1500 | }; | | |
| 1501 | return &node.base; | | |
| 1502 | } | 1580 | } |
| 1503 | if (eatToken(it, .Keyword_true)) |token| return createLiteral(arena, Node.BoolLiteral, token); | | |
| 1504 | if (eatToken(it, .Keyword_undefined)) |token| return createLiteral(arena, Node.UndefinedLiteral, token); | | |
| 1505 | if (eatToken(it, .Keyword_unreachable)) |token| return createLiteral(arena, Node.Unreachable, token); | | |
| 1506 | if (try parseStringLiteral(arena, it, tree)) |node| return node; | | |
| 1507 | if (try parseSwitchExpr(arena, it, tree)) |node| return node; | | |
| 1508 | | 1581 | |
| 1509 | return null; | 1582 | /// LabeledTypeExpr |
| 1510 | } | 1583 | /// <- BlockLabel Block |
| | 1584 | /// / BlockLabel? LoopTypeExpr |
| | 1585 | fn parseLabeledTypeExpr(p: *Parser) !?*Node { |
| | 1586 | var colon: TokenIndex = undefined; |
| | 1587 | const label = p.parseBlockLabel(&colon); |
| 1511 | | 1588 | |
| 1512 | /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto | 1589 | if (label) |token| { |
| 1513 | fn parseContainerDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1590 | if (try p.parseBlock()) |node| { |
| 1514 | const layout_token = eatToken(it, .Keyword_extern) orelse | 1591 | node.cast(Node.Block).?.label = token; |
| 1515 | eatToken(it, .Keyword_packed); | 1592 | return node; |
| | 1593 | } |
| | 1594 | } |
| 1516 | | 1595 | |
| 1517 | const node = (try parseContainerDeclAuto(arena, it, tree)) orelse { | 1596 | if (try p.parseLoopTypeExpr()) |node| { |
| 1518 | if (layout_token) |token| | 1597 | switch (node.id) { |
| 1519 | putBackToken(it, token); | 1598 | .For => node.cast(Node.For).?.label = label, |
| 1520 | return null; | 1599 | .While => node.cast(Node.While).?.label = label, |
| 1521 | }; | 1600 | else => unreachable, |
| 1522 | node.cast(Node.ContainerDecl).?.*.layout_token = layout_token; | 1601 | } |
| 1523 | return node; | 1602 | return node; |
| 1524 | } | 1603 | } |
| 1525 | | 1604 | |
| 1526 | /// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE | 1605 | if (label) |token| { |
| 1527 | fn parseErrorSetDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1606 | p.putBackToken(colon); |
| 1528 | const error_token = eatToken(it, .Keyword_error) orelse return null; | 1607 | p.putBackToken(token); |
| 1529 | if (eatToken(it, .LBrace) == null) { | 1608 | } |
| 1530 | // Might parse as `KEYWORD_error DOT IDENTIFIER` later in PrimaryTypeExpr, so don't error | | |
| 1531 | putBackToken(it, error_token); | | |
| 1532 | return null; | 1609 | return null; |
| 1533 | } | 1610 | } |
| 1534 | const decls = try parseErrorTagList(arena, it, tree); | | |
| 1535 | const rbrace = try expectToken(it, tree, .RBrace); | | |
| 1536 | | | |
| 1537 | const node = try arena.create(Node.ErrorSetDecl); | | |
| 1538 | node.* = .{ | | |
| 1539 | .error_token = error_token, | | |
| 1540 | .decls = decls, | | |
| 1541 | .rbrace_token = rbrace, | | |
| 1542 | }; | | |
| 1543 | return &node.base; | | |
| 1544 | } | | |
| 1545 | | | |
| 1546 | /// GroupedExpr <- LPAREN Expr RPAREN | | |
| 1547 | fn parseGroupedExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 1548 | const lparen = eatToken(it, .LParen) orelse return null; | | |
| 1549 | const expr = try expectNode(arena, it, tree, parseExpr, .{ | | |
| 1550 | .ExpectedExpr = .{ .token = it.index }, | | |
| 1551 | }); | | |
| 1552 | const rparen = try expectToken(it, tree, .RParen); | | |
| 1553 | | | |
| 1554 | const node = try arena.create(Node.GroupedExpression); | | |
| 1555 | node.* = .{ | | |
| 1556 | .lparen = lparen, | | |
| 1557 | .expr = expr, | | |
| 1558 | .rparen = rparen, | | |
| 1559 | }; | | |
| 1560 | return &node.base; | | |
| 1561 | } | | |
| 1562 | | | |
| 1563 | /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? | | |
| 1564 | fn parseIfTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 1565 | return parseIf(arena, it, tree, parseTypeExpr); | | |
| 1566 | } | | |
| 1567 | | 1611 | |
| 1568 | /// LabeledTypeExpr | 1612 | /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) |
| 1569 | /// <- BlockLabel Block | 1613 | fn parseLoopTypeExpr(p: *Parser) !?*Node { |
| 1570 | /// / BlockLabel? LoopTypeExpr | 1614 | const inline_token = p.eatToken(.Keyword_inline); |
| 1571 | fn parseLabeledTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 1572 | var colon: TokenIndex = undefined; | | |
| 1573 | const label = parseBlockLabel(arena, it, tree, &colon); | | |
| 1574 | | 1615 | |
| 1575 | if (label) |token| { | 1616 | if (try p.parseForTypeExpr()) |node| { |
| 1576 | if (try parseBlock(arena, it, tree)) |node| { | 1617 | node.cast(Node.For).?.inline_token = inline_token; |
| 1577 | node.cast(Node.Block).?.label = token; | | |
| 1578 | return node; | 1618 | return node; |
| 1579 | } | 1619 | } |
| 1580 | } | | |
| 1581 | | 1620 | |
| 1582 | if (try parseLoopTypeExpr(arena, it, tree)) |node| { | 1621 | if (try p.parseWhileTypeExpr()) |node| { |
| 1583 | switch (node.id) { | 1622 | node.cast(Node.While).?.inline_token = inline_token; |
| 1584 | .For => node.cast(Node.For).?.label = label, | 1623 | return node; |
| 1585 | .While => node.cast(Node.While).?.label = label, | | |
| 1586 | else => unreachable, | | |
| 1587 | } | 1624 | } |
| 1588 | return node; | | |
| 1589 | } | | |
| 1590 | | 1625 | |
| 1591 | if (label) |token| { | 1626 | if (inline_token == null) return null; |
| 1592 | putBackToken(it, colon); | 1627 | |
| 1593 | putBackToken(it, token); | 1628 | // If we've seen "inline", there should have been a "for" or "while" |
| | 1629 | try p.errors.append(p.gpa, .{ |
| | 1630 | .ExpectedInlinable = .{ .token = p.tok_i }, |
| | 1631 | }); |
| | 1632 | return error.ParseError; |
| 1594 | } | 1633 | } |
| 1595 | return null; | | |
| 1596 | } | | |
| 1597 | | 1634 | |
| 1598 | /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) | 1635 | /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? |
| 1599 | fn parseLoopTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1636 | fn parseForTypeExpr(p: *Parser) !?*Node { |
| 1600 | const inline_token = eatToken(it, .Keyword_inline); | 1637 | const node = (try p.parseForPrefix()) orelse return null; |
| | 1638 | const for_prefix = node.cast(Node.For).?; |
| 1601 | | 1639 | |
| 1602 | if (try parseForTypeExpr(arena, it, tree)) |node| { | 1640 | const type_expr = try p.expectNode(parseTypeExpr, .{ |
| 1603 | node.cast(Node.For).?.inline_token = inline_token; | 1641 | .ExpectedTypeExpr = .{ .token = p.tok_i }, |
| 1604 | return node; | 1642 | }); |
| 1605 | } | 1643 | for_prefix.body = type_expr; |
| 1606 | | 1644 | |
| 1607 | if (try parseWhileTypeExpr(arena, it, tree)) |node| { | 1645 | if (p.eatToken(.Keyword_else)) |else_token| { |
| 1608 | node.cast(Node.While).?.inline_token = inline_token; | 1646 | const else_expr = try p.expectNode(parseTypeExpr, .{ |
| 1609 | return node; | 1647 | .ExpectedTypeExpr = .{ .token = p.tok_i }, |
| 1610 | } | 1648 | }); |
| 1611 | | 1649 | |
| 1612 | if (inline_token == null) return null; | 1650 | const else_node = try p.arena.allocator.create(Node.Else); |
| | 1651 | else_node.* = .{ |
| | 1652 | .else_token = else_token, |
| | 1653 | .payload = null, |
| | 1654 | .body = else_expr, |
| | 1655 | }; |
| 1613 | | 1656 | |
| 1614 | // If we've seen "inline", there should have been a "for" or "while" | 1657 | for_prefix.@"else" = else_node; |
| 1615 | try tree.errors.push(.{ | 1658 | } |
| 1616 | .ExpectedInlinable = .{ .token = it.index }, | | |
| 1617 | }); | | |
| 1618 | return error.ParseError; | | |
| 1619 | } | | |
| 1620 | | 1659 | |
| 1621 | /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? | 1660 | return node; |
| 1622 | fn parseForTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1661 | } |
| 1623 | const node = (try parseForPrefix(arena, it, tree)) orelse return null; | | |
| 1624 | const for_prefix = node.cast(Node.For).?; | | |
| 1625 | | 1662 | |
| 1626 | const type_expr = try expectNode(arena, it, tree, parseTypeExpr, .{ | 1663 | /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? |
| 1627 | .ExpectedTypeExpr = .{ .token = it.index }, | 1664 | fn parseWhileTypeExpr(p: *Parser) !?*Node { |
| 1628 | }); | 1665 | const node = (try p.parseWhilePrefix()) orelse return null; |
| 1629 | for_prefix.body = type_expr; | 1666 | const while_prefix = node.cast(Node.While).?; |
| 1630 | | 1667 | |
| 1631 | if (eatToken(it, .Keyword_else)) |else_token| { | 1668 | const type_expr = try p.expectNode(parseTypeExpr, .{ |
| 1632 | const else_expr = try expectNode(arena, it, tree, parseTypeExpr, .{ | 1669 | .ExpectedTypeExpr = .{ .token = p.tok_i }, |
| 1633 | .ExpectedTypeExpr = .{ .token = it.index }, | | |
| 1634 | }); | 1670 | }); |
| | 1671 | while_prefix.body = type_expr; |
| 1635 | | 1672 | |
| 1636 | const else_node = try arena.create(Node.Else); | 1673 | if (p.eatToken(.Keyword_else)) |else_token| { |
| 1637 | else_node.* = .{ | 1674 | const payload = try p.parsePayload(); |
| 1638 | .else_token = else_token, | | |
| 1639 | .payload = null, | | |
| 1640 | .body = else_expr, | | |
| 1641 | }; | | |
| 1642 | | 1675 | |
| 1643 | for_prefix.@"else" = else_node; | 1676 | const else_expr = try p.expectNode(parseTypeExpr, .{ |
| 1644 | } | 1677 | .ExpectedTypeExpr = .{ .token = p.tok_i }, |
| | 1678 | }); |
| 1645 | | 1679 | |
| 1646 | return node; | 1680 | const else_node = try p.arena.allocator.create(Node.Else); |
| 1647 | } | 1681 | else_node.* = .{ |
| | 1682 | .else_token = else_token, |
| | 1683 | .payload = null, |
| | 1684 | .body = else_expr, |
| | 1685 | }; |
| | 1686 | |
| | 1687 | while_prefix.@"else" = else_node; |
| | 1688 | } |
| 1648 | | 1689 | |
| 1649 | /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? | 1690 | return node; |
| 1650 | fn parseWhileTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1691 | } |
| 1651 | const node = (try parseWhilePrefix(arena, it, tree)) orelse return null; | | |
| 1652 | const while_prefix = node.cast(Node.While).?; | | |
| 1653 | | 1692 | |
| 1654 | const type_expr = try expectNode(arena, it, tree, parseTypeExpr, .{ | 1693 | /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE |
| 1655 | .ExpectedTypeExpr = .{ .token = it.index }, | 1694 | fn parseSwitchExpr(p: *Parser) !?*Node { |
| 1656 | }); | 1695 | const switch_token = p.eatToken(.Keyword_switch) orelse return null; |
| 1657 | while_prefix.body = type_expr; | 1696 | _ = try p.expectToken(.LParen); |
| | 1697 | const expr_node = try p.expectNode(parseExpr, .{ |
| | 1698 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 1699 | }); |
| | 1700 | _ = try p.expectToken(.RParen); |
| | 1701 | _ = try p.expectToken(.LBrace); |
| | 1702 | const cases = try p.parseSwitchProngList(); |
| | 1703 | const rbrace = try p.expectToken(.RBrace); |
| 1658 | | 1704 | |
| 1659 | if (eatToken(it, .Keyword_else)) |else_token| { | 1705 | const node = try p.arena.allocator.create(Node.Switch); |
| 1660 | const payload = try parsePayload(arena, it, tree); | 1706 | node.* = .{ |
| | 1707 | .switch_token = switch_token, |
| | 1708 | .expr = expr_node, |
| | 1709 | .cases = cases, |
| | 1710 | .rbrace = rbrace, |
| | 1711 | }; |
| | 1712 | return &node.base; |
| | 1713 | } |
| 1661 | | 1714 | |
| 1662 | const else_expr = try expectNode(arena, it, tree, parseTypeExpr, .{ | 1715 | /// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN |
| 1663 | .ExpectedTypeExpr = .{ .token = it.index }, | 1716 | fn parseAsmExpr(p: *Parser) !?*Node { |
| | 1717 | const asm_token = p.eatToken(.Keyword_asm) orelse return null; |
| | 1718 | const volatile_token = p.eatToken(.Keyword_volatile); |
| | 1719 | _ = try p.expectToken(.LParen); |
| | 1720 | const template = try p.expectNode(parseExpr, .{ |
| | 1721 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 1664 | }); | 1722 | }); |
| 1665 | | 1723 | |
| 1666 | const else_node = try arena.create(Node.Else); | 1724 | const node = try p.arena.allocator.create(Node.Asm); |
| 1667 | else_node.* = .{ | 1725 | node.* = .{ |
| 1668 | .else_token = else_token, | 1726 | .asm_token = asm_token, |
| 1669 | .payload = null, | 1727 | .volatile_token = volatile_token, |
| 1670 | .body = else_expr, | 1728 | .template = template, |
| | 1729 | .outputs = Node.Asm.OutputList{}, |
| | 1730 | .inputs = Node.Asm.InputList{}, |
| | 1731 | .clobbers = Node.Asm.ClobberList{}, |
| | 1732 | .rparen = undefined, |
| 1671 | }; | 1733 | }; |
| 1672 | | 1734 | |
| 1673 | while_prefix.@"else" = else_node; | 1735 | try p.parseAsmOutput(node); |
| | 1736 | node.rparen = try p.expectToken(.RParen); |
| | 1737 | return &node.base; |
| 1674 | } | 1738 | } |
| 1675 | | 1739 | |
| 1676 | return node; | 1740 | /// DOT IDENTIFIER |
| 1677 | } | 1741 | fn parseAnonLiteral(p: *Parser) !?*Node { |
| | 1742 | const dot = p.eatToken(.Period) orelse return null; |
| 1678 | | 1743 | |
| 1679 | /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE | 1744 | // anon enum literal |
| 1680 | fn parseSwitchExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1745 | if (p.eatToken(.Identifier)) |name| { |
| 1681 | const switch_token = eatToken(it, .Keyword_switch) orelse return null; | 1746 | const node = try p.arena.allocator.create(Node.EnumLiteral); |
| 1682 | _ = try expectToken(it, tree, .LParen); | 1747 | node.* = .{ |
| 1683 | const expr_node = try expectNode(arena, it, tree, parseExpr, .{ | 1748 | .dot = dot, |
| 1684 | .ExpectedExpr = .{ .token = it.index }, | 1749 | .name = name, |
| 1685 | }); | 1750 | }; |
| 1686 | _ = try expectToken(it, tree, .RParen); | 1751 | return &node.base; |
| 1687 | _ = try expectToken(it, tree, .LBrace); | 1752 | } |
| 1688 | const cases = try parseSwitchProngList(arena, it, tree); | | |
| 1689 | const rbrace = try expectToken(it, tree, .RBrace); | | |
| 1690 | | | |
| 1691 | const node = try arena.create(Node.Switch); | | |
| 1692 | node.* = .{ | | |
| 1693 | .switch_token = switch_token, | | |
| 1694 | .expr = expr_node, | | |
| 1695 | .cases = cases, | | |
| 1696 | .rbrace = rbrace, | | |
| 1697 | }; | | |
| 1698 | return &node.base; | | |
| 1699 | } | | |
| 1700 | | 1753 | |
| 1701 | /// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN | 1754 | // anon container literal |
| 1702 | fn parseAsmExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1755 | if (try p.parseInitList()) |node| { |
| 1703 | const asm_token = eatToken(it, .Keyword_asm) orelse return null; | 1756 | node.lhs = .{ .dot = dot }; |
| 1704 | const volatile_token = eatToken(it, .Keyword_volatile); | 1757 | return &node.base; |
| 1705 | _ = try expectToken(it, tree, .LParen); | 1758 | } |
| 1706 | const template = try expectNode(arena, it, tree, parseExpr, .{ | | |
| 1707 | .ExpectedExpr = .{ .token = it.index }, | | |
| 1708 | }); | | |
| 1709 | | | |
| 1710 | const node = try arena.create(Node.Asm); | | |
| 1711 | node.* = .{ | | |
| 1712 | .asm_token = asm_token, | | |
| 1713 | .volatile_token = volatile_token, | | |
| 1714 | .template = template, | | |
| 1715 | .outputs = Node.Asm.OutputList.init(arena), | | |
| 1716 | .inputs = Node.Asm.InputList.init(arena), | | |
| 1717 | .clobbers = Node.Asm.ClobberList.init(arena), | | |
| 1718 | .rparen = undefined, | | |
| 1719 | }; | | |
| 1720 | | 1759 | |
| 1721 | try parseAsmOutput(arena, it, tree, node); | 1760 | p.putBackToken(dot); |
| 1722 | node.rparen = try expectToken(it, tree, .RParen); | 1761 | return null; |
| 1723 | return &node.base; | 1762 | } |
| 1724 | } | 1763 | |
| | 1764 | /// AsmOutput <- COLON AsmOutputList AsmInput? |
| | 1765 | fn parseAsmOutput(p: *Parser, asm_node: *Node.Asm) !void { |
| | 1766 | if (p.eatToken(.Colon) == null) return; |
| | 1767 | asm_node.outputs = try p.parseAsmOutputList(); |
| | 1768 | try p.parseAsmInput(asm_node); |
| | 1769 | } |
| | 1770 | |
| | 1771 | /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN |
| | 1772 | fn parseAsmOutputItem(p: *Parser) !?*Node.AsmOutput { |
| | 1773 | const lbracket = p.eatToken(.LBracket) orelse return null; |
| | 1774 | const name = try p.expectNode(parseIdentifier, .{ |
| | 1775 | .ExpectedIdentifier = .{ .token = p.tok_i }, |
| | 1776 | }); |
| | 1777 | _ = try p.expectToken(.RBracket); |
| | 1778 | |
| | 1779 | const constraint = try p.expectNode(parseStringLiteral, .{ |
| | 1780 | .ExpectedStringLiteral = .{ .token = p.tok_i }, |
| | 1781 | }); |
| 1725 | | 1782 | |
| 1726 | /// DOT IDENTIFIER | 1783 | _ = try p.expectToken(.LParen); |
| 1727 | fn parseAnonLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1784 | const kind: Node.AsmOutput.Kind = blk: { |
| 1728 | const dot = eatToken(it, .Period) orelse return null; | 1785 | if (p.eatToken(.Arrow) != null) { |
| | 1786 | const return_ident = try p.expectNode(parseTypeExpr, .{ |
| | 1787 | .ExpectedTypeExpr = .{ .token = p.tok_i }, |
| | 1788 | }); |
| | 1789 | break :blk .{ .Return = return_ident }; |
| | 1790 | } |
| | 1791 | const variable = try p.expectNode(parseIdentifier, .{ |
| | 1792 | .ExpectedIdentifier = .{ .token = p.tok_i }, |
| | 1793 | }); |
| | 1794 | break :blk .{ .Variable = variable.cast(Node.Identifier).? }; |
| | 1795 | }; |
| | 1796 | const rparen = try p.expectToken(.RParen); |
| 1729 | | 1797 | |
| 1730 | // anon enum literal | 1798 | const node = try p.arena.allocator.create(Node.AsmOutput); |
| 1731 | if (eatToken(it, .Identifier)) |name| { | | |
| 1732 | const node = try arena.create(Node.EnumLiteral); | | |
| 1733 | node.* = .{ | 1799 | node.* = .{ |
| 1734 | .dot = dot, | 1800 | .lbracket = lbracket, |
| 1735 | .name = name, | 1801 | .symbolic_name = name, |
| | 1802 | .constraint = constraint, |
| | 1803 | .kind = kind, |
| | 1804 | .rparen = rparen, |
| 1736 | }; | 1805 | }; |
| 1737 | return &node.base; | 1806 | return node; |
| 1738 | } | 1807 | } |
| 1739 | | 1808 | |
| 1740 | // anon container literal | 1809 | /// AsmInput <- COLON AsmInputList AsmClobbers? |
| 1741 | if (try parseInitList(arena, it, tree)) |node| { | 1810 | fn parseAsmInput(p: *Parser, asm_node: *Node.Asm) !void { |
| 1742 | node.lhs = .{ .dot = dot }; | 1811 | if (p.eatToken(.Colon) == null) return; |
| 1743 | return &node.base; | 1812 | asm_node.inputs = try p.parseAsmInputList(); |
| | 1813 | try p.parseAsmClobbers(asm_node); |
| 1744 | } | 1814 | } |
| 1745 | | 1815 | |
| 1746 | putBackToken(it, dot); | 1816 | /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN |
| 1747 | return null; | 1817 | fn parseAsmInputItem(p: *Parser) !?*Node.AsmInput { |
| 1748 | } | 1818 | const lbracket = p.eatToken(.LBracket) orelse return null; |
| 1749 | | 1819 | const name = try p.expectNode(parseIdentifier, .{ |
| 1750 | /// AsmOutput <- COLON AsmOutputList AsmInput? | 1820 | .ExpectedIdentifier = .{ .token = p.tok_i }, |
| 1751 | fn parseAsmOutput(arena: *Allocator, it: *TokenIterator, tree: *Tree, asm_node: *Node.Asm) !void { | | |
| 1752 | if (eatToken(it, .Colon) == null) return; | | |
| 1753 | asm_node.outputs = try parseAsmOutputList(arena, it, tree); | | |
| 1754 | try parseAsmInput(arena, it, tree, asm_node); | | |
| 1755 | } | | |
| 1756 | | | |
| 1757 | /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN | | |
| 1758 | fn parseAsmOutputItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.AsmOutput { | | |
| 1759 | const lbracket = eatToken(it, .LBracket) orelse return null; | | |
| 1760 | const name = try expectNode(arena, it, tree, parseIdentifier, .{ | | |
| 1761 | .ExpectedIdentifier = .{ .token = it.index }, | | |
| 1762 | }); | | |
| 1763 | _ = try expectToken(it, tree, .RBracket); | | |
| 1764 | | | |
| 1765 | const constraint = try expectNode(arena, it, tree, parseStringLiteral, .{ | | |
| 1766 | .ExpectedStringLiteral = .{ .token = it.index }, | | |
| 1767 | }); | | |
| 1768 | | | |
| 1769 | _ = try expectToken(it, tree, .LParen); | | |
| 1770 | const kind: Node.AsmOutput.Kind = blk: { | | |
| 1771 | if (eatToken(it, .Arrow) != null) { | | |
| 1772 | const return_ident = try expectNode(arena, it, tree, parseTypeExpr, .{ | | |
| 1773 | .ExpectedTypeExpr = .{ .token = it.index }, | | |
| 1774 | }); | | |
| 1775 | break :blk .{ .Return = return_ident }; | | |
| 1776 | } | | |
| 1777 | const variable = try expectNode(arena, it, tree, parseIdentifier, .{ | | |
| 1778 | .ExpectedIdentifier = .{ .token = it.index }, | | |
| 1779 | }); | 1821 | }); |
| 1780 | break :blk .{ .Variable = variable.cast(Node.Identifier).? }; | 1822 | _ = try p.expectToken(.RBracket); |
| 1781 | }; | | |
| 1782 | const rparen = try expectToken(it, tree, .RParen); | | |
| 1783 | | | |
| 1784 | const node = try arena.create(Node.AsmOutput); | | |
| 1785 | node.* = .{ | | |
| 1786 | .lbracket = lbracket, | | |
| 1787 | .symbolic_name = name, | | |
| 1788 | .constraint = constraint, | | |
| 1789 | .kind = kind, | | |
| 1790 | .rparen = rparen, | | |
| 1791 | }; | | |
| 1792 | return node; | | |
| 1793 | } | | |
| 1794 | | 1823 | |
| 1795 | /// AsmInput <- COLON AsmInputList AsmClobbers? | 1824 | const constraint = try p.expectNode(parseStringLiteral, .{ |
| 1796 | fn parseAsmInput(arena: *Allocator, it: *TokenIterator, tree: *Tree, asm_node: *Node.Asm) !void { | 1825 | .ExpectedStringLiteral = .{ .token = p.tok_i }, |
| 1797 | if (eatToken(it, .Colon) == null) return; | 1826 | }); |
| 1798 | asm_node.inputs = try parseAsmInputList(arena, it, tree); | | |
| 1799 | try parseAsmClobbers(arena, it, tree, asm_node); | | |
| 1800 | } | | |
| 1801 | | 1827 | |
| 1802 | /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN | 1828 | _ = try p.expectToken(.LParen); |
| 1803 | fn parseAsmInputItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.AsmInput { | 1829 | const expr = try p.expectNode(parseExpr, .{ |
| 1804 | const lbracket = eatToken(it, .LBracket) orelse return null; | 1830 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 1805 | const name = try expectNode(arena, it, tree, parseIdentifier, .{ | 1831 | }); |
| 1806 | .ExpectedIdentifier = .{ .token = it.index }, | 1832 | const rparen = try p.expectToken(.RParen); |
| 1807 | }); | | |
| 1808 | _ = try expectToken(it, tree, .RBracket); | | |
| 1809 | | | |
| 1810 | const constraint = try expectNode(arena, it, tree, parseStringLiteral, .{ | | |
| 1811 | .ExpectedStringLiteral = .{ .token = it.index }, | | |
| 1812 | }); | | |
| 1813 | | | |
| 1814 | _ = try expectToken(it, tree, .LParen); | | |
| 1815 | const expr = try expectNode(arena, it, tree, parseExpr, .{ | | |
| 1816 | .ExpectedExpr = .{ .token = it.index }, | | |
| 1817 | }); | | |
| 1818 | const rparen = try expectToken(it, tree, .RParen); | | |
| 1819 | | | |
| 1820 | const node = try arena.create(Node.AsmInput); | | |
| 1821 | node.* = .{ | | |
| 1822 | .lbracket = lbracket, | | |
| 1823 | .symbolic_name = name, | | |
| 1824 | .constraint = constraint, | | |
| 1825 | .expr = expr, | | |
| 1826 | .rparen = rparen, | | |
| 1827 | }; | | |
| 1828 | return node; | | |
| 1829 | } | | |
| 1830 | | 1833 | |
| 1831 | /// AsmClobbers <- COLON StringList | 1834 | const node = try p.arena.allocator.create(Node.AsmInput); |
| 1832 | /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL? | 1835 | node.* = .{ |
| 1833 | fn parseAsmClobbers(arena: *Allocator, it: *TokenIterator, tree: *Tree, asm_node: *Node.Asm) !void { | 1836 | .lbracket = lbracket, |
| 1834 | if (eatToken(it, .Colon) == null) return; | 1837 | .symbolic_name = name, |
| 1835 | asm_node.clobbers = try ListParseFn( | 1838 | .constraint = constraint, |
| 1836 | Node.Asm.ClobberList, | 1839 | .expr = expr, |
| 1837 | parseStringLiteral, | 1840 | .rparen = rparen, |
| 1838 | )(arena, it, tree); | 1841 | }; |
| 1839 | } | 1842 | return node; |
| | 1843 | } |
| 1840 | | 1844 | |
| 1841 | /// BreakLabel <- COLON IDENTIFIER | 1845 | /// AsmClobbers <- COLON StringList |
| 1842 | fn parseBreakLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1846 | /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL? |
| 1843 | _ = eatToken(it, .Colon) orelse return null; | 1847 | fn parseAsmClobbers(p: *Parser, asm_node: *Node.Asm) !void { |
| 1844 | return try expectNode(arena, it, tree, parseIdentifier, .{ | 1848 | if (p.eatToken(.Colon) == null) return; |
| 1845 | .ExpectedIdentifier = .{ .token = it.index }, | 1849 | asm_node.clobbers = try ListParseFn( |
| 1846 | }); | 1850 | Node.Asm.ClobberList, |
| 1847 | } | 1851 | parseStringLiteral, |
| | 1852 | )(p); |
| | 1853 | } |
| 1848 | | 1854 | |
| 1849 | /// BlockLabel <- IDENTIFIER COLON | 1855 | /// BreakLabel <- COLON IDENTIFIER |
| 1850 | fn parseBlockLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree, colon_token: *TokenIndex) ?TokenIndex { | 1856 | fn parseBreakLabel(p: *Parser) !?*Node { |
| 1851 | const identifier = eatToken(it, .Identifier) orelse return null; | 1857 | _ = p.eatToken(.Colon) orelse return null; |
| 1852 | if (eatToken(it, .Colon)) |colon| { | 1858 | return p.expectNode(parseIdentifier, .{ |
| 1853 | colon_token.* = colon; | 1859 | .ExpectedIdentifier = .{ .token = p.tok_i }, |
| 1854 | return identifier; | 1860 | }); |
| 1855 | } | 1861 | } |
| 1856 | putBackToken(it, identifier); | | |
| 1857 | return null; | | |
| 1858 | } | | |
| 1859 | | 1862 | |
| 1860 | /// FieldInit <- DOT IDENTIFIER EQUAL Expr | 1863 | /// BlockLabel <- IDENTIFIER COLON |
| 1861 | fn parseFieldInit(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1864 | fn parseBlockLabel(p: *Parser, colon_token: *TokenIndex) ?TokenIndex { |
| 1862 | const period_token = eatToken(it, .Period) orelse return null; | 1865 | const identifier = p.eatToken(.Identifier) orelse return null; |
| 1863 | const name_token = eatToken(it, .Identifier) orelse { | 1866 | if (p.eatToken(.Colon)) |colon| { |
| 1864 | // Because of anon literals `.{` is also valid. | 1867 | colon_token.* = colon; |
| 1865 | putBackToken(it, period_token); | 1868 | return identifier; |
| 1866 | return null; | 1869 | } |
| 1867 | }; | 1870 | p.putBackToken(identifier); |
| 1868 | const eq_token = eatToken(it, .Equal) orelse { | | |
| 1869 | // `.Name` may also be an enum literal, which is a later rule. | | |
| 1870 | putBackToken(it, name_token); | | |
| 1871 | putBackToken(it, period_token); | | |
| 1872 | return null; | 1871 | return null; |
| 1873 | }; | 1872 | } |
| 1874 | const expr_node = try expectNode(arena, it, tree, parseExpr, .{ | | |
| 1875 | .ExpectedExpr = .{ .token = it.index }, | | |
| 1876 | }); | | |
| 1877 | | | |
| 1878 | const node = try arena.create(Node.FieldInitializer); | | |
| 1879 | node.* = .{ | | |
| 1880 | .period_token = period_token, | | |
| 1881 | .name_token = name_token, | | |
| 1882 | .expr = expr_node, | | |
| 1883 | }; | | |
| 1884 | return &node.base; | | |
| 1885 | } | | |
| 1886 | | | |
| 1887 | /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN | | |
| 1888 | fn parseWhileContinueExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 1889 | _ = eatToken(it, .Colon) orelse return null; | | |
| 1890 | _ = try expectToken(it, tree, .LParen); | | |
| 1891 | const node = try expectNode(arena, it, tree, parseAssignExpr, .{ | | |
| 1892 | .ExpectedExprOrAssignment = .{ .token = it.index }, | | |
| 1893 | }); | | |
| 1894 | _ = try expectToken(it, tree, .RParen); | | |
| 1895 | return node; | | |
| 1896 | } | | |
| 1897 | | 1873 | |
| 1898 | /// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN | 1874 | /// FieldInit <- DOT IDENTIFIER EQUAL Expr |
| 1899 | fn parseLinkSection(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1875 | fn parseFieldInit(p: *Parser) !?*Node { |
| 1900 | _ = eatToken(it, .Keyword_linksection) orelse return null; | 1876 | const period_token = p.eatToken(.Period) orelse return null; |
| 1901 | _ = try expectToken(it, tree, .LParen); | 1877 | const name_token = p.eatToken(.Identifier) orelse { |
| 1902 | const expr_node = try expectNode(arena, it, tree, parseExpr, .{ | 1878 | // Because of anon literals `.{` is also valid. |
| 1903 | .ExpectedExpr = .{ .token = it.index }, | 1879 | p.putBackToken(period_token); |
| 1904 | }); | 1880 | return null; |
| 1905 | _ = try expectToken(it, tree, .RParen); | 1881 | }; |
| 1906 | return expr_node; | 1882 | const eq_token = p.eatToken(.Equal) orelse { |
| 1907 | } | 1883 | // `.Name` may also be an enum literal, which is a later rule. |
| | 1884 | p.putBackToken(name_token); |
| | 1885 | p.putBackToken(period_token); |
| | 1886 | return null; |
| | 1887 | }; |
| | 1888 | const expr_node = try p.expectNode(parseExpr, .{ |
| | 1889 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 1890 | }); |
| 1908 | | 1891 | |
| 1909 | /// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN | 1892 | const node = try p.arena.allocator.create(Node.FieldInitializer); |
| 1910 | fn parseCallconv(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1893 | node.* = .{ |
| 1911 | _ = eatToken(it, .Keyword_callconv) orelse return null; | 1894 | .period_token = period_token, |
| 1912 | _ = try expectToken(it, tree, .LParen); | 1895 | .name_token = name_token, |
| 1913 | const expr_node = try expectNode(arena, it, tree, parseExpr, .{ | 1896 | .expr = expr_node, |
| 1914 | .ExpectedExpr = .{ .token = it.index }, | 1897 | }; |
| 1915 | }); | 1898 | return &node.base; |
| 1916 | _ = try expectToken(it, tree, .RParen); | 1899 | } |
| 1917 | return expr_node; | | |
| 1918 | } | | |
| 1919 | | 1900 | |
| 1920 | /// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType | 1901 | /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN |
| 1921 | fn parseParamDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1902 | fn parseWhileContinueExpr(p: *Parser) !?*Node { |
| 1922 | const doc_comments = try parseDocComment(arena, it, tree); | 1903 | _ = p.eatToken(.Colon) orelse return null; |
| 1923 | const noalias_token = eatToken(it, .Keyword_noalias); | 1904 | _ = try p.expectToken(.LParen); |
| 1924 | const comptime_token = if (noalias_token == null) eatToken(it, .Keyword_comptime) else null; | 1905 | const node = try p.expectNode(parseAssignExpr, .{ |
| 1925 | const name_token = blk: { | 1906 | .ExpectedExprOrAssignment = .{ .token = p.tok_i }, |
| 1926 | const identifier = eatToken(it, .Identifier) orelse break :blk null; | | |
| 1927 | if (eatToken(it, .Colon) != null) break :blk identifier; | | |
| 1928 | putBackToken(it, identifier); // ParamType may also be an identifier | | |
| 1929 | break :blk null; | | |
| 1930 | }; | | |
| 1931 | const param_type = (try parseParamType(arena, it, tree)) orelse { | | |
| 1932 | // Only return cleanly if no keyword, identifier, or doc comment was found | | |
| 1933 | if (noalias_token == null and | | |
| 1934 | comptime_token == null and | | |
| 1935 | name_token == null and | | |
| 1936 | doc_comments == null) return null; | | |
| 1937 | try tree.errors.push(.{ | | |
| 1938 | .ExpectedParamType = .{ .token = it.index }, | | |
| 1939 | }); | 1907 | }); |
| 1940 | return error.ParseError; | 1908 | _ = try p.expectToken(.RParen); |
| 1941 | }; | 1909 | return node; |
| | 1910 | } |
| 1942 | | 1911 | |
| 1943 | const param_decl = try arena.create(Node.ParamDecl); | 1912 | /// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN |
| 1944 | param_decl.* = .{ | 1913 | fn parseLinkSection(p: *Parser) !?*Node { |
| 1945 | .doc_comments = doc_comments, | 1914 | _ = p.eatToken(.Keyword_linksection) orelse return null; |
| 1946 | .comptime_token = comptime_token, | 1915 | _ = try p.expectToken(.LParen); |
| 1947 | .noalias_token = noalias_token, | 1916 | const expr_node = try p.expectNode(parseExpr, .{ |
| 1948 | .name_token = name_token, | 1917 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 1949 | .param_type = param_type, | 1918 | }); |
| 1950 | }; | 1919 | _ = try p.expectToken(.RParen); |
| 1951 | return &param_decl.base; | 1920 | return expr_node; |
| 1952 | } | 1921 | } |
| 1953 | | 1922 | |
| 1954 | /// ParamType | 1923 | /// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN |
| 1955 | /// <- KEYWORD_var | 1924 | fn parseCallconv(p: *Parser) !?*Node { |
| 1956 | /// / DOT3 | 1925 | _ = p.eatToken(.Keyword_callconv) orelse return null; |
| 1957 | /// / TypeExpr | 1926 | _ = try p.expectToken(.LParen); |
| 1958 | fn parseParamType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?Node.ParamDecl.ParamType { | 1927 | const expr_node = try p.expectNode(parseExpr, .{ |
| 1959 | // TODO cast from tuple to error union is broken | 1928 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 1960 | const P = Node.ParamDecl.ParamType; | 1929 | }); |
| 1961 | if (try parseVarType(arena, it, tree)) |node| return P{ .var_type = node }; | 1930 | _ = try p.expectToken(.RParen); |
| 1962 | if (eatToken(it, .Ellipsis3)) |token| return P{ .var_args = token }; | 1931 | return expr_node; |
| 1963 | if (try parseTypeExpr(arena, it, tree)) |node| return P{ .type_expr = node }; | 1932 | } |
| 1964 | return null; | 1933 | |
| 1965 | } | 1934 | /// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType |
| 1966 | | 1935 | fn parseParamDecl(p: *Parser) !?*Node { |
| 1967 | /// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? | 1936 | const doc_comments = try p.parseDocComment(); |
| 1968 | fn parseIfPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1937 | const noalias_token = p.eatToken(.Keyword_noalias); |
| 1969 | const if_token = eatToken(it, .Keyword_if) orelse return null; | 1938 | const comptime_token = if (noalias_token == null) p.eatToken(.Keyword_comptime) else null; |
| 1970 | _ = try expectToken(it, tree, .LParen); | 1939 | const name_token = blk: { |
| 1971 | const condition = try expectNode(arena, it, tree, parseExpr, .{ | 1940 | const identifier = p.eatToken(.Identifier) orelse break :blk null; |
| 1972 | .ExpectedExpr = .{ .token = it.index }, | 1941 | if (p.eatToken(.Colon) != null) break :blk identifier; |
| 1973 | }); | 1942 | p.putBackToken(identifier); // ParamType may also be an identifier |
| 1974 | _ = try expectToken(it, tree, .RParen); | 1943 | break :blk null; |
| 1975 | const payload = try parsePtrPayload(arena, it, tree); | 1944 | }; |
| 1976 | | 1945 | const param_type = (try p.parseParamType()) orelse { |
| 1977 | const node = try arena.create(Node.If); | 1946 | // Only return cleanly if no keyword, identifier, or doc comment was found |
| 1978 | node.* = .{ | 1947 | if (noalias_token == null and |
| 1979 | .if_token = if_token, | 1948 | comptime_token == null and |
| 1980 | .condition = condition, | 1949 | name_token == null and |
| 1981 | .payload = payload, | 1950 | doc_comments == null) return null; |
| 1982 | .body = undefined, // set by caller | 1951 | try p.errors.append(p.gpa, .{ |
| 1983 | .@"else" = null, | 1952 | .ExpectedParamType = .{ .token = p.tok_i }, |
| 1984 | }; | 1953 | }); |
| 1985 | return &node.base; | 1954 | return error.ParseError; |
| 1986 | } | 1955 | }; |
| 1987 | | 1956 | |
| 1988 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | 1957 | const param_decl = try p.arena.allocator.create(Node.ParamDecl); |
| 1989 | fn parseWhilePrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1958 | param_decl.* = .{ |
| 1990 | const while_token = eatToken(it, .Keyword_while) orelse return null; | 1959 | .doc_comments = doc_comments, |
| 1991 | | 1960 | .comptime_token = comptime_token, |
| 1992 | _ = try expectToken(it, tree, .LParen); | 1961 | .noalias_token = noalias_token, |
| 1993 | const condition = try expectNode(arena, it, tree, parseExpr, .{ | 1962 | .name_token = name_token, |
| 1994 | .ExpectedExpr = .{ .token = it.index }, | 1963 | .param_type = param_type, |
| 1995 | }); | 1964 | }; |
| 1996 | _ = try expectToken(it, tree, .RParen); | 1965 | return &param_decl.base; |
| 1997 | | 1966 | } |
| 1998 | const payload = try parsePtrPayload(arena, it, tree); | 1967 | |
| 1999 | const continue_expr = try parseWhileContinueExpr(arena, it, tree); | 1968 | /// ParamType |
| 2000 | | 1969 | /// <- KEYWORD_var |
| 2001 | const node = try arena.create(Node.While); | 1970 | /// / DOT3 |
| 2002 | node.* = .{ | 1971 | /// / TypeExpr |
| 2003 | .label = null, | 1972 | fn parseParamType(p: *Parser) !?Node.ParamDecl.ParamType { |
| 2004 | .inline_token = null, | 1973 | // TODO cast from tuple to error union is broken |
| 2005 | .while_token = while_token, | 1974 | const P = Node.ParamDecl.ParamType; |
| 2006 | .condition = condition, | 1975 | if (try p.parseVarType()) |node| return P{ .var_type = node }; |
| 2007 | .payload = payload, | 1976 | if (p.eatToken(.Ellipsis3)) |token| return P{ .var_args = token }; |
| 2008 | .continue_expr = continue_expr, | 1977 | if (try p.parseTypeExpr()) |node| return P{ .type_expr = node }; |
| 2009 | .body = undefined, // set by caller | 1978 | return null; |
| 2010 | .@"else" = null, | 1979 | } |
| 2011 | }; | | |
| 2012 | return &node.base; | | |
| 2013 | } | | |
| 2014 | | 1980 | |
| 2015 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | 1981 | /// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? |
| 2016 | fn parseForPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1982 | fn parseIfPrefix(p: *Parser) !?*Node { |
| 2017 | const for_token = eatToken(it, .Keyword_for) orelse return null; | 1983 | const if_token = p.eatToken(.Keyword_if) orelse return null; |
| 2018 | | 1984 | _ = try p.expectToken(.LParen); |
| 2019 | _ = try expectToken(it, tree, .LParen); | 1985 | const condition = try p.expectNode(parseExpr, .{ |
| 2020 | const array_expr = try expectNode(arena, it, tree, parseExpr, .{ | 1986 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 2021 | .ExpectedExpr = .{ .token = it.index }, | 1987 | }); |
| 2022 | }); | 1988 | _ = try p.expectToken(.RParen); |
| 2023 | _ = try expectToken(it, tree, .RParen); | 1989 | const payload = try p.parsePtrPayload(); |
| 2024 | | | |
| 2025 | const payload = try expectNode(arena, it, tree, parsePtrIndexPayload, .{ | | |
| 2026 | .ExpectedPayload = .{ .token = it.index }, | | |
| 2027 | }); | | |
| 2028 | | | |
| 2029 | const node = try arena.create(Node.For); | | |
| 2030 | node.* = .{ | | |
| 2031 | .label = null, | | |
| 2032 | .inline_token = null, | | |
| 2033 | .for_token = for_token, | | |
| 2034 | .array_expr = array_expr, | | |
| 2035 | .payload = payload, | | |
| 2036 | .body = undefined, // set by caller | | |
| 2037 | .@"else" = null, | | |
| 2038 | }; | | |
| 2039 | return &node.base; | | |
| 2040 | } | | |
| 2041 | | 1990 | |
| 2042 | /// Payload <- PIPE IDENTIFIER PIPE | 1991 | const node = try p.arena.allocator.create(Node.If); |
| 2043 | fn parsePayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 1992 | node.* = .{ |
| 2044 | const lpipe = eatToken(it, .Pipe) orelse return null; | 1993 | .if_token = if_token, |
| 2045 | const identifier = try expectNode(arena, it, tree, parseIdentifier, .{ | 1994 | .condition = condition, |
| 2046 | .ExpectedIdentifier = .{ .token = it.index }, | 1995 | .payload = payload, |
| 2047 | }); | 1996 | .body = undefined, // set by caller |
| 2048 | const rpipe = try expectToken(it, tree, .Pipe); | 1997 | .@"else" = null, |
| 2049 | | 1998 | }; |
| 2050 | const node = try arena.create(Node.Payload); | 1999 | return &node.base; |
| 2051 | node.* = .{ | 2000 | } |
| 2052 | .lpipe = lpipe, | | |
| 2053 | .error_symbol = identifier, | | |
| 2054 | .rpipe = rpipe, | | |
| 2055 | }; | | |
| 2056 | return &node.base; | | |
| 2057 | } | | |
| 2058 | | 2001 | |
| 2059 | /// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE | 2002 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? |
| 2060 | fn parsePtrPayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 2003 | fn parseWhilePrefix(p: *Parser) !?*Node { |
| 2061 | const lpipe = eatToken(it, .Pipe) orelse return null; | 2004 | const while_token = p.eatToken(.Keyword_while) orelse return null; |
| 2062 | const asterisk = eatToken(it, .Asterisk); | | |
| 2063 | const identifier = try expectNode(arena, it, tree, parseIdentifier, .{ | | |
| 2064 | .ExpectedIdentifier = .{ .token = it.index }, | | |
| 2065 | }); | | |
| 2066 | const rpipe = try expectToken(it, tree, .Pipe); | | |
| 2067 | | | |
| 2068 | const node = try arena.create(Node.PointerPayload); | | |
| 2069 | node.* = .{ | | |
| 2070 | .lpipe = lpipe, | | |
| 2071 | .ptr_token = asterisk, | | |
| 2072 | .value_symbol = identifier, | | |
| 2073 | .rpipe = rpipe, | | |
| 2074 | }; | | |
| 2075 | return &node.base; | | |
| 2076 | } | | |
| 2077 | | 2005 | |
| 2078 | /// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE | 2006 | _ = try p.expectToken(.LParen); |
| 2079 | fn parsePtrIndexPayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 2007 | const condition = try p.expectNode(parseExpr, .{ |
| 2080 | const lpipe = eatToken(it, .Pipe) orelse return null; | 2008 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 2081 | const asterisk = eatToken(it, .Asterisk); | | |
| 2082 | const identifier = try expectNode(arena, it, tree, parseIdentifier, .{ | | |
| 2083 | .ExpectedIdentifier = .{ .token = it.index }, | | |
| 2084 | }); | | |
| 2085 | | | |
| 2086 | const index = if (eatToken(it, .Comma) == null) | | |
| 2087 | null | | |
| 2088 | else | | |
| 2089 | try expectNode(arena, it, tree, parseIdentifier, .{ | | |
| 2090 | .ExpectedIdentifier = .{ .token = it.index }, | | |
| 2091 | }); | 2009 | }); |
| | 2010 | _ = try p.expectToken(.RParen); |
| 2092 | | 2011 | |
| 2093 | const rpipe = try expectToken(it, tree, .Pipe); | 2012 | const payload = try p.parsePtrPayload(); |
| | 2013 | const continue_expr = try p.parseWhileContinueExpr(); |
| 2094 | | 2014 | |
| 2095 | const node = try arena.create(Node.PointerIndexPayload); | 2015 | const node = try p.arena.allocator.create(Node.While); |
| 2096 | node.* = .{ | 2016 | node.* = .{ |
| 2097 | .lpipe = lpipe, | 2017 | .label = null, |
| 2098 | .ptr_token = asterisk, | 2018 | .inline_token = null, |
| 2099 | .value_symbol = identifier, | 2019 | .while_token = while_token, |
| 2100 | .index_symbol = index, | 2020 | .condition = condition, |
| 2101 | .rpipe = rpipe, | 2021 | .payload = payload, |
| 2102 | }; | 2022 | .continue_expr = continue_expr, |
| 2103 | return &node.base; | 2023 | .body = undefined, // set by caller |
| 2104 | } | 2024 | .@"else" = null, |
| | 2025 | }; |
| | 2026 | return &node.base; |
| | 2027 | } |
| 2105 | | 2028 | |
| 2106 | /// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr | 2029 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload |
| 2107 | fn parseSwitchProng(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 2030 | fn parseForPrefix(p: *Parser) !?*Node { |
| 2108 | const node = (try parseSwitchCase(arena, it, tree)) orelse return null; | 2031 | const for_token = p.eatToken(.Keyword_for) orelse return null; |
| 2109 | const arrow = try expectToken(it, tree, .EqualAngleBracketRight); | | |
| 2110 | const payload = try parsePtrPayload(arena, it, tree); | | |
| 2111 | const expr = try expectNode(arena, it, tree, parseAssignExpr, .{ | | |
| 2112 | .ExpectedExprOrAssignment = .{ .token = it.index }, | | |
| 2113 | }); | | |
| 2114 | | | |
| 2115 | const switch_case = node.cast(Node.SwitchCase).?; | | |
| 2116 | switch_case.arrow_token = arrow; | | |
| 2117 | switch_case.payload = payload; | | |
| 2118 | switch_case.expr = expr; | | |
| 2119 | | | |
| 2120 | return node; | | |
| 2121 | } | | |
| 2122 | | 2032 | |
| 2123 | /// SwitchCase | 2033 | _ = try p.expectToken(.LParen); |
| 2124 | /// <- SwitchItem (COMMA SwitchItem)* COMMA? | 2034 | const array_expr = try p.expectNode(parseExpr, .{ |
| 2125 | /// / KEYWORD_else | 2035 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 2126 | fn parseSwitchCase(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 2036 | }); |
| 2127 | var list = Node.SwitchCase.ItemList.init(arena); | 2037 | _ = try p.expectToken(.RParen); |
| 2128 | | 2038 | |
| 2129 | if (try parseSwitchItem(arena, it, tree)) |first_item| { | 2039 | const payload = try p.expectNode(parsePtrIndexPayload, .{ |
| 2130 | try list.push(first_item); | 2040 | .ExpectedPayload = .{ .token = p.tok_i }, |
| 2131 | while (eatToken(it, .Comma) != null) { | 2041 | }); |
| 2132 | const next_item = (try parseSwitchItem(arena, it, tree)) orelse break; | | |
| 2133 | try list.push(next_item); | | |
| 2134 | } | | |
| 2135 | } else if (eatToken(it, .Keyword_else)) |else_token| { | | |
| 2136 | const else_node = try arena.create(Node.SwitchElse); | | |
| 2137 | else_node.* = .{ | | |
| 2138 | .token = else_token, | | |
| 2139 | }; | | |
| 2140 | try list.push(&else_node.base); | | |
| 2141 | } else return null; | | |
| 2142 | | 2042 | |
| 2143 | const node = try arena.create(Node.SwitchCase); | 2043 | const node = try p.arena.allocator.create(Node.For); |
| 2144 | node.* = .{ | 2044 | node.* = .{ |
| 2145 | .items = list, | 2045 | .label = null, |
| 2146 | .arrow_token = undefined, // set by caller | 2046 | .inline_token = null, |
| 2147 | .payload = null, | 2047 | .for_token = for_token, |
| 2148 | .expr = undefined, // set by caller | 2048 | .array_expr = array_expr, |
| 2149 | }; | 2049 | .payload = payload, |
| 2150 | return &node.base; | 2050 | .body = undefined, // set by caller |
| 2151 | } | 2051 | .@"else" = null, |
| | 2052 | }; |
| | 2053 | return &node.base; |
| | 2054 | } |
| 2152 | | 2055 | |
| 2153 | /// SwitchItem <- Expr (DOT3 Expr)? | 2056 | /// Payload <- PIPE IDENTIFIER PIPE |
| 2154 | fn parseSwitchItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 2057 | fn parsePayload(p: *Parser) !?*Node { |
| 2155 | const expr = (try parseExpr(arena, it, tree)) orelse return null; | 2058 | const lpipe = p.eatToken(.Pipe) orelse return null; |
| 2156 | if (eatToken(it, .Ellipsis3)) |token| { | 2059 | const identifier = try p.expectNode(parseIdentifier, .{ |
| 2157 | const range_end = try expectNode(arena, it, tree, parseExpr, .{ | 2060 | .ExpectedIdentifier = .{ .token = p.tok_i }, |
| 2158 | .ExpectedExpr = .{ .token = it.index }, | | |
| 2159 | }); | 2061 | }); |
| | 2062 | const rpipe = try p.expectToken(.Pipe); |
| 2160 | | 2063 | |
| 2161 | const node = try arena.create(Node.InfixOp); | 2064 | const node = try p.arena.allocator.create(Node.Payload); |
| 2162 | node.* = .{ | 2065 | node.* = .{ |
| 2163 | .op_token = token, | 2066 | .lpipe = lpipe, |
| 2164 | .lhs = expr, | 2067 | .error_symbol = identifier, |
| 2165 | .op = .Range, | 2068 | .rpipe = rpipe, |
| 2166 | .rhs = range_end, | | |
| 2167 | }; | 2069 | }; |
| 2168 | return &node.base; | 2070 | return &node.base; |
| 2169 | } | 2071 | } |
| 2170 | return expr; | | |
| 2171 | } | | |
| 2172 | | 2072 | |
| 2173 | /// AssignOp | 2073 | /// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE |
| 2174 | /// <- ASTERISKEQUAL | 2074 | fn parsePtrPayload(p: *Parser) !?*Node { |
| 2175 | /// / SLASHEQUAL | 2075 | const lpipe = p.eatToken(.Pipe) orelse return null; |
| 2176 | /// / PERCENTEQUAL | 2076 | const asterisk = p.eatToken(.Asterisk); |
| 2177 | /// / PLUSEQUAL | 2077 | const identifier = try p.expectNode(parseIdentifier, .{ |
| 2178 | /// / MINUSEQUAL | 2078 | .ExpectedIdentifier = .{ .token = p.tok_i }, |
| 2179 | /// / LARROW2EQUAL | 2079 | }); |
| 2180 | /// / RARROW2EQUAL | 2080 | const rpipe = try p.expectToken(.Pipe); |
| 2181 | /// / AMPERSANDEQUAL | | |
| 2182 | /// / CARETEQUAL | | |
| 2183 | /// / PIPEEQUAL | | |
| 2184 | /// / ASTERISKPERCENTEQUAL | | |
| 2185 | /// / PLUSPERCENTEQUAL | | |
| 2186 | /// / MINUSPERCENTEQUAL | | |
| 2187 | /// / EQUAL | | |
| 2188 | fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2189 | const token = nextToken(it); | | |
| 2190 | const op: Node.InfixOp.Op = switch (token.ptr.id) { | | |
| 2191 | .AsteriskEqual => .AssignMul, | | |
| 2192 | .SlashEqual => .AssignDiv, | | |
| 2193 | .PercentEqual => .AssignMod, | | |
| 2194 | .PlusEqual => .AssignAdd, | | |
| 2195 | .MinusEqual => .AssignSub, | | |
| 2196 | .AngleBracketAngleBracketLeftEqual => .AssignBitShiftLeft, | | |
| 2197 | .AngleBracketAngleBracketRightEqual => .AssignBitShiftRight, | | |
| 2198 | .AmpersandEqual => .AssignBitAnd, | | |
| 2199 | .CaretEqual => .AssignBitXor, | | |
| 2200 | .PipeEqual => .AssignBitOr, | | |
| 2201 | .AsteriskPercentEqual => .AssignMulWrap, | | |
| 2202 | .PlusPercentEqual => .AssignAddWrap, | | |
| 2203 | .MinusPercentEqual => .AssignSubWrap, | | |
| 2204 | .Equal => .Assign, | | |
| 2205 | else => { | | |
| 2206 | putBackToken(it, token.index); | | |
| 2207 | return null; | | |
| 2208 | }, | | |
| 2209 | }; | | |
| 2210 | | 2081 | |
| 2211 | const node = try arena.create(Node.InfixOp); | 2082 | const node = try p.arena.allocator.create(Node.PointerPayload); |
| 2212 | node.* = .{ | 2083 | node.* = .{ |
| 2213 | .op_token = token.index, | 2084 | .lpipe = lpipe, |
| 2214 | .lhs = undefined, // set by caller | 2085 | .ptr_token = asterisk, |
| 2215 | .op = op, | 2086 | .value_symbol = identifier, |
| 2216 | .rhs = undefined, // set by caller | 2087 | .rpipe = rpipe, |
| 2217 | }; | 2088 | }; |
| 2218 | return &node.base; | 2089 | return &node.base; |
| 2219 | } | 2090 | } |
| 2220 | | 2091 | |
| 2221 | /// CompareOp | 2092 | /// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE |
| 2222 | /// <- EQUALEQUAL | 2093 | fn parsePtrIndexPayload(p: *Parser) !?*Node { |
| 2223 | /// / EXCLAMATIONMARKEQUAL | 2094 | const lpipe = p.eatToken(.Pipe) orelse return null; |
| 2224 | /// / LARROW | 2095 | const asterisk = p.eatToken(.Asterisk); |
| 2225 | /// / RARROW | 2096 | const identifier = try p.expectNode(parseIdentifier, .{ |
| 2226 | /// / LARROWEQUAL | 2097 | .ExpectedIdentifier = .{ .token = p.tok_i }, |
| 2227 | /// / RARROWEQUAL | 2098 | }); |
| 2228 | fn parseCompareOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2229 | const token = nextToken(it); | | |
| 2230 | const op: Node.InfixOp.Op = switch (token.ptr.id) { | | |
| 2231 | .EqualEqual => .EqualEqual, | | |
| 2232 | .BangEqual => .BangEqual, | | |
| 2233 | .AngleBracketLeft => .LessThan, | | |
| 2234 | .AngleBracketRight => .GreaterThan, | | |
| 2235 | .AngleBracketLeftEqual => .LessOrEqual, | | |
| 2236 | .AngleBracketRightEqual => .GreaterOrEqual, | | |
| 2237 | else => { | | |
| 2238 | putBackToken(it, token.index); | | |
| 2239 | return null; | | |
| 2240 | }, | | |
| 2241 | }; | | |
| 2242 | | 2099 | |
| 2243 | return try createInfixOp(arena, token.index, op); | 2100 | const index = if (p.eatToken(.Comma) == null) |
| 2244 | } | 2101 | null |
| | 2102 | else |
| | 2103 | try p.expectNode(parseIdentifier, .{ |
| | 2104 | .ExpectedIdentifier = .{ .token = p.tok_i }, |
| | 2105 | }); |
| 2245 | | 2106 | |
| 2246 | /// BitwiseOp | 2107 | const rpipe = try p.expectToken(.Pipe); |
| 2247 | /// <- AMPERSAND | | |
| 2248 | /// / CARET | | |
| 2249 | /// / PIPE | | |
| 2250 | /// / KEYWORD_orelse | | |
| 2251 | /// / KEYWORD_catch Payload? | | |
| 2252 | fn parseBitwiseOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2253 | const token = nextToken(it); | | |
| 2254 | const op: Node.InfixOp.Op = switch (token.ptr.id) { | | |
| 2255 | .Ampersand => .BitAnd, | | |
| 2256 | .Caret => .BitXor, | | |
| 2257 | .Pipe => .BitOr, | | |
| 2258 | .Keyword_orelse => .UnwrapOptional, | | |
| 2259 | .Keyword_catch => .{ .Catch = try parsePayload(arena, it, tree) }, | | |
| 2260 | else => { | | |
| 2261 | putBackToken(it, token.index); | | |
| 2262 | return null; | | |
| 2263 | }, | | |
| 2264 | }; | | |
| 2265 | | 2108 | |
| 2266 | return try createInfixOp(arena, token.index, op); | 2109 | const node = try p.arena.allocator.create(Node.PointerIndexPayload); |
| 2267 | } | 2110 | node.* = .{ |
| | 2111 | .lpipe = lpipe, |
| | 2112 | .ptr_token = asterisk, |
| | 2113 | .value_symbol = identifier, |
| | 2114 | .index_symbol = index, |
| | 2115 | .rpipe = rpipe, |
| | 2116 | }; |
| | 2117 | return &node.base; |
| | 2118 | } |
| 2268 | | 2119 | |
| 2269 | /// BitShiftOp | 2120 | /// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr |
| 2270 | /// <- LARROW2 | 2121 | fn parseSwitchProng(p: *Parser) !?*Node { |
| 2271 | /// / RARROW2 | 2122 | const node = (try p.parseSwitchCase()) orelse return null; |
| 2272 | fn parseBitShiftOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 2123 | const arrow = try p.expectToken(.EqualAngleBracketRight); |
| 2273 | const token = nextToken(it); | 2124 | const payload = try p.parsePtrPayload(); |
| 2274 | const op: Node.InfixOp.Op = switch (token.ptr.id) { | 2125 | const expr = try p.expectNode(parseAssignExpr, .{ |
| 2275 | .AngleBracketAngleBracketLeft => .BitShiftLeft, | 2126 | .ExpectedExprOrAssignment = .{ .token = p.tok_i }, |
| 2276 | .AngleBracketAngleBracketRight => .BitShiftRight, | 2127 | }); |
| 2277 | else => { | | |
| 2278 | putBackToken(it, token.index); | | |
| 2279 | return null; | | |
| 2280 | }, | | |
| 2281 | }; | | |
| 2282 | | 2128 | |
| 2283 | return try createInfixOp(arena, token.index, op); | 2129 | const switch_case = node.cast(Node.SwitchCase).?; |
| 2284 | } | 2130 | switch_case.arrow_token = arrow; |
| | 2131 | switch_case.payload = payload; |
| | 2132 | switch_case.expr = expr; |
| 2285 | | 2133 | |
| 2286 | /// AdditionOp | 2134 | return node; |
| 2287 | /// <- PLUS | 2135 | } |
| 2288 | /// / MINUS | | |
| 2289 | /// / PLUS2 | | |
| 2290 | /// / PLUSPERCENT | | |
| 2291 | /// / MINUSPERCENT | | |
| 2292 | fn parseAdditionOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2293 | const token = nextToken(it); | | |
| 2294 | const op: Node.InfixOp.Op = switch (token.ptr.id) { | | |
| 2295 | .Plus => .Add, | | |
| 2296 | .Minus => .Sub, | | |
| 2297 | .PlusPlus => .ArrayCat, | | |
| 2298 | .PlusPercent => .AddWrap, | | |
| 2299 | .MinusPercent => .SubWrap, | | |
| 2300 | else => { | | |
| 2301 | putBackToken(it, token.index); | | |
| 2302 | return null; | | |
| 2303 | }, | | |
| 2304 | }; | | |
| 2305 | | 2136 | |
| 2306 | return try createInfixOp(arena, token.index, op); | 2137 | /// SwitchCase |
| 2307 | } | 2138 | /// <- SwitchItem (COMMA SwitchItem)* COMMA? |
| | 2139 | /// / KEYWORD_else |
| | 2140 | fn parseSwitchCase(p: *Parser) !?*Node { |
| | 2141 | var list = Node.SwitchCase.ItemList{}; |
| | 2142 | var list_it = &list.first; |
| 2308 | | 2143 | |
| 2309 | /// MultiplyOp | 2144 | if (try p.parseSwitchItem()) |first_item| { |
| 2310 | /// <- PIPE2 | 2145 | list_it = try p.llpush(*Node, list_it, first_item); |
| 2311 | /// / ASTERISK | 2146 | while (p.eatToken(.Comma) != null) { |
| 2312 | /// / SLASH | 2147 | const next_item = (try p.parseSwitchItem()) orelse break; |
| 2313 | /// / PERCENT | 2148 | list_it = try p.llpush(*Node, list_it, next_item); |
| 2314 | /// / ASTERISK2 | 2149 | } |
| 2315 | /// / ASTERISKPERCENT | 2150 | } else if (p.eatToken(.Keyword_else)) |else_token| { |
| 2316 | fn parseMultiplyOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 2151 | const else_node = try p.arena.allocator.create(Node.SwitchElse); |
| 2317 | const token = nextToken(it); | 2152 | else_node.* = .{ |
| 2318 | const op: Node.InfixOp.Op = switch (token.ptr.id) { | 2153 | .token = else_token, |
| 2319 | .PipePipe => .MergeErrorSets, | 2154 | }; |
| 2320 | .Asterisk => .Mul, | 2155 | list_it = try p.llpush(*Node, list_it, &else_node.base); |
| 2321 | .Slash => .Div, | 2156 | } else return null; |
| 2322 | .Percent => .Mod, | | |
| 2323 | .AsteriskAsterisk => .ArrayMult, | | |
| 2324 | .AsteriskPercent => .MulWrap, | | |
| 2325 | else => { | | |
| 2326 | putBackToken(it, token.index); | | |
| 2327 | return null; | | |
| 2328 | }, | | |
| 2329 | }; | | |
| 2330 | | 2157 | |
| 2331 | return try createInfixOp(arena, token.index, op); | 2158 | const node = try p.arena.allocator.create(Node.SwitchCase); |
| 2332 | } | 2159 | node.* = .{ |
| | 2160 | .items = list, |
| | 2161 | .arrow_token = undefined, // set by caller |
| | 2162 | .payload = null, |
| | 2163 | .expr = undefined, // set by caller |
| | 2164 | }; |
| | 2165 | return &node.base; |
| | 2166 | } |
| 2333 | | 2167 | |
| 2334 | /// PrefixOp | 2168 | /// SwitchItem <- Expr (DOT3 Expr)? |
| 2335 | /// <- EXCLAMATIONMARK | 2169 | fn parseSwitchItem(p: *Parser) !?*Node { |
| 2336 | /// / MINUS | 2170 | const expr = (try p.parseExpr()) orelse return null; |
| 2337 | /// / TILDE | 2171 | if (p.eatToken(.Ellipsis3)) |token| { |
| 2338 | /// / MINUSPERCENT | 2172 | const range_end = try p.expectNode(parseExpr, .{ |
| 2339 | /// / AMPERSAND | 2173 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 2340 | /// / KEYWORD_try | 2174 | }); |
| 2341 | /// / KEYWORD_await | | |
| 2342 | fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2343 | const token = nextToken(it); | | |
| 2344 | const op: Node.PrefixOp.Op = switch (token.ptr.id) { | | |
| 2345 | .Bang => .BoolNot, | | |
| 2346 | .Minus => .Negation, | | |
| 2347 | .Tilde => .BitNot, | | |
| 2348 | .MinusPercent => .NegationWrap, | | |
| 2349 | .Ampersand => .AddressOf, | | |
| 2350 | .Keyword_try => .Try, | | |
| 2351 | .Keyword_await => .Await, | | |
| 2352 | else => { | | |
| 2353 | putBackToken(it, token.index); | | |
| 2354 | return null; | | |
| 2355 | }, | | |
| 2356 | }; | | |
| 2357 | | 2175 | |
| 2358 | const node = try arena.create(Node.PrefixOp); | 2176 | const node = try p.arena.allocator.create(Node.InfixOp); |
| 2359 | node.* = .{ | 2177 | node.* = .{ |
| 2360 | .op_token = token.index, | 2178 | .op_token = token, |
| 2361 | .op = op, | 2179 | .lhs = expr, |
| 2362 | .rhs = undefined, // set by caller | 2180 | .op = .Range, |
| 2363 | }; | 2181 | .rhs = range_end, |
| 2364 | return &node.base; | 2182 | }; |
| 2365 | } | 2183 | return &node.base; |
| | 2184 | } |
| | 2185 | return expr; |
| | 2186 | } |
| | 2187 | |
| | 2188 | /// AssignOp |
| | 2189 | /// <- ASTERISKEQUAL |
| | 2190 | /// / SLASHEQUAL |
| | 2191 | /// / PERCENTEQUAL |
| | 2192 | /// / PLUSEQUAL |
| | 2193 | /// / MINUSEQUAL |
| | 2194 | /// / LARROW2EQUAL |
| | 2195 | /// / RARROW2EQUAL |
| | 2196 | /// / AMPERSANDEQUAL |
| | 2197 | /// / CARETEQUAL |
| | 2198 | /// / PIPEEQUAL |
| | 2199 | /// / ASTERISKPERCENTEQUAL |
| | 2200 | /// / PLUSPERCENTEQUAL |
| | 2201 | /// / MINUSPERCENTEQUAL |
| | 2202 | /// / EQUAL |
| | 2203 | fn parseAssignOp(p: *Parser) !?*Node { |
| | 2204 | const token = p.nextToken(); |
| | 2205 | const op: Node.InfixOp.Op = switch (token.ptr.id) { |
| | 2206 | .AsteriskEqual => .AssignMul, |
| | 2207 | .SlashEqual => .AssignDiv, |
| | 2208 | .PercentEqual => .AssignMod, |
| | 2209 | .PlusEqual => .AssignAdd, |
| | 2210 | .MinusEqual => .AssignSub, |
| | 2211 | .AngleBracketAngleBracketLeftEqual => .AssignBitShiftLeft, |
| | 2212 | .AngleBracketAngleBracketRightEqual => .AssignBitShiftRight, |
| | 2213 | .AmpersandEqual => .AssignBitAnd, |
| | 2214 | .CaretEqual => .AssignBitXor, |
| | 2215 | .PipeEqual => .AssignBitOr, |
| | 2216 | .AsteriskPercentEqual => .AssignMulWrap, |
| | 2217 | .PlusPercentEqual => .AssignAddWrap, |
| | 2218 | .MinusPercentEqual => .AssignSubWrap, |
| | 2219 | .Equal => .Assign, |
| | 2220 | else => { |
| | 2221 | p.putBackToken(token.index); |
| | 2222 | return null; |
| | 2223 | }, |
| | 2224 | }; |
| 2366 | | 2225 | |
| 2367 | // TODO: ArrayTypeStart is either an array or a slice, but const/allowzero only work on | 2226 | const node = try p.arena.allocator.create(Node.InfixOp); |
| 2368 | // pointers. Consider updating this rule: | | |
| 2369 | // ... | | |
| 2370 | // / ArrayTypeStart | | |
| 2371 | // / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | | |
| 2372 | // / PtrTypeStart ... | | |
| 2373 | | | |
| 2374 | /// PrefixTypeOp | | |
| 2375 | /// <- QUESTIONMARK | | |
| 2376 | /// / KEYWORD_anyframe MINUSRARROW | | |
| 2377 | /// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | | |
| 2378 | /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | | |
| 2379 | fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2380 | if (eatToken(it, .QuestionMark)) |token| { | | |
| 2381 | const node = try arena.create(Node.PrefixOp); | | |
| 2382 | node.* = .{ | 2227 | node.* = .{ |
| 2383 | .op_token = token, | 2228 | .op_token = token.index, |
| 2384 | .op = .OptionalType, | 2229 | .lhs = undefined, // set by caller |
| | 2230 | .op = op, |
| 2385 | .rhs = undefined, // set by caller | 2231 | .rhs = undefined, // set by caller |
| 2386 | }; | 2232 | }; |
| 2387 | return &node.base; | 2233 | return &node.base; |
| 2388 | } | 2234 | } |
| 2389 | | 2235 | |
| 2390 | // TODO: Returning a AnyFrameType instead of PrefixOp makes casting and setting .rhs or | 2236 | /// CompareOp |
| 2391 | // .return_type more difficult for the caller (see parsePrefixOpExpr helper). | 2237 | /// <- EQUALEQUAL |
| 2392 | // Consider making the AnyFrameType a member of PrefixOp and add a | 2238 | /// / EXCLAMATIONMARKEQUAL |
| 2393 | // PrefixOp.AnyFrameType variant? | 2239 | /// / LARROW |
| 2394 | if (eatToken(it, .Keyword_anyframe)) |token| { | 2240 | /// / RARROW |
| 2395 | const arrow = eatToken(it, .Arrow) orelse { | 2241 | /// / LARROWEQUAL |
| 2396 | putBackToken(it, token); | 2242 | /// / RARROWEQUAL |
| 2397 | return null; | 2243 | fn parseCompareOp(p: *Parser) !?*Node { |
| | 2244 | const token = p.nextToken(); |
| | 2245 | const op: Node.InfixOp.Op = switch (token.ptr.id) { |
| | 2246 | .EqualEqual => .EqualEqual, |
| | 2247 | .BangEqual => .BangEqual, |
| | 2248 | .AngleBracketLeft => .LessThan, |
| | 2249 | .AngleBracketRight => .GreaterThan, |
| | 2250 | .AngleBracketLeftEqual => .LessOrEqual, |
| | 2251 | .AngleBracketRightEqual => .GreaterOrEqual, |
| | 2252 | else => { |
| | 2253 | p.putBackToken(token.index); |
| | 2254 | return null; |
| | 2255 | }, |
| 2398 | }; | 2256 | }; |
| 2399 | const node = try arena.create(Node.AnyFrameType); | 2257 | |
| 2400 | node.* = .{ | 2258 | return p.createInfixOp(token.index, op); |
| 2401 | .anyframe_token = token, | 2259 | } |
| 2402 | .result = .{ | 2260 | |
| 2403 | .arrow_token = arrow, | 2261 | /// BitwiseOp |
| 2404 | .return_type = undefined, // set by caller | 2262 | /// <- AMPERSAND |
| | 2263 | /// / CARET |
| | 2264 | /// / PIPE |
| | 2265 | /// / KEYWORD_orelse |
| | 2266 | /// / KEYWORD_catch Payload? |
| | 2267 | fn parseBitwiseOp(p: *Parser) !?*Node { |
| | 2268 | const token = p.nextToken(); |
| | 2269 | const op: Node.InfixOp.Op = switch (token.ptr.id) { |
| | 2270 | .Ampersand => .BitAnd, |
| | 2271 | .Caret => .BitXor, |
| | 2272 | .Pipe => .BitOr, |
| | 2273 | .Keyword_orelse => .UnwrapOptional, |
| | 2274 | .Keyword_catch => .{ .Catch = try p.parsePayload() }, |
| | 2275 | else => { |
| | 2276 | p.putBackToken(token.index); |
| | 2277 | return null; |
| | 2278 | }, |
| | 2279 | }; |
| | 2280 | |
| | 2281 | return p.createInfixOp(token.index, op); |
| | 2282 | } |
| | 2283 | |
| | 2284 | /// BitShiftOp |
| | 2285 | /// <- LARROW2 |
| | 2286 | /// / RARROW2 |
| | 2287 | fn parseBitShiftOp(p: *Parser) !?*Node { |
| | 2288 | const token = p.nextToken(); |
| | 2289 | const op: Node.InfixOp.Op = switch (token.ptr.id) { |
| | 2290 | .AngleBracketAngleBracketLeft => .BitShiftLeft, |
| | 2291 | .AngleBracketAngleBracketRight => .BitShiftRight, |
| | 2292 | else => { |
| | 2293 | p.putBackToken(token.index); |
| | 2294 | return null; |
| | 2295 | }, |
| | 2296 | }; |
| | 2297 | |
| | 2298 | return p.createInfixOp(token.index, op); |
| | 2299 | } |
| | 2300 | |
| | 2301 | /// AdditionOp |
| | 2302 | /// <- PLUS |
| | 2303 | /// / MINUS |
| | 2304 | /// / PLUS2 |
| | 2305 | /// / PLUSPERCENT |
| | 2306 | /// / MINUSPERCENT |
| | 2307 | fn parseAdditionOp(p: *Parser) !?*Node { |
| | 2308 | const token = p.nextToken(); |
| | 2309 | const op: Node.InfixOp.Op = switch (token.ptr.id) { |
| | 2310 | .Plus => .Add, |
| | 2311 | .Minus => .Sub, |
| | 2312 | .PlusPlus => .ArrayCat, |
| | 2313 | .PlusPercent => .AddWrap, |
| | 2314 | .MinusPercent => .SubWrap, |
| | 2315 | else => { |
| | 2316 | p.putBackToken(token.index); |
| | 2317 | return null; |
| | 2318 | }, |
| | 2319 | }; |
| | 2320 | |
| | 2321 | return p.createInfixOp(token.index, op); |
| | 2322 | } |
| | 2323 | |
| | 2324 | /// MultiplyOp |
| | 2325 | /// <- PIPE2 |
| | 2326 | /// / ASTERISK |
| | 2327 | /// / SLASH |
| | 2328 | /// / PERCENT |
| | 2329 | /// / ASTERISK2 |
| | 2330 | /// / ASTERISKPERCENT |
| | 2331 | fn parseMultiplyOp(p: *Parser) !?*Node { |
| | 2332 | const token = p.nextToken(); |
| | 2333 | const op: Node.InfixOp.Op = switch (token.ptr.id) { |
| | 2334 | .PipePipe => .MergeErrorSets, |
| | 2335 | .Asterisk => .Mul, |
| | 2336 | .Slash => .Div, |
| | 2337 | .Percent => .Mod, |
| | 2338 | .AsteriskAsterisk => .ArrayMult, |
| | 2339 | .AsteriskPercent => .MulWrap, |
| | 2340 | else => { |
| | 2341 | p.putBackToken(token.index); |
| | 2342 | return null; |
| | 2343 | }, |
| | 2344 | }; |
| | 2345 | |
| | 2346 | return p.createInfixOp(token.index, op); |
| | 2347 | } |
| | 2348 | |
| | 2349 | /// PrefixOp |
| | 2350 | /// <- EXCLAMATIONMARK |
| | 2351 | /// / MINUS |
| | 2352 | /// / TILDE |
| | 2353 | /// / MINUSPERCENT |
| | 2354 | /// / AMPERSAND |
| | 2355 | /// / KEYWORD_try |
| | 2356 | /// / KEYWORD_await |
| | 2357 | fn parsePrefixOp(p: *Parser) !?*Node { |
| | 2358 | const token = p.nextToken(); |
| | 2359 | const op: Node.PrefixOp.Op = switch (token.ptr.id) { |
| | 2360 | .Bang => .BoolNot, |
| | 2361 | .Minus => .Negation, |
| | 2362 | .Tilde => .BitNot, |
| | 2363 | .MinusPercent => .NegationWrap, |
| | 2364 | .Ampersand => .AddressOf, |
| | 2365 | .Keyword_try => .Try, |
| | 2366 | .Keyword_await => .Await, |
| | 2367 | else => { |
| | 2368 | p.putBackToken(token.index); |
| | 2369 | return null; |
| 2405 | }, | 2370 | }, |
| 2406 | }; | 2371 | }; |
| | 2372 | |
| | 2373 | const node = try p.arena.allocator.create(Node.PrefixOp); |
| | 2374 | node.* = .{ |
| | 2375 | .op_token = token.index, |
| | 2376 | .op = op, |
| | 2377 | .rhs = undefined, // set by caller |
| | 2378 | }; |
| 2407 | return &node.base; | 2379 | return &node.base; |
| 2408 | } | 2380 | } |
| 2409 | | 2381 | |
| 2410 | if (try parsePtrTypeStart(arena, it, tree)) |node| { | 2382 | // TODO: ArrayTypeStart is either an array or a slice, but const/allowzero only work on |
| 2411 | // If the token encountered was **, there will be two nodes instead of one. | 2383 | // pointers. Consider updating this rule: |
| 2412 | // The attributes should be applied to the rightmost operator. | 2384 | // ... |
| 2413 | const prefix_op = node.cast(Node.PrefixOp).?; | 2385 | // / ArrayTypeStart |
| 2414 | var ptr_info = if (tree.tokens.at(prefix_op.op_token).id == .AsteriskAsterisk) | 2386 | // / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* |
| 2415 | &prefix_op.rhs.cast(Node.PrefixOp).?.op.PtrType | 2387 | // / PtrTypeStart ... |
| 2416 | else | 2388 | |
| 2417 | &prefix_op.op.PtrType; | 2389 | /// PrefixTypeOp |
| | 2390 | /// <- QUESTIONMARK |
| | 2391 | /// / KEYWORD_anyframe MINUSRARROW |
| | 2392 | /// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* |
| | 2393 | /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* |
| | 2394 | fn parsePrefixTypeOp(p: *Parser) !?*Node { |
| | 2395 | if (p.eatToken(.QuestionMark)) |token| { |
| | 2396 | const node = try p.arena.allocator.create(Node.PrefixOp); |
| | 2397 | node.* = .{ |
| | 2398 | .op_token = token, |
| | 2399 | .op = .OptionalType, |
| | 2400 | .rhs = undefined, // set by caller |
| | 2401 | }; |
| | 2402 | return &node.base; |
| | 2403 | } |
| 2418 | | 2404 | |
| 2419 | while (true) { | 2405 | // TODO: Returning a AnyFrameType instead of PrefixOp makes casting and setting .rhs or |
| 2420 | if (eatToken(it, .Keyword_align)) |align_token| { | 2406 | // .return_type more difficult for the caller (see parsePrefixOpExpr helper). |
| 2421 | const lparen = try expectToken(it, tree, .LParen); | 2407 | // Consider making the AnyFrameType a member of PrefixOp and add a |
| 2422 | const expr_node = try expectNode(arena, it, tree, parseExpr, .{ | 2408 | // PrefixOp.AnyFrameType variant? |
| 2423 | .ExpectedExpr = .{ .token = it.index }, | 2409 | if (p.eatToken(.Keyword_anyframe)) |token| { |
| 2424 | }); | 2410 | const arrow = p.eatToken(.Arrow) orelse { |
| | 2411 | p.putBackToken(token); |
| | 2412 | return null; |
| | 2413 | }; |
| | 2414 | const node = try p.arena.allocator.create(Node.AnyFrameType); |
| | 2415 | node.* = .{ |
| | 2416 | .anyframe_token = token, |
| | 2417 | .result = .{ |
| | 2418 | .arrow_token = arrow, |
| | 2419 | .return_type = undefined, // set by caller |
| | 2420 | }, |
| | 2421 | }; |
| | 2422 | return &node.base; |
| | 2423 | } |
| 2425 | | 2424 | |
| 2426 | // Optional bit range | 2425 | if (try p.parsePtrTypeStart()) |node| { |
| 2427 | const bit_range = if (eatToken(it, .Colon)) |_| bit_range_value: { | 2426 | // If the token encountered was **, there will be two nodes instead of one. |
| 2428 | const range_start = try expectNode(arena, it, tree, parseIntegerLiteral, .{ | 2427 | // The attributes should be applied to the rightmost operator. |
| 2429 | .ExpectedIntegerLiteral = .{ .token = it.index }, | 2428 | const prefix_op = node.cast(Node.PrefixOp).?; |
| 2430 | }); | 2429 | var ptr_info = if (p.tokens[prefix_op.op_token].id == .AsteriskAsterisk) |
| 2431 | _ = try expectToken(it, tree, .Colon); | 2430 | &prefix_op.rhs.cast(Node.PrefixOp).?.op.PtrType |
| 2432 | const range_end = try expectNode(arena, it, tree, parseIntegerLiteral, .{ | 2431 | else |
| 2433 | .ExpectedIntegerLiteral = .{ .token = it.index }, | 2432 | &prefix_op.op.PtrType; |
| | 2433 | |
| | 2434 | while (true) { |
| | 2435 | if (p.eatToken(.Keyword_align)) |align_token| { |
| | 2436 | const lparen = try p.expectToken(.LParen); |
| | 2437 | const expr_node = try p.expectNode(parseExpr, .{ |
| | 2438 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 2434 | }); | 2439 | }); |
| 2435 | | 2440 | |
| 2436 | break :bit_range_value Node.PrefixOp.PtrInfo.Align.BitRange{ | 2441 | // Optional bit range |
| 2437 | .start = range_start, | 2442 | const bit_range = if (p.eatToken(.Colon)) |_| bit_range_value: { |
| 2438 | .end = range_end, | 2443 | const range_start = try p.expectNode(parseIntegerLiteral, .{ |
| | 2444 | .ExpectedIntegerLiteral = .{ .token = p.tok_i }, |
| | 2445 | }); |
| | 2446 | _ = try p.expectToken(.Colon); |
| | 2447 | const range_end = try p.expectNode(parseIntegerLiteral, .{ |
| | 2448 | .ExpectedIntegerLiteral = .{ .token = p.tok_i }, |
| | 2449 | }); |
| | 2450 | |
| | 2451 | break :bit_range_value Node.PrefixOp.PtrInfo.Align.BitRange{ |
| | 2452 | .start = range_start, |
| | 2453 | .end = range_end, |
| | 2454 | }; |
| | 2455 | } else null; |
| | 2456 | _ = try p.expectToken(.RParen); |
| | 2457 | |
| | 2458 | if (ptr_info.align_info != null) { |
| | 2459 | try p.errors.append(p.gpa, .{ |
| | 2460 | .ExtraAlignQualifier = .{ .token = p.tok_i - 1 }, |
| | 2461 | }); |
| | 2462 | continue; |
| | 2463 | } |
| | 2464 | |
| | 2465 | ptr_info.align_info = Node.PrefixOp.PtrInfo.Align{ |
| | 2466 | .node = expr_node, |
| | 2467 | .bit_range = bit_range, |
| 2439 | }; | 2468 | }; |
| 2440 | } else null; | | |
| 2441 | _ = try expectToken(it, tree, .RParen); | | |
| 2442 | | 2469 | |
| 2443 | if (ptr_info.align_info != null) { | | |
| 2444 | try tree.errors.push(.{ | | |
| 2445 | .ExtraAlignQualifier = .{ .token = it.index - 1 }, | | |
| 2446 | }); | | |
| 2447 | continue; | 2470 | continue; |
| 2448 | } | 2471 | } |
| 2449 | | 2472 | if (p.eatToken(.Keyword_const)) |const_token| { |
| 2450 | ptr_info.align_info = Node.PrefixOp.PtrInfo.Align{ | 2473 | if (ptr_info.const_token != null) { |
| 2451 | .node = expr_node, | 2474 | try p.errors.append(p.gpa, .{ |
| 2452 | .bit_range = bit_range, | 2475 | .ExtraConstQualifier = .{ .token = p.tok_i - 1 }, |
| 2453 | }; | 2476 | }); |
| 2454 | | 2477 | continue; |
| 2455 | continue; | 2478 | } |
| 2456 | } | 2479 | ptr_info.const_token = const_token; |
| 2457 | if (eatToken(it, .Keyword_const)) |const_token| { | | |
| 2458 | if (ptr_info.const_token != null) { | | |
| 2459 | try tree.errors.push(.{ | | |
| 2460 | .ExtraConstQualifier = .{ .token = it.index - 1 }, | | |
| 2461 | }); | | |
| 2462 | continue; | 2480 | continue; |
| 2463 | } | 2481 | } |
| 2464 | ptr_info.const_token = const_token; | 2482 | if (p.eatToken(.Keyword_volatile)) |volatile_token| { |
| 2465 | continue; | 2483 | if (ptr_info.volatile_token != null) { |
| 2466 | } | 2484 | try p.errors.append(p.gpa, .{ |
| 2467 | if (eatToken(it, .Keyword_volatile)) |volatile_token| { | 2485 | .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 }, |
| 2468 | if (ptr_info.volatile_token != null) { | 2486 | }); |
| 2469 | try tree.errors.push(.{ | 2487 | continue; |
| 2470 | .ExtraVolatileQualifier = .{ .token = it.index - 1 }, | 2488 | } |
| 2471 | }); | 2489 | ptr_info.volatile_token = volatile_token; |
| 2472 | continue; | 2490 | continue; |
| 2473 | } | 2491 | } |
| 2474 | ptr_info.volatile_token = volatile_token; | 2492 | if (p.eatToken(.Keyword_allowzero)) |allowzero_token| { |
| 2475 | continue; | 2493 | if (ptr_info.allowzero_token != null) { |
| 2476 | } | 2494 | try p.errors.append(p.gpa, .{ |
| 2477 | if (eatToken(it, .Keyword_allowzero)) |allowzero_token| { | 2495 | .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 }, |
| 2478 | if (ptr_info.allowzero_token != null) { | 2496 | }); |
| 2479 | try tree.errors.push(.{ | 2497 | continue; |
| 2480 | .ExtraAllowZeroQualifier = .{ .token = it.index - 1 }, | 2498 | } |
| 2481 | }); | 2499 | ptr_info.allowzero_token = allowzero_token; |
| 2482 | continue; | 2500 | continue; |
| 2483 | } | 2501 | } |
| 2484 | ptr_info.allowzero_token = allowzero_token; | 2502 | break; |
| 2485 | continue; | | |
| 2486 | } | 2503 | } |
| 2487 | break; | | |
| 2488 | } | | |
| 2489 | | 2504 | |
| 2490 | return node; | 2505 | return node; |
| 2491 | } | 2506 | } |
| 2492 | | 2507 | |
| 2493 | if (try parseArrayTypeStart(arena, it, tree)) |node| { | 2508 | if (try p.parseArrayTypeStart()) |node| { |
| 2494 | switch (node.cast(Node.PrefixOp).?.op) { | 2509 | switch (node.cast(Node.PrefixOp).?.op) { |
| 2495 | .ArrayType => {}, | 2510 | .ArrayType => {}, |
| 2496 | .SliceType => |*slice_type| { | 2511 | .SliceType => |*slice_type| { |
| 2497 | // Collect pointer qualifiers in any order, but disallow duplicates | 2512 | // Collect pointer qualifiers in any order, but disallow duplicates |
| 2498 | while (true) { | 2513 | while (true) { |
| 2499 | if (try parseByteAlign(arena, it, tree)) |align_expr| { | 2514 | if (try p.parseByteAlign()) |align_expr| { |
| 2500 | if (slice_type.align_info != null) { | 2515 | if (slice_type.align_info != null) { |
| 2501 | try tree.errors.push(.{ | 2516 | try p.errors.append(p.gpa, .{ |
| 2502 | .ExtraAlignQualifier = .{ .token = it.index - 1 }, | 2517 | .ExtraAlignQualifier = .{ .token = p.tok_i - 1 }, |
| 2503 | }); | 2518 | }); |
| | 2519 | continue; |
| | 2520 | } |
| | 2521 | slice_type.align_info = Node.PrefixOp.PtrInfo.Align{ |
| | 2522 | .node = align_expr, |
| | 2523 | .bit_range = null, |
| | 2524 | }; |
| 2504 | continue; | 2525 | continue; |
| 2505 | } | 2526 | } |
| 2506 | slice_type.align_info = Node.PrefixOp.PtrInfo.Align{ | 2527 | if (p.eatToken(.Keyword_const)) |const_token| { |
| 2507 | .node = align_expr, | 2528 | if (slice_type.const_token != null) { |
| 2508 | .bit_range = null, | 2529 | try p.errors.append(p.gpa, .{ |
| 2509 | }; | 2530 | .ExtraConstQualifier = .{ .token = p.tok_i - 1 }, |
| 2510 | continue; | 2531 | }); |
| 2511 | } | 2532 | continue; |
| 2512 | if (eatToken(it, .Keyword_const)) |const_token| { | 2533 | } |
| 2513 | if (slice_type.const_token != null) { | 2534 | slice_type.const_token = const_token; |
| 2514 | try tree.errors.push(.{ | | |
| 2515 | .ExtraConstQualifier = .{ .token = it.index - 1 }, | | |
| 2516 | }); | | |
| 2517 | continue; | 2535 | continue; |
| 2518 | } | 2536 | } |
| 2519 | slice_type.const_token = const_token; | 2537 | if (p.eatToken(.Keyword_volatile)) |volatile_token| { |
| 2520 | continue; | 2538 | if (slice_type.volatile_token != null) { |
| 2521 | } | 2539 | try p.errors.append(p.gpa, .{ |
| 2522 | if (eatToken(it, .Keyword_volatile)) |volatile_token| { | 2540 | .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 }, |
| 2523 | if (slice_type.volatile_token != null) { | 2541 | }); |
| 2524 | try tree.errors.push(.{ | 2542 | continue; |
| 2525 | .ExtraVolatileQualifier = .{ .token = it.index - 1 }, | 2543 | } |
| 2526 | }); | 2544 | slice_type.volatile_token = volatile_token; |
| 2527 | continue; | 2545 | continue; |
| 2528 | } | 2546 | } |
| 2529 | slice_type.volatile_token = volatile_token; | 2547 | if (p.eatToken(.Keyword_allowzero)) |allowzero_token| { |
| 2530 | continue; | 2548 | if (slice_type.allowzero_token != null) { |
| | 2549 | try p.errors.append(p.gpa, .{ |
| | 2550 | .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 }, |
| | 2551 | }); |
| | 2552 | continue; |
| | 2553 | } |
| | 2554 | slice_type.allowzero_token = allowzero_token; |
| | 2555 | continue; |
| | 2556 | } |
| | 2557 | break; |
| 2531 | } | 2558 | } |
| 2532 | if (eatToken(it, .Keyword_allowzero)) |allowzero_token| { | 2559 | }, |
| 2533 | if (slice_type.allowzero_token != null) { | 2560 | else => unreachable, |
| 2534 | try tree.errors.push(.{ | 2561 | } |
| 2535 | .ExtraAllowZeroQualifier = .{ .token = it.index - 1 }, | 2562 | return node; |
| | 2563 | } |
| | 2564 | |
| | 2565 | return null; |
| | 2566 | } |
| | 2567 | |
| | 2568 | /// SuffixOp |
| | 2569 | /// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET |
| | 2570 | /// / DOT IDENTIFIER |
| | 2571 | /// / DOTASTERISK |
| | 2572 | /// / DOTQUESTIONMARK |
| | 2573 | fn parseSuffixOp(p: *Parser) !?*Node { |
| | 2574 | const OpAndToken = struct { |
| | 2575 | op: Node.SuffixOp.Op, |
| | 2576 | token: TokenIndex, |
| | 2577 | }; |
| | 2578 | const op_and_token: OpAndToken = blk: { |
| | 2579 | if (p.eatToken(.LBracket)) |_| { |
| | 2580 | const index_expr = try p.expectNode(parseExpr, .{ |
| | 2581 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 2582 | }); |
| | 2583 | |
| | 2584 | if (p.eatToken(.Ellipsis2) != null) { |
| | 2585 | const end_expr = try p.parseExpr(); |
| | 2586 | const sentinel: ?*ast.Node = if (p.eatToken(.Colon) != null) |
| | 2587 | try p.parseExpr() |
| | 2588 | else |
| | 2589 | null; |
| | 2590 | break :blk .{ |
| | 2591 | .op = .{ |
| | 2592 | .Slice = .{ |
| | 2593 | .start = index_expr, |
| | 2594 | .end = end_expr, |
| | 2595 | .sentinel = sentinel, |
| | 2596 | }, |
| | 2597 | }, |
| | 2598 | .token = try p.expectToken(.RBracket), |
| | 2599 | }; |
| | 2600 | } |
| | 2601 | |
| | 2602 | break :blk .{ |
| | 2603 | .op = .{ .ArrayAccess = index_expr }, |
| | 2604 | .token = try p.expectToken(.RBracket), |
| | 2605 | }; |
| | 2606 | } |
| | 2607 | |
| | 2608 | if (p.eatToken(.PeriodAsterisk)) |period_asterisk| { |
| | 2609 | break :blk .{ .op = .Deref, .token = period_asterisk }; |
| | 2610 | } |
| | 2611 | |
| | 2612 | if (p.eatToken(.Period)) |period| { |
| | 2613 | if (try p.parseIdentifier()) |identifier| { |
| | 2614 | // TODO: It's a bit weird to return an InfixOp from the SuffixOp parser. |
| | 2615 | // Should there be an ast.Node.SuffixOp.FieldAccess variant? Or should |
| | 2616 | // this grammar rule be altered? |
| | 2617 | const node = try p.arena.allocator.create(Node.InfixOp); |
| | 2618 | node.* = .{ |
| | 2619 | .op_token = period, |
| | 2620 | .lhs = undefined, // set by caller |
| | 2621 | .op = .Period, |
| | 2622 | .rhs = identifier, |
| | 2623 | }; |
| | 2624 | return &node.base; |
| | 2625 | } |
| | 2626 | if (p.eatToken(.QuestionMark)) |question_mark| { |
| | 2627 | break :blk .{ .op = .UnwrapOptional, .token = question_mark }; |
| | 2628 | } |
| | 2629 | try p.errors.append(p.gpa, .{ |
| | 2630 | .ExpectedSuffixOp = .{ .token = p.tok_i }, |
| | 2631 | }); |
| | 2632 | return null; |
| | 2633 | } |
| | 2634 | |
| | 2635 | return null; |
| | 2636 | }; |
| | 2637 | |
| | 2638 | const node = try p.arena.allocator.create(Node.SuffixOp); |
| | 2639 | node.* = .{ |
| | 2640 | .lhs = undefined, // set by caller |
| | 2641 | .op = op_and_token.op, |
| | 2642 | .rtoken = op_and_token.token, |
| | 2643 | }; |
| | 2644 | return &node.base; |
| | 2645 | } |
| | 2646 | |
| | 2647 | /// FnCallArguments <- LPAREN ExprList RPAREN |
| | 2648 | /// ExprList <- (Expr COMMA)* Expr? |
| | 2649 | fn parseFnCallArguments(p: *Parser) !?AnnotatedParamList { |
| | 2650 | if (p.eatToken(.LParen) == null) return null; |
| | 2651 | const list = try ListParseFn(Node.FnProto.ParamList, parseExpr)(p); |
| | 2652 | const rparen = try p.expectToken(.RParen); |
| | 2653 | return AnnotatedParamList{ .list = list, .rparen = rparen }; |
| | 2654 | } |
| | 2655 | |
| | 2656 | const AnnotatedParamList = struct { |
| | 2657 | list: Node.FnProto.ParamList, // NOTE: may also be any other type SegmentedList(*Node, 2) |
| | 2658 | rparen: TokenIndex, |
| | 2659 | }; |
| | 2660 | |
| | 2661 | /// ArrayTypeStart <- LBRACKET Expr? RBRACKET |
| | 2662 | fn parseArrayTypeStart(p: *Parser) !?*Node { |
| | 2663 | const lbracket = p.eatToken(.LBracket) orelse return null; |
| | 2664 | const expr = try p.parseExpr(); |
| | 2665 | const sentinel = if (p.eatToken(.Colon)) |_| |
| | 2666 | try p.expectNode(parseExpr, .{ |
| | 2667 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 2668 | }) |
| | 2669 | else |
| | 2670 | null; |
| | 2671 | const rbracket = try p.expectToken(.RBracket); |
| | 2672 | |
| | 2673 | const op: Node.PrefixOp.Op = if (expr) |len_expr| |
| | 2674 | .{ |
| | 2675 | .ArrayType = .{ |
| | 2676 | .len_expr = len_expr, |
| | 2677 | .sentinel = sentinel, |
| | 2678 | }, |
| | 2679 | } |
| | 2680 | else |
| | 2681 | .{ |
| | 2682 | .SliceType = Node.PrefixOp.PtrInfo{ |
| | 2683 | .allowzero_token = null, |
| | 2684 | .align_info = null, |
| | 2685 | .const_token = null, |
| | 2686 | .volatile_token = null, |
| | 2687 | .sentinel = sentinel, |
| | 2688 | }, |
| | 2689 | }; |
| | 2690 | |
| | 2691 | const node = try p.arena.allocator.create(Node.PrefixOp); |
| | 2692 | node.* = .{ |
| | 2693 | .op_token = lbracket, |
| | 2694 | .op = op, |
| | 2695 | .rhs = undefined, // set by caller |
| | 2696 | }; |
| | 2697 | return &node.base; |
| | 2698 | } |
| | 2699 | |
| | 2700 | /// PtrTypeStart |
| | 2701 | /// <- ASTERISK |
| | 2702 | /// / ASTERISK2 |
| | 2703 | /// / PTRUNKNOWN |
| | 2704 | /// / PTRC |
| | 2705 | fn parsePtrTypeStart(p: *Parser) !?*Node { |
| | 2706 | if (p.eatToken(.Asterisk)) |asterisk| { |
| | 2707 | const sentinel = if (p.eatToken(.Colon)) |_| |
| | 2708 | try p.expectNode(parseExpr, .{ |
| | 2709 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 2710 | }) |
| | 2711 | else |
| | 2712 | null; |
| | 2713 | const node = try p.arena.allocator.create(Node.PrefixOp); |
| | 2714 | node.* = .{ |
| | 2715 | .op_token = asterisk, |
| | 2716 | .op = .{ .PtrType = .{ .sentinel = sentinel } }, |
| | 2717 | .rhs = undefined, // set by caller |
| | 2718 | }; |
| | 2719 | return &node.base; |
| | 2720 | } |
| | 2721 | |
| | 2722 | if (p.eatToken(.AsteriskAsterisk)) |double_asterisk| { |
| | 2723 | const node = try p.arena.allocator.create(Node.PrefixOp); |
| | 2724 | node.* = .{ |
| | 2725 | .op_token = double_asterisk, |
| | 2726 | .op = .{ .PtrType = .{} }, |
| | 2727 | .rhs = undefined, // set by caller |
| | 2728 | }; |
| | 2729 | |
| | 2730 | // Special case for **, which is its own token |
| | 2731 | const child = try p.arena.allocator.create(Node.PrefixOp); |
| | 2732 | child.* = .{ |
| | 2733 | .op_token = double_asterisk, |
| | 2734 | .op = .{ .PtrType = .{} }, |
| | 2735 | .rhs = undefined, // set by caller |
| | 2736 | }; |
| | 2737 | node.rhs = &child.base; |
| | 2738 | |
| | 2739 | return &node.base; |
| | 2740 | } |
| | 2741 | if (p.eatToken(.LBracket)) |lbracket| { |
| | 2742 | const asterisk = p.eatToken(.Asterisk) orelse { |
| | 2743 | p.putBackToken(lbracket); |
| | 2744 | return null; |
| | 2745 | }; |
| | 2746 | if (p.eatToken(.Identifier)) |ident| { |
| | 2747 | const token_slice = p.source[p.tokens[ident].start..p.tokens[ident].end]; |
| | 2748 | if (!std.mem.eql(u8, token_slice, "c")) { |
| | 2749 | p.putBackToken(ident); |
| | 2750 | } else { |
| | 2751 | _ = try p.expectToken(.RBracket); |
| | 2752 | const node = try p.arena.allocator.create(Node.PrefixOp); |
| | 2753 | node.* = .{ |
| | 2754 | .op_token = lbracket, |
| | 2755 | .op = .{ .PtrType = .{} }, |
| | 2756 | .rhs = undefined, // set by caller |
| | 2757 | }; |
| | 2758 | return &node.base; |
| | 2759 | } |
| | 2760 | } |
| | 2761 | const sentinel = if (p.eatToken(.Colon)) |_| |
| | 2762 | try p.expectNode(parseExpr, .{ |
| | 2763 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 2764 | }) |
| | 2765 | else |
| | 2766 | null; |
| | 2767 | _ = try p.expectToken(.RBracket); |
| | 2768 | const node = try p.arena.allocator.create(Node.PrefixOp); |
| | 2769 | node.* = .{ |
| | 2770 | .op_token = lbracket, |
| | 2771 | .op = .{ .PtrType = .{ .sentinel = sentinel } }, |
| | 2772 | .rhs = undefined, // set by caller |
| | 2773 | }; |
| | 2774 | return &node.base; |
| | 2775 | } |
| | 2776 | return null; |
| | 2777 | } |
| | 2778 | |
| | 2779 | /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE |
| | 2780 | fn parseContainerDeclAuto(p: *Parser) !?*Node { |
| | 2781 | const node = (try p.parseContainerDeclType()) orelse return null; |
| | 2782 | const lbrace = try p.expectToken(.LBrace); |
| | 2783 | const members = try p.parseContainerMembers(false); |
| | 2784 | const rbrace = try p.expectToken(.RBrace); |
| | 2785 | |
| | 2786 | const decl_type = node.cast(Node.ContainerDecl).?; |
| | 2787 | decl_type.fields_and_decls = members; |
| | 2788 | decl_type.lbrace_token = lbrace; |
| | 2789 | decl_type.rbrace_token = rbrace; |
| | 2790 | |
| | 2791 | return node; |
| | 2792 | } |
| | 2793 | |
| | 2794 | /// ContainerDeclType |
| | 2795 | /// <- KEYWORD_struct |
| | 2796 | /// / KEYWORD_enum (LPAREN Expr RPAREN)? |
| | 2797 | /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? |
| | 2798 | fn parseContainerDeclType(p: *Parser) !?*Node { |
| | 2799 | const kind_token = p.nextToken(); |
| | 2800 | |
| | 2801 | const init_arg_expr = switch (kind_token.ptr.id) { |
| | 2802 | .Keyword_struct => Node.ContainerDecl.InitArg{ .None = {} }, |
| | 2803 | .Keyword_enum => blk: { |
| | 2804 | if (p.eatToken(.LParen) != null) { |
| | 2805 | const expr = try p.expectNode(parseExpr, .{ |
| | 2806 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 2807 | }); |
| | 2808 | _ = try p.expectToken(.RParen); |
| | 2809 | break :blk Node.ContainerDecl.InitArg{ .Type = expr }; |
| | 2810 | } |
| | 2811 | break :blk Node.ContainerDecl.InitArg{ .None = {} }; |
| | 2812 | }, |
| | 2813 | .Keyword_union => blk: { |
| | 2814 | if (p.eatToken(.LParen) != null) { |
| | 2815 | if (p.eatToken(.Keyword_enum) != null) { |
| | 2816 | if (p.eatToken(.LParen) != null) { |
| | 2817 | const expr = try p.expectNode(parseExpr, .{ |
| | 2818 | .ExpectedExpr = .{ .token = p.tok_i }, |
| 2536 | }); | 2819 | }); |
| 2537 | continue; | 2820 | _ = try p.expectToken(.RParen); |
| | 2821 | _ = try p.expectToken(.RParen); |
| | 2822 | break :blk Node.ContainerDecl.InitArg{ .Enum = expr }; |
| 2538 | } | 2823 | } |
| 2539 | slice_type.allowzero_token = allowzero_token; | 2824 | _ = try p.expectToken(.RParen); |
| 2540 | continue; | 2825 | break :blk Node.ContainerDecl.InitArg{ .Enum = null }; |
| | 2826 | } |
| | 2827 | const expr = try p.expectNode(parseExpr, .{ |
| | 2828 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 2829 | }); |
| | 2830 | _ = try p.expectToken(.RParen); |
| | 2831 | break :blk Node.ContainerDecl.InitArg{ .Type = expr }; |
| | 2832 | } |
| | 2833 | break :blk Node.ContainerDecl.InitArg{ .None = {} }; |
| | 2834 | }, |
| | 2835 | else => { |
| | 2836 | p.putBackToken(kind_token.index); |
| | 2837 | return null; |
| | 2838 | }, |
| | 2839 | }; |
| | 2840 | |
| | 2841 | const node = try p.arena.allocator.create(Node.ContainerDecl); |
| | 2842 | node.* = .{ |
| | 2843 | .layout_token = null, |
| | 2844 | .kind_token = kind_token.index, |
| | 2845 | .init_arg_expr = init_arg_expr, |
| | 2846 | .fields_and_decls = undefined, // set by caller |
| | 2847 | .lbrace_token = undefined, // set by caller |
| | 2848 | .rbrace_token = undefined, // set by caller |
| | 2849 | }; |
| | 2850 | return &node.base; |
| | 2851 | } |
| | 2852 | |
| | 2853 | /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN |
| | 2854 | fn parseByteAlign(p: *Parser) !?*Node { |
| | 2855 | _ = p.eatToken(.Keyword_align) orelse return null; |
| | 2856 | _ = try p.expectToken(.LParen); |
| | 2857 | const expr = try p.expectNode(parseExpr, .{ |
| | 2858 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 2859 | }); |
| | 2860 | _ = try p.expectToken(.RParen); |
| | 2861 | return expr; |
| | 2862 | } |
| | 2863 | |
| | 2864 | /// IdentifierList <- (IDENTIFIER COMMA)* IDENTIFIER? |
| | 2865 | /// Only ErrorSetDecl parses an IdentifierList |
| | 2866 | fn parseErrorTagList(p: *Parser) !Node.ErrorSetDecl.DeclList { |
| | 2867 | return ListParseFn(Node.ErrorSetDecl.DeclList, parseErrorTag)(p); |
| | 2868 | } |
| | 2869 | |
| | 2870 | /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng? |
| | 2871 | fn parseSwitchProngList(p: *Parser) !Node.Switch.CaseList { |
| | 2872 | return ListParseFn(Node.Switch.CaseList, parseSwitchProng)(p); |
| | 2873 | } |
| | 2874 | |
| | 2875 | /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem? |
| | 2876 | fn parseAsmOutputList(p: *Parser) Error!Node.Asm.OutputList { |
| | 2877 | return ListParseFn(Node.Asm.OutputList, parseAsmOutputItem)(p); |
| | 2878 | } |
| | 2879 | |
| | 2880 | /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem? |
| | 2881 | fn parseAsmInputList(p: *Parser) Error!Node.Asm.InputList { |
| | 2882 | return ListParseFn(Node.Asm.InputList, parseAsmInputItem)(p); |
| | 2883 | } |
| | 2884 | |
| | 2885 | /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl? |
| | 2886 | fn parseParamDeclList(p: *Parser, var_args_token: *?TokenIndex) !Node.FnProto.ParamList { |
| | 2887 | var list = Node.FnProto.ParamList{}; |
| | 2888 | var list_it = &list.first; |
| | 2889 | var last: ?*Node = null; |
| | 2890 | while (try p.parseParamDecl()) |node| { |
| | 2891 | last = node; |
| | 2892 | list_it = try p.llpush(*Node, list_it, node); |
| | 2893 | |
| | 2894 | switch (p.tokens[p.tok_i].id) { |
| | 2895 | .Comma => _ = p.nextToken(), |
| | 2896 | // all possible delimiters |
| | 2897 | .Colon, .RParen, .RBrace, .RBracket => break, |
| | 2898 | else => { |
| | 2899 | // this is likely just a missing comma, |
| | 2900 | // continue parsing this list and give an error |
| | 2901 | try p.errors.append(p.gpa, .{ |
| | 2902 | .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma }, |
| | 2903 | }); |
| | 2904 | }, |
| | 2905 | } |
| | 2906 | } |
| | 2907 | if (last) |node| { |
| | 2908 | const param_type = node.cast(Node.ParamDecl).?.param_type; |
| | 2909 | if (param_type == .var_args) { |
| | 2910 | var_args_token.* = param_type.var_args; |
| | 2911 | } |
| | 2912 | } |
| | 2913 | return list; |
| | 2914 | } |
| | 2915 | |
| | 2916 | const NodeParseFn = fn (p: *Parser) Error!?*Node; |
| | 2917 | |
| | 2918 | fn ListParseFn(comptime L: type, comptime nodeParseFn: var) ParseFn(L) { |
| | 2919 | return struct { |
| | 2920 | pub fn parse(p: *Parser) !L { |
| | 2921 | var list = L{}; |
| | 2922 | var list_it = &list.first; |
| | 2923 | while (try nodeParseFn(p)) |node| { |
| | 2924 | list_it = try p.llpush(L.Node.Data, list_it, node); |
| | 2925 | |
| | 2926 | switch (p.tokens[p.tok_i].id) { |
| | 2927 | .Comma => _ = p.nextToken(), |
| | 2928 | // all possible delimiters |
| | 2929 | .Colon, .RParen, .RBrace, .RBracket => break, |
| | 2930 | else => { |
| | 2931 | // this is likely just a missing comma, |
| | 2932 | // continue parsing this list and give an error |
| | 2933 | try p.errors.append(p.gpa, .{ |
| | 2934 | .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma }, |
| | 2935 | }); |
| | 2936 | }, |
| 2541 | } | 2937 | } |
| 2542 | break; | | |
| 2543 | } | 2938 | } |
| 2544 | }, | 2939 | return list; |
| 2545 | else => unreachable, | 2940 | } |
| 2546 | } | 2941 | }.parse; |
| 2547 | return node; | | |
| 2548 | } | 2942 | } |
| 2549 | | 2943 | |
| 2550 | return null; | 2944 | fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.InfixOp.Op) NodeParseFn { |
| 2551 | } | 2945 | return struct { |
| 2552 | | 2946 | pub fn parse(p: *Parser) Error!?*Node { |
| 2553 | /// SuffixOp | 2947 | const op_token = if (token == .Keyword_and) switch (p.tokens[p.tok_i].id) { |
| 2554 | /// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET | 2948 | .Keyword_and => p.nextToken().index, |
| 2555 | /// / DOT IDENTIFIER | 2949 | .Invalid_ampersands => blk: { |
| 2556 | /// / DOTASTERISK | 2950 | try p.errors.append(p.gpa, .{ |
| 2557 | /// / DOTQUESTIONMARK | 2951 | .InvalidAnd = .{ .token = p.tok_i }, |
| 2558 | fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 2952 | }); |
| 2559 | const OpAndToken = struct { | 2953 | break :blk p.nextToken().index; |
| 2560 | op: Node.SuffixOp.Op, | | |
| 2561 | token: TokenIndex, | | |
| 2562 | }; | | |
| 2563 | const op_and_token: OpAndToken = blk: { | | |
| 2564 | if (eatToken(it, .LBracket)) |_| { | | |
| 2565 | const index_expr = try expectNode(arena, it, tree, parseExpr, .{ | | |
| 2566 | .ExpectedExpr = .{ .token = it.index }, | | |
| 2567 | }); | | |
| 2568 | | | |
| 2569 | if (eatToken(it, .Ellipsis2) != null) { | | |
| 2570 | const end_expr = try parseExpr(arena, it, tree); | | |
| 2571 | const sentinel: ?*ast.Node = if (eatToken(it, .Colon) != null) | | |
| 2572 | try parseExpr(arena, it, tree) | | |
| 2573 | else | | |
| 2574 | null; | | |
| 2575 | break :blk .{ | | |
| 2576 | .op = .{ | | |
| 2577 | .Slice = .{ | | |
| 2578 | .start = index_expr, | | |
| 2579 | .end = end_expr, | | |
| 2580 | .sentinel = sentinel, | | |
| 2581 | }, | | |
| 2582 | }, | 2954 | }, |
| 2583 | .token = try expectToken(it, tree, .RBracket), | 2955 | else => return null, |
| 2584 | }; | 2956 | } else p.eatToken(token) orelse return null; |
| 2585 | } | | |
| 2586 | | | |
| 2587 | break :blk .{ | | |
| 2588 | .op = .{ .ArrayAccess = index_expr }, | | |
| 2589 | .token = try expectToken(it, tree, .RBracket), | | |
| 2590 | }; | | |
| 2591 | } | | |
| 2592 | | | |
| 2593 | if (eatToken(it, .PeriodAsterisk)) |period_asterisk| { | | |
| 2594 | break :blk .{ .op = .Deref, .token = period_asterisk }; | | |
| 2595 | } | | |
| 2596 | | 2957 | |
| 2597 | if (eatToken(it, .Period)) |period| { | 2958 | const node = try p.arena.allocator.create(Node.InfixOp); |
| 2598 | if (try parseIdentifier(arena, it, tree)) |identifier| { | | |
| 2599 | // TODO: It's a bit weird to return an InfixOp from the SuffixOp parser. | | |
| 2600 | // Should there be an ast.Node.SuffixOp.FieldAccess variant? Or should | | |
| 2601 | // this grammar rule be altered? | | |
| 2602 | const node = try arena.create(Node.InfixOp); | | |
| 2603 | node.* = .{ | 2959 | node.* = .{ |
| 2604 | .op_token = period, | 2960 | .op_token = op_token, |
| 2605 | .lhs = undefined, // set by caller | 2961 | .lhs = undefined, // set by caller |
| 2606 | .op = .Period, | 2962 | .op = op, |
| 2607 | .rhs = identifier, | 2963 | .rhs = undefined, // set by caller |
| 2608 | }; | 2964 | }; |
| 2609 | return &node.base; | 2965 | return &node.base; |
| 2610 | } | 2966 | } |
| 2611 | if (eatToken(it, .QuestionMark)) |question_mark| { | 2967 | }.parse; |
| 2612 | break :blk .{ .op = .UnwrapOptional, .token = question_mark }; | 2968 | } |
| 2613 | } | | |
| 2614 | try tree.errors.push(.{ | | |
| 2615 | .ExpectedSuffixOp = .{ .token = it.index }, | | |
| 2616 | }); | | |
| 2617 | return null; | | |
| 2618 | } | | |
| 2619 | | | |
| 2620 | return null; | | |
| 2621 | }; | | |
| 2622 | | | |
| 2623 | const node = try arena.create(Node.SuffixOp); | | |
| 2624 | node.* = .{ | | |
| 2625 | .lhs = undefined, // set by caller | | |
| 2626 | .op = op_and_token.op, | | |
| 2627 | .rtoken = op_and_token.token, | | |
| 2628 | }; | | |
| 2629 | return &node.base; | | |
| 2630 | } | | |
| 2631 | | 2969 | |
| 2632 | /// FnCallArguments <- LPAREN ExprList RPAREN | 2970 | // Helper parsers not included in the grammar |
| 2633 | /// ExprList <- (Expr COMMA)* Expr? | | |
| 2634 | fn parseFnCallArguments(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?AnnotatedParamList { | | |
| 2635 | if (eatToken(it, .LParen) == null) return null; | | |
| 2636 | const list = try ListParseFn(Node.FnProto.ParamList, parseExpr)(arena, it, tree); | | |
| 2637 | const rparen = try expectToken(it, tree, .RParen); | | |
| 2638 | return AnnotatedParamList{ .list = list, .rparen = rparen }; | | |
| 2639 | } | | |
| 2640 | | 2971 | |
| 2641 | const AnnotatedParamList = struct { | 2972 | fn parseBuiltinCall(p: *Parser) !?*Node { |
| 2642 | list: Node.FnProto.ParamList, // NOTE: may also be any other type SegmentedList(*Node, 2) | 2973 | const token = p.eatToken(.Builtin) orelse return null; |
| 2643 | rparen: TokenIndex, | 2974 | const params = (try p.parseFnCallArguments()) orelse { |
| 2644 | }; | 2975 | try p.errors.append(p.gpa, .{ |
| | 2976 | .ExpectedParamList = .{ .token = p.tok_i }, |
| | 2977 | }); |
| 2645 | | 2978 | |
| 2646 | /// ArrayTypeStart <- LBRACKET Expr? RBRACKET | 2979 | // lets pretend this was an identifier so we can continue parsing |
| 2647 | fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 2980 | const node = try p.arena.allocator.create(Node.Identifier); |
| 2648 | const lbracket = eatToken(it, .LBracket) orelse return null; | 2981 | node.* = .{ |
| 2649 | const expr = try parseExpr(arena, it, tree); | 2982 | .token = token, |
| 2650 | const sentinel = if (eatToken(it, .Colon)) |_| | 2983 | }; |
| 2651 | try expectNode(arena, it, tree, parseExpr, .{ | 2984 | return &node.base; |
| 2652 | .ExpectedExpr = .{ .token = it.index }, | | |
| 2653 | }) | | |
| 2654 | else | | |
| 2655 | null; | | |
| 2656 | const rbracket = try expectToken(it, tree, .RBracket); | | |
| 2657 | | | |
| 2658 | const op: Node.PrefixOp.Op = if (expr) |len_expr| | | |
| 2659 | .{ | | |
| 2660 | .ArrayType = .{ | | |
| 2661 | .len_expr = len_expr, | | |
| 2662 | .sentinel = sentinel, | | |
| 2663 | }, | | |
| 2664 | } | | |
| 2665 | else | | |
| 2666 | .{ | | |
| 2667 | .SliceType = Node.PrefixOp.PtrInfo{ | | |
| 2668 | .allowzero_token = null, | | |
| 2669 | .align_info = null, | | |
| 2670 | .const_token = null, | | |
| 2671 | .volatile_token = null, | | |
| 2672 | .sentinel = sentinel, | | |
| 2673 | }, | | |
| 2674 | }; | 2985 | }; |
| 2675 | | 2986 | const node = try p.arena.allocator.create(Node.BuiltinCall); |
| 2676 | const node = try arena.create(Node.PrefixOp); | | |
| 2677 | node.* = .{ | | |
| 2678 | .op_token = lbracket, | | |
| 2679 | .op = op, | | |
| 2680 | .rhs = undefined, // set by caller | | |
| 2681 | }; | | |
| 2682 | return &node.base; | | |
| 2683 | } | | |
| 2684 | | | |
| 2685 | /// PtrTypeStart | | |
| 2686 | /// <- ASTERISK | | |
| 2687 | /// / ASTERISK2 | | |
| 2688 | /// / PTRUNKNOWN | | |
| 2689 | /// / PTRC | | |
| 2690 | fn parsePtrTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2691 | if (eatToken(it, .Asterisk)) |asterisk| { | | |
| 2692 | const sentinel = if (eatToken(it, .Colon)) |_| | | |
| 2693 | try expectNode(arena, it, tree, parseExpr, .{ | | |
| 2694 | .ExpectedExpr = .{ .token = it.index }, | | |
| 2695 | }) | | |
| 2696 | else | | |
| 2697 | null; | | |
| 2698 | const node = try arena.create(Node.PrefixOp); | | |
| 2699 | node.* = .{ | 2987 | node.* = .{ |
| 2700 | .op_token = asterisk, | 2988 | .builtin_token = token, |
| 2701 | .op = .{ .PtrType = .{ .sentinel = sentinel } }, | 2989 | .params = params.list, |
| 2702 | .rhs = undefined, // set by caller | 2990 | .rparen_token = params.rparen, |
| 2703 | }; | 2991 | }; |
| 2704 | return &node.base; | 2992 | return &node.base; |
| 2705 | } | 2993 | } |
| 2706 | | 2994 | |
| 2707 | if (eatToken(it, .AsteriskAsterisk)) |double_asterisk| { | 2995 | fn parseErrorTag(p: *Parser) !?*Node { |
| 2708 | const node = try arena.create(Node.PrefixOp); | 2996 | const doc_comments = try p.parseDocComment(); // no need to rewind on failure |
| | 2997 | const token = p.eatToken(.Identifier) orelse return null; |
| | 2998 | |
| | 2999 | const node = try p.arena.allocator.create(Node.ErrorTag); |
| 2709 | node.* = .{ | 3000 | node.* = .{ |
| 2710 | .op_token = double_asterisk, | 3001 | .doc_comments = doc_comments, |
| 2711 | .op = .{ .PtrType = .{} }, | 3002 | .name_token = token, |
| 2712 | .rhs = undefined, // set by caller | | |
| 2713 | }; | 3003 | }; |
| | 3004 | return &node.base; |
| | 3005 | } |
| 2714 | | 3006 | |
| 2715 | // Special case for **, which is its own token | 3007 | fn parseIdentifier(p: *Parser) !?*Node { |
| 2716 | const child = try arena.create(Node.PrefixOp); | 3008 | const token = p.eatToken(.Identifier) orelse return null; |
| 2717 | child.* = .{ | 3009 | const node = try p.arena.allocator.create(Node.Identifier); |
| 2718 | .op_token = double_asterisk, | 3010 | node.* = .{ |
| 2719 | .op = .{ .PtrType = .{} }, | 3011 | .token = token, |
| 2720 | .rhs = undefined, // set by caller | | |
| 2721 | }; | 3012 | }; |
| 2722 | node.rhs = &child.base; | | |
| 2723 | | | |
| 2724 | return &node.base; | 3013 | return &node.base; |
| 2725 | } | 3014 | } |
| 2726 | if (eatToken(it, .LBracket)) |lbracket| { | 3015 | |
| 2727 | const asterisk = eatToken(it, .Asterisk) orelse { | 3016 | fn parseVarType(p: *Parser) !?*Node { |
| 2728 | putBackToken(it, lbracket); | 3017 | const token = p.eatToken(.Keyword_var) orelse return null; |
| 2729 | return null; | 3018 | const node = try p.arena.allocator.create(Node.VarType); |
| 2730 | }; | | |
| 2731 | if (eatToken(it, .Identifier)) |ident| { | | |
| 2732 | if (!std.mem.eql(u8, tree.tokenSlice(ident), "c")) { | | |
| 2733 | putBackToken(it, ident); | | |
| 2734 | } else { | | |
| 2735 | _ = try expectToken(it, tree, .RBracket); | | |
| 2736 | const node = try arena.create(Node.PrefixOp); | | |
| 2737 | node.* = .{ | | |
| 2738 | .op_token = lbracket, | | |
| 2739 | .op = .{ .PtrType = .{} }, | | |
| 2740 | .rhs = undefined, // set by caller | | |
| 2741 | }; | | |
| 2742 | return &node.base; | | |
| 2743 | } | | |
| 2744 | } | | |
| 2745 | const sentinel = if (eatToken(it, .Colon)) |_| | | |
| 2746 | try expectNode(arena, it, tree, parseExpr, .{ | | |
| 2747 | .ExpectedExpr = .{ .token = it.index }, | | |
| 2748 | }) | | |
| 2749 | else | | |
| 2750 | null; | | |
| 2751 | _ = try expectToken(it, tree, .RBracket); | | |
| 2752 | const node = try arena.create(Node.PrefixOp); | | |
| 2753 | node.* = .{ | 3019 | node.* = .{ |
| 2754 | .op_token = lbracket, | 3020 | .token = token, |
| 2755 | .op = .{ .PtrType = .{ .sentinel = sentinel } }, | | |
| 2756 | .rhs = undefined, // set by caller | | |
| 2757 | }; | 3021 | }; |
| 2758 | return &node.base; | 3022 | return &node.base; |
| 2759 | } | 3023 | } |
| 2760 | return null; | | |
| 2761 | } | | |
| 2762 | | | |
| 2763 | /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE | | |
| 2764 | fn parseContainerDeclAuto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2765 | const node = (try parseContainerDeclType(arena, it, tree)) orelse return null; | | |
| 2766 | const lbrace = try expectToken(it, tree, .LBrace); | | |
| 2767 | const members = try parseContainerMembers(arena, it, tree, false); | | |
| 2768 | const rbrace = try expectToken(it, tree, .RBrace); | | |
| 2769 | | | |
| 2770 | const decl_type = node.cast(Node.ContainerDecl).?; | | |
| 2771 | decl_type.fields_and_decls = members; | | |
| 2772 | decl_type.lbrace_token = lbrace; | | |
| 2773 | decl_type.rbrace_token = rbrace; | | |
| 2774 | | | |
| 2775 | return node; | | |
| 2776 | } | | |
| 2777 | | | |
| 2778 | /// ContainerDeclType | | |
| 2779 | /// <- KEYWORD_struct | | |
| 2780 | /// / KEYWORD_enum (LPAREN Expr RPAREN)? | | |
| 2781 | /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? | | |
| 2782 | fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2783 | const kind_token = nextToken(it); | | |
| 2784 | | | |
| 2785 | const init_arg_expr = switch (kind_token.ptr.id) { | | |
| 2786 | .Keyword_struct => Node.ContainerDecl.InitArg{ .None = {} }, | | |
| 2787 | .Keyword_enum => blk: { | | |
| 2788 | if (eatToken(it, .LParen) != null) { | | |
| 2789 | const expr = try expectNode(arena, it, tree, parseExpr, .{ | | |
| 2790 | .ExpectedExpr = .{ .token = it.index }, | | |
| 2791 | }); | | |
| 2792 | _ = try expectToken(it, tree, .RParen); | | |
| 2793 | break :blk Node.ContainerDecl.InitArg{ .Type = expr }; | | |
| 2794 | } | | |
| 2795 | break :blk Node.ContainerDecl.InitArg{ .None = {} }; | | |
| 2796 | }, | | |
| 2797 | .Keyword_union => blk: { | | |
| 2798 | if (eatToken(it, .LParen) != null) { | | |
| 2799 | if (eatToken(it, .Keyword_enum) != null) { | | |
| 2800 | if (eatToken(it, .LParen) != null) { | | |
| 2801 | const expr = try expectNode(arena, it, tree, parseExpr, .{ | | |
| 2802 | .ExpectedExpr = .{ .token = it.index }, | | |
| 2803 | }); | | |
| 2804 | _ = try expectToken(it, tree, .RParen); | | |
| 2805 | _ = try expectToken(it, tree, .RParen); | | |
| 2806 | break :blk Node.ContainerDecl.InitArg{ .Enum = expr }; | | |
| 2807 | } | | |
| 2808 | _ = try expectToken(it, tree, .RParen); | | |
| 2809 | break :blk Node.ContainerDecl.InitArg{ .Enum = null }; | | |
| 2810 | } | | |
| 2811 | const expr = try expectNode(arena, it, tree, parseExpr, .{ | | |
| 2812 | .ExpectedExpr = .{ .token = it.index }, | | |
| 2813 | }); | | |
| 2814 | _ = try expectToken(it, tree, .RParen); | | |
| 2815 | break :blk Node.ContainerDecl.InitArg{ .Type = expr }; | | |
| 2816 | } | | |
| 2817 | break :blk Node.ContainerDecl.InitArg{ .None = {} }; | | |
| 2818 | }, | | |
| 2819 | else => { | | |
| 2820 | putBackToken(it, kind_token.index); | | |
| 2821 | return null; | | |
| 2822 | }, | | |
| 2823 | }; | | |
| 2824 | | | |
| 2825 | const node = try arena.create(Node.ContainerDecl); | | |
| 2826 | node.* = .{ | | |
| 2827 | .layout_token = null, | | |
| 2828 | .kind_token = kind_token.index, | | |
| 2829 | .init_arg_expr = init_arg_expr, | | |
| 2830 | .fields_and_decls = undefined, // set by caller | | |
| 2831 | .lbrace_token = undefined, // set by caller | | |
| 2832 | .rbrace_token = undefined, // set by caller | | |
| 2833 | }; | | |
| 2834 | return &node.base; | | |
| 2835 | } | | |
| 2836 | | | |
| 2837 | /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN | | |
| 2838 | fn parseByteAlign(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2839 | _ = eatToken(it, .Keyword_align) orelse return null; | | |
| 2840 | _ = try expectToken(it, tree, .LParen); | | |
| 2841 | const expr = try expectNode(arena, it, tree, parseExpr, .{ | | |
| 2842 | .ExpectedExpr = .{ .token = it.index }, | | |
| 2843 | }); | | |
| 2844 | _ = try expectToken(it, tree, .RParen); | | |
| 2845 | return expr; | | |
| 2846 | } | | |
| 2847 | | 3024 | |
| 2848 | /// IdentifierList <- (IDENTIFIER COMMA)* IDENTIFIER? | 3025 | fn createLiteral(p: *Parser, comptime T: type, token: TokenIndex) !*Node { |
| 2849 | /// Only ErrorSetDecl parses an IdentifierList | 3026 | const result = try p.arena.allocator.create(T); |
| 2850 | fn parseErrorTagList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !Node.ErrorSetDecl.DeclList { | 3027 | result.* = T{ |
| 2851 | return try ListParseFn(Node.ErrorSetDecl.DeclList, parseErrorTag)(arena, it, tree); | 3028 | .base = Node{ .id = Node.typeToId(T) }, |
| 2852 | } | 3029 | .token = token, |
| 2853 | | 3030 | }; |
| 2854 | /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng? | 3031 | return &result.base; |
| 2855 | fn parseSwitchProngList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !Node.Switch.CaseList { | 3032 | } |
| 2856 | return try ListParseFn(Node.Switch.CaseList, parseSwitchProng)(arena, it, tree); | | |
| 2857 | } | | |
| 2858 | | | |
| 2859 | /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem? | | |
| 2860 | fn parseAsmOutputList(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!Node.Asm.OutputList { | | |
| 2861 | return try ListParseFn(Node.Asm.OutputList, parseAsmOutputItem)(arena, it, tree); | | |
| 2862 | } | | |
| 2863 | | | |
| 2864 | /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem? | | |
| 2865 | fn parseAsmInputList(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!Node.Asm.InputList { | | |
| 2866 | return try ListParseFn(Node.Asm.InputList, parseAsmInputItem)(arena, it, tree); | | |
| 2867 | } | | |
| 2868 | | | |
| 2869 | /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl? | | |
| 2870 | fn parseParamDeclList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !Node.FnProto.ParamList { | | |
| 2871 | return try ListParseFn(Node.FnProto.ParamList, parseParamDecl)(arena, it, tree); | | |
| 2872 | } | | |
| 2873 | | | |
| 2874 | fn ParseFn(comptime T: type) type { | | |
| 2875 | return fn (*Allocator, *TokenIterator, *Tree) Error!T; | | |
| 2876 | } | | |
| 2877 | | 3033 | |
| 2878 | const NodeParseFn = fn (*Allocator, *TokenIterator, *Tree) Error!?*Node; | 3034 | fn parseStringLiteralSingle(p: *Parser) !?*Node { |
| 2879 | | 3035 | if (p.eatToken(.StringLiteral)) |token| { |
| 2880 | fn ListParseFn(comptime L: type, comptime nodeParseFn: var) ParseFn(L) { | 3036 | const node = try p.arena.allocator.create(Node.StringLiteral); |
| 2881 | return struct { | 3037 | node.* = .{ |
| 2882 | pub fn parse(arena: *Allocator, it: *TokenIterator, tree: *Tree) !L { | 3038 | .token = token, |
| 2883 | var list = L.init(arena); | 3039 | }; |
| 2884 | while (try nodeParseFn(arena, it, tree)) |node| { | 3040 | return &node.base; |
| 2885 | try list.push(node); | | |
| 2886 | | | |
| 2887 | switch (it.peek().?.id) { | | |
| 2888 | .Comma => _ = nextToken(it), | | |
| 2889 | // all possible delimiters | | |
| 2890 | .Colon, .RParen, .RBrace, .RBracket => break, | | |
| 2891 | else => { | | |
| 2892 | // this is likely just a missing comma, | | |
| 2893 | // continue parsing this list and give an error | | |
| 2894 | try tree.errors.push(.{ | | |
| 2895 | .ExpectedToken = .{ .token = it.index, .expected_id = .Comma }, | | |
| 2896 | }); | | |
| 2897 | }, | | |
| 2898 | } | | |
| 2899 | } | | |
| 2900 | return list; | | |
| 2901 | } | 3041 | } |
| 2902 | }.parse; | 3042 | return null; |
| 2903 | } | 3043 | } |
| 2904 | | 3044 | |
| 2905 | fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.InfixOp.Op) NodeParseFn { | 3045 | // string literal or multiline string literal |
| 2906 | return struct { | 3046 | fn parseStringLiteral(p: *Parser) !?*Node { |
| 2907 | pub fn parse(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*Node { | 3047 | if (try p.parseStringLiteralSingle()) |node| return node; |
| 2908 | const op_token = if (token == .Keyword_and) switch (it.peek().?.id) { | | |
| 2909 | .Keyword_and => nextToken(it).index, | | |
| 2910 | .Invalid_ampersands => blk: { | | |
| 2911 | try tree.errors.push(.{ | | |
| 2912 | .InvalidAnd = .{ .token = it.index }, | | |
| 2913 | }); | | |
| 2914 | break :blk nextToken(it).index; | | |
| 2915 | }, | | |
| 2916 | else => return null, | | |
| 2917 | } else eatToken(it, token) orelse return null; | | |
| 2918 | | 3048 | |
| 2919 | const node = try arena.create(Node.InfixOp); | 3049 | if (p.eatToken(.MultilineStringLiteralLine)) |first_line| { |
| | 3050 | const node = try p.arena.allocator.create(Node.MultilineStringLiteral); |
| 2920 | node.* = .{ | 3051 | node.* = .{ |
| 2921 | .op_token = op_token, | 3052 | .lines = Node.MultilineStringLiteral.LineList{}, |
| 2922 | .lhs = undefined, // set by caller | | |
| 2923 | .op = op, | | |
| 2924 | .rhs = undefined, // set by caller | | |
| 2925 | }; | 3053 | }; |
| | 3054 | var lines_it = &node.lines.first; |
| | 3055 | lines_it = try p.llpush(TokenIndex, lines_it, first_line); |
| | 3056 | while (p.eatToken(.MultilineStringLiteralLine)) |line| |
| | 3057 | lines_it = try p.llpush(TokenIndex, lines_it, line); |
| | 3058 | |
| 2926 | return &node.base; | 3059 | return &node.base; |
| 2927 | } | 3060 | } |
| 2928 | }.parse; | | |
| 2929 | } | | |
| 2930 | | 3061 | |
| 2931 | // Helper parsers not included in the grammar | 3062 | return null; |
| 2932 | | 3063 | } |
| 2933 | fn parseBuiltinCall(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2934 | const token = eatToken(it, .Builtin) orelse return null; | | |
| 2935 | const params = (try parseFnCallArguments(arena, it, tree)) orelse { | | |
| 2936 | try tree.errors.push(.{ | | |
| 2937 | .ExpectedParamList = .{ .token = it.index }, | | |
| 2938 | }); | | |
| 2939 | | 3064 | |
| 2940 | // lets pretend this was an identifier so we can continue parsing | 3065 | fn parseIntegerLiteral(p: *Parser) !?*Node { |
| 2941 | const node = try arena.create(Node.Identifier); | 3066 | const token = p.eatToken(.IntegerLiteral) orelse return null; |
| | 3067 | const node = try p.arena.allocator.create(Node.IntegerLiteral); |
| 2942 | node.* = .{ | 3068 | node.* = .{ |
| 2943 | .token = token, | 3069 | .token = token, |
| 2944 | }; | 3070 | }; |
| 2945 | return &node.base; | 3071 | return &node.base; |
| 2946 | }; | 3072 | } |
| 2947 | const node = try arena.create(Node.BuiltinCall); | | |
| 2948 | node.* = .{ | | |
| 2949 | .builtin_token = token, | | |
| 2950 | .params = params.list, | | |
| 2951 | .rparen_token = params.rparen, | | |
| 2952 | }; | | |
| 2953 | return &node.base; | | |
| 2954 | } | | |
| 2955 | | | |
| 2956 | fn parseErrorTag(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2957 | const doc_comments = try parseDocComment(arena, it, tree); // no need to rewind on failure | | |
| 2958 | const token = eatToken(it, .Identifier) orelse return null; | | |
| 2959 | | | |
| 2960 | const node = try arena.create(Node.ErrorTag); | | |
| 2961 | node.* = .{ | | |
| 2962 | .doc_comments = doc_comments, | | |
| 2963 | .name_token = token, | | |
| 2964 | }; | | |
| 2965 | return &node.base; | | |
| 2966 | } | | |
| 2967 | | | |
| 2968 | fn parseIdentifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2969 | const token = eatToken(it, .Identifier) orelse return null; | | |
| 2970 | const node = try arena.create(Node.Identifier); | | |
| 2971 | node.* = .{ | | |
| 2972 | .token = token, | | |
| 2973 | }; | | |
| 2974 | return &node.base; | | |
| 2975 | } | | |
| 2976 | | | |
| 2977 | fn parseVarType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | | |
| 2978 | const token = eatToken(it, .Keyword_var) orelse return null; | | |
| 2979 | const node = try arena.create(Node.VarType); | | |
| 2980 | node.* = .{ | | |
| 2981 | .token = token, | | |
| 2982 | }; | | |
| 2983 | return &node.base; | | |
| 2984 | } | | |
| 2985 | | | |
| 2986 | fn createLiteral(arena: *Allocator, comptime T: type, token: TokenIndex) !*Node { | | |
| 2987 | const result = try arena.create(T); | | |
| 2988 | result.* = T{ | | |
| 2989 | .base = Node{ .id = Node.typeToId(T) }, | | |
| 2990 | .token = token, | | |
| 2991 | }; | | |
| 2992 | return &result.base; | | |
| 2993 | } | | |
| 2994 | | 3073 | |
| 2995 | fn parseStringLiteralSingle(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 3074 | fn parseFloatLiteral(p: *Parser) !?*Node { |
| 2996 | if (eatToken(it, .StringLiteral)) |token| { | 3075 | const token = p.eatToken(.FloatLiteral) orelse return null; |
| 2997 | const node = try arena.create(Node.StringLiteral); | 3076 | const node = try p.arena.allocator.create(Node.FloatLiteral); |
| 2998 | node.* = .{ | 3077 | node.* = .{ |
| 2999 | .token = token, | 3078 | .token = token, |
| 3000 | }; | 3079 | }; |
| 3001 | return &node.base; | 3080 | return &node.base; |
| 3002 | } | 3081 | } |
| 3003 | return null; | | |
| 3004 | } | | |
| 3005 | | 3082 | |
| 3006 | // string literal or multiline string literal | 3083 | fn parseTry(p: *Parser) !?*Node { |
| 3007 | fn parseStringLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 3084 | const token = p.eatToken(.Keyword_try) orelse return null; |
| 3008 | if (try parseStringLiteralSingle(arena, it, tree)) |node| return node; | 3085 | const node = try p.arena.allocator.create(Node.PrefixOp); |
| 3009 | | | |
| 3010 | if (eatToken(it, .MultilineStringLiteralLine)) |first_line| { | | |
| 3011 | const node = try arena.create(Node.MultilineStringLiteral); | | |
| 3012 | node.* = .{ | 3086 | node.* = .{ |
| 3013 | .lines = Node.MultilineStringLiteral.LineList.init(arena), | 3087 | .op_token = token, |
| | 3088 | .op = .Try, |
| | 3089 | .rhs = undefined, // set by caller |
| 3014 | }; | 3090 | }; |
| 3015 | try node.lines.push(first_line); | 3091 | return &node.base; |
| 3016 | while (eatToken(it, .MultilineStringLiteralLine)) |line| | 3092 | } |
| 3017 | try node.lines.push(line); | | |
| 3018 | | 3093 | |
| | 3094 | fn parseUse(p: *Parser) !?*Node { |
| | 3095 | const token = p.eatToken(.Keyword_usingnamespace) orelse return null; |
| | 3096 | const node = try p.arena.allocator.create(Node.Use); |
| | 3097 | node.* = .{ |
| | 3098 | .doc_comments = null, |
| | 3099 | .visib_token = null, |
| | 3100 | .use_token = token, |
| | 3101 | .expr = try p.expectNode(parseExpr, .{ |
| | 3102 | .ExpectedExpr = .{ .token = p.tok_i }, |
| | 3103 | }), |
| | 3104 | .semicolon_token = try p.expectToken(.Semicolon), |
| | 3105 | }; |
| 3019 | return &node.base; | 3106 | return &node.base; |
| 3020 | } | 3107 | } |
| 3021 | | 3108 | |
| 3022 | return null; | 3109 | /// IfPrefix Body (KEYWORD_else Payload? Body)? |
| 3023 | } | 3110 | fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !?*Node { |
| | 3111 | const node = (try p.parseIfPrefix()) orelse return null; |
| | 3112 | const if_prefix = node.cast(Node.If).?; |
| 3024 | | 3113 | |
| 3025 | fn parseIntegerLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 3114 | if_prefix.body = try p.expectNode(bodyParseFn, .{ |
| 3026 | const token = eatToken(it, .IntegerLiteral) orelse return null; | 3115 | .InvalidToken = .{ .token = p.tok_i }, |
| 3027 | const node = try arena.create(Node.IntegerLiteral); | 3116 | }); |
| 3028 | node.* = .{ | | |
| 3029 | .token = token, | | |
| 3030 | }; | | |
| 3031 | return &node.base; | | |
| 3032 | } | | |
| 3033 | | 3117 | |
| 3034 | fn parseFloatLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 3118 | const else_token = p.eatToken(.Keyword_else) orelse return node; |
| 3035 | const token = eatToken(it, .FloatLiteral) orelse return null; | 3119 | const payload = try p.parsePayload(); |
| 3036 | const node = try arena.create(Node.FloatLiteral); | 3120 | const else_expr = try p.expectNode(bodyParseFn, .{ |
| 3037 | node.* = .{ | 3121 | .InvalidToken = .{ .token = p.tok_i }, |
| 3038 | .token = token, | 3122 | }); |
| 3039 | }; | 3123 | const else_node = try p.arena.allocator.create(Node.Else); |
| 3040 | return &node.base; | 3124 | else_node.* = .{ |
| 3041 | } | 3125 | .else_token = else_token, |
| | 3126 | .payload = payload, |
| | 3127 | .body = else_expr, |
| | 3128 | }; |
| | 3129 | if_prefix.@"else" = else_node; |
| 3042 | | 3130 | |
| 3043 | fn parseTry(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 3131 | return node; |
| 3044 | const token = eatToken(it, .Keyword_try) orelse return null; | 3132 | } |
| 3045 | const node = try arena.create(Node.PrefixOp); | | |
| 3046 | node.* = .{ | | |
| 3047 | .op_token = token, | | |
| 3048 | .op = .Try, | | |
| 3049 | .rhs = undefined, // set by caller | | |
| 3050 | }; | | |
| 3051 | return &node.base; | | |
| 3052 | } | | |
| 3053 | | 3133 | |
| 3054 | fn parseUse(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { | 3134 | /// Eat a multiline doc comment |
| 3055 | const token = eatToken(it, .Keyword_usingnamespace) orelse return null; | 3135 | fn parseDocComment(p: *Parser) !?*Node.DocComment { |
| 3056 | const node = try arena.create(Node.Use); | 3136 | var lines = Node.DocComment.LineList{}; |
| 3057 | node.* = .{ | 3137 | var lines_it = &lines.first; |
| 3058 | .doc_comments = null, | | |
| 3059 | .visib_token = null, | | |
| 3060 | .use_token = token, | | |
| 3061 | .expr = try expectNode(arena, it, tree, parseExpr, .{ | | |
| 3062 | .ExpectedExpr = .{ .token = it.index }, | | |
| 3063 | }), | | |
| 3064 | .semicolon_token = try expectToken(it, tree, .Semicolon), | | |
| 3065 | }; | | |
| 3066 | return &node.base; | | |
| 3067 | } | | |
| 3068 | | 3138 | |
| 3069 | /// IfPrefix Body (KEYWORD_else Payload? Body)? | 3139 | while (p.eatToken(.DocComment)) |line| { |
| 3070 | fn parseIf(arena: *Allocator, it: *TokenIterator, tree: *Tree, bodyParseFn: NodeParseFn) !?*Node { | 3140 | lines_it = try p.llpush(TokenIndex, lines_it, line); |
| 3071 | const node = (try parseIfPrefix(arena, it, tree)) orelse return null; | 3141 | } |
| 3072 | const if_prefix = node.cast(Node.If).?; | | |
| 3073 | | | |
| 3074 | if_prefix.body = try expectNode(arena, it, tree, bodyParseFn, .{ | | |
| 3075 | .InvalidToken = .{ .token = it.index }, | | |
| 3076 | }); | | |
| 3077 | | | |
| 3078 | const else_token = eatToken(it, .Keyword_else) orelse return node; | | |
| 3079 | const payload = try parsePayload(arena, it, tree); | | |
| 3080 | const else_expr = try expectNode(arena, it, tree, bodyParseFn, .{ | | |
| 3081 | .InvalidToken = .{ .token = it.index }, | | |
| 3082 | }); | | |
| 3083 | const else_node = try arena.create(Node.Else); | | |
| 3084 | else_node.* = .{ | | |
| 3085 | .else_token = else_token, | | |
| 3086 | .payload = payload, | | |
| 3087 | .body = else_expr, | | |
| 3088 | }; | | |
| 3089 | if_prefix.@"else" = else_node; | | |
| 3090 | | 3142 | |
| 3091 | return node; | 3143 | if (lines.first == null) return null; |
| 3092 | } | | |
| 3093 | | 3144 | |
| 3094 | /// Eat a multiline doc comment | 3145 | const node = try p.arena.allocator.create(Node.DocComment); |
| 3095 | fn parseDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.DocComment { | 3146 | node.* = .{ |
| 3096 | var lines = Node.DocComment.LineList.init(arena); | 3147 | .lines = lines, |
| 3097 | while (eatToken(it, .DocComment)) |line| { | 3148 | }; |
| 3098 | try lines.push(line); | 3149 | return node; |
| 3099 | } | 3150 | } |
| 3100 | | 3151 | |
| 3101 | if (lines.len == 0) return null; | 3152 | fn tokensOnSameLine(p: *Parser, token1: TokenIndex, token2: TokenIndex) bool { |
| | 3153 | return std.mem.indexOfScalar(u8, p.source[p.tokens[token1].end..p.tokens[token2].start], '\n') == null; |
| | 3154 | } |
| 3102 | | 3155 | |
| 3103 | const node = try arena.create(Node.DocComment); | 3156 | /// Eat a single-line doc comment on the same line as another node |
| 3104 | node.* = .{ | 3157 | fn parseAppendedDocComment(p: *Parser, after_token: TokenIndex) !?*Node.DocComment { |
| 3105 | .lines = lines, | 3158 | const comment_token = p.eatToken(.DocComment) orelse return null; |
| 3106 | }; | 3159 | if (p.tokensOnSameLine(after_token, comment_token)) { |
| 3107 | return node; | 3160 | var lines = Node.DocComment.LineList{}; |
| 3108 | } | 3161 | _ = try p.llpush(TokenIndex, &lines.first, comment_token); |
| 3109 | | 3162 | |
| 3110 | /// Eat a single-line doc comment on the same line as another node | 3163 | const node = try p.arena.allocator.create(Node.DocComment); |
| 3111 | fn parseAppendedDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree, after_token: TokenIndex) !?*Node.DocComment { | 3164 | node.* = .{ .lines = lines }; |
| 3112 | const comment_token = eatToken(it, .DocComment) orelse return null; | 3165 | return node; |
| 3113 | if (tree.tokensOnSameLine(after_token, comment_token)) { | 3166 | } |
| 3114 | const node = try arena.create(Node.DocComment); | 3167 | p.putBackToken(comment_token); |
| 3115 | node.* = .{ | 3168 | return null; |
| 3116 | .lines = Node.DocComment.LineList.init(arena), | | |
| 3117 | }; | | |
| 3118 | try node.lines.push(comment_token); | | |
| 3119 | return node; | | |
| 3120 | } | 3169 | } |
| 3121 | putBackToken(it, comment_token); | | |
| 3122 | return null; | | |
| 3123 | } | | |
| 3124 | | 3170 | |
| 3125 | /// Op* Child | 3171 | /// Op* Child |
| 3126 | fn parsePrefixOpExpr( | 3172 | fn parsePrefixOpExpr(p: *Parser, opParseFn: NodeParseFn, childParseFn: NodeParseFn) Error!?*Node { |
| 3127 | arena: *Allocator, | 3173 | if (try opParseFn(p)) |first_op| { |
| 3128 | it: *TokenIterator, | 3174 | var rightmost_op = first_op; |
| 3129 | tree: *Tree, | 3175 | while (true) { |
| 3130 | opParseFn: NodeParseFn, | 3176 | switch (rightmost_op.id) { |
| 3131 | childParseFn: NodeParseFn, | 3177 | .PrefixOp => { |
| 3132 | ) Error!?*Node { | 3178 | var prefix_op = rightmost_op.cast(Node.PrefixOp).?; |
| 3133 | if (try opParseFn(arena, it, tree)) |first_op| { | 3179 | // If the token encountered was **, there will be two nodes |
| 3134 | var rightmost_op = first_op; | 3180 | if (p.tokens[prefix_op.op_token].id == .AsteriskAsterisk) { |
| 3135 | while (true) { | 3181 | rightmost_op = prefix_op.rhs; |
| | 3182 | prefix_op = rightmost_op.cast(Node.PrefixOp).?; |
| | 3183 | } |
| | 3184 | if (try opParseFn(p)) |rhs| { |
| | 3185 | prefix_op.rhs = rhs; |
| | 3186 | rightmost_op = rhs; |
| | 3187 | } else break; |
| | 3188 | }, |
| | 3189 | .AnyFrameType => { |
| | 3190 | const prom = rightmost_op.cast(Node.AnyFrameType).?; |
| | 3191 | if (try opParseFn(p)) |rhs| { |
| | 3192 | prom.result.?.return_type = rhs; |
| | 3193 | rightmost_op = rhs; |
| | 3194 | } else break; |
| | 3195 | }, |
| | 3196 | else => unreachable, |
| | 3197 | } |
| | 3198 | } |
| | 3199 | |
| | 3200 | // If any prefix op existed, a child node on the RHS is required |
| 3136 | switch (rightmost_op.id) { | 3201 | switch (rightmost_op.id) { |
| 3137 | .PrefixOp => { | 3202 | .PrefixOp => { |
| 3138 | var prefix_op = rightmost_op.cast(Node.PrefixOp).?; | 3203 | const prefix_op = rightmost_op.cast(Node.PrefixOp).?; |
| 3139 | // If the token encountered was **, there will be two nodes | 3204 | prefix_op.rhs = try p.expectNode(childParseFn, .{ |
| 3140 | if (tree.tokens.at(prefix_op.op_token).id == .AsteriskAsterisk) { | 3205 | .InvalidToken = .{ .token = p.tok_i }, |
| 3141 | rightmost_op = prefix_op.rhs; | 3206 | }); |
| 3142 | prefix_op = rightmost_op.cast(Node.PrefixOp).?; | | |
| 3143 | } | | |
| 3144 | if (try opParseFn(arena, it, tree)) |rhs| { | | |
| 3145 | prefix_op.rhs = rhs; | | |
| 3146 | rightmost_op = rhs; | | |
| 3147 | } else break; | | |
| 3148 | }, | 3207 | }, |
| 3149 | .AnyFrameType => { | 3208 | .AnyFrameType => { |
| 3150 | const prom = rightmost_op.cast(Node.AnyFrameType).?; | 3209 | const prom = rightmost_op.cast(Node.AnyFrameType).?; |
| 3151 | if (try opParseFn(arena, it, tree)) |rhs| { | 3210 | prom.result.?.return_type = try p.expectNode(childParseFn, .{ |
| 3152 | prom.result.?.return_type = rhs; | 3211 | .InvalidToken = .{ .token = p.tok_i }, |
| 3153 | rightmost_op = rhs; | 3212 | }); |
| 3154 | } else break; | | |
| 3155 | }, | 3213 | }, |
| 3156 | else => unreachable, | 3214 | else => unreachable, |
| 3157 | } | 3215 | } |
| 3158 | } | | |
| 3159 | | 3216 | |
| 3160 | // If any prefix op existed, a child node on the RHS is required | 3217 | return first_op; |
| 3161 | switch (rightmost_op.id) { | | |
| 3162 | .PrefixOp => { | | |
| 3163 | const prefix_op = rightmost_op.cast(Node.PrefixOp).?; | | |
| 3164 | prefix_op.rhs = try expectNode(arena, it, tree, childParseFn, .{ | | |
| 3165 | .InvalidToken = .{ .token = it.index }, | | |
| 3166 | }); | | |
| 3167 | }, | | |
| 3168 | .AnyFrameType => { | | |
| 3169 | const prom = rightmost_op.cast(Node.AnyFrameType).?; | | |
| 3170 | prom.result.?.return_type = try expectNode(arena, it, tree, childParseFn, .{ | | |
| 3171 | .InvalidToken = .{ .token = it.index }, | | |
| 3172 | }); | | |
| 3173 | }, | | |
| 3174 | else => unreachable, | | |
| 3175 | } | 3218 | } |
| 3176 | | 3219 | |
| 3177 | return first_op; | 3220 | // Otherwise, the child node is optional |
| | 3221 | return childParseFn(p); |
| 3178 | } | 3222 | } |
| 3179 | | 3223 | |
| 3180 | // Otherwise, the child node is optional | 3224 | /// Child (Op Child)* |
| 3181 | return try childParseFn(arena, it, tree); | 3225 | /// Child (Op Child)? |
| 3182 | } | 3226 | fn parseBinOpExpr( |
| | 3227 | p: *Parser, |
| | 3228 | opParseFn: NodeParseFn, |
| | 3229 | childParseFn: NodeParseFn, |
| | 3230 | chain: enum { |
| | 3231 | Once, |
| | 3232 | Infinitely, |
| | 3233 | }, |
| | 3234 | ) Error!?*Node { |
| | 3235 | var res = (try childParseFn(p)) orelse return null; |
| 3183 | | 3236 | |
| 3184 | /// Child (Op Child)* | 3237 | while (try opParseFn(p)) |node| { |
| 3185 | /// Child (Op Child)? | 3238 | const right = try p.expectNode(childParseFn, .{ |
| 3186 | fn parseBinOpExpr( | 3239 | .InvalidToken = .{ .token = p.tok_i }, |
| 3187 | arena: *Allocator, | 3240 | }); |
| 3188 | it: *TokenIterator, | 3241 | const left = res; |
| 3189 | tree: *Tree, | 3242 | res = node; |
| 3190 | opParseFn: NodeParseFn, | | |
| 3191 | childParseFn: NodeParseFn, | | |
| 3192 | chain: enum { | | |
| 3193 | Once, | | |
| 3194 | Infinitely, | | |
| 3195 | }, | | |
| 3196 | ) Error!?*Node { | | |
| 3197 | var res = (try childParseFn(arena, it, tree)) orelse return null; | | |
| 3198 | | | |
| 3199 | while (try opParseFn(arena, it, tree)) |node| { | | |
| 3200 | const right = try expectNode(arena, it, tree, childParseFn, .{ | | |
| 3201 | .InvalidToken = .{ .token = it.index }, | | |
| 3202 | }); | | |
| 3203 | const left = res; | | |
| 3204 | res = node; | | |
| 3205 | | 3243 | |
| 3206 | const op = node.cast(Node.InfixOp).?; | 3244 | const op = node.cast(Node.InfixOp).?; |
| 3207 | op.*.lhs = left; | 3245 | op.*.lhs = left; |
| 3208 | op.*.rhs = right; | 3246 | op.*.rhs = right; |
| 3209 | | 3247 | |
| 3210 | switch (chain) { | 3248 | switch (chain) { |
| 3211 | .Once => break, | 3249 | .Once => break, |
| 3212 | .Infinitely => continue, | 3250 | .Infinitely => continue, |
| | 3251 | } |
| 3213 | } | 3252 | } |
| | 3253 | |
| | 3254 | return res; |
| 3214 | } | 3255 | } |
| 3215 | | 3256 | |
| 3216 | return res; | 3257 | fn createInfixOp(p: *Parser, index: TokenIndex, op: Node.InfixOp.Op) !*Node { |
| 3217 | } | 3258 | const node = try p.arena.allocator.create(Node.InfixOp); |
| | 3259 | node.* = .{ |
| | 3260 | .op_token = index, |
| | 3261 | .lhs = undefined, // set by caller |
| | 3262 | .op = op, |
| | 3263 | .rhs = undefined, // set by caller |
| | 3264 | }; |
| | 3265 | return &node.base; |
| | 3266 | } |
| 3218 | | 3267 | |
| 3219 | fn createInfixOp(arena: *Allocator, index: TokenIndex, op: Node.InfixOp.Op) !*Node { | 3268 | fn eatToken(p: *Parser, id: Token.Id) ?TokenIndex { |
| 3220 | const node = try arena.create(Node.InfixOp); | 3269 | return if (p.eatAnnotatedToken(id)) |token| token.index else null; |
| 3221 | node.* = .{ | 3270 | } |
| 3222 | .op_token = index, | | |
| 3223 | .lhs = undefined, // set by caller | | |
| 3224 | .op = op, | | |
| 3225 | .rhs = undefined, // set by caller | | |
| 3226 | }; | | |
| 3227 | return &node.base; | | |
| 3228 | } | | |
| 3229 | | 3271 | |
| 3230 | fn eatToken(it: *TokenIterator, id: Token.Id) ?TokenIndex { | 3272 | fn eatAnnotatedToken(p: *Parser, id: Token.Id) ?AnnotatedToken { |
| 3231 | return if (eatAnnotatedToken(it, id)) |token| token.index else null; | 3273 | return if (p.tokens[p.tok_i].id == id) p.nextToken() else null; |
| 3232 | } | 3274 | } |
| 3233 | | 3275 | |
| 3234 | fn eatAnnotatedToken(it: *TokenIterator, id: Token.Id) ?AnnotatedToken { | 3276 | fn expectToken(p: *Parser, id: Token.Id) Error!TokenIndex { |
| 3235 | return if (it.peek().?.id == id) nextToken(it) else null; | 3277 | return (try p.expectTokenRecoverable(id)) orelse |
| 3236 | } | 3278 | error.ParseError; |
| | 3279 | } |
| 3237 | | 3280 | |
| 3238 | fn expectToken(it: *TokenIterator, tree: *Tree, id: Token.Id) Error!TokenIndex { | 3281 | fn expectTokenRecoverable(p: *Parser, id: Token.Id) !?TokenIndex { |
| 3239 | return (try expectTokenRecoverable(it, tree, id)) orelse | 3282 | const token = p.nextToken(); |
| 3240 | error.ParseError; | 3283 | if (token.ptr.id != id) { |
| 3241 | } | 3284 | try p.errors.append(p.gpa, .{ |
| | 3285 | .ExpectedToken = .{ .token = token.index, .expected_id = id }, |
| | 3286 | }); |
| | 3287 | // go back so that we can recover properly |
| | 3288 | p.putBackToken(token.index); |
| | 3289 | return null; |
| | 3290 | } |
| | 3291 | return token.index; |
| | 3292 | } |
| 3242 | | 3293 | |
| 3243 | fn expectTokenRecoverable(it: *TokenIterator, tree: *Tree, id: Token.Id) !?TokenIndex { | 3294 | fn nextToken(p: *Parser) AnnotatedToken { |
| 3244 | const token = nextToken(it); | 3295 | const result = AnnotatedToken{ |
| 3245 | if (token.ptr.id != id) { | 3296 | .index = p.tok_i, |
| 3246 | try tree.errors.push(.{ | 3297 | .ptr = &p.tokens[p.tok_i], |
| 3247 | .ExpectedToken = .{ .token = token.index, .expected_id = id }, | 3298 | }; |
| 3248 | }); | 3299 | if (p.tokens[p.tok_i].id == .Eof) { |
| 3249 | // go back so that we can recover properly | 3300 | return result; |
| 3250 | putBackToken(it, token.index); | 3301 | } |
| 3251 | return null; | 3302 | p.tok_i += 1; |
| | 3303 | assert(result.ptr.id != .LineComment); |
| | 3304 | |
| | 3305 | while (true) { |
| | 3306 | const next_tok = p.tokens[p.tok_i]; |
| | 3307 | if (next_tok.id != .LineComment) return result; |
| | 3308 | p.tok_i += 1; |
| | 3309 | } |
| | 3310 | } |
| | 3311 | |
| | 3312 | fn putBackToken(p: *Parser, putting_back: TokenIndex) void { |
| | 3313 | while (p.tok_i > 0) { |
| | 3314 | p.tok_i -= 1; |
| | 3315 | const prev_tok = p.tokens[p.tok_i]; |
| | 3316 | if (prev_tok.id == .LineComment) continue; |
| | 3317 | assert(putting_back == p.tok_i); |
| | 3318 | return; |
| | 3319 | } |
| 3252 | } | 3320 | } |
| 3253 | return token.index; | | |
| 3254 | } | | |
| 3255 | | 3321 | |
| 3256 | fn nextToken(it: *TokenIterator) AnnotatedToken { | 3322 | const AnnotatedToken = struct { |
| 3257 | const result = AnnotatedToken{ | 3323 | index: TokenIndex, |
| 3258 | .index = it.index, | 3324 | ptr: *const Token, |
| 3259 | .ptr = it.next().?, | | |
| 3260 | }; | 3325 | }; |
| 3261 | assert(result.ptr.id != .LineComment); | | |
| 3262 | | 3326 | |
| 3263 | while (true) { | 3327 | fn expectNode( |
| 3264 | const next_tok = it.peek() orelse return result; | 3328 | p: *Parser, |
| 3265 | if (next_tok.id != .LineComment) return result; | 3329 | parseFn: NodeParseFn, |
| 3266 | _ = it.next(); | 3330 | /// if parsing fails |
| | 3331 | err: AstError, |
| | 3332 | ) Error!*Node { |
| | 3333 | return (try p.expectNodeRecoverable(parseFn, err)) orelse return error.ParseError; |
| 3267 | } | 3334 | } |
| 3268 | } | | |
| 3269 | | 3335 | |
| 3270 | fn putBackToken(it: *TokenIterator, putting_back: TokenIndex) void { | 3336 | fn expectNodeRecoverable( |
| 3271 | while (true) { | 3337 | p: *Parser, |
| 3272 | const prev_tok = it.prev() orelse return; | 3338 | parseFn: NodeParseFn, |
| 3273 | if (prev_tok.id == .LineComment) continue; | 3339 | /// if parsing fails |
| 3274 | assert(it.list.at(putting_back) == prev_tok); | 3340 | err: AstError, |
| 3275 | return; | 3341 | ) !?*Node { |
| | 3342 | return (try parseFn(p)) orelse { |
| | 3343 | try p.errors.append(p.gpa, err); |
| | 3344 | return null; |
| | 3345 | }; |
| 3276 | } | 3346 | } |
| 3277 | } | | |
| 3278 | | | |
| 3279 | const AnnotatedToken = struct { | | |
| 3280 | index: TokenIndex, | | |
| 3281 | ptr: *Token, | | |
| 3282 | }; | 3347 | }; |
| 3283 | | 3348 | |
| 3284 | fn expectNode( | 3349 | fn ParseFn(comptime T: type) type { |
| 3285 | arena: *Allocator, | 3350 | return fn (p: *Parser) Error!T; |
| 3286 | it: *TokenIterator, | | |
| 3287 | tree: *Tree, | | |
| 3288 | parseFn: NodeParseFn, | | |
| 3289 | err: AstError, // if parsing fails | | |
| 3290 | ) Error!*Node { | | |
| 3291 | return (try expectNodeRecoverable(arena, it, tree, parseFn, err)) orelse | | |
| 3292 | return error.ParseError; | | |
| 3293 | } | 3351 | } |
| 3294 | | 3352 | |
| 3295 | fn expectNodeRecoverable( | | |
| 3296 | arena: *Allocator, | | |
| 3297 | it: *TokenIterator, | | |
| 3298 | tree: *Tree, | | |
| 3299 | parseFn: NodeParseFn, | | |
| 3300 | err: AstError, // if parsing fails | | |
| 3301 | ) !?*Node { | | |
| 3302 | return (try parseFn(arena, it, tree)) orelse { | | |
| 3303 | try tree.errors.push(err); | | |
| 3304 | return null; | | |
| 3305 | }; | | |
| 3306 | } | | |
| 3307 | | 3353 | |
| 3308 | test "std.zig.parser" { | 3354 | test "std.zig.parser" { |
| 3309 | _ = @import("parser_test.zig"); | 3355 | _ = @import("parser_test.zig"); |