123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156(** This modules defines a functor whose image is a parser for terms with
applications, binary and unary operators. These terms are specified in the
argument of the functor.
The algorithm implemented is an extension of the Pratt parser. The Shunting
Yard algorithm could also be used.
@see <https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html>
@see <https://dev.to/jrop/pratt-parsing> *)(** Associativity of an operator. *)typeassociativity=|Left(** If [+] is a left associative operator, [x + y + z] is parsed [(x +
y) + z]. *)|Right(** If [+] is a right associative operator, [x + y + z] is parsed [x +
(y + z)]. *)|Neither(** If [+] is not associative, then [(x + y) + z] is not [x + (y + z)] and
[x + y + z] results in a syntax error. *)typepriority=float(** Priority of operators. If [*] has a higher priority than [+], than [x + y *
z] is parsed [x + (y * z)]. *)(** A type to designate operators and their properties. *)typeoperator=|Binofassociativity(** Binary operator with an associativity. *)|Una(** Unary operator. *)(** Types and utilities on terms that are to be Pratt parsed. *)moduletypeSUPPORT=sigtypeterm(** The main type of terms, that contains symbols, applications, binary and
unary operators. *)typetable(** The table is used to store available operators. *)valget:table->term->(operator*priority)option(** [get tbl t] returns [None] if [t] is not an operator according to table
[tbl], and it returns the properties of the operator otherwise. *)valmake_appl:term->term->term(** [make_appl t u] returns the application of [t] to [u], sometimes noted
[@(t, u)], or just [t u]. *)endmoduleMake:functor(Sup:SUPPORT)->sigexceptionOpConflictofSup.term*Sup.term(** Raised when there is a priority or associativiy conflict between two
operators. The arguments are the terms that generate the conflict. *)exceptionTooFewArguments(** Raised when more arguments are expected. It is raised for instance on
partial application of operators, such as [x +]. *)valexpression:Sup.table->Sup.termStream.t->Sup.term(** [expression tbl s] parses stream of tokens [s] with table of operators
[tbl]. It transforms a sequence of applications to a structured
application tree containing infix and prefix operators. For instance,
assuming that [+] is declared infix, it transforms [3 + 5 + 2],
represented as [@(@(@(@(3,+),5),+),2)] (where [@] is the application) into
[(@(+(@(+,3,5)),2)].
@raise TooFewArguments when the stream [s] is empty or does not have
enough elements.
@raise OpConflict when the input terms cannot be parenthesised
unambiguously. *)end=functor(Sup:SUPPORT)->structtypetable=Sup.tableexceptionOpConflictofSup.term*Sup.termexceptionTooFewArguments(* NOTE: among the four functions operating on streams, only [expression]
consumes elements from it. *)(** [nud tbl strm t] is the production of term [t] with {b no} left context.
If [t] is not an operator, [nud] is the identity. Otherwise, the output
is a production rule. *)letrecnud:table->Sup.termStream.t->Sup.term->Sup.term=funtblstrmt->matchSup.gettbltwith|Some(Una,rbp)->Sup.make_applt(expression~tbl~rbp~rassoc:Neitherstrm)|_->t(** [led ~tbl ~strm ~left t assoc bp] is the production of term [t] with
left context [left]. We have the invariant that [t] is a binary operator
with associativity [assoc] and binding power [bp]. This invariant is
ensured while called in {!val:expression}. *)andled:tbl:table->strm:Sup.termStream.t->left:Sup.term->Sup.term->associativity->priority->Sup.term=fun~tbl~strm~lefttassocbp->letrbp=matchassocwith|Right->bp*.(1.-.epsilon_float)|Left|Neither->bpinSup.(make_appl(make_appltleft)(expression~tbl~rbp~rassoc:assocstrm))(** [expression ~tbl ~rbp ~rassoc strm] parses next token of stream
[strm] with previous operator having a right binding power [~rbp] and
associativity [~rassoc]. *)andexpression:tbl:table->rbp:priority->rassoc:associativity->Sup.termStream.t->Sup.term=fun~tbl~rbp~rassocstrm->(* [aux left] inspects the stream and may consume one of its elements, or
return [left] unchanged. *)letrecaux(left:Sup.term)=matchStream.peekstrmwith|None->left|Somept->(matchSup.gettblptwith|Some(Binlassoc,lbp)->iflbp>rbp||(lbp=rbp&&lassoc=Right&&rassoc=Right)then(* Performed before to execute side effect on stream. *)letnext=Stream.nextstrminaux(led~tbl~strm~leftnextlassoclbp)elseiflbp<rbp||(lbp=rbp&&lassoc=Left&&rassoc=Left)thenleftelseraise(OpConflict(left,pt))|_->(* argument of an application *)letnext=Stream.nextstrminletright=nudtblstrmnextinaux(Sup.make_applleftright))intryletnext=Stream.nextstrminletleft=nudtblstrmnextinauxleftwithStream.Failure->raiseTooFewArgumentsletexpression:table->Sup.termStream.t->Sup.term=funtblstrm->expression~tbl~rbp:0.~rassoc:Neitherstrmend