Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file input.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352(*
======================================================================
Copyright Christophe Raffalli & Rodolphe Lepigre
LAMA, UMR 5127 CNRS, Université Savoie Mont Blanc
christophe.raffalli@univ-savoie.fr
rodolphe.lepigre@univ-savoie.fr
This software contains a parser combinator library for the OCaml lang-
uage. It is intended to be used in conjunction with pa_ocaml (an OCaml
parser and syntax extention mechanism) to provide a fully-integrated
way of building parsers using an extention of OCaml's syntax.
This software is governed by the CeCILL-B license under French law and
abiding by the rules of distribution of free software. You can use,
modify and/or redistribute the software under the terms of the CeCILL-
B license as circulated by CEA, CNRS and INRIA at the following URL.
http://www.cecill.info
As a counterpart to the access to the source code and rights to copy,
modify and redistribute granted by the license, users are provided
only with a limited warranty and the software's author, the holder of
the economic rights, and the successive licensors have only limited
liability.
In this respect, the user's attention is drawn to the risks associated
with loading, using, modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean that it is complicated to manipulate, and that also
therefore means that it is reserved for developers and experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards
their requirements in conditions enabling the security of their sys-
tems and/or data to be ensured and, more generally, to use and operate
it in the same conditions as regards security.
The fact that you are presently reading this means that you have had
knowledge of the CeCILL-B license and that you accept its terms.
======================================================================
*)typeline={is_eof:bool(* Has the end of the buffer been reached? *);lnum:int(* Line number (startig at 1) *);loff:int(* Offset to the line *);llen:int(* Length of the line *);data:string(* Contents of the line *);mutablenext:buffer(* Following line *);name:string(* The name of the buffer (e.g. file name) *);uid:int(* Unique identifier *);ctnr:Container.t}(* for map table *)andbuffer=lineLazy.t(* Generate a unique identifier. *)letnew_uid=letc=ref0infun()->letuid=!cinincrc;uid(* Emtpy buffer. *)letempty_buffernamelnumloff=letrecline=lazy{is_eof=true;name;lnum;loff;llen=0;data="";next=line;uid=new_uid();ctnr=Container.create()}inline(* Test if a buffer is empty. *)letrecis_empty(lazyl)pos=ifpos<l.llenthenfalseelseifpos=0thenl.is_eofelseis_emptyl.next(pos-l.llen)(* Read the character at the given position in the given buffer. *)letrecread(lazylasb)i=ifl.is_eofthen('\255',b,0)elsematchcompare(i+1)l.llenwith|-1->(l.data.[i],b,i+1)|0->(l.data.[i],l.next,0)|_->readl.next(i-l.llen)(* Get the character at the given position in the given buffer. *)letrecget(lazyl)i=ifl.is_eofthen'\255'elseifi<l.llenthenl.data.[i]elsegetl.next(i-l.llen)(* Get the name of a buffer. *)letfilename(lazyb)=b.name(* Get the current line number of a buffer. *)letline_num(lazyb)=b.lnum(* Get the offset of the current line in the full buffer. *)letline_offset(lazyb)=b.loff(* Get the current line as a string. *)letline(lazyb)=b.data(* Get the length of the current line. *)letline_length(lazyb)=b.llen(* Get the utf8 column number corresponding to the given position. *)letutf8_col_num(lazy{data;_})i=letrecfindnumpos=ifpos<ithenletcc=Char.codedata.[pos]inifcclsr7=0thenfind(num+1)(pos+1)elseif(cclsr6)land1=0then-1else(* Invalid utf8 character *)if(cclsr5)land1=0thenfind(num+1)(pos+2)elseif(cclsr4)land1=0thenfind(num+1)(pos+3)elseif(cclsr3)land1=0thenfind(num+1)(pos+4)else-0(* Invalid utf8 character. *)elsenuminfind00(* Ensure that the given position is in the current line. *)letrecnormalize(lazybasstr)pos=ifpos>=b.llenthenifb.is_eofthen(str,0)elsenormalizeb.next(pos-b.llen)else(str,pos)(* Equality of buffers. *)letbuffer_equal(lazyb1)(lazyb2)=b1.uid=b2.uid(* Comparison of buffers. *)letbuffer_compare(lazyb1)(lazyb2)=b1.uid-b2.uid(* Get the unique identifier of the buffer. *)letbuffer_uid(lazybuf)=buf.uidmoduletypeMinimalInput=sigvalfrom_fun:('a->unit)->string->('a->string)->'a->bufferend(* The following code has been borrowed from OCaml's “pervasives.ml” file of
the standard library. This version preserves the newline in the output. *)externalunsafe_input:in_channel->bytes->int->int->int="caml_ml_input"externalinput_scan_line:in_channel->int="caml_ml_input_scan_line"letinput_linech=letrecbuild_resultbufpos=function|[]->buf|hd::tl->letlen=Bytes.lengthhdinBytes.blithd0buf(pos-len)len;build_resultbuf(pos-len)tlinletrecscanacculen=letn=input_scan_linechinifn=0then(* n = 0: we are at EOF *)matchaccuwith|[]->raiseEnd_of_file|_->build_result(Bytes.createlen)lenaccuelseifn>0then(* n > 0: newline found in buffer *)letres=Bytes.createninignore(unsafe_inputchres0n);matchaccuwith|[]->res|_->letlen=len+ninbuild_result(Bytes.createlen)len(res::accu)else(* n < 0: newline not found *)letbeg=Bytes.create(-n)inignore(unsafe_inputchbeg0(-n));scan(beg::accu)(len-n)inBytes.to_string(scan[]0)moduleGenericInput(M:MinimalInput)=structincludeMletfrom_channel:?filename:string->in_channel->buffer=fun?(filename="")ch->from_funignorefilenameinput_linechletfrom_file:string->buffer=funfname->from_funclose_infnameinput_line(open_infname)letfrom_string:?filename:string->string->buffer=fun?(filename="")str->letget_string_line(str,p)=letlen=String.lengthstrinletstart=!pinifstart>=lenthenraiseEnd_of_file;while(!p<len&&str.[!p]<>'\n')doincrpdone;if!p<lenthenincrp;letlen'=!p-startinString.substrstartlen'infrom_funignorefilenameget_string_line(str,ref0)endincludeGenericInput(structletfrom_funfinalisenameget_linefile=letrecfnnamelnumloffcont=letlnum=lnum+1inbegin(* Tail rec exception trick to avoid stack overflow. *)tryletdata=get_linefileinletllen=String.lengthdatainfun()->{is_eof=false;lnum;loff;llen;data;name;next=lazy(fnnamelnum(loff+llen)cont);uid=new_uid();ctnr=Container.create()}withEnd_of_file->finalisefile;fun()->contnamelnumloffend()inlazybeginletcontnamelnumloff=Lazy.force(empty_buffernamelnumloff)infnname00contendend)(* Exception to be raised on errors in custom preprocessors. *)exceptionPreprocessor_errorofstring*stringletpp_error:typea.string->string->a=funnamemsg->raise(Preprocessor_error(name,msg))moduletypePreprocessor=sigtypestatevalinitial_state:statevalupdate:state->string->int->string->state*string*int*boolvalcheck_final:state->string->unitendmoduleMake(PP:Preprocessor)=structletfrom_funfinalisenameget_linefile=letrecfnnamelnumloffstcont=letlnum=lnum+1inbegin(* Tail rec exception trick to avoid stack overflow. *)tryletdata=get_linefileinlet(st,name,lnum,take)=PP.updatestnamelnumdatainiftakethenletllen=String.lengthdatainfun()->{is_eof=false;lnum;loff;llen;data;name;next=lazy(fnnamelnum(loff+llen)stcont);uid=new_uid();ctnr=Container.create()}elsefun()->fnnamelnumloffstcontwithEnd_of_file->finalisefile;fun()->contnamelnumloffstend()inlazybeginletcontnamelnumloffst=PP.check_finalstname;Lazy.force(empty_buffernamelnumloff)infnname00PP.initial_statecontendendmoduleWithPP(PP:Preprocessor)=GenericInput(Make(PP))letleq_buf{uid=ident1;_}i1{uid=ident2;_}i2=(ident1=ident2&&i1<=i2)||ident1<ident2letbuffer_beforeb1i1b2i2=leq_buf(Lazy.forceb1)i1(Lazy.forceb2)i2(** First kind of table: association list in file order
(first position in the beginning *)moduleOrdTbl=structtype'at=(line*int*'alist)listletempty=[]letaddbufposxtbl=letbuf=Lazy.forcebufinletrecfnacc=function|[]->List.rev_appendacc[(buf,pos,[x])]|((buf',pos',yasc)::rest)astbl->ifpos=pos'&&buf.uid=buf'.uidthenList.rev_appendacc((buf',pos',(x::y))::rest)elseifleq_bufbufposbuf'pos'thenList.rev_appendacc((buf,pos,[x])::tbl)elsefn(c::acc)restinfn[]tblletpop=function|[]->raiseNot_found|(buf,pos,l)::rest->Lazy.from_valbuf,pos,l,restletis_emptytbl=tbl=[]letiterbuffn=List.iter(fun(_,_,l)->List.iterfnl)bufend(** Second kind of table: unordered, but imperative and more efficient *)moduleTbl=structtype'at='aoptionarrayContainer.tableletcreate=Container.create_tableletaddtblbufposx=letbuf=Lazy.forcebufintryleta=Container.findtblbuf.ctnrina.(pos)<-SomexwithNot_found->leta=Array.make(buf.llen+1)Noneina.(pos)<-Somex;Container.addtblbuf.ctnraletfindtblbufpos=letbuf=Lazy.forcebufinleta=Container.findtblbuf.ctnrinmatcha.(pos)with|None->raiseNot_found|Somex->xletclear=Container.clearletiter:typea.at->(a->unit)->unit=funtblf->letopenContainerinletfn:aoptionarray->unit=funa->Array.iter(functionNone->()|Somex->fx)ain(* FIXME: https://caml.inria.fr/mantis/view.php?id=7636 *)iter{Container.f=Obj.magicfn}tbl(* Tests for the above FIXME: the type is not abstract ! *)(*
let test1 : type a b. (a, b) Container.elt -> a = fun x -> x
let test2 : type a b. a -> (a, b) Container.elt = fun x -> x
*)end