A character written in the ^...^ mnemonic form -- ^oe^, ^zz^, ^hello^ --
used to kill ktext outright: no message, no output, and an exit status
that looked like a crash because it was one. Only the forms that happen
to spell hexadecimal digits escaped, and they were right by luck rather
than by reading. Any caret form that names no character is now a located
error that quotes what was written.
A klammer may now sit inside square brackets and braces as it already
could inside parentheses, and may be followed by the punctuation that
ends a sentence: [@i word@], {@b word@}, @i word@'s. The rule is a fixed
list of characters, because none of them can occur inside a klammer name
and so none of them is ambiguous next to a delimiter. A letter still
cannot: "x@" is the named closing delimiter, so a letter before "@" has
two readings and no rule can separate them.
The abbreviations an author writes -- the hyphen form @i-word, a klammer
glued to its neighbouring punctuation -- are now recognised as author
conveniences only. They are not applied to @@ definitions or @@@ system
commands, which are written by someone who needs no such shorthand and
whose forms are already whole.
Also fixed, each of them silent: an exported shell function in the
environment broke every @eval in the document; a line break written with
@nl was destroyed when it passed through a klammer that generates a
table, printing as \textbackslash{}newline in the finished PDF; and
"kdesc --coverage" shortened its file column by comparing the spelling of
a path rather than the path, so a klammer set reached by a symbolic link
showed a full path where its neighbours showed a short one.
New in the distribution: ref/, a small klammerset that describes the
Klammermachine's own tables in Klammertext. It currently provides the
character tables -- the diacritics and the extended Latin letters -- as
klammers that render to any target, so the documentation of what can be
written cannot drift from what the machine accepts. It is early, and
will grow.
The Klammertext taxonomy now has a section on targets, describes the
@@@klammerset system command alongside the other three, and states the
precedence of "^" -- what a caret means before a letter, a digit, a
punctuation character, and a quote, in one ordered list.
(from dev 6594c7ff8c1f)
10 KiB
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, 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.
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: what klammers exist, what arguments each
takes, what the argument types accept, what targets exist. 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 | kdesc -i FILE -k |
| the usage of any command | run it with no arguments |
kdesc -k <name> before using an unfamiliar klammer costs one command and
replaces a guess. The parameter list it prints is authoritative; this file is
not.
Check a document without rendering it:
kdiag --process --check doc.kt
reports every klammer application whose name, argument count, option names, or target coverage is wrong — including inside branches this render will not take. It applies nothing, so it is safe and fast. Use it before claiming a document is correct.
Inspect how text is being parsed: kdiag --type '<text>' shows the katoms
("katoms") and their types. This settles arguments about what a piece of syntax
actually means.
2. What you will get wrong if you assume
| assumption | reality |
|---|---|
| It is a macro language like TeX | No catcodes. Katom structure — the indivisible input units, which kdiag displays — 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 ` |
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. |
| Whitespace is insignificant | It is content, and it is preserved. Blank lines separate paragraphs. |
\\ escapes |
^ quotes: ^@ ^| ^# ^^ ^: produce those characters literally. |
| 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. |
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 formname@when the klammer's body contains other klammer calls. The reason is diagnostic, not stylistic: if delimiters are unbalanced, a named close lets the machine say exactly which klammer was left open. With bare closes everywhere it can only report that the count did not work out. Do not name closes on short, klammer-free bodies — it is noise.
@i just text i@ ← unnecessary; @i just text @ is better
@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 valueis an optional argument; it may appear anywhere in the list.:namewritten alone takes the argument type's "alone" value — for a boolean that istrue, so:numbermeans:number true.- An optional argument that is not written takes its default.
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, and both are
literal: nothing inside either is interpreted, so #, @, |, ^ and *
all pass through as written and none of them needs quoting.
Code that lives in a file is @source_listing, never @code — @code is
only for a block written out inline, and 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, each
beginning in the first column. The source file thus declares its own extractable
regions and the document asks for one by name, so editing the code cannot
silently change what the document shows. A marker that is missing, or that does
not appear exactly twice, is an error — never a silent fallback to the whole
file.
kdesc -k <name> is how you tell: a parameter shown as text.literal is a
literal parameter. Do not infer it from a klammer's purpose — check.
4. A complete document
@document
:title Normalizing to OpenEXR Standard Attributes
:subtitle Predictability and provenance
:author Joseph Goldstone
:date @date@
:point_size 12
:text
@s1 Principles s1@
Ordinary paragraph text. A blank line starts a new paragraph.
Use @i emphasis i@ and @b bold @ inline, and @c code_fragment c@ for code.
@link https://openexr.com/ :text the OpenEXR site link@
@ul
first item
| second item
| third item
ul@
# A comment: removed, and never reaches the output.
@image figure_name :width .95w :caption A caption @
@
Render it:
ktext doc.kt -t pdf # also: -t html, -t tex, -t txt
ktext doc.kt -t html # writes a DIRECTORY doc/ containing index.html
ktext -s '@i-x' -t html -d # -d displays instead of writing; -s takes a string
Output goes next to the input file unless -o says otherwise.
5. Klammers most used when authoring
Confirm parameters with kdesc -k <name> — this list is names and intent only.
| inline | @i italic, @b bold, @t typewriter, @u underline, @c inline code (literal; close with c@), @sup, @sub |
| structure | @s1…@s7 numbered headings, @h1…@h7 unnumbered, @part, @preface, @par |
| blocks | @code (a listing written inline), @source_listing (a listing read from a file), @quote, @note, @indent, @block, @center, @right, @columns |
| lists | @ul unordered, @ol ordered, @define term/definition — items separated by | |
| tables | @table — cells by |, rows by ||; @tbl, @rowcolor |
| figures | @image, @image_grid, @fig, @reference |
| other | @link, @date, @datetime, @color, @font, @newpage, @vspace, @email |
Bar depth is the dimension: one | separates items, two || separates rows.
6. Defining a klammer (only if asked)
@@name.k arg1 | arg2 :opt default : Description of what it does @@
@@name.html :: <span>*arg1*</span> @@
@@name.tex :: \textbf{*arg1*} @@
.kdeclares the interface and documents it; per-target definitions inherit the parameter list with::.*arg*substitutes an argument into the body.- A definition with no target suffix applies to every target.
- Separators:
:create,::create inheriting parameters,:::override an existing definition,::::define a default that a later:may replace.
7. Behaviour worth knowing before you debug something
@condis strict about truth. True:true,True,1. False:false,False,0, and empty. Anything else is an error, not "probably false".@condevaluates only the branch it selects. The branch not taken is not read, not evaluated, not expanded — a@readof a missing file in the unused branch is harmless.- A state variable reaches a
@condpredicate. The document behaves like a function body whose arguments are its@@@statevariables. @eval :shelland:haskell: the command's standard output becomes document text; its standard error does not (it appears under-v 1); a nonzero exit is an error. Append|| trueif a nonzero exit is expected.- A definition inside a
@condbranch does not register — in either branch. Define outside and select between applications.
8. Diagnostics
-v 1reports every decision the command made that the user could not have read off their own input: which klammerset was loaded and from where, which file a font or a:filesname resolved to, how the output path was built. Higher levels are an implementation trace.- Logging and errors go to standard error; the rendered document goes to
standard output, so
ktext doc.kt -d | …is safe. - An error stops the run and names a file, line, and character. There is no warn-and-continue except in two documented cases.
- A bare
ktext,kdesc, orkdiagprints its usage and exits successfully.
When something is wrong: run kdiag --process --check doc.kt first — it
finds name, arity, option and coverage faults without rendering. Then kdesc -k <klammer> to check the parameter list against what was written. Then kdiag --type '<fragment>' if the question is how something is being parsed.