authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-06-18 13:44:03+02:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-07-08 11:48:59+02:00
logd9c6669f72c6d6d12a6533282c07239deac0c9c7
treef9db157e55e80917a85eb744a96dbb3e67238912
parenta4254123ebb257c1c739ad5bb3c93049b51539db
signaturelock-open Commit is signed but in an unrecognized format.

grammar: introduce !ContainerDeclPrefix

Eliminate unbounded lookahead, match Parse.zig behavior

3 files changed, 97 insertions(+), 18 deletions(-)

doc/langref/grammar.peg+19-7
......@@ -39,9 +39,16 @@
3939Root <- ContainerMembers skip eof
4040
4141# *** Top level ***
42ContainerMembers <- container_doc_comment? ContainerDeclaration* (ContainerField COMMA)* (ContainerField / ContainerDeclaration*)
42ContainerMembers <- container_doc_comment? ContainerDecl* !ContainerDeclPrefix
43 (!ContainerDeclPrefix ContainerField COMMA)*
44 (!ContainerDeclPrefix ContainerField / ContainerDecl*)
4345
44ContainerDeclaration <- TestDecl / ComptimeDecl / doc_comment? KEYWORD_pub? Decl
46ContainerDecl <- TestDecl / ComptimeDecl / doc_comment? KEYWORD_pub? Decl
47
48ContainerDeclPrefix
49 <- KEYWORD_test
50 / KEYWORD_comptime LBRACE
51 / doc_comment? KEYWORD_pub? DeclPrefix
4552
4653TestDecl <- KEYWORD_test (STRINGLITERALSINGLE / IDENTIFIER)? Block
4754
......@@ -52,6 +59,11 @@ Decl
5259 / KEYWORD_extern STRINGLITERALSINGLE? FnProto SEMICOLON
5360 / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? GlobalVarDecl
5461
62DeclPrefix
63 <- (KEYWORD_export / KEYWORD_inline / KEYWORD_noinline)? KEYWORD_fn
64 / KEYWORD_extern STRINGLITERALSINGLE? KEYWORD_fn
65 / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? (KEYWORD_const / KEYWORD_var)
66
5567FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
5668
5769VarDeclProto <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection?
......@@ -165,7 +177,7 @@ SuffixExpr <- PrimaryTypeExpr SuffixOp* !SuffixOpPrefix
165177PrimaryTypeExpr
166178 <- BUILTINIDENTIFIER FnCallArguments
167179 / CHAR_LITERAL
168 / ContainerDecl
180 / ContainerType
169181 / DOT IDENTIFIER
170182 / DOT InitList
171183 / ErrorSetDecl
......@@ -181,7 +193,7 @@ PrimaryTypeExpr
181193 / NUMBERLITERAL
182194 / STRINGLITERAL
183195
184ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
196ContainerType <- (KEYWORD_extern / KEYWORD_packed)? ContainerTypeAuto
185197
186198ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE
187199
......@@ -387,10 +399,10 @@ ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET
387399
388400ArrayTypeStart <- LBRACKET !ASTERISK Expr (COLON Expr)? RBRACKET
389401
390# ContainerDecl specific
391ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE
402# ContainerType specific
403ContainerTypeAuto <- ContainerTypeKind LBRACE ContainerMembers RBRACE
392404
393ContainerDeclType
405ContainerTypeKind
394406 <- KEYWORD_struct (LPAREN Expr RPAREN)?
395407 / KEYWORD_opaque
396408 / KEYWORD_enum (LPAREN Expr RPAREN)?
lib/std/zig/parser_fuzz.zig+5
......@@ -73,6 +73,11 @@ test "return asterisk" {
7373 try checkAgainstOracle("test{return*!0;}");
7474}
7575
76// Found using AFL++
77test "fn container field" {
78 try checkAgainstOracle("fn()0");
79}
80
7681fn checkAgainstOracle(source: [:0]const u8) !void {
7782 var fba_buf: [1 << 18]u8 = undefined;
7883 var fba: std.heap.FixedBufferAllocator = .init(&fba_buf);
lib/std/zig/parser_generated_oracle.zig+73-11
......@@ -25,22 +25,37 @@ const Parser = struct {
2525 return blk_0: {
2626 const pos_0 = p.i;
2727 if ((p.parsecontainer_doc_comment() or true) and blk_1: {
28 while (p.parseContainerDeclaration()) {}
28 while (p.parseContainerDecl()) {}
2929 break :blk_1 true;
30 } and blk_1: {
31 const pos_1 = p.i;
32 const match_1 = p.parseContainerDeclPrefix();
33 p.i = pos_1;
34 break :blk_1 !match_1;
3035 } and blk_1: {
3136 while (blk_3: {
3237 const pos_3 = p.i;
33 if (p.parseContainerField() and p.parseCOMMA()) break :blk_3 true;
38 if (blk_4: {
39 const pos_4 = p.i;
40 const match_4 = p.parseContainerDeclPrefix();
41 p.i = pos_4;
42 break :blk_4 !match_4;
43 } and p.parseContainerField() and p.parseCOMMA()) break :blk_3 true;
3444 p.i = pos_3;
3545 break :blk_3 false;
3646 }) {}
3747 break :blk_1 true;
3848 } and blk_2: {
3949 const pos_2 = p.i;
40 if (p.parseContainerField()) break :blk_2 true;
50 if (blk_3: {
51 const pos_3 = p.i;
52 const match_3 = p.parseContainerDeclPrefix();
53 p.i = pos_3;
54 break :blk_3 !match_3;
55 } and p.parseContainerField()) break :blk_2 true;
4156 p.i = pos_2;
4257 if (blk_3: {
43 while (p.parseContainerDeclaration()) {}
58 while (p.parseContainerDecl()) {}
4459 break :blk_3 true;
4560 }) break :blk_2 true;
4661 p.i = pos_2;
......@@ -50,7 +65,7 @@ const Parser = struct {
5065 break :blk_0 false;
5166 };
5267 }
53 pub fn parseContainerDeclaration(p: *Parser) bool {
68 pub fn parseContainerDecl(p: *Parser) bool {
5469 return blk_0: {
5570 const pos_0 = p.i;
5671 if (p.parseTestDecl()) break :blk_0 true;
......@@ -62,6 +77,18 @@ const Parser = struct {
6277 break :blk_0 false;
6378 };
6479 }
80 pub fn parseContainerDeclPrefix(p: *Parser) bool {
81 return blk_0: {
82 const pos_0 = p.i;
83 if (p.parseKEYWORD_test()) break :blk_0 true;
84 p.i = pos_0;
85 if (p.parseKEYWORD_comptime() and p.parseLBRACE()) break :blk_0 true;
86 p.i = pos_0;
87 if ((p.parsedoc_comment() or true) and (p.parseKEYWORD_pub() or true) and p.parseDeclPrefix()) break :blk_0 true;
88 p.i = pos_0;
89 break :blk_0 false;
90 };
91 }
6592 pub fn parseTestDecl(p: *Parser) bool {
6693 return blk_0: {
6794 const pos_0 = p.i;
......@@ -120,6 +147,41 @@ const Parser = struct {
120147 break :blk_0 false;
121148 };
122149 }
150 pub fn parseDeclPrefix(p: *Parser) bool {
151 return blk_0: {
152 const pos_0 = p.i;
153 if ((blk_3: {
154 const pos_3 = p.i;
155 if (p.parseKEYWORD_export()) break :blk_3 true;
156 p.i = pos_3;
157 if (p.parseKEYWORD_inline()) break :blk_3 true;
158 p.i = pos_3;
159 if (p.parseKEYWORD_noinline()) break :blk_3 true;
160 p.i = pos_3;
161 break :blk_3 false;
162 } or true) and p.parseKEYWORD_fn()) break :blk_0 true;
163 p.i = pos_0;
164 if (p.parseKEYWORD_extern() and (p.parseSTRINGLITERALSINGLE() or true) and p.parseKEYWORD_fn()) break :blk_0 true;
165 p.i = pos_0;
166 if ((blk_3: {
167 const pos_3 = p.i;
168 if (p.parseKEYWORD_export()) break :blk_3 true;
169 p.i = pos_3;
170 if (p.parseKEYWORD_extern() and (p.parseSTRINGLITERALSINGLE() or true)) break :blk_3 true;
171 p.i = pos_3;
172 break :blk_3 false;
173 } or true) and (p.parseKEYWORD_threadlocal() or true) and blk_2: {
174 const pos_2 = p.i;
175 if (p.parseKEYWORD_const()) break :blk_2 true;
176 p.i = pos_2;
177 if (p.parseKEYWORD_var()) break :blk_2 true;
178 p.i = pos_2;
179 break :blk_2 false;
180 }) break :blk_0 true;
181 p.i = pos_0;
182 break :blk_0 false;
183 };
184 }
123185 pub fn parseFnProto(p: *Parser) bool {
124186 return blk_0: {
125187 const pos_0 = p.i;
......@@ -904,7 +966,7 @@ const Parser = struct {
904966 p.i = pos_0;
905967 if (p.parseCHAR_LITERAL()) break :blk_0 true;
906968 p.i = pos_0;
907 if (p.parseContainerDecl()) break :blk_0 true;
969 if (p.parseContainerType()) break :blk_0 true;
908970 p.i = pos_0;
909971 if (p.parseDOT() and p.parseIDENTIFIER()) break :blk_0 true;
910972 p.i = pos_0;
......@@ -937,7 +999,7 @@ const Parser = struct {
937999 break :blk_0 false;
9381000 };
9391001 }
940 pub fn parseContainerDecl(p: *Parser) bool {
1002 pub fn parseContainerType(p: *Parser) bool {
9411003 return blk_0: {
9421004 const pos_0 = p.i;
9431005 if ((blk_3: {
......@@ -947,7 +1009,7 @@ const Parser = struct {
9471009 if (p.parseKEYWORD_packed()) break :blk_3 true;
9481010 p.i = pos_3;
9491011 break :blk_3 false;
950 } or true) and p.parseContainerDeclAuto()) break :blk_0 true;
1012 } or true) and p.parseContainerTypeAuto()) break :blk_0 true;
9511013 p.i = pos_0;
9521014 break :blk_0 false;
9531015 };
......@@ -1828,15 +1890,15 @@ const Parser = struct {
18281890 break :blk_0 false;
18291891 };
18301892 }
1831 pub fn parseContainerDeclAuto(p: *Parser) bool {
1893 pub fn parseContainerTypeAuto(p: *Parser) bool {
18321894 return blk_0: {
18331895 const pos_0 = p.i;
1834 if (p.parseContainerDeclType() and p.parseLBRACE() and p.parseContainerMembers() and p.parseRBRACE()) break :blk_0 true;
1896 if (p.parseContainerTypeKind() and p.parseLBRACE() and p.parseContainerMembers() and p.parseRBRACE()) break :blk_0 true;
18351897 p.i = pos_0;
18361898 break :blk_0 false;
18371899 };
18381900 }
1839 pub fn parseContainerDeclType(p: *Parser) bool {
1901 pub fn parseContainerTypeKind(p: *Parser) bool {
18401902 return blk_0: {
18411903 const pos_0 = p.i;
18421904 if (p.parseKEYWORD_struct() and (blk_3: {