#include #include #include #include #include "argtype_set.h" #include "error.h" #include "show.h" #include "log.h" #include "util.h" #include "character.h" #include "katom.h" Parameter_set& Argtype_set::parameters() { static Parameter_set instance("name | desc :pattern .* :python_cast str :default"); return instance; } Argtype_set::Argtype_set() { (void)(void)K::log(2); Locator loc = current_locator(); for (auto [name, desc, pattern, python_cast, python_format] : base_argtypes) { add(name, desc, pattern, "", python_cast, python_format, loc); } // rest is a parameterized type (rest(N)); an unparameterized use is // one-dimensional. m_types["rest"].m_parameter = "1"; } std::string Argtype_set::replace_symbols(const std::string& pattern, const Locator& loc) { std::smatch match {}; std::regex symbol_pat(R"('(\w+)')"); std::string expanded { pattern }; for (const std::string& symbol : find_all(pattern, symbol_pat, 0)) { std::string name { symbol.begin()+1, symbol.end()-1 }; if (m_types.find(name) != m_types.end()) { expanded = string_replace( expanded, symbol, R"((?:)" + m_types[name].m_pattern + R"())"); } else { std::stringstream ss; ss << "Argtype symbol " << symbol << " not defined.\n\n" << "Defined argtypes:\n"; ss << describe(); throw Definition_error(ss.str(), loc, false); } } return expanded; } void Argtype_set::check_for_existing_definition( const std::string& name, const Locator& loc) { if (count(m_names.begin(), m_names.end(), name) > 0) { std::stringstream ss {}; ss << "Argument type '" << name << "' is already defined at " << m_types[name].m_loc; throw Definition_error(ss.str(), loc); } } void Argtype_set::add(const std::string& name, const std::string& desc, const std::string& pattern, const std::string& default_value, const std::string& python_cast, modify_string_f python_format, const Locator& loc) { // (void)K::log(3, name, ":", abbrev(string_replace(desc, "\n", "/"), 50), pattern); check_for_existing_definition(name, loc); std::string expanded_pattern = replace_symbols(pattern, loc); m_name_size = std::max(m_name_size, name.size()); // For display m_pattern_size = std::max(m_pattern_size, expanded_pattern.size()); try { m_types[name] = Argtype(name, desc, pattern, expanded_pattern, default_value, python_cast, python_format, loc); } catch (const std::regex_error& e) { std::stringstream ss {}; ss << "The pattern for argument type \"" << name << "\" is not a valid regular expression (" << e.what() << "):\n" << " " << pattern << "\n"; if (pattern != expanded_pattern) { ss << "expanded to:\n " << expanded_pattern << "\n"; } throw Definition_error(ss.str(), loc, false); } if (!default_value.empty() && !std::regex_match(default_value, m_types[name].m_regex)) { std::stringstream ss {}; ss << "The default value \"" << default_value << "\" for argument type \"" << name << "\" does not match its own pattern:\n" << " " << pattern << "\n"; throw Definition_error(ss.str(), loc, false); } m_names.push_back(name); } void Argtype_set::add(std::vector::iterator begin, std::vector::iterator end, std::vector& katoms) { (void)K::log(3); auto [positional, optional, rest] = argument_split(begin + 1, end - 1); //, Argtype_set::parameters.m_positional.size()); check_for_existing_definition(positional[0][0].m_text, begin->m_loc); auto values = Argtype_set::parameters().value_map(positional, optional, rest, begin->m_loc); // std::for_each(begin, end+1, [](Katom& k) { k.m_type = katom_t::replaced; }); add(values["name"], values["desc"], values["pattern"], values["default"], values["python_cast"], modify_string_f{}, begin->m_loc); modify_type(katom_t::replaced, begin, end); auto next_iter = end; ignore_whitespace(next_iter, katoms); } Argtype Argtype_set::get(const std::string& name, const Locator& loc) const { if (is_not_in(name, m_names)) { std::stringstream msg {}; msg << "Type '" << name << "' is not an argument type"; throw Argument_error(msg.str(), loc); } return m_types.at(name); } std::string Argtype_set::eval( const std::string& value, const std::string& type_name, const Locator& loc) { std::regex re = m_types[type_name].m_regex; std::smatch match {}; if (std::regex_match(value, match, re)) { return value; } else { //return "NO MATCH"; std::stringstream ss {}; ss << "Argument \"" << value << "\" does not match the pattern for \"" << type_name << "\"\n"; throw Definition_error(ss.str(), loc); } } std::string Argtype_set::describe(bool long_form, int indent_width) const { std::string indent(' ', indent_width); std::size_t name_width = std::accumulate( m_names.begin(), m_names.end(), 0, [&] (size_t w, const std::string& name) { return std::max(w, name.size()); }); std::stringstream result {}; for (const auto& name : m_names) { std::string label { "Regex:" }; int pat_width = name_width + 2 + indent_width + label.size(); result << indent << std::setw(name_width) << name << sp_arrow; if (long_form) { result << m_types.at(name).m_desc << "\n"; result << std::setw(pat_width) << "regex: " << m_types.at(name).m_symbolic_pattern; if (m_types.at(name).m_symbolic_pattern != m_types.at(name).m_pattern) result << sp_arrow << m_types.at(name).m_pattern; result << "\n"; } else { // result << abbrev(m_types.at(name).m_desc) << "\n"; result << regex_split(m_types.at(name).m_desc, std::regex("\\n"), true)[0] << "\n"; } } if (long_form) { result << "\nA previously defined type can be included in the definition of a new type\n" << "by surrounding the name of the existing type in single quotation marks.\n"; } return result.str(); }