A snapshot of the development tree. The substantial changes since the last one:
COMMAND OUTPUT POLICY. The three commands display text in exactly three cases,
and each owns a stream: LOGGING under "-v" greater than 0 and an ERROR before
termination go to STDERR; OUTPUT THE USER ASKED FOR goes to STDOUT. For ktext
that output is a document, so "ktext doc.kt -d | ..." is now safe -- logging
used to share the stream and land inside the document. A bare command prints
its usage and succeeds rather than failing. Colour is emitted only to a
terminal, per stream, and NO_COLOR is honoured.
"-v 1" reports every decision whose outcome you could not have read off your own
input: the klammerset that was loaded and from which file, a font's directory, a
":files" name's file, how "-o" was expanded. Higher levels are the trace.
The commands no longer warn and continue: an anomaly is an error, described with
its location. Two exceptions remain, each for a stated reason -- a condition
that is expected and temporary by design, and a judgment that is a heuristic
rather than exact.
@cond IS NOW A TRUE SPECIAL FORM, resolved at APPLICATION time rather than when
the file is read. Two consequences for a writer:
* a state variable reaches the predicate. "@@@state Flag :value true @@@
@cond *Flag* | T | F @" renders "T"; it used to see the literal "*Flag*" and
silently take the false branch. The document now behaves like a klammer
body, whose arguments are bound before its conditionals are decided.
* nothing in a discarded branch happens -- it is not read, not evaluated, not
expanded. An @eval in the branch not taken used to run anyway.
Its predicate relation is total and strict: true, True, 1; false, False, 0, and
empty; anything else is an error at the @cond rather than silently false.
@eval REACHING OUTSIDE. ":shell" and ":haskell" now keep the command's standard
error out of the document (it appears under "-v 1") and treat a nonzero exit as
an error naming what the command reported. A command that exits nonzero on
purpose -- "grep" finding no match -- says so with "|| true".
KLAMMER SETS. Several combine: "--klammersets a b c" loads all three in the
order given, sharing one namespace, with the definition modes deciding
collisions. "none" means none and may not be combined with other symbols. A
klammerset with symbol X is declared in a file X/X.k, which is what lets two
sets require the same third set without loading it twice.
TESTS. Four new suites: the kdiag command's interface, the @eval primitive's
contract with the outside world, and verbosity at both tiers. Three suites
that could not run on macOS at all now do.
Assembled from dev commit 6c8ee6c22fca.
142 lines
6.0 KiB
C++
142 lines
6.0 KiB
C++
#pragma once
|
|
|
|
#include <regex>
|
|
|
|
#include "deftype.h"
|
|
#include "argument_set.h"
|
|
#include "option_set_registry.h"
|
|
#include "target_registry.h"
|
|
#include "locator.h"
|
|
|
|
class Klammer
|
|
{
|
|
public:
|
|
Klammer() = default;
|
|
Klammer(const std::string& name)
|
|
: m_name(name)
|
|
{};
|
|
|
|
using variable_map_t = std::map<std::string, std::vector<int>>;
|
|
using target_variable_map_t = std::map<std::string, variable_map_t>;
|
|
// <name>[.<target>[,<target>...]] -- the target part is a comma-separated
|
|
// list so that one body can serve several targets (see parse_name).
|
|
static std::regex name_re; // = std::regex(R"((\w+)(?:\.(\w+(?:,\w+)*))?)");
|
|
|
|
struct components {
|
|
std::string target;
|
|
katom_t deftype;
|
|
Parameter_set parameters;
|
|
std::vector<Katom> body;
|
|
variable_map_t varmap;
|
|
Locator loc;
|
|
};
|
|
|
|
// target-name -> [variable -> index]
|
|
|
|
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);
|
|
|
|
auto target_defs(const std::vector<std::string>& target_names);
|
|
auto instance_defs();
|
|
void disallow_instances(); //Klammer::components declaration);
|
|
bool copy_to_instances(const Target_registry& targets);
|
|
void copy_components(
|
|
const Parameter_set& parameters, const std::vector<Klammer::components>& cs,
|
|
const Target_registry& targets);
|
|
void check_for_multiple_general_klammers();
|
|
void check_for_declaration_and_definitions();
|
|
void copy_general_klammer_to_undefined(const Target_registry& targets);
|
|
void no_declarations(const Target_registry& targets);
|
|
void one_declaration(const Target_registry& targets, const Klammer::components& declare);
|
|
void many_declarations(const std::vector<Klammer::components>& declares);
|
|
void rationalize(const Target_registry& targets);
|
|
/*
|
|
void add_description(const std::string& desc, Katom definition_type);
|
|
|
|
auto user_defs();
|
|
auto klammer_defines_parameters();
|
|
auto klammer_uses_parameters();
|
|
void check_for_target_errors(Target_registry targets);
|
|
bool explicit_parameters_match();
|
|
void copy_components(Klammer::components cs, Target_registry targets);
|
|
*/
|
|
|
|
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 {
|
|
for (const auto& p : m_parameters.m_positional)
|
|
if (p.m_argtype.m_name == "literal") return true;
|
|
for (const auto& p : m_parameters.m_rest)
|
|
if (p.m_argtype.m_name == "literal") return true;
|
|
return false;
|
|
}
|
|
// True when at least one definition of this klammer came from a file
|
|
// OUTSIDE the given list -- that is, the input defined it rather than a
|
|
// klammerset supplying it. "At least one" is deliberate: a klammer the
|
|
// input REDEFINES (":::") came originally from the klammerset, and a
|
|
// reader of that input needs to know it was changed.
|
|
bool defined_outside(const strings_t& files) const;
|
|
strings_t get_target_names() const;
|
|
strings_t get_locations();
|
|
|
|
// Initial instantiation:
|
|
std::string m_name {};
|
|
|
|
// Collection: target, deftype, parameters, body, locator
|
|
std::vector<Klammer::components> m_defs {};
|
|
|
|
// After rationalization:
|
|
Parameter_set m_parameters {};
|
|
std::map<std::string, std::vector<Katom>> m_body {};
|
|
std::string m_desc {};
|
|
std::map<std::string, Locator> m_defloc {}; // target -> Locator
|
|
std::map<std::string, defmode_t> m_defmode {}; // target -> defmode
|
|
target_variable_map_t m_varmap {}; // target -> map: variable -> index
|
|
// target -> true if this target's body came from a general ("*") definition
|
|
// (writer content, subject to target escaping) vs a target-specific one.
|
|
std::map<std::string, bool> m_body_generic {};
|
|
// True when a definition wrote the general target out as ".*" instead of
|
|
// omitting the suffix. Identical to the engine; to a reader it is the
|
|
// difference between "I did not say" and "I say: every target, whatever
|
|
// they turn out to be". Only the second is a claim the coverage report
|
|
// can repeat.
|
|
bool m_general_declared { false };
|
|
};
|
|
|
|
std::string klammer_name_from_katom(const std::string& s, const Locator& loc);
|
|
|
|
// Split "@@<name>[.<target>[,<target>...]]" into the klammer name and the
|
|
// targets the definition is for. Always at least one name: an absent suffix
|
|
// is the general target. A comma list is surface syntax only -- the caller
|
|
// registers one definition per target, so nothing downstream of registration
|
|
// knows a list was written.
|
|
// The third result is true when the general target was written out as "*"
|
|
// rather than left off. Both mean the same to the engine; they mean
|
|
// different things to a reader and to the coverage analysis -- see
|
|
// Klammer::m_general_declared.
|
|
std::tuple<std::string,strings_t,bool>
|
|
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 std::string& target_name,
|
|
const Argtype_registry& argtypes, Option_set_registry& option_sets,
|
|
std::vector<Katom>::iterator& begin, std::vector<Katom>::iterator& end);
|
|
|
|
/*
|
|
klammer_definition_args parse_klammer_definition_katoms(
|
|
katom_list& katoms, Argtype_registry& argtypes);
|
|
|
|
void check_for_undefined_arguments(
|
|
std::string name, Parameters parameters, katom_list body_katoms, Locator loc);
|
|
*/
|