Tsonnet #5 – JSON چاپ زیبا

به سری TSONNET خوش آمدید!
در پست قبلی ، ما انواع عددی را دوباره اصلاح کردیم:
امروز ، من می خواهم با لایه ارائه مقابله کنم: چاپ کننده خروجی JSON.
در print
عملکرد در واقع چاپ “JSON” نیست ، فقط بازنمایی های ارزش خام.
Yojson یک انتخاب بسیار رایج برای تجزیه JSON در OCAML است.
بیایید آن را با OPAM نصب کنیم:
$ opam install yojson
و آن را به وابستگی های ما در پروژه Dune اضافه کنید:
Diff-Git A/Dune-Project B/Dune-Project INDEX 383BBEF..775351B 100644
--- A/Dune Project
+++ b/dune-project
-33،7 +33،9
(bisect_ppx (و: با آزمون
- (> = 2.8.3))))
+ (> = 2.8.3))) + (yojson + (> = 2.2.2))
(برچسب ها (کامپایلر مترجم JSONNET)))
Diff -git a/tsonnet.opam b/tsonnet.opam شاخص 31C792C..16783C3 100644
--- a/tsonnet.opam
+++ b/tsonnet.opam
-15،6 +15،7 بستگی دارد: [
"menhir" {= "20240715"}
"alcotest" {with-test & >= "1.8.0"}
"bisect_ppx" {with-test & >= "2.8.3"}
+ "yojson" {>= "2.2.2"}
"odoc" {with-doc}
]
ساخت: [
The opam file is automatically updated by dune.
Before using the library, we need to specify that the lib config will depend on yojson:
diff --git a/lib/dune b/lib/dune
index 3452131..05c4dc3 100644
--- a/lib/dune
+++ b/lib/dune
@@ -1,7 +1,8 @@
(library
(name tsonnet)
(instrumentation
- (backend bisect_ppx)))
+ (backend bisect_ppx))
+ (libraries yojson))
(menhir
(modules parser))
To get rid of the printfs, we need a way of converting a expr
to JSON.
Let’s encapsulate the expression conversion in a new module, dedicated to transforming expressions into JSON representation:
diff --git a/lib/json.ml b/lib/json.ml
new file mode 100644
index 0000000..886d2bd
--- /dev/null
+++ b/lib/json.ml
@@ -0,0 +1,17 @@
+open Ast
+
+let rec expr_to_yojson : (expr -> Yojson.t) = function
+ | Number n ->
+ (match n with
+ | Int i -> `Int i
+ | Float f -> `Float f)
+ | Null -> `Null
+ | Bool b -> `Bool b
+ | String s -> `String s
+ | Array values -> `List (List.map expr_to_yojson values)
+ | Object attrs ->
+ let eval' = fun (k, v) -> (k, expr_to_yojson v)
+ in `Assoc (List.map eval' attrs)
+ | _ -> failwith "value type not representable as JSON"
+
+let expr_to_string expr = Yojson.pretty_to_string (expr_to_yojson expr)
The new Json
module will implement two functions. The function expr_to_yojson
basically maps our expr
types to Yojson.t
. The function expr_to_string
will use the previous function to convert from Yojson.t
to string
.
If eventually we decide to change the way we want to render JSON, like using a more performant library, for example, we refactor this module and there’s no need to update anywhere else — maybe the tests.
With that, we can remove our messy hand-written print function:
diff --git a/lib/tsonnet.ml b/lib/tsonnet.ml
index 832b56c..a528488 100644
--- a/lib/tsonnet.ml
+++ b/lib/tsonnet.ml
@@ -6,24 +6,6 @@ let parse (s: string) : expr =
let ast = Parser.prog Lexer.read lexbuf in
ast
-let rec print = function
- | Number n ->
- (match n with
- | Int i -> Printf.sprintf "%d" i
- | Float f -> Printf.sprintf "%f" f)
- | Null -> Printf.sprintf "null"
- | Bool b -> Printf.sprintf "%b" b
- | String s -> Printf.sprintf "\"%s\"" s
- | Array values -> Printf.sprintf "[%s]"(string.concat" ، "(لیست. مقادیر چاپی)) - | شیء شی -> - اجازه دهید print_key_val = تابع - | (k ، v) -> printf.sprintf" \ "٪ s \": ٪ s " k (print v) - in - printf.sprintf "{٪ s}" ( - string.concat "،" (list.map print_key_val جاذبه) -) - | _ -> failwith "اجرا نشده" -
اجازه دهید تفسیر_BIN_OP (OP: BIN_OP) (N1: شماره) (N2: شماره): Expr = Match OP ، N1 ، N2 با | اضافه کردن ، (int a) ، (int b) -> شماره (int (a + b))
-52،7 +34،7 اجازه دهید تفسیر (e: expr): expr = | (شماره V1) ، (شماره V2) -> تفسیر_BIN_OP OP V1 V2 | _ -> FailWith "عملیات باینری نامعتبر"
-let Run (S: String): expr =
+اجازه دهید اجرا شود (s: string) =
اجازه دهید AST = تجزیه در let let letuated_ast = تفسیر AST در
- ارزیابی_ست
+ json.expr_to_string ارزیابی_ast
ماژول کتابخانه Tsonnet بسیار تمیز تر به نظر می رسد!
همانطور که ما در حال ساختن run
عملکرد بازگشت a string
، ما باید به روز کنیم main.ml
بر این اساس:
diff --git a/bin/main.ml b/bin/main.ml
index 78e9bb8..723b106 100644
--- a/bin/main.ml
+++ b/bin/main.ml
@@ -7,8 +7,8 @@ let run_parser filename =
let input_channel = open_in filename in
let content = really_input_string input_channel (in_channel_length input_channel) in
close_in input_channel;
- let expr = Tsonnet.run content in
- print_endline (Tsonnet.print expr)
+ let result = Tsonnet.run content in
+ print_endline result
let () =
Arg.parse spec_list anonymous_fun usage_msg;
و تست های CRAM نیز باید خروجی را منعکس کنند:
diff --git a/bin/usage.t b/bin/usage.t
index a8fc82f..b488efe 100644
--- a/bin/usage.t
+++ b/bin/usage.t
@@ -7,7 +7,14 @@ Using the Tsonnet program:
"Hello, world!"
$ tsonnet ../samples/literals/object.jsonnet
- {"int_attr": 1, "float_attr": 4.200000, "string_attr": "Hello, world!", "null_attr": null, "array_attr": [1, false, {}], "obj_attr": {"a": true, "b": false, "c": {"d": [42]}}}
+ {
+ "int_attr": 1,
+ "float_attr": 4.2,
+ "string_attr": "Hello, world!",
+ "null_attr": null,
+ "array_attr": [ 1, false, {} ],
+ "obj_attr": { "a": true, "b": false, "c": { "d": [ 42 ] } }
+ }
$ tsonnet ../samples/binary_operations.jsonnet
- 44.700000
+ 44.7
یکی از ترفندهایی که زندگی ما را بسیار آسان می کند این واقعیت است که Dune ویژگی ای به نام دارد promote
بشر
چه کاری انجام می دهد؟ اگر تست ها اجرا شوند و خروجی صحیح باشد ، اما تفاوت متناقض است ، شما می توانید آن را ترویج کنید ، به این معنی که تفاوت آن را برای شما با یک دستور واحد اعمال می کند:
$ dune runtest --auto-promote
دستور بالا آزمایش ها را دوباره اجرا می کند و تفاوت خروجی را در پرونده های مربوطه اعمال می کند.
این فوق العاده مفید است! من در اینجا به جزئیات نمی پردازم ، اما واقعاً توصیه می کنم مستندات متفاوت و تبلیغی را بخوانید.
مراقب باشید ، این می تواند شما را در برابر ابزار ساختمان فعلی خود ناراحت کند – با احتیاط. واقعاً جادو می شود! 😜
این احساس بهتر است که یک خروجی زیبا چاپ شده داشته باشید ، اینطور نیست؟!
با تشکر از شما برای خواندن بیت شاید عاقلانه! برای دریافت پست های جدید درباره TSONNET مشترک شوید.
عکس توسط پیت گودفری در Unsplash