#include #include #include #include "show.h" #include "log.h" #include "katom.h" #include "argtype_set.h" #include "argument_set.h" #include "util.h" bool operator==(Parameter_set lhs, Parameter_set rhs) { return as_string(lhs.m_katoms.begin(), lhs.m_katoms.end(), true) == as_string(rhs.m_katoms.begin(), rhs.m_katoms.end(), true); } std::regex parameter_regex(bool optional=false) { //std::string pattern = R"(([A-Za-z]\w*)(?:(\.\w*)(?:(\.\w*)))?)"; std::string pattern = R"((?:([A-Za-z]\w*))|(?:([A-Za-z]\w*)\.(\w+))|(?:([A-Za-z]\w*)\.(\w+)\.(\w+)))"; if (optional) { pattern = R"((?::([A-Za-z]\w*))|(?::([A-Za-z]\w*)\.(\w+))|(?::([A-Za-z]\w*)\.(\w*)\.(\w+)))"; // x x } // (void)K::log(3, pattern); return std::regex(pattern); } Parameter_set::Parameter_set(const std::string parameter_string) { (void)K::log(3); Argtype_set argtypes {}; parse_parameters( katomize(line_split(parameter_string), Locator().str()), argtypes); } Parameter_set::Parameter_set(const std::vector& katoms) : m_katoms(katoms) { (void)K::log(3); Argtype_set argtypes {}; parse_parameters(m_katoms, argtypes); } Parameter_set::Parameter_set(const std::vector& katoms, const Argtype_set& argtypes) : m_katoms(katoms) { (void)K::log(3); parse_parameters(m_katoms, argtypes); } // Parameter parsing Parameter parse_positional_parameter(const katom_list& katoms, const Argtype_set& argtypes) { // (void)K::log(3, katoms); if (katoms.size() > 1) { throw Argument_error("Multiple katoms for positional argument: " + as_string(katoms.begin(), katoms.end(), true) + "\nPositional arguments are separated by the bar (|) character.", katoms[0].m_loc, false); } Katom k = katoms[0]; std::string name = k.m_text; std::smatch match {}; if (!std::regex_match(name, match, parameter_regex())) { throw Argument_error( "The structure of the word \"" + name + "\" is not correct for a positional parameter", k.m_loc); } else { std::string match_name = std::string(match[1]) + std::string(match[2]) + std::string(match[4]); std::string match_type = std::string(match[3]) + std::string(match[5]); std::string match_target = match[6]; if (match_type.empty()) { match_type = "string"; } return Parameter(match_name, argtypes.get(match_type, k.m_loc), k.m_loc); } } Parameter parse_optional_parameter(const katom_list& katoms, const Argtype_set& argtypes) { //(void)K::log(3); Katom k = katoms[0]; std::string default_value {}; if (katoms.size() > 1) { default_value = to_string(katoms.cbegin() + 1, katoms.cend(), true); } std::string name = k.m_text; std::smatch match {}; if (!std::regex_match(name, match, parameter_regex(true))) { throw Argument_error( "The structure of the word \"" + name + "\" is not correct for an optional parameter", k.m_loc); } else { std::string match_name = std::string(match[1]) + std::string(match[2]) + std::string(match[4]); std::string match_type = std::string(match[3]) + std::string(match[5]); std::string match_target = match[6]; if (match_type.empty()) { match_type = "string"; } return Parameter(match_name, argtypes.get(match_type, k.m_loc), k.m_loc, true, default_value); } } void check_for_missing_parameter(const katom_list& katoms) { (void)K::log(3, katoms.size()); // Yeah, yeah, "algorithms." auto ki = katoms.begin(); while (ki < katoms.end() - 1) { ki = std::find_if(ki, katoms.end(), [](const Katom& k) { return k.m_type == katom_t::bar; }); if (ki == katoms.end()) { break; } auto kstart = ki; ki = std::find_if(ki + 1, katoms.end(), [](const Katom& k) { return !k.is_whitespace(); }); if (ki == katoms.end()) { throw Argument_error( "A parameter list ends with a bar character", kstart->m_loc); } auto type_after_bar = ki->m_type; if (type_after_bar == katom_t::bar) { throw Argument_error( "A parameter name was missing between two bar characters", kstart->m_loc); } else if (type_after_bar == katom_t::option_name) { throw Argument_error( "A parameter name was missing between a bar character and an option name", kstart->m_loc); } ++ki; } } bool is_boundary(katom_list::const_iterator ki) { return ki->m_type == katom_t::option_name || ki->m_type == katom_t::bar; } std::vector> function_symbol_parts(katom_list::const_iterator kbegin, katom_list::const_iterator kend) { std::vector> parts; katom_list part {}; auto ki = kbegin; while (ki != kend && ki->is_whitespace()) { ki++; } if (ki == kend) { return {}; } if (ki->m_type != katom_t::option_name) { part.push_back(Katom("|", katom_t::bar, kbegin->m_loc)); } while (ki < kend) { if (!part.empty() && is_boundary(ki)) { parts.push_back(trim(part)); part = {}; } part.push_back(*ki); ki++; } if (!part.empty()) { parts.push_back(trim(part)); } // std::cout << "PARTS:\n"; // for (size_t i = 0; i < parts.size(); i++) { // std::cout << i << sp_arrow << parts[i] << "\n"; // } return parts; } katom_list trim_part(katom_list part) { return trim(part, {katom_t::space, katom_t::newline, katom_t::bar}); } std::tuple parameter_split(katom_list::const_iterator kbegin, katom_list::const_iterator kend) { (void)K::log(3); //, "begin:", *kbegin, "end:", *(kend - 1)); // "distance:", std::distance(kbegin, kend)); auto parts = function_symbol_parts(kbegin, kend); // std::cout << "parts: " << parts << "\n"; katom_lists positional {}; katom_lists optional {}; for (auto p : parts) { if (p[0].m_type == katom_t::option_name) { optional.push_back(p); } else { positional.push_back(trim_part(p)); } } return {positional, optional}; } void Parameter_set::parse_parameters(const katom_list& katoms, const Argtype_set& argtypes) { (void)K::log(3, trim(katoms)); if (katoms.empty()) { return; } check_for_missing_parameter(katoms); auto [positional, optional] = parameter_split(katoms.cbegin(), katoms.cend()); for (auto req : positional) { auto pos = parse_positional_parameter(req, argtypes); if (pos.m_argtype.m_name == "rest") { m_rest.push_back(pos); } else { m_positional.push_back(pos); } } for (auto opt : optional) { auto param = parse_optional_parameter(opt, argtypes); if (std::ranges::count(m_optional_names, param.m_name) > 0) { throw Argument_error( "Optional parameter \":" + param.m_name + "\" already defined", katoms[0].m_loc); } m_optional.push_back(param); m_optional_names.push_back(param.m_name); } if (!m_rest.empty()) { m_positional_count = m_positional.size(); } } void describe_arguments( std::string label, std::vector> positional, std::vector> optional, std::vector rest) { std::cout << label << ":\n" << " positional: " << positional << "\n" << " optional: " << optional << "\n" << " rest: " << rest << "\n"; } void Parameter_set::describe_parameters() { (void)K::log(3); std::cout << " positional: "; if (!m_positional.empty()) { for (auto p : m_positional) { std::cout << p << " "; } } else { std::cout << "[none]"; } std::cout << "\n optional: "; if (!m_optional.empty()) { for (auto p : m_optional) { std::cout << p << " "; } } else { std::cout << "[none]"; } std::cout << "\n rest: "; if (!m_rest.empty()) { std::cout << kall << m_rest << kreset << "\n"; } else { std::cout << "[none]"; } std::cout << "\n"; } std::tuple argument_split(katom_list::const_iterator kbegin, katom_list::const_iterator kend, long unsigned int positional_limit) { // (void)K::log(3, "begin:", *(kbegin+1), "end:", *(kend - 1), // "distance:", std::distance(kbegin, kend), "limit:", positional_limit); (void)K::log(3); // msg() << std::pair(kbegin, kend) << "\n"; auto parts = function_symbol_parts(kbegin, kend); katom_lists positional {}; katom_lists optional {}; katom_list rest {}; for (auto p : parts) { if (p[0].m_type == katom_t::option_name) { optional.push_back(p); } else if (positional.size() < positional_limit) { positional.push_back(trim_part(p)); } else { rest.insert(rest.end(), p.begin(), p.end()); } } return {positional, optional, trim_part(rest)}; } // Parameter/argument mapping void Parameter_set::check_positional(const katom_lists& positional_arguments, const Locator& loc) { (void)K::log(3, "required:", m_positional.size(), positional_arguments.size()); //, positional_arguments); auto positional_count = m_positional.size(); auto given_count = positional_arguments.size(); if (positional_count > given_count) { // std::cout << "Given less than required\n"; std::vector missing(m_positional.begin() + given_count, m_positional.end()); //std::cout << "missing: " << missing << "\n"; auto missing_count = missing.size(); std::stringstream ss {}; ss << "Positional " << plural("argument", missing_count) << " " << to_be(missing_count) << " missing:\n"; std::cout << ss.str(); for (auto arg : missing) { ss << " " << arg.m_name << "\n"; } //std::cout << ss.str(); throw Argument_error(ss.str(), loc, false); } else if (positional_count < given_count) { //std::cout << "DESCRIBE\n"; //describe_parameters(); std::stringstream ss {}; ss << "Too many positional arguments were given; " << positional_count << " needed but " << given_count << " given"; throw Argument_error(ss.str(), loc); } } std::map Parameter_set::check_optional(const katom_lists& optional_arguments, const Locator& loc) { (void)K::log(3, optional_arguments.size()); std::vector optional_names_used {}; std::map values {}; for (const auto& opt : optional_arguments) { std::string name(opt[0].m_text, 1); if (std::ranges::count(m_optional_names, name) == 0) { throw Argument_error("Optional argument \":" + name + "\" not defined", loc); } if (std::ranges::count(optional_names_used, name) > 0) { throw Argument_error("Optional argument \":" + name + "\" already provided " + "with a value of:\n" + values[name], loc, false); } katom_list value_katoms(opt.begin()+1, opt.end()); std::string value = trim(to_string(value_katoms)); values[name] = value; optional_names_used.push_back(name); } return values; } const std::map Parameter_set::value_map( const katom_lists& positional, const katom_lists& optional, const katom_list& rest, const Locator& loc) { (void)K::log(3, "positional:", positional.size(), "optional:", optional.size(), "rest:", rest.size()); std::map values {}; check_positional(positional, loc); for (size_t i = 0; i < m_positional.size(); ++i) { auto param = m_positional[i]; std::string arg = as_string(positional[i].begin(), positional[i].end(), true); values[param.m_name] = arg; } auto optional_values = check_optional(optional, loc); for (auto [key, value] : optional_values) { values[key] = value; } for (auto opt : m_optional) { values.try_emplace(opt.m_name, opt.m_default); } if (active(rest)) { if (!m_rest.empty()) { values[m_rest[0].m_name] = as_string(rest.begin(), rest.end(), true); } else { std::stringstream ss {}; ss << "More positional arguments were given (" << positional.size() + rest.size() << ") than defined (" << m_positional.size() << ")"; throw Argument_error(ss.str(), loc); } } return values; } // Parameter/argument substitution std::string replace_arguments( const std::map& values, const std::string& parameterized_text, const Locator& loc) { (void)K::log(3); std::string result = parameterized_text; for (auto [name, value] : values) { result = string_replace(result, '*' + name + '*', value); } auto matches = find_all(result, std::regex(R"((\*.*?\*))")); std::vector unmatched; unmatched.reserve(matches.size()); std::copy(matches.begin(), matches.end(), std::back_inserter(unmatched)); auto unmatched_count = unmatched.size(); if (unmatched_count > 0) { std::stringstream ss {}; ss << "Undefined " << plural("argument", unmatched_count) << " in klammer:\n"; for (const auto& arg : unmatched) { ss << " " << arg << "\n"; } ss << "To prevent the \"*\" character from specifying an argument, " << "precede it with the \"^\" character."; throw Argument_error(ss.str(), loc, false); } return result; }