123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292openFmlib_stdopenInterfacesmoduleMake(State:ANY)(Token:ANY)(Expect:ANY)(Semantic:ANY)=structmoduleError=Error.Make(Expect)(Semantic)type_end=|No_end|End_received|End_consumedtypet={state:State.t;has_consumed:(* Are there consumed token? *)bool;error:Error.t;la_ptr:(* Pointer to the first lookahead token in the buffer. *)int;is_buffering:(* Are we in buffering mode? I.e. are we within a backtrackable
parser? *)bool;toks:(* Buffered token. The token from [la_ptr] to the end are lookahead
token. *)Token.tarray;_end:(* Buffered end of token stream. The end of token stream can only be
consumed once. Once in the state [End_consumed] there is no way
back. *)_end;}letinit(st:State.t):t={state=st;has_consumed=false;error=Error.init;la_ptr=0;is_buffering=false;toks=[||];_end=No_end}letstate(b:t):State.t=b.stateleterror(b:t):Error.t=b.errorletcount_toks(b:t):int=(* Number of token in the buffer. *)Array.lengthb.tokslethas_end(b:t):bool=matchb._endwith|No_end->false|_->truelethas_lookahead(b:t):bool=(* Are there lookahead token in the buffer? *)b.la_ptr<count_toksb||b._end=End_receivedletlookaheads(b:t):Token.tarray=(* An array consisting only of the lookahead token in the buffer. *)letlen=count_toksb-b.la_ptrinArray.subb.toksb.la_ptrlenletfirst_lookahead(b:t):Token.toption=(* The first lookahead token. *)assert(has_lookaheadb);ifb.la_ptr<count_toksbthenSomeb.toks.(b.la_ptr)elseNoneletpush_token(t:Token.t)(b:t):t=(* Push a new lookahead token to the buffer. *)ifb.is_buffering||has_lookaheadbthen(* In buffering mode or if they are lookahead token, the new token
is pushed to the buffer. *){bwithtoks=Array.pushtb.toks}else(* Not in buffering mode an no lookaheads. We can forget all token
in the buffer. *){bwithla_ptr=0;toks=[|t|]}letpush_end(b:t):t=assert(not(has_endb));{bwith_end=End_received}letput(state:State.t)(b:t):t={bwithstate}letupdate(f:State.t->State.t)(b:t):t=(* Update the state. *){bwithstate=fb.state}letadd_expected(e:Expect.t)(b:t):t={bwitherror=Error.add_expectedeb.error}letput_error(e:Semantic.t)(b:t):t={bwitherror=Error.make_semantice}letclear_errors(b:t):t={bwitherror=Error.init}letclear_last_error(b:t):t={bwitherror=Error.clear_lastb.error}letreset_errors(b0:t)(b:t):t={bwitherror=b0.error}letconsume(state:State.t)(b:t):t=(* Consume the first lookahead token. *)assert(has_lookaheadb);ifb.la_ptr<count_toksbthen{bwithstate;has_consumed=true;error=Error.init;la_ptr=1+b.la_ptr}elseifb._end=End_receivedthen{bwithstate;has_consumed=true;error=Error.init;_end=End_consumed}elseassertfalse(* Cannot happen. *)letreject(e:Expect.t)(b:t):t=(* Reject the first lookahead token.
The token are unchanged. The failed expectation [e] is added to the
syntax errors.
*)add_expectedebletstart_new_consumer(b:t):t={bwithhas_consumed=false}lethas_consumed(b:t):bool=b.has_consumedletend_new_consumer(b0:t)(b:t):t={bwithhas_consumed=b0.has_consumed||b.has_consumed;state=ifb.has_consumedthenb.stateelseb0.state}letstart_alternatives(b:t):t={bwithhas_consumed=false}letend_failed_alternatives(e:Expect.t)(b0:t)(b:t):t=ifb.has_consumedthenbelse{bwithhas_consumed=b0.has_consumed;error=Error.add_expectedeb0.error}letend_succeeded_alternatives(b0:t)(b:t):t=ifb.has_consumedthenbelse{bwithhas_consumed=b0.has_consumed;error=b0.error}letstart_backtrack(b:t):t=(* Start backtracking i.e. set the buffer into buffering mode.
Token have to be buffered from now on. In case of failure we treat
the consumed token as lookahead token.
*){bwithis_buffering=true}letend_backtrack_success(b0:t)(b:t):t=(* The current backtrackable parser has succeeded. *)ifb0.is_bufferingthen(* The current backtrackable parser is nested within another
backtrackable parser. Therefore no change to the buffer. *)belse(* The current backtrackable parser is not nested within another
backtrackable parser. We end buffering and forget all consumed
token. The lookahead tokens remain in the buffer. *){bwithis_buffering=false;toks=lookaheadsb;(* only lookahead token *)la_ptr=0}letend_backtrack_fail(e:Expect.toption)(b0:t)(b:t):t=(* The current backtrackable parser has failed.
Reestablish the buffer at the start of the backtrackable parser and
treat the consumed token as lookahead token (i.e. unconsume them).
*)assert(count_toksb0<=count_toksb);assert(b._end<>End_consumed);ifb0.la_ptr=b.la_ptrthen(* failed without consumption, no backtracking necessary *){bwithis_buffering=b0.is_buffering}else(* failed with consumption, backtracking necessary *){b0withtoks=b.toks;_end=b._end;error=matchewith|None->b0.error(* not_followed_by ? *)|Somee->Error.add_expectedeb0.error}end