#ifndef TSTRINGS_H_INCLUDED_ #define TSTRINGS_H_INCLUDED_ #define KBLIB_DEF_MACROS 1 #include "kblib/kblib.h" #include "floating_array.h" #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "random.h" //move to grammar file header //translate old-style doubled-character escapes into backslash escapes // inline std::string compat_filter(std::string_view in); //remove backslash-escape sequences [[nodiscard]] inline std::string unescape(std::string_view in) { bool escape = false; std::string ret; for (auto& c : in) { if (!escape) { if (c == '\\') { escape = true; } else { ret.push_back(c); } } else { ret.push_back(c); } } return ret; } std::pair argsDeclared(std::string_view in); struct node; using arg_t = std::variant, const node*, wordgen_error, std::unique_ptr>; using argslist = std::vector; [[nodiscard]] std::string stringize(const arg_t& arg); [[nodiscard]] std::string stringize(arg_t&& arg); class transformer { public: [[nodiscard]] virtual arg_t operator()(argslist) const = 0; virtual ~transformer() noexcept = default; }; class Echo final : public transformer { public: [[nodiscard]] arg_t operator()(argslist v) const override; }; class bytecode_machine; class Evaluator final : public transformer { public: Evaluator(bytecode_machine* owner) : owner(owner) {} [[nodiscard]] arg_t operator()(argslist v) const override; private: bytecode_machine* owner; }; namespace helpers { template using _t_h = std::is_convertible< decltype(std::declval()(std::declval())) , arg_t >; template > struct is_transformer : //can't find is_invocable //std::is_invocable public std::false_type {}; template struct is_transformer>> : public _t_h {}; template constexpr bool is_transformer_v = is_transformer::value; }; template < typename Callable, typename std::enable_if_t, int> = 0> class dynamic_transformer final : public transformer { public: dynamic_transformer(Callable _c) : c(std::move(_c)) {} [[nodiscard]] arg_t operator()(argslist v) const override {return c(std::move(v));} private: Callable c; }; template [[nodiscard]] std::unique_ptr>> make_transformer(Callable&& c) {return std::make_unique>>(std::forward(c));} enum class op : unsigned char { null=0, //monostate text, //string push_func, //transformer* call_func, //monostate argument, //int special_argument, //string ellipsis, //string }; using op_data = std::variant; struct bytecode_op { op type; op_data data; }; using bytecodes = boost::container::small_vector; struct token; class datafile; struct counters { int depthLimit; int expansionsLimit; int* expansions; int depth {0}; }; inline counters incr(counters& c) { ++(*c.expansions); return {c.depthLimit, c.expansionsLimit, c.expansions, c.depth + 1}; } using ArgsType = floating_array>>; ArgsType copy(const ArgsType& special, const std::vector& vals); [[nodiscard]] std::map, std::less<>> default_transformers(bytecode_machine*, datafile*, RandomGenerator&); [[nodiscard]] std::vector> default_names(); //One per data file class bytecode_machine { private: std::map, std::less<>> transformers; std::vector> arg_names; public: bytecode_machine(datafile* owner, RandomGenerator& rng) : transformers(default_transformers(this, owner, rng)) , arg_names(default_names()) {transformers["."] = std::make_unique();} //t must map to a contiguous sequence of negative numbers ending at -1 bytecode_machine(decltype(transformers) t, decltype(arg_names) a) : transformers(std::move(t)) , arg_names(std::move(a)) {transformers["."] = std::make_unique();} [[nodiscard]] bytecodes to_bytes(const std::string&) const; [[nodiscard]] std::string eval(const bytecodes&, const std::vector&, const counters&, int argc) const; // Given the name of a special argument, return its index in an ArgsType /* Special argument documentation: * * #p: escaped | (pipe) character * #lt: escaped < (less than) character * #gt: escaped > (greater than) character * * #d: current expansion depth * #D: maximum expansion depth * * #e: current expansion count * #E: maximum expansion count * * #c: number of normal arguments passed to this node * #C: number of positional arguments declared by this node * * #a: a list containing the full series of declared arguments * ...: a list containing all extra (variadic) arguments * #A: the concatenation of #a and ... * */ [[nodiscard]] int special_argument(std::string_view) const; [[nodiscard]] int min_argument() const; // Returns the transformer with the given name [[nodiscard]] const transformer& lookup(const std::string& name) const noexcept(false) { return *transformers.at(name); } // Returns a string which is a valid name for t // O(transformers.size()), so call it sparingly // Note that the reverse mapping is not unique, and the returned string is unspecified in the ambiguous case. // That is: // given: std::string fun; // reverse_lookup(lookup(fun)) == fun // is unspecified // While: // given: const transformer *t; // &lookup(reverse_lookup(*t)) == t // is guaranteed [[nodiscard]] const std::string& reverse_lookup(const transformer& t) const noexcept(false); //register a new transformer by name //returns false if a transformer with that name already exists bool set(std::string key, std::unique_ptr val) { return transformers.emplace(std::move(key), std::move(val)).second; } //re-assign a name to a new transformer (key must already be associated with a transformer) void reset(const std::string& key, std::unique_ptr val) { // . is an invariant function assert(key != "."); auto it = transformers.find(key); assert(it != transformers.end()); //reset does not register new transformers it->second = std::move(val); } //clears a key void unset(const std::string& key) { assert(key != "."); auto it = transformers.find(key); assert(it != transformers.end()); //key must be registered already transformers.erase(it); } private: friend bytecode_op token_to_bytecode(token in, const bytecode_machine& b); }; [[nodiscard]] std::string to_string(const bytecode_op& op, const bytecode_machine& b); [[nodiscard]] std::string to_string(const op_data& op, const bytecode_machine& b); [[nodiscard]] std::string serialize(const bytecode_op& op, const bytecode_machine& b); #endif //TSTRINGS_H_INCLUDED_