#ifndef INTERPOLATE_H_INCLUDED_ #define INTERPOLATE_H_INCLUDED_ #include "containers.h" #include "tstrings.h" #include "random.h" #include #include #include #include #include #include #include #include "cppcoro/generator.hpp" #include #include namespace details { template struct tag { using type = T; }; // base case: just fail template struct min_unsigned; // recursive case: check using numeric_limits template struct min_unsigned : std::conditional_t<(V <= sizeof(T)), tag, min_unsigned> { }; template using min_unsigned_t = typename details::min_unsigned::type; } template struct str_prefix { static_assert(N > 0, "str_prefix of size zero is disallowed"); char str[N]; constexpr str_prefix() : str{} {} constexpr str_prefix(std::string_view s) : str{} { kblib::copy_n(s.begin(), std::min(s.size(), (std::size_t)N), &str[0]); } constexpr str_prefix reverse() const { str_prefix reversed; for (std::size_t i = 0; i < N; ++i) { reversed.str[N - i - 1] = str[i]; } return reversed; } //I checked already and compilers can't inline this code, but it's needed for constexpr capability. template ::digits * (N-1)> constexpr auto to_uint() const { using To = details::min_unsigned_t; static_assert(N <= sizeof(To)); if constexpr (N >= 2) { return static_cast(str[N-1]) << shift | str_prefix(std::string_view{ std::begin(str), N}).template to_uint< shift - std::numeric_limits::digits>(); } else { return static_cast(str[0]) << shift; } } //This function is much faster than the above but is not constexpr. auto to_uint_fast() const { using To = details::min_unsigned_t; static_assert(N <= sizeof(To)); To x; if constexpr (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) { std::memcpy(&x, &str[0], N); } else { auto r = reverse(); std::memcpy(&x, &r.str[0], N); } return x; } constexpr char& operator[](std::size_t n) {return str[n];} constexpr const char& operator[](std::size_t n) const {return str[n];} constexpr friend bool operator==(str_prefix lhs, str_prefix rhs) { return kblib::equal(&lhs[0], &lhs[N-1], &rhs[0]); } constexpr friend bool operator!=(str_prefix lhs, str_prefix rhs) { return !(lhs == rhs); } constexpr friend bool operator<(str_prefix lesser, str_prefix greater) { return kblib::lexicographical_compare(&lesser[0], &lesser[N-1], &greater[0], &greater[N-1]); } constexpr friend bool operator>(str_prefix greater, str_prefix lesser) { return lesser < greater; } constexpr friend bool operator<=(str_prefix lesser, str_prefix greater) { return !(greater < lesser); } constexpr friend bool operator>=(str_prefix greater, str_prefix lesser) { return !(greater < lesser); } }; //In order for two channel names to collide, they must have a common prefix of at least 4 characters and hash to the same 32-bit value. Considering a file can be assumed to have less than ten channels all with distinct and short names, this is astronomically unlikely. using channelID = uint64_t; constexpr inline channelID ch(std::string_view s) { return (static_cast(str_prefix<4>(s).to_uint_fast())<<32 | kblib::FNVa(s)); } constexpr channelID operator""_ch(const char* s, std::size_t len) { return (static_cast(str_prefix<4>({s, len}).to_uint())<<32 | kblib::FNVa(std::string_view{s, len})); } struct path_t { int branch; std::vector children; }; using word_data_t = vmap; struct Word { word_data_t data; double freq; path_t path; std::string& val() {return data["val"_ch];} const std::string& val() const {return data.at("val"_ch);} }; inline auto word_from_val(std::string v) -> word_data_t { word_data_t ret; ret.emplace("val"_ch, std::move(v)); return ret; } std::string format(const Word&, std::string_view fmt); using string_transformer = std::function; using replace_sequence = std::vector; struct node { using Alternative = vmap; std::vector> freqs; std::vector vals; std::vector other_channels; int declared_argc; bool variadic; std::string name; }; struct ReplaceStage { vmap actions; Word operator()(const Word&) const; }; class datafile { public: datafile() = default; datafile(YAML::Node data, RandomGenerator&); Word transform(Word, bool keephist, std::string_view hist_sep) const; // private: bytecode_machine bm; static std::tuple parse_nodename(std::string_view); static std::unordered_map& ch_db(); vmap channelNames; vmap> externFiles; small_vector replace; std::optional startNode; vmap, 1> nodes; node* find_node(std::string_view name); const node* find_node(std::string_view name) const; node* find_node(std::string_view name, int argc); const node* find_node(std::string_view name, int argc) const; datafile* lookup_domain(std::string_view name) { if (name.empty()) { return this; } if (auto check = kblib::get_check(externFiles, ch(name))) { return check->second.get(); } return nullptr; // auto it = externFiles.find(ch(name)); // if (it == externFiles.end()) { // return nullptr; // } // return it->second.get(); } const datafile* lookup_domain(std::string_view name) const { if (name.empty()) { return this; } if (auto check = kblib::get_check(externFiles, ch(name))) { return check->second.get(); } return nullptr; // auto it = externFiles.find(ch(name)); // if (it == externFiles.end()) { // return nullptr; // } // return it->second.get(); } }; using flist_t = std::vector; using ilist_t = std::vector>>; struct noderef { std::optional source; std::string name; std::vector args; std::variant freq_override; }; //After eval [[nodiscard]] cppcoro::generator> fparse(const datafile&, const std::string& s); Word chooseFrom(const datafile&, RandomGenerator&, counters, const node&, const std::vector& args, std::variant freq_override); small_vector freqs_of(const bytecode_machine& bm, const node& n, const std::vector& args, const counters& c, int argc); struct basic_parse_token { std::string pre_text; std::optional id; std::string mods; }; cppcoro::generator basic_parser(std::string_view input); #endif //INTERPOLATE_H_INCLUDED_