Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 71 additions & 1 deletion src/JSON.mo
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Char "mo:base-0.7.3/Char";
import Iter "mo:base-0.7.3/Iter";
import Int "mo:base-0.7.3/Int";
import Float "mo:base-0.7.3/Float";
import List "mo:base-0.7.3/List";
import Nat8 "mo:base-0.7.3/Nat8";
import Nat32 "mo:base-0.7.3/Nat32";
Expand All @@ -13,7 +14,8 @@ import P "mo:parser-combinators/Parser";

module JSON {
public type JSON = {
#Number : Int; // TODO: float
#Number : Int;
#Float : Float;
#String : Text;
#Array : [JSON];
#Object : [(Text, JSON)];
Expand All @@ -23,6 +25,7 @@ module JSON {

public func show(json : JSON) : Text = switch (json) {
case (#Number(v)) { Int.toText(v); };
case (#Float(v)) { Float.format(#exact, v); };
case (#String(v)) { "\"" # v # "\""; };
case (#Array(v)) {
var s = "[";
Expand Down Expand Up @@ -103,6 +106,7 @@ module JSON {
objectParser(),
arrayParser(),
stringParser(),
floatParser(),
numberParser(),
boolParser(),
nullParser()
Expand Down Expand Up @@ -175,6 +179,72 @@ module JSON {
}
);

private func listToNat(list : List.List<Char>) : Nat {
var n = 0;

for (c in Iter.fromList(list)) {
n := n * 10 + Nat32.toNat(Char.toNat32(c) - Char.toNat32('0'));
};

n;
};

private func floatParser() : P.Parser<Char, JSON> = C.map(
C.oneOf([
parseFloatWithExp(),
parseFloat(),
]),
func(n : Float) : JSON = #Float(n),
);

private func parseFloat() : P.Parser<Char, Float> {
C.map(
C.seq<Char, Int, List.List<Char>>(
C.Int.int(),
C.right(
C.Character.char('.'),
C.many1(C.Character.digit()),
),
),
func((n, decimal_list) : (Int, List.List<Char>)) : Float {
let n_of_decimals = Float.fromInt(List.size(decimal_list));

let num = Float.fromInt(n);
let decimals = Float.fromInt(listToNat(decimal_list)) / (10 ** n_of_decimals);

let isNegative = num < 0;

let float = if (isNegative) {
num - decimals;
} else {
num + decimals;
};

float;
},
);
};

private func parseFloatWithExp() : P.Parser<Char, Float> = C.map(
C.seq(
C.oneOf([
parseFloat(),
C.map(
C.Int.int(),
func(i : Int) : Float = Float.fromInt(i),
),
]),
C.right(
C.oneOf([C.String.string("e"), C.String.string("E")]),
C.Int.int(),
),
),
func((n, exponent) : (Float, Int)) : Float {
let exp = Float.fromInt(exponent);
n * (10 ** exp);
},
);

private func numberParser() : P.Parser<Char, JSON> = C.map(
C.Int.int(),
func (i : Int) : JSON {
Expand Down
26 changes: 18 additions & 8 deletions test/Show.mo
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import JSON "../src/JSON";
import Text "mo:base-0.7.3/Text";
import Debug "mo:base-0.7.3/Debug";

assert(JSON.show(#String("hello")) == "\"hello\"");
assert(JSON.show(#Number(1)) == "1");
assert(JSON.show(#Number(-1)) == "-1");
assert (JSON.show(#String("hello")) == "\"hello\"");
assert (JSON.show(#Number(1)) == "1");
assert (JSON.show(#Number(-1)) == "-1");
assert (JSON.show(#Number(-1)) == "-1");

assert(JSON.show(#Object([
("givenName", #String("John")),
("familyName", #String("Doe")),
("favNumber", #Number(5))
])) == "{\"givenName\": \"John\", \"familyName\": \"Doe\", \"favNumber\": 5}");
assert (JSON.show(#Float(-3.14)) == "-3.1400000000000001");
assert (JSON.show(#Float(1.234e-4)) == "0.00012339999999999999");
assert (JSON.show(#Float(43e-02)) == "0.42999999999999999");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Motoko does not seem to work well with precision.


assert (
JSON.show(
#Object([
("givenName", #String("John")),
("familyName", #String("Doe")),
("favNumber", #Number(5)),
]),
) == "{\"givenName\": \"John\", \"familyName\": \"Doe\", \"favNumber\": 5}",
);
24 changes: 24 additions & 0 deletions test/Value.mo
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Debug "mo:base-0.7.3/Debug";

import JSON "../src/JSON";

switch (JSON.parse("{ }")) {
Expand Down Expand Up @@ -107,3 +109,25 @@ switch (JSON.parse(" null")) { // Test with spaces.
}
};
};

switch (JSON.parse(" 1.23")) {
// Test with spaces.
case (null) { assert (false) };
case (?v) {
switch (v) {
case (#Float(1.23)) {};
case (_) { assert (false) };
};
};
};

switch (JSON.parse(" 1.234e-4")) {
// Test with spaces.
case (null) { assert (false) };
case (?v) {
switch (v) {
case (#Float(0.000_123_400_000_000_000_02)) {};
case (_) { assert (false) };
};
};
};