Files
klammertext/doc/klammertext_for_llm.md
T
ackandClaude Fable 5.1 0ead40f272 Width and height argtypes, kdesc --argtypes from ref/, txt @define/@left_right/@columns, kdiag --check value validation (from dev d6993110e9b3)
Also: the klammertext argtype (descriptions kept as source), txt table
wrapping and :hpos/:offset, @c between ‹ ›, txt section headings, the
ktext newline-only trim, capitalize(), and the argtype test suite.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:59:49 +02:00

26 KiB
Raw Blame History

Klammertext — reference for an LLM assistant

For a language model helping someone author Klammertext documents. Dense and example-led on purpose: it is not a tutorial, and it motivates nothing. A person wanting to learn Klammertext should read the other documents in doc/.

Read §1 and §2 before writing any Klammertext. §2 exists because a model that has not seen this language will pattern-match it to LaTeX, Markdown, or Lisp and produce confidently wrong syntax.

The klammers named here are the Standard Klammer Set (SKS), which is what loads by default. A klammer set is user-extensible, so the authority for what exists in any particular setup is kdesc, not this file — see §1. Where this file and kdesc disagree, kdesc is right and this file is stale.

Vocabulary: text is katomized into katoms (the indivisible input units) by katom rewrite rules; a klammer is the function-like operator; a target is an output format; a klammerset is a named group of klammer definitions. The three user modes are author (@), designer (@@), programmer (@@@).


1. Do not guess — the machine will tell you

Klammertext is introspectable. kdesc prints the live state of the machine's data structures in digested form. Query it instead of inferring from examples.

ask command
every klammer, with parameters and description kdesc -k
klammers whose name or description mentions X kdesc -k X
the output targets kdesc -t
argument types and what each accepts kdesc --argtypes
shared option groups and who uses them kdesc --optionsets
which targets each klammer can render to kdesc --coverage
what a user's own file defines (against the SKS) kdesc -i FILE -k
the klammersets available, with provenance kdesc --klammersets
how to write accented / non-English characters kdesc -c
how to write any Unicode character, with examples kdesc -u
installed typefaces; install or preview fonts kdesc --typeface, --typeface help
the usage of any command run it with no arguments (exit 0)

kdesc -k <name> before using an unfamiliar klammer costs one command and replaces a guess. The parameter list it prints is authoritative: a parameter shows as name.type default, an option as :name.type default.

Check a document without rendering it:

kdiag --process --check doc.kt

reports every klammer application whose name, argument count, option names, target coverage, or literal argument value is wrong (a value that fails its argument type, such as :width .5h) — including inside @cond branches this render will not take and inside klammer bodies never applied. A value holding a nested klammer or a *variable* is computed at run time and is not judged. It applies nothing. Use it before claiming a document is correct.

Inspect how text is parsed: kdiag --type '<text>' shows the katoms and their types; kdiag --args '<text>' shows how text splits into arguments; kdiag --rewrite '<text>' names the rewrite rule that fired. This settles arguments about what a piece of syntax means. kdiag deliberately loads no klammerset; give it @read sks/sks.k @ in the input if it needs one.


2. What you will get wrong if you assume

assumption reality
It is a macro language like TeX No catcodes. Katom structure is fixed when a file is read and cannot be changed by anything later. This is why static checking works.
\command{arg} @name arg @ — an @ opens, and a closing delimiter is required.
Arguments are {a}{b} Positional arguments are separated by | — @sup 2 | 3 @.
Options are [key=value] :name value, anywhere in the argument list — @link URL :text Click @.
% starts a comment # removes the rest of the line. ## removes the rest of the file. #[ … ]# removes a block (nestable).
*x* is emphasis (Markdown) @i x @ is emphasis. *name* is variable substitution inside a klammer body; in prose write ^* for a literal asterisk.
\\ escapes ^ quotes: ^@ ^| ^# ^^ ^: ^* and ^ before any ASCII punctuation gives that character literally.
&, %, _, <, \ must be escaped for LaTeX/HTML Never. The machine escapes the target's characters. An author quotes only Klammertext's own specials. Inside @c/@code nothing needs quoting.
-- is two hyphens Under every target -- is an en dash, --- an em dash, / `''` curly double quotes, ``/'curly single quotes,a non-breaking space (the LaTeX conventions). A literal--flagis^-^-flag; a literal tilde ^. Verbatim text (@c, @code, ^'…'^`) is exempt.
Whitespace is insignificant It is content, and it is preserved. Blank lines separate paragraphs.
Text is nested in braces Text lives between the opening and closing delimiter with no wrapper.
The document body is the first argument For @document the body goes in the :text option (or :files). A positional body is an error.
x@i is "x, then italic" A letter may not touch an opening @: x@i could be the close of a klammer named x. Write x @i. Brackets and sentence punctuation may abut: (@i-x), @i x @.
:files ch1 names a file ch1 A bare word means kt/ch1.kt beside the root file. A name with a directory or extension is a pathname.
@font sets the font Retired. @typeface name | text @ is the temporary change; @document :serif_typeface etc. set the roles.

3. Core syntax

Three prefix tiers, distinguished by how many @:

written tier meaning
@name application use a klammer
@@name definition define a klammer
@@@name system modify the machine (targets, argument types, state, klammersets)

Closing a klammer. The close is @ for the application tier, @@ for definitions, @@@ for system commands. A close may be named by putting the klammer's name immediately before it: @i text i@.

When to name the close. Use a bare @ by default. Use the named form name@ when the body contains other klammer calls: if delimiters are unbalanced, a named close lets the machine say which klammer was left open. Do not name closes on short, klammer-free bodies — it is noise.

@i just text @                       ← bare close
@s1 A @c fragment c@ heading s1@     ← name it: the body contains a klammer call

Arguments.

@name positional1 | positional2 :option value :flag @
  • | separates positional arguments.
  • :name value is an optional argument; it may appear anywhere in the list. The value runs to the next | or :option.
  • :name written alone takes the argument type's "alone" value — for a boolean that is true, so :number means :number true; @table :hline means :hline all.
  • An optional argument that is not written takes its default (kdesc shows it).

Abbreviated form. @name-arg1-arg2 is @name arg1 | arg2 @, valid only while every argument is letters and digits: @i-italic, @sup-2-3, @date@.

Literal klammers take their content uninterpreted. It is not examined for @, |, #, ^ or *, so the machine cannot find the end by counting and the named close is required:

@code
  int x = a[0];   // # and @ are safe in here
code@

@c a | b c@      ← inline: same rule, and the close must be "c@"

@c and @code are the in-line and block forms of the same thing. Code that lives in a file is @source_listing, never @code (which has no filename argument):

@source_listing src/derivations.cpp @                       ← the whole file
@source_listing src/derivations.cpp :marker fromRdd55 @     ← one region of it

:marker P lists what lies between two lines that are exactly //P in column one. A marker missing or not appearing exactly twice is an error, never a fallback. kdesc -k <name> shows a literal parameter as text.literal.

Whitespace and removal (#). # rest of line, ## rest of file, #[ … ]# block; #- removes surrounding whitespace, #+N inserts N spaces, #/N inserts N newlines (default 1).

Characters (^). One mechanism, four readings, in this order:

form gives example
^P (P any ASCII punctuation) that character literally ^-^-flag, ^~, ^^, ^@
^'…'^ a literal span: raw target markup passed through untouched ^'$\cdot$'^
^UUUU^ (4–6 hex digits) the code point ^0418^ И, ^263A^ ☺, ^13000^ 𓀀
^UUUU UUUU …^ a run of code points, one caret pair ^9053 53EF 9053^ 道可道
^ + base letter + mark a diacritic ^e' é, ^u" ü, ^n~ ñ, ^cc ç, ^oh ô
^xx^ mnemonic an extended-Latin letter ^s^ ß, ^ae^ æ, ^o^ ø

kdesc -c prints the diacritic, extended-Latin and pinyin tables; kdesc -u the code-point notation with examples. The one ^ exception: ^' opens a literal span, so a literal apostrophe is ^0027^. A caret form that names nothing is an error, not silence.


4. A complete document

@document
:title Normalizing to OpenEXR Standard Attributes
:subtitle Predictability and provenance
:author Joseph Goldstone
:date @date@
:structure article
:point_size 12
:text

@s1 Principles s1@

Ordinary paragraph text. A blank line starts a new paragraph.
Use @i emphasis @ and @b bold @ inline, and @c code_fragment c@ for code.
A footnote @footnote Its text. @ is written inline.

@link https://openexr.com/ :text the OpenEXR site @

@ul
  first item
| second item
| third item
ul@

# A comment: removed, and never reaches the output.

@image figure_name :width .95w :caption A caption @

@table :caption Sizes :column_width fit fill
  | Name | Description
  || alpha | the first
  || beta | the second
table@

@

A long document names its chapters instead of holding them:

@document :title The Book :structure book :files preface one two @

which reads kt/preface.kt, kt/one.kt, kt/two.kt beside the root file.

Render it:

ktext doc.kt -t pdf          # also: -t html, -t tex
ktext doc.kt -t html         # writes a DIRECTORY doc/ containing index.html (+css, fonts, js)
ktext doc.kt -t txt -d       # -d displays on stdout instead of writing -- but this
                             #   example errors under txt/ansi: @image has no txt
                             #   definition (§5), and coverage is never silently degraded
ktext -s '@i-x' -t html -d   # -s takes a string; no file needed
ktext doc.kt -s '@@version : 3.2.1 @@' -t pdf   # -s is processed BEFORE the files:
                                                #   define a klammer the document uses
  • Output goes next to the input file unless -o names a directory and/or basename. String-only input writes to the current directory.
  • -s, -t, -o take exactly one argument (quote spaces). --klammersets takes a list; end it with -- if filenames follow.
  • Filenames may contain spaces: quote them on the command line; in a list (:files, several input files) separate names with a standalone /.
  • @document :structure is plain (default; no TOC), article (in-page TOC), book (cover, navigation panel in html; chapters, recto/verso in pdf). Headings @s1… are \chapter in a book and \section otherwise.
  • Role typefaces: :serif_typeface, :sans_typeface, :mono_typeface (family name or store directory name, e.g. EB Garamond or eb-garamond); :text_scale scales all text; :point_size, :leading, :paper_size, :landscape, :two_column. kdesc -k document lists all forty.
  • Outside a @document nothing makes paragraphs: a fragment rendered on its own writes @par text @ explicitly.

5. Klammers most used when authoring

Confirm parameters with kdesc -k <name> — this is names and intent only. Not every klammer exists for every target (kdesc --coverage); txt and ansi are the limited ones.

inline @i italic, @b bold, @t typewriter, @u underline, @s sans, @r roman, @ri roman italic, @c inline code (literal; close c@), @sup base | script @, @sub, @ts scale | text @ resize, @color rgb :text, @typeface name | text :scale @, @cjk (needs an installed CJK typeface)
structure @s1…@s7 numbered headings, @h1…@h7 unnumbered, @part, @preface, @par, @newpage, @vspace lines, @vfill, @nl line break
blocks @code (inline listing), @source_listing file :marker P, @quote text | attribution @, @note, @indent :left :right, @center, @right, @block to | content @ (absolute placement), @columns items :n @, @multicolumn text :n :gap @, @lines
lists @ul :bullet, @ol :initial, @define :style term/definition, @qa question | answer @ — items separated by |
tables @table — cells by |, rows by || (§6); @rowcolor
figures & references @image basename :width :caption, @image_grid, @fig id, @tbl id, @reference spec | name @, @target id :text
footnotes & links @footnote text @, @link target :text :footnote, @email name | domain @
dates @date, @datetime (:days N, :lang de, :number), @year, @timestamp
symbols @copyright, @registered, @trademark, @euro amount, @pi, @rightarrow, @dot, @sp (space)

Bar depth is the dimension: one | separates items, two || separates rows. Argument order is free for options, fixed for positionals.

Coverage today (kdesc --coverage; 89 klammers): html, tex and pdf cover nearly all. txt and ansi cover 40, among them @b @i @t @u @s @r @ri @ts @typeface, @s1–@s7, @par @newpage @vspace @vfill @nl, @indent @right @lines @qa, @ul @ol, @table @tbl @reference @define @left_right, @footnote @link, @date @datetime, @c. Not for txt: @code @source_listing @image @image_grid @quote @note @center @block @columns @multicolumn @sup @sub @color @h1–@h7. @u has no html definition. A klammer applied under a target that lacks it is an error, never silently dropped.


6. Tables

@table :caption Quarterly :column_width fit fit fill :cell_hpos l r r :calc 3(1-2) = + 1-2(1-2)
  | Item | Q1 | Q2
  || Widgets | 10.5 | 12
  || Gadgets | 3 | 4.25
  || Total | |
table@

Cell indices are zero-origin and count the header row: 3(1-2) is the fourth row, second and third columns; 1-2(1-2) the two body rows above it; -1 is the last row or column. A ranged target repeats the calculation across the range (here: one sum per column).

  • First row is the header (:header false otherwise); header style :header_style i by default; :style r i b per column.
  • :column_width per column: fit (widest entry), fill (the remaining width, capped at the widest entry), a fraction of the text width (.2), or * (the remainder). fill may not combine with a fraction or *.
  • :cell_hpos l c r per column; :justify <cells> <pos>; … per cell.
  • Lines: default under the header and at the bottom; :hline/:vline replace them (all, none, or indexed ranges); :grid for all lines.
  • :colspan/:rowspan with the indexed-range syntax; :allow_break true makes a page-breaking table (pdf).
  • :calc — ;-separated <row>(<col>) = <op> <operands>, prefix + - * / folding left over the operands, which are cell ranges or numbers; display-precision (a printed total equals the sum of the printed lines); notes/calc_notation.md is the gallery. :format <cells> module.function formats cells with user Python. :decimal comma for comma decimals.
  • Placement: :hpos left|center|right|none, :offset 2em (inset from that margin); a caption goes above a table (:caption_side top default), below a figure. :number false for an unnumbered caption.

7. Targets

target output notes
html a directory <name>/ with index.html, css/, fonts/, js/ self-contained, fonts localized; book structure adds navigation, search
tex <name>.tex XeLaTeX source; fonts via fontspec from the font store
pdf <name>.pdf tex plus a xelatex run (TeX Live required)
txt <name>.txt plain text filled to Target_txt_width (72), blocks indented by Target_txt_indent (3); tables ruled and padded; captions numbered; 40 of 89 klammers (§5)
ansi txt + SGR escape sequences @b/@i/@u and table header styles shown by a terminal; identical to txt once the escape sequences are stripped; pick it with -t ansi -d; a terminal needs a font with an italic face (macOS Terminal: Menlo, not Monaco)

k, o, * also appear in kdesc -t: they are declaration pseudo-targets (interface, option set, all targets), not outputs.

State variables an author may set (@@@state Name :value V @@@ in the document, or -s): Language (en/de, for dates), Image_quality (JPEG quality of cached photographs, 85), Target_txt_width, Target_txt_indent, Image_default_width, Image_search_path.

Typefaces: the font store is closed-world (no network). The distribution ships eb-garamond, crimson-pro, open-sans, source-sans-3, inconsolata, source-code-pro, noto-sans-egyptian-hieroglyphs, noto-sans-cuneiform (see kdesc --typeface); more are installed with kdesc --typeface install <dir> into KLAMMERTEXT_FONTS. An unknown typeface is an error listing the available ones. CJK is deliberately not shipped.


8. Defining klammers (only if asked)

@@name.k arg1 | arg2 :opt.type default : Description of what it does @@
@@name.html :: <span>*arg1*</span> @@
@@name.tex  :: \textbf{*arg1*} @@
@@name.txt  :: *arg1* @@
  • .k declares the interface and documents it; per-target definitions inherit the parameter list with ::. Every target's list must agree.
  • *arg* substitutes an argument into the body; *Name* also reads a state or environment variable.
  • Several targets may share a body: @@name.html,tex :: … @@. A definition with no suffix applies to every target (the writer's macro form; its body is escaped for the target, so @@corp : Smith & Jones @@ is safe). .* written out is a claim of target independence.
  • Separators: : create, :: create inheriting parameters, ::: override an existing definition, :::: define a default a later : may replace.
  • Argument types: name.type — string (default), int, float, bool, list, width/height (a bare number = characters / lines in every target; units em pt px, w pw for widths, h ph for heights; the axes are checked), literal (raw content, named close required), rest (everything left, bars included; rest(2) = list of lists), and any @@@argtype the klammerset declares (kdesc --argtypes). An argtype may carry :default and :alone; a klammer may override the default, never the alone value.
  • Option sets (.o): a named group of optional parameters used by several klammers — @@name.k arg @caption_args :caption_side top @ : … @@ splices the SKS's caption options with one default overridden. Usable only in a .k parameter list. A klammer application inside a parameter list is an error.
  • @eval is the primitive behind every non-trivial klammer: @eval python_expr @ (module.function auto-imports; K holds the arguments), @eval :cpp lib.function @, @eval :shell cmd @, @eval :haskell src @; :cwd DIR runs it there (:cwd *K_input_dir*). A Python module is found next to the file that names it. The result is re-read as Klammertext: a result holding klammers is generator output and its text is escaped for the target; a result with none is renderer output and left alone. A literal @ in a result must be emitted as ^@.
  • A klammer whose tex body is target markup with a backslash must not be passed through a generator raw — the SKS carries such things as markers (@nl), resolved in an :after_apply phase.
  • Klammersets: @@@klammerset sym | description :requires sks :files a.k b.k @@@ in sym/sym.k; loaded with ktext --klammersets sym doc.kt (symbols only, resolved in the document's directory, then KLAMMERTEXT_KLAMMERSETS, then the distribution; several combine in order, later definitions deciding collisions via the separators above). kdesc -i file.k -k shows what a file defines against the SKS. --klammersets ref adds the reflection klammers (@diacritics_examples@, @unicode_examples@, …) that generate the reference tables kdesc -c/-u print.

9. Behaviour worth knowing before you debug something

  • @cond is strict about truth. True: true, True, 1. False: false, False, 0, empty. Anything else is an error, not "probably false".
  • @cond is resolved at application time and evaluates only the branch it selects. A @read/@eval in the other branch does not run. A top-level @cond *Flag* | … @ sees the variable's value.
  • A state variable is visible from the moment its declaration is read — in every klammer body applied afterwards and in the top-level text of later input units, but not in the top-level text of the same unit that declares it. Declare early (-s, a first file, a klammerset).
  • A definition inside a @cond branch does not register — in either branch. Define outside and select between applications.
  • @eval :shell and :haskell: stdout becomes document text; stderr does not (shown under -v 1); a nonzero exit is an error. Append || true if a nonzero exit is expected.
  • Arguments arrive as written, nested klammers already rendered; a macro escapes only its own body text. Never escape an output format's characters yourself, anywhere.
  • Typographic transforms are per katom and skip verbatim text: a -- split by a klammer is two hyphens; inside @c/@code nothing is transformed.
  • Recursion is guarded: a klammer reaching itself 200 deep is a named Recursion_error; a document needing more than 100 top-level passes is an error.
  • @eval results are trimmed; a klammer that must emit leading space or pure vertical space uses the txt/tex markers the SKS provides (phases.txt_nbsp_marker(), @vspace).
  • The html page cache (/dev/shm/_klammertext_cache/ on Linux) can serve a stale page after a change to a klammer's html; clear it when output looks old (@document :cache false disables it).

10. Diagnostics and what the commands print

  • Three cases only: logging (stderr, -v > 0), an error (stderr, then exit ≠ 0), the requested output (stdout). So ktext doc.kt -t txt -d | … is safe; nothing else lands in the document.
  • -v 1 reports every decision the user could not read off their input: which klammerset loaded and from where, which file a typeface or :files name resolved to, how the output path was built, an override of another set's definition. -v 2+ is an implementation trace.
  • An error stops the run and names a file, line and character; messages are justified to 80 columns. There is no warn-and-continue except two aggregated heuristics: unknown target coverage, and a table wider than the page.
  • "Word not parsed: …" means a piece of text matched no katom type — usually a letter touching a delimiter (§2) or a punctuation run needing ^.
  • Colour on stderr only when it is a terminal; NO_COLOR disables it. kdesc's -c/-u render through ansi on a terminal and txt in a pipe.

When something is wrong: kdiag --process --check doc.kt first (name, arity, option, literal-value and coverage faults without rendering); then kdesc -k <klammer> against what was written; then kdiag --type '<fragment>' if the question is how something is parsed; then ktext … -v 1 if the question is which file or klammerset was used.


11. Klammertext from Python

import klammertext                                   # kpy/ on PYTHONPATH (env/runtime.env)
M = klammertext.Machine(target="html", files=["doc.kt"])   # as ktext sets itself up
M.read("@@version : 3.2.1 @@")                       # what "ktext -s" does
M.read_files()                                       # the constructor's files, in order
html = M.apply()                                     # the rendered text

Machine(target='*', klammersets=None, files=None, output=None, display=False, verbose=0); read(text), read_file(name), read_files(), apply(target=None, final_processing=True, escape_characters=True). One Machine at a time (a second raises RuntimeError); apply() reduces in place, so a second target needs a second Machine. Errors are klammertext.Error subclasses under the C++ names (Parsing_error, Argument_error, Definition_error, Environment_error, …) with kind, description, filename, line, column; str(e) is ktext's report; an @eval's Python exception is chained as __cause__.


12. Adjacent tools

  • Markdown → Klammertext: sks/tns/md_to_sks.py converts a .md file; what it cannot convert exactly is left in the output as #[MD … ]# markers, so the draft carries its own worklist (spec: doc/markdown_to_klammertext.md). mdpdf file.md renders Markdown straight to PDF through a headless Chromium, bypassing Klammertext.
  • Editors: doc/edit/ holds Emacs, Sublime Text, Vim and VS Code support (highlighting, delimiter matching, reindent, table alignment, a language server); doc/edit/README.md.
  • Containers: akopra/klammertext:latest (and :haskell) hold the three commands, the SKS, TeX Live and the default fonts; doc/linux_container_install.md, doc/macos_container_install.md.
  • Environment: KLAMMERTEXT_HOME (the tree), KLAMMERTEXT_FONTS, KLAMMERTEXT_KLAMMERSETS (colon-separated search paths); env/runtime.env sets them when sourced.