Commit 3acbc0a0 authored by Dominik Charousset's avatar Dominik Charousset

Re-use floating point parser in number parser

parent ace0227f
...@@ -45,7 +45,8 @@ namespace parser { ...@@ -45,7 +45,8 @@ namespace parser {
/// the pre-decimal value. /// the pre-decimal value.
template <class Iterator, class Sentinel, class Consumer, class ValueType> template <class Iterator, class Sentinel, class Consumer, class ValueType>
void read_floating_point(state<Iterator, Sentinel>& ps, Consumer& consumer, void read_floating_point(state<Iterator, Sentinel>& ps, Consumer& consumer,
optional<ValueType> start_value) { optional<ValueType> start_value,
bool negative = false) {
// Any exponent larger than 511 always overflows. // Any exponent larger than 511 always overflows.
static constexpr int max_double_exponent = 511; static constexpr int max_double_exponent = 511;
// We assume a simple integer until proven wrong. // We assume a simple integer until proven wrong.
...@@ -58,6 +59,9 @@ void read_floating_point(state<Iterator, Sentinel>& ps, Consumer& consumer, ...@@ -58,6 +59,9 @@ void read_floating_point(state<Iterator, Sentinel>& ps, Consumer& consumer,
} else if (*start_value < 0) { } else if (*start_value < 0) {
sign = minus; sign = minus;
result = -*start_value; result = -*start_value;
} else if (negative) {
sign = minus;
result = *start_value;
} else { } else {
sign = plus; sign = plus;
result = *start_value; result = *start_value;
......
...@@ -21,10 +21,11 @@ ...@@ -21,10 +21,11 @@
#include <cstdint> #include <cstdint>
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/add_ascii.hpp" #include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp" #include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp" #include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/read_floating_point.hpp"
#include "caf/detail/parser/state.hpp" #include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp" #include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp" #include "caf/detail/scope_guard.hpp"
...@@ -42,87 +43,32 @@ namespace parser { ...@@ -42,87 +43,32 @@ namespace parser {
/// `double`. /// `double`.
template <class Iterator, class Sentinel, class Consumer> template <class Iterator, class Sentinel, class Consumer>
void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) { void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
// Any exponent larger than 511 always overflows.
static constexpr int max_double_exponent = 511;
// We assume a simple integer until proven wrong.
enum result_type_t { integer, positive_double, negative_double };
auto result_type = integer;
// Adjusts our mantissa, e.g., 1.23 becomes 123 with a dec_exp of -2.
auto dec_exp = 0;
// Exponent part of a floating point literal.
auto exp = 0;
// Our result when reading a floating point number.
auto dbl_res = 0.;
// Our result when reading an integer number. // Our result when reading an integer number.
int64_t int_res = 0; int64_t result = 0;
// Computes the result on success. // Computes the result on success.
auto g = caf::detail::make_scope_guard([&] { auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= pec::trailing_character) { if (ps.code <= pec::trailing_character)
if (result_type == integer) { consumer.value(result);
consumer.value(int_res);
return;
}
// Compute final floating point number.
// 1) Fix the exponent.
exp += dec_exp;
// 2) Check whether exponent is in valid range.
if (exp < -max_double_exponent) {
ps.code = pec::exponent_underflow;
return;
}
if (exp > max_double_exponent) {
ps.code = pec::exponent_overflow;
return;
}
// 3) Scale result.
// Pre-computed powers of 10 for the scaling loop.
static double powerTable[] = {1e1, 1e2, 1e4, 1e8, 1e16,
1e32, 1e64, 1e128, 1e256};
auto i = 0;
if (exp < 0) {
for (auto n = -exp; n != 0; n >>= 1, ++i)
if (n & 0x01)
dbl_res /= powerTable[i];
} else {
for (auto n = exp; n != 0; n >>= 1, ++i)
if (n & 0x01)
dbl_res *= powerTable[i];
}
// 4) Fix sign and call consumer.
consumer.value(result_type == positive_double ? dbl_res : -dbl_res);
}
}); });
// Switches from parsing an integer to parsing a double. // clang-format off
auto ch_res = [&](result_type_t x) {
CAF_ASSERT(result_type == integer);
result_type = x;
// We parse doubles as positive number and restore the sign in `g`.
dbl_res = result_type == negative_double
? -static_cast<double>(int_res)
: static_cast<double>(int_res);
};
// Reads the a decimal place.
auto rd_decimal = [&](char c) {
--dec_exp;
return add_ascii<10>(dbl_res, c);
};
// Definition of our parser FSM. // Definition of our parser FSM.
start(); start();
state(init) { state(init) {
transition(init, " \t") transition(init, " \t")
transition(has_plus, '+') transition(has_plus, '+')
transition(has_minus, '-') transition(has_minus, '-')
transition(pos_zero, '0')
epsilon(has_plus) epsilon(has_plus)
} }
// "+" or "-" alone aren't numbers. // "+" or "-" alone aren't numbers.
state(has_plus) { state(has_plus) {
transition(leading_dot, '.', ch_res(positive_double)) fsm_epsilon(read_floating_point(ps, consumer, optional<double>{0.}),
done, '.', g.disable())
transition(pos_zero, '0') transition(pos_zero, '0')
epsilon(pos_dec) epsilon(pos_dec)
} }
state(has_minus) { state(has_minus) {
transition(leading_dot, '.', ch_res(negative_double)) fsm_epsilon(read_floating_point(ps, consumer, optional<double>{0.}, true),
done, '.', g.disable())
transition(neg_zero, '0') transition(neg_zero, '0')
epsilon(neg_dec) epsilon(neg_dec)
} }
...@@ -130,13 +76,15 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -130,13 +76,15 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
term_state(pos_zero) { term_state(pos_zero) {
transition(start_pos_bin, "bB") transition(start_pos_bin, "bB")
transition(start_pos_hex, "xX") transition(start_pos_hex, "xX")
transition(trailing_dot, '.', ch_res(positive_double)) fsm_epsilon(read_floating_point(ps, consumer, optional<double>{0.}),
done, '.', g.disable())
epsilon(pos_oct) epsilon(pos_oct)
} }
term_state(neg_zero) { term_state(neg_zero) {
transition(start_neg_bin, "bB") transition(start_neg_bin, "bB")
transition(start_neg_hex, "xX") transition(start_neg_hex, "xX")
transition(trailing_dot, '.', ch_res(negative_double)) fsm_epsilon(read_floating_point(ps, consumer, optional<double>{0.}, true),
done, '.', g.disable())
epsilon(neg_oct) epsilon(neg_oct)
} }
// Binary integers. // Binary integers.
...@@ -144,27 +92,27 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -144,27 +92,27 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
epsilon(pos_bin) epsilon(pos_bin)
} }
term_state(pos_bin) { term_state(pos_bin) {
transition(pos_bin, "01", add_ascii<2>(int_res, ch), pec::integer_overflow) transition(pos_bin, "01", add_ascii<2>(result, ch), pec::integer_overflow)
} }
state(start_neg_bin) { state(start_neg_bin) {
epsilon(neg_bin) epsilon(neg_bin)
} }
term_state(neg_bin) { term_state(neg_bin) {
transition(neg_bin, "01", sub_ascii<2>(int_res, ch), pec::integer_underflow) transition(neg_bin, "01", sub_ascii<2>(result, ch), pec::integer_underflow)
} }
// Octal integers. // Octal integers.
state(start_pos_oct) { state(start_pos_oct) {
epsilon(pos_oct) epsilon(pos_oct)
} }
term_state(pos_oct) { term_state(pos_oct) {
transition(pos_oct, octal_chars, add_ascii<8>(int_res, ch), transition(pos_oct, octal_chars, add_ascii<8>(result, ch),
pec::integer_overflow) pec::integer_overflow)
} }
state(start_neg_oct) { state(start_neg_oct) {
epsilon(neg_oct) epsilon(neg_oct)
} }
term_state(neg_oct) { term_state(neg_oct) {
transition(neg_oct, octal_chars, sub_ascii<8>(int_res, ch), transition(neg_oct, octal_chars, sub_ascii<8>(result, ch),
pec::integer_underflow) pec::integer_underflow)
} }
// Hexal integers. // Hexal integers.
...@@ -172,70 +120,39 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -172,70 +120,39 @@ void read_number(state<Iterator, Sentinel>& ps, Consumer& consumer) {
epsilon(pos_hex) epsilon(pos_hex)
} }
term_state(pos_hex) { term_state(pos_hex) {
transition(pos_hex, hexadecimal_chars, add_ascii<16>(int_res, ch), transition(pos_hex, hexadecimal_chars, add_ascii<16>(result, ch),
pec::integer_overflow) pec::integer_overflow)
} }
state(start_neg_hex) { state(start_neg_hex) {
epsilon(neg_hex) epsilon(neg_hex)
} }
term_state(neg_hex) { term_state(neg_hex) {
transition(neg_hex, hexadecimal_chars, sub_ascii<16>(int_res, ch), transition(neg_hex, hexadecimal_chars, sub_ascii<16>(result, ch),
pec::integer_underflow) pec::integer_underflow)
} }
// Reads the integer part of the mantissa or a positive decimal integer. // Reads the integer part of the mantissa or a positive decimal integer.
term_state(pos_dec) { term_state(pos_dec) {
transition(pos_dec, decimal_chars, add_ascii<10>(int_res, ch), transition(pos_dec, decimal_chars, add_ascii<10>(result, ch),
pec::integer_overflow) pec::integer_overflow)
transition(has_e, "eE", ch_res(positive_double)) fsm_epsilon(read_floating_point(ps, consumer, optional<double>{result}),
transition(trailing_dot, '.', ch_res(positive_double)) done, "eE", g.disable())
fsm_epsilon(read_floating_point(ps, consumer, optional<double>{result}),
done, '.', g.disable())
} }
// Reads the integer part of the mantissa or a negative decimal integer. // Reads the integer part of the mantissa or a negative decimal integer.
term_state(neg_dec) { term_state(neg_dec) {
transition(neg_dec, decimal_chars, sub_ascii<10>(int_res, ch), transition(neg_dec, decimal_chars, sub_ascii<10>(result, ch),
pec::integer_underflow) pec::integer_underflow)
transition(has_e, "eE", ch_res(negative_double)) fsm_epsilon(read_floating_point(ps, consumer, optional<double>{result}, true),
transition(trailing_dot, '.', ch_res(negative_double)) done, "eE", g.disable())
} fsm_epsilon(read_floating_point(ps, consumer, optional<double>{result}, true),
// ".", "+.", etc. aren't valid numbers, so this state isn't terminal. done, '.', g.disable())
state(leading_dot) { }
transition(after_dot, decimal_chars, rd_decimal(ch), pec::exponent_underflow) term_state(done) {
} // nop
// "1." is a valid number, so a trailing dot is a terminal state.
term_state(trailing_dot) {
epsilon(after_dot)
}
// Read the decimal part of a mantissa.
term_state(after_dot) {
transition(after_dot, decimal_chars, rd_decimal(ch), pec::exponent_underflow)
transition(has_e, "eE")
}
// "...e", "...e+", and "...e-" aren't valid numbers, so these states are not
// terminal.
state(has_e) {
transition(has_plus_after_e, '+')
transition(has_minus_after_e, '-')
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
pec::exponent_overflow)
}
state(has_plus_after_e) {
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
pec::exponent_overflow)
}
state(has_minus_after_e) {
transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch),
pec::exponent_underflow)
}
// Read a positive exponent.
term_state(pos_exp) {
transition(pos_exp, decimal_chars, add_ascii<10>(exp, ch),
pec::exponent_overflow)
}
// Read a negative exponent.
term_state(neg_exp) {
transition(neg_exp, decimal_chars, sub_ascii<10>(exp, ch),
pec::exponent_underflow)
} }
fin(); fin();
// clang-format on
} }
} // namespace parser } // namespace parser
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment