├── hevea-insert3.html
├── hevea-insert2.html
├── modularity
├── part1.atd
├── part3.atd
├── main.ml
├── part2.atd
└── demo.sh
├── hello
├── hello.atd
├── hello.ml
└── demo.sh
├── README.md
├── pretty-json
├── single.json
├── stream.json
├── prettify.ml
└── demo.sh
├── inspect-biniou
├── tree.atd
├── demo.sh
└── tree.ml
├── hevea-insert1.html
├── config-file
├── bad-config1.json
├── sample-config.json
├── bad-config2.json
├── config.atd
├── demo.sh
└── config.ml
├── validate
├── resume.atd
├── demo.sh
├── resume.ml
└── resume_util.ml
├── misc-examples
└── README.md
├── Makefile
├── macros.ml
├── atdgen-tutorial.mlx
└── atdgen-tutorial-body.mlx
/hevea-insert3.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/hevea-insert2.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/modularity/part1.atd:
--------------------------------------------------------------------------------
1 | type t = { x : int; y : int }
2 |
--------------------------------------------------------------------------------
/hello/hello.atd:
--------------------------------------------------------------------------------
1 | type date = {
2 | year : int;
3 | month : int;
4 | day : int;
5 | }
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **This tutorial is now maintained with other atdgen-related documents at
2 | https://github.com/mjambon/atdgen-doc**
3 |
--------------------------------------------------------------------------------
/modularity/part3.atd:
--------------------------------------------------------------------------------
1 | type t2 = abstract
2 |
3 | type t3 = {
4 | name : string;
5 | ?data : t2 option;
6 | }
7 |
--------------------------------------------------------------------------------
/pretty-json/single.json:
--------------------------------------------------------------------------------
1 | [1234,"abcde",{"start_date":{"year":1970,"month":1,"day":1},
2 | "end_date":{"year":1980,"month":1,"day":1}}]
3 |
--------------------------------------------------------------------------------
/hello/hello.ml:
--------------------------------------------------------------------------------
1 | open Hello_t
2 | let () =
3 | let date = { year = 1970; month = 1; day = 1 } in
4 | print_endline (Hello_j.string_of_date date)
5 |
--------------------------------------------------------------------------------
/pretty-json/stream.json:
--------------------------------------------------------------------------------
1 | [1234,"abcde",{"start_date":{"year":1970,"month":1,"day":1},
2 | "end_date":{"year":1980,"month":1,"day":1}}]
3 | [1,"a",{}]
4 |
--------------------------------------------------------------------------------
/inspect-biniou/tree.atd:
--------------------------------------------------------------------------------
1 | (* This a binary tree. Just for the purpose of pretty-printing trees. *)
2 | type tree =
3 | [ Empty
4 | | Node of (tree * int * tree) ]
5 |
6 |
--------------------------------------------------------------------------------
/pretty-json/prettify.ml:
--------------------------------------------------------------------------------
1 | let json =
2 | "[1234,\"abcde\",{\"start_date\":{\"year\":1970,\"month\":1,\"day\":1},
3 | \"end_date\":{\"year\":1980,\"month\":1,\"day\":1}}]"
4 |
5 | let () = print_endline (Yojson.Safe.prettify json)
6 |
--------------------------------------------------------------------------------
/hevea-insert1.html:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/pretty-json/demo.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh -e
2 |
3 | set -x
4 | cat single.json
5 | ydump single.json
6 | cat stream.json
7 | ydump -s stream.json
8 |
9 | cat prettify.ml
10 | ocamlfind ocamlopt -o prettify prettify.ml -package atdgen -linkpkg
11 | ./prettify
12 |
--------------------------------------------------------------------------------
/config-file/bad-config1.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Example",
3 | "credentials": [
4 | {
5 | "name": 0,
6 | "key": "db7c0877bdef3016"
7 | },
8 | {
9 | "name": "tester",
10 | "key": "09871ff387ac2b10"
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/modularity/main.ml:
--------------------------------------------------------------------------------
1 | let v = {
2 | Part3_t.name = "foo";
3 | data = Some [
4 | { Part1_t.x = 1; y = 2 };
5 | { Part1_t.x = 3; y = 4 };
6 | ]
7 | }
8 |
9 | let () =
10 | Ag_util.Json.to_channel Part3_j.write_t3 stdout v;
11 | print_newline ()
12 |
--------------------------------------------------------------------------------
/config-file/sample-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Example",
3 | "credentials": [
4 | {
5 | "name": "joeuser",
6 | "key": "db7c0877bdef3016"
7 | },
8 | {
9 | "name": "tester",
10 | "key": "09871ff387ac2b10"
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/config-file/bad-config2.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Example",
3 | "tiemout": 20,
4 | "credentials": [
5 | {
6 | "name": "joeuser",
7 | "key": "db7c0877bdef3016"
8 | },
9 | {
10 | "name": "tester",
11 | "key": "09871ff387ac2b10"
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/modularity/part2.atd:
--------------------------------------------------------------------------------
1 | type t1 = abstract
2 | (*
3 | Imports type t defined in file part1.atd.
4 | The local name is t1. Because the local name (t1) is different from the
5 | original name (t), we must specify the original name using t=.
6 | *)
7 |
8 | type t2 = t1 list
9 |
--------------------------------------------------------------------------------
/inspect-biniou/demo.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh -e
2 |
3 | set -x
4 |
5 | cat tree.atd
6 | cat tree.ml
7 |
8 | atdgen -t tree.atd
9 | atdgen -b tree.atd
10 | ocamlfind ocamlopt -o tree \
11 | tree_t.mli tree_t.ml tree_b.mli tree_b.ml tree.ml \
12 | -package atdgen -linkpkg
13 | ./tree
14 |
15 | ls -l tree.dat
16 | bdump tree.dat
17 | bdump -w Empty,Node tree.dat
18 | bdump tree.dat
19 |
--------------------------------------------------------------------------------
/validate/resume.atd:
--------------------------------------------------------------------------------
1 | type text = string
2 |
3 | type date = {
4 | year : int;
5 | month : int;
6 | day : int;
7 | }
8 |
9 | type job = {
10 | company : text;
11 | title : text;
12 | start_date : date;
13 | ?end_date : date option;
14 | }
15 |
16 | type work_experience = job list
17 |
--------------------------------------------------------------------------------
/config-file/config.atd:
--------------------------------------------------------------------------------
1 | type config = {
2 | title : string;
3 | ?description : string option;
4 | ~timeout : int;
5 | ~credentials : param list
6 | ;
8 | }
9 |
10 | type param = {
11 | name : string
12 | ;
13 | key : string
14 | ;
15 | }
16 |
--------------------------------------------------------------------------------
/hello/demo.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh -e
2 |
3 | set -x
4 | cat hello.atd
5 | atdgen -t hello.atd
6 | atdgen -j hello.atd
7 | ls
8 | ocamlfind ocamlc -c hello_t.mli -package atdgen
9 | ocamlfind ocamlc -c hello_j.mli -package atdgen
10 | ocamlfind ocamlopt -c hello_t.ml -package atdgen
11 | ocamlfind ocamlopt -c hello_j.ml -package atdgen
12 | ocamlfind ocamlopt -c hello.ml -package atdgen
13 | ocamlfind ocamlopt -o hello hello_t.cmx hello_j.cmx hello.cmx \
14 | -package atdgen -linkpkg
15 | ./hello
16 |
--------------------------------------------------------------------------------
/modularity/demo.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh -e
2 |
3 | set -x
4 | cat part1.atd
5 | cat part2.atd
6 | cat part3.atd
7 | for x in part1 part2 part3; do
8 | atdgen -t $x.atd
9 | atdgen -j $x.atd
10 | ocamlfind ocamlc -c ${x}_t.mli -package atdgen
11 | ocamlfind ocamlc -c ${x}_j.mli -package atdgen
12 | ocamlfind ocamlopt -c ${x}_t.ml -package atdgen
13 | ocamlfind ocamlopt -c ${x}_j.ml -package atdgen
14 | done
15 | ocamlfind ocamlopt -c main.ml -package atdgen
16 |
17 | ocamlfind ocamlopt -o test_modularity \
18 | part1_t.cmx part1_j.cmx \
19 | part2_t.cmx part2_j.cmx \
20 | part3_t.cmx part3_j.cmx \
21 | main.cmx \
22 | -package atdgen -linkpkg
23 | ./test_modularity
24 |
--------------------------------------------------------------------------------
/validate/demo.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh -e
2 |
3 | set -x
4 | cat resume.atd
5 | atdgen -t resume.atd
6 | atdgen -j resume.atd
7 | atdgen -v resume.atd
8 | ls
9 | ocamlfind ocamlc -c resume_t.mli -package atdgen
10 | ocamlfind ocamlc -c resume_v.mli -package atdgen
11 | ocamlfind ocamlc -c resume_j.mli -package atdgen
12 | ocamlfind ocamlopt -c resume_t.ml -package atdgen
13 | ocamlfind ocamlopt -c resume_util.ml -package atdgen
14 | ocamlfind ocamlopt -c resume_v.ml -package atdgen
15 | ocamlfind ocamlopt -c resume_j.ml -package atdgen
16 | ocamlfind ocamlopt -c resume.ml -package atdgen
17 | ocamlfind ocamlopt -o test_resume \
18 | resume_t.cmx resume_util.cmx resume_v.cmx resume_j.cmx resume.cmx \
19 | -package atdgen -linkpkg
20 | ./test_resume
21 |
--------------------------------------------------------------------------------
/inspect-biniou/tree.ml:
--------------------------------------------------------------------------------
1 | open Printf
2 |
3 | (* sample value *)
4 | let tree : Tree_t.tree =
5 | `Node (
6 | `Node (`Empty, 1, `Empty),
7 | 2,
8 | `Node (
9 | `Node (`Empty, 3, `Empty),
10 | 4,
11 | `Node (`Empty, 5, `Empty)
12 | )
13 | )
14 |
15 | let () =
16 | (* write sample value to file *)
17 | let fname = "tree.dat" in
18 | Ag_util.Biniou.to_file Tree_b.write_tree fname tree;
19 |
20 | (* write sample value to string *)
21 | let s = Tree_b.string_of_tree tree in
22 | printf "raw value (saved as %s):\n%S\n" fname s;
23 | printf "length: %i\n" (String.length s);
24 |
25 | printf "pretty-printed value (without dictionary):\n";
26 | print_endline (Bi_io.view s);
27 |
28 | printf "pretty-printed value (with dictionary):\n";
29 | let unhash = Bi_io.make_unhash ["Empty"; "Node"; "foo"; "bar" ] in
30 | print_endline (Bi_io.view ~unhash s)
31 |
--------------------------------------------------------------------------------
/config-file/demo.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh -e
2 |
3 | set -x
4 |
5 | # Embed the contents of the .atd file into our OCaml program
6 | echo 'let contents = "\' > config_atd.ml
7 | sed -e 's/\([\\"]\)/\\\1/g' config.atd >> config_atd.ml
8 | echo '"' >> config_atd.ml
9 |
10 | # Derive OCaml type definitions from .atd file
11 | atdgen -t config.atd
12 |
13 | # Derive JSON-related functions from .atd file
14 | atdgen -j -j-defaults -j-strict-fields config.atd
15 |
16 | # Derive validator from .atd file
17 | atdgen -v config.atd
18 |
19 | # Compile the OCaml program
20 | ocamlfind ocamlopt -o config \
21 | config_t.mli config_t.ml config_j.mli config_j.ml config_v.mli config_v.ml \
22 | config_atd.ml config.ml -package atdgen -linkpkg
23 |
24 | # Output a sample config
25 | ./config -template
26 |
27 | # Print the original type definitions
28 | ./config -format
29 |
30 | # Fail to validate an invalid config file
31 | ./config -validate bad-config1.json || :
32 |
33 | # Fail to validate another invalid config file (using custom validators)
34 | ./config -validate bad-config3.json || :
35 |
36 | # Validate, inject missing defaults and pretty-print
37 | ./config -validate sample-config.json
38 |
--------------------------------------------------------------------------------
/validate/resume.ml:
--------------------------------------------------------------------------------
1 | let check_experience x =
2 | (match Resume_v.validate_work_experience [] x with
3 | None ->
4 | Printf.printf "VALID:\n"
5 | | Some error ->
6 | Printf.printf "INVALID: %s\n"
7 | (Ag_util.Validation.string_of_error error)
8 | );
9 | Printf.printf "%s\n"
10 | (Yojson.Safe.prettify (Resume_j.string_of_work_experience x))
11 |
12 | let () =
13 | (* one valid date *)
14 | let valid = { Resume_t.year = 2000; month = 2; day = 29 } in
15 | (* one invalid date *)
16 | let invalid = { Resume_t.year = 2010; month = 0; day = 0 } in
17 | (* two more valid dates, created with Resume_v.create_date *)
18 | let date1 = { Resume_t.year = 2005; month = 8; day = 1 } in
19 | let date2 = { Resume_t.year = 2006; month = 3; day = 22 } in
20 |
21 | let job = {
22 | Resume_t.company = "Acme Corp.";
23 | title = "Tester";
24 | start_date = date1;
25 | end_date = Some date2;
26 | }
27 | in
28 | let valid_job = { job with Resume_t.start_date = valid } in
29 | let invalid_job = { job with Resume_t.end_date = Some invalid } in
30 | let valid_experience = [ job; valid_job ] in
31 | let invalid_experience = [ job; invalid_job ] in
32 | check_experience valid_experience;
33 | check_experience invalid_experience
34 |
--------------------------------------------------------------------------------
/validate/resume_util.ml:
--------------------------------------------------------------------------------
1 | open Resume_t
2 |
3 | let ascii_printable c =
4 | let n = Char.code c in
5 | n >= 32 && n <= 127
6 |
7 | (*
8 | Check that string is not empty and contains only ASCII printable
9 | characters (for the sake of the example; we use UTF-8 these days)
10 | *)
11 | let validate_some_text path s =
12 | let success =
13 | s <> "" &&
14 | try
15 | String.iter (fun c -> if not (ascii_printable c) then raise Exit) s;
16 | true
17 | with Exit ->
18 | false
19 | in
20 | if success then None
21 | else
22 | Some (Ag_util.Validation.error path)
23 |
24 | (*
25 | Check that the combination of year, month and day exists in the
26 | Gregorian calendar.
27 | *)
28 | let validate_date path x =
29 | let y = x.year in
30 | let m = x.month in
31 | let d = x.day in
32 | let success =
33 | m >= 1 && m <= 12 && d >= 1 &&
34 | (let dmax =
35 | match m with
36 | 2 ->
37 | if y mod 4 = 0 && not (y mod 100 = 0) || y mod 400 = 0 then 29
38 | else 28
39 | | 1 | 3 | 5 | 7 | 8 | 10 | 12 -> 31
40 | | _ -> 30
41 | in
42 | d <= dmax)
43 | in
44 | if success then None
45 | else Some (Ag_util.Validation.error path)
46 |
47 | (* Compare dates chronologically *)
48 | let compare_date a b =
49 | let c = compare a.year b.year in
50 | if c <> 0 then c
51 | else
52 | let c = compare a.month b.month in
53 | if c <> 0 then c
54 | else compare a.day b.day
55 |
56 | (* Check that the end_date, when defined, is not earlier than the start_date *)
57 | let validate_job path x =
58 | match x.end_date with
59 | None -> None
60 | | Some end_date ->
61 | if compare_date x.start_date end_date <= 0 then
62 | None
63 | else
64 | Some (Ag_util.Validation.error path)
65 |
--------------------------------------------------------------------------------
/misc-examples/README.md:
--------------------------------------------------------------------------------
1 | Atdgen examples
2 | ===============
3 |
4 | Examples accumulate here, typically based on questions asked by
5 | users, pending their integration into a better organized document.
6 |
7 | Serialization of type defined in an external library
8 | ----------------------------------------------------
9 |
10 | ```ocaml
11 | (*
12 | atdgen -t ext.atd
13 | atdgen -j -j-std ext.atd
14 | ocamlfind opt -c -package atdgen ext_t.mli ext_t.ml ext_j.mli ext_j.ml
15 | *)
16 | type status_t =
17 | [ WEXITED of int
18 | | WSIGNALED of int
19 | | WSTOPPED of int ]
20 | ```
21 |
22 | This produces JSON serialization code (`_j` files) and the following
23 | type re-definition:
24 |
25 | ```ocaml
26 | type status_t = Unix.process_status =
27 | WEXITED of int
28 | | WSIGNALED of int
29 | | WSTOPPED of int
30 | ```
31 |
32 | Serializing abstract types that come with their own `to_string` and `of_string`
33 | -------------------------------------------------------------------------------
34 |
35 | ```ocaml
36 | (*
37 | atdgen -t ex.atd
38 | atdgen -j -j-std ex.atd
39 | ocamlfind opt -c -package atdgen ex_t.mli ex_t.ml ex_j.mli ex_j.ml
40 | *)
41 | type calendar =
42 | string wrap
46 |
47 | type has_calendar_t = {
48 | c : calendar;
49 | }
50 | ```
51 |
52 | produces the following OCaml type definitions:
53 |
54 | ```ocaml
55 | type calendar = Calendar.t
56 | type has_calendar_t = { c: calendar }
57 | ```
58 |
59 | When converting to JSON, a value of type `calendar` is converted
60 | (unwrapped) into an OCaml string using the supplied function
61 | `Calendar.to_string`. Conversely, when reading from JSON, an OCaml
62 | string is parsed (wrapped) into a `calendar` using the supplied function
63 | `Calendar.of_string`.
64 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all demo pdf txt html install clean
2 | all: demo pdf txt html
3 |
4 | demo:
5 | cd hello && ./demo.sh
6 | cd pretty-json && ./demo.sh
7 | cd inspect-biniou && ./demo.sh
8 | cd validate && ./demo.sh
9 | cd modularity && ./demo.sh
10 |
11 | pdf: atdgen-tutorial.pdf
12 | txt: atdgen-tutorial.txt
13 | html: atdgen-tutorial.html
14 |
15 | TEXFILES = atdgen-tutorial.tex atdgen-tutorial-body.tex
16 | TUTSRC = $(TEXFILES) \
17 | config-file/demo.sh config-file/config.atd config-file/config.ml
18 |
19 | atdgen-tutorial.tex: atdgen-tutorial.mlx
20 | OCAMLPATH=../..:$$OCAMLPATH \
21 | camlmix atdgen-tutorial.mlx -o atdgen-tutorial.tex
22 |
23 | atdgen-tutorial-body.tex: macros.ml atdgen-tutorial-body.mlx
24 | OCAMLPATH=../..:$$OCAMLPATH \
25 | camlmix atdgen-tutorial-body.mlx -o atdgen-tutorial-body.tex
26 |
27 | atdgen-tutorial.txt: $(TUTSRC)
28 | rm -f *.aux
29 | hevea -fix -text atdgen-tutorial
30 | mv atdgen-tutorial.txt atdgen-tutorial.txt.orig
31 | iconv -f ISO_8859-1 -t UTF-8 < atdgen-tutorial.txt.orig \
32 | > atdgen-tutorial.txt
33 |
34 | atdgen-tutorial.html: $(TUTSRC) \
35 | hevea-insert1.html hevea-insert2.html hevea-insert3.html
36 | rm -f *.aux
37 | hevea -fix atdgen-tutorial
38 | sed -i '/<\/STYLE>/ r hevea-insert1.html' atdgen-tutorial.html
39 | sed -i '// r hevea-insert2.html' atdgen-tutorial.html
40 | sed -i 's/<\/BLOCKQUOTE><\/BODY>/<\/BLOCKQUOTE>\n<\/BODY>/' \
41 | atdgen-tutorial.html
42 | sed -i -e 's:^
.*::' atdgen-tutorial.html
43 | sed -i -e 's:^.*::' atdgen-tutorial.html
44 | sed -i '// r hevea-insert3.html' atdgen-tutorial.html
45 |
46 | atdgen-tutorial.pdf: $(TUTSRC)
47 | pdflatex atdgen-tutorial
48 | pdflatex atdgen-tutorial
49 | pdflatex atdgen-tutorial
50 |
51 | install:
52 | cp atdgen-tutorial.html atdgen-tutorial.txt atdgen-tutorial.pdf \
53 | ../mylifelabs.github.com/
54 | cd ../mylifelabs.github.com/; \
55 | git add atdgen-tutorial.html atdgen-tutorial.txt atdgen-tutorial.pdf; \
56 | git commit -m "Update"; git push
57 |
58 | clean:
59 | rm -f *~
60 | rm -f *.aux *.toc *.log *.out *.haux *.htoc *.fls \
61 | atdgen-tutorial-body.tex atdgen-tutorial.tex \
62 | atdgen-tutorial.pdf atdgen-tutorial.txt \
63 | atdgen-tutorial.html \
64 | *.mlx.ml
65 | rm -f */*_[tbjv].* */*_[tbjv].* */*~ */*.cm[iox] */*.o
66 | rm -f hello/hello pretty-json/prettify \
67 | inspect-biniou/tree inspect-biniou/tree.dat \
68 | validate/test_resume modularity/test_modularity
69 |
--------------------------------------------------------------------------------
/config-file/config.ml:
--------------------------------------------------------------------------------
1 | open Printf
2 |
3 | let param_template =
4 | (* Sample item used to populate the template config file *)
5 | {
6 | Config_v.name = "foo";
7 | key = "0123456789abcdef"
8 | }
9 |
10 | let config_template =
11 | (*
12 | Records can be conveniently created using functions generated by
13 | "atdgen -v".
14 | Here we use Config_v.create_config to create a record of type
15 | Config_t.config. The big advantage over creating the record
16 | directly using the record notation {...} is that we don't have to
17 | specify default values (such as timeout in this example).
18 | *)
19 | Config_v.create_config ~title:"" ~credentials: [param_template] ()
20 |
21 | let make_json_template () =
22 | (* Thanks to the -j-defaults flag passed to atdgen, even default
23 | fields will be printed out *)
24 | let compact_json = Config_j.string_of_config config_template in
25 | Yojson.Safe.prettify compact_json
26 |
27 | let print_template () =
28 | print_endline (make_json_template ())
29 |
30 | let print_format () =
31 | print_string Config_atd.contents
32 |
33 | let validate fname =
34 | let x =
35 | try
36 | (* Read config data structure from JSON file *)
37 | let x = Ag_util.Json.from_file Config_j.read_config fname in
38 | (* Call the validators specified by *)
39 | if not (Config_v.validate_config x) then
40 | failwith "Some fields are invalid"
41 | else
42 | x
43 | with e ->
44 | (* Print decent error message and exit *)
45 | let msg =
46 | match e with
47 | Failure s
48 | | Yojson.Json_error s -> s
49 | | e -> Printexc.to_string e
50 | in
51 | eprintf "Error: %s\n%!" msg;
52 | exit 1
53 | in
54 | (* Convert config to compact JSON and pretty-print it.
55 | ~std:true means that the output will not use extended syntax for
56 | variants and tuples but only standard JSON. *)
57 | let json = Yojson.Safe.prettify ~std:true (Config_j.string_of_config x) in
58 | print_endline json
59 |
60 | type action = Template | Format | Validate of string
61 |
62 | let main () =
63 | let action = ref Template in
64 | let options = [
65 | "-template", Arg.Unit (fun () -> action := Template),
66 | "
67 | prints a sample configuration file";
68 |
69 | "-format", Arg.Unit (fun () -> action := Format),
70 | "
71 | prints the format specification of the config files (atd format)";
72 |
73 | "-validate", Arg.String (fun s -> action := Validate s),
74 | "
75 | reads a config file, validates it, adds default values
76 | and prints the config nicely to stdout";
77 | ]
78 | in
79 | let usage_msg = sprintf "\
80 | Usage: %s [-template|-format|-validate ...]
81 | Demonstration of how to manage JSON configuration files with atdgen.
82 | "
83 | Sys.argv.(0)
84 | in
85 | let anon_fun s = eprintf "Invalid command parameter %S\n%!" s; exit 1 in
86 | Arg.parse options anon_fun usage_msg;
87 |
88 | match !action with
89 | Template -> print_template ()
90 | | Format -> print_format ()
91 | | Validate s -> validate s
92 |
93 | let () = main ()
94 |
--------------------------------------------------------------------------------
/macros.ml:
--------------------------------------------------------------------------------
1 | open Printf
2 |
3 | let latex_of_string s =
4 | let tokens = Caml2html.Input.string s in
5 | let buf = Buffer.create 1000 in
6 | Caml2html.Output_latex.ocaml buf tokens;
7 | Buffer.contents buf
8 |
9 | let print_ocaml s =
10 | print "\\begin{alltt}";
11 | print (latex_of_string s);
12 | print "\\end{alltt}"
13 |
14 | (* Validate ATD syntax before printing *)
15 | let print_atd s =
16 | try
17 | ignore (Atd_util.load_string
18 | ~expand:true ~keep_poly:true
19 | ~inherit_fields:true ~inherit_variants:true s);
20 | print_ocaml s
21 | with e ->
22 | let msg =
23 | match e with
24 | Failure s
25 | | Atd_ast.Atd_error s -> s
26 | | _ -> Printexc.to_string e
27 | in
28 | Printf.eprintf "\
29 | *** Invalid ATD ***
30 | %s
31 | *** Error ***
32 | %s
33 |
34 | %!"
35 | s msg;
36 | raise e
37 |
38 |
39 |
40 | let read_command_output f s =
41 | let ic = Unix.open_process_in s in
42 | (try
43 | while true do
44 | f (input_char ic)
45 | done
46 | with End_of_file -> ());
47 | match Unix.close_process_in ic with
48 | Unix.WEXITED 0 -> ()
49 | | _ -> invalid_arg ("read_command_output: " ^ s)
50 |
51 |
52 | let file_contents fn =
53 | let buf = Buffer.create 1000 in
54 | let ic = open_in fn in
55 | (try
56 | while true do
57 | bprintf buf "%s\n" (input_line ic)
58 | done
59 | with End_of_file -> ()
60 | );
61 | close_in ic;
62 | Buffer.contents buf
63 |
64 |
65 | let shell s =
66 | let buf = Buffer.create 100 in
67 | read_command_output (Buffer.add_char buf) s;
68 | print "\\begin{verbatim}";
69 | print (Buffer.contents buf); (* no escaping! *)
70 | print "\\end{verbatim}"
71 |
72 |
73 | let check_atdgen ?prefix output_type s =
74 | let fn =
75 | match prefix with
76 | None -> Filename.temp_file "atdgen_" ".atd"
77 | | Some s -> s ^ ".atd"
78 | in
79 | let prefix = Filename.chop_extension fn in
80 | let mli = prefix ^ ".mli" in
81 | let ml = prefix ^ ".ml" in
82 | let oc = open_out fn in
83 | let finally () =
84 | close_out_noerr oc;
85 | Sys.remove fn;
86 | (try Sys.remove mli with _ -> ());
87 | (try Sys.remove ml with _ -> ());
88 | in
89 | try
90 | output_string oc s;
91 | close_out oc;
92 | let cmd =
93 | sprintf "../atdgen %s %s"
94 | (match output_type with
95 | `Biniou -> "-biniou"
96 | | `Json -> "-json")
97 | fn
98 | in
99 | match Sys.command cmd with
100 | 0 ->
101 | let mli_data = file_contents mli in
102 | finally ();
103 | mli_data
104 | | n ->
105 | eprintf "\
106 | -- File %s --
107 | %s
108 | ----
109 | Command failed: %s
110 | "
111 | fn s cmd;
112 | finally ();
113 | exit 1
114 |
115 | with e ->
116 | finally ();
117 | raise e
118 |
119 |
120 | let print_atdgen ot s =
121 | ignore (check_atdgen ot s);
122 | print_atd s
123 |
124 |
125 |
126 | let ocaml () =
127 | Camlmix.print_with print_ocaml
128 |
129 | let atd () =
130 | Camlmix.print_with print_atd
131 |
132 | let atdgen_biniou () =
133 | Camlmix.print_with (print_atdgen `Biniou)
134 |
135 | let atdgen_json () =
136 | Camlmix.print_with (print_atdgen `Json)
137 |
138 | let atdgen = atdgen_biniou
139 |
140 |
141 | let print_atdgen_mli ?prefix ot msg s =
142 | let mli = check_atdgen ?prefix ot s in
143 | print_atd s;
144 | print msg;
145 | print_ocaml mli
146 |
147 |
148 | let atdgen_biniou_mli ?prefix msg =
149 | Camlmix.print_with (print_atdgen_mli ?prefix `Biniou msg)
150 |
151 | let atdgen_json_mli ?prefix msg =
152 | Camlmix.print_with (print_atdgen_mli ?prefix `Json msg)
153 |
--------------------------------------------------------------------------------
/atdgen-tutorial.mlx:
--------------------------------------------------------------------------------
1 | % -*- latex -*-
2 | \documentclass[letterpaper,10pt]{article}
3 |
4 | \usepackage{ae}
5 | \usepackage{hyperref}
6 | \usepackage{hevea}
7 |
8 | \usepackage{verbatim}
9 | \usepackage{alltt}
10 |
11 | \usepackage[latin1]{inputenc}
12 | \usepackage[T1]{fontenc}
13 | \usepackage{url}
14 |
15 | \usepackage{booktabs}
16 |
17 | \title{Atdgen tutorial}
18 | \author{Martin Jambon\\
19 | $\copyright$ 2011 MyLife}
20 | %\date{October 19, 2072}
21 | \pagestyle{headings}
22 |
23 | % We suppress indentation at the beginning of each paragraph because paragraphs
24 | % are short and indentation is used heavily for bullet points and code examples
25 | \setlength{\parindent}{0mm}
26 | \setlength{\parskip}{2mm}
27 |
28 | % Thickness of top and bottom lines of tables (\toprule, \bottomrule)
29 | \setlength{\heavyrulewidth}{1.5pt}
30 | % Note: \toprule, \midrule and \bottomrule are really nice-looking but are not
31 | % understood by hevea.
32 |
33 | \usepackage{alltt}
34 | \usepackage{color}
35 | \newcommand\Clinenum[1]{#1}
36 | \definecolor{CconstructorColor}{rgb}{0.00,0.20,0.80}
37 | \newcommand\Cconstructor[1]{\textcolor{CconstructorColor}{#1}}
38 | \definecolor{CcommentColor}{rgb}{0.60,0.00,0.00}
39 | \newcommand\Ccomment[1]{\textcolor{CcommentColor}{#1}}
40 | \definecolor{CstringColor}{rgb}{0.67,0.27,0.27}
41 | \newcommand\Cstring[1]{\textcolor{CstringColor}{#1}}
42 | \newcommand\Cquotation[1]{#1}
43 | \definecolor{CalphakeywordColor}{rgb}{0.50,0.50,0.50}
44 | \newcommand\Calphakeyword[1]{\textcolor{CalphakeywordColor}{#1}}
45 | \newcommand\Cnonalphakeyword[1]{#1}
46 | \definecolor{CandColor}{rgb}{0.00,0.50,0.00}
47 | \newcommand\Cand[1]{\textcolor{CandColor}{#1}}
48 | \definecolor{CasColor}{rgb}{0.00,0.50,0.00}
49 | \newcommand\Cas[1]{\textcolor{CasColor}{#1}}
50 | \definecolor{CclassColor}{rgb}{0.00,0.50,0.00}
51 | \newcommand\Cclass[1]{\textcolor{CclassColor}{#1}}
52 | \definecolor{CconstraintColor}{rgb}{0.00,0.50,0.00}
53 | \newcommand\Cconstraint[1]{\textcolor{CconstraintColor}{#1}}
54 | \definecolor{CexceptionColor}{rgb}{0.00,0.50,0.00}
55 | \newcommand\Cexception[1]{\textcolor{CexceptionColor}{#1}}
56 | \definecolor{CexternalColor}{rgb}{0.00,0.50,0.00}
57 | \newcommand\Cexternal[1]{\textcolor{CexternalColor}{#1}}
58 | \definecolor{CfunColor}{rgb}{0.00,0.50,0.00}
59 | \newcommand\Cfun[1]{\textcolor{CfunColor}{#1}}
60 | \definecolor{CfunctionColor}{rgb}{0.00,0.50,0.00}
61 | \newcommand\Cfunction[1]{\textcolor{CfunctionColor}{#1}}
62 | \definecolor{CfunctorColor}{rgb}{0.00,0.50,0.00}
63 | \newcommand\Cfunctor[1]{\textcolor{CfunctorColor}{#1}}
64 | \definecolor{CinColor}{rgb}{0.00,0.50,0.00}
65 | \newcommand\Cin[1]{\textcolor{CinColor}{#1}}
66 | \definecolor{CinheritColor}{rgb}{0.00,0.50,0.00}
67 | \newcommand\Cinherit[1]{\textcolor{CinheritColor}{#1}}
68 | \definecolor{CinitializerColor}{rgb}{0.00,0.50,0.00}
69 | \newcommand\Cinitializer[1]{\textcolor{CinitializerColor}{#1}}
70 | \definecolor{CletColor}{rgb}{0.00,0.50,0.00}
71 | \newcommand\Clet[1]{\textcolor{CletColor}{#1}}
72 | \definecolor{CmethodColor}{rgb}{0.00,0.50,0.00}
73 | \newcommand\Cmethod[1]{\textcolor{CmethodColor}{#1}}
74 | %\definecolor{CmoduleColor}{rgb}{0.00,0.50,0.00}
75 | %\newcommand\Cmodule[1]{\textcolor{CmoduleColor}{#1}}
76 | \definecolor{CmutableColor}{rgb}{0.00,0.50,0.00}
77 | \newcommand\Cmutable[1]{\textcolor{CmutableColor}{#1}}
78 | \definecolor{CofColor}{rgb}{0.00,0.50,0.00}
79 | \newcommand\Cof[1]{\textcolor{CofColor}{#1}}
80 | \definecolor{CprivateColor}{rgb}{0.00,0.50,0.00}
81 | \newcommand\Cprivate[1]{\textcolor{CprivateColor}{#1}}
82 | \definecolor{CrecColor}{rgb}{0.00,0.50,0.00}
83 | \newcommand\Crec[1]{\textcolor{CrecColor}{#1}}
84 | \definecolor{CtypeColor}{rgb}{0.00,0.50,0.00}
85 | \newcommand\Ctype[1]{\textcolor{CtypeColor}{#1}}
86 | \definecolor{CvalColor}{rgb}{0.00,0.50,0.00}
87 | \newcommand\Cval[1]{\textcolor{CvalColor}{#1}}
88 | \definecolor{CvirtualColor}{rgb}{0.00,0.50,0.00}
89 | \newcommand\Cvirtual[1]{\textcolor{CvirtualColor}{#1}}
90 | \definecolor{CdoColor}{rgb}{0.47,0.67,0.67}
91 | \newcommand\Cdo[1]{\textcolor{CdoColor}{#1}}
92 | \definecolor{CdoneColor}{rgb}{0.47,0.67,0.67}
93 | \newcommand\Cdone[1]{\textcolor{CdoneColor}{#1}}
94 | \definecolor{CdowntoColor}{rgb}{0.47,0.67,0.67}
95 | \newcommand\Cdownto[1]{\textcolor{CdowntoColor}{#1}}
96 | \definecolor{CelseColor}{rgb}{0.47,0.67,0.67}
97 | \newcommand\Celse[1]{\textcolor{CelseColor}{#1}}
98 | \definecolor{CforColor}{rgb}{0.47,0.67,0.67}
99 | \newcommand\Cfor[1]{\textcolor{CforColor}{#1}}
100 | \definecolor{CifColor}{rgb}{0.47,0.67,0.67}
101 | \newcommand\Cif[1]{\textcolor{CifColor}{#1}}
102 | \definecolor{ClazyColor}{rgb}{0.47,0.67,0.67}
103 | \newcommand\Clazy[1]{\textcolor{ClazyColor}{#1}}
104 | \definecolor{CmatchColor}{rgb}{0.47,0.67,0.67}
105 | \newcommand\Cmatch[1]{\textcolor{CmatchColor}{#1}}
106 | \definecolor{CnewColor}{rgb}{0.47,0.67,0.67}
107 | \newcommand\Cnew[1]{\textcolor{CnewColor}{#1}}
108 | \definecolor{CorColor}{rgb}{0.47,0.67,0.67}
109 | \newcommand\Cor[1]{\textcolor{CorColor}{#1}}
110 | \definecolor{CthenColor}{rgb}{0.47,0.67,0.67}
111 | \newcommand\Cthen[1]{\textcolor{CthenColor}{#1}}
112 | \definecolor{CtoColor}{rgb}{0.47,0.67,0.67}
113 | \newcommand\Cto[1]{\textcolor{CtoColor}{#1}}
114 | \definecolor{CtryColor}{rgb}{0.47,0.67,0.67}
115 | \newcommand\Ctry[1]{\textcolor{CtryColor}{#1}}
116 | \definecolor{CwhenColor}{rgb}{0.47,0.67,0.67}
117 | \newcommand\Cwhen[1]{\textcolor{CwhenColor}{#1}}
118 | \definecolor{CwhileColor}{rgb}{0.47,0.67,0.67}
119 | \newcommand\Cwhile[1]{\textcolor{CwhileColor}{#1}}
120 | \definecolor{CwithColor}{rgb}{0.47,0.67,0.67}
121 | \newcommand\Cwith[1]{\textcolor{CwithColor}{#1}}
122 | \definecolor{CassertColor}{rgb}{0.80,0.60,0.00}
123 | \newcommand\Cassert[1]{\textcolor{CassertColor}{#1}}
124 | \definecolor{CincludeColor}{rgb}{0.80,0.60,0.00}
125 | \newcommand\Cinclude[1]{\textcolor{CincludeColor}{#1}}
126 | \definecolor{CopenColor}{rgb}{0.80,0.60,0.00}
127 | \newcommand\Copen[1]{\textcolor{CopenColor}{#1}}
128 | \definecolor{CbeginColor}{rgb}{0.60,0.00,0.60}
129 | \newcommand\Cbegin[1]{\textcolor{CbeginColor}{#1}}
130 | \definecolor{CendColor}{rgb}{0.60,0.00,0.60}
131 | \newcommand\Cend[1]{\textcolor{CendColor}{#1}}
132 | \definecolor{CobjectColor}{rgb}{0.60,0.00,0.60}
133 | \newcommand\Cobject[1]{\textcolor{CobjectColor}{#1}}
134 | \definecolor{CsigColor}{rgb}{0.60,0.00,0.60}
135 | \newcommand\Csig[1]{\textcolor{CsigColor}{#1}}
136 | \definecolor{CstructColor}{rgb}{0.60,0.00,0.60}
137 | \newcommand\Cstruct[1]{\textcolor{CstructColor}{#1}}
138 | \definecolor{CraiseColor}{rgb}{1.00,0.00,0.00}
139 | \newcommand\Craise[1]{\textcolor{CraiseColor}{#1}}
140 | \definecolor{CasrColor}{rgb}{0.50,0.50,0.50}
141 | \newcommand\Casr[1]{\textcolor{CasrColor}{#1}}
142 | \definecolor{ClandColor}{rgb}{0.50,0.50,0.50}
143 | \newcommand\Cland[1]{\textcolor{ClandColor}{#1}}
144 | \definecolor{ClorColor}{rgb}{0.50,0.50,0.50}
145 | \newcommand\Clor[1]{\textcolor{ClorColor}{#1}}
146 | \definecolor{ClslColor}{rgb}{0.50,0.50,0.50}
147 | \newcommand\Clsl[1]{\textcolor{ClslColor}{#1}}
148 | \definecolor{ClsrColor}{rgb}{0.50,0.50,0.50}
149 | \newcommand\Clsr[1]{\textcolor{ClsrColor}{#1}}
150 | \definecolor{ClxorColor}{rgb}{0.50,0.50,0.50}
151 | \newcommand\Clxor[1]{\textcolor{ClxorColor}{#1}}
152 | \definecolor{CmodColor}{rgb}{0.50,0.50,0.50}
153 | \newcommand\Cmod[1]{\textcolor{CmodColor}{#1}}
154 | \newcommand\Cfalse[1]{#1}
155 | \newcommand\Ctrue[1]{#1}
156 | \definecolor{CbarColor}{rgb}{0.47,0.67,0.67}
157 | \newcommand\Cbar[1]{\textcolor{CbarColor}{#1}}
158 |
159 | % Exception:
160 | \newcommand\Cmodule[1]{#1}
161 |
162 | \newcommand\bs{$\mathtt\backslash$}
163 |
164 | \begin{document}
165 | \maketitle
166 | \tableofcontents
167 | \include{atdgen-tutorial-body}
168 | \end{document}
169 |
--------------------------------------------------------------------------------
/atdgen-tutorial-body.mlx:
--------------------------------------------------------------------------------
1 | % -*- latex -*-
2 | ##
3 | #use "topfind";;
4 | #require "caml2html";;
5 | #require "atd";;
6 | #require "unix";;
7 | #use "macros.ml";;
8 | ##
9 |
10 | \section{What is Atdgen?}
11 |
12 | Atdgen is a tool that derives OCaml boilerplate code from type definitions.
13 | Currently it provides support for:
14 |
15 | \begin{itemize}
16 | \item \href{http://json.org/}{JSON} serialization and deserialization.
17 | \item \href{http://martin.jambon.free.fr/biniou-format.txt}{Biniou}
18 | serialization and deserialization.
19 | Biniou is a binary format extensible like JSON but more compact
20 | and faster to process.
21 | \item Convenience functions for creating and validating OCaml data.
22 | \end{itemize}
23 |
24 | \section{What are the advantages of Atdgen?}
25 |
26 | Atdgen has a number of advantages over its predecessor json-static
27 | which was based on Camlp4:
28 | \begin{itemize}
29 | \item produces explicit interfaces which describe what is available to
30 | the user (\texttt{.mli} files).
31 | \item produces readable OCaml code that can be easily reviewed
32 | (\texttt{.ml} files).
33 | \item produces fast code, 3x faster than json-static.
34 | \item runs fast, keeping build times low.
35 | \item same ATD definitions can be used to generate code other than
36 | OCaml. See for instance
37 | \href{https://github.com/MyLifeLabs/atdj}{Atdj}
38 | which generates Java classes for JSON IO.
39 | Auto-generating GUI widgets from type definitions is another
40 | popular use of annotated type definitions. The implementation of
41 | such code generators is facilitated by the
42 | \href{http://oss.wink.com/atd/}{\texttt{atd}} library.
43 | \end{itemize}
44 |
45 |
46 | \section{Prerequisites}
47 |
48 | This tutorial assumes that you are using Atdgen version 1.2.0 or above.
49 | The following command tells you which version you are using:
50 |
51 | \begin{verbatim}
52 | $ atdgen -version
53 | 1.2.0
54 | \end{verbatim}
55 |
56 | A quick way of installing Atdgen and all its dependencies is via Godi.
57 |
58 | Alternatively, read and follow the instructions in the
59 | \texttt{INSTALL} file of the source package of Atdgen.
60 |
61 |
62 | \section{Getting started}
63 |
64 | From now on we assume that Atdgen 1.2.0 or above is installed properly.
65 |
66 | \begin{verbatim}
67 | $ atdgen -version
68 | 1.2.0
69 | \end{verbatim}
70 |
71 | Type definitions are placed in a \texttt{.atd} file (\texttt{hello.atd}):
72 |
73 | ## ocaml () ##
74 | type date = {
75 | year : int;
76 | month : int;
77 | day : int;
78 | }
79 | ## () ##
80 |
81 | Our handwritten OCaml program is \texttt{hello.ml}:
82 |
83 | ## ocaml () ##
84 | open Hello_t
85 | let () =
86 | let date = { year = 1970; month = 1; day = 1 } in
87 | print_endline (Hello_j.string_of_date date)
88 | ## () ##
89 |
90 | We produce OCaml code from the type definitions using \texttt{atdgen}:
91 |
92 | \begin{verbatim}
93 | $ atdgen -t hello.atd # produces OCaml type definitions
94 | $ atdgen -j hello.atd # produces OCaml code dealing with JSON
95 | \end{verbatim}
96 |
97 | We now have \texttt{\_t} and \texttt{\_j} files produced by \texttt{atdgen -t} and \texttt{atdgen -j}
98 | respectively:
99 |
100 | \begin{verbatim}
101 | $ ls
102 | hello.atd hello.ml hello_j.ml hello_j.mli hello_t.ml hello_t.mli
103 | \end{verbatim}
104 |
105 | We compile all \texttt{.mli} and \texttt{.ml} files:
106 |
107 | \begin{verbatim}
108 | $ ocamlfind ocamlc -c hello_t.mli -package atdgen
109 | $ ocamlfind ocamlc -c hello_j.mli -package atdgen
110 | $ ocamlfind ocamlopt -c hello_t.ml -package atdgen
111 | $ ocamlfind ocamlopt -c hello_j.ml -package atdgen
112 | $ ocamlfind ocamlopt -c hello.ml -package atdgen
113 | $ ocamlfind ocamlopt -o hello hello_t.cmx hello_j.cmx hello.cmx \
114 | -package atdgen -linkpkg
115 | \end{verbatim}
116 |
117 | And finally we run our \texttt{hello} program:
118 |
119 | \begin{verbatim}
120 | $ ./hello
121 | {"year":1970,"month":1,"day":1}
122 | \end{verbatim}
123 |
124 |
125 | Source code for this section:
126 | \url{https://github.com/MyLifeLabs/atdgen-tutorial/tree/master/hello}
127 |
128 |
129 | \section{Inspecting and pretty-printing JSON}
130 |
131 | Input JSON data:
132 |
133 | \begin{verbatim}
134 | $ cat single.json
135 | [1234,"abcde",{"start_date":{"year":1970,"month":1,"day":1},
136 | "end_date":{"year":1980,"month":1,"day":1}}]
137 | \end{verbatim}
138 |
139 | Pretty-printed JSON can be produced with the \texttt{ydump} command:
140 |
141 | \begin{verbatim}
142 | $ ydump single.json
143 | [
144 | 1234,
145 | "abcde",
146 | {
147 | "start_date": { "year": 1970, "month": 1, "day": 1 },
148 | "end_date": { "year": 1980, "month": 1, "day": 1 }
149 | }
150 | ]
151 | \end{verbatim}
152 |
153 | Multiple JSON objects separated by whitespace, typically one JSON object
154 | per line, can also be pretty-printed with \texttt{ydump}. Input:
155 |
156 | \begin{verbatim}
157 | $ cat stream.json
158 | [1234,"abcde",{"start_date":{"year":1970,"month":1,"day":1},
159 | "end_date":{"year":1980,"month":1,"day":1}}]
160 | [1,"a",{}]
161 | \end{verbatim}
162 |
163 | In this case the \texttt{-s} option is required:
164 |
165 | \begin{verbatim}
166 | $ ydump -s stream.json
167 | [
168 | 1234,
169 | "abcde",
170 | {
171 | "start_date": { "year": 1970, "month": 1, "day": 1 },
172 | "end_date": { "year": 1980, "month": 1, "day": 1 }
173 | }
174 | ]
175 | [ 1, "a", {} ]
176 | \end{verbatim}
177 |
178 | From an OCaml program, pretty-printing can be done with
179 | \texttt{Yojson.Safe.prettify}
180 | which has the following signature:
181 |
182 | ## ocaml () ##
183 | val prettify : string -> string
184 | ## () ##
185 |
186 | We wrote a tiny program that simply calls the \texttt{prettify} function on
187 | some predefined JSON data (file \texttt{prettify.ml}):
188 |
189 | ## ocaml () ##
190 | let json =
191 | "[1234,\"abcde\",{\"start_date\":{\"year\":1970,\"month\":1,\"day\":1},
192 | \"end_date\":{\"year\":1980,\"month\":1,\"day\":1}}]"
193 |
194 | let () = print_endline (Yojson.Safe.prettify json)
195 | ## () ##
196 |
197 | We now compile and run prettify.ml:
198 |
199 | \begin{verbatim}
200 | $ ocamlfind ocamlopt -o prettify prettify.ml -package atdgen -linkpkg
201 | $ ./prettify
202 | [
203 | 1234,
204 | "abcde",
205 | {
206 | "start_date": { "year": 1970, "month": 1, "day": 1 },
207 | "end_date": { "year": 1980, "month": 1, "day": 1 }
208 | }
209 | ]
210 | \end{verbatim}
211 |
212 | Source code for this section:
213 | \url{https://github.com/MyLifeLabs/atdgen-tutorial/tree/master/pretty-json}
214 |
215 |
216 |
217 | \section{Inspecting biniou data}
218 |
219 | Biniou is a binary format that can be displayed as text using a generic
220 | command called \texttt{bdump}. The only practical difficulty is to recover
221 | the original field names and variant names which are stored as 31-bit hashes.
222 | Unhashing them is done by consulting a dictionary (list of words)
223 | maintained by the user.
224 |
225 | Let's first produce a sample data file \texttt{tree.dat} containing the
226 | biniou representation of a binary tree. In the same program
227 | we will also demonstrate how to render biniou data into text from an
228 | OCaml program.
229 |
230 | Here is the ATD file defining our tree type (file \texttt{tree.atd}):
231 |
232 | ## atd () ##
233 | type tree =
234 | [ Empty
235 | | Node of (tree * int * tree) ]
236 | ## () ##
237 |
238 | This is our OCaml program (file \texttt{tree.ml}):
239 |
240 | ## ocaml () ##
241 | open Printf
242 |
243 | (* sample value *)
244 | let tree : Tree_t.tree =
245 | `Node (
246 | `Node (`Empty, 1, `Empty),
247 | 2,
248 | `Node (
249 | `Node (`Empty, 3, `Empty),
250 | 4,
251 | `Node (`Empty, 5, `Empty)
252 | )
253 | )
254 |
255 | let () =
256 | (* write sample value to file *)
257 | let fname = "tree.dat" in
258 | Ag_util.Biniou.to_file Tree_b.write_tree fname tree;
259 |
260 | (* write sample value to string *)
261 | let s = Tree_b.string_of_tree tree in
262 | printf "raw value (saved as %s):\n%S\n" fname s;
263 | printf "length: %i\n" (String.length s);
264 |
265 | printf "pretty-printed value (without dictionary):\n";
266 | print_endline (Bi_io.view s);
267 |
268 | printf "pretty-printed value (with dictionary):\n";
269 | let unhash = Bi_io.make_unhash ["Empty"; "Node"; "foo"; "bar" ] in
270 | print_endline (Bi_io.view ~unhash s)
271 | ## () ##
272 |
273 | Compilation:
274 |
275 | \begin{verbatim}
276 | $ atdgen -t tree.atd
277 | $ atdgen -b tree.atd
278 | $ ocamlfind ocamlopt -o tree \
279 | tree_t.mli tree_t.ml tree_b.mli tree_b.ml tree.ml \
280 | -package atdgen -linkpkg
281 | \end{verbatim}
282 |
283 | Running the program:
284 |
285 | \begin{verbatim}
286 | $ ./tree
287 | raw value (saved as tree.dat):
288 | "\023\179\2276\"\020\003\023\179\2276\"\020\003\023\003\007\170m\017\002\023\003\007\170m\017\004\023\179\2276\"\020\003\023\179\2276\"\020\003\023\003\007\170m\017\006\023\003\007\170m\017\b\023\179\2276\"\020\003\023\003\007\170m\017\n\023\003\007\170m"
289 | length: 75
290 | pretty-printed value (without dictionary):
291 | <#33e33622:
292 | (<#33e33622: (<#0307aa6d>, 1, <#0307aa6d>)>,
293 | 2,
294 | <#33e33622:
295 | (<#33e33622: (<#0307aa6d>, 3, <#0307aa6d>)>,
296 | 4,
297 | <#33e33622: (<#0307aa6d>, 5, <#0307aa6d>)>)>)>
298 | pretty-printed value (with dictionary):
299 | <"Node":
300 | (<"Node": (<"Empty">, 1, <"Empty">)>,
301 | 2,
302 | <"Node":
303 | (<"Node": (<"Empty">, 3, <"Empty">)>,
304 | 4,
305 | <"Node": (<"Empty">, 5, <"Empty">)>)>)>
306 | \end{verbatim}
307 |
308 | Now let's see how to pretty-print any biniou data from the command line.
309 | Our sample data are now in file \texttt{tree.dat}:
310 |
311 | \begin{verbatim}
312 | $ ls -l tree.dat
313 | -rw-r--r-- 1 martin martin 75 Apr 17 01:46 tree.dat
314 | \end{verbatim}
315 |
316 | We use the command \texttt{bdump} to render our sample biniou data as text:
317 |
318 | \begin{verbatim}
319 | $ bdump tree.dat
320 | <#33e33622:
321 | (<#33e33622: (<#0307aa6d>, 1, <#0307aa6d>)>,
322 | 2,
323 | <#33e33622:
324 | (<#33e33622: (<#0307aa6d>, 3, <#0307aa6d>)>,
325 | 4,
326 | <#33e33622: (<#0307aa6d>, 5, <#0307aa6d>)>)>)>
327 | \end{verbatim}
328 |
329 | We got hashes for the variant names \texttt{Empty} and \texttt{Node}.
330 | Let's add them to the dictionary:
331 |
332 | \begin{verbatim}
333 | $ bdump -w Empty,Node tree.dat
334 | <"Node":
335 | (<"Node": (<"Empty">, 1, <"Empty">)>,
336 | 2,
337 | <"Node":
338 | (<"Node": (<"Empty">, 3, <"Empty">)>,
339 | 4,
340 | <"Node": (<"Empty">, 5, <"Empty">)>)>)>
341 | \end{verbatim}
342 |
343 | \texttt{bdump} remembers the dictionary so we don't have to pass the
344 | \texttt{-w} option anymore (for this user on this machine).
345 | The following now works:
346 |
347 | \begin{verbatim}
348 | $ bdump tree.dat
349 | <"Node":
350 | (<"Node": (<"Empty">, 1, <"Empty">)>,
351 | 2,
352 | <"Node":
353 | (<"Node": (<"Empty">, 3, <"Empty">)>,
354 | 4,
355 | <"Node": (<"Empty">, 5, <"Empty">)>)>)>
356 | \end{verbatim}
357 |
358 | Source code for this section:
359 | \url{https://github.com/MyLifeLabs/atdgen-tutorial/tree/master/inspect-biniou}
360 |
361 | \section{\label{defaults}Optional fields and default values}
362 |
363 | Although OCaml records do not support optional fields, both the JSON
364 | and biniou formats make it possible to omit certain fields on a
365 | per-record basis.
366 |
367 | For example the JSON record \texttt{\{ "x": 0, "y": 0 \}} can be more
368 | compactly written as \texttt{\{\}} if the reader knows the default values for
369 | the missing fields \texttt{x} and \texttt{y}. Here is the corresponding type
370 | definition:
371 |
372 | ## atd () ##
373 | type vector_v1 = { ~x: int; ~y: int }
374 | ## () ##
375 |
376 | \texttt{\~{}x} means that field \texttt{x} supports a default
377 | value. Since we do not
378 | specify the default value ourselves, the built-in default is used,
379 | which is 0.
380 |
381 | If we want the default to be something else than 0, we just have to
382 | specify it as follows:
383 |
384 | ## atd () ##
385 | type vector_v2 = {
386 | ~x : int; (* default x is 1 *)
387 | ~y: int; (* default y is 0 *)
388 | }
389 | ## () ##
390 |
391 | It is also possible to specify optional fields without a default
392 | value. For example, let's add an optional \texttt{z} field:
393 |
394 | ## atd () ##
395 | type vector_v3 = {
396 | ~x: int;
397 | ~y: int;
398 | ?z: int option;
399 | }
400 | ## () ##
401 |
402 | The following two examples are valid JSON representations of data of
403 | type \texttt{vector\_v3}:
404 |
405 | \begin{verbatim}
406 | { "x": 2, "y": 2, "z": 3 } // OCaml: { x = 2; y = 2; z = Some 3 }
407 | \end{verbatim}
408 |
409 | \begin{verbatim}
410 | { "x": 2, "y": 2 } // OCaml: { x = 2; y = 2; z = None }
411 | \end{verbatim}
412 |
413 | For a variety of good reasons JSON's \texttt{null} value may not be used to
414 | indicate that a field is undefined.
415 | Therefore the following JSON data cannot be read as a record of type
416 | \texttt{vector\_v3}:
417 |
418 | \begin{verbatim}
419 | { "x": 2, "y": 2, "z": null } // invalid value for field z
420 | \end{verbatim}
421 |
422 |
423 | Note also the difference between \texttt{?z: int option} and \texttt{\~{}z: int
424 | option}:
425 |
426 | ## atd () ##
427 | type vector_v4 = {
428 | ~x: int;
429 | ~y: int;
430 | ~z: int option; (* no unwrapping of the JSON field value! *)
431 | }
432 | ## () ##
433 |
434 | Here are valid values of type \texttt{vector\_v4}, showing that it is usually
435 | not what is intended:
436 |
437 | \begin{verbatim}
438 | { "x": 2, "y": 2, "z": [ "Some", 3 ] }
439 | \end{verbatim}
440 |
441 | \begin{verbatim}
442 | { "x": 2, "y": 2, "z": "None" }
443 | \end{verbatim}
444 |
445 | \begin{verbatim}
446 | { "x": 2, "y": 2 }
447 | \end{verbatim}
448 |
449 |
450 | \section{Smooth protocol upgrades}
451 |
452 | Problem: you have a production system that uses a specific
453 | JSON or biniou format. It may be data files or a client-server
454 | pair. You now want to add a field to a record type or to add a case to
455 | a variant type.
456 |
457 | Both JSON and biniou allow extra record fields. If the
458 | consumer does not know how to deal with the extra field, the default
459 | behavior is to happily ignore it.
460 |
461 |
462 | \subsection{Adding or removing an optional record field}
463 |
464 | ## atd () ##
465 | type t = {
466 | x: int;
467 | y: int;
468 | }
469 | ## () ##
470 |
471 | Same \texttt{.atd} source file, edited:
472 |
473 | ## atd () ##
474 | type t = {
475 | x: int;
476 | y: int;
477 | ~z: int; (* new field *)
478 | }
479 | ## () ##
480 |
481 | \begin{itemize}
482 | \item Upgrade producers and consumers in any order
483 | \item Converting old data is not required nor useful
484 | \end{itemize}
485 |
486 | \subsection{Adding a required record field}
487 |
488 | ## atd () ##
489 | type t = {
490 | x: int;
491 | y: int;
492 | }
493 | ## () ##
494 |
495 | Same \texttt{.atd} source file, edited:
496 |
497 | ## atd () ##
498 | type t = {
499 | x: int;
500 | y: int;
501 | z: int; (* new field *)
502 | }
503 | ## () ##
504 |
505 | \begin{itemize}
506 | \item Upgrade all producers before the consumers
507 | \item Converting old data requires special-purpose hand-written code
508 | \end{itemize}
509 |
510 | \subsection{Removing a required record field}
511 |
512 | \begin{itemize}
513 | \item Upgrade all consumers before the producers
514 | \item Converting old data is not required but may save some storage space
515 | (just read and re-write each record using the new type)
516 | \end{itemize}
517 |
518 | \subsection{Adding a variant case}
519 |
520 | ## atd () ##
521 | type t = [ A | B ]
522 | ## () ##
523 |
524 | Same \texttt{.atd} source file, edited:
525 |
526 | ## atd () ##
527 | type t = [ A | B | C ]
528 | ## () ##
529 |
530 | \begin{itemize}
531 | \item Upgrade all consumers before the producers
532 | \item Converting old data is not required and would have no effect
533 | \end{itemize}
534 |
535 | \subsection{Removing a variant case}
536 |
537 | \begin{itemize}
538 | \item Upgrade all producers before the consumers
539 | \item Converting old data requires special-purpose hand-written code
540 | \end{itemize}
541 |
542 | \subsection{Avoiding future problems}
543 |
544 | \begin{itemize}
545 | \item In doubt, use records rather than tuples because it makes it
546 | possible to add or remove any field or to reorder them.
547 | \item Do not hesitate to create variant types with only one case or
548 | records with only one field if you think they might be extended
549 | later.
550 | \end{itemize}
551 |
552 |
553 | \section{Data validation}
554 |
555 | Atdgen can be used to produce data validators for all types defined
556 | in an ATD file,
557 | based on user-given validators specified only for certain types.
558 | A simple example is:
559 |
560 | ## atd () ##
561 | type t = string option
562 | ## () ##
563 |
564 | \texttt{atdgen -v} will produce something equivalent to the following
565 | implementation:
566 |
567 | ## ocaml () ##
568 | let validate_t x =
569 | match x with
570 | None -> true
571 | | Some x -> (fun s -> String.length s >= 8) x
572 | ## () ##
573 |
574 | Let's now consider a more realistic example with complex validators defined
575 | in a separate .ml file. We created the following 3 source files:
576 |
577 | \begin{itemize}
578 | \item \texttt{resume.atd}: contains the type definitions with annotations
579 | \item \texttt{resume\_util.ml}: contains our handwritten validators
580 | \item \texttt{resume.ml}: is our main program that creates data and
581 | calls the validators
582 | \end{itemize}
583 |
584 | In terms of OCaml modules we have:
585 |
586 | \begin{itemize}
587 | \item \texttt{Resume\_t}: produced by \texttt{atdgen -t resume.atd},
588 | provides OCaml type definitions
589 | \item \texttt{Resume\_util}: depends on \texttt{Resume\_t}, provides
590 | validators mentioned in \texttt{resume.atd}
591 | \item \texttt{Resume\_v}: produced by \texttt{atdgen -v resume.atd},
592 | depends on \texttt{Resume\_util}, provides a validator for each type
593 | \item \texttt{Resume}: depends on \texttt{Resume\_v}, uses the validators
594 | \end{itemize}
595 |
596 | Type definitions are placed in \texttt{resume.atd}:
597 |
598 | ## atd () ##
599 | type text = string
600 |
601 | type date = {
602 | year : int;
603 | month : int;
604 | day : int;
605 | }
606 |
607 | type job = {
608 | company : text;
609 | title : text;
610 | start_date : date;
611 | ?end_date : date option;
612 | }
613 |
614 | type work_experience = job list
615 | ## () ##
616 |
617 | \texttt{resume\_util.ml} contains our handwritten validators:
618 |
619 | ## ocaml () ##
620 | open Resume_t
621 |
622 | let ascii_printable c =
623 | let n = Char.code c in
624 | n >= 32 && n <= 127
625 |
626 | (*
627 | Check that string is not empty and contains only ASCII printable
628 | characters (for the sake of the example; we use UTF-8 these days)
629 | *)
630 | let validate_some_text s =
631 | s <> "" &&
632 | try
633 | String.iter (fun c -> if not (ascii_printable c) then raise Exit) s;
634 | true
635 | with Exit ->
636 | false
637 |
638 | (*
639 | Check that the combination of year, month and day exists in the
640 | Gregorian calendar.
641 | *)
642 | let validate_date x =
643 | let y = x.year in
644 | let m = x.month in
645 | let d = x.day in
646 | m >= 1 && m <= 12 && d >= 1 &&
647 | (let dmax =
648 | match m with
649 | 2 ->
650 | if y mod 4 = 0 && not (y mod 100 = 0) || y mod 400 = 0 then 29
651 | else 28
652 | | 1 | 3 | 5 | 7 | 8 | 10 | 12 -> 31
653 | | _ -> 30
654 | in
655 | d <= dmax)
656 |
657 | (* Compare dates chronologically *)
658 | let compare_date a b =
659 | let c = compare a.year b.year in
660 | if c <> 0 then c
661 | else
662 | let c = compare a.month b.month in
663 | if c <> 0 then c
664 | else compare a.day b.day
665 |
666 | (* Check that the end_date, when defined, is not earlier than the start_date *)
667 | let validate_job x =
668 | match x.end_date with
669 | None -> true
670 | | Some end_date ->
671 | compare_date x.start_date end_date <= 0
672 | ## () ##
673 |
674 | \texttt{resume.ml} uses the \texttt{validate\_work\_experience}
675 | function provided by the \texttt{Resume\_v} module:
676 |
677 | ## ocaml () ##
678 | let check_experience x =
679 | let is_valid = Resume_v.validate_work_experience x in
680 | Printf.printf "%s:\n%s\n"
681 | (if is_valid then "VALID" else "INVALID")
682 | (Yojson.Safe.prettify (Resume_j.string_of_work_experience x))
683 |
684 | let () =
685 | (* one valid date *)
686 | let valid = { Resume_t.year = 2000; month = 2; day = 29 } in
687 | (* one invalid date *)
688 | let invalid = { Resume_t.year = 1900; month = 0; day = 0 } in
689 | (* two more valid dates, created with Resume_v.create_date *)
690 | let date1 = { Resume_t.year = 2005; month = 8; day = 1 } in
691 | let date2 = { Resume_t.year = 2006; month = 3; day = 22 } in
692 |
693 | let job = {
694 | Resume_t.company = "Acme Corp.";
695 | title = "Tester";
696 | start_date = date1;
697 | end_date = Some date2;
698 | }
699 | in
700 | let valid_job = { job with Resume_t.start_date = valid } in
701 | let invalid_job = { job with Resume_t.end_date = Some invalid } in
702 | let valid_experience = [ job; valid_job ] in
703 | let invalid_experience = [ job; invalid_job ] in
704 | check_experience valid_experience;
705 | check_experience invalid_experience
706 | ## () ##
707 |
708 | Output:
709 |
710 | \begin{verbatim}
711 | VALID:
712 | [
713 | {
714 | "company": "Acme Corp.",
715 | "title": "Tester",
716 | "start_date": { "year": 2005, "month": 8, "day": 1 },
717 | "end_date": { "year": 2006, "month": 3, "day": 22 }
718 | },
719 | {
720 | "company": "Acme Corp.",
721 | "title": "Tester",
722 | "start_date": { "year": 2000, "month": 2, "day": 29 },
723 | "end_date": { "year": 2006, "month": 3, "day": 22 }
724 | }
725 | ]
726 | INVALID:
727 | [
728 | {
729 | "company": "Acme Corp.",
730 | "title": "Tester",
731 | "start_date": { "year": 2005, "month": 8, "day": 1 },
732 | "end_date": { "year": 2006, "month": 3, "day": 22 }
733 | },
734 | {
735 | "company": "Acme Corp.",
736 | "title": "Tester",
737 | "start_date": { "year": 2005, "month": 8, "day": 1 },
738 | "end_date": { "year": 1900, "month": 0, "day": 0 }
739 | }
740 | ]
741 | \end{verbatim}
742 |
743 | Source code for this section:
744 | \url{https://github.com/MyLifeLabs/atdgen-tutorial/tree/master/validate}
745 |
746 |
747 |
748 | \section{Modularity: referring to type definitions from another ATD file}
749 |
750 | It is possible to define types that depend on types
751 | defined in other \texttt{.atd} files.
752 | The example below is self-explanatory.
753 |
754 | \texttt{part1.atd}:
755 |
756 | ## atd () ##
757 | type t = { x : int; y : int }
758 | ## () ##
759 |
760 | \texttt{part2.atd}:
761 |
762 | ## atd () ##
763 | type t1 = abstract
764 | (*
765 | Imports type t defined in file part1.atd.
766 | The local name is t1. Because the local name (t1) is different from the
767 | original name (t), we must specify the original name using t=.
768 | *)
769 |
770 | type t2 = t1 list
771 | ## () ##
772 |
773 | \texttt{part3.atd}:
774 |
775 | ## atd () ##
776 | type t2 = abstract
777 |
778 | type t3 = {
779 | name : string;
780 | ?data : t2 option;
781 | }
782 | ## () ##
783 |
784 | \texttt{main.ml}:
785 |
786 | ## ocaml () ##
787 | let v = {
788 | Part3_t.name = "foo";
789 | data = Some [
790 | { Part1_t.x = 1; y = 2 };
791 | { Part1_t.x = 3; y = 4 };
792 | ]
793 | }
794 |
795 | let () =
796 | Ag_util.Json.to_channel Part3_j.write_t3 stdout v;
797 | print_newline ()
798 | ## () ##
799 |
800 | Output:
801 |
802 | \begin{verbatim}
803 | {"name":"foo","data":[{"x":1,"y":2},{"x":3,"y":4}]}
804 | \end{verbatim}
805 |
806 | Source code for this section:
807 | \url{https://github.com/MyLifeLabs/atdgen-tutorial/tree/master/modularity}
808 |
809 |
810 | \section{Managing JSON configuration files}
811 |
812 | JSON makes a good format for configuration files because it is
813 | human-readable, easy to modify programmatically and widespread.
814 | Here is an example of how to use atdgen to manage config files.
815 |
816 | \begin{itemize}
817 | \item \textbf{Specifying defaults} is done in the .atd file. See
818 | section \ref{defaults} for details on how to do that.
819 | \item \textbf{Auto-generating a template config file with default values}:
820 | a sample value in the OCaml world needs to be created but only
821 | fields without default need to be specified.
822 | \item \textbf{Describing the format} is achieved by embedding the .atd
823 | type definitions in the OCaml program and printing it out on request.
824 | \item \textbf{Loading a config file and reporting illegal fields} is
825 | achieved using the JSON deserializers produced by \texttt{atdgen
826 | -j}. Option \texttt{-j-strict-fields} ensures the misspelled field
827 | names are not ignored but reported as errors.
828 | \item \textbf{Reindenting a config file} is achieved by the
829 | pretty-printing function \texttt{Yojson.Safe.prettify} that takes a
830 | JSON string and returns an equivalent JSON string.
831 | \item \textbf{Showing implicit (default) settings} is achieved by
832 | passing the \texttt{-j-defaults} option to \texttt{atdgen}.
833 | The OCaml config data is then serialized into JSON containing all
834 | fields, including those whose value is the default.
835 | \end{itemize}
836 |
837 | The example uses the following type definitions:
838 | ## print_atd (file_contents "config-file/config.atd") ##
839 |
840 | Our program will perform the following actions:
841 | \begin{verbatim}
842 | $ ./config -template
843 | {
844 | "title": "",
845 | "timeout": 10,
846 | "credentials": [ { "name": "foo", "key": "0123456789abcdef" } ]
847 | }
848 |
849 | $ ./config -format
850 | type config = {
851 | title : string;
852 | ?description : string option;
853 | ~timeout : int;
854 | ~credentials : param list
855 | ;
857 | }
858 |
859 | type param = {
860 | name : string
861 | ;
862 | key : string
863 | ;
864 | }
865 |
866 | $ cat sample-config.json
867 | {
868 | "title": "Example",
869 | "credentials": [
870 | {
871 | "name": "joeuser",
872 | "key": "db7c0877bdef3016"
873 | },
874 | {
875 | "name": "tester",
876 | "key": "09871ff387ac2b10"
877 | }
878 | ]
879 | }
880 |
881 | $ ./config -validate sample-config.json
882 | {
883 | "title": "Example",
884 | "timeout": 10,
885 | "credentials": [
886 | { "name": "joeuser", "key": "db7c0877bdef3016" },
887 | { "name": "tester", "key": "09871ff387ac2b10" }
888 | ]
889 | }
890 | \end{verbatim}
891 |
892 | This is our \texttt{demo.sh} script that builds and runs our example
893 | program called \texttt{config}:
894 | \verbatiminput{config-file/demo.sh}
895 |
896 | This is the hand-written OCaml program. It can be used as a start
897 | point for a real-world program using a JSON config file:
898 | ## print_ocaml (file_contents "config-file/config.ml") ##
899 |
900 | The full source code for this section with examples can be inspected
901 | and downloaded here:
902 | \url{https://github.com/MyLifeLabs/atdgen-tutorial/tree/master/config-file}
903 |
904 |
905 | \section{Integration with ocamldoc}
906 |
907 | Ocamldoc is a tool that comes with the core OCaml distribution.
908 | It uses comments within \texttt{(**} and \texttt{*)} to produce
909 | hyperlinked documentation (HTML) of module signatures.
910 |
911 | Atdgen can produce \texttt{.mli} files with comments in the syntax supported by
912 | ocamldoc but regular ATD comments within \texttt{(*} and \texttt{*)}
913 | are always discarded
914 | by Atdgen. Instead, \texttt{} must be used and placed after the
915 | element they describe. The contents of the text field must be UTF8-encoded.
916 |
917 | ## atd () ##
918 | type point = {
919 | x : float;
920 | y : float;
921 | ~z
922 |
923 | : float;
924 | }
925 |
933 | ## () ##
934 |
935 | is converted into the following \texttt{.mli} file with
936 | ocamldoc-compatible comments:
937 |
938 | ## ocaml () ##
939 | (**
940 | Point with optional 3rd dimension.
941 |
942 | OCaml example:
943 |
944 | {v
945 | let p =
946 | \{ x = 0.5; y = 1.0; z = 0. \}
947 | v}
948 | *)
949 | type point = {
950 | x: float;
951 | y: float;
952 | z: float (** Optional depth, its default value is [0.0]. *)
953 | }
954 | ## () ##
955 |
956 | The only two forms of markup supported by \texttt{}
957 | are \texttt{\{\{} ... \texttt{\}\}}
958 | for inline code and \texttt{\{\{\{} ... \texttt{\}\}\}} for a block of
959 | preformatted code.
960 |
961 |
962 | \section{Integration with build systems}
963 |
964 | \subsubsection{OMake}
965 |
966 | We provide an
967 | \href{https://github.com/MyLifeLabs/atdgen-omake}{Atdgen
968 | plugin} for \href{http://omake.metaprl.org}{OMake}.
969 | It allows to simplify the compilation rules to a minimum.
970 |
971 | The plugin consists of a self-documented file to copy into a project's
972 | root. The following is a sample \texttt{OMakefile} for a project using JSON and
973 | five source files (\texttt{foo.atd}, \texttt{foo.ml},
974 | \texttt{bar.atd}, \texttt{bar.ml} and \texttt{main.ml}):
975 |
976 | \begin{verbatim}
977 | include Atdgen
978 | # requires file Atdgen.om
979 |
980 | OCAMLFILES = foo_t foo_j foo bar_t bar_j bar main
981 | # correspond to the OCaml modules we want to build
982 |
983 | Atdgen(foo bar, -j-std)
984 | OCamlProgram(foobar, $(OCAMLFILES))
985 |
986 | .DEFAULT: foobar.opt
987 |
988 | .PHONY: clean
989 | clean:
990 | rm -f *.cm[ioxa] *.cmx[as] *.[oa] *.opt *.run *~
991 | rm -f $(ATDGEN_OUTFILES)
992 | \end{verbatim}
993 |
994 | Running \texttt{omake} builds the native code executable
995 | \texttt{foobar.opt}.
996 |
997 | \texttt{omake clean} removes all the products
998 | of compilation including the \texttt{.mli} and \texttt{.ml} produced
999 | by \texttt{atdgen}.
1000 |
1001 |
1002 | \subsubsection{GNU Make}
1003 |
1004 | We provide
1005 | \href{https://github.com/MyLifeLabs/atdgen-omake}{\texttt{Atdgen.mk}},
1006 | a generic makefile that defines the
1007 | dependencies and rules for generating OCaml \texttt{.mli} and
1008 | \texttt{.ml} files from \texttt{.atd} files containing type
1009 | definitions. The \texttt{Atdgen.mk} file contains its own documentation.
1010 |
1011 | Here is a sample \texttt{Makefile} that takes advantage of
1012 | \href{http://www.ocaml.info/home/ocaml\_sources.html\#ocaml-make}{\texttt{OCamlMakefile}}:
1013 | \begin{verbatim}
1014 | .PHONY: default
1015 | default: opt
1016 |
1017 | ATDGEN_SOURCES = foo.atd bar.atd
1018 | ATDGEN_FLAGS = -j-std
1019 | include Atdgen.mk
1020 |
1021 | SOURCES = \
1022 | foo_t.mli foo_t.ml foo_j.mli foo_j.ml \
1023 | bar_t.mli bar_t.ml bar_j.mli bar_j.ml \
1024 | hello.ml
1025 | RESULT = hello
1026 | PACKS = atdgen
1027 | # "include OCamlMakefile" must come after defs for SOURCES, RESULT, PACKS, etc.
1028 | include OCamlMakefile
1029 |
1030 | .PHONY: sources opt all
1031 | sources: $(SOURCES)
1032 | opt: sources
1033 | $(MAKE) native-code
1034 | all: sources
1035 | $(MAKE) byte-code
1036 | \end{verbatim}
1037 |
1038 | \texttt{make} alone builds a native code executable from source files
1039 | \texttt{foo.atd}, \texttt{bar.atd} and \texttt{hello.ml}.
1040 | \texttt{make clean} removes generated files. \texttt{make all} builds
1041 | a bytecode executable.
1042 | In addition to \texttt{native-code}, \texttt{byte-code} and
1043 | \texttt{clean}, \texttt{OCamlMakefile} provides a number of other targets
1044 | and options which are documented in \texttt{OCamlMakefile}'s README.
1045 |
1046 | \subsubsection{Ocamlbuild}
1047 |
1048 | Rule to add in \texttt{myocamlbuild.ml}:
1049 | \begin{verbatim}
1050 | let _ = dispatch begin function
1051 | | After_rules -> rule "atdgen: .atd -> _t.ml*, _j.ml*"
1052 | ~prods:["%_t.ml";"%_t.mli";"%_j.ml";"%_j.mli";"%_v.ml";"%_v.mli";]
1053 | ~dep:"%.atd"
1054 | (fun env build ->
1055 | let atdgen = "atdgen" in
1056 | Seq [
1057 | Cmd (S [A atdgen; A "-t"; P (env "%.atd")]);
1058 | Cmd (S [A atdgen; A "-j"; A "-j-std"; P (env "%.atd")]);
1059 | Cmd (S [A atdgen; A "-v"; P (env "%.atd")]);
1060 | ]
1061 | );
1062 | end
1063 | \end{verbatim}
1064 |
1065 | Flag to add in \texttt{_tags} file:
1066 | \begin{verbatim}
1067 | <*.{ml,mli,byte,native}>:package(atdgen),package(biniou)
1068 | \end{verbatim}
1069 |
--------------------------------------------------------------------------------