lang: rename signed integer types sN -> iN
Surface rename of the signed integer family: s1..s64 become i1..i64
(u1..u64, usize, isize unchanged). 'string' keeps the s-prefix arm in
name classification; width parsing moves to the i-prefix arm next to
isize.
Internal TypeId tags follow the surface (.s8/.s16/.s32/.s64 ->
.i8/.i16/.i32/.i64), as do mono-key mangle fragments (ptr_i64,
tu_i64_bool) and all display/diagnostic formatting (i{d}).
Migrated in the same sweep: stdlib + examples + issue repros + FFI C
companions (shared symbol names like ffi_id_i64), expected
stdout/stderr/ir snapshots, specs.md, readme.md, CLAUDE.md/AGENTS.md,
implementation_plan.md, docs/, issue writeups. Vendored stb_image and
historical flow state left untouched.
zig build test: 426/426; examples suite: 595/595.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
// This module delivers the JSON VALUE MODEL, the WRITER, and the READER
|
||||
// (parser). The model is built once and shared by both directions.
|
||||
//
|
||||
// NUMBERS ARE INTEGERS ONLY (s64) for this milestone — there is no
|
||||
// NUMBERS ARE INTEGERS ONLY (i64) for this milestone — there is no
|
||||
// fraction or exponent. A JSON value is one of: null, bool, integer,
|
||||
// string, array, object. The reader REJECTS a fraction or exponent
|
||||
// (`error.BadNumber`) rather than silently truncating it.
|
||||
@@ -82,7 +82,7 @@ JsonError :: error { Overflow, Io }
|
||||
Value :: enum {
|
||||
null_;
|
||||
bool_: bool;
|
||||
int_: s64;
|
||||
int_: i64;
|
||||
str: string; // view into caller-owned bytes; not copied
|
||||
array: Array;
|
||||
object: Object;
|
||||
@@ -100,8 +100,8 @@ Member :: struct {
|
||||
// allocator explicitly.
|
||||
Array :: struct {
|
||||
items: [*]Value = null;
|
||||
len: s64 = 0;
|
||||
cap: s64 = 0;
|
||||
len: i64 = 0;
|
||||
cap: i64 = 0;
|
||||
|
||||
// Append `v`, preserving order. Grows the backing store through the
|
||||
// explicit `alloc` when full (doubling), freeing the old buffer.
|
||||
@@ -133,8 +133,8 @@ Array :: struct {
|
||||
// explicit `alloc`.
|
||||
Object :: struct {
|
||||
items: [*]Member = null;
|
||||
len: s64 = 0;
|
||||
cap: s64 = 0;
|
||||
len: i64 = 0;
|
||||
cap: i64 = 0;
|
||||
|
||||
// Append a (key, val) pair at the end. Does not check for or merge a
|
||||
// duplicate key — insertion order is the contract; a repeated key is
|
||||
@@ -177,7 +177,7 @@ Object :: struct {
|
||||
|
||||
Sink :: struct {
|
||||
dst: []u8; // caller-owned destination (buffer mode) or staging (file mode)
|
||||
pos: s64 = 0; // bytes currently in `dst`
|
||||
pos: i64 = 0; // bytes currently in `dst`
|
||||
file: *File = null; // null => buffer mode
|
||||
|
||||
put_byte :: (self: *Sink, b: u8) -> !JsonError {
|
||||
@@ -213,7 +213,7 @@ Sink :: struct {
|
||||
// ── Writer ───────────────────────────────────────────────────────────
|
||||
|
||||
// Lowercase-hex ASCII byte for a 0..15 nibble. 48='0', 97='a'.
|
||||
hex_digit :: (n: s64) -> u8 {
|
||||
hex_digit :: (n: i64) -> u8 {
|
||||
if n < 10 then xx (n + 48) else xx (n - 10 + 97)
|
||||
}
|
||||
|
||||
@@ -224,8 +224,8 @@ write_u_escape :: (c: u8, sink: *Sink) -> !JsonError {
|
||||
try sink.put_byte(117); // 'u'
|
||||
try sink.put_byte(48); // '0'
|
||||
try sink.put_byte(48); // '0'
|
||||
try sink.put_byte(hex_digit((cast(s64) c >> 4) & 0xF));
|
||||
try sink.put_byte(hex_digit(cast(s64) c & 0xF));
|
||||
try sink.put_byte(hex_digit((cast(i64) c >> 4) & 0xF));
|
||||
try sink.put_byte(hex_digit(cast(i64) c & 0xF));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -254,12 +254,12 @@ write_string :: (s: string, sink: *Sink) -> !JsonError {
|
||||
}
|
||||
|
||||
// Emit a signed integer in decimal, no allocation. Digits are formed in a
|
||||
// stack buffer working in NEGATIVE space so s64 MIN
|
||||
// stack buffer working in NEGATIVE space so i64 MIN
|
||||
// (-9223372036854775808) — whose magnitude is not representable as a
|
||||
// positive s64 — serializes correctly.
|
||||
write_int :: (n: s64, sink: *Sink) -> !JsonError {
|
||||
// positive i64 — serializes correctly.
|
||||
write_int :: (n: i64, sink: *Sink) -> !JsonError {
|
||||
if n == 0 { try sink.put_byte(48); return; }
|
||||
tmp : [20]u8 = ---; // 19 digits + sign is the s64 worst case
|
||||
tmp : [20]u8 = ---; // 19 digits + sign is the i64 worst case
|
||||
neg := n < 0;
|
||||
v := n;
|
||||
if !neg { v = 0 - n; } // fold positives into negative space
|
||||
@@ -321,7 +321,7 @@ write_object :: (obj: Object, sink: *Sink) -> !JsonError {
|
||||
// bytes written. Raises `error.Overflow` if `dst` is too small (the
|
||||
// partial contents of `dst` are then undefined — nothing is truncated
|
||||
// silently). No allocation.
|
||||
write_to_buffer :: (v: Value, dst: []u8) -> (s64, !JsonError) {
|
||||
write_to_buffer :: (v: Value, dst: []u8) -> (i64, !JsonError) {
|
||||
sink := Sink.{ dst = dst };
|
||||
try write_value(v, @sink);
|
||||
return sink.pos;
|
||||
@@ -343,7 +343,7 @@ write_to_file :: (v: Value, file: *File, staging: []u8) -> !JsonError {
|
||||
// `parse(src, alloc)` turns a JSON document in `src` into the value model
|
||||
// above. It is the inverse of the writer for the v0 scope: objects (in
|
||||
// INSERTION ORDER), arrays, strings (with full unescaping incl. \uXXXX
|
||||
// and surrogate pairs), s64 integers, bool, null.
|
||||
// and surrogate pairs), i64 integers, bool, null.
|
||||
//
|
||||
// FAILURE SURFACING (hard contract): every malformed input raises on the
|
||||
// error channel (`!JsonParseError`) — never a bogus or default value.
|
||||
@@ -351,7 +351,7 @@ write_to_file :: (v: Value, file: *File, staging: []u8) -> !JsonError {
|
||||
// `pos` (the parser cursor) marks where the failure was detected.
|
||||
//
|
||||
// NOT SUPPORTED (rejected, not silently accepted): a fraction or exponent
|
||||
// in a number (`1.5`, `1e9`) → `BadNumber`; a number outside s64 →
|
||||
// in a number (`1.5`, `1e9`) → `BadNumber`; a number outside i64 →
|
||||
// `BadNumber`; a leading-zero integer (`01`) → `BadNumber`. An UNESCAPED
|
||||
// raw control byte (U+0000..U+001F) inside a string → `BadControlChar`
|
||||
// (RFC 8259 §7 requires those bytes to be escaped); the escaped forms
|
||||
@@ -386,17 +386,17 @@ JsonParseError :: error { UnexpectedToken, UnexpectedEnd, BadEscape, BadNumber,
|
||||
|
||||
// Lowercase/uppercase hex nibble value (0..15) of an ASCII byte; a non-hex
|
||||
// byte in a `\uXXXX` escape is a `BadEscape`.
|
||||
hex_value :: (c: u8) -> (s64, !JsonParseError) {
|
||||
if c >= 48 and c <= 57 { return (cast(s64) c) - 48; } // '0'..'9'
|
||||
if c >= 97 and c <= 102 { return (cast(s64) c) - 97 + 10; } // 'a'..'f'
|
||||
if c >= 65 and c <= 70 { return (cast(s64) c) - 65 + 10; } // 'A'..'F'
|
||||
hex_value :: (c: u8) -> (i64, !JsonParseError) {
|
||||
if c >= 48 and c <= 57 { return (cast(i64) c) - 48; } // '0'..'9'
|
||||
if c >= 97 and c <= 102 { return (cast(i64) c) - 97 + 10; } // 'a'..'f'
|
||||
if c >= 65 and c <= 70 { return (cast(i64) c) - 65 + 10; } // 'A'..'F'
|
||||
raise error.BadEscape;
|
||||
}
|
||||
|
||||
// Encode code point `cp` (already validated 0..0x10FFFF, non-surrogate) as
|
||||
// UTF-8 into `out`, returning the byte count (1..4). No bounds check: the
|
||||
// decode buffer is sized to the raw escaped span, which always dominates.
|
||||
encode_utf8 :: (cp: s64, out: [*]u8) -> s64 {
|
||||
encode_utf8 :: (cp: i64, out: [*]u8) -> i64 {
|
||||
if cp < 0x80 {
|
||||
out[0] = xx cp;
|
||||
return 1;
|
||||
@@ -424,7 +424,7 @@ encode_utf8 :: (cp: s64, out: [*]u8) -> s64 {
|
||||
// EXPLICIT allocator for composites + decoded strings.
|
||||
Parser :: struct {
|
||||
src: string;
|
||||
pos: s64 = 0;
|
||||
pos: i64 = 0;
|
||||
alloc: Allocator;
|
||||
|
||||
// Advance past JSON whitespace (space / tab / LF / CR).
|
||||
@@ -450,7 +450,7 @@ Parser :: struct {
|
||||
|
||||
// Read 4 hex digits at `i` (which must lie within [.., end)); returns
|
||||
// the 16-bit value. Fewer than 4 digits before `end` is a BadEscape.
|
||||
read_hex4 :: (self: *Parser, i: s64, end: s64) -> (s64, !JsonParseError) {
|
||||
read_hex4 :: (self: *Parser, i: i64, end: i64) -> (i64, !JsonParseError) {
|
||||
if i + 4 > end { raise error.BadEscape; }
|
||||
v := 0;
|
||||
k := 0;
|
||||
@@ -464,7 +464,7 @@ Parser :: struct {
|
||||
// Decode the escaped string body in [start, end) into `out`, returning
|
||||
// the decoded byte length. Pass 1 (in parse_string) guarantees there is
|
||||
// no dangling backslash, so the byte after every `\` is in range.
|
||||
decode_into :: (self: *Parser, start: s64, end: s64, out: [*]u8) -> (s64, !JsonParseError) {
|
||||
decode_into :: (self: *Parser, start: i64, end: i64, out: [*]u8) -> (i64, !JsonParseError) {
|
||||
di := 0;
|
||||
i := start;
|
||||
while i < end {
|
||||
@@ -544,15 +544,15 @@ Parser :: struct {
|
||||
return string.{ ptr = out, len = dlen };
|
||||
}
|
||||
|
||||
// Parse an s64 integer (optional '-', then digits). Rejects leading
|
||||
// zeros, a fraction/exponent tail, and any value outside s64 — all
|
||||
// `BadNumber`. Accumulates in NEGATIVE space so s64 MIN parses exactly.
|
||||
parse_number :: (self: *Parser) -> (s64, !JsonParseError) {
|
||||
// s64 bounds, built positionally because |MIN| is not a
|
||||
// representable positive s64 literal. `min_div10` is `MIN / 10`
|
||||
// Parse an i64 integer (optional '-', then digits). Rejects leading
|
||||
// zeros, a fraction/exponent tail, and any value outside i64 — all
|
||||
// `BadNumber`. Accumulates in NEGATIVE space so i64 MIN parses exactly.
|
||||
parse_number :: (self: *Parser) -> (i64, !JsonParseError) {
|
||||
// i64 bounds, built positionally because |MIN| is not a
|
||||
// representable positive i64 literal. `min_div10` is `MIN / 10`
|
||||
// truncated toward zero (remainder -8) — the digit loop's overflow
|
||||
// threshold. Accumulation runs in NEGATIVE space so MIN is exact.
|
||||
s64_min := 0 - 9223372036854775807 - 1;
|
||||
i64_min := 0 - 9223372036854775807 - 1;
|
||||
min_div10 := 0 - 922337203685477580;
|
||||
neg := false;
|
||||
if self.src[self.pos] == 45 { neg = true; self.pos += 1; } // '-'
|
||||
@@ -560,12 +560,12 @@ Parser :: struct {
|
||||
dstart := self.pos;
|
||||
c0 := self.src[self.pos];
|
||||
if c0 < 48 or c0 > 57 { raise error.BadNumber; }
|
||||
val : s64 = 0;
|
||||
val : i64 = 0;
|
||||
digits := 0;
|
||||
while self.pos < self.src.len {
|
||||
c := self.src[self.pos];
|
||||
if c < 48 or c > 57 { break; }
|
||||
d := (cast(s64) c) - 48;
|
||||
d := (cast(i64) c) - 48;
|
||||
if val < min_div10 { raise error.BadNumber; }
|
||||
if val == min_div10 and d > 8 { raise error.BadNumber; }
|
||||
val = val * 10 - d;
|
||||
@@ -578,7 +578,7 @@ Parser :: struct {
|
||||
if nc == 46 or nc == 101 or nc == 69 { raise error.BadNumber; } // '.' / 'e' / 'E' — ints only
|
||||
}
|
||||
if !neg {
|
||||
if val == s64_min { raise error.BadNumber; } // |MIN| not representable as +s64
|
||||
if val == i64_min { raise error.BadNumber; } // |MIN| not representable as +i64
|
||||
val = 0 - val;
|
||||
}
|
||||
return val;
|
||||
|
||||
Reference in New Issue
Block a user