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

222 lines
7.0 KiB
C++

#pragma once
#include <limits>
#include <memory>
#include "ktype.h"
#include "locator.h"
#include "util.h"
inline bool show_rewrite_rules = false;
class Katom
{
public:
static size_t index;
Katom(const std::string& src, katom_t type, Locator loc);
// Copy constructor
Katom(const Katom& other)
: m_index(other.m_index)
, m_text(other.m_text)
, m_src(other.m_src)
, m_loc(other.m_loc)
, m_type(other.m_type)
, m_initial_type(other.m_initial_type)
, m_display(other.m_display)
, m_unparsed(other.m_unparsed)
, m_deferred(other.m_deferred)
{}
// Copy assignment operator
Katom& operator=(const Katom& other) {
if (this != &other) {
m_index = other.m_index;
m_text = other.m_text;
m_src = other.m_src;
m_loc = other.m_loc;
m_type = other.m_type;
m_initial_type = other.m_initial_type;
m_display = other.m_display;
m_unparsed = other.m_unparsed;
m_deferred = other.m_deferred;
}
return *this;
}
static inline bool show_index;
static inline bool show_type;
static inline bool show_id;
static inline bool show_whitespace;
static inline bool show_all;
static inline bool show_replaced;
static inline bool show_ignored;
bool is_whitespace() const {
return m_initial_type == katom_t::space
|| m_initial_type == katom_t::newline
|| m_type == katom_t::ws_added; }
bool is_word() const { return m_initial_type == katom_t::word; }
bool is_text() const { return m_initial_type == katom_t::text; }
//bool is_text() { return m_initial_type == katom_t::text; }
bool is_literal() const { return m_type == katom_t::literal; }
bool is_active() const {
return m_type != katom_t::ignored
&& m_type != katom_t::replaced; };
bool is_nonascii() const {
return m_type == katom_t::nonascii; };
size_t m_index;
std::string m_text{};
std::string m_src{};
Locator m_loc;
katom_t m_type;
katom_t m_initial_type;
std::string m_display {};
// Set when the word matched no katom type and fell back to katom_t::word.
// The warning is deferred to warn_unparsed_katoms(), after removal and
// literal marking, so removed text (comments, #[...]# blocks) never warns.
bool m_unparsed {};
// Inside an unresolved @cond span, and therefore INERT: the passes with
// observable effects -- @eval and @read -- skip it, so a branch that is
// never selected never runs anything. @cond is a non-strict special form
// (doc/cond_evaluation_order.md), and this flag is the mechanism; the flag
// is not the semantics. Cleared on the selected branch when the @cond is
// resolved in the apply fold, which then processes that branch normally.
bool m_deferred {};
};
bool active(const std::vector<Katom>& katoms);
int active_count(const std::vector<Katom>& katoms);
std::vector<Katom> split_into_katoms(std::string s, const std::string& source, int source_line);
void restore_initial_type(std::vector<Katom>::iterator begin, std::vector<Katom>::iterator end);
void modify_type(katom_t new_type, std::vector<Katom>::iterator begin, std::vector<Katom>::iterator end);
void modify_type(katom_t old_type, katom_t new_type,
std::vector<Katom>::iterator begin, std::vector<Katom>::iterator end);
void ignore_whitespace(std::vector<Katom>::iterator& begin, std::vector<Katom>& katoms);
std::vector<Katom>::iterator after_whitespace(std::vector<Katom>::iterator begin);
std::vector<Katom> text_katoms(
std::vector<Katom>::iterator& begin, std::vector<Katom>::iterator& end);
std::string as_string(std::vector<Katom>::const_iterator begin, std::vector<Katom>::const_iterator end, bool strip_whitespace);
std::string as_string(const std::vector<Katom>& katoms, bool strip_whitespace);
std::vector<Katom> trim(std::vector<Katom>& katoms, std::set<katom_t> trim_types = {katom_t::space, katom_t::newline});
std::vector<Katom> trim(const std::vector<Katom>& katoms, bool trim_inactive = false);
std::vector<std::vector<Katom>> bar_split(std::vector<Katom>::iterator kbegin, std::vector<Katom>::iterator kend);
std::vector<std::string> line_split(const std::string& s);
std::pair<std::string, std::vector<std::string>> line_split(const fs::path& pathname);
std::vector<Katom> katomize(const std::vector<std::string>& lines, const std::string& source_desc);
void warn_unparsed_katoms(std::vector<Katom>& katoms, bool warn = true);
void process_whitespace_modifiers(std::vector<Katom>& katoms);
std::vector<Katom> trim_whitespace(std::vector<Katom> katoms);
// escape_backslash() and unescape_backslash() (the ___BS___ hack) were
// removed. Backslash is now handled by the general target escape mechanism
// via the :escape parameter on @@@target.
inline bool is_bar(const Katom& k) {
return k.m_type == katom_t::bar;
}
inline bool is_nonascii(const Katom& k) {
return k.m_type == katom_t::nonascii;
}
inline bool is_newline(const Katom& k) {
return k.m_type == katom_t::newline;
}
inline bool is_ignore_rest(const Katom& k) {
return k.m_type == katom_t::ignore_rest;
}
inline bool is_ignore_line(const Katom& k) {
return k.m_type == katom_t::ignore_line;
}
inline void mark_as_replaced(Katom& k) {
k.m_type = katom_t::replaced;
}
inline void mark_as_literal(Katom& k) {
k.m_type = katom_t::literal;
}
inline void mark_as_ignored(Katom& k) {
k.m_type = katom_t::ignored;
}
inline bool begin_read(Katom& k) {
return k.m_type == katom_t::read_begin;
}
inline bool begin_ignore(const Katom& k) {
return k.m_type == katom_t::ignore_begin;
}
inline bool end_ignore(const Katom& k) {
return k.m_type == katom_t::ignore_end;
}
inline bool begin_literal(const Katom& k) {
return k.m_type == katom_t::literal_begin;
}
inline bool end_literal(const Katom& k) {
return k.m_type == katom_t::literal_end;
}
inline bool begin_eval(const Katom& k) {
return k.m_type == katom_t::eval_begin;
}
inline bool begin_cond(const Katom& k) {
return k.m_type == katom_t::cond_begin;
}
inline bool begin_klammer_def(const Katom& k) {
return k.m_type == katom_t::define_begin;
}
inline bool end_klammer_def(const Katom& k) {
return k.m_type == katom_t::define_end;
}
inline bool begin_klammer_apply(const Katom& k) {
return k.m_type == katom_t::apply_begin;
}
inline bool end_klammer_apply(const Katom& k) {
return k.m_type == katom_t::apply_end;
}
inline bool begin_machine_def(const Katom& k) {
return k.m_type == katom_t::machine_begin;
}
inline bool end_machine_def(const Katom& k) {
return k.m_type == katom_t::machine_end;
}
inline bool begin_apply(const Katom& k)
{
const std::set<katom_t> opens {
katom_t::read_begin,
katom_t::eval_begin,
katom_t::cond_begin,
katom_t::apply_begin};
return opens.contains(k.m_type);
}
inline bool end_apply(const Katom& k)
{
return k.m_type == katom_t::apply_end;
}