#include #include #include "target_registry.h" #include "error.h" #include "log.h" #include "util.h" #include "show.h" #include "log.h" #include "katom.h" std::string Target_registry::declare_name = "k"; std::string Target_registry::general_name = "*"; std::string Target_registry::optionset_name = "o"; Target_registry::Target_registry() : m_parameters(Parameter_set("name | desc :after_apply :after_write :includes :escape :resolve | transforms.rest")) { // Registration order is display order. The two that declare an interface // come first -- "k" a klammer's, "o" an option set's -- and then "*", the // targets themselves beginning after it. Target k(declare_name, "Description of parameters and klammer result", Locator()); Target option_set(optionset_name, "Declaration of an option set: parameters shared by klammers", Locator()); Target general(general_name, "All targets", Locator()); add(k); add(option_set); add(general); } void Target_registry::add(Target target) { (void)K::log(3, target); check_for_previous_definition(target.m_name, target.m_loc); m_targets[target.m_name] = target; m_names.push_back(target.m_name); } void Target_registry::add(std::vector::iterator begin, std::vector::iterator end, std::vector& katoms) { (void)K::log(3, *begin, *(end - 1)); auto [positional, optional, rest] = argument_split(begin + 1, end - 1, m_parameters.m_positional.size()); auto values = m_parameters.value_map(positional, optional, rest, begin->m_loc); //std::cout << ktype << "Transformed: " << kreplaced << std::pair(begin, end) << "\n"; //std::cout << values << "\n"; Target target(values["name"], values["desc"], begin->m_loc); target.add_transforms(values["transforms"]); target.add_escapes(values["escape"]); target.add_resolves(values["resolve"]); target.add_after_apply(values["after_apply"]); target.m_includes = word_split(values["includes"]); // Inherit escapes AND typographic transforms from included targets: an // including target renders through the included one's syntax (pdf // through tex), so both tables apply there too. Inherited entries are // appended after the including target's own, so a declaration can // override an inherited pair by declaring its source first. for (const auto& included : target.m_includes) { if (m_targets.count(included)) { for (const auto& esc : m_targets[included].m_escapes) { target.m_escapes.push_back(esc); } for (const auto& tr : m_targets[included].m_transforms) { target.add_transform(tr.first, tr.second); } for (const auto& res : m_targets[included].m_resolves) { target.m_resolves.push_back(res); } } } // Registration (and the previous-definition check) goes through // add(Target) -- the single registration path. add(target); for (auto& [name, defined_target] : m_targets) { if (is_in(name, target.m_includes)) { defined_target.m_provides.push_back(target.m_name); } } // std::for_each(begin, end, [](Katom& k) { k.m_type = katom_t::replaced; }); modify_type(katom_t::replaced, begin, end); auto next_iter = end; ignore_whitespace(next_iter, katoms); } void Target_registry::check_for_previous_definition(const std::string& name, const Locator& loc) const { if (has(name)) { const Target& current = m_targets.at(name); throw Target_error("Target \"" + name + "\" is already defined:\n " + current.m_loc.desc(), loc); } } bool Target_registry::has(const std::string& target_name) const { // Membership comes from the map; m_names exists only to preserve // definition order for describe(). return m_targets.count(target_name) > 0; } Target Target_registry::get(const std::string& target_name, const Locator& loc) const { if (has(target_name) || target_name == Target_registry::general_name) { return m_targets.at(target_name); } else { throw Target_error("Target " + target_name + " does not exist", loc); } } void Target_registry::transform(const std::string& target_name, katom_list& katoms) const { (void)K::log(3); m_targets.at(target_name).transform(katoms); } std::vector Target_registry::user_defined() const { return collect_if( m_names, [](const auto& name) { return name != Target_registry::declare_name && name != Target_registry::general_name && name != Target_registry::optionset_name; }); } // The targets a klammer body can be applied under. Neither "k" nor "o" // produces output: both declare an interface and describe it. std::vector Target_registry::applicable() const { return collect_if( m_names, [](const auto& name) { return name != Target_registry::declare_name && name != Target_registry::optionset_name; }); } std::string Target_registry::describe(int margin, bool long_format, const strings_t& defined_outside) const { // The built-in pseudo-targets belong to the language rather than to any // klammerset, so a filtered listing keeps them. auto shown = [&](const std::string& name) { if (defined_outside.empty()) return true; if (name == declare_name || name == general_name || name == optionset_name) { return true; } return !is_in(m_targets.at(name).m_loc.m_filename, defined_outside); }; std::string tab(margin, ' '); std::stringstream ss {}; std::vector descs {}; for (const std::string& name : m_names) { descs.push_back(m_targets.at(name).m_desc); } auto name_width = max_length(m_names); auto desc_width = max_length(descs); for (const std::string& name : m_names) { if (!shown(name)) continue; const Target& t = m_targets.at(name); if (long_format) { ss << tab << std::setfill(' ') << std::setw(name_width) << std::right << name << " " << std::setw(desc_width) << std::left << t.m_desc << " " << t.m_loc.str() << "\n"; } else { ss << tab << std::setfill(' ') << std::setw(name_width) << name << sp_arrow << t << "\n"; } } return ss.str(); } /* void Targets::describe() { std::string intro = "A \"target\" specifies the output format of Klammertext processing. " "Targets are identified by the typical filename extension of the format. " "A klammer defines how it converts its arguments to the appropriate structure for one or more targets. " "The special target \"k\" is used for a klammer definition that describes that klammer's " "arguments and purpose in the various targets for which it is defined. " "If the klammer definition does not specify a target, the klammer can be used for any target."; std::cout << "Klammertext targets\n\n" << justify(intro) << "\n\n"; for (std::string name : names) { if (name == Target::any_target_name) continue; targets[name]->describe(); std::cout << "\n"; } } */