Verbatim-safe typography, ^-punctuation quoting, full-range ^UUUU^, polyglot html

Typographic transforms (---, quote pairs, ~) no longer touch verbatim
text: @c/@code/@source_listing content and ^'...'^ spans show exactly the
characters written. "^" before any punctuation character quotes it in
every target (the apostrophe excepted: ^' opens a literal span), with the
new :resolve option on @@@target declaring per-target renderings. The
^UUUU^ code-point form accepts 4-6 hex digits, the full Unicode range.
The html output and transform spellings are polyglot (XML-valid), in
preparation for an EPUB target. New suites: transform_test, character_test
(engine), typography_test (SKS).

(from dev 07ce5ea86a0a)
This commit is contained in:
2026-08-23 20:48:26 +02:00
parent d982c0d6cc
commit 37b6ba1c4f
77 changed files with 1665 additions and 1608 deletions

View File

@@ -42,8 +42,8 @@ std::string shell(State state, std::string command, Locator loc)
err_template.push_back('\0');
int err_fd = mkstemp(err_template.data());
if (err_fd == -1) {
throw Environment_error("Could not create a temporary file for the "
"command's error output", loc, false);
throw Environment_error(
"Could not create a temporary file for the command's error output", loc);
}
close(err_fd);
err_path = err_template.data();
@@ -55,7 +55,7 @@ std::string shell(State state, std::string command, Locator loc)
if (!pipe) {
fs::remove(err_path);
throw Environment_error(
"Could not run command:\n" + command, loc, false);
"Could not run command:\n " + command, loc);
}
char buffer[128];
std::string result = "";
@@ -87,7 +87,7 @@ std::string shell(State state, std::string command, Locator loc)
// "environment", not "parsing": the failure is OUTSIDE Klammertext,
// in the command the document invoked -- the same class as a missing
// xelatex or an unset environment variable.
throw Environment_error(ss.str(), loc, false);
throw Environment_error(ss.str(), loc);
}
if (!error_output.empty()) {
(void)K::log(1, "shell command wrote to stderr:", command,
@@ -122,8 +122,8 @@ std::string run_haskell(const std::string& hsfile, Locator loc)
err_template.push_back('\0');
int err_fd = mkstemp(err_template.data());
if (err_fd == -1) {
throw Environment_error("Could not create a temporary file for runghc's "
"error output", loc, false);
throw Environment_error(
"Could not create a temporary file for runghc's error output", loc);
}
close(err_fd);
err_path = err_template.data();
@@ -132,7 +132,7 @@ std::string run_haskell(const std::string& hsfile, Locator loc)
FILE* pipe = popen(command.c_str(), "r");
if (!pipe) {
fs::remove(err_path);
throw Environment_error("Could not run runghc.", loc, false);
throw Environment_error("Could not run runghc.", loc);
}
char buffer[128];
std::string result = "";
@@ -154,12 +154,12 @@ std::string run_haskell(const std::string& hsfile, Locator loc)
}
ss << ".";
if (!error_output.empty()) {
ss << "\nrunghc reported:\n" << error_output;
ss << "\n\nrunghc reported:\n " << error_output;
}
// A compile error arrives here, and so does a program that ran and then
// exited nonzero; the message does not guess which, it shows what
// runghc said.
throw Environment_error(ss.str(), loc, false);
throw Environment_error(ss.str(), loc);
}
if (!error_output.empty()) {
// GHC's warnings, and anything the program itself wrote to stderr.
@@ -175,9 +175,9 @@ std::string haskell(State state, std::string code, Locator loc)
// "environment", not "parsing": a missing external command is the same
// class as a missing xelatex, and "parsing error" misdescribes it.
throw Environment_error(
"@eval with the :haskell argument requires runghc, which was not found in PATH.\n"
"@eval with the :haskell argument requires runghc, which was not found in PATH.\n\n"
"Install it using GHCup; see https://www.haskell.org/ghcup/install/.",
loc, false);
loc);
}
code = state.subst(code);
@@ -193,7 +193,7 @@ std::string haskell(State state, std::string code, Locator loc)
int fd = mkstemp(tmppath.data());
if (fd < 0) {
throw Environment_error(
"Could not create temporary file for Haskell evaluation.", loc, false);
"Could not create temporary file for Haskell evaluation.", loc);
}
std::string hsfile = std::string(tmppath.data()) + ".hs";
close(fd);
@@ -202,8 +202,7 @@ std::string haskell(State state, std::string code, Locator loc)
FILE* f = fopen(hsfile.c_str(), "w");
if (!f) {
unlink(hsfile.c_str());
throw Parsing_error(
"Could not write temporary Haskell file.", loc, false);
throw Parsing_error("Could not write temporary Haskell file.", loc);
}
fprintf(f, "%s\n", code.c_str());
fclose(f);
@@ -222,7 +221,7 @@ void check_cpp_arguments(katom_list args, Locator loc)
<< "or\n"
<< " @eval :cpp <library-basename> <function-name> @\n"
<< "In the first case, the library basename is used for the function name.";
throw Argument_error(ss.str(), loc, false);
throw Argument_error(ss.str(), loc);
}
}
@@ -279,7 +278,7 @@ std::string Eval::eval_command(katom_iter begin, katom_iter end)
if (dir.empty() || !fs::is_directory(dir)) {
throw Argument_error(
"The :cwd directory does not exist: \"" + dir + "\"",
begin->m_loc, false);
begin->m_loc);
}
cwd_guard.emplace(dir);
first = resume;