Module Import.Ast_builder

include module type of struct include Ppxlib.Ast_builder.Default end
val pdir_int : loc:Astlib.Location.t -> string -> char option -> Astlib.Ast_500.Parsetree.directive_argument
val pjk_abbreviation : loc:Astlib.Location.t -> string -> Astlib.Ast_500.Parsetree.jkind_annotation
val location : start:Lexing.position -> end_:Lexing.position -> ghost:bool -> Astlib.Location.t
val module_instance : instance_head:string -> instance_args:(string * Astlib.Ast_500.Parsetree.module_instance) list -> Astlib.Ast_500.Parsetree.module_instance
val position : fname:string -> lnum:int -> bol:int -> cnum:int -> Lexing.position
val enativeint : loc:Ppxlib.Location.t -> nativeint -> Astlib.Ast_500.Parsetree.expression
val pnativeint : loc:Ppxlib.Location.t -> nativeint -> Astlib.Ast_500.Parsetree.pattern

evar id produces a Pexp_ident _ expression, it parses its input so you can pass any dot-separated identifier, for instance: evar ~loc "Foo.bar".

pstr_value_list ~loc rf vbs = pstr_value ~loc rf vbs if vbs <> [], [] otherwise.

  • deprecated [since 2016-10] use Nonrecursive on the P(str|sig)_type instead
val unapplied_type_constr_conv : loc:Ppxlib.Location.t -> Ppxlib.Longident.t Ppxlib.Loc.t -> f:(string -> string) -> Astlib.Ast_500.Parsetree.expression

unapplied_type_constr_conv is the standard way to map identifiers to conversion fonctions, for preprocessor that creates values that follow the structure of types. More precisely, path_conv path (sprintf "sexp_of_%s") is:

  • sexp_of_t if path is "t"
  • A.B.sexp_of_foo if path is "A.B.foo"
  • A.B.sexp_of_f__foo (module A1) (module A2) if path is "A.B.F(A1)(A2).foo" type_constr_conv also applies it to a list of expression, which both prevents the compiler from allocating useless closures, and almost always what is needed, since type constructors are always applied.

