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