2026-07-18 18:48:23 +02:00
|
|
|
#include <iostream>
|
|
|
|
|
#include "error.h"
|
|
|
|
|
#include "command.h"
|
|
|
|
|
#include "log.h"
|
|
|
|
|
#include "argv.h"
|
|
|
|
|
#include "file.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "machine.h"
|
|
|
|
|
#include "show.h"
|
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
|
|
|
#include "target_registry.h"
|
|
|
|
|
#include "klammerset_registry.h"
|
2026-07-18 18:48:23 +02:00
|
|
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
set_verbose_level(argc, argv);
|
|
|
|
|
Argv args {};
|
|
|
|
|
args.req("filenames", "Input files in Klammertext format. ", "'list'");
|
|
|
|
|
args.opt("s", "Text processed before input files.", "input-string", "", "'text'");
|
|
|
|
|
args.opt("t","Output target; default is general (unspecified)", "target",
|
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
|
|
|
Target_registry::general_name, "'word'");
|
2026-07-18 18:48:23 +02:00
|
|
|
args.opt("o", "Output basename; meaning and default defined by target.", "basename",
|
|
|
|
|
"", "'word'");
|
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
|
|
|
args.flag("d", "Display the output to the screen, rather than writing a file.");
|
|
|
|
|
args.opt("klammersets", "Klammersets to load, as a list of symbols resolved on the "
|
|
|
|
|
"klammerset search path. Several combine: they are loaded in the order given "
|
|
|
|
|
"and share one namespace. The default is the Standard Klammer Set; "
|
|
|
|
|
"\"none\" loads none.",
|
|
|
|
|
"symbols", "", "'list'");
|
2026-07-18 18:48:23 +02:00
|
|
|
args.opt("v", "'verbosity'", "degree", "0", "'verbosity'");
|
|
|
|
|
|
|
|
|
|
if (show_usage(argc, argv)) {
|
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
|
|
|
// A bare command is a REQUEST for information, not a failure:
|
|
|
|
|
// usage goes to stdout (it is the result being asked for) and the
|
|
|
|
|
// exit status is 0, so "ktext && echo ok" reports what happened.
|
2026-07-18 18:48:23 +02:00
|
|
|
args.usage(file_basename(argv[0]));
|
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
|
|
|
exit(0);
|
2026-07-18 18:48:23 +02:00
|
|
|
}
|
|
|
|
|
args.parse(argc, argv);
|
|
|
|
|
if (verbose_level > 0) {
|
|
|
|
|
args.describe();
|
|
|
|
|
}
|
|
|
|
|
std::string input_text = args.as_string("s");
|
2026-07-26 23:22:59 +02:00
|
|
|
// argv boundaries are authoritative (a shell-quoted "my file.kt" is
|
|
|
|
|
// one element); group_filename_tokens adds the standalone-"/" list
|
|
|
|
|
// form, the existence rescue for unquoted spaces, and ~ expansion.
|
|
|
|
|
std::vector<std::string> input_filenames =
|
|
|
|
|
group_filename_tokens(args.as_vector("filenames"));
|
2026-07-18 18:48:23 +02:00
|
|
|
|
|
|
|
|
if (input_text.empty() && input_filenames.empty()) {
|
|
|
|
|
throw Argument_error(
|
|
|
|
|
"You must specify input filenames and/or text", Locator());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto [target, output_dir, output_basename, output_filename,
|
|
|
|
|
write_files, display_only] =
|
2026-07-26 23:22:59 +02:00
|
|
|
parse_args(input_filenames, args.as_string("t"),
|
|
|
|
|
expand_tilde(args.as_string("o")), args.as_bool("d"));
|
2026-07-18 18:48:23 +02:00
|
|
|
|
|
|
|
|
Machine M;
|
|
|
|
|
M.m_state.open_frame("ktext");
|
|
|
|
|
M.m_state.set("K_target", target);
|
|
|
|
|
M.m_state.set("K_output_dir", output_dir);
|
|
|
|
|
M.m_state.set("K_output_basename", output_basename);
|
|
|
|
|
M.m_state.set("K_stdout_only", display_only ? "true" : "false");
|
2026-07-26 23:22:59 +02:00
|
|
|
// The list form uses the standalone-"/" separator (filenames may
|
|
|
|
|
// contain spaces); K_input_filename is the single root input file.
|
|
|
|
|
M.m_state.set("K_input_filenames", join(input_filenames, " / "));
|
|
|
|
|
M.m_state.set("K_input_filename",
|
|
|
|
|
input_filenames.empty() ? "" : input_filenames[0]);
|
2026-07-18 18:48:23 +02:00
|
|
|
M.m_state.set("K_verbose_level", std::to_string(verbose_level));
|
|
|
|
|
|
|
|
|
|
if (!input_filenames.empty()) {
|
|
|
|
|
M.m_state.set(
|
|
|
|
|
"K_input_dir",absolute_pathname(file_directory(input_filenames[0])));
|
|
|
|
|
} else {
|
|
|
|
|
M.m_state.set("K_input_dir", fs::current_path().string());
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
load_klammersets(M, word_split(args.as_string("klammersets")));
|
2026-07-18 18:48:23 +02:00
|
|
|
|
|
|
|
|
if (!input_text.empty()) {
|
|
|
|
|
M.read(input_text + "\n");
|
|
|
|
|
}
|
|
|
|
|
for (auto p : input_filenames) {
|
|
|
|
|
M.read(fs::path(p));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string result = trim(M.apply(target));
|
|
|
|
|
if (display_only && !result.empty()) {
|
|
|
|
|
std::cout << result << "\n";
|
|
|
|
|
} else if (!result.empty()) {
|
|
|
|
|
string_to_file(output_filename, result + "\n");
|
|
|
|
|
(void)K::log(1, "Wrote file: " + output_filename);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Error& err) {
|
|
|
|
|
err.print_message();
|
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
|
|
|
std::cerr << "\n";
|
2026-07-18 18:48:23 +02:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|