add better numbers

This commit is contained in:
yuzu 2025-05-31 16:48:45 -05:00
parent 07bce5ef28
commit b0a1735145
4 changed files with 79 additions and 29 deletions

View File

@ -3,7 +3,7 @@
.version = "1.0.0",
.fingerprint = 0x255cfdbd72bde30d,
.fingerprint = 0x27a0a7c056e7482c,
.minimum_zig_version = "0.15.0-dev.552+bc2f7c754",
.dependencies = .{},

View File

@ -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{

View File

@ -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;

View File

@ -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(),
};