123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697(* The compiler of the backtracking matcher. This is the port of compile.go of the ecmaregex package of the Go
module. *)openParsetypeopcode=|Op_char(** [Op_char] and [Op_set] consume one code point going forward. The back forms consume the one before the
position, which is how the body of a lookbehind runs. *)|Op_char_back|Op_set|Op_set_back|Op_lit(** Consumes a run of literal bytes going forward. *)|Op_fail|Op_bol|Op_eol|Op_word_boundary|Op_not_word_boundary|Op_line_start(** [Op_line_start] and [Op_line_end] are [^] and [$] with the m modifier. The fold forms of the word boundaries
and of the backreferences belong to a case-insensitive group. *)|Op_line_end|Op_word_boundary_fold|Op_not_word_boundary_fold|Op_jmp|Op_split(** Continues at [x] and leaves [y] as the alternative. *)|Op_save_tmp(** Notes where a group was entered. [Op_capture] records the group once it has closed. *)|Op_capture|Op_clear(** Resets the groups [a] to [b], as ECMA-262 does at the start of each iteration of a quantified body. *)|Op_backref|Op_backref_back|Op_backref_fold|Op_backref_fold_back|Op_backref_units(** [Op_backref_units] and [Op_backref_units_back] compare a group a code unit at a time, for a pattern matched by
code unit. *)|Op_backref_units_back|Op_look(** Opens a lookaround. [Op_look_ok] ends the body of a positive one and [Op_look_fail] the body of a negative
one. *)|Op_look_ok|Op_look_fail|Op_rep(** [Op_rep] and [Op_rep_lazy] repeat one character or class without entering the general loop. *)|Op_rep_lazy|Op_loop_init(** [Op_loop_init], [Op_loop] and [Op_loop_iter] run a counted or possibly empty quantified body. *)|Op_loop|Op_loop_iter|Op_matchtypequick={ascii:Bytes.t;(** The characters below 128 a way can consume next, a bit for each in 16 bytes. Nothing writes to it once
{!set_quick} has returned. *)other:bool;(** Says a way can consume a character that is not ASCII. *)mid:bool;(** Says a way reaches the end of its match consuming nothing more while text is left. *)at_end:bool;(** Says a way does at the end of the text. *)}(** What can come next on a way from an instruction to the end of its match, which is the end of the pattern or of the
lookaround the instruction is in. The matcher looks at it before it takes a way: when the next character of the text
is none that the way can start with, and the way cannot end without one, the way fails, so it is not taken, and
where it was one of two no choice is kept for it. V8 makes the same check ("quick check") at its choices. It is what
keeps a choice between ways that need different characters from being a choice. *)letquick_none={ascii=Bytes.make16'\000';other=false;mid=false;at_end=false}letquick_all={ascii=Bytes.make16'\255';other=true;mid=true;at_end=true}(** The number of loops whose state a memo point can tell apart. *)letmax_memo_loops=2typeinst={op:opcode;flag:bool;(** Marks a lazy [Op_loop], a backward [Op_capture], a negative [Op_look], and an [Op_backref_fold] that folds
case as the u flag grammar does. *)r:int;set:Charset.t;has_set:bool;(** Says [set] is in use, where the Go source tests the set for nil. *)lit:string;mutablex:int;(** [x] and [y] are jump targets. *)mutabley:int;a:int;(** [a] and [b] are a group, a loop or a range of groups, depending on the instruction. *)b:int;min:int;(** [min] and [max] bound a repetition. A max of -1 is no bound. *)max:int;mutableneed:int;(** The least number of bytes of text that every way from this instruction to a match consumes going forward (see
{!set_needs}). It is set for an [Op_loop], and counts none of the iterations the loop has still to make. *)iter:int;(** The least number of bytes one iteration of the body of an [Op_loop] consumes going forward. *)mutableq:quick;(** What can come next on a way from the instruction (see {!set_quick}). *)m:int;(** The number of the instruction's memo point, counted from 1, or 0 when it has none. It is set for an
[Op_split], an [Op_loop], an [Op_rep] and an [Op_rep_lazy].
A memo point is a place where the matcher makes a choice and can remember that the choice has failed: a split,
a loop that may be entered or left, and what follows a single character loop that gives characters back or
takes more. Whether a way from such a place leads to a match depends on the place and the position alone when
nothing else the matcher keeps can change what follows. So a pattern with a backreference has no memo points
(what a group holds changes what follows), and neither has a place inside a loop that counts its iterations up
to a bound. Inside a loop with no upper bound and a least count of zero or one, one thing more decides what
follows: whether the loop would refuse the iteration as empty if it ended here. That is one bit for each such
loop the place is in, and a place in more than [max_memo_loops] of them has no memo point. *)ml0:int;(** [ml0] and [ml1] are the loops around the memo point, outermost first, or -1, and [mm0] and [mm1] the least
count of each. *)ml1:int;mm0:int;mm1:int;}(** One instruction of the backtracking matcher. The Go source holds the jump targets, the groups and the bounds in 32
bits. Here each is an [int]. *)letno_inst={op=Op_fail;flag=false;r=0;set=Charset.none;has_set=false;lit="";x=0;y=0;a=0;b=0;min=0;max=0;need=0;iter=0;q=quick_none;m=0;ml0=-1;ml1=-1;mm0=0;mm1=0}[@@ocamlformat"disable"]typeprogram={insts:instarray;groups:int;(** The number of capturing groups some backreference names. *)loops:int;(** The number of counted loops. *)units:bool;(** Says the pattern was read without the u flag, so the text is matched by UTF-16 code unit (see utf8.ml). *)anchored:bool;(** Says every match starts at the start of the text. *)need:int;(** The least number of bytes a match consumes (see {!set_needs}). *)need_passes:int;(** The passes over the instructions that {!set_needs} made, the last of which changed nothing. *)points:int;(** The number of memo points of the program (see [m] of an instruction). *)quick_passes:int;(** The passes over the instructions that {!set_quick} made, the last of which changed nothing. *)prefix:string;(** Literal text every match starts with. *)first:Charset.t;(** Every code point a match can start with. It is in use when [has_first] is set, which it is not when a match
can be empty or when the set is not known. *)has_first:bool;}(** A pattern compiled for the backtracking matcher. Nothing writes to it once it is made. The Go source keeps a pool of
working memory in the program. Here the working memory belongs to the caller of the match (see vm.ml). *)typecompiler={mutablecode:instarray;(** The instructions, of which the first [count] are in use. *)mutablecount:int;tracked:intarray;(** The slot of each capturing group by its number, for the groups some backreference names, and -1 for the
others, which are not recorded at all. *)mutableloop_count:int;unicode_mode:bool;(** Says the pattern was read with the u flag grammar, which decides how a backreference folds case. *)memo:bool;(** Says the pattern has no backreference, so its choices can have memo points. *)mutableopen_loops:(int*int*bool)list;(** The counted loops around the instruction being written, innermost first, inside the innermost lookaround (what
a lookaround's body does depends on nothing outside it): the number of each, its least count, and whether its
state is one bit to a memo point (no upper bound, and a least count of zero or one). *)mutablepoint_count:int;}(* Returns an instruction with the number of a new memo point for a place here, or the instruction as it is when a
choice here cannot be remembered. *)letwith_pointcinst=letloops=List.revc.open_loopsinif(notc.memo)||List.lengthloops>max_memo_loops||List.exists(fun(_,_,simple)->notsimple)loopstheninstelsebeginc.point_count<-c.point_count+1;letloopk=matchList.nth_optloopskwithSome(l,_,_)->l|None->-1inletleastk=matchList.nth_optloopskwithSome(_,m,_)->m|None->0in{instwithm=c.point_count;ml0=loop0;ml1=loop1;mm0=least0;mm1=least1}end(** Reports whether n can match one character or more. It errs towards true. *)letrecconsumesn=matchn.kindwith|Char|Set|Backref->true|Cat|Alt|Group->Array.existsconsumesn.subs|Repeat->n.max<>0&&consumesn.subs.(0)|_->false(** [simplify n inside] returns the tree with two rewrites, both of which V8 makes. Neither changes what the pattern
matches or what any group holds. Without them a pattern can take time exponential in its counts for no reason that
ECMA-262 gives. [inside] is the capturing groups n is inside of. The tree is a value nothing writes to, so a node
that changes is a new node.
A backreference to a group it is inside of is the empty string. The group is not set until it closes, and the
repetition around a group clears it at the start of each iteration, so inside the group it is never set.
A repetition whose body cannot consume a character, such as [(?:\b|(?=a)|){4,5}], is the body once when its minimum
is one or more, and nothing when its minimum is zero. Every iteration of such a body is empty. ECMA-262 refuses an
empty iteration after the minimum, so the iterations are exactly the minimum. Each of them starts with the groups of
the body cleared and at the same position, so what the last one can do does not depend on the ones before it, and
the ways through the last one come in the same order whatever the others chose. Run as written, the matcher would
try every combination of the ways through every iteration. *)letrecsimplifyninside=ifn.kind=BackrefthenifArray.for_all(fung->List.memginside)n.groupsthenempty_nodeelsenelseifArray.lengthn.subs=0thennelseletinside=ifn.kind=Group&&n.group>0thenn.group::insideelseinsideinletsubs=Array.map(funsub->simplifysubinside)n.subsinletout=ifArray.for_all2(==)subsn.substhennelse{nwithsubs}inifout.kind=Repeat&&out.max<>0&¬(consumesout.subs.(0))thenifout.min=0thenempty_nodeelseout.subs.(0)elseoutletreccollect_backrefsnseen=ifn.kind=BackrefthenArray.iter(fung->seen.(g)<-true)n.groups;Array.iter(funsub->collect_backrefssubseen)n.subsletaddcinst=ifc.count=Array.lengthc.codethenbeginletgrown=Array.make((2*c.count)+16)no_instinArray.blitc.code0grown0c.count;c.code<-grownend;c.code.(c.count)<-inst;c.count<-c.count+1;c.count-1letpcc=c.countletslotcgroup=ifgroup>0&&group<Array.lengthc.trackedthenc.tracked.(group)else-1(* Returns the first and last slot of the tracked groups inside n, or (-1, -1) when it has none. *)letgroup_rangecn=letlo=ref(-1)andhi=ref(-1)inletrecwalkn=ifn.kind=Group&&n.group>0thenbeginlett=slotcn.groupinift>=0thenbeginif!lo<0||t<!lothenlo:=t;ift>!hithenhi:=tendend;Array.iterwalkn.subsinwalkn;(!lo,!hi)(** Reports whether a code point can be matched by comparing its UTF-8 bytes. A surrogate has no encoding, and U+FFFD is
also what a malformed byte of the text decodes to. *)letliteral_runer=Utf8.valid_runer&&r<>Utf8.rune_error(** The need of an instruction from which no way leads to a match. Every need is capped at it. *)letno_match=1lsl30(** Returns count times need, capped at [no_match]. *)lettimes_needcountneed=ifcount=0||need=0then0elseifneed>=no_match||count>=no_match/needthenno_matchelsecount*need(** Returns the least number of bytes a match of n consumes, capped at [no_match]. Like the needs, it counts a character
as its UTF-8 bytes when the matcher compares bytes for it and as one byte otherwise, so it can only be too small. *)letrecmin_lengthn=matchn.kindwith|Char->ifliteral_runen.rthenUtf8.rune_lengthn.relse1|Set->ifCharset.is_emptyn.setthenno_matchelse1|Cat->letrecsumitotal=ifi=Array.lengthn.substhentotalelselettotal=total+min_lengthn.subs.(i)iniftotal>=no_matchthenno_matchelsesum(i+1)totalinsum00|Alt->Array.fold_left(funleastsub->minleast(min_lengthsub))no_matchn.subs|Group->min_lengthn.subs.(0)|Repeat->times_needn.min(min_lengthn.subs.(0))|_->0(** Reports whether n can match without consuming anything. It errs towards true. *)letrecnullablen=matchn.kindwith|Char|Set->false|Cat->Array.for_allnullablen.subs|Alt->Array.existsnullablen.subs|Group->nullablen.subs.(0)|Repeat->n.min=0||nullablen.subs.(0)|_->true(* Writes the instructions for n. With back set the instructions match right to left, as the body of a lookbehind
must. *)letrecemitcnback=matchn.kindwith|Empty->()|Char->ignore(addc{no_instwithop=(ifbackthenOp_char_backelseOp_char);r=n.r})|Set->ifCharset.is_emptyn.setthenignore(addc{no_instwithop=Op_fail})elseignore(addc{no_instwithop=(ifbackthenOp_set_backelseOp_set);set=n.set;has_set=true})|Cat->ifbackthenfori=Array.lengthn.subs-1downto0doemitcn.subs.(i)truedoneelsebeginletcount=Array.lengthn.subsinleti=ref0inwhile!i<countdo(* A run of literal characters becomes one comparison of bytes. *)letj=ref!iinletlit=Buffer.create16inwhile!j<count&&n.subs.(!j).kind=Char&&literal_runen.subs.(!j).rdoUtf8.add_runelitn.subs.(!j).r;incrjdone;if!j-!i>=2thenbeginignore(addc{no_instwithop=Op_lit;lit=Buffer.contentslit});i:=!jendelsebeginemitcn.subs.(!i)false;incrienddoneend|Alt->letjumps=ref[]inletlast=Array.lengthn.subs-1inArray.iteri(funisub->ifi=lastthenemitcsubbackelsebeginletsplit=addc(with_pointc{no_instwithop=Op_split})inc.code.(split).x<-pcc;emitcsubback;jumps:=addc{no_instwithop=Op_jmp}::!jumps;c.code.(split).y<-pccend)n.subs;List.iter(funj->c.code.(j).x<-pcc)!jumps|Group->lett=slotcn.groupinift<0thenemitcn.subs.(0)backelsebeginignore(addc{no_instwithop=Op_save_tmp;a=t});emitcn.subs.(0)back;ignore(addc{no_instwithop=Op_capture;a=t;flag=back})end|Bol->ignore(addc{no_instwithop=Op_bol})|Eol->ignore(addc{no_instwithop=Op_eol})|Line_start->ignore(addc{no_instwithop=Op_line_start})|Line_end->ignore(addc{no_instwithop=Op_line_end})|Word_boundary->ignore(addc{no_instwithop=(ifn.foldthenOp_word_boundary_foldelseOp_word_boundary)})|Not_word_boundary->ignore(addc{no_instwithop=(ifn.foldthenOp_not_word_boundary_foldelseOp_not_word_boundary)})|Backref->letop=ifn.fold&&backthenOp_backref_fold_backelseifn.foldthenOp_backref_foldelseifbackthenifc.unicode_modethenOp_backref_backelseOp_backref_units_backelseifc.unicode_modethenOp_backrefelseOp_backref_unitsin(* When several groups share the name, at most one has taken part, and the others match the empty string. So
matching each in turn matches the one that took part. *)Array.iter(fung->ignore(addc{no_instwithop;a=slotcg;flag=c.unicode_mode}))n.groups|Look->letlook=addc{no_instwithop=Op_look;flag=n.negative}in(* Whether the body matches depends on nothing outside it. *)letouter=c.open_loopsinc.open_loops<-[];emitcn.subs.(0)n.behind;c.open_loops<-outer;ignore(addc{no_instwithop=(ifn.negativethenOp_look_failelseOp_look_ok)});c.code.(look).x<-pcc|Repeat->emit_repeatcnbackandemit_repeatcnback=letbody=n.subs.(0)inifn.max<>0thenif(notback)&&(body.kind=Char||(body.kind=Set&¬(Charset.is_emptybody.set)))thenignore(addc(with_pointc{no_instwithop=(ifn.lazy_thenOp_rep_lazyelseOp_rep);r=body.r;set=body.set;has_set=body.kind=Set;min=n.min;max=n.max;}))elsebeginletclear_lo,clear_hi=group_rangecbodyinletemit_body()=ifclear_lo>=0thenignore(addc{no_instwithop=Op_clear;a=clear_lo;b=clear_hi});emitcbodybackin(* split writes a choice between entering the body and leaving the loop, in the order the quantifier prefers. *)letsplit()=addc(with_pointc{no_instwithop=Op_split})inletset_splitatenterleave=ifn.lazy_thenbeginc.code.(at).x<-leave;c.code.(at).y<-enterendelsebeginc.code.(at).x<-enter;c.code.(at).y<-leaveendin(* A body that always consumes something needs no check for empty iterations, so the unbounded forms and the
optional form are plain choices. *)letplain=not(nullablebody)inifplain&&n.min=0&&n.max=unboundedthenbeginletat=split()inletenter=pccinemit_body();ignore(addc{no_instwithop=Op_jmp;x=at});set_splitatenter(pcc)endelseifplain&&n.min=1&&n.max=unboundedthenbeginletenter=pccinemit_body();letat=split()inset_splitatenter(pcc)endelseifplain&&n.min=0&&n.max=1thenbeginletat=split()inletenter=pccinemit_body();set_splitatenter(pcc)endelsebeginletk=c.loop_countinc.loop_count<-c.loop_count+1;ignore(addc{no_instwithop=Op_loop_init;a=k});letiter=ifbackthen0elsemin_lengthbodyinletsimple=n.max=unbounded&&n.min<=1inletloop_inst={no_instwithop=Op_loop;a=k;min=n.min;max=n.max;flag=n.lazy_;iter}in(* The loop is left or entered from here, and what follows either depends on the loops around this one. *)letloop=addc(ifsimplethenwith_pointcloop_instelseloop_inst)inignore(addc{no_instwithop=Op_loop_iter;a=k});c.open_loops<-(k,n.min,simple)::c.open_loops;emit_body();c.open_loops<-List.tlc.open_loops;ignore(addc{no_instwithop=Op_jmp;x=loop});c.code.(loop).x<-pccendend(* Reports whether every way through n begins with ^. *)letrecstarts_at_boln=matchn.kindwith|Bol->true|Cat->starts_at_boln.subs.(0)|Alt->Array.for_allstarts_at_boln.subs|Group->starts_at_boln.subs.(0)|Repeat->n.min>=1&&starts_at_boln.subs.(0)|_->false(* Appends the literal text every match of n starts with. *)letrecliteral_prefixnprefix=matchn.kindwith|Char->ifliteral_runen.rthenUtf8.add_runeprefixn.r|Cat->letreceachi=ifi<Array.lengthn.substhenletsub=n.subs.(i)inifsub.kind<>Char||not(literal_runesub.r)thenbeginifBuffer.lengthprefix=0&&sub.kind=Groupthenliteral_prefixsubprefixendelsebeginUtf8.add_runeprefixsub.r;each(i+1)endineach0|Group->literal_prefixn.subs.(0)prefix|_->()exceptionUnknown_first(* Appends, as inclusive pairs, the code points a match of n can start with, and reports whether n can match the
empty string. It raises Unknown_first when the set is not known. Assertions are treated as matching the empty
string, which can only make the set larger than it need be. *)letrecfirst_charsnpairs=matchn.kindwith|Char->pairs:=[|n.r;n.r|]::!pairs;false|Set->pairs:=n.set.ranges::!pairs;false|Cat->letreceachi=ifi=Array.lengthn.substhentrueelseiffirst_charsn.subs.(i)pairstheneach(i+1)elsefalseineach0|Alt->Array.fold_left(funemptysub->first_charssubpairs||empty)falsen.subs|Group->first_charsn.subs.(0)pairs|Repeat->ifn.max=0thentrueelseletempty=first_charsn.subs.(0)pairsinempty||n.min=0|Backref->raiseUnknown_first|_->true(** [set_needs insts] works out, for every instruction, the least number of bytes of text that any way from it to a
match consumes going forward, and writes it into the counted loops. It returns the need of the whole program, and
the number of passes it made. The matcher gives up at a counted loop when fewer bytes than that are left, as V8 does
at every choice. A loop whose iterations can be empty would otherwise try every combination of its ways at a
position from which the rest of the pattern cannot match at all.
A counted loop that is entered needs its least count of iterations, each of them the least its body consumes
([iter]), and then what follows it. The loop instruction itself is also reached after each iteration, so its own
need counts only what follows, and the matcher adds the iterations it has still to make.
A character is counted as one byte, a literal run as its bytes and a single character loop as its minimum, so the
figure can only be too small. The body of a lookaround ends at its own last instruction and the instruction that
opens one goes straight to what follows it, so a body is counted by itself. An instruction that matches right to
left, which is the body of a lookbehind, counts nothing. *)letset_needsinsts=letcount=Array.lengthinstsinletneed=Array.makecountno_matchin(* The needs only fall, and nearly every way goes forward, so going through the instructions from the last to the
first settles them in one pass, and a second pass finds nothing to change. The one way that goes back is the jump
at the end of the body of a counted loop, to the loop instruction. The need of that instruction is the need of
what follows the loop, since the other way from it, through the body, comes back to it and cannot need less. So
the jump takes the need of what follows the loop, which this pass has settled already. The jump at the end of the
body of a plain loop goes back to a choice between the body and what follows, and takes the lesser of the two in
the same way. Taking the need of the
loop instruction, which this pass has not reached, would settle one loop of a nest for each pass: a thousand
passes for loops a thousand deep. *)letnexttargetweight=need.(target)+weightinletchanged=reftrueandpasses=ref0inwhile!changeddochanged:=false;incrpasses;forpc=count-1downto0doleti=insts.(pc)inletn=matchi.opwith|Op_match|Op_look_ok|Op_look_fail->0|Op_fail->no_match|Op_char|Op_set->next(pc+1)1|Op_lit->next(pc+1)(String.lengthi.lit)|Op_rep|Op_rep_lazy->next(pc+1)i.min|Op_jmpwheni.x<pc&&insts.(i.x).op=Op_loop->nextinsts.(i.x).x0|Op_jmpwheni.x<pc&&insts.(i.x).op=Op_split->min(nextinsts.(i.x).x0)(nextinsts.(i.x).y0)|Op_jmp|Op_look->nexti.x0|Op_split->min(nexti.x0)(nexti.y0)|Op_loop->min(next(pc+1)0)(nexti.x0)|Op_loop_init->letloop=insts.(pc+1)innext(pc+1)(times_needloop.minloop.iter)|_->next(pc+1)0inletn=minnno_matchinifn<need.(pc)thenbeginneed.(pc)<-n;changed:=trueenddonedone;Array.iteri(funpci->ifi.op=Op_looptheni.need<-need.(pc))insts;(need.(0),!passes)letquick_unionab={ascii=Bytes.init16(funk->Char.chr(Char.code(Bytes.geta.asciik)lorChar.code(Bytes.getb.asciik)));other=a.other||b.other;mid=a.mid||b.mid;at_end=a.at_end||b.at_end;}letquick_interab={ascii=Bytes.init16(funk->Char.chr(Char.code(Bytes.geta.asciik)landChar.code(Bytes.getb.asciik)));other=a.other&&b.other;mid=a.mid&&b.mid;at_end=a.at_end&&b.at_end;}letquick_runer=ifr<Utf8.rune_selfthenbeginletascii=Bytes.make16'\000'inBytes.setascii(rlsr3)(Char.chr(1lsl(rland7)));{quick_nonewithascii}endelse{quick_nonewithother=true}letquick_set(set:Charset.t)=letn=Array.lengthset.rangesin{quick_nonewithascii=Bytes.of_stringset.ascii;other=n>0&&set.ranges.(n-1)>=Utf8.rune_self}letquick_equalab=Bytes.equala.asciib.ascii&&a.other=b.other&&a.mid=b.mid&&a.at_end=b.at_end(** [set_quick insts] works out the quick of every instruction, and returns the number of passes it made. Every figure
is a set that can only be too large: an assertion is taken to hold, a backreference and an instruction that matches
right to left (the body of a lookbehind) to allow anything. A positive lookahead needs both its body and what
follows it to go on from the same position, so it allows what both allow. *)letset_quickinsts=letcount=Array.lengthinstsin(* The sets only grow, and nearly every way goes forward, so the instructions are gone through from the last to the
first until a pass changes nothing. The one way that goes back is the jump at the end of the body of a loop, to
the instruction at its head, which the pass has not reached. Taking what that instruction had after the pass
before would settle one loop of a nest for each pass: a thousand passes for loops a thousand deep. The head of a
loop allows what follows the loop, which this pass has settled, and what its body starts with. So the jump takes
what follows the loop too. Then the first pass settles the head of every loop, since all it still lacks at the
jump is a part of itself, the second settles every instruction, and the third finds nothing to change, whatever
the depth. *)letchanged=reftrueandpasses=ref0inwhile!changeddochanged:=false;incrpasses;forpc=count-1downto0doleti=insts.(pc)inletqk=insts.(k).qinletn=matchi.opwith|Op_match|Op_look_ok|Op_look_fail->quick_all|Op_fail->quick_none|Op_char->quick_runei.r|Op_set->quick_seti.set|Op_lit->quick_rune(Char.code(String.geti.lit0))|Op_rep|Op_rep_lazy->letown=ifi.has_setthenquick_seti.setelsequick_runei.rinifi.min=0thenquick_unionown(q(pc+1))elseown|Op_jmpwheni.x<pc&&insts.(i.x).op=Op_loop->quick_union(qi.x)(qinsts.(i.x).x)|Op_jmpwheni.x<pc&&insts.(i.x).op=Op_split->quick_union(qi.x)(quick_union(qinsts.(i.x).x)(qinsts.(i.x).y))|Op_jmp->qi.x|Op_split->quick_union(qi.x)(qi.y)|Op_loop->quick_union(q(pc+1))(qi.x)|Op_eol->{quick_nonewithat_end=(q(pc+1)).at_end}|Op_look->ifi.flagthenqi.xelsequick_inter(qi.x)(q(pc+1))|Op_char_back|Op_set_back|Op_backref|Op_backref_back|Op_backref_fold|Op_backref_fold_back|Op_backref_units|Op_backref_units_back->quick_all|_->q(pc+1)inifnot(quick_equalni.q)thenbegini.q<-n;changed:=trueenddonedone;!passes(** The most bytes of the literal text a match starts with that the search for a match looks for. *)letmax_prefix=16(** Compiles the tree the parser read for the backtracking matcher. [group_count] is the number of capturing groups of
the pattern. *)letcompile_programrootgroup_countunicode_mode=letroot=simplifyroot[]inletseen=Array.make(group_count+1)falseincollect_backrefsrootseen;(* Slots follow group order, so the groups inside any one subtree take a contiguous range of slots. *)lettracked=Array.make(group_count+1)(-1)inletslots=ref0inforg=1togroup_countdoifseen.(g)thenbegintracked.(g)<-!slots;incrslotsenddone;letc={code=[||];count=0;tracked;loop_count=0;unicode_mode;memo=!slots=0;open_loops=[];point_count=0;}inemitcrootfalse;ignore(addc{no_instwithop=Op_match});letanchored=starts_at_bolrootinletprefix=Buffer.create16inifnotanchoredthenliteral_prefixrootprefix;letfirst=ifanchored||Buffer.lengthprefix>0thenNoneelseletpairs=ref[]inmatchfirst_charsrootpairswith|false->Some(Charset.make(Array.concat!pairs)false)|true->None|exceptionUnknown_first->Noneinletinsts=Array.subc.code0c.countinletneed,need_passes=set_needsinstsinletquick_passes=set_quickinstsin{insts;groups=!slots;loops=c.loop_count;units=notunicode_mode;anchored;need;need_passes;points=c.point_count;quick_passes;(* The search looks for the prefix at every position of the text, a byte at a time, so only its first bytes are
used: a long prefix that nearly matches everywhere would cost its length at each position, outside the steps
the matcher counts. The matcher compares the rest when it runs. *)prefix=Buffer.subprefix0(min(Buffer.lengthprefix)max_prefix);first=(matchfirstwithSomeset->set|None->Charset.none);has_first=first<>None;}