Source file mreader_lexer.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
open Std
type keywords = Lexer_raw.keywords
type triple = Parser_raw.token * Lexing.position * Lexing.position
type item =
| Triple of triple
| Error of Lexer_raw.error * Location.t
type t =
{ keywords : keywords;
config : Mconfig.t;
source : Msource.t;
items : item list
}
let get_tokens keywords pos text =
let state = Lexer_raw.make keywords in
let lexbuf = Lexing.from_string text in
Lexing.move lexbuf pos;
let rec aux items = function
| Lexer_raw.Return (Parser_raw.COMMENT ) ->
continue (Comment comment :: items)
| Lexer_raw.Refill k -> aux items (k ())
| Lexer_raw.Return t ->
let triple = (t, lexbuf.Lexing.lex_start_p, lexbuf.Lexing.lex_curr_p) in
let items = Triple triple :: items in
if t = Parser_raw.EOF then items else continue items
| Lexer_raw.Fail (err, loc) -> continue (Error (err, loc) :: items)
and continue items = aux items (Lexer_raw.token state lexbuf) in
function
| [] ->
aux [] (Lexer_raw.skip_sharp_bang state lexbuf)
| items ->
continue items
let initial_position config =
{ Lexing.pos_fname = Mconfig.filename config;
pos_lnum = 1;
pos_bol = 0;
pos_cnum = 0
}
let make warnings keywords config source =
Msupport.catch_errors warnings (ref []) @@ fun () ->
let items =
get_tokens keywords (initial_position config) (Msource.text source) []
in
{ keywords; items; config; source }
let item_start = function
| Triple (_, s, _) -> s
| Comment (_, l) | Error (_, l) -> l.Location.loc_start
let item_end = function
| Triple (_, _, e) -> e
| Comment (_, l) | Error (_, l) -> l.Location.loc_end
let initial_position t = initial_position t.config
let rev_filter_map ~f lst =
let rec aux acc = function
| [] -> acc
| x :: xs ->
let acc =
match f x with
| Some x' -> x' :: acc
| None -> acc
in
aux acc xs
in
aux [] lst
let tokens t =
rev_filter_map t.items ~f:(function
| Triple t -> Some t
| _ -> None)
let keywords t = Lexer_raw.list_keywords t.keywords
let errors t =
rev_filter_map t.items ~f:(function
| Error (err, loc) -> Some (Lexer_raw.Error (err, loc))
| _ -> None)
let t =
rev_filter_map t.items ~f:(function
| Comment t -> Some t
| _ -> None)
open Parser_raw
let is_operator = function
| PREFIXOP s
| LETOP s
| ANDOP s
| INFIXOP0 s
| INFIXOP1 s
| INFIXOP2 s
| INFIXOP3 s
| INFIXOP4 s -> Some s
| BANG -> Some "!"
| PERCENT -> Some "%"
| PLUS -> Some "+"
| PLUSDOT -> Some "+."
| MINUS -> Some "-"
| MINUSDOT -> Some "-."
| STAR -> Some "*"
| EQUAL -> Some "="
| LESS -> Some "<"
| GREATER -> Some ">"
| OR -> Some "or"
| BARBAR -> Some "||"
| AMPERSAND -> Some "&"
| AMPERAMPER -> Some "&&"
| COLONEQUAL -> Some ":="
| PLUSEQ -> Some "+="
| _ -> None
let reconstruct_identifier_from_tokens tokens pos =
let rec look_for_component acc = function
| ((LIDENT _ | UIDENT _), _, _) :: ((BACKQUOTE | QUOTE), _, _) :: items ->
check acc items
| ((UIDENT _, _, _) as item) :: items -> look_for_dot (item :: acc) items
| ((LIDENT _, _, _) as item) :: items ->
if acc = [] then look_for_dot [ item ] items else check acc (item :: items)
| (RPAREN, _, _) :: ((token, _, _) as item) :: (LPAREN, _, _) :: items
when is_operator token <> None && acc = [] -> look_for_dot [ item ] items
| ((token, _, _) as item) :: items
when is_operator token <> None && acc = [] -> check [ item ] items
| _ :: items -> check acc items
| [] -> raise Not_found
and look_for_dot acc = function
| (DOT, _, _) :: items -> look_for_component acc items
| items -> check acc items
and check acc items =
if
acc <> []
&& (let startp =
match acc with
| (_, startp, _) :: _ -> startp
| _ -> assert false
in
Lexing.compare_pos startp pos <= 0)
&&
let endp =
match List.last acc with
| Some (_, _, endp) -> endp
| _ -> assert false
in
Lexing.compare_pos pos endp <= 0
then acc
else
match items with
| [] -> raise Not_found
| (_, _, endp) :: _ when Lexing.compare_pos endp pos < 0 ->
raise Not_found
| _ -> look_for_component [] items
in
match look_for_component [] tokens with
| exception Not_found -> []
| acc ->
let fmt (token, loc_start, loc_end) =
let id =
match token with
| UIDENT s | LIDENT s -> s
| _ -> (
match is_operator token with
| Some t -> t
| None -> assert false)
in
Location.mkloc id { Location.loc_start; loc_end; loc_ghost = false }
in
let before_pos = function
| _, s, _ -> Lexing.compare_pos s pos <= 0
in
List.map ~f:fmt (List.filter ~f:before_pos acc)
let reconstruct_identifier config source pos =
let rec lex acc lexbuf =
let token = Lexer_ident.token lexbuf in
let item = (token, lexbuf.Lexing.lex_start_p, lexbuf.Lexing.lex_curr_p) in
match token with
| EOF -> item :: acc
| EOL when Lexing.compare_pos lexbuf.Lexing.lex_curr_p pos > 0 ->
item :: acc
| EOL -> lex [] lexbuf
| _ -> lex (item :: acc) lexbuf
in
let lexbuf = Lexing.from_string (Msource.text source) in
Location.init lexbuf (Mconfig.filename config);
let tokens = lex [] lexbuf in
reconstruct_identifier_from_tokens tokens pos
let is_uppercase { Location.txt = x; _ } = x <> "" && Char.is_uppercase x.[0]
let rec drop_lowercase acc = function
| [ x ] -> List.rev (x :: acc)
| x :: xs when not (is_uppercase x) -> drop_lowercase [] xs
| x :: xs -> drop_lowercase (x :: acc) xs
| [] -> List.rev acc
let for_completion t pos =
let no_labels = ref false in
let check_label = function
| Triple ((LABEL _ | OPTLABEL _), _, _) -> no_labels := true
| _ -> ()
in
let rec aux acc = function
| item :: items when Lexing.compare_pos (item_start item) pos >= 0 ->
aux (item :: acc) items
| item :: _ when Lexing.compare_pos (item_end item) pos > 0 ->
check_label item;
raise Exit
| (Triple (token, _, loc_end) as item) :: _ as items
when Lexing.compare_pos pos loc_end = 0 ->
check_label item;
begin
match token with
| UIDENT _ | LIDENT _ -> raise Exit
| _ -> (acc, items)
end
| items -> (acc, items)
in
let t =
match aux [] t.items with
| exception Exit -> t
| acc, items ->
{ t with
items = List.rev_append acc (Triple (LIDENT "", pos, pos) :: items)
}
in
(!no_labels, t)
let identifier_suffix ident =
match List.last ident with
| Some x when is_uppercase x -> drop_lowercase [] ident
| _ -> ident