#define KBLIB_DEF_MACROS 1 #include "kblib/build.h" #include "kblib/convert.h" #undef KBLIB_DEF_MACROS #include "error.h" #include "interpolate.h" #include "logger.h" #include "srell/srell.hpp" #include #include #include using namespace std::literals; template using vstack = kblib::stack; using kblib::coerce; using kblib::lexical_coerce; using kblib::pop; [[nodiscard]] auto stringize(const arg_t& arg) -> std::string { return kblib::visit2( arg, [](const std::string& str) { return str; }, [](const std::vector& vec) { return std::accumulate(vec.begin(), vec.end(), std::string{}); }, [](double x) -> std::string { return std::to_string(x); }, [](const node* n) { return kblib::concat(n->name, std::string(n->declared_argc, '|'), kblib::repeat("..."s, n->variadic)); }, [](const std::unique_ptr& /*unused*/) -> std::string { throw wordgen_error(ec::node_bad_cvt, "", "", ""); }, [](const wordgen_error& e) -> std::string { throw e; }); } [[nodiscard]] static auto stringize(const arg_t& arg, const std::string& joiner) -> std::string { return kblib::visit2( arg, [](const std::string& str) { return str; }, [&joiner](const std::vector& vec) { return kblib::sum(vec.begin(), vec.end(), [&joiner](std::string l, const std::string& r) { return (l += joiner) += r; }); }, [](double x) -> std::string { return std::to_string(x); }, [](const node* n) { return kblib::concat(n->name, std::string(n->declared_argc, '|'), kblib::repeat("..."s, n->variadic)); }, [](const std::unique_ptr& /*unused*/) -> std::string { throw wordgen_error(ec::node_bad_cvt, "", "", ""); }, [](const wordgen_error& e) -> std::string { throw e; }); } [[nodiscard]] auto stringize(arg_t&& arg) -> std::string { return kblib::visit2( std::move(arg), [](std::string&& str) { return std::move(str); }, [](std::vector&& vec) { return std::accumulate(vec.begin(), vec.end(), std::string{}); }, [](double x) -> std::string { return std::to_string(x); }, [](const node* n) { return kblib::concat(n->name, std::string(n->declared_argc, '|'), kblib::repeat("..."s, n->variadic)); }, [](const std::unique_ptr& /*unused*/) -> std::string { throw wordgen_error(ec::node_bad_cvt, "", "", ""); }, [](const wordgen_error& e) -> std::string { throw e; }); } [[nodiscard]] static auto stringize(arg_t&& arg, const std::string& joiner) -> std::string { return kblib::visit2( std::move(arg), [](std::string&& str) { return std::move(str); }, [&joiner](std::vector&& vec) { return kblib::sum(vec.begin(), vec.end(), [&joiner](std::string l, const std::string& r) { return (l += joiner) += r; }); }, [](double x) -> std::string { return std::to_string(x); }, [](const node* n) { return kblib::concat(n->name, std::string(n->declared_argc, '|'), kblib::repeat("..."s, n->variadic)); }, [](const std::unique_ptr& /*unused*/) -> std::string { throw wordgen_error(ec::node_bad_cvt, "", "", ""); }, [](const wordgen_error& e) -> std::string { throw e; }); } [[nodiscard, maybe_unused]] static auto to_string(const arg_t& arg) -> arg_t { return kblib::visit2( arg, [](const std::string& str) -> arg_t { return str; }, [](const std::vector& vec) -> arg_t { return std::accumulate(vec.begin(), vec.end(), std::string{}); }, [](double x) -> arg_t { return std::to_string(x); }, [](const node* n) -> arg_t { return kblib::concat(n->name, std::string(n->declared_argc, '|'), kblib::repeat("..."s, n->variadic)); }, [](const std::unique_ptr& /*unused*/) -> arg_t { return wordgen_error(ec::node_bad_cvt, "", "", ""); }, [](const wordgen_error& e) -> arg_t { return e; }); } [[nodiscard, maybe_unused]] static auto to_string(arg_t&& arg) -> arg_t { return kblib::visit2( std::move(arg), [](std::string&& str) -> arg_t { return std::move(str); }, [](const std::vector& vec) -> arg_t { std::string out; out.reserve( std::accumulate(vec.begin(), vec.end(), std::size_t{}, [](std::size_t acc, const std::string& s) { return acc + s.length(); })); std::copy(vec.begin(), vec.end(), kblib::consumer([&](const std::string& s) { out += s; })); return out; }, [](double x) -> arg_t { return std::to_string(x); }, [](const node* n) -> arg_t { return kblib::concat(n->name, std::string(n->declared_argc, '|'), kblib::repeat("..."s, n->variadic)); }, [](const std::unique_ptr& /*unused*/) -> arg_t { return wordgen_error(ec::node_bad_cvt, "", "", ""); }, [](const wordgen_error& e) -> arg_t { return e; }); } [[nodiscard]] static auto to_num(arg_t arg) -> double { if (auto p = std::get_if(&arg)) { return *p; } return pFromStr(double, stringize(arg)); } // std::vector flatten(argslist args) // { // return std::accumulate( // args.begin(), args.end(), // std::vector{}, // [](std::vector vec, const arg_t& a) { // std::visit(kblib::visitor{ // [&](const std::string& s) {vec.push_back(s);}, // [&](const std::vector& v) { // vec.insert(vec.end(), v.begin(), v.end()); // }, // }, a); // return vec; // }); // } [[nodiscard]] static auto flatten_impl(argslist args) -> std::vector { return std::accumulate( args.begin(), args.end(), std::vector{}, [](std::vector vec, const arg_t& a) { kblib::visit2( a, [&](const std::string& s) { vec.push_back(s); }, [&](const std::vector& v) { vec.insert(vec.end(), v.begin(), v.end()); }, [&](double x) { vec.push_back(std::to_string(x)); }, [&](const node* n) { vec.push_back(stringize(n)); }, [](const std::unique_ptr& /*unused*/) { throw wordgen_error(ec::node_bad_cvt, "", "", ""); }, [](const wordgen_error& e) { throw e; }); return vec; }); } [[nodiscard]] static auto flatten(argslist args) -> arg_t { try { return flatten_impl(std::move(args)); } catch (const wordgen_error& e) { return e; } } [[nodiscard]] static auto listize(arg_t arg) -> std::vector { return kblib::visit2( std::move(arg), [](std::string&& s) -> std::vector { std::vector v; v.emplace_back(std::move(s)); return v; }, [](std::vector&& v) -> std::vector { return {std::move(v)}; }, [](double x) -> std::vector { return {std::to_string(x)}; }, [](const node* n) -> std::vector { return std::vector{stringize(n)}; }, [](const std::unique_ptr& /*unused*/) -> std::vector { throw wordgen_error(ec::node_bad_cvt, "", "", ""); }, [](const wordgen_error& e) -> std::vector { throw e; }); } [[nodiscard]] auto Echo::operator()(argslist v) const -> arg_t { try { std::string ret; std::copy(v.begin(), v.end(), kblib::consumer([&](arg_t& a) { ret += stringize(std::move(a), "|"); })); return ret; } catch (const wordgen_error& e) { return e; } } [[nodiscard]] auto Evaluator::operator()(argslist v) const -> arg_t { if (v.empty()) { return wordgen_error(ec::invoke_null, "", "", ""); } if (not std::holds_alternative(v.front())) { return wordgen_error(ec::invoke_type, "", "", ""); } try { // the first argument is the name of the function to invoke std::string fun = [&] { if (auto f = std::get_if(&v.front()); f) { return std::move(*f); } else { return stringize(v.front()); } }(); v.erase(v.begin()); return owner->lookup(fun)(std::move(v)); } catch (const wordgen_error& e) { return e; } } [[nodiscard]] static auto concat(argslist v) -> arg_t { try { return std::accumulate( v.begin(), v.end(), std::string{}, [](std::string s, const arg_t& arg) { return s += stringize(arg); }); } catch (const wordgen_error& e) { return e; } } [[nodiscard]] static auto repeat(argslist args) -> arg_t { if (args.size() % 2) { args.push_back("1"s); // handle odd lists by adding the last element once } std::string out; auto pos = args.begin(); auto end = args.end(); for (; pos != end; pos += 2) { if (not std::holds_alternative(*std::next(pos))) { return wordgen_error(ec::e_internal, "", "", ""); } try { out += kblib::repeat( stringize(*pos, "|"), std::stoi(std::get(*std::next(pos)))); } catch (const wordgen_error& e) { return e; } } return out; } [[nodiscard]] static auto len(argslist args) -> arg_t { if (args.size() > 1) { return wordgen_error(ec::too_many_args, "len", std::to_string(args.size()), ""); } else if (args.empty()) { return wordgen_error(ec::not_enough_args, "len", std::to_string(args.size()), ""); } return kblib::visit2( args[0], [](const std::string& /*unused*/) -> arg_t { return "1"s; }, [](const std::vector& vec) -> arg_t { return std::to_string(vec.size()); }, [](double /*unused*/) -> arg_t { return "1"s; }, [](const node* n) -> arg_t { return std::to_string(n->vals.size()); }, [](const std::unique_ptr& n) -> arg_t { return std::to_string(n->vals.size()); }, [](const wordgen_error& e) -> arg_t { return e; }); } template struct power { [[nodiscard]] auto operator()(const T& l, const T& r) -> T { return std::pow(l, r); } }; // left fold template [[nodiscard]] auto left_fold(argslist args) -> arg_t { try { auto flat = flatten_impl(std::move(args)); return kblib::toStr( std::accumulate(flat.begin(), flat.end(), static_cast(init), [](double L, const std::string& str) { return op{}(L, pFromStr(double, str)); })); } catch (const wordgen_error& e) { return e; } #if 0 return kblib::toStr(std::accumulate( args.begin(), args.end(), static_cast(init), [](double L, const arg_t& a) { return op{}(L, std::visit(kblib::visitor{ [](const std::string& str) { return pFromStr(double, str); }, [](const std::vector& vec) { return std::accumulate( vec.begin(), vec.end(), static_cast(init), [](double L, std::string str) { return op{}(L, pFromStr(double, str)); } ); }, }, a)); } )); #endif } template [[nodiscard]] auto left_fold(argslist args) -> arg_t { try { auto flat = flatten_impl(std::move(args)); return kblib::toStr(std::accumulate( std::next(flat.begin()), flat.end(), pFromStr(double, flat.front()), [](double L, const std::string& str) { return op{}(L, pFromStr(double, str)); })); } catch (const wordgen_error& e) { return e; } } [[nodiscard]] static auto select_(argslist args) -> arg_t { try { return std::move( args.at(1 + pFromStr(unsigned, stringize(std::move(args.at(0)))))); } catch (const wordgen_error& e) { return e; } catch (const std::out_of_range&) { return wordgen_error( ec::range_error, "?", kblib::concat("failed to index ", stringize(args.front())), kblib::concat("with ", args.size(), " args")); } } [[nodiscard]] static auto which(argslist args) -> arg_t { return std::to_string( kblib::find_in(args.begin() + 1, args.end(), args.at(0))); } [[nodiscard]] static auto normalizeNum(argslist args) -> arg_t { if (args.size() > 1) { return wordgen_error(ec::too_many_args, "num", std::to_string(args.size()), ""); } if (auto p = std::get_if(&args[0])) { return *p; } return kblib::toStr(pFromStr(double, stringize(args[0]))); } [[nodiscard]] static auto greater(argslist args) -> arg_t { if (args.size() > 4) { return wordgen_error(ec::too_many_args, "gt", std::to_string(args.size()), ""); } else if (args.size() < 4) { return wordgen_error(ec::not_enough_args, "gt", std::to_string(args.size()), ""); } if (pFromStr(unsigned, stringize(args[0])) > pFromStr(unsigned, stringize(args[1]))) { return std::move(args[2]); } else { return std::move(args[3]); } } [[nodiscard]] static auto lesser(argslist args) -> arg_t { if (args.size() > 4) { return wordgen_error(ec::too_many_args, "lt", std::to_string(args.size()), ""); } else if (args.size() < 4) { return wordgen_error(ec::not_enough_args, "lt", std::to_string(args.size()), ""); } if (pFromStr(unsigned, stringize(args[0])) < pFromStr(unsigned, stringize(args[1]))) { return std::move(args[2]); } else { return std::move(args[3]); } } [[nodiscard]] static auto equal(argslist args) -> arg_t { if (args.size() > 4) { return wordgen_error(ec::too_many_args, "eq", std::to_string(args.size()), ""); } else if (args.size() < 4) { return wordgen_error(ec::not_enough_args, "eq", std::to_string(args.size()), ""); } if (args[0] == args[1]) { return std::move(args[2]); } else { return std::move(args[3]); } } [[nodiscard]] static auto nequal(argslist args) -> arg_t { if (args.size() > 4) { return wordgen_error(ec::too_many_args, "ne", std::to_string(args.size()), ""); } else if (args.size() < 4) { return wordgen_error(ec::not_enough_args, "ne", std::to_string(args.size()), ""); } if (args[0] != args[1]) { return std::move(args[2]); } else { return std::move(args[3]); } } #if 0 static icu::UnicodeString math(const std::vector& p, int s, const ArgsType& a) { //Fill pvals with the substituted values of the parameter list (flattening ...) std::vector pvals; std::for_each( p.begin(), p.end(), [&a, &s, &pvals](const param& p) { if (p.isRest()) { std::transform( std::next(a.begin(), s), a.end(), std::back_inserter(pvals), [&a, &s](const icu::UnicodeString& s){return s;} ); } else { pvals.push_back(p.getVal(s, a)); } } ); const std::array ops = {u'+',u'-',u'*',u'/',u'.'}; std::stack opstack; std::stack valstack; bool pending = false; for (auto& p : pvals) { decltype(&ops[0]) op; if (p.countChar32() == 1 and (op = std::find(ops.begin(), ops.end(), p.char32At(0))) != ops.end()) { opstack.push(*op); } else { if (pending) { while(valstack.size()) { auto v1 = pop(valstack); auto op = pop(opstack); # define APPLYOP(OP) \ p = fromUTF8(toStr(pFromStr(double, v1) OP pFromStr(double, p))) if (op == '+') APPLYOP(+); else if (op == '-') APPLYOP(-); else if (op == '*') APPLYOP(*); else if (op == '/') APPLYOP(/); else if (op == '.') p=v1+p; } } valstack.push(p); pending = true; } } return pop(valstack); } #endif // save effort and accuracy on reconverting between double and string by using a // variant value type using math_type = std::variant; [[nodiscard]] static auto is_operator(char c) -> bool { static const std::string ops = "+-*/^."; return ops.find(c) != std::string::npos; } [[nodiscard]] static auto plus(const math_type& v1, const math_type& v2) -> math_type { return lexical_coerce(v1) + lexical_coerce(v2); } [[nodiscard]] static auto minus(const math_type& v1, const math_type& v2) -> math_type { return lexical_coerce(v1) - lexical_coerce(v2); } [[nodiscard]] static auto mul(const math_type& v1, const math_type& v2) -> math_type { return lexical_coerce(v1) * lexical_coerce(v2); } [[nodiscard]] static auto div(const math_type& v1, const math_type& v2) -> math_type { auto rhs = lexical_coerce(v2); if (rhs == 0) { throw wordgen_error(ec::m_divzero, "", "", ""); } return lexical_coerce(v1) / rhs; } [[nodiscard]] static auto pow(const math_type& v1, const math_type& v2) -> math_type { return std::pow(lexical_coerce(v1), lexical_coerce(v2)); } [[nodiscard]] static auto cat(const math_type& v1, const math_type& v2) -> math_type { return lexical_coerce(v1) + lexical_coerce(v2); } [[nodiscard]] static auto pn(argslist args) -> arg_t { auto check_pn = [](std::vector tokens) { int expected_vals = 1; for (const auto& tok : tokens) { if (expected_vals == 0) { throw wordgen_error(ec::bad_math, "validation failure\n", kblib::quoted(tok), ""); } if (tok.size() == 1 and is_operator(tok[0])) { ++expected_vals; } else { --expected_vals; } } if (expected_vals != 0) { throw wordgen_error(ec::bad_math, "validation failure", "", ""); } return tokens; }; vstack valstack; vstack opstack; bool pending = false; // auto print_state = [&](const math_type& curr, bool nested = false){ // using std::clog; // if (not nested) clog<<"---"; // clog<<"---\n" // <<"current token: "<(curr)<<'\n' // <<"pending: "<(c)<<", "; // })); // clog<<'\n'; // }; try { for (const std::string& a : check_pn(flatten_impl(std::move(args)))) { // print_state(a); if (a.size() == 1 and is_operator(a[0])) { opstack.push(a[0]); pending = false; } else { math_type v2 = a; if (pending) { while (not valstack.empty()) { // print_state(v1, true); if (opstack.empty()) { throw wordgen_error(ec::bad_math, "pn", "\nnot enough operators", ""); } auto v1 = pop(valstack); switch (pop(opstack)) { case '+': v2 = plus(v1, v2); break; case '-': v2 = minus(v1, v2); break; case '*': v2 = mul(v1, v2); break; case '/': v2 = div(v1, v2); break; case '^': v2 = pow(v1, v2); break; case '.': v2 = cat(v1, v2); } } } valstack.push(std::move(v2)); pending = true; } } if (not pending or not opstack.empty() or valstack.size() != 1) { return wordgen_error(ec::bad_math, "pn", "", ""); } return kblib::lexical_coerce(pop(valstack)); } catch (const wordgen_error& e) { return e; } } [[nodiscard]] static auto rpn(argslist args) -> arg_t { vstack valstack; try { for (const auto& a : flatten_impl(std::move(args))) { if (a.size() == 1 and is_operator(a[0])) { auto v2 = pop(valstack); auto v1 = pop(valstack); switch (a[0]) { case '+': valstack.push(plus(v1, v2)); break; case '-': valstack.push(minus(v1, v2)); break; case '*': valstack.push(mul(v1, v2)); break; case '/': valstack.push(div(v1, v2)); break; case '^': valstack.push(pow(v1, v2)); break; case '.': valstack.push(cat(v1, v2)); } } else { valstack.push(a); } } if (valstack.size() != 1) { return wordgen_error(ec::bad_math, "rpn", "", ""); } return kblib::lexical_coerce(pop(valstack)); } catch (const wordgen_error& e) { return e; } } [[nodiscard]] static auto truthy(const arg_t& arg) -> bool { auto str = stringize(arg); if (str.empty()) return false; try { if (pFromStr(double, str) == 0) return false; } catch (std::runtime_error&) { return true; } return true; } [[nodiscard]] static auto if_(argslist args) -> arg_t { if (args.size() > 3) { return wordgen_error(ec::too_many_args, "if", std::to_string(args.size()), ""); } else if (args.size() < 3) { return wordgen_error(ec::not_enough_args, "if", std::to_string(args.size()), ""); } return std::move(truthy(args[0]) ? args[1] : args[2]); } [[nodiscard]] static auto ifn(argslist args) -> arg_t { if (args.size() > 3) { return wordgen_error(ec::too_many_args, "ifn", std::to_string(args.size()), ""); } else if (args.size() < 3) { return wordgen_error(ec::not_enough_args, "ifn", std::to_string(args.size()), ""); } return std::move((not truthy(args[0])) ? args[1] : args[2]); } [[nodiscard]] static auto negate(argslist args) -> arg_t { if (args.size() > 1) { return wordgen_error(ec::too_many_args, "not", std::to_string(args.size()), ""); } else if (args.empty()) { return wordgen_error(ec::not_enough_args, "not", std::to_string(args.size()), ""); } return (not truthy(args[0])) ? "1" : "0"; } [[nodiscard, maybe_unused]] static auto subscript(argslist args) -> arg_t { if (args.size() < 2) { return wordgen_error(ec::not_enough_args, "[]", std::to_string(args.size()), ""); } auto list = listize(std::move(args.front())); auto remainder = flatten(argslist{std::make_move_iterator(args.begin() + 1), std::make_move_iterator(args.end())}); args.clear(); return kblib::visit(remainder)( [&](const std::string& str) -> arg_t { return list.at(pFromStr(unsigned, str)); }, [&](const std::vector& idxs) -> arg_t { std::vector retvals; retvals.reserve(idxs.size()); for (const auto& idx : idxs) { retvals.push_back(list.at(pFromStr(unsigned, idx))); } return retvals; }, [&](double x) -> arg_t { return list.at(static_cast(x)); }, [&](const node* /*unused*/) -> arg_t { return wordgen_error(ec::bad_arg_type, "[]", "", ""); }, [](wordgen_error& e) -> arg_t { return std::move(e); }); } [[nodiscard]] static auto replacer(argslist args) -> arg_t { if (not (args.size() % 2)) { return wordgen_error(ec::unpaired_args, "replace", std::to_string(args.size()), "Even number of arguments expected."); } auto str = stringize(args.front()); auto pos = std::next(args.begin()); auto end = args.end(); for (; pos != end; pos += 2) { str = srell::regex_replace( str, srell::regex{stringize(*pos), srell::regex_constants::dotall}, stringize(*std::next(pos))); } return str; } [[nodiscard, maybe_unused]] static auto polychannel_literal(argslist args) -> arg_t { // Replace literal nodes with an inline syntax, using template-strings. // ex: // These control characters will be interpreted by the noderef layer. if (not (args.size() % 2)) { return wordgen_error(ec::unpaired_args, "_literal", std::to_string(args.size()), "Even number of arguments expected."); } auto out = "\021!"s; auto pos = args.begin(); auto end = args.end(); for (; pos != end; pos += 2) { std::string ch = stringize(*pos); std::string contents = stringize(*std::next(pos)); out = kblib::concat(out, '\036', ch, '\037', contents); } out.push_back('\023'); return out; } [[nodiscard]] auto default_transformers(template_machine* owner, [[maybe_unused]] datafile* df, RandomGenerator& rng) -> tmap> { tmap> transformers; transformers.emplace("cat", make_transformer(concat)); transformers.emplace(".*", make_transformer(repeat)); transformers.emplace("flatten", make_transformer(flatten)); transformers.emplace("list", make_transformer(flatten)); transformers.emplace("len", make_transformer(len)); transformers.emplace("+", make_transformer(left_fold, 0>)); transformers.emplace("-", make_transformer(left_fold>)); transformers.emplace( "*", make_transformer(left_fold, 1>)); transformers.emplace("/", make_transformer(left_fold>)); transformers.emplace("^", make_transformer(left_fold>)); transformers.emplace("not", make_transformer(negate)); transformers.emplace("if", make_transformer(if_)); transformers.emplace("ifn", make_transformer(ifn)); transformers.emplace("?", make_transformer(select_)); transformers.emplace("which", make_transformer(which)); transformers.emplace("[]", make_transformer(subscript)); transformers.emplace("num", make_transformer(normalizeNum)); transformers.emplace("gt", make_transformer(greater)); transformers.emplace("lt", make_transformer(lesser)); transformers.emplace("=", make_transformer(equal)); transformers.emplace("eq", make_transformer(equal)); transformers.emplace("!=", make_transformer(nequal)); transformers.emplace("ne", make_transformer(nequal)); transformers.emplace("math", make_transformer(pn)); transformers.emplace("calc", make_transformer(pn)); transformers.emplace("pn", make_transformer(pn)); transformers.emplace("rpn", make_transformer(rpn)); transformers.emplace("replace", make_transformer(replacer)); transformers.emplace( "set", make_transformer([owner](argslist args) -> arg_t { if (args.size() == 2) { if (auto name = std::get_if(&args[0])) { owner->variables.insert_or_assign(*name, std::move(args[1])); return args[1]; } else { throw wordgen_error(ec::bad_arg_type, "expected name|value", "", ""); } } else if (args.size() < 2) { throw wordgen_error(ec::not_enough_args, "Expected name|value.", "", ""); } else { throw wordgen_error(ec::too_many_args, "Expected name|value.", "", ""); } })); transformers.emplace( "get", make_transformer([owner](argslist args) -> arg_t { if (args.size() == 1) { if (auto name = std::get_if(&args[0])) { if (auto it = owner->variables.find(*name); it != owner->variables.end()) { return it->second; } else { throw wordgen_error(ec::null_var, kblib::quoted(*name), "", ""); } } else { throw wordgen_error(ec::bad_arg_type, "Variable name must be string", "", ""); } } else if (args.empty()) { throw wordgen_error(ec::not_enough_args, "Expected name.", "", ""); } else { throw wordgen_error(ec::too_many_args, "Expected only name.", "", ""); } })); transformers.emplace( "rand", make_transformer([&rng](argslist argsin) -> arg_t { auto args = flatten_impl(std::move(argsin)); if (args.empty()) { throw wordgen_error( ec::bad_randspec, "No arguments received. Expected either max or min|max.", "", ""); } else if (args.size() == 1) { std::uniform_real_distribution dist(0.0, std::stod(args[0])); return std::to_string(dist(rng)); } else if (args.size() == 2) { std::uniform_real_distribution dist(std::stod(args[0]), std::stod(args[1])); return std::to_string(dist(rng)); } else { throw wordgen_error( ec::bad_randspec, "Too many arguments received. Expected either max or min|max.", "", ""); } })); transformers.emplace( "randnorm", make_transformer([&rng](argslist argsin) -> arg_t { auto args = flatten_impl(std::move(argsin)); if (args.size() == 2) { std::normal_distribution dist(std::stod(args[0]), std::stod(args[1])); return std::to_string(dist(rng)); } else { throw wordgen_error(ec::bad_randspec, "Expected avg|stddev.", "", ""); } })); transformers.emplace( "randint", make_transformer([&rng](argslist argsin) -> arg_t { auto args = flatten_impl(std::move(argsin)); if (args.empty()) { throw wordgen_error( ec::bad_randspec, "No arguments received. Expected either max or min|max.", "", ""); } else if (args.size() == 1) { return std::to_string(rng(std::stoi(args[0]))); } else if (args.size() == 2) { std::uniform_int_distribution dist(std::stoll(args[0]), std::stoll(args[1])); return std::to_string(dist(rng)); } else { throw wordgen_error( ec::bad_randspec, "Too many arguments received. Expected either max or min|max.", "", ""); } })); transformers.emplace( "randcat", make_transformer([&rng](argslist argsin) -> arg_t { auto args = flatten_impl(std::move(argsin)); auto freqs = kblib::build>( args.begin(), args.end(), [](const std::string& str) -> double { return std::stod(str); }); std::discrete_distribution dist(freqs.begin(), freqs.end()); return std::to_string(dist(rng)); })); transformers.emplace( "oneof", make_transformer([&rng](argslist args) -> arg_t { auto exparams = flatten(std::move(args)); return kblib::visit(exparams)( [&](std::vector& options) -> arg_t { if (options.empty()) { return wordgen_error(ec::not_enough_args, "oneof", std::to_string(options.size()), "(arguments have been flattened)"); } std::uniform_int_distribution dist(std::size_t{}, options.size() - 1); return std::move(options[dist(rng)]); }, [](wordgen_error& e) -> arg_t { return std::move(e); }, [](auto& x) -> arg_t { return std::move(x); }); })); transformers.emplace("invoke", std::make_unique(owner)); transformers.emplace( "branches", make_transformer([df](argslist args) -> arg_t { assert(not args.empty()); if (args.size() == 1) { return kblib::visit2( args.front(), [](const node* n) { return std::to_string(n->vals.size()); }, [&](const std::string& str) { const node* n = df->find_node(str); if (n) return std::to_string(n->vals.size()); else throw wordgen_error(ec::e_nonode, str, "", ""); }, [&](const std::vector& vec) -> std::string { if (vec.size() != 1) throw wordgen_error( ec::bad_arg_rank, stringize(vec), "", "'branches' expects either a node-name or a node."); const node* n = df->find_node(vec[0]); if (n) return std::to_string(n->vals.size()); else throw wordgen_error(ec::e_nonode, vec[0], "", ""); }, [](double x) -> std::string { throw wordgen_error( ec::bad_arg_type, std::to_string(x), "", "'branches' expects either a node-name or a node."); }, [](const wordgen_error& e) -> std::string { throw e; }); } else { throw wordgen_error( ec::bad_arg_rank, stringize(flatten(args)), "", "'branches' expects either a node-name or a node."); } })); #if 0 transformers.emplace("node", make_transformer([df](argslist args) -> arg_t { assert(args.size() == 1); assert(std::holds_alternative(args[0])); auto name = std::get(args[0]); const node* n = df->find_node(name); if (n) return n; else return wordgen_error(ec::e_nonode, name, "", ""); })); transformers.emplace("freqs", make_transformer([df, owner](argslist args) -> arg_t { assert(args.size() > 0); if (args.size() == 1) { auto node_to_freqs = [&](const node* n) -> std::vector { return kblib::build>( n->freqs.begin(), n->freqs.end(), [&](const std::variant& v) { return kblib::visit2(v, [](double d) {return std::to_string(d);}, [&](const bytecodes& b) {return owner->eval(b);} ); } ); }; return kblib::visit2(args.front(), node_to_freqs, [&](const std::string& str) -> std::vector { const node* n = df->find_node(str); if (n) return node_to_freqs(n); else throw wordgen_error(ec::e_nonode, str, "", ""); }, [](const std::vector& vec) -> std::vector { if (vec.size() != 1) throw wordgen_error(ec::bad_arg_rank, stringize(vec), "", "'freqs' expects either a node-name or a node."); const node* n = df->find_node(vec[0]); if (n) return node_to_freqs(n); else throw wordgen_error(ec::e_nonode, str, "", ""); }, [](const wordgen_error& e) -> std::vector {throw e;} ); } else { throw wordgen_error(ec::bad_arg_rank, stringize(vec), "", "'freqs' expects either a node-name or a node."); } })); transformers.emplace("makenode", make_transformer([df, owner](argslist args) -> arg_t { })); transformers.emplace("_literal", make_transformer(polychannel_literal)); #endif return transformers; } template [[nodiscard]] static auto basic_transformer(Callable&& c) -> t_func::func_t { return [c = std::forward(c)]( const template_machine& tm, const ArgsType& args, gsl::span> t_args) -> arg_t { argslist n_args; std::transform(t_args.begin(), t_args.end(), std::back_inserter(n_args), [&](const auto& n) { return eval(tm, *n, args); }); return c(std::move(n_args)); }; } template [[nodiscard]] static auto basic_transformer() -> t_func::func_t { return [](const template_machine& tm, const ArgsType& args, gsl::span> t_args) -> arg_t { argslist n_args; std::transform(t_args.begin(), t_args.end(), std::back_inserter(n_args), [&](const auto& n) { return eval(tm, *n, args); }); return c(std::move(n_args)); }; } [[nodiscard]] static auto t_if(const template_machine& tm, const ArgsType& n_args, gsl::span> args) -> arg_t { if (args.size() > 3) { return wordgen_error(ec::too_many_args, "if", std::to_string(n_args.size()), ""); } else if (args.size() < 3) { return wordgen_error(ec::not_enough_args, "if", std::to_string(n_args.size()), ""); } return eval(tm, truthy(eval(tm, *args[0], n_args)) ? *args[1] : *args[2], n_args); } [[nodiscard]] static auto t_ifn(const template_machine& tm, const ArgsType& n_args, gsl::span> args) -> arg_t { if (args.size() > 3) { return wordgen_error(ec::too_many_args, "if", std::to_string(n_args.size()), ""); } else if (args.size() < 3) { return wordgen_error(ec::not_enough_args, "if", std::to_string(n_args.size()), ""); } return eval(tm, not truthy(eval(tm, *args[0], n_args)) ? *args[1] : *args[2], n_args); } [[nodiscard]] static auto t_greater( const template_machine& tm, const ArgsType& n_args, gsl::span> args) -> arg_t { if (args.size() > 4) { return wordgen_error(ec::too_many_args, "gt", std::to_string(args.size()), ""); } else if (args.size() < 4) { return wordgen_error(ec::not_enough_args, "gt", std::to_string(args.size()), ""); } if (to_num(eval(tm, *args[0], n_args)) > to_num(eval(tm, *args[1], n_args))) { return eval(tm, *args[2], n_args); } else { return eval(tm, *args[3], n_args); } } [[nodiscard]] static auto t_lesser(const template_machine& tm, const ArgsType& n_args, gsl::span> args) -> arg_t { if (args.size() > 4) { return wordgen_error(ec::too_many_args, "lt", std::to_string(args.size()), ""); } else if (args.size() < 4) { return wordgen_error(ec::not_enough_args, "lt", std::to_string(args.size()), ""); } if (to_num(eval(tm, *args[0], n_args)) < to_num(eval(tm, *args[1], n_args))) { return eval(tm, *args[2], n_args); } else { return eval(tm, *args[3], n_args); } } [[nodiscard]] static auto t_equal(const template_machine& tm, const ArgsType& n_args, gsl::span> args) -> arg_t { if (args.size() > 4) { return wordgen_error(ec::too_many_args, "eq", std::to_string(args.size()), ""); } else if (args.size() < 4) { return wordgen_error(ec::not_enough_args, "eq", std::to_string(args.size()), ""); } if (to_num(eval(tm, *args[0], n_args)) == to_num(eval(tm, *args[1], n_args))) { return eval(tm, *args[2], n_args); } else { return eval(tm, *args[3], n_args); } } [[nodiscard]] static auto t_nequal(const template_machine& tm, const ArgsType& n_args, gsl::span> args) -> arg_t { if (args.size() > 4) { return wordgen_error(ec::too_many_args, "ne", std::to_string(args.size()), ""); } else if (args.size() < 4) { return wordgen_error(ec::not_enough_args, "ne", std::to_string(args.size()), ""); } if (to_num(eval(tm, *args[0], n_args)) != to_num(eval(tm, *args[1], n_args))) { return eval(tm, *args[2], n_args); } else { return eval(tm, *args[3], n_args); } } [[nodiscard]] auto default_new_transformers(template_machine* owner, [[maybe_unused]] datafile* df, RandomGenerator& rng) -> tmap { tmap transformers; auto emplace = [&](auto& name, auto&& f) { transformers.emplace(name, t_func{name, std::forward(f)}); }; emplace("", basic_transformer(Evaluator(owner))); emplace(".", basic_transformer(Evaluator(owner))); emplace("cat", basic_transformer()); emplace(".*", basic_transformer()); emplace("flatten", basic_transformer()); emplace("list", basic_transformer()); emplace("len", basic_transformer()); emplace("+", basic_transformer(left_fold, 0>)); emplace("-", basic_transformer(left_fold>)); emplace("*", basic_transformer(left_fold, 1>)); emplace("/", basic_transformer(left_fold, 1>)); emplace("^", basic_transformer(left_fold>)); emplace("not", basic_transformer()); emplace("if", t_if); emplace("ifn", t_ifn); emplace("?", basic_transformer()); emplace("which", basic_transformer()); emplace("[]", basic_transformer()); emplace("num", basic_transformer()); emplace("gt", t_greater); emplace("lt", t_lesser); emplace("=", t_equal); emplace("eq", t_equal); emplace("!=", t_nequal); emplace("ne", t_nequal); emplace("math", basic_transformer()); emplace("calc", basic_transformer()); emplace("pn", basic_transformer()); emplace("rpn", basic_transformer()); emplace("replace", basic_transformer()); emplace("set", basic_transformer([owner](argslist args) -> arg_t { if (args.size() == 2) { if (auto name = std::get_if(&args[0])) { owner->variables.insert_or_assign(*name, std::move(args[1])); return args[1]; } else { throw wordgen_error(ec::bad_arg_type, "expected name|value", "", ""); } } else if (args.size() < 2) { throw wordgen_error(ec::not_enough_args, "Expected name|value.", "", ""); } else { throw wordgen_error(ec::too_many_args, "Expected name|value.", "", ""); } })); emplace("get", basic_transformer([owner](argslist args) -> arg_t { if (args.size() == 1) { if (auto name = std::get_if(&args[0])) { if (auto it = owner->variables.find(*name); it != owner->variables.end()) { return it->second; } else { throw wordgen_error(ec::null_var, kblib::quoted(*name), "", ""); } } else { throw wordgen_error(ec::bad_arg_type, "Variable name must be string", "", ""); } } else if (args.empty()) { throw wordgen_error(ec::not_enough_args, "Expected name.", "", ""); } else { throw wordgen_error(ec::too_many_args, "Expected only name.", "", ""); } })); emplace( "rand", basic_transformer([&rng](argslist argsin) -> arg_t { auto args = flatten_impl(std::move(argsin)); if (args.empty()) { throw wordgen_error( ec::bad_randspec, "No arguments received. Expected either max or min|max.", "", ""); } else if (args.size() == 1) { std::uniform_real_distribution dist(0.0, std::stod(args[0])); return std::to_string(dist(rng)); } else if (args.size() == 2) { std::uniform_real_distribution dist(std::stod(args[0]), std::stod(args[1])); return std::to_string(dist(rng)); } else { throw wordgen_error( ec::bad_randspec, "Too many arguments received. Expected either max or min|max.", "", ""); } })); emplace("randnorm", basic_transformer([&rng](argslist argsin) -> arg_t { auto args = flatten_impl(std::move(argsin)); if (args.size() == 2) { std::normal_distribution dist(std::stod(args[0]), std::stod(args[1])); return std::to_string(dist(rng)); } else { throw wordgen_error(ec::bad_randspec, "Expected avg|stddev.", "", ""); } })); emplace( "randint", basic_transformer([&rng](argslist argsin) -> arg_t { auto args = flatten_impl(std::move(argsin)); if (args.empty()) { throw wordgen_error( ec::bad_randspec, "No arguments received. Expected either max or min|max.", "", ""); } else if (args.size() == 1) { return std::to_string(rng(std::stoi(args[0]))); } else if (args.size() == 2) { std::uniform_int_distribution dist(std::stoll(args[0]), std::stoll(args[1])); return std::to_string(dist(rng)); } else { throw wordgen_error( ec::bad_randspec, "Too many arguments received. Expected either max or min|max.", "", ""); } })); emplace( "randcat", basic_transformer([&rng](argslist argsin) -> arg_t { auto args = flatten_impl(std::move(argsin)); auto freqs = kblib::build>( args.begin(), args.end(), [](const std::string& str) -> double { return std::stod(str); }); std::discrete_distribution dist(freqs.begin(), freqs.end()); return std::to_string(dist(rng)); })); emplace("oneof", basic_transformer([&rng](argslist args) -> arg_t { auto exparams = flatten(std::move(args)); return kblib::visit(exparams)( [&](std::vector& options) -> arg_t { if (options.empty()) { return wordgen_error(ec::not_enough_args, "oneof", std::to_string(options.size()), "(arguments have been flattened)"); } std::uniform_int_distribution dist(std::size_t{}, options.size() - 1); return std::move(options[dist(rng)]); }, [](wordgen_error& e) -> arg_t { return std::move(e); }, [](auto& x) -> arg_t { return std::move(x); }); })); // emplace("invoke", std::make_unique(owner)); emplace( "branches", basic_transformer([df](argslist args) -> arg_t { assert(not args.empty()); if (args.size() == 1) { return kblib::visit2( args.front(), [](const node* n) { return std::to_string(n->vals.size()); }, [&](const std::string& str) { const node* n = df->find_node(str); if (n) return std::to_string(n->vals.size()); else throw wordgen_error(ec::e_nonode, str, "", ""); }, [&](const std::vector& vec) -> std::string { if (vec.size() != 1) throw wordgen_error( ec::bad_arg_rank, stringize(vec), "", "'branches' expects either a node-name or a node."); const node* n = df->find_node(vec[0]); if (n) return std::to_string(n->vals.size()); else throw wordgen_error(ec::e_nonode, vec[0], "", ""); }, [](double x) -> std::string { throw wordgen_error( ec::bad_arg_type, std::to_string(x), "", "'branches' expects either a node-name or a node."); }, [](const wordgen_error& e) -> std::string { throw e; }); } else { throw wordgen_error( ec::bad_arg_rank, stringize(flatten(args)), "", "'branches' expects either a node-name or a node."); } })); return transformers; } [[nodiscard]] auto default_names() -> std::vector> { return {{"lb", -15}, {"rb", -14}, {"b", -13}, {"p", -12}, {"lt", -11}, {"gt", -10}, {"d", -9}, {"D", -8}, {"e", -7}, {"E", -6}, {"c", -5}, {"C", -4}, {"a", -3}, {"A", -2}, {"...", -1}}; } [[nodiscard]] auto template_machine::min_argument() const -> int { assert(not arg_names.empty()); return std::min_element(arg_names.begin(), arg_names.end(), [](auto& l, auto& r) { return l.second < r.second; }) ->second; } auto copy(const ArgsType& special, const std::vector& vals) -> ArgsType { ArgsType ret{origin_tag{special.low()}}; for (auto i = special.low(); i < 0; ++i) { ret.push_back(special[i]); } std::copy(vals.begin(), vals.end(), std::back_inserter(ret)); return ret; } template auto set_sieve(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt1 out1, OutputIt2 out2) -> void { while (first1 != last1) { if (first2 == last2) { std::copy(first1, last1, out1); return; } if (*first1 < *first2) { *out1++ = *first1++; } else { if (not (*first2 < *first1)) { *out2++ = *first1++; } ++first2; } } } template auto set_copy_and_difference(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt1 out1, OutputIt2 out2, Compare comp) -> void { while (first1 != last1) { if (first2 == last2) { std::copy(first1, last1, out1); } if (comp(*first2, *first1)) { *out2 = *first2; ++first2; ++out2; } else { *out1 = *first1; ++out1; if (not comp(*first1, *first2)) ++first2; ++first1; } } std::copy(first2, last2, out2); } auto ReplaceStage::operator()(const Word& word) const -> Word { Word ret = word; for (auto& [ch, act] : actions) { auto& chname = datafile::ch_db()[ch]; log_debug("replace channel ", chname, ":\n\t", kblib::quoted(kblib::get_or(word.data, ch, ""))); for (auto& step : act) { ret.data = step(ret, ch); log_debug(" step: ", kblib::quoted(kblib::get_or(ret.data, ch, ""))); // ret.data.insert_or_assign(ch, step(ret, kblib::get_or(ret.data, ch, // std::string{}))); } // log_debug(" final: ", kblib::quoted(kblib::get_or(word.data, ch, // ""))); } // set_copy_and_difference( // actions.begin(), actions.end(), // word.data.begin(), word.data.end(), // kblib::consumer([&](const std::pair& f) { // channelID ch = f.first; // for (const auto& step : f.second) { // ret.data[ch] = step(word, kblib::get_or(word.data, ch, std::string{})); // } // }), // kblib::consumer([&](const std::pair& s) { // ret.data.insert(s); // }), // [](const auto& f, const auto& s) {return f.first < s.first;} // ); // std::set_intersection( // actions.begin(), actions.end(), // word.data.begin(), word.data.end(), // kblib::consumer([&](const auto& p) { // channelID ch = p.first; // for (const auto& step : actions.at(ch)) { // std::clog< Word { // The 'freq' channel is special during generation, but not during // formatting, so we stringize it here. word.data.insert_or_assign("freq"_ch, std::to_string(word.freq)); for (const auto& stage : replace) { // std::clog<