kdesc --katoms is Klammertext rendered from ref/, the types in titled groups, with --katoms full for the regex column. @table :leading L is L lines of the surrounding text in html as in pdf (default 1), and :rulesep spaces the horizontal rules. The html table has one style attribute, so :column_width fractions take effect again. @code and @source_listing render in plain text; captions are numbered per kind there, and a table needs no blank line around it. @c takes :nd, and an option-shaped word before a bar inside literal code is code. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
293 lines
12 KiB
C++
293 lines
12 KiB
C++
#include <set>
|
|
|
|
#include "machine.h"
|
|
#include "character.h"
|
|
#include "error.h"
|
|
#include "show.h"
|
|
#include "font_store.h"
|
|
#include "ktype.h"
|
|
|
|
// extern "C" is needed for dlsym name lookup. Returning std::string from
|
|
// C-linkage functions is technically non-standard but works correctly on
|
|
// Linux (Itanium ABI) where C and C++ calling conventions are identical.
|
|
#if defined(__clang__)
|
|
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
|
|
#endif
|
|
|
|
extern "C"
|
|
std::string diacritics_examples([[maybe_unused]] Machine& machine)
|
|
{
|
|
if (diacritics.size() != diacritic_symbols_order.size()) {
|
|
throw Internal_error("Mismatch between diacritics order list and their definitions");
|
|
}
|
|
//std::cout << machine;
|
|
|
|
std::stringstream kt {};
|
|
|
|
kt << "@s2 Roman letters with a diacritical mark @\n\n";
|
|
|
|
kt << "@table :caption Diacritics (with typical base characters) :cell_hpos c c l :leading 1.2 |\n"
|
|
<< "Displayed | Written | Name ||\n";
|
|
|
|
for (char symbol : diacritic_symbols_order) {
|
|
std::string sym { symbol };
|
|
auto [code, name] = diacritics[sym];
|
|
std::string letter = diacritic_example_letter[sym];
|
|
std::string written = "^" + letter + sym;
|
|
if (sym == "~") {
|
|
written = "^" + letter + "^~";
|
|
}
|
|
std::string display = "^" + letter + sym;
|
|
kt << display << " | @t ^" << written << " @ | " << name << " ||\n";
|
|
}
|
|
kt << "table@";
|
|
return kt.str();
|
|
}
|
|
|
|
extern "C"
|
|
std::string extended_latin_examples([[maybe_unused]] Machine& machine)
|
|
{
|
|
std::stringstream kt {};
|
|
kt << "@s2 Non-Roman characters @\n\n";
|
|
|
|
kt << "@table :caption Extended Latin characters and ligatures :cell_hpos c c l :leading 1.2 |\n"
|
|
<< "Displayed | Written | Name ||\n";
|
|
for (const std::string& symbol : extended_latin_symbols) {
|
|
auto [code, name] = extended_latin[symbol];
|
|
std::string coded = "^" + symbol + "^";
|
|
std::string file_literal = "^^ #- " + symbol + " #- ^^";
|
|
kt << coded << " | @t " << file_literal << " @ | " << name << " ||\n";
|
|
}
|
|
kt << "table@";
|
|
return kt.str();
|
|
}
|
|
|
|
extern "C"
|
|
std::string pinyin_examples([[maybe_unused]] Machine& machine)
|
|
{
|
|
std::stringstream kt {};
|
|
kt << "@s2 Chinese Pinyin syllable accents @\n\n";
|
|
|
|
kt << "@table :caption Mandarin pinyin tones (using vowel ``a'') :cell_hpos c c l :leading 1.2 |\n"
|
|
<< "Displayed | Written | Name ||\n";
|
|
|
|
for (auto [symbol, codename] : pinyin_tones) {
|
|
auto [code, name] = codename;
|
|
std::string literal = "^a" + symbol;
|
|
kt << literal << " | @t ^" << literal << " @ | " << name << " ||\n";
|
|
}
|
|
kt << "table@";
|
|
return kt.str();
|
|
}
|
|
|
|
extern "C"
|
|
std::string unicode_examples([[maybe_unused]] Machine& machine)
|
|
{
|
|
std::stringstream kt {};
|
|
kt << "\n@s2 Including Unicode in Klammertext files @\n\n"
|
|
<< "To specify a wide variety of different letter forms, the Unicode Standard "
|
|
<< "defines a large set of \"code points\" organized by groups. To include a "
|
|
<< "Unicode character in Klammertext, you surround its hexadecimal number with "
|
|
<< "the \"^^\" character. For example, the Cyrillic capital letter \"I\" is \"И\", "
|
|
<< "included in Klammertext as @t ^^0418^^ @.\n\n"
|
|
<< "To search for Unicode characters by their description, see: "
|
|
<< "@link https://www.fileformat.info/info/unicode/char/search.htm @\n\n";
|
|
|
|
kt << "@s3 Egyptian hieroglyphs @\n\n";
|
|
|
|
kt << "@table :caption Examples of Egyptian hieroglyphs :cell_hpos c c c l :leading 1.1 |\n"
|
|
<< "Hieroglyph | Written | Sound | Gardiner ||\n";
|
|
|
|
// The body font carries no hieroglyphs, so the glyph names the
|
|
// typeface it needs (shipped in fnt/); @document declares and localizes
|
|
// it. Under txt @typeface passes the text through unchanged.
|
|
for (auto glyph : ptolemy) {
|
|
std::string code = "^" + glyph[2] + "^";
|
|
kt << "@egyptian "
|
|
<< process_unicode_codepoint(code) << " @ | "
|
|
<< "@t ^'" << code << "'^ @ | "
|
|
<< glyph[1] << " | "
|
|
<< glyph[0] << " ||\n";
|
|
}
|
|
kt << "table@\n\n"
|
|
<< "\nIn @link https://en.wikipedia.org/wiki/List_of_Egyptian_hieroglyphs @, "
|
|
<< "the Unicode codepoint in the second column of the \"List of hieroglyphs\" "
|
|
<< "table is the number in the ^^...^^ code used by Klammertext.\n\n";
|
|
|
|
|
|
kt << "@s3 Chinese characters @\n\n";
|
|
|
|
bool chinese = font_installed("Noto Serif SC");
|
|
std::string target = machine.m_state.get("K_target").m_value;
|
|
const bool txt_or_ansi = target == "txt"|| target == "ansi";
|
|
|
|
if (txt_or_ansi || chinese) {
|
|
kt << "@table :caption Examples of Chinese characters :cell_hpos c c l :leading 1.2 |\n"
|
|
<< " Character | Written | Pinyin ||\n";
|
|
|
|
for (auto chr : dao) {
|
|
std::string code = "^" + chr[1] + "^";
|
|
kt << "@chinese " << process_unicode_codepoint(code) << " @ | "
|
|
<< "@t ^'" << code << "'^ @" << " | "
|
|
<< chr[0] << " ||\n";
|
|
}
|
|
kt << "table@\n\n";
|
|
}
|
|
if (!txt_or_ansi && !chinese) {
|
|
kt << "To display Chinese characters, you need to install a Chinese typeface, "
|
|
"which Klammertext does not ship. "
|
|
"Download @i Noto Serif SC @ from Google Fonts, unzip it, and run: \n\n"
|
|
" @t kdesc ^-^-typeface install @ ~ @i the-installed-font-directory @ \n\n"
|
|
"The font goes into your "
|
|
"@t ^~/.klammertext/fonts @ directory or the first directory in the list of "
|
|
"directories defined by the @t KLAMMERTEXT_FONTS @ environment variable.\n\n";
|
|
}
|
|
return kt.str();
|
|
}
|
|
|
|
extern "C"
|
|
std::string argtype_definitions(Machine& machine)
|
|
{
|
|
auto argtypes = machine.m_argtypes;
|
|
std::stringstream kt {};
|
|
std::set<std::string> custom {};
|
|
|
|
std::map<std::string, std::vector<std::string>> custom_types {};
|
|
|
|
kt << "@s2 Types of klammer arguments @\n\n"
|
|
<< "@define :style r | \n";
|
|
// Hack to make the "Pattern:" label line up vertically;
|
|
// there should be a klammer that aligns text at "tab stops".
|
|
auto name_width = std::string("klammertext").size() + 1;
|
|
for (const auto& name : argtypes.m_names) {
|
|
auto argtype = argtypes.m_types.at(name);
|
|
auto filename = argtype.m_loc.m_filename;
|
|
if (contains(filename, "argtype_registry")) {
|
|
std::string fill(name_width - name.size(), '~');
|
|
kt << "\n@c " << name << " c@ @t " << fill << " t@ @i Pattern: i@ ~ @c "
|
|
<< argtype.m_symbolic_pattern << " c@ |\n"
|
|
<< capitalize(trim(argtype.m_desc)) << " ||\n";
|
|
} else {
|
|
custom.insert(filename);
|
|
custom_types[filename].push_back(name);
|
|
}
|
|
}
|
|
kt << "\ndefine@\n\n"
|
|
<< "@vspace .5 @\n@s3 Reusing argtype symbols @\n\n"
|
|
<< "A previously defined type can be included in the definition of a new type "
|
|
<< "by surrounding the name of the existing type in single quotation marks. "
|
|
<< "For example, the @c uint c@ type (unsigned integer) is used to define an integer with a "
|
|
<< "`+' or `-' character before it.\n\n"
|
|
<< "@vspace .5 @\n@s3 Custom argument types @\n\n"
|
|
<< "Other argtypes are defined for special purposes for klammers defined in the following files:\n\n";
|
|
|
|
kt << "@define :style t |\n";
|
|
for (auto [filename, types] : custom_types) {
|
|
auto type_count = types.size();
|
|
int column_count = type_count < 4 ? 1 : int(float(type_count)/3);
|
|
// auto count = int(float(types.size()) / 3);
|
|
kt << symbolic_klammertext_filename(filename) << " |\n"
|
|
<< "@columns :style c :n " << column_count << " | " << join(types, " ") << " @ ||\n\n";
|
|
}
|
|
kt << "define@\n\n";
|
|
return kt.str();
|
|
}
|
|
|
|
|
|
// The Regex cell of a katom type. A type the Machine assigns is matched by
|
|
// no regex and its cell is empty -- ws_added among them, whose m_pattern is a
|
|
// description and not a regex (see the note in mac/katom_matching.cpp). The
|
|
// two whitespace types are named, since "@c c@" would show nothing.
|
|
static std::string katom_regex_cell(const Ktype& k)
|
|
{
|
|
if (k.m_pattern.empty() || k.m_type == katom_t::ws_added) {
|
|
return "";
|
|
}
|
|
if (k.m_type == katom_t::space) {
|
|
return "@symb-space";
|
|
}
|
|
if (k.m_type == katom_t::newline) {
|
|
return "@t \\n @";
|
|
}
|
|
// A literal value has its caret forms processed as it is built, so a
|
|
// caret is written quoted: bare, "[^^@#|]+" would display as "[^@#|]+"
|
|
// and "[@|#^:*]" would lose its caret to the colon.
|
|
// The exception is a caret before an apostrophe: "^'" is not a quoting
|
|
// form (it opens a literal span elsewhere) and passes through as written.
|
|
std::string quoted {};
|
|
for (size_t i = 0; i < k.m_pattern.size(); i++) {
|
|
const char c = k.m_pattern[i];
|
|
const bool before_apostrophe = i + 1 < k.m_pattern.size() && k.m_pattern[i + 1] == '\'';
|
|
quoted += (c == '^' && !before_apostrophe) ? "^^" : std::string(1, c);
|
|
}
|
|
return "@c :nd | " + quoted + " c@";
|
|
}
|
|
|
|
extern "C"
|
|
std::string katom_descriptions([[maybe_unused]] Machine& machine)
|
|
{
|
|
std::stringstream kt {};
|
|
kt << R"(
|
|
A ``katom'' is an individual element in the Klammertext input text. Each katom
|
|
has a type, listed below. You can see how Klammertext divides up text into
|
|
katoms with the @t-kdiag command.
|
|
|
|
In the katom patterns, @symb-name is a word that begins with a letter and only
|
|
contains letters, numbers, or the underscore (_) or period (.) characters. The
|
|
@symb-n symbol is an integer greater than or equal to 1. The @symb-code symbol
|
|
is one of the codes used with the @t ^^ @ character to create characters that
|
|
are not available in the English alphabet. The codes for producing those
|
|
characters can be displayed by the @t-kdesc command: @t kdesc -c -u @
|
|
|
|
)";
|
|
// ":txt_width N" widens the TABLE alone. This text is re-read by a
|
|
// Machine of its own (Eval::eval), so a @@@state here changes the width
|
|
// the @table below is laid out to and nothing else: the prose above
|
|
// leaves that Machine as plain text and is filled to the caller's width
|
|
// by the caller's phases, which skip the table because it is laid out.
|
|
const std::string txt_width = machine.m_state.value("txt_width", false);
|
|
if (!txt_width.empty()) {
|
|
kt << "@@@state Target_txt_width :replace " << txt_width << " @@@\n";
|
|
}
|
|
const bool show_regex = machine.m_state.value("regex", false) == "true";
|
|
|
|
// The rows first, so that the rules can be placed by the number of rows
|
|
// before them (the header is the first): one above each group's title
|
|
// and one below it, which is what marks the row as a title. The first
|
|
// group needs none above: "head" already rules the header off.
|
|
const std::string b = " | ";
|
|
const std::string title_cells = show_regex ? b + b + b + b : b + b + b;
|
|
std::stringstream rows {};
|
|
std::stringstream hlines {};
|
|
int row_count = 1;
|
|
for (const auto& group : katom_type_groups) {
|
|
if (row_count > 1) {
|
|
hlines << " " << row_count;
|
|
}
|
|
rows << title_cells << "@i " << group.m_title << " @ ||\n";
|
|
row_count++;
|
|
hlines << " " << row_count; // ... and one below the title
|
|
for (const auto& type : group.m_types) {
|
|
const Ktype& k = type_to_katom_instance(type.m_type);
|
|
rows << static_cast<int>(k.m_type) << b << k.m_name << b << type.m_pattern << b;
|
|
if (show_regex) {
|
|
rows << katom_regex_cell(k) << b;
|
|
}
|
|
rows << type.m_description << " ||\n";
|
|
row_count++;
|
|
}
|
|
}
|
|
kt << "@table "
|
|
<< ":number false "
|
|
<< ":hline head bottom" << hlines.str() << " :leading .95 ";
|
|
if (show_regex) {
|
|
kt << ":cell_hpos c l :style r r t t r :column_width fit fit fit fit fill |\n"
|
|
<< "Index | Name | Pattern | Regex | Description ||\n";
|
|
} else {
|
|
kt << ":cell_hpos c l :style r r t r :column_width fit fit fit fill |\n"
|
|
<< "Index | Name | Pattern | Description ||\n";
|
|
}
|
|
kt << rows.str() << "table@\n";
|
|
return kt.str();
|
|
}
|