Klammerset: the @@@klammerset construct, its search path, and const correctness
The @@@klammerset system command formally declares a klammerset — a
named, logically related group of klammer definitions — with an
operative, idempotent declaration (:requires and :files load in order
at the declaration point, relative to the declaring file). A bare
symbol given to ktext -k, kdesc --input, or :requires resolves to
x/x.k on the search path: the document's directory, then
KLAMMERTEXT_KLAMMERSETS, then KLAMMERTEXT_HOME; kdesc --klammerset
lists the available sets. sks/sks.k is the first declared klammerset,
so `-k sks` loads the SKS by name. The engine's lookup classes were
renamed *_set → *_registry to keep the two concepts apart, and the
whole C++ tree now follows standard const-correctness conventions.
tst/ gains klammerset_test.sh (18 cases).
(from dev 64b1abf23e56)
2026-07-30 23:50:07 +02:00
|
|
|
#pragma once
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <optional>
|
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
|
|
#include "klammerset.h"
|
|
|
|
|
#include "argument_set.h"
|
|
|
|
|
#include "katom.h"
|
|
|
|
|
|
|
|
|
|
class Klammerset_registry
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
Klammerset_registry();
|
|
|
|
|
|
|
|
|
|
void add(Klammerset klammerset);
|
|
|
|
|
// Parse a @@@klammerset span. Returns the new Klammerset so the
|
|
|
|
|
// Machine can load its files, or nullopt when the symbol is already
|
|
|
|
|
// registered: a klammerset is loaded once, and a repeated declaration
|
|
|
|
|
// (typically reached through :requires) is skipped, not an error.
|
|
|
|
|
std::optional<Klammerset> add(std::vector<Katom>::iterator begin, std::vector<Katom>::iterator end, std::vector<Katom>& katoms);
|
|
|
|
|
void check_symbol(const std::string& symbol, const Locator& loc) const;
|
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
|
|
|
// Symbol X must be declared in X/X.k, so that the symbol is a function of
|
|
|
|
|
// the path and the already-loaded guard can run before a file is read.
|
|
|
|
|
void check_declaring_file(const std::string& symbol, const Locator& loc) const;
|
Klammerset: the @@@klammerset construct, its search path, and const correctness
The @@@klammerset system command formally declares a klammerset — a
named, logically related group of klammer definitions — with an
operative, idempotent declaration (:requires and :files load in order
at the declaration point, relative to the declaring file). A bare
symbol given to ktext -k, kdesc --input, or :requires resolves to
x/x.k on the search path: the document's directory, then
KLAMMERTEXT_KLAMMERSETS, then KLAMMERTEXT_HOME; kdesc --klammerset
lists the available sets. sks/sks.k is the first declared klammerset,
so `-k sks` loads the SKS by name. The engine's lookup classes were
renamed *_set → *_registry to keep the two concepts apart, and the
whole C++ tree now follows standard const-correctness conventions.
tst/ gains klammerset_test.sh (18 cases).
(from dev 64b1abf23e56)
2026-07-30 23:50:07 +02:00
|
|
|
|
|
|
|
|
bool has(const std::string& symbol) const;
|
|
|
|
|
Klammerset get(const std::string& symbol, const Locator& loc) const;
|
|
|
|
|
|
|
|
|
|
std::string describe(int margin=2, bool long_format=false) const;
|
|
|
|
|
|
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
|
|
|
// Record what a set actually opened (see Klammerset::m_loaded_files).
|
|
|
|
|
void set_loaded_files(const std::string& symbol, const strings_t& files);
|
|
|
|
|
|
|
|
|
|
// Every file every registered klammerset read. A klammer whose
|
|
|
|
|
// definitions all come from these was supplied by a klammerset; one with
|
|
|
|
|
// a definition elsewhere was defined by the input -- which is the
|
|
|
|
|
// distinction "kdesc -i" needs, since it loads a klammerset in order to
|
|
|
|
|
// analyse a file without wanting to LIST the klammerset's own klammers.
|
|
|
|
|
strings_t loaded_files() const;
|
|
|
|
|
|
Klammerset: the @@@klammerset construct, its search path, and const correctness
The @@@klammerset system command formally declares a klammerset — a
named, logically related group of klammer definitions — with an
operative, idempotent declaration (:requires and :files load in order
at the declaration point, relative to the declaring file). A bare
symbol given to ktext -k, kdesc --input, or :requires resolves to
x/x.k on the search path: the document's directory, then
KLAMMERTEXT_KLAMMERSETS, then KLAMMERTEXT_HOME; kdesc --klammerset
lists the available sets. sks/sks.k is the first declared klammerset,
so `-k sks` loads the SKS by name. The engine's lookup classes were
renamed *_set → *_registry to keep the two concepts apart, and the
whole C++ tree now follows standard const-correctness conventions.
tst/ gains klammerset_test.sh (18 cases).
(from dev 64b1abf23e56)
2026-07-30 23:50:07 +02:00
|
|
|
std::map<std::string, Klammerset> m_klammersets {};
|
|
|
|
|
std::vector<std::string> m_symbols {};
|
|
|
|
|
Parameter_set m_parameters {};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// --- The klammerset search path (symbol -> declaration file) ---
|
|
|
|
|
|
|
|
|
|
// A bare identifier (letters, digits, underscores; starts with a letter)
|
|
|
|
|
// names a klammerset symbol; anything else -- a path component, an
|
|
|
|
|
// extension, a space -- is a filename used as given.
|
|
|
|
|
bool is_klammerset_symbol(const std::string& name);
|
|
|
|
|
|
|
|
|
|
// The directories searched for a klammerset symbol, most specific first:
|
|
|
|
|
// 1. local_dir -- the directory of whatever names the symbol: the input
|
|
|
|
|
// document's directory (ktext -k), the declaring file's directory
|
|
|
|
|
// (:requires), or the cwd when there is no document
|
|
|
|
|
// 2. the KLAMMERTEXT_KLAMMERSETS directories (colon-separated; default
|
|
|
|
|
// ~/.klammertext/klammersets), in listed order
|
|
|
|
|
// 3. $KLAMMERTEXT_HOME (the distribution's sets, e.g. sks/sks.k)
|
|
|
|
|
// A symbol x resolves to the first <dir>/x/x.k found, so a document-local
|
|
|
|
|
// set shadows an installed one, which shadows a distributed one. (This is
|
|
|
|
|
// deliberately the OPPOSITE order from the @eval module path, which puts
|
|
|
|
|
// the SKS directories first: a stray util.py next to a document shadowing
|
|
|
|
|
// an SKS module is an everyday accident, while a directory named sks/
|
|
|
|
|
// holding an sks.k next to a manuscript is not.)
|
|
|
|
|
std::vector<std::string> klammerset_search_dirs(const std::string& local_dir);
|
|
|
|
|
|
|
|
|
|
// Resolve a symbol to its declaration file; first hit wins. Throws a
|
|
|
|
|
// Klammerset_error naming the searched directories when nothing matches.
|
|
|
|
|
fs::path resolve_klammerset_symbol(
|
|
|
|
|
const std::string& symbol, const std::string& local_dir, const Locator& loc);
|
|
|
|
|
|
|
|
|
|
// The symbols available on the search path, with provenance; a symbol
|
|
|
|
|
// found again in a later directory is marked as shadowed. For
|
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
|
|
|
// kdesc --klammersets.
|
Klammerset: the @@@klammerset construct, its search path, and const correctness
The @@@klammerset system command formally declares a klammerset — a
named, logically related group of klammer definitions — with an
operative, idempotent declaration (:requires and :files load in order
at the declaration point, relative to the declaring file). A bare
symbol given to ktext -k, kdesc --input, or :requires resolves to
x/x.k on the search path: the document's directory, then
KLAMMERTEXT_KLAMMERSETS, then KLAMMERTEXT_HOME; kdesc --klammerset
lists the available sets. sks/sks.k is the first declared klammerset,
so `-k sks` loads the SKS by name. The engine's lookup classes were
renamed *_set → *_registry to keep the two concepts apart, and the
whole C++ tree now follows standard const-correctness conventions.
tst/ gains klammerset_test.sh (18 cases).
(from dev 64b1abf23e56)
2026-07-30 23:50:07 +02:00
|
|
|
std::string describe_klammerset_search(const std::string& local_dir, int margin=2);
|