package reparse

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file reparse.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
(*-------------------------------------------------------------------------
 * Copyright (c) 2020 Bikal Gurung. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License,  v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 *
 * reparse v1.0.2
 *-------------------------------------------------------------------------*)
module R = Result
module String = StringLabels
open Sexplib0.Sexp_conv

type error = [ `Msg of string ] [@@deriving sexp_of]

type state = { src : input; len : int; offset : int; cc : current_char }

and input = [ `String of string | `Bigstring of Bigstringaf.t ]

and current_char = [ `Char of char | `Eof ]

type ('a, 'error) t = state -> (state * 'a, state * 'error) result

let msgf state fmt = Format.kasprintf (fun s -> R.error (state, `Msg s)) fmt

let pp_current_char fmt = function
  | `Char c -> Format.fprintf fmt "%c" c
  | `Eof -> Format.fprintf fmt "EOF"

let ( <|> ) p q state = match p state with Ok _ as o -> o | Error _ -> q state

let ( *> ) (p : (_, 'error) t) (q : ('a, 'error) t) state =
  R.bind (p state) (fun (state, _) -> q state)

let ( <* ) (p : ('a, 'error) t) (q : (_, 'error) t) state =
  R.bind (q state) (fun (state, _) -> p state)

let ( *>| ) p a state = R.map (fun (state, _) -> (state, a)) (p state)

let ( >>= ) t f state = R.bind (t state) (fun (state, a) -> f a state)

let ( >>| ) (t : ('a, 'error) t) (f : 'a -> 'b) state =
  R.map (fun (state, a) -> (state, f a)) (t state)

let ( >>|? ) t f state = R.map_error (fun (state, e) -> (state, f e)) (t state)

let ( >>*? ) t e state = R.map_error (fun (state, _) -> (state, e)) (t state)

let advance n state =
  let current_char offset =
    `Char
      ( match state.src with
      | `String src -> src.[offset]
      | `Bigstring src -> Bigstringaf.unsafe_get src offset )
  in
  if state.offset + n < state.len then
    let offset = state.offset + n in
    let state = { state with offset; cc = current_char offset } in
    R.ok (state, ())
  else
    let state = { state with offset = state.len; cc = `Eof } in
    R.ok (state, ())

let parse src t =
  let len =
    match src with
    | `String s -> String.length s
    | `Bigstring s -> Bigstringaf.length s
  in
  let state = { src; len; offset = -1; cc = `Eof } in
  R.bind (advance 1 state) (fun (state, ()) -> t state) |> function
  | Ok (_, a) -> Ok a
  | Error (_, e) -> Error e

let end_of_input state =
  let eof = match state.cc with `Char _ -> false | `Eof -> true in
  R.ok (state, eof)

let substring len state =
  if state.offset + len < state.len then
    ( match state.src with
    | `String src -> String.sub src ~pos:state.offset ~len
    | `Bigstring src -> Bigstringaf.substring src ~off:state.offset ~len )
    |> Option.some
  else None

let ok v state = R.ok (state, v)

let fail e state = R.error (state, e)

let char c state =
  if state.cc = `Char c then
    R.map (fun (state, ()) -> (state, c)) (advance 1 state)
  else
    msgf state "%d: char '%c' expected instead of '%a'" state.offset c
      pp_current_char state.cc

let char_if f state =
  match state.cc with
  | `Char c when f c ->
      R.map (fun (state, ()) -> (state, Some c)) (advance 1 state)
  | `Eof | `Char _ -> R.ok (state, None)

let satisfy f state =
  match state.cc with
  | `Char c when f c ->
      R.bind (advance 1 state) (fun (state, ()) -> R.ok (state, c))
  | `Char _ | `Eof ->
      msgf state "%d: satisfy is 'false' for char '%a'" state.offset
        pp_current_char state.cc

let peek_char state =
  let v = match state.cc with `Char c -> Some c | `Eof -> None in
  R.ok (state, v)

let peek_char_fail state =
  match state.cc with
  | `Char c -> R.ok (state, c)
  | `Eof -> msgf state "%d: peek_char_fail returned EOF" state.offset

let any_char state =
  match state.cc with
  | `Char c -> R.bind (advance 1 state) (fun (state, ()) -> R.ok (state, c))
  | `Eof -> msgf state "%d: any_char returned EOF" state.offset

let peek_string n state = R.ok (state, substring n state)

let string s state =
  let len = String.length s in
  match substring len state with
  | Some s2 ->
      if s = s2 then R.map (fun (state, ()) -> (state, s)) (advance len state)
      else msgf state "%d: string \"%s\" not found" state.offset s
  | None -> msgf state "%d: got EOF while parsing string \"%s\"" state.offset s

let string_if s state =
  let len = String.length s in
  match substring len state with
  | Some s2 ->
      if s = s2 then
        R.map (fun (state, ()) -> (state, Some s)) (advance len state)
      else R.ok (state, None)
  | None -> R.ok (state, None)

let rec skip_while f state =
  match satisfy f state with
  | Ok (state, _) -> skip_while f state
  | Error (state, _) -> ok () state

let count_skip_while f state =
  let rec loop count state =
    match satisfy f state with
    | Ok (state, _) -> loop (count + 1) state
    | Error (state, _) -> ok count state
  in
  loop 0 state

let count_skip_while_string n f =
  let rec loop count =
    peek_string n >>= function
    | Some s -> if f s then advance n *> loop (count + 1) else ok count
    | None -> ok count
  in
  loop 0

let take_while f state =
  let rec loop buf state =
    match satisfy f state with
    | Ok (state, c) ->
        Buffer.add_char buf c;
        loop buf state
    | Error (state, _) -> R.ok (state, Buffer.contents buf)
  in
  loop (Buffer.create 10) state

let many t state =
  let rec loop l state =
    match t state with
    | Ok (state, a) -> loop (a :: l) state
    | Error _ -> (state, l)
  in
  let state, v = loop [] state in
  ok (List.rev v) state

let count_skip_many t state =
  let rec loop count state =
    match t state with
    | Ok (state, _) -> loop (count + 1) state
    | Error _ -> (state, count)
  in
  let state, v = loop 0 state in
  ok v state

let take_while_n n f state =
  let rec loop count buf state =
    if count < n then
      match satisfy f state with
      | Ok (state, c) ->
          Buffer.add_char buf c;
          loop (count + 1) buf state
      | Error (state, _) -> R.ok (state, Buffer.contents buf)
    else R.ok (state, Buffer.contents buf)
  in
  loop 0 (Buffer.create n) state

let line state =
  let peek_2chars state =
    let c1 = state.cc in
    let c2 =
      match advance 1 state with
      | Ok (state2, ()) -> state2.cc
      | Error _ -> `Eof
    in
    (c1, c2)
  in
  let rec loop buf state =
    match peek_2chars state with
    | `Char '\r', `Char '\n' ->
        R.map
          (fun (state, ()) -> (state, Buffer.contents buf |> Option.some))
          (advance 2 state)
    | `Char '\n', _ ->
        R.map
          (fun (state, ()) -> (state, Buffer.contents buf |> Option.some))
          (advance 1 state)
    | `Char c1, _ ->
        Buffer.add_char buf c1;
        R.bind (advance 1 state) (fun (state, ()) -> loop buf state)
    | `Eof, _ -> R.ok (state, None)
  in
  loop (Buffer.create 1) state