123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117(*
* Copyright (c) 2018 Thomas Gazagnaire <thomas@gazagnaire.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)openMdx.CompatmodulePart=structtypet={name:string;sep_indent:string;(** Whitespaces before the [@@@part] separator *)body:string;}letv~name~sep_indent~body={name;sep_indent;body}letname{name;_}=nameletsep_indent{sep_indent;_}=sep_indentletbody{body;_}=bodyend(** Remove empty strings at the beginning of a list *)letrecremove_empty_heads=function|""::tl->remove_empty_headstl|l->llettrim_empty_revl=remove_empty_heads(List.rev(remove_empty_headsl))moduleParse_parts=structletpart_statement_re=letopenReinletws=repspaceincompile@@whole_string@@seq[groupws;str"[@@@";ws;str"part";ws;str"\"";group(rep1any);str"\"";ws;str"]";ws;opt(str";;");ws;]letnext_part~name~sep_indent=funlines_rev->letbody=String.concat"\n"(trim_empty_revlines_rev)inPart.v~name~sep_indent~bodyletnext_part_of_groupsgroups=letsep_indent=Re.Group.getgroups1inletname=Re.Group.getgroups2innext_part~name~sep_indentletrecparse_partsinputmake_partlines=matchinput_lineinputwith|exceptionEnd_of_file->[make_partlines]|line->matchRe.exec_optpart_statement_relinewith|None->parse_partsinputmake_part(line::lines)|Somegroups->letnext_part=next_part_of_groupsgroupsinmake_partlines::parse_partsinputnext_part[]letof_filename=letinput=open_innameinparse_partsinput(next_part~name:""~sep_indent:"")[]endtypefile=Part.tlistletreadfile=Parse_parts.of_filefileletfindfile~part=matchpartwith|Somepart->(matchList.find_opt(funp->String.equal(Part.namep)part)filewith|Somep->Some[Part.bodyp]|None->None)|None->List.fold_left(funaccp->Part.bodyp::[""]@acc)[]file|>List.rev|>funx->Somexletrecreplace_or_appendpart_namebody=function|p::tlwhenString.equal(Part.namep)part_name->{pwithbody}::tl|p::tl->p::replace_or_appendpart_namebodytl|[]->[{name=part_name;sep_indent="";body}]letreplacefile~part~lines=letpart=matchpartwithNone->""|Somep->pinreplace_or_appendpart(String.concat"\n"lines)fileletcontentsfile=letlines=List.fold_left(funaccp->letbody=Part.bodypinmatchPart.namepwith|""->body::acc|n->letindent=Part.sep_indentpinbody::("\n"^indent^"[@@@part \""^n^"\"] ;;\n")::acc)[]fileinletlines=List.revlinesinletlines=String.concat"\n"linesinString.trimlines^"\n"