authorgravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2026-03-22 17:13:44-04:00
committergravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2026-03-25 17:29:56-04:00
log785fb1be111186525bf288fa3460945404f676eb
treeea9b801ad5b85ecd25daf51338d1b864333cc25b
parent2aee0cd6b95d8154a8245d2d1d7be3f6058c659c

fix several inconsistencies between parser and PEG

- PEG / Parser Changes All the changes made here are to places where the PEG was more permissive than the parser. Changes to the parser make it more permissive and changes to the PEG make it more strict. When choosing between these two options for discrepancies, I opted for the choice that was more natural and increased code readability. Changes to the Parser * Tuple types can now be `inline` and `extern` (e.g. `extern struct`). * Break labels are now only consumed if both the colon and identifier are present instead of failing if there is only a colon. * Labeled blocks are no longer parsed in PrimaryExpr (so they are now allowed to have CurlySuffixExpr) as in the PEG. * While expressions can now be grouped on the same line. * Added distinction in error messages for "a multiline string literal" so places where only single string literals are allowed do not give "expected 'a string literal', found 'a string literal'". Changes to the PEG * Made it so extern functions cannot have a body * Made it so ... can be only the last function argument * Made it so many item pointers can't have bit alignment * Made it so asm inputs / outputs can not be multiline string literals * Added distinction between block-level statements and regular statements -- Pointer Qualifier Order The PEG allowed for duplicated qualifiers, which the parser did not. The simplest fix for this was to make each be allowed zero or one times which required giving them a order similar to how FnProto already works. The chosen order is the same as used by zig fmt. The parser still accepts them in any order similar to functions. -- Backtracking Made it so several places could not backtrack in the PEG. A common pattern for this was (A / !A). --- !ExprSuffix Expressions ending with expressions now have !ExprSuffix after. This change prevents expressions such as `if (a) T else U{}` being be parsable as `(if (a) T else U){}`. It also stops some backtracking, take for example: `if (a) for (b) |c| d else |e| f` It may seem at first that the else clause belongs to the `for`, however it actually belongs to the `if` because for else-clauses cannot have a payload. This is fixed by a new `KEYWORD_else / !KEYWORD_else`, however this alone does not fix more complex cases such as: `if (a) for (b) |c| d() else |e| f` The PEG would first attempt to parse it as expected but fail due to the new guard. It will then backtrack to `if (a) (for (b) |c| d)() else |e| f` which is surprising but avoids the new gaurd. So, !ExprSuffix is required to disallow this type of backtracking. --- !LabelableExpr For identifiers, excluding labels is necessary despite ordered choice due to pointer bit alignment. For example `*align(a : b: for (c) e) T` could backtrack to `*align(a : b : (for (c) e)) T`. --- !SinglePtrTypeStart Prevents expressions like `break * break` which is parsed as `break (*break)` backtracking to `(break) * (break)` --- !BlockExpr Prevents expressions like `test { {} = a; }` being backtracked to and parsed as `test { ({} = a); }` (the parenthesis are just for demonstration, that expression is not legal either) --- !ExprStatement In addition to splitting up block level statements, statements that are also parsable as expressions are now part of ExprStatement to disallow backtracking.

5 files changed, 189 insertions(+), 143 deletions(-)

