A named group of optional parameters, declared once and used by several
klammers, so a writer learns one vocabulary instead of a spelling per
klammer. The "o" target is a pseudo-target beside "k": "k" declares a
klammer's interface and documents it, "o" declares an option interface and
documents it, and neither produces output for any target.
@@caption_args.o :caption :number.bool true :caption_side.side
: Arguments that define a caption for a block element @@
@@code.k :filename @hpos_args :hpos left @ @caption_args :caption_side top @
| text.literal : A source file displayed verbatim @@
A set is used only in the parameter list of a ".k" declaration -- the one
place a klammer's interface is declared once for all of its targets -- and
is resolved as that list is read. Names and types come from the set; a
default may be overridden where it is used. A klammer application in a
parameter list is now a definition-time error.
The SKS gains the sets caption_args and hpos_args (:hpos and :offset), and
@table, @image, @image_grid, @reference and @show gain .k declarations. A
distance is no longer written as a position: :hpos 4em is rejected, and the
same layout is :hpos left :offset 4em. Code listings are numbered by
default, like tables and figures.
New engine sources mac/option_set{,_registry}.{h,cpp}; tst/ ships two more
suites, option_set_test.sh and signature_test.sh (twelve in all).
(from dev 34e536cb0329)
187 lines
6.4 KiB
C++
187 lines
6.4 KiB
C++
#include "klammer.h"
|
|
#include "klammer_registry.h"
|
|
#include "show.h"
|
|
#include "util.h"
|
|
#include "log.h"
|
|
#include "error.h"
|
|
|
|
/*
|
|
bool Klammer_registry::has(std::string name, std::string target)
|
|
{
|
|
return m_klammers.count(name) > 0;
|
|
}
|
|
*/
|
|
|
|
void Klammer_registry::add(
|
|
const Argtype_registry& argtypes, const Target_registry& targets,
|
|
Option_set_registry& option_sets, katom_iter begin, katom_iter end, katom_list& katoms)
|
|
{
|
|
(void)K::log(3, *begin, *(end - 1));
|
|
restore_initial_type(begin, end);
|
|
auto [klammer_name, target_name] = parse_name(targets, *begin);
|
|
if (!targets.has(target_name)) {
|
|
throw Argument_error("The target \"" + target_name + "\" is not defined", begin->m_loc);
|
|
}
|
|
if (target_name == Target_registry::optionset_name) {
|
|
// The Machine routes an ".o" definition to the option set registry;
|
|
// reaching here means it did not.
|
|
throw Internal_error(
|
|
"The option set declaration \"" + klammer_name + ".o\" reached the klammer registry",
|
|
begin->m_loc);
|
|
}
|
|
// Find the incoming definition mode
|
|
katom_t incoming_deftype = katom_t::klammer_definition;
|
|
for (auto it = begin + 1; it != end - 1; ++it) {
|
|
if (is_deftype(it->m_type)) {
|
|
incoming_deftype = it->m_type;
|
|
break;
|
|
}
|
|
}
|
|
defmode_t incoming_mode = defmode_from_katom(incoming_deftype);
|
|
|
|
if (m_klammers.count(klammer_name) == 0) {
|
|
m_klammers[klammer_name] = Klammer(klammer_name);
|
|
} else if (m_klammers[klammer_name].m_defloc.count(target_name) > 0) {
|
|
defmode_t existing_mode = m_klammers[klammer_name].m_defmode[target_name];
|
|
const auto& result = defmode_transition(existing_mode, incoming_mode);
|
|
std::string name_target = klammer_name + "." + target_name;
|
|
std::string at_desc = m_klammers[klammer_name].m_defloc[target_name].desc();
|
|
if (!result.replace) {
|
|
if (result.message.empty()) {
|
|
// Silent ignore (e.g., create + default)
|
|
modify_type(katom_t::replaced, begin, end);
|
|
ignore_whitespace(end, katoms);
|
|
return;
|
|
}
|
|
std::string msg = result.message;
|
|
msg = string_replace(msg, "NAME", q_(name_target));
|
|
msg = string_replace(msg, "AT", at_desc);
|
|
throw Definition_error(msg, begin->m_loc);
|
|
}
|
|
if (result.warn) {
|
|
std::string msg = result.message;
|
|
msg = string_replace(msg, "NAME", q_(name_target));
|
|
msg = string_replace(msg, "AT", at_desc);
|
|
warning(msg, begin->m_loc);
|
|
}
|
|
m_klammers[klammer_name].remove_target_definition(target_name);
|
|
}
|
|
m_klammers[klammer_name].add_target_definition(
|
|
target_name, argtypes, option_sets, begin + 1, end - 1);
|
|
|
|
// This add's target:
|
|
Target target = targets.get(target_name, begin->m_loc);
|
|
if (!target.m_provides.empty()) {
|
|
for (const auto& provide_name : target.m_provides) {
|
|
if (m_klammers[klammer_name].m_defloc.count(provide_name) > 0) {
|
|
m_klammers[klammer_name].remove_target_definition(provide_name);
|
|
}
|
|
m_klammers[klammer_name].add_target_definition(
|
|
provide_name, argtypes, option_sets, begin + 1, end - 1);
|
|
}
|
|
}
|
|
|
|
modify_type(katom_t::replaced, begin, end);
|
|
auto next_iter = end;
|
|
if (next_iter < katoms.end()) {
|
|
// std::cout << "next_iter: " << kindex << *next_iter << "\n";
|
|
} else {
|
|
// std::cout << "next_iter past end. katoms length: " << katoms.size() << "\n";
|
|
}
|
|
ignore_whitespace(next_iter, katoms);
|
|
}
|
|
|
|
void Klammer_registry::rationalize(const Target_registry& targets)
|
|
{
|
|
(void)K::log(3);
|
|
for (auto& [name, klammer] : m_klammers) {
|
|
klammer.rationalize(targets);
|
|
}
|
|
}
|
|
|
|
/*
|
|
bool Klammer_registry::has(std::string name, std::string target)
|
|
{
|
|
return m_klammers.count(name) > 0;
|
|
}
|
|
*/
|
|
|
|
void Klammer_registry::check_klammer(const std::string& name, const std::string& target, const Locator& loc) const
|
|
{
|
|
if (m_klammers.count(name) == 0) {
|
|
throw Definition_error("The klammer " + q_(name) + " is not defined for an unspecified target", loc);
|
|
}
|
|
const Klammer& k = m_klammers.at(name);
|
|
if (k.m_defloc.count(target) == 0) {
|
|
std::string desc;
|
|
if (target == Target_registry::general_name) {
|
|
desc = "an unspecified target";
|
|
} else {
|
|
desc = "target " + q_(target);
|
|
}
|
|
throw Definition_error("The " + q_(name) + " klammer is not defined for " + desc, loc);
|
|
}
|
|
}
|
|
|
|
|
|
const std::vector<Katom>* Klammer_registry::constant_body(const std::string& name) const
|
|
{
|
|
auto it = m_klammers.find(name);
|
|
if (it == m_klammers.end()) return nullptr;
|
|
const Klammer& k = it->second;
|
|
// A constant klammer has no parameters at all — neither in the general
|
|
// definition nor inherited from a .k declaration. A "::" instance has
|
|
// empty d.parameters (it inherits), so we must also check that no other
|
|
// definition (especially .k) declares parameters for this klammer.
|
|
for (const auto& d : k.m_defs) {
|
|
if (!d.parameters.empty()) return nullptr;
|
|
}
|
|
for (const auto& d : k.m_defs) {
|
|
if (d.target == Target_registry::general_name && d.parameters.empty()) {
|
|
return &d.body;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int max_length(const std::map<std::string, Klammer>& ss)
|
|
{
|
|
size_t result = 0;
|
|
for_each(ss.begin(), ss.end(),
|
|
[&result](const auto& s) { result = std::max(result, s.first.size()); });
|
|
return result;
|
|
}
|
|
|
|
std::string Klammer_registry::instance_list(int margin) const
|
|
{
|
|
std::stringstream ss {};
|
|
std::string tab(margin, ' ');
|
|
auto name_width = max_length(m_klammers);
|
|
for (const auto& [name, k] : m_klammers) {
|
|
ss << tab << std::setfill(' ') << std::setw(name_width) << name
|
|
<< sp_arrow << k << "\n";
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
std::string Klammer_registry::describe(int margin) const
|
|
{
|
|
/*
|
|
strings_t names {};
|
|
std::vector<strings_t> targets {};
|
|
strings_t locations {};
|
|
*/
|
|
std::string result;
|
|
for (const auto& [name, k] : m_klammers) {
|
|
result += k.describe(margin) + "\n";
|
|
/*
|
|
names.push_back(name);
|
|
targets.push_back(k.get_target_names());
|
|
std::cout << name << " " << k.get_target_names() << "\n";
|
|
locator_summary(k.get_locations());
|
|
//locations.push_back(
|
|
*/
|
|
}
|
|
return result;
|
|
}
|