| 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 | 7944 | ComptimeDecl <- KEYWORD_comptime Block |
| 7945 | 7945 | |
| 7946 | 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 | 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 | 7953 | VarDeclProto <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? |
| 7953 | 7954 | |
| 7954 | 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 | 7959 | # *** Block Level *** |
| 7959 | Statement | |
| 7960 | <- KEYWORD_comptime ComptimeStatement | |
| 7961 | / KEYWORD_nosuspend BlockExprStatement | |
| 7962 | / KEYWORD_suspend BlockExprStatement | |
| 7960 | BlockStatement | |
| 7961 | <- Statement | |
| 7963 | 7962 | / KEYWORD_defer BlockExprStatement |
| 7964 | 7963 | / KEYWORD_errdefer Payload? BlockExprStatement |
| 7965 | / IfStatement | |
| 7966 | / LabeledStatement | |
| 7967 | / VarDeclExprStatement | |
| 7964 | / !ExprStatement (KEYWORD_comptime !BlockExpr)? VarAssignStatement | |
| 7968 | 7965 | |
| 7969 | ComptimeStatement | |
| 7970 | <- BlockExpr | |
| 7971 | / VarDeclExprStatement | |
| 7966 | Statement | |
| 7967 | <- ExprStatement | |
| 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 | 7977 | IfStatement |
| 7974 | 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 | 7981 | LabeledStatement <- BlockLabel? (Block / LoopStatement / SwitchExpr) |
| 7978 | 7982 | |
| 7979 | 7983 | LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) |
| 7980 | 7984 | |
| 7981 | 7985 | ForStatement |
| 7982 | <- ForPrefix BlockExpr ( KEYWORD_else Statement )? | |
| 7983 | / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) | |
| 7986 | <- ForPrefix BlockExpr ( KEYWORD_else Statement / !KEYWORD_else ) | |
| 7987 | / ForPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Statement ) | |
| 7984 | 7988 | |
| 7985 | 7989 | WhileStatement |
| 7986 | 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 | 7993 | BlockExprStatement |
| 7990 | 7994 | <- BlockExpr |
| 7991 | / AssignExpr SEMICOLON | |
| 7995 | / !BlockExpr AssignExpr SEMICOLON | |
| 7992 | 7996 | |
| 7993 | 7997 | BlockExpr <- BlockLabel? Block |
| 7994 | 7998 | |
| 7995 | # An expression, assignment, or any destructure, as a statement. | |
| 7996 | VarDeclExprStatement | |
| 7997 | <- VarDeclProto (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON | |
| 7998 | / Expr (AssignOp Expr / (COMMA (VarDeclProto / Expr))+ EQUAL Expr)? SEMICOLON | |
| 7999 | # An assignment or a destructure whose LHS are all lvalue expressions or variable declarations. | |
| 8000 | VarAssignStatement <- (VarDeclProto / Expr) (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON | |
| 7999 | 8001 | |
| 8000 | 8002 | # *** Expression Level *** |
| 8001 | 8003 | |
| ... | ... | @@ -8025,25 +8027,25 @@ PrefixExpr <- PrefixOp* PrimaryExpr |
| 8025 | 8027 | PrimaryExpr |
| 8026 | 8028 | <- AsmExpr |
| 8027 | 8029 | / IfExpr |
| 8028 | / KEYWORD_break BreakLabel? Expr? | |
| 8029 | / KEYWORD_comptime Expr | |
| 8030 | / KEYWORD_nosuspend Expr | |
| 8031 | / KEYWORD_continue BreakLabel? Expr? | |
| 8032 | / KEYWORD_resume Expr | |
| 8033 | / KEYWORD_return Expr? | |
| 8030 | / KEYWORD_break (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart) | |
| 8031 | / KEYWORD_comptime Expr !ExprSuffix | |
| 8032 | / KEYWORD_nosuspend Expr !ExprSuffix | |
| 8033 | / KEYWORD_continue (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart) | |
| 8034 | / KEYWORD_resume Expr !ExprSuffix | |
| 8035 | / KEYWORD_return (Expr !ExprSuffix / !SinglePtrTypeStart) | |
| 8034 | 8036 | / BlockLabel? LoopExpr |
| 8035 | 8037 | / Block |
| 8036 | 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 | 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 | 8050 | CurlySuffixExpr <- TypeExpr InitList? |
| 8049 | 8051 | |
| ... | ... | @@ -8070,10 +8072,10 @@ PrimaryTypeExpr |
| 8070 | 8072 | / FnProto |
| 8071 | 8073 | / GroupedExpr |
| 8072 | 8074 | / LabeledTypeExpr |
| 8073 | / IDENTIFIER | |
| 8075 | / IDENTIFIER !(COLON LabelableExpr) | |
| 8074 | 8076 | / IfTypeExpr |
| 8075 | 8077 | / INTEGER |
| 8076 | / KEYWORD_comptime TypeExpr | |
| 8078 | / KEYWORD_comptime TypeExpr !ExprSuffix | |
| 8077 | 8079 | / KEYWORD_error DOT IDENTIFIER |
| 8078 | 8080 | / KEYWORD_anyframe |
| 8079 | 8081 | / KEYWORD_unreachable |
| ... | ... | @@ -8085,7 +8087,7 @@ ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE |
| 8085 | 8087 | |
| 8086 | 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 | 8092 | LabeledTypeExpr |
| 8091 | 8093 | <- BlockLabel Block |
| ... | ... | @@ -8094,9 +8096,9 @@ LabeledTypeExpr |
| 8094 | 8096 | |
| 8095 | 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 | 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 | 8107 | |
| 8106 | 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 | 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 | 8116 | AsmClobbers <- COLON Expr |
| 8115 | 8117 | |
| ... | ... | @@ -8129,9 +8131,7 @@ AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN |
| 8129 | 8131 | # Fn specific |
| 8130 | 8132 | CallConv <- KEYWORD_callconv LPAREN Expr RPAREN |
| 8131 | 8133 | |
| 8132 | ParamDecl | |
| 8133 | <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType | |
| 8134 | / DOT3 | |
| 8134 | ParamDecl <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime / !KEYWORD_comptime) (IDENTIFIER COLON / !(IDENTIFIER_COLON)) ParamType | |
| 8135 | 8135 | |
| 8136 | 8136 | ParamType |
| 8137 | 8137 | <- KEYWORD_anytype |
| ... | ... | @@ -8237,8 +8237,8 @@ PrefixOp |
| 8237 | 8237 | PrefixTypeOp |
| 8238 | 8238 | <- QUESTIONMARK |
| 8239 | 8239 | / KEYWORD_anyframe MINUSRARROW |
| 8240 | / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | |
| 8241 | / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | |
| 8240 | / (ManyPtrTypeStart / SliceTypeStart) KEYWORD_allowzero? ByteAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile? | |
| 8241 | / SinglePtrTypeStart KEYWORD_allowzero? BitAlign? AddrSpace? KEYWORD_const? KEYWORD_volatile? | |
| 8242 | 8242 | / ArrayTypeStart |
| 8243 | 8243 | |
| 8244 | 8244 | SuffixOp |
| ... | ... | @@ -8249,15 +8249,31 @@ SuffixOp |
| 8249 | 8249 | |
| 8250 | 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 | 8269 | # Ptr specific |
| 8253 | 8270 | SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET |
| 8254 | 8271 | |
| 8255 | PtrTypeStart | |
| 8256 | <- ASTERISK | |
| 8257 | / ASTERISK2 | |
| 8258 | / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET | |
| 8272 | SinglePtrTypeStart <- ASTERISK / ASTERISK2 | |
| 8273 | ||
| 8274 | ManyPtrTypeStart <- 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 | 8278 | # ContainerDecl specific |
| 8263 | 8279 | ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE |
| ... | ... | @@ -8266,11 +8282,13 @@ ContainerDeclType |
| 8266 | 8282 | <- KEYWORD_struct (LPAREN Expr RPAREN)? |
| 8267 | 8283 | / KEYWORD_opaque |
| 8268 | 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 | 8287 | # Alignment |
| 8272 | 8288 | ByteAlign <- KEYWORD_align LPAREN Expr RPAREN |
| 8273 | 8289 | |
| 8290 | BitAlign <- KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN | |
| 8291 | ||
| 8274 | 8292 | # Lists |
| 8275 | 8293 | IdentifierList <- (doc_comment? IDENTIFIER COMMA)* (doc_comment? IDENTIFIER)? |
| 8276 | 8294 | |
| ... | ... | @@ -8280,7 +8298,7 @@ AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem? |
| 8280 | 8298 | |
| 8281 | 8299 | AsmInputList <- (AsmInputItem COMMA)* AsmInputItem? |
| 8282 | 8300 | |
| 8283 | ParamDeclList <- (ParamDecl COMMA)* ParamDecl? | |
| 8301 | ParamDeclList <- (ParamDecl COMMA)* (ParamDecl / DOT3 COMMA?)? | |
| 8284 | 8302 | |
| 8285 | 8303 | ExprList <- (Expr COMMA)* Expr? |
| 8286 | 8304 | |
| ... | ... | @@ -8337,6 +8355,7 @@ multibyte_utf8 <- |
| 8337 | 8355 | / oxC2_oxDF ox80_oxBF |
| 8338 | 8356 | |
| 8339 | 8357 | non_control_ascii <- [\040-\176] |
| 8358 | non_control_utf8 <- [\040-\377] | |
| 8340 | 8359 | |
| 8341 | 8360 | char_escape |
| 8342 | 8361 | <- "\\x" hex hex |
| ... | ... | @@ -8352,10 +8371,10 @@ string_char |
| 8352 | 8371 | / char_escape |
| 8353 | 8372 | / ![\\"\n] non_control_ascii |
| 8354 | 8373 | |
| 8355 | container_doc_comment <- ('//!' [^\n]* [ \n]* skip)+ | |
| 8356 | doc_comment <- ('///' [^\n]* [ \n]* skip)+ | |
| 8357 | line_comment <- '//' ![!/][^\n]* / '////' [^\n]* | |
| 8358 | line_string <- ('\\\\' [^\n]* [ \n]*)+ | |
| 8374 | container_doc_comment <- ('//!' non_control_utf8* [ \n]* skip)+ | |
| 8375 | doc_comment <- ('///' non_control_utf8* [ \n]* skip)+ | |
| 8376 | line_comment <- '//' ![!/] non_control_utf8* / '////' non_control_utf8* | |
| 8377 | line_string <- '\\\\' non_control_utf8* [ \n]* | |
| 8359 | 8378 | skip <- ([ \n] / line_comment)* |
| 8360 | 8379 | |
| 8361 | 8380 | CHAR_LITERAL <- ['] char_char ['] skip |
lib/std/Build/abi.zig+8-4| ... | ... | @@ -235,7 +235,8 @@ pub const fuzz = struct { |
| 235 | 235 | max: u64, |
| 236 | 236 | weight: u64, |
| 237 | 237 | |
| 238 | fn intFromValue(x: anytype) u64 { | |
| 238 | /// `inline` to propogate comptimeness | |
| 239 | inline fn intFromValue(x: anytype) u64 { | |
| 239 | 240 | const T = @TypeOf(x); |
| 240 | 241 | return switch (@typeInfo(T)) { |
| 241 | 242 | .comptime_int => x, |
| ... | ... | @@ -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 | 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 | 280 | std.debug.assert(intFromValue(at_least) <= intFromValue(at_most)); |
| 278 | 281 | return .{ |
| 279 | 282 | .min = intFromValue(at_least), |
| ... | ... | @@ -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 | 290 | std.debug.assert(intFromValue(at_least) < intFromValue(less_than)); |
| 287 | 291 | return .{ |
| 288 | 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 | 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 | 226 | /// This is similar to `value(bool)` however it is gauraunteed to eventually |
| 209 | 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 | 246 | /// This is similar to `value(bool)` however it is gauraunteed to eventually |
| 229 | 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 | 250 | /// It is asserted that the weight of `true` is non-zero. |
| 232 | 251 | // |
| 233 | 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 | 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 | 503 | /// This is similar to `value(bool)` however it is gauraunteed to eventually |
| 467 | 504 | /// return `true` and provides the fuzzer with an extra hint about the data. |
| 468 | 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 | 541 | /// |
| 505 | 542 | /// It is asserted that the weight of `false` is non-zero. |
| 506 | 543 | /// It is asserted that the weight of `true` is non-zero. |
| 507 | // | |
| 508 | // `noinline` to capture a unique return address | |
| 509 | 544 | pub fn eosWeightedSimpleWithHash(s: *Smith, false_weight: u64, true_weight: u64, hash: u32) bool { |
| 510 | 545 | @disableInstrumentation(); |
| 511 | 546 | return s.eosWeightedWithHash(&.{ |
lib/std/zig.zig+2| ... | ... | @@ -23,6 +23,7 @@ pub const primitives = @import("zig/primitives.zig"); |
| 23 | 23 | pub const isPrimitive = primitives.isPrimitive; |
| 24 | 24 | pub const Ast = @import("zig/Ast.zig"); |
| 25 | 25 | pub const AstGen = @import("zig/AstGen.zig"); |
| 26 | pub const AstSmith = @import("zig/AstSmith.zig"); | |
| 26 | 27 | pub const Zir = @import("zig/Zir.zig"); |
| 27 | 28 | pub const Zoir = @import("zig/Zoir.zig"); |
| 28 | 29 | pub const ZonGen = @import("zig/ZonGen.zig"); |
| ... | ... | @@ -1166,6 +1167,7 @@ pub const ClangCliParam = struct { |
| 1166 | 1167 | test { |
| 1167 | 1168 | _ = Ast; |
| 1168 | 1169 | _ = AstRlAnnotate; |
| 1170 | _ = AstSmith; | |
| 1169 | 1171 | _ = BuiltinFn; |
| 1170 | 1172 | _ = Client; |
| 1171 | 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 | 504 | .varargs_nonfinal => { |
| 505 | 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 | 508 | .expected_semi_after_decl => { |
| 512 | 509 | return w.writeAll("expected ';' after declaration"); |
| ... | ... | @@ -2888,7 +2885,6 @@ pub const Error = struct { |
| 2888 | 2885 | test_doc_comment, |
| 2889 | 2886 | comptime_doc_comment, |
| 2890 | 2887 | varargs_nonfinal, |
| 2891 | expected_continue_expr, | |
| 2892 | 2888 | expected_semi_after_decl, |
| 2893 | 2889 | expected_semi_after_stmt, |
| 2894 | 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 | 338 | try ais.maybeInsertNewline(); |
| 339 | 339 | |
| 340 | 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 | 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 | 352 | const next_token = last_tok + 1; |
| 346 | 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 | 652 | const lhs, const rhs = tree.nodeData(node).node_and_node; |
| 646 | 653 | const lbracket = tree.firstToken(rhs) - 1; |
| 647 | 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 | 658 | const one_line = tree.tokensOnSameLine(lbracket, rbracket) and |
| 649 | !becomesMultilineExpr(tree, rhs); | |
| 659 | !try rendersMultiline(r, rhs); | |
| 650 | 660 | const inner_space = if (one_line) Space.none else Space.newline; |
| 651 | try renderExpression(r, lhs, .none); | |
| 652 | 661 | try ais.pushIndent(.normal); |
| 653 | 662 | try renderToken(r, lbracket, inner_space); // [ |
| 654 | 663 | try renderExpression(r, rhs, inner_space); |
| ... | ... | @@ -793,34 +802,47 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { |
| 793 | 802 | canonicalize: { |
| 794 | 803 | if (params.len != 1) break :canonicalize; |
| 795 | 804 | |
| 796 | const CastKind = enum { | |
| 805 | const CastKind = enum(u8) { | |
| 797 | 806 | ptrCast, |
| 798 | 807 | alignCast, |
| 799 | 808 | addrSpaceCast, |
| 800 | 809 | constCast, |
| 801 | 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 | 817 | var cast_map = std.EnumMap(CastKind, Ast.TokenIndex).init(.{}); |
| 806 | 818 | cast_map.put(kind, builtin_token); |
| 807 | 819 | |
| 808 | 820 | var casts_before: usize = 0; |
| 809 | if (builtin_token >= 2) { | |
| 810 | var prev_builtin_token = builtin_token - 2; | |
| 811 | while (tree.tokenTag(prev_builtin_token) == .builtin) : (prev_builtin_token -= 2) { | |
| 812 | const prev_kind = meta.stringToEnum(CastKind, tree.tokenSlice(prev_builtin_token)[1..]) orelse break; | |
| 813 | if (cast_map.contains(prev_kind)) break :canonicalize; | |
| 814 | cast_map.put(prev_kind, prev_builtin_token); | |
| 815 | casts_before += 1; | |
| 816 | } | |
| 821 | var prev_builtin_token = builtin_token; | |
| 822 | while (prev_builtin_token >= 2) { | |
| 823 | prev_builtin_token -= 2; | |
| 824 | if (tree.tokenTag(prev_builtin_token) != .builtin) break; | |
| 825 | const builtin_name = tree.tokenSlice(prev_builtin_token)[1..]; | |
| 826 | const prev_kind = meta.stringToEnum(CastKind, builtin_name) orelse break; | |
| 827 | if (cast_map.contains(prev_kind)) break :canonicalize; | |
| 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 | 836 | var next_builtin_token = builtin_token + 2; |
| 820 | while (tree.tokenTag(next_builtin_token) == .builtin) : (next_builtin_token += 2) { | |
| 821 | const next_kind = meta.stringToEnum(CastKind, tree.tokenSlice(next_builtin_token)[1..]) orelse break; | |
| 837 | while (true) { | |
| 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 | 843 | if (cast_map.contains(next_kind)) break :canonicalize; |
| 823 | 844 | cast_map.put(next_kind, next_builtin_token); |
| 845 | next_builtin_token += 2; | |
| 824 | 846 | } |
| 825 | 847 | |
| 826 | 848 | var it = cast_map.iterator(); |
| ... | ... | @@ -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` | |
| 935 | fn optBecomesMultilineExpr(tree: Ast, node: Ast.Node.OptionalIndex) bool { | |
| 936 | return if (node.unwrap()) |payload| becomesMultilineExpr(tree, payload) else false; | |
| 937 | } | |
| 956 | fn drainNoNewline(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize { | |
| 957 | if (std.mem.indexOfScalar(u8, w.buffered(), '\n') != null) { | |
| 958 | return error.WriteFailed; | |
| 959 | } | |
| 938 | 960 | |
| 939 | /// May return false if `node` is already multiline | |
| 940 | fn becomesMultilineExpr(tree: Ast, node: Ast.Node.Index) bool { | |
| 941 | // Conditions related to comments, doc comments, and multiline string literals are ignored | |
| 942 | // since they always go to the end of the line, which already make them a multi-line | |
| 943 | // expression (since they contain a newline). | |
| 944 | switch (tree.nodeTag(node)) { | |
| 945 | .identifier, | |
| 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 | } | |
| 961 | var n: usize = 0; | |
| 962 | for (data[0 .. data.len - 1]) |v| { | |
| 963 | if (std.mem.indexOfScalar(u8, v, '\n') != null) { | |
| 964 | return error.WriteFailed; | |
| 965 | } | |
| 966 | n += v.len; | |
| 967 | } | |
| 1088 | 968 | |
| 1089 | const lparen = fn_proto.ast.fn_token + 1; | |
| 1090 | const return_type = fn_proto.ast.return_type.unwrap().?; | |
| 1091 | const maybe_bang = tree.firstToken(return_type) - 1; | |
| 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, | |
| 969 | const pattern = data[data.len - 1]; | |
| 970 | if (splat != 0 and std.mem.indexOfScalar(u8, pattern, '\n') != null) { | |
| 971 | return error.WriteFailed; | |
| 1297 | 972 | } |
| 973 | n += pattern.len * splat; | |
| 974 | ||
| 975 | w.end = 0; | |
| 976 | return n; | |
| 1298 | 977 | } |
| 1299 | 978 | |
| 1300 | fn isOneLineArrayType( | |
| 1301 | tree: Ast, | |
| 1302 | array_type: Ast.full.ArrayType, | |
| 1303 | rbracket: Ast.TokenIndex, | |
| 1304 | ) bool { | |
| 1305 | return tree.tokensOnSameLine(array_type.ast.lbracket, rbracket) and | |
| 1306 | !becomesMultilineExpr(tree, array_type.ast.elem_count) and | |
| 1307 | !optBecomesMultilineExpr(tree, array_type.ast.sentinel); | |
| 979 | fn rendersMultiline(r: *const Render, node: Ast.Node.Index) error{OutOfMemory}!bool { | |
| 980 | var no_nl_buf: [64]u8 = undefined; | |
| 981 | var no_nl_w: Writer = .{ | |
| 982 | .vtable = &.{ .drain = drainNoNewline }, | |
| 983 | .buffer = &no_nl_buf, | |
| 984 | }; | |
| 985 | ||
| 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 | 1013 | fn renderArrayType( |
| ... | ... | @@ -1315,7 +1018,9 @@ fn renderArrayType( |
| 1315 | 1018 | const tree = r.tree; |
| 1316 | 1019 | const ais = r.ais; |
| 1317 | 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 | 1024 | const inner_space = if (one_line) Space.none else Space.newline; |
| 1320 | 1025 | try ais.pushIndent(.normal); |
| 1321 | 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 | 1799 | break; |
| 2095 | 1800 | }, |
| 2096 | 1801 | .keyword_noalias, .keyword_comptime => { |
| 2097 | try renderToken(r, last_param_token, .space); | |
| 1802 | try renderToken(r, last_param_token, .maybe_space); | |
| 2098 | 1803 | last_param_token += 1; |
| 2099 | 1804 | }, |
| 2100 | 1805 | .identifier => {}, |
| ... | ... | @@ -2145,7 +1850,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi |
| 2145 | 1850 | break; |
| 2146 | 1851 | }, |
| 2147 | 1852 | .keyword_noalias, .keyword_comptime => { |
| 2148 | try renderToken(r, last_param_token, .space); | |
| 1853 | try renderToken(r, last_param_token, .maybe_space); | |
| 2149 | 1854 | last_param_token += 1; |
| 2150 | 1855 | }, |
| 2151 | 1856 | .identifier => {}, |
| ... | ... | @@ -2163,7 +1868,7 @@ fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!voi |
| 2163 | 1868 | { |
| 2164 | 1869 | try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name |
| 2165 | 1870 | last_param_token += 1; |
| 2166 | try renderToken(r, last_param_token, .space); // : | |
| 1871 | try renderToken(r, last_param_token, .maybe_space); // : | |
| 2167 | 1872 | last_param_token += 1; |
| 2168 | 1873 | } |
| 2169 | 1874 | if (tree.tokenTag(last_param_token) == .keyword_anytype) { |
| ... | ... | @@ -2504,6 +2209,10 @@ fn renderArrayInit( |
| 2504 | 2209 | try renderSpace(&sub_r, after_expr, tokenSliceForRender(tree, after_expr).len, .none); |
| 2505 | 2210 | |
| 2506 | 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 | 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 | 2501 | if (asm_node.ast.items.len == 0) { |
| 2753 | try ais.forcePushIndent(.normal); | |
| 2754 | 2502 | if (asm_node.ast.clobbers.unwrap()) |clobbers| { |
| 2755 | 2503 | // asm ("foo" ::: clobbers) |
| 2756 | 2504 | try renderExpression(r, asm_node.ast.template, .space); |
| ... | ... | @@ -2764,99 +2512,78 @@ fn renderAsm( |
| 2764 | 2512 | return renderToken(r, asm_node.ast.rparen, space); // rparen |
| 2765 | 2513 | } |
| 2766 | 2514 | |
| 2767 | // asm ("foo") | |
| 2768 | try renderExpression(r, asm_node.ast.template, .none); | |
| 2769 | ais.popIndent(); | |
| 2770 | return renderToken(r, asm_node.ast.rparen, space); // rparen | |
| 2515 | if (render_colons[0] == null) { | |
| 2516 | // asm ("foo") | |
| 2517 | try renderExpression(r, asm_node.ast.template, .none); | |
| 2518 | ais.popIndent(); | |
| 2519 | return renderToken(r, asm_node.ast.rparen, space); // rparen | |
| 2520 | } | |
| 2771 | 2521 | } |
| 2772 | 2522 | |
| 2773 | try ais.forcePushIndent(.normal); | |
| 2774 | 2523 | try renderExpression(r, asm_node.ast.template, .newline); |
| 2775 | 2524 | ais.forceLastIndent(); // Might have been dedented by a multiline string literal |
| 2776 | 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 | 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: { | |
| 2782 | try renderToken(r, colon1, .newline); // : | |
| 2783 | break :colon2 colon1 + 1; | |
| 2784 | } else colon2: { | |
| 2785 | try renderToken(r, colon1, .space); // : | |
| 2531 | rendered: { | |
| 2532 | if (render_colons[0]) |colon1| { | |
| 2533 | if (asm_node.outputs.len != 0) { | |
| 2534 | try renderToken(r, colon1, .space); | |
| 2535 | try ais.forcePushIndent(.normal); | |
| 2786 | 2536 | |
| 2787 | try ais.forcePushIndent(.normal); | |
| 2788 | for (asm_node.outputs, 0..) |asm_output, i| { | |
| 2789 | if (i + 1 < asm_node.outputs.len) { | |
| 2790 | const next_asm_output = asm_node.outputs[i + 1]; | |
| 2791 | try renderAsmOutput(r, asm_output, .none); | |
| 2537 | const final = asm_node.outputs.len - 1; | |
| 2538 | for (asm_node.outputs[0..final], 0..) |asm_output, i| { | |
| 2539 | try renderAsmOutput(r, asm_output, .none); | |
| 2540 | ||
| 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 | 2546 | try ais.pushSpace(.comma); |
| 2798 | try renderAsmOutput(r, asm_output, .comma); | |
| 2547 | try renderAsmOutput(r, asm_node.outputs[final], .comma); | |
| 2799 | 2548 | ais.popSpace(); |
| 2800 | 2549 | ais.popIndent(); |
| 2801 | ais.setIndentDelta(indent_delta); | |
| 2802 | ais.popIndent(); | |
| 2803 | return renderToken(r, asm_node.ast.rparen, space); // rparen | |
| 2804 | 2550 | } else { |
| 2805 | try ais.pushSpace(.comma); | |
| 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 | }; | |
| 2551 | try renderToken(r, colon1, .newline); | |
| 2814 | 2552 | } |
| 2815 | 2553 | } else unreachable; |
| 2816 | }; | |
| 2817 | 2554 | |
| 2818 | const colon3 = if (asm_node.inputs.len == 0) colon3: { | |
| 2819 | try renderToken(r, colon2, .newline); // : | |
| 2820 | break :colon3 colon2 + 1; | |
| 2821 | } else colon3: { | |
| 2822 | try renderToken(r, colon2, .space); // : | |
| 2823 | try ais.forcePushIndent(.normal); | |
| 2824 | for (asm_node.inputs, 0..) |asm_input, i| { | |
| 2825 | if (i + 1 < asm_node.inputs.len) { | |
| 2826 | const next_asm_input = asm_node.inputs[i + 1]; | |
| 2827 | try renderAsmInput(r, asm_input, .none); | |
| 2828 | ||
| 2829 | const first_token = tree.firstToken(next_asm_input); | |
| 2830 | try renderToken(r, first_token - 1, .newline); // , | |
| 2831 | try renderExtraNewlineToken(r, first_token); | |
| 2832 | } else if (asm_node.ast.clobbers == .none) { | |
| 2555 | if (render_colons[1]) |colon2| { | |
| 2556 | if (asm_node.inputs.len != 0) { | |
| 2557 | try renderToken(r, colon2, .space); | |
| 2558 | try ais.forcePushIndent(.normal); | |
| 2559 | ||
| 2560 | const final = asm_node.inputs.len - 1; | |
| 2561 | for (asm_node.inputs[0..final], 0..) |asm_input, i| { | |
| 2562 | try renderAsmInput(r, asm_input, .none); | |
| 2563 | ||
| 2564 | const next_start = tree.firstToken(asm_node.inputs[i + 1]); | |
| 2565 | try renderToken(r, next_start - 1, .newline); // , | |
| 2566 | try renderExtraNewlineToken(r, next_start); | |
| 2567 | } | |
| 2568 | ||
| 2833 | 2569 | try ais.pushSpace(.comma); |
| 2834 | try renderAsmInput(r, asm_input, .comma); | |
| 2570 | try renderAsmInput(r, asm_node.inputs[final], .comma); | |
| 2835 | 2571 | ais.popSpace(); |
| 2836 | 2572 | ais.popIndent(); |
| 2837 | ais.setIndentDelta(indent_delta); | |
| 2838 | ais.popIndent(); | |
| 2839 | return renderToken(r, asm_node.ast.rparen, space); // rparen | |
| 2840 | 2573 | } else { |
| 2841 | try ais.pushSpace(.comma); | |
| 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 | }; | |
| 2574 | try renderToken(r, colon2, .newline); | |
| 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; | |
| 2853 | }; | |
| 2584 | } | |
| 2854 | 2585 | |
| 2855 | try renderToken(r, colon3, .maybe_space); // : | |
| 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); | |
| 2586 | ais.setIndentDelta(prev_indent_delta); | |
| 2860 | 2587 | ais.popIndent(); |
| 2861 | 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 | 3051 | // Write with the canonical single space. |
| 3325 | 3052 | try ais.underlying_writer.writeAll("// zig fmt: on\n"); |
| 3326 | 3053 | ais.disabled_offset = null; |
| 3054 | ais.resetLine(); | |
| 3327 | 3055 | } else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) { |
| 3328 | 3056 | // Write with the canonical single space. |
| 3329 | 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 | 257 | while (true) { |
| 258 | 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 | 261 | .keyword_test => { |
| 262 | 262 | if (doc_comment) |some| { |
| 263 | 263 | try p.warnMsg(.{ .tag = .test_doc_comment, .token = some }); |
| ... | ... | @@ -348,17 +348,7 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members { |
| 348 | 348 | p.findNextContainerMember(); |
| 349 | 349 | }, |
| 350 | 350 | }, |
| 351 | .keyword_pub => { | |
| 352 | p.tok_i += 1; | |
| 353 | const opt_top_level_decl = try p.expectTopLevelDeclRecoverable(); | |
| 354 | if (opt_top_level_decl) |top_level_decl| { | |
| 355 | if (field_state == .seen) { | |
| 356 | field_state = .{ .end = top_level_decl }; | |
| 357 | } | |
| 358 | try p.scratch.append(p.gpa, top_level_decl); | |
| 359 | } | |
| 360 | trailing = p.tokenTag(p.tok_i - 1) == .semicolon; | |
| 361 | }, | |
| 351 | .keyword_pub, | |
| 362 | 352 | .keyword_const, |
| 363 | 353 | .keyword_var, |
| 364 | 354 | .keyword_threadlocal, |
| ... | ... | @@ -367,7 +357,27 @@ fn parseContainerMembers(p: *Parse) Allocator.Error!Members { |
| 367 | 357 | .keyword_inline, |
| 368 | 358 | .keyword_noinline, |
| 369 | 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 | 381 | const opt_top_level_decl = try p.expectTopLevelDeclRecoverable(); |
| 372 | 382 | if (opt_top_level_decl) |top_level_decl| { |
| 373 | 383 | if (field_state == .seen) { |
| ... | ... | @@ -588,7 +598,8 @@ fn expectTestDeclRecoverable(p: *Parse) error{OutOfMemory}!?Node.Index { |
| 588 | 598 | } |
| 589 | 599 | |
| 590 | 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 | 603 | /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl |
| 593 | 604 | fn expectTopLevelDecl(p: *Parse) !?Node.Index { |
| 594 | 605 | const extern_export_inline_token = p.nextToken(); |
| ... | ... | @@ -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 | 680 | fn parseFnProto(p: *Parse) !?Node.Index { |
| 670 | 681 | const fn_token = p.eatToken(.keyword_fn) orelse return null; |
| 671 | 682 | |
| ... | ... | @@ -853,7 +864,7 @@ fn parseGlobalVarDecl(p: *Parse) !?Node.Index { |
| 853 | 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 | 868 | fn expectContainerField(p: *Parse) !Node.Index { |
| 858 | 869 | _ = p.eatToken(.keyword_comptime); |
| 859 | 870 | const main_token = p.tok_i; |
| ... | ... | @@ -895,16 +906,23 @@ fn expectContainerField(p: *Parse) !Node.Index { |
| 895 | 906 | } |
| 896 | 907 | } |
| 897 | 908 | |
| 898 | /// Statement | |
| 899 | /// <- KEYWORD_comptime ComptimeStatement | |
| 900 | /// / KEYWORD_nosuspend BlockExprStatement | |
| 901 | /// / KEYWORD_suspend BlockExprStatement | |
| 909 | /// BlockStatement | |
| 910 | /// <- Statement | |
| 902 | 911 | /// / KEYWORD_defer BlockExprStatement |
| 903 | 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 | 922 | /// / LabeledStatement |
| 906 | /// / VarDeclExprStatement | |
| 907 | fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { | |
| 923 | /// / KEYWORD_nosuspend BlockExprStatement | |
| 924 | /// / KEYWORD_comptime BlockExpr | |
| 925 | fn expectStatement(p: *Parse, is_block_level: bool) Error!Node.Index { | |
| 908 | 926 | if (p.eatToken(.keyword_comptime)) |comptime_token| { |
| 909 | 927 | const opt_block_expr = try p.parseBlockExpr(); |
| 910 | 928 | if (opt_block_expr) |block_expr| { |
| ... | ... | @@ -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 | 937 | return p.expectVarDeclExprStatement(comptime_token); |
| 920 | 938 | } else { |
| 921 | 939 | const assign = try p.expectAssignExpr(); |
| ... | ... | @@ -949,12 +967,12 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { |
| 949 | 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 | 971 | .tag = .@"defer", |
| 954 | 972 | .main_token = p.nextToken(), |
| 955 | 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 | 976 | .tag = .@"errdefer", |
| 959 | 977 | .main_token = p.nextToken(), |
| 960 | 978 | .data = .{ .opt_token_and_node = .{ |
| ... | ... | @@ -979,7 +997,7 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { |
| 979 | 997 | |
| 980 | 998 | if (try p.parseLabeledStatement()) |labeled_statement| return labeled_statement; |
| 981 | 999 | |
| 982 | if (allow_defer_var) { | |
| 1000 | if (is_block_level) { | |
| 983 | 1001 | return p.expectVarDeclExprStatement(null); |
| 984 | 1002 | } else { |
| 985 | 1003 | const assign = try p.expectAssignExpr(); |
| ... | ... | @@ -1007,8 +1025,10 @@ fn expectComptimeStatement(p: *Parse, comptime_token: TokenIndex) !Node.Index { |
| 1007 | 1025 | } |
| 1008 | 1026 | |
| 1009 | 1027 | /// VarDeclExprStatement |
| 1010 | /// <- VarDeclProto (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON | |
| 1011 | /// / Expr (AssignOp Expr / (COMMA (VarDeclProto / Expr))+ EQUAL Expr)? SEMICOLON | |
| 1028 | /// <- Expr | |
| 1029 | /// / VarAssignStatement | |
| 1030 | /// | |
| 1031 | /// VarAssignStatement <- (VarDeclProto / Expr) (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON | |
| 1012 | 1032 | fn expectVarDeclExprStatement(p: *Parse, comptime_token: ?TokenIndex) !Node.Index { |
| 1013 | 1033 | const scratch_top = p.scratch.items.len; |
| 1014 | 1034 | defer p.scratch.shrinkRetainingCapacity(scratch_top); |
| ... | ... | @@ -1140,7 +1160,7 @@ fn expectStatementRecoverable(p: *Parse) Error!?Node.Index { |
| 1140 | 1160 | |
| 1141 | 1161 | /// IfStatement |
| 1142 | 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 | 1164 | fn expectIfStatement(p: *Parse) !Node.Index { |
| 1145 | 1165 | const if_token = p.assertToken(.keyword_if); |
| 1146 | 1166 | _ = try p.expectToken(.l_paren); |
| ... | ... | @@ -1235,8 +1255,8 @@ fn parseLoopStatement(p: *Parse) !?Node.Index { |
| 1235 | 1255 | } |
| 1236 | 1256 | |
| 1237 | 1257 | /// ForStatement |
| 1238 | /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? | |
| 1239 | /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) | |
| 1258 | /// <- ForPrefix BlockExpr ( KEYWORD_else Statement / !KEYWORD_else ) | |
| 1259 | /// / ForPrefix !BlockExpr AssignExpr ( SEMICOLON / KEYWORD_else Statement ) | |
| 1240 | 1260 | fn parseForStatement(p: *Parse) !?Node.Index { |
| 1241 | 1261 | const for_token = p.eatToken(.keyword_for) orelse return null; |
| 1242 | 1262 | |
| ... | ... | @@ -1293,7 +1313,7 @@ fn parseForStatement(p: *Parse) !?Node.Index { |
| 1293 | 1313 | /// |
| 1294 | 1314 | /// WhileStatement |
| 1295 | 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 | 1317 | fn parseWhileStatement(p: *Parse) !?Node.Index { |
| 1298 | 1318 | const while_token = p.eatToken(.keyword_while) orelse return null; |
| 1299 | 1319 | _ = try p.expectToken(.l_paren); |
| ... | ... | @@ -1383,7 +1403,7 @@ fn parseWhileStatement(p: *Parse) !?Node.Index { |
| 1383 | 1403 | |
| 1384 | 1404 | /// BlockExprStatement |
| 1385 | 1405 | /// <- BlockExpr |
| 1386 | /// / AssignExpr SEMICOLON | |
| 1406 | /// / !BlockExpr AssignExpr SEMICOLON | |
| 1387 | 1407 | fn parseBlockExprStatement(p: *Parse) !?Node.Index { |
| 1388 | 1408 | const block_expr = try p.parseBlockExpr(); |
| 1389 | 1409 | if (block_expr) |expr| return expr; |
| ... | ... | @@ -1685,18 +1705,20 @@ fn expectPrefixExpr(p: *Parse) Error!Node.Index { |
| 1685 | 1705 | /// PrefixTypeOp |
| 1686 | 1706 | /// <- QUESTIONMARK |
| 1687 | 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 | 1710 | /// / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* |
| 1690 | 1711 | /// / ArrayTypeStart |
| 1691 | 1712 | /// |
| 1692 | 1713 | /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET |
| 1693 | 1714 | /// |
| 1694 | /// PtrTypeStart | |
| 1695 | /// <- ASTERISK | |
| 1696 | /// / ASTERISK2 | |
| 1697 | /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET | |
| 1715 | /// SinglePtrTypeStart <- ASTERISK / ASTERISK2 | |
| 1716 | /// | |
| 1717 | /// ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET | |
| 1718 | /// | |
| 1719 | /// ArrayTypeStart <- LBRACKET Expr !(ASTERISK / ASTERISK2) (COLON Expr)? RBRACKET | |
| 1698 | 1720 | /// |
| 1699 | /// ArrayTypeStart <- LBRACKET Expr (COLON Expr)? RBRACKET | |
| 1721 | /// BitAlign <- KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN | |
| 1700 | 1722 | fn parseTypeExpr(p: *Parse) Error!?Node.Index { |
| 1701 | 1723 | switch (p.tokenTag(p.tok_i)) { |
| 1702 | 1724 | .question_mark => return try p.addNode(.{ |
| ... | ... | @@ -1962,12 +1984,12 @@ fn expectTypeExpr(p: *Parse) Error!Node.Index { |
| 1962 | 1984 | /// PrimaryExpr |
| 1963 | 1985 | /// <- AsmExpr |
| 1964 | 1986 | /// / IfExpr |
| 1965 | /// / KEYWORD_break BreakLabel? Expr? | |
| 1966 | /// / KEYWORD_comptime Expr | |
| 1967 | /// / KEYWORD_nosuspend Expr | |
| 1968 | /// / KEYWORD_continue BreakLabel? Expr? | |
| 1969 | /// / KEYWORD_resume Expr | |
| 1970 | /// / KEYWORD_return Expr? | |
| 1987 | /// / KEYWORD_break (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart) | |
| 1988 | /// / KEYWORD_comptime Expr !ExprSuffix | |
| 1989 | /// / KEYWORD_nosuspend Expr !ExprSuffix | |
| 1990 | /// / KEYWORD_continue (BreakLabel / !BreakLabel) (Expr !ExprSuffix / !SinglePtrTypeStart) | |
| 1991 | /// / KEYWORD_resume Expr !ExprSuffix | |
| 1992 | /// / KEYWORD_return (Expr !ExprSuffix / !SinglePtrTypeStart) | |
| 1971 | 1993 | /// / BlockLabel? LoopExpr |
| 1972 | 1994 | /// / Block |
| 1973 | 1995 | /// / CurlySuffixExpr |
| ... | ... | @@ -2042,10 +2064,6 @@ fn parsePrimaryExpr(p: *Parse) !?Node.Index { |
| 2042 | 2064 | p.tok_i += 2; |
| 2043 | 2065 | return try p.parseWhileExpr(); |
| 2044 | 2066 | }, |
| 2045 | .l_brace => { | |
| 2046 | p.tok_i += 2; | |
| 2047 | return try p.parseBlock(); | |
| 2048 | }, | |
| 2049 | 2067 | else => return try p.parseCurlySuffixExpr(), |
| 2050 | 2068 | } |
| 2051 | 2069 | } else { |
| ... | ... | @@ -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 | 2089 | fn parseIfExpr(p: *Parse) !?Node.Index { |
| 2072 | 2090 | return try p.parseIf(expectExpr); |
| 2073 | 2091 | } |
| 2074 | 2092 | |
| 2075 | /// Block <- LBRACE Statement* RBRACE | |
| 2093 | /// Block <- LBRACE BlockStatement* RBRACE | |
| 2076 | 2094 | fn parseBlock(p: *Parse) !?Node.Index { |
| 2077 | 2095 | const lbrace = p.eatToken(.l_brace) orelse return null; |
| 2078 | 2096 | const scratch_top = p.scratch.items.len; |
| ... | ... | @@ -2177,7 +2195,7 @@ fn forPrefix(p: *Parse) Error!usize { |
| 2177 | 2195 | |
| 2178 | 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 | 2199 | fn parseWhileExpr(p: *Parse) !?Node.Index { |
| 2182 | 2200 | const while_token = p.eatToken(.keyword_while) orelse return null; |
| 2183 | 2201 | _ = try p.expectToken(.l_paren); |
| ... | ... | @@ -2409,10 +2427,10 @@ fn parseSuffixExpr(p: *Parse) !?Node.Index { |
| 2409 | 2427 | /// / FnProto |
| 2410 | 2428 | /// / GroupedExpr |
| 2411 | 2429 | /// / LabeledTypeExpr |
| 2412 | /// / IDENTIFIER | |
| 2430 | /// / IDENTIFIER !(COLON LabelableExpr) | |
| 2413 | 2431 | /// / IfTypeExpr |
| 2414 | 2432 | /// / INTEGER |
| 2415 | /// / KEYWORD_comptime TypeExpr | |
| 2433 | /// / KEYWORD_comptime TypeExpr !ExprSuffix | |
| 2416 | 2434 | /// / KEYWORD_error DOT IDENTIFIER |
| 2417 | 2435 | /// / KEYWORD_anyframe |
| 2418 | 2436 | /// / KEYWORD_unreachable |
| ... | ... | @@ -2431,7 +2449,7 @@ fn parseSuffixExpr(p: *Parse) !?Node.Index { |
| 2431 | 2449 | /// |
| 2432 | 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 | 2454 | /// LabeledTypeExpr |
| 2437 | 2455 | /// <- BlockLabel Block |
| ... | ... | @@ -2711,7 +2729,7 @@ fn expectPrimaryTypeExpr(p: *Parse) !Node.Index { |
| 2711 | 2729 | |
| 2712 | 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 | 2733 | fn parseWhileTypeExpr(p: *Parse) !?Node.Index { |
| 2716 | 2734 | const while_token = p.eatToken(.keyword_while) orelse return null; |
| 2717 | 2735 | _ = try p.expectToken(.l_paren); |
| ... | ... | @@ -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 | 2898 | fn parseAsmOutputItem(p: *Parse) !?Node.Index { |
| 2881 | 2899 | _ = p.eatToken(.l_bracket) orelse return null; |
| 2882 | 2900 | const identifier = try p.expectToken(.identifier); |
| ... | ... | @@ -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 | 2924 | fn parseAsmInputItem(p: *Parse) !?Node.Index { |
| 2907 | 2925 | _ = p.eatToken(.l_bracket) orelse return null; |
| 2908 | 2926 | const identifier = try p.expectToken(.identifier); |
| ... | ... | @@ -2923,9 +2941,7 @@ fn parseAsmInputItem(p: *Parse) !?Node.Index { |
| 2923 | 2941 | |
| 2924 | 2942 | /// BreakLabel <- COLON IDENTIFIER |
| 2925 | 2943 | fn parseBreakLabel(p: *Parse) Error!OptionalTokenIndex { |
| 2926 | _ = p.eatToken(.colon) orelse return .none; | |
| 2927 | const next_token = try p.expectToken(.identifier); | |
| 2928 | return .fromToken(next_token); | |
| 2944 | return if (p.eatTokens(&.{ .colon, .identifier })) |i| .fromToken(i + 1) else .none; | |
| 2929 | 2945 | } |
| 2930 | 2946 | |
| 2931 | 2947 | /// BlockLabel <- IDENTIFIER COLON |
| ... | ... | @@ -2950,12 +2966,7 @@ fn expectFieldInit(p: *Parse) !Node.Index { |
| 2950 | 2966 | |
| 2951 | 2967 | /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN |
| 2952 | 2968 | fn parseWhileContinueExpr(p: *Parse) !?Node.Index { |
| 2953 | _ = p.eatToken(.colon) orelse { | |
| 2954 | if (p.tokenTag(p.tok_i) == .l_paren and | |
| 2955 | p.tokensOnSameLine(p.tok_i - 1, p.tok_i)) | |
| 2956 | return p.fail(.expected_continue_expr); | |
| 2957 | return null; | |
| 2958 | }; | |
| 2969 | _ = p.eatToken(.colon) orelse return null; | |
| 2959 | 2970 | _ = try p.expectToken(.l_paren); |
| 2960 | 2971 | const node = try p.parseAssignExpr() orelse return p.fail(.expected_expr_or_assignment); |
| 2961 | 2972 | _ = try p.expectToken(.r_paren); |
| ... | ... | @@ -2993,9 +3004,7 @@ fn parseAddrSpace(p: *Parse) !?Node.Index { |
| 2993 | 3004 | /// such as in the case of anytype and `...`. Caller must look for rparen to find |
| 2994 | 3005 | /// out when there are no more param decls left. |
| 2995 | 3006 | /// |
| 2996 | /// ParamDecl | |
| 2997 | /// <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType | |
| 2998 | /// / DOT3 | |
| 3007 | /// ParamDecl <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime / !KEYWORD_comptime) (IDENTIFIER COLON / !(IDENTIFIER_COLON)) ParamType | |
| 2999 | 3008 | /// |
| 3000 | 3009 | /// ParamType |
| 3001 | 3010 | /// <- KEYWORD_anytype |
| ... | ... | @@ -3482,7 +3491,7 @@ fn parseSwitchProngList(p: *Parse) !Node.SubRange { |
| 3482 | 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 | 3495 | fn parseParamDeclList(p: *Parse) !SmallSpan { |
| 3487 | 3496 | _ = try p.expectToken(.l_paren); |
| 3488 | 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 | 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 | 3619 | fn parseFor(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !?Node.Index { |
| 3611 | 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 | 5472 | \\ while (i > 0) |
| 5473 | 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 | 5482 | test "zig fmt: canonicalize symbols (simple)" { |
| ... | ... | @@ -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 | 6146 | test "zig fmt: extern addrspace in struct" { |
| 6143 | 6147 | try testCanonical( |
| 6144 | 6148 | \\const namespace = struct { |
| ... | ... | @@ -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 | 6962 | test "recovery: top level" { |
| 6842 | 6963 | try testError( |
| 6843 | 6964 | \\test "" {inline} |
| ... | ... | @@ -7258,83 +7379,61 @@ test "zig fmt: fuzz" { |
| 7258 | 7379 | try std.testing.fuzz({}, fuzzRender, .{}); |
| 7259 | 7380 | } |
| 7260 | 7381 | |
| 7261 | fn parseTokens( | |
| 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 | } { | |
| 7382 | fn isRewritable(source: []const u8, tokens: std.zig.Ast.TokenList.Slice) !bool { | |
| 7269 | 7383 | @disableInstrumentation(); |
| 7384 | ||
| 7270 | 7385 | // Byte-order marker is stripped |
| 7271 | 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 | |
| 7273 | ||
| 7274 | var tokens: std.zig.Ast.TokenList = .{}; | |
| 7275 | try tokens.ensureTotalCapacity(fba, source.len / 2); | |
| 7276 | var tokenizer: std.zig.Tokenizer = .init(source); | |
| 7277 | while (true) { | |
| 7278 | const tok = tokenizer.next(); | |
| 7279 | switch (tok.tag) { | |
| 7280 | .invalid, | |
| 7281 | .invalid_periodasterisks, | |
| 7282 | => return error.SkipZigTest, | |
| 7283 | // Extra colons can be removed | |
| 7284 | .keyword_asm, | |
| 7285 | // Qualifiers can be reordered | |
| 7286 | // keyword_const is intentionally excluded since it is used in other contexts and | |
| 7287 | // having only one qualifier will never lead to reordering. | |
| 7288 | .keyword_addrspace, | |
| 7289 | .keyword_align, | |
| 7290 | .keyword_allowzero, | |
| 7291 | .keyword_callconv, | |
| 7292 | .keyword_linksection, | |
| 7293 | .keyword_volatile, | |
| 7294 | => maybe_rewritable = true, | |
| 7295 | .builtin, | |
| 7296 | // Pointer casts can be reordered | |
| 7297 | => for ([_][]const u8{ | |
| 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 })) { | |
| 7387 | // The above variable can not yet be replaced by returns since error.SkipZigTest still needs to | |
| 7388 | // be checked for. | |
| 7389 | ||
| 7390 | for (0.., tokens.items(.tag), tokens.items(.start)) |i, tag, start| switch (tag) { | |
| 7391 | // Extra colons can be removed | |
| 7392 | .keyword_asm, | |
| 7393 | // Qualifiers can be reordered | |
| 7394 | // keyword_const is intentionally excluded since it is used in other contexts and | |
| 7395 | // having only one qualifier will never lead to reordering. | |
| 7396 | .keyword_addrspace, | |
| 7397 | .keyword_align, | |
| 7398 | .keyword_allowzero, | |
| 7399 | .keyword_callconv, | |
| 7400 | .keyword_linksection, | |
| 7401 | .keyword_volatile, | |
| 7402 | => maybe_rewritable = true, | |
| 7403 | .builtin, | |
| 7404 | // Pointer casts can be reordered | |
| 7405 | => for ([_][]const u8{ | |
| 7406 | "ptrCast", | |
| 7407 | "alignCast", | |
| 7408 | "addrSpaceCast", | |
| 7409 | "constCast", | |
| 7410 | "volatileCast", | |
| 7411 | }) |id| { | |
| 7412 | if (std.mem.startsWith(u8, source[start + 1 ..], id)) { | |
| 7322 | 7413 | maybe_rewritable = true; |
| 7323 | skip_idempotency = true; | |
| 7324 | }, | |
| 7325 | } | |
| 7326 | try tokens.append(fba, .{ | |
| 7327 | .tag = tok.tag, | |
| 7328 | .start = @intCast(tok.loc.start), | |
| 7329 | }); | |
| 7330 | if (tok.tag == .eof) | |
| 7331 | break; | |
| 7332 | } | |
| 7333 | return .{ | |
| 7334 | .toks = tokens, | |
| 7335 | .maybe_rewritable = maybe_rewritable, | |
| 7336 | .skip_idempotency = skip_idempotency, | |
| 7414 | } | |
| 7415 | }, | |
| 7416 | // Quoted identifiers can be unquoted | |
| 7417 | .identifier => if (source[start] == '@') { | |
| 7418 | maybe_rewritable = true; | |
| 7419 | }, | |
| 7420 | else => {}, | |
| 7421 | // #23754 | |
| 7422 | .container_doc_comment, | |
| 7423 | => if (std.mem.endsWith(Token.Tag, tokens.items(.tag)[0..i], &.{.l_brace})) { | |
| 7424 | return error.SkipZigTest; // Can cause I.B. | |
| 7425 | }, | |
| 7426 | // #24507 | |
| 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 | 7439 | /// Checks equivelence of non-whitespace characters. |
| ... | ... | @@ -7447,34 +7546,29 @@ fn reparseTokens( |
| 7447 | 7546 | fn fuzzRender(_: void, smith: *std.testing.Smith) !void { |
| 7448 | 7547 | @disableInstrumentation(); |
| 7449 | 7548 | |
| 7450 | var src_buf: [512]u8 = undefined; | |
| 7451 | const src_len = smith.sliceWeighted(&src_buf, &.{ | |
| 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 | ||
| 7549 | var ast_smith: std.zig.AstSmith = .init(smith); | |
| 7550 | try ast_smith.generateSource(); | |
| 7464 | 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) { | |
| 7466 | error.OutOfMemory => {}, | |
| 7467 | else => e, | |
| 7552 | var opt_rendered: ?[]const u8 = null; | |
| 7553 | fuzzRenderInner(&ast_smith, fba_ctx.allocator(), &opt_rendered) catch |e| switch (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 | 7566 | @disableInstrumentation(); |
| 7473 | 7567 | |
| 7474 | const src_toks = try parseTokens(fba, source); | |
| 7475 | const src_tree = try std.zig.Ast.parseTokens(fba, source, src_toks.toks.slice(), .zig); | |
| 7476 | if (src_tree.errors.len != 0) | |
| 7477 | return; | |
| 7568 | const source = ast_smith.source(); | |
| 7569 | const src_rewritable = try isRewritable(source, ast_smith.tokens()); | |
| 7570 | const src_tree = try std.zig.Ast.parseTokens(fba, source, ast_smith.tokens(), .zig); | |
| 7571 | std.debug.assert(src_tree.errors.len == 0); | |
| 7478 | 7572 | for (src_tree.nodes.items(.tag)) |tag| switch (tag) { |
| 7479 | 7573 | // #24507 (`switch(x) { inline for (a) |a| a => {} }` to |
| 7480 | 7574 | // `switch(x) { { inline for (a) |a| a => {} }` since |
| ... | ... | @@ -7490,15 +7584,16 @@ fn fuzzRenderInner(source: [:0]const u8, fba: Allocator) !void { |
| 7490 | 7584 | // list to save space which is useless for fixed buffer allocators. |
| 7491 | 7585 | try rendered_w.writer.writeByte(0); |
| 7492 | 7586 | const rendered = rendered_w.written()[0 .. rendered_w.written().len - 1 :0]; |
| 7587 | opt_rendered.* = rendered; | |
| 7493 | 7588 | |
| 7494 | 7589 | // First check that the non-whitespace characters match. This ensures that |
| 7495 | 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 | 7592 | return error.Rewritten; |
| 7498 | 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 | 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 | 7597 | return error.Rewritten; |
| 7503 | 7598 | |
| 7504 | 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 | 7601 | const rendered_tree = try std.zig.Ast.parseTokens(fba, rendered, rendered_toks.toks.slice(), .zig); |
| 7507 | 7602 | if (rendered_tree.errors.len != 0) |
| 7508 | 7603 | return error.Rewritten; |
| 7509 | if (!src_toks.skip_idempotency) { | |
| 7510 | var rerendered_w: std.Io.Writer.Allocating = .init(fba); | |
| 7511 | try rerendered_w.ensureUnusedCapacity(source.len); | |
| 7512 | try rendered_tree.render(fba, &rerendered_w.writer, .{}); | |
| 7513 | try std.testing.expectEqualStrings(rendered, rerendered_w.written()); | |
| 7514 | } | |
| 7604 | var rerendered_w: std.Io.Writer.Allocating = .init(fba); | |
| 7605 | try rerendered_w.ensureUnusedCapacity(source.len); | |
| 7606 | try rendered_tree.render(fba, &rerendered_w.writer, .{}); | |
| 7607 | try std.testing.expectEqualStrings(rendered, rerendered_w.written()); | |
| 7608 | } | |
| 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 | 313 | return tag.lexeme() orelse switch (tag) { |
| 314 | 314 | .invalid => "invalid token", |
| 315 | 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 | 318 | .char_literal => "a character literal", |
| 318 | 319 | .eof => "EOF", |
| 319 | 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 | 170801 | else => |e| return e, |
| 170802 | 170802 | }; |
| 170803 | 170803 | try ops[0].toSlicePtr(cg); |
| 170804 | try ops[1].toSlicePtr(cg); | |
| 170804 | 170805 | cg.select(&.{}, &.{}, &ops, switch (air_tag) { |
| 170805 | 170806 | else => unreachable, |
| 170806 | 170807 | inline .memcpy, .memmove => |symbol| comptime &.{.{ |
test/behavior/array.zig+12| ... | ... | @@ -45,6 +45,18 @@ fn getArrayLen(a: []const u32) usize { |
| 45 | 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 | 60 | test "array concat with undefined" { |
| 49 | 61 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 50 | 62 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |