🔗 Docs
This section has code examples to help you understand it better. To run the examples:
make demo-destructing_ast
- Description
- AST Structure Pattern Matching
- Using
Ast_pattern
High-Level Destructors - Using Metaquot
- Conclusion
Destructuring an AST (Abstract Syntax Tree) is essential when creating a PPX (preprocessor extension) in OCaml. To generate or transform code, you must first break down the AST to understand and manipulate its structure.
For example, if you want to transform this code:
let one = [%one]
into:
let one = 1
You’ll need to destructure the AST representing the extension point ([%one]
) to replace it with 1
.
There are several ways to destructure an AST. We’ll explore three methods:
- AST Structure Pattern Matching
- Using
Ast_pattern
High-Level Destructors - Using Metaquot
The most fundamental method for destructuring an AST in PPXLib is by directly matching on the AST’s structure.
Let’s say we want to destructure an AST representing the integer 1
:
let match_int_payload ~loc payload =
match payload with
| PStr
[
{
pstr_desc =
Pstr_eval
({ pexp_desc = Pexp_constant (Pconst_integer (value, None)); _ }, _);
_;
};
] -> (
try Ok (value |> int_of_string)
with Failure _ ->
Error (Location.Error.createf ~loc "Value is not a valid integer"))
| _ -> Error (Location.Error.createf ~loc "Wrong pattern")
- Pattern Matching the Payload:
- Begins by matching the
payload
with the expected structure. - The pattern expects a structure (
PStr
) containing a single item.
- Begins by matching the
- Destructuring the Structure Item:
- Matches the
pstr_desc
field, expecting an evaluated expression (Pstr_eval
). - The expression should be a constant integer (
Pexp_constant
withPconst_integer
). - Captures the integer value as a string in
value
.
- Matches the
- Handling the Matched Value:
- Converts the
value
to an integer and returnsOk
if successful. - If conversion fails, returns an error message.
- Converts the
- Handling Mismatched Patterns:
- If the
payload
doesn’t match the expected structure, it returns an error.
- If the
While this method is powerful, it can be verbose and difficult to maintain as patterns become more complex.
To make AST destructuring more readable, PPXLib provides the Ast_pattern
module, which offers high-level destructors.
Let’s destructure the same integer 1
AST using Ast_pattern
:
open Ppxlib
let match_int_payload =
let open Ast_pattern in
pstr (pstr_eval (pexp_constant (pconst_integer (string "1") none)) nil ^:: nil)
This code achieves the same result as the previous example but in a more concise and readable way.
PStr
becomespstr
Pstr_eval
becomespstr_eval
Pexp_constant
becomespexp_constant
Pconst_integer
becomespconst_integer
You can further simplify it:
let match_int_payload =
let open Ast_pattern in
pstr (pstr_eval (eint (int 1)) nil ^:: nil)
Using eint
instead of pexp_constant
and pconst_integer
provides better type safety. The int
wildcard captures the integer value.
Metaquot is a syntax extension that allows you to write and destructure ASTs more intuitively.
Let’s destructure the same integer 1
AST with Metaquot:
let match_int_payload expr =
match expr with
| [%expr 1] -> Ok 1
| _ -> Error (Location.Error.createf ~loc:expr.pexp_loc "Wrong pattern")
For example, to match any expression of the form 1 + <int>
:
let match_int_payload expr =
match expr with
| [%expr 1 + [%e? e]] -> (
match e with
| { pexp_desc = Pexp_constant (Pconst_integer (value, None)); _ } ->
Ok (1 + int_of_string value)
| _ -> Error (Location.Error.createf ~loc:e.pexp_loc "Invalid integer"))
| _ -> Error (Location.Error.createf ~loc:expr.pexp_loc "Wrong pattern")
Metaquot simplifies the process, making the AST patterns more readable, especially for complex structures.
In this section, we explored different methods to destructure an AST using PPXLib:
- AST Structure Pattern Matching: Powerful but verbose.
- Using
Ast_pattern
High-Level Destructors: More readable and maintainable. - Using Metaquot: Intuitive and effective for both simple and complex patterns.
There’s no right way to destructure an AST, choose the approach that best fits your use case. Understanding all these methods is valuable for creating robust and maintainable PPXs.