From b84b65fa746295e7bb2fe7781dca2bbc4de0870f Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Sat, 21 Dec 2024 21:18:06 -0800 Subject: [PATCH] Cleans up diff with master, resovles some feedback --- lib/std/fmt.zig | 2 +- lib/std/zig/Ast.zig | 3 +- lib/std/zig/AstGen.zig | 90 ++++--- lib/std/zig/number_literal.zig | 70 ------ lib/std/zig/string_literal.zig | 220 +----------------- lib/std/zon.zig | 41 ++-- lib/std/zon/parse.zig | 28 +-- src/Air.zig | 5 - src/Package/Manifest.zig | 98 +++++++- src/Zcu.zig | 2 +- src/zon.zig | 2 +- test/behavior/zon.zig | 6 +- test/behavior/zon/escaped_struct.zon | 2 +- .../{slice-1.zon => slice1_no_newline.zon} | 0 14 files changed, 186 insertions(+), 383 deletions(-) rename test/behavior/zon/{slice-1.zon => slice1_no_newline.zon} (100%) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index a16295cda599..13531ce64cd7 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1582,7 +1582,7 @@ test parseInt { } /// Like `parseIntWithGenericCharacter`, but with a sign argument. -pub fn parseIntWithSign( +fn parseIntWithSign( comptime Result: type, comptime Character: type, buf: []const Character, diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index d9050e9659f1..34531ccc3ede 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -7,13 +7,12 @@ /// Reference to externally-owned data. source: [:0]const u8, -mode: Mode, - tokens: TokenList.Slice, /// The root AST node is assumed to be index 0. Since there can be no /// references to the root node, this means 0 is available to indicate null. nodes: NodeList.Slice, extra_data: []Node.Index, +mode: Mode, errors: []const Error, diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 25f9b670ae3c..c2f15f75d173 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -8780,22 +8780,36 @@ fn numberLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index, source_node: } } -fn failWithNumberError( - astgen: *AstGen, - err: std.zig.number_literal.Error, - token: Ast.TokenIndex, - bytes: []const u8, -) InnerError { - const note = err.noteWithSource(bytes); - const notes: []const u32 = if (note) |n| &.{try astgen.errNoteTok(token, "{s}", .{n})} else &.{}; - try astgen.appendErrorTokNotesOff( - token, - @as(u32, @intCast(err.offset())), - "{}", - .{err.fmtWithSource(bytes)}, - notes, - ); - return error.AnalysisFail; +fn failWithNumberError(astgen: *AstGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) InnerError { + const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null; + switch (err) { + .leading_zero => if (is_float) { + return astgen.failTok(token, "number '{s}' has leading zero", .{bytes}); + } else { + return astgen.failTokNotes(token, "number '{s}' has leading zero", .{bytes}, &.{ + try astgen.errNoteTok(token, "use '0o' prefix for octal literals", .{}), + }); + }, + .digit_after_base => return astgen.failTok(token, "expected a digit after base prefix", .{}), + .upper_case_base => |i| return astgen.failOff(token, @intCast(i), "base prefix must be lowercase", .{}), + .invalid_float_base => |i| return astgen.failOff(token, @intCast(i), "invalid base for float literal", .{}), + .repeated_underscore => |i| return astgen.failOff(token, @intCast(i), "repeated digit separator", .{}), + .invalid_underscore_after_special => |i| return astgen.failOff(token, @intCast(i), "expected digit before digit separator", .{}), + .invalid_digit => |info| return astgen.failOff(token, @intCast(info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }), + .invalid_digit_exponent => |i| return astgen.failOff(token, @intCast(i), "invalid digit '{c}' in exponent", .{bytes[i]}), + .duplicate_exponent => |i| return astgen.failOff(token, @intCast(i), "duplicate exponent", .{}), + .exponent_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before exponent", .{}), + .special_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before '{c}'", .{bytes[i]}), + .trailing_special => |i| return astgen.failOff(token, @intCast(i), "expected digit after '{c}'", .{bytes[i - 1]}), + .trailing_underscore => |i| return astgen.failOff(token, @intCast(i), "trailing digit separator", .{}), + .duplicate_period => unreachable, // Validated by tokenizer + .invalid_character => unreachable, // Validated by tokenizer + .invalid_exponent_sign => |i| { + assert(bytes.len >= 2 and bytes[0] == '0' and bytes[1] == 'x'); // Validated by tokenizer + return astgen.failOff(token, @intCast(i), "sign '{c}' cannot follow digit '{c}' in hex base", .{ bytes[i], bytes[i - 1] }); + }, + .period_after_exponent => |i| return astgen.failOff(token, @intCast(i), "unexpected period after exponent", .{}), + } } fn asmExpr( @@ -11384,20 +11398,9 @@ fn parseStrLit( } } -fn failWithStrLitError( - astgen: *AstGen, - err: std.zig.string_literal.Error, - token: Ast.TokenIndex, - bytes: []const u8, - offset: u32, -) InnerError { +fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError { const raw_string = bytes[offset..]; - return astgen.failOff( - token, - offset + @as(u32, @intCast(err.offset())), - "{}", - .{err.fmtWithSource(raw_string)}, - ); + return err.lower(raw_string, offset, AstGen.failOff, .{ astgen, token }); } fn failNode( @@ -11515,7 +11518,7 @@ fn appendErrorTokNotesOff( comptime format: []const u8, args: anytype, notes: []const u32, -) Allocator.Error!void { +) !void { @branchHint(.cold); const gpa = astgen.gpa; const string_bytes = &astgen.string_bytes; @@ -11644,17 +11647,32 @@ fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice { } fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice { + const tree = astgen.tree; + const node_datas = tree.nodes.items(.data); + + const start = node_datas[node].lhs; + const end = node_datas[node].rhs; + const gpa = astgen.gpa; - const data = astgen.tree.nodes.items(.data); const string_bytes = &astgen.string_bytes; const str_index = string_bytes.items.len; - var parser = std.zig.string_literal.multilineParser(string_bytes.writer(gpa)); - var tok_i = data[node].lhs; - while (tok_i <= data[node].rhs) : (tok_i += 1) { - try parser.line(astgen.tree.tokenSlice(tok_i)); + // First line: do not append a newline. + var tok_i = start; + { + const slice = tree.tokenSlice(tok_i); + const line_bytes = slice[2..]; + try string_bytes.appendSlice(gpa, line_bytes); + tok_i += 1; + } + // Following lines: each line prepends a newline. + while (tok_i <= end) : (tok_i += 1) { + const slice = tree.tokenSlice(tok_i); + const line_bytes = slice[2..]; + try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1); + string_bytes.appendAssumeCapacity('\n'); + string_bytes.appendSliceAssumeCapacity(line_bytes); } - const len = string_bytes.items.len - str_index; try string_bytes.append(gpa, 0); return IndexSlice{ diff --git a/lib/std/zig/number_literal.zig b/lib/std/zig/number_literal.zig index 40b8c44c176d..5ff027a43f67 100644 --- a/lib/std/zig/number_literal.zig +++ b/lib/std/zig/number_literal.zig @@ -58,40 +58,6 @@ pub const Error = union(enum) { invalid_exponent_sign: usize, /// Period comes directly after exponent. period_after_exponent: usize, - - pub fn fmtWithSource(self: Error, bytes: []const u8) std.fmt.Formatter(formatErrorWithSource) { - return .{ .data = .{ .err = self, .bytes = bytes } }; - } - - pub fn noteWithSource(self: Error, bytes: []const u8) ?[]const u8 { - if (self == .leading_zero) { - const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null; - if (!is_float) return "use '0o' prefix for octal literals"; - } - return null; - } - - pub fn offset(self: Error) usize { - return switch (self) { - .leading_zero => 0, - .digit_after_base => 0, - .upper_case_base => |i| i, - .invalid_float_base => |i| i, - .repeated_underscore => |i| i, - .invalid_underscore_after_special => |i| i, - .invalid_digit => |e| e.i, - .invalid_digit_exponent => |i| i, - .duplicate_period => 0, - .duplicate_exponent => |i| i, - .exponent_after_underscore => |i| i, - .special_after_underscore => |i| i, - .trailing_special => |i| i, - .trailing_underscore => |i| i, - .invalid_character => |i| i, - .invalid_exponent_sign => |i| i, - .period_after_exponent => |i| i, - }; - } }; const FormatWithSource = struct { @@ -99,42 +65,6 @@ const FormatWithSource = struct { err: Error, }; -fn formatErrorWithSource( - self: FormatWithSource, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = options; - _ = fmt; - switch (self.err) { - .leading_zero => try writer.print("number '{s}' has leading zero", .{self.bytes}), - .digit_after_base => try writer.writeAll("expected a digit after base prefix"), - .upper_case_base => try writer.writeAll("base prefix must be lowercase"), - .invalid_float_base => try writer.writeAll("invalid base for float literal"), - .repeated_underscore => try writer.writeAll("repeated digit separator"), - .invalid_underscore_after_special => try writer.writeAll("expected digit before digit separator"), - .invalid_digit => |info| try writer.print("invalid digit '{c}' for {s} base", .{ self.bytes[info.i], @tagName(info.base) }), - .invalid_digit_exponent => |i| try writer.print("invalid digit '{c}' in exponent", .{self.bytes[i]}), - .duplicate_exponent => try writer.writeAll("duplicate exponent"), - .exponent_after_underscore => try writer.writeAll("expected digit before exponent"), - .special_after_underscore => |i| try writer.print("expected digit before '{c}'", .{self.bytes[i]}), - .trailing_special => |i| try writer.print("expected digit after '{c}'", .{self.bytes[i - 1]}), - .trailing_underscore => try writer.writeAll("trailing digit separator"), - .duplicate_period => try writer.writeAll("duplicate period"), - .invalid_character => try writer.writeAll("invalid character"), - .invalid_exponent_sign => |i| { - const hex = self.bytes.len >= 2 and self.bytes[0] == '0' and self.bytes[1] == 'x'; - if (hex) { - try writer.print("sign '{c}' cannot follow digit '{c}' in hex base", .{ self.bytes[i], self.bytes[i - 1] }); - } else { - try writer.print("sign '{c}' cannot follow digit '{c}' in current base", .{ self.bytes[i], self.bytes[i - 1] }); - } - }, - .period_after_exponent => try writer.writeAll("unexpected period after exponent"), - } -} - /// Parse Zig number literal accepted by fmt.parseInt, fmt.parseFloat and big_int.setString. /// Valid for any input. pub fn parseNumberLiteral(bytes: []const u8) Result { diff --git a/lib/std/zig/string_literal.zig b/lib/std/zig/string_literal.zig index daba9b147f39..716a9b90f01c 100644 --- a/lib/std/zig/string_literal.zig +++ b/lib/std/zig/string_literal.zig @@ -43,7 +43,7 @@ pub const Error = union(enum) { pub fn lower( err: Error, raw_string: []const u8, - off: u32, + offset: u32, comptime func: anytype, first_args: anytype, ) @typeInfo(@TypeOf(func)).@"fn".return_type.? { @@ -66,81 +66,15 @@ pub const Error = union(enum) { .empty_char_literal => .{ "empty character literal", .{} }, }; return @call(.auto, func, first_args ++ .{ - off + bad_index, + offset + bad_index, fmt_str, args, }); }, } } - - pub fn fmtWithSource(self: Error, raw_string: []const u8) std.fmt.Formatter(formatErrorWithSource) { - return .{ .data = .{ .err = self, .raw_string = raw_string } }; - } - - pub fn offset(self: Error) usize { - return switch (self) { - .invalid_escape_character => |i| i, - .expected_hex_digit => |i| i, - .empty_unicode_escape_sequence => |i| i, - .expected_hex_digit_or_rbrace => |i| i, - .invalid_unicode_codepoint => |i| i, - .expected_lbrace => |i| i, - .expected_rbrace => |i| i, - .expected_single_quote => |i| i, - .invalid_character => |i| i, - .empty_char_literal => 0, - }; - } }; -const FormatWithSource = struct { - raw_string: []const u8, - err: Error, -}; - -fn formatErrorWithSource( - self: FormatWithSource, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - _ = options; - _ = fmt; - switch (self.err) { - .invalid_escape_character => |bad_index| { - try writer.print("invalid escape character: '{c}'", .{self.raw_string[bad_index]}); - }, - .expected_hex_digit => |bad_index| { - try writer.print("expected hex digit, found '{c}'", .{self.raw_string[bad_index]}); - }, - .empty_unicode_escape_sequence => { - try writer.writeAll("empty unicode escape sequence"); - }, - .expected_hex_digit_or_rbrace => |bad_index| { - try writer.print("expected hex digit or '}}', found '{c}'", .{self.raw_string[bad_index]}); - }, - .invalid_unicode_codepoint => { - try writer.writeAll("unicode escape does not correspond to a valid unicode scalar value"); - }, - .expected_lbrace => |bad_index| { - try writer.print("expected '{{', found '{c}", .{self.raw_string[bad_index]}); - }, - .expected_rbrace => |bad_index| { - try writer.print("expected '}}', found '{c}", .{self.raw_string[bad_index]}); - }, - .expected_single_quote => |bad_index| { - try writer.print("expected single quote ('), found '{c}", .{self.raw_string[bad_index]}); - }, - .invalid_character => |bad_index| { - try writer.print("invalid byte in string or character literal: '{c}'", .{self.raw_string[bad_index]}); - }, - .empty_char_literal => { - try writer.print("empty character literal", .{}); - }, - } -} - /// Asserts the slice starts and ends with single-quotes. /// Returns an error if there is not exactly one UTF-8 codepoint in between. pub fn parseCharLiteral(slice: []const u8) ParsedCharLiteral { @@ -348,7 +282,7 @@ test parseCharLiteral { /// Parses `bytes` as a Zig string literal and writes the result to the std.io.Writer type. /// Asserts `bytes` has '"' at beginning and end. -pub fn parseWrite(writer: anytype, bytes: []const u8) !Result { +pub fn parseWrite(writer: anytype, bytes: []const u8) error{OutOfMemory}!Result { assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"'); var index: usize = 1; @@ -413,151 +347,3 @@ test parseAlloc { try expect(eql(u8, "foo", try parseAlloc(alloc, "\"f\x6f\x6f\""))); try expect(eql(u8, "f💯", try parseAlloc(alloc, "\"f\u{1f4af}\""))); } - -/// Parses one line at a time of a multiline Zig string literal to a std.io.Writer type. Does not append a null terminator. -pub fn MultilineParser(comptime Writer: type) type { - return struct { - writer: Writer, - first_line: bool, - - pub fn init(writer: Writer) @This() { - return .{ - .writer = writer, - .first_line = true, - }; - } - - /// Parse one line of a multiline string, writing the result to the writer prepending a - /// newline if necessary. - /// - /// Asserts bytes begins with "\\". The line may be terminated with '\n' or "\r\n", but may - /// not contain any interior newlines. - pub fn line(self: *@This(), bytes: []const u8) Writer.Error!void { - assert(bytes.len >= 2 and bytes[0] == '\\' and bytes[1] == '\\'); - var terminator_len: usize = 0; - terminator_len += @intFromBool(bytes[bytes.len - 1] == '\n'); - terminator_len += @intFromBool(bytes[bytes.len - 2] == '\r'); - if (self.first_line) { - self.first_line = false; - } else { - try self.writer.writeByte('\n'); - } - try self.writer.writeAll(bytes[2 .. bytes.len - terminator_len]); - } - }; -} - -pub fn multilineParser(writer: anytype) MultilineParser(@TypeOf(writer)) { - return MultilineParser(@TypeOf(writer)).init(writer); -} - -test "parse multiline" { - // Varying newlines - { - { - var parsed = std.ArrayList(u8).init(std.testing.allocator); - defer parsed.deinit(); - const writer = parsed.writer(); - var parser = multilineParser(writer); - try parser.line("\\\\foo"); - try std.testing.expectEqualStrings("foo", parsed.items); - try parser.line("\\\\bar"); - try std.testing.expectEqualStrings("foo\nbar", parsed.items); - } - - { - const temp = - \\foo - \\bar - ; - try std.testing.expectEqualStrings("foo\nbar", temp); - var parsed = std.ArrayList(u8).init(std.testing.allocator); - defer parsed.deinit(); - const writer = parsed.writer(); - var parser = multilineParser(writer); - try parser.line("\\\\foo"); - try std.testing.expectEqualStrings("foo", parsed.items); - // XXX: this adds the newline but like...does the input ever actually have a newline there? - try parser.line("\\\\bar\n"); - try std.testing.expectEqualStrings("foo\nbar", parsed.items); - } - - { - var parsed = std.ArrayList(u8).init(std.testing.allocator); - defer parsed.deinit(); - const writer = parsed.writer(); - var parser = multilineParser(writer); - try parser.line("\\\\foo"); - try std.testing.expectEqualStrings("foo", parsed.items); - try parser.line("\\\\bar\r\n"); - try std.testing.expectEqualStrings("foo\nbar", parsed.items); - } - - { - var parsed = std.ArrayList(u8).init(std.testing.allocator); - defer parsed.deinit(); - const writer = parsed.writer(); - var parser = multilineParser(writer); - try parser.line("\\\\foo\n"); - try std.testing.expectEqualStrings("foo", parsed.items); - try parser.line("\\\\bar"); - try std.testing.expectEqualStrings("foo\nbar", parsed.items); - } - - { - var parsed = std.ArrayList(u8).init(std.testing.allocator); - defer parsed.deinit(); - const writer = parsed.writer(); - var parser = multilineParser(writer); - try parser.line("\\\\foo\r\n"); - try std.testing.expectEqualStrings("foo", parsed.items); - try parser.line("\\\\bar"); - try std.testing.expectEqualStrings("foo\nbar", parsed.items); - } - } - - // Empty lines - { - { - var parsed = std.ArrayList(u8).init(std.testing.allocator); - defer parsed.deinit(); - const writer = parsed.writer(); - var parser = multilineParser(writer); - try parser.line("\\\\"); - try std.testing.expectEqualStrings("", parsed.items); - try parser.line("\\\\"); - try std.testing.expectEqualStrings("\n", parsed.items); - try parser.line("\\\\foo"); - try std.testing.expectEqualStrings("\n\nfoo", parsed.items); - try parser.line("\\\\bar"); - try std.testing.expectEqualStrings("\n\nfoo\nbar", parsed.items); - } - - { - var parsed = std.ArrayList(u8).init(std.testing.allocator); - defer parsed.deinit(); - const writer = parsed.writer(); - var parser = multilineParser(writer); - try parser.line("\\\\foo"); - try std.testing.expectEqualStrings("foo", parsed.items); - try parser.line("\\\\"); - try std.testing.expectEqualStrings("foo\n", parsed.items); - try parser.line("\\\\bar"); - try std.testing.expectEqualStrings("foo\n\nbar", parsed.items); - try parser.line("\\\\"); - try std.testing.expectEqualStrings("foo\n\nbar\n", parsed.items); - } - } - - // No escapes - { - var parsed = std.ArrayList(u8).init(std.testing.allocator); - defer parsed.deinit(); - const writer = parsed.writer(); - var parser = multilineParser(writer); - try parser.line("\\\\no \\n escape"); - try std.testing.expectEqualStrings("no \\n escape", parsed.items); - try parser.line("\\\\still no \\n escape"); - try std.testing.expectEqualStrings("no \\n escape\nstill no \\n escape", parsed.items); - } -} diff --git a/lib/std/zon.zig b/lib/std/zon.zig index 232229eb3df8..4288c52729ab 100644 --- a/lib/std/zon.zig +++ b/lib/std/zon.zig @@ -9,17 +9,13 @@ //! * number literals (including `nan` and `inf`) //! * character literals //! * enum literals -//! * `null` and `void` literals +//! * `null` literals //! * string literals //! * multiline string literals //! -//! Supported Zig containers: +//! Supported Zig container types: //! * anonymous struct literals //! * anonymous tuple literals -//! * slices -//! * notated as a reference to a tuple literal -//! * this syntax will likely be removed in the future, at which point ZON will not distinguish -//! between slices and tuples //! //! Here is an example ZON object: //! ```zon @@ -27,7 +23,7 @@ //! .a = 1.5, //! .b = "hello, world!", //! .c = .{ true, false }, -//! .d = &.{ 1, 2, 3 }, +//! .d = .{ 1, 2, 3 }, //! } //! ``` //! @@ -36,25 +32,22 @@ //! "This string is a valid ZON object." //! ``` //! -//! \* ZON is not currently a true subset of Zig, because it supports `nan` and +//! * ZON is not currently a true subset of Zig, because it supports `nan` and //! `inf` literals, which Zig does not. //! //! # Deserialization //! -//! The simplest way to deserialize ZON at runtime is `parseFromSlice`. (For reading ZON at +//! The simplest way to deserialize ZON at runtime is `parseFromSlice`. (For parsing ZON at //! comptime, you can use `@import`.) //! -//! If you need lower level control, or more detailed diagnostics, you can generate the AST yourself -//! with `std.zig.Ast.parse` and then deserialize it with: -//! * `parseFromAst` -//! * `parseFromAstNoAlloc` -//! -//! If you'd like to deserialize just part of an AST, you can use: -//! * `parseFromAstNode` -//! * `parseFromAstNodeNoAlloc` +//! Parsing from individual Zoir nodes is also available: +//! * `parseFromZoir` +//! * `parseFromZoirNode` +//! * `parseFromZoirNode` +//! * `parseFromZoirNodeNoAlloc` //! //! If you need lower level control than provided by this module, you can operate directly on the -//! results of `std.zig.Ast.parse`. +//! results of `std.zig.Zoir` directly. This module is a good example of how that can be done. //! //! //! # Serialization @@ -72,13 +65,13 @@ //! Note that serializing floats with more than 64 bits may result in a loss of precision //! (see https://github.com/ziglang/zig/issues/1181). -pub const ParseOptions = @import("zon/parse.zig").ParseOptions; -pub const ParseStatus = @import("zon/parse.zig").ParseStatus; +pub const ParseOptions = @import("zon/parse.zig").Options; +pub const ParseStatus = @import("zon/parse.zig").Status; pub const parseFromSlice = @import("zon/parse.zig").parseFromSlice; -pub const parseFromAst = @import("zon/parse.zig").parseFromAst; -pub const parseFromAstNoAlloc = @import("zon/parse.zig").parseFromAstNoAlloc; -pub const parseFromAstNode = @import("zon/parse.zig").parseFromAstNode; -pub const parseFromAstNodeNoAlloc = @import("zon/parse.zig").parseFromAstNodeNoAlloc; +pub const parseFromZoir = @import("zon/parse.zig").parseFromZoir; +pub const parseFromZoirNoAlloc = @import("zon/parse.zig").parseFromZoirNoAlloc; +pub const parseFromZoirNode = @import("zon/parse.zig").parseFromZoirNode; +pub const parseFromZoirNodeNoAlloc = @import("zon/parse.zig").parseFromZoirNodeNoAlloc; pub const parseFree = @import("zon/parse.zig").parseFree; pub const StringifierOptions = @import("zon/stringify.zig").StringifierOptions; diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 74a9f821c88b..5643f661684a 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -16,7 +16,7 @@ zoir: Zoir, status: ?*Status, /// Configuration for the runtime parser. -pub const ParseOptions = struct { +pub const Options = struct { /// If true, unknown fields do not error. ignore_unknown_fields: bool = false, /// If true, the parser cleans up partially parsed values on error. This requires some extra @@ -258,7 +258,7 @@ pub fn parseFromSlice( gpa: Allocator, source: [:0]const u8, status: ?*Status, - comptime options: ParseOptions, + comptime options: Options, ) error{ OutOfMemory, ParseZon }!T { if (status) |s| s.assertEmpty(); @@ -286,7 +286,7 @@ pub fn parseFromZoir( ast: Ast, zoir: Zoir, status: ?*Status, - comptime options: ParseOptions, + comptime options: Options, ) error{ OutOfMemory, ParseZon }!T { return parseFromZoirNode(T, gpa, ast, zoir, .root, status, options); } @@ -299,7 +299,7 @@ pub fn parseFromZoirNoAlloc( ast: Ast, zoir: Zoir, status: ?*Status, - comptime options: ParseOptions, + comptime options: Options, ) error{ParseZon}!T { return parseFromZoirNodeNoAlloc(T, ast, zoir, .root, status, options); } @@ -322,7 +322,7 @@ pub fn parseFromZoirNode( zoir: Zoir, node: Zoir.Node.Index, status: ?*Status, - comptime options: ParseOptions, + comptime options: Options, ) error{ OutOfMemory, ParseZon }!T { if (status) |s| { s.assertEmpty(); @@ -351,7 +351,7 @@ pub fn parseFromZoirNodeNoAlloc( zoir: Zoir, node: Zoir.Node.Index, status: ?*Status, - comptime options: ParseOptions, + comptime options: Options, ) error{ParseZon}!T { if (comptime requiresAllocator(T)) { @compileError(@typeName(T) ++ ": requires allocator"); @@ -474,7 +474,7 @@ pub fn parseFree(gpa: Allocator, value: anytype) void { fn parseExpr( self: *@This(), comptime T: type, - comptime options: ParseOptions, + comptime options: Options, node: Zoir.Node.Index, ) error{ OutOfMemory, ParseZon }!T { // Keep in sync with parseFree, stringify, and requiresAllocator. @@ -499,7 +499,7 @@ fn parseExpr( fn parseOptional( self: *@This(), comptime T: type, - comptime options: ParseOptions, + comptime options: Options, node: Zoir.Node.Index, ) error{ OutOfMemory, ParseZon }!T { if (node.get(self.zoir) == .null) { @@ -533,7 +533,7 @@ test "std.zon optional" { fn parseUnion( self: *@This(), comptime T: type, - comptime options: ParseOptions, + comptime options: Options, node: Zoir.Node.Index, ) error{ OutOfMemory, ParseZon }!T { const @"union" = @typeInfo(T).@"union"; @@ -712,7 +712,7 @@ test "std.zon unions" { fn parseStruct( self: *@This(), comptime T: type, - comptime options: ParseOptions, + comptime options: Options, node: Zoir.Node.Index, ) error{ OutOfMemory, ParseZon }!T { const fields: std.meta.fieldInfo(Zoir.Node, .struct_literal).type = switch (node.get(self.zoir)) { @@ -970,7 +970,7 @@ test "std.zon structs" { fn parseTuple( self: *@This(), comptime T: type, - comptime options: ParseOptions, + comptime options: Options, node: Zoir.Node.Index, ) error{ OutOfMemory, ParseZon }!T { const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) { @@ -1072,7 +1072,7 @@ test "std.zon tuples" { fn parseArray( self: *@This(), comptime T: type, - comptime options: ParseOptions, + comptime options: Options, node: Zoir.Node.Index, ) error{ OutOfMemory, ParseZon }!T { const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) { @@ -1284,7 +1284,7 @@ test "std.zon arrays and slices" { fn parsePointer( self: *@This(), comptime T: type, - comptime options: ParseOptions, + comptime options: Options, node: Zoir.Node.Index, ) error{ OutOfMemory, ParseZon }!T { switch (node.get(self.zoir)) { @@ -1325,7 +1325,7 @@ fn parseString( fn parseSlice( self: *@This(), comptime T: type, - comptime options: ParseOptions, + comptime options: Options, nodes: Zoir.Node.Index.Range, ) error{ OutOfMemory, ParseZon }!T { const pointer = @typeInfo(T).pointer; diff --git a/src/Air.zig b/src/Air.zig index 883991488c7c..4589bb1557cf 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -1020,11 +1020,6 @@ pub const Inst = struct { pub fn toType(ref: Ref) Type { return Type.fromInterned(ref.toInterned().?); } - - pub fn toTypeAllowNone(ref: Ref) ?Type { - if (ref == .none) return null; - return ref.toType(); - } }; /// All instructions have an 8-byte payload, which is contained within diff --git a/src/Package/Manifest.zig b/src/Package/Manifest.zig index 7691ac3405e1..4eed6cc386e6 100644 --- a/src/Package/Manifest.zig +++ b/src/Package/Manifest.zig @@ -14,7 +14,6 @@ pub const Digest = [Hash.digest_length]u8; pub const multihash_len = 1 + 1 + Hash.digest_length; pub const multihash_hex_digest_len = 2 * multihash_len; pub const MultiHashHexDigest = [multihash_hex_digest_len]u8; -const AstGen = std.zig.AstGen; pub const Dependency = struct { location: Location, @@ -457,6 +456,7 @@ const Parse = struct { return duped; } + /// TODO: try to DRY this with AstGen.parseStrLit fn parseStrLit( p: *Parse, token: Ast.TokenIndex, @@ -470,13 +470,95 @@ const Parse = struct { buf.* = buf_managed.moveToUnmanaged(); switch (try result) { .success => {}, - .failure => |err| try appendErrorOff( - p, - token, - offset + @as(u32, @intCast(err.offset())), - "{}", - err.fmtWithSource(raw_string), - ), + .failure => |err| try p.appendStrLitError(err, token, bytes, offset), + } + } + + /// TODO: try to DRY this with AstGen.failWithStrLitError + fn appendStrLitError( + p: *Parse, + err: std.zig.string_literal.Error, + token: Ast.TokenIndex, + bytes: []const u8, + offset: u32, + ) Allocator.Error!void { + const raw_string = bytes[offset..]; + switch (err) { + .invalid_escape_character => |bad_index| { + try p.appendErrorOff( + token, + offset + @as(u32, @intCast(bad_index)), + "invalid escape character: '{c}'", + .{raw_string[bad_index]}, + ); + }, + .expected_hex_digit => |bad_index| { + try p.appendErrorOff( + token, + offset + @as(u32, @intCast(bad_index)), + "expected hex digit, found '{c}'", + .{raw_string[bad_index]}, + ); + }, + .empty_unicode_escape_sequence => |bad_index| { + try p.appendErrorOff( + token, + offset + @as(u32, @intCast(bad_index)), + "empty unicode escape sequence", + .{}, + ); + }, + .expected_hex_digit_or_rbrace => |bad_index| { + try p.appendErrorOff( + token, + offset + @as(u32, @intCast(bad_index)), + "expected hex digit or '}}', found '{c}'", + .{raw_string[bad_index]}, + ); + }, + .invalid_unicode_codepoint => |bad_index| { + try p.appendErrorOff( + token, + offset + @as(u32, @intCast(bad_index)), + "unicode escape does not correspond to a valid unicode scalar value", + .{}, + ); + }, + .expected_lbrace => |bad_index| { + try p.appendErrorOff( + token, + offset + @as(u32, @intCast(bad_index)), + "expected '{{', found '{c}", + .{raw_string[bad_index]}, + ); + }, + .expected_rbrace => |bad_index| { + try p.appendErrorOff( + token, + offset + @as(u32, @intCast(bad_index)), + "expected '}}', found '{c}", + .{raw_string[bad_index]}, + ); + }, + .expected_single_quote => |bad_index| { + try p.appendErrorOff( + token, + offset + @as(u32, @intCast(bad_index)), + "expected single quote ('), found '{c}", + .{raw_string[bad_index]}, + ); + }, + .invalid_character => |bad_index| { + try p.appendErrorOff( + token, + offset + @as(u32, @intCast(bad_index)), + "invalid byte in string or character literal: '{c}'", + .{raw_string[bad_index]}, + ); + }, + .empty_char_literal => { + try p.appendErrorOff(token, offset, "empty character literal", .{}); + }, } } diff --git a/src/Zcu.zig b/src/Zcu.zig index a2f85719059f..6c0951541e98 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -457,7 +457,7 @@ pub const File = struct { /// successful, this field is unloaded. prev_zir: ?*Zir = null, - /// Whether the file is Zig or ZON. This filed is always populated. + /// Whether the file is Zig or ZON. This field is always populated. mode: Ast.Mode, pub const Status = enum { diff --git a/src/zon.zig b/src/zon.zig index 9cd839817849..2580d619a519 100644 --- a/src/zon.zig +++ b/src/zon.zig @@ -107,7 +107,7 @@ fn ident(self: LowerZon, token: Ast.TokenIndex) !Ident { const gpa = self.sema.gpa; const raw_string = bytes[1..bytes.len]; - var parsed = std.ArrayListUnmanaged(u8){}; + var parsed: std.ArrayListUnmanaged(u8) = .{}; defer parsed.deinit(gpa); switch (try std.zig.string_literal.parseWrite(parsed.writer(gpa), raw_string)) { diff --git a/test/behavior/zon.zig b/test/behavior/zon.zig index 2e68496b6d42..3edab54d2b3f 100644 --- a/test/behavior/zon.zig +++ b/test/behavior/zon.zig @@ -144,16 +144,16 @@ test "slices, arrays, tuples" { { const expected_slice: []const u8 = &.{1}; - const found_slice: []const u8 = @import("zon/slice-1.zon"); + const found_slice: []const u8 = @import("zon/slice1_no_newline.zon"); try expectEqualSlices(u8, expected_slice, found_slice); const expected_array: [1]u8 = .{1}; - const found_array: [1]u8 = @import("zon/slice-1.zon"); + const found_array: [1]u8 = @import("zon/slice1_no_newline.zon"); try expectEqual(expected_array, found_array); const T = struct { u8 }; const expected_tuple: T = .{1}; - const found_tuple: T = @import("zon/slice-1.zon"); + const found_tuple: T = @import("zon/slice1_no_newline.zon"); try expectEqual(expected_tuple, found_tuple); } diff --git a/test/behavior/zon/escaped_struct.zon b/test/behavior/zon/escaped_struct.zon index c5cb978f3303..f304aa632752 100644 --- a/test/behavior/zon/escaped_struct.zon +++ b/test/behavior/zon/escaped_struct.zon @@ -1,2 +1,2 @@ -// zig fmt: off + .{ .@"0" = 1.5, .@"foo" = 2 } diff --git a/test/behavior/zon/slice-1.zon b/test/behavior/zon/slice1_no_newline.zon similarity index 100% rename from test/behavior/zon/slice-1.zon rename to test/behavior/zon/slice1_no_newline.zon