| ... | @@ -6,6 +6,8 @@ | ... | @@ -6,6 +6,8 @@ |
| 6 | //! * `fromZoir`/`fromZoirAlloc` | 6 | //! * `fromZoir`/`fromZoirAlloc` |
| 7 | //! * `fromZoirNode`/`fromZoirNodeAlloc` | 7 | //! * `fromZoirNode`/`fromZoirNodeAlloc` |
| 8 | //! | 8 | //! |
| | 9 | //! To update an existing values, see the `updateFrom*` variants. |
| | 10 | //! |
| 9 | //! For lower level control over parsing, see `std.zig.Zoir`. | 11 | //! For lower level control over parsing, see `std.zig.Zoir`. |
| 10 | | 12 | |
| 11 | const std = @import("std"); | 13 | const std = @import("std"); |
| ... | @@ -23,14 +25,39 @@ const ArrayList = std.ArrayList; | ... | @@ -23,14 +25,39 @@ const ArrayList = std.ArrayList; |
| 23 | /// Rename when adding or removing support for a type. | 25 | /// Rename when adding or removing support for a type. |
| 24 | const valid_types = {}; | 26 | const valid_types = {}; |
| 25 | | 27 | |
| 26 | /// Configuration for the runtime parser. | 28 | /// Deprecated. |
| 27 | pub const Options = struct { | 29 | pub const Options = FromOptions; |
| | 30 | |
| | 31 | /// Configuration for the `from*` functions. |
| | 32 | pub const FromOptions = struct { |
| 28 | /// If true, unknown fields do not error. | 33 | /// If true, unknown fields do not error. |
| 29 | ignore_unknown_fields: bool = false, | 34 | ignore_unknown_fields: bool = false, |
| 30 | /// If true, the parser cleans up partially parsed values on error. This requires some extra | 35 | /// If true, the parser cleans up partially parsed values on error. This requires some extra |
| 31 | /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because | 36 | /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because |
| 32 | /// you're using arena allocation.) | 37 | /// you're using arena allocation.) |
| 33 | free_on_error: bool = true, | 38 | free_on_error: bool = true, |
| | 39 | |
| | 40 | fn toParserOptions(self: FromOptions) Parser.Options { |
| | 41 | return .{ |
| | 42 | .ignore_unknown_fields = self.ignore_unknown_fields, |
| | 43 | .ignore_missing_fields = false, |
| | 44 | .free_on_error = self.free_on_error, |
| | 45 | }; |
| | 46 | } |
| | 47 | }; |
| | 48 | |
| | 49 | /// Configuration for the `updateFrom*` variants. |
| | 50 | pub const UpdateFromOptions = struct { |
| | 51 | /// If true, unknown fields do not error. |
| | 52 | ignore_unknown_fields: bool = false, |
| | 53 | |
| | 54 | fn toParserOptions(self: UpdateFromOptions) Parser.Options { |
| | 55 | return .{ |
| | 56 | .ignore_unknown_fields = self.ignore_unknown_fields, |
| | 57 | .ignore_missing_fields = true, |
| | 58 | .free_on_error = false, |
| | 59 | }; |
| | 60 | } |
| 34 | }; | 61 | }; |
| 35 | | 62 | |
| 36 | pub const Error = union(enum) { | 63 | pub const Error = union(enum) { |
| ... | @@ -260,50 +287,92 @@ pub const Diagnostics = struct { | ... | @@ -260,50 +287,92 @@ pub const Diagnostics = struct { |
| 260 | /// | 287 | /// |
| 261 | /// An allocator is still required for temporary allocations made during parsing. | 288 | /// An allocator is still required for temporary allocations made during parsing. |
| 262 | pub fn fromSlice( | 289 | pub fn fromSlice( |
| | 290 | /// The type to deserialize into. Only the following types are supported, unsupported types will |
| | 291 | /// result in a compiler error: |
| | 292 | /// |
| | 293 | /// * Optionals of supported types |
| | 294 | /// * Booleans |
| | 295 | /// * Integers except for `comptime_int` |
| | 296 | /// * Floats except for `comptime_float` |
| | 297 | /// * Enums |
| | 298 | /// * Single item pointers to supported types |
| | 299 | /// * Slices of supported types |
| | 300 | /// * Arrays of supported types |
| | 301 | /// * Vectors of supported types |
| | 302 | /// * Structures of supported types |
| | 303 | /// * Tuples of supported types |
| | 304 | /// * Unions |
| 263 | T: type, | 305 | T: type, |
| 264 | gpa: Allocator, | 306 | gpa: Allocator, |
| 265 | source: [:0]const u8, | 307 | source: [:0]const u8, |
| 266 | diag: ?*Diagnostics, | 308 | diag: ?*Diagnostics, |
| 267 | options: Options, | 309 | options: FromOptions, |
| 268 | ) error{ OutOfMemory, ParseZon }!T { | 310 | ) error{ OutOfMemory, ParseZon }!T { |
| 269 | comptime assert(!requiresAllocator(T)); | 311 | comptime assert(!requiresAllocator(T)); |
| 270 | return fromSliceAlloc(T, gpa, source, diag, options); | 312 | return fromSliceAlloc(T, gpa, source, diag, options); |
| 271 | } | 313 | } |
| 272 | | 314 | |
| | 315 | /// Similar to `fromSlice`, but updates an existing value `current`. Fields not specified by ZON are |
| | 316 | /// left at their current values. Pointers are replaced with new values not mutated in place. |
| | 317 | pub fn updateFromSlice( |
| | 318 | T: type, |
| | 319 | gpa: Allocator, |
| | 320 | current: *T, |
| | 321 | source: [:0]const u8, |
| | 322 | diag: ?*Diagnostics, |
| | 323 | options: UpdateFromOptions, |
| | 324 | ) error{ OutOfMemory, ParseZon }!void { |
| | 325 | comptime assert(!requiresAllocator(T)); |
| | 326 | try updateFromSliceAlloc(T, gpa, current, source, diag, options); |
| | 327 | } |
| | 328 | |
| 273 | /// Like `fromSlice`, but the result may contain pointers. To automatically free the result, see | 329 | /// Like `fromSlice`, but the result may contain pointers. To automatically free the result, see |
| 274 | /// `free`. | 330 | /// `free`. |
| 275 | pub fn fromSliceAlloc( | 331 | pub fn fromSliceAlloc( |
| 276 | /// The type to deserialize into. May not be or contain any of the following types: | | |
| 277 | /// * Any comptime-only type, except in a comptime field | | |
| 278 | /// * `type` | | |
| 279 | /// * `void`, except as a union payload | | |
| 280 | /// * `noreturn` | | |
| 281 | /// * An error set/error union | | |
| 282 | /// * A many-pointer or C-pointer | | |
| 283 | /// * An opaque type, including `anyopaque` | | |
| 284 | /// * An async frame type, including `anyframe` and `anyframe->T` | | |
| 285 | /// * A function | | |
| 286 | /// | | |
| 287 | /// All other types are valid. Unsupported types will fail at compile time. | | |
| 288 | T: type, | 332 | T: type, |
| 289 | gpa: Allocator, | 333 | gpa: Allocator, |
| 290 | source: [:0]const u8, | 334 | source: [:0]const u8, |
| 291 | diag: ?*Diagnostics, | 335 | diag: ?*Diagnostics, |
| 292 | options: Options, | 336 | options: FromOptions, |
| 293 | ) error{ OutOfMemory, ParseZon }!T { | 337 | ) error{ OutOfMemory, ParseZon }!T { |
| | 338 | var result: T = undefined; |
| | 339 | try parseSliceAlloc(T, &result, gpa, source, diag, options.toParserOptions()); |
| | 340 | return result; |
| | 341 | } |
| | 342 | |
| | 343 | /// Like updateFromSlice`, but the result may contain pointers. Requires an arena for cleanup. |
| | 344 | pub fn updateFromSliceAlloc( |
| | 345 | T: type, |
| | 346 | arena: Allocator, |
| | 347 | current: *T, |
| | 348 | source: [:0]const u8, |
| | 349 | diag: ?*Diagnostics, |
| | 350 | options: UpdateFromOptions, |
| | 351 | ) error{ OutOfMemory, ParseZon }!void { |
| | 352 | try parseSliceAlloc(T, current, arena, source, diag, options.toParserOptions()); |
| | 353 | } |
| | 354 | |
| | 355 | fn parseSliceAlloc( |
| | 356 | T: type, |
| | 357 | current: *T, |
| | 358 | arena: Allocator, |
| | 359 | source: [:0]const u8, |
| | 360 | diag: ?*Diagnostics, |
| | 361 | options: Parser.Options, |
| | 362 | ) error{ OutOfMemory, ParseZon }!void { |
| 294 | if (diag) |s| s.assertEmpty(); | 363 | if (diag) |s| s.assertEmpty(); |
| 295 | | 364 | |
| 296 | var ast = try std.zig.Ast.parse(gpa, source, .{ .mode = .zon }); | 365 | var ast = try std.zig.Ast.parse(arena, source, .{ .mode = .zon }); |
| 297 | defer if (diag == null) ast.deinit(gpa); | 366 | defer if (diag == null) ast.deinit(arena); |
| 298 | if (diag) |s| s.ast = ast; | 367 | if (diag) |s| s.ast = ast; |
| 299 | | 368 | |
| 300 | // If there's no diagnostics, Zoir exists for the lifetime of this function. If there is a | 369 | // If there's no diagnostics, Zoir exists for the lifetime of this function. If there is a |
| 301 | // diagnostics, ownership is transferred to diagnostics. | 370 | // diagnostics, ownership is transferred to diagnostics. |
| 302 | var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); | 371 | var zoir = try ZonGen.generate(arena, ast, .{ .parse_str_lits = false }); |
| 303 | defer if (diag == null) zoir.deinit(gpa); | 372 | defer if (diag == null) zoir.deinit(arena); |
| 304 | | 373 | |
| 305 | if (diag) |s| s.* = .{}; | 374 | if (diag) |s| s.* = .{}; |
| 306 | return fromZoirAlloc(T, gpa, ast, zoir, diag, options); | 375 | try parseZoirAlloc(T, arena, current, ast, zoir, diag, options); |
| 307 | } | 376 | } |
| 308 | | 377 | |
| 309 | /// Like `fromSlice`, but operates on `Zoir` instead of ZON source. | 378 | /// Like `fromSlice`, but operates on `Zoir` instead of ZON source. |
| ... | @@ -312,7 +381,7 @@ pub fn fromZoir( | ... | @@ -312,7 +381,7 @@ pub fn fromZoir( |
| 312 | ast: Ast, | 381 | ast: Ast, |
| 313 | zoir: Zoir, | 382 | zoir: Zoir, |
| 314 | diag: ?*Diagnostics, | 383 | diag: ?*Diagnostics, |
| 315 | options: Options, | 384 | options: FromOptions, |
| 316 | ) error{ParseZon}!T { | 385 | ) error{ParseZon}!T { |
| 317 | comptime assert(!requiresAllocator(T)); | 386 | comptime assert(!requiresAllocator(T)); |
| 318 | var buf: [0]u8 = .{}; | 387 | var buf: [0]u8 = .{}; |
| ... | @@ -330,6 +399,32 @@ pub fn fromZoir( | ... | @@ -330,6 +399,32 @@ pub fn fromZoir( |
| 330 | }; | 399 | }; |
| 331 | } | 400 | } |
| 332 | | 401 | |
| | 402 | /// Like `updateSlice`, but operates on `Zoir` instead of ZON source. |
| | 403 | pub fn updateFromZoir( |
| | 404 | T: type, |
| | 405 | current: *T, |
| | 406 | ast: Ast, |
| | 407 | zoir: Zoir, |
| | 408 | diag: ?*Diagnostics, |
| | 409 | options: UpdateFromOptions, |
| | 410 | ) error{ParseZon}!void { |
| | 411 | comptime assert(!requiresAllocator(T)); |
| | 412 | var buf: [0]u8 = .{}; |
| | 413 | var failing_allocator = std.heap.FixedBufferAllocator.init(&buf); |
| | 414 | updateFromZoirAlloc( |
| | 415 | T, |
| | 416 | failing_allocator.allocator(), |
| | 417 | current, |
| | 418 | ast, |
| | 419 | zoir, |
| | 420 | diag, |
| | 421 | options, |
| | 422 | ) catch |err| switch (err) { |
| | 423 | error.OutOfMemory => unreachable, // Checked by comptime assertion above |
| | 424 | else => |e| return e, |
| | 425 | }; |
| | 426 | } |
| | 427 | |
| 333 | /// Like `fromSliceAlloc`, but operates on `Zoir` instead of ZON source. | 428 | /// Like `fromSliceAlloc`, but operates on `Zoir` instead of ZON source. |
| 334 | pub fn fromZoirAlloc( | 429 | pub fn fromZoirAlloc( |
| 335 | T: type, | 430 | T: type, |
| ... | @@ -337,11 +432,36 @@ pub fn fromZoirAlloc( | ... | @@ -337,11 +432,36 @@ pub fn fromZoirAlloc( |
| 337 | ast: Ast, | 432 | ast: Ast, |
| 338 | zoir: Zoir, | 433 | zoir: Zoir, |
| 339 | diag: ?*Diagnostics, | 434 | diag: ?*Diagnostics, |
| 340 | options: Options, | 435 | options: FromOptions, |
| 341 | ) error{ OutOfMemory, ParseZon }!T { | 436 | ) error{ OutOfMemory, ParseZon }!T { |
| 342 | return fromZoirNodeAlloc(T, gpa, ast, zoir, .root, diag, options); | 437 | return fromZoirNodeAlloc(T, gpa, ast, zoir, .root, diag, options); |
| 343 | } | 438 | } |
| 344 | | 439 | |
| | 440 | /// Like updateSliceAlloc`, but operates on `Zoir` instead of ZON source. |
| | 441 | pub fn updateFromZoirAlloc( |
| | 442 | T: type, |
| | 443 | arena: Allocator, |
| | 444 | current: *T, |
| | 445 | ast: Ast, |
| | 446 | zoir: Zoir, |
| | 447 | diag: ?*Diagnostics, |
| | 448 | options: UpdateFromOptions, |
| | 449 | ) error{ OutOfMemory, ParseZon }!void { |
| | 450 | try parseZoirAlloc(T, arena, current, ast, zoir, diag, options.toParserOptions()); |
| | 451 | } |
| | 452 | |
| | 453 | fn parseZoirAlloc( |
| | 454 | T: type, |
| | 455 | gpa: Allocator, |
| | 456 | current: *T, |
| | 457 | ast: Ast, |
| | 458 | zoir: Zoir, |
| | 459 | diag: ?*Diagnostics, |
| | 460 | options: Parser.Options, |
| | 461 | ) error{ OutOfMemory, ParseZon }!void { |
| | 462 | try parseZoirNodeAlloc(T, gpa, current, ast, zoir, .root, diag, options); |
| | 463 | } |
| | 464 | |
| 345 | /// Like `fromZoir`, but the parse starts at `node` instead of root. | 465 | /// Like `fromZoir`, but the parse starts at `node` instead of root. |
| 346 | pub fn fromZoirNode( | 466 | pub fn fromZoirNode( |
| 347 | T: type, | 467 | T: type, |
| ... | @@ -349,7 +469,7 @@ pub fn fromZoirNode( | ... | @@ -349,7 +469,7 @@ pub fn fromZoirNode( |
| 349 | zoir: Zoir, | 469 | zoir: Zoir, |
| 350 | node: Zoir.Node.Index, | 470 | node: Zoir.Node.Index, |
| 351 | diag: ?*Diagnostics, | 471 | diag: ?*Diagnostics, |
| 352 | options: Options, | 472 | options: FromOptions, |
| 353 | ) error{ParseZon}!T { | 473 | ) error{ParseZon}!T { |
| 354 | comptime assert(!requiresAllocator(T)); | 474 | comptime assert(!requiresAllocator(T)); |
| 355 | var buf: [0]u8 = .{}; | 475 | var buf: [0]u8 = .{}; |
| ... | @@ -368,6 +488,34 @@ pub fn fromZoirNode( | ... | @@ -368,6 +488,34 @@ pub fn fromZoirNode( |
| 368 | }; | 488 | }; |
| 369 | } | 489 | } |
| 370 | | 490 | |
| | 491 | /// Like `updateZoir`, but the parse starts at `node` instead of root. |
| | 492 | pub fn updateFromZoirNode( |
| | 493 | T: type, |
| | 494 | current: *T, |
| | 495 | ast: Ast, |
| | 496 | zoir: Zoir, |
| | 497 | node: Zoir.Node.Index, |
| | 498 | diag: ?*Diagnostics, |
| | 499 | options: UpdateFromOptions, |
| | 500 | ) error{ParseZon}!void { |
| | 501 | comptime assert(!requiresAllocator(T)); |
| | 502 | var buf: [0]u8 = .{}; |
| | 503 | var failing_allocator = std.heap.FixedBufferAllocator.init(&buf); |
| | 504 | updateFromZoirNodeAlloc( |
| | 505 | T, |
| | 506 | failing_allocator.allocator(), |
| | 507 | current, |
| | 508 | ast, |
| | 509 | zoir, |
| | 510 | node, |
| | 511 | diag, |
| | 512 | options, |
| | 513 | ) catch |err| switch (err) { |
| | 514 | error.OutOfMemory => unreachable, // Checked by comptime assertion above |
| | 515 | else => |e| return e, |
| | 516 | }; |
| | 517 | } |
| | 518 | |
| 371 | /// Like `fromZoirAlloc`, but the parse starts at `node` instead of root. | 519 | /// Like `fromZoirAlloc`, but the parse starts at `node` instead of root. |
| 372 | pub fn fromZoirNodeAlloc( | 520 | pub fn fromZoirNodeAlloc( |
| 373 | T: type, | 521 | T: type, |
| ... | @@ -376,8 +524,37 @@ pub fn fromZoirNodeAlloc( | ... | @@ -376,8 +524,37 @@ pub fn fromZoirNodeAlloc( |
| 376 | zoir: Zoir, | 524 | zoir: Zoir, |
| 377 | node: Zoir.Node.Index, | 525 | node: Zoir.Node.Index, |
| 378 | diag: ?*Diagnostics, | 526 | diag: ?*Diagnostics, |
| 379 | options: Options, | 527 | options: FromOptions, |
| 380 | ) error{ OutOfMemory, ParseZon }!T { | 528 | ) error{ OutOfMemory, ParseZon }!T { |
| | 529 | var out: T = undefined; |
| | 530 | try parseZoirNodeAlloc(T, gpa, &out, ast, zoir, node, diag, options.toParserOptions()); |
| | 531 | return out; |
| | 532 | } |
| | 533 | |
| | 534 | /// Like `updateZoirNodeAlloc`, but the parse starts at `node` instead of root. |
| | 535 | pub fn updateFromZoirNodeAlloc( |
| | 536 | T: type, |
| | 537 | arena: Allocator, |
| | 538 | current: *T, |
| | 539 | ast: Ast, |
| | 540 | zoir: Zoir, |
| | 541 | node: Zoir.Node.Index, |
| | 542 | diag: ?*Diagnostics, |
| | 543 | options: UpdateFromOptions, |
| | 544 | ) error{ OutOfMemory, ParseZon }!void { |
| | 545 | return parseZoirNodeAlloc(T, arena, current, ast, zoir, node, diag, options.toParserOptions()); |
| | 546 | } |
| | 547 | |
| | 548 | fn parseZoirNodeAlloc( |
| | 549 | T: type, |
| | 550 | gpa: Allocator, |
| | 551 | out: *T, |
| | 552 | ast: Ast, |
| | 553 | zoir: Zoir, |
| | 554 | node: Zoir.Node.Index, |
| | 555 | diag: ?*Diagnostics, |
| | 556 | options: Parser.Options, |
| | 557 | ) error{ OutOfMemory, ParseZon }!void { |
| 381 | comptime assert(canParseType(T)); | 558 | comptime assert(canParseType(T)); |
| 382 | | 559 | |
| 383 | if (diag) |s| { | 560 | if (diag) |s| { |
| ... | @@ -398,9 +575,7 @@ pub fn fromZoirNodeAlloc( | ... | @@ -398,9 +575,7 @@ pub fn fromZoirNodeAlloc( |
| 398 | .diag = diag, | 575 | .diag = diag, |
| 399 | }; | 576 | }; |
| 400 | | 577 | |
| 401 | var out: T = undefined; | 578 | try parser.parseExprInto(node, out); |
| 402 | try parser.parseExprInto(node, &out); | | |
| 403 | return out; | | |
| 404 | } | 579 | } |
| 405 | | 580 | |
| 406 | /// Frees ZON values. | 581 | /// Frees ZON values. |
| ... | @@ -486,7 +661,14 @@ const Parser = struct { | ... | @@ -486,7 +661,14 @@ const Parser = struct { |
| 486 | ast: Ast, | 661 | ast: Ast, |
| 487 | zoir: Zoir, | 662 | zoir: Zoir, |
| 488 | diag: ?*Diagnostics, | 663 | diag: ?*Diagnostics, |
| 489 | options: Options, | 664 | options: Parser.Options, |
| | 665 | pointer_depth: usize = 0, |
| | 666 | |
| | 667 | pub const Options = struct { |
| | 668 | ignore_unknown_fields: bool, |
| | 669 | free_on_error: bool, |
| | 670 | ignore_missing_fields: bool, |
| | 671 | }; |
| 490 | | 672 | |
| 491 | const ParseExprError = error{ ParseZon, OutOfMemory }; | 673 | const ParseExprError = error{ ParseZon, OutOfMemory }; |
| 492 | | 674 | |
| ... | @@ -529,6 +711,8 @@ const Parser = struct { | ... | @@ -529,6 +711,8 @@ const Parser = struct { |
| 529 | ); | 711 | ); |
| 530 | errdefer self.gpa.free(slice); | 712 | errdefer self.gpa.free(slice); |
| 531 | const ptr = &slice[0]; | 713 | const ptr = &slice[0]; |
| | 714 | self.pointer_depth += 1; |
| | 715 | defer self.pointer_depth -= 1; |
| 532 | try self.parseExprInnerInto(node, ptr); | 716 | try self.parseExprInnerInto(node, ptr); |
| 533 | out.* = ptr; | 717 | out.* = ptr; |
| 534 | }, | 718 | }, |
| ... | @@ -883,17 +1067,19 @@ const Parser = struct { | ... | @@ -883,17 +1067,19 @@ const Parser = struct { |
| 883 | } | 1067 | } |
| 884 | | 1068 | |
| 885 | // Fill in any missing default fields | 1069 | // Fill in any missing default fields |
| 886 | inline for (field_found, 0..) |found, i| { | 1070 | if (!self.options.ignore_missing_fields or self.pointer_depth > 0) { |
| 887 | if (!found) { | 1071 | inline for (field_found, 0..) |found, i| { |
| 888 | const field_attrs = info.field_attrs[i]; | 1072 | if (!found) { |
| 889 | if (field_attrs.defaultValue(info.field_types[i])) |default| { | 1073 | const field_attrs = info.field_attrs[i]; |
| 890 | @field(out, info.field_names[i]) = default; | 1074 | if (field_attrs.defaultValue(info.field_types[i])) |default| { |
| 891 | } else { | 1075 | @field(out, info.field_names[i]) = default; |
| 892 | return self.failNodeFmt( | 1076 | } else { |
| 893 | node, | 1077 | return self.failNodeFmt( |
| 894 | "missing required field {s}", | 1078 | node, |
| 895 | .{info.field_names[i]}, | 1079 | "missing required field {s}", |
| 896 | ); | 1080 | .{info.field_names[i]}, |
| | 1081 | ); |
| | 1082 | } |
| 897 | } | 1083 | } |
| 898 | } | 1084 | } |
| 899 | } | 1085 | } |
| ... | @@ -3598,3 +3784,198 @@ test "std.zon aligned pointers" { | ... | @@ -3598,3 +3784,198 @@ test "std.zon aligned pointers" { |
| 3598 | defer free(gpa, found); | 3784 | defer free(gpa, found); |
| 3599 | try std.testing.expectEqualDeep(expected.inner, found.inner); | 3785 | try std.testing.expectEqualDeep(expected.inner, found.inner); |
| 3600 | } | 3786 | } |
| | 3787 | |
| | 3788 | test "std.zon update semantics" { |
| | 3789 | // Test that the update logic works as expected |
| | 3790 | |
| | 3791 | var arena_allocator: std.heap.ArenaAllocator = .init(std.testing.allocator); |
| | 3792 | defer arena_allocator.deinit(); |
| | 3793 | const arena = arena_allocator.allocator(); |
| | 3794 | |
| | 3795 | const Vector = struct { x: f32, y: f32, z: f32 }; |
| | 3796 | const MyStruct = struct { |
| | 3797 | foo: u32 = 123, |
| | 3798 | bar: bool, |
| | 3799 | baz: struct { |
| | 3800 | a: [2]u32, |
| | 3801 | b: enum { c, d } = .d, |
| | 3802 | }, |
| | 3803 | ptr: *Vector, |
| | 3804 | optional: ?u32 = null, |
| | 3805 | str: []const u8 = "hello, world", |
| | 3806 | }; |
| | 3807 | |
| | 3808 | var v: Vector = .{ .x = 1, .y = 2, .z = 3 }; |
| | 3809 | var expected: MyStruct = .{ |
| | 3810 | .foo = 10, |
| | 3811 | .bar = true, |
| | 3812 | .baz = .{ .a = .{ 1, 2 }, .b = .c }, |
| | 3813 | .ptr = &v, |
| | 3814 | }; |
| | 3815 | var found = expected; |
| | 3816 | |
| | 3817 | try updateFromSliceAlloc( |
| | 3818 | MyStruct, |
| | 3819 | arena, |
| | 3820 | &found, |
| | 3821 | ".{}", |
| | 3822 | null, |
| | 3823 | .{}, |
| | 3824 | ); |
| | 3825 | try std.testing.expectEqual(expected, found); |
| | 3826 | |
| | 3827 | try updateFromSliceAlloc( |
| | 3828 | MyStruct, |
| | 3829 | arena, |
| | 3830 | &found, |
| | 3831 | ".{ .bar = false }", |
| | 3832 | null, |
| | 3833 | .{}, |
| | 3834 | ); |
| | 3835 | expected.bar = false; |
| | 3836 | try std.testing.expectEqual(expected, found); |
| | 3837 | |
| | 3838 | try updateFromSliceAlloc( |
| | 3839 | MyStruct, |
| | 3840 | arena, |
| | 3841 | &found, |
| | 3842 | ".{ .baz = .{ .a = .{ 2, 4 } } }", |
| | 3843 | null, |
| | 3844 | .{}, |
| | 3845 | ); |
| | 3846 | expected.baz.a[0] = 2; |
| | 3847 | expected.baz.a[1] = 4; |
| | 3848 | try std.testing.expectEqual(expected, found); |
| | 3849 | |
| | 3850 | try updateFromSliceAlloc( |
| | 3851 | MyStruct, |
| | 3852 | arena, |
| | 3853 | &found, |
| | 3854 | ".{ .foo = 11, .baz = .{ .b = .d } }", |
| | 3855 | null, |
| | 3856 | .{}, |
| | 3857 | ); |
| | 3858 | expected.foo = 11; |
| | 3859 | expected.baz.b = .d; |
| | 3860 | try std.testing.expectEqual(expected, found); |
| | 3861 | |
| | 3862 | try updateFromSliceAlloc( |
| | 3863 | MyStruct, |
| | 3864 | arena, |
| | 3865 | &found, |
| | 3866 | ".{ .optional = 10 }", |
| | 3867 | null, |
| | 3868 | .{}, |
| | 3869 | ); |
| | 3870 | expected.optional = 10; |
| | 3871 | try std.testing.expectEqual(expected, found); |
| | 3872 | |
| | 3873 | try updateFromSliceAlloc( |
| | 3874 | MyStruct, |
| | 3875 | arena, |
| | 3876 | &found, |
| | 3877 | ".{ .optional = null }", |
| | 3878 | null, |
| | 3879 | .{}, |
| | 3880 | ); |
| | 3881 | expected.optional = null; |
| | 3882 | try std.testing.expectEqual(expected, found); |
| | 3883 | |
| | 3884 | try updateFromSliceAlloc( |
| | 3885 | MyStruct, |
| | 3886 | arena, |
| | 3887 | &found, |
| | 3888 | ".{ .ptr = .{ .x = 10, .y = 20, .z = 30 } }", |
| | 3889 | null, |
| | 3890 | .{}, |
| | 3891 | ); |
| | 3892 | expected.ptr.x = 10; |
| | 3893 | expected.ptr.y = 20; |
| | 3894 | expected.ptr.z = 30; |
| | 3895 | try std.testing.expectEqualDeep(expected, found); |
| | 3896 | |
| | 3897 | try updateFromSliceAlloc( |
| | 3898 | MyStruct, |
| | 3899 | arena, |
| | 3900 | &found, |
| | 3901 | ".{ .str = \"foo\" }", |
| | 3902 | null, |
| | 3903 | .{}, |
| | 3904 | ); |
| | 3905 | expected.str = "foo"; |
| | 3906 | try std.testing.expectEqualDeep(expected, found); |
| | 3907 | } |
| | 3908 | |
| | 3909 | test "std.zon variants" { |
| | 3910 | // Just a smoke test, we don't need to test each variant thoroughly because they all call into |
| | 3911 | // the same code. |
| | 3912 | |
| | 3913 | var arena_allocator: std.heap.ArenaAllocator = .init(std.testing.allocator); |
| | 3914 | defer arena_allocator.deinit(); |
| | 3915 | const arena = arena_allocator.allocator(); |
| | 3916 | |
| | 3917 | const Struct = struct { a: u32, b: u32 }; |
| | 3918 | const start: Struct = .{ .a = 10, .b = 20 }; |
| | 3919 | const end: Struct = .{ .a = 100, .b = 20 }; |
| | 3920 | |
| | 3921 | // Update |
| | 3922 | { |
| | 3923 | const ast = try std.zig.Ast.parse(arena, ".{ .a = 100 }", .{ .mode = .zon }); |
| | 3924 | const zoir = try ZonGen.generate(arena, ast, .{ .parse_str_lits = false }); |
| | 3925 | |
| | 3926 | var curr = start; |
| | 3927 | try updateFromSlice(Struct, arena, &curr, ".{ .a = 100 }", null, .{}); |
| | 3928 | try std.testing.expectEqual(end, curr); |
| | 3929 | |
| | 3930 | curr = start; |
| | 3931 | try updateFromSliceAlloc(Struct, arena, &curr, ".{ .a = 100 }", null, .{}); |
| | 3932 | try std.testing.expectEqual(end, curr); |
| | 3933 | |
| | 3934 | curr = start; |
| | 3935 | try updateFromZoir(Struct, &curr, ast, zoir, null, .{}); |
| | 3936 | try std.testing.expectEqual(end, curr); |
| | 3937 | |
| | 3938 | curr = start; |
| | 3939 | try updateFromZoirAlloc(Struct, arena, &curr, ast, zoir, null, .{}); |
| | 3940 | try std.testing.expectEqual(end, curr); |
| | 3941 | |
| | 3942 | curr = start; |
| | 3943 | try updateFromZoirNode(Struct, &curr, ast, zoir, .root, null, .{}); |
| | 3944 | try std.testing.expectEqual(end, curr); |
| | 3945 | |
| | 3946 | curr = start; |
| | 3947 | try updateFromZoirNodeAlloc(Struct, arena, &curr, ast, zoir, .root, null, .{}); |
| | 3948 | try std.testing.expectEqual(end, curr); |
| | 3949 | } |
| | 3950 | |
| | 3951 | // From |
| | 3952 | { |
| | 3953 | const ast = try std.zig.Ast.parse(arena, ".{ .a = 100, .b = 20 }", .{ .mode = .zon }); |
| | 3954 | const zoir = try ZonGen.generate(arena, ast, .{ .parse_str_lits = false }); |
| | 3955 | |
| | 3956 | try std.testing.expectEqual( |
| | 3957 | end, |
| | 3958 | try fromSlice(Struct, arena, ".{ .a = 100, .b = 20 }", null, .{}), |
| | 3959 | ); |
| | 3960 | try std.testing.expectEqual( |
| | 3961 | end, |
| | 3962 | try fromSliceAlloc(Struct, arena, ".{ .a = 100, .b = 20 }", null, .{}), |
| | 3963 | ); |
| | 3964 | try std.testing.expectEqual( |
| | 3965 | try fromZoir(Struct, ast, zoir, null, .{}), |
| | 3966 | end, |
| | 3967 | ); |
| | 3968 | try std.testing.expectEqual( |
| | 3969 | try fromZoirAlloc(Struct, arena, ast, zoir, null, .{}), |
| | 3970 | end, |
| | 3971 | ); |
| | 3972 | try std.testing.expectEqual( |
| | 3973 | try fromZoirNode(Struct, ast, zoir, .root, null, .{}), |
| | 3974 | end, |
| | 3975 | ); |
| | 3976 | try std.testing.expectEqual( |
| | 3977 | try fromZoirNodeAlloc(Struct, arena, ast, zoir, .root, null, .{}), |
| | 3978 | end, |
| | 3979 | ); |
| | 3980 | } |
| | 3981 | } |