mirror of
https://github.com/ocaml-tracing/ocaml-trace.git
synced 2026-03-09 12:23:32 -04:00
102 lines
6.9 KiB
HTML
102 lines
6.9 KiB
HTML
<!DOCTYPE html>
|
||
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>examples (ppxlib.examples)</title><meta charset="utf-8"/><link rel="stylesheet" href="../_odoc-theme/odoc.css"/><meta name="generator" content="odoc 3.1.0"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body class="odoc"><nav class="odoc-nav"><a href="index.html">Up</a> – <a href="../index.html">Index</a> » <a href="index.html">ppxlib</a> » examples</nav><header class="odoc-preamble"><p> <div style="display: flex; justify-content:space-between"><div><a href="good-practices.html" title="good-practices">< Good practices</a> </div><div> </div></div></p><h1 id="examples"><a href="#examples" class="anchor"></a>Examples</h1><p>This section is here to allow viewing complete examples of PPXs written using <code>ppxlib</code> directly in the documentation. However, they are not "complete" in the sense that the overall organization, such as the <code>dune</code> files, is not included.</p><p>In order to see a fully working complete example of a PPX written using <code>ppxlib</code>, that you can compile, modify and test, go to the <a href="https://github.com/ocaml-ppx/ppxlib/tree/main/examples">examples</a> folder of ppxlib sources.</p></header><div class="odoc-tocs"><nav class="odoc-toc odoc-local-toc"><ul><li><a href="#ppx_deriving_accesors"><code>ppx_deriving_accesors</code></a></li><li><a href="#ppx_get_env"><code>ppx_get_env</code></a></li></ul></nav></div><div class="odoc-content"><h2 id="ppx_deriving_accesors"><a href="#ppx_deriving_accesors" class="anchor"></a><code>ppx_deriving_accesors</code></h2><p>The fully complete, ready-to-compile <code>ppx_deriving_accesors</code> example is accessible in <code>ppxlib</code>'s <a href="https://github.com/ocaml-ppx/ppxlib/tree/main/examples/simple-deriver">sources</a>.</p><p>This deriver will generate accessors for record fields, from the record type definition.</p><p>For example, this code:</p><pre class="language-ocaml"><code>type t =
|
||
{ a : string
|
||
; b : int
|
||
}
|
||
[@@deriving accessors]</code></pre><p>will generate the following, appended after the type definition:</p><pre class="language-ocaml"><code>let a x = x.a
|
||
let b x = x.b</code></pre><p>The entire code is:</p><pre class="language-ocaml"><code>open Ppxlib
|
||
module List = ListLabels
|
||
open Ast_builder.Default
|
||
|
||
let accessor_impl (ld : label_declaration) =
|
||
let loc = ld.pld_loc in
|
||
pstr_value ~loc Nonrecursive
|
||
[
|
||
{
|
||
pvb_pat = ppat_var ~loc ld.pld_name;
|
||
pvb_expr =
|
||
pexp_fun ~loc Nolabel None
|
||
(ppat_var ~loc { loc; txt = "x" })
|
||
(pexp_field ~loc
|
||
(pexp_ident ~loc { loc; txt = lident "x" })
|
||
{ loc; txt = lident ld.pld_name.txt });
|
||
pvb_attributes = [];
|
||
pvb_loc = loc;
|
||
};
|
||
]
|
||
|
||
let accessor_intf ~ptype_name (ld : label_declaration) =
|
||
let loc = ld.pld_loc in
|
||
psig_value ~loc
|
||
{
|
||
pval_name = ld.pld_name;
|
||
pval_type =
|
||
ptyp_arrow ~loc Nolabel
|
||
(ptyp_constr ~loc { loc; txt = lident ptype_name.txt } [])
|
||
ld.pld_type;
|
||
pval_attributes = [];
|
||
pval_loc = loc;
|
||
pval_prim = [];
|
||
}
|
||
|
||
let generate_impl ~ctxt (_rec_flag, type_declarations) =
|
||
let loc = Expansion_context.Deriver.derived_item_loc ctxt in
|
||
List.map type_declarations ~f:(fun (td : type_declaration) ->
|
||
match td with
|
||
| {
|
||
ptype_kind = Ptype_abstract | Ptype_variant _ | Ptype_open;
|
||
ptype_loc;
|
||
_;
|
||
} ->
|
||
let ext =
|
||
Location.error_extensionf ~loc:ptype_loc
|
||
"Cannot derive accessors for non record types"
|
||
in
|
||
[ Ast_builder.Default.pstr_extension ~loc ext [] ]
|
||
| { ptype_kind = Ptype_record fields; _ } ->
|
||
List.map fields ~f:accessor_impl)
|
||
|> List.concat
|
||
|
||
let generate_intf ~ctxt (_rec_flag, type_declarations) =
|
||
let loc = Expansion_context.Deriver.derived_item_loc ctxt in
|
||
List.map type_declarations ~f:(fun (td : type_declaration) ->
|
||
match td with
|
||
| {
|
||
ptype_kind = Ptype_abstract | Ptype_variant _ | Ptype_open;
|
||
ptype_loc;
|
||
_;
|
||
} ->
|
||
let ext =
|
||
Location.error_extensionf ~loc:ptype_loc
|
||
"Cannot derive accessors for non record types"
|
||
in
|
||
[ Ast_builder.Default.psig_extension ~loc ext [] ]
|
||
| { ptype_kind = Ptype_record fields; ptype_name; _ } ->
|
||
List.map fields ~f:(accessor_intf ~ptype_name))
|
||
|> List.concat
|
||
|
||
let impl_generator = Deriving.Generator.V2.make_noarg generate_impl
|
||
let intf_generator = Deriving.Generator.V2.make_noarg generate_intf
|
||
|
||
let my_deriver =
|
||
Deriving.add "accessors" ~str_type_decl:impl_generator
|
||
~sig_type_decl:intf_generator</code></pre><h2 id="ppx_get_env"><a href="#ppx_get_env" class="anchor"></a><code>ppx_get_env</code></h2><p>The fully complete, ready-to-compile <code>ppx_get_env</code> example is accessible in <code>ppxlib</code>'s <a href="https://github.com/ocaml-ppx/ppxlib/tree/main/examples/simple-extension-rewriter">sources</a>.</p><p>A PPX rewriter that will expand <code>[%get_env "SOME_ENV_VAR"]</code> into the value of the env variable <code>SOME_ENV_VAR</code> at compile time, as a string.</p><p>E.g., assuming we set <code>MY_VAR="foo"</code>, it will turn:</p><pre class="language-ocaml"><code>let () = print_string [%get_env "foo"]</code></pre><p>```</p><p>into:</p><pre class="language-ocaml"><code>let () = print_string "foo"</code></pre><p>Note that this is just a toy example, and we actually advise against this type of PPX that has side effects or relies heavily on the file system or <code>env</code> variables, unless you absolutely you know what you're doing.</p><p>In this case, it won't work well with Dune, since Dune won't know about the dependency on the env variables specified in the extension's payload.</p><p>The entire code is:</p><pre class="language-ocaml"><code>open Ppxlib
|
||
|
||
let expand ~ctxt env_var =
|
||
let loc = Expansion_context.Extension.extension_point_loc ctxt in
|
||
match Sys.getenv env_var with
|
||
| value -> Ast_builder.Default.estring ~loc value
|
||
| exception Not_found ->
|
||
let ext =
|
||
Location.error_extensionf ~loc "The environment variable %s is unbound"
|
||
env_var
|
||
in
|
||
Ast_builder.Default.pexp_extension ~loc ext
|
||
|
||
let my_extension =
|
||
Extension.V3.declare "get_env" Extension.Context.expression
|
||
Ast_pattern.(single_expr_payload (estring __))
|
||
expand
|
||
|
||
let rule = Ppxlib.Context_free.Rule.extension my_extension
|
||
let () = Driver.register_transformation ~rules:[ rule ] "get_env"</code></pre><p> <div style="display: flex; justify-content:space-between"><div><a href="good-practices.html" title="good-practices">< Good practices</a> </div><div> </div></div></p></div></body></html>
|