Ppx_elpiSourceThis PPX deriver can synthesize glue code for Elpi. The following kind of data types are supported:
type t (or types with a definition that one does not want to expose to Elpi). See the @@elpi.opaque e attribute. Phantom parameters are not supported for now.type 'a t = ('a * int) list .type t = K | S. Such a type can have two roles:The type must come with a pretty printer named following the usual convention (named pp if the type is named t, pp_ty otherwise). Using both @@deriving show, elpi on each data type is the simplest option (show is the one from ppx_deriving, hence preprocess with (pps ppx_deriving.show ppx_elpi)). See also @@elpi.pp.
Example of a HOAS data type
type lctx = | Entry of (string@elpi.key "term") * ty @@elpi.index (module String) @@deriving show, elpi
type term = | Lam of string * ty * (term@elpi.binder "term" lctx (fun s ty -> Entry(s,ty))) | Var of string @elpi.var lctx @@deriving show, elpi
Output (for term):
class type ctx_for_term = object inherit Elpi.API.PPX.ctx method lctx : lctx Elpi.API.PPX.ctx_field end val term : 'c 'csts. (term, #ctx_for_term as 'c, 'csts) ContextualConversion.t val in_ctx_for_term : (ctx_for_term, 'csts) ContextualConversion.ctx_readback
Usage: predicates using HOAS arguments must specify a context large enough for all arguments. They are declared with ContextualPred, whose FFI is BuiltInPredicate.PPX.ffi (it has the constructors of the FFI of Pred, but the conversions of the arguments are contextual).
ContextualPred("term->string", in_ctx_for_term, In(term, "T", Out(BuiltInData.stringC, "S", Read("what else"))), fun (x : term) _ ~depth:_ (c : ctx_for_term) (_ : Data.constraints) (_ : State.t) -> ... x ... c#lctx ...)
Here in_ctx_for_term is a context rich enough to support the readback of data of type term and string.
In Elpi the context type is not a data type: each of its constructors is declared as builtin func entry term -> string, ty. that is, a function from the bound variable (of the type given by @elpi.key "term") to all the fields of the constructor, in order. The entry of a variable is a rule of that function, added under the binder (eg entry x "x" tt ==> ...).
Several data under different contexts
A predicate has a single context, hence one taking in input data of types t1 and t2 living under different contexts needs a context rich enough for both. The structure item
%%elpi.compose_ctx both [t1; t2]
defines it, given that t1 and t2 are derived (with a context each, say c1 and c2, see above). It expands to
class ctx_for_both : Data.hyps -> Data.state -> object inherit ctx_for_t1 inherit ctx_for_t2 end val in_ctx_for_both : (ctx_for_both,'csts) ContextualConversion.ctx_readback
where in_ctx_for_both reads back the context of t1, then the one of t2, and builds the object. The predicate then declares in_ctx_for_both as its context and gets, in the object it receives, the methods c1 and c2:
ContextualPred("t1->t2->string", in_ctx_for_both, In(t1, "A", In(t2, "B", Out(string, "S", Read("...")))), fun (a : t1) (b : t2) _ ~depth:_ c _ _ -> ... c#c1 ... c#c2 ...)
The list must contain at least one type, and the types are read back in the order they are listed. A context shared by several of them is read back once for each (the result is the same). See test_compose_contextual.ml.
Deriving directives: @@deriving elpi Derive a Elpi.API.ContextualConversion.t for the data types in the mutually recursive block. The name of the conversion is the one of the type. See the Conventions section of this doc for more info on the naming of generated code. @@deriving elpi { context = [ty1; ...; tyn]} Specify the types describing the context under which the data type lives and the order in which they should be read back. Default is the list of types mentioned in @elpi.binder and @elpi.var, in no specified order. @@deriving elpi { declaration = l } Also add to l (made by Elpi.API.PPX.empty_declaration ()) the MLDataC declarations that were derived, each one followed by the LPCode of its deep copy function (see below). Elpi.API.PPX.to_list l returns them in the order they were added.
Deep copy:
For each data type ty (but not for context types) the string elpi__ty__deep_copy contains the Elpi code of a deep copy function, eg func ty.copy ty -> ty. (with a copy function for each parameter if the type is a container, eg func t.copy (func X -> Y), t X -> t Y). The LPCode of this string is added to the list passed as { declaration = l }. The copy is the identity, it is up to the user to place his code before it and override the cases he wants in order to implement a non trivial copy (eg a substitution). The elements of lists are copied by list.copy, the ones of the other containers in the standard library by option.copy, pair.copy, triple.copy and quadruple.copy, and the fields of any other type by the function ty.copy, that is assumed to exist (it does for the types of the standard library, eg int.copy, and for the types derived by this ppx; ty is the default Elpi name of the type, hence a type with a custom name given by @@elpi.type_code is only found if it is derived in the same block). A type abbreviation is copied as its definition is, and an opaque type is copied by the identity. A field below a binder is copied under the binder, in the context extended with the entry for the bound variable, which is copied to itself: pi x\ entry x B0 ==> term.copy x x ==> term.copy (A1 x) (B1 x) where entry is the constructor built by the function of the binder (it must have the shape fun p1 ... pn -> C (e1, ..., ek) with the ei among the pi, otherwise no entry is added to the context), B0 is the copy of the field that is the argument of the function and term is the type of the bound variable (the first argument of @elpi.binder), that must come with its copy function. The variables that are not bound by the data being copied are not copied by the generated rules: the user has to add the rule, eg term.copy x x ==> ..., for the ones that occur in the data.
This code currently sucks, it generates a string and not an AST.
Type attributes:
@@elpi.pp f f mandatory: code for pretty printing the data. Its type is the one ppx_deriving.show would produce.
@@elpi.type_code "name" "code" name mandatory: a string, the name of the type in Elpi. The default is the name of the OCaml type in lowercase where _ is replaced by - . code optional: a string used as the Elpi kind declaration of the type (instead of the generated one).
@@elpi.type_doc s s mandatory: a string, a comment printed before the declaration of the type. By default there is none.
@@elpi.default_constructor_readback f f mandatory: a function of type Elpi.API.ContextualConversion.(readback -> readback) called when the term is not any of the constructors (variants and records only). Its argument is the default behavior, a runtime type error. This option can be used to read back flexible terms (in addition to regular constructors).
@@elpi.index (module M) M mandatory: is an OrderedType and Show, it is used to instantiate the functor Elpi.API.Utils.Map.Make. When used in a type, each constructors must have exactly one argument with attribute @elpi.key and that argument must be of type M.t.
@@elpi.opaque e e mandatory: is a Elpi.API.OpaqueData.declaration, it is necessary for opaque data types.
Constructor attributes:
@elpi.var ctx to_key An Elpi bound variable. ctx mandatory: is the name of the context in which the variable is bound. to_key optional: is a function from the constructor arguments to the value being the @elpi.key for the context ctx.
@elpi.skip Not exposed to Elpi.
@elpi.variant n The variant of the global symbol of the constructor. n mandatory: an integer literal, >= 1. Constructors with the same name (eg in different types) must have different variants, and the Elpi declaration is builtin symb name : ... = "n".
@elpi.embed f Custom embedding code. f mandatory: function of type Elpi.API.ContextualConversion.(embedding -> embedding) where the input function is the one this ppx would generate. If you want to override it only in some cases, just call this argument in the other ones.
@elpi.readback f Custom readback code. f mandatory: function of type Elpi.API.ContextualConversion.(readback -> readback) see @elpi.embed.
@elpi.code name code Custom Elpi declaration. name mandatory: a string that stands for the name of the constructor in Elpi. The default is the name of the OCaml constructor in lowercase where _ is replaced by - . Eg Foo_BAR becomes foo-bar. code optional: is a string used as the Elpi type declaration for the constructor. Default is derived from the types of the fields. Example "type lam (term -> term) -> term. % Lam"
@elpi.doc s Custom documentation. s mandatory: a string. Default doc is the name of the OCaml constructor, see the example above.
Constructor field attributes:
@elpi.key "ty" Field used as a key in the Map to values of this type. It can be any of the fields of the constructor. ty mandatory: name (string) of the elpi type of the variables bound by this context, eg the "term" in @elpi.binder "term" ctx .... If the type has parameters, a list of strings "term"; "A"; ...: the parameters are called A, B, C, ... in Elpi, in order. A constructor of the context is declared as builtin func name ty -> field1, ..., fieldN in Elpi (no data type).
@elpi.binder ty ctx mk_ctx_entry Field is below one binder. ty optional: name (string) of the elpi abstraction type, eg the "XXX" in (XXX -> term), or the list "XXX"; "A"; ... if the type has parameters. Default is the type name. ctx mandatory: name of the context in which the variable is bound mk_ctx_entry mandatory: function taking all other fields and returning a ctx entry (a value in the type ctx).
Extensions:
%elpi : ty the conversion of type ty This does not synthesize the conversion code but rather compose the existing ones.
%%elpi.compose_ctx name [ty1; ...; tyn] a context for several data Structure item generating the class ctx_for_<name> and the value in_ctx_for_<name>, a context in which data of type ty1, ..., tyn can all be read back even if they live under different contexts. See the section "Several data under different contexts" above.
Conventions:
<ty> is a value of type Elpi.API.ContextualConversion.t for type ty.
in_ctx_for_<ty> is a value of type Elpi.API.ContextualConversion.ctx_readback for type <ty>, and ctx_for_<ty> is the class type of the context it builds.
ctx_for_<name> and in_ctx_for_<name> are the class and the ctx_readback generated by %%elpi.compose_ctx name [...], same shape as the ones generated for a type.
Elpi_<ctx>_Map is a module of signature Elpi.API.Utils.Map.S built using Elpi.API.Utils.Map.Make(M) where type is annotated with @@elpi.index (module M).
Internal conventions:
Variables are named elpi__something so that they don't collide with any variable named elpi_something or something.
val att_elpi_tcode :
(Ppxlib__.Import.type_declaration, Ppxlib__.Import.expression)
Ppxlib.Attribute.tval att_elpi_def_k_readback :
(Ppxlib__.Import.type_declaration, Ppxlib__.Import.expression)
Ppxlib.Attribute.tval att_elpi_tpp :
(Ppxlib__.Import.type_declaration, Ppxlib__.Import.expression)
Ppxlib.Attribute.tval att_elpi_tindex :
(Ppxlib__.Import.type_declaration, Ppxlib__.Import.module_expr)
Ppxlib.Attribute.tval att_elpi_tcdata :
(Ppxlib__.Import.type_declaration, Ppxlib__.Import.expression)
Ppxlib.Attribute.tval att_elpi_var :
(Ppxlib__.Import.constructor_declaration, Ppxlib__.Import.expression)
Ppxlib.Attribute.tval att_elpi_embed :
(Ppxlib__.Import.constructor_declaration, Ppxlib__.Import.expression)
Ppxlib.Attribute.tval att_elpi_readback :
(Ppxlib__.Import.constructor_declaration, Ppxlib__.Import.expression)
Ppxlib.Attribute.tval att_elpi_code :
(Ppxlib__.Import.constructor_declaration, Ppxlib__.Import.expression)
Ppxlib.Attribute.tval att_elpi_binder :
(Ppxlib__.Import.core_type, Ppxlib__.Import.expression) Ppxlib.Attribute.tval elpi_loc_of_position :
(module Ppxlib.Ast_builder.S) ->
Lexing.position ->
Ppxlib_ast.Ast.expressionval pexp_disable_warnings :
(module Ppxlib.Ast_builder.S) ->
Ppxlib_ast.Ast.expression ->
Ppxlib_ast.Ast.expressiontype arg_type = | FO of {key : string list option;readback : Ppxlib.expression;embed : Ppxlib.expression;ty_ast : Ppxlib.expression;ty : Ppxlib.core_type;}| HO of {ctx : string;build_ctx : Ppxlib.expression;arrow_src_elpi : string list;readback : Ppxlib.expression;embed : Ppxlib.expression;ty_ast : Ppxlib.expression;ty : Ppxlib.core_type;}and expose = {declaration : Ppxlib.structure_item list;constant : Ppxlib.expression;constant_name : string;variant : int;constructor : Ppxlib.expression list -> Ppxlib.expression;pattern : Ppxlib.pattern list -> Ppxlib.pattern;arg_types : arg_type list;embed : codegen_directive;readback : codegen_directive;elpi_code : Ppxlib.expression option;elpi_doc : string;ctx_names : string list;}type elpi_type_decl = | Opaque of Ppxlib.expression| Alias of Ppxlib.core_type| Algebraic of elpi_constructor list * Ppxlib.expression optiontype elpi_type = {name : string;elpi_name : string;elpi_code : string option;elpi_doc : string;params : string list;type_decl : elpi_type_decl;pp : Ppxlib.expression option;index : Ppxlib.module_expr option;}type type_extras = {ty_constants : Ppxlib.structure_item list;ty_embed : Ppxlib.value_binding;ty_readback : Ppxlib.value_binding;ty_ctx_class_type : Ppxlib.structure_item;ty_conversion : Ppxlib.value_binding list;ty_conversion_name : string;ty_elpi_declaration : elpi_declaration;ty_opaque : bool;ty_in_ctx : Ppxlib.structure_item list;ty_deep_copy : string option;}val embed_var :
(module Ppxlib.Ast_builder.S) ->
string ->
Ppxlib__.Import.expression list ->
Ppxlib__.Import.expression ->
Ppxlib_ast.Ast.expressionval error_constructor_not_supported :
(module Ppxlib.Ast_builder.S) ->
(string * bool) ->
Ppxlib__.Import.caseval abstract_standard_branch_embed :
(module Ppxlib.Ast_builder.S) ->
string list ->
Ppxlib_ast.Ast.expression ->
Ppxlib_ast.Ast.expressionval embed_branch :
(module Ppxlib.Ast_builder.S) ->
bool ->
elpi_constructor ->
Ppxlib__.Import.caseval pexp_function_of_cases :
(module Ppxlib.Ast_builder.S) ->
Ppxlib__.Import.cases ->
Ppxlib_ast.Ast.expressionval embed :
(module Ppxlib.Ast_builder.S) ->
bool ->
elpi_constructor list ->
Ppxlib_ast.Ast.expressionval readback_var :
(module Ppxlib.Ast_builder.S) ->
string ->
(Ppxlib_ast.Ast.expression list -> Ppxlib_ast.Ast.expression) ->
Ppxlib_ast.Ast.expressionval abstract_standard_branch_readback :
(module Ppxlib.Ast_builder.S) ->
Lexing.position ->
Ppxlib_ast.Ast.expression ->
Ppxlib_ast.Ast.expressionval abstract_standard_branch_readback2 :
(module Ppxlib.Ast_builder.S) ->
Lexing.position ->
Ppxlib_ast.Ast.expression ->
Ppxlib_ast.Ast.expressionval abstract_standard_default_readback :
(module Ppxlib.Ast_builder.S) ->
Ppxlib_ast.Ast.expression ->
Ppxlib_ast.Ast.expressionval readback :
(module Ppxlib.Ast_builder.S) ->
string ->
bool ->
Ppxlib_ast.Ast.expression option ->
elpi_constructor list ->
Ppxlib_ast.Ast.expressionval ctx_entry_key :
(module Ppxlib.Ast_builder.S) ->
elpi_constructor list ->
Ppxlib_ast.Ast.expressionval is_ctx_entry :
(module Ppxlib.Ast_builder.S) ->
elpi_constructor list ->
Ppxlib_ast.Ast.expressionval compose_ctx_readback :
(module Ppxlib.Ast_builder.S) ->
string ->
string list ->
Ppxlib__.Import.structure_item listval conversion_of :
(module Ppxlib.Ast_builder.S) ->
Ppxlib_ast.Ast.core_type ->
Ppxlib_ast.Ast.expressionval find_embed_of :
(module Ppxlib.Ast_builder.S) ->
string list ->
Ppxlib_ast.Ast.core_type ->
Ppxlib_ast.Ast.expressionval find_readback_of :
(module Ppxlib.Ast_builder.S) ->
string list ->
Ppxlib_ast.Ast.core_type ->
Ppxlib_ast.Ast.expressionval find_ty_ast_of :
(module Ppxlib.Ast_builder.S) ->
string list ->
Ppxlib.core_type ->
Ppxlib_ast.Ast.expressionval find_mapper_of :
(string * string) list ->
(string * string) list ->
Ppxlib_ast.Ast.core_type ->
(string * string) ->
stringval one_or_two_strings :
(module Ppxlib.Ast_builder.S) ->
Ppxlib.expression_desc ->
string * string optionval get_elpi_code :
(module Ppxlib.Ast_builder.S) ->
string ->
Ppxlib__.Import.constructor_declaration ->
string * string optionval get_elpi_tcode :
(module Ppxlib.Ast_builder.S) ->
string ->
Ppxlib__.Import.type_declaration ->
string * string optionval get_elpi_tdefkreadback :
Ppxlib__.Import.type_declaration ->
Ppxlib__.Import.expression optionval get_elpi_tcdata :
loc:Ppxlib.Location.t ->
Ppxlib__.Import.type_declaration ->
Ppxlib__.Import.expressionval parse_lident_list :
(module Ppxlib.Ast_builder.S) ->
Ppxlib_ast.Ast.expression ->
string listval analyze_tuple_constructor :
(module Ppxlib.Ast_builder.S) ->
string ->
string ->
Ppxlib__.Import.constructor_declaration ->
Ppxlib__.Import.core_type list ->
(Ppxlib.expression list -> Ppxlib.expression) ->
(Ppxlib.pattern list -> Ppxlib.pattern) ->
string list ->
elpi_constructorval analyze_constructor :
(module Ppxlib.Ast_builder.S) ->
string ->
string list ->
Ppxlib.constructor_declaration ->
elpi_constructorval analyze_params :
(module Ppxlib.Ast_builder.S) ->
(Ppxlib.core_type * 'a) list ->
string list * Ppxlib.Ast_traverse.mapval mk_kind :
(module Ppxlib.Ast_builder.S) ->
string list ->
Ppxlib_ast.Ast.expression ->
Ppxlib_ast.Ast.expressionval pp_doc :
(module Ppxlib.Ast_builder.S) ->
Ppxlib_ast.Ast.expression ->
Ppxlib_ast.Ast.expression ->
Ppxlib_ast.Ast.expression option ->
string ->
bool ->
elpi_constructor list ->
Ppxlib_ast.Ast.expressionval typeabbrev_for :
(module Ppxlib.Ast_builder.S) ->
Ppxlib_ast.Ast.expression ->
'a list ->
Ppxlib_ast.Ast.expressionval typeabbrev_for_conv :
(module Ppxlib.Ast_builder.S) ->
Ppxlib_ast.Ast.core_type ->
Ppxlib_ast.Ast.expressionval mk_pp_name :
(module Ppxlib.Ast_builder.S) ->
string ->
Ppxlib__.Import.expression option ->
Ppxlib__.Import.expressionval pp_for_conversion :
(module Ppxlib.Ast_builder.S) ->
string ->
bool ->
string list ->
Ppxlib__.Import.expression option ->
Ppxlib_ast.Ast.expressionval quantify_ty_over_params :
(module Ppxlib.Ast_builder.S) ->
string list ->
Ppxlib__.Import.core_type ->
Ppxlib__.Import.core_typeval ctx_obj :
(module Ppxlib.Ast_builder.S) ->
string ->
'a ->
'b list ->
Ppxlib_ast.Ast.core_typeval conversion_type :
(module Ppxlib.Ast_builder.S) ->
string ->
string list ->
bool ->
'a list ->
Ppxlib__.Import.core_typeval readback_type :
(module Ppxlib.Ast_builder.S) ->
string ->
string list ->
bool ->
'a list ->
Ppxlib__.Import.core_typeval embed_type :
(module Ppxlib.Ast_builder.S) ->
string ->
string list ->
bool ->
'a list ->
Ppxlib__.Import.core_typeval lift_conversion :
(module Ppxlib.Ast_builder.S) ->
Ppxlib_ast.Ast.expression ->
Ppxlib_ast.Ast.expressionval coversion_for_opaque :
(module Ppxlib.Ast_builder.S) ->
'a ->
string ->
Ppxlib__.Import.value_bindingval abstract_expr_over_params :
(module Ppxlib.Ast_builder.S) ->
'a list ->
('a -> string) ->
Ppxlib_ast.Ast.expression ->
Ppxlib_ast.Ast.expressionval conversion_context_for_tyd :
(module Ppxlib.Ast_builder.S) ->
string ->
Ppxlib_ast.Ast.structure_item listval typedecl_extras :
(module Ppxlib.Ast_builder.S) ->
Ppxlib_ast.Ast.expression option ->
Ppxlib.type_declaration list ->
mutual_type_extrasval tydecls :
loc:Ppxlib__.Location.t ->
Ppxlib_ast.Ast.expression option ->
Ppxlib_ast.Ast.expression option ->
'a ->
Ppxlib.type_declaration list ->
Ppxlib__.Import.structure_item listval conversion_of_expansion :
loc:Ppxlib__.Location.t ->
path:'a ->
Ppxlib_ast.Ast.core_type ->
Ppxlib_ast.Ast.expressionval expand_str :
loc:Ppxlib__.Location.t ->
path:'a ->
('b * Ppxlib.type_declaration list) ->
Ppxlib.expression option ->
Ppxlib.expression option ->
Ppxlib__.Import.structure_item listval expand_sig :
loc:Ppxlib.Location.t ->
path:'a ->
('b * 'c) ->
Ppxlib.module_expr option ->
'dval str_type_decl_generator :
(Ppxlib__.Import.structure,
Ppxlib__.Import.rec_flag * Ppxlib.type_declaration list)
Ppxlib.Deriving.Generator.tval arguments :
(Ppxlib__.Import.module_expr option ->
Ppxlib__.Import.signature,
Ppxlib__.Import.signature)
Ppxlib.Deriving.Args.tval sig_type_decl_generator :
(Ppxlib__.Import.signature,
Ppxlib__.Import.rec_flag * Ppxlib__.Import.type_declaration list)
Ppxlib.Deriving.Generator.tval compose_ctx_expansion :
loc:Ppxlib__.Location.t ->
path:'a ->
(string * string list) ->
Ppxlib__.Import.structure_item