Skip to content

Commit d024c75

Browse files
committed
Start ppx-by-example doc
Signed-off-by: Pedro B S Lisboa <[email protected]>
1 parent c1e73ea commit d024c75

File tree

2 files changed

+26
-160
lines changed

2 files changed

+26
-160
lines changed

doc/dune

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,13 @@
11
(documentation
22
(package ppxlib))
3+
4+
(rule
5+
(alias doc)
6+
(deps
7+
(glob_files ./images/*))
8+
(action
9+
(progn
10+
(system "mkdir -p %{project_root}/_doc/_html/ppxlib/assets/images")
11+
(system "chmod -R a+rw %{project_root}/_doc/_html/ppxlib/assets/images")
12+
(system
13+
"cp -R ./images/ %{project_root}/_doc/_html/ppxlib/assets/images/"))))

doc/examples.mld

Lines changed: 15 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1,167 +1,22 @@
1-
{%html: <div style="display: flex; justify-content:space-between"><div>%}{{!"good-practices"}< Good practices}{%html: </div><div>%}{%html: </div></div>%}
1+
{0 PPX by examples}
22

3-
{0 Examples}
3+
This part of the documentation was made to help on understanding what are and how to write PPXs in OCaml with PPXLib.
44

5-
This section is here to allow viewing complete examples of PPXs written using [ppxlib] directly in the documentation. However, they are not "complete" in the sense that the overall organization, such as the [dune] files, is not included.
5+
{1 Content}
66

7-
In order to see a fully working complete example of a PPX written using [ppxlib], that you can compile, modify and test, go to the {{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples}examples} folder of ppxlib sources.
7+
- {{!page-"example-ast"} AST}
8+
{ul {- {{!page-"example-ast-building"} Building an AST}}}
9+
{ul {- {{!page-"example-ast-destructing"} Destructing an AST}}}
810

9-
{1 [ppx_deriving_accesors]}
11+
- {{!page-"example-writing-ppxs"} Writing a PPX}
12+
{ul {- {{!page-"example-writing-ppxs-context-free"} Context-free transformations}}}
13+
{ul {- {{!page-"example-writing-ppxs-global"} Global transformations}}}
1014

11-
The fully complete, ready-to-compile [ppx_deriving_accesors] example is accessible in [ppxlib]'s {{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/simple-deriver}sources}.
15+
- {{!page-"example-testing-ppxs-index"} Testing a PPX}
1216

13-
This deriver will generate accessors for record fields, from the record type
14-
definition.
17+
{1 References}
1518

16-
For example, this code:
17-
18-
{@ocaml[
19-
type t =
20-
{ a : string
21-
; b : int
22-
}
23-
[@@deriving accessors]
24-
]}
25-
26-
will generate the following, appended after the type definition:
27-
28-
{@ocaml[
29-
let a x = x.a
30-
let b x = x.b
31-
]}
32-
33-
The entire code is:
34-
35-
{@ocaml[
36-
open Ppxlib
37-
module List = ListLabels
38-
open Ast_builder.Default
39-
40-
let accessor_impl (ld : label_declaration) =
41-
let loc = ld.pld_loc in
42-
pstr_value ~loc Nonrecursive
43-
[
44-
{
45-
pvb_pat = ppat_var ~loc ld.pld_name;
46-
pvb_expr =
47-
pexp_fun ~loc Nolabel None
48-
(ppat_var ~loc { loc; txt = "x" })
49-
(pexp_field ~loc
50-
(pexp_ident ~loc { loc; txt = lident "x" })
51-
{ loc; txt = lident ld.pld_name.txt });
52-
pvb_attributes = [];
53-
pvb_loc = loc;
54-
};
55-
]
56-
57-
let accessor_intf ~ptype_name (ld : label_declaration) =
58-
let loc = ld.pld_loc in
59-
psig_value ~loc
60-
{
61-
pval_name = ld.pld_name;
62-
pval_type =
63-
ptyp_arrow ~loc Nolabel
64-
(ptyp_constr ~loc { loc; txt = lident ptype_name.txt } [])
65-
ld.pld_type;
66-
pval_attributes = [];
67-
pval_loc = loc;
68-
pval_prim = [];
69-
}
70-
71-
let generate_impl ~ctxt (_rec_flag, type_declarations) =
72-
let loc = Expansion_context.Deriver.derived_item_loc ctxt in
73-
List.map type_declarations ~f:(fun (td : type_declaration) ->
74-
match td with
75-
| {
76-
ptype_kind = Ptype_abstract | Ptype_variant _ | Ptype_open;
77-
ptype_loc;
78-
_;
79-
} ->
80-
let ext =
81-
Location.error_extensionf ~loc:ptype_loc
82-
"Cannot derive accessors for non record types"
83-
in
84-
[ Ast_builder.Default.pstr_extension ~loc ext [] ]
85-
| { ptype_kind = Ptype_record fields; _ } ->
86-
List.map fields ~f:accessor_impl)
87-
|> List.concat
88-
89-
let generate_intf ~ctxt (_rec_flag, type_declarations) =
90-
let loc = Expansion_context.Deriver.derived_item_loc ctxt in
91-
List.map type_declarations ~f:(fun (td : type_declaration) ->
92-
match td with
93-
| {
94-
ptype_kind = Ptype_abstract | Ptype_variant _ | Ptype_open;
95-
ptype_loc;
96-
_;
97-
} ->
98-
let ext =
99-
Location.error_extensionf ~loc:ptype_loc
100-
"Cannot derive accessors for non record types"
101-
in
102-
[ Ast_builder.Default.psig_extension ~loc ext [] ]
103-
| { ptype_kind = Ptype_record fields; ptype_name; _ } ->
104-
List.map fields ~f:(accessor_intf ~ptype_name))
105-
|> List.concat
106-
107-
let impl_generator = Deriving.Generator.V2.make_noarg generate_impl
108-
let intf_generator = Deriving.Generator.V2.make_noarg generate_intf
109-
110-
let my_deriver =
111-
Deriving.add "accessors" ~str_type_decl:impl_generator
112-
~sig_type_decl:intf_generator
113-
]}
114-
115-
{1 [ppx_get_env]}
116-
117-
The fully complete, ready-to-compile [ppx_get_env] example is accessible in [ppxlib]'s {{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/simple-extension-rewriter}sources}.
118-
119-
A PPX rewriter that will expand [[%get_env "SOME_ENV_VAR"]] into the value of the
120-
env variable [SOME_ENV_VAR] at compile time, as a string.
121-
122-
E.g., assuming we set [MY_VAR="foo"], it will turn:
123-
124-
{@ocaml[
125-
let () = print_string [%get_env "foo"]
126-
]}```
127-
128-
into:
129-
130-
{@ocaml[
131-
let () = print_string "foo"
132-
]}
133-
134-
135-
Note that this is just a toy example, and we actually advise against this
136-
type of PPX that has side effects or relies heavily on the file system or [env]
137-
variables, unless you absolutely you know what you're doing.
138-
139-
In this case, it won't work well with Dune, since Dune won't know
140-
about the dependency on the env variables specified in the extension's payload.
141-
142-
The entire code is:
143-
144-
{@ocaml[
145-
open Ppxlib
146-
147-
let expand ~ctxt env_var =
148-
let loc = Expansion_context.Extension.extension_point_loc ctxt in
149-
match Sys.getenv env_var with
150-
| value -> Ast_builder.Default.estring ~loc value
151-
| exception Not_found ->
152-
let ext =
153-
Location.error_extensionf ~loc "The environment variable %s is unbound"
154-
env_var
155-
in
156-
Ast_builder.Default.pexp_extension ~loc ext
157-
158-
let my_extension =
159-
Extension.V3.declare "get_env" Extension.Context.expression
160-
Ast_pattern.(single_expr_payload (estring __))
161-
expand
162-
163-
let rule = Ppxlib.Context_free.Rule.extension my_extension
164-
let () = Driver.register_transformation ~rules:[ rule ] "get_env"
165-
]}
166-
167-
{%html: <div style="display: flex; justify-content:space-between"><div>%}{{!"good-practices"}< Good practices}{%html: </div><div>%}{%html: </div></div>%}
19+
- {{:https://ocaml.org/docs/metaprogramming} Ocaml Metaprogramming Docs}
20+
- {{:https://ocaml-ppx.github.io/ppxlib/ppxlib/index.html} PPXLib documentation}
21+
- {{:https://www.youtube.com/live/dMoRMqQ6GLs?feature=shared&t=4251} The needed introduction to writing a ppx}
22+
- {{:https://tarides.com/blog/2019-05-09-an-introduction-to-ocaml-ppx-ecosystem/} An introduction to OCaml PPX ecosystem}

0 commit comments

Comments
 (0)