diff --git a/build.zig.zon b/build.zig.zon index 7a42f2c..8d2eb49 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -3,7 +3,7 @@ .version = "1.0.0", - .fingerprint = 0x255cfdbd72bde30d, + .fingerprint = 0x27a0a7c056e7482c, .minimum_zig_version = "0.15.0-dev.552+bc2f7c754", .dependencies = .{}, diff --git a/language.zig b/language.zig index 83a0aae..3826348 100644 --- a/language.zig +++ b/language.zig @@ -13,10 +13,30 @@ pub const Error = enum { Eof, TrailingComma, MissingKey, MissingValue, Unexpecte pub const JsonType = enum { null, bool, number, string, array, object }; +pub const JsonNumber = union(enum) { + int: i128, + float: f64, + + pub fn cast(self: JsonNumber, comptime T: type) T { + return switch (self) { + .int => |i| switch (@typeInfo(T)) { + .float => @as(T, @floatFromInt(i)), + .int => @as(T, @intCast(i)), + else => @compileError("not a number type"), + }, + .float => |f| switch (@typeInfo(T)) { + .float => @as(T, @floatCast(f)), + .int => @as(T, @intFromFloat(f)), + else => @compileError("not a number type"), + }, + }; + } +}; + pub const JsonValue = union(JsonType) { null: void, bool: bool, - number: f64, + number: JsonNumber, string: StringIndex, array: ArraySlice, object: ObjectEntry, @@ -25,7 +45,7 @@ pub const JsonValue = union(JsonType) { pub const JsonInput = union(JsonType) { null: void, bool: bool, - number: f64, + number: JsonNumber, string: []const u8, array: []JsonInput, object: std.StringArrayHashMapUnmanaged(JsonInput), @@ -39,9 +59,8 @@ pub const JsonInput = union(JsonType) { }, .object => |*object| { var it = object.iterator(); - while (it.next()) |entry| { + while (it.next()) |entry| entry.value_ptr.deinit(allocator); - } @constCast(object).deinit(allocator); }, else => {}, @@ -57,7 +76,10 @@ pub const JsonInput = union(JsonType) { switch (self) { .null => try writer.writeAll("null"), .bool => try writer.writeAll(if (self.bool) "true" else "false"), - .number => try writer.print("{d}", .{self.number}), + .number => switch (self.number) { + .int => try writer.print("{d}", .{self.number.int}), + .float => try writer.print("{d:.1}", .{self.number.float}), + }, .string => try writer.print("\"{s}\"", .{self.string}), .array => { try writer.writeByte('['); @@ -127,7 +149,7 @@ pub fn deinit(self: *Self, allocator: mem.Allocator) void { self.property_map.deinit(allocator); } -fn addNumber(self: *Self, allocator: mem.Allocator, number: f64) !usize { +fn addNumber(self: *Self, allocator: mem.Allocator, number: JsonNumber) !usize { try self.index.ensureUnusedCapacity(allocator, 1); const idx = self.index.addOneAssumeCapacity(); self.index.set(idx, .{ .number = number }); @@ -382,7 +404,6 @@ pub fn parse(self: *Self, allocator: mem.Allocator, tokenizer: *Tokenizer) !usiz } }, .true, .false => { - defer tokenizer.skipWhitespace(); const idx = try self.addBool(allocator, if (token.type == .true) true else false); if (query.len == 0) { @@ -445,16 +466,28 @@ pub fn parse(self: *Self, allocator: mem.Allocator, tokenizer: *Tokenizer) !usiz }, } }, - .number => { + .int, .float => |number| { if (query.len == 0) { // root - _ = try self.addNumber(allocator, token.value.?.number); - self.index.set(root, .{ .number = token.value.?.number }); + _ = switch (number) { + .int => try self.addNumber(allocator, .{ .int = token.value.?.int }), + .float => try self.addNumber(allocator, .{ .float = token.value.?.float }), + else => unreachable, + }; + self.index.set(root, .{ .number = switch (number) { + .int => .{ .int = token.value.?.int }, + .float => .{ .float = token.value.?.float }, + else => unreachable, + } }); return root; } const parent_idx = query.get(query.len - 1); - const idx = try self.addNumber(allocator, token.value.?.number); + const idx = switch (number) { + .int => try self.addNumber(allocator, .{ .int = token.value.?.int }), + .float => try self.addNumber(allocator, .{ .float = token.value.?.float }), + else => unreachable, + }; switch (self.index.get(parent_idx)) { .array => |slice| { self.index.set(parent_idx, .{ .array = ArraySlice{ diff --git a/reflection.zig b/reflection.zig index e46ee37..94841d4 100644 --- a/reflection.zig +++ b/reflection.zig @@ -58,23 +58,26 @@ pub fn reflectT(self: *Self, comptime T: type, allocator: mem.Allocator, idx: us else => return error.TypeError, }, .number => |number| switch (Schema) { - .int, .comptime_int => return @intFromFloat(number), - .float, .comptime_float => return @floatCast(number), + .int, .comptime_int => return @intCast(number.int), + .float, .comptime_float => return switch (number) { + .float => @floatCast(number.float), + .int => @floatFromInt(number.int), + }, .@"enum" => |enumInfo| { - const int: enumInfo.tag_type = @intFromFloat(number); + const int: enumInfo.tag_type = @intCast(number.int); return @enumFromInt(int); }, .@"struct" => |structInfo| switch (structInfo.layout) { .@"packed" => { - const int: structInfo.backing_integer.? = @intFromFloat(number); + const int: structInfo.backing_integer.? = @intCast(number.int); return @bitCast(int); }, else => return error.TypeError, }, .@"union" => |unionInfo| { inline for (unionInfo.fields) |field| switch (@typeInfo(field.type)) { - .int, .comptime_int => return @unionInit(T, field.name, @intFromFloat(number)), - .float, .comptime_float => return @unionInit(T, field.name, @floatCast(number)), + .int, .comptime_int => return @unionInit(T, field.name, @intCast(number.int)), + .float, .comptime_float => return @unionInit(T, field.name, @floatCast(number.float)), else => {}, }; return error.TypeError; diff --git a/tokenizer.zig b/tokenizer.zig index ad26c1e..82fe3f0 100644 --- a/tokenizer.zig +++ b/tokenizer.zig @@ -20,7 +20,8 @@ pub const TokenType = enum(u8) { null, true, false, - number, + int, + float, string, property, object_begin, @@ -34,7 +35,12 @@ pub const TokenType = enum(u8) { pub const Token = struct { type: TokenType, - value: ?union { number: f64, string: []const u8, symbol: u8 }, + value: ?union { + int: i128, + float: f64, + string: []const u8, + symbol: u8, + }, start: usize, end: usize, }; @@ -217,18 +223,28 @@ pub fn nextNumber(self: *Self, allocator: mem.Allocator) Error!Token { if (self.matchChar('e') != null or self.matchChar('E') != null) { self.matchChar('+') orelse self.matchChar('-') orelse {}; while (self.matchCharRange('0', '9') != null) {} + } else { + // int found + const int = std.fmt.parseInt(i128, self.extractSlice(start), 10) catch { + return error.BadNumber; // no floating point + }; + + return Token{ + .type = .int, + .value = .{ .int = int }, + .start = start, + .end = self.currentPosition(), + }; } - // int found + // float with e found const float = std.fmt.parseFloat(f64, self.extractSlice(start)) catch { return error.BadNumber; // no floating point }; return Token{ - .type = .number, - .value = .{ - .number = float, - }, + .type = .float, + .value = .{ .float = float }, .start = start, .end = self.currentPosition(), }; @@ -246,10 +262,8 @@ pub fn nextNumber(self: *Self, allocator: mem.Allocator) Error!Token { }; return .{ - .type = .number, - .value = .{ - .number = float, - }, + .type = .float, + .value = .{ .float = float }, .start = start, .end = self.currentPosition(), };