Source file otoml_base.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
include Common
module Utils = struct
include Utils
end
include Impl_sigs
module Make (I: TomlInteger) (F: TomlFloat) (D: TomlDate) = struct
type toml_integer = I.t
type toml_float = F.t
type toml_date = D.t
type t =
| TomlString of string
| TomlInteger of toml_integer
| TomlFloat of toml_float
| TomlBoolean of bool
| TomlOffsetDateTime of toml_date
| TomlLocalDateTime of toml_date
| TomlLocalDate of toml_date
| TomlLocalTime of toml_date
| TomlArray of t list
| TomlTable of (string * t) list
| TomlInlineTable of (string * t) list
| TomlTableArray of t list
let type_string v =
match v with
| TomlString _ -> "string"
| TomlInteger _ -> "integer"
| TomlFloat _ -> "float"
| TomlBoolean _ -> "boolean"
| TomlLocalTime _ -> "local time"
| TomlLocalDate _ -> "local date"
| TomlLocalDateTime _ -> "local date-time"
| TomlOffsetDateTime _ -> "offset date-time"
| TomlArray _ | TomlTableArray _ -> "array"
| TomlTable _ | TomlInlineTable _ -> "table"
let table_to_inline t =
match t with
| TomlInlineTable _ as t -> t
| TomlTable t -> TomlInlineTable t
| _ as t -> Printf.ksprintf type_error "cannot convert %s to an inline table" (type_string t)
let inline_to_table t =
match t with
| TomlInlineTable t -> TomlTable t
| TomlTable _ as t -> t
| _ as t -> Printf.ksprintf type_error "cannot convert %s to a table" (type_string t)
let string s = TomlString s
let integer n = TomlInteger n
let float n = TomlFloat n
let boolean b = TomlBoolean b
let offset_datetime dt = TomlOffsetDateTime dt
let local_datetime dt = TomlLocalDateTime dt
let local_date d = TomlLocalDate d
let local_time t = TomlLocalTime t
let array xs = TomlArray xs
let table kvs = TomlTable kvs
let inline_table kvs = TomlInlineTable kvs
let table_array xs =
let is_table t =
match t with
| TomlTable _ | TomlInlineTable _ -> true
| _ -> false
in
if List.for_all is_table xs then TomlTableArray (List.map inline_to_table xs)
else Printf.ksprintf type_error "cannot create an array of tables: original array contains a non-table item"
let get_table t =
match t with
| TomlTable os | TomlInlineTable os -> os
| _ -> Printf.ksprintf type_error "value is %s, not a table" (type_string t)
let get_string ?(strict=true) t =
match t with
| TomlString s -> s
| _ ->
begin
if strict then Printf.ksprintf type_error "value must be a string, found a %s" (type_string t) else
match t with
| TomlInteger i -> I.to_string i
| TomlFloat f -> F.to_string f
| TomlBoolean b -> string_of_bool b
| _ -> Printf.ksprintf type_error "cannot convert %s to string" (type_string t)
end
let get_integer ?(strict=true) t =
match t with
| TomlInteger i -> i
| _ ->
begin
if strict then Printf.ksprintf type_error "value must be an integer, found %s" (type_string t) else
match t with
| TomlString s -> I.of_string s
| TomlBoolean b -> I.of_boolean b
| _ -> Printf.ksprintf type_error "cannot convert %s to integer" (type_string t)
end
let get_float ?(strict=true) t =
match t with
| TomlFloat f -> f
| _ ->
begin
if strict then Printf.ksprintf type_error "value must be an float, found %s" (type_string t) else
match t with
| TomlString s -> F.of_string s
| TomlBoolean b -> F.of_boolean b
| _ -> Printf.ksprintf type_error "cannot convert %s to float" (type_string t)
end
let get_boolean ?(strict=true) t =
match t with
| TomlBoolean b -> b
| _ ->
begin
if strict then Printf.ksprintf type_error "value must be n boolean, found a %s" (type_string t) else
match t with
| TomlString s -> (s = "")
| TomlInteger i -> I.to_boolean i
| TomlFloat f -> F.to_boolean f
| TomlArray a | TomlTableArray a -> (a = [])
| TomlTable o | TomlInlineTable o -> (o = [])
| _ -> false
end
let get_array ?(strict=true) t =
match t with
| TomlArray a | TomlTableArray a -> a
| _ as v ->
if strict then Printf.ksprintf type_error "value must be an array, found %s" (type_string t)
else [v]
let get_value t = t
let get_offset_datetime t =
match t with
| TomlOffsetDateTime dt -> dt
| _ -> Printf.ksprintf type_error "value must be an offset datetime, found a %s" (type_string t)
let get_local_datetime t =
match t with
| TomlLocalDateTime dt -> dt
| _ -> Printf.ksprintf type_error "value must be a local datetime, found a %s" (type_string t)
let get_datetime t =
match t with
| TomlOffsetDateTime dt -> dt
| TomlLocalDateTime dt -> dt
| _ -> Printf.ksprintf type_error "value must be a datetime, found a %s" (type_string t)
let get_local_date t =
match t with
| TomlLocalDate dt -> dt
| _ -> Printf.ksprintf type_error "value must be a local date, found a %s" (type_string t)
let get_date t =
match t with
| TomlOffsetDateTime dt -> dt
| TomlLocalDateTime dt -> dt
| TomlLocalDate dt -> dt
| _ -> Printf.ksprintf type_error "value must be a date or datetime, found a %s" (type_string t)
let get_local_time t =
match t with
| TomlLocalTime dt -> dt
| _ -> Printf.ksprintf type_error "value must be a local time, found a %s" (type_string t)
let list_table_keys t =
let t =
try get_table t
with Type_error msg -> Printf.ksprintf type_error "cannot list keys: %s" msg
in
List.fold_left (fun acc (x, _) -> x :: acc) [] t |> List.rev
let field k t =
try
begin
let t = get_table t in
let res = List.assoc_opt k t in
match res with
| Some res -> res
| None -> Printf.ksprintf key_error "field \"%s\" not found" k
end
with
| Key_error msg -> Printf.ksprintf key_error "cannot retrieve field \"%s\": %s" k msg
| Type_error msg -> Printf.ksprintf type_error "cannot retrieve field \"%s\": %s" k msg
let field_opt k t =
try Some (field k t)
with Key_error _ -> None
let find value accessor path =
let make_dotted_path ps = Utils.string_of_path ps in
let rec aux accessor path value =
match path with
| [] -> accessor value
| p :: ps ->
let value = field p value in
aux accessor ps value
in
try
aux accessor path value
with
| Key_error msg ->
Printf.ksprintf key_error "Failed to retrieve a value at %s: %s" (make_dotted_path path) msg
| Type_error msg ->
Printf.ksprintf type_error "TOML type error occured while trying to retrieve a value at %s: %s"
(make_dotted_path path) msg
let find_opt value accessor path =
try Some (find value accessor path)
with Key_error _ -> None
let find_or ~default:default value accessor path =
find_opt value accessor path |> Option.value ~default:default
let find_result value accessor path =
try Ok (find value accessor path)
with
| Key_error msg -> Error msg
| Type_error msg -> Error msg
let update_field value key new_value =
let rec update assoc key value =
match assoc with
| [] ->
begin
match value with
| None -> []
| Some v -> [(key, v)]
end
| (key', value') :: assoc' ->
if key = key' then
begin
match value with
| None -> assoc'
| Some v -> (key, v) :: assoc'
end
else (key', value') :: (update assoc' key value)
in
match value with
| TomlTable fs -> TomlTable (update fs key new_value)
| TomlInlineTable fs -> TomlInlineTable (update fs key new_value)
| _ -> Printf.ksprintf key_error "cannot update field %s: value is %s, not a table" key (type_string value)
let rec update ?(use_inline_tables=false) value path new_value =
let make_empty_table use_inline =
if use_inline then (TomlInlineTable []) else (TomlTable [])
in
match path with
| [] -> failwith "Cannot update a TOML value at an empty path"
| [p] -> update_field value p new_value
| p :: ps ->
let nested_value = field_opt p value |> Option.value ~default:(make_empty_table use_inline_tables) in
let nested_value = update nested_value ps new_value in
update_field value p (Some nested_value)
module Printer = struct
let force_inline v =
match v with
| TomlTable t -> TomlInlineTable t
| _ as v -> v
type formatter_settings = {
indent_width: int;
indent_character: char;
indent_subtables: bool;
newline_before_table: bool;
collapse_tables: bool
}
let make_indent indent settings level =
if not indent then "" else
String.make (settings.indent_width * level) settings.indent_character
let has_nontable_items t =
if t = [] then true else
List.fold_left (fun acc (_, v) -> (match v with TomlTable _ -> false | _ -> true) || acc) false t
let rec format_primitive ?(table_path=[]) ?(inline=false) ?(table_array=false) ?(indent=true) ?(indent_level=0) settings callback v =
match v with
| TomlString s ->
if String.contains s '\n' then
begin
callback "\"\"\"\n";
callback @@ Utils.escape_string ~exclude:['\r'; '\n'] s;
callback "\"\"\"";
end
else
begin
callback "\"";
callback @@ Utils.escape_string s;
callback "\""
end
| TomlInteger i ->
callback @@ I.to_string i
| TomlFloat f ->
callback @@ F.to_string f
| TomlBoolean b ->
callback @@ string_of_bool b
| TomlOffsetDateTime dt ->
callback @@ D.offset_datetime_to_string dt
| TomlLocalDateTime dt ->
callback @@ D.local_datetime_to_string dt
| TomlLocalDate dt ->
callback @@ D.local_date_to_string dt
| TomlLocalTime t ->
callback @@ D.local_time_to_string t
| TomlArray a ->
let a = List.map force_inline a in
let last_index = (List.length a) - 1 in
callback "[";
List.iteri (fun n v ->
format_primitive ~indent:false settings callback v;
if n <> last_index then callback ", ")
a;
callback "]"
| TomlTable t ->
let is_shell_table = has_nontable_items t in
let () =
if (table_path <> []) && (not settings.collapse_tables || is_shell_table) then begin
if settings.newline_before_table then callback "\n";
let indent_string = make_indent indent settings (indent_level - 1) in
let path_string = Utils.string_of_path table_path in
if table_array then callback @@ Printf.sprintf "%s[[%s]]\n" indent_string path_string
else callback @@ Printf.sprintf "%s[%s]\n" indent_string path_string
end
in
let inline = if table_array then false else inline in
let t = if table_array then List.map (fun (k, v) -> (k, force_inline v)) t else t in
let f = format_pair ~table_path:table_path
~indent:indent ~indent_level:indent_level
~inline:inline ~table_array:table_array
settings callback
in
List.iter f t
| TomlInlineTable t ->
let last_index = (List.length t) - 1 in
callback "{";
List.iteri (fun n (k, v) ->
callback @@ Printf.sprintf "%s = " (Utils.make_printable_key k);
let v = force_inline v in
format_primitive ~table_path:[] ~indent:false settings callback v;
if n <> last_index then callback ", ")
t;
callback "}"
| TomlTableArray _ ->
failwith "TOML arrays of tables cannot be formatted out of the parent table context"
and format_pair ?(table_path=[]) ?(indent=true) ?(indent_level=0)
?(inline=false) ?(table_array=false) settings callback (k, v) =
match v with
| TomlTable kvs as v ->
let no_level_increase = (has_nontable_items kvs) && settings.collapse_tables in
let indent_level =
if settings.indent_subtables && not no_level_increase then indent_level + 1
else if indent_level < 1 then indent_level + 1 else indent_level
in
format_primitive ~table_path:(table_path @ [k]) ~indent_level:indent_level ~table_array:table_array
settings callback v
| TomlTableArray v ->
let v = List.map (fun v -> (k, v)) v in
let f = format_pair ~table_path:table_path ~indent:indent ~indent_level:indent_level
~inline:inline ~table_array:true
settings callback
in
List.iter f v
| _ as v ->
let k = Utils.make_printable_key k in
callback @@ Printf.sprintf "%s%s = " (make_indent indent settings indent_level) k;
format_primitive ~table_path:table_path ~indent:indent settings callback v;
if not inline then callback "\n"
let to_string ?(indent_width=2) ?(indent_character=' ') ?(indent_subtables=false) ?(newline_before_table=true) ?(collapse_tables=false) v =
let settings = {
indent_width = indent_width;
indent_character = indent_character;
indent_subtables = indent_subtables;
newline_before_table = newline_before_table;
collapse_tables= collapse_tables
}
in
let buf = Buffer.create 4096 in
let () = format_primitive settings (Buffer.add_string buf) v in
Buffer.contents buf
let to_channel ?(indent_width=2) ?(indent_character=' ') ?(indent_subtables=false) ?(newline_before_table=true) ?(collapse_tables=false) chan v =
let settings = {
indent_width = indent_width;
indent_character = indent_character;
indent_subtables = indent_subtables;
newline_before_table = newline_before_table;
collapse_tables= collapse_tables
}
in
format_primitive settings (output_string chan) v
end
module Parser = struct
open Lexing
open Parser_utils
exception Duplicate_key of string
let duplicate_key_error msg = raise (Duplicate_key msg)
let parse_error pos msg = raise (Parse_error (pos, msg))
module MI = Toml_parser.MenhirInterpreter
let get_parse_error env =
match MI.stack env with
| lazy Nil -> "Invalid syntax"
| lazy (Cons (MI.Element (state, _, _, _), _)) ->
try (Toml_parser_messages.message (MI.number state)) with
| Not_found -> "invalid syntax (no specific message for this eror)"
let rec _parse lexbuf (checkpoint : (node list) MI.checkpoint ) =
match checkpoint with
| MI.InputNeeded _env ->
let token = Toml_lexer.token lexbuf in
let startp = lexbuf.lex_start_p
and endp = lexbuf.lex_curr_p in
let checkpoint = MI.offer checkpoint (token, startp, endp) in
_parse lexbuf checkpoint
| MI.Shifting _
| MI.AboutToReduce _ ->
let checkpoint = MI.resume checkpoint in
_parse lexbuf checkpoint
| MI.HandlingError _env ->
let line, pos = Parser_utils.get_lexing_position lexbuf in
let err = get_parse_error _env in
raise (Parse_error (Some (line, pos), err))
| MI.Accepted v -> v
| MI.Rejected ->
raise (Parse_error (None, "invalid syntax (parser rejected the input)"))
let check_duplicate p' p =
match p, p' with
| TableHeader p, TableHeader p' ->
if p = p' then Printf.ksprintf (parse_error None) "table [%s] is defined more than once"
(Utils.string_of_path p)
| TableHeader p, TableArrayHeader p' ->
if p = p' then
let path_str = (Utils.string_of_path p) in
Printf.ksprintf (parse_error None) "table [%s] is duplicated by an array of tables [[%s]]"
path_str path_str
| TableArrayHeader p, TableHeader p' ->
if p = p' then
let path_str = (Utils.string_of_path p) in
Printf.ksprintf (parse_error None) "array of tables [[%s]] is duplicated by a table [%s]"
path_str path_str
| _ -> ()
let check_duplicates xs x = List.iter (check_duplicate x) xs
let rec path_complement child parent =
match child, parent with
| [], [] ->
Some []
| [], (_ :: _) ->
None
| (_ :: _) as ps, [] ->
Some ps
| (x :: xs), (y :: ys) ->
if x = y then path_complement xs ys
else None
let is_child_path child parent =
let c = path_complement child parent in
match c with
| None | Some [] -> false
| _ -> true
let to_pairs ns = List.map (fun (k, v) -> Pair (k, v)) ns
let internal_error msg =
failwith @@ Printf.sprintf "otoml internal error: %s. Please report a bug." msg
let rec insert ?(if_not_exists=false) ?(append_table_arrays=false) toml path value =
let check_exists tbl p if_not_exists value =
let orig_value = field_opt p tbl in
match orig_value with
| Some v ->
if if_not_exists then true
else duplicate_key_error @@ Printf.sprintf
"duplicate key \"%s\" overrides a value of type %s with a value of type %s"
p (type_string v) (type_string value)
| None -> false
in
match path with
| [] -> internal_error "insert called with empty path"
| [p] ->
begin match toml with
| (TomlTable kvs) as tbl ->
if append_table_arrays then
let orig_value = field_opt p toml in
begin match orig_value, value with
| Some (TomlTableArray ts), TomlTable _ ->
let t_array = TomlTableArray (ts @ [value]) in
update_field tbl p (Some t_array)
| Some (TomlTableArray _), v ->
internal_error @@ Printf.sprintf "trying to append a value of type %s to an array of tables"
(type_string v)
| Some v, v' ->
internal_error @@ Printf.sprintf "insert ~append_table_arrays:true called on values of types %s and %s"
(type_string v) (type_string v')
| None, _ ->
internal_error @@ Printf.sprintf "insert ~append_table_arrays:true called on an empty array"
end
else if (check_exists tbl p if_not_exists value) then toml
else TomlTable (kvs @ [p, value])
| (TomlInlineTable kvs) as tbl ->
if (check_exists tbl p if_not_exists value) then toml
else TomlInlineTable (kvs @ [p, value])
| _ as v ->
internal_error @@ Printf.sprintf "path is too long (key \"%s\" left, at a value of type %s)"
p (type_string v)
end
| p :: ps ->
begin match toml with
| ((TomlTable kvs) | (TomlInlineTable kvs)) as orig_table ->
let orig_value = field_opt p toml in
begin match orig_value with
| Some (((TomlTable _) | (TomlInlineTable _)) as t) ->
let subtable = insert ~if_not_exists:if_not_exists ~append_table_arrays:append_table_arrays t ps value
in update_field orig_table p (Some subtable)
| Some (TomlTableArray ts) ->
let body, tail = Utils.split_list ts in
let tail = Option.value ~default:(TomlTable []) tail in
let tail =
insert ~if_not_exists:if_not_exists ~append_table_arrays:append_table_arrays tail ps value
in
let t_array = TomlTableArray (body @ [tail]) in
update_field orig_table p (Some t_array)
| Some (_ as ov) ->
duplicate_key_error @@ Printf.sprintf
"duplicate key \"%s\" overrides a value of type %s with a value of type %s"
p (type_string ov) (type_string value)
| None ->
let tbl = TomlTable [] in
let tbl = insert ~if_not_exists:if_not_exists ~append_table_arrays:append_table_arrays tbl ps value in
TomlTable (kvs @ [p, tbl])
end
| _ as v ->
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_string v) (type_string value) (Utils.string_of_path ps)
end
let rec value_of_node n =
match n with
| NodeInteger n -> TomlInteger (I.of_string n)
| NodeFloat x -> TomlFloat (F.of_string x)
| NodeString s -> TomlString s
| NodeBoolean b -> TomlBoolean (bool_of_string b)
| NodeOffsetDateTime dt -> TomlOffsetDateTime (D.offset_datetime_of_string dt)
| NodeLocalDateTime dt -> TomlLocalDateTime (D.local_datetime_of_string dt)
| NodeLocalDate d -> TomlLocalDate (D.local_date_of_string d)
| NodeLocalTime t -> TomlLocalTime (D.local_time_of_string t)
| NodeArray ns -> TomlArray (List.map value_of_node ns)
| NodeInlineTable ns ->
let ns = to_pairs ns in
let _, res = from_statements (TomlInlineTable []) [] [] ns in
res
| _ -> internal_error "table header or a non-inline table inside a value"
and from_statements toml parent_path seen_paths statements =
match statements with
| [] -> [], toml
| s :: ss ->
begin match s with
| Pair (k, v) ->
let full_path = parent_path @ k in
let value = value_of_node v in
let toml = insert toml full_path value in
let seen_paths = (TableHeader full_path) :: seen_paths in
from_statements toml parent_path seen_paths ss
| (TableHeader ks) as n ->
let () = check_duplicates seen_paths n in
let seen_paths = n :: seen_paths in
let toml = insert ~if_not_exists:true toml ks (TomlTable []) in
from_statements toml ks seen_paths ss
| (TableArrayHeader ks) as n ->
let () = check_duplicates seen_paths n in
let toml = insert ~if_not_exists:true toml ks (TomlTableArray []) in
if not (is_child_path ks parent_path) then
let toml = insert ~append_table_arrays:true toml ks (TomlTable []) in
let stmts, toml = from_statements toml ks [n] ss in
from_statements toml [] seen_paths stmts
else
from_statements toml ks (n :: seen_paths) ss
| _ -> internal_error "bare value in the AST"
end
let format_parse_error pos err =
match pos with
| Some (line, pos) ->
Printf.sprintf "Syntax error on line %d, character %d: %s" line pos err
| None ->
Printf.sprintf "Parse error: %s" err
let parse lexbuf =
let () = Toml_lexer.context_stack := [] in
let toml_statements = _parse lexbuf (Toml_parser.Incremental.toml_ast lexbuf.lex_curr_p) in
let tail_stmts, toml = from_statements (TomlTable []) [] [] toml_statements in
if tail_stmts <> [] then internal_error "from_statements left a non-empty tail"
else toml
let from_channel ic =
let lexbuf = Lexing.from_channel ic in
parse lexbuf
let from_file filename =
let ic = open_in filename in
let t = from_channel ic in
let () = close_in ic in
t
let from_string s =
let lexbuf = Lexing.from_string s in
parse lexbuf
let from_string_result s =
try Ok (from_string s)
with
| Parse_error (pos, err) -> Error (format_parse_error pos err)
| Failure err -> Error (Printf.sprintf "otoml internal error: %s" err)
let from_channel_result ic =
try Ok (from_channel ic)
with
| Parse_error (pos, err) -> Error (format_parse_error pos err)
| Sys_error err -> Error err
| Failure err -> Error (Printf.sprintf "otoml internal error: %s" err)
let from_file_result f =
try Ok (from_file f)
with
| Parse_error (pos, err) -> Error (format_parse_error pos err)
| Sys_error err -> Error err
| Failure err -> Error (Printf.sprintf "otoml internal error: %s" err)
end
end