Skip to content

Commit 1c898d7

Browse files
committed
parse floats with exponent and format float to the exact number of dps
1 parent badd601 commit 1c898d7

File tree

3 files changed

+135
-80
lines changed

3 files changed

+135
-80
lines changed

src/JSON.mo

Lines changed: 117 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -24,59 +24,69 @@ module JSON {
2424
};
2525

2626
public func show(json : JSON) : Text = switch (json) {
27-
case (#Number(v)) { Int.toText(v); };
28-
case (#Float(v)) { Float.format(#fix 2, v); };
29-
case (#String(v)) { "\"" # v # "\""; };
27+
case (#Number(v)) { Int.toText(v) };
28+
case (#Float(v)) { Float.format(#exact, v) };
29+
case (#String(v)) { "\"" # v # "\"" };
3030
case (#Array(v)) {
3131
var s = "[";
3232
for (i in v.vals()) {
33-
if (s != "[") { s #= ", "; };
33+
if (s != "[") { s #= ", " };
3434
s #= show(i);
3535
};
3636
s # "]";
3737
};
3838
case (#Object(v)) {
3939
var s = "{";
4040
for ((k, v) in v.vals()) {
41-
if (s != "{") { s #= ", "; };
41+
if (s != "{") { s #= ", " };
4242
s #= "\"" # k # "\"" # ": " # show(v);
4343
};
4444
s # "}";
4545
};
4646
case (#Boolean(v)) {
47-
if (v) { return "true"; };
47+
if (v) { return "true" };
4848
"false";
4949
};
50-
case (#Null) { "null"; };
50+
case (#Null) { "null" };
5151
};
5252

5353
private func character() : P.Parser<Char, Char> = C.oneOf([
54-
C.sat<Char>(func (c : Char) : Bool {
55-
c != Char.fromNat32(0x22) and c != '\\';
56-
}),
54+
C.sat<Char>(
55+
func(c : Char) : Bool {
56+
c != Char.fromNat32(0x22) and c != '\\';
57+
},
58+
),
5759
C.right(
5860
C.Character.char('\\'),
5961
C.map(
6062
C.Character.oneOf([
61-
Char.fromNat32(0x22), '\\', '/', 'b', 'f', 'n', 'r', 't',
63+
Char.fromNat32(0x22),
64+
'\\',
65+
'/',
66+
'b',
67+
'f',
68+
'n',
69+
'r',
70+
't',
6271
// TODO: u hex{4}
6372
]),
64-
func (c : Char) : Char {
73+
func(c : Char) : Char {
6574
switch (c) {
66-
case ('b') { Char.fromNat32(0x08); };
67-
case ('f') { Char.fromNat32(0x0C); };
68-
case ('n') { Char.fromNat32(0x0A); };
69-
case ('r') { Char.fromNat32(0x0D); };
70-
case ('t') { Char.fromNat32(0x09); };
71-
case (_) { c; };
75+
case ('b') { Char.fromNat32(0x08) };
76+
case ('f') { Char.fromNat32(0x0C) };
77+
case ('n') { Char.fromNat32(0x0A) };
78+
case ('r') { Char.fromNat32(0x0D) };
79+
case ('t') { Char.fromNat32(0x09) };
80+
case (_) { c };
7281
};
73-
}
74-
)
75-
)
82+
},
83+
),
84+
),
7685
]);
7786

7887
private func ignoreSpace<A>(parserA : P.Parser<Char, A>) : P.Parser<Char, A> = C.right(
79-
C.many(C.Character.space()), parserA
88+
C.many(C.Character.space()),
89+
parserA,
8090
);
8191

8292
public func parse(t : Text) : ?JSON = parseValues(L.fromText(t));
@@ -86,18 +96,20 @@ module JSON {
8696
public func parseRawASCII(data : [Nat8]) : ?JSON = parseValues(nat8sToCharList(data.vals()));
8797

8898
private func parseValues(l : List.List<Char>) : ?JSON = switch (valueParser()(l)) {
89-
case (null) { null; };
90-
case (? (x, xs)) {
99+
case (null) { null };
100+
case (?(x, xs)) {
91101
switch (xs) {
92-
case (null) { ?x; };
93-
case (_) { null; };
102+
case (null) { ?x };
103+
case (_) { null };
94104
};
95105
};
96106
};
97107

98108
private func nat8sToCharList(i : Iter.Iter<Nat8>) : List.List<Char> = switch (i.next()) {
99-
case (null) { null; };
100-
case (? v) { ?(Char.fromNat32(Nat32.fromNat(Nat8.toNat(v))), nat8sToCharList(i)); };
109+
case (null) { null };
110+
case (?v) {
111+
?(Char.fromNat32(Nat32.fromNat(Nat8.toNat(v))), nat8sToCharList(i));
112+
};
101113
};
102114

103115
private func valueParser() : P.Parser<Char, JSON> = C.bracket(
@@ -109,9 +121,9 @@ module JSON {
109121
floatParser(),
110122
numberParser(),
111123
boolParser(),
112-
nullParser()
124+
nullParser(),
113125
]),
114-
C.many(C.Character.space())
126+
C.many(C.Character.space()),
115127
);
116128

117129
private func objectParser() : P.Parser<Char, JSON> = C.map(
@@ -122,18 +134,18 @@ module JSON {
122134
C.seq(
123135
C.left(
124136
ignoreSpace(string()),
125-
ignoreSpace(C.Character.char(':'))
137+
ignoreSpace(C.Character.char(':')),
126138
),
127-
P.delay(valueParser)
139+
P.delay(valueParser),
128140
),
129-
C.Character.char(',')
130-
)
141+
C.Character.char(','),
142+
),
131143
),
132-
C.Character.char('}')
144+
C.Character.char('}'),
133145
),
134-
func (t : List.List<(Text, JSON)>) : JSON {
146+
func(t : List.List<(Text, JSON)>) : JSON {
135147
#Object(List.toArray(t));
136-
}
148+
},
137149
);
138150

139151
private func arrayParser() : P.Parser<Char, JSON> = C.map(
@@ -142,53 +154,43 @@ module JSON {
142154
ignoreSpace(
143155
C.sepBy(
144156
P.delay(valueParser),
145-
C.Character.char(',')
157+
C.Character.char(','),
146158
),
147159
),
148-
C.Character.char(']')
160+
C.Character.char(']'),
149161
),
150-
func (t : List.List<JSON>) : JSON {
162+
func(t : List.List<JSON>) : JSON {
151163
#Array(List.toArray(t));
152-
}
164+
},
153165
);
154166

155167
private func string() : P.Parser<Char, Text> = C.map(
156168
C.bracket(
157169
C.Character.char(Char.fromNat32(0x22)),
158170
C.many(character()),
159-
C.Character.char(Char.fromNat32(0x22))
171+
C.Character.char(Char.fromNat32(0x22)),
160172
),
161-
func (t : List.List<Char>) : Text {
173+
func(t : List.List<Char>) : Text {
162174
Text.fromIter(L.toIter(t));
163-
}
175+
},
164176
);
165177

166178
private func stringParser() : P.Parser<Char, JSON> = C.map(
167179
C.map(
168180
C.bracket(
169-
C.Character.char(Char.fromNat32(0x22)),
170-
C.many(character()),
171-
C.Character.char(Char.fromNat32(0x22))
181+
C.Character.char(Char.fromNat32(0x22)),
182+
C.many(character()),
183+
C.Character.char(Char.fromNat32(0x22)),
172184
),
173-
func (t : List.List<Char>) : Text {
185+
func(t : List.List<Char>) : Text {
174186
Text.fromIter(L.toIter(t));
175187
},
176188
),
177-
func (t : Text) : JSON {
189+
func(t : Text) : JSON {
178190
#String(t);
179-
}
191+
},
180192
);
181193

182-
private func parseFloat() : P.Parser<Char, (Int, List.List<Char>)> {
183-
C.seq<Char, Int, List.List<Char>>(
184-
C.Int.int(),
185-
C.right(
186-
C.Character.char('.'),
187-
C.many1(C.Character.digit()),
188-
),
189-
);
190-
};
191-
192194
private func listToNat(list : List.List<Char>) : Nat {
193195
var n = 0;
194196

@@ -200,47 +202,83 @@ module JSON {
200202
};
201203

202204
private func floatParser() : P.Parser<Char, JSON> = C.map(
203-
parseFloat(),
204-
func((n, decimal_list) : (Int, List.List<Char>)) : JSON {
205-
let n_of_decimals = Float.fromInt(List.size(decimal_list));
205+
C.oneOf([
206+
parseFloatWithExp(),
207+
parseFloat(),
208+
]),
209+
func(n : Float) : JSON = #Float(n),
210+
);
206211

207-
let num = Float.fromInt(n);
208-
let decimals = Float.fromInt(listToNat(decimal_list)) / (10 ** n_of_decimals);
212+
private func parseFloat() : P.Parser<Char, Float> {
213+
C.map(
214+
C.seq<Char, Int, List.List<Char>>(
215+
C.Int.int(),
216+
C.right(
217+
C.Character.char('.'),
218+
C.many1(C.Character.digit()),
219+
),
220+
),
221+
func((n, decimal_list) : (Int, List.List<Char>)) : Float {
222+
let n_of_decimals = Float.fromInt(List.size(decimal_list));
209223

210-
let isNegative = num < 0;
224+
let num = Float.fromInt(n);
225+
let decimals = Float.fromInt(listToNat(decimal_list)) / (10 ** n_of_decimals);
211226

212-
let float = if (isNegative) {
213-
num - decimals;
214-
} else {
215-
num + decimals;
216-
};
227+
let isNegative = num < 0;
228+
229+
let float = if (isNegative) {
230+
num - decimals;
231+
} else {
232+
num + decimals;
233+
};
234+
235+
float;
236+
},
237+
);
238+
};
217239

218-
#Float(float);
240+
private func parseFloatWithExp() : P.Parser<Char, Float> = C.map(
241+
C.seq(
242+
C.oneOf([
243+
parseFloat(),
244+
C.map(
245+
C.Int.int(),
246+
func(i : Int) : Float = Float.fromInt(i),
247+
),
248+
]),
249+
C.right(
250+
C.oneOf([C.String.string("e"), C.String.string("E")]),
251+
C.Int.int(),
252+
),
253+
),
254+
func((n, exponent) : (Float, Int)) : Float {
255+
let exp = Float.fromInt(exponent);
256+
n * (10 ** exp);
219257
},
220258
);
221259

222260
private func numberParser() : P.Parser<Char, JSON> = C.map(
223261
C.Int.int(),
224-
func (i : Int) : JSON {
262+
func(i : Int) : JSON {
225263
#Number(i);
226-
}
264+
},
227265
);
228266

229267
private func boolParser() : P.Parser<Char, JSON> = C.map(
230268
C.choose(
231269
C.String.string("true"),
232-
C.String.string("false")
270+
C.String.string("false"),
233271
),
234-
func (t : Text) : JSON {
272+
func(t : Text) : JSON {
235273
if (t == "true") return #Boolean(true);
236274
#Boolean(false);
237-
}
275+
},
238276
);
239277

240278
private func nullParser() : P.Parser<Char, JSON> = C.map(
241279
C.String.string("null"),
242-
func (_ : Text) : JSON {
280+
func(_ : Text) : JSON {
243281
#Null;
244-
}
282+
},
245283
);
246284
};

test/Show.mo

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import Debug "mo:base-0.7.3/Debug";
55
assert (JSON.show(#String("hello")) == "\"hello\"");
66
assert (JSON.show(#Number(1)) == "1");
77
assert (JSON.show(#Number(-1)) == "-1");
8-
assert (JSON.show(#Float(3.14)) == "3.14");
8+
assert (JSON.show(#Number(-1)) == "-1");
9+
10+
assert (JSON.show(#Float(-3.14)) == "-3.1400000000000001");
11+
assert (JSON.show(#Float(1.234e-4)) == "0.00012339999999999999");
12+
assert (JSON.show(#Float(43e-02)) == "0.42999999999999999");
913

1014
assert (
1115
JSON.show(

test/Value.mo

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import Debug "mo:base-0.7.3/Debug";
2+
13
import JSON "../src/JSON";
24

35
switch (JSON.parse("{ }")) {
@@ -117,4 +119,15 @@ switch (JSON.parse(" 1.23")) {
117119
case (_) { assert (false) };
118120
};
119121
};
122+
};
123+
124+
switch (JSON.parse(" 1.234e-4")) {
125+
// Test with spaces.
126+
case (null) { assert (false) };
127+
case (?v) {
128+
switch (v) {
129+
case (#Float(0.000_123_400_000_000_000_02)) {};
130+
case (_) { assert (false) };
131+
};
132+
};
120133
};

0 commit comments

Comments
 (0)