Commit 350b1941 authored by Dominik Charousset's avatar Dominik Charousset

Implement an FSM-based one-pass number parser

parent ea4fb677
...@@ -46,6 +46,7 @@ set(LIBCAF_CORE_SRCS ...@@ -46,6 +46,7 @@ set(LIBCAF_CORE_SRCS
src/downstream_messages.cpp src/downstream_messages.cpp
src/duration.cpp src/duration.cpp
src/dynamic_message_data.cpp src/dynamic_message_data.cpp
src/ec.cpp
src/error.cpp src/error.cpp
src/event_based_actor.cpp src/event_based_actor.cpp
src/execution_unit.cpp src/execution_unit.cpp
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/detail/parser/ascii_to_int.hpp"
namespace caf {
namespace detail {
namespace parser {
// Sum up integers when parsing positive integers.
// @returns `false` on an overflow, otherwise `true`.
// @pre `isdigit(c) || (Base == 16 && isxdigit(c))`
template <int Base, class T>
bool add_ascii(T& x, char c) {
ascii_to_int<Base, T> f;
auto before = x;
x = (x * Base) + f(c);
return before <= x;
}
} // namespace parser
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
namespace caf {
namespace detail {
namespace parser {
template <int Base, class T>
struct ascii_to_int {
// @pre `c` is a valid ASCII digit, i.e., matches [0-9].
constexpr T operator()(char c) const {
return c - '0';
}
};
template <class T>
struct ascii_to_int<16, T> {
// @pre `c` is a valid ASCII hex digit, i.e., matches [0-9A-Fa-f].
constexpr T operator()(char c) const {
// Numbers start at position 48 in the ASCII table.
// Uppercase characters start at position 65 in the ASCII table.
// Lowercase characters start at position 97 in the ASCII table.
return c <= '9' ? c - '0' : (c <= 'F' ? 10 + (c - 'A') : 10 + (c - 'a'));
}
};
} // namespace parser
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstdint>
namespace caf {
namespace detail {
namespace parser {
enum class ec : uint8_t {
/// Not-an-error.
success,
/// Parser succeeded but found trailing character(s).
trailing_character,
/// Parser stopped after reaching the end while still expecting input.
unexpected_eof,
/// Parser stopped after reading an unexpected character.
unexpected_character,
/// Cannot construct a negative caf::duration.
negative_duration,
/// Cannot construct a caf::duration with a value exceeding uint32_t.
duration_overflow,
/// Too many characters for an atom.
too_many_characters,
/// Unrecognized character after escaping `\`.
illegal_escape_sequence,
/// Misplaced newline, e.g., inside a string.
unexpected_newline,
/// Parsed positive integer exceeds the number of available bits.
integer_overflow,
/// Parsed negative integer exceeds the number of available bits.
integer_underflow,
/// Exponent of parsed double is less than the minimum supported exponent.
exponent_underflow,
/// Exponent of parsed double is greater than the maximum supported exponent.
exponent_overflow,
};
const char* to_string(ec x);
} // namespace parser
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#define start() \
char ch = ps.current(); \
goto s_init; \
s_unexpected_eof: \
ps.code = ec::unexpected_eof; \
return; \
{ \
static constexpr ec mismatch_ec = ec::unexpected_character
#define state(name) \
ps.code = mismatch_ec; \
return; \
} \
{ \
static constexpr ec mismatch_ec = ec::unexpected_character; \
s_##name : __attribute__((unused)); \
if (ch == '\0') \
goto s_unexpected_eof; \
e_##name : __attribute__((unused));
#define term_state(name) \
ps.code = mismatch_ec; \
return; \
} \
{ \
static constexpr ec mismatch_ec = ec::trailing_character; \
s_##name : __attribute__((unused)); \
if (ch == '\0') \
goto s_fin; \
e_##name : __attribute__((unused));
#define fin() \
ps.code = mismatch_ec; \
return; \
} \
s_fin: \
ps.code = ec::success; \
return;
#define input(predicate, target) \
if (predicate(ch)) { \
ch = ps.next(); \
goto s_##target; \
}
#define action(predicate, target, statement) \
if (predicate(ch)) { \
statement; \
ch = ps.next(); \
goto s_##target; \
}
#define checked_action(predicate, target, statement, error_code) \
if (predicate(ch)) { \
if (statement) { \
ch = ps.next(); \
goto s_##target; \
} else { \
ps.code = error_code; \
return; \
} \
}
#define epsilon(target) goto e_##target;
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
namespace caf {
namespace detail {
namespace parser {
/// Returns whether `c` equals `C`.
template <char C>
bool is_char(char c) {
return c == C;
}
/// Returns whether `c` equals `C` (case insensitive).
template <char C>
bool is_ichar(char c) {
static_assert(C >= 'a' && C <= 'z', "Expected a-z (lowercase).");
return c == C || c == (C - 32);
}
} // namespace parser
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
namespace caf {
namespace detail {
namespace parser {
/// Returns whether `c` is a valid digit for a given base.
template <int Base>
bool is_digit(char c);
template <>
bool is_digit<2>(char c) {
return c == '0' || c == '1';
}
template <>
bool is_digit<8>(char c) {
return c >= '0' && c <= '7';
}
template <>
bool is_digit<10>(char c) {
return c >= '0' && c <= '9';
}
template <>
bool is_digit<16>(char c) {
return (c >= '0' && c <= '9')
|| (c >= 'A' && c <= 'F')
|| (c >= 'a' && c <= 'f');
}
} // namespace parser
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstdint>
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/parser/ec.hpp"
#include "caf/detail/parser/fsm.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
namespace caf {
namespace detail {
namespace parser {
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template <class Iterator, class Sentinel, class 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.
int64_t int_res = 0;
// Computes the result on success.
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= ec::trailing_character) {
if (result_type == integer) {
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 = ec::exponent_underflow;
return;
}
if (exp > max_double_exponent) {
ps.code = ec::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.
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.
start();
state(init) {
input(is_char<' '>, init)
input(is_char<'\t'>, init)
input(is_char<'+'>, has_plus)
input(is_char<'-'>, has_minus)
input(is_char<'0'>, pos_zero)
epsilon(has_plus)
}
// "+" or "-" alone aren't numbers.
state(has_plus) {
action(is_char<'.'>, leading_dot, ch_res(positive_double))
input(is_char<'0'>, pos_zero)
epsilon(pos_dec)
}
state(has_minus) {
action(is_char<'.'>, leading_dot, ch_res(negative_double))
input(is_char<'0'>, neg_zero)
epsilon(neg_dec)
}
// Disambiguate base.
term_state(pos_zero) {
input(is_ichar<'b'>, start_pos_bin)
input(is_ichar<'x'>, start_pos_hex)
action(is_char<'.'>, trailing_dot, ch_res(positive_double))
epsilon(pos_oct)
}
term_state(neg_zero) {
input(is_ichar<'b'>, start_neg_bin)
input(is_ichar<'x'>, start_neg_hex)
action(is_char<'.'>, trailing_dot, ch_res(negative_double))
epsilon(neg_oct)
}
// Binary integers.
state(start_pos_bin) {
epsilon(pos_bin)
}
term_state(pos_bin) {
checked_action(is_digit<2>, pos_bin, add_ascii<2>(int_res, ch),
ec::integer_overflow)
}
state(start_neg_bin) {
epsilon(neg_bin)
}
term_state(neg_bin) {
checked_action(is_digit<2>, neg_bin, sub_ascii<2>(int_res, ch),
ec::integer_underflow)
}
// Octal integers.
state(start_pos_oct) {
epsilon(pos_oct)
}
term_state(pos_oct) {
checked_action(is_digit<8>, pos_oct, add_ascii<8>(int_res, ch),
ec::integer_overflow)
}
state(start_neg_oct) {
epsilon(neg_oct)
}
term_state(neg_oct) {
checked_action(is_digit<8>, neg_oct, sub_ascii<8>(int_res, ch),
ec::integer_underflow)
}
// Hexal integers.
state(start_pos_hex) {
epsilon(pos_hex)
}
term_state(pos_hex) {
checked_action(is_digit<16>, pos_hex, add_ascii<16>(int_res, ch),
ec::integer_overflow)
}
state(start_neg_hex) {
epsilon(neg_hex)
}
term_state(neg_hex) {
checked_action(is_digit<16>, neg_hex, sub_ascii<16>(int_res, ch),
ec::integer_underflow)
}
// Reads the integer part of the mantissa or a positive decimal integer.
term_state(pos_dec) {
checked_action(is_digit<10>, pos_dec, add_ascii<10>(int_res, ch),
ec::integer_overflow)
action(is_ichar<'e'>, has_e, ch_res(positive_double))
action(is_char<'.'>, trailing_dot, ch_res(positive_double))
}
// Reads the integer part of the mantissa or a negative decimal integer.
term_state(neg_dec) {
checked_action(is_digit<10>, neg_dec, sub_ascii<10>(int_res, ch),
ec::integer_underflow)
action(is_ichar<'e'>, has_e, ch_res(negative_double))
action(is_char<'.'>, trailing_dot, ch_res(negative_double))
}
// ".", "+.", etc. aren't valid numbers, so this state isn't terminal.
state(leading_dot) {
checked_action(is_digit<10>, after_dot, rd_decimal(ch),
ec::exponent_underflow)
}
// "1." is a valid number, so a trailing dot is a terminal state.
term_state(trailing_dot) {
checked_action(is_digit<10>, after_dot, rd_decimal(ch),
ec::exponent_underflow)
input(is_ichar<'e'>, has_e)
}
// Read the decimal part of a mantissa.
term_state(after_dot) {
checked_action(is_digit<10>, after_dot, rd_decimal(ch),
ec::exponent_underflow)
input(is_ichar<'e'>, has_e)
}
// "...e", "...e+", and "...e-" aren't valid numbers, so these states are not
// terminal.
state(has_e) {
input(is_char<'+'>, has_plus_after_e)
input(is_char<'-'>, has_minus_after_e)
checked_action(is_digit<10>, pos_exp, add_ascii<10>(exp, ch),
ec::exponent_overflow)
}
state(has_plus_after_e) {
checked_action(is_digit<10>, pos_exp, add_ascii<10>(exp, ch),
ec::exponent_overflow)
}
state(has_minus_after_e) {
checked_action(is_digit<10>, neg_exp, sub_ascii<10>(exp, ch),
ec::exponent_underflow)
}
// Read a positive exponent.
term_state(pos_exp) {
checked_action(is_digit<10>, pos_exp, add_ascii<10>(exp, ch),
ec::exponent_overflow)
}
// Read a negative exponent.
term_state(neg_exp) {
checked_action(is_digit<10>, neg_exp, sub_ascii<10>(exp, ch),
ec::exponent_underflow)
}
fin();
}
} // namespace parser
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstdint>
#include "caf/detail/parser/ec.hpp"
namespace caf {
namespace detail {
namespace parser {
template <class Iterator, class Sentinel = Iterator>
struct state {
Iterator i;
Sentinel e;
ec code;
int32_t line = 1;
int32_t column = 1;
/// Returns the null terminator when reaching the end of the string,
/// otherwise the next character.
char next() noexcept {
++i;
if (i != e) {
auto c = *i;
if (c == '\n') {
++line;
column = 1;
} else {
++column;
}
return c;
}
return '\0';
}
/// Returns the null terminator if `i == e`, otherwise the current character.
char current() const noexcept {
return i != e ? *i : '\0';
}
};
} // namespace parser
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/detail/parser/ascii_to_int.hpp"
namespace caf {
namespace detail {
namespace parser {
// Subtracs integers when parsing negative integers.
// @returns `false` on an underflow, otherwise `true`.
// @pre `isdigit(c) || (Base == 16 && isxdigit(c))`
template <int Base, class T>
bool sub_ascii(T& x, char c) {
ascii_to_int<Base, T> f;
auto before = x;
x = (x * Base) - f(c);
return before >= x;
}
} // namespace parser
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/detail/parser/ec.hpp"
namespace {
constexpr const char* tbl[] = {
"success",
"trailing_character",
"unexpected_eof",
"unexpected_character",
"negative_duration",
"duration_overflow",
"too_many_characters",
"illegal_escape_sequence",
"unexpected_newline",
"integer_overflow",
"integer_underflow",
"exponent_underflow",
"exponent_overflow",
};
} // namespace <anonymous>
namespace caf {
namespace detail {
namespace parser {
const char* to_string(ec x) {
return tbl[static_cast<uint8_t>(x)];
}
} // namespace parser
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE read_number
#include <string>
#include "caf/test/unit_test.hpp"
#include "caf/variant.hpp"
#include "caf/detail/parser/read_number.hpp"
using namespace caf;
using detail::parser::ec;
namespace {
struct numbers_parser_consumer {
variant<int64_t, double> x;
inline void value(double y) {
x = y;
}
inline void value(int64_t y) {
x = y;
}
};
struct res_t {
variant<ec, double, int64_t> val;
template <class T>
res_t(T&& x) : val(std::forward<T>(x)) {
// nop
}
};
bool operator==(const res_t& x, const res_t& y) {
if (x.val.index() != y.val.index())
return false;
// Implements a safe equal comparison for double.
caf::test::equal_to f;
using caf::get;
using caf::holds_alternative;
if (holds_alternative<ec>(x.val))
return f(get<ec>(x.val), get<ec>(y.val));
if (holds_alternative<double>(x.val))
return f(get<double>(x.val), get<double>(y.val));
return f(get<int64_t>(x.val), get<int64_t>(y.val));
}
struct numbers_parser {
res_t operator()(std::string str) {
detail::parser::state<std::string::iterator> res;
numbers_parser_consumer f;
res.i = str.begin();
res.e = str.end();
detail::parser::read_number(res, f);
if (res.code == ec::success)
return f.x;
return res.code;
}
};
template <class T>
typename std::enable_if<std::is_integral<T>::value, res_t>::type res(T x) {
return {static_cast<int64_t>(x)};
}
template <class T>
typename std::enable_if<std::is_floating_point<T>::value, res_t>::type
res(T x) {
return {static_cast<double>(x)};
}
struct fixture {
numbers_parser p;
};
} // namespace <anonymous>
#define CHECK_NUMBER(x) CAF_CHECK_EQUAL(p(#x), res(x))
CAF_TEST_FIXTURE_SCOPE(read_number_tests, fixture)
CAF_TEST(binary numbers) {
/* TODO: use this implementation when switching to C++14
CHECK_NUMBER(0b0);
CHECK_NUMBER(0b10);
CHECK_NUMBER(0b101);
CHECK_NUMBER(0B1001);
CHECK_NUMBER(-0b0);
CHECK_NUMBER(-0b101);
CHECK_NUMBER(-0B1001);
*/
CAF_CHECK_EQUAL(p("0b0"), res(0));
CAF_CHECK_EQUAL(p("0b10"), res(2));
CAF_CHECK_EQUAL(p("0b101"), res(5));
CAF_CHECK_EQUAL(p("0B1001"), res(9));
CAF_CHECK_EQUAL(p("-0b0"), res(0));
CAF_CHECK_EQUAL(p("-0b101"), res(-5));
CAF_CHECK_EQUAL(p("-0B1001"), res(-9));
}
CAF_TEST(octal numbers) {
// valid numbers
CHECK_NUMBER(00);
CHECK_NUMBER(010);
CHECK_NUMBER(0123);
CHECK_NUMBER(0777);
CHECK_NUMBER(-00);
CHECK_NUMBER(-0123);
// invalid numbers
CAF_CHECK_EQUAL(p("018"), ec::trailing_character);
}
CAF_TEST(decimal numbers) {
CHECK_NUMBER(0);
CHECK_NUMBER(10);
CHECK_NUMBER(123);
CHECK_NUMBER(-0);
CHECK_NUMBER(-123);
}
CAF_TEST(hexadecimal numbers) {
// valid numbers
CHECK_NUMBER(0x0);
CHECK_NUMBER(0x10);
CHECK_NUMBER(0X123);
CHECK_NUMBER(0xAF01);
CHECK_NUMBER(-0x0);
CHECK_NUMBER(-0x123);
CHECK_NUMBER(-0xaf01);
// invalid numbers
CAF_CHECK_EQUAL(p("0xFG"), ec::trailing_character);
CAF_CHECK_EQUAL(p("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
ec::integer_overflow);
CAF_CHECK_EQUAL(p("-0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
ec::integer_underflow);
}
CAF_TEST(floating point numbers) {
CHECK_NUMBER(0.0);
CHECK_NUMBER(.0);
CHECK_NUMBER(0.);
CHECK_NUMBER(0.123);
CHECK_NUMBER(.123);
CHECK_NUMBER(123.456);
CHECK_NUMBER(-0.0);
CHECK_NUMBER(-.0);
CHECK_NUMBER(-0.);
CHECK_NUMBER(-0.123);
CHECK_NUMBER(-.123);
CHECK_NUMBER(-123.456);
}
CAF_TEST(integer mantissa with positive exponent) {
CHECK_NUMBER(321E1);
CHECK_NUMBER(321e1);
CHECK_NUMBER(321e+1);
CHECK_NUMBER(123e2);
CHECK_NUMBER(-4e2);
CHECK_NUMBER(1e1);
CHECK_NUMBER(1e2);
CHECK_NUMBER(1e3);
CHECK_NUMBER(1e4);
CHECK_NUMBER(1e5);
CHECK_NUMBER(1e6);
}
CAF_TEST(integer mantissa with negative exponent) {
// valid numbers
CHECK_NUMBER(321E-1);
CHECK_NUMBER(321e-1);
CHECK_NUMBER(123e-2);
CHECK_NUMBER(-4e-2);
CHECK_NUMBER(1e-1);
CHECK_NUMBER(1e-2);
CHECK_NUMBER(1e-3);
CHECK_NUMBER(1e-4);
CHECK_NUMBER(1e-5);
CHECK_NUMBER(1e-6);
// invalid numbers
CAF_CHECK_EQUAL(p("-9.9999e-e511"), ec::unexpected_character);
CAF_CHECK_EQUAL(p("-9.9999e-511"), ec::exponent_underflow);
}
CAF_TEST(fractional mantissa with positive exponent) {
CHECK_NUMBER(3.21E1);
CHECK_NUMBER(3.21e+1);
CHECK_NUMBER(3.21e+1);
CHECK_NUMBER(12.3e2);
CHECK_NUMBER(-0.001e3);
CHECK_NUMBER(0.0001e5);
CHECK_NUMBER(-42.001e3);
CHECK_NUMBER(42.0001e5);
}
CAF_TEST(fractional mantissa with negative exponent) {
CHECK_NUMBER(3.21E-1);
CHECK_NUMBER(3.21e-1);
CHECK_NUMBER(12.3e-2);
CHECK_NUMBER(-0.001e-3);
CHECK_NUMBER(-0.0001e-5);
CHECK_NUMBER(-42.001e-3);
CHECK_NUMBER(-42001e-6);
CHECK_NUMBER(-42.0001e-5);
}
CAF_TEST_FIXTURE_SCOPE_END()
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