doc/langref.html.in+74-55
......@@ -7944,58 +7944,60 @@ TestDecl <- KEYWORD_test (STRINGLITERALSINGLE / IDENTIFIER)? Block
79447944ComptimeDecl <- KEYWORD_comptime Block
79457945
79467946Decl
7947 <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block)
7947 <- (KEYWORD_export / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block)
7948 / KEYWORD_extern STRINGLITERALSINGLE? FnProto SEMICOLON
79487949 / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? GlobalVarDecl
79497950
7950FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
7951FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr !ExprSuffix
79517952
79527953VarDeclProto <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection?
79537954
79547955GlobalVarDecl <- VarDeclProto (EQUAL Expr)? SEMICOLON
79557956
7956ContainerField <- doc_comment? KEYWORD_comptime? !KEYWORD_fn (IDENTIFIER COLON)? TypeExpr ByteAlign? (EQUAL Expr)?
7957ContainerField <- doc_comment? (KEYWORD_comptime / !KEYWORD_comptime) !KEYWORD_fn (IDENTIFIER COLON / !(IDENTIFIER COLON))? TypeExpr ByteAlign? (EQUAL Expr)?
79577958
79587959# *** Block Level ***
7959Statement
7960 <- KEYWORD_comptime ComptimeStatement
7961 / KEYWORD_nosuspend BlockExprStatement
7962 / KEYWORD_suspend BlockExprStatement
7960BlockStatement
7961 <- Statement
79637962 / KEYWORD_defer BlockExprStatement
79647963 / KEYWORD_errdefer Payload? BlockExprStatement
7965 / IfStatement
7966 / LabeledStatement
7967 / VarDeclExprStatement
7964 / !ExprStatement (KEYWORD_comptime !BlockExpr)? VarAssignStatement
79687965
7969ComptimeStatement
7970 <- BlockExpr
7971 / VarDeclExprStatement
7966Statement
7967 <- ExprStatement
7968 / KEYWORD_suspend BlockExprStatement
7969 / !ExprStatement (KEYWORD_comptime !BlockExpr)? AssignExpr SEMICOLON
7970
7971ExprStatement
7972 <- IfStatement
7973 / LabeledStatement
7974 / KEYWORD_nosuspend BlockExprStatement
7975 / KEYWORD_comptime BlockExpr
79727976
79737977IfStatement
79747978 <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
7975 / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
7979 / IfPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
79767980
79777981LabeledStatement <- BlockLabel? (Block / LoopStatement / SwitchExpr)
79787982
79797983LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
79807984
79817985ForStatement
7982 <- ForPrefix BlockExpr ( KEYWORD_else Statement )?
7983 / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )
7986 <- ForPrefix BlockExpr ( KEYWORD_else Statement / !KEYWORD_else )
7987 / ForPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Statement )
79847988
79857989WhileStatement
79867990 <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
7987 / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
7991 / WhilePrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
79887992
79897993BlockExprStatement
79907994 <- BlockExpr
7991 / AssignExpr SEMICOLON
7995 / !BlockExpr AssignExpr SEMICOLON
79927996
79937997BlockExpr <- BlockLabel? Block
79947998
7995# An expression, assignment, or any destructure, as a statement.
7996VarDeclExprStatement
7997 <- VarDeclProto (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON
7998 / Expr (AssignOp Expr / (COMMA (VarDeclProto / Expr))+ EQUAL Expr)? SEMICOLON
7999# An assignment or a destructure whose LHS are all lvalue expressions or variable declarations.
8000VarAssignStatement <- (VarDeclProto / Expr) (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON
79998001
80008002# *** Expression Level ***
80018003
......@@ -8025,25 +8027,25 @@ PrefixExpr <- PrefixOp* PrimaryExpr
80258027PrimaryExpr
80268028 <- AsmExpr
80278029 / IfExpr
8028 / KEYWORD_break BreakLabel? Expr?
8029 / KEYWORD_comptime Expr
8030 / KEYWORD_nosuspend Expr
8031 / KEYWORD_continue BreakLabel? Expr?
8032 / KEYWORD_resume Expr
8033 / KEYWORD_return Expr?
8030 / KEYWORD_break (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart)
8031 / KEYWORD_comptime Expr !ExprSuffix
8032 / KEYWORD_nosuspend Expr !ExprSuffix
8033 / KEYWORD_continue (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart)
8034 / KEYWORD_resume Expr !ExprSuffix
8035 / KEYWORD_return (Expr !ExprSuffix / !SinglePtrTypeStart)
80348036 / BlockLabel? LoopExpr
80358037 / Block
80368038 / CurlySuffixExpr
80378039
8038IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)?
8040IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffix
80398041
8040Block <- LBRACE Statement* RBRACE
8042Block <- LBRACE BlockStatement* RBRACE
80418043
80428044LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)
80438045
8044ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
8046ForExpr <- ForPrefix Expr (KEYWORD_else Expr / !KEYWORD_else) !ExprSuffix
80458047
8046WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
8048WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffix
80478049
80488050CurlySuffixExpr <- TypeExpr InitList?
80498051
......@@ -8070,10 +8072,10 @@ PrimaryTypeExpr
80708072 / FnProto
80718073 / GroupedExpr
80728074 / LabeledTypeExpr
8073 / IDENTIFIER
8075 / IDENTIFIER !(COLON LabelableExpr)
80748076 / IfTypeExpr
80758077 / INTEGER
8076 / KEYWORD_comptime TypeExpr
8078 / KEYWORD_comptime TypeExpr !ExprSuffix
80778079 / KEYWORD_error DOT IDENTIFIER
80788080 / KEYWORD_anyframe
80798081 / KEYWORD_unreachable
......@@ -8085,7 +8087,7 @@ ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE
80858087
80868088GroupedExpr <- LPAREN Expr RPAREN
80878089
8088IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
8090IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix
80898091
80908092LabeledTypeExpr
80918093 <- BlockLabel Block
......@@ -8094,9 +8096,9 @@ LabeledTypeExpr
80948096
80958097LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
80968098
8097ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?
8099ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr / !KEYWORD_else) !ExprSuffix
80988100
8099WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
8101WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix
81008102
81018103SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
81028104
......@@ -8105,11 +8107,11 @@ AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN
81058107
81068108AsmOutput <- COLON AsmOutputList AsmInput?
81078109
8108AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
8110AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
81098111
81108112AsmInput <- COLON AsmInputList AsmClobbers?
81118113
8112AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
8114AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN Expr RPAREN
81138115
81148116AsmClobbers <- COLON Expr
81158117
......@@ -8129,9 +8131,7 @@ AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN
81298131# Fn specific
81308132CallConv <- KEYWORD_callconv LPAREN Expr RPAREN
81318133
8132ParamDecl
8133 <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
8134 / DOT3
8134ParamDecl <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime / !KEYWORD_comptime) (IDENTIFIER COLON / !(IDENTIFIER_COLON)) ParamType
81358135
81368136ParamType
81378137 <- KEYWORD_anytype
......@@ -8237,8 +8237,8 @@ PrefixOp
82378237PrefixTypeOp
82388238 <- QUESTIONMARK
82398239 / KEYWORD_anyframe MINUSRARROW
8240 / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
8241 / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
8240 / (ManyPtrTypeStart / SliceTypeStart) KEYWORD_allowzero? ByteAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile?
8241 / SinglePtrTypeStart KEYWORD_allowzero? BitAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile?
82428242 / ArrayTypeStart
82438243
82448244SuffixOp
......@@ -8249,15 +8249,31 @@ SuffixOp
82498249
82508250FnCallArguments <- LPAREN ExprList RPAREN
82518251
8252ExprSuffix
8253 <- KEYWORD_or
8254 / KEYWORD_and
8255 / CompareOp
8256 / BitwiseOp
8257 / BitShiftOp
8258 / AdditionOp
8259 / MultiplyOp
8260 / EXCLAMATIONMARK
8261 / SuffixOp
8262 / FnCallArguments
8263
8264LabelableExpr
8265 <- Block
8266 / SwitchExpr
8267 / LoopExpr
8268
82528269# Ptr specific
82538270SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET
82548271
8255PtrTypeStart
8256 <- ASTERISK
8257 / ASTERISK2
8258 / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET
8272SinglePtrTypeStart <- ASTERISK / ASTERISK2
8273
8274ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET
82598275
8260ArrayTypeStart <- LBRACKET Expr (COLON Expr)? RBRACKET
8276ArrayTypeStart <- LBRACKET Expr !(ASTERISK / ASTERISK2) (COLON Expr)? RBRACKET
82618277
82628278# ContainerDecl specific
82638279ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE
......@@ -8266,11 +8282,13 @@ ContainerDeclType
82668282 <- KEYWORD_struct (LPAREN Expr RPAREN)?
82678283 / KEYWORD_opaque
82688284 / KEYWORD_enum (LPAREN Expr RPAREN)?
8269 / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
8285 / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / !KEYWORD_enum Expr) RPAREN)?
82708286
82718287# Alignment
82728288ByteAlign <- KEYWORD_align LPAREN Expr RPAREN
82738289
8290BitAlign <- KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN
8291
82748292# Lists
82758293IdentifierList <- (doc_comment? IDENTIFIER COMMA)* (doc_comment? IDENTIFIER)?
82768294
......@@ -8280,7 +8298,7 @@ AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem?
82808298
82818299AsmInputList <- (AsmInputItem COMMA)* AsmInputItem?
82828300
8283ParamDeclList <- (ParamDecl COMMA)* ParamDecl?
8301ParamDeclList <- (ParamDecl COMMA)* (ParamDecl / DOT3 COMMA?)?
82848302
82858303ExprList <- (Expr COMMA)* Expr?
82868304
......@@ -8337,6 +8355,7 @@ multibyte_utf8 <-
83378355 / oxC2_oxDF ox80_oxBF
83388356
83398357non_control_ascii <- [\040-\176]
8358non_control_utf8 <- [\040-\377]
83408359
83418360char_escape
83428361 <- "\\x" hex hex
......@@ -8352,10 +8371,10 @@ string_char
83528371 / char_escape
83538372 / ![\\"\n] non_control_ascii
83548373
8355container_doc_comment <- ('//!' [^\n]* [ \n]* skip)+
8356doc_comment <- ('///' [^\n]* [ \n]* skip)+
8357line_comment <- '//' ![!/][^\n]* / '////' [^\n]*
8358line_string <- ('\\\\' [^\n]* [ \n]*)+
8374container_doc_comment <- ('//!' non_control_utf8* [ \n]* skip)+
8375doc_comment <- ('///' non_control_utf8* [ \n]* skip)+
8376line_comment <- '//' ![!/] non_control_utf8* / '////' non_control_utf8*
8377line_string <- '\\\\' non_control_utf8* [ \n]*
83598378skip <- ([ \n] / line_comment)*
83608379
83618380CHAR_LITERAL <- ['] char_char ['] skip
lib/std/zig/Ast.zig-4
......@@ -504,9 +504,6 @@ pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void
504504 .varargs_nonfinal => {
505505 return w.writeAll("function prototype has parameter after varargs");
506506 },
507 .expected_continue_expr => {
508 return w.writeAll("expected ':' before while continue expression");
509 },
510507
511508 .expected_semi_after_decl => {
512509 return w.writeAll("expected ';' after declaration");
......@@ -2888,7 +2885,6 @@ pub const Error = struct {
28882885 test_doc_comment,
28892886 comptime_doc_comment,
28902887 varargs_nonfinal,
2891 expected_continue_expr,
28922888 expected_semi_after_decl,
28932889 expected_semi_after_stmt,
28942890 expected_comma_after_field,
lib/std/zig/Parse.zig+83-74
......@@ -257,7 +257,7 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members {
257257 while (true) {
258258 const doc_comment = try p.eatDocComments();
259259
260 switch (p.tokenTag(p.tok_i)) {
260 sw: switch (p.tokenTag(p.tok_i)) {
261261 .keyword_test => {
262262 if (doc_comment) |some| {
263263 try p.warnMsg(.{ .tag = .test_doc_comment, .token = some });
......@@ -348,17 +348,7 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members {
348348 p.findNextContainerMember();
349349 },
350350 },
351 .keyword_pub => {
352 p.tok_i += 1;
353 const opt_top_level_decl = try p.expectTopLevelDeclRecoverable();
354 if (opt_top_level_decl) |top_level_decl| {
355 if (field_state == .seen) {
356 field_state = .{ .end = top_level_decl };
357 }
358 try p.scratch.append(p.gpa, top_level_decl);
359 }
360 trailing = p.tokenTag(p.tok_i - 1) == .semicolon;
361 },
351 .keyword_pub,
362352 .keyword_const,
363353 .keyword_var,
364354 .keyword_threadlocal,
......@@ -367,7 +357,27 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members {
367357 .keyword_inline,
368358 .keyword_noinline,
369359 .keyword_fn,
370 => {
360 => |t| {
361 if (t == .keyword_extern) {
362 switch (p.tokenTag(p.tok_i + 1)) {
363 .keyword_struct,
364 .keyword_union,
365 .keyword_enum,
366 .keyword_opaque,
367 => |ct| continue :sw ct,
368 else => {},
369 }
370 }
371 if (t == .keyword_inline) {
372 switch (p.tokenTag(p.tok_i + 1)) {
373 .keyword_for,
374 .keyword_while,
375 => |ct| continue :sw ct,
376 else => {},
377 }
378 }
379
380 p.tok_i += @intFromBool(t == .keyword_pub);
371381 const opt_top_level_decl = try p.expectTopLevelDeclRecoverable();
372382 if (opt_top_level_decl) |top_level_decl| {
373383 if (field_state == .seen) {
......@@ -588,7 +598,8 @@ fn expectTestDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index {
588598}
589599
590600/// Decl
591/// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block)
601/// <- (KEYWORD_export / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block)
602/// / KEYWORD_extern STRINGLITERALSINGLE? FnProto SEMICOLON
592603/// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
593604fn expectTopLevelDecl(p: *Parse) !?Node.Index {
594605 const extern_export_inline_token = p.nextToken();
......@@ -665,7 +676,7 @@ fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index {
665676 };
666677}
667678
668/// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
679/// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr !ExprSuffix
669680fn parseFnProto(p: *Parse) !?Node.Index {
670681 const fn_token = p.eatToken(.keyword_fn) orelse return null;
671682
......@@ -853,7 +864,7 @@ fn parseGlobalVarDecl(p: *Parse) !?Node.Index {
853864 return var_decl;
854865}
855866
856/// ContainerField <- doc_comment? KEYWORD_comptime? !KEYWORD_fn (IDENTIFIER COLON)? TypeExpr ByteAlign? (EQUAL Expr)?
867/// ContainerField <- doc_comment? (KEYWORD_comptime / !KEYWORD_comptime) !KEYWORD_fn (IDENTIFIER COLON / !(IDENTIFIER COLON))? TypeExpr ByteAlign? (EQUAL Expr)?
857868fn expectContainerField(p: *Parse) !Node.Index {
858869 _ = p.eatToken(.keyword_comptime);
859870 const main_token = p.tok_i;
......@@ -895,16 +906,23 @@ fn expectContainerField(p: *Parse) !Node.Index {
895906 }
896907}
897908
898/// Statement
899/// <- KEYWORD_comptime ComptimeStatement
900/// / KEYWORD_nosuspend BlockExprStatement
901/// / KEYWORD_suspend BlockExprStatement
909/// BlockStatement
910/// <- Statement
902911/// / KEYWORD_defer BlockExprStatement
903912/// / KEYWORD_errdefer Payload? BlockExprStatement
904/// / IfStatement
913/// / !ExprStatement (KEYWORD_comptime !BlockExpr)? VarAssignStatement
914///
915/// Statement
916/// <- ExprStatement
917/// / KEYWORD_suspend BlockExprStatement
918/// / !ExprStatement (KEYWORD_comptime !BlockExpr)? AssignExpr SEMICOLON
919///
920/// ExprStatement
921/// <- IfStatement
905922/// / LabeledStatement
906/// / VarDeclExprStatement
907fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index {
923/// / KEYWORD_nosuspend BlockExprStatement
924/// / KEYWORD_comptime BlockExpr
925fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index {
908926 if (p.eatToken(.keyword_comptime)) |comptime_token| {
909927 const opt_block_expr = try p.parseBlockExpr();
910928 if (opt_block_expr) |block_expr| {
......@@ -915,7 +933,7 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index {
915933 });
916934 }
917935
918 if (allow_defer_var) {
936 if (is_block_level) {
919937 return p.expectVarDeclExprStatement(comptime_token);
920938 } else {
921939 const assign = try p.expectAssignExpr();
......@@ -949,12 +967,12 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index {
949967 .data = .{ .node = block_expr },
950968 });
951969 },
952 .keyword_defer => if (allow_defer_var) return p.addNode(.{
970 .keyword_defer => if (is_block_level) return p.addNode(.{
953971 .tag = .@"defer",
954972 .main_token = p.nextToken(),
955973 .data = .{ .node = try p.expectBlockExprStatement() },
956974 }),
957 .keyword_errdefer => if (allow_defer_var) return p.addNode(.{
975 .keyword_errdefer => if (is_block_level) return p.addNode(.{
958976 .tag = .@"errdefer",
959977 .main_token = p.nextToken(),
960978 .data = .{ .opt_token_and_node = .{
......@@ -979,7 +997,7 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index {
979997
980998 if (try p.parseLabeledStatement()) |labeled_statement| return labeled_statement;
981999
982 if (allow_defer_var) {
1000 if (is_block_level) {
9831001 return p.expectVarDeclExprStatement(null);
9841002 } else {
9851003 const assign = try p.expectAssignExpr();
......@@ -1007,8 +1025,10 @@ fn expectComptimeStatement(p: *Parse, comptime_token: TokenIndex) !Node.Index {
10071025}
10081026
10091027/// VarDeclExprStatement
1010/// <- VarDeclProto (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON
1011/// / Expr (AssignOp Expr / (COMMA (VarDeclProto / Expr))+ EQUAL Expr)? SEMICOLON
1028/// <- Expr
1029/// / VarAssignStatement
1030///
1031/// VarAssignStatement <- (VarDeclProto / Expr) (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON
10121032fn expectVarDeclExprStatement(p: *Parse, comptime_token: ?TokenIndex) !Node.Index {
10131033 const scratch_top = p.scratch.items.len;
10141034 defer p.scratch.shrinkRetainingCapacity(scratch_top);
......@@ -1140,7 +1160,7 @@ fn expectStatementRecoverable(p: *Parse) Error!?Node.Index {
11401160
11411161/// IfStatement
11421162/// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
1143/// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
1163/// / IfPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
11441164fn expectIfStatement(p: *Parse) !Node.Index {
11451165 const if_token = p.assertToken(.keyword_if);
11461166 _ = try p.expectToken(.l_paren);
......@@ -1235,8 +1255,8 @@ fn parseLoopStatement(p: *Parse) !?Node.Index {
12351255}
12361256
12371257/// ForStatement
1238/// <- ForPrefix BlockExpr ( KEYWORD_else Statement )?
1239/// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )
1258/// <- ForPrefix BlockExpr ( KEYWORD_else Statement / !KEYWORD_else )
1259/// / ForPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Statement )
12401260fn parseForStatement(p: *Parse) !?Node.Index {
12411261 const for_token = p.eatToken(.keyword_for) orelse return null;
12421262
......@@ -1293,7 +1313,7 @@ fn parseForStatement(p: *Parse) !?Node.Index {
12931313///
12941314/// WhileStatement
12951315/// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
1296/// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
1316/// / WhilePrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
12971317fn parseWhileStatement(p: *Parse) !?Node.Index {
12981318 const while_token = p.eatToken(.keyword_while) orelse return null;
12991319 _ = try p.expectToken(.l_paren);
......@@ -1383,7 +1403,7 @@ fn parseWhileStatement(p: *Parse) !?Node.Index {
13831403
13841404/// BlockExprStatement
13851405/// <- BlockExpr
1386/// / AssignExpr SEMICOLON
1406/// / !BlockExpr AssignExpr SEMICOLON
13871407fn parseBlockExprStatement(p: *Parse) !?Node.Index {
13881408 const block_expr = try p.parseBlockExpr();
13891409 if (block_expr) |expr| return expr;
......@@ -1685,18 +1705,20 @@ fn expectPrefixExpr(p: *Parse) Error!Node.Index {
16851705/// PrefixTypeOp
16861706/// <- QUESTIONMARK
16871707/// / KEYWORD_anyframe MINUSRARROW
1688/// / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
1708/// / (ManyPtrTypeStart / SliceTypeStart) KEYWORD_allowzero? ByteAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile?
1709/// / SinglePtrTypeStart KEYWORD_allowzero? BitAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile?
16891710/// / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
16901711/// / ArrayTypeStart
16911712///
16921713/// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET
16931714///
1694/// PtrTypeStart
1695/// <- ASTERISK
1696/// / ASTERISK2
1697/// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET
1715/// SinglePtrTypeStart <- ASTERISK / ASTERISK2
1716///
1717/// ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET
1718///
1719/// ArrayTypeStart <- LBRACKET Expr !(ASTERISK / ASTERISK2) (COLON Expr)? RBRACKET
16981720///
1699/// ArrayTypeStart <- LBRACKET Expr (COLON Expr)? RBRACKET
1721/// BitAlign <- KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN
17001722fn parseTypeExpr(p: *Parse) Error!?Node.Index {
17011723 switch (p.tokenTag(p.tok_i)) {
17021724 .question_mark => return try p.addNode(.{
......@@ -1962,12 +1984,12 @@ fn expectTypeExpr(p: *Parse) Error!Node.Index {
19621984/// PrimaryExpr
19631985/// <- AsmExpr
19641986/// / IfExpr
1965/// / KEYWORD_break BreakLabel? Expr?
1966/// / KEYWORD_comptime Expr
1967/// / KEYWORD_nosuspend Expr
1968/// / KEYWORD_continue BreakLabel? Expr?
1969/// / KEYWORD_resume Expr
1970/// / KEYWORD_return Expr?
1987/// / KEYWORD_break (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart)
1988/// / KEYWORD_comptime Expr !ExprSuffix
1989/// / KEYWORD_nosuspend Expr !ExprSuffix
1990/// / KEYWORD_continue (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart)
1991/// / KEYWORD_resume Expr !ExprSuffix
1992/// / KEYWORD_return (Expr !ExprSuffix / !SinglePtrTypeStart)
19711993/// / BlockLabel? LoopExpr
19721994/// / Block
19731995/// / CurlySuffixExpr
......@@ -2042,10 +2064,6 @@ fn parsePrimaryExpr(p: *Parse) !?Node.Index {
20422064 p.tok_i += 2;
20432065 return try p.parseWhileExpr();
20442066 },
2045 .l_brace => {
2046 p.tok_i += 2;
2047 return try p.parseBlock();
2048 },
20492067 else => return try p.parseCurlySuffixExpr(),
20502068 }
20512069 } else {
......@@ -2067,12 +2085,12 @@ fn parsePrimaryExpr(p: *Parse) !?Node.Index {
20672085 }
20682086}
20692087
2070/// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)?
2088/// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffix
20712089fn parseIfExpr(p: *Parse) !?Node.Index {
20722090 return try p.parseIf(expectExpr);
20732091}
20742092
2075/// Block <- LBRACE Statement* RBRACE
2093/// Block <- LBRACE BlockStatement* RBRACE
20762094fn parseBlock(p: *Parse) !?Node.Index {
20772095 const lbrace = p.eatToken(.l_brace) orelse return null;
20782096 const scratch_top = p.scratch.items.len;
......@@ -2177,7 +2195,7 @@ fn forPrefix(p: *Parse) Error!usize {
21772195
21782196/// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
21792197///
2180/// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
2198/// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffi
21812199fn parseWhileExpr(p: *Parse) !?Node.Index {
21822200 const while_token = p.eatToken(.keyword_while) orelse return null;
21832201 _ = try p.expectToken(.l_paren);
......@@ -2409,10 +2427,10 @@ fn parseSuffixExpr(p: *Parse) !?Node.Index {
24092427/// / FnProto
24102428/// / GroupedExpr
24112429/// / LabeledTypeExpr
2412/// / IDENTIFIER
2430/// / IDENTIFIER !(COLON LabelableExpr)
24132431/// / IfTypeExpr
24142432/// / INTEGER
2415/// / KEYWORD_comptime TypeExpr
2433/// / KEYWORD_comptime TypeExpr !ExprSuffix
24162434/// / KEYWORD_error DOT IDENTIFIER
24172435/// / KEYWORD_anyframe
24182436/// / KEYWORD_unreachable
......@@ -2431,7 +2449,7 @@ fn parseSuffixExpr(p: *Parse) !?Node.Index {
24312449///
24322450/// GroupedExpr <- LPAREN Expr RPAREN
24332451///
2434/// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
2452/// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix
24352453///
24362454/// LabeledTypeExpr
24372455/// <- BlockLabel Block
......@@ -2711,7 +2729,7 @@ fn expectPrimaryTypeExpr(p: *Parse) !Node.Index {
27112729
27122730/// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
27132731///
2714/// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
2732/// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix
27152733fn parseWhileTypeExpr(p: *Parse) !?Node.Index {
27162734 const while_token = p.eatToken(.keyword_while) orelse return null;
27172735 _ = try p.expectToken(.l_paren);
......@@ -2876,7 +2894,7 @@ fn expectAsmExpr(p: *Parse) !Node.Index {
28762894 });
28772895}
28782896
2879/// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
2897/// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
28802898fn parseAsmOutputItem(p: *Parse) !?Node.Index {
28812899 _ = p.eatToken(.l_bracket) orelse return null;
28822900 const identifier = try p.expectToken(.identifier);
......@@ -2902,7 +2920,7 @@ fn parseAsmOutputItem(p: *Parse) !?Node.Index {
29022920 });
29032921}
29042922
2905/// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
2923/// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN Expr RPAREN
29062924fn parseAsmInputItem(p: *Parse) !?Node.Index {
29072925 _ = p.eatToken(.l_bracket) orelse return null;
29082926 const identifier = try p.expectToken(.identifier);
......@@ -2923,9 +2941,7 @@ fn parseAsmInputItem(p: *Parse) !?Node.Index {
29232941
29242942/// BreakLabel <- COLON IDENTIFIER
29252943fn parseBreakLabel(p: *Parse) Error!OptionalTokenIndex {
2926 _ = p.eatToken(.colon) orelse return .none;
2927 const next_token = try p.expectToken(.identifier);
2928 return .fromToken(next_token);
2944 return if (p.eatTokens(&.{ .colon, .identifier })) |i| .fromToken(i + 1) else .none;
29292945}
29302946
29312947/// BlockLabel <- IDENTIFIER COLON
......@@ -2950,12 +2966,7 @@ fn expectFieldInit(p: *Parse) !Node.Index {
29502966
29512967/// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
29522968fn parseWhileContinueExpr(p: *Parse) !?Node.Index {
2953 _ = p.eatToken(.colon) orelse {
2954 if (p.tokenTag(p.tok_i) == .l_paren and
2955 p.tokensOnSameLine(p.tok_i - 1, p.tok_i))
2956 return p.fail(.expected_continue_expr);
2957 return null;
2958 };
2969 _ = p.eatToken(.colon) orelse return null;
29592970 _ = try p.expectToken(.l_paren);
29602971 const node = try p.parseAssignExpr() orelse return p.fail(.expected_expr_or_assignment);
29612972 _ = try p.expectToken(.r_paren);
......@@ -2993,9 +3004,7 @@ fn parseAddrSpace(p: *Parse) !?Node.Index {
29933004/// such as in the case of anytype and `...`. Caller must look for rparen to find
29943005/// out when there are no more param decls left.
29953006///
2996/// ParamDecl
2997/// <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
2998/// / DOT3
3007/// ParamDecl <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime / !KEYWORD_comptime) (IDENTIFIER COLON / !(IDENTIFIER_COLON)) ParamType
29993008///
30003009/// ParamType
30013010/// <- KEYWORD_anytype
......@@ -3482,7 +3491,7 @@ fn parseSwitchProngList(p: *Parse) !Node.SubRange {
34823491 return p.listToSpan(p.scratch.items[scratch_top..]);
34833492}
34843493
3485/// ParamDeclList <- (ParamDecl COMMA)* ParamDecl?
3494/// ParamDeclList <- (ParamDecl COMMA)* (ParamDecl / DOT3 COMMA?)?
34863495fn parseParamDeclList(p: *Parse) !SmallSpan {
34873496 _ = try p.expectToken(.l_paren);
34883497 const scratch_top = p.scratch.items.len;
......@@ -3604,9 +3613,9 @@ fn parseIf(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !?N
36043613 });
36053614}
36063615
3607/// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
3616/// ForExpr <- ForPrefix Expr (KEYWORD_else Expr / !KEYWORD_else) !ExprSuffix
36083617///
3609/// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?
3618/// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr / !KEYWORD_else) !ExprSuffix
36103619fn parseFor(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !?Node.Index {
36113620 const for_token = p.eatToken(.keyword_for) orelse return null;
36123621
lib/std/zig/parser_test.zig+30-9
......@@ -5472,17 +5472,11 @@ test "zig fmt: while continue expr" {
54725472 \\ while (i > 0)
54735473 \\ (i * 2);
54745474 \\}
5475 \\T: (while (true) ({
5476 \\ break usize;
5477 \\})),
54755478 \\
54765479 );
5477 try testError(
5478 \\test {
5479 \\ while (i > 0) (i -= 1) {
5480 \\ print("test123", .{});
5481 \\ }
5482 \\}
5483 , &[_]Error{
5484 .expected_continue_expr,
5485 });
54865480}
54875481
54885482test "zig fmt: canonicalize symbols (simple)" {
......@@ -6838,6 +6832,33 @@ test "zig fmt: error set with extra newline before comma" {
68386832 );
68396833}
68406834
6835test "zig fmt: extern container in tuple" {
6836 try testCanonical(
6837 \\const T = struct {
6838 \\ extern struct {},
6839 \\ extern union {},
6840 \\ extern enum {},
6841 \\};
6842 \\
6843 );
6844}
6845
6846test "zig fmt: break followed by colon" {
6847 try testCanonical(
6848 \\const a = [if (cond) len else break:0]u8;
6849 \\
6850 );
6851}
6852
6853test "zig fmt: array init of labeled block" {
6854 try testCanonical(
6855 \\const a = blk: {
6856 \\ break :blk T;
6857 \\}{ .a = false };
6858 \\
6859 );
6860}
6861
68416862test "recovery: top level" {
68426863 try testError(
68436864 \\test "" {inline}
lib/std/zig/tokenizer.zig+2-1
......@@ -313,7 +313,8 @@ pub const Token = struct {
313313 return tag.lexeme() orelse switch (tag) {
314314 .invalid => "invalid token",
315315 .identifier => "an identifier",
316 .string_literal, .multiline_string_literal_line => "a string literal",
316 .string_literal => "a string literal",
317 .multiline_string_literal_line => "a multiline string literal",
317318 .char_literal => "a character literal",
318319 .eof => "EOF",
319320 .builtin => "a builtin function",