123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974includeCommonmoduleUtils=structincludeUtilsendincludeImpl_sigs(* Internal exception for tracking non-existent fields.
Since it's possible to query a full path like "table.subtable.field",
error messages should tell the user which exact component of that path
does not exist.
So field lookup functions will raise this exception to signal bad field,
and the public interface will convert it to a complete Key_error. *)exceptionField_errorofstring(* Raised by lookup functions when the TOML value given to them
is not a table at all and can't have fields;
to differentiate from a situation when the target value exists,
but it's type is wrong for the accessor function passed by the user. *)exceptionNot_a_tableofstringmoduleMake(N:TomlNumber)(D:TomlDate)=structincludeCommontypetoml_integer=N.inttypetoml_float=N.floattypetoml_date=D.ttypet=|TomlStringofstring|TomlIntegeroftoml_integer|TomlFloatoftoml_float|TomlBooleanofbool|TomlOffsetDateTimeoftoml_date|TomlLocalDateTimeoftoml_date|TomlLocalDateoftoml_date|TomlLocalTimeoftoml_date|TomlArrayoftlist|TomlTableof(string*t)list|TomlInlineTableof(string*t)list|TomlTableArrayoftlistlettype_stringv=matchvwith|TomlString_->"string"|TomlInteger_->"integer"|TomlFloat_->"float"|TomlBoolean_->"boolean"|TomlLocalTime_->"local time"|TomlLocalDate_->"local date"|TomlLocalDateTime_->"local date-time"|TomlOffsetDateTime_->"offset date-time"(* For the purpose of type error printing,
we don't make a difference between syntactic variants
because there's no cases when that would matter. *)|TomlArray_->"array"|TomlTableArray_->"array"|TomlTable_->"table"|TomlInlineTable_->"table"(* Conversions between different variants of the same type. *)lettable_to_inlinet=matchtwith|TomlInlineTable_ast->t|TomlTablet->TomlInlineTablet|_ast->Printf.ksprintftype_error"cannot convert %s to an inline table"(type_stringt)letinline_to_tablet=matchtwith|TomlInlineTablet->TomlTablet|TomlTable_ast->t|_ast->Printf.ksprintftype_error"cannot convert %s to a table"(type_stringt)(* Constructors *)letstrings=TomlStringsletintegern=TomlIntegernletfloatn=TomlFloatnletbooleanb=TomlBooleanbletoffset_datetimedt=TomlOffsetDateTimedtletlocal_datetimedt=TomlLocalDateTimedtletlocal_dated=TomlLocalDatedletlocal_timet=TomlLocalTimetletarrayxs=TomlArrayxslettablekvs=TomlTablekvsletinline_tablekvs=TomlInlineTablekvslettable_arrayxs=letis_tablet=matchtwith|TomlTable_|TomlInlineTable_->true|_->falseinifList.for_allis_tablexsthenTomlTableArray(List.mapinline_to_tablexs)elsePrintf.ksprintftype_error"cannot create an array of tables: original array contains a non-table item"(* Accessors *)letget_tablet=matchtwith|TomlTableos|TomlInlineTableos->os|_->Printf.ksprintftype_error"value is %s, not a table"(type_stringt)letget_table_valuesaccessort=letkvs=get_tabletinList.map(fun(k,v)->(k,accessorv))kvsletget_string?(strict=true)t=matchtwith|TomlStrings->s|_->beginifstrictthenPrintf.ksprintftype_error"value must be a string, found %s"(type_stringt)elsematchtwith|TomlIntegeri->N.int_to_stringi|TomlFloatf->N.float_to_stringf|TomlBooleanb->string_of_boolb|_->Printf.ksprintftype_error"cannot convert %s to string"(type_stringt)endletget_integer?(strict=true)t=matchtwith|TomlIntegeri->i|_->beginifstrictthenPrintf.ksprintftype_error"value must be an integer, found %s"(type_stringt)elsematchtwith|TomlFloatf->N.int_of_floatf|TomlStrings->begintryN.int_of_stringswithFailure_->Printf.ksprintftype_error"string \"%s\" does not represent a valid integer"(Utils.escape_strings)end|TomlBooleanb->N.int_of_booleanb|_->Printf.ksprintftype_error"cannot convert %s to integer"(type_stringt)endletget_float?(strict=true)t=matchtwith|TomlIntegeri->N.float_of_inti|TomlFloatf->f|_->beginifstrictthenPrintf.ksprintftype_error"value must be a float, found %s"(type_stringt)elsematchtwith|TomlStrings->begintryN.float_of_stringswithFailure_->Printf.ksprintftype_error"string \"%s\" does not represent a valid floating point number"(Utils.escape_strings)end|TomlBooleanb->N.float_of_booleanb|_->Printf.ksprintftype_error"cannot convert %s to float"(type_stringt)endletget_boolean?(strict=true)t=matchtwith|TomlBooleanb->b|_->beginifstrictthenPrintf.ksprintftype_error"value must be a boolean, found %s"(type_stringt)elsematchtwith|TomlStrings->(s="")|TomlIntegeri->N.int_to_booleani|TomlFloatf->N.float_to_booleanf|TomlArraya|TomlTableArraya->(a<>[])|TomlTableo|TomlInlineTableo->(o<>[])|_->falseendletget_array?(strict=true)accessort=matchtwith|TomlArraya|TomlTableArraya->List.mapaccessora|_asv->ifstrictthenPrintf.ksprintftype_error"value must be an array, found %s"(type_stringt)elseList.mapaccessor[v]letget_valuet=tletget_offset_datetime?(strict=true)t=matchtwith|TomlOffsetDateTimedt->dt|_asv->beginifstrictthenPrintf.ksprintftype_error"value must be an offset datetime, found %s"(type_stringt)elsematchvwith|TomlStrings->(tryD.offset_datetime_of_stringswithFailuremsg->Printf.ksprintftype_error"failed to convert %s to offset datetime: %s"smsg)|_->Printf.ksprintftype_error"cannot convert %s to offset datetime"(type_stringv)endletget_local_datetime?(strict=true)t=matchtwith|TomlLocalDateTimedt->dt|_asv->beginifstrictthenPrintf.ksprintftype_error"value must be a local datetime, found %s"(type_stringt)elsematchvwith|TomlStrings->(tryD.local_datetime_of_stringswithFailuremsg->Printf.ksprintftype_error"failed to convert %s to local datetime: %s"smsg)|_->Printf.ksprintftype_error"cannot convert %s to local datetime"(type_stringv)endletget_local_date?(strict=true)t=matchtwith|TomlLocalDatedt->dt|_asv->beginifstrictthenPrintf.ksprintftype_error"value must be a local date, found %s"(type_stringt)elsematchvwith|TomlStrings->(tryD.local_date_of_stringswithFailuremsg->Printf.ksprintftype_error"failed to convert %s to local date: %s"smsg)|_->Printf.ksprintftype_error"cannot convert %s to local date"(type_stringv)endletget_local_time?(strict=true)t=matchtwith|TomlLocalTimedt->dt|_asv->beginifstrictthenPrintf.ksprintftype_error"value must be a local time, found %s"(type_stringt)elsematchvwith|TomlStrings->(tryD.local_time_of_stringswithFailuremsg->Printf.ksprintftype_error"failed to convert %s to local time: %s"smsg)|_->Printf.ksprintftype_error"cannot convert %s to local time"(type_stringv)end(* Datetime retrieval convenience functions *)letget_datetimet=matchtwith|TomlOffsetDateTimedt->dt|TomlLocalDateTimedt->dt|_->Printf.ksprintftype_error"value must be a datetime (local or offset), found %s"(type_stringt)letget_datet=matchtwith|TomlOffsetDateTimedt->dt|TomlLocalDateTimedt->dt|TomlLocalDatedt->dt|_->Printf.ksprintftype_error"value must be a date or datetime, found %s"(type_stringt)(* Combinators *)letget_optft=trySome(ft)withType_error_->Noneletget_resultft=tryOk(ft)withType_errormsg->Errormsg(* High-level interfaces *)letlist_table_keyst=lett=tryget_tabletwithType_errormsg->Printf.ksprintftype_error"cannot list table keys: %s"msginList.fold_left(funacc(x,_)->x::acc)[]t|>List.revletlist_table_keys_exn=list_table_keysletlist_table_keys_resultt=tryOk(list_table_keys_exnt)withType_errormsg->Errormsglet_fieldkt=beginlett=get_tabletinletres=List.assoc_optktinmatchreswith|Someres->res|None->raise(Field_errork)endletfieldkt=try_fieldktwithField_errormsg->Printf.ksprintfkey_error"field \"%s\" not found"(Utils.make_printable_keyk)msgletfield_optkt=trySome(fieldkt)withKey_error_->Noneletfindvalueaccessorpath=letmake_dotted_pathps=Utils.string_of_pathpsinletcheck_if_tablekv=matchvwith|TomlTable_|TomlInlineTable_->()|_asv->Printf.ksprintf(funs->raise(Not_a_tables))"value at field \"%s\" is %s, not a table"(Utils.make_printable_keyk)(type_stringv)inletrecauxaccessorpathvalue=matchpathwith|[]->accessorvalue|p::ps->let_=check_if_tablepvalueinletvalue=_fieldpvalueinauxaccessorpsvalueintryauxaccessorpathvaluewith|Field_errormsg->(* The Field_error will contain the first non-existent field in the path.
E.g. trying to find [table.subtable.no_such_field] should produce
"Failed to retrieve ... "table.subtable.bad_field": field "bad_field" not found" *)Printf.ksprintfkey_error"Failed to retrieve a value at %s: field %s not found"(make_dotted_pathpath)(Utils.make_printable_keymsg)|Not_a_tablek->(* Something in the middle of the path isn't a table,
or path is too long. *)Printf.ksprintfkey_error"Failed to retrieve a value at %s: %s"(make_dotted_pathpath)k|Type_errormsg->Printf.ksprintftype_error"Unexpected TOML value type at key %s: %s"(make_dotted_pathpath)msgletfind_exn=findletfind_optvalueaccessorpath=trySome(findvalueaccessorpath)withKey_error_->Noneletfind_or~default:defaultvalueaccessorpath=find_optvalueaccessorpath|>Option.value~default:defaultletfind_resultvalueaccessorpath=tryOk(findvalueaccessorpath)with|Key_errormsg->Errormsg|Type_errormsg->Errormsgletpath_existsvaluepath=letres=find_optvalueget_valuepathinmatchreswith|Some_->true|None->falseletupdate_fieldvaluekeynew_value=letrecupdateassockeyvalue=matchassocwith|[]->beginmatchvaluewith|None->[]|Somev->[(key,v)]end|(key',value')::assoc'->ifkey=key'thenbeginmatchvaluewith|None->assoc'|Somev->(key,v)::assoc'endelse(key',value')::(updateassoc'keyvalue)inmatchvaluewith|TomlTablefs->TomlTable(updatefskeynew_value)|TomlInlineTablefs->TomlInlineTable(updatefskeynew_value)|_->Printf.ksprintfkey_error"cannot update field %s: value is %s, not a table"(Utils.make_printable_keykey)(type_stringvalue)letrecupdate?(use_inline_tables=false)valuepathnew_value=letmake_empty_tableuse_inline=ifuse_inlinethen(TomlInlineTable[])else(TomlTable[])inmatchpathwith|[]->Printf.ksprintfkey_error"Cannot update a TOML value at an empty path"|[p]->update_fieldvaluepnew_value|p::ps->letnested_value=field_optpvalue|>Option.value~default:(make_empty_tableuse_inline_tables)inletnested_value=updatenested_valuepsnew_valueinupdate_fieldvaluep(Somenested_value)letupdate_result?(use_inline_tables=false)valuepathnew_value=tryOk(update~use_inline_tables:use_inline_tablesvaluepathnew_value)withKey_errormsg|Type_errormsg->Errormsgletstring_of_path=Utils.string_of_pathmodulePrinter=structletforce_inlinev=matchvwith|TomlTablet->TomlInlineTablet|_asv->vtypeformatter_settings={indent_width:int;indent_character:char;indent_subtables:bool;newline_before_table:bool;collapse_tables:bool}letmake_indentindentsettingslevel=ifnotindentthen""elseString.make(settings.indent_width*level)settings.indent_characterlethas_nontable_itemst=(* Headers of empty tables _must_ be displayed
(since "table exists and has no items" and "table does not exist" are different conditions),
so for the purpose of collapsing tables to improve readability,
an empty table is _not_ collapsible.
*)ift=[]thentrueelseList.fold_left(funacc(_,v)->(matchvwithTomlTable_->false|_->true)||acc)falsetletreorder_itemst=letrecauxacc_itemsacc_tblsvs=matchvswith|[]->(List.revacc_items)@(List.revacc_tbls)|(k,v)::vs'->beginmatchvwith|TomlTable_->auxacc_items((k,v)::acc_tbls)vs'|_->aux((k,v)::acc_items)acc_tblsvs'endinaux[][]tletis_table_arrayt=ift=[]thenfalseelseList.fold_left(funaccv->(matchvwithTomlTable_|TomlInlineTable_->true|_->false)&&acc)truetletrec_force_table_arrayst=matchtwith|TomlArrayvs->if(is_table_arrayvs)thenletvs=List.map_force_table_arraysvsinTomlTableArray(List.mapinline_to_tablevs)elseTomlArrayvs|TomlTablekvs->TomlTable(List.map(fun(k,v)->(k,_force_table_arraysv))kvs)|_->tletrecformat_primitive?(table_path=[])?(inline=false)?(table_array=false)?(indent=true)?(indent_level=0)settingscallbackv=matchvwith|TomlStrings->(* Use multi-line string syntax for strings with line breaks. *)ifString.containss'\n'thenbegin(* As the spec says:
>A newline immediately following the opening delimiter will be trimmed.
Thus it's safe to add a line break after the opening quotes,
which I think is much more readable.
*)callback"\"\"\"\n";callback@@Utils.escape_string~exclude:['\r';'\n']s;callback"\"\"\"";endelsebegincallback"\"";callback@@Utils.escape_strings;callback"\""end|TomlIntegeri->callback@@N.int_to_stringi|TomlFloatf->callback@@N.float_to_stringf|TomlBooleanb->callback@@string_of_boolb|TomlOffsetDateTimedt->callback@@D.offset_datetime_to_stringdt|TomlLocalDateTimedt->callback@@D.local_datetime_to_stringdt|TomlLocalDatedt->callback@@D.local_date_to_stringdt|TomlLocalTimet->callback@@D.local_time_to_stringt|TomlArraya->leta=List.mapforce_inlineainletlast_index=(List.lengtha)-1incallback"[";List.iteri(funnv->(* Nothing inside an array should be indented. *)format_primitive~indent:falsesettingscallbackv;(* Avoid trailing commas after the last item (even though the 1.0 spec allows them). *)ifn<>last_indexthencallback", ")a;callback"]"|TomlTablet->lett=reorder_itemstinletis_shell_table=has_nontable_itemstinlet()=if(table_path<>[])&&(notsettings.collapse_tables||is_shell_table)thenbeginifsettings.newline_before_tablethencallback"\n";(* Table headers look best when they are at the same indent level as the parent table's keys.
Since the indent level is incremented by the format_pair function,
when this function is called on a nested table, the indent level is what it should be
for the _current table keys_.
To compensate for this, we decrement the level by one for header printing. *)letindent_string=make_indentindentsettings(indent_level-1)inletpath_string=Utils.string_of_pathtable_pathiniftable_arraythencallback@@Printf.sprintf"%s[[%s]]\n"indent_stringpath_stringelsecallback@@Printf.sprintf"%s[%s]\n"indent_stringpath_stringendinletinline=iftable_arraythenfalseelseinlineinlett=iftable_arraythenList.map(fun(k,v)->(k,force_inlinev))telsetinletf=format_pair~table_path:table_path~indent:indent~indent_level:indent_level~inline:inline~table_array:table_arraysettingscallbackinList.iterft|TomlInlineTablet->letlast_index=(List.lengtht)-1incallback"{";List.iteri(funn(k,v)->callback@@Printf.sprintf"%s = "(Utils.make_printable_keyk);(* If an _inline_ table contains other tables or table arrays,
we have to force them all to inline table format to produce valid TOML. *)letv=force_inlinevin(* We also need to disable key indentation, else it will look weird. *)format_primitive~table_path:[]~indent:falsesettingscallbackv;ifn<>last_indexthencallback", ")t;callback"}"|TomlTableArray_->(* A non-inline table array must have a [[$name]] header, but $name has to come from somewhere,
so, unlike other values, it's impossible to render it in isolation.
Only the render_pair function called from a table can render table arrays correctly. *)failwith"TOML arrays of tables cannot be formatted out of the parent table context"andformat_pair?(table_path=[])?(indent=true)?(indent_level=0)?(inline=false)?(table_array=false)settingscallback(k,v)=matchvwith|TomlTablekvsasv->letno_level_increase=(has_nontable_itemskvs)&&settings.collapse_tablesinletindent_level=ifsettings.indent_subtables&¬no_level_increasethenindent_level+1elseifindent_level<1thenindent_level+1elseindent_levelinformat_primitive~table_path:(table_path@[k])~indent_level:indent_level~table_array:table_arraysettingscallbackv|TomlTableArrayv->letv=List.map(funv->(k,v))vinletf=format_pair~table_path:table_path~indent:indent~indent_level:indent_level~inline:inline~table_array:truesettingscallbackinList.iterfv|_asv->letk=Utils.make_printable_keykincallback@@Printf.sprintf"%s%s = "(make_indentindentsettingsindent_level)k;format_primitive~table_path:table_path~indent:indentsettingscallbackv;ifnotinlinethencallback"\n"letto_string?(indent_width=2)?(indent_character=' ')?(indent_subtables=false)?(newline_before_table=true)?(collapse_tables=false)?(force_table_arrays=false)v=letsettings={indent_width=indent_width;indent_character=indent_character;indent_subtables=indent_subtables;newline_before_table=newline_before_table;collapse_tables=collapse_tables}inletbuf=Buffer.create4096inletv=ifforce_table_arraysthen_force_table_arraysvelsevinlet()=format_primitivesettings(Buffer.add_stringbuf)vinBuffer.contentsbufletto_channel?(indent_width=2)?(indent_character=' ')?(indent_subtables=false)?(newline_before_table=true)?(collapse_tables=false)?(force_table_arrays=false)chanv=letsettings={indent_width=indent_width;indent_character=indent_character;indent_subtables=indent_subtables;newline_before_table=newline_before_table;collapse_tables=collapse_tables}inletv=ifforce_table_arraysthen_force_table_arraysvelsevinformat_primitivesettings(output_stringchan)vendmoduleParser=structopenLexingopenParser_utilsletparse_errorposmsg=raise(Parse_error(pos,msg))moduleMI=Toml_parser.MenhirInterpreterletget_parse_errorenv=matchMI.stackenvwith|lazyNil->"Invalid syntax"|lazy(Cons(MI.Element(state,_,_,_),_))->try(String.trim(Toml_parser_messages.message(MI.numberstate)))with|Not_found->"invalid syntax (no specific message for this error)"letrec_parsestatelexbuf(checkpoint:(nodelist)MI.checkpoint)=matchcheckpointwith|MI.InputNeeded_env->letstate,token=Toml_lexer.tokenstatelexbufinletstartp=lexbuf.lex_start_pandendp=lexbuf.lex_curr_pinletcheckpoint=MI.offercheckpoint(token,startp,endp)in_parsestatelexbufcheckpoint|MI.Shifting_|MI.AboutToReduce_->letcheckpoint=MI.resumecheckpointin_parsestatelexbufcheckpoint|MI.HandlingError_env->letline,pos=Parser_utils.get_lexing_positionlexbufinleterr=get_parse_error_envinraise(Parse_error(Some(line,pos),err))|MI.Acceptedv->v|MI.Rejected->raise(Parse_error(None,"invalid syntax (parser rejected the input)"))letcheck_duplicatep'p=matchp,p'with|TableHeaderp,TableHeaderp'->ifp=p'thenPrintf.ksprintfduplicate_key_error"table [%s] is defined more than once"(Utils.string_of_pathp)|TableHeaderp,TableArrayHeaderp'->ifp=p'thenletpath_str=(Utils.string_of_pathp)inPrintf.ksprintfduplicate_key_error"table [%s] is duplicated by an array of tables [[%s]]"path_strpath_str|TableArrayHeaderp,TableHeaderp'->ifp=p'thenletpath_str=(Utils.string_of_pathp)inPrintf.ksprintfduplicate_key_error"array of tables [[%s]] is duplicated by a table [%s]"path_strpath_str|_->()letcheck_duplicatesxsx=List.iter(check_duplicatex)xs(* Takes a child and a parent path and finds the part of the child path unique to the child.
E.g. `path_complement [1;2;3] [1]` is `[2;3]`.
*)letrecpath_complementchildparent=matchchild,parentwith|[],[]->(* They are the same path. *)Some[]|[],(_::_)->(* The alleged parent path is longer, so it's not actually a parent path. *)None|(_::_)asps,[]->(* The parent path is exhausted, so what's left is the part unique to the child. *)Someps|(x::xs),(y::ys)->ifx=ythenpath_complementxsys(* Still in the common part of the path. *)elseNone(* Like Buster and Babs Bunny, no relation. *)letis_child_pathchildparent=letc=path_complementchildparentinmatchcwith|None|Some[]->false|_->trueletto_pairsns=List.map(fun(k,v)->Pair(k,v))ns(* This is for cases that _should not happen_,
but I haven't proved that they actually _can't_ happen. *)letinternal_errormsg=failwith@@Printf.sprintf"otoml internal error: %s. Please report a bug."msgletrecinsert?(if_not_exists=false)?(append_table_arrays=false)tomlpathvalue=letcheck_existstblpif_not_existsvalue=letorig_value=field_optptblinmatchorig_valuewith|Somev->ifif_not_existsthentrueelseduplicate_key_error@@Printf.sprintf"duplicate key \"%s\" overrides a value of type %s with a value of type %s"p(type_stringv)(type_stringvalue)|None->falseinmatchpathwith|[]->internal_error"insert called with empty path"|[p]->beginmatchtomlwith|(TomlTablekvs)astbl->ifappend_table_arraysthenletorig_value=field_optptomlinbeginmatchorig_value,valuewith|Some(TomlTableArrayts),TomlTable_->lett_array=TomlTableArray(ts@[value])inupdate_fieldtblp(Somet_array)|Some(TomlTableArray_),v->internal_error@@Printf.sprintf"trying to append a value of type %s to an array of tables"(type_stringv)|Somev,v'->internal_error@@Printf.sprintf"insert ~append_table_arrays:true called on values of types %s and %s"(type_stringv)(type_stringv')|None,_->internal_error@@Printf.sprintf"insert ~append_table_arrays:true called on an empty array"endelseif(check_existstblpif_not_existsvalue)thentomlelseTomlTable(kvs@[p,value])|(TomlInlineTablekvs)astbl->if(check_existstblpif_not_existsvalue)thentomlelseTomlInlineTable(kvs@[p,value])|_asv->internal_error@@Printf.sprintf"path is too long (key \"%s\" left, at a value of type %s)"p(type_stringv)end|p::ps->beginmatchtomlwith|((TomlTablekvs)|(TomlInlineTablekvs))asorig_table->letorig_value=field_optptomlinbeginmatchorig_valuewith|Some(((TomlTable_)|(TomlInlineTable_))ast)->letsubtable=insert~if_not_exists:if_not_exists~append_table_arrays:append_table_arraystpsvalueinupdate_fieldorig_tablep(Somesubtable)|Some(TomlTableArrayts)->letbody,tail=Utils.split_listtsinlettail=Option.value~default:(TomlTable[])tailinlettail=insert~if_not_exists:if_not_exists~append_table_arrays:append_table_arraystailpsvalueinlett_array=TomlTableArray(body@[tail])inupdate_fieldorig_tablep(Somet_array)|Some(_asov)->duplicate_key_error@@Printf.sprintf"duplicate key \"%s\" overrides a value of type %s with a value of type %s"p(type_stringov)(type_stringvalue)|None->lettbl=TomlTable[]inlettbl=insert~if_not_exists:if_not_exists~append_table_arrays:append_table_arraystblpsvalueinTomlTable(kvs@[p,tbl])end|_asv->duplicate_key_error@@Printf.sprintf"duplicate key \"%s\" overrides a value of type %s with a value of type %s (path remainder: [%s])"p(type_stringv)(type_stringvalue)(Utils.string_of_pathps)endletrecvalue_of_noden=matchnwith|NodeIntegern->TomlInteger(N.int_of_stringn)|NodeFloatx->TomlFloat(N.float_of_stringx)|NodeStrings->TomlStrings|NodeBooleanb->TomlBoolean(bool_of_stringb)|NodeOffsetDateTimedt->TomlOffsetDateTime(D.offset_datetime_of_stringdt)|NodeLocalDateTimedt->TomlLocalDateTime(D.local_datetime_of_stringdt)|NodeLocalDated->TomlLocalDate(D.local_date_of_stringd)|NodeLocalTimet->TomlLocalTime(D.local_time_of_stringt)|NodeArrayns->TomlArray(List.mapvalue_of_nodens)|NodeInlineTablens->letns=to_pairsnsin(* Since inline tables cannot contain table arrays,
the tail returned by from_statements must always be empty.
*)let_,res=from_statements(TomlInlineTable[])[][]nsinres|_->internal_error"table header or a non-inline table inside a value"andfrom_statementstomlparent_pathseen_pathsstatements=matchstatementswith|[]->[],toml|s::ss->beginmatchswith|Pair(k,v)->letfull_path=parent_path@kinletvalue=value_of_nodevinlettoml=inserttomlfull_pathvaluein(* Add value paths to seen paths as fake table headers to prevent
actual table and table array headers from duplicating then. *)letseen_paths=(TableHeaderfull_path)::seen_pathsinfrom_statementstomlparent_pathseen_pathsss|(TableHeaderks)asn->let()=check_duplicatesseen_pathsninletseen_paths=n::seen_pathsinlettoml=insert~if_not_exists:truetomlks(TomlTable[])infrom_statementstomlksseen_pathsss|(TableArrayHeaderks)asn->let()=check_duplicatesseen_pathsninlettoml=insert~if_not_exists:truetomlks(TomlTableArray[])inifnot(is_child_pathksparent_path)thenlettoml=insert~append_table_arrays:truetomlks(TomlTable[])inletstmts,toml=from_statementstomlks[n]ssinfrom_statementstoml[]seen_pathsstmtselsefrom_statementstomlks(n::seen_paths)ss|_->internal_error"bare value in the AST"endletformat_parse_errorposerr=matchposwith|Some(line,pos)->Printf.sprintf"Syntax error on line %d, character %d: %s"lineposerr|None->Printf.sprintf"Parse error: %s"errletparselexbuf=(* Make a fresh lexer context *)letstate=[]inlettoml_statements=_parsestatelexbuf(Toml_parser.Incremental.toml_astlexbuf.lex_curr_p)inlettail_stmts,toml=from_statements(TomlTable[])[][]toml_statementsiniftail_stmts<>[]theninternal_error"from_statements left a non-empty tail"elsetomlletfrom_channelic=letlexbuf=Lexing.from_channelicinparselexbufletfrom_filefilename=letic=open_infilenameinFun.protect~finally:(fun()->close_inic)(fun()->from_channelic)letfrom_strings=letlexbuf=Lexing.from_stringsinparselexbufletfrom_string_results=tryOk(from_strings)with|Parse_error(pos,err)->Error(format_parse_errorposerr)|Duplicate_keyerr->Errorerr|Failureerr->Error(Printf.sprintf"otoml internal error: %s"err)letfrom_channel_resultic=tryOk(from_channelic)with|Parse_error(pos,err)->Error(format_parse_errorposerr)|Sys_errorerr->Errorerr|Failureerr->Error(Printf.sprintf"otoml internal error: %s"err)letfrom_file_resultf=tryOk(from_filef)with|Parse_error(pos,err)->Error(format_parse_errorposerr)|Sys_errorerr->Errorerr|Failureerr->Error(Printf.sprintf"otoml internal error: %s"err)endmoduleHelpers=structletfind_string?(strict=true)tomlpath=findtoml(get_string~strict:strict)pathletfind_string_exn?(strict=true)tomlpath=findtoml(get_string~strict:strict)pathletfind_string_opt?(strict=true)tomlpath=find_opttoml(get_string~strict:strict)pathletfind_string_result?(strict=true)tomlpath=find_resulttoml(get_string~strict:strict)pathletfind_string_default?(strict=true)~defaulttomlpath=tryfindtoml(get_string~strict:strict)pathwithKey_error_->defaultletfind_strings?(strict=true)tomlpath=findtoml(get_array~strict:false(get_string~strict:strict))pathletfind_strings_exn?(strict=true)tomlpath=findtoml(get_array~strict:false(get_string~strict:strict))pathletfind_strings_opt?(strict=true)tomlpath=find_opttoml(get_array~strict:false(get_string~strict:strict))pathletfind_strings_result?(strict=true)tomlpath=find_resulttoml(get_array~strict:false(get_string~strict:strict))pathletfind_strings_default?(strict=true)~defaulttomlpath=tryfindtoml(get_array~strict:false(get_string~strict:strict))pathwithKey_error_->defaultletfind_integer?(strict=true)tomlpath=findtoml(get_integer~strict:strict)pathletfind_integer_exn?(strict=true)tomlpath=findtoml(get_integer~strict:strict)pathletfind_integer_opt?(strict=true)tomlpath=find_opttoml(get_integer~strict:strict)pathletfind_integer_result?(strict=true)tomlpath=find_resulttoml(get_integer~strict:strict)pathletfind_integer_default?(strict=true)~defaulttomlpath=tryfindtoml(get_integer~strict:strict)pathwithKey_error_->defaultletfind_integers?(strict=true)tomlpath=findtoml(get_array~strict:false(get_integer~strict:strict))pathletfind_integers_exn?(strict=true)tomlpath=findtoml(get_array~strict:false(get_integer~strict:strict))pathletfind_integers_opt?(strict=true)tomlpath=find_opttoml(get_array~strict:false(get_integer~strict:strict))pathletfind_integers_result?(strict=true)tomlpath=find_resulttoml(get_array~strict:false(get_integer~strict:strict))pathletfind_integers_default?(strict=true)~defaulttomlpath=tryfindtoml(get_array~strict:false(get_integer~strict:strict))pathwithKey_error_->defaultletfind_float?(strict=true)tomlpath=findtoml(get_float~strict:strict)pathletfind_float_exn?(strict=true)tomlpath=findtoml(get_float~strict:strict)pathletfind_float_opt?(strict=true)tomlpath=find_opttoml(get_float~strict:strict)pathletfind_float_result?(strict=true)tomlpath=find_resulttoml(get_float~strict:strict)pathletfind_float_default?(strict=true)~defaulttomlpath=tryfindtoml(get_float~strict:strict)pathwithKey_error_->defaultletfind_floats?(strict=true)tomlpath=findtoml(get_array~strict:false(get_float~strict:strict))pathletfind_floats_exn?(strict=true)tomlpath=findtoml(get_array~strict:false(get_float~strict:strict))pathletfind_floats_opt?(strict=true)tomlpath=find_opttoml(get_array~strict:false(get_float~strict:strict))pathletfind_floats_result?(strict=true)tomlpath=find_resulttoml(get_array~strict:false(get_float~strict:strict))pathletfind_floats_default?(strict=true)~defaulttomlpath=tryfindtoml(get_array~strict:false(get_float~strict:strict))pathwithKey_error_->defaultletfind_boolean?(strict=true)tomlpath=findtoml(get_boolean~strict:strict)pathletfind_boolean_exn?(strict=true)tomlpath=findtoml(get_boolean~strict:strict)pathletfind_boolean_opt?(strict=true)tomlpath=find_opttoml(get_boolean~strict:strict)pathletfind_boolean_result?(strict=true)tomlpath=find_resulttoml(get_boolean~strict:strict)pathletfind_boolean_default?(strict=true)~defaulttomlpath=tryfindtoml(get_boolean~strict:strict)pathwithKey_error_->defaultletfind_booleans?(strict=true)tomlpath=findtoml(get_array~strict:false(get_boolean~strict:strict))pathletfind_booleans_exn?(strict=true)tomlpath=findtoml(get_array~strict:false(get_boolean~strict:strict))pathletfind_booleans_opt?(strict=true)tomlpath=find_opttoml(get_array~strict:false(get_boolean~strict:strict))pathletfind_booleans_result?(strict=true)tomlpath=find_resulttoml(get_array~strict:false(get_boolean~strict:strict))pathletfind_booleans_default?(strict=true)~defaulttomlpath=tryfindtoml(get_array~strict:false(get_boolean~strict:strict))pathwithKey_error_->defaultletfind_offset_datetime?(strict=true)tomlpath=findtoml(get_offset_datetime~strict:strict)pathletfind_offset_datetime_exn?(strict=true)tomlpath=findtoml(get_offset_datetime~strict:strict)pathletfind_offset_datetime_opt?(strict=true)tomlpath=find_opttoml(get_offset_datetime~strict:strict)pathletfind_offset_datetime_result?(strict=true)tomlpath=find_resulttoml(get_offset_datetime~strict:strict)pathletfind_offset_datetime_default?(strict=true)~defaulttomlpath=tryfindtoml(get_offset_datetime~strict:strict)pathwithKey_error_->defaultletfind_offset_datetimes?(strict=true)tomlpath=findtoml(get_array~strict:false(get_offset_datetime~strict:strict))pathletfind_offset_datetimes_exn?(strict=true)tomlpath=findtoml(get_array~strict:false(get_offset_datetime~strict:strict))pathletfind_offset_datetimes_opt?(strict=true)tomlpath=find_opttoml(get_array~strict:false(get_offset_datetime~strict:strict))pathletfind_offset_datetimes_result?(strict=true)tomlpath=find_resulttoml(get_array~strict:false(get_offset_datetime~strict:strict))pathletfind_offset_datetimes_default?(strict=true)~defaulttomlpath=tryfindtoml(get_array~strict:false(get_offset_datetime~strict:strict))pathwithKey_error_->defaultletfind_local_datetime?(strict=true)tomlpath=findtoml(get_local_datetime~strict:strict)pathletfind_local_datetime_exn?(strict=true)tomlpath=findtoml(get_local_datetime~strict:strict)pathletfind_local_datetime_opt?(strict=true)tomlpath=find_opttoml(get_local_datetime~strict:strict)pathletfind_local_datetime_result?(strict=true)tomlpath=find_resulttoml(get_local_datetime~strict:strict)pathletfind_local_datetime_default?(strict=true)~defaulttomlpath=tryfindtoml(get_local_datetime~strict:strict)pathwithKey_error_->defaultletfind_local_datetimes?(strict=true)tomlpath=findtoml(get_array~strict:false(get_local_datetime~strict:strict))pathletfind_local_datetimes_exn?(strict=true)tomlpath=findtoml(get_array~strict:false(get_local_datetime~strict:strict))pathletfind_local_datetimes_opt?(strict=true)tomlpath=find_opttoml(get_array~strict:false(get_local_datetime~strict:strict))pathletfind_local_datetimes_result?(strict=true)tomlpath=find_resulttoml(get_array~strict:false(get_local_datetime~strict:strict))pathletfind_local_datetimes_default?(strict=true)~defaulttomlpath=tryfindtoml(get_array~strict:false(get_local_datetime~strict:strict))pathwithKey_error_->defaultletfind_local_date?(strict=true)tomlpath=findtoml(get_local_date~strict:strict)pathletfind_local_date_exn?(strict=true)tomlpath=findtoml(get_local_date~strict:strict)pathletfind_local_date_opt?(strict=true)tomlpath=find_opttoml(get_local_date~strict:strict)pathletfind_local_date_result?(strict=true)tomlpath=find_resulttoml(get_local_date~strict:strict)pathletfind_local_date_default?(strict=true)~defaulttomlpath=tryfindtoml(get_local_date~strict:strict)pathwithKey_error_->defaultletfind_local_dates?(strict=true)tomlpath=findtoml(get_array~strict:false(get_local_date~strict:strict))pathletfind_local_dates_exn?(strict=true)tomlpath=findtoml(get_array~strict:false(get_local_date~strict:strict))pathletfind_local_dates_opt?(strict=true)tomlpath=find_opttoml(get_array~strict:false(get_local_date~strict:strict))pathletfind_local_dates_result?(strict=true)tomlpath=find_resulttoml(get_array~strict:false(get_local_date~strict:strict))pathletfind_local_dates_default?(strict=true)~defaulttomlpath=tryfindtoml(get_array~strict:false(get_local_date~strict:strict))pathwithKey_error_->defaultletfind_local_time?(strict=true)tomlpath=findtoml(get_local_time~strict:strict)pathletfind_local_time_exn?(strict=true)tomlpath=findtoml(get_local_time~strict:strict)pathletfind_local_time_opt?(strict=true)tomlpath=find_opttoml(get_local_time~strict:strict)pathletfind_local_time_result?(strict=true)tomlpath=find_resulttoml(get_local_time~strict:strict)pathletfind_local_time_default?(strict=true)~defaulttomlpath=tryfindtoml(get_local_time~strict:strict)pathwithKey_error_->defaultletfind_local_times?(strict=true)tomlpath=findtoml(get_array~strict:false(get_local_time~strict:strict))pathletfind_local_times_exn?(strict=true)tomlpath=findtoml(get_array~strict:false(get_local_time~strict:strict))pathletfind_local_times_opt?(strict=true)tomlpath=find_opttoml(get_array~strict:false(get_local_time~strict:strict))pathletfind_local_times_result?(strict=true)tomlpath=find_resulttoml(get_array~strict:false(get_local_time~strict:strict))pathletfind_local_times_default?(strict=true)~defaulttomlpath=tryfindtoml(get_array~strict:false(get_local_time~strict:strict))pathwithKey_error_->defaultendend