123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269openBaseopenPpxlibopenPpxlib.Ast_builder.Defaultletdeclare_let_binding_extension~name~generate_naming_function=letpattern=(* Matches let bindings. The __' also captures the location of the [mutable] and [rec]
flags, if present. The third __ captures the bindings, and the final __ captures
the [rhs] of the let binding. *)Ast_pattern.(single_expr_payload(pexp_let__'____))inExtension.declare_with_path_argnameExpressionpattern(fun~loc~path:_~argrecursive_flagbindingsrhs->(* We don't support recursive let bindings *)(matchrecursive_flag.txtwith|Recursive->Location.raise_errorf~loc:recursive_flag.loc"[let rec] not supported."|Nonrecursive->());(* Wrap all the bindings in a naming function. Turns:
{v
let x = 0
and y = 1
in rhs
v}
into:
{v
let x = (generated_naming_function) 0
and y = (generated_naming_function) 1
in rhs
v}
We only support simple bindings like the above right now. Bindings like:
let [{ x; y }] = something in rhs
aren't supported. *)letbindings=List.mapbindings~f:(fun{pvb_pat;pvb_expr;pvb_attributes;pvb_loc;_}->(* The [pvb_pat] must be a simple assignment to a name right now. Maybe we can
add support for structure unpacking later. *)letraise_binding_not_supported()=Location.raise_errorf~loc:pvb_pat.ppat_loc"This form of let binding is not currently supported"inletloc={pvb_locwithloc_ghost=true}inletjane_pattern_desc=Ppxlib_jane.Shim.Pattern_desc.of_parsetreepvb_pat.ppat_desc(* This ppx is built to operate with Jane Street's parse tree, so we have to
use [Ppxlib_jane.Shim] to convert to/from the upstream parse tree (which
notably doesn't support labeled tuples). *)inmatchjane_pattern_descwith|Ppat_var{txt;loc=_}|Ppat_constraint({ppat_desc=Ppat_var{txt;loc=_};_},_,_)->letvb=value_binding~loc:pvb_loc~pat:pvb_pat~expr:[%expr[%egenerate_naming_function~arg~loc~name:txt][%epvb_expr]]in{vbwithpvb_attributes}|Ppat_tuple(pat_list,Closed)->(* {[
let ~a:c, b = expr in
]}
transforms to
{[
let ~a:c, b = (fun ~a:c, b -> ~a:(name c "c"), (name b "b")) expr in
]}
We require that all tuple components have the same type.
*)letpat_list=List.mappat_list~f:(function(* We transform the locations of the patterns into ghost locations,
otherwise we get an error about pattern locations overlapping. I'm not
too sure that this doesn't cause any harmful side effects... *)|label,p->label,{pwithppat_loc={p.ppat_locwithloc_ghost=true}})inlettuple_naming_func=lettuple_component_naming_fn_calls=List.mappat_list~f:(function_,var->(matchvar.ppat_descwith|Ppat_var{txt;loc=_}->[%expr[%egenerate_naming_function~arg~loc~name:txt][%eevar~loctxt]]|_->raise_binding_not_supported()))inletnames_with_expressions=(* If the tuple is labelled, this pairs the labels with the naming
expressions. *)List.map2_exnpat_listtuple_component_naming_fn_calls(* [name] here is [None] for regular tuple component, [Some "a"] for
~a:c labelled component. Therefore when creating the labelled tuple
expression, we can use the same [name] from the pattern. *)~f:(fun(name,_)expr->name,expr)inletbody:expression={pexp_loc_stack=[];pexp_attributes=[];pexp_loc=loc;pexp_desc=Ppxlib_jane.Shim.Expression_desc.(Pexp_tuplenames_with_expressions|>to_parsetree~loc)(* Convert the created descriptions back to upstream. *)}inletarg_pat:pattern={ppat_loc_stack=pvb_pat.ppat_loc_stack;ppat_attributes=pvb_pat.ppat_attributes;ppat_loc=loc;ppat_desc=Ppxlib_jane.Shim.Pattern_desc.(Ppat_tuple(pat_list,Closed)|>to_parsetree~loc)}(* [arg_pat] is effectively the same thing as [pvb_pat], just with ghost
locations *)in[%exprfun[%parg_pat]->[%ebody]]inletvb=value_binding~loc:pvb_loc~pat:pvb_pat~expr:[%expr[%etuple_naming_func][%epvb_expr]]in{vbwithpvb_attributes}|_->raise_binding_not_supported())inpexp_let~locNonrecursivebindingsrhs);;(* Assumes [name], [scope], and [thing_to_name] are variables in context. *)letname_intf_expression~module_of_type_of_expression_being_named~loc=letppx_auto_name=String.concat~sep:"."(Longident.flatten_exnmodule_of_type_of_expression_being_named@["__ppx_auto_name"])in[%expr[%eevar~locppx_auto_name]thing_to_name(Hardcaml.Scope.namescopename)];;lethardcaml_signal=Longident.parse"Signal"letalways_variable=Longident.parse"Always.Variable"lethardcaml_name()=declare_let_binding_extension~name:"hw"~generate_naming_function:(fun~arg~loc~name->letmodule_of_type_of_expression_being_named=Option.value_maparg~default:hardcaml_signal~f:(fun{loc=_;txt}->txt)in[%exprfunthing_to_name->letname=[%eestring~locname]in[%ename_intf_expression~module_of_type_of_expression_being_named~loc]]);;lethardcaml_name_collectioncollection=letname=Collection.to_stringcollectioninletmapi_function=Collection.mapicollection~iter_or_map:Mapindeclare_let_binding_extension~name:("hw_"^name)~generate_naming_function:(fun~arg~loc~name->letmodule_of_type_of_expression_being_named=Option.value_maparg~default:hardcaml_signal~f:(fun{loc=_;txt}->txt)in[%expr[%emapi_functionloc]~f:(funidxthing_to_name->letname=[%eestring~locname]^"$"^Ppx_hardcaml_runtime0.Int.to_stringidxin[%ename_intf_expression~module_of_type_of_expression_being_named~loc])]);;lethardcaml_name_option()=declare_let_binding_extension~name:"hw_option"~generate_naming_function:(fun~arg~loc~name->letmodule_of_type_of_expression_being_named=Option.value_maparg~default:hardcaml_signal~f:(fun{loc=_;txt}->txt)in[%exprPpx_hardcaml_runtime0.Option.map~f:(funthing_to_name->letname=[%eestring~locname]in[%ename_intf_expression~module_of_type_of_expression_being_named~loc])]);;letraise_hw_var_doesn't_support_intfs~loc~hw_var_variant=Location.raise_errorf~loc"[hw_var%s] does not take a module argument. It is only used with plain \
[Variable.t]s - use [let%%hw%s.Your_type_here.Of_always] instead"hw_var_varianthw_var_variant;;(* Assumes [scope], [thing_to_name], [name] are variables in context *)letname_always_variable_expr~loc=name_intf_expression~module_of_type_of_expression_being_named:always_variable~loc;;lethardcaml_name_var()=declare_let_binding_extension~name:"hw_var"~generate_naming_function:(fun~arg~loc~name->matchargwith|None->[%exprfun(thing_to_name:Always.Variable.t)->letname=[%eestring~locname]in[%ename_always_variable_expr~loc]]|Some_->raise_hw_var_doesn't_support_intfs~loc~hw_var_variant:"");;lethardcaml_name_var_collectioncollection=letname=Collection.to_stringcollectioninletmapi_function=Collection.mapicollection~iter_or_map:Mapindeclare_let_binding_extension~name:("hw_var_"^name)~generate_naming_function:(fun~arg~loc~name->matchargwith|None->[%expr[%emapi_functionloc]~f:(funidx(thing_to_name:Always.Variable.t)->letname=[%eestring~locname]^"$"^Ppx_hardcaml_runtime0.Int.to_stringidxin[%ename_always_variable_expr~loc])]|Some_->raise_hw_var_doesn't_support_intfs~loc~hw_var_variant:("_"^name));;lethardcaml_name_var_option()=declare_let_binding_extension~name:"hw_var_option"~generate_naming_function:(fun~arg~loc~name->matchargwith|None->[%exprPpx_hardcaml_runtime0.Option.map~f:(fun(thing_to_name:Always.Variable.t)->letname=[%eestring~locname]in[%ename_always_variable_expr~loc])]|Some_->raise_hw_var_doesn't_support_intfs~loc~hw_var_variant:"_option");;letregister()=Driver.register_transformation"hardcaml_naming"~rules:[Context_free.Rule.extension(hardcaml_name());Context_free.Rule.extension(hardcaml_name_collectionList);Context_free.Rule.extension(hardcaml_name_collectionArray);Context_free.Rule.extension(hardcaml_name_collectionIarray);Context_free.Rule.extension(hardcaml_name_option());Context_free.Rule.extension(hardcaml_name_var());Context_free.Rule.extension(hardcaml_name_var_collectionList);Context_free.Rule.extension(hardcaml_name_var_collectionArray);Context_free.Rule.extension(hardcaml_name_var_collectionIarray);Context_free.Rule.extension(hardcaml_name_var_option())];;