forked from rossberg/1ml
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlexer.mll
More file actions
328 lines (294 loc) · 9.4 KB
/
Copy pathlexer.mll
File metadata and controls
328 lines (294 loc) · 9.4 KB
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
(*
* (c) 2014 Andreas Rossberg
*)
{
open Parser
type pos = {file : string; line : int; column : int}
type region = {left : pos; right : pos}
let column_pos pos =
pos.Lexing.pos_cnum - pos.Lexing.pos_bol
let convert_pos pos =
{ Source.file = pos.Lexing.pos_fname;
Source.line = pos.Lexing.pos_lnum;
Source.column = column_pos pos
}
let region lexbuf =
let left = convert_pos (Lexing.lexeme_start_p lexbuf) in
let right = convert_pos (Lexing.lexeme_end_p lexbuf) in
{Source.left = left; Source.right = right}
let error lexbuf m = raise (Source.Error (region lexbuf, m))
let error_nest start lexbuf m =
lexbuf.Lexing.lex_start_p <- start;
error lexbuf m
let convert_num s =
let n = ref 0 in
for i = 0 to String.length s - 1 do
n := !n*10 + Char.code s.[i] - Char.code '0'
done;
!n
let convert_escape = function
| 'n' -> '\n'
| 't' -> '\t'
| '\\' -> '\\'
| '\'' -> '\''
| '\"' -> '\"'
| _ -> assert false
let convert_char s =
if s.[0] <> '\\' then s.[0] else convert_escape s.[1]
let convert_text s =
let b = Buffer.create (String.length s) in
let i = ref 1 in
while !i < String.length s - 1 do
Buffer.add_char b
(if s.[!i] <> '\\' then s.[!i] else (incr i; convert_escape s.[!i]));
incr i
done;
Buffer.contents b
module Offside = struct
type 'a monad =
| Monad of ((Lexing.lexbuf -> Parser.token)
-> Lexing.lexbuf
-> (Parser.token * int) option
-> 'a result * (Parser.token * int) option)
and 'a result =
| Emit of Parser.token * 'a monad
| Return of 'a
let return value = Monad (fun _ _ tco -> (Return value, tco))
let unit = return ()
let rec (>>=) (Monad xM) xyM =
Monad (fun get_token lexbuf tco ->
match xM get_token lexbuf tco with
| (Emit (token, xM), tco) -> (Emit (token, xM >>= xyM), tco)
| (Return x, tco) -> let (Monad yM) = xyM x in yM get_token lexbuf tco)
let (>>) lhs rhs = lhs >>= fun () -> rhs
let get =
Monad (fun get_token lexbuf tco ->
(Return (match tco with
| Some tc -> tc
| None ->
let token = get_token lexbuf in
let column = column_pos (Lexing.lexeme_start_p lexbuf) in
(token, column)),
None))
let unget (token, column) =
Monad (fun get_token lexbuf tco ->
match tco with
| Some _ -> failwith "unget"
| None -> (Return (), Some (token, column)))
let error message = Monad (fun _ lexbuf _ -> error lexbuf message)
let emit token = Monad (fun _ _ tco -> (Emit (token, unit), tco))
let emit_if bool token = if bool then emit token else unit
let slack_of token =
match token with
| SYM text -> String.length text + 1
| _ -> 0
let expect expected =
get >>= fun (token, column) ->
if token <> expected then error "unexpected" else emit token
let rec inside_braces break insert indent (token, column) =
match token with
| EOF | RBRACE -> if token = break then emit token else error "unexpected"
| COMMA ->
if column < indent - 2 then error "offside" else
emit token >>
get >>= inside_braces break false indent
| _ ->
if column < indent then error "offside" else
emit_if (column = indent && insert) COMMA >>
nest (token, column) >>
get >>= inside_braces break (token <> LOCAL) indent
and inside_local insert indent (token, column) =
match token with
| IN ->
emit token
| COMMA ->
if column < indent - 2 then error "offside" else
emit token >>
get >>= inside_local false indent
| _ ->
if column < indent then
unget (token, column) >>
emit IN
else
emit_if (column = indent && insert) COMMA >>
nest (token, column) >>
get >>= inside_local (token <> LOCAL) indent
and inside_let insert indent (token, column) =
match token with
| IN ->
emit token >>
get >>= fun (token, column) ->
inside_in false column (token, column)
| COMMA ->
if column < indent - 2 then error "offside" else
emit token >>
get >>= inside_let false indent
| _ ->
if column < indent then
emit IN >>
inside_in false column (token, column)
else
emit_if (column = indent && insert) COMMA >>
nest (token, column) >>
get >>= inside_let (token <> LOCAL) indent
and inside_in insert indent (token, column) =
match token with
| RBRACE | COMMA | IN | EOF | RPAR | ELSE | THEN -> unget (token, column)
| SEMI ->
if column < indent - 2 then error "offside" else
emit token >>
get >>= inside_in false indent
| LET when column = indent ->
emit_if (column = indent && insert) SEMI >>
emit token >>
get >>= fun (token, column) ->
inside_let false column (token, column)
| _ ->
let slack = slack_of token in
if column < indent - slack then unget (token, column) else
emit_if (slack = 0 && column = indent && insert) SEMI >>
nest (token, column) >>
get >>= inside_in (slack = 0 && indent <= column) indent
and inside_parens indent (token, column) =
match token with
| RPAR -> emit token
| COMMA ->
emit token >>
get >>= inside_parens indent
| _ ->
nest (token, column) >>
get >>= inside_in false indent >>
get >>= inside_parens indent
and inside_if indent =
get >>= fun (token, column) ->
inside_in false column (token, column) >>
expect THEN >>
get >>= fun (token, column) ->
inside_in false column (token, column) >>
get >>= fun (token, column) ->
if token = ELSE && indent <= column then
emit token >>
get >>= fun (token, column) ->
if token = IF then
emit token >>
inside_if indent
else
inside_in false column (token, column)
else
emit ELSE >> emit LBRACE >> emit RBRACE >>
unget (token, column)
and nest (token, column) =
(match token with FUN | REC | IF -> emit LPAR | _ -> unit) >>
emit token >>
match token with
| LBRACE ->
get >>= fun (token, column) ->
inside_braces RBRACE false column (token, column)
| LPAR ->
get >>= fun (token, column) ->
inside_parens column (token, column)
| LET ->
get >>= fun (token, column) -> inside_let false column (token, column)
| LOCAL ->
get >>= fun (token, column) -> inside_local false column (token, column)
| DARROW ->
get >>= fun (token, column) ->
inside_in false column (token, column) >>
emit RPAR
| EQUAL | DO ->
get >>= fun (token, column) -> inside_in false column (token, column)
| IF ->
inside_if column >>
emit RPAR
| _ ->
unit
type state = (unit monad * (Parser.token * int) option) ref
let init () =
ref ((get >>= fun (token, column) ->
inside_braces EOF false column (token, column)),
None)
let token state get_token lexbuf =
let (Monad uM, tco) = !state in
match uM get_token lexbuf tco with
| (Emit (token, continue), tco) -> state := (continue, tco); token
| (Return (), _) -> failwith "return"
end
}
let space = [' ''\t']
let digit = ['0'-'9']
let letter = ['a'-'z''A'-'Z']
let symbol = ['+''-''*''/''\\''^''~''=''<''>''!''?''@''#''$''%''&''|'':''`']
let tick = '\''
let escape = ['n''t''\\''\'''\"']
let character = [^'"''\\''\n'] | '\\'escape
let num = digit+
let word = (letter | '_') (letter | digit | '_' | tick)*
let text = '"'character*'"'
let char = '\''character '\''
let eol = '\r'?'\n'
rule token = parse
| "_" { HOLE }
| "&&" { LOGICAL_AND }
| "as" { AS }
| "do" { DO }
| "else" { ELSE }
| "type_check" { TYPE_CHECK }
| "type_error" { TYPE_ERROR }
| "fun" { FUN }
| "if" { IF }
| "in" { IN }
| "fixity" { FIXITY }
| "..." { ELLIPSIS }
| "let" { LET }
| "||" { LOGICAL_OR }
| "wrap" { WRAP }
| "local" { LOCAL }
| "import" { IMPORT }
| "primitive" { PRIMITIVE }
| "rec" { REC }
| "then" { THEN }
| "type" { TYPE }
| "with" { WITH }
| "=" { EQUAL }
| ":" { COLON }
| ":>" { SEAL }
| ":@" { ROLL_OP }
| "@:" { UNROLL_OP }
| ":#" { WRAP_OP }
| "#:" { UNWRAP_OP }
| "->" { ARROW }
| "~>" { SARROW }
| "=>" { DARROW }
| "." { DOT }
| "'" { TICK }
| "(" { LPAR }
| ")" { RPAR }
| "{" { LBRACE }
| "}" { RBRACE }
| "," { COMMA }
| ";" { SEMI }
| word as s { WORD s }
| symbol* as s { SYM s }
| num as s { NUM (convert_num s) }
| char as s { CHAR (convert_char s) }
| '\''character(eol|eof) { error lexbuf "unclosed char literal" }
| '\''character '\\'_
{ error_nest (Lexing.lexeme_end_p lexbuf) lexbuf "illegal escape" }
| text as s { TEXT (convert_text s) }
| '"'character*(eol|eof) { error lexbuf "unclosed text literal" }
| '"'character*'\\'_
{ error_nest (Lexing.lexeme_end_p lexbuf) lexbuf "illegal escape" }
| ";;;;"_*eof { EOF }
| ";;"[^'\n''\r']*eof { EOF }
| ";;"[^'\n''\r']*eol { Lexing.new_line lexbuf; token lexbuf }
| "(;" { comment (Lexing.lexeme_start_p lexbuf) lexbuf; token lexbuf }
| space { token lexbuf }
| eol { Lexing.new_line lexbuf; token lexbuf }
| eof { EOF }
| _ { error lexbuf "illegal character" }
and comment start = parse
| ";)" { () }
| "(;" { comment (Lexing.lexeme_start_p lexbuf) lexbuf; comment start lexbuf }
| eol { Lexing.new_line lexbuf; comment start lexbuf }
| eof { error_nest start lexbuf "unclosed comment" }
| _ { comment start lexbuf }