123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297(*
* pa_ocanren: a camlp5 extension to implement syntax-level
* miniKanren constructs.
* Copyright (C) 2015-2020
* Dmitri Boulytchev, St.Petersburg State University
*
* 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).
*)(** {1 Camlp5 syntax extension for miniKanren syntax constructs}
There are two syntax extensions provided: [fresh] and [defer].
[fresh] is a direct analog to the corresponding construct in original miniKanren. It has the form
[fresh (x y ...) g], where [x y ...] is a list of free variables, created by [fresh], [g] --- some goal.
[defer] performs "inverse-eta-delay". It has the form [defer (g)] and expanded into [fun st -> Lazy.from_fun (fun () -> g st)].
*)openPcamlopenPrintfletrecfold_right1f=function|[h]->h|h::t->fh(fold_right1ft)|[]->failwith"fold_right1"letrecfold_left1fxs=List.fold_leftf(List.hdxs)(List.tlxs)letdecapitalizes=String.init(String.lengths)(function0->Char.lowercase_asciis.[0]|i->s.[i])letrecctore=letloc=MLast.loc_of_expreinmatchewith|<:expr<$uid:u$>>->Some(<:expr<$lid:decapitalizeu$>>)|<:expr<$longid:m$.($e$)>>->(matchctorewithSomee->Some(<:expr<$longid:m$.($e$)>>)|_->None)|<:expr<$m$.($e$)>>->(matchctorewithSomee->Some(<:expr<$m$.($e$)>>)|_->None)|_->Noneletlist_of_listes=letloc=MLast.loc_of_expr(List.hdes)inletconsab=<:expr<[$a$::$b$]>>inList.fold_right(funelst->conselst)es<:expr<[]>>letrecfix_terme=letloc=MLast.loc_of_expreinmatchewith|<:expr<$e1$$e2$>>->(matchctore1with|Somee1'->(matche2with|<:expr<($list:ts$)>>->List.fold_left(funacce-><:expr<$acc$$fix_terme$>>)e1'ts|_-><:expr<$e1'$$fix_terme2$>>)|_->(matchewith|<:expr<OCanren.Std.nil()>>->e|_-><:expr<$fix_terme1$$fix_terme2$>>))|<:expr<($list:ts$)>>->(* isolated tuple case (not an argument to a constructor *)(matchtswith|[e]->fix_terme|_->fold_right1(funetup-><:expr<OCanren.Std.pair$e$$tup$>>)@@List.mapfix_termts)|_->(* everything else *)(matchctorewith|Somee-><:expr<$e$()>>|_->e)(* Borrowed from camlp5 OCaml parser *)letis_operator=letht=Hashtbl.create73inletct=Hashtbl.create73inList.iter(funx->Hashtbl.addhtxtrue)["asr";"land";"lor";"lsl";"lsr";"lxor";"mod";"or"];List.iter(funx->Hashtbl.addctxtrue)['!';'*';'+';'-';'/';':';'<';'=';'>';'@';'^';'~';'?';'%';'.';'$'];funx->tryHashtbl.findhtxwithNot_found->tryHashtbl.findctx.[0]with_->falseletoperator_rparen=Grammar.Entry.of_parsergram"operator_rparen"(funstrm->matchStream.npeek2strmwith|[("",s);("",")")]whenis_operators->Stream.junkstrm;Stream.junkstrm;s|_->raiseStream.Failure)letoperator=Grammar.Entry.of_parsergram"operator"(funstrm->matchStream.npeek1strmwith|[("",s)]whenis_operators->Stream.junkstrm;s|_->raiseStream.Failure)letsymbolchar=letlist=['!';'$';'%';'&';'*';'+';'-';'.';'/';':';'<';'=';'>';'?';'@';'^';'|';'~']inletrecloopsi=ifi==String.lengthsthentrueelseifList.mems.[i]listthenloops(i+1)elsefalseinloopletprefix=letlist=['!';'?';'~']inletexcl=["!=";"??";"?!"]inGrammar.Entry.of_parsergram"prefixop"(funstrm->matchStream.npeek1strmwith|[("",s)]whennot(List.memsexcl)&&String.lengths>=2&&List.mems.[0]list&&symbolchars1->Stream.junkstrm;s|_->raiseStream.Failure)letop_from_listl=letb=Buffer.create64inletadd=Buffer.add_stringbinList.iteraddl;Buffer.contentsbletof_val=function|Ploc.VaValx->x|Ploc.VaAnt_->failwith"Should not happen in our setup of Camlp5"(* Decorate type expressions *)letrecdecorate_typectyp=letloc=MLast.loc_of_ctypctypinmatchctypwith|<:ctyp<int>>-><:ctyp<OCanren.Std.Nat.logic>>|<:ctyp<bool>>-><:ctyp<OCanren.Std.Bool.logic>>|<:ctyp<$lid:id$>>-><:ctyp<OCanren.logic$ctyp$>>|<:ctyp<ocanren$t$>>->t|<:ctyp<list$y$>>-><:ctyp<OCanren.Std.List.logic$decorate_typey$>>|<:ctyp<option$y$>>-><:ctyp<OCanren.Std.Option.logic$decorate_typey$>>|<:ctyp<$x$$y$>>->lett=<:ctyp<$x$$decorate_typey$>>in<:ctyp<OCanren.logic$t$>>|<:ctyp<$longid:p$.$lid:t$>>-><:ctyp<OCanren.logic$ctyp$>>|<:ctyp<($list:ts$)>>->fold_right1(funt1t2-><:ctyp<OCanren.Std.Pair.logic$t1$$t2$>>)@@List.mapdecorate_typets|_->ctypEXTENDGLOBAL:exprctypstr_item;(* Kakadu: It looks like this function has become unneeded *)(* long_ident:
[ RIGHTA
[ i = LIDENT -> <:expr< $lid:i$ >>
| i = UIDENT -> <:expr< $uid:i$ >>
| "("; op=operator_rparen -> <:expr< $lid:op$ >>
| i = UIDENT; "."; j = SELF ->
let rec loop m =
function
| <:expr< $x$ . ($y$) >> -> loop <:expr< $m$ . ($x$) >> y
| e -> <:expr< $m$ . ($e$) >>
in
loop <:expr< $uid:i$ >> j
]]; *)(* TODO: support conde expansion here *)expr:LEVEL"expr1"[["fresh";"(";vars=LIST0LIDENT;")";clauses=LIST1exprLEVEL"."->letbody=letconjunctions=fold_left1(funaccx-><:expr<conj($acc$)($x$)>>)clausesin<:expr<delay(fun()->$conjunctions$)>>inletans=letrecloop=function|a::b::c::tl->letpa=<:patt<$lid:a$>>inletpb=<:patt<$lid:b$>>inletpc=<:patt<$lid:c$>>in<:expr<OCanren.Fresh.three(fun$pa$$pb$$pc$->$looptl$)>>|a::b::tl->letrez=looptlinletpa=<:patt<$lid:a$>>inletpb=<:patt<$lid:b$>>in<:expr<OCanren.Fresh.two(fun$pa$$pb$->$rez$)>>|a::[]->letpa=<:patt<$lid:a$>>in<:expr<OCanren.Fresh.one(fun$pa$->$body$)>>|[]->bodyinloopvarsinans]|["defer";subj=exprLEVEL"."-><:expr<delay(fun()->$subj$)>>]|[e=ocanren_embedding->e]];ocanren_embedding:[["ocanren";"{";e=ocanren_expr;"}"->e]];ocanren_expr:["top"RIGHTA[l=SELF;"|";r=SELF-><:expr<OCanren.disj$l$$r$>>]|RIGHTA[l=SELF;"&";r=SELF-><:expr<OCanren.conj$l$$r$>>]|["fresh";vars=LIST1LIDENTSEP",";"in";b=ocanren_exprLEVEL"top"->List.fold_right(funxb->letp=<:patt<$lid:x$>>in<:expr<OCanren.call_fresh(fun$p$->$b$)>>)varsb]|"primary"[p=prefix;t=ocanren_term->letp=<:expr<$lid:p$>>in<:expr<$p$$t$>>|l=ocanren_term;"==";r=ocanren_term-><:expr<OCanren.unify$l$$r$>>|l=ocanren_term;"=/=";r=ocanren_term-><:expr<OCanren.diseq$l$$r$>>|l=ocanren_term;op=operator;r=ocanren_term->letp=<:expr<$lid:op$>>inleta=<:expr<$p$$l$>>in<:expr<$a$$r$>>|x=ocanren_term->x|"{";e=ocanren_expr;"}"->e|"||";"(";es=LIST1ocanren_exprSEP";";")"-><:expr<OCanren.conde$list_of_listes$>>|"&&";"(";es=LIST1ocanren_exprSEP";";")"->letop=<:expr<$lid:"?&"$>>inletid=<:expr<OCanren.($op$)>>in<:expr<$id$$list_of_listes$>>]];ocanren_term:[[t=ocanren_term'->fix_termt]];ocanren_term':["app"LEFTA[l=SELF;r=SELF-><:expr<$l$$r$>>]|"list"RIGHTA[l=SELF;"::";r=SELF-><:expr<OCanren.Std.List.cons$l$$r$>>]|"primary"["!";"(";e=expr;")"->e|c=INT->letn=<:expr<$int:c$>>in<:expr<OCanren.Std.nat$n$>>|c=CHAR->lets=<:expr<$chr:c$>>in<:expr<OCanren.inj(OCanren.lift$s$)>>|s=STRING->lets=<:expr<$str:s$>>in<:expr<OCanren.inj(OCanren.lift$s$)>>|"true"-><:expr<OCanren.Std.Bool.truo>>|"false"-><:expr<OCanren.Std.Bool.falso>>|"[";ts=LIST0ocanren_term'SEP";";"]"->(matchtswith|[]-><:expr<OCanren.Std.nil()>>|_->List.fold_right(funxl-><:expr<OCanren.Std.List.cons$x$$l$>>)ts<:expr<OCanren.Std.nil()>>)|"(";op=operator_rparen-><:expr<$lid:op$>>|"(";ts=LIST0ocanren_term'SEP",";")"->(matchtswith|[]-><:expr<OCanren.inj(OCanren.lift())>>|[t]->t|_-><:expr<($list:ts$)>>)]|[e=exprLEVEL"simple"->e]];ctyp:[["ocanren";"{";t=ctyp;"}"->decorate_typet]|"simple"["!";"(";t=ctyp;")"-><:ctyp<ocanren$t$>>]];END;