diff --git a/Makefile b/Makefile
index 738caf6c7..e0f79b0d8 100644
--- a/Makefile
+++ b/Makefile
@@ -36,3 +36,7 @@ bench:
.PHONY: default install uninstall reinstall clean test doc bench
.PHONY: all-supported-ocaml-versions opam-release
+
+.PHONY: $(TARGET)
+example-%:
+ DUNE_CONFIG__GLOBAL_LOCK=disabled opam exec -- dune exec $*-example
diff --git a/doc/dune b/doc/dune
index 8942dccc3..aad6f3b77 100644
--- a/doc/dune
+++ b/doc/dune
@@ -1,2 +1,12 @@
(documentation
(package ppxlib))
+
+(rule
+ (alias doc)
+ (deps
+ (glob_files ./images/*))
+ (action
+ (progn
+ (system "echo 'hello'")
+ (system "mkdir -p %{project_root}/_doc/_html/ppxlib/assets/images")
+ (system "cp -R ./images/ %{project_root}/_doc/_html/ppxlib/assets/images/"))))
diff --git a/doc/example-ast copy.mld b/doc/example-ast copy.mld
new file mode 100644
index 000000000..cf34f20c0
--- /dev/null
+++ b/doc/example-ast copy.mld
@@ -0,0 +1,407 @@
+{0 Abstract Syntax Tree (AST)}
+
+{1 Table of Contents}
+
+- {{!section-description} Description}
+- {{!section-"preprocessing-in-ocaml"} Preprocessing in OCaml}
+- {{!section-"ast-guide"} AST Guide}
+- {{!section-"why-should-i-understand-the-ast"} Why Should I Understand the AST?}
+- {{!section-"first-look"} First Look}
+- {{!section-"structure"} Structure}
+- {{!section-"language-extensions-and-attributes"} Language Extensions and Attributes}
+- {{!section-"samples"} Samples}
+
+{1 Description}
+
+The Abstract Syntax Tree (AST) is a critical component in the OCaml compilation process. It represents the structure of the source code in a tree-like format, allowing for advanced code manipulations and transformations. This guide explores the importance of the AST, how it is used in preprocessing, and the different methods available for working with it through {b PPX} (PreProcessor eXtensions).
+
+{1:preprocessing-in-ocaml Preprocessing in OCaml}
+
+Unlike some programming languages that have built-in preprocessing features—such as C's preprocessor or Rust's macro system, OCaml lacks an integrated macro system. Instead, it relies on standalone preprocessors.
+
+The OCaml Platform officially supports a library for creating these preprocessors, which can operate at two levels:
+
+- {b Source Level}: Preprocessors work directly on the source code.
+- {b AST Level}: Preprocessors manipulate the AST, offering more powerful and flexible transformations. (Covered in this guide)
+
+{%html:
+
+
⚠️ WARNING
+
One of the key challenges with working with the Parsetree (the AST in OCaml) is that its API is not stable. For instance, in the OCaml 4.13 release, significant changes were made to the Parsetree type, which can impact the compatibility of your preprocessing tools. Read more about it in The Future of PPX.
+
+%}
+
+{1:ast-guide AST Guide}
+
+This guide will concentrate on AST-level preprocessing using {b PPX} (PreProcessor eXtensions), providing a comprehensive overview of the following topics:
+
+1. {b AST Construction}: Learning how to build and manipulate ASTs.
+2. {b AST Destructuring}: Breaking down ASTs into manageable components for advanced transformations.
+
+{1:why-should-i-understand-the-ast Why Should I Understand the AST?}
+
+OCaml's Parsetree can be confusing, verbose, and hard to understand, but it's a powerful tool that can help you write better code, understand how the compiler works, and develop your own PPXs.
+
+You don't need to be an expert on it knowing all the tree possibilities, but you should know how to read it. For this, I'm going to use the {{:https://astexplorer.net/} AST Explorer} throughout the repository to help you understand the AST.
+
+A simple example of learning more about the OCaml compiler is that types are recursive by default, while values are non-recursive.
+With the AST, we can see this clearly:
+{[
+type name = string
+let name = "John Doe"
+
+(* AST Tree *)
+(* type name = string *)
+[ Pstr_type
+ ( Recursive
+ , [ { ptype_name =
+ { txt = "name"
+ ; loc = { (* ... *) }
+ }
+ ; ptype_params = []
+ ; ptype_cstrs = []
+ ; ptype_kind = Ptype_abstract
+ ; ptype_private = Public
+ ; ptype_manifest =
+ Some
+ { ptyp_desc =
+ Ptyp_constr
+ ( { txt = Lident "string"
+ ; loc = { (* ... *) }
+ }
+ , []
+ )
+ ; ptyp_loc = { (* ... *) }
+ ; ptyp_loc_stack = __lstack
+ ; ptyp_attributes = []
+ }
+ ; ptype_attributes = []
+ ; ptype_loc = { (* ... *) }
+ }
+ ]
+ )
+; Pstr_value
+ ( Nonrecursive
+ , [ { pvb_pat =
+ { ppat_desc =
+ Ppat_var
+ { txt = "name"
+ ; loc = { (* ... *) }
+ }
+ ; ppat_loc = { (* ... *) }
+ ; ppat_loc_stack = [ ]
+ ; ppat_attributes = []
+ }
+ ; pvb_expr =
+ { pexp_desc =
+ Pexp_constant
+ (Pconst_string
+ ( "John Doe"
+ , (* loc ... *)
+ , None
+ ))
+ ; pexp_loc = { (* ... *) }
+ ; pexp_loc_stack = [ (* ... *) ]
+ ; pexp_attributes = []
+ }
+ ; pvb_attributes = []
+ ; pvb_loc = { (* ... *) }
+ }
+ ]
+ )
+]
+]}
+
+{1:first-look First Look}
+
+By comparing code snippets with their AST representations, you'll better understand how OCaml interprets your code, which is essential for working with PPXs or delving into the compiler's internals. The {{:https://astexplorer.net/} AST Explorer} tool will help make these concepts clearer and more accessible.
+
+Let's take a quick look at the JSON AST representation of a simple OCaml expression:
+
+{[
+(* Foo.ml *)
+let name = "john doe"
+
+(* AST Tree *)
+(* let name = "john doe" *)
+[ Pstr_type
+ ( Recursive
+ , [ { ptype_name = "name"
+ ; ptype_params = []
+ ; ptype_cstrs = []
+ ; ptype_kind = Ptype_abstract
+ ; ptype_private = Public
+ ; ptype_manifest = Some (Ptyp_constr ( Lident "string", []))
+ ; ptype_attributes = []
+ ; ptype_loc = { (* ... *) }
+ }
+ ]
+ )
+; Pstr_value
+ ( Nonrecursive
+ , [ { pvb_pat = Ppat_var "name"
+ ; pvb_expr = Pexp_constant (Pconst_string ( "John Doe", (* loc ... *) , None))
+ ; pvb_attributes = []
+ ; pvb_loc = { (* ... *) }
+ }
+ ]
+ )
+]
+]}
+
+As you can see, it's a little bit verbose. Don't be scared; we are going to learn how to read it, which is the most important thing.
+
+{1 Structure}
+
+{[
+(* Foo.ml *)
+let name = "john doe"
+
+(* AST Tree *)
+(* let name = "john doe" *)
+(* This entire list is a structure *)
+[ Pstr_value
+ ( Nonrecursive
+ , [ { pvb_pat = Ppat_var "name"
+ ; pvb_expr = Pexp_constant (Pconst_string ( "john doe", (* loc ... *) , None))
+ ; pvb_attributes = []
+ ; pvb_loc = { (* ... *) }
+ }
+ ]
+ )
+]
+]}
+
+In OCaml, a {b module} serves as a container for grouping related definitions, such as types, values, functions, and even other modules, into a single cohesive unit. This modular approach helps organize your code, making it more manageable, reusable, and easier to understand.
+
+A {b structure} refers to the content within a module. It is composed of various declarations, known as {b structure items}, which include:
+
+- {b Type definitions} (e.g., [type t = ...])
+- {b let bindings} (e.g., [let x = 1])
+- {b Function definitions}
+- {b Exception declarations}
+- {b Other nested modules}
+
+The structure represents the body of the module, where all these items are defined and implemented. Since each {.ml} file is implicitly a module, the entire content of a file can be viewed as the structure of that module.
+
+{%html:
+
+
💡 Tip
+
Every module in OCaml creates a new structure, and nested modules create nested structures.
+
+%}
+
+Consider the following example:
+
+{[
+(* Bar.ml *)
+let name = "john doe"
+
+module GameEnum = struct
+ type t = Rock | Paper | Scissors
+
+ let to_string = function
+ | Rock -> "Rock"
+ | Paper -> "Paper"
+ | Scissors -> "Scissors"
+
+ let from_string = function
+ | "Rock" -> Rock
+ | "Paper" -> Paper
+ | "Scissors" -> Scissors
+ | _ -> failwith "Invalid string"
+end
+]}
+
+{[
+[
+ (* This is a structure item *)
+ Pstr_value
+ ( Nonrecursive
+ , [ { pvb_pat = Ppat_var "name"
+ ; pvb_expr = Pexp_constant (Pconst_string ( "john doe", __loc, None))
+ ; pvb_attributes = []
+ ; pvb_loc = __loc
+ }
+ ]
+ )
+
+ (* This is a structure item *)
+ ; Pstr_module
+ { pmb_name = Some "GameEnum"
+ (* This is a structure *)
+ ; pmb_expr =
+ Pmod_structure
+ [ (* ... Structure items ... *) ]
+ ; pmb_attributes = []
+ ; pmb_loc = { (* ... *) }
+ }
+]
+]}
+
+As you can see, [Bar.ml] and [GameEnum] are modules, and their content is a {b structure} that contain a list of {b structure items}.
+
+{%html:
+
+
✏️ Note
+
A structure item can either represent a top-level expression, a type definition, a let definition, etc.
+
+%}
+
+I'm not going to be able to cover all structure items, but you can find more about it in the {{:https://ocaml.org/learn/tutorials/modules.html} OCaml documentation}. I strongly advise you to take a look at the {{:./ast_explorer.ml} AST Explorer} file and play with it; it will help you a lot. The [ppxlib-pp-ast] command is an official ppxlib tool that allows you to see the AST of a given OCaml file/string.
+
+{1 Language Extensions and Attributes}
+
+As the AST represents the structure of the source code in a tree-like format, it also represents the Extension nodes and Attributes. It is mostly from the extension and attributes that the PPXs are built, so it's important to understand that they are part of the AST and have their own structure.
+
+- {4 Extension nodes} are generic placeholders in the syntax tree. They are rejected by the type-checker and are intended to be “expanded” by external tools such as -ppx rewriters. On AST, it is represented as [string Ast_414.Asttypes.loc * payload].
+
+ So, as extension nodes are placeholders for a code to be added, adding a new extension node with no extender declared should break the compilation. For example, in the code [let name = [%name "John Doe"]]. See a demo {{:https://sketch.sh/s/6DxhTCXYpOkI0G8k9keD0d/} here}.
+
+ There are 2 forms of extension nodes:
+
+ - {b For “algebraic” categories}: [[%name "John Doe"]]
+ - {b For structures and signatures}: [[%%name "John Doe"]]
+
+{%html:
+
+
✏️ Note
+
In the code let name = [%name "John Doe"]
, [%name "John Doe"]
is the extension node, where name
is the extension name string Ast_414.Asttypes.loc
and "John Doe"
is the payload
. For the entire item like let name = "John Doe"
, you must use %%
: [%%name "John Doe"]
.
+
+
+%}
+
+ Don't worry much about how to create a new extension node; we'll cover it in the {{:../2%20-%20Writing%20PPXs/README.md} Writing PPXs section}.
+
+- {4 Attributes} are “decorations” of the syntax tree, which are mostly ignored by the type-checker but can be used by external tools. Decorators must be attached to a specific node in the syntax tree, otherwise it will break the compilation. (Check it breaking on this running [ppxlib-pp-ast --exp "[@foo]"]).
+
+ As attributes are just “decorations”, you can add a new attribute without breaking the compilation. For example, in the code, [let name = "John Doe" [@print]]. See a demo {{:https://sketch.sh/s/6DxhTCXYpOkI0G8k9keD0d/} here}.
+
+ There are 3 forms of attributes:
+
+ - {b Attached to on “algebraic” categories}: [[@name]]
+ - {b Attached to “blocks”}: [[@@name]]
+ - {b Stand-alone of signatures or structures modules}: [[@@@name]]
+
+ {5 Note}
+ In the code [let name = "John Doe" [@print expr]], [@print expr] is the attribute of the {b "John Doe"} node, where {b print} is the attribute name ([string Ast_414.Asttypes.loc]) and {b expr} is the [payload]. To be an attribute of the entire item [let name = "John Doe"], you must use [@@]: [@@print]. If it is an stand-alone attribute of a module, you must use [@@@]: [@@@print].
+
+ Don't worry much about creating a new attributes node; we'll cover it in the {{:../2%20-%20Writing%20PPXs/README.md} Writing PPXs section}.
+
+I know that it can be a lot, but don't worry; we are going step by step, and you are going to understand it.
+
+{1 Samples}
+To help you understand a little bit more about the AST, let's show it with some highlighted examples:
+
+{%html:
+
+
+
+ Playground |
+ Code |
+ AST |
+
+
+ Link |
+
+ 1 |let name = "john doe"
+ |
+
+ 1 |[
+2 | {
+3 | Pstr_value: [ (* ... *) ];
+4 | }
+5 |]
+ |
+
+
+ Link |
+
+ 1 | module GameEnum = struct
+2 | type t = Rock | Paper | Scissors
+3 | let to_string = function
+4 | | Rock -> "Rock"
+5 | | Paper -> "Paper"
+6 | | Scissors -> "Scissors"
+7 | let from_string = function
+8 | | "Rock" -> Rock
+9 | | "Paper" -> Paper
+10| | "Scissors" -> Scissors
+11| | _ -> failwith "Invalid string"
+12| end
+ |
+
+ 1 |[
+2 | Pstr_module
+3 | { pmb_name = {
+4 | txt = Some "GameEnum"
+5 | loc = { (* ... *) }
+6 | };
+7 | pmb_expr = {
+8 | pmod_desc = Pmod_structure [ (* ... *) ]
+9 | pmod_loc = { (* ... *) }
+10| pmod_loc_stack = []
+11| pmod_attributes = []
+12| };
+13| pmb_attributes = [];
+14| pmb_loc = { (* ... *) }
+15| }
+16|]
+ |
+
+
+ Link |
+ |
+ |
+
+
+ Link |
+ |
+ |
+
+
+ Link |
+ |
+ |
+
+
+ Link |
+ |
+ |
+
+
+ Link |
+ |
+ |
+
+
+ Link |
+ |
+ |
+
+
+ Link |
+ |
+ |
+
+
+ Link |
+ |
+ |
+
+
+ Link |
+ |
+ |
+
+
+%}
+
+{1 Next Steps}
+On the next section, we will learn how to build an AST. {{:./a%20-%20Building%20AST/README.md} Read more}
diff --git a/doc/example-ast-building.mld b/doc/example-ast-building.mld
new file mode 100644
index 000000000..bc4d3c918
--- /dev/null
+++ b/doc/example-ast-building.mld
@@ -0,0 +1,173 @@
+{0 Building AST}
+
+{1 Table of Contents}
+
+- {{!section-description} Description}
+- {{!section-"building-asts-with-pure-ocaml"} Building ASTs with Pure OCaml}
+{ul {- {{!section-"example-building-a-simple-integer-ast-manually"} Example: Building a Simple Integer AST Manually}}}
+
+- {{!section-"building-asts-with-ast_builder"} Building ASTs with `AST_builder`}
+{ul {- {{!section-"example-1-using-pexp_constant-for-integer-ast"} Example 1: Using `pexp_constant` for Integer AST}}}
+{ul {- {{!section-"example-2-using-eint-for-simplified-integer-ast"} Example 2: Using `eint` for Simplified Integer AST}}}
+
+- {{!section-"using-metaquot-for-ast-construction"} Using Metaquot for AST Construction}
+{ul {- {{!section-"example-building-an-integer-ast-with-metaquot"} Example: Building an Integer AST with Metaquot}}}
+
+- {{!section-"using-anti-quotations-in-metaquot"} Using Anti-Quotations in Metaquot}
+{ul {- {{!section-"example-inserting-dynamic-expressions-with-anti-quotations"} Example: Inserting Dynamic Expressions with Anti-Quotations}}}
+
+- {{!section-"building-complex-expressions"} Building Complex Expressions}
+{ul {- {{!section-"example-1-constructing-a-let-expression-with-ast_builder"} Example 1: Constructing a Let Expression with `AST_builder`}}}
+{ul {- {{!section-"example-2-constructing-a-let-expression-with-metaquot"} Example 2: Constructing a Let Expression with Metaquot}}}
+
+- {{!section-conclusion} Conclusion}
+
+{1:description Description}
+
+Building an AST (Abstract Syntax Tree) is a fundamental part of creating a PPX in OCaml. You'll need to construct an AST to represent the code you want to generate or transform.
+
+For example, if you want to generate the following code:
+
+{[
+let zero = [%int 0]
+]}
+
+and replace the extension point `[%int 0]` with `0` to produce `let zero = 0`, you’ll need to build an AST that represents this transformation.
+
+There are several methods to build an AST. We’ll discuss three approaches:
+
+- {b Building ASTs with Pure OCaml}
+- {b Building ASTs with `AST_builder`}
+- {b Using Metaquot for AST Construction}
+
+{1:building-asts-with-pure-ocaml Building ASTs with Low-Level Builders}
+
+The most fundamental way to build an AST is to manually construct it using Low-Level Builders data structures.
+
+{2:example-building-a-simple-integer-ast-manually Example: Building a Simple Integer AST Manually}
+
+{{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/1-AST/a%20-%20Building%20AST/building_ast.ml#L5-L16} 🔗 Sample Code}
+
+{[
+let zero ~loc : Ppxlib_ast.Ast.expression =
+ {
+ pexp_desc = Pexp_constant (Pconst_integer ("0", None));
+ pexp_loc = loc;
+ pexp_loc_stack = [];
+ pexp_attributes = [];
+ }
+]}
+
+While this method provides full control over the AST, it is verbose and less maintainable.
+
+{1:building-asts-with-ast_builder Building ASTs with `AST_builder`}
+
+PPXLib provides the `AST_builder` module, which simplifies the process of building ASTs by providing helper functions.
+
+{2:example-1-using-pexp_constant-for-integer-ast Example 1: Using `pexp_constant` for Integer AST}
+
+{{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/1-AST/a%20-%20Building%20AST/building_ast.ml#L18-L24} 🔗 Sample Code}
+
+{[
+let one ~loc =
+ Ast_builder.Default.pexp_constant ~loc (Parsetree.Pconst_integer ("1", None))
+]}
+
+This method is more readable and concise compared to the pure OCaml approach.
+
+{2:example-2-using-eint-for-simplified-integer-ast Example 2: Using `eint` for Simplified Integer AST}
+
+{{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/1-AST/a%20-%20Building%20AST/building_ast.ml#L26-L31} 🔗 Sample Code}
+
+{[
+let two ~loc = Ast_builder.Default.eint ~loc 2
+]}
+
+{b Tip:} `eint` is an abbreviation for expression (`e`) integer (`int`).
+
+{1:using-metaquot-for-ast-construction Using Metaquot for AST Construction}
+
+Metaquot is a syntax extension that allows you to write ASTs in a more natural and readable way.
+
+{2:example-building-an-integer-ast-with-metaquot Example: Building an Integer AST with Metaquot}
+
+{{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/1-AST/a%20-%20Building%20AST/building_ast.ml#L33-L38} 🔗 Sample Code}
+
+{[
+let three ~loc = [%expr 3]
+]}
+
+{b Tip:} Metaquot is highly readable and intuitive but is static. For dynamic values, use Anti-Quotations.
+
+{2:using-anti-quotations-in-metaquot Using Anti-Quotations in Metaquot}
+
+Anti-Quotations allow you to insert dynamic expressions into your Metaquot ASTs.
+
+{3:example-inserting-dynamic-expressions-with-anti-quotations Example: Inserting Dynamic Expressions with Anti-Quotations}
+
+{{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/1-AST/a%20-%20Building%20AST/building_ast.ml#L72-L77} 🔗 Sample Code}
+
+{[
+let anti_quotation_expr expr = [%expr 1 + [%e expr]]
+]}
+
+For example, to insert the AST for `1`:
+
+{[
+let _ =
+ print_endline
+ ("\nLet expression with metaquot and anti-quotation: "
+ ^ Astlib.Pprintast.string_of_expression (anti_quotation_expr (one ~loc)))
+]}
+
+{1:building-complex-expressions Building Complex Expressions}
+
+Beyond simple expressions, you may need to build more complex ASTs, such as `let` expressions.
+
+{2:example-1-constructing-a-let-expression-with-ast_builder Example 1: Constructing a Let Expression with `AST_builder`}
+
+{{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/1-AST/a%20-%20Building%20AST/building_ast.ml#L40-L60} 🔗 Sample Code}
+
+{[
+let let_expression =
+ let expression =
+ Ast_builder.Default.pexp_constant ~loc:Location.none
+ (Pconst_integer ("3", None))
+ in
+ let pattern =
+ Ast_builder.Default.ppat_var ~loc:Location.none
+ (Ast_builder.Default.Located.mk ~loc:Location.none "foo")
+ in
+ let let_binding =
+ Ast_builder.Default.value_binding ~loc:Location.none ~pat:pattern
+ ~expr:expression
+ in
+ Ast_builder.Default.pexp_let ~loc:Location.none Nonrecursive [ let_binding ]
+ (Ast_builder.Default.eunit ~loc:Location.none)
+]}
+
+{2:example-2-constructing-a-let-expression-with-metaquot Example 2: Constructing a Let Expression with Metaquot}
+
+{{:https://github.com/ocaml-ppx/ppxlib/tree/main/examples/1-AST/a%20-%20Building%20AST/building_ast.ml#L62-L70} 🔗 Sample Code}
+
+{[
+let let_expression =
+ [%expr
+ let foo = 3 in
+ ()]
+]}
+
+This approach is shorter and easier to understand.
+
+{1:conclusion Conclusion}
+
+In this section, we explored three methods for building ASTs:
+
+- {b Pure OCaml}: The most basic but verbose approach.
+- {b Using `AST_builder`}: A more readable and maintainable option.
+- {b Using Metaquot}: The most intuitive method, especially when combined with Anti-Quotations for dynamic values.
+
+Each method has its strengths, so choose the one that best fits your needs. Understanding all three will give you greater flexibility in creating effective and maintainable PPXs.
+
+{2 Next Steps}
+On the next section, we will learn how to destructure an AST. {{:../b%20-%20Destructing%20AST/README.md} Read more}
diff --git a/doc/example-ast-destructing.mld b/doc/example-ast-destructing.mld
new file mode 100644
index 000000000..8293aad36
--- /dev/null
+++ b/doc/example-ast-destructing.mld
@@ -0,0 +1,171 @@
+{0 Destructuring AST}
+
+{1:table-of-contents Table of Contents}
+
+- {{!section-description} Description}
+- {{!section-"ast-structure-pattern-matching"} AST Structure Pattern Matching}
+{ul {- {{!section-"example-matching-integer-payload-manually"} Example: Matching Integer Payload Manually}}}
+
+- {{!section-"using-ast_pattern-high-level-destructors"} Using `Ast_pattern` High-Level Destructors}
+{ul {- {{!section-"example-1-matching-integer-payload-with-ast_pattern"} Example 1: Matching Integer Payload with `Ast_pattern`}}}
+{ul {- {{!section-"example-2-simplifying-matching-with-eint"} Example 2: Simplifying Matching with `eint`}}}
+
+- {{!section-"using-metaquot"} Using Metaquot}
+{ul {- {{!section-"example-1-matching-integer-payload-with-metaquot"} Example 1: Matching Integer Payload with Metaquot}}}
+{ul {- {{!section-"example-2-matching-complex-expressions-with-metaquot-and-anti-quotations"} Example 2: Matching Complex Expressions with Metaquot and Anti-Quotations}}}
+
+- {{!section-"conclusion"} Conclusion}
+
+{1:description Description}
+
+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:
+
+- {b AST Structure Pattern Matching}
+- {b Using `Ast_pattern` High-Level Destructors}
+- {b Using Metaquot}
+
+{1:ast-structure-pattern-matching AST Structure Pattern Matching}
+
+The most fundamental method for destructuring an AST in PPXLib is by directly matching on the AST’s structure.
+
+{2:example-matching-integer-payload-manually Example: Matching Integer Payload Manually}
+
+{[:link: Sample Code](./destructuring_ast.ml#L11-L26)}
+
+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")
+]}
+
+1. {b Pattern Matching the Payload}:
+ - Begins by matching the `payload` with the expected structure.
+ - The pattern expects a structure (`PStr`) containing a single item.
+2. {b Destructuring the Structure Item}:
+ - Matches the `pstr_desc` field, expecting an evaluated expression (`Pstr_eval`).
+ - The expression should be a constant integer (`Pexp_constant` with `Pconst_integer`).
+ - Captures the integer value as a string in `value`.
+3. {b Handling the Matched Value}:
+ - Converts the `value` to an integer and returns `Ok` if successful.
+ - If conversion fails, returns an error message.
+4. {b Handling Mismatched Patterns}:
+ - If the `payload` doesn’t match the expected structure, it returns an error.
+
+While this method is powerful, it can be verbose and difficult to maintain as patterns become more complex.
+
+{1:using-ast_pattern-high-level-destructors Using `Ast_pattern` High-Level Destructors}
+
+To make AST destructuring more readable, PPXLib provides the `Ast_pattern` module, which offers high-level destructors.
+
+{2:example-1-matching-integer-payload-with-ast_pattern Example 1: Matching Integer Payload with `Ast_pattern`}
+
+{[:link: Sample Code](./destructuring_ast.ml#L37-L40)}
+
+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.
+
+- {b `PStr`} becomes `pstr`
+- {b `Pstr_eval`} becomes `pstr_eval`
+- {b `Pexp_constant`} becomes `pexp_constant`
+- {b `Pconst_integer`} becomes `pconst_integer`
+
+{2:example-2-simplifying-matching-with-eint Example 2: Simplifying Matching with `eint`}
+
+{[:link: Sample Code](./destructuring_ast.ml#L40-L49)}
+
+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.
+
+{1:using-metaquot Using Metaquot}
+
+Metaquot is a syntax extension that allows you to write and destructure ASTs more intuitively.
+
+{2:example-1-matching-integer-payload-with-metaquot Example 1: Matching Integer Payload with Metaquot}
+
+{[:link: Sample Code](./destructuring_ast.ml#L51-L60)}
+
+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")
+]}
+
+{2:example-2-matching-complex-expressions-with-metaquot-and-anti-quotations Example 2: Matching Complex Expressions with Metaquot and Anti-Quotations}
+
+{[:link: Sample Code](./destructuring_ast.ml#L79-L90)}
+
+For example, to match any expression of the form `1 + `:
+
+{[
+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.
+
+{1:conclusion Conclusion}
+
+In this section, we explored different methods to destructure an AST using PPXLib:
+
+- {b AST Structure Pattern Matching}: Powerful but verbose.
+- {b Using `Ast_pattern` High-Level Destructors}: More readable and maintainable.
+- {b 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.
+
+{1:next-steps Next Steps}
+On the next section, we will learn how to write a PPX. {{:../../2%20-%20Writing%20PPXs/README.md} Read more}
\ No newline at end of file
diff --git a/doc/example-ast.mld b/doc/example-ast.mld
new file mode 100644
index 000000000..e6f08fa02
--- /dev/null
+++ b/doc/example-ast.mld
@@ -0,0 +1,312 @@
+{0 Abstract Syntax Tree (AST)}
+
+{1 Table of Contents}
+
+- {{!section-description} Description}
+- {{!section-"preprocessing-in-ocaml"} Preprocessing in OCaml}
+- {{!section-"ast-guide"} AST Guide}
+- {{!section-"why-should-i-understand-the-ast"} Why Should I Understand the AST?}
+- {{!section-"first-look"} First Look}
+- {{!section-"structure"} Structure}
+- {{!section-"language-extensions-and-attributes"} Language Extensions and Attributes}
+- {{!section-"samples"} Samples}
+
+{1 Description}
+
+The Abstract Syntax Tree (AST) is a critical component in the OCaml compilation process. It represents the structure of the source code in a tree-like format, allowing for advanced code manipulations and transformations. This guide explores the importance of the AST, how it is used in preprocessing, and the different methods available for working with it through {b PPX} (PreProcessor eXtensions).
+
+{1:preprocessing-in-ocaml Preprocessing in OCaml}
+
+Unlike some programming languages that have built-in preprocessing features—such as C's preprocessor or Rust's macro system, OCaml lacks an integrated macro system. Instead, it relies on standalone preprocessors.
+
+The OCaml Platform officially supports a library for creating these preprocessors, which can operate at two levels:
+
+- {b Source Level}: Preprocessors work directly on the source code.
+- {b AST Level}: Preprocessors manipulate the AST, offering more powerful and flexible transformations. (Covered in this guide)
+
+{%html:
+
+
⚠️ WARNING
+
One of the key challenges with working with the Parsetree (the AST in OCaml) is that its API is not stable. For instance, in the OCaml 4.13 release, significant changes were made to the Parsetree type, which can impact the compatibility of your preprocessing tools. Read more about it in The Future of PPX.
+
+%}
+
+{1:ast-guide AST Guide}
+
+This guide will concentrate on AST-level preprocessing using {b PPX} (PreProcessor eXtensions), providing a comprehensive overview of the following topics:
+
+1. {b AST Construction}: Learning how to build and manipulate ASTs.
+2. {b AST Destructuring}: Breaking down ASTs into manageable components for advanced transformations.
+
+{1:why-should-i-understand-the-ast Why Should I Understand the AST?}
+
+OCaml's Parsetree can be confusing, verbose, and hard to understand, but it's a powerful tool that can help you write better code, understand how the compiler works, and develop your own PPXs.
+
+You don't need to be an expert on it knowing all the tree possibilities, but you should know how to read it. For this, I'm going to use the {{:https://astexplorer.net/} AST Explorer} throughout the repository to help you understand the AST.
+
+A simple example of learning more about the OCaml compiler is that types are recursive by default, while values are non-recursive.
+With the AST, we can see this clearly:
+{[
+type name = string
+let name = "John Doe"
+
+(* AST Tree *)
+(* type name = string *)
+[ Pstr_type
+ ( Recursive
+ , [ { ptype_name =
+ { txt = "name"
+ ; loc = { (* ... *) }
+ }
+ ; ptype_params = []
+ ; ptype_cstrs = []
+ ; ptype_kind = Ptype_abstract
+ ; ptype_private = Public
+ ; ptype_manifest =
+ Some
+ { ptyp_desc =
+ Ptyp_constr
+ ( { txt = Lident "string"
+ ; loc = { (* ... *) }
+ }
+ , []
+ )
+ ; ptyp_loc = { (* ... *) }
+ ; ptyp_loc_stack = __lstack
+ ; ptyp_attributes = []
+ }
+ ; ptype_attributes = []
+ ; ptype_loc = { (* ... *) }
+ }
+ ]
+ )
+; Pstr_value
+ ( Nonrecursive
+ , [ { pvb_pat =
+ { ppat_desc =
+ Ppat_var
+ { txt = "name"
+ ; loc = { (* ... *) }
+ }
+ ; ppat_loc = { (* ... *) }
+ ; ppat_loc_stack = [ ]
+ ; ppat_attributes = []
+ }
+ ; pvb_expr =
+ { pexp_desc =
+ Pexp_constant
+ (Pconst_string
+ ( "John Doe"
+ , (* loc ... *)
+ , None
+ ))
+ ; pexp_loc = { (* ... *) }
+ ; pexp_loc_stack = [ (* ... *) ]
+ ; pexp_attributes = []
+ }
+ ; pvb_attributes = []
+ ; pvb_loc = { (* ... *) }
+ }
+ ]
+ )
+]
+]}
+
+{1:first-look First Look}
+
+By comparing code snippets with their AST representations, you'll better understand how OCaml interprets your code, which is essential for working with PPXs or delving into the compiler's internals. The {{:https://astexplorer.net/} AST Explorer} tool will help make these concepts clearer and more accessible.
+
+Let's take a quick look at the JSON AST representation of a simple OCaml expression:
+
+{[
+(* Foo.ml *)
+let name = "john doe"
+
+(* AST Tree *)
+(* let name = "john doe" *)
+[ Pstr_type
+ ( Recursive
+ , [ { ptype_name = "name"
+ ; ptype_params = []
+ ; ptype_cstrs = []
+ ; ptype_kind = Ptype_abstract
+ ; ptype_private = Public
+ ; ptype_manifest = Some (Ptyp_constr ( Lident "string", []))
+ ; ptype_attributes = []
+ ; ptype_loc = { (* ... *) }
+ }
+ ]
+ )
+; Pstr_value
+ ( Nonrecursive
+ , [ { pvb_pat = Ppat_var "name"
+ ; pvb_expr = Pexp_constant (Pconst_string ( "John Doe", (* loc ... *) , None))
+ ; pvb_attributes = []
+ ; pvb_loc = { (* ... *) }
+ }
+ ]
+ )
+]
+]}
+
+As you can see, it's a little bit verbose. Don't be scared; we are going to learn how to read it, which is the most important thing.
+
+{1 Structure}
+
+{[
+(* Foo.ml *)
+let name = "john doe"
+
+(* AST Tree *)
+(* let name = "john doe" *)
+(* This entire list is a structure *)
+[ Pstr_value
+ ( Nonrecursive
+ , [ { pvb_pat = Ppat_var "name"
+ ; pvb_expr = Pexp_constant (Pconst_string ( "john doe", (* loc ... *) , None))
+ ; pvb_attributes = []
+ ; pvb_loc = { (* ... *) }
+ }
+ ]
+ )
+]
+]}
+
+In OCaml, a {b module} serves as a container for grouping related definitions, such as types, values, functions, and even other modules, into a single cohesive unit. This modular approach helps organize your code, making it more manageable, reusable, and easier to understand.
+
+A {b structure} refers to the content within a module. It is composed of various declarations, known as {b structure items}, which include:
+
+- {b Type definitions} (e.g., [type t = ...])
+- {b let bindings} (e.g., [let x = 1])
+- {b Function definitions}
+- {b Exception declarations}
+- {b Other nested modules}
+
+The structure represents the body of the module, where all these items are defined and implemented. Since each {.ml} file is implicitly a module, the entire content of a file can be viewed as the structure of that module.
+
+{%html:
+
+
💡 Tip
+
Every module in OCaml creates a new structure, and nested modules create nested structures.
+
+%}
+
+Consider the following example:
+
+{[
+(* Bar.ml *)
+let name = "john doe"
+
+module GameEnum = struct
+ type t = Rock | Paper | Scissors
+
+ let to_string = function
+ | Rock -> "Rock"
+ | Paper -> "Paper"
+ | Scissors -> "Scissors"
+
+ let from_string = function
+ | "Rock" -> Rock
+ | "Paper" -> Paper
+ | "Scissors" -> Scissors
+ | _ -> failwith "Invalid string"
+end
+]}
+
+{[
+[
+ (* This is a structure item *)
+ Pstr_value
+ ( Nonrecursive
+ , [ { pvb_pat = Ppat_var "name"
+ ; pvb_expr = Pexp_constant (Pconst_string ( "john doe", __loc, None))
+ ; pvb_attributes = []
+ ; pvb_loc = __loc
+ }
+ ]
+ )
+
+ (* This is a structure item *)
+ ; Pstr_module
+ { pmb_name = Some "GameEnum"
+ (* This is a structure *)
+ ; pmb_expr =
+ Pmod_structure
+ [ (* ... Structure items ... *) ]
+ ; pmb_attributes = []
+ ; pmb_loc = { (* ... *) }
+ }
+]
+]}
+
+As you can see, [Bar.ml] and [GameEnum] are modules, and their content is a {b structure} that contain a list of {b structure items}.
+
+{%html:
+
+
✏️ Note
+
A structure item can either represent a top-level expression, a type definition, a let definition, etc.
+
+%}
+
+I'm not going to be able to cover all structure items, but you can find more about it in the {{:https://ocaml.org/learn/tutorials/modules.html} OCaml documentation}. I strongly advise you to take a look at the {{:./ast_explorer.ml} AST Explorer} file and play with it; it will help you a lot. The [ppxlib-pp-ast] command is an official ppxlib tool that allows you to see the AST of a given OCaml file/string.
+
+{1 Language Extensions and Attributes}
+
+As the AST represents the structure of the source code in a tree-like format, it also represents the Extension nodes and Attributes. It is mostly from the extension and attributes that the PPXs are built, so it's important to understand that they are part of the AST and have their own structure.
+
+- {4 Extension nodes} are generic placeholders in the syntax tree. They are rejected by the type-checker and are intended to be “expanded” by external tools such as -ppx rewriters. On AST, it is represented as [string Ast_414.Asttypes.loc * payload].
+
+ So, as extension nodes are placeholders for a code to be added, adding a new extension node with no extender declared should break the compilation. For example, in the code [let name = [%name "John Doe"]]. See a demo {{:https://sketch.sh/s/6DxhTCXYpOkI0G8k9keD0d/} here}.
+
+ There are 2 forms of extension nodes:
+
+ - {b For “algebraic” categories}: [[%name "John Doe"]]
+ - {b For structures and signatures}: [[%%name "John Doe"]]
+
+{%html:
+
+
✏️ Note
+
In the code let name = [%name "John Doe"]
, [%name "John Doe"]
is the extension node, where name
is the extension name string Ast_414.Asttypes.loc
and "John Doe"
is the payload
. For the entire item like let name = "John Doe"
, you must use %%
: [%%name "John Doe"]
.
+
+
+%}
+
+ Don't worry much about how to create a new extension node; we'll cover it in the {{:../2%20-%20Writing%20PPXs/README.md} Writing PPXs section}.
+
+- {4 Attributes} are “decorations” of the syntax tree, which are mostly ignored by the type-checker but can be used by external tools. Decorators must be attached to a specific node in the syntax tree, otherwise it will break the compilation. (Check it breaking on this running [ppxlib-pp-ast --exp "[@foo]"]).
+
+ As attributes are just “decorations”, you can add a new attribute without breaking the compilation. For example, in the code, [let name = "John Doe" [@print]]. See a demo {{:https://sketch.sh/s/6DxhTCXYpOkI0G8k9keD0d/} here}.
+
+ There are 3 forms of attributes:
+
+ - {b Attached to on “algebraic” categories}: [[@name]]
+ - {b Attached to “blocks”}: [[@@name]]
+ - {b Stand-alone of signatures or structures modules}: [[@@@name]]
+
+ {5 Note}
+ In the code [let name = "John Doe" [@print expr]], [@print expr] is the attribute of the {b "John Doe"} node, where {b print} is the attribute name ([string Ast_414.Asttypes.loc]) and {b expr} is the [payload]. To be an attribute of the entire item [let name = "John Doe"], you must use [@@]: [@@print]. If it is an stand-alone attribute of a module, you must use [@@@]: [@@@print].
+
+ Don't worry much about creating a new attributes node; we'll cover it in the {{:../2%20-%20Writing%20PPXs/README.md} Writing PPXs section}.
+
+I know that it can be a lot, but don't worry; we are going step by step, and you are going to understand it.
+
+{1 Samples}
+To help you understand a little bit more about the AST, let's show it with some highlighted examples:
+
+{t
+ | Playground | Code | AST |
+ | :---: | :---: | :---: |
+ | {{:https://astexplorer.net/#/gist/d479d32127d6fcb418622ee84b9aa3b2/1d56a1d5b20fc0a55d5ae9d309226dce58f93d2c} Link} | {%html:%} | {%html:%} |
+ | {{:https://astexplorer.net/#/gist/d479d32127d6fcb418622ee84b9aa3b2/1d56a1d5b20fc0a55d5ae9d309226dce58f93d2c} Link} | {%html:%} | {%html:%} |
+ | {{:https://astexplorer.net/#/gist/d479d32127d6fcb418622ee84b9aa3b2/4002362a8c42e1c4f28790f54682a9cb4fc07a85} Link} | {%html:%} | {%html:%} |
+ | {{:https://astexplorer.net/#/gist/d479d32127d6fcb418622ee84b9aa3b2/4002362a8c42e1c4f28790f54682a9cb4fc07a85} Link} | {%html:%} | {%html:%} |
+ | {{:https://astexplorer.net/#/gist/d479d32127d6fcb418622ee84b9aa3b2/4002362a8c42e1c4f28790f54682a9cb4fc07a85} Link} | {%html:%} | {%html:%} |
+ | {{:https://astexplorer.net/#/gist/d479d32127d6fcb418622ee84b9aa3b2/b4492b3d2d1b34029d367ff278f5bcda0496c0d2} Link} | {%html:%} | {%html:%} |
+ | {{:https://astexplorer.net/#/gist/d479d32127d6fcb418622ee84b9aa3b2/b4492b3d2d1b34029d367ff278f5bcda0496c0d2} Link} | {%html:%} | {%html:%} |
+ | {{:https://astexplorer.net/#/gist/d479d32127d6fcb418622ee84b9aa3b2/27d0a140f268bae1a32c8882d55c0b26c7e03fe9} Link} | {%html:%} | {%html:%} |
+ | {{:https://astexplorer.net/#/gist/d479d32127d6fcb418622ee84b9aa3b2/27d0a140f268bae1a32c8882d55c0b26c7e03fe9} Link} | {%html:%} | {%html:%} |
+ | {{:https://astexplorer.net/#/gist/d479d32127d6fcb418622ee84b9aa3b2/27d0a140f268bae1a32c8882d55c0b26c7e03fe9} Link} | {%html:%} | {%html:%} |
+}
+
+{1 Next Steps}
+On the next section, we will learn how to build an AST. {{:./a%20-%20Building%20AST/README.md} Read more}
diff --git a/doc/examples.mld b/doc/examples.mld
index ca72780ea..34769c106 100644
--- a/doc/examples.mld
+++ b/doc/examples.mld
@@ -1,167 +1,22 @@
-{%html: %}{{!"good-practices"}< Good practices}{%html:
%}{%html:
%}
+{0 PPX by examples}
-{0 Examples}
+This part of the documentation was made to help on understanding what are and how to write PPXs in OCaml with PPXLib.
-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.
+{1 Content}
-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.
+- {{!page-"example-ast"} AST}
+{ul {- {{!page-"example-ast-building"} Building an AST}}}
+{ul {- {{!page-"example-ast-destructing"} Destructing an AST}}}
-{1 [ppx_deriving_accesors]}
+- {{!page-"example-writing-ppxs"} Writing a PPX}
+{ul {- {{!page-"example-writing-ppxs-context-free"} Context-free transformations}}}
+{ul {- {{!page-"example-writing-ppxs-global"} Global transformations}}}
-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}.
+- {{!page-"example-testing-ppxs-index"} Testing a PPX}
-This deriver will generate accessors for record fields, from the record type
-definition.
+{1 References}
-For example, this code:
-
-{@ocaml[
-type t =
- { a : string
- ; b : int
- }
- [@@deriving accessors]
-]}
-
-will generate the following, appended after the type definition:
-
-{@ocaml[
-let a x = x.a
-let b x = x.b
-]}
-
-The entire code is:
-
-{@ocaml[
-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
-]}
-
-{1 [ppx_get_env]}
-
-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}.
-
-A PPX rewriter that will expand [[%get_env "SOME_ENV_VAR"]] into the value of the
-env variable [SOME_ENV_VAR] at compile time, as a string.
-
-E.g., assuming we set [MY_VAR="foo"], it will turn:
-
-{@ocaml[
-let () = print_string [%get_env "foo"]
-]}```
-
-into:
-
-{@ocaml[
-let () = print_string "foo"
-]}
-
-
-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 [env]
-variables, unless you absolutely you know what you're doing.
-
-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.
-
-The entire code is:
-
-{@ocaml[
-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"
-]}
-
-{%html: %}{{!"good-practices"}< Good practices}{%html:
%}{%html:
%}
+- {{:https://ocaml.org/docs/metaprogramming} Ocaml Metaprogramming Docs}
+- {{:https://ocaml-ppx.github.io/ppxlib/ppxlib/index.html} PPXLib documentation}
+- {{:https://www.youtube.com/live/dMoRMqQ6GLs?feature=shared&t=4251} The needed introduction to writing a ppx}
+- {{:https://tarides.com/blog/2019-05-09-an-introduction-to-ocaml-ppx-ecosystem/} An introduction to OCaml PPX ecosystem}
diff --git a/doc/images/attribute_name-ast.png b/doc/images/attribute_name-ast.png
new file mode 100644
index 000000000..35d78b9e1
Binary files /dev/null and b/doc/images/attribute_name-ast.png differ
diff --git a/doc/images/attribute_name.png b/doc/images/attribute_name.png
new file mode 100644
index 000000000..c9c6c4361
Binary files /dev/null and b/doc/images/attribute_name.png differ
diff --git a/doc/images/attribute_payload-ast.png b/doc/images/attribute_payload-ast.png
new file mode 100644
index 000000000..ed3ebd901
Binary files /dev/null and b/doc/images/attribute_payload-ast.png differ
diff --git a/doc/images/attribute_payload.png b/doc/images/attribute_payload.png
new file mode 100644
index 000000000..8f3e777ef
Binary files /dev/null and b/doc/images/attribute_payload.png differ
diff --git a/doc/images/extension_node-ast.png b/doc/images/extension_node-ast.png
new file mode 100644
index 000000000..c78f4d65e
Binary files /dev/null and b/doc/images/extension_node-ast.png differ
diff --git a/doc/images/extension_node.png b/doc/images/extension_node.png
new file mode 100644
index 000000000..0d10b83db
Binary files /dev/null and b/doc/images/extension_node.png differ
diff --git a/doc/images/extension_node_name-ast.png b/doc/images/extension_node_name-ast.png
new file mode 100644
index 000000000..d92212b05
Binary files /dev/null and b/doc/images/extension_node_name-ast.png differ
diff --git a/doc/images/extension_node_name.png b/doc/images/extension_node_name.png
new file mode 100644
index 000000000..40fed1be7
Binary files /dev/null and b/doc/images/extension_node_name.png differ
diff --git a/doc/images/extension_node_payload-ast.png b/doc/images/extension_node_payload-ast.png
new file mode 100644
index 000000000..41381d669
Binary files /dev/null and b/doc/images/extension_node_payload-ast.png differ
diff --git a/doc/images/extension_node_payload.png b/doc/images/extension_node_payload.png
new file mode 100644
index 000000000..c9ef05f06
Binary files /dev/null and b/doc/images/extension_node_payload.png differ
diff --git a/doc/images/module_name-ast.png b/doc/images/module_name-ast.png
new file mode 100644
index 000000000..08d5d2900
Binary files /dev/null and b/doc/images/module_name-ast.png differ
diff --git a/doc/images/module_name.png b/doc/images/module_name.png
new file mode 100644
index 000000000..b2a312e25
Binary files /dev/null and b/doc/images/module_name.png differ
diff --git a/doc/images/module_structure-ast.png b/doc/images/module_structure-ast.png
new file mode 100644
index 000000000..fdedf158f
Binary files /dev/null and b/doc/images/module_structure-ast.png differ
diff --git a/doc/images/module_structure.png b/doc/images/module_structure.png
new file mode 100644
index 000000000..7a4110458
Binary files /dev/null and b/doc/images/module_structure.png differ
diff --git a/doc/images/module_structure_item-ast.png b/doc/images/module_structure_item-ast.png
new file mode 100644
index 000000000..3aa6515b5
Binary files /dev/null and b/doc/images/module_structure_item-ast.png differ
diff --git a/doc/images/module_structure_item.png b/doc/images/module_structure_item.png
new file mode 100644
index 000000000..f63da845b
Binary files /dev/null and b/doc/images/module_structure_item.png differ
diff --git a/doc/images/pattern-ast.png b/doc/images/pattern-ast.png
new file mode 100644
index 000000000..e9d3fb149
Binary files /dev/null and b/doc/images/pattern-ast.png differ
diff --git a/doc/images/pattern.png b/doc/images/pattern.png
new file mode 100644
index 000000000..c512ff3c5
Binary files /dev/null and b/doc/images/pattern.png differ
diff --git a/doc/images/strucure_item-ast.png b/doc/images/strucure_item-ast.png
new file mode 100644
index 000000000..975dbee3e
Binary files /dev/null and b/doc/images/strucure_item-ast.png differ
diff --git a/doc/images/strucure_item.png b/doc/images/strucure_item.png
new file mode 100644
index 000000000..322d52a07
Binary files /dev/null and b/doc/images/strucure_item.png differ
diff --git a/examples/1-AST/a - Building AST/README.md b/examples/1-AST/a - Building AST/README.md
new file mode 100644
index 000000000..4b0b8dda9
--- /dev/null
+++ b/examples/1-AST/a - Building AST/README.md
@@ -0,0 +1,9 @@
+# Building AST
+
+Checkout the [Examples - Building AST](https://ocaml-ppx.github.io/ppxlib/examples-ast-building.html) for more information
+
+To run this example:
+
+```sh
+make example-building_ast
+```
diff --git a/examples/1-AST/a - Building AST/building_ast.ml b/examples/1-AST/a - Building AST/building_ast.ml
new file mode 100644
index 000000000..7c337f71c
--- /dev/null
+++ b/examples/1-AST/a - Building AST/building_ast.ml
@@ -0,0 +1,77 @@
+open Ppxlib
+
+let loc = Location.none
+
+let zero ~loc : Ppxlib_ast.Ast.expression =
+ {
+ pexp_desc = Pexp_constant (Pconst_integer ("0", None));
+ pexp_loc = loc;
+ pexp_loc_stack = [];
+ pexp_attributes = [];
+ }
+
+let _ =
+ print_endline
+ ("\nAST with AST pure tree build: "
+ ^ Astlib.Pprintast.string_of_expression (zero ~loc))
+
+let one ~loc =
+ Ast_builder.Default.pexp_constant ~loc (Parsetree.Pconst_integer ("1", None))
+
+let _ =
+ print_endline
+ ("\nAST with AST build pexp_constant: "
+ ^ Astlib.Pprintast.string_of_expression (one ~loc))
+
+let two ~loc = Ast_builder.Default.eint ~loc 2
+
+let _ =
+ print_endline
+ ("\nAST with AST build eint: "
+ ^ Astlib.Pprintast.string_of_expression (two ~loc))
+
+let three ~loc = [%expr 3]
+
+let _ =
+ print_endline
+ ("\nAST with AST build eint: "
+ ^ Astlib.Pprintast.string_of_expression (three ~loc))
+
+let let_expression =
+ let expression =
+ Ast_builder.Default.pexp_constant ~loc:Location.none
+ (Pconst_integer ("3", None))
+ in
+ let pattern =
+ Ast_builder.Default.ppat_var ~loc:Location.none
+ (Ast_builder.Default.Located.mk ~loc:Location.none "foo")
+ in
+ let let_binding =
+ Ast_builder.Default.value_binding ~loc:Location.none ~pat:pattern
+ ~expr:expression
+ in
+
+ Ast_builder.Default.pexp_let ~loc:Location.none Nonrecursive [ let_binding ]
+ (Ast_builder.Default.eunit ~loc:Location.none)
+
+let _ =
+ print_endline
+ ("\nLet expression with Ast_builder: "
+ ^ Astlib.Pprintast.string_of_expression let_expression)
+
+let let_expression =
+ [%expr
+ let foo = 3 in
+ ()]
+
+let _ =
+ print_endline
+ ("\nLet expression with metaquot: "
+ ^ Astlib.Pprintast.string_of_expression let_expression)
+
+let anti_quotation_expr expr = [%expr 1 + [%e expr]]
+
+let _ =
+ print_endline
+ ("\nLet expression with metaquot and anti-quotation: "
+ ^ Astlib.Pprintast.string_of_expression (anti_quotation_expr (one ~loc)))
diff --git a/examples/1-AST/a - Building AST/dune b/examples/1-AST/a - Building AST/dune
new file mode 100644
index 000000000..82eb2ed13
--- /dev/null
+++ b/examples/1-AST/a - Building AST/dune
@@ -0,0 +1,7 @@
+(executable
+ (name building_ast)
+ (public_name building-ast-example)
+ (package ppxlib)
+ (libraries ppxlib ppxlib.astlib)
+ (preprocess
+ (pps ppxlib.metaquot)))
diff --git a/examples/1-AST/ast_explorer.ml b/examples/1-AST/ast_explorer.ml
new file mode 100644
index 000000000..79e5f384d
--- /dev/null
+++ b/examples/1-AST/ast_explorer.ml
@@ -0,0 +1,8 @@
+(*
+ Add any code you want to explore here
+ and run `ppxlib-pp-ast ./examples/1-AST/ast_explorer.ml` on the repo root to see the AST
+*)
+
+(* Sample code, edit as you wish *)
+let name = "john doe"
+let age = 99
\ No newline at end of file
diff --git a/examples/1-AST/b - Destructing AST/README.md b/examples/1-AST/b - Destructing AST/README.md
new file mode 100644
index 000000000..e807aadac
--- /dev/null
+++ b/examples/1-AST/b - Destructing AST/README.md
@@ -0,0 +1,9 @@
+# Destructuring AST
+
+Checkout the [Examples - Destructing AST](https://ocaml-ppx.github.io/ppxlib/examples-ast-destructing.html) for more information
+
+To run this example:
+
+```sh
+make example-destructing_ast
+```
diff --git a/examples/1-AST/b - Destructing AST/destructuring_ast.ml b/examples/1-AST/b - Destructing AST/destructuring_ast.ml
new file mode 100644
index 000000000..6ca4fc544
--- /dev/null
+++ b/examples/1-AST/b - Destructing AST/destructuring_ast.ml
@@ -0,0 +1,98 @@
+open Ppxlib
+
+let loc = Location.none
+
+let one ~loc = [%expr 1]
+
+let structure_item loc =
+ let expr = one ~loc in
+ Ast_builder.Default.pstr_eval ~loc expr []
+
+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")
+
+let test_match_pstr_eval () =
+ let structure_item = structure_item loc in
+ let structure = [ structure_item ] in
+ match match_int_payload ~loc (PStr structure) with
+ | Ok _ ->
+ Printf.printf "\nMatched 1 using Ast_pattern"
+ | Error _ -> Printf.printf "\nDid not match pstr_eval"
+
+let _ = test_match_pstr_eval ()
+
+let match_int_payload =
+ let open Ast_pattern in
+ pstr (pstr_eval (pexp_constant (pconst_integer (string "1") none)) nil ^:: nil)
+
+let test_match_pstr_eval () =
+ let structure_item = structure_item loc in
+ let structure = [ structure_item ] in
+ try Ast_pattern.parse match_int_payload loc (PStr structure) Printf.printf "\nMatched 1 using Ast_pattern"
+ with _ -> Printf.printf "\nDid not match 1 payload using Ast_pattern"
+
+let _ = test_match_pstr_eval ()
+
+let match_int_payload =
+ let open Ast_pattern in
+ pstr (pstr_eval (eint (int 1)) nil ^:: nil)
+
+let test_match_pstr_eval () =
+ let structure_item = structure_item loc in
+ let structure = [ structure_item ] in
+ try Ast_pattern.parse match_int_payload loc (PStr structure) Printf.printf "\nMatched 1 using Ast_patter with eint"
+ with _ -> Printf.printf "\nDid not match 1 payload using Ast_pattern with eint"
+
+let _ = test_match_pstr_eval ()
+
+let match_int_payload expr =
+ match expr with
+ | [%expr 1] -> Ok 1
+ | _ ->
+ Error
+ (Location.Error.createf ~loc:expr.pexp_loc
+ "Value is not a valid integer")
+
+let test_match_pstr_eval () =
+ let expr = one ~loc in
+ match match_int_payload expr with
+ | Ok _ ->
+ Printf.printf "\nMatched 1 using metaquot"
+ | Error _ -> Printf.printf "\nDid not match 1 using metaquot"
+
+let _ = test_match_pstr_eval ()
+let let_expression = [%expr 1 + 4]
+
+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
+ "Value is not a valid integer"))
+ | _ -> Error (Location.Error.createf ~loc:expr.pexp_loc "Wrong pattern")
+
+let test_match_pstr_eval () =
+ match match_int_payload let_expression with
+ | Ok value ->
+ Printf.printf "\nMatched 1 + using metaquot and anti-quotation: %s"
+ (value |> string_of_int)
+ | Error _ -> Printf.printf "\nDid not match matched 1 + using metaquot and anti-quotation"
+
+let _ = test_match_pstr_eval ()
diff --git a/examples/1-AST/b - Destructing AST/dune b/examples/1-AST/b - Destructing AST/dune
new file mode 100644
index 000000000..6399e89dc
--- /dev/null
+++ b/examples/1-AST/b - Destructing AST/dune
@@ -0,0 +1,7 @@
+(executable
+ (name destructuring_ast)
+ (public_name destructuring-ast-example)
+ (package ppxlib)
+ (libraries ppxlib ppxlib.astlib)
+ (preprocess
+ (pps ppxlib.metaquot)))