| author | |
| committer | |
| log | 13c6eb0d71b253cc55a667e33dbdd4932f3710f1 |
| tree | 8c6eee3ffc63cb6b0ec8f6a4407af3a94a949c64 |
| parent | 953355ebeab881abff4a2c9315daa4fbb290d733 |
| signature |
This commit allows using ZON (Zig Object Notation) in a few ways.
* `@import` can be used to load ZON at comptime and convert it to a
normal Zig value. In this case, `@import` must have a result type.
* `std.zon.parse` can be used to parse ZON at runtime, akin to the
parsing logic in `std.json`.
* `std.zon.stringify` can be used to convert arbitrary data structures
to ZON at runtime, again akin to `std.json`.145 files changed, 8786 insertions(+), 434 deletions(-)
build.zig+1-1| ... | ... | @@ -406,7 +406,7 @@ pub fn build(b: *std.Build) !void { |
| 406 | 406 | const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index]; |
| 407 | 407 | |
| 408 | 408 | const fmt_include_paths = &.{ "lib", "src", "test", "tools", "build.zig", "build.zig.zon" }; |
| 409 | const fmt_exclude_paths = &.{"test/cases"}; | |
| 409 | const fmt_exclude_paths = &.{ "test/cases", "test/behavior/zon" }; | |
| 410 | 410 | const do_fmt = b.addFmt(.{ |
| 411 | 411 | .paths = fmt_include_paths, |
| 412 | 412 | .exclude_paths = fmt_exclude_paths, |
lib/compiler/aro/aro/Value.zig+1-1| ... | ... | @@ -473,7 +473,7 @@ pub fn toInt(v: Value, comptime T: type, comp: *const Compilation) ?T { |
| 473 | 473 | if (comp.interner.get(v.ref()) != .int) return null; |
| 474 | 474 | var space: BigIntSpace = undefined; |
| 475 | 475 | const big_int = v.toBigInt(&space, comp); |
| 476 | return big_int.to(T) catch null; | |
| 476 | return big_int.toInt(T) catch null; | |
| 477 | 477 | } |
| 478 | 478 | |
| 479 | 479 | const ComplexOp = enum { |
lib/compiler/aro/backend/Interner.zig+2-2| ... | ... | @@ -628,13 +628,13 @@ pub fn put(i: *Interner, gpa: Allocator, key: Key) !Ref { |
| 628 | 628 | if (data.fitsInTwosComp(.unsigned, 32)) { |
| 629 | 629 | i.items.appendAssumeCapacity(.{ |
| 630 | 630 | .tag = .u32, |
| 631 | .data = data.to(u32) catch unreachable, | |
| 631 | .data = data.toInt(u32) catch unreachable, | |
| 632 | 632 | }); |
| 633 | 633 | break :int; |
| 634 | 634 | } else if (data.fitsInTwosComp(.signed, 32)) { |
| 635 | 635 | i.items.appendAssumeCapacity(.{ |
| 636 | 636 | .tag = .i32, |
| 637 | .data = @bitCast(data.to(i32) catch unreachable), | |
| 637 | .data = @bitCast(data.toInt(i32) catch unreachable), | |
| 638 | 638 | }); |
| 639 | 639 | break :int; |
| 640 | 640 | } |
lib/std/math/big/int.zig+36-6| ... | ... | @@ -2175,10 +2175,13 @@ pub const Const = struct { |
| 2175 | 2175 | TargetTooSmall, |
| 2176 | 2176 | }; |
| 2177 | 2177 | |
| 2178 | /// Convert self to type T. | |
| 2178 | /// Deprecated; use `toInt`. | |
| 2179 | pub const to = toInt; | |
| 2180 | ||
| 2181 | /// Convert self to integer type T. | |
| 2179 | 2182 | /// |
| 2180 | 2183 | /// Returns an error if self cannot be narrowed into the requested type without truncation. |
| 2181 | pub fn to(self: Const, comptime T: type) ConvertError!T { | |
| 2184 | pub fn toInt(self: Const, comptime T: type) ConvertError!T { | |
| 2182 | 2185 | switch (@typeInfo(T)) { |
| 2183 | 2186 | .int => |info| { |
| 2184 | 2187 | // Make sure -0 is handled correctly. |
| ... | ... | @@ -2216,7 +2219,26 @@ pub const Const = struct { |
| 2216 | 2219 | } |
| 2217 | 2220 | } |
| 2218 | 2221 | }, |
| 2219 | else => @compileError("cannot convert Const to type " ++ @typeName(T)), | |
| 2222 | else => @compileError("expected int type, found '" ++ @typeName(T) ++ "'"), | |
| 2223 | } | |
| 2224 | } | |
| 2225 | ||
| 2226 | /// Convert self to float type T. | |
| 2227 | pub fn toFloat(self: Const, comptime T: type) T { | |
| 2228 | if (self.limbs.len == 0) return 0; | |
| 2229 | ||
| 2230 | const base = std.math.maxInt(std.math.big.Limb) + 1; | |
| 2231 | var result: f128 = 0; | |
| 2232 | var i: usize = self.limbs.len; | |
| 2233 | while (i != 0) { | |
| 2234 | i -= 1; | |
| 2235 | const limb: f128 = @floatFromInt(self.limbs[i]); | |
| 2236 | result = @mulAdd(f128, base, result, limb); | |
| 2237 | } | |
| 2238 | if (self.positive) { | |
| 2239 | return @floatCast(result); | |
| 2240 | } else { | |
| 2241 | return @floatCast(-result); | |
| 2220 | 2242 | } |
| 2221 | 2243 | } |
| 2222 | 2244 | |
| ... | ... | @@ -2775,11 +2797,19 @@ pub const Managed = struct { |
| 2775 | 2797 | |
| 2776 | 2798 | pub const ConvertError = Const.ConvertError; |
| 2777 | 2799 | |
| 2778 | /// Convert self to type T. | |
| 2800 | /// Deprecated; use `toInt`. | |
| 2801 | pub const to = toInt; | |
| 2802 | ||
| 2803 | /// Convert self to integer type T. | |
| 2779 | 2804 | /// |
| 2780 | 2805 | /// Returns an error if self cannot be narrowed into the requested type without truncation. |
| 2781 | pub fn to(self: Managed, comptime T: type) ConvertError!T { | |
| 2782 | return self.toConst().to(T); | |
| 2806 | pub fn toInt(self: Managed, comptime T: type) ConvertError!T { | |
| 2807 | return self.toConst().toInt(T); | |
| 2808 | } | |
| 2809 | ||
| 2810 | /// Convert self to float type T. | |
| 2811 | pub fn toFloat(self: Managed, comptime T: type) T { | |
| 2812 | return self.toConst().toFloat(T); | |
| 2783 | 2813 | } |
| 2784 | 2814 | |
| 2785 | 2815 | /// Set self from the string representation `value`. |
lib/std/math/big/int_test.zig+219-219| ... | ... | @@ -53,21 +53,21 @@ test "comptime_int to" { |
| 53 | 53 | var a = try Managed.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab); |
| 54 | 54 | defer a.deinit(); |
| 55 | 55 | |
| 56 | try testing.expect((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab); | |
| 56 | try testing.expect((try a.toInt(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab); | |
| 57 | 57 | } |
| 58 | 58 | |
| 59 | 59 | test "sub-limb to" { |
| 60 | 60 | var a = try Managed.initSet(testing.allocator, 10); |
| 61 | 61 | defer a.deinit(); |
| 62 | 62 | |
| 63 | try testing.expect((try a.to(u8)) == 10); | |
| 63 | try testing.expect((try a.toInt(u8)) == 10); | |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | 66 | test "set negative minimum" { |
| 67 | 67 | var a = try Managed.initSet(testing.allocator, @as(i64, minInt(i64))); |
| 68 | 68 | defer a.deinit(); |
| 69 | 69 | |
| 70 | try testing.expect((try a.to(i64)) == minInt(i64)); | |
| 70 | try testing.expect((try a.toInt(i64)) == minInt(i64)); | |
| 71 | 71 | } |
| 72 | 72 | |
| 73 | 73 | test "set double-width maximum then zero" { |
| ... | ... | @@ -75,14 +75,14 @@ test "set double-width maximum then zero" { |
| 75 | 75 | defer a.deinit(); |
| 76 | 76 | try a.set(@as(DoubleLimb, 0)); |
| 77 | 77 | |
| 78 | try testing.expectEqual(@as(DoubleLimb, 0), try a.to(DoubleLimb)); | |
| 78 | try testing.expectEqual(@as(DoubleLimb, 0), try a.toInt(DoubleLimb)); | |
| 79 | 79 | } |
| 80 | 80 | |
| 81 | 81 | test "to target too small error" { |
| 82 | 82 | var a = try Managed.initSet(testing.allocator, 0xffffffff); |
| 83 | 83 | defer a.deinit(); |
| 84 | 84 | |
| 85 | try testing.expectError(error.TargetTooSmall, a.to(u8)); | |
| 85 | try testing.expectError(error.TargetTooSmall, a.toInt(u8)); | |
| 86 | 86 | } |
| 87 | 87 | |
| 88 | 88 | test "normalize" { |
| ... | ... | @@ -191,28 +191,28 @@ test "bitcount/to" { |
| 191 | 191 | try a.set(0); |
| 192 | 192 | try testing.expect(a.bitCountTwosComp() == 0); |
| 193 | 193 | |
| 194 | try testing.expect((try a.to(u0)) == 0); | |
| 195 | try testing.expect((try a.to(i0)) == 0); | |
| 194 | try testing.expect((try a.toInt(u0)) == 0); | |
| 195 | try testing.expect((try a.toInt(i0)) == 0); | |
| 196 | 196 | |
| 197 | 197 | try a.set(-1); |
| 198 | 198 | try testing.expect(a.bitCountTwosComp() == 1); |
| 199 | try testing.expect((try a.to(i1)) == -1); | |
| 199 | try testing.expect((try a.toInt(i1)) == -1); | |
| 200 | 200 | |
| 201 | 201 | try a.set(-8); |
| 202 | 202 | try testing.expect(a.bitCountTwosComp() == 4); |
| 203 | try testing.expect((try a.to(i4)) == -8); | |
| 203 | try testing.expect((try a.toInt(i4)) == -8); | |
| 204 | 204 | |
| 205 | 205 | try a.set(127); |
| 206 | 206 | try testing.expect(a.bitCountTwosComp() == 7); |
| 207 | try testing.expect((try a.to(u7)) == 127); | |
| 207 | try testing.expect((try a.toInt(u7)) == 127); | |
| 208 | 208 | |
| 209 | 209 | try a.set(-128); |
| 210 | 210 | try testing.expect(a.bitCountTwosComp() == 8); |
| 211 | try testing.expect((try a.to(i8)) == -128); | |
| 211 | try testing.expect((try a.toInt(i8)) == -128); | |
| 212 | 212 | |
| 213 | 213 | try a.set(-129); |
| 214 | 214 | try testing.expect(a.bitCountTwosComp() == 9); |
| 215 | try testing.expect((try a.to(i9)) == -129); | |
| 215 | try testing.expect((try a.toInt(i9)) == -129); | |
| 216 | 216 | } |
| 217 | 217 | |
| 218 | 218 | test "fits" { |
| ... | ... | @@ -248,7 +248,7 @@ test "string set" { |
| 248 | 248 | defer a.deinit(); |
| 249 | 249 | |
| 250 | 250 | try a.setString(10, "120317241209124781241290847124"); |
| 251 | try testing.expect((try a.to(u128)) == 120317241209124781241290847124); | |
| 251 | try testing.expect((try a.toInt(u128)) == 120317241209124781241290847124); | |
| 252 | 252 | } |
| 253 | 253 | |
| 254 | 254 | test "string negative" { |
| ... | ... | @@ -256,7 +256,7 @@ test "string negative" { |
| 256 | 256 | defer a.deinit(); |
| 257 | 257 | |
| 258 | 258 | try a.setString(10, "-1023"); |
| 259 | try testing.expect((try a.to(i32)) == -1023); | |
| 259 | try testing.expect((try a.toInt(i32)) == -1023); | |
| 260 | 260 | } |
| 261 | 261 | |
| 262 | 262 | test "string set number with underscores" { |
| ... | ... | @@ -264,7 +264,7 @@ test "string set number with underscores" { |
| 264 | 264 | defer a.deinit(); |
| 265 | 265 | |
| 266 | 266 | try a.setString(10, "__1_2_0_3_1_7_2_4_1_2_0_____9_1__2__4_7_8_1_2_4_1_2_9_0_8_4_7_1_2_4___"); |
| 267 | try testing.expect((try a.to(u128)) == 120317241209124781241290847124); | |
| 267 | try testing.expect((try a.toInt(u128)) == 120317241209124781241290847124); | |
| 268 | 268 | } |
| 269 | 269 | |
| 270 | 270 | test "string set case insensitive number" { |
| ... | ... | @@ -272,7 +272,7 @@ test "string set case insensitive number" { |
| 272 | 272 | defer a.deinit(); |
| 273 | 273 | |
| 274 | 274 | try a.setString(16, "aB_cD_eF"); |
| 275 | try testing.expect((try a.to(u32)) == 0xabcdef); | |
| 275 | try testing.expect((try a.toInt(u32)) == 0xabcdef); | |
| 276 | 276 | } |
| 277 | 277 | |
| 278 | 278 | test "string set bad char error" { |
| ... | ... | @@ -306,11 +306,11 @@ fn testTwosComplementLimit(comptime T: type) !void { |
| 306 | 306 | |
| 307 | 307 | try a.setTwosCompIntLimit(.max, int_info.signedness, int_info.bits); |
| 308 | 308 | const max: T = maxInt(T); |
| 309 | try testing.expect(max == try a.to(T)); | |
| 309 | try testing.expect(max == try a.toInt(T)); | |
| 310 | 310 | |
| 311 | 311 | try a.setTwosCompIntLimit(.min, int_info.signedness, int_info.bits); |
| 312 | 312 | const min: T = minInt(T); |
| 313 | try testing.expect(min == try a.to(T)); | |
| 313 | try testing.expect(min == try a.toInt(T)); | |
| 314 | 314 | } |
| 315 | 315 | |
| 316 | 316 | test "string to" { |
| ... | ... | @@ -381,12 +381,12 @@ test "clone" { |
| 381 | 381 | var b = try a.clone(); |
| 382 | 382 | defer b.deinit(); |
| 383 | 383 | |
| 384 | try testing.expect((try a.to(u32)) == 1234); | |
| 385 | try testing.expect((try b.to(u32)) == 1234); | |
| 384 | try testing.expect((try a.toInt(u32)) == 1234); | |
| 385 | try testing.expect((try b.toInt(u32)) == 1234); | |
| 386 | 386 | |
| 387 | 387 | try a.set(77); |
| 388 | try testing.expect((try a.to(u32)) == 77); | |
| 389 | try testing.expect((try b.to(u32)) == 1234); | |
| 388 | try testing.expect((try a.toInt(u32)) == 77); | |
| 389 | try testing.expect((try b.toInt(u32)) == 1234); | |
| 390 | 390 | } |
| 391 | 391 | |
| 392 | 392 | test "swap" { |
| ... | ... | @@ -395,20 +395,20 @@ test "swap" { |
| 395 | 395 | var b = try Managed.initSet(testing.allocator, 5678); |
| 396 | 396 | defer b.deinit(); |
| 397 | 397 | |
| 398 | try testing.expect((try a.to(u32)) == 1234); | |
| 399 | try testing.expect((try b.to(u32)) == 5678); | |
| 398 | try testing.expect((try a.toInt(u32)) == 1234); | |
| 399 | try testing.expect((try b.toInt(u32)) == 5678); | |
| 400 | 400 | |
| 401 | 401 | a.swap(&b); |
| 402 | 402 | |
| 403 | try testing.expect((try a.to(u32)) == 5678); | |
| 404 | try testing.expect((try b.to(u32)) == 1234); | |
| 403 | try testing.expect((try a.toInt(u32)) == 5678); | |
| 404 | try testing.expect((try b.toInt(u32)) == 1234); | |
| 405 | 405 | } |
| 406 | 406 | |
| 407 | 407 | test "to negative" { |
| 408 | 408 | var a = try Managed.initSet(testing.allocator, -10); |
| 409 | 409 | defer a.deinit(); |
| 410 | 410 | |
| 411 | try testing.expect((try a.to(i32)) == -10); | |
| 411 | try testing.expect((try a.toInt(i32)) == -10); | |
| 412 | 412 | } |
| 413 | 413 | |
| 414 | 414 | test "compare" { |
| ... | ... | @@ -466,10 +466,10 @@ test "abs" { |
| 466 | 466 | defer a.deinit(); |
| 467 | 467 | |
| 468 | 468 | a.abs(); |
| 469 | try testing.expect((try a.to(u32)) == 5); | |
| 469 | try testing.expect((try a.toInt(u32)) == 5); | |
| 470 | 470 | |
| 471 | 471 | a.abs(); |
| 472 | try testing.expect((try a.to(u32)) == 5); | |
| 472 | try testing.expect((try a.toInt(u32)) == 5); | |
| 473 | 473 | } |
| 474 | 474 | |
| 475 | 475 | test "negate" { |
| ... | ... | @@ -477,10 +477,10 @@ test "negate" { |
| 477 | 477 | defer a.deinit(); |
| 478 | 478 | |
| 479 | 479 | a.negate(); |
| 480 | try testing.expect((try a.to(i32)) == -5); | |
| 480 | try testing.expect((try a.toInt(i32)) == -5); | |
| 481 | 481 | |
| 482 | 482 | a.negate(); |
| 483 | try testing.expect((try a.to(i32)) == 5); | |
| 483 | try testing.expect((try a.toInt(i32)) == 5); | |
| 484 | 484 | } |
| 485 | 485 | |
| 486 | 486 | test "add single-single" { |
| ... | ... | @@ -493,7 +493,7 @@ test "add single-single" { |
| 493 | 493 | defer c.deinit(); |
| 494 | 494 | try c.add(&a, &b); |
| 495 | 495 | |
| 496 | try testing.expect((try c.to(u32)) == 55); | |
| 496 | try testing.expect((try c.toInt(u32)) == 55); | |
| 497 | 497 | } |
| 498 | 498 | |
| 499 | 499 | test "add multi-single" { |
| ... | ... | @@ -506,10 +506,10 @@ test "add multi-single" { |
| 506 | 506 | defer c.deinit(); |
| 507 | 507 | |
| 508 | 508 | try c.add(&a, &b); |
| 509 | try testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2); | |
| 509 | try testing.expect((try c.toInt(DoubleLimb)) == maxInt(Limb) + 2); | |
| 510 | 510 | |
| 511 | 511 | try c.add(&b, &a); |
| 512 | try testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2); | |
| 512 | try testing.expect((try c.toInt(DoubleLimb)) == maxInt(Limb) + 2); | |
| 513 | 513 | } |
| 514 | 514 | |
| 515 | 515 | test "add multi-multi" { |
| ... | ... | @@ -527,7 +527,7 @@ test "add multi-multi" { |
| 527 | 527 | defer c.deinit(); |
| 528 | 528 | try c.add(&a, &b); |
| 529 | 529 | |
| 530 | try testing.expect((try c.to(u128)) == op1 + op2); | |
| 530 | try testing.expect((try c.toInt(u128)) == op1 + op2); | |
| 531 | 531 | } |
| 532 | 532 | |
| 533 | 533 | test "add zero-zero" { |
| ... | ... | @@ -540,7 +540,7 @@ test "add zero-zero" { |
| 540 | 540 | defer c.deinit(); |
| 541 | 541 | try c.add(&a, &b); |
| 542 | 542 | |
| 543 | try testing.expect((try c.to(u32)) == 0); | |
| 543 | try testing.expect((try c.toInt(u32)) == 0); | |
| 544 | 544 | } |
| 545 | 545 | |
| 546 | 546 | test "add alias multi-limb nonzero-zero" { |
| ... | ... | @@ -552,7 +552,7 @@ test "add alias multi-limb nonzero-zero" { |
| 552 | 552 | |
| 553 | 553 | try a.add(&a, &b); |
| 554 | 554 | |
| 555 | try testing.expect((try a.to(u128)) == op1); | |
| 555 | try testing.expect((try a.toInt(u128)) == op1); | |
| 556 | 556 | } |
| 557 | 557 | |
| 558 | 558 | test "add sign" { |
| ... | ... | @@ -569,16 +569,16 @@ test "add sign" { |
| 569 | 569 | defer neg_two.deinit(); |
| 570 | 570 | |
| 571 | 571 | try a.add(&one, &two); |
| 572 | try testing.expect((try a.to(i32)) == 3); | |
| 572 | try testing.expect((try a.toInt(i32)) == 3); | |
| 573 | 573 | |
| 574 | 574 | try a.add(&neg_one, &two); |
| 575 | try testing.expect((try a.to(i32)) == 1); | |
| 575 | try testing.expect((try a.toInt(i32)) == 1); | |
| 576 | 576 | |
| 577 | 577 | try a.add(&one, &neg_two); |
| 578 | try testing.expect((try a.to(i32)) == -1); | |
| 578 | try testing.expect((try a.toInt(i32)) == -1); | |
| 579 | 579 | |
| 580 | 580 | try a.add(&neg_one, &neg_two); |
| 581 | try testing.expect((try a.to(i32)) == -3); | |
| 581 | try testing.expect((try a.toInt(i32)) == -3); | |
| 582 | 582 | } |
| 583 | 583 | |
| 584 | 584 | test "add comptime scalar" { |
| ... | ... | @@ -589,7 +589,7 @@ test "add comptime scalar" { |
| 589 | 589 | defer b.deinit(); |
| 590 | 590 | try b.addScalar(&a, 5); |
| 591 | 591 | |
| 592 | try testing.expect((try b.to(u32)) == 55); | |
| 592 | try testing.expect((try b.toInt(u32)) == 55); | |
| 593 | 593 | } |
| 594 | 594 | |
| 595 | 595 | test "add scalar" { |
| ... | ... | @@ -600,7 +600,7 @@ test "add scalar" { |
| 600 | 600 | defer b.deinit(); |
| 601 | 601 | try b.addScalar(&a, @as(u32, 31)); |
| 602 | 602 | |
| 603 | try testing.expect((try b.to(u32)) == 154); | |
| 603 | try testing.expect((try b.toInt(u32)) == 154); | |
| 604 | 604 | } |
| 605 | 605 | |
| 606 | 606 | test "addWrap single-single, unsigned" { |
| ... | ... | @@ -613,7 +613,7 @@ test "addWrap single-single, unsigned" { |
| 613 | 613 | const wrapped = try a.addWrap(&a, &b, .unsigned, 17); |
| 614 | 614 | |
| 615 | 615 | try testing.expect(wrapped); |
| 616 | try testing.expect((try a.to(u17)) == 9); | |
| 616 | try testing.expect((try a.toInt(u17)) == 9); | |
| 617 | 617 | } |
| 618 | 618 | |
| 619 | 619 | test "subWrap single-single, unsigned" { |
| ... | ... | @@ -626,7 +626,7 @@ test "subWrap single-single, unsigned" { |
| 626 | 626 | const wrapped = try a.subWrap(&a, &b, .unsigned, 17); |
| 627 | 627 | |
| 628 | 628 | try testing.expect(wrapped); |
| 629 | try testing.expect((try a.to(u17)) == 1); | |
| 629 | try testing.expect((try a.toInt(u17)) == 1); | |
| 630 | 630 | } |
| 631 | 631 | |
| 632 | 632 | test "addWrap multi-multi, unsigned, limb aligned" { |
| ... | ... | @@ -639,7 +639,7 @@ test "addWrap multi-multi, unsigned, limb aligned" { |
| 639 | 639 | const wrapped = try a.addWrap(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); |
| 640 | 640 | |
| 641 | 641 | try testing.expect(wrapped); |
| 642 | try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 1); | |
| 642 | try testing.expect((try a.toInt(DoubleLimb)) == maxInt(DoubleLimb) - 1); | |
| 643 | 643 | } |
| 644 | 644 | |
| 645 | 645 | test "subWrap single-multi, unsigned, limb aligned" { |
| ... | ... | @@ -652,7 +652,7 @@ test "subWrap single-multi, unsigned, limb aligned" { |
| 652 | 652 | const wrapped = try a.subWrap(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); |
| 653 | 653 | |
| 654 | 654 | try testing.expect(wrapped); |
| 655 | try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 88); | |
| 655 | try testing.expect((try a.toInt(DoubleLimb)) == maxInt(DoubleLimb) - 88); | |
| 656 | 656 | } |
| 657 | 657 | |
| 658 | 658 | test "addWrap single-single, signed" { |
| ... | ... | @@ -665,7 +665,7 @@ test "addWrap single-single, signed" { |
| 665 | 665 | const wrapped = try a.addWrap(&a, &b, .signed, @bitSizeOf(i21)); |
| 666 | 666 | |
| 667 | 667 | try testing.expect(wrapped); |
| 668 | try testing.expect((try a.to(i21)) == minInt(i21)); | |
| 668 | try testing.expect((try a.toInt(i21)) == minInt(i21)); | |
| 669 | 669 | } |
| 670 | 670 | |
| 671 | 671 | test "subWrap single-single, signed" { |
| ... | ... | @@ -678,7 +678,7 @@ test "subWrap single-single, signed" { |
| 678 | 678 | const wrapped = try a.subWrap(&a, &b, .signed, @bitSizeOf(i21)); |
| 679 | 679 | |
| 680 | 680 | try testing.expect(wrapped); |
| 681 | try testing.expect((try a.to(i21)) == maxInt(i21)); | |
| 681 | try testing.expect((try a.toInt(i21)) == maxInt(i21)); | |
| 682 | 682 | } |
| 683 | 683 | |
| 684 | 684 | test "addWrap multi-multi, signed, limb aligned" { |
| ... | ... | @@ -691,7 +691,7 @@ test "addWrap multi-multi, signed, limb aligned" { |
| 691 | 691 | const wrapped = try a.addWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); |
| 692 | 692 | |
| 693 | 693 | try testing.expect(wrapped); |
| 694 | try testing.expect((try a.to(SignedDoubleLimb)) == -2); | |
| 694 | try testing.expect((try a.toInt(SignedDoubleLimb)) == -2); | |
| 695 | 695 | } |
| 696 | 696 | |
| 697 | 697 | test "subWrap single-multi, signed, limb aligned" { |
| ... | ... | @@ -704,7 +704,7 @@ test "subWrap single-multi, signed, limb aligned" { |
| 704 | 704 | const wrapped = try a.subWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); |
| 705 | 705 | |
| 706 | 706 | try testing.expect(wrapped); |
| 707 | try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb)); | |
| 707 | try testing.expect((try a.toInt(SignedDoubleLimb)) == maxInt(SignedDoubleLimb)); | |
| 708 | 708 | } |
| 709 | 709 | |
| 710 | 710 | test "addSat single-single, unsigned" { |
| ... | ... | @@ -716,7 +716,7 @@ test "addSat single-single, unsigned" { |
| 716 | 716 | |
| 717 | 717 | try a.addSat(&a, &b, .unsigned, 17); |
| 718 | 718 | |
| 719 | try testing.expect((try a.to(u17)) == maxInt(u17)); | |
| 719 | try testing.expect((try a.toInt(u17)) == maxInt(u17)); | |
| 720 | 720 | } |
| 721 | 721 | |
| 722 | 722 | test "subSat single-single, unsigned" { |
| ... | ... | @@ -728,7 +728,7 @@ test "subSat single-single, unsigned" { |
| 728 | 728 | |
| 729 | 729 | try a.subSat(&a, &b, .unsigned, 17); |
| 730 | 730 | |
| 731 | try testing.expect((try a.to(u17)) == 0); | |
| 731 | try testing.expect((try a.toInt(u17)) == 0); | |
| 732 | 732 | } |
| 733 | 733 | |
| 734 | 734 | test "addSat multi-multi, unsigned, limb aligned" { |
| ... | ... | @@ -740,7 +740,7 @@ test "addSat multi-multi, unsigned, limb aligned" { |
| 740 | 740 | |
| 741 | 741 | try a.addSat(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); |
| 742 | 742 | |
| 743 | try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb)); | |
| 743 | try testing.expect((try a.toInt(DoubleLimb)) == maxInt(DoubleLimb)); | |
| 744 | 744 | } |
| 745 | 745 | |
| 746 | 746 | test "subSat single-multi, unsigned, limb aligned" { |
| ... | ... | @@ -752,7 +752,7 @@ test "subSat single-multi, unsigned, limb aligned" { |
| 752 | 752 | |
| 753 | 753 | try a.subSat(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); |
| 754 | 754 | |
| 755 | try testing.expect((try a.to(DoubleLimb)) == 0); | |
| 755 | try testing.expect((try a.toInt(DoubleLimb)) == 0); | |
| 756 | 756 | } |
| 757 | 757 | |
| 758 | 758 | test "addSat single-single, signed" { |
| ... | ... | @@ -764,7 +764,7 @@ test "addSat single-single, signed" { |
| 764 | 764 | |
| 765 | 765 | try a.addSat(&a, &b, .signed, @bitSizeOf(i14)); |
| 766 | 766 | |
| 767 | try testing.expect((try a.to(i14)) == maxInt(i14)); | |
| 767 | try testing.expect((try a.toInt(i14)) == maxInt(i14)); | |
| 768 | 768 | } |
| 769 | 769 | |
| 770 | 770 | test "subSat single-single, signed" { |
| ... | ... | @@ -776,7 +776,7 @@ test "subSat single-single, signed" { |
| 776 | 776 | |
| 777 | 777 | try a.subSat(&a, &b, .signed, @bitSizeOf(i21)); |
| 778 | 778 | |
| 779 | try testing.expect((try a.to(i21)) == minInt(i21)); | |
| 779 | try testing.expect((try a.toInt(i21)) == minInt(i21)); | |
| 780 | 780 | } |
| 781 | 781 | |
| 782 | 782 | test "addSat multi-multi, signed, limb aligned" { |
| ... | ... | @@ -788,7 +788,7 @@ test "addSat multi-multi, signed, limb aligned" { |
| 788 | 788 | |
| 789 | 789 | try a.addSat(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); |
| 790 | 790 | |
| 791 | try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb)); | |
| 791 | try testing.expect((try a.toInt(SignedDoubleLimb)) == maxInt(SignedDoubleLimb)); | |
| 792 | 792 | } |
| 793 | 793 | |
| 794 | 794 | test "subSat single-multi, signed, limb aligned" { |
| ... | ... | @@ -800,7 +800,7 @@ test "subSat single-multi, signed, limb aligned" { |
| 800 | 800 | |
| 801 | 801 | try a.subSat(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); |
| 802 | 802 | |
| 803 | try testing.expect((try a.to(SignedDoubleLimb)) == minInt(SignedDoubleLimb)); | |
| 803 | try testing.expect((try a.toInt(SignedDoubleLimb)) == minInt(SignedDoubleLimb)); | |
| 804 | 804 | } |
| 805 | 805 | |
| 806 | 806 | test "sub single-single" { |
| ... | ... | @@ -813,7 +813,7 @@ test "sub single-single" { |
| 813 | 813 | defer c.deinit(); |
| 814 | 814 | try c.sub(&a, &b); |
| 815 | 815 | |
| 816 | try testing.expect((try c.to(u32)) == 45); | |
| 816 | try testing.expect((try c.toInt(u32)) == 45); | |
| 817 | 817 | } |
| 818 | 818 | |
| 819 | 819 | test "sub multi-single" { |
| ... | ... | @@ -826,7 +826,7 @@ test "sub multi-single" { |
| 826 | 826 | defer c.deinit(); |
| 827 | 827 | try c.sub(&a, &b); |
| 828 | 828 | |
| 829 | try testing.expect((try c.to(Limb)) == maxInt(Limb)); | |
| 829 | try testing.expect((try c.toInt(Limb)) == maxInt(Limb)); | |
| 830 | 830 | } |
| 831 | 831 | |
| 832 | 832 | test "sub multi-multi" { |
| ... | ... | @@ -843,7 +843,7 @@ test "sub multi-multi" { |
| 843 | 843 | defer c.deinit(); |
| 844 | 844 | try c.sub(&a, &b); |
| 845 | 845 | |
| 846 | try testing.expect((try c.to(u128)) == op1 - op2); | |
| 846 | try testing.expect((try c.toInt(u128)) == op1 - op2); | |
| 847 | 847 | } |
| 848 | 848 | |
| 849 | 849 | test "sub equal" { |
| ... | ... | @@ -856,7 +856,7 @@ test "sub equal" { |
| 856 | 856 | defer c.deinit(); |
| 857 | 857 | try c.sub(&a, &b); |
| 858 | 858 | |
| 859 | try testing.expect((try c.to(u32)) == 0); | |
| 859 | try testing.expect((try c.toInt(u32)) == 0); | |
| 860 | 860 | } |
| 861 | 861 | |
| 862 | 862 | test "sub sign" { |
| ... | ... | @@ -873,19 +873,19 @@ test "sub sign" { |
| 873 | 873 | defer neg_two.deinit(); |
| 874 | 874 | |
| 875 | 875 | try a.sub(&one, &two); |
| 876 | try testing.expect((try a.to(i32)) == -1); | |
| 876 | try testing.expect((try a.toInt(i32)) == -1); | |
| 877 | 877 | |
| 878 | 878 | try a.sub(&neg_one, &two); |
| 879 | try testing.expect((try a.to(i32)) == -3); | |
| 879 | try testing.expect((try a.toInt(i32)) == -3); | |
| 880 | 880 | |
| 881 | 881 | try a.sub(&one, &neg_two); |
| 882 | try testing.expect((try a.to(i32)) == 3); | |
| 882 | try testing.expect((try a.toInt(i32)) == 3); | |
| 883 | 883 | |
| 884 | 884 | try a.sub(&neg_one, &neg_two); |
| 885 | try testing.expect((try a.to(i32)) == 1); | |
| 885 | try testing.expect((try a.toInt(i32)) == 1); | |
| 886 | 886 | |
| 887 | 887 | try a.sub(&neg_two, &neg_one); |
| 888 | try testing.expect((try a.to(i32)) == -1); | |
| 888 | try testing.expect((try a.toInt(i32)) == -1); | |
| 889 | 889 | } |
| 890 | 890 | |
| 891 | 891 | test "mul single-single" { |
| ... | ... | @@ -898,7 +898,7 @@ test "mul single-single" { |
| 898 | 898 | defer c.deinit(); |
| 899 | 899 | try c.mul(&a, &b); |
| 900 | 900 | |
| 901 | try testing.expect((try c.to(u64)) == 250); | |
| 901 | try testing.expect((try c.toInt(u64)) == 250); | |
| 902 | 902 | } |
| 903 | 903 | |
| 904 | 904 | test "mul multi-single" { |
| ... | ... | @@ -911,7 +911,7 @@ test "mul multi-single" { |
| 911 | 911 | defer c.deinit(); |
| 912 | 912 | try c.mul(&a, &b); |
| 913 | 913 | |
| 914 | try testing.expect((try c.to(DoubleLimb)) == 2 * maxInt(Limb)); | |
| 914 | try testing.expect((try c.toInt(DoubleLimb)) == 2 * maxInt(Limb)); | |
| 915 | 915 | } |
| 916 | 916 | |
| 917 | 917 | test "mul multi-multi" { |
| ... | ... | @@ -930,7 +930,7 @@ test "mul multi-multi" { |
| 930 | 930 | defer c.deinit(); |
| 931 | 931 | try c.mul(&a, &b); |
| 932 | 932 | |
| 933 | try testing.expect((try c.to(u256)) == op1 * op2); | |
| 933 | try testing.expect((try c.toInt(u256)) == op1 * op2); | |
| 934 | 934 | } |
| 935 | 935 | |
| 936 | 936 | test "mul alias r with a" { |
| ... | ... | @@ -941,7 +941,7 @@ test "mul alias r with a" { |
| 941 | 941 | |
| 942 | 942 | try a.mul(&a, &b); |
| 943 | 943 | |
| 944 | try testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb)); | |
| 944 | try testing.expect((try a.toInt(DoubleLimb)) == 2 * maxInt(Limb)); | |
| 945 | 945 | } |
| 946 | 946 | |
| 947 | 947 | test "mul alias r with b" { |
| ... | ... | @@ -952,7 +952,7 @@ test "mul alias r with b" { |
| 952 | 952 | |
| 953 | 953 | try a.mul(&b, &a); |
| 954 | 954 | |
| 955 | try testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb)); | |
| 955 | try testing.expect((try a.toInt(DoubleLimb)) == 2 * maxInt(Limb)); | |
| 956 | 956 | } |
| 957 | 957 | |
| 958 | 958 | test "mul alias r with a and b" { |
| ... | ... | @@ -961,7 +961,7 @@ test "mul alias r with a and b" { |
| 961 | 961 | |
| 962 | 962 | try a.mul(&a, &a); |
| 963 | 963 | |
| 964 | try testing.expect((try a.to(DoubleLimb)) == maxInt(Limb) * maxInt(Limb)); | |
| 964 | try testing.expect((try a.toInt(DoubleLimb)) == maxInt(Limb) * maxInt(Limb)); | |
| 965 | 965 | } |
| 966 | 966 | |
| 967 | 967 | test "mul a*0" { |
| ... | ... | @@ -974,7 +974,7 @@ test "mul a*0" { |
| 974 | 974 | defer c.deinit(); |
| 975 | 975 | try c.mul(&a, &b); |
| 976 | 976 | |
| 977 | try testing.expect((try c.to(u32)) == 0); | |
| 977 | try testing.expect((try c.toInt(u32)) == 0); | |
| 978 | 978 | } |
| 979 | 979 | |
| 980 | 980 | test "mul 0*0" { |
| ... | ... | @@ -987,7 +987,7 @@ test "mul 0*0" { |
| 987 | 987 | defer c.deinit(); |
| 988 | 988 | try c.mul(&a, &b); |
| 989 | 989 | |
| 990 | try testing.expect((try c.to(u32)) == 0); | |
| 990 | try testing.expect((try c.toInt(u32)) == 0); | |
| 991 | 991 | } |
| 992 | 992 | |
| 993 | 993 | test "mul large" { |
| ... | ... | @@ -1021,7 +1021,7 @@ test "mulWrap single-single unsigned" { |
| 1021 | 1021 | defer c.deinit(); |
| 1022 | 1022 | try c.mulWrap(&a, &b, .unsigned, 17); |
| 1023 | 1023 | |
| 1024 | try testing.expect((try c.to(u17)) == 59836); | |
| 1024 | try testing.expect((try c.toInt(u17)) == 59836); | |
| 1025 | 1025 | } |
| 1026 | 1026 | |
| 1027 | 1027 | test "mulWrap single-single signed" { |
| ... | ... | @@ -1034,7 +1034,7 @@ test "mulWrap single-single signed" { |
| 1034 | 1034 | defer c.deinit(); |
| 1035 | 1035 | try c.mulWrap(&a, &b, .signed, 17); |
| 1036 | 1036 | |
| 1037 | try testing.expect((try c.to(i17)) == -59836); | |
| 1037 | try testing.expect((try c.toInt(i17)) == -59836); | |
| 1038 | 1038 | } |
| 1039 | 1039 | |
| 1040 | 1040 | test "mulWrap multi-multi unsigned" { |
| ... | ... | @@ -1053,7 +1053,7 @@ test "mulWrap multi-multi unsigned" { |
| 1053 | 1053 | defer c.deinit(); |
| 1054 | 1054 | try c.mulWrap(&a, &b, .unsigned, 65); |
| 1055 | 1055 | |
| 1056 | try testing.expect((try c.to(u256)) == (op1 * op2) & ((1 << 65) - 1)); | |
| 1056 | try testing.expect((try c.toInt(u256)) == (op1 * op2) & ((1 << 65) - 1)); | |
| 1057 | 1057 | } |
| 1058 | 1058 | |
| 1059 | 1059 | test "mulWrap multi-multi signed" { |
| ... | ... | @@ -1071,7 +1071,7 @@ test "mulWrap multi-multi signed" { |
| 1071 | 1071 | defer c.deinit(); |
| 1072 | 1072 | try c.mulWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); |
| 1073 | 1073 | |
| 1074 | try testing.expect((try c.to(SignedDoubleLimb)) == minInt(SignedDoubleLimb) + 2); | |
| 1074 | try testing.expect((try c.toInt(SignedDoubleLimb)) == minInt(SignedDoubleLimb) + 2); | |
| 1075 | 1075 | } |
| 1076 | 1076 | |
| 1077 | 1077 | test "mulWrap large" { |
| ... | ... | @@ -1110,8 +1110,8 @@ test "div single-half no rem" { |
| 1110 | 1110 | defer r.deinit(); |
| 1111 | 1111 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1112 | 1112 | |
| 1113 | try testing.expect((try q.to(u32)) == 10); | |
| 1114 | try testing.expect((try r.to(u32)) == 0); | |
| 1113 | try testing.expect((try q.toInt(u32)) == 10); | |
| 1114 | try testing.expect((try r.toInt(u32)) == 0); | |
| 1115 | 1115 | } |
| 1116 | 1116 | |
| 1117 | 1117 | test "div single-half with rem" { |
| ... | ... | @@ -1126,8 +1126,8 @@ test "div single-half with rem" { |
| 1126 | 1126 | defer r.deinit(); |
| 1127 | 1127 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1128 | 1128 | |
| 1129 | try testing.expect((try q.to(u32)) == 9); | |
| 1130 | try testing.expect((try r.to(u32)) == 4); | |
| 1129 | try testing.expect((try q.toInt(u32)) == 9); | |
| 1130 | try testing.expect((try r.toInt(u32)) == 4); | |
| 1131 | 1131 | } |
| 1132 | 1132 | |
| 1133 | 1133 | test "div single-single no rem" { |
| ... | ... | @@ -1143,8 +1143,8 @@ test "div single-single no rem" { |
| 1143 | 1143 | defer r.deinit(); |
| 1144 | 1144 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1145 | 1145 | |
| 1146 | try testing.expect((try q.to(u32)) == 131072); | |
| 1147 | try testing.expect((try r.to(u32)) == 0); | |
| 1146 | try testing.expect((try q.toInt(u32)) == 131072); | |
| 1147 | try testing.expect((try r.toInt(u32)) == 0); | |
| 1148 | 1148 | } |
| 1149 | 1149 | |
| 1150 | 1150 | test "div single-single with rem" { |
| ... | ... | @@ -1159,8 +1159,8 @@ test "div single-single with rem" { |
| 1159 | 1159 | defer r.deinit(); |
| 1160 | 1160 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1161 | 1161 | |
| 1162 | try testing.expect((try q.to(u64)) == 131072); | |
| 1163 | try testing.expect((try r.to(u64)) == 8589934592); | |
| 1162 | try testing.expect((try q.toInt(u64)) == 131072); | |
| 1163 | try testing.expect((try r.toInt(u64)) == 8589934592); | |
| 1164 | 1164 | } |
| 1165 | 1165 | |
| 1166 | 1166 | test "div multi-single no rem" { |
| ... | ... | @@ -1179,8 +1179,8 @@ test "div multi-single no rem" { |
| 1179 | 1179 | defer r.deinit(); |
| 1180 | 1180 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1181 | 1181 | |
| 1182 | try testing.expect((try q.to(u64)) == op1 / op2); | |
| 1183 | try testing.expect((try r.to(u64)) == 0); | |
| 1182 | try testing.expect((try q.toInt(u64)) == op1 / op2); | |
| 1183 | try testing.expect((try r.toInt(u64)) == 0); | |
| 1184 | 1184 | } |
| 1185 | 1185 | |
| 1186 | 1186 | test "div multi-single with rem" { |
| ... | ... | @@ -1199,8 +1199,8 @@ test "div multi-single with rem" { |
| 1199 | 1199 | defer r.deinit(); |
| 1200 | 1200 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1201 | 1201 | |
| 1202 | try testing.expect((try q.to(u64)) == op1 / op2); | |
| 1203 | try testing.expect((try r.to(u64)) == 3); | |
| 1202 | try testing.expect((try q.toInt(u64)) == op1 / op2); | |
| 1203 | try testing.expect((try r.toInt(u64)) == 3); | |
| 1204 | 1204 | } |
| 1205 | 1205 | |
| 1206 | 1206 | test "div multi>2-single" { |
| ... | ... | @@ -1219,8 +1219,8 @@ test "div multi>2-single" { |
| 1219 | 1219 | defer r.deinit(); |
| 1220 | 1220 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1221 | 1221 | |
| 1222 | try testing.expect((try q.to(u128)) == op1 / op2); | |
| 1223 | try testing.expect((try r.to(u32)) == 0x3e4e); | |
| 1222 | try testing.expect((try q.toInt(u128)) == op1 / op2); | |
| 1223 | try testing.expect((try r.toInt(u32)) == 0x3e4e); | |
| 1224 | 1224 | } |
| 1225 | 1225 | |
| 1226 | 1226 | test "div single-single q < r" { |
| ... | ... | @@ -1235,8 +1235,8 @@ test "div single-single q < r" { |
| 1235 | 1235 | defer r.deinit(); |
| 1236 | 1236 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1237 | 1237 | |
| 1238 | try testing.expect((try q.to(u64)) == 0); | |
| 1239 | try testing.expect((try r.to(u64)) == 0x0078f432); | |
| 1238 | try testing.expect((try q.toInt(u64)) == 0); | |
| 1239 | try testing.expect((try r.toInt(u64)) == 0x0078f432); | |
| 1240 | 1240 | } |
| 1241 | 1241 | |
| 1242 | 1242 | test "div single-single q == r" { |
| ... | ... | @@ -1251,8 +1251,8 @@ test "div single-single q == r" { |
| 1251 | 1251 | defer r.deinit(); |
| 1252 | 1252 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1253 | 1253 | |
| 1254 | try testing.expect((try q.to(u64)) == 1); | |
| 1255 | try testing.expect((try r.to(u64)) == 0); | |
| 1254 | try testing.expect((try q.toInt(u64)) == 1); | |
| 1255 | try testing.expect((try r.toInt(u64)) == 0); | |
| 1256 | 1256 | } |
| 1257 | 1257 | |
| 1258 | 1258 | test "div q=0 alias" { |
| ... | ... | @@ -1263,8 +1263,8 @@ test "div q=0 alias" { |
| 1263 | 1263 | |
| 1264 | 1264 | try Managed.divTrunc(&a, &b, &a, &b); |
| 1265 | 1265 | |
| 1266 | try testing.expect((try a.to(u64)) == 0); | |
| 1267 | try testing.expect((try b.to(u64)) == 3); | |
| 1266 | try testing.expect((try a.toInt(u64)) == 0); | |
| 1267 | try testing.expect((try b.toInt(u64)) == 3); | |
| 1268 | 1268 | } |
| 1269 | 1269 | |
| 1270 | 1270 | test "div multi-multi q < r" { |
| ... | ... | @@ -1283,8 +1283,8 @@ test "div multi-multi q < r" { |
| 1283 | 1283 | defer r.deinit(); |
| 1284 | 1284 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1285 | 1285 | |
| 1286 | try testing.expect((try q.to(u128)) == 0); | |
| 1287 | try testing.expect((try r.to(u128)) == op1); | |
| 1286 | try testing.expect((try q.toInt(u128)) == 0); | |
| 1287 | try testing.expect((try r.toInt(u128)) == op1); | |
| 1288 | 1288 | } |
| 1289 | 1289 | |
| 1290 | 1290 | test "div trunc single-single +/+" { |
| ... | ... | @@ -1307,8 +1307,8 @@ test "div trunc single-single +/+" { |
| 1307 | 1307 | const eq = @divTrunc(u, v); |
| 1308 | 1308 | const er = @mod(u, v); |
| 1309 | 1309 | |
| 1310 | try testing.expect((try q.to(i32)) == eq); | |
| 1311 | try testing.expect((try r.to(i32)) == er); | |
| 1310 | try testing.expect((try q.toInt(i32)) == eq); | |
| 1311 | try testing.expect((try r.toInt(i32)) == er); | |
| 1312 | 1312 | } |
| 1313 | 1313 | |
| 1314 | 1314 | test "div trunc single-single -/+" { |
| ... | ... | @@ -1331,8 +1331,8 @@ test "div trunc single-single -/+" { |
| 1331 | 1331 | const eq = -1; |
| 1332 | 1332 | const er = -2; |
| 1333 | 1333 | |
| 1334 | try testing.expect((try q.to(i32)) == eq); | |
| 1335 | try testing.expect((try r.to(i32)) == er); | |
| 1334 | try testing.expect((try q.toInt(i32)) == eq); | |
| 1335 | try testing.expect((try r.toInt(i32)) == er); | |
| 1336 | 1336 | } |
| 1337 | 1337 | |
| 1338 | 1338 | test "div trunc single-single +/-" { |
| ... | ... | @@ -1355,8 +1355,8 @@ test "div trunc single-single +/-" { |
| 1355 | 1355 | const eq = -1; |
| 1356 | 1356 | const er = 2; |
| 1357 | 1357 | |
| 1358 | try testing.expect((try q.to(i32)) == eq); | |
| 1359 | try testing.expect((try r.to(i32)) == er); | |
| 1358 | try testing.expect((try q.toInt(i32)) == eq); | |
| 1359 | try testing.expect((try r.toInt(i32)) == er); | |
| 1360 | 1360 | } |
| 1361 | 1361 | |
| 1362 | 1362 | test "div trunc single-single -/-" { |
| ... | ... | @@ -1379,8 +1379,8 @@ test "div trunc single-single -/-" { |
| 1379 | 1379 | const eq = 1; |
| 1380 | 1380 | const er = -2; |
| 1381 | 1381 | |
| 1382 | try testing.expect((try q.to(i32)) == eq); | |
| 1383 | try testing.expect((try r.to(i32)) == er); | |
| 1382 | try testing.expect((try q.toInt(i32)) == eq); | |
| 1383 | try testing.expect((try r.toInt(i32)) == er); | |
| 1384 | 1384 | } |
| 1385 | 1385 | |
| 1386 | 1386 | test "divTrunc #15535" { |
| ... | ... | @@ -1417,7 +1417,7 @@ test "divFloor #10932" { |
| 1417 | 1417 | const ress = try res.toString(testing.allocator, 16, .lower); |
| 1418 | 1418 | defer testing.allocator.free(ress); |
| 1419 | 1419 | try testing.expect(std.mem.eql(u8, ress, "194bd136316c046d070b763396297bf8869a605030216b52597015902a172b2a752f62af1568dcd431602f03725bfa62b0be71ae86616210972c0126e173503011ca48c5747ff066d159c95e46b69cbb14c8fc0bd2bf0919f921be96463200000000000000000000000000000000000000000000000000000000000000000000000000000000")); |
| 1420 | try testing.expect((try mod.to(i32)) == 0); | |
| 1420 | try testing.expect((try mod.toInt(i32)) == 0); | |
| 1421 | 1421 | } |
| 1422 | 1422 | |
| 1423 | 1423 | test "divFloor #11166" { |
| ... | ... | @@ -1482,7 +1482,7 @@ test "bitAnd #10932" { |
| 1482 | 1482 | |
| 1483 | 1483 | try res.bitAnd(&a, &b); |
| 1484 | 1484 | |
| 1485 | try testing.expect((try res.to(i32)) == 0); | |
| 1485 | try testing.expect((try res.toInt(i32)) == 0); | |
| 1486 | 1486 | } |
| 1487 | 1487 | |
| 1488 | 1488 | test "bit And #19235" { |
| ... | ... | @@ -1495,7 +1495,7 @@ test "bit And #19235" { |
| 1495 | 1495 | |
| 1496 | 1496 | try r.bitAnd(&a, &b); |
| 1497 | 1497 | |
| 1498 | try testing.expect((try r.to(i128)) == 0x10000000000000000); | |
| 1498 | try testing.expect((try r.toInt(i128)) == 0x10000000000000000); | |
| 1499 | 1499 | } |
| 1500 | 1500 | |
| 1501 | 1501 | test "div floor single-single +/+" { |
| ... | ... | @@ -1518,8 +1518,8 @@ test "div floor single-single +/+" { |
| 1518 | 1518 | const eq = 1; |
| 1519 | 1519 | const er = 2; |
| 1520 | 1520 | |
| 1521 | try testing.expect((try q.to(i32)) == eq); | |
| 1522 | try testing.expect((try r.to(i32)) == er); | |
| 1521 | try testing.expect((try q.toInt(i32)) == eq); | |
| 1522 | try testing.expect((try r.toInt(i32)) == er); | |
| 1523 | 1523 | } |
| 1524 | 1524 | |
| 1525 | 1525 | test "div floor single-single -/+" { |
| ... | ... | @@ -1542,8 +1542,8 @@ test "div floor single-single -/+" { |
| 1542 | 1542 | const eq = -2; |
| 1543 | 1543 | const er = 1; |
| 1544 | 1544 | |
| 1545 | try testing.expect((try q.to(i32)) == eq); | |
| 1546 | try testing.expect((try r.to(i32)) == er); | |
| 1545 | try testing.expect((try q.toInt(i32)) == eq); | |
| 1546 | try testing.expect((try r.toInt(i32)) == er); | |
| 1547 | 1547 | } |
| 1548 | 1548 | |
| 1549 | 1549 | test "div floor single-single +/-" { |
| ... | ... | @@ -1566,8 +1566,8 @@ test "div floor single-single +/-" { |
| 1566 | 1566 | const eq = -2; |
| 1567 | 1567 | const er = -1; |
| 1568 | 1568 | |
| 1569 | try testing.expect((try q.to(i32)) == eq); | |
| 1570 | try testing.expect((try r.to(i32)) == er); | |
| 1569 | try testing.expect((try q.toInt(i32)) == eq); | |
| 1570 | try testing.expect((try r.toInt(i32)) == er); | |
| 1571 | 1571 | } |
| 1572 | 1572 | |
| 1573 | 1573 | test "div floor single-single -/-" { |
| ... | ... | @@ -1590,8 +1590,8 @@ test "div floor single-single -/-" { |
| 1590 | 1590 | const eq = 1; |
| 1591 | 1591 | const er = -2; |
| 1592 | 1592 | |
| 1593 | try testing.expect((try q.to(i32)) == eq); | |
| 1594 | try testing.expect((try r.to(i32)) == er); | |
| 1593 | try testing.expect((try q.toInt(i32)) == eq); | |
| 1594 | try testing.expect((try r.toInt(i32)) == er); | |
| 1595 | 1595 | } |
| 1596 | 1596 | |
| 1597 | 1597 | test "div floor no remainder negative quotient" { |
| ... | ... | @@ -1609,8 +1609,8 @@ test "div floor no remainder negative quotient" { |
| 1609 | 1609 | defer r.deinit(); |
| 1610 | 1610 | try Managed.divFloor(&q, &r, &a, &b); |
| 1611 | 1611 | |
| 1612 | try testing.expect((try q.to(i32)) == -0x80000000); | |
| 1613 | try testing.expect((try r.to(i32)) == 0); | |
| 1612 | try testing.expect((try q.toInt(i32)) == -0x80000000); | |
| 1613 | try testing.expect((try r.toInt(i32)) == 0); | |
| 1614 | 1614 | } |
| 1615 | 1615 | |
| 1616 | 1616 | test "div floor negative close to zero" { |
| ... | ... | @@ -1628,8 +1628,8 @@ test "div floor negative close to zero" { |
| 1628 | 1628 | defer r.deinit(); |
| 1629 | 1629 | try Managed.divFloor(&q, &r, &a, &b); |
| 1630 | 1630 | |
| 1631 | try testing.expect((try q.to(i32)) == -1); | |
| 1632 | try testing.expect((try r.to(i32)) == 10); | |
| 1631 | try testing.expect((try q.toInt(i32)) == -1); | |
| 1632 | try testing.expect((try r.toInt(i32)) == 10); | |
| 1633 | 1633 | } |
| 1634 | 1634 | |
| 1635 | 1635 | test "div floor positive close to zero" { |
| ... | ... | @@ -1647,8 +1647,8 @@ test "div floor positive close to zero" { |
| 1647 | 1647 | defer r.deinit(); |
| 1648 | 1648 | try Managed.divFloor(&q, &r, &a, &b); |
| 1649 | 1649 | |
| 1650 | try testing.expect((try q.to(i32)) == 0); | |
| 1651 | try testing.expect((try r.to(i32)) == 10); | |
| 1650 | try testing.expect((try q.toInt(i32)) == 0); | |
| 1651 | try testing.expect((try r.toInt(i32)) == 10); | |
| 1652 | 1652 | } |
| 1653 | 1653 | |
| 1654 | 1654 | test "div multi-multi with rem" { |
| ... | ... | @@ -1665,8 +1665,8 @@ test "div multi-multi with rem" { |
| 1665 | 1665 | defer r.deinit(); |
| 1666 | 1666 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1667 | 1667 | |
| 1668 | try testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); | |
| 1669 | try testing.expect((try r.to(u128)) == 0x28de0acacd806823638); | |
| 1668 | try testing.expect((try q.toInt(u128)) == 0xe38f38e39161aaabd03f0f1b); | |
| 1669 | try testing.expect((try r.toInt(u128)) == 0x28de0acacd806823638); | |
| 1670 | 1670 | } |
| 1671 | 1671 | |
| 1672 | 1672 | test "div multi-multi no rem" { |
| ... | ... | @@ -1683,8 +1683,8 @@ test "div multi-multi no rem" { |
| 1683 | 1683 | defer r.deinit(); |
| 1684 | 1684 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1685 | 1685 | |
| 1686 | try testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); | |
| 1687 | try testing.expect((try r.to(u128)) == 0); | |
| 1686 | try testing.expect((try q.toInt(u128)) == 0xe38f38e39161aaabd03f0f1b); | |
| 1687 | try testing.expect((try r.toInt(u128)) == 0); | |
| 1688 | 1688 | } |
| 1689 | 1689 | |
| 1690 | 1690 | test "div multi-multi (2 branch)" { |
| ... | ... | @@ -1701,8 +1701,8 @@ test "div multi-multi (2 branch)" { |
| 1701 | 1701 | defer r.deinit(); |
| 1702 | 1702 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1703 | 1703 | |
| 1704 | try testing.expect((try q.to(u128)) == 0x10000000000000000); | |
| 1705 | try testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111); | |
| 1704 | try testing.expect((try q.toInt(u128)) == 0x10000000000000000); | |
| 1705 | try testing.expect((try r.toInt(u128)) == 0x44444443444444431111111111111111); | |
| 1706 | 1706 | } |
| 1707 | 1707 | |
| 1708 | 1708 | test "div multi-multi (3.1/3.3 branch)" { |
| ... | ... | @@ -1719,8 +1719,8 @@ test "div multi-multi (3.1/3.3 branch)" { |
| 1719 | 1719 | defer r.deinit(); |
| 1720 | 1720 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1721 | 1721 | |
| 1722 | try testing.expect((try q.to(u128)) == 0xfffffffffffffffffff); | |
| 1723 | try testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282); | |
| 1722 | try testing.expect((try q.toInt(u128)) == 0xfffffffffffffffffff); | |
| 1723 | try testing.expect((try r.toInt(u256)) == 0x1111111111111111111110b12222222222222222282); | |
| 1724 | 1724 | } |
| 1725 | 1725 | |
| 1726 | 1726 | test "div multi-single zero-limb trailing" { |
| ... | ... | @@ -1757,7 +1757,7 @@ test "div multi-multi zero-limb trailing (with rem)" { |
| 1757 | 1757 | defer r.deinit(); |
| 1758 | 1758 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1759 | 1759 | |
| 1760 | try testing.expect((try q.to(u128)) == 0x10000000000000000); | |
| 1760 | try testing.expect((try q.toInt(u128)) == 0x10000000000000000); | |
| 1761 | 1761 | |
| 1762 | 1762 | const rs = try r.toString(testing.allocator, 16, .lower); |
| 1763 | 1763 | defer testing.allocator.free(rs); |
| ... | ... | @@ -1778,7 +1778,7 @@ test "div multi-multi zero-limb trailing (with rem) and dividend zero-limb count |
| 1778 | 1778 | defer r.deinit(); |
| 1779 | 1779 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1780 | 1780 | |
| 1781 | try testing.expect((try q.to(u128)) == 0x1); | |
| 1781 | try testing.expect((try q.toInt(u128)) == 0x1); | |
| 1782 | 1782 | |
| 1783 | 1783 | const rs = try r.toString(testing.allocator, 16, .lower); |
| 1784 | 1784 | defer testing.allocator.free(rs); |
| ... | ... | @@ -1862,7 +1862,7 @@ test "truncate single unsigned" { |
| 1862 | 1862 | |
| 1863 | 1863 | try a.truncate(&a, .unsigned, 17); |
| 1864 | 1864 | |
| 1865 | try testing.expect((try a.to(u17)) == maxInt(u17)); | |
| 1865 | try testing.expect((try a.toInt(u17)) == maxInt(u17)); | |
| 1866 | 1866 | } |
| 1867 | 1867 | |
| 1868 | 1868 | test "truncate single signed" { |
| ... | ... | @@ -1871,7 +1871,7 @@ test "truncate single signed" { |
| 1871 | 1871 | |
| 1872 | 1872 | try a.truncate(&a, .signed, 17); |
| 1873 | 1873 | |
| 1874 | try testing.expect((try a.to(i17)) == minInt(i17)); | |
| 1874 | try testing.expect((try a.toInt(i17)) == minInt(i17)); | |
| 1875 | 1875 | } |
| 1876 | 1876 | |
| 1877 | 1877 | test "truncate multi to single unsigned" { |
| ... | ... | @@ -1880,7 +1880,7 @@ test "truncate multi to single unsigned" { |
| 1880 | 1880 | |
| 1881 | 1881 | try a.truncate(&a, .unsigned, 27); |
| 1882 | 1882 | |
| 1883 | try testing.expect((try a.to(u27)) == 0x2BC_DEF0); | |
| 1883 | try testing.expect((try a.toInt(u27)) == 0x2BC_DEF0); | |
| 1884 | 1884 | } |
| 1885 | 1885 | |
| 1886 | 1886 | test "truncate multi to single signed" { |
| ... | ... | @@ -1889,7 +1889,7 @@ test "truncate multi to single signed" { |
| 1889 | 1889 | |
| 1890 | 1890 | try a.truncate(&a, .signed, @bitSizeOf(i11)); |
| 1891 | 1891 | |
| 1892 | try testing.expect((try a.to(i11)) == minInt(i11)); | |
| 1892 | try testing.expect((try a.toInt(i11)) == minInt(i11)); | |
| 1893 | 1893 | } |
| 1894 | 1894 | |
| 1895 | 1895 | test "truncate multi to multi unsigned" { |
| ... | ... | @@ -1901,7 +1901,7 @@ test "truncate multi to multi unsigned" { |
| 1901 | 1901 | |
| 1902 | 1902 | try a.truncate(&a, .unsigned, bits - 1); |
| 1903 | 1903 | |
| 1904 | try testing.expect((try a.to(Int)) == maxInt(Int)); | |
| 1904 | try testing.expect((try a.toInt(Int)) == maxInt(Int)); | |
| 1905 | 1905 | } |
| 1906 | 1906 | |
| 1907 | 1907 | test "truncate multi to multi signed" { |
| ... | ... | @@ -1910,7 +1910,7 @@ test "truncate multi to multi signed" { |
| 1910 | 1910 | |
| 1911 | 1911 | try a.truncate(&a, .signed, @bitSizeOf(Limb) + 1); |
| 1912 | 1912 | |
| 1913 | try testing.expect((try a.to(std.meta.Int(.signed, @bitSizeOf(Limb) + 1))) == -1 << @bitSizeOf(Limb)); | |
| 1913 | try testing.expect((try a.toInt(std.meta.Int(.signed, @bitSizeOf(Limb) + 1))) == -1 << @bitSizeOf(Limb)); | |
| 1914 | 1914 | } |
| 1915 | 1915 | |
| 1916 | 1916 | test "truncate negative multi to single" { |
| ... | ... | @@ -1919,7 +1919,7 @@ test "truncate negative multi to single" { |
| 1919 | 1919 | |
| 1920 | 1920 | try a.truncate(&a, .signed, @bitSizeOf(i17)); |
| 1921 | 1921 | |
| 1922 | try testing.expect((try a.to(i17)) == 0); | |
| 1922 | try testing.expect((try a.toInt(i17)) == 0); | |
| 1923 | 1923 | } |
| 1924 | 1924 | |
| 1925 | 1925 | test "truncate multi unsigned many" { |
| ... | ... | @@ -1931,7 +1931,7 @@ test "truncate multi unsigned many" { |
| 1931 | 1931 | defer b.deinit(); |
| 1932 | 1932 | try b.truncate(&a, .signed, @bitSizeOf(i1)); |
| 1933 | 1933 | |
| 1934 | try testing.expect((try b.to(i1)) == 0); | |
| 1934 | try testing.expect((try b.toInt(i1)) == 0); | |
| 1935 | 1935 | } |
| 1936 | 1936 | |
| 1937 | 1937 | test "saturate single signed positive" { |
| ... | ... | @@ -1940,7 +1940,7 @@ test "saturate single signed positive" { |
| 1940 | 1940 | |
| 1941 | 1941 | try a.saturate(&a, .signed, 17); |
| 1942 | 1942 | |
| 1943 | try testing.expect((try a.to(i17)) == maxInt(i17)); | |
| 1943 | try testing.expect((try a.toInt(i17)) == maxInt(i17)); | |
| 1944 | 1944 | } |
| 1945 | 1945 | |
| 1946 | 1946 | test "saturate single signed negative" { |
| ... | ... | @@ -1949,7 +1949,7 @@ test "saturate single signed negative" { |
| 1949 | 1949 | |
| 1950 | 1950 | try a.saturate(&a, .signed, 17); |
| 1951 | 1951 | |
| 1952 | try testing.expect((try a.to(i17)) == minInt(i17)); | |
| 1952 | try testing.expect((try a.toInt(i17)) == minInt(i17)); | |
| 1953 | 1953 | } |
| 1954 | 1954 | |
| 1955 | 1955 | test "saturate single signed" { |
| ... | ... | @@ -1958,7 +1958,7 @@ test "saturate single signed" { |
| 1958 | 1958 | |
| 1959 | 1959 | try a.saturate(&a, .signed, 17); |
| 1960 | 1960 | |
| 1961 | try testing.expect((try a.to(i17)) == maxInt(i17) - 1); | |
| 1961 | try testing.expect((try a.toInt(i17)) == maxInt(i17) - 1); | |
| 1962 | 1962 | } |
| 1963 | 1963 | |
| 1964 | 1964 | test "saturate multi signed" { |
| ... | ... | @@ -1967,7 +1967,7 @@ test "saturate multi signed" { |
| 1967 | 1967 | |
| 1968 | 1968 | try a.saturate(&a, .signed, @bitSizeOf(SignedDoubleLimb)); |
| 1969 | 1969 | |
| 1970 | try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb)); | |
| 1970 | try testing.expect((try a.toInt(SignedDoubleLimb)) == maxInt(SignedDoubleLimb)); | |
| 1971 | 1971 | } |
| 1972 | 1972 | |
| 1973 | 1973 | test "saturate single unsigned" { |
| ... | ... | @@ -1976,7 +1976,7 @@ test "saturate single unsigned" { |
| 1976 | 1976 | |
| 1977 | 1977 | try a.saturate(&a, .unsigned, 23); |
| 1978 | 1978 | |
| 1979 | try testing.expect((try a.to(u23)) == maxInt(u23)); | |
| 1979 | try testing.expect((try a.toInt(u23)) == maxInt(u23)); | |
| 1980 | 1980 | } |
| 1981 | 1981 | |
| 1982 | 1982 | test "saturate multi unsigned zero" { |
| ... | ... | @@ -1994,7 +1994,7 @@ test "saturate multi unsigned" { |
| 1994 | 1994 | |
| 1995 | 1995 | try a.saturate(&a, .unsigned, @bitSizeOf(DoubleLimb)); |
| 1996 | 1996 | |
| 1997 | try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb)); | |
| 1997 | try testing.expect((try a.toInt(DoubleLimb)) == maxInt(DoubleLimb)); | |
| 1998 | 1998 | } |
| 1999 | 1999 | |
| 2000 | 2000 | test "shift-right single" { |
| ... | ... | @@ -2002,7 +2002,7 @@ test "shift-right single" { |
| 2002 | 2002 | defer a.deinit(); |
| 2003 | 2003 | try a.shiftRight(&a, 16); |
| 2004 | 2004 | |
| 2005 | try testing.expect((try a.to(u32)) == 0xffff); | |
| 2005 | try testing.expect((try a.toInt(u32)) == 0xffff); | |
| 2006 | 2006 | } |
| 2007 | 2007 | |
| 2008 | 2008 | test "shift-right multi" { |
| ... | ... | @@ -2010,7 +2010,7 @@ test "shift-right multi" { |
| 2010 | 2010 | defer a.deinit(); |
| 2011 | 2011 | try a.shiftRight(&a, 67); |
| 2012 | 2012 | |
| 2013 | try testing.expect((try a.to(u64)) == 0x1fffe0001dddc222); | |
| 2013 | try testing.expect((try a.toInt(u64)) == 0x1fffe0001dddc222); | |
| 2014 | 2014 | |
| 2015 | 2015 | try a.set(0xffff0000eeee1111dddd2222cccc3333); |
| 2016 | 2016 | try a.shiftRight(&a, 63); |
| ... | ... | @@ -2037,7 +2037,7 @@ test "shift-left single" { |
| 2037 | 2037 | defer a.deinit(); |
| 2038 | 2038 | try a.shiftLeft(&a, 16); |
| 2039 | 2039 | |
| 2040 | try testing.expect((try a.to(u64)) == 0xffff0000); | |
| 2040 | try testing.expect((try a.toInt(u64)) == 0xffff0000); | |
| 2041 | 2041 | } |
| 2042 | 2042 | |
| 2043 | 2043 | test "shift-left multi" { |
| ... | ... | @@ -2045,7 +2045,7 @@ test "shift-left multi" { |
| 2045 | 2045 | defer a.deinit(); |
| 2046 | 2046 | try a.shiftLeft(&a, 67); |
| 2047 | 2047 | |
| 2048 | try testing.expect((try a.to(u128)) == 0xffff0000eeee11100000000000000000); | |
| 2048 | try testing.expect((try a.toInt(u128)) == 0xffff0000eeee11100000000000000000); | |
| 2049 | 2049 | } |
| 2050 | 2050 | |
| 2051 | 2051 | test "shift-right negative" { |
| ... | ... | @@ -2055,43 +2055,43 @@ test "shift-right negative" { |
| 2055 | 2055 | var arg = try Managed.initSet(testing.allocator, -20); |
| 2056 | 2056 | defer arg.deinit(); |
| 2057 | 2057 | try a.shiftRight(&arg, 2); |
| 2058 | try testing.expect((try a.to(i32)) == -5); // -20 >> 2 == -5 | |
| 2058 | try testing.expect((try a.toInt(i32)) == -5); // -20 >> 2 == -5 | |
| 2059 | 2059 | |
| 2060 | 2060 | var arg2 = try Managed.initSet(testing.allocator, -5); |
| 2061 | 2061 | defer arg2.deinit(); |
| 2062 | 2062 | try a.shiftRight(&arg2, 10); |
| 2063 | try testing.expect((try a.to(i32)) == -1); // -5 >> 10 == -1 | |
| 2063 | try testing.expect((try a.toInt(i32)) == -1); // -5 >> 10 == -1 | |
| 2064 | 2064 | |
| 2065 | 2065 | var arg3 = try Managed.initSet(testing.allocator, -10); |
| 2066 | 2066 | defer arg3.deinit(); |
| 2067 | 2067 | try a.shiftRight(&arg3, 1232); |
| 2068 | try testing.expect((try a.to(i32)) == -1); // -10 >> 1232 == -1 | |
| 2068 | try testing.expect((try a.toInt(i32)) == -1); // -10 >> 1232 == -1 | |
| 2069 | 2069 | |
| 2070 | 2070 | var arg4 = try Managed.initSet(testing.allocator, -5); |
| 2071 | 2071 | defer arg4.deinit(); |
| 2072 | 2072 | try a.shiftRight(&arg4, 2); |
| 2073 | try testing.expect(try a.to(i32) == -2); // -5 >> 2 == -2 | |
| 2073 | try testing.expect(try a.toInt(i32) == -2); // -5 >> 2 == -2 | |
| 2074 | 2074 | |
| 2075 | 2075 | var arg5 = try Managed.initSet(testing.allocator, -0xffff0000eeee1111dddd2222cccc3333); |
| 2076 | 2076 | defer arg5.deinit(); |
| 2077 | 2077 | try a.shiftRight(&arg5, 67); |
| 2078 | try testing.expect(try a.to(i64) == -0x1fffe0001dddc223); | |
| 2078 | try testing.expect(try a.toInt(i64) == -0x1fffe0001dddc223); | |
| 2079 | 2079 | |
| 2080 | 2080 | var arg6 = try Managed.initSet(testing.allocator, -0x1ffffffffffffffff); |
| 2081 | 2081 | defer arg6.deinit(); |
| 2082 | 2082 | try a.shiftRight(&arg6, 1); |
| 2083 | 2083 | try a.shiftRight(&a, 1); |
| 2084 | 2084 | a.setSign(true); |
| 2085 | try testing.expect(try a.to(u64) == 0x8000000000000000); | |
| 2085 | try testing.expect(try a.toInt(u64) == 0x8000000000000000); | |
| 2086 | 2086 | |
| 2087 | 2087 | var arg7 = try Managed.initSet(testing.allocator, -32767); |
| 2088 | 2088 | defer arg7.deinit(); |
| 2089 | 2089 | a.setSign(false); |
| 2090 | 2090 | try a.shiftRight(&arg7, 4); |
| 2091 | try testing.expect(try a.to(i16) == -2048); | |
| 2091 | try testing.expect(try a.toInt(i16) == -2048); | |
| 2092 | 2092 | a.setSign(true); |
| 2093 | 2093 | try a.shiftRight(&arg7, 4); |
| 2094 | try testing.expect(try a.to(i16) == -2048); | |
| 2094 | try testing.expect(try a.toInt(i16) == -2048); | |
| 2095 | 2095 | } |
| 2096 | 2096 | |
| 2097 | 2097 | test "sat shift-left simple unsigned" { |
| ... | ... | @@ -2099,7 +2099,7 @@ test "sat shift-left simple unsigned" { |
| 2099 | 2099 | defer a.deinit(); |
| 2100 | 2100 | try a.shiftLeftSat(&a, 16, .unsigned, 21); |
| 2101 | 2101 | |
| 2102 | try testing.expect((try a.to(u64)) == 0x1fffff); | |
| 2102 | try testing.expect((try a.toInt(u64)) == 0x1fffff); | |
| 2103 | 2103 | } |
| 2104 | 2104 | |
| 2105 | 2105 | test "sat shift-left simple unsigned no sat" { |
| ... | ... | @@ -2107,7 +2107,7 @@ test "sat shift-left simple unsigned no sat" { |
| 2107 | 2107 | defer a.deinit(); |
| 2108 | 2108 | try a.shiftLeftSat(&a, 16, .unsigned, 21); |
| 2109 | 2109 | |
| 2110 | try testing.expect((try a.to(u64)) == 0x10000); | |
| 2110 | try testing.expect((try a.toInt(u64)) == 0x10000); | |
| 2111 | 2111 | } |
| 2112 | 2112 | |
| 2113 | 2113 | test "sat shift-left multi unsigned" { |
| ... | ... | @@ -2115,7 +2115,7 @@ test "sat shift-left multi unsigned" { |
| 2115 | 2115 | defer a.deinit(); |
| 2116 | 2116 | try a.shiftLeftSat(&a, @bitSizeOf(DoubleLimb) - 3, .unsigned, @bitSizeOf(DoubleLimb) - 1); |
| 2117 | 2117 | |
| 2118 | try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) >> 1); | |
| 2118 | try testing.expect((try a.toInt(DoubleLimb)) == maxInt(DoubleLimb) >> 1); | |
| 2119 | 2119 | } |
| 2120 | 2120 | |
| 2121 | 2121 | test "sat shift-left unsigned shift > bitcount" { |
| ... | ... | @@ -2123,7 +2123,7 @@ test "sat shift-left unsigned shift > bitcount" { |
| 2123 | 2123 | defer a.deinit(); |
| 2124 | 2124 | try a.shiftLeftSat(&a, 10, .unsigned, 10); |
| 2125 | 2125 | |
| 2126 | try testing.expect((try a.to(u10)) == maxInt(u10)); | |
| 2126 | try testing.expect((try a.toInt(u10)) == maxInt(u10)); | |
| 2127 | 2127 | } |
| 2128 | 2128 | |
| 2129 | 2129 | test "sat shift-left unsigned zero" { |
| ... | ... | @@ -2131,7 +2131,7 @@ test "sat shift-left unsigned zero" { |
| 2131 | 2131 | defer a.deinit(); |
| 2132 | 2132 | try a.shiftLeftSat(&a, 1, .unsigned, 0); |
| 2133 | 2133 | |
| 2134 | try testing.expect((try a.to(u64)) == 0); | |
| 2134 | try testing.expect((try a.toInt(u64)) == 0); | |
| 2135 | 2135 | } |
| 2136 | 2136 | |
| 2137 | 2137 | test "sat shift-left unsigned negative" { |
| ... | ... | @@ -2139,7 +2139,7 @@ test "sat shift-left unsigned negative" { |
| 2139 | 2139 | defer a.deinit(); |
| 2140 | 2140 | try a.shiftLeftSat(&a, 0, .unsigned, 0); |
| 2141 | 2141 | |
| 2142 | try testing.expect((try a.to(u64)) == 0); | |
| 2142 | try testing.expect((try a.toInt(u64)) == 0); | |
| 2143 | 2143 | } |
| 2144 | 2144 | |
| 2145 | 2145 | test "sat shift-left signed simple negative" { |
| ... | ... | @@ -2147,7 +2147,7 @@ test "sat shift-left signed simple negative" { |
| 2147 | 2147 | defer a.deinit(); |
| 2148 | 2148 | try a.shiftLeftSat(&a, 3, .signed, 10); |
| 2149 | 2149 | |
| 2150 | try testing.expect((try a.to(i10)) == minInt(i10)); | |
| 2150 | try testing.expect((try a.toInt(i10)) == minInt(i10)); | |
| 2151 | 2151 | } |
| 2152 | 2152 | |
| 2153 | 2153 | test "sat shift-left signed simple positive" { |
| ... | ... | @@ -2155,7 +2155,7 @@ test "sat shift-left signed simple positive" { |
| 2155 | 2155 | defer a.deinit(); |
| 2156 | 2156 | try a.shiftLeftSat(&a, 3, .signed, 10); |
| 2157 | 2157 | |
| 2158 | try testing.expect((try a.to(i10)) == maxInt(i10)); | |
| 2158 | try testing.expect((try a.toInt(i10)) == maxInt(i10)); | |
| 2159 | 2159 | } |
| 2160 | 2160 | |
| 2161 | 2161 | test "sat shift-left signed multi positive" { |
| ... | ... | @@ -2170,7 +2170,7 @@ test "sat shift-left signed multi positive" { |
| 2170 | 2170 | defer a.deinit(); |
| 2171 | 2171 | try a.shiftLeftSat(&a, shift, .signed, @bitSizeOf(SignedDoubleLimb)); |
| 2172 | 2172 | |
| 2173 | try testing.expect((try a.to(SignedDoubleLimb)) == x <<| shift); | |
| 2173 | try testing.expect((try a.toInt(SignedDoubleLimb)) == x <<| shift); | |
| 2174 | 2174 | } |
| 2175 | 2175 | |
| 2176 | 2176 | test "sat shift-left signed multi negative" { |
| ... | ... | @@ -2185,7 +2185,7 @@ test "sat shift-left signed multi negative" { |
| 2185 | 2185 | defer a.deinit(); |
| 2186 | 2186 | try a.shiftLeftSat(&a, shift, .signed, @bitSizeOf(SignedDoubleLimb)); |
| 2187 | 2187 | |
| 2188 | try testing.expect((try a.to(SignedDoubleLimb)) == x <<| shift); | |
| 2188 | try testing.expect((try a.toInt(SignedDoubleLimb)) == x <<| shift); | |
| 2189 | 2189 | } |
| 2190 | 2190 | |
| 2191 | 2191 | test "bitNotWrap unsigned simple" { |
| ... | ... | @@ -2197,7 +2197,7 @@ test "bitNotWrap unsigned simple" { |
| 2197 | 2197 | |
| 2198 | 2198 | try a.bitNotWrap(&a, .unsigned, 10); |
| 2199 | 2199 | |
| 2200 | try testing.expect((try a.to(u10)) == ~x); | |
| 2200 | try testing.expect((try a.toInt(u10)) == ~x); | |
| 2201 | 2201 | } |
| 2202 | 2202 | |
| 2203 | 2203 | test "bitNotWrap unsigned multi" { |
| ... | ... | @@ -2206,7 +2206,7 @@ test "bitNotWrap unsigned multi" { |
| 2206 | 2206 | |
| 2207 | 2207 | try a.bitNotWrap(&a, .unsigned, @bitSizeOf(DoubleLimb)); |
| 2208 | 2208 | |
| 2209 | try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb)); | |
| 2209 | try testing.expect((try a.toInt(DoubleLimb)) == maxInt(DoubleLimb)); | |
| 2210 | 2210 | } |
| 2211 | 2211 | |
| 2212 | 2212 | test "bitNotWrap signed simple" { |
| ... | ... | @@ -2218,7 +2218,7 @@ test "bitNotWrap signed simple" { |
| 2218 | 2218 | |
| 2219 | 2219 | try a.bitNotWrap(&a, .signed, 11); |
| 2220 | 2220 | |
| 2221 | try testing.expect((try a.to(i11)) == ~x); | |
| 2221 | try testing.expect((try a.toInt(i11)) == ~x); | |
| 2222 | 2222 | } |
| 2223 | 2223 | |
| 2224 | 2224 | test "bitNotWrap signed multi" { |
| ... | ... | @@ -2227,7 +2227,7 @@ test "bitNotWrap signed multi" { |
| 2227 | 2227 | |
| 2228 | 2228 | try a.bitNotWrap(&a, .signed, @bitSizeOf(SignedDoubleLimb)); |
| 2229 | 2229 | |
| 2230 | try testing.expect((try a.to(SignedDoubleLimb)) == -1); | |
| 2230 | try testing.expect((try a.toInt(SignedDoubleLimb)) == -1); | |
| 2231 | 2231 | } |
| 2232 | 2232 | |
| 2233 | 2233 | test "bitNotWrap more than two limbs" { |
| ... | ... | @@ -2249,11 +2249,11 @@ test "bitNotWrap more than two limbs" { |
| 2249 | 2249 | |
| 2250 | 2250 | try res.bitNotWrap(&a, .unsigned, bits); |
| 2251 | 2251 | const Unsigned = @Type(.{ .int = .{ .signedness = .unsigned, .bits = bits } }); |
| 2252 | try testing.expectEqual((try res.to(Unsigned)), ~@as(Unsigned, maxInt(Limb))); | |
| 2252 | try testing.expectEqual((try res.toInt(Unsigned)), ~@as(Unsigned, maxInt(Limb))); | |
| 2253 | 2253 | |
| 2254 | 2254 | try res.bitNotWrap(&a, .signed, bits); |
| 2255 | 2255 | const Signed = @Type(.{ .int = .{ .signedness = .signed, .bits = bits } }); |
| 2256 | try testing.expectEqual((try res.to(Signed)), ~@as(Signed, maxInt(Limb))); | |
| 2256 | try testing.expectEqual((try res.toInt(Signed)), ~@as(Signed, maxInt(Limb))); | |
| 2257 | 2257 | } |
| 2258 | 2258 | |
| 2259 | 2259 | test "bitwise and simple" { |
| ... | ... | @@ -2264,7 +2264,7 @@ test "bitwise and simple" { |
| 2264 | 2264 | |
| 2265 | 2265 | try a.bitAnd(&a, &b); |
| 2266 | 2266 | |
| 2267 | try testing.expect((try a.to(u64)) == 0xeeeeeeee00000000); | |
| 2267 | try testing.expect((try a.toInt(u64)) == 0xeeeeeeee00000000); | |
| 2268 | 2268 | } |
| 2269 | 2269 | |
| 2270 | 2270 | test "bitwise and multi-limb" { |
| ... | ... | @@ -2275,7 +2275,7 @@ test "bitwise and multi-limb" { |
| 2275 | 2275 | |
| 2276 | 2276 | try a.bitAnd(&a, &b); |
| 2277 | 2277 | |
| 2278 | try testing.expect((try a.to(u128)) == 0); | |
| 2278 | try testing.expect((try a.toInt(u128)) == 0); | |
| 2279 | 2279 | } |
| 2280 | 2280 | |
| 2281 | 2281 | test "bitwise and negative-positive simple" { |
| ... | ... | @@ -2286,7 +2286,7 @@ test "bitwise and negative-positive simple" { |
| 2286 | 2286 | |
| 2287 | 2287 | try a.bitAnd(&a, &b); |
| 2288 | 2288 | |
| 2289 | try testing.expect((try a.to(u64)) == 0x22222222); | |
| 2289 | try testing.expect((try a.toInt(u64)) == 0x22222222); | |
| 2290 | 2290 | } |
| 2291 | 2291 | |
| 2292 | 2292 | test "bitwise and negative-positive multi-limb" { |
| ... | ... | @@ -2308,7 +2308,7 @@ test "bitwise and positive-negative simple" { |
| 2308 | 2308 | |
| 2309 | 2309 | try a.bitAnd(&a, &b); |
| 2310 | 2310 | |
| 2311 | try testing.expect((try a.to(u64)) == 0x1111111111111110); | |
| 2311 | try testing.expect((try a.toInt(u64)) == 0x1111111111111110); | |
| 2312 | 2312 | } |
| 2313 | 2313 | |
| 2314 | 2314 | test "bitwise and positive-negative multi-limb" { |
| ... | ... | @@ -2330,7 +2330,7 @@ test "bitwise and negative-negative simple" { |
| 2330 | 2330 | |
| 2331 | 2331 | try a.bitAnd(&a, &b); |
| 2332 | 2332 | |
| 2333 | try testing.expect((try a.to(i128)) == -0xffffffff33333332); | |
| 2333 | try testing.expect((try a.toInt(i128)) == -0xffffffff33333332); | |
| 2334 | 2334 | } |
| 2335 | 2335 | |
| 2336 | 2336 | test "bitwise and negative-negative multi-limb" { |
| ... | ... | @@ -2341,7 +2341,7 @@ test "bitwise and negative-negative multi-limb" { |
| 2341 | 2341 | |
| 2342 | 2342 | try a.bitAnd(&a, &b); |
| 2343 | 2343 | |
| 2344 | try testing.expect((try a.to(i128)) == -maxInt(Limb) * 2 - 2); | |
| 2344 | try testing.expect((try a.toInt(i128)) == -maxInt(Limb) * 2 - 2); | |
| 2345 | 2345 | } |
| 2346 | 2346 | |
| 2347 | 2347 | test "bitwise and negative overflow" { |
| ... | ... | @@ -2352,7 +2352,7 @@ test "bitwise and negative overflow" { |
| 2352 | 2352 | |
| 2353 | 2353 | try a.bitAnd(&a, &b); |
| 2354 | 2354 | |
| 2355 | try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb) - 1); | |
| 2355 | try testing.expect((try a.toInt(SignedDoubleLimb)) == -maxInt(Limb) - 1); | |
| 2356 | 2356 | } |
| 2357 | 2357 | |
| 2358 | 2358 | test "bitwise xor simple" { |
| ... | ... | @@ -2363,7 +2363,7 @@ test "bitwise xor simple" { |
| 2363 | 2363 | |
| 2364 | 2364 | try a.bitXor(&a, &b); |
| 2365 | 2365 | |
| 2366 | try testing.expect((try a.to(u64)) == 0x1111111133333333); | |
| 2366 | try testing.expect((try a.toInt(u64)) == 0x1111111133333333); | |
| 2367 | 2367 | } |
| 2368 | 2368 | |
| 2369 | 2369 | test "bitwise xor multi-limb" { |
| ... | ... | @@ -2378,7 +2378,7 @@ test "bitwise xor multi-limb" { |
| 2378 | 2378 | |
| 2379 | 2379 | try a.bitXor(&a, &b); |
| 2380 | 2380 | |
| 2381 | try testing.expect((try a.to(DoubleLimb)) == x ^ y); | |
| 2381 | try testing.expect((try a.toInt(DoubleLimb)) == x ^ y); | |
| 2382 | 2382 | } |
| 2383 | 2383 | |
| 2384 | 2384 | test "bitwise xor single negative simple" { |
| ... | ... | @@ -2389,7 +2389,7 @@ test "bitwise xor single negative simple" { |
| 2389 | 2389 | |
| 2390 | 2390 | try a.bitXor(&a, &b); |
| 2391 | 2391 | |
| 2392 | try testing.expect((try a.to(i64)) == -0x2efed94fcb932ef9); | |
| 2392 | try testing.expect((try a.toInt(i64)) == -0x2efed94fcb932ef9); | |
| 2393 | 2393 | } |
| 2394 | 2394 | |
| 2395 | 2395 | test "bitwise xor single negative multi-limb" { |
| ... | ... | @@ -2400,7 +2400,7 @@ test "bitwise xor single negative multi-limb" { |
| 2400 | 2400 | |
| 2401 | 2401 | try a.bitXor(&a, &b); |
| 2402 | 2402 | |
| 2403 | try testing.expect((try a.to(i128)) == -0x6a50889abd8834a24db1f19650d3999a); | |
| 2403 | try testing.expect((try a.toInt(i128)) == -0x6a50889abd8834a24db1f19650d3999a); | |
| 2404 | 2404 | } |
| 2405 | 2405 | |
| 2406 | 2406 | test "bitwise xor single negative overflow" { |
| ... | ... | @@ -2411,7 +2411,7 @@ test "bitwise xor single negative overflow" { |
| 2411 | 2411 | |
| 2412 | 2412 | try a.bitXor(&a, &b); |
| 2413 | 2413 | |
| 2414 | try testing.expect((try a.to(SignedDoubleLimb)) == -(maxInt(Limb) + 1)); | |
| 2414 | try testing.expect((try a.toInt(SignedDoubleLimb)) == -(maxInt(Limb) + 1)); | |
| 2415 | 2415 | } |
| 2416 | 2416 | |
| 2417 | 2417 | test "bitwise xor double negative simple" { |
| ... | ... | @@ -2422,7 +2422,7 @@ test "bitwise xor double negative simple" { |
| 2422 | 2422 | |
| 2423 | 2423 | try a.bitXor(&a, &b); |
| 2424 | 2424 | |
| 2425 | try testing.expect((try a.to(u64)) == 0xc39c47081a6eb759); | |
| 2425 | try testing.expect((try a.toInt(u64)) == 0xc39c47081a6eb759); | |
| 2426 | 2426 | } |
| 2427 | 2427 | |
| 2428 | 2428 | test "bitwise xor double negative multi-limb" { |
| ... | ... | @@ -2433,7 +2433,7 @@ test "bitwise xor double negative multi-limb" { |
| 2433 | 2433 | |
| 2434 | 2434 | try a.bitXor(&a, &b); |
| 2435 | 2435 | |
| 2436 | try testing.expect((try a.to(u128)) == 0xa3492ec28e62c410dff92bf0549bf771); | |
| 2436 | try testing.expect((try a.toInt(u128)) == 0xa3492ec28e62c410dff92bf0549bf771); | |
| 2437 | 2437 | } |
| 2438 | 2438 | |
| 2439 | 2439 | test "bitwise or simple" { |
| ... | ... | @@ -2444,7 +2444,7 @@ test "bitwise or simple" { |
| 2444 | 2444 | |
| 2445 | 2445 | try a.bitOr(&a, &b); |
| 2446 | 2446 | |
| 2447 | try testing.expect((try a.to(u64)) == 0xffffffff33333333); | |
| 2447 | try testing.expect((try a.toInt(u64)) == 0xffffffff33333333); | |
| 2448 | 2448 | } |
| 2449 | 2449 | |
| 2450 | 2450 | test "bitwise or multi-limb" { |
| ... | ... | @@ -2455,7 +2455,7 @@ test "bitwise or multi-limb" { |
| 2455 | 2455 | |
| 2456 | 2456 | try a.bitOr(&a, &b); |
| 2457 | 2457 | |
| 2458 | try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb)); | |
| 2458 | try testing.expect((try a.toInt(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb)); | |
| 2459 | 2459 | } |
| 2460 | 2460 | |
| 2461 | 2461 | test "bitwise or negative-positive simple" { |
| ... | ... | @@ -2466,7 +2466,7 @@ test "bitwise or negative-positive simple" { |
| 2466 | 2466 | |
| 2467 | 2467 | try a.bitOr(&a, &b); |
| 2468 | 2468 | |
| 2469 | try testing.expect((try a.to(i64)) == -0x1111111111111111); | |
| 2469 | try testing.expect((try a.toInt(i64)) == -0x1111111111111111); | |
| 2470 | 2470 | } |
| 2471 | 2471 | |
| 2472 | 2472 | test "bitwise or negative-positive multi-limb" { |
| ... | ... | @@ -2477,7 +2477,7 @@ test "bitwise or negative-positive multi-limb" { |
| 2477 | 2477 | |
| 2478 | 2478 | try a.bitOr(&a, &b); |
| 2479 | 2479 | |
| 2480 | try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb)); | |
| 2480 | try testing.expect((try a.toInt(SignedDoubleLimb)) == -maxInt(Limb)); | |
| 2481 | 2481 | } |
| 2482 | 2482 | |
| 2483 | 2483 | test "bitwise or positive-negative simple" { |
| ... | ... | @@ -2488,7 +2488,7 @@ test "bitwise or positive-negative simple" { |
| 2488 | 2488 | |
| 2489 | 2489 | try a.bitOr(&a, &b); |
| 2490 | 2490 | |
| 2491 | try testing.expect((try a.to(i64)) == -0x22222221); | |
| 2491 | try testing.expect((try a.toInt(i64)) == -0x22222221); | |
| 2492 | 2492 | } |
| 2493 | 2493 | |
| 2494 | 2494 | test "bitwise or positive-negative multi-limb" { |
| ... | ... | @@ -2499,7 +2499,7 @@ test "bitwise or positive-negative multi-limb" { |
| 2499 | 2499 | |
| 2500 | 2500 | try a.bitOr(&a, &b); |
| 2501 | 2501 | |
| 2502 | try testing.expect((try a.to(SignedDoubleLimb)) == -1); | |
| 2502 | try testing.expect((try a.toInt(SignedDoubleLimb)) == -1); | |
| 2503 | 2503 | } |
| 2504 | 2504 | |
| 2505 | 2505 | test "bitwise or negative-negative simple" { |
| ... | ... | @@ -2510,7 +2510,7 @@ test "bitwise or negative-negative simple" { |
| 2510 | 2510 | |
| 2511 | 2511 | try a.bitOr(&a, &b); |
| 2512 | 2512 | |
| 2513 | try testing.expect((try a.to(i128)) == -0xeeeeeeee00000001); | |
| 2513 | try testing.expect((try a.toInt(i128)) == -0xeeeeeeee00000001); | |
| 2514 | 2514 | } |
| 2515 | 2515 | |
| 2516 | 2516 | test "bitwise or negative-negative multi-limb" { |
| ... | ... | @@ -2521,7 +2521,7 @@ test "bitwise or negative-negative multi-limb" { |
| 2521 | 2521 | |
| 2522 | 2522 | try a.bitOr(&a, &b); |
| 2523 | 2523 | |
| 2524 | try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb)); | |
| 2524 | try testing.expect((try a.toInt(SignedDoubleLimb)) == -maxInt(Limb)); | |
| 2525 | 2525 | } |
| 2526 | 2526 | |
| 2527 | 2527 | test "var args" { |
| ... | ... | @@ -2531,7 +2531,7 @@ test "var args" { |
| 2531 | 2531 | var b = try Managed.initSet(testing.allocator, 6); |
| 2532 | 2532 | defer b.deinit(); |
| 2533 | 2533 | try a.add(&a, &b); |
| 2534 | try testing.expect((try a.to(u64)) == 11); | |
| 2534 | try testing.expect((try a.toInt(u64)) == 11); | |
| 2535 | 2535 | |
| 2536 | 2536 | var c = try Managed.initSet(testing.allocator, 11); |
| 2537 | 2537 | defer c.deinit(); |
| ... | ... | @@ -2552,7 +2552,7 @@ test "gcd non-one small" { |
| 2552 | 2552 | |
| 2553 | 2553 | try r.gcd(&a, &b); |
| 2554 | 2554 | |
| 2555 | try testing.expect((try r.to(u32)) == 1); | |
| 2555 | try testing.expect((try r.toInt(u32)) == 1); | |
| 2556 | 2556 | } |
| 2557 | 2557 | |
| 2558 | 2558 | test "gcd non-one medium" { |
| ... | ... | @@ -2565,7 +2565,7 @@ test "gcd non-one medium" { |
| 2565 | 2565 | |
| 2566 | 2566 | try r.gcd(&a, &b); |
| 2567 | 2567 | |
| 2568 | try testing.expect((try r.to(u32)) == 38); | |
| 2568 | try testing.expect((try r.toInt(u32)) == 38); | |
| 2569 | 2569 | } |
| 2570 | 2570 | |
| 2571 | 2571 | test "gcd non-one large" { |
| ... | ... | @@ -2578,7 +2578,7 @@ test "gcd non-one large" { |
| 2578 | 2578 | |
| 2579 | 2579 | try r.gcd(&a, &b); |
| 2580 | 2580 | |
| 2581 | try testing.expect((try r.to(u32)) == 4369); | |
| 2581 | try testing.expect((try r.toInt(u32)) == 4369); | |
| 2582 | 2582 | } |
| 2583 | 2583 | |
| 2584 | 2584 | test "gcd large multi-limb result" { |
| ... | ... | @@ -2593,7 +2593,7 @@ test "gcd large multi-limb result" { |
| 2593 | 2593 | |
| 2594 | 2594 | try r.gcd(&a, &b); |
| 2595 | 2595 | |
| 2596 | const answer = (try r.to(u256)); | |
| 2596 | const answer = (try r.toInt(u256)); | |
| 2597 | 2597 | try testing.expect(answer == 0xf000000ff00000fff0000ffff000fffff00ffffff1); |
| 2598 | 2598 | } |
| 2599 | 2599 | |
| ... | ... | @@ -2607,7 +2607,7 @@ test "gcd one large" { |
| 2607 | 2607 | |
| 2608 | 2608 | try r.gcd(&a, &b); |
| 2609 | 2609 | |
| 2610 | try testing.expect((try r.to(u64)) == 1); | |
| 2610 | try testing.expect((try r.toInt(u64)) == 1); | |
| 2611 | 2611 | } |
| 2612 | 2612 | |
| 2613 | 2613 | test "mutable to managed" { |
| ... | ... | @@ -2637,10 +2637,10 @@ test "pow" { |
| 2637 | 2637 | defer a.deinit(); |
| 2638 | 2638 | |
| 2639 | 2639 | try a.pow(&a, 3); |
| 2640 | try testing.expectEqual(@as(i32, -27), try a.to(i32)); | |
| 2640 | try testing.expectEqual(@as(i32, -27), try a.toInt(i32)); | |
| 2641 | 2641 | |
| 2642 | 2642 | try a.pow(&a, 4); |
| 2643 | try testing.expectEqual(@as(i32, 531441), try a.to(i32)); | |
| 2643 | try testing.expectEqual(@as(i32, 531441), try a.toInt(i32)); | |
| 2644 | 2644 | } |
| 2645 | 2645 | { |
| 2646 | 2646 | var a = try Managed.initSet(testing.allocator, 10); |
| ... | ... | @@ -2671,18 +2671,18 @@ test "pow" { |
| 2671 | 2671 | defer a.deinit(); |
| 2672 | 2672 | |
| 2673 | 2673 | try a.pow(&a, 100); |
| 2674 | try testing.expectEqual(@as(i32, 0), try a.to(i32)); | |
| 2674 | try testing.expectEqual(@as(i32, 0), try a.toInt(i32)); | |
| 2675 | 2675 | |
| 2676 | 2676 | try a.set(1); |
| 2677 | 2677 | try a.pow(&a, 0); |
| 2678 | try testing.expectEqual(@as(i32, 1), try a.to(i32)); | |
| 2678 | try testing.expectEqual(@as(i32, 1), try a.toInt(i32)); | |
| 2679 | 2679 | try a.pow(&a, 100); |
| 2680 | try testing.expectEqual(@as(i32, 1), try a.to(i32)); | |
| 2680 | try testing.expectEqual(@as(i32, 1), try a.toInt(i32)); | |
| 2681 | 2681 | try a.set(-1); |
| 2682 | 2682 | try a.pow(&a, 15); |
| 2683 | try testing.expectEqual(@as(i32, -1), try a.to(i32)); | |
| 2683 | try testing.expectEqual(@as(i32, -1), try a.toInt(i32)); | |
| 2684 | 2684 | try a.pow(&a, 16); |
| 2685 | try testing.expectEqual(@as(i32, 1), try a.to(i32)); | |
| 2685 | try testing.expectEqual(@as(i32, 1), try a.toInt(i32)); | |
| 2686 | 2686 | } |
| 2687 | 2687 | } |
| 2688 | 2688 | |
| ... | ... | @@ -2696,24 +2696,24 @@ test "sqrt" { |
| 2696 | 2696 | try r.set(0); |
| 2697 | 2697 | try a.set(25); |
| 2698 | 2698 | try r.sqrt(&a); |
| 2699 | try testing.expectEqual(@as(i32, 5), try r.to(i32)); | |
| 2699 | try testing.expectEqual(@as(i32, 5), try r.toInt(i32)); | |
| 2700 | 2700 | |
| 2701 | 2701 | // aliased |
| 2702 | 2702 | try a.set(25); |
| 2703 | 2703 | try a.sqrt(&a); |
| 2704 | try testing.expectEqual(@as(i32, 5), try a.to(i32)); | |
| 2704 | try testing.expectEqual(@as(i32, 5), try a.toInt(i32)); | |
| 2705 | 2705 | |
| 2706 | 2706 | // bottom |
| 2707 | 2707 | try r.set(0); |
| 2708 | 2708 | try a.set(24); |
| 2709 | 2709 | try r.sqrt(&a); |
| 2710 | try testing.expectEqual(@as(i32, 4), try r.to(i32)); | |
| 2710 | try testing.expectEqual(@as(i32, 4), try r.toInt(i32)); | |
| 2711 | 2711 | |
| 2712 | 2712 | // large number |
| 2713 | 2713 | try r.set(0); |
| 2714 | 2714 | try a.set(0x1_0000_0000_0000); |
| 2715 | 2715 | try r.sqrt(&a); |
| 2716 | try testing.expectEqual(@as(i32, 0x100_0000), try r.to(i32)); | |
| 2716 | try testing.expectEqual(@as(i32, 0x100_0000), try r.toInt(i32)); | |
| 2717 | 2717 | } |
| 2718 | 2718 | |
| 2719 | 2719 | test "regression test for 1 limb overflow with alias" { |
| ... | ... | @@ -3225,7 +3225,7 @@ test "Managed sqrt(0) = 0" { |
| 3225 | 3225 | try a.setString(10, "0"); |
| 3226 | 3226 | |
| 3227 | 3227 | try res.sqrt(&a); |
| 3228 | try testing.expectEqual(@as(i32, 0), try res.to(i32)); | |
| 3228 | try testing.expectEqual(@as(i32, 0), try res.toInt(i32)); | |
| 3229 | 3229 | } |
| 3230 | 3230 | |
| 3231 | 3231 | test "Managed sqrt(-1) = error" { |
lib/std/math/big/rational.zig+48-48| ... | ... | @@ -518,28 +518,28 @@ test "set" { |
| 518 | 518 | defer a.deinit(); |
| 519 | 519 | |
| 520 | 520 | try a.setInt(5); |
| 521 | try testing.expect((try a.p.to(u32)) == 5); | |
| 522 | try testing.expect((try a.q.to(u32)) == 1); | |
| 521 | try testing.expect((try a.p.toInt(u32)) == 5); | |
| 522 | try testing.expect((try a.q.toInt(u32)) == 1); | |
| 523 | 523 | |
| 524 | 524 | try a.setRatio(7, 3); |
| 525 | try testing.expect((try a.p.to(u32)) == 7); | |
| 526 | try testing.expect((try a.q.to(u32)) == 3); | |
| 525 | try testing.expect((try a.p.toInt(u32)) == 7); | |
| 526 | try testing.expect((try a.q.toInt(u32)) == 3); | |
| 527 | 527 | |
| 528 | 528 | try a.setRatio(9, 3); |
| 529 | try testing.expect((try a.p.to(i32)) == 3); | |
| 530 | try testing.expect((try a.q.to(i32)) == 1); | |
| 529 | try testing.expect((try a.p.toInt(i32)) == 3); | |
| 530 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 531 | 531 | |
| 532 | 532 | try a.setRatio(-9, 3); |
| 533 | try testing.expect((try a.p.to(i32)) == -3); | |
| 534 | try testing.expect((try a.q.to(i32)) == 1); | |
| 533 | try testing.expect((try a.p.toInt(i32)) == -3); | |
| 534 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 535 | 535 | |
| 536 | 536 | try a.setRatio(9, -3); |
| 537 | try testing.expect((try a.p.to(i32)) == -3); | |
| 538 | try testing.expect((try a.q.to(i32)) == 1); | |
| 537 | try testing.expect((try a.p.toInt(i32)) == -3); | |
| 538 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 539 | 539 | |
| 540 | 540 | try a.setRatio(-9, -3); |
| 541 | try testing.expect((try a.p.to(i32)) == 3); | |
| 542 | try testing.expect((try a.q.to(i32)) == 1); | |
| 541 | try testing.expect((try a.p.toInt(i32)) == 3); | |
| 542 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 543 | 543 | } |
| 544 | 544 | |
| 545 | 545 | test "setFloat" { |
| ... | ... | @@ -547,24 +547,24 @@ test "setFloat" { |
| 547 | 547 | defer a.deinit(); |
| 548 | 548 | |
| 549 | 549 | try a.setFloat(f64, 2.5); |
| 550 | try testing.expect((try a.p.to(i32)) == 5); | |
| 551 | try testing.expect((try a.q.to(i32)) == 2); | |
| 550 | try testing.expect((try a.p.toInt(i32)) == 5); | |
| 551 | try testing.expect((try a.q.toInt(i32)) == 2); | |
| 552 | 552 | |
| 553 | 553 | try a.setFloat(f32, -2.5); |
| 554 | try testing.expect((try a.p.to(i32)) == -5); | |
| 555 | try testing.expect((try a.q.to(i32)) == 2); | |
| 554 | try testing.expect((try a.p.toInt(i32)) == -5); | |
| 555 | try testing.expect((try a.q.toInt(i32)) == 2); | |
| 556 | 556 | |
| 557 | 557 | try a.setFloat(f32, 3.141593); |
| 558 | 558 | |
| 559 | 559 | // = 3.14159297943115234375 |
| 560 | try testing.expect((try a.p.to(u32)) == 3294199); | |
| 561 | try testing.expect((try a.q.to(u32)) == 1048576); | |
| 560 | try testing.expect((try a.p.toInt(u32)) == 3294199); | |
| 561 | try testing.expect((try a.q.toInt(u32)) == 1048576); | |
| 562 | 562 | |
| 563 | 563 | try a.setFloat(f64, 72.141593120712409172417410926841290461290467124); |
| 564 | 564 | |
| 565 | 565 | // = 72.1415931207124145885245525278151035308837890625 |
| 566 | try testing.expect((try a.p.to(u128)) == 5076513310880537); | |
| 567 | try testing.expect((try a.q.to(u128)) == 70368744177664); | |
| 566 | try testing.expect((try a.p.toInt(u128)) == 5076513310880537); | |
| 567 | try testing.expect((try a.q.toInt(u128)) == 70368744177664); | |
| 568 | 568 | } |
| 569 | 569 | |
| 570 | 570 | test "setFloatString" { |
| ... | ... | @@ -574,8 +574,8 @@ test "setFloatString" { |
| 574 | 574 | try a.setFloatString("72.14159312071241458852455252781510353"); |
| 575 | 575 | |
| 576 | 576 | // = 72.1415931207124145885245525278151035308837890625 |
| 577 | try testing.expect((try a.p.to(u128)) == 7214159312071241458852455252781510353); | |
| 578 | try testing.expect((try a.q.to(u128)) == 100000000000000000000000000000000000); | |
| 577 | try testing.expect((try a.p.toInt(u128)) == 7214159312071241458852455252781510353); | |
| 578 | try testing.expect((try a.q.toInt(u128)) == 100000000000000000000000000000000000); | |
| 579 | 579 | } |
| 580 | 580 | |
| 581 | 581 | test "toFloat" { |
| ... | ... | @@ -612,8 +612,8 @@ test "copy" { |
| 612 | 612 | defer b.deinit(); |
| 613 | 613 | |
| 614 | 614 | try a.copyInt(b); |
| 615 | try testing.expect((try a.p.to(u32)) == 5); | |
| 616 | try testing.expect((try a.q.to(u32)) == 1); | |
| 615 | try testing.expect((try a.p.toInt(u32)) == 5); | |
| 616 | try testing.expect((try a.q.toInt(u32)) == 1); | |
| 617 | 617 | |
| 618 | 618 | var c = try Int.initSet(testing.allocator, 7); |
| 619 | 619 | defer c.deinit(); |
| ... | ... | @@ -621,8 +621,8 @@ test "copy" { |
| 621 | 621 | defer d.deinit(); |
| 622 | 622 | |
| 623 | 623 | try a.copyRatio(c, d); |
| 624 | try testing.expect((try a.p.to(u32)) == 7); | |
| 625 | try testing.expect((try a.q.to(u32)) == 3); | |
| 624 | try testing.expect((try a.p.toInt(u32)) == 7); | |
| 625 | try testing.expect((try a.q.toInt(u32)) == 3); | |
| 626 | 626 | |
| 627 | 627 | var e = try Int.initSet(testing.allocator, 9); |
| 628 | 628 | defer e.deinit(); |
| ... | ... | @@ -630,8 +630,8 @@ test "copy" { |
| 630 | 630 | defer f.deinit(); |
| 631 | 631 | |
| 632 | 632 | try a.copyRatio(e, f); |
| 633 | try testing.expect((try a.p.to(u32)) == 3); | |
| 634 | try testing.expect((try a.q.to(u32)) == 1); | |
| 633 | try testing.expect((try a.p.toInt(u32)) == 3); | |
| 634 | try testing.expect((try a.q.toInt(u32)) == 1); | |
| 635 | 635 | } |
| 636 | 636 | |
| 637 | 637 | test "negate" { |
| ... | ... | @@ -639,16 +639,16 @@ test "negate" { |
| 639 | 639 | defer a.deinit(); |
| 640 | 640 | |
| 641 | 641 | try a.setInt(-50); |
| 642 | try testing.expect((try a.p.to(i32)) == -50); | |
| 643 | try testing.expect((try a.q.to(i32)) == 1); | |
| 642 | try testing.expect((try a.p.toInt(i32)) == -50); | |
| 643 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 644 | 644 | |
| 645 | 645 | a.negate(); |
| 646 | try testing.expect((try a.p.to(i32)) == 50); | |
| 647 | try testing.expect((try a.q.to(i32)) == 1); | |
| 646 | try testing.expect((try a.p.toInt(i32)) == 50); | |
| 647 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 648 | 648 | |
| 649 | 649 | a.negate(); |
| 650 | try testing.expect((try a.p.to(i32)) == -50); | |
| 651 | try testing.expect((try a.q.to(i32)) == 1); | |
| 650 | try testing.expect((try a.p.toInt(i32)) == -50); | |
| 651 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 652 | 652 | } |
| 653 | 653 | |
| 654 | 654 | test "abs" { |
| ... | ... | @@ -656,16 +656,16 @@ test "abs" { |
| 656 | 656 | defer a.deinit(); |
| 657 | 657 | |
| 658 | 658 | try a.setInt(-50); |
| 659 | try testing.expect((try a.p.to(i32)) == -50); | |
| 660 | try testing.expect((try a.q.to(i32)) == 1); | |
| 659 | try testing.expect((try a.p.toInt(i32)) == -50); | |
| 660 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 661 | 661 | |
| 662 | 662 | a.abs(); |
| 663 | try testing.expect((try a.p.to(i32)) == 50); | |
| 664 | try testing.expect((try a.q.to(i32)) == 1); | |
| 663 | try testing.expect((try a.p.toInt(i32)) == 50); | |
| 664 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 665 | 665 | |
| 666 | 666 | a.abs(); |
| 667 | try testing.expect((try a.p.to(i32)) == 50); | |
| 668 | try testing.expect((try a.q.to(i32)) == 1); | |
| 667 | try testing.expect((try a.p.toInt(i32)) == 50); | |
| 668 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 669 | 669 | } |
| 670 | 670 | |
| 671 | 671 | test "swap" { |
| ... | ... | @@ -677,19 +677,19 @@ test "swap" { |
| 677 | 677 | try a.setRatio(50, 23); |
| 678 | 678 | try b.setRatio(17, 3); |
| 679 | 679 | |
| 680 | try testing.expect((try a.p.to(u32)) == 50); | |
| 681 | try testing.expect((try a.q.to(u32)) == 23); | |
| 680 | try testing.expect((try a.p.toInt(u32)) == 50); | |
| 681 | try testing.expect((try a.q.toInt(u32)) == 23); | |
| 682 | 682 | |
| 683 | try testing.expect((try b.p.to(u32)) == 17); | |
| 684 | try testing.expect((try b.q.to(u32)) == 3); | |
| 683 | try testing.expect((try b.p.toInt(u32)) == 17); | |
| 684 | try testing.expect((try b.q.toInt(u32)) == 3); | |
| 685 | 685 | |
| 686 | 686 | a.swap(&b); |
| 687 | 687 | |
| 688 | try testing.expect((try a.p.to(u32)) == 17); | |
| 689 | try testing.expect((try a.q.to(u32)) == 3); | |
| 688 | try testing.expect((try a.p.toInt(u32)) == 17); | |
| 689 | try testing.expect((try a.q.toInt(u32)) == 3); | |
| 690 | 690 | |
| 691 | try testing.expect((try b.p.to(u32)) == 50); | |
| 692 | try testing.expect((try b.q.to(u32)) == 23); | |
| 691 | try testing.expect((try b.p.toInt(u32)) == 50); | |
| 692 | try testing.expect((try b.q.toInt(u32)) == 23); | |
| 693 | 693 | } |
| 694 | 694 | |
| 695 | 695 | test "order" { |
lib/std/std.zig+1| ... | ... | @@ -93,6 +93,7 @@ pub const valgrind = @import("valgrind.zig"); |
| 93 | 93 | pub const wasm = @import("wasm.zig"); |
| 94 | 94 | pub const zig = @import("zig.zig"); |
| 95 | 95 | pub const zip = @import("zip.zig"); |
| 96 | pub const zon = @import("zon.zig"); | |
| 96 | 97 | pub const start = @import("start.zig"); |
| 97 | 98 | |
| 98 | 99 | const root = @import("root"); |
lib/std/zig/AstGen.zig+26-3| ... | ... | @@ -9448,7 +9448,18 @@ fn builtinCall( |
| 9448 | 9448 | } else if (str.len == 0) { |
| 9449 | 9449 | return astgen.failTok(str_lit_token, "import path cannot be empty", .{}); |
| 9450 | 9450 | } |
| 9451 | const result = try gz.addStrTok(.import, str.index, str_lit_token); | |
| 9451 | const res_ty = try ri.rl.resultType(gz, node) orelse .none; | |
| 9452 | const payload_index = try addExtra(gz.astgen, Zir.Inst.Import{ | |
| 9453 | .res_ty = res_ty, | |
| 9454 | .path = str.index, | |
| 9455 | }); | |
| 9456 | const result = try gz.add(.{ | |
| 9457 | .tag = .import, | |
| 9458 | .data = .{ .pl_tok = .{ | |
| 9459 | .src_tok = gz.tokenIndexToRelative(str_lit_token), | |
| 9460 | .payload_index = payload_index, | |
| 9461 | } }, | |
| 9462 | }); | |
| 9452 | 9463 | const gop = try astgen.imports.getOrPut(astgen.gpa, str.index); |
| 9453 | 9464 | if (!gop.found_existing) { |
| 9454 | 9465 | gop.value_ptr.* = str_lit_token; |
| ... | ... | @@ -11551,9 +11562,21 @@ fn parseStrLit( |
| 11551 | 11562 | } |
| 11552 | 11563 | } |
| 11553 | 11564 | |
| 11554 | fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError { | |
| 11565 | fn failWithStrLitError( | |
| 11566 | astgen: *AstGen, | |
| 11567 | err: std.zig.string_literal.Error, | |
| 11568 | token: Ast.TokenIndex, | |
| 11569 | bytes: []const u8, | |
| 11570 | offset: u32, | |
| 11571 | ) InnerError { | |
| 11555 | 11572 | const raw_string = bytes[offset..]; |
| 11556 | return err.lower(raw_string, offset, AstGen.failOff, .{ astgen, token }); | |
| 11573 | return failOff( | |
| 11574 | astgen, | |
| 11575 | token, | |
| 11576 | @intCast(offset + err.offset()), | |
| 11577 | "{}", | |
| 11578 | .{err.fmt(raw_string)}, | |
| 11579 | ); | |
| 11557 | 11580 | } |
| 11558 | 11581 | |
| 11559 | 11582 | fn failNode( |
lib/std/zig/Zir.zig+9-2| ... | ... | @@ -483,7 +483,7 @@ pub const Inst = struct { |
| 483 | 483 | /// Uses the `pl_node` union field. `payload_index` points to a `FuncFancy`. |
| 484 | 484 | func_fancy, |
| 485 | 485 | /// Implements the `@import` builtin. |
| 486 | /// Uses the `str_tok` field. | |
| 486 | /// Uses the `pl_tok` field. | |
| 487 | 487 | import, |
| 488 | 488 | /// Integer literal that fits in a u64. Uses the `int` union field. |
| 489 | 489 | int, |
| ... | ... | @@ -1673,7 +1673,7 @@ pub const Inst = struct { |
| 1673 | 1673 | .func = .pl_node, |
| 1674 | 1674 | .func_inferred = .pl_node, |
| 1675 | 1675 | .func_fancy = .pl_node, |
| 1676 | .import = .str_tok, | |
| 1676 | .import = .pl_tok, | |
| 1677 | 1677 | .int = .int, |
| 1678 | 1678 | .int_big = .str, |
| 1679 | 1679 | .float = .float, |
| ... | ... | @@ -3841,6 +3841,13 @@ pub const Inst = struct { |
| 3841 | 3841 | /// If `.none`, restore unconditionally. |
| 3842 | 3842 | operand: Ref, |
| 3843 | 3843 | }; |
| 3844 | ||
| 3845 | pub const Import = struct { | |
| 3846 | /// The result type of the import, or `.none` if none was available. | |
| 3847 | res_ty: Ref, | |
| 3848 | /// The import path. | |
| 3849 | path: NullTerminatedString, | |
| 3850 | }; | |
| 3844 | 3851 | }; |
| 3845 | 3852 | |
| 3846 | 3853 | pub const SpecialProng = enum { none, @"else", under }; |
lib/std/zig/Zoir.zig+2-2| ... | ... | @@ -54,7 +54,7 @@ pub const Node = union(enum) { |
| 54 | 54 | /// A floating-point literal. |
| 55 | 55 | float_literal: f128, |
| 56 | 56 | /// A Unicode codepoint literal. |
| 57 | char_literal: u32, | |
| 57 | char_literal: u21, | |
| 58 | 58 | /// An enum literal. The string is the literal, i.e. `foo` for `.foo`. |
| 59 | 59 | enum_literal: NullTerminatedString, |
| 60 | 60 | /// A string literal. |
| ... | ... | @@ -96,7 +96,7 @@ pub const Node = union(enum) { |
| 96 | 96 | } } }, |
| 97 | 97 | .float_literal_small => .{ .float_literal = @as(f32, @bitCast(repr.data)) }, |
| 98 | 98 | .float_literal => .{ .float_literal = @bitCast(zoir.extra[repr.data..][0..4].*) }, |
| 99 | .char_literal => .{ .char_literal = repr.data }, | |
| 99 | .char_literal => .{ .char_literal = @intCast(repr.data) }, | |
| 100 | 100 | .enum_literal => .{ .enum_literal = @enumFromInt(repr.data) }, |
| 101 | 101 | .string_literal => .{ .string_literal = s: { |
| 102 | 102 | const start, const len = zoir.extra[repr.data..][0..2].*; |
lib/std/zig/ZonGen.zig+154-54| ... | ... | @@ -3,6 +3,8 @@ |
| 3 | 3 | gpa: Allocator, |
| 4 | 4 | tree: Ast, |
| 5 | 5 | |
| 6 | options: Options, | |
| 7 | ||
| 6 | 8 | nodes: std.MultiArrayList(Zoir.Node.Repr), |
| 7 | 9 | extra: std.ArrayListUnmanaged(u32), |
| 8 | 10 | limbs: std.ArrayListUnmanaged(std.math.big.Limb), |
| ... | ... | @@ -12,12 +14,21 @@ string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.d |
| 12 | 14 | compile_errors: std.ArrayListUnmanaged(Zoir.CompileError), |
| 13 | 15 | error_notes: std.ArrayListUnmanaged(Zoir.CompileError.Note), |
| 14 | 16 | |
| 15 | pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zoir { | |
| 17 | pub const Options = struct { | |
| 18 | /// When false, string literals are not parsed. `string_literal` nodes will contain empty | |
| 19 | /// strings, and errors that normally occur during string parsing will not be raised. | |
| 20 | /// | |
| 21 | /// `parseStrLit` and `strLitSizeHint` may be used to parse string literals after the fact. | |
| 22 | parse_str_lits: bool = true, | |
| 23 | }; | |
| 24 | ||
| 25 | pub fn generate(gpa: Allocator, tree: Ast, options: Options) Allocator.Error!Zoir { | |
| 16 | 26 | assert(tree.mode == .zon); |
| 17 | 27 | |
| 18 | 28 | var zg: ZonGen = .{ |
| 19 | 29 | .gpa = gpa, |
| 20 | 30 | .tree = tree, |
| 31 | .options = options, | |
| 21 | 32 | .nodes = .empty, |
| 22 | 33 | .extra = .empty, |
| 23 | 34 | .limbs = .empty, |
| ... | ... | @@ -250,7 +261,20 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator |
| 250 | 261 | .block_two_semicolon, |
| 251 | 262 | .block, |
| 252 | 263 | .block_semicolon, |
| 253 | => try zg.addErrorNode(node, "blocks are not allowed in ZON", .{}), | |
| 264 | => { | |
| 265 | const size = switch (node_tags[node]) { | |
| 266 | .block_two, .block_two_semicolon => @intFromBool(node_datas[node].lhs != 0) + @intFromBool(node_datas[node].rhs != 0), | |
| 267 | .block, .block_semicolon => node_datas[node].rhs - node_datas[node].lhs, | |
| 268 | else => unreachable, | |
| 269 | }; | |
| 270 | if (size == 0) { | |
| 271 | try zg.addErrorNodeNotes(node, "void literals are not available in ZON", .{}, &.{ | |
| 272 | try zg.errNoteNode(node, "void union payloads can be represented by enum literals", .{}), | |
| 273 | }); | |
| 274 | } else { | |
| 275 | try zg.addErrorNode(node, "blocks are not allowed in ZON", .{}); | |
| 276 | } | |
| 277 | }, | |
| 254 | 278 | |
| 255 | 279 | .array_init_one, |
| 256 | 280 | .array_init_one_comma, |
| ... | ... | @@ -403,58 +427,37 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator |
| 403 | 427 | .ast_node = node, |
| 404 | 428 | }); |
| 405 | 429 | |
| 430 | // For short initializers, track the names on the stack rather than going through gpa. | |
| 431 | var sfba_state = std.heap.stackFallback(256, gpa); | |
| 432 | const sfba = sfba_state.get(); | |
| 433 | var field_names: std.AutoHashMapUnmanaged(Zoir.NullTerminatedString, Ast.TokenIndex) = .empty; | |
| 434 | defer field_names.deinit(sfba); | |
| 435 | ||
| 436 | var reported_any_duplicate = false; | |
| 437 | ||
| 406 | 438 | for (full.ast.fields, names_start.., first_elem..) |elem_node, extra_name_idx, elem_dest_node| { |
| 407 | 439 | const name_token = tree.firstToken(elem_node) - 2; |
| 408 | zg.extra.items[extra_name_idx] = @intFromEnum(zg.identAsString(name_token) catch |err| switch (err) { | |
| 409 | error.BadString => undefined, // doesn't matter, there's an error | |
| 440 | if (zg.identAsString(name_token)) |name_str| { | |
| 441 | zg.extra.items[extra_name_idx] = @intFromEnum(name_str); | |
| 442 | const gop = try field_names.getOrPut(sfba, name_str); | |
| 443 | if (gop.found_existing and !reported_any_duplicate) { | |
| 444 | reported_any_duplicate = true; | |
| 445 | const earlier_token = gop.value_ptr.*; | |
| 446 | try zg.addErrorTokNotes(earlier_token, "duplicate struct field name", .{}, &.{ | |
| 447 | try zg.errNoteTok(name_token, "duplicate name here", .{}), | |
| 448 | }); | |
| 449 | } | |
| 450 | gop.value_ptr.* = name_token; | |
| 451 | } else |err| switch (err) { | |
| 452 | error.BadString => {}, // there's an error, so it's fine to not populate `zg.extra` | |
| 410 | 453 | error.OutOfMemory => |e| return e, |
| 411 | }); | |
| 454 | } | |
| 412 | 455 | try zg.expr(elem_node, @enumFromInt(elem_dest_node)); |
| 413 | 456 | } |
| 414 | 457 | }, |
| 415 | 458 | } |
| 416 | 459 | } |
| 417 | 460 | |
| 418 | fn parseStrLit(zg: *ZonGen, token: Ast.TokenIndex, offset: u32) !u32 { | |
| 419 | const raw_string = zg.tree.tokenSlice(token)[offset..]; | |
| 420 | const start = zg.string_bytes.items.len; | |
| 421 | switch (try std.zig.string_literal.parseWrite(zg.string_bytes.writer(zg.gpa), raw_string)) { | |
| 422 | .success => return @intCast(start), | |
| 423 | .failure => |err| { | |
| 424 | try zg.lowerStrLitError(err, token, raw_string, offset); | |
| 425 | return error.BadString; | |
| 426 | }, | |
| 427 | } | |
| 428 | } | |
| 429 | ||
| 430 | fn parseMultilineStrLit(zg: *ZonGen, node: Ast.Node.Index) !u32 { | |
| 431 | const gpa = zg.gpa; | |
| 432 | const tree = zg.tree; | |
| 433 | const string_bytes = &zg.string_bytes; | |
| 434 | ||
| 435 | const first_tok, const last_tok = bounds: { | |
| 436 | const node_data = tree.nodes.items(.data)[node]; | |
| 437 | break :bounds .{ node_data.lhs, node_data.rhs }; | |
| 438 | }; | |
| 439 | ||
| 440 | const str_index: u32 = @intCast(string_bytes.items.len); | |
| 441 | ||
| 442 | // First line: do not append a newline. | |
| 443 | { | |
| 444 | const line_bytes = tree.tokenSlice(first_tok)[2..]; | |
| 445 | try string_bytes.appendSlice(gpa, line_bytes); | |
| 446 | } | |
| 447 | // Following lines: each line prepends a newline. | |
| 448 | for (first_tok + 1..last_tok + 1) |tok_idx| { | |
| 449 | const line_bytes = tree.tokenSlice(@intCast(tok_idx))[2..]; | |
| 450 | try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1); | |
| 451 | string_bytes.appendAssumeCapacity('\n'); | |
| 452 | string_bytes.appendSliceAssumeCapacity(line_bytes); | |
| 453 | } | |
| 454 | ||
| 455 | return @intCast(str_index); | |
| 456 | } | |
| 457 | ||
| 458 | 461 | fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 { |
| 459 | 462 | const tree = zg.tree; |
| 460 | 463 | assert(tree.tokens.items(.tag)[ident_token] == .identifier); |
| ... | ... | @@ -464,7 +467,18 @@ fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 { |
| 464 | 467 | try zg.string_bytes.appendSlice(zg.gpa, ident_name); |
| 465 | 468 | return @intCast(start); |
| 466 | 469 | } else { |
| 467 | const start = try zg.parseStrLit(ident_token, 1); | |
| 470 | const offset = 1; | |
| 471 | const start: u32 = @intCast(zg.string_bytes.items.len); | |
| 472 | const raw_string = zg.tree.tokenSlice(ident_token)[offset..]; | |
| 473 | try zg.string_bytes.ensureUnusedCapacity(zg.gpa, raw_string.len); | |
| 474 | switch (try std.zig.string_literal.parseWrite(zg.string_bytes.writer(zg.gpa), raw_string)) { | |
| 475 | .success => {}, | |
| 476 | .failure => |err| { | |
| 477 | try zg.lowerStrLitError(err, ident_token, raw_string, offset); | |
| 478 | return error.BadString; | |
| 479 | }, | |
| 480 | } | |
| 481 | ||
| 468 | 482 | const slice = zg.string_bytes.items[start..]; |
| 469 | 483 | if (mem.indexOfScalar(u8, slice, 0) != null) { |
| 470 | 484 | try zg.addErrorTok(ident_token, "identifier cannot contain null bytes", .{}); |
| ... | ... | @@ -477,19 +491,93 @@ fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 { |
| 477 | 491 | } |
| 478 | 492 | } |
| 479 | 493 | |
| 494 | /// Estimates the size of a string node without parsing it. | |
| 495 | pub fn strLitSizeHint(tree: Ast, node: Ast.Node.Index) usize { | |
| 496 | switch (tree.nodes.items(.tag)[node]) { | |
| 497 | // Parsed string literals are typically around the size of the raw strings. | |
| 498 | .string_literal => { | |
| 499 | const token = tree.nodes.items(.main_token)[node]; | |
| 500 | const raw_string = tree.tokenSlice(token); | |
| 501 | return raw_string.len; | |
| 502 | }, | |
| 503 | // Multiline string literal lengths can be computed exactly. | |
| 504 | .multiline_string_literal => { | |
| 505 | const first_tok, const last_tok = bounds: { | |
| 506 | const node_data = tree.nodes.items(.data)[node]; | |
| 507 | break :bounds .{ node_data.lhs, node_data.rhs }; | |
| 508 | }; | |
| 509 | ||
| 510 | var size = tree.tokenSlice(first_tok)[2..].len; | |
| 511 | for (first_tok + 1..last_tok + 1) |tok_idx| { | |
| 512 | size += 1; // Newline | |
| 513 | size += tree.tokenSlice(@intCast(tok_idx))[2..].len; | |
| 514 | } | |
| 515 | return size; | |
| 516 | }, | |
| 517 | else => unreachable, | |
| 518 | } | |
| 519 | } | |
| 520 | ||
| 521 | /// Parses the given node as a string literal. | |
| 522 | pub fn parseStrLit( | |
| 523 | tree: Ast, | |
| 524 | node: Ast.Node.Index, | |
| 525 | writer: anytype, | |
| 526 | ) error{OutOfMemory}!std.zig.string_literal.Result { | |
| 527 | switch (tree.nodes.items(.tag)[node]) { | |
| 528 | .string_literal => { | |
| 529 | const token = tree.nodes.items(.main_token)[node]; | |
| 530 | const raw_string = tree.tokenSlice(token); | |
| 531 | return std.zig.string_literal.parseWrite(writer, raw_string); | |
| 532 | }, | |
| 533 | .multiline_string_literal => { | |
| 534 | const first_tok, const last_tok = bounds: { | |
| 535 | const node_data = tree.nodes.items(.data)[node]; | |
| 536 | break :bounds .{ node_data.lhs, node_data.rhs }; | |
| 537 | }; | |
| 538 | ||
| 539 | // First line: do not append a newline. | |
| 540 | { | |
| 541 | const line_bytes = tree.tokenSlice(first_tok)[2..]; | |
| 542 | try writer.writeAll(line_bytes); | |
| 543 | } | |
| 544 | ||
| 545 | // Following lines: each line prepends a newline. | |
| 546 | for (first_tok + 1..last_tok + 1) |tok_idx| { | |
| 547 | const line_bytes = tree.tokenSlice(@intCast(tok_idx))[2..]; | |
| 548 | try writer.writeByte('\n'); | |
| 549 | try writer.writeAll(line_bytes); | |
| 550 | } | |
| 551 | ||
| 552 | return .success; | |
| 553 | }, | |
| 554 | // Node must represent a string | |
| 555 | else => unreachable, | |
| 556 | } | |
| 557 | } | |
| 558 | ||
| 480 | 559 | const StringLiteralResult = union(enum) { |
| 481 | 560 | nts: Zoir.NullTerminatedString, |
| 482 | 561 | slice: struct { start: u32, len: u32 }, |
| 483 | 562 | }; |
| 484 | 563 | |
| 485 | 564 | fn strLitAsString(zg: *ZonGen, str_node: Ast.Node.Index) !StringLiteralResult { |
| 565 | if (!zg.options.parse_str_lits) return .{ .slice = .{ .start = 0, .len = 0 } }; | |
| 566 | ||
| 486 | 567 | const gpa = zg.gpa; |
| 487 | 568 | const string_bytes = &zg.string_bytes; |
| 488 | const str_index = switch (zg.tree.nodes.items(.tag)[str_node]) { | |
| 489 | .string_literal => try zg.parseStrLit(zg.tree.nodes.items(.main_token)[str_node], 0), | |
| 490 | .multiline_string_literal => try zg.parseMultilineStrLit(str_node), | |
| 491 | else => unreachable, | |
| 492 | }; | |
| 569 | const str_index: u32 = @intCast(zg.string_bytes.items.len); | |
| 570 | const size_hint = strLitSizeHint(zg.tree, str_node); | |
| 571 | try string_bytes.ensureUnusedCapacity(zg.gpa, size_hint); | |
| 572 | switch (try parseStrLit(zg.tree, str_node, zg.string_bytes.writer(zg.gpa))) { | |
| 573 | .success => {}, | |
| 574 | .failure => |err| { | |
| 575 | const token = zg.tree.nodes.items(.main_token)[str_node]; | |
| 576 | const raw_string = zg.tree.tokenSlice(token); | |
| 577 | try zg.lowerStrLitError(err, token, raw_string, 0); | |
| 578 | return error.BadString; | |
| 579 | }, | |
| 580 | } | |
| 493 | 581 | const key: []const u8 = string_bytes.items[str_index..]; |
| 494 | 582 | if (std.mem.indexOfScalar(u8, key, 0) != null) return .{ .slice = .{ |
| 495 | 583 | .start = str_index, |
| ... | ... | @@ -540,7 +628,7 @@ fn numberLiteral(zg: *ZonGen, num_node: Ast.Node.Index, src_node: Ast.Node.Index |
| 540 | 628 | if (unsigned_num == 0 and sign == .negative) { |
| 541 | 629 | try zg.addErrorTokNotes(num_token, "integer literal '-0' is ambiguous", .{}, &.{ |
| 542 | 630 | try zg.errNoteTok(num_token, "use '0' for an integer zero", .{}), |
| 543 | try zg.errNoteTok(num_token, "use '-0.0' for a flaoting-point signed zero", .{}), | |
| 631 | try zg.errNoteTok(num_token, "use '-0.0' for a floating-point signed zero", .{}), | |
| 544 | 632 | }); |
| 545 | 633 | return; |
| 546 | 634 | } |
| ... | ... | @@ -679,8 +767,20 @@ fn setNode(zg: *ZonGen, dest: Zoir.Node.Index, repr: Zoir.Node.Repr) void { |
| 679 | 767 | zg.nodes.set(@intFromEnum(dest), repr); |
| 680 | 768 | } |
| 681 | 769 | |
| 682 | fn lowerStrLitError(zg: *ZonGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, raw_string: []const u8, offset: u32) Allocator.Error!void { | |
| 683 | return err.lower(raw_string, offset, ZonGen.addErrorTokOff, .{ zg, token }); | |
| 770 | fn lowerStrLitError( | |
| 771 | zg: *ZonGen, | |
| 772 | err: std.zig.string_literal.Error, | |
| 773 | token: Ast.TokenIndex, | |
| 774 | raw_string: []const u8, | |
| 775 | offset: u32, | |
| 776 | ) Allocator.Error!void { | |
| 777 | return ZonGen.addErrorTokOff( | |
| 778 | zg, | |
| 779 | token, | |
| 780 | @intCast(offset + err.offset()), | |
| 781 | "{}", | |
| 782 | .{err.fmt(raw_string)}, | |
| 783 | ); | |
| 684 | 784 | } |
| 685 | 785 | |
| 686 | 786 | fn lowerNumberError(zg: *ZonGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) Allocator.Error!void { |
lib/std/zig/string_literal.zig+72-30| ... | ... | @@ -39,40 +39,82 @@ pub const Error = union(enum) { |
| 39 | 39 | /// `''`. Not returned for string literals. |
| 40 | 40 | empty_char_literal, |
| 41 | 41 | |
| 42 | /// Returns `func(first_args[0], ..., first_args[n], offset + bad_idx, format, args)`. | |
| 43 | pub fn lower( | |
| 42 | const FormatMessage = struct { | |
| 44 | 43 | err: Error, |
| 45 | 44 | raw_string: []const u8, |
| 46 | offset: u32, | |
| 47 | comptime func: anytype, | |
| 48 | first_args: anytype, | |
| 49 | ) @typeInfo(@TypeOf(func)).@"fn".return_type.? { | |
| 50 | switch (err) { | |
| 51 | inline else => |bad_index_or_void, tag| { | |
| 52 | const bad_index: u32 = switch (@TypeOf(bad_index_or_void)) { | |
| 53 | void => 0, | |
| 54 | else => @intCast(bad_index_or_void), | |
| 55 | }; | |
| 56 | const fmt_str: []const u8, const args = switch (tag) { | |
| 57 | .invalid_escape_character => .{ "invalid escape character: '{c}'", .{raw_string[bad_index]} }, | |
| 58 | .expected_hex_digit => .{ "expected hex digit, found '{c}'", .{raw_string[bad_index]} }, | |
| 59 | .empty_unicode_escape_sequence => .{ "empty unicode escape sequence", .{} }, | |
| 60 | .expected_hex_digit_or_rbrace => .{ "expected hex digit or '}}', found '{c}'", .{raw_string[bad_index]} }, | |
| 61 | .invalid_unicode_codepoint => .{ "unicode escape does not correspond to a valid unicode scalar value", .{} }, | |
| 62 | .expected_lbrace => .{ "expected '{{', found '{c}'", .{raw_string[bad_index]} }, | |
| 63 | .expected_rbrace => .{ "expected '}}', found '{c}'", .{raw_string[bad_index]} }, | |
| 64 | .expected_single_quote => .{ "expected singel quote ('), found '{c}'", .{raw_string[bad_index]} }, | |
| 65 | .invalid_character => .{ "invalid byte in string or character literal: '{c}'", .{raw_string[bad_index]} }, | |
| 66 | .empty_char_literal => .{ "empty character literal", .{} }, | |
| 67 | }; | |
| 68 | return @call(.auto, func, first_args ++ .{ | |
| 69 | offset + bad_index, | |
| 70 | fmt_str, | |
| 71 | args, | |
| 72 | }); | |
| 73 | }, | |
| 45 | }; | |
| 46 | ||
| 47 | fn formatMessage( | |
| 48 | self: FormatMessage, | |
| 49 | comptime f: []const u8, | |
| 50 | options: std.fmt.FormatOptions, | |
| 51 | writer: anytype, | |
| 52 | ) !void { | |
| 53 | _ = f; | |
| 54 | _ = options; | |
| 55 | switch (self.err) { | |
| 56 | .invalid_escape_character => |bad_index| try writer.print( | |
| 57 | "invalid escape character: '{c}'", | |
| 58 | .{self.raw_string[bad_index]}, | |
| 59 | ), | |
| 60 | .expected_hex_digit => |bad_index| try writer.print( | |
| 61 | "expected hex digit, found '{c}'", | |
| 62 | .{self.raw_string[bad_index]}, | |
| 63 | ), | |
| 64 | .empty_unicode_escape_sequence => try writer.writeAll( | |
| 65 | "empty unicode escape sequence", | |
| 66 | ), | |
| 67 | .expected_hex_digit_or_rbrace => |bad_index| try writer.print( | |
| 68 | "expected hex digit or '}}', found '{c}'", | |
| 69 | .{self.raw_string[bad_index]}, | |
| 70 | ), | |
| 71 | .invalid_unicode_codepoint => try writer.writeAll( | |
| 72 | "unicode escape does not correspond to a valid unicode scalar value", | |
| 73 | ), | |
| 74 | .expected_lbrace => |bad_index| try writer.print( | |
| 75 | "expected '{{', found '{c}'", | |
| 76 | .{self.raw_string[bad_index]}, | |
| 77 | ), | |
| 78 | .expected_rbrace => |bad_index| try writer.print( | |
| 79 | "expected '}}', found '{c}'", | |
| 80 | .{self.raw_string[bad_index]}, | |
| 81 | ), | |
| 82 | .expected_single_quote => |bad_index| try writer.print( | |
| 83 | "expected single quote ('), found '{c}'", | |
| 84 | .{self.raw_string[bad_index]}, | |
| 85 | ), | |
| 86 | .invalid_character => |bad_index| try writer.print( | |
| 87 | "invalid byte in string or character literal: '{c}'", | |
| 88 | .{self.raw_string[bad_index]}, | |
| 89 | ), | |
| 90 | .empty_char_literal => try writer.writeAll( | |
| 91 | "empty character literal", | |
| 92 | ), | |
| 74 | 93 | } |
| 75 | 94 | } |
| 95 | ||
| 96 | pub fn fmt(self: @This(), raw_string: []const u8) std.fmt.Formatter(formatMessage) { | |
| 97 | return .{ .data = .{ | |
| 98 | .err = self, | |
| 99 | .raw_string = raw_string, | |
| 100 | } }; | |
| 101 | } | |
| 102 | ||
| 103 | pub fn offset(err: Error) usize { | |
| 104 | return switch (err) { | |
| 105 | inline .invalid_escape_character, | |
| 106 | .expected_hex_digit, | |
| 107 | .empty_unicode_escape_sequence, | |
| 108 | .expected_hex_digit_or_rbrace, | |
| 109 | .invalid_unicode_codepoint, | |
| 110 | .expected_lbrace, | |
| 111 | .expected_rbrace, | |
| 112 | .expected_single_quote, | |
| 113 | .invalid_character, | |
| 114 | => |n| n, | |
| 115 | .empty_char_literal => 0, | |
| 116 | }; | |
| 117 | } | |
| 76 | 118 | }; |
| 77 | 119 | |
| 78 | 120 | /// Asserts the slice starts and ends with single-quotes. |
lib/std/zon.zig created+45| ... | ... | @@ -0,0 +1,45 @@ |
| 1 | //! ZON parsing and stringification. | |
| 2 | //! | |
| 3 | //! ZON ("Zig Object Notation") is a textual file format. Outside of `nan` and `inf` literals, ZON's | |
| 4 | //! grammar is a subset of Zig's. | |
| 5 | //! | |
| 6 | //! Supported Zig primitives: | |
| 7 | //! * boolean literals | |
| 8 | //! * number literals (including `nan` and `inf`) | |
| 9 | //! * character literals | |
| 10 | //! * enum literals | |
| 11 | //! * `null` literals | |
| 12 | //! * string literals | |
| 13 | //! * multiline string literals | |
| 14 | //! | |
| 15 | //! Supported Zig container types: | |
| 16 | //! * anonymous struct literals | |
| 17 | //! * anonymous tuple literals | |
| 18 | //! | |
| 19 | //! Here is an example ZON object: | |
| 20 | //! ``` | |
| 21 | //! .{ | |
| 22 | //! .a = 1.5, | |
| 23 | //! .b = "hello, world!", | |
| 24 | //! .c = .{ true, false }, | |
| 25 | //! .d = .{ 1, 2, 3 }, | |
| 26 | //! } | |
| 27 | //! ``` | |
| 28 | //! | |
| 29 | //! Individual primitives are also valid ZON, for example: | |
| 30 | //! ``` | |
| 31 | //! "This string is a valid ZON object." | |
| 32 | //! ``` | |
| 33 | //! | |
| 34 | //! ZON may not contain type names. | |
| 35 | //! | |
| 36 | //! ZON does not have syntax for pointers, but the parsers will allocate as needed to match the | |
| 37 | //! given Zig types. Similarly, the serializer will traverse pointers. | |
| 38 | ||
| 39 | pub const parse = @import("zon/parse.zig"); | |
| 40 | pub const stringify = @import("zon/stringify.zig"); | |
| 41 | ||
| 42 | test { | |
| 43 | _ = parse; | |
| 44 | _ = stringify; | |
| 45 | } |
lib/std/zon/parse.zig created+3449| ... | ... | @@ -0,0 +1,3449 @@ |
| 1 | //! The simplest way to parse ZON at runtime is to use `fromSlice`. If you need to parse ZON at | |
| 2 | //! compile time, you may use `@import`. | |
| 3 | //! | |
| 4 | //! Parsing from individual Zoir nodes is also available: | |
| 5 | //! * `fromZoir` | |
| 6 | //! * `fromZoirNode` | |
| 7 | //! | |
| 8 | //! For lower level control, it is possible to operate on `std.zig.Zoir` directly. | |
| 9 | ||
| 10 | const std = @import("std"); | |
| 11 | const builtin = @import("builtin"); | |
| 12 | const Allocator = std.mem.Allocator; | |
| 13 | const Ast = std.zig.Ast; | |
| 14 | const Zoir = std.zig.Zoir; | |
| 15 | const ZonGen = std.zig.ZonGen; | |
| 16 | const TokenIndex = std.zig.Ast.TokenIndex; | |
| 17 | const Base = std.zig.number_literal.Base; | |
| 18 | const StrLitErr = std.zig.string_literal.Error; | |
| 19 | const NumberLiteralError = std.zig.number_literal.Error; | |
| 20 | const assert = std.debug.assert; | |
| 21 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | |
| 22 | ||
| 23 | /// Rename when adding or removing support for a type. | |
| 24 | const valid_types = {}; | |
| 25 | ||
| 26 | /// Configuration for the runtime parser. | |
| 27 | pub const Options = struct { | |
| 28 | /// If true, unknown fields do not error. | |
| 29 | ignore_unknown_fields: bool = false, | |
| 30 | /// If true, the parser cleans up partially parsed values on error. This requires some extra | |
| 31 | /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because | |
| 32 | /// you're using arena allocation.) | |
| 33 | free_on_error: bool = true, | |
| 34 | }; | |
| 35 | ||
| 36 | pub const Error = union(enum) { | |
| 37 | zoir: Zoir.CompileError, | |
| 38 | type_check: Error.TypeCheckFailure, | |
| 39 | ||
| 40 | pub const Note = union(enum) { | |
| 41 | zoir: Zoir.CompileError.Note, | |
| 42 | type_check: TypeCheckFailure.Note, | |
| 43 | ||
| 44 | pub const Iterator = struct { | |
| 45 | index: usize = 0, | |
| 46 | err: Error, | |
| 47 | status: *const Status, | |
| 48 | ||
| 49 | pub fn next(self: *@This()) ?Note { | |
| 50 | switch (self.err) { | |
| 51 | .zoir => |err| { | |
| 52 | if (self.index >= err.note_count) return null; | |
| 53 | const zoir = self.status.zoir.?; | |
| 54 | const note = err.getNotes(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( | |
| 69 | self: []const u8, | |
| 70 | comptime f: []const u8, | |
| 71 | options: std.fmt.FormatOptions, | |
| 72 | writer: anytype, | |
| 73 | ) !void { | |
| 74 | _ = f; | |
| 75 | _ = options; | |
| 76 | ||
| 77 | // Just writes the string for now, but we're keeping this behind a formatter so we have | |
| 78 | // the option to extend it in the future to print more advanced messages (like `Error` | |
| 79 | // does) without breaking the API. | |
| 80 | try writer.writeAll(self); | |
| 81 | } | |
| 82 | ||
| 83 | pub fn fmtMessage(self: Note, status: *const Status) std.fmt.Formatter(Note.formatMessage) { | |
| 84 | return .{ .data = switch (self) { | |
| 85 | .zoir => |note| note.msg.get(status.zoir.?), | |
| 86 | .type_check => |note| note.msg, | |
| 87 | } }; | |
| 88 | } | |
| 89 | ||
| 90 | pub fn getLocation(self: Note, status: *const Status) Ast.Location { | |
| 91 | const ast = status.ast.?; | |
| 92 | switch (self) { | |
| 93 | .zoir => |note| return zoirErrorLocation(ast, note.token, note.node_or_offset), | |
| 94 | .type_check => |note| return ast.tokenLocation(note.offset, note.token), | |
| 95 | } | |
| 96 | } | |
| 97 | }; | |
| 98 | ||
| 99 | pub const Iterator = struct { | |
| 100 | index: usize = 0, | |
| 101 | status: *const Status, | |
| 102 | ||
| 103 | pub fn next(self: *@This()) ?Error { | |
| 104 | const zoir = self.status.zoir orelse return null; | |
| 105 | ||
| 106 | if (self.index < zoir.compile_errors.len) { | |
| 107 | const result: Error = .{ .zoir = zoir.compile_errors[self.index] }; | |
| 108 | self.index += 1; | |
| 109 | return result; | |
| 110 | } | |
| 111 | ||
| 112 | if (self.status.type_check) |err| { | |
| 113 | if (self.index == zoir.compile_errors.len) { | |
| 114 | const result: Error = .{ .type_check = err }; | |
| 115 | self.index += 1; | |
| 116 | return result; | |
| 117 | } | |
| 118 | } | |
| 119 | ||
| 120 | return null; | |
| 121 | } | |
| 122 | }; | |
| 123 | ||
| 124 | const TypeCheckFailure = struct { | |
| 125 | const Note = struct { | |
| 126 | token: Ast.TokenIndex, | |
| 127 | offset: u32, | |
| 128 | msg: []const u8, | |
| 129 | owned: bool, | |
| 130 | ||
| 131 | fn deinit(self: @This(), gpa: Allocator) void { | |
| 132 | if (self.owned) gpa.free(self.msg); | |
| 133 | } | |
| 134 | }; | |
| 135 | ||
| 136 | message: []const u8, | |
| 137 | owned: bool, | |
| 138 | token: Ast.TokenIndex, | |
| 139 | offset: u32, | |
| 140 | note: ?@This().Note, | |
| 141 | ||
| 142 | fn deinit(self: @This(), gpa: Allocator) void { | |
| 143 | if (self.note) |note| note.deinit(gpa); | |
| 144 | if (self.owned) gpa.free(self.message); | |
| 145 | } | |
| 146 | ||
| 147 | fn getNoteCount(self: @This()) usize { | |
| 148 | return @intFromBool(self.note != null); | |
| 149 | } | |
| 150 | ||
| 151 | fn getNote(self: @This(), index: usize) @This().Note { | |
| 152 | assert(index == 0); | |
| 153 | return self.note.?; | |
| 154 | } | |
| 155 | }; | |
| 156 | ||
| 157 | const FormatMessage = struct { | |
| 158 | err: Error, | |
| 159 | status: *const Status, | |
| 160 | }; | |
| 161 | ||
| 162 | fn formatMessage( | |
| 163 | self: FormatMessage, | |
| 164 | comptime f: []const u8, | |
| 165 | options: std.fmt.FormatOptions, | |
| 166 | writer: anytype, | |
| 167 | ) !void { | |
| 168 | _ = f; | |
| 169 | _ = options; | |
| 170 | switch (self.err) { | |
| 171 | .zoir => |err| try writer.writeAll(err.msg.get(self.status.zoir.?)), | |
| 172 | .type_check => |tc| try writer.writeAll(tc.message), | |
| 173 | } | |
| 174 | } | |
| 175 | ||
| 176 | pub fn fmtMessage(self: @This(), status: *const Status) std.fmt.Formatter(formatMessage) { | |
| 177 | return .{ .data = .{ | |
| 178 | .err = self, | |
| 179 | .status = status, | |
| 180 | } }; | |
| 181 | } | |
| 182 | ||
| 183 | pub fn getLocation(self: @This(), status: *const Status) Ast.Location { | |
| 184 | const ast = status.ast.?; | |
| 185 | return switch (self) { | |
| 186 | .zoir => |err| return zoirErrorLocation( | |
| 187 | status.ast.?, | |
| 188 | err.token, | |
| 189 | err.node_or_offset, | |
| 190 | ), | |
| 191 | .type_check => |err| return ast.tokenLocation(err.offset, err.token), | |
| 192 | }; | |
| 193 | } | |
| 194 | ||
| 195 | pub fn iterateNotes(self: @This(), status: *const Status) Note.Iterator { | |
| 196 | return .{ .err = self, .status = status }; | |
| 197 | } | |
| 198 | ||
| 199 | fn zoirErrorLocation(ast: Ast, maybe_token: Ast.TokenIndex, node_or_offset: u32) Ast.Location { | |
| 200 | if (maybe_token == Zoir.CompileError.invalid_token) { | |
| 201 | const main_tokens = ast.nodes.items(.main_token); | |
| 202 | const ast_node = node_or_offset; | |
| 203 | const token = main_tokens[ast_node]; | |
| 204 | return ast.tokenLocation(0, token); | |
| 205 | } else { | |
| 206 | var location = ast.tokenLocation(0, maybe_token); | |
| 207 | location.column += node_or_offset; | |
| 208 | return location; | |
| 209 | } | |
| 210 | } | |
| 211 | }; | |
| 212 | ||
| 213 | /// Information about the success or failure of a parse. | |
| 214 | pub const Status = struct { | |
| 215 | ast: ?Ast = null, | |
| 216 | zoir: ?Zoir = null, | |
| 217 | type_check: ?Error.TypeCheckFailure = null, | |
| 218 | ||
| 219 | fn assertEmpty(self: Status) void { | |
| 220 | assert(self.ast == null); | |
| 221 | assert(self.zoir == null); | |
| 222 | assert(self.type_check == null); | |
| 223 | } | |
| 224 | ||
| 225 | pub fn deinit(self: *Status, gpa: Allocator) void { | |
| 226 | if (self.ast) |*ast| ast.deinit(gpa); | |
| 227 | if (self.zoir) |*zoir| zoir.deinit(gpa); | |
| 228 | if (self.type_check) |tc| tc.deinit(gpa); | |
| 229 | self.* = undefined; | |
| 230 | } | |
| 231 | ||
| 232 | pub fn iterateErrors(self: *const Status) Error.Iterator { | |
| 233 | return .{ .status = self }; | |
| 234 | } | |
| 235 | ||
| 236 | pub fn format( | |
| 237 | self: *const @This(), | |
| 238 | comptime fmt: []const u8, | |
| 239 | options: std.fmt.FormatOptions, | |
| 240 | writer: anytype, | |
| 241 | ) !void { | |
| 242 | _ = fmt; | |
| 243 | _ = options; | |
| 244 | var errors = self.iterateErrors(); | |
| 245 | while (errors.next()) |err| { | |
| 246 | const loc = err.getLocation(self); | |
| 247 | const msg = err.fmtMessage(self); | |
| 248 | try writer.print("{}:{}: error: {}\n", .{ loc.line + 1, loc.column + 1, msg }); | |
| 249 | ||
| 250 | var notes = err.iterateNotes(self); | |
| 251 | while (notes.next()) |note| { | |
| 252 | const note_loc = note.getLocation(self); | |
| 253 | const note_msg = note.fmtMessage(self); | |
| 254 | try writer.print("{}:{}: note: {s}\n", .{ | |
| 255 | note_loc.line + 1, | |
| 256 | note_loc.column + 1, | |
| 257 | note_msg, | |
| 258 | }); | |
| 259 | } | |
| 260 | } | |
| 261 | } | |
| 262 | }; | |
| 263 | ||
| 264 | /// Parses the given slice as ZON. | |
| 265 | /// | |
| 266 | /// Returns `error.OutOfMemory` on allocation failure, or `error.ParseZon` error if the ZON is | |
| 267 | /// invalid or can not be deserialized into type `T`. | |
| 268 | /// | |
| 269 | /// When the parser returns `error.ParseZon`, it will also store a human readable explanation in | |
| 270 | /// `status` if non null. If status is not null, it must be initialized to `.{}`. | |
| 271 | pub fn fromSlice( | |
| 272 | /// The type to deserialize into. May not be or contain any of the following types: | |
| 273 | /// * Any comptime-only type, except in a comptime field | |
| 274 | /// * `type` | |
| 275 | /// * `void`, except as a union payload | |
| 276 | /// * `noreturn` | |
| 277 | /// * An error set/error union | |
| 278 | /// * A many-pointer or C-pointer | |
| 279 | /// * An opaque type, including `anyopaque` | |
| 280 | /// * An async frame type, including `anyframe` and `anyframe->T` | |
| 281 | /// * A function | |
| 282 | /// | |
| 283 | /// All other types are valid. Unsupported types will fail at compile time. | |
| 284 | T: type, | |
| 285 | gpa: Allocator, | |
| 286 | source: [:0]const u8, | |
| 287 | status: ?*Status, | |
| 288 | options: Options, | |
| 289 | ) error{ OutOfMemory, ParseZon }!T { | |
| 290 | if (status) |s| s.assertEmpty(); | |
| 291 | ||
| 292 | var ast = try std.zig.Ast.parse(gpa, source, .zon); | |
| 293 | defer if (status == null) ast.deinit(gpa); | |
| 294 | if (status) |s| s.ast = ast; | |
| 295 | ||
| 296 | // If there's no status, Zoir exists for the lifetime of this function. If there is a status, | |
| 297 | // ownership is transferred to status. | |
| 298 | var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); | |
| 299 | defer if (status == null) zoir.deinit(gpa); | |
| 300 | ||
| 301 | if (status) |s| s.* = .{}; | |
| 302 | return fromZoir(T, gpa, ast, zoir, status, options); | |
| 303 | } | |
| 304 | ||
| 305 | /// Like `fromSlice`, but operates on `Zoir` instead of ZON source. | |
| 306 | pub fn fromZoir( | |
| 307 | T: type, | |
| 308 | gpa: Allocator, | |
| 309 | ast: Ast, | |
| 310 | zoir: Zoir, | |
| 311 | status: ?*Status, | |
| 312 | options: Options, | |
| 313 | ) error{ OutOfMemory, ParseZon }!T { | |
| 314 | return fromZoirNode(T, gpa, ast, zoir, .root, status, options); | |
| 315 | } | |
| 316 | ||
| 317 | /// Like `fromZoir`, but the parse starts on `node` instead of root. | |
| 318 | pub fn fromZoirNode( | |
| 319 | T: type, | |
| 320 | gpa: Allocator, | |
| 321 | ast: Ast, | |
| 322 | zoir: Zoir, | |
| 323 | node: Zoir.Node.Index, | |
| 324 | status: ?*Status, | |
| 325 | options: Options, | |
| 326 | ) error{ OutOfMemory, ParseZon }!T { | |
| 327 | comptime assert(canParseType(T)); | |
| 328 | ||
| 329 | if (status) |s| { | |
| 330 | s.assertEmpty(); | |
| 331 | s.ast = ast; | |
| 332 | s.zoir = zoir; | |
| 333 | } | |
| 334 | ||
| 335 | if (zoir.hasCompileErrors()) { | |
| 336 | return error.ParseZon; | |
| 337 | } | |
| 338 | ||
| 339 | var parser: Parser = .{ | |
| 340 | .gpa = gpa, | |
| 341 | .ast = ast, | |
| 342 | .zoir = zoir, | |
| 343 | .options = options, | |
| 344 | .status = status, | |
| 345 | }; | |
| 346 | ||
| 347 | return parser.parseExpr(T, node); | |
| 348 | } | |
| 349 | ||
| 350 | /// Frees ZON values. | |
| 351 | /// | |
| 352 | /// Provided for convenience, you may also free these values on your own using the same allocator | |
| 353 | /// passed into the parser. | |
| 354 | /// | |
| 355 | /// Asserts at comptime that sufficient information is available via the type system to free this | |
| 356 | /// value. Untagged unions, for example, will fail this assert. | |
| 357 | pub fn free(gpa: Allocator, value: anytype) void { | |
| 358 | const Value = @TypeOf(value); | |
| 359 | ||
| 360 | _ = valid_types; | |
| 361 | switch (@typeInfo(Value)) { | |
| 362 | .bool, .int, .float, .@"enum" => {}, | |
| 363 | .pointer => |pointer| { | |
| 364 | switch (pointer.size) { | |
| 365 | .one => { | |
| 366 | free(gpa, value.*); | |
| 367 | gpa.destroy(value); | |
| 368 | }, | |
| 369 | .slice => { | |
| 370 | for (value) |item| { | |
| 371 | free(gpa, item); | |
| 372 | } | |
| 373 | gpa.free(value); | |
| 374 | }, | |
| 375 | .many, .c => comptime unreachable, | |
| 376 | } | |
| 377 | }, | |
| 378 | .array => for (value) |item| { | |
| 379 | free(gpa, item); | |
| 380 | }, | |
| 381 | .@"struct" => |@"struct"| inline for (@"struct".fields) |field| { | |
| 382 | free(gpa, @field(value, field.name)); | |
| 383 | }, | |
| 384 | .@"union" => |@"union"| if (@"union".tag_type == null) { | |
| 385 | if (comptime requiresAllocator(Value)) unreachable; | |
| 386 | } else switch (value) { | |
| 387 | inline else => |_, tag| { | |
| 388 | free(gpa, @field(value, @tagName(tag))); | |
| 389 | }, | |
| 390 | }, | |
| 391 | .optional => if (value) |some| { | |
| 392 | free(gpa, some); | |
| 393 | }, | |
| 394 | .vector => |vector| for (0..vector.len) |i| free(gpa, value[i]), | |
| 395 | .void => {}, | |
| 396 | else => comptime unreachable, | |
| 397 | } | |
| 398 | } | |
| 399 | ||
| 400 | fn requiresAllocator(T: type) bool { | |
| 401 | _ = valid_types; | |
| 402 | return switch (@typeInfo(T)) { | |
| 403 | .pointer => true, | |
| 404 | .array => |array| return array.len > 0 and requiresAllocator(array.child), | |
| 405 | .@"struct" => |@"struct"| inline for (@"struct".fields) |field| { | |
| 406 | if (requiresAllocator(field.type)) { | |
| 407 | break true; | |
| 408 | } | |
| 409 | } else false, | |
| 410 | .@"union" => |@"union"| inline for (@"union".fields) |field| { | |
| 411 | if (requiresAllocator(field.type)) { | |
| 412 | break true; | |
| 413 | } | |
| 414 | } else false, | |
| 415 | .optional => |optional| requiresAllocator(optional.child), | |
| 416 | .vector => |vector| return vector.len > 0 and requiresAllocator(vector.child), | |
| 417 | else => false, | |
| 418 | }; | |
| 419 | } | |
| 420 | ||
| 421 | const Parser = struct { | |
| 422 | gpa: Allocator, | |
| 423 | ast: Ast, | |
| 424 | zoir: Zoir, | |
| 425 | status: ?*Status, | |
| 426 | options: Options, | |
| 427 | ||
| 428 | fn parseExpr(self: *@This(), T: type, node: Zoir.Node.Index) error{ ParseZon, OutOfMemory }!T { | |
| 429 | return self.parseExprInner(T, node) catch |err| switch (err) { | |
| 430 | error.WrongType => return self.failExpectedType(T, node), | |
| 431 | else => |e| return e, | |
| 432 | }; | |
| 433 | } | |
| 434 | ||
| 435 | fn parseExprInner( | |
| 436 | self: *@This(), | |
| 437 | T: type, | |
| 438 | node: Zoir.Node.Index, | |
| 439 | ) error{ ParseZon, OutOfMemory, WrongType }!T { | |
| 440 | switch (@typeInfo(T)) { | |
| 441 | .optional => |optional| if (node.get(self.zoir) == .null) { | |
| 442 | return null; | |
| 443 | } else { | |
| 444 | return try self.parseExprInner(optional.child, node); | |
| 445 | }, | |
| 446 | .bool => return self.parseBool(node), | |
| 447 | .int => return self.parseInt(T, node), | |
| 448 | .float => return self.parseFloat(T, node), | |
| 449 | .@"enum" => return self.parseEnumLiteral(T, node), | |
| 450 | .pointer => |pointer| switch (pointer.size) { | |
| 451 | .one => { | |
| 452 | const result = try self.gpa.create(pointer.child); | |
| 453 | errdefer self.gpa.destroy(result); | |
| 454 | result.* = try self.parseExprInner(pointer.child, node); | |
| 455 | return result; | |
| 456 | }, | |
| 457 | .slice => return self.parseSlicePointer(T, node), | |
| 458 | else => comptime unreachable, | |
| 459 | }, | |
| 460 | .array => return self.parseArray(T, node), | |
| 461 | .@"struct" => |@"struct"| if (@"struct".is_tuple) | |
| 462 | return self.parseTuple(T, node) | |
| 463 | else | |
| 464 | return self.parseStruct(T, node), | |
| 465 | .@"union" => return self.parseUnion(T, node), | |
| 466 | .vector => return self.parseVector(T, node), | |
| 467 | ||
| 468 | else => comptime unreachable, | |
| 469 | } | |
| 470 | } | |
| 471 | ||
| 472 | /// Prints a message of the form `expected T` where T is first converted to a ZON type. For | |
| 473 | /// example, `**?**u8` becomes `?u8`, and types that involve user specified type names are just | |
| 474 | /// referred to by the type of container. | |
| 475 | fn failExpectedType( | |
| 476 | self: @This(), | |
| 477 | T: type, | |
| 478 | node: Zoir.Node.Index, | |
| 479 | ) error{ ParseZon, OutOfMemory } { | |
| 480 | @branchHint(.cold); | |
| 481 | return self.failExpectedTypeInner(T, false, node); | |
| 482 | } | |
| 483 | ||
| 484 | fn failExpectedTypeInner( | |
| 485 | self: @This(), | |
| 486 | T: type, | |
| 487 | opt: bool, | |
| 488 | node: Zoir.Node.Index, | |
| 489 | ) error{ ParseZon, OutOfMemory } { | |
| 490 | _ = valid_types; | |
| 491 | switch (@typeInfo(T)) { | |
| 492 | .@"struct" => |@"struct"| if (@"struct".is_tuple) { | |
| 493 | if (opt) { | |
| 494 | return self.failNode(node, "expected optional tuple"); | |
| 495 | } else { | |
| 496 | return self.failNode(node, "expected tuple"); | |
| 497 | } | |
| 498 | } else { | |
| 499 | if (opt) { | |
| 500 | return self.failNode(node, "expected optional struct"); | |
| 501 | } else { | |
| 502 | return self.failNode(node, "expected struct"); | |
| 503 | } | |
| 504 | }, | |
| 505 | .@"union" => if (opt) { | |
| 506 | return self.failNode(node, "expected optional union"); | |
| 507 | } else { | |
| 508 | return self.failNode(node, "expected union"); | |
| 509 | }, | |
| 510 | .array => if (opt) { | |
| 511 | return self.failNode(node, "expected optional array"); | |
| 512 | } else { | |
| 513 | return self.failNode(node, "expected array"); | |
| 514 | }, | |
| 515 | .pointer => |pointer| switch (pointer.size) { | |
| 516 | .one => return self.failExpectedTypeInner(pointer.child, opt, node), | |
| 517 | .slice => { | |
| 518 | if (pointer.child == u8 and | |
| 519 | pointer.is_const and | |
| 520 | (pointer.sentinel() == null or pointer.sentinel() == 0) and | |
| 521 | pointer.alignment == 1) | |
| 522 | { | |
| 523 | if (opt) { | |
| 524 | return self.failNode(node, "expected optional string"); | |
| 525 | } else { | |
| 526 | return self.failNode(node, "expected string"); | |
| 527 | } | |
| 528 | } else { | |
| 529 | if (opt) { | |
| 530 | return self.failNode(node, "expected optional array"); | |
| 531 | } else { | |
| 532 | return self.failNode(node, "expected array"); | |
| 533 | } | |
| 534 | } | |
| 535 | }, | |
| 536 | else => comptime unreachable, | |
| 537 | }, | |
| 538 | .vector, .bool, .int, .float => if (opt) { | |
| 539 | return self.failNodeFmt(node, "expected type '{s}'", .{@typeName(?T)}); | |
| 540 | } else { | |
| 541 | return self.failNodeFmt(node, "expected type '{s}'", .{@typeName(T)}); | |
| 542 | }, | |
| 543 | .@"enum" => if (opt) { | |
| 544 | return self.failNode(node, "expected optional enum literal"); | |
| 545 | } else { | |
| 546 | return self.failNode(node, "expected enum literal"); | |
| 547 | }, | |
| 548 | .optional => |optional| { | |
| 549 | return self.failExpectedTypeInner(optional.child, true, node); | |
| 550 | }, | |
| 551 | else => comptime unreachable, | |
| 552 | } | |
| 553 | } | |
| 554 | ||
| 555 | fn parseBool(self: @This(), node: Zoir.Node.Index) !bool { | |
| 556 | switch (node.get(self.zoir)) { | |
| 557 | .true => return true, | |
| 558 | .false => return false, | |
| 559 | else => return error.WrongType, | |
| 560 | } | |
| 561 | } | |
| 562 | ||
| 563 | fn parseInt(self: @This(), T: type, node: Zoir.Node.Index) !T { | |
| 564 | switch (node.get(self.zoir)) { | |
| 565 | .int_literal => |int| switch (int) { | |
| 566 | .small => |val| return std.math.cast(T, val) orelse | |
| 567 | self.failCannotRepresent(T, node), | |
| 568 | .big => |val| return val.toInt(T) catch | |
| 569 | self.failCannotRepresent(T, node), | |
| 570 | }, | |
| 571 | .float_literal => |val| return intFromFloatExact(T, val) orelse | |
| 572 | self.failCannotRepresent(T, node), | |
| 573 | ||
| 574 | .char_literal => |val| return std.math.cast(T, val) orelse | |
| 575 | self.failCannotRepresent(T, node), | |
| 576 | else => return error.WrongType, | |
| 577 | } | |
| 578 | } | |
| 579 | ||
| 580 | fn parseFloat(self: @This(), T: type, node: Zoir.Node.Index) !T { | |
| 581 | switch (node.get(self.zoir)) { | |
| 582 | .int_literal => |int| switch (int) { | |
| 583 | .small => |val| return @floatFromInt(val), | |
| 584 | .big => |val| return val.toFloat(T), | |
| 585 | }, | |
| 586 | .float_literal => |val| return @floatCast(val), | |
| 587 | .pos_inf => return std.math.inf(T), | |
| 588 | .neg_inf => return -std.math.inf(T), | |
| 589 | .nan => return std.math.nan(T), | |
| 590 | .char_literal => |val| return @floatFromInt(val), | |
| 591 | else => return error.WrongType, | |
| 592 | } | |
| 593 | } | |
| 594 | ||
| 595 | fn parseEnumLiteral(self: @This(), T: type, node: Zoir.Node.Index) !T { | |
| 596 | switch (node.get(self.zoir)) { | |
| 597 | .enum_literal => |field_name| { | |
| 598 | // Create a comptime string map for the enum fields | |
| 599 | const enum_fields = @typeInfo(T).@"enum".fields; | |
| 600 | comptime var kvs_list: [enum_fields.len]struct { []const u8, T } = undefined; | |
| 601 | inline for (enum_fields, 0..) |field, i| { | |
| 602 | kvs_list[i] = .{ field.name, @enumFromInt(field.value) }; | |
| 603 | } | |
| 604 | const enum_tags = std.StaticStringMap(T).initComptime(kvs_list); | |
| 605 | ||
| 606 | // Get the tag if it exists | |
| 607 | const field_name_str = field_name.get(self.zoir); | |
| 608 | return enum_tags.get(field_name_str) orelse | |
| 609 | self.failUnexpected(T, "enum literal", node, null, field_name_str); | |
| 610 | }, | |
| 611 | else => return error.WrongType, | |
| 612 | } | |
| 613 | } | |
| 614 | ||
| 615 | fn parseSlicePointer(self: *@This(), T: type, node: Zoir.Node.Index) !T { | |
| 616 | switch (node.get(self.zoir)) { | |
| 617 | .string_literal => return self.parseString(T, node), | |
| 618 | .array_literal => |nodes| return self.parseSlice(T, nodes), | |
| 619 | .empty_literal => return self.parseSlice(T, .{ .start = node, .len = 0 }), | |
| 620 | else => return error.WrongType, | |
| 621 | } | |
| 622 | } | |
| 623 | ||
| 624 | fn parseString(self: *@This(), T: type, node: Zoir.Node.Index) !T { | |
| 625 | const ast_node = node.getAstNode(self.zoir); | |
| 626 | const pointer = @typeInfo(T).pointer; | |
| 627 | var size_hint = ZonGen.strLitSizeHint(self.ast, ast_node); | |
| 628 | if (pointer.sentinel() != null) size_hint += 1; | |
| 629 | ||
| 630 | var buf: std.ArrayListUnmanaged(u8) = try .initCapacity(self.gpa, size_hint); | |
| 631 | defer buf.deinit(self.gpa); | |
| 632 | switch (try ZonGen.parseStrLit(self.ast, ast_node, buf.writer(self.gpa))) { | |
| 633 | .success => {}, | |
| 634 | .failure => |err| { | |
| 635 | const token = self.ast.nodes.items(.main_token)[ast_node]; | |
| 636 | const raw_string = self.ast.tokenSlice(token); | |
| 637 | return self.failTokenFmt(token, @intCast(err.offset()), "{s}", .{err.fmt(raw_string)}); | |
| 638 | }, | |
| 639 | } | |
| 640 | ||
| 641 | if (pointer.child != u8 or | |
| 642 | pointer.size != .slice or | |
| 643 | !pointer.is_const or | |
| 644 | (pointer.sentinel() != null and pointer.sentinel() != 0) or | |
| 645 | pointer.alignment != 1) | |
| 646 | { | |
| 647 | return error.WrongType; | |
| 648 | } | |
| 649 | ||
| 650 | if (pointer.sentinel() != null) { | |
| 651 | return buf.toOwnedSliceSentinel(self.gpa, 0); | |
| 652 | } else { | |
| 653 | return buf.toOwnedSlice(self.gpa); | |
| 654 | } | |
| 655 | } | |
| 656 | ||
| 657 | fn parseSlice(self: *@This(), T: type, nodes: Zoir.Node.Index.Range) !T { | |
| 658 | const pointer = @typeInfo(T).pointer; | |
| 659 | ||
| 660 | // Make sure we're working with a slice | |
| 661 | switch (pointer.size) { | |
| 662 | .slice => {}, | |
| 663 | .one, .many, .c => comptime unreachable, | |
| 664 | } | |
| 665 | ||
| 666 | // Allocate the slice | |
| 667 | const slice = try self.gpa.allocWithOptions( | |
| 668 | pointer.child, | |
| 669 | nodes.len, | |
| 670 | pointer.alignment, | |
| 671 | pointer.sentinel(), | |
| 672 | ); | |
| 673 | errdefer self.gpa.free(slice); | |
| 674 | ||
| 675 | // Parse the elements and return the slice | |
| 676 | for (slice, 0..) |*elem, i| { | |
| 677 | errdefer if (self.options.free_on_error) { | |
| 678 | for (slice[0..i]) |item| { | |
| 679 | free(self.gpa, item); | |
| 680 | } | |
| 681 | }; | |
| 682 | elem.* = try self.parseExpr(pointer.child, nodes.at(@intCast(i))); | |
| 683 | } | |
| 684 | ||
| 685 | return slice; | |
| 686 | } | |
| 687 | ||
| 688 | fn parseArray(self: *@This(), T: type, node: Zoir.Node.Index) !T { | |
| 689 | const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) { | |
| 690 | .array_literal => |nodes| nodes, | |
| 691 | .empty_literal => .{ .start = node, .len = 0 }, | |
| 692 | else => return error.WrongType, | |
| 693 | }; | |
| 694 | ||
| 695 | const array_info = @typeInfo(T).array; | |
| 696 | ||
| 697 | // Check if the size matches | |
| 698 | if (nodes.len < array_info.len) { | |
| 699 | return self.failNodeFmt( | |
| 700 | node, | |
| 701 | "expected {} array elements; found {}", | |
| 702 | .{ array_info.len, nodes.len }, | |
| 703 | ); | |
| 704 | } else if (nodes.len > array_info.len) { | |
| 705 | return self.failNodeFmt( | |
| 706 | nodes.at(array_info.len), | |
| 707 | "index {} outside of array of length {}", | |
| 708 | .{ array_info.len, array_info.len }, | |
| 709 | ); | |
| 710 | } | |
| 711 | ||
| 712 | // Parse the elements and return the array | |
| 713 | var result: T = undefined; | |
| 714 | for (&result, 0..) |*elem, i| { | |
| 715 | // If we fail to parse this field, free all fields before it | |
| 716 | errdefer if (self.options.free_on_error) { | |
| 717 | for (result[0..i]) |item| { | |
| 718 | free(self.gpa, item); | |
| 719 | } | |
| 720 | }; | |
| 721 | ||
| 722 | elem.* = try self.parseExpr(array_info.child, nodes.at(@intCast(i))); | |
| 723 | } | |
| 724 | return result; | |
| 725 | } | |
| 726 | ||
| 727 | fn parseStruct(self: *@This(), T: type, node: Zoir.Node.Index) !T { | |
| 728 | const repr = node.get(self.zoir); | |
| 729 | const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) { | |
| 730 | .struct_literal => |nodes| nodes, | |
| 731 | .empty_literal => .{ .names = &.{}, .vals = .{ .start = node, .len = 0 } }, | |
| 732 | else => return error.WrongType, | |
| 733 | }; | |
| 734 | ||
| 735 | const field_infos = @typeInfo(T).@"struct".fields; | |
| 736 | ||
| 737 | // Build a map from field name to index. | |
| 738 | // The special value `comptime_field` indicates that this is actually a comptime field. | |
| 739 | const comptime_field = std.math.maxInt(usize); | |
| 740 | const field_indices: std.StaticStringMap(usize) = comptime b: { | |
| 741 | var kvs_list: [field_infos.len]struct { []const u8, usize } = undefined; | |
| 742 | for (&kvs_list, field_infos, 0..) |*kv, field, i| { | |
| 743 | kv.* = .{ field.name, if (field.is_comptime) comptime_field else i }; | |
| 744 | } | |
| 745 | break :b .initComptime(kvs_list); | |
| 746 | }; | |
| 747 | ||
| 748 | // Parse the struct | |
| 749 | var result: T = undefined; | |
| 750 | var field_found: [field_infos.len]bool = @splat(false); | |
| 751 | ||
| 752 | // If we fail partway through, free all already initialized fields | |
| 753 | var initialized: usize = 0; | |
| 754 | errdefer if (self.options.free_on_error and field_infos.len > 0) { | |
| 755 | for (fields.names[0..initialized]) |name_runtime| { | |
| 756 | switch (field_indices.get(name_runtime.get(self.zoir)) orelse continue) { | |
| 757 | inline 0...(field_infos.len - 1) => |name_index| { | |
| 758 | const name = field_infos[name_index].name; | |
| 759 | free(self.gpa, @field(result, name)); | |
| 760 | }, | |
| 761 | else => unreachable, // Can't be out of bounds | |
| 762 | } | |
| 763 | } | |
| 764 | }; | |
| 765 | ||
| 766 | // Fill in the fields we found | |
| 767 | for (0..fields.names.len) |i| { | |
| 768 | const name = fields.names[i].get(self.zoir); | |
| 769 | const field_index = field_indices.get(name) orelse { | |
| 770 | if (self.options.ignore_unknown_fields) continue; | |
| 771 | return self.failUnexpected(T, "field", node, i, name); | |
| 772 | }; | |
| 773 | if (field_index == comptime_field) { | |
| 774 | return self.failComptimeField(node, i); | |
| 775 | } | |
| 776 | ||
| 777 | // Mark the field as found. Assert that the found array is not zero length to satisfy | |
| 778 | // the type checker (it can't be since we made it into an iteration of this loop.) | |
| 779 | if (field_found.len == 0) unreachable; | |
| 780 | field_found[field_index] = true; | |
| 781 | ||
| 782 | switch (field_index) { | |
| 783 | inline 0...(field_infos.len - 1) => |j| { | |
| 784 | if (field_infos[j].is_comptime) unreachable; | |
| 785 | ||
| 786 | @field(result, field_infos[j].name) = try self.parseExpr( | |
| 787 | field_infos[j].type, | |
| 788 | fields.vals.at(@intCast(i)), | |
| 789 | ); | |
| 790 | }, | |
| 791 | else => unreachable, // Can't be out of bounds | |
| 792 | } | |
| 793 | ||
| 794 | initialized += 1; | |
| 795 | } | |
| 796 | ||
| 797 | // Fill in any missing default fields | |
| 798 | inline for (field_found, 0..) |found, i| { | |
| 799 | if (!found) { | |
| 800 | const field_info = field_infos[i]; | |
| 801 | if (field_info.default_value_ptr) |default| { | |
| 802 | const typed: *const field_info.type = @ptrCast(@alignCast(default)); | |
| 803 | @field(result, field_info.name) = typed.*; | |
| 804 | } else { | |
| 805 | return self.failNodeFmt( | |
| 806 | node, | |
| 807 | "missing required field {s}", | |
| 808 | .{field_infos[i].name}, | |
| 809 | ); | |
| 810 | } | |
| 811 | } | |
| 812 | } | |
| 813 | ||
| 814 | return result; | |
| 815 | } | |
| 816 | ||
| 817 | fn parseTuple(self: *@This(), T: type, node: Zoir.Node.Index) !T { | |
| 818 | const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) { | |
| 819 | .array_literal => |nodes| nodes, | |
| 820 | .empty_literal => .{ .start = node, .len = 0 }, | |
| 821 | else => return error.WrongType, | |
| 822 | }; | |
| 823 | ||
| 824 | var result: T = undefined; | |
| 825 | const field_infos = @typeInfo(T).@"struct".fields; | |
| 826 | ||
| 827 | if (nodes.len > field_infos.len) { | |
| 828 | return self.failNodeFmt( | |
| 829 | nodes.at(field_infos.len), | |
| 830 | "index {} outside of tuple length {}", | |
| 831 | .{ field_infos.len, field_infos.len }, | |
| 832 | ); | |
| 833 | } | |
| 834 | ||
| 835 | inline for (0..field_infos.len) |i| { | |
| 836 | // Check if we're out of bounds | |
| 837 | if (i >= nodes.len) { | |
| 838 | if (field_infos[i].default_value_ptr) |default| { | |
| 839 | const typed: *const field_infos[i].type = @ptrCast(@alignCast(default)); | |
| 840 | @field(result, field_infos[i].name) = typed.*; | |
| 841 | } else { | |
| 842 | return self.failNodeFmt(node, "missing tuple field with index {}", .{i}); | |
| 843 | } | |
| 844 | } else { | |
| 845 | // If we fail to parse this field, free all fields before it | |
| 846 | errdefer if (self.options.free_on_error) { | |
| 847 | inline for (0..i) |j| { | |
| 848 | if (j >= i) break; | |
| 849 | free(self.gpa, result[j]); | |
| 850 | } | |
| 851 | }; | |
| 852 | ||
| 853 | if (field_infos[i].is_comptime) { | |
| 854 | return self.failComptimeField(node, i); | |
| 855 | } else { | |
| 856 | result[i] = try self.parseExpr(field_infos[i].type, nodes.at(i)); | |
| 857 | } | |
| 858 | } | |
| 859 | } | |
| 860 | ||
| 861 | return result; | |
| 862 | } | |
| 863 | ||
| 864 | fn parseUnion(self: *@This(), T: type, node: Zoir.Node.Index) !T { | |
| 865 | const @"union" = @typeInfo(T).@"union"; | |
| 866 | const field_infos = @"union".fields; | |
| 867 | ||
| 868 | if (field_infos.len == 0) comptime unreachable; | |
| 869 | ||
| 870 | // Gather info on the fields | |
| 871 | const field_indices = b: { | |
| 872 | comptime var kvs_list: [field_infos.len]struct { []const u8, usize } = undefined; | |
| 873 | inline for (field_infos, 0..) |field, i| { | |
| 874 | kvs_list[i] = .{ field.name, i }; | |
| 875 | } | |
| 876 | break :b std.StaticStringMap(usize).initComptime(kvs_list); | |
| 877 | }; | |
| 878 | ||
| 879 | // Parse the union | |
| 880 | switch (node.get(self.zoir)) { | |
| 881 | .enum_literal => |field_name| { | |
| 882 | // The union must be tagged for an enum literal to coerce to it | |
| 883 | if (@"union".tag_type == null) { | |
| 884 | return error.WrongType; | |
| 885 | } | |
| 886 | ||
| 887 | // Get the index of the named field. We don't use `parseEnum` here as | |
| 888 | // the order of the enum and the order of the union might not match! | |
| 889 | const field_index = b: { | |
| 890 | const field_name_str = field_name.get(self.zoir); | |
| 891 | break :b field_indices.get(field_name_str) orelse | |
| 892 | return self.failUnexpected(T, "field", node, null, field_name_str); | |
| 893 | }; | |
| 894 | ||
| 895 | // Initialize the union from the given field. | |
| 896 | switch (field_index) { | |
| 897 | inline 0...field_infos.len - 1 => |i| { | |
| 898 | // Fail if the field is not void | |
| 899 | if (field_infos[i].type != void) | |
| 900 | return self.failNode(node, "expected union"); | |
| 901 | ||
| 902 | // Instantiate the union | |
| 903 | return @unionInit(T, field_infos[i].name, {}); | |
| 904 | }, | |
| 905 | else => unreachable, // Can't be out of bounds | |
| 906 | } | |
| 907 | }, | |
| 908 | .struct_literal => |struct_fields| { | |
| 909 | if (struct_fields.names.len != 1) { | |
| 910 | return error.WrongType; | |
| 911 | } | |
| 912 | ||
| 913 | // Fill in the field we found | |
| 914 | const field_name = struct_fields.names[0]; | |
| 915 | const field_name_str = field_name.get(self.zoir); | |
| 916 | const field_val = struct_fields.vals.at(0); | |
| 917 | const field_index = field_indices.get(field_name_str) orelse | |
| 918 | return self.failUnexpected(T, "field", node, 0, field_name_str); | |
| 919 | ||
| 920 | switch (field_index) { | |
| 921 | inline 0...field_infos.len - 1 => |i| { | |
| 922 | if (field_infos[i].type == void) { | |
| 923 | return self.failNode(field_val, "expected type 'void'"); | |
| 924 | } else { | |
| 925 | const value = try self.parseExpr(field_infos[i].type, field_val); | |
| 926 | return @unionInit(T, field_infos[i].name, value); | |
| 927 | } | |
| 928 | }, | |
| 929 | else => unreachable, // Can't be out of bounds | |
| 930 | } | |
| 931 | }, | |
| 932 | else => return error.WrongType, | |
| 933 | } | |
| 934 | } | |
| 935 | ||
| 936 | fn parseVector( | |
| 937 | self: *@This(), | |
| 938 | T: type, | |
| 939 | node: Zoir.Node.Index, | |
| 940 | ) !T { | |
| 941 | const vector_info = @typeInfo(T).vector; | |
| 942 | ||
| 943 | const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) { | |
| 944 | .array_literal => |nodes| nodes, | |
| 945 | .empty_literal => .{ .start = node, .len = 0 }, | |
| 946 | else => return error.WrongType, | |
| 947 | }; | |
| 948 | ||
| 949 | var result: T = undefined; | |
| 950 | ||
| 951 | if (nodes.len != vector_info.len) { | |
| 952 | return self.failNodeFmt( | |
| 953 | node, | |
| 954 | "expected {} vector elements; found {}", | |
| 955 | .{ vector_info.len, nodes.len }, | |
| 956 | ); | |
| 957 | } | |
| 958 | ||
| 959 | for (0..vector_info.len) |i| { | |
| 960 | errdefer for (0..i) |j| free(self.gpa, result[j]); | |
| 961 | result[i] = try self.parseExpr(vector_info.child, nodes.at(@intCast(i))); | |
| 962 | } | |
| 963 | ||
| 964 | return result; | |
| 965 | } | |
| 966 | ||
| 967 | fn failTokenFmt( | |
| 968 | self: @This(), | |
| 969 | token: Ast.TokenIndex, | |
| 970 | offset: u32, | |
| 971 | comptime fmt: []const u8, | |
| 972 | args: anytype, | |
| 973 | ) error{ OutOfMemory, ParseZon } { | |
| 974 | @branchHint(.cold); | |
| 975 | return self.failTokenFmtNote(token, offset, fmt, args, null); | |
| 976 | } | |
| 977 | ||
| 978 | fn failTokenFmtNote( | |
| 979 | self: @This(), | |
| 980 | token: Ast.TokenIndex, | |
| 981 | offset: u32, | |
| 982 | comptime fmt: []const u8, | |
| 983 | args: anytype, | |
| 984 | note: ?Error.TypeCheckFailure.Note, | |
| 985 | ) error{ OutOfMemory, ParseZon } { | |
| 986 | @branchHint(.cold); | |
| 987 | comptime assert(args.len > 0); | |
| 988 | if (self.status) |s| s.type_check = .{ | |
| 989 | .token = token, | |
| 990 | .offset = offset, | |
| 991 | .message = std.fmt.allocPrint(self.gpa, fmt, args) catch |err| { | |
| 992 | if (note) |n| n.deinit(self.gpa); | |
| 993 | return err; | |
| 994 | }, | |
| 995 | .owned = true, | |
| 996 | .note = note, | |
| 997 | }; | |
| 998 | return error.ParseZon; | |
| 999 | } | |
| 1000 | ||
| 1001 | fn failNodeFmt( | |
| 1002 | self: @This(), | |
| 1003 | node: Zoir.Node.Index, | |
| 1004 | comptime fmt: []const u8, | |
| 1005 | args: anytype, | |
| 1006 | ) error{ OutOfMemory, ParseZon } { | |
| 1007 | @branchHint(.cold); | |
| 1008 | const main_tokens = self.ast.nodes.items(.main_token); | |
| 1009 | const token = main_tokens[node.getAstNode(self.zoir)]; | |
| 1010 | return self.failTokenFmt(token, 0, fmt, args); | |
| 1011 | } | |
| 1012 | ||
| 1013 | fn failToken( | |
| 1014 | self: @This(), | |
| 1015 | failure: Error.TypeCheckFailure, | |
| 1016 | ) error{ParseZon} { | |
| 1017 | @branchHint(.cold); | |
| 1018 | if (self.status) |s| s.type_check = failure; | |
| 1019 | return error.ParseZon; | |
| 1020 | } | |
| 1021 | ||
| 1022 | fn failNode( | |
| 1023 | self: @This(), | |
| 1024 | node: Zoir.Node.Index, | |
| 1025 | message: []const u8, | |
| 1026 | ) error{ParseZon} { | |
| 1027 | @branchHint(.cold); | |
| 1028 | const main_tokens = self.ast.nodes.items(.main_token); | |
| 1029 | const token = main_tokens[node.getAstNode(self.zoir)]; | |
| 1030 | return self.failToken(.{ | |
| 1031 | .token = token, | |
| 1032 | .offset = 0, | |
| 1033 | .message = message, | |
| 1034 | .owned = false, | |
| 1035 | .note = null, | |
| 1036 | }); | |
| 1037 | } | |
| 1038 | ||
| 1039 | fn failCannotRepresent( | |
| 1040 | self: @This(), | |
| 1041 | T: type, | |
| 1042 | node: Zoir.Node.Index, | |
| 1043 | ) error{ OutOfMemory, ParseZon } { | |
| 1044 | @branchHint(.cold); | |
| 1045 | return self.failNodeFmt(node, "type '{s}' cannot represent value", .{@typeName(T)}); | |
| 1046 | } | |
| 1047 | ||
| 1048 | fn failUnexpected( | |
| 1049 | self: @This(), | |
| 1050 | T: type, | |
| 1051 | item_kind: []const u8, | |
| 1052 | node: Zoir.Node.Index, | |
| 1053 | field: ?usize, | |
| 1054 | name: []const u8, | |
| 1055 | ) error{ OutOfMemory, ParseZon } { | |
| 1056 | @branchHint(.cold); | |
| 1057 | const token = if (field) |f| b: { | |
| 1058 | var buf: [2]Ast.Node.Index = undefined; | |
| 1059 | const struct_init = self.ast.fullStructInit(&buf, node.getAstNode(self.zoir)).?; | |
| 1060 | const field_node = struct_init.ast.fields[f]; | |
| 1061 | break :b self.ast.firstToken(field_node) - 2; | |
| 1062 | } else b: { | |
| 1063 | const main_tokens = self.ast.nodes.items(.main_token); | |
| 1064 | break :b main_tokens[node.getAstNode(self.zoir)]; | |
| 1065 | }; | |
| 1066 | switch (@typeInfo(T)) { | |
| 1067 | inline .@"struct", .@"union", .@"enum" => |info| { | |
| 1068 | const note: Error.TypeCheckFailure.Note = if (info.fields.len == 0) b: { | |
| 1069 | break :b .{ | |
| 1070 | .token = token, | |
| 1071 | .offset = 0, | |
| 1072 | .msg = "none expected", | |
| 1073 | .owned = false, | |
| 1074 | }; | |
| 1075 | } else b: { | |
| 1076 | const msg = "supported: "; | |
| 1077 | var buf: std.ArrayListUnmanaged(u8) = try .initCapacity(self.gpa, 64); | |
| 1078 | defer buf.deinit(self.gpa); | |
| 1079 | const writer = buf.writer(self.gpa); | |
| 1080 | try writer.writeAll(msg); | |
| 1081 | inline for (info.fields, 0..) |field_info, i| { | |
| 1082 | if (i != 0) try writer.writeAll(", "); | |
| 1083 | try writer.print("'{p_}'", .{std.zig.fmtId(field_info.name)}); | |
| 1084 | } | |
| 1085 | break :b .{ | |
| 1086 | .token = token, | |
| 1087 | .offset = 0, | |
| 1088 | .msg = try buf.toOwnedSlice(self.gpa), | |
| 1089 | .owned = true, | |
| 1090 | }; | |
| 1091 | }; | |
| 1092 | return self.failTokenFmtNote( | |
| 1093 | token, | |
| 1094 | 0, | |
| 1095 | "unexpected {s} '{s}'", | |
| 1096 | .{ item_kind, name }, | |
| 1097 | note, | |
| 1098 | ); | |
| 1099 | }, | |
| 1100 | else => comptime unreachable, | |
| 1101 | } | |
| 1102 | } | |
| 1103 | ||
| 1104 | // Technically we could do this if we were willing to do a deep equal to verify | |
| 1105 | // the value matched, but doing so doesn't seem to support any real use cases | |
| 1106 | // so isn't worth the complexity at the moment. | |
| 1107 | fn failComptimeField( | |
| 1108 | self: @This(), | |
| 1109 | node: Zoir.Node.Index, | |
| 1110 | field: usize, | |
| 1111 | ) error{ OutOfMemory, ParseZon } { | |
| 1112 | @branchHint(.cold); | |
| 1113 | const ast_node = node.getAstNode(self.zoir); | |
| 1114 | var buf: [2]Ast.Node.Index = undefined; | |
| 1115 | const token = if (self.ast.fullStructInit(&buf, ast_node)) |struct_init| b: { | |
| 1116 | const field_node = struct_init.ast.fields[field]; | |
| 1117 | break :b self.ast.firstToken(field_node); | |
| 1118 | } else b: { | |
| 1119 | const array_init = self.ast.fullArrayInit(&buf, ast_node).?; | |
| 1120 | const value_node = array_init.ast.elements[field]; | |
| 1121 | break :b self.ast.firstToken(value_node); | |
| 1122 | }; | |
| 1123 | return self.failToken(.{ | |
| 1124 | .token = token, | |
| 1125 | .offset = 0, | |
| 1126 | .message = "cannot initialize comptime field", | |
| 1127 | .owned = false, | |
| 1128 | .note = null, | |
| 1129 | }); | |
| 1130 | } | |
| 1131 | }; | |
| 1132 | ||
| 1133 | fn intFromFloatExact(T: type, value: anytype) ?T { | |
| 1134 | if (value > std.math.maxInt(T) or value < std.math.minInt(T)) { | |
| 1135 | return null; | |
| 1136 | } | |
| 1137 | ||
| 1138 | if (std.math.isNan(value) or std.math.trunc(value) != value) { | |
| 1139 | return null; | |
| 1140 | } | |
| 1141 | ||
| 1142 | return @intFromFloat(value); | |
| 1143 | } | |
| 1144 | ||
| 1145 | fn canParseType(T: type) bool { | |
| 1146 | comptime return canParseTypeInner(T, &.{}, false); | |
| 1147 | } | |
| 1148 | ||
| 1149 | fn canParseTypeInner( | |
| 1150 | T: type, | |
| 1151 | /// Visited structs and unions, to avoid infinite recursion. | |
| 1152 | /// Tracking more types is unnecessary, and a little complex due to optional nesting. | |
| 1153 | visited: []const type, | |
| 1154 | parent_is_optional: bool, | |
| 1155 | ) bool { | |
| 1156 | return switch (@typeInfo(T)) { | |
| 1157 | .bool, | |
| 1158 | .int, | |
| 1159 | .float, | |
| 1160 | .null, | |
| 1161 | .@"enum", | |
| 1162 | => true, | |
| 1163 | ||
| 1164 | .noreturn, | |
| 1165 | .void, | |
| 1166 | .type, | |
| 1167 | .undefined, | |
| 1168 | .error_union, | |
| 1169 | .error_set, | |
| 1170 | .@"fn", | |
| 1171 | .frame, | |
| 1172 | .@"anyframe", | |
| 1173 | .@"opaque", | |
| 1174 | .comptime_int, | |
| 1175 | .comptime_float, | |
| 1176 | .enum_literal, | |
| 1177 | => false, | |
| 1178 | ||
| 1179 | .pointer => |pointer| switch (pointer.size) { | |
| 1180 | .one => canParseTypeInner(pointer.child, visited, parent_is_optional), | |
| 1181 | .slice => canParseTypeInner(pointer.child, visited, false), | |
| 1182 | .many, .c => false, | |
| 1183 | }, | |
| 1184 | ||
| 1185 | .optional => |optional| if (parent_is_optional) | |
| 1186 | false | |
| 1187 | else | |
| 1188 | canParseTypeInner(optional.child, visited, true), | |
| 1189 | ||
| 1190 | .array => |array| canParseTypeInner(array.child, visited, false), | |
| 1191 | .vector => |vector| canParseTypeInner(vector.child, visited, false), | |
| 1192 | ||
| 1193 | .@"struct" => |@"struct"| { | |
| 1194 | for (visited) |V| if (T == V) return true; | |
| 1195 | const new_visited = visited ++ .{T}; | |
| 1196 | for (@"struct".fields) |field| { | |
| 1197 | if (!field.is_comptime and !canParseTypeInner(field.type, new_visited, false)) { | |
| 1198 | return false; | |
| 1199 | } | |
| 1200 | } | |
| 1201 | return true; | |
| 1202 | }, | |
| 1203 | .@"union" => |@"union"| { | |
| 1204 | for (visited) |V| if (T == V) return true; | |
| 1205 | const new_visited = visited ++ .{T}; | |
| 1206 | for (@"union".fields) |field| { | |
| 1207 | if (field.type != void and !canParseTypeInner(field.type, new_visited, false)) { | |
| 1208 | return false; | |
| 1209 | } | |
| 1210 | } | |
| 1211 | return true; | |
| 1212 | }, | |
| 1213 | }; | |
| 1214 | } | |
| 1215 | ||
| 1216 | test "std.zon parse canParseType" { | |
| 1217 | try std.testing.expect(!comptime canParseType(void)); | |
| 1218 | try std.testing.expect(!comptime canParseType(struct { f: [*]u8 })); | |
| 1219 | try std.testing.expect(!comptime canParseType(struct { error{foo} })); | |
| 1220 | try std.testing.expect(!comptime canParseType(union(enum) { a: void, b: [*c]u8 })); | |
| 1221 | try std.testing.expect(!comptime canParseType(@Vector(0, [*c]u8))); | |
| 1222 | try std.testing.expect(!comptime canParseType(*?[*c]u8)); | |
| 1223 | try std.testing.expect(comptime canParseType(enum(u8) { _ })); | |
| 1224 | try std.testing.expect(comptime canParseType(union { foo: void })); | |
| 1225 | try std.testing.expect(comptime canParseType(union(enum) { foo: void })); | |
| 1226 | try std.testing.expect(!comptime canParseType(comptime_float)); | |
| 1227 | try std.testing.expect(!comptime canParseType(comptime_int)); | |
| 1228 | try std.testing.expect(comptime canParseType(struct { comptime foo: ??u8 = null })); | |
| 1229 | try std.testing.expect(!comptime canParseType(@TypeOf(.foo))); | |
| 1230 | try std.testing.expect(comptime canParseType(?u8)); | |
| 1231 | try std.testing.expect(comptime canParseType(*?*u8)); | |
| 1232 | try std.testing.expect(comptime canParseType(?struct { | |
| 1233 | foo: ?struct { | |
| 1234 | ?union(enum) { | |
| 1235 | a: ?@Vector(0, ?*u8), | |
| 1236 | }, | |
| 1237 | ?struct { | |
| 1238 | f: ?[]?u8, | |
| 1239 | }, | |
| 1240 | }, | |
| 1241 | })); | |
| 1242 | try std.testing.expect(!comptime canParseType(??u8)); | |
| 1243 | try std.testing.expect(!comptime canParseType(?*?u8)); | |
| 1244 | try std.testing.expect(!comptime canParseType(*?*?*u8)); | |
| 1245 | try std.testing.expect(!comptime canParseType(struct { x: comptime_int = 2 })); | |
| 1246 | try std.testing.expect(!comptime canParseType(struct { x: comptime_float = 2 })); | |
| 1247 | try std.testing.expect(comptime canParseType(struct { comptime x: @TypeOf(.foo) = .foo })); | |
| 1248 | try std.testing.expect(!comptime canParseType(struct { comptime_int })); | |
| 1249 | const Recursive = struct { foo: ?*@This() }; | |
| 1250 | try std.testing.expect(comptime canParseType(Recursive)); | |
| 1251 | ||
| 1252 | // Make sure we validate nested optional before we early out due to already having seen | |
| 1253 | // a type recursion! | |
| 1254 | try std.testing.expect(!comptime canParseType(struct { | |
| 1255 | add_to_visited: ?u8, | |
| 1256 | retrieve_from_visited: ??u8, | |
| 1257 | })); | |
| 1258 | } | |
| 1259 | ||
| 1260 | test "std.zon requiresAllocator" { | |
| 1261 | try std.testing.expect(!requiresAllocator(u8)); | |
| 1262 | try std.testing.expect(!requiresAllocator(f32)); | |
| 1263 | try std.testing.expect(!requiresAllocator(enum { foo })); | |
| 1264 | try std.testing.expect(!requiresAllocator(struct { f32 })); | |
| 1265 | try std.testing.expect(!requiresAllocator(struct { x: f32 })); | |
| 1266 | try std.testing.expect(!requiresAllocator([0][]const u8)); | |
| 1267 | try std.testing.expect(!requiresAllocator([2]u8)); | |
| 1268 | try std.testing.expect(!requiresAllocator(union { x: f32, y: f32 })); | |
| 1269 | try std.testing.expect(!requiresAllocator(union(enum) { x: f32, y: f32 })); | |
| 1270 | try std.testing.expect(!requiresAllocator(?f32)); | |
| 1271 | try std.testing.expect(!requiresAllocator(void)); | |
| 1272 | try std.testing.expect(!requiresAllocator(@TypeOf(null))); | |
| 1273 | try std.testing.expect(!requiresAllocator(@Vector(3, u8))); | |
| 1274 | try std.testing.expect(!requiresAllocator(@Vector(0, *const u8))); | |
| 1275 | ||
| 1276 | try std.testing.expect(requiresAllocator([]u8)); | |
| 1277 | try std.testing.expect(requiresAllocator(*struct { u8, u8 })); | |
| 1278 | try std.testing.expect(requiresAllocator([1][]const u8)); | |
| 1279 | try std.testing.expect(requiresAllocator(struct { x: i32, y: []u8 })); | |
| 1280 | try std.testing.expect(requiresAllocator(union { x: i32, y: []u8 })); | |
| 1281 | try std.testing.expect(requiresAllocator(union(enum) { x: i32, y: []u8 })); | |
| 1282 | try std.testing.expect(requiresAllocator(?[]u8)); | |
| 1283 | try std.testing.expect(requiresAllocator(@Vector(3, *const u8))); | |
| 1284 | } | |
| 1285 | ||
| 1286 | test "std.zon ast errors" { | |
| 1287 | const gpa = std.testing.allocator; | |
| 1288 | var status: Status = .{}; | |
| 1289 | defer status.deinit(gpa); | |
| 1290 | try std.testing.expectError( | |
| 1291 | error.ParseZon, | |
| 1292 | fromSlice(struct {}, gpa, ".{.x = 1 .y = 2}", &status, .{}), | |
| 1293 | ); | |
| 1294 | try std.testing.expectFmt("1:13: error: expected ',' after initializer\n", "{}", .{status}); | |
| 1295 | } | |
| 1296 | ||
| 1297 | test "std.zon comments" { | |
| 1298 | const gpa = std.testing.allocator; | |
| 1299 | ||
| 1300 | try std.testing.expectEqual(@as(u8, 10), fromSlice(u8, gpa, | |
| 1301 | \\// comment | |
| 1302 | \\10 // comment | |
| 1303 | \\// comment | |
| 1304 | , null, .{})); | |
| 1305 | ||
| 1306 | { | |
| 1307 | var status: Status = .{}; | |
| 1308 | defer status.deinit(gpa); | |
| 1309 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, | |
| 1310 | \\//! comment | |
| 1311 | \\10 // comment | |
| 1312 | \\// comment | |
| 1313 | , &status, .{})); | |
| 1314 | try std.testing.expectFmt( | |
| 1315 | "1:1: error: expected expression, found 'a document comment'\n", | |
| 1316 | "{}", | |
| 1317 | .{status}, | |
| 1318 | ); | |
| 1319 | } | |
| 1320 | } | |
| 1321 | ||
| 1322 | test "std.zon failure/oom formatting" { | |
| 1323 | const gpa = std.testing.allocator; | |
| 1324 | var failing_allocator = std.testing.FailingAllocator.init(gpa, .{ | |
| 1325 | .fail_index = 0, | |
| 1326 | .resize_fail_index = 0, | |
| 1327 | }); | |
| 1328 | var status: Status = .{}; | |
| 1329 | defer status.deinit(gpa); | |
| 1330 | try std.testing.expectError(error.OutOfMemory, fromSlice( | |
| 1331 | []const u8, | |
| 1332 | failing_allocator.allocator(), | |
| 1333 | "\"foo\"", | |
| 1334 | &status, | |
| 1335 | .{}, | |
| 1336 | )); | |
| 1337 | try std.testing.expectFmt("", "{}", .{status}); | |
| 1338 | } | |
| 1339 | ||
| 1340 | test "std.zon fromSlice syntax error" { | |
| 1341 | try std.testing.expectError( | |
| 1342 | error.ParseZon, | |
| 1343 | fromSlice(u8, std.testing.allocator, ".{", null, .{}), | |
| 1344 | ); | |
| 1345 | } | |
| 1346 | ||
| 1347 | test "std.zon optional" { | |
| 1348 | const gpa = std.testing.allocator; | |
| 1349 | ||
| 1350 | // Basic usage | |
| 1351 | { | |
| 1352 | const none = try fromSlice(?u32, gpa, "null", null, .{}); | |
| 1353 | try std.testing.expect(none == null); | |
| 1354 | const some = try fromSlice(?u32, gpa, "1", null, .{}); | |
| 1355 | try std.testing.expect(some.? == 1); | |
| 1356 | } | |
| 1357 | ||
| 1358 | // Deep free | |
| 1359 | { | |
| 1360 | const none = try fromSlice(?[]const u8, gpa, "null", null, .{}); | |
| 1361 | try std.testing.expect(none == null); | |
| 1362 | const some = try fromSlice(?[]const u8, gpa, "\"foo\"", null, .{}); | |
| 1363 | defer free(gpa, some); | |
| 1364 | try std.testing.expectEqualStrings("foo", some.?); | |
| 1365 | } | |
| 1366 | } | |
| 1367 | ||
| 1368 | test "std.zon unions" { | |
| 1369 | const gpa = std.testing.allocator; | |
| 1370 | ||
| 1371 | // Unions | |
| 1372 | { | |
| 1373 | const Tagged = union(enum) { x: f32, @"y y": bool, z, @"z z" }; | |
| 1374 | const Untagged = union { x: f32, @"y y": bool, z: void, @"z z": void }; | |
| 1375 | ||
| 1376 | const tagged_x = try fromSlice(Tagged, gpa, ".{.x = 1.5}", null, .{}); | |
| 1377 | try std.testing.expectEqual(Tagged{ .x = 1.5 }, tagged_x); | |
| 1378 | const tagged_y = try fromSlice(Tagged, gpa, ".{.@\"y y\" = true}", null, .{}); | |
| 1379 | try std.testing.expectEqual(Tagged{ .@"y y" = true }, tagged_y); | |
| 1380 | const tagged_z_shorthand = try fromSlice(Tagged, gpa, ".z", null, .{}); | |
| 1381 | try std.testing.expectEqual(@as(Tagged, .z), tagged_z_shorthand); | |
| 1382 | const tagged_zz_shorthand = try fromSlice(Tagged, gpa, ".@\"z z\"", null, .{}); | |
| 1383 | try std.testing.expectEqual(@as(Tagged, .@"z z"), tagged_zz_shorthand); | |
| 1384 | ||
| 1385 | const untagged_x = try fromSlice(Untagged, gpa, ".{.x = 1.5}", null, .{}); | |
| 1386 | try std.testing.expect(untagged_x.x == 1.5); | |
| 1387 | const untagged_y = try fromSlice(Untagged, gpa, ".{.@\"y y\" = true}", null, .{}); | |
| 1388 | try std.testing.expect(untagged_y.@"y y"); | |
| 1389 | } | |
| 1390 | ||
| 1391 | // Deep free | |
| 1392 | { | |
| 1393 | const Union = union(enum) { bar: []const u8, baz: bool }; | |
| 1394 | ||
| 1395 | const noalloc = try fromSlice(Union, gpa, ".{.baz = false}", null, .{}); | |
| 1396 | try std.testing.expectEqual(Union{ .baz = false }, noalloc); | |
| 1397 | ||
| 1398 | const alloc = try fromSlice(Union, gpa, ".{.bar = \"qux\"}", null, .{}); | |
| 1399 | defer free(gpa, alloc); | |
| 1400 | try std.testing.expectEqualDeep(Union{ .bar = "qux" }, alloc); | |
| 1401 | } | |
| 1402 | ||
| 1403 | // Unknown field | |
| 1404 | { | |
| 1405 | const Union = union { x: f32, y: f32 }; | |
| 1406 | var status: Status = .{}; | |
| 1407 | defer status.deinit(gpa); | |
| 1408 | try std.testing.expectError( | |
| 1409 | error.ParseZon, | |
| 1410 | fromSlice(Union, gpa, ".{.z=2.5}", &status, .{}), | |
| 1411 | ); | |
| 1412 | try std.testing.expectFmt( | |
| 1413 | \\1:4: error: unexpected field 'z' | |
| 1414 | \\1:4: note: supported: 'x', 'y' | |
| 1415 | \\ | |
| 1416 | , | |
| 1417 | "{}", | |
| 1418 | .{status}, | |
| 1419 | ); | |
| 1420 | } | |
| 1421 | ||
| 1422 | // Explicit void field | |
| 1423 | { | |
| 1424 | const Union = union(enum) { x: void }; | |
| 1425 | var status: Status = .{}; | |
| 1426 | defer status.deinit(gpa); | |
| 1427 | try std.testing.expectError( | |
| 1428 | error.ParseZon, | |
| 1429 | fromSlice(Union, gpa, ".{.x=1}", &status, .{}), | |
| 1430 | ); | |
| 1431 | try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{}", .{status}); | |
| 1432 | } | |
| 1433 | ||
| 1434 | // Extra field | |
| 1435 | { | |
| 1436 | const Union = union { x: f32, y: bool }; | |
| 1437 | var status: Status = .{}; | |
| 1438 | defer status.deinit(gpa); | |
| 1439 | try std.testing.expectError( | |
| 1440 | error.ParseZon, | |
| 1441 | fromSlice(Union, gpa, ".{.x = 1.5, .y = true}", &status, .{}), | |
| 1442 | ); | |
| 1443 | try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{status}); | |
| 1444 | } | |
| 1445 | ||
| 1446 | // No fields | |
| 1447 | { | |
| 1448 | const Union = union { x: f32, y: bool }; | |
| 1449 | var status: Status = .{}; | |
| 1450 | defer status.deinit(gpa); | |
| 1451 | try std.testing.expectError( | |
| 1452 | error.ParseZon, | |
| 1453 | fromSlice(Union, gpa, ".{}", &status, .{}), | |
| 1454 | ); | |
| 1455 | try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{status}); | |
| 1456 | } | |
| 1457 | ||
| 1458 | // Enum literals cannot coerce into untagged unions | |
| 1459 | { | |
| 1460 | const Union = union { x: void }; | |
| 1461 | var status: Status = .{}; | |
| 1462 | defer status.deinit(gpa); | |
| 1463 | try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".x", &status, .{})); | |
| 1464 | try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{status}); | |
| 1465 | } | |
| 1466 | ||
| 1467 | // Unknown field for enum literal coercion | |
| 1468 | { | |
| 1469 | const Union = union(enum) { x: void }; | |
| 1470 | var status: Status = .{}; | |
| 1471 | defer status.deinit(gpa); | |
| 1472 | try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".y", &status, .{})); | |
| 1473 | try std.testing.expectFmt( | |
| 1474 | \\1:2: error: unexpected field 'y' | |
| 1475 | \\1:2: note: supported: 'x' | |
| 1476 | \\ | |
| 1477 | , | |
| 1478 | "{}", | |
| 1479 | .{status}, | |
| 1480 | ); | |
| 1481 | } | |
| 1482 | ||
| 1483 | // Non void field for enum literal coercion | |
| 1484 | { | |
| 1485 | const Union = union(enum) { x: f32 }; | |
| 1486 | var status: Status = .{}; | |
| 1487 | defer status.deinit(gpa); | |
| 1488 | try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".x", &status, .{})); | |
| 1489 | try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{status}); | |
| 1490 | } | |
| 1491 | } | |
| 1492 | ||
| 1493 | test "std.zon structs" { | |
| 1494 | const gpa = std.testing.allocator; | |
| 1495 | ||
| 1496 | // Structs (various sizes tested since they're parsed differently) | |
| 1497 | { | |
| 1498 | const Vec0 = struct {}; | |
| 1499 | const Vec1 = struct { x: f32 }; | |
| 1500 | const Vec2 = struct { x: f32, y: f32 }; | |
| 1501 | const Vec3 = struct { x: f32, y: f32, z: f32 }; | |
| 1502 | ||
| 1503 | const zero = try fromSlice(Vec0, gpa, ".{}", null, .{}); | |
| 1504 | try std.testing.expectEqual(Vec0{}, zero); | |
| 1505 | ||
| 1506 | const one = try fromSlice(Vec1, gpa, ".{.x = 1.2}", null, .{}); | |
| 1507 | try std.testing.expectEqual(Vec1{ .x = 1.2 }, one); | |
| 1508 | ||
| 1509 | const two = try fromSlice(Vec2, gpa, ".{.x = 1.2, .y = 3.4}", null, .{}); | |
| 1510 | try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 3.4 }, two); | |
| 1511 | ||
| 1512 | const three = try fromSlice(Vec3, gpa, ".{.x = 1.2, .y = 3.4, .z = 5.6}", null, .{}); | |
| 1513 | try std.testing.expectEqual(Vec3{ .x = 1.2, .y = 3.4, .z = 5.6 }, three); | |
| 1514 | } | |
| 1515 | ||
| 1516 | // Deep free (structs and arrays) | |
| 1517 | { | |
| 1518 | const Foo = struct { bar: []const u8, baz: []const []const u8 }; | |
| 1519 | ||
| 1520 | const parsed = try fromSlice( | |
| 1521 | Foo, | |
| 1522 | gpa, | |
| 1523 | ".{.bar = \"qux\", .baz = .{\"a\", \"b\"}}", | |
| 1524 | null, | |
| 1525 | .{}, | |
| 1526 | ); | |
| 1527 | defer free(gpa, parsed); | |
| 1528 | try std.testing.expectEqualDeep(Foo{ .bar = "qux", .baz = &.{ "a", "b" } }, parsed); | |
| 1529 | } | |
| 1530 | ||
| 1531 | // Unknown field | |
| 1532 | { | |
| 1533 | const Vec2 = struct { x: f32, y: f32 }; | |
| 1534 | var status: Status = .{}; | |
| 1535 | defer status.deinit(gpa); | |
| 1536 | try std.testing.expectError( | |
| 1537 | error.ParseZon, | |
| 1538 | fromSlice(Vec2, gpa, ".{.x=1.5, .z=2.5}", &status, .{}), | |
| 1539 | ); | |
| 1540 | try std.testing.expectFmt( | |
| 1541 | \\1:12: error: unexpected field 'z' | |
| 1542 | \\1:12: note: supported: 'x', 'y' | |
| 1543 | \\ | |
| 1544 | , | |
| 1545 | "{}", | |
| 1546 | .{status}, | |
| 1547 | ); | |
| 1548 | } | |
| 1549 | ||
| 1550 | // Duplicate field | |
| 1551 | { | |
| 1552 | const Vec2 = struct { x: f32, y: f32 }; | |
| 1553 | var status: Status = .{}; | |
| 1554 | defer status.deinit(gpa); | |
| 1555 | try std.testing.expectError( | |
| 1556 | error.ParseZon, | |
| 1557 | fromSlice(Vec2, gpa, ".{.x=1.5, .x=2.5, .x=3.5}", &status, .{}), | |
| 1558 | ); | |
| 1559 | try std.testing.expectFmt( | |
| 1560 | \\1:4: error: duplicate struct field name | |
| 1561 | \\1:12: note: duplicate name here | |
| 1562 | \\ | |
| 1563 | , "{}", .{status}); | |
| 1564 | } | |
| 1565 | ||
| 1566 | // Ignore unknown fields | |
| 1567 | { | |
| 1568 | const Vec2 = struct { x: f32, y: f32 = 2.0 }; | |
| 1569 | const parsed = try fromSlice(Vec2, gpa, ".{ .x = 1.0, .z = 3.0 }", null, .{ | |
| 1570 | .ignore_unknown_fields = true, | |
| 1571 | }); | |
| 1572 | try std.testing.expectEqual(Vec2{ .x = 1.0, .y = 2.0 }, parsed); | |
| 1573 | } | |
| 1574 | ||
| 1575 | // Unknown field when struct has no fields (regression test) | |
| 1576 | { | |
| 1577 | const Vec2 = struct {}; | |
| 1578 | var status: Status = .{}; | |
| 1579 | defer status.deinit(gpa); | |
| 1580 | try std.testing.expectError( | |
| 1581 | error.ParseZon, | |
| 1582 | fromSlice(Vec2, gpa, ".{.x=1.5, .z=2.5}", &status, .{}), | |
| 1583 | ); | |
| 1584 | try std.testing.expectFmt( | |
| 1585 | \\1:4: error: unexpected field 'x' | |
| 1586 | \\1:4: note: none expected | |
| 1587 | \\ | |
| 1588 | , "{}", .{status}); | |
| 1589 | } | |
| 1590 | ||
| 1591 | // Missing field | |
| 1592 | { | |
| 1593 | const Vec2 = struct { x: f32, y: f32 }; | |
| 1594 | var status: Status = .{}; | |
| 1595 | defer status.deinit(gpa); | |
| 1596 | try std.testing.expectError( | |
| 1597 | error.ParseZon, | |
| 1598 | fromSlice(Vec2, gpa, ".{.x=1.5}", &status, .{}), | |
| 1599 | ); | |
| 1600 | try std.testing.expectFmt("1:2: error: missing required field y\n", "{}", .{status}); | |
| 1601 | } | |
| 1602 | ||
| 1603 | // Default field | |
| 1604 | { | |
| 1605 | const Vec2 = struct { x: f32, y: f32 = 1.5 }; | |
| 1606 | const parsed = try fromSlice(Vec2, gpa, ".{.x = 1.2}", null, .{}); | |
| 1607 | try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed); | |
| 1608 | } | |
| 1609 | ||
| 1610 | // Comptime field | |
| 1611 | { | |
| 1612 | const Vec2 = struct { x: f32, comptime y: f32 = 1.5 }; | |
| 1613 | const parsed = try fromSlice(Vec2, gpa, ".{.x = 1.2}", null, .{}); | |
| 1614 | try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed); | |
| 1615 | } | |
| 1616 | ||
| 1617 | // Comptime field assignment | |
| 1618 | { | |
| 1619 | const Vec2 = struct { x: f32, comptime y: f32 = 1.5 }; | |
| 1620 | var status: Status = .{}; | |
| 1621 | defer status.deinit(gpa); | |
| 1622 | const parsed = fromSlice(Vec2, gpa, ".{.x = 1.2, .y = 1.5}", &status, .{}); | |
| 1623 | try std.testing.expectError(error.ParseZon, parsed); | |
| 1624 | try std.testing.expectFmt( | |
| 1625 | \\1:18: error: cannot initialize comptime field | |
| 1626 | \\ | |
| 1627 | , "{}", .{status}); | |
| 1628 | } | |
| 1629 | ||
| 1630 | // Enum field (regression test, we were previously getting the field name in an | |
| 1631 | // incorrect way that broke for enum values) | |
| 1632 | { | |
| 1633 | const Vec0 = struct { x: enum { x } }; | |
| 1634 | const parsed = try fromSlice(Vec0, gpa, ".{ .x = .x }", null, .{}); | |
| 1635 | try std.testing.expectEqual(Vec0{ .x = .x }, parsed); | |
| 1636 | } | |
| 1637 | ||
| 1638 | // Enum field and struct field with @ | |
| 1639 | { | |
| 1640 | const Vec0 = struct { @"x x": enum { @"x x" } }; | |
| 1641 | const parsed = try fromSlice(Vec0, gpa, ".{ .@\"x x\" = .@\"x x\" }", null, .{}); | |
| 1642 | try std.testing.expectEqual(Vec0{ .@"x x" = .@"x x" }, parsed); | |
| 1643 | } | |
| 1644 | ||
| 1645 | // Type expressions are not allowed | |
| 1646 | { | |
| 1647 | // Structs | |
| 1648 | { | |
| 1649 | var status: Status = .{}; | |
| 1650 | defer status.deinit(gpa); | |
| 1651 | const parsed = fromSlice(struct {}, gpa, "Empty{}", &status, .{}); | |
| 1652 | try std.testing.expectError(error.ParseZon, parsed); | |
| 1653 | try std.testing.expectFmt( | |
| 1654 | \\1:1: error: types are not available in ZON | |
| 1655 | \\1:1: note: replace the type with '.' | |
| 1656 | \\ | |
| 1657 | , "{}", .{status}); | |
| 1658 | } | |
| 1659 | ||
| 1660 | // Arrays | |
| 1661 | { | |
| 1662 | var status: Status = .{}; | |
| 1663 | defer status.deinit(gpa); | |
| 1664 | const parsed = fromSlice([3]u8, gpa, "[3]u8{1, 2, 3}", &status, .{}); | |
| 1665 | try std.testing.expectError(error.ParseZon, parsed); | |
| 1666 | try std.testing.expectFmt( | |
| 1667 | \\1:1: error: types are not available in ZON | |
| 1668 | \\1:1: note: replace the type with '.' | |
| 1669 | \\ | |
| 1670 | , "{}", .{status}); | |
| 1671 | } | |
| 1672 | ||
| 1673 | // Slices | |
| 1674 | { | |
| 1675 | var status: Status = .{}; | |
| 1676 | defer status.deinit(gpa); | |
| 1677 | const parsed = fromSlice([]u8, gpa, "[]u8{1, 2, 3}", &status, .{}); | |
| 1678 | try std.testing.expectError(error.ParseZon, parsed); | |
| 1679 | try std.testing.expectFmt( | |
| 1680 | \\1:1: error: types are not available in ZON | |
| 1681 | \\1:1: note: replace the type with '.' | |
| 1682 | \\ | |
| 1683 | , "{}", .{status}); | |
| 1684 | } | |
| 1685 | ||
| 1686 | // Tuples | |
| 1687 | { | |
| 1688 | var status: Status = .{}; | |
| 1689 | defer status.deinit(gpa); | |
| 1690 | const parsed = fromSlice( | |
| 1691 | struct { u8, u8, u8 }, | |
| 1692 | gpa, | |
| 1693 | "Tuple{1, 2, 3}", | |
| 1694 | &status, | |
| 1695 | .{}, | |
| 1696 | ); | |
| 1697 | try std.testing.expectError(error.ParseZon, parsed); | |
| 1698 | try std.testing.expectFmt( | |
| 1699 | \\1:1: error: types are not available in ZON | |
| 1700 | \\1:1: note: replace the type with '.' | |
| 1701 | \\ | |
| 1702 | , "{}", .{status}); | |
| 1703 | } | |
| 1704 | ||
| 1705 | // Nested | |
| 1706 | { | |
| 1707 | var status: Status = .{}; | |
| 1708 | defer status.deinit(gpa); | |
| 1709 | const parsed = fromSlice(struct {}, gpa, ".{ .x = Tuple{1, 2, 3} }", &status, .{}); | |
| 1710 | try std.testing.expectError(error.ParseZon, parsed); | |
| 1711 | try std.testing.expectFmt( | |
| 1712 | \\1:9: error: types are not available in ZON | |
| 1713 | \\1:9: note: replace the type with '.' | |
| 1714 | \\ | |
| 1715 | , "{}", .{status}); | |
| 1716 | } | |
| 1717 | } | |
| 1718 | } | |
| 1719 | ||
| 1720 | test "std.zon tuples" { | |
| 1721 | const gpa = std.testing.allocator; | |
| 1722 | ||
| 1723 | // Structs (various sizes tested since they're parsed differently) | |
| 1724 | { | |
| 1725 | const Tuple0 = struct {}; | |
| 1726 | const Tuple1 = struct { f32 }; | |
| 1727 | const Tuple2 = struct { f32, bool }; | |
| 1728 | const Tuple3 = struct { f32, bool, u8 }; | |
| 1729 | ||
| 1730 | const zero = try fromSlice(Tuple0, gpa, ".{}", null, .{}); | |
| 1731 | try std.testing.expectEqual(Tuple0{}, zero); | |
| 1732 | ||
| 1733 | const one = try fromSlice(Tuple1, gpa, ".{1.2}", null, .{}); | |
| 1734 | try std.testing.expectEqual(Tuple1{1.2}, one); | |
| 1735 | ||
| 1736 | const two = try fromSlice(Tuple2, gpa, ".{1.2, true}", null, .{}); | |
| 1737 | try std.testing.expectEqual(Tuple2{ 1.2, true }, two); | |
| 1738 | ||
| 1739 | const three = try fromSlice(Tuple3, gpa, ".{1.2, false, 3}", null, .{}); | |
| 1740 | try std.testing.expectEqual(Tuple3{ 1.2, false, 3 }, three); | |
| 1741 | } | |
| 1742 | ||
| 1743 | // Deep free | |
| 1744 | { | |
| 1745 | const Tuple = struct { []const u8, []const u8 }; | |
| 1746 | const parsed = try fromSlice(Tuple, gpa, ".{\"hello\", \"world\"}", null, .{}); | |
| 1747 | defer free(gpa, parsed); | |
| 1748 | try std.testing.expectEqualDeep(Tuple{ "hello", "world" }, parsed); | |
| 1749 | } | |
| 1750 | ||
| 1751 | // Extra field | |
| 1752 | { | |
| 1753 | const Tuple = struct { f32, bool }; | |
| 1754 | var status: Status = .{}; | |
| 1755 | defer status.deinit(gpa); | |
| 1756 | try std.testing.expectError( | |
| 1757 | error.ParseZon, | |
| 1758 | fromSlice(Tuple, gpa, ".{0.5, true, 123}", &status, .{}), | |
| 1759 | ); | |
| 1760 | try std.testing.expectFmt("1:14: error: index 2 outside of tuple length 2\n", "{}", .{status}); | |
| 1761 | } | |
| 1762 | ||
| 1763 | // Extra field | |
| 1764 | { | |
| 1765 | const Tuple = struct { f32, bool }; | |
| 1766 | var status: Status = .{}; | |
| 1767 | defer status.deinit(gpa); | |
| 1768 | try std.testing.expectError( | |
| 1769 | error.ParseZon, | |
| 1770 | fromSlice(Tuple, gpa, ".{0.5}", &status, .{}), | |
| 1771 | ); | |
| 1772 | try std.testing.expectFmt( | |
| 1773 | "1:2: error: missing tuple field with index 1\n", | |
| 1774 | "{}", | |
| 1775 | .{status}, | |
| 1776 | ); | |
| 1777 | } | |
| 1778 | ||
| 1779 | // Tuple with unexpected field names | |
| 1780 | { | |
| 1781 | const Tuple = struct { f32 }; | |
| 1782 | var status: Status = .{}; | |
| 1783 | defer status.deinit(gpa); | |
| 1784 | try std.testing.expectError( | |
| 1785 | error.ParseZon, | |
| 1786 | fromSlice(Tuple, gpa, ".{.foo = 10.0}", &status, .{}), | |
| 1787 | ); | |
| 1788 | try std.testing.expectFmt("1:2: error: expected tuple\n", "{}", .{status}); | |
| 1789 | } | |
| 1790 | ||
| 1791 | // Struct with missing field names | |
| 1792 | { | |
| 1793 | const Struct = struct { foo: f32 }; | |
| 1794 | var status: Status = .{}; | |
| 1795 | defer status.deinit(gpa); | |
| 1796 | try std.testing.expectError( | |
| 1797 | error.ParseZon, | |
| 1798 | fromSlice(Struct, gpa, ".{10.0}", &status, .{}), | |
| 1799 | ); | |
| 1800 | try std.testing.expectFmt("1:2: error: expected struct\n", "{}", .{status}); | |
| 1801 | } | |
| 1802 | ||
| 1803 | // Comptime field | |
| 1804 | { | |
| 1805 | const Vec2 = struct { f32, comptime f32 = 1.5 }; | |
| 1806 | const parsed = try fromSlice(Vec2, gpa, ".{ 1.2 }", null, .{}); | |
| 1807 | try std.testing.expectEqual(Vec2{ 1.2, 1.5 }, parsed); | |
| 1808 | } | |
| 1809 | ||
| 1810 | // Comptime field assignment | |
| 1811 | { | |
| 1812 | const Vec2 = struct { f32, comptime f32 = 1.5 }; | |
| 1813 | var status: Status = .{}; | |
| 1814 | defer status.deinit(gpa); | |
| 1815 | const parsed = fromSlice(Vec2, gpa, ".{ 1.2, 1.5}", &status, .{}); | |
| 1816 | try std.testing.expectError(error.ParseZon, parsed); | |
| 1817 | try std.testing.expectFmt( | |
| 1818 | \\1:9: error: cannot initialize comptime field | |
| 1819 | \\ | |
| 1820 | , "{}", .{status}); | |
| 1821 | } | |
| 1822 | } | |
| 1823 | ||
| 1824 | // Test sizes 0 to 3 since small sizes get parsed differently | |
| 1825 | test "std.zon arrays and slices" { | |
| 1826 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/20881 | |
| 1827 | ||
| 1828 | const gpa = std.testing.allocator; | |
| 1829 | ||
| 1830 | // Literals | |
| 1831 | { | |
| 1832 | // Arrays | |
| 1833 | { | |
| 1834 | const zero = try fromSlice([0]u8, gpa, ".{}", null, .{}); | |
| 1835 | try std.testing.expectEqualSlices(u8, &@as([0]u8, .{}), &zero); | |
| 1836 | ||
| 1837 | const one = try fromSlice([1]u8, gpa, ".{'a'}", null, .{}); | |
| 1838 | try std.testing.expectEqualSlices(u8, &@as([1]u8, .{'a'}), &one); | |
| 1839 | ||
| 1840 | const two = try fromSlice([2]u8, gpa, ".{'a', 'b'}", null, .{}); | |
| 1841 | try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two); | |
| 1842 | ||
| 1843 | const two_comma = try fromSlice([2]u8, gpa, ".{'a', 'b',}", null, .{}); | |
| 1844 | try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two_comma); | |
| 1845 | ||
| 1846 | const three = try fromSlice([3]u8, gpa, ".{'a', 'b', 'c'}", null, .{}); | |
| 1847 | try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, &three); | |
| 1848 | ||
| 1849 | const sentinel = try fromSlice([3:'z']u8, gpa, ".{'a', 'b', 'c'}", null, .{}); | |
| 1850 | const expected_sentinel: [3:'z']u8 = .{ 'a', 'b', 'c' }; | |
| 1851 | try std.testing.expectEqualSlices(u8, &expected_sentinel, &sentinel); | |
| 1852 | } | |
| 1853 | ||
| 1854 | // Slice literals | |
| 1855 | { | |
| 1856 | const zero = try fromSlice([]const u8, gpa, ".{}", null, .{}); | |
| 1857 | defer free(gpa, zero); | |
| 1858 | try std.testing.expectEqualSlices(u8, @as([]const u8, &.{}), zero); | |
| 1859 | ||
| 1860 | const one = try fromSlice([]u8, gpa, ".{'a'}", null, .{}); | |
| 1861 | defer free(gpa, one); | |
| 1862 | try std.testing.expectEqualSlices(u8, &.{'a'}, one); | |
| 1863 | ||
| 1864 | const two = try fromSlice([]const u8, gpa, ".{'a', 'b'}", null, .{}); | |
| 1865 | defer free(gpa, two); | |
| 1866 | try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two); | |
| 1867 | ||
| 1868 | const two_comma = try fromSlice([]const u8, gpa, ".{'a', 'b',}", null, .{}); | |
| 1869 | defer free(gpa, two_comma); | |
| 1870 | try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two_comma); | |
| 1871 | ||
| 1872 | const three = try fromSlice([]u8, gpa, ".{'a', 'b', 'c'}", null, .{}); | |
| 1873 | defer free(gpa, three); | |
| 1874 | try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, three); | |
| 1875 | ||
| 1876 | const sentinel = try fromSlice([:'z']const u8, gpa, ".{'a', 'b', 'c'}", null, .{}); | |
| 1877 | defer free(gpa, sentinel); | |
| 1878 | const expected_sentinel: [:'z']const u8 = &.{ 'a', 'b', 'c' }; | |
| 1879 | try std.testing.expectEqualSlices(u8, expected_sentinel, sentinel); | |
| 1880 | } | |
| 1881 | } | |
| 1882 | ||
| 1883 | // Deep free | |
| 1884 | { | |
| 1885 | // Arrays | |
| 1886 | { | |
| 1887 | const parsed = try fromSlice([1][]const u8, gpa, ".{\"abc\"}", null, .{}); | |
| 1888 | defer free(gpa, parsed); | |
| 1889 | const expected: [1][]const u8 = .{"abc"}; | |
| 1890 | try std.testing.expectEqualDeep(expected, parsed); | |
| 1891 | } | |
| 1892 | ||
| 1893 | // Slice literals | |
| 1894 | { | |
| 1895 | const parsed = try fromSlice([]const []const u8, gpa, ".{\"abc\"}", null, .{}); | |
| 1896 | defer free(gpa, parsed); | |
| 1897 | const expected: []const []const u8 = &.{"abc"}; | |
| 1898 | try std.testing.expectEqualDeep(expected, parsed); | |
| 1899 | } | |
| 1900 | } | |
| 1901 | ||
| 1902 | // Sentinels and alignment | |
| 1903 | { | |
| 1904 | // Arrays | |
| 1905 | { | |
| 1906 | const sentinel = try fromSlice([1:2]u8, gpa, ".{1}", null, .{}); | |
| 1907 | try std.testing.expectEqual(@as(usize, 1), sentinel.len); | |
| 1908 | try std.testing.expectEqual(@as(u8, 1), sentinel[0]); | |
| 1909 | try std.testing.expectEqual(@as(u8, 2), sentinel[1]); | |
| 1910 | } | |
| 1911 | ||
| 1912 | // Slice literals | |
| 1913 | { | |
| 1914 | const sentinel = try fromSlice([:2]align(4) u8, gpa, ".{1}", null, .{}); | |
| 1915 | defer free(gpa, sentinel); | |
| 1916 | try std.testing.expectEqual(@as(usize, 1), sentinel.len); | |
| 1917 | try std.testing.expectEqual(@as(u8, 1), sentinel[0]); | |
| 1918 | try std.testing.expectEqual(@as(u8, 2), sentinel[1]); | |
| 1919 | } | |
| 1920 | } | |
| 1921 | ||
| 1922 | // Expect 0 find 3 | |
| 1923 | { | |
| 1924 | var status: Status = .{}; | |
| 1925 | defer status.deinit(gpa); | |
| 1926 | try std.testing.expectError( | |
| 1927 | error.ParseZon, | |
| 1928 | fromSlice([0]u8, gpa, ".{'a', 'b', 'c'}", &status, .{}), | |
| 1929 | ); | |
| 1930 | try std.testing.expectFmt( | |
| 1931 | "1:3: error: index 0 outside of array of length 0\n", | |
| 1932 | "{}", | |
| 1933 | .{status}, | |
| 1934 | ); | |
| 1935 | } | |
| 1936 | ||
| 1937 | // Expect 1 find 2 | |
| 1938 | { | |
| 1939 | var status: Status = .{}; | |
| 1940 | defer status.deinit(gpa); | |
| 1941 | try std.testing.expectError( | |
| 1942 | error.ParseZon, | |
| 1943 | fromSlice([1]u8, gpa, ".{'a', 'b'}", &status, .{}), | |
| 1944 | ); | |
| 1945 | try std.testing.expectFmt( | |
| 1946 | "1:8: error: index 1 outside of array of length 1\n", | |
| 1947 | "{}", | |
| 1948 | .{status}, | |
| 1949 | ); | |
| 1950 | } | |
| 1951 | ||
| 1952 | // Expect 2 find 1 | |
| 1953 | { | |
| 1954 | var status: Status = .{}; | |
| 1955 | defer status.deinit(gpa); | |
| 1956 | try std.testing.expectError( | |
| 1957 | error.ParseZon, | |
| 1958 | fromSlice([2]u8, gpa, ".{'a'}", &status, .{}), | |
| 1959 | ); | |
| 1960 | try std.testing.expectFmt( | |
| 1961 | "1:2: error: expected 2 array elements; found 1\n", | |
| 1962 | "{}", | |
| 1963 | .{status}, | |
| 1964 | ); | |
| 1965 | } | |
| 1966 | ||
| 1967 | // Expect 3 find 0 | |
| 1968 | { | |
| 1969 | var status: Status = .{}; | |
| 1970 | defer status.deinit(gpa); | |
| 1971 | try std.testing.expectError( | |
| 1972 | error.ParseZon, | |
| 1973 | fromSlice([3]u8, gpa, ".{}", &status, .{}), | |
| 1974 | ); | |
| 1975 | try std.testing.expectFmt( | |
| 1976 | "1:2: error: expected 3 array elements; found 0\n", | |
| 1977 | "{}", | |
| 1978 | .{status}, | |
| 1979 | ); | |
| 1980 | } | |
| 1981 | ||
| 1982 | // Wrong inner type | |
| 1983 | { | |
| 1984 | // Array | |
| 1985 | { | |
| 1986 | var status: Status = .{}; | |
| 1987 | defer status.deinit(gpa); | |
| 1988 | try std.testing.expectError( | |
| 1989 | error.ParseZon, | |
| 1990 | fromSlice([3]bool, gpa, ".{'a', 'b', 'c'}", &status, .{}), | |
| 1991 | ); | |
| 1992 | try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{}", .{status}); | |
| 1993 | } | |
| 1994 | ||
| 1995 | // Slice | |
| 1996 | { | |
| 1997 | var status: Status = .{}; | |
| 1998 | defer status.deinit(gpa); | |
| 1999 | try std.testing.expectError( | |
| 2000 | error.ParseZon, | |
| 2001 | fromSlice([]bool, gpa, ".{'a', 'b', 'c'}", &status, .{}), | |
| 2002 | ); | |
| 2003 | try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{}", .{status}); | |
| 2004 | } | |
| 2005 | } | |
| 2006 | ||
| 2007 | // Complete wrong type | |
| 2008 | { | |
| 2009 | // Array | |
| 2010 | { | |
| 2011 | var status: Status = .{}; | |
| 2012 | defer status.deinit(gpa); | |
| 2013 | try std.testing.expectError( | |
| 2014 | error.ParseZon, | |
| 2015 | fromSlice([3]u8, gpa, "'a'", &status, .{}), | |
| 2016 | ); | |
| 2017 | try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status}); | |
| 2018 | } | |
| 2019 | ||
| 2020 | // Slice | |
| 2021 | { | |
| 2022 | var status: Status = .{}; | |
| 2023 | defer status.deinit(gpa); | |
| 2024 | try std.testing.expectError( | |
| 2025 | error.ParseZon, | |
| 2026 | fromSlice([]u8, gpa, "'a'", &status, .{}), | |
| 2027 | ); | |
| 2028 | try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status}); | |
| 2029 | } | |
| 2030 | } | |
| 2031 | ||
| 2032 | // Address of is not allowed (indirection for slices in ZON is implicit) | |
| 2033 | { | |
| 2034 | var status: Status = .{}; | |
| 2035 | defer status.deinit(gpa); | |
| 2036 | try std.testing.expectError( | |
| 2037 | error.ParseZon, | |
| 2038 | fromSlice([]u8, gpa, " &.{'a', 'b', 'c'}", &status, .{}), | |
| 2039 | ); | |
| 2040 | try std.testing.expectFmt( | |
| 2041 | "1:3: error: pointers are not available in ZON\n", | |
| 2042 | "{}", | |
| 2043 | .{status}, | |
| 2044 | ); | |
| 2045 | } | |
| 2046 | } | |
| 2047 | ||
| 2048 | test "std.zon string literal" { | |
| 2049 | const gpa = std.testing.allocator; | |
| 2050 | ||
| 2051 | // Basic string literal | |
| 2052 | { | |
| 2053 | const parsed = try fromSlice([]const u8, gpa, "\"abc\"", null, .{}); | |
| 2054 | defer free(gpa, parsed); | |
| 2055 | try std.testing.expectEqualStrings(@as([]const u8, "abc"), parsed); | |
| 2056 | } | |
| 2057 | ||
| 2058 | // String literal with escape characters | |
| 2059 | { | |
| 2060 | const parsed = try fromSlice([]const u8, gpa, "\"ab\\nc\"", null, .{}); | |
| 2061 | defer free(gpa, parsed); | |
| 2062 | try std.testing.expectEqualStrings(@as([]const u8, "ab\nc"), parsed); | |
| 2063 | } | |
| 2064 | ||
| 2065 | // String literal with embedded null | |
| 2066 | { | |
| 2067 | const parsed = try fromSlice([]const u8, gpa, "\"ab\\x00c\"", null, .{}); | |
| 2068 | defer free(gpa, parsed); | |
| 2069 | try std.testing.expectEqualStrings(@as([]const u8, "ab\x00c"), parsed); | |
| 2070 | } | |
| 2071 | ||
| 2072 | // Passing string literal to a mutable slice | |
| 2073 | { | |
| 2074 | { | |
| 2075 | var status: Status = .{}; | |
| 2076 | defer status.deinit(gpa); | |
| 2077 | try std.testing.expectError( | |
| 2078 | error.ParseZon, | |
| 2079 | fromSlice([]u8, gpa, "\"abcd\"", &status, .{}), | |
| 2080 | ); | |
| 2081 | try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status}); | |
| 2082 | } | |
| 2083 | ||
| 2084 | { | |
| 2085 | var status: Status = .{}; | |
| 2086 | defer status.deinit(gpa); | |
| 2087 | try std.testing.expectError( | |
| 2088 | error.ParseZon, | |
| 2089 | fromSlice([]u8, gpa, "\\\\abcd", &status, .{}), | |
| 2090 | ); | |
| 2091 | try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status}); | |
| 2092 | } | |
| 2093 | } | |
| 2094 | ||
| 2095 | // Passing string literal to a array | |
| 2096 | { | |
| 2097 | { | |
| 2098 | var ast = try std.zig.Ast.parse(gpa, "\"abcd\"", .zon); | |
| 2099 | defer ast.deinit(gpa); | |
| 2100 | var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); | |
| 2101 | defer zoir.deinit(gpa); | |
| 2102 | var status: Status = .{}; | |
| 2103 | defer status.deinit(gpa); | |
| 2104 | try std.testing.expectError( | |
| 2105 | error.ParseZon, | |
| 2106 | fromSlice([4:0]u8, gpa, "\"abcd\"", &status, .{}), | |
| 2107 | ); | |
| 2108 | try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status}); | |
| 2109 | } | |
| 2110 | ||
| 2111 | { | |
| 2112 | var status: Status = .{}; | |
| 2113 | defer status.deinit(gpa); | |
| 2114 | try std.testing.expectError( | |
| 2115 | error.ParseZon, | |
| 2116 | fromSlice([4:0]u8, gpa, "\\\\abcd", &status, .{}), | |
| 2117 | ); | |
| 2118 | try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status}); | |
| 2119 | } | |
| 2120 | } | |
| 2121 | ||
| 2122 | // Zero terminated slices | |
| 2123 | { | |
| 2124 | { | |
| 2125 | const parsed: [:0]const u8 = try fromSlice( | |
| 2126 | [:0]const u8, | |
| 2127 | gpa, | |
| 2128 | "\"abc\"", | |
| 2129 | null, | |
| 2130 | .{}, | |
| 2131 | ); | |
| 2132 | defer free(gpa, parsed); | |
| 2133 | try std.testing.expectEqualStrings("abc", parsed); | |
| 2134 | try std.testing.expectEqual(@as(u8, 0), parsed[3]); | |
| 2135 | } | |
| 2136 | ||
| 2137 | { | |
| 2138 | const parsed: [:0]const u8 = try fromSlice( | |
| 2139 | [:0]const u8, | |
| 2140 | gpa, | |
| 2141 | "\\\\abc", | |
| 2142 | null, | |
| 2143 | .{}, | |
| 2144 | ); | |
| 2145 | defer free(gpa, parsed); | |
| 2146 | try std.testing.expectEqualStrings("abc", parsed); | |
| 2147 | try std.testing.expectEqual(@as(u8, 0), parsed[3]); | |
| 2148 | } | |
| 2149 | } | |
| 2150 | ||
| 2151 | // Other value terminated slices | |
| 2152 | { | |
| 2153 | { | |
| 2154 | var status: Status = .{}; | |
| 2155 | defer status.deinit(gpa); | |
| 2156 | try std.testing.expectError( | |
| 2157 | error.ParseZon, | |
| 2158 | fromSlice([:1]const u8, gpa, "\"foo\"", &status, .{}), | |
| 2159 | ); | |
| 2160 | try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status}); | |
| 2161 | } | |
| 2162 | ||
| 2163 | { | |
| 2164 | var status: Status = .{}; | |
| 2165 | defer status.deinit(gpa); | |
| 2166 | try std.testing.expectError( | |
| 2167 | error.ParseZon, | |
| 2168 | fromSlice([:1]const u8, gpa, "\\\\foo", &status, .{}), | |
| 2169 | ); | |
| 2170 | try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status}); | |
| 2171 | } | |
| 2172 | } | |
| 2173 | ||
| 2174 | // Expecting string literal, getting something else | |
| 2175 | { | |
| 2176 | var status: Status = .{}; | |
| 2177 | defer status.deinit(gpa); | |
| 2178 | try std.testing.expectError( | |
| 2179 | error.ParseZon, | |
| 2180 | fromSlice([]const u8, gpa, "true", &status, .{}), | |
| 2181 | ); | |
| 2182 | try std.testing.expectFmt("1:1: error: expected string\n", "{}", .{status}); | |
| 2183 | } | |
| 2184 | ||
| 2185 | // Expecting string literal, getting an incompatible tuple | |
| 2186 | { | |
| 2187 | var status: Status = .{}; | |
| 2188 | defer status.deinit(gpa); | |
| 2189 | try std.testing.expectError( | |
| 2190 | error.ParseZon, | |
| 2191 | fromSlice([]const u8, gpa, ".{false}", &status, .{}), | |
| 2192 | ); | |
| 2193 | try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{}", .{status}); | |
| 2194 | } | |
| 2195 | ||
| 2196 | // Invalid string literal | |
| 2197 | { | |
| 2198 | var status: Status = .{}; | |
| 2199 | defer status.deinit(gpa); | |
| 2200 | try std.testing.expectError( | |
| 2201 | error.ParseZon, | |
| 2202 | fromSlice([]const i8, gpa, "\"\\a\"", &status, .{}), | |
| 2203 | ); | |
| 2204 | try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{}", .{status}); | |
| 2205 | } | |
| 2206 | ||
| 2207 | // Slice wrong child type | |
| 2208 | { | |
| 2209 | { | |
| 2210 | var status: Status = .{}; | |
| 2211 | defer status.deinit(gpa); | |
| 2212 | try std.testing.expectError( | |
| 2213 | error.ParseZon, | |
| 2214 | fromSlice([]const i8, gpa, "\"a\"", &status, .{}), | |
| 2215 | ); | |
| 2216 | try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status}); | |
| 2217 | } | |
| 2218 | ||
| 2219 | { | |
| 2220 | var status: Status = .{}; | |
| 2221 | defer status.deinit(gpa); | |
| 2222 | try std.testing.expectError( | |
| 2223 | error.ParseZon, | |
| 2224 | fromSlice([]const i8, gpa, "\\\\a", &status, .{}), | |
| 2225 | ); | |
| 2226 | try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status}); | |
| 2227 | } | |
| 2228 | } | |
| 2229 | ||
| 2230 | // Bad alignment | |
| 2231 | { | |
| 2232 | { | |
| 2233 | var status: Status = .{}; | |
| 2234 | defer status.deinit(gpa); | |
| 2235 | try std.testing.expectError( | |
| 2236 | error.ParseZon, | |
| 2237 | fromSlice([]align(2) const u8, gpa, "\"abc\"", &status, .{}), | |
| 2238 | ); | |
| 2239 | try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status}); | |
| 2240 | } | |
| 2241 | ||
| 2242 | { | |
| 2243 | var status: Status = .{}; | |
| 2244 | defer status.deinit(gpa); | |
| 2245 | try std.testing.expectError( | |
| 2246 | error.ParseZon, | |
| 2247 | fromSlice([]align(2) const u8, gpa, "\\\\abc", &status, .{}), | |
| 2248 | ); | |
| 2249 | try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status}); | |
| 2250 | } | |
| 2251 | } | |
| 2252 | ||
| 2253 | // Multi line strings | |
| 2254 | inline for (.{ []const u8, [:0]const u8 }) |String| { | |
| 2255 | // Nested | |
| 2256 | { | |
| 2257 | const S = struct { | |
| 2258 | message: String, | |
| 2259 | message2: String, | |
| 2260 | message3: String, | |
| 2261 | }; | |
| 2262 | const parsed = try fromSlice(S, gpa, | |
| 2263 | \\.{ | |
| 2264 | \\ .message = | |
| 2265 | \\ \\hello, world! | |
| 2266 | \\ | |
| 2267 | \\ \\this is a multiline string! | |
| 2268 | \\ \\ | |
| 2269 | \\ \\... | |
| 2270 | \\ | |
| 2271 | \\ , | |
| 2272 | \\ .message2 = | |
| 2273 | \\ \\this too...sort of. | |
| 2274 | \\ , | |
| 2275 | \\ .message3 = | |
| 2276 | \\ \\ | |
| 2277 | \\ \\and this. | |
| 2278 | \\} | |
| 2279 | , null, .{}); | |
| 2280 | defer free(gpa, parsed); | |
| 2281 | try std.testing.expectEqualStrings( | |
| 2282 | "hello, world!\nthis is a multiline string!\n\n...", | |
| 2283 | parsed.message, | |
| 2284 | ); | |
| 2285 | try std.testing.expectEqualStrings("this too...sort of.", parsed.message2); | |
| 2286 | try std.testing.expectEqualStrings("\nand this.", parsed.message3); | |
| 2287 | } | |
| 2288 | } | |
| 2289 | } | |
| 2290 | ||
| 2291 | test "std.zon enum literals" { | |
| 2292 | const gpa = std.testing.allocator; | |
| 2293 | ||
| 2294 | const Enum = enum { | |
| 2295 | foo, | |
| 2296 | bar, | |
| 2297 | baz, | |
| 2298 | @"ab\nc", | |
| 2299 | }; | |
| 2300 | ||
| 2301 | // Tags that exist | |
| 2302 | try std.testing.expectEqual(Enum.foo, try fromSlice(Enum, gpa, ".foo", null, .{})); | |
| 2303 | try std.testing.expectEqual(Enum.bar, try fromSlice(Enum, gpa, ".bar", null, .{})); | |
| 2304 | try std.testing.expectEqual(Enum.baz, try fromSlice(Enum, gpa, ".baz", null, .{})); | |
| 2305 | try std.testing.expectEqual( | |
| 2306 | Enum.@"ab\nc", | |
| 2307 | try fromSlice(Enum, gpa, ".@\"ab\\nc\"", null, .{}), | |
| 2308 | ); | |
| 2309 | ||
| 2310 | // Bad tag | |
| 2311 | { | |
| 2312 | var status: Status = .{}; | |
| 2313 | defer status.deinit(gpa); | |
| 2314 | try std.testing.expectError( | |
| 2315 | error.ParseZon, | |
| 2316 | fromSlice(Enum, gpa, ".qux", &status, .{}), | |
| 2317 | ); | |
| 2318 | try std.testing.expectFmt( | |
| 2319 | \\1:2: error: unexpected enum literal 'qux' | |
| 2320 | \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' | |
| 2321 | \\ | |
| 2322 | , | |
| 2323 | "{}", | |
| 2324 | .{status}, | |
| 2325 | ); | |
| 2326 | } | |
| 2327 | ||
| 2328 | // Bad tag that's too long for parser | |
| 2329 | { | |
| 2330 | var status: Status = .{}; | |
| 2331 | defer status.deinit(gpa); | |
| 2332 | try std.testing.expectError( | |
| 2333 | error.ParseZon, | |
| 2334 | fromSlice(Enum, gpa, ".@\"foobarbaz\"", &status, .{}), | |
| 2335 | ); | |
| 2336 | try std.testing.expectFmt( | |
| 2337 | \\1:2: error: unexpected enum literal 'foobarbaz' | |
| 2338 | \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"' | |
| 2339 | \\ | |
| 2340 | , | |
| 2341 | "{}", | |
| 2342 | .{status}, | |
| 2343 | ); | |
| 2344 | } | |
| 2345 | ||
| 2346 | // Bad type | |
| 2347 | { | |
| 2348 | var status: Status = .{}; | |
| 2349 | defer status.deinit(gpa); | |
| 2350 | try std.testing.expectError( | |
| 2351 | error.ParseZon, | |
| 2352 | fromSlice(Enum, gpa, "true", &status, .{}), | |
| 2353 | ); | |
| 2354 | try std.testing.expectFmt("1:1: error: expected enum literal\n", "{}", .{status}); | |
| 2355 | } | |
| 2356 | ||
| 2357 | // Test embedded nulls in an identifier | |
| 2358 | { | |
| 2359 | var status: Status = .{}; | |
| 2360 | defer status.deinit(gpa); | |
| 2361 | try std.testing.expectError( | |
| 2362 | error.ParseZon, | |
| 2363 | fromSlice(Enum, gpa, ".@\"\\x00\"", &status, .{}), | |
| 2364 | ); | |
| 2365 | try std.testing.expectFmt( | |
| 2366 | "1:2: error: identifier cannot contain null bytes\n", | |
| 2367 | "{}", | |
| 2368 | .{status}, | |
| 2369 | ); | |
| 2370 | } | |
| 2371 | } | |
| 2372 | ||
| 2373 | test "std.zon parse bool" { | |
| 2374 | const gpa = std.testing.allocator; | |
| 2375 | ||
| 2376 | // Correct floats | |
| 2377 | try std.testing.expectEqual(true, try fromSlice(bool, gpa, "true", null, .{})); | |
| 2378 | try std.testing.expectEqual(false, try fromSlice(bool, gpa, "false", null, .{})); | |
| 2379 | ||
| 2380 | // Errors | |
| 2381 | { | |
| 2382 | var status: Status = .{}; | |
| 2383 | defer status.deinit(gpa); | |
| 2384 | try std.testing.expectError( | |
| 2385 | error.ParseZon, | |
| 2386 | fromSlice(bool, gpa, " foo", &status, .{}), | |
| 2387 | ); | |
| 2388 | try std.testing.expectFmt( | |
| 2389 | \\1:2: error: invalid expression | |
| 2390 | \\1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' | |
| 2391 | \\1:2: note: precede identifier with '.' for an enum literal | |
| 2392 | \\ | |
| 2393 | , "{}", .{status}); | |
| 2394 | } | |
| 2395 | { | |
| 2396 | var status: Status = .{}; | |
| 2397 | defer status.deinit(gpa); | |
| 2398 | try std.testing.expectError(error.ParseZon, fromSlice(bool, gpa, "123", &status, .{})); | |
| 2399 | try std.testing.expectFmt("1:1: error: expected type 'bool'\n", "{}", .{status}); | |
| 2400 | } | |
| 2401 | } | |
| 2402 | ||
| 2403 | test "std.zon intFromFloatExact" { | |
| 2404 | // Valid conversions | |
| 2405 | try std.testing.expectEqual(@as(u8, 10), intFromFloatExact(u8, @as(f32, 10.0)).?); | |
| 2406 | try std.testing.expectEqual(@as(i8, -123), intFromFloatExact(i8, @as(f64, @as(f64, -123.0))).?); | |
| 2407 | try std.testing.expectEqual(@as(i16, 45), intFromFloatExact(i16, @as(f128, @as(f128, 45.0))).?); | |
| 2408 | ||
| 2409 | // Out of range | |
| 2410 | try std.testing.expectEqual(@as(?u4, null), intFromFloatExact(u4, @as(f32, 16.0))); | |
| 2411 | try std.testing.expectEqual(@as(?i4, null), intFromFloatExact(i4, @as(f64, -17.0))); | |
| 2412 | try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, @as(f128, -2.0))); | |
| 2413 | ||
| 2414 | // Not a whole number | |
| 2415 | try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, @as(f32, 0.5))); | |
| 2416 | try std.testing.expectEqual(@as(?i8, null), intFromFloatExact(i8, @as(f64, 0.01))); | |
| 2417 | ||
| 2418 | // Infinity and NaN | |
| 2419 | try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, std.math.inf(f32))); | |
| 2420 | try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, -std.math.inf(f32))); | |
| 2421 | try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, std.math.nan(f32))); | |
| 2422 | } | |
| 2423 | ||
| 2424 | test "std.zon parse int" { | |
| 2425 | const gpa = std.testing.allocator; | |
| 2426 | ||
| 2427 | // Test various numbers and types | |
| 2428 | try std.testing.expectEqual(@as(u8, 10), try fromSlice(u8, gpa, "10", null, .{})); | |
| 2429 | try std.testing.expectEqual(@as(i16, 24), try fromSlice(i16, gpa, "24", null, .{})); | |
| 2430 | try std.testing.expectEqual(@as(i14, -4), try fromSlice(i14, gpa, "-4", null, .{})); | |
| 2431 | try std.testing.expectEqual(@as(i32, -123), try fromSlice(i32, gpa, "-123", null, .{})); | |
| 2432 | ||
| 2433 | // Test limits | |
| 2434 | try std.testing.expectEqual(@as(i8, 127), try fromSlice(i8, gpa, "127", null, .{})); | |
| 2435 | try std.testing.expectEqual(@as(i8, -128), try fromSlice(i8, gpa, "-128", null, .{})); | |
| 2436 | ||
| 2437 | // Test characters | |
| 2438 | try std.testing.expectEqual(@as(u8, 'a'), try fromSlice(u8, gpa, "'a'", null, .{})); | |
| 2439 | try std.testing.expectEqual(@as(u8, 'z'), try fromSlice(u8, gpa, "'z'", null, .{})); | |
| 2440 | ||
| 2441 | // Test big integers | |
| 2442 | try std.testing.expectEqual( | |
| 2443 | @as(u65, 36893488147419103231), | |
| 2444 | try fromSlice(u65, gpa, "36893488147419103231", null, .{}), | |
| 2445 | ); | |
| 2446 | try std.testing.expectEqual( | |
| 2447 | @as(u65, 36893488147419103231), | |
| 2448 | try fromSlice(u65, gpa, "368934_881_474191032_31", null, .{}), | |
| 2449 | ); | |
| 2450 | ||
| 2451 | // Test big integer limits | |
| 2452 | try std.testing.expectEqual( | |
| 2453 | @as(i66, 36893488147419103231), | |
| 2454 | try fromSlice(i66, gpa, "36893488147419103231", null, .{}), | |
| 2455 | ); | |
| 2456 | try std.testing.expectEqual( | |
| 2457 | @as(i66, -36893488147419103232), | |
| 2458 | try fromSlice(i66, gpa, "-36893488147419103232", null, .{}), | |
| 2459 | ); | |
| 2460 | { | |
| 2461 | var status: Status = .{}; | |
| 2462 | defer status.deinit(gpa); | |
| 2463 | try std.testing.expectError(error.ParseZon, fromSlice( | |
| 2464 | i66, | |
| 2465 | gpa, | |
| 2466 | "36893488147419103232", | |
| 2467 | &status, | |
| 2468 | .{}, | |
| 2469 | )); | |
| 2470 | try std.testing.expectFmt( | |
| 2471 | "1:1: error: type 'i66' cannot represent value\n", | |
| 2472 | "{}", | |
| 2473 | .{status}, | |
| 2474 | ); | |
| 2475 | } | |
| 2476 | { | |
| 2477 | var status: Status = .{}; | |
| 2478 | defer status.deinit(gpa); | |
| 2479 | try std.testing.expectError(error.ParseZon, fromSlice( | |
| 2480 | i66, | |
| 2481 | gpa, | |
| 2482 | "-36893488147419103233", | |
| 2483 | &status, | |
| 2484 | .{}, | |
| 2485 | )); | |
| 2486 | try std.testing.expectFmt( | |
| 2487 | "1:1: error: type 'i66' cannot represent value\n", | |
| 2488 | "{}", | |
| 2489 | .{status}, | |
| 2490 | ); | |
| 2491 | } | |
| 2492 | ||
| 2493 | // Test parsing whole number floats as integers | |
| 2494 | try std.testing.expectEqual(@as(i8, -1), try fromSlice(i8, gpa, "-1.0", null, .{})); | |
| 2495 | try std.testing.expectEqual(@as(i8, 123), try fromSlice(i8, gpa, "123.0", null, .{})); | |
| 2496 | ||
| 2497 | // Test non-decimal integers | |
| 2498 | try std.testing.expectEqual(@as(i16, 0xff), try fromSlice(i16, gpa, "0xff", null, .{})); | |
| 2499 | try std.testing.expectEqual(@as(i16, -0xff), try fromSlice(i16, gpa, "-0xff", null, .{})); | |
| 2500 | try std.testing.expectEqual(@as(i16, 0o77), try fromSlice(i16, gpa, "0o77", null, .{})); | |
| 2501 | try std.testing.expectEqual(@as(i16, -0o77), try fromSlice(i16, gpa, "-0o77", null, .{})); | |
| 2502 | try std.testing.expectEqual(@as(i16, 0b11), try fromSlice(i16, gpa, "0b11", null, .{})); | |
| 2503 | try std.testing.expectEqual(@as(i16, -0b11), try fromSlice(i16, gpa, "-0b11", null, .{})); | |
| 2504 | ||
| 2505 | // Test non-decimal big integers | |
| 2506 | try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice( | |
| 2507 | u65, | |
| 2508 | gpa, | |
| 2509 | "0x1ffffffffffffffff", | |
| 2510 | null, | |
| 2511 | .{}, | |
| 2512 | )); | |
| 2513 | try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice( | |
| 2514 | i66, | |
| 2515 | gpa, | |
| 2516 | "0x1ffffffffffffffff", | |
| 2517 | null, | |
| 2518 | .{}, | |
| 2519 | )); | |
| 2520 | try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice( | |
| 2521 | i66, | |
| 2522 | gpa, | |
| 2523 | "-0x1ffffffffffffffff", | |
| 2524 | null, | |
| 2525 | .{}, | |
| 2526 | )); | |
| 2527 | try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice( | |
| 2528 | u65, | |
| 2529 | gpa, | |
| 2530 | "0o3777777777777777777777", | |
| 2531 | null, | |
| 2532 | .{}, | |
| 2533 | )); | |
| 2534 | try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice( | |
| 2535 | i66, | |
| 2536 | gpa, | |
| 2537 | "0o3777777777777777777777", | |
| 2538 | null, | |
| 2539 | .{}, | |
| 2540 | )); | |
| 2541 | try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice( | |
| 2542 | i66, | |
| 2543 | gpa, | |
| 2544 | "-0o3777777777777777777777", | |
| 2545 | null, | |
| 2546 | .{}, | |
| 2547 | )); | |
| 2548 | try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice( | |
| 2549 | u65, | |
| 2550 | gpa, | |
| 2551 | "0b11111111111111111111111111111111111111111111111111111111111111111", | |
| 2552 | null, | |
| 2553 | .{}, | |
| 2554 | )); | |
| 2555 | try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice( | |
| 2556 | i66, | |
| 2557 | gpa, | |
| 2558 | "0b11111111111111111111111111111111111111111111111111111111111111111", | |
| 2559 | null, | |
| 2560 | .{}, | |
| 2561 | )); | |
| 2562 | try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice( | |
| 2563 | i66, | |
| 2564 | gpa, | |
| 2565 | "-0b11111111111111111111111111111111111111111111111111111111111111111", | |
| 2566 | null, | |
| 2567 | .{}, | |
| 2568 | )); | |
| 2569 | ||
| 2570 | // Number with invalid character in the middle | |
| 2571 | { | |
| 2572 | var status: Status = .{}; | |
| 2573 | defer status.deinit(gpa); | |
| 2574 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "32a32", &status, .{})); | |
| 2575 | try std.testing.expectFmt( | |
| 2576 | "1:3: error: invalid digit 'a' for decimal base\n", | |
| 2577 | "{}", | |
| 2578 | .{status}, | |
| 2579 | ); | |
| 2580 | } | |
| 2581 | ||
| 2582 | // Failing to parse as int | |
| 2583 | { | |
| 2584 | var status: Status = .{}; | |
| 2585 | defer status.deinit(gpa); | |
| 2586 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "true", &status, .{})); | |
| 2587 | try std.testing.expectFmt("1:1: error: expected type 'u8'\n", "{}", .{status}); | |
| 2588 | } | |
| 2589 | ||
| 2590 | // Failing because an int is out of range | |
| 2591 | { | |
| 2592 | var status: Status = .{}; | |
| 2593 | defer status.deinit(gpa); | |
| 2594 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "256", &status, .{})); | |
| 2595 | try std.testing.expectFmt( | |
| 2596 | "1:1: error: type 'u8' cannot represent value\n", | |
| 2597 | "{}", | |
| 2598 | .{status}, | |
| 2599 | ); | |
| 2600 | } | |
| 2601 | ||
| 2602 | // Failing because a negative int is out of range | |
| 2603 | { | |
| 2604 | var status: Status = .{}; | |
| 2605 | defer status.deinit(gpa); | |
| 2606 | try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-129", &status, .{})); | |
| 2607 | try std.testing.expectFmt( | |
| 2608 | "1:1: error: type 'i8' cannot represent value\n", | |
| 2609 | "{}", | |
| 2610 | .{status}, | |
| 2611 | ); | |
| 2612 | } | |
| 2613 | ||
| 2614 | // Failing because an unsigned int is negative | |
| 2615 | { | |
| 2616 | var status: Status = .{}; | |
| 2617 | defer status.deinit(gpa); | |
| 2618 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1", &status, .{})); | |
| 2619 | try std.testing.expectFmt( | |
| 2620 | "1:1: error: type 'u8' cannot represent value\n", | |
| 2621 | "{}", | |
| 2622 | .{status}, | |
| 2623 | ); | |
| 2624 | } | |
| 2625 | ||
| 2626 | // Failing because a float is non-whole | |
| 2627 | { | |
| 2628 | var status: Status = .{}; | |
| 2629 | defer status.deinit(gpa); | |
| 2630 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "1.5", &status, .{})); | |
| 2631 | try std.testing.expectFmt( | |
| 2632 | "1:1: error: type 'u8' cannot represent value\n", | |
| 2633 | "{}", | |
| 2634 | .{status}, | |
| 2635 | ); | |
| 2636 | } | |
| 2637 | ||
| 2638 | // Failing because a float is negative | |
| 2639 | { | |
| 2640 | var status: Status = .{}; | |
| 2641 | defer status.deinit(gpa); | |
| 2642 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1.0", &status, .{})); | |
| 2643 | try std.testing.expectFmt( | |
| 2644 | "1:1: error: type 'u8' cannot represent value\n", | |
| 2645 | "{}", | |
| 2646 | .{status}, | |
| 2647 | ); | |
| 2648 | } | |
| 2649 | ||
| 2650 | // Negative integer zero | |
| 2651 | { | |
| 2652 | var status: Status = .{}; | |
| 2653 | defer status.deinit(gpa); | |
| 2654 | try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-0", &status, .{})); | |
| 2655 | try std.testing.expectFmt( | |
| 2656 | \\1:2: error: integer literal '-0' is ambiguous | |
| 2657 | \\1:2: note: use '0' for an integer zero | |
| 2658 | \\1:2: note: use '-0.0' for a floating-point signed zero | |
| 2659 | \\ | |
| 2660 | , "{}", .{status}); | |
| 2661 | } | |
| 2662 | ||
| 2663 | // Negative integer zero casted to float | |
| 2664 | { | |
| 2665 | var status: Status = .{}; | |
| 2666 | defer status.deinit(gpa); | |
| 2667 | try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-0", &status, .{})); | |
| 2668 | try std.testing.expectFmt( | |
| 2669 | \\1:2: error: integer literal '-0' is ambiguous | |
| 2670 | \\1:2: note: use '0' for an integer zero | |
| 2671 | \\1:2: note: use '-0.0' for a floating-point signed zero | |
| 2672 | \\ | |
| 2673 | , "{}", .{status}); | |
| 2674 | } | |
| 2675 | ||
| 2676 | // Negative float 0 is allowed | |
| 2677 | try std.testing.expect( | |
| 2678 | std.math.isNegativeZero(try fromSlice(f32, gpa, "-0.0", null, .{})), | |
| 2679 | ); | |
| 2680 | try std.testing.expect(std.math.isPositiveZero(try fromSlice(f32, gpa, "0.0", null, .{}))); | |
| 2681 | ||
| 2682 | // Double negation is not allowed | |
| 2683 | { | |
| 2684 | var status: Status = .{}; | |
| 2685 | defer status.deinit(gpa); | |
| 2686 | try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "--2", &status, .{})); | |
| 2687 | try std.testing.expectFmt( | |
| 2688 | "1:1: error: expected number or 'inf' after '-'\n", | |
| 2689 | "{}", | |
| 2690 | .{status}, | |
| 2691 | ); | |
| 2692 | } | |
| 2693 | ||
| 2694 | { | |
| 2695 | var status: Status = .{}; | |
| 2696 | defer status.deinit(gpa); | |
| 2697 | try std.testing.expectError( | |
| 2698 | error.ParseZon, | |
| 2699 | fromSlice(f32, gpa, "--2.0", &status, .{}), | |
| 2700 | ); | |
| 2701 | try std.testing.expectFmt( | |
| 2702 | "1:1: error: expected number or 'inf' after '-'\n", | |
| 2703 | "{}", | |
| 2704 | .{status}, | |
| 2705 | ); | |
| 2706 | } | |
| 2707 | ||
| 2708 | // Invalid int literal | |
| 2709 | { | |
| 2710 | var status: Status = .{}; | |
| 2711 | defer status.deinit(gpa); | |
| 2712 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "0xg", &status, .{})); | |
| 2713 | try std.testing.expectFmt("1:3: error: invalid digit 'g' for hex base\n", "{}", .{status}); | |
| 2714 | } | |
| 2715 | ||
| 2716 | // Notes on invalid int literal | |
| 2717 | { | |
| 2718 | var status: Status = .{}; | |
| 2719 | defer status.deinit(gpa); | |
| 2720 | try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "0123", &status, .{})); | |
| 2721 | try std.testing.expectFmt( | |
| 2722 | \\1:1: error: number '0123' has leading zero | |
| 2723 | \\1:1: note: use '0o' prefix for octal literals | |
| 2724 | \\ | |
| 2725 | , "{}", .{status}); | |
| 2726 | } | |
| 2727 | } | |
| 2728 | ||
| 2729 | test "std.zon negative char" { | |
| 2730 | const gpa = std.testing.allocator; | |
| 2731 | ||
| 2732 | { | |
| 2733 | var status: Status = .{}; | |
| 2734 | defer status.deinit(gpa); | |
| 2735 | try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-'a'", &status, .{})); | |
| 2736 | try std.testing.expectFmt( | |
| 2737 | "1:1: error: expected number or 'inf' after '-'\n", | |
| 2738 | "{}", | |
| 2739 | .{status}, | |
| 2740 | ); | |
| 2741 | } | |
| 2742 | { | |
| 2743 | var status: Status = .{}; | |
| 2744 | defer status.deinit(gpa); | |
| 2745 | try std.testing.expectError(error.ParseZon, fromSlice(i16, gpa, "-'a'", &status, .{})); | |
| 2746 | try std.testing.expectFmt( | |
| 2747 | "1:1: error: expected number or 'inf' after '-'\n", | |
| 2748 | "{}", | |
| 2749 | .{status}, | |
| 2750 | ); | |
| 2751 | } | |
| 2752 | } | |
| 2753 | ||
| 2754 | test "std.zon parse float" { | |
| 2755 | const gpa = std.testing.allocator; | |
| 2756 | ||
| 2757 | // Test decimals | |
| 2758 | try std.testing.expectEqual(@as(f16, 0.5), try fromSlice(f16, gpa, "0.5", null, .{})); | |
| 2759 | try std.testing.expectEqual( | |
| 2760 | @as(f32, 123.456), | |
| 2761 | try fromSlice(f32, gpa, "123.456", null, .{}), | |
| 2762 | ); | |
| 2763 | try std.testing.expectEqual( | |
| 2764 | @as(f64, -123.456), | |
| 2765 | try fromSlice(f64, gpa, "-123.456", null, .{}), | |
| 2766 | ); | |
| 2767 | try std.testing.expectEqual(@as(f128, 42.5), try fromSlice(f128, gpa, "42.5", null, .{})); | |
| 2768 | ||
| 2769 | // Test whole numbers with and without decimals | |
| 2770 | try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, gpa, "5.0", null, .{})); | |
| 2771 | try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, gpa, "5", null, .{})); | |
| 2772 | try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, gpa, "-102.0", null, .{})); | |
| 2773 | try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, gpa, "-102", null, .{})); | |
| 2774 | ||
| 2775 | // Test characters and negated characters | |
| 2776 | try std.testing.expectEqual(@as(f32, 'a'), try fromSlice(f32, gpa, "'a'", null, .{})); | |
| 2777 | try std.testing.expectEqual(@as(f32, 'z'), try fromSlice(f32, gpa, "'z'", null, .{})); | |
| 2778 | ||
| 2779 | // Test big integers | |
| 2780 | try std.testing.expectEqual( | |
| 2781 | @as(f32, 36893488147419103231), | |
| 2782 | try fromSlice(f32, gpa, "36893488147419103231", null, .{}), | |
| 2783 | ); | |
| 2784 | try std.testing.expectEqual( | |
| 2785 | @as(f32, -36893488147419103231), | |
| 2786 | try fromSlice(f32, gpa, "-36893488147419103231", null, .{}), | |
| 2787 | ); | |
| 2788 | try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice( | |
| 2789 | f128, | |
| 2790 | gpa, | |
| 2791 | "0x1ffffffffffffffff", | |
| 2792 | null, | |
| 2793 | .{}, | |
| 2794 | )); | |
| 2795 | try std.testing.expectEqual(@as(f32, 0x1ffffffffffffffff), try fromSlice( | |
| 2796 | f32, | |
| 2797 | gpa, | |
| 2798 | "0x1ffffffffffffffff", | |
| 2799 | null, | |
| 2800 | .{}, | |
| 2801 | )); | |
| 2802 | ||
| 2803 | // Exponents, underscores | |
| 2804 | try std.testing.expectEqual( | |
| 2805 | @as(f32, 123.0E+77), | |
| 2806 | try fromSlice(f32, gpa, "12_3.0E+77", null, .{}), | |
| 2807 | ); | |
| 2808 | ||
| 2809 | // Hexadecimal | |
| 2810 | try std.testing.expectEqual( | |
| 2811 | @as(f32, 0x103.70p-5), | |
| 2812 | try fromSlice(f32, gpa, "0x103.70p-5", null, .{}), | |
| 2813 | ); | |
| 2814 | try std.testing.expectEqual( | |
| 2815 | @as(f32, -0x103.70), | |
| 2816 | try fromSlice(f32, gpa, "-0x103.70", null, .{}), | |
| 2817 | ); | |
| 2818 | try std.testing.expectEqual( | |
| 2819 | @as(f32, 0x1234_5678.9ABC_CDEFp-10), | |
| 2820 | try fromSlice(f32, gpa, "0x1234_5678.9ABC_CDEFp-10", null, .{}), | |
| 2821 | ); | |
| 2822 | ||
| 2823 | // inf, nan | |
| 2824 | try std.testing.expect(std.math.isPositiveInf(try fromSlice(f32, gpa, "inf", null, .{}))); | |
| 2825 | try std.testing.expect(std.math.isNegativeInf(try fromSlice(f32, gpa, "-inf", null, .{}))); | |
| 2826 | try std.testing.expect(std.math.isNan(try fromSlice(f32, gpa, "nan", null, .{}))); | |
| 2827 | ||
| 2828 | // Negative nan not allowed | |
| 2829 | { | |
| 2830 | var status: Status = .{}; | |
| 2831 | defer status.deinit(gpa); | |
| 2832 | try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-nan", &status, .{})); | |
| 2833 | try std.testing.expectFmt( | |
| 2834 | "1:1: error: expected number or 'inf' after '-'\n", | |
| 2835 | "{}", | |
| 2836 | .{status}, | |
| 2837 | ); | |
| 2838 | } | |
| 2839 | ||
| 2840 | // nan as int not allowed | |
| 2841 | { | |
| 2842 | var status: Status = .{}; | |
| 2843 | defer status.deinit(gpa); | |
| 2844 | try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &status, .{})); | |
| 2845 | try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{status}); | |
| 2846 | } | |
| 2847 | ||
| 2848 | // nan as int not allowed | |
| 2849 | { | |
| 2850 | var status: Status = .{}; | |
| 2851 | defer status.deinit(gpa); | |
| 2852 | try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &status, .{})); | |
| 2853 | try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{status}); | |
| 2854 | } | |
| 2855 | ||
| 2856 | // inf as int not allowed | |
| 2857 | { | |
| 2858 | var status: Status = .{}; | |
| 2859 | defer status.deinit(gpa); | |
| 2860 | try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "inf", &status, .{})); | |
| 2861 | try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{status}); | |
| 2862 | } | |
| 2863 | ||
| 2864 | // -inf as int not allowed | |
| 2865 | { | |
| 2866 | var status: Status = .{}; | |
| 2867 | defer status.deinit(gpa); | |
| 2868 | try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-inf", &status, .{})); | |
| 2869 | try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{status}); | |
| 2870 | } | |
| 2871 | ||
| 2872 | // Bad identifier as float | |
| 2873 | { | |
| 2874 | var status: Status = .{}; | |
| 2875 | defer status.deinit(gpa); | |
| 2876 | try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "foo", &status, .{})); | |
| 2877 | try std.testing.expectFmt( | |
| 2878 | \\1:1: error: invalid expression | |
| 2879 | \\1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' | |
| 2880 | \\1:1: note: precede identifier with '.' for an enum literal | |
| 2881 | \\ | |
| 2882 | , "{}", .{status}); | |
| 2883 | } | |
| 2884 | ||
| 2885 | { | |
| 2886 | var status: Status = .{}; | |
| 2887 | defer status.deinit(gpa); | |
| 2888 | try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-foo", &status, .{})); | |
| 2889 | try std.testing.expectFmt( | |
| 2890 | "1:1: error: expected number or 'inf' after '-'\n", | |
| 2891 | "{}", | |
| 2892 | .{status}, | |
| 2893 | ); | |
| 2894 | } | |
| 2895 | ||
| 2896 | // Non float as float | |
| 2897 | { | |
| 2898 | var status: Status = .{}; | |
| 2899 | defer status.deinit(gpa); | |
| 2900 | try std.testing.expectError( | |
| 2901 | error.ParseZon, | |
| 2902 | fromSlice(f32, gpa, "\"foo\"", &status, .{}), | |
| 2903 | ); | |
| 2904 | try std.testing.expectFmt("1:1: error: expected type 'f32'\n", "{}", .{status}); | |
| 2905 | } | |
| 2906 | } | |
| 2907 | ||
| 2908 | test "std.zon free on error" { | |
| 2909 | // Test freeing partially allocated structs | |
| 2910 | { | |
| 2911 | const Struct = struct { | |
| 2912 | x: []const u8, | |
| 2913 | y: []const u8, | |
| 2914 | z: bool, | |
| 2915 | }; | |
| 2916 | try std.testing.expectError(error.ParseZon, fromSlice(Struct, std.testing.allocator, | |
| 2917 | \\.{ | |
| 2918 | \\ .x = "hello", | |
| 2919 | \\ .y = "world", | |
| 2920 | \\ .z = "fail", | |
| 2921 | \\} | |
| 2922 | , null, .{})); | |
| 2923 | } | |
| 2924 | ||
| 2925 | // Test freeing partially allocated tuples | |
| 2926 | { | |
| 2927 | const Struct = struct { | |
| 2928 | []const u8, | |
| 2929 | []const u8, | |
| 2930 | bool, | |
| 2931 | }; | |
| 2932 | try std.testing.expectError(error.ParseZon, fromSlice(Struct, std.testing.allocator, | |
| 2933 | \\.{ | |
| 2934 | \\ "hello", | |
| 2935 | \\ "world", | |
| 2936 | \\ "fail", | |
| 2937 | \\} | |
| 2938 | , null, .{})); | |
| 2939 | } | |
| 2940 | ||
| 2941 | // Test freeing structs with missing fields | |
| 2942 | { | |
| 2943 | const Struct = struct { | |
| 2944 | x: []const u8, | |
| 2945 | y: bool, | |
| 2946 | }; | |
| 2947 | try std.testing.expectError(error.ParseZon, fromSlice(Struct, std.testing.allocator, | |
| 2948 | \\.{ | |
| 2949 | \\ .x = "hello", | |
| 2950 | \\} | |
| 2951 | , null, .{})); | |
| 2952 | } | |
| 2953 | ||
| 2954 | // Test freeing partially allocated arrays | |
| 2955 | { | |
| 2956 | try std.testing.expectError(error.ParseZon, fromSlice( | |
| 2957 | [3][]const u8, | |
| 2958 | std.testing.allocator, | |
| 2959 | \\.{ | |
| 2960 | \\ "hello", | |
| 2961 | \\ false, | |
| 2962 | \\ false, | |
| 2963 | \\} | |
| 2964 | , | |
| 2965 | null, | |
| 2966 | .{}, | |
| 2967 | )); | |
| 2968 | } | |
| 2969 | ||
| 2970 | // Test freeing partially allocated slices | |
| 2971 | { | |
| 2972 | try std.testing.expectError(error.ParseZon, fromSlice( | |
| 2973 | [][]const u8, | |
| 2974 | std.testing.allocator, | |
| 2975 | \\.{ | |
| 2976 | \\ "hello", | |
| 2977 | \\ "world", | |
| 2978 | \\ false, | |
| 2979 | \\} | |
| 2980 | , | |
| 2981 | null, | |
| 2982 | .{}, | |
| 2983 | )); | |
| 2984 | } | |
| 2985 | ||
| 2986 | // We can parse types that can't be freed, as long as they contain no allocations, e.g. untagged | |
| 2987 | // unions. | |
| 2988 | try std.testing.expectEqual( | |
| 2989 | @as(f32, 1.5), | |
| 2990 | (try fromSlice(union { x: f32 }, std.testing.allocator, ".{ .x = 1.5 }", null, .{})).x, | |
| 2991 | ); | |
| 2992 | ||
| 2993 | // We can also parse types that can't be freed if it's impossible for an error to occur after | |
| 2994 | // the allocation, as is the case here. | |
| 2995 | { | |
| 2996 | const result = try fromSlice( | |
| 2997 | union { x: []const u8 }, | |
| 2998 | std.testing.allocator, | |
| 2999 | ".{ .x = \"foo\" }", | |
| 3000 | null, | |
| 3001 | .{}, | |
| 3002 | ); | |
| 3003 | defer free(std.testing.allocator, result.x); | |
| 3004 | try std.testing.expectEqualStrings("foo", result.x); | |
| 3005 | } | |
| 3006 | ||
| 3007 | // However, if it's possible we could get an error requiring we free the value, but the value | |
| 3008 | // cannot be freed (e.g. untagged unions) then we need to turn off `free_on_error` for it to | |
| 3009 | // compile. | |
| 3010 | { | |
| 3011 | const S = struct { | |
| 3012 | union { x: []const u8 }, | |
| 3013 | bool, | |
| 3014 | }; | |
| 3015 | const result = try fromSlice( | |
| 3016 | S, | |
| 3017 | std.testing.allocator, | |
| 3018 | ".{ .{ .x = \"foo\" }, true }", | |
| 3019 | null, | |
| 3020 | .{ .free_on_error = false }, | |
| 3021 | ); | |
| 3022 | defer free(std.testing.allocator, result[0].x); | |
| 3023 | try std.testing.expectEqualStrings("foo", result[0].x); | |
| 3024 | try std.testing.expect(result[1]); | |
| 3025 | } | |
| 3026 | ||
| 3027 | // Again but for structs. | |
| 3028 | { | |
| 3029 | const S = struct { | |
| 3030 | a: union { x: []const u8 }, | |
| 3031 | b: bool, | |
| 3032 | }; | |
| 3033 | const result = try fromSlice( | |
| 3034 | S, | |
| 3035 | std.testing.allocator, | |
| 3036 | ".{ .a = .{ .x = \"foo\" }, .b = true }", | |
| 3037 | null, | |
| 3038 | .{ | |
| 3039 | .free_on_error = false, | |
| 3040 | }, | |
| 3041 | ); | |
| 3042 | defer free(std.testing.allocator, result.a.x); | |
| 3043 | try std.testing.expectEqualStrings("foo", result.a.x); | |
| 3044 | try std.testing.expect(result.b); | |
| 3045 | } | |
| 3046 | ||
| 3047 | // Again but for arrays. | |
| 3048 | { | |
| 3049 | const S = [2]union { x: []const u8 }; | |
| 3050 | const result = try fromSlice( | |
| 3051 | S, | |
| 3052 | std.testing.allocator, | |
| 3053 | ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }", | |
| 3054 | null, | |
| 3055 | .{ | |
| 3056 | .free_on_error = false, | |
| 3057 | }, | |
| 3058 | ); | |
| 3059 | defer free(std.testing.allocator, result[0].x); | |
| 3060 | defer free(std.testing.allocator, result[1].x); | |
| 3061 | try std.testing.expectEqualStrings("foo", result[0].x); | |
| 3062 | try std.testing.expectEqualStrings("bar", result[1].x); | |
| 3063 | } | |
| 3064 | ||
| 3065 | // Again but for slices. | |
| 3066 | { | |
| 3067 | const S = []union { x: []const u8 }; | |
| 3068 | const result = try fromSlice( | |
| 3069 | S, | |
| 3070 | std.testing.allocator, | |
| 3071 | ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }", | |
| 3072 | null, | |
| 3073 | .{ | |
| 3074 | .free_on_error = false, | |
| 3075 | }, | |
| 3076 | ); | |
| 3077 | defer std.testing.allocator.free(result); | |
| 3078 | defer free(std.testing.allocator, result[0].x); | |
| 3079 | defer free(std.testing.allocator, result[1].x); | |
| 3080 | try std.testing.expectEqualStrings("foo", result[0].x); | |
| 3081 | try std.testing.expectEqualStrings("bar", result[1].x); | |
| 3082 | } | |
| 3083 | } | |
| 3084 | ||
| 3085 | test "std.zon vector" { | |
| 3086 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/15330 | |
| 3087 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/15329 | |
| 3088 | ||
| 3089 | const gpa = std.testing.allocator; | |
| 3090 | ||
| 3091 | // Passing cases | |
| 3092 | try std.testing.expectEqual( | |
| 3093 | @Vector(0, bool){}, | |
| 3094 | try fromSlice(@Vector(0, bool), gpa, ".{}", null, .{}), | |
| 3095 | ); | |
| 3096 | try std.testing.expectEqual( | |
| 3097 | @Vector(3, bool){ true, false, true }, | |
| 3098 | try fromSlice(@Vector(3, bool), gpa, ".{true, false, true}", null, .{}), | |
| 3099 | ); | |
| 3100 | ||
| 3101 | try std.testing.expectEqual( | |
| 3102 | @Vector(0, f32){}, | |
| 3103 | try fromSlice(@Vector(0, f32), gpa, ".{}", null, .{}), | |
| 3104 | ); | |
| 3105 | try std.testing.expectEqual( | |
| 3106 | @Vector(3, f32){ 1.5, 2.5, 3.5 }, | |
| 3107 | try fromSlice(@Vector(3, f32), gpa, ".{1.5, 2.5, 3.5}", null, .{}), | |
| 3108 | ); | |
| 3109 | ||
| 3110 | try std.testing.expectEqual( | |
| 3111 | @Vector(0, u8){}, | |
| 3112 | try fromSlice(@Vector(0, u8), gpa, ".{}", null, .{}), | |
| 3113 | ); | |
| 3114 | try std.testing.expectEqual( | |
| 3115 | @Vector(3, u8){ 2, 4, 6 }, | |
| 3116 | try fromSlice(@Vector(3, u8), gpa, ".{2, 4, 6}", null, .{}), | |
| 3117 | ); | |
| 3118 | ||
| 3119 | { | |
| 3120 | try std.testing.expectEqual( | |
| 3121 | @Vector(0, *const u8){}, | |
| 3122 | try fromSlice(@Vector(0, *const u8), gpa, ".{}", null, .{}), | |
| 3123 | ); | |
| 3124 | const pointers = try fromSlice(@Vector(3, *const u8), gpa, ".{2, 4, 6}", null, .{}); | |
| 3125 | defer free(gpa, pointers); | |
| 3126 | try std.testing.expectEqualDeep(@Vector(3, *const u8){ &2, &4, &6 }, pointers); | |
| 3127 | } | |
| 3128 | ||
| 3129 | { | |
| 3130 | try std.testing.expectEqual( | |
| 3131 | @Vector(0, ?*const u8){}, | |
| 3132 | try fromSlice(@Vector(0, ?*const u8), gpa, ".{}", null, .{}), | |
| 3133 | ); | |
| 3134 | const pointers = try fromSlice(@Vector(3, ?*const u8), gpa, ".{2, null, 6}", null, .{}); | |
| 3135 | defer free(gpa, pointers); | |
| 3136 | try std.testing.expectEqualDeep(@Vector(3, ?*const u8){ &2, null, &6 }, pointers); | |
| 3137 | } | |
| 3138 | ||
| 3139 | // Too few fields | |
| 3140 | { | |
| 3141 | var status: Status = .{}; | |
| 3142 | defer status.deinit(gpa); | |
| 3143 | try std.testing.expectError( | |
| 3144 | error.ParseZon, | |
| 3145 | fromSlice(@Vector(2, f32), gpa, ".{0.5}", &status, .{}), | |
| 3146 | ); | |
| 3147 | try std.testing.expectFmt( | |
| 3148 | "1:2: error: expected 2 vector elements; found 1\n", | |
| 3149 | "{}", | |
| 3150 | .{status}, | |
| 3151 | ); | |
| 3152 | } | |
| 3153 | ||
| 3154 | // Too many fields | |
| 3155 | { | |
| 3156 | var status: Status = .{}; | |
| 3157 | defer status.deinit(gpa); | |
| 3158 | try std.testing.expectError( | |
| 3159 | error.ParseZon, | |
| 3160 | fromSlice(@Vector(2, f32), gpa, ".{0.5, 1.5, 2.5}", &status, .{}), | |
| 3161 | ); | |
| 3162 | try std.testing.expectFmt( | |
| 3163 | "1:2: error: expected 2 vector elements; found 3\n", | |
| 3164 | "{}", | |
| 3165 | .{status}, | |
| 3166 | ); | |
| 3167 | } | |
| 3168 | ||
| 3169 | // Wrong type fields | |
| 3170 | { | |
| 3171 | var status: Status = .{}; | |
| 3172 | defer status.deinit(gpa); | |
| 3173 | try std.testing.expectError( | |
| 3174 | error.ParseZon, | |
| 3175 | fromSlice(@Vector(3, f32), gpa, ".{0.5, true, 2.5}", &status, .{}), | |
| 3176 | ); | |
| 3177 | try std.testing.expectFmt( | |
| 3178 | "1:8: error: expected type 'f32'\n", | |
| 3179 | "{}", | |
| 3180 | .{status}, | |
| 3181 | ); | |
| 3182 | } | |
| 3183 | ||
| 3184 | // Wrong type | |
| 3185 | { | |
| 3186 | var status: Status = .{}; | |
| 3187 | defer status.deinit(gpa); | |
| 3188 | try std.testing.expectError( | |
| 3189 | error.ParseZon, | |
| 3190 | fromSlice(@Vector(3, u8), gpa, "true", &status, .{}), | |
| 3191 | ); | |
| 3192 | try std.testing.expectFmt("1:1: error: expected type '@Vector(3, u8)'\n", "{}", .{status}); | |
| 3193 | } | |
| 3194 | ||
| 3195 | // Elements should get freed on error | |
| 3196 | { | |
| 3197 | var status: Status = .{}; | |
| 3198 | defer status.deinit(gpa); | |
| 3199 | try std.testing.expectError( | |
| 3200 | error.ParseZon, | |
| 3201 | fromSlice(@Vector(3, *u8), gpa, ".{1, true, 3}", &status, .{}), | |
| 3202 | ); | |
| 3203 | try std.testing.expectFmt("1:6: error: expected type 'u8'\n", "{}", .{status}); | |
| 3204 | } | |
| 3205 | } | |
| 3206 | ||
| 3207 | test "std.zon add pointers" { | |
| 3208 | const gpa = std.testing.allocator; | |
| 3209 | ||
| 3210 | // Primitive with varying levels of pointers | |
| 3211 | { | |
| 3212 | const result = try fromSlice(*u32, gpa, "10", null, .{}); | |
| 3213 | defer free(gpa, result); | |
| 3214 | try std.testing.expectEqual(@as(u32, 10), result.*); | |
| 3215 | } | |
| 3216 | ||
| 3217 | { | |
| 3218 | const result = try fromSlice(**u32, gpa, "10", null, .{}); | |
| 3219 | defer free(gpa, result); | |
| 3220 | try std.testing.expectEqual(@as(u32, 10), result.*.*); | |
| 3221 | } | |
| 3222 | ||
| 3223 | { | |
| 3224 | const result = try fromSlice(***u32, gpa, "10", null, .{}); | |
| 3225 | defer free(gpa, result); | |
| 3226 | try std.testing.expectEqual(@as(u32, 10), result.*.*.*); | |
| 3227 | } | |
| 3228 | ||
| 3229 | // Primitive optional with varying levels of pointers | |
| 3230 | { | |
| 3231 | const some = try fromSlice(?*u32, gpa, "10", null, .{}); | |
| 3232 | defer free(gpa, some); | |
| 3233 | try std.testing.expectEqual(@as(u32, 10), some.?.*); | |
| 3234 | ||
| 3235 | const none = try fromSlice(?*u32, gpa, "null", null, .{}); | |
| 3236 | defer free(gpa, none); | |
| 3237 | try std.testing.expectEqual(null, none); | |
| 3238 | } | |
| 3239 | ||
| 3240 | { | |
| 3241 | const some = try fromSlice(*?u32, gpa, "10", null, .{}); | |
| 3242 | defer free(gpa, some); | |
| 3243 | try std.testing.expectEqual(@as(u32, 10), some.*.?); | |
| 3244 | ||
| 3245 | const none = try fromSlice(*?u32, gpa, "null", null, .{}); | |
| 3246 | defer free(gpa, none); | |
| 3247 | try std.testing.expectEqual(null, none.*); | |
| 3248 | } | |
| 3249 | ||
| 3250 | { | |
| 3251 | const some = try fromSlice(?**u32, gpa, "10", null, .{}); | |
| 3252 | defer free(gpa, some); | |
| 3253 | try std.testing.expectEqual(@as(u32, 10), some.?.*.*); | |
| 3254 | ||
| 3255 | const none = try fromSlice(?**u32, gpa, "null", null, .{}); | |
| 3256 | defer free(gpa, none); | |
| 3257 | try std.testing.expectEqual(null, none); | |
| 3258 | } | |
| 3259 | ||
| 3260 | { | |
| 3261 | const some = try fromSlice(*?*u32, gpa, "10", null, .{}); | |
| 3262 | defer free(gpa, some); | |
| 3263 | try std.testing.expectEqual(@as(u32, 10), some.*.?.*); | |
| 3264 | ||
| 3265 | const none = try fromSlice(*?*u32, gpa, "null", null, .{}); | |
| 3266 | defer free(gpa, none); | |
| 3267 | try std.testing.expectEqual(null, none.*); | |
| 3268 | } | |
| 3269 | ||
| 3270 | { | |
| 3271 | const some = try fromSlice(**?u32, gpa, "10", null, .{}); | |
| 3272 | defer free(gpa, some); | |
| 3273 | try std.testing.expectEqual(@as(u32, 10), some.*.*.?); | |
| 3274 | ||
| 3275 | const none = try fromSlice(**?u32, gpa, "null", null, .{}); | |
| 3276 | defer free(gpa, none); | |
| 3277 | try std.testing.expectEqual(null, none.*.*); | |
| 3278 | } | |
| 3279 | ||
| 3280 | // Pointer to an array | |
| 3281 | { | |
| 3282 | const result = try fromSlice(*[3]u8, gpa, ".{ 1, 2, 3 }", null, .{}); | |
| 3283 | defer free(gpa, result); | |
| 3284 | try std.testing.expectEqual([3]u8{ 1, 2, 3 }, result.*); | |
| 3285 | } | |
| 3286 | ||
| 3287 | // A complicated type with nested internal pointers and string allocations | |
| 3288 | { | |
| 3289 | const Inner = struct { | |
| 3290 | f1: *const ?*const []const u8, | |
| 3291 | f2: *const ?*const []const u8, | |
| 3292 | }; | |
| 3293 | const Outer = struct { | |
| 3294 | f1: *const ?*const Inner, | |
| 3295 | f2: *const ?*const Inner, | |
| 3296 | }; | |
| 3297 | const expected: Outer = .{ | |
| 3298 | .f1 = &&.{ | |
| 3299 | .f1 = &null, | |
| 3300 | .f2 = &&"foo", | |
| 3301 | }, | |
| 3302 | .f2 = &null, | |
| 3303 | }; | |
| 3304 | ||
| 3305 | const found = try fromSlice(?*Outer, gpa, | |
| 3306 | \\.{ | |
| 3307 | \\ .f1 = .{ | |
| 3308 | \\ .f1 = null, | |
| 3309 | \\ .f2 = "foo", | |
| 3310 | \\ }, | |
| 3311 | \\ .f2 = null, | |
| 3312 | \\} | |
| 3313 | , null, .{}); | |
| 3314 | defer free(gpa, found); | |
| 3315 | ||
| 3316 | try std.testing.expectEqualDeep(expected, found.?.*); | |
| 3317 | } | |
| 3318 | ||
| 3319 | // Test that optional types are flattened correctly in errors | |
| 3320 | { | |
| 3321 | var status: Status = .{}; | |
| 3322 | defer status.deinit(gpa); | |
| 3323 | try std.testing.expectError( | |
| 3324 | error.ParseZon, | |
| 3325 | fromSlice(*const ?*const u8, gpa, "true", &status, .{}), | |
| 3326 | ); | |
| 3327 | try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{}", .{status}); | |
| 3328 | } | |
| 3329 | ||
| 3330 | { | |
| 3331 | var status: Status = .{}; | |
| 3332 | defer status.deinit(gpa); | |
| 3333 | try std.testing.expectError( | |
| 3334 | error.ParseZon, | |
| 3335 | fromSlice(*const ?*const f32, gpa, "true", &status, .{}), | |
| 3336 | ); | |
| 3337 | try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{}", .{status}); | |
| 3338 | } | |
| 3339 | ||
| 3340 | { | |
| 3341 | var status: Status = .{}; | |
| 3342 | defer status.deinit(gpa); | |
| 3343 | try std.testing.expectError( | |
| 3344 | error.ParseZon, | |
| 3345 | fromSlice(*const ?*const @Vector(3, u8), gpa, "true", &status, .{}), | |
| 3346 | ); | |
| 3347 | try std.testing.expectFmt("1:1: error: expected type '?@Vector(3, u8)'\n", "{}", .{status}); | |
| 3348 | } | |
| 3349 | ||
| 3350 | { | |
| 3351 | var status: Status = .{}; | |
| 3352 | defer status.deinit(gpa); | |
| 3353 | try std.testing.expectError( | |
| 3354 | error.ParseZon, | |
| 3355 | fromSlice(*const ?*const bool, gpa, "10", &status, .{}), | |
| 3356 | ); | |
| 3357 | try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{}", .{status}); | |
| 3358 | } | |
| 3359 | ||
| 3360 | { | |
| 3361 | var status: Status = .{}; | |
| 3362 | defer status.deinit(gpa); | |
| 3363 | try std.testing.expectError( | |
| 3364 | error.ParseZon, | |
| 3365 | fromSlice(*const ?*const struct { a: i32 }, gpa, "true", &status, .{}), | |
| 3366 | ); | |
| 3367 | try std.testing.expectFmt("1:1: error: expected optional struct\n", "{}", .{status}); | |
| 3368 | } | |
| 3369 | ||
| 3370 | { | |
| 3371 | var status: Status = .{}; | |
| 3372 | defer status.deinit(gpa); | |
| 3373 | try std.testing.expectError( | |
| 3374 | error.ParseZon, | |
| 3375 | fromSlice(*const ?*const struct { i32 }, gpa, "true", &status, .{}), | |
| 3376 | ); | |
| 3377 | try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{}", .{status}); | |
| 3378 | } | |
| 3379 | ||
| 3380 | { | |
| 3381 | var status: Status = .{}; | |
| 3382 | defer status.deinit(gpa); | |
| 3383 | try std.testing.expectError( | |
| 3384 | error.ParseZon, | |
| 3385 | fromSlice(*const ?*const union { x: void }, gpa, "true", &status, .{}), | |
| 3386 | ); | |
| 3387 | try std.testing.expectFmt("1:1: error: expected optional union\n", "{}", .{status}); | |
| 3388 | } | |
| 3389 | ||
| 3390 | { | |
| 3391 | var status: Status = .{}; | |
| 3392 | defer status.deinit(gpa); | |
| 3393 | try std.testing.expectError( | |
| 3394 | error.ParseZon, | |
| 3395 | fromSlice(*const ?*const [3]u8, gpa, "true", &status, .{}), | |
| 3396 | ); | |
| 3397 | try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{status}); | |
| 3398 | } | |
| 3399 | ||
| 3400 | { | |
| 3401 | var status: Status = .{}; | |
| 3402 | defer status.deinit(gpa); | |
| 3403 | try std.testing.expectError( | |
| 3404 | error.ParseZon, | |
| 3405 | fromSlice(?[3]u8, gpa, "true", &status, .{}), | |
| 3406 | ); | |
| 3407 | try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{status}); | |
| 3408 | } | |
| 3409 | ||
| 3410 | { | |
| 3411 | var status: Status = .{}; | |
| 3412 | defer status.deinit(gpa); | |
| 3413 | try std.testing.expectError( | |
| 3414 | error.ParseZon, | |
| 3415 | fromSlice(*const ?*const []u8, gpa, "true", &status, .{}), | |
| 3416 | ); | |
| 3417 | try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{status}); | |
| 3418 | } | |
| 3419 | ||
| 3420 | { | |
| 3421 | var status: Status = .{}; | |
| 3422 | defer status.deinit(gpa); | |
| 3423 | try std.testing.expectError( | |
| 3424 | error.ParseZon, | |
| 3425 | fromSlice(?[]u8, gpa, "true", &status, .{}), | |
| 3426 | ); | |
| 3427 | try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{status}); | |
| 3428 | } | |
| 3429 | ||
| 3430 | { | |
| 3431 | var status: Status = .{}; | |
| 3432 | defer status.deinit(gpa); | |
| 3433 | try std.testing.expectError( | |
| 3434 | error.ParseZon, | |
| 3435 | fromSlice(*const ?*const []const u8, gpa, "true", &status, .{}), | |
| 3436 | ); | |
| 3437 | try std.testing.expectFmt("1:1: error: expected optional string\n", "{}", .{status}); | |
| 3438 | } | |
| 3439 | ||
| 3440 | { | |
| 3441 | var status: Status = .{}; | |
| 3442 | defer status.deinit(gpa); | |
| 3443 | try std.testing.expectError( | |
| 3444 | error.ParseZon, | |
| 3445 | fromSlice(*const ?*const enum { foo }, gpa, "true", &status, .{}), | |
| 3446 | ); | |
| 3447 | try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{}", .{status}); | |
| 3448 | } | |
| 3449 | } |
lib/std/zon/stringify.zig created+2306| ... | ... | @@ -0,0 +1,2306 @@ |
| 1 | //! ZON can be serialized with `serialize`. | |
| 2 | //! | |
| 3 | //! The following functions are provided for serializing recursive types: | |
| 4 | //! * `serializeMaxDepth` | |
| 5 | //! * `serializeArbitraryDepth` | |
| 6 | //! | |
| 7 | //! For additional control over serialization, see `Serializer`. | |
| 8 | //! | |
| 9 | //! The following types and any types that contain them may not be serialized: | |
| 10 | //! * `type` | |
| 11 | //! * `void`, except as a union payload | |
| 12 | //! * `noreturn` | |
| 13 | //! * Error sets/error unions | |
| 14 | //! * Untagged unions | |
| 15 | //! * Many-pointers or C-pointers | |
| 16 | //! * Opaque types, including `anyopaque` | |
| 17 | //! * Async frame types, including `anyframe` and `anyframe->T` | |
| 18 | //! * Functions | |
| 19 | //! | |
| 20 | //! All other types are valid. Unsupported types will fail to serialize at compile time. Pointers | |
| 21 | //! are followed. | |
| 22 | ||
| 23 | const std = @import("std"); | |
| 24 | const assert = std.debug.assert; | |
| 25 | ||
| 26 | /// Options for `serialize`. | |
| 27 | pub const SerializeOptions = struct { | |
| 28 | /// If false, whitespace is omitted. Otherwise whitespace is emitted in standard Zig style. | |
| 29 | whitespace: bool = true, | |
| 30 | /// Determines when to emit Unicode code point literals as opposed to integer literals. | |
| 31 | emit_codepoint_literals: EmitCodepointLiterals = .never, | |
| 32 | /// If true, slices of `u8`s, and pointers to arrays of `u8` are serialized as containers. | |
| 33 | /// Otherwise they are serialized as string literals. | |
| 34 | emit_strings_as_containers: bool = false, | |
| 35 | /// If false, struct fields are not written if they are equal to their default value. Comparison | |
| 36 | /// is done by `std.meta.eql`. | |
| 37 | emit_default_optional_fields: bool = true, | |
| 38 | }; | |
| 39 | ||
| 40 | /// Serialize the given value as ZON. | |
| 41 | /// | |
| 42 | /// It is asserted at comptime that `@TypeOf(val)` is not a recursive type. | |
| 43 | pub fn serialize( | |
| 44 | val: anytype, | |
| 45 | options: SerializeOptions, | |
| 46 | writer: anytype, | |
| 47 | ) @TypeOf(writer).Error!void { | |
| 48 | var sz = serializer(writer, .{ | |
| 49 | .whitespace = options.whitespace, | |
| 50 | }); | |
| 51 | try sz.value(val, .{ | |
| 52 | .emit_codepoint_literals = options.emit_codepoint_literals, | |
| 53 | .emit_strings_as_containers = options.emit_strings_as_containers, | |
| 54 | .emit_default_optional_fields = options.emit_default_optional_fields, | |
| 55 | }); | |
| 56 | } | |
| 57 | ||
| 58 | /// Like `serialize`, but recursive types are allowed. | |
| 59 | /// | |
| 60 | /// Returns `error.ExceededMaxDepth` if `depth` is exceeded. Every nested value adds one to a | |
| 61 | /// value's depth. | |
| 62 | pub fn serializeMaxDepth( | |
| 63 | val: anytype, | |
| 64 | options: SerializeOptions, | |
| 65 | writer: anytype, | |
| 66 | depth: usize, | |
| 67 | ) (@TypeOf(writer).Error || error{ExceededMaxDepth})!void { | |
| 68 | var sz = serializer(writer, .{ | |
| 69 | .whitespace = options.whitespace, | |
| 70 | }); | |
| 71 | try sz.valueMaxDepth(val, .{ | |
| 72 | .emit_codepoint_literals = options.emit_codepoint_literals, | |
| 73 | .emit_strings_as_containers = options.emit_strings_as_containers, | |
| 74 | .emit_default_optional_fields = options.emit_default_optional_fields, | |
| 75 | }, depth); | |
| 76 | } | |
| 77 | ||
| 78 | /// Like `serialize`, but recursive types are allowed. | |
| 79 | /// | |
| 80 | /// It is the caller's responsibility to ensure that `val` does not contain cycles. | |
| 81 | pub fn serializeArbitraryDepth( | |
| 82 | val: anytype, | |
| 83 | options: SerializeOptions, | |
| 84 | writer: anytype, | |
| 85 | ) @TypeOf(writer).Error!void { | |
| 86 | var sz = serializer(writer, .{ | |
| 87 | .whitespace = options.whitespace, | |
| 88 | }); | |
| 89 | try sz.valueArbitraryDepth(val, .{ | |
| 90 | .emit_codepoint_literals = options.emit_codepoint_literals, | |
| 91 | .emit_strings_as_containers = options.emit_strings_as_containers, | |
| 92 | .emit_default_optional_fields = options.emit_default_optional_fields, | |
| 93 | }); | |
| 94 | } | |
| 95 | ||
| 96 | fn typeIsRecursive(comptime T: type) bool { | |
| 97 | return comptime typeIsRecursiveImpl(T, &.{}); | |
| 98 | } | |
| 99 | ||
| 100 | fn typeIsRecursiveImpl(comptime T: type, comptime prev_visited: []const type) bool { | |
| 101 | for (prev_visited) |V| { | |
| 102 | if (V == T) return true; | |
| 103 | } | |
| 104 | const visited = prev_visited ++ .{T}; | |
| 105 | ||
| 106 | return switch (@typeInfo(T)) { | |
| 107 | .pointer => |pointer| typeIsRecursiveImpl(pointer.child, visited), | |
| 108 | .optional => |optional| typeIsRecursiveImpl(optional.child, visited), | |
| 109 | .array => |array| typeIsRecursiveImpl(array.child, visited), | |
| 110 | .vector => |vector| typeIsRecursiveImpl(vector.child, visited), | |
| 111 | .@"struct" => |@"struct"| for (@"struct".fields) |field| { | |
| 112 | if (typeIsRecursiveImpl(field.type, visited)) break true; | |
| 113 | } else false, | |
| 114 | .@"union" => |@"union"| inline for (@"union".fields) |field| { | |
| 115 | if (typeIsRecursiveImpl(field.type, visited)) break true; | |
| 116 | } else false, | |
| 117 | else => false, | |
| 118 | }; | |
| 119 | } | |
| 120 | ||
| 121 | fn canSerializeType(T: type) bool { | |
| 122 | comptime return canSerializeTypeInner(T, &.{}, false); | |
| 123 | } | |
| 124 | ||
| 125 | fn canSerializeTypeInner( | |
| 126 | T: type, | |
| 127 | /// Visited structs and unions, to avoid infinite recursion. | |
| 128 | /// Tracking more types is unnecessary, and a little complex due to optional nesting. | |
| 129 | visited: []const type, | |
| 130 | parent_is_optional: bool, | |
| 131 | ) bool { | |
| 132 | return switch (@typeInfo(T)) { | |
| 133 | .bool, | |
| 134 | .int, | |
| 135 | .float, | |
| 136 | .comptime_float, | |
| 137 | .comptime_int, | |
| 138 | .null, | |
| 139 | .enum_literal, | |
| 140 | => true, | |
| 141 | ||
| 142 | .noreturn, | |
| 143 | .void, | |
| 144 | .type, | |
| 145 | .undefined, | |
| 146 | .error_union, | |
| 147 | .error_set, | |
| 148 | .@"fn", | |
| 149 | .frame, | |
| 150 | .@"anyframe", | |
| 151 | .@"opaque", | |
| 152 | => false, | |
| 153 | ||
| 154 | .@"enum" => |@"enum"| @"enum".is_exhaustive, | |
| 155 | ||
| 156 | .pointer => |pointer| switch (pointer.size) { | |
| 157 | .one => canSerializeTypeInner(pointer.child, visited, parent_is_optional), | |
| 158 | .slice => canSerializeTypeInner(pointer.child, visited, false), | |
| 159 | .many, .c => false, | |
| 160 | }, | |
| 161 | ||
| 162 | .optional => |optional| if (parent_is_optional) | |
| 163 | false | |
| 164 | else | |
| 165 | canSerializeTypeInner(optional.child, visited, true), | |
| 166 | ||
| 167 | .array => |array| canSerializeTypeInner(array.child, visited, false), | |
| 168 | .vector => |vector| canSerializeTypeInner(vector.child, visited, false), | |
| 169 | ||
| 170 | .@"struct" => |@"struct"| { | |
| 171 | for (visited) |V| if (T == V) return true; | |
| 172 | const new_visited = visited ++ .{T}; | |
| 173 | for (@"struct".fields) |field| { | |
| 174 | if (!canSerializeTypeInner(field.type, new_visited, false)) return false; | |
| 175 | } | |
| 176 | return true; | |
| 177 | }, | |
| 178 | .@"union" => |@"union"| { | |
| 179 | for (visited) |V| if (T == V) return true; | |
| 180 | const new_visited = visited ++ .{T}; | |
| 181 | if (@"union".tag_type == null) return false; | |
| 182 | for (@"union".fields) |field| { | |
| 183 | if (field.type != void and !canSerializeTypeInner(field.type, new_visited, false)) { | |
| 184 | return false; | |
| 185 | } | |
| 186 | } | |
| 187 | return true; | |
| 188 | }, | |
| 189 | }; | |
| 190 | } | |
| 191 | ||
| 192 | fn isNestedOptional(T: type) bool { | |
| 193 | comptime switch (@typeInfo(T)) { | |
| 194 | .optional => |optional| return isNestedOptionalInner(optional.child), | |
| 195 | else => return false, | |
| 196 | }; | |
| 197 | } | |
| 198 | ||
| 199 | fn isNestedOptionalInner(T: type) bool { | |
| 200 | switch (@typeInfo(T)) { | |
| 201 | .pointer => |pointer| { | |
| 202 | if (pointer.size == .one) { | |
| 203 | return isNestedOptionalInner(pointer.child); | |
| 204 | } else { | |
| 205 | return false; | |
| 206 | } | |
| 207 | }, | |
| 208 | .optional => return true, | |
| 209 | else => return false, | |
| 210 | } | |
| 211 | } | |
| 212 | ||
| 213 | test "std.zon stringify canSerializeType" { | |
| 214 | try std.testing.expect(!comptime canSerializeType(void)); | |
| 215 | try std.testing.expect(!comptime canSerializeType(struct { f: [*]u8 })); | |
| 216 | try std.testing.expect(!comptime canSerializeType(struct { error{foo} })); | |
| 217 | try std.testing.expect(!comptime canSerializeType(union(enum) { a: void, f: [*c]u8 })); | |
| 218 | try std.testing.expect(!comptime canSerializeType(@Vector(0, [*c]u8))); | |
| 219 | try std.testing.expect(!comptime canSerializeType(*?[*c]u8)); | |
| 220 | try std.testing.expect(!comptime canSerializeType(enum(u8) { _ })); | |
| 221 | try std.testing.expect(!comptime canSerializeType(union { foo: void })); | |
| 222 | try std.testing.expect(comptime canSerializeType(union(enum) { foo: void })); | |
| 223 | try std.testing.expect(comptime canSerializeType(comptime_float)); | |
| 224 | try std.testing.expect(comptime canSerializeType(comptime_int)); | |
| 225 | try std.testing.expect(!comptime canSerializeType(struct { comptime foo: ??u8 = null })); | |
| 226 | try std.testing.expect(comptime canSerializeType(@TypeOf(.foo))); | |
| 227 | try std.testing.expect(comptime canSerializeType(?u8)); | |
| 228 | try std.testing.expect(comptime canSerializeType(*?*u8)); | |
| 229 | try std.testing.expect(comptime canSerializeType(?struct { | |
| 230 | foo: ?struct { | |
| 231 | ?union(enum) { | |
| 232 | a: ?@Vector(0, ?*u8), | |
| 233 | }, | |
| 234 | ?struct { | |
| 235 | f: ?[]?u8, | |
| 236 | }, | |
| 237 | }, | |
| 238 | })); | |
| 239 | try std.testing.expect(!comptime canSerializeType(??u8)); | |
| 240 | try std.testing.expect(!comptime canSerializeType(?*?u8)); | |
| 241 | try std.testing.expect(!comptime canSerializeType(*?*?*u8)); | |
| 242 | try std.testing.expect(comptime canSerializeType(struct { x: comptime_int = 2 })); | |
| 243 | try std.testing.expect(comptime canSerializeType(struct { x: comptime_float = 2 })); | |
| 244 | try std.testing.expect(comptime canSerializeType(struct { comptime_int })); | |
| 245 | try std.testing.expect(comptime canSerializeType(struct { comptime x: @TypeOf(.foo) = .foo })); | |
| 246 | const Recursive = struct { foo: ?*@This() }; | |
| 247 | try std.testing.expect(comptime canSerializeType(Recursive)); | |
| 248 | ||
| 249 | // Make sure we validate nested optional before we early out due to already having seen | |
| 250 | // a type recursion! | |
| 251 | try std.testing.expect(!comptime canSerializeType(struct { | |
| 252 | add_to_visited: ?u8, | |
| 253 | retrieve_from_visited: ??u8, | |
| 254 | })); | |
| 255 | } | |
| 256 | ||
| 257 | test "std.zon typeIsRecursive" { | |
| 258 | try std.testing.expect(!typeIsRecursive(bool)); | |
| 259 | try std.testing.expect(!typeIsRecursive(struct { x: i32, y: i32 })); | |
| 260 | try std.testing.expect(!typeIsRecursive(struct { i32, i32 })); | |
| 261 | try std.testing.expect(typeIsRecursive(struct { x: i32, y: i32, z: *@This() })); | |
| 262 | try std.testing.expect(typeIsRecursive(struct { | |
| 263 | a: struct { | |
| 264 | const A = @This(); | |
| 265 | b: struct { | |
| 266 | c: *struct { | |
| 267 | a: ?A, | |
| 268 | }, | |
| 269 | }, | |
| 270 | }, | |
| 271 | })); | |
| 272 | try std.testing.expect(typeIsRecursive(struct { | |
| 273 | a: [3]*@This(), | |
| 274 | })); | |
| 275 | try std.testing.expect(typeIsRecursive(struct { | |
| 276 | a: union { a: i32, b: *@This() }, | |
| 277 | })); | |
| 278 | } | |
| 279 | ||
| 280 | fn checkValueDepth(val: anytype, depth: usize) error{ExceededMaxDepth}!void { | |
| 281 | if (depth == 0) return error.ExceededMaxDepth; | |
| 282 | const child_depth = depth - 1; | |
| 283 | ||
| 284 | switch (@typeInfo(@TypeOf(val))) { | |
| 285 | .pointer => |pointer| switch (pointer.size) { | |
| 286 | .one => try checkValueDepth(val.*, child_depth), | |
| 287 | .slice => for (val) |item| { | |
| 288 | try checkValueDepth(item, child_depth); | |
| 289 | }, | |
| 290 | .c, .many => {}, | |
| 291 | }, | |
| 292 | .array => for (val) |item| { | |
| 293 | try checkValueDepth(item, child_depth); | |
| 294 | }, | |
| 295 | .@"struct" => |@"struct"| inline for (@"struct".fields) |field_info| { | |
| 296 | try checkValueDepth(@field(val, field_info.name), child_depth); | |
| 297 | }, | |
| 298 | .@"union" => |@"union"| if (@"union".tag_type == null) { | |
| 299 | return; | |
| 300 | } else switch (val) { | |
| 301 | inline else => |payload| { | |
| 302 | return checkValueDepth(payload, child_depth); | |
| 303 | }, | |
| 304 | }, | |
| 305 | .optional => if (val) |inner| try checkValueDepth(inner, child_depth), | |
| 306 | else => {}, | |
| 307 | } | |
| 308 | } | |
| 309 | ||
| 310 | fn expectValueDepthEquals(expected: usize, value: anytype) !void { | |
| 311 | try checkValueDepth(value, expected); | |
| 312 | try std.testing.expectError(error.ExceededMaxDepth, checkValueDepth(value, expected - 1)); | |
| 313 | } | |
| 314 | ||
| 315 | test "std.zon checkValueDepth" { | |
| 316 | try expectValueDepthEquals(1, 10); | |
| 317 | try expectValueDepthEquals(2, .{ .x = 1, .y = 2 }); | |
| 318 | try expectValueDepthEquals(2, .{ 1, 2 }); | |
| 319 | try expectValueDepthEquals(3, .{ 1, .{ 2, 3 } }); | |
| 320 | try expectValueDepthEquals(3, .{ .{ 1, 2 }, 3 }); | |
| 321 | try expectValueDepthEquals(3, .{ .x = 0, .y = 1, .z = .{ .x = 3 } }); | |
| 322 | try expectValueDepthEquals(3, .{ .x = 0, .y = .{ .x = 1 }, .z = 2 }); | |
| 323 | try expectValueDepthEquals(3, .{ .x = .{ .x = 0 }, .y = 1, .z = 2 }); | |
| 324 | try expectValueDepthEquals(2, @as(?u32, 1)); | |
| 325 | try expectValueDepthEquals(1, @as(?u32, null)); | |
| 326 | try expectValueDepthEquals(1, null); | |
| 327 | try expectValueDepthEquals(2, &1); | |
| 328 | try expectValueDepthEquals(3, &@as(?u32, 1)); | |
| 329 | ||
| 330 | const Union = union(enum) { | |
| 331 | x: u32, | |
| 332 | y: struct { x: u32 }, | |
| 333 | }; | |
| 334 | try expectValueDepthEquals(2, Union{ .x = 1 }); | |
| 335 | try expectValueDepthEquals(3, Union{ .y = .{ .x = 1 } }); | |
| 336 | ||
| 337 | const Recurse = struct { r: ?*const @This() }; | |
| 338 | try expectValueDepthEquals(2, Recurse{ .r = null }); | |
| 339 | try expectValueDepthEquals(5, Recurse{ .r = &Recurse{ .r = null } }); | |
| 340 | try expectValueDepthEquals(8, Recurse{ .r = &Recurse{ .r = &Recurse{ .r = null } } }); | |
| 341 | ||
| 342 | try expectValueDepthEquals(2, @as([]const u8, &.{ 1, 2, 3 })); | |
| 343 | try expectValueDepthEquals(3, @as([]const []const u8, &.{&.{ 1, 2, 3 }})); | |
| 344 | } | |
| 345 | ||
| 346 | /// Options for `Serializer`. | |
| 347 | pub const SerializerOptions = struct { | |
| 348 | /// If false, only syntactically necessary whitespace is emitted. | |
| 349 | whitespace: bool = true, | |
| 350 | }; | |
| 351 | ||
| 352 | /// Determines when to emit Unicode code point literals as opposed to integer literals. | |
| 353 | pub const EmitCodepointLiterals = enum { | |
| 354 | /// Never emit Unicode code point literals. | |
| 355 | never, | |
| 356 | /// Emit Unicode code point literals for any `u8` in the printable ASCII range. | |
| 357 | printable_ascii, | |
| 358 | /// Emit Unicode code point literals for any unsigned integer with 21 bits or fewer | |
| 359 | /// whose value is a valid non-surrogate code point. | |
| 360 | always, | |
| 361 | ||
| 362 | /// If the value should be emitted as a Unicode codepoint, return it as a u21. | |
| 363 | fn emitAsCodepoint(self: @This(), val: anytype) ?u21 { | |
| 364 | // Rule out incompatible integer types | |
| 365 | switch (@typeInfo(@TypeOf(val))) { | |
| 366 | .int => |int_info| if (int_info.signedness == .signed or int_info.bits > 21) { | |
| 367 | return null; | |
| 368 | }, | |
| 369 | .comptime_int => {}, | |
| 370 | else => comptime unreachable, | |
| 371 | } | |
| 372 | ||
| 373 | // Return null if the value shouldn't be printed as a Unicode codepoint, or the value casted | |
| 374 | // to a u21 if it should. | |
| 375 | switch (self) { | |
| 376 | .always => { | |
| 377 | const c = std.math.cast(u21, val) orelse return null; | |
| 378 | if (!std.unicode.utf8ValidCodepoint(c)) return null; | |
| 379 | return c; | |
| 380 | }, | |
| 381 | .printable_ascii => { | |
| 382 | const c = std.math.cast(u8, val) orelse return null; | |
| 383 | if (!std.ascii.isPrint(c)) return null; | |
| 384 | return c; | |
| 385 | }, | |
| 386 | .never => { | |
| 387 | return null; | |
| 388 | }, | |
| 389 | } | |
| 390 | } | |
| 391 | }; | |
| 392 | ||
| 393 | /// Options for serialization of an individual value. | |
| 394 | /// | |
| 395 | /// See `SerializeOptions` for more information on these options. | |
| 396 | pub const ValueOptions = struct { | |
| 397 | emit_codepoint_literals: EmitCodepointLiterals = .never, | |
| 398 | emit_strings_as_containers: bool = false, | |
| 399 | emit_default_optional_fields: bool = true, | |
| 400 | }; | |
| 401 | ||
| 402 | /// Options for manual serialization of container types. | |
| 403 | pub const SerializeContainerOptions = struct { | |
| 404 | /// The whitespace style that should be used for this container. Ignored if whitespace is off. | |
| 405 | whitespace_style: union(enum) { | |
| 406 | /// If true, wrap every field. If false do not. | |
| 407 | wrap: bool, | |
| 408 | /// Automatically decide whether to wrap or not based on the number of fields. Following | |
| 409 | /// the standard rule of thumb, containers with more than two fields are wrapped. | |
| 410 | fields: usize, | |
| 411 | } = .{ .wrap = true }, | |
| 412 | ||
| 413 | fn shouldWrap(self: SerializeContainerOptions) bool { | |
| 414 | return switch (self.whitespace_style) { | |
| 415 | .wrap => |wrap| wrap, | |
| 416 | .fields => |fields| fields > 2, | |
| 417 | }; | |
| 418 | } | |
| 419 | }; | |
| 420 | ||
| 421 | /// Lower level control over serialization, you can create a new instance with `serializer`. | |
| 422 | /// | |
| 423 | /// Useful when you want control over which fields are serialized, how they're represented, | |
| 424 | /// or want to write a ZON object that does not exist in memory. | |
| 425 | /// | |
| 426 | /// You can serialize values with `value`. To serialize recursive types, the following are provided: | |
| 427 | /// * `valueMaxDepth` | |
| 428 | /// * `valueArbitraryDepth` | |
| 429 | /// | |
| 430 | /// You can also serialize values using specific notations: | |
| 431 | /// * `int` | |
| 432 | /// * `float` | |
| 433 | /// * `codePoint` | |
| 434 | /// * `tuple` | |
| 435 | /// * `tupleMaxDepth` | |
| 436 | /// * `tupleArbitraryDepth` | |
| 437 | /// * `string` | |
| 438 | /// * `multilineString` | |
| 439 | /// | |
| 440 | /// For manual serialization of containers, see: | |
| 441 | /// * `startStruct` | |
| 442 | /// * `startTuple` | |
| 443 | /// | |
| 444 | /// # Example | |
| 445 | /// ```zig | |
| 446 | /// var sz = serializer(writer, .{}); | |
| 447 | /// var vec2 = try sz.startStruct(.{}); | |
| 448 | /// try vec2.field("x", 1.5, .{}); | |
| 449 | /// try vec2.fieldPrefix(); | |
| 450 | /// try sz.value(2.5); | |
| 451 | /// try vec2.finish(); | |
| 452 | /// ``` | |
| 453 | pub fn Serializer(Writer: type) type { | |
| 454 | return struct { | |
| 455 | const Self = @This(); | |
| 456 | ||
| 457 | options: SerializerOptions, | |
| 458 | indent_level: u8, | |
| 459 | writer: Writer, | |
| 460 | ||
| 461 | /// Initialize a serializer. | |
| 462 | fn init(writer: Writer, options: SerializerOptions) Self { | |
| 463 | return .{ | |
| 464 | .options = options, | |
| 465 | .writer = writer, | |
| 466 | .indent_level = 0, | |
| 467 | }; | |
| 468 | } | |
| 469 | ||
| 470 | /// Serialize a value, similar to `serialize`. | |
| 471 | pub fn value(self: *Self, val: anytype, options: ValueOptions) Writer.Error!void { | |
| 472 | comptime assert(!typeIsRecursive(@TypeOf(val))); | |
| 473 | return self.valueArbitraryDepth(val, options); | |
| 474 | } | |
| 475 | ||
| 476 | /// Serialize a value, similar to `serializeMaxDepth`. | |
| 477 | pub fn valueMaxDepth( | |
| 478 | self: *Self, | |
| 479 | val: anytype, | |
| 480 | options: ValueOptions, | |
| 481 | depth: usize, | |
| 482 | ) (Writer.Error || error{ExceededMaxDepth})!void { | |
| 483 | try checkValueDepth(val, depth); | |
| 484 | return self.valueArbitraryDepth(val, options); | |
| 485 | } | |
| 486 | ||
| 487 | /// Serialize a value, similar to `serializeArbitraryDepth`. | |
| 488 | pub fn valueArbitraryDepth( | |
| 489 | self: *Self, | |
| 490 | val: anytype, | |
| 491 | options: ValueOptions, | |
| 492 | ) Writer.Error!void { | |
| 493 | comptime assert(canSerializeType(@TypeOf(val))); | |
| 494 | switch (@typeInfo(@TypeOf(val))) { | |
| 495 | .int, .comptime_int => if (options.emit_codepoint_literals.emitAsCodepoint(val)) |c| { | |
| 496 | self.codePoint(c) catch |err| switch (err) { | |
| 497 | error.InvalidCodepoint => unreachable, // Already validated | |
| 498 | else => |e| return e, | |
| 499 | }; | |
| 500 | } else { | |
| 501 | try self.int(val); | |
| 502 | }, | |
| 503 | .float, .comptime_float => try self.float(val), | |
| 504 | .bool, .null => try std.fmt.format(self.writer, "{}", .{val}), | |
| 505 | .enum_literal => try self.ident(@tagName(val)), | |
| 506 | .@"enum" => try self.ident(@tagName(val)), | |
| 507 | .void => try self.writer.writeAll("{}"), | |
| 508 | .pointer => |pointer| { | |
| 509 | // Try to serialize as a string | |
| 510 | const item: ?type = switch (@typeInfo(pointer.child)) { | |
| 511 | .array => |array| array.child, | |
| 512 | else => if (pointer.size == .slice) pointer.child else null, | |
| 513 | }; | |
| 514 | if (item == u8 and | |
| 515 | (pointer.sentinel() == null or pointer.sentinel() == 0) and | |
| 516 | !options.emit_strings_as_containers) | |
| 517 | { | |
| 518 | return try self.string(val); | |
| 519 | } | |
| 520 | ||
| 521 | // Serialize as either a tuple or as the child type | |
| 522 | switch (pointer.size) { | |
| 523 | .slice => try self.tupleImpl(val, options), | |
| 524 | .one => try self.valueArbitraryDepth(val.*, options), | |
| 525 | else => comptime unreachable, | |
| 526 | } | |
| 527 | }, | |
| 528 | .array => { | |
| 529 | var container = try self.startTuple( | |
| 530 | .{ .whitespace_style = .{ .fields = val.len } }, | |
| 531 | ); | |
| 532 | for (val) |item_val| { | |
| 533 | try container.fieldArbitraryDepth(item_val, options); | |
| 534 | } | |
| 535 | try container.finish(); | |
| 536 | }, | |
| 537 | .@"struct" => |@"struct"| if (@"struct".is_tuple) { | |
| 538 | var container = try self.startTuple( | |
| 539 | .{ .whitespace_style = .{ .fields = @"struct".fields.len } }, | |
| 540 | ); | |
| 541 | inline for (val) |field_value| { | |
| 542 | try container.fieldArbitraryDepth(field_value, options); | |
| 543 | } | |
| 544 | try container.finish(); | |
| 545 | } else { | |
| 546 | // Decide which fields to emit | |
| 547 | const fields, const skipped: [@"struct".fields.len]bool = if (options.emit_default_optional_fields) b: { | |
| 548 | break :b .{ @"struct".fields.len, @splat(false) }; | |
| 549 | } else b: { | |
| 550 | var fields = @"struct".fields.len; | |
| 551 | var skipped: [@"struct".fields.len]bool = @splat(false); | |
| 552 | inline for (@"struct".fields, &skipped) |field_info, *skip| { | |
| 553 | if (field_info.default_value_ptr) |ptr| { | |
| 554 | const default: *const field_info.type = @ptrCast(@alignCast(ptr)); | |
| 555 | const field_value = @field(val, field_info.name); | |
| 556 | if (std.meta.eql(field_value, default.*)) { | |
| 557 | skip.* = true; | |
| 558 | fields -= 1; | |
| 559 | } | |
| 560 | } | |
| 561 | } | |
| 562 | break :b .{ fields, skipped }; | |
| 563 | }; | |
| 564 | ||
| 565 | // Emit those fields | |
| 566 | var container = try self.startStruct( | |
| 567 | .{ .whitespace_style = .{ .fields = fields } }, | |
| 568 | ); | |
| 569 | inline for (@"struct".fields, skipped) |field_info, skip| { | |
| 570 | if (!skip) { | |
| 571 | try container.fieldArbitraryDepth( | |
| 572 | field_info.name, | |
| 573 | @field(val, field_info.name), | |
| 574 | options, | |
| 575 | ); | |
| 576 | } | |
| 577 | } | |
| 578 | try container.finish(); | |
| 579 | }, | |
| 580 | .@"union" => |@"union"| { | |
| 581 | comptime assert(@"union".tag_type != null); | |
| 582 | var container = try self.startStruct(.{ .whitespace_style = .{ .fields = 1 } }); | |
| 583 | switch (val) { | |
| 584 | inline else => |pl, tag| try container.fieldArbitraryDepth( | |
| 585 | @tagName(tag), | |
| 586 | pl, | |
| 587 | options, | |
| 588 | ), | |
| 589 | } | |
| 590 | try container.finish(); | |
| 591 | }, | |
| 592 | .optional => if (val) |inner| { | |
| 593 | try self.valueArbitraryDepth(inner, options); | |
| 594 | } else { | |
| 595 | try self.writer.writeAll("null"); | |
| 596 | }, | |
| 597 | .vector => |vector| { | |
| 598 | var container = try self.startTuple( | |
| 599 | .{ .whitespace_style = .{ .fields = vector.len } }, | |
| 600 | ); | |
| 601 | for (0..vector.len) |i| { | |
| 602 | try container.fieldArbitraryDepth(val[i], options); | |
| 603 | } | |
| 604 | try container.finish(); | |
| 605 | }, | |
| 606 | ||
| 607 | else => comptime unreachable, | |
| 608 | } | |
| 609 | } | |
| 610 | ||
| 611 | /// Serialize an integer. | |
| 612 | pub fn int(self: *Self, val: anytype) Writer.Error!void { | |
| 613 | try std.fmt.formatInt(val, 10, .lower, .{}, self.writer); | |
| 614 | } | |
| 615 | ||
| 616 | /// Serialize a float. | |
| 617 | pub fn float(self: *Self, val: anytype) Writer.Error!void { | |
| 618 | switch (@typeInfo(@TypeOf(val))) { | |
| 619 | .float => if (std.math.isNan(val)) { | |
| 620 | return self.writer.writeAll("nan"); | |
| 621 | } else if (std.math.isPositiveInf(val)) { | |
| 622 | return self.writer.writeAll("inf"); | |
| 623 | } else if (std.math.isNegativeInf(val)) { | |
| 624 | return self.writer.writeAll("-inf"); | |
| 625 | } else { | |
| 626 | try std.fmt.format(self.writer, "{d}", .{val}); | |
| 627 | }, | |
| 628 | .comptime_float => try std.fmt.format(self.writer, "{d}", .{val}), | |
| 629 | else => comptime unreachable, | |
| 630 | } | |
| 631 | } | |
| 632 | ||
| 633 | /// Serialize `name` as an identifier prefixed with `.`. | |
| 634 | /// | |
| 635 | /// Escapes the identifier if necessary. | |
| 636 | pub fn ident(self: *Self, name: []const u8) Writer.Error!void { | |
| 637 | try self.writer.print(".{p_}", .{std.zig.fmtId(name)}); | |
| 638 | } | |
| 639 | ||
| 640 | /// Serialize `val` as a Unicode codepoint. | |
| 641 | /// | |
| 642 | /// Returns `error.InvalidCodepoint` if `val` is not a valid Unicode codepoint. | |
| 643 | pub fn codePoint( | |
| 644 | self: *Self, | |
| 645 | val: u21, | |
| 646 | ) (Writer.Error || error{InvalidCodepoint})!void { | |
| 647 | var buf: [8]u8 = undefined; | |
| 648 | const len = std.unicode.utf8Encode(val, &buf) catch return error.InvalidCodepoint; | |
| 649 | const str = buf[0..len]; | |
| 650 | try std.fmt.format(self.writer, "'{'}'", .{std.zig.fmtEscapes(str)}); | |
| 651 | } | |
| 652 | ||
| 653 | /// Like `value`, but always serializes `val` as a tuple. | |
| 654 | /// | |
| 655 | /// Will fail at comptime if `val` is not a tuple, array, pointer to an array, or slice. | |
| 656 | pub fn tuple(self: *Self, val: anytype, options: ValueOptions) Writer.Error!void { | |
| 657 | comptime assert(!typeIsRecursive(@TypeOf(val))); | |
| 658 | try self.tupleArbitraryDepth(val, options); | |
| 659 | } | |
| 660 | ||
| 661 | /// Like `tuple`, but recursive types are allowed. | |
| 662 | /// | |
| 663 | /// Returns `error.ExceededMaxDepth` if `depth` is exceeded. | |
| 664 | pub fn tupleMaxDepth( | |
| 665 | self: *Self, | |
| 666 | val: anytype, | |
| 667 | options: ValueOptions, | |
| 668 | depth: usize, | |
| 669 | ) (Writer.Error || error{ExceededMaxDepth})!void { | |
| 670 | try checkValueDepth(val, depth); | |
| 671 | try self.tupleArbitraryDepth(val, options); | |
| 672 | } | |
| 673 | ||
| 674 | /// Like `tuple`, but recursive types are allowed. | |
| 675 | /// | |
| 676 | /// It is the caller's responsibility to ensure that `val` does not contain cycles. | |
| 677 | pub fn tupleArbitraryDepth( | |
| 678 | self: *Self, | |
| 679 | val: anytype, | |
| 680 | options: ValueOptions, | |
| 681 | ) Writer.Error!void { | |
| 682 | try self.tupleImpl(val, options); | |
| 683 | } | |
| 684 | ||
| 685 | fn tupleImpl(self: *Self, val: anytype, options: ValueOptions) Writer.Error!void { | |
| 686 | comptime assert(canSerializeType(@TypeOf(val))); | |
| 687 | switch (@typeInfo(@TypeOf(val))) { | |
| 688 | .@"struct" => { | |
| 689 | var container = try self.startTuple(.{ .whitespace_style = .{ .fields = val.len } }); | |
| 690 | inline for (val) |item_val| { | |
| 691 | try container.fieldArbitraryDepth(item_val, options); | |
| 692 | } | |
| 693 | try container.finish(); | |
| 694 | }, | |
| 695 | .pointer, .array => { | |
| 696 | var container = try self.startTuple(.{ .whitespace_style = .{ .fields = val.len } }); | |
| 697 | for (val) |item_val| { | |
| 698 | try container.fieldArbitraryDepth(item_val, options); | |
| 699 | } | |
| 700 | try container.finish(); | |
| 701 | }, | |
| 702 | else => comptime unreachable, | |
| 703 | } | |
| 704 | } | |
| 705 | ||
| 706 | /// Like `value`, but always serializes `val` as a string. | |
| 707 | pub fn string(self: *Self, val: []const u8) Writer.Error!void { | |
| 708 | try std.fmt.format(self.writer, "\"{}\"", .{std.zig.fmtEscapes(val)}); | |
| 709 | } | |
| 710 | ||
| 711 | /// Options for formatting multiline strings. | |
| 712 | pub const MultilineStringOptions = struct { | |
| 713 | /// If top level is true, whitespace before and after the multiline string is elided. | |
| 714 | /// If it is true, a newline is printed, then the value, followed by a newline, and if | |
| 715 | /// whitespace is true any necessary indentation follows. | |
| 716 | top_level: bool = false, | |
| 717 | }; | |
| 718 | ||
| 719 | /// Like `value`, but always serializes to a multiline string literal. | |
| 720 | /// | |
| 721 | /// Returns `error.InnerCarriageReturn` if `val` contains a CR not followed by a newline, | |
| 722 | /// since multiline strings cannot represent CR without a following newline. | |
| 723 | pub fn multilineString( | |
| 724 | self: *Self, | |
| 725 | val: []const u8, | |
| 726 | options: MultilineStringOptions, | |
| 727 | ) (Writer.Error || error{InnerCarriageReturn})!void { | |
| 728 | // Make sure the string does not contain any carriage returns not followed by a newline | |
| 729 | var i: usize = 0; | |
| 730 | while (i < val.len) : (i += 1) { | |
| 731 | if (val[i] == '\r') { | |
| 732 | if (i + 1 < val.len) { | |
| 733 | if (val[i + 1] == '\n') { | |
| 734 | i += 1; | |
| 735 | continue; | |
| 736 | } | |
| 737 | } | |
| 738 | return error.InnerCarriageReturn; | |
| 739 | } | |
| 740 | } | |
| 741 | ||
| 742 | if (!options.top_level) { | |
| 743 | try self.newline(); | |
| 744 | try self.indent(); | |
| 745 | } | |
| 746 | ||
| 747 | try self.writer.writeAll("\\\\"); | |
| 748 | for (val) |c| { | |
| 749 | if (c != '\r') { | |
| 750 | try self.writer.writeByte(c); // We write newlines here even if whitespace off | |
| 751 | if (c == '\n') { | |
| 752 | try self.indent(); | |
| 753 | try self.writer.writeAll("\\\\"); | |
| 754 | } | |
| 755 | } | |
| 756 | } | |
| 757 | ||
| 758 | if (!options.top_level) { | |
| 759 | try self.writer.writeByte('\n'); // Even if whitespace off | |
| 760 | try self.indent(); | |
| 761 | } | |
| 762 | } | |
| 763 | ||
| 764 | /// Create a `Struct` for writing ZON structs field by field. | |
| 765 | pub fn startStruct( | |
| 766 | self: *Self, | |
| 767 | options: SerializeContainerOptions, | |
| 768 | ) Writer.Error!Struct { | |
| 769 | return Struct.start(self, options); | |
| 770 | } | |
| 771 | ||
| 772 | /// Creates a `Tuple` for writing ZON tuples field by field. | |
| 773 | pub fn startTuple( | |
| 774 | self: *Self, | |
| 775 | options: SerializeContainerOptions, | |
| 776 | ) Writer.Error!Tuple { | |
| 777 | return Tuple.start(self, options); | |
| 778 | } | |
| 779 | ||
| 780 | fn indent(self: *Self) Writer.Error!void { | |
| 781 | if (self.options.whitespace) { | |
| 782 | try self.writer.writeByteNTimes(' ', 4 * self.indent_level); | |
| 783 | } | |
| 784 | } | |
| 785 | ||
| 786 | fn newline(self: *Self) Writer.Error!void { | |
| 787 | if (self.options.whitespace) { | |
| 788 | try self.writer.writeByte('\n'); | |
| 789 | } | |
| 790 | } | |
| 791 | ||
| 792 | fn newlineOrSpace(self: *Self, len: usize) Writer.Error!void { | |
| 793 | if (self.containerShouldWrap(len)) { | |
| 794 | try self.newline(); | |
| 795 | } else { | |
| 796 | try self.space(); | |
| 797 | } | |
| 798 | } | |
| 799 | ||
| 800 | fn space(self: *Self) Writer.Error!void { | |
| 801 | if (self.options.whitespace) { | |
| 802 | try self.writer.writeByte(' '); | |
| 803 | } | |
| 804 | } | |
| 805 | ||
| 806 | /// Writes ZON tuples field by field. | |
| 807 | pub const Tuple = struct { | |
| 808 | container: Container, | |
| 809 | ||
| 810 | fn start(parent: *Self, options: SerializeContainerOptions) Writer.Error!Tuple { | |
| 811 | return .{ | |
| 812 | .container = try Container.start(parent, .anon, options), | |
| 813 | }; | |
| 814 | } | |
| 815 | ||
| 816 | /// Finishes serializing the tuple. | |
| 817 | /// | |
| 818 | /// Prints a trailing comma as configured when appropriate, and the closing bracket. | |
| 819 | pub fn finish(self: *Tuple) Writer.Error!void { | |
| 820 | try self.container.finish(); | |
| 821 | self.* = undefined; | |
| 822 | } | |
| 823 | ||
| 824 | /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`. | |
| 825 | pub fn field( | |
| 826 | self: *Tuple, | |
| 827 | val: anytype, | |
| 828 | options: ValueOptions, | |
| 829 | ) Writer.Error!void { | |
| 830 | try self.container.field(null, val, options); | |
| 831 | } | |
| 832 | ||
| 833 | /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`. | |
| 834 | pub fn fieldMaxDepth( | |
| 835 | self: *Tuple, | |
| 836 | val: anytype, | |
| 837 | options: ValueOptions, | |
| 838 | depth: usize, | |
| 839 | ) (Writer.Error || error{ExceededMaxDepth})!void { | |
| 840 | try self.container.fieldMaxDepth(null, val, options, depth); | |
| 841 | } | |
| 842 | ||
| 843 | /// Serialize a field. Equivalent to calling `fieldPrefix` followed by | |
| 844 | /// `valueArbitraryDepth`. | |
| 845 | pub fn fieldArbitraryDepth( | |
| 846 | self: *Tuple, | |
| 847 | val: anytype, | |
| 848 | options: ValueOptions, | |
| 849 | ) Writer.Error!void { | |
| 850 | try self.container.fieldArbitraryDepth(null, val, options); | |
| 851 | } | |
| 852 | ||
| 853 | /// Print a field prefix. This prints any necessary commas, and whitespace as | |
| 854 | /// configured. Useful if you want to serialize the field value yourself. | |
| 855 | pub fn fieldPrefix(self: *Tuple) Writer.Error!void { | |
| 856 | try self.container.fieldPrefix(null); | |
| 857 | } | |
| 858 | }; | |
| 859 | ||
| 860 | /// Writes ZON structs field by field. | |
| 861 | pub const Struct = struct { | |
| 862 | container: Container, | |
| 863 | ||
| 864 | fn start(parent: *Self, options: SerializeContainerOptions) Writer.Error!Struct { | |
| 865 | return .{ | |
| 866 | .container = try Container.start(parent, .named, options), | |
| 867 | }; | |
| 868 | } | |
| 869 | ||
| 870 | /// Finishes serializing the struct. | |
| 871 | /// | |
| 872 | /// Prints a trailing comma as configured when appropriate, and the closing bracket. | |
| 873 | pub fn finish(self: *Struct) Writer.Error!void { | |
| 874 | try self.container.finish(); | |
| 875 | self.* = undefined; | |
| 876 | } | |
| 877 | ||
| 878 | /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`. | |
| 879 | pub fn field( | |
| 880 | self: *Struct, | |
| 881 | name: []const u8, | |
| 882 | val: anytype, | |
| 883 | options: ValueOptions, | |
| 884 | ) Writer.Error!void { | |
| 885 | try self.container.field(name, val, options); | |
| 886 | } | |
| 887 | ||
| 888 | /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`. | |
| 889 | pub fn fieldMaxDepth( | |
| 890 | self: *Struct, | |
| 891 | name: []const u8, | |
| 892 | val: anytype, | |
| 893 | options: ValueOptions, | |
| 894 | depth: usize, | |
| 895 | ) (Writer.Error || error{ExceededMaxDepth})!void { | |
| 896 | try self.container.fieldMaxDepth(name, val, options, depth); | |
| 897 | } | |
| 898 | ||
| 899 | /// Serialize a field. Equivalent to calling `fieldPrefix` followed by | |
| 900 | /// `valueArbitraryDepth`. | |
| 901 | pub fn fieldArbitraryDepth( | |
| 902 | self: *Struct, | |
| 903 | name: []const u8, | |
| 904 | val: anytype, | |
| 905 | options: ValueOptions, | |
| 906 | ) Writer.Error!void { | |
| 907 | try self.container.fieldArbitraryDepth(name, val, options); | |
| 908 | } | |
| 909 | ||
| 910 | /// Print a field prefix. This prints any necessary commas, the field name (escaped if | |
| 911 | /// necessary) and whitespace as configured. Useful if you want to serialize the field | |
| 912 | /// value yourself. | |
| 913 | pub fn fieldPrefix(self: *Struct, name: []const u8) Writer.Error!void { | |
| 914 | try self.container.fieldPrefix(name); | |
| 915 | } | |
| 916 | }; | |
| 917 | ||
| 918 | const Container = struct { | |
| 919 | const FieldStyle = enum { named, anon }; | |
| 920 | ||
| 921 | serializer: *Self, | |
| 922 | field_style: FieldStyle, | |
| 923 | options: SerializeContainerOptions, | |
| 924 | empty: bool, | |
| 925 | ||
| 926 | fn start( | |
| 927 | sz: *Self, | |
| 928 | field_style: FieldStyle, | |
| 929 | options: SerializeContainerOptions, | |
| 930 | ) Writer.Error!Container { | |
| 931 | if (options.shouldWrap()) sz.indent_level +|= 1; | |
| 932 | try sz.writer.writeAll(".{"); | |
| 933 | return .{ | |
| 934 | .serializer = sz, | |
| 935 | .field_style = field_style, | |
| 936 | .options = options, | |
| 937 | .empty = true, | |
| 938 | }; | |
| 939 | } | |
| 940 | ||
| 941 | fn finish(self: *Container) Writer.Error!void { | |
| 942 | if (self.options.shouldWrap()) self.serializer.indent_level -|= 1; | |
| 943 | if (!self.empty) { | |
| 944 | if (self.options.shouldWrap()) { | |
| 945 | if (self.serializer.options.whitespace) { | |
| 946 | try self.serializer.writer.writeByte(','); | |
| 947 | } | |
| 948 | try self.serializer.newline(); | |
| 949 | try self.serializer.indent(); | |
| 950 | } else if (!self.shouldElideSpaces()) { | |
| 951 | try self.serializer.space(); | |
| 952 | } | |
| 953 | } | |
| 954 | try self.serializer.writer.writeByte('}'); | |
| 955 | self.* = undefined; | |
| 956 | } | |
| 957 | ||
| 958 | fn fieldPrefix(self: *Container, name: ?[]const u8) Writer.Error!void { | |
| 959 | if (!self.empty) { | |
| 960 | try self.serializer.writer.writeByte(','); | |
| 961 | } | |
| 962 | self.empty = false; | |
| 963 | if (self.options.shouldWrap()) { | |
| 964 | try self.serializer.newline(); | |
| 965 | } else if (!self.shouldElideSpaces()) { | |
| 966 | try self.serializer.space(); | |
| 967 | } | |
| 968 | if (self.options.shouldWrap()) try self.serializer.indent(); | |
| 969 | if (name) |n| { | |
| 970 | try self.serializer.ident(n); | |
| 971 | try self.serializer.space(); | |
| 972 | try self.serializer.writer.writeByte('='); | |
| 973 | try self.serializer.space(); | |
| 974 | } | |
| 975 | } | |
| 976 | ||
| 977 | fn field( | |
| 978 | self: *Container, | |
| 979 | name: ?[]const u8, | |
| 980 | val: anytype, | |
| 981 | options: ValueOptions, | |
| 982 | ) Writer.Error!void { | |
| 983 | comptime assert(!typeIsRecursive(@TypeOf(val))); | |
| 984 | try self.fieldArbitraryDepth(name, val, options); | |
| 985 | } | |
| 986 | ||
| 987 | fn fieldMaxDepth( | |
| 988 | self: *Container, | |
| 989 | name: ?[]const u8, | |
| 990 | val: anytype, | |
| 991 | options: ValueOptions, | |
| 992 | depth: usize, | |
| 993 | ) (Writer.Error || error{ExceededMaxDepth})!void { | |
| 994 | try checkValueDepth(val, depth); | |
| 995 | try self.fieldArbitraryDepth(name, val, options); | |
| 996 | } | |
| 997 | ||
| 998 | fn fieldArbitraryDepth( | |
| 999 | self: *Container, | |
| 1000 | name: ?[]const u8, | |
| 1001 | val: anytype, | |
| 1002 | options: ValueOptions, | |
| 1003 | ) Writer.Error!void { | |
| 1004 | try self.fieldPrefix(name); | |
| 1005 | try self.serializer.valueArbitraryDepth(val, options); | |
| 1006 | } | |
| 1007 | ||
| 1008 | fn shouldElideSpaces(self: *const Container) bool { | |
| 1009 | return switch (self.options.whitespace_style) { | |
| 1010 | .fields => |fields| self.field_style != .named and fields == 1, | |
| 1011 | else => false, | |
| 1012 | }; | |
| 1013 | } | |
| 1014 | }; | |
| 1015 | }; | |
| 1016 | } | |
| 1017 | ||
| 1018 | /// Creates a new `Serializer` with the given writer and options. | |
| 1019 | pub fn serializer(writer: anytype, options: SerializerOptions) Serializer(@TypeOf(writer)) { | |
| 1020 | return .init(writer, options); | |
| 1021 | } | |
| 1022 | ||
| 1023 | fn expectSerializeEqual( | |
| 1024 | expected: []const u8, | |
| 1025 | value: anytype, | |
| 1026 | options: SerializeOptions, | |
| 1027 | ) !void { | |
| 1028 | var buf = std.ArrayList(u8).init(std.testing.allocator); | |
| 1029 | defer buf.deinit(); | |
| 1030 | try serialize(value, options, buf.writer()); | |
| 1031 | try std.testing.expectEqualStrings(expected, buf.items); | |
| 1032 | } | |
| 1033 | ||
| 1034 | test "std.zon stringify whitespace, high level API" { | |
| 1035 | try expectSerializeEqual(".{}", .{}, .{}); | |
| 1036 | try expectSerializeEqual(".{}", .{}, .{ .whitespace = false }); | |
| 1037 | ||
| 1038 | try expectSerializeEqual(".{1}", .{1}, .{}); | |
| 1039 | try expectSerializeEqual(".{1}", .{1}, .{ .whitespace = false }); | |
| 1040 | ||
| 1041 | try expectSerializeEqual(".{1}", @as([1]u32, .{1}), .{}); | |
| 1042 | try expectSerializeEqual(".{1}", @as([1]u32, .{1}), .{ .whitespace = false }); | |
| 1043 | ||
| 1044 | try expectSerializeEqual(".{1}", @as([]const u32, &.{1}), .{}); | |
| 1045 | try expectSerializeEqual(".{1}", @as([]const u32, &.{1}), .{ .whitespace = false }); | |
| 1046 | ||
| 1047 | try expectSerializeEqual(".{ .x = 1 }", .{ .x = 1 }, .{}); | |
| 1048 | try expectSerializeEqual(".{.x=1}", .{ .x = 1 }, .{ .whitespace = false }); | |
| 1049 | ||
| 1050 | try expectSerializeEqual(".{ 1, 2 }", .{ 1, 2 }, .{}); | |
| 1051 | try expectSerializeEqual(".{1,2}", .{ 1, 2 }, .{ .whitespace = false }); | |
| 1052 | ||
| 1053 | try expectSerializeEqual(".{ 1, 2 }", @as([2]u32, .{ 1, 2 }), .{}); | |
| 1054 | try expectSerializeEqual(".{1,2}", @as([2]u32, .{ 1, 2 }), .{ .whitespace = false }); | |
| 1055 | ||
| 1056 | try expectSerializeEqual(".{ 1, 2 }", @as([]const u32, &.{ 1, 2 }), .{}); | |
| 1057 | try expectSerializeEqual(".{1,2}", @as([]const u32, &.{ 1, 2 }), .{ .whitespace = false }); | |
| 1058 | ||
| 1059 | try expectSerializeEqual(".{ .x = 1, .y = 2 }", .{ .x = 1, .y = 2 }, .{}); | |
| 1060 | try expectSerializeEqual(".{.x=1,.y=2}", .{ .x = 1, .y = 2 }, .{ .whitespace = false }); | |
| 1061 | ||
| 1062 | try expectSerializeEqual( | |
| 1063 | \\.{ | |
| 1064 | \\ 1, | |
| 1065 | \\ 2, | |
| 1066 | \\ 3, | |
| 1067 | \\} | |
| 1068 | , .{ 1, 2, 3 }, .{}); | |
| 1069 | try expectSerializeEqual(".{1,2,3}", .{ 1, 2, 3 }, .{ .whitespace = false }); | |
| 1070 | ||
| 1071 | try expectSerializeEqual( | |
| 1072 | \\.{ | |
| 1073 | \\ 1, | |
| 1074 | \\ 2, | |
| 1075 | \\ 3, | |
| 1076 | \\} | |
| 1077 | , @as([3]u32, .{ 1, 2, 3 }), .{}); | |
| 1078 | try expectSerializeEqual(".{1,2,3}", @as([3]u32, .{ 1, 2, 3 }), .{ .whitespace = false }); | |
| 1079 | ||
| 1080 | try expectSerializeEqual( | |
| 1081 | \\.{ | |
| 1082 | \\ 1, | |
| 1083 | \\ 2, | |
| 1084 | \\ 3, | |
| 1085 | \\} | |
| 1086 | , @as([]const u32, &.{ 1, 2, 3 }), .{}); | |
| 1087 | try expectSerializeEqual( | |
| 1088 | ".{1,2,3}", | |
| 1089 | @as([]const u32, &.{ 1, 2, 3 }), | |
| 1090 | .{ .whitespace = false }, | |
| 1091 | ); | |
| 1092 | ||
| 1093 | try expectSerializeEqual( | |
| 1094 | \\.{ | |
| 1095 | \\ .x = 1, | |
| 1096 | \\ .y = 2, | |
| 1097 | \\ .z = 3, | |
| 1098 | \\} | |
| 1099 | , .{ .x = 1, .y = 2, .z = 3 }, .{}); | |
| 1100 | try expectSerializeEqual( | |
| 1101 | ".{.x=1,.y=2,.z=3}", | |
| 1102 | .{ .x = 1, .y = 2, .z = 3 }, | |
| 1103 | .{ .whitespace = false }, | |
| 1104 | ); | |
| 1105 | ||
| 1106 | const Union = union(enum) { a: bool, b: i32, c: u8 }; | |
| 1107 | ||
| 1108 | try expectSerializeEqual(".{ .b = 1 }", Union{ .b = 1 }, .{}); | |
| 1109 | try expectSerializeEqual(".{.b=1}", Union{ .b = 1 }, .{ .whitespace = false }); | |
| 1110 | ||
| 1111 | // Nested indentation where outer object doesn't wrap | |
| 1112 | try expectSerializeEqual( | |
| 1113 | \\.{ .inner = .{ | |
| 1114 | \\ 1, | |
| 1115 | \\ 2, | |
| 1116 | \\ 3, | |
| 1117 | \\} } | |
| 1118 | , .{ .inner = .{ 1, 2, 3 } }, .{}); | |
| 1119 | } | |
| 1120 | ||
| 1121 | test "std.zon stringify whitespace, low level API" { | |
| 1122 | var buf = std.ArrayList(u8).init(std.testing.allocator); | |
| 1123 | defer buf.deinit(); | |
| 1124 | var sz = serializer(buf.writer(), .{}); | |
| 1125 | ||
| 1126 | inline for (.{ true, false }) |whitespace| { | |
| 1127 | sz.options = .{ .whitespace = whitespace }; | |
| 1128 | ||
| 1129 | // Empty containers | |
| 1130 | { | |
| 1131 | var container = try sz.startStruct(.{}); | |
| 1132 | try container.finish(); | |
| 1133 | try std.testing.expectEqualStrings(".{}", buf.items); | |
| 1134 | buf.clearRetainingCapacity(); | |
| 1135 | } | |
| 1136 | ||
| 1137 | { | |
| 1138 | var container = try sz.startTuple(.{}); | |
| 1139 | try container.finish(); | |
| 1140 | try std.testing.expectEqualStrings(".{}", buf.items); | |
| 1141 | buf.clearRetainingCapacity(); | |
| 1142 | } | |
| 1143 | ||
| 1144 | { | |
| 1145 | var container = try sz.startStruct(.{ .whitespace_style = .{ .wrap = false } }); | |
| 1146 | try container.finish(); | |
| 1147 | try std.testing.expectEqualStrings(".{}", buf.items); | |
| 1148 | buf.clearRetainingCapacity(); | |
| 1149 | } | |
| 1150 | ||
| 1151 | { | |
| 1152 | var container = try sz.startTuple(.{ .whitespace_style = .{ .wrap = false } }); | |
| 1153 | try container.finish(); | |
| 1154 | try std.testing.expectEqualStrings(".{}", buf.items); | |
| 1155 | buf.clearRetainingCapacity(); | |
| 1156 | } | |
| 1157 | ||
| 1158 | { | |
| 1159 | var container = try sz.startStruct(.{ .whitespace_style = .{ .fields = 0 } }); | |
| 1160 | try container.finish(); | |
| 1161 | try std.testing.expectEqualStrings(".{}", buf.items); | |
| 1162 | buf.clearRetainingCapacity(); | |
| 1163 | } | |
| 1164 | ||
| 1165 | { | |
| 1166 | var container = try sz.startTuple(.{ .whitespace_style = .{ .fields = 0 } }); | |
| 1167 | try container.finish(); | |
| 1168 | try std.testing.expectEqualStrings(".{}", buf.items); | |
| 1169 | buf.clearRetainingCapacity(); | |
| 1170 | } | |
| 1171 | ||
| 1172 | // Size 1 | |
| 1173 | { | |
| 1174 | var container = try sz.startStruct(.{}); | |
| 1175 | try container.field("a", 1, .{}); | |
| 1176 | try container.finish(); | |
| 1177 | if (whitespace) { | |
| 1178 | try std.testing.expectEqualStrings( | |
| 1179 | \\.{ | |
| 1180 | \\ .a = 1, | |
| 1181 | \\} | |
| 1182 | , buf.items); | |
| 1183 | } else { | |
| 1184 | try std.testing.expectEqualStrings(".{.a=1}", buf.items); | |
| 1185 | } | |
| 1186 | buf.clearRetainingCapacity(); | |
| 1187 | } | |
| 1188 | ||
| 1189 | { | |
| 1190 | var container = try sz.startTuple(.{}); | |
| 1191 | try container.field(1, .{}); | |
| 1192 | try container.finish(); | |
| 1193 | if (whitespace) { | |
| 1194 | try std.testing.expectEqualStrings( | |
| 1195 | \\.{ | |
| 1196 | \\ 1, | |
| 1197 | \\} | |
| 1198 | , buf.items); | |
| 1199 | } else { | |
| 1200 | try std.testing.expectEqualStrings(".{1}", buf.items); | |
| 1201 | } | |
| 1202 | buf.clearRetainingCapacity(); | |
| 1203 | } | |
| 1204 | ||
| 1205 | { | |
| 1206 | var container = try sz.startStruct(.{ .whitespace_style = .{ .wrap = false } }); | |
| 1207 | try container.field("a", 1, .{}); | |
| 1208 | try container.finish(); | |
| 1209 | if (whitespace) { | |
| 1210 | try std.testing.expectEqualStrings(".{ .a = 1 }", buf.items); | |
| 1211 | } else { | |
| 1212 | try std.testing.expectEqualStrings(".{.a=1}", buf.items); | |
| 1213 | } | |
| 1214 | buf.clearRetainingCapacity(); | |
| 1215 | } | |
| 1216 | ||
| 1217 | { | |
| 1218 | // We get extra spaces here, since we didn't know up front that there would only be one | |
| 1219 | // field. | |
| 1220 | var container = try sz.startTuple(.{ .whitespace_style = .{ .wrap = false } }); | |
| 1221 | try container.field(1, .{}); | |
| 1222 | try container.finish(); | |
| 1223 | if (whitespace) { | |
| 1224 | try std.testing.expectEqualStrings(".{ 1 }", buf.items); | |
| 1225 | } else { | |
| 1226 | try std.testing.expectEqualStrings(".{1}", buf.items); | |
| 1227 | } | |
| 1228 | buf.clearRetainingCapacity(); | |
| 1229 | } | |
| 1230 | ||
| 1231 | { | |
| 1232 | var container = try sz.startStruct(.{ .whitespace_style = .{ .fields = 1 } }); | |
| 1233 | try container.field("a", 1, .{}); | |
| 1234 | try container.finish(); | |
| 1235 | if (whitespace) { | |
| 1236 | try std.testing.expectEqualStrings(".{ .a = 1 }", buf.items); | |
| 1237 | } else { | |
| 1238 | try std.testing.expectEqualStrings(".{.a=1}", buf.items); | |
| 1239 | } | |
| 1240 | buf.clearRetainingCapacity(); | |
| 1241 | } | |
| 1242 | ||
| 1243 | { | |
| 1244 | var container = try sz.startTuple(.{ .whitespace_style = .{ .fields = 1 } }); | |
| 1245 | try container.field(1, .{}); | |
| 1246 | try container.finish(); | |
| 1247 | try std.testing.expectEqualStrings(".{1}", buf.items); | |
| 1248 | buf.clearRetainingCapacity(); | |
| 1249 | } | |
| 1250 | ||
| 1251 | // Size 2 | |
| 1252 | { | |
| 1253 | var container = try sz.startStruct(.{}); | |
| 1254 | try container.field("a", 1, .{}); | |
| 1255 | try container.field("b", 2, .{}); | |
| 1256 | try container.finish(); | |
| 1257 | if (whitespace) { | |
| 1258 | try std.testing.expectEqualStrings( | |
| 1259 | \\.{ | |
| 1260 | \\ .a = 1, | |
| 1261 | \\ .b = 2, | |
| 1262 | \\} | |
| 1263 | , buf.items); | |
| 1264 | } else { | |
| 1265 | try std.testing.expectEqualStrings(".{.a=1,.b=2}", buf.items); | |
| 1266 | } | |
| 1267 | buf.clearRetainingCapacity(); | |
| 1268 | } | |
| 1269 | ||
| 1270 | { | |
| 1271 | var container = try sz.startTuple(.{}); | |
| 1272 | try container.field(1, .{}); | |
| 1273 | try container.field(2, .{}); | |
| 1274 | try container.finish(); | |
| 1275 | if (whitespace) { | |
| 1276 | try std.testing.expectEqualStrings( | |
| 1277 | \\.{ | |
| 1278 | \\ 1, | |
| 1279 | \\ 2, | |
| 1280 | \\} | |
| 1281 | , buf.items); | |
| 1282 | } else { | |
| 1283 | try std.testing.expectEqualStrings(".{1,2}", buf.items); | |
| 1284 | } | |
| 1285 | buf.clearRetainingCapacity(); | |
| 1286 | } | |
| 1287 | ||
| 1288 | { | |
| 1289 | var container = try sz.startStruct(.{ .whitespace_style = .{ .wrap = false } }); | |
| 1290 | try container.field("a", 1, .{}); | |
| 1291 | try container.field("b", 2, .{}); | |
| 1292 | try container.finish(); | |
| 1293 | if (whitespace) { | |
| 1294 | try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", buf.items); | |
| 1295 | } else { | |
| 1296 | try std.testing.expectEqualStrings(".{.a=1,.b=2}", buf.items); | |
| 1297 | } | |
| 1298 | buf.clearRetainingCapacity(); | |
| 1299 | } | |
| 1300 | ||
| 1301 | { | |
| 1302 | var container = try sz.startTuple(.{ .whitespace_style = .{ .wrap = false } }); | |
| 1303 | try container.field(1, .{}); | |
| 1304 | try container.field(2, .{}); | |
| 1305 | try container.finish(); | |
| 1306 | if (whitespace) { | |
| 1307 | try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items); | |
| 1308 | } else { | |
| 1309 | try std.testing.expectEqualStrings(".{1,2}", buf.items); | |
| 1310 | } | |
| 1311 | buf.clearRetainingCapacity(); | |
| 1312 | } | |
| 1313 | ||
| 1314 | { | |
| 1315 | var container = try sz.startStruct(.{ .whitespace_style = .{ .fields = 2 } }); | |
| 1316 | try container.field("a", 1, .{}); | |
| 1317 | try container.field("b", 2, .{}); | |
| 1318 | try container.finish(); | |
| 1319 | if (whitespace) { | |
| 1320 | try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", buf.items); | |
| 1321 | } else { | |
| 1322 | try std.testing.expectEqualStrings(".{.a=1,.b=2}", buf.items); | |
| 1323 | } | |
| 1324 | buf.clearRetainingCapacity(); | |
| 1325 | } | |
| 1326 | ||
| 1327 | { | |
| 1328 | var container = try sz.startTuple(.{ .whitespace_style = .{ .fields = 2 } }); | |
| 1329 | try container.field(1, .{}); | |
| 1330 | try container.field(2, .{}); | |
| 1331 | try container.finish(); | |
| 1332 | if (whitespace) { | |
| 1333 | try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items); | |
| 1334 | } else { | |
| 1335 | try std.testing.expectEqualStrings(".{1,2}", buf.items); | |
| 1336 | } | |
| 1337 | buf.clearRetainingCapacity(); | |
| 1338 | } | |
| 1339 | ||
| 1340 | // Size 3 | |
| 1341 | { | |
| 1342 | var container = try sz.startStruct(.{}); | |
| 1343 | try container.field("a", 1, .{}); | |
| 1344 | try container.field("b", 2, .{}); | |
| 1345 | try container.field("c", 3, .{}); | |
| 1346 | try container.finish(); | |
| 1347 | if (whitespace) { | |
| 1348 | try std.testing.expectEqualStrings( | |
| 1349 | \\.{ | |
| 1350 | \\ .a = 1, | |
| 1351 | \\ .b = 2, | |
| 1352 | \\ .c = 3, | |
| 1353 | \\} | |
| 1354 | , buf.items); | |
| 1355 | } else { | |
| 1356 | try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", buf.items); | |
| 1357 | } | |
| 1358 | buf.clearRetainingCapacity(); | |
| 1359 | } | |
| 1360 | ||
| 1361 | { | |
| 1362 | var container = try sz.startTuple(.{}); | |
| 1363 | try container.field(1, .{}); | |
| 1364 | try container.field(2, .{}); | |
| 1365 | try container.field(3, .{}); | |
| 1366 | try container.finish(); | |
| 1367 | if (whitespace) { | |
| 1368 | try std.testing.expectEqualStrings( | |
| 1369 | \\.{ | |
| 1370 | \\ 1, | |
| 1371 | \\ 2, | |
| 1372 | \\ 3, | |
| 1373 | \\} | |
| 1374 | , buf.items); | |
| 1375 | } else { | |
| 1376 | try std.testing.expectEqualStrings(".{1,2,3}", buf.items); | |
| 1377 | } | |
| 1378 | buf.clearRetainingCapacity(); | |
| 1379 | } | |
| 1380 | ||
| 1381 | { | |
| 1382 | var container = try sz.startStruct(.{ .whitespace_style = .{ .wrap = false } }); | |
| 1383 | try container.field("a", 1, .{}); | |
| 1384 | try container.field("b", 2, .{}); | |
| 1385 | try container.field("c", 3, .{}); | |
| 1386 | try container.finish(); | |
| 1387 | if (whitespace) { | |
| 1388 | try std.testing.expectEqualStrings(".{ .a = 1, .b = 2, .c = 3 }", buf.items); | |
| 1389 | } else { | |
| 1390 | try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", buf.items); | |
| 1391 | } | |
| 1392 | buf.clearRetainingCapacity(); | |
| 1393 | } | |
| 1394 | ||
| 1395 | { | |
| 1396 | var container = try sz.startTuple(.{ .whitespace_style = .{ .wrap = false } }); | |
| 1397 | try container.field(1, .{}); | |
| 1398 | try container.field(2, .{}); | |
| 1399 | try container.field(3, .{}); | |
| 1400 | try container.finish(); | |
| 1401 | if (whitespace) { | |
| 1402 | try std.testing.expectEqualStrings(".{ 1, 2, 3 }", buf.items); | |
| 1403 | } else { | |
| 1404 | try std.testing.expectEqualStrings(".{1,2,3}", buf.items); | |
| 1405 | } | |
| 1406 | buf.clearRetainingCapacity(); | |
| 1407 | } | |
| 1408 | ||
| 1409 | { | |
| 1410 | var container = try sz.startStruct(.{ .whitespace_style = .{ .fields = 3 } }); | |
| 1411 | try container.field("a", 1, .{}); | |
| 1412 | try container.field("b", 2, .{}); | |
| 1413 | try container.field("c", 3, .{}); | |
| 1414 | try container.finish(); | |
| 1415 | if (whitespace) { | |
| 1416 | try std.testing.expectEqualStrings( | |
| 1417 | \\.{ | |
| 1418 | \\ .a = 1, | |
| 1419 | \\ .b = 2, | |
| 1420 | \\ .c = 3, | |
| 1421 | \\} | |
| 1422 | , buf.items); | |
| 1423 | } else { | |
| 1424 | try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", buf.items); | |
| 1425 | } | |
| 1426 | buf.clearRetainingCapacity(); | |
| 1427 | } | |
| 1428 | ||
| 1429 | { | |
| 1430 | var container = try sz.startTuple(.{ .whitespace_style = .{ .fields = 3 } }); | |
| 1431 | try container.field(1, .{}); | |
| 1432 | try container.field(2, .{}); | |
| 1433 | try container.field(3, .{}); | |
| 1434 | try container.finish(); | |
| 1435 | if (whitespace) { | |
| 1436 | try std.testing.expectEqualStrings( | |
| 1437 | \\.{ | |
| 1438 | \\ 1, | |
| 1439 | \\ 2, | |
| 1440 | \\ 3, | |
| 1441 | \\} | |
| 1442 | , buf.items); | |
| 1443 | } else { | |
| 1444 | try std.testing.expectEqualStrings(".{1,2,3}", buf.items); | |
| 1445 | } | |
| 1446 | buf.clearRetainingCapacity(); | |
| 1447 | } | |
| 1448 | ||
| 1449 | // Nested objects where the outer container doesn't wrap but the inner containers do | |
| 1450 | { | |
| 1451 | var container = try sz.startStruct(.{ .whitespace_style = .{ .wrap = false } }); | |
| 1452 | try container.field("first", .{ 1, 2, 3 }, .{}); | |
| 1453 | try container.field("second", .{ 4, 5, 6 }, .{}); | |
| 1454 | try container.finish(); | |
| 1455 | if (whitespace) { | |
| 1456 | try std.testing.expectEqualStrings( | |
| 1457 | \\.{ .first = .{ | |
| 1458 | \\ 1, | |
| 1459 | \\ 2, | |
| 1460 | \\ 3, | |
| 1461 | \\}, .second = .{ | |
| 1462 | \\ 4, | |
| 1463 | \\ 5, | |
| 1464 | \\ 6, | |
| 1465 | \\} } | |
| 1466 | , buf.items); | |
| 1467 | } else { | |
| 1468 | try std.testing.expectEqualStrings( | |
| 1469 | ".{.first=.{1,2,3},.second=.{4,5,6}}", | |
| 1470 | buf.items, | |
| 1471 | ); | |
| 1472 | } | |
| 1473 | buf.clearRetainingCapacity(); | |
| 1474 | } | |
| 1475 | } | |
| 1476 | } | |
| 1477 | ||
| 1478 | test "std.zon stringify utf8 codepoints" { | |
| 1479 | var buf = std.ArrayList(u8).init(std.testing.allocator); | |
| 1480 | defer buf.deinit(); | |
| 1481 | var sz = serializer(buf.writer(), .{}); | |
| 1482 | ||
| 1483 | // Printable ASCII | |
| 1484 | try sz.int('a'); | |
| 1485 | try std.testing.expectEqualStrings("97", buf.items); | |
| 1486 | buf.clearRetainingCapacity(); | |
| 1487 | ||
| 1488 | try sz.codePoint('a'); | |
| 1489 | try std.testing.expectEqualStrings("'a'", buf.items); | |
| 1490 | buf.clearRetainingCapacity(); | |
| 1491 | ||
| 1492 | try sz.value('a', .{ .emit_codepoint_literals = .always }); | |
| 1493 | try std.testing.expectEqualStrings("'a'", buf.items); | |
| 1494 | buf.clearRetainingCapacity(); | |
| 1495 | ||
| 1496 | try sz.value('a', .{ .emit_codepoint_literals = .printable_ascii }); | |
| 1497 | try std.testing.expectEqualStrings("'a'", buf.items); | |
| 1498 | buf.clearRetainingCapacity(); | |
| 1499 | ||
| 1500 | try sz.value('a', .{ .emit_codepoint_literals = .never }); | |
| 1501 | try std.testing.expectEqualStrings("97", buf.items); | |
| 1502 | buf.clearRetainingCapacity(); | |
| 1503 | ||
| 1504 | // Short escaped codepoint | |
| 1505 | try sz.int('\n'); | |
| 1506 | try std.testing.expectEqualStrings("10", buf.items); | |
| 1507 | buf.clearRetainingCapacity(); | |
| 1508 | ||
| 1509 | try sz.codePoint('\n'); | |
| 1510 | try std.testing.expectEqualStrings("'\\n'", buf.items); | |
| 1511 | buf.clearRetainingCapacity(); | |
| 1512 | ||
| 1513 | try sz.value('\n', .{ .emit_codepoint_literals = .always }); | |
| 1514 | try std.testing.expectEqualStrings("'\\n'", buf.items); | |
| 1515 | buf.clearRetainingCapacity(); | |
| 1516 | ||
| 1517 | try sz.value('\n', .{ .emit_codepoint_literals = .printable_ascii }); | |
| 1518 | try std.testing.expectEqualStrings("10", buf.items); | |
| 1519 | buf.clearRetainingCapacity(); | |
| 1520 | ||
| 1521 | try sz.value('\n', .{ .emit_codepoint_literals = .never }); | |
| 1522 | try std.testing.expectEqualStrings("10", buf.items); | |
| 1523 | buf.clearRetainingCapacity(); | |
| 1524 | ||
| 1525 | // Large codepoint | |
| 1526 | try sz.int('âš¡'); | |
| 1527 | try std.testing.expectEqualStrings("9889", buf.items); | |
| 1528 | buf.clearRetainingCapacity(); | |
| 1529 | ||
| 1530 | try sz.codePoint('âš¡'); | |
| 1531 | try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", buf.items); | |
| 1532 | buf.clearRetainingCapacity(); | |
| 1533 | ||
| 1534 | try sz.value('âš¡', .{ .emit_codepoint_literals = .always }); | |
| 1535 | try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", buf.items); | |
| 1536 | buf.clearRetainingCapacity(); | |
| 1537 | ||
| 1538 | try sz.value('âš¡', .{ .emit_codepoint_literals = .printable_ascii }); | |
| 1539 | try std.testing.expectEqualStrings("9889", buf.items); | |
| 1540 | buf.clearRetainingCapacity(); | |
| 1541 | ||
| 1542 | try sz.value('âš¡', .{ .emit_codepoint_literals = .never }); | |
| 1543 | try std.testing.expectEqualStrings("9889", buf.items); | |
| 1544 | buf.clearRetainingCapacity(); | |
| 1545 | ||
| 1546 | // Invalid codepoint | |
| 1547 | try std.testing.expectError(error.InvalidCodepoint, sz.codePoint(0x110000 + 1)); | |
| 1548 | ||
| 1549 | try sz.int(0x110000 + 1); | |
| 1550 | try std.testing.expectEqualStrings("1114113", buf.items); | |
| 1551 | buf.clearRetainingCapacity(); | |
| 1552 | ||
| 1553 | try sz.value(0x110000 + 1, .{ .emit_codepoint_literals = .always }); | |
| 1554 | try std.testing.expectEqualStrings("1114113", buf.items); | |
| 1555 | buf.clearRetainingCapacity(); | |
| 1556 | ||
| 1557 | try sz.value(0x110000 + 1, .{ .emit_codepoint_literals = .printable_ascii }); | |
| 1558 | try std.testing.expectEqualStrings("1114113", buf.items); | |
| 1559 | buf.clearRetainingCapacity(); | |
| 1560 | ||
| 1561 | try sz.value(0x110000 + 1, .{ .emit_codepoint_literals = .never }); | |
| 1562 | try std.testing.expectEqualStrings("1114113", buf.items); | |
| 1563 | buf.clearRetainingCapacity(); | |
| 1564 | ||
| 1565 | // Valid codepoint, not a codepoint type | |
| 1566 | try sz.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .always }); | |
| 1567 | try std.testing.expectEqualStrings("97", buf.items); | |
| 1568 | buf.clearRetainingCapacity(); | |
| 1569 | ||
| 1570 | try sz.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .printable_ascii }); | |
| 1571 | try std.testing.expectEqualStrings("97", buf.items); | |
| 1572 | buf.clearRetainingCapacity(); | |
| 1573 | ||
| 1574 | try sz.value(@as(i32, 'a'), .{ .emit_codepoint_literals = .never }); | |
| 1575 | try std.testing.expectEqualStrings("97", buf.items); | |
| 1576 | buf.clearRetainingCapacity(); | |
| 1577 | ||
| 1578 | // Make sure value options are passed to children | |
| 1579 | try sz.value(.{ .c = 'âš¡' }, .{ .emit_codepoint_literals = .always }); | |
| 1580 | try std.testing.expectEqualStrings(".{ .c = '\\xe2\\x9a\\xa1' }", buf.items); | |
| 1581 | buf.clearRetainingCapacity(); | |
| 1582 | ||
| 1583 | try sz.value(.{ .c = 'âš¡' }, .{ .emit_codepoint_literals = .never }); | |
| 1584 | try std.testing.expectEqualStrings(".{ .c = 9889 }", buf.items); | |
| 1585 | buf.clearRetainingCapacity(); | |
| 1586 | } | |
| 1587 | ||
| 1588 | test "std.zon stringify strings" { | |
| 1589 | var buf = std.ArrayList(u8).init(std.testing.allocator); | |
| 1590 | defer buf.deinit(); | |
| 1591 | var sz = serializer(buf.writer(), .{}); | |
| 1592 | ||
| 1593 | // Minimal case | |
| 1594 | try sz.string("abcâš¡\n"); | |
| 1595 | try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", buf.items); | |
| 1596 | buf.clearRetainingCapacity(); | |
| 1597 | ||
| 1598 | try sz.tuple("abcâš¡\n", .{}); | |
| 1599 | try std.testing.expectEqualStrings( | |
| 1600 | \\.{ | |
| 1601 | \\ 97, | |
| 1602 | \\ 98, | |
| 1603 | \\ 99, | |
| 1604 | \\ 226, | |
| 1605 | \\ 154, | |
| 1606 | \\ 161, | |
| 1607 | \\ 10, | |
| 1608 | \\} | |
| 1609 | , buf.items); | |
| 1610 | buf.clearRetainingCapacity(); | |
| 1611 | ||
| 1612 | try sz.value("abcâš¡\n", .{}); | |
| 1613 | try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", buf.items); | |
| 1614 | buf.clearRetainingCapacity(); | |
| 1615 | ||
| 1616 | try sz.value("abcâš¡\n", .{ .emit_strings_as_containers = true }); | |
| 1617 | try std.testing.expectEqualStrings( | |
| 1618 | \\.{ | |
| 1619 | \\ 97, | |
| 1620 | \\ 98, | |
| 1621 | \\ 99, | |
| 1622 | \\ 226, | |
| 1623 | \\ 154, | |
| 1624 | \\ 161, | |
| 1625 | \\ 10, | |
| 1626 | \\} | |
| 1627 | , buf.items); | |
| 1628 | buf.clearRetainingCapacity(); | |
| 1629 | ||
| 1630 | // Value options are inherited by children | |
| 1631 | try sz.value(.{ .str = "abc" }, .{}); | |
| 1632 | try std.testing.expectEqualStrings(".{ .str = \"abc\" }", buf.items); | |
| 1633 | buf.clearRetainingCapacity(); | |
| 1634 | ||
| 1635 | try sz.value(.{ .str = "abc" }, .{ .emit_strings_as_containers = true }); | |
| 1636 | try std.testing.expectEqualStrings( | |
| 1637 | \\.{ .str = .{ | |
| 1638 | \\ 97, | |
| 1639 | \\ 98, | |
| 1640 | \\ 99, | |
| 1641 | \\} } | |
| 1642 | , buf.items); | |
| 1643 | buf.clearRetainingCapacity(); | |
| 1644 | ||
| 1645 | // Arrays (rather than pointers to arrays) of u8s are not considered strings, so that data can | |
| 1646 | // round trip correctly. | |
| 1647 | try sz.value("abc".*, .{}); | |
| 1648 | try std.testing.expectEqualStrings( | |
| 1649 | \\.{ | |
| 1650 | \\ 97, | |
| 1651 | \\ 98, | |
| 1652 | \\ 99, | |
| 1653 | \\} | |
| 1654 | , buf.items); | |
| 1655 | buf.clearRetainingCapacity(); | |
| 1656 | } | |
| 1657 | ||
| 1658 | test "std.zon stringify multiline strings" { | |
| 1659 | var buf = std.ArrayList(u8).init(std.testing.allocator); | |
| 1660 | defer buf.deinit(); | |
| 1661 | var sz = serializer(buf.writer(), .{}); | |
| 1662 | ||
| 1663 | inline for (.{ true, false }) |whitespace| { | |
| 1664 | sz.options.whitespace = whitespace; | |
| 1665 | ||
| 1666 | { | |
| 1667 | try sz.multilineString("", .{ .top_level = true }); | |
| 1668 | try std.testing.expectEqualStrings("\\\\", buf.items); | |
| 1669 | buf.clearRetainingCapacity(); | |
| 1670 | } | |
| 1671 | ||
| 1672 | { | |
| 1673 | try sz.multilineString("abcâš¡", .{ .top_level = true }); | |
| 1674 | try std.testing.expectEqualStrings("\\\\abcâš¡", buf.items); | |
| 1675 | buf.clearRetainingCapacity(); | |
| 1676 | } | |
| 1677 | ||
| 1678 | { | |
| 1679 | try sz.multilineString("abcâš¡\ndef", .{ .top_level = true }); | |
| 1680 | try std.testing.expectEqualStrings("\\\\abcâš¡\n\\\\def", buf.items); | |
| 1681 | buf.clearRetainingCapacity(); | |
| 1682 | } | |
| 1683 | ||
| 1684 | { | |
| 1685 | try sz.multilineString("abcâš¡\r\ndef", .{ .top_level = true }); | |
| 1686 | try std.testing.expectEqualStrings("\\\\abcâš¡\n\\\\def", buf.items); | |
| 1687 | buf.clearRetainingCapacity(); | |
| 1688 | } | |
| 1689 | ||
| 1690 | { | |
| 1691 | try sz.multilineString("\nabcâš¡", .{ .top_level = true }); | |
| 1692 | try std.testing.expectEqualStrings("\\\\\n\\\\abcâš¡", buf.items); | |
| 1693 | buf.clearRetainingCapacity(); | |
| 1694 | } | |
| 1695 | ||
| 1696 | { | |
| 1697 | try sz.multilineString("\r\nabcâš¡", .{ .top_level = true }); | |
| 1698 | try std.testing.expectEqualStrings("\\\\\n\\\\abcâš¡", buf.items); | |
| 1699 | buf.clearRetainingCapacity(); | |
| 1700 | } | |
| 1701 | ||
| 1702 | { | |
| 1703 | try sz.multilineString("abc\ndef", .{}); | |
| 1704 | if (whitespace) { | |
| 1705 | try std.testing.expectEqualStrings("\n\\\\abc\n\\\\def\n", buf.items); | |
| 1706 | } else { | |
| 1707 | try std.testing.expectEqualStrings("\\\\abc\n\\\\def\n", buf.items); | |
| 1708 | } | |
| 1709 | buf.clearRetainingCapacity(); | |
| 1710 | } | |
| 1711 | ||
| 1712 | { | |
| 1713 | const str: []const u8 = &.{ 'a', '\r', 'c' }; | |
| 1714 | try sz.string(str); | |
| 1715 | try std.testing.expectEqualStrings("\"a\\rc\"", buf.items); | |
| 1716 | buf.clearRetainingCapacity(); | |
| 1717 | } | |
| 1718 | ||
| 1719 | { | |
| 1720 | try std.testing.expectError( | |
| 1721 | error.InnerCarriageReturn, | |
| 1722 | sz.multilineString(@as([]const u8, &.{ 'a', '\r', 'c' }), .{}), | |
| 1723 | ); | |
| 1724 | try std.testing.expectError( | |
| 1725 | error.InnerCarriageReturn, | |
| 1726 | sz.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\n' }), .{}), | |
| 1727 | ); | |
| 1728 | try std.testing.expectError( | |
| 1729 | error.InnerCarriageReturn, | |
| 1730 | sz.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\r', '\n' }), .{}), | |
| 1731 | ); | |
| 1732 | try std.testing.expectEqualStrings("", buf.items); | |
| 1733 | buf.clearRetainingCapacity(); | |
| 1734 | } | |
| 1735 | } | |
| 1736 | } | |
| 1737 | ||
| 1738 | test "std.zon stringify skip default fields" { | |
| 1739 | const Struct = struct { | |
| 1740 | x: i32 = 2, | |
| 1741 | y: i8, | |
| 1742 | z: u32 = 4, | |
| 1743 | inner1: struct { a: u8 = 'z', b: u8 = 'y', c: u8 } = .{ | |
| 1744 | .a = '1', | |
| 1745 | .b = '2', | |
| 1746 | .c = '3', | |
| 1747 | }, | |
| 1748 | inner2: struct { u8, u8, u8 } = .{ | |
| 1749 | 'a', | |
| 1750 | 'b', | |
| 1751 | 'c', | |
| 1752 | }, | |
| 1753 | inner3: struct { u8, u8, u8 } = .{ | |
| 1754 | 'a', | |
| 1755 | 'b', | |
| 1756 | 'c', | |
| 1757 | }, | |
| 1758 | }; | |
| 1759 | ||
| 1760 | // Not skipping if not set | |
| 1761 | try expectSerializeEqual( | |
| 1762 | \\.{ | |
| 1763 | \\ .x = 2, | |
| 1764 | \\ .y = 3, | |
| 1765 | \\ .z = 4, | |
| 1766 | \\ .inner1 = .{ | |
| 1767 | \\ .a = '1', | |
| 1768 | \\ .b = '2', | |
| 1769 | \\ .c = '3', | |
| 1770 | \\ }, | |
| 1771 | \\ .inner2 = .{ | |
| 1772 | \\ 'a', | |
| 1773 | \\ 'b', | |
| 1774 | \\ 'c', | |
| 1775 | \\ }, | |
| 1776 | \\ .inner3 = .{ | |
| 1777 | \\ 'a', | |
| 1778 | \\ 'b', | |
| 1779 | \\ 'd', | |
| 1780 | \\ }, | |
| 1781 | \\} | |
| 1782 | , | |
| 1783 | Struct{ | |
| 1784 | .y = 3, | |
| 1785 | .z = 4, | |
| 1786 | .inner1 = .{ | |
| 1787 | .a = '1', | |
| 1788 | .b = '2', | |
| 1789 | .c = '3', | |
| 1790 | }, | |
| 1791 | .inner3 = .{ | |
| 1792 | 'a', | |
| 1793 | 'b', | |
| 1794 | 'd', | |
| 1795 | }, | |
| 1796 | }, | |
| 1797 | .{ .emit_codepoint_literals = .always }, | |
| 1798 | ); | |
| 1799 | ||
| 1800 | // Top level defaults | |
| 1801 | try expectSerializeEqual( | |
| 1802 | \\.{ .y = 3, .inner3 = .{ | |
| 1803 | \\ 'a', | |
| 1804 | \\ 'b', | |
| 1805 | \\ 'd', | |
| 1806 | \\} } | |
| 1807 | , | |
| 1808 | Struct{ | |
| 1809 | .y = 3, | |
| 1810 | .z = 4, | |
| 1811 | .inner1 = .{ | |
| 1812 | .a = '1', | |
| 1813 | .b = '2', | |
| 1814 | .c = '3', | |
| 1815 | }, | |
| 1816 | .inner3 = .{ | |
| 1817 | 'a', | |
| 1818 | 'b', | |
| 1819 | 'd', | |
| 1820 | }, | |
| 1821 | }, | |
| 1822 | .{ | |
| 1823 | .emit_default_optional_fields = false, | |
| 1824 | .emit_codepoint_literals = .always, | |
| 1825 | }, | |
| 1826 | ); | |
| 1827 | ||
| 1828 | // Inner types having defaults, and defaults changing the number of fields affecting the | |
| 1829 | // formatting | |
| 1830 | try expectSerializeEqual( | |
| 1831 | \\.{ | |
| 1832 | \\ .y = 3, | |
| 1833 | \\ .inner1 = .{ .b = '2', .c = '3' }, | |
| 1834 | \\ .inner3 = .{ | |
| 1835 | \\ 'a', | |
| 1836 | \\ 'b', | |
| 1837 | \\ 'd', | |
| 1838 | \\ }, | |
| 1839 | \\} | |
| 1840 | , | |
| 1841 | Struct{ | |
| 1842 | .y = 3, | |
| 1843 | .z = 4, | |
| 1844 | .inner1 = .{ | |
| 1845 | .a = 'z', | |
| 1846 | .b = '2', | |
| 1847 | .c = '3', | |
| 1848 | }, | |
| 1849 | .inner3 = .{ | |
| 1850 | 'a', | |
| 1851 | 'b', | |
| 1852 | 'd', | |
| 1853 | }, | |
| 1854 | }, | |
| 1855 | .{ | |
| 1856 | .emit_default_optional_fields = false, | |
| 1857 | .emit_codepoint_literals = .always, | |
| 1858 | }, | |
| 1859 | ); | |
| 1860 | ||
| 1861 | const DefaultStrings = struct { | |
| 1862 | foo: []const u8 = "abc", | |
| 1863 | }; | |
| 1864 | try expectSerializeEqual( | |
| 1865 | \\.{} | |
| 1866 | , | |
| 1867 | DefaultStrings{ .foo = "abc" }, | |
| 1868 | .{ .emit_default_optional_fields = false }, | |
| 1869 | ); | |
| 1870 | try expectSerializeEqual( | |
| 1871 | \\.{ .foo = "abcd" } | |
| 1872 | , | |
| 1873 | DefaultStrings{ .foo = "abcd" }, | |
| 1874 | .{ .emit_default_optional_fields = false }, | |
| 1875 | ); | |
| 1876 | } | |
| 1877 | ||
| 1878 | test "std.zon depth limits" { | |
| 1879 | var buf = std.ArrayList(u8).init(std.testing.allocator); | |
| 1880 | defer buf.deinit(); | |
| 1881 | ||
| 1882 | const Recurse = struct { r: []const @This() }; | |
| 1883 | ||
| 1884 | // Normal operation | |
| 1885 | try serializeMaxDepth(.{ 1, .{ 2, 3 } }, .{}, buf.writer(), 16); | |
| 1886 | try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", buf.items); | |
| 1887 | buf.clearRetainingCapacity(); | |
| 1888 | ||
| 1889 | try serializeArbitraryDepth(.{ 1, .{ 2, 3 } }, .{}, buf.writer()); | |
| 1890 | try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", buf.items); | |
| 1891 | buf.clearRetainingCapacity(); | |
| 1892 | ||
| 1893 | // Max depth failing on non recursive type | |
| 1894 | try std.testing.expectError( | |
| 1895 | error.ExceededMaxDepth, | |
| 1896 | serializeMaxDepth(.{ 1, .{ 2, .{ 3, 4 } } }, .{}, buf.writer(), 3), | |
| 1897 | ); | |
| 1898 | try std.testing.expectEqualStrings("", buf.items); | |
| 1899 | buf.clearRetainingCapacity(); | |
| 1900 | ||
| 1901 | // Max depth passing on recursive type | |
| 1902 | { | |
| 1903 | const maybe_recurse = Recurse{ .r = &.{} }; | |
| 1904 | try serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 2); | |
| 1905 | try std.testing.expectEqualStrings(".{ .r = .{} }", buf.items); | |
| 1906 | buf.clearRetainingCapacity(); | |
| 1907 | } | |
| 1908 | ||
| 1909 | // Unchecked passing on recursive type | |
| 1910 | { | |
| 1911 | const maybe_recurse = Recurse{ .r = &.{} }; | |
| 1912 | try serializeArbitraryDepth(maybe_recurse, .{}, buf.writer()); | |
| 1913 | try std.testing.expectEqualStrings(".{ .r = .{} }", buf.items); | |
| 1914 | buf.clearRetainingCapacity(); | |
| 1915 | } | |
| 1916 | ||
| 1917 | // Max depth failing on recursive type due to depth | |
| 1918 | { | |
| 1919 | var maybe_recurse = Recurse{ .r = &.{} }; | |
| 1920 | maybe_recurse.r = &.{.{ .r = &.{} }}; | |
| 1921 | try std.testing.expectError( | |
| 1922 | error.ExceededMaxDepth, | |
| 1923 | serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 2), | |
| 1924 | ); | |
| 1925 | try std.testing.expectEqualStrings("", buf.items); | |
| 1926 | buf.clearRetainingCapacity(); | |
| 1927 | } | |
| 1928 | ||
| 1929 | // Same but for a slice | |
| 1930 | { | |
| 1931 | var temp: [1]Recurse = .{.{ .r = &.{} }}; | |
| 1932 | const maybe_recurse: []const Recurse = &temp; | |
| 1933 | ||
| 1934 | try std.testing.expectError( | |
| 1935 | error.ExceededMaxDepth, | |
| 1936 | serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 2), | |
| 1937 | ); | |
| 1938 | try std.testing.expectEqualStrings("", buf.items); | |
| 1939 | buf.clearRetainingCapacity(); | |
| 1940 | ||
| 1941 | var sz = serializer(buf.writer(), .{}); | |
| 1942 | ||
| 1943 | try std.testing.expectError( | |
| 1944 | error.ExceededMaxDepth, | |
| 1945 | sz.tupleMaxDepth(maybe_recurse, .{}, 2), | |
| 1946 | ); | |
| 1947 | try std.testing.expectEqualStrings("", buf.items); | |
| 1948 | buf.clearRetainingCapacity(); | |
| 1949 | ||
| 1950 | try sz.tupleArbitraryDepth(maybe_recurse, .{}); | |
| 1951 | try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items); | |
| 1952 | buf.clearRetainingCapacity(); | |
| 1953 | } | |
| 1954 | ||
| 1955 | // A slice succeeding | |
| 1956 | { | |
| 1957 | var temp: [1]Recurse = .{.{ .r = &.{} }}; | |
| 1958 | const maybe_recurse: []const Recurse = &temp; | |
| 1959 | ||
| 1960 | try serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 3); | |
| 1961 | try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items); | |
| 1962 | buf.clearRetainingCapacity(); | |
| 1963 | ||
| 1964 | var sz = serializer(buf.writer(), .{}); | |
| 1965 | ||
| 1966 | try sz.tupleMaxDepth(maybe_recurse, .{}, 3); | |
| 1967 | try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items); | |
| 1968 | buf.clearRetainingCapacity(); | |
| 1969 | ||
| 1970 | try sz.tupleArbitraryDepth(maybe_recurse, .{}); | |
| 1971 | try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items); | |
| 1972 | buf.clearRetainingCapacity(); | |
| 1973 | } | |
| 1974 | ||
| 1975 | // Max depth failing on recursive type due to recursion | |
| 1976 | { | |
| 1977 | var temp: [1]Recurse = .{.{ .r = &.{} }}; | |
| 1978 | temp[0].r = &temp; | |
| 1979 | const maybe_recurse: []const Recurse = &temp; | |
| 1980 | ||
| 1981 | try std.testing.expectError( | |
| 1982 | error.ExceededMaxDepth, | |
| 1983 | serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 128), | |
| 1984 | ); | |
| 1985 | try std.testing.expectEqualStrings("", buf.items); | |
| 1986 | buf.clearRetainingCapacity(); | |
| 1987 | ||
| 1988 | var sz = serializer(buf.writer(), .{}); | |
| 1989 | try std.testing.expectError( | |
| 1990 | error.ExceededMaxDepth, | |
| 1991 | sz.tupleMaxDepth(maybe_recurse, .{}, 128), | |
| 1992 | ); | |
| 1993 | try std.testing.expectEqualStrings("", buf.items); | |
| 1994 | buf.clearRetainingCapacity(); | |
| 1995 | } | |
| 1996 | ||
| 1997 | // Max depth on other parts of the lower level API | |
| 1998 | { | |
| 1999 | var sz = serializer(buf.writer(), .{}); | |
| 2000 | ||
| 2001 | const maybe_recurse: []const Recurse = &.{}; | |
| 2002 | ||
| 2003 | try std.testing.expectError(error.ExceededMaxDepth, sz.valueMaxDepth(1, .{}, 0)); | |
| 2004 | try sz.valueMaxDepth(2, .{}, 1); | |
| 2005 | try sz.value(3, .{}); | |
| 2006 | try sz.valueArbitraryDepth(maybe_recurse, .{}); | |
| 2007 | ||
| 2008 | var s = try sz.startStruct(.{}); | |
| 2009 | try std.testing.expectError(error.ExceededMaxDepth, s.fieldMaxDepth("a", 1, .{}, 0)); | |
| 2010 | try s.fieldMaxDepth("b", 4, .{}, 1); | |
| 2011 | try s.field("c", 5, .{}); | |
| 2012 | try s.fieldArbitraryDepth("d", maybe_recurse, .{}); | |
| 2013 | try s.finish(); | |
| 2014 | ||
| 2015 | var t = try sz.startTuple(.{}); | |
| 2016 | try std.testing.expectError(error.ExceededMaxDepth, t.fieldMaxDepth(1, .{}, 0)); | |
| 2017 | try t.fieldMaxDepth(6, .{}, 1); | |
| 2018 | try t.field(7, .{}); | |
| 2019 | try t.fieldArbitraryDepth(maybe_recurse, .{}); | |
| 2020 | try t.finish(); | |
| 2021 | ||
| 2022 | var a = try sz.startTuple(.{}); | |
| 2023 | try std.testing.expectError(error.ExceededMaxDepth, a.fieldMaxDepth(1, .{}, 0)); | |
| 2024 | try a.fieldMaxDepth(8, .{}, 1); | |
| 2025 | try a.field(9, .{}); | |
| 2026 | try a.fieldArbitraryDepth(maybe_recurse, .{}); | |
| 2027 | try a.finish(); | |
| 2028 | ||
| 2029 | try std.testing.expectEqualStrings( | |
| 2030 | \\23.{}.{ | |
| 2031 | \\ .b = 4, | |
| 2032 | \\ .c = 5, | |
| 2033 | \\ .d = .{}, | |
| 2034 | \\}.{ | |
| 2035 | \\ 6, | |
| 2036 | \\ 7, | |
| 2037 | \\ .{}, | |
| 2038 | \\}.{ | |
| 2039 | \\ 8, | |
| 2040 | \\ 9, | |
| 2041 | \\ .{}, | |
| 2042 | \\} | |
| 2043 | , buf.items); | |
| 2044 | } | |
| 2045 | } | |
| 2046 | ||
| 2047 | test "std.zon stringify primitives" { | |
| 2048 | // Issue: https://github.com/ziglang/zig/issues/20880 | |
| 2049 | if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; | |
| 2050 | ||
| 2051 | try expectSerializeEqual( | |
| 2052 | \\.{ | |
| 2053 | \\ .a = 1.5, | |
| 2054 | \\ .b = 0.3333333333333333333333333333333333, | |
| 2055 | \\ .c = 3.1415926535897932384626433832795028, | |
| 2056 | \\ .d = 0, | |
| 2057 | \\ .e = -0, | |
| 2058 | \\ .f = inf, | |
| 2059 | \\ .g = -inf, | |
| 2060 | \\ .h = nan, | |
| 2061 | \\} | |
| 2062 | , | |
| 2063 | .{ | |
| 2064 | .a = @as(f128, 1.5), // Make sure explicit f128s work | |
| 2065 | .b = 1.0 / 3.0, | |
| 2066 | .c = std.math.pi, | |
| 2067 | .d = 0.0, | |
| 2068 | .e = -0.0, | |
| 2069 | .f = std.math.inf(f32), | |
| 2070 | .g = -std.math.inf(f32), | |
| 2071 | .h = std.math.nan(f32), | |
| 2072 | }, | |
| 2073 | .{}, | |
| 2074 | ); | |
| 2075 | ||
| 2076 | try expectSerializeEqual( | |
| 2077 | \\.{ | |
| 2078 | \\ .a = 18446744073709551616, | |
| 2079 | \\ .b = -18446744073709551616, | |
| 2080 | \\ .c = 680564733841876926926749214863536422912, | |
| 2081 | \\ .d = -680564733841876926926749214863536422912, | |
| 2082 | \\ .e = 0, | |
| 2083 | \\} | |
| 2084 | , | |
| 2085 | .{ | |
| 2086 | .a = 18446744073709551616, | |
| 2087 | .b = -18446744073709551616, | |
| 2088 | .c = 680564733841876926926749214863536422912, | |
| 2089 | .d = -680564733841876926926749214863536422912, | |
| 2090 | .e = 0, | |
| 2091 | }, | |
| 2092 | .{}, | |
| 2093 | ); | |
| 2094 | ||
| 2095 | try expectSerializeEqual( | |
| 2096 | \\.{ | |
| 2097 | \\ .a = true, | |
| 2098 | \\ .b = false, | |
| 2099 | \\ .c = .foo, | |
| 2100 | \\ .e = null, | |
| 2101 | \\} | |
| 2102 | , | |
| 2103 | .{ | |
| 2104 | .a = true, | |
| 2105 | .b = false, | |
| 2106 | .c = .foo, | |
| 2107 | .e = null, | |
| 2108 | }, | |
| 2109 | .{}, | |
| 2110 | ); | |
| 2111 | ||
| 2112 | const Struct = struct { x: f32, y: f32 }; | |
| 2113 | try expectSerializeEqual( | |
| 2114 | ".{ .a = .{ .x = 1, .y = 2 }, .b = null }", | |
| 2115 | .{ | |
| 2116 | .a = @as(?Struct, .{ .x = 1, .y = 2 }), | |
| 2117 | .b = @as(?Struct, null), | |
| 2118 | }, | |
| 2119 | .{}, | |
| 2120 | ); | |
| 2121 | ||
| 2122 | const E = enum(u8) { | |
| 2123 | foo, | |
| 2124 | bar, | |
| 2125 | }; | |
| 2126 | try expectSerializeEqual( | |
| 2127 | ".{ .a = .foo, .b = .foo }", | |
| 2128 | .{ | |
| 2129 | .a = .foo, | |
| 2130 | .b = E.foo, | |
| 2131 | }, | |
| 2132 | .{}, | |
| 2133 | ); | |
| 2134 | } | |
| 2135 | ||
| 2136 | test "std.zon stringify ident" { | |
| 2137 | var buf = std.ArrayList(u8).init(std.testing.allocator); | |
| 2138 | defer buf.deinit(); | |
| 2139 | var sz = serializer(buf.writer(), .{}); | |
| 2140 | ||
| 2141 | try expectSerializeEqual(".{ .a = 0 }", .{ .a = 0 }, .{}); | |
| 2142 | try sz.ident("a"); | |
| 2143 | try std.testing.expectEqualStrings(".a", buf.items); | |
| 2144 | buf.clearRetainingCapacity(); | |
| 2145 | ||
| 2146 | try sz.ident("foo_1"); | |
| 2147 | try std.testing.expectEqualStrings(".foo_1", buf.items); | |
| 2148 | buf.clearRetainingCapacity(); | |
| 2149 | ||
| 2150 | try sz.ident("_foo_1"); | |
| 2151 | try std.testing.expectEqualStrings("._foo_1", buf.items); | |
| 2152 | buf.clearRetainingCapacity(); | |
| 2153 | ||
| 2154 | try sz.ident("foo bar"); | |
| 2155 | try std.testing.expectEqualStrings(".@\"foo bar\"", buf.items); | |
| 2156 | buf.clearRetainingCapacity(); | |
| 2157 | ||
| 2158 | try sz.ident("1foo"); | |
| 2159 | try std.testing.expectEqualStrings(".@\"1foo\"", buf.items); | |
| 2160 | buf.clearRetainingCapacity(); | |
| 2161 | ||
| 2162 | try sz.ident("var"); | |
| 2163 | try std.testing.expectEqualStrings(".@\"var\"", buf.items); | |
| 2164 | buf.clearRetainingCapacity(); | |
| 2165 | ||
| 2166 | try sz.ident("true"); | |
| 2167 | try std.testing.expectEqualStrings(".true", buf.items); | |
| 2168 | buf.clearRetainingCapacity(); | |
| 2169 | ||
| 2170 | try sz.ident("_"); | |
| 2171 | try std.testing.expectEqualStrings("._", buf.items); | |
| 2172 | buf.clearRetainingCapacity(); | |
| 2173 | ||
| 2174 | const Enum = enum { | |
| 2175 | @"foo bar", | |
| 2176 | }; | |
| 2177 | try expectSerializeEqual(".{ .@\"var\" = .@\"foo bar\", .@\"1\" = .@\"foo bar\" }", .{ | |
| 2178 | .@"var" = .@"foo bar", | |
| 2179 | .@"1" = Enum.@"foo bar", | |
| 2180 | }, .{}); | |
| 2181 | } | |
| 2182 | ||
| 2183 | test "std.zon stringify as tuple" { | |
| 2184 | var buf = std.ArrayList(u8).init(std.testing.allocator); | |
| 2185 | defer buf.deinit(); | |
| 2186 | var sz = serializer(buf.writer(), .{}); | |
| 2187 | ||
| 2188 | // Tuples | |
| 2189 | try sz.tuple(.{ 1, 2 }, .{}); | |
| 2190 | try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items); | |
| 2191 | buf.clearRetainingCapacity(); | |
| 2192 | ||
| 2193 | // Slice | |
| 2194 | try sz.tuple(@as([]const u8, &.{ 1, 2 }), .{}); | |
| 2195 | try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items); | |
| 2196 | buf.clearRetainingCapacity(); | |
| 2197 | ||
| 2198 | // Array | |
| 2199 | try sz.tuple([2]u8{ 1, 2 }, .{}); | |
| 2200 | try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items); | |
| 2201 | buf.clearRetainingCapacity(); | |
| 2202 | } | |
| 2203 | ||
| 2204 | test "std.zon stringify as float" { | |
| 2205 | var buf = std.ArrayList(u8).init(std.testing.allocator); | |
| 2206 | defer buf.deinit(); | |
| 2207 | var sz = serializer(buf.writer(), .{}); | |
| 2208 | ||
| 2209 | // Comptime float | |
| 2210 | try sz.float(2.5); | |
| 2211 | try std.testing.expectEqualStrings("2.5", buf.items); | |
| 2212 | buf.clearRetainingCapacity(); | |
| 2213 | ||
| 2214 | // Sized float | |
| 2215 | try sz.float(@as(f32, 2.5)); | |
| 2216 | try std.testing.expectEqualStrings("2.5", buf.items); | |
| 2217 | buf.clearRetainingCapacity(); | |
| 2218 | } | |
| 2219 | ||
| 2220 | test "std.zon stringify vector" { | |
| 2221 | try expectSerializeEqual( | |
| 2222 | \\.{ | |
| 2223 | \\ .{}, | |
| 2224 | \\ .{ | |
| 2225 | \\ true, | |
| 2226 | \\ false, | |
| 2227 | \\ true, | |
| 2228 | \\ }, | |
| 2229 | \\ .{}, | |
| 2230 | \\ .{ | |
| 2231 | \\ 1.5, | |
| 2232 | \\ 2.5, | |
| 2233 | \\ 3.5, | |
| 2234 | \\ }, | |
| 2235 | \\ .{}, | |
| 2236 | \\ .{ | |
| 2237 | \\ 2, | |
| 2238 | \\ 4, | |
| 2239 | \\ 6, | |
| 2240 | \\ }, | |
| 2241 | \\ .{ 1, 2 }, | |
| 2242 | \\ .{ | |
| 2243 | \\ 3, | |
| 2244 | \\ 4, | |
| 2245 | \\ null, | |
| 2246 | \\ }, | |
| 2247 | \\} | |
| 2248 | , | |
| 2249 | .{ | |
| 2250 | @Vector(0, bool){}, | |
| 2251 | @Vector(3, bool){ true, false, true }, | |
| 2252 | @Vector(0, f32){}, | |
| 2253 | @Vector(3, f32){ 1.5, 2.5, 3.5 }, | |
| 2254 | @Vector(0, u8){}, | |
| 2255 | @Vector(3, u8){ 2, 4, 6 }, | |
| 2256 | @Vector(2, *const u8){ &1, &2 }, | |
| 2257 | @Vector(3, ?*const u8){ &3, &4, null }, | |
| 2258 | }, | |
| 2259 | .{}, | |
| 2260 | ); | |
| 2261 | } | |
| 2262 | ||
| 2263 | test "std.zon pointers" { | |
| 2264 | // Primitive with varying levels of pointers | |
| 2265 | try expectSerializeEqual("10", &@as(u32, 10), .{}); | |
| 2266 | try expectSerializeEqual("10", &&@as(u32, 10), .{}); | |
| 2267 | try expectSerializeEqual("10", &&&@as(u32, 10), .{}); | |
| 2268 | ||
| 2269 | // Primitive optional with varying levels of pointers | |
| 2270 | try expectSerializeEqual("10", @as(?*const u32, &10), .{}); | |
| 2271 | try expectSerializeEqual("null", @as(?*const u32, null), .{}); | |
| 2272 | try expectSerializeEqual("10", @as(?*const u32, &10), .{}); | |
| 2273 | try expectSerializeEqual("null", @as(*const ?u32, &null), .{}); | |
| 2274 | ||
| 2275 | try expectSerializeEqual("10", @as(?*const *const u32, &&10), .{}); | |
| 2276 | try expectSerializeEqual("null", @as(?*const *const u32, null), .{}); | |
| 2277 | try expectSerializeEqual("10", @as(*const ?*const u32, &&10), .{}); | |
| 2278 | try expectSerializeEqual("null", @as(*const ?*const u32, &null), .{}); | |
| 2279 | try expectSerializeEqual("10", @as(*const *const ?u32, &&10), .{}); | |
| 2280 | try expectSerializeEqual("null", @as(*const *const ?u32, &&null), .{}); | |
| 2281 | ||
| 2282 | try expectSerializeEqual(".{ 1, 2 }", &[2]u32{ 1, 2 }, .{}); | |
| 2283 | ||
| 2284 | // A complicated type with nested internal pointers and string allocations | |
| 2285 | { | |
| 2286 | const Inner = struct { | |
| 2287 | f1: *const ?*const []const u8, | |
| 2288 | f2: *const ?*const []const u8, | |
| 2289 | }; | |
| 2290 | const Outer = struct { | |
| 2291 | f1: *const ?*const Inner, | |
| 2292 | f2: *const ?*const Inner, | |
| 2293 | }; | |
| 2294 | const val: ?*const Outer = &.{ | |
| 2295 | .f1 = &&.{ | |
| 2296 | .f1 = &null, | |
| 2297 | .f2 = &&"foo", | |
| 2298 | }, | |
| 2299 | .f2 = &null, | |
| 2300 | }; | |
| 2301 | ||
| 2302 | try expectSerializeEqual( | |
| 2303 | \\.{ .f1 = .{ .f1 = null, .f2 = "foo" }, .f2 = null } | |
| 2304 | , val, .{}); | |
| 2305 | } | |
| 2306 | } |
src/Compilation.zig+25-6| ... | ... | @@ -2220,7 +2220,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2220 | 2220 | try comp.astgen_work_queue.ensureUnusedCapacity(zcu.import_table.count()); |
| 2221 | 2221 | for (zcu.import_table.values()) |file_index| { |
| 2222 | 2222 | if (zcu.fileByIndex(file_index).mod.isBuiltin()) continue; |
| 2223 | comp.astgen_work_queue.writeItemAssumeCapacity(file_index); | |
| 2223 | const file = zcu.fileByIndex(file_index); | |
| 2224 | if (file.getMode() == .zig) { | |
| 2225 | comp.astgen_work_queue.writeItemAssumeCapacity(file_index); | |
| 2226 | } | |
| 2224 | 2227 | } |
| 2225 | 2228 | if (comp.file_system_inputs) |fsi| { |
| 2226 | 2229 | for (zcu.import_table.values()) |file_index| { |
| ... | ... | @@ -3206,10 +3209,16 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3206 | 3209 | if (error_msg) |msg| { |
| 3207 | 3210 | try addModuleErrorMsg(zcu, &bundle, msg.*); |
| 3208 | 3211 | } else { |
| 3209 | // Must be ZIR errors. Note that this may include AST errors. | |
| 3210 | // addZirErrorMessages asserts that the tree is loaded. | |
| 3211 | _ = try file.getTree(gpa); | |
| 3212 | try addZirErrorMessages(&bundle, file); | |
| 3212 | // Must be ZIR or Zoir errors. Note that this may include AST errors. | |
| 3213 | _ = try file.getTree(gpa); // Tree must be loaded. | |
| 3214 | if (file.zir_loaded) { | |
| 3215 | try addZirErrorMessages(&bundle, file); | |
| 3216 | } else if (file.zoir != null) { | |
| 3217 | try addZoirErrorMessages(&bundle, file); | |
| 3218 | } else { | |
| 3219 | // Either Zir or Zoir must have been loaded. | |
| 3220 | unreachable; | |
| 3221 | } | |
| 3213 | 3222 | } |
| 3214 | 3223 | } |
| 3215 | 3224 | var sorted_failed_analysis: std.AutoArrayHashMapUnmanaged(InternPool.AnalUnit, *Zcu.ErrorMsg).DataList.Slice = s: { |
| ... | ... | @@ -3623,6 +3632,15 @@ pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void { |
| 3623 | 3632 | return eb.addZirErrorMessages(file.zir, file.tree, file.source, src_path); |
| 3624 | 3633 | } |
| 3625 | 3634 | |
| 3635 | pub fn addZoirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void { | |
| 3636 | assert(file.source_loaded); | |
| 3637 | assert(file.tree_loaded); | |
| 3638 | const gpa = eb.gpa; | |
| 3639 | const src_path = try file.fullPath(gpa); | |
| 3640 | defer gpa.free(src_path); | |
| 3641 | return eb.addZoirErrorMessages(file.zoir.?, file.tree, file.source, src_path); | |
| 3642 | } | |
| 3643 | ||
| 3626 | 3644 | pub fn performAllTheWork( |
| 3627 | 3645 | comp: *Compilation, |
| 3628 | 3646 | main_progress_node: std.Progress.Node, |
| ... | ... | @@ -4272,6 +4290,7 @@ fn workerAstGenFile( |
| 4272 | 4290 | wg: *WaitGroup, |
| 4273 | 4291 | src: Zcu.AstGenSrc, |
| 4274 | 4292 | ) void { |
| 4293 | assert(file.getMode() == .zig); | |
| 4275 | 4294 | const child_prog_node = prog_node.start(file.sub_file_path, 0); |
| 4276 | 4295 | defer child_prog_node.end(); |
| 4277 | 4296 | |
| ... | ... | @@ -4325,7 +4344,7 @@ fn workerAstGenFile( |
| 4325 | 4344 | const imported_path_digest = pt.zcu.filePathDigest(res.file_index); |
| 4326 | 4345 | break :blk .{ res, imported_path_digest }; |
| 4327 | 4346 | }; |
| 4328 | if (import_result.is_new) { | |
| 4347 | if (import_result.is_new and import_result.file.getMode() == .zig) { | |
| 4329 | 4348 | log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{ |
| 4330 | 4349 | file.sub_file_path, import_path, import_result.file.sub_file_path, |
| 4331 | 4350 | }); |
src/InternPool.zig+9-9| ... | ... | @@ -4389,7 +4389,7 @@ pub const LoadedEnumType = struct { |
| 4389 | 4389 | // Auto-numbered enum. Convert `int_tag_val` to field index. |
| 4390 | 4390 | const field_index = switch (ip.indexToKey(int_tag_val).int.storage) { |
| 4391 | 4391 | inline .u64, .i64 => |x| std.math.cast(u32, x) orelse return null, |
| 4392 | .big_int => |x| x.to(u32) catch return null, | |
| 4392 | .big_int => |x| x.toInt(u32) catch return null, | |
| 4393 | 4393 | .lazy_align, .lazy_size => unreachable, |
| 4394 | 4394 | }; |
| 4395 | 4395 | return if (field_index < self.names.len) field_index else null; |
| ... | ... | @@ -7957,7 +7957,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 7957 | 7957 | .big_int => |big_int| { |
| 7958 | 7958 | items.appendAssumeCapacity(.{ |
| 7959 | 7959 | .tag = .int_u8, |
| 7960 | .data = big_int.to(u8) catch unreachable, | |
| 7960 | .data = big_int.toInt(u8) catch unreachable, | |
| 7961 | 7961 | }); |
| 7962 | 7962 | break :b; |
| 7963 | 7963 | }, |
| ... | ... | @@ -7974,7 +7974,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 7974 | 7974 | .big_int => |big_int| { |
| 7975 | 7975 | items.appendAssumeCapacity(.{ |
| 7976 | 7976 | .tag = .int_u16, |
| 7977 | .data = big_int.to(u16) catch unreachable, | |
| 7977 | .data = big_int.toInt(u16) catch unreachable, | |
| 7978 | 7978 | }); |
| 7979 | 7979 | break :b; |
| 7980 | 7980 | }, |
| ... | ... | @@ -7991,7 +7991,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 7991 | 7991 | .big_int => |big_int| { |
| 7992 | 7992 | items.appendAssumeCapacity(.{ |
| 7993 | 7993 | .tag = .int_u32, |
| 7994 | .data = big_int.to(u32) catch unreachable, | |
| 7994 | .data = big_int.toInt(u32) catch unreachable, | |
| 7995 | 7995 | }); |
| 7996 | 7996 | break :b; |
| 7997 | 7997 | }, |
| ... | ... | @@ -8006,7 +8006,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 8006 | 8006 | }, |
| 8007 | 8007 | .i32_type => switch (int.storage) { |
| 8008 | 8008 | .big_int => |big_int| { |
| 8009 | const casted = big_int.to(i32) catch unreachable; | |
| 8009 | const casted = big_int.toInt(i32) catch unreachable; | |
| 8010 | 8010 | items.appendAssumeCapacity(.{ |
| 8011 | 8011 | .tag = .int_i32, |
| 8012 | 8012 | .data = @as(u32, @bitCast(casted)), |
| ... | ... | @@ -8024,7 +8024,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 8024 | 8024 | }, |
| 8025 | 8025 | .usize_type => switch (int.storage) { |
| 8026 | 8026 | .big_int => |big_int| { |
| 8027 | if (big_int.to(u32)) |casted| { | |
| 8027 | if (big_int.toInt(u32)) |casted| { | |
| 8028 | 8028 | items.appendAssumeCapacity(.{ |
| 8029 | 8029 | .tag = .int_usize, |
| 8030 | 8030 | .data = casted, |
| ... | ... | @@ -8045,14 +8045,14 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 8045 | 8045 | }, |
| 8046 | 8046 | .comptime_int_type => switch (int.storage) { |
| 8047 | 8047 | .big_int => |big_int| { |
| 8048 | if (big_int.to(u32)) |casted| { | |
| 8048 | if (big_int.toInt(u32)) |casted| { | |
| 8049 | 8049 | items.appendAssumeCapacity(.{ |
| 8050 | 8050 | .tag = .int_comptime_int_u32, |
| 8051 | 8051 | .data = casted, |
| 8052 | 8052 | }); |
| 8053 | 8053 | break :b; |
| 8054 | 8054 | } else |_| {} |
| 8055 | if (big_int.to(i32)) |casted| { | |
| 8055 | if (big_int.toInt(i32)) |casted| { | |
| 8056 | 8056 | items.appendAssumeCapacity(.{ |
| 8057 | 8057 | .tag = .int_comptime_int_i32, |
| 8058 | 8058 | .data = @as(u32, @bitCast(casted)), |
| ... | ... | @@ -8082,7 +8082,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 8082 | 8082 | } |
| 8083 | 8083 | switch (int.storage) { |
| 8084 | 8084 | .big_int => |big_int| { |
| 8085 | if (big_int.to(u32)) |casted| { | |
| 8085 | if (big_int.toInt(u32)) |casted| { | |
| 8086 | 8086 | items.appendAssumeCapacity(.{ |
| 8087 | 8087 | .tag = .int_small, |
| 8088 | 8088 | .data = try addExtra(extra, IntSmall{ |
src/Sema.zig+41-9| ... | ... | @@ -187,6 +187,7 @@ const Alignment = InternPool.Alignment; |
| 187 | 187 | const AnalUnit = InternPool.AnalUnit; |
| 188 | 188 | const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; |
| 189 | 189 | const Cache = std.Build.Cache; |
| 190 | const LowerZon = @import("Sema/LowerZon.zig"); | |
| 190 | 191 | |
| 191 | 192 | pub const default_branch_quota = 1000; |
| 192 | 193 | pub const default_reference_trace_len = 2; |
| ... | ... | @@ -5790,7 +5791,7 @@ fn addNullTerminatedStrLit(sema: *Sema, string: InternPool.NullTerminatedString) |
| 5790 | 5791 | return sema.addStrLit(string.toString(), string.length(&sema.pt.zcu.intern_pool)); |
| 5791 | 5792 | } |
| 5792 | 5793 | |
| 5793 | fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!Air.Inst.Ref { | |
| 5794 | pub fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!Air.Inst.Ref { | |
| 5794 | 5795 | const pt = sema.pt; |
| 5795 | 5796 | const array_ty = try pt.arrayType(.{ |
| 5796 | 5797 | .len = len, |
| ... | ... | @@ -13964,9 +13965,10 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 13964 | 13965 | |
| 13965 | 13966 | const pt = sema.pt; |
| 13966 | 13967 | const zcu = pt.zcu; |
| 13967 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; | |
| 13968 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok; | |
| 13969 | const extra = sema.code.extraData(Zir.Inst.Import, inst_data.payload_index).data; | |
| 13968 | 13970 | const operand_src = block.tokenOffset(inst_data.src_tok); |
| 13969 | const operand = inst_data.get(sema.code); | |
| 13971 | const operand = sema.code.nullTerminatedString(extra.path); | |
| 13970 | 13972 | |
| 13971 | 13973 | const result = pt.importFile(block.getFileScope(zcu), operand) catch |err| switch (err) { |
| 13972 | 13974 | error.ImportOutsideModulePath => { |
| ... | ... | @@ -13983,12 +13985,42 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 13983 | 13985 | return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ operand, @errorName(err) }); |
| 13984 | 13986 | }, |
| 13985 | 13987 | }; |
| 13986 | try sema.declareDependency(.{ .file = result.file_index }); | |
| 13987 | try pt.ensureFileAnalyzed(result.file_index); | |
| 13988 | const ty = zcu.fileRootType(result.file_index); | |
| 13989 | try sema.declareDependency(.{ .interned = ty }); | |
| 13990 | try sema.addTypeReferenceEntry(operand_src, ty); | |
| 13991 | return Air.internedToRef(ty); | |
| 13988 | switch (result.file.getMode()) { | |
| 13989 | .zig => { | |
| 13990 | try sema.declareDependency(.{ .file = result.file_index }); | |
| 13991 | try pt.ensureFileAnalyzed(result.file_index); | |
| 13992 | const ty = zcu.fileRootType(result.file_index); | |
| 13993 | try sema.declareDependency(.{ .interned = ty }); | |
| 13994 | try sema.addTypeReferenceEntry(operand_src, ty); | |
| 13995 | return Air.internedToRef(ty); | |
| 13996 | }, | |
| 13997 | .zon => { | |
| 13998 | _ = result.file.getTree(zcu.gpa) catch |err| { | |
| 13999 | // TODO: these errors are file system errors; make sure an update() will | |
| 14000 | // retry this and not cache the file system error, which may be transient. | |
| 14001 | return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ result.file.sub_file_path, @errorName(err) }); | |
| 14002 | }; | |
| 14003 | ||
| 14004 | if (extra.res_ty == .none) { | |
| 14005 | return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{}); | |
| 14006 | } | |
| 14007 | const res_ty_inst = try sema.resolveInst(extra.res_ty); | |
| 14008 | const res_ty = try sema.analyzeAsType(block, operand_src, res_ty_inst); | |
| 14009 | if (res_ty.isGenericPoison()) { | |
| 14010 | return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{}); | |
| 14011 | } | |
| 14012 | ||
| 14013 | const interned = try LowerZon.run( | |
| 14014 | sema, | |
| 14015 | result.file, | |
| 14016 | result.file_index, | |
| 14017 | res_ty, | |
| 14018 | operand_src, | |
| 14019 | block, | |
| 14020 | ); | |
| 14021 | return Air.internedToRef(interned); | |
| 14022 | }, | |
| 14023 | } | |
| 13992 | 14024 | } |
| 13993 | 14025 | |
| 13994 | 14026 | fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
src/Sema/LowerZon.zig created+858| ... | ... | @@ -0,0 +1,858 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Zcu = @import("../Zcu.zig"); | |
| 3 | const Sema = @import("../Sema.zig"); | |
| 4 | const Air = @import("../Air.zig"); | |
| 5 | const InternPool = @import("../InternPool.zig"); | |
| 6 | const Type = @import("../Type.zig"); | |
| 7 | const Value = @import("../Value.zig"); | |
| 8 | const Zir = std.zig.Zir; | |
| 9 | const AstGen = std.zig.AstGen; | |
| 10 | const CompileError = Zcu.CompileError; | |
| 11 | const Ast = std.zig.Ast; | |
| 12 | const Allocator = std.mem.Allocator; | |
| 13 | const assert = std.debug.assert; | |
| 14 | const File = Zcu.File; | |
| 15 | const LazySrcLoc = Zcu.LazySrcLoc; | |
| 16 | const Ref = std.zig.Zir.Inst.Ref; | |
| 17 | const NullTerminatedString = InternPool.NullTerminatedString; | |
| 18 | const NumberLiteralError = std.zig.number_literal.Error; | |
| 19 | const NodeIndex = std.zig.Ast.Node.Index; | |
| 20 | const Zoir = std.zig.Zoir; | |
| 21 | ||
| 22 | const LowerZon = @This(); | |
| 23 | ||
| 24 | sema: *Sema, | |
| 25 | file: *File, | |
| 26 | file_index: Zcu.File.Index, | |
| 27 | import_loc: LazySrcLoc, | |
| 28 | block: *Sema.Block, | |
| 29 | base_node_inst: InternPool.TrackedInst.Index, | |
| 30 | ||
| 31 | /// Lowers the given file as ZON. | |
| 32 | pub fn run( | |
| 33 | sema: *Sema, | |
| 34 | file: *File, | |
| 35 | file_index: Zcu.File.Index, | |
| 36 | res_ty: Type, | |
| 37 | import_loc: LazySrcLoc, | |
| 38 | block: *Sema.Block, | |
| 39 | ) CompileError!InternPool.Index { | |
| 40 | const pt = sema.pt; | |
| 41 | ||
| 42 | _ = try file.getZoir(pt.zcu); | |
| 43 | ||
| 44 | const tracked_inst = try pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{ | |
| 45 | .file = file_index, | |
| 46 | .inst = .main_struct_inst, // this is the only trackable instruction in a ZON file | |
| 47 | }); | |
| 48 | ||
| 49 | var lower_zon: LowerZon = .{ | |
| 50 | .sema = sema, | |
| 51 | .file = file, | |
| 52 | .file_index = file_index, | |
| 53 | .import_loc = import_loc, | |
| 54 | .block = block, | |
| 55 | .base_node_inst = tracked_inst, | |
| 56 | }; | |
| 57 | ||
| 58 | try lower_zon.checkType(res_ty); | |
| 59 | ||
| 60 | return lower_zon.lowerExpr(.root, res_ty); | |
| 61 | } | |
| 62 | ||
| 63 | /// Validate that `ty` is a valid ZON type. If not, emit a compile error. | |
| 64 | /// i.e. no nested optionals, no error sets, etc. | |
| 65 | fn checkType(self: *LowerZon, ty: Type) !void { | |
| 66 | var visited: std.AutoHashMapUnmanaged(InternPool.Index, void) = .empty; | |
| 67 | try self.checkTypeInner(ty, null, &visited); | |
| 68 | } | |
| 69 | ||
| 70 | fn checkTypeInner( | |
| 71 | self: *LowerZon, | |
| 72 | ty: Type, | |
| 73 | parent_opt_ty: ?Type, | |
| 74 | /// Visited structs and unions (not tuples). These are tracked because they are the only way in | |
| 75 | /// which a type can be self-referential, so must be tracked to avoid loops. Tracking more types | |
| 76 | /// consumes memory unnecessarily, and would be complicated by optionals. | |
| 77 | /// Allocated into `self.sema.arena`. | |
| 78 | visited: *std.AutoHashMapUnmanaged(InternPool.Index, void), | |
| 79 | ) !void { | |
| 80 | const sema = self.sema; | |
| 81 | const pt = sema.pt; | |
| 82 | const zcu = pt.zcu; | |
| 83 | const ip = &zcu.intern_pool; | |
| 84 | ||
| 85 | switch (ty.zigTypeTag(zcu)) { | |
| 86 | .bool, | |
| 87 | .int, | |
| 88 | .float, | |
| 89 | .null, | |
| 90 | .@"enum", | |
| 91 | .comptime_float, | |
| 92 | .comptime_int, | |
| 93 | .enum_literal, | |
| 94 | => {}, | |
| 95 | ||
| 96 | .noreturn, | |
| 97 | .void, | |
| 98 | .type, | |
| 99 | .undefined, | |
| 100 | .error_union, | |
| 101 | .error_set, | |
| 102 | .@"fn", | |
| 103 | .frame, | |
| 104 | .@"anyframe", | |
| 105 | .@"opaque", | |
| 106 | => return self.failUnsupportedResultType(ty, null), | |
| 107 | ||
| 108 | .pointer => { | |
| 109 | const ptr_info = ty.ptrInfo(zcu); | |
| 110 | if (!ptr_info.flags.is_const) { | |
| 111 | return self.failUnsupportedResultType( | |
| 112 | ty, | |
| 113 | "ZON does not allow mutable pointers", | |
| 114 | ); | |
| 115 | } | |
| 116 | switch (ptr_info.flags.size) { | |
| 117 | .one => try self.checkTypeInner( | |
| 118 | .fromInterned(ptr_info.child), | |
| 119 | parent_opt_ty, // preserved | |
| 120 | visited, | |
| 121 | ), | |
| 122 | .slice => try self.checkTypeInner( | |
| 123 | .fromInterned(ptr_info.child), | |
| 124 | null, | |
| 125 | visited, | |
| 126 | ), | |
| 127 | .many => return self.failUnsupportedResultType(ty, "ZON does not allow many-pointers"), | |
| 128 | .c => return self.failUnsupportedResultType(ty, "ZON does not allow C pointers"), | |
| 129 | } | |
| 130 | }, | |
| 131 | .optional => if (parent_opt_ty) |p| { | |
| 132 | return self.failUnsupportedResultType(p, "ZON does not allow nested optionals"); | |
| 133 | } else try self.checkTypeInner( | |
| 134 | ty.optionalChild(zcu), | |
| 135 | ty, | |
| 136 | visited, | |
| 137 | ), | |
| 138 | .array, .vector => { | |
| 139 | try self.checkTypeInner(ty.childType(zcu), null, visited); | |
| 140 | }, | |
| 141 | .@"struct" => if (ty.isTuple(zcu)) { | |
| 142 | const tuple_info = ip.indexToKey(ty.toIntern()).tuple_type; | |
| 143 | const field_types = tuple_info.types.get(ip); | |
| 144 | for (field_types) |field_type| { | |
| 145 | try self.checkTypeInner(.fromInterned(field_type), null, visited); | |
| 146 | } | |
| 147 | } else { | |
| 148 | const gop = try visited.getOrPut(sema.arena, ty.toIntern()); | |
| 149 | if (gop.found_existing) return; | |
| 150 | try ty.resolveFields(pt); | |
| 151 | const struct_info = zcu.typeToStruct(ty).?; | |
| 152 | for (struct_info.field_types.get(ip)) |field_type| { | |
| 153 | try self.checkTypeInner(.fromInterned(field_type), null, visited); | |
| 154 | } | |
| 155 | }, | |
| 156 | .@"union" => { | |
| 157 | const gop = try visited.getOrPut(sema.arena, ty.toIntern()); | |
| 158 | if (gop.found_existing) return; | |
| 159 | try ty.resolveFields(pt); | |
| 160 | const union_info = zcu.typeToUnion(ty).?; | |
| 161 | for (union_info.field_types.get(ip)) |field_type| { | |
| 162 | if (field_type != .void_type) { | |
| 163 | try self.checkTypeInner(.fromInterned(field_type), null, visited); | |
| 164 | } | |
| 165 | } | |
| 166 | }, | |
| 167 | } | |
| 168 | } | |
| 169 | ||
| 170 | fn nodeSrc(self: *LowerZon, node: Zoir.Node.Index) LazySrcLoc { | |
| 171 | return .{ | |
| 172 | .base_node_inst = self.base_node_inst, | |
| 173 | .offset = .{ .node_abs = node.getAstNode(self.file.zoir.?) }, | |
| 174 | }; | |
| 175 | } | |
| 176 | ||
| 177 | fn failUnsupportedResultType( | |
| 178 | self: *LowerZon, | |
| 179 | ty: Type, | |
| 180 | opt_note: ?[]const u8, | |
| 181 | ) error{ AnalysisFail, OutOfMemory } { | |
| 182 | @branchHint(.cold); | |
| 183 | const sema = self.sema; | |
| 184 | const gpa = sema.gpa; | |
| 185 | const pt = sema.pt; | |
| 186 | return sema.failWithOwnedErrorMsg(self.block, msg: { | |
| 187 | const msg = try sema.errMsg(self.import_loc, "type '{}' is not available in ZON", .{ty.fmt(pt)}); | |
| 188 | errdefer msg.destroy(gpa); | |
| 189 | if (opt_note) |n| try sema.errNote(self.import_loc, msg, "{s}", .{n}); | |
| 190 | break :msg msg; | |
| 191 | }); | |
| 192 | } | |
| 193 | ||
| 194 | fn fail( | |
| 195 | self: *LowerZon, | |
| 196 | node: Zoir.Node.Index, | |
| 197 | comptime format: []const u8, | |
| 198 | args: anytype, | |
| 199 | ) error{ AnalysisFail, OutOfMemory } { | |
| 200 | @branchHint(.cold); | |
| 201 | const err_msg = try Zcu.ErrorMsg.create(self.sema.pt.zcu.gpa, self.nodeSrc(node), format, args); | |
| 202 | try self.sema.pt.zcu.errNote(self.import_loc, err_msg, "imported here", .{}); | |
| 203 | return self.sema.failWithOwnedErrorMsg(self.block, err_msg); | |
| 204 | } | |
| 205 | ||
| 206 | fn lowerExpr(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index { | |
| 207 | const pt = self.sema.pt; | |
| 208 | return self.lowerExprInner(node, res_ty) catch |err| switch (err) { | |
| 209 | error.WrongType => return self.fail( | |
| 210 | node, | |
| 211 | "expected type '{}'", | |
| 212 | .{res_ty.fmt(pt)}, | |
| 213 | ), | |
| 214 | else => |e| return e, | |
| 215 | }; | |
| 216 | } | |
| 217 | ||
| 218 | fn lowerExprInner( | |
| 219 | self: *LowerZon, | |
| 220 | node: Zoir.Node.Index, | |
| 221 | res_ty: Type, | |
| 222 | ) (CompileError || error{WrongType})!InternPool.Index { | |
| 223 | const pt = self.sema.pt; | |
| 224 | switch (res_ty.zigTypeTag(pt.zcu)) { | |
| 225 | .optional => return pt.intern(.{ | |
| 226 | .opt = .{ | |
| 227 | .ty = res_ty.toIntern(), | |
| 228 | .val = if (node.get(self.file.zoir.?) == .null) b: { | |
| 229 | break :b .none; | |
| 230 | } else b: { | |
| 231 | const child_type = res_ty.optionalChild(pt.zcu); | |
| 232 | break :b try self.lowerExprInner(node, child_type); | |
| 233 | }, | |
| 234 | }, | |
| 235 | }), | |
| 236 | .pointer => { | |
| 237 | const ptr_info = res_ty.ptrInfo(pt.zcu); | |
| 238 | switch (ptr_info.flags.size) { | |
| 239 | .one => return pt.intern(.{ .ptr = .{ | |
| 240 | .ty = res_ty.toIntern(), | |
| 241 | .base_addr = .{ | |
| 242 | .uav = .{ | |
| 243 | .orig_ty = res_ty.toIntern(), | |
| 244 | .val = try self.lowerExprInner(node, .fromInterned(ptr_info.child)), | |
| 245 | }, | |
| 246 | }, | |
| 247 | .byte_offset = 0, | |
| 248 | } }), | |
| 249 | .slice => return self.lowerSlice(node, res_ty), | |
| 250 | else => { | |
| 251 | // Unsupported pointer type, checked in `lower` | |
| 252 | unreachable; | |
| 253 | }, | |
| 254 | } | |
| 255 | }, | |
| 256 | .bool => return self.lowerBool(node), | |
| 257 | .int, .comptime_int => return self.lowerInt(node, res_ty), | |
| 258 | .float, .comptime_float => return self.lowerFloat(node, res_ty), | |
| 259 | .null => return self.lowerNull(node), | |
| 260 | .@"enum" => return self.lowerEnum(node, res_ty), | |
| 261 | .enum_literal => return self.lowerEnumLiteral(node), | |
| 262 | .array => return self.lowerArray(node, res_ty), | |
| 263 | .@"struct" => return self.lowerStructOrTuple(node, res_ty), | |
| 264 | .@"union" => return self.lowerUnion(node, res_ty), | |
| 265 | .vector => return self.lowerVector(node, res_ty), | |
| 266 | ||
| 267 | .type, | |
| 268 | .noreturn, | |
| 269 | .undefined, | |
| 270 | .error_union, | |
| 271 | .error_set, | |
| 272 | .@"fn", | |
| 273 | .@"opaque", | |
| 274 | .frame, | |
| 275 | .@"anyframe", | |
| 276 | .void, | |
| 277 | => return self.fail(node, "type '{}' not available in ZON", .{res_ty.fmt(pt)}), | |
| 278 | } | |
| 279 | } | |
| 280 | ||
| 281 | fn lowerBool(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index { | |
| 282 | return switch (node.get(self.file.zoir.?)) { | |
| 283 | .true => .bool_true, | |
| 284 | .false => .bool_false, | |
| 285 | else => return error.WrongType, | |
| 286 | }; | |
| 287 | } | |
| 288 | ||
| 289 | fn lowerInt( | |
| 290 | self: *LowerZon, | |
| 291 | node: Zoir.Node.Index, | |
| 292 | res_ty: Type, | |
| 293 | ) !InternPool.Index { | |
| 294 | @setFloatMode(.strict); | |
| 295 | return switch (node.get(self.file.zoir.?)) { | |
| 296 | .int_literal => |int| switch (int) { | |
| 297 | .small => |val| { | |
| 298 | const rhs: i32 = val; | |
| 299 | ||
| 300 | // If our result is a fixed size integer, check that our value is not out of bounds | |
| 301 | if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) { | |
| 302 | const lhs_info = res_ty.intInfo(self.sema.pt.zcu); | |
| 303 | ||
| 304 | // If lhs is unsigned and rhs is less than 0, we're out of bounds | |
| 305 | if (lhs_info.signedness == .unsigned and rhs < 0) return self.fail( | |
| 306 | node, | |
| 307 | "type '{}' cannot represent integer value '{}'", | |
| 308 | .{ res_ty.fmt(self.sema.pt), rhs }, | |
| 309 | ); | |
| 310 | ||
| 311 | // If lhs has less than the 32 bits rhs can hold, we need to check the max and | |
| 312 | // min values | |
| 313 | if (std.math.cast(u5, lhs_info.bits)) |bits| { | |
| 314 | const min_int: i32 = if (lhs_info.signedness == .unsigned or bits == 0) b: { | |
| 315 | break :b 0; | |
| 316 | } else b: { | |
| 317 | break :b -(@as(i32, 1) << (bits - 1)); | |
| 318 | }; | |
| 319 | const max_int: i32 = if (bits == 0) b: { | |
| 320 | break :b 0; | |
| 321 | } else b: { | |
| 322 | break :b (@as(i32, 1) << (bits - @intFromBool(lhs_info.signedness == .signed))) - 1; | |
| 323 | }; | |
| 324 | if (rhs < min_int or rhs > max_int) { | |
| 325 | return self.fail( | |
| 326 | node, | |
| 327 | "type '{}' cannot represent integer value '{}'", | |
| 328 | .{ res_ty.fmt(self.sema.pt), rhs }, | |
| 329 | ); | |
| 330 | } | |
| 331 | } | |
| 332 | } | |
| 333 | ||
| 334 | return self.sema.pt.intern(.{ .int = .{ | |
| 335 | .ty = res_ty.toIntern(), | |
| 336 | .storage = .{ .i64 = rhs }, | |
| 337 | } }); | |
| 338 | }, | |
| 339 | .big => |val| { | |
| 340 | if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) { | |
| 341 | const int_info = res_ty.intInfo(self.sema.pt.zcu); | |
| 342 | if (!val.fitsInTwosComp(int_info.signedness, int_info.bits)) { | |
| 343 | return self.fail( | |
| 344 | node, | |
| 345 | "type '{}' cannot represent integer value '{}'", | |
| 346 | .{ res_ty.fmt(self.sema.pt), val }, | |
| 347 | ); | |
| 348 | } | |
| 349 | } | |
| 350 | ||
| 351 | return self.sema.pt.intern(.{ .int = .{ | |
| 352 | .ty = res_ty.toIntern(), | |
| 353 | .storage = .{ .big_int = val }, | |
| 354 | } }); | |
| 355 | }, | |
| 356 | }, | |
| 357 | .float_literal => |val| { | |
| 358 | // Check for fractional components | |
| 359 | if (@rem(val, 1) != 0) { | |
| 360 | return self.fail( | |
| 361 | node, | |
| 362 | "fractional component prevents float value '{}' from coercion to type '{}'", | |
| 363 | .{ val, res_ty.fmt(self.sema.pt) }, | |
| 364 | ); | |
| 365 | } | |
| 366 | ||
| 367 | // Create a rational representation of the float | |
| 368 | var rational = try std.math.big.Rational.init(self.sema.arena); | |
| 369 | rational.setFloat(f128, val) catch |err| switch (err) { | |
| 370 | error.NonFiniteFloat => unreachable, | |
| 371 | error.OutOfMemory => return error.OutOfMemory, | |
| 372 | }; | |
| 373 | ||
| 374 | // The float is reduced in rational.setFloat, so we assert that denominator is equal to | |
| 375 | // one | |
| 376 | const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true }; | |
| 377 | assert(rational.q.toConst().eqlAbs(big_one)); | |
| 378 | ||
| 379 | // Check that the result is in range of the result type | |
| 380 | const int_info = res_ty.intInfo(self.sema.pt.zcu); | |
| 381 | if (!rational.p.fitsInTwosComp(int_info.signedness, int_info.bits)) { | |
| 382 | return self.fail( | |
| 383 | node, | |
| 384 | "type '{}' cannot represent integer value '{}'", | |
| 385 | .{ val, res_ty.fmt(self.sema.pt) }, | |
| 386 | ); | |
| 387 | } | |
| 388 | ||
| 389 | return self.sema.pt.intern(.{ | |
| 390 | .int = .{ | |
| 391 | .ty = res_ty.toIntern(), | |
| 392 | .storage = .{ .big_int = rational.p.toConst() }, | |
| 393 | }, | |
| 394 | }); | |
| 395 | }, | |
| 396 | .char_literal => |val| { | |
| 397 | // If our result is a fixed size integer, check that our value is not out of bounds | |
| 398 | if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) { | |
| 399 | const dest_info = res_ty.intInfo(self.sema.pt.zcu); | |
| 400 | const unsigned_bits = dest_info.bits - @intFromBool(dest_info.signedness == .signed); | |
| 401 | if (unsigned_bits < 21) { | |
| 402 | const out_of_range: u21 = @as(u21, 1) << @intCast(unsigned_bits); | |
| 403 | if (val >= out_of_range) { | |
| 404 | return self.fail( | |
| 405 | node, | |
| 406 | "type '{}' cannot represent integer value '{}'", | |
| 407 | .{ res_ty.fmt(self.sema.pt), val }, | |
| 408 | ); | |
| 409 | } | |
| 410 | } | |
| 411 | } | |
| 412 | return self.sema.pt.intern(.{ | |
| 413 | .int = .{ | |
| 414 | .ty = res_ty.toIntern(), | |
| 415 | .storage = .{ .i64 = val }, | |
| 416 | }, | |
| 417 | }); | |
| 418 | }, | |
| 419 | ||
| 420 | else => return error.WrongType, | |
| 421 | }; | |
| 422 | } | |
| 423 | ||
| 424 | fn lowerFloat( | |
| 425 | self: *LowerZon, | |
| 426 | node: Zoir.Node.Index, | |
| 427 | res_ty: Type, | |
| 428 | ) !InternPool.Index { | |
| 429 | @setFloatMode(.strict); | |
| 430 | const value = switch (node.get(self.file.zoir.?)) { | |
| 431 | .int_literal => |int| switch (int) { | |
| 432 | .small => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))), | |
| 433 | .big => |val| try self.sema.pt.floatValue(res_ty, val.toFloat(f128)), | |
| 434 | }, | |
| 435 | .float_literal => |val| try self.sema.pt.floatValue(res_ty, val), | |
| 436 | .char_literal => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))), | |
| 437 | .pos_inf => b: { | |
| 438 | if (res_ty.toIntern() == .comptime_float_type) return self.fail( | |
| 439 | node, | |
| 440 | "expected type '{}'", | |
| 441 | .{res_ty.fmt(self.sema.pt)}, | |
| 442 | ); | |
| 443 | break :b try self.sema.pt.floatValue(res_ty, std.math.inf(f128)); | |
| 444 | }, | |
| 445 | .neg_inf => b: { | |
| 446 | if (res_ty.toIntern() == .comptime_float_type) return self.fail( | |
| 447 | node, | |
| 448 | "expected type '{}'", | |
| 449 | .{res_ty.fmt(self.sema.pt)}, | |
| 450 | ); | |
| 451 | break :b try self.sema.pt.floatValue(res_ty, -std.math.inf(f128)); | |
| 452 | }, | |
| 453 | .nan => b: { | |
| 454 | if (res_ty.toIntern() == .comptime_float_type) return self.fail( | |
| 455 | node, | |
| 456 | "expected type '{}'", | |
| 457 | .{res_ty.fmt(self.sema.pt)}, | |
| 458 | ); | |
| 459 | break :b try self.sema.pt.floatValue(res_ty, std.math.nan(f128)); | |
| 460 | }, | |
| 461 | else => return error.WrongType, | |
| 462 | }; | |
| 463 | return value.toIntern(); | |
| 464 | } | |
| 465 | ||
| 466 | fn lowerNull(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index { | |
| 467 | switch (node.get(self.file.zoir.?)) { | |
| 468 | .null => return .null_value, | |
| 469 | else => return error.WrongType, | |
| 470 | } | |
| 471 | } | |
| 472 | ||
| 473 | fn lowerArray(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index { | |
| 474 | const array_info = res_ty.arrayInfo(self.sema.pt.zcu); | |
| 475 | const nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) { | |
| 476 | .array_literal => |nodes| nodes, | |
| 477 | .empty_literal => .{ .start = node, .len = 0 }, | |
| 478 | else => return error.WrongType, | |
| 479 | }; | |
| 480 | ||
| 481 | if (nodes.len != array_info.len) { | |
| 482 | return error.WrongType; | |
| 483 | } | |
| 484 | ||
| 485 | const elems = try self.sema.arena.alloc( | |
| 486 | InternPool.Index, | |
| 487 | nodes.len + @intFromBool(array_info.sentinel != null), | |
| 488 | ); | |
| 489 | ||
| 490 | for (0..nodes.len) |i| { | |
| 491 | elems[i] = try self.lowerExpr(nodes.at(@intCast(i)), array_info.elem_type); | |
| 492 | } | |
| 493 | ||
| 494 | if (array_info.sentinel) |sentinel| { | |
| 495 | elems[elems.len - 1] = sentinel.toIntern(); | |
| 496 | } | |
| 497 | ||
| 498 | return self.sema.pt.intern(.{ .aggregate = .{ | |
| 499 | .ty = res_ty.toIntern(), | |
| 500 | .storage = .{ .elems = elems }, | |
| 501 | } }); | |
| 502 | } | |
| 503 | ||
| 504 | fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index { | |
| 505 | const ip = &self.sema.pt.zcu.intern_pool; | |
| 506 | switch (node.get(self.file.zoir.?)) { | |
| 507 | .enum_literal => |field_name| { | |
| 508 | const field_name_interned = try ip.getOrPutString( | |
| 509 | self.sema.gpa, | |
| 510 | self.sema.pt.tid, | |
| 511 | field_name.get(self.file.zoir.?), | |
| 512 | .no_embedded_nulls, | |
| 513 | ); | |
| 514 | const field_index = res_ty.enumFieldIndex(field_name_interned, self.sema.pt.zcu) orelse { | |
| 515 | return self.fail( | |
| 516 | node, | |
| 517 | "enum {} has no member named '{}'", | |
| 518 | .{ | |
| 519 | res_ty.fmt(self.sema.pt), | |
| 520 | std.zig.fmtId(field_name.get(self.file.zoir.?)), | |
| 521 | }, | |
| 522 | ); | |
| 523 | }; | |
| 524 | ||
| 525 | const value = try self.sema.pt.enumValueFieldIndex(res_ty, field_index); | |
| 526 | ||
| 527 | return value.toIntern(); | |
| 528 | }, | |
| 529 | else => return error.WrongType, | |
| 530 | } | |
| 531 | } | |
| 532 | ||
| 533 | fn lowerEnumLiteral(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index { | |
| 534 | const ip = &self.sema.pt.zcu.intern_pool; | |
| 535 | switch (node.get(self.file.zoir.?)) { | |
| 536 | .enum_literal => |field_name| { | |
| 537 | const field_name_interned = try ip.getOrPutString( | |
| 538 | self.sema.gpa, | |
| 539 | self.sema.pt.tid, | |
| 540 | field_name.get(self.file.zoir.?), | |
| 541 | .no_embedded_nulls, | |
| 542 | ); | |
| 543 | return self.sema.pt.intern(.{ .enum_literal = field_name_interned }); | |
| 544 | }, | |
| 545 | else => return error.WrongType, | |
| 546 | } | |
| 547 | } | |
| 548 | ||
| 549 | fn lowerStructOrTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index { | |
| 550 | const ip = &self.sema.pt.zcu.intern_pool; | |
| 551 | return switch (ip.indexToKey(res_ty.toIntern())) { | |
| 552 | .tuple_type => self.lowerTuple(node, res_ty), | |
| 553 | .struct_type => self.lowerStruct(node, res_ty), | |
| 554 | else => unreachable, | |
| 555 | }; | |
| 556 | } | |
| 557 | ||
| 558 | fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index { | |
| 559 | const ip = &self.sema.pt.zcu.intern_pool; | |
| 560 | ||
| 561 | const tuple_info = ip.indexToKey(res_ty.toIntern()).tuple_type; | |
| 562 | ||
| 563 | const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) { | |
| 564 | .array_literal => |nodes| nodes, | |
| 565 | .empty_literal => .{ .start = node, .len = 0 }, | |
| 566 | else => return error.WrongType, | |
| 567 | }; | |
| 568 | ||
| 569 | const field_types = tuple_info.types.get(ip); | |
| 570 | const elems = try self.sema.arena.alloc(InternPool.Index, field_types.len); | |
| 571 | ||
| 572 | const field_comptime_vals = tuple_info.values.get(ip); | |
| 573 | if (field_comptime_vals.len > 0) { | |
| 574 | @memcpy(elems, field_comptime_vals); | |
| 575 | } else { | |
| 576 | @memset(elems, .none); | |
| 577 | } | |
| 578 | ||
| 579 | for (0..elem_nodes.len) |i| { | |
| 580 | if (i >= elems.len) { | |
| 581 | const elem_node = elem_nodes.at(@intCast(i)); | |
| 582 | return self.fail( | |
| 583 | elem_node, | |
| 584 | "index {} outside tuple of length {}", | |
| 585 | .{ | |
| 586 | elems.len, | |
| 587 | elem_nodes.at(@intCast(i)).getAstNode(self.file.zoir.?), | |
| 588 | }, | |
| 589 | ); | |
| 590 | } | |
| 591 | ||
| 592 | const val = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(field_types[i])); | |
| 593 | ||
| 594 | if (elems[i] != .none and val != elems[i]) { | |
| 595 | const elem_node = elem_nodes.at(@intCast(i)); | |
| 596 | return self.fail( | |
| 597 | elem_node, | |
| 598 | "value stored in comptime field does not match the default value of the field", | |
| 599 | .{}, | |
| 600 | ); | |
| 601 | } | |
| 602 | ||
| 603 | elems[i] = val; | |
| 604 | } | |
| 605 | ||
| 606 | for (elems, 0..) |val, i| { | |
| 607 | if (val == .none) { | |
| 608 | return self.fail(node, "missing tuple field with index {}", .{i}); | |
| 609 | } | |
| 610 | } | |
| 611 | ||
| 612 | return self.sema.pt.intern(.{ .aggregate = .{ | |
| 613 | .ty = res_ty.toIntern(), | |
| 614 | .storage = .{ .elems = elems }, | |
| 615 | } }); | |
| 616 | } | |
| 617 | ||
| 618 | fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index { | |
| 619 | const ip = &self.sema.pt.zcu.intern_pool; | |
| 620 | const gpa = self.sema.gpa; | |
| 621 | ||
| 622 | try res_ty.resolveFields(self.sema.pt); | |
| 623 | try res_ty.resolveStructFieldInits(self.sema.pt); | |
| 624 | const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?; | |
| 625 | ||
| 626 | const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) { | |
| 627 | .struct_literal => |fields| fields, | |
| 628 | .empty_literal => .{ .names = &.{}, .vals = .{ .start = node, .len = 0 } }, | |
| 629 | else => return error.WrongType, | |
| 630 | }; | |
| 631 | ||
| 632 | const field_values = try self.sema.arena.alloc(InternPool.Index, struct_info.field_names.len); | |
| 633 | ||
| 634 | const field_defaults = struct_info.field_inits.get(ip); | |
| 635 | if (field_defaults.len > 0) { | |
| 636 | @memcpy(field_values, field_defaults); | |
| 637 | } else { | |
| 638 | @memset(field_values, .none); | |
| 639 | } | |
| 640 | ||
| 641 | for (0..fields.names.len) |i| { | |
| 642 | const field_name = try ip.getOrPutString( | |
| 643 | gpa, | |
| 644 | self.sema.pt.tid, | |
| 645 | fields.names[i].get(self.file.zoir.?), | |
| 646 | .no_embedded_nulls, | |
| 647 | ); | |
| 648 | const field_node = fields.vals.at(@intCast(i)); | |
| 649 | ||
| 650 | const name_index = struct_info.nameIndex(ip, field_name) orelse { | |
| 651 | return self.fail(field_node, "unexpected field '{}'", .{field_name.fmt(ip)}); | |
| 652 | }; | |
| 653 | ||
| 654 | const field_type: Type = .fromInterned(struct_info.field_types.get(ip)[name_index]); | |
| 655 | field_values[name_index] = try self.lowerExpr(field_node, field_type); | |
| 656 | ||
| 657 | if (struct_info.comptime_bits.getBit(ip, name_index)) { | |
| 658 | const val = ip.indexToKey(field_values[name_index]); | |
| 659 | const default = ip.indexToKey(field_defaults[name_index]); | |
| 660 | if (!val.eql(default, ip)) { | |
| 661 | return self.fail( | |
| 662 | field_node, | |
| 663 | "value stored in comptime field does not match the default value of the field", | |
| 664 | .{}, | |
| 665 | ); | |
| 666 | } | |
| 667 | } | |
| 668 | } | |
| 669 | ||
| 670 | const field_names = struct_info.field_names.get(ip); | |
| 671 | for (field_values, field_names) |*value, name| { | |
| 672 | if (value.* == .none) return self.fail(node, "missing field '{}'", .{name.fmt(ip)}); | |
| 673 | } | |
| 674 | ||
| 675 | return self.sema.pt.intern(.{ .aggregate = .{ | |
| 676 | .ty = res_ty.toIntern(), | |
| 677 | .storage = .{ | |
| 678 | .elems = field_values, | |
| 679 | }, | |
| 680 | } }); | |
| 681 | } | |
| 682 | ||
| 683 | fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index { | |
| 684 | const ip = &self.sema.pt.zcu.intern_pool; | |
| 685 | const gpa = self.sema.gpa; | |
| 686 | ||
| 687 | const ptr_info = res_ty.ptrInfo(self.sema.pt.zcu); | |
| 688 | ||
| 689 | assert(ptr_info.flags.size == .slice); | |
| 690 | ||
| 691 | // String literals | |
| 692 | const string_alignment = ptr_info.flags.alignment == .none or ptr_info.flags.alignment == .@"1"; | |
| 693 | const string_sentinel = ptr_info.sentinel == .none or ptr_info.sentinel == .zero_u8; | |
| 694 | if (string_alignment and ptr_info.child == .u8_type and string_sentinel) { | |
| 695 | switch (node.get(self.file.zoir.?)) { | |
| 696 | .string_literal => |val| { | |
| 697 | const ip_str = try ip.getOrPutString(gpa, self.sema.pt.tid, val, .maybe_embedded_nulls); | |
| 698 | const str_ref = try self.sema.addStrLit(ip_str, val.len); | |
| 699 | return (try self.sema.coerce( | |
| 700 | self.block, | |
| 701 | res_ty, | |
| 702 | str_ref, | |
| 703 | self.nodeSrc(node), | |
| 704 | )).toInterned().?; | |
| 705 | }, | |
| 706 | else => {}, | |
| 707 | } | |
| 708 | } | |
| 709 | ||
| 710 | // Slice literals | |
| 711 | const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) { | |
| 712 | .array_literal => |nodes| nodes, | |
| 713 | .empty_literal => .{ .start = node, .len = 0 }, | |
| 714 | else => return error.WrongType, | |
| 715 | }; | |
| 716 | ||
| 717 | const elems = try self.sema.arena.alloc(InternPool.Index, elem_nodes.len + @intFromBool(ptr_info.sentinel != .none)); | |
| 718 | ||
| 719 | for (elems, 0..) |*elem, i| { | |
| 720 | elem.* = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(ptr_info.child)); | |
| 721 | } | |
| 722 | ||
| 723 | if (ptr_info.sentinel != .none) { | |
| 724 | elems[elems.len - 1] = ptr_info.sentinel; | |
| 725 | } | |
| 726 | ||
| 727 | const array_ty = try self.sema.pt.intern(.{ .array_type = .{ | |
| 728 | .len = elems.len, | |
| 729 | .sentinel = ptr_info.sentinel, | |
| 730 | .child = ptr_info.child, | |
| 731 | } }); | |
| 732 | ||
| 733 | const array = try self.sema.pt.intern(.{ .aggregate = .{ | |
| 734 | .ty = array_ty, | |
| 735 | .storage = .{ .elems = elems }, | |
| 736 | } }); | |
| 737 | ||
| 738 | const many_item_ptr_type = try self.sema.pt.intern(.{ .ptr_type = .{ | |
| 739 | .child = ptr_info.child, | |
| 740 | .sentinel = ptr_info.sentinel, | |
| 741 | .flags = b: { | |
| 742 | var flags = ptr_info.flags; | |
| 743 | flags.size = .many; | |
| 744 | break :b flags; | |
| 745 | }, | |
| 746 | .packed_offset = ptr_info.packed_offset, | |
| 747 | } }); | |
| 748 | ||
| 749 | const many_item_ptr = try self.sema.pt.intern(.{ | |
| 750 | .ptr = .{ | |
| 751 | .ty = many_item_ptr_type, | |
| 752 | .base_addr = .{ | |
| 753 | .uav = .{ | |
| 754 | .orig_ty = (try self.sema.pt.singleConstPtrType(.fromInterned(array_ty))).toIntern(), | |
| 755 | .val = array, | |
| 756 | }, | |
| 757 | }, | |
| 758 | .byte_offset = 0, | |
| 759 | }, | |
| 760 | }); | |
| 761 | ||
| 762 | const len = (try self.sema.pt.intValue(.usize, elems.len)).toIntern(); | |
| 763 | ||
| 764 | return self.sema.pt.intern(.{ .slice = .{ | |
| 765 | .ty = res_ty.toIntern(), | |
| 766 | .ptr = many_item_ptr, | |
| 767 | .len = len, | |
| 768 | } }); | |
| 769 | } | |
| 770 | ||
| 771 | fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index { | |
| 772 | const ip = &self.sema.pt.zcu.intern_pool; | |
| 773 | try res_ty.resolveFields(self.sema.pt); | |
| 774 | const union_info = self.sema.pt.zcu.typeToUnion(res_ty).?; | |
| 775 | const enum_tag_info = union_info.loadTagType(ip); | |
| 776 | ||
| 777 | const field_name, const maybe_field_node = switch (node.get(self.file.zoir.?)) { | |
| 778 | .enum_literal => |name| b: { | |
| 779 | const field_name = try ip.getOrPutString( | |
| 780 | self.sema.gpa, | |
| 781 | self.sema.pt.tid, | |
| 782 | name.get(self.file.zoir.?), | |
| 783 | .no_embedded_nulls, | |
| 784 | ); | |
| 785 | break :b .{ field_name, null }; | |
| 786 | }, | |
| 787 | .struct_literal => b: { | |
| 788 | const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) { | |
| 789 | .struct_literal => |fields| fields, | |
| 790 | else => return self.fail(node, "expected type '{}'", .{res_ty.fmt(self.sema.pt)}), | |
| 791 | }; | |
| 792 | if (fields.names.len != 1) { | |
| 793 | return error.WrongType; | |
| 794 | } | |
| 795 | const field_name = try ip.getOrPutString( | |
| 796 | self.sema.gpa, | |
| 797 | self.sema.pt.tid, | |
| 798 | fields.names[0].get(self.file.zoir.?), | |
| 799 | .no_embedded_nulls, | |
| 800 | ); | |
| 801 | break :b .{ field_name, fields.vals.at(0) }; | |
| 802 | }, | |
| 803 | else => return error.WrongType, | |
| 804 | }; | |
| 805 | ||
| 806 | const name_index = enum_tag_info.nameIndex(ip, field_name) orelse { | |
| 807 | return error.WrongType; | |
| 808 | }; | |
| 809 | const tag = try self.sema.pt.enumValueFieldIndex(.fromInterned(union_info.enum_tag_ty), name_index); | |
| 810 | const field_type: Type = .fromInterned(union_info.field_types.get(ip)[name_index]); | |
| 811 | const val = if (maybe_field_node) |field_node| b: { | |
| 812 | if (field_type.toIntern() == .void_type) { | |
| 813 | return self.fail(field_node, "expected type 'void'", .{}); | |
| 814 | } | |
| 815 | break :b try self.lowerExpr(field_node, field_type); | |
| 816 | } else b: { | |
| 817 | if (field_type.toIntern() != .void_type) { | |
| 818 | return error.WrongType; | |
| 819 | } | |
| 820 | break :b .void_value; | |
| 821 | }; | |
| 822 | return ip.getUnion(self.sema.pt.zcu.gpa, self.sema.pt.tid, .{ | |
| 823 | .ty = res_ty.toIntern(), | |
| 824 | .tag = tag.toIntern(), | |
| 825 | .val = val, | |
| 826 | }); | |
| 827 | } | |
| 828 | ||
| 829 | fn lowerVector(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index { | |
| 830 | const ip = &self.sema.pt.zcu.intern_pool; | |
| 831 | ||
| 832 | const vector_info = ip.indexToKey(res_ty.toIntern()).vector_type; | |
| 833 | ||
| 834 | const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) { | |
| 835 | .array_literal => |nodes| nodes, | |
| 836 | .empty_literal => .{ .start = node, .len = 0 }, | |
| 837 | else => return error.WrongType, | |
| 838 | }; | |
| 839 | ||
| 840 | const elems = try self.sema.arena.alloc(InternPool.Index, vector_info.len); | |
| 841 | ||
| 842 | if (elem_nodes.len != vector_info.len) { | |
| 843 | return self.fail( | |
| 844 | node, | |
| 845 | "expected {} vector elements; found {}", | |
| 846 | .{ vector_info.len, elem_nodes.len }, | |
| 847 | ); | |
| 848 | } | |
| 849 | ||
| 850 | for (elems, 0..) |*elem, i| { | |
| 851 | elem.* = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(vector_info.child)); | |
| 852 | } | |
| 853 | ||
| 854 | return self.sema.pt.intern(.{ .aggregate = .{ | |
| 855 | .ty = res_ty.toIntern(), | |
| 856 | .storage = .{ .elems = elems }, | |
| 857 | } }); | |
| 858 | } |
src/Value.zig+5-24| ... | ... | @@ -270,7 +270,7 @@ pub fn getUnsignedIntInner( |
| 270 | 270 | else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { |
| 271 | 271 | .undef => unreachable, |
| 272 | 272 | .int => |int| switch (int.storage) { |
| 273 | .big_int => |big_int| big_int.to(u64) catch null, | |
| 273 | .big_int => |big_int| big_int.toInt(u64) catch null, | |
| 274 | 274 | .u64 => |x| x, |
| 275 | 275 | .i64 => |x| std.math.cast(u64, x), |
| 276 | 276 | .lazy_align => |ty| (try Type.fromInterned(ty).abiAlignmentInner(strat.toLazy(), zcu, tid)).scalar.toByteUnits() orelse 0, |
| ... | ... | @@ -311,7 +311,7 @@ pub fn toSignedInt(val: Value, zcu: *const Zcu) i64 { |
| 311 | 311 | .bool_true => 1, |
| 312 | 312 | else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { |
| 313 | 313 | .int => |int| switch (int.storage) { |
| 314 | .big_int => |big_int| big_int.to(i64) catch unreachable, | |
| 314 | .big_int => |big_int| big_int.toInt(i64) catch unreachable, | |
| 315 | 315 | .i64 => |x| x, |
| 316 | 316 | .u64 => |x| @intCast(x), |
| 317 | 317 | .lazy_align => |ty| @intCast(Type.fromInterned(ty).abiAlignment(zcu).toByteUnits() orelse 0), |
| ... | ... | @@ -898,7 +898,7 @@ pub fn readFromPackedMemory( |
| 898 | 898 | pub fn toFloat(val: Value, comptime T: type, zcu: *Zcu) T { |
| 899 | 899 | return switch (zcu.intern_pool.indexToKey(val.toIntern())) { |
| 900 | 900 | .int => |int| switch (int.storage) { |
| 901 | .big_int => |big_int| @floatCast(bigIntToFloat(big_int.limbs, big_int.positive)), | |
| 901 | .big_int => |big_int| big_int.toFloat(T), | |
| 902 | 902 | inline .u64, .i64 => |x| { |
| 903 | 903 | if (T == f80) { |
| 904 | 904 | @panic("TODO we can't lower this properly on non-x86 llvm backend yet"); |
| ... | ... | @@ -915,25 +915,6 @@ pub fn toFloat(val: Value, comptime T: type, zcu: *Zcu) T { |
| 915 | 915 | }; |
| 916 | 916 | } |
| 917 | 917 | |
| 918 | /// TODO move this to std lib big int code | |
| 919 | fn bigIntToFloat(limbs: []const std.math.big.Limb, positive: bool) f128 { | |
| 920 | if (limbs.len == 0) return 0; | |
| 921 | ||
| 922 | const base = std.math.maxInt(std.math.big.Limb) + 1; | |
| 923 | var result: f128 = 0; | |
| 924 | var i: usize = limbs.len; | |
| 925 | while (i != 0) { | |
| 926 | i -= 1; | |
| 927 | const limb: f128 = @floatFromInt(limbs[i]); | |
| 928 | result = @mulAdd(f128, base, result, limb); | |
| 929 | } | |
| 930 | if (positive) { | |
| 931 | return result; | |
| 932 | } else { | |
| 933 | return -result; | |
| 934 | } | |
| 935 | } | |
| 936 | ||
| 937 | 918 | pub fn clz(val: Value, ty: Type, zcu: *Zcu) u64 { |
| 938 | 919 | var bigint_buf: BigIntSpace = undefined; |
| 939 | 920 | const bigint = val.toBigInt(&bigint_buf, zcu); |
| ... | ... | @@ -1548,7 +1529,7 @@ pub fn floatFromIntScalar(val: Value, float_ty: Type, pt: Zcu.PerThread, comptim |
| 1548 | 1529 | .undef => try pt.undefValue(float_ty), |
| 1549 | 1530 | .int => |int| switch (int.storage) { |
| 1550 | 1531 | .big_int => |big_int| { |
| 1551 | const float = bigIntToFloat(big_int.limbs, big_int.positive); | |
| 1532 | const float = big_int.toFloat(f128); | |
| 1552 | 1533 | return pt.floatValue(float_ty, float); |
| 1553 | 1534 | }, |
| 1554 | 1535 | inline .u64, .i64 => |x| floatFromIntInner(x, float_ty, pt), |
| ... | ... | @@ -4583,7 +4564,7 @@ pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMe |
| 4583 | 4564 | .int => switch (ip.indexToKey(val.toIntern()).int.storage) { |
| 4584 | 4565 | .lazy_align, .lazy_size => unreachable, // `val` is fully resolved |
| 4585 | 4566 | inline .u64, .i64 => |x| std.math.cast(T, x) orelse return error.TypeMismatch, |
| 4586 | .big_int => |big| big.to(T) catch return error.TypeMismatch, | |
| 4567 | .big_int => |big| big.toInt(T) catch return error.TypeMismatch, | |
| 4587 | 4568 | }, |
| 4588 | 4569 | |
| 4589 | 4570 | .float => val.toFloat(T, zcu), |
src/Zcu.zig+37-3| ... | ... | @@ -39,6 +39,8 @@ const AnalUnit = InternPool.AnalUnit; |
| 39 | 39 | const BuiltinFn = std.zig.BuiltinFn; |
| 40 | 40 | const LlvmObject = @import("codegen/llvm.zig").Object; |
| 41 | 41 | const dev = @import("dev.zig"); |
| 42 | const Zoir = std.zig.Zoir; | |
| 43 | const ZonGen = std.zig.ZonGen; | |
| 42 | 44 | |
| 43 | 45 | comptime { |
| 44 | 46 | @setEvalBranchQuota(4000); |
| ... | ... | @@ -672,6 +674,8 @@ pub const File = struct { |
| 672 | 674 | tree: Ast, |
| 673 | 675 | /// Whether this is populated or not depends on `zir_loaded`. |
| 674 | 676 | zir: Zir, |
| 677 | /// Cached Zoir, generated lazily. | |
| 678 | zoir: ?Zoir = null, | |
| 675 | 679 | /// Module that this file is a part of, managed externally. |
| 676 | 680 | mod: *Package.Module, |
| 677 | 681 | /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen. |
| ... | ... | @@ -704,7 +708,19 @@ pub const File = struct { |
| 704 | 708 | root: *Package.Module, |
| 705 | 709 | }; |
| 706 | 710 | |
| 711 | pub fn getMode(self: File) Ast.Mode { | |
| 712 | if (std.mem.endsWith(u8, self.sub_file_path, ".zon")) { | |
| 713 | return .zon; | |
| 714 | } else if (std.mem.endsWith(u8, self.sub_file_path, ".zig")) { | |
| 715 | return .zig; | |
| 716 | } else { | |
| 717 | // `Module.importFile` rejects all other extensions | |
| 718 | unreachable; | |
| 719 | } | |
| 720 | } | |
| 721 | ||
| 707 | 722 | pub fn unload(file: *File, gpa: Allocator) void { |
| 723 | if (file.zoir) |zoir| zoir.deinit(gpa); | |
| 708 | 724 | file.unloadTree(gpa); |
| 709 | 725 | file.unloadSource(gpa); |
| 710 | 726 | file.unloadZir(gpa); |
| ... | ... | @@ -778,11 +794,24 @@ pub const File = struct { |
| 778 | 794 | if (file.tree_loaded) return &file.tree; |
| 779 | 795 | |
| 780 | 796 | const source = try file.getSource(gpa); |
| 781 | file.tree = try Ast.parse(gpa, source.bytes, .zig); | |
| 797 | file.tree = try Ast.parse(gpa, source.bytes, file.getMode()); | |
| 782 | 798 | file.tree_loaded = true; |
| 783 | 799 | return &file.tree; |
| 784 | 800 | } |
| 785 | 801 | |
| 802 | pub fn getZoir(file: *File, zcu: *Zcu) !*const Zoir { | |
| 803 | if (file.zoir) |*zoir| return zoir; | |
| 804 | ||
| 805 | assert(file.tree_loaded); | |
| 806 | assert(file.tree.mode == .zon); | |
| 807 | file.zoir = try ZonGen.generate(zcu.gpa, file.tree, .{}); | |
| 808 | if (file.zoir.?.hasCompileErrors()) { | |
| 809 | try zcu.failed_files.putNoClobber(zcu.gpa, file, null); | |
| 810 | return error.AnalysisFail; | |
| 811 | } | |
| 812 | return &file.zoir.?; | |
| 813 | } | |
| 814 | ||
| 786 | 815 | pub fn fullyQualifiedNameLen(file: File) usize { |
| 787 | 816 | const ext = std.fs.path.extension(file.sub_file_path); |
| 788 | 817 | return file.sub_file_path.len - ext.len; |
| ... | ... | @@ -895,6 +924,7 @@ pub const File = struct { |
| 895 | 924 | pub const Index = InternPool.FileIndex; |
| 896 | 925 | }; |
| 897 | 926 | |
| 927 | /// Represents the contents of a file loaded with `@embedFile`. | |
| 898 | 928 | pub const EmbedFile = struct { |
| 899 | 929 | /// Module that this file is a part of, managed externally. |
| 900 | 930 | owner: *Package.Module, |
| ... | ... | @@ -2372,6 +2402,12 @@ pub const LazySrcLoc = struct { |
| 2372 | 2402 | break :inst .{ info.file, info.inst }; |
| 2373 | 2403 | }; |
| 2374 | 2404 | const file = zcu.fileByIndex(file_index); |
| 2405 | ||
| 2406 | // If we're relative to .main_struct_inst, we know the ast node is the root and don't need to resolve the ZIR, | |
| 2407 | // which may not exist e.g. in the case of errors in ZON files. | |
| 2408 | if (zir_inst == .main_struct_inst) return .{ file, 0 }; | |
| 2409 | ||
| 2410 | // Otherwise, make sure ZIR is loaded. | |
| 2375 | 2411 | assert(file.zir_loaded); |
| 2376 | 2412 | |
| 2377 | 2413 | const zir = file.zir; |
| ... | ... | @@ -3461,8 +3497,6 @@ pub fn atomicPtrAlignment( |
| 3461 | 3497 | } |
| 3462 | 3498 | |
| 3463 | 3499 | /// Returns null in the following cases: |
| 3464 | /// * `@TypeOf(.{})` | |
| 3465 | /// * A struct which has no fields (`struct {}`). | |
| 3466 | 3500 | /// * Not a struct. |
| 3467 | 3501 | pub fn typeToStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType { |
| 3468 | 3502 | if (ty.ip_index == .none) return null; |
src/Zcu/PerThread.zig+4-1| ... | ... | @@ -1867,6 +1867,7 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { |
| 1867 | 1867 | const zcu = pt.zcu; |
| 1868 | 1868 | const gpa = zcu.gpa; |
| 1869 | 1869 | const file = zcu.fileByIndex(file_index); |
| 1870 | assert(file.getMode() == .zig); | |
| 1870 | 1871 | assert(zcu.fileRootType(file_index) == .none); |
| 1871 | 1872 | |
| 1872 | 1873 | if (file.status != .success_zir) { |
| ... | ... | @@ -2022,7 +2023,9 @@ pub fn importFile( |
| 2022 | 2023 | if (mod.deps.get(import_string)) |pkg| { |
| 2023 | 2024 | return pt.importPkg(pkg); |
| 2024 | 2025 | } |
| 2025 | if (!std.mem.endsWith(u8, import_string, ".zig")) { | |
| 2026 | if (!std.mem.endsWith(u8, import_string, ".zig") and | |
| 2027 | !std.mem.endsWith(u8, import_string, ".zon")) | |
| 2028 | { | |
| 2026 | 2029 | return error.ModuleNotFound; |
| 2027 | 2030 | } |
| 2028 | 2031 | const gpa = zcu.gpa; |
src/codegen/llvm/Builder.zig+4-4| ... | ... | @@ -13776,8 +13776,8 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co |
| 13776 | 13776 | }; |
| 13777 | 13777 | const bit_count = extra.type.scalarBits(self); |
| 13778 | 13778 | const val: i64 = if (bit_count <= 64) |
| 13779 | bigint.to(i64) catch unreachable | |
| 13780 | else if (bigint.to(u64)) |val| | |
| 13779 | bigint.toInt(i64) catch unreachable | |
| 13780 | else if (bigint.toInt(u64)) |val| | |
| 13781 | 13781 | @bitCast(val) |
| 13782 | 13782 | else |_| { |
| 13783 | 13783 | const limbs = try record.addManyAsSlice( |
| ... | ... | @@ -14276,9 +14276,9 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co |
| 14276 | 14276 | else => unreachable, |
| 14277 | 14277 | }, |
| 14278 | 14278 | }; |
| 14279 | const val: i64 = if (bigint.to(i64)) |val| | |
| 14279 | const val: i64 = if (bigint.toInt(i64)) |val| | |
| 14280 | 14280 | val |
| 14281 | else |_| if (bigint.to(u64)) |val| | |
| 14281 | else |_| if (bigint.toInt(u64)) |val| | |
| 14282 | 14282 | @bitCast(val) |
| 14283 | 14283 | else |_| { |
| 14284 | 14284 | const limbs_len = std.math.divCeil(u32, extra.bit_width, 64) catch unreachable; |
src/fmt.zig+2-2| ... | ... | @@ -120,7 +120,7 @@ pub fn run( |
| 120 | 120 | process.exit(2); |
| 121 | 121 | } |
| 122 | 122 | } else { |
| 123 | const zoir = try std.zig.ZonGen.generate(gpa, tree); | |
| 123 | const zoir = try std.zig.ZonGen.generate(gpa, tree, .{}); | |
| 124 | 124 | defer zoir.deinit(gpa); |
| 125 | 125 | |
| 126 | 126 | if (zoir.hasCompileErrors()) { |
| ... | ... | @@ -335,7 +335,7 @@ fn fmtPathFile( |
| 335 | 335 | } |
| 336 | 336 | }, |
| 337 | 337 | .zon => { |
| 338 | var zoir = try std.zig.ZonGen.generate(gpa, tree); | |
| 338 | var zoir = try std.zig.ZonGen.generate(gpa, tree, .{}); | |
| 339 | 339 | defer zoir.deinit(gpa); |
| 340 | 340 | |
| 341 | 341 | if (zoir.hasCompileErrors()) { |
src/main.zig+1-1| ... | ... | @@ -6278,7 +6278,7 @@ fn cmdAstCheck( |
| 6278 | 6278 | } |
| 6279 | 6279 | }, |
| 6280 | 6280 | .zon => { |
| 6281 | const zoir = try ZonGen.generate(gpa, file.tree); | |
| 6281 | const zoir = try ZonGen.generate(gpa, file.tree, .{}); | |
| 6282 | 6282 | defer zoir.deinit(gpa); |
| 6283 | 6283 | |
| 6284 | 6284 | if (zoir.hasCompileErrors()) { |
src/print_zir.zig+11-1| ... | ... | @@ -488,7 +488,6 @@ const Writer = struct { |
| 488 | 488 | .enum_literal, |
| 489 | 489 | .decl_ref, |
| 490 | 490 | .decl_val, |
| 491 | .import, | |
| 492 | 491 | .ret_err_value, |
| 493 | 492 | .ret_err_value_code, |
| 494 | 493 | .param_anytype, |
| ... | ... | @@ -515,6 +514,8 @@ const Writer = struct { |
| 515 | 514 | .declaration => try self.writeDeclaration(stream, inst), |
| 516 | 515 | |
| 517 | 516 | .extended => try self.writeExtended(stream, inst), |
| 517 | ||
| 518 | .import => try self.writeImport(stream, inst), | |
| 518 | 519 | } |
| 519 | 520 | } |
| 520 | 521 | |
| ... | ... | @@ -2842,4 +2843,13 @@ const Writer = struct { |
| 2842 | 2843 | try stream.writeByte('\n'); |
| 2843 | 2844 | } |
| 2844 | 2845 | } |
| 2846 | ||
| 2847 | fn writeImport(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | |
| 2848 | const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok; | |
| 2849 | const extra = self.code.extraData(Zir.Inst.Import, inst_data.payload_index).data; | |
| 2850 | try self.writeInstRef(stream, extra.res_ty); | |
| 2851 | const import_path = self.code.nullTerminatedString(extra.path); | |
| 2852 | try stream.print(", \"{}\") ", .{std.zig.fmtEscapes(import_path)}); | |
| 2853 | try self.writeSrcTok(stream, inst_data.src_tok); | |
| 2854 | } | |
| 2845 | 2855 | }; |
test/behavior/zon.zig created+519| ... | ... | @@ -0,0 +1,519 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | const expect = std.testing.expect; | |
| 4 | const expectEqual = std.testing.expectEqual; | |
| 5 | const expectEqualDeep = std.testing.expectEqualDeep; | |
| 6 | const expectEqualSlices = std.testing.expectEqualSlices; | |
| 7 | const expectEqualStrings = std.testing.expectEqualStrings; | |
| 8 | ||
| 9 | test "bool" { | |
| 10 | try expectEqual(true, @as(bool, @import("zon/true.zon"))); | |
| 11 | try expectEqual(false, @as(bool, @import("zon/false.zon"))); | |
| 12 | } | |
| 13 | ||
| 14 | test "optional" { | |
| 15 | const some: ?u32 = @import("zon/some.zon"); | |
| 16 | const none: ?u32 = @import("zon/none.zon"); | |
| 17 | const @"null": @TypeOf(null) = @import("zon/none.zon"); | |
| 18 | try expectEqual(@as(u32, 10), some); | |
| 19 | try expectEqual(@as(?u32, null), none); | |
| 20 | try expectEqual(null, @"null"); | |
| 21 | } | |
| 22 | ||
| 23 | test "union" { | |
| 24 | // No tag | |
| 25 | { | |
| 26 | const Union = union { | |
| 27 | x: f32, | |
| 28 | y: bool, | |
| 29 | z: void, | |
| 30 | }; | |
| 31 | ||
| 32 | const union1: Union = @import("zon/union1.zon"); | |
| 33 | const union2: Union = @import("zon/union2.zon"); | |
| 34 | const union3: Union = @import("zon/union3.zon"); | |
| 35 | ||
| 36 | try expectEqual(1.5, union1.x); | |
| 37 | try expectEqual(true, union2.y); | |
| 38 | try expectEqual({}, union3.z); | |
| 39 | } | |
| 40 | ||
| 41 | // Inferred tag | |
| 42 | { | |
| 43 | const Union = union(enum) { | |
| 44 | x: f32, | |
| 45 | y: bool, | |
| 46 | z: void, | |
| 47 | }; | |
| 48 | ||
| 49 | const union1: Union = comptime @import("zon/union1.zon"); | |
| 50 | const union2: Union = @import("zon/union2.zon"); | |
| 51 | const union3: Union = @import("zon/union3.zon"); | |
| 52 | ||
| 53 | try expectEqual(1.5, union1.x); | |
| 54 | try expectEqual(true, union2.y); | |
| 55 | try expectEqual({}, union3.z); | |
| 56 | } | |
| 57 | ||
| 58 | // Explicit tag | |
| 59 | { | |
| 60 | const Tag = enum(i128) { | |
| 61 | x = -1, | |
| 62 | y = 2, | |
| 63 | z = 1, | |
| 64 | }; | |
| 65 | const Union = union(Tag) { | |
| 66 | x: f32, | |
| 67 | y: bool, | |
| 68 | z: void, | |
| 69 | }; | |
| 70 | ||
| 71 | const union1: Union = @import("zon/union1.zon"); | |
| 72 | const union2: Union = @import("zon/union2.zon"); | |
| 73 | const union3: Union = @import("zon/union3.zon"); | |
| 74 | ||
| 75 | try expectEqual(1.5, union1.x); | |
| 76 | try expectEqual(true, union2.y); | |
| 77 | try expectEqual({}, union3.z); | |
| 78 | } | |
| 79 | } | |
| 80 | ||
| 81 | test "struct" { | |
| 82 | const Vec0 = struct {}; | |
| 83 | const Vec1 = struct { x: f32 }; | |
| 84 | const Vec2 = struct { x: f32, y: f32 }; | |
| 85 | const Escaped = struct { @"0": f32, foo: f32 }; | |
| 86 | try expectEqual(Vec0{}, @as(Vec0, @import("zon/vec0.zon"))); | |
| 87 | try expectEqual(Vec1{ .x = 1.5 }, @as(Vec1, @import("zon/vec1.zon"))); | |
| 88 | try expectEqual(Vec2{ .x = 1.5, .y = 2 }, @as(Vec2, @import("zon/vec2.zon"))); | |
| 89 | try expectEqual(Escaped{ .@"0" = 1.5, .foo = 2 }, @as(Escaped, @import("zon/escaped_struct.zon"))); | |
| 90 | } | |
| 91 | ||
| 92 | test "struct default fields" { | |
| 93 | const Vec3 = struct { | |
| 94 | x: f32, | |
| 95 | y: f32, | |
| 96 | z: f32 = 123.4, | |
| 97 | }; | |
| 98 | try expectEqual(Vec3{ .x = 1.5, .y = 2.0, .z = 123.4 }, @as(Vec3, @import("zon/vec2.zon"))); | |
| 99 | const ascribed: Vec3 = @import("zon/vec2.zon"); | |
| 100 | try expectEqual(Vec3{ .x = 1.5, .y = 2.0, .z = 123.4 }, ascribed); | |
| 101 | ||
| 102 | const Vec2 = struct { | |
| 103 | x: f32 = 20.0, | |
| 104 | y: f32 = 10.0, | |
| 105 | }; | |
| 106 | try expectEqual(Vec2{ .x = 1.5, .y = 2.0 }, @as(Vec2, @import("zon/vec2.zon"))); | |
| 107 | } | |
| 108 | ||
| 109 | test "struct enum field" { | |
| 110 | const Struct = struct { | |
| 111 | x: enum { x, y, z }, | |
| 112 | }; | |
| 113 | try expectEqual(Struct{ .x = .z }, @as(Struct, @import("zon/enum_field.zon"))); | |
| 114 | } | |
| 115 | ||
| 116 | test "tuple" { | |
| 117 | const Tuple = struct { f32, bool, []const u8, u16 }; | |
| 118 | try expectEqualDeep(Tuple{ 1.2, true, "hello", 3 }, @as(Tuple, @import("zon/tuple.zon"))); | |
| 119 | } | |
| 120 | ||
| 121 | test "comptime fields" { | |
| 122 | // Test setting comptime tuple fields to the correct value | |
| 123 | { | |
| 124 | const Tuple = struct { | |
| 125 | comptime f32 = 1.2, | |
| 126 | comptime bool = true, | |
| 127 | comptime []const u8 = "hello", | |
| 128 | comptime u16 = 3, | |
| 129 | }; | |
| 130 | try expectEqualDeep(Tuple{ 1.2, true, "hello", 3 }, @as(Tuple, @import("zon/tuple.zon"))); | |
| 131 | } | |
| 132 | ||
| 133 | // Test setting comptime struct fields to the correct value | |
| 134 | { | |
| 135 | const Vec2 = struct { | |
| 136 | comptime x: f32 = 1.5, | |
| 137 | comptime y: f32 = 2.0, | |
| 138 | }; | |
| 139 | try expectEqualDeep(Vec2{}, @as(Vec2, @import("zon/vec2.zon"))); | |
| 140 | } | |
| 141 | ||
| 142 | // Test allowing comptime tuple fields to be set to their defaults | |
| 143 | { | |
| 144 | const Tuple = struct { | |
| 145 | f32, | |
| 146 | bool, | |
| 147 | []const u8, | |
| 148 | u16, | |
| 149 | comptime u8 = 255, | |
| 150 | }; | |
| 151 | try expectEqualDeep(Tuple{ 1.2, true, "hello", 3 }, @as(Tuple, @import("zon/tuple.zon"))); | |
| 152 | } | |
| 153 | ||
| 154 | // Test allowing comptime struct fields to be set to their defaults | |
| 155 | { | |
| 156 | const Vec2 = struct { | |
| 157 | comptime x: f32 = 1.5, | |
| 158 | comptime y: f32 = 2.0, | |
| 159 | }; | |
| 160 | try expectEqualDeep(Vec2{}, @as(Vec2, @import("zon/slice-empty.zon"))); | |
| 161 | } | |
| 162 | } | |
| 163 | ||
| 164 | test "char" { | |
| 165 | try expectEqual(@as(u8, 'a'), @as(u8, @import("zon/a.zon"))); | |
| 166 | try expectEqual(@as(u8, 'z'), @as(u8, @import("zon/z.zon"))); | |
| 167 | } | |
| 168 | ||
| 169 | test "arrays" { | |
| 170 | try expectEqual([0]u8{}, @as([0]u8, @import("zon/vec0.zon"))); | |
| 171 | try expectEqual([0:1]u8{}, @as([0:1]u8, @import("zon/vec0.zon"))); | |
| 172 | try expectEqual(1, @as([0:1]u8, @import("zon/vec0.zon"))[0]); | |
| 173 | try expectEqual([4]u8{ 'a', 'b', 'c', 'd' }, @as([4]u8, @import("zon/array.zon"))); | |
| 174 | try expectEqual([4:2]u8{ 'a', 'b', 'c', 'd' }, @as([4:2]u8, @import("zon/array.zon"))); | |
| 175 | try expectEqual(2, @as([4:2]u8, @import("zon/array.zon"))[4]); | |
| 176 | } | |
| 177 | ||
| 178 | test "slices, arrays, tuples" { | |
| 179 | { | |
| 180 | const expected_slice: []const u8 = &.{}; | |
| 181 | const found_slice: []const u8 = @import("zon/slice-empty.zon"); | |
| 182 | try expectEqualSlices(u8, expected_slice, found_slice); | |
| 183 | ||
| 184 | const expected_array: [0]u8 = .{}; | |
| 185 | const found_array: [0]u8 = @import("zon/slice-empty.zon"); | |
| 186 | try expectEqual(expected_array, found_array); | |
| 187 | ||
| 188 | const T = struct {}; | |
| 189 | const expected_tuple: T = .{}; | |
| 190 | const found_tuple: T = @import("zon/slice-empty.zon"); | |
| 191 | try expectEqual(expected_tuple, found_tuple); | |
| 192 | } | |
| 193 | ||
| 194 | { | |
| 195 | const expected_slice: []const u8 = &.{1}; | |
| 196 | const found_slice: []const u8 = @import("zon/slice1_no_newline.zon"); | |
| 197 | try expectEqualSlices(u8, expected_slice, found_slice); | |
| 198 | ||
| 199 | const expected_array: [1]u8 = .{1}; | |
| 200 | const found_array: [1]u8 = @import("zon/slice1_no_newline.zon"); | |
| 201 | try expectEqual(expected_array, found_array); | |
| 202 | ||
| 203 | const T = struct { u8 }; | |
| 204 | const expected_tuple: T = .{1}; | |
| 205 | const found_tuple: T = @import("zon/slice1_no_newline.zon"); | |
| 206 | try expectEqual(expected_tuple, found_tuple); | |
| 207 | } | |
| 208 | ||
| 209 | { | |
| 210 | const expected_slice: []const u8 = &.{ 'a', 'b', 'c' }; | |
| 211 | const found_slice: []const u8 = @import("zon/slice-abc.zon"); | |
| 212 | try expectEqualSlices(u8, expected_slice, found_slice); | |
| 213 | ||
| 214 | const expected_array: [3]u8 = .{ 'a', 'b', 'c' }; | |
| 215 | const found_array: [3]u8 = @import("zon/slice-abc.zon"); | |
| 216 | try expectEqual(expected_array, found_array); | |
| 217 | ||
| 218 | const T = struct { u8, u8, u8 }; | |
| 219 | const expected_tuple: T = .{ 'a', 'b', 'c' }; | |
| 220 | const found_tuple: T = @import("zon/slice-abc.zon"); | |
| 221 | try expectEqual(expected_tuple, found_tuple); | |
| 222 | } | |
| 223 | } | |
| 224 | ||
| 225 | test "string literals" { | |
| 226 | try expectEqualSlices(u8, "abc", @import("zon/abc.zon")); | |
| 227 | try expectEqualSlices(u8, "ab\\c", @import("zon/abc-escaped.zon")); | |
| 228 | const zero_terminated: [:0]const u8 = @import("zon/abc.zon"); | |
| 229 | try expectEqualDeep(zero_terminated, "abc"); | |
| 230 | try expectEqual(0, zero_terminated[zero_terminated.len]); | |
| 231 | try expectEqualStrings( | |
| 232 | \\Hello, world! | |
| 233 | \\This is a multiline string! | |
| 234 | \\ There are no escapes, we can, for example, include \n in the string | |
| 235 | , @import("zon/multiline_string.zon")); | |
| 236 | try expectEqualStrings("a\nb\x00c", @import("zon/string_embedded_null.zon")); | |
| 237 | } | |
| 238 | ||
| 239 | test "enum literals" { | |
| 240 | const Enum = enum { | |
| 241 | foo, | |
| 242 | bar, | |
| 243 | baz, | |
| 244 | @"0\na", | |
| 245 | }; | |
| 246 | try expectEqual(Enum.foo, @as(Enum, @import("zon/foo.zon"))); | |
| 247 | try expectEqual(.foo, @as(@TypeOf(.foo), @import("zon/foo.zon"))); | |
| 248 | try expectEqual(Enum.@"0\na", @as(Enum, @import("zon/escaped_enum.zon"))); | |
| 249 | } | |
| 250 | ||
| 251 | test "int" { | |
| 252 | const T = struct { | |
| 253 | u8, | |
| 254 | i16, | |
| 255 | i14, | |
| 256 | i32, | |
| 257 | i8, | |
| 258 | i8, | |
| 259 | u8, | |
| 260 | u8, | |
| 261 | u65, | |
| 262 | u65, | |
| 263 | i128, | |
| 264 | i128, | |
| 265 | i66, | |
| 266 | i66, | |
| 267 | i8, | |
| 268 | i8, | |
| 269 | i16, | |
| 270 | i16, | |
| 271 | i16, | |
| 272 | i16, | |
| 273 | i16, | |
| 274 | i16, | |
| 275 | u65, | |
| 276 | i66, | |
| 277 | i66, | |
| 278 | u65, | |
| 279 | i66, | |
| 280 | i66, | |
| 281 | u65, | |
| 282 | i66, | |
| 283 | i66, | |
| 284 | }; | |
| 285 | const expected: T = .{ | |
| 286 | // Test various numbers and types | |
| 287 | 10, | |
| 288 | 24, | |
| 289 | -4, | |
| 290 | -123, | |
| 291 | ||
| 292 | // Test limits | |
| 293 | 127, | |
| 294 | -128, | |
| 295 | ||
| 296 | // Test characters | |
| 297 | 'a', | |
| 298 | 'z', | |
| 299 | ||
| 300 | // Test big integers | |
| 301 | 36893488147419103231, | |
| 302 | 36893488147419103231, | |
| 303 | -18446744073709551615, // Only a big int due to negation | |
| 304 | -9223372036854775809, // Only a big int due to negation | |
| 305 | ||
| 306 | // Test big integer limits | |
| 307 | 36893488147419103231, | |
| 308 | -36893488147419103232, | |
| 309 | ||
| 310 | // Test parsing whole number floats as integers | |
| 311 | -1, | |
| 312 | 123, | |
| 313 | ||
| 314 | // Test non-decimal integers | |
| 315 | 0xff, | |
| 316 | -0xff, | |
| 317 | 0o77, | |
| 318 | -0o77, | |
| 319 | 0b11, | |
| 320 | -0b11, | |
| 321 | ||
| 322 | // Test non-decimal big integers | |
| 323 | 0x1ffffffffffffffff, | |
| 324 | 0x1ffffffffffffffff, | |
| 325 | -0x1ffffffffffffffff, | |
| 326 | 0x1ffffffffffffffff, | |
| 327 | 0x1ffffffffffffffff, | |
| 328 | -0x1ffffffffffffffff, | |
| 329 | 0x1ffffffffffffffff, | |
| 330 | 0x1ffffffffffffffff, | |
| 331 | -0x1ffffffffffffffff, | |
| 332 | }; | |
| 333 | const actual: T = @import("zon/ints.zon"); | |
| 334 | try expectEqual(expected, actual); | |
| 335 | } | |
| 336 | ||
| 337 | test "floats" { | |
| 338 | const T = struct { | |
| 339 | f16, | |
| 340 | f32, | |
| 341 | f64, | |
| 342 | f128, | |
| 343 | f16, | |
| 344 | f16, | |
| 345 | f32, | |
| 346 | f32, | |
| 347 | f32, | |
| 348 | f32, | |
| 349 | f32, | |
| 350 | f32, | |
| 351 | f128, | |
| 352 | f32, | |
| 353 | f32, | |
| 354 | f32, | |
| 355 | f32, | |
| 356 | f32, | |
| 357 | }; | |
| 358 | const expected: T = .{ | |
| 359 | // Test decimals | |
| 360 | 0.5, | |
| 361 | 123.456, | |
| 362 | -123.456, | |
| 363 | 42.5, | |
| 364 | ||
| 365 | // Test whole numbers with and without decimals | |
| 366 | 5.0, | |
| 367 | 5.0, | |
| 368 | -102, | |
| 369 | -102, | |
| 370 | ||
| 371 | // Test characters and negated characters | |
| 372 | 'a', | |
| 373 | 'z', | |
| 374 | ||
| 375 | // Test big integers | |
| 376 | 36893488147419103231, | |
| 377 | -36893488147419103231, | |
| 378 | 0x1ffffffffffffffff, | |
| 379 | 0x1ffffffffffffffff, | |
| 380 | ||
| 381 | // Exponents, underscores | |
| 382 | 123.0E+77, | |
| 383 | ||
| 384 | // Hexadecimal | |
| 385 | 0x103.70p-5, | |
| 386 | -0x103.70, | |
| 387 | 0x1234_5678.9ABC_CDEFp-10, | |
| 388 | }; | |
| 389 | const actual: T = @import("zon/floats.zon"); | |
| 390 | try expectEqual(expected, actual); | |
| 391 | } | |
| 392 | ||
| 393 | test "inf and nan" { | |
| 394 | // f32 | |
| 395 | { | |
| 396 | const actual: struct { f32, f32, f32 } = @import("zon/inf_and_nan.zon"); | |
| 397 | try expect(std.math.isNan(actual[0])); | |
| 398 | try expect(std.math.isPositiveInf(actual[1])); | |
| 399 | try expect(std.math.isNegativeInf(actual[2])); | |
| 400 | } | |
| 401 | ||
| 402 | // f128 | |
| 403 | { | |
| 404 | const actual: struct { f128, f128, f128 } = @import("zon/inf_and_nan.zon"); | |
| 405 | try expect(std.math.isNan(actual[0])); | |
| 406 | try expect(std.math.isPositiveInf(actual[1])); | |
| 407 | try expect(std.math.isNegativeInf(actual[2])); | |
| 408 | } | |
| 409 | } | |
| 410 | ||
| 411 | test "vector" { | |
| 412 | { | |
| 413 | const actual: @Vector(0, bool) = @import("zon/vec0.zon"); | |
| 414 | const expected: @Vector(0, bool) = .{}; | |
| 415 | try expectEqual(expected, actual); | |
| 416 | } | |
| 417 | { | |
| 418 | const actual: @Vector(3, bool) = @import("zon/vec3_bool.zon"); | |
| 419 | const expected: @Vector(3, bool) = .{ false, false, true }; | |
| 420 | try expectEqual(expected, actual); | |
| 421 | } | |
| 422 | ||
| 423 | { | |
| 424 | const actual: @Vector(0, f32) = @import("zon/vec0.zon"); | |
| 425 | const expected: @Vector(0, f32) = .{}; | |
| 426 | try expectEqual(expected, actual); | |
| 427 | } | |
| 428 | { | |
| 429 | const actual: @Vector(3, f32) = @import("zon/vec3_float.zon"); | |
| 430 | const expected: @Vector(3, f32) = .{ 1.5, 2.5, 3.5 }; | |
| 431 | try expectEqual(expected, actual); | |
| 432 | } | |
| 433 | ||
| 434 | { | |
| 435 | const actual: @Vector(0, u8) = @import("zon/vec0.zon"); | |
| 436 | const expected: @Vector(0, u8) = .{}; | |
| 437 | try expectEqual(expected, actual); | |
| 438 | } | |
| 439 | { | |
| 440 | const actual: @Vector(3, u8) = @import("zon/vec3_int.zon"); | |
| 441 | const expected: @Vector(3, u8) = .{ 2, 4, 6 }; | |
| 442 | try expectEqual(expected, actual); | |
| 443 | } | |
| 444 | ||
| 445 | { | |
| 446 | const actual: @Vector(0, *const u8) = @import("zon/vec0.zon"); | |
| 447 | const expected: @Vector(0, *const u8) = .{}; | |
| 448 | try expectEqual(expected, actual); | |
| 449 | } | |
| 450 | { | |
| 451 | const actual: @Vector(3, *const u8) = @import("zon/vec3_int.zon"); | |
| 452 | const expected: @Vector(3, *const u8) = .{ &2, &4, &6 }; | |
| 453 | try expectEqual(expected, actual); | |
| 454 | } | |
| 455 | ||
| 456 | { | |
| 457 | const actual: @Vector(0, ?*const u8) = @import("zon/vec0.zon"); | |
| 458 | const expected: @Vector(0, ?*const u8) = .{}; | |
| 459 | try expectEqual(expected, actual); | |
| 460 | } | |
| 461 | { | |
| 462 | const actual: @Vector(3, ?*const u8) = @import("zon/vec3_int_opt.zon"); | |
| 463 | const expected: @Vector(3, ?*const u8) = .{ &2, null, &6 }; | |
| 464 | try expectEqual(expected, actual); | |
| 465 | } | |
| 466 | } | |
| 467 | ||
| 468 | test "pointers" { | |
| 469 | // Primitive with varying levels of pointers | |
| 470 | try expectEqual(@as(u8, 'a'), @as(*const u8, @import("zon/a.zon")).*); | |
| 471 | try expectEqual(@as(u8, 'a'), @as(*const *const u8, @import("zon/a.zon")).*.*); | |
| 472 | try expectEqual(@as(u8, 'a'), @as(*const *const *const u8, @import("zon/a.zon")).*.*.*); | |
| 473 | ||
| 474 | // Primitive optional with varying levels of pointers | |
| 475 | try expectEqual(@as(u8, 'a'), @as(?*const u8, @import("zon/a.zon")).?.*); | |
| 476 | try expectEqual(null, @as(?*const u8, @import("zon/none.zon"))); | |
| 477 | ||
| 478 | try expectEqual(@as(u8, 'a'), @as(*const ?u8, @import("zon/a.zon")).*.?); | |
| 479 | try expectEqual(null, @as(*const ?u8, @import("zon/none.zon")).*); | |
| 480 | ||
| 481 | try expectEqual(@as(u8, 'a'), @as(?*const *const u8, @import("zon/a.zon")).?.*.*); | |
| 482 | try expectEqual(null, @as(?*const *const u8, @import("zon/none.zon"))); | |
| 483 | ||
| 484 | try expectEqual(@as(u8, 'a'), @as(*const ?*const u8, @import("zon/a.zon")).*.?.*); | |
| 485 | try expectEqual(null, @as(*const ?*const u8, @import("zon/none.zon")).*); | |
| 486 | ||
| 487 | try expectEqual(@as(u8, 'a'), @as(*const *const ?u8, @import("zon/a.zon")).*.*.?); | |
| 488 | try expectEqual(null, @as(*const *const ?u8, @import("zon/none.zon")).*.*); | |
| 489 | ||
| 490 | try expectEqual([3]u8{ 2, 4, 6 }, @as(*const [3]u8, @import("zon/vec3_int.zon")).*); | |
| 491 | ||
| 492 | // A complicated type with nested internal pointers and string allocations | |
| 493 | { | |
| 494 | const Inner = struct { | |
| 495 | f1: *const ?*const []const u8, | |
| 496 | f2: *const ?*const []const u8, | |
| 497 | }; | |
| 498 | const Outer = struct { | |
| 499 | f1: *const ?*const Inner, | |
| 500 | f2: *const ?*const Inner, | |
| 501 | }; | |
| 502 | const expected: Outer = .{ | |
| 503 | .f1 = &&.{ | |
| 504 | .f1 = &null, | |
| 505 | .f2 = &&"foo", | |
| 506 | }, | |
| 507 | .f2 = &null, | |
| 508 | }; | |
| 509 | ||
| 510 | const found: ?*const Outer = @import("zon/complex.zon"); | |
| 511 | try std.testing.expectEqualDeep(expected, found.?.*); | |
| 512 | } | |
| 513 | } | |
| 514 | ||
| 515 | test "recursive" { | |
| 516 | const Recursive = struct { foo: ?*const @This() }; | |
| 517 | const expected: Recursive = .{ .foo = &.{ .foo = null } }; | |
| 518 | try expectEqualDeep(expected, @as(Recursive, @import("zon/recursive.zon"))); | |
| 519 | } |
test/behavior/zon/a.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | 'a' |
test/behavior/zon/abc-escaped.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | "ab\\c" |
test/behavior/zon/abc.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | "abc" |
test/behavior/zon/array.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ 'a', 'b', 'c', 'd' } |
test/behavior/zon/complex.zon created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | .{ | |
| 2 | .f1 = .{ | |
| 3 | .f1 = null, | |
| 4 | .f2 = "foo", | |
| 5 | }, | |
| 6 | .f2 = null, | |
| 7 | } |
test/behavior/zon/enum_field.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ .x = .z } |
test/behavior/zon/escaped_enum.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .@"0\na" |
test/behavior/zon/escaped_struct.zon created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | ||
| 2 | .{ .@"0" = 1.5, .@"foo" = 2 } |
test/behavior/zon/false.zon created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | // Comment | |
| 2 | false // Another comment | |
| 3 | // Yet another comment | |
| 4 |
test/behavior/zon/floats.zon created+25| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | .{ | |
| 2 | 0.5, | |
| 3 | 123.456, | |
| 4 | -123.456, | |
| 5 | 42.5, | |
| 6 | ||
| 7 | 5.0, | |
| 8 | 5, | |
| 9 | -102.0, | |
| 10 | -102, | |
| 11 | ||
| 12 | 'a', | |
| 13 | 'z', | |
| 14 | ||
| 15 | 36893488147419103231, | |
| 16 | -36893488147419103231, | |
| 17 | 0x1ffffffffffffffff, | |
| 18 | 0x1ffffffffffffffff, | |
| 19 | ||
| 20 | 12_3.0E+77, | |
| 21 | ||
| 22 | 0x103.70p-5, | |
| 23 | -0x103.70, | |
| 24 | 0x1234_5678.9ABC_CDEFp-10, | |
| 25 | } |
test/behavior/zon/foo.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .foo |
test/behavior/zon/inf_and_nan.zon created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | .{ | |
| 2 | nan, | |
| 3 | inf, | |
| 4 | -inf, | |
| 5 | } |
test/behavior/zon/ints.zon created+40| ... | ... | @@ -0,0 +1,40 @@ |
| 1 | .{ | |
| 2 | 10, | |
| 3 | 24, | |
| 4 | -4, | |
| 5 | -123, | |
| 6 | ||
| 7 | 127, | |
| 8 | -128, | |
| 9 | ||
| 10 | 'a', | |
| 11 | 'z', | |
| 12 | ||
| 13 | 36893488147419103231, | |
| 14 | 368934_881_474191032_31, | |
| 15 | -18446744073709551615, | |
| 16 | -9223372036854775809, | |
| 17 | ||
| 18 | 36893488147419103231, | |
| 19 | -36893488147419103232, | |
| 20 | ||
| 21 | -1.0, | |
| 22 | 123.0, | |
| 23 | ||
| 24 | 0xff, | |
| 25 | -0xff, | |
| 26 | 0o77, | |
| 27 | -0o77, | |
| 28 | 0b11, | |
| 29 | -0b11, | |
| 30 | ||
| 31 | 0x1ffffffffffffffff, | |
| 32 | 0x1ffffffffffffffff, | |
| 33 | -0x1ffffffffffffffff, | |
| 34 | 0o3777777777777777777777, | |
| 35 | 0o3777777777777777777777, | |
| 36 | -0o3777777777777777777777, | |
| 37 | 0b11111111111111111111111111111111111111111111111111111111111111111, | |
| 38 | 0b11111111111111111111111111111111111111111111111111111111111111111, | |
| 39 | -0b11111111111111111111111111111111111111111111111111111111111111111, | |
| 40 | } |
test/behavior/zon/multiline_string.zon created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | // zig fmt: off | |
| 2 | \\Hello, world! | |
| 3 | \\This is a multiline string! | |
| 4 | \\ There are no escapes, we can, for example, include \n in the string |
test/behavior/zon/none.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | null |
test/behavior/zon/recursive.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ .foo = .{ .foo = null } } |
test/behavior/zon/slice-abc.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{'a', 'b', 'c'} | |
| \ No newline at end of file |
test/behavior/zon/slice-empty.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{} | |
| \ No newline at end of file |
test/behavior/zon/slice1_no_newline.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ 1 } | |
| \ No newline at end of file |
test/behavior/zon/some.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | 10 |
test/behavior/zon/string_embedded_null.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | "a\nb\x00c" |
test/behavior/zon/true.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | true |
test/behavior/zon/tuple.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ 1.2, true, "hello", 3 } |
test/behavior/zon/union1.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ .x = 1.5 } |
test/behavior/zon/union2.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ .y = true } |
test/behavior/zon/union3.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .z |
test/behavior/zon/vec0.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{} |
test/behavior/zon/vec1.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ .x = 1.5 } |
test/behavior/zon/vec2.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ .x = 1.5, .y = 2 } |
test/behavior/zon/vec3_bool.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ false, false, true } |
test/behavior/zon/vec3_float.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ 1.5, 2.5, 3.5 } |
test/behavior/zon/vec3_int.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ 2, 4, 6 } |
test/behavior/zon/vec3_int_opt.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ 2, null, 6 } |
test/behavior/zon/z.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | 'z' |
test/cases/compile_errors/@import_zon_addr_slice.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | pub fn main() void { | |
| 2 | const f: struct { value: []const i32 } = @import("zon/addr_slice.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/addr_slice.zon | |
| 8 | // | |
| 9 | // addr_slice.zon:2:14: error: pointers are not available in ZON |
test/cases/compile_errors/@import_zon_array_len.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: [4]u8 = @import("zon/array.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/array.zon | |
| 8 | // | |
| 9 | // array.zon:1:2: error: expected type '[4]u8' | |
| 10 | // tmp.zig:2:30: note: imported here |
test/cases/compile_errors/@import_zon_bad_import.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | _ = @import( | |
| 3 | "bogus-does-not-exist.zon", | |
| 4 | ); | |
| 5 | } | |
| 6 | ||
| 7 | // error | |
| 8 | // | |
| 9 | // :3:9: error: unable to open 'bogus-does-not-exist.zon': FileNotFound |
test/cases/compile_errors/@import_zon_bad_type.zig created+125| ... | ... | @@ -0,0 +1,125 @@ |
| 1 | export fn testVoid() void { | |
| 2 | const f: void = @import("zon/neg_inf.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | export fn testInStruct() void { | |
| 7 | const f: struct { f: [*]const u8 } = @import("zon/neg_inf.zon"); | |
| 8 | _ = f; | |
| 9 | } | |
| 10 | ||
| 11 | export fn testError() void { | |
| 12 | const f: struct { error{foo} } = @import("zon/neg_inf.zon"); | |
| 13 | _ = f; | |
| 14 | } | |
| 15 | ||
| 16 | export fn testInUnion() void { | |
| 17 | const f: union(enum) { a: void, b: [*c]const u8 } = @import("zon/neg_inf.zon"); | |
| 18 | _ = f; | |
| 19 | } | |
| 20 | ||
| 21 | export fn testInVector() void { | |
| 22 | const f: @Vector(0, [*c]const u8) = @import("zon/neg_inf.zon"); | |
| 23 | _ = f; | |
| 24 | } | |
| 25 | ||
| 26 | export fn testInOpt() void { | |
| 27 | const f: *const ?[*c]const u8 = @import("zon/neg_inf.zon"); | |
| 28 | _ = f; | |
| 29 | } | |
| 30 | ||
| 31 | export fn testComptimeField() void { | |
| 32 | const f: struct { comptime foo: ??u8 = null } = @import("zon/neg_inf.zon"); | |
| 33 | _ = f; | |
| 34 | } | |
| 35 | ||
| 36 | export fn testEnumLiteral() void { | |
| 37 | const f: @TypeOf(.foo) = @import("zon/neg_inf.zon"); | |
| 38 | _ = f; | |
| 39 | } | |
| 40 | ||
| 41 | export fn testNestedOpt1() void { | |
| 42 | const f: ??u8 = @import("zon/neg_inf.zon"); | |
| 43 | _ = f; | |
| 44 | } | |
| 45 | ||
| 46 | export fn testNestedOpt2() void { | |
| 47 | const f: ?*const ?u8 = @import("zon/neg_inf.zon"); | |
| 48 | _ = f; | |
| 49 | } | |
| 50 | ||
| 51 | export fn testNestedOpt3() void { | |
| 52 | const f: *const ?*const ?*const u8 = @import("zon/neg_inf.zon"); | |
| 53 | _ = f; | |
| 54 | } | |
| 55 | ||
| 56 | export fn testOpt() void { | |
| 57 | const f: ?u8 = @import("zon/neg_inf.zon"); | |
| 58 | _ = f; | |
| 59 | } | |
| 60 | ||
| 61 | export fn testNonExhaustiveEnum() void { | |
| 62 | const f: enum(u8) { _ } = @import("zon/neg_inf.zon"); | |
| 63 | _ = f; | |
| 64 | } | |
| 65 | ||
| 66 | export fn testUntaggedUnion() void { | |
| 67 | const f: union { foo: void } = @import("zon/neg_inf.zon"); | |
| 68 | _ = f; | |
| 69 | } | |
| 70 | ||
| 71 | export fn testTaggedUnionVoid() void { | |
| 72 | const f: union(enum) { foo: void } = @import("zon/neg_inf.zon"); | |
| 73 | _ = f; | |
| 74 | } | |
| 75 | ||
| 76 | export fn testVisited() void { | |
| 77 | const V = struct { | |
| 78 | ?f32, // Adds `?f32` to the visited list | |
| 79 | ??f32, // `?f32` is already visited, we need to detect the nested opt anyway | |
| 80 | f32, | |
| 81 | }; | |
| 82 | const f: V = @import("zon/neg_inf.zon"); | |
| 83 | _ = f; | |
| 84 | } | |
| 85 | ||
| 86 | export fn testMutablePointer() void { | |
| 87 | const f: *i32 = @import("zon/neg_inf.zon"); | |
| 88 | _ = f; | |
| 89 | } | |
| 90 | ||
| 91 | // error | |
| 92 | // imports=zon/neg_inf.zon | |
| 93 | // | |
| 94 | // tmp.zig:2:29: error: type 'void' is not available in ZON | |
| 95 | // tmp.zig:7:50: error: type '[*]const u8' is not available in ZON | |
| 96 | // tmp.zig:7:50: note: ZON does not allow many-pointers | |
| 97 | // tmp.zig:12:46: error: type 'error{foo}' is not available in ZON | |
| 98 | // tmp.zig:17:65: error: type '[*c]const u8' is not available in ZON | |
| 99 | // tmp.zig:17:65: note: ZON does not allow C pointers | |
| 100 | // tmp.zig:22:49: error: type '[*c]const u8' is not available in ZON | |
| 101 | // tmp.zig:22:49: note: ZON does not allow C pointers | |
| 102 | // tmp.zig:27:45: error: type '[*c]const u8' is not available in ZON | |
| 103 | // tmp.zig:27:45: note: ZON does not allow C pointers | |
| 104 | // tmp.zig:32:61: error: type '??u8' is not available in ZON | |
| 105 | // tmp.zig:32:61: note: ZON does not allow nested optionals | |
| 106 | // tmp.zig:42:29: error: type '??u8' is not available in ZON | |
| 107 | // tmp.zig:42:29: note: ZON does not allow nested optionals | |
| 108 | // tmp.zig:47:36: error: type '?*const ?u8' is not available in ZON | |
| 109 | // tmp.zig:47:36: note: ZON does not allow nested optionals | |
| 110 | // tmp.zig:52:50: error: type '?*const ?*const u8' is not available in ZON | |
| 111 | // tmp.zig:52:50: note: ZON does not allow nested optionals | |
| 112 | // tmp.zig:82:26: error: type '??f32' is not available in ZON | |
| 113 | // tmp.zig:82:26: note: ZON does not allow nested optionals | |
| 114 | // tmp.zig:87:29: error: type '*i32' is not available in ZON | |
| 115 | // tmp.zig:87:29: note: ZON does not allow mutable pointers | |
| 116 | // neg_inf.zon:1:1: error: expected type '@Type(.enum_literal)' | |
| 117 | // tmp.zig:37:38: note: imported here | |
| 118 | // neg_inf.zon:1:1: error: expected type '?u8' | |
| 119 | // tmp.zig:57:28: note: imported here | |
| 120 | // neg_inf.zon:1:1: error: expected type 'tmp.testNonExhaustiveEnum__enum_490' | |
| 121 | // tmp.zig:62:39: note: imported here | |
| 122 | // neg_inf.zon:1:1: error: expected type 'tmp.testUntaggedUnion__union_492' | |
| 123 | // tmp.zig:67:44: note: imported here | |
| 124 | // neg_inf.zon:1:1: error: expected type 'tmp.testTaggedUnionVoid__union_495' | |
| 125 | // tmp.zig:72:50: note: imported here |
test/cases/compile_errors/@import_zon_comptime_inf.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: comptime_float = @import("zon/inf.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/inf.zon | |
| 8 | // | |
| 9 | // inf.zon:1:1: error: expected type 'comptime_float' | |
| 10 | // tmp.zig:2:39: note: imported here |
test/cases/compile_errors/@import_zon_comptime_nan.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: comptime_float = @import("zon/nan.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/nan.zon | |
| 8 | // | |
| 9 | // nan.zon:1:1: error: expected type 'comptime_float' | |
| 10 | // tmp.zig:2:39: note: imported here |
test/cases/compile_errors/@import_zon_comptime_neg_inf.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: comptime_float = @import("zon/neg_inf.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/neg_inf.zon | |
| 8 | // | |
| 9 | // neg_inf.zon:1:1: error: expected type 'comptime_float' | |
| 10 | // tmp.zig:2:39: note: imported here |
test/cases/compile_errors/@import_zon_doc_comment.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: struct { foo: type } = @import("zon/doc_comment.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/doc_comment.zon | |
| 8 | // | |
| 9 | // doc_comment.zon:1:1: error: expected expression, found 'a document comment' |
test/cases/compile_errors/@import_zon_double_negation_float.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: f32 = @import("zon/double_negation_float.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/double_negation_float.zon | |
| 8 | // | |
| 9 | // double_negation_float.zon:1:1: error: expected number or 'inf' after '-' |
test/cases/compile_errors/@import_zon_double_negation_int.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: i32 = @import("zon/double_negation_int.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/double_negation_int.zon | |
| 8 | // | |
| 9 | // double_negation_int.zon:1:1: error: expected number or 'inf' after '-' |
test/cases/compile_errors/@import_zon_enum_embedded_null.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | export fn entry() void { | |
| 3 | const E = enum { foo }; | |
| 4 | const f: struct { E, E } = @import("zon/enum_embedded_null.zon"); | |
| 5 | _ = f; | |
| 6 | } | |
| 7 | ||
| 8 | // error | |
| 9 | // imports=zon/enum_embedded_null.zon | |
| 10 | // | |
| 11 | // enum_embedded_null.zon:2:6: error: identifier cannot contain null bytes |
test/cases/compile_errors/@import_zon_expected_void.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | export fn entry() void { | |
| 2 | const U = union(enum) { a: void }; | |
| 3 | const f: U = @import("zon/simple_union.zon"); | |
| 4 | _ = f; | |
| 5 | } | |
| 6 | ||
| 7 | // error | |
| 8 | // imports=zon/simple_union.zon | |
| 9 | // | |
| 10 | // simple_union.zon:1:9: error: expected type 'void' | |
| 11 | // tmp.zig:3:26: note: imported here |
test/cases/compile_errors/@import_zon_invalid_character.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: u8 = @import("zon/invalid_character.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/invalid_character.zon | |
| 8 | // | |
| 9 | // invalid_character.zon:1:3: error: invalid escape character: 'a' |
test/cases/compile_errors/@import_zon_invalid_number.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: u128 = @import("zon/invalid_number.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/invalid_number.zon | |
| 8 | // | |
| 9 | // invalid_number.zon:1:19: error: invalid digit 'a' for decimal base |
test/cases/compile_errors/@import_zon_invalid_string.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: []const u8 = @import("zon/invalid_string.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/invalid_string.zon | |
| 8 | // | |
| 9 | // invalid_string.zon:1:5: error: invalid escape character: 'a' |
test/cases/compile_errors/@import_zon_leading_zero_in_integer.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: u128 = @import("zon/leading_zero_in_integer.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/leading_zero_in_integer.zon | |
| 8 | // | |
| 9 | // leading_zero_in_integer.zon:1:1: error: number '0012' has leading zero | |
| 10 | // leading_zero_in_integer.zon:1:1: note: use '0o' prefix for octal literals |
test/cases/compile_errors/@import_zon_neg_char.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: u8 = @import("zon/neg_char.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/neg_char.zon | |
| 8 | // | |
| 9 | // neg_char.zon:1:1: error: expected number or 'inf' after '-' |
test/cases/compile_errors/@import_zon_neg_nan.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: u8 = @import("zon/neg_nan.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/neg_nan.zon | |
| 8 | // | |
| 9 | // neg_nan.zon:1:1: error: expected number or 'inf' after '-' |
test/cases/compile_errors/@import_zon_negative_zero.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: i8 = @import("zon/negative_zero.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/negative_zero.zon | |
| 8 | // | |
| 9 | // negative_zero.zon:1:2: error: integer literal '-0' is ambiguous | |
| 10 | // negative_zero.zon:1:2: note: use '0' for an integer zero | |
| 11 | // negative_zero.zon:1:2: note: use '-0.0' for a floating-point signed zero |
test/cases/compile_errors/@import_zon_no_rt.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | const f = @import("zon/simple_union.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/simple_union.zon | |
| 8 | // | |
| 9 | // tmp.zig:2:23: error: '@import' of ZON must have a known result type |
test/cases/compile_errors/@import_zon_number_fail_limits.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: i66 = @import("zon/large_number.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/large_number.zon | |
| 8 | // | |
| 9 | // large_number.zon:1:1: error: type 'i66' cannot represent integer value '36893488147419103232' | |
| 10 | // tmp.zig:2:28: note: imported here |
test/cases/compile_errors/@import_zon_oob_char_0.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | export fn entry() void { | |
| 2 | { | |
| 3 | const f: u6 = @import("zon/char_32.zon"); | |
| 4 | _ = f; | |
| 5 | } | |
| 6 | { | |
| 7 | const f: u5 = @import("zon/char_32.zon"); | |
| 8 | _ = f; | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | // error | |
| 13 | // imports=zon/char_32.zon | |
| 14 | // | |
| 15 | // char_32.zon:1:1: error: type 'u5' cannot represent integer value '32' | |
| 16 | // tmp.zig:7:31: note: imported here |
test/cases/compile_errors/@import_zon_oob_char_1.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | export fn entry() void { | |
| 2 | { | |
| 3 | const f: i7 = @import("zon/char_32.zon"); | |
| 4 | _ = f; | |
| 5 | } | |
| 6 | { | |
| 7 | const f: i6 = @import("zon/char_32.zon"); | |
| 8 | _ = f; | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | // error | |
| 13 | // imports=zon/char_32.zon | |
| 14 | // | |
| 15 | // char_32.zon:1:1: error: type 'i6' cannot represent integer value '32' | |
| 16 | // tmp.zig:7:31: note: imported here |
test/cases/compile_errors/@import_zon_oob_int_0.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | export fn entry() void { | |
| 2 | { | |
| 3 | const f: u6 = @import("zon/int_32.zon"); | |
| 4 | _ = f; | |
| 5 | } | |
| 6 | { | |
| 7 | const f: u5 = @import("zon/int_32.zon"); | |
| 8 | _ = f; | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | // error | |
| 13 | // imports=zon/int_32.zon | |
| 14 | // | |
| 15 | // int_32.zon:1:1: error: type 'u5' cannot represent integer value '32' | |
| 16 | // tmp.zig:7:31: note: imported here |
test/cases/compile_errors/@import_zon_oob_int_1.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | export fn entry() void { | |
| 2 | { | |
| 3 | const f: i7 = @import("zon/int_32.zon"); | |
| 4 | _ = f; | |
| 5 | } | |
| 6 | { | |
| 7 | const f: i6 = @import("zon/int_32.zon"); | |
| 8 | _ = f; | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | // error | |
| 13 | // imports=zon/int_32.zon | |
| 14 | // | |
| 15 | // int_32.zon:1:1: error: type 'i6' cannot represent integer value '32' | |
| 16 | // tmp.zig:7:31: note: imported here |
test/cases/compile_errors/@import_zon_oob_int_2.zig created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | export fn entry() void { | |
| 2 | { | |
| 3 | const f: i7 = @import("zon/int_neg_33.zon"); | |
| 4 | _ = f; | |
| 5 | } | |
| 6 | { | |
| 7 | const f: i6 = @import("zon/int_neg_33.zon"); | |
| 8 | _ = f; | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | // error | |
| 13 | // imports=zon/int_neg_33.zon | |
| 14 | // | |
| 15 | // int_neg_33.zon:1:1: error: type 'i6' cannot represent integer value '-33' | |
| 16 | // tmp.zig:7:31: note: imported here |
test/cases/compile_errors/@import_zon_oob_int_3.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: u64 = @import("zon/int_neg_33.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/int_neg_33.zon | |
| 8 | // | |
| 9 | // int_neg_33.zon:1:1: error: type 'u64' cannot represent integer value '-33' | |
| 10 | // tmp.zig:2:28: note: imported here |
test/cases/compile_errors/@import_zon_opt_in_err.zig created+82| ... | ... | @@ -0,0 +1,82 @@ |
| 1 | export fn testFloatA() void { | |
| 2 | const f: ?f32 = @import("zon/vec2.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | export fn testFloatB() void { | |
| 7 | const f: *const ?f32 = @import("zon/vec2.zon"); | |
| 8 | _ = f; | |
| 9 | } | |
| 10 | ||
| 11 | export fn testFloatC() void { | |
| 12 | const f: ?*const f32 = @import("zon/vec2.zon"); | |
| 13 | _ = f; | |
| 14 | } | |
| 15 | ||
| 16 | export fn testBool() void { | |
| 17 | const f: ?bool = @import("zon/vec2.zon"); | |
| 18 | _ = f; | |
| 19 | } | |
| 20 | ||
| 21 | export fn testInt() void { | |
| 22 | const f: ?i32 = @import("zon/vec2.zon"); | |
| 23 | _ = f; | |
| 24 | } | |
| 25 | ||
| 26 | const Enum = enum { foo }; | |
| 27 | export fn testEnum() void { | |
| 28 | const f: ?Enum = @import("zon/vec2.zon"); | |
| 29 | _ = f; | |
| 30 | } | |
| 31 | ||
| 32 | export fn testEnumLit() void { | |
| 33 | const f: ?@TypeOf(.foo) = @import("zon/vec2.zon"); | |
| 34 | _ = f; | |
| 35 | } | |
| 36 | ||
| 37 | export fn testArray() void { | |
| 38 | const f: ?[1]u8 = @import("zon/vec2.zon"); | |
| 39 | _ = f; | |
| 40 | } | |
| 41 | ||
| 42 | const Union = union {}; | |
| 43 | export fn testUnion() void { | |
| 44 | const f: ?Union = @import("zon/vec2.zon"); | |
| 45 | _ = f; | |
| 46 | } | |
| 47 | ||
| 48 | export fn testSlice() void { | |
| 49 | const f: ?[]const u8 = @import("zon/vec2.zon"); | |
| 50 | _ = f; | |
| 51 | } | |
| 52 | ||
| 53 | export fn testVector() void { | |
| 54 | const f: ?@Vector(3, f32) = @import("zon/vec2.zon"); | |
| 55 | _ = f; | |
| 56 | } | |
| 57 | ||
| 58 | // error | |
| 59 | // imports=zon/vec2.zon | |
| 60 | // | |
| 61 | // vec2.zon:1:2: error: expected type '?f32' | |
| 62 | // tmp.zig:2:29: note: imported here | |
| 63 | // vec2.zon:1:2: error: expected type '*const ?f32' | |
| 64 | // tmp.zig:7:36: note: imported here | |
| 65 | // vec2.zon:1:2: error: expected type '?*const f32' | |
| 66 | // tmp.zig:12:36: note: imported here | |
| 67 | // vec2.zon:1:2: error: expected type '?bool' | |
| 68 | // tmp.zig:17:30: note: imported here | |
| 69 | // vec2.zon:1:2: error: expected type '?i32' | |
| 70 | // tmp.zig:22:29: note: imported here | |
| 71 | // vec2.zon:1:2: error: expected type '?tmp.Enum' | |
| 72 | // tmp.zig:28:30: note: imported here | |
| 73 | // vec2.zon:1:2: error: expected type '?@Type(.enum_literal)' | |
| 74 | // tmp.zig:33:39: note: imported here | |
| 75 | // vec2.zon:1:2: error: expected type '?[1]u8' | |
| 76 | // tmp.zig:38:31: note: imported here | |
| 77 | // vec2.zon:1:2: error: expected type '?tmp.Union' | |
| 78 | // tmp.zig:44:31: note: imported here | |
| 79 | // vec2.zon:1:2: error: expected type '?[]const u8' | |
| 80 | // tmp.zig:49:36: note: imported here | |
| 81 | // vec2.zon:1:2: error: expected type '?@Vector(3, f32)' | |
| 82 | // tmp.zig:54:41: note: imported here |
test/cases/compile_errors/@import_zon_opt_in_err_struct.zig created+19| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | const Struct = struct { f: bool }; | |
| 2 | export fn testStruct() void { | |
| 3 | const f: ?Struct = @import("zon/nan.zon"); | |
| 4 | _ = f; | |
| 5 | } | |
| 6 | ||
| 7 | const Tuple = struct { bool }; | |
| 8 | export fn testTuple() void { | |
| 9 | const f: ?Tuple = @import("zon/nan.zon"); | |
| 10 | _ = f; | |
| 11 | } | |
| 12 | ||
| 13 | // error | |
| 14 | // imports=zon/nan.zon | |
| 15 | // | |
| 16 | //nan.zon:1:1: error: expected type '?tmp.Struct' | |
| 17 | //tmp.zig:3:32: note: imported here | |
| 18 | //nan.zon:1:1: error: expected type '?struct { bool }' | |
| 19 | //tmp.zig:9:31: note: imported here |
test/cases/compile_errors/@import_zon_string_as_array.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: [5]u8 = @import("zon/hello.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/hello.zon | |
| 8 | // | |
| 9 | // hello.zon:1:1: error: expected type '[5]u8' | |
| 10 | // tmp.zig:2:30: note: imported here |
test/cases/compile_errors/@import_zon_struct_dup_field.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const std = @import("std"); | |
| 2 | export fn entry() void { | |
| 3 | const f: struct { name: u8 } = @import("zon/struct_dup_field.zon"); | |
| 4 | _ = f; | |
| 5 | } | |
| 6 | ||
| 7 | // error | |
| 8 | // imports=zon/struct_dup_field.zon | |
| 9 | // | |
| 10 | // struct_dup_field.zon:2:6: error: duplicate struct field name | |
| 11 | // struct_dup_field.zon:3:6: note: duplicate name here |
test/cases/compile_errors/@import_zon_struct_wrong_comptime_field.zig created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | export fn entry() void { | |
| 2 | const Vec2 = struct { | |
| 3 | comptime x: f32 = 1.5, | |
| 4 | comptime y: f32 = 2.5, | |
| 5 | }; | |
| 6 | const f: Vec2 = @import("zon/vec2.zon"); | |
| 7 | _ = f; | |
| 8 | } | |
| 9 | ||
| 10 | // error | |
| 11 | // imports=zon/vec2.zon | |
| 12 | // | |
| 13 | // vec2.zon:1:19: error: value stored in comptime field does not match the default value of the field | |
| 14 | // tmp.zig:6:29: note: imported here |
test/cases/compile_errors/@import_zon_syntax_error.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: bool = @import("zon/syntax_error.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/syntax_error.zon | |
| 8 | // | |
| 9 | // syntax_error.zon:3:13: error: expected ',' after initializer |
test/cases/compile_errors/@import_zon_tuple_wrong_comptime_field.zig created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | export fn entry() void { | |
| 2 | const T = struct { | |
| 3 | comptime f32 = 1.5, | |
| 4 | comptime f32 = 2.5, | |
| 5 | }; | |
| 6 | const f: T = @import("zon/tuple.zon"); | |
| 7 | _ = f; | |
| 8 | } | |
| 9 | ||
| 10 | // error | |
| 11 | // imports=zon/tuple.zon | |
| 12 | // | |
| 13 | // tuple.zon:1:9: error: value stored in comptime field does not match the default value of the field | |
| 14 | // tmp.zig:6:26: note: imported here |
test/cases/compile_errors/@import_zon_type_decl.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: struct { foo: type } = @import("zon/type_decl.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/type_decl.zon | |
| 8 | // | |
| 9 | // type_decl.zon:2:12: error: types are not available in ZON |
test/cases/compile_errors/@import_zon_type_expr_array.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: [3]i32 = @import("zon/type_expr_array.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/type_expr_array.zon | |
| 8 | // | |
| 9 | // type_expr_array.zon:1:1: error: types are not available in ZON | |
| 10 | // type_expr_array.zon:1:1: note: replace the type with '.' |
test/cases/compile_errors/@import_zon_type_expr_fn.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: i32 = @import("zon/type_expr_fn.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/type_expr_fn.zon | |
| 8 | // | |
| 9 | // type_expr_fn.zon:1:1: error: types are not available in ZON | |
| 10 | // type_expr_fn.zon:1:1: note: replace the type with '.' |
test/cases/compile_errors/@import_zon_type_expr_struct.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: struct { x: f32, y: f32 } = @import("zon/type_expr_struct.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/type_expr_struct.zon | |
| 8 | // | |
| 9 | // type_expr_struct.zon:1:1: error: types are not available in ZON | |
| 10 | // type_expr_struct.zon:1:1: note: replace the type with '.' |
test/cases/compile_errors/@import_zon_type_expr_tuple.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: struct { f32, f32 } = @import("zon/type_expr_tuple.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/type_expr_tuple.zon | |
| 8 | // | |
| 9 | // type_expr_tuple.zon:1:1: error: types are not available in ZON | |
| 10 | // type_expr_tuple.zon:1:1: note: replace the type with '.' |
test/cases/compile_errors/@import_zon_type_mismatch.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: bool = @import("zon/struct.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/struct.zon | |
| 8 | // | |
| 9 | // struct.zon:1:2: error: expected type 'bool' | |
| 10 | // tmp.zig:2:29: note: imported here |
test/cases/compile_errors/@import_zon_unescaped_newline.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: i8 = @import("zon/unescaped_newline.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/unescaped_newline.zon | |
| 8 | // | |
| 9 | // unescaped_newline.zon:1:1: error: expected expression, found 'invalid token' |
test/cases/compile_errors/@import_zon_unknown_ident.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: struct { value: bool } = @import("zon/unknown_ident.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/unknown_ident.zon | |
| 8 | // | |
| 9 | // unknown_ident.zon:2:14: error: invalid expression | |
| 10 | // unknown_ident.zon:2:14: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan' | |
| 11 | // unknown_ident.zon:2:14: note: precede identifier with '.' for an enum literal |
test/cases/compile_errors/@import_zon_vec_too_few.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: @Vector(3, f32) = @import("zon/tuple.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/tuple.zon | |
| 8 | // | |
| 9 | // tuple.zon:1:2: error: expected 3 vector elements; found 2 | |
| 10 | // tmp.zig:2:40: note: imported here |
test/cases/compile_errors/@import_zon_vec_too_many.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: @Vector(1, f32) = @import("zon/tuple.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/tuple.zon | |
| 8 | // | |
| 9 | // tuple.zon:1:2: error: expected 1 vector elements; found 2 | |
| 10 | // tmp.zig:2:40: note: imported here |
test/cases/compile_errors/@import_zon_vec_wrong_type.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: @Vector(2, bool) = @import("zon/tuple.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/tuple.zon | |
| 8 | // | |
| 9 | // tuple.zon:1:4: error: expected type 'bool' | |
| 10 | // tmp.zig:2:41: note: imported here |
test/cases/compile_errors/@import_zon_void.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const f: union { foo: void } = @import("zon/void.zon"); | |
| 3 | _ = f; | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // imports=zon/void.zon | |
| 8 | // | |
| 9 | // void.zon:1:11: error: void literals are not available in ZON | |
| 10 | // void.zon:1:11: note: void union payloads can be represented by enum literals |
test/cases/compile_errors/redundant_try.zig+2-2| ... | ... | @@ -44,9 +44,9 @@ comptime { |
| 44 | 44 | // |
| 45 | 45 | // :5:23: error: expected error union type, found 'comptime_int' |
| 46 | 46 | // :10:23: error: expected error union type, found '@TypeOf(.{})' |
| 47 | // :15:23: error: expected error union type, found 'tmp.test2__struct_493' | |
| 47 | // :15:23: error: expected error union type, found 'tmp.test2__struct_494' | |
| 48 | 48 | // :15:23: note: struct declared here |
| 49 | // :20:27: error: expected error union type, found 'tmp.test3__struct_495' | |
| 49 | // :20:27: error: expected error union type, found 'tmp.test3__struct_496' | |
| 50 | 50 | // :20:27: note: struct declared here |
| 51 | 51 | // :25:23: error: expected error union type, found 'struct { comptime *const [5:0]u8 = "hello" }' |
| 52 | 52 | // :31:13: error: expected error union type, found 'u32' |
test/cases/compile_errors/zon/addr_slice.zon created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | .{ | |
| 2 | .value = &.{ 1, 2, 3 }, | |
| 3 | } |
test/cases/compile_errors/zon/array.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ 'a', 'b', 'c' } |
test/cases/compile_errors/zon/char_32.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ' ' | |
| \ No newline at end of file |
test/cases/compile_errors/zon/desktop.ini created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | [LocalizedFileNames] | |
| 2 | invalid_zon_2.zig=@invalid_zon_2.zig,0 | |
| 3 | invalid_zon_1.zig=@invalid_zon_1.zig,0 |
test/cases/compile_errors/zon/doc_comment.zon created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | //! Doc comments aren't allowed in ZON | |
| 2 | .{} |
test/cases/compile_errors/zon/double_negation_float.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | --1.0 | |
| \ No newline at end of file |
test/cases/compile_errors/zon/double_negation_int.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | --1 | |
| \ No newline at end of file |
test/cases/compile_errors/zon/enum_embedded_null.zon created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | .{ | |
| 2 | .@"\x00", | |
| 3 | 10, | |
| 4 | } |
test/cases/compile_errors/zon/hello.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | "hello" |
test/cases/compile_errors/zon/inf.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | inf | |
| \ No newline at end of file |
test/cases/compile_errors/zon/int_32.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | 32 | |
| \ No newline at end of file |
test/cases/compile_errors/zon/int_neg_33.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | -33 | |
| \ No newline at end of file |
test/cases/compile_errors/zon/invalid_character.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | '\a' |
test/cases/compile_errors/zon/invalid_number.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | 368934881474191032a32 |
test/cases/compile_errors/zon/invalid_string.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | "\"\a\"" |
test/cases/compile_errors/zon/large_number.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | 36893488147419103232 |
test/cases/compile_errors/zon/leading_zero_in_integer.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | 0012 | |
| \ No newline at end of file |
test/cases/compile_errors/zon/nan.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | nan | |
| \ No newline at end of file |
test/cases/compile_errors/zon/neg_char.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | -'a' |
test/cases/compile_errors/zon/neg_inf.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | -inf | |
| \ No newline at end of file |
test/cases/compile_errors/zon/neg_nan.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | -nan |
test/cases/compile_errors/zon/negative_zero.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | -0 | |
| \ No newline at end of file |
test/cases/compile_errors/zon/simple_union.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ .a = 10 } |
test/cases/compile_errors/zon/struct.zon created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | .{ | |
| 2 | .boolean = true, | |
| 3 | .number = 123, | |
| 4 | } |
test/cases/compile_errors/zon/struct_dup_field.zon created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | .{ | |
| 2 | .name = 10, | |
| 3 | .name = 20, | |
| 4 | } |
test/cases/compile_errors/zon/syntax_error.zon created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | .{ | |
| 2 | .boolean = true | |
| 3 | .number = 123, | |
| 4 | } |
test/cases/compile_errors/zon/tuple.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ 1.5, 2 } |
test/cases/compile_errors/zon/type_decl.zon created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | .{ | |
| 2 | .foo = struct {}, | |
| 3 | } |
test/cases/compile_errors/zon/type_expr_array.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | [3]i32{1, 2, 3} | |
| \ No newline at end of file |
test/cases/compile_errors/zon/type_expr_fn.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | fn foo() void {} |
test/cases/compile_errors/zon/type_expr_struct.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | Vec2{ .x = 1.0, .y = 2.0 } | |
| \ No newline at end of file |
test/cases/compile_errors/zon/type_expr_tuple.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | Vec2{1.0, 2.0} | |
| \ No newline at end of file |
test/cases/compile_errors/zon/unescaped_newline.zon created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | "a | |
| 2 | b" | |
| \ No newline at end of file |
test/cases/compile_errors/zon/unknown_ident.zon created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | .{ | |
| 2 | .value = truefalse, | |
| 3 | } |
test/cases/compile_errors/zon/vec2.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ .x = 1.5, .y = 2 } |
test/cases/compile_errors/zon/void.zon created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | .{ .foo = {} } |
test/src/Cases.zig+40-4| ... | ... | @@ -90,6 +90,11 @@ pub const Case = struct { |
| 90 | 90 | link_libc: bool = false, |
| 91 | 91 | pic: ?bool = null, |
| 92 | 92 | pie: ?bool = null, |
| 93 | /// A list of imports to cache alongside the source file. | |
| 94 | imports: []const []const u8 = &.{}, | |
| 95 | /// Where to look for imports relative to the `cases_dir_path` given to | |
| 96 | /// `lower_to_build_steps`. If null, file imports will assert. | |
| 97 | import_path: ?[]const u8 = null, | |
| 93 | 98 | |
| 94 | 99 | deps: std.ArrayList(DepModule), |
| 95 | 100 | |
| ... | ... | @@ -413,6 +418,7 @@ fn addFromDirInner( |
| 413 | 418 | const pic = try manifest.getConfigForKeyAssertSingle("pic", ?bool); |
| 414 | 419 | const pie = try manifest.getConfigForKeyAssertSingle("pie", ?bool); |
| 415 | 420 | const emit_bin = try manifest.getConfigForKeyAssertSingle("emit_bin", bool); |
| 421 | const imports = try manifest.getConfigForKeyAlloc(ctx.arena, "imports", []const u8); | |
| 416 | 422 | |
| 417 | 423 | if (manifest.type == .translate_c) { |
| 418 | 424 | for (c_frontends) |c_frontend| { |
| ... | ... | @@ -470,7 +476,7 @@ fn addFromDirInner( |
| 470 | 476 | const next = ctx.cases.items.len; |
| 471 | 477 | try ctx.cases.append(.{ |
| 472 | 478 | .name = std.fs.path.stem(filename), |
| 473 | .target = resolved_target, | |
| 479 | .import_path = std.fs.path.dirname(filename), | |
| 474 | 480 | .backend = backend, |
| 475 | 481 | .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator), |
| 476 | 482 | .emit_bin = emit_bin, |
| ... | ... | @@ -480,6 +486,8 @@ fn addFromDirInner( |
| 480 | 486 | .pic = pic, |
| 481 | 487 | .pie = pie, |
| 482 | 488 | .deps = std.ArrayList(DepModule).init(ctx.cases.allocator), |
| 489 | .imports = imports, | |
| 490 | .target = resolved_target, | |
| 483 | 491 | }); |
| 484 | 492 | try cases.append(next); |
| 485 | 493 | } |
| ... | ... | @@ -619,6 +627,7 @@ pub fn lowerToBuildSteps( |
| 619 | 627 | ) void { |
| 620 | 628 | const host = std.zig.system.resolveTargetQuery(.{}) catch |err| |
| 621 | 629 | std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)}); |
| 630 | const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM"); | |
| 622 | 631 | |
| 623 | 632 | for (self.incremental_cases.items) |incr_case| { |
| 624 | 633 | if (true) { |
| ... | ... | @@ -662,11 +671,21 @@ pub fn lowerToBuildSteps( |
| 662 | 671 | file_sources.put(file.path, writefiles.add(file.path, file.src)) catch @panic("OOM"); |
| 663 | 672 | } |
| 664 | 673 | |
| 674 | for (case.imports) |import_rel| { | |
| 675 | const import_abs = std.fs.path.join(b.allocator, &.{ | |
| 676 | cases_dir_path, | |
| 677 | case.import_path orelse @panic("import_path not set"), | |
| 678 | import_rel, | |
| 679 | }) catch @panic("OOM"); | |
| 680 | _ = writefiles.addCopyFile(.{ .cwd_relative = import_abs }, import_rel); | |
| 681 | } | |
| 682 | ||
| 665 | 683 | const mod = b.createModule(.{ |
| 666 | 684 | .root_source_file = root_source_file, |
| 667 | 685 | .target = case.target, |
| 668 | 686 | .optimize = case.optimize_mode, |
| 669 | 687 | }); |
| 688 | ||
| 670 | 689 | if (case.link_libc) mod.link_libc = true; |
| 671 | 690 | if (case.pic) |pic| mod.pic = pic; |
| 672 | 691 | for (case.deps.items) |dep| { |
| ... | ... | @@ -962,6 +981,8 @@ const TestManifestConfigDefaults = struct { |
| 962 | 981 | return "null"; |
| 963 | 982 | } else if (std.mem.eql(u8, key, "pie")) { |
| 964 | 983 | return "null"; |
| 984 | } else if (std.mem.eql(u8, key, "imports")) { | |
| 985 | return ""; | |
| 965 | 986 | } else unreachable; |
| 966 | 987 | } |
| 967 | 988 | }; |
| ... | ... | @@ -998,6 +1019,7 @@ const TestManifest = struct { |
| 998 | 1019 | .{ "backend", {} }, |
| 999 | 1020 | .{ "pic", {} }, |
| 1000 | 1021 | .{ "pie", {} }, |
| 1022 | .{ "imports", {} }, | |
| 1001 | 1023 | }); |
| 1002 | 1024 | |
| 1003 | 1025 | const Type = enum { |
| ... | ... | @@ -1020,7 +1042,7 @@ const TestManifest = struct { |
| 1020 | 1042 | |
| 1021 | 1043 | fn ConfigValueIterator(comptime T: type) type { |
| 1022 | 1044 | return struct { |
| 1023 | inner: std.mem.SplitIterator(u8, .scalar), | |
| 1045 | inner: std.mem.TokenIterator(u8, .scalar), | |
| 1024 | 1046 | |
| 1025 | 1047 | fn next(self: *@This()) !?T { |
| 1026 | 1048 | const next_raw = self.inner.next() orelse return null; |
| ... | ... | @@ -1098,7 +1120,9 @@ const TestManifest = struct { |
| 1098 | 1120 | // Parse key=value(s) |
| 1099 | 1121 | var kv_it = std.mem.splitScalar(u8, trimmed, '='); |
| 1100 | 1122 | const key = kv_it.first(); |
| 1101 | if (!valid_keys.has(key)) return error.InvalidKey; | |
| 1123 | if (!valid_keys.has(key)) { | |
| 1124 | return error.InvalidKey; | |
| 1125 | } | |
| 1102 | 1126 | try manifest.config_map.putNoClobber(key, kv_it.next() orelse return error.MissingValuesForConfig); |
| 1103 | 1127 | } |
| 1104 | 1128 | |
| ... | ... | @@ -1115,7 +1139,7 @@ const TestManifest = struct { |
| 1115 | 1139 | ) ConfigValueIterator(T) { |
| 1116 | 1140 | const bytes = self.config_map.get(key) orelse TestManifestConfigDefaults.get(self.type, key); |
| 1117 | 1141 | return ConfigValueIterator(T){ |
| 1118 | .inner = std.mem.splitScalar(u8, bytes, ','), | |
| 1142 | .inner = std.mem.tokenizeScalar(u8, bytes, ','), | |
| 1119 | 1143 | }; |
| 1120 | 1144 | } |
| 1121 | 1145 | |
| ... | ... | @@ -1232,6 +1256,18 @@ const TestManifest = struct { |
| 1232 | 1256 | return try getDefaultParser(o.child)(str); |
| 1233 | 1257 | } |
| 1234 | 1258 | }.parse, |
| 1259 | .@"struct" => @compileError("no default parser for " ++ @typeName(T)), | |
| 1260 | .pointer => { | |
| 1261 | if (T == []const u8) { | |
| 1262 | return struct { | |
| 1263 | fn parse(str: []const u8) anyerror!T { | |
| 1264 | return str; | |
| 1265 | } | |
| 1266 | }.parse; | |
| 1267 | } else { | |
| 1268 | @compileError("no default parser for " ++ @typeName(T)); | |
| 1269 | } | |
| 1270 | }, | |
| 1235 | 1271 | else => @compileError("no default parser for " ++ @typeName(T)), |
| 1236 | 1272 | } |
| 1237 | 1273 | } |