Files
klammertext/mac/log.cpp
Andy Kopra 240cff4278 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

118 lines
3.5 KiB
C++

#include <source_location>
#include "log.h"
#include "show.h"
#include "util.h"
int verbose_level = 0;
using log_arg = std::variant<bool,int,float,std::string,const char*,Locator>;
std::ostream& operator<<(std::ostream& os, const log_arg& arg)
{
bool quoted_string = verbose_level > 2;
if (std::holds_alternative<bool>(arg)) {
os << (std::get<bool>(arg) ? "true" : "false");
} else if (std::holds_alternative<int>(arg)) {
os << std::get<int>(arg);
} else if (std::holds_alternative<float>(arg)) {
os << std::get<float>(arg);
}
else if (std::holds_alternative<std::string>(arg)) {
std::string s = std::get<std::string>(arg);
if (quoted_string)
os << "\"";
if (!quoted_string && s.empty()) {
os << "<none>";
} else {
os << s;
}
if (quoted_string)
os << "\"";
} else if (std::holds_alternative<const char*>(arg)) {
os << std::get<const char*>(arg);
// } else if (std::holds_alternative<strings_t>(arg)) {
// os << std::get<strings_t>(arg);
} else if (std::holds_alternative<Locator>(arg)) {
os << std::get<Locator>(arg);
// } else if (std::holds_alternative<Source>(arg)) {
// os << std::get<Source>(arg);
// } else if (std::holds_alternative<katom_t>(arg)) {
// os << std::get<katom_t>(arg);
// } else if (std::holds_alternative<Katom>(arg)) {
// os << std::get<Katom>(arg);
}
/*
} else if (std::holds_alternative<katom_list>(arg)) {
os << std::get<katom_list>(arg);
} else if (std::holds_alternative<katom_iter>(arg)) {
os << std::get<katom_iter>(arg);
}
*/
return os;
}
void log_indent(const std::string& filename, const Color& color)
{
std::cerr
<< color
<< std::setfill(' ')
<< std::setw(22-static_cast<int>(std::size(filename)))
<< std::right;
}
void log_filepos(const std::string& filename, int line, const Color& color)
{
// if (show_verbose_location) {
log_indent(filename, color);
std::cerr
<< "[" << filename << ":"
<< std::setw(3) << std::setfill('0')
<< line << "] " << reset;
// }
}
std::string prettify_name(const std::string& s)
{
std::string result = s;
result = string_replace(result, "std::__cxx11::basic_string<char>", "std::string");
result = string_replace(result, " >", ">");
std::smatch match;
std::regex re(R"(.*?(\w+)\(.*)");
if (verbose_level < 4 && std::regex_search(result, match, re)) {
std::stringstream ss;
ss << match[1] << "()";
result = ss.str();
}
return result;
}
void display_location(int log_level, std::source_location location)
{
if (verbose_level >= log_level) {
Color color = blue; // cyan;
if (log_level == 1) {
color = magenta;
}
if (verbose_level > 1) {
log_filepos(location.file_name() ? location.file_name() : "",
location.line(), color);
}
if (verbose_level > 3) {
std::cerr << location.function_name();
} else if (verbose_level > 1) {
std::cerr << prettify_name(location.function_name());
}
}
}
void warning(const std::string& message, const Locator& loc)
{
std::cerr << " " << red << command_name << " (warning): " << message << "\n";
if (loc.m_filename != "") {
std::cerr << " " << loc.desc();
}
std::cerr << "\n" << reset;
}