Target coverage: a klammer states the targets it serves
kdesc gains --coverage, which reports for every klammer the set of targets it
can render to, and — the point of it — which klammers' coverage cannot be
derived and must therefore be declared. Three rules: coverage is DERIVED
where the definitions determine it (a general body of klammer calls covers
the intersection of what those klammers cover, by a greatest fixpoint after
loading), DECLARED where the engine cannot interpret what decides it (an
@eval body, whose targets are undecidable), and UNKNOWN where nothing is
written — which never means "deliberately unavailable".
Two new spellings in a definition's name. A comma-separated target list,
"@@table.html,tex :: ...", gives one body several targets; it is surface
syntax, expanded at registration, and each member goes through the
redefinition rules on its own. And "@@date.* :: ..." writes the general
target out, asserting that the klammer works for EVERY target including ones
not yet defined — a stronger claim than a list of the targets defined today,
and the one target declaration that could be mechanically falsified.
The Standard Klammer Set was swept accordingly: it now has no general
definitions at all, every klammer names the targets it serves, six use ".*",
and tex and pdf are at zero undecided.
kdesc's flags are reorganised on two rules: a flag reached for often gets a
single letter (-k klammers, -t targets, -c characters, -i input), a more
specialised topic a multi-letter name (--argtypes, --katoms, --rewrite,
--optionsets, --coverage, --klammerset, --font); and -v says how much to show
about PROCESSING, never what the RESULT contains — so the katom regex column
is "--katoms full" and the coverage detail "--coverage all". NOTE: "-k" now
lists klammers (optionally filtered by a name/description search); the katom
table moved to "--katoms".
Fixes carried along: an option written with no value crashed the command with
SIGSEGV instead of reporting the mistake; two required positional arguments
never parsed; kdesc and kdiag printed an error and exited 0; and definition
diagnostics counted registrations rather than what was written, so one line
could be reported as two definitions and then printed twice.
Four new test suites: target_list, coverage, command_option, kdesc.
(from dev 46f54080bd9a)
2026-08-12 17:20:23 +02:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <iosfwd>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
An output policy for the three commands, and @cond as a true special form
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.
2026-08-16 01:37:59 +02:00
|
|
|
#include "util.h" // strings_t
|
|
|
|
|
|
Target coverage: a klammer states the targets it serves
kdesc gains --coverage, which reports for every klammer the set of targets it
can render to, and — the point of it — which klammers' coverage cannot be
derived and must therefore be declared. Three rules: coverage is DERIVED
where the definitions determine it (a general body of klammer calls covers
the intersection of what those klammers cover, by a greatest fixpoint after
loading), DECLARED where the engine cannot interpret what decides it (an
@eval body, whose targets are undecidable), and UNKNOWN where nothing is
written — which never means "deliberately unavailable".
Two new spellings in a definition's name. A comma-separated target list,
"@@table.html,tex :: ...", gives one body several targets; it is surface
syntax, expanded at registration, and each member goes through the
redefinition rules on its own. And "@@date.* :: ..." writes the general
target out, asserting that the klammer works for EVERY target including ones
not yet defined — a stronger claim than a list of the targets defined today,
and the one target declaration that could be mechanically falsified.
The Standard Klammer Set was swept accordingly: it now has no general
definitions at all, every klammer names the targets it serves, six use ".*",
and tex and pdf are at zero undecided.
kdesc's flags are reorganised on two rules: a flag reached for often gets a
single letter (-k klammers, -t targets, -c characters, -i input), a more
specialised topic a multi-letter name (--argtypes, --katoms, --rewrite,
--optionsets, --coverage, --klammerset, --font); and -v says how much to show
about PROCESSING, never what the RESULT contains — so the katom regex column
is "--katoms full" and the coverage detail "--coverage all". NOTE: "-k" now
lists klammers (optionally filtered by a name/description search); the katom
table moved to "--katoms".
Fixes carried along: an option written with no value crashed the command with
SIGSEGV instead of reporting the mistake; two required positional arguments
never parsed; kdesc and kdiag printed an error and exited 0; and definition
diagnostics counted registrations rather than what was written, so one line
could be reported as two definitions and then printed twice.
Four new test suites: target_list, coverage, command_option, kdesc.
(from dev 46f54080bd9a)
2026-08-12 17:20:23 +02:00
|
|
|
class Machine;
|
|
|
|
|
|
|
|
|
|
// Target coverage: which targets a klammer can actually render to.
|
|
|
|
|
//
|
|
|
|
|
// Coverage is a FACT about a klammer, distinct from a klammer set's claim
|
|
|
|
|
// about what it supports and from what happens when a document meets a
|
|
|
|
|
// target. This module computes the fact, and computes only what can be
|
|
|
|
|
// computed -- it changes nothing about how the Machine behaves. See
|
|
|
|
|
// notes/target_coverage.md for why the fact has to come first.
|
|
|
|
|
//
|
|
|
|
|
// Three rules, and the whole of the analysis is deciding which one applies:
|
|
|
|
|
//
|
|
|
|
|
// DERIVED where coverage is structurally determined. A general
|
|
|
|
|
// definition (no target suffix) whose body is text and nothing
|
|
|
|
|
// else covers every target. One whose body calls other klammers
|
|
|
|
|
// covers the INTERSECTION of what those klammers cover -- a
|
|
|
|
|
// klammer can only render where everything it is made of renders.
|
|
|
|
|
//
|
|
|
|
|
// DECLARED where coverage depends on something the engine cannot
|
|
|
|
|
// interpret. A general body holding an @eval is the main case:
|
|
|
|
|
// deciding which targets a Python function answers for is
|
|
|
|
|
// undecidable, so the targets have to be written down (which is
|
|
|
|
|
// what the comma-separated target list is for). A general body
|
|
|
|
|
// holding a ^'...'^ literal span is the same problem wearing
|
|
|
|
|
// different clothes -- the span exists precisely to carry raw
|
|
|
|
|
// target markup past the escaping pass, so a body containing one
|
|
|
|
|
// is target-specific with nothing for the intersection rule to
|
|
|
|
|
// see. @read is included: its content is not known statically.
|
|
|
|
|
//
|
|
|
|
|
// DECLARED-ALL a definition written ".*" asserts that the klammer works for
|
|
|
|
|
// EVERY target, including targets that do not exist yet. A list
|
|
|
|
|
// of the targets defined today cannot say that. It is the one
|
|
|
|
|
// target declaration a machine could later falsify: a ".*"
|
|
|
|
|
// klammer whose implementation branches per target is
|
|
|
|
|
// contradicting itself, which is a structural property.
|
|
|
|
|
//
|
|
|
|
|
// UNKNOWN where neither applies -- no definition at all. Absence means
|
|
|
|
|
// "not decided yet", never "deliberately unavailable": the SKS is
|
|
|
|
|
// incomplete on schedule rather than by design, so nothing may
|
|
|
|
|
// read a missing definition as a statement of intent.
|
|
|
|
|
//
|
|
|
|
|
// The intersection is computed as a GREATEST FIXPOINT rather than by
|
|
|
|
|
// recursion: general klammers may call each other, and a cycle would not
|
|
|
|
|
// terminate. Every general klammer starts at "all targets" and the rule is
|
|
|
|
|
// applied until nothing shrinks, which terminates because the sets only ever
|
|
|
|
|
// lose members. The pass runs over the whole registry AFTER loading, not at
|
|
|
|
|
// definition time -- definitions load in file order, so a body may call a
|
|
|
|
|
// klammer defined later.
|
|
|
|
|
enum class coverage_t {
|
|
|
|
|
all_declared, // written ".*": every target, including ones not yet defined
|
|
|
|
|
all, // general body, no klammer calls: every target
|
|
|
|
|
derived, // general body of klammer calls: their intersection
|
|
|
|
|
declared, // written per target, no general body to derive from
|
|
|
|
|
undecidable, // general body holding @eval, @read, or a literal span
|
|
|
|
|
none, // declared (.k) but never defined
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct Klammer_coverage
|
|
|
|
|
{
|
|
|
|
|
std::string m_name {};
|
|
|
|
|
coverage_t m_kind { coverage_t::none };
|
|
|
|
|
// The targets this klammer can render to, as computed.
|
|
|
|
|
std::vector<std::string> m_targets {};
|
|
|
|
|
// Targets the Machine currently offers it for. These differ exactly
|
|
|
|
|
// where a general body is copied to targets it cannot really serve, which
|
|
|
|
|
// is the hazard the report exists to surface.
|
|
|
|
|
std::vector<std::string> m_effective {};
|
|
|
|
|
// Targets named in a written definition (the comma-list, or one per
|
|
|
|
|
// definition), empty for a purely general klammer.
|
|
|
|
|
std::vector<std::string> m_written {};
|
|
|
|
|
// For `derived`: the klammers the body calls. For `undecidable`: why.
|
|
|
|
|
std::vector<std::string> m_from {};
|
|
|
|
|
std::string m_reason {};
|
|
|
|
|
// Whether a ".k" declaration exists. A klammer without one still works
|
|
|
|
|
// -- its parameters can be declared on the definition itself -- but it
|
|
|
|
|
// has no DESCRIPTION, so kdesc can say nothing about what it does and
|
|
|
|
|
// "kdesc -k <text>" can only find it by name.
|
|
|
|
|
bool m_declared { false };
|
|
|
|
|
// The file(s) the klammer is written in, in definition order. Usually
|
|
|
|
|
// one -- a klammer's targets are declared together -- but a klammer whose
|
|
|
|
|
// definitions are spread over several files lists them all. Shown by
|
|
|
|
|
// "--coverage -v", and only on a row that names ONE klammer.
|
|
|
|
|
std::vector<std::string> m_files {};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Compute the coverage of every klammer the machine has loaded. Analysis
|
|
|
|
|
// only: nothing in the Machine is modified.
|
An output policy for the three commands, and @cond as a true special form
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.
2026-08-16 01:37:59 +02:00
|
|
|
// `defined_outside` restricts the RESULT to klammers with a definition from a
|
|
|
|
|
// file not in the list. The ANALYSIS still sees every klammer: a general
|
|
|
|
|
// body's coverage is the intersection of what the klammers it calls cover, and
|
|
|
|
|
// most of those come from the klammerset. So the klammersets are loaded, used
|
|
|
|
|
// to derive, and left out of the report -- which is what "kdesc -i" wants.
|
|
|
|
|
std::vector<Klammer_coverage> klammer_coverage(const Machine& machine,
|
|
|
|
|
const strings_t& defined_outside = {});
|
Target coverage: a klammer states the targets it serves
kdesc gains --coverage, which reports for every klammer the set of targets it
can render to, and — the point of it — which klammers' coverage cannot be
derived and must therefore be declared. Three rules: coverage is DERIVED
where the definitions determine it (a general body of klammer calls covers
the intersection of what those klammers cover, by a greatest fixpoint after
loading), DECLARED where the engine cannot interpret what decides it (an
@eval body, whose targets are undecidable), and UNKNOWN where nothing is
written — which never means "deliberately unavailable".
Two new spellings in a definition's name. A comma-separated target list,
"@@table.html,tex :: ...", gives one body several targets; it is surface
syntax, expanded at registration, and each member goes through the
redefinition rules on its own. And "@@date.* :: ..." writes the general
target out, asserting that the klammer works for EVERY target including ones
not yet defined — a stronger claim than a list of the targets defined today,
and the one target declaration that could be mechanically falsified.
The Standard Klammer Set was swept accordingly: it now has no general
definitions at all, every klammer names the targets it serves, six use ".*",
and tex and pdf are at zero undecided.
kdesc's flags are reorganised on two rules: a flag reached for often gets a
single letter (-k klammers, -t targets, -c characters, -i input), a more
specialised topic a multi-letter name (--argtypes, --katoms, --rewrite,
--optionsets, --coverage, --klammerset, --font); and -v says how much to show
about PROCESSING, never what the RESULT contains — so the katom regex column
is "--katoms full" and the coverage detail "--coverage all". NOTE: "-k" now
lists klammers (optionally filtered by a name/description search); the katom
table moved to "--katoms".
Fixes carried along: an option written with no value crashed the command with
SIGSEGV instead of reporting the mistake; two required positional arguments
never parsed; kdesc and kdiag printed an error and exited 0; and definition
diagnostics counted registrations rather than what was written, so one line
could be reported as two definitions and then printed twice.
Four new test suites: target_list, coverage, command_option, kdesc.
(from dev 46f54080bd9a)
2026-08-12 17:20:23 +02:00
|
|
|
|
|
|
|
|
// The report behind "kdesc --coverage". `full` ("--coverage all") adds the
|
|
|
|
|
// source-file column and shows every "Needs attention" category, including
|
|
|
|
|
// the empty ones; without it those categories appear only when they have
|
|
|
|
|
// entries, so a klammer set with nothing wrong produces a short report.
|
|
|
|
|
//
|
|
|
|
|
// It is an argument rather than a verbosity level because the two are
|
|
|
|
|
// independent: -v says how much to show about the command's PROCESSING, and
|
|
|
|
|
// this says what the command's RESULT contains.
|
|
|
|
|
//
|
|
|
|
|
// Audiences, in the project's terms: an AUTHOR runs it to see what is
|
|
|
|
|
// available for a target; a DESIGNER runs "--coverage all" to be reminded of
|
|
|
|
|
// the full set of categories while building a klammer set.
|
|
|
|
|
void report_coverage(const Machine& machine,
|
|
|
|
|
const std::vector<Klammer_coverage>& coverage,
|
|
|
|
|
bool full, std::ostream& os);
|