| author | |
| committer | |
| log | 3c46da14db296beec186c3df4fe3669f6c99b715 |
| tree | 8c34fd2046c87da2592b39454e833e0160900af0 |
| parent | bf4e2ab8d16e74374c4769f90e1f4042c0f968a1 |
| parent | 9296ec14e9efe901b397cf8560f75cacf867f170 |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/365966 files changed, 162 insertions(+), 94 deletions(-)
doc/langref.html.in+17-36| ... | ... | @@ -7274,46 +7274,27 @@ fn readU32Be() u32 {} |
| 7274 | 7274 | </ul> |
| 7275 | 7275 | {#header_close#} |
| 7276 | 7276 | {#header_close#} |
| 7277 | ||
| 7277 | 7278 | {#header_open|Source Encoding#} |
| 7278 | <p>Zig source code is encoded in UTF-8. An invalid UTF-8 byte sequence results in a compile error.</p> | |
| 7279 | <p>Throughout all zig source code (including in comments), some code points are never allowed:</p> | |
| 7279 | <p>Zig source code is UTF-8 encoded. Invalid UTF-8 byte sequences are not allowed anywhere.</p> | |
| 7280 | <p>Some code points are never allowed, even in {#link|Comments#}:</p> | |
| 7280 | 7281 | <ul> |
| 7281 | <li>Ascii control characters, except for U+000a (LF), U+000d (CR), and U+0009 (HT): U+0000 - U+0008, U+000b - U+000c, U+000e - U+0001f, U+007f.</li> | |
| 7282 | <li>Non-Ascii Unicode line endings: U+0085 (NEL), U+2028 (LS), U+2029 (PS).</li> | |
| 7282 | <li>ASCII control characters, except for U+000a (LF): U+0000...U+0009, U+000b...U+0001f, U+007f.</li> | |
| 7283 | <li>Non-ASCII Unicode line endings: U+0085 (NEL), U+2028 (LS), U+2029 (PS).</li> | |
| 7284 | <li>Byte order marks: U+FEFF (BOM).</li> | |
| 7283 | 7285 | </ul> |
| 7284 | 7286 | <p> |
| 7285 | LF (byte value 0x0a, code point U+000a, {#syntax#}'\n'{#endsyntax#}) is the line terminator in Zig source code. | |
| 7286 | This byte value terminates every line of zig source code except the last line of the file. | |
| 7287 | It is recommended that non-empty source files end with an empty line, which means the last byte would be 0x0a (LF). | |
| 7288 | </p> | |
| 7289 | <p> | |
| 7290 | Each LF may be immediately preceded by a single CR (byte value 0x0d, code point U+000d, {#syntax#}'\r'{#endsyntax#}) | |
| 7291 | to form a Windows style line ending, but this is discouraged. Note that in multiline strings, CRLF sequences will | |
| 7292 | be encoded as LF when compiled into a zig program. | |
| 7293 | A CR in any other context is not allowed. | |
| 7294 | </p> | |
| 7295 | <p> | |
| 7296 | HT hard tabs (byte value 0x09, code point U+0009, {#syntax#}'\t'{#endsyntax#}) are interchangeable with | |
| 7297 | SP spaces (byte value 0x20, code point U+0020, {#syntax#}' '{#endsyntax#}) as a token separator, | |
| 7298 | but use of hard tabs is discouraged. See {#link|Grammar#}. | |
| 7299 | </p> | |
| 7300 | <p> | |
| 7301 | For compatibility with other tools, the compiler ignores a UTF-8-encoded byte order mark (U+FEFF) | |
| 7302 | if it is the first Unicode code point in the source text. A byte order mark is not allowed anywhere else in the source. | |
| 7303 | </p> | |
| 7304 | <p> | |
| 7305 | Note that running <kbd>zig fmt</kbd> on a source file will implement all recommendations mentioned here. | |
| 7306 | </p> | |
| 7307 | <p> | |
| 7308 | Note that a tool reading Zig source code can make assumptions if the source code is assumed to be correct Zig code. | |
| 7309 | For example, when identifying the ends of lines, a tool can use a naive search such as <code>/\n/</code>, | |
| 7310 | or an <a href="https://msdn.microsoft.com/en-us/library/dd409797.aspx">advanced</a> | |
| 7311 | search such as <code>/\r\n?|[\n\u0085\u2028\u2029]/</code>, and in either case line endings will be correctly identified. | |
| 7312 | For another example, when identifying the whitespace before the first token on a line, | |
| 7313 | a tool can either use a naive search such as <code>/[ \t]/</code>, | |
| 7314 | or an <a href="https://tc39.es/ecma262/#sec-characterclassescape">advanced</a> search such as <code>/\s/</code>, | |
| 7315 | and in either case whitespace will be correctly identified. | |
| 7316 | </p> | |
| 7287 | LF (byte value 0x0a, code point U+000a, {#syntax#}'\n'{#endsyntax#}) is | |
| 7288 | the line terminator in Zig source code. This byte value terminates every | |
| 7289 | line of Zig source code, including last line of the file. | |
| 7290 | </p> | |
| 7291 | <p>These conservative rules mean that third party tools reading | |
| 7292 | already-validated Zig source code may make simplifying assumptions, such | |
| 7293 | as naively separating lines based on {#syntax#}'\n'{#endsyntax#}. | |
| 7294 | However, tooling such as <kbd>zig fmt</kbd> provides convenience | |
| 7295 | functionality to convert invalid source encodings to valid source | |
| 7296 | encodings, for instance by stripping byte order marks and carriage | |
| 7297 | returns.</p> | |
| 7317 | 7298 | {#header_close#} |
| 7318 | 7299 | |
| 7319 | 7300 | {#header_open|Keyword Reference#} |
lib/compiler/Maker.zig+26-20| ... | ... | @@ -292,9 +292,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 292 | 292 | try cached_passthru_configure.append(arena, @intCast(configure_argv.items.len)); |
| 293 | 293 | configure_argv.appendAssumeCapacity(arg); |
| 294 | 294 | } else if (mem.eql(u8, arg, "--color")) { |
| 295 | const next_arg = nextArgOrFatal(args, &arg_i); | |
| 296 | color = stringToEnum(Color, next_arg) orelse | |
| 297 | fatalWithHint("expected [auto|on|off] found {q}", .{next_arg}); | |
| 295 | color = nextEnumArg(args, &arg_i, Color); | |
| 298 | 296 | |
| 299 | 297 | try cached_passthru_configure.append(arena, @intCast(configure_argv.items.len)); |
| 300 | 298 | configure_argv.appendAssumeCapacity(try arena.print("--color={t}", .{color})); |
| ... | ... | @@ -401,25 +399,11 @@ pub fn main(init: process.Init.Minimal) !void { |
| 401 | 399 | } else if (mem.eql(u8, arg, "--libc")) { |
| 402 | 400 | graph.libc_file = nextArgOrFatal(args, &arg_i); |
| 403 | 401 | } else if (mem.eql(u8, arg, "--error-style")) { |
| 404 | const next_arg = nextArg(args, &arg_i) orelse | |
| 405 | fatalWithHint("expected style after {q}", .{arg}); | |
| 406 | error_style = stringToEnum(ErrorStyle, next_arg) orelse { | |
| 407 | fatalWithHint("expected style after {q}, found {q}", .{ arg, next_arg }); | |
| 408 | }; | |
| 402 | error_style = nextEnumArg(args, &arg_i, ErrorStyle); | |
| 409 | 403 | } else if (mem.eql(u8, arg, "--multiline-errors")) { |
| 410 | const next_arg = nextArg(args, &arg_i) orelse | |
| 411 | fatalWithHint("expected style after {q}", .{arg}); | |
| 412 | multiline_errors = stringToEnum(MultilineErrors, next_arg) orelse { | |
| 413 | fatalWithHint("expected style after {q}, found {q}", .{ arg, next_arg }); | |
| 414 | }; | |
| 404 | multiline_errors = nextEnumArg(args, &arg_i, MultilineErrors); | |
| 415 | 405 | } else if (mem.eql(u8, arg, "--summary")) { |
| 416 | const next_arg = nextArg(args, &arg_i) orelse | |
| 417 | fatalWithHint("expected [all|new|failures|line|none] after {q}", .{arg}); | |
| 418 | summary = stringToEnum(Summary, next_arg) orelse { | |
| 419 | fatalWithHint("expected [all|new|failures|line|none] after {q}, found {q}", .{ | |
| 420 | arg, next_arg, | |
| 421 | }); | |
| 422 | }; | |
| 406 | summary = nextEnumArg(args, &arg_i, Summary); | |
| 423 | 407 | } else if (mem.cutPrefix(u8, arg, "--seed=")) |rest| { |
| 424 | 408 | graph.random_seed = parseRandomSeed(rest); |
| 425 | 409 | } else if (mem.eql(u8, arg, "--build-id")) { |
| ... | ... | @@ -4053,3 +4037,25 @@ fn confPathDepToCachePath( |
| 4053 | 4037 | .install_include => @panic("TODO"), |
| 4054 | 4038 | }; |
| 4055 | 4039 | } |
| 4040 | ||
| 4041 | fn fatalEnumHint(comptime E: type, arg: []const u8, param: ?[]const u8) noreturn { | |
| 4042 | var buf: [100]u8 = undefined; | |
| 4043 | var w: Io.Writer = .fixed(&buf); | |
| 4044 | for (@typeInfo(E).@"enum".field_names) |field_name| { | |
| 4045 | w.writeAll(field_name) catch unreachable; | |
| 4046 | w.writeByte('|') catch unreachable; | |
| 4047 | } | |
| 4048 | const buffered = w.buffered(); | |
| 4049 | const enum_options_text = buffered[0 .. buffered.len - 1]; | |
| 4050 | if (param) |p| { | |
| 4051 | fatalWithHint("expected [{s}] after {q}; found {q}", .{ enum_options_text, arg, p }); | |
| 4052 | } else { | |
| 4053 | fatalWithHint("expected [{s}] after {q}", .{ enum_options_text, arg }); | |
| 4054 | } | |
| 4055 | } | |
| 4056 | ||
| 4057 | fn nextEnumArg(args: []const []const u8, i: *usize, comptime E: type) E { | |
| 4058 | const arg = args[i.* - 1]; | |
| 4059 | const next_arg = nextArg(args, i) orelse fatalEnumHint(E, arg, null); | |
| 4060 | return stringToEnum(E, next_arg) orelse fatalEnumHint(E, arg, next_arg); | |
| 4061 | } |
lib/std/Io/Writer.zig+42-20| ... | ... | @@ -584,36 +584,41 @@ pub fn writeAll(w: *Writer, bytes: []const u8) Error!void { |
| 584 | 584 | /// required, otherwise the digit following ':' is interpreted as **width**. |
| 585 | 585 | /// |
| 586 | 586 | /// **specifier** supports: |
| 587 | /// - `x` and `X`: numeric value in hexadecimal notation, or string in hexadecimal bytes | |
| 588 | /// - `s`: | |
| 587 | /// - "x" and "X": numeric value in hexadecimal notation, or string in hexadecimal bytes | |
| 588 | /// - "s": | |
| 589 | 589 | /// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination |
| 590 | 590 | /// - for slices of u8, print the entire slice as a string without zero-termination |
| 591 | /// - `t`: | |
| 591 | /// - "t": | |
| 592 | 592 | /// - for enums and tagged unions: prints the tag name |
| 593 | 593 | /// - for error sets: prints the error name |
| 594 | /// - `b64`: string as standard base64 | |
| 595 | /// - `e`: floating point value in scientific notation | |
| 596 | /// - `d`: numeric value in decimal notation | |
| 597 | /// - `b`: integer value in binary notation | |
| 598 | /// - `o`: integer value in octal notation | |
| 599 | /// - `c`: integer as an ASCII character. Integer type must have 8 bits at max. | |
| 600 | /// - `u`: integer as an UTF-8 sequence. Integer type must have 21 bits at max. | |
| 601 | /// - `B`: bytes in SI units (decimal) | |
| 602 | /// - `Bi`: bytes in IEC units (binary) | |
| 603 | /// - `?`: optional value as either the unwrapped value, or `null`; may be | |
| 594 | /// - "b64": string as standard base64 | |
| 595 | /// - "e": floating point value in scientific notation | |
| 596 | /// - "d": numeric value in decimal notation | |
| 597 | /// - "b": integer value in binary notation | |
| 598 | /// - "o": integer value in octal notation | |
| 599 | /// - "c": integer as an ASCII character. Integer type must have 8 bits at max. | |
| 600 | /// - "u": integer as an UTF-8 sequence. Integer type must have 21 bits at max. | |
| 601 | /// - "B": bytes in SI units (decimal) | |
| 602 | /// - "Bi": bytes in IEC units (binary) | |
| 603 | /// - "?": optional value as either the unwrapped value, or `null`; may be | |
| 604 | 604 | /// followed by a format specifier for the underlying value. |
| 605 | /// - `!`: error union value as either the unwrapped value, or the formatted | |
| 605 | /// - "!": error union value as either the unwrapped value, or the formatted | |
| 606 | 606 | /// error value; may be followed by a format specifier for the underlying |
| 607 | 607 | /// value. |
| 608 | /// - `*`: the address of the value instead of the value itself. | |
| 609 | /// - `any`: a value of any type using its default format. | |
| 610 | /// - `f`: delegates to the `format` method of the type, passing `*Writer` and | |
| 608 | /// - "*": the address of the value instead of the value itself. | |
| 609 | /// - "any": a value of any type using its default format. | |
| 610 | /// - "f": delegates to the `format` method of the type, passing `*Writer` and | |
| 611 | 611 | /// expecting `Error!void` returned. |
| 612 | /// | |
| 613 | /// A user type may be a struct, vector, union or enum type. | |
| 612 | /// - "q": prints as a double-quote escaped string. Inside the double-quoted | |
| 613 | /// string, everything is passed through unmodified, except for the following | |
| 614 | /// transformations: | |
| 615 | /// - escaped: '\n', '\r', '\t', '\\', '"' | |
| 616 | /// - hex-encoded: ASCII control characters | |
| 617 | /// - "qf": delegates to the `format` method of the type, while double-quote | |
| 618 | /// escaping. | |
| 614 | 619 | /// |
| 615 | 620 | /// Literal curly braces can be escaped in the format string via doubling, e.g. |
| 616 | /// `{{` or `}}`. | |
| 621 | /// "{{" or "}}". | |
| 617 | 622 | pub fn print(w: *Writer, comptime fmt: []const u8, args: anytype) Error!void { |
| 618 | 623 | const ArgsType = @TypeOf(args); |
| 619 | 624 | const args_type_info = @typeInfo(ArgsType); |
| ... | ... | @@ -1231,6 +1236,18 @@ pub fn printValue( |
| 1231 | 1236 | }, |
| 1232 | 1237 | else => {}, |
| 1233 | 1238 | }, |
| 1239 | 'q' => switch (fmt[1]) { | |
| 1240 | 'f' => { | |
| 1241 | try w.writeByte('"'); | |
| 1242 | var buffer: [64]u8 = undefined; | |
| 1243 | var escaping_writer: std.zig.StringEscapeWriter = .init(w, &buffer); | |
| 1244 | try value.format(&escaping_writer.writer); | |
| 1245 | try escaping_writer.writer.flush(); | |
| 1246 | try w.writeByte('"'); | |
| 1247 | return; | |
| 1248 | }, | |
| 1249 | else => {}, | |
| 1250 | }, | |
| 1234 | 1251 | else => {}, |
| 1235 | 1252 | }, |
| 1236 | 1253 | 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') switch (@typeInfo(T)) { |
| ... | ... | @@ -2143,6 +2160,11 @@ test "{q} format string" { |
| 2143 | 2160 | try testing.expectFmt("hello \"i\\tlike\\\"cheese\\x00\\x05cheese\" world", "hello {q} world", .{data}); |
| 2144 | 2161 | } |
| 2145 | 2162 | |
| 2163 | test "{qf} format string" { | |
| 2164 | const data: []const u8 = "😎"; | |
| 2165 | try testing.expectFmt("hello \"@\\\"😎\\\"\" world", "hello {qf} world", .{std.zig.fmtId(data)}); | |
| 2166 | } | |
| 2167 | ||
| 2146 | 2168 | fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void { |
| 2147 | 2169 | var buffer: [100]u8 = undefined; |
| 2148 | 2170 | var w: Writer = .fixed(&buffer); |
lib/std/zig.zig+69-7| ... | ... | @@ -538,21 +538,83 @@ test fmtChar { |
| 538 | 538 | } |
| 539 | 539 | |
| 540 | 540 | /// Print the string as escaped contents of a double quoted string. |
| 541 | /// | |
| 542 | /// The following transformations are made: | |
| 543 | /// * escaped: '\n', '\r', '\t', '\\', '"' | |
| 544 | /// * hex-encoded: ascii control characters | |
| 545 | /// | |
| 546 | /// Everything else is passed through unmodified. | |
| 541 | 547 | pub fn stringEscape(bytes: []const u8, w: *Writer) Writer.Error!void { |
| 548 | _ = try stringEscapeCounting(bytes, w); | |
| 549 | } | |
| 550 | ||
| 551 | pub fn stringEscapeCounting(bytes: []const u8, w: *Writer) Writer.Error!usize { | |
| 552 | var n: usize = 0; | |
| 542 | 553 | for (bytes) |byte| switch (byte) { |
| 543 | '\n' => try w.writeAll("\\n"), | |
| 544 | '\r' => try w.writeAll("\\r"), | |
| 545 | '\t' => try w.writeAll("\\t"), | |
| 546 | '\\' => try w.writeAll("\\\\"), | |
| 547 | '"' => try w.writeAll("\\\""), | |
| 548 | ' ', '!', '#'...'[', ']'...'~' => try w.writeByte(byte), | |
| 549 | else => { | |
| 554 | '\t' => { | |
| 555 | try w.writeAll("\\t"); | |
| 556 | n += 2; | |
| 557 | }, | |
| 558 | '\n' => { | |
| 559 | try w.writeAll("\\n"); | |
| 560 | n += 2; | |
| 561 | }, | |
| 562 | '\r' => { | |
| 563 | try w.writeAll("\\r"); | |
| 564 | n += 2; | |
| 565 | }, | |
| 566 | '\\' => { | |
| 567 | try w.writeAll("\\\\"); | |
| 568 | n += 2; | |
| 569 | }, | |
| 570 | '"' => { | |
| 571 | try w.writeAll("\\\""); | |
| 572 | n += 2; | |
| 573 | }, | |
| 574 | 0...8, 11, 12, 14...0x1f, 0x7f => { | |
| 550 | 575 | try w.writeAll("\\x"); |
| 551 | 576 | try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); |
| 577 | n += 4; | |
| 578 | }, | |
| 579 | else => { | |
| 580 | try w.writeByte(byte); | |
| 581 | n += 1; | |
| 552 | 582 | }, |
| 553 | 583 | }; |
| 584 | return n; | |
| 554 | 585 | } |
| 555 | 586 | |
| 587 | pub const StringEscapeWriter = struct { | |
| 588 | out: *Writer, | |
| 589 | writer: Writer, | |
| 590 | ||
| 591 | pub fn init(out: *Writer, buffer: []u8) @This() { | |
| 592 | return .{ | |
| 593 | .out = out, | |
| 594 | .writer = .{ | |
| 595 | .vtable = &.{ .drain = @This().drain }, | |
| 596 | .buffer = buffer, | |
| 597 | }, | |
| 598 | }; | |
| 599 | } | |
| 600 | ||
| 601 | fn drain(w: *Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize { | |
| 602 | const sew: *StringEscapeWriter = @alignCast(@fieldParentPtr("writer", w)); | |
| 603 | const out = sew.out; | |
| 604 | _ = try stringEscapeCounting(w.buffered(), out); | |
| 605 | w.end = 0; | |
| 606 | var n: usize = 0; | |
| 607 | for (data[0 .. data.len - 1]) |bytes| { | |
| 608 | n += try stringEscapeCounting(bytes, out); | |
| 609 | } | |
| 610 | const pattern = data[data.len - 1]; | |
| 611 | for (0..splat) |_| { | |
| 612 | n += try stringEscapeCounting(pattern, out); | |
| 613 | } | |
| 614 | return n; | |
| 615 | } | |
| 616 | }; | |
| 617 | ||
| 556 | 618 | /// Print as escaped contents of a single-quoted string. |
| 557 | 619 | pub fn charEscape(codepoint: u21, w: *Writer) Writer.Error!void { |
| 558 | 620 | switch (codepoint) { |
lib/std/zig/parser_generated_oracle.zig+5-5| ... | ... | @@ -3073,7 +3073,7 @@ const Parser = struct { |
| 3073 | 3073 | return blk_0: { |
| 3074 | 3074 | const pos_0 = p.i; |
| 3075 | 3075 | if (blk_1: { |
| 3076 | if (std.mem.startsWith(u8, p.source[p.i..], "\xef\xbb\xbf")) { | |
| 3076 | if (std.mem.startsWith(u8, p.source[p.i..], "")) { | |
| 3077 | 3077 | p.i += 3; |
| 3078 | 3078 | break :blk_1 true; |
| 3079 | 3079 | } |
| ... | ... | @@ -3145,7 +3145,7 @@ const Parser = struct { |
| 3145 | 3145 | return blk_0: { |
| 3146 | 3146 | const pos_0 = p.i; |
| 3147 | 3147 | if (blk_1: { |
| 3148 | if (std.mem.startsWith(u8, p.source[p.i..], "\xf4")) { | |
| 3148 | if (std.mem.startsWith(u8, p.source[p.i..], "�")) { | |
| 3149 | 3149 | p.i += 1; |
| 3150 | 3150 | break :blk_1 true; |
| 3151 | 3151 | } |
| ... | ... | @@ -3201,7 +3201,7 @@ const Parser = struct { |
| 3201 | 3201 | return blk_0: { |
| 3202 | 3202 | const pos_0 = p.i; |
| 3203 | 3203 | if (blk_1: { |
| 3204 | if (std.mem.startsWith(u8, p.source[p.i..], "\xf0")) { | |
| 3204 | if (std.mem.startsWith(u8, p.source[p.i..], "�")) { | |
| 3205 | 3205 | p.i += 1; |
| 3206 | 3206 | break :blk_1 true; |
| 3207 | 3207 | } |
| ... | ... | @@ -3257,7 +3257,7 @@ const Parser = struct { |
| 3257 | 3257 | return blk_0: { |
| 3258 | 3258 | const pos_0 = p.i; |
| 3259 | 3259 | if (blk_1: { |
| 3260 | if (std.mem.startsWith(u8, p.source[p.i..], "\xed")) { | |
| 3260 | if (std.mem.startsWith(u8, p.source[p.i..], "�")) { | |
| 3261 | 3261 | p.i += 1; |
| 3262 | 3262 | break :blk_1 true; |
| 3263 | 3263 | } |
| ... | ... | @@ -3313,7 +3313,7 @@ const Parser = struct { |
| 3313 | 3313 | return blk_0: { |
| 3314 | 3314 | const pos_0 = p.i; |
| 3315 | 3315 | if (blk_1: { |
| 3316 | if (std.mem.startsWith(u8, p.source[p.i..], "\xe0")) { | |
| 3316 | if (std.mem.startsWith(u8, p.source[p.i..], "�")) { | |
| 3317 | 3317 | p.i += 1; |
| 3318 | 3318 | break :blk_1 true; |
| 3319 | 3319 | } |
tools/gen_parser_oracle.zig+3-6| ... | ... | @@ -267,17 +267,14 @@ const Generator = struct { |
| 267 | 267 | const bytes = g.p.strings.items[literal.off..][0..literal.len]; |
| 268 | 268 | try g.w.print( |
| 269 | 269 | \\blk_{d}: {{ |
| 270 | \\if (std.mem.startsWith(u8, p.source[p.i..], " | |
| 271 | , .{suffix}); | |
| 272 | try std.zig.stringEscape(bytes, g.w); | |
| 273 | try g.w.print( | |
| 274 | \\")) {{ | |
| 270 | \\if (std.mem.startsWith(u8, p.source[p.i..], {q})) {{ | |
| 271 | \\ | |
| 275 | 272 | \\p.i += {d}; |
| 276 | 273 | \\ break :blk_{d} true; |
| 277 | 274 | \\}} |
| 278 | 275 | \\break :blk_{d} false; |
| 279 | 276 | \\}} |
| 280 | , .{ bytes.len, suffix, suffix }); | |
| 277 | , .{ suffix, bytes, bytes.len, suffix, suffix }); | |
| 281 | 278 | }, |
| 282 | 279 | .class => |ranges| { |
| 283 | 280 | try g.w.writeAll("(p.i < p.source.len and switch (p.source[p.i]) {"); |