1//! The simplest way to parse ZON at runtime is to use `fromSlice`/`fromSliceAlloc`.
2//!
3//! Note that if you need to parse ZON at compile time, you may use `@import`.
4//!
5//! Parsing from individual Zoir nodes is also available:
6//! * `fromZoir`/`fromZoirAlloc`
7//! * `fromZoirNode`/`fromZoirNodeAlloc`
8//!
9//! For lower level control over parsing, see `std.zig.Zoir`.
10
11const std = @import("std");
12const Allocator = std.mem.Allocator;
13const Ast = std.zig.Ast;
14const Zoir = std.zig.Zoir;
15const ZonGen = std.zig.ZonGen;
16const TokenIndex = std.zig.Ast.TokenIndex;
17const Base = std.zig.number_literal.Base;
18const StrLitErr = std.zig.string_literal.Error;
19const NumberLiteralError = std.zig.number_literal.Error;
20const assert = std.debug.assert;
21const ArrayList = std.ArrayList;
22
23/// Rename when adding or removing support for a type.
24const valid_types = {};
25
26/// Configuration for the runtime parser.
27pub const Options = struct {
28 /// If true, unknown fields do not error.
29 ignore_unknown_fields: bool = false,
30 /// If true, the parser cleans up partially parsed values on error. This requires some extra
31 /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because
32 /// you're using arena allocation.)
33 free_on_error: bool = true,
34};
35
36pub const Error = union(enum) {
37 zoir: Zoir.CompileError,
38 type_check: Error.TypeCheckFailure,
39
40 pub const Note = union(enum) {
41 zoir: Zoir.CompileError.Note,
42 type_check: TypeCheckFailure.Note,
43
44 pub const Iterator = struct {
45 index: usize = 0,
46 err: Error,
47 diag: *const Diagnostics,
48
49 pub fn next(self: *@This()) ?Note {
50 switch (self.err) {
51 .zoir => |err| {
52 if (self.index >= err.note_count) return null;
53 const note = err.getNotes(self.diag.zoir)[self.index];
54 self.index += 1;
55 return .{ .zoir = note };
56 },
57 .type_check => |err| {
58 if (self.index >= err.getNoteCount()) return null;
59 const note = err.getNote(self.index);
60 self.index += 1;
61 return .{ .type_check = note };
62 },
63 }
64 }
65 };
66
67 fn formatMessage(self: []const u8, w: *std.Io.Writer) std.Io.Writer.Error!void {
68 // Just writes the string for now, but we're keeping this behind a formatter so we have
69 // the option to extend it in the future to print more advanced messages (like `Error`
70 // does) without breaking the API.
71 try w.writeAll(self);
72 }
73
74 pub fn fmtMessage(self: Note, diag: *const Diagnostics) std.fmt.Alt([]const u8, Note.formatMessage) {
75 return .{ .data = switch (self) {
76 .zoir => |note| note.msg.get(diag.zoir),
77 .type_check => |note| note.msg,
78 } };
79 }
80
81 pub fn getLocation(self: Note, diag: *const Diagnostics) Ast.Location {
82 switch (self) {
83 .zoir => |note| return zoirErrorLocation(diag.ast, note.token, note.node_or_offset),
84 .type_check => |note| return diag.ast.tokenLocation(note.offset, note.token),
85 }
86 }
87 };
88
89 pub const Iterator = struct {
90 index: usize = 0,
91 diag: *const Diagnostics,
92
93 pub fn next(self: *@This()) ?Error {
94 if (self.index < self.diag.zoir.compile_errors.len) {
95 const result: Error = .{ .zoir = self.diag.zoir.compile_errors[self.index] };
96 self.index += 1;
97 return result;
98 }
99
100 if (self.diag.type_check) |err| {
101 if (self.index == self.diag.zoir.compile_errors.len) {
102 const result: Error = .{ .type_check = err };
103 self.index += 1;
104 return result;
105 }
106 }
107
108 return null;
109 }
110 };
111
112 const TypeCheckFailure = struct {
113 const Note = struct {
114 token: Ast.TokenIndex,
115 offset: u32,
116 msg: []const u8,
117 owned: bool,
118
119 fn deinit(self: @This(), gpa: Allocator) void {
120 if (self.owned) gpa.free(self.msg);
121 }
122 };
123
124 message: []const u8,
125 owned: bool,
126 token: Ast.TokenIndex,
127 offset: u32,
128 note: ?@This().Note,
129
130 fn deinit(self: @This(), gpa: Allocator) void {
131 if (self.note) |note| note.deinit(gpa);
132 if (self.owned) gpa.free(self.message);
133 }
134
135 fn getNoteCount(self: @This()) usize {
136 return @intFromBool(self.note != null);
137 }
138
139 fn getNote(self: @This(), index: usize) @This().Note {
140 assert(index == 0);
141 return self.note.?;
142 }
143 };
144
145 const FormatMessage = struct {
146 err: Error,
147 diag: *const Diagnostics,
148 };
149
150 fn formatMessage(self: FormatMessage, w: *std.Io.Writer) std.Io.Writer.Error!void {
151 switch (self.err) {
152 .zoir => |err| try w.writeAll(err.msg.get(self.diag.zoir)),
153 .type_check => |tc| try w.writeAll(tc.message),
154 }
155 }
156
157 pub fn fmtMessage(self: @This(), diag: *const Diagnostics) std.fmt.Alt(FormatMessage, formatMessage) {
158 return .{ .data = .{
159 .err = self,
160 .diag = diag,
161 } };
162 }
163
164 pub fn getLocation(self: @This(), diag: *const Diagnostics) Ast.Location {
165 return switch (self) {
166 .zoir => |err| return zoirErrorLocation(
167 diag.ast,
168 err.token,
169 err.node_or_offset,
170 ),
171 .type_check => |err| return diag.ast.tokenLocation(err.offset, err.token),
172 };
173 }
174
175 pub fn iterateNotes(self: @This(), diag: *const Diagnostics) Note.Iterator {
176 return .{ .err = self, .diag = diag };
177 }
178
179 fn zoirErrorLocation(ast: Ast, maybe_token: Ast.OptionalTokenIndex, node_or_offset: u32) Ast.Location {
180 if (maybe_token.unwrap()) |token| {
181 var location = ast.tokenLocation(0, token);
182 location.column += node_or_offset;
183 return location;
184 } else {
185 const ast_node: Ast.Node.Index = @fromBackingInt(@intCast(node_or_offset));
186 const token = ast.nodeMainToken(ast_node);
187 return ast.tokenLocation(0, token);
188 }
189 }
190};
191
192/// Information about the success or failure of a parse.
193pub const Diagnostics = struct {
194 ast: Ast = .{
195 .source = "",
196 .tokens = .empty,
197 .nodes = .empty,
198 .extra_data = &.{},
199 .mode = .zon,
200 .errors = &.{},
201 },
202 zoir: Zoir = .{
203 .nodes = .empty,
204 .extra = &.{},
205 .limbs = &.{},
206 .string_bytes = &.{},
207 .compile_errors = &.{},
208 .error_notes = &.{},
209 },
210 type_check: ?Error.TypeCheckFailure = null,
211
212 fn assertEmpty(self: Diagnostics) void {
213 assert(self.ast.tokens.len == 0);
214 assert(self.zoir.nodes.len == 0);
215 assert(self.type_check == null);
216 }
217
218 pub fn deinit(self: *Diagnostics, gpa: Allocator) void {
219 self.ast.deinit(gpa);
220 self.zoir.deinit(gpa);
221 if (self.type_check) |tc| tc.deinit(gpa);
222 self.* = undefined;
223 }
224
225 pub fn iterateErrors(self: *const Diagnostics) Error.Iterator {
226 return .{ .diag = self };
227 }
228
229 pub fn format(self: *const @This(), w: *std.Io.Writer) std.Io.Writer.Error!void {
230 var errors = self.iterateErrors();
231 while (errors.next()) |err| {
232 const loc = err.getLocation(self);
233 const msg = err.fmtMessage(self);
234 try w.print("{d}:{d}: error: {f}\n", .{ loc.line + 1, loc.column + 1, msg });
235
236 var notes = err.iterateNotes(self);
237 while (notes.next()) |note| {
238 const note_loc = note.getLocation(self);
239 const note_msg = note.fmtMessage(self);
240 try w.print("{d}:{d}: note: {f}\n", .{
241 note_loc.line + 1,
242 note_loc.column + 1,
243 note_msg,
244 });
245 }
246 }
247 }
248};
249
250/// Parses the given slice as ZON.
251///
252/// Returns `error.OutOfMemory` on allocation failure, or `error.ParseZon` error if the ZON is
253/// invalid or can not be deserialized into type `T`.
254///
255/// When the parser returns `error.ParseZon`, it will also store a human readable explanation in
256/// `diag` if non null. If diag is not null, it must be initialized to `.{}`.
257///
258/// Asserts at compile time that the result type doesn't contain pointers. As such, the result
259/// doesn't need to be freed.
260///
261/// An allocator is still required for temporary allocations made during parsing.
262pub fn fromSlice(
263 T: type,
264 gpa: Allocator,
265 source: [:0]const u8,
266 diag: ?*Diagnostics,
267 options: Options,
268) error{ OutOfMemory, ParseZon }!T {
269 comptime assert(!requiresAllocator(T));
270 return fromSliceAlloc(T, gpa, source, diag, options);
271}
272
273/// Like `fromSlice`, but the result may contain pointers. To automatically free the result, see
274/// `free`.
275pub fn fromSliceAlloc(
276 /// The type to deserialize into. May not be or contain any of the following types:
277 /// * Any comptime-only type, except in a comptime field
278 /// * `type`
279 /// * `void`, except as a union payload
280 /// * `noreturn`
281 /// * An error set/error union
282 /// * A many-pointer or C-pointer
283 /// * An opaque type, including `anyopaque`
284 /// * An async frame type, including `anyframe` and `anyframe->T`
285 /// * A function
286 ///
287 /// All other types are valid. Unsupported types will fail at compile time.
288 T: type,
289 gpa: Allocator,
290 source: [:0]const u8,
291 diag: ?*Diagnostics,
292 options: Options,
293) error{ OutOfMemory, ParseZon }!T {
294 if (diag) |s| s.assertEmpty();
295
296 var ast = try std.zig.Ast.parse(gpa, source, .{ .mode = .zon });
297 defer if (diag == null) ast.deinit(gpa);
298 if (diag) |s| s.ast = ast;
299
300 // If there's no diagnostics, Zoir exists for the lifetime of this function. If there is a
301 // diagnostics, ownership is transferred to diagnostics.
302 var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false });
303 defer if (diag == null) zoir.deinit(gpa);
304
305 if (diag) |s| s.* = .{};
306 return fromZoirAlloc(T, gpa, ast, zoir, diag, options);
307}
308
309/// Like `fromSlice`, but operates on `Zoir` instead of ZON source.
310pub fn fromZoir(
311 T: type,
312 ast: Ast,
313 zoir: Zoir,
314 diag: ?*Diagnostics,
315 options: Options,
316) error{ParseZon}!T {
317 comptime assert(!requiresAllocator(T));
318 var buf: [0]u8 = .{};
319 var failing_allocator = std.heap.FixedBufferAllocator.init(&buf);
320 return fromZoirAlloc(
321 T,
322 failing_allocator.allocator(),
323 ast,
324 zoir,
325 diag,
326 options,
327 ) catch |err| switch (err) {
328 error.OutOfMemory => unreachable, // Checked by comptime assertion above
329 else => |e| return e,
330 };
331}
332
333/// Like `fromSliceAlloc`, but operates on `Zoir` instead of ZON source.
334pub fn fromZoirAlloc(
335 T: type,
336 gpa: Allocator,
337 ast: Ast,
338 zoir: Zoir,
339 diag: ?*Diagnostics,
340 options: Options,
341) error{ OutOfMemory, ParseZon }!T {
342 return fromZoirNodeAlloc(T, gpa, ast, zoir, .root, diag, options);
343}
344
345/// Like `fromZoir`, but the parse starts at `node` instead of root.
346pub fn fromZoirNode(
347 T: type,
348 ast: Ast,
349 zoir: Zoir,
350 node: Zoir.Node.Index,
351 diag: ?*Diagnostics,
352 options: Options,
353) error{ParseZon}!T {
354 comptime assert(!requiresAllocator(T));
355 var buf: [0]u8 = .{};
356 var failing_allocator = std.heap.FixedBufferAllocator.init(&buf);
357 return fromZoirNodeAlloc(
358 T,
359 failing_allocator.allocator(),
360 ast,
361 zoir,
362 node,
363 diag,
364 options,
365 ) catch |err| switch (err) {
366 error.OutOfMemory => unreachable, // Checked by comptime assertion above
367 else => |e| return e,
368 };
369}
370
371/// Like `fromZoirAlloc`, but the parse starts at `node` instead of root.
372pub fn fromZoirNodeAlloc(
373 T: type,
374 gpa: Allocator,
375 ast: Ast,
376 zoir: Zoir,
377 node: Zoir.Node.Index,
378 diag: ?*Diagnostics,
379 options: Options,
380) error{ OutOfMemory, ParseZon }!T {
381 comptime assert(canParseType(T));
382
383 if (diag) |s| {
384 s.assertEmpty();
385 s.ast = ast;
386 s.zoir = zoir;
387 }
388
389 if (zoir.hasCompileErrors()) {
390 return error.ParseZon;
391 }
392
393 var parser: Parser = .{
394 .gpa = gpa,
395 .ast = ast,
396 .zoir = zoir,
397 .options = options,
398 .diag = diag,
399 };
400
401 return parser.parseExpr(T, node);
402}
403
404/// Frees ZON values.
405///
406/// Provided for convenience, you may also free these values on your own using the same allocator
407/// passed into the parser.
408///
409/// Asserts at comptime that sufficient information is available via the type system to free this
410/// value. Untagged unions, for example, will fail this assert.
411pub fn free(gpa: Allocator, value: anytype) void {
412 const Value = @TypeOf(value);
413
414 _ = valid_types;
415 switch (@typeInfo(Value)) {
416 .bool, .int, .float, .@"enum" => {},
417 .pointer => |pointer| {
418 switch (pointer.size) {
419 .one => {
420 free(gpa, value.*);
421 gpa.destroy(value);
422 },
423 .slice => {
424 for (value) |item| {
425 free(gpa, item);
426 }
427 gpa.free(value);
428 },
429 .many, .c => comptime unreachable,
430 }
431 },
432 .array => {
433 freeArray(gpa, @TypeOf(value), &value);
434 },
435 .vector => |vector| {
436 const array: [vector.len]vector.child = value;
437 freeArray(gpa, @TypeOf(array), &array);
438 },
439 .@"struct" => |@"struct"| inline for (@"struct".field_names) |field_name| {
440 free(gpa, @field(value, field_name));
441 },
442 .@"union" => |@"union"| if (@"union".tag_type == null) {
443 if (comptime requiresAllocator(Value)) unreachable;
444 } else switch (value) {
445 inline else => |_, tag| {
446 free(gpa, @field(value, @tagName(tag)));
447 },
448 },
449 .optional => if (value) |some| {
450 free(gpa, some);
451 },
452 .void => {},
453 else => comptime unreachable,
454 }
455}
456
457fn freeArray(gpa: Allocator, comptime A: type, array: *const A) void {
458 for (array) |elem| free(gpa, elem);
459}
460
461fn requiresAllocator(T: type) bool {
462 _ = valid_types;
463 return switch (@typeInfo(T)) {
464 .pointer => true,
465 .array => |array| return array.len > 0 and requiresAllocator(array.child),
466 .@"struct" => |@"struct"| inline for (@"struct".field_types) |field_type| {
467 if (requiresAllocator(field_type)) {
468 break true;
469 }
470 } else false,
471 .@"union" => |@"union"| inline for (@"union".field_types) |field_type| {
472 if (requiresAllocator(field_type)) {
473 break true;
474 }
475 } else false,
476 .optional => |optional| requiresAllocator(optional.child),
477 .vector => |vector| return vector.len > 0 and requiresAllocator(vector.child),
478 else => false,
479 };
480}
481
482const Parser = struct {
483 gpa: Allocator,
484 ast: Ast,
485 zoir: Zoir,
486 diag: ?*Diagnostics,
487 options: Options,
488
489 const ParseExprError = error{ ParseZon, OutOfMemory };
490
491 fn parseExpr(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprError!T {
492 return self.parseExprInner(T, node) catch |err| switch (err) {
493 error.WrongType => return self.failExpectedType(T, node),
494 else => |e| return e,
495 };
496 }
497
498 const ParseExprInnerError = error{ ParseZon, OutOfMemory, WrongType };
499
500 fn parseExprInner(
501 self: *@This(),
502 T: type,
503 node: Zoir.Node.Index,
504 ) ParseExprInnerError!T {
505 if (T == Zoir.Node.Index) {
506 return node;
507 }
508
509 switch (@typeInfo(T)) {
510 .optional => |optional| if (node.get(self.zoir) == .null) {
511 return null;
512 } else {
513 return try self.parseExprInner(optional.child, node);
514 },
515 .bool => return self.parseBool(node),
516 .int => return self.parseInt(T, node),
517 .float => return self.parseFloat(T, node),
518 .@"enum" => return self.parseEnumLiteral(T, node),
519 .pointer => |pointer| switch (pointer.size) {
520 .one => {
521 const result = try self.gpa.create(pointer.child);
522 errdefer self.gpa.destroy(result);
523 result.* = try self.parseExprInner(pointer.child, node);
524 return result;
525 },
526 .slice => return self.parseSlicePointer(T, node),
527 else => comptime unreachable,
528 },
529 .array => return self.parseArray(T, node),
530 .vector => |vector| {
531 const A = [vector.len]vector.child;
532 return try self.parseArray(A, node);
533 },
534 .@"struct" => |@"struct"| if (@"struct".is_tuple)
535 return self.parseTuple(T, node)
536 else
537 return self.parseStruct(T, node),
538 .@"union" => return self.parseUnion(T, node),
539
540 else => comptime unreachable,
541 }
542 }
543
544 /// Prints a message of the form `expected T` where T is first converted to a ZON type. For
545 /// example, `**?**u8` becomes `?u8`, and types that involve user specified type names are just
546 /// referred to by the type of container.
547 fn failExpectedType(
548 self: @This(),
549 T: type,
550 node: Zoir.Node.Index,
551 ) error{ ParseZon, OutOfMemory } {
552 @branchHint(.cold);
553 return self.failExpectedTypeInner(T, false, node);
554 }
555
556 fn failExpectedTypeInner(
557 self: @This(),
558 T: type,
559 opt: bool,
560 node: Zoir.Node.Index,
561 ) error{ ParseZon, OutOfMemory } {
562 _ = valid_types;
563 switch (@typeInfo(T)) {
564 .@"struct" => |@"struct"| if (@"struct".is_tuple) {
565 if (opt) {
566 return self.failNode(node, "expected optional tuple");
567 } else {
568 return self.failNode(node, "expected tuple");
569 }
570 } else {
571 if (opt) {
572 return self.failNode(node, "expected optional struct");
573 } else {
574 return self.failNode(node, "expected struct");
575 }
576 },
577 .@"union" => if (opt) {
578 return self.failNode(node, "expected optional union");
579 } else {
580 return self.failNode(node, "expected union");
581 },
582 .array => if (opt) {
583 return self.failNode(node, "expected optional array");
584 } else {
585 return self.failNode(node, "expected array");
586 },
587 .pointer => |pointer| switch (pointer.size) {
588 .one => return self.failExpectedTypeInner(pointer.child, opt, node),
589 .slice => {
590 if (pointer.child == u8 and
591 pointer.attrs.@"const" and
592 (pointer.sentinel() == null or pointer.sentinel() == 0) and
593 (pointer.attrs.@"align" == null or pointer.attrs.@"align" == 1))
594 {
595 if (opt) {
596 return self.failNode(node, "expected optional string");
597 } else {
598 return self.failNode(node, "expected string");
599 }
600 } else {
601 if (opt) {
602 return self.failNode(node, "expected optional array");
603 } else {
604 return self.failNode(node, "expected array");
605 }
606 }
607 },
608 else => comptime unreachable,
609 },
610 .vector, .bool, .int, .float => if (opt) {
611 return self.failNodeFmt(node, "expected type '{s}'", .{@typeName(?T)});
612 } else {
613 return self.failNodeFmt(node, "expected type '{s}'", .{@typeName(T)});
614 },
615 .@"enum" => if (opt) {
616 return self.failNode(node, "expected optional enum literal");
617 } else {
618 return self.failNode(node, "expected enum literal");
619 },
620 .optional => |optional| {
621 return self.failExpectedTypeInner(optional.child, true, node);
622 },
623 else => comptime unreachable,
624 }
625 }
626
627 fn parseBool(self: @This(), node: Zoir.Node.Index) !bool {
628 switch (node.get(self.zoir)) {
629 .true => return true,
630 .false => return false,
631 else => return error.WrongType,
632 }
633 }
634
635 fn parseInt(self: @This(), T: type, node: Zoir.Node.Index) !T {
636 switch (node.get(self.zoir)) {
637 .int_literal => |int| switch (int) {
638 .small => |val| return std.math.cast(T, val) orelse
639 self.failCannotRepresent(T, node),
640 .big => |val| return val.toInt(T) catch
641 self.failCannotRepresent(T, node),
642 },
643 .float_literal => |val| return intFromFloatExact(T, val) orelse
644 self.failCannotRepresent(T, node),
645
646 .char_literal => |val| return std.math.cast(T, val) orelse
647 self.failCannotRepresent(T, node),
648 else => return error.WrongType,
649 }
650 }
651
652 fn parseFloat(self: @This(), T: type, node: Zoir.Node.Index) !T {
653 switch (node.get(self.zoir)) {
654 .int_literal => |int| switch (int) {
655 .small => |val| return @floatFromInt(val),
656 .big => |val| return val.toFloat(T, .nearest_even)[0],
657 },
658 .float_literal => |val| return @floatCast(val),
659 .pos_inf => return std.math.inf(T),
660 .neg_inf => return -std.math.inf(T),
661 .nan => return std.math.nan(T),
662 .char_literal => |val| return @floatFromInt(val),
663 else => return error.WrongType,
664 }
665 }
666
667 fn parseEnumLiteral(self: @This(), T: type, node: Zoir.Node.Index) !T {
668 switch (node.get(self.zoir)) {
669 .enum_literal => |field_name| {
670 // Create a comptime string map for the enum fields
671 const enum_info = @typeInfo(T).@"enum";
672 comptime var kvs_list: [enum_info.field_names.len]struct { []const u8, T } = undefined;
673 inline for (enum_info.field_names, enum_info.field_values, 0..) |enum_field_name, enum_field_value, i| {
674 kvs_list[i] = .{ enum_field_name, @fromBackingInt(@intCast(enum_field_value)) };
675 }
676 const enum_tags = std.StaticStringMap(T).initComptime(kvs_list);
677
678 // Get the tag if it exists
679 const field_name_str = field_name.get(self.zoir);
680 return enum_tags.get(field_name_str) orelse
681 self.failUnexpected(T, "enum literal", node, null, field_name_str);
682 },
683 else => return error.WrongType,
684 }
685 }
686
687 fn parseSlicePointer(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprInnerError!T {
688 switch (node.get(self.zoir)) {
689 .string_literal => return self.parseString(T, node),
690 .array_literal => |nodes| return self.parseSlice(T, nodes),
691 .empty_literal => return self.parseSlice(T, .{ .start = node, .len = 0 }),
692 else => return error.WrongType,
693 }
694 }
695
696 fn parseString(self: *@This(), T: type, node: Zoir.Node.Index) ParseExprInnerError!T {
697 const ast_node = node.getAstNode(self.zoir);
698 const pointer = @typeInfo(T).pointer;
699 var size_hint = ZonGen.strLitSizeHint(self.ast, ast_node);
700 if (pointer.sentinel() != null) size_hint += 1;
701
702 var aw: std.Io.Writer.Allocating = .init(self.gpa);
703 try aw.ensureUnusedCapacity(size_hint);
704 defer aw.deinit();
705 const result = ZonGen.parseStrLit(self.ast, ast_node, &aw.writer) catch return error.OutOfMemory;
706 switch (result) {
707 .success => {},
708 .failure => |err| {
709 const token = self.ast.nodeMainToken(ast_node);
710 const raw_string = self.ast.tokenSlice(token);
711 return self.failTokenFmt(token, @intCast(err.offset()), "{f}", .{err.fmt(raw_string)});
712 },
713 }
714
715 if (pointer.child != u8 or
716 pointer.size != .slice or
717 !pointer.attrs.@"const" or
718 (pointer.sentinel() != null and pointer.sentinel() != 0) or
719 (pointer.attrs.@"align" != null and pointer.attrs.@"align" != 1))
720 {
721 return error.WrongType;
722 }
723
724 if (pointer.sentinel() != null) {
725 return aw.toOwnedSliceSentinel(0);
726 } else {
727 return aw.toOwnedSlice();
728 }
729 }
730
731 fn parseSlice(self: *@This(), T: type, nodes: Zoir.Node.Index.Range) !T {
732 const pointer = @typeInfo(T).pointer;
733
734 // Make sure we're working with a slice
735 switch (pointer.size) {
736 .slice => {},
737 .one, .many, .c => comptime unreachable,
738 }
739
740 // Allocate the slice
741 const slice = try self.gpa.allocWithOptions(
742 pointer.child,
743 nodes.len,
744 .fromByteUnitsOptional(pointer.attrs.@"align"),
745 pointer.sentinel(),
746 );
747 errdefer self.gpa.free(slice);
748
749 // Parse the elements and return the slice
750 for (slice, 0..) |*elem, i| {
751 errdefer if (self.options.free_on_error) {
752 for (slice[0..i]) |item| {
753 free(self.gpa, item);
754 }
755 };
756 elem.* = try self.parseExpr(pointer.child, nodes.at(@intCast(i)));
757 }
758
759 return slice;
760 }
761
762 fn parseArray(self: *@This(), T: type, node: Zoir.Node.Index) !T {
763 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {
764 .array_literal => |nodes| nodes,
765 .empty_literal => .{ .start = node, .len = 0 },
766 else => return error.WrongType,
767 };
768
769 const array_info = @typeInfo(T).array;
770
771 // Check if the size matches
772 if (nodes.len < array_info.len) {
773 return self.failNodeFmt(
774 node,
775 "expected {} array elements; found {}",
776 .{ array_info.len, nodes.len },
777 );
778 } else if (nodes.len > array_info.len) {
779 return self.failNodeFmt(
780 nodes.at(array_info.len),
781 "index {} outside of array of length {}",
782 .{ array_info.len, array_info.len },
783 );
784 }
785
786 // Parse the elements and return the array
787 var result: T = undefined;
788 for (&result, 0..) |*elem, i| {
789 // If we fail to parse this field, free all fields before it
790 errdefer if (self.options.free_on_error) {
791 for (result[0..i]) |item| {
792 free(self.gpa, item);
793 }
794 };
795
796 elem.* = try self.parseExpr(array_info.child, nodes.at(@intCast(i)));
797 }
798 if (array_info.sentinel()) |s| result[result.len] = s;
799 return result;
800 }
801
802 fn parseStruct(self: *@This(), T: type, node: Zoir.Node.Index) !T {
803 const repr = node.get(self.zoir);
804 const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) {
805 .struct_literal => |nodes| nodes,
806 .empty_literal => .{ .names = &.{}, .vals = .{ .start = node, .len = 0 } },
807 else => return error.WrongType,
808 };
809
810 const info = @typeInfo(T).@"struct";
811
812 // Build a map from field name to index.
813 // The special value `comptime_field` indicates that this is actually a comptime field.
814 const comptime_field = std.math.maxInt(usize);
815 const field_indices: std.StaticStringMap(usize) = comptime b: {
816 var kvs_list: [info.field_names.len]struct { []const u8, usize } = undefined;
817 for (&kvs_list, info.field_names, info.field_attrs, 0..) |*kv, field_name, field_attrs, i| {
818 kv.* = .{ field_name, if (field_attrs.@"comptime") comptime_field else i };
819 }
820 break :b .initComptime(kvs_list);
821 };
822
823 // Parse the struct
824 var result: T = undefined;
825 var field_found: [info.field_names.len]bool = @splat(false);
826
827 // If we fail partway through, free all already initialized fields
828 var initialized: usize = 0;
829 errdefer if (self.options.free_on_error and info.field_names.len > 0) {
830 for (fields.names[0..initialized]) |name_runtime| {
831 switch (field_indices.get(name_runtime.get(self.zoir)) orelse continue) {
832 inline 0...(info.field_names.len - 1) => |name_index| {
833 const name = info.field_names[name_index];
834 free(self.gpa, @field(result, name));
835 },
836 else => unreachable, // Can't be out of bounds
837 }
838 }
839 };
840
841 // Fill in the fields we found
842 for (0..fields.names.len) |i| {
843 const name = fields.names[i].get(self.zoir);
844 const field_index = field_indices.get(name) orelse {
845 if (self.options.ignore_unknown_fields) continue;
846 return self.failUnexpected(T, "field", node, i, name);
847 };
848 if (field_index == comptime_field) {
849 return self.failComptimeField(node, i);
850 }
851
852 // Mark the field as found. Assert that the found array is not zero length to satisfy
853 // the type checker (it can't be since we made it into an iteration of this loop.)
854 if (field_found.len == 0) unreachable;
855 field_found[field_index] = true;
856
857 switch (field_index) {
858 inline 0...(info.field_names.len - 1) => |j| {
859 if (info.field_attrs[j].@"comptime") unreachable;
860
861 @field(result, info.field_names[j]) = try self.parseExpr(
862 info.field_types[j],
863 fields.vals.at(@intCast(i)),
864 );
865 },
866 else => unreachable, // Can't be out of bounds
867 }
868
869 initialized += 1;
870 }
871
872 // Fill in any missing default fields
873 inline for (field_found, 0..) |found, i| {
874 if (!found) {
875 const field_attrs = info.field_attrs[i];
876 if (field_attrs.defaultValue(info.field_types[i])) |default| {
877 @field(result, info.field_names[i]) = default;
878 } else {
879 return self.failNodeFmt(
880 node,
881 "missing required field {s}",
882 .{info.field_names[i]},
883 );
884 }
885 }
886 }
887
888 return result;
889 }
890
891 fn parseTuple(self: *@This(), T: type, node: Zoir.Node.Index) !T {
892 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {
893 .array_literal => |nodes| nodes,
894 .empty_literal => .{ .start = node, .len = 0 },
895 else => return error.WrongType,
896 };
897
898 var result: T = undefined;
899 const info = @typeInfo(T).@"struct";
900
901 if (nodes.len > info.field_names.len) {
902 return self.failNodeFmt(
903 nodes.at(info.field_names.len),
904 "index {} outside of tuple length {}",
905 .{ info.field_names.len, info.field_names.len },
906 );
907 }
908
909 inline for (0..info.field_names.len) |i| {
910 // Check if we're out of bounds
911 if (i >= nodes.len) {
912 if (info.field_attrs[i].defaultValue(info.field_types[i])) |default| {
913 @field(result, info.field_names[i]) = default;
914 } else {
915 return self.failNodeFmt(node, "missing tuple field with index {}", .{i});
916 }
917 } else {
918 // If we fail to parse this field, free all fields before it
919 errdefer if (self.options.free_on_error) {
920 inline for (0..i) |j| {
921 if (j >= i) break;
922 free(self.gpa, result[j]);
923 }
924 };
925
926 if (info.field_attrs[i].@"comptime") {
927 return self.failComptimeField(node, i);
928 } else {
929 result[i] = try self.parseExpr(info.field_types[i], nodes.at(i));
930 }
931 }
932 }
933
934 return result;
935 }
936
937 fn parseUnion(self: *@This(), T: type, node: Zoir.Node.Index) !T {
938 const @"union" = @typeInfo(T).@"union";
939
940 if (@"union".field_names.len == 0) comptime unreachable;
941
942 // Gather info on the fields
943 const field_indices = b: {
944 comptime var kvs_list: [@"union".field_names.len]struct { []const u8, usize } = undefined;
945 inline for (@"union".field_names, 0..) |field_name, i| {
946 kvs_list[i] = .{ field_name, i };
947 }
948 break :b std.StaticStringMap(usize).initComptime(kvs_list);
949 };
950
951 // Parse the union
952 switch (node.get(self.zoir)) {
953 .enum_literal => |field_name| {
954 // The union must be tagged for an enum literal to coerce to it
955 if (@"union".tag_type == null) {
956 return error.WrongType;
957 }
958
959 // Get the index of the named field. We don't use `parseEnum` here as
960 // the order of the enum and the order of the union might not match!
961 const field_index = b: {
962 const field_name_str = field_name.get(self.zoir);
963 break :b field_indices.get(field_name_str) orelse
964 return self.failUnexpected(T, "field", node, null, field_name_str);
965 };
966
967 // Initialize the union from the given field.
968 switch (field_index) {
969 inline 0...@"union".field_names.len - 1 => |i| {
970 // Fail if the field is not void
971 if (@"union".field_types[i] != void)
972 return self.failNode(node, "expected union");
973
974 // Instantiate the union
975 return @unionInit(T, @"union".field_names[i], {});
976 },
977 else => unreachable, // Can't be out of bounds
978 }
979 },
980 .struct_literal => |struct_fields| {
981 if (struct_fields.names.len != 1) {
982 return error.WrongType;
983 }
984
985 // Fill in the field we found
986 const field_name = struct_fields.names[0];
987 const field_name_str = field_name.get(self.zoir);
988 const field_val = struct_fields.vals.at(0);
989 const field_index = field_indices.get(field_name_str) orelse
990 return self.failUnexpected(T, "field", node, 0, field_name_str);
991
992 switch (field_index) {
993 inline 0...@"union".field_names.len - 1 => |i| {
994 if (@"union".field_types[i] == void) {
995 return self.failNode(field_val, "expected type 'void'");
996 } else {
997 const value = try self.parseExpr(@"union".field_types[i], field_val);
998 return @unionInit(T, @"union".field_names[i], value);
999 }
1000 },
1001 else => unreachable, // Can't be out of bounds
1002 }
1003 },
1004 else => return error.WrongType,
1005 }
1006 }
1007
1008 fn failTokenFmt(
1009 self: @This(),
1010 token: Ast.TokenIndex,
1011 offset: u32,
1012 comptime fmt: []const u8,
1013 args: anytype,
1014 ) error{ OutOfMemory, ParseZon } {
1015 @branchHint(.cold);
1016 return self.failTokenFmtNote(token, offset, fmt, args, null);
1017 }
1018
1019 fn failTokenFmtNote(
1020 self: @This(),
1021 token: Ast.TokenIndex,
1022 offset: u32,
1023 comptime fmt: []const u8,
1024 args: anytype,
1025 note: ?Error.TypeCheckFailure.Note,
1026 ) error{ OutOfMemory, ParseZon } {
1027 @branchHint(.cold);
1028 comptime assert(args.len > 0);
1029 if (self.diag) |s| s.type_check = .{
1030 .token = token,
1031 .offset = offset,
1032 .message = std.fmt.allocPrint(self.gpa, fmt, args) catch |err| {
1033 if (note) |n| n.deinit(self.gpa);
1034 return err;
1035 },
1036 .owned = true,
1037 .note = note,
1038 };
1039 return error.ParseZon;
1040 }
1041
1042 fn failNodeFmt(
1043 self: @This(),
1044 node: Zoir.Node.Index,
1045 comptime fmt: []const u8,
1046 args: anytype,
1047 ) error{ OutOfMemory, ParseZon } {
1048 @branchHint(.cold);
1049 const token = self.ast.nodeMainToken(node.getAstNode(self.zoir));
1050 return self.failTokenFmt(token, 0, fmt, args);
1051 }
1052
1053 fn failToken(
1054 self: @This(),
1055 failure: Error.TypeCheckFailure,
1056 ) error{ParseZon} {
1057 @branchHint(.cold);
1058 if (self.diag) |s| s.type_check = failure;
1059 return error.ParseZon;
1060 }
1061
1062 fn failNode(
1063 self: @This(),
1064 node: Zoir.Node.Index,
1065 message: []const u8,
1066 ) error{ParseZon} {
1067 @branchHint(.cold);
1068 const token = self.ast.nodeMainToken(node.getAstNode(self.zoir));
1069 return self.failToken(.{
1070 .token = token,
1071 .offset = 0,
1072 .message = message,
1073 .owned = false,
1074 .note = null,
1075 });
1076 }
1077
1078 fn failCannotRepresent(
1079 self: @This(),
1080 T: type,
1081 node: Zoir.Node.Index,
1082 ) error{ OutOfMemory, ParseZon } {
1083 @branchHint(.cold);
1084 return self.failNodeFmt(node, "type '{s}' cannot represent value", .{@typeName(T)});
1085 }
1086
1087 fn failUnexpected(
1088 self: @This(),
1089 T: type,
1090 item_kind: []const u8,
1091 node: Zoir.Node.Index,
1092 field: ?usize,
1093 name: []const u8,
1094 ) error{ OutOfMemory, ParseZon } {
1095 @branchHint(.cold);
1096 if (self.diag == null) return error.ParseZon;
1097 const gpa = self.gpa;
1098 const token = if (field) |f| b: {
1099 var buf: [2]Ast.Node.Index = undefined;
1100 const struct_init = self.ast.fullStructInit(&buf, node.getAstNode(self.zoir)).?;
1101 const field_node = struct_init.ast.fields[f];
1102 break :b self.ast.firstToken(field_node) - 2;
1103 } else self.ast.nodeMainToken(node.getAstNode(self.zoir));
1104 switch (@typeInfo(T)) {
1105 inline .@"struct", .@"union", .@"enum" => |info| {
1106 const note: Error.TypeCheckFailure.Note = if (info.field_names.len == 0) b: {
1107 break :b .{
1108 .token = token,
1109 .offset = 0,
1110 .msg = "none expected",
1111 .owned = false,
1112 };
1113 } else b: {
1114 const msg = "supported: ";
1115 var buf: std.ArrayList(u8) = try .initCapacity(gpa, 64);
1116 defer buf.deinit(gpa);
1117 try buf.appendSlice(gpa, msg);
1118 inline for (info.field_names, 0..) |field_name, i| {
1119 if (i != 0) try buf.appendSlice(gpa, ", ");
1120 try buf.print(gpa, "'{f}'", .{std.zig.fmtIdFlags(field_name, .{
1121 .allow_primitive = true,
1122 .allow_underscore = true,
1123 })});
1124 }
1125 break :b .{
1126 .token = token,
1127 .offset = 0,
1128 .msg = try buf.toOwnedSlice(gpa),
1129 .owned = true,
1130 };
1131 };
1132 return self.failTokenFmtNote(
1133 token,
1134 0,
1135 "unexpected {s} '{s}'",
1136 .{ item_kind, name },
1137 note,
1138 );
1139 },
1140 else => comptime unreachable,
1141 }
1142 }
1143
1144 // Technically we could do this if we were willing to do a deep equal to verify
1145 // the value matched, but doing so doesn't seem to support any real use cases
1146 // so isn't worth the complexity at the moment.
1147 fn failComptimeField(
1148 self: @This(),
1149 node: Zoir.Node.Index,
1150 field: usize,
1151 ) error{ OutOfMemory, ParseZon } {
1152 @branchHint(.cold);
1153 if (self.diag == null) return error.ParseZon;
1154 const ast_node = node.getAstNode(self.zoir);
1155 var buf: [2]Ast.Node.Index = undefined;
1156 const token = if (self.ast.fullStructInit(&buf, ast_node)) |struct_init| b: {
1157 const field_node = struct_init.ast.fields[field];
1158 break :b self.ast.firstToken(field_node);
1159 } else b: {
1160 const array_init = self.ast.fullArrayInit(&buf, ast_node).?;
1161 const value_node = array_init.ast.elements[field];
1162 break :b self.ast.firstToken(value_node);
1163 };
1164 return self.failToken(.{
1165 .token = token,
1166 .offset = 0,
1167 .message = "cannot initialize comptime field",
1168 .owned = false,
1169 .note = null,
1170 });
1171 }
1172};
1173
1174fn intFromFloatExact(T: type, value: anytype) ?T {
1175 const max: @TypeOf(value) = @floatFromInt(std.math.maxInt(T));
1176 const min: @TypeOf(value) = @floatFromInt(std.math.minInt(T));
1177 if (value > max or value < min) {
1178 return null;
1179 }
1180
1181 if (std.math.isNan(value) or std.math.trunc(value) != value) {
1182 return null;
1183 }
1184
1185 return @intFromFloat(value);
1186}
1187
1188fn canParseType(T: type) bool {
1189 comptime return canParseTypeInner(T, &.{}, false);
1190}
1191
1192fn canParseTypeInner(
1193 T: type,
1194 /// Visited structs and unions, to avoid infinite recursion.
1195 /// Tracking more types is unnecessary, and a little complex due to optional nesting.
1196 visited: []const type,
1197 parent_is_optional: bool,
1198) bool {
1199 return switch (@typeInfo(T)) {
1200 .bool,
1201 .int,
1202 .float,
1203 .null,
1204 .@"enum",
1205 => true,
1206
1207 .noreturn,
1208 .void,
1209 .type,
1210 .undefined,
1211 .error_union,
1212 .error_set,
1213 .@"fn",
1214 .frame,
1215 .@"anyframe",
1216 .@"opaque",
1217 .spirv,
1218 .comptime_int,
1219 .comptime_float,
1220 .enum_literal,
1221 => false,
1222
1223 .pointer => |pointer| switch (pointer.size) {
1224 .one => canParseTypeInner(pointer.child, visited, parent_is_optional),
1225 .slice => canParseTypeInner(pointer.child, visited, false),
1226 .many, .c => false,
1227 },
1228
1229 .optional => |optional| if (parent_is_optional)
1230 false
1231 else
1232 canParseTypeInner(optional.child, visited, true),
1233
1234 .array => |array| canParseTypeInner(array.child, visited, false),
1235 .vector => |vector| canParseTypeInner(vector.child, visited, false),
1236
1237 .@"struct" => |@"struct"| {
1238 for (visited) |V| if (T == V) return true;
1239 const new_visited = visited ++ .{T};
1240 for (@"struct".field_types, @"struct".field_attrs) |field_type, field_attrs| {
1241 if (!field_attrs.@"comptime" and !canParseTypeInner(field_type, new_visited, false)) {
1242 return false;
1243 }
1244 }
1245 return true;
1246 },
1247 .@"union" => |@"union"| {
1248 for (visited) |V| if (T == V) return true;
1249 const new_visited = visited ++ .{T};
1250 for (@"union".field_types) |field_type| {
1251 if (field_type != void and !canParseTypeInner(field_type, new_visited, false)) {
1252 return false;
1253 }
1254 }
1255 return true;
1256 },
1257 };
1258}
1259
1260test "std.zon parse canParseType" {
1261 try std.testing.expect(!comptime canParseType(void));
1262 try std.testing.expect(!comptime canParseType(struct { f: [*]u8 }));
1263 try std.testing.expect(!comptime canParseType(struct { error{foo} }));
1264 try std.testing.expect(!comptime canParseType(union(enum) { a: void, b: [*c]u8 }));
1265 try std.testing.expect(!comptime canParseType(@Vector(0, [*c]u8)));
1266 try std.testing.expect(!comptime canParseType(*?[*c]u8));
1267 try std.testing.expect(comptime canParseType(enum(u8) { _ }));
1268 try std.testing.expect(comptime canParseType(union { foo: void }));
1269 try std.testing.expect(comptime canParseType(union(enum) { foo: void }));
1270 try std.testing.expect(!comptime canParseType(comptime_float));
1271 try std.testing.expect(!comptime canParseType(comptime_int));
1272 try std.testing.expect(comptime canParseType(struct { comptime foo: ??u8 = null }));
1273 try std.testing.expect(!comptime canParseType(@TypeOf(.foo)));
1274 try std.testing.expect(comptime canParseType(?u8));
1275 try std.testing.expect(comptime canParseType(*?*u8));
1276 try std.testing.expect(comptime canParseType(?struct {
1277 foo: ?struct {
1278 ?union(enum) {
1279 a: ?@Vector(0, ?*u8),
1280 },
1281 ?struct {
1282 f: ?[]?u8,
1283 },
1284 },
1285 }));
1286 try std.testing.expect(!comptime canParseType(??u8));
1287 try std.testing.expect(!comptime canParseType(?*?u8));
1288 try std.testing.expect(!comptime canParseType(*?*?*u8));
1289 try std.testing.expect(!comptime canParseType(struct { x: comptime_int = 2 }));
1290 try std.testing.expect(!comptime canParseType(struct { x: comptime_float = 2 }));
1291 try std.testing.expect(comptime canParseType(struct { comptime x: @TypeOf(.foo) = .foo }));
1292 try std.testing.expect(!comptime canParseType(struct { comptime_int }));
1293 const Recursive = struct { foo: ?*@This() };
1294 try std.testing.expect(comptime canParseType(Recursive));
1295
1296 // Make sure we validate nested optional before we early out due to already having seen
1297 // a type recursion!
1298 try std.testing.expect(!comptime canParseType(struct {
1299 add_to_visited: ?u8,
1300 retrieve_from_visited: ??u8,
1301 }));
1302}
1303
1304test "std.zon requiresAllocator" {
1305 try std.testing.expect(!requiresAllocator(u8));
1306 try std.testing.expect(!requiresAllocator(f32));
1307 try std.testing.expect(!requiresAllocator(enum { foo }));
1308 try std.testing.expect(!requiresAllocator(struct { f32 }));
1309 try std.testing.expect(!requiresAllocator(struct { x: f32 }));
1310 try std.testing.expect(!requiresAllocator([0][]const u8));
1311 try std.testing.expect(!requiresAllocator([2]u8));
1312 try std.testing.expect(!requiresAllocator(union { x: f32, y: f32 }));
1313 try std.testing.expect(!requiresAllocator(union(enum) { x: f32, y: f32 }));
1314 try std.testing.expect(!requiresAllocator(?f32));
1315 try std.testing.expect(!requiresAllocator(void));
1316 try std.testing.expect(!requiresAllocator(@TypeOf(null)));
1317 try std.testing.expect(!requiresAllocator(@Vector(3, u8)));
1318 try std.testing.expect(!requiresAllocator(@Vector(0, *const u8)));
1319
1320 try std.testing.expect(requiresAllocator([]u8));
1321 try std.testing.expect(requiresAllocator(*struct { u8, u8 }));
1322 try std.testing.expect(requiresAllocator([1][]const u8));
1323 try std.testing.expect(requiresAllocator(struct { x: i32, y: []u8 }));
1324 try std.testing.expect(requiresAllocator(union { x: i32, y: []u8 }));
1325 try std.testing.expect(requiresAllocator(union(enum) { x: i32, y: []u8 }));
1326 try std.testing.expect(requiresAllocator(?[]u8));
1327 try std.testing.expect(requiresAllocator(@Vector(3, *const u8)));
1328}
1329
1330test "std.zon ast errors" {
1331 const gpa = std.testing.allocator;
1332 var diag: Diagnostics = .{};
1333 defer diag.deinit(gpa);
1334 try std.testing.expectError(
1335 error.ParseZon,
1336 fromSlice(struct {}, gpa, ".{.x = 1 .y = 2}", &diag, .{}),
1337 );
1338 try std.testing.expectFmt("1:13: error: expected ',' after initializer\n", "{f}", .{diag});
1339}
1340
1341test "std.zon comments" {
1342 const gpa = std.testing.allocator;
1343
1344 try std.testing.expectEqual(@as(u8, 10), fromSlice(u8, gpa,
1345 \\// comment
1346 \\10 // comment
1347 \\// comment
1348 , null, .{}));
1349
1350 {
1351 var diag: Diagnostics = .{};
1352 defer diag.deinit(gpa);
1353 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa,
1354 \\//! comment
1355 \\10 // comment
1356 \\// comment
1357 , &diag, .{}));
1358 try std.testing.expectFmt(
1359 "1:1: error: expected expression, found 'a document comment'\n",
1360 "{f}",
1361 .{diag},
1362 );
1363 }
1364}
1365
1366test "std.zon failure/oom formatting" {
1367 const gpa = std.testing.allocator;
1368 var failing_allocator = std.testing.FailingAllocator.init(gpa, .{
1369 .fail_index = 0,
1370 .resize_fail_index = 0,
1371 });
1372 var diag: Diagnostics = .{};
1373 defer diag.deinit(gpa);
1374 try std.testing.expectError(error.OutOfMemory, fromSliceAlloc(
1375 []const u8,
1376 failing_allocator.allocator(),
1377 "\"foo\"",
1378 &diag,
1379 .{},
1380 ));
1381 try std.testing.expectFmt("", "{f}", .{diag});
1382}
1383
1384test "std.zon fromSliceAlloc syntax error" {
1385 try std.testing.expectError(
1386 error.ParseZon,
1387 fromSlice(u8, std.testing.allocator, ".{", null, .{}),
1388 );
1389}
1390
1391test "std.zon optional" {
1392 const gpa = std.testing.allocator;
1393
1394 // Basic usage
1395 {
1396 const none = try fromSlice(?u32, gpa, "null", null, .{});
1397 try std.testing.expect(none == null);
1398 const some = try fromSlice(?u32, gpa, "1", null, .{});
1399 try std.testing.expect(some.? == 1);
1400 }
1401
1402 // Deep free
1403 {
1404 const none = try fromSliceAlloc(?[]const u8, gpa, "null", null, .{});
1405 try std.testing.expect(none == null);
1406 const some = try fromSliceAlloc(?[]const u8, gpa, "\"foo\"", null, .{});
1407 defer free(gpa, some);
1408 try std.testing.expectEqualStrings("foo", some.?);
1409 }
1410}
1411
1412test "std.zon unions" {
1413 const gpa = std.testing.allocator;
1414
1415 // Unions
1416 {
1417 const Tagged = union(enum) { x: f32, @"y y": bool, z, @"z z" };
1418 const Untagged = union { x: f32, @"y y": bool, z: void, @"z z": void };
1419
1420 const tagged_x = try fromSlice(Tagged, gpa, ".{.x = 1.5}", null, .{});
1421 try std.testing.expectEqual(Tagged{ .x = 1.5 }, tagged_x);
1422 const tagged_y = try fromSlice(Tagged, gpa, ".{.@\"y y\" = true}", null, .{});
1423 try std.testing.expectEqual(Tagged{ .@"y y" = true }, tagged_y);
1424 const tagged_z_shorthand = try fromSlice(Tagged, gpa, ".z", null, .{});
1425 try std.testing.expectEqual(@as(Tagged, .z), tagged_z_shorthand);
1426 const tagged_zz_shorthand = try fromSlice(Tagged, gpa, ".@\"z z\"", null, .{});
1427 try std.testing.expectEqual(@as(Tagged, .@"z z"), tagged_zz_shorthand);
1428
1429 const untagged_x = try fromSlice(Untagged, gpa, ".{.x = 1.5}", null, .{});
1430 try std.testing.expect(untagged_x.x == 1.5);
1431 const untagged_y = try fromSlice(Untagged, gpa, ".{.@\"y y\" = true}", null, .{});
1432 try std.testing.expect(untagged_y.@"y y");
1433 }
1434
1435 // Deep free
1436 {
1437 const Union = union(enum) { bar: []const u8, baz: bool };
1438
1439 const noalloc = try fromSliceAlloc(Union, gpa, ".{.baz = false}", null, .{});
1440 try std.testing.expectEqual(Union{ .baz = false }, noalloc);
1441
1442 const alloc = try fromSliceAlloc(Union, gpa, ".{.bar = \"qux\"}", null, .{});
1443 defer free(gpa, alloc);
1444 try std.testing.expectEqualDeep(Union{ .bar = "qux" }, alloc);
1445 }
1446
1447 // Unknown field
1448 {
1449 const Union = union { x: f32, y: f32 };
1450 var diag: Diagnostics = .{};
1451 defer diag.deinit(gpa);
1452 try std.testing.expectError(
1453 error.ParseZon,
1454 fromSliceAlloc(Union, gpa, ".{.z=2.5}", &diag, .{}),
1455 );
1456 try std.testing.expectFmt(
1457 \\1:4: error: unexpected field 'z'
1458 \\1:4: note: supported: 'x', 'y'
1459 \\
1460 ,
1461 "{f}",
1462 .{diag},
1463 );
1464 }
1465
1466 // Explicit void field
1467 {
1468 const Union = union(enum) { x: void };
1469 var diag: Diagnostics = .{};
1470 defer diag.deinit(gpa);
1471 try std.testing.expectError(
1472 error.ParseZon,
1473 fromSliceAlloc(Union, gpa, ".{.x=1}", &diag, .{}),
1474 );
1475 try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{f}", .{diag});
1476 }
1477
1478 // Extra field
1479 {
1480 const Union = union { x: f32, y: bool };
1481 var diag: Diagnostics = .{};
1482 defer diag.deinit(gpa);
1483 try std.testing.expectError(
1484 error.ParseZon,
1485 fromSliceAlloc(Union, gpa, ".{.x = 1.5, .y = true}", &diag, .{}),
1486 );
1487 try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag});
1488 }
1489
1490 // No fields
1491 {
1492 const Union = union { x: f32, y: bool };
1493 var diag: Diagnostics = .{};
1494 defer diag.deinit(gpa);
1495 try std.testing.expectError(
1496 error.ParseZon,
1497 fromSliceAlloc(Union, gpa, ".{}", &diag, .{}),
1498 );
1499 try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag});
1500 }
1501
1502 // Enum literals cannot coerce into untagged unions
1503 {
1504 const Union = union { x: void };
1505 var diag: Diagnostics = .{};
1506 defer diag.deinit(gpa);
1507 try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".x", &diag, .{}));
1508 try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag});
1509 }
1510
1511 // Unknown field for enum literal coercion
1512 {
1513 const Union = union(enum) { x: void };
1514 var diag: Diagnostics = .{};
1515 defer diag.deinit(gpa);
1516 try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".y", &diag, .{}));
1517 try std.testing.expectFmt(
1518 \\1:2: error: unexpected field 'y'
1519 \\1:2: note: supported: 'x'
1520 \\
1521 ,
1522 "{f}",
1523 .{diag},
1524 );
1525 }
1526
1527 // Non void field for enum literal coercion
1528 {
1529 const Union = union(enum) { x: f32 };
1530 var diag: Diagnostics = .{};
1531 defer diag.deinit(gpa);
1532 try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".x", &diag, .{}));
1533 try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag});
1534 }
1535}
1536
1537test "std.zon structs" {
1538 const gpa = std.testing.allocator;
1539
1540 // Structs (various sizes tested since they're parsed differently)
1541 {
1542 const Vec0 = struct {};
1543 const Vec1 = struct { x: f32 };
1544 const Vec2 = struct { x: f32, y: f32 };
1545 const Vec3 = struct { x: f32, y: f32, z: f32 };
1546
1547 const zero = try fromSlice(Vec0, gpa, ".{}", null, .{});
1548 try std.testing.expectEqual(Vec0{}, zero);
1549
1550 const one = try fromSlice(Vec1, gpa, ".{.x = 1.2}", null, .{});
1551 try std.testing.expectEqual(Vec1{ .x = 1.2 }, one);
1552
1553 const two = try fromSlice(Vec2, gpa, ".{.x = 1.2, .y = 3.4}", null, .{});
1554 try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 3.4 }, two);
1555
1556 const three = try fromSlice(Vec3, gpa, ".{.x = 1.2, .y = 3.4, .z = 5.6}", null, .{});
1557 try std.testing.expectEqual(Vec3{ .x = 1.2, .y = 3.4, .z = 5.6 }, three);
1558 }
1559
1560 // Deep free (structs and arrays)
1561 {
1562 const Foo = struct { bar: []const u8, baz: []const []const u8 };
1563
1564 const parsed = try fromSliceAlloc(
1565 Foo,
1566 gpa,
1567 ".{.bar = \"qux\", .baz = .{\"a\", \"b\"}}",
1568 null,
1569 .{},
1570 );
1571 defer free(gpa, parsed);
1572 try std.testing.expectEqualDeep(Foo{ .bar = "qux", .baz = &.{ "a", "b" } }, parsed);
1573 }
1574
1575 // Unknown field
1576 {
1577 const Vec2 = struct { x: f32, y: f32 };
1578 var diag: Diagnostics = .{};
1579 defer diag.deinit(gpa);
1580 try std.testing.expectError(
1581 error.ParseZon,
1582 fromSlice(Vec2, gpa, ".{.x=1.5, .z=2.5}", &diag, .{}),
1583 );
1584 try std.testing.expectFmt(
1585 \\1:12: error: unexpected field 'z'
1586 \\1:12: note: supported: 'x', 'y'
1587 \\
1588 ,
1589 "{f}",
1590 .{diag},
1591 );
1592 }
1593
1594 // Duplicate field
1595 {
1596 const Vec2 = struct { x: f32, y: f32 };
1597 var diag: Diagnostics = .{};
1598 defer diag.deinit(gpa);
1599 try std.testing.expectError(
1600 error.ParseZon,
1601 fromSlice(Vec2, gpa, ".{.x=1.5, .x=2.5, .x=3.5}", &diag, .{}),
1602 );
1603 try std.testing.expectFmt(
1604 \\1:4: error: duplicate struct field name
1605 \\1:12: note: duplicate name here
1606 \\
1607 , "{f}", .{diag});
1608 }
1609
1610 // Ignore unknown fields
1611 {
1612 const Vec2 = struct { x: f32, y: f32 = 2.0 };
1613 const parsed = try fromSlice(Vec2, gpa, ".{ .x = 1.0, .z = 3.0 }", null, .{
1614 .ignore_unknown_fields = true,
1615 });
1616 try std.testing.expectEqual(Vec2{ .x = 1.0, .y = 2.0 }, parsed);
1617 }
1618
1619 // Unknown field when struct has no fields (regression test)
1620 {
1621 const Vec2 = struct {};
1622 var diag: Diagnostics = .{};
1623 defer diag.deinit(gpa);
1624 try std.testing.expectError(
1625 error.ParseZon,
1626 fromSlice(Vec2, gpa, ".{.x=1.5, .z=2.5}", &diag, .{}),
1627 );
1628 try std.testing.expectFmt(
1629 \\1:4: error: unexpected field 'x'
1630 \\1:4: note: none expected
1631 \\
1632 , "{f}", .{diag});
1633 }
1634
1635 // Missing field
1636 {
1637 const Vec2 = struct { x: f32, y: f32 };
1638 var diag: Diagnostics = .{};
1639 defer diag.deinit(gpa);
1640 try std.testing.expectError(
1641 error.ParseZon,
1642 fromSlice(Vec2, gpa, ".{.x=1.5}", &diag, .{}),
1643 );
1644 try std.testing.expectFmt("1:2: error: missing required field y\n", "{f}", .{diag});
1645 }
1646
1647 // Default field
1648 {
1649 const Vec2 = struct { x: f32, y: f32 = 1.5 };
1650 const parsed = try fromSlice(Vec2, gpa, ".{.x = 1.2}", null, .{});
1651 try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed);
1652 }
1653
1654 // Comptime field
1655 {
1656 const Vec2 = struct { x: f32, comptime y: f32 = 1.5 };
1657 const parsed = try fromSlice(Vec2, gpa, ".{.x = 1.2}", null, .{});
1658 try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed);
1659 }
1660
1661 // Comptime field assignment
1662 {
1663 const Vec2 = struct { x: f32, comptime y: f32 = 1.5 };
1664 var diag: Diagnostics = .{};
1665 defer diag.deinit(gpa);
1666 const parsed = fromSlice(Vec2, gpa, ".{.x = 1.2, .y = 1.5}", &diag, .{});
1667 try std.testing.expectError(error.ParseZon, parsed);
1668 try std.testing.expectFmt(
1669 \\1:18: error: cannot initialize comptime field
1670 \\
1671 , "{f}", .{diag});
1672 }
1673
1674 // Enum field (regression test, we were previously getting the field name in an
1675 // incorrect way that broke for enum values)
1676 {
1677 const Vec0 = struct { x: enum { x } };
1678 const parsed = try fromSlice(Vec0, gpa, ".{ .x = .x }", null, .{});
1679 try std.testing.expectEqual(Vec0{ .x = .x }, parsed);
1680 }
1681
1682 // Enum field and struct field with @
1683 {
1684 const Vec0 = struct { @"x x": enum { @"x x" } };
1685 const parsed = try fromSlice(Vec0, gpa, ".{ .@\"x x\" = .@\"x x\" }", null, .{});
1686 try std.testing.expectEqual(Vec0{ .@"x x" = .@"x x" }, parsed);
1687 }
1688
1689 // Type expressions are not allowed
1690 {
1691 // Structs
1692 {
1693 var diag: Diagnostics = .{};
1694 defer diag.deinit(gpa);
1695 const parsed = fromSlice(struct {}, gpa, "Empty{}", &diag, .{});
1696 try std.testing.expectError(error.ParseZon, parsed);
1697 try std.testing.expectFmt(
1698 \\1:1: error: types are not available in ZON
1699 \\1:1: note: replace the type with '.'
1700 \\
1701 , "{f}", .{diag});
1702 }
1703
1704 // Arrays
1705 {
1706 var diag: Diagnostics = .{};
1707 defer diag.deinit(gpa);
1708 const parsed = fromSlice([3]u8, gpa, "[3]u8{1, 2, 3}", &diag, .{});
1709 try std.testing.expectError(error.ParseZon, parsed);
1710 try std.testing.expectFmt(
1711 \\1:1: error: types are not available in ZON
1712 \\1:1: note: replace the type with '.'
1713 \\
1714 , "{f}", .{diag});
1715 }
1716
1717 // Slices
1718 {
1719 var diag: Diagnostics = .{};
1720 defer diag.deinit(gpa);
1721 const parsed = fromSliceAlloc([]u8, gpa, "[]u8{1, 2, 3}", &diag, .{});
1722 try std.testing.expectError(error.ParseZon, parsed);
1723 try std.testing.expectFmt(
1724 \\1:1: error: types are not available in ZON
1725 \\1:1: note: replace the type with '.'
1726 \\
1727 , "{f}", .{diag});
1728 }
1729
1730 // Tuples
1731 {
1732 var diag: Diagnostics = .{};
1733 defer diag.deinit(gpa);
1734 const parsed = fromSlice(
1735 struct { u8, u8, u8 },
1736 gpa,
1737 "Tuple{1, 2, 3}",
1738 &diag,
1739 .{},
1740 );
1741 try std.testing.expectError(error.ParseZon, parsed);
1742 try std.testing.expectFmt(
1743 \\1:1: error: types are not available in ZON
1744 \\1:1: note: replace the type with '.'
1745 \\
1746 , "{f}", .{diag});
1747 }
1748
1749 // Nested
1750 {
1751 var diag: Diagnostics = .{};
1752 defer diag.deinit(gpa);
1753 const parsed = fromSlice(struct {}, gpa, ".{ .x = Tuple{1, 2, 3} }", &diag, .{});
1754 try std.testing.expectError(error.ParseZon, parsed);
1755 try std.testing.expectFmt(
1756 \\1:9: error: types are not available in ZON
1757 \\1:9: note: replace the type with '.'
1758 \\
1759 , "{f}", .{diag});
1760 }
1761 }
1762}
1763
1764test "std.zon tuples" {
1765 const gpa = std.testing.allocator;
1766
1767 // Structs (various sizes tested since they're parsed differently)
1768 {
1769 const Tuple0 = struct {};
1770 const Tuple1 = struct { f32 };
1771 const Tuple2 = struct { f32, bool };
1772 const Tuple3 = struct { f32, bool, u8 };
1773
1774 const zero = try fromSlice(Tuple0, gpa, ".{}", null, .{});
1775 try std.testing.expectEqual(Tuple0{}, zero);
1776
1777 const one = try fromSlice(Tuple1, gpa, ".{1.2}", null, .{});
1778 try std.testing.expectEqual(Tuple1{1.2}, one);
1779
1780 const two = try fromSlice(Tuple2, gpa, ".{1.2, true}", null, .{});
1781 try std.testing.expectEqual(Tuple2{ 1.2, true }, two);
1782
1783 const three = try fromSlice(Tuple3, gpa, ".{1.2, false, 3}", null, .{});
1784 try std.testing.expectEqual(Tuple3{ 1.2, false, 3 }, three);
1785 }
1786
1787 // Deep free
1788 {
1789 const Tuple = struct { []const u8, []const u8 };
1790 const parsed = try fromSliceAlloc(Tuple, gpa, ".{\"hello\", \"world\"}", null, .{});
1791 defer free(gpa, parsed);
1792 try std.testing.expectEqualDeep(Tuple{ "hello", "world" }, parsed);
1793 }
1794
1795 // Extra field
1796 {
1797 const Tuple = struct { f32, bool };
1798 var diag: Diagnostics = .{};
1799 defer diag.deinit(gpa);
1800 try std.testing.expectError(
1801 error.ParseZon,
1802 fromSlice(Tuple, gpa, ".{0.5, true, 123}", &diag, .{}),
1803 );
1804 try std.testing.expectFmt("1:14: error: index 2 outside of tuple length 2\n", "{f}", .{diag});
1805 }
1806
1807 // Extra field
1808 {
1809 const Tuple = struct { f32, bool };
1810 var diag: Diagnostics = .{};
1811 defer diag.deinit(gpa);
1812 try std.testing.expectError(
1813 error.ParseZon,
1814 fromSlice(Tuple, gpa, ".{0.5}", &diag, .{}),
1815 );
1816 try std.testing.expectFmt(
1817 "1:2: error: missing tuple field with index 1\n",
1818 "{f}",
1819 .{diag},
1820 );
1821 }
1822
1823 // Tuple with unexpected field names
1824 {
1825 const Tuple = struct { f32 };
1826 var diag: Diagnostics = .{};
1827 defer diag.deinit(gpa);
1828 try std.testing.expectError(
1829 error.ParseZon,
1830 fromSlice(Tuple, gpa, ".{.foo = 10.0}", &diag, .{}),
1831 );
1832 try std.testing.expectFmt("1:2: error: expected tuple\n", "{f}", .{diag});
1833 }
1834
1835 // Struct with missing field names
1836 {
1837 const Struct = struct { foo: f32 };
1838 var diag: Diagnostics = .{};
1839 defer diag.deinit(gpa);
1840 try std.testing.expectError(
1841 error.ParseZon,
1842 fromSlice(Struct, gpa, ".{10.0}", &diag, .{}),
1843 );
1844 try std.testing.expectFmt("1:2: error: expected struct\n", "{f}", .{diag});
1845 }
1846
1847 // Comptime field
1848 {
1849 const Vec2 = struct { f32, comptime f32 = 1.5 };
1850 const parsed = try fromSlice(Vec2, gpa, ".{ 1.2 }", null, .{});
1851 try std.testing.expectEqual(Vec2{ 1.2, 1.5 }, parsed);
1852 }
1853
1854 // Comptime field assignment
1855 {
1856 const Vec2 = struct { f32, comptime f32 = 1.5 };
1857 var diag: Diagnostics = .{};
1858 defer diag.deinit(gpa);
1859 const parsed = fromSlice(Vec2, gpa, ".{ 1.2, 1.5}", &diag, .{});
1860 try std.testing.expectError(error.ParseZon, parsed);
1861 try std.testing.expectFmt(
1862 \\1:9: error: cannot initialize comptime field
1863 \\
1864 , "{f}", .{diag});
1865 }
1866}
1867
1868// Test sizes 0 to 3 since small sizes get parsed differently
1869test "std.zon arrays and slices" {
1870 const gpa = std.testing.allocator;
1871
1872 // Literals
1873 {
1874 // Arrays
1875 {
1876 const zero = try fromSlice([0]u8, gpa, ".{}", null, .{});
1877 try std.testing.expectEqualSlices(u8, &@as([0]u8, .{}), &zero);
1878
1879 const one = try fromSlice([1]u8, gpa, ".{'a'}", null, .{});
1880 try std.testing.expectEqualSlices(u8, &@as([1]u8, .{'a'}), &one);
1881
1882 const two = try fromSlice([2]u8, gpa, ".{'a', 'b'}", null, .{});
1883 try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two);
1884
1885 const two_comma = try fromSlice([2]u8, gpa, ".{'a', 'b',}", null, .{});
1886 try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two_comma);
1887
1888 const three = try fromSlice([3]u8, gpa, ".{'a', 'b', 'c'}", null, .{});
1889 try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, &three);
1890
1891 const sentinel = try fromSlice([3:'z']u8, gpa, ".{'a', 'b', 'c'}", null, .{});
1892 const expected_sentinel: [3:'z']u8 = .{ 'a', 'b', 'c' };
1893 try std.testing.expectEqualSlices(u8, &expected_sentinel, &sentinel);
1894 }
1895
1896 // Slice literals
1897 {
1898 const zero = try fromSliceAlloc([]const u8, gpa, ".{}", null, .{});
1899 defer free(gpa, zero);
1900 try std.testing.expectEqualSlices(u8, @as([]const u8, &.{}), zero);
1901
1902 const one = try fromSliceAlloc([]u8, gpa, ".{'a'}", null, .{});
1903 defer free(gpa, one);
1904 try std.testing.expectEqualSlices(u8, &.{'a'}, one);
1905
1906 const two = try fromSliceAlloc([]const u8, gpa, ".{'a', 'b'}", null, .{});
1907 defer free(gpa, two);
1908 try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two);
1909
1910 const two_comma = try fromSliceAlloc([]const u8, gpa, ".{'a', 'b',}", null, .{});
1911 defer free(gpa, two_comma);
1912 try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two_comma);
1913
1914 const three = try fromSliceAlloc([]u8, gpa, ".{'a', 'b', 'c'}", null, .{});
1915 defer free(gpa, three);
1916 try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, three);
1917
1918 const sentinel = try fromSliceAlloc([:'z']const u8, gpa, ".{'a', 'b', 'c'}", null, .{});
1919 defer free(gpa, sentinel);
1920 const expected_sentinel: [:'z']const u8 = &.{ 'a', 'b', 'c' };
1921 try std.testing.expectEqualSlices(u8, expected_sentinel, sentinel);
1922 }
1923 }
1924
1925 // Deep free
1926 {
1927 // Arrays
1928 {
1929 const parsed = try fromSliceAlloc([1][]const u8, gpa, ".{\"abc\"}", null, .{});
1930 defer free(gpa, parsed);
1931 const expected: [1][]const u8 = .{"abc"};
1932 try std.testing.expectEqualDeep(expected, parsed);
1933 }
1934
1935 // Slice literals
1936 {
1937 const parsed = try fromSliceAlloc([]const []const u8, gpa, ".{\"abc\"}", null, .{});
1938 defer free(gpa, parsed);
1939 const expected: []const []const u8 = &.{"abc"};
1940 try std.testing.expectEqualDeep(expected, parsed);
1941 }
1942 }
1943
1944 // Sentinels and alignment
1945 {
1946 // Arrays
1947 {
1948 const sentinel = try fromSlice([1:2]u8, gpa, ".{1}", null, .{});
1949 try std.testing.expectEqual(@as(usize, 1), sentinel.len);
1950 try std.testing.expectEqual(@as(u8, 1), sentinel[0]);
1951 try std.testing.expectEqual(@as(u8, 2), sentinel[1]);
1952 }
1953
1954 // Slice literals
1955 {
1956 const sentinel = try fromSliceAlloc([:2]align(4) u8, gpa, ".{1}", null, .{});
1957 defer free(gpa, sentinel);
1958 try std.testing.expectEqual(@as(usize, 1), sentinel.len);
1959 try std.testing.expectEqual(@as(u8, 1), sentinel[0]);
1960 try std.testing.expectEqual(@as(u8, 2), sentinel[1]);
1961 }
1962 }
1963
1964 // Expect 0 find 3
1965 {
1966 var diag: Diagnostics = .{};
1967 defer diag.deinit(gpa);
1968 try std.testing.expectError(
1969 error.ParseZon,
1970 fromSlice([0]u8, gpa, ".{'a', 'b', 'c'}", &diag, .{}),
1971 );
1972 try std.testing.expectFmt(
1973 "1:3: error: index 0 outside of array of length 0\n",
1974 "{f}",
1975 .{diag},
1976 );
1977 }
1978
1979 // Expect 1 find 2
1980 {
1981 var diag: Diagnostics = .{};
1982 defer diag.deinit(gpa);
1983 try std.testing.expectError(
1984 error.ParseZon,
1985 fromSlice([1]u8, gpa, ".{'a', 'b'}", &diag, .{}),
1986 );
1987 try std.testing.expectFmt(
1988 "1:8: error: index 1 outside of array of length 1\n",
1989 "{f}",
1990 .{diag},
1991 );
1992 }
1993
1994 // Expect 2 find 1
1995 {
1996 var diag: Diagnostics = .{};
1997 defer diag.deinit(gpa);
1998 try std.testing.expectError(
1999 error.ParseZon,
2000 fromSlice([2]u8, gpa, ".{'a'}", &diag, .{}),
2001 );
2002 try std.testing.expectFmt(
2003 "1:2: error: expected 2 array elements; found 1\n",
2004 "{f}",
2005 .{diag},
2006 );
2007 }
2008
2009 // Expect 3 find 0
2010 {
2011 var diag: Diagnostics = .{};
2012 defer diag.deinit(gpa);
2013 try std.testing.expectError(
2014 error.ParseZon,
2015 fromSlice([3]u8, gpa, ".{}", &diag, .{}),
2016 );
2017 try std.testing.expectFmt(
2018 "1:2: error: expected 3 array elements; found 0\n",
2019 "{f}",
2020 .{diag},
2021 );
2022 }
2023
2024 // Wrong inner type
2025 {
2026 // Array
2027 {
2028 var diag: Diagnostics = .{};
2029 defer diag.deinit(gpa);
2030 try std.testing.expectError(
2031 error.ParseZon,
2032 fromSlice([3]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}),
2033 );
2034 try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{diag});
2035 }
2036
2037 // Slice
2038 {
2039 var diag: Diagnostics = .{};
2040 defer diag.deinit(gpa);
2041 try std.testing.expectError(
2042 error.ParseZon,
2043 fromSliceAlloc([]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}),
2044 );
2045 try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{diag});
2046 }
2047 }
2048
2049 // Complete wrong type
2050 {
2051 // Array
2052 {
2053 var diag: Diagnostics = .{};
2054 defer diag.deinit(gpa);
2055 try std.testing.expectError(
2056 error.ParseZon,
2057 fromSlice([3]u8, gpa, "'a'", &diag, .{}),
2058 );
2059 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
2060 }
2061
2062 // Slice
2063 {
2064 var diag: Diagnostics = .{};
2065 defer diag.deinit(gpa);
2066 try std.testing.expectError(
2067 error.ParseZon,
2068 fromSliceAlloc([]u8, gpa, "'a'", &diag, .{}),
2069 );
2070 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
2071 }
2072 }
2073
2074 // Address of is not allowed (indirection for slices in ZON is implicit)
2075 {
2076 var diag: Diagnostics = .{};
2077 defer diag.deinit(gpa);
2078 try std.testing.expectError(
2079 error.ParseZon,
2080 fromSliceAlloc([]u8, gpa, " &.{'a', 'b', 'c'}", &diag, .{}),
2081 );
2082 try std.testing.expectFmt(
2083 "1:3: error: pointers are not available in ZON\n",
2084 "{f}",
2085 .{diag},
2086 );
2087 }
2088}
2089
2090test "std.zon string literal" {
2091 const gpa = std.testing.allocator;
2092
2093 // Basic string literal
2094 {
2095 const parsed = try fromSliceAlloc([]const u8, gpa, "\"abc\"", null, .{});
2096 defer free(gpa, parsed);
2097 try std.testing.expectEqualStrings(@as([]const u8, "abc"), parsed);
2098 }
2099
2100 // String literal with escape characters
2101 {
2102 const parsed = try fromSliceAlloc([]const u8, gpa, "\"ab\\nc\"", null, .{});
2103 defer free(gpa, parsed);
2104 try std.testing.expectEqualStrings(@as([]const u8, "ab\nc"), parsed);
2105 }
2106
2107 // String literal with embedded null
2108 {
2109 const parsed = try fromSliceAlloc([]const u8, gpa, "\"ab\\x00c\"", null, .{});
2110 defer free(gpa, parsed);
2111 try std.testing.expectEqualStrings(@as([]const u8, "ab\x00c"), parsed);
2112 }
2113
2114 // Passing string literal to a mutable slice
2115 {
2116 {
2117 var diag: Diagnostics = .{};
2118 defer diag.deinit(gpa);
2119 try std.testing.expectError(
2120 error.ParseZon,
2121 fromSliceAlloc([]u8, gpa, "\"abcd\"", &diag, .{}),
2122 );
2123 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
2124 }
2125
2126 {
2127 var diag: Diagnostics = .{};
2128 defer diag.deinit(gpa);
2129 try std.testing.expectError(
2130 error.ParseZon,
2131 fromSliceAlloc([]u8, gpa, "\\\\abcd", &diag, .{}),
2132 );
2133 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
2134 }
2135 }
2136
2137 // Passing string literal to a array
2138 {
2139 {
2140 var ast = try std.zig.Ast.parse(gpa, "\"abcd\"", .{ .mode = .zon });
2141 defer ast.deinit(gpa);
2142 var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false });
2143 defer zoir.deinit(gpa);
2144 var diag: Diagnostics = .{};
2145 defer diag.deinit(gpa);
2146 try std.testing.expectError(
2147 error.ParseZon,
2148 fromSlice([4:0]u8, gpa, "\"abcd\"", &diag, .{}),
2149 );
2150 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
2151 }
2152
2153 {
2154 var diag: Diagnostics = .{};
2155 defer diag.deinit(gpa);
2156 try std.testing.expectError(
2157 error.ParseZon,
2158 fromSlice([4:0]u8, gpa, "\\\\abcd", &diag, .{}),
2159 );
2160 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
2161 }
2162 }
2163
2164 // Zero terminated slices
2165 {
2166 {
2167 const parsed: [:0]const u8 = try fromSliceAlloc(
2168 [:0]const u8,
2169 gpa,
2170 "\"abc\"",
2171 null,
2172 .{},
2173 );
2174 defer free(gpa, parsed);
2175 try std.testing.expectEqualStrings("abc", parsed);
2176 try std.testing.expectEqual(@as(u8, 0), parsed[3]);
2177 }
2178
2179 {
2180 const parsed: [:0]const u8 = try fromSliceAlloc(
2181 [:0]const u8,
2182 gpa,
2183 "\\\\abc",
2184 null,
2185 .{},
2186 );
2187 defer free(gpa, parsed);
2188 try std.testing.expectEqualStrings("abc", parsed);
2189 try std.testing.expectEqual(@as(u8, 0), parsed[3]);
2190 }
2191 }
2192
2193 // Other value terminated slices
2194 {
2195 {
2196 var diag: Diagnostics = .{};
2197 defer diag.deinit(gpa);
2198 try std.testing.expectError(
2199 error.ParseZon,
2200 fromSliceAlloc([:1]const u8, gpa, "\"foo\"", &diag, .{}),
2201 );
2202 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
2203 }
2204
2205 {
2206 var diag: Diagnostics = .{};
2207 defer diag.deinit(gpa);
2208 try std.testing.expectError(
2209 error.ParseZon,
2210 fromSliceAlloc([:1]const u8, gpa, "\\\\foo", &diag, .{}),
2211 );
2212 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
2213 }
2214 }
2215
2216 // Expecting string literal, getting something else
2217 {
2218 var diag: Diagnostics = .{};
2219 defer diag.deinit(gpa);
2220 try std.testing.expectError(
2221 error.ParseZon,
2222 fromSliceAlloc([]const u8, gpa, "true", &diag, .{}),
2223 );
2224 try std.testing.expectFmt("1:1: error: expected string\n", "{f}", .{diag});
2225 }
2226
2227 // Expecting string literal, getting an incompatible tuple
2228 {
2229 var diag: Diagnostics = .{};
2230 defer diag.deinit(gpa);
2231 try std.testing.expectError(
2232 error.ParseZon,
2233 fromSliceAlloc([]const u8, gpa, ".{false}", &diag, .{}),
2234 );
2235 try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{f}", .{diag});
2236 }
2237
2238 // Invalid string literal
2239 {
2240 var diag: Diagnostics = .{};
2241 defer diag.deinit(gpa);
2242 try std.testing.expectError(
2243 error.ParseZon,
2244 fromSliceAlloc([]const i8, gpa, "\"\\a\"", &diag, .{}),
2245 );
2246 try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{f}", .{diag});
2247 }
2248
2249 // Slice wrong child type
2250 {
2251 {
2252 var diag: Diagnostics = .{};
2253 defer diag.deinit(gpa);
2254 try std.testing.expectError(
2255 error.ParseZon,
2256 fromSliceAlloc([]const i8, gpa, "\"a\"", &diag, .{}),
2257 );
2258 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
2259 }
2260
2261 {
2262 var diag: Diagnostics = .{};
2263 defer diag.deinit(gpa);
2264 try std.testing.expectError(
2265 error.ParseZon,
2266 fromSliceAlloc([]const i8, gpa, "\\\\a", &diag, .{}),
2267 );
2268 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
2269 }
2270 }
2271
2272 // Bad alignment
2273 {
2274 {
2275 var diag: Diagnostics = .{};
2276 defer diag.deinit(gpa);
2277 try std.testing.expectError(
2278 error.ParseZon,
2279 fromSliceAlloc([]align(2) const u8, gpa, "\"abc\"", &diag, .{}),
2280 );
2281 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
2282 }
2283
2284 {
2285 var diag: Diagnostics = .{};
2286 defer diag.deinit(gpa);
2287 try std.testing.expectError(
2288 error.ParseZon,
2289 fromSliceAlloc([]align(2) const u8, gpa, "\\\\abc", &diag, .{}),
2290 );
2291 try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag});
2292 }
2293 }
2294
2295 // Multi line strings
2296 inline for (.{ []const u8, [:0]const u8 }) |String| {
2297 // Nested
2298 {
2299 const S = struct {
2300 message: String,
2301 message2: String,
2302 message3: String,
2303 };
2304 const parsed = try fromSliceAlloc(S, gpa,
2305 \\.{
2306 \\ .message =
2307 \\ \\hello, world!
2308 \\
2309 \\ \\this is a multiline string!
2310 \\ \\
2311 \\ \\...
2312 \\
2313 \\ ,
2314 \\ .message2 =
2315 \\ \\this too...sort of.
2316 \\ ,
2317 \\ .message3 =
2318 \\ \\
2319 \\ \\and this.
2320 \\}
2321 , null, .{});
2322 defer free(gpa, parsed);
2323 try std.testing.expectEqualStrings(
2324 "hello, world!\nthis is a multiline string!\n\n...",
2325 parsed.message,
2326 );
2327 try std.testing.expectEqualStrings("this too...sort of.", parsed.message2);
2328 try std.testing.expectEqualStrings("\nand this.", parsed.message3);
2329 }
2330 }
2331}
2332
2333test "std.zon enum literals" {
2334 const gpa = std.testing.allocator;
2335
2336 const Enum = enum {
2337 foo,
2338 bar,
2339 baz,
2340 @"ab\nc",
2341 };
2342
2343 // Tags that exist
2344 try std.testing.expectEqual(Enum.foo, try fromSlice(Enum, gpa, ".foo", null, .{}));
2345 try std.testing.expectEqual(Enum.bar, try fromSlice(Enum, gpa, ".bar", null, .{}));
2346 try std.testing.expectEqual(Enum.baz, try fromSlice(Enum, gpa, ".baz", null, .{}));
2347 try std.testing.expectEqual(
2348 Enum.@"ab\nc",
2349 try fromSlice(Enum, gpa, ".@\"ab\\nc\"", null, .{}),
2350 );
2351
2352 // Bad tag
2353 {
2354 var diag: Diagnostics = .{};
2355 defer diag.deinit(gpa);
2356 try std.testing.expectError(
2357 error.ParseZon,
2358 fromSlice(Enum, gpa, ".qux", &diag, .{}),
2359 );
2360 try std.testing.expectFmt(
2361 \\1:2: error: unexpected enum literal 'qux'
2362 \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"'
2363 \\
2364 ,
2365 "{f}",
2366 .{diag},
2367 );
2368 }
2369
2370 // Bad tag that's too long for parser
2371 {
2372 var diag: Diagnostics = .{};
2373 defer diag.deinit(gpa);
2374 try std.testing.expectError(
2375 error.ParseZon,
2376 fromSlice(Enum, gpa, ".@\"foobarbaz\"", &diag, .{}),
2377 );
2378 try std.testing.expectFmt(
2379 \\1:2: error: unexpected enum literal 'foobarbaz'
2380 \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"'
2381 \\
2382 ,
2383 "{f}",
2384 .{diag},
2385 );
2386 }
2387
2388 // Bad type
2389 {
2390 var diag: Diagnostics = .{};
2391 defer diag.deinit(gpa);
2392 try std.testing.expectError(
2393 error.ParseZon,
2394 fromSlice(Enum, gpa, "true", &diag, .{}),
2395 );
2396 try std.testing.expectFmt("1:1: error: expected enum literal\n", "{f}", .{diag});
2397 }
2398
2399 // Test embedded nulls in an identifier
2400 {
2401 var diag: Diagnostics = .{};
2402 defer diag.deinit(gpa);
2403 try std.testing.expectError(
2404 error.ParseZon,
2405 fromSlice(Enum, gpa, ".@\"\\x00\"", &diag, .{}),
2406 );
2407 try std.testing.expectFmt(
2408 "1:2: error: identifier cannot contain null bytes\n",
2409 "{f}",
2410 .{diag},
2411 );
2412 }
2413}
2414
2415test "std.zon parse bool" {
2416 const gpa = std.testing.allocator;
2417
2418 // Correct bools
2419 try std.testing.expectEqual(true, try fromSlice(bool, gpa, "true", null, .{}));
2420 try std.testing.expectEqual(false, try fromSlice(bool, gpa, "false", null, .{}));
2421
2422 // Errors
2423 {
2424 var diag: Diagnostics = .{};
2425 defer diag.deinit(gpa);
2426 try std.testing.expectError(
2427 error.ParseZon,
2428 fromSlice(bool, gpa, " foo", &diag, .{}),
2429 );
2430 try std.testing.expectFmt(
2431 \\1:2: error: invalid expression
2432 \\1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'
2433 \\1:2: note: precede identifier with '.' for an enum literal
2434 \\
2435 , "{f}", .{diag});
2436 }
2437 {
2438 var diag: Diagnostics = .{};
2439 defer diag.deinit(gpa);
2440 try std.testing.expectError(error.ParseZon, fromSlice(bool, gpa, "123", &diag, .{}));
2441 try std.testing.expectFmt("1:1: error: expected type 'bool'\n", "{f}", .{diag});
2442 }
2443}
2444
2445test "std.zon intFromFloatExact" {
2446 // Valid conversions
2447 try std.testing.expectEqual(@as(u8, 10), intFromFloatExact(u8, @as(f32, 10.0)).?);
2448 try std.testing.expectEqual(@as(i8, -123), intFromFloatExact(i8, @as(f64, @as(f64, -123.0))).?);
2449 try std.testing.expectEqual(@as(i16, 45), intFromFloatExact(i16, @as(f128, @as(f128, 45.0))).?);
2450 try std.testing.expectEqual(@as(u128, 67), intFromFloatExact(u128, @as(f128, @as(f128, 67.0))).?);
2451
2452 // Out of range
2453 try std.testing.expectEqual(@as(?u4, null), intFromFloatExact(u4, @as(f32, 16.0)));
2454 try std.testing.expectEqual(@as(?i4, null), intFromFloatExact(i4, @as(f64, -17.0)));
2455 try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, @as(f128, -2.0)));
2456
2457 // Not a whole number
2458 try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, @as(f32, 0.5)));
2459 try std.testing.expectEqual(@as(?i8, null), intFromFloatExact(i8, @as(f64, 0.01)));
2460
2461 // Infinity and NaN
2462 try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, std.math.inf(f32)));
2463 try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, -std.math.inf(f32)));
2464 try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, std.math.nan(f32)));
2465}
2466
2467test "std.zon parse int" {
2468 const gpa = std.testing.allocator;
2469
2470 // Test various numbers and types
2471 try std.testing.expectEqual(@as(u8, 10), try fromSlice(u8, gpa, "10", null, .{}));
2472 try std.testing.expectEqual(@as(i16, 24), try fromSlice(i16, gpa, "24", null, .{}));
2473 try std.testing.expectEqual(@as(i14, -4), try fromSlice(i14, gpa, "-4", null, .{}));
2474 try std.testing.expectEqual(@as(i32, -123), try fromSlice(i32, gpa, "-123", null, .{}));
2475
2476 // Test limits
2477 try std.testing.expectEqual(@as(i8, 127), try fromSlice(i8, gpa, "127", null, .{}));
2478 try std.testing.expectEqual(@as(i8, -128), try fromSlice(i8, gpa, "-128", null, .{}));
2479
2480 // Test characters
2481 try std.testing.expectEqual(@as(u8, 'a'), try fromSlice(u8, gpa, "'a'", null, .{}));
2482 try std.testing.expectEqual(@as(u8, 'z'), try fromSlice(u8, gpa, "'z'", null, .{}));
2483
2484 // Test big integers
2485 try std.testing.expectEqual(
2486 @as(u65, 36893488147419103231),
2487 try fromSlice(u65, gpa, "36893488147419103231", null, .{}),
2488 );
2489 try std.testing.expectEqual(
2490 @as(u65, 36893488147419103231),
2491 try fromSlice(u65, gpa, "368934_881_474191032_31", null, .{}),
2492 );
2493 try std.testing.expectEqual(
2494 @as(u128, 340282366920938463463374607431768211455),
2495 try fromSlice(u128, gpa, "340282366920938463463374607431768211455", null, .{}),
2496 );
2497
2498 // Test big integer limits
2499 try std.testing.expectEqual(
2500 @as(i66, 36893488147419103231),
2501 try fromSlice(i66, gpa, "36893488147419103231", null, .{}),
2502 );
2503 try std.testing.expectEqual(
2504 @as(i66, -36893488147419103232),
2505 try fromSlice(i66, gpa, "-36893488147419103232", null, .{}),
2506 );
2507 {
2508 var diag: Diagnostics = .{};
2509 defer diag.deinit(gpa);
2510 try std.testing.expectError(error.ParseZon, fromSlice(
2511 i66,
2512 gpa,
2513 "36893488147419103232",
2514 &diag,
2515 .{},
2516 ));
2517 try std.testing.expectFmt(
2518 "1:1: error: type 'i66' cannot represent value\n",
2519 "{f}",
2520 .{diag},
2521 );
2522 }
2523 {
2524 var diag: Diagnostics = .{};
2525 defer diag.deinit(gpa);
2526 try std.testing.expectError(error.ParseZon, fromSlice(
2527 i66,
2528 gpa,
2529 "-36893488147419103233",
2530 &diag,
2531 .{},
2532 ));
2533 try std.testing.expectFmt(
2534 "1:1: error: type 'i66' cannot represent value\n",
2535 "{f}",
2536 .{diag},
2537 );
2538 }
2539
2540 // Test parsing whole number floats as integers
2541 try std.testing.expectEqual(@as(i8, -1), try fromSlice(i8, gpa, "-1.0", null, .{}));
2542 try std.testing.expectEqual(@as(i8, 123), try fromSlice(i8, gpa, "123.0", null, .{}));
2543
2544 // Test non-decimal integers
2545 try std.testing.expectEqual(@as(i16, 0xff), try fromSlice(i16, gpa, "0xff", null, .{}));
2546 try std.testing.expectEqual(@as(i16, -0xff), try fromSlice(i16, gpa, "-0xff", null, .{}));
2547 try std.testing.expectEqual(@as(i16, 0o77), try fromSlice(i16, gpa, "0o77", null, .{}));
2548 try std.testing.expectEqual(@as(i16, -0o77), try fromSlice(i16, gpa, "-0o77", null, .{}));
2549 try std.testing.expectEqual(@as(i16, 0b11), try fromSlice(i16, gpa, "0b11", null, .{}));
2550 try std.testing.expectEqual(@as(i16, -0b11), try fromSlice(i16, gpa, "-0b11", null, .{}));
2551
2552 // Test non-decimal big integers
2553 try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(
2554 u65,
2555 gpa,
2556 "0x1ffffffffffffffff",
2557 null,
2558 .{},
2559 ));
2560 try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(
2561 i66,
2562 gpa,
2563 "0x1ffffffffffffffff",
2564 null,
2565 .{},
2566 ));
2567 try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(
2568 i66,
2569 gpa,
2570 "-0x1ffffffffffffffff",
2571 null,
2572 .{},
2573 ));
2574 try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(
2575 u65,
2576 gpa,
2577 "0o3777777777777777777777",
2578 null,
2579 .{},
2580 ));
2581 try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(
2582 i66,
2583 gpa,
2584 "0o3777777777777777777777",
2585 null,
2586 .{},
2587 ));
2588 try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(
2589 i66,
2590 gpa,
2591 "-0o3777777777777777777777",
2592 null,
2593 .{},
2594 ));
2595 try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(
2596 u65,
2597 gpa,
2598 "0b11111111111111111111111111111111111111111111111111111111111111111",
2599 null,
2600 .{},
2601 ));
2602 try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(
2603 i66,
2604 gpa,
2605 "0b11111111111111111111111111111111111111111111111111111111111111111",
2606 null,
2607 .{},
2608 ));
2609 try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(
2610 i66,
2611 gpa,
2612 "-0b11111111111111111111111111111111111111111111111111111111111111111",
2613 null,
2614 .{},
2615 ));
2616
2617 // Number with invalid character in the middle
2618 {
2619 var diag: Diagnostics = .{};
2620 defer diag.deinit(gpa);
2621 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "32a32", &diag, .{}));
2622 try std.testing.expectFmt(
2623 "1:3: error: invalid digit 'a' for decimal base\n",
2624 "{f}",
2625 .{diag},
2626 );
2627 }
2628
2629 // Failing to parse as int
2630 {
2631 var diag: Diagnostics = .{};
2632 defer diag.deinit(gpa);
2633 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "true", &diag, .{}));
2634 try std.testing.expectFmt("1:1: error: expected type 'u8'\n", "{f}", .{diag});
2635 }
2636
2637 // Failing because an int is out of range
2638 {
2639 var diag: Diagnostics = .{};
2640 defer diag.deinit(gpa);
2641 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "256", &diag, .{}));
2642 try std.testing.expectFmt(
2643 "1:1: error: type 'u8' cannot represent value\n",
2644 "{f}",
2645 .{diag},
2646 );
2647 }
2648
2649 // Failing because a negative int is out of range
2650 {
2651 var diag: Diagnostics = .{};
2652 defer diag.deinit(gpa);
2653 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-129", &diag, .{}));
2654 try std.testing.expectFmt(
2655 "1:1: error: type 'i8' cannot represent value\n",
2656 "{f}",
2657 .{diag},
2658 );
2659 }
2660
2661 // Failing because an unsigned int is negative
2662 {
2663 var diag: Diagnostics = .{};
2664 defer diag.deinit(gpa);
2665 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1", &diag, .{}));
2666 try std.testing.expectFmt(
2667 "1:1: error: type 'u8' cannot represent value\n",
2668 "{f}",
2669 .{diag},
2670 );
2671 }
2672
2673 // Failing because a float is non-whole
2674 {
2675 var diag: Diagnostics = .{};
2676 defer diag.deinit(gpa);
2677 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "1.5", &diag, .{}));
2678 try std.testing.expectFmt(
2679 "1:1: error: type 'u8' cannot represent value\n",
2680 "{f}",
2681 .{diag},
2682 );
2683 }
2684
2685 // Failing because a float is negative
2686 {
2687 var diag: Diagnostics = .{};
2688 defer diag.deinit(gpa);
2689 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1.0", &diag, .{}));
2690 try std.testing.expectFmt(
2691 "1:1: error: type 'u8' cannot represent value\n",
2692 "{f}",
2693 .{diag},
2694 );
2695 }
2696
2697 // Negative integer zero
2698 {
2699 var diag: Diagnostics = .{};
2700 defer diag.deinit(gpa);
2701 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-0", &diag, .{}));
2702 try std.testing.expectFmt(
2703 \\1:2: error: integer literal '-0' is ambiguous
2704 \\1:2: note: use '0' for an integer zero
2705 \\1:2: note: use '-0.0' for a floating-point signed zero
2706 \\
2707 , "{f}", .{diag});
2708 }
2709
2710 // Negative integer zero casted to float
2711 {
2712 var diag: Diagnostics = .{};
2713 defer diag.deinit(gpa);
2714 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-0", &diag, .{}));
2715 try std.testing.expectFmt(
2716 \\1:2: error: integer literal '-0' is ambiguous
2717 \\1:2: note: use '0' for an integer zero
2718 \\1:2: note: use '-0.0' for a floating-point signed zero
2719 \\
2720 , "{f}", .{diag});
2721 }
2722
2723 // Negative float 0 is allowed
2724 try std.testing.expect(
2725 std.math.isNegativeZero(try fromSlice(f32, gpa, "-0.0", null, .{})),
2726 );
2727 try std.testing.expect(std.math.isPositiveZero(try fromSlice(f32, gpa, "0.0", null, .{})));
2728
2729 // Double negation is not allowed
2730 {
2731 var diag: Diagnostics = .{};
2732 defer diag.deinit(gpa);
2733 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "--2", &diag, .{}));
2734 try std.testing.expectFmt(
2735 "1:1: error: expected number or 'inf' after '-'\n",
2736 "{f}",
2737 .{diag},
2738 );
2739 }
2740
2741 {
2742 var diag: Diagnostics = .{};
2743 defer diag.deinit(gpa);
2744 try std.testing.expectError(
2745 error.ParseZon,
2746 fromSlice(f32, gpa, "--2.0", &diag, .{}),
2747 );
2748 try std.testing.expectFmt(
2749 "1:1: error: expected number or 'inf' after '-'\n",
2750 "{f}",
2751 .{diag},
2752 );
2753 }
2754
2755 // Invalid int literal
2756 {
2757 var diag: Diagnostics = .{};
2758 defer diag.deinit(gpa);
2759 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "0xg", &diag, .{}));
2760 try std.testing.expectFmt("1:3: error: invalid digit 'g' for hex base\n", "{f}", .{diag});
2761 }
2762
2763 // Notes on invalid int literal
2764 {
2765 var diag: Diagnostics = .{};
2766 defer diag.deinit(gpa);
2767 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "0123", &diag, .{}));
2768 try std.testing.expectFmt(
2769 \\1:1: error: number '0123' has leading zero
2770 \\1:1: note: use '0o' prefix for octal literals
2771 \\
2772 , "{f}", .{diag});
2773 }
2774}
2775
2776test "std.zon negative char" {
2777 const gpa = std.testing.allocator;
2778
2779 {
2780 var diag: Diagnostics = .{};
2781 defer diag.deinit(gpa);
2782 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-'a'", &diag, .{}));
2783 try std.testing.expectFmt(
2784 "1:1: error: expected number or 'inf' after '-'\n",
2785 "{f}",
2786 .{diag},
2787 );
2788 }
2789 {
2790 var diag: Diagnostics = .{};
2791 defer diag.deinit(gpa);
2792 try std.testing.expectError(error.ParseZon, fromSlice(i16, gpa, "-'a'", &diag, .{}));
2793 try std.testing.expectFmt(
2794 "1:1: error: expected number or 'inf' after '-'\n",
2795 "{f}",
2796 .{diag},
2797 );
2798 }
2799}
2800
2801test "std.zon parse float" {
2802 const gpa = std.testing.allocator;
2803
2804 // Test decimals
2805 try std.testing.expectEqual(@as(f16, 0.5), try fromSlice(f16, gpa, "0.5", null, .{}));
2806 try std.testing.expectEqual(
2807 @as(f32, 123.456),
2808 try fromSlice(f32, gpa, "123.456", null, .{}),
2809 );
2810 try std.testing.expectEqual(
2811 @as(f64, -123.456),
2812 try fromSlice(f64, gpa, "-123.456", null, .{}),
2813 );
2814 try std.testing.expectEqual(@as(f128, 42.5), try fromSlice(f128, gpa, "42.5", null, .{}));
2815
2816 // Test whole numbers with and without decimals
2817 try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, gpa, "5.0", null, .{}));
2818 try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, gpa, "5", null, .{}));
2819 try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, gpa, "-102.0", null, .{}));
2820 try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, gpa, "-102", null, .{}));
2821
2822 // Test characters and negated characters
2823 try std.testing.expectEqual(@as(f32, 'a'), try fromSlice(f32, gpa, "'a'", null, .{}));
2824 try std.testing.expectEqual(@as(f32, 'z'), try fromSlice(f32, gpa, "'z'", null, .{}));
2825
2826 // Test big integers
2827 try std.testing.expectEqual(
2828 @as(f32, 36893488147419103231.0),
2829 try fromSlice(f32, gpa, "36893488147419103231", null, .{}),
2830 );
2831 try std.testing.expectEqual(
2832 @as(f32, -36893488147419103231.0),
2833 try fromSlice(f32, gpa, "-36893488147419103231", null, .{}),
2834 );
2835 try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice(
2836 f128,
2837 gpa,
2838 "0x1ffffffffffffffff",
2839 null,
2840 .{},
2841 ));
2842 try std.testing.expectEqual(@as(f32, @floatFromInt(0x1ffffffffffffffff)), try fromSlice(
2843 f32,
2844 gpa,
2845 "0x1ffffffffffffffff",
2846 null,
2847 .{},
2848 ));
2849
2850 // Exponents, underscores
2851 try std.testing.expectEqual(
2852 @as(f32, 123.0E+77),
2853 try fromSlice(f32, gpa, "12_3.0E+77", null, .{}),
2854 );
2855
2856 // Hexadecimal
2857 try std.testing.expectEqual(
2858 @as(f32, 0x103.70p-5),
2859 try fromSlice(f32, gpa, "0x103.70p-5", null, .{}),
2860 );
2861 try std.testing.expectEqual(
2862 @as(f32, -0x103.70),
2863 try fromSlice(f32, gpa, "-0x103.70", null, .{}),
2864 );
2865 try std.testing.expectEqual(
2866 @as(f32, 0x1234_5678.9ABC_CDEFp-10),
2867 try fromSlice(f32, gpa, "0x1234_5678.9ABC_CDEFp-10", null, .{}),
2868 );
2869
2870 // inf, nan
2871 try std.testing.expect(std.math.isPositiveInf(try fromSlice(f32, gpa, "inf", null, .{})));
2872 try std.testing.expect(std.math.isNegativeInf(try fromSlice(f32, gpa, "-inf", null, .{})));
2873 try std.testing.expect(std.math.isNan(try fromSlice(f32, gpa, "nan", null, .{})));
2874
2875 // Negative nan not allowed
2876 {
2877 var diag: Diagnostics = .{};
2878 defer diag.deinit(gpa);
2879 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-nan", &diag, .{}));
2880 try std.testing.expectFmt(
2881 "1:1: error: expected number or 'inf' after '-'\n",
2882 "{f}",
2883 .{diag},
2884 );
2885 }
2886
2887 // nan as int not allowed
2888 {
2889 var diag: Diagnostics = .{};
2890 defer diag.deinit(gpa);
2891 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &diag, .{}));
2892 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag});
2893 }
2894
2895 // nan as int not allowed
2896 {
2897 var diag: Diagnostics = .{};
2898 defer diag.deinit(gpa);
2899 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &diag, .{}));
2900 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag});
2901 }
2902
2903 // inf as int not allowed
2904 {
2905 var diag: Diagnostics = .{};
2906 defer diag.deinit(gpa);
2907 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "inf", &diag, .{}));
2908 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag});
2909 }
2910
2911 // -inf as int not allowed
2912 {
2913 var diag: Diagnostics = .{};
2914 defer diag.deinit(gpa);
2915 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-inf", &diag, .{}));
2916 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{f}", .{diag});
2917 }
2918
2919 // Bad identifier as float
2920 {
2921 var diag: Diagnostics = .{};
2922 defer diag.deinit(gpa);
2923 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "foo", &diag, .{}));
2924 try std.testing.expectFmt(
2925 \\1:1: error: invalid expression
2926 \\1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'
2927 \\1:1: note: precede identifier with '.' for an enum literal
2928 \\
2929 , "{f}", .{diag});
2930 }
2931
2932 {
2933 var diag: Diagnostics = .{};
2934 defer diag.deinit(gpa);
2935 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-foo", &diag, .{}));
2936 try std.testing.expectFmt(
2937 "1:1: error: expected number or 'inf' after '-'\n",
2938 "{f}",
2939 .{diag},
2940 );
2941 }
2942
2943 // Non float as float
2944 {
2945 var diag: Diagnostics = .{};
2946 defer diag.deinit(gpa);
2947 try std.testing.expectError(
2948 error.ParseZon,
2949 fromSlice(f32, gpa, "\"foo\"", &diag, .{}),
2950 );
2951 try std.testing.expectFmt("1:1: error: expected type 'f32'\n", "{f}", .{diag});
2952 }
2953}
2954
2955test "std.zon free on error" {
2956 // Test freeing partially allocated structs
2957 {
2958 const Struct = struct {
2959 x: []const u8,
2960 y: []const u8,
2961 z: bool,
2962 };
2963 try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, std.testing.allocator,
2964 \\.{
2965 \\ .x = "hello",
2966 \\ .y = "world",
2967 \\ .z = "fail",
2968 \\}
2969 , null, .{}));
2970 }
2971
2972 // Test freeing partially allocated tuples
2973 {
2974 const Struct = struct {
2975 []const u8,
2976 []const u8,
2977 bool,
2978 };
2979 try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, std.testing.allocator,
2980 \\.{
2981 \\ "hello",
2982 \\ "world",
2983 \\ "fail",
2984 \\}
2985 , null, .{}));
2986 }
2987
2988 // Test freeing structs with missing fields
2989 {
2990 const Struct = struct {
2991 x: []const u8,
2992 y: bool,
2993 };
2994 try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, std.testing.allocator,
2995 \\.{
2996 \\ .x = "hello",
2997 \\}
2998 , null, .{}));
2999 }
3000
3001 // Test freeing partially allocated arrays
3002 {
3003 try std.testing.expectError(error.ParseZon, fromSliceAlloc(
3004 [3][]const u8,
3005 std.testing.allocator,
3006 \\.{
3007 \\ "hello",
3008 \\ false,
3009 \\ false,
3010 \\}
3011 ,
3012 null,
3013 .{},
3014 ));
3015 }
3016
3017 // Test freeing partially allocated slices
3018 {
3019 try std.testing.expectError(error.ParseZon, fromSliceAlloc(
3020 [][]const u8,
3021 std.testing.allocator,
3022 \\.{
3023 \\ "hello",
3024 \\ "world",
3025 \\ false,
3026 \\}
3027 ,
3028 null,
3029 .{},
3030 ));
3031 }
3032
3033 // We can parse types that can't be freed, as long as they contain no allocations, e.g. untagged
3034 // unions.
3035 try std.testing.expectEqual(
3036 @as(f32, 1.5),
3037 (try fromSlice(union { x: f32 }, std.testing.allocator, ".{ .x = 1.5 }", null, .{})).x,
3038 );
3039
3040 // We can also parse types that can't be freed if it's impossible for an error to occur after
3041 // the allocation, as is the case here.
3042 {
3043 const result = try fromSliceAlloc(
3044 union { x: []const u8 },
3045 std.testing.allocator,
3046 ".{ .x = \"foo\" }",
3047 null,
3048 .{},
3049 );
3050 defer free(std.testing.allocator, result.x);
3051 try std.testing.expectEqualStrings("foo", result.x);
3052 }
3053
3054 // However, if it's possible we could get an error requiring we free the value, but the value
3055 // cannot be freed (e.g. untagged unions) then we need to turn off `free_on_error` for it to
3056 // compile.
3057 {
3058 const S = struct {
3059 union { x: []const u8 },
3060 bool,
3061 };
3062 const result = try fromSliceAlloc(
3063 S,
3064 std.testing.allocator,
3065 ".{ .{ .x = \"foo\" }, true }",
3066 null,
3067 .{ .free_on_error = false },
3068 );
3069 defer free(std.testing.allocator, result[0].x);
3070 try std.testing.expectEqualStrings("foo", result[0].x);
3071 try std.testing.expect(result[1]);
3072 }
3073
3074 // Again but for structs.
3075 {
3076 const S = struct {
3077 a: union { x: []const u8 },
3078 b: bool,
3079 };
3080 const result = try fromSliceAlloc(
3081 S,
3082 std.testing.allocator,
3083 ".{ .a = .{ .x = \"foo\" }, .b = true }",
3084 null,
3085 .{
3086 .free_on_error = false,
3087 },
3088 );
3089 defer free(std.testing.allocator, result.a.x);
3090 try std.testing.expectEqualStrings("foo", result.a.x);
3091 try std.testing.expect(result.b);
3092 }
3093
3094 // Again but for arrays.
3095 {
3096 const S = [2]union { x: []const u8 };
3097 const result = try fromSliceAlloc(
3098 S,
3099 std.testing.allocator,
3100 ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }",
3101 null,
3102 .{
3103 .free_on_error = false,
3104 },
3105 );
3106 defer free(std.testing.allocator, result[0].x);
3107 defer free(std.testing.allocator, result[1].x);
3108 try std.testing.expectEqualStrings("foo", result[0].x);
3109 try std.testing.expectEqualStrings("bar", result[1].x);
3110 }
3111
3112 // Again but for slices.
3113 {
3114 const S = []union { x: []const u8 };
3115 const result = try fromSliceAlloc(
3116 S,
3117 std.testing.allocator,
3118 ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }",
3119 null,
3120 .{
3121 .free_on_error = false,
3122 },
3123 );
3124 defer std.testing.allocator.free(result);
3125 defer free(std.testing.allocator, result[0].x);
3126 defer free(std.testing.allocator, result[1].x);
3127 try std.testing.expectEqualStrings("foo", result[0].x);
3128 try std.testing.expectEqualStrings("bar", result[1].x);
3129 }
3130}
3131
3132test "std.zon vector" {
3133 const builtin = @import("builtin");
3134 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .s390x) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/25957
3135
3136 const gpa = std.testing.allocator;
3137
3138 // Passing cases
3139 try std.testing.expectEqual(
3140 @Vector(0, bool){},
3141 try fromSlice(@Vector(0, bool), gpa, ".{}", null, .{}),
3142 );
3143 try std.testing.expectEqual(
3144 @Vector(3, bool){ true, false, true },
3145 try fromSlice(@Vector(3, bool), gpa, ".{true, false, true}", null, .{}),
3146 );
3147
3148 try std.testing.expectEqual(
3149 @Vector(0, f32){},
3150 try fromSlice(@Vector(0, f32), gpa, ".{}", null, .{}),
3151 );
3152 try std.testing.expectEqual(
3153 @Vector(3, f32){ 1.5, 2.5, 3.5 },
3154 try fromSlice(@Vector(3, f32), gpa, ".{1.5, 2.5, 3.5}", null, .{}),
3155 );
3156
3157 try std.testing.expectEqual(
3158 @Vector(0, u8){},
3159 try fromSlice(@Vector(0, u8), gpa, ".{}", null, .{}),
3160 );
3161 try std.testing.expectEqual(
3162 @Vector(3, u8){ 2, 4, 6 },
3163 try fromSlice(@Vector(3, u8), gpa, ".{2, 4, 6}", null, .{}),
3164 );
3165
3166 {
3167 try std.testing.expectEqual(
3168 @Vector(0, *const u8){},
3169 try fromSliceAlloc(@Vector(0, *const u8), gpa, ".{}", null, .{}),
3170 );
3171 const pointers = try fromSliceAlloc(@Vector(3, *const u8), gpa, ".{2, 4, 6}", null, .{});
3172 defer free(gpa, pointers);
3173 try std.testing.expectEqualDeep(@Vector(3, *const u8){ &2, &4, &6 }, pointers);
3174 }
3175
3176 {
3177 try std.testing.expectEqual(
3178 @Vector(0, ?*const u8){},
3179 try fromSliceAlloc(@Vector(0, ?*const u8), gpa, ".{}", null, .{}),
3180 );
3181 const pointers = try fromSliceAlloc(@Vector(3, ?*const u8), gpa, ".{2, null, 6}", null, .{});
3182 defer free(gpa, pointers);
3183 try std.testing.expectEqualDeep(@Vector(3, ?*const u8){ &2, null, &6 }, pointers);
3184 }
3185
3186 // Too few fields
3187 {
3188 var diag: Diagnostics = .{};
3189 defer diag.deinit(gpa);
3190 try std.testing.expectError(
3191 error.ParseZon,
3192 fromSlice(@Vector(2, f32), gpa, ".{0.5}", &diag, .{}),
3193 );
3194 try std.testing.expectFmt(
3195 "1:2: error: expected 2 array elements; found 1\n",
3196 "{f}",
3197 .{diag},
3198 );
3199 }
3200
3201 // Too many fields
3202 {
3203 var diag: Diagnostics = .{};
3204 defer diag.deinit(gpa);
3205 try std.testing.expectError(
3206 error.ParseZon,
3207 fromSlice(@Vector(2, f32), gpa, ".{0.5, 1.5, 2.5}", &diag, .{}),
3208 );
3209 try std.testing.expectFmt(
3210 "1:13: error: index 2 outside of array of length 2\n",
3211 "{f}",
3212 .{diag},
3213 );
3214 }
3215
3216 // Wrong type fields
3217 {
3218 var diag: Diagnostics = .{};
3219 defer diag.deinit(gpa);
3220 try std.testing.expectError(
3221 error.ParseZon,
3222 fromSlice(@Vector(3, f32), gpa, ".{0.5, true, 2.5}", &diag, .{}),
3223 );
3224 try std.testing.expectFmt(
3225 "1:8: error: expected type 'f32'\n",
3226 "{f}",
3227 .{diag},
3228 );
3229 }
3230
3231 // Wrong type
3232 {
3233 var diag: Diagnostics = .{};
3234 defer diag.deinit(gpa);
3235 try std.testing.expectError(
3236 error.ParseZon,
3237 fromSlice(@Vector(3, u8), gpa, "true", &diag, .{}),
3238 );
3239 try std.testing.expectFmt("1:1: error: expected type '@Vector(3, u8)'\n", "{f}", .{diag});
3240 }
3241
3242 // Elements should get freed on error
3243 {
3244 var diag: Diagnostics = .{};
3245 defer diag.deinit(gpa);
3246 try std.testing.expectError(
3247 error.ParseZon,
3248 fromSliceAlloc(@Vector(3, *u8), gpa, ".{1, true, 3}", &diag, .{}),
3249 );
3250 try std.testing.expectFmt("1:6: error: expected type 'u8'\n", "{f}", .{diag});
3251 }
3252}
3253
3254test "std.zon add pointers" {
3255 const gpa = std.testing.allocator;
3256
3257 // Primitive with varying levels of pointers
3258 {
3259 const result = try fromSliceAlloc(*u32, gpa, "10", null, .{});
3260 defer free(gpa, result);
3261 try std.testing.expectEqual(@as(u32, 10), result.*);
3262 }
3263
3264 {
3265 const result = try fromSliceAlloc(**u32, gpa, "10", null, .{});
3266 defer free(gpa, result);
3267 try std.testing.expectEqual(@as(u32, 10), result.*.*);
3268 }
3269
3270 {
3271 const result = try fromSliceAlloc(***u32, gpa, "10", null, .{});
3272 defer free(gpa, result);
3273 try std.testing.expectEqual(@as(u32, 10), result.*.*.*);
3274 }
3275
3276 // Primitive optional with varying levels of pointers
3277 {
3278 const some = try fromSliceAlloc(?*u32, gpa, "10", null, .{});
3279 defer free(gpa, some);
3280 try std.testing.expectEqual(@as(u32, 10), some.?.*);
3281
3282 const none = try fromSliceAlloc(?*u32, gpa, "null", null, .{});
3283 defer free(gpa, none);
3284 try std.testing.expectEqual(null, none);
3285 }
3286
3287 {
3288 const some = try fromSliceAlloc(*?u32, gpa, "10", null, .{});
3289 defer free(gpa, some);
3290 try std.testing.expectEqual(@as(u32, 10), some.*.?);
3291
3292 const none = try fromSliceAlloc(*?u32, gpa, "null", null, .{});
3293 defer free(gpa, none);
3294 try std.testing.expectEqual(null, none.*);
3295 }
3296
3297 {
3298 const some = try fromSliceAlloc(?**u32, gpa, "10", null, .{});
3299 defer free(gpa, some);
3300 try std.testing.expectEqual(@as(u32, 10), some.?.*.*);
3301
3302 const none = try fromSliceAlloc(?**u32, gpa, "null", null, .{});
3303 defer free(gpa, none);
3304 try std.testing.expectEqual(null, none);
3305 }
3306
3307 {
3308 const some = try fromSliceAlloc(*?*u32, gpa, "10", null, .{});
3309 defer free(gpa, some);
3310 try std.testing.expectEqual(@as(u32, 10), some.*.?.*);
3311
3312 const none = try fromSliceAlloc(*?*u32, gpa, "null", null, .{});
3313 defer free(gpa, none);
3314 try std.testing.expectEqual(null, none.*);
3315 }
3316
3317 {
3318 const some = try fromSliceAlloc(**?u32, gpa, "10", null, .{});
3319 defer free(gpa, some);
3320 try std.testing.expectEqual(@as(u32, 10), some.*.*.?);
3321
3322 const none = try fromSliceAlloc(**?u32, gpa, "null", null, .{});
3323 defer free(gpa, none);
3324 try std.testing.expectEqual(null, none.*.*);
3325 }
3326
3327 // Pointer to an array
3328 {
3329 const result = try fromSliceAlloc(*[3]u8, gpa, ".{ 1, 2, 3 }", null, .{});
3330 defer free(gpa, result);
3331 try std.testing.expectEqual([3]u8{ 1, 2, 3 }, result.*);
3332 }
3333
3334 // A complicated type with nested internal pointers and string allocations
3335 {
3336 const Inner = struct {
3337 f1: *const ?*const []const u8,
3338 f2: *const ?*const []const u8,
3339 };
3340 const Outer = struct {
3341 f1: *const ?*const Inner,
3342 f2: *const ?*const Inner,
3343 };
3344 const expected: Outer = .{
3345 .f1 = &&.{
3346 .f1 = &null,
3347 .f2 = &&"foo",
3348 },
3349 .f2 = &null,
3350 };
3351
3352 const found = try fromSliceAlloc(?*Outer, gpa,
3353 \\.{
3354 \\ .f1 = .{
3355 \\ .f1 = null,
3356 \\ .f2 = "foo",
3357 \\ },
3358 \\ .f2 = null,
3359 \\}
3360 , null, .{});
3361 defer free(gpa, found);
3362
3363 try std.testing.expectEqualDeep(expected, found.?.*);
3364 }
3365
3366 // Test that optional types are flattened correctly in errors
3367 {
3368 var diag: Diagnostics = .{};
3369 defer diag.deinit(gpa);
3370 try std.testing.expectError(
3371 error.ParseZon,
3372 fromSliceAlloc(*const ?*const u8, gpa, "true", &diag, .{}),
3373 );
3374 try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{f}", .{diag});
3375 }
3376
3377 {
3378 var diag: Diagnostics = .{};
3379 defer diag.deinit(gpa);
3380 try std.testing.expectError(
3381 error.ParseZon,
3382 fromSliceAlloc(*const ?*const f32, gpa, "true", &diag, .{}),
3383 );
3384 try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{f}", .{diag});
3385 }
3386
3387 {
3388 var diag: Diagnostics = .{};
3389 defer diag.deinit(gpa);
3390 try std.testing.expectError(
3391 error.ParseZon,
3392 fromSliceAlloc(*const ?*const @Vector(3, u8), gpa, "true", &diag, .{}),
3393 );
3394 try std.testing.expectFmt("1:1: error: expected type '?@Vector(3, u8)'\n", "{f}", .{diag});
3395 }
3396
3397 {
3398 var diag: Diagnostics = .{};
3399 defer diag.deinit(gpa);
3400 try std.testing.expectError(
3401 error.ParseZon,
3402 fromSliceAlloc(*const ?*const bool, gpa, "10", &diag, .{}),
3403 );
3404 try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{f}", .{diag});
3405 }
3406
3407 {
3408 var diag: Diagnostics = .{};
3409 defer diag.deinit(gpa);
3410 try std.testing.expectError(
3411 error.ParseZon,
3412 fromSliceAlloc(*const ?*const struct { a: i32 }, gpa, "true", &diag, .{}),
3413 );
3414 try std.testing.expectFmt("1:1: error: expected optional struct\n", "{f}", .{diag});
3415 }
3416
3417 {
3418 var diag: Diagnostics = .{};
3419 defer diag.deinit(gpa);
3420 try std.testing.expectError(
3421 error.ParseZon,
3422 fromSliceAlloc(*const ?*const struct { i32 }, gpa, "true", &diag, .{}),
3423 );
3424 try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{f}", .{diag});
3425 }
3426
3427 {
3428 var diag: Diagnostics = .{};
3429 defer diag.deinit(gpa);
3430 try std.testing.expectError(
3431 error.ParseZon,
3432 fromSliceAlloc(*const ?*const union { x: void }, gpa, "true", &diag, .{}),
3433 );
3434 try std.testing.expectFmt("1:1: error: expected optional union\n", "{f}", .{diag});
3435 }
3436
3437 {
3438 var diag: Diagnostics = .{};
3439 defer diag.deinit(gpa);
3440 try std.testing.expectError(
3441 error.ParseZon,
3442 fromSliceAlloc(*const ?*const [3]u8, gpa, "true", &diag, .{}),
3443 );
3444 try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag});
3445 }
3446
3447 {
3448 var diag: Diagnostics = .{};
3449 defer diag.deinit(gpa);
3450 try std.testing.expectError(
3451 error.ParseZon,
3452 fromSliceAlloc(?[3]u8, gpa, "true", &diag, .{}),
3453 );
3454 try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag});
3455 }
3456
3457 {
3458 var diag: Diagnostics = .{};
3459 defer diag.deinit(gpa);
3460 try std.testing.expectError(
3461 error.ParseZon,
3462 fromSliceAlloc(*const ?*const []u8, gpa, "true", &diag, .{}),
3463 );
3464 try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag});
3465 }
3466
3467 {
3468 var diag: Diagnostics = .{};
3469 defer diag.deinit(gpa);
3470 try std.testing.expectError(
3471 error.ParseZon,
3472 fromSliceAlloc(?[]u8, gpa, "true", &diag, .{}),
3473 );
3474 try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag});
3475 }
3476
3477 {
3478 var diag: Diagnostics = .{};
3479 defer diag.deinit(gpa);
3480 try std.testing.expectError(
3481 error.ParseZon,
3482 fromSliceAlloc(*const ?*const []const u8, gpa, "true", &diag, .{}),
3483 );
3484 try std.testing.expectFmt("1:1: error: expected optional string\n", "{f}", .{diag});
3485 }
3486
3487 {
3488 var diag: Diagnostics = .{};
3489 defer diag.deinit(gpa);
3490 try std.testing.expectError(
3491 error.ParseZon,
3492 fromSliceAlloc(*const ?*const enum { foo }, gpa, "true", &diag, .{}),
3493 );
3494 try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{f}", .{diag});
3495 }
3496}
3497
3498test "std.zon stop on node" {
3499 const gpa = std.testing.allocator;
3500
3501 {
3502 const Vec2 = struct {
3503 x: Zoir.Node.Index,
3504 y: f32,
3505 };
3506
3507 var diag: Diagnostics = .{};
3508 defer diag.deinit(gpa);
3509 const result = try fromSlice(Vec2, gpa, ".{ .x = 1.5, .y = 2.5 }", &diag, .{});
3510 try std.testing.expectEqual(result.y, 2.5);
3511 try std.testing.expectEqual(Zoir.Node{ .float_literal = 1.5 }, result.x.get(diag.zoir));
3512 }
3513
3514 {
3515 var diag: Diagnostics = .{};
3516 defer diag.deinit(gpa);
3517 const result = try fromSlice(Zoir.Node.Index, gpa, "1.23", &diag, .{});
3518 try std.testing.expectEqual(Zoir.Node{ .float_literal = 1.23 }, result.get(diag.zoir));
3519 }
3520}
3521
3522test "std.zon no alloc" {
3523 const gpa = std.testing.allocator;
3524
3525 try std.testing.expectEqual(
3526 [3]u8{ 1, 2, 3 },
3527 try fromSlice([3]u8, gpa, ".{ 1, 2, 3 }", null, .{}),
3528 );
3529
3530 const Nested = struct { u8, u8, struct { u8, u8 } };
3531
3532 var ast = try std.zig.Ast.parse(gpa, ".{ 1, 2, .{ 3, 4 } }", .{ .mode = .zon });
3533 defer ast.deinit(gpa);
3534
3535 var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false });
3536 defer zoir.deinit(gpa);
3537
3538 try std.testing.expectEqual(
3539 Nested{ 1, 2, .{ 3, 4 } },
3540 try fromZoir(Nested, ast, zoir, null, .{}),
3541 );
3542
3543 try std.testing.expectEqual(
3544 Nested{ 1, 2, .{ 3, 4 } },
3545 try fromZoirNode(Nested, ast, zoir, .root, null, .{}),
3546 );
3547}
3548
3549test "std.zon errors without diagnostics" {
3550 const gpa = std.testing.allocator;
3551
3552 const Enum = enum {
3553 foo,
3554 bar,
3555 baz,
3556 };
3557 try std.testing.expectError(error.ParseZon, fromSliceAlloc(Enum, gpa, ".nothing", null, .{}));
3558
3559 const Struct = struct {
3560 name: []const u8,
3561 };
3562 try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, gpa, ".{ .name = \"Alice\", .age = 25 }", null, .{}));
3563
3564 const Union = union(enum) {
3565 x,
3566 y: u32,
3567 };
3568 try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".a", null, .{}));
3569 try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".{ .b = 8 }", null, .{}));
3570}