Skip to content

Commit

Permalink
add JSON encoding/decoding functions
Browse files Browse the repository at this point in the history
  • Loading branch information
yawaramin committed Nov 26, 2021
1 parent d75a60e commit 08c5718
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 2 deletions.
1 change: 1 addition & 0 deletions decimal.opam
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ doc: "https://yawaramin.github.io/ocaml-decimal/api"
bug-reports: "https://github.com/yawaramin/ocaml-decimal/issues"
depends: [
"dune" {>= "2.7"}
"alcotest" {>= "1.5.0" & < "2.0.0" & with-test}
"angstrom" {>= "0.15.0" & < "1.0.0" & with-test}
"ocaml" {>= "4.08.0"}
"zarith" {>= "1.10" & < "2.0.0"}
Expand Down
3 changes: 2 additions & 1 deletion dune-project
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(lang dune 2.7)

(name decimal)
(version v0.1.1)
(version v0.3.0)
(generate_opam_files true)
(license PSF-2.0)
(authors "Yawar Amin <[email protected]>")
Expand All @@ -15,6 +15,7 @@
the Python decimal module.")
(documentation "https://yawaramin.github.io/ocaml-decimal/api")
(depends
(alcotest (and (>= 1.5.0) (< 2.0.0) :with-test))
(angstrom (and (>= 0.15.0) (< 1.0.0) :with-test))
(ocaml (>= 4.08.0))
(zarith (and (>= 1.10) (< 2.0.0)))))
18 changes: 18 additions & 0 deletions lib/decimal.ml
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,22 @@ let of_float ?(context= !Context.default) value =
| _ ->
Context.raise ~msg:(Sign.to_string sign ^ str) Conversion_syntax context

let of_yojson = function
| `Int i ->
Ok (of_int i)
| `Float f ->
begin match of_float f with
| t -> Ok t
| exception Invalid_argument msg -> Error msg
end
| `String s ->
begin match of_string s with
| t -> Ok t
| exception Invalid_argument msg -> Error msg
end
| _ ->
Error "of_yojson: invalid argument"

let to_bool = function Finite { coef = "0"; _ } -> false | _ -> true

let to_string ?(eng=false) ?(context= !Context.default) = function
Expand Down Expand Up @@ -479,6 +495,8 @@ let to_string ?(eng=false) ?(context= !Context.default) = function
in
Sign.to_string sign ^ intpart ^ fracpart ^ exp

let to_yojson t = `String (to_string t)

let pp f t = t |> to_string |> Format.pp_print_string f

let z10 = Calc.z10
Expand Down
18 changes: 18 additions & 0 deletions lib/decimal.mli
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,17 @@ val of_bigint : Z.t -> t
val of_int : int -> t
val of_string : ?context:Context.t -> string -> t

val of_yojson :
[> `Int of int | `Float of float | `String of string] -> (t, string) result
(** [of_yojson json] is the result of parsing a JSON value into a decimal:
- integer is parsed
- float is parsed with the usual caveat about float imprecision
- string is parsed
- anything else fails to parse
@since 0.3.0 *)

val of_float : ?context:Context.t -> float -> t
[@@alert lossy "Suffers from floating-point precision loss. Other constructors should be preferred."]
(** [of_float ?context float] is the decimal representation of the [float]. This
Expand All @@ -318,6 +329,13 @@ val to_bigint : t -> Z.t
val to_bool : t -> bool
val to_rational : t -> Q.t
val to_string : ?eng:bool -> ?context:Context.t -> t -> string

val to_yojson : t -> [> `String of string]
(** [to_yojson t] is the JSON representation of decimal value [t]. Note that it
is encoded as a string to avoid losing precision.
@since 0.3.0 *)

val pp : Format.formatter -> t -> unit

val to_tuple : t -> int * string * int
Expand Down
2 changes: 2 additions & 0 deletions test/decimal_test.ml
Original file line number Diff line number Diff line change
Expand Up @@ -196,5 +196,7 @@ let () =
"data/remainder.decTest";
"data/subtract.decTest";
];
print_endline "";
Json.test ();

print_endline "\nOK."
2 changes: 1 addition & 1 deletion test/dune
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(test
(name decimal_test)
(deps (glob_files data/*.decTest))
(libraries angstrom decimal))
(libraries alcotest angstrom decimal))
62 changes: 62 additions & 0 deletions test/json.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
open Alcotest

let decimal = (module Decimal : TESTABLE with type t = Decimal.t)

let test () =
run "Decimal" [
"of_yojson", [
test_case "int" `Quick begin fun () ->
`Int 0
|> Decimal.of_yojson
|> Result.get_ok
|> check decimal "0" Decimal.zero
end;

test_case "int" `Quick begin fun () ->
`Int ~-1
|> Decimal.of_yojson
|> Result.get_ok
|> check decimal "-1" (Decimal.of_int ~-1)
end;

test_case "float" `Quick begin[@alert "-lossy"] fun () ->
`Float 1.
|> Decimal.of_yojson
|> Result.get_ok
|> check decimal "1.0" (Decimal.of_float 1.)
end;

test_case "float" `Quick begin[@alert "-lossy"] fun () ->
let nan = "NaN" in
`Float Float.nan
|> Decimal.of_yojson
|> Result.get_ok
|> Decimal.to_string
|> check string nan nan
end;

test_case "string" `Quick begin fun () ->
let pi = "3.14159" in
`String pi
|> Decimal.of_yojson
|> Result.get_ok
|> check decimal pi (Decimal.of_string pi)
end;

test_case "other" `Quick begin fun () ->
`Null
|> Decimal.of_yojson
|> Result.get_error
|> check string "null" "of_yojson: invalid argument"
end;
];

"to_yojson", [
test_case "pi" `Quick begin fun () ->
let pi = "3.14159" in
match pi |> Decimal.of_string |> Decimal.to_yojson with
| `String s -> check string pi pi s
| _ -> fail "to_yojson must always produce a string"
end;
];
]

0 comments on commit 08c5718

Please sign in to comment.