|
| 1 | +{0 Destructuring AST} |
| 2 | + |
| 3 | +{1:table-of-contents Table of Contents} |
| 4 | + |
| 5 | +- {{!section-description} Description} |
| 6 | +- {{!section-"ast-structure-pattern-matching"} AST Structure Pattern Matching} |
| 7 | +{ul {- {{!section-"example-matching-integer-payload-manually"} Example: Matching Integer Payload Manually}}} |
| 8 | + |
| 9 | +- {{!section-"using-ast_pattern-high-level-destructors"} Using {!Ppxlib.Ast_pattern} High-Level Destructors} |
| 10 | +{ul {- {{!section-"example-1-matching-integer-payload-with-ast_pattern"} Example 1: Matching Integer Payload with [Ppxlib.Ast_pattern]}}} |
| 11 | +{ul {- {{!section-"example-2-simplifying-matching-with-eint"} Example 2: Simplifying Matching with [eint]}}} |
| 12 | + |
| 13 | +- {{!section-"using-metaquot"} Using Metaquot} |
| 14 | +{ul {- {{!section-"example-1-matching-integer-payload-with-metaquot"} Example 1: Matching Integer Payload with Metaquot}}} |
| 15 | +{ul {- {{!section-"example-2-matching-complex-expressions-with-metaquot-and-anti-quotations"} Example 2: Matching Complex Expressions with Metaquot and Anti-Quotations}}} |
| 16 | + |
| 17 | +- {{!section-"conclusion"} Conclusion} |
| 18 | + |
| 19 | +{1:description Description} |
| 20 | + |
| 21 | +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. |
| 22 | + |
| 23 | +For example, if you want to transform this code: |
| 24 | + |
| 25 | +{[ |
| 26 | +let one = [%one] |
| 27 | +]} |
| 28 | + |
| 29 | +into: |
| 30 | + |
| 31 | +{[ |
| 32 | +let one = 1 |
| 33 | +]} |
| 34 | + |
| 35 | +You’ll need to destructure the AST representing the extension point ([%one]) to replace it with [1]. |
| 36 | +There are several ways to destructure an AST. We’ll explore three methods: |
| 37 | + |
| 38 | +- {b AST Structure Pattern Matching} |
| 39 | +- {b Using {!Ppxlib.Ast_pattern} High-Level Destructors} |
| 40 | +- {b Using Metaquot} |
| 41 | + |
| 42 | +{1:ast-structure-pattern-matching AST Structure Pattern Matching} |
| 43 | + |
| 44 | +The most fundamental method for destructuring an AST in PPXLib is by directly matching on the AST’s structure. |
| 45 | + |
| 46 | +{2:example-matching-integer-payload-manually Example: Matching Integer Payload Manually} |
| 47 | + |
| 48 | +{{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/1-AST/a%20-%20Building%20AST/destructuring_ast.ml#L11-L26} 🔗 Sample Code} |
| 49 | + |
| 50 | +Let’s say we want to destructure an AST representing the integer [1]: |
| 51 | + |
| 52 | +{[ |
| 53 | +let match_int_payload ~loc payload = |
| 54 | + match payload with |
| 55 | + | PStr |
| 56 | + [ |
| 57 | + { |
| 58 | + pstr_desc = |
| 59 | + Pstr_eval |
| 60 | + ({ pexp_desc = Pexp_constant (Pconst_integer (value, None)); _ }, _); |
| 61 | + _; |
| 62 | + }; |
| 63 | + ] -> ( |
| 64 | + try Ok (value |> int_of_string) |
| 65 | + with Failure _ -> |
| 66 | + Error (Location.Error.createf ~loc "Value is not a valid integer")) |
| 67 | + | _ -> Error (Location.Error.createf ~loc "Wrong pattern") |
| 68 | +]} |
| 69 | + |
| 70 | +1. {b Pattern Matching the Payload}: |
| 71 | + - Begins by matching the [payload] with the expected structure. |
| 72 | + - The pattern expects a structure ([PStr]) containing a single item. |
| 73 | +2. {b Destructuring the Structure Item}: |
| 74 | + - Matches the [pstr_desc] field, expecting an evaluated expression ([Pstr_eval]). |
| 75 | + - The expression should be a constant integer ([Pexp_constant] with [Pconst_integer]). |
| 76 | + - Captures the integer value as a string in [value]. |
| 77 | +3. {b Handling the Matched Value}: |
| 78 | + - Converts the [value] to an integer and returns [Ok] if successful. |
| 79 | + - If conversion fails, returns an error message. |
| 80 | +4. {b Handling Mismatched Patterns}: |
| 81 | + - If the [payload] doesn’t match the expected structure, it returns an error. |
| 82 | + |
| 83 | +While this method is powerful, it can be verbose and difficult to maintain as patterns become more complex. |
| 84 | + |
| 85 | +{1:using-ast_pattern-high-level-destructors Using {!Ppxlib.Ast_pattern} High-Level Destructors} |
| 86 | + |
| 87 | +To make AST destructuring more readable, PPXLib provides the {!Ppxlib.Ast_pattern} module, which offers high-level destructors. |
| 88 | + |
| 89 | +{2:example-1-matching-integer-payload-with-ast_pattern Example 1: Matching Integer Payload with {!Ppxlib.Ast_pattern}} |
| 90 | + |
| 91 | +{{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/1-AST/b%20-%20Destructing%20AST/destructuring_ast.ml#L37-L40} 🔗 Sample Code} |
| 92 | + |
| 93 | +Let’s destructure the same integer [1] AST using {!Ppxlib.Ast_pattern}: |
| 94 | + |
| 95 | +{[ |
| 96 | +open Ppxlib |
| 97 | + |
| 98 | +let match_int_payload = |
| 99 | + let open Ast_pattern in |
| 100 | + pstr (pstr_eval (pexp_constant (pconst_integer (string "1") none)) nil ^:: nil) |
| 101 | +]} |
| 102 | + |
| 103 | +This code achieves the same result as the previous example but in a more concise and readable way. |
| 104 | + |
| 105 | +- {b [PStr]} becomes [pstr] |
| 106 | +- {b [Pstr_eval]} becomes [pstr_eval] |
| 107 | +- {b [Pexp_constant]} becomes [pexp_constant] |
| 108 | +- {b [Pconst_integer]} becomes [pconst_integer] |
| 109 | + |
| 110 | +{2:example-2-simplifying-matching-with-eint Example 2: Simplifying Matching with [eint]} |
| 111 | + |
| 112 | +{{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/1-AST/b%20-%20Destructing%20AST/destructuring_ast.ml#L40-L49} 🔗 Sample Code} |
| 113 | + |
| 114 | +You can further simplify it: |
| 115 | + |
| 116 | +{[ |
| 117 | +let match_int_payload = |
| 118 | + let open Ast_pattern in |
| 119 | + pstr (pstr_eval (eint (int 1)) nil ^:: nil) |
| 120 | +]} |
| 121 | + |
| 122 | +Using [eint] instead of [pexp_constant] and [pconst_integer] provides better type safety. The [int] wildcard captures the integer value. |
| 123 | + |
| 124 | +{1:using-metaquot Using Metaquot} |
| 125 | + |
| 126 | +Metaquot is a syntax extension that allows you to write and destructure ASTs more intuitively. |
| 127 | + |
| 128 | +{2:example-1-matching-integer-payload-with-metaquot Example 1: Matching Integer Payload with Metaquot} |
| 129 | + |
| 130 | +{{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/1-AST/b%20-%20Destructing%20AST/destructuring_ast.ml#L51-L60} 🔗 Sample Code} |
| 131 | + |
| 132 | +Let’s destructure the same integer [1] AST with Metaquot: |
| 133 | + |
| 134 | +{[ |
| 135 | +let match_int_payload expr = |
| 136 | + match expr with |
| 137 | + | [%expr 1] -> Ok 1 |
| 138 | + | _ -> Error (Location.Error.createf ~loc:expr.pexp_loc "Wrong pattern") |
| 139 | +]} |
| 140 | + |
| 141 | +{2:example-2-matching-complex-expressions-with-metaquot-and-anti-quotations Example 2: Matching Complex Expressions with Metaquot and Anti-Quotations} |
| 142 | + |
| 143 | +{{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/1-AST/b%20-%20Destructing%20AST/destructuring_ast.ml#L79-L90} 🔗 Sample Code} |
| 144 | + |
| 145 | +For example, to match any expression of the form [1 + <int>]: |
| 146 | + |
| 147 | +{[ |
| 148 | +let match_int_payload expr = |
| 149 | + match expr with |
| 150 | + | [%expr 1 + [%e? e]] -> ( |
| 151 | + match e with |
| 152 | + | { pexp_desc = Pexp_constant (Pconst_integer (value, None)); _ } -> |
| 153 | + Ok (1 + int_of_string value) |
| 154 | + | _ -> Error (Location.Error.createf ~loc:e.pexp_loc "Invalid integer")) |
| 155 | + | _ -> Error (Location.Error.createf ~loc:expr.pexp_loc "Wrong pattern") |
| 156 | +]} |
| 157 | + |
| 158 | +Metaquot simplifies the process, making the AST patterns more readable, especially for complex structures. |
| 159 | + |
| 160 | +{1:conclusion Conclusion} |
| 161 | + |
| 162 | +In this section, we explored different methods to destructure an AST using PPXLib: |
| 163 | + |
| 164 | +- {b AST Structure Pattern Matching}: Powerful but verbose. |
| 165 | +- {b Using {!Ppxlib.Ast_pattern} High-Level Destructors}: More readable and maintainable. |
| 166 | +- {b Using Metaquot}: Intuitive and effective for both simple and complex patterns. |
| 167 | + |
| 168 | +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. |
| 169 | + |
| 170 | +{1:next-steps Next Steps} |
| 171 | +On the next section, we will learn how to write a PPX. {{!page-"example-writing-ppxs"} Read more} |
0 commit comments