Initial commit: Klammertext source distribution
Curated source subset assembled by klammertext-dev's doc/make_dist.sh: the Klammermachine (mac), the Standard Klammer Set (sks), the commands (com), editor plugins and install guides (doc), a test subset (tst), and lib/bin placeholders. Builds with 'make -C com'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1
sks/kutil/.gitignore
vendored
Normal file
1
sks/kutil/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.d
|
||||
33
sks/kutil/Makefile
Normal file
33
sks/kutil/Makefile
Normal file
@@ -0,0 +1,33 @@
|
||||
# Klammertext sks/kutil/ Makefile
|
||||
# Improved version with automatic header dependency tracking
|
||||
|
||||
K := $(KLAMMERTEXT_HOME)
|
||||
KM := $(K)/mac
|
||||
|
||||
include $(KM)/env/makefile.env
|
||||
|
||||
# Source files
|
||||
SOURCES := kutil.cpp klammer_base.cpp
|
||||
OBJECTS := kutil.o klammer_base.o
|
||||
DEPFILES := kutil.d klammer_base.d
|
||||
|
||||
# Additional include paths
|
||||
LOCAL_CPPFLAGS := -I$(KM)
|
||||
|
||||
# Compiler flags for dependency generation
|
||||
DEPFLAGS = -MMD -MP -MF $(@:.o=.d)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
# Default target
|
||||
all : $(OBJECTS)
|
||||
|
||||
# Pattern rule for object files
|
||||
%.o : %.cpp
|
||||
$(CXX) -c $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(CXXFLAGS) $(DEPFLAGS) $< -o $@
|
||||
|
||||
clean :
|
||||
rm -f $(OBJECTS) $(DEPFILES) *.so *~
|
||||
|
||||
# Include generated dependency files (if they exist)
|
||||
-include $(DEPFILES)
|
||||
260
sks/kutil/js/kutil.js
Normal file
260
sks/kutil/js/kutil.js
Normal file
@@ -0,0 +1,260 @@
|
||||
|
||||
const K = (function() {
|
||||
function get(selector_or_node) {
|
||||
let result = selector_or_node;
|
||||
if (typeof selector_or_node === "string") {
|
||||
result = document.querySelector(selector_or_node);
|
||||
}
|
||||
//console.log("NODE: " + selector_or_node + " "
|
||||
// + typeof result + " " + result.nodeName);
|
||||
return result;
|
||||
}
|
||||
|
||||
function getv(selector) {
|
||||
return document.querySelectorAll(selector);
|
||||
}
|
||||
|
||||
function visible(node, new_state) {
|
||||
const nd = K.get(node);
|
||||
if (!nd) { console.warn("K.visible: element not found:", node); return false; }
|
||||
let result = nd;
|
||||
if (new_state === "hide") {
|
||||
nd.setAttribute("original-display", nd.style.display);
|
||||
nd.style.display = "none";
|
||||
} else if (new_state === "show") {
|
||||
nd.style.display = nd.getAttribute("original-display");
|
||||
} else if (new_state) {
|
||||
alert("Klammertext: Bad visible state for function K.visible: "
|
||||
+ new_state);
|
||||
} else {
|
||||
result = nd.style.display !== "none";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function fetch_html(url) {
|
||||
//console.log("fetch_html: " + url);
|
||||
return await (await fetch(url)).text();
|
||||
}
|
||||
|
||||
async function load(node, url) {
|
||||
const nd = get(node);
|
||||
nd.innerHTML = await fetch_html(url);
|
||||
}
|
||||
|
||||
function width(node, new_width) {
|
||||
let nd = K.get(node);
|
||||
if (nd === null || typeof nd === 'undefined') {
|
||||
console.warn("K.width: element not found:", node);
|
||||
return 0;
|
||||
}
|
||||
let result;
|
||||
if (new_width !== undefined) {
|
||||
if (!nd.style) {
|
||||
nd.style = {};
|
||||
}
|
||||
new_width = Math.max(0, new_width);
|
||||
nd.style.width = new_width + "px";
|
||||
result = new_width;
|
||||
} else {
|
||||
result = nd.offsetWidth;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function height(node, new_height) {
|
||||
let nd = K.get(node);
|
||||
if (nd === null) {
|
||||
console.warn("K.height: element not found:", node);
|
||||
return 0;
|
||||
}
|
||||
//console.log("height: " + node + " " + nd);
|
||||
let result = nd;
|
||||
if (new_height !== undefined) {
|
||||
new_height = Math.max(0, new_height);
|
||||
nd.style.height = new_height + "px";
|
||||
result = new_height;
|
||||
} else {
|
||||
result = nd.offsetHeight;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function top(node) {
|
||||
return K.get(node).getBoundingClientRect().top;
|
||||
}
|
||||
|
||||
function bottom(node) {
|
||||
return K.get(node).getBoundingClientRect().bottom;
|
||||
}
|
||||
|
||||
function left(node) {
|
||||
return K.get(node).getBoundingClientRect().left;
|
||||
}
|
||||
|
||||
function right(node) {
|
||||
return K.get(node).getBoundingClientRect().right;
|
||||
}
|
||||
|
||||
function attr(node, attr_name, new_value) {
|
||||
const nd = K.get(node);
|
||||
if (!nd) { console.warn("K.attr: element not found:", node); return undefined; }
|
||||
let result = new_value;
|
||||
if (new_value) {
|
||||
nd.setAttribute(attr_name, new_value);
|
||||
} else {
|
||||
result = nd.getAttribute(attr_name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function map(selector, func) {
|
||||
return [...(K.getv(selector))].map(func);
|
||||
}
|
||||
|
||||
function has_class(node_or_vector, cls) {
|
||||
if (!node_or_vector) { console.warn("K.has_class: null element"); return node_or_vector; }
|
||||
let result;
|
||||
if (node_or_vector.length) {
|
||||
[...node_or_vector].map(function(elt) {
|
||||
result = elt.classList.contains(cls);
|
||||
});
|
||||
} else {
|
||||
result = node_or_vector.classList.contains(cls);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function add_class(node_or_vector, cls) {
|
||||
if (!node_or_vector) { console.warn("K.add_class: null element"); return node_or_vector; }
|
||||
if (node_or_vector.length) {
|
||||
[...node_or_vector].map(function(elt) {
|
||||
elt.classList.add(cls);
|
||||
});
|
||||
} else {
|
||||
node_or_vector.classList.add(cls);
|
||||
}
|
||||
return node_or_vector;
|
||||
}
|
||||
|
||||
function remove_class(node_or_vector, cls) {
|
||||
if (!node_or_vector) { console.warn("K.remove_class: null element"); return node_or_vector; }
|
||||
if (node_or_vector.length) {
|
||||
[...node_or_vector].map(function(elt) {
|
||||
elt.classList.remove(cls);
|
||||
});
|
||||
} else {
|
||||
node_or_vector.classList.remove(cls);
|
||||
}
|
||||
return node_or_vector;
|
||||
}
|
||||
|
||||
function text(node, new_text) {
|
||||
let result = new_text;
|
||||
if (new_text) {
|
||||
K.get(node).textContent = new_text;
|
||||
} else {
|
||||
result = K.get(node).textContent;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function style(node, name, new_value) {
|
||||
const nd = K.get(node);
|
||||
let result;
|
||||
if (new_value) {
|
||||
nd.style.setProperty(name, new_value);
|
||||
result = new_value;
|
||||
} else {
|
||||
result = window.getComputedStyle(nd).getPropertyValue(name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function hpad(selector) {
|
||||
const node = K.get(selector);
|
||||
let result = parseInt(K.style(node, "padding-left"))
|
||||
+ parseInt(K.style(node, "padding-right"));
|
||||
return result;
|
||||
}
|
||||
return {add_class, attr, bottom, get, getv, has_class,
|
||||
height, hpad, left, load, map, remove_class, right,
|
||||
style, text, top, visible, width};
|
||||
|
||||
}());
|
||||
|
||||
|
||||
|
||||
function current_basename()
|
||||
{
|
||||
//var result = window.location.hash.toString().split("#")[1];
|
||||
let parts = window.location.href.split("#");
|
||||
let result;
|
||||
if (parts.length > 1) {
|
||||
result = parts[1];
|
||||
} else {
|
||||
result = "";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function search_not_active() {
|
||||
return document.activeElement.getAttribute("id") !== "search_input";
|
||||
}
|
||||
|
||||
function define_keypress(letter, id) {
|
||||
let uppercase = letter.charCodeAt();
|
||||
let lowercase = uppercase + 32;
|
||||
K.get("html").addEventListener(
|
||||
"keypress",
|
||||
function (event) {
|
||||
if (search_not_active()) {
|
||||
if (event.which === uppercase || event.which === lowercase) {
|
||||
K.get(id).click();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function update_page_title(element) {
|
||||
let element_text = element.firstChild;
|
||||
let regex = /((?:Part )?[0-9.]*)(.*)/;
|
||||
let parts = regex.exec(element_text);
|
||||
let title;
|
||||
if (element_text) {
|
||||
if (!parts[1]) {
|
||||
title = parts[2].trim();
|
||||
} else {
|
||||
title = parts[1] + " - " + parts[2];
|
||||
}
|
||||
K.text("title", title);
|
||||
}
|
||||
}
|
||||
|
||||
//window.scrollY + document.querySelector('#elementId')
|
||||
// .getBoundingClientRect().top // Y
|
||||
|
||||
function count_highlighted_toc_items() {
|
||||
const highlighted = K.map(
|
||||
".section-title",
|
||||
function (o) {
|
||||
return K.has_class(o, "highlight");
|
||||
});
|
||||
const count = highlighted.filter(
|
||||
function (x) {
|
||||
return x === true;
|
||||
}).length;
|
||||
return count;
|
||||
}
|
||||
|
||||
function remove_toc_highlighting() {
|
||||
[...K.getv(".section-title")].forEach(function (toc_item) {
|
||||
K.remove_class(toc_item, "highlight");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function displayed_text() {
|
||||
return K.get("#text") || K.get("#search_page") || K.get("#help_page");
|
||||
}
|
||||
56
sks/kutil/klammer_base.cpp
Normal file
56
sks/kutil/klammer_base.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "error.h"
|
||||
#include "klammer_base.h"
|
||||
#include "show.h"
|
||||
|
||||
std::string Klammer_base::get(std::string name)
|
||||
{
|
||||
//std::cout << "GET " << name << "\n";
|
||||
std::string result = m_machine.m_state.value(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Klammer_base::get(const char* name)
|
||||
{
|
||||
// msg() << "GETc " << name << "\n";
|
||||
std::string sname { name };
|
||||
std::string result = m_machine.m_state.value(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::string Klammer_base::html()
|
||||
{
|
||||
return "[html target undefined]";
|
||||
}
|
||||
|
||||
std::string Klammer_base::tex()
|
||||
{
|
||||
return "[tex target undefined]";
|
||||
}
|
||||
|
||||
std::string Klammer_base::txt()
|
||||
{
|
||||
return "[txt target undefined]";
|
||||
}
|
||||
|
||||
std::string Klammer_base::result()
|
||||
{
|
||||
std::string target = get("K_target");
|
||||
if (target == "html")
|
||||
return html();
|
||||
else if (target == "tex" or target == "pdf")
|
||||
return tex();
|
||||
else if (target == "txt")
|
||||
return txt();
|
||||
else
|
||||
throw Target_error("Unknown target: " + target);
|
||||
}
|
||||
|
||||
void Klammer_base::show(std::string klammer_name)
|
||||
{
|
||||
std::cout << "Arguments of klammer \"" << klammer_name << "\"\n"
|
||||
<< m_machine.m_state.describe() << "\n";
|
||||
}
|
||||
31
sks/kutil/klammer_base.h
Normal file
31
sks/kutil/klammer_base.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <filesystem>
|
||||
#include "eval.h"
|
||||
#include "machine.h"
|
||||
|
||||
#define VISIBLE __attribute__ ((visibility ("default")))
|
||||
|
||||
using argmap = std::map<std::string, std::string>;
|
||||
|
||||
class Klammer_base {
|
||||
public:
|
||||
Klammer_base(Machine& machine)
|
||||
: m_machine(machine)
|
||||
{};
|
||||
virtual ~Klammer_base() {};
|
||||
|
||||
std::string get(std::string);
|
||||
std::string get(const char* name);
|
||||
virtual std::string html();
|
||||
virtual std::string tex();
|
||||
virtual std::string txt();
|
||||
|
||||
std::string result();
|
||||
void show(std::string klammer_name);
|
||||
|
||||
Machine m_machine;
|
||||
};
|
||||
58
sks/kutil/klammer_base.py
Normal file
58
sks/kutil/klammer_base.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import pprint
|
||||
import re
|
||||
import kutil
|
||||
|
||||
def unescape_ktesc(s):
|
||||
"""Resolve KTESC escape markers back to original characters.
|
||||
Use this for argument values used programmatically (filenames, etc.)."""
|
||||
def replace(match):
|
||||
hex_chars = match.group(1)
|
||||
result = ''
|
||||
for i in range(0, len(hex_chars), 4):
|
||||
result += chr(int(hex_chars[i:i+4], 16))
|
||||
return result
|
||||
return re.sub(r'KTESC([0-9a-f]+)KTESC', replace, s)
|
||||
|
||||
class Klammer_base:
|
||||
def __init__(self, K):
|
||||
#args = {k:escape(v) for k, v in K.__dict__.items()
|
||||
args = {k:v for k, v in K.__dict__.items()
|
||||
if not k.startswith('__')}
|
||||
|
||||
for key in args:
|
||||
setattr(self, key, args[key])
|
||||
|
||||
#setattr(self, "_klammer_name", klammer_name)
|
||||
#pprint.pprint(self.__dict__)
|
||||
|
||||
|
||||
def html(self):
|
||||
#return '[{}: HTML]'.format(self.__class__.__name__)
|
||||
return None
|
||||
|
||||
def tex(self):
|
||||
#return '[{}: LaTeX]'.format(self.__class__.__name__)
|
||||
return None
|
||||
|
||||
def txt(self):
|
||||
#return '[{}: Plain text]'.format(self.__class__.__name__)
|
||||
return None
|
||||
|
||||
def show(self, label=""):
|
||||
if label:
|
||||
print(label)
|
||||
pprint.pprint(self.__dict__)
|
||||
|
||||
def __str__(self):
|
||||
result = None
|
||||
if self.K_target == 'html':
|
||||
result = self.html()
|
||||
elif self.K_target in {'tex', 'pdf'}:
|
||||
result = kutil.escape(self.tex())
|
||||
elif self.K_target == 'txt':
|
||||
result = self.txt()
|
||||
if result is None:
|
||||
#print(self.__dict__)
|
||||
raise Exception(
|
||||
f'Target "{self.K_target}" is undefined for klammer "@{self.K_klammer}"')
|
||||
return result
|
||||
173
sks/kutil/kutil.cpp
Normal file
173
sks/kutil/kutil.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include "error.h"
|
||||
#include "util.h"
|
||||
#include "katom.h"
|
||||
#include "kutil.h"
|
||||
#include "show.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
std::string read_file(std::string filename)
|
||||
{
|
||||
std::ifstream stream {};
|
||||
std::ostringstream buffer {};
|
||||
stream.open(filename);
|
||||
if (stream.is_open()) {
|
||||
stream >> buffer.rdbuf();
|
||||
} else {
|
||||
std::cout << "File not opened\n";
|
||||
}
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
|
||||
std::string klammertext_dir()
|
||||
{
|
||||
std::string khome { "KLAMMERTEXT_HOME" };
|
||||
char* kdir = getenv(khome.c_str());
|
||||
if (kdir == nullptr) {
|
||||
std::stringstream ss {};
|
||||
ss << "The environment variable " << q_(khome) << " is not defined";
|
||||
throw Environment_error(ss.str());
|
||||
}
|
||||
return kdir;
|
||||
}
|
||||
|
||||
bool in_subset(std::string base, std::vector<std::string> subset)
|
||||
{
|
||||
return subset.empty() ||
|
||||
(std::find(subset.begin(), subset.end(), base) != subset.end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<std::string> sks_files_of_type(std::string extension, std::vector<std::string> subset)
|
||||
{
|
||||
bool dbg = false;
|
||||
std::string kdir = klammertext_dir();
|
||||
std::vector<std::string> result {};
|
||||
if (dbg) msg() << "Extension: " << extension << "\n";
|
||||
for (auto base : sks_basenames) {
|
||||
std::string ext_dir { kdir + "/sks/" + base + "/" + extension };
|
||||
if (file_exists(ext_dir)) {
|
||||
std::string list_filename { ext_dir + "/list.txt" };
|
||||
if (file_exists(list_filename)) {
|
||||
if (dbg) msg() << " List: " << list_filename << "\n";
|
||||
std::string list = string_from_file(list_filename);
|
||||
for (auto s : regex_split(list, std::regex(R"(\n+)"), true)) { // split_lines(list)) {
|
||||
if (s[0] == '#')
|
||||
continue;
|
||||
std::string ext_filename = { ext_dir + "/" + s };
|
||||
if (!file_exists(ext_filename)) {
|
||||
std::string msg = "File " + ext_filename + " in " + list_filename + " does not exist";
|
||||
throw Internal_error(msg);
|
||||
}
|
||||
if (in_subset(fs::path(s).stem(), subset)) {
|
||||
if (dbg) msg() << " Add: " << ext_filename << "\n";
|
||||
result.push_back(ext_filename);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto f : get_files_in_directory(ext_dir)) {
|
||||
if (f[f.size()-1] != '~') {
|
||||
if (in_subset(fs::path(f).stem(), subset)) {
|
||||
result.push_back(ext_dir + "/" + f);
|
||||
if (dbg) msg() << " Add: " << f << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string find_kt_file(std::string filename)
|
||||
{
|
||||
std::string result {};
|
||||
std::vector<std::string> prefix {"", "kt/"};
|
||||
std::vector<std::string> suffix {"", ".kt"};
|
||||
std::vector<std::string> all_possible {};
|
||||
for (auto p : prefix) {
|
||||
for (auto s : suffix) {
|
||||
std::string possible { p + filename + s };
|
||||
//std::cout << "Checking: " << possible << "\n";
|
||||
if (file_exists(possible, false)) {
|
||||
result = possible;
|
||||
all_possible.push_back(possible);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result.size() > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result.size() == 0) {
|
||||
std::cout << "Filename \"" << filename << "\" does not exist and\ndoes not match any of the default patterns:\n";
|
||||
for (std::string f : all_possible) {
|
||||
std::cout << " " << f << "\n";
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
//std::cout << " Found: " << result << "\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::string caption_marker(std::string name, std::string caption, std::string delimiter)
|
||||
{
|
||||
std::string result {caption};
|
||||
if (caption.size() > 0) {
|
||||
result = delimiter + caption;
|
||||
}
|
||||
result = caption_delimiter + name + caption_delimiter + result + caption_delimiter;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
std::string process(Machine& M, std::string target, fs::path source_filename,
|
||||
bool post_process, bool unescape_chars)
|
||||
{
|
||||
// Text text(source_filename, false, false);
|
||||
Source source;
|
||||
source.read(source_filename
|
||||
text.parse();
|
||||
M.preserve_parse(text);
|
||||
M.extract_definitions(text);
|
||||
M.apply(target, text);
|
||||
std::string processed = M.to_string(target, text, post_process, unescape_chars);
|
||||
//processed = modify_whitespace_string(processed);
|
||||
//M.restore_parse(text);
|
||||
return processed;
|
||||
}
|
||||
|
||||
std::string process(Machine& M, std::string target, std::string source_text,
|
||||
bool post_process, bool unescape_chars)
|
||||
{
|
||||
Text text(source_text, false, false);
|
||||
text.parse();
|
||||
M.preserve_parse(text);
|
||||
M.extract_definitions(text);
|
||||
M.apply(target, text);
|
||||
std::string processed = M.to_string(target, text, post_process, unescape_chars);
|
||||
//M.restore_parse(text);
|
||||
return processed;
|
||||
}
|
||||
|
||||
std::string process(Machine& M, std::string target,
|
||||
std::string source_text, fs::path source_filename,
|
||||
bool post_process, bool unescape_chars)
|
||||
{
|
||||
Text text({source_text}, {source_filename}, false, false);
|
||||
text.parse();
|
||||
M.preserve_parse(text);
|
||||
M.extract_definitions(text);
|
||||
M.apply(target, text);
|
||||
std::string processed = M.to_string(target, text, post_process, unescape_chars);
|
||||
//M.restore_parse(text);
|
||||
return processed;
|
||||
}
|
||||
*/
|
||||
35
sks/kutil/kutil.h
Normal file
35
sks/kutil/kutil.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "machine.h"
|
||||
#include "file.h"
|
||||
|
||||
inline
|
||||
std::vector<std::string> sks_basenames { // removed "book"
|
||||
"kutil", "color", "document", "block", "font", "link", "section",
|
||||
"list", "image", "table", "code", "target", "date" };
|
||||
|
||||
const std::string caption_delimiter = "__CAPTION__";
|
||||
|
||||
std::string read_file(std::string filename);
|
||||
|
||||
std::string klammertext_dir();
|
||||
|
||||
std::vector<std::string> old_sks_files_of_type(std::string extension);
|
||||
std::vector<std::string> sks_files_of_type(std::string extension, std::vector<std::string> subset={});
|
||||
|
||||
std::string caption_marker(std::string name, std::string caption, std::string delimiter=" – ");
|
||||
|
||||
std::string find_kt_file(std::string filename);
|
||||
|
||||
std::string process(Machine& M, std::string target, std::string source_text,
|
||||
bool post_process=false, bool unescape_chars=false);
|
||||
|
||||
std::string process(Machine& M, std::string target, fs::path source_filename,
|
||||
bool post_process=false, bool unescape_chars=false);
|
||||
|
||||
std::string process(Machine& M, std::string target,
|
||||
std::string source_text, fs::path source_filename,
|
||||
bool post_process, bool unescape_chars);
|
||||
|
||||
63
sks/kutil/kutil.k
Normal file
63
sks/kutil/kutil.k
Normal file
@@ -0,0 +1,63 @@
|
||||
@@show s : @eval :cpp show show @ @@
|
||||
|
||||
@@caption_arguments :
|
||||
:caption
|
||||
:number.bool true
|
||||
:caption_side.side bottom
|
||||
:caption_font.font i
|
||||
:caption_font_size.float .9
|
||||
@@
|
||||
|
||||
@@reference spec | name :
|
||||
__REF__*spec*__*name*__
|
||||
@@
|
||||
|
||||
@@@argtype number | a number
|
||||
:pattern 'float'^|'int'
|
||||
:python_cast (lambda s: float(s))
|
||||
@@@
|
||||
|
||||
@@@argtype length | a length specifier
|
||||
#:pattern f^|none^|'float'w^|'float'h^|'int'px^|'float'em^|'int'pt
|
||||
# The last pattern is a string, used where possible to determine its length
|
||||
:pattern f^|none^|'float'w^|'float'h^|'int'px^|'float'em^|'int'pt^|"[^^"]+"^|'float'pw^|'float'ph
|
||||
# :python_cast (lambda s : __import__("kutil").parse_length("tex", s))
|
||||
@@@
|
||||
|
||||
@@@argtype lengths | a list of lengths
|
||||
:pattern ('length'^|\s+)*
|
||||
#:python_cast (lambda s : [__import__("kutil").parse_length("tex", e) for e in s.split()])
|
||||
@@@
|
||||
|
||||
@@@argtype side | a side of a box
|
||||
:pattern top^|right^|bottom^|left
|
||||
@@@
|
||||
|
||||
@@@argtype hpos | horizontal position
|
||||
:pattern left^|center^|right^|none
|
||||
@@@
|
||||
|
||||
@@@argtype figure_id |
|
||||
an identifier for a figure.
|
||||
|
||||
The identifier can be in one of six forms:
|
||||
|
||||
before
|
||||
before <offset-to-figure>
|
||||
after
|
||||
after <offset-to-figure>
|
||||
<image-basename>
|
||||
<id>
|
||||
|
||||
The "before" value means the figure before this place in the text;
|
||||
the number indicates the number of figures behind that place in the
|
||||
text. This means that "before" is equivalent to "before 1". The
|
||||
"after" value uses an offset in the same way but counting forewards.
|
||||
|
||||
For images, the <image-basename> argument is the basename argument to
|
||||
the ^@image klammer and can be used as an identifier.
|
||||
|
||||
An <id> is the value of the ^:id argument for an image.
|
||||
|
||||
^:pattern before(?^:\s+\d+)?^|after(?^:\s+\d+)?^|[-\w]+
|
||||
@@@
|
||||
209
sks/kutil/kutil.py
Normal file
209
sks/kutil/kutil.py
Normal file
@@ -0,0 +1,209 @@
|
||||
import os
|
||||
import re
|
||||
import inspect
|
||||
import html_util
|
||||
from html_util import E
|
||||
|
||||
|
||||
def black_text():
|
||||
return "\033[0;30m"
|
||||
|
||||
def blue_text(text):
|
||||
return f"\033[0;34m{text}\033[0m"
|
||||
|
||||
def red_text(text):
|
||||
return f"\033[31m{text}\033[0m"
|
||||
|
||||
def msg(text=""):
|
||||
frame = inspect.currentframe().f_back
|
||||
print(blue_text(f"[{os.path.basename(frame.f_code.co_filename)}:{frame.f_lineno}]"), text)
|
||||
|
||||
|
||||
def escape(s):
|
||||
result = s
|
||||
|
||||
#result = re.sub(r"\b", r"\b", result)
|
||||
#result = re.sub(r"\t", r"\t", result)
|
||||
|
||||
#result = re.sub("\f", r"\\f", result)
|
||||
#result = re.sub("\v", r"\\v", result)
|
||||
#result = re.compile("\\(.)").sub(r"\\\1", result)
|
||||
return result
|
||||
|
||||
def klammertext_dir():
|
||||
var = 'KLAMMERTEXT_HOME'
|
||||
kdir = os.environ.get(var)
|
||||
if kdir is None:
|
||||
raise Exception(
|
||||
f"The environment variable {var} must be defined as the top-level Klammertext directory")
|
||||
return kdir
|
||||
|
||||
def cache_directory(relative_pathname, basename):
|
||||
directory = relative_pathname
|
||||
if not os.path.isdir(directory):
|
||||
os.makedirs(directory)
|
||||
result = directory + "/_klammertext_cache/" + basename
|
||||
return result;
|
||||
|
||||
def sks_dirs():
|
||||
result = 'kutil book document block font link section list image table code color'.split()
|
||||
k = klammertext_dir()
|
||||
result = [[e, f"{k}/sks/{e}"] for e in result]
|
||||
return result
|
||||
|
||||
def sks_files_of_type(extension):
|
||||
result = []
|
||||
for base, sks_dir in sks_dirs():
|
||||
filename = f'{sks_dir}/{base}.{extension}'
|
||||
if os.path.exists(filename):
|
||||
result.append([base, filename])
|
||||
return result
|
||||
|
||||
def make_dir_if_necessary(d, delete_contents=False):
|
||||
if not os.path.exists(d):
|
||||
os.makedirs(d)
|
||||
if delete_contents:
|
||||
os.system(f'rm -rf {d}/*')
|
||||
|
||||
def protect_klammertext_special_characters(text):
|
||||
result = text
|
||||
result = re.compile(r"\^").sub("H̅", result)
|
||||
result = re.compile(r"@").sub("A̅", result)
|
||||
result = re.compile(r":").sub("̅C̅", result)
|
||||
result = re.compile(r"\|").sub("̅B̅", result)
|
||||
return result
|
||||
|
||||
# When did this ever make sense? Before the new word patterns, probably.
|
||||
|
||||
def bar_delimiter():
|
||||
return "\3"
|
||||
|
||||
def double_bar_delimiter():
|
||||
return "\4"
|
||||
|
||||
def caption_delimiter():
|
||||
return "__CAPTION__"
|
||||
|
||||
def caption_marker(name, caption, delimiter=" - "):
|
||||
caption = re.sub("\n", " ", caption)
|
||||
d = caption_delimiter()
|
||||
if caption:
|
||||
caption = f"{delimiter}{caption}"
|
||||
return f"{d}{name}{d}{caption}{d}"
|
||||
|
||||
def rest_args(s, dimensions=1):
|
||||
if s.endswith("||"):
|
||||
s = s[:-2]
|
||||
s = re.sub(r"\t", r"\\t", s)
|
||||
double_bar_pat = re.compile(r"\|\|")
|
||||
bar_pat = re.compile(r"\s*\|\s*")
|
||||
parts = [e.strip() for e in double_bar_pat.split(s.strip())]
|
||||
for p in parts:
|
||||
elts = bar_pat.split(p.strip())
|
||||
elts = ["~" if (e.strip() == "") else e.strip() for e in elts]
|
||||
result = [bar_pat.split(e) for e in parts]
|
||||
if dimensions == 1:
|
||||
result = result[0]
|
||||
return result
|
||||
|
||||
def parse_length(target, s, rel_fraction):
|
||||
def choose(html_value, tex_value):
|
||||
return html_value if target == "html" else tex_value
|
||||
pat = re.compile(r'(?:([0-9.]+)([a-z]+))|(none|f)|(?:"([^"]+)")')
|
||||
#pat = re.compile(r'(?:([0-9.]+)([a-z]+))|(none|f)|(?:{([^}]+)})')
|
||||
|
||||
|
||||
#pat = re.compile(r'(?:([0-9.]+)([a-z]+))|(f)')
|
||||
match = pat.match(s)
|
||||
if match is None: # But already checked by the klammer
|
||||
raise Exception(f'The argument "{s}" is not a length')
|
||||
|
||||
#print(match.groups())
|
||||
num, units, fit, text = match.groups()
|
||||
num = float(num) if num else ""
|
||||
|
||||
num *= rel_fraction
|
||||
|
||||
if fit:
|
||||
result = "f" # choose("100vw", "\\textwidth")
|
||||
elif text:
|
||||
text = re.compile(r"\{\}\\textbackslash\{\}").sub(r"\\", text)
|
||||
if text[0] == "-":
|
||||
result = choose("", f"\\textwidth - \\widthof{{ {text[1:]}}}")
|
||||
else:
|
||||
result = choose("", f"\\widthof{{ {text}}}")
|
||||
elif units == "w":
|
||||
result = choose(f"{100 * num}vw", f"{num}\\textwidth")
|
||||
elif units == "h":
|
||||
result = choose(f"{100 * num}vh", f"{num}\\textheight")
|
||||
elif units == "pw":
|
||||
result = choose(f"{100 * num}vw", f"{num}\\paperwidth")
|
||||
elif units == "ph":
|
||||
result = choose(f"{100 * num}vh", f"{num}\\paperheight")
|
||||
elif units == "px":
|
||||
result = choose(f"{round(num)}", f"{num}px")
|
||||
else:
|
||||
result = f"{num}{units}"
|
||||
result = re.sub("\t", "\\t", result)
|
||||
return result, num, units
|
||||
|
||||
|
||||
def old_parse_length(target, s):
|
||||
pat = re.compile(r'(?:([0-9.]+)([a-z]+))|(none|f)|("([^"]+)")')
|
||||
match = pat.match(s)
|
||||
if match is None:
|
||||
raise Exception(f'The argument "{s}" is not a length')
|
||||
if match.group(3) in {"none", "f"}:
|
||||
return "f"
|
||||
if s[0] == '"':
|
||||
if target in {"tex", "pdf"}:
|
||||
return f"\\widthof{{{match.group(5)} }}"
|
||||
else:
|
||||
return ""
|
||||
num, units = match.groups()[:2]
|
||||
if float(num) == 0:
|
||||
return None
|
||||
result = f"{num}{units}"
|
||||
if target in {"tex", "pdf"}:
|
||||
if units == "w":
|
||||
result = fr"{num}\textwidth"
|
||||
elif units == "h":
|
||||
result = fr"{numb}\textheight"
|
||||
elif target in {"html"}:
|
||||
scale_x = None
|
||||
scale_y = None
|
||||
if units == "w":
|
||||
#result = fr"{int(float(num)*100)}vw"
|
||||
result = fr"calc({int(float(num)*100)}vw - 2rem)"
|
||||
#result = 0
|
||||
scale_x = num
|
||||
result = "none"
|
||||
elif units == "h":
|
||||
result = fr"{int(float(num)*100)}vw"
|
||||
#result = 0
|
||||
scale_y = num
|
||||
result = "none"
|
||||
result = [result, scale_x, scale_y]
|
||||
else:
|
||||
raise Exception(f"Uknown length: '{s}'")
|
||||
return result
|
||||
|
||||
def parse_lengths(target, s):
|
||||
# Fill strings with space character for the split:
|
||||
#print("parse_lengths:", s)
|
||||
def replace(match):
|
||||
return re.sub(r"\s", "~", match.group(1))
|
||||
result = re.compile(r'("[^"]+")').sub(replace, s)
|
||||
#print(result, result.split())
|
||||
result = [parse_length(target, e)[0] for e in result.split()]
|
||||
return result
|
||||
|
||||
paragraph_separator_re = re.compile(r'\n *\n', re.S)
|
||||
|
||||
def format_for_paragraphs(s):
|
||||
result = s
|
||||
if len(paragraph_separator_re.findall(result)) > 0:
|
||||
result = '\n\n{}\n\n'.format(result)
|
||||
return result
|
||||
|
||||
|
||||
43
sks/kutil/show.cpp
Normal file
43
sks/kutil/show.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include "util.h"
|
||||
#include "eval.h"
|
||||
#include "machine.h"
|
||||
#include "klammer_base.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
extern "C" VISIBLE string show(std::map<std::string, std::string> args)
|
||||
{
|
||||
cout << "SHOW\n" << args << "\n";
|
||||
|
||||
return "SHOW";
|
||||
|
||||
string s = getarg(args, "s");
|
||||
string target = getarg(args, "_target");
|
||||
|
||||
string kt = string_replace(s, "\\^\\^", "__CIRCUM__");
|
||||
kt = string_replace(kt, "\\^", "");
|
||||
kt = string_replace(kt, "__CIRCUM__", "HAT");
|
||||
//kt = "@lit " + kt + " @";
|
||||
cout << "Modified: " << kt << "\n";
|
||||
|
||||
Machine M {};
|
||||
M.state.state.set("K_program_name", "show");
|
||||
Text T(true);
|
||||
|
||||
T.read_string(kt);
|
||||
T.parse();
|
||||
M.extract_definitions(T);
|
||||
M.apply(target, T);
|
||||
string processed = M.to_string(target, T);
|
||||
|
||||
stringstream ss {};
|
||||
//ss << s << sp_arrow << processed;
|
||||
|
||||
ss << "@t " << s << " @ | " << processed;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user