123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161(******************************************************************************)(* *)(* Menhir *)(* *)(* Copyright Inria. All rights reserved. This file is distributed under *)(* the terms of the GNU Library General Public License version 2, with a *)(* special exception on linking, as described in the file LICENSE. *)(* *)(******************************************************************************)(* -------------------------------------------------------------------------- *)(* A two-place buffer stores zero, one, or two elements. *)type'acontent=|Zero|Oneof'a|Twoof'a*(* most recent: *)'atype'abuffer='acontentref(* [update buffer x] pushes [x] into [buffer], causing the buffer to slide. *)letupdatebufferx=buffer:=match!buffer,xwith|Zero,_->Onex|Onex1,x2|Two(_,x1),x2->Two(x1,x2)letshowfbuffer:string=match!bufferwith|Zero->(* The buffer cannot be empty. If we have read no tokens,
we cannot have detected a syntax error. *)assertfalse|Oneinvalid->(* It is unlikely, but possible, that we have read just one token. *)Printf.sprintf"before '%s'"(finvalid)|Two(valid,invalid)->(* In the most likely case, we have read two tokens. *)Printf.sprintf"after '%s' and before '%s'"(fvalid)(finvalid)letlastbuffer=match!bufferwith|Zero->(* The buffer cannot be empty. If we have read no tokens,
we cannot have detected a syntax error. *)assertfalse|Oneinvalid|Two(_,invalid)->invalidopenLexingletwraplexer=letbuffer=refZeroinbuffer,funlexbuf->lettoken=lexerlexbufinupdatebuffer(lexbuf.lex_start_p,lexbuf.lex_curr_p);tokenletwrap_suppliersupplier=letbuffer=refZeroinbuffer,fun()->let(_token,pos1,pos2)astriple=supplier()inupdatebuffer(pos1,pos2);triple(* -------------------------------------------------------------------------- *)letextracttext(pos1,pos2):string=letofs1=pos1.pos_cnumandofs2=pos2.pos_cnuminletlen=ofs2-ofs1intryString.subtextofs1lenwithInvalid_argument_->(* In principle, this should not happen, but if it does, let's make this
a non-fatal error. *)"???"letsanitizetext=String.map(func->ifChar.codec<32then' 'elsec)text(* If we were willing to depend on [Str], we could implement [compress] as
follows:
let compress text =
Str.global_replace (Str.regexp "[ \t\n\r]+") " " text
*)letreccompressnbijskipping=ifj<nthenletc,j=Bytes.getbj,j+1inmatchcwith|' '|'\t'|'\n'|'\r'->leti=ifnotskippingthen(Bytes.setbi' ';i+1)elseiinletskipping=trueincompressnbijskipping|_->leti=Bytes.setbic;i+1inletskipping=falseincompressnbijskippingelseBytes.sub_stringb0iletcompresstext=letb=Bytes.of_stringtextinletn=Bytes.lengthbincompressnb00falseletshortenktext=letn=String.lengthtextinifn<=2*k+3thentextelseString.subtext0k^"..."^String.subtext(n-k)kletis_digitc=letc=Char.codecinChar.code'0'<=c&&c<=Char.code'9'exceptionCopyletexpandftext=letn=String.lengthtextinletb=Buffer.createninletrecloopi=ifi<nthenbeginletc,i=text.[i],i+1inloop(tryifc<>'$'thenraiseCopy;letj=refiinwhile!j<n&&is_digittext.[!j]doincrjdone;ifi=!jthenraiseCopy;letk=int_of_string(String.subtexti(!j-i))inBuffer.add_stringb(fk);!jwithCopy->(* We reach this point if either [c] is not '$' or [c] is '$'
but is not followed by an integer literal. *)Buffer.add_charbc;i)endelseBuffer.contentsbinloop0