_site/cover/scran_multi.COVER.html

1 %% Copyright (c) 2023 Peter Morgan <peter.james.morgan@gmail.com>
2 %%
3 %% Licensed under the Apache License, Version 2.0 (the "License");
4 %% you may not use this file except in compliance with the License.
5 %% You may obtain a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing, software
10 %% distributed under the License is distributed on an "AS IS" BASIS,
11 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 %% See the License for the specific language governing permissions and
13 %% limitations under the License.
14
15 %% @doc Parser combinators applying their child parser multiple times.
16
17 -module(scran_multi).
18
19
20 -feature(maybe_expr, enable).
21
22
23 -export([count/2]).
24 -export([fold/4]).
25 -export([fold_many0/3]).
26 -export([fold_many1/3]).
27 -export([fold_many_m_n/5]).
28 -export([many1/1]).
29 -export([separated_list0/2]).
30 -export([separated_list1/2]).
31 -export_type([gatherer/0]).
32 -include_lib("kernel/include/logger.hrl").
33
34
35 -type gatherer() :: fun((term(), term()) -> term()).
36
37
38 %% @doc Run the embedded parser N times gathering the results in a
39 %% list.
40
41 -spec count(pos_integer() | scran:parser(scran:input(), pos_integer()), scran:parser()) -> scran:parser().
42
43 count(NumOfItemParser, ItemParser) when is_function(NumOfItemParser) ->
44 6 ?LOG_DEBUG(#{num_of_item_parser => NumOfItemParser,
45 6 parser => scran_debug:pp(ItemParser)}),
46 6 fun
47 (Input) ->
48 6 maybe
49 6 {Remainding, N} ?= NumOfItemParser(Input),
50 5 (?FUNCTION_NAME(N, ItemParser))(Remainding)
51 end
52 end;
53
54 count(N, Parser) when is_integer(N), N >= 0 ->
55 9 ?LOG_DEBUG(#{n => N, parser => scran_debug:pp(Parser)}),
56 9 fun
57 (Input) ->
58 9 ?FUNCTION_NAME(Parser, Input, N, [])
59 end.
60
61 count(Parser, Input, 0 = N, A) ->
62 5 ?LOG_DEBUG(#{input => Input,
63 a => A,
64 n => N,
65 5 parser => scran_debug:pp(Parser)}),
66 5 {Input, lists:reverse(A)};
67
68 count(Parser, Input, N, A) ->
69 16 ?LOG_DEBUG(#{input => Input,
70 a => A,
71 n => N,
72 16 parser => scran_debug:pp(Parser)}),
73 16 case Parser(Input) of
74 {Remaining, Result} ->
75 12 ?FUNCTION_NAME(Parser, Remaining, N - 1, [Result | A]);
76
77 nomatch ->
78 4 nomatch
79 end.
80
81
82 fold(NumOfItemParser, ItemParser, Initial, Gatherer) ->
83 6 ?LOG_DEBUG(#{num_of_items_parser => NumOfItemParser,
84 item_parser => scran_debug:pp(ItemParser),
85 initial => Initial,
86 6 gatherer => scran_debug:pp(Gatherer)}),
87 6 fun
88 (Input) ->
89 6 maybe
90 6 {Remainding, N} ?= NumOfItemParser(Input),
91 5 ?FUNCTION_NAME(Remainding, N, ItemParser, Initial, Gatherer)
92 end
93 end.
94
95 fold(Input, 0 = N, ItemParser, A, Gatherer) ->
96 3 ?LOG_DEBUG(#{input => Input,
97 n => N,
98 item_parser => scran_debug:pp(ItemParser),
99 a => A,
100 3 gatherer => scran_debug:pp(Gatherer)}),
101 3 {Input, A};
102
103 fold(Input, N, ItemParser, A, Gatherer) when N > 0 ->
104 6 ?LOG_DEBUG(#{input => Input,
105 n => N,
106 item_parser => scran_debug:pp(ItemParser),
107 a => A,
108 6 gatherer => scran_debug:pp(Gatherer)}),
109 6 maybe
110 6 {Remaining, Result} ?= ItemParser(Input),
111 4 ?FUNCTION_NAME(Remaining, N - 1, ItemParser, Gatherer(Result, A), Gatherer)
112 end.
113
114
115 %% @doc Repeats the embedded parser gathering the results.
116
117 -spec fold_many0(scran:parser(), term(), gatherer()) -> scran:parser().
118
119 fold_many0(Parser, Initial, Gatherer) ->
120 4 ?LOG_DEBUG(#{initial => Initial,
121 parser => scran_debug:pp(Parser),
122 4 gatherer => scran_debug:pp(Gatherer)}),
123
124 4 fun
125 (Input) ->
126 4 ?FUNCTION_NAME(Parser, Input, Initial, Gatherer)
127 end.
128
129 fold_many0(Parser, Input, A, Gatherer) ->
130 13 ?LOG_DEBUG(#{parser => scran_debug:pp(Parser),
131 input => Input,
132 a => A,
133 13 gatherer => scran_debug:pp(Gatherer)}),
134
135 13 case Parser(Input) of
136 {Remaining, Result} ->
137 9 ?FUNCTION_NAME(Parser, Remaining, Gatherer(Result, A), Gatherer);
138
139 nomatch ->
140 4 {Input, A}
141 end.
142
143
144 %% @doc Repeats the embedded parser gathering the results.
145
146 -spec fold_many1(scran:parser(), term(), gatherer()) -> scran:parser().
147
148 fold_many1(Parser, Initial, Gatherer) ->
149 4 ?LOG_DEBUG(#{initial => Initial,
150 parser => scran_debug:pp(Parser),
151 4 gatherer => scran_debug:pp(Gatherer)}),
152
153 4 fun
154 (Input) ->
155 4 ?FUNCTION_NAME(Parser, Input, 0, Initial, Gatherer)
156 end.
157
158 fold_many1(Parser, Input, I, A, Gatherer) ->
159 13 ?LOG_DEBUG(#{parser => scran_debug:pp(Parser),
160 input => Input,
161 i => I,
162 a => A,
163 13 gatherer => scran_debug:pp(Gatherer)}),
164
165 13 case Parser(Input) of
166 {Remaining, Result} ->
167 9 ?FUNCTION_NAME(Parser, Remaining, I + 1, Gatherer(Result, A), Gatherer);
168
169 nomatch when I == 0 ->
170 2 nomatch;
171
172 nomatch ->
173 2 {Input, A}
174 end.
175
176 %% @doc Repeats the embedded parser Minimum..=Maximum times, calling Gather to gather the results
177
178 -spec fold_many_m_n(non_neg_integer(), pos_integer(), scran:parser(), term(), gatherer()) -> scran:parser().
179
180 fold_many_m_n(Minimum, Maximum, Parser, Initial, Gatherer) when Minimum =< Maximum ->
181 16 ?LOG_DEBUG(#{minimum => Minimum,
182 maximum => Maximum,
183 parser => scran_debug:pp(Parser),
184 initial => Initial,
185 16 gatherer => scran_debug:pp(Gatherer)}),
186
187 16 fun
188 (Input) ->
189 16 ?FUNCTION_NAME(Minimum, Maximum, Parser, Input, 0, Initial, Gatherer)
190 end.
191
192 fold_many_m_n(Minimum, Maximum, Parser, Input, I, A, Gatherer) when Minimum < Maximum, I < Minimum ->
193 19 ?LOG_DEBUG(#{minimum => Minimum,
194 maximum => Maximum,
195 parser => scran_debug:pp(Parser),
196 input => Input,
197 i => I,
198 a => A,
199 19 gatherer => scran_debug:pp(Gatherer)}),
200
201 19 case Parser(Input) of
202 {Remaining, Result} ->
203 15 ?FUNCTION_NAME(Minimum,
204 Maximum,
205 Parser,
206 Remaining,
207 I + 1,
208 Gatherer(Result, A),
209 Gatherer);
210 nomatch ->
211 4 nomatch
212 end;
213
214 fold_many_m_n(Minimum, Maximum, Parser, Input, I, A, Gatherer) when I < Maximum ->
215 26 ?LOG_DEBUG(#{minimum => Minimum,
216 maximum => Maximum,
217 parser => scran_debug:pp(Parser),
218 input => Input,
219 i => I,
220 a => A,
221 26 gatherer => scran_debug:pp(Gatherer)}),
222
223 26 case Parser(Input) of
224 {Remaining, Result} when I < (Maximum - 1) ->
225 14 ?FUNCTION_NAME(Minimum,
226 Maximum,
227 Parser,
228 Remaining,
229 I + 1,
230 Gatherer(Result, A),
231 Gatherer);
232
233 {Remaining, Result} ->
234 6 {Remaining, Gatherer(Result, A)};
235
236 nomatch when Minimum =< I ->
237 2 {Input, A};
238
239 nomatch ->
240 4 nomatch
241 end.
242
243
244 %% @doc Runs the embedded parser at least once, gathering the results.
245
246 -spec many1(scran:parser(I, O)) -> scran:parser(I, [O, ...]).
247
248 many1(Parser) ->
249 17 fun
250 (Input) ->
251 20 ?FUNCTION_NAME(Parser, Input, [])
252 end.
253
254
255 -spec many1(scran:parser(I, O), I, [O]) -> scran:result(I, [O, ...]).
256
257 many1(Parser, Input, A) ->
258 91 ?LOG_DEBUG(#{input => Input,
259 a => A,
260 91 parser => scran_debug:pp(Parser)}),
261
262 91 case Parser(Input) of
263 {Remaing, Result} when Result /= none ->
264 71 ?FUNCTION_NAME(Parser, Remaing, [Result | A]);
265
266 nomatch when A == [] ->
267 3 nomatch;
268
269 nomatch ->
270 17 {Input, lists:reverse(A)}
271 end.
272
273
274 %% @doc Alternates between two parsers to produce a possibly empty
275 %% list of elements.
276
277 -spec separated_list0(scran:parser(I, any()),
278 scran:parser(I, Element)) -> scran:parser(I, [Element]).
279
280 separated_list0(SeparatorParser, ElementParser) ->
281 5 fun
282 (Input) ->
283 5 maybe
284 5 ?LOG_DEBUG(
285 #{input => Input,
286 separator_parser => scran_debug:pp(SeparatorParser),
287 5 element_parser => scran_debug:pp(ElementParser)}),
288
289 5 {SeparatorInput, Element} ?= ElementParser(Input),
290
291 3 ?LOG_DEBUG(#{separator_input => SeparatorInput,
292 3 element => Element}),
293
294 3 separated_list(SeparatorParser,
295 ElementParser,
296 SeparatorInput,
297 [Element])
298 else
299 nomatch ->
300 2 {Input, []}
301 end
302 end.
303
304
305 %% @doc Alternates between two parsers to produce a non-empty list of
306 %% elements.
307
308 -spec separated_list1(scran:parser(I, any()),
309 scran:parser(I, Element)) ->
310 scran:parser(I, [Element, ...]).
311
312 separated_list1(SeparatorParser, ElementParser) ->
313 6 fun
314 (Input) ->
315 6 maybe
316 6 ?LOG_DEBUG(
317 #{input => Input,
318 separator_parser => scran_debug:pp(SeparatorParser),
319 6 element_parser => scran_debug:pp(ElementParser)}),
320
321 6 {SeparatorInput, Element} ?= ElementParser(Input),
322
323 4 ?LOG_DEBUG(#{separator_input => SeparatorInput,
324 4 element => Element}),
325
326 4 separated_list(SeparatorParser,
327 ElementParser,
328 SeparatorInput,
329 [Element])
330 end
331 end.
332
333 separated_list(SeparatorParser, ElementParser, Input, A) ->
334 11 maybe
335 11 ?LOG_DEBUG(
336 #{input => Input,
337 separator_parser => scran_debug:pp(SeparatorParser),
338 a => A,
339 11 element_parser => scran_debug:pp(ElementParser)}),
340
341 11 {ElementInput, _} ?= SeparatorParser(Input),
342 6 {NextIterationInput, Element} ?= ElementParser(ElementInput),
343
344 4 ?LOG_DEBUG(#{element_input => ElementInput,
345 next_iteration_input => NextIterationInput,
346 4 element => Element}),
347
348 4 ?FUNCTION_NAME(SeparatorParser,
349 ElementParser,
350 NextIterationInput,
351 [Element | A])
352 else
353 nomatch ->
354 7 ?LOG_DEBUG(#{input => Input, a => lists:reverse(A)}),
355 7 {Input, lists:reverse(A)}
356 end.
Line Hits Source