# Generate typed AST fragments

**URL:** https://discuss.ocaml.org/t/generate-typed-ast-fragments/13824
**Category:** Ecosystem
**Tags:** metaprogramming
**Created:** [January 9, 2024, 2:58pm UTC](https://discuss.ocaml.org/t/generate-typed-ast-fragments/13824 "2024-01-09T14:58:25Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![samoht](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/samoht/32/81_2.png) [@samoht](https://discuss.ocaml.org/u/samoht)
#### Post date: [January 9, 2024, 2:58pm UTC](https://discuss.ocaml.org/t/generate-typed-ast-fragments/13824/1 "2024-01-09T14:58:25Z")

</div>

What’s the best way nowadays to generate a well-formed AST (optionally check that it is well-typed) and serialize it to a string? These ASTs might use some external libraries (that are not necessarily directly available within the code that generates those ASTs).

I guess it’s a good use case for MetaOCaml, but is there any lightweight way to do this, for instance, with `ppx`? I’ve seen [metapp](https://github.com/thierry-martinez/metapp) and [ppx\_stage](https://github.com/stedolan/ppx_stage), but I’m not sure I need the full power of those. I want to keep the difference between code that runs at configure-time and runtime (because they correspond to different aspects of my software lifecycle) – so I just want to generate, at configure-time, a well-typed AST with some well-typed holes that will not fail at compile-time or runtime.

The use-case is to generate devices for MirageOS, where we need to generate runtime code for parsing command-line arguments or initialising devices. Right now, we have a mix of runtime libraries (where this runtime code lives and that needs to be linked with our program), some meta-programming shims for configure-time configuration (to generate some runtime code that will reference and manipulate these libraries and runtime functions) and some poor-man configure-time code generation (using strings). I am sure there is nice/clean way to do this 🙂

---

<div class="post-metadata">

### Author: ![rizo](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/rizo/32/93_2.png) [@rizo](https://discuss.ocaml.org/u/rizo)
#### Post date: [January 9, 2024, 4:37pm UTC](https://discuss.ocaml.org/t/generate-typed-ast-fragments/13824/2 "2024-01-09T16:37:48Z")

</div>

For simple untyped AST construction and manipulation, I think ppxlib’s `metaquot` ppx is quite nice. Once a `Parsetree` type is constructed it can be easily serialised to a string with `Pprintast`.

For example, that’s the approach used by [ocaml-swagger](https://github.com/andrenth/ocaml-swagger) to generated HTTP+JSON bindings for APIs.

For typed code generation _and_ stage differentiation it might be harder to find a simple off-the-shelf solution. From my understanding, the difficulty is that MetaOCaml and inspired variants like ppx\_stage are great for generating typed expressions, but not entire programs.

---

<div class="post-metadata">

### Author: ![jbeckford](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/jbeckford/32/3027_2.png) [@jbeckford](https://discuss.ocaml.org/u/jbeckford)
#### Post date: [January 9, 2024, 9:13pm UTC](https://discuss.ocaml.org/t/generate-typed-ast-fragments/13824/3 "2024-01-09T21:13:18Z")

</div>

Building on @rizo’s answer, once you have a `Parsetree` you can build a `Typedtree` with compiler-libs as long as you are willing to predefine the referenced types that the code needs. That is how I ported some of the BER MetaOCaml code to PPX.

Quick walkthrough:

1. The input type `type 'a code = Parsetree.expression` and the output type `type closed_code_repr = Typedtree.expression` in [trx.ml](https://gitlab.com/diskuv/samples/merrychristmas2023/-/blob/049abb26afdaedbf4264e0cb21c9ca964947f953/dependencies/okmij/trx.ml).
2. The transformation signature `val close_code : 'a code -> 'a closed_code` in [codelib.mli](https://gitlab.com/diskuv/samples/merrychristmas2023/-/blob/049abb26afdaedbf4264e0cb21c9ca964947f953/dependencies/okmij/codelib.mli).
3. The implementation of `close_code` including the definitions of external modules that your `Parsetree` AST is type-checked against. [codelib.ml](https://gitlab.com/diskuv/samples/merrychristmas2023/-/blob/049abb26afdaedbf4264e0cb21c9ca964947f953/dependencies/okmij/codelib.ml)

All of that assumes a closed environment that you can type-check against. From what I understand of MirageOS configuration that should be plausible.
