Syntax reference
Every form Wit 0.3 accepts, grouped by job. Each block below builds as written. The normative text is the spec; the node set is in core vocabulary.
Prose
| Write | Get |
|---|---|
| a blank line | a new paragraph; a single line break is part of the text |
_word_ | italic |
*word* | bold |
~ note at line start | line comment; a bare ~ line is empty |
~~ note ~~/ | block comment, works mid-line |
\ + punctuation | that character, literally; before a letter, digit or space it is a plain backslash |
A mark needs a non-word character outside and a non-space character inside, so 5*6*7 and snake_case stay prose. Any marker no
construct can use stays text: emails, C#, URLs with #, (see figure 2), !! at the end of
a sentence.
This is _italic_ and *bold*. 5*6*7 stays as typed.
~ a note to self
Escape \@name, \#tag, \*stars\* and \(if \@x).This is italic and bold. 5*6*7 stays as typed. Escape @name, #tag, *stars* and (if @x).
Using nodes
| Form | Meaning |
|---|---|
@name | bare reference — inserts a definition; also a closeless use |
@name … name@ | use with a body; own lines → block, inside a sentence → inline |
(name … name) | parenthesis form — the same use, easier to type |
@name(k: v, k2: v2) | parameters, glued to the name |
@name(v1, v2) | positional values — needs a definition header |
@name(…): … !! | value-block use — !! alone on its line closes it |
H@sub 2 sub@O | a known node glued to a word |
@x.field.0 | data access |
#tip(label) ::label:: ::body:: tip# (h2 The Keeper h2) She crossed the @em last em@ threshold — see (a(href: https://x.org) the log a). @img(src: lamp.png, alt: Note: a ruler, 12 inches long) @aside Fresnel lenses rarely fail. aside@ @tip(label: Tip:): Keep the wick trimmed. !! Water is H@sub 2 sub@O.
Parameters
- Parens attach with no space:
@img(src: a.png). A space before(makes it prose. key: valuepairs, comma-separated. A value runs until the nextkey:the node declares, so it may hold commas and colons. No quoting.- Parentheses inside a value balance, so URLs keep theirs.
- One pair per line is allowed; commas are then optional. A blank line before
)raisesE_UNCLOSED_PAREN. - Positional values come first and bind in header order; a positional value cannot hold a comma.
- A use's body is always content, never parameters.
#cite(author, year) ::author:: (::year::) cite# @img( src: lamp.png alt: The second-order Fresnel lens ) As @cite(Weil, 1952) argued, and @cite(author: Berger, year: 1972) agreed.
When a use needs a close
| Use | Close |
|---|---|
a definition without ::body:: | none — closing it is E_BODY_WITHOUT_SLOT |
void core names br, hr, img | none |
a definition with ::body::, or a core container, opening a block | required — else E_UNCLOSED_NODE |
| the same, inside a sentence | optional; without it the use is empty |
A close pairs with the nearest open of the same name. The parenthesis form opens
only when a known node name and a space, line break or its params follow (, and its name) sits in the same paragraph; (a) first item stays prose.
Defining nodes
| Form | Meaning |
|---|---|
#name … name# | block definition |
#name(a, literal b) | header: parameter order; literal values are used as written |
::a:: | insert a parameter |
::body:: | where the use's body goes; body is reserved |
#name: value !! | single-line definition; the value may span lines up to !! |
#name(a): … ::a:: … !! | single-line definition with a header |
+#name: entry !! | add an entry to the list name |
Without a header, parameters are inferred from the ::name:: placeholders and passed by name. A definition cannot sit inside (if …) or (each …) (E_CONDITIONAL_DEFINITION). ... is ordinary prose
everywhere.
#chapter(number, title) @h2 ::number:: — ::title:: h2@ ::body:: chapter# #link(literal href, label) @a(href: ::href::) ::label:: a@ link# #year: 1923 !! #epigraph: The sea does not forgive forgetting. !! @chapter(I, The Keeper) In @year, @link(href: https://x.org/a_b, label: the _log_) began. chapter@ @epigraph
I — The Keeper
In 1923, the log began.
The sea does not forgive forgetting.
Entry lists
+#name entries are collected from every file, in the order met,
each once. Render them with a loop.
@ol (each @sources as s) @li @s li@ (end) ol@ +#sources: Weil, S. (1952). _Gravity and Grace_. !! +#sources: Berger, J. (1972). _Ways of Seeing_. !!
- Weil, S. (1952). Gravity and Grace.
- Berger, J. (1972). Ways of Seeing.
Data
| Form | Meaning |
|---|---|
{ k: v, k2: v2 } | record; a key may hold spaces, the first : ends it |
[ a, b ] | collection of values or records |
#name: + k: v lines + !! | form-fill record definition; lowercase keys |
42 0.050 | number, rendered as written |
true false null | boolean, null |
"Came, H." | quoted — always text |
@x.a.0 | dot path; numbers index from 0; keys match loosely |
A record value runs until the next key:, so it may hold commas.
Records and collections may span lines, commas then optional, and nest. index, count and last are reserved
field names (E_RESERVED_FIELD).
#keeper: { name: Aldous Vane years at post: 31 history: { incidents: 2 } } !! #weil: author: Weil, S. year: 1952 !! #themes: [ attention, perception ] !! @keeper.name served @keeper.years_at_post years (@keeper.history.incidents incidents). @weil.author (@weil.year). First theme: @themes.0.
Aldous Vane served 31 years (2 incidents). Weil, S. (1952). First theme: attention.
Logic
| Form | Meaning |
|---|---|
(if @x) … (end) | truthy: defined, non-empty, not false, null or 0 |
(if @x is v) … (else) … (end) | comparison |
(each @xs as x) … (end) | repeat per item |
@x.index @x.count @x.last | loop position (from 0), list length, last item |
(if ::p::) | test a parameter inside a definition |
Statements written inside a line stay inline. A glued ( starting
with if, each, else or end is a statement, not parameters. Every statement ends with (end).
#book: { status: draft } !! #authors: [ Smith, Brown, Jones ] !! (if @book.status is draft) Draft — do not distribute. (else) Final. (end) By (each @authors as a)@a(if @a.last)(else), (end)(end).
Draft — do not distribute.
By Smith, Brown, Jones.
Raw, scripts, files
| Form | Meaning |
|---|---|
@@name … name@@ | verbatim body; {{path}} holes still fill |
@@@name … name@@@ | frozen body — nothing expands |
@node(type: x, k: v) … node@ | opaque pass-through for custom renderers |
<% expr %> | JavaScript; inline, the value is inserted |
@scriptCall(fn, arg) | call a script function |
reference ./path.wit | pull in another file's definitions and entries |
@load(as: rows) alias load@ | external data — see config |
Raw node names: code, pre, math, mathblock, diagram, style, script. A reference line needs a path that starts
with ./, ../, / or ~/, or ends in .wit; otherwise it is prose.
#theme: { accent: teal } !! #paper: { words: 4200 } !! @@style .lead { color: {{theme.accent}} } style@@ The paper has <% lh.data.paper.words %> words.
The paper has 4200 words.
Removed in 0.3
wit migrate <file|dir> -w rewrites every form below. Without -w it prints one file's result, or lists the files that need it.
| 0.2 form | 0.3 form | Old form now gives |
|---|---|---|
@x |k v|, |k - v|, |flag!| | @x(k: v), @x(flag: true) | E_PIPE_SYNTAX_REMOVED |
#x ||a, b|| capture list | #x(a, b) header | E_PIPE_SYNTAX_REMOVED |
@x { k: v } call | @x(k: v) | E_REMOVED_SYNTAX |
@x(k v) space-separated | @x(k: v) | E_REMOVED_SYNTAX |
@x k:v body x@ colon-scatter | @x(k: v) body x@ | prose |
form-fill use body (k: v lines) | @x(k: v) + body | prose |
... body slot | ::body:: | prose |
{ k - v } | { k: v } | E_MALFORMED_RECORD |
(if @x equals v) | (if @x is v) | E_MALFORMED_STATEMENT |
| mid-body pipe switching | child nodes or closeless markers, e.g. @cue(mood: tense) | — |
See also
Cheatsheet · Core vocabulary · Data model · Errors · Gotchas.