123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330(*
* OCanren. PPX suntax extensions.
* Copyright (C) 2015-2019
* Dmitri Boulytchev, Dmitry Kosarev, Alexey Syomin, Evgeny Moiseenko
* St.Petersburg State University, JetBrains Research
*
* This software is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2, as published by the Free Software Foundation.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU Library General Public License version 2 for more details
* (enclosed in the file COPYING).
*)(* Performs two minikanren-specific macro expansions:
* 1) fresh (x1 ... xm) e1 ... en
* to
* Fresh.numeral (fun x1 ... xm -> e1 &&& ... &&& en)
*
* 2) TODO: write about defer
*)openBaseopenPpxlibopenPpxlib.Ast_helperletis_state_patternpat=matchpat.ppat_descwith|Ppat_varvwhenString.equalv.txt"st"||String.equalv.txt"state"->Somev.txt|_->Noneletclassify_name~fe=matche.pexp_descwith|Pexp_identiwhenfi.txt->true|_->falseletneed_insert_fname~namee=classify_namee~f:(Stdlib.(=)(Lidentname))(* match e.pexp_desc with
| Pexp_ident i when i.txt = Lident name -> true
| _ -> false *)letis_defer=need_insert_fname~name:"defer"letis_conde=need_insert_fname~name:"conde"letis_fresh=need_insert_fname~name:"fresh"letis_call_fresh=need_insert_fname~name:"call_fresh"letis_unif=classify_name~f:(function|Lidents->(String.lengths>=3)&&(String.equal(String.subs~pos:0~len:3)"===")|_->false)letis_conj=need_insert_fname~name:"conj"letis_conj_list=need_insert_fname~name:"?&"letis_disje=need_insert_fname~name:"disj"e||need_insert_fname~name:"|||"e(*
let rec walkthrough ~fname (expr: expression) =
let add_fname () =
[%expr [%e Ast_helper.Exp.constant (Pconst_string (fname,None))] <=>
[%e expr]
]
in
match expr.pexp_desc with
| Pexp_fun (_label, _opt, pat, e2) -> begin
match is_state_pattern pat with
| None ->
{ expr with pexp_desc =
Pexp_fun (_label, _opt, pat, walkthrough ~fname e2) }
| Some argname ->
(* printf "found good function with statearg '%s'\n%!" argname; *)
let new_body =
[%expr
let () = Printf.printf "entering '%s'\n%!" [%e Ast_helper.Exp.constant (Pconst_string (fname,None))] in
let ans = [%e e2] in
let () = Printf.printf "leaving '%s'\n%!" [%e Ast_helper.Exp.constant (Pconst_string (fname,None))] in
ans
]
in
{ expr with pexp_desc= Pexp_fun (_label, _opt, pat, new_body) }
end
| Pexp_apply (e,_) when is_call_fresh e -> add_fname ()
| Pexp_apply (e,_) when is_disj e -> add_fname ()
| Pexp_apply (e,_) when is_conj e -> add_fname ()
| _ -> expr
let map_value_binding (vb : value_binding) =
match vb.pvb_pat.ppat_desc with
| Ppat_var name ->
let fname = name.txt in
{ vb with pvb_expr = walkthrough ~fname vb.pvb_expr }
| _ -> vb
let smart_logger =
{ default_mapper with
structure_item = fun mapper sitem ->
match sitem.pstr_desc with
| Pstr_value (_rec, vbs) ->
{ sitem with pstr_desc = Pstr_value (_rec, List.map vbs ~f:map_value_binding) }
| x -> default_mapper.structure_item mapper sitem
}
*)letoption_map~f=functionSomex->Some(fx)|None->Noneletoption_bind~f=functionSomex->fx|None->NoneexceptionNot_an_identletreconstruct_argse=letopenLongidentinletare_all_idents(xs:(_*expression)list)=trySome(List.mapxs~f:(fun(_,e)->matche.pexp_descwith|Pexp_ident{txt=(Longident.Lidenti);_}->i|_->raiseNot_an_ident))withNot_an_ident->Noneinmatche.pexp_descwith|Pexp_apply({pexp_desc=Pexp_ident{txt=Longident.Lidentarg1;_}},ys)->(* fresh (var1 var2 var3) body *)option_map(are_all_identsys)~f:(funxs->arg1::xs)(* no fresh variables: just for geting rid of &&& *)|Pexp_construct({txt=Lident"()"},None)->Some[](* [fresh arg0 body] -- single fresh variable *)|Pexp_ident{txt=Lidentarg1;_}->Some[arg1]|_->Noneletlist_fold~f~initerxs=matchxswith|[]->failwith"bad argument"|start::xs->List.fold~init:(initerstart)~fxsletlist_fold_right0~f~initerxs=letrechelper=function|[]->failwith"bad_argument"|x::xs->list_fold~initer~f:(funaccx->fxacc)(x::xs)inhelper(List.revxs)letmy_list~loces=List.fold_right~init:[%expr[]]es~f:(funxacc->[%expr[%ex]::[%eacc]])letparse_to_listalist=letrechelperaccele=matchele.pexp_descwith|Pexp_construct({txt=Lident"[]"},None)->acc|Pexp_construct({txt=Lident"::"},Some{pexp_desc=Pexp_tuple[y1;y2];_})->helper(y1::acc)y2|x->[ele]inList.rev@@helper[]alistletmapper=object(self)inheritAst_traverse.mapassupermethod!expressione=letloc=e.pexp_locinmatche.pexp_descwith|Pexp_apply(_,[])->e|Pexp_apply(e1,(_,alist)::args)whenis_conj_liste1->letclauses:expressionlist=parse_to_listalistinletans=list_fold_right0clauses~initer:(funx->x)~f:(funxacc->[%expr[%ex]&&&[%eacc]])insuper#expressionans|Pexp_apply(e1,(_,alist)::otherargs)whenis_condee1->begin[%exprconde[%eself#expressionalist]]end|Pexp_apply(e1,[args])whenis_freshe1->(* bad syntax -- no body*)e|Pexp_apply(e1,(Nolabel,args)::body)whenis_freshe1->beginassert(List.lengthbody>0);letbody=List.map~f:sndbodyinletnew_body:expression=matchbodywith|[]->assertfalse|[body]->self#expressionbody|body->letxs=List.map~f:self#expressionbodyin[%expr?&[%emy_list~locxs]]inmatchreconstruct_argsargswith|Some(xs:stringlist)->letans=List.fold_rightxs~f:(funidentacc->[%exprFresh.one(fun[%pPat.var~loc(Ast_builder.Default.Located.mkident~loc)]->[%eacc])])~init:[%exprdelay(fun()->[%enew_body])]inans|None->Caml.Format.eprintf"Can't reconstruct args of 'fresh'";{ewithpexp_desc=Pexp_apply(e1,[Nolabel,new_body])}end|Pexp_apply(d,[(_,body)])whenis_deferd->letans=[%exprdelay(fun()->[%eself#expressionbody])]inans|Pexp_apply(d,body)whenis_unifd->(* let loc_str =
Caml.Format.asprintf "%a" Selected_ast.Ast.Location.print_compact e.pexp_loc;
in
let body = (Labelled "loc", Exp.constant (Pconst_string (loc_str,None))) :: body in *)Exp.apply~loc:e.pexp_locdbody|Pexp_apply(e,xs)->letans=Pexp_apply(self#expressione,List.map~f:(fun(lbl,e)->(lbl,self#expressione))xs)inletans={ewithpexp_desc=ans}inans|Pexp_fun(l,opt,pat,e)->{ewithpexp_desc=Pexp_fun(l,opt,pat,self#expressione)}|Pexp_construct(_,None)->e|Pexp_construct(id,Somee1)->{ewithpexp_desc=Pexp_construct(id,Some(self#expressione1))}(* kind of default mapping below *)|Pexp_constant_|Pexp_ident_->e|Pexp_variant(l,eopt)->leteopt=option_mapeopt~f:self#expressionin{ewithpexp_desc=Pexp_variant(l,eopt)}|Pexp_record(xs,o)->leto=option_mapo~f:self#expressioninletxs=List.mapxs~f:(fun(s,e)->(s,self#expressione))in{ewithpexp_desc=Pexp_record(xs,o)}|Pexp_field(e,lident)->lete=self#expressionein{ewithpexp_desc=Pexp_field(e,lident)}|Pexp_setfield(l,lab,r)->letl=self#expressionlinletr=self#expressionrin{ewithpexp_desc=Pexp_setfield(l,lab,r)}|Pexp_arrayes->{ewithpexp_desc=Pexp_array(List.map~f:self#expressiones)}|Pexp_ifthenelse(s,th,el)->lets=self#expressionsinletth=self#expressionthinletel=option_mapel~f:self#expressionin{ewithpexp_desc=Pexp_ifthenelse(s,th,el)}|Pexp_sequence(e1,e2)->{ewithpexp_desc=Pexp_sequence(self#expressione1,self#expressione2)}|Pexp_tuplees->{ewithpexp_desc=Pexp_tuple(List.map~f:self#expressiones)}|Pexp_let(_recflag,vbs,where_expr)->letvbs_new=List.mapvbs~f:(funvb->{vbwithpvb_expr=(self#expressionvb.pvb_expr)})in{ewithpexp_desc=Pexp_let(_recflag,vbs_new,self#expressionwhere_expr)}|Pexp_while(e1,e2)->lete1=self#expressione1inlete2=self#expressione2in{ewithpexp_desc=Pexp_while(e1,e2)}|Pexp_for(p,e1,e2,flg,e3)->lete1=self#expressione1inlete2=self#expressione2inlete3=self#expressione3in{ewithpexp_desc=Pexp_for(p,e1,e2,flg,e3)}|Pexp_constraint(ee,t)->{ewithpexp_desc=Pexp_constraint(self#expressionee,t)}|Pexp_coerce(expr,t1,t2)->letexpr=self#expressionexprin{ewithpexp_desc=Pexp_coerce(expr,t1,t2)}|Pexp_send(e,lab)->lete=self#expressionein{ewithpexp_desc=Pexp_send(e,lab)}|Pexp_new_->e|Pexp_setinstvar(l,body)->letbody=self#expressionbodyin{ewithpexp_desc=Pexp_setinstvar(l,body)}|Pexp_overridees->letes=List.mapes~f:(fun(l,e)->(l,self#expressione))in{ewithpexp_desc=Pexp_overridees}|Pexp_letmodule(name,me,body)->{ewithpexp_desc=Pexp_letmodule(name,self#module_exprme,self#expressionbody)}|Pexp_letexception(ec,e1)->lete1=self#expressione1in{ewithpexp_desc=Pexp_letexception(ec,e1)}|Pexp_asserte->{ewithpexp_desc=Pexp_assert(self#expressione)}|Pexp_lazye1->lete1=self#expressione1in{ewithpexp_desc=Pexp_lazye1}|Pexp_poly(e1,t)->lete1=self#expressione1in{ewithpexp_desc=Pexp_poly(e1,t)}|Pexp_newtype(name,ee)->{ewithpexp_desc=Pexp_newtype(name,self#expressionee)}|Pexp_functioncases->{ewithpexp_desc=Pexp_function(List.map~f:self#casecases)}|Pexp_match(s,cases)->letscru=self#expressionsin{ewithpexp_desc=Pexp_match(scru,List.map~f:self#casecases)}|Pexp_try(s,cases)->letscru=self#expressionsin{ewithpexp_desc=Pexp_try(scru,List.map~f:self#casecases)}|Pexp_object_|Pexp_unreachable->e|Pexp_open(_od,ee)->{ewithpexp_desc=Pexp_open(_od,self#expressionee)}|Pexp_letop_|Pexp_extension_|Pexp_pack_->e(* | _ ->
Caml.Format.printf "%a\n%a\n%!" Location.print loc Pprintast.expression e;
assert false*)endlet()=Ppxlib.Driver.register_transformation~impl:mapper#structure"pa_minikanren"