Skip to content

Commit f93259b

Browse files
authored
Added support for floats (#111)
* Implemented Float support * Used parseFloat to handle float precisions * Added support for minus e * Made requested changes for special floats * Removed NaN and Infinity
1 parent f13eba4 commit f93259b

File tree

4 files changed

+168
-16
lines changed

4 files changed

+168
-16
lines changed

assembly/JSON.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ class Handler {
3030
}
3131

3232
setInteger(name: string, value: i64): void {
33-
const obj = JSON.Value.Number(value);
33+
const obj = JSON.Value.Integer(value);
34+
this.addValue(name, obj);
35+
}
36+
37+
setFloat(name: string, value: f64): void {
38+
const obj = JSON.Value.Float(value);
3439
this.addValue(name, obj);
3540
}
3641

@@ -109,9 +114,15 @@ export namespace JSON {
109114
static String(str: string): Str {
110115
return new Str(str);
111116
}
112-
static Number(num: i64): Num {
117+
static Number(num: f64): Num {
113118
return new Num(num);
114119
}
120+
static Float(num: f64): Float {
121+
return new Float(num);
122+
}
123+
static Integer(num: i64): Integer {
124+
return new Integer(num);
125+
}
115126
static Bool(b: bool): Bool {
116127
return new Bool(b);
117128
}
@@ -159,6 +170,19 @@ export namespace JSON {
159170
}
160171

161172
export class Num extends Value {
173+
constructor(public _num: f64) {
174+
super();
175+
}
176+
177+
toString(): string {
178+
return this._num.toString();
179+
}
180+
}
181+
182+
export class Float extends Num {
183+
}
184+
185+
export class Integer extends Value {
162186
constructor(public _num: i64) {
163187
super();
164188
}
@@ -265,7 +289,10 @@ export namespace JSON {
265289
return Value.Bool(<bool>val);
266290
}
267291
if (isInteger<T>(val)) {
268-
return Value.Number(val);
292+
return Value.Integer(val);
293+
}
294+
if (isFloat<T>(val)) {
295+
return Value.Float(val);
269296
}
270297
if (isString<T>(val)) {
271298
return Value.String(<string>val);

assembly/__tests__/roundtrip.spec.ts

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,41 @@ describe("Round trip", () => {
3232
roundtripTest('{"int":4660}');
3333
});
3434

35+
it("should handle float32", () => {
36+
// expectFn(():void => {
37+
roundtripTest('{"float":24.24}');
38+
});
39+
3540
it("should handle int32Sign", () => {
3641
roundtripTest('{"int":-4660}');
3742
});
3843

44+
it("should handle float32Sign", () => {
45+
roundtripTest('{"float":-24.24}');
46+
});
47+
48+
it("should handle scientific notation float", () => {
49+
// Lower and Upper E
50+
roundtripTest(
51+
'{"floatLowerE":1.23456e5,"floatUpperE":1.23456E5}',
52+
'{"floatLowerE":123456.0,"floatUpperE":123456.0}'
53+
);
54+
55+
// Complex Scientific Notation
56+
roundtripTest(
57+
'{"floatEMinus":123456e-5,"floatEPlus":1.23456E+5}',
58+
'{"floatEMinus":1.23456,"floatEPlus":123456.0}'
59+
);
60+
});
61+
62+
it("should handle special floats", () => {
63+
roundtripTest(
64+
'{"negativeZero":-0}',
65+
'{"negativeZero":0.0}',
66+
);
67+
});
68+
69+
3970
it("should handle true", () => {
4071
roundtripTest('{"val":true}');
4172
});
@@ -117,8 +148,48 @@ describe("JSON.parse", () => {
117148

118149
describe("Primitive Values", () => {
119150
it("should handle numbers", () => {
120-
expect((<JSON.Num>JSON.parse("123456789"))._num).toStrictEqual(
121-
(<JSON.Num>JSON.from(123456789))._num
151+
expect((<JSON.Num>JSON.parse("123456789.0"))._num).toStrictEqual(
152+
(<JSON.Num>JSON.from(123456789.0))._num
153+
);
154+
});
155+
156+
it("should handle floats", () => {
157+
expect((<JSON.Float>JSON.parse("123456789.0"))._num).toStrictEqual(
158+
(<JSON.Float>JSON.from(123456789.0))._num
159+
);
160+
});
161+
162+
it("should handle scientific notation floats", () => {
163+
// Supports lower e
164+
expect((<JSON.Float>JSON.parse("1.23456e5"))._num).toStrictEqual(
165+
(<JSON.Float>JSON.from(123456.0))._num
166+
);
167+
168+
// Supports Upper e
169+
expect((<JSON.Float>JSON.parse("1.23456E5"))._num).toStrictEqual(
170+
(<JSON.Float>JSON.from(123456.0))._num
171+
);
172+
173+
// Supports Complex +
174+
expect((<JSON.Float>JSON.parse("1.23456e+5"))._num).toStrictEqual(
175+
(<JSON.Float>JSON.from(123456.0))._num
176+
);
177+
178+
// Supports Complex -
179+
expect((<JSON.Float>JSON.parse("123456E-5"))._num).toStrictEqual(
180+
(<JSON.Float>JSON.from(1.23456))._num
181+
);
182+
});
183+
184+
it("should handle special floats", () => {
185+
expect((<JSON.Float>JSON.parse("-0"))._num).toStrictEqual(
186+
(<JSON.Float>JSON.from(-0.0))._num
187+
);
188+
});
189+
190+
it("should handle integers", () => {
191+
expect((<JSON.Integer>JSON.parse("123456789"))._num).toStrictEqual(
192+
(<JSON.Integer>JSON.from(123456789))._num
122193
);
123194
});
124195

assembly/decoder.ts

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export abstract class JSONHandler {
1313

1414
setInteger(name: string, value: i64): void {}
1515

16+
setFloat(name: string, value: f64): void {}
17+
1618
pushArray(name: string): bool {
1719
return true;
1820
}
@@ -55,6 +57,14 @@ export class ThrowingJSONHandler extends JSONHandler {
5557
);
5658
}
5759

60+
setFloat(name: string, value: f64): void {
61+
// @ts-ignore integer does have toString
62+
assert(
63+
false,
64+
"Unexpected float field " + name + " : " + value.toString()
65+
);
66+
}
67+
5868
pushArray(name: string): bool {
5969
assert(false, "Unexpected array field " + name);
6070
return true;
@@ -80,6 +90,16 @@ export class ThrowingJSONHandler extends JSONHandler {
8090
@lazy const CHAR_A: i32 = 65; // "A".charCodeAt(0);
8191
// @ts-ignore: decorator
8292
@lazy const CHAR_A_LOWER: i32 = 97; // "a".charCodeAt(0);
93+
// @ts-ignore: decorator
94+
@lazy const CHAR_PERIOD: i32 = 46; // ".".charCodeAt(0);
95+
// @ts-ignore: decorator
96+
@lazy const CHAR_MINUS: i32 = 45; // "-".charCodeAt(0);
97+
// @ts-ignore: decorator
98+
@lazy const CHAR_PLUS: i32 = 43; // "+".charCodeAt(0);
99+
// @ts-ignore: decorator
100+
@lazy const CHAR_E: i32 = 69; // "E".charCodeAt(0);
101+
// @ts-ignore: decorator
102+
@lazy const CHAR_E_LOWER: i32 = 101; // "e".charCodeAt(0);
83103

84104
export class DecoderState {
85105
lastKey: string = "";
@@ -301,22 +321,47 @@ export class JSONDecoder<JSONHandlerT extends JSONHandler> {
301321
}
302322

303323
private parseNumber(): bool {
304-
// TODO: Parse floats
305-
let number: i64 = 0;
306-
let sign: i64 = 1;
307-
if (this.peekChar() == "-".charCodeAt(0)) {
324+
let number: f64 = 0;
325+
let sign: f64 = 1;
326+
let isFloat: boolean = false;
327+
// Also keeping the number as a string, because we will want to use the
328+
// AS parseFloat as it handles precision best.
329+
let numberAsString: string = "";
330+
331+
if (this.peekChar() == CHAR_MINUS) {
308332
sign = -1;
309-
this.readChar();
333+
numberAsString += String.fromCharCode(this.readChar());
310334
}
311335
let digits = 0;
312-
while (CHAR_0 <= this.peekChar() && this.peekChar() <= CHAR_9) {
313-
let byte = this.readChar();
314-
number *= 10;
315-
number += byte - CHAR_0;
316-
digits++;
336+
while (
337+
(CHAR_0 <= this.peekChar() && this.peekChar() <= CHAR_9) ||
338+
CHAR_PERIOD == this.peekChar() ||
339+
CHAR_MINUS == this.peekChar() ||
340+
CHAR_PLUS == this.peekChar() ||
341+
CHAR_E == this.peekChar() ||
342+
CHAR_E_LOWER == this.peekChar()
343+
) {
344+
345+
let charCode = this.readChar();
346+
numberAsString += String.fromCharCode(charCode);
347+
348+
if (charCode == CHAR_E || charCode == CHAR_E_LOWER || charCode == CHAR_PERIOD || charCode == CHAR_PLUS || charCode == CHAR_MINUS) {
349+
isFloat = true;
350+
} else {
351+
if (!isFloat) {
352+
let value: f64 = charCode - CHAR_0;
353+
number *= 10;
354+
number += value;
355+
}
356+
digits++;
357+
}
317358
}
318359
if (digits > 0) {
319-
this.handler.setInteger(this.state.lastKey, number * sign);
360+
if (isFloat || numberAsString == "-0") {
361+
this.handler.setFloat(this.state.lastKey, parseFloat(numberAsString));
362+
} else {
363+
this.handler.setInteger(this.state.lastKey, <i64>(number * sign));
364+
}
320365
return true;
321366
}
322367
return false;

assembly/encoder.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ export class JSONEncoder {
4343
this.writeInteger(value);
4444
}
4545

46+
setFloat(name: string | null, value: f64): void {
47+
this.writeKey(name);
48+
this.writeFloat(value);
49+
}
50+
4651
pushArray(name: string | null): bool {
4752
this.writeKey(name);
4853
this.write("[");
@@ -123,6 +128,10 @@ export class JSONEncoder {
123128
this.write(value.toString());
124129
}
125130

131+
private writeFloat(value: f64): void {
132+
this.write(value.toString());
133+
}
134+
126135
private write(str: string): void {
127136
this.result.push(str);
128137
}

0 commit comments

Comments
 (0)