Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file VisitorsSettings.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371openResultopenVisitorsStringopenListletsprintf=Printf.sprintfopenPpxlibopenParsetreeopenPpx_derivingopenVisitorsPluginopenVisitorsAnalysisopenVisitorsGeneration(* -------------------------------------------------------------------------- *)(* We can generate classes that adhere to several distinct schemes, listed
below. These schemes differ only in the re-building code that is executed
after the recursive calls. In [iter], this code does nothing. In [map], it
reconstructs a data structure. In [endo], it also reconstructs a data
structure, but attempts to preserve sharing. In [reduce], it combines the
results of the recursive calls using a monoid operation. In [fold], this
code is missing; it is represented by a virtual method. *)typescheme=|Iter|Map|Endo|Reduce|MapReduce|Fold(* -------------------------------------------------------------------------- *)(* The parameters that can be set by the user. *)moduletypeSETTINGS=sig(* The type declarations that we are processing. *)valdecls:type_declarationlist(* The name of the generated class. *)valname:classe(* The arity of the generated code, e.g., 1 if one wishes to generate [iter]
and [map], 2 if one wishes to generate [iter2] and [map2], and so on. *)valarity:int(* The scheme of visitor that we wish to generate (see the definition of
the type [scheme] above). *)valscheme:scheme(* [variety] combines the information in [scheme] and [arity]. It is just
the string provided by the user. *)valvariety:string(* [visit_prefix] is the common prefix used to name the descending visitor
methods. It must be nonempty and a valid identifier by itself. Its
default value is "visit_". *)valvisit_prefix:string(* [build_prefix] is the common prefix used to name the ascending visitor
methods. It must be nonempty and a valid identifier by itself. Its
default value is "build_". *)valbuild_prefix:string(* [fail_prefix] is the common prefix used to name the failure methods. It
must be nonempty and a valid identifier by itself. Its default value is
"fail_". *)valfail_prefix:string(* The classes that the visitor should inherit. If [nude] is [false], the
class [VisitorsRuntime.<scheme>] is implicitly prepended to this list.
If [nude] is [true], it is not. *)valancestors:Longident.tlist(* [concrete] controls whether the generated class should be concrete or
virtual. By default, it is virtual. *)valconcrete:bool(* If [irregular] is [true], the regularity check is suppressed; this allows
a local parameterized type to be instantiated. The definition of ['a t]
can then refer to [int t]. However, in most situations, this will lead to
ill-typed generated code. The generated code should be well-typed if [t]
is always instantiated in the same manner, e.g., if there are references
to [int t] but not to other instances of [t]. *)valirregular:bool(* If [public] is present, then every method is declared private, except
the methods whose name appears in the list [public]. *)valpublic:stringlistoption(* If [polymorphic] is [true], then (possibly polymorphic) type annotations
for methods are generated. The function [poly], applied to the name of a
type variable (without its quote), tells whether this type variable
should receive monomorphic or polymorphic treatment. In the former case,
this type variable is dealt with via a visitor method; in the latter
case, it is dealt with via a visitor function. *)valpolymorphic:boolvalpoly:tyvar->bool(* If [data] is [true], then descending visitor methods for data constructors
are generated. This allows the user to request per-data-constructor custom
behavior by overriding these methods. If [data] is [false], then these
methods are not generated. This yields simpler and faster code with
fewer customization opportunities. *)valdata:boolend(* -------------------------------------------------------------------------- *)(* The supported varieties. *)(* Note that [mapreduce] must appear in this list before [map], as shorter
prefixes must be tested last. *)letsupported=["mapreduce",MapReduce;"map",Map;"iter",Iter;"endo",Endo;"reduce",Reduce;"fold",Fold;]letvalid_varieties="iter, map, endo, reduce, mapreduce, fold,\n\
iter2, map2, reduce2, mapreduce2, fold2"letinvalid_varietyloc=raise_errorf~loc"%s: invalid variety. The valid varieties are\n\
%s."pluginvalid_varieties(* [parse_variety] takes a variety, which could be "iter", "map2", etc. and
returns a pair of a scheme and an arity. *)letparse_varietyloc(s:string):scheme*int=(* A loop over [supported] tries each supported variety in turn. *)letrecloopsupporteds=matchsupportedwith|(p,scheme)::supported->ifprefixpsthenlets=remainderpsinleti=ifs=""then1elseint_of_stringsinifi<=0thenfailwith"negative integer"elsescheme,ielseloopsupporteds|[]->failwith"unexpected prefix"in(* Start the loop and handle errors. *)tryloopsupportedswithFailure_->invalid_varietyloc(* -------------------------------------------------------------------------- *)letmust_be_valid_method_name_prefixlocp=ifnot(is_valid_method_name_prefixp)thenraise_errorf~loc"%s: %S is not a valid method name prefix."pluginpletmust_be_valid_mod_longidentlocm=ifnot(is_valid_mod_longidentm)thenraise_errorf~loc"%s: %S is not a valid module identifier."pluginmletmust_be_valid_class_longidentlocc=ifnot(is_valid_class_longidentc)thenraise_errorf~loc"%s: %S is not a valid class identifier."pluginc(* -------------------------------------------------------------------------- *)typebool_or_strings=|Boolofbool|Stringsofstringlistletbool_or_strings:bool_or_stringsArg.conv=fune->matchArg.boole,Arg.listArg.stringewith|Okb,Error_->Ok(Boolb)|Error_,Okalphas->Ok(Stringsalphas)|Error_,Error_->Error"Boolean or string list"|Ok_,Ok_->assertfalse(* -------------------------------------------------------------------------- *)(* The option processing code constructs a module of type [SETTINGS]. *)moduleParse(O:sigvalloc:Location.tvaldecls:type_declarationlistvaloptions:(string*expression)listend):SETTINGS=structopenO(* Set up a few parsers. *)letbool=Arg.get_expr~deriver:pluginArg.boolletstring=Arg.get_expr~deriver:pluginArg.stringletstrings=Arg.get_expr~deriver:plugin(Arg.listArg.string)letbool_or_strings=Arg.get_expr~deriver:pluginbool_or_strings(* Default values. *)letname=refNoneletarity=ref1(* dummy: [variety] is mandatory; see below *)letscheme=refIter(* dummy: [variety] is mandatory; see below *)letvariety=refNoneletvisit_prefix=ref"visit_"letbuild_prefix=ref"build_"letfail_prefix=ref"fail_"letancestors=ref[]letconcrete=reffalseletdata=reftrueletirregular=reffalseletnude=reffalseletpolymorphic=reffalseletpoly=ref(fun_->false)letpublic=refNone(* Parse every option. *)let()=iter(fun(o,e)->letloc=e.pexp_locinmatchowith|"visit_prefix"->visit_prefix:=stringe;must_be_valid_method_name_prefixloc!visit_prefix|"build_prefix"->build_prefix:=stringe;must_be_valid_method_name_prefixloc!build_prefix|"fail_prefix"->fail_prefix:=stringe;must_be_valid_method_name_prefixloc!fail_prefix|"ancestors"->ancestors:=stringse|"concrete"->concrete:=boole|"data"->data:=boole|"irregular"->irregular:=boole|"name"->name:=Some(stringe)|"nude"->nude:=boole|"polymorphic"->(* The [polymorphic] parameter can be a Boolean constant or a list
of type variable names. If [true], then all type variables are
considered polymorphic. If a list of type variables, then only
the variables in the list are considered polymorphic. *)beginmatchbool_or_stringsewith|Boolb->polymorphic:=b;poly:=(fun_->b)|Stringsalphas->letalphas=List.mapunquotealphasinpolymorphic:=true;poly:=(funalpha->List.memalphaalphas)end|"monomorphic"->(* The [monomorphic] parameter is provided as a facility for the user.
It means the reverse of [polymorphic]. This is particularly useful
when the parameter is a list of type variables: then, only the
variables *not* in the list are considered polymorphic. *)beginmatchbool_or_stringsewith|Boolb->polymorphic:=notb;poly:=(fun_->notb)|Stringsalphas->letalphas=List.mapunquotealphasinpolymorphic:=true;(* yes, [true] *)poly:=(funalpha->not(List.memalphaalphas))end|"public"->public:=Some(stringse)|"variety"->letv=stringeinvariety:=Somev;lets,a=parse_varietylocvinscheme:=s;arity:=a;(* [endo] is supported only at arity 1. *)ifs=Endo&&a>1theninvalid_varietyloc|_->(* We could emit a warning, instead of an error, if we find an
unsupported option. That might be preferable for forward
compatibility. That said, I am not sure that ignoring unknown
options is a good idea; it might cause us to generate code
that does not work as expected by the user. *)raise_errorf~loc"%s: option %s is not supported."plugino)options(* Export the results. *)letdecls=declsletarity=!arityletscheme=!schemeletvisit_prefix=!visit_prefixletbuild_prefix=!build_prefixletfail_prefix=!fail_prefixletancestors=!ancestorsletconcrete=!concreteletdata=!dataletirregular=!irregularletnude=!nudeletpolymorphic=!polymorphicletpoly=!polyletpublic=!public(* Perform sanity checking. *)(* The parameter [variety] is not optional. *)letvariety=match!varietywith|None->raise_errorf~loc"%s: please specify the variety of the generated class.\n\
e.g. [@@deriving visitors { variety = \"iter\" }]"plugin|Somevariety->variety(* The parameter [name] is optional. If it is absent, then [variety]
is used as its default value. *)letname=match!namewith|Somename->(* We expect [name] to be a valid class name. *)ifclassifyname<>LIDENTthenraise_errorf~loc"%s: %s is not a valid class name."pluginname;name|None->variety(* Every string in the list [ancestors] must be a valid (long) class
identifier. *)let()=iter(must_be_valid_class_longidentloc)ancestors(* When the variety is [iter], the class [VisitorsRuntime.iter] is an
implicit ancestor, and similarly for every variety. *)letancestors=ifnudethenancestorselse("VisitorsRuntime."^variety)::ancestorsletancestors=mapparseancestors(* If [scheme] is [Fold], then [polymorphic] must be [false]. Indeed,
we currently cannot generate polymorphic type annotations in that
case, as we cannot guess the return types of the visitor methods. *)let()=ifscheme=Fold&&polymorphicthenraise_errorf~loc"%s: cannot generate polymorphic\n\
type annotations for fold visitors."pluginend