978 lines
36 KiB
C++
978 lines
36 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
#include "dpf.hpp"
|
|
|
|
namespace
|
|
{
|
|
|
|
static constexpr char dec4[] = "pad('0')[0-9]{0,4}";
|
|
static constexpr char dec12[] = "pad('0')[0-9]{1,2}";
|
|
static constexpr char letters[] = "pad('\\0')[a-z]{0,3}";
|
|
static constexpr char hex3[] = "pad('0')[0-9a-f]{0,3}";
|
|
static constexpr char hexfixed[] = "[0-9a-f]{3}";
|
|
static constexpr char digits4[] = "[0-9]{4}";
|
|
static constexpr char fields[] = "pad('0')[0-9]{0,2}-pad('0')[0-9]{0,3}";
|
|
static constexpr char words[] = "[ab]{1,2}";
|
|
static constexpr char either[] = "cat|car";
|
|
static constexpr char holes[] = "pad('0')[013]{0,2}";
|
|
static constexpr char overlap[] = "a|a";
|
|
static constexpr char ambiguous[] = "(a|ab)(b|)";
|
|
static constexpr char adjacent[] = "pad('0')[0-9]{0,2}pad('0')[0-9]{0,2}";
|
|
static constexpr char star[] = "a*";
|
|
static constexpr char too_long[] = "[a-z]{65}";
|
|
static constexpr char undelimited[] = "[a-z]{1,3}[a-z]";
|
|
|
|
static constexpr char empty_pat[] = "";
|
|
static constexpr char group_pat[] = "()";
|
|
static constexpr char a_empty[] = "a()";
|
|
static constexpr char empty_a[] = "()a";
|
|
static constexpr char a_empty_b[] = "a()b";
|
|
static constexpr char padding[] = "padding";
|
|
static constexpr char lit_star[] = "[*]";
|
|
static constexpr char lit_plus[] = "[+]";
|
|
static constexpr char esc_star[] = "\\*";
|
|
static constexpr char esc_plus[] = "\\+";
|
|
static constexpr char lit_dot[] = "\\.";
|
|
static constexpr char lit_q[] = "\\?";
|
|
static constexpr char lit_lp[] = "\\(";
|
|
static constexpr char lit_rp[] = "\\)";
|
|
static constexpr char lit_bar[] = "\\|";
|
|
static constexpr char lit_lb[] = "\\[";
|
|
static constexpr char lit_rb[] = "\\]";
|
|
static constexpr char lit_lc[] = "\\{";
|
|
static constexpr char lit_rc[] = "\\}";
|
|
static constexpr char lit_bs[] = "\\\\";
|
|
static constexpr char lit_n[] = "\\n";
|
|
static constexpr char lit_t[] = "\\t";
|
|
static constexpr char lit_r[] = "\\r";
|
|
static constexpr char lit_A[] = "\\x41";
|
|
static constexpr char lit_nul[] = "\\x00";
|
|
static constexpr char lit_sq[] = "\\'";
|
|
static constexpr char lit_dq[] = "\\\"";
|
|
static constexpr char lit_dash[] = "\\-";
|
|
static constexpr char esc_cat[] = "\\n\\t\\x41";
|
|
static constexpr char cba[] = "[cba]";
|
|
static constexpr char dupcls[] = "[aab]";
|
|
static constexpr char dashcls[] = "[a\\-c]";
|
|
static constexpr char endash[] = "[a-]";
|
|
static constexpr char bracketcls[] = "[\\]a]";
|
|
static constexpr char range_esc[] = "[\\x01-\\x03]";
|
|
static constexpr char dashrange[] = "[--a]";
|
|
static constexpr char highcls[] = "[\\x7f-\\x81]";
|
|
static constexpr char negab[] = "[^ab]";
|
|
static constexpr char dot1[] = ".";
|
|
static constexpr char dot2[] = ".{2}";
|
|
static constexpr char dotq[] = ".?";
|
|
static constexpr char adot[] = "a.";
|
|
static constexpr char src_alt[] = "c|a|b";
|
|
static constexpr char a_ab[] = "a|ab";
|
|
static constexpr char ab_a[] = "ab|a";
|
|
static constexpr char mid_empty[] = "a||b";
|
|
static constexpr char lead_empty[] = "|a";
|
|
static constexpr char trail_empty[] = "a|";
|
|
static constexpr char nested_alt[] = "(a|bb)(c|dd)";
|
|
static constexpr char blocks[] = "(a|b)(c|d)";
|
|
static constexpr char nest_paren[] = "((((a|b))))";
|
|
static constexpr char opt2[] = "a?b?";
|
|
static constexpr char opt7[] = "a?b?c?d?e?f?g?";
|
|
static constexpr char alt_rep[] = "(a|b){2}";
|
|
static constexpr char alt_opt[] = "(a|b)?";
|
|
static constexpr char alt_pow[] = "(c|a|b){2}";
|
|
static constexpr char grp_rep[] = "(ab){0,2}";
|
|
static constexpr char grp_opt[] = "(ab)?";
|
|
static constexpr char a_rep[] = "a{2,4}";
|
|
static constexpr char a01[] = "a{01}";
|
|
static constexpr char a00[] = "a{0,0}";
|
|
static constexpr char pad0[] = "pad('0')[0-9]{0}";
|
|
static constexpr char a63[] = "a{63}";
|
|
static constexpr char a0_63[] = "a{0,63}";
|
|
static constexpr char cls_pow8[] = "[ab]{8}";
|
|
static constexpr char cls_pow9[] = "[ab]{9}";
|
|
static constexpr char cls_cat[] = "[ab][cd]";
|
|
static constexpr char xcls[] = "x[ab]";
|
|
static constexpr char three[] = "[ab]c[de]";
|
|
static constexpr char cls2[] = "[ab]{2}[ab]";
|
|
static constexpr char repx[] = "[ab]{0,2}x";
|
|
static constexpr char varlen[] = "[ab]{1,3}";
|
|
static constexpr char grp2[] = "(a[bc]){1,2}";
|
|
static constexpr char abx[] = "(ab|cd){1,2}x";
|
|
static constexpr char pad_order[] = "pad('0')[a-f0-9]{0,1}";
|
|
static constexpr char pad_x[] = "pad('x')[ab]{0,2}";
|
|
static constexpr char pad_sp[] = "pad(' ')[ab]{0,2}";
|
|
static constexpr char pad_fix[] = "pad('0')[0-9]{2}";
|
|
static constexpr char pad_fix_x[] = "pad('0')[0-9]{2}x";
|
|
static constexpr char two_fields[] = "pad('0')[0-9]{0,1}xpad('0')[0-9]{0,1}";
|
|
static constexpr char pad_quote[] = "pad('\\'')[ab]{0,2}";
|
|
static constexpr char pad_nl[] = "pad('\\n')[ab]{0,2}";
|
|
static constexpr char pad_hex[] = "pad('\\x2a')[ab]{0,2}";
|
|
static constexpr char space_pat[] = "a b";
|
|
static constexpr char alts53[] =
|
|
"!|#|$|%|&|,|-|/|0|1|2|3|4|5|6|7|8|9|:|;|<|=|>|@|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|^|_|`";
|
|
static constexpr char dots32[] = ".{32}";
|
|
|
|
static constexpr char a64[] = "a{64}";
|
|
static constexpr char a0_64[] = "a{0,64}";
|
|
static constexpr char a64b[] = "a{64}b";
|
|
static constexpr char ab32[] = "(ab){32}";
|
|
static constexpr char ab33[] = "(ab){33}";
|
|
static constexpr char alts54[] =
|
|
"!|#|$|%|&|,|-|/|0|1|2|3|4|5|6|7|8|9|:|;|<|=|>|@|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|^|_|`|a";
|
|
static constexpr char dots32ab[] = ".{32}[ab]";
|
|
static constexpr char dots33[] = ".{33}";
|
|
static constexpr char dot65[] = ".{65}";
|
|
static constexpr char qempty[] = "()?";
|
|
static constexpr char deepq[] = "(a?)?";
|
|
static constexpr char both_empty[] = "(|)";
|
|
static constexpr char justbar[] = "|";
|
|
static constexpr char dotora[] = ".|a";
|
|
static constexpr char classora[] = "[ab]|b";
|
|
static constexpr char aq[] = "a?|a";
|
|
static constexpr char padfixd[] = "pad('0')[0-9]{2}[0-9]";
|
|
static constexpr char varfollow[] = "[ab]{0,1}a";
|
|
static constexpr char repnull[] = "(a?){1,2}";
|
|
static constexpr char altnull[] = "(a|){1,2}";
|
|
static constexpr char quantpad[] = "pad('0')[0-9]{1}{1}";
|
|
static constexpr char padneg[] = "pad('a')[^b]{0,1}";
|
|
static constexpr char lohi[] = "a{2,1}";
|
|
static constexpr char unbounded[] = "a{1,}";
|
|
static constexpr char fullneg[] = "[^\\x00-\\xff]";
|
|
static constexpr char plus[] = "a+";
|
|
static constexpr char emptycls[] = "[]";
|
|
static constexpr char negempty[] = "[^]";
|
|
static constexpr char inv[] = "[z-a]";
|
|
static constexpr char inv_esc[] = "[\\x03-\\x01]";
|
|
static constexpr char badesc[] = "\\q";
|
|
static constexpr char badhex[] = "\\xGG";
|
|
static constexpr char shorthex[] = "\\x1";
|
|
static constexpr char openp[] = "(a";
|
|
static constexpr char closep[] = "a)";
|
|
static constexpr char openb[] = "[a-z";
|
|
static constexpr char openq[] = "a{1";
|
|
static constexpr char spaceq[] = "a{1, 2}";
|
|
static constexpr char padopen[] = "pad(";
|
|
static constexpr char padempty[] = "pad('')[a]{1}";
|
|
static constexpr char padnoq[] = "pad('a')[a]";
|
|
static constexpr char padnoc[] = "pad('a')";
|
|
static constexpr char aqq[] = "a??";
|
|
|
|
#define KW2_A8 "aaaaaaaa"
|
|
#define KW2_A64 KW2_A8 KW2_A8 KW2_A8 KW2_A8 KW2_A8 KW2_A8 KW2_A8 KW2_A8
|
|
#define KW2_A512 KW2_A64 KW2_A64 KW2_A64 KW2_A64 KW2_A64 KW2_A64 KW2_A64 KW2_A64
|
|
static constexpr char lits63[] =
|
|
KW2_A8 KW2_A8 KW2_A8 KW2_A8 KW2_A8 KW2_A8 KW2_A8 "aaaaaaa";
|
|
static constexpr char lits64[] = KW2_A64;
|
|
static constexpr char long_pat[] = KW2_A512 KW2_A512;
|
|
#undef KW2_A512
|
|
#undef KW2_A64
|
|
#undef KW2_A8
|
|
|
|
std::size_t bits_for_cardinality(std::uint64_t n)
|
|
{
|
|
if (n <= 1) return 1;
|
|
std::size_t bits = 0;
|
|
for (auto v = n - 1; v != 0; v >>= 1) ++bits;
|
|
return bits;
|
|
}
|
|
|
|
std::vector<std::string> class_bytes(std::initializer_list<unsigned char> chars)
|
|
{
|
|
std::vector<unsigned char> ordered(chars);
|
|
std::sort(ordered.begin(), ordered.end());
|
|
ordered.erase(std::unique(ordered.begin(), ordered.end()), ordered.end());
|
|
std::vector<std::string> out;
|
|
for (unsigned char c : ordered) out.emplace_back(1, static_cast<char>(c));
|
|
return out;
|
|
}
|
|
|
|
std::vector<std::string> byte_span(unsigned lo, unsigned hi)
|
|
{
|
|
std::vector<std::string> out;
|
|
for (unsigned c = lo; c <= hi; ++c) out.emplace_back(1, static_cast<char>(c));
|
|
return out;
|
|
}
|
|
|
|
std::vector<std::string> negated_bytes(std::initializer_list<unsigned char> excluded)
|
|
{
|
|
bool drop[256] = {};
|
|
for (unsigned char c : excluded) drop[c] = true;
|
|
std::vector<std::string> out;
|
|
for (int c = 0; c < 256; ++c)
|
|
if (!drop[c]) out.emplace_back(1, static_cast<char>(c));
|
|
return out;
|
|
}
|
|
|
|
std::vector<std::string> concat_lang(const std::vector<std::string> & left,
|
|
const std::vector<std::string> & right)
|
|
{
|
|
std::vector<std::string> out;
|
|
out.reserve(left.size() * right.size());
|
|
for (const auto & l : left)
|
|
for (const auto & r : right)
|
|
out.push_back(l + r);
|
|
return out;
|
|
}
|
|
|
|
std::vector<std::string> block_lang(std::vector<std::vector<std::string>> parts)
|
|
{
|
|
std::vector<std::string> out;
|
|
for (auto & part : parts)
|
|
out.insert(out.end(), part.begin(), part.end());
|
|
return out;
|
|
}
|
|
|
|
std::vector<std::string> repeat_lang(const std::vector<std::string> & unit, int lo, int hi)
|
|
{
|
|
std::vector<std::string> acc;
|
|
std::vector<std::string> level{""};
|
|
for (int k = 0; k <= hi; ++k)
|
|
{
|
|
if (k >= lo) acc.insert(acc.end(), level.begin(), level.end());
|
|
if (k == hi) break;
|
|
std::vector<std::string> next;
|
|
next.reserve(level.size() * unit.size());
|
|
for (const auto & prefix : level)
|
|
for (const auto & piece : unit)
|
|
next.push_back(prefix + piece);
|
|
level.swap(next);
|
|
}
|
|
return acc;
|
|
}
|
|
|
|
std::vector<std::string> pad_language(std::string digits, int lo, int hi)
|
|
{
|
|
const int radix = static_cast<int>(digits.size());
|
|
int count = 1;
|
|
for (int i = 0; i < hi; ++i) count *= radix;
|
|
std::vector<std::string> out;
|
|
out.reserve(static_cast<std::size_t>(count));
|
|
for (int value = 0; value < count; ++value)
|
|
{
|
|
std::string raw(static_cast<std::size_t>(hi), digits[0]);
|
|
int rest = value;
|
|
for (int i = hi - 1; i >= 0; --i)
|
|
{
|
|
raw[static_cast<std::size_t>(i)] = digits[static_cast<std::size_t>(rest % radix)];
|
|
rest /= radix;
|
|
}
|
|
int start = 0;
|
|
while (start < hi - lo && raw[static_cast<std::size_t>(start)] == digits[0]) ++start;
|
|
out.push_back(raw.substr(static_cast<std::size_t>(start)));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
std::vector<std::string> optional_chain(std::string letters)
|
|
{
|
|
const int n = static_cast<int>(letters.size());
|
|
std::vector<std::string> out;
|
|
const unsigned total = 1u << n;
|
|
out.reserve(total);
|
|
for (unsigned mask = 0; mask < total; ++mask)
|
|
{
|
|
std::string spelling;
|
|
for (int i = 0; i < n; ++i)
|
|
if ((mask & (1u << (n - 1 - i))) != 0) spelling.push_back(letters[static_cast<std::size_t>(i)]);
|
|
out.push_back(std::move(spelling));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
template <const char * Pattern>
|
|
void expect_language(const std::vector<std::string> & canonical)
|
|
{
|
|
using kw = dpf::keyword2<Pattern>;
|
|
ASSERT_EQ(dpf::keyword2_status<Pattern>(), dpf::keyword2_error::ok);
|
|
const auto n = static_cast<std::uint64_t>(std::numeric_limits<kw>::max().rank()) + 1;
|
|
ASSERT_EQ(n, canonical.size());
|
|
ASSERT_EQ(kw::bits, bits_for_cardinality(n));
|
|
ASSERT_EQ(dpf::utils::bitlength_of_v<kw>, kw::bits);
|
|
for (std::uint64_t r = 0; r < n; ++r)
|
|
{
|
|
const std::string & spelling = canonical[static_cast<std::size_t>(r)];
|
|
kw spelled{std::string_view(spelling)};
|
|
kw ranked = kw::from_rank(static_cast<typename kw::integral_type>(r));
|
|
EXPECT_EQ(spelled.rank(), r) << spelling;
|
|
EXPECT_EQ(std::string(ranked), spelling) << r;
|
|
EXPECT_EQ(spelled, ranked);
|
|
EXPECT_EQ(dpf::to_string(ranked), spelling);
|
|
if (r > 0)
|
|
EXPECT_LT(kw::from_rank(static_cast<typename kw::integral_type>(r - 1)), ranked);
|
|
}
|
|
if (!canonical.empty())
|
|
{
|
|
EXPECT_EQ(std::string(std::numeric_limits<kw>::min()), canonical.front());
|
|
EXPECT_EQ(std::string(std::numeric_limits<kw>::max()), canonical.back());
|
|
EXPECT_EQ(std::numeric_limits<kw>::lowest(), std::numeric_limits<kw>::min());
|
|
}
|
|
if (kw::bits < 63 && n < (std::uint64_t{1} << kw::bits))
|
|
{
|
|
kw ghost = kw::from_rank(static_cast<typename kw::integral_type>(n));
|
|
EXPECT_EQ(ghost.rank(), n);
|
|
EXPECT_GT(ghost, std::numeric_limits<kw>::max());
|
|
EXPECT_THROW(static_cast<std::string>(ghost), std::domain_error);
|
|
}
|
|
}
|
|
|
|
template <const char * Pattern>
|
|
void expect_spellings(std::initializer_list<const char *> spellings)
|
|
{
|
|
expect_language<Pattern>(std::vector<std::string>(spellings.begin(), spellings.end()));
|
|
}
|
|
|
|
template <const char * Pattern>
|
|
void expect_only(std::string text)
|
|
{
|
|
expect_language<Pattern>(std::vector<std::string>{std::move(text)});
|
|
}
|
|
|
|
template <const char * Pattern>
|
|
void expect_status(dpf::keyword2_error error)
|
|
{
|
|
EXPECT_EQ(dpf::keyword2_status<Pattern>(), error);
|
|
}
|
|
|
|
template <const char * Pattern>
|
|
void expect_rejected(std::initializer_list<std::string_view> bad)
|
|
{
|
|
using kw = dpf::keyword2<Pattern>;
|
|
for (std::string_view spelling : bad)
|
|
EXPECT_THROW(kw{spelling}, std::domain_error) << std::string(spelling);
|
|
}
|
|
|
|
template <const char * Pattern>
|
|
void roundtrip_language()
|
|
{
|
|
using kw = dpf::keyword2<Pattern>;
|
|
const auto last = std::numeric_limits<kw>::max().rank();
|
|
const auto count = static_cast<std::uint64_t>(last) + 1;
|
|
ASSERT_LE(count, 200000u);
|
|
for (std::uint64_t r = 0; r < count; ++r)
|
|
{
|
|
kw key = kw::from_rank(static_cast<typename kw::integral_type>(r));
|
|
kw back{std::string(key)};
|
|
ASSERT_EQ(back.rank(), r) << r;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(keyword2, decimal_pad_collapses_leading_zeros)
|
|
{
|
|
using kw = dpf::keyword2<dec4>;
|
|
EXPECT_EQ(kw::bits, 14u);
|
|
EXPECT_EQ(kw{"7"}.rank(), 7u);
|
|
EXPECT_EQ(kw{"007"}.rank(), 7u);
|
|
EXPECT_EQ(kw{""}.rank(), 0u);
|
|
EXPECT_EQ(kw{"0"}.rank(), 0u);
|
|
EXPECT_EQ(std::string(kw{"007"}), "7");
|
|
EXPECT_EQ(std::string(std::numeric_limits<kw>::max()), "9999");
|
|
EXPECT_THROW(kw{"10000"}, std::domain_error);
|
|
EXPECT_THROW(kw{"12a"}, std::domain_error);
|
|
}
|
|
|
|
TEST(keyword2, decimal_minimum_width_keeps_a_zero)
|
|
{
|
|
using kw = dpf::keyword2<dec12>;
|
|
EXPECT_EQ(std::string(kw{"0"}), "0");
|
|
EXPECT_EQ(kw{"00"}.rank(), 0u);
|
|
EXPECT_EQ(std::string(kw{"00"}), "0");
|
|
EXPECT_THROW(kw{""}, std::domain_error);
|
|
}
|
|
|
|
TEST(keyword2, nul_pad_matches_legacy_letter_ranks)
|
|
{
|
|
using kw = dpf::keyword2<letters>;
|
|
EXPECT_EQ(kw{"b"}.rank(), 2u);
|
|
EXPECT_EQ(kw{"aa"}.rank(), 28u);
|
|
EXPECT_EQ(kw{"cat"}.rank(), 2234u);
|
|
EXPECT_LT(kw{"b"}.rank(), kw{"aa"}.rank());
|
|
EXPECT_EQ(std::string(kw{"cat"}), "cat");
|
|
EXPECT_EQ(kw{std::string_view("\0a", 2)}.rank(), kw{"a"}.rank());
|
|
}
|
|
|
|
TEST(keyword2, hex_pad_versus_fixed_width)
|
|
{
|
|
using padded = dpf::keyword2<hex3>;
|
|
EXPECT_EQ(padded{"a"}.rank(), 10u);
|
|
EXPECT_EQ(padded{"00a"}.rank(), 10u);
|
|
EXPECT_EQ(std::string(padded{"00a"}), "a");
|
|
|
|
using fixed = dpf::keyword2<hexfixed>;
|
|
EXPECT_EQ(std::string(fixed{"00a"}), "00a");
|
|
EXPECT_THROW(fixed{"a"}, std::domain_error);
|
|
}
|
|
|
|
TEST(keyword2, fixed_digits_keep_leading_zeros)
|
|
{
|
|
using kw = dpf::keyword2<digits4>;
|
|
EXPECT_EQ(kw{"0007"}.rank(), 7u);
|
|
EXPECT_EQ(std::string(kw{"0007"}), "0007");
|
|
EXPECT_THROW(kw{"7"}, std::domain_error);
|
|
}
|
|
|
|
TEST(keyword2, delimited_pad_fields_share_a_rank)
|
|
{
|
|
using kw = dpf::keyword2<fields>;
|
|
EXPECT_EQ(kw{"7-8"}.rank(), 7008u);
|
|
EXPECT_EQ(kw{"07-008"}.rank(), 7008u);
|
|
EXPECT_EQ(std::string(kw{"07-008"}), "7-8");
|
|
}
|
|
|
|
TEST(keyword2, variable_repeat_is_shortlex)
|
|
{
|
|
using kw = dpf::keyword2<words>;
|
|
EXPECT_EQ(std::string(kw::from_rank(0)), "a");
|
|
EXPECT_EQ(std::string(kw::from_rank(1)), "b");
|
|
EXPECT_EQ(std::string(kw::from_rank(2)), "aa");
|
|
EXPECT_LT(kw{"b"}.rank(), kw{"aa"}.rank());
|
|
}
|
|
|
|
TEST(keyword2, alternatives_are_source_order_blocks)
|
|
{
|
|
using kw = dpf::keyword2<either>;
|
|
EXPECT_EQ(std::string(kw{"cat"}), "cat");
|
|
EXPECT_EQ(std::string(kw{"car"}), "car");
|
|
EXPECT_LT(kw{"cat"}.rank(), kw{"car"}.rank());
|
|
}
|
|
|
|
TEST(keyword2, pad_digit_order_follows_the_class_text)
|
|
{
|
|
using kw = dpf::keyword2<holes>;
|
|
EXPECT_EQ(kw{"3"}.rank(), 2u);
|
|
EXPECT_EQ(std::string(kw{"3"}), "3");
|
|
EXPECT_EQ(kw{"13"}.rank(), 5u);
|
|
}
|
|
|
|
TEST(keyword2, rejected_patterns)
|
|
{
|
|
EXPECT_EQ(dpf::keyword2_status<overlap>(), dpf::keyword2_error::overlap);
|
|
EXPECT_EQ(dpf::keyword2_status<ambiguous>(), dpf::keyword2_error::overlap);
|
|
EXPECT_EQ(dpf::keyword2_status<adjacent>(), dpf::keyword2_error::delim);
|
|
EXPECT_EQ(dpf::keyword2_status<undelimited>(), dpf::keyword2_error::delim);
|
|
EXPECT_EQ(dpf::keyword2_status<star>(), dpf::keyword2_error::syntax);
|
|
EXPECT_EQ(dpf::keyword2_status<too_long>(), dpf::keyword2_error::length);
|
|
EXPECT_EQ(dpf::keyword2_status<either>(), dpf::keyword2_error::ok);
|
|
}
|
|
|
|
TEST(keyword2, roundtrip_covers_every_rank)
|
|
{
|
|
roundtrip_language<dec12>();
|
|
roundtrip_language<words>();
|
|
roundtrip_language<holes>();
|
|
roundtrip_language<either>();
|
|
roundtrip_language<fields>();
|
|
roundtrip_language<dec4>();
|
|
roundtrip_language<letters>();
|
|
roundtrip_language<hex3>();
|
|
roundtrip_language<hexfixed>();
|
|
roundtrip_language<digits4>();
|
|
}
|
|
|
|
TEST(keyword2, point_query_treats_pad_spellings_as_one_key)
|
|
{
|
|
using kw = dpf::keyword2<dec4>;
|
|
auto [k0, k1] = dpf::make_dpf(kw{"7"}, dpf::bit{1});
|
|
auto hit = [&](kw point) {
|
|
return dpf::reconstruct(*dpf::eval_point(k0, point), *dpf::eval_point(k1, point))
|
|
== dpf::bit::one;
|
|
};
|
|
EXPECT_TRUE(hit(kw{"7"}));
|
|
EXPECT_TRUE(hit(kw{"007"}));
|
|
EXPECT_FALSE(hit(kw{"42"}));
|
|
}
|
|
|
|
TEST(keyword2, stream_extractor_reports_a_bad_token)
|
|
{
|
|
using kw = dpf::keyword2<dec4>;
|
|
std::stringstream in("007 12a");
|
|
kw good;
|
|
ASSERT_TRUE(static_cast<bool>(in >> good));
|
|
EXPECT_EQ(std::string(good), "7");
|
|
kw bad;
|
|
EXPECT_FALSE(static_cast<bool>(in >> bad));
|
|
}
|
|
|
|
TEST(keyword2, literals_and_escapes_are_single_strings)
|
|
{
|
|
expect_only<empty_pat>("");
|
|
expect_only<group_pat>("");
|
|
expect_only<a_empty>("a");
|
|
expect_only<empty_a>("a");
|
|
expect_only<a_empty_b>("ab");
|
|
expect_only<padding>("padding");
|
|
expect_only<lit_star>("*");
|
|
expect_only<lit_plus>("+");
|
|
expect_only<lit_dot>(".");
|
|
expect_only<lit_q>("?");
|
|
expect_only<lit_lp>("(");
|
|
expect_only<lit_rp>(")");
|
|
expect_only<lit_bar>("|");
|
|
expect_only<lit_lb>("[");
|
|
expect_only<lit_rb>("]");
|
|
expect_only<lit_lc>("{");
|
|
expect_only<lit_rc>("}");
|
|
expect_only<lit_bs>("\\");
|
|
expect_only<lit_n>("\n");
|
|
expect_only<lit_t>("\t");
|
|
expect_only<lit_r>("\r");
|
|
expect_only<lit_A>("A");
|
|
expect_only<lit_nul>(std::string(1, '\0'));
|
|
expect_only<lit_sq>("'");
|
|
expect_only<lit_dq>("\"");
|
|
expect_only<lit_dash>("-");
|
|
expect_only<esc_cat>("\n\tA");
|
|
expect_only<a01>("a");
|
|
expect_only<a00>("");
|
|
expect_only<pad0>("");
|
|
expect_only<space_pat>("a b");
|
|
expect_rejected<pad0>({"0", "00"});
|
|
expect_rejected<a00>({"a"});
|
|
expect_rejected<space_pat>({"a", "ab", "a b"});
|
|
}
|
|
|
|
TEST(keyword2, classes_rank_by_byte_value)
|
|
{
|
|
using kw = dpf::keyword2<cba>;
|
|
EXPECT_EQ(kw{"a"}.rank(), 0u);
|
|
EXPECT_EQ(kw{"c"}.rank(), 2u);
|
|
expect_language<cba>(class_bytes({'c', 'b', 'a'}));
|
|
expect_language<dupcls>(class_bytes({'a', 'a', 'b'}));
|
|
expect_language<dashcls>(class_bytes({'a', '-', 'c'}));
|
|
expect_language<endash>(class_bytes({'a', '-'}));
|
|
expect_language<bracketcls>(class_bytes({']', 'a'}));
|
|
expect_language<range_esc>(byte_span(1, 3));
|
|
expect_language<dashrange>(byte_span(static_cast<unsigned>('-'), static_cast<unsigned>('a')));
|
|
expect_language<highcls>(byte_span(0x7f, 0x81));
|
|
expect_rejected<cba>({"", "ab", "d", "A"});
|
|
}
|
|
|
|
TEST(keyword2, dot_and_negated_class_cover_bytes)
|
|
{
|
|
expect_language<dot1>(byte_span(0, 255));
|
|
expect_language<negab>(negated_bytes({'a', 'b'}));
|
|
expect_rejected<negab>({"a", "b", "", "ab"});
|
|
|
|
using dot = dpf::keyword2<dot2>;
|
|
char pair[] = {1, 2};
|
|
EXPECT_EQ(dot{std::string_view(pair, 2)}.rank(), 258u);
|
|
char hi[] = {'\xff', '\xff'};
|
|
EXPECT_EQ(std::string(dot{std::string_view(hi, 2)}), std::string(hi, hi + 2));
|
|
EXPECT_EQ(dot::bits, 16u);
|
|
expect_rejected<dot2>({"", "a", std::string_view(pair, 1)});
|
|
|
|
std::vector<std::string> optional{""};
|
|
for (int b = 0; b < 256; ++b) optional.emplace_back(1, static_cast<char>(b));
|
|
expect_language<dotq>(optional);
|
|
using opt = dpf::keyword2<dotq>;
|
|
EXPECT_EQ(opt{std::string_view("\0", 1)}.rank(), 1u);
|
|
EXPECT_EQ(std::string(opt::from_rank(256)).size(), 1u);
|
|
EXPECT_EQ(static_cast<unsigned char>(std::string(opt::from_rank(256))[0]), 255u);
|
|
|
|
auto prefixed = concat_lang({"a"}, byte_span(0, 255));
|
|
expect_language<adot>(prefixed);
|
|
}
|
|
|
|
TEST(keyword2, concatenation_is_mixed_radix_and_alternation_is_source_order)
|
|
{
|
|
expect_spellings<src_alt>({"c", "a", "b"});
|
|
expect_spellings<a_ab>({"a", "ab"});
|
|
expect_spellings<ab_a>({"ab", "a"});
|
|
expect_spellings<mid_empty>({"a", "", "b"});
|
|
expect_spellings<lead_empty>({"", "a"});
|
|
expect_spellings<trail_empty>({"a", ""});
|
|
expect_spellings<nested_alt>({"ac", "add", "bbc", "bbdd"});
|
|
expect_language<blocks>(concat_lang({"a", "b"}, {"c", "d"}));
|
|
expect_spellings<nest_paren>({"a", "b"});
|
|
expect_language<cls_cat>(concat_lang({"a", "b"}, {"c", "d"}));
|
|
expect_spellings<xcls>({"xa", "xb"});
|
|
expect_spellings<three>({"acd", "ace", "bcd", "bce"});
|
|
expect_language<alts53>([&] {
|
|
std::vector<std::string> out;
|
|
std::string token;
|
|
for (char c : std::string(alts53))
|
|
{
|
|
if (c == '|')
|
|
{
|
|
out.push_back(token);
|
|
token.clear();
|
|
}
|
|
else token.push_back(c);
|
|
}
|
|
out.push_back(token);
|
|
return out;
|
|
}());
|
|
using alt = dpf::keyword2<alts53>;
|
|
EXPECT_EQ(alt{"!"}.rank(), 0u);
|
|
EXPECT_EQ(alt{"0"}.rank(), 8u);
|
|
EXPECT_EQ(alt{"A"}.rank(), 24u);
|
|
EXPECT_EQ(std::string(std::numeric_limits<alt>::max()), "`");
|
|
}
|
|
|
|
TEST(keyword2, quantifiers_are_shortlex_and_left_to_right)
|
|
{
|
|
expect_language<opt2>(optional_chain("ab"));
|
|
expect_language<opt7>(optional_chain("abcdefg"));
|
|
using chain = dpf::keyword2<opt7>;
|
|
EXPECT_EQ(chain{""}.rank(), 0u);
|
|
EXPECT_EQ(chain{"g"}.rank(), 1u);
|
|
EXPECT_EQ(chain{"a"}.rank(), 64u);
|
|
EXPECT_EQ(chain{"abcdefg"}.rank(), 127u);
|
|
|
|
expect_language<alt_rep>(repeat_lang({"a", "b"}, 2, 2));
|
|
expect_language<alt_opt>(block_lang({{""}, {"a", "b"}}));
|
|
expect_language<alt_pow>(repeat_lang({"c", "a", "b"}, 2, 2));
|
|
expect_language<grp_rep>(repeat_lang({"ab"}, 0, 2));
|
|
expect_spellings<grp_opt>({"", "ab"});
|
|
expect_spellings<a_rep>({"aa", "aaa", "aaaa"});
|
|
expect_language<cls2>(repeat_lang({"a", "b"}, 3, 3));
|
|
expect_language<repx>(concat_lang(repeat_lang({"a", "b"}, 0, 2), {"x"}));
|
|
expect_language<varlen>(repeat_lang({"a", "b"}, 1, 3));
|
|
expect_language<grp2>(repeat_lang({"ab", "ac"}, 1, 2));
|
|
expect_language<abx>(concat_lang(repeat_lang({"ab", "cd"}, 1, 2), {"x"}));
|
|
expect_language<cls_pow8>(repeat_lang({"a", "b"}, 8, 8));
|
|
expect_language<cls_pow9>(repeat_lang({"a", "b"}, 9, 9));
|
|
|
|
std::vector<std::string> up_to_63;
|
|
for (int n = 0; n <= 63; ++n) up_to_63.emplace_back(static_cast<std::size_t>(n), 'a');
|
|
expect_language<a0_63>(up_to_63);
|
|
expect_only<a63>(std::string(63, 'a'));
|
|
expect_only<lits63>(std::string(63, 'a'));
|
|
expect_rejected<a_rep>({"", "a", "aaaaa"});
|
|
expect_rejected<repx>({"", "a", "aa", "xx"});
|
|
}
|
|
|
|
TEST(keyword2, pad_fields_rank_as_integers_in_written_digit_order)
|
|
{
|
|
expect_language<pad_order>(pad_language("0abcdef123456789", 0, 1));
|
|
expect_language<pad_x>(pad_language("xab", 0, 2));
|
|
expect_language<pad_sp>(pad_language(" ab", 0, 2));
|
|
expect_language<pad_fix>(pad_language("0123456789", 2, 2));
|
|
expect_language<pad_fix_x>(concat_lang(pad_language("0123456789", 2, 2), {"x"}));
|
|
expect_language<two_fields>(concat_lang(
|
|
concat_lang(pad_language("0123456789", 0, 1), {"x"}),
|
|
pad_language("0123456789", 0, 1)));
|
|
expect_language<pad_quote>(pad_language("'ab", 0, 2));
|
|
expect_language<pad_nl>(pad_language("\nab", 0, 2));
|
|
expect_language<pad_hex>(pad_language("*ab", 0, 2));
|
|
|
|
using px = dpf::keyword2<pad_x>;
|
|
EXPECT_EQ(px{"x"}, px{""});
|
|
EXPECT_EQ(px{"xx"}, px{""});
|
|
EXPECT_EQ(std::string(px{"xa"}), "a");
|
|
EXPECT_EQ(std::string(px{"ax"}), "ax");
|
|
EXPECT_EQ(px{"xa"}.rank(), 1u);
|
|
|
|
using pq = dpf::keyword2<pad_quote>;
|
|
EXPECT_EQ(pq{"'"}, pq{""});
|
|
EXPECT_EQ(std::string(pq{"'a"}), "a");
|
|
|
|
using pn = dpf::keyword2<pad_nl>;
|
|
EXPECT_EQ(pn{std::string_view("\n", 1)}, pn{""});
|
|
EXPECT_EQ(std::string(pn{std::string_view("\na", 2)}), "a");
|
|
|
|
using ps = dpf::keyword2<pad_sp>;
|
|
EXPECT_EQ(ps{" "}, ps{""});
|
|
EXPECT_EQ(std::string(ps{" a"}), "a");
|
|
|
|
using pf = dpf::keyword2<pad_fix>;
|
|
EXPECT_EQ(std::string(pf{"07"}), "07");
|
|
EXPECT_EQ(pf{"07"}.rank(), 7u);
|
|
expect_rejected<pad_fix>({"7", "007", "a0"});
|
|
|
|
using dec = dpf::keyword2<dec4>;
|
|
for (int value = 0; value < 10000; ++value)
|
|
{
|
|
std::string body = std::to_string(value);
|
|
std::string padded = std::string(4 - body.size(), '0') + body;
|
|
std::string canonical = value == 0 ? std::string() : body;
|
|
EXPECT_EQ(dec{padded}.rank(), static_cast<unsigned>(value));
|
|
EXPECT_EQ(std::string(dec{padded}), canonical);
|
|
}
|
|
|
|
expect_language<holes>(pad_language("013", 0, 2));
|
|
}
|
|
|
|
TEST(keyword2, syntax_errors_name_the_broken_token)
|
|
{
|
|
static_assert(dpf::keyword2_error::ok == dpf::keyword2_error{0});
|
|
expect_status<star>(dpf::keyword2_error::syntax);
|
|
expect_status<plus>(dpf::keyword2_error::syntax);
|
|
expect_status<esc_star>(dpf::keyword2_error::syntax);
|
|
expect_status<esc_plus>(dpf::keyword2_error::syntax);
|
|
expect_status<aqq>(dpf::keyword2_error::syntax);
|
|
expect_status<emptycls>(dpf::keyword2_error::syntax);
|
|
expect_status<negempty>(dpf::keyword2_error::syntax);
|
|
expect_status<fullneg>(dpf::keyword2_error::syntax);
|
|
expect_status<inv>(dpf::keyword2_error::syntax);
|
|
expect_status<inv_esc>(dpf::keyword2_error::syntax);
|
|
expect_status<badesc>(dpf::keyword2_error::syntax);
|
|
expect_status<badhex>(dpf::keyword2_error::syntax);
|
|
expect_status<shorthex>(dpf::keyword2_error::syntax);
|
|
expect_status<openp>(dpf::keyword2_error::syntax);
|
|
expect_status<closep>(dpf::keyword2_error::syntax);
|
|
expect_status<openb>(dpf::keyword2_error::syntax);
|
|
expect_status<openq>(dpf::keyword2_error::syntax);
|
|
expect_status<spaceq>(dpf::keyword2_error::syntax);
|
|
expect_status<lohi>(dpf::keyword2_error::syntax);
|
|
expect_status<unbounded>(dpf::keyword2_error::syntax);
|
|
expect_status<quantpad>(dpf::keyword2_error::syntax);
|
|
expect_status<padneg>(dpf::keyword2_error::syntax);
|
|
expect_status<padopen>(dpf::keyword2_error::syntax);
|
|
expect_status<padempty>(dpf::keyword2_error::syntax);
|
|
expect_status<padnoq>(dpf::keyword2_error::syntax);
|
|
expect_status<padnoc>(dpf::keyword2_error::syntax);
|
|
expect_status<long_pat>(dpf::keyword2_error::syntax);
|
|
}
|
|
|
|
TEST(keyword2, delimiter_and_overlap_errors)
|
|
{
|
|
expect_status<adjacent>(dpf::keyword2_error::delim);
|
|
expect_status<undelimited>(dpf::keyword2_error::delim);
|
|
expect_status<qempty>(dpf::keyword2_error::delim);
|
|
expect_status<deepq>(dpf::keyword2_error::delim);
|
|
expect_status<padfixd>(dpf::keyword2_error::delim);
|
|
expect_status<varfollow>(dpf::keyword2_error::delim);
|
|
expect_status<repnull>(dpf::keyword2_error::delim);
|
|
expect_status<altnull>(dpf::keyword2_error::delim);
|
|
|
|
expect_status<overlap>(dpf::keyword2_error::overlap);
|
|
expect_status<ambiguous>(dpf::keyword2_error::overlap);
|
|
expect_status<both_empty>(dpf::keyword2_error::overlap);
|
|
expect_status<justbar>(dpf::keyword2_error::overlap);
|
|
expect_status<dotora>(dpf::keyword2_error::overlap);
|
|
expect_status<classora>(dpf::keyword2_error::overlap);
|
|
expect_status<aq>(dpf::keyword2_error::overlap);
|
|
}
|
|
|
|
TEST(keyword2, length_state_and_width_caps)
|
|
{
|
|
expect_status<a64>(dpf::keyword2_error::states);
|
|
expect_status<a0_64>(dpf::keyword2_error::states);
|
|
expect_status<ab32>(dpf::keyword2_error::states);
|
|
expect_status<lits64>(dpf::keyword2_error::states);
|
|
expect_status<alts54>(dpf::keyword2_error::states);
|
|
|
|
expect_status<a64b>(dpf::keyword2_error::length);
|
|
expect_status<ab33>(dpf::keyword2_error::length);
|
|
expect_status<too_long>(dpf::keyword2_error::length);
|
|
expect_status<dot65>(dpf::keyword2_error::length);
|
|
|
|
expect_status<dots33>(dpf::keyword2_error::width);
|
|
expect_status<dots32ab>(dpf::keyword2_error::width);
|
|
|
|
using full = dpf::keyword2<dots32>;
|
|
EXPECT_EQ(dpf::keyword2_status<dots32>(), dpf::keyword2_error::ok);
|
|
EXPECT_EQ(full::bits, 256u);
|
|
EXPECT_EQ(std::string(full::from_rank(0)), std::string(32, '\0'));
|
|
char low[32] = {};
|
|
low[31] = 5;
|
|
EXPECT_EQ(full{std::string_view(low, 32)}, full::from_rank(full::integral_type{5}));
|
|
char high[32] = {};
|
|
high[0] = 1;
|
|
EXPECT_GT(full{std::string_view(high, 32)}, full{std::string_view(low, 32)});
|
|
EXPECT_EQ(std::string(std::numeric_limits<full>::max()), std::string(32, '\xff'));
|
|
EXPECT_EQ(std::numeric_limits<full>::digits, 256);
|
|
expect_rejected<dots32>({std::string(31, '\0'), std::string(33, 'a')});
|
|
}
|
|
|
|
TEST(keyword2, values_compare_assign_and_print_as_spellings)
|
|
{
|
|
using kw = dpf::keyword2<dec4>;
|
|
kw original{"42"};
|
|
kw copy = original;
|
|
kw moved = std::move(copy);
|
|
EXPECT_EQ(std::string(moved), "42");
|
|
EXPECT_EQ(moved, original);
|
|
kw assigned;
|
|
EXPECT_EQ(assigned.rank(), 0u);
|
|
EXPECT_FALSE(static_cast<bool>(assigned));
|
|
assigned = std::string_view{"007"};
|
|
EXPECT_EQ(std::string(assigned), "7");
|
|
EXPECT_TRUE(static_cast<bool>(assigned));
|
|
assigned = original;
|
|
EXPECT_EQ(assigned, kw{"42"});
|
|
EXPECT_EQ(kw{"007"}, kw{"7"});
|
|
EXPECT_FALSE(kw{"007"} < kw{"7"});
|
|
EXPECT_LT(kw{"7"}, kw{"42"});
|
|
EXPECT_EQ(static_cast<kw::integral_type>(kw{"42"}), 42u);
|
|
|
|
kw held{"42"};
|
|
EXPECT_THROW(held = std::string_view{"12a"}, std::domain_error);
|
|
EXPECT_EQ(std::string(held), "42");
|
|
|
|
std::ostringstream printed;
|
|
printed << dpf::keyword2<hex3>{"00a"};
|
|
EXPECT_EQ(printed.str(), "a");
|
|
std::ostringstream empty;
|
|
empty << kw{""};
|
|
EXPECT_TRUE(empty.str().empty());
|
|
|
|
std::stringstream spaced(" 00a b zz");
|
|
dpf::keyword2<hex3> hex;
|
|
ASSERT_TRUE(static_cast<bool>(spaced >> hex));
|
|
EXPECT_EQ(std::string(hex), "a");
|
|
ASSERT_TRUE(static_cast<bool>(spaced >> hex));
|
|
EXPECT_EQ(hex.rank(), 11u);
|
|
dpf::keyword2<hex3> kept = hex;
|
|
EXPECT_FALSE(static_cast<bool>(spaced >> kept));
|
|
EXPECT_EQ(kept, hex);
|
|
|
|
std::stringstream retry("zz a");
|
|
dpf::keyword2<hex3> again{"b"};
|
|
EXPECT_FALSE(static_cast<bool>(retry >> again));
|
|
EXPECT_EQ(std::string(again), "b");
|
|
retry.clear();
|
|
ASSERT_TRUE(static_cast<bool>(retry >> again));
|
|
EXPECT_EQ(std::string(again), "a");
|
|
|
|
std::stringstream blank(" ");
|
|
EXPECT_FALSE(static_cast<bool>(blank >> again));
|
|
EXPECT_EQ(std::string(again), "a");
|
|
|
|
using spaced_kw = dpf::keyword2<space_pat>;
|
|
std::stringstream words_in;
|
|
words_in << spaced_kw{"a b"};
|
|
EXPECT_EQ(words_in.str(), "a b");
|
|
spaced_kw unread{"a b"};
|
|
EXPECT_FALSE(static_cast<bool>(words_in >> unread));
|
|
EXPECT_EQ(std::string(unread), "a b");
|
|
|
|
using dot = dpf::keyword2<dot2>;
|
|
char raw[] = {0, 1};
|
|
EXPECT_EQ(dot{std::string_view(raw, 2)}.rank(), 1u);
|
|
EXPECT_THROW(dot{"\0\1"}, std::domain_error);
|
|
|
|
try
|
|
{
|
|
kw{"nope"};
|
|
FAIL() << "expected domain_error";
|
|
}
|
|
catch (const std::domain_error & err)
|
|
{
|
|
EXPECT_NE(std::string(err.what()).find("keyword2"), std::string::npos);
|
|
}
|
|
try
|
|
{
|
|
static_cast<std::string>(kw::from_rank(10000));
|
|
FAIL() << "expected domain_error";
|
|
}
|
|
catch (const std::domain_error & err)
|
|
{
|
|
EXPECT_NE(std::string(err.what()).find("outside"), std::string::npos);
|
|
}
|
|
}
|
|
|
|
TEST(keyword2, numeric_limits_and_bit_utilities_follow_the_rank)
|
|
{
|
|
using kw = dpf::keyword2<words>;
|
|
EXPECT_EQ(kw::bits, 3u);
|
|
EXPECT_EQ(std::numeric_limits<kw>::digits, 3);
|
|
EXPECT_EQ(std::numeric_limits<kw>::digits10, 0);
|
|
EXPECT_EQ(std::numeric_limits<kw>::radix, 2);
|
|
EXPECT_FALSE(std::numeric_limits<kw>::is_signed);
|
|
EXPECT_TRUE(std::numeric_limits<kw>::is_integer);
|
|
EXPECT_TRUE(std::numeric_limits<kw>::is_exact);
|
|
EXPECT_TRUE(std::numeric_limits<kw>::is_bounded);
|
|
EXPECT_TRUE(std::numeric_limits<kw>::is_modulo);
|
|
EXPECT_FALSE(std::numeric_limits<kw>::has_infinity);
|
|
EXPECT_EQ(std::numeric_limits<kw>::epsilon().rank(), 0u);
|
|
EXPECT_EQ(std::numeric_limits<kw>::min(), kw{"a"});
|
|
EXPECT_EQ(std::string(std::numeric_limits<kw>::max()), "bb");
|
|
EXPECT_EQ(std::numeric_limits<kw const>::digits, 3);
|
|
EXPECT_EQ(std::numeric_limits<kw volatile>::max(), std::numeric_limits<kw>::max());
|
|
EXPECT_EQ(std::numeric_limits<kw const volatile>::min(), kw{"a"});
|
|
|
|
using dec = dpf::keyword2<dec4>;
|
|
EXPECT_EQ(std::numeric_limits<dec>::digits, 14);
|
|
EXPECT_EQ(std::numeric_limits<dec>::digits10, 4);
|
|
|
|
EXPECT_EQ(std::string(dpf::utils::msb_of_v<kw>), "ba");
|
|
EXPECT_EQ(dpf::utils::make_default_v<kw>, kw{"b"});
|
|
EXPECT_EQ(dpf::utils::make_from_integral_value<kw>{}(4), kw{"ba"});
|
|
EXPECT_EQ(dpf::utils::to_integral_type<kw>{}(kw{"ba"}), 4u);
|
|
EXPECT_EQ(dpf::utils::mod_pow_2<kw>{}(kw{"bb"}, 2), 1u);
|
|
EXPECT_EQ(dpf::utils::countl_zero_symmetric_difference<kw>{}(kw{"a"}, kw{"b"}), 2u);
|
|
EXPECT_EQ(dpf::utils::countl_zero_symmetric_difference<kw>{}(kw{"aa"}, kw{"aa"}), kw::bits);
|
|
EXPECT_EQ((~kw{"a"}).rank(), 7u);
|
|
EXPECT_EQ((~kw::from_rank(7)).rank(), 0u);
|
|
|
|
kw cursor = std::numeric_limits<kw>::max();
|
|
++cursor;
|
|
EXPECT_EQ(cursor.rank(), 6u);
|
|
EXPECT_THROW(static_cast<std::string>(cursor), std::domain_error);
|
|
cursor = kw::from_rank(7);
|
|
cursor++;
|
|
EXPECT_EQ(cursor.rank(), 0u);
|
|
EXPECT_EQ(std::string(cursor), "a");
|
|
}
|
|
|
|
TEST(keyword2, dpf_queries_follow_the_rank)
|
|
{
|
|
using kw = dpf::keyword2<cls_pow8>;
|
|
kw target{"abababab"};
|
|
ASSERT_EQ(target.rank(), 0b01010101u);
|
|
auto [k0, k1] = dpf::make_dpf(target, dpf::bit{1});
|
|
auto memo0 = dpf::make_basic_path_memoizer<std::decay_t<decltype(k0)>>();
|
|
auto memo1 = dpf::make_basic_path_memoizer<std::decay_t<decltype(k1)>>();
|
|
|
|
auto hit = [&](kw point, auto & m0, auto & m1) {
|
|
return dpf::reconstruct(*dpf::eval_point(k0, point, m0), *dpf::eval_point(k1, point, m1))
|
|
== dpf::bit::one;
|
|
};
|
|
auto plain0 = dpf::make_nonmemoizing_path_memoizer<std::decay_t<decltype(k0)>>();
|
|
auto plain1 = dpf::make_nonmemoizing_path_memoizer<std::decay_t<decltype(k1)>>();
|
|
for (unsigned rank = 0; rank < (1u << kw::bits); ++rank)
|
|
{
|
|
kw point = kw::from_rank(rank);
|
|
EXPECT_EQ(hit(point, plain0, plain1), point == target) << rank;
|
|
EXPECT_EQ(hit(point, memo0, memo1), point == target) << rank;
|
|
}
|
|
|
|
kw from = kw::from_rank(83);
|
|
kw to = kw::from_rank(87);
|
|
auto [buf0, iter0] = dpf::eval_interval(k0, from, to);
|
|
auto [buf1, iter1] = dpf::eval_interval(k1, from, to);
|
|
auto it0 = iter0.begin();
|
|
auto it1 = iter1.begin();
|
|
for (unsigned rank = 83; it0 != iter0.end(); ++rank, ++it0, ++it1)
|
|
{
|
|
bool on = static_cast<bool>(*it0) ^ static_cast<bool>(*it1);
|
|
EXPECT_EQ(on, rank == target.rank()) << rank;
|
|
}
|
|
|
|
auto [full0, full0_iter] = dpf::eval_full(k0);
|
|
auto [full1, full1_iter] = dpf::eval_full(k1);
|
|
auto fit0 = full0_iter.begin();
|
|
auto fit1 = full1_iter.begin();
|
|
unsigned seen = 0;
|
|
for (; fit0 != full0_iter.end(); ++seen, ++fit0, ++fit1)
|
|
{
|
|
bool on = static_cast<bool>(*fit0) ^ static_cast<bool>(*fit1);
|
|
EXPECT_EQ(on, seen == target.rank()) << seen;
|
|
}
|
|
EXPECT_EQ(seen, 1u << kw::bits);
|
|
}
|
|
|
|
static_assert(dpf::keyword2<src_alt>{"c"}.rank() == 0);
|
|
static_assert(dpf::keyword2<src_alt>{"a"}.rank() == 1);
|
|
static_assert(dpf::keyword2<src_alt>{"c"} < dpf::keyword2<src_alt>{"b"});
|
|
static_assert(dpf::keyword2<dec4>{"007"}.rank() == 7);
|
|
static_assert(dpf::keyword2<opt2>{""}.rank() == 0);
|
|
static_assert(dpf::keyword2<opt2>{"ab"}.rank() == 3);
|
|
static_assert(dpf::keyword2_status<star>() == dpf::keyword2_error::syntax);
|
|
static_assert(dpf::keyword2_status<overlap>() == dpf::keyword2_error::overlap);
|
|
static_assert(dpf::keyword2_status<adjacent>() == dpf::keyword2_error::delim);
|
|
static_assert(dpf::keyword2_status<a64>() == dpf::keyword2_error::states);
|
|
static_assert(dpf::keyword2_status<a64b>() == dpf::keyword2_error::length);
|
|
static_assert(dpf::keyword2_status<dots33>() == dpf::keyword2_error::width);
|
|
static_assert(std::numeric_limits<dpf::keyword2<words>>::max().rank() == 5);
|
|
static_assert(std::numeric_limits<dpf::keyword2<either>>::min().rank() == 0);
|