Option sets: a .o target for shared parameters
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)
This commit is contained in:
@@ -9,7 +9,8 @@ include $(K)/env/makefile.env
|
||||
# Source files
|
||||
BASENAMES := util error locator file argv character ktype katom katom_list \
|
||||
log show command argument argument_set argtype argtype_registry \
|
||||
state eval eval_python eval_cpp klammer klammer_registry klammerset klammerset_registry deftype \
|
||||
state eval eval_python eval_cpp klammer klammer_registry klammerset klammerset_registry \
|
||||
option_set option_set_registry deftype \
|
||||
target target_registry machine font_store check
|
||||
|
||||
SOURCES := $(addsuffix .cpp,$(BASENAMES))
|
||||
|
||||
@@ -29,10 +29,19 @@ public:
|
||||
Argtype m_argtype; // {};
|
||||
bool m_optional {};
|
||||
std::string m_default {};
|
||||
// True when m_default was filled from the argument type's :default
|
||||
// rather than declared in the klammer's parameter list (for kdesc
|
||||
// provenance display).
|
||||
// Where m_default came from, for kdesc provenance display. Default
|
||||
// resolution has three levels -- the argument type's :default, an option
|
||||
// set's declaration, and the site where the set is used (or the klammer's
|
||||
// own parameter list) -- and a reader of kdesc is entitled to know which
|
||||
// one produced the value a klammer will use when the argument is absent.
|
||||
// True when m_default was filled from the argument type's :default:
|
||||
bool m_default_from_type {};
|
||||
// The option set (".o" target) this parameter was declared in; empty when
|
||||
// it was declared in the klammer's own parameter list.
|
||||
std::string m_option_set {};
|
||||
// True when the option set declares a default for this parameter and the
|
||||
// declaration using the set overrode it.
|
||||
bool m_default_overridden {};
|
||||
Locator m_loc;
|
||||
std::string m_target {};
|
||||
};
|
||||
|
||||
@@ -13,6 +13,13 @@ std::tuple<std::vector<std::vector<Katom>>,std::vector<std::vector<Katom>>,std::
|
||||
argument_split(std::vector<Katom>::const_iterator kbegin, std::vector<Katom>::const_iterator kend,
|
||||
long unsigned int positional_limit = std::numeric_limits<int>::max());
|
||||
|
||||
// The katoms of a parameter list, split into one list per parameter:
|
||||
// positional first, then optional (each beginning with its option-name
|
||||
// katom). An option set keeps the katoms of its members this way, because
|
||||
// those katoms are what a declaration using the set ends up declaring.
|
||||
std::tuple<std::vector<std::vector<Katom>>,std::vector<std::vector<Katom>>>
|
||||
parameter_split(std::vector<Katom>::const_iterator kbegin, std::vector<Katom>::const_iterator kend);
|
||||
|
||||
class Parameter_set
|
||||
{
|
||||
public:
|
||||
|
||||
127
mac/klammer.cpp
127
mac/klammer.cpp
@@ -41,7 +41,10 @@ parse_name(const Target_registry& targets, const Katom& name_katom)
|
||||
}
|
||||
|
||||
std::tuple<Katom, Parameter_set, katom_list, Locator>
|
||||
parse_definition_katoms(const std::string& klammer_name, const Argtype_registry& argtypes, katom_iter& begin, katom_iter& end)
|
||||
parse_definition_katoms(
|
||||
const std::string& klammer_name, const std::string& target_name,
|
||||
const Argtype_registry& argtypes, Option_set_registry& option_sets,
|
||||
katom_iter& begin, katom_iter& end)
|
||||
{
|
||||
(void)K::log(3, *begin, *(end - 1));
|
||||
katom_iter deftype = std::find_if(
|
||||
@@ -68,7 +71,13 @@ parse_definition_katoms(const std::string& klammer_name, const Argtype_registry&
|
||||
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 ||
|
||||
// "::" and ":::" take their parameters from the ".k" declaration, so a
|
||||
// parameter list written with them is a mistake -- EXCEPT for an option
|
||||
// set, which has no separate declaration to inherit from: a set is its
|
||||
// own declaration, so an override restates what it declares.
|
||||
bool inherits_parameters = target_name != Target_registry::optionset_name;
|
||||
if (inherits_parameters &&
|
||||
(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 ? "::" : ":::";
|
||||
@@ -76,7 +85,16 @@ parse_definition_katoms(const std::string& klammer_name, const Argtype_registry&
|
||||
"The \"" + klammer_name + "\" klammer uses the \"" + sym + "\" symbol but defines parameters.",
|
||||
begin->m_loc);
|
||||
}
|
||||
// Replace the option sets used in the parameter list with the parameters
|
||||
// they declare. This is where the difference between an option set and a
|
||||
// klammer lies: a set is resolved HERE, as the parameter list is read,
|
||||
// rather than in the fixed-point apply loop, so what it contributes is
|
||||
// present when the list is parsed. Any other klammer application in a
|
||||
// parameter list is rejected by the same pass.
|
||||
option_set_uses_t option_set_uses =
|
||||
expand_option_sets(parameter_katoms, klammer_name, target_name, option_sets);
|
||||
Parameter_set parameters(parameter_katoms, argtypes);
|
||||
stamp_option_set_uses(parameters, option_set_uses);
|
||||
|
||||
katom_list body_katoms(deftype + 1, end - 1);
|
||||
body_katoms = trim_whitespace(body_katoms);
|
||||
@@ -84,11 +102,12 @@ parse_definition_katoms(const std::string& klammer_name, const Argtype_registry&
|
||||
}
|
||||
|
||||
void Klammer::add_target_definition(
|
||||
const std::string& target_name, const Argtype_registry& argtypes, katom_iter begin, katom_iter end)
|
||||
const std::string& target_name, const Argtype_registry& argtypes,
|
||||
Option_set_registry& option_sets, 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);
|
||||
parse_definition_katoms(m_name, target_name, argtypes, option_sets, begin, end);
|
||||
// msg() << "Klammer " << m_name << " add: " << target_name << "\n";
|
||||
// parameters.describe_parameters();
|
||||
|
||||
@@ -208,7 +227,8 @@ void Klammer::copy_components(
|
||||
(void)K::log(4);
|
||||
for (const auto& target_name : targets.m_names) {
|
||||
if (target_name == Target_registry::declare_name ||
|
||||
target_name == Target_registry::general_name) {
|
||||
target_name == Target_registry::general_name ||
|
||||
target_name == Target_registry::optionset_name) {
|
||||
continue;
|
||||
}
|
||||
m_parameters = parameters;
|
||||
@@ -243,7 +263,6 @@ void Klammer::check_for_declaration_and_definitions()
|
||||
if (def.target != Target_registry::declare_name) {
|
||||
if (def.deftype == katom_t::klammer_definition ||
|
||||
def.deftype == katom_t::klammer_default) {
|
||||
msg() << def << "\n";
|
||||
definitions.push_back(def);
|
||||
}
|
||||
}
|
||||
@@ -286,7 +305,11 @@ void Klammer::copy_general_klammer_to_undefined(const Target_registry& targets)
|
||||
}
|
||||
for (const auto& target_name : targets.m_names) {
|
||||
// std::cout << "General copy, considering " << target_name << "\n";
|
||||
if (m_body.count(target_name) == 0 && target_name != Target_registry::declare_name) {
|
||||
// "k" and "o" declare interfaces rather than produce output, so a
|
||||
// general body is never copied to them.
|
||||
if (m_body.count(target_name) == 0 &&
|
||||
target_name != Target_registry::declare_name &&
|
||||
target_name != Target_registry::optionset_name) {
|
||||
// std::cout << " Copying to " << target_name << "\n";
|
||||
m_body[target_name] = body;
|
||||
m_body_generic[target_name] = true; // general body -> writer content
|
||||
@@ -299,6 +322,39 @@ void Klammer::copy_general_klammer_to_undefined(const Target_registry& targets)
|
||||
|
||||
// Three declaration cases: none, one, many
|
||||
|
||||
namespace {
|
||||
|
||||
// A compact, plain-text rendering of ONE definition's parameter list, for
|
||||
// diagnostics that must show how two definitions differ.
|
||||
// Klammer::signature_text() cannot serve here: it renders the rationalized
|
||||
// m_parameters, which is exactly what does not exist yet when the per-target
|
||||
// lists disagree.
|
||||
std::string parameter_signature(const Parameter_set& parameters)
|
||||
{
|
||||
std::string result {};
|
||||
auto add = [&result](const std::string& s) {
|
||||
if (!result.empty()) result += " ";
|
||||
result += s;
|
||||
};
|
||||
auto typed = [](const Parameter& p) {
|
||||
return p.m_argtype.m_name == default_argtype
|
||||
? p.m_name : p.m_name + "." + p.m_argtype.m_name;
|
||||
};
|
||||
bool first = true;
|
||||
for (const auto& pos : parameters.m_positional) {
|
||||
add(first ? typed(pos) : "| " + typed(pos));
|
||||
first = false;
|
||||
}
|
||||
for (const auto& rest : parameters.m_rest)
|
||||
add(typed(rest));
|
||||
for (const auto& opt : parameters.m_optional)
|
||||
add(":" + typed(opt)
|
||||
+ (opt.m_default.empty() ? "" : " " + opt.m_default));
|
||||
return result.empty() ? "(no parameters)" : result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Klammer::no_declarations(const Target_registry& targets)
|
||||
{
|
||||
(void)K::log(4);
|
||||
@@ -318,14 +374,24 @@ void Klammer::no_declarations(const Target_registry& targets)
|
||||
}
|
||||
}
|
||||
if (!all_equal<Parameter_set>(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);
|
||||
// Show each target's own parameter list, not just its location: with
|
||||
// more than two targets the designer would otherwise have to diff the
|
||||
// definitions by hand to find which one drifted.
|
||||
std::stringstream ss {};
|
||||
ss << "The parameters of klammer \"" << m_name
|
||||
<< "\" are not the same for every target,\n"
|
||||
"and there is no declaration (.k) target to define them once:\n";
|
||||
for (const auto& def : m_defs) {
|
||||
if (std::ranges::find(target_names, def.target) == target_names.end())
|
||||
continue;
|
||||
std::string target = def.target;
|
||||
target.resize(std::max(target.size(), size_t(6)), ' ');
|
||||
ss << " " << target << " " << parameter_signature(def.parameters)
|
||||
<< "\n " << def.loc.desc() << "\n";
|
||||
}
|
||||
ss << "Use a .k target to declare the parameters and describe the klammer,\n"
|
||||
"and \"::\" with no parameters for each target's definition.";
|
||||
throw Definition_error(ss.str(), m_defs[0].loc, false);
|
||||
} else {
|
||||
// std::cout << " All equal\n";
|
||||
copy_components(m_defs[0].parameters, m_defs, targets);
|
||||
@@ -485,10 +551,39 @@ std::string Klammer::description_text() const
|
||||
}
|
||||
|
||||
|
||||
// Which option sets this klammer's parameters came from, and which of their
|
||||
// defaults it overrode. A reader of the signature sees the effective
|
||||
// interface; this says how much of it the klammer shares with other klammers,
|
||||
// which is the reason for declaring a set in the first place.
|
||||
std::string Klammer::option_set_text() const
|
||||
{
|
||||
std::vector<std::string> sets {};
|
||||
std::map<std::string, std::vector<std::string>> overridden {};
|
||||
for (const auto& opt : m_parameters.m_optional) {
|
||||
if (opt.m_option_set.empty()) continue;
|
||||
if (!is_in(opt.m_option_set, sets)) {
|
||||
sets.push_back(opt.m_option_set);
|
||||
}
|
||||
if (opt.m_default_overridden) {
|
||||
overridden[opt.m_option_set].push_back(":" + opt.m_name);
|
||||
}
|
||||
}
|
||||
if (sets.empty()) return "";
|
||||
std::vector<std::string> descriptions {};
|
||||
for (const auto& set : sets) {
|
||||
std::string desc = set;
|
||||
if (overridden.count(set) > 0) {
|
||||
desc += " (" + join(overridden[set], ", ") + " defaulted here)";
|
||||
}
|
||||
descriptions.push_back(desc);
|
||||
}
|
||||
return "\n Option sets: " + join(descriptions, ", ");
|
||||
}
|
||||
|
||||
std::string Klammer::describe(int margin) const
|
||||
{
|
||||
std::string result {};
|
||||
result += "@" + m_name + signature_text() + description_text();
|
||||
result += "@" + m_name + signature_text() + description_text() + option_set_text();
|
||||
result = add_margin(result, margin) + "\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "deftype.h"
|
||||
#include "argument_set.h"
|
||||
#include "option_set_registry.h"
|
||||
#include "target_registry.h"
|
||||
#include "locator.h"
|
||||
|
||||
@@ -32,6 +33,7 @@ public:
|
||||
|
||||
void add_target_definition(
|
||||
const std::string& target_name, const Argtype_registry& argtypes,
|
||||
Option_set_registry& option_sets,
|
||||
std::vector<Katom>::iterator begin, std::vector<Katom>::iterator end);
|
||||
void remove_target_definition(const std::string& target_name);
|
||||
|
||||
@@ -62,6 +64,7 @@ public:
|
||||
|
||||
std::string signature_text() const;
|
||||
std::string description_text() const;
|
||||
std::string option_set_text() const;
|
||||
std::string describe(int margin=0) const;
|
||||
|
||||
bool has_literal_param() const {
|
||||
@@ -97,8 +100,13 @@ std::string klammer_name_from_katom(const std::string& s, const Locator& loc);
|
||||
std::tuple<std::string,std::string>
|
||||
parse_name(const Target_registry& targets, const Katom& name_katom);
|
||||
|
||||
// Split a definition's katoms into its definition separator, parameters,
|
||||
// body and location. The target name is needed because the parameter list
|
||||
// is where option sets are used, and a set may be used only in a ".k"
|
||||
// declaration; option_sets is not const because a use is recorded on the set.
|
||||
std::tuple<Katom, Parameter_set, std::vector<Katom>, Locator>
|
||||
parse_definition_katoms(const std::string& klammer_name, const Argtype_registry& argtypes,
|
||||
parse_definition_katoms(const std::string& klammer_name, const std::string& target_name,
|
||||
const Argtype_registry& argtypes, Option_set_registry& option_sets,
|
||||
std::vector<Katom>::iterator& begin, std::vector<Katom>::iterator& end);
|
||||
|
||||
/*
|
||||
|
||||
@@ -12,7 +12,9 @@ bool Klammer_registry::has(std::string name, std::string target)
|
||||
}
|
||||
*/
|
||||
|
||||
void Klammer_registry::add(const Argtype_registry& argtypes, const Target_registry& targets, katom_iter begin, katom_iter end, katom_list& katoms)
|
||||
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);
|
||||
@@ -20,6 +22,13 @@ void Klammer_registry::add(const Argtype_registry& argtypes, const Target_regist
|
||||
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) {
|
||||
@@ -57,7 +66,8 @@ void Klammer_registry::add(const Argtype_registry& argtypes, const Target_regist
|
||||
}
|
||||
m_klammers[klammer_name].remove_target_definition(target_name);
|
||||
}
|
||||
m_klammers[klammer_name].add_target_definition(target_name, argtypes, begin + 1, end - 1);
|
||||
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);
|
||||
@@ -66,7 +76,8 @@ void Klammer_registry::add(const Argtype_registry& argtypes, const Target_regist
|
||||
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, begin + 1, end - 1);
|
||||
m_klammers[klammer_name].add_target_definition(
|
||||
provide_name, argtypes, option_sets, begin + 1, end - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ class Klammer_registry
|
||||
public:
|
||||
Klammer_registry() = default;
|
||||
void add(const Argtype_registry& argtypes, const Target_registry& targets,
|
||||
Option_set_registry& option_sets,
|
||||
std::vector<Katom>::iterator begin, std::vector<Katom>::iterator end, std::vector<Katom>& katoms);
|
||||
void rationalize(const Target_registry& targets);
|
||||
void check_klammer(const std::string& name, const std::string& target, const Locator& loc) const;
|
||||
|
||||
@@ -207,12 +207,27 @@ void Machine::process_cond_katoms(katom_list& katoms)
|
||||
}
|
||||
|
||||
|
||||
// Expand the constant klammers written in a definition's BODY. A constant is
|
||||
// expanded at definition time, which is what makes it a constant; the body is
|
||||
// where that is meaningful.
|
||||
//
|
||||
// The parameter list is deliberately excluded. A klammer application there
|
||||
// is an error (see expand_option_sets): parameters shared between klammers
|
||||
// are declared by an option set, whose ".o" declaration is resolved as the
|
||||
// parameter list is read. Expanding a constant into a parameter list used to
|
||||
// be the way to share parameters, and it silently destroyed the parameter
|
||||
// list of a ".k" declaration -- the spliced options AND the declared
|
||||
// positionals -- surfacing only as an argument error at the first
|
||||
// application, in the document rather than the declaration.
|
||||
void Machine::expand_constant_klammers(katom_list& katoms, const Katom& op, const Katom& cl)
|
||||
{
|
||||
auto [begin, end] = find_span_katoms(katoms, op, cl);
|
||||
restore_initial_type(begin + 1, end - 1);
|
||||
if (std::find_if(begin + 1, end - 1, begin_klammer_apply) == end - 1) return;
|
||||
for (const auto& [app_op, app_cl] : find_spans(begin + 1, end - 1, begin_apply, end_apply, false, "def-time")) {
|
||||
auto body_begin = std::find_if(
|
||||
begin + 1, end - 1, [](const Katom& k) { return is_deftype(k.m_type); });
|
||||
if (body_begin == end - 1) return;
|
||||
if (std::find_if(body_begin, end - 1, begin_klammer_apply) == end - 1) return;
|
||||
for (const auto& [app_op, app_cl] : find_spans(body_begin, end - 1, begin_apply, end_apply, false, "def-time")) {
|
||||
auto [app_begin, app_end] = find_span_katoms(katoms, app_op, app_cl);
|
||||
if (app_begin->m_type == katom_t::apply_begin) {
|
||||
std::string name = trim_char(app_begin->m_text, '@');
|
||||
@@ -506,14 +521,27 @@ void Machine::load_klammerset_files(const Klammerset& klammerset, katom_iter ins
|
||||
m_katoms.insert(insert_at, loaded.begin(), loaded.end());
|
||||
}
|
||||
|
||||
// Register one "@@...@@" definition. An ".o" target declares an option set
|
||||
// -- parameters shared by klammers -- and goes to its own registry: it
|
||||
// defines no klammer and produces no output for any target.
|
||||
void Machine::add_definition(katom_list& katoms, const Katom& op, const Katom& cl)
|
||||
{
|
||||
expand_constant_klammers(katoms, op, cl);
|
||||
auto [begin, end] = find_span_katoms(katoms, op, cl);
|
||||
auto [name, target] = parse_name(m_targets, *begin);
|
||||
if (target == Target_registry::optionset_name) {
|
||||
m_option_sets.add(m_argtypes, name, begin, end, katoms);
|
||||
} else {
|
||||
m_klammers.add(m_argtypes, m_targets, m_option_sets, begin, end, katoms);
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::extract_klammer_definitions(katom_list katoms)
|
||||
{
|
||||
fmsg() << katoms << "\n";
|
||||
(void)K::log(3);
|
||||
for (const auto& [op, cl] : find_spans(katoms, begin_klammer_def, end_klammer_def, true, command_name)) {
|
||||
expand_constant_klammers(katoms, op, cl);
|
||||
auto [begin, end] = find_span_katoms(katoms, op, cl);
|
||||
m_klammers.add(m_argtypes, m_targets, begin, end, katoms);
|
||||
add_definition(katoms, op, cl);
|
||||
}
|
||||
m_klammers.rationalize(m_targets);
|
||||
}
|
||||
@@ -522,9 +550,7 @@ void Machine::extract_klammer_definitions()
|
||||
{
|
||||
(void)K::log(3);
|
||||
for (const auto& [op, cl] : find_spans(m_katoms, begin_klammer_def, end_klammer_def, true, command_name)) {
|
||||
expand_constant_klammers(m_katoms, op, cl);
|
||||
auto [begin, end] = find_span_katoms(m_katoms, op, cl);
|
||||
m_klammers.add(m_argtypes, m_targets, begin, end, m_katoms);
|
||||
add_definition(m_katoms, op, cl);
|
||||
}
|
||||
m_klammers.rationalize(m_targets);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "katom_list.h"
|
||||
#include "klammer_registry.h"
|
||||
#include "klammerset_registry.h"
|
||||
#include "option_set_registry.h"
|
||||
#include "target_registry.h"
|
||||
#include "argtype_registry.h"
|
||||
#include "state.h"
|
||||
@@ -27,6 +28,7 @@ public:
|
||||
, m_targets(other.m_targets)
|
||||
, m_klammers(other.m_klammers)
|
||||
, m_klammersets(other.m_klammersets)
|
||||
, m_option_sets(other.m_option_sets)
|
||||
, m_result(other.m_result)
|
||||
{}
|
||||
|
||||
@@ -38,6 +40,7 @@ public:
|
||||
m_targets = other.m_targets;
|
||||
m_klammers = other.m_klammers;
|
||||
m_klammersets = other.m_klammersets;
|
||||
m_option_sets = other.m_option_sets;
|
||||
m_result = other.m_result;
|
||||
}
|
||||
return *this;
|
||||
@@ -68,6 +71,7 @@ public:
|
||||
bool klammers=true, bool eval=true, bool cond=true, bool read=true);
|
||||
|
||||
void expand_constant_klammers(katom_list& katoms, const Katom& op, const Katom& cl);
|
||||
void add_definition(katom_list& katoms, const Katom& op, const Katom& cl);
|
||||
void load_klammerset_files(const Klammerset& klammerset, katom_iter insert_at);
|
||||
void extract_machine_definitions();
|
||||
void extract_klammer_definitions();
|
||||
@@ -92,6 +96,7 @@ public:
|
||||
Target_registry m_targets {};
|
||||
Klammer_registry m_klammers {};
|
||||
Klammerset_registry m_klammersets {};
|
||||
Option_set_registry m_option_sets {};
|
||||
input_sources_t m_sources {};
|
||||
std::string m_result {};
|
||||
std::vector<Katom> m_katoms {};
|
||||
|
||||
64
mac/option_set.cpp
Normal file
64
mac/option_set.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "option_set.h"
|
||||
#include "argtype_registry.h"
|
||||
#include "util.h"
|
||||
|
||||
Option_set::Option_set(
|
||||
const std::string& name, const std::string& desc,
|
||||
const Parameter_set& parameters, const katom_lists& members, const Locator& loc)
|
||||
: m_name(name)
|
||||
, m_desc(desc)
|
||||
, m_parameters(parameters)
|
||||
, m_members(members)
|
||||
, m_loc(loc)
|
||||
{}
|
||||
|
||||
const Parameter* Option_set::find(const std::string& name) const
|
||||
{
|
||||
for (const Parameter& p : m_parameters.m_optional) {
|
||||
if (p.m_name == name) {
|
||||
return &p;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string Option_set::member_names() const
|
||||
{
|
||||
std::vector<std::string> names {};
|
||||
for (const Parameter& p : m_parameters.m_optional) {
|
||||
names.push_back(":" + p.m_name);
|
||||
}
|
||||
return join(names, " ");
|
||||
}
|
||||
|
||||
// One member per line: the name, its type, and its default -- the same three
|
||||
// things a klammer's signature shows, since that is what the using klammer
|
||||
// ends up declaring.
|
||||
std::string Option_set::describe(int margin) const
|
||||
{
|
||||
std::string tab(margin, ' ');
|
||||
std::stringstream ss {};
|
||||
ss << tab << "@@" << m_name << ".o\n";
|
||||
for (const Parameter& p : m_parameters.m_optional) {
|
||||
std::string line = ":" + p.m_name;
|
||||
if (p.m_argtype.m_name != default_argtype) {
|
||||
line += "." + p.m_argtype.m_name;
|
||||
}
|
||||
if (!p.m_default.empty()) {
|
||||
line += " " + p.m_default;
|
||||
if (p.m_default_from_type) {
|
||||
line += " (from the " + p.m_argtype.m_name + " argument type)";
|
||||
}
|
||||
}
|
||||
ss << tab << " " << line << "\n";
|
||||
}
|
||||
if (!m_desc.empty()) {
|
||||
ss << justify(m_desc, 80, margin + 2) << "\n";
|
||||
}
|
||||
if (!m_users.empty()) {
|
||||
ss << tab << " Used by: " << join(m_users, ", ") << "\n";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
70
mac/option_set.h
Normal file
70
mac/option_set.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "argument_set.h"
|
||||
#include "deftype.h"
|
||||
#include "locator.h"
|
||||
#include "util.h"
|
||||
|
||||
// An Option_set is the construct declared by the "o" target:
|
||||
//
|
||||
// @@name.o :opt.argtype default ... : <description> @@
|
||||
//
|
||||
// a named group of OPTIONAL parameters declared once and used by several
|
||||
// klammers, so that a writer learns one vocabulary (:hpos, :offset, the
|
||||
// caption parameters) instead of a spelling per klammer. The "o" target is
|
||||
// symmetric with "k": "k" declares a klammer's interface and documents it,
|
||||
// "o" declares an option interface and documents it. Informally a klammer
|
||||
// mix-in -- the term is from Flavors, where a mixin contributes slots
|
||||
// without necessarily contributing methods, which is the shape here: a set
|
||||
// contributes a DECLARATION, and the obligation to honor it stays with the
|
||||
// klammer that uses it.
|
||||
//
|
||||
// A set is used in the parameter list of a ".k" declaration and nowhere
|
||||
// else:
|
||||
//
|
||||
// @@code.k :filename @caption_args :caption_side top @ | text.literal :
|
||||
//
|
||||
// Names and types come from the set and cannot be changed at the use site; a
|
||||
// DEFAULT may be overridden there, because what varies between klammers is
|
||||
// only what silence means for that one klammer -- and kdesc shows a klammer's
|
||||
// effective defaults anyway. Everything a reader relies on is the same
|
||||
// wherever the set is used, which is the whole point of having one.
|
||||
//
|
||||
// The set keeps the katoms of each member as declared, because those are what
|
||||
// is spliced into the using declaration's parameter list. The parsed
|
||||
// Parameter_set beside them validates a use-site override at definition time
|
||||
// and describes the set for kdesc.
|
||||
class Option_set
|
||||
{
|
||||
public:
|
||||
Option_set() = default;
|
||||
Option_set(const std::string& name, const std::string& desc,
|
||||
const Parameter_set& parameters, const katom_lists& members,
|
||||
const Locator& loc);
|
||||
|
||||
// The member parameter named `name`, or nullptr if the set has none.
|
||||
const Parameter* find(const std::string& name) const;
|
||||
|
||||
// ":a :b :c" -- the members, for a diagnostic that has to show them.
|
||||
std::string member_names() const;
|
||||
|
||||
std::string describe(int margin = 2) const;
|
||||
|
||||
std::string m_name {};
|
||||
std::string m_desc {};
|
||||
Parameter_set m_parameters {};
|
||||
// One katom list per member, in declared order, each beginning with the
|
||||
// member's option-name katom.
|
||||
katom_lists m_members {};
|
||||
Locator m_loc {};
|
||||
// How this set was declared, for the redefinition policy: the same
|
||||
// transition table that governs klammer redefinition governs sets.
|
||||
defmode_t m_defmode { defmode_t::def_create };
|
||||
// The klammers whose ".k" declaration uses this set, in declaration
|
||||
// order. A set's users are as interesting as its members: they are the
|
||||
// klammers that promise to honor the vocabulary.
|
||||
std::vector<std::string> m_users {};
|
||||
};
|
||||
359
mac/option_set_registry.cpp
Normal file
359
mac/option_set_registry.cpp
Normal file
@@ -0,0 +1,359 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "option_set_registry.h"
|
||||
#include "argument_set.h"
|
||||
#include "error.h"
|
||||
#include "katom_list.h"
|
||||
#include "klammer.h"
|
||||
#include "log.h"
|
||||
#include "show.h"
|
||||
#include "target_registry.h"
|
||||
#include "util.h"
|
||||
|
||||
// --- Registration ---------------------------------------------------------
|
||||
|
||||
void Option_set_registry::add(
|
||||
const Argtype_registry& argtypes, const std::string& name,
|
||||
katom_iter begin, katom_iter end, katom_list& katoms)
|
||||
{
|
||||
(void)K::log(3, *begin, *(end - 1));
|
||||
restore_initial_type(begin, end);
|
||||
katom_iter definition_begin = begin + 1;
|
||||
katom_iter definition_end = end - 1;
|
||||
auto [deftype, parameters, body, loc] = parse_definition_katoms(
|
||||
name, Target_registry::optionset_name, argtypes, *this,
|
||||
definition_begin, definition_end);
|
||||
|
||||
if (deftype.m_initial_type == katom_t::klammer_instance) {
|
||||
throw Definition_error(
|
||||
"The option set " + q_(name) + " is declared with \"::\", which takes its "
|
||||
"parameters from a \".k\" declaration. An option set IS a declaration: "
|
||||
"it declares its parameters itself, after \":\".",
|
||||
begin->m_loc);
|
||||
}
|
||||
defmode_t incoming_mode = defmode_from_katom(deftype.m_initial_type);
|
||||
if (has(name)) {
|
||||
const Option_set& current = m_option_sets.at(name);
|
||||
const auto& result = defmode_transition(current.m_defmode, incoming_mode);
|
||||
// The transition table is the single statement of the redefinition
|
||||
// policy, for klammers and option sets alike; only the noun in its
|
||||
// messages is specific to what is being redefined.
|
||||
std::string message = string_replace(result.message, "Klammer NAME", "Option set NAME");
|
||||
message = string_replace(message, "klammer NAME", "option set NAME");
|
||||
message = string_replace(message, "NAME", q_(name + ".o"));
|
||||
message = string_replace(message, "AT", current.m_loc.desc());
|
||||
if (!result.replace) {
|
||||
if (message.empty()) { // a default silently superseded
|
||||
modify_type(katom_t::replaced, begin, end);
|
||||
auto next_iter = end;
|
||||
ignore_whitespace(next_iter, katoms);
|
||||
return;
|
||||
}
|
||||
throw Definition_error(message, begin->m_loc);
|
||||
}
|
||||
if (result.warn) {
|
||||
warning(message, begin->m_loc);
|
||||
}
|
||||
}
|
||||
|
||||
// Optional parameters only. A positional parameter is not writer-facing
|
||||
// -- the author never types its name -- so there is nothing for an option
|
||||
// set to standardize, and a set of them would be a klammer signature
|
||||
// rather than a shared vocabulary.
|
||||
if (!parameters.m_positional.empty() || !parameters.m_rest.empty()) {
|
||||
std::vector<std::string> names {};
|
||||
for (const auto& p : parameters.m_positional) names.push_back(p.m_name);
|
||||
for (const auto& p : parameters.m_rest) names.push_back(p.m_name);
|
||||
throw Definition_error(
|
||||
"The option set " + q_(name) + " declares the positional "
|
||||
+ plural("parameter", static_cast<int>(names.size())) + " "
|
||||
+ join(names, ", ") + ".\n"
|
||||
"An option set declares only optional parameters -- names written with "
|
||||
"a leading \":\".",
|
||||
begin->m_loc, false);
|
||||
}
|
||||
if (parameters.m_optional.empty()) {
|
||||
throw Definition_error(
|
||||
"The option set " + q_(name) + " declares no parameters.\n"
|
||||
"The form is: @@" + name + ".o :name.argtype default ... : <description> @@",
|
||||
begin->m_loc, false);
|
||||
}
|
||||
|
||||
// The members as written: these katoms are what is spliced into the
|
||||
// parameter list of a declaration that uses the set.
|
||||
auto [positional_katoms, member_katoms] =
|
||||
parameter_split(parameters.m_katoms.cbegin(), parameters.m_katoms.cend());
|
||||
(void)positional_katoms; // already rejected above
|
||||
// The two views of the members -- as katoms and as parsed parameters --
|
||||
// are used together when the set is spliced into a parameter list, and
|
||||
// are paired by position.
|
||||
if (member_katoms.size() != parameters.m_optional.size()) {
|
||||
throw Internal_error(
|
||||
"The option set " + q_(name) + " parsed " + std::to_string(parameters.m_optional.size())
|
||||
+ " parameters from " + std::to_string(member_katoms.size()) + " declarations",
|
||||
begin->m_loc);
|
||||
}
|
||||
|
||||
Option_set option_set(name, to_string(body, true), parameters, member_katoms, begin->m_loc);
|
||||
option_set.m_defmode = incoming_mode;
|
||||
if (has(name)) {
|
||||
// A redefinition keeps the users recorded so far: they used the name,
|
||||
// and the name is what they are bound to.
|
||||
option_set.m_users = m_option_sets.at(name).m_users;
|
||||
} else {
|
||||
m_names.push_back(name);
|
||||
}
|
||||
m_option_sets[name] = option_set;
|
||||
|
||||
modify_type(katom_t::replaced, begin, end);
|
||||
auto next_iter = end;
|
||||
ignore_whitespace(next_iter, katoms);
|
||||
}
|
||||
|
||||
bool Option_set_registry::has(const std::string& name) const
|
||||
{
|
||||
return m_option_sets.count(name) > 0;
|
||||
}
|
||||
|
||||
const Option_set& Option_set_registry::get(const std::string& name, const Locator& loc) const
|
||||
{
|
||||
auto it = m_option_sets.find(name);
|
||||
if (it == m_option_sets.end()) {
|
||||
throw Definition_error("The option set " + q_(name) + " is not declared", loc);
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void Option_set_registry::add_user(const std::string& set_name, const std::string& klammer_name)
|
||||
{
|
||||
auto it = m_option_sets.find(set_name);
|
||||
if (it != m_option_sets.end() && !is_in(klammer_name, it->second.m_users)) {
|
||||
it->second.m_users.push_back(klammer_name);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Option_set_registry::available() const
|
||||
{
|
||||
return m_names.empty() ? "(none are declared)" : join(m_names, ", ");
|
||||
}
|
||||
|
||||
std::string Option_set_registry::describe(int margin) const
|
||||
{
|
||||
std::string result {};
|
||||
for (const auto& name : m_names) {
|
||||
result += m_option_sets.at(name).describe(margin) + "\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// --- Use in a parameter list ----------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
// The end of the application opening at `begin`: one past its closing
|
||||
// delimiter. Depth is counted over klammer applications only, which is all
|
||||
// that can nest inside an option-set use.
|
||||
katom_iter application_end(katom_iter begin, katom_iter end)
|
||||
{
|
||||
int depth = 0;
|
||||
for (auto k = begin; k != end; ++k) {
|
||||
if (begin_klammer_apply(*k)) {
|
||||
++depth;
|
||||
} else if (end_klammer_apply(*k)) {
|
||||
if (--depth == 0) return k + 1;
|
||||
}
|
||||
}
|
||||
throw Definition_error(
|
||||
"The klammer " + q_(trim_char(begin->m_text, '@')) +
|
||||
" in a parameter list is not closed", begin->m_loc);
|
||||
}
|
||||
|
||||
// The parameter name in an option-name katom, which carries the type and any
|
||||
// target as written: ":number.bool" declares the parameter named "number".
|
||||
std::string option_name(const Katom& katom)
|
||||
{
|
||||
std::string name(katom.m_text, 1);
|
||||
auto type = name.find('.');
|
||||
return type == std::string::npos ? name : name.substr(0, type);
|
||||
}
|
||||
|
||||
// How a definition is named in a diagnostic: a general definition has no
|
||||
// target to name, and the pseudo-targets read better with their suffix.
|
||||
std::string definition_name(const std::string& klammer_name, const std::string& target_name)
|
||||
{
|
||||
return target_name == Target_registry::general_name
|
||||
? q_(klammer_name) : q_(klammer_name + "." + target_name);
|
||||
}
|
||||
|
||||
// A klammer application in a parameter list is never legal. Which of the two
|
||||
// ways it is wrong decides what the writer has to do about it, so the message
|
||||
// says which.
|
||||
[[noreturn]] void reject_application(
|
||||
const std::string& name, const std::string& klammer_name,
|
||||
const std::string& target_name, const Option_set_registry& option_sets,
|
||||
const Locator& loc)
|
||||
{
|
||||
std::stringstream ss {};
|
||||
if (option_sets.has(name) && target_name == Target_registry::optionset_name) {
|
||||
ss << "The option set " << q_(name) << " is used in the declaration of the option "
|
||||
<< "set " << q_(klammer_name) << ".\n"
|
||||
<< "An option set is used only in the parameter list of a \".k\" declaration, "
|
||||
<< "so a set does not include another set: a klammer that needs two "
|
||||
<< "vocabularies names two sets, and each set stays a vocabulary that can "
|
||||
<< "be learned whole.";
|
||||
} else if (option_sets.has(name)) {
|
||||
ss << "The option set " << q_(name) << " is used in the parameter list of "
|
||||
<< definition_name(klammer_name, target_name) << ".\n"
|
||||
<< "An option set may be used only in the parameter list of a \".k\" "
|
||||
<< "declaration, which is where a klammer's interface is declared once "
|
||||
<< "for all of its targets. Declare "
|
||||
<< q_(klammer_name + ".k") << " and give each target's definition as "
|
||||
<< "an instance (\"::\"), which inherits the declared parameters.";
|
||||
} else {
|
||||
ss << "The klammer " << q_(name) << " is applied in the parameter list of "
|
||||
<< definition_name(klammer_name, target_name) << ".\n"
|
||||
<< "A klammer application in a parameter list is not allowed: it is "
|
||||
<< "resolved after the parameters are parsed, so the parameter list it "
|
||||
<< "was meant to contribute is not there when the list is read. An "
|
||||
<< "option set, declared with a \".o\" target, is how parameters are "
|
||||
<< "shared between klammers. Declared option sets: "
|
||||
<< option_sets.available() << ".";
|
||||
}
|
||||
throw Definition_error(ss.str(), loc, false);
|
||||
}
|
||||
|
||||
// The default written for each member at the use site:
|
||||
// @caption_args :number false :caption_side top @
|
||||
// Only defaults may be given -- names and types belong to the set.
|
||||
std::map<std::string, std::string> use_site_defaults(
|
||||
const Option_set& option_set, katom_iter begin, katom_iter end)
|
||||
{
|
||||
auto [positional, optional, rest] = argument_split(begin + 1, end - 1, 0);
|
||||
if (!positional.empty() || active(rest)) {
|
||||
throw Definition_error(
|
||||
"The use of the option set " + q_(option_set.m_name) +
|
||||
" gives a value that is not an option.\n"
|
||||
"A set's names and types are fixed where the set is declared; only a "
|
||||
"default may be given where it is used, written as \":name value\".",
|
||||
begin->m_loc, false);
|
||||
}
|
||||
std::map<std::string, std::string> defaults {};
|
||||
for (const auto& option : optional) {
|
||||
std::string name = option_name(option[0]);
|
||||
if (name.size() + 1 != option[0].m_text.size()) {
|
||||
throw Definition_error(
|
||||
"The use of the option set " + q_(option_set.m_name) + " gives a type for \":"
|
||||
+ name + "\".\n"
|
||||
"A set's names and types are declared where the set is; only a default "
|
||||
"may be given where it is used.",
|
||||
option[0].m_loc, false);
|
||||
}
|
||||
const Parameter* member = option_set.find(name);
|
||||
if (member == nullptr) {
|
||||
throw Definition_error(
|
||||
"The option set " + q_(option_set.m_name) + " has no parameter \":"
|
||||
+ name + "\".\n It declares: " + option_set.member_names(),
|
||||
option[0].m_loc, false);
|
||||
}
|
||||
if (defaults.count(name) > 0) {
|
||||
throw Definition_error(
|
||||
"A default for \":" + name + "\" is given more than once in the use of "
|
||||
"the option set " + q_(option_set.m_name),
|
||||
option[0].m_loc);
|
||||
}
|
||||
std::string value = trim(to_string(katom_list(option.begin() + 1, option.end())));
|
||||
// A bare option name means the argument type's :alone value, exactly
|
||||
// as it does where an argument is written.
|
||||
if (value.empty() && !member->m_argtype.m_alone.empty()) {
|
||||
value = member->m_argtype.m_alone;
|
||||
}
|
||||
// Validated here, at definition time: an invalid default must not
|
||||
// wait for an application that happens not to supply the argument.
|
||||
Parameter_set::validate(*member, value, option[0].m_loc);
|
||||
defaults[name] = value;
|
||||
}
|
||||
return defaults;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
option_set_uses_t expand_option_sets(
|
||||
katom_list& parameters, const std::string& klammer_name,
|
||||
const std::string& target_name, Option_set_registry& option_sets)
|
||||
{
|
||||
option_set_uses_t uses {};
|
||||
// Every parameter name in the list, and where it came from, so that a
|
||||
// collision between two sets (or between a set and a name written here)
|
||||
// can name both origins rather than just the name.
|
||||
std::map<std::string, std::string> origin {};
|
||||
auto declare = [&](const std::string& name, const std::string& from, const Locator& loc) {
|
||||
auto previous = origin.find(name);
|
||||
if (previous != origin.end()) {
|
||||
throw Definition_error(
|
||||
"The parameter \":" + name + "\" of " + q_(klammer_name) +
|
||||
" is declared twice:\n " + previous->second + "\n " + from,
|
||||
loc, false);
|
||||
}
|
||||
origin[name] = from;
|
||||
};
|
||||
|
||||
katom_list result {};
|
||||
result.reserve(parameters.size());
|
||||
for (auto k = parameters.begin(); k != parameters.end(); ) {
|
||||
if (!begin_klammer_apply(*k)) {
|
||||
if (k->m_type == katom_t::option_name) {
|
||||
declare(option_name(*k), "declared in the parameter list", k->m_loc);
|
||||
}
|
||||
result.push_back(*k);
|
||||
++k;
|
||||
continue;
|
||||
}
|
||||
std::string name = trim_char(k->m_text, '@');
|
||||
auto end = application_end(k, parameters.end());
|
||||
if (target_name != Target_registry::declare_name || !option_sets.has(name)) {
|
||||
reject_application(name, klammer_name, target_name, option_sets, k->m_loc);
|
||||
}
|
||||
const Option_set& option_set = option_sets.get(name, k->m_loc);
|
||||
std::map<std::string, std::string> defaults = use_site_defaults(option_set, k, end);
|
||||
|
||||
// m_members and m_parameters.m_optional are the same members in the
|
||||
// same order -- one is the katoms as declared, the other what they
|
||||
// parsed to -- so the parameter's name is taken from the parsed side
|
||||
// rather than re-derived from the katom text (":number.bool").
|
||||
for (size_t i = 0; i < option_set.m_members.size(); ++i) {
|
||||
const katom_list& member = option_set.m_members[i];
|
||||
const std::string& member_name = option_set.m_parameters.m_optional[i].m_name;
|
||||
declare(member_name, "from the option set " + q_(name) + ", "
|
||||
+ option_set.m_loc.desc(), k->m_loc);
|
||||
if (!result.empty()) {
|
||||
result.push_back(Katom(" ", katom_t::space, member[0].m_loc));
|
||||
}
|
||||
// The name katom comes from the set, so the parameter's location
|
||||
// is where it is declared; an overriding default comes from here.
|
||||
result.push_back(member[0]);
|
||||
auto given = defaults.find(member_name);
|
||||
if (given == defaults.end()) {
|
||||
result.insert(result.end(), member.begin() + 1, member.end());
|
||||
} else if (!given->second.empty()) {
|
||||
result.push_back(Katom(" ", katom_t::space, k->m_loc));
|
||||
result.push_back(Katom(given->second, katom_t::text, k->m_loc));
|
||||
}
|
||||
uses[member_name] = {name, given != defaults.end()};
|
||||
}
|
||||
option_sets.add_user(name, klammer_name);
|
||||
k = end;
|
||||
}
|
||||
parameters = result;
|
||||
return uses;
|
||||
}
|
||||
|
||||
void stamp_option_set_uses(Parameter_set& parameters, const option_set_uses_t& uses)
|
||||
{
|
||||
if (uses.empty()) return;
|
||||
for (Parameter& parameter : parameters.m_optional) {
|
||||
auto use = uses.find(parameter.m_name);
|
||||
if (use == uses.end()) continue;
|
||||
parameter.m_option_set = use->second.m_set;
|
||||
parameter.m_default_overridden = use->second.m_overridden;
|
||||
}
|
||||
}
|
||||
60
mac/option_set_registry.h
Normal file
60
mac/option_set_registry.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "argtype_registry.h"
|
||||
#include "katom.h"
|
||||
#include "option_set.h"
|
||||
|
||||
class Option_set_registry
|
||||
{
|
||||
public:
|
||||
// Parse and register an "@@name.o <parameters> : <description> @@"
|
||||
// declaration. The name is passed in because the caller has already
|
||||
// split it from its ".o" target suffix in order to route here.
|
||||
void add(const Argtype_registry& argtypes, const std::string& name,
|
||||
katom_iter begin, katom_iter end, katom_list& katoms);
|
||||
|
||||
bool has(const std::string& name) const;
|
||||
const Option_set& get(const std::string& name, const Locator& loc) const;
|
||||
void add_user(const std::string& set_name, const std::string& klammer_name);
|
||||
|
||||
std::string describe(int margin = 2) const;
|
||||
// "a, b, c" -- the sets that exist, for a diagnostic about one that does not.
|
||||
std::string available() const;
|
||||
|
||||
std::map<std::string, Option_set> m_option_sets {};
|
||||
std::vector<std::string> m_names {};
|
||||
};
|
||||
|
||||
// What one parameter inherited from an option set: which set it came from,
|
||||
// and whether its default was overridden where the set was used. Default
|
||||
// resolution has three levels -- argument type, option set, use site -- and
|
||||
// kdesc says which one a klammer's effective default came from, so the level
|
||||
// has to be recorded rather than inferred from the value.
|
||||
struct Option_set_use
|
||||
{
|
||||
std::string m_set {};
|
||||
bool m_overridden {};
|
||||
};
|
||||
|
||||
// parameter name -> where it came from
|
||||
using option_set_uses_t = std::map<std::string, Option_set_use>;
|
||||
|
||||
// Replace every option-set use in a parameter list with the parameters it
|
||||
// stands for. Any other klammer application in a parameter list is an
|
||||
// error: an option set is the only construct that may put parameters there,
|
||||
// and only in a ".k" declaration.
|
||||
//
|
||||
// `parameters` is modified in place. Sets are recorded as used by
|
||||
// `klammer_name`, so `option_sets` is not const.
|
||||
option_set_uses_t expand_option_sets(
|
||||
katom_list& parameters, const std::string& klammer_name,
|
||||
const std::string& target_name, Option_set_registry& option_sets);
|
||||
|
||||
// Record on each parameter that came from a set which set it came from and
|
||||
// whether its default was overridden. Called after the expanded parameter
|
||||
// list has been parsed.
|
||||
void stamp_option_set_uses(Parameter_set& parameters, const option_set_uses_t& uses);
|
||||
@@ -570,6 +570,7 @@ std::ostream& operator<<(std::ostream& os, const Machine& m)
|
||||
std::string state_desc = m.m_state.describe(false, 3);
|
||||
std::string targets_desc = m.m_targets.describe(4);
|
||||
std::string klammersets_desc = m.m_klammersets.describe(4);
|
||||
std::string option_sets_desc = m.m_option_sets.describe(4);
|
||||
std::string klammer_desc = m.m_klammers.describe(2);
|
||||
std::string source_desc = describe_sources(m);
|
||||
|
||||
@@ -578,6 +579,7 @@ std::ostream& operator<<(std::ostream& os, const Machine& m)
|
||||
<< label("Argtypes", m.m_argtypes.m_names.size()) << argtypes_desc << "\n"
|
||||
<< label("Targets", m.m_targets.m_names.size()) << targets_desc << "\n"
|
||||
<< label("Klammersets", m.m_klammersets.m_symbols.size()) << klammersets_desc << "\n"
|
||||
<< label("Option sets", m.m_option_sets.m_names.size()) << option_sets_desc << "\n"
|
||||
<< label("Klammers", m.m_klammers.m_klammers.size()) << klammer_desc << "\n"
|
||||
<< label("State", m.m_state.m_frames.size()) << state_desc;
|
||||
return os;
|
||||
|
||||
@@ -11,14 +11,17 @@
|
||||
|
||||
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 | transforms.rest"))
|
||||
{
|
||||
Target k(declare_name, "Description of parameters and klammer result", Locator());
|
||||
Target general(general_name, "General target, used when a target is not specified", Locator());
|
||||
Target option_set(optionset_name, "Declaration of an option set: parameters shared by klammers", Locator());
|
||||
add(k);
|
||||
add(general);
|
||||
add(option_set);
|
||||
}
|
||||
|
||||
void Target_registry::add(Target target)
|
||||
@@ -104,14 +107,19 @@ std::vector<std::string> Target_registry::user_defined() const
|
||||
{
|
||||
return collect_if(
|
||||
m_names, [](const auto& name) {
|
||||
return name != Target_registry::declare_name && name != Target_registry::general_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<std::string> Target_registry::applicable() const
|
||||
{
|
||||
return collect_if(
|
||||
m_names, [](const auto& name) {
|
||||
return name != Target_registry::declare_name; });
|
||||
return name != Target_registry::declare_name
|
||||
&& name != Target_registry::optionset_name; });
|
||||
}
|
||||
|
||||
std::string Target_registry::describe(int margin, bool long_format) const
|
||||
|
||||
@@ -11,6 +11,10 @@ class Target_registry
|
||||
public:
|
||||
static std::string declare_name;
|
||||
static std::string general_name;
|
||||
// The pseudo-target of an option set declaration (@@name.o), reserved in
|
||||
// the target namespace exactly as "k" is: both declare an interface and
|
||||
// document it, and neither produces output for any target.
|
||||
static std::string optionset_name;
|
||||
|
||||
Target_registry();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user