Tries to simplify fun v1 v2 .. -> f v1 v2 .. into f. Only works when f is a path, not an arbitrary expression as that would change the meaning of the code. This can be used either for cleaning up the generated code, or to reduce allocation if f is a local variable (the compiler won't optimize the allocation of the closure).

Eta-reduction can change the types/behavior in some corner cases that are unlikely to show up in generated code:

  • if f has optional arguments, eta-expanding f can drop them
  • because labels commute, it can change the type of an expression: $ let f ~x y = x + y let f2 = fun x -> add x;; val f : x:int -> int -> int = <fun> val f2 : int -> x:int -> int = <fun> In fact, if f does side effects before receiving all its arguments, and if the eta-expansion is partially applied, eta-reducing could change behavior.

eta_reduce_if_possible_and_nonrec is meant for the case where the resulting expression is going to be bound in a potentially recursive let-binding, where we have to keep the eta-expansion when rec_flag is Recursive to avoid a compile error.

include module type of struct include Ppxlib_jane.Ast_builder.Default end
type index_kind = Ppxlib_jane.Shim.index_kind

Modes

Construct a multi-argument arrow type with the provided arguments and result.

  • raises [Invalid_argument]

    if the input list is empty.

As tarrow, but will return the result if the input list is empty rather than erroring; this means the result type cannot have a mode annotation.

Construct a Ppat_constraint with modes

Contruct a value_binding with modes

Construct a Pcstr_tuple, a representation for the contents of a tupled variant constructor, that attaches the provided modalities to each field.

Construct a Psig_include with modalities

Splits a possibly-modality-annotated field of a tupled variant constructor into a pair of its modality and the unannotated field. If the resulting mode is None, then the field is returned unchanged.

Splits a possibly-modality-annotated label declaration into a pair of its modality and the unannotated label declaration. If the resulting modality is None, then the label declaration is returned unchanged.

val value_description : loc:Astlib.Location.t -> name:string Astlib.Location.loc -> type_:Ppxlib_ast.Parsetree.core_type -> modalities:Ppxlib_jane.Shim.Modalities.t -> prim:string list -> Ppxlib_ast.Parsetree.value_description

N-ary functions

Many comments below make reference to the Jane Street compiler's treatment of function arity. These comments refer to a parsetree change made to upstream OCaml in https://github.com/ocaml/ocaml/pull/12236, but that Jane Street has mirrored internally already.

The treatment of arity can be summarized as follows:

  • In a previous version of OCaml, a function's runtime arity was inferred at a late stage of the compiler, after typechecking, where it fuses together nested lambdas.
  • In the new version of OCaml (both upstream OCaml after #12236 and the internal Jane Street compiler), a function's runtime arity is purely a syntactic notion: it's the number of parameters in a fun x1 ... xn -> body construct, with some special allowances for function cases.

Why is arity important? In native code, application sites of a function to n syntactic arguments will trigger a fast path (where arguments are passed in registers) only if the function's runtime arity is n.

As a result, ppxes must take more care than before to generate functions of the correct arity. Now, a nested function like fun x -> fun y -> e has arity 1 (returning still another function of arity 1) instead of arity 2. All bindings below that construct functions are documented as to the arity of the returned function.

Some examples of arity:

  • 2-ary function: fun x y -> e
  • 1-ary function returning 1-ary function: fun x -> fun y -> e
  • 3-ary function: fun x y -> function P1 -> e1 | P2 -> e2
  • 2-ary function returning 1-ary function: fun x y -> (function P1 -> e1 | P2 -> e2)
  • 2-ary function returning 1-ary function: fun x -> function P1 -> function P2 -> e

Notably, unparenthesized function has a special meaning when used as a direct body of fun: the function becomes part of the arity of the outer fun. The same does not apply for multiple nested functions, even if they each have a single case; the nested functions are treated as unary. (See the last example.)

Create a function with unlabeled parameters and an expression body. Like Ppxlib.Ast_builder.eapply, but for constructing functions.

coalesce_fun_arity is relevant for the Jane Street compiler. By default, coalesce_fun_arity is true.

Suppose there is a call eabstract pats body ~coalesce_fun_arity

  • If colaesce_fun_arity is true, the arity of the returned function is the same as the arity of: add_fun_params (List.map params ~f:(Fun.param Nolabel)) body
  • If coalesce_fun_arity is false, then the arity of the returned function is the length of pats.

In other words, coalesce_fun_arity = true allows you to build up the arity of an already-constructed function rather than necessarily creating a new function.

unary_function cases is function <cases>. When used with the Jane Street compiler, the function's runtime arity is 1, so the fast path for function application happens only when application sites of the resulting function receive 1 argument. To create a function with multiple argument that pattern-matches on the last one, use add_param or add_params to add more parameters. Alternatively, use pexp_function to provide all parameters at once.

The attributes of the resulting expression will be the attrs argument together with any attributes added by the Jane Street compiler.

fun_param lbl pat is Pparam_val (lbl, None, pat). This gives a more self-documenting way of constructing the usual case: value parameters without optional argument defaults.

Say an expression is a "function" if it is a Pexp_fun or a Pexp_function. All functions have parameters and arity.

Suppose add_param lbl def pat e ==> e'. Then, letting param = Pparam_val (lbl, def, pat),

  • If e is a function with arity n, then e' is a function with arity n+1. param is added at the outermost layer. For example, if e = fun <params> -> <body>, then e' = fun <param :: params> -> body. The attributes on the resulting expression will be the attrs argument together with any attributes already present on e.
  • If e is not a function, then e' is a function with arity 1, namely: fun <param> -> <e>. The attributes of the resulting expression will be the attrs argument together with any attributes added by the Jane Street compiler.

add_params params e is List.fold_right params ~init:e ~f:add_param. Note the fold_right: if e is fun <params'> -> <body>, then add_params params e is fun <params @ params'> -> <body>.

This operation is a no-op, except as interpreted by the Jane Street compiler. If e is a function with arity n with an expression body that itself is a function with arity m, then coalesce_fun_arity e is a function of arity n + m.

You should usually call coalesce_fun_arity on metaquot fun expressions whose body may be a function, e.g.:

coalesce_fun_arity [%expr fun x y -> [%e possibly_function]]

Unboxed types

val ptyp_unboxed_tuple : loc:Astlib.Location.t -> (string option * Ppxlib_ast.Parsetree.core_type) list -> Ppxlib_ast.Parsetree.core_type

Expression literals

val eint64_u : loc:Astlib.Location.t -> int64 -> Ppxlib_ast.Parsetree.expression

e.g. #42L

val eint32_u : loc:Astlib.Location.t -> int32 -> Ppxlib_ast.Parsetree.expression

e.g. #42l

val enativeint_u : loc:Astlib.Location.t -> nativeint -> Ppxlib_ast.Parsetree.expression

e.g. #42n

val efloat_u : loc:Astlib.Location.t -> string -> Ppxlib_ast.Parsetree.expression

e.g. #42.

Pattern literals

val pint64_u : loc:Astlib.Location.t -> int64 -> Ppxlib_ast.Parsetree.pattern

e.g. #42L

val pint32_u : loc:Astlib.Location.t -> int32 -> Ppxlib_ast.Parsetree.pattern

e.g. #42l

val pnativeint_u : loc:Astlib.Location.t -> nativeint -> Ppxlib_ast.Parsetree.pattern

e.g. #42n

val pfloat_u : loc:Astlib.Location.t -> string -> Ppxlib_ast.Parsetree.pattern

e.g. #42.

Layouts

Labeled tuples

Immutable arrays