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. |