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