Files
klammertext/mac/log.h
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

68 lines
2.2 KiB
C++

#pragma once
#include <iostream>
#include <source_location>
#include <sstream>
#include <string>
#include <variant>
#include <vector>
#include "error.h"
#include "locator.h"
// #include "source.h"
#include "katom_list.h"
extern int verbose_level;
extern bool show_verbose_location;
std::ostream& operator<<(std::ostream& os, const std::variant<bool,int,float,std::string,const char*,Locator>& arg);
void display_location(int log_level, std::source_location location);
namespace K {
template <typename... Ts>
struct log
{
log(int log_level, Ts&&... ts, const std::source_location& location = std::source_location::current()) {
if (verbose_level >= log_level) {
display_location(log_level, location);
// stderr, always. Logging is category 1 (see the output policy in
// CLAUDE.md); stdout belongs to the command's RESULT, and for
// ktext that result is a document a user may pipe. Writing a log
// line to stdout put it inside the document.
if (verbose_level > 1 && sizeof...(ts) > 0) {
std::cerr << ": ";
} else if (verbose_level == 1) {
std::cerr << command_name << ": ";
}
if (log_level > 0) {
((std::cerr << std::forward<Ts>(ts) << " "), ...);
std::cerr << '\n';
}
}
}
};
template <typename... Ts>
log(int log_level, Ts&&...) -> log<Ts...>;
}
/*
void log(int level = 3, const std::source_location location = std::source_location::current());
template<typename T, typename... Args>
void log(Args... args, int level = 3, const std::source_location location = std::source_location::current());
*/
/*
void log(std::vector<std::variant<bool,int,float,std::string,const char*,Locator,Source>> args={}, int level=3,
const std::source_location location
= std::source_location::current());
void xlog(std::vector<std::variant<bool,int,float,std::string,const char*,Locator,Source>> args={}, int verbose_override=1,
const std::source_location location
= std::source_location::current());
*/
void warning(const std::string& message, const Locator& loc);