Recursion guard and static klammer checking
A klammer that reaches itself, directly or through a cycle, expanded until the C++ stack was exhausted: the process died from SIGSEGV with no message and no location. The former limit guarded only the top-level fixed-point iteration, never the descent through klammer application. A depth guard now raises a recursion error naming the klammer and where it was applied. The same loop's termination test moves from "the katom list stopped growing" to "a pass applied no klammer", since a klammer whose body expands to nothing is a reduction that adds no katoms; exceeding the round limit is now an error rather than a message followed by rendering a document with live klammers still in it. ktext --check locates every klammer application written in a document or in a klammer body and checks name existence, argument count, option names, and target coverage without applying anything, reporting all problems at once. This is possible because Klammertext has no catcodes: katom structure is fixed when a file is read, so a klammer body has a determinate shape before it is expanded. The check therefore reaches what the engine cannot -- the branch of a @cond that is not selected, and bodies a given render never enters. @cond's set of truth values is an open language question, so its meaning is unchanged here; an unrecognized predicate now warns, giving its value and location. tst/ gains recursion_test.sh (7 cases) and check_test.sh (19 cases), and this snapshot's test Makefile is generated from the shipped suite list so the two cannot drift apart. (from dev c27e63802406) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include <iostream>
|
||||
#include "error.h"
|
||||
#include "check.h"
|
||||
#include "command.h"
|
||||
#include "log.h"
|
||||
#include "argv.h"
|
||||
@@ -25,6 +26,9 @@ int main(int argc, char* argv[])
|
||||
"pathname", "", "'word'");
|
||||
args.flag("d", "Display the output to the screen, rather than writing files.");
|
||||
args.flag("m", "Show the Klammermachine state at the beginning of processing.");
|
||||
args.flag("check", "Check every klammer application in the input and in the "
|
||||
"body of every defined klammer, report all problems found, and exit "
|
||||
"without producing output.");
|
||||
args.opt("v", "'verbosity'", "degree", "0", "'verbosity'");
|
||||
|
||||
if (show_usage(argc, argv)) {
|
||||
@@ -52,8 +56,13 @@ int main(int argc, char* argv[])
|
||||
parse_args(input_filenames, args.as_string("t"),
|
||||
expand_tilde(args.as_string("o")), args.as_bool("d"));
|
||||
|
||||
// --check produces no output, so it needs neither a target nor -d.
|
||||
// With no -t it checks every defined target, which is the more useful
|
||||
// default here: "does this document hold together at all?"
|
||||
bool check_only = args.as_bool("check");
|
||||
|
||||
if (*(output_filename.end() - 1) == '*'
|
||||
&& !display_only) {
|
||||
&& !display_only && !check_only) {
|
||||
throw Argument_error(
|
||||
"You must specify an output target or "
|
||||
"display the results with the \"-d\" flag.",
|
||||
@@ -106,6 +115,14 @@ int main(int argc, char* argv[])
|
||||
std::cout << M << "\n";
|
||||
}
|
||||
|
||||
// --check reports statically, before anything is applied: all problems
|
||||
// at once, including ones in @cond branches that are not selected and
|
||||
// in klammer bodies that this render would never reach.
|
||||
if (check_only) {
|
||||
int errors = report_diagnostics(check_machine(M, target), std::cout);
|
||||
return errors > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
std::string result = trim(M.apply(target));
|
||||
if (display_only && !result.empty()) {
|
||||
std::cout << result << "\n";
|
||||
|
||||
Reference in New Issue
Block a user