123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156openBaseopenPpxlibopenAst_builder.Defaultletassert_no_guard=function|None->()|Someguard->Location.raise_errorf~loc:guard.pexp_loc"guards are not supported in [%%optional ]"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_exhaustivity_warninge=letattr=letloc=Location.nonein{Location.loc;txt="ocaml.warning"},PStr[pstr_eval~loc(estring~loc"-8")[]]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_bindingipattern=letloc=pattern.ppat_locinletpat,binding_opt=matchpatternwith|[%pat?Some[%p?x]]->assert_binderx;letbinding=value_binding~loc~pat:x~expr:(eapply~loc[%exprOptional_syntax.unsafe_value][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_optletrewrite_case~match_loc{pc_lhs=pat;pc_rhs=body;pc_guard}=assert_no_guardpc_guard;letppat_desc,bindings=matchpat.ppat_descwith|Ppat_tuplepatts->letpatts,binding_opts=List.unzip(List.mapipatts~f:get_pattern_and_binding)inPpat_tuplepatts,List.filter_mapbinding_opts~f:Fn.id|_->letpat,binding_opt=get_pattern_and_binding0patinpat.ppat_desc,Option.to_listbinding_optinletpc_lhs={patwithppat_desc}inletpc_rhs=matchbindingswith|[]->body|_->pexp_let~loc:match_locNonrecursivebindingsbodyin{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_expr~wrapperexpr=letsubst_and_wrapiexpr=letloc=expr.pexp_locin{(wrapper(evar~loci))withpexp_loc=loc}inletpexp_desc=matchexpr.pexp_descwith|Pexp_tupleexprs->Pexp_tuple(List.mapiexprs~f:subst_and_wrap)|_->(subst_and_wrap0expr).pexp_descin{exprwithpexp_desc}letreal_match~match_locmatched_exprcases=letmatched_expr=rewrite_matched_exprmatched_expr~wrapper:(funexpr->letloc=expr.pexp_locineapply~loc[%exprOptional_syntax.is_none][expr])inletcases=List.mapcases~f:(rewrite_case~match_loc)in(* we can disable the warning here as we rely on the other match we generate for
error messages. *)disable_exhaustivity_warning(pexp_match~loc:match_locmatched_exprcases)letfake_match~match_locmatched_exprcases=letmatched_expr=rewrite_matched_exprmatched_expr~wrapper:(funexpr->letloc=expr.pexp_locin[%expr(* This code will never be executed, it is just here so the type checker
generates nice error messages. *)ifOptional_syntax.is_none[%eexpr]thenNoneelseSome(Optional_syntax.unsafe_value[%eexpr])])inpexp_match~loc:match_locmatched_exprcasesletbindings_for_matched_exprmatched_expr=letbindiexpr=letloc=expr.pexp_locinvalue_binding~loc~pat:(pvar~loci)~exprinmatchmatched_expr.pexp_descwith|Pexp_tupleexprs->List.mapiexprs~f:bind|_->[bind0matched_expr]letexpand_match~match_locmatched_exprcases=letfake_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_match~match_locmatched_exprcases)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_match~match_locmatched_exprcases)inletbindings=bindings_for_matched_exprmatched_exprinletloc=match_locinpexp_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:_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)letoptional=Extension.declare"optional"Extension.Context.expressionAst_pattern.(single_expr_payload__)expand_matchlet()=Driver.register_transformation"optional"~extensions:[optional]