Source file Matcher.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
open Types
open String
open Printf
open Re.Str
open Reason
module Token =
struct
type t = string * Msg.Coord.t
let toString (t, c) = sprintf "%s at %s" t (Msg.Coord.toString c)
let loc (t, c) = Msg.Locator.Interval (c, Msg.Coord.shift c t 0 (length t))
let repr = fst
end
let except str =
let n = String.length str - 1 in
let b = Buffer.create 64 in
Buffer.add_string b "\\(";
for i=0 to n do
Buffer.add_string b "\\(";
for j=0 to i-1 do
Buffer.add_string b (quote (String.sub str j 1))
done;
Buffer.add_string b (sprintf "[^%s]\\)" (quote (String.sub str i 1)));
if i < n then Buffer.add_string b "\\|"
done;
Buffer.add_string b "\\)*";
Buffer.contents b
let checkPrefix prefix s p =
try
for i=0 to (String.length prefix) - 1
do
if prefix.[i] <> s.[p+i] then raise (Invalid_argument "")
done;
true
with Invalid_argument _ -> false
module Skip =
struct
type t = string -> int -> [`Skipped of int | `Failed of string]
let start stop =
let pattern = regexp ((except start) ^ (quote stop)) in
let l = String.length start in
(fun s p ->
if checkPrefix start s p
then
if string_match pattern s (p+l) then `Skipped (p+(String.length (matched_string s))+l)
else `Failed (sprintf "unterminated comment ('%s' not detected)" stop)
else `Skipped p
)
let start stop =
let n = String.length start in
let m = String.length stop in
let d = regexp (sprintf "\\(%s\\)\\|\\(%s\\)" (quote start) (quote stop)) in
(fun s p ->
let inner p =
if checkPrefix start s p
then
let rec jnner p c =
try
let j = search_forward d s p in
let nest, l = (try ignore (matched_group 1 s); true, n with Not_found -> false, m) in
let c = if nest then c+1 else c-1 in
if c = 0
then `Skipped (j+l)
else jnner (j+l) c
with Not_found -> `Failed (sprintf "unterminated comment ('%s' not detected)" stop)
in
jnner (p+n) 1
else `Skipped p
in
inner p
)
let start =
let e = regexp ".*$" in
let n = String.length start in
(fun s p ->
if checkPrefix start s p
then
if string_match e s (p+n)
then `Skipped (p+n+(String.length (matched_string s)))
else `Skipped (String.length s)
else `Skipped p
)
let whitespaces symbols =
let e = regexp (sprintf "[%s]*" (quote symbols)) in
(fun s p ->
try
if string_match e s p
then `Skipped (p+(String.length (matched_string s)))
else `Skipped p
with Not_found -> `Skipped p
)
let create skippers =
let f =
List.fold_left
(fun acc g ->
(fun s p ->
match acc s p with
| `Skipped p -> g s p
| x -> x
)
)
(fun _s p -> `Skipped p)
skippers
in
(fun s p coord ->
let rec iterate s p =
match f s p with
| (`Skipped p') as x when p = p' -> x
| `Skipped p' -> iterate s p'
| x -> x
in
match iterate s p with
| `Skipped p' -> `Skipped (p', Msg.Coord.shift coord s p p')
| `Failed msg -> `Failed (Msg.make msg [||] (Msg.Locator.Point coord))
)
end
type aux = [`Skipped of int * Msg.Coord.t | `Failed of Msg.t | `Init]
let defaultSkipper = fun (p : int) (c : Msg.Coord.t) -> (`Skipped (p, c) :> [`Skipped of int * Msg.Coord.t | `Failed of Msg.t])
let of_string s =
let n = String.length s in
let rec loop i =
if i = n then [] else s.[i] :: loop (i + 1)
in
loop 0
let of_chars chars =
let buf = Buffer.create 16 in
List.iter (Buffer.add_char buf) chars;
Buffer.contents buf
class t (s : String.t) =
object (self : 'self)
val regexps = Hashtbl.create 256
val p = 0
val coord = (1, 1)
val skipper = defaultSkipper
val context : aux = `Init
method coord = coord
method line = fst coord
method col = snd coord
method skip = skipper
method pos = p
method str = s
method chrs = of_string s
method equal : 'self -> bool =
fun s' -> (s = s' # str) && (p = s' # pos)
method private changeSkip sk =
let newContext =
match context with
| `Failed msg -> `Failed msg
| `Init -> ((sk p coord) :> aux)
| `Skipped (p, coord) -> ((sk p coord) :> aux)
in {< skipper = sk; context = newContext >}
method private failed : 'b . String.t -> (int * int) -> ('self, 'b, Reason.t) result =
fun x c -> Failed (reason (Msg.make x [||] (Msg.Locator.Point c)))
method private proceed : 'b . (int -> (int * int) -> ('self, 'b, Reason.t) result) -> ('self, 'b, Reason.t) result =
fun f ->
match context with
| `Failed msg -> Failed (reason msg)
| `Init ->
(match self#skip p coord with
| `Skipped (p, coord) -> f p coord
| `Failed msg -> Failed (reason msg)
)
| `Skipped (p, coord) -> f p coord
method prefix n =
if p + n < String.length s
then String.sub s p n
else String.sub s p (String.length s - p)
method regexp : 'b . String.t -> String.t -> ('a -> 'self -> ('self, 'b, Reason.t) result) -> ('self, 'b, Reason.t) result =
fun name str -> self#get name
(try Hashtbl.find regexps str with Not_found ->
let regexp = Re.Str.regexp str in
Hashtbl.add regexps str regexp;
regexp
)
method get : 'b . String.t -> regexp -> ('a -> 'self -> ('self, 'b, Reason.t) result) -> ('self, 'b, Reason.t) result =
fun name regexp k -> self#proceed
(fun p coord ->
if string_match regexp s p
then
let m = matched_string s in
let l = length m in
let p = p + l in
let c = Msg.Coord.shift coord m 0 l in
k (m, coord) {< p = p; coord = c; context = ((self#skip p c) :> aux) >}
else self#failed (sprintf "\"%s\" expected" name) coord
)
method look : 'b. String.t -> ('a -> 'self -> ('self, 'b, Reason.t) result) -> ('self, 'b, Reason.t) result =
fun str k -> self#proceed
(fun p coord ->
try
let l = String.length str in
let m = String.sub s p l in
let p = p + l in
let c = Msg.Coord.shift coord m 0 (length m) in
if str = m
then k (m, coord) {< p = p; coord = c; context = ((self#skip p c) :> aux) >}
else self#failed (sprintf "\"%s\" expected" str) coord
with Invalid_argument _ -> self#failed (sprintf "\"%s\" expected" str) coord
)
method getEOF : 'b . ('a -> 'self -> ('self, 'b, Reason.t) result) -> ('self, 'b, Reason.t) result =
fun k -> self#proceed
(fun p coord ->
if p = length s
then k ("<EOF>", coord) {< p = p; coord = coord>}
else self#failed "<EOF> expected" coord
)
method loc = Msg.Locator.Point coord
end