TomatenMark is a lightweight markup language for prose. Its primary implementation is in Zig: tomatenmark_zig.
It inspired by to D★Mark, with two changes: anonymous blocks, and indentation-free blocks.
Writing TomatenMark
A TomatenMark file is a plaintext file, where paragraphs are separated by a blank line:
This is the first paragraph. Autem blog occupy deleniti. Voluptas scenester carry ea pariatur. Perferendis tempora chicharrones. Amet odit excepturi consequuntur humblebrag food truck et. Possimus veniam non synth crucifix shoreditch sartorial. Magni eaque officia quia. Nihil ut mollitia organic aut banh mi hammock.
This is the second paragraph. Locavore aliquam libero listicle messenger bag vitae. Quo magni nihil et. Everyday tilde put a bird on it pug whatever velit quo sed. Sit veritatis brunch quidem ab brooklyn ea flannel. Iusto voluptatum itaque. Illo gentrify butcher. Vitae ramps distinctio kickstarter viral ut ea flexitarian.
TomatenMark supports inline markup:
Ruby provides a %code{StringScanner} class.
Equivalent XML code (assuming paragraphs are translated to para):
<para>Ruby provides a <code>StringScanner</code> class.</para>
Block elements are started with #:
#title The beginning
Equivalent XML code:
<title>The beginning</title>
Multiple blocks can be grouped using section elements:
@begin aside
Here is an example aside.
With two paragraphs.
@end aside
Equivalent XML code:
<aside>
<para>Here is an example aside.</para>
<para>With two paragraphs.</para>
</aside>
There are verbatim elements too, which start with ~, and run until a closing ~. These verbatim elements are intended for code:
Here is some code:
~listing
def next_token(mode_stack)
if @scanner.scan("%}")
mode_stack.pop
[:STRING_INTERP_END, nil]
~
And a paragraph that follows.
Equivalent XML code:
<para>Here is some code:</para>
<listing> def next_token(mode_stack)
if @scanner.scan("%}")
mode_stack.pop
[:STRING_INTERP_END, nil]
</listing>
<para>And a paragraph that follows.</para>
Block elements, section elements, verbatim elements and inline elements can have attributes, specified in square brackets:
#title[in-nav=true] Beginning
@begin section[type=chapter]
~listing[lang=ruby]
def next_token(mode_stack)
if @scanner.scan("%}")
mode_stack.pop
[:STRING_INTERP_END, nil]
~
Here is some text in %i[lang=de]{Deutsch}.
Multiple attributes are possible, too:
@begin section[type=chapter,in-toc=false]
To do: explain escape sequences
Examples
Some technical writing:
@begin section[important]
#title Something
Pork belly et tattooed aut corporis quidem intelligentsia. Impedit schlitz gastropub nihil meditation voluptatem ullam viral.
#note[important] Blah
~listing[lang=ruby]
def greet(name)
puts "Uhh, hi #{name} I guess"
roll_eyes
end
~
Pork belly et tattooed aut corporis quidem intelligentsia. Impedit schlitz gastropub nihil meditation voluptatem ullam viral.
@end section
It’s intended for structured technical writing, but It could work for a play, too. Here is a scene with some set description, dialogue and stage directions:
@begin scene
#desc Old wooden house interior or something.
@begin dia[who=Nora]
okay
well
can we agree now that you’ll
do what you need to do
to make the divorce ‘official’.
@end dia
@begin dia[who=Torvald]
and why do I need to do that.
@end dia
#dir Something wild happens idk.
@end scene
This would be parsed as something like this (as XML, because XML is not too terrible at making up prose tbh):
<scene>
<desc>Old wooden house interior or something.</desc>
<dialogue who="Nora">
<p>okay</p>
<p>well</p>
<p>can we agree now that you’ll<br />do what you need to do<br />to make the divorce ‘official’.</p>
</dialogue>
<dialogue who="Torvald">
<p>and why do I need to do that.</p>
</dialogue>
<dir>Something wild happens idk.</dir>
</scene>
To do: figure out parentheticals in this example.
Document model
There are only two node types: text and element.
Text is just that — text.
Elements have a type (optional the element is anonymous; e.g. note or nil), optional attributes (a key-value map), and children (an array of elements and/or text nodes).
Context
I am creating this markup language so that I can write Write an article on expression parsing.1
In Weeknotes 2025 W04: Scary, man, I talked about this lightweight markup language for the first time. It has evolved since then — including the name.
See also
-
Look, I am fully aware that I do not need to create a markup language to write this article. Just… leave me be! I enjoy this stuff! ↩︎