123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171(** 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 Sunting
Yard algorithm could also be used.
@see <https://dev.to/jrop/pratt-parsing>
@see <https://effbot.org/zone/simple-top-down-parsing.htm> *)(** 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)]. *)(** Reprensentation of operators. *)typeoperator=|Unary(** Unary prefix operators. *)|Binaryofassociativity(** Binary infix operators with its associativity. *)typepriority=float(** Priority of operators. If [*] has a higher priority than [+], than [x + y *
z] is parsed [x + (y * z)]. *)(** 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. *)typepos(** The type of positions. *)typepopt=posoptiontypeident(** Type of identifiers of symbols. *)valget_ident:term->(ident*popt)option(** [get_ident t] returns the identifier and (optional) position of term [t],
if [t] is an identifier. *)valmake_appl:term->term->term(** [make_appl t u] returns the application of [t] to [u], sometimes noted
[@(t, u)], or just [t u]. *)valmake_bin_appl:term->popt->ident*associativity*priority->term->term(** [make_bin_appl t p op u] returns the application of binary operator [op]
in position [p] to terms [t] and [u], so the term [t op u]. *)valmake_una_appl:popt->ident*priority->term->term(** [make_una_appl p op t] returns the application of unary operator [op] in
position [p] to term [t], so the term [op t]. *)endmoduleMake:functor(Sup:SUPPORT)->sigvaladd_unary:Sup.ident->priority->unit(** [add_unary id pr] adds unary operator identified by [id] with priority
[pr] to the table of operators. *)valadd_binary:Sup.ident->priority->associativity->unit(** [add_binary id pr assoc] adds binary operator identified by [id] with
priority [pr] to the table of operators, with associativity [assoc]. *)valflush:unit->unit(** [flush ()] empties the table of operators. *)valexpression:?rbp:priority->Sup.termStream.t->Sup.term(** [expression rbp s] parses stream of tokens [s] with right binding power
[rbp] (which is 0 by default). 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)]. *)end=functor(Sup:SUPPORT)->struct(** Table containing all registered binary and unary operators that may
appear in terms parsed by {!val:Pratt.expression}. *)letoperators:(Sup.ident,operator*priority)Hashtbl.t=Hashtbl.create17letadd_unarysp=Hashtbl.addoperatorss(Unary,p)letadd_binaryspa=Hashtbl.addoperatorss(Binarya,p)letflush()=Hashtbl.resetoperators(** [lbp t] returns the left binding power of term [t] (which is 0 if [t] is
not an operator). *)letlbp:Sup.term->priority=funpt->matchSup.get_identptwith|Some(s,_)->(matchHashtbl.find_optoperatorsswith|Some(Binary_,bp)|Some(Unary,bp)->bp|None->assertfalse)|_->(* [t] must be an operator *)assertfalse(* NOTE: among the four functions operating on streams, only [expression]
consumes elements from it. *)(** [is_binop t] returns [true] iff term [t] is a binary operator. *)letis_binop:Sup.term->bool=funt->matchSup.get_identtwith|Some(s,_)->(matchHashtbl.find_optoperatorsswith|Some(Binary_,_)->true|_->false)|_->false(** [nud 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:Sup.termStream.t->Sup.term->Sup.term=funstrmt->matchSup.get_identtwith|Some(s,p)->(matchHashtbl.find_optoperatorsswith|Some(Unary,rbp)->Sup.make_una_applp(s,rbp)(expression~rbpstrm)|_->t)|_->t(** [led left t] is the production of term [t] with left context
[left]. *)andled:Sup.termStream.t->Sup.term->Sup.term->Sup.term=funstrmleftt->matchSup.get_identtwith|Some(s,p)->(matchHashtbl.find_optoperatorsswith|Some(Binaryassoc,bp)->letrbp=ifassoc=Rightthenbp*.(1.-.epsilon_float)elsebpinSup.make_bin_applleftp(s,assoc,bp)(expression~rbpstrm)|_->assertfalse(* [t] must be an operator. *))|_->(* [t] must be an operator *)assertfalseandexpression:?rbp:priority->Sup.termStream.t->Sup.term=fun?(rbp=0.)strm->(* [aux left] inspects the stream and may consume one of its elements, or
return [left] unchanged. *)letrecauxleft=matchStream.peekstrmwith|None->left|Someptwhenis_binoppt->(* If [pt] has a higher left binding power than the binding power of
the previous operator in the stream. *)iflbppt>rbpthen(* Performed before to execute side effect on stream. *)letnext=Stream.nextstrminaux(ledstrmleftnext)elseleft|Some_->(* argument of an application *)letnext=Stream.nextstrminletright=nudstrmnextinaux(Sup.make_applleftright)inletnext=Stream.nextstrminletleft=nudstrmnextinauxleftend