123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276openBaseopenPpxlibopenAst_builder.Default(* The scope in which to find [Optional_syntax]. [From_module] means using
module.Optional_syntax.Optional_syntax *)typemodule_scope=|Use_optional_syntax|Use_optional_syntax_optional_syntax|From_moduleoflongidentlocmoduleMatched_expression_element=structtypet={module_:module_scope;exp:expression}endtypet={default_module:module_scope;original_matched_expr:expression;elements:Matched_expression_element.tlist;match_loc:Location.t;cases:caselist}letmodule_scope_of_option=function|None->Use_optional_syntax|Somemodule_->From_modulemodule_;;letinfer_module_from_core_type~module_(core_type:core_type)=letdefault=module_scope_of_optionmodule_inmatchcore_type.ptyp_descwith|Ptyp_constr(longident,_params)->(matchlongident.txtwith|Lident_->Use_optional_syntax_optional_syntax|Ldot(longident,_label)->From_module{txt=longident;loc=core_type.ptyp_loc}|Lapply_->default)|_->default;;letexpand_matched_expr~(module_:longidentlocoption)matched_expr=letindividual_exprs=matchmatched_expr.pexp_descwith|Pexp_tupleexprs->exprs|_->[matched_expr]inList.mapindividual_exprs~f:(funexp->matchexp.pexp_descwith|Pexp_constraint(_exp,core_type)->{Matched_expression_element.module_=infer_module_from_core_type~module_core_type;exp}|_->{module_=module_scope_of_optionmodule_;exp});;letoptional_syntax_str="Optional_syntax"letoptional_syntax~module_:Longident.t=match(module_:module_scope)with|Use_optional_syntax->Lidentoptional_syntax_str|Use_optional_syntax_optional_syntax->Ldot(Lidentoptional_syntax_str,optional_syntax_str)|From_moduleid->Ldot(Ldot(id.txt,optional_syntax_str),optional_syntax_str);;leteoperator~loc~module_func=letlid:Longident.t=Ldot(optional_syntax~module_,func)inpexp_ident~loc(Located.mk~loclid);;leteunsafe_value=eoperator"unsafe_value"leteis_none=eoperator"is_none"letrecassert_binderpat=matchpat.ppat_descwith|Ppat_constraint(pat,_)->(* Allow "Some (_ : typ)" *)assert_binderpat|Ppat_var_|Ppat_any->()|_->Location.raise_errorf~loc:pat.ppat_loc"sub patterns are restricted to variable names and wildcards";;letdisable_all_warningse=letattr=letloc=Location.noneinattribute~loc~name:{Location.loc;txt="ocaml.warning"}~payload:(PStr[pstr_eval~loc(estring~loc"-a")[]])in{ewithpexp_attributes=attr::e.pexp_attributes};;letvarnamei=Printf.sprintf"__ppx_optional_e_%i"iletevar~loci=evar~loc(varnamei)letpvar~loci=pvar~loc(varnamei)letget_pattern_and_binding~module_ipattern=letloc=pattern.ppat_locinletpat,binding_opt=matchpatternwith|[%pat?Some[%p?x]]->assert_binderx;letbinding=value_binding~loc~pat:[%pat?([%px]:_)]~expr:(eapply~loc(eunsafe_value~loc~module_)[evar~loci])in[%pat?false],Somebinding|[%pat?None]->[%pat?true],None|[%pat?_]->pattern,None|_->Location.raise_errorf~loc:pattern.ppat_loc"only None, Some and _ are supported in [%%optional ]"in(* by only using the ppat_desc from the pattern we just generated we ensure that the
location of the original pattern is kept. *){patternwithppat_desc=pat.ppat_desc},binding_opt;;letrewrite_case~match_loc~modules~default_module{pc_lhs=pat;pc_rhs=body;pc_guard}=letmodules_array=Array.of_listmodulesinletget_modulei=(* Sadly, we need to be able to handle the case when the length of the matched
expression doesn't equal the length of the case, in order to produce useful
error messages (with the proper types). *)ifi<Array.lengthmodules_arraythenmodules_array.(i)elsedefault_moduleinletppat_desc,bindings=matchpat.ppat_descwith|Ppat_tuplepatts->letpatts,binding_opts=List.mapipatts~f:(funipatt->letmodule_=get_moduleiinget_pattern_and_binding~module_ipatt)|>List.unzipinPpat_tuplepatts,List.filter_mapbinding_opts~f:Fn.id|_->letpat,binding_opt=get_pattern_and_binding0pat~module_:(List.hd_exnmodules)inpat.ppat_desc,Option.to_listbinding_optinletpc_lhs={patwithppat_desc}inletpc_rhs,pc_guard=matchbindingswith|[]->body,pc_guard|_->(pexp_let~loc:match_locNonrecursivebindingsbody,Option.mappc_guard~f:(funpc_guard->pexp_let~loc:pc_guard.pexp_locNonrecursivebindingspc_guard))in{pc_lhs;pc_rhs;pc_guard};;(** Take the matched expression and replace all its components by a variable, which will
have been bound previously, wrapped by [wrapper].
We do keep the location of the initial component for the new one. *)letrewrite_matched_exprt~wrapper=letsubst_and_wrapi{Matched_expression_element.module_;exp}=letloc={exp.pexp_locwithloc_ghost=true}inwrapper~module_(evar~loci)inletpexp_desc=matcht.elementswith|[singleton]->(subst_and_wrap0singleton).pexp_desc|list->Pexp_tuple(List.mapilist~f:subst_and_wrap)inletpexp_loc={t.original_matched_expr.pexp_locwithloc_ghost=true}in{t.original_matched_exprwithpexp_desc;pexp_loc};;letreal_matcht=letnew_matched_expr=rewrite_matched_exprt~wrapper:(fun~module_expr->letloc=expr.pexp_locineapply~loc(eis_none~loc~module_)[expr])inletmodules=List.mapt.elements~f:(fun{module_;_}->module_)inletcases=List.mapt.cases~f:(rewrite_case~match_loc:t.match_loc~modules~default_module:t.default_module)in(* we can disable the warning here as we rely on the other match we generate for
error messages. *)disable_all_warnings(pexp_match~loc:t.match_locnew_matched_exprcases);;letfake_matcht=letnew_matched_expr=rewrite_matched_exprt~wrapper:(fun~module_expr->letloc=expr.pexp_locin[%expr(* This code will never be executed, it is just here so the type checker
generates nice error messages. *)if[%eeis_none~loc~module_][%eexpr]thenNoneelseSome([%eeunsafe_value~loc~module_][%eexpr])])inpexp_match~loc:{t.match_locwithloc_ghost=true}new_matched_exprt.cases;;letbindings_for_matched_exprmatched_expr=letbindiexpr=letloc={expr.pexp_locwithloc_ghost=true}invalue_binding~loc~pat:(pvar~loci)~exprinList.mapimatched_expr~f:(funi{Matched_expression_element.exp;_}->bindiexp);;letexpand_match~match_loc~(module_:longidentlocoption)matched_exprcases=lett={default_module=module_scope_of_optionmodule_;original_matched_expr=matched_expr;elements=expand_matched_expr~module_matched_expr;match_loc;cases}inletfake_match=(* The types in this branch actually match what the user would expect given the source
code, so we tell merlin to do all its work in here. *)Merlin_helpers.focus_expression(fake_matcht)inletreal_match=(* The types here actually have nothing to do with what's in the source ([bool]
appears for example), so we tell merlin to avoid that branch. *)Merlin_helpers.hide_expression(real_matcht)inletbindings=bindings_for_matched_exprt.elementsinletloc={match_locwithloc_ghost=true}inpexp_let~locNonrecursivebindings(pexp_ifthenelse~loc(ebool~locfalse)fake_match(Somereal_match));;(* We add the indirection instead of directly matching on [pexp_match] when declaring the
extension because we want more informative error messages than "Extension was not
translated". *)letexpand_match~loc~path:_~arg:(module_:longidentlocoption)e=Ast_pattern.parseAst_pattern.(pexp_match____)loce~on_error:(fun()->Location.raise_errorf~loc"[%%optional ] must apply to a match statement")(expand_match~match_loc:e.pexp_loc~module_);;letoptional=Extension.declare_with_path_arg"optional"Extension.Context.expressionAst_pattern.(single_expr_payload__)expand_match;;let()=Driver.register_transformation"optional"~extensions:[optional]