Source file bcfg_stream.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
module Stream = Bcfg_type.Stream
type lexeme = Stream.lexeme = Ds of string | P of string | Os | Oe | De
let pp_lexeme = Stream.pp
type error =
[ `Lexer_error of
Bcfg_txtloc.t * [ `Invalid_character of char | `Message of string ]
| `Parser_error of Bcfg_txtloc.t * string ]
let pp_error ppf = function
| `Lexer_error (loc, `Invalid_character chr) ->
Format.fprintf ppf "%a: invalid character %C" Bcfg_txtloc.pp loc chr
| `Lexer_error (loc, `Message msg) ->
Format.fprintf ppf "%a: %s" Bcfg_txtloc.pp loc msg
| `Parser_error (loc, msg) ->
Format.fprintf ppf "%a: %s" Bcfg_txtloc.pp loc msg
let to_seq lexbuf : (lexeme, error) result Seq.t =
let read () =
try Ok (Bcfg_lexer.token lexbuf)
with Bcfg_lexer.Unexpected_character chr ->
Error
(`Lexer_error (Bcfg_txtloc.from_lexbuf lexbuf, `Invalid_character chr))
in
let perr msg = `Parser_error (Bcfg_txtloc.from_lexbuf lexbuf, msg) in
let fail e () = Seq.Cons (Error e, fun () -> Seq.Nil) in
let rec top () =
match read () with
| Error e -> fail e ()
| Ok Bcfg_parser.NEWLINE -> top ()
| Ok Bcfg_parser.EOF -> Seq.Nil
| Ok (Bcfg_parser.WORD d) -> directive d top ()
| Ok Bcfg_parser.LBRACE -> fail (perr "unexpected '{'") ()
| Ok Bcfg_parser.RBRACE -> fail (perr "unexpected '}'") ()
and directive d k () = Seq.Cons (Ok (Ds d), fun () -> params k ())
and params k () =
match read () with
| Error e -> fail e ()
| Ok (Bcfg_parser.WORD p) -> Seq.Cons (Ok (P p), fun () -> params k ())
| Ok Bcfg_parser.LBRACE -> Seq.Cons (Ok Os, fun () -> children k ())
| Ok Bcfg_parser.NEWLINE -> Seq.Cons (Ok De, k)
| Ok Bcfg_parser.EOF -> Seq.Cons (Ok De, fun () -> Seq.Nil)
| Ok Bcfg_parser.RBRACE -> fail (perr "unexpected '}' after a directive") ()
and children k () =
match read () with
| Error e -> fail e ()
| Ok Bcfg_parser.NEWLINE -> children k ()
| Ok (Bcfg_parser.WORD d) -> directive d (fun () -> children k ()) ()
| Ok Bcfg_parser.RBRACE ->
Seq.Cons (Ok Oe, fun () -> Seq.Cons (Ok De, k))
| Ok Bcfg_parser.EOF -> fail (perr "unterminated children block '{'") ()
| Ok Bcfg_parser.LBRACE -> fail (perr "unexpected '{'") ()
in
top
type decoder = { mutable seq : (lexeme, error) result Seq.t }
let decoder lexbuf = { seq = to_seq lexbuf }
let decode d =
match d.seq () with
| Seq.Nil -> Ok None
| Seq.Cons (Ok lx, seq) ->
d.seq <- seq;
Ok (Some lx)
| Seq.Cons (Error e, _) ->
d.seq <- Seq.empty;
Error e
let encode ?(cfg = Bcfg_out.config ()) next sink =
let w = Bcfg_out.lexeme_emitter ~cfg next (Bcfg_out.Writer.ctx ()) in
Seq.iter sink (Bcfg_out.Writer.to_seq w)
let to_string ?(cfg = Bcfg_out.config ()) seq =
let s = ref seq in
let next () =
match !s () with
| Seq.Nil -> None
| Seq.Cons (x, r) ->
s := r;
Some x
in
let w = Bcfg_out.lexeme_emitter ~cfg next (Bcfg_out.Writer.ctx ()) in
Bcfg_out.Writer.to_seq w
let of_t (t : Bcfg_type.t) : lexeme Seq.t =
let rec dirs ds k () =
match ds with [] -> k () | d :: ds -> dir d (fun () -> dirs ds k ()) ()
and dir d k () =
Seq.Cons (Ds d.Bcfg_type.name, fun () -> params d.parameters d.children k ())
and params ps children k () =
match ps with
| p :: ps -> Seq.Cons (P p, fun () -> params ps children k ())
| [] -> body children k ()
and body children k () =
match children with
| [] -> Seq.Cons (De, k)
| cs ->
Seq.Cons
( Os,
fun () ->
dirs cs (fun () -> Seq.Cons (Oe, fun () -> Seq.Cons (De, k))) ()
)
in
fun () -> dirs t (fun () -> Seq.Nil) ()
let nowhere = Bcfg_txtloc.Nowhere
let to_t seq =
let result = ref [] in
let stack = ref [] in
let push_child d =
match !stack with
| (_, _, cs) :: _ -> cs := d :: !cs
| [] -> result := d :: !result
in
let finish (name, ps, cs) =
{ Bcfg_type.name; parameters = List.rev !ps; children = List.rev !cs }
in
let rec go seq =
match seq () with
| Seq.Nil ->
begin match !stack with
| [] -> Ok (List.rev !result)
| _ -> Error (`Parser_error (nowhere, "unterminated directive"))
end
| Seq.Cons (Ds name, seq) ->
stack := (name, ref [], ref []) :: !stack;
go seq
| Seq.Cons (P p, seq) ->
begin match !stack with
| (_, ps, _) :: _ ->
ps := p :: !ps;
go seq
| [] ->
Error (`Parser_error (nowhere, "parameter outside of a directive"))
end
| Seq.Cons ((Os | Oe), seq) -> go seq
| Seq.Cons (De, seq) ->
begin match !stack with
| top :: rest ->
stack := rest;
push_child (finish top);
go seq
| [] -> Error (`Parser_error (nowhere, "unbalanced end of directive"))
end
in
go seq
let to_directives lexbuf =
let lexemes = to_seq lexbuf in
let rec go acc depth seq () =
match seq () with
| Seq.Nil ->
begin match acc with
| [] -> Seq.Nil
| _ ->
let err = `Parser_error (nowhere, "unterminated directive") in
Seq.Cons (Error err, fun () -> Seq.Nil)
end
| Seq.Cons (Error e, _) -> Seq.Cons (Error e, fun () -> Seq.Nil)
| Seq.Cons (Ok lx, seq) -> begin
let depth =
match lx with Os -> depth + 1 | Oe -> depth - 1 | _ -> depth
in
let acc = lx :: acc in
match lx with
| De when depth = 0 ->
begin match to_t (List.to_seq (List.rev acc)) with
| Ok [ d ] -> Seq.Cons (Ok d, go [] 0 seq)
| Ok _ ->
let err =
`Parser_error (nowhere, "expected a single directive")
in
Seq.Cons (Error err, fun () -> Seq.Nil)
| Error e -> Seq.Cons (Error e, fun () -> Seq.Nil)
end
| _ -> go acc depth seq ()
end
in
fun () -> go [] 0 lexemes ()