diff --git a/doc/langref/grammar.peg b/doc/langref/grammar.peg index abb9a96e6b1efe8b7467f953f5849ebd58aa053a..b4831516d99f03dec90e2a9794946b5e0ee7b7a0 100644 --- a/doc/langref/grammar.peg +++ b/doc/langref/grammar.peg @@ -1,3 +1,41 @@ +# This grammar is intended to be LL(k) at the token level. +# In other words, whenever the parser is required to make a choice it must be +# able to choose "correctly" with constant token lookahead. This means that the +# handwritten recursive descent parser for this grammar can be implemented with +# worst-case linear run time rather than worst-case exponential runtime. +# +# To ensure this property is upheld, care must be taken to avoid requiring +# unbounded token lookahead in the grammar. To illustrate this, consider the +# following examples requiring unbounded lookahead: +# +# This is rather subtle as it looks fine at first glance: +# +# SingleAssignExpr <- Expr (AssignOp Expr)? +# +# Consider the case where the AssignOp token matches but Expr after it does +# not match. The parser would attempt to parse the Expr after the AssignOp, +# fail, then backtrack and succeed the SingleAssignExpr rule matching only +# the first Expr. This is an example of unbounded lookahead. The rule must +# be rewritten so that if the AssignOp token is not followed by an Expr the +# whole SingleAssignExpr rule fails to match: +# +# SingleAssignExpr <- Expr (AssignOp Expr / !AssignOp) +# +# The pattern is similar for e.g. the * operator. The following rule requires +# unbounded lookahead: +# +# BoolOrExpr <- BoolAndExpr (OrOp BoolAndExpr)* +# +# and can be fixed to require only bounded lookahead by adding a !OrOp: +# +# BoolOrExpr <- BoolAndExpr (OrOp BoolAndExpr)* !OrOp +# +# Note also that the negative lookahead added with the ! operator must always be +# for a constant maximum number of tokens. +# +# See this paper for analysis of how LL(k) grammars relate to PEGs: +# https://arxiv.org/abs/1304.3177 + Root <- ContainerMembers skip eof # *** Top level *** @@ -66,27 +104,27 @@ VarAssignStatement <- (VarDeclProto / Expr) (COMMA (VarDeclProto / Expr))* EQUAL # *** Expression Level *** # An assignment or a destructure whose LHS are all lvalue expressions. -AssignExpr <- Expr (AssignOp Expr / (COMMA Expr)+ EQUAL Expr)? +AssignExpr <- Expr (AssignOp Expr / (COMMA Expr)+ EQUAL Expr / !AssignOp !COMMA) -SingleAssignExpr <- Expr (AssignOp Expr)? +SingleAssignExpr <- Expr (AssignOp Expr / !AssignOp) Expr <- BoolOrExpr -BoolOrExpr <- BoolAndExpr (OrOp BoolAndExpr)* +BoolOrExpr <- BoolAndExpr (OrOp BoolAndExpr)* !OrOp -BoolAndExpr <- CompareExpr (AndOp CompareExpr)* +BoolAndExpr <- CompareExpr (AndOp CompareExpr)* !AndOp -CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? +CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr / !CompareOp) -BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* +BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* !BitwiseOp -BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* +BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* !BitShiftOp -AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* +AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* !AdditionOp -MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* +MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* !MultiplyOp -PrefixExpr <- PrefixOp* PrimaryExpr +PrefixExpr <- PrefixOp* !PrefixOp PrimaryExpr PrimaryExpr <- AsmExpr @@ -101,7 +139,7 @@ PrimaryExpr / Block / CurlySuffixExpr -IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? +IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr / !KEYWORD_else) Block <- LBRACE BlockStatement* RBRACE @@ -111,19 +149,18 @@ ForExpr <- ForPrefix Expr (KEYWORD_else Expr / !KEYWORD_else) WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? -CurlySuffixExpr <- TypeExpr InitList? +CurlySuffixExpr <- TypeExpr (InitList / !LBRACE) InitList <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE / LBRACE Expr (COMMA Expr)* COMMA? RBRACE / LBRACE RBRACE -TypeExpr <- PrefixTypeOp* ErrorUnionExpr +TypeExpr <- PrefixTypeOp* !PrefixTypeOpPrefix ErrorUnionExpr -ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? +ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr / !EXCLAMATIONMARK) -SuffixExpr - <- PrimaryTypeExpr (SuffixOp / FnCallArguments)* +SuffixExpr <- PrimaryTypeExpr SuffixOp* !SuffixOpPrefix PrimaryTypeExpr <- BUILTINIDENTIFIER FnCallArguments @@ -150,7 +187,7 @@ ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE GroupedExpr <- LPAREN Expr RPAREN -IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? +IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr / !KEYWORD_else) LabeledTypeExpr <- BlockLabel Block @@ -161,7 +198,7 @@ LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr / !KEYWORD_else) -WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? +WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr / !KEYWORD_else) SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE @@ -319,11 +356,25 @@ PrefixTypeOp / SinglePtrTypeStart KEYWORD_allowzero? BitAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile? / ArrayTypeStart +PrefixTypeOpPrefix + <- QUESTIONMARK + / KEYWORD_anyframe MINUSRARROW + / LBRACKET + / ASTERISK + SuffixOp <- LBRACKET Expr (DOT2 Expr? (COLON Expr)?)? RBRACKET / DOT IDENTIFIER / DOTASTERISK / DOTQUESTIONMARK + / FnCallArguments + +SuffixOpPrefix + <- LBRACKET + / DOT IDENTIFIER + / DOTASTERISK + / DOTQUESTIONMARK + / LPAREN FnCallArguments <- LPAREN ExprList RPAREN @@ -334,7 +385,7 @@ SinglePtrTypeStart <- ASTERISK ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET -ArrayTypeStart <- LBRACKET Expr (COLON Expr)? RBRACKET +ArrayTypeStart <- LBRACKET !ASTERISK Expr (COLON Expr)? RBRACKET # ContainerDecl specific ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE diff --git a/lib/std/zig/Parse.zig b/lib/std/zig/Parse.zig index b0918b237c6c4d8e827d0a2182a7378646077a5c..8b694eea05c9f39a00ff06aa43b314073d0e98bc 100644 --- a/lib/std/zig/Parse.zig +++ b/lib/std/zig/Parse.zig @@ -1,5 +1,18 @@ //! Represents in-progress parsing, will be converted to an Ast after completion. +// This recursive descent parser must be "predictive," using only constant +// token lookahead and never backtracking. +// +// This means that whenever the parser encounters a choice it is allowed to look +// at the next k tokens in order to make that choice, where k is a fixed constant. +// +// Once the parser has made the choice, it must either succeed at parsing that sub +// expression or fail entirely and present an error to the user. The parser is never +// allowed to backtrack and make a different choice to see if that one succeeds. +// +// This ensures worst-case linear runtime rather than worst-case exponential runtime +// and requires the Zig grammar to be LL(k). + pub const Error = error{ParseError} || Allocator.Error; gpa: Allocator, @@ -1411,7 +1424,7 @@ fn parseBlockExpr(p: *Parse) Error!?Node.Index { } } -/// AssignExpr <- Expr (AssignOp Expr / (COMMA Expr)+ EQUAL Expr)? +/// AssignExpr <- Expr (AssignOp Expr / (COMMA Expr)+ EQUAL Expr / !AssignOp !COMMA) /// /// AssignOp /// <- ASTERISKEQUAL @@ -1437,7 +1450,7 @@ fn parseAssignExpr(p: *Parse) !?Node.Index { return try p.finishAssignExpr(expr); } -/// SingleAssignExpr <- Expr (AssignOp Expr)? +/// SingleAssignExpr <- Expr (AssignOp Expr / !AssignOp) fn parseSingleAssignExpr(p: *Parse) !?Node.Index { const lhs = try p.parseExpr() orelse return null; const tag = assignOpNode(p.tokenTag(p.tok_i)) orelse return lhs; @@ -1643,7 +1656,7 @@ fn parseExprPrecedence(p: *Parse, min_prec: i32) Error!?Node.Index { return node; } -/// PrefixExpr <- PrefixOp* PrimaryExpr +/// PrefixExpr <- PrefixOp* PrimaryExpr / !PrefixOp /// /// PrefixOp /// <- EXCLAMATIONMARK @@ -1673,23 +1686,28 @@ fn expectPrefixExpr(p: *Parse) Error!Node.Index { return try p.parsePrefixExpr() orelse return p.fail(.expected_prefix_expr); } -/// TypeExpr <- PrefixTypeOp* ErrorUnionExpr +/// TypeExpr <- PrefixTypeOp* !PrefixTypeOpPrefix ErrorUnionExpr /// /// PrefixTypeOp /// <- QUESTIONMARK /// / KEYWORD_anyframe MINUSRARROW /// / (ManyPtrTypeStart / SliceTypeStart) KEYWORD_allowzero? ByteAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile? /// / SinglePtrTypeStart KEYWORD_allowzero? BitAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile? -/// / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* /// / ArrayTypeStart /// +/// PrefixTypeOpPrefix +/// <- QUESTIONMARK +/// / KEYWORD_anyframe MINUSRARROW +/// / LBRACKET +/// / ASTERISK +/// /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET /// /// SinglePtrTypeStart <- ASTERISK /// /// ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET /// -/// ArrayTypeStart <- LBRACKET Expr !ASTERISK (COLON Expr)? RBRACKET +/// ArrayTypeStart <- LBRACKET !ASTERISK Expr (COLON Expr)? RBRACKET /// /// BitAlign <- KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN fn parseTypeExpr(p: *Parse) Error!?Node.Index { @@ -1904,19 +1922,19 @@ fn expectTypeExpr(p: *Parse) Error!Node.Index { /// PrimaryExpr /// <- AsmExpr /// / IfExpr -/// / KEYWORD_break (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart) -/// / KEYWORD_comptime Expr !ExprSuffix -/// / KEYWORD_nosuspend Expr !ExprSuffix -/// / KEYWORD_continue (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart) -/// / KEYWORD_resume Expr !ExprSuffix -/// / KEYWORD_return (Expr !ExprSuffix / !SinglePtrTypeStart) +/// / KEYWORD_break BreakLabel? Expr? +/// / KEYWORD_comptime Expr +/// / KEYWORD_nosuspend Expr +/// / KEYWORD_continue BreakLabel? Expr? +/// / KEYWORD_resume Expr +/// / KEYWORD_return Expr? /// / BlockLabel? LoopExpr /// / Block /// / CurlySuffixExpr fn parsePrimaryExpr(p: *Parse) !?Node.Index { switch (p.tokenTag(p.tok_i)) { .keyword_asm => return try p.expectAsmExpr(), - .keyword_if => return try p.parseIfExpr(), + .keyword_if => return try p.expectIf(expectExpr), .keyword_break => { return try p.addNode(.{ .tag = .@"break", @@ -1971,18 +1989,18 @@ fn parsePrimaryExpr(p: *Parse) !?Node.Index { .keyword_inline => { p.tok_i += 3; switch (p.tokenTag(p.tok_i)) { - .keyword_for => return try p.parseFor(expectExpr), - .keyword_while => return try p.parseWhileExpr(), + .keyword_for => return try p.expectFor(expectExpr), + .keyword_while => return try p.expectWhileExpr(), else => return p.fail(.expected_inlinable), } }, .keyword_for => { p.tok_i += 2; - return try p.parseFor(expectExpr); + return try p.expectFor(expectExpr); }, .keyword_while => { p.tok_i += 2; - return try p.parseWhileExpr(); + return try p.expectWhileExpr(); }, else => return try p.parseCurlySuffixExpr(), } @@ -1993,23 +2011,18 @@ fn parsePrimaryExpr(p: *Parse) !?Node.Index { .keyword_inline => { p.tok_i += 1; switch (p.tokenTag(p.tok_i)) { - .keyword_for => return try p.parseFor(expectExpr), - .keyword_while => return try p.parseWhileExpr(), + .keyword_for => return try p.expectFor(expectExpr), + .keyword_while => return try p.expectWhileExpr(), else => return p.fail(.expected_inlinable), } }, - .keyword_for => return try p.parseFor(expectExpr), - .keyword_while => return try p.parseWhileExpr(), + .keyword_for => return try p.expectFor(expectExpr), + .keyword_while => return try p.expectWhileExpr(), .l_brace => return try p.parseBlock(), else => return try p.parseCurlySuffixExpr(), } } -/// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffix -fn parseIfExpr(p: *Parse) !?Node.Index { - return try p.parseIf(expectExpr); -} - /// Block <- LBRACE BlockStatement* RBRACE fn parseBlock(p: *Parse) !?Node.Index { const lbrace = p.eatToken(.l_brace) orelse return null; @@ -2104,8 +2117,8 @@ fn forPrefix(p: *Parse) Error!usize { /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? /// /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffi -fn parseWhileExpr(p: *Parse) !?Node.Index { - const while_token = p.eatToken(.keyword_while) orelse return null; +fn expectWhileExpr(p: *Parse) !Node.Index { + const while_token = p.assertToken(.keyword_while); _ = try p.expectToken(.l_paren); const condition = try p.expectExpr(); _ = try p.expectToken(.r_paren); @@ -2153,7 +2166,7 @@ fn parseWhileExpr(p: *Parse) !?Node.Index { }); } -/// CurlySuffixExpr <- TypeExpr InitList? +/// CurlySuffixExpr <- TypeExpr (InitList / !LBRACE) /// /// InitList /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE @@ -2255,7 +2268,7 @@ fn parseCurlySuffixExpr(p: *Parse) !?Node.Index { } } -/// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? +/// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr / !EXCLAMATIONMARK) fn parseErrorUnionExpr(p: *Parse) !?Node.Index { const suffix_expr = try p.parseSuffixExpr() orelse return null; const bang = p.eatToken(.bang) orelse return suffix_expr; @@ -2269,59 +2282,13 @@ fn parseErrorUnionExpr(p: *Parse) !?Node.Index { }); } -/// SuffixExpr -/// <- PrimaryTypeExpr (SuffixOp / FnCallArguments)* -/// -/// FnCallArguments <- LPAREN ExprList RPAREN -/// -/// ExprList <- (Expr COMMA)* Expr? +/// SuffixExpr <- PrimaryTypeExpr SuffixOp* !SuffixOpPrefix fn parseSuffixExpr(p: *Parse) !?Node.Index { var res = try p.parsePrimaryTypeExpr() orelse return null; - while (true) { - const opt_suffix_op = try p.parseSuffixOp(res); - if (opt_suffix_op) |suffix_op| { - res = suffix_op; - continue; - } - const lparen = p.eatToken(.l_paren) orelse return res; - const scratch_top = p.scratch.items.len; - defer p.scratch.shrinkRetainingCapacity(scratch_top); - while (true) { - if (p.eatToken(.r_paren)) |_| break; - const param = try p.expectExpr(); - try p.scratch.append(p.gpa, param); - switch (p.tokenTag(p.tok_i)) { - .comma => p.tok_i += 1, - .r_paren => { - p.tok_i += 1; - break; - }, - .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), - // Likely just a missing comma; give error but continue parsing. - else => try p.warn(.expected_comma_after_arg), - } - } - const comma = (p.tokenTag(p.tok_i - 2)) == .comma; - const params = p.scratch.items[scratch_top..]; - res = switch (params.len) { - 0, 1 => try p.addNode(.{ - .tag = if (comma) .call_one_comma else .call_one, - .main_token = lparen, - .data = .{ .node_and_opt_node = .{ - res, - if (params.len >= 1) .fromOptional(params[0]) else .none, - } }, - }), - else => try p.addNode(.{ - .tag = if (comma) .call_comma else .call, - .main_token = lparen, - .data = .{ .node_and_extra = .{ - res, - try p.addExtra(try p.listToSpan(params)), - } }, - }), - }; + while (try p.parseSuffixOp(res)) |suffix_op| { + res = suffix_op; } + return res; } /// PrimaryTypeExpr @@ -2331,17 +2298,16 @@ fn parseSuffixExpr(p: *Parse) !?Node.Index { /// / DOT IDENTIFIER /// / DOT InitList /// / ErrorSetDecl -/// / FLOAT /// / FnProto /// / GroupedExpr /// / LabeledTypeExpr -/// / IDENTIFIER !(COLON LabelableExpr) +/// / IDENTIFIER /// / IfTypeExpr -/// / INTEGER -/// / KEYWORD_comptime TypeExpr !ExprSuffix +/// / KEYWORD_comptime TypeExpr /// / KEYWORD_error DOT IDENTIFIER /// / KEYWORD_anyframe /// / KEYWORD_unreachable +/// / NUMBERLITERAL /// / STRINGLITERAL /// /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto @@ -2396,23 +2362,23 @@ fn parsePrimaryTypeExpr(p: *Parse) !?Node.Index { }); }, - .builtin => return try p.parseBuiltinCall(), - .keyword_fn => return try p.parseFnProto(), - .keyword_if => return try p.parseIf(expectTypeExpr), + .builtin => return try p.expectBuiltinCall(), + .keyword_fn => return (try p.parseFnProto()).?, + .keyword_if => return try p.expectIf(expectTypeExpr), .keyword_switch => return try p.expectSwitchExpr(false), .keyword_extern, .keyword_packed, => { p.tok_i += 1; - return try p.parseContainerDeclAuto(); + return try p.expectContainerDeclAuto(); }, .keyword_struct, .keyword_opaque, .keyword_enum, .keyword_union, - => return try p.parseContainerDeclAuto(), + => return try p.expectContainerDeclAuto(), .keyword_comptime => return try p.addNode(.{ .tag = .@"comptime", @@ -2438,18 +2404,18 @@ fn parsePrimaryTypeExpr(p: *Parse) !?Node.Index { .keyword_inline => { p.tok_i += 3; switch (p.tokenTag(p.tok_i)) { - .keyword_for => return try p.parseFor(expectTypeExpr), - .keyword_while => return try p.parseWhileTypeExpr(), + .keyword_for => return try p.expectFor(expectTypeExpr), + .keyword_while => return try p.expectWhileTypeExpr(), else => return p.fail(.expected_inlinable), } }, .keyword_for => { p.tok_i += 2; - return try p.parseFor(expectTypeExpr); + return try p.expectFor(expectTypeExpr); }, .keyword_while => { p.tok_i += 2; - return try p.parseWhileTypeExpr(); + return try p.expectWhileTypeExpr(); }, .keyword_switch => { p.tok_i += 2; @@ -2457,7 +2423,7 @@ fn parsePrimaryTypeExpr(p: *Parse) !?Node.Index { }, .l_brace => { p.tok_i += 2; - return try p.parseBlock(); + return (try p.parseBlock()).?; }, else => return try p.addNode(.{ .tag = .identifier, @@ -2474,13 +2440,13 @@ fn parsePrimaryTypeExpr(p: *Parse) !?Node.Index { .keyword_inline => { p.tok_i += 1; switch (p.tokenTag(p.tok_i)) { - .keyword_for => return try p.parseFor(expectTypeExpr), - .keyword_while => return try p.parseWhileTypeExpr(), + .keyword_for => return try p.expectFor(expectTypeExpr), + .keyword_while => return try p.expectWhileTypeExpr(), else => return p.fail(.expected_inlinable), } }, - .keyword_for => return try p.parseFor(expectTypeExpr), - .keyword_while => return try p.parseWhileTypeExpr(), + .keyword_for => return try p.expectFor(expectTypeExpr), + .keyword_while => return try p.expectWhileTypeExpr(), .period => switch (p.tokenTag(p.tok_i + 1)) { .identifier => { p.tok_i += 1; @@ -2608,10 +2574,8 @@ fn parsePrimaryTypeExpr(p: *Parse) !?Node.Index { }, else => { const main_token = p.nextToken(); - const period = p.eatToken(.period); - if (period == null) return p.failExpected(.period); - const identifier = p.eatToken(.identifier); - if (identifier == null) return p.failExpected(.identifier); + _ = try p.expectToken(.period); + _ = try p.expectToken(.identifier); return try p.addNode(.{ .tag = .error_value, .main_token = main_token, @@ -2637,9 +2601,9 @@ fn expectPrimaryTypeExpr(p: *Parse) !Node.Index { /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? /// -/// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix -fn parseWhileTypeExpr(p: *Parse) !?Node.Index { - const while_token = p.eatToken(.keyword_while) orelse return null; +/// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr / !KEYWORD_else) +fn expectWhileTypeExpr(p: *Parse) !Node.Index { + const while_token = p.assertToken(.keyword_while); _ = try p.expectToken(.l_paren); const condition = try p.expectExpr(); _ = try p.expectToken(.r_paren); @@ -3106,6 +3070,11 @@ fn parsePtrModifiers(p: *Parse) !PtrModifiers { /// / DOT IDENTIFIER /// / DOTASTERISK /// / DOTQUESTIONMARK +/// / FnCallArguments +/// +/// FnCallArguments <- LPAREN ExprList RPAREN +/// +/// ExprList <- (Expr COMMA)* Expr? fn parseSuffixOp(p: *Parse, lhs: Node.Index) !?Node.Index { switch (p.tokenTag(p.tok_i)) { .l_bracket => { @@ -3195,6 +3164,46 @@ fn parseSuffixOp(p: *Parse, lhs: Node.Index) !?Node.Index { return null; }, }, + .l_paren => { + const lparen = p.nextToken(); + const scratch_top = p.scratch.items.len; + defer p.scratch.shrinkRetainingCapacity(scratch_top); + while (true) { + if (p.eatToken(.r_paren)) |_| break; + const param = try p.expectExpr(); + try p.scratch.append(p.gpa, param); + switch (p.tokenTag(p.tok_i)) { + .comma => p.tok_i += 1, + .r_paren => { + p.tok_i += 1; + break; + }, + .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), + // Likely just a missing comma; give error but continue parsing. + else => try p.warn(.expected_comma_after_arg), + } + } + const comma = (p.tokenTag(p.tok_i - 2)) == .comma; + const params = p.scratch.items[scratch_top..]; + switch (params.len) { + 0, 1 => return try p.addNode(.{ + .tag = if (comma) .call_one_comma else .call_one, + .main_token = lparen, + .data = .{ .node_and_opt_node = .{ + lhs, + if (params.len >= 1) .fromOptional(params[0]) else .none, + } }, + }), + else => return try p.addNode(.{ + .tag = if (comma) .call_comma else .call, + .main_token = lparen, + .data = .{ .node_and_extra = .{ + lhs, + try p.addExtra(try p.listToSpan(params)), + } }, + }), + } + }, else => return null, } } @@ -3208,7 +3217,7 @@ fn parseSuffixOp(p: *Parse, lhs: Node.Index) !?Node.Index { /// / KEYWORD_opaque /// / KEYWORD_enum (LPAREN Expr RPAREN)? /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? -fn parseContainerDeclAuto(p: *Parse) !?Node.Index { +fn expectContainerDeclAuto(p: *Parse) !Node.Index { const main_token = p.nextToken(); const arg_expr = switch (p.tokenTag(main_token)) { .keyword_opaque => null, @@ -3431,7 +3440,7 @@ fn parseParamDeclList(p: *Parse) !SmallSpan { /// FnCallArguments <- LPAREN ExprList RPAREN /// /// ExprList <- (Expr COMMA)* Expr? -fn parseBuiltinCall(p: *Parse) !Node.Index { +fn expectBuiltinCall(p: *Parse) !Node.Index { const builtin_token = p.assertToken(.builtin); _ = p.eatToken(.l_paren) orelse { try p.warn(.expected_param_list); @@ -3479,9 +3488,13 @@ fn parseBuiltinCall(p: *Parse) !Node.Index { } } +/// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr / !KEYWORD_else) +/// +/// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr / !KEYWORD_else) +/// /// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? -fn parseIf(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !?Node.Index { - const if_token = p.eatToken(.keyword_if) orelse return null; +fn expectIf(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !Node.Index { + const if_token = p.assertToken(.keyword_if); _ = try p.expectToken(.l_paren); const condition = try p.expectExpr(); _ = try p.expectToken(.r_paren); @@ -3513,11 +3526,11 @@ fn parseIf(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !?N }); } -/// ForExpr <- ForPrefix Expr (KEYWORD_else Expr / !KEYWORD_else) !ExprSuffix +/// ForExpr <- ForPrefix Expr (KEYWORD_else Expr / !KEYWORD_else) /// -/// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr / !KEYWORD_else) !ExprSuffix -fn parseFor(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !?Node.Index { - const for_token = p.eatToken(.keyword_for) orelse return null; +/// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr / !KEYWORD_else) +fn expectFor(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !Node.Index { + const for_token = p.assertToken(.keyword_for); const scratch_top = p.scratch.items.len; defer p.scratch.shrinkRetainingCapacity(scratch_top); diff --git a/lib/std/zig/parser_generated_oracle.zig b/lib/std/zig/parser_generated_oracle.zig index 80bfe9db6c01d79b2bb5eda7e006f2855515b0e4..b4c9f708845793b8da979bdcae2310d6326f714f 100644 --- a/lib/std/zig/parser_generated_oracle.zig +++ b/lib/std/zig/parser_generated_oracle.zig @@ -429,25 +429,37 @@ const Parser = struct { pub fn parseAssignExpr(p: *Parser) bool { return blk_0: { const pos_0 = p.i; - if (p.parseExpr() and (blk_3: { - const pos_3 = p.i; - if (p.parseAssignOp() and p.parseExpr()) break :blk_3 true; - p.i = pos_3; - if (blk_4: { - var match_4 = false; - while (blk_6: { - const pos_6 = p.i; - if (p.parseCOMMA() and p.parseExpr()) break :blk_6 true; - p.i = pos_6; - break :blk_6 false; + if (p.parseExpr() and blk_2: { + const pos_2 = p.i; + if (p.parseAssignOp() and p.parseExpr()) break :blk_2 true; + p.i = pos_2; + if (blk_3: { + var match_3 = false; + while (blk_5: { + const pos_5 = p.i; + if (p.parseCOMMA() and p.parseExpr()) break :blk_5 true; + p.i = pos_5; + break :blk_5 false; }) { - match_4 = true; + match_3 = true; } - break :blk_4 match_4; - } and p.parseEQUAL() and p.parseExpr()) break :blk_3 true; - p.i = pos_3; - break :blk_3 false; - } or true)) break :blk_0 true; + break :blk_3 match_3; + } and p.parseEQUAL() and p.parseExpr()) break :blk_2 true; + p.i = pos_2; + if (blk_3: { + const pos_3 = p.i; + const match_3 = p.parseAssignOp(); + p.i = pos_3; + break :blk_3 !match_3; + } and blk_3: { + const pos_3 = p.i; + const match_3 = p.parseCOMMA(); + p.i = pos_3; + break :blk_3 !match_3; + }) break :blk_2 true; + p.i = pos_2; + break :blk_2 false; + }) break :blk_0 true; p.i = pos_0; break :blk_0 false; }; @@ -455,12 +467,19 @@ const Parser = struct { pub fn parseSingleAssignExpr(p: *Parser) bool { return blk_0: { const pos_0 = p.i; - if (p.parseExpr() and (blk_3: { - const pos_3 = p.i; - if (p.parseAssignOp() and p.parseExpr()) break :blk_3 true; - p.i = pos_3; - break :blk_3 false; - } or true)) break :blk_0 true; + if (p.parseExpr() and blk_2: { + const pos_2 = p.i; + if (p.parseAssignOp() and p.parseExpr()) break :blk_2 true; + p.i = pos_2; + if (blk_3: { + const pos_3 = p.i; + const match_3 = p.parseAssignOp(); + p.i = pos_3; + break :blk_3 !match_3; + }) break :blk_2 true; + p.i = pos_2; + break :blk_2 false; + }) break :blk_0 true; p.i = pos_0; break :blk_0 false; }; @@ -484,6 +503,11 @@ const Parser = struct { break :blk_3 false; }) {} break :blk_1 true; + } and blk_1: { + const pos_1 = p.i; + const match_1 = p.parseOrOp(); + p.i = pos_1; + break :blk_1 !match_1; }) break :blk_0 true; p.i = pos_0; break :blk_0 false; @@ -500,6 +524,11 @@ const Parser = struct { break :blk_3 false; }) {} break :blk_1 true; + } and blk_1: { + const pos_1 = p.i; + const match_1 = p.parseAndOp(); + p.i = pos_1; + break :blk_1 !match_1; }) break :blk_0 true; p.i = pos_0; break :blk_0 false; @@ -508,12 +537,19 @@ const Parser = struct { pub fn parseCompareExpr(p: *Parser) bool { return blk_0: { const pos_0 = p.i; - if (p.parseBitwiseExpr() and (blk_3: { - const pos_3 = p.i; - if (p.parseCompareOp() and p.parseBitwiseExpr()) break :blk_3 true; - p.i = pos_3; - break :blk_3 false; - } or true)) break :blk_0 true; + if (p.parseBitwiseExpr() and blk_2: { + const pos_2 = p.i; + if (p.parseCompareOp() and p.parseBitwiseExpr()) break :blk_2 true; + p.i = pos_2; + if (blk_3: { + const pos_3 = p.i; + const match_3 = p.parseCompareOp(); + p.i = pos_3; + break :blk_3 !match_3; + }) break :blk_2 true; + p.i = pos_2; + break :blk_2 false; + }) break :blk_0 true; p.i = pos_0; break :blk_0 false; }; @@ -529,6 +565,11 @@ const Parser = struct { break :blk_3 false; }) {} break :blk_1 true; + } and blk_1: { + const pos_1 = p.i; + const match_1 = p.parseBitwiseOp(); + p.i = pos_1; + break :blk_1 !match_1; }) break :blk_0 true; p.i = pos_0; break :blk_0 false; @@ -545,6 +586,11 @@ const Parser = struct { break :blk_3 false; }) {} break :blk_1 true; + } and blk_1: { + const pos_1 = p.i; + const match_1 = p.parseBitShiftOp(); + p.i = pos_1; + break :blk_1 !match_1; }) break :blk_0 true; p.i = pos_0; break :blk_0 false; @@ -561,6 +607,11 @@ const Parser = struct { break :blk_3 false; }) {} break :blk_1 true; + } and blk_1: { + const pos_1 = p.i; + const match_1 = p.parseAdditionOp(); + p.i = pos_1; + break :blk_1 !match_1; }) break :blk_0 true; p.i = pos_0; break :blk_0 false; @@ -577,6 +628,11 @@ const Parser = struct { break :blk_3 false; }) {} break :blk_1 true; + } and blk_1: { + const pos_1 = p.i; + const match_1 = p.parseMultiplyOp(); + p.i = pos_1; + break :blk_1 !match_1; }) break :blk_0 true; p.i = pos_0; break :blk_0 false; @@ -588,6 +644,11 @@ const Parser = struct { if (blk_1: { while (p.parsePrefixOp()) {} break :blk_1 true; + } and blk_1: { + const pos_1 = p.i; + const match_1 = p.parsePrefixOp(); + p.i = pos_1; + break :blk_1 !match_1; } and p.parsePrimaryExpr()) break :blk_0 true; p.i = pos_0; break :blk_0 false; @@ -624,12 +685,19 @@ const Parser = struct { pub fn parseIfExpr(p: *Parser) bool { return blk_0: { const pos_0 = p.i; - if (p.parseIfPrefix() and p.parseExpr() and (blk_3: { - const pos_3 = p.i; - if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseExpr()) break :blk_3 true; - p.i = pos_3; - break :blk_3 false; - } or true)) break :blk_0 true; + if (p.parseIfPrefix() and p.parseExpr() and blk_2: { + const pos_2 = p.i; + if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseExpr()) break :blk_2 true; + p.i = pos_2; + if (blk_3: { + const pos_3 = p.i; + const match_3 = p.parseKEYWORD_else(); + p.i = pos_3; + break :blk_3 !match_3; + }) break :blk_2 true; + p.i = pos_2; + break :blk_2 false; + }) break :blk_0 true; p.i = pos_0; break :blk_0 false; }; @@ -696,7 +764,19 @@ const Parser = struct { pub fn parseCurlySuffixExpr(p: *Parser) bool { return blk_0: { const pos_0 = p.i; - if (p.parseTypeExpr() and (p.parseInitList() or true)) break :blk_0 true; + if (p.parseTypeExpr() and blk_2: { + const pos_2 = p.i; + if (p.parseInitList()) break :blk_2 true; + p.i = pos_2; + if (blk_3: { + const pos_3 = p.i; + const match_3 = p.parseLBRACE(); + p.i = pos_3; + break :blk_3 !match_3; + }) break :blk_2 true; + p.i = pos_2; + break :blk_2 false; + }) break :blk_0 true; p.i = pos_0; break :blk_0 false; }; @@ -735,6 +815,11 @@ const Parser = struct { if (blk_1: { while (p.parsePrefixTypeOp()) {} break :blk_1 true; + } and blk_1: { + const pos_1 = p.i; + const match_1 = p.parsePrefixTypeOpPrefix(); + p.i = pos_1; + break :blk_1 !match_1; } and p.parseErrorUnionExpr()) break :blk_0 true; p.i = pos_0; break :blk_0 false; @@ -743,12 +828,19 @@ const Parser = struct { pub fn parseErrorUnionExpr(p: *Parser) bool { return blk_0: { const pos_0 = p.i; - if (p.parseSuffixExpr() and (blk_3: { - const pos_3 = p.i; - if (p.parseEXCLAMATIONMARK() and p.parseTypeExpr()) break :blk_3 true; - p.i = pos_3; - break :blk_3 false; - } or true)) break :blk_0 true; + if (p.parseSuffixExpr() and blk_2: { + const pos_2 = p.i; + if (p.parseEXCLAMATIONMARK() and p.parseTypeExpr()) break :blk_2 true; + p.i = pos_2; + if (blk_3: { + const pos_3 = p.i; + const match_3 = p.parseEXCLAMATIONMARK(); + p.i = pos_3; + break :blk_3 !match_3; + }) break :blk_2 true; + p.i = pos_2; + break :blk_2 false; + }) break :blk_0 true; p.i = pos_0; break :blk_0 false; }; @@ -757,15 +849,13 @@ const Parser = struct { return blk_0: { const pos_0 = p.i; if (p.parsePrimaryTypeExpr() and blk_1: { - while (blk_3: { - const pos_3 = p.i; - if (p.parseSuffixOp()) break :blk_3 true; - p.i = pos_3; - if (p.parseFnCallArguments()) break :blk_3 true; - p.i = pos_3; - break :blk_3 false; - }) {} + while (p.parseSuffixOp()) {} break :blk_1 true; + } and blk_1: { + const pos_1 = p.i; + const match_1 = p.parseSuffixOpPrefix(); + p.i = pos_1; + break :blk_1 !match_1; }) break :blk_0 true; p.i = pos_0; break :blk_0 false; @@ -845,12 +935,19 @@ const Parser = struct { pub fn parseIfTypeExpr(p: *Parser) bool { return blk_0: { const pos_0 = p.i; - if (p.parseIfPrefix() and p.parseTypeExpr() and (blk_3: { - const pos_3 = p.i; - if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseTypeExpr()) break :blk_3 true; - p.i = pos_3; - break :blk_3 false; - } or true)) break :blk_0 true; + if (p.parseIfPrefix() and p.parseTypeExpr() and blk_2: { + const pos_2 = p.i; + if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseTypeExpr()) break :blk_2 true; + p.i = pos_2; + if (blk_3: { + const pos_3 = p.i; + const match_3 = p.parseKEYWORD_else(); + p.i = pos_3; + break :blk_3 !match_3; + }) break :blk_2 true; + p.i = pos_2; + break :blk_2 false; + }) break :blk_0 true; p.i = pos_0; break :blk_0 false; }; @@ -905,12 +1002,19 @@ const Parser = struct { pub fn parseWhileTypeExpr(p: *Parser) bool { return blk_0: { const pos_0 = p.i; - if (p.parseWhilePrefix() and p.parseTypeExpr() and (blk_3: { - const pos_3 = p.i; - if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseTypeExpr()) break :blk_3 true; - p.i = pos_3; - break :blk_3 false; - } or true)) break :blk_0 true; + if (p.parseWhilePrefix() and p.parseTypeExpr() and blk_2: { + const pos_2 = p.i; + if (p.parseKEYWORD_else() and (p.parsePayload() or true) and p.parseTypeExpr()) break :blk_2 true; + p.i = pos_2; + if (blk_3: { + const pos_3 = p.i; + const match_3 = p.parseKEYWORD_else(); + p.i = pos_3; + break :blk_3 !match_3; + }) break :blk_2 true; + p.i = pos_2; + break :blk_2 false; + }) break :blk_0 true; p.i = pos_0; break :blk_0 false; }; @@ -1539,6 +1643,20 @@ const Parser = struct { break :blk_0 false; }; } + pub fn parsePrefixTypeOpPrefix(p: *Parser) bool { + return blk_0: { + const pos_0 = p.i; + if (p.parseQUESTIONMARK()) break :blk_0 true; + p.i = pos_0; + if (p.parseKEYWORD_anyframe() and p.parseMINUSRARROW()) break :blk_0 true; + p.i = pos_0; + if (p.parseLBRACKET()) break :blk_0 true; + p.i = pos_0; + if (p.parseASTERISK()) break :blk_0 true; + p.i = pos_0; + break :blk_0 false; + }; + } pub fn parseSuffixOp(p: *Parser) bool { return blk_0: { const pos_0 = p.i; @@ -1560,6 +1678,24 @@ const Parser = struct { p.i = pos_0; if (p.parseDOTQUESTIONMARK()) break :blk_0 true; p.i = pos_0; + if (p.parseFnCallArguments()) break :blk_0 true; + p.i = pos_0; + break :blk_0 false; + }; + } + pub fn parseSuffixOpPrefix(p: *Parser) bool { + return blk_0: { + const pos_0 = p.i; + if (p.parseLBRACKET()) break :blk_0 true; + p.i = pos_0; + if (p.parseDOT() and p.parseIDENTIFIER()) break :blk_0 true; + p.i = pos_0; + if (p.parseDOTASTERISK()) break :blk_0 true; + p.i = pos_0; + if (p.parseDOTQUESTIONMARK()) break :blk_0 true; + p.i = pos_0; + if (p.parseLPAREN()) break :blk_0 true; + p.i = pos_0; break :blk_0 false; }; } @@ -1610,7 +1746,12 @@ const Parser = struct { pub fn parseArrayTypeStart(p: *Parser) bool { return blk_0: { const pos_0 = p.i; - if (p.parseLBRACKET() and p.parseExpr() and (blk_3: { + if (p.parseLBRACKET() and blk_1: { + const pos_1 = p.i; + const match_1 = p.parseASTERISK(); + p.i = pos_1; + break :blk_1 !match_1; + } and p.parseExpr() and (blk_3: { const pos_3 = p.i; if (p.parseCOLON() and p.parseExpr()) break :blk_3 true; p.i = pos_3;