| author | |
| committer | |
| log | 3e0f55fcc7f90feb441b62cd8e84735646e50176 |
| tree | 36e3a3ae87c954c83ee8bc485ff436a6bc8b63b3 |
| parent | 022c76fc899d738e090d8a50e5591a1ec24ab779 |
| parent | 0b52cac43465bdd920a506e43b716a8754bb3426 |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31635
Reviewed-by: Andrew Kelley <andrew@ziglang.org>12 files changed, 3243 insertions(+), 710 deletions(-)
doc/langref.html.in+74-55| ... | @@ -7944,58 +7944,60 @@ TestDecl <- KEYWORD_test (STRINGLITERALSINGLE / IDENTIFIER)? Block | ... | @@ -7944,58 +7944,60 @@ TestDecl <- KEYWORD_test (STRINGLITERALSINGLE / IDENTIFIER)? Block |
| 7944 | ComptimeDecl <- KEYWORD_comptime Block | 7944 | ComptimeDecl <- KEYWORD_comptime Block |
| 7945 | 7945 | ||
| 7946 | Decl | 7946 | Decl |
| 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 | ||
| 7948 | / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? GlobalVarDecl | 7949 | / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? GlobalVarDecl |
| 7949 | 7950 | ||
| 7950 | FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr | 7951 | FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr !ExprSuffix |
| 7951 | 7952 | ||
| 7952 | VarDeclProto <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? | 7953 | VarDeclProto <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? |
| 7953 | 7954 | ||
| 7954 | GlobalVarDecl <- VarDeclProto (EQUAL Expr)? SEMICOLON | 7955 | GlobalVarDecl <- VarDeclProto (EQUAL Expr)? SEMICOLON |
| 7955 | 7956 | ||
| 7956 | ContainerField <- doc_comment? KEYWORD_comptime? !KEYWORD_fn (IDENTIFIER COLON)? TypeExpr ByteAlign? (EQUAL Expr)? | 7957 | ContainerField <- doc_comment? (KEYWORD_comptime / !KEYWORD_comptime) !KEYWORD_fn (IDENTIFIER COLON / !(IDENTIFIER COLON))? TypeExpr ByteAlign? (EQUAL Expr)? |
| 7957 | 7958 | ||
| 7958 | # *** Block Level *** | 7959 | # *** Block Level *** |
| 7959 | Statement | 7960 | BlockStatement |
| 7960 | <- KEYWORD_comptime ComptimeStatement | 7961 | <- Statement |
| 7961 | / KEYWORD_nosuspend BlockExprStatement | ||
| 7962 | / KEYWORD_suspend BlockExprStatement | ||
| 7963 | / KEYWORD_defer BlockExprStatement | 7962 | / KEYWORD_defer BlockExprStatement |
| 7964 | / KEYWORD_errdefer Payload? BlockExprStatement | 7963 | / KEYWORD_errdefer Payload? BlockExprStatement |
| 7965 | / IfStatement | 7964 | / !ExprStatement (KEYWORD_comptime !BlockExpr)? VarAssignStatement |
| 7966 | / LabeledStatement | ||
| 7967 | / VarDeclExprStatement | ||
| 7968 | 7965 | ||
| 7969 | ComptimeStatement | 7966 | Statement |
| 7970 | <- BlockExpr | 7967 | <- ExprStatement |
| 7971 | / VarDeclExprStatement | 7968 | / KEYWORD_suspend BlockExprStatement |
| 7969 | / !ExprStatement (KEYWORD_comptime !BlockExpr)? AssignExpr SEMICOLON | ||
| 7970 | |||
| 7971 | ExprStatement | ||
| 7972 | <- IfStatement | ||
| 7973 | / LabeledStatement | ||
| 7974 | / KEYWORD_nosuspend BlockExprStatement | ||
| 7975 | / KEYWORD_comptime BlockExpr | ||
| 7972 | 7976 | ||
| 7973 | IfStatement | 7977 | IfStatement |
| 7974 | <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? | 7978 | <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? |
| 7975 | / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | 7979 | / IfPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) |
| 7976 | 7980 | ||
| 7977 | LabeledStatement <- BlockLabel? (Block / LoopStatement / SwitchExpr) | 7981 | LabeledStatement <- BlockLabel? (Block / LoopStatement / SwitchExpr) |
| 7978 | 7982 | ||
| 7979 | LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) | 7983 | LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) |
| 7980 | 7984 | ||
| 7981 | ForStatement | 7985 | ForStatement |
| 7982 | <- ForPrefix BlockExpr ( KEYWORD_else Statement )? | 7986 | <- ForPrefix BlockExpr ( KEYWORD_else Statement / !KEYWORD_else ) |
| 7983 | / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) | 7987 | / ForPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Statement ) |
| 7984 | 7988 | ||
| 7985 | WhileStatement | 7989 | WhileStatement |
| 7986 | <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? | 7990 | <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? |
| 7987 | / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | 7991 | / WhilePrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) |
| 7988 | 7992 | ||
| 7989 | BlockExprStatement | 7993 | BlockExprStatement |
| 7990 | <- BlockExpr | 7994 | <- BlockExpr |
| 7991 | / AssignExpr SEMICOLON | 7995 | / !BlockExpr AssignExpr SEMICOLON |
| 7992 | 7996 | ||
| 7993 | BlockExpr <- BlockLabel? Block | 7997 | BlockExpr <- BlockLabel? Block |
| 7994 | 7998 | ||
| 7995 | # An expression, assignment, or any destructure, as a statement. | 7999 | # An assignment or a destructure whose LHS are all lvalue expressions or variable declarations. |
| 7996 | VarDeclExprStatement | 8000 | VarAssignStatement <- (VarDeclProto / Expr) (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON |
| 7997 | <- VarDeclProto (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON | ||
| 7998 | / Expr (AssignOp Expr / (COMMA (VarDeclProto / Expr))+ EQUAL Expr)? SEMICOLON | ||
| 7999 | 8001 | ||
| 8000 | # *** Expression Level *** | 8002 | # *** Expression Level *** |
| 8001 | 8003 | ||
| ... | @@ -8025,25 +8027,25 @@ PrefixExpr <- PrefixOp* PrimaryExpr | ... | @@ -8025,25 +8027,25 @@ PrefixExpr <- PrefixOp* PrimaryExpr |
| 8025 | PrimaryExpr | 8027 | PrimaryExpr |
| 8026 | <- AsmExpr | 8028 | <- AsmExpr |
| 8027 | / IfExpr | 8029 | / IfExpr |
| 8028 | / KEYWORD_break BreakLabel? Expr? | 8030 | / KEYWORD_break (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart) |
| 8029 | / KEYWORD_comptime Expr | 8031 | / KEYWORD_comptime Expr !ExprSuffix |
| 8030 | / KEYWORD_nosuspend Expr | 8032 | / KEYWORD_nosuspend Expr !ExprSuffix |
| 8031 | / KEYWORD_continue BreakLabel? Expr? | 8033 | / KEYWORD_continue (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart) |
| 8032 | / KEYWORD_resume Expr | 8034 | / KEYWORD_resume Expr !ExprSuffix |
| 8033 | / KEYWORD_return Expr? | 8035 | / KEYWORD_return (Expr !ExprSuffix / !SinglePtrTypeStart) |
| 8034 | / BlockLabel? LoopExpr | 8036 | / BlockLabel? LoopExpr |
| 8035 | / Block | 8037 | / Block |
| 8036 | / CurlySuffixExpr | 8038 | / CurlySuffixExpr |
| 8037 | 8039 | ||
| 8038 | IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? | 8040 | IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffix |
| 8039 | 8041 | ||
| 8040 | Block <- LBRACE Statement* RBRACE | 8042 | Block <- LBRACE BlockStatement* RBRACE |
| 8041 | 8043 | ||
| 8042 | LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) | 8044 | LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) |
| 8043 | 8045 | ||
| 8044 | ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? | 8046 | ForExpr <- ForPrefix Expr (KEYWORD_else Expr / !KEYWORD_else) !ExprSuffix |
| 8045 | 8047 | ||
| 8046 | WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? | 8048 | WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffix |
| 8047 | 8049 | ||
| 8048 | CurlySuffixExpr <- TypeExpr InitList? | 8050 | CurlySuffixExpr <- TypeExpr InitList? |
| 8049 | 8051 | ||
| ... | @@ -8070,10 +8072,10 @@ PrimaryTypeExpr | ... | @@ -8070,10 +8072,10 @@ PrimaryTypeExpr |
| 8070 | / FnProto | 8072 | / FnProto |
| 8071 | / GroupedExpr | 8073 | / GroupedExpr |
| 8072 | / LabeledTypeExpr | 8074 | / LabeledTypeExpr |
| 8073 | / IDENTIFIER | 8075 | / IDENTIFIER !(COLON LabelableExpr) |
| 8074 | / IfTypeExpr | 8076 | / IfTypeExpr |
| 8075 | / INTEGER | 8077 | / INTEGER |
| 8076 | / KEYWORD_comptime TypeExpr | 8078 | / KEYWORD_comptime TypeExpr !ExprSuffix |
| 8077 | / KEYWORD_error DOT IDENTIFIER | 8079 | / KEYWORD_error DOT IDENTIFIER |
| 8078 | / KEYWORD_anyframe | 8080 | / KEYWORD_anyframe |
| 8079 | / KEYWORD_unreachable | 8081 | / KEYWORD_unreachable |
| ... | @@ -8085,7 +8087,7 @@ ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE | ... | @@ -8085,7 +8087,7 @@ ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE |
| 8085 | 8087 | ||
| 8086 | GroupedExpr <- LPAREN Expr RPAREN | 8088 | GroupedExpr <- LPAREN Expr RPAREN |
| 8087 | 8089 | ||
| 8088 | IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? | 8090 | IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix |
| 8089 | 8091 | ||
| 8090 | LabeledTypeExpr | 8092 | LabeledTypeExpr |
| 8091 | <- BlockLabel Block | 8093 | <- BlockLabel Block |
| ... | @@ -8094,9 +8096,9 @@ LabeledTypeExpr | ... | @@ -8094,9 +8096,9 @@ LabeledTypeExpr |
| 8094 | 8096 | ||
| 8095 | LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) | 8097 | LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) |
| 8096 | 8098 | ||
| 8097 | ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? | 8099 | ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr / !KEYWORD_else) !ExprSuffix |
| 8098 | 8100 | ||
| 8099 | WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? | 8101 | WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix |
| 8100 | 8102 | ||
| 8101 | SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE | 8103 | SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE |
| 8102 | 8104 | ||
| ... | @@ -8105,11 +8107,11 @@ AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN | ... | @@ -8105,11 +8107,11 @@ AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN |
| 8105 | 8107 | ||
| 8106 | AsmOutput <- COLON AsmOutputList AsmInput? | 8108 | AsmOutput <- COLON AsmOutputList AsmInput? |
| 8107 | 8109 | ||
| 8108 | AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN | 8110 | AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN |
| 8109 | 8111 | ||
| 8110 | AsmInput <- COLON AsmInputList AsmClobbers? | 8112 | AsmInput <- COLON AsmInputList AsmClobbers? |
| 8111 | 8113 | ||
| 8112 | AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN | 8114 | AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN Expr RPAREN |
| 8113 | 8115 | ||
| 8114 | AsmClobbers <- COLON Expr | 8116 | AsmClobbers <- COLON Expr |
| 8115 | 8117 | ||
| ... | @@ -8129,9 +8131,7 @@ AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN | ... | @@ -8129,9 +8131,7 @@ AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN |
| 8129 | # Fn specific | 8131 | # Fn specific |
| 8130 | CallConv <- KEYWORD_callconv LPAREN Expr RPAREN | 8132 | CallConv <- KEYWORD_callconv LPAREN Expr RPAREN |
| 8131 | 8133 | ||
| 8132 | ParamDecl | 8134 | ParamDecl <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime / !KEYWORD_comptime) (IDENTIFIER COLON / !(IDENTIFIER_COLON)) ParamType |
| 8133 | <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType | ||
| 8134 | / DOT3 | ||
| 8135 | 8135 | ||
| 8136 | ParamType | 8136 | ParamType |
| 8137 | <- KEYWORD_anytype | 8137 | <- KEYWORD_anytype |
| ... | @@ -8237,8 +8237,8 @@ PrefixOp | ... | @@ -8237,8 +8237,8 @@ PrefixOp |
| 8237 | PrefixTypeOp | 8237 | PrefixTypeOp |
| 8238 | <- QUESTIONMARK | 8238 | <- QUESTIONMARK |
| 8239 | / KEYWORD_anyframe MINUSRARROW | 8239 | / KEYWORD_anyframe MINUSRARROW |
| 8240 | / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | 8240 | / (ManyPtrTypeStart / SliceTypeStart) KEYWORD_allowzero? ByteAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile? |
| 8241 | / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | 8241 | / SinglePtrTypeStart KEYWORD_allowzero? BitAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile? |
| 8242 | / ArrayTypeStart | 8242 | / ArrayTypeStart |
| 8243 | 8243 | ||
| 8244 | SuffixOp | 8244 | SuffixOp |
| ... | @@ -8249,15 +8249,31 @@ SuffixOp | ... | @@ -8249,15 +8249,31 @@ SuffixOp |
| 8249 | 8249 | ||
| 8250 | FnCallArguments <- LPAREN ExprList RPAREN | 8250 | FnCallArguments <- LPAREN ExprList RPAREN |
| 8251 | 8251 | ||
| 8252 | ExprSuffix | ||
| 8253 | <- KEYWORD_or | ||
| 8254 | / KEYWORD_and | ||
| 8255 | / CompareOp | ||
| 8256 | / BitwiseOp | ||
| 8257 | / BitShiftOp | ||
| 8258 | / AdditionOp | ||
| 8259 | / MultiplyOp | ||
| 8260 | / EXCLAMATIONMARK | ||
| 8261 | / SuffixOp | ||
| 8262 | / FnCallArguments | ||
| 8263 | |||
| 8264 | LabelableExpr | ||
| 8265 | <- Block | ||
| 8266 | / SwitchExpr | ||
| 8267 | / LoopExpr | ||
| 8268 | |||
| 8252 | # Ptr specific | 8269 | # Ptr specific |
| 8253 | SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET | 8270 | SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET |
| 8254 | 8271 | ||
| 8255 | PtrTypeStart | 8272 | SinglePtrTypeStart <- ASTERISK / ASTERISK2 |
| 8256 | <- ASTERISK | 8273 | |
| 8257 | / ASTERISK2 | 8274 | ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET |
| 8258 | / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET | ||
| 8259 | 8275 | ||
| 8260 | ArrayTypeStart <- LBRACKET Expr (COLON Expr)? RBRACKET | 8276 | ArrayTypeStart <- LBRACKET Expr !(ASTERISK / ASTERISK2) (COLON Expr)? RBRACKET |
| 8261 | 8277 | ||
| 8262 | # ContainerDecl specific | 8278 | # ContainerDecl specific |
| 8263 | ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE | 8279 | ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE |
| ... | @@ -8266,11 +8282,13 @@ ContainerDeclType | ... | @@ -8266,11 +8282,13 @@ ContainerDeclType |
| 8266 | <- KEYWORD_struct (LPAREN Expr RPAREN)? | 8282 | <- KEYWORD_struct (LPAREN Expr RPAREN)? |
| 8267 | / KEYWORD_opaque | 8283 | / KEYWORD_opaque |
| 8268 | / KEYWORD_enum (LPAREN Expr RPAREN)? | 8284 | / 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)? |
| 8270 | 8286 | ||
| 8271 | # Alignment | 8287 | # Alignment |
| 8272 | ByteAlign <- KEYWORD_align LPAREN Expr RPAREN | 8288 | ByteAlign <- KEYWORD_align LPAREN Expr RPAREN |
| 8273 | 8289 | ||
| 8290 | BitAlign <- KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN | ||
| 8291 | |||
| 8274 | # Lists | 8292 | # Lists |
| 8275 | IdentifierList <- (doc_comment? IDENTIFIER COMMA)* (doc_comment? IDENTIFIER)? | 8293 | IdentifierList <- (doc_comment? IDENTIFIER COMMA)* (doc_comment? IDENTIFIER)? |
| 8276 | 8294 | ||
| ... | @@ -8280,7 +8298,7 @@ AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem? | ... | @@ -8280,7 +8298,7 @@ AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem? |
| 8280 | 8298 | ||
| 8281 | AsmInputList <- (AsmInputItem COMMA)* AsmInputItem? | 8299 | AsmInputList <- (AsmInputItem COMMA)* AsmInputItem? |
| 8282 | 8300 | ||
| 8283 | ParamDeclList <- (ParamDecl COMMA)* ParamDecl? | 8301 | ParamDeclList <- (ParamDecl COMMA)* (ParamDecl / DOT3 COMMA?)? |
| 8284 | 8302 | ||
| 8285 | ExprList <- (Expr COMMA)* Expr? | 8303 | ExprList <- (Expr COMMA)* Expr? |
| 8286 | 8304 | ||
| ... | @@ -8337,6 +8355,7 @@ multibyte_utf8 <- | ... | @@ -8337,6 +8355,7 @@ multibyte_utf8 <- |
| 8337 | / oxC2_oxDF ox80_oxBF | 8355 | / oxC2_oxDF ox80_oxBF |
| 8338 | 8356 | ||
| 8339 | non_control_ascii <- [\040-\176] | 8357 | non_control_ascii <- [\040-\176] |
| 8358 | non_control_utf8 <- [\040-\377] | ||
| 8340 | 8359 | ||
| 8341 | char_escape | 8360 | char_escape |
| 8342 | <- "\\x" hex hex | 8361 | <- "\\x" hex hex |
| ... | @@ -8352,10 +8371,10 @@ string_char | ... | @@ -8352,10 +8371,10 @@ string_char |
| 8352 | / char_escape | 8371 | / char_escape |
| 8353 | / ![\\"\n] non_control_ascii | 8372 | / ![\\"\n] non_control_ascii |
| 8354 | 8373 | ||
| 8355 | container_doc_comment <- ('//!' [^\n]* [ \n]* skip)+ | 8374 | container_doc_comment <- ('//!' non_control_utf8* [ \n]* skip)+ |
| 8356 | doc_comment <- ('///' [^\n]* [ \n]* skip)+ | 8375 | doc_comment <- ('///' non_control_utf8* [ \n]* skip)+ |
| 8357 | line_comment <- '//' ![!/][^\n]* / '////' [^\n]* | 8376 | line_comment <- '//' ![!/] non_control_utf8* / '////' non_control_utf8* |
| 8358 | line_string <- ('\\\\' [^\n]* [ \n]*)+ | 8377 | line_string <- '\\\\' non_control_utf8* [ \n]* |
| 8359 | skip <- ([ \n] / line_comment)* | 8378 | skip <- ([ \n] / line_comment)* |
| 8360 | 8379 | ||
| 8361 | CHAR_LITERAL <- ['] char_char ['] skip | 8380 | CHAR_LITERAL <- ['] char_char ['] skip |
lib/std/Build/abi.zig+8-4| ... | @@ -235,7 +235,8 @@ pub const fuzz = struct { | ... | @@ -235,7 +235,8 @@ pub const fuzz = struct { |
| 235 | max: u64, | 235 | max: u64, |
| 236 | weight: u64, | 236 | weight: u64, |
| 237 | 237 | ||
| 238 | fn intFromValue(x: anytype) u64 { | 238 | /// `inline` to propogate comptimeness |
| 239 | inline fn intFromValue(x: anytype) u64 { | ||
| 239 | const T = @TypeOf(x); | 240 | const T = @TypeOf(x); |
| 240 | return switch (@typeInfo(T)) { | 241 | return switch (@typeInfo(T)) { |
| 241 | .comptime_int => x, | 242 | .comptime_int => x, |
| ... | @@ -269,11 +270,13 @@ pub const fuzz = struct { | ... | @@ -269,11 +270,13 @@ pub const fuzz = struct { |
| 269 | }; | 270 | }; |
| 270 | } | 271 | } |
| 271 | 272 | ||
| 272 | pub fn value(T: type, x: T, weight: u64) Weight { | 273 | /// `inline` to propogate comptimeness |
| 274 | pub inline fn value(T: type, x: T, weight: u64) Weight { | ||
| 273 | return .{ .min = intFromValue(x), .max = intFromValue(x), .weight = weight }; | 275 | return .{ .min = intFromValue(x), .max = intFromValue(x), .weight = weight }; |
| 274 | } | 276 | } |
| 275 | 277 | ||
| 276 | pub fn rangeAtMost(T: type, at_least: T, at_most: T, weight: u64) Weight { | 278 | /// `inline` to propogate comptimeness |
| 279 | pub inline fn rangeAtMost(T: type, at_least: T, at_most: T, weight: u64) Weight { | ||
| 277 | std.debug.assert(intFromValue(at_least) <= intFromValue(at_most)); | 280 | std.debug.assert(intFromValue(at_least) <= intFromValue(at_most)); |
| 278 | return .{ | 281 | return .{ |
| 279 | .min = intFromValue(at_least), | 282 | .min = intFromValue(at_least), |
| ... | @@ -282,7 +285,8 @@ pub const fuzz = struct { | ... | @@ -282,7 +285,8 @@ pub const fuzz = struct { |
| 282 | }; | 285 | }; |
| 283 | } | 286 | } |
| 284 | 287 | ||
| 285 | pub fn rangeLessThan(T: type, at_least: T, less_than: T, weight: u64) Weight { | 288 | /// `inline` to propogate comptimeness |
| 289 | pub inline fn rangeLessThan(T: type, at_least: T, less_than: T, weight: u64) Weight { | ||
| 286 | std.debug.assert(intFromValue(at_least) < intFromValue(less_than)); | 290 | std.debug.assert(intFromValue(at_least) < intFromValue(less_than)); |
| 287 | return .{ | 291 | return .{ |
| 288 | .min = intFromValue(at_least), | 292 | .min = intFromValue(at_least), |
lib/std/testing/Smith.zig+37-2| ... | @@ -205,6 +205,24 @@ pub noinline fn valueRangeLessThan(s: *Smith, T: type, at_least: T, less_than: T | ... | @@ -205,6 +205,24 @@ pub noinline fn valueRangeLessThan(s: *Smith, T: type, at_least: T, less_than: T |
| 205 | return s.valueRangeLessThanWithHash(T, at_least, less_than, firstHash()); | 205 | return s.valueRangeLessThanWithHash(T, at_least, less_than, firstHash()); |
| 206 | } | 206 | } |
| 207 | 207 | ||
| 208 | /// It is asserted `len` is nonzero. | ||
| 209 | /// It is asserted `len` fits within 64 bits. | ||
| 210 | // | ||
| 211 | // `noinline` to capture a unique return address | ||
| 212 | pub noinline fn index(s: *Smith, len: usize) usize { | ||
| 213 | @disableInstrumentation(); | ||
| 214 | return s.indexWithHash(len, firstHash()); | ||
| 215 | } | ||
| 216 | |||
| 217 | /// It is asserted that the weight of `false` is non-zero. | ||
| 218 | /// It is asserted that the weight of `true` is non-zero. | ||
| 219 | // | ||
| 220 | // `noinline` to capture a unique return address | ||
| 221 | pub noinline fn boolWeighted(s: *Smith, false_weight: u64, true_weight: u64) bool { | ||
| 222 | @disableInstrumentation(); | ||
| 223 | return s.boolWeightedWithHash(false_weight, true_weight, firstHash()); | ||
| 224 | } | ||
| 225 | |||
| 208 | /// This is similar to `value(bool)` however it is gauraunteed to eventually | 226 | /// This is similar to `value(bool)` however it is gauraunteed to eventually |
| 209 | /// return `true` and provides the fuzzer with an extra hint about the data. | 227 | /// return `true` and provides the fuzzer with an extra hint about the data. |
| 210 | // | 228 | // |
| ... | @@ -228,6 +246,7 @@ pub noinline fn eosWeighted(s: *Smith, weights: []const Weight) bool { | ... | @@ -228,6 +246,7 @@ pub noinline fn eosWeighted(s: *Smith, weights: []const Weight) bool { |
| 228 | /// This is similar to `value(bool)` however it is gauraunteed to eventually | 246 | /// This is similar to `value(bool)` however it is gauraunteed to eventually |
| 229 | /// return `true` and provides the fuzzer with an extra hint about the data. | 247 | /// return `true` and provides the fuzzer with an extra hint about the data. |
| 230 | /// | 248 | /// |
| 249 | /// It is asserted that the weight of `false` is non-zero. | ||
| 231 | /// It is asserted that the weight of `true` is non-zero. | 250 | /// It is asserted that the weight of `true` is non-zero. |
| 232 | // | 251 | // |
| 233 | // `noinline` to capture a unique return address | 252 | // `noinline` to capture a unique return address |
| ... | @@ -463,6 +482,24 @@ pub fn valueRangeLessThanWithHash(s: *Smith, T: type, at_least: T, less_than: T, | ... | @@ -463,6 +482,24 @@ pub fn valueRangeLessThanWithHash(s: *Smith, T: type, at_least: T, less_than: T, |
| 463 | return s.valueWeightedWithHash(T, &.{.rangeLessThan(T, at_least, less_than, 1)}, hash); | 482 | return s.valueWeightedWithHash(T, &.{.rangeLessThan(T, at_least, less_than, 1)}, hash); |
| 464 | } | 483 | } |
| 465 | 484 | ||
| 485 | /// It is asserted `len` is nonzero. | ||
| 486 | /// It is asserted `len` fits within 64 bits. | ||
| 487 | pub fn indexWithHash(s: *Smith, len: usize, hash: u32) usize { | ||
| 488 | @disableInstrumentation(); | ||
| 489 | assert(len != 0); | ||
| 490 | return @intCast(s.valueWeightedWithHash(u64, &.{.rangeLessThan(u64, 0, @intCast(len), 1)}, hash)); | ||
| 491 | } | ||
| 492 | |||
| 493 | /// It is asserted that the weight of `false` is non-zero. | ||
| 494 | /// It is asserted that the weight of `true` is non-zero. | ||
| 495 | pub fn boolWeightedWithHash(s: *Smith, false_weight: u64, true_weight: u64, hash: u32) bool { | ||
| 496 | @disableInstrumentation(); | ||
| 497 | return s.valueWeightedWithHash(bool, &.{ | ||
| 498 | .value(bool, false, false_weight), | ||
| 499 | .value(bool, true, true_weight), | ||
| 500 | }, hash); | ||
| 501 | } | ||
| 502 | |||
| 466 | /// This is similar to `value(bool)` however it is gauraunteed to eventually | 503 | /// This is similar to `value(bool)` however it is gauraunteed to eventually |
| 467 | /// return `true` and provides the fuzzer with an extra hint about the data. | 504 | /// return `true` and provides the fuzzer with an extra hint about the data. |
| 468 | pub fn eosWithHash(s: *Smith, hash: u32) bool { | 505 | pub fn eosWithHash(s: *Smith, hash: u32) bool { |
| ... | @@ -504,8 +541,6 @@ pub fn eosWeightedWithHash(s: *Smith, weights: []const Weight, hash: u32) bool { | ... | @@ -504,8 +541,6 @@ pub fn eosWeightedWithHash(s: *Smith, weights: []const Weight, hash: u32) bool { |
| 504 | /// | 541 | /// |
| 505 | /// It is asserted that the weight of `false` is non-zero. | 542 | /// It is asserted that the weight of `false` is non-zero. |
| 506 | /// It is asserted that the weight of `true` is non-zero. | 543 | /// It is asserted that the weight of `true` is non-zero. |
| 507 | // | ||
| 508 | // `noinline` to capture a unique return address | ||
| 509 | pub fn eosWeightedSimpleWithHash(s: *Smith, false_weight: u64, true_weight: u64, hash: u32) bool { | 544 | pub fn eosWeightedSimpleWithHash(s: *Smith, false_weight: u64, true_weight: u64, hash: u32) bool { |
| 510 | @disableInstrumentation(); | 545 | @disableInstrumentation(); |
| 511 | return s.eosWeightedWithHash(&.{ | 546 | return s.eosWeightedWithHash(&.{ |
lib/std/zig.zig+2| ... | @@ -23,6 +23,7 @@ pub const primitives = @import("zig/primitives.zig"); | ... | @@ -23,6 +23,7 @@ pub const primitives = @import("zig/primitives.zig"); |
| 23 | pub const isPrimitive = primitives.isPrimitive; | 23 | pub const isPrimitive = primitives.isPrimitive; |
| 24 | pub const Ast = @import("zig/Ast.zig"); | 24 | pub const Ast = @import("zig/Ast.zig"); |
| 25 | pub const AstGen = @import("zig/AstGen.zig"); | 25 | pub const AstGen = @import("zig/AstGen.zig"); |
| 26 | pub const AstSmith = @import("zig/AstSmith.zig"); | ||
| 26 | pub const Zir = @import("zig/Zir.zig"); | 27 | pub const Zir = @import("zig/Zir.zig"); |
| 27 | pub const Zoir = @import("zig/Zoir.zig"); | 28 | pub const Zoir = @import("zig/Zoir.zig"); |
| 28 | pub const ZonGen = @import("zig/ZonGen.zig"); | 29 | pub const ZonGen = @import("zig/ZonGen.zig"); |
| ... | @@ -1166,6 +1167,7 @@ pub const ClangCliParam = struct { | ... | @@ -1166,6 +1167,7 @@ pub const ClangCliParam = struct { |
| 1166 | test { | 1167 | test { |
| 1167 | _ = Ast; | 1168 | _ = Ast; |
| 1168 | _ = AstRlAnnotate; | 1169 | _ = AstRlAnnotate; |
| 1170 | _ = AstSmith; | ||
| 1169 | _ = BuiltinFn; | 1171 | _ = BuiltinFn; |
| 1170 | _ = Client; | 1172 | _ = Client; |
| 1171 | _ = ErrorBundle; | 1173 | _ = ErrorBundle; |
lib/std/zig/Ast.zig-4| ... | @@ -504,9 +504,6 @@ pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void | ... | @@ -504,9 +504,6 @@ pub fn renderError(tree: Ast, parse_error: Error, w: *Writer) Writer.Error!void |
| 504 | .varargs_nonfinal => { | 504 | .varargs_nonfinal => { |
| 505 | return w.writeAll("function prototype has parameter after varargs"); | 505 | return w.writeAll("function prototype has parameter after varargs"); |
| 506 | }, | 506 | }, |
| 507 | .expected_continue_expr => { | ||
| 508 | return w.writeAll("expected ':' before while continue expression"); | ||
| 509 | }, | ||
| 510 | 507 | ||
| 511 | .expected_semi_after_decl => { | 508 | .expected_semi_after_decl => { |
| 512 | return w.writeAll("expected ';' after declaration"); | 509 | return w.writeAll("expected ';' after declaration"); |
| ... | @@ -2888,7 +2885,6 @@ pub const Error = struct { | ... | @@ -2888,7 +2885,6 @@ pub const Error = struct { |
| 2888 | test_doc_comment, | 2885 | test_doc_comment, |
| 2889 | comptime_doc_comment, | 2886 | comptime_doc_comment, |
| 2890 | varargs_nonfinal, | 2887 | varargs_nonfinal, |
| 2891 | expected_continue_expr, | ||
| 2892 | expected_semi_after_decl, | 2888 | expected_semi_after_decl, |
| 2893 | expected_semi_after_stmt, | 2889 | expected_semi_after_stmt, |
| 2894 | expected_comma_after_field, | 2890 | expected_comma_after_field, |
lib/std/zig/Ast/Render.zig+186-458| ... | @@ -338,9 +338,16 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { | ... | @@ -338,9 +338,16 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { |
| 338 | try ais.maybeInsertNewline(); | 338 | try ais.maybeInsertNewline(); |
| 339 | 339 | ||
| 340 | const first_tok, const last_tok = tree.nodeData(node).token_and_token; | 340 | const first_tok, const last_tok = tree.nodeData(node).token_and_token; |
| 341 | for (first_tok..last_tok + 1) |i| { | 341 | for (first_tok..last_tok) |i| { |
| 342 | try renderToken(r, @intCast(i), .newline); | 342 | try renderToken(r, @intCast(i), .newline); |
| 343 | } | 343 | } |
| 344 | if (space != .skip) { | ||
| 345 | try renderToken(r, last_tok, .newline); | ||
| 346 | } else { | ||
| 347 | try renderToken(r, last_tok, .skip); | ||
| 348 | try ais.insertNewline(); // A newline is part of the token, so it still needs | ||
| 349 | // rendered here. | ||
| 350 | } | ||
| 344 | 351 | ||
| 345 | const next_token = last_tok + 1; | 352 | const next_token = last_tok + 1; |
| 346 | const next_token_tag = tree.tokenTag(next_token); | 353 | const next_token_tag = tree.tokenTag(next_token); |
| ... | @@ -645,10 +652,12 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { | ... | @@ -645,10 +652,12 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { |
| 645 | const lhs, const rhs = tree.nodeData(node).node_and_node; | 652 | const lhs, const rhs = tree.nodeData(node).node_and_node; |
| 646 | const lbracket = tree.firstToken(rhs) - 1; | 653 | const lbracket = tree.firstToken(rhs) - 1; |
| 647 | const rbracket = tree.lastToken(rhs) + 1; | 654 | const rbracket = tree.lastToken(rhs) + 1; |
| 655 | try renderExpression(r, lhs, .none); | ||
| 656 | // One lien check must come after rendering lhs since it can influence | ||
| 657 | // isLineOverIndented | ||
| 648 | const one_line = tree.tokensOnSameLine(lbracket, rbracket) and | 658 | const one_line = tree.tokensOnSameLine(lbracket, rbracket) and |
| 649 | !becomesMultilineExpr(tree, rhs); | 659 | !try rendersMultiline(r, rhs); |
| 650 | const inner_space = if (one_line) Space.none else Space.newline; | 660 | const inner_space = if (one_line) Space.none else Space.newline; |
| 651 | try renderExpression(r, lhs, .none); | ||
| 652 | try ais.pushIndent(.normal); | 661 | try ais.pushIndent(.normal); |
| 653 | try renderToken(r, lbracket, inner_space); // [ | 662 | try renderToken(r, lbracket, inner_space); // [ |
| 654 | try renderExpression(r, rhs, inner_space); | 663 | try renderExpression(r, rhs, inner_space); |
| ... | @@ -793,34 +802,47 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { | ... | @@ -793,34 +802,47 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { |
| 793 | canonicalize: { | 802 | canonicalize: { |
| 794 | if (params.len != 1) break :canonicalize; | 803 | if (params.len != 1) break :canonicalize; |
| 795 | 804 | ||
| 796 | const CastKind = enum { | 805 | const CastKind = enum(u8) { |
| 797 | ptrCast, | 806 | ptrCast, |
| 798 | alignCast, | 807 | alignCast, |
| 799 | addrSpaceCast, | 808 | addrSpaceCast, |
| 800 | constCast, | 809 | constCast, |
| 801 | volatileCast, | 810 | volatileCast, |
| 802 | }; | 811 | }; |
| 803 | const kind = meta.stringToEnum(CastKind, tree.tokenSlice(builtin_token)[1..]) orelse break :canonicalize; | 812 | const kind = meta.stringToEnum( |
| 813 | CastKind, | ||
| 814 | tree.tokenSlice(builtin_token)[1..], | ||
| 815 | ) orelse break :canonicalize; | ||
| 804 | 816 | ||
| 805 | var cast_map = std.EnumMap(CastKind, Ast.TokenIndex).init(.{}); | 817 | var cast_map = std.EnumMap(CastKind, Ast.TokenIndex).init(.{}); |
| 806 | cast_map.put(kind, builtin_token); | 818 | cast_map.put(kind, builtin_token); |
| 807 | 819 | ||
| 808 | var casts_before: usize = 0; | 820 | var casts_before: usize = 0; |
| 809 | if (builtin_token >= 2) { | 821 | var prev_builtin_token = builtin_token; |
| 810 | var prev_builtin_token = builtin_token - 2; | 822 | while (prev_builtin_token >= 2) { |
| 811 | while (tree.tokenTag(prev_builtin_token) == .builtin) : (prev_builtin_token -= 2) { | 823 | prev_builtin_token -= 2; |
| 812 | const prev_kind = meta.stringToEnum(CastKind, tree.tokenSlice(prev_builtin_token)[1..]) orelse break; | 824 | if (tree.tokenTag(prev_builtin_token) != .builtin) break; |
| 813 | if (cast_map.contains(prev_kind)) break :canonicalize; | 825 | const builtin_name = tree.tokenSlice(prev_builtin_token)[1..]; |
| 814 | cast_map.put(prev_kind, prev_builtin_token); | 826 | const prev_kind = meta.stringToEnum(CastKind, builtin_name) orelse break; |
| 815 | casts_before += 1; | 827 | if (cast_map.contains(prev_kind)) break :canonicalize; |
| 816 | } | 828 | // This must be checked after so that cast builtins as arguments to other |
| 829 | // builtins containing comments are reordered. | ||
| 830 | if (hasComment(tree, prev_builtin_token, prev_builtin_token + 2)) | ||
| 831 | break :canonicalize; | ||
| 832 | cast_map.put(prev_kind, prev_builtin_token); | ||
| 833 | casts_before += 1; | ||
| 817 | } | 834 | } |
| 818 | 835 | ||
| 819 | var next_builtin_token = builtin_token + 2; | 836 | var next_builtin_token = builtin_token + 2; |
| 820 | while (tree.tokenTag(next_builtin_token) == .builtin) : (next_builtin_token += 2) { | 837 | while (true) { |
| 821 | const next_kind = meta.stringToEnum(CastKind, tree.tokenSlice(next_builtin_token)[1..]) orelse break; | 838 | if (hasComment(tree, next_builtin_token - 2, next_builtin_token)) |
| 839 | break :canonicalize; | ||
| 840 | if (tree.tokenTag(next_builtin_token) != .builtin) break; | ||
| 841 | const builtin_name = tree.tokenSlice(next_builtin_token)[1..]; | ||
| 842 | const next_kind = meta.stringToEnum(CastKind, builtin_name) orelse break; | ||
| 822 | if (cast_map.contains(next_kind)) break :canonicalize; | 843 | if (cast_map.contains(next_kind)) break :canonicalize; |
| 823 | cast_map.put(next_kind, next_builtin_token); | 844 | cast_map.put(next_kind, next_builtin_token); |
| 845 | next_builtin_token += 2; | ||
| 824 | } | 846 | } |
| 825 | 847 | ||
| 826 | var it = cast_map.iterator(); | 848 | var it = cast_map.iterator(); |
| ... | @@ -931,380 +953,61 @@ fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!v | ... | @@ -931,380 +953,61 @@ fn renderExpressionFixup(r: *Render, node: Ast.Node.Index, space: Space) Error!v |
| 931 | } | 953 | } |
| 932 | } | 954 | } |
| 933 | 955 | ||
| 934 | /// Same as becomesMultilineExpr, but returns false when `node == .none` | 956 | fn drainNoNewline(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize { |
| 935 | fn optBecomesMultilineExpr(tree: Ast, node: Ast.Node.OptionalIndex) bool { | 957 | if (std.mem.indexOfScalar(u8, w.buffered(), '\n') != null) { |
| 936 | return if (node.unwrap()) |payload| becomesMultilineExpr(tree, payload) else false; | 958 | return error.WriteFailed; |
| 937 | } | 959 | } |
| 938 | 960 | ||
| 939 | /// May return false if `node` is already multiline | 961 | var n: usize = 0; |
| 940 | fn becomesMultilineExpr(tree: Ast, node: Ast.Node.Index) bool { | 962 | for (data[0 .. data.len - 1]) |v| { |
| 941 | // Conditions related to comments, doc comments, and multiline string literals are ignored | 963 | if (std.mem.indexOfScalar(u8, v, '\n') != null) { |
| 942 | // since they always go to the end of the line, which already make them a multi-line | 964 | return error.WriteFailed; |
| 943 | // expression (since they contain a newline). | 965 | } |
| 944 | switch (tree.nodeTag(node)) { | 966 | n += v.len; |
| 945 | .identifier, | 967 | } |
| 946 | .number_literal, | ||
| 947 | .char_literal, | ||
| 948 | .unreachable_literal, | ||
| 949 | .anyframe_literal, | ||
| 950 | .string_literal, | ||
| 951 | .multiline_string_literal, | ||
| 952 | .error_value, | ||
| 953 | .enum_literal, | ||
| 954 | => return false, | ||
| 955 | .container_decl_trailing, | ||
| 956 | .container_decl_arg_trailing, | ||
| 957 | .container_decl_two_trailing, | ||
| 958 | .tagged_union_trailing, | ||
| 959 | .tagged_union_enum_tag_trailing, | ||
| 960 | .tagged_union_two_trailing, | ||
| 961 | .switch_comma, | ||
| 962 | .builtin_call_two_comma, | ||
| 963 | .builtin_call_comma, | ||
| 964 | .call_one_comma, | ||
| 965 | .call_comma, | ||
| 966 | .struct_init_one_comma, | ||
| 967 | .struct_init_dot_two_comma, | ||
| 968 | .struct_init_dot_comma, | ||
| 969 | .struct_init_comma, | ||
| 970 | .array_init_one_comma, | ||
| 971 | .array_init_dot_two_comma, | ||
| 972 | .array_init_dot_comma, | ||
| 973 | .array_init_comma, | ||
| 974 | // The following always have a non-zero amount of members | ||
| 975 | // which is also the condition for them to be multi-line. | ||
| 976 | .block, | ||
| 977 | .block_semicolon, | ||
| 978 | => return true, | ||
| 979 | .block_two, | ||
| 980 | .block_two_semicolon, | ||
| 981 | => return tree.nodeData(node).opt_node_and_opt_node[0] != .none, | ||
| 982 | .container_decl, | ||
| 983 | .container_decl_arg, | ||
| 984 | .container_decl_two, | ||
| 985 | .tagged_union, | ||
| 986 | .tagged_union_enum_tag, | ||
| 987 | .tagged_union_two, | ||
| 988 | => { | ||
| 989 | var buf: [2]Ast.Node.Index = undefined; | ||
| 990 | const full = tree.fullContainerDecl(&buf, node).?; | ||
| 991 | if (full.ast.arg.unwrap()) |arg| { | ||
| 992 | if (becomesMultilineExpr(tree, arg)) | ||
| 993 | return true; | ||
| 994 | } | ||
| 995 | // This does the same checks as `isOneLineContainerDecl`, however it avoids unnecessary | ||
| 996 | // checks related to comments and multiline strings, which would mean the container is | ||
| 997 | // already multiple lines. | ||
| 998 | for (full.ast.members) |member| { | ||
| 999 | if (tree.fullContainerField(member)) |field_full| { | ||
| 1000 | for ([_]Ast.Node.OptionalIndex{ | ||
| 1001 | field_full.ast.type_expr, | ||
| 1002 | field_full.ast.align_expr, | ||
| 1003 | field_full.ast.value_expr, | ||
| 1004 | }) |opt_expr| { | ||
| 1005 | if (opt_expr.unwrap()) |expr| { | ||
| 1006 | if (becomesMultilineExpr(tree, expr)) | ||
| 1007 | return true; | ||
| 1008 | } | ||
| 1009 | } | ||
| 1010 | } else return true; | ||
| 1011 | } | ||
| 1012 | return false; | ||
| 1013 | }, | ||
| 1014 | .error_set_decl => { | ||
| 1015 | const lbrace, const rbrace = tree.nodeData(node).token_and_token; | ||
| 1016 | return !isOneLineErrorSetDecl(tree, lbrace, rbrace); | ||
| 1017 | }, | ||
| 1018 | .@"switch" => { | ||
| 1019 | const op, const extra_index = tree.nodeData(node).node_and_extra; | ||
| 1020 | const case_range = tree.extraData(extra_index, Ast.Node.SubRange); | ||
| 1021 | return @intFromEnum(case_range.end) - @intFromEnum(case_range.start) != 0 or | ||
| 1022 | becomesMultilineExpr(tree, op); | ||
| 1023 | }, | ||
| 1024 | .for_simple, .@"for" => { | ||
| 1025 | const full = tree.fullFor(node).?; | ||
| 1026 | if (becomesMultilineExpr(tree, full.ast.then_expr) or | ||
| 1027 | optBecomesMultilineExpr(tree, full.ast.else_expr)) | ||
| 1028 | return true; | ||
| 1029 | |||
| 1030 | for (full.ast.inputs) |expr| { | ||
| 1031 | if (if (tree.nodeTag(expr) == .for_range) blk: { | ||
| 1032 | const lhs, const rhs = tree.nodeData(expr).node_and_opt_node; | ||
| 1033 | break :blk becomesMultilineExpr(tree, lhs) or optBecomesMultilineExpr(tree, rhs); | ||
| 1034 | } else becomesMultilineExpr(tree, expr)) | ||
| 1035 | return true; | ||
| 1036 | } | ||
| 1037 | const final_input_expr = full.ast.inputs[full.ast.inputs.len - 1]; | ||
| 1038 | if (tree.tokenTag(tree.lastToken(final_input_expr) + 1) == .comma) | ||
| 1039 | return true; | ||
| 1040 | |||
| 1041 | const token_tags = tree.tokens.items(.tag); | ||
| 1042 | const payload = full.payload_token; | ||
| 1043 | const pipe = std.mem.indexOfScalarPos(Token.Tag, token_tags, payload, .pipe).?; | ||
| 1044 | return token_tags[@intCast(pipe - 1)] == .comma; | ||
| 1045 | }, | ||
| 1046 | .while_simple, | ||
| 1047 | .while_cont, | ||
| 1048 | .@"while", | ||
| 1049 | => { | ||
| 1050 | const full = tree.fullWhile(node).?; | ||
| 1051 | return becomesMultilineExpr(tree, full.ast.cond_expr) or | ||
| 1052 | becomesMultilineExpr(tree, full.ast.then_expr) or | ||
| 1053 | optBecomesMultilineExpr(tree, full.ast.cont_expr) or | ||
| 1054 | optBecomesMultilineExpr(tree, full.ast.else_expr); | ||
| 1055 | }, | ||
| 1056 | .if_simple, | ||
| 1057 | .@"if", | ||
| 1058 | => { | ||
| 1059 | const full = tree.fullIf(node).?; | ||
| 1060 | return becomesMultilineExpr(tree, full.ast.cond_expr) or | ||
| 1061 | becomesMultilineExpr(tree, full.ast.then_expr) or | ||
| 1062 | optBecomesMultilineExpr(tree, full.ast.else_expr); | ||
| 1063 | }, | ||
| 1064 | .fn_proto_simple, | ||
| 1065 | .fn_proto_multi, | ||
| 1066 | .fn_proto_one, | ||
| 1067 | .fn_proto, | ||
| 1068 | => { | ||
| 1069 | var buf: [1]Ast.Node.Index = undefined; | ||
| 1070 | const fn_proto = tree.fullFnProto(&buf, node).?; | ||
| 1071 | |||
| 1072 | for ([_]Ast.Node.OptionalIndex{ | ||
| 1073 | fn_proto.ast.return_type, | ||
| 1074 | fn_proto.ast.align_expr, | ||
| 1075 | fn_proto.ast.addrspace_expr, | ||
| 1076 | fn_proto.ast.section_expr, | ||
| 1077 | fn_proto.ast.callconv_expr, | ||
| 1078 | }) |opt_expr| { | ||
| 1079 | if (opt_expr.unwrap()) |expr| { | ||
| 1080 | if (becomesMultilineExpr(tree, expr)) | ||
| 1081 | return true; | ||
| 1082 | } | ||
| 1083 | } | ||
| 1084 | for (fn_proto.ast.params) |expr| { | ||
| 1085 | if (becomesMultilineExpr(tree, expr)) | ||
| 1086 | return true; | ||
| 1087 | } | ||
| 1088 | 968 | ||
| 1089 | const lparen = fn_proto.ast.fn_token + 1; | 969 | const pattern = data[data.len - 1]; |
| 1090 | const return_type = fn_proto.ast.return_type.unwrap().?; | 970 | if (splat != 0 and std.mem.indexOfScalar(u8, pattern, '\n') != null) { |
| 1091 | const maybe_bang = tree.firstToken(return_type) - 1; | 971 | return error.WriteFailed; |
| 1092 | const rparen = fnProtoRparen(tree, fn_proto, maybe_bang); | ||
| 1093 | return !isOneLineFnProto(tree, fn_proto, lparen, rparen); | ||
| 1094 | }, | ||
| 1095 | .asm_simple, | ||
| 1096 | => { | ||
| 1097 | const lhs = tree.nodeData(node).node_and_token[0]; | ||
| 1098 | return becomesMultilineExpr(tree, lhs); | ||
| 1099 | }, | ||
| 1100 | .@"asm", | ||
| 1101 | => { | ||
| 1102 | const lhs, const extra_index = tree.nodeData(node).node_and_extra; | ||
| 1103 | const asm_extra = tree.extraData(extra_index, Ast.Node.Asm); | ||
| 1104 | return @intFromEnum(asm_extra.items_end) - @intFromEnum(asm_extra.items_start) != 0 or | ||
| 1105 | becomesMultilineExpr(tree, lhs) or optBecomesMultilineExpr(tree, asm_extra.clobbers); | ||
| 1106 | }, | ||
| 1107 | .array_type, .array_type_sentinel => { | ||
| 1108 | const array_type = tree.fullArrayType(node).?; | ||
| 1109 | const rbracket = tree.firstToken(array_type.ast.elem_type) - 1; | ||
| 1110 | return !isOneLineArrayType(tree, array_type, rbracket) or | ||
| 1111 | becomesMultilineExpr(tree, array_type.ast.elem_type); | ||
| 1112 | }, | ||
| 1113 | .array_access => { | ||
| 1114 | const lhs, const rhs = tree.nodeData(node).node_and_node; | ||
| 1115 | const lbracket = tree.firstToken(rhs) - 1; | ||
| 1116 | const rbracket = tree.lastToken(rhs) + 1; | ||
| 1117 | return !tree.tokensOnSameLine(lbracket, rbracket) or | ||
| 1118 | becomesMultilineExpr(tree, lhs) or | ||
| 1119 | becomesMultilineExpr(tree, rhs); | ||
| 1120 | }, | ||
| 1121 | .call_one, | ||
| 1122 | .call, | ||
| 1123 | .builtin_call_two, | ||
| 1124 | .builtin_call, | ||
| 1125 | .array_init_one, | ||
| 1126 | .array_init_dot_two, | ||
| 1127 | .array_init_dot, | ||
| 1128 | .array_init, | ||
| 1129 | .struct_init_one, | ||
| 1130 | .struct_init_dot_two, | ||
| 1131 | .struct_init_dot, | ||
| 1132 | .struct_init, | ||
| 1133 | => |tag| { | ||
| 1134 | var buf: [2]Ast.Node.Index = undefined; | ||
| 1135 | const opt_lhs: Ast.Node.OptionalIndex, const items = switch (tag) { | ||
| 1136 | .call_one, .call => blk: { | ||
| 1137 | const full = tree.fullCall(buf[0..1], node).?; | ||
| 1138 | break :blk .{ full.ast.fn_expr.toOptional(), full.ast.params }; | ||
| 1139 | }, | ||
| 1140 | .builtin_call_two, .builtin_call => .{ .none, tree.builtinCallParams(&buf, node).? }, | ||
| 1141 | .array_init_one, | ||
| 1142 | .array_init_dot_two, | ||
| 1143 | .array_init_dot, | ||
| 1144 | .array_init, | ||
| 1145 | => blk: { | ||
| 1146 | const full = tree.fullArrayInit(&buf, node).?; | ||
| 1147 | break :blk .{ full.ast.type_expr, full.ast.elements }; | ||
| 1148 | }, | ||
| 1149 | .struct_init_one, | ||
| 1150 | .struct_init_dot_two, | ||
| 1151 | .struct_init_dot, | ||
| 1152 | .struct_init, | ||
| 1153 | => blk: { | ||
| 1154 | const full = tree.fullStructInit(&buf, node).?; | ||
| 1155 | break :blk .{ full.ast.type_expr, full.ast.fields }; | ||
| 1156 | }, | ||
| 1157 | else => unreachable, | ||
| 1158 | }; | ||
| 1159 | if (opt_lhs.unwrap()) |lhs| { | ||
| 1160 | if (becomesMultilineExpr(tree, lhs)) | ||
| 1161 | return true; | ||
| 1162 | } | ||
| 1163 | for (items) |expr| { | ||
| 1164 | if (becomesMultilineExpr(tree, expr)) | ||
| 1165 | return true; | ||
| 1166 | } | ||
| 1167 | return false; | ||
| 1168 | }, | ||
| 1169 | .assign_destructure => { | ||
| 1170 | const full = tree.assignDestructure(node); | ||
| 1171 | for (full.ast.variables) |expr| { | ||
| 1172 | if (becomesMultilineExpr(tree, expr)) | ||
| 1173 | return true; | ||
| 1174 | } | ||
| 1175 | return becomesMultilineExpr(tree, full.ast.value_expr); | ||
| 1176 | }, | ||
| 1177 | .ptr_type_aligned, | ||
| 1178 | .ptr_type_sentinel, | ||
| 1179 | .ptr_type, | ||
| 1180 | .ptr_type_bit_range, | ||
| 1181 | => { | ||
| 1182 | const full = tree.fullPtrType(node).?; | ||
| 1183 | return becomesMultilineExpr(tree, full.ast.child_type) or | ||
| 1184 | optBecomesMultilineExpr(tree, full.ast.sentinel) or | ||
| 1185 | optBecomesMultilineExpr(tree, full.ast.align_node) or | ||
| 1186 | optBecomesMultilineExpr(tree, full.ast.addrspace_node) or | ||
| 1187 | optBecomesMultilineExpr(tree, full.ast.bit_range_start) or | ||
| 1188 | optBecomesMultilineExpr(tree, full.ast.bit_range_end); | ||
| 1189 | }, | ||
| 1190 | .slice_open, | ||
| 1191 | .slice, | ||
| 1192 | .slice_sentinel, | ||
| 1193 | => { | ||
| 1194 | const full = tree.fullSlice(node).?; | ||
| 1195 | return becomesMultilineExpr(tree, full.ast.sliced) or | ||
| 1196 | becomesMultilineExpr(tree, full.ast.start) or | ||
| 1197 | optBecomesMultilineExpr(tree, full.ast.end) or | ||
| 1198 | optBecomesMultilineExpr(tree, full.ast.sentinel); | ||
| 1199 | }, | ||
| 1200 | .@"comptime", | ||
| 1201 | .@"nosuspend", | ||
| 1202 | .@"suspend", | ||
| 1203 | .@"resume", | ||
| 1204 | .bit_not, | ||
| 1205 | .bool_not, | ||
| 1206 | .negation, | ||
| 1207 | .negation_wrap, | ||
| 1208 | .optional_type, | ||
| 1209 | .address_of, | ||
| 1210 | .deref, | ||
| 1211 | .@"try", | ||
| 1212 | => return becomesMultilineExpr(tree, tree.nodeData(node).node), | ||
| 1213 | .@"return" => return optBecomesMultilineExpr(tree, tree.nodeData(node).opt_node), | ||
| 1214 | .field_access, | ||
| 1215 | .unwrap_optional, | ||
| 1216 | .grouped_expression, | ||
| 1217 | => return becomesMultilineExpr(tree, tree.nodeData(node).node_and_token[0]), | ||
| 1218 | .add, | ||
| 1219 | .add_wrap, | ||
| 1220 | .add_sat, | ||
| 1221 | .array_cat, | ||
| 1222 | .array_mult, | ||
| 1223 | .bang_equal, | ||
| 1224 | .bit_and, | ||
| 1225 | .bit_or, | ||
| 1226 | .shl, | ||
| 1227 | .shl_sat, | ||
| 1228 | .shr, | ||
| 1229 | .bit_xor, | ||
| 1230 | .bool_and, | ||
| 1231 | .bool_or, | ||
| 1232 | .div, | ||
| 1233 | .equal_equal, | ||
| 1234 | .greater_or_equal, | ||
| 1235 | .greater_than, | ||
| 1236 | .less_or_equal, | ||
| 1237 | .less_than, | ||
| 1238 | .merge_error_sets, | ||
| 1239 | .mod, | ||
| 1240 | .mul, | ||
| 1241 | .mul_wrap, | ||
| 1242 | .mul_sat, | ||
| 1243 | .sub, | ||
| 1244 | .sub_wrap, | ||
| 1245 | .sub_sat, | ||
| 1246 | .@"orelse", | ||
| 1247 | .@"catch", | ||
| 1248 | .error_union, | ||
| 1249 | .assign, | ||
| 1250 | .assign_bit_and, | ||
| 1251 | .assign_bit_or, | ||
| 1252 | .assign_shl, | ||
| 1253 | .assign_shl_sat, | ||
| 1254 | .assign_shr, | ||
| 1255 | .assign_bit_xor, | ||
| 1256 | .assign_div, | ||
| 1257 | .assign_sub, | ||
| 1258 | .assign_sub_wrap, | ||
| 1259 | .assign_sub_sat, | ||
| 1260 | .assign_mod, | ||
| 1261 | .assign_add, | ||
| 1262 | .assign_add_wrap, | ||
| 1263 | .assign_add_sat, | ||
| 1264 | .assign_mul, | ||
| 1265 | .assign_mul_wrap, | ||
| 1266 | .assign_mul_sat, | ||
| 1267 | => { | ||
| 1268 | const lhs, const rhs = tree.nodeData(node).node_and_node; | ||
| 1269 | return becomesMultilineExpr(tree, lhs) or becomesMultilineExpr(tree, rhs); | ||
| 1270 | }, | ||
| 1271 | .@"break", .@"continue" => { | ||
| 1272 | const opt_expr = tree.nodeData(node).opt_token_and_opt_node[1]; | ||
| 1273 | return optBecomesMultilineExpr(tree, opt_expr); | ||
| 1274 | }, | ||
| 1275 | .anyframe_type => return becomesMultilineExpr(tree, tree.nodeData(node).token_and_node[1]), | ||
| 1276 | .@"errdefer", | ||
| 1277 | .@"defer", | ||
| 1278 | .for_range, | ||
| 1279 | .switch_range, | ||
| 1280 | .switch_case_one, | ||
| 1281 | .switch_case_inline_one, | ||
| 1282 | .switch_case, | ||
| 1283 | .switch_case_inline, | ||
| 1284 | .asm_output, | ||
| 1285 | .asm_input, | ||
| 1286 | .fn_decl, | ||
| 1287 | .container_field, | ||
| 1288 | .container_field_init, | ||
| 1289 | .container_field_align, | ||
| 1290 | .root, | ||
| 1291 | .global_var_decl, | ||
| 1292 | .local_var_decl, | ||
| 1293 | .simple_var_decl, | ||
| 1294 | .aligned_var_decl, | ||
| 1295 | .test_decl, | ||
| 1296 | => unreachable, | ||
| 1297 | } | 972 | } |
| 973 | n += pattern.len * splat; | ||
| 974 | |||
| 975 | w.end = 0; | ||
| 976 | return n; | ||
| 1298 | } | 977 | } |
| 1299 | 978 | ||
| 1300 | fn isOneLineArrayType( | 979 | fn rendersMultiline(r: *const Render, node: Ast.Node.Index) error{OutOfMemory}!bool { |
| 1301 | tree: Ast, | 980 | var no_nl_buf: [64]u8 = undefined; |
| 1302 | array_type: Ast.full.ArrayType, | 981 | var no_nl_w: Writer = .{ |
| 1303 | rbracket: Ast.TokenIndex, | 982 | .vtable = &.{ .drain = drainNoNewline }, |
| 1304 | ) bool { | 983 | .buffer = &no_nl_buf, |
| 1305 | return tree.tokensOnSameLine(array_type.ast.lbracket, rbracket) and | 984 | }; |
| 1306 | !becomesMultilineExpr(tree, array_type.ast.elem_count) and | 985 | |
| 1307 | !optBecomesMultilineExpr(tree, array_type.ast.sentinel); | 986 | if (r.ais.disabled_offset != null) return true; |
| 987 | var sub_ais: AutoIndentingStream = .init(r.gpa, &no_nl_w, r.ais.indent_delta); | ||
| 988 | defer sub_ais.deinit(); | ||
| 989 | // The following are needed to make sure isLineOverIndented is correct | ||
| 990 | sub_ais.indent_count = r.ais.indent_count; | ||
| 991 | sub_ais.applied_indent = r.ais.applied_indent; | ||
| 992 | sub_ais.current_line_empty = r.ais.current_line_empty; | ||
| 993 | |||
| 994 | var sub_r: Render = .{ | ||
| 995 | .gpa = r.gpa, | ||
| 996 | .ais = &sub_ais, | ||
| 997 | .tree = r.tree, | ||
| 998 | .fixups = r.fixups, | ||
| 999 | }; | ||
| 1000 | |||
| 1001 | renderExpression(&sub_r, node, .none) catch |e| return switch (e) { | ||
| 1002 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1003 | error.WriteFailed => return true, | ||
| 1004 | }; | ||
| 1005 | if (sub_ais.disabled_offset != null) return true; | ||
| 1006 | if (std.mem.indexOfScalar(u8, no_nl_w.buffered(), '\n') != null) { | ||
| 1007 | return true; | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | return false; | ||
| 1308 | } | 1011 | } |
| 1309 | 1012 | ||
| 1310 | fn renderArrayType( | 1013 | fn renderArrayType( |
| ... | @@ -1315,7 +1018,9 @@ fn renderArrayType( | ... | @@ -1315,7 +1018,9 @@ fn renderArrayType( |
| 1315 | const tree = r.tree; | 1018 | const tree = r.tree; |
| 1316 | const ais = r.ais; | 1019 | const ais = r.ais; |
| 1317 | const rbracket = tree.firstToken(array_type.ast.elem_type) - 1; | 1020 | const rbracket = tree.firstToken(array_type.ast.elem_type) - 1; |
| 1318 | const one_line = isOneLineArrayType(tree, array_type, rbracket); | 1021 | const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket) and |
| 1022 | !try rendersMultiline(r, array_type.ast.elem_count) and | ||
| 1023 | (if (array_type.ast.sentinel.unwrap()) |s| !try rendersMultiline(r, s) else true); | ||
| 1319 | const inner_space = if (one_line) Space.none else Space.newline; | 1024 | const inner_space = if (one_line) Space.none else Space.newline; |
| 1320 | try ais.pushIndent(.normal); | 1025 | try ais.pushIndent(.normal); |
| 1321 | try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket | 1026 | try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket |
| ... | @@ -2094,7 +1799,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi | ... | @@ -2094,7 +1799,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi |
| 2094 | break; | 1799 | break; |
| 2095 | }, | 1800 | }, |
| 2096 | .keyword_noalias, .keyword_comptime => { | 1801 | .keyword_noalias, .keyword_comptime => { |
| 2097 | try renderToken(r, last_param_token, .space); | 1802 | try renderToken(r, last_param_token, .maybe_space); |
| 2098 | last_param_token += 1; | 1803 | last_param_token += 1; |
| 2099 | }, | 1804 | }, |
| 2100 | .identifier => {}, | 1805 | .identifier => {}, |
| ... | @@ -2145,7 +1850,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi | ... | @@ -2145,7 +1850,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi |
| 2145 | break; | 1850 | break; |
| 2146 | }, | 1851 | }, |
| 2147 | .keyword_noalias, .keyword_comptime => { | 1852 | .keyword_noalias, .keyword_comptime => { |
| 2148 | try renderToken(r, last_param_token, .space); | 1853 | try renderToken(r, last_param_token, .maybe_space); |
| 2149 | last_param_token += 1; | 1854 | last_param_token += 1; |
| 2150 | }, | 1855 | }, |
| 2151 | .identifier => {}, | 1856 | .identifier => {}, |
| ... | @@ -2163,7 +1868,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi | ... | @@ -2163,7 +1868,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi |
| 2163 | { | 1868 | { |
| 2164 | try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name | 1869 | try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name |
| 2165 | last_param_token += 1; | 1870 | last_param_token += 1; |
| 2166 | try renderToken(r, last_param_token, .space); // : | 1871 | try renderToken(r, last_param_token, .maybe_space); // : |
| 2167 | last_param_token += 1; | 1872 | last_param_token += 1; |
| 2168 | } | 1873 | } |
| 2169 | if (tree.tokenTag(last_param_token) == .keyword_anytype) { | 1874 | if (tree.tokenTag(last_param_token) == .keyword_anytype) { |
| ... | @@ -2504,6 +2209,10 @@ fn renderArrayInit( | ... | @@ -2504,6 +2209,10 @@ fn renderArrayInit( |
| 2504 | try renderSpace(&sub_r, after_expr, tokenSliceForRender(tree, after_expr).len, .none); | 2209 | try renderSpace(&sub_r, after_expr, tokenSliceForRender(tree, after_expr).len, .none); |
| 2505 | 2210 | ||
| 2506 | buf.clearRetainingCapacity(); | 2211 | buf.clearRetainingCapacity(); |
| 2212 | // The following are needed to make sure isLineOverIndented is not influenced by | ||
| 2213 | // the previous element. | ||
| 2214 | sub_ais.indent_count = 0; | ||
| 2215 | sub_ais.applied_indent = 0; | ||
| 2507 | } | 2216 | } |
| 2508 | } | 2217 | } |
| 2509 | 2218 | ||
| ... | @@ -2749,8 +2458,47 @@ fn renderAsm( | ... | @@ -2749,8 +2458,47 @@ fn renderAsm( |
| 2749 | try renderToken(r, asm_node.ast.asm_token + 1, .none); // lparen | 2458 | try renderToken(r, asm_node.ast.asm_token + 1, .none); // lparen |
| 2750 | } | 2459 | } |
| 2751 | 2460 | ||
| 2461 | const render_colons: [3]?Ast.TokenIndex = colons: { | ||
| 2462 | var colons: [3]Ast.TokenIndex = undefined; | ||
| 2463 | var render: u2 = 0; | ||
| 2464 | |||
| 2465 | const rparen = asm_node.ast.rparen; | ||
| 2466 | filled: { | ||
| 2467 | colons[0] = tree.lastToken(asm_node.ast.template) + 1; | ||
| 2468 | if (colons[0] == rparen) break :filled; | ||
| 2469 | |||
| 2470 | if (asm_node.outputs.len != 0) { | ||
| 2471 | colons[1] = tree.lastToken(asm_node.outputs[asm_node.outputs.len - 1]) + 1; | ||
| 2472 | colons[1] += @intFromBool(tree.tokenTag(colons[1]) == .comma); | ||
| 2473 | render = 1; | ||
| 2474 | } else { | ||
| 2475 | colons[1] = colons[0] + 1; | ||
| 2476 | if (hasComment(tree, colons[0], colons[1])) render = 1; | ||
| 2477 | } | ||
| 2478 | if (colons[1] == rparen) break :filled; | ||
| 2479 | |||
| 2480 | // Next colon is not checked for here since it cannot present without clobbers | ||
| 2481 | if (asm_node.inputs.len != 0) { | ||
| 2482 | render = 2; | ||
| 2483 | } else { | ||
| 2484 | const colon_or_rparen = colons[1] + 1; | ||
| 2485 | if (hasComment(tree, colons[1], colon_or_rparen)) render = 2; | ||
| 2486 | } | ||
| 2487 | |||
| 2488 | if (asm_node.ast.clobbers.unwrap()) |clobbers| { | ||
| 2489 | colons[2] = tree.firstToken(clobbers) - 1; | ||
| 2490 | render = 3; | ||
| 2491 | } | ||
| 2492 | } | ||
| 2493 | |||
| 2494 | var opt_colons: [3]?Ast.TokenIndex = @splat(null); | ||
| 2495 | for (0..render) |i| opt_colons[i] = colons[i]; | ||
| 2496 | break :colons opt_colons; | ||
| 2497 | }; | ||
| 2498 | |||
| 2499 | try ais.forcePushIndent(.normal); | ||
| 2500 | |||
| 2752 | if (asm_node.ast.items.len == 0) { | 2501 | if (asm_node.ast.items.len == 0) { |
| 2753 | try ais.forcePushIndent(.normal); | ||
| 2754 | if (asm_node.ast.clobbers.unwrap()) |clobbers| { | 2502 | if (asm_node.ast.clobbers.unwrap()) |clobbers| { |
| 2755 | // asm ("foo" ::: clobbers) | 2503 | // asm ("foo" ::: clobbers) |
| 2756 | try renderExpression(r, asm_node.ast.template, .space); | 2504 | try renderExpression(r, asm_node.ast.template, .space); |
| ... | @@ -2764,99 +2512,78 @@ fn renderAsm( | ... | @@ -2764,99 +2512,78 @@ fn renderAsm( |
| 2764 | return renderToken(r, asm_node.ast.rparen, space); // rparen | 2512 | return renderToken(r, asm_node.ast.rparen, space); // rparen |
| 2765 | } | 2513 | } |
| 2766 | 2514 | ||
| 2767 | // asm ("foo") | 2515 | if (render_colons[0] == null) { |
| 2768 | try renderExpression(r, asm_node.ast.template, .none); | 2516 | // asm ("foo") |
| 2769 | ais.popIndent(); | 2517 | try renderExpression(r, asm_node.ast.template, .none); |
| 2770 | return renderToken(r, asm_node.ast.rparen, space); // rparen | 2518 | ais.popIndent(); |
| 2519 | return renderToken(r, asm_node.ast.rparen, space); // rparen | ||
| 2520 | } | ||
| 2771 | } | 2521 | } |
| 2772 | 2522 | ||
| 2773 | try ais.forcePushIndent(.normal); | ||
| 2774 | try renderExpression(r, asm_node.ast.template, .newline); | 2523 | try renderExpression(r, asm_node.ast.template, .newline); |
| 2775 | ais.forceLastIndent(); // Might have been dedented by a multiline string literal | 2524 | ais.forceLastIndent(); // Might have been dedented by a multiline string literal |
| 2776 | assert(ais.current_line_empty); | 2525 | assert(ais.current_line_empty); |
| 2777 | 2526 | ||
| 2527 | const prev_indent_delta = ais.indent_delta; // May be part of another asm expression | ||
| 2528 | // so indent_delta can't be unconditionally used | ||
| 2778 | ais.setIndentDelta(asm_indent_delta); | 2529 | ais.setIndentDelta(asm_indent_delta); |
| 2779 | const colon1 = tree.lastToken(asm_node.ast.template) + 1; | ||
| 2780 | 2530 | ||
| 2781 | const colon2 = if (asm_node.outputs.len == 0) colon2: { | 2531 | rendered: { |
| 2782 | try renderToken(r, colon1, .newline); // : | 2532 | if (render_colons[0]) |colon1| { |
| 2783 | break :colon2 colon1 + 1; | 2533 | if (asm_node.outputs.len != 0) { |
| 2784 | } else colon2: { | 2534 | try renderToken(r, colon1, .space); |
| 2785 | try renderToken(r, colon1, .space); // : | 2535 | try ais.forcePushIndent(.normal); |
| 2786 | 2536 | ||
| 2787 | try ais.forcePushIndent(.normal); | 2537 | const final = asm_node.outputs.len - 1; |
| 2788 | for (asm_node.outputs, 0..) |asm_output, i| { | 2538 | for (asm_node.outputs[0..final], 0..) |asm_output, i| { |
| 2789 | if (i + 1 < asm_node.outputs.len) { | 2539 | try renderAsmOutput(r, asm_output, .none); |
| 2790 | const next_asm_output = asm_node.outputs[i + 1]; | 2540 | |
| 2791 | try renderAsmOutput(r, asm_output, .none); | 2541 | const next_start = tree.firstToken(asm_node.outputs[i + 1]); |
| 2542 | try renderToken(r, next_start - 1, .newline); // , | ||
| 2543 | try renderExtraNewlineToken(r, next_start); | ||
| 2544 | } | ||
| 2792 | 2545 | ||
| 2793 | const comma = tree.firstToken(next_asm_output) - 1; | ||
| 2794 | try renderToken(r, comma, .newline); // , | ||
| 2795 | try renderExtraNewlineToken(r, tree.firstToken(next_asm_output)); | ||
| 2796 | } else if (asm_node.inputs.len == 0 and asm_node.ast.clobbers == .none) { | ||
| 2797 | try ais.pushSpace(.comma); | 2546 | try ais.pushSpace(.comma); |
| 2798 | try renderAsmOutput(r, asm_output, .comma); | 2547 | try renderAsmOutput(r, asm_node.outputs[final], .comma); |
| 2799 | ais.popSpace(); | 2548 | ais.popSpace(); |
| 2800 | ais.popIndent(); | 2549 | ais.popIndent(); |
| 2801 | ais.setIndentDelta(indent_delta); | ||
| 2802 | ais.popIndent(); | ||
| 2803 | return renderToken(r, asm_node.ast.rparen, space); // rparen | ||
| 2804 | } else { | 2550 | } else { |
| 2805 | try ais.pushSpace(.comma); | 2551 | try renderToken(r, colon1, .newline); |
| 2806 | try renderAsmOutput(r, asm_output, .comma); | ||
| 2807 | ais.popSpace(); | ||
| 2808 | const comma_or_colon = tree.lastToken(asm_output) + 1; | ||
| 2809 | ais.popIndent(); | ||
| 2810 | break :colon2 switch (tree.tokenTag(comma_or_colon)) { | ||
| 2811 | .comma => comma_or_colon + 1, | ||
| 2812 | else => comma_or_colon, | ||
| 2813 | }; | ||
| 2814 | } | 2552 | } |
| 2815 | } else unreachable; | 2553 | } else unreachable; |
| 2816 | }; | ||
| 2817 | 2554 | ||
| 2818 | const colon3 = if (asm_node.inputs.len == 0) colon3: { | 2555 | if (render_colons[1]) |colon2| { |
| 2819 | try renderToken(r, colon2, .newline); // : | 2556 | if (asm_node.inputs.len != 0) { |
| 2820 | break :colon3 colon2 + 1; | 2557 | try renderToken(r, colon2, .space); |
| 2821 | } else colon3: { | 2558 | try ais.forcePushIndent(.normal); |
| 2822 | try renderToken(r, colon2, .space); // : | 2559 | |
| 2823 | try ais.forcePushIndent(.normal); | 2560 | const final = asm_node.inputs.len - 1; |
| 2824 | for (asm_node.inputs, 0..) |asm_input, i| { | 2561 | for (asm_node.inputs[0..final], 0..) |asm_input, i| { |
| 2825 | if (i + 1 < asm_node.inputs.len) { | 2562 | try renderAsmInput(r, asm_input, .none); |
| 2826 | const next_asm_input = asm_node.inputs[i + 1]; | 2563 | |
| 2827 | try renderAsmInput(r, asm_input, .none); | 2564 | const next_start = tree.firstToken(asm_node.inputs[i + 1]); |
| 2828 | 2565 | try renderToken(r, next_start - 1, .newline); // , | |
| 2829 | const first_token = tree.firstToken(next_asm_input); | 2566 | try renderExtraNewlineToken(r, next_start); |
| 2830 | try renderToken(r, first_token - 1, .newline); // , | 2567 | } |
| 2831 | try renderExtraNewlineToken(r, first_token); | 2568 | |
| 2832 | } else if (asm_node.ast.clobbers == .none) { | ||
| 2833 | try ais.pushSpace(.comma); | 2569 | try ais.pushSpace(.comma); |
| 2834 | try renderAsmInput(r, asm_input, .comma); | 2570 | try renderAsmInput(r, asm_node.inputs[final], .comma); |
| 2835 | ais.popSpace(); | 2571 | ais.popSpace(); |
| 2836 | ais.popIndent(); | 2572 | ais.popIndent(); |
| 2837 | ais.setIndentDelta(indent_delta); | ||
| 2838 | ais.popIndent(); | ||
| 2839 | return renderToken(r, asm_node.ast.rparen, space); // rparen | ||
| 2840 | } else { | 2573 | } else { |
| 2841 | try ais.pushSpace(.comma); | 2574 | try renderToken(r, colon2, .newline); |
| 2842 | try renderAsmInput(r, asm_input, .comma); | ||
| 2843 | ais.popSpace(); | ||
| 2844 | const comma_or_colon = tree.lastToken(asm_input) + 1; | ||
| 2845 | ais.popIndent(); | ||
| 2846 | break :colon3 switch (tree.tokenTag(comma_or_colon)) { | ||
| 2847 | .comma => comma_or_colon + 1, | ||
| 2848 | else => comma_or_colon, | ||
| 2849 | }; | ||
| 2850 | } | 2575 | } |
| 2576 | } else break :rendered; | ||
| 2577 | |||
| 2578 | if (render_colons[2]) |colon3| { | ||
| 2579 | const clobbers = asm_node.ast.clobbers.unwrap().?; | ||
| 2580 | try renderToken(r, colon3, .maybe_space); | ||
| 2581 | try renderExpression(r, clobbers, .none); | ||
| 2582 | ais.forceLastIndent(); // Might have been dedented by a multiline string literal | ||
| 2851 | } | 2583 | } |
| 2852 | unreachable; | 2584 | } |
| 2853 | }; | ||
| 2854 | 2585 | ||
| 2855 | try renderToken(r, colon3, .maybe_space); // : | 2586 | ais.setIndentDelta(prev_indent_delta); |
| 2856 | const clobbers = asm_node.ast.clobbers.unwrap().?; | ||
| 2857 | try renderExpression(r, clobbers, .none); | ||
| 2858 | ais.forceLastIndent(); // Might have been dedented by a multiline string literal | ||
| 2859 | ais.setIndentDelta(indent_delta); | ||
| 2860 | ais.popIndent(); | 2587 | ais.popIndent(); |
| 2861 | return renderToken(r, asm_node.ast.rparen, space); // rparen | 2588 | return renderToken(r, asm_node.ast.rparen, space); // rparen |
| 2862 | } | 2589 | } |
| ... | @@ -3324,6 +3051,7 @@ fn renderComments(r: *Render, start: usize, end: usize) Error!bool { | ... | @@ -3324,6 +3051,7 @@ fn renderComments(r: *Render, start: usize, end: usize) Error!bool { |
| 3324 | // Write with the canonical single space. | 3051 | // Write with the canonical single space. |
| 3325 | try ais.underlying_writer.writeAll("// zig fmt: on\n"); | 3052 | try ais.underlying_writer.writeAll("// zig fmt: on\n"); |
| 3326 | ais.disabled_offset = null; | 3053 | ais.disabled_offset = null; |
| 3054 | ais.resetLine(); | ||
| 3327 | } else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) { | 3055 | } else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) { |
| 3328 | // Write with the canonical single space. | 3056 | // Write with the canonical single space. |
| 3329 | try ais.writeAll("// zig fmt: off\n"); | 3057 | try ais.writeAll("// zig fmt: off\n"); |
lib/std/zig/AstSmith.zig created+2602| ... | @@ -0,0 +1,2602 @@ | ||
| 1 | //! Generates a valid AST and corresponding source. | ||
| 2 | //! | ||
| 3 | //! This is based directly off grammer.peg | ||
| 4 | |||
| 5 | const std = @import("../std.zig"); | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | const Token = std.zig.Token; | ||
| 8 | const Smith = std.testing.Smith; | ||
| 9 | const Weight = Smith.Weight; | ||
| 10 | const AstSmith = @This(); | ||
| 11 | |||
| 12 | smith: *Smith, | ||
| 13 | |||
| 14 | source_buf: [16384]u8, | ||
| 15 | source_len: usize, | ||
| 16 | |||
| 17 | token_tag_buf: [2048]Token.Tag, | ||
| 18 | token_start_buf: [2048]std.zig.Ast.ByteOffset, | ||
| 19 | tokens_len: usize, | ||
| 20 | |||
| 21 | /// For `.asterisk`, this also includes `.asterisk2` | ||
| 22 | not_token: ?Token.Tag, | ||
| 23 | not_token_comptime: bool, | ||
| 24 | /// ExprSuffix | ||
| 25 | /// <- KEYWORD_or | ||
| 26 | /// / KEYWORD_and | ||
| 27 | /// / CompareOp | ||
| 28 | /// / BitwiseOp | ||
| 29 | /// / BitShiftOp | ||
| 30 | /// / AdditionOp | ||
| 31 | /// / MultiplyOp | ||
| 32 | /// / EXCLAMATIONMARK | ||
| 33 | /// / SuffixOp | ||
| 34 | /// / FnCallArguments | ||
| 35 | not_expr_suffix: bool, | ||
| 36 | /// LabelableExpr | ||
| 37 | /// <- Block | ||
| 38 | /// / SwitchExpr | ||
| 39 | /// / LoopExpr | ||
| 40 | not_labelable_expr: ?enum { colon, expr }, | ||
| 41 | not_label: bool, | ||
| 42 | not_break_label: bool, | ||
| 43 | not_block_expr: bool, | ||
| 44 | not_expr_statement: bool, | ||
| 45 | |||
| 46 | prev_ids_buf: [256]struct { start: u16, len: u16 }, | ||
| 47 | /// This may be larger than `prev_ids` in which case, | ||
| 48 | /// x % prev_ids.len = next index | ||
| 49 | /// @min(x, prev_ids) = length | ||
| 50 | prev_ids_len: usize, | ||
| 51 | |||
| 52 | /// `generate` must be called on the returned value before any other methods | ||
| 53 | pub fn init(smith: *Smith) AstSmith { | ||
| 54 | return .{ | ||
| 55 | .smith = smith, | ||
| 56 | |||
| 57 | .source_buf = undefined, | ||
| 58 | .source_len = 0, | ||
| 59 | |||
| 60 | .token_tag_buf = undefined, | ||
| 61 | .token_start_buf = undefined, | ||
| 62 | .tokens_len = 0, | ||
| 63 | |||
| 64 | .not_token = null, | ||
| 65 | .not_token_comptime = false, | ||
| 66 | .not_expr_suffix = false, | ||
| 67 | .not_labelable_expr = null, | ||
| 68 | .not_label = false, | ||
| 69 | .not_break_label = false, | ||
| 70 | .not_block_expr = false, | ||
| 71 | .not_expr_statement = false, | ||
| 72 | |||
| 73 | .prev_ids_buf = undefined, | ||
| 74 | .prev_ids_len = 0, | ||
| 75 | }; | ||
| 76 | } | ||
| 77 | |||
| 78 | pub fn source(t: *AstSmith) [:0]u8 { | ||
| 79 | return t.source_buf[0..t.source_len :0]; | ||
| 80 | } | ||
| 81 | |||
| 82 | /// The Slice is not backed by a MultiArrayList, so calling deinit or toMultiArrayList is illegal. | ||
| 83 | pub fn tokens(t: *AstSmith) std.zig.Ast.TokenList.Slice { | ||
| 84 | var slice: std.zig.Ast.TokenList.Slice = .{ | ||
| 85 | .ptrs = undefined, | ||
| 86 | .len = t.tokens_len, | ||
| 87 | .capacity = t.tokens_len, | ||
| 88 | }; | ||
| 89 | comptime assert(slice.ptrs.len == 2); | ||
| 90 | slice.ptrs[@intFromEnum(std.zig.Ast.TokenList.Field.tag)] = @ptrCast(&t.token_tag_buf); | ||
| 91 | slice.ptrs[@intFromEnum(std.zig.Ast.TokenList.Field.start)] = @ptrCast(&t.token_start_buf); | ||
| 92 | return slice; | ||
| 93 | } | ||
| 94 | |||
| 95 | pub const Error = error{ OutOfMemory, SkipZigTest }; | ||
| 96 | const SourceError = error{SkipZigTest}; | ||
| 97 | |||
| 98 | pub fn generate(a: *AstSmith, gpa: std.mem.Allocator) Error!std.zig.Ast { | ||
| 99 | try a.generateSource(); | ||
| 100 | const ast = try std.zig.Ast.parseTokens(gpa, a.source(), a.tokens(), .zig); | ||
| 101 | assert(ast.errors.len == 0); | ||
| 102 | return ast; | ||
| 103 | } | ||
| 104 | |||
| 105 | pub fn generateSource(a: *AstSmith) SourceError!void { | ||
| 106 | try a.pegRoot(); | ||
| 107 | try a.ensureSourceCapacity(1); | ||
| 108 | a.source_buf[a.source_len] = 0; | ||
| 109 | try a.addTokenTag(.eof); | ||
| 110 | } | ||
| 111 | |||
| 112 | /// For choices which can introduce a variable number of expressions, this should be used to reduce | ||
| 113 | /// unbounded recursion. | ||
| 114 | // | ||
| 115 | // `inline` to propogate caller's return address | ||
| 116 | inline fn smithListItemBool(a: *AstSmith) bool { | ||
| 117 | return a.smith.boolWeighted(63, 1); | ||
| 118 | } | ||
| 119 | |||
| 120 | /// For choices which can introduce a variable number of expressions, this should be used to reduce | ||
| 121 | /// unbounded recursion. | ||
| 122 | // | ||
| 123 | // `inline` to propogate caller's return address | ||
| 124 | inline fn smithListItemEos(a: *AstSmith) bool { | ||
| 125 | return a.smith.eosWeightedSimple(1, 63); | ||
| 126 | } | ||
| 127 | |||
| 128 | fn sourceCapacity(a: *AstSmith) []u8 { | ||
| 129 | return a.source_buf[a.source_len..]; | ||
| 130 | } | ||
| 131 | |||
| 132 | fn sourceCapacityLen(a: *AstSmith) usize { | ||
| 133 | return a.source_buf.len - a.source_len; | ||
| 134 | } | ||
| 135 | |||
| 136 | fn ensureSourceCapacity(a: *AstSmith, n: usize) SourceError!void { | ||
| 137 | if (a.sourceCapacityLen() < n) return error.SkipZigTest; | ||
| 138 | } | ||
| 139 | |||
| 140 | fn addSourceByte(a: *AstSmith, byte: u8) SourceError!void { | ||
| 141 | try a.ensureSourceCapacity(1); | ||
| 142 | a.addSourceByteAssumeCapacity(byte); | ||
| 143 | } | ||
| 144 | |||
| 145 | fn addSourceByteAssumeCapacity(a: *AstSmith, byte: u8) void { | ||
| 146 | a.sourceCapacity()[0] = byte; | ||
| 147 | a.source_len += 1; | ||
| 148 | } | ||
| 149 | |||
| 150 | fn addSource(a: *AstSmith, bytes: []const u8) SourceError!void { | ||
| 151 | try a.ensureSourceCapacity(bytes.len); | ||
| 152 | a.addSourceAssumeCapacity(bytes); | ||
| 153 | } | ||
| 154 | |||
| 155 | fn addSourceAssumeCapacity(a: *AstSmith, bytes: []const u8) void { | ||
| 156 | @memcpy(a.sourceCapacity()[0..bytes.len], bytes); | ||
| 157 | a.source_len += bytes.len; | ||
| 158 | } | ||
| 159 | |||
| 160 | fn addSourceAsSlice(a: *AstSmith, len: usize) SourceError![]u8 { | ||
| 161 | try a.ensureSourceCapacity(len); | ||
| 162 | return a.addSourceAsSliceAssumeCapacity(len); | ||
| 163 | } | ||
| 164 | |||
| 165 | fn addSourceAsSliceAssumeCapacity(a: *AstSmith, len: usize) []u8 { | ||
| 166 | const slice = a.sourceCapacity()[0..len]; | ||
| 167 | a.source_len += len; | ||
| 168 | return slice; | ||
| 169 | } | ||
| 170 | |||
| 171 | fn tokenCapacityLen(a: *AstSmith) usize { | ||
| 172 | return a.token_tag_buf.len - a.tokens_len; | ||
| 173 | } | ||
| 174 | |||
| 175 | fn ensureTokenCapacity(a: *AstSmith, n: usize) SourceError!void { | ||
| 176 | if (a.tokenCapacityLen() < n) return error.SkipZigTest; | ||
| 177 | } | ||
| 178 | |||
| 179 | fn isAlphanumeric(c: u8) bool { | ||
| 180 | return switch (c) { | ||
| 181 | '_', 'a'...'z', 'A'...'Z', '0'...'9' => true, | ||
| 182 | else => false, | ||
| 183 | }; | ||
| 184 | } | ||
| 185 | |||
| 186 | /// For tokens starting with alphanumerics, this ensures | ||
| 187 | /// previous tokens followed by end_of_word aren't altered. | ||
| 188 | /// | ||
| 189 | /// end_of_word <- ![a-zA-Z0-9_] skip | ||
| 190 | fn preservePegEndOfWord(a: *AstSmith) SourceError!void { | ||
| 191 | if (a.source_len > 0 and isAlphanumeric(a.source_buf[a.source_len - 1])) { | ||
| 192 | try a.addSourceByte(' '); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | /// Assumes the token has not been written yet | ||
| 197 | fn addTokenTag(a: *AstSmith, tag: Token.Tag) SourceError!void { | ||
| 198 | assert(tag != a.not_token); | ||
| 199 | if (a.not_token == .asterisk) assert(tag != .asterisk_asterisk); | ||
| 200 | a.not_token = null; | ||
| 201 | |||
| 202 | if (a.not_token_comptime) assert(tag != .keyword_comptime); | ||
| 203 | a.not_token_comptime = false; | ||
| 204 | |||
| 205 | if (a.not_label and tag == .identifier) { | ||
| 206 | a.not_token = .colon; | ||
| 207 | } | ||
| 208 | a.not_label = false; | ||
| 209 | |||
| 210 | if (a.not_break_label and tag == .colon) { | ||
| 211 | a.not_token = .identifier; | ||
| 212 | } | ||
| 213 | a.not_break_label = false; | ||
| 214 | |||
| 215 | if (a.not_labelable_expr) |part| switch (part) { | ||
| 216 | .colon => a.not_labelable_expr = if (tag == .colon) .expr else null, | ||
| 217 | .expr => switch (tag) { | ||
| 218 | .l_brace => unreachable, | ||
| 219 | .keyword_inline => {}, | ||
| 220 | .keyword_for => unreachable, | ||
| 221 | .keyword_while => unreachable, | ||
| 222 | .keyword_switch => unreachable, | ||
| 223 | else => a.not_labelable_expr = null, | ||
| 224 | }, | ||
| 225 | }; | ||
| 226 | |||
| 227 | a.not_expr_suffix = false; | ||
| 228 | a.not_block_expr = false; | ||
| 229 | a.not_expr_statement = false; | ||
| 230 | |||
| 231 | try a.ensureTokenCapacity(1); | ||
| 232 | a.token_tag_buf[a.tokens_len] = tag; | ||
| 233 | a.token_start_buf[a.tokens_len] = @intCast(a.source_len); | ||
| 234 | a.tokens_len += 1; | ||
| 235 | } | ||
| 236 | |||
| 237 | /// Asserts the token has a lexeme (those without have corresponding methods) | ||
| 238 | fn pegToken(a: *AstSmith, tag: Token.Tag) SourceError!void { | ||
| 239 | const lexeme = tag.lexeme().?; | ||
| 240 | |||
| 241 | switch (lexeme[0]) { | ||
| 242 | '_', 'a'...'z', 'A'...'Z', '0'...'9' => try a.preservePegEndOfWord(), | ||
| 243 | '*' => if (a.tokens_len > 0 and a.source_buf[a.source_len - 1] == '*' and | ||
| 244 | a.token_tag_buf[a.tokens_len - 1] != .asterisk_asterisk) | ||
| 245 | { | ||
| 246 | try a.addSourceByte(' '); | ||
| 247 | }, | ||
| 248 | '.' => if (a.tokens_len > 0 and switch (a.source_buf[a.source_len - 1]) { | ||
| 249 | '.' => true, | ||
| 250 | '0'...'9', 'a'...'z', 'A'...'Z' => a.token_tag_buf[a.tokens_len - 1] == .number_literal, | ||
| 251 | else => false, | ||
| 252 | }) { | ||
| 253 | try a.addSourceByte(' '); | ||
| 254 | }, | ||
| 255 | '+', '-' => if (a.tokens_len > 0 and a.token_tag_buf[a.tokens_len - 1] == .number_literal and | ||
| 256 | switch (a.source_buf[a.source_len - 1]) { | ||
| 257 | 'e', 'E', 'p', 'P' => true, | ||
| 258 | else => false, | ||
| 259 | }) | ||
| 260 | { | ||
| 261 | // Would otherwise be tokenized as the sign of a float's exponent | ||
| 262 | // | ||
| 263 | // e.g. "0xFE" ++ "+" ++ "2" (number_literal, plus, number_literal) | ||
| 264 | try a.addSourceByte(' '); | ||
| 265 | }, | ||
| 266 | else => {}, | ||
| 267 | } | ||
| 268 | |||
| 269 | if (isAlphanumeric(lexeme[0])) try a.preservePegEndOfWord(); | ||
| 270 | |||
| 271 | try a.addTokenTag(tag); | ||
| 272 | try a.addSource(lexeme); | ||
| 273 | try a.pegSkip(); | ||
| 274 | } | ||
| 275 | |||
| 276 | /// Asserts `a.source_len != 0` | ||
| 277 | fn pegTokenWhitespaceAround(a: *AstSmith, tag: Token.Tag) SourceError!void { | ||
| 278 | switch (a.source_buf[a.source_len - 1]) { | ||
| 279 | ' ', '\n' => {}, | ||
| 280 | else => try a.addSourceByte(' '), | ||
| 281 | } | ||
| 282 | try a.addTokenTag(tag); | ||
| 283 | try a.addSource(tag.lexeme().?); | ||
| 284 | switch (a.smith.value(enum { space, line_break, cr_line_break })) { | ||
| 285 | // This is not the same as 'skip' since comments are not whitespace | ||
| 286 | .space => try a.addSourceByte(' '), | ||
| 287 | .line_break => try a.addSourceByte('\n'), | ||
| 288 | .cr_line_break => try a.addSource("\r\n"), | ||
| 289 | } | ||
| 290 | try a.pegSkip(); | ||
| 291 | } | ||
| 292 | |||
| 293 | /// Root <- skip ContainerMembers eof | ||
| 294 | fn pegRoot(a: *AstSmith) SourceError!void { | ||
| 295 | try a.pegSkip(); | ||
| 296 | try a.pegContainerMembers(); | ||
| 297 | } | ||
| 298 | |||
| 299 | /// ContainerMembers <- container_doc_comment? ContainerDeclaration* (ContainerField COMMA)* | ||
| 300 | /// (ContainerField / ContainerDeclaration*) | ||
| 301 | fn pegContainerMembers(a: *AstSmith) SourceError!void { | ||
| 302 | if (a.smith.boolWeighted(63, 1)) { | ||
| 303 | try a.pegContainerDocComment(); | ||
| 304 | } | ||
| 305 | while (!a.smithListItemEos()) { | ||
| 306 | try a.pegContainerDeclaration(); | ||
| 307 | } | ||
| 308 | while (!a.smithListItemEos()) { | ||
| 309 | try a.pegContainerField(); | ||
| 310 | try a.pegToken(.comma); | ||
| 311 | } | ||
| 312 | if (a.smithListItemBool()) { | ||
| 313 | if (a.smith.value(bool)) { | ||
| 314 | try a.pegContainerField(); | ||
| 315 | } else while (true) { | ||
| 316 | try a.pegContainerDeclaration(); | ||
| 317 | if (a.smithListItemEos()) break; | ||
| 318 | } | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | /// ContainerDeclaration <- TestDecl / ComptimeDecl / doc_comment? KEYWORD_pub? Decl | ||
| 323 | fn pegContainerDeclaration(a: *AstSmith) SourceError!void { | ||
| 324 | switch (a.smith.value(enum { TestDecl, ComptimeDecl, Decl })) { | ||
| 325 | .TestDecl => try a.pegTestDecl(), | ||
| 326 | .ComptimeDecl => try a.pegComptimeDecl(), | ||
| 327 | .Decl => { | ||
| 328 | try a.pegMaybeDocComment(); | ||
| 329 | if (a.smith.value(bool)) { | ||
| 330 | try a.pegToken(.keyword_pub); | ||
| 331 | } | ||
| 332 | try a.pegDecl(); | ||
| 333 | }, | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | /// KEYWORD_test (STRINGLITERALSINGLE / IDENTIFIER)? Block | ||
| 338 | fn pegTestDecl(a: *AstSmith) SourceError!void { | ||
| 339 | try a.pegToken(.keyword_test); | ||
| 340 | switch (a.smith.value(enum { none, string, id })) { | ||
| 341 | .none => {}, | ||
| 342 | .string => try a.pegStringLiteralSingle(), | ||
| 343 | .id => try a.pegIdentifier(), | ||
| 344 | } | ||
| 345 | try a.pegBlock(); | ||
| 346 | } | ||
| 347 | |||
| 348 | /// ComptimeDecl <- KEYWORD_comptime Block | ||
| 349 | fn pegComptimeDecl(a: *AstSmith) SourceError!void { | ||
| 350 | try a.pegToken(.keyword_comptime); | ||
| 351 | try a.pegBlock(); | ||
| 352 | } | ||
| 353 | |||
| 354 | /// Decl | ||
| 355 | /// <- (KEYWORD_export / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block) | ||
| 356 | /// / KEYWORD_extern STRINGLITERALSINGLE? FnProto SEMICOLON | ||
| 357 | /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? | ||
| 358 | /// GlobalVarDecl | ||
| 359 | fn pegDecl(a: *AstSmith) SourceError!void { | ||
| 360 | const Modifier = enum(u8) { | ||
| 361 | none, | ||
| 362 | @"export", | ||
| 363 | @"extern", | ||
| 364 | extern_library, | ||
| 365 | @"inline", | ||
| 366 | @"noinline", | ||
| 367 | }; | ||
| 368 | const is_fn = a.smith.value(bool); | ||
| 369 | const fn_modifiers = Smith.baselineWeights(Modifier); | ||
| 370 | const var_modifiers: []const Weight = &.{.rangeAtMost(Modifier, .none, .extern_library, 1)}; | ||
| 371 | const modifier = a.smith.valueWeighted(Modifier, if (is_fn) fn_modifiers else var_modifiers); | ||
| 372 | |||
| 373 | switch (modifier) { | ||
| 374 | .none => {}, | ||
| 375 | .@"export" => try a.pegToken(.keyword_export), | ||
| 376 | .@"extern" => try a.pegToken(.keyword_extern), | ||
| 377 | .extern_library => { | ||
| 378 | try a.pegToken(.keyword_extern); | ||
| 379 | try a.pegStringLiteralSingle(); | ||
| 380 | }, | ||
| 381 | .@"inline" => try a.pegToken(.keyword_inline), | ||
| 382 | .@"noinline" => try a.pegToken(.keyword_noinline), | ||
| 383 | } | ||
| 384 | |||
| 385 | if (is_fn) { | ||
| 386 | try a.pegFnProto(); | ||
| 387 | if (modifier == .@"extern" or modifier == .extern_library or a.smith.value(bool)) { | ||
| 388 | try a.pegToken(.semicolon); | ||
| 389 | } else { | ||
| 390 | try a.pegBlock(); | ||
| 391 | } | ||
| 392 | } else { | ||
| 393 | if (a.smith.value(bool)) try a.pegToken(.keyword_threadlocal); | ||
| 394 | try a.pegGlobalVarDecl(); | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? | ||
| 399 | /// LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr !ExprSuffix | ||
| 400 | fn pegFnProto(a: *AstSmith) SourceError!void { | ||
| 401 | try a.pegToken(.keyword_fn); | ||
| 402 | if (a.smith.value(bool)) { | ||
| 403 | try a.pegIdentifier(); | ||
| 404 | } | ||
| 405 | try a.pegToken(.l_paren); | ||
| 406 | try a.pegParamDeclList(); | ||
| 407 | try a.pegToken(.r_paren); | ||
| 408 | if (a.smith.value(bool)) { | ||
| 409 | try a.pegByteAlign(); | ||
| 410 | } | ||
| 411 | if (a.smith.value(bool)) { | ||
| 412 | try a.pegAddrSpace(); | ||
| 413 | } | ||
| 414 | if (a.smith.value(bool)) { | ||
| 415 | try a.pegLinkSection(); | ||
| 416 | } | ||
| 417 | if (a.smith.value(bool)) { | ||
| 418 | try a.pegCallConv(); | ||
| 419 | } | ||
| 420 | if (a.smith.value(bool)) { | ||
| 421 | try a.pegToken(.bang); | ||
| 422 | } | ||
| 423 | try a.pegTypeExpr(); | ||
| 424 | a.not_expr_suffix = true; | ||
| 425 | } | ||
| 426 | |||
| 427 | /// VarDeclProto <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? | ||
| 428 | /// AddrSpace? LinkSection? | ||
| 429 | fn pegVarDeclProto(a: *AstSmith) SourceError!void { | ||
| 430 | try a.pegToken(if (a.smith.value(bool)) .keyword_var else .keyword_const); | ||
| 431 | try a.pegIdentifier(); | ||
| 432 | |||
| 433 | if (a.smith.value(bool)) { | ||
| 434 | try a.pegToken(.colon); | ||
| 435 | try a.pegTypeExpr(); | ||
| 436 | } | ||
| 437 | |||
| 438 | if (a.smith.value(bool)) { | ||
| 439 | try a.pegByteAlign(); | ||
| 440 | } | ||
| 441 | |||
| 442 | if (a.smith.value(bool)) { | ||
| 443 | try a.pegAddrSpace(); | ||
| 444 | } | ||
| 445 | |||
| 446 | if (a.smith.value(bool)) { | ||
| 447 | try a.pegLinkSection(); | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | /// GlobalVarDecl <- VarDeclProto (EQUAL Expr)? SEMICOLON | ||
| 452 | fn pegGlobalVarDecl(a: *AstSmith) SourceError!void { | ||
| 453 | try a.pegVarDeclProto(); | ||
| 454 | if (a.smithListItemBool()) { | ||
| 455 | try a.pegToken(.equal); | ||
| 456 | try a.pegExpr(); | ||
| 457 | } | ||
| 458 | try a.pegToken(.semicolon); | ||
| 459 | } | ||
| 460 | |||
| 461 | /// ContainerField <- doc_comment? (KEYWORD_comptime / !KEYWORD_comptime) !KEYWORD_fn | ||
| 462 | /// (IDENTIFIER COLON !(IDENTIFIER COLON)) TypeExpr ByteAlign? (EQUAL Expr)? | ||
| 463 | fn pegContainerField(a: *AstSmith) SourceError!void { | ||
| 464 | try a.pegMaybeDocComment(); | ||
| 465 | if (a.smith.value(bool)) { | ||
| 466 | try a.pegToken(.keyword_comptime); | ||
| 467 | } | ||
| 468 | if (a.smith.value(bool)) { | ||
| 469 | try a.pegIdentifier(); | ||
| 470 | try a.pegToken(.colon); | ||
| 471 | } else { | ||
| 472 | a.not_token = .keyword_fn; | ||
| 473 | a.not_token_comptime = true; | ||
| 474 | a.not_label = true; | ||
| 475 | } | ||
| 476 | try a.pegTypeExpr(); | ||
| 477 | if (a.smith.value(bool)) { | ||
| 478 | try a.pegByteAlign(); | ||
| 479 | } | ||
| 480 | if (a.smith.value(bool)) { | ||
| 481 | try a.pegToken(.equal); | ||
| 482 | try a.pegExpr(); | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | /// BlockStatement | ||
| 487 | /// <- Statement | ||
| 488 | /// / KEYWORD_defer BlockExprStatement | ||
| 489 | /// / KEYWORD_errdefer Payload? BlockExprStatement | ||
| 490 | /// / !ExprStatement (KEYWORD_comptime !BlockExpr)? VarAssignStatement | ||
| 491 | fn pegBlockStatement(a: *AstSmith) SourceError!void { | ||
| 492 | const Kind = enum { | ||
| 493 | statement, | ||
| 494 | defer_statement, | ||
| 495 | errdefer_statement, | ||
| 496 | var_assign, | ||
| 497 | comptime_var_assign, | ||
| 498 | }; | ||
| 499 | const weights = Smith.baselineWeights(Kind) ++ &[1]Weight{.value(Kind, .statement, 4)}; | ||
| 500 | switch (a.smith.valueWeighted(Kind, weights)) { | ||
| 501 | .statement => try a.pegStatement(), | ||
| 502 | .defer_statement, .errdefer_statement => |kind| { | ||
| 503 | try a.pegToken(switch (kind) { | ||
| 504 | .defer_statement => .keyword_defer, | ||
| 505 | .errdefer_statement => .keyword_errdefer, | ||
| 506 | else => unreachable, | ||
| 507 | }); | ||
| 508 | try a.pegBlockExprStatement(); | ||
| 509 | }, | ||
| 510 | .var_assign, .comptime_var_assign => |kind| { | ||
| 511 | a.not_expr_statement = true; | ||
| 512 | if (kind == .comptime_var_assign) { | ||
| 513 | try a.pegToken(.keyword_comptime); | ||
| 514 | a.not_block_expr = true; | ||
| 515 | } | ||
| 516 | try a.pegVarAssignStatement(); | ||
| 517 | }, | ||
| 518 | } | ||
| 519 | } | ||
| 520 | |||
| 521 | /// Statement | ||
| 522 | /// <- ExprStatement | ||
| 523 | /// / KEYWORD_suspend BlockExprStatement | ||
| 524 | /// / !ExprStatement (KEYWORD_comptime !BlockExpr)? AssignExpr SEMICOLON | ||
| 525 | /// | ||
| 526 | /// ExprStatement | ||
| 527 | /// <- IfStatement | ||
| 528 | /// / LabeledStatement | ||
| 529 | /// / KEYWORD_nosuspend BlockExprStatement | ||
| 530 | /// / KEYWORD_comptime BlockExpr | ||
| 531 | fn pegStatement(a: *AstSmith) SourceError!void { | ||
| 532 | switch (a.smith.value(enum { | ||
| 533 | if_statement, | ||
| 534 | labeled_statement, | ||
| 535 | comptime_block_expr, | ||
| 536 | |||
| 537 | nosuspend_statement, | ||
| 538 | suspend_statement, | ||
| 539 | assign_expr, | ||
| 540 | comptime_assign_expr, | ||
| 541 | })) { | ||
| 542 | .if_statement => try a.pegIfStatement(), | ||
| 543 | .labeled_statement => try a.pegLabeledStatement(), | ||
| 544 | .comptime_block_expr => { | ||
| 545 | try a.pegToken(.keyword_comptime); | ||
| 546 | try a.pegBlockExpr(); | ||
| 547 | }, | ||
| 548 | |||
| 549 | .nosuspend_statement, | ||
| 550 | .suspend_statement, | ||
| 551 | => |kind| { | ||
| 552 | try a.pegToken(switch (kind) { | ||
| 553 | .nosuspend_statement => .keyword_nosuspend, | ||
| 554 | .suspend_statement => .keyword_suspend, | ||
| 555 | else => unreachable, | ||
| 556 | }); | ||
| 557 | try a.pegBlockExprStatement(); | ||
| 558 | }, | ||
| 559 | .assign_expr, .comptime_assign_expr => |kind| { | ||
| 560 | a.not_expr_statement = true; | ||
| 561 | if (kind == .comptime_assign_expr) { | ||
| 562 | try a.pegToken(.keyword_comptime); | ||
| 563 | a.not_block_expr = true; | ||
| 564 | } | ||
| 565 | try a.pegAssignExpr(); | ||
| 566 | try a.pegToken(.semicolon); | ||
| 567 | }, | ||
| 568 | } | ||
| 569 | } | ||
| 570 | |||
| 571 | /// IfStatement | ||
| 572 | /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? | ||
| 573 | /// / IfPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | ||
| 574 | fn pegIfStatement(a: *AstSmith) SourceError!void { | ||
| 575 | try a.pegIfPrefix(); | ||
| 576 | const is_assign = a.smith.value(bool); | ||
| 577 | if (!is_assign) { | ||
| 578 | try a.pegBlockExpr(); | ||
| 579 | } else { | ||
| 580 | a.not_block_expr = true; | ||
| 581 | try a.pegAssignExpr(); | ||
| 582 | } | ||
| 583 | if (a.not_token != .keyword_else and a.smithListItemBool()) { | ||
| 584 | try a.pegToken(.keyword_else); | ||
| 585 | if (a.smith.value(bool)) { | ||
| 586 | try a.pegPayload(); | ||
| 587 | } | ||
| 588 | try a.pegStatement(); | ||
| 589 | } else if (is_assign) { | ||
| 590 | try a.pegToken(.semicolon); | ||
| 591 | } else { | ||
| 592 | a.not_token = .keyword_else; | ||
| 593 | } | ||
| 594 | } | ||
| 595 | |||
| 596 | /// LabeledStatement <- BlockLabel? (Block / LoopStatement / SwitchExpr) | ||
| 597 | fn pegLabeledStatement(a: *AstSmith) SourceError!void { | ||
| 598 | if (a.smith.value(bool)) { | ||
| 599 | try a.pegBlockLabel(); | ||
| 600 | } | ||
| 601 | switch (a.smith.value(enum { block, loop_statement, switch_expr })) { | ||
| 602 | .block => try a.pegBlock(), | ||
| 603 | .loop_statement => try a.pegLoopStatement(), | ||
| 604 | .switch_expr => try a.pegSwitchExpr(), | ||
| 605 | } | ||
| 606 | } | ||
| 607 | |||
| 608 | /// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) | ||
| 609 | fn pegLoopStatement(a: *AstSmith) SourceError!void { | ||
| 610 | if (a.smith.value(bool)) { | ||
| 611 | try a.pegToken(.keyword_inline); | ||
| 612 | } | ||
| 613 | if (a.smith.value(bool)) { | ||
| 614 | try a.pegForStatement(); | ||
| 615 | } else { | ||
| 616 | try a.pegWhileStatement(); | ||
| 617 | } | ||
| 618 | } | ||
| 619 | |||
| 620 | /// ForStatement | ||
| 621 | /// <- ForPrefix BlockExpr ( KEYWORD_else Statement / !KEYWORD_else ) | ||
| 622 | /// / ForPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Statement ) | ||
| 623 | fn pegForStatement(a: *AstSmith) SourceError!void { | ||
| 624 | try a.pegForPrefix(); | ||
| 625 | const is_assign = a.smith.value(bool); | ||
| 626 | if (!is_assign) { | ||
| 627 | try a.pegBlockExpr(); | ||
| 628 | } else { | ||
| 629 | a.not_block_expr = true; | ||
| 630 | try a.pegAssignExpr(); | ||
| 631 | } | ||
| 632 | if (a.not_token != .keyword_else and a.smithListItemBool()) { | ||
| 633 | try a.pegToken(.keyword_else); | ||
| 634 | try a.pegStatement(); | ||
| 635 | } else if (is_assign) { | ||
| 636 | try a.pegToken(.semicolon); | ||
| 637 | } else { | ||
| 638 | a.not_token = .keyword_else; | ||
| 639 | } | ||
| 640 | } | ||
| 641 | |||
| 642 | /// WhileStatement | ||
| 643 | /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? | ||
| 644 | /// / WhilePrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | ||
| 645 | fn pegWhileStatement(a: *AstSmith) SourceError!void { | ||
| 646 | try a.pegWhilePrefix(); | ||
| 647 | const is_assign = a.smith.value(bool); | ||
| 648 | if (!is_assign) { | ||
| 649 | try a.pegBlockExpr(); | ||
| 650 | } else { | ||
| 651 | a.not_block_expr = true; | ||
| 652 | try a.pegAssignExpr(); | ||
| 653 | } | ||
| 654 | if (a.not_token != .keyword_else and a.smithListItemBool()) { | ||
| 655 | try a.pegToken(.keyword_else); | ||
| 656 | if (a.smith.value(bool)) { | ||
| 657 | try a.pegPayload(); | ||
| 658 | } | ||
| 659 | try a.pegStatement(); | ||
| 660 | } else if (is_assign) { | ||
| 661 | try a.pegToken(.semicolon); | ||
| 662 | } else { | ||
| 663 | a.not_token = .keyword_else; | ||
| 664 | } | ||
| 665 | } | ||
| 666 | |||
| 667 | /// BlockExprStatement | ||
| 668 | /// <- BlockExpr | ||
| 669 | /// / !BlockExpr AssignExpr SEMICOLON | ||
| 670 | fn pegBlockExprStatement(a: *AstSmith) SourceError!void { | ||
| 671 | if (a.smith.value(bool)) { | ||
| 672 | try a.pegBlockExpr(); | ||
| 673 | } else { | ||
| 674 | a.not_block_expr = true; | ||
| 675 | try a.pegAssignExpr(); | ||
| 676 | try a.pegToken(.semicolon); | ||
| 677 | } | ||
| 678 | } | ||
| 679 | |||
| 680 | /// BlockExpr <- BlockLabel? Block | ||
| 681 | fn pegBlockExpr(a: *AstSmith) SourceError!void { | ||
| 682 | if (a.smith.value(bool)) { | ||
| 683 | try a.pegBlockLabel(); | ||
| 684 | } | ||
| 685 | try a.pegBlock(); | ||
| 686 | } | ||
| 687 | |||
| 688 | /// VarAssignStatement <- (Expr / VarDeclProto) (COMMA (Expr / VarDeclProto))* EQUAL Expr SEMICOLON | ||
| 689 | fn pegVarAssignStatement(a: *AstSmith) SourceError!void { | ||
| 690 | while (true) { | ||
| 691 | if (a.smith.value(bool)) { | ||
| 692 | try a.pegVarDeclProto(); | ||
| 693 | } else { | ||
| 694 | try a.pegExpr(); | ||
| 695 | } | ||
| 696 | |||
| 697 | if (a.smithListItemEos()) { | ||
| 698 | break; | ||
| 699 | } else { | ||
| 700 | try a.pegToken(.comma); | ||
| 701 | } | ||
| 702 | } | ||
| 703 | |||
| 704 | try a.pegToken(.equal); | ||
| 705 | try a.pegExpr(); | ||
| 706 | try a.pegToken(.semicolon); | ||
| 707 | } | ||
| 708 | |||
| 709 | /// AssignExpr <- Expr (AssignOp Expr / (COMMA Expr)+ EQUAL Expr)? | ||
| 710 | fn pegAssignExpr(a: *AstSmith) SourceError!void { | ||
| 711 | try a.pegExpr(); | ||
| 712 | if (a.smith.value(bool)) { | ||
| 713 | if (!a.smithListItemBool()) { | ||
| 714 | try a.pegAssignOp(); | ||
| 715 | } else { | ||
| 716 | while (true) { | ||
| 717 | try a.pegToken(.comma); | ||
| 718 | try a.pegExpr(); | ||
| 719 | if (a.smithListItemEos()) break; | ||
| 720 | } | ||
| 721 | try a.pegToken(.equal); | ||
| 722 | } | ||
| 723 | try a.pegExpr(); | ||
| 724 | } | ||
| 725 | } | ||
| 726 | |||
| 727 | /// SingleAssignExpr <- Expr (AssignOp Expr)? | ||
| 728 | fn pegSingleAssignExpr(a: *AstSmith) SourceError!void { | ||
| 729 | try a.pegExpr(); | ||
| 730 | if (a.smith.value(bool)) { | ||
| 731 | try a.pegAssignOp(); | ||
| 732 | try a.pegExpr(); | ||
| 733 | } | ||
| 734 | } | ||
| 735 | |||
| 736 | /// Expr <- BoolOrExpr | ||
| 737 | const pegExpr = pegBoolOrExpr; | ||
| 738 | |||
| 739 | /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)* | ||
| 740 | fn pegBoolOrExpr(a: *AstSmith) SourceError!void { | ||
| 741 | try a.pegBoolAndExpr(); | ||
| 742 | while (!a.not_expr_suffix and !a.smithListItemEos()) { | ||
| 743 | try a.pegTokenWhitespaceAround(.keyword_or); | ||
| 744 | try a.pegBoolAndExpr(); | ||
| 745 | } | ||
| 746 | } | ||
| 747 | |||
| 748 | /// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)* | ||
| 749 | fn pegBoolAndExpr(a: *AstSmith) SourceError!void { | ||
| 750 | try a.pegCompareExpr(); | ||
| 751 | while (!a.not_expr_suffix and !a.smithListItemEos()) { | ||
| 752 | try a.pegTokenWhitespaceAround(.keyword_and); | ||
| 753 | try a.pegCompareExpr(); | ||
| 754 | } | ||
| 755 | } | ||
| 756 | |||
| 757 | /// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? | ||
| 758 | fn pegCompareExpr(a: *AstSmith) SourceError!void { | ||
| 759 | try a.pegBitwiseExpr(); | ||
| 760 | if (!a.not_expr_suffix and a.smithListItemBool()) { | ||
| 761 | try a.pegCompareOp(); | ||
| 762 | try a.pegBitwiseExpr(); | ||
| 763 | } | ||
| 764 | } | ||
| 765 | |||
| 766 | /// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* | ||
| 767 | fn pegBitwiseExpr(a: *AstSmith) SourceError!void { | ||
| 768 | try a.pegBitShiftExpr(); | ||
| 769 | while (!a.not_expr_suffix and !a.smithListItemEos()) { | ||
| 770 | try a.pegBitwiseOp(); | ||
| 771 | try a.pegBitShiftExpr(); | ||
| 772 | } | ||
| 773 | } | ||
| 774 | |||
| 775 | /// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* | ||
| 776 | fn pegBitShiftExpr(a: *AstSmith) SourceError!void { | ||
| 777 | try a.pegAdditionExpr(); | ||
| 778 | while (!a.not_expr_suffix and !a.smithListItemEos()) { | ||
| 779 | try a.pegBitShiftOp(); | ||
| 780 | try a.pegAdditionExpr(); | ||
| 781 | } | ||
| 782 | } | ||
| 783 | |||
| 784 | /// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* | ||
| 785 | fn pegAdditionExpr(a: *AstSmith) SourceError!void { | ||
| 786 | try a.pegMultiplyExpr(); | ||
| 787 | while (!a.not_expr_suffix and !a.smithListItemEos()) { | ||
| 788 | try a.pegAdditionOp(); | ||
| 789 | try a.pegMultiplyExpr(); | ||
| 790 | } | ||
| 791 | } | ||
| 792 | |||
| 793 | /// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* | ||
| 794 | fn pegMultiplyExpr(a: *AstSmith) SourceError!void { | ||
| 795 | try a.pegPrefixExpr(); | ||
| 796 | while (!a.not_expr_suffix and !a.smithListItemEos()) { | ||
| 797 | try a.pegMultiplyOp(); | ||
| 798 | try a.pegPrefixExpr(); | ||
| 799 | } | ||
| 800 | } | ||
| 801 | |||
| 802 | /// PrefixExpr <- PrefixOp* PrimaryExpr | ||
| 803 | fn pegPrefixExpr(a: *AstSmith) SourceError!void { | ||
| 804 | while (!a.smithListItemEos()) { | ||
| 805 | try a.pegPrefixOp(); | ||
| 806 | } | ||
| 807 | try a.pegPrimaryExpr(); | ||
| 808 | } | ||
| 809 | |||
| 810 | /// PrimaryExpr | ||
| 811 | /// <- AsmExpr | ||
| 812 | /// / IfExpr | ||
| 813 | /// / KEYWORD_break (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart) | ||
| 814 | /// / KEYWORD_comptime Expr !ExprSuffix | ||
| 815 | /// / KEYWORD_nosuspend Expr !ExprSuffix | ||
| 816 | /// / KEYWORD_continue (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart) | ||
| 817 | /// / KEYWORD_resume Expr !ExprSuffix | ||
| 818 | /// / KEYWORD_return (Expr !ExprSuffix / !SinglePtrTypeStart) | ||
| 819 | /// / BlockLabel? LoopExpr | ||
| 820 | /// / Block | ||
| 821 | /// / CurlySuffixExpr | ||
| 822 | fn pegPrimaryExpr(a: *AstSmith) SourceError!void { | ||
| 823 | const Kind = enum(u8) { | ||
| 824 | curly_suffix_expr, | ||
| 825 | @"return", | ||
| 826 | @"continue", | ||
| 827 | @"break", | ||
| 828 | block, | ||
| 829 | asm_expr, | ||
| 830 | // Always contain more expressions | ||
| 831 | if_expr, | ||
| 832 | loop_expr, | ||
| 833 | @"resume", | ||
| 834 | @"comptime", | ||
| 835 | @"nosuspend", | ||
| 836 | }; | ||
| 837 | |||
| 838 | switch (a.smith.valueWeighted(Kind, &.{ | ||
| 839 | .value(Kind, .curly_suffix_expr, 75), | ||
| 840 | .rangeAtMost(Kind, .@"return", .asm_expr, 4), | ||
| 841 | .rangeAtMost(Kind, .if_expr, .@"nosuspend", 1), | ||
| 842 | })) { | ||
| 843 | .curly_suffix_expr => try a.pegCurlySuffixExpr(), | ||
| 844 | |||
| 845 | .block => if (a.not_labelable_expr != .expr and !a.not_block_expr and !a.not_expr_statement) { | ||
| 846 | try a.pegBlock(); | ||
| 847 | } else { | ||
| 848 | // Group | ||
| 849 | try a.pegToken(.l_paren); | ||
| 850 | try a.pegBlock(); | ||
| 851 | try a.pegToken(.r_paren); | ||
| 852 | }, | ||
| 853 | .asm_expr => try a.pegAsmExpr(), | ||
| 854 | .if_expr => if (!a.not_expr_statement) { | ||
| 855 | try a.pegIfExpr(); | ||
| 856 | } else { | ||
| 857 | // Group | ||
| 858 | try a.pegToken(.l_paren); | ||
| 859 | try a.pegIfExpr(); | ||
| 860 | try a.pegToken(.r_paren); | ||
| 861 | }, | ||
| 862 | .loop_expr => { | ||
| 863 | const group = a.not_labelable_expr == .expr or a.not_expr_statement; | ||
| 864 | if (group) try a.pegToken(.l_paren); | ||
| 865 | if (!a.not_label and a.not_token != .identifier and a.smith.value(bool)) { | ||
| 866 | try a.pegBlockLabel(); | ||
| 867 | } | ||
| 868 | try a.pegLoopExpr(); | ||
| 869 | if (group) try a.pegToken(.r_paren); | ||
| 870 | }, | ||
| 871 | |||
| 872 | .@"return", | ||
| 873 | .@"comptime", | ||
| 874 | .@"nosuspend", | ||
| 875 | .@"resume", | ||
| 876 | .@"break", | ||
| 877 | .@"continue", | ||
| 878 | => |t| { | ||
| 879 | const group = a.not_expr_statement and (t == .@"nosuspend" or t == .@"comptime"); | ||
| 880 | if (group) try a.pegToken(.l_paren); | ||
| 881 | |||
| 882 | const kw: Token.Tag, const label, const expr = switch (t) { | ||
| 883 | .@"return" => .{ .keyword_return, false, a.smithListItemBool() }, | ||
| 884 | .@"comptime" => .{ .keyword_comptime, false, true }, | ||
| 885 | .@"nosuspend" => .{ .keyword_nosuspend, false, true }, | ||
| 886 | .@"resume" => .{ .keyword_resume, false, true }, | ||
| 887 | .@"break" => .{ .keyword_break, a.smith.value(bool), a.smithListItemBool() }, | ||
| 888 | .@"continue" => .{ .keyword_continue, a.smith.value(bool), a.smithListItemBool() }, | ||
| 889 | else => unreachable, | ||
| 890 | }; | ||
| 891 | try a.pegToken(kw); | ||
| 892 | if (label) { | ||
| 893 | try a.pegBreakLabel(); | ||
| 894 | } else { | ||
| 895 | a.not_break_label = true; | ||
| 896 | } | ||
| 897 | if (expr) { | ||
| 898 | try a.pegExpr(); | ||
| 899 | a.not_expr_suffix = true; | ||
| 900 | } else { | ||
| 901 | a.not_token = .asterisk; | ||
| 902 | } | ||
| 903 | |||
| 904 | if (group) try a.pegToken(.r_paren); | ||
| 905 | }, | ||
| 906 | } | ||
| 907 | } | ||
| 908 | |||
| 909 | /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffix | ||
| 910 | fn pegIfExpr(a: *AstSmith) SourceError!void { | ||
| 911 | try a.pegIfPrefix(); | ||
| 912 | try a.pegExpr(); | ||
| 913 | const Else = enum { none, @"else", else_payload }; | ||
| 914 | switch (if (a.not_token != .keyword_else) a.smith.value(Else) else .none) { | ||
| 915 | .none => a.not_token = .keyword_else, | ||
| 916 | .@"else" => { | ||
| 917 | try a.pegToken(.keyword_else); | ||
| 918 | try a.pegExpr(); | ||
| 919 | }, | ||
| 920 | .else_payload => { | ||
| 921 | try a.pegToken(.keyword_else); | ||
| 922 | try a.pegPayload(); | ||
| 923 | try a.pegExpr(); | ||
| 924 | }, | ||
| 925 | } | ||
| 926 | a.not_expr_suffix = true; | ||
| 927 | } | ||
| 928 | |||
| 929 | /// Block <- LBRACE Statement* RBRACE | ||
| 930 | fn pegBlock(a: *AstSmith) SourceError!void { | ||
| 931 | try a.pegToken(.l_brace); | ||
| 932 | while (!a.smithListItemEos()) { | ||
| 933 | try a.pegBlockStatement(); | ||
| 934 | } | ||
| 935 | try a.pegToken(.r_brace); | ||
| 936 | } | ||
| 937 | |||
| 938 | /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) | ||
| 939 | fn pegLoopExpr(a: *AstSmith) SourceError!void { | ||
| 940 | if (a.smith.value(bool)) { | ||
| 941 | try a.pegToken(.keyword_inline); | ||
| 942 | } | ||
| 943 | |||
| 944 | if (a.smith.value(bool)) { | ||
| 945 | try a.pegForExpr(); | ||
| 946 | } else { | ||
| 947 | try a.pegWhileExpr(); | ||
| 948 | } | ||
| 949 | } | ||
| 950 | |||
| 951 | /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr / !KEYWORD_else) !ExprSuffix | ||
| 952 | fn pegForExpr(a: *AstSmith) SourceError!void { | ||
| 953 | try a.pegForPrefix(); | ||
| 954 | try a.pegExpr(); | ||
| 955 | if (a.not_token != .keyword_else and a.smith.value(bool)) { | ||
| 956 | try a.pegToken(.keyword_else); | ||
| 957 | try a.pegExpr(); | ||
| 958 | } else { | ||
| 959 | a.not_token = .keyword_else; | ||
| 960 | } | ||
| 961 | a.not_expr_suffix = true; | ||
| 962 | } | ||
| 963 | |||
| 964 | /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffix | ||
| 965 | fn pegWhileExpr(a: *AstSmith) SourceError!void { | ||
| 966 | try a.pegWhilePrefix(); | ||
| 967 | try a.pegExpr(); | ||
| 968 | const Else = enum { none, @"else", else_payload }; | ||
| 969 | switch (if (a.not_token != .keyword_else) a.smith.value(Else) else .none) { | ||
| 970 | .none => a.not_token = .keyword_else, | ||
| 971 | .@"else" => { | ||
| 972 | try a.pegToken(.keyword_else); | ||
| 973 | try a.pegExpr(); | ||
| 974 | }, | ||
| 975 | .else_payload => { | ||
| 976 | try a.pegToken(.keyword_else); | ||
| 977 | try a.pegPayload(); | ||
| 978 | try a.pegExpr(); | ||
| 979 | }, | ||
| 980 | } | ||
| 981 | a.not_expr_suffix = true; | ||
| 982 | } | ||
| 983 | |||
| 984 | /// CurlySuffixExpr <- TypeExpr InitList? | ||
| 985 | fn pegCurlySuffixExpr(a: *AstSmith) SourceError!void { | ||
| 986 | try a.pegTypeExpr(); | ||
| 987 | if (!a.not_expr_suffix and a.smith.value(bool)) { | ||
| 988 | try a.pegInitList(); | ||
| 989 | } | ||
| 990 | } | ||
| 991 | |||
| 992 | /// InitList | ||
| 993 | /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE | ||
| 994 | /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE | ||
| 995 | /// / LBRACE RBRACE | ||
| 996 | fn pegInitList(a: *AstSmith) SourceError!void { | ||
| 997 | try a.pegToken(.l_brace); | ||
| 998 | if (a.smithListItemBool()) { | ||
| 999 | if (a.smith.value(bool)) { | ||
| 1000 | try a.pegFieldInit(); | ||
| 1001 | while (!a.smithListItemEos()) { | ||
| 1002 | try a.pegToken(.comma); | ||
| 1003 | try a.pegFieldInit(); | ||
| 1004 | } | ||
| 1005 | } else { | ||
| 1006 | try a.pegExpr(); | ||
| 1007 | while (!a.smithListItemEos()) { | ||
| 1008 | try a.pegToken(.comma); | ||
| 1009 | try a.pegExpr(); | ||
| 1010 | } | ||
| 1011 | } | ||
| 1012 | if (a.smith.value(bool)) { | ||
| 1013 | try a.pegToken(.comma); | ||
| 1014 | } | ||
| 1015 | } | ||
| 1016 | try a.pegToken(.r_brace); | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | /// PrefixTypeOp* ErrorUnionExpr | ||
| 1020 | fn pegTypeExpr(a: *AstSmith) SourceError!void { | ||
| 1021 | while (!a.smithListItemEos()) { | ||
| 1022 | try a.pegPrefixTypeOp(); | ||
| 1023 | } | ||
| 1024 | try a.pegErrorUnionExpr(); | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | /// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? | ||
| 1028 | fn pegErrorUnionExpr(a: *AstSmith) SourceError!void { | ||
| 1029 | try a.pegSuffixExpr(); | ||
| 1030 | if (!a.not_expr_suffix and a.smithListItemBool()) { | ||
| 1031 | try a.pegToken(.bang); | ||
| 1032 | try a.pegTypeExpr(); | ||
| 1033 | } | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | /// SuffixExpr | ||
| 1037 | /// <- PrimaryTypeExpr (SuffixOp / FnCallArguments)* | ||
| 1038 | fn pegSuffixExpr(a: *AstSmith) SourceError!void { | ||
| 1039 | try a.pegPrimaryTypeExpr(); | ||
| 1040 | while (!a.not_expr_suffix and !a.smithListItemEos()) { | ||
| 1041 | if (a.smith.value(bool)) { | ||
| 1042 | try a.pegSuffixOp(); | ||
| 1043 | } else { | ||
| 1044 | try a.pegFnCallArguments(); | ||
| 1045 | } | ||
| 1046 | } | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | /// PrimaryTypeExpr | ||
| 1050 | /// <- BUILTINIDENTIFIER FnCallArguments | ||
| 1051 | /// / CHAR_LITERAL | ||
| 1052 | /// / ContainerDecl | ||
| 1053 | /// / DOT IDENTIFIER | ||
| 1054 | /// / DOT InitList | ||
| 1055 | /// / ErrorSetDecl | ||
| 1056 | /// / FLOAT | ||
| 1057 | /// / FnProto | ||
| 1058 | /// / GroupedExpr | ||
| 1059 | /// / LabeledTypeExpr | ||
| 1060 | /// / IDENTIFIER !(COLON LabelableExpr) | ||
| 1061 | /// / IfTypeExpr | ||
| 1062 | /// / INTEGER | ||
| 1063 | /// / KEYWORD_comptime TypeExpr !ExprSuffix | ||
| 1064 | /// / KEYWORD_error DOT IDENTIFIER | ||
| 1065 | /// / KEYWORD_anyframe | ||
| 1066 | /// / KEYWORD_unreachable | ||
| 1067 | /// / STRINGLITERAL | ||
| 1068 | fn pegPrimaryTypeExpr(a: *AstSmith) SourceError!void { | ||
| 1069 | const Kind = enum(u8) { | ||
| 1070 | identifier, | ||
| 1071 | float, | ||
| 1072 | integer, | ||
| 1073 | char_literal, | ||
| 1074 | string_literal, | ||
| 1075 | enum_literal, | ||
| 1076 | error_literal, | ||
| 1077 | unreachable_type, | ||
| 1078 | anyframe_type, | ||
| 1079 | |||
| 1080 | // Containing zero or more expressions | ||
| 1081 | builtin_call, | ||
| 1082 | array_literal, | ||
| 1083 | container_decl, | ||
| 1084 | fn_proto, | ||
| 1085 | error_set, | ||
| 1086 | |||
| 1087 | // Containing one or more epressions | ||
| 1088 | grouped, | ||
| 1089 | labeled_type_expr, | ||
| 1090 | if_type_expr, | ||
| 1091 | comptime_expr, | ||
| 1092 | }; | ||
| 1093 | |||
| 1094 | switch (a.smith.valueWeighted(Kind, &.{ | ||
| 1095 | .rangeAtMost(Kind, .identifier, .anyframe_type, 5), | ||
| 1096 | .rangeAtMost(Kind, .builtin_call, .error_set, 2), | ||
| 1097 | .rangeAtMost(Kind, .grouped, .comptime_expr, 1), | ||
| 1098 | })) { | ||
| 1099 | .identifier => if (a.not_token != .identifier) { | ||
| 1100 | try a.pegIdentifier(); | ||
| 1101 | a.not_labelable_expr = .colon; | ||
| 1102 | } else { | ||
| 1103 | // Group | ||
| 1104 | try a.pegToken(.l_paren); | ||
| 1105 | try a.pegIdentifier(); | ||
| 1106 | try a.pegToken(.r_paren); | ||
| 1107 | }, | ||
| 1108 | .float => try a.pegFloat(), | ||
| 1109 | .integer => try a.pegInteger(), | ||
| 1110 | .char_literal => try a.pegCharLiteral(), | ||
| 1111 | .string_literal => try a.pegStringLiteral(), | ||
| 1112 | .enum_literal => { | ||
| 1113 | try a.pegToken(.period); | ||
| 1114 | try a.pegIdentifier(); | ||
| 1115 | }, | ||
| 1116 | .error_literal => { | ||
| 1117 | try a.pegToken(.keyword_error); | ||
| 1118 | try a.pegToken(.period); | ||
| 1119 | try a.pegIdentifier(); | ||
| 1120 | }, | ||
| 1121 | .unreachable_type => try a.pegToken(.keyword_unreachable), | ||
| 1122 | .anyframe_type => try a.pegToken(.keyword_anyframe), | ||
| 1123 | |||
| 1124 | .builtin_call => { | ||
| 1125 | try a.pegBuiltinIdentifier(); | ||
| 1126 | try a.pegFnCallArguments(); | ||
| 1127 | }, | ||
| 1128 | .array_literal => { | ||
| 1129 | try a.pegToken(.period); | ||
| 1130 | try a.pegInitList(); | ||
| 1131 | }, | ||
| 1132 | .container_decl => try a.pegContainerDecl(), | ||
| 1133 | .fn_proto => if (a.not_token != .keyword_fn) { | ||
| 1134 | try a.pegFnProto(); | ||
| 1135 | } else { | ||
| 1136 | // Group | ||
| 1137 | try a.pegToken(.l_paren); | ||
| 1138 | try a.pegFnProto(); | ||
| 1139 | try a.pegToken(.r_paren); | ||
| 1140 | }, | ||
| 1141 | .error_set => try a.pegErrorSetDecl(), | ||
| 1142 | |||
| 1143 | .grouped => try a.pegGroupedExpr(), | ||
| 1144 | .labeled_type_expr => try a.pegLabeledTypeExpr(), | ||
| 1145 | .if_type_expr => if (!a.not_expr_statement) { | ||
| 1146 | try a.pegIfTypeExpr(); | ||
| 1147 | } else { | ||
| 1148 | // Group | ||
| 1149 | try a.pegToken(.l_paren); | ||
| 1150 | try a.pegIfTypeExpr(); | ||
| 1151 | try a.pegToken(.r_paren); | ||
| 1152 | }, | ||
| 1153 | .comptime_expr => if (!a.not_token_comptime and !a.not_expr_statement) { | ||
| 1154 | try a.pegToken(.keyword_comptime); | ||
| 1155 | try a.pegTypeExpr(); | ||
| 1156 | } else { | ||
| 1157 | // Group | ||
| 1158 | try a.pegToken(.l_paren); | ||
| 1159 | try a.pegToken(.keyword_comptime); | ||
| 1160 | try a.pegTypeExpr(); | ||
| 1161 | try a.pegToken(.r_paren); | ||
| 1162 | }, | ||
| 1163 | } | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto | ||
| 1167 | fn pegContainerDecl(a: *AstSmith) SourceError!void { | ||
| 1168 | switch (a.smith.value(enum { auto, @"extern", @"packed" })) { | ||
| 1169 | .auto => {}, | ||
| 1170 | .@"extern" => try a.pegToken(.keyword_extern), | ||
| 1171 | .@"packed" => try a.pegToken(.keyword_packed), | ||
| 1172 | } | ||
| 1173 | try a.pegContainerDeclAuto(); | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | /// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE | ||
| 1177 | fn pegErrorSetDecl(a: *AstSmith) SourceError!void { | ||
| 1178 | try a.pegToken(.keyword_error); | ||
| 1179 | try a.pegToken(.l_brace); | ||
| 1180 | try a.pegIdentifierList(); | ||
| 1181 | try a.pegToken(.r_brace); | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | /// GroupedExpr <- LPAREN Expr RPAREN | ||
| 1185 | fn pegGroupedExpr(a: *AstSmith) SourceError!void { | ||
| 1186 | try a.pegToken(.l_paren); | ||
| 1187 | try a.pegExpr(); | ||
| 1188 | try a.pegToken(.r_paren); | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix | ||
| 1192 | fn pegIfTypeExpr(a: *AstSmith) SourceError!void { | ||
| 1193 | try a.pegIfPrefix(); | ||
| 1194 | try a.pegTypeExpr(); | ||
| 1195 | const Else = enum { none, @"else", else_payload }; | ||
| 1196 | switch (if (a.not_token != .keyword_else) a.smith.value(Else) else .none) { | ||
| 1197 | .none => a.not_token = .keyword_else, | ||
| 1198 | .@"else" => { | ||
| 1199 | try a.pegToken(.keyword_else); | ||
| 1200 | try a.pegTypeExpr(); | ||
| 1201 | }, | ||
| 1202 | .else_payload => { | ||
| 1203 | try a.pegToken(.keyword_else); | ||
| 1204 | try a.pegPayload(); | ||
| 1205 | try a.pegTypeExpr(); | ||
| 1206 | }, | ||
| 1207 | } | ||
| 1208 | a.not_expr_suffix = true; | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | /// LabeledTypeExpr | ||
| 1212 | /// <- BlockLabel Block | ||
| 1213 | /// / BlockLabel? LoopTypeExpr | ||
| 1214 | /// / BlockLabel? SwitchExpr | ||
| 1215 | fn pegLabeledTypeExpr(a: *AstSmith) SourceError!void { | ||
| 1216 | const kind = a.smith.value(enum { block, loop, @"switch" }); | ||
| 1217 | const not_any = a.not_labelable_expr == .expr or a.not_expr_statement; | ||
| 1218 | const no_label = a.not_label or a.not_token == .identifier; | ||
| 1219 | const no_block = no_label or a.not_block_expr; | ||
| 1220 | const group = not_any or (kind == .block and no_block); | ||
| 1221 | if (group) try a.pegToken(.l_paren); | ||
| 1222 | |||
| 1223 | switch (kind) { | ||
| 1224 | .block => { | ||
| 1225 | try a.pegBlockLabel(); | ||
| 1226 | try a.pegBlock(); | ||
| 1227 | }, | ||
| 1228 | .loop => { | ||
| 1229 | if (!no_label and a.smith.value(bool)) { | ||
| 1230 | try a.pegBlockLabel(); | ||
| 1231 | } | ||
| 1232 | try a.pegLoopTypeExpr(); | ||
| 1233 | }, | ||
| 1234 | .@"switch" => { | ||
| 1235 | if (!no_label and a.smith.value(bool)) { | ||
| 1236 | try a.pegBlockLabel(); | ||
| 1237 | } | ||
| 1238 | try a.pegSwitchExpr(); | ||
| 1239 | }, | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | if (group) try a.pegToken(.r_paren); | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) | ||
| 1246 | fn pegLoopTypeExpr(a: *AstSmith) SourceError!void { | ||
| 1247 | if (a.smith.value(bool)) { | ||
| 1248 | try a.pegToken(.keyword_inline); | ||
| 1249 | } | ||
| 1250 | |||
| 1251 | if (a.smith.value(bool)) { | ||
| 1252 | try a.pegForTypeExpr(); | ||
| 1253 | } else { | ||
| 1254 | try a.pegWhileTypeExpr(); | ||
| 1255 | } | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr / !KEYWORD_else) !ExprSuffix | ||
| 1259 | fn pegForTypeExpr(a: *AstSmith) SourceError!void { | ||
| 1260 | try a.pegForPrefix(); | ||
| 1261 | try a.pegTypeExpr(); | ||
| 1262 | if (a.not_token != .keyword_else and a.smith.value(bool)) { | ||
| 1263 | try a.pegToken(.keyword_else); | ||
| 1264 | try a.pegTypeExpr(); | ||
| 1265 | } else { | ||
| 1266 | a.not_token = .keyword_else; | ||
| 1267 | } | ||
| 1268 | a.not_expr_suffix = true; | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix | ||
| 1272 | fn pegWhileTypeExpr(a: *AstSmith) SourceError!void { | ||
| 1273 | try a.pegWhilePrefix(); | ||
| 1274 | try a.pegTypeExpr(); | ||
| 1275 | const Else = enum { none, @"else", else_payload }; | ||
| 1276 | switch (if (a.not_token != .keyword_else) a.smith.value(Else) else .none) { | ||
| 1277 | .none => a.not_token = .keyword_else, | ||
| 1278 | .@"else" => { | ||
| 1279 | try a.pegToken(.keyword_else); | ||
| 1280 | try a.pegTypeExpr(); | ||
| 1281 | }, | ||
| 1282 | .else_payload => { | ||
| 1283 | try a.pegToken(.keyword_else); | ||
| 1284 | try a.pegPayload(); | ||
| 1285 | try a.pegTypeExpr(); | ||
| 1286 | }, | ||
| 1287 | } | ||
| 1288 | a.not_expr_suffix = true; | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE | ||
| 1292 | fn pegSwitchExpr(a: *AstSmith) SourceError!void { | ||
| 1293 | try a.pegToken(.keyword_switch); | ||
| 1294 | try a.pegToken(.l_paren); | ||
| 1295 | try a.pegExpr(); | ||
| 1296 | try a.pegToken(.r_paren); | ||
| 1297 | |||
| 1298 | try a.pegToken(.l_brace); | ||
| 1299 | try a.pegSwitchProngList(); | ||
| 1300 | try a.pegToken(.r_brace); | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | /// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN | ||
| 1304 | fn pegAsmExpr(a: *AstSmith) SourceError!void { | ||
| 1305 | try a.pegToken(.keyword_asm); | ||
| 1306 | if (a.smith.value(bool)) { | ||
| 1307 | try a.pegToken(.keyword_volatile); | ||
| 1308 | } | ||
| 1309 | try a.pegToken(.l_paren); | ||
| 1310 | try a.pegExpr(); | ||
| 1311 | if (a.smith.value(bool)) { | ||
| 1312 | try a.pegAsmOutput(); | ||
| 1313 | } | ||
| 1314 | try a.pegToken(.r_paren); | ||
| 1315 | } | ||
| 1316 | |||
| 1317 | /// AsmOutput <- COLON AsmOutputList AsmInput? | ||
| 1318 | fn pegAsmOutput(a: *AstSmith) SourceError!void { | ||
| 1319 | try a.pegToken(.colon); | ||
| 1320 | try a.pegAsmOutputList(); | ||
| 1321 | if (a.smith.value(bool)) { | ||
| 1322 | try a.pegAsmInput(); | ||
| 1323 | } | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN | ||
| 1327 | fn pegAsmOutputItem(a: *AstSmith) SourceError!void { | ||
| 1328 | try a.pegToken(.l_bracket); | ||
| 1329 | try a.pegIdentifier(); | ||
| 1330 | try a.pegToken(.r_bracket); | ||
| 1331 | try a.pegStringLiteralSingle(); | ||
| 1332 | try a.pegToken(.l_paren); | ||
| 1333 | if (a.smith.value(bool)) { | ||
| 1334 | try a.pegToken(.arrow); | ||
| 1335 | try a.pegTypeExpr(); | ||
| 1336 | } else { | ||
| 1337 | try a.pegIdentifier(); | ||
| 1338 | } | ||
| 1339 | try a.pegToken(.r_paren); | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | /// AsmInput <- COLON AsmInputList AsmClobbers? | ||
| 1343 | fn pegAsmInput(a: *AstSmith) SourceError!void { | ||
| 1344 | try a.pegToken(.colon); | ||
| 1345 | try a.pegAsmInputList(); | ||
| 1346 | if (a.smith.value(bool)) { | ||
| 1347 | try a.pegAsmClobbers(); | ||
| 1348 | } | ||
| 1349 | } | ||
| 1350 | |||
| 1351 | /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN Expr RPAREN | ||
| 1352 | fn pegAsmInputItem(a: *AstSmith) SourceError!void { | ||
| 1353 | try a.pegToken(.l_bracket); | ||
| 1354 | try a.pegIdentifier(); | ||
| 1355 | try a.pegToken(.r_bracket); | ||
| 1356 | try a.pegStringLiteralSingle(); | ||
| 1357 | try a.pegToken(.l_paren); | ||
| 1358 | try a.pegExpr(); | ||
| 1359 | try a.pegToken(.r_paren); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | /// AsmClobbers <- COLON Expr | ||
| 1363 | fn pegAsmClobbers(a: *AstSmith) SourceError!void { | ||
| 1364 | try a.pegToken(.colon); | ||
| 1365 | try a.pegExpr(); | ||
| 1366 | } | ||
| 1367 | |||
| 1368 | /// BreakLabel <- COLON IDENTIFIER | ||
| 1369 | fn pegBreakLabel(a: *AstSmith) SourceError!void { | ||
| 1370 | try a.pegToken(.colon); | ||
| 1371 | try a.pegIdentifier(); | ||
| 1372 | } | ||
| 1373 | |||
| 1374 | /// BlockLabel <- IDENTIFIER COLON | ||
| 1375 | fn pegBlockLabel(a: *AstSmith) SourceError!void { | ||
| 1376 | try a.pegIdentifier(); | ||
| 1377 | try a.pegToken(.colon); | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | /// FieldInit <- DOT IDENTIFIER EQUAL Expr | ||
| 1381 | fn pegFieldInit(a: *AstSmith) SourceError!void { | ||
| 1382 | try a.pegToken(.period); | ||
| 1383 | try a.pegIdentifier(); | ||
| 1384 | try a.pegToken(.equal); | ||
| 1385 | try a.pegExpr(); | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN | ||
| 1389 | fn pegWhileContinueExpr(a: *AstSmith) SourceError!void { | ||
| 1390 | try a.pegToken(.colon); | ||
| 1391 | try a.pegToken(.l_paren); | ||
| 1392 | try a.pegAssignExpr(); | ||
| 1393 | try a.pegToken(.r_paren); | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | /// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN | ||
| 1397 | fn pegLinkSection(a: *AstSmith) SourceError!void { | ||
| 1398 | try a.pegToken(.keyword_linksection); | ||
| 1399 | try a.pegToken(.l_paren); | ||
| 1400 | try a.pegExpr(); | ||
| 1401 | try a.pegToken(.r_paren); | ||
| 1402 | } | ||
| 1403 | |||
| 1404 | /// AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN | ||
| 1405 | fn pegAddrSpace(a: *AstSmith) SourceError!void { | ||
| 1406 | try a.pegToken(.keyword_addrspace); | ||
| 1407 | try a.pegToken(.l_paren); | ||
| 1408 | try a.pegExpr(); | ||
| 1409 | try a.pegToken(.r_paren); | ||
| 1410 | } | ||
| 1411 | |||
| 1412 | /// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN | ||
| 1413 | fn pegCallConv(a: *AstSmith) SourceError!void { | ||
| 1414 | try a.pegToken(.keyword_callconv); | ||
| 1415 | try a.pegToken(.l_paren); | ||
| 1416 | try a.pegExpr(); | ||
| 1417 | try a.pegToken(.r_paren); | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | /// ParamDecl <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime)? | ||
| 1421 | /// ((IDENTIFIER COLON) / !KEYWORD_comptime !(IDENTIFIER COLON)) | ||
| 1422 | /// ParamType | ||
| 1423 | fn pegParamDecl(a: *AstSmith) SourceError!void { | ||
| 1424 | try a.pegMaybeDocComment(); | ||
| 1425 | const modifier = a.smith.value(enum { none, @"noalias", @"comptime" }); | ||
| 1426 | switch (modifier) { | ||
| 1427 | .none => a.not_token_comptime = true, | ||
| 1428 | .@"noalias" => try a.pegToken(.keyword_noalias), | ||
| 1429 | .@"comptime" => try a.pegToken(.keyword_comptime), | ||
| 1430 | } | ||
| 1431 | if (a.smith.value(bool)) { | ||
| 1432 | try a.pegIdentifier(); | ||
| 1433 | try a.pegToken(.colon); | ||
| 1434 | } else { | ||
| 1435 | a.not_label = true; | ||
| 1436 | } | ||
| 1437 | try a.pegParamType(); | ||
| 1438 | } | ||
| 1439 | |||
| 1440 | /// ParamType | ||
| 1441 | /// <- KEYWORD_anytype | ||
| 1442 | /// / TypeExpr | ||
| 1443 | fn pegParamType(a: *AstSmith) SourceError!void { | ||
| 1444 | if (a.smith.value(bool)) { | ||
| 1445 | try a.pegToken(.keyword_anytype); | ||
| 1446 | } else { | ||
| 1447 | try a.pegTypeExpr(); | ||
| 1448 | } | ||
| 1449 | } | ||
| 1450 | |||
| 1451 | /// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? | ||
| 1452 | fn pegIfPrefix(a: *AstSmith) SourceError!void { | ||
| 1453 | try a.pegToken(.keyword_if); | ||
| 1454 | try a.pegToken(.l_paren); | ||
| 1455 | try a.pegExpr(); | ||
| 1456 | try a.pegToken(.r_paren); | ||
| 1457 | try a.pegPtrPayload(); | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | ||
| 1461 | fn pegWhilePrefix(a: *AstSmith) SourceError!void { | ||
| 1462 | try a.pegToken(.keyword_while); | ||
| 1463 | try a.pegToken(.l_paren); | ||
| 1464 | try a.pegExpr(); | ||
| 1465 | try a.pegToken(.r_paren); | ||
| 1466 | |||
| 1467 | if (a.smith.value(bool)) { | ||
| 1468 | try a.pegPtrPayload(); | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | if (a.smith.value(bool)) { | ||
| 1472 | try a.pegWhileContinueExpr(); | ||
| 1473 | } | ||
| 1474 | } | ||
| 1475 | |||
| 1476 | /// ForPrefix <- KEYWORD_for LPAREN ForArgumentsList RPAREN PtrListPayload | ||
| 1477 | /// | ||
| 1478 | /// An additional requirement checked in the Parser is that the number of | ||
| 1479 | /// arguments and payload elements are the same. | ||
| 1480 | fn pegForPrefix(a: *AstSmith) SourceError!void { | ||
| 1481 | try a.pegToken(.keyword_for); | ||
| 1482 | try a.pegToken(.l_paren); | ||
| 1483 | const n = try a.pegForArgumentsList(); | ||
| 1484 | try a.pegToken(.r_paren); | ||
| 1485 | try a.pegPtrListPayload(n); | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | /// Payload <- PIPE IDENTIFIER PIPE | ||
| 1489 | fn pegPayload(a: *AstSmith) SourceError!void { | ||
| 1490 | try a.pegToken(.pipe); | ||
| 1491 | try a.pegIdentifier(); | ||
| 1492 | try a.pegToken(.pipe); | ||
| 1493 | } | ||
| 1494 | |||
| 1495 | /// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE | ||
| 1496 | fn pegPtrPayload(a: *AstSmith) SourceError!void { | ||
| 1497 | try a.pegToken(.pipe); | ||
| 1498 | if (a.smith.value(bool)) { | ||
| 1499 | try a.pegToken(.asterisk); | ||
| 1500 | } | ||
| 1501 | try a.pegIdentifier(); | ||
| 1502 | try a.pegToken(.pipe); | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | /// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE | ||
| 1506 | fn pegPtrIndexPayload(a: *AstSmith) SourceError!void { | ||
| 1507 | try a.pegToken(.pipe); | ||
| 1508 | if (a.smith.value(bool)) { | ||
| 1509 | try a.pegToken(.asterisk); | ||
| 1510 | } | ||
| 1511 | try a.pegIdentifier(); | ||
| 1512 | if (a.smith.value(bool)) { | ||
| 1513 | try a.pegToken(.comma); | ||
| 1514 | try a.pegIdentifier(); | ||
| 1515 | } | ||
| 1516 | try a.pegToken(.pipe); | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | /// PtrListPayload <- PIPE ASTERISK? IDENTIFIER (COMMA ASTERISK? IDENTIFIER)* COMMA? PIPE | ||
| 1520 | fn pegPtrListPayload(a: *AstSmith, n: usize) SourceError!void { | ||
| 1521 | try a.pegToken(.pipe); | ||
| 1522 | if (a.smith.value(bool)) { | ||
| 1523 | try a.pegToken(.asterisk); | ||
| 1524 | } | ||
| 1525 | try a.pegIdentifier(); | ||
| 1526 | |||
| 1527 | for (1..n) |_| { | ||
| 1528 | try a.pegToken(.comma); | ||
| 1529 | if (a.smith.value(bool)) { | ||
| 1530 | try a.pegToken(.asterisk); | ||
| 1531 | } | ||
| 1532 | try a.pegIdentifier(); | ||
| 1533 | } | ||
| 1534 | |||
| 1535 | if (a.smith.value(bool)) { | ||
| 1536 | try a.pegToken(.comma); | ||
| 1537 | } | ||
| 1538 | try a.pegToken(.pipe); | ||
| 1539 | } | ||
| 1540 | |||
| 1541 | /// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? SingleAssignExpr | ||
| 1542 | fn pegSwitchProng(a: *AstSmith) SourceError!void { | ||
| 1543 | if (a.smith.value(bool)) { | ||
| 1544 | try a.pegToken(.keyword_inline); | ||
| 1545 | } | ||
| 1546 | try a.pegSwitchCase(); | ||
| 1547 | try a.pegToken(.equal_angle_bracket_right); | ||
| 1548 | if (a.smith.value(bool)) { | ||
| 1549 | try a.pegPtrIndexPayload(); | ||
| 1550 | } | ||
| 1551 | try a.pegSingleAssignExpr(); | ||
| 1552 | } | ||
| 1553 | |||
| 1554 | /// SwitchCase | ||
| 1555 | /// <- SwitchItem (COMMA SwitchItem)* COMMA? | ||
| 1556 | /// / KEYWORD_else | ||
| 1557 | fn pegSwitchCase(a: *AstSmith) SourceError!void { | ||
| 1558 | if (a.smith.value(bool)) { | ||
| 1559 | try a.pegSwitchItem(); | ||
| 1560 | while (!a.smithListItemEos()) { | ||
| 1561 | try a.pegToken(.comma); | ||
| 1562 | try a.pegSwitchItem(); | ||
| 1563 | } | ||
| 1564 | if (a.smith.value(bool)) { | ||
| 1565 | try a.pegToken(.comma); | ||
| 1566 | } | ||
| 1567 | } else { | ||
| 1568 | try a.pegToken(.keyword_else); | ||
| 1569 | } | ||
| 1570 | } | ||
| 1571 | |||
| 1572 | /// SwitchItem <- Expr (DOT3 Expr)? | ||
| 1573 | fn pegSwitchItem(a: *AstSmith) SourceError!void { | ||
| 1574 | try a.pegExpr(); | ||
| 1575 | if (a.smith.value(bool)) { | ||
| 1576 | try a.pegToken(.ellipsis3); | ||
| 1577 | try a.pegExpr(); | ||
| 1578 | } | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | /// ForArgumentsList <- ForItem (COMMA ForItem)* COMMA? | ||
| 1582 | fn pegForArgumentsList(a: *AstSmith) SourceError!usize { | ||
| 1583 | try a.pegForItem(); | ||
| 1584 | var n: usize = 1; | ||
| 1585 | while (!a.smithListItemEos()) { | ||
| 1586 | try a.pegToken(.comma); | ||
| 1587 | try a.pegForItem(); | ||
| 1588 | n += 1; | ||
| 1589 | } | ||
| 1590 | if (a.smith.value(bool)) { | ||
| 1591 | try a.pegToken(.comma); | ||
| 1592 | } | ||
| 1593 | return n; | ||
| 1594 | } | ||
| 1595 | |||
| 1596 | /// ForItem <- Expr (DOT2 Expr?)? | ||
| 1597 | fn pegForItem(a: *AstSmith) SourceError!void { | ||
| 1598 | try a.pegExpr(); | ||
| 1599 | const components = a.smith.valueRangeAtMost(u2, 0, 2); | ||
| 1600 | if (components >= 1) try a.pegToken(.ellipsis2); | ||
| 1601 | if (components >= 2) try a.pegExpr(); | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | /// AssignOp | ||
| 1605 | /// <- ASTERISKEQUAL | ||
| 1606 | /// / ASTERISKPIPEEQUAL | ||
| 1607 | /// / SLASHEQUAL | ||
| 1608 | /// / PERCENTEQUAL | ||
| 1609 | /// / PLUSEQUAL | ||
| 1610 | /// / PLUSPIPEEQUAL | ||
| 1611 | /// / MINUSEQUAL | ||
| 1612 | /// / MINUSPIPEEQUAL | ||
| 1613 | /// / LARROW2EQUAL | ||
| 1614 | /// / LARROW2PIPEEQUAL | ||
| 1615 | /// / RARROW2EQUAL | ||
| 1616 | /// / AMPERSANDEQUAL | ||
| 1617 | /// / CARETEQUAL | ||
| 1618 | /// / PIPEEQUAL | ||
| 1619 | /// / ASTERISKPERCENTEQUAL | ||
| 1620 | /// / PLUSPERCENTEQUAL | ||
| 1621 | /// / MINUSPERCENTEQUAL | ||
| 1622 | /// / EQUAL | ||
| 1623 | fn pegAssignOp(a: *AstSmith) SourceError!void { | ||
| 1624 | const tags = [_]Token.Tag{ | ||
| 1625 | .asterisk_equal, | ||
| 1626 | .asterisk_pipe_equal, | ||
| 1627 | .slash_equal, | ||
| 1628 | .percent_equal, | ||
| 1629 | .plus_equal, | ||
| 1630 | .plus_pipe_equal, | ||
| 1631 | .minus_equal, | ||
| 1632 | .minus_pipe_equal, | ||
| 1633 | .angle_bracket_angle_bracket_left_equal, | ||
| 1634 | .angle_bracket_angle_bracket_left_pipe_equal, | ||
| 1635 | .angle_bracket_angle_bracket_right_equal, | ||
| 1636 | .ampersand_equal, | ||
| 1637 | .caret_equal, | ||
| 1638 | .pipe_equal, | ||
| 1639 | .asterisk_percent_equal, | ||
| 1640 | .plus_percent_equal, | ||
| 1641 | .minus_percent_equal, | ||
| 1642 | .equal, | ||
| 1643 | }; | ||
| 1644 | try a.pegToken(tags[a.smith.index(tags.len)]); | ||
| 1645 | } | ||
| 1646 | |||
| 1647 | /// CompareOp | ||
| 1648 | /// <- EQUALEQUAL | ||
| 1649 | /// / EXCLAMATIONMARKEQUAL | ||
| 1650 | /// / LARROW | ||
| 1651 | /// / RARROW | ||
| 1652 | /// / LARROWEQUAL | ||
| 1653 | /// / RARROWEQUAL | ||
| 1654 | fn pegCompareOp(a: *AstSmith) SourceError!void { | ||
| 1655 | const tags = [_]Token.Tag{ | ||
| 1656 | .equal_equal, | ||
| 1657 | .bang_equal, | ||
| 1658 | .angle_bracket_left, | ||
| 1659 | .angle_bracket_right, | ||
| 1660 | .angle_bracket_left_equal, | ||
| 1661 | .angle_bracket_right_equal, | ||
| 1662 | }; | ||
| 1663 | try a.pegTokenWhitespaceAround(tags[a.smith.index(tags.len)]); | ||
| 1664 | } | ||
| 1665 | |||
| 1666 | /// BitwiseOp | ||
| 1667 | /// <- AMPERSAND | ||
| 1668 | /// / CARET | ||
| 1669 | /// / PIPE | ||
| 1670 | /// / KEYWORD_orelse | ||
| 1671 | /// / KEYWORD_catch Payload? | ||
| 1672 | fn pegBitwiseOp(a: *AstSmith) SourceError!void { | ||
| 1673 | const tags = [_]Token.Tag{ | ||
| 1674 | .ampersand, | ||
| 1675 | .caret, | ||
| 1676 | .pipe, | ||
| 1677 | .keyword_orelse, | ||
| 1678 | .keyword_catch, | ||
| 1679 | }; | ||
| 1680 | const tag = tags[a.smith.index(tags.len)]; | ||
| 1681 | try a.pegTokenWhitespaceAround(tag); | ||
| 1682 | if (tag == .keyword_catch and a.smith.value(bool)) { | ||
| 1683 | try a.pegPayload(); | ||
| 1684 | } | ||
| 1685 | } | ||
| 1686 | |||
| 1687 | /// BitShiftOp | ||
| 1688 | /// <- LARROW2 | ||
| 1689 | /// / RARROW2 | ||
| 1690 | /// / LARROW2PIPE | ||
| 1691 | fn pegBitShiftOp(a: *AstSmith) SourceError!void { | ||
| 1692 | const tags = [_]Token.Tag{ | ||
| 1693 | .angle_bracket_angle_bracket_left, | ||
| 1694 | .angle_bracket_angle_bracket_right, | ||
| 1695 | .angle_bracket_angle_bracket_left_pipe, | ||
| 1696 | }; | ||
| 1697 | try a.pegTokenWhitespaceAround(tags[a.smith.index(tags.len)]); | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | /// AdditionOp | ||
| 1701 | /// <- PLUS | ||
| 1702 | /// / MINUS | ||
| 1703 | /// / PLUS2 | ||
| 1704 | /// / PLUSPERCENT | ||
| 1705 | /// / MINUSPERCENT | ||
| 1706 | /// / PLUSPIPE | ||
| 1707 | /// / MINUSPIPE | ||
| 1708 | fn pegAdditionOp(a: *AstSmith) SourceError!void { | ||
| 1709 | const tags = [_]Token.Tag{ | ||
| 1710 | .plus, | ||
| 1711 | .minus, | ||
| 1712 | .plus_plus, | ||
| 1713 | .plus_percent, | ||
| 1714 | .minus_percent, | ||
| 1715 | .plus_pipe, | ||
| 1716 | .minus_pipe, | ||
| 1717 | }; | ||
| 1718 | try a.pegTokenWhitespaceAround(tags[a.smith.index(tags.len)]); | ||
| 1719 | } | ||
| 1720 | |||
| 1721 | /// MultiplyOp | ||
| 1722 | /// <- PIPE2 | ||
| 1723 | /// / ASTERISK | ||
| 1724 | /// / SLASH | ||
| 1725 | /// / PERCENT | ||
| 1726 | /// / ASTERISK2 | ||
| 1727 | /// / ASTERISKPERCENT | ||
| 1728 | /// / ASTERISKPIPE | ||
| 1729 | fn pegMultiplyOp(a: *AstSmith) SourceError!void { | ||
| 1730 | const tags = [_]Token.Tag{ | ||
| 1731 | .asterisk, | ||
| 1732 | .asterisk_asterisk, | ||
| 1733 | .pipe_pipe, | ||
| 1734 | .slash, | ||
| 1735 | .percent, | ||
| 1736 | .asterisk_percent, | ||
| 1737 | .asterisk_pipe, | ||
| 1738 | }; | ||
| 1739 | const start = @as(u8, 2) * @intFromBool(a.not_token == .asterisk); | ||
| 1740 | try a.pegTokenWhitespaceAround(tags[a.smith.valueRangeLessThan(u8, start, tags.len)]); | ||
| 1741 | } | ||
| 1742 | |||
| 1743 | /// PrefixOp | ||
| 1744 | /// <- EXCLAMATIONMARK | ||
| 1745 | /// / MINUS | ||
| 1746 | /// / TILDE | ||
| 1747 | /// / MINUSPERCENT | ||
| 1748 | /// / AMPERSAND | ||
| 1749 | /// / KEYWORD_try | ||
| 1750 | fn pegPrefixOp(a: *AstSmith) SourceError!void { | ||
| 1751 | const tags = [_]Token.Tag{ | ||
| 1752 | .bang, | ||
| 1753 | .minus, | ||
| 1754 | .tilde, | ||
| 1755 | .minus_percent, | ||
| 1756 | .ampersand, | ||
| 1757 | .keyword_try, | ||
| 1758 | }; | ||
| 1759 | try a.pegToken(tags[a.smith.index(tags.len)]); | ||
| 1760 | } | ||
| 1761 | |||
| 1762 | /// PrefixTypeOp | ||
| 1763 | /// <- QUESTIONMARK | ||
| 1764 | /// / KEYWORD_anyframe MINUSRARROW | ||
| 1765 | /// / (ManyPtrTypeStart / SliceTypeStart) KEYWORD_allowzero? ByteAlign? AddrSpace? | ||
| 1766 | /// KEYWORD_const? KEYWORD_volatile? | ||
| 1767 | /// / SinglePtrTypeStart KEYWORD_allowzero? BitAlign? AddrSpace? | ||
| 1768 | /// KEYWORD_const? KEYWORD_volatile? | ||
| 1769 | /// / ArrayTypeStart | ||
| 1770 | fn pegPrefixTypeOp(a: *AstSmith) SourceError!void { | ||
| 1771 | switch (a.smith.value(enum { | ||
| 1772 | optional, | ||
| 1773 | anyframe_arrow, | ||
| 1774 | array, | ||
| 1775 | single_pointer, | ||
| 1776 | many_pointer, | ||
| 1777 | slice, | ||
| 1778 | })) { | ||
| 1779 | .optional => try a.pegToken(.question_mark), | ||
| 1780 | .anyframe_arrow => { | ||
| 1781 | try a.pegToken(.keyword_anyframe); | ||
| 1782 | try a.pegToken(.arrow); | ||
| 1783 | }, | ||
| 1784 | .array => try a.pegArrayTypeStart(), | ||
| 1785 | .single_pointer, .many_pointer, .slice => |kind| { | ||
| 1786 | const is_single = kind == .single_pointer and a.not_token != .asterisk; | ||
| 1787 | if (is_single) { | ||
| 1788 | try a.pegSinglePtrTypeStart(); | ||
| 1789 | } else if (kind == .many_pointer) { | ||
| 1790 | try a.pegManyPtrTypeStart(); | ||
| 1791 | } else { | ||
| 1792 | try a.pegSliceTypeStart(); | ||
| 1793 | } | ||
| 1794 | |||
| 1795 | if (a.smith.value(bool)) { | ||
| 1796 | try a.pegToken(.keyword_allowzero); | ||
| 1797 | } | ||
| 1798 | if (a.smith.value(bool)) { | ||
| 1799 | if (is_single) { | ||
| 1800 | try a.pegBitAlign(); | ||
| 1801 | } else { | ||
| 1802 | try a.pegByteAlign(); | ||
| 1803 | } | ||
| 1804 | } | ||
| 1805 | if (a.smith.value(bool)) { | ||
| 1806 | try a.pegAddrSpace(); | ||
| 1807 | } | ||
| 1808 | if (a.smith.value(bool)) { | ||
| 1809 | try a.pegToken(.keyword_const); | ||
| 1810 | } | ||
| 1811 | if (a.smith.value(bool)) { | ||
| 1812 | try a.pegToken(.keyword_volatile); | ||
| 1813 | } | ||
| 1814 | }, | ||
| 1815 | } | ||
| 1816 | } | ||
| 1817 | |||
| 1818 | /// SuffixOp | ||
| 1819 | /// <- LBRACKET Expr (DOT2 (Expr? (COLON Expr)?)?)? RBRACKET | ||
| 1820 | /// / DOT IDENTIFIER | ||
| 1821 | /// / DOTASTERISK | ||
| 1822 | /// / DOTQUESTIONMARK | ||
| 1823 | fn pegSuffixOp(a: *AstSmith) SourceError!void { | ||
| 1824 | switch (a.smith.value(enum { slice, field, deref, unwrap })) { | ||
| 1825 | .slice => { | ||
| 1826 | try a.pegToken(.l_bracket); | ||
| 1827 | try a.pegExpr(); | ||
| 1828 | |||
| 1829 | const components = a.smith.value(u2); | ||
| 1830 | if (components >= 1) try a.pegToken(.ellipsis2); | ||
| 1831 | if (components >= 2) try a.pegExpr(); | ||
| 1832 | if (components >= 3) { | ||
| 1833 | try a.pegToken(.colon); | ||
| 1834 | try a.pegExpr(); | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | try a.pegToken(.r_bracket); | ||
| 1838 | }, | ||
| 1839 | .field => { | ||
| 1840 | try a.pegToken(.period); | ||
| 1841 | try a.pegIdentifier(); | ||
| 1842 | }, | ||
| 1843 | .deref => try a.pegToken(.period_asterisk), | ||
| 1844 | .unwrap => { | ||
| 1845 | try a.pegToken(.period); | ||
| 1846 | try a.pegToken(.question_mark); | ||
| 1847 | }, | ||
| 1848 | } | ||
| 1849 | } | ||
| 1850 | |||
| 1851 | /// FnCallArguments <- LPAREN ExprList RPAREN | ||
| 1852 | fn pegFnCallArguments(a: *AstSmith) SourceError!void { | ||
| 1853 | try a.pegToken(.l_paren); | ||
| 1854 | try a.pegExprList(); | ||
| 1855 | try a.pegToken(.r_paren); | ||
| 1856 | } | ||
| 1857 | |||
| 1858 | /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET | ||
| 1859 | fn pegSliceTypeStart(a: *AstSmith) SourceError!void { | ||
| 1860 | try a.pegToken(.l_bracket); | ||
| 1861 | if (a.smith.value(bool)) { | ||
| 1862 | try a.pegToken(.colon); | ||
| 1863 | try a.pegExpr(); | ||
| 1864 | } | ||
| 1865 | try a.pegToken(.r_bracket); | ||
| 1866 | } | ||
| 1867 | |||
| 1868 | /// SinglePtrTypeStart <- ASTERISK / ASTERISK2 | ||
| 1869 | fn pegSinglePtrTypeStart(a: *AstSmith) SourceError!void { | ||
| 1870 | try a.pegToken(if (!a.smith.value(bool)) .asterisk else .asterisk_asterisk); | ||
| 1871 | } | ||
| 1872 | |||
| 1873 | /// ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET | ||
| 1874 | fn pegManyPtrTypeStart(a: *AstSmith) SourceError!void { | ||
| 1875 | try a.pegToken(.l_bracket); | ||
| 1876 | try a.pegToken(.asterisk); | ||
| 1877 | switch (a.smith.value(enum { many, many_c, many_sentinel })) { | ||
| 1878 | .many => {}, | ||
| 1879 | .many_c => { | ||
| 1880 | // No need for `preservePegEndOfWord` because the previous token is an asterisk | ||
| 1881 | try a.addTokenTag(.identifier); | ||
| 1882 | try a.addSourceByte('c'); | ||
| 1883 | }, | ||
| 1884 | .many_sentinel => { | ||
| 1885 | try a.pegToken(.colon); | ||
| 1886 | try a.pegExpr(); | ||
| 1887 | }, | ||
| 1888 | } | ||
| 1889 | try a.pegToken(.r_bracket); | ||
| 1890 | } | ||
| 1891 | |||
| 1892 | /// ArrayTypeStart <- LBRACKET !(ASTERISK / ASTERISK2) Expr (COLON Expr)? RBRACKET | ||
| 1893 | fn pegArrayTypeStart(a: *AstSmith) SourceError!void { | ||
| 1894 | try a.pegToken(.l_bracket); | ||
| 1895 | a.not_token = .asterisk; | ||
| 1896 | try a.pegExpr(); | ||
| 1897 | if (a.smith.value(bool)) { | ||
| 1898 | try a.pegToken(.colon); | ||
| 1899 | try a.pegExpr(); | ||
| 1900 | } | ||
| 1901 | try a.pegToken(.r_bracket); | ||
| 1902 | } | ||
| 1903 | |||
| 1904 | /// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE | ||
| 1905 | fn pegContainerDeclAuto(a: *AstSmith) SourceError!void { | ||
| 1906 | try a.pegContainerDeclType(); | ||
| 1907 | try a.pegToken(.l_brace); | ||
| 1908 | try a.pegContainerMembers(); | ||
| 1909 | try a.pegToken(.r_brace); | ||
| 1910 | } | ||
| 1911 | |||
| 1912 | /// ContainerDeclType | ||
| 1913 | /// <- KEYWORD_struct (LPAREN Expr RPAREN)? | ||
| 1914 | /// / KEYWORD_opaque | ||
| 1915 | /// / KEYWORD_enum (LPAREN Expr RPAREN)? | ||
| 1916 | /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / !KEYWORD_enum Expr) RPAREN)? | ||
| 1917 | fn pegContainerDeclType(a: *AstSmith) SourceError!void { | ||
| 1918 | switch (a.smith.value(enum { @"struct", @"opaque", @"enum", @"union" })) { | ||
| 1919 | .@"struct", .@"enum" => |c| { | ||
| 1920 | const is_struct = c == .@"struct" or a.not_token == .keyword_enum; | ||
| 1921 | try a.pegToken(if (is_struct) .keyword_struct else .keyword_enum); | ||
| 1922 | if (a.smith.value(bool)) { | ||
| 1923 | try a.pegToken(.l_paren); | ||
| 1924 | try a.pegExpr(); | ||
| 1925 | try a.pegToken(.r_paren); | ||
| 1926 | } | ||
| 1927 | }, | ||
| 1928 | .@"opaque" => try a.pegToken(.keyword_opaque), | ||
| 1929 | .@"union" => { | ||
| 1930 | try a.pegToken(.keyword_union); | ||
| 1931 | switch (a.smith.value(enum { no_tag, expr_tag, enum_tag, enum_expr_tag })) { | ||
| 1932 | .no_tag => {}, | ||
| 1933 | .expr_tag => { | ||
| 1934 | try a.pegToken(.l_paren); | ||
| 1935 | a.not_token = .keyword_enum; | ||
| 1936 | try a.pegExpr(); | ||
| 1937 | try a.pegToken(.r_paren); | ||
| 1938 | }, | ||
| 1939 | .enum_tag => { | ||
| 1940 | try a.pegToken(.l_paren); | ||
| 1941 | try a.pegToken(.keyword_enum); | ||
| 1942 | try a.pegToken(.r_paren); | ||
| 1943 | }, | ||
| 1944 | .enum_expr_tag => { | ||
| 1945 | try a.pegToken(.l_paren); | ||
| 1946 | try a.pegToken(.keyword_enum); | ||
| 1947 | try a.pegToken(.l_paren); | ||
| 1948 | try a.pegExpr(); | ||
| 1949 | try a.pegToken(.r_paren); | ||
| 1950 | try a.pegToken(.r_paren); | ||
| 1951 | }, | ||
| 1952 | } | ||
| 1953 | }, | ||
| 1954 | } | ||
| 1955 | } | ||
| 1956 | |||
| 1957 | /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN | ||
| 1958 | fn pegByteAlign(a: *AstSmith) SourceError!void { | ||
| 1959 | try a.pegToken(.keyword_align); | ||
| 1960 | try a.pegToken(.l_paren); | ||
| 1961 | try a.pegExpr(); | ||
| 1962 | try a.pegToken(.r_paren); | ||
| 1963 | } | ||
| 1964 | |||
| 1965 | /// BitAlign <- KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN | ||
| 1966 | fn pegBitAlign(a: *AstSmith) SourceError!void { | ||
| 1967 | try a.pegToken(.keyword_align); | ||
| 1968 | try a.pegToken(.l_paren); | ||
| 1969 | try a.pegExpr(); | ||
| 1970 | if (a.smith.value(bool)) { | ||
| 1971 | try a.pegToken(.colon); | ||
| 1972 | try a.pegExpr(); | ||
| 1973 | try a.pegToken(.colon); | ||
| 1974 | try a.pegExpr(); | ||
| 1975 | } | ||
| 1976 | try a.pegToken(.r_paren); | ||
| 1977 | } | ||
| 1978 | |||
| 1979 | /// IdentifierList <- (doc_comment? IDENTIFIER COMMA)* (doc_comment? IDENTIFIER)? | ||
| 1980 | fn pegIdentifierList(a: *AstSmith) SourceError!void { | ||
| 1981 | while (!a.smith.eos()) { | ||
| 1982 | try a.pegMaybeDocComment(); | ||
| 1983 | try a.pegIdentifier(); | ||
| 1984 | try a.pegToken(.comma); | ||
| 1985 | } | ||
| 1986 | if (a.smith.value(bool)) { | ||
| 1987 | try a.pegMaybeDocComment(); | ||
| 1988 | try a.pegIdentifier(); | ||
| 1989 | } | ||
| 1990 | } | ||
| 1991 | |||
| 1992 | /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng? | ||
| 1993 | fn pegSwitchProngList(a: *AstSmith) SourceError!void { | ||
| 1994 | while (!a.smithListItemEos()) { | ||
| 1995 | try a.pegSwitchProng(); | ||
| 1996 | try a.pegToken(.comma); | ||
| 1997 | } | ||
| 1998 | if (a.smithListItemBool()) { | ||
| 1999 | try a.pegSwitchProng(); | ||
| 2000 | } | ||
| 2001 | } | ||
| 2002 | |||
| 2003 | /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem? | ||
| 2004 | fn pegAsmOutputList(a: *AstSmith) SourceError!void { | ||
| 2005 | while (!a.smithListItemEos()) { | ||
| 2006 | try a.pegAsmOutputItem(); | ||
| 2007 | try a.pegToken(.comma); | ||
| 2008 | } | ||
| 2009 | if (a.smithListItemBool()) { | ||
| 2010 | try a.pegAsmOutputItem(); | ||
| 2011 | } | ||
| 2012 | } | ||
| 2013 | |||
| 2014 | /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem? | ||
| 2015 | fn pegAsmInputList(a: *AstSmith) SourceError!void { | ||
| 2016 | while (!a.smithListItemEos()) { | ||
| 2017 | try a.pegAsmInputItem(); | ||
| 2018 | try a.pegToken(.comma); | ||
| 2019 | } | ||
| 2020 | if (a.smithListItemBool()) { | ||
| 2021 | try a.pegAsmInputItem(); | ||
| 2022 | } | ||
| 2023 | } | ||
| 2024 | |||
| 2025 | /// ParamDeclList <- (ParamDecl COMMA)* (ParamDecl / DOT3 COMMA?)? | ||
| 2026 | fn pegParamDeclList(a: *AstSmith) SourceError!void { | ||
| 2027 | while (!a.smithListItemEos()) { | ||
| 2028 | try a.pegParamDecl(); | ||
| 2029 | try a.pegToken(.comma); | ||
| 2030 | } | ||
| 2031 | const Final = enum { none, dot3, dot3_comma, param }; | ||
| 2032 | switch (a.smith.valueWeighted(Final, &.{ | ||
| 2033 | .rangeLessThan(Final, .none, .param, 2), | ||
| 2034 | .value(Final, .param, 1), | ||
| 2035 | })) { | ||
| 2036 | .none => {}, | ||
| 2037 | .dot3 => try a.pegToken(.ellipsis3), | ||
| 2038 | .dot3_comma => { | ||
| 2039 | try a.pegToken(.ellipsis3); | ||
| 2040 | try a.pegToken(.comma); | ||
| 2041 | }, | ||
| 2042 | .param => try a.pegParamDecl(), | ||
| 2043 | } | ||
| 2044 | } | ||
| 2045 | |||
| 2046 | /// ExprList <- (Expr COMMA)* Expr? | ||
| 2047 | fn pegExprList(a: *AstSmith) SourceError!void { | ||
| 2048 | while (!a.smithListItemEos()) { | ||
| 2049 | try a.pegExpr(); | ||
| 2050 | try a.pegToken(.comma); | ||
| 2051 | } | ||
| 2052 | if (a.smithListItemBool()) { | ||
| 2053 | try a.pegExpr(); | ||
| 2054 | } | ||
| 2055 | } | ||
| 2056 | |||
| 2057 | /// container_doc_comment <- ('//!' non_control_utf8* [ \n]* skip)+ | ||
| 2058 | fn pegContainerDocComment(a: *AstSmith) SourceError!void { | ||
| 2059 | while (true) { | ||
| 2060 | try a.addTokenTag(.container_doc_comment); | ||
| 2061 | try a.pegGenericLine("//!", .any); | ||
| 2062 | try a.pegSkip(); | ||
| 2063 | if (a.smith.eos()) break; | ||
| 2064 | } | ||
| 2065 | } | ||
| 2066 | |||
| 2067 | /// doc_comment? | ||
| 2068 | fn pegMaybeDocComment(a: *AstSmith) SourceError!void { | ||
| 2069 | // A specific hash is provided here since this function is likely to be inlined, | ||
| 2070 | // however having all doc comments with the same uid is beneficial. | ||
| 2071 | if (a.smith.boolWeightedWithHash(63, 1, 0x39b94392)) { | ||
| 2072 | try a.pegDocComment(); | ||
| 2073 | } | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | /// doc_comment <- ('///' non_control_utf8* [ \n]* skip)+ | ||
| 2077 | fn pegDocComment(a: *AstSmith) SourceError!void { | ||
| 2078 | if (a.source_len > 0 and a.source_buf[a.source_len - 1] != '\n') { | ||
| 2079 | try a.addSourceByte('\n'); | ||
| 2080 | } | ||
| 2081 | while (true) { | ||
| 2082 | try a.addTokenTag(.doc_comment); | ||
| 2083 | try a.pegGenericLine("///", .doc_comment); | ||
| 2084 | try a.pegSkip(); | ||
| 2085 | if (a.smith.eosWeightedSimple(1, 3)) break; | ||
| 2086 | } | ||
| 2087 | } | ||
| 2088 | |||
| 2089 | /// line_comment <- '//' ![!/] non_control_utf8* / '////' non_control_utf8* | ||
| 2090 | fn pegLineComment(a: *AstSmith) SourceError!void { | ||
| 2091 | return a.pegGenericLine("//", .line_comment); | ||
| 2092 | } | ||
| 2093 | |||
| 2094 | /// line_string <- '\\\\' non_control_utf8* [ \n]* | ||
| 2095 | fn pegLineString(a: *AstSmith) SourceError!void { | ||
| 2096 | try a.addTokenTag(.multiline_string_literal_line); | ||
| 2097 | return a.pegGenericLine("\\\\", .any); | ||
| 2098 | } | ||
| 2099 | |||
| 2100 | /// non_control_utf8 <- [\040-\377] | ||
| 2101 | /// | ||
| 2102 | /// Used for line, doc, and container comments as well as | ||
| 2103 | /// multiline string literal lines. | ||
| 2104 | fn pegGenericLine( | ||
| 2105 | a: *AstSmith, | ||
| 2106 | prefix: []const u8, | ||
| 2107 | /// Adds constraints to what the line contains | ||
| 2108 | prefix_kind: enum { any, line_comment, doc_comment }, | ||
| 2109 | ) SourceError!void { | ||
| 2110 | const cr = a.smith.value(bool); | ||
| 2111 | const newline_len = @intFromBool(cr) + @as(usize, 1); | ||
| 2112 | |||
| 2113 | try a.ensureSourceCapacity(prefix.len + newline_len); | ||
| 2114 | a.addSourceAssumeCapacity(prefix); | ||
| 2115 | |||
| 2116 | const line = a.variableChar(newline_len, 0, &.{ | ||
| 2117 | .rangeAtMost(u8, ' ', 0x7f - 1, 1), | ||
| 2118 | .rangeAtMost(u8, 0x7f + 1, 0xff, 1), | ||
| 2119 | }); | ||
| 2120 | if (line.len >= 1) switch (prefix_kind) { | ||
| 2121 | .any => {}, | ||
| 2122 | .line_comment => { | ||
| 2123 | // Convert doc comments to quadruple slashes when possible; | ||
| 2124 | // Otherwise, and for container doc comments, erase the '/' or '!' | ||
| 2125 | if (line[0] == '/' and line.len >= 2) { | ||
| 2126 | line[1] = '/'; | ||
| 2127 | } else if (line[0] == '/' or line[0] == '!') { | ||
| 2128 | line[0] = ' '; | ||
| 2129 | } | ||
| 2130 | }, | ||
| 2131 | .doc_comment => { | ||
| 2132 | // Avoid quadruple slashes | ||
| 2133 | if (line[0] == '/') { | ||
| 2134 | line[0] = ' '; | ||
| 2135 | } | ||
| 2136 | }, | ||
| 2137 | }; | ||
| 2138 | |||
| 2139 | if (cr) a.addSourceByteAssumeCapacity('\r'); | ||
| 2140 | a.addSourceByteAssumeCapacity('\n'); | ||
| 2141 | } | ||
| 2142 | |||
| 2143 | /// skip <- ([ \n] / line_comment)* | ||
| 2144 | fn pegSkip(a: *AstSmith) SourceError!void { | ||
| 2145 | if (a.smith.boolWeighted(63, 1)) { | ||
| 2146 | while (true) { | ||
| 2147 | const Kind = enum { | ||
| 2148 | space, | ||
| 2149 | line_break, | ||
| 2150 | cr_line_break, | ||
| 2151 | line_comment, | ||
| 2152 | line_comment_zig_fmt_off, | ||
| 2153 | line_comment_zig_fmt_on, | ||
| 2154 | }; | ||
| 2155 | |||
| 2156 | const weights = Smith.baselineWeights(Kind) ++ | ||
| 2157 | [_]Weight{.value(Kind, .space, 11)}; | ||
| 2158 | switch (a.smith.valueWeighted(Kind, weights)) { | ||
| 2159 | .space => try a.addSourceByte(' '), | ||
| 2160 | .line_break => try a.addSourceByte('\n'), | ||
| 2161 | .cr_line_break => try a.addSource("\r\n"), | ||
| 2162 | .line_comment => try a.pegLineComment(), | ||
| 2163 | .line_comment_zig_fmt_off => try a.addSource("//zig fmt: off\n"), | ||
| 2164 | .line_comment_zig_fmt_on => try a.addSource("//zig fmt: on\n"), | ||
| 2165 | } | ||
| 2166 | |||
| 2167 | if (a.smith.eos()) break; | ||
| 2168 | } | ||
| 2169 | } | ||
| 2170 | } | ||
| 2171 | |||
| 2172 | const bin_weights: []const Weight = &.{.rangeAtMost(u8, '0', '1', 1)}; | ||
| 2173 | const oct_weights: []const Weight = &.{.rangeAtMost(u8, '0', '7', 1)}; | ||
| 2174 | const dec_weights: []const Weight = &.{.rangeAtMost(u8, '0', '9', 1)}; | ||
| 2175 | const hex_weights: []const Weight = &.{ | ||
| 2176 | .rangeAtMost(u8, '0', '9', 1), | ||
| 2177 | .rangeAtMost(u8, 'a', 'f', 1), | ||
| 2178 | .rangeAtMost(u8, 'A', 'F', 1), | ||
| 2179 | }; | ||
| 2180 | |||
| 2181 | /// Asserts enough capacity for at `min + reserved_capacity` | ||
| 2182 | fn variableChar( | ||
| 2183 | a: *AstSmith, | ||
| 2184 | reserved_capacity: usize, | ||
| 2185 | min: usize, | ||
| 2186 | weights: []const Weight, | ||
| 2187 | ) []u8 { | ||
| 2188 | const capacity = a.sourceCapacity(); | ||
| 2189 | const max_out = capacity.len - reserved_capacity; | ||
| 2190 | |||
| 2191 | const len_weights: [3]Weight = .{ | ||
| 2192 | .rangeAtMost(u32, @intCast(min), @min(2, max_out), 32678), | ||
| 2193 | // For the below `.rangeAtMost` is not used because max may be less than min. | ||
| 2194 | // In this case, the weights are omitted. | ||
| 2195 | .{ .min = 3, .max = @min(16, max_out), .weight = 512 }, | ||
| 2196 | // Still allow much longer sequences to test parsing overflows | ||
| 2197 | .{ .min = 17, .max = @min(256, max_out), .weight = 1 }, | ||
| 2198 | }; | ||
| 2199 | const n_weights = @as(usize, 1) + @intFromBool(max_out >= 3) + @intFromBool(max_out >= 17); | ||
| 2200 | |||
| 2201 | const len = a.smith.sliceWeighted(capacity, len_weights[0..n_weights], weights); | ||
| 2202 | a.source_len += len; | ||
| 2203 | return capacity[0..len]; | ||
| 2204 | } | ||
| 2205 | |||
| 2206 | /// char_escape | ||
| 2207 | /// <- "\\x" hex hex | ||
| 2208 | /// / "\\u{" hex+ "}" | ||
| 2209 | /// / "\\" [nr\\t'"] | ||
| 2210 | /// char_char | ||
| 2211 | /// <- multibyte_utf8 | ||
| 2212 | /// / char_escape | ||
| 2213 | /// / ![\\'\n] non_control_ascii | ||
| 2214 | /// | ||
| 2215 | /// string_char | ||
| 2216 | /// <- multibyte_utf8 | ||
| 2217 | /// / char_escape | ||
| 2218 | /// / ![\\"\n] non_control_ascii | ||
| 2219 | fn pegChar(a: *AstSmith, quote: u8) SourceError!void { | ||
| 2220 | const Char = enum(u8) { | ||
| 2221 | ascii, | ||
| 2222 | unicode_2, | ||
| 2223 | unicode_3, | ||
| 2224 | unicode_4, | ||
| 2225 | hex_escape, | ||
| 2226 | unicode_escape, | ||
| 2227 | char_escape, | ||
| 2228 | }; | ||
| 2229 | const weights = Smith.baselineWeights(Char) ++ &[_]Weight{.value(Char, .ascii, 32)}; | ||
| 2230 | switch (a.smith.valueWeighted(Char, weights)) { | ||
| 2231 | .ascii => try a.addSourceByte(a.smith.valueWeighted(u8, &.{ | ||
| 2232 | .rangeAtMost(u8, ' ', quote - 1, 1), | ||
| 2233 | .rangeAtMost(u8, quote + 1, '\\' - 1, 1), | ||
| 2234 | .rangeAtMost(u8, '\\' + 1, 0x7e, 1), | ||
| 2235 | })), | ||
| 2236 | .unicode_2 => assert(2 == std.unicode.wtf8Encode( | ||
| 2237 | a.smith.valueRangeLessThan(u21, 0x80, 0x800), | ||
| 2238 | try a.addSourceAsSlice(2), | ||
| 2239 | ) catch unreachable), | ||
| 2240 | .unicode_3 => assert(3 == std.unicode.wtf8Encode( | ||
| 2241 | a.smith.valueRangeLessThan(u21, 0x800, 0x10000), | ||
| 2242 | try a.addSourceAsSlice(3), | ||
| 2243 | ) catch unreachable), | ||
| 2244 | .unicode_4 => assert(4 == std.unicode.wtf8Encode( | ||
| 2245 | a.smith.valueRangeLessThan(u21, 0x10000, 0x110000), | ||
| 2246 | try a.addSourceAsSlice(4), | ||
| 2247 | ) catch unreachable), | ||
| 2248 | .hex_escape => { | ||
| 2249 | try a.ensureSourceCapacity(4); | ||
| 2250 | a.addSourceAssumeCapacity("\\x"); | ||
| 2251 | a.smith.bytesWeighted(a.addSourceAsSliceAssumeCapacity(2), hex_weights); | ||
| 2252 | }, | ||
| 2253 | .unicode_escape => { | ||
| 2254 | try a.ensureSourceCapacity(5); | ||
| 2255 | a.addSourceAssumeCapacity("\\u{"); | ||
| 2256 | _ = a.variableChar(1, 1, hex_weights); | ||
| 2257 | a.addSourceByteAssumeCapacity('}'); | ||
| 2258 | }, | ||
| 2259 | .char_escape => { | ||
| 2260 | try a.ensureSourceCapacity(2); | ||
| 2261 | a.addSourceByteAssumeCapacity('\\'); | ||
| 2262 | a.addSourceByteAssumeCapacity(a.smith.valueWeighted(u8, &.{ | ||
| 2263 | .value(u8, 'n', 1), | ||
| 2264 | .value(u8, 'r', 1), | ||
| 2265 | .value(u8, 't', 1), | ||
| 2266 | .value(u8, '\\', 1), | ||
| 2267 | .value(u8, '\'', 1), | ||
| 2268 | .value(u8, '"', 1), | ||
| 2269 | })); | ||
| 2270 | }, | ||
| 2271 | } | ||
| 2272 | } | ||
| 2273 | |||
| 2274 | /// CHAR_LITERAL <- ['] char_char ['] skip | ||
| 2275 | fn pegCharLiteral(a: *AstSmith) SourceError!void { | ||
| 2276 | try a.addTokenTag(.char_literal); | ||
| 2277 | try a.addSourceByte('\''); | ||
| 2278 | try a.pegChar('\''); | ||
| 2279 | try a.addSourceByte('\''); | ||
| 2280 | try a.pegSkip(); | ||
| 2281 | } | ||
| 2282 | |||
| 2283 | ///FLOAT | ||
| 2284 | /// <- '0x' hex_int '.' hex_int ([pP] [-+]? dec_int)? skip | ||
| 2285 | /// / dec_int '.' dec_int ([eE] [-+]? dec_int)? skip | ||
| 2286 | /// / '0x' hex_int [pP] [-+]? dec_int skip | ||
| 2287 | /// / dec_int [eE] [-+]? dec_int skip | ||
| 2288 | fn pegFloat(a: *AstSmith) SourceError!void { | ||
| 2289 | try a.preservePegEndOfWord(); | ||
| 2290 | try a.addTokenTag(.number_literal); | ||
| 2291 | |||
| 2292 | const hex = a.smith.value(bool); | ||
| 2293 | const exp = a.smith.value(packed struct(u3) { | ||
| 2294 | kind: enum(u2) { none, no_sign, minus, plus }, | ||
| 2295 | upper: bool, | ||
| 2296 | }); | ||
| 2297 | const dot = exp.kind == .none or a.smith.value(bool); | ||
| 2298 | |||
| 2299 | var reserved: usize = @intFromBool(hex) * "0x".len + "0".len + @intFromBool(dot) * ".0".len + | ||
| 2300 | switch (exp.kind) { | ||
| 2301 | .none => 0, | ||
| 2302 | .no_sign => "e0".len, | ||
| 2303 | .minus => "e-0".len, | ||
| 2304 | .plus => "e+0".len, | ||
| 2305 | }; | ||
| 2306 | try a.ensureSourceCapacity(reserved); | ||
| 2307 | |||
| 2308 | if (hex) { | ||
| 2309 | reserved -= 2; | ||
| 2310 | a.addSourceAssumeCapacity("0x"); | ||
| 2311 | } | ||
| 2312 | const digits = if (hex) hex_weights else dec_weights; | ||
| 2313 | |||
| 2314 | reserved -= 1; | ||
| 2315 | _ = a.variableChar(reserved, 1, digits); | ||
| 2316 | |||
| 2317 | if (dot) { | ||
| 2318 | reserved -= 2; | ||
| 2319 | a.addSourceByteAssumeCapacity('.'); | ||
| 2320 | _ = a.variableChar(reserved, 1, digits); | ||
| 2321 | } | ||
| 2322 | |||
| 2323 | if (exp.kind != .none) { | ||
| 2324 | reserved -= 1; | ||
| 2325 | const case_diff = @as(u8, 'a' - 'A') * @intFromBool(exp.upper); | ||
| 2326 | a.addSourceByteAssumeCapacity(@as(u8, if (hex) 'p' else 'e') - case_diff); | ||
| 2327 | |||
| 2328 | if (exp.kind != .no_sign) { | ||
| 2329 | reserved -= 1; | ||
| 2330 | a.addSourceByteAssumeCapacity(if (exp.kind == .plus) '+' else '-'); | ||
| 2331 | } | ||
| 2332 | |||
| 2333 | reserved -= 1; | ||
| 2334 | assert(reserved == 0); | ||
| 2335 | _ = a.variableChar(reserved, 1, dec_weights); | ||
| 2336 | } | ||
| 2337 | } | ||
| 2338 | |||
| 2339 | ///INTEGER | ||
| 2340 | /// <- '0b' bin_int skip | ||
| 2341 | /// / '0o' oct_int skip | ||
| 2342 | /// / '0x' hex_int skip | ||
| 2343 | /// / dec_int skip | ||
| 2344 | fn pegInteger(a: *AstSmith) SourceError!void { | ||
| 2345 | try a.preservePegEndOfWord(); | ||
| 2346 | try a.addTokenTag(.number_literal); | ||
| 2347 | const Base = enum { bin, dec, oct, hex }; | ||
| 2348 | const base_weights: []const Weight = Smith.baselineWeights(Base) ++ | ||
| 2349 | &[_]Weight{ .value(Base, .dec, 6), .value(Base, .hex, 2) }; | ||
| 2350 | const digits, const prefix = switch (a.smith.valueWeighted(Base, base_weights)) { | ||
| 2351 | .bin => .{ bin_weights, "0b" }, | ||
| 2352 | .oct => .{ oct_weights, "0o" }, | ||
| 2353 | .dec => .{ dec_weights, "" }, | ||
| 2354 | .hex => .{ hex_weights, "0x" }, | ||
| 2355 | }; | ||
| 2356 | try a.ensureSourceCapacity(prefix.len + 1); | ||
| 2357 | if (prefix.len != 0) a.addSourceAssumeCapacity(prefix); | ||
| 2358 | _ = a.variableChar(0, 1, digits); | ||
| 2359 | } | ||
| 2360 | |||
| 2361 | /// Does not include 'skip'. Does not add any token tag. | ||
| 2362 | fn stringLiteralSingleInner(a: *AstSmith) SourceError!void { | ||
| 2363 | try a.addSourceByte('"'); | ||
| 2364 | while (!a.smith.eosWeightedSimple(3, 1)) { | ||
| 2365 | try a.pegChar('"'); | ||
| 2366 | } | ||
| 2367 | try a.addSourceByte('"'); | ||
| 2368 | } | ||
| 2369 | |||
| 2370 | /// STRINGLITERALSINGLE <- ["] string_char* ["] skip | ||
| 2371 | fn pegStringLiteralSingle(a: *AstSmith) SourceError!void { | ||
| 2372 | try a.addTokenTag(.string_literal); | ||
| 2373 | try a.stringLiteralSingleInner(); | ||
| 2374 | try a.pegSkip(); | ||
| 2375 | } | ||
| 2376 | |||
| 2377 | /// STRINGLITERAL | ||
| 2378 | /// <- STRINGLITERALSINGLE | ||
| 2379 | /// / (line_string skip)+ | ||
| 2380 | fn pegStringLiteral(a: *AstSmith) SourceError!void { | ||
| 2381 | if (a.smith.value(bool)) { | ||
| 2382 | try a.pegStringLiteralSingle(); | ||
| 2383 | } else { | ||
| 2384 | while (true) { | ||
| 2385 | try a.pegLineString(); | ||
| 2386 | try a.pegSkip(); | ||
| 2387 | if (a.smith.eos()) break; | ||
| 2388 | } | ||
| 2389 | } | ||
| 2390 | } | ||
| 2391 | |||
| 2392 | const alphanumeric_weights: [4]Weight = .{ | ||
| 2393 | .rangeAtMost(u8, '0', '9', 1), | ||
| 2394 | .rangeAtMost(u8, 'A', 'Z', 1), | ||
| 2395 | .rangeAtMost(u8, 'a', 'z', 1), | ||
| 2396 | .value(u8, '_', 1), | ||
| 2397 | }; | ||
| 2398 | |||
| 2399 | /// IDENTIFIER | ||
| 2400 | /// <- !keyword [A-Za-z_] [A-Za-z0-9_]* skip | ||
| 2401 | /// / '@' STRINGLITERALSINGLE | ||
| 2402 | fn pegIdentifier(a: *AstSmith) SourceError!void { | ||
| 2403 | const Kind = enum(u2) { underscore, regular_identifier, quoted_identifier, copy_identifier }; | ||
| 2404 | const kind_weights: [4]Weight = .{ | ||
| 2405 | .value(Kind, .underscore, 6), | ||
| 2406 | .value(Kind, .regular_identifier, 3), | ||
| 2407 | .value(Kind, .quoted_identifier, 1), | ||
| 2408 | .value(Kind, .copy_identifier, 6), | ||
| 2409 | }; | ||
| 2410 | const n_weights = @as(usize, kind_weights.len) - @intFromBool(a.prev_ids_len == 0); | ||
| 2411 | const kind = a.smith.valueWeighted(Kind, kind_weights[0..n_weights]); | ||
| 2412 | |||
| 2413 | switch (kind) { | ||
| 2414 | .underscore => { | ||
| 2415 | try a.preservePegEndOfWord(); | ||
| 2416 | try a.addTokenTag(.identifier); | ||
| 2417 | try a.addSourceByte('_'); | ||
| 2418 | }, | ||
| 2419 | .regular_identifier => { | ||
| 2420 | try a.preservePegEndOfWord(); | ||
| 2421 | try a.addTokenTag(.identifier); | ||
| 2422 | |||
| 2423 | const start = a.source_len; | ||
| 2424 | try a.addSourceByte(a.smith.valueWeighted(u8, alphanumeric_weights[1..])); | ||
| 2425 | _ = a.variableChar(0, 0, &alphanumeric_weights); | ||
| 2426 | |||
| 2427 | if (Token.getKeyword(a.source_buf[start..a.source_len]) != null) { | ||
| 2428 | a.source_buf[start] = '_'; // No keywords start with '_' | ||
| 2429 | } | ||
| 2430 | }, | ||
| 2431 | .quoted_identifier => { | ||
| 2432 | try a.addTokenTag(.identifier); | ||
| 2433 | try a.addSourceByte('@'); | ||
| 2434 | try a.stringLiteralSingleInner(); | ||
| 2435 | }, | ||
| 2436 | .copy_identifier => { | ||
| 2437 | const n_prev = @min(a.prev_ids_len, a.prev_ids_buf.len); | ||
| 2438 | const prev_i = a.smith.valueRangeLessThan(u16, 0, n_prev); | ||
| 2439 | const prev = a.prev_ids_buf[prev_i]; | ||
| 2440 | |||
| 2441 | if (a.source_buf[prev.start] != '@') try a.preservePegEndOfWord(); | ||
| 2442 | try a.addTokenTag(.identifier); | ||
| 2443 | try a.addSource(a.source_buf[prev.start..][0..prev.len]); | ||
| 2444 | }, | ||
| 2445 | } | ||
| 2446 | try a.pegSkip(); | ||
| 2447 | if (kind != .copy_identifier) { | ||
| 2448 | const start = a.token_start_buf[a.tokens_len - 1]; | ||
| 2449 | a.prev_ids_buf[a.prev_ids_len % a.prev_ids_buf.len] = .{ | ||
| 2450 | .start = @intCast(start), | ||
| 2451 | .len = @intCast(a.source_len - start), | ||
| 2452 | }; | ||
| 2453 | a.prev_ids_len += 1; | ||
| 2454 | } | ||
| 2455 | } | ||
| 2456 | |||
| 2457 | /// BUILTINIDENTIFIER <- '@'[A-Za-z_][A-Za-z0-9_]* skip | ||
| 2458 | fn pegBuiltinIdentifier(a: *AstSmith) SourceError!void { | ||
| 2459 | try a.addTokenTag(.builtin); | ||
| 2460 | if (a.smith.boolWeighted(1, 31)) { | ||
| 2461 | if (a.smith.boolWeighted(1, 8)) { | ||
| 2462 | // Pointer cast (reordable with zig fmt) | ||
| 2463 | const ids = [_][]const u8{ | ||
| 2464 | "@ptrCast", | ||
| 2465 | "@addrspaceCast", | ||
| 2466 | "@alignCast", | ||
| 2467 | "@constCast", | ||
| 2468 | "@volatileCast", | ||
| 2469 | }; | ||
| 2470 | try a.addSource(ids[a.smith.index(ids.len)]); | ||
| 2471 | } else { | ||
| 2472 | const ids = std.zig.BuiltinFn.list.keys(); | ||
| 2473 | try a.addSource(ids[a.smith.index(ids.len)]); | ||
| 2474 | } | ||
| 2475 | } else { | ||
| 2476 | try a.ensureSourceCapacity(2); | ||
| 2477 | a.addSourceByteAssumeCapacity('@'); | ||
| 2478 | a.addSourceByteAssumeCapacity(a.smith.valueWeighted(u8, alphanumeric_weights[1..])); | ||
| 2479 | _ = a.variableChar(0, 0, &alphanumeric_weights); | ||
| 2480 | } | ||
| 2481 | try a.pegSkip(); | ||
| 2482 | } | ||
| 2483 | |||
| 2484 | test AstSmith { | ||
| 2485 | try std.testing.fuzz({}, checkGenerated, .{}); | ||
| 2486 | } | ||
| 2487 | |||
| 2488 | fn checkGenerated(_: void, smith: *Smith) !void { | ||
| 2489 | var a: AstSmith = .init(smith); | ||
| 2490 | try a.generateSource(); | ||
| 2491 | |||
| 2492 | { // Check tokenization matches source | ||
| 2493 | errdefer a.logBadSource(null); | ||
| 2494 | |||
| 2495 | const token_tags = a.token_tag_buf[0..a.tokens_len]; | ||
| 2496 | const token_starts = a.token_start_buf[0..a.tokens_len]; | ||
| 2497 | try std.testing.expectEqual(Token.Tag.eof, token_tags[token_tags.len - 1]); | ||
| 2498 | |||
| 2499 | var tokenizer: std.zig.Tokenizer = .init(a.source()); | ||
| 2500 | for (token_tags, token_starts) |tag, start| { | ||
| 2501 | const tok = tokenizer.next(); | ||
| 2502 | try std.testing.expectEqual(tok.tag, tag); | ||
| 2503 | try std.testing.expectEqual(tok.loc.start, start); | ||
| 2504 | if (tag == .invalid) return error.InvalidToken; | ||
| 2505 | } | ||
| 2506 | } | ||
| 2507 | |||
| 2508 | var fba_buf: [1 << 18]u8 = undefined; | ||
| 2509 | var fba: std.heap.FixedBufferAllocator = .init(&fba_buf); | ||
| 2510 | const ast = std.zig.Ast.parseTokens(fba.allocator(), a.source(), a.tokens(), .zig) catch | ||
| 2511 | return error.SkipZigTest; | ||
| 2512 | |||
| 2513 | errdefer a.logBadSource(ast); | ||
| 2514 | try std.testing.expectEqual(0, ast.errors.len); | ||
| 2515 | } | ||
| 2516 | |||
| 2517 | fn logBadSource(a: *AstSmith, ast: ?std.zig.Ast) void { | ||
| 2518 | var buf: [256]u8 = undefined; | ||
| 2519 | const ls = std.debug.lockStderr(&buf); | ||
| 2520 | defer std.debug.unlockStderr(); | ||
| 2521 | a.logBadSourceInner(ls.terminal(), ast) catch {}; | ||
| 2522 | } | ||
| 2523 | |||
| 2524 | fn logBadSourceInner(a: *AstSmith, t: std.Io.Terminal, ast: ?std.zig.Ast) std.Io.Writer.Error!void { | ||
| 2525 | try a.logSourceInner(t); | ||
| 2526 | const w = t.writer; | ||
| 2527 | |||
| 2528 | if (ast) |bad_ast| { | ||
| 2529 | try w.writeAll("=== Parse Errors ===\n"); | ||
| 2530 | for (bad_ast.errors) |err| { | ||
| 2531 | const loc = bad_ast.tokenLocation(0, err.token); | ||
| 2532 | try w.print("{}:{}: ", .{ loc.line + 1, loc.column + 1 }); | ||
| 2533 | try bad_ast.renderError(err, w); | ||
| 2534 | try w.writeByte('\n'); | ||
| 2535 | } | ||
| 2536 | } else { | ||
| 2537 | t.setColor(.dim) catch {}; | ||
| 2538 | try w.writeAll("=== Tokens ===\n"); | ||
| 2539 | t.setColor(.reset) catch {}; | ||
| 2540 | for ( | ||
| 2541 | 0.., | ||
| 2542 | a.token_tag_buf[0..a.tokens_len], | ||
| 2543 | a.token_start_buf[0..a.tokens_len], | ||
| 2544 | ) |i, tag, start| { | ||
| 2545 | try w.print("#{} @{}: {t}\n", .{ i, start, tag }); | ||
| 2546 | } | ||
| 2547 | |||
| 2548 | t.setColor(.dim) catch {}; | ||
| 2549 | try w.writeAll("\n=== Expected Tokens ===\n"); | ||
| 2550 | t.setColor(.reset) catch {}; | ||
| 2551 | |||
| 2552 | var tokenizer: std.zig.Tokenizer = .init(a.source()); | ||
| 2553 | var i: usize = 0; | ||
| 2554 | while (true) { | ||
| 2555 | const tok = tokenizer.next(); | ||
| 2556 | try w.print("#{} @{}-{}: {t}\n", .{ i, tok.loc.start, tok.loc.end, tok.tag }); | ||
| 2557 | i += 1; | ||
| 2558 | if (tok.tag == .invalid or tok.tag == .eof) break; | ||
| 2559 | } | ||
| 2560 | } | ||
| 2561 | } | ||
| 2562 | |||
| 2563 | pub fn logSource(a: *AstSmith) void { | ||
| 2564 | var buf: [256]u8 = undefined; | ||
| 2565 | const ls = std.debug.lockStderr(&buf); | ||
| 2566 | defer std.debug.unlockStderr(); | ||
| 2567 | a.logSourceInner(ls.terminal()) catch {}; | ||
| 2568 | } | ||
| 2569 | |||
| 2570 | fn logSourceInner(a: *AstSmith, t: std.Io.Terminal) std.Io.Writer.Error!void { | ||
| 2571 | const w = t.writer; | ||
| 2572 | |||
| 2573 | t.setColor(.dim) catch {}; | ||
| 2574 | try w.writeAll("=== Source ===\n"); | ||
| 2575 | t.setColor(.reset) catch {}; | ||
| 2576 | |||
| 2577 | var line: usize = 1; | ||
| 2578 | try w.print("{: >5} ", .{line}); | ||
| 2579 | for (a.source()) |c| switch (c) { | ||
| 2580 | ' '...0x7e => try w.writeByte(c), | ||
| 2581 | '\n' => { | ||
| 2582 | line += 1; | ||
| 2583 | try w.print("\n{: >5} ", .{line}); | ||
| 2584 | }, | ||
| 2585 | '\r' => { | ||
| 2586 | t.setColor(.cyan) catch {}; | ||
| 2587 | try w.writeAll("\\r"); | ||
| 2588 | t.setColor(.reset) catch {}; | ||
| 2589 | }, | ||
| 2590 | '\t' => { | ||
| 2591 | t.setColor(.cyan) catch {}; | ||
| 2592 | try w.writeAll("\\t"); | ||
| 2593 | t.setColor(.reset) catch {}; | ||
| 2594 | }, | ||
| 2595 | else => { | ||
| 2596 | t.setColor(.cyan) catch {}; | ||
| 2597 | try w.print("\\x{x:0>2}", .{c}); | ||
| 2598 | t.setColor(.reset) catch {}; | ||
| 2599 | }, | ||
| 2600 | }; | ||
| 2601 | try w.writeByte('\n'); | ||
| 2602 | } | ||
lib/std/zig/Parse.zig+83-74| ... | @@ -257,7 +257,7 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members { | ... | @@ -257,7 +257,7 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members { |
| 257 | while (true) { | 257 | while (true) { |
| 258 | const doc_comment = try p.eatDocComments(); | 258 | const doc_comment = try p.eatDocComments(); |
| 259 | 259 | ||
| 260 | switch (p.tokenTag(p.tok_i)) { | 260 | sw: switch (p.tokenTag(p.tok_i)) { |
| 261 | .keyword_test => { | 261 | .keyword_test => { |
| 262 | if (doc_comment) |some| { | 262 | if (doc_comment) |some| { |
| 263 | try p.warnMsg(.{ .tag = .test_doc_comment, .token = some }); | 263 | try p.warnMsg(.{ .tag = .test_doc_comment, .token = some }); |
| ... | @@ -348,17 +348,7 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members { | ... | @@ -348,17 +348,7 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members { |
| 348 | p.findNextContainerMember(); | 348 | p.findNextContainerMember(); |
| 349 | }, | 349 | }, |
| 350 | }, | 350 | }, |
| 351 | .keyword_pub => { | 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 | }, | ||
| 362 | .keyword_const, | 352 | .keyword_const, |
| 363 | .keyword_var, | 353 | .keyword_var, |
| 364 | .keyword_threadlocal, | 354 | .keyword_threadlocal, |
| ... | @@ -367,7 +357,27 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members { | ... | @@ -367,7 +357,27 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members { |
| 367 | .keyword_inline, | 357 | .keyword_inline, |
| 368 | .keyword_noinline, | 358 | .keyword_noinline, |
| 369 | .keyword_fn, | 359 | .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); | ||
| 371 | const opt_top_level_decl = try p.expectTopLevelDeclRecoverable(); | 381 | const opt_top_level_decl = try p.expectTopLevelDeclRecoverable(); |
| 372 | if (opt_top_level_decl) |top_level_decl| { | 382 | if (opt_top_level_decl) |top_level_decl| { |
| 373 | if (field_state == .seen) { | 383 | if (field_state == .seen) { |
| ... | @@ -588,7 +598,8 @@ fn expectTestDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index { | ... | @@ -588,7 +598,8 @@ fn expectTestDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index { |
| 588 | } | 598 | } |
| 589 | 599 | ||
| 590 | /// Decl | 600 | /// 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 | ||
| 592 | /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl | 603 | /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl |
| 593 | fn expectTopLevelDecl(p: *Parse) !?Node.Index { | 604 | fn expectTopLevelDecl(p: *Parse) !?Node.Index { |
| 594 | const extern_export_inline_token = p.nextToken(); | 605 | const extern_export_inline_token = p.nextToken(); |
| ... | @@ -665,7 +676,7 @@ fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index { | ... | @@ -665,7 +676,7 @@ fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index { |
| 665 | }; | 676 | }; |
| 666 | } | 677 | } |
| 667 | 678 | ||
| 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 |
| 669 | fn parseFnProto(p: *Parse) !?Node.Index { | 680 | fn parseFnProto(p: *Parse) !?Node.Index { |
| 670 | const fn_token = p.eatToken(.keyword_fn) orelse return null; | 681 | const fn_token = p.eatToken(.keyword_fn) orelse return null; |
| 671 | 682 | ||
| ... | @@ -853,7 +864,7 @@ fn parseGlobalVarDecl(p: *Parse) !?Node.Index { | ... | @@ -853,7 +864,7 @@ fn parseGlobalVarDecl(p: *Parse) !?Node.Index { |
| 853 | return var_decl; | 864 | return var_decl; |
| 854 | } | 865 | } |
| 855 | 866 | ||
| 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)? |
| 857 | fn expectContainerField(p: *Parse) !Node.Index { | 868 | fn expectContainerField(p: *Parse) !Node.Index { |
| 858 | _ = p.eatToken(.keyword_comptime); | 869 | _ = p.eatToken(.keyword_comptime); |
| 859 | const main_token = p.tok_i; | 870 | const main_token = p.tok_i; |
| ... | @@ -895,16 +906,23 @@ fn expectContainerField(p: *Parse) !Node.Index { | ... | @@ -895,16 +906,23 @@ fn expectContainerField(p: *Parse) !Node.Index { |
| 895 | } | 906 | } |
| 896 | } | 907 | } |
| 897 | 908 | ||
| 898 | /// Statement | 909 | /// BlockStatement |
| 899 | /// <- KEYWORD_comptime ComptimeStatement | 910 | /// <- Statement |
| 900 | /// / KEYWORD_nosuspend BlockExprStatement | ||
| 901 | /// / KEYWORD_suspend BlockExprStatement | ||
| 902 | /// / KEYWORD_defer BlockExprStatement | 911 | /// / KEYWORD_defer BlockExprStatement |
| 903 | /// / KEYWORD_errdefer Payload? BlockExprStatement | 912 | /// / 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 | ||
| 905 | /// / LabeledStatement | 922 | /// / LabeledStatement |
| 906 | /// / VarDeclExprStatement | 923 | /// / KEYWORD_nosuspend BlockExprStatement |
| 907 | fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { | 924 | /// / KEYWORD_comptime BlockExpr |
| 925 | fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index { | ||
| 908 | if (p.eatToken(.keyword_comptime)) |comptime_token| { | 926 | if (p.eatToken(.keyword_comptime)) |comptime_token| { |
| 909 | const opt_block_expr = try p.parseBlockExpr(); | 927 | const opt_block_expr = try p.parseBlockExpr(); |
| 910 | if (opt_block_expr) |block_expr| { | 928 | if (opt_block_expr) |block_expr| { |
| ... | @@ -915,7 +933,7 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { | ... | @@ -915,7 +933,7 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { |
| 915 | }); | 933 | }); |
| 916 | } | 934 | } |
| 917 | 935 | ||
| 918 | if (allow_defer_var) { | 936 | if (is_block_level) { |
| 919 | return p.expectVarDeclExprStatement(comptime_token); | 937 | return p.expectVarDeclExprStatement(comptime_token); |
| 920 | } else { | 938 | } else { |
| 921 | const assign = try p.expectAssignExpr(); | 939 | const assign = try p.expectAssignExpr(); |
| ... | @@ -949,12 +967,12 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { | ... | @@ -949,12 +967,12 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { |
| 949 | .data = .{ .node = block_expr }, | 967 | .data = .{ .node = block_expr }, |
| 950 | }); | 968 | }); |
| 951 | }, | 969 | }, |
| 952 | .keyword_defer => if (allow_defer_var) return p.addNode(.{ | 970 | .keyword_defer => if (is_block_level) return p.addNode(.{ |
| 953 | .tag = .@"defer", | 971 | .tag = .@"defer", |
| 954 | .main_token = p.nextToken(), | 972 | .main_token = p.nextToken(), |
| 955 | .data = .{ .node = try p.expectBlockExprStatement() }, | 973 | .data = .{ .node = try p.expectBlockExprStatement() }, |
| 956 | }), | 974 | }), |
| 957 | .keyword_errdefer => if (allow_defer_var) return p.addNode(.{ | 975 | .keyword_errdefer => if (is_block_level) return p.addNode(.{ |
| 958 | .tag = .@"errdefer", | 976 | .tag = .@"errdefer", |
| 959 | .main_token = p.nextToken(), | 977 | .main_token = p.nextToken(), |
| 960 | .data = .{ .opt_token_and_node = .{ | 978 | .data = .{ .opt_token_and_node = .{ |
| ... | @@ -979,7 +997,7 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { | ... | @@ -979,7 +997,7 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { |
| 979 | 997 | ||
| 980 | if (try p.parseLabeledStatement()) |labeled_statement| return labeled_statement; | 998 | if (try p.parseLabeledStatement()) |labeled_statement| return labeled_statement; |
| 981 | 999 | ||
| 982 | if (allow_defer_var) { | 1000 | if (is_block_level) { |
| 983 | return p.expectVarDeclExprStatement(null); | 1001 | return p.expectVarDeclExprStatement(null); |
| 984 | } else { | 1002 | } else { |
| 985 | const assign = try p.expectAssignExpr(); | 1003 | const assign = try p.expectAssignExpr(); |
| ... | @@ -1007,8 +1025,10 @@ fn expectComptimeStatement(p: *Parse, comptime_token: TokenIndex) !Node.Index { | ... | @@ -1007,8 +1025,10 @@ fn expectComptimeStatement(p: *Parse, comptime_token: TokenIndex) !Node.Index { |
| 1007 | } | 1025 | } |
| 1008 | 1026 | ||
| 1009 | /// VarDeclExprStatement | 1027 | /// VarDeclExprStatement |
| 1010 | /// <- VarDeclProto (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON | 1028 | /// <- Expr |
| 1011 | /// / Expr (AssignOp Expr / (COMMA (VarDeclProto / Expr))+ EQUAL Expr)? SEMICOLON | 1029 | /// / VarAssignStatement |
| 1030 | /// | ||
| 1031 | /// VarAssignStatement <- (VarDeclProto / Expr) (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON | ||
| 1012 | fn expectVarDeclExprStatement(p: *Parse, comptime_token: ?TokenIndex) !Node.Index { | 1032 | fn expectVarDeclExprStatement(p: *Parse, comptime_token: ?TokenIndex) !Node.Index { |
| 1013 | const scratch_top = p.scratch.items.len; | 1033 | const scratch_top = p.scratch.items.len; |
| 1014 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | 1034 | defer p.scratch.shrinkRetainingCapacity(scratch_top); |
| ... | @@ -1140,7 +1160,7 @@ fn expectStatementRecoverable(p: *Parse) Error!?Node.Index { | ... | @@ -1140,7 +1160,7 @@ fn expectStatementRecoverable(p: *Parse) Error!?Node.Index { |
| 1140 | 1160 | ||
| 1141 | /// IfStatement | 1161 | /// IfStatement |
| 1142 | /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? | 1162 | /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? |
| 1143 | /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | 1163 | /// / IfPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) |
| 1144 | fn expectIfStatement(p: *Parse) !Node.Index { | 1164 | fn expectIfStatement(p: *Parse) !Node.Index { |
| 1145 | const if_token = p.assertToken(.keyword_if); | 1165 | const if_token = p.assertToken(.keyword_if); |
| 1146 | _ = try p.expectToken(.l_paren); | 1166 | _ = try p.expectToken(.l_paren); |
| ... | @@ -1235,8 +1255,8 @@ fn parseLoopStatement(p: *Parse) !?Node.Index { | ... | @@ -1235,8 +1255,8 @@ fn parseLoopStatement(p: *Parse) !?Node.Index { |
| 1235 | } | 1255 | } |
| 1236 | 1256 | ||
| 1237 | /// ForStatement | 1257 | /// ForStatement |
| 1238 | /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? | 1258 | /// <- ForPrefix BlockExpr ( KEYWORD_else Statement / !KEYWORD_else ) |
| 1239 | /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) | 1259 | /// / ForPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Statement ) |
| 1240 | fn parseForStatement(p: *Parse) !?Node.Index { | 1260 | fn parseForStatement(p: *Parse) !?Node.Index { |
| 1241 | const for_token = p.eatToken(.keyword_for) orelse return null; | 1261 | const for_token = p.eatToken(.keyword_for) orelse return null; |
| 1242 | 1262 | ||
| ... | @@ -1293,7 +1313,7 @@ fn parseForStatement(p: *Parse) !?Node.Index { | ... | @@ -1293,7 +1313,7 @@ fn parseForStatement(p: *Parse) !?Node.Index { |
| 1293 | /// | 1313 | /// |
| 1294 | /// WhileStatement | 1314 | /// WhileStatement |
| 1295 | /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? | 1315 | /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? |
| 1296 | /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | 1316 | /// / WhilePrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) |
| 1297 | fn parseWhileStatement(p: *Parse) !?Node.Index { | 1317 | fn parseWhileStatement(p: *Parse) !?Node.Index { |
| 1298 | const while_token = p.eatToken(.keyword_while) orelse return null; | 1318 | const while_token = p.eatToken(.keyword_while) orelse return null; |
| 1299 | _ = try p.expectToken(.l_paren); | 1319 | _ = try p.expectToken(.l_paren); |
| ... | @@ -1383,7 +1403,7 @@ fn parseWhileStatement(p: *Parse) !?Node.Index { | ... | @@ -1383,7 +1403,7 @@ fn parseWhileStatement(p: *Parse) !?Node.Index { |
| 1383 | 1403 | ||
| 1384 | /// BlockExprStatement | 1404 | /// BlockExprStatement |
| 1385 | /// <- BlockExpr | 1405 | /// <- BlockExpr |
| 1386 | /// / AssignExpr SEMICOLON | 1406 | /// / !BlockExpr AssignExpr SEMICOLON |
| 1387 | fn parseBlockExprStatement(p: *Parse) !?Node.Index { | 1407 | fn parseBlockExprStatement(p: *Parse) !?Node.Index { |
| 1388 | const block_expr = try p.parseBlockExpr(); | 1408 | const block_expr = try p.parseBlockExpr(); |
| 1389 | if (block_expr) |expr| return expr; | 1409 | if (block_expr) |expr| return expr; |
| ... | @@ -1685,18 +1705,20 @@ fn expectPrefixExpr(p: *Parse) Error!Node.Index { | ... | @@ -1685,18 +1705,20 @@ fn expectPrefixExpr(p: *Parse) Error!Node.Index { |
| 1685 | /// PrefixTypeOp | 1705 | /// PrefixTypeOp |
| 1686 | /// <- QUESTIONMARK | 1706 | /// <- QUESTIONMARK |
| 1687 | /// / KEYWORD_anyframe MINUSRARROW | 1707 | /// / 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? | ||
| 1689 | /// / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | 1710 | /// / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* |
| 1690 | /// / ArrayTypeStart | 1711 | /// / ArrayTypeStart |
| 1691 | /// | 1712 | /// |
| 1692 | /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET | 1713 | /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET |
| 1693 | /// | 1714 | /// |
| 1694 | /// PtrTypeStart | 1715 | /// SinglePtrTypeStart <- ASTERISK / ASTERISK2 |
| 1695 | /// <- ASTERISK | 1716 | /// |
| 1696 | /// / ASTERISK2 | 1717 | /// ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET |
| 1697 | /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET | 1718 | /// |
| 1719 | /// ArrayTypeStart <- LBRACKET Expr !(ASTERISK / ASTERISK2) (COLON Expr)? RBRACKET | ||
| 1698 | /// | 1720 | /// |
| 1699 | /// ArrayTypeStart <- LBRACKET Expr (COLON Expr)? RBRACKET | 1721 | /// BitAlign <- KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN |
| 1700 | fn parseTypeExpr(p: *Parse) Error!?Node.Index { | 1722 | fn parseTypeExpr(p: *Parse) Error!?Node.Index { |
| 1701 | switch (p.tokenTag(p.tok_i)) { | 1723 | switch (p.tokenTag(p.tok_i)) { |
| 1702 | .question_mark => return try p.addNode(.{ | 1724 | .question_mark => return try p.addNode(.{ |
| ... | @@ -1962,12 +1984,12 @@ fn expectTypeExpr(p: *Parse) Error!Node.Index { | ... | @@ -1962,12 +1984,12 @@ fn expectTypeExpr(p: *Parse) Error!Node.Index { |
| 1962 | /// PrimaryExpr | 1984 | /// PrimaryExpr |
| 1963 | /// <- AsmExpr | 1985 | /// <- AsmExpr |
| 1964 | /// / IfExpr | 1986 | /// / IfExpr |
| 1965 | /// / KEYWORD_break BreakLabel? Expr? | 1987 | /// / KEYWORD_break (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart) |
| 1966 | /// / KEYWORD_comptime Expr | 1988 | /// / KEYWORD_comptime Expr !ExprSuffix |
| 1967 | /// / KEYWORD_nosuspend Expr | 1989 | /// / KEYWORD_nosuspend Expr !ExprSuffix |
| 1968 | /// / KEYWORD_continue BreakLabel? Expr? | 1990 | /// / KEYWORD_continue (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart) |
| 1969 | /// / KEYWORD_resume Expr | 1991 | /// / KEYWORD_resume Expr !ExprSuffix |
| 1970 | /// / KEYWORD_return Expr? | 1992 | /// / KEYWORD_return (Expr !ExprSuffix / !SinglePtrTypeStart) |
| 1971 | /// / BlockLabel? LoopExpr | 1993 | /// / BlockLabel? LoopExpr |
| 1972 | /// / Block | 1994 | /// / Block |
| 1973 | /// / CurlySuffixExpr | 1995 | /// / CurlySuffixExpr |
| ... | @@ -2042,10 +2064,6 @@ fn parsePrimaryExpr(p: *Parse) !?Node.Index { | ... | @@ -2042,10 +2064,6 @@ fn parsePrimaryExpr(p: *Parse) !?Node.Index { |
| 2042 | p.tok_i += 2; | 2064 | p.tok_i += 2; |
| 2043 | return try p.parseWhileExpr(); | 2065 | return try p.parseWhileExpr(); |
| 2044 | }, | 2066 | }, |
| 2045 | .l_brace => { | ||
| 2046 | p.tok_i += 2; | ||
| 2047 | return try p.parseBlock(); | ||
| 2048 | }, | ||
| 2049 | else => return try p.parseCurlySuffixExpr(), | 2067 | else => return try p.parseCurlySuffixExpr(), |
| 2050 | } | 2068 | } |
| 2051 | } else { | 2069 | } else { |
| ... | @@ -2067,12 +2085,12 @@ fn parsePrimaryExpr(p: *Parse) !?Node.Index { | ... | @@ -2067,12 +2085,12 @@ fn parsePrimaryExpr(p: *Parse) !?Node.Index { |
| 2067 | } | 2085 | } |
| 2068 | } | 2086 | } |
| 2069 | 2087 | ||
| 2070 | /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? | 2088 | /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffix |
| 2071 | fn parseIfExpr(p: *Parse) !?Node.Index { | 2089 | fn parseIfExpr(p: *Parse) !?Node.Index { |
| 2072 | return try p.parseIf(expectExpr); | 2090 | return try p.parseIf(expectExpr); |
| 2073 | } | 2091 | } |
| 2074 | 2092 | ||
| 2075 | /// Block <- LBRACE Statement* RBRACE | 2093 | /// Block <- LBRACE BlockStatement* RBRACE |
| 2076 | fn parseBlock(p: *Parse) !?Node.Index { | 2094 | fn parseBlock(p: *Parse) !?Node.Index { |
| 2077 | const lbrace = p.eatToken(.l_brace) orelse return null; | 2095 | const lbrace = p.eatToken(.l_brace) orelse return null; |
| 2078 | const scratch_top = p.scratch.items.len; | 2096 | const scratch_top = p.scratch.items.len; |
| ... | @@ -2177,7 +2195,7 @@ fn forPrefix(p: *Parse) Error!usize { | ... | @@ -2177,7 +2195,7 @@ fn forPrefix(p: *Parse) Error!usize { |
| 2177 | 2195 | ||
| 2178 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | 2196 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? |
| 2179 | /// | 2197 | /// |
| 2180 | /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? | 2198 | /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? !ExprSuffi |
| 2181 | fn parseWhileExpr(p: *Parse) !?Node.Index { | 2199 | fn parseWhileExpr(p: *Parse) !?Node.Index { |
| 2182 | const while_token = p.eatToken(.keyword_while) orelse return null; | 2200 | const while_token = p.eatToken(.keyword_while) orelse return null; |
| 2183 | _ = try p.expectToken(.l_paren); | 2201 | _ = try p.expectToken(.l_paren); |
| ... | @@ -2409,10 +2427,10 @@ fn parseSuffixExpr(p: *Parse) !?Node.Index { | ... | @@ -2409,10 +2427,10 @@ fn parseSuffixExpr(p: *Parse) !?Node.Index { |
| 2409 | /// / FnProto | 2427 | /// / FnProto |
| 2410 | /// / GroupedExpr | 2428 | /// / GroupedExpr |
| 2411 | /// / LabeledTypeExpr | 2429 | /// / LabeledTypeExpr |
| 2412 | /// / IDENTIFIER | 2430 | /// / IDENTIFIER !(COLON LabelableExpr) |
| 2413 | /// / IfTypeExpr | 2431 | /// / IfTypeExpr |
| 2414 | /// / INTEGER | 2432 | /// / INTEGER |
| 2415 | /// / KEYWORD_comptime TypeExpr | 2433 | /// / KEYWORD_comptime TypeExpr !ExprSuffix |
| 2416 | /// / KEYWORD_error DOT IDENTIFIER | 2434 | /// / KEYWORD_error DOT IDENTIFIER |
| 2417 | /// / KEYWORD_anyframe | 2435 | /// / KEYWORD_anyframe |
| 2418 | /// / KEYWORD_unreachable | 2436 | /// / KEYWORD_unreachable |
| ... | @@ -2431,7 +2449,7 @@ fn parseSuffixExpr(p: *Parse) !?Node.Index { | ... | @@ -2431,7 +2449,7 @@ fn parseSuffixExpr(p: *Parse) !?Node.Index { |
| 2431 | /// | 2449 | /// |
| 2432 | /// GroupedExpr <- LPAREN Expr RPAREN | 2450 | /// GroupedExpr <- LPAREN Expr RPAREN |
| 2433 | /// | 2451 | /// |
| 2434 | /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? | 2452 | /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix |
| 2435 | /// | 2453 | /// |
| 2436 | /// LabeledTypeExpr | 2454 | /// LabeledTypeExpr |
| 2437 | /// <- BlockLabel Block | 2455 | /// <- BlockLabel Block |
| ... | @@ -2711,7 +2729,7 @@ fn expectPrimaryTypeExpr(p: *Parse) !Node.Index { | ... | @@ -2711,7 +2729,7 @@ fn expectPrimaryTypeExpr(p: *Parse) !Node.Index { |
| 2711 | 2729 | ||
| 2712 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | 2730 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? |
| 2713 | /// | 2731 | /// |
| 2714 | /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? | 2732 | /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? !ExprSuffix |
| 2715 | fn parseWhileTypeExpr(p: *Parse) !?Node.Index { | 2733 | fn parseWhileTypeExpr(p: *Parse) !?Node.Index { |
| 2716 | const while_token = p.eatToken(.keyword_while) orelse return null; | 2734 | const while_token = p.eatToken(.keyword_while) orelse return null; |
| 2717 | _ = try p.expectToken(.l_paren); | 2735 | _ = try p.expectToken(.l_paren); |
| ... | @@ -2876,7 +2894,7 @@ fn expectAsmExpr(p: *Parse) !Node.Index { | ... | @@ -2876,7 +2894,7 @@ fn expectAsmExpr(p: *Parse) !Node.Index { |
| 2876 | }); | 2894 | }); |
| 2877 | } | 2895 | } |
| 2878 | 2896 | ||
| 2879 | /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN | 2897 | /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN |
| 2880 | fn parseAsmOutputItem(p: *Parse) !?Node.Index { | 2898 | fn parseAsmOutputItem(p: *Parse) !?Node.Index { |
| 2881 | _ = p.eatToken(.l_bracket) orelse return null; | 2899 | _ = p.eatToken(.l_bracket) orelse return null; |
| 2882 | const identifier = try p.expectToken(.identifier); | 2900 | const identifier = try p.expectToken(.identifier); |
| ... | @@ -2902,7 +2920,7 @@ fn parseAsmOutputItem(p: *Parse) !?Node.Index { | ... | @@ -2902,7 +2920,7 @@ fn parseAsmOutputItem(p: *Parse) !?Node.Index { |
| 2902 | }); | 2920 | }); |
| 2903 | } | 2921 | } |
| 2904 | 2922 | ||
| 2905 | /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN | 2923 | /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN Expr RPAREN |
| 2906 | fn parseAsmInputItem(p: *Parse) !?Node.Index { | 2924 | fn parseAsmInputItem(p: *Parse) !?Node.Index { |
| 2907 | _ = p.eatToken(.l_bracket) orelse return null; | 2925 | _ = p.eatToken(.l_bracket) orelse return null; |
| 2908 | const identifier = try p.expectToken(.identifier); | 2926 | const identifier = try p.expectToken(.identifier); |
| ... | @@ -2923,9 +2941,7 @@ fn parseAsmInputItem(p: *Parse) !?Node.Index { | ... | @@ -2923,9 +2941,7 @@ fn parseAsmInputItem(p: *Parse) !?Node.Index { |
| 2923 | 2941 | ||
| 2924 | /// BreakLabel <- COLON IDENTIFIER | 2942 | /// BreakLabel <- COLON IDENTIFIER |
| 2925 | fn parseBreakLabel(p: *Parse) Error!OptionalTokenIndex { | 2943 | fn parseBreakLabel(p: *Parse) Error!OptionalTokenIndex { |
| 2926 | _ = p.eatToken(.colon) orelse return .none; | 2944 | return if (p.eatTokens(&.{ .colon, .identifier })) |i| .fromToken(i + 1) else .none; |
| 2927 | const next_token = try p.expectToken(.identifier); | ||
| 2928 | return .fromToken(next_token); | ||
| 2929 | } | 2945 | } |
| 2930 | 2946 | ||
| 2931 | /// BlockLabel <- IDENTIFIER COLON | 2947 | /// BlockLabel <- IDENTIFIER COLON |
| ... | @@ -2950,12 +2966,7 @@ fn expectFieldInit(p: *Parse) !Node.Index { | ... | @@ -2950,12 +2966,7 @@ fn expectFieldInit(p: *Parse) !Node.Index { |
| 2950 | 2966 | ||
| 2951 | /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN | 2967 | /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN |
| 2952 | fn parseWhileContinueExpr(p: *Parse) !?Node.Index { | 2968 | fn parseWhileContinueExpr(p: *Parse) !?Node.Index { |
| 2953 | _ = p.eatToken(.colon) orelse { | 2969 | _ = p.eatToken(.colon) orelse return null; |
| 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 | }; | ||
| 2959 | _ = try p.expectToken(.l_paren); | 2970 | _ = try p.expectToken(.l_paren); |
| 2960 | const node = try p.parseAssignExpr() orelse return p.fail(.expected_expr_or_assignment); | 2971 | const node = try p.parseAssignExpr() orelse return p.fail(.expected_expr_or_assignment); |
| 2961 | _ = try p.expectToken(.r_paren); | 2972 | _ = try p.expectToken(.r_paren); |
| ... | @@ -2993,9 +3004,7 @@ fn parseAddrSpace(p: *Parse) !?Node.Index { | ... | @@ -2993,9 +3004,7 @@ fn parseAddrSpace(p: *Parse) !?Node.Index { |
| 2993 | /// such as in the case of anytype and `...`. Caller must look for rparen to find | 3004 | /// such as in the case of anytype and `...`. Caller must look for rparen to find |
| 2994 | /// out when there are no more param decls left. | 3005 | /// out when there are no more param decls left. |
| 2995 | /// | 3006 | /// |
| 2996 | /// ParamDecl | 3007 | /// ParamDecl <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime / !KEYWORD_comptime) (IDENTIFIER COLON / !(IDENTIFIER_COLON)) ParamType |
| 2997 | /// <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType | ||
| 2998 | /// / DOT3 | ||
| 2999 | /// | 3008 | /// |
| 3000 | /// ParamType | 3009 | /// ParamType |
| 3001 | /// <- KEYWORD_anytype | 3010 | /// <- KEYWORD_anytype |
| ... | @@ -3482,7 +3491,7 @@ fn parseSwitchProngList(p: *Parse) !Node.SubRange { | ... | @@ -3482,7 +3491,7 @@ fn parseSwitchProngList(p: *Parse) !Node.SubRange { |
| 3482 | return p.listToSpan(p.scratch.items[scratch_top..]); | 3491 | return p.listToSpan(p.scratch.items[scratch_top..]); |
| 3483 | } | 3492 | } |
| 3484 | 3493 | ||
| 3485 | /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl? | 3494 | /// ParamDeclList <- (ParamDecl COMMA)* (ParamDecl / DOT3 COMMA?)? |
| 3486 | fn parseParamDeclList(p: *Parse) !SmallSpan { | 3495 | fn parseParamDeclList(p: *Parse) !SmallSpan { |
| 3487 | _ = try p.expectToken(.l_paren); | 3496 | _ = try p.expectToken(.l_paren); |
| 3488 | const scratch_top = p.scratch.items.len; | 3497 | const scratch_top = p.scratch.items.len; |
| ... | @@ -3604,9 +3613,9 @@ fn parseIf(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !?N | ... | @@ -3604,9 +3613,9 @@ fn parseIf(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !?N |
| 3604 | }); | 3613 | }); |
| 3605 | } | 3614 | } |
| 3606 | 3615 | ||
| 3607 | /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? | 3616 | /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr / !KEYWORD_else) !ExprSuffix |
| 3608 | /// | 3617 | /// |
| 3609 | /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? | 3618 | /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr / !KEYWORD_else) !ExprSuffix |
| 3610 | fn parseFor(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !?Node.Index { | 3619 | fn parseFor(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !?Node.Index { |
| 3611 | const for_token = p.eatToken(.keyword_for) orelse return null; | 3620 | const for_token = p.eatToken(.keyword_for) orelse return null; |
| 3612 | 3621 |
lib/std/zig/parser_test.zig+236-112| ... | @@ -5472,17 +5472,11 @@ test "zig fmt: while continue expr" { | ... | @@ -5472,17 +5472,11 @@ test "zig fmt: while continue expr" { |
| 5472 | \\ while (i > 0) | 5472 | \\ while (i > 0) |
| 5473 | \\ (i * 2); | 5473 | \\ (i * 2); |
| 5474 | \\} | 5474 | \\} |
| 5475 | \\T: (while (true) ({ | ||
| 5476 | \\ break usize; | ||
| 5477 | \\})), | ||
| 5475 | \\ | 5478 | \\ |
| 5476 | ); | 5479 | ); |
| 5477 | try testError( | ||
| 5478 | \\test { | ||
| 5479 | \\ while (i > 0) (i -= 1) { | ||
| 5480 | \\ print("test123", .{}); | ||
| 5481 | \\ } | ||
| 5482 | \\} | ||
| 5483 | , &[_]Error{ | ||
| 5484 | .expected_continue_expr, | ||
| 5485 | }); | ||
| 5486 | } | 5480 | } |
| 5487 | 5481 | ||
| 5488 | test "zig fmt: canonicalize symbols (simple)" { | 5482 | test "zig fmt: canonicalize symbols (simple)" { |
| ... | @@ -6139,6 +6133,16 @@ test "zig fmt: do not canonicalize invalid cast builtins" { | ... | @@ -6139,6 +6133,16 @@ test "zig fmt: do not canonicalize invalid cast builtins" { |
| 6139 | ); | 6133 | ); |
| 6140 | } | 6134 | } |
| 6141 | 6135 | ||
| 6136 | test "zig fmt: canonicalize cast builtins at file start" { | ||
| 6137 | try testTransform( | ||
| 6138 | \\@alignCast(@ptrCast(a)), | ||
| 6139 | \\ | ||
| 6140 | , | ||
| 6141 | \\@ptrCast(@alignCast(a)), | ||
| 6142 | \\ | ||
| 6143 | ); | ||
| 6144 | } | ||
| 6145 | |||
| 6142 | test "zig fmt: extern addrspace in struct" { | 6146 | test "zig fmt: extern addrspace in struct" { |
| 6143 | try testCanonical( | 6147 | try testCanonical( |
| 6144 | \\const namespace = struct { | 6148 | \\const namespace = struct { |
| ... | @@ -6838,6 +6842,123 @@ test "zig fmt: error set with extra newline before comma" { | ... | @@ -6838,6 +6842,123 @@ test "zig fmt: error set with extra newline before comma" { |
| 6838 | ); | 6842 | ); |
| 6839 | } | 6843 | } |
| 6840 | 6844 | ||
| 6845 | test "zig fmt: extern container in tuple" { | ||
| 6846 | try testCanonical( | ||
| 6847 | \\const T = struct { | ||
| 6848 | \\ extern struct {}, | ||
| 6849 | \\ extern union {}, | ||
| 6850 | \\ extern enum {}, | ||
| 6851 | \\}; | ||
| 6852 | \\ | ||
| 6853 | ); | ||
| 6854 | } | ||
| 6855 | |||
| 6856 | test "zig fmt: break followed by colon" { | ||
| 6857 | try testCanonical( | ||
| 6858 | \\const a = [if (cond) len else break:0]u8; | ||
| 6859 | \\ | ||
| 6860 | ); | ||
| 6861 | } | ||
| 6862 | |||
| 6863 | test "zig fmt: array init of labeled block" { | ||
| 6864 | try testCanonical( | ||
| 6865 | \\const a = blk: { | ||
| 6866 | \\ break :blk T; | ||
| 6867 | \\}{ .a = false }; | ||
| 6868 | \\ | ||
| 6869 | ); | ||
| 6870 | } | ||
| 6871 | |||
| 6872 | test "zig fmt: nested asm indentation" { | ||
| 6873 | try testCanonical( | ||
| 6874 | \\const A = asm ("" | ||
| 6875 | \\ : [_] "" (_), | ||
| 6876 | \\ : | ||
| 6877 | \\ : asm ("" | ||
| 6878 | \\ : [_] "" (_), | ||
| 6879 | \\ )); | ||
| 6880 | \\ | ||
| 6881 | ); | ||
| 6882 | } | ||
| 6883 | |||
| 6884 | test "zig fmt: asm with zig fmt on" { | ||
| 6885 | try testCanonical( | ||
| 6886 | \\// zig fmt: off | ||
| 6887 | \\const A = asm("a" // zig fmt: on | ||
| 6888 | \\ : [_] "" (_), | ||
| 6889 | \\); | ||
| 6890 | \\ | ||
| 6891 | ); | ||
| 6892 | } | ||
| 6893 | |||
| 6894 | test "zig fmt: array init with multiline string literal with fmt on/off" { | ||
| 6895 | try testCanonical( | ||
| 6896 | \\const array = .{ | ||
| 6897 | \\ \\ | ||
| 6898 | \\ // zig fmt: on | ||
| 6899 | \\ // zig fmt: off | ||
| 6900 | \\}; | ||
| 6901 | \\ | ||
| 6902 | ); | ||
| 6903 | } | ||
| 6904 | |||
| 6905 | test "zig fmt: render extra colons with comments" { | ||
| 6906 | try testCanonical( | ||
| 6907 | \\const a = asm ("" | ||
| 6908 | \\ : // testing | ||
| 6909 | \\); | ||
| 6910 | \\const b = asm ("" | ||
| 6911 | \\ : // testing | ||
| 6912 | \\ : // testing | ||
| 6913 | \\); | ||
| 6914 | \\const c = asm ("" | ||
| 6915 | \\ : | ||
| 6916 | \\ : // testing | ||
| 6917 | \\); | ||
| 6918 | \\ | ||
| 6919 | ); | ||
| 6920 | } | ||
| 6921 | |||
| 6922 | test "zig fmt: cast builtins are not reordered with comments" { | ||
| 6923 | try testCanonical( | ||
| 6924 | \\const a = @volatileCast(@constCast( // ... | ||
| 6925 | \\ @alignCast(@ptrCast(a)))); | ||
| 6926 | \\ | ||
| 6927 | \\const b = @alignCast(@ptrCast( // zig fmt: off | ||
| 6928 | \\ c)); | ||
| 6929 | \\ | ||
| 6930 | ); | ||
| 6931 | } | ||
| 6932 | |||
| 6933 | test "zig fmt: inner over-indented if expressions becoming multiline" { | ||
| 6934 | try testTransform( | ||
| 6935 | \\const a = (b or | ||
| 6936 | \\c) and [if (d) {}]T; // If the if-statement is kept on the same line it becomes multiline | ||
| 6937 | \\const a = (b or | ||
| 6938 | \\c)[if (d) {}]; // If the if-statement is kept on the same line it becomes multiline | ||
| 6939 | \\const a = .{a, b, (c or | ||
| 6940 | \\d), if (d) {}, e, f, g,}; | ||
| 6941 | \\ | ||
| 6942 | , | ||
| 6943 | \\const a = (b or | ||
| 6944 | \\ c) and [ | ||
| 6945 | \\ if (d) {} | ||
| 6946 | \\]T; // If the if-statement is kept on the same line it becomes multiline | ||
| 6947 | \\const a = (b or | ||
| 6948 | \\ c)[ | ||
| 6949 | \\ if (d) {} | ||
| 6950 | \\]; // If the if-statement is kept on the same line it becomes multiline | ||
| 6951 | \\const a = .{ | ||
| 6952 | \\ a, b, | ||
| 6953 | \\ (c or | ||
| 6954 | \\ d), | ||
| 6955 | \\ if (d) {}, e, | ||
| 6956 | \\ f, g, | ||
| 6957 | \\}; | ||
| 6958 | \\ | ||
| 6959 | ); | ||
| 6960 | } | ||
| 6961 | |||
| 6841 | test "recovery: top level" { | 6962 | test "recovery: top level" { |
| 6842 | try testError( | 6963 | try testError( |
| 6843 | \\test "" {inline} | 6964 | \\test "" {inline} |
| ... | @@ -7258,83 +7379,61 @@ test "zig fmt: fuzz" { | ... | @@ -7258,83 +7379,61 @@ test "zig fmt: fuzz" { |
| 7258 | try std.testing.fuzz({}, fuzzRender, .{}); | 7379 | try std.testing.fuzz({}, fuzzRender, .{}); |
| 7259 | } | 7380 | } |
| 7260 | 7381 | ||
| 7261 | fn parseTokens( | 7382 | fn isRewritable(source: []const u8, tokens: std.zig.Ast.TokenList.Slice) !bool { |
| 7262 | fba: Allocator, | ||
| 7263 | source: [:0]const u8, | ||
| 7264 | ) error{ SkipZigTest, OutOfMemory }!struct { | ||
| 7265 | toks: std.zig.Ast.TokenList, | ||
| 7266 | maybe_rewritable: bool, | ||
| 7267 | skip_idempotency: bool, | ||
| 7268 | } { | ||
| 7269 | @disableInstrumentation(); | 7383 | @disableInstrumentation(); |
| 7384 | |||
| 7270 | // Byte-order marker is stripped | 7385 | // Byte-order marker is stripped |
| 7271 | var maybe_rewritable = std.mem.startsWith(u8, source, "\xEF\xBB\xBF"); | 7386 | var maybe_rewritable = std.mem.startsWith(u8, source, "\xEF\xBB\xBF"); |
| 7272 | var skip_idempotency = false; // This should be able to be removed once all the bugs are fixed | 7387 | // The above variable can not yet be replaced by returns since error.SkipZigTest still needs to |
| 7273 | 7388 | // be checked for. | |
| 7274 | var tokens: std.zig.Ast.TokenList = .{}; | 7389 | |
| 7275 | try tokens.ensureTotalCapacity(fba, source.len / 2); | 7390 | for (0.., tokens.items(.tag), tokens.items(.start)) |i, tag, start| switch (tag) { |
| 7276 | var tokenizer: std.zig.Tokenizer = .init(source); | 7391 | // Extra colons can be removed |
| 7277 | while (true) { | 7392 | .keyword_asm, |
| 7278 | const tok = tokenizer.next(); | 7393 | // Qualifiers can be reordered |
| 7279 | switch (tok.tag) { | 7394 | // keyword_const is intentionally excluded since it is used in other contexts and |
| 7280 | .invalid, | 7395 | // having only one qualifier will never lead to reordering. |
| 7281 | .invalid_periodasterisks, | 7396 | .keyword_addrspace, |
| 7282 | => return error.SkipZigTest, | 7397 | .keyword_align, |
| 7283 | // Extra colons can be removed | 7398 | .keyword_allowzero, |
| 7284 | .keyword_asm, | 7399 | .keyword_callconv, |
| 7285 | // Qualifiers can be reordered | 7400 | .keyword_linksection, |
| 7286 | // keyword_const is intentionally excluded since it is used in other contexts and | 7401 | .keyword_volatile, |
| 7287 | // having only one qualifier will never lead to reordering. | 7402 | => maybe_rewritable = true, |
| 7288 | .keyword_addrspace, | 7403 | .builtin, |
| 7289 | .keyword_align, | 7404 | // Pointer casts can be reordered |
| 7290 | .keyword_allowzero, | 7405 | => for ([_][]const u8{ |
| 7291 | .keyword_callconv, | 7406 | "ptrCast", |
| 7292 | .keyword_linksection, | 7407 | "alignCast", |
| 7293 | .keyword_volatile, | 7408 | "addrSpaceCast", |
| 7294 | => maybe_rewritable = true, | 7409 | "constCast", |
| 7295 | .builtin, | 7410 | "volatileCast", |
| 7296 | // Pointer casts can be reordered | 7411 | }) |id| { |
| 7297 | => for ([_][]const u8{ | 7412 | if (std.mem.startsWith(u8, source[start + 1 ..], id)) { |
| 7298 | "ptrCast", | ||
| 7299 | "alignCast", | ||
| 7300 | "addrSpaceCast", | ||
| 7301 | "constCast", | ||
| 7302 | "volatileCast", | ||
| 7303 | }) |id| { | ||
| 7304 | if (std.mem.eql(u8, source[tok.loc.start + 1 .. tok.loc.end], id)) { | ||
| 7305 | maybe_rewritable = false; | ||
| 7306 | } | ||
| 7307 | }, | ||
| 7308 | // Quoted identifiers can be unquoted | ||
| 7309 | .identifier => maybe_rewritable = maybe_rewritable or source[tok.loc.start] == '@', | ||
| 7310 | else => {}, | ||
| 7311 | // #23754 | ||
| 7312 | .container_doc_comment, | ||
| 7313 | => if (std.mem.endsWith(Token.Tag, tokens.items(.tag), &.{.l_brace})) { | ||
| 7314 | return error.SkipZigTest; | ||
| 7315 | }, | ||
| 7316 | // #24507 | ||
| 7317 | .keyword_inline, | ||
| 7318 | .keyword_for, | ||
| 7319 | .keyword_while, | ||
| 7320 | .l_brace, | ||
| 7321 | => if (std.mem.endsWith(Token.Tag, tokens.items(.tag), &.{ .identifier, .colon })) { | ||
| 7322 | maybe_rewritable = true; | 7413 | maybe_rewritable = true; |
| 7323 | skip_idempotency = true; | 7414 | } |
| 7324 | }, | 7415 | }, |
| 7325 | } | 7416 | // Quoted identifiers can be unquoted |
| 7326 | try tokens.append(fba, .{ | 7417 | .identifier => if (source[start] == '@') { |
| 7327 | .tag = tok.tag, | 7418 | maybe_rewritable = true; |
| 7328 | .start = @intCast(tok.loc.start), | 7419 | }, |
| 7329 | }); | 7420 | else => {}, |
| 7330 | if (tok.tag == .eof) | 7421 | // #23754 |
| 7331 | break; | 7422 | .container_doc_comment, |
| 7332 | } | 7423 | => if (std.mem.endsWith(Token.Tag, tokens.items(.tag)[0..i], &.{.l_brace})) { |
| 7333 | return .{ | 7424 | return error.SkipZigTest; // Can cause I.B. |
| 7334 | .toks = tokens, | 7425 | }, |
| 7335 | .maybe_rewritable = maybe_rewritable, | 7426 | // #24507 |
| 7336 | .skip_idempotency = skip_idempotency, | 7427 | .keyword_inline, |
| 7428 | .keyword_for, | ||
| 7429 | .keyword_while, | ||
| 7430 | .l_brace, | ||
| 7431 | => if (std.mem.endsWith(Token.Tag, tokens.items(.tag)[0..i], &.{ .identifier, .colon })) { | ||
| 7432 | return error.SkipZigTest; // Can cause I.B. due to double rendering of zig fmt on/off | ||
| 7433 | }, | ||
| 7337 | }; | 7434 | }; |
| 7435 | |||
| 7436 | return maybe_rewritable; | ||
| 7338 | } | 7437 | } |
| 7339 | 7438 | ||
| 7340 | /// Checks equivelence of non-whitespace characters. | 7439 | /// Checks equivelence of non-whitespace characters. |
| ... | @@ -7447,34 +7546,29 @@ fn reparseTokens( | ... | @@ -7447,34 +7546,29 @@ fn reparseTokens( |
| 7447 | fn fuzzRender(_: void, smith: *std.testing.Smith) !void { | 7546 | fn fuzzRender(_: void, smith: *std.testing.Smith) !void { |
| 7448 | @disableInstrumentation(); | 7547 | @disableInstrumentation(); |
| 7449 | 7548 | ||
| 7450 | var src_buf: [512]u8 = undefined; | 7549 | var ast_smith: std.zig.AstSmith = .init(smith); |
| 7451 | const src_len = smith.sliceWeighted(&src_buf, &.{ | 7550 | try ast_smith.generateSource(); |
| 7452 | .rangeLessThan(u32, 0, 32, 256), | ||
| 7453 | .rangeLessThan(u32, 32, 64, 64), | ||
| 7454 | .rangeLessThan(u32, 64, src_buf.len, 1), | ||
| 7455 | }, &.{ | ||
| 7456 | .rangeAtMost(u8, 0x20, 0x7e, 8), | ||
| 7457 | .value(u8, '\n', 32), | ||
| 7458 | .value(u8, '\t', 8), | ||
| 7459 | .value(u8, '\r', 4), | ||
| 7460 | .rangeAtMost(u8, 0x7f, 0xff, 1), | ||
| 7461 | }); | ||
| 7462 | src_buf[src_len] = 0; | ||
| 7463 | |||
| 7464 | var fba_ctx = std.heap.FixedBufferAllocator.init(&fixed_buffer_mem); | 7551 | var fba_ctx = std.heap.FixedBufferAllocator.init(&fixed_buffer_mem); |
| 7465 | fuzzRenderInner(src_buf[0..src_len :0], fba_ctx.allocator()) catch |e| return switch (e) { | 7552 | var opt_rendered: ?[]const u8 = null; |
| 7466 | error.OutOfMemory => {}, | 7553 | fuzzRenderInner(&ast_smith, fba_ctx.allocator(), &opt_rendered) catch |e| switch (e) { |
| 7467 | else => e, | 7554 | error.SkipZigTest, error.OutOfMemory, error.WriteFailed => return error.SkipZigTest, |
| 7555 | else => |failure| { | ||
| 7556 | ast_smith.logSource(); | ||
| 7557 | if (opt_rendered) |rendered| { | ||
| 7558 | logRenderedSource(rendered); | ||
| 7559 | } | ||
| 7560 | return failure; | ||
| 7561 | }, | ||
| 7468 | }; | 7562 | }; |
| 7469 | } | 7563 | } |
| 7470 | 7564 | ||
| 7471 | fn fuzzRenderInner(source: [:0]const u8, fba: Allocator) !void { | 7565 | fn fuzzRenderInner(ast_smith: *std.zig.AstSmith, fba: Allocator, opt_rendered: *?[]const u8) !void { |
| 7472 | @disableInstrumentation(); | 7566 | @disableInstrumentation(); |
| 7473 | 7567 | ||
| 7474 | const src_toks = try parseTokens(fba, source); | 7568 | const source = ast_smith.source(); |
| 7475 | const src_tree = try std.zig.Ast.parseTokens(fba, source, src_toks.toks.slice(), .zig); | 7569 | const src_rewritable = try isRewritable(source, ast_smith.tokens()); |
| 7476 | if (src_tree.errors.len != 0) | 7570 | const src_tree = try std.zig.Ast.parseTokens(fba, source, ast_smith.tokens(), .zig); |
| 7477 | return; | 7571 | std.debug.assert(src_tree.errors.len == 0); |
| 7478 | for (src_tree.nodes.items(.tag)) |tag| switch (tag) { | 7572 | for (src_tree.nodes.items(.tag)) |tag| switch (tag) { |
| 7479 | // #24507 (`switch(x) { inline for (a) |a| a => {} }` to | 7573 | // #24507 (`switch(x) { inline for (a) |a| a => {} }` to |
| 7480 | // `switch(x) { { inline for (a) |a| a => {} }` since | 7574 | // `switch(x) { { inline for (a) |a| a => {} }` since |
| ... | @@ -7490,15 +7584,16 @@ fn fuzzRenderInner(source: [:0]const u8, fba: Allocator) !void { | ... | @@ -7490,15 +7584,16 @@ fn fuzzRenderInner(source: [:0]const u8, fba: Allocator) !void { |
| 7490 | // list to save space which is useless for fixed buffer allocators. | 7584 | // list to save space which is useless for fixed buffer allocators. |
| 7491 | try rendered_w.writer.writeByte(0); | 7585 | try rendered_w.writer.writeByte(0); |
| 7492 | const rendered = rendered_w.written()[0 .. rendered_w.written().len - 1 :0]; | 7586 | const rendered = rendered_w.written()[0 .. rendered_w.written().len - 1 :0]; |
| 7587 | opt_rendered.* = rendered; | ||
| 7493 | 7588 | ||
| 7494 | // First check that the non-whitespace characters match. This ensures that | 7589 | // First check that the non-whitespace characters match. This ensures that |
| 7495 | // identifier names, numbers, comments, et cetera are preserved. | 7590 | // identifier names, numbers, comments, et cetera are preserved. |
| 7496 | if (!src_toks.maybe_rewritable and isRewritten(source, rendered)) | 7591 | if (!src_rewritable and isRewritten(source, rendered)) |
| 7497 | return error.Rewritten; | 7592 | return error.Rewritten; |
| 7498 | // Next check that the tokens are the same since whitespace removal can change the tokens | 7593 | // Next check that the tokens are the same since whitespace removal can change the tokens |
| 7499 | const src_tags = src_toks.toks.items(.tag); | 7594 | const src_tags = ast_smith.tokens().items(.tag); |
| 7500 | const rendered_toks = try reparseTokens(fba, rendered, src_tags[0 .. src_tags.len - 1 :.eof]); | 7595 | const rendered_toks = try reparseTokens(fba, rendered, src_tags[0 .. src_tags.len - 1 :.eof]); |
| 7501 | if (!src_toks.maybe_rewritable and rendered_toks.rewritten) | 7596 | if (!src_rewritable and rendered_toks.rewritten) |
| 7502 | return error.Rewritten; | 7597 | return error.Rewritten; |
| 7503 | 7598 | ||
| 7504 | // Rerender the tree to check idempotency and that new commas | 7599 | // Rerender the tree to check idempotency and that new commas |
| ... | @@ -7506,10 +7601,39 @@ fn fuzzRenderInner(source: [:0]const u8, fba: Allocator) !void { | ... | @@ -7506,10 +7601,39 @@ fn fuzzRenderInner(source: [:0]const u8, fba: Allocator) !void { |
| 7506 | const rendered_tree = try std.zig.Ast.parseTokens(fba, rendered, rendered_toks.toks.slice(), .zig); | 7601 | const rendered_tree = try std.zig.Ast.parseTokens(fba, rendered, rendered_toks.toks.slice(), .zig); |
| 7507 | if (rendered_tree.errors.len != 0) | 7602 | if (rendered_tree.errors.len != 0) |
| 7508 | return error.Rewritten; | 7603 | return error.Rewritten; |
| 7509 | if (!src_toks.skip_idempotency) { | 7604 | var rerendered_w: std.Io.Writer.Allocating = .init(fba); |
| 7510 | var rerendered_w: std.Io.Writer.Allocating = .init(fba); | 7605 | try rerendered_w.ensureUnusedCapacity(source.len); |
| 7511 | try rerendered_w.ensureUnusedCapacity(source.len); | 7606 | try rendered_tree.render(fba, &rerendered_w.writer, .{}); |
| 7512 | try rendered_tree.render(fba, &rerendered_w.writer, .{}); | 7607 | try std.testing.expectEqualStrings(rendered, rerendered_w.written()); |
| 7513 | try std.testing.expectEqualStrings(rendered, rerendered_w.written()); | 7608 | } |
| 7514 | } | 7609 | |
| 7610 | fn logRenderedSource(source: []const u8) void { | ||
| 7611 | var buf: [256]u8 = undefined; | ||
| 7612 | const ls = std.debug.lockStderr(&buf); | ||
| 7613 | defer std.debug.unlockStderr(); | ||
| 7614 | logRenderedSourceInner(source, ls.terminal()) catch {}; | ||
| 7615 | } | ||
| 7616 | |||
| 7617 | fn logRenderedSourceInner(source: []const u8, t: std.Io.Terminal) std.Io.Writer.Error!void { | ||
| 7618 | const w = t.writer; | ||
| 7619 | |||
| 7620 | t.setColor(.dim) catch {}; | ||
| 7621 | try w.writeAll("=== Rendered Source ===\n"); | ||
| 7622 | t.setColor(.reset) catch {}; | ||
| 7623 | |||
| 7624 | for (0.., source) |i, c| switch (c) { | ||
| 7625 | ' '...0x7e => try w.writeByte(c), | ||
| 7626 | '\n' => { | ||
| 7627 | if (i != 0 and source[i - 1] == ' ') { | ||
| 7628 | try w.writeAll("⏎"); | ||
| 7629 | } | ||
| 7630 | try w.writeByte('\n'); | ||
| 7631 | }, | ||
| 7632 | else => { | ||
| 7633 | t.setColor(.cyan) catch {}; | ||
| 7634 | try w.print("\\x{x:0>2}", .{c}); | ||
| 7635 | t.setColor(.reset) catch {}; | ||
| 7636 | }, | ||
| 7637 | }; | ||
| 7638 | try w.writeAll("␃\n"); | ||
| 7515 | } | 7639 | } |
lib/std/zig/tokenizer.zig+2-1| ... | @@ -313,7 +313,8 @@ pub const Token = struct { | ... | @@ -313,7 +313,8 @@ pub const Token = struct { |
| 313 | return tag.lexeme() orelse switch (tag) { | 313 | return tag.lexeme() orelse switch (tag) { |
| 314 | .invalid => "invalid token", | 314 | .invalid => "invalid token", |
| 315 | .identifier => "an identifier", | 315 | .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", | ||
| 317 | .char_literal => "a character literal", | 318 | .char_literal => "a character literal", |
| 318 | .eof => "EOF", | 319 | .eof => "EOF", |
| 319 | .builtin => "a builtin function", | 320 | .builtin => "a builtin function", |
src/codegen/x86_64/CodeGen.zig+1| ... | @@ -170801,6 +170801,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -170801,6 +170801,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 170801 | else => |e| return e, | 170801 | else => |e| return e, |
| 170802 | }; | 170802 | }; |
| 170803 | try ops[0].toSlicePtr(cg); | 170803 | try ops[0].toSlicePtr(cg); |
| 170804 | try ops[1].toSlicePtr(cg); | ||
| 170804 | cg.select(&.{}, &.{}, &ops, switch (air_tag) { | 170805 | cg.select(&.{}, &.{}, &ops, switch (air_tag) { |
| 170805 | else => unreachable, | 170806 | else => unreachable, |
| 170806 | inline .memcpy, .memmove => |symbol| comptime &.{.{ | 170807 | inline .memcpy, .memmove => |symbol| comptime &.{.{ |
test/behavior/array.zig+12| ... | @@ -45,6 +45,18 @@ fn getArrayLen(a: []const u32) usize { | ... | @@ -45,6 +45,18 @@ fn getArrayLen(a: []const u32) usize { |
| 45 | return a.len; | 45 | return a.len; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | test "runtime array concat with comptime slice" { | ||
| 49 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 50 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; | ||
| 51 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | ||
| 52 | |||
| 53 | var a: [1]u8 = .{1}; | ||
| 54 | const b = (comptime @as([]const u8, &.{0})) ++ &a; | ||
| 55 | const c = &a ++ (comptime @as([]const u8, &.{0})); | ||
| 56 | try std.testing.expectEqualSlices(u8, &.{ 0, 1 }, b); | ||
| 57 | try std.testing.expectEqualSlices(u8, &.{ 1, 0 }, c); | ||
| 58 | } | ||
| 59 | |||
| 48 | test "array concat with undefined" { | 60 | test "array concat with undefined" { |
| 49 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 61 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 50 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | 62 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |