Source file ocamlc_loc.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
include Lexer
module List = ListLabels
type report =
{ loc : loc
; severity : severity
; message : string
; related : (loc * string) list
}
let dyn_of_code { code; name } =
let open Dyn in
record [ "code", int code; "name", string name ]
;;
let dyn_of_source =
let open Dyn in
function
| Code { code; name } -> record [ "code", int code; "name", string name ]
| Alert s -> string s
;;
let dyn_of_severity =
let open Dyn in
function
| Error w -> variant "Error" [ option dyn_of_source w ]
| Warning w -> variant "Warning" [ dyn_of_code w ]
| Alert { name; source } ->
variant "Alert" [ record [ "name", string name; "source", string source ] ]
;;
let dyn_of_loc { path; lines; chars } =
let open Dyn in
record
[ "path", string path
; ( "line"
, match lines with
| Single i -> variant "Single" [ int i ]
| Range (i, j) -> variant "Range" [ int i; int j ] )
; "chars", option (pair int int) chars
]
;;
let dyn_of_report { loc; message; related; severity } =
let open Dyn in
record
[ "loc", dyn_of_loc loc
; "message", string message
; "related", list (pair dyn_of_loc string) related
; "severity", dyn_of_severity severity
]
;;
module Tokens : sig
type t
val create : Lexing.lexbuf -> t
val peek : t -> Lexer.token
val junk : t -> unit
val push : t -> Lexer.token -> unit
val next : t -> Lexer.token
end = struct
type t =
{ lexbuf : Lexing.lexbuf
; mutable unread : Lexer.token list
}
let create lexbuf = { lexbuf; unread = [] }
let push t token = t.unread <- token :: t.unread
let next t =
match t.unread with
| [] -> Lexer.token t.lexbuf
| x :: xs ->
t.unread <- xs;
x
;;
let peek t =
match t.unread with
| x :: _ -> x
| [] ->
let token = Lexer.token t.lexbuf in
t.unread <- [ token ];
token
;;
let junk t =
match t.unread with
| _ :: xs -> t.unread <- xs
| _ -> ignore (Lexer.token t.lexbuf)
;;
end
let indent_of_severity = function
| Error _ -> String.length "Error: "
| Warning _ -> String.length "Warning: "
| Alert { name; source } ->
String.length "Alert :" + String.length name + String.length source + 1
;;
let severity tokens =
match Tokens.peek tokens with
| Line { contents; indent } ->
(match Lexer.severity (Lexing.from_string contents) with
| None -> raise Unknown_format
| Some (severity, new_contents) ->
Tokens.junk tokens;
let indent = indent_of_severity severity + indent in
Tokens.push tokens (Line { indent; contents = new_contents });
severity)
| _ -> raise Unknown_format
;;
let skip_excerpt =
let make_skip_excerpt tokens self lex =
match Tokens.peek tokens with
| Line { contents; indent = _ } ->
(match lex (Lexing.from_string contents) with
| `Continue ->
Tokens.junk tokens;
self tokens
| `Stop -> ())
| _ -> ()
in
let rec tail tokens = make_skip_excerpt tokens tail Lexer.skip_excerpt_tail in
let head tokens = make_skip_excerpt tokens tail Lexer.skip_excerpt_head in
head
;;
let rec acc_message tokens min_indent acc =
match Tokens.peek tokens with
| Line line ->
Tokens.junk tokens;
let min_indent = min min_indent line.indent in
acc_message tokens min_indent (line :: acc)
| _ ->
List.rev_map acc ~f:(fun { indent; contents } ->
let prefix = String.make (indent - min_indent) ' ' in
prefix ^ contents)
|> String.concat "\n"
|> String.trim
;;
let rec related tokens acc =
match Tokens.peek tokens with
| Loc { indent; message; loc } ->
if indent = 0
then List.rev acc
else (
Tokens.junk tokens;
let message = acc_message tokens indent [ { indent; contents = message } ] in
let acc = (loc, message) :: acc in
related tokens acc)
| _ -> List.rev acc
;;
let toplevel tokens =
match Tokens.next tokens with
| Loc { indent; message; loc } ->
if indent > 0 then raise Unknown_format;
skip_excerpt tokens;
let severity = severity tokens in
let indent = indent + indent_of_severity severity in
let message = acc_message tokens indent [ { indent; contents = message } ] in
let related = related tokens [] in
{ loc; severity; message; related }
| _ -> raise Unknown_format
;;
let parse s =
let lexbuf = Lexing.from_string s in
let tokens = Tokens.create lexbuf in
let rec loop acc =
match toplevel tokens with
| exception Unknown_format -> List.rev acc
| t -> loop (t :: acc)
in
loop []
;;
let dyn_of_raw =
Dyn.list (function
| `Loc loc -> dyn_of_loc loc
| `Message m -> Dyn.string m)
;;
let parse_raw s =
let lexbuf = Lexing.from_string s in
let tokens = Tokens.create lexbuf in
let rec loop acc =
match Tokens.peek tokens with
| Loc { loc; message; indent } ->
Tokens.junk tokens;
let acc = `Loc loc :: acc in
let message = acc_message tokens indent [ { contents = message; indent } ] in
let acc = `Message message :: acc in
loop acc
| Line line ->
Tokens.junk tokens;
let message = acc_message tokens line.indent [ line ] in
let acc = `Message message :: acc in
loop acc
| Eof ->
Tokens.junk tokens;
List.rev acc
in
loop []
;;