31 | using parse_t = typename pair_parse_t::first_type;
32 |
33 | //----------------------------------------------------------------------------
34 | // parsers as monads
35 |
36 | // fmap a function into a parser. F :: parse_t
-> a
37 | template
38 | constexpr auto fmap(F&& f, P&& p)
39 | {
40 | using R = parse_result_t)>>;
41 | return [f = std::forward(f),
42 | p = std::forward(p)] (parse_input_t i) -> R {
43 | const auto r = p(i);
44 | if (!r) return std::nullopt;
45 | return R(cx::make_pair(f(r->first), r->second));
46 | };
47 | }
48 |
49 | // bind a function into a parser. F :: (parse_t
, parse_input_t) -> a
50 | template
51 | constexpr auto bind(P&& p, F&& f)
52 | {
53 | using R = std::result_of_t, parse_input_t)>;
54 | return [=] (parse_input_t i) -> R {
55 | const auto r = p(i);
56 | if (!r) return std::nullopt;
57 | return f(r->first, r->second);
58 | };
59 | }
60 |
61 | // lift a value into a parser
62 | template
63 | constexpr auto lift(T&& t)
64 | {
65 | return [t = std::forward(t)] (parse_input_t s) {
66 | return parse_result_t(
67 | cx::make_pair(std::move(t), s));
68 | };
69 | }
70 |
71 | // the parser that always fails
72 | template
73 | constexpr auto fail(T)
74 | {
75 | return [=] (parse_input_t) -> parse_result_t {
76 | return std::nullopt;
77 | };
78 | }
79 |
80 | template
81 | constexpr auto fail(T, ErrorFn f)
82 | {
83 | return [=] (parse_input_t) -> parse_result_t {
84 | f();
85 | return std::nullopt;
86 | };
87 | }
88 |
89 | //----------------------------------------------------------------------------
90 | // parser combinators
91 |
92 | // alternation: try the first parser, and if it fails, do the second. They
93 | // must both return the same type.
94 | template , parse_t>>>
96 | constexpr auto operator|(P1&& p1, P2&& p2) {
97 | return [=] (parse_input_t i) {
98 | const auto r1 = p1(i);
99 | if (r1) return r1;
100 | return p2(i);
101 | };
102 | }
103 |
104 | // accumulation: run two parsers in sequence and combine the outputs using the
105 | // given function. Both parsers must succeed.
106 | template , parse_t)>>
108 | constexpr auto combine(P1&& p1, P2&& p2, F&& f) {
109 | return [=] (parse_input_t i) -> parse_result_t {
110 | const auto r1 = p1(i);
111 | if (!r1) return std::nullopt;
112 | const auto r2 = p2(r1->second);
113 | if (!r2) return std::nullopt;
114 | return parse_result_t(
115 | cx::make_pair(f(r1->first, r2->first), r2->second));
116 | };
117 | }
118 |
119 | // for convenience, overload < and > to mean sequencing parsers
120 | // and returning the result of the chosen one
121 | template , typename = parse_t>
123 | constexpr auto operator<(P1&& p1, P2&& p2) {
124 | return combine(std::forward(p1),
125 | std::forward(p2),
126 | [] (auto, const auto& r) { return r; });
127 | }
128 |
129 | template , typename = parse_t>
131 | constexpr auto operator>(P1&& p1, P2&& p2) {
132 | return combine(std::forward(p1),
133 | std::forward(p2),
134 | [] (const auto& r, auto) { return r; });
135 | }
136 |
137 | // apply ? (zero or one) of a parser
138 | template
139 | constexpr auto zero_or_one(P&& p)
140 | {
141 | using R = parse_result_t;
142 | return [p = std::forward(p)] (parse_input_t s) -> R {
143 | const auto r = p(s);
144 | if (r) return r;
145 | return R(cx::make_pair(parse_input_t(s.data(), 0), s));
146 | };
147 | }
148 |
149 | namespace detail
150 | {
151 | template
152 | constexpr cx::pair accumulate_parse(
153 | parse_input_t s, P&& p, T init, F&& f)
154 | {
155 | while (!s.empty()) {
156 | const auto r = p(s);
157 | if (!r) return cx::make_pair(init, s);
158 | init = f(init, r->first);
159 | s = r->second;
160 | }
161 | return cx::make_pair(init, s);
162 | }
163 |
164 | template
165 | constexpr cx::pair accumulate_n_parse(
166 | parse_input_t s, P&& p, std::size_t n, T init, F&& f)
167 | {
168 | while (n != 0) {
169 | const auto r = p(s);
170 | if (!r) return cx::make_pair(init, s);
171 | init = f(init, r->first);
172 | s = r->second;
173 | --n;
174 | }
175 | return cx::make_pair(init, s);
176 | }
177 | }
178 |
179 | // apply * (zero or more) of a parser, accumulating the results according to a
180 | // function F. F :: T -> (parse_t, parse_input_t) -> T
181 | template
182 | constexpr auto many(P&& p, T&& init, F&& f)
183 | {
184 | return [p = std::forward(p), init = std::forward(init),
185 | f = std::forward(f)] (parse_input_t s) {
186 | return parse_result_t(
187 | detail::accumulate_parse(s, p, init, f));
188 | };
189 | }
190 |
191 | // apply + (one or more) of a parser, accumulating the results according to a
192 | // function F. F :: T -> (parse_t, parse_input_t) -> T
193 | template
194 | constexpr auto many1(P&& p, T&& init, F&& f)
195 | {
196 | return [p = std::forward(p), init = std::forward(init),
197 | f = std::forward(f)] (parse_input_t s) -> parse_result_t {
198 | const auto r = p(s);
199 | if (!r) return std::nullopt;
200 | return parse_result_t(
201 | detail::accumulate_parse(r->second, p, f(init, r->first), f));
202 | };
203 | }
204 |
205 | // apply a parser exactly n times, accumulating the results according to a
206 | // function F. F :: T -> (parse_t, parse_input_t) -> T
207 | template
208 | constexpr auto exactly_n(P&& p, std::size_t n, T&& init, F&& f)
209 | {
210 | return [p = std::forward(p), n, init = std::forward(init),
211 | f = std::forward(f)] (parse_input_t s) {
212 | return parse_result_t(
213 | detail::accumulate_n_parse(s, p, n, init, f));
214 | };
215 | }
216 |
217 | // try to apply a parser, and if it fails, return a default
218 | template >
219 | constexpr auto option(T&& def, P&& p)
220 | {
221 | return [p = std::forward(p),
222 | def = std::forward(def)] (parse_input_t s) {
223 | const auto r = p(s);
224 | if (r) return r;
225 | return parse_result_t(cx::make_pair(def, s));
226 | };
227 | }
228 |
229 | // apply many1 instances of a parser, with another parser interleaved.
230 | // accumulate the results with the given function.
231 | template
232 | constexpr auto separated_by(P1&& p1, P2&& p2, F&& f)
233 | {
234 | using T = parse_t;
235 | return [p1 = std::forward(p1), p2 = std::forward(p2),
236 | f = std::forward(f)] (
237 | parse_input_t s) -> parse_result_t {
238 | const auto r = p1(s);
239 | if (!r) return std::nullopt;
240 | const auto p = p2 < p1;
241 | return parse_result_t(
242 | detail::accumulate_parse(r->second, p, r->first, f));
243 | };
244 | }
245 |
246 | // apply many instances of a parser, with another parser interleaved.
247 | // accumulate the results with the given function.
248 | template
249 | constexpr auto separated_by(P1&& p1, P2&& p2, F0&& init, F&& f)
250 | {
251 | using R = parse_result_t>;
252 | return [p1 = std::forward(p1), p2 = std::forward(p2),
253 | init = std::forward(init), f = std::forward(f)] (
254 | parse_input_t s) -> R {
255 | const auto r = p1(s);
256 | if (!r) return R(cx::make_pair(init(), s));
257 | const auto p = p2 < p1;
258 | return R(detail::accumulate_parse(r->second, p, f(init(), r->first), f));
259 | };
260 | }
261 |
262 | // apply many instances of a parser, with another parser interleaved.
263 | // accumulate the results with the given function.
264 | template
265 | constexpr auto separated_by_val(P1&& p1, P2&& p2, T&& init, F&& f)
266 | {
267 | using R = parse_result_t>;
268 | return [p1 = std::forward(p1), p2 = std::forward(p2),
269 | init = std::forward(init), f = std::forward(f)] (
270 | parse_input_t s) -> R {
271 | const auto r = p1(s);
272 | if (!r) return R(cx::make_pair(init, s));
273 | const auto p = p2 < p1;
274 | return R(detail::accumulate_parse(r->second, p, f(init, r->first), f));
275 | };
276 | }
277 |
278 | //----------------------------------------------------------------------------
279 | // parsers for various types
280 |
281 | // parse a given char
282 | constexpr auto make_char_parser(char c)
283 | {
284 | return [=] (parse_input_t s) -> parse_result_t {
285 | if (s.empty() || s[0] != c) return std::nullopt;
286 | return parse_result_t(
287 | cx::make_pair(c, parse_input_t(s.data()+1, s.size()-1)));
288 | };
289 | }
290 |
291 | // parse one of a set of chars
292 | constexpr auto one_of(std::string_view chars)
293 | {
294 | return [=] (parse_input_t s) -> parse_result_t {
295 | if (s.empty()) return std::nullopt;
296 | // basic_string_view::find is supposed to be constexpr, but no...
297 | auto j = cx::find(chars.cbegin(), chars.cend(), s[0]);
298 | if (j != chars.cend()) {
299 | return parse_result_t(
300 | cx::make_pair(s[0], parse_input_t(s.data()+1, s.size()-1)));
301 | }
302 | return std::nullopt;
303 | };
304 | }
305 |
306 | // parse none of a set of chars
307 | constexpr auto none_of(std::string_view chars)
308 | {
309 | return [=] (parse_input_t s) -> parse_result_t {
310 | if (s.empty()) return std::nullopt;
311 | // basic_string_view::find is supposed to be constexpr, but no...
312 | auto j = cx::find(chars.cbegin(), chars.cend(), s[0]);
313 | if (j == chars.cend()) {
314 | return parse_result_t(
315 | cx::make_pair(s[0], parse_input_t(s.data()+1, s.size()-1)));
316 | }
317 | return std::nullopt;
318 | };
319 | }
320 |
321 | // parse a given string
322 | constexpr auto make_string_parser(std::string_view str)
323 | {
324 | return [=] (parse_input_t s) -> parse_result_t {
325 | const auto p = cx::mismatch(str.cbegin(), str.cend(), s.cbegin(), s.cend());
326 | if (p.first == str.cend()) {
327 | // std::distance is not constexpr?
328 | const auto len = static_cast(
329 | s.cend() - p.second);
330 | return parse_result_t(
331 | cx::make_pair(str, parse_input_t(p.second, len)));
332 | }
333 | return std::nullopt;
334 | };
335 | }
336 |
337 | // parse an int (may begin with 0)
338 | constexpr auto int0_parser()
339 | {
340 | using namespace std::literals;
341 | return many1(one_of("0123456789"sv),
342 | 0,
343 | [] (int acc, char c) { return (acc*10) + (c-'0'); });
344 | }
345 |
346 | // parse an int (may not begin with 0)
347 | constexpr auto int1_parser()
348 | {
349 | using namespace std::literals;
350 | return bind(one_of("123456789"sv),
351 | [] (char x, parse_input_t rest) {
352 | return many(one_of("0123456789"sv),
353 | static_cast(x - '0'),
354 | [] (int acc, char c) { return (acc*10) + (c-'0'); })(rest);
355 | });
356 | }
357 |
358 | // a parser for skipping whitespace
359 | constexpr auto skip_whitespace()
360 | {
361 | constexpr auto ws_parser =
362 | make_char_parser(' ')
363 | | make_char_parser('\t')
364 | | make_char_parser('\n')
365 | | make_char_parser('\r');
366 | return many(ws_parser, std::monostate{}, [] (auto m, auto) { return m; });
367 | }
368 |
369 | }
370 | }
371 |
--------------------------------------------------------------------------------
/src/include/cx_string.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #include "cx_vector.h"
8 |
9 | namespace cx
10 | {
11 | struct static_string
12 | {
13 | template
14 | constexpr static_string(const char (&str)[N])
15 | : m_size(N-1), m_data(&str[0])
16 | {
17 | }
18 | constexpr static_string(const char* str, std::size_t s)
19 | : m_size(s), m_data(str)
20 | {
21 | }
22 |
23 | constexpr static_string() = default;
24 |
25 | constexpr size_t size() const {
26 | return m_size;
27 | }
28 |
29 | constexpr const char *c_str() const {
30 | return m_data;
31 | }
32 |
33 | constexpr const char *begin() const {
34 | return m_data;
35 | }
36 |
37 | constexpr const char *end() const {
38 | return m_data + m_size;
39 | }
40 |
41 | std::size_t m_size{0};
42 | const char *m_data = nullptr;
43 | };
44 |
45 | constexpr bool operator==(const static_string &x, const static_string &y)
46 | {
47 | return cx::equal(x.begin(), x.end(), y.begin(), y.end());
48 | }
49 |
50 |
51 | // note that this works because vector is implicitly null terminated with its data initializer
52 | template
53 | struct basic_string : vector
54 | {
55 | constexpr basic_string(const static_string &s)
56 | : vector(s.begin(), s.end())
57 | {
58 | }
59 | constexpr basic_string(const std::string_view &s)
60 | : vector(s.cbegin(), s.cend())
61 | {
62 | }
63 |
64 | constexpr basic_string() = default;
65 |
66 | constexpr basic_string &operator=(const static_string &s) {
67 | return *this = basic_string(s);
68 | }
69 |
70 | constexpr basic_string &operator=(const std::string_view &s) {
71 | return *this = basic_string(s);
72 | }
73 |
74 | constexpr const char *c_str() const {
75 | return this->data();
76 | }
77 | };
78 |
79 | template
80 | constexpr bool operator==(const basic_string