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", .version = "1.0.0",
.fingerprint = 0x255cfdbd72bde30d, .fingerprint = 0x27a0a7c056e7482c,
.minimum_zig_version = "0.15.0-dev.552+bc2f7c754", .minimum_zig_version = "0.15.0-dev.552+bc2f7c754",
.dependencies = .{}, .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 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) { pub const JsonValue = union(JsonType) {
null: void, null: void,
bool: bool, bool: bool,
number: f64, number: JsonNumber,
string: StringIndex, string: StringIndex,
array: ArraySlice, array: ArraySlice,
object: ObjectEntry, object: ObjectEntry,
@ -25,7 +45,7 @@ pub const JsonValue = union(JsonType) {
pub const JsonInput = union(JsonType) { pub const JsonInput = union(JsonType) {
null: void, null: void,
bool: bool, bool: bool,
number: f64, number: JsonNumber,
string: []const u8, string: []const u8,
array: []JsonInput, array: []JsonInput,
object: std.StringArrayHashMapUnmanaged(JsonInput), object: std.StringArrayHashMapUnmanaged(JsonInput),
@ -39,9 +59,8 @@ pub const JsonInput = union(JsonType) {
}, },
.object => |*object| { .object => |*object| {
var it = object.iterator(); var it = object.iterator();
while (it.next()) |entry| { while (it.next()) |entry|
entry.value_ptr.deinit(allocator); entry.value_ptr.deinit(allocator);
}
@constCast(object).deinit(allocator); @constCast(object).deinit(allocator);
}, },
else => {}, else => {},
@ -57,7 +76,10 @@ pub const JsonInput = union(JsonType) {
switch (self) { switch (self) {
.null => try writer.writeAll("null"), .null => try writer.writeAll("null"),
.bool => try writer.writeAll(if (self.bool) "true" else "false"), .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}), .string => try writer.print("\"{s}\"", .{self.string}),
.array => { .array => {
try writer.writeByte('['); try writer.writeByte('[');
@ -127,7 +149,7 @@ pub fn deinit(self: *Self, allocator: mem.Allocator) void {
self.property_map.deinit(allocator); 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); try self.index.ensureUnusedCapacity(allocator, 1);
const idx = self.index.addOneAssumeCapacity(); const idx = self.index.addOneAssumeCapacity();
self.index.set(idx, .{ .number = number }); self.index.set(idx, .{ .number = number });
@ -382,7 +404,6 @@ pub fn parse(self: *Self, allocator: mem.Allocator, tokenizer: *Tokenizer) !usiz
} }
}, },
.true, .false => { .true, .false => {
defer tokenizer.skipWhitespace();
const idx = try self.addBool(allocator, if (token.type == .true) true else false); const idx = try self.addBool(allocator, if (token.type == .true) true else false);
if (query.len == 0) { 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) { if (query.len == 0) {
// root // root
_ = try self.addNumber(allocator, token.value.?.number); _ = switch (number) {
self.index.set(root, .{ .number = token.value.?.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; return root;
} }
const parent_idx = query.get(query.len - 1); 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)) { switch (self.index.get(parent_idx)) {
.array => |slice| { .array => |slice| {
self.index.set(parent_idx, .{ .array = ArraySlice{ 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, else => return error.TypeError,
}, },
.number => |number| switch (Schema) { .number => |number| switch (Schema) {
.int, .comptime_int => return @intFromFloat(number), .int, .comptime_int => return @intCast(number.int),
.float, .comptime_float => return @floatCast(number), .float, .comptime_float => return switch (number) {
.float => @floatCast(number.float),
.int => @floatFromInt(number.int),
},
.@"enum" => |enumInfo| { .@"enum" => |enumInfo| {
const int: enumInfo.tag_type = @intFromFloat(number); const int: enumInfo.tag_type = @intCast(number.int);
return @enumFromInt(int); return @enumFromInt(int);
}, },
.@"struct" => |structInfo| switch (structInfo.layout) { .@"struct" => |structInfo| switch (structInfo.layout) {
.@"packed" => { .@"packed" => {
const int: structInfo.backing_integer.? = @intFromFloat(number); const int: structInfo.backing_integer.? = @intCast(number.int);
return @bitCast(int); return @bitCast(int);
}, },
else => return error.TypeError, else => return error.TypeError,
}, },
.@"union" => |unionInfo| { .@"union" => |unionInfo| {
inline for (unionInfo.fields) |field| switch (@typeInfo(field.type)) { inline for (unionInfo.fields) |field| switch (@typeInfo(field.type)) {
.int, .comptime_int => return @unionInit(T, field.name, @intFromFloat(number)), .int, .comptime_int => return @unionInit(T, field.name, @intCast(number.int)),
.float, .comptime_float => return @unionInit(T, field.name, @floatCast(number)), .float, .comptime_float => return @unionInit(T, field.name, @floatCast(number.float)),
else => {}, else => {},
}; };
return error.TypeError; return error.TypeError;

View File

@ -20,7 +20,8 @@ pub const TokenType = enum(u8) {
null, null,
true, true,
false, false,
number, int,
float,
string, string,
property, property,
object_begin, object_begin,
@ -34,7 +35,12 @@ pub const TokenType = enum(u8) {
pub const Token = struct { pub const Token = struct {
type: TokenType, type: TokenType,
value: ?union { number: f64, string: []const u8, symbol: u8 }, value: ?union {
int: i128,
float: f64,
string: []const u8,
symbol: u8,
},
start: usize, start: usize,
end: 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) { if (self.matchChar('e') != null or self.matchChar('E') != null) {
self.matchChar('+') orelse self.matchChar('-') orelse {}; self.matchChar('+') orelse self.matchChar('-') orelse {};
while (self.matchCharRange('0', '9') != null) {} 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 { const float = std.fmt.parseFloat(f64, self.extractSlice(start)) catch {
return error.BadNumber; // no floating point return error.BadNumber; // no floating point
}; };
return Token{ return Token{
.type = .number, .type = .float,
.value = .{ .value = .{ .float = float },
.number = float,
},
.start = start, .start = start,
.end = self.currentPosition(), .end = self.currentPosition(),
}; };
@ -246,10 +262,8 @@ pub fn nextNumber(self: *Self, allocator: mem.Allocator) Error!Token {
}; };
return .{ return .{
.type = .number, .type = .float,
.value = .{ .value = .{ .float = float },
.number = float,
},
.start = start, .start = start,
.end = self.currentPosition(), .end = self.currentPosition(),
}; };