| 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,7 +406,7 @@ pub fn build(b: *std.Build) !void { |
| 406 | const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index]; | 406 | const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index]; |
| 407 | 407 | ||
| 408 | const fmt_include_paths = &.{ "lib", "src", "test", "tools", "build.zig", "build.zig.zon" }; | 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 | const do_fmt = b.addFmt(.{ | 410 | const do_fmt = b.addFmt(.{ |
| 411 | .paths = fmt_include_paths, | 411 | .paths = fmt_include_paths, |
| 412 | .exclude_paths = fmt_exclude_paths, | 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,7 +473,7 @@ pub fn toInt(v: Value, comptime T: type, comp: *const Compilation) ?T { |
| 473 | if (comp.interner.get(v.ref()) != .int) return null; | 473 | if (comp.interner.get(v.ref()) != .int) return null; |
| 474 | var space: BigIntSpace = undefined; | 474 | var space: BigIntSpace = undefined; |
| 475 | const big_int = v.toBigInt(&space, comp); | 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 | const ComplexOp = enum { | 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,13 +628,13 @@ pub fn put(i: *Interner, gpa: Allocator, key: Key) !Ref { |
| 628 | if (data.fitsInTwosComp(.unsigned, 32)) { | 628 | if (data.fitsInTwosComp(.unsigned, 32)) { |
| 629 | i.items.appendAssumeCapacity(.{ | 629 | i.items.appendAssumeCapacity(.{ |
| 630 | .tag = .u32, | 630 | .tag = .u32, |
| 631 | .data = data.to(u32) catch unreachable, | 631 | .data = data.toInt(u32) catch unreachable, |
| 632 | }); | 632 | }); |
| 633 | break :int; | 633 | break :int; |
| 634 | } else if (data.fitsInTwosComp(.signed, 32)) { | 634 | } else if (data.fitsInTwosComp(.signed, 32)) { |
| 635 | i.items.appendAssumeCapacity(.{ | 635 | i.items.appendAssumeCapacity(.{ |
| 636 | .tag = .i32, | 636 | .tag = .i32, |
| 637 | .data = @bitCast(data.to(i32) catch unreachable), | 637 | .data = @bitCast(data.toInt(i32) catch unreachable), |
| 638 | }); | 638 | }); |
| 639 | break :int; | 639 | break :int; |
| 640 | } | 640 | } |
lib/std/math/big/int.zig+36-6| ... | @@ -2175,10 +2175,13 @@ pub const Const = struct { | ... | @@ -2175,10 +2175,13 @@ pub const Const = struct { |
| 2175 | TargetTooSmall, | 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 | /// Returns an error if self cannot be narrowed into the requested type without truncation. | 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 | switch (@typeInfo(T)) { | 2185 | switch (@typeInfo(T)) { |
| 2183 | .int => |info| { | 2186 | .int => |info| { |
| 2184 | // Make sure -0 is handled correctly. | 2187 | // Make sure -0 is handled correctly. |
| ... | @@ -2216,7 +2219,26 @@ pub const Const = struct { | ... | @@ -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,11 +2797,19 @@ pub const Managed = struct { |
| 2775 | 2797 | ||
| 2776 | pub const ConvertError = Const.ConvertError; | 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 | /// Returns an error if self cannot be narrowed into the requested type without truncation. | 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 { | 2806 | pub fn toInt(self: Managed, comptime T: type) ConvertError!T { |
| 2782 | return self.toConst().to(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 | /// Set self from the string representation `value`. | 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,21 +53,21 @@ test "comptime_int to" { |
| 53 | var a = try Managed.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab); | 53 | var a = try Managed.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab); |
| 54 | defer a.deinit(); | 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 | test "sub-limb to" { | 59 | test "sub-limb to" { |
| 60 | var a = try Managed.initSet(testing.allocator, 10); | 60 | var a = try Managed.initSet(testing.allocator, 10); |
| 61 | defer a.deinit(); | 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 | test "set negative minimum" { | 66 | test "set negative minimum" { |
| 67 | var a = try Managed.initSet(testing.allocator, @as(i64, minInt(i64))); | 67 | var a = try Managed.initSet(testing.allocator, @as(i64, minInt(i64))); |
| 68 | defer a.deinit(); | 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 | test "set double-width maximum then zero" { | 73 | test "set double-width maximum then zero" { |
| ... | @@ -75,14 +75,14 @@ test "set double-width maximum then zero" { | ... | @@ -75,14 +75,14 @@ test "set double-width maximum then zero" { |
| 75 | defer a.deinit(); | 75 | defer a.deinit(); |
| 76 | try a.set(@as(DoubleLimb, 0)); | 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 | test "to target too small error" { | 81 | test "to target too small error" { |
| 82 | var a = try Managed.initSet(testing.allocator, 0xffffffff); | 82 | var a = try Managed.initSet(testing.allocator, 0xffffffff); |
| 83 | defer a.deinit(); | 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 | test "normalize" { | 88 | test "normalize" { |
| ... | @@ -191,28 +191,28 @@ test "bitcount/to" { | ... | @@ -191,28 +191,28 @@ test "bitcount/to" { |
| 191 | try a.set(0); | 191 | try a.set(0); |
| 192 | try testing.expect(a.bitCountTwosComp() == 0); | 192 | try testing.expect(a.bitCountTwosComp() == 0); |
| 193 | 193 | ||
| 194 | try testing.expect((try a.to(u0)) == 0); | 194 | try testing.expect((try a.toInt(u0)) == 0); |
| 195 | try testing.expect((try a.to(i0)) == 0); | 195 | try testing.expect((try a.toInt(i0)) == 0); |
| 196 | 196 | ||
| 197 | try a.set(-1); | 197 | try a.set(-1); |
| 198 | try testing.expect(a.bitCountTwosComp() == 1); | 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 | try a.set(-8); | 201 | try a.set(-8); |
| 202 | try testing.expect(a.bitCountTwosComp() == 4); | 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 | try a.set(127); | 205 | try a.set(127); |
| 206 | try testing.expect(a.bitCountTwosComp() == 7); | 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 | try a.set(-128); | 209 | try a.set(-128); |
| 210 | try testing.expect(a.bitCountTwosComp() == 8); | 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 | try a.set(-129); | 213 | try a.set(-129); |
| 214 | try testing.expect(a.bitCountTwosComp() == 9); | 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 | test "fits" { | 218 | test "fits" { |
| ... | @@ -248,7 +248,7 @@ test "string set" { | ... | @@ -248,7 +248,7 @@ test "string set" { |
| 248 | defer a.deinit(); | 248 | defer a.deinit(); |
| 249 | 249 | ||
| 250 | try a.setString(10, "120317241209124781241290847124"); | 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 | test "string negative" { | 254 | test "string negative" { |
| ... | @@ -256,7 +256,7 @@ test "string negative" { | ... | @@ -256,7 +256,7 @@ test "string negative" { |
| 256 | defer a.deinit(); | 256 | defer a.deinit(); |
| 257 | 257 | ||
| 258 | try a.setString(10, "-1023"); | 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 | test "string set number with underscores" { | 262 | test "string set number with underscores" { |
| ... | @@ -264,7 +264,7 @@ test "string set number with underscores" { | ... | @@ -264,7 +264,7 @@ test "string set number with underscores" { |
| 264 | defer a.deinit(); | 264 | defer a.deinit(); |
| 265 | 265 | ||
| 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___"); | 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 | test "string set case insensitive number" { | 270 | test "string set case insensitive number" { |
| ... | @@ -272,7 +272,7 @@ test "string set case insensitive number" { | ... | @@ -272,7 +272,7 @@ test "string set case insensitive number" { |
| 272 | defer a.deinit(); | 272 | defer a.deinit(); |
| 273 | 273 | ||
| 274 | try a.setString(16, "aB_cD_eF"); | 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 | test "string set bad char error" { | 278 | test "string set bad char error" { |
| ... | @@ -306,11 +306,11 @@ fn testTwosComplementLimit(comptime T: type) !void { | ... | @@ -306,11 +306,11 @@ fn testTwosComplementLimit(comptime T: type) !void { |
| 306 | 306 | ||
| 307 | try a.setTwosCompIntLimit(.max, int_info.signedness, int_info.bits); | 307 | try a.setTwosCompIntLimit(.max, int_info.signedness, int_info.bits); |
| 308 | const max: T = maxInt(T); | 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 | try a.setTwosCompIntLimit(.min, int_info.signedness, int_info.bits); | 311 | try a.setTwosCompIntLimit(.min, int_info.signedness, int_info.bits); |
| 312 | const min: T = minInt(T); | 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 | test "string to" { | 316 | test "string to" { |
| ... | @@ -381,12 +381,12 @@ test "clone" { | ... | @@ -381,12 +381,12 @@ test "clone" { |
| 381 | var b = try a.clone(); | 381 | var b = try a.clone(); |
| 382 | defer b.deinit(); | 382 | defer b.deinit(); |
| 383 | 383 | ||
| 384 | try testing.expect((try a.to(u32)) == 1234); | 384 | try testing.expect((try a.toInt(u32)) == 1234); |
| 385 | try testing.expect((try b.to(u32)) == 1234); | 385 | try testing.expect((try b.toInt(u32)) == 1234); |
| 386 | 386 | ||
| 387 | try a.set(77); | 387 | try a.set(77); |
| 388 | try testing.expect((try a.to(u32)) == 77); | 388 | try testing.expect((try a.toInt(u32)) == 77); |
| 389 | try testing.expect((try b.to(u32)) == 1234); | 389 | try testing.expect((try b.toInt(u32)) == 1234); |
| 390 | } | 390 | } |
| 391 | 391 | ||
| 392 | test "swap" { | 392 | test "swap" { |
| ... | @@ -395,20 +395,20 @@ test "swap" { | ... | @@ -395,20 +395,20 @@ test "swap" { |
| 395 | var b = try Managed.initSet(testing.allocator, 5678); | 395 | var b = try Managed.initSet(testing.allocator, 5678); |
| 396 | defer b.deinit(); | 396 | defer b.deinit(); |
| 397 | 397 | ||
| 398 | try testing.expect((try a.to(u32)) == 1234); | 398 | try testing.expect((try a.toInt(u32)) == 1234); |
| 399 | try testing.expect((try b.to(u32)) == 5678); | 399 | try testing.expect((try b.toInt(u32)) == 5678); |
| 400 | 400 | ||
| 401 | a.swap(&b); | 401 | a.swap(&b); |
| 402 | 402 | ||
| 403 | try testing.expect((try a.to(u32)) == 5678); | 403 | try testing.expect((try a.toInt(u32)) == 5678); |
| 404 | try testing.expect((try b.to(u32)) == 1234); | 404 | try testing.expect((try b.toInt(u32)) == 1234); |
| 405 | } | 405 | } |
| 406 | 406 | ||
| 407 | test "to negative" { | 407 | test "to negative" { |
| 408 | var a = try Managed.initSet(testing.allocator, -10); | 408 | var a = try Managed.initSet(testing.allocator, -10); |
| 409 | defer a.deinit(); | 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 | test "compare" { | 414 | test "compare" { |
| ... | @@ -466,10 +466,10 @@ test "abs" { | ... | @@ -466,10 +466,10 @@ test "abs" { |
| 466 | defer a.deinit(); | 466 | defer a.deinit(); |
| 467 | 467 | ||
| 468 | a.abs(); | 468 | a.abs(); |
| 469 | try testing.expect((try a.to(u32)) == 5); | 469 | try testing.expect((try a.toInt(u32)) == 5); |
| 470 | 470 | ||
| 471 | a.abs(); | 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 | test "negate" { | 475 | test "negate" { |
| ... | @@ -477,10 +477,10 @@ test "negate" { | ... | @@ -477,10 +477,10 @@ test "negate" { |
| 477 | defer a.deinit(); | 477 | defer a.deinit(); |
| 478 | 478 | ||
| 479 | a.negate(); | 479 | a.negate(); |
| 480 | try testing.expect((try a.to(i32)) == -5); | 480 | try testing.expect((try a.toInt(i32)) == -5); |
| 481 | 481 | ||
| 482 | a.negate(); | 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 | test "add single-single" { | 486 | test "add single-single" { |
| ... | @@ -493,7 +493,7 @@ test "add single-single" { | ... | @@ -493,7 +493,7 @@ test "add single-single" { |
| 493 | defer c.deinit(); | 493 | defer c.deinit(); |
| 494 | try c.add(&a, &b); | 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 | test "add multi-single" { | 499 | test "add multi-single" { |
| ... | @@ -506,10 +506,10 @@ test "add multi-single" { | ... | @@ -506,10 +506,10 @@ test "add multi-single" { |
| 506 | defer c.deinit(); | 506 | defer c.deinit(); |
| 507 | 507 | ||
| 508 | try c.add(&a, &b); | 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 | try c.add(&b, &a); | 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 | test "add multi-multi" { | 515 | test "add multi-multi" { |
| ... | @@ -527,7 +527,7 @@ test "add multi-multi" { | ... | @@ -527,7 +527,7 @@ test "add multi-multi" { |
| 527 | defer c.deinit(); | 527 | defer c.deinit(); |
| 528 | try c.add(&a, &b); | 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 | test "add zero-zero" { | 533 | test "add zero-zero" { |
| ... | @@ -540,7 +540,7 @@ test "add zero-zero" { | ... | @@ -540,7 +540,7 @@ test "add zero-zero" { |
| 540 | defer c.deinit(); | 540 | defer c.deinit(); |
| 541 | try c.add(&a, &b); | 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 | test "add alias multi-limb nonzero-zero" { | 546 | test "add alias multi-limb nonzero-zero" { |
| ... | @@ -552,7 +552,7 @@ test "add alias multi-limb nonzero-zero" { | ... | @@ -552,7 +552,7 @@ test "add alias multi-limb nonzero-zero" { |
| 552 | 552 | ||
| 553 | try a.add(&a, &b); | 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 | test "add sign" { | 558 | test "add sign" { |
| ... | @@ -569,16 +569,16 @@ test "add sign" { | ... | @@ -569,16 +569,16 @@ test "add sign" { |
| 569 | defer neg_two.deinit(); | 569 | defer neg_two.deinit(); |
| 570 | 570 | ||
| 571 | try a.add(&one, &two); | 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 | try a.add(&neg_one, &two); | 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 | try a.add(&one, &neg_two); | 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 | try a.add(&neg_one, &neg_two); | 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 | test "add comptime scalar" { | 584 | test "add comptime scalar" { |
| ... | @@ -589,7 +589,7 @@ test "add comptime scalar" { | ... | @@ -589,7 +589,7 @@ test "add comptime scalar" { |
| 589 | defer b.deinit(); | 589 | defer b.deinit(); |
| 590 | try b.addScalar(&a, 5); | 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 | test "add scalar" { | 595 | test "add scalar" { |
| ... | @@ -600,7 +600,7 @@ test "add scalar" { | ... | @@ -600,7 +600,7 @@ test "add scalar" { |
| 600 | defer b.deinit(); | 600 | defer b.deinit(); |
| 601 | try b.addScalar(&a, @as(u32, 31)); | 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 | test "addWrap single-single, unsigned" { | 606 | test "addWrap single-single, unsigned" { |
| ... | @@ -613,7 +613,7 @@ test "addWrap single-single, unsigned" { | ... | @@ -613,7 +613,7 @@ test "addWrap single-single, unsigned" { |
| 613 | const wrapped = try a.addWrap(&a, &b, .unsigned, 17); | 613 | const wrapped = try a.addWrap(&a, &b, .unsigned, 17); |
| 614 | 614 | ||
| 615 | try testing.expect(wrapped); | 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 | test "subWrap single-single, unsigned" { | 619 | test "subWrap single-single, unsigned" { |
| ... | @@ -626,7 +626,7 @@ test "subWrap single-single, unsigned" { | ... | @@ -626,7 +626,7 @@ test "subWrap single-single, unsigned" { |
| 626 | const wrapped = try a.subWrap(&a, &b, .unsigned, 17); | 626 | const wrapped = try a.subWrap(&a, &b, .unsigned, 17); |
| 627 | 627 | ||
| 628 | try testing.expect(wrapped); | 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 | test "addWrap multi-multi, unsigned, limb aligned" { | 632 | test "addWrap multi-multi, unsigned, limb aligned" { |
| ... | @@ -639,7 +639,7 @@ test "addWrap multi-multi, unsigned, limb aligned" { | ... | @@ -639,7 +639,7 @@ test "addWrap multi-multi, unsigned, limb aligned" { |
| 639 | const wrapped = try a.addWrap(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); | 639 | const wrapped = try a.addWrap(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); |
| 640 | 640 | ||
| 641 | try testing.expect(wrapped); | 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 | test "subWrap single-multi, unsigned, limb aligned" { | 645 | test "subWrap single-multi, unsigned, limb aligned" { |
| ... | @@ -652,7 +652,7 @@ test "subWrap single-multi, unsigned, limb aligned" { | ... | @@ -652,7 +652,7 @@ test "subWrap single-multi, unsigned, limb aligned" { |
| 652 | const wrapped = try a.subWrap(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); | 652 | const wrapped = try a.subWrap(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); |
| 653 | 653 | ||
| 654 | try testing.expect(wrapped); | 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 | test "addWrap single-single, signed" { | 658 | test "addWrap single-single, signed" { |
| ... | @@ -665,7 +665,7 @@ test "addWrap single-single, signed" { | ... | @@ -665,7 +665,7 @@ test "addWrap single-single, signed" { |
| 665 | const wrapped = try a.addWrap(&a, &b, .signed, @bitSizeOf(i21)); | 665 | const wrapped = try a.addWrap(&a, &b, .signed, @bitSizeOf(i21)); |
| 666 | 666 | ||
| 667 | try testing.expect(wrapped); | 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 | test "subWrap single-single, signed" { | 671 | test "subWrap single-single, signed" { |
| ... | @@ -678,7 +678,7 @@ test "subWrap single-single, signed" { | ... | @@ -678,7 +678,7 @@ test "subWrap single-single, signed" { |
| 678 | const wrapped = try a.subWrap(&a, &b, .signed, @bitSizeOf(i21)); | 678 | const wrapped = try a.subWrap(&a, &b, .signed, @bitSizeOf(i21)); |
| 679 | 679 | ||
| 680 | try testing.expect(wrapped); | 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 | test "addWrap multi-multi, signed, limb aligned" { | 684 | test "addWrap multi-multi, signed, limb aligned" { |
| ... | @@ -691,7 +691,7 @@ test "addWrap multi-multi, signed, limb aligned" { | ... | @@ -691,7 +691,7 @@ test "addWrap multi-multi, signed, limb aligned" { |
| 691 | const wrapped = try a.addWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); | 691 | const wrapped = try a.addWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); |
| 692 | 692 | ||
| 693 | try testing.expect(wrapped); | 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 | test "subWrap single-multi, signed, limb aligned" { | 697 | test "subWrap single-multi, signed, limb aligned" { |
| ... | @@ -704,7 +704,7 @@ test "subWrap single-multi, signed, limb aligned" { | ... | @@ -704,7 +704,7 @@ test "subWrap single-multi, signed, limb aligned" { |
| 704 | const wrapped = try a.subWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); | 704 | const wrapped = try a.subWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); |
| 705 | 705 | ||
| 706 | try testing.expect(wrapped); | 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 | test "addSat single-single, unsigned" { | 710 | test "addSat single-single, unsigned" { |
| ... | @@ -716,7 +716,7 @@ test "addSat single-single, unsigned" { | ... | @@ -716,7 +716,7 @@ test "addSat single-single, unsigned" { |
| 716 | 716 | ||
| 717 | try a.addSat(&a, &b, .unsigned, 17); | 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 | test "subSat single-single, unsigned" { | 722 | test "subSat single-single, unsigned" { |
| ... | @@ -728,7 +728,7 @@ test "subSat single-single, unsigned" { | ... | @@ -728,7 +728,7 @@ test "subSat single-single, unsigned" { |
| 728 | 728 | ||
| 729 | try a.subSat(&a, &b, .unsigned, 17); | 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 | test "addSat multi-multi, unsigned, limb aligned" { | 734 | test "addSat multi-multi, unsigned, limb aligned" { |
| ... | @@ -740,7 +740,7 @@ test "addSat multi-multi, unsigned, limb aligned" { | ... | @@ -740,7 +740,7 @@ test "addSat multi-multi, unsigned, limb aligned" { |
| 740 | 740 | ||
| 741 | try a.addSat(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); | 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 | test "subSat single-multi, unsigned, limb aligned" { | 746 | test "subSat single-multi, unsigned, limb aligned" { |
| ... | @@ -752,7 +752,7 @@ test "subSat single-multi, unsigned, limb aligned" { | ... | @@ -752,7 +752,7 @@ test "subSat single-multi, unsigned, limb aligned" { |
| 752 | 752 | ||
| 753 | try a.subSat(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); | 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 | test "addSat single-single, signed" { | 758 | test "addSat single-single, signed" { |
| ... | @@ -764,7 +764,7 @@ test "addSat single-single, signed" { | ... | @@ -764,7 +764,7 @@ test "addSat single-single, signed" { |
| 764 | 764 | ||
| 765 | try a.addSat(&a, &b, .signed, @bitSizeOf(i14)); | 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 | test "subSat single-single, signed" { | 770 | test "subSat single-single, signed" { |
| ... | @@ -776,7 +776,7 @@ test "subSat single-single, signed" { | ... | @@ -776,7 +776,7 @@ test "subSat single-single, signed" { |
| 776 | 776 | ||
| 777 | try a.subSat(&a, &b, .signed, @bitSizeOf(i21)); | 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 | test "addSat multi-multi, signed, limb aligned" { | 782 | test "addSat multi-multi, signed, limb aligned" { |
| ... | @@ -788,7 +788,7 @@ test "addSat multi-multi, signed, limb aligned" { | ... | @@ -788,7 +788,7 @@ test "addSat multi-multi, signed, limb aligned" { |
| 788 | 788 | ||
| 789 | try a.addSat(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); | 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 | test "subSat single-multi, signed, limb aligned" { | 794 | test "subSat single-multi, signed, limb aligned" { |
| ... | @@ -800,7 +800,7 @@ test "subSat single-multi, signed, limb aligned" { | ... | @@ -800,7 +800,7 @@ test "subSat single-multi, signed, limb aligned" { |
| 800 | 800 | ||
| 801 | try a.subSat(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); | 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 | test "sub single-single" { | 806 | test "sub single-single" { |
| ... | @@ -813,7 +813,7 @@ test "sub single-single" { | ... | @@ -813,7 +813,7 @@ test "sub single-single" { |
| 813 | defer c.deinit(); | 813 | defer c.deinit(); |
| 814 | try c.sub(&a, &b); | 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 | test "sub multi-single" { | 819 | test "sub multi-single" { |
| ... | @@ -826,7 +826,7 @@ test "sub multi-single" { | ... | @@ -826,7 +826,7 @@ test "sub multi-single" { |
| 826 | defer c.deinit(); | 826 | defer c.deinit(); |
| 827 | try c.sub(&a, &b); | 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 | test "sub multi-multi" { | 832 | test "sub multi-multi" { |
| ... | @@ -843,7 +843,7 @@ test "sub multi-multi" { | ... | @@ -843,7 +843,7 @@ test "sub multi-multi" { |
| 843 | defer c.deinit(); | 843 | defer c.deinit(); |
| 844 | try c.sub(&a, &b); | 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 | test "sub equal" { | 849 | test "sub equal" { |
| ... | @@ -856,7 +856,7 @@ test "sub equal" { | ... | @@ -856,7 +856,7 @@ test "sub equal" { |
| 856 | defer c.deinit(); | 856 | defer c.deinit(); |
| 857 | try c.sub(&a, &b); | 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 | test "sub sign" { | 862 | test "sub sign" { |
| ... | @@ -873,19 +873,19 @@ test "sub sign" { | ... | @@ -873,19 +873,19 @@ test "sub sign" { |
| 873 | defer neg_two.deinit(); | 873 | defer neg_two.deinit(); |
| 874 | 874 | ||
| 875 | try a.sub(&one, &two); | 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 | try a.sub(&neg_one, &two); | 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 | try a.sub(&one, &neg_two); | 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 | try a.sub(&neg_one, &neg_two); | 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 | try a.sub(&neg_two, &neg_one); | 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 | test "mul single-single" { | 891 | test "mul single-single" { |
| ... | @@ -898,7 +898,7 @@ test "mul single-single" { | ... | @@ -898,7 +898,7 @@ test "mul single-single" { |
| 898 | defer c.deinit(); | 898 | defer c.deinit(); |
| 899 | try c.mul(&a, &b); | 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 | test "mul multi-single" { | 904 | test "mul multi-single" { |
| ... | @@ -911,7 +911,7 @@ test "mul multi-single" { | ... | @@ -911,7 +911,7 @@ test "mul multi-single" { |
| 911 | defer c.deinit(); | 911 | defer c.deinit(); |
| 912 | try c.mul(&a, &b); | 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 | test "mul multi-multi" { | 917 | test "mul multi-multi" { |
| ... | @@ -930,7 +930,7 @@ test "mul multi-multi" { | ... | @@ -930,7 +930,7 @@ test "mul multi-multi" { |
| 930 | defer c.deinit(); | 930 | defer c.deinit(); |
| 931 | try c.mul(&a, &b); | 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 | test "mul alias r with a" { | 936 | test "mul alias r with a" { |
| ... | @@ -941,7 +941,7 @@ test "mul alias r with a" { | ... | @@ -941,7 +941,7 @@ test "mul alias r with a" { |
| 941 | 941 | ||
| 942 | try a.mul(&a, &b); | 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 | test "mul alias r with b" { | 947 | test "mul alias r with b" { |
| ... | @@ -952,7 +952,7 @@ test "mul alias r with b" { | ... | @@ -952,7 +952,7 @@ test "mul alias r with b" { |
| 952 | 952 | ||
| 953 | try a.mul(&b, &a); | 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 | test "mul alias r with a and b" { | 958 | test "mul alias r with a and b" { |
| ... | @@ -961,7 +961,7 @@ test "mul alias r with a and b" { | ... | @@ -961,7 +961,7 @@ test "mul alias r with a and b" { |
| 961 | 961 | ||
| 962 | try a.mul(&a, &a); | 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 | test "mul a*0" { | 967 | test "mul a*0" { |
| ... | @@ -974,7 +974,7 @@ test "mul a*0" { | ... | @@ -974,7 +974,7 @@ test "mul a*0" { |
| 974 | defer c.deinit(); | 974 | defer c.deinit(); |
| 975 | try c.mul(&a, &b); | 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 | test "mul 0*0" { | 980 | test "mul 0*0" { |
| ... | @@ -987,7 +987,7 @@ test "mul 0*0" { | ... | @@ -987,7 +987,7 @@ test "mul 0*0" { |
| 987 | defer c.deinit(); | 987 | defer c.deinit(); |
| 988 | try c.mul(&a, &b); | 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 | test "mul large" { | 993 | test "mul large" { |
| ... | @@ -1021,7 +1021,7 @@ test "mulWrap single-single unsigned" { | ... | @@ -1021,7 +1021,7 @@ test "mulWrap single-single unsigned" { |
| 1021 | defer c.deinit(); | 1021 | defer c.deinit(); |
| 1022 | try c.mulWrap(&a, &b, .unsigned, 17); | 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 | test "mulWrap single-single signed" { | 1027 | test "mulWrap single-single signed" { |
| ... | @@ -1034,7 +1034,7 @@ test "mulWrap single-single signed" { | ... | @@ -1034,7 +1034,7 @@ test "mulWrap single-single signed" { |
| 1034 | defer c.deinit(); | 1034 | defer c.deinit(); |
| 1035 | try c.mulWrap(&a, &b, .signed, 17); | 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 | test "mulWrap multi-multi unsigned" { | 1040 | test "mulWrap multi-multi unsigned" { |
| ... | @@ -1053,7 +1053,7 @@ test "mulWrap multi-multi unsigned" { | ... | @@ -1053,7 +1053,7 @@ test "mulWrap multi-multi unsigned" { |
| 1053 | defer c.deinit(); | 1053 | defer c.deinit(); |
| 1054 | try c.mulWrap(&a, &b, .unsigned, 65); | 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 | test "mulWrap multi-multi signed" { | 1059 | test "mulWrap multi-multi signed" { |
| ... | @@ -1071,7 +1071,7 @@ test "mulWrap multi-multi signed" { | ... | @@ -1071,7 +1071,7 @@ test "mulWrap multi-multi signed" { |
| 1071 | defer c.deinit(); | 1071 | defer c.deinit(); |
| 1072 | try c.mulWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); | 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 | test "mulWrap large" { | 1077 | test "mulWrap large" { |
| ... | @@ -1110,8 +1110,8 @@ test "div single-half no rem" { | ... | @@ -1110,8 +1110,8 @@ test "div single-half no rem" { |
| 1110 | defer r.deinit(); | 1110 | defer r.deinit(); |
| 1111 | try Managed.divTrunc(&q, &r, &a, &b); | 1111 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1112 | 1112 | ||
| 1113 | try testing.expect((try q.to(u32)) == 10); | 1113 | try testing.expect((try q.toInt(u32)) == 10); |
| 1114 | try testing.expect((try r.to(u32)) == 0); | 1114 | try testing.expect((try r.toInt(u32)) == 0); |
| 1115 | } | 1115 | } |
| 1116 | 1116 | ||
| 1117 | test "div single-half with rem" { | 1117 | test "div single-half with rem" { |
| ... | @@ -1126,8 +1126,8 @@ test "div single-half with rem" { | ... | @@ -1126,8 +1126,8 @@ test "div single-half with rem" { |
| 1126 | defer r.deinit(); | 1126 | defer r.deinit(); |
| 1127 | try Managed.divTrunc(&q, &r, &a, &b); | 1127 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1128 | 1128 | ||
| 1129 | try testing.expect((try q.to(u32)) == 9); | 1129 | try testing.expect((try q.toInt(u32)) == 9); |
| 1130 | try testing.expect((try r.to(u32)) == 4); | 1130 | try testing.expect((try r.toInt(u32)) == 4); |
| 1131 | } | 1131 | } |
| 1132 | 1132 | ||
| 1133 | test "div single-single no rem" { | 1133 | test "div single-single no rem" { |
| ... | @@ -1143,8 +1143,8 @@ test "div single-single no rem" { | ... | @@ -1143,8 +1143,8 @@ test "div single-single no rem" { |
| 1143 | defer r.deinit(); | 1143 | defer r.deinit(); |
| 1144 | try Managed.divTrunc(&q, &r, &a, &b); | 1144 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1145 | 1145 | ||
| 1146 | try testing.expect((try q.to(u32)) == 131072); | 1146 | try testing.expect((try q.toInt(u32)) == 131072); |
| 1147 | try testing.expect((try r.to(u32)) == 0); | 1147 | try testing.expect((try r.toInt(u32)) == 0); |
| 1148 | } | 1148 | } |
| 1149 | 1149 | ||
| 1150 | test "div single-single with rem" { | 1150 | test "div single-single with rem" { |
| ... | @@ -1159,8 +1159,8 @@ test "div single-single with rem" { | ... | @@ -1159,8 +1159,8 @@ test "div single-single with rem" { |
| 1159 | defer r.deinit(); | 1159 | defer r.deinit(); |
| 1160 | try Managed.divTrunc(&q, &r, &a, &b); | 1160 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1161 | 1161 | ||
| 1162 | try testing.expect((try q.to(u64)) == 131072); | 1162 | try testing.expect((try q.toInt(u64)) == 131072); |
| 1163 | try testing.expect((try r.to(u64)) == 8589934592); | 1163 | try testing.expect((try r.toInt(u64)) == 8589934592); |
| 1164 | } | 1164 | } |
| 1165 | 1165 | ||
| 1166 | test "div multi-single no rem" { | 1166 | test "div multi-single no rem" { |
| ... | @@ -1179,8 +1179,8 @@ test "div multi-single no rem" { | ... | @@ -1179,8 +1179,8 @@ test "div multi-single no rem" { |
| 1179 | defer r.deinit(); | 1179 | defer r.deinit(); |
| 1180 | try Managed.divTrunc(&q, &r, &a, &b); | 1180 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1181 | 1181 | ||
| 1182 | try testing.expect((try q.to(u64)) == op1 / op2); | 1182 | try testing.expect((try q.toInt(u64)) == op1 / op2); |
| 1183 | try testing.expect((try r.to(u64)) == 0); | 1183 | try testing.expect((try r.toInt(u64)) == 0); |
| 1184 | } | 1184 | } |
| 1185 | 1185 | ||
| 1186 | test "div multi-single with rem" { | 1186 | test "div multi-single with rem" { |
| ... | @@ -1199,8 +1199,8 @@ test "div multi-single with rem" { | ... | @@ -1199,8 +1199,8 @@ test "div multi-single with rem" { |
| 1199 | defer r.deinit(); | 1199 | defer r.deinit(); |
| 1200 | try Managed.divTrunc(&q, &r, &a, &b); | 1200 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1201 | 1201 | ||
| 1202 | try testing.expect((try q.to(u64)) == op1 / op2); | 1202 | try testing.expect((try q.toInt(u64)) == op1 / op2); |
| 1203 | try testing.expect((try r.to(u64)) == 3); | 1203 | try testing.expect((try r.toInt(u64)) == 3); |
| 1204 | } | 1204 | } |
| 1205 | 1205 | ||
| 1206 | test "div multi>2-single" { | 1206 | test "div multi>2-single" { |
| ... | @@ -1219,8 +1219,8 @@ test "div multi>2-single" { | ... | @@ -1219,8 +1219,8 @@ test "div multi>2-single" { |
| 1219 | defer r.deinit(); | 1219 | defer r.deinit(); |
| 1220 | try Managed.divTrunc(&q, &r, &a, &b); | 1220 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1221 | 1221 | ||
| 1222 | try testing.expect((try q.to(u128)) == op1 / op2); | 1222 | try testing.expect((try q.toInt(u128)) == op1 / op2); |
| 1223 | try testing.expect((try r.to(u32)) == 0x3e4e); | 1223 | try testing.expect((try r.toInt(u32)) == 0x3e4e); |
| 1224 | } | 1224 | } |
| 1225 | 1225 | ||
| 1226 | test "div single-single q < r" { | 1226 | test "div single-single q < r" { |
| ... | @@ -1235,8 +1235,8 @@ test "div single-single q < r" { | ... | @@ -1235,8 +1235,8 @@ test "div single-single q < r" { |
| 1235 | defer r.deinit(); | 1235 | defer r.deinit(); |
| 1236 | try Managed.divTrunc(&q, &r, &a, &b); | 1236 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1237 | 1237 | ||
| 1238 | try testing.expect((try q.to(u64)) == 0); | 1238 | try testing.expect((try q.toInt(u64)) == 0); |
| 1239 | try testing.expect((try r.to(u64)) == 0x0078f432); | 1239 | try testing.expect((try r.toInt(u64)) == 0x0078f432); |
| 1240 | } | 1240 | } |
| 1241 | 1241 | ||
| 1242 | test "div single-single q == r" { | 1242 | test "div single-single q == r" { |
| ... | @@ -1251,8 +1251,8 @@ test "div single-single q == r" { | ... | @@ -1251,8 +1251,8 @@ test "div single-single q == r" { |
| 1251 | defer r.deinit(); | 1251 | defer r.deinit(); |
| 1252 | try Managed.divTrunc(&q, &r, &a, &b); | 1252 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1253 | 1253 | ||
| 1254 | try testing.expect((try q.to(u64)) == 1); | 1254 | try testing.expect((try q.toInt(u64)) == 1); |
| 1255 | try testing.expect((try r.to(u64)) == 0); | 1255 | try testing.expect((try r.toInt(u64)) == 0); |
| 1256 | } | 1256 | } |
| 1257 | 1257 | ||
| 1258 | test "div q=0 alias" { | 1258 | test "div q=0 alias" { |
| ... | @@ -1263,8 +1263,8 @@ test "div q=0 alias" { | ... | @@ -1263,8 +1263,8 @@ test "div q=0 alias" { |
| 1263 | 1263 | ||
| 1264 | try Managed.divTrunc(&a, &b, &a, &b); | 1264 | try Managed.divTrunc(&a, &b, &a, &b); |
| 1265 | 1265 | ||
| 1266 | try testing.expect((try a.to(u64)) == 0); | 1266 | try testing.expect((try a.toInt(u64)) == 0); |
| 1267 | try testing.expect((try b.to(u64)) == 3); | 1267 | try testing.expect((try b.toInt(u64)) == 3); |
| 1268 | } | 1268 | } |
| 1269 | 1269 | ||
| 1270 | test "div multi-multi q < r" { | 1270 | test "div multi-multi q < r" { |
| ... | @@ -1283,8 +1283,8 @@ test "div multi-multi q < r" { | ... | @@ -1283,8 +1283,8 @@ test "div multi-multi q < r" { |
| 1283 | defer r.deinit(); | 1283 | defer r.deinit(); |
| 1284 | try Managed.divTrunc(&q, &r, &a, &b); | 1284 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1285 | 1285 | ||
| 1286 | try testing.expect((try q.to(u128)) == 0); | 1286 | try testing.expect((try q.toInt(u128)) == 0); |
| 1287 | try testing.expect((try r.to(u128)) == op1); | 1287 | try testing.expect((try r.toInt(u128)) == op1); |
| 1288 | } | 1288 | } |
| 1289 | 1289 | ||
| 1290 | test "div trunc single-single +/+" { | 1290 | test "div trunc single-single +/+" { |
| ... | @@ -1307,8 +1307,8 @@ test "div trunc single-single +/+" { | ... | @@ -1307,8 +1307,8 @@ test "div trunc single-single +/+" { |
| 1307 | const eq = @divTrunc(u, v); | 1307 | const eq = @divTrunc(u, v); |
| 1308 | const er = @mod(u, v); | 1308 | const er = @mod(u, v); |
| 1309 | 1309 | ||
| 1310 | try testing.expect((try q.to(i32)) == eq); | 1310 | try testing.expect((try q.toInt(i32)) == eq); |
| 1311 | try testing.expect((try r.to(i32)) == er); | 1311 | try testing.expect((try r.toInt(i32)) == er); |
| 1312 | } | 1312 | } |
| 1313 | 1313 | ||
| 1314 | test "div trunc single-single -/+" { | 1314 | test "div trunc single-single -/+" { |
| ... | @@ -1331,8 +1331,8 @@ test "div trunc single-single -/+" { | ... | @@ -1331,8 +1331,8 @@ test "div trunc single-single -/+" { |
| 1331 | const eq = -1; | 1331 | const eq = -1; |
| 1332 | const er = -2; | 1332 | const er = -2; |
| 1333 | 1333 | ||
| 1334 | try testing.expect((try q.to(i32)) == eq); | 1334 | try testing.expect((try q.toInt(i32)) == eq); |
| 1335 | try testing.expect((try r.to(i32)) == er); | 1335 | try testing.expect((try r.toInt(i32)) == er); |
| 1336 | } | 1336 | } |
| 1337 | 1337 | ||
| 1338 | test "div trunc single-single +/-" { | 1338 | test "div trunc single-single +/-" { |
| ... | @@ -1355,8 +1355,8 @@ test "div trunc single-single +/-" { | ... | @@ -1355,8 +1355,8 @@ test "div trunc single-single +/-" { |
| 1355 | const eq = -1; | 1355 | const eq = -1; |
| 1356 | const er = 2; | 1356 | const er = 2; |
| 1357 | 1357 | ||
| 1358 | try testing.expect((try q.to(i32)) == eq); | 1358 | try testing.expect((try q.toInt(i32)) == eq); |
| 1359 | try testing.expect((try r.to(i32)) == er); | 1359 | try testing.expect((try r.toInt(i32)) == er); |
| 1360 | } | 1360 | } |
| 1361 | 1361 | ||
| 1362 | test "div trunc single-single -/-" { | 1362 | test "div trunc single-single -/-" { |
| ... | @@ -1379,8 +1379,8 @@ test "div trunc single-single -/-" { | ... | @@ -1379,8 +1379,8 @@ test "div trunc single-single -/-" { |
| 1379 | const eq = 1; | 1379 | const eq = 1; |
| 1380 | const er = -2; | 1380 | const er = -2; |
| 1381 | 1381 | ||
| 1382 | try testing.expect((try q.to(i32)) == eq); | 1382 | try testing.expect((try q.toInt(i32)) == eq); |
| 1383 | try testing.expect((try r.to(i32)) == er); | 1383 | try testing.expect((try r.toInt(i32)) == er); |
| 1384 | } | 1384 | } |
| 1385 | 1385 | ||
| 1386 | test "divTrunc #15535" { | 1386 | test "divTrunc #15535" { |
| ... | @@ -1417,7 +1417,7 @@ test "divFloor #10932" { | ... | @@ -1417,7 +1417,7 @@ test "divFloor #10932" { |
| 1417 | const ress = try res.toString(testing.allocator, 16, .lower); | 1417 | const ress = try res.toString(testing.allocator, 16, .lower); |
| 1418 | defer testing.allocator.free(ress); | 1418 | defer testing.allocator.free(ress); |
| 1419 | try testing.expect(std.mem.eql(u8, ress, "194bd136316c046d070b763396297bf8869a605030216b52597015902a172b2a752f62af1568dcd431602f03725bfa62b0be71ae86616210972c0126e173503011ca48c5747ff066d159c95e46b69cbb14c8fc0bd2bf0919f921be96463200000000000000000000000000000000000000000000000000000000000000000000000000000000")); | 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 | test "divFloor #11166" { | 1423 | test "divFloor #11166" { |
| ... | @@ -1482,7 +1482,7 @@ test "bitAnd #10932" { | ... | @@ -1482,7 +1482,7 @@ test "bitAnd #10932" { |
| 1482 | 1482 | ||
| 1483 | try res.bitAnd(&a, &b); | 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 | test "bit And #19235" { | 1488 | test "bit And #19235" { |
| ... | @@ -1495,7 +1495,7 @@ test "bit And #19235" { | ... | @@ -1495,7 +1495,7 @@ test "bit And #19235" { |
| 1495 | 1495 | ||
| 1496 | try r.bitAnd(&a, &b); | 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 | test "div floor single-single +/+" { | 1501 | test "div floor single-single +/+" { |
| ... | @@ -1518,8 +1518,8 @@ test "div floor single-single +/+" { | ... | @@ -1518,8 +1518,8 @@ test "div floor single-single +/+" { |
| 1518 | const eq = 1; | 1518 | const eq = 1; |
| 1519 | const er = 2; | 1519 | const er = 2; |
| 1520 | 1520 | ||
| 1521 | try testing.expect((try q.to(i32)) == eq); | 1521 | try testing.expect((try q.toInt(i32)) == eq); |
| 1522 | try testing.expect((try r.to(i32)) == er); | 1522 | try testing.expect((try r.toInt(i32)) == er); |
| 1523 | } | 1523 | } |
| 1524 | 1524 | ||
| 1525 | test "div floor single-single -/+" { | 1525 | test "div floor single-single -/+" { |
| ... | @@ -1542,8 +1542,8 @@ test "div floor single-single -/+" { | ... | @@ -1542,8 +1542,8 @@ test "div floor single-single -/+" { |
| 1542 | const eq = -2; | 1542 | const eq = -2; |
| 1543 | const er = 1; | 1543 | const er = 1; |
| 1544 | 1544 | ||
| 1545 | try testing.expect((try q.to(i32)) == eq); | 1545 | try testing.expect((try q.toInt(i32)) == eq); |
| 1546 | try testing.expect((try r.to(i32)) == er); | 1546 | try testing.expect((try r.toInt(i32)) == er); |
| 1547 | } | 1547 | } |
| 1548 | 1548 | ||
| 1549 | test "div floor single-single +/-" { | 1549 | test "div floor single-single +/-" { |
| ... | @@ -1566,8 +1566,8 @@ test "div floor single-single +/-" { | ... | @@ -1566,8 +1566,8 @@ test "div floor single-single +/-" { |
| 1566 | const eq = -2; | 1566 | const eq = -2; |
| 1567 | const er = -1; | 1567 | const er = -1; |
| 1568 | 1568 | ||
| 1569 | try testing.expect((try q.to(i32)) == eq); | 1569 | try testing.expect((try q.toInt(i32)) == eq); |
| 1570 | try testing.expect((try r.to(i32)) == er); | 1570 | try testing.expect((try r.toInt(i32)) == er); |
| 1571 | } | 1571 | } |
| 1572 | 1572 | ||
| 1573 | test "div floor single-single -/-" { | 1573 | test "div floor single-single -/-" { |
| ... | @@ -1590,8 +1590,8 @@ test "div floor single-single -/-" { | ... | @@ -1590,8 +1590,8 @@ test "div floor single-single -/-" { |
| 1590 | const eq = 1; | 1590 | const eq = 1; |
| 1591 | const er = -2; | 1591 | const er = -2; |
| 1592 | 1592 | ||
| 1593 | try testing.expect((try q.to(i32)) == eq); | 1593 | try testing.expect((try q.toInt(i32)) == eq); |
| 1594 | try testing.expect((try r.to(i32)) == er); | 1594 | try testing.expect((try r.toInt(i32)) == er); |
| 1595 | } | 1595 | } |
| 1596 | 1596 | ||
| 1597 | test "div floor no remainder negative quotient" { | 1597 | test "div floor no remainder negative quotient" { |
| ... | @@ -1609,8 +1609,8 @@ test "div floor no remainder negative quotient" { | ... | @@ -1609,8 +1609,8 @@ test "div floor no remainder negative quotient" { |
| 1609 | defer r.deinit(); | 1609 | defer r.deinit(); |
| 1610 | try Managed.divFloor(&q, &r, &a, &b); | 1610 | try Managed.divFloor(&q, &r, &a, &b); |
| 1611 | 1611 | ||
| 1612 | try testing.expect((try q.to(i32)) == -0x80000000); | 1612 | try testing.expect((try q.toInt(i32)) == -0x80000000); |
| 1613 | try testing.expect((try r.to(i32)) == 0); | 1613 | try testing.expect((try r.toInt(i32)) == 0); |
| 1614 | } | 1614 | } |
| 1615 | 1615 | ||
| 1616 | test "div floor negative close to zero" { | 1616 | test "div floor negative close to zero" { |
| ... | @@ -1628,8 +1628,8 @@ test "div floor negative close to zero" { | ... | @@ -1628,8 +1628,8 @@ test "div floor negative close to zero" { |
| 1628 | defer r.deinit(); | 1628 | defer r.deinit(); |
| 1629 | try Managed.divFloor(&q, &r, &a, &b); | 1629 | try Managed.divFloor(&q, &r, &a, &b); |
| 1630 | 1630 | ||
| 1631 | try testing.expect((try q.to(i32)) == -1); | 1631 | try testing.expect((try q.toInt(i32)) == -1); |
| 1632 | try testing.expect((try r.to(i32)) == 10); | 1632 | try testing.expect((try r.toInt(i32)) == 10); |
| 1633 | } | 1633 | } |
| 1634 | 1634 | ||
| 1635 | test "div floor positive close to zero" { | 1635 | test "div floor positive close to zero" { |
| ... | @@ -1647,8 +1647,8 @@ test "div floor positive close to zero" { | ... | @@ -1647,8 +1647,8 @@ test "div floor positive close to zero" { |
| 1647 | defer r.deinit(); | 1647 | defer r.deinit(); |
| 1648 | try Managed.divFloor(&q, &r, &a, &b); | 1648 | try Managed.divFloor(&q, &r, &a, &b); |
| 1649 | 1649 | ||
| 1650 | try testing.expect((try q.to(i32)) == 0); | 1650 | try testing.expect((try q.toInt(i32)) == 0); |
| 1651 | try testing.expect((try r.to(i32)) == 10); | 1651 | try testing.expect((try r.toInt(i32)) == 10); |
| 1652 | } | 1652 | } |
| 1653 | 1653 | ||
| 1654 | test "div multi-multi with rem" { | 1654 | test "div multi-multi with rem" { |
| ... | @@ -1665,8 +1665,8 @@ test "div multi-multi with rem" { | ... | @@ -1665,8 +1665,8 @@ test "div multi-multi with rem" { |
| 1665 | defer r.deinit(); | 1665 | defer r.deinit(); |
| 1666 | try Managed.divTrunc(&q, &r, &a, &b); | 1666 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1667 | 1667 | ||
| 1668 | try testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); | 1668 | try testing.expect((try q.toInt(u128)) == 0xe38f38e39161aaabd03f0f1b); |
| 1669 | try testing.expect((try r.to(u128)) == 0x28de0acacd806823638); | 1669 | try testing.expect((try r.toInt(u128)) == 0x28de0acacd806823638); |
| 1670 | } | 1670 | } |
| 1671 | 1671 | ||
| 1672 | test "div multi-multi no rem" { | 1672 | test "div multi-multi no rem" { |
| ... | @@ -1683,8 +1683,8 @@ test "div multi-multi no rem" { | ... | @@ -1683,8 +1683,8 @@ test "div multi-multi no rem" { |
| 1683 | defer r.deinit(); | 1683 | defer r.deinit(); |
| 1684 | try Managed.divTrunc(&q, &r, &a, &b); | 1684 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1685 | 1685 | ||
| 1686 | try testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); | 1686 | try testing.expect((try q.toInt(u128)) == 0xe38f38e39161aaabd03f0f1b); |
| 1687 | try testing.expect((try r.to(u128)) == 0); | 1687 | try testing.expect((try r.toInt(u128)) == 0); |
| 1688 | } | 1688 | } |
| 1689 | 1689 | ||
| 1690 | test "div multi-multi (2 branch)" { | 1690 | test "div multi-multi (2 branch)" { |
| ... | @@ -1701,8 +1701,8 @@ test "div multi-multi (2 branch)" { | ... | @@ -1701,8 +1701,8 @@ test "div multi-multi (2 branch)" { |
| 1701 | defer r.deinit(); | 1701 | defer r.deinit(); |
| 1702 | try Managed.divTrunc(&q, &r, &a, &b); | 1702 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1703 | 1703 | ||
| 1704 | try testing.expect((try q.to(u128)) == 0x10000000000000000); | 1704 | try testing.expect((try q.toInt(u128)) == 0x10000000000000000); |
| 1705 | try testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111); | 1705 | try testing.expect((try r.toInt(u128)) == 0x44444443444444431111111111111111); |
| 1706 | } | 1706 | } |
| 1707 | 1707 | ||
| 1708 | test "div multi-multi (3.1/3.3 branch)" { | 1708 | test "div multi-multi (3.1/3.3 branch)" { |
| ... | @@ -1719,8 +1719,8 @@ test "div multi-multi (3.1/3.3 branch)" { | ... | @@ -1719,8 +1719,8 @@ test "div multi-multi (3.1/3.3 branch)" { |
| 1719 | defer r.deinit(); | 1719 | defer r.deinit(); |
| 1720 | try Managed.divTrunc(&q, &r, &a, &b); | 1720 | try Managed.divTrunc(&q, &r, &a, &b); |
| 1721 | 1721 | ||
| 1722 | try testing.expect((try q.to(u128)) == 0xfffffffffffffffffff); | 1722 | try testing.expect((try q.toInt(u128)) == 0xfffffffffffffffffff); |
| 1723 | try testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282); | 1723 | try testing.expect((try r.toInt(u256)) == 0x1111111111111111111110b12222222222222222282); |
| 1724 | } | 1724 | } |
| 1725 | 1725 | ||
| 1726 | test "div multi-single zero-limb trailing" { | 1726 | test "div multi-single zero-limb trailing" { |
| ... | @@ -1757,7 +1757,7 @@ test "div multi-multi zero-limb trailing (with rem)" { | ... | @@ -1757,7 +1757,7 @@ test "div multi-multi zero-limb trailing (with rem)" { |
| 1757 | defer r.deinit(); | 1757 | defer r.deinit(); |
| 1758 | try Managed.divTrunc(&q, &r, &a, &b); | 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 | const rs = try r.toString(testing.allocator, 16, .lower); | 1762 | const rs = try r.toString(testing.allocator, 16, .lower); |
| 1763 | defer testing.allocator.free(rs); | 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,7 +1778,7 @@ test "div multi-multi zero-limb trailing (with rem) and dividend zero-limb count |
| 1778 | defer r.deinit(); | 1778 | defer r.deinit(); |
| 1779 | try Managed.divTrunc(&q, &r, &a, &b); | 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 | const rs = try r.toString(testing.allocator, 16, .lower); | 1783 | const rs = try r.toString(testing.allocator, 16, .lower); |
| 1784 | defer testing.allocator.free(rs); | 1784 | defer testing.allocator.free(rs); |
| ... | @@ -1862,7 +1862,7 @@ test "truncate single unsigned" { | ... | @@ -1862,7 +1862,7 @@ test "truncate single unsigned" { |
| 1862 | 1862 | ||
| 1863 | try a.truncate(&a, .unsigned, 17); | 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 | test "truncate single signed" { | 1868 | test "truncate single signed" { |
| ... | @@ -1871,7 +1871,7 @@ test "truncate single signed" { | ... | @@ -1871,7 +1871,7 @@ test "truncate single signed" { |
| 1871 | 1871 | ||
| 1872 | try a.truncate(&a, .signed, 17); | 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 | test "truncate multi to single unsigned" { | 1877 | test "truncate multi to single unsigned" { |
| ... | @@ -1880,7 +1880,7 @@ test "truncate multi to single unsigned" { | ... | @@ -1880,7 +1880,7 @@ test "truncate multi to single unsigned" { |
| 1880 | 1880 | ||
| 1881 | try a.truncate(&a, .unsigned, 27); | 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 | test "truncate multi to single signed" { | 1886 | test "truncate multi to single signed" { |
| ... | @@ -1889,7 +1889,7 @@ test "truncate multi to single signed" { | ... | @@ -1889,7 +1889,7 @@ test "truncate multi to single signed" { |
| 1889 | 1889 | ||
| 1890 | try a.truncate(&a, .signed, @bitSizeOf(i11)); | 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 | test "truncate multi to multi unsigned" { | 1895 | test "truncate multi to multi unsigned" { |
| ... | @@ -1901,7 +1901,7 @@ test "truncate multi to multi unsigned" { | ... | @@ -1901,7 +1901,7 @@ test "truncate multi to multi unsigned" { |
| 1901 | 1901 | ||
| 1902 | try a.truncate(&a, .unsigned, bits - 1); | 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 | test "truncate multi to multi signed" { | 1907 | test "truncate multi to multi signed" { |
| ... | @@ -1910,7 +1910,7 @@ test "truncate multi to multi signed" { | ... | @@ -1910,7 +1910,7 @@ test "truncate multi to multi signed" { |
| 1910 | 1910 | ||
| 1911 | try a.truncate(&a, .signed, @bitSizeOf(Limb) + 1); | 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 | test "truncate negative multi to single" { | 1916 | test "truncate negative multi to single" { |
| ... | @@ -1919,7 +1919,7 @@ test "truncate negative multi to single" { | ... | @@ -1919,7 +1919,7 @@ test "truncate negative multi to single" { |
| 1919 | 1919 | ||
| 1920 | try a.truncate(&a, .signed, @bitSizeOf(i17)); | 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 | test "truncate multi unsigned many" { | 1925 | test "truncate multi unsigned many" { |
| ... | @@ -1931,7 +1931,7 @@ test "truncate multi unsigned many" { | ... | @@ -1931,7 +1931,7 @@ test "truncate multi unsigned many" { |
| 1931 | defer b.deinit(); | 1931 | defer b.deinit(); |
| 1932 | try b.truncate(&a, .signed, @bitSizeOf(i1)); | 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 | test "saturate single signed positive" { | 1937 | test "saturate single signed positive" { |
| ... | @@ -1940,7 +1940,7 @@ test "saturate single signed positive" { | ... | @@ -1940,7 +1940,7 @@ test "saturate single signed positive" { |
| 1940 | 1940 | ||
| 1941 | try a.saturate(&a, .signed, 17); | 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 | test "saturate single signed negative" { | 1946 | test "saturate single signed negative" { |
| ... | @@ -1949,7 +1949,7 @@ test "saturate single signed negative" { | ... | @@ -1949,7 +1949,7 @@ test "saturate single signed negative" { |
| 1949 | 1949 | ||
| 1950 | try a.saturate(&a, .signed, 17); | 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 | test "saturate single signed" { | 1955 | test "saturate single signed" { |
| ... | @@ -1958,7 +1958,7 @@ test "saturate single signed" { | ... | @@ -1958,7 +1958,7 @@ test "saturate single signed" { |
| 1958 | 1958 | ||
| 1959 | try a.saturate(&a, .signed, 17); | 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 | test "saturate multi signed" { | 1964 | test "saturate multi signed" { |
| ... | @@ -1967,7 +1967,7 @@ test "saturate multi signed" { | ... | @@ -1967,7 +1967,7 @@ test "saturate multi signed" { |
| 1967 | 1967 | ||
| 1968 | try a.saturate(&a, .signed, @bitSizeOf(SignedDoubleLimb)); | 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 | test "saturate single unsigned" { | 1973 | test "saturate single unsigned" { |
| ... | @@ -1976,7 +1976,7 @@ test "saturate single unsigned" { | ... | @@ -1976,7 +1976,7 @@ test "saturate single unsigned" { |
| 1976 | 1976 | ||
| 1977 | try a.saturate(&a, .unsigned, 23); | 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 | test "saturate multi unsigned zero" { | 1982 | test "saturate multi unsigned zero" { |
| ... | @@ -1994,7 +1994,7 @@ test "saturate multi unsigned" { | ... | @@ -1994,7 +1994,7 @@ test "saturate multi unsigned" { |
| 1994 | 1994 | ||
| 1995 | try a.saturate(&a, .unsigned, @bitSizeOf(DoubleLimb)); | 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 | test "shift-right single" { | 2000 | test "shift-right single" { |
| ... | @@ -2002,7 +2002,7 @@ test "shift-right single" { | ... | @@ -2002,7 +2002,7 @@ test "shift-right single" { |
| 2002 | defer a.deinit(); | 2002 | defer a.deinit(); |
| 2003 | try a.shiftRight(&a, 16); | 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 | test "shift-right multi" { | 2008 | test "shift-right multi" { |
| ... | @@ -2010,7 +2010,7 @@ test "shift-right multi" { | ... | @@ -2010,7 +2010,7 @@ test "shift-right multi" { |
| 2010 | defer a.deinit(); | 2010 | defer a.deinit(); |
| 2011 | try a.shiftRight(&a, 67); | 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 | try a.set(0xffff0000eeee1111dddd2222cccc3333); | 2015 | try a.set(0xffff0000eeee1111dddd2222cccc3333); |
| 2016 | try a.shiftRight(&a, 63); | 2016 | try a.shiftRight(&a, 63); |
| ... | @@ -2037,7 +2037,7 @@ test "shift-left single" { | ... | @@ -2037,7 +2037,7 @@ test "shift-left single" { |
| 2037 | defer a.deinit(); | 2037 | defer a.deinit(); |
| 2038 | try a.shiftLeft(&a, 16); | 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 | test "shift-left multi" { | 2043 | test "shift-left multi" { |
| ... | @@ -2045,7 +2045,7 @@ test "shift-left multi" { | ... | @@ -2045,7 +2045,7 @@ test "shift-left multi" { |
| 2045 | defer a.deinit(); | 2045 | defer a.deinit(); |
| 2046 | try a.shiftLeft(&a, 67); | 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 | test "shift-right negative" { | 2051 | test "shift-right negative" { |
| ... | @@ -2055,43 +2055,43 @@ test "shift-right negative" { | ... | @@ -2055,43 +2055,43 @@ test "shift-right negative" { |
| 2055 | var arg = try Managed.initSet(testing.allocator, -20); | 2055 | var arg = try Managed.initSet(testing.allocator, -20); |
| 2056 | defer arg.deinit(); | 2056 | defer arg.deinit(); |
| 2057 | try a.shiftRight(&arg, 2); | 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 | var arg2 = try Managed.initSet(testing.allocator, -5); | 2060 | var arg2 = try Managed.initSet(testing.allocator, -5); |
| 2061 | defer arg2.deinit(); | 2061 | defer arg2.deinit(); |
| 2062 | try a.shiftRight(&arg2, 10); | 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 | var arg3 = try Managed.initSet(testing.allocator, -10); | 2065 | var arg3 = try Managed.initSet(testing.allocator, -10); |
| 2066 | defer arg3.deinit(); | 2066 | defer arg3.deinit(); |
| 2067 | try a.shiftRight(&arg3, 1232); | 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 | var arg4 = try Managed.initSet(testing.allocator, -5); | 2070 | var arg4 = try Managed.initSet(testing.allocator, -5); |
| 2071 | defer arg4.deinit(); | 2071 | defer arg4.deinit(); |
| 2072 | try a.shiftRight(&arg4, 2); | 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 | var arg5 = try Managed.initSet(testing.allocator, -0xffff0000eeee1111dddd2222cccc3333); | 2075 | var arg5 = try Managed.initSet(testing.allocator, -0xffff0000eeee1111dddd2222cccc3333); |
| 2076 | defer arg5.deinit(); | 2076 | defer arg5.deinit(); |
| 2077 | try a.shiftRight(&arg5, 67); | 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 | var arg6 = try Managed.initSet(testing.allocator, -0x1ffffffffffffffff); | 2080 | var arg6 = try Managed.initSet(testing.allocator, -0x1ffffffffffffffff); |
| 2081 | defer arg6.deinit(); | 2081 | defer arg6.deinit(); |
| 2082 | try a.shiftRight(&arg6, 1); | 2082 | try a.shiftRight(&arg6, 1); |
| 2083 | try a.shiftRight(&a, 1); | 2083 | try a.shiftRight(&a, 1); |
| 2084 | a.setSign(true); | 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 | var arg7 = try Managed.initSet(testing.allocator, -32767); | 2087 | var arg7 = try Managed.initSet(testing.allocator, -32767); |
| 2088 | defer arg7.deinit(); | 2088 | defer arg7.deinit(); |
| 2089 | a.setSign(false); | 2089 | a.setSign(false); |
| 2090 | try a.shiftRight(&arg7, 4); | 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 | a.setSign(true); | 2092 | a.setSign(true); |
| 2093 | try a.shiftRight(&arg7, 4); | 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 | test "sat shift-left simple unsigned" { | 2097 | test "sat shift-left simple unsigned" { |
| ... | @@ -2099,7 +2099,7 @@ test "sat shift-left simple unsigned" { | ... | @@ -2099,7 +2099,7 @@ test "sat shift-left simple unsigned" { |
| 2099 | defer a.deinit(); | 2099 | defer a.deinit(); |
| 2100 | try a.shiftLeftSat(&a, 16, .unsigned, 21); | 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 | test "sat shift-left simple unsigned no sat" { | 2105 | test "sat shift-left simple unsigned no sat" { |
| ... | @@ -2107,7 +2107,7 @@ test "sat shift-left simple unsigned no sat" { | ... | @@ -2107,7 +2107,7 @@ test "sat shift-left simple unsigned no sat" { |
| 2107 | defer a.deinit(); | 2107 | defer a.deinit(); |
| 2108 | try a.shiftLeftSat(&a, 16, .unsigned, 21); | 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 | test "sat shift-left multi unsigned" { | 2113 | test "sat shift-left multi unsigned" { |
| ... | @@ -2115,7 +2115,7 @@ test "sat shift-left multi unsigned" { | ... | @@ -2115,7 +2115,7 @@ test "sat shift-left multi unsigned" { |
| 2115 | defer a.deinit(); | 2115 | defer a.deinit(); |
| 2116 | try a.shiftLeftSat(&a, @bitSizeOf(DoubleLimb) - 3, .unsigned, @bitSizeOf(DoubleLimb) - 1); | 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 | test "sat shift-left unsigned shift > bitcount" { | 2121 | test "sat shift-left unsigned shift > bitcount" { |
| ... | @@ -2123,7 +2123,7 @@ test "sat shift-left unsigned shift > bitcount" { | ... | @@ -2123,7 +2123,7 @@ test "sat shift-left unsigned shift > bitcount" { |
| 2123 | defer a.deinit(); | 2123 | defer a.deinit(); |
| 2124 | try a.shiftLeftSat(&a, 10, .unsigned, 10); | 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 | test "sat shift-left unsigned zero" { | 2129 | test "sat shift-left unsigned zero" { |
| ... | @@ -2131,7 +2131,7 @@ test "sat shift-left unsigned zero" { | ... | @@ -2131,7 +2131,7 @@ test "sat shift-left unsigned zero" { |
| 2131 | defer a.deinit(); | 2131 | defer a.deinit(); |
| 2132 | try a.shiftLeftSat(&a, 1, .unsigned, 0); | 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 | test "sat shift-left unsigned negative" { | 2137 | test "sat shift-left unsigned negative" { |
| ... | @@ -2139,7 +2139,7 @@ test "sat shift-left unsigned negative" { | ... | @@ -2139,7 +2139,7 @@ test "sat shift-left unsigned negative" { |
| 2139 | defer a.deinit(); | 2139 | defer a.deinit(); |
| 2140 | try a.shiftLeftSat(&a, 0, .unsigned, 0); | 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 | test "sat shift-left signed simple negative" { | 2145 | test "sat shift-left signed simple negative" { |
| ... | @@ -2147,7 +2147,7 @@ test "sat shift-left signed simple negative" { | ... | @@ -2147,7 +2147,7 @@ test "sat shift-left signed simple negative" { |
| 2147 | defer a.deinit(); | 2147 | defer a.deinit(); |
| 2148 | try a.shiftLeftSat(&a, 3, .signed, 10); | 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 | test "sat shift-left signed simple positive" { | 2153 | test "sat shift-left signed simple positive" { |
| ... | @@ -2155,7 +2155,7 @@ test "sat shift-left signed simple positive" { | ... | @@ -2155,7 +2155,7 @@ test "sat shift-left signed simple positive" { |
| 2155 | defer a.deinit(); | 2155 | defer a.deinit(); |
| 2156 | try a.shiftLeftSat(&a, 3, .signed, 10); | 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 | test "sat shift-left signed multi positive" { | 2161 | test "sat shift-left signed multi positive" { |
| ... | @@ -2170,7 +2170,7 @@ test "sat shift-left signed multi positive" { | ... | @@ -2170,7 +2170,7 @@ test "sat shift-left signed multi positive" { |
| 2170 | defer a.deinit(); | 2170 | defer a.deinit(); |
| 2171 | try a.shiftLeftSat(&a, shift, .signed, @bitSizeOf(SignedDoubleLimb)); | 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 | test "sat shift-left signed multi negative" { | 2176 | test "sat shift-left signed multi negative" { |
| ... | @@ -2185,7 +2185,7 @@ test "sat shift-left signed multi negative" { | ... | @@ -2185,7 +2185,7 @@ test "sat shift-left signed multi negative" { |
| 2185 | defer a.deinit(); | 2185 | defer a.deinit(); |
| 2186 | try a.shiftLeftSat(&a, shift, .signed, @bitSizeOf(SignedDoubleLimb)); | 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 | test "bitNotWrap unsigned simple" { | 2191 | test "bitNotWrap unsigned simple" { |
| ... | @@ -2197,7 +2197,7 @@ test "bitNotWrap unsigned simple" { | ... | @@ -2197,7 +2197,7 @@ test "bitNotWrap unsigned simple" { |
| 2197 | 2197 | ||
| 2198 | try a.bitNotWrap(&a, .unsigned, 10); | 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 | test "bitNotWrap unsigned multi" { | 2203 | test "bitNotWrap unsigned multi" { |
| ... | @@ -2206,7 +2206,7 @@ test "bitNotWrap unsigned multi" { | ... | @@ -2206,7 +2206,7 @@ test "bitNotWrap unsigned multi" { |
| 2206 | 2206 | ||
| 2207 | try a.bitNotWrap(&a, .unsigned, @bitSizeOf(DoubleLimb)); | 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 | test "bitNotWrap signed simple" { | 2212 | test "bitNotWrap signed simple" { |
| ... | @@ -2218,7 +2218,7 @@ test "bitNotWrap signed simple" { | ... | @@ -2218,7 +2218,7 @@ test "bitNotWrap signed simple" { |
| 2218 | 2218 | ||
| 2219 | try a.bitNotWrap(&a, .signed, 11); | 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 | test "bitNotWrap signed multi" { | 2224 | test "bitNotWrap signed multi" { |
| ... | @@ -2227,7 +2227,7 @@ test "bitNotWrap signed multi" { | ... | @@ -2227,7 +2227,7 @@ test "bitNotWrap signed multi" { |
| 2227 | 2227 | ||
| 2228 | try a.bitNotWrap(&a, .signed, @bitSizeOf(SignedDoubleLimb)); | 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 | test "bitNotWrap more than two limbs" { | 2233 | test "bitNotWrap more than two limbs" { |
| ... | @@ -2249,11 +2249,11 @@ test "bitNotWrap more than two limbs" { | ... | @@ -2249,11 +2249,11 @@ test "bitNotWrap more than two limbs" { |
| 2249 | 2249 | ||
| 2250 | try res.bitNotWrap(&a, .unsigned, bits); | 2250 | try res.bitNotWrap(&a, .unsigned, bits); |
| 2251 | const Unsigned = @Type(.{ .int = .{ .signedness = .unsigned, .bits = bits } }); | 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 | try res.bitNotWrap(&a, .signed, bits); | 2254 | try res.bitNotWrap(&a, .signed, bits); |
| 2255 | const Signed = @Type(.{ .int = .{ .signedness = .signed, .bits = bits } }); | 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 | test "bitwise and simple" { | 2259 | test "bitwise and simple" { |
| ... | @@ -2264,7 +2264,7 @@ test "bitwise and simple" { | ... | @@ -2264,7 +2264,7 @@ test "bitwise and simple" { |
| 2264 | 2264 | ||
| 2265 | try a.bitAnd(&a, &b); | 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 | test "bitwise and multi-limb" { | 2270 | test "bitwise and multi-limb" { |
| ... | @@ -2275,7 +2275,7 @@ test "bitwise and multi-limb" { | ... | @@ -2275,7 +2275,7 @@ test "bitwise and multi-limb" { |
| 2275 | 2275 | ||
| 2276 | try a.bitAnd(&a, &b); | 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 | test "bitwise and negative-positive simple" { | 2281 | test "bitwise and negative-positive simple" { |
| ... | @@ -2286,7 +2286,7 @@ test "bitwise and negative-positive simple" { | ... | @@ -2286,7 +2286,7 @@ test "bitwise and negative-positive simple" { |
| 2286 | 2286 | ||
| 2287 | try a.bitAnd(&a, &b); | 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 | test "bitwise and negative-positive multi-limb" { | 2292 | test "bitwise and negative-positive multi-limb" { |
| ... | @@ -2308,7 +2308,7 @@ test "bitwise and positive-negative simple" { | ... | @@ -2308,7 +2308,7 @@ test "bitwise and positive-negative simple" { |
| 2308 | 2308 | ||
| 2309 | try a.bitAnd(&a, &b); | 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 | test "bitwise and positive-negative multi-limb" { | 2314 | test "bitwise and positive-negative multi-limb" { |
| ... | @@ -2330,7 +2330,7 @@ test "bitwise and negative-negative simple" { | ... | @@ -2330,7 +2330,7 @@ test "bitwise and negative-negative simple" { |
| 2330 | 2330 | ||
| 2331 | try a.bitAnd(&a, &b); | 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 | test "bitwise and negative-negative multi-limb" { | 2336 | test "bitwise and negative-negative multi-limb" { |
| ... | @@ -2341,7 +2341,7 @@ test "bitwise and negative-negative multi-limb" { | ... | @@ -2341,7 +2341,7 @@ test "bitwise and negative-negative multi-limb" { |
| 2341 | 2341 | ||
| 2342 | try a.bitAnd(&a, &b); | 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 | test "bitwise and negative overflow" { | 2347 | test "bitwise and negative overflow" { |
| ... | @@ -2352,7 +2352,7 @@ test "bitwise and negative overflow" { | ... | @@ -2352,7 +2352,7 @@ test "bitwise and negative overflow" { |
| 2352 | 2352 | ||
| 2353 | try a.bitAnd(&a, &b); | 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 | test "bitwise xor simple" { | 2358 | test "bitwise xor simple" { |
| ... | @@ -2363,7 +2363,7 @@ test "bitwise xor simple" { | ... | @@ -2363,7 +2363,7 @@ test "bitwise xor simple" { |
| 2363 | 2363 | ||
| 2364 | try a.bitXor(&a, &b); | 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 | test "bitwise xor multi-limb" { | 2369 | test "bitwise xor multi-limb" { |
| ... | @@ -2378,7 +2378,7 @@ test "bitwise xor multi-limb" { | ... | @@ -2378,7 +2378,7 @@ test "bitwise xor multi-limb" { |
| 2378 | 2378 | ||
| 2379 | try a.bitXor(&a, &b); | 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 | test "bitwise xor single negative simple" { | 2384 | test "bitwise xor single negative simple" { |
| ... | @@ -2389,7 +2389,7 @@ test "bitwise xor single negative simple" { | ... | @@ -2389,7 +2389,7 @@ test "bitwise xor single negative simple" { |
| 2389 | 2389 | ||
| 2390 | try a.bitXor(&a, &b); | 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 | test "bitwise xor single negative multi-limb" { | 2395 | test "bitwise xor single negative multi-limb" { |
| ... | @@ -2400,7 +2400,7 @@ test "bitwise xor single negative multi-limb" { | ... | @@ -2400,7 +2400,7 @@ test "bitwise xor single negative multi-limb" { |
| 2400 | 2400 | ||
| 2401 | try a.bitXor(&a, &b); | 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 | test "bitwise xor single negative overflow" { | 2406 | test "bitwise xor single negative overflow" { |
| ... | @@ -2411,7 +2411,7 @@ test "bitwise xor single negative overflow" { | ... | @@ -2411,7 +2411,7 @@ test "bitwise xor single negative overflow" { |
| 2411 | 2411 | ||
| 2412 | try a.bitXor(&a, &b); | 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 | test "bitwise xor double negative simple" { | 2417 | test "bitwise xor double negative simple" { |
| ... | @@ -2422,7 +2422,7 @@ test "bitwise xor double negative simple" { | ... | @@ -2422,7 +2422,7 @@ test "bitwise xor double negative simple" { |
| 2422 | 2422 | ||
| 2423 | try a.bitXor(&a, &b); | 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 | test "bitwise xor double negative multi-limb" { | 2428 | test "bitwise xor double negative multi-limb" { |
| ... | @@ -2433,7 +2433,7 @@ test "bitwise xor double negative multi-limb" { | ... | @@ -2433,7 +2433,7 @@ test "bitwise xor double negative multi-limb" { |
| 2433 | 2433 | ||
| 2434 | try a.bitXor(&a, &b); | 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 | test "bitwise or simple" { | 2439 | test "bitwise or simple" { |
| ... | @@ -2444,7 +2444,7 @@ test "bitwise or simple" { | ... | @@ -2444,7 +2444,7 @@ test "bitwise or simple" { |
| 2444 | 2444 | ||
| 2445 | try a.bitOr(&a, &b); | 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 | test "bitwise or multi-limb" { | 2450 | test "bitwise or multi-limb" { |
| ... | @@ -2455,7 +2455,7 @@ test "bitwise or multi-limb" { | ... | @@ -2455,7 +2455,7 @@ test "bitwise or multi-limb" { |
| 2455 | 2455 | ||
| 2456 | try a.bitOr(&a, &b); | 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 | test "bitwise or negative-positive simple" { | 2461 | test "bitwise or negative-positive simple" { |
| ... | @@ -2466,7 +2466,7 @@ test "bitwise or negative-positive simple" { | ... | @@ -2466,7 +2466,7 @@ test "bitwise or negative-positive simple" { |
| 2466 | 2466 | ||
| 2467 | try a.bitOr(&a, &b); | 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 | test "bitwise or negative-positive multi-limb" { | 2472 | test "bitwise or negative-positive multi-limb" { |
| ... | @@ -2477,7 +2477,7 @@ test "bitwise or negative-positive multi-limb" { | ... | @@ -2477,7 +2477,7 @@ test "bitwise or negative-positive multi-limb" { |
| 2477 | 2477 | ||
| 2478 | try a.bitOr(&a, &b); | 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 | test "bitwise or positive-negative simple" { | 2483 | test "bitwise or positive-negative simple" { |
| ... | @@ -2488,7 +2488,7 @@ test "bitwise or positive-negative simple" { | ... | @@ -2488,7 +2488,7 @@ test "bitwise or positive-negative simple" { |
| 2488 | 2488 | ||
| 2489 | try a.bitOr(&a, &b); | 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 | test "bitwise or positive-negative multi-limb" { | 2494 | test "bitwise or positive-negative multi-limb" { |
| ... | @@ -2499,7 +2499,7 @@ test "bitwise or positive-negative multi-limb" { | ... | @@ -2499,7 +2499,7 @@ test "bitwise or positive-negative multi-limb" { |
| 2499 | 2499 | ||
| 2500 | try a.bitOr(&a, &b); | 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 | test "bitwise or negative-negative simple" { | 2505 | test "bitwise or negative-negative simple" { |
| ... | @@ -2510,7 +2510,7 @@ test "bitwise or negative-negative simple" { | ... | @@ -2510,7 +2510,7 @@ test "bitwise or negative-negative simple" { |
| 2510 | 2510 | ||
| 2511 | try a.bitOr(&a, &b); | 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 | test "bitwise or negative-negative multi-limb" { | 2516 | test "bitwise or negative-negative multi-limb" { |
| ... | @@ -2521,7 +2521,7 @@ test "bitwise or negative-negative multi-limb" { | ... | @@ -2521,7 +2521,7 @@ test "bitwise or negative-negative multi-limb" { |
| 2521 | 2521 | ||
| 2522 | try a.bitOr(&a, &b); | 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 | test "var args" { | 2527 | test "var args" { |
| ... | @@ -2531,7 +2531,7 @@ test "var args" { | ... | @@ -2531,7 +2531,7 @@ test "var args" { |
| 2531 | var b = try Managed.initSet(testing.allocator, 6); | 2531 | var b = try Managed.initSet(testing.allocator, 6); |
| 2532 | defer b.deinit(); | 2532 | defer b.deinit(); |
| 2533 | try a.add(&a, &b); | 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 | var c = try Managed.initSet(testing.allocator, 11); | 2536 | var c = try Managed.initSet(testing.allocator, 11); |
| 2537 | defer c.deinit(); | 2537 | defer c.deinit(); |
| ... | @@ -2552,7 +2552,7 @@ test "gcd non-one small" { | ... | @@ -2552,7 +2552,7 @@ test "gcd non-one small" { |
| 2552 | 2552 | ||
| 2553 | try r.gcd(&a, &b); | 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 | test "gcd non-one medium" { | 2558 | test "gcd non-one medium" { |
| ... | @@ -2565,7 +2565,7 @@ test "gcd non-one medium" { | ... | @@ -2565,7 +2565,7 @@ test "gcd non-one medium" { |
| 2565 | 2565 | ||
| 2566 | try r.gcd(&a, &b); | 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 | test "gcd non-one large" { | 2571 | test "gcd non-one large" { |
| ... | @@ -2578,7 +2578,7 @@ test "gcd non-one large" { | ... | @@ -2578,7 +2578,7 @@ test "gcd non-one large" { |
| 2578 | 2578 | ||
| 2579 | try r.gcd(&a, &b); | 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 | test "gcd large multi-limb result" { | 2584 | test "gcd large multi-limb result" { |
| ... | @@ -2593,7 +2593,7 @@ test "gcd large multi-limb result" { | ... | @@ -2593,7 +2593,7 @@ test "gcd large multi-limb result" { |
| 2593 | 2593 | ||
| 2594 | try r.gcd(&a, &b); | 2594 | try r.gcd(&a, &b); |
| 2595 | 2595 | ||
| 2596 | const answer = (try r.to(u256)); | 2596 | const answer = (try r.toInt(u256)); |
| 2597 | try testing.expect(answer == 0xf000000ff00000fff0000ffff000fffff00ffffff1); | 2597 | try testing.expect(answer == 0xf000000ff00000fff0000ffff000fffff00ffffff1); |
| 2598 | } | 2598 | } |
| 2599 | 2599 | ||
| ... | @@ -2607,7 +2607,7 @@ test "gcd one large" { | ... | @@ -2607,7 +2607,7 @@ test "gcd one large" { |
| 2607 | 2607 | ||
| 2608 | try r.gcd(&a, &b); | 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 | test "mutable to managed" { | 2613 | test "mutable to managed" { |
| ... | @@ -2637,10 +2637,10 @@ test "pow" { | ... | @@ -2637,10 +2637,10 @@ test "pow" { |
| 2637 | defer a.deinit(); | 2637 | defer a.deinit(); |
| 2638 | 2638 | ||
| 2639 | try a.pow(&a, 3); | 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 | try a.pow(&a, 4); | 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 | var a = try Managed.initSet(testing.allocator, 10); | 2646 | var a = try Managed.initSet(testing.allocator, 10); |
| ... | @@ -2671,18 +2671,18 @@ test "pow" { | ... | @@ -2671,18 +2671,18 @@ test "pow" { |
| 2671 | defer a.deinit(); | 2671 | defer a.deinit(); |
| 2672 | 2672 | ||
| 2673 | try a.pow(&a, 100); | 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 | try a.set(1); | 2676 | try a.set(1); |
| 2677 | try a.pow(&a, 0); | 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 | try a.pow(&a, 100); | 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 | try a.set(-1); | 2681 | try a.set(-1); |
| 2682 | try a.pow(&a, 15); | 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 | try a.pow(&a, 16); | 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,24 +2696,24 @@ test "sqrt" { |
| 2696 | try r.set(0); | 2696 | try r.set(0); |
| 2697 | try a.set(25); | 2697 | try a.set(25); |
| 2698 | try r.sqrt(&a); | 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 | // aliased | 2701 | // aliased |
| 2702 | try a.set(25); | 2702 | try a.set(25); |
| 2703 | try a.sqrt(&a); | 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 | // bottom | 2706 | // bottom |
| 2707 | try r.set(0); | 2707 | try r.set(0); |
| 2708 | try a.set(24); | 2708 | try a.set(24); |
| 2709 | try r.sqrt(&a); | 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 | // large number | 2712 | // large number |
| 2713 | try r.set(0); | 2713 | try r.set(0); |
| 2714 | try a.set(0x1_0000_0000_0000); | 2714 | try a.set(0x1_0000_0000_0000); |
| 2715 | try r.sqrt(&a); | 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 | test "regression test for 1 limb overflow with alias" { | 2719 | test "regression test for 1 limb overflow with alias" { |
| ... | @@ -3225,7 +3225,7 @@ test "Managed sqrt(0) = 0" { | ... | @@ -3225,7 +3225,7 @@ test "Managed sqrt(0) = 0" { |
| 3225 | try a.setString(10, "0"); | 3225 | try a.setString(10, "0"); |
| 3226 | 3226 | ||
| 3227 | try res.sqrt(&a); | 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 | test "Managed sqrt(-1) = error" { | 3231 | test "Managed sqrt(-1) = error" { |
lib/std/math/big/rational.zig+48-48| ... | @@ -518,28 +518,28 @@ test "set" { | ... | @@ -518,28 +518,28 @@ test "set" { |
| 518 | defer a.deinit(); | 518 | defer a.deinit(); |
| 519 | 519 | ||
| 520 | try a.setInt(5); | 520 | try a.setInt(5); |
| 521 | try testing.expect((try a.p.to(u32)) == 5); | 521 | try testing.expect((try a.p.toInt(u32)) == 5); |
| 522 | try testing.expect((try a.q.to(u32)) == 1); | 522 | try testing.expect((try a.q.toInt(u32)) == 1); |
| 523 | 523 | ||
| 524 | try a.setRatio(7, 3); | 524 | try a.setRatio(7, 3); |
| 525 | try testing.expect((try a.p.to(u32)) == 7); | 525 | try testing.expect((try a.p.toInt(u32)) == 7); |
| 526 | try testing.expect((try a.q.to(u32)) == 3); | 526 | try testing.expect((try a.q.toInt(u32)) == 3); |
| 527 | 527 | ||
| 528 | try a.setRatio(9, 3); | 528 | try a.setRatio(9, 3); |
| 529 | try testing.expect((try a.p.to(i32)) == 3); | 529 | try testing.expect((try a.p.toInt(i32)) == 3); |
| 530 | try testing.expect((try a.q.to(i32)) == 1); | 530 | try testing.expect((try a.q.toInt(i32)) == 1); |
| 531 | 531 | ||
| 532 | try a.setRatio(-9, 3); | 532 | try a.setRatio(-9, 3); |
| 533 | try testing.expect((try a.p.to(i32)) == -3); | 533 | try testing.expect((try a.p.toInt(i32)) == -3); |
| 534 | try testing.expect((try a.q.to(i32)) == 1); | 534 | try testing.expect((try a.q.toInt(i32)) == 1); |
| 535 | 535 | ||
| 536 | try a.setRatio(9, -3); | 536 | try a.setRatio(9, -3); |
| 537 | try testing.expect((try a.p.to(i32)) == -3); | 537 | try testing.expect((try a.p.toInt(i32)) == -3); |
| 538 | try testing.expect((try a.q.to(i32)) == 1); | 538 | try testing.expect((try a.q.toInt(i32)) == 1); |
| 539 | 539 | ||
| 540 | try a.setRatio(-9, -3); | 540 | try a.setRatio(-9, -3); |
| 541 | try testing.expect((try a.p.to(i32)) == 3); | 541 | try testing.expect((try a.p.toInt(i32)) == 3); |
| 542 | try testing.expect((try a.q.to(i32)) == 1); | 542 | try testing.expect((try a.q.toInt(i32)) == 1); |
| 543 | } | 543 | } |
| 544 | 544 | ||
| 545 | test "setFloat" { | 545 | test "setFloat" { |
| ... | @@ -547,24 +547,24 @@ test "setFloat" { | ... | @@ -547,24 +547,24 @@ test "setFloat" { |
| 547 | defer a.deinit(); | 547 | defer a.deinit(); |
| 548 | 548 | ||
| 549 | try a.setFloat(f64, 2.5); | 549 | try a.setFloat(f64, 2.5); |
| 550 | try testing.expect((try a.p.to(i32)) == 5); | 550 | try testing.expect((try a.p.toInt(i32)) == 5); |
| 551 | try testing.expect((try a.q.to(i32)) == 2); | 551 | try testing.expect((try a.q.toInt(i32)) == 2); |
| 552 | 552 | ||
| 553 | try a.setFloat(f32, -2.5); | 553 | try a.setFloat(f32, -2.5); |
| 554 | try testing.expect((try a.p.to(i32)) == -5); | 554 | try testing.expect((try a.p.toInt(i32)) == -5); |
| 555 | try testing.expect((try a.q.to(i32)) == 2); | 555 | try testing.expect((try a.q.toInt(i32)) == 2); |
| 556 | 556 | ||
| 557 | try a.setFloat(f32, 3.141593); | 557 | try a.setFloat(f32, 3.141593); |
| 558 | 558 | ||
| 559 | // = 3.14159297943115234375 | 559 | // = 3.14159297943115234375 |
| 560 | try testing.expect((try a.p.to(u32)) == 3294199); | 560 | try testing.expect((try a.p.toInt(u32)) == 3294199); |
| 561 | try testing.expect((try a.q.to(u32)) == 1048576); | 561 | try testing.expect((try a.q.toInt(u32)) == 1048576); |
| 562 | 562 | ||
| 563 | try a.setFloat(f64, 72.141593120712409172417410926841290461290467124); | 563 | try a.setFloat(f64, 72.141593120712409172417410926841290461290467124); |
| 564 | 564 | ||
| 565 | // = 72.1415931207124145885245525278151035308837890625 | 565 | // = 72.1415931207124145885245525278151035308837890625 |
| 566 | try testing.expect((try a.p.to(u128)) == 5076513310880537); | 566 | try testing.expect((try a.p.toInt(u128)) == 5076513310880537); |
| 567 | try testing.expect((try a.q.to(u128)) == 70368744177664); | 567 | try testing.expect((try a.q.toInt(u128)) == 70368744177664); |
| 568 | } | 568 | } |
| 569 | 569 | ||
| 570 | test "setFloatString" { | 570 | test "setFloatString" { |
| ... | @@ -574,8 +574,8 @@ test "setFloatString" { | ... | @@ -574,8 +574,8 @@ test "setFloatString" { |
| 574 | try a.setFloatString("72.14159312071241458852455252781510353"); | 574 | try a.setFloatString("72.14159312071241458852455252781510353"); |
| 575 | 575 | ||
| 576 | // = 72.1415931207124145885245525278151035308837890625 | 576 | // = 72.1415931207124145885245525278151035308837890625 |
| 577 | try testing.expect((try a.p.to(u128)) == 7214159312071241458852455252781510353); | 577 | try testing.expect((try a.p.toInt(u128)) == 7214159312071241458852455252781510353); |
| 578 | try testing.expect((try a.q.to(u128)) == 100000000000000000000000000000000000); | 578 | try testing.expect((try a.q.toInt(u128)) == 100000000000000000000000000000000000); |
| 579 | } | 579 | } |
| 580 | 580 | ||
| 581 | test "toFloat" { | 581 | test "toFloat" { |
| ... | @@ -612,8 +612,8 @@ test "copy" { | ... | @@ -612,8 +612,8 @@ test "copy" { |
| 612 | defer b.deinit(); | 612 | defer b.deinit(); |
| 613 | 613 | ||
| 614 | try a.copyInt(b); | 614 | try a.copyInt(b); |
| 615 | try testing.expect((try a.p.to(u32)) == 5); | 615 | try testing.expect((try a.p.toInt(u32)) == 5); |
| 616 | try testing.expect((try a.q.to(u32)) == 1); | 616 | try testing.expect((try a.q.toInt(u32)) == 1); |
| 617 | 617 | ||
| 618 | var c = try Int.initSet(testing.allocator, 7); | 618 | var c = try Int.initSet(testing.allocator, 7); |
| 619 | defer c.deinit(); | 619 | defer c.deinit(); |
| ... | @@ -621,8 +621,8 @@ test "copy" { | ... | @@ -621,8 +621,8 @@ test "copy" { |
| 621 | defer d.deinit(); | 621 | defer d.deinit(); |
| 622 | 622 | ||
| 623 | try a.copyRatio(c, d); | 623 | try a.copyRatio(c, d); |
| 624 | try testing.expect((try a.p.to(u32)) == 7); | 624 | try testing.expect((try a.p.toInt(u32)) == 7); |
| 625 | try testing.expect((try a.q.to(u32)) == 3); | 625 | try testing.expect((try a.q.toInt(u32)) == 3); |
| 626 | 626 | ||
| 627 | var e = try Int.initSet(testing.allocator, 9); | 627 | var e = try Int.initSet(testing.allocator, 9); |
| 628 | defer e.deinit(); | 628 | defer e.deinit(); |
| ... | @@ -630,8 +630,8 @@ test "copy" { | ... | @@ -630,8 +630,8 @@ test "copy" { |
| 630 | defer f.deinit(); | 630 | defer f.deinit(); |
| 631 | 631 | ||
| 632 | try a.copyRatio(e, f); | 632 | try a.copyRatio(e, f); |
| 633 | try testing.expect((try a.p.to(u32)) == 3); | 633 | try testing.expect((try a.p.toInt(u32)) == 3); |
| 634 | try testing.expect((try a.q.to(u32)) == 1); | 634 | try testing.expect((try a.q.toInt(u32)) == 1); |
| 635 | } | 635 | } |
| 636 | 636 | ||
| 637 | test "negate" { | 637 | test "negate" { |
| ... | @@ -639,16 +639,16 @@ test "negate" { | ... | @@ -639,16 +639,16 @@ test "negate" { |
| 639 | defer a.deinit(); | 639 | defer a.deinit(); |
| 640 | 640 | ||
| 641 | try a.setInt(-50); | 641 | try a.setInt(-50); |
| 642 | try testing.expect((try a.p.to(i32)) == -50); | 642 | try testing.expect((try a.p.toInt(i32)) == -50); |
| 643 | try testing.expect((try a.q.to(i32)) == 1); | 643 | try testing.expect((try a.q.toInt(i32)) == 1); |
| 644 | 644 | ||
| 645 | a.negate(); | 645 | a.negate(); |
| 646 | try testing.expect((try a.p.to(i32)) == 50); | 646 | try testing.expect((try a.p.toInt(i32)) == 50); |
| 647 | try testing.expect((try a.q.to(i32)) == 1); | 647 | try testing.expect((try a.q.toInt(i32)) == 1); |
| 648 | 648 | ||
| 649 | a.negate(); | 649 | a.negate(); |
| 650 | try testing.expect((try a.p.to(i32)) == -50); | 650 | try testing.expect((try a.p.toInt(i32)) == -50); |
| 651 | try testing.expect((try a.q.to(i32)) == 1); | 651 | try testing.expect((try a.q.toInt(i32)) == 1); |
| 652 | } | 652 | } |
| 653 | 653 | ||
| 654 | test "abs" { | 654 | test "abs" { |
| ... | @@ -656,16 +656,16 @@ test "abs" { | ... | @@ -656,16 +656,16 @@ test "abs" { |
| 656 | defer a.deinit(); | 656 | defer a.deinit(); |
| 657 | 657 | ||
| 658 | try a.setInt(-50); | 658 | try a.setInt(-50); |
| 659 | try testing.expect((try a.p.to(i32)) == -50); | 659 | try testing.expect((try a.p.toInt(i32)) == -50); |
| 660 | try testing.expect((try a.q.to(i32)) == 1); | 660 | try testing.expect((try a.q.toInt(i32)) == 1); |
| 661 | 661 | ||
| 662 | a.abs(); | 662 | a.abs(); |
| 663 | try testing.expect((try a.p.to(i32)) == 50); | 663 | try testing.expect((try a.p.toInt(i32)) == 50); |
| 664 | try testing.expect((try a.q.to(i32)) == 1); | 664 | try testing.expect((try a.q.toInt(i32)) == 1); |
| 665 | 665 | ||
| 666 | a.abs(); | 666 | a.abs(); |
| 667 | try testing.expect((try a.p.to(i32)) == 50); | 667 | try testing.expect((try a.p.toInt(i32)) == 50); |
| 668 | try testing.expect((try a.q.to(i32)) == 1); | 668 | try testing.expect((try a.q.toInt(i32)) == 1); |
| 669 | } | 669 | } |
| 670 | 670 | ||
| 671 | test "swap" { | 671 | test "swap" { |
| ... | @@ -677,19 +677,19 @@ test "swap" { | ... | @@ -677,19 +677,19 @@ test "swap" { |
| 677 | try a.setRatio(50, 23); | 677 | try a.setRatio(50, 23); |
| 678 | try b.setRatio(17, 3); | 678 | try b.setRatio(17, 3); |
| 679 | 679 | ||
| 680 | try testing.expect((try a.p.to(u32)) == 50); | 680 | try testing.expect((try a.p.toInt(u32)) == 50); |
| 681 | try testing.expect((try a.q.to(u32)) == 23); | 681 | try testing.expect((try a.q.toInt(u32)) == 23); |
| 682 | 682 | ||
| 683 | try testing.expect((try b.p.to(u32)) == 17); | 683 | try testing.expect((try b.p.toInt(u32)) == 17); |
| 684 | try testing.expect((try b.q.to(u32)) == 3); | 684 | try testing.expect((try b.q.toInt(u32)) == 3); |
| 685 | 685 | ||
| 686 | a.swap(&b); | 686 | a.swap(&b); |
| 687 | 687 | ||
| 688 | try testing.expect((try a.p.to(u32)) == 17); | 688 | try testing.expect((try a.p.toInt(u32)) == 17); |
| 689 | try testing.expect((try a.q.to(u32)) == 3); | 689 | try testing.expect((try a.q.toInt(u32)) == 3); |
| 690 | 690 | ||
| 691 | try testing.expect((try b.p.to(u32)) == 50); | 691 | try testing.expect((try b.p.toInt(u32)) == 50); |
| 692 | try testing.expect((try b.q.to(u32)) == 23); | 692 | try testing.expect((try b.q.toInt(u32)) == 23); |
| 693 | } | 693 | } |
| 694 | 694 | ||
| 695 | test "order" { | 695 | test "order" { |
lib/std/std.zig+1| ... | @@ -93,6 +93,7 @@ pub const valgrind = @import("valgrind.zig"); | ... | @@ -93,6 +93,7 @@ pub const valgrind = @import("valgrind.zig"); |
| 93 | pub const wasm = @import("wasm.zig"); | 93 | pub const wasm = @import("wasm.zig"); |
| 94 | pub const zig = @import("zig.zig"); | 94 | pub const zig = @import("zig.zig"); |
| 95 | pub const zip = @import("zip.zig"); | 95 | pub const zip = @import("zip.zig"); |
| 96 | pub const zon = @import("zon.zig"); | ||
| 96 | pub const start = @import("start.zig"); | 97 | pub const start = @import("start.zig"); |
| 97 | 98 | ||
| 98 | const root = @import("root"); | 99 | const root = @import("root"); |
lib/std/zig/AstGen.zig+26-3| ... | @@ -9448,7 +9448,18 @@ fn builtinCall( | ... | @@ -9448,7 +9448,18 @@ fn builtinCall( |
| 9448 | } else if (str.len == 0) { | 9448 | } else if (str.len == 0) { |
| 9449 | return astgen.failTok(str_lit_token, "import path cannot be empty", .{}); | 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 | const gop = try astgen.imports.getOrPut(astgen.gpa, str.index); | 9463 | const gop = try astgen.imports.getOrPut(astgen.gpa, str.index); |
| 9453 | if (!gop.found_existing) { | 9464 | if (!gop.found_existing) { |
| 9454 | gop.value_ptr.* = str_lit_token; | 9465 | gop.value_ptr.* = str_lit_token; |
| ... | @@ -11551,9 +11562,21 @@ fn parseStrLit( | ... | @@ -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 | const raw_string = bytes[offset..]; | 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 | fn failNode( | 11582 | fn failNode( |
lib/std/zig/Zir.zig+9-2| ... | @@ -483,7 +483,7 @@ pub const Inst = struct { | ... | @@ -483,7 +483,7 @@ pub const Inst = struct { |
| 483 | /// Uses the `pl_node` union field. `payload_index` points to a `FuncFancy`. | 483 | /// Uses the `pl_node` union field. `payload_index` points to a `FuncFancy`. |
| 484 | func_fancy, | 484 | func_fancy, |
| 485 | /// Implements the `@import` builtin. | 485 | /// Implements the `@import` builtin. |
| 486 | /// Uses the `str_tok` field. | 486 | /// Uses the `pl_tok` field. |
| 487 | import, | 487 | import, |
| 488 | /// Integer literal that fits in a u64. Uses the `int` union field. | 488 | /// Integer literal that fits in a u64. Uses the `int` union field. |
| 489 | int, | 489 | int, |
| ... | @@ -1673,7 +1673,7 @@ pub const Inst = struct { | ... | @@ -1673,7 +1673,7 @@ pub const Inst = struct { |
| 1673 | .func = .pl_node, | 1673 | .func = .pl_node, |
| 1674 | .func_inferred = .pl_node, | 1674 | .func_inferred = .pl_node, |
| 1675 | .func_fancy = .pl_node, | 1675 | .func_fancy = .pl_node, |
| 1676 | .import = .str_tok, | 1676 | .import = .pl_tok, |
| 1677 | .int = .int, | 1677 | .int = .int, |
| 1678 | .int_big = .str, | 1678 | .int_big = .str, |
| 1679 | .float = .float, | 1679 | .float = .float, |
| ... | @@ -3841,6 +3841,13 @@ pub const Inst = struct { | ... | @@ -3841,6 +3841,13 @@ pub const Inst = struct { |
| 3841 | /// If `.none`, restore unconditionally. | 3841 | /// If `.none`, restore unconditionally. |
| 3842 | operand: Ref, | 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 | pub const SpecialProng = enum { none, @"else", under }; | 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,7 +54,7 @@ pub const Node = union(enum) { |
| 54 | /// A floating-point literal. | 54 | /// A floating-point literal. |
| 55 | float_literal: f128, | 55 | float_literal: f128, |
| 56 | /// A Unicode codepoint literal. | 56 | /// A Unicode codepoint literal. |
| 57 | char_literal: u32, | 57 | char_literal: u21, |
| 58 | /// An enum literal. The string is the literal, i.e. `foo` for `.foo`. | 58 | /// An enum literal. The string is the literal, i.e. `foo` for `.foo`. |
| 59 | enum_literal: NullTerminatedString, | 59 | enum_literal: NullTerminatedString, |
| 60 | /// A string literal. | 60 | /// A string literal. |
| ... | @@ -96,7 +96,7 @@ pub const Node = union(enum) { | ... | @@ -96,7 +96,7 @@ pub const Node = union(enum) { |
| 96 | } } }, | 96 | } } }, |
| 97 | .float_literal_small => .{ .float_literal = @as(f32, @bitCast(repr.data)) }, | 97 | .float_literal_small => .{ .float_literal = @as(f32, @bitCast(repr.data)) }, |
| 98 | .float_literal => .{ .float_literal = @bitCast(zoir.extra[repr.data..][0..4].*) }, | 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 | .enum_literal => .{ .enum_literal = @enumFromInt(repr.data) }, | 100 | .enum_literal => .{ .enum_literal = @enumFromInt(repr.data) }, |
| 101 | .string_literal => .{ .string_literal = s: { | 101 | .string_literal => .{ .string_literal = s: { |
| 102 | const start, const len = zoir.extra[repr.data..][0..2].*; | 102 | const start, const len = zoir.extra[repr.data..][0..2].*; |
lib/std/zig/ZonGen.zig+154-54| ... | @@ -3,6 +3,8 @@ | ... | @@ -3,6 +3,8 @@ |
| 3 | gpa: Allocator, | 3 | gpa: Allocator, |
| 4 | tree: Ast, | 4 | tree: Ast, |
| 5 | 5 | ||
| 6 | options: Options, | ||
| 7 | |||
| 6 | nodes: std.MultiArrayList(Zoir.Node.Repr), | 8 | nodes: std.MultiArrayList(Zoir.Node.Repr), |
| 7 | extra: std.ArrayListUnmanaged(u32), | 9 | extra: std.ArrayListUnmanaged(u32), |
| 8 | limbs: std.ArrayListUnmanaged(std.math.big.Limb), | 10 | limbs: std.ArrayListUnmanaged(std.math.big.Limb), |
| ... | @@ -12,12 +14,21 @@ string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.d | ... | @@ -12,12 +14,21 @@ string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.d |
| 12 | compile_errors: std.ArrayListUnmanaged(Zoir.CompileError), | 14 | compile_errors: std.ArrayListUnmanaged(Zoir.CompileError), |
| 13 | error_notes: std.ArrayListUnmanaged(Zoir.CompileError.Note), | 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 | assert(tree.mode == .zon); | 26 | assert(tree.mode == .zon); |
| 17 | 27 | ||
| 18 | var zg: ZonGen = .{ | 28 | var zg: ZonGen = .{ |
| 19 | .gpa = gpa, | 29 | .gpa = gpa, |
| 20 | .tree = tree, | 30 | .tree = tree, |
| 31 | .options = options, | ||
| 21 | .nodes = .empty, | 32 | .nodes = .empty, |
| 22 | .extra = .empty, | 33 | .extra = .empty, |
| 23 | .limbs = .empty, | 34 | .limbs = .empty, |
| ... | @@ -250,7 +261,20 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator | ... | @@ -250,7 +261,20 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator |
| 250 | .block_two_semicolon, | 261 | .block_two_semicolon, |
| 251 | .block, | 262 | .block, |
| 252 | .block_semicolon, | 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 | .array_init_one, | 279 | .array_init_one, |
| 256 | .array_init_one_comma, | 280 | .array_init_one_comma, |
| ... | @@ -403,58 +427,37 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator | ... | @@ -403,58 +427,37 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator |
| 403 | .ast_node = node, | 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 | for (full.ast.fields, names_start.., first_elem..) |elem_node, extra_name_idx, elem_dest_node| { | 438 | for (full.ast.fields, names_start.., first_elem..) |elem_node, extra_name_idx, elem_dest_node| { |
| 407 | const name_token = tree.firstToken(elem_node) - 2; | 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) { | 440 | if (zg.identAsString(name_token)) |name_str| { |
| 409 | error.BadString => undefined, // doesn't matter, there's an error | 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 | error.OutOfMemory => |e| return e, | 453 | error.OutOfMemory => |e| return e, |
| 411 | }); | 454 | } |
| 412 | try zg.expr(elem_node, @enumFromInt(elem_dest_node)); | 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 | fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 { | 461 | fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 { |
| 459 | const tree = zg.tree; | 462 | const tree = zg.tree; |
| 460 | assert(tree.tokens.items(.tag)[ident_token] == .identifier); | 463 | assert(tree.tokens.items(.tag)[ident_token] == .identifier); |
| ... | @@ -464,7 +467,18 @@ fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 { | ... | @@ -464,7 +467,18 @@ fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 { |
| 464 | try zg.string_bytes.appendSlice(zg.gpa, ident_name); | 467 | try zg.string_bytes.appendSlice(zg.gpa, ident_name); |
| 465 | return @intCast(start); | 468 | return @intCast(start); |
| 466 | } else { | 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 | const slice = zg.string_bytes.items[start..]; | 482 | const slice = zg.string_bytes.items[start..]; |
| 469 | if (mem.indexOfScalar(u8, slice, 0) != null) { | 483 | if (mem.indexOfScalar(u8, slice, 0) != null) { |
| 470 | try zg.addErrorTok(ident_token, "identifier cannot contain null bytes", .{}); | 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,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 | const StringLiteralResult = union(enum) { | 559 | const StringLiteralResult = union(enum) { |
| 481 | nts: Zoir.NullTerminatedString, | 560 | nts: Zoir.NullTerminatedString, |
| 482 | slice: struct { start: u32, len: u32 }, | 561 | slice: struct { start: u32, len: u32 }, |
| 483 | }; | 562 | }; |
| 484 | 563 | ||
| 485 | fn strLitAsString(zg: *ZonGen, str_node: Ast.Node.Index) !StringLiteralResult { | 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 | const gpa = zg.gpa; | 567 | const gpa = zg.gpa; |
| 487 | const string_bytes = &zg.string_bytes; | 568 | const string_bytes = &zg.string_bytes; |
| 488 | const str_index = switch (zg.tree.nodes.items(.tag)[str_node]) { | 569 | const str_index: u32 = @intCast(zg.string_bytes.items.len); |
| 489 | .string_literal => try zg.parseStrLit(zg.tree.nodes.items(.main_token)[str_node], 0), | 570 | const size_hint = strLitSizeHint(zg.tree, str_node); |
| 490 | .multiline_string_literal => try zg.parseMultilineStrLit(str_node), | 571 | try string_bytes.ensureUnusedCapacity(zg.gpa, size_hint); |
| 491 | else => unreachable, | 572 | switch (try parseStrLit(zg.tree, str_node, zg.string_bytes.writer(zg.gpa))) { |
| 492 | }; | 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 | const key: []const u8 = string_bytes.items[str_index..]; | 581 | const key: []const u8 = string_bytes.items[str_index..]; |
| 494 | if (std.mem.indexOfScalar(u8, key, 0) != null) return .{ .slice = .{ | 582 | if (std.mem.indexOfScalar(u8, key, 0) != null) return .{ .slice = .{ |
| 495 | .start = str_index, | 583 | .start = str_index, |
| ... | @@ -540,7 +628,7 @@ fn numberLiteral(zg: *ZonGen, num_node: Ast.Node.Index, src_node: Ast.Node.Index | ... | @@ -540,7 +628,7 @@ fn numberLiteral(zg: *ZonGen, num_node: Ast.Node.Index, src_node: Ast.Node.Index |
| 540 | if (unsigned_num == 0 and sign == .negative) { | 628 | if (unsigned_num == 0 and sign == .negative) { |
| 541 | try zg.addErrorTokNotes(num_token, "integer literal '-0' is ambiguous", .{}, &.{ | 629 | try zg.addErrorTokNotes(num_token, "integer literal '-0' is ambiguous", .{}, &.{ |
| 542 | try zg.errNoteTok(num_token, "use '0' for an integer zero", .{}), | 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 | return; | 633 | return; |
| 546 | } | 634 | } |
| ... | @@ -679,8 +767,20 @@ fn setNode(zg: *ZonGen, dest: Zoir.Node.Index, repr: Zoir.Node.Repr) void { | ... | @@ -679,8 +767,20 @@ fn setNode(zg: *ZonGen, dest: Zoir.Node.Index, repr: Zoir.Node.Repr) void { |
| 679 | zg.nodes.set(@intFromEnum(dest), repr); | 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 { | 770 | fn lowerStrLitError( |
| 683 | return err.lower(raw_string, offset, ZonGen.addErrorTokOff, .{ zg, token }); | 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 | fn lowerNumberError(zg: *ZonGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) Allocator.Error!void { | 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,40 +39,82 @@ pub const Error = union(enum) { |
| 39 | /// `''`. Not returned for string literals. | 39 | /// `''`. Not returned for string literals. |
| 40 | empty_char_literal, | 40 | empty_char_literal, |
| 41 | 41 | ||
| 42 | /// Returns `func(first_args[0], ..., first_args[n], offset + bad_idx, format, args)`. | 42 | const FormatMessage = struct { |
| 43 | pub fn lower( | ||
| 44 | err: Error, | 43 | err: Error, |
| 45 | raw_string: []const u8, | 44 | raw_string: []const u8, |
| 46 | offset: u32, | 45 | }; |
| 47 | comptime func: anytype, | 46 | |
| 48 | first_args: anytype, | 47 | fn formatMessage( |
| 49 | ) @typeInfo(@TypeOf(func)).@"fn".return_type.? { | 48 | self: FormatMessage, |
| 50 | switch (err) { | 49 | comptime f: []const u8, |
| 51 | inline else => |bad_index_or_void, tag| { | 50 | options: std.fmt.FormatOptions, |
| 52 | const bad_index: u32 = switch (@TypeOf(bad_index_or_void)) { | 51 | writer: anytype, |
| 53 | void => 0, | 52 | ) !void { |
| 54 | else => @intCast(bad_index_or_void), | 53 | _ = f; |
| 55 | }; | 54 | _ = options; |
| 56 | const fmt_str: []const u8, const args = switch (tag) { | 55 | switch (self.err) { |
| 57 | .invalid_escape_character => .{ "invalid escape character: '{c}'", .{raw_string[bad_index]} }, | 56 | .invalid_escape_character => |bad_index| try writer.print( |
| 58 | .expected_hex_digit => .{ "expected hex digit, found '{c}'", .{raw_string[bad_index]} }, | 57 | "invalid escape character: '{c}'", |
| 59 | .empty_unicode_escape_sequence => .{ "empty unicode escape sequence", .{} }, | 58 | .{self.raw_string[bad_index]}, |
| 60 | .expected_hex_digit_or_rbrace => .{ "expected hex digit or '}}', found '{c}'", .{raw_string[bad_index]} }, | 59 | ), |
| 61 | .invalid_unicode_codepoint => .{ "unicode escape does not correspond to a valid unicode scalar value", .{} }, | 60 | .expected_hex_digit => |bad_index| try writer.print( |
| 62 | .expected_lbrace => .{ "expected '{{', found '{c}'", .{raw_string[bad_index]} }, | 61 | "expected hex digit, found '{c}'", |
| 63 | .expected_rbrace => .{ "expected '}}', found '{c}'", .{raw_string[bad_index]} }, | 62 | .{self.raw_string[bad_index]}, |
| 64 | .expected_single_quote => .{ "expected singel quote ('), found '{c}'", .{raw_string[bad_index]} }, | 63 | ), |
| 65 | .invalid_character => .{ "invalid byte in string or character literal: '{c}'", .{raw_string[bad_index]} }, | 64 | .empty_unicode_escape_sequence => try writer.writeAll( |
| 66 | .empty_char_literal => .{ "empty character literal", .{} }, | 65 | "empty unicode escape sequence", |
| 67 | }; | 66 | ), |
| 68 | return @call(.auto, func, first_args ++ .{ | 67 | .expected_hex_digit_or_rbrace => |bad_index| try writer.print( |
| 69 | offset + bad_index, | 68 | "expected hex digit or '}}', found '{c}'", |
| 70 | fmt_str, | 69 | .{self.raw_string[bad_index]}, |
| 71 | args, | 70 | ), |
| 72 | }); | 71 | .invalid_unicode_codepoint => try writer.writeAll( |
| 73 | }, | 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 | /// Asserts the slice starts and ends with single-quotes. | 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,7 +2220,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2220 | try comp.astgen_work_queue.ensureUnusedCapacity(zcu.import_table.count()); | 2220 | try comp.astgen_work_queue.ensureUnusedCapacity(zcu.import_table.count()); |
| 2221 | for (zcu.import_table.values()) |file_index| { | 2221 | for (zcu.import_table.values()) |file_index| { |
| 2222 | if (zcu.fileByIndex(file_index).mod.isBuiltin()) continue; | 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 | if (comp.file_system_inputs) |fsi| { | 2228 | if (comp.file_system_inputs) |fsi| { |
| 2226 | for (zcu.import_table.values()) |file_index| { | 2229 | for (zcu.import_table.values()) |file_index| { |
| ... | @@ -3206,10 +3209,16 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { | ... | @@ -3206,10 +3209,16 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { |
| 3206 | if (error_msg) |msg| { | 3209 | if (error_msg) |msg| { |
| 3207 | try addModuleErrorMsg(zcu, &bundle, msg.*); | 3210 | try addModuleErrorMsg(zcu, &bundle, msg.*); |
| 3208 | } else { | 3211 | } else { |
| 3209 | // Must be ZIR errors. Note that this may include AST errors. | 3212 | // Must be ZIR or Zoir errors. Note that this may include AST errors. |
| 3210 | // addZirErrorMessages asserts that the tree is loaded. | 3213 | _ = try file.getTree(gpa); // Tree must be loaded. |
| 3211 | _ = try file.getTree(gpa); | 3214 | if (file.zir_loaded) { |
| 3212 | try addZirErrorMessages(&bundle, file); | 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 | var sorted_failed_analysis: std.AutoArrayHashMapUnmanaged(InternPool.AnalUnit, *Zcu.ErrorMsg).DataList.Slice = s: { | 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,6 +3632,15 @@ pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void { |
| 3623 | return eb.addZirErrorMessages(file.zir, file.tree, file.source, src_path); | 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 | pub fn performAllTheWork( | 3644 | pub fn performAllTheWork( |
| 3627 | comp: *Compilation, | 3645 | comp: *Compilation, |
| 3628 | main_progress_node: std.Progress.Node, | 3646 | main_progress_node: std.Progress.Node, |
| ... | @@ -4272,6 +4290,7 @@ fn workerAstGenFile( | ... | @@ -4272,6 +4290,7 @@ fn workerAstGenFile( |
| 4272 | wg: *WaitGroup, | 4290 | wg: *WaitGroup, |
| 4273 | src: Zcu.AstGenSrc, | 4291 | src: Zcu.AstGenSrc, |
| 4274 | ) void { | 4292 | ) void { |
| 4293 | assert(file.getMode() == .zig); | ||
| 4275 | const child_prog_node = prog_node.start(file.sub_file_path, 0); | 4294 | const child_prog_node = prog_node.start(file.sub_file_path, 0); |
| 4276 | defer child_prog_node.end(); | 4295 | defer child_prog_node.end(); |
| 4277 | 4296 | ||
| ... | @@ -4325,7 +4344,7 @@ fn workerAstGenFile( | ... | @@ -4325,7 +4344,7 @@ fn workerAstGenFile( |
| 4325 | const imported_path_digest = pt.zcu.filePathDigest(res.file_index); | 4344 | const imported_path_digest = pt.zcu.filePathDigest(res.file_index); |
| 4326 | break :blk .{ res, imported_path_digest }; | 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 | log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{ | 4348 | log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{ |
| 4330 | file.sub_file_path, import_path, import_result.file.sub_file_path, | 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,7 +4389,7 @@ pub const LoadedEnumType = struct { |
| 4389 | // Auto-numbered enum. Convert `int_tag_val` to field index. | 4389 | // Auto-numbered enum. Convert `int_tag_val` to field index. |
| 4390 | const field_index = switch (ip.indexToKey(int_tag_val).int.storage) { | 4390 | const field_index = switch (ip.indexToKey(int_tag_val).int.storage) { |
| 4391 | inline .u64, .i64 => |x| std.math.cast(u32, x) orelse return null, | 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 | .lazy_align, .lazy_size => unreachable, | 4393 | .lazy_align, .lazy_size => unreachable, |
| 4394 | }; | 4394 | }; |
| 4395 | return if (field_index < self.names.len) field_index else null; | 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,7 +7957,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 7957 | .big_int => |big_int| { | 7957 | .big_int => |big_int| { |
| 7958 | items.appendAssumeCapacity(.{ | 7958 | items.appendAssumeCapacity(.{ |
| 7959 | .tag = .int_u8, | 7959 | .tag = .int_u8, |
| 7960 | .data = big_int.to(u8) catch unreachable, | 7960 | .data = big_int.toInt(u8) catch unreachable, |
| 7961 | }); | 7961 | }); |
| 7962 | break :b; | 7962 | break :b; |
| 7963 | }, | 7963 | }, |
| ... | @@ -7974,7 +7974,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All | ... | @@ -7974,7 +7974,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 7974 | .big_int => |big_int| { | 7974 | .big_int => |big_int| { |
| 7975 | items.appendAssumeCapacity(.{ | 7975 | items.appendAssumeCapacity(.{ |
| 7976 | .tag = .int_u16, | 7976 | .tag = .int_u16, |
| 7977 | .data = big_int.to(u16) catch unreachable, | 7977 | .data = big_int.toInt(u16) catch unreachable, |
| 7978 | }); | 7978 | }); |
| 7979 | break :b; | 7979 | break :b; |
| 7980 | }, | 7980 | }, |
| ... | @@ -7991,7 +7991,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All | ... | @@ -7991,7 +7991,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 7991 | .big_int => |big_int| { | 7991 | .big_int => |big_int| { |
| 7992 | items.appendAssumeCapacity(.{ | 7992 | items.appendAssumeCapacity(.{ |
| 7993 | .tag = .int_u32, | 7993 | .tag = .int_u32, |
| 7994 | .data = big_int.to(u32) catch unreachable, | 7994 | .data = big_int.toInt(u32) catch unreachable, |
| 7995 | }); | 7995 | }); |
| 7996 | break :b; | 7996 | break :b; |
| 7997 | }, | 7997 | }, |
| ... | @@ -8006,7 +8006,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All | ... | @@ -8006,7 +8006,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 8006 | }, | 8006 | }, |
| 8007 | .i32_type => switch (int.storage) { | 8007 | .i32_type => switch (int.storage) { |
| 8008 | .big_int => |big_int| { | 8008 | .big_int => |big_int| { |
| 8009 | const casted = big_int.to(i32) catch unreachable; | 8009 | const casted = big_int.toInt(i32) catch unreachable; |
| 8010 | items.appendAssumeCapacity(.{ | 8010 | items.appendAssumeCapacity(.{ |
| 8011 | .tag = .int_i32, | 8011 | .tag = .int_i32, |
| 8012 | .data = @as(u32, @bitCast(casted)), | 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,7 +8024,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 8024 | }, | 8024 | }, |
| 8025 | .usize_type => switch (int.storage) { | 8025 | .usize_type => switch (int.storage) { |
| 8026 | .big_int => |big_int| { | 8026 | .big_int => |big_int| { |
| 8027 | if (big_int.to(u32)) |casted| { | 8027 | if (big_int.toInt(u32)) |casted| { |
| 8028 | items.appendAssumeCapacity(.{ | 8028 | items.appendAssumeCapacity(.{ |
| 8029 | .tag = .int_usize, | 8029 | .tag = .int_usize, |
| 8030 | .data = casted, | 8030 | .data = casted, |
| ... | @@ -8045,14 +8045,14 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All | ... | @@ -8045,14 +8045,14 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 8045 | }, | 8045 | }, |
| 8046 | .comptime_int_type => switch (int.storage) { | 8046 | .comptime_int_type => switch (int.storage) { |
| 8047 | .big_int => |big_int| { | 8047 | .big_int => |big_int| { |
| 8048 | if (big_int.to(u32)) |casted| { | 8048 | if (big_int.toInt(u32)) |casted| { |
| 8049 | items.appendAssumeCapacity(.{ | 8049 | items.appendAssumeCapacity(.{ |
| 8050 | .tag = .int_comptime_int_u32, | 8050 | .tag = .int_comptime_int_u32, |
| 8051 | .data = casted, | 8051 | .data = casted, |
| 8052 | }); | 8052 | }); |
| 8053 | break :b; | 8053 | break :b; |
| 8054 | } else |_| {} | 8054 | } else |_| {} |
| 8055 | if (big_int.to(i32)) |casted| { | 8055 | if (big_int.toInt(i32)) |casted| { |
| 8056 | items.appendAssumeCapacity(.{ | 8056 | items.appendAssumeCapacity(.{ |
| 8057 | .tag = .int_comptime_int_i32, | 8057 | .tag = .int_comptime_int_i32, |
| 8058 | .data = @as(u32, @bitCast(casted)), | 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,7 +8082,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All |
| 8082 | } | 8082 | } |
| 8083 | switch (int.storage) { | 8083 | switch (int.storage) { |
| 8084 | .big_int => |big_int| { | 8084 | .big_int => |big_int| { |
| 8085 | if (big_int.to(u32)) |casted| { | 8085 | if (big_int.toInt(u32)) |casted| { |
| 8086 | items.appendAssumeCapacity(.{ | 8086 | items.appendAssumeCapacity(.{ |
| 8087 | .tag = .int_small, | 8087 | .tag = .int_small, |
| 8088 | .data = try addExtra(extra, IntSmall{ | 8088 | .data = try addExtra(extra, IntSmall{ |
src/Sema.zig+41-9| ... | @@ -187,6 +187,7 @@ const Alignment = InternPool.Alignment; | ... | @@ -187,6 +187,7 @@ const Alignment = InternPool.Alignment; |
| 187 | const AnalUnit = InternPool.AnalUnit; | 187 | const AnalUnit = InternPool.AnalUnit; |
| 188 | const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; | 188 | const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; |
| 189 | const Cache = std.Build.Cache; | 189 | const Cache = std.Build.Cache; |
| 190 | const LowerZon = @import("Sema/LowerZon.zig"); | ||
| 190 | 191 | ||
| 191 | pub const default_branch_quota = 1000; | 192 | pub const default_branch_quota = 1000; |
| 192 | pub const default_reference_trace_len = 2; | 193 | pub const default_reference_trace_len = 2; |
| ... | @@ -5790,7 +5791,7 @@ fn addNullTerminatedStrLit(sema: *Sema, string: InternPool.NullTerminatedString) | ... | @@ -5790,7 +5791,7 @@ fn addNullTerminatedStrLit(sema: *Sema, string: InternPool.NullTerminatedString) |
| 5790 | return sema.addStrLit(string.toString(), string.length(&sema.pt.zcu.intern_pool)); | 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 | const pt = sema.pt; | 5795 | const pt = sema.pt; |
| 5795 | const array_ty = try pt.arrayType(.{ | 5796 | const array_ty = try pt.arrayType(.{ |
| 5796 | .len = len, | 5797 | .len = len, |
| ... | @@ -13964,9 +13965,10 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. | ... | @@ -13964,9 +13965,10 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 13964 | 13965 | ||
| 13965 | const pt = sema.pt; | 13966 | const pt = sema.pt; |
| 13966 | const zcu = pt.zcu; | 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 | const operand_src = block.tokenOffset(inst_data.src_tok); | 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 | const result = pt.importFile(block.getFileScope(zcu), operand) catch |err| switch (err) { | 13973 | const result = pt.importFile(block.getFileScope(zcu), operand) catch |err| switch (err) { |
| 13972 | error.ImportOutsideModulePath => { | 13974 | error.ImportOutsideModulePath => { |
| ... | @@ -13983,12 +13985,42 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. | ... | @@ -13983,12 +13985,42 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 13983 | return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ operand, @errorName(err) }); | 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 }); | 13988 | switch (result.file.getMode()) { |
| 13987 | try pt.ensureFileAnalyzed(result.file_index); | 13989 | .zig => { |
| 13988 | const ty = zcu.fileRootType(result.file_index); | 13990 | try sema.declareDependency(.{ .file = result.file_index }); |
| 13989 | try sema.declareDependency(.{ .interned = ty }); | 13991 | try pt.ensureFileAnalyzed(result.file_index); |
| 13990 | try sema.addTypeReferenceEntry(operand_src, ty); | 13992 | const ty = zcu.fileRootType(result.file_index); |
| 13991 | return Air.internedToRef(ty); | 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 | fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 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,7 +270,7 @@ pub fn getUnsignedIntInner( |
| 270 | else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { | 270 | else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { |
| 271 | .undef => unreachable, | 271 | .undef => unreachable, |
| 272 | .int => |int| switch (int.storage) { | 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 | .u64 => |x| x, | 274 | .u64 => |x| x, |
| 275 | .i64 => |x| std.math.cast(u64, x), | 275 | .i64 => |x| std.math.cast(u64, x), |
| 276 | .lazy_align => |ty| (try Type.fromInterned(ty).abiAlignmentInner(strat.toLazy(), zcu, tid)).scalar.toByteUnits() orelse 0, | 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,7 +311,7 @@ pub fn toSignedInt(val: Value, zcu: *const Zcu) i64 { |
| 311 | .bool_true => 1, | 311 | .bool_true => 1, |
| 312 | else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { | 312 | else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { |
| 313 | .int => |int| switch (int.storage) { | 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 | .i64 => |x| x, | 315 | .i64 => |x| x, |
| 316 | .u64 => |x| @intCast(x), | 316 | .u64 => |x| @intCast(x), |
| 317 | .lazy_align => |ty| @intCast(Type.fromInterned(ty).abiAlignment(zcu).toByteUnits() orelse 0), | 317 | .lazy_align => |ty| @intCast(Type.fromInterned(ty).abiAlignment(zcu).toByteUnits() orelse 0), |
| ... | @@ -898,7 +898,7 @@ pub fn readFromPackedMemory( | ... | @@ -898,7 +898,7 @@ pub fn readFromPackedMemory( |
| 898 | pub fn toFloat(val: Value, comptime T: type, zcu: *Zcu) T { | 898 | pub fn toFloat(val: Value, comptime T: type, zcu: *Zcu) T { |
| 899 | return switch (zcu.intern_pool.indexToKey(val.toIntern())) { | 899 | return switch (zcu.intern_pool.indexToKey(val.toIntern())) { |
| 900 | .int => |int| switch (int.storage) { | 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 | inline .u64, .i64 => |x| { | 902 | inline .u64, .i64 => |x| { |
| 903 | if (T == f80) { | 903 | if (T == f80) { |
| 904 | @panic("TODO we can't lower this properly on non-x86 llvm backend yet"); | 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,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 | pub fn clz(val: Value, ty: Type, zcu: *Zcu) u64 { | 918 | pub fn clz(val: Value, ty: Type, zcu: *Zcu) u64 { |
| 938 | var bigint_buf: BigIntSpace = undefined; | 919 | var bigint_buf: BigIntSpace = undefined; |
| 939 | const bigint = val.toBigInt(&bigint_buf, zcu); | 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,7 +1529,7 @@ pub fn floatFromIntScalar(val: Value, float_ty: Type, pt: Zcu.PerThread, comptim |
| 1548 | .undef => try pt.undefValue(float_ty), | 1529 | .undef => try pt.undefValue(float_ty), |
| 1549 | .int => |int| switch (int.storage) { | 1530 | .int => |int| switch (int.storage) { |
| 1550 | .big_int => |big_int| { | 1531 | .big_int => |big_int| { |
| 1551 | const float = bigIntToFloat(big_int.limbs, big_int.positive); | 1532 | const float = big_int.toFloat(f128); |
| 1552 | return pt.floatValue(float_ty, float); | 1533 | return pt.floatValue(float_ty, float); |
| 1553 | }, | 1534 | }, |
| 1554 | inline .u64, .i64 => |x| floatFromIntInner(x, float_ty, pt), | 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,7 +4564,7 @@ pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMe |
| 4583 | .int => switch (ip.indexToKey(val.toIntern()).int.storage) { | 4564 | .int => switch (ip.indexToKey(val.toIntern()).int.storage) { |
| 4584 | .lazy_align, .lazy_size => unreachable, // `val` is fully resolved | 4565 | .lazy_align, .lazy_size => unreachable, // `val` is fully resolved |
| 4585 | inline .u64, .i64 => |x| std.math.cast(T, x) orelse return error.TypeMismatch, | 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 | .float => val.toFloat(T, zcu), | 4570 | .float => val.toFloat(T, zcu), |
src/Zcu.zig+37-3| ... | @@ -39,6 +39,8 @@ const AnalUnit = InternPool.AnalUnit; | ... | @@ -39,6 +39,8 @@ const AnalUnit = InternPool.AnalUnit; |
| 39 | const BuiltinFn = std.zig.BuiltinFn; | 39 | const BuiltinFn = std.zig.BuiltinFn; |
| 40 | const LlvmObject = @import("codegen/llvm.zig").Object; | 40 | const LlvmObject = @import("codegen/llvm.zig").Object; |
| 41 | const dev = @import("dev.zig"); | 41 | const dev = @import("dev.zig"); |
| 42 | const Zoir = std.zig.Zoir; | ||
| 43 | const ZonGen = std.zig.ZonGen; | ||
| 42 | 44 | ||
| 43 | comptime { | 45 | comptime { |
| 44 | @setEvalBranchQuota(4000); | 46 | @setEvalBranchQuota(4000); |
| ... | @@ -672,6 +674,8 @@ pub const File = struct { | ... | @@ -672,6 +674,8 @@ pub const File = struct { |
| 672 | tree: Ast, | 674 | tree: Ast, |
| 673 | /// Whether this is populated or not depends on `zir_loaded`. | 675 | /// Whether this is populated or not depends on `zir_loaded`. |
| 674 | zir: Zir, | 676 | zir: Zir, |
| 677 | /// Cached Zoir, generated lazily. | ||
| 678 | zoir: ?Zoir = null, | ||
| 675 | /// Module that this file is a part of, managed externally. | 679 | /// Module that this file is a part of, managed externally. |
| 676 | mod: *Package.Module, | 680 | mod: *Package.Module, |
| 677 | /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen. | 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,7 +708,19 @@ pub const File = struct { |
| 704 | root: *Package.Module, | 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 | pub fn unload(file: *File, gpa: Allocator) void { | 722 | pub fn unload(file: *File, gpa: Allocator) void { |
| 723 | if (file.zoir) |zoir| zoir.deinit(gpa); | ||
| 708 | file.unloadTree(gpa); | 724 | file.unloadTree(gpa); |
| 709 | file.unloadSource(gpa); | 725 | file.unloadSource(gpa); |
| 710 | file.unloadZir(gpa); | 726 | file.unloadZir(gpa); |
| ... | @@ -778,11 +794,24 @@ pub const File = struct { | ... | @@ -778,11 +794,24 @@ pub const File = struct { |
| 778 | if (file.tree_loaded) return &file.tree; | 794 | if (file.tree_loaded) return &file.tree; |
| 779 | 795 | ||
| 780 | const source = try file.getSource(gpa); | 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 | file.tree_loaded = true; | 798 | file.tree_loaded = true; |
| 783 | return &file.tree; | 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 | pub fn fullyQualifiedNameLen(file: File) usize { | 815 | pub fn fullyQualifiedNameLen(file: File) usize { |
| 787 | const ext = std.fs.path.extension(file.sub_file_path); | 816 | const ext = std.fs.path.extension(file.sub_file_path); |
| 788 | return file.sub_file_path.len - ext.len; | 817 | return file.sub_file_path.len - ext.len; |
| ... | @@ -895,6 +924,7 @@ pub const File = struct { | ... | @@ -895,6 +924,7 @@ pub const File = struct { |
| 895 | pub const Index = InternPool.FileIndex; | 924 | pub const Index = InternPool.FileIndex; |
| 896 | }; | 925 | }; |
| 897 | 926 | ||
| 927 | /// Represents the contents of a file loaded with `@embedFile`. | ||
| 898 | pub const EmbedFile = struct { | 928 | pub const EmbedFile = struct { |
| 899 | /// Module that this file is a part of, managed externally. | 929 | /// Module that this file is a part of, managed externally. |
| 900 | owner: *Package.Module, | 930 | owner: *Package.Module, |
| ... | @@ -2372,6 +2402,12 @@ pub const LazySrcLoc = struct { | ... | @@ -2372,6 +2402,12 @@ pub const LazySrcLoc = struct { |
| 2372 | break :inst .{ info.file, info.inst }; | 2402 | break :inst .{ info.file, info.inst }; |
| 2373 | }; | 2403 | }; |
| 2374 | const file = zcu.fileByIndex(file_index); | 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 | assert(file.zir_loaded); | 2411 | assert(file.zir_loaded); |
| 2376 | 2412 | ||
| 2377 | const zir = file.zir; | 2413 | const zir = file.zir; |
| ... | @@ -3461,8 +3497,6 @@ pub fn atomicPtrAlignment( | ... | @@ -3461,8 +3497,6 @@ pub fn atomicPtrAlignment( |
| 3461 | } | 3497 | } |
| 3462 | 3498 | ||
| 3463 | /// Returns null in the following cases: | 3499 | /// Returns null in the following cases: |
| 3464 | /// * `@TypeOf(.{})` | ||
| 3465 | /// * A struct which has no fields (`struct {}`). | ||
| 3466 | /// * Not a struct. | 3500 | /// * Not a struct. |
| 3467 | pub fn typeToStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType { | 3501 | pub fn typeToStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType { |
| 3468 | if (ty.ip_index == .none) return null; | 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,6 +1867,7 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { |
| 1867 | const zcu = pt.zcu; | 1867 | const zcu = pt.zcu; |
| 1868 | const gpa = zcu.gpa; | 1868 | const gpa = zcu.gpa; |
| 1869 | const file = zcu.fileByIndex(file_index); | 1869 | const file = zcu.fileByIndex(file_index); |
| 1870 | assert(file.getMode() == .zig); | ||
| 1870 | assert(zcu.fileRootType(file_index) == .none); | 1871 | assert(zcu.fileRootType(file_index) == .none); |
| 1871 | 1872 | ||
| 1872 | if (file.status != .success_zir) { | 1873 | if (file.status != .success_zir) { |
| ... | @@ -2022,7 +2023,9 @@ pub fn importFile( | ... | @@ -2022,7 +2023,9 @@ pub fn importFile( |
| 2022 | if (mod.deps.get(import_string)) |pkg| { | 2023 | if (mod.deps.get(import_string)) |pkg| { |
| 2023 | return pt.importPkg(pkg); | 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 | return error.ModuleNotFound; | 2029 | return error.ModuleNotFound; |
| 2027 | } | 2030 | } |
| 2028 | const gpa = zcu.gpa; | 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,8 +13776,8 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co |
| 13776 | }; | 13776 | }; |
| 13777 | const bit_count = extra.type.scalarBits(self); | 13777 | const bit_count = extra.type.scalarBits(self); |
| 13778 | const val: i64 = if (bit_count <= 64) | 13778 | const val: i64 = if (bit_count <= 64) |
| 13779 | bigint.to(i64) catch unreachable | 13779 | bigint.toInt(i64) catch unreachable |
| 13780 | else if (bigint.to(u64)) |val| | 13780 | else if (bigint.toInt(u64)) |val| |
| 13781 | @bitCast(val) | 13781 | @bitCast(val) |
| 13782 | else |_| { | 13782 | else |_| { |
| 13783 | const limbs = try record.addManyAsSlice( | 13783 | const limbs = try record.addManyAsSlice( |
| ... | @@ -14276,9 +14276,9 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co | ... | @@ -14276,9 +14276,9 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co |
| 14276 | else => unreachable, | 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 | val | 14280 | val |
| 14281 | else |_| if (bigint.to(u64)) |val| | 14281 | else |_| if (bigint.toInt(u64)) |val| |
| 14282 | @bitCast(val) | 14282 | @bitCast(val) |
| 14283 | else |_| { | 14283 | else |_| { |
| 14284 | const limbs_len = std.math.divCeil(u32, extra.bit_width, 64) catch unreachable; | 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,7 +120,7 @@ pub fn run( |
| 120 | process.exit(2); | 120 | process.exit(2); |
| 121 | } | 121 | } |
| 122 | } else { | 122 | } else { |
| 123 | const zoir = try std.zig.ZonGen.generate(gpa, tree); | 123 | const zoir = try std.zig.ZonGen.generate(gpa, tree, .{}); |
| 124 | defer zoir.deinit(gpa); | 124 | defer zoir.deinit(gpa); |
| 125 | 125 | ||
| 126 | if (zoir.hasCompileErrors()) { | 126 | if (zoir.hasCompileErrors()) { |
| ... | @@ -335,7 +335,7 @@ fn fmtPathFile( | ... | @@ -335,7 +335,7 @@ fn fmtPathFile( |
| 335 | } | 335 | } |
| 336 | }, | 336 | }, |
| 337 | .zon => { | 337 | .zon => { |
| 338 | var zoir = try std.zig.ZonGen.generate(gpa, tree); | 338 | var zoir = try std.zig.ZonGen.generate(gpa, tree, .{}); |
| 339 | defer zoir.deinit(gpa); | 339 | defer zoir.deinit(gpa); |
| 340 | 340 | ||
| 341 | if (zoir.hasCompileErrors()) { | 341 | if (zoir.hasCompileErrors()) { |
src/main.zig+1-1| ... | @@ -6278,7 +6278,7 @@ fn cmdAstCheck( | ... | @@ -6278,7 +6278,7 @@ fn cmdAstCheck( |
| 6278 | } | 6278 | } |
| 6279 | }, | 6279 | }, |
| 6280 | .zon => { | 6280 | .zon => { |
| 6281 | const zoir = try ZonGen.generate(gpa, file.tree); | 6281 | const zoir = try ZonGen.generate(gpa, file.tree, .{}); |
| 6282 | defer zoir.deinit(gpa); | 6282 | defer zoir.deinit(gpa); |
| 6283 | 6283 | ||
| 6284 | if (zoir.hasCompileErrors()) { | 6284 | if (zoir.hasCompileErrors()) { |
src/print_zir.zig+11-1| ... | @@ -488,7 +488,6 @@ const Writer = struct { | ... | @@ -488,7 +488,6 @@ const Writer = struct { |
| 488 | .enum_literal, | 488 | .enum_literal, |
| 489 | .decl_ref, | 489 | .decl_ref, |
| 490 | .decl_val, | 490 | .decl_val, |
| 491 | .import, | ||
| 492 | .ret_err_value, | 491 | .ret_err_value, |
| 493 | .ret_err_value_code, | 492 | .ret_err_value_code, |
| 494 | .param_anytype, | 493 | .param_anytype, |
| ... | @@ -515,6 +514,8 @@ const Writer = struct { | ... | @@ -515,6 +514,8 @@ const Writer = struct { |
| 515 | .declaration => try self.writeDeclaration(stream, inst), | 514 | .declaration => try self.writeDeclaration(stream, inst), |
| 516 | 515 | ||
| 517 | .extended => try self.writeExtended(stream, inst), | 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,4 +2843,13 @@ const Writer = struct { |
| 2842 | try stream.writeByte('\n'); | 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,9 +44,9 @@ comptime { |
| 44 | // | 44 | // |
| 45 | // :5:23: error: expected error union type, found 'comptime_int' | 45 | // :5:23: error: expected error union type, found 'comptime_int' |
| 46 | // :10:23: error: expected error union type, found '@TypeOf(.{})' | 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 | // :15:23: note: struct declared here | 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 | // :20:27: note: struct declared here | 50 | // :20:27: note: struct declared here |
| 51 | // :25:23: error: expected error union type, found 'struct { comptime *const [5:0]u8 = "hello" }' | 51 | // :25:23: error: expected error union type, found 'struct { comptime *const [5:0]u8 = "hello" }' |
| 52 | // :31:13: error: expected error union type, found 'u32' | 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,6 +90,11 @@ pub const Case = struct { |
| 90 | link_libc: bool = false, | 90 | link_libc: bool = false, |
| 91 | pic: ?bool = null, | 91 | pic: ?bool = null, |
| 92 | pie: ?bool = null, | 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 | deps: std.ArrayList(DepModule), | 99 | deps: std.ArrayList(DepModule), |
| 95 | 100 | ||
| ... | @@ -413,6 +418,7 @@ fn addFromDirInner( | ... | @@ -413,6 +418,7 @@ fn addFromDirInner( |
| 413 | const pic = try manifest.getConfigForKeyAssertSingle("pic", ?bool); | 418 | const pic = try manifest.getConfigForKeyAssertSingle("pic", ?bool); |
| 414 | const pie = try manifest.getConfigForKeyAssertSingle("pie", ?bool); | 419 | const pie = try manifest.getConfigForKeyAssertSingle("pie", ?bool); |
| 415 | const emit_bin = try manifest.getConfigForKeyAssertSingle("emit_bin", bool); | 420 | const emit_bin = try manifest.getConfigForKeyAssertSingle("emit_bin", bool); |
| 421 | const imports = try manifest.getConfigForKeyAlloc(ctx.arena, "imports", []const u8); | ||
| 416 | 422 | ||
| 417 | if (manifest.type == .translate_c) { | 423 | if (manifest.type == .translate_c) { |
| 418 | for (c_frontends) |c_frontend| { | 424 | for (c_frontends) |c_frontend| { |
| ... | @@ -470,7 +476,7 @@ fn addFromDirInner( | ... | @@ -470,7 +476,7 @@ fn addFromDirInner( |
| 470 | const next = ctx.cases.items.len; | 476 | const next = ctx.cases.items.len; |
| 471 | try ctx.cases.append(.{ | 477 | try ctx.cases.append(.{ |
| 472 | .name = std.fs.path.stem(filename), | 478 | .name = std.fs.path.stem(filename), |
| 473 | .target = resolved_target, | 479 | .import_path = std.fs.path.dirname(filename), |
| 474 | .backend = backend, | 480 | .backend = backend, |
| 475 | .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator), | 481 | .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator), |
| 476 | .emit_bin = emit_bin, | 482 | .emit_bin = emit_bin, |
| ... | @@ -480,6 +486,8 @@ fn addFromDirInner( | ... | @@ -480,6 +486,8 @@ fn addFromDirInner( |
| 480 | .pic = pic, | 486 | .pic = pic, |
| 481 | .pie = pie, | 487 | .pie = pie, |
| 482 | .deps = std.ArrayList(DepModule).init(ctx.cases.allocator), | 488 | .deps = std.ArrayList(DepModule).init(ctx.cases.allocator), |
| 489 | .imports = imports, | ||
| 490 | .target = resolved_target, | ||
| 483 | }); | 491 | }); |
| 484 | try cases.append(next); | 492 | try cases.append(next); |
| 485 | } | 493 | } |
| ... | @@ -619,6 +627,7 @@ pub fn lowerToBuildSteps( | ... | @@ -619,6 +627,7 @@ pub fn lowerToBuildSteps( |
| 619 | ) void { | 627 | ) void { |
| 620 | const host = std.zig.system.resolveTargetQuery(.{}) catch |err| | 628 | const host = std.zig.system.resolveTargetQuery(.{}) catch |err| |
| 621 | std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)}); | 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 | for (self.incremental_cases.items) |incr_case| { | 632 | for (self.incremental_cases.items) |incr_case| { |
| 624 | if (true) { | 633 | if (true) { |
| ... | @@ -662,11 +671,21 @@ pub fn lowerToBuildSteps( | ... | @@ -662,11 +671,21 @@ pub fn lowerToBuildSteps( |
| 662 | file_sources.put(file.path, writefiles.add(file.path, file.src)) catch @panic("OOM"); | 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 | const mod = b.createModule(.{ | 683 | const mod = b.createModule(.{ |
| 666 | .root_source_file = root_source_file, | 684 | .root_source_file = root_source_file, |
| 667 | .target = case.target, | 685 | .target = case.target, |
| 668 | .optimize = case.optimize_mode, | 686 | .optimize = case.optimize_mode, |
| 669 | }); | 687 | }); |
| 688 | |||
| 670 | if (case.link_libc) mod.link_libc = true; | 689 | if (case.link_libc) mod.link_libc = true; |
| 671 | if (case.pic) |pic| mod.pic = pic; | 690 | if (case.pic) |pic| mod.pic = pic; |
| 672 | for (case.deps.items) |dep| { | 691 | for (case.deps.items) |dep| { |
| ... | @@ -962,6 +981,8 @@ const TestManifestConfigDefaults = struct { | ... | @@ -962,6 +981,8 @@ const TestManifestConfigDefaults = struct { |
| 962 | return "null"; | 981 | return "null"; |
| 963 | } else if (std.mem.eql(u8, key, "pie")) { | 982 | } else if (std.mem.eql(u8, key, "pie")) { |
| 964 | return "null"; | 983 | return "null"; |
| 984 | } else if (std.mem.eql(u8, key, "imports")) { | ||
| 985 | return ""; | ||
| 965 | } else unreachable; | 986 | } else unreachable; |
| 966 | } | 987 | } |
| 967 | }; | 988 | }; |
| ... | @@ -998,6 +1019,7 @@ const TestManifest = struct { | ... | @@ -998,6 +1019,7 @@ const TestManifest = struct { |
| 998 | .{ "backend", {} }, | 1019 | .{ "backend", {} }, |
| 999 | .{ "pic", {} }, | 1020 | .{ "pic", {} }, |
| 1000 | .{ "pie", {} }, | 1021 | .{ "pie", {} }, |
| 1022 | .{ "imports", {} }, | ||
| 1001 | }); | 1023 | }); |
| 1002 | 1024 | ||
| 1003 | const Type = enum { | 1025 | const Type = enum { |
| ... | @@ -1020,7 +1042,7 @@ const TestManifest = struct { | ... | @@ -1020,7 +1042,7 @@ const TestManifest = struct { |
| 1020 | 1042 | ||
| 1021 | fn ConfigValueIterator(comptime T: type) type { | 1043 | fn ConfigValueIterator(comptime T: type) type { |
| 1022 | return struct { | 1044 | return struct { |
| 1023 | inner: std.mem.SplitIterator(u8, .scalar), | 1045 | inner: std.mem.TokenIterator(u8, .scalar), |
| 1024 | 1046 | ||
| 1025 | fn next(self: *@This()) !?T { | 1047 | fn next(self: *@This()) !?T { |
| 1026 | const next_raw = self.inner.next() orelse return null; | 1048 | const next_raw = self.inner.next() orelse return null; |
| ... | @@ -1098,7 +1120,9 @@ const TestManifest = struct { | ... | @@ -1098,7 +1120,9 @@ const TestManifest = struct { |
| 1098 | // Parse key=value(s) | 1120 | // Parse key=value(s) |
| 1099 | var kv_it = std.mem.splitScalar(u8, trimmed, '='); | 1121 | var kv_it = std.mem.splitScalar(u8, trimmed, '='); |
| 1100 | const key = kv_it.first(); | 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 | try manifest.config_map.putNoClobber(key, kv_it.next() orelse return error.MissingValuesForConfig); | 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,7 +1139,7 @@ const TestManifest = struct { |
| 1115 | ) ConfigValueIterator(T) { | 1139 | ) ConfigValueIterator(T) { |
| 1116 | const bytes = self.config_map.get(key) orelse TestManifestConfigDefaults.get(self.type, key); | 1140 | const bytes = self.config_map.get(key) orelse TestManifestConfigDefaults.get(self.type, key); |
| 1117 | return ConfigValueIterator(T){ | 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,6 +1256,18 @@ const TestManifest = struct { |
| 1232 | return try getDefaultParser(o.child)(str); | 1256 | return try getDefaultParser(o.child)(str); |
| 1233 | } | 1257 | } |
| 1234 | }.parse, | 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 | else => @compileError("no default parser for " ++ @typeName(T)), | 1271 | else => @compileError("no default parser for " ++ @typeName(T)), |
| 1236 | } | 1272 | } |
| 1237 | } | 1273 | } |