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.
222 lines
7.0 KiB
C++
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;
|
|
}
|