Source file message_file.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
open Fix.Indexing
open Utils
open Misc
open Kernel.Info
open Interpreter
let classify_line txt =
let is_whitespace = function ' ' | '\t' -> true | _ -> false in
let l = String.length txt in
let i = ref 0 in
while !i < l && is_whitespace txt.[!i] do
incr i
done;
if !i = l then
`Whitespace
else if txt.[!i] <> '#' then
`Text
else if !i + 1 < l && txt.[!i+1] = '#' then
`Autocomment
else
`Comment (String.sub txt (!i + 1) (l - !i - 1))
type 'a line =
| Text of 'a
type 'a file =
| Mixed of 'a
let rec lines () =
let prepare block =
if List.for_all (function Comment _ -> true | Text _ -> false) block then
let prj = function Comment cmt -> cmt | Text _ -> assert false in
Comments (List.rev_map prj block)
else
Mixed (List.rev block)
in
let rec aux acc lines =
match lines () with
| Seq.Nil ->
if list_is_empty acc then
Seq.Nil
else
Seq.Cons (prepare acc, extract_pre_block lines)
| Seq.Cons (line, lines) ->
match classify_line line with
| `Whitespace ->
Seq.Cons (prepare acc, extract_pre_block lines)
| `Autocomment ->
aux acc lines
| `Comment ->
aux (Comment comment :: acc) lines
| `Text ->
aux (Text line :: acc) lines
in
aux [] lines
type 'sentence block = {
sentences: 'sentence line list;
comments: string list list;
message: string line list;
}
let rec pblocks () =
let rec sentences pblocks =
match pblocks () with
| Seq.Nil ->
Printf.eprintf "error: last sentences without message in .messages file\n";
exit 1
| Seq.Cons (Comments , pblocks') ->
extract_message sentences (comments' :: comments) pblocks'
| Seq.Cons (Mixed message, pblocks') ->
let = List.rev comments in
Seq.Cons (Mixed {sentences; comments; message}, extract_block pblocks')
in
match pblocks () with
| Seq.Nil ->
Seq.Nil
| Seq.Cons (Comments _ as comm, pblocks') ->
Seq.Cons (comm, extract_block pblocks')
| Seq.Cons (Mixed sentences, pblocks') ->
extract_message sentences [] pblocks'
let map_block f = function
| Comments _ as cmts -> cmts
| Mixed block ->
Mixed (f block)
let map_line f = function
| Comment _ as cmt -> cmt
| Text txt -> Text (f txt)
let parse_sentence (type g) (g : g grammar) =
let action_table : (g lr1 index * g terminal index, _) Hashtbl.t = Hashtbl.create 7 in
let get_action state terminal =
match Lr1.default_reduction g state with
| Some prod -> `Reduce prod
| None ->
let key = (state, terminal) in
match Hashtbl.find_opt action_table key with
| Some action -> action
| None ->
let action =
match
IndexSet.find
(fun red -> IndexSet.mem terminal (Reduction.lookaheads g red))
(Reduction.from_lr1 g state)
with
| red -> `Reduce (Reduction.production g red)
| exception Not_found ->
let sym = Symbol.inj_t g terminal in
match
IndexSet.find
(fun tr -> Index.equal sym (Transition.symbol g tr))
(Transition.successors g state)
with
| tr -> `Shift (Transition.target g tr)
| exception Not_found ->
`Reject
in
Hashtbl.add action_table key action;
action
in
fun {entrypoint; symbols} ->
let rec consume_terminal stack (t, startp, endp as token) =
let (state, _, currp) = List.hd stack in
match get_action state t with
| `Reject -> Result.Error stack
| `Shift state -> Result.Ok ((state, startp, endp) :: stack)
| `Reduce prod ->
let (stack, startp', endp') =
match Production.length g prod with
| 0 -> (stack, currp, currp)
| n ->
let (_, _, endp) = List.hd stack in
let stack = list_drop (n - 1) stack in
let (_, startp, _) = List.hd stack in
let stack = List.tl stack in
(stack, startp, endp)
in
let (state, _, _) = List.hd stack in
let state' = Transition.find_goto_target g state (Production.lhs g prod) in
let stack = (state', startp', endp') :: stack in
consume_terminal stack token
in
let rec loop stack ts =
match ts () with
| Seq.Nil -> (stack, stack, Seq.empty)
| Seq.Cons (t, ts') as ts0 ->
match consume_terminal stack t with
| Result.Ok stack' -> loop stack' ts'
| Result.Error stack' -> (stack, stack', fun () -> ts0)
in
let entrypoint = match entrypoint with
| None -> (IndexSet.choose (Lr1.entrypoints g), Lexing.dummy_pos, Lexing.dummy_pos)
| Some lhs -> lhs
in
let _canonical_stack, intermediate_stack, _remainder =
loop [entrypoint] (List.to_seq symbols)
in
let state, _, _ = List.hd intermediate_stack in
state
let wrap_lines prefix newline mid_suffix suffix = function
| [] -> []
| first :: rest ->
match List.rev rest with
| [] -> [prefix ^ first ^ suffix]
| last :: mid ->
(prefix ^ first ^ mid_suffix) ::
List.rev_map (fun mid -> newline ^ mid ^ mid_suffix) mid @
[newline ^ last ^ suffix]
let state_to_pattern g lr1 =
let items = Kernel.Coverage.string_of_items_for_filter g (Lr1.to_lr0 g lr1) in
match Lr1.incoming g lr1 with
| Some sym when Symbol.is_nonterminal g sym ->
wrap_lines "| [_* /" " /" "" "]" items
| _ ->
wrap_lines "| /" " /" "" "" items
let fold_consecutive ~ ~text lines acc =
let rec acc lines = function
| [] -> comment (List.rev lines) acc
| Comment line :: rest ->
comments acc (line :: lines) rest
| Text line :: rest ->
texts (comment (List.rev lines) acc) [line] rest
and texts acc lines = function
| [] -> text (List.rev lines) acc
| Comment line :: rest ->
comments (text (List.rev lines) acc) [line] rest
| Text line :: rest ->
texts acc (line :: lines) rest
in
match lines with
| [] -> acc
| Comment line :: rest ->
comments acc [line] rest
| Text line :: rest ->
texts acc [line] rest
let block_to_lines g = function
| Comments ->
wrap_lines "(* " " " "" " *)" comments
| Mixed {sentences; ; message} ->
let sentences =
fold_consecutive
~comment:(fun lines acc -> wrap_lines " (* " " " "" " *)" lines :: acc)
~text:(fun states acc -> List.concat_map (state_to_pattern g) states :: acc)
sentences []
in
let =
List.rev_map
(fun lines -> [""] @ wrap_lines " (* " " " "" " *)" lines @ [""])
comments
in
let message =
fold_consecutive
~comment:(fun lines acc -> wrap_lines " (* " " " "" " *)" lines :: acc)
~text:(fun lines acc ->
let lines = List.mapi (fun i line ->
let line = String.escaped line in
if i = 0
then line
else if line <> "" && line.[0] = ' '
then "\\" ^ line
else " " ^ line
) lines in
wrap_lines " { \"" " " "\\n\\" "\" }" lines :: acc
)
message []
in
List.concat (List.rev_append sentences (List.rev_append comments (List.rev message)))
let blocks_to_file g ~shortest blocks ()=
let prepare i block =
let lines = block_to_lines g block in
let lines = if i = 0 then lines else "" :: lines in
List.to_seq lines
in
let body = Seq.concat (seq_mapi prepare blocks) in
let body =
if shortest then
fun () -> Seq.Cons ("| %shortest [", Seq.append body (seq_singleton "]"))
else
body
in
Seq.Cons ("rule error_messages = parse error", body)