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.
76 lines
3.7 KiB
C++
76 lines
3.7 KiB
C++
#pragma once
|
|
|
|
#include <iosfwd>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "locator.h"
|
|
#include "util.h" // katom_list
|
|
|
|
class Machine;
|
|
|
|
// Static checking of klammer applications.
|
|
//
|
|
// Klammertext can do something TeX structurally cannot: know the shape of a
|
|
// klammer body before that body is expanded. Katom structure is fixed when a
|
|
// file is read -- there are no catcodes, so no later assignment can change how
|
|
// text already read is divided into katoms -- which means every klammer
|
|
// application that appears literally in a document or in a klammer body can be
|
|
// located, named, and checked against the registry without running anything.
|
|
//
|
|
// This matters most where dynamic checking cannot reach. @cond is a
|
|
// non-strict special form: the branch it does not select is never applied, so
|
|
// an undefined klammer or a wrong argument count sitting in that branch is
|
|
// invisible at run time and stays invisible until the day the predicate flips.
|
|
// The same holds for a klammer body that is never applied for the target being
|
|
// built. The checker reports all of them.
|
|
//
|
|
// What it deliberately does NOT see: klammers produced by @eval (a generator's
|
|
// result is text computed at run time), and the contents of @eval and @read
|
|
// argument spans (code and filenames, not applications). Its guarantee is
|
|
// therefore about what is written, not about what will run.
|
|
//
|
|
// One thing it does not see that it SHOULD: a @cond written at the top level
|
|
// of a document is resolved when the file is read (process_cond_katoms() runs
|
|
// inside process_katoms()), so by the time anything can be checked the
|
|
// unselected branch has already been discarded. Inside a klammer body the
|
|
// @cond survives until the klammer is applied, so body branches ARE checked --
|
|
// which is where most of them are written. Closing the gap means resolving
|
|
// @cond at application time rather than at read time, which is part of the
|
|
// pass-ordering question; see notes/Klammertext_improvements.md.
|
|
struct Diagnostic
|
|
{
|
|
Diagnostic(const std::string& severity, const std::string& message,
|
|
const std::string& context, const Locator& loc)
|
|
: m_severity(severity)
|
|
, m_message(message)
|
|
, m_context(context)
|
|
, m_loc(loc)
|
|
{}
|
|
|
|
std::string m_severity {}; // "error" or "warning"
|
|
std::string m_message {};
|
|
std::string m_context {}; // where it was found, e.g. "body of @s1 (tex)"
|
|
Locator m_loc {};
|
|
};
|
|
|
|
// Check every statically visible klammer application in the document and in
|
|
// the body of every defined klammer, for the named target. A target of "*"
|
|
// (Target_registry::general_name) checks every defined target. Diagnostics
|
|
// accumulate: checking never stops at the first failure, because the point is
|
|
// to see all of them at once.
|
|
// `document` is the katom list to check. It is a parameter rather than being
|
|
// taken from machine.m_katoms because the caller is the one that has it, and
|
|
// the two are not always the same list: Machine::read() fills m_katoms, but
|
|
// Machine::process() -- which is how kdiag builds its katoms -- does not.
|
|
// Reading m_katoms therefore checked an EMPTY document under kdiag and
|
|
// reported "0 diagnostics, 0 errors" for anything, which is worse than not
|
|
// checking: it reports success for input it never examined. Passing the list
|
|
// makes that mistake impossible to write.
|
|
std::vector<Diagnostic> check_machine(Machine& machine, const katom_list& document,
|
|
const std::string& target);
|
|
|
|
// Print diagnostics, grouped in the order found, and return the number of
|
|
// errors (warnings do not count). Used by "ktext --check".
|
|
int report_diagnostics(const std::vector<Diagnostic>& diagnostics, std::ostream& os);
|