1 // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
2 // the main distribution directory for license terms and copyright or visit
3 // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
4
5 #pragma once
6
7 #include <cstdint>
8 #include <type_traits>
9
10 #include "caf/config.hpp"
11 #include "caf/detail/consumer.hpp"
12 #include "caf/detail/parser/add_ascii.hpp"
13 #include "caf/detail/parser/chars.hpp"
14 #include "caf/detail/parser/is_char.hpp"
15 #include "caf/detail/parser/is_digit.hpp"
16 #include "caf/detail/parser/read_floating_point.hpp"
17 #include "caf/detail/parser/sub_ascii.hpp"
18 #include "caf/detail/scope_guard.hpp"
19 #include "caf/pec.hpp"
20
21 CAF_PUSH_UNUSED_LABEL_WARNING
22
23 #include "caf/detail/parser/fsm.hpp"
24
25 namespace caf::detail::parser {
26
27 /// Reads the second half of 'n..m' range statement.
28 ///
29 /// Expect the current position to point at the number *after* the dots:
30 ///
31 /// ~~~
32 /// foo = [1..2]
33 /// ~~~^
34 /// ~~~
35 template <class State, class Consumer>
36 void read_number_range(State& ps, Consumer& consumer, int64_t begin);
37
38 /// Reads a number, i.e., on success produces either an `int64_t` or a
39 /// `double`.
40 template <class State, class Consumer, class EnableFloat = std::true_type,
41 class EnableRange = std::false_type>
read_number(State & ps,Consumer & consumer,EnableFloat={},EnableRange={})42 void read_number(State& ps, Consumer& consumer, EnableFloat = {},
43 EnableRange = {}) {
44 static constexpr bool enable_float = EnableFloat::value;
45 static constexpr bool enable_range = EnableRange::value;
46 // Our result when reading an integer number.
47 int64_t result = 0;
48 // Computes the result on success.
__anonc2fb6f250102null49 auto g = caf::detail::make_scope_guard([&] {
50 if (ps.code <= pec::trailing_character)
51 consumer.value(result);
52 });
53 using odbl = optional<double>;
54 // clang-format off
55 // Definition of our parser FSM.
56 start();
57 state(init) {
58 transition(init, " \t")
59 transition(has_plus, '+')
60 transition(has_minus, '-')
61 fsm_epsilon_static_if(enable_float,
62 read_floating_point(ps, consumer, odbl{0.}),
63 done, '.', g.disable())
64 epsilon(has_plus)
65 }
66 // "+" or "-" alone aren't numbers.
67 state(has_plus) {
68 fsm_epsilon_static_if(enable_float,
69 read_floating_point(ps, consumer, odbl{0.}),
70 done, '.', g.disable())
71 transition(pos_zero, '0')
72 epsilon(pos_dec)
73 }
74 state(has_minus) {
75 fsm_epsilon_static_if(enable_float,
76 read_floating_point(ps, consumer, odbl{0.}, true),
77 done, '.', g.disable())
78 transition(neg_zero, '0')
79 epsilon(neg_dec)
80 }
81 // Disambiguate base.
82 term_state(pos_zero) {
83 transition(start_pos_bin, "bB")
84 transition(start_pos_hex, "xX")
85 transition_static_if(enable_float || enable_range, pos_dot, '.')
86 epsilon(pos_oct)
87 }
88 term_state(neg_zero) {
89 transition(start_neg_bin, "bB")
90 transition(start_neg_hex, "xX")
91 transition_static_if(enable_float || enable_range, neg_dot, '.')
92 epsilon(neg_oct)
93 }
94 // Binary integers.
95 state(start_pos_bin) {
96 epsilon(pos_bin)
97 }
98 term_state(pos_bin) {
99 transition(pos_bin, "01", add_ascii<2>(result, ch), pec::integer_overflow)
100 }
101 state(start_neg_bin) {
102 epsilon(neg_bin)
103 }
104 term_state(neg_bin) {
105 transition(neg_bin, "01", sub_ascii<2>(result, ch), pec::integer_underflow)
106 }
107 // Octal integers.
108 state(start_pos_oct) {
109 epsilon(pos_oct)
110 }
111 term_state(pos_oct) {
112 transition(pos_oct, octal_chars, add_ascii<8>(result, ch),
113 pec::integer_overflow)
114 }
115 state(start_neg_oct) {
116 epsilon(neg_oct)
117 }
118 term_state(neg_oct) {
119 transition(neg_oct, octal_chars, sub_ascii<8>(result, ch),
120 pec::integer_underflow)
121 }
122 // Hexal integers.
123 state(start_pos_hex) {
124 epsilon(pos_hex)
125 }
126 term_state(pos_hex) {
127 transition(pos_hex, hexadecimal_chars, add_ascii<16>(result, ch),
128 pec::integer_overflow)
129 }
130 state(start_neg_hex) {
131 epsilon(neg_hex)
132 }
133 term_state(neg_hex) {
134 transition(neg_hex, hexadecimal_chars, sub_ascii<16>(result, ch),
135 pec::integer_underflow)
136 }
137 // Reads the integer part of the mantissa or a positive decimal integer.
138 term_state(pos_dec) {
139 transition(pos_dec, decimal_chars, add_ascii<10>(result, ch),
140 pec::integer_overflow)
141 fsm_epsilon_static_if(enable_float,
142 read_floating_point(ps, consumer, odbl{result}),
143 done, "eE", g.disable())
144 transition_static_if(enable_float || enable_range, pos_dot, '.')
145 }
146 // Reads the integer part of the mantissa or a negative decimal integer.
147 term_state(neg_dec) {
148 transition(neg_dec, decimal_chars, sub_ascii<10>(result, ch),
149 pec::integer_underflow)
150 fsm_epsilon_static_if(enable_float,
151 read_floating_point(ps, consumer, odbl{result}, true),
152 done, "eE", g.disable())
153 transition_static_if(enable_float || enable_range, neg_dot, '.')
154 }
155 unstable_state(pos_dot) {
156 fsm_transition_static_if(enable_range,
157 read_number_range(ps, consumer, result),
158 done, '.', g.disable())
159 fsm_epsilon_static_if(enable_float,
160 read_floating_point(ps, consumer, odbl{result}),
161 done, any_char, g.disable())
162 epsilon(done)
163 }
164 unstable_state(neg_dot) {
165 fsm_transition_static_if(enable_range,
166 read_number_range(ps, consumer, result),
167 done, '.', g.disable())
168 fsm_epsilon_static_if(enable_float,
169 read_floating_point(ps, consumer, odbl{result}, true),
170 done, any_char, g.disable())
171 epsilon(done)
172 }
173 term_state(done) {
174 // nop
175 }
176 fin();
177 // clang-format on
178 }
179
180 template <class State, class Consumer>
read_number_range(State & ps,Consumer & consumer,int64_t begin)181 void read_number_range(State& ps, Consumer& consumer, int64_t begin) {
182 optional<int64_t> end;
183 optional<int64_t> step;
184 auto end_consumer = make_consumer(end);
185 auto step_consumer = make_consumer(step);
186 auto generate_2 = [&](int64_t n, int64_t m) {
187 if (n <= m)
188 while (n <= m)
189 consumer.value(n++);
190 else
191 while (n >= m)
192 consumer.value(n--);
193 };
194 auto generate_3 = [&](int64_t n, int64_t m, int64_t s) {
195 if (n == m) {
196 consumer.value(n);
197 return;
198 }
199 if (s == 0 || (n > m && s > 0) || (n < m && s < 0)) {
200 ps.code = pec::invalid_range_expression;
201 return;
202 }
203 if (n <= m)
204 for (auto i = n; i <= m; i += s)
205 consumer.value(i);
206 else
207 for (auto i = n; i >= m; i += s)
208 consumer.value(i);
209 };
210 auto g = caf::detail::make_scope_guard([&] {
211 if (ps.code <= pec::trailing_character) {
212 if (!end) {
213 ps.code = pec::invalid_range_expression;
214 } else if (!step) {
215 generate_2(begin, *end);
216 } else {
217 generate_3(begin, *end, *step);
218 }
219 }
220 });
221 static constexpr std::false_type no_float = std::false_type{};
222 // clang-format off
223 // Definition of our parser FSM.
224 start();
225 state(init) {
226 fsm_epsilon(read_number(ps, end_consumer, no_float), after_end_num)
227 }
228 term_state(after_end_num) {
229 transition(first_dot, '.')
230 }
231 state(first_dot) {
232 transition(second_dot, '.')
233 }
234 state(second_dot) {
235 fsm_epsilon(read_number(ps, step_consumer, no_float), done)
236 }
237 term_state(done) {
238 // nop
239 }
240 fin();
241 // clang-format on
242 }
243
244 } // namespace caf::detail::parser
245
246 #include "caf/detail/parser/fsm_undef.hpp"
247
248 CAF_POP_WARNINGS
249