123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202(*********************************************************************************)(* OCaml-CSS *)(* *)(* Copyright (C) 2023-2024 INRIA All rights reserved. *)(* Author: Maxence Guesdon, INRIA Saclay *)(* *)(* This program is free software; you can redistribute it and/or modify *)(* it under the terms of the GNU General Public License as *)(* published by the Free Software Foundation, version 3 of the License. *)(* *)(* This program is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU General Public License for more details. *)(* *)(* You should have received a copy of the GNU General Public *)(* License along with this program; if not, write to the Free Software *)(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA *)(* 02111-1307 USA *)(* *)(* As a special exception, you have permission to link this program *)(* with the OCaml compiler and distribute executables, as long as you *)(* follow the requirements of the GNU GPL in regard to all of the *)(* software in the executable aside from the OCaml compiler. *)(* *)(* Contact: Maxence.Guesdon@inria.fr *)(* *)(*********************************************************************************)(** Utilities for parsing. *)openAngstromopenTletremove_options:'aoptionlist->'alist=funl->List.fold_right(funxacc->matchxwith|None->acc|Somex->x::acc)l[]letwith_loc:ctx->'aAngstrom.t->('a*loc)Angstrom.t=functxp->ctx.get_pos>>=funstart->p>>=funv->ctx.get_pos>>=funstop->return(v,(start,stop))letis_hex=function|'0'..'9'|'a'..'f'|'A'..'F'->true|_->falseletis_digit=function'0'..'9'->true|_->false(** Parser failing with the given message where current position is added. *)letfail_atctxmsg=letp=matchAngstrom.Unbuffered.parsectx.last_poswith|Done(_,p)->Somep|Partial_|Fail_->NoneinT.error(Parse_error(p,msg))(** [parse_error ctx e] raises a {!T.Error} with a parse error [e]
and last position of [ctx]. *)letparse_error_atctxe=ctx.last_pos>>=funp->T.error(Parse_error(Somep,e))(**/**)includeLog(**/**)letstring_of_char_listl=leta=Array.of_listlinString.init(Array.lengtha)(Array.geta)(** {2 Util parsers} *)(** [opt_ p] returns [None] if [p] fails, else return [Some r] where [r]
is the result of [p]. *)letopt_p=optionNone(p>>|funx->Somex)(** [take_char] is a parser returning [None] is there is no more char,
else accept any character [c], {!Angstrom.advance} by 1 char and returns [Some c].*)lettake_char=peek_char>>=function|None->returnNone|Somec->advance1>>=(fun_->return(Somec))(** [take_while_upto pred n] accepts at most [n] characters
or as long a [pred] returns [true]. Returns accepted characters as a string.*)lettake_while_upto=letrecfpredaccn=ifn<=0thenreturn(String.concat""(List.revacc))elsepeek_char>>=function|Somecwhenpredc->(advance1>>=function()->fpred(String.make1c::acc)(n-1))|_->return(String.concat""(List.revacc))infunpredn->fpred[]n(** Same as {!take_while_upto} but fails if no character is available. *)lettake_while1_uptopredn=take_char>>=function|None->fail""|Somec->take_while_uptopredn>>=funs->return(Printf.sprintf"%c%s"cs)(** Accept a comment. *)letcommentctx=ctx.get_pos>>=funstart_pos->Angstrom.(string"/*"*>choice[((many_tillany_char(string"*/"))>>|string_of_char_list);(peek_char>>=fun_->T.(error(Parse_error(Somestart_pos,Unterminated_comment))))])<?>"comment"letis_ws=function|'\x20'|'\x0a'|'\x0d'|'\x09'->true|_->false(*let is_ws c =
prerr_endline (Printf.sprintf "is_ws %c" c);
is_ws c
*)(** [ws ctx] accepts any number of white spaces or comments. *)letwsctx:stringAngstrom.t=many((take_while1is_ws)<|>commentctx)>>|String.concat""(*let ws ctx = skip_many ((take_while1 is_ws >>| fun _ -> ()) <|> comment ctx) >>| fun _ -> ()*)(** {2 Parser combinators} *)letmap_fstparser=(functx->Angstrom.map(parserctx)~f:fst)(** [p1 &&& p2] returns a parser succeeding when [p1] and [p2] succeed, in any order. *)let(&&&)p1p2=choice[(p1>>=funv1->p2>>|funv2->(v1,v2));(p2>>=funv2->p1>>|funv1->(v1,v2));](** [p1 ||| p2] returns a parser accepting values for [p1] or [p2] or both. *)let(|||):'aAngstrom.t->'bAngstrom.t->('aoption*'boption)Angstrom.t=funp1p2->choice[(p1>>=funv1->choice[(p2>>|funv2->(Somev1,Somev2));return(Somev1,None)]);(p2>>=funv2->choice[(p1>>|funv1->(Somev1,Somev2));return(None,Somev2)])](** [alt_1_2 def1 p1 def2 p2] is the same as {!(|||)} but returns the provided
default values for each parser. *)letalt_1_2:'a->'aAngstrom.t->'b->'bAngstrom.t->('a*'b)Angstrom.t=fundef1p1def2p2->p1|||p2>>|fun(x1,x2)->(Option.valuex1~default:def1,Option.valuex2~default:def2)(** {2 Predefined character parsers}
All these parser accept a character after optional white spaces or comments.
*)letlcharcctx=wsctx*>charcletlbracket=lchar'['letrbracket=lchar']'letlbrace=lchar'{'letrbrace=lchar'}'letlpar=lchar'('letrpar=lchar')'letdot=lchar'.'letcolon=lchar':'letsemicolon=lchar';'letampersand=lchar'&'letcomma=lchar','letdquote=lchar'"'letquote=lchar'\''letplus=lchar'+'letminus=lchar'-'letpipe=lchar'|'letsharp=lchar'#'letslash=lchar'/'letgt=lchar'>'lettilde=lchar'~'(** {2 Parsing integers and sign} *)letsignctx=choice[minusctx;plusctx]letintegerctx=(wsctx*>option'+'(signctx)>>=funsign->take_while1is_digit>>=funn->return(int_of_string(Printf.sprintf"%c%s"signn)))<?>"integer"