// SPDX-License-Identifier: MIT // Copyright (c) 2015-2020 Zig Contributors // This file is part of [zig](https://ziglang.org/), which is MIT licensed. // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; const ast = @import("ast.zig"); const Node = ast.Node; const Tree = ast.Tree; const AstError = ast.Error; const TokenIndex = ast.TokenIndex; const NodeIndex = ast.NodeIndex; const lexer = @import("tokenizer.zig"); const Token = lexer.Token; const Tokenizer = lexer.Tokenizer; pub const Error = error{ParseError} || Allocator.Error; /// Result should be freed with tree.deinit() when there are /// no more references to any of the tokens or nodes. pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!*Tree { var token_ids = std.ArrayList(Token.Id).init(gpa); defer token_ids.deinit(); var token_locs = std.ArrayList(Token.Loc).init(gpa); defer token_locs.deinit(); // Before starting, just check that we have about enough memory. const estimated_token_count = source.len / 8; try token_ids.ensureCapacity(estimated_token_count); try token_locs.ensureCapacity(estimated_token_count); // Use the lexer to get all the tokens from the source code. var tokenizer = Tokenizer.init(source); while (true) { const token = tokenizer.next(); try token_ids.append(token.id); try token_locs.append(token.loc); if (token.id == .Eof) break; } // Create a Parser structure. var parser: Parser = .{ .source = source, // Source code. .arena = std.heap.ArenaAllocator.init(gpa), // Arena allocator. .gpa = gpa, // General Purpose Allocator. .token_ids = token_ids.items, // IDs of the tokens. .token_locs = token_locs.items, // Location of the tokens. .errors = .{}, // List of errors in our parsing. .tok_i = 0, // Index of current token being analyzed. }; defer parser.errors.deinit(gpa); errdefer parser.arena.deinit(); // Ignore the first line comments from our code. while (token_ids.items[parser.tok_i] == .LineComment) parser.tok_i += 1; // Perform parsing, called once. const root_node = try parser.parseRoot(); // Create a parsing Tree, with the nodes parsed early on. // toOwnedSlice: free the memory and return the list. Arrays are empty, // allocator can be free, arrays are owned by a different allocator. const tree = try parser.arena.allocator.create(Tree); tree.* = .{ .gpa = gpa, .source = source, .token_ids = token_ids.toOwnedSlice(), .token_locs = token_locs.toOwnedSlice(), .errors = parser.errors.toOwnedSlice(gpa), .root_node = root_node, .arena = parser.arena.state, }; return tree; } /// Represents in-progress parsing, will be converted to an ast.Tree after completion. const Parser = struct { arena: std.heap.ArenaAllocator, gpa: *Allocator, source: []const u8, token_ids: []const Token.Id, token_locs: []const Token.Loc, tok_i: TokenIndex, errors: std.ArrayListUnmanaged(AstError), /// Root <- skip ContainerMembers eof fn parseRoot(p: *Parser) Allocator.Error!*Node.Root { // Parse declarations. const decls = try parseContainerMembers(p, true); defer p.gpa.free(decls); // parseContainerMembers will try to skip as much // invalid tokens as it can so this can only be the EOF // eatToken returns next token or null (if current token id isn't parameter). // If current token is .Eof, next token is actually the first. const eof_token = p.eatToken(.Eof).?; // Nb of declarations becomes an ast.NodeIndex integer variable (usize). const decls_len = @intCast(NodeIndex, decls.len); const node = try Node.Root.create(&p.arena.allocator, decls_len, eof_token); // std.mem.copy: T, dest, src std.mem.copy(*Node, node.decls(), decls); return node; // Root node. } /// ContainerMembers /// <- TestDecl ContainerMembers /// / TopLevelComptime ContainerMembers /// / KEYWORD_pub? TopLevelDecl ContainerMembers /// / ContainerField COMMA ContainerMembers /// / ContainerField /// / // parseContainerMembers: actual parsing code starts here. fn parseContainerMembers(p: *Parser, top_level: bool) ![]*Node { std.debug.print("parseContainerMembers: is top? {}\n", .{top_level}); // list: all nodes in the ast. var list = std.ArrayList(*Node).init(p.gpa); defer list.deinit(); // field_state: union of enum. // Tagged union: eligible to use in switch expressions and coerce their value. // Example: switch (some_tagged_union) { SomeType => |value| print("{}\n", value); } // If a '*' is placed before the variable name, it's a pointer to the value inside // the tagged union. // Example: switch (some_tagged_union) { SomeType => |*value| value.* += 1; } // @TagType can be used to get the right enum type. var field_state: union(enum) { /// no fields have been seen none, /// currently parsing fields seen, /// saw fields and then a declaration after them. /// payload is first token of previous declaration. end: TokenIndex, // TokenIndex is defined as usize in std.zig.ast. /// there was a declaration between fields, don't report more errors err, } = .none; /// Start => Requires Thing => End /// Requires => require StringLiteral Requires | nil /// Thing => Definition Thing | ClassHdr Thing | nil /// Definition => define Identifier ClassHdrSimple | /// ClassHdr => ClassHdrFull | ClassHdrSimple /// ClassHdrSimple => Identifier LBrace ClassCon RBrace /// ClassHdrFull => Identifier LParen Identifier RParen LBrace ClassCon RBrace /// ClassCon => ClassHdr | statement ClassCon | nil /// statement => Keyword_property Identifier Identifier Colon value /// value => StringLiteral | NullLiteral | IntegerLiteral | FloatLiteral // True start of parsing. while (true) { const token = p.nextToken(); switch (p.token_ids[token]) { .Keyword_require => { p.putBackToken(token); // TODO: read require p.parseRequire(); }, .Identifier => { p.putBackToken(token); p.parseClass(); }, .Eof => { p.putBackToken(token); break; }, else => { std.debug.print("token: {}\n", .{p.token_ids[token]}); continue; }, } } return list.toOwnedSlice(); } // TODO: require "file" // file should be read, parsed and a loop detection should take place. fn parseRequire(p: *Parser) !void { const require_token = p.eatToken(.Keyword_require); const file_to_read = p.eatToken(.StringLiteral); std.debug.print("TODO: file required: {}\n", .{file_to_read}); } // TODO: class // file should be read, parsed and a loop detection should take place. fn parseRequire(p: *Parser) !void { const require_token = p.eatToken(.Keyword_require); const file_to_read = p.eatToken(.StringLiteral); std.debug.print("TODO: file required: {}\n", .{file_to_read}); } /// Statement /// <- KEYWORD_comptime? VarDecl /// / KEYWORD_comptime BlockExprStatement /// / KEYWORD_nosuspend BlockExprStatement /// / KEYWORD_suspend (SEMICOLON / BlockExprStatement) /// / KEYWORD_defer BlockExprStatement /// / KEYWORD_errdefer Payload? BlockExprStatement /// / IfStatement /// / LabeledStatement /// / SwitchExpr /// / AssignExpr SEMICOLON fn parseStatement(p: *Parser) Error!?*Node { const comptime_token = p.eatToken(.Keyword_comptime); if (try p.parseVarDecl(.{ .comptime_token = comptime_token, })) |node| { return node; } if (comptime_token) |token| { const block_expr = try p.expectNode(parseBlockExprStatement, .{ .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, }); const node = try p.arena.allocator.create(Node.Comptime); node.* = .{ .doc_comments = null, .comptime_token = token, .expr = block_expr, }; return &node.base; } if (p.eatToken(.Keyword_nosuspend)) |nosuspend_token| { const block_expr = try p.expectNode(parseBlockExprStatement, .{ .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, }); const node = try p.arena.allocator.create(Node.Nosuspend); node.* = .{ .nosuspend_token = nosuspend_token, .expr = block_expr, }; return &node.base; } if (p.eatToken(.Keyword_suspend)) |suspend_token| { const semicolon = p.eatToken(.Semicolon); const body_node = if (semicolon == null) blk: { break :blk try p.expectNode(parseBlockExprStatement, .{ .ExpectedBlockOrExpression = .{ .token = p.tok_i }, }); } else null; const node = try p.arena.allocator.create(Node.Suspend); node.* = .{ .suspend_token = suspend_token, .body = body_node, }; return &node.base; } const defer_token = p.eatToken(.Keyword_defer) orelse p.eatToken(.Keyword_errdefer); if (defer_token) |token| { const payload = if (p.token_ids[token] == .Keyword_errdefer) try p.parsePayload() else null; const expr_node = try p.expectNode(parseBlockExprStatement, .{ .ExpectedBlockOrExpression = .{ .token = p.tok_i }, }); const node = try p.arena.allocator.create(Node.Defer); node.* = .{ .defer_token = token, .expr = expr_node, .payload = payload, }; return &node.base; } if (try p.parseIfStatement()) |node| return node; if (try p.parseLabeledStatement()) |node| return node; if (try p.parseSwitchExpr()) |node| return node; if (try p.parseAssignExpr()) |node| { _ = try p.expectTokenRecoverable(.Semicolon); return node; } return null; } // /// Attempts to find next container member by searching for certain tokens // fn findNextContainerMember(p: *Parser) void { // var level: u32 = 0; // while (true) { // const tok = p.nextToken(); // switch (p.token_ids[tok]) { // // any of these can start a new top level declaration // .Keyword_test, // .Keyword_comptime, // .Keyword_pub, // .Keyword_export, // .Keyword_extern, // .Keyword_inline, // .Keyword_noinline, // .Keyword_usingnamespace, // .Keyword_threadlocal, // .Keyword_const, // .Keyword_var, // .Keyword_fn, // .Identifier, // => { // if (level == 0) { // p.putBackToken(tok); // return; // } // }, // .Comma, .Semicolon => { // // this decl was likely meant to end here // if (level == 0) { // return; // } // }, // .LParen, .LBracket, .LBrace => level += 1, // .RParen, .RBracket => { // if (level != 0) level -= 1; // }, // .RBrace => { // if (level == 0) { // // end of container, exit // p.putBackToken(tok); // return; // } // level -= 1; // }, // .Eof => { // p.putBackToken(tok); // return; // }, // else => {}, // } // } // } // // /// Attempts to find the next statement by searching for a semicolon // fn findNextStmt(p: *Parser) void { // var level: u32 = 0; // while (true) { // const tok = p.nextToken(); // switch (p.token_ids[tok]) { // .LBrace => level += 1, // .RBrace => { // if (level == 0) { // p.putBackToken(tok); // return; // } // level -= 1; // }, // .Semicolon => { // if (level == 0) { // return; // } // }, // .Eof => { // p.putBackToken(tok); // return; // }, // else => {}, // } // } // } // // /// Eat a multiline container doc comment // fn parseContainerDocComments(p: *Parser) !?*Node { // if (p.eatToken(.ContainerDocComment)) |first_line| { // while (p.eatToken(.ContainerDocComment)) |_| {} // const node = try p.arena.allocator.create(Node.DocComment); // node.* = .{ .first_line = first_line }; // return &node.base; // } // return null; // } // // /// TopLevelComptime <- KEYWORD_comptime BlockExpr // fn parseTopLevelComptime(p: *Parser) !?*Node { // const tok = p.eatToken(.Keyword_comptime) orelse return null; // const lbrace = p.eatToken(.LBrace) orelse { // p.putBackToken(tok); // return null; // }; // p.putBackToken(lbrace); // const block_node = try p.expectNode(parseBlockExpr, .{ // .ExpectedLabelOrLBrace = .{ .token = p.tok_i }, // }); // // const comptime_node = try p.arena.allocator.create(Node.Comptime); // comptime_node.* = .{ // .doc_comments = null, // .comptime_token = tok, // .expr = block_node, // }; // return &comptime_node.base; // } // // /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr) // fn parseFnProto(p: *Parser, level: enum { top_level, as_type }, fields: struct { // doc_comments: ?*Node.DocComment = null, // visib_token: ?TokenIndex = null, // extern_export_inline_token: ?TokenIndex = null, // lib_name: ?*Node = null, // }) !?*Node { // // TODO: Remove once extern/async fn rewriting is // var is_async: ?void = null; // var is_extern_prototype: ?void = null; // const cc_token: ?TokenIndex = blk: { // if (p.eatToken(.Keyword_extern)) |token| { // is_extern_prototype = {}; // break :blk token; // } // if (p.eatToken(.Keyword_async)) |token| { // is_async = {}; // break :blk token; // } // break :blk null; // }; // const fn_token = p.eatToken(.Keyword_fn) orelse { // if (cc_token) |token| // p.putBackToken(token); // return null; // }; // const name_token = p.eatToken(.Identifier); // const lparen = try p.expectToken(.LParen); // const params = try p.parseParamDeclList(); // defer p.gpa.free(params); // const var_args_token = p.eatToken(.Ellipsis3); // const rparen = try p.expectToken(.RParen); // const align_expr = try p.parseByteAlign(); // const section_expr = try p.parseLinkSection(); // const callconv_expr = try p.parseCallconv(); // const exclamation_token = p.eatToken(.Bang); // // const return_type_expr = (try p.parseAnyType()) orelse // try p.expectNodeRecoverable(parseTypeExpr, .{ // // most likely the user forgot to specify the return type. // // Mark return type as invalid and try to continue. // .ExpectedReturnType = .{ .token = p.tok_i }, // }); // // // TODO https://github.com/ziglang/zig/issues/3750 // const R = Node.FnProto.ReturnType; // const return_type = if (return_type_expr == null) // R{ .Invalid = rparen } // else if (exclamation_token != null) // R{ .InferErrorSet = return_type_expr.? } // else // R{ .Explicit = return_type_expr.? }; // // const body_node: ?*Node = switch (level) { // .top_level => blk: { // if (p.eatToken(.Semicolon)) |_| { // break :blk null; // } // const body_block = (try p.parseBlock(null)) orelse { // // Since parseBlock only return error.ParseError on // // a missing '}' we can assume this function was // // supposed to end here. // try p.errors.append(p.gpa, .{ .ExpectedSemiOrLBrace = .{ .token = p.tok_i } }); // break :blk null; // }; // break :blk body_block; // }, // .as_type => null, // }; // // const fn_proto_node = try Node.FnProto.create(&p.arena.allocator, .{ // .params_len = params.len, // .fn_token = fn_token, // .return_type = return_type, // }, .{ // .doc_comments = fields.doc_comments, // .visib_token = fields.visib_token, // .name_token = name_token, // .var_args_token = var_args_token, // .extern_export_inline_token = fields.extern_export_inline_token, // .body_node = body_node, // .lib_name = fields.lib_name, // .align_expr = align_expr, // .section_expr = section_expr, // .callconv_expr = callconv_expr, // .is_extern_prototype = is_extern_prototype, // .is_async = is_async, // }); // std.mem.copy(Node.FnProto.ParamDecl, fn_proto_node.params(), params); // // return &fn_proto_node.base; // } // // /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON // fn parseVarDecl(p: *Parser, fields: struct { // doc_comments: ?*Node.DocComment = null, // visib_token: ?TokenIndex = null, // thread_local_token: ?TokenIndex = null, // extern_export_token: ?TokenIndex = null, // lib_name: ?*Node = null, // comptime_token: ?TokenIndex = null, // }) !?*Node { // const mut_token = p.eatToken(.Keyword_const) orelse // p.eatToken(.Keyword_var) orelse // return null; // // const name_token = try p.expectToken(.Identifier); // const type_node = if (p.eatToken(.Colon) != null) // try p.expectNode(parseTypeExpr, .{ // .ExpectedTypeExpr = .{ .token = p.tok_i }, // }) // else // null; // const align_node = try p.parseByteAlign(); // const section_node = try p.parseLinkSection(); // const eq_token = p.eatToken(.Equal); // const init_node = if (eq_token != null) blk: { // break :blk try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // } else null; // const semicolon_token = try p.expectToken(.Semicolon); // // const doc_comments = fields.doc_comments orelse try p.parseAppendedDocComment(semicolon_token); // // const node = try Node.VarDecl.create(&p.arena.allocator, .{ // .mut_token = mut_token, // .name_token = name_token, // .semicolon_token = semicolon_token, // }, .{ // .doc_comments = doc_comments, // .visib_token = fields.visib_token, // .thread_local_token = fields.thread_local_token, // .eq_token = eq_token, // .comptime_token = fields.comptime_token, // .extern_export_token = fields.extern_export_token, // .lib_name = fields.lib_name, // .type_node = type_node, // .align_node = align_node, // .section_node = section_node, // .init_node = init_node, // }); // return &node.base; // } // // /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? // fn parseContainerField(p: *Parser) !?*Node { // const comptime_token = p.eatToken(.Keyword_comptime); // const name_token = p.eatToken(.Identifier) orelse { // if (comptime_token) |t| p.putBackToken(t); // return null; // }; // // var align_expr: ?*Node = null; // var type_expr: ?*Node = null; // if (p.eatToken(.Colon)) |_| { // if (p.eatToken(.Keyword_anytype) orelse p.eatToken(.Keyword_var)) |anytype_tok| { // const node = try p.arena.allocator.create(Node.OneToken); // node.* = .{ // .base = .{ .tag = .AnyType }, // .token = anytype_tok, // }; // type_expr = &node.base; // } else { // type_expr = try p.expectNode(parseTypeExpr, .{ // .ExpectedTypeExpr = .{ .token = p.tok_i }, // }); // align_expr = try p.parseByteAlign(); // } // } // // const value_expr = if (p.eatToken(.Equal)) |_| // try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }) // else // null; // // const node = try p.arena.allocator.create(Node.ContainerField); // node.* = .{ // .doc_comments = null, // .comptime_token = comptime_token, // .name_token = name_token, // .type_expr = type_expr, // .value_expr = value_expr, // .align_expr = align_expr, // }; // return &node.base; // } // /// IfStatement // /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? // /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) // fn parseIfStatement(p: *Parser) !?*Node { // const if_node = (try p.parseIfPrefix()) orelse return null; // const if_prefix = if_node.cast(Node.If).?; // // const block_expr = (try p.parseBlockExpr()); // const assign_expr = if (block_expr == null) // try p.expectNode(parseAssignExpr, .{ // .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, // }) // else // null; // // const semicolon = if (assign_expr != null) p.eatToken(.Semicolon) else null; // // const else_node = if (semicolon == null) blk: { // const else_token = p.eatToken(.Keyword_else) orelse break :blk null; // const payload = try p.parsePayload(); // const else_body = try p.expectNode(parseStatement, .{ // .InvalidToken = .{ .token = p.tok_i }, // }); // // const node = try p.arena.allocator.create(Node.Else); // node.* = .{ // .else_token = else_token, // .payload = payload, // .body = else_body, // }; // // break :blk node; // } else null; // // if (block_expr) |body| { // if_prefix.body = body; // if_prefix.@"else" = else_node; // return if_node; // } // // if (assign_expr) |body| { // if_prefix.body = body; // if (semicolon != null) return if_node; // if (else_node != null) { // if_prefix.@"else" = else_node; // return if_node; // } // try p.errors.append(p.gpa, .{ // .ExpectedSemiOrElse = .{ .token = p.tok_i }, // }); // } // // return if_node; // } // // /// LabeledStatement <- BlockLabel? (Block / LoopStatement) // fn parseLabeledStatement(p: *Parser) !?*Node { // var colon: TokenIndex = undefined; // const label_token = p.parseBlockLabel(&colon); // // if (try p.parseBlock(label_token)) |node| return node; // // if (try p.parseLoopStatement()) |node| { // if (node.cast(Node.For)) |for_node| { // for_node.label = label_token; // } else if (node.cast(Node.While)) |while_node| { // while_node.label = label_token; // } else unreachable; // return node; // } // // if (label_token != null) { // try p.errors.append(p.gpa, .{ // .ExpectedLabelable = .{ .token = p.tok_i }, // }); // return error.ParseError; // } // // return null; // } // // /// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) // fn parseLoopStatement(p: *Parser) !?*Node { // const inline_token = p.eatToken(.Keyword_inline); // // if (try p.parseForStatement()) |node| { // node.cast(Node.For).?.inline_token = inline_token; // return node; // } // // if (try p.parseWhileStatement()) |node| { // node.cast(Node.While).?.inline_token = inline_token; // return node; // } // if (inline_token == null) return null; // // // If we've seen "inline", there should have been a "for" or "while" // try p.errors.append(p.gpa, .{ // .ExpectedInlinable = .{ .token = p.tok_i }, // }); // return error.ParseError; // } // // /// ForStatement // /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? // /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) // fn parseForStatement(p: *Parser) !?*Node { // const node = (try p.parseForPrefix()) orelse return null; // const for_prefix = node.cast(Node.For).?; // // if (try p.parseBlockExpr()) |block_expr_node| { // for_prefix.body = block_expr_node; // // if (p.eatToken(.Keyword_else)) |else_token| { // const statement_node = try p.expectNode(parseStatement, .{ // .InvalidToken = .{ .token = p.tok_i }, // }); // // const else_node = try p.arena.allocator.create(Node.Else); // else_node.* = .{ // .else_token = else_token, // .payload = null, // .body = statement_node, // }; // for_prefix.@"else" = else_node; // // return node; // } // // return node; // } // // for_prefix.body = try p.expectNode(parseAssignExpr, .{ // .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, // }); // // if (p.eatToken(.Semicolon) != null) return node; // // if (p.eatToken(.Keyword_else)) |else_token| { // const statement_node = try p.expectNode(parseStatement, .{ // .ExpectedStatement = .{ .token = p.tok_i }, // }); // // const else_node = try p.arena.allocator.create(Node.Else); // else_node.* = .{ // .else_token = else_token, // .payload = null, // .body = statement_node, // }; // for_prefix.@"else" = else_node; // return node; // } // // try p.errors.append(p.gpa, .{ // .ExpectedSemiOrElse = .{ .token = p.tok_i }, // }); // // return node; // } // // /// WhileStatement // /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? // /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) // fn parseWhileStatement(p: *Parser) !?*Node { // const node = (try p.parseWhilePrefix()) orelse return null; // const while_prefix = node.cast(Node.While).?; // // if (try p.parseBlockExpr()) |block_expr_node| { // while_prefix.body = block_expr_node; // // if (p.eatToken(.Keyword_else)) |else_token| { // const payload = try p.parsePayload(); // // const statement_node = try p.expectNode(parseStatement, .{ // .InvalidToken = .{ .token = p.tok_i }, // }); // // const else_node = try p.arena.allocator.create(Node.Else); // else_node.* = .{ // .else_token = else_token, // .payload = payload, // .body = statement_node, // }; // while_prefix.@"else" = else_node; // // return node; // } // // return node; // } // // while_prefix.body = try p.expectNode(parseAssignExpr, .{ // .ExpectedBlockOrAssignment = .{ .token = p.tok_i }, // }); // // if (p.eatToken(.Semicolon) != null) return node; // // if (p.eatToken(.Keyword_else)) |else_token| { // const payload = try p.parsePayload(); // // const statement_node = try p.expectNode(parseStatement, .{ // .ExpectedStatement = .{ .token = p.tok_i }, // }); // // const else_node = try p.arena.allocator.create(Node.Else); // else_node.* = .{ // .else_token = else_token, // .payload = payload, // .body = statement_node, // }; // while_prefix.@"else" = else_node; // return node; // } // // try p.errors.append(p.gpa, .{ // .ExpectedSemiOrElse = .{ .token = p.tok_i }, // }); // // return node; // } // // /// BlockExprStatement // /// <- BlockExpr // /// / AssignExpr SEMICOLON // fn parseBlockExprStatement(p: *Parser) !?*Node { // if (try p.parseBlockExpr()) |node| return node; // if (try p.parseAssignExpr()) |node| { // _ = try p.expectTokenRecoverable(.Semicolon); // return node; // } // return null; // } // // /// BlockExpr <- BlockLabel? Block // fn parseBlockExpr(p: *Parser) Error!?*Node { // var colon: TokenIndex = undefined; // const label_token = p.parseBlockLabel(&colon); // const block_node = (try p.parseBlock(label_token)) orelse { // if (label_token) |label| { // p.putBackToken(label + 1); // ":" // p.putBackToken(label); // IDENTIFIER // } // return null; // }; // return block_node; // } // // /// AssignExpr <- Expr (AssignOp Expr)? // fn parseAssignExpr(p: *Parser) !?*Node { // return p.parseBinOpExpr(parseAssignOp, parseExpr, .Once); // } // // /// Expr <- KEYWORD_try* BoolOrExpr // fn parseExpr(p: *Parser) Error!?*Node { // return p.parsePrefixOpExpr(parseTry, parseBoolOrExpr); // } // // /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)* // fn parseBoolOrExpr(p: *Parser) !?*Node { // return p.parseBinOpExpr( // SimpleBinOpParseFn(.Keyword_or, .BoolOr), // parseBoolAndExpr, // .Infinitely, // ); // } // // /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)* // fn parseBoolAndExpr(p: *Parser) !?*Node { // return p.parseBinOpExpr( // SimpleBinOpParseFn(.Keyword_and, .BoolAnd), // parseCompareExpr, // .Infinitely, // ); // } // // /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? // fn parseCompareExpr(p: *Parser) !?*Node { // return p.parseBinOpExpr(parseCompareOp, parseBitwiseExpr, .Once); // } // // /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* // fn parseBitwiseExpr(p: *Parser) !?*Node { // return p.parseBinOpExpr(parseBitwiseOp, parseBitShiftExpr, .Infinitely); // } // // /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* // fn parseBitShiftExpr(p: *Parser) !?*Node { // return p.parseBinOpExpr(parseBitShiftOp, parseAdditionExpr, .Infinitely); // } // // /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* // fn parseAdditionExpr(p: *Parser) !?*Node { // return p.parseBinOpExpr(parseAdditionOp, parseMultiplyExpr, .Infinitely); // } // // /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* // fn parseMultiplyExpr(p: *Parser) !?*Node { // return p.parseBinOpExpr(parseMultiplyOp, parsePrefixExpr, .Infinitely); // } // // /// PrefixExpr <- PrefixOp* PrimaryExpr // fn parsePrefixExpr(p: *Parser) !?*Node { // return p.parsePrefixOpExpr(parsePrefixOp, parsePrimaryExpr); // } // // /// PrimaryExpr // /// <- AsmExpr // /// / IfExpr // /// / KEYWORD_break BreakLabel? Expr? // /// / KEYWORD_comptime Expr // /// / KEYWORD_nosuspend Expr // /// / KEYWORD_continue BreakLabel? // /// / KEYWORD_resume Expr // /// / KEYWORD_return Expr? // /// / BlockLabel? LoopExpr // /// / Block // /// / CurlySuffixExpr // fn parsePrimaryExpr(p: *Parser) !?*Node { // if (try p.parseAsmExpr()) |node| return node; // if (try p.parseIfExpr()) |node| return node; // // if (p.eatToken(.Keyword_break)) |token| { // const label = try p.parseBreakLabel(); // const expr_node = try p.parseExpr(); // const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{ // .tag = .Break, // .ltoken = token, // }, .{ // .label = label, // .rhs = expr_node, // }); // return &node.base; // } // // if (p.eatToken(.Keyword_comptime)) |token| { // const expr_node = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // const node = try p.arena.allocator.create(Node.Comptime); // node.* = .{ // .doc_comments = null, // .comptime_token = token, // .expr = expr_node, // }; // return &node.base; // } // // if (p.eatToken(.Keyword_nosuspend)) |token| { // const expr_node = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // const node = try p.arena.allocator.create(Node.Nosuspend); // node.* = .{ // .nosuspend_token = token, // .expr = expr_node, // }; // return &node.base; // } // // if (p.eatToken(.Keyword_continue)) |token| { // const label = try p.parseBreakLabel(); // const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{ // .tag = .Continue, // .ltoken = token, // }, .{ // .label = label, // .rhs = null, // }); // return &node.base; // } // // if (p.eatToken(.Keyword_resume)) |token| { // const expr_node = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // const node = try p.arena.allocator.create(Node.SimplePrefixOp); // node.* = .{ // .base = .{ .tag = .Resume }, // .op_token = token, // .rhs = expr_node, // }; // return &node.base; // } // // if (p.eatToken(.Keyword_return)) |token| { // const expr_node = try p.parseExpr(); // const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{ // .tag = .Return, // .ltoken = token, // }, .{ // .rhs = expr_node, // }); // return &node.base; // } // // var colon: TokenIndex = undefined; // const label = p.parseBlockLabel(&colon); // if (try p.parseLoopExpr()) |node| { // if (node.cast(Node.For)) |for_node| { // for_node.label = label; // } else if (node.cast(Node.While)) |while_node| { // while_node.label = label; // } else unreachable; // return node; // } // if (label) |token| { // p.putBackToken(token + 1); // ":" // p.putBackToken(token); // IDENTIFIER // } // // if (try p.parseBlock(null)) |node| return node; // if (try p.parseCurlySuffixExpr()) |node| return node; // // return null; // } // // /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? // fn parseIfExpr(p: *Parser) !?*Node { // return p.parseIf(parseExpr); // } // // /// Block <- LBRACE Statement* RBRACE // fn parseBlock(p: *Parser, label_token: ?TokenIndex) !?*Node { // const lbrace = p.eatToken(.LBrace) orelse return null; // // var statements = std.ArrayList(*Node).init(p.gpa); // defer statements.deinit(); // // while (true) { // const statement = (p.parseStatement() catch |err| switch (err) { // error.OutOfMemory => return error.OutOfMemory, // error.ParseError => { // // try to skip to the next statement // p.findNextStmt(); // continue; // }, // }) orelse break; // try statements.append(statement); // } // // const rbrace = try p.expectToken(.RBrace); // // const statements_len = @intCast(NodeIndex, statements.items.len); // // if (label_token) |label| { // const block_node = try Node.LabeledBlock.alloc(&p.arena.allocator, statements_len); // block_node.* = .{ // .label = label, // .lbrace = lbrace, // .statements_len = statements_len, // .rbrace = rbrace, // }; // std.mem.copy(*Node, block_node.statements(), statements.items); // return &block_node.base; // } else { // const block_node = try Node.Block.alloc(&p.arena.allocator, statements_len); // block_node.* = .{ // .lbrace = lbrace, // .statements_len = statements_len, // .rbrace = rbrace, // }; // std.mem.copy(*Node, block_node.statements(), statements.items); // return &block_node.base; // } // } // // /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) // fn parseLoopExpr(p: *Parser) !?*Node { // const inline_token = p.eatToken(.Keyword_inline); // // if (try p.parseForExpr()) |node| { // node.cast(Node.For).?.inline_token = inline_token; // return node; // } // // if (try p.parseWhileExpr()) |node| { // node.cast(Node.While).?.inline_token = inline_token; // return node; // } // // if (inline_token == null) return null; // // // If we've seen "inline", there should have been a "for" or "while" // try p.errors.append(p.gpa, .{ // .ExpectedInlinable = .{ .token = p.tok_i }, // }); // return error.ParseError; // } // // /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? // fn parseForExpr(p: *Parser) !?*Node { // const node = (try p.parseForPrefix()) orelse return null; // const for_prefix = node.cast(Node.For).?; // // const body_node = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // for_prefix.body = body_node; // // if (p.eatToken(.Keyword_else)) |else_token| { // const body = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // // const else_node = try p.arena.allocator.create(Node.Else); // else_node.* = .{ // .else_token = else_token, // .payload = null, // .body = body, // }; // // for_prefix.@"else" = else_node; // } // // return node; // } // // /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? // fn parseWhileExpr(p: *Parser) !?*Node { // const node = (try p.parseWhilePrefix()) orelse return null; // const while_prefix = node.cast(Node.While).?; // // const body_node = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // while_prefix.body = body_node; // // if (p.eatToken(.Keyword_else)) |else_token| { // const payload = try p.parsePayload(); // const body = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // // const else_node = try p.arena.allocator.create(Node.Else); // else_node.* = .{ // .else_token = else_token, // .payload = payload, // .body = body, // }; // // while_prefix.@"else" = else_node; // } // // return node; // } // // /// CurlySuffixExpr <- TypeExpr InitList? // fn parseCurlySuffixExpr(p: *Parser) !?*Node { // const lhs = (try p.parseTypeExpr()) orelse return null; // const suffix_op = (try p.parseInitList(lhs)) orelse return lhs; // return suffix_op; // } // // /// InitList // /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE // /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE // /// / LBRACE RBRACE // fn parseInitList(p: *Parser, lhs: *Node) !?*Node { // const lbrace = p.eatToken(.LBrace) orelse return null; // var init_list = std.ArrayList(*Node).init(p.gpa); // defer init_list.deinit(); // // if (try p.parseFieldInit()) |field_init| { // try init_list.append(field_init); // while (p.eatToken(.Comma)) |_| { // const next = (try p.parseFieldInit()) orelse break; // try init_list.append(next); // } // const node = try Node.StructInitializer.alloc(&p.arena.allocator, init_list.items.len); // node.* = .{ // .lhs = lhs, // .rtoken = try p.expectToken(.RBrace), // .list_len = init_list.items.len, // }; // std.mem.copy(*Node, node.list(), init_list.items); // return &node.base; // } // // if (try p.parseExpr()) |expr| { // try init_list.append(expr); // while (p.eatToken(.Comma)) |_| { // const next = (try p.parseExpr()) orelse break; // try init_list.append(next); // } // const node = try Node.ArrayInitializer.alloc(&p.arena.allocator, init_list.items.len); // node.* = .{ // .lhs = lhs, // .rtoken = try p.expectToken(.RBrace), // .list_len = init_list.items.len, // }; // std.mem.copy(*Node, node.list(), init_list.items); // return &node.base; // } // // const node = try p.arena.allocator.create(Node.StructInitializer); // node.* = .{ // .lhs = lhs, // .rtoken = try p.expectToken(.RBrace), // .list_len = 0, // }; // return &node.base; // } // // /// InitList // /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE // /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE // /// / LBRACE RBRACE // fn parseAnonInitList(p: *Parser, dot: TokenIndex) !?*Node { // const lbrace = p.eatToken(.LBrace) orelse return null; // var init_list = std.ArrayList(*Node).init(p.gpa); // defer init_list.deinit(); // // if (try p.parseFieldInit()) |field_init| { // try init_list.append(field_init); // while (p.eatToken(.Comma)) |_| { // const next = (try p.parseFieldInit()) orelse break; // try init_list.append(next); // } // const node = try Node.StructInitializerDot.alloc(&p.arena.allocator, init_list.items.len); // node.* = .{ // .dot = dot, // .rtoken = try p.expectToken(.RBrace), // .list_len = init_list.items.len, // }; // std.mem.copy(*Node, node.list(), init_list.items); // return &node.base; // } // // if (try p.parseExpr()) |expr| { // try init_list.append(expr); // while (p.eatToken(.Comma)) |_| { // const next = (try p.parseExpr()) orelse break; // try init_list.append(next); // } // const node = try Node.ArrayInitializerDot.alloc(&p.arena.allocator, init_list.items.len); // node.* = .{ // .dot = dot, // .rtoken = try p.expectToken(.RBrace), // .list_len = init_list.items.len, // }; // std.mem.copy(*Node, node.list(), init_list.items); // return &node.base; // } // // const node = try p.arena.allocator.create(Node.StructInitializerDot); // node.* = .{ // .dot = dot, // .rtoken = try p.expectToken(.RBrace), // .list_len = 0, // }; // return &node.base; // } // // /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr // fn parseTypeExpr(p: *Parser) Error!?*Node { // return p.parsePrefixOpExpr(parsePrefixTypeOp, parseErrorUnionExpr); // } // // /// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? // fn parseErrorUnionExpr(p: *Parser) !?*Node { // const suffix_expr = (try p.parseSuffixExpr()) orelse return null; // // if (try SimpleBinOpParseFn(.Bang, .ErrorUnion)(p)) |node| { // const error_union = node.castTag(.ErrorUnion).?; // const type_expr = try p.expectNode(parseTypeExpr, .{ // .ExpectedTypeExpr = .{ .token = p.tok_i }, // }); // error_union.lhs = suffix_expr; // error_union.rhs = type_expr; // return node; // } // // return suffix_expr; // } // // /// SuffixExpr // /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments // /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* // fn parseSuffixExpr(p: *Parser) !?*Node { // const maybe_async = p.eatToken(.Keyword_async); // if (maybe_async) |async_token| { // const token_fn = p.eatToken(.Keyword_fn); // if (token_fn != null) { // // TODO: remove this hack when async fn rewriting is // // HACK: If we see the keyword `fn`, then we assume that // // we are parsing an async fn proto, and not a call. // // We therefore put back all tokens consumed by the async // // prefix... // p.putBackToken(token_fn.?); // p.putBackToken(async_token); // return p.parsePrimaryTypeExpr(); // } // var res = try p.expectNode(parsePrimaryTypeExpr, .{ // .ExpectedPrimaryTypeExpr = .{ .token = p.tok_i }, // }); // // while (try p.parseSuffixOp(res)) |node| { // res = node; // } // // const params = (try p.parseFnCallArguments()) orelse { // try p.errors.append(p.gpa, .{ // .ExpectedParamList = .{ .token = p.tok_i }, // }); // // ignore this, continue parsing // return res; // }; // defer p.gpa.free(params.list); // const node = try Node.Call.alloc(&p.arena.allocator, params.list.len); // node.* = .{ // .lhs = res, // .params_len = params.list.len, // .async_token = async_token, // .rtoken = params.rparen, // }; // std.mem.copy(*Node, node.params(), params.list); // return &node.base; // } // if (try p.parsePrimaryTypeExpr()) |expr| { // var res = expr; // // while (true) { // if (try p.parseSuffixOp(res)) |node| { // res = node; // continue; // } // if (try p.parseFnCallArguments()) |params| { // defer p.gpa.free(params.list); // const call = try Node.Call.alloc(&p.arena.allocator, params.list.len); // call.* = .{ // .lhs = res, // .params_len = params.list.len, // .async_token = null, // .rtoken = params.rparen, // }; // std.mem.copy(*Node, call.params(), params.list); // res = &call.base; // continue; // } // break; // } // return res; // } // // return null; // } // // /// PrimaryTypeExpr // /// <- BUILTINIDENTIFIER FnCallArguments // /// / CHAR_LITERAL // /// / ContainerDecl // /// / DOT IDENTIFIER // /// / ErrorSetDecl // /// / FLOAT // /// / FnProto // /// / GroupedExpr // /// / LabeledTypeExpr // /// / IDENTIFIER // /// / IfTypeExpr // /// / INTEGER // /// / KEYWORD_comptime TypeExpr // /// / KEYWORD_error DOT IDENTIFIER // /// / KEYWORD_false // /// / KEYWORD_null // /// / KEYWORD_anyframe // /// / KEYWORD_true // /// / KEYWORD_undefined // /// / KEYWORD_unreachable // /// / STRINGLITERAL // /// / SwitchExpr // fn parsePrimaryTypeExpr(p: *Parser) !?*Node { // if (try p.parseBuiltinCall()) |node| return node; // if (p.eatToken(.CharLiteral)) |token| { // const node = try p.arena.allocator.create(Node.OneToken); // node.* = .{ // .base = .{ .tag = .CharLiteral }, // .token = token, // }; // return &node.base; // } // if (try p.parseContainerDecl()) |node| return node; // if (try p.parseAnonLiteral()) |node| return node; // if (try p.parseErrorSetDecl()) |node| return node; // if (try p.parseFloatLiteral()) |node| return node; // if (try p.parseFnProto(.as_type, .{})) |node| return node; // if (try p.parseGroupedExpr()) |node| return node; // if (try p.parseLabeledTypeExpr()) |node| return node; // if (try p.parseIdentifier()) |node| return node; // if (try p.parseIfTypeExpr()) |node| return node; // if (try p.parseIntegerLiteral()) |node| return node; // if (p.eatToken(.Keyword_comptime)) |token| { // const expr = (try p.parseTypeExpr()) orelse return null; // const node = try p.arena.allocator.create(Node.Comptime); // node.* = .{ // .doc_comments = null, // .comptime_token = token, // .expr = expr, // }; // return &node.base; // } // if (p.eatToken(.Keyword_error)) |token| { // const period = try p.expectTokenRecoverable(.Period); // const identifier = try p.expectNodeRecoverable(parseIdentifier, .{ // .ExpectedIdentifier = .{ .token = p.tok_i }, // }); // const global_error_set = try p.createLiteral(.ErrorType, token); // if (period == null or identifier == null) return global_error_set; // // const node = try p.arena.allocator.create(Node.SimpleInfixOp); // node.* = .{ // .base = Node{ .tag = .Period }, // .op_token = period.?, // .lhs = global_error_set, // .rhs = identifier.?, // }; // return &node.base; // } // if (p.eatToken(.Keyword_false)) |token| return p.createLiteral(.BoolLiteral, token); // if (p.eatToken(.Keyword_null)) |token| return p.createLiteral(.NullLiteral, token); // if (p.eatToken(.Keyword_anyframe)) |token| { // const node = try p.arena.allocator.create(Node.AnyFrameType); // node.* = .{ // .anyframe_token = token, // .result = null, // }; // return &node.base; // } // if (p.eatToken(.Keyword_true)) |token| return p.createLiteral(.BoolLiteral, token); // if (p.eatToken(.Keyword_undefined)) |token| return p.createLiteral(.UndefinedLiteral, token); // if (p.eatToken(.Keyword_unreachable)) |token| return p.createLiteral(.Unreachable, token); // if (try p.parseStringLiteral()) |node| return node; // if (try p.parseSwitchExpr()) |node| return node; // // return null; // } // // /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto // fn parseContainerDecl(p: *Parser) !?*Node { // const layout_token = p.eatToken(.Keyword_extern) orelse // p.eatToken(.Keyword_packed); // // const node = (try p.parseContainerDeclAuto()) orelse { // if (layout_token) |token| // p.putBackToken(token); // return null; // }; // node.cast(Node.ContainerDecl).?.*.layout_token = layout_token; // return node; // } // // /// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE // fn parseErrorSetDecl(p: *Parser) !?*Node { // const error_token = p.eatToken(.Keyword_error) orelse return null; // if (p.eatToken(.LBrace) == null) { // // Might parse as `KEYWORD_error DOT IDENTIFIER` later in PrimaryTypeExpr, so don't error // p.putBackToken(error_token); // return null; // } // const decls = try p.parseErrorTagList(); // defer p.gpa.free(decls); // const rbrace = try p.expectToken(.RBrace); // // const node = try Node.ErrorSetDecl.alloc(&p.arena.allocator, decls.len); // node.* = .{ // .error_token = error_token, // .decls_len = decls.len, // .rbrace_token = rbrace, // }; // std.mem.copy(*Node, node.decls(), decls); // return &node.base; // } // // /// GroupedExpr <- LPAREN Expr RPAREN // fn parseGroupedExpr(p: *Parser) !?*Node { // const lparen = p.eatToken(.LParen) orelse return null; // const expr = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // const rparen = try p.expectToken(.RParen); // // const node = try p.arena.allocator.create(Node.GroupedExpression); // node.* = .{ // .lparen = lparen, // .expr = expr, // .rparen = rparen, // }; // return &node.base; // } // // /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? // fn parseIfTypeExpr(p: *Parser) !?*Node { // return p.parseIf(parseTypeExpr); // } // // /// LabeledTypeExpr // /// <- BlockLabel Block // /// / BlockLabel? LoopTypeExpr // fn parseLabeledTypeExpr(p: *Parser) !?*Node { // var colon: TokenIndex = undefined; // const label = p.parseBlockLabel(&colon); // // if (label) |label_token| { // if (try p.parseBlock(label_token)) |node| return node; // } // // if (try p.parseLoopTypeExpr()) |node| { // switch (node.tag) { // .For => node.cast(Node.For).?.label = label, // .While => node.cast(Node.While).?.label = label, // else => unreachable, // } // return node; // } // // if (label) |token| { // p.putBackToken(colon); // p.putBackToken(token); // } // return null; // } // // /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) // fn parseLoopTypeExpr(p: *Parser) !?*Node { // const inline_token = p.eatToken(.Keyword_inline); // // if (try p.parseForTypeExpr()) |node| { // node.cast(Node.For).?.inline_token = inline_token; // return node; // } // // if (try p.parseWhileTypeExpr()) |node| { // node.cast(Node.While).?.inline_token = inline_token; // return node; // } // // if (inline_token == null) return null; // // // If we've seen "inline", there should have been a "for" or "while" // try p.errors.append(p.gpa, .{ // .ExpectedInlinable = .{ .token = p.tok_i }, // }); // return error.ParseError; // } // // /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? // fn parseForTypeExpr(p: *Parser) !?*Node { // const node = (try p.parseForPrefix()) orelse return null; // const for_prefix = node.cast(Node.For).?; // // const type_expr = try p.expectNode(parseTypeExpr, .{ // .ExpectedTypeExpr = .{ .token = p.tok_i }, // }); // for_prefix.body = type_expr; // // if (p.eatToken(.Keyword_else)) |else_token| { // const else_expr = try p.expectNode(parseTypeExpr, .{ // .ExpectedTypeExpr = .{ .token = p.tok_i }, // }); // // const else_node = try p.arena.allocator.create(Node.Else); // else_node.* = .{ // .else_token = else_token, // .payload = null, // .body = else_expr, // }; // // for_prefix.@"else" = else_node; // } // // return node; // } // // /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? // fn parseWhileTypeExpr(p: *Parser) !?*Node { // const node = (try p.parseWhilePrefix()) orelse return null; // const while_prefix = node.cast(Node.While).?; // // const type_expr = try p.expectNode(parseTypeExpr, .{ // .ExpectedTypeExpr = .{ .token = p.tok_i }, // }); // while_prefix.body = type_expr; // // if (p.eatToken(.Keyword_else)) |else_token| { // const payload = try p.parsePayload(); // // const else_expr = try p.expectNode(parseTypeExpr, .{ // .ExpectedTypeExpr = .{ .token = p.tok_i }, // }); // // const else_node = try p.arena.allocator.create(Node.Else); // else_node.* = .{ // .else_token = else_token, // .payload = null, // .body = else_expr, // }; // // while_prefix.@"else" = else_node; // } // // return node; // } // // /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE // fn parseSwitchExpr(p: *Parser) !?*Node { // const switch_token = p.eatToken(.Keyword_switch) orelse return null; // _ = try p.expectToken(.LParen); // const expr_node = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RParen); // _ = try p.expectToken(.LBrace); // const cases = try p.parseSwitchProngList(); // defer p.gpa.free(cases); // const rbrace = try p.expectToken(.RBrace); // // const node = try Node.Switch.alloc(&p.arena.allocator, cases.len); // node.* = .{ // .switch_token = switch_token, // .expr = expr_node, // .cases_len = cases.len, // .rbrace = rbrace, // }; // std.mem.copy(*Node, node.cases(), cases); // return &node.base; // } // // /// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN // /// AsmOutput <- COLON AsmOutputList AsmInput? // /// AsmInput <- COLON AsmInputList AsmClobbers? // /// AsmClobbers <- COLON StringList // /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL? // fn parseAsmExpr(p: *Parser) !?*Node { // const asm_token = p.eatToken(.Keyword_asm) orelse return null; // const volatile_token = p.eatToken(.Keyword_volatile); // _ = try p.expectToken(.LParen); // const template = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // // var arena_outputs: []Node.Asm.Output = &[0]Node.Asm.Output{}; // var arena_inputs: []Node.Asm.Input = &[0]Node.Asm.Input{}; // var arena_clobbers: []*Node = &[0]*Node{}; // // if (p.eatToken(.Colon) != null) { // const outputs = try p.parseAsmOutputList(); // defer p.gpa.free(outputs); // arena_outputs = try p.arena.allocator.dupe(Node.Asm.Output, outputs); // // if (p.eatToken(.Colon) != null) { // const inputs = try p.parseAsmInputList(); // defer p.gpa.free(inputs); // arena_inputs = try p.arena.allocator.dupe(Node.Asm.Input, inputs); // // if (p.eatToken(.Colon) != null) { // const clobbers = try ListParseFn(*Node, parseStringLiteral)(p); // defer p.gpa.free(clobbers); // arena_clobbers = try p.arena.allocator.dupe(*Node, clobbers); // } // } // } // // const node = try p.arena.allocator.create(Node.Asm); // node.* = .{ // .asm_token = asm_token, // .volatile_token = volatile_token, // .template = template, // .outputs = arena_outputs, // .inputs = arena_inputs, // .clobbers = arena_clobbers, // .rparen = try p.expectToken(.RParen), // }; // // return &node.base; // } // // /// DOT IDENTIFIER // fn parseAnonLiteral(p: *Parser) !?*Node { // const dot = p.eatToken(.Period) orelse return null; // // // anon enum literal // if (p.eatToken(.Identifier)) |name| { // const node = try p.arena.allocator.create(Node.EnumLiteral); // node.* = .{ // .dot = dot, // .name = name, // }; // return &node.base; // } // // if (try p.parseAnonInitList(dot)) |node| { // return node; // } // // p.putBackToken(dot); // return null; // } // // /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN // fn parseAsmOutputItem(p: *Parser) !?Node.Asm.Output { // const lbracket = p.eatToken(.LBracket) orelse return null; // const name = try p.expectNode(parseIdentifier, .{ // .ExpectedIdentifier = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RBracket); // // const constraint = try p.expectNode(parseStringLiteral, .{ // .ExpectedStringLiteral = .{ .token = p.tok_i }, // }); // // _ = try p.expectToken(.LParen); // const kind: Node.Asm.Output.Kind = blk: { // if (p.eatToken(.Arrow) != null) { // const return_ident = try p.expectNode(parseTypeExpr, .{ // .ExpectedTypeExpr = .{ .token = p.tok_i }, // }); // break :blk .{ .Return = return_ident }; // } // const variable = try p.expectNode(parseIdentifier, .{ // .ExpectedIdentifier = .{ .token = p.tok_i }, // }); // break :blk .{ .Variable = variable.castTag(.Identifier).? }; // }; // const rparen = try p.expectToken(.RParen); // // return Node.Asm.Output{ // .lbracket = lbracket, // .symbolic_name = name, // .constraint = constraint, // .kind = kind, // .rparen = rparen, // }; // } // // /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN // fn parseAsmInputItem(p: *Parser) !?Node.Asm.Input { // const lbracket = p.eatToken(.LBracket) orelse return null; // const name = try p.expectNode(parseIdentifier, .{ // .ExpectedIdentifier = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RBracket); // // const constraint = try p.expectNode(parseStringLiteral, .{ // .ExpectedStringLiteral = .{ .token = p.tok_i }, // }); // // _ = try p.expectToken(.LParen); // const expr = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // const rparen = try p.expectToken(.RParen); // // return Node.Asm.Input{ // .lbracket = lbracket, // .symbolic_name = name, // .constraint = constraint, // .expr = expr, // .rparen = rparen, // }; // } // // /// BreakLabel <- COLON IDENTIFIER // fn parseBreakLabel(p: *Parser) !?TokenIndex { // _ = p.eatToken(.Colon) orelse return null; // const ident = try p.expectToken(.Identifier); // return ident; // } // // /// BlockLabel <- IDENTIFIER COLON // fn parseBlockLabel(p: *Parser, colon_token: *TokenIndex) ?TokenIndex { // const identifier = p.eatToken(.Identifier) orelse return null; // if (p.eatToken(.Colon)) |colon| { // colon_token.* = colon; // return identifier; // } // p.putBackToken(identifier); // return null; // } // // /// FieldInit <- DOT IDENTIFIER EQUAL Expr // fn parseFieldInit(p: *Parser) !?*Node { // const period_token = p.eatToken(.Period) orelse return null; // const name_token = p.eatToken(.Identifier) orelse { // // Because of anon literals `.{` is also valid. // p.putBackToken(period_token); // return null; // }; // const eq_token = p.eatToken(.Equal) orelse { // // `.Name` may also be an enum literal, which is a later rule. // p.putBackToken(name_token); // p.putBackToken(period_token); // return null; // }; // const expr_node = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // // const node = try p.arena.allocator.create(Node.FieldInitializer); // node.* = .{ // .period_token = period_token, // .name_token = name_token, // .expr = expr_node, // }; // return &node.base; // } // // /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN // fn parseWhileContinueExpr(p: *Parser) !?*Node { // _ = p.eatToken(.Colon) orelse return null; // _ = try p.expectToken(.LParen); // const node = try p.expectNode(parseAssignExpr, .{ // .ExpectedExprOrAssignment = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RParen); // return node; // } // // /// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN // fn parseLinkSection(p: *Parser) !?*Node { // _ = p.eatToken(.Keyword_linksection) orelse return null; // _ = try p.expectToken(.LParen); // const expr_node = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RParen); // return expr_node; // } // // /// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN // fn parseCallconv(p: *Parser) !?*Node { // _ = p.eatToken(.Keyword_callconv) orelse return null; // _ = try p.expectToken(.LParen); // const expr_node = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RParen); // return expr_node; // } // // /// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType // fn parseParamDecl(p: *Parser) !?Node.FnProto.ParamDecl { // const doc_comments = try p.parseDocComment(); // const noalias_token = p.eatToken(.Keyword_noalias); // const comptime_token = if (noalias_token == null) p.eatToken(.Keyword_comptime) else null; // const name_token = blk: { // const identifier = p.eatToken(.Identifier) orelse break :blk null; // if (p.eatToken(.Colon) != null) break :blk identifier; // p.putBackToken(identifier); // ParamType may also be an identifier // break :blk null; // }; // const param_type = (try p.parseParamType()) orelse { // // Only return cleanly if no keyword, identifier, or doc comment was found // if (noalias_token == null and // comptime_token == null and // name_token == null and // doc_comments == null) // { // return null; // } // try p.errors.append(p.gpa, .{ // .ExpectedParamType = .{ .token = p.tok_i }, // }); // return error.ParseError; // }; // // return Node.FnProto.ParamDecl{ // .doc_comments = doc_comments, // .comptime_token = comptime_token, // .noalias_token = noalias_token, // .name_token = name_token, // .param_type = param_type, // }; // } // // /// ParamType // /// <- Keyword_anytype // /// / DOT3 // /// / TypeExpr // fn parseParamType(p: *Parser) !?Node.FnProto.ParamDecl.ParamType { // // TODO cast from tuple to error union is broken // const P = Node.FnProto.ParamDecl.ParamType; // if (try p.parseAnyType()) |node| return P{ .any_type = node }; // if (try p.parseTypeExpr()) |node| return P{ .type_expr = node }; // return null; // } // // /// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? // fn parseIfPrefix(p: *Parser) !?*Node { // const if_token = p.eatToken(.Keyword_if) orelse return null; // _ = try p.expectToken(.LParen); // const condition = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RParen); // const payload = try p.parsePtrPayload(); // // const node = try p.arena.allocator.create(Node.If); // node.* = .{ // .if_token = if_token, // .condition = condition, // .payload = payload, // .body = undefined, // set by caller // .@"else" = null, // }; // return &node.base; // } // // /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? // fn parseWhilePrefix(p: *Parser) !?*Node { // const while_token = p.eatToken(.Keyword_while) orelse return null; // // _ = try p.expectToken(.LParen); // const condition = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RParen); // // const payload = try p.parsePtrPayload(); // const continue_expr = try p.parseWhileContinueExpr(); // // const node = try p.arena.allocator.create(Node.While); // node.* = .{ // .label = null, // .inline_token = null, // .while_token = while_token, // .condition = condition, // .payload = payload, // .continue_expr = continue_expr, // .body = undefined, // set by caller // .@"else" = null, // }; // return &node.base; // } // // /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload // fn parseForPrefix(p: *Parser) !?*Node { // const for_token = p.eatToken(.Keyword_for) orelse return null; // // _ = try p.expectToken(.LParen); // const array_expr = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RParen); // // const payload = try p.expectNode(parsePtrIndexPayload, .{ // .ExpectedPayload = .{ .token = p.tok_i }, // }); // // const node = try p.arena.allocator.create(Node.For); // node.* = .{ // .label = null, // .inline_token = null, // .for_token = for_token, // .array_expr = array_expr, // .payload = payload, // .body = undefined, // set by caller // .@"else" = null, // }; // return &node.base; // } // // /// Payload <- PIPE IDENTIFIER PIPE // fn parsePayload(p: *Parser) !?*Node { // const lpipe = p.eatToken(.Pipe) orelse return null; // const identifier = try p.expectNode(parseIdentifier, .{ // .ExpectedIdentifier = .{ .token = p.tok_i }, // }); // const rpipe = try p.expectToken(.Pipe); // // const node = try p.arena.allocator.create(Node.Payload); // node.* = .{ // .lpipe = lpipe, // .error_symbol = identifier, // .rpipe = rpipe, // }; // return &node.base; // } // // /// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE // fn parsePtrPayload(p: *Parser) !?*Node { // const lpipe = p.eatToken(.Pipe) orelse return null; // const asterisk = p.eatToken(.Asterisk); // const identifier = try p.expectNode(parseIdentifier, .{ // .ExpectedIdentifier = .{ .token = p.tok_i }, // }); // const rpipe = try p.expectToken(.Pipe); // // const node = try p.arena.allocator.create(Node.PointerPayload); // node.* = .{ // .lpipe = lpipe, // .ptr_token = asterisk, // .value_symbol = identifier, // .rpipe = rpipe, // }; // return &node.base; // } // // /// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE // fn parsePtrIndexPayload(p: *Parser) !?*Node { // const lpipe = p.eatToken(.Pipe) orelse return null; // const asterisk = p.eatToken(.Asterisk); // const identifier = try p.expectNode(parseIdentifier, .{ // .ExpectedIdentifier = .{ .token = p.tok_i }, // }); // // const index = if (p.eatToken(.Comma) == null) // null // else // try p.expectNode(parseIdentifier, .{ // .ExpectedIdentifier = .{ .token = p.tok_i }, // }); // // const rpipe = try p.expectToken(.Pipe); // // const node = try p.arena.allocator.create(Node.PointerIndexPayload); // node.* = .{ // .lpipe = lpipe, // .ptr_token = asterisk, // .value_symbol = identifier, // .index_symbol = index, // .rpipe = rpipe, // }; // return &node.base; // } // // /// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr // fn parseSwitchProng(p: *Parser) !?*Node { // const node = (try p.parseSwitchCase()) orelse return null; // const arrow = try p.expectToken(.EqualAngleBracketRight); // const payload = try p.parsePtrPayload(); // const expr = try p.expectNode(parseAssignExpr, .{ // .ExpectedExprOrAssignment = .{ .token = p.tok_i }, // }); // // const switch_case = node.cast(Node.SwitchCase).?; // switch_case.arrow_token = arrow; // switch_case.payload = payload; // switch_case.expr = expr; // // return node; // } // // /// SwitchCase // /// <- SwitchItem (COMMA SwitchItem)* COMMA? // /// / KEYWORD_else // fn parseSwitchCase(p: *Parser) !?*Node { // var list = std.ArrayList(*Node).init(p.gpa); // defer list.deinit(); // // if (try p.parseSwitchItem()) |first_item| { // try list.append(first_item); // while (p.eatToken(.Comma) != null) { // const next_item = (try p.parseSwitchItem()) orelse break; // try list.append(next_item); // } // } else if (p.eatToken(.Keyword_else)) |else_token| { // const else_node = try p.arena.allocator.create(Node.SwitchElse); // else_node.* = .{ // .token = else_token, // }; // try list.append(&else_node.base); // } else return null; // // const node = try Node.SwitchCase.alloc(&p.arena.allocator, list.items.len); // node.* = .{ // .items_len = list.items.len, // .arrow_token = undefined, // set by caller // .payload = null, // .expr = undefined, // set by caller // }; // std.mem.copy(*Node, node.items(), list.items); // return &node.base; // } // // /// SwitchItem <- Expr (DOT3 Expr)? // fn parseSwitchItem(p: *Parser) !?*Node { // const expr = (try p.parseExpr()) orelse return null; // if (p.eatToken(.Ellipsis3)) |token| { // const range_end = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // // const node = try p.arena.allocator.create(Node.SimpleInfixOp); // node.* = .{ // .base = Node{ .tag = .Range }, // .op_token = token, // .lhs = expr, // .rhs = range_end, // }; // return &node.base; // } // return expr; // } // // /// AssignOp // /// <- ASTERISKEQUAL // /// / SLASHEQUAL // /// / PERCENTEQUAL // /// / PLUSEQUAL // /// / MINUSEQUAL // /// / LARROW2EQUAL // /// / RARROW2EQUAL // /// / AMPERSANDEQUAL // /// / CARETEQUAL // /// / PIPEEQUAL // /// / ASTERISKPERCENTEQUAL // /// / PLUSPERCENTEQUAL // /// / MINUSPERCENTEQUAL // /// / EQUAL // fn parseAssignOp(p: *Parser) !?*Node { // const token = p.nextToken(); // const op: Node.Tag = switch (p.token_ids[token]) { // .AsteriskEqual => .AssignMul, // .SlashEqual => .AssignDiv, // .PercentEqual => .AssignMod, // .PlusEqual => .AssignAdd, // .MinusEqual => .AssignSub, // .AngleBracketAngleBracketLeftEqual => .AssignBitShiftLeft, // .AngleBracketAngleBracketRightEqual => .AssignBitShiftRight, // .AmpersandEqual => .AssignBitAnd, // .CaretEqual => .AssignBitXor, // .PipeEqual => .AssignBitOr, // .AsteriskPercentEqual => .AssignMulWrap, // .PlusPercentEqual => .AssignAddWrap, // .MinusPercentEqual => .AssignSubWrap, // .Equal => .Assign, // else => { // p.putBackToken(token); // return null; // }, // }; // // const node = try p.arena.allocator.create(Node.SimpleInfixOp); // node.* = .{ // .base = .{ .tag = op }, // .op_token = token, // .lhs = undefined, // set by caller // .rhs = undefined, // set by caller // }; // return &node.base; // } // // /// CompareOp // /// <- EQUALEQUAL // /// / EXCLAMATIONMARKEQUAL // /// / LARROW // /// / RARROW // /// / LARROWEQUAL // /// / RARROWEQUAL // fn parseCompareOp(p: *Parser) !?*Node { // const token = p.nextToken(); // const op: Node.Tag = switch (p.token_ids[token]) { // .EqualEqual => .EqualEqual, // .BangEqual => .BangEqual, // .AngleBracketLeft => .LessThan, // .AngleBracketRight => .GreaterThan, // .AngleBracketLeftEqual => .LessOrEqual, // .AngleBracketRightEqual => .GreaterOrEqual, // else => { // p.putBackToken(token); // return null; // }, // }; // // return p.createInfixOp(token, op); // } // // /// BitwiseOp // /// <- AMPERSAND // /// / CARET // /// / PIPE // /// / KEYWORD_orelse // /// / KEYWORD_catch Payload? // fn parseBitwiseOp(p: *Parser) !?*Node { // const token = p.nextToken(); // const op: Node.Tag = switch (p.token_ids[token]) { // .Ampersand => .BitAnd, // .Caret => .BitXor, // .Pipe => .BitOr, // .Keyword_orelse => .OrElse, // .Keyword_catch => { // const payload = try p.parsePayload(); // const node = try p.arena.allocator.create(Node.Catch); // node.* = .{ // .op_token = token, // .lhs = undefined, // set by caller // .rhs = undefined, // set by caller // .payload = payload, // }; // return &node.base; // }, // else => { // p.putBackToken(token); // return null; // }, // }; // // return p.createInfixOp(token, op); // } // // /// BitShiftOp // /// <- LARROW2 // /// / RARROW2 // fn parseBitShiftOp(p: *Parser) !?*Node { // const token = p.nextToken(); // const op: Node.Tag = switch (p.token_ids[token]) { // .AngleBracketAngleBracketLeft => .BitShiftLeft, // .AngleBracketAngleBracketRight => .BitShiftRight, // else => { // p.putBackToken(token); // return null; // }, // }; // // return p.createInfixOp(token, op); // } // // /// AdditionOp // /// <- PLUS // /// / MINUS // /// / PLUS2 // /// / PLUSPERCENT // /// / MINUSPERCENT // fn parseAdditionOp(p: *Parser) !?*Node { // const token = p.nextToken(); // const op: Node.Tag = switch (p.token_ids[token]) { // .Plus => .Add, // .Minus => .Sub, // .PlusPlus => .ArrayCat, // .PlusPercent => .AddWrap, // .MinusPercent => .SubWrap, // else => { // p.putBackToken(token); // return null; // }, // }; // // return p.createInfixOp(token, op); // } // // /// MultiplyOp // /// <- PIPE2 // /// / ASTERISK // /// / SLASH // /// / PERCENT // /// / ASTERISK2 // /// / ASTERISKPERCENT // fn parseMultiplyOp(p: *Parser) !?*Node { // const token = p.nextToken(); // const op: Node.Tag = switch (p.token_ids[token]) { // .PipePipe => .MergeErrorSets, // .Asterisk => .Mul, // .Slash => .Div, // .Percent => .Mod, // .AsteriskAsterisk => .ArrayMult, // .AsteriskPercent => .MulWrap, // else => { // p.putBackToken(token); // return null; // }, // }; // // return p.createInfixOp(token, op); // } // // /// PrefixOp // /// <- EXCLAMATIONMARK // /// / MINUS // /// / TILDE // /// / MINUSPERCENT // /// / AMPERSAND // /// / KEYWORD_try // /// / KEYWORD_await // fn parsePrefixOp(p: *Parser) !?*Node { // const token = p.nextToken(); // switch (p.token_ids[token]) { // .Bang => return p.allocSimplePrefixOp(.BoolNot, token), // .Minus => return p.allocSimplePrefixOp(.Negation, token), // .Tilde => return p.allocSimplePrefixOp(.BitNot, token), // .MinusPercent => return p.allocSimplePrefixOp(.NegationWrap, token), // .Ampersand => return p.allocSimplePrefixOp(.AddressOf, token), // .Keyword_try => return p.allocSimplePrefixOp(.Try, token), // .Keyword_await => return p.allocSimplePrefixOp(.Await, token), // else => { // p.putBackToken(token); // return null; // }, // } // } // // fn allocSimplePrefixOp(p: *Parser, comptime tag: Node.Tag, token: TokenIndex) !?*Node { // const node = try p.arena.allocator.create(Node.SimplePrefixOp); // node.* = .{ // .base = .{ .tag = tag }, // .op_token = token, // .rhs = undefined, // set by caller // }; // return &node.base; // } // // // TODO: ArrayTypeStart is either an array or a slice, but const/allowzero only work on // // pointers. Consider updating this rule: // // ... // // / ArrayTypeStart // // / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* // // / PtrTypeStart ... // // /// PrefixTypeOp // /// <- QUESTIONMARK // /// / KEYWORD_anyframe MINUSRARROW // /// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* // /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* // fn parsePrefixTypeOp(p: *Parser) !?*Node { // if (p.eatToken(.QuestionMark)) |token| { // const node = try p.arena.allocator.create(Node.SimplePrefixOp); // node.* = .{ // .base = .{ .tag = .OptionalType }, // .op_token = token, // .rhs = undefined, // set by caller // }; // return &node.base; // } // // if (p.eatToken(.Keyword_anyframe)) |token| { // const arrow = p.eatToken(.Arrow) orelse { // p.putBackToken(token); // return null; // }; // const node = try p.arena.allocator.create(Node.AnyFrameType); // node.* = .{ // .anyframe_token = token, // .result = .{ // .arrow_token = arrow, // .return_type = undefined, // set by caller // }, // }; // return &node.base; // } // // if (try p.parsePtrTypeStart()) |node| { // // If the token encountered was **, there will be two nodes instead of one. // // The attributes should be applied to the rightmost operator. // var ptr_info = if (node.cast(Node.PtrType)) |ptr_type| // if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) // &ptr_type.rhs.cast(Node.PtrType).?.ptr_info // else // &ptr_type.ptr_info // else if (node.cast(Node.SliceType)) |slice_type| // &slice_type.ptr_info // else // unreachable; // // while (true) { // if (p.eatToken(.Keyword_align)) |align_token| { // const lparen = try p.expectToken(.LParen); // const expr_node = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // // // Optional bit range // const bit_range = if (p.eatToken(.Colon)) |_| bit_range_value: { // const range_start = try p.expectNode(parseIntegerLiteral, .{ // .ExpectedIntegerLiteral = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.Colon); // const range_end = try p.expectNode(parseIntegerLiteral, .{ // .ExpectedIntegerLiteral = .{ .token = p.tok_i }, // }); // // break :bit_range_value ast.PtrInfo.Align.BitRange{ // .start = range_start, // .end = range_end, // }; // } else null; // _ = try p.expectToken(.RParen); // // if (ptr_info.align_info != null) { // try p.errors.append(p.gpa, .{ // .ExtraAlignQualifier = .{ .token = p.tok_i - 1 }, // }); // continue; // } // // ptr_info.align_info = ast.PtrInfo.Align{ // .node = expr_node, // .bit_range = bit_range, // }; // // continue; // } // if (p.eatToken(.Keyword_const)) |const_token| { // if (ptr_info.const_token != null) { // try p.errors.append(p.gpa, .{ // .ExtraConstQualifier = .{ .token = p.tok_i - 1 }, // }); // continue; // } // ptr_info.const_token = const_token; // continue; // } // if (p.eatToken(.Keyword_volatile)) |volatile_token| { // if (ptr_info.volatile_token != null) { // try p.errors.append(p.gpa, .{ // .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 }, // }); // continue; // } // ptr_info.volatile_token = volatile_token; // continue; // } // if (p.eatToken(.Keyword_allowzero)) |allowzero_token| { // if (ptr_info.allowzero_token != null) { // try p.errors.append(p.gpa, .{ // .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 }, // }); // continue; // } // ptr_info.allowzero_token = allowzero_token; // continue; // } // break; // } // // return node; // } // // if (try p.parseArrayTypeStart()) |node| { // if (node.cast(Node.SliceType)) |slice_type| { // // Collect pointer qualifiers in any order, but disallow duplicates // while (true) { // if (try p.parseByteAlign()) |align_expr| { // if (slice_type.ptr_info.align_info != null) { // try p.errors.append(p.gpa, .{ // .ExtraAlignQualifier = .{ .token = p.tok_i - 1 }, // }); // continue; // } // slice_type.ptr_info.align_info = ast.PtrInfo.Align{ // .node = align_expr, // .bit_range = null, // }; // continue; // } // if (p.eatToken(.Keyword_const)) |const_token| { // if (slice_type.ptr_info.const_token != null) { // try p.errors.append(p.gpa, .{ // .ExtraConstQualifier = .{ .token = p.tok_i - 1 }, // }); // continue; // } // slice_type.ptr_info.const_token = const_token; // continue; // } // if (p.eatToken(.Keyword_volatile)) |volatile_token| { // if (slice_type.ptr_info.volatile_token != null) { // try p.errors.append(p.gpa, .{ // .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 }, // }); // continue; // } // slice_type.ptr_info.volatile_token = volatile_token; // continue; // } // if (p.eatToken(.Keyword_allowzero)) |allowzero_token| { // if (slice_type.ptr_info.allowzero_token != null) { // try p.errors.append(p.gpa, .{ // .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 }, // }); // continue; // } // slice_type.ptr_info.allowzero_token = allowzero_token; // continue; // } // break; // } // } // return node; // } // // return null; // } // // /// SuffixOp // /// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET // /// / DOT IDENTIFIER // /// / DOTASTERISK // /// / DOTQUESTIONMARK // fn parseSuffixOp(p: *Parser, lhs: *Node) !?*Node { // if (p.eatToken(.LBracket)) |_| { // const index_expr = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // // if (p.eatToken(.Ellipsis2) != null) { // const end_expr = try p.parseExpr(); // const sentinel: ?*Node = if (p.eatToken(.Colon) != null) // try p.parseExpr() // else // null; // const rtoken = try p.expectToken(.RBracket); // const node = try p.arena.allocator.create(Node.Slice); // node.* = .{ // .lhs = lhs, // .rtoken = rtoken, // .start = index_expr, // .end = end_expr, // .sentinel = sentinel, // }; // return &node.base; // } // // const rtoken = try p.expectToken(.RBracket); // const node = try p.arena.allocator.create(Node.ArrayAccess); // node.* = .{ // .lhs = lhs, // .rtoken = rtoken, // .index_expr = index_expr, // }; // return &node.base; // } // // if (p.eatToken(.PeriodAsterisk)) |period_asterisk| { // const node = try p.arena.allocator.create(Node.SimpleSuffixOp); // node.* = .{ // .base = .{ .tag = .Deref }, // .lhs = lhs, // .rtoken = period_asterisk, // }; // return &node.base; // } // // if (p.eatToken(.Period)) |period| { // if (try p.parseIdentifier()) |identifier| { // const node = try p.arena.allocator.create(Node.SimpleInfixOp); // node.* = .{ // .base = Node{ .tag = .Period }, // .op_token = period, // .lhs = lhs, // .rhs = identifier, // }; // return &node.base; // } // if (p.eatToken(.QuestionMark)) |question_mark| { // const node = try p.arena.allocator.create(Node.SimpleSuffixOp); // node.* = .{ // .base = .{ .tag = .UnwrapOptional }, // .lhs = lhs, // .rtoken = question_mark, // }; // return &node.base; // } // try p.errors.append(p.gpa, .{ // .ExpectedSuffixOp = .{ .token = p.tok_i }, // }); // return null; // } // // return null; // } // // /// FnCallArguments <- LPAREN ExprList RPAREN // /// ExprList <- (Expr COMMA)* Expr? // fn parseFnCallArguments(p: *Parser) !?AnnotatedParamList { // if (p.eatToken(.LParen) == null) return null; // const list = try ListParseFn(*Node, parseExpr)(p); // errdefer p.gpa.free(list); // const rparen = try p.expectToken(.RParen); // return AnnotatedParamList{ .list = list, .rparen = rparen }; // } // // const AnnotatedParamList = struct { // list: []*Node, // rparen: TokenIndex, // }; // // /// ArrayTypeStart <- LBRACKET Expr? RBRACKET // fn parseArrayTypeStart(p: *Parser) !?*Node { // const lbracket = p.eatToken(.LBracket) orelse return null; // const expr = try p.parseExpr(); // const sentinel = if (p.eatToken(.Colon)) |_| // try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }) // else // null; // const rbracket = try p.expectToken(.RBracket); // // if (expr) |len_expr| { // if (sentinel) |s| { // const node = try p.arena.allocator.create(Node.ArrayTypeSentinel); // node.* = .{ // .op_token = lbracket, // .rhs = undefined, // set by caller // .len_expr = len_expr, // .sentinel = s, // }; // return &node.base; // } else { // const node = try p.arena.allocator.create(Node.ArrayType); // node.* = .{ // .op_token = lbracket, // .rhs = undefined, // set by caller // .len_expr = len_expr, // }; // return &node.base; // } // } // // const node = try p.arena.allocator.create(Node.SliceType); // node.* = .{ // .op_token = lbracket, // .rhs = undefined, // set by caller // .ptr_info = .{ .sentinel = sentinel }, // }; // return &node.base; // } // // /// PtrTypeStart // /// <- ASTERISK // /// / ASTERISK2 // /// / PTRUNKNOWN // /// / PTRC // fn parsePtrTypeStart(p: *Parser) !?*Node { // if (p.eatToken(.Asterisk)) |asterisk| { // const sentinel = if (p.eatToken(.Colon)) |_| // try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }) // else // null; // const node = try p.arena.allocator.create(Node.PtrType); // node.* = .{ // .op_token = asterisk, // .rhs = undefined, // set by caller // .ptr_info = .{ .sentinel = sentinel }, // }; // return &node.base; // } // // if (p.eatToken(.AsteriskAsterisk)) |double_asterisk| { // const node = try p.arena.allocator.create(Node.PtrType); // node.* = .{ // .op_token = double_asterisk, // .rhs = undefined, // set by caller // }; // // // Special case for **, which is its own token // const child = try p.arena.allocator.create(Node.PtrType); // child.* = .{ // .op_token = double_asterisk, // .rhs = undefined, // set by caller // }; // node.rhs = &child.base; // // return &node.base; // } // if (p.eatToken(.LBracket)) |lbracket| { // const asterisk = p.eatToken(.Asterisk) orelse { // p.putBackToken(lbracket); // return null; // }; // if (p.eatToken(.Identifier)) |ident| { // const token_loc = p.token_locs[ident]; // const token_slice = p.source[token_loc.start..token_loc.end]; // if (!std.mem.eql(u8, token_slice, "c")) { // p.putBackToken(ident); // } else { // _ = try p.expectToken(.RBracket); // const node = try p.arena.allocator.create(Node.PtrType); // node.* = .{ // .op_token = lbracket, // .rhs = undefined, // set by caller // }; // return &node.base; // } // } // const sentinel = if (p.eatToken(.Colon)) |_| // try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }) // else // null; // _ = try p.expectToken(.RBracket); // const node = try p.arena.allocator.create(Node.PtrType); // node.* = .{ // .op_token = lbracket, // .rhs = undefined, // set by caller // .ptr_info = .{ .sentinel = sentinel }, // }; // return &node.base; // } // return null; // } // // /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE // fn parseContainerDeclAuto(p: *Parser) !?*Node { // const container_decl_type = (try p.parseContainerDeclType()) orelse return null; // const lbrace = try p.expectToken(.LBrace); // const members = try p.parseContainerMembers(false); // defer p.gpa.free(members); // const rbrace = try p.expectToken(.RBrace); // // const members_len = @intCast(NodeIndex, members.len); // const node = try Node.ContainerDecl.alloc(&p.arena.allocator, members_len); // node.* = .{ // .layout_token = null, // .kind_token = container_decl_type.kind_token, // .init_arg_expr = container_decl_type.init_arg_expr, // .fields_and_decls_len = members_len, // .lbrace_token = lbrace, // .rbrace_token = rbrace, // }; // std.mem.copy(*Node, node.fieldsAndDecls(), members); // return &node.base; // } // // /// Holds temporary data until we are ready to construct the full ContainerDecl AST node. // const ContainerDeclType = struct { // kind_token: TokenIndex, // init_arg_expr: Node.ContainerDecl.InitArg, // }; // // /// ContainerDeclType // /// <- KEYWORD_struct // /// / KEYWORD_enum (LPAREN Expr RPAREN)? // /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? // fn parseContainerDeclType(p: *Parser) !?ContainerDeclType { // const kind_token = p.nextToken(); // // const init_arg_expr = switch (p.token_ids[kind_token]) { // .Keyword_struct => Node.ContainerDecl.InitArg{ .None = {} }, // .Keyword_enum => blk: { // if (p.eatToken(.LParen) != null) { // const expr = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RParen); // break :blk Node.ContainerDecl.InitArg{ .Type = expr }; // } // break :blk Node.ContainerDecl.InitArg{ .None = {} }; // }, // .Keyword_union => blk: { // if (p.eatToken(.LParen) != null) { // if (p.eatToken(.Keyword_enum) != null) { // if (p.eatToken(.LParen) != null) { // const expr = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RParen); // _ = try p.expectToken(.RParen); // break :blk Node.ContainerDecl.InitArg{ .Enum = expr }; // } // _ = try p.expectToken(.RParen); // break :blk Node.ContainerDecl.InitArg{ .Enum = null }; // } // const expr = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RParen); // break :blk Node.ContainerDecl.InitArg{ .Type = expr }; // } // break :blk Node.ContainerDecl.InitArg{ .None = {} }; // }, // else => { // p.putBackToken(kind_token); // return null; // }, // }; // // return ContainerDeclType{ // .kind_token = kind_token, // .init_arg_expr = init_arg_expr, // }; // } // // /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN // fn parseByteAlign(p: *Parser) !?*Node { // _ = p.eatToken(.Keyword_align) orelse return null; // _ = try p.expectToken(.LParen); // const expr = try p.expectNode(parseExpr, .{ // .ExpectedExpr = .{ .token = p.tok_i }, // }); // _ = try p.expectToken(.RParen); // return expr; // } // // /// IdentifierList <- (IDENTIFIER COMMA)* IDENTIFIER? // /// Only ErrorSetDecl parses an IdentifierList // fn parseErrorTagList(p: *Parser) ![]*Node { // return ListParseFn(*Node, parseErrorTag)(p); // } // // /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng? // fn parseSwitchProngList(p: *Parser) ![]*Node { // return ListParseFn(*Node, parseSwitchProng)(p); // } // // /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem? // fn parseAsmOutputList(p: *Parser) Error![]Node.Asm.Output { // return ListParseFn(Node.Asm.Output, parseAsmOutputItem)(p); // } // // /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem? // fn parseAsmInputList(p: *Parser) Error![]Node.Asm.Input { // return ListParseFn(Node.Asm.Input, parseAsmInputItem)(p); // } // // /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl? // fn parseParamDeclList(p: *Parser) ![]Node.FnProto.ParamDecl { // return ListParseFn(Node.FnProto.ParamDecl, parseParamDecl)(p); // } // // const NodeParseFn = fn (p: *Parser) Error!?*Node; // // fn ListParseFn(comptime E: type, comptime nodeParseFn: anytype) ParseFn([]E) { // return struct { // pub fn parse(p: *Parser) ![]E { // var list = std.ArrayList(E).init(p.gpa); // defer list.deinit(); // // while (try nodeParseFn(p)) |item| { // try list.append(item); // // switch (p.token_ids[p.tok_i]) { // .Comma => _ = p.nextToken(), // // all possible delimiters // .Colon, .RParen, .RBrace, .RBracket => break, // else => { // // this is likely just a missing comma, // // continue parsing this list and give an error // try p.errors.append(p.gpa, .{ // .ExpectedToken = .{ .token = p.tok_i, .expected_id = .Comma }, // }); // }, // } // } // return list.toOwnedSlice(); // } // }.parse; // } // // fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.Tag) NodeParseFn { // return struct { // pub fn parse(p: *Parser) Error!?*Node { // const op_token = if (token == .Keyword_and) switch (p.token_ids[p.tok_i]) { // .Keyword_and => p.nextToken(), // .Invalid_ampersands => blk: { // try p.errors.append(p.gpa, .{ // .InvalidAnd = .{ .token = p.tok_i }, // }); // break :blk p.nextToken(); // }, // else => return null, // } else p.eatToken(token) orelse return null; // // const node = try p.arena.allocator.create(Node.SimpleInfixOp); // node.* = .{ // .base = .{ .tag = op }, // .op_token = op_token, // .lhs = undefined, // set by caller // .rhs = undefined, // set by caller // }; // return &node.base; // } // }.parse; // } // // // Helper parsers not included in the grammar // // fn parseBuiltinCall(p: *Parser) !?*Node { // const token = p.eatToken(.Builtin) orelse return null; // const params = (try p.parseFnCallArguments()) orelse { // try p.errors.append(p.gpa, .{ // .ExpectedParamList = .{ .token = p.tok_i }, // }); // // // lets pretend this was an identifier so we can continue parsing // const node = try p.arena.allocator.create(Node.OneToken); // node.* = .{ // .base = .{ .tag = .Identifier }, // .token = token, // }; // return &node.base; // }; // defer p.gpa.free(params.list); // // const node = try Node.BuiltinCall.alloc(&p.arena.allocator, params.list.len); // node.* = .{ // .builtin_token = token, // .params_len = params.list.len, // .rparen_token = params.rparen, // }; // std.mem.copy(*Node, node.params(), params.list); // return &node.base; // } // // fn parseErrorTag(p: *Parser) !?*Node { // const doc_comments = try p.parseDocComment(); // no need to rewind on failure // const token = p.eatToken(.Identifier) orelse return null; // // const node = try p.arena.allocator.create(Node.ErrorTag); // node.* = .{ // .doc_comments = doc_comments, // .name_token = token, // }; // return &node.base; // } // // fn parseIdentifier(p: *Parser) !?*Node { // const token = p.eatToken(.Identifier) orelse return null; // const node = try p.arena.allocator.create(Node.OneToken); // node.* = .{ // .base = .{ .tag = .Identifier }, // .token = token, // }; // return &node.base; // } // // fn parseAnyType(p: *Parser) !?*Node { // const token = p.eatToken(.Keyword_anytype) orelse // p.eatToken(.Keyword_var) orelse return null; // TODO remove in next release cycle // const node = try p.arena.allocator.create(Node.OneToken); // node.* = .{ // .base = .{ .tag = .AnyType }, // .token = token, // }; // return &node.base; // } // // fn createLiteral(p: *Parser, tag: ast.Node.Tag, token: TokenIndex) !*Node { // const result = try p.arena.allocator.create(Node.OneToken); // result.* = .{ // .base = .{ .tag = tag }, // .token = token, // }; // return &result.base; // } // // fn parseStringLiteralSingle(p: *Parser) !?*Node { // if (p.eatToken(.StringLiteral)) |token| { // const node = try p.arena.allocator.create(Node.OneToken); // node.* = .{ // .base = .{ .tag = .StringLiteral }, // .token = token, // }; // return &node.base; // } // return null; // } // // // string literal or multiline string literal // fn parseStringLiteral(p: *Parser) !?*Node { // if (try p.parseStringLiteralSingle()) |node| return node; // // if (p.eatToken(.MultilineStringLiteralLine)) |first_line| { // const start_tok_i = p.tok_i; // var tok_i = start_tok_i; // var count: usize = 1; // including first_line // while (true) : (tok_i += 1) { // switch (p.token_ids[tok_i]) { // .LineComment => continue, // .MultilineStringLiteralLine => count += 1, // else => break, // } // } // // const node = try Node.MultilineStringLiteral.alloc(&p.arena.allocator, count); // node.* = .{ .lines_len = count }; // const lines = node.lines(); // tok_i = start_tok_i; // lines[0] = first_line; // count = 1; // while (true) : (tok_i += 1) { // switch (p.token_ids[tok_i]) { // .LineComment => continue, // .MultilineStringLiteralLine => { // lines[count] = tok_i; // count += 1; // }, // else => break, // } // } // p.tok_i = tok_i; // return &node.base; // } // // return null; // } // // fn parseIntegerLiteral(p: *Parser) !?*Node { // const token = p.eatToken(.IntegerLiteral) orelse return null; // const node = try p.arena.allocator.create(Node.OneToken); // node.* = .{ // .base = .{ .tag = .IntegerLiteral }, // .token = token, // }; // return &node.base; // } // // fn parseFloatLiteral(p: *Parser) !?*Node { // const token = p.eatToken(.FloatLiteral) orelse return null; // const node = try p.arena.allocator.create(Node.OneToken); // node.* = .{ // .base = .{ .tag = .FloatLiteral }, // .token = token, // }; // return &node.base; // } // // fn parseTry(p: *Parser) !?*Node { // const token = p.eatToken(.Keyword_try) orelse return null; // const node = try p.arena.allocator.create(Node.SimplePrefixOp); // node.* = .{ // .base = .{ .tag = .Try }, // .op_token = token, // .rhs = undefined, // set by caller // }; // return &node.base; // } // // /// IfPrefix Body (KEYWORD_else Payload? Body)? // fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !?*Node { // const node = (try p.parseIfPrefix()) orelse return null; // const if_prefix = node.cast(Node.If).?; // // if_prefix.body = try p.expectNode(bodyParseFn, .{ // .InvalidToken = .{ .token = p.tok_i }, // }); // // const else_token = p.eatToken(.Keyword_else) orelse return node; // const payload = try p.parsePayload(); // const else_expr = try p.expectNode(bodyParseFn, .{ // .InvalidToken = .{ .token = p.tok_i }, // }); // const else_node = try p.arena.allocator.create(Node.Else); // else_node.* = .{ // .else_token = else_token, // .payload = payload, // .body = else_expr, // }; // if_prefix.@"else" = else_node; // // return node; // } // // /// Eat a multiline doc comment // fn parseDocComment(p: *Parser) !?*Node.DocComment { // if (p.eatToken(.DocComment)) |first_line| { // while (p.eatToken(.DocComment)) |_| {} // const node = try p.arena.allocator.create(Node.DocComment); // node.* = .{ .first_line = first_line }; // return node; // } // return null; // } // // fn tokensOnSameLine(p: *Parser, token1: TokenIndex, token2: TokenIndex) bool { // return std.mem.indexOfScalar(u8, p.source[p.token_locs[token1].end..p.token_locs[token2].start], '\n') == null; // } // // /// Eat a single-line doc comment on the same line as another node // fn parseAppendedDocComment(p: *Parser, after_token: TokenIndex) !?*Node.DocComment { // const comment_token = p.eatToken(.DocComment) orelse return null; // if (p.tokensOnSameLine(after_token, comment_token)) { // const node = try p.arena.allocator.create(Node.DocComment); // node.* = .{ .first_line = comment_token }; // return node; // } // p.putBackToken(comment_token); // return null; // } // // /// Op* Child // fn parsePrefixOpExpr(p: *Parser, comptime opParseFn: NodeParseFn, comptime childParseFn: NodeParseFn) Error!?*Node { // if (try opParseFn(p)) |first_op| { // var rightmost_op = first_op; // while (true) { // switch (rightmost_op.tag) { // .AddressOf, // .Await, // .BitNot, // .BoolNot, // .OptionalType, // .Negation, // .NegationWrap, // .Resume, // .Try, // => { // if (try opParseFn(p)) |rhs| { // rightmost_op.cast(Node.SimplePrefixOp).?.rhs = rhs; // rightmost_op = rhs; // } else break; // }, // .ArrayType => { // if (try opParseFn(p)) |rhs| { // rightmost_op.cast(Node.ArrayType).?.rhs = rhs; // rightmost_op = rhs; // } else break; // }, // .ArrayTypeSentinel => { // if (try opParseFn(p)) |rhs| { // rightmost_op.cast(Node.ArrayTypeSentinel).?.rhs = rhs; // rightmost_op = rhs; // } else break; // }, // .SliceType => { // if (try opParseFn(p)) |rhs| { // rightmost_op.cast(Node.SliceType).?.rhs = rhs; // rightmost_op = rhs; // } else break; // }, // .PtrType => { // var ptr_type = rightmost_op.cast(Node.PtrType).?; // // If the token encountered was **, there will be two nodes // if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) { // rightmost_op = ptr_type.rhs; // ptr_type = rightmost_op.cast(Node.PtrType).?; // } // if (try opParseFn(p)) |rhs| { // ptr_type.rhs = rhs; // rightmost_op = rhs; // } else break; // }, // .AnyFrameType => { // const prom = rightmost_op.cast(Node.AnyFrameType).?; // if (try opParseFn(p)) |rhs| { // prom.result.?.return_type = rhs; // rightmost_op = rhs; // } else break; // }, // else => unreachable, // } // } // // // If any prefix op existed, a child node on the RHS is required // switch (rightmost_op.tag) { // .AddressOf, // .Await, // .BitNot, // .BoolNot, // .OptionalType, // .Negation, // .NegationWrap, // .Resume, // .Try, // => { // const prefix_op = rightmost_op.cast(Node.SimplePrefixOp).?; // prefix_op.rhs = try p.expectNode(childParseFn, .{ // .InvalidToken = .{ .token = p.tok_i }, // }); // }, // .ArrayType => { // const prefix_op = rightmost_op.cast(Node.ArrayType).?; // prefix_op.rhs = try p.expectNode(childParseFn, .{ // .InvalidToken = .{ .token = p.tok_i }, // }); // }, // .ArrayTypeSentinel => { // const prefix_op = rightmost_op.cast(Node.ArrayTypeSentinel).?; // prefix_op.rhs = try p.expectNode(childParseFn, .{ // .InvalidToken = .{ .token = p.tok_i }, // }); // }, // .PtrType => { // const prefix_op = rightmost_op.cast(Node.PtrType).?; // prefix_op.rhs = try p.expectNode(childParseFn, .{ // .InvalidToken = .{ .token = p.tok_i }, // }); // }, // .SliceType => { // const prefix_op = rightmost_op.cast(Node.SliceType).?; // prefix_op.rhs = try p.expectNode(childParseFn, .{ // .InvalidToken = .{ .token = p.tok_i }, // }); // }, // .AnyFrameType => { // const prom = rightmost_op.cast(Node.AnyFrameType).?; // prom.result.?.return_type = try p.expectNode(childParseFn, .{ // .InvalidToken = .{ .token = p.tok_i }, // }); // }, // else => unreachable, // } // // return first_op; // } // // // Otherwise, the child node is optional // return childParseFn(p); // } // // /// Child (Op Child)* // /// Child (Op Child)? // fn parseBinOpExpr( // p: *Parser, // opParseFn: NodeParseFn, // childParseFn: NodeParseFn, // chain: enum { // Once, // Infinitely, // }, // ) Error!?*Node { // var res = (try childParseFn(p)) orelse return null; // // while (try opParseFn(p)) |node| { // const right = try p.expectNode(childParseFn, .{ // .InvalidToken = .{ .token = p.tok_i }, // }); // const left = res; // res = node; // // if (node.castTag(.Catch)) |op| { // op.lhs = left; // op.rhs = right; // } else if (node.cast(Node.SimpleInfixOp)) |op| { // op.lhs = left; // op.rhs = right; // } // // switch (chain) { // .Once => break, // .Infinitely => continue, // } // } // // return res; // } // // fn createInfixOp(p: *Parser, op_token: TokenIndex, tag: Node.Tag) !*Node { // const node = try p.arena.allocator.create(Node.SimpleInfixOp); // node.* = .{ // .base = Node{ .tag = tag }, // .op_token = op_token, // .lhs = undefined, // set by caller // .rhs = undefined, // set by caller // }; // return &node.base; // } // fn eatToken(p: *Parser, id: Token.Id) ?TokenIndex { return if (p.token_ids[p.tok_i] == id) p.nextToken() else null; } // expectToken: either returns the token or an error. fn expectToken(p: *Parser, id: Token.Id) Error!TokenIndex { return (try p.expectTokenRecoverable(id)) orelse error.ParseError; } // expectTokenRecoverable: either returns the token or null if not the one expected. // Also, appends the error inside p.errors. fn expectTokenRecoverable(p: *Parser, id: Token.Id) !?TokenIndex { const token = p.nextToken(); if (p.token_ids[token] != id) { try p.errors.append(p.gpa, .{ .ExpectedToken = .{ .token = token, .expected_id = id }, }); // go back so that we can recover properly p.putBackToken(token); return null; } return token; } // nextToken: provide the TokenIndex of the current token, but increases the tok_i // inside the Parser structure. fn nextToken(p: *Parser) TokenIndex { const result = p.tok_i; p.tok_i += 1; assert(p.token_ids[result] != .LineComment); if (p.tok_i >= p.token_ids.len) return result; while (true) { if (p.token_ids[p.tok_i] != .LineComment) return result; p.tok_i += 1; } } // putBackToken: come back one token (except for comment lines which are ignored). // Example: we have a function searching for a declaration, // the function read a token "my-variable" so it returns after putting back the token. // Caller now have the start of a declaration in its parsing structure. fn putBackToken(p: *Parser, putting_back: TokenIndex) void { while (p.tok_i > 0) { p.tok_i -= 1; if (p.token_ids[p.tok_i] == .LineComment) continue; assert(putting_back == p.tok_i); return; } } /// TODO Delete this function. I don't like the inversion of control. fn expectNode( p: *Parser, parseFn: NodeParseFn, /// if parsing fails err: AstError, ) Error!*Node { return (try p.expectNodeRecoverable(parseFn, err)) orelse return error.ParseError; } /// TODO Delete this function. I don't like the inversion of control. fn expectNodeRecoverable( p: *Parser, parseFn: NodeParseFn, /// if parsing fails err: AstError, ) !?*Node { return (try parseFn(p)) orelse { try p.errors.append(p.gpa, err); return null; }; } }; fn ParseFn(comptime T: type) type { return fn (p: *Parser) Error!T; } test "std.zig.parser" { _ = @import("parser_test.zig"); }