#include #include "klammer.h" #include "show.h" #include "log.h" #include "argument_set.h" #include "eval.h" #include "util.h" #include "character.h" using namespace std::literals; std::regex Klammer::name_re = std::regex(R"((\w+)(?:\.(\w+))?)"); std::tuple parse_name(Target_set targets, Katom name_katom) { std::string name_with_target = trim_char(name_katom.m_text, '@'); std::smatch match {}; if (!std::regex_match(name_with_target, match, Klammer::name_re)) { throw Parsing_error( "The klammer name \"" + name_with_target + "\" is not correctly defined. " "The form is \"\" for general klammers or \".\" " "for a specialized target. The klammer defined as \".k\" specifies the " "arguments and contains a description of the klammer in the definition body.", name_katom.m_loc); } std::string klammer_name = match[1]; std::string target_name = match[2]; if (target_name.empty()) { target_name = Target_set::general_name; } if (!targets.has(target_name)) { throw Target_error( "The target \"" + target_name + "\" in klammer definition \"" + name_with_target + "\" " "is not defined. Enter \"kdesc -t\" to see the targets defined by the Standard Klammer Set.", name_katom.m_loc); } return { klammer_name, target_name }; } std::tuple parse_definition_katoms(std::string klammer_name, Argtype_set argtypes, katom_iter& begin, katom_iter& end) { (void)K::log(3, *begin, *(end - 1)); katom_iter deftype = std::find_if( begin, end, [](const Katom& k) { return is_deftype(k.m_type); }); if (deftype == end) { throw Argument_error( "The klammer definition does not contain a definition separator that specifies\n" "how the klammer should be defined. The klammer syntax is:\n\n" " @@[.] : @@ definition\n" " @@[.] :: @@ instance (uses .k parameters)\n" " @@[.] ::: @@ override existing definition\n" " @@[.] :::: @@ default (can be overridden)", begin->m_loc, false); } katom_list parameter_katoms(begin, deftype); // Resolve text-removal markers (#, #[...]#, ##) in the parameter list. // The interior of a @@...@@ definition is held verbatim until now (so a // literal klammer body receives its content untouched), which means the // parameter declarations still contain any removal markers the writer // used. Apply removal here, to the parameters only -- the body is left // verbatim by add_target_definition. mark_ignored_katoms(parameter_katoms); std::erase_if(parameter_katoms, [](const Katom& k) { return k.m_type == katom_t::ignored; }); parameter_katoms = trim_whitespace(parameter_katoms); if ((deftype->m_type == katom_t::klammer_instance || deftype->m_type == katom_t::klammer_override) && !parameter_katoms.empty()) { std::string sym = deftype->m_type == katom_t::klammer_instance ? "::" : ":::"; throw Definition_error( "The \"" + klammer_name + "\" klammer uses the \"" + sym + "\" symbol but defines parameters.", begin->m_loc); } Parameter_set parameters(parameter_katoms, argtypes); katom_list body_katoms(deftype + 1, end - 1); body_katoms = trim_whitespace(body_katoms); return { *deftype, parameters, body_katoms, begin->m_loc }; } void Klammer::add_target_definition( std::string target_name, Argtype_set argtypes, katom_iter begin, katom_iter end) { (void)K::log(3, *begin, *(end-1)); auto [deftype, parameters, body, loc] = parse_definition_katoms(m_name, argtypes, begin, end); // targets, begin, end); // msg() << "Klammer " << m_name << " add: " << target_name << "\n"; // parameters.describe_parameters(); std::regex variable_re(R"(\*(\w+)\*)"); int i = 0; variable_map_t varmap {}; for (auto k : body) { std::smatch match {}; std::string txt = k.m_text; while (std::regex_search(txt, match, variable_re) && (k.m_type == katom_t::karg || k.m_type == katom_t::text)) { //std::cout << " txt: " << txt << "\n"; std::string varname = match[1]; //m_varmap[target_name][varname].push_back(i); varmap[varname].push_back(i); txt = std::regex_replace(txt, std::regex(R"(\*)" + varname + R"(\*)"), ""); } i++; } // m_body[target_name] = body; m_defs.push_back({target_name, deftype.m_initial_type, parameters, body, varmap, begin->m_loc}); m_defloc[target_name] = begin->m_loc; m_defmode[target_name] = defmode_from_katom(deftype.m_initial_type); // std::cout << "Klammer " << m_name << "." << target_name << ": " << m_defloc[target_name] << "\n"; } void Klammer::remove_target_definition(const std::string& target_name) { std::erase_if(m_defs, [&target_name](const auto& def) { return def.target == target_name; }); m_defloc.erase(target_name); m_defmode.erase(target_name); m_body.erase(target_name); m_varmap.erase(target_name); } // Rationalize multiple definitions std::string error_list(std::string label, auto components, std::string after="") { std::stringstream ss {}; ss << label << ":\n"; for (auto c : components) { ss << " " << c.loc.desc() << "\n"; } ss << after; return ss.str(); } auto Klammer::target_defs(std::vector target_names) { std::vector defs {}; for (auto target : target_names) { auto target_defs = collect_if( m_defs, [target](const auto& def) { return def.target == target; }); defs.insert(defs.end(), target_defs.begin(), target_defs.end()); } return defs; } auto Klammer::instance_defs() { return collect_if( m_defs, [] (const auto& def) { return def.deftype == katom_t::klammer_instance || def.deftype == katom_t::klammer_override; }); } void Klammer::disallow_instances() //Klammer::components declaration) { auto instances = instance_defs(); if (!instances.empty()) { int icount = instances.size(); std::stringstream ss {}; ss << "There " << to_be(icount) << " " << icount << " " << plural("instance", icount) << " (defined by \"::\"), but " << "no declarations (defined by a \".k\" target)"; throw Definition_error( error_list(ss.str(), instances), instances[0].loc, false); } } bool Klammer::copy_to_instances(Target_set targets) { auto instances = instance_defs(); if (!instances.empty()) { auto definitions = collect_if( m_defs, [] (const auto& def) { return def.deftype != katom_t::klammer_instance && def.deftype != katom_t::klammer_override; }); int dcount = definitions.size(); if (dcount != 1) { int icount = instances.size(); std::stringstream ss {}; ss << "There " << to_be(icount) << " " << icount << " " << plural("instance", icount) << " (defined by \"::\"), but " << dcount << " "<< plural("definition", dcount) << " (defined by \":\")"; throw Definition_error( error_list(ss.str(), definitions), definitions[0].loc, false); } else { copy_components(definitions[0].parameters, m_defs, targets); return true; } } else { return false; } } void Klammer::copy_components( Parameter_set parameters, std::vector cs, Target_set targets) { (void)K::log(4); for (auto target_name : targets.m_names) { if (target_name == Target_set::declare_name || target_name == Target_set::general_name) { continue; } m_parameters = parameters; } for (auto c : cs) { m_body[c.target] = c.body; m_varmap[c.target] = c.varmap; } } // Verify that there is no more than one general klammer definition void Klammer::check_for_multiple_general_klammers() { auto general_klammers = target_defs({Target_set::general_name}); if (general_klammers.size() > 1) { throw Definition_error( error_list( "There is more than one general klammer (a klammer in which no target is defined)", general_klammers), general_klammers[0].loc, false); } } void Klammer::check_for_declaration_and_definitions() { auto declares = target_defs({Target_set::declare_name}); if (!declares.empty()) { std::vector definitions {}; for (auto def : m_defs) { if (def.target != Target_set::declare_name) { if (def.deftype == katom_t::klammer_definition || def.deftype == katom_t::klammer_default) { msg() << def << "\n"; definitions.push_back(def); } } } if (!definitions.empty()) { auto defsize = definitions.size(); std::string desc = defsize == 1 ? "a definition" : std::to_string(defsize) + " definitions"; throw Definition_error( error_list("A klammer has both a declaration (.k) as well as " + desc + "\n(instances are defined by \"::\")", definitions), declares[0].loc, false); } } } // If a general definition exists, use it for targets not defined, but check signatures void Klammer::copy_general_klammer_to_undefined(Target_set targets) { (void)K::log(4); auto general_klammers = target_defs({Target_set::general_name}); auto declares = target_defs({Target_set::declare_name}); // Check matching signatures (though this case already handled) if (general_klammers.size() == 1) { if (declares.empty() && !m_parameters.m_katoms.empty()) { auto general_parameters = general_klammers[0].parameters; if (general_parameters != m_parameters) { // m_parameters.describe_parameters(); throw Definition_error( error_list("The general parameters are different than the defined parameters", general_klammers), m_parameters.m_katoms[0].m_loc, false); } } auto [target, deftype, parameters, body, varmap, loc] = general_klammers[0]; if (m_parameters.m_katoms.empty()) { m_parameters = parameters; } for (auto target_name : targets.m_names) { // std::cout << "General copy, considering " << target_name << "\n"; if (m_body.count(target_name) == 0 && target_name != Target_set::declare_name) { // std::cout << " Copying to " << target_name << "\n"; m_body[target_name] = body; m_defloc[target_name] = loc; m_varmap[target_name] = varmap; } } } } // Three declaration cases: none, one, many void Klammer::no_declarations(Target_set targets) { (void)K::log(4); // std::cout << boldblack << "No declarations\n" << black; check_for_multiple_general_klammers(); std::vector defs {}; std::vector target_names = targets.applicable(); std::vector all_parameter_sets {}; // Are all parameters the same? for (auto def : m_defs) { if (std::ranges::find(target_names, def.target) != target_names.end()) { // std::cout << " Found: " << def.target << "\n"; all_parameter_sets.push_back(def.parameters); } else { // std::cout << " Not found: " << def.target << "\n"; } } if (!all_equal(all_parameter_sets)) { // std::cout << " Not all equal\n"; throw Definition_error( error_list("There is no declaration (.k) target for klammer \"" + m_name + "\"\n" "but the parameters of all targets are not the same", m_defs, "Use a .k klammer to define the parameters and describe the klammer,\n" "with \"::\" and no parameters for all targets."), m_defs[0].loc, false); } else { // std::cout << " All equal\n"; copy_components(m_defs[0].parameters, m_defs, targets); } copy_general_klammer_to_undefined(targets); } void Klammer::one_declaration(Target_set targets, Klammer::components declare) { (void)K::log(4); // std::cout << boldblack << "One declaration\n" << black; check_for_multiple_general_klammers(); check_for_declaration_and_definitions(); copy_components(declare.parameters, m_defs, targets); copy_general_klammer_to_undefined(targets); } void Klammer::many_declarations(std::vector declares) { (void)K::log(4); // std::cout << boldblack << "Many declarations\n" << black; throw Definition_error( error_list("More than one declaration (.k) klammer", declares), declares[0].loc, false); } void Klammer::rationalize(Target_set targets) { (void)K::log(3, m_name); auto declares = target_defs({Target_set::declare_name}); auto declare_count = declares.size(); if (declare_count == 0) { disallow_instances(); if (!copy_to_instances(targets)) { no_declarations(targets); } } else if (declare_count == 1) { one_declaration(targets, declares[0]); } else { many_declarations(declares); } } std::string klammer_name_from_katom(std::string s, Locator loc) { std::regex rgx(R"(@(\w+).*)"); std::smatch match {}; if (std::regex_match(s, match, rgx)) { return match[1]; } else { throw Definition_error("The form of the klammer name " + q_(s) + " is not correct", loc); } } void label(const std::string& s) { int w = 13; std::cout << std::right << std::setw(w) << std::setfill(' ') << s << ": "; } void show_args(const std::string& label_text, std::vector arguments) { if (!arguments.empty()) { label(label_text); for (auto a : arguments) { std::cout << a << " "; } std::cout << '\n'; } } strings_t Klammer::get_target_names() const { strings_t names {}; for (auto [target, body] : m_body) { std::stringstream ss {}; // ss << name << target.m_loc.m_line; ss << target; names.push_back(ss.str()); } return names; // return join(names, ","s); } strings_t Klammer::get_locations() { strings_t locs {}; for (auto [target, locator] : m_defloc) { std::cout << target << right_arrow << locator << "\n"; } return {}; } std::string Klammer::signature_text() { std::string result {}; bool has_pos = false; bool has_opt = false; for (auto pos : m_parameters.m_positional) { result += pos.m_name; std::string type = pos.m_argtype.m_name; if (type != default_argtype) { result += "." + type; } result += " | "; has_pos = true; } if (result.size() >= 2) result.resize(result.size() - 2); auto opt_count = m_parameters.m_optional.size(); if (opt_count <= 3 && !has_pos) { result += " "; } for (auto opt : m_parameters.m_optional) { if (opt_count > 3) { result += "\n :" + opt.m_name; } else { result += ":" + opt.m_name; } std::string type = opt.m_argtype.m_name; if (type != default_argtype) { result += "." + type; } // result += ":" + opt.m_name + "." + opt.m_argtype.m_name; if (!opt.m_default.empty()) { result += " " + italic_on() + opt.m_default + italic_off(); } result += " "; has_opt = true; } result = trim_right(result); if (has_pos) { result = " " + result; } if (opt_count > 3) { result += "\n"; } else if (has_opt or has_pos) { result += " "; } result += "@\n"; return result; } std::string Klammer::description_text() { std::string result = " [" + m_name + ": no description]"; if (m_body.contains("k")) { result = to_string(m_body["k"], true); result = justify(result, 80, 1); } return result; } std::string Klammer::describe(int margin) { std::string result {}; result += "@" + m_name + signature_text() + description_text(); result = add_margin(result, margin) + "\n"; return result; }