_site/cover/scran_combinator.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
16 %% @doc Parser combinators.
17
18 -module(scran_combinator).
19
20 -feature(maybe_expr, enable).
21
22
23 -export([all_consuming/1]).
24 -export([condition/2]).
25 -export([condition/3]).
26 -export([eof/0]).
27 -export([failure/0]).
28 -export([ignore_result/1]).
29 -export([is_not/1]).
30 -export([map_parser/2]).
31 -export([map_result/2]).
32 -export([opt/1]).
33 -export([peek/1]).
34 -export([rest/0]).
35 -export([success/1]).
36 -export([value/2]).
37 -export_type([mapper/1]).
38 -include_lib("kernel/include/logger.hrl").
39
40
41 %% @doc Succeeds if all the input has been consumed by its child
42 %% parser.
43 %%
44
45 -spec all_consuming(scran:parser()) -> scran:parser().
46
47 all_consuming(Parser) ->
48 4 fun
49 (Input) ->
50 4 ?LOG_DEBUG(#{input => Input, parser => scran_debug:pp(Parser)}),
51 4 maybe
52 4 {_, Input} ?= Parser(Input)
53
54 else
55 _ ->
56 2 nomatch
57 end
58 end.
59
60
61 %% @doc Maps a function on the result of a parser.
62
63 -type mapper(O) :: fun((O) -> O).
64
65 -spec map_result(scran:parser(I, O), mapper(O)) -> scran:parser(I, O).
66
67 map_result(Parser, Function) ->
68 153 fun
69 (Input) ->
70 153 ?LOG_DEBUG(#{input => Input, parser => scran_debug:pp(Parser)}),
71 153 maybe
72 153 {Remaining, Result} ?= Parser(Input),
73 116 {Remaining, Function(Result)}
74 end
75 end.
76
77
78 %% @doc Ignore the result of the embedded parser.
79 %% @deprecated Please use {@link scran_result:ignore/1} instead.
80
81 -spec ignore_result(scran:parser()) -> scran:parser().
82
83 ignore_result(Parser) ->
84
:-(
scran_result:ignore(Parser).
85
86
87 %% @doc Applies a parser over the result of another one.
88
89 -spec map_parser(scran:parser(), scran:parser()) -> scran:parser().
90
91 map_parser(Parser, AppliedParser) ->
92 12 fun
93 (Input) ->
94 12 ?LOG_DEBUG(#{input => Input, parser => scran_debug:pp(Parser)}),
95 12 maybe
96 12 {Remaining, Result} ?= Parser(Input),
97 11 {_, AppliedResult} ?= AppliedParser(Result),
98 11 {Remaining, AppliedResult}
99 end
100 end.
101
102
103 %% @doc Optional parser, will return none if the option is not taken.
104
105 -spec opt(scran:parser()) -> scran:parser().
106
107 opt(Parser) ->
108 3 fun
109 (Input) ->
110 3 ?LOG_DEBUG(#{input => Input, parser => scran_debug:pp(Parser)}),
111 3 maybe
112 3 {_, _} ?= Parser(Input)
113 else
114 nomatch ->
115 1 {Input, none}
116 end
117 end.
118
119 %% @doc Returns the provided value if the child parser succeeds.
120
121 -spec value(any(), scran:parser()) -> scran:parser().
122
123 value(Return, Parser) ->
124 5 fun
125 (Input) ->
126 5 ?LOG_DEBUG(#{input => Input, parser => scran_debug:pp(Parser)}),
127 5 maybe
128 5 {Remaining, _} ?= Parser(Input),
129 4 {Remaining, Return}
130 end
131 end.
132
133
134 %% @doc Calls the parser if the condition is met.
135
136 -spec condition(function() | boolean(), scran:parser()) -> scran:parser().
137
138 condition(false, Parser) ->
139 10 fun
140 (Input) ->
141 7 ?LOG_DEBUG(#{input => Input, parser => scran_debug:pp(Parser)}),
142 7 {Input, none}
143 end;
144
145 condition(true, Parser) ->
146 12 fun
147 (Input) ->
148 9 ?LOG_DEBUG(#{input => Input, parser => scran_debug:pp(Parser)}),
149 9 maybe
150 9 {_, _} ?= Parser(Input)
151 end
152 end;
153
154 condition(F, Parser) when is_function(F) ->
155 5 fun
156 (Input) ->
157 5 ?LOG_DEBUG(#{f => F,
158 input => Input,
159 5 parser => scran_debug:pp(Parser)}),
160
161 5 (?FUNCTION_NAME(F(), Parser))(Input)
162 end.
163
164
165 -spec condition(function() | boolean(), scran:parser(), scran:parser()) -> scran:parser().
166
167 condition(true, Parser, _) ->
168 8 fun
169 (Input) ->
170 8 ?LOG_DEBUG(#{input => Input, parser => scran_debug:pp(Parser)}),
171 8 maybe
172 8 {_, _} ?= Parser(Input)
173 end
174 end;
175
176 condition(false, _, Parser) ->
177 8 fun
178 (Input) ->
179 8 ?LOG_DEBUG(#{input => Input, parser => scran_debug:pp(Parser)}),
180 8 maybe
181 8 {_, _} ?= Parser(Input)
182 end
183 end;
184
185 condition(F, TrueBranch, FalseBranch) when is_function(F) ->
186 8 fun
187 (Input) ->
188 8 ?LOG_DEBUG(#{f => F,
189 input => Input,
190 false_branch => scran_debug:pp(FalseBranch),
191 8 true_branch => scran_debug:pp(TrueBranch)}),
192
193 8 (?FUNCTION_NAME(F(), TrueBranch, FalseBranch))(Input)
194 end.
195
196
197
198 %% @doc Tries to apply its parser without consuming the input.
199
200 -spec peek(scran:parser()) -> scran:parser().
201
202 peek(Parser) ->
203 2 fun
204 (Input) ->
205 2 ?LOG_DEBUG(#{input => Input, parser => scran_debug:pp(Parser)}),
206 2 maybe
207 2 {_, Matched} ?= Parser(Input),
208 1 ?LOG_DEBUG(#{input => Input,
209 matched => Matched,
210 1 parser => scran_debug:pp(Parser)}),
211 1 {Input, Matched}
212 end
213 end.
214
215
216 %% @doc Returns its input if it is at the end of input data.
217
218 -spec eof() -> scran:parser().
219
220 eof() ->
221 3 fun
222 (Input) ->
223 3 case string:is_empty(Input) of
224 true ->
225 2 {Input, Input};
226
227 false ->
228 1 nomatch
229 end
230 end.
231
232
233 %% @doc Returns the remaining input.
234
235 -spec rest() -> scran:parser().
236
237 rest() ->
238 6 fun
239 (Input) when is_binary(Input) ->
240 2 {<<>>, Input};
241
242 (Input) ->
243 4 {[], Input}
244 end.
245
246
247 %% @doc Succeeds if the child parser returns an error.
248
249 -spec is_not(scran:parser()) -> scran:parser().
250
251 is_not(Parser) ->
252 4 fun
253 (Input) ->
254 4 ?LOG_DEBUG(#{input => Input, parser => scran_debug:pp(Parser)}),
255 4 case Parser(Input) of
256 nomatch when is_binary(Input) ->
257 2 {<<>>, Input};
258
259 nomatch ->
260 1 {"", Input};
261
262 {_Input, _Matched} ->
263 1 nomatch
264 end
265 end.
266
267
268 %% @doc A parser which always succeeds with given value without
269 %% consuming any input.
270
271 -spec success(any()) -> scran:parser().
272
273 success(Result) ->
274 4 fun
275 (Input) ->
276 4 {Input, Result}
277 end.
278
279
280 %% @doc A parser which always fails without consuming any input.
281
282 -spec failure() -> scran:parser().
283
284 failure() ->
285
:-(
fun
286 (_) ->
287
:-(
nomatch
288 end.
Line Hits Source