Text markup¶
This document defines the syntax and semantics of text markup in neumaRk: common formatting rules applicable to all textual containers of the language (markers, end-decorators, comment-labels, note annotations, section labels, form prose).
The goal is to provide a unified and orthogonal system in which a first axis decides the graphical frame (container), a second axis decides the default style (hosting construct), and a third axis — optional — allows the user to apply explicit markup.
1. Three orthogonal axes¶
Text rendering in neumaRk is a function of three independent axes:
- Container — the text sits between quotes; the frame around it is
optional:
"…"(none),["…"](box, shorthand[…]),("…")(round frame). See §2.1. - Default style — determined by the hosting construct (marker, comment-label, annotation, prose, etc.).
- User markup — optional, defined through a subset of Markdown applied inside the container.
None of the three axes constrains the others. The same text can be expressed with any frame, any default semantics, with or without explicit markup.
Exception: marker
M). In markers of theM)line (sections and annotations, seeneumaRk_markers.md§3.1) brackets and quotes carry structural semantics, not a frame:[…]= section (target of PLAY/FORM, delimits collapsible scopes),"…"= annotation (descriptive text, non-structural). The frame model of §2.1 does not apply inM). A section written["NAME"]means[NAME]: the quotes delimit the name and are not part of it.
2. Textual containers¶
2.1 Forms: the frame model¶
A textual container is a quoted text "…" with an optional frame
around it:
| Form | Frame | Notes |
|---|---|---|
"text" |
none | |
["text"] |
box | reference form |
[text] |
box | shorthand for ["text"]: same result |
("text") |
round frame | a pair of parentheses around the text |
The quotes of ["…"] and ("…") delimit the text and never appear in
the rendering: ["text"] and [text] produce the same box.
The model applies to all the containers of §2.2 that point to it: chord
labels (chord, group and row level), A) texts (on the note, at the barline,
above C)), D) texts (on the note, at the barline), note annotations and
textual end-decorators. Left out, with their own syntax: the M) section and
annotation (see the exception in §1), wave and analysis-bracket labels
(~"…", {"…"), PLAY)/FORM) box labels.
Inside the frame the text is an ordinary container: markup (§3), ; line
breaks (§3bis, a single frame around the whole stack), links (§3quater),
\" escape (§4).
Recognizing the round frame. Only the exact form ("…") is a frame:
( immediately followed by ", and the closing " immediately followed by
). Any other ( keeps the meaning of its context: slur in A)
(("dolce" … ) is a slur that starts with a text), optional chord group
or duration in C). In D) parentheses have no other meaning: a ( or )
outside ("…") is ignored and reported with W177.
2.2 Where markup applies¶
The markup defined in this document applies to all textual containers of the language:
| Construct | Allowed containers | Reference |
|---|---|---|
Section M) |
[…] (structural) |
neumaRk_markers.md §3.1 |
Annotation M) |
"…" |
neumaRk_markers.md §3.1 |
| Textual end-decorator | frame model | neumaRk_flow_and_repeats.md §4.3 |
| Comment-label chord / group / row | frame model | neumaRk_chords.md §8 |
| Note annotation | frame model | neumaRk_notes_and_durations.md §8 |
Dynamics D) annotation |
frame model | neumaRk_dynamics.md §3.4 |
A) text (note, barline, above C)) |
frame model | neumaRk_articulations.md §13.1 |
Articulation A) label (wave, bracket) |
"…" |
neumaRk_articulations.md §10.2/§10.3 |
| Top/bottom section label box | "…" |
neumaRk_play_and_form.md §3.3 |
Free prose in PLAY) / FORM) |
all the prose | neumaRk_play_and_form.md §5 |
Free prose in TEXT) |
all the prose | neumaRk_text_line.md §3 |
Markup does not apply to:
- header values (title, credits, year, style, key, meter, BPM), which are rendered autonomously by the system;
- technical marker names when referenced in
PLAY)/FORM), where matching is by text-equality (markup, if present, is applied in display but stripped for matching); - volta begin (
|[1.]), which is a predetermined box-like container and does not admit internal markup.
3. Markup syntax¶
neumaRk markup is a subset of Markdown, chosen to be deterministic and single-line.
3.1 Text size¶
The 3 prefixes #/##/### select a relative size that
depends on the size role of the construct (see §5):
- role
reduced(default for: comment-label chord/group/row, note annotation,D)annotation, PLAY/FORM label, marker"…"annotation): the default is the medium size;###drops to small,#rises to large. - role
body(default for: markerM)[NAME], PLAY/FORM prose): the default is the medium size; the prefixes scale ABOVE and BELOW.
| Prefix | role reduced |
role body |
|---|---|---|
# |
large (1.33× default) | large (1.5× default) |
## |
medium (= default, no prefix) | medium (1.0× default = body) |
### |
small (0.67× default) | small (0.7× default) |
Reference rendering (informative): the factors in parentheses are those of the reference implementation. The three steps (large, medium, small) and the medium default are normative.
Rules:
- the space after
#/##/###is mandatory:#hashtagis literal,# titleopens the span; - the prefix appears only at the beginning of the container text or
at the beginning of a prose run (in
PLAY)/FORM), after a structural token or at the beginning of the body); - the span opened by
#/##/###closes at the first of the following events: end of the container, next structural token inPLAY)/FORM)([…],&kw,$,@); ####and beyond are treated as literals;##is allowed to make the size level explicit even if it is redundant with the default (in both roles medium equals the no-prefix default).
3.2 Emphasis¶
Three forms of emphasis, activated by paired delimiters:
| Syntax | Effect |
|---|---|
*foo* |
italic |
**foo** |
bold |
***foo*** |
bold + italic |
Rules:
- the spans are closed delimiters: an unclosed
*is literal (no partial render); - the closing asterisk must be preceded by non-empty content;
- the spans may appear in any position of the text;
- nesting is not allowed for emphasis (for example
**foo *bar* baz**is invalid): the form***foo***is atomic.
3.3 Underline¶
Syntax activated by the double underscore:
| Syntax | Effect |
|---|---|
__foo__ |
underline |
Rules:
- like the emphasis spans, the span is a closed delimiter: an
unclosed
__is literal; - the closing
__must be preceded by non-empty content; - the span may appear in any position of the text;
- single underscore
_is literal:_x_is not alternative italic (NRK does not adopt the Markdown convention of_as an alias of*); ___foo___(three or more opening/closing underscores) is treated as literal, analogously to####for size prefixes.
Underline is not a form of emphasis: it is an orthogonal axis to bold/italic. The "no nesting of emphasis" rule of §3.2 does not apply to underline.
3.4 Combination¶
Multiple emphasis spans can coexist in the same text, provided they are disjoint:
*foo* and **bar** ✓ disjoint italic + bold
*foo and **bar*** ✗ nesting not allowed
***foo*** plain ***bar*** ✓ two disjoint bold-italic spans
Underline is an orthogonal axis: it can coexist with bold and italic in the same span:
**__foo__** ✓ bold + underline
*__foo__* ✓ italic + underline
***__foo__*** ✓ bold + italic + underline
__**foo**__ ✓ free delimiter order
__*foo* and **bar**__ ✓ underline over a span with internal disjoint emphasis
An emphasis or underline span can coexist with a size prefix: the prefix applies to the entire text span, the other delimiters modify only the inner portion:
# Title with **bold** and __underline__ words
3bis. Line break ; (multi-line text)¶
Inside every text container (§2), in both forms "…" and […]
and with any frame (["…"], ("…")), an unescaped ; is a line
break: the content is made of multiple lines. For a literal ; use
the escape \; (§4).
c4"ten.;trp." → 2 lines: "ten." / "trp."
c4("in 3\;4") → 1 line: "in 3;4" (literal ';')
c4"r1;r2;r3" → 3 lines
M) | "poco;a poco" | → 2 lines: "poco" / "a poco"
C) | C7"alt;b9" | → 2 lines: "alt" / "b9"
The rule is the same for "…" and […]: the two forms differ only by the
box (§2.1), so switching from C7"a;b" to C7[a;b] does not change the
line break.
Why ;. A container occupies a single source line (§6.2): the
file's newline is not available, so a separator is needed. ; is inert
inside a container (the parser keeps its whole content), rare as a
literal in a label, and unused elsewhere in neumaRk. A ; inside the
address of a link [text=>url] belongs to the address and does not break
the line (§3quater).
Exclusions. These are not line-breaking containers, and ; stays
literal in them:
- the section name
M) [NAME]: it is the target ofPLAY)/FORM)by text equality (the same reason it does not admit links); - the version label
%%NAME "…": it is the name of the variant; - the label of
INFO): it is a single line; - volta
|[1.], header values andL)syllables, which carry no markup.
In TEXT) and INFO) prose ; is a character like any other. The
prose of these blocks is not a container: it spans several source lines and
breaks on the source newline, so it needs no separator. Two rules, each with
its own reason: between source lines, the newline; inside a container, which
must fit on one line, the ;.
In PLAY)/FORM) ; breaks the line in prose too. There a source newline
counts as a space (neumaRk_play_and_form.md §6), so ; is the only way to
break at a precise point, as in containers: every unescaped ; ends the row
of boxes and prose (§6.3), and \; prints a ;.
Multi-line rendering. The first segment is always the top line:
it reads top to bottom. The line closest to the staff stays where the
single line would be and the stack grows away from the staff: above the
staff (note, M) and A) annotations, chord-band labels, wave and bracket
labels, end-decorators) it grows upward and the last line stays put; below the
staff (D) annotation) it grows downward and the first line stays put. With a single line nothing changes. Line spacing is the same
everywhere (in the reference rendering 1.18 × the largest size among the
lines), and an empty line
(a;;b) stays empty. The markup of §3 applies per line:
("**ten.**;trp."). With a frame, a single frame wraps the whole stack
(not one per line).
Each line keeps the container's alignment: centred where the text is
centred (chord and group comment-labels, an A) annotation on a chord
symbol), left-aligned where it is anchored on the left (M) annotation, A)
annotation on a note, row label, side label of C-7"…"-, wave and {…}
bracket labels), right-aligned where it hangs from the right edge of the
measure (end-decorator, A) and D) at the closing barline). A text
stacked above another one (group label, A) annotation, row label) clears
the whole stack below it, not just its last line. A dashed extension
line stays attached to the line closest to the staff, for both height and
starting point.
Rendering coverage. Every container draws ; on multiple lines: note,
M), D) and A) annotations (note- and barline-anchored), chord, group and
row comment-labels, text on chords, wave and analysis-bracket labels, textual
end-decorators, PLAY)/FORM) box labels, FOOT) entries. The labels of
PLAY)/FORM) boxes also wrap to the box width, line by line, and the label
below a box grows downward (its line closest to the box is the first).
Source vs content — the source line stays single-line (no newline character in the file: see §6.2); it is the container's content that is multi-line, split on the
;.
3ter. Footnote reference [^label]¶
Markup admits the footnote reference [^label]: a small superscript mark
inside the text, pointing to a definition in the global FOOT) block. The full
grammar (labels, FOOT) block, multi-page repetition, diagnostics W162–W166)
is in neumaRk_footnotes.md. In brief:
[^*][^†][^1]= explicit label (mark = symbol/number);[^]= auto-numbered.- Recognized as a reference only inside
"…"for note/chord/marker containers andA)/D)texts (where[NAME]is structural), also inside a frame (["…"],("…")), not in the[…]abbreviation, where the reference's]would close the container:c4[a[^1]]is invalid (E001), writec4["a[^1]"]; inPLAY)/FORM)/TEXT)(unquoted) prose it applies to the whole text. The\[escape stays a literal bracket. - Does NOT collide with italic
*…*(hence the[^…]frame).
c4("piano[^*]")
3quater. External link [text=>url]¶
Markup admits a link to external material: the video a transcription comes from, an encyclopedia entry, an author's page.
| Form | Shown | Points to |
|---|---|---|
[text=>url] |
text |
url |
[=>url] |
the domain (it.wikipedia.org, without www.) |
url |
TEXT) Transcribed from the [Montreux live set=>https://www.youtube.com/watch?v=abc123]
TEXT) See [=>https://en.wikipedia.org/wiki/Bill_Evans]
C) | Cm7"cf. [the source=>https://example.org/lp]" | F7 |
Rules:
- the text is separated from the address at the first
=>; - markup from §3 applies in the text (
[*Montreux* **1978**=>…]); it does not apply in the address, which is literal up to the]._,__,*,#,;and//in the address are characters of the address — no underline, no line break (§3bis), no comment; - a
]inside the address is written\]: it is the only escape resolved in the address; - allowed schemes: only
http://andhttps://, with no spaces (write a space as%20). An address with another scheme (mailto:,ftp:…), a relative one, or one with spaces is not a link: the text stays, not activatable (W175). An empty link —[text=>]or[=>]— leaves the text, if any (W176); \[text=>url]is literal text, shown as written (§4): it is how the syntax is shown. Here too, the//of the address does not open a comment;- the domain of the short form is derived when rendering: the document keeps the full address.
Where it applies. Same recognition rule as the [^…] reference (§3ter):
inside "…" for notes, chords, A)/D) texts and marker annotations; on the whole text for
TEXT), PLAY) and FORM) prose, for the label and body of INFO), and for
FOOT) entries. In PLAY)/FORM) a […] containing => is a link, never a
section box. A link does not apply in section names M) [NAME], which are
the target of PLAY)/FORM) by text equality, nor in L) syllables.
Rendering. A link is a property of the text, not of the musical content. On
screen the text is rendered as a link — distinct from the __…__ underline —
and can be activated. In print and in PDF it comes out as normal text, with no
link styling and no link.
4. Escape¶
The character \ (backslash) precedes a meta-character to make it
literal.
| Token | Escaped form |
|---|---|
* |
\* |
_ |
\_ |
# |
\# |
[ |
\[ |
] |
\] |
" |
\" |
; |
\; |
\ |
\\ |
The escape \_ is needed when one wants to insert a literal __ in a
text that would otherwise be consumed by the markup (rare: single
_ are already literal by default).
Examples:
M) | [The \*Real\* Book] |
c4"track \#3"
A \ not followed by a meta-character is literal.
Escapes apply in any text that admits markup: in containers and in
prose. In particular \; writes a ; even where ; is already literal
(prose, section names), so the same spelling works everywhere. The only
exception is the address of a link, where only \] is resolved
(§3quater).
Inside a "…" container (with any frame) \" does not close the text:
c4"the \"King\"" → the "King"
C) | C("a \"tempo\"") | → (a "tempo")
The header (title, credits, style) is not markup text: there " and \ are
literal and are not escaped (neumaRk_header.md §5).
5. Default style per construct¶
The frame (none, box, round frame) decides only the graphics around the text. The default style (size, weight, italic) is a property of the hosting construct.
| Construct | Default style |
|---|---|
Marker M) [NAME] |
bold, body size (role body, see §3.1) |
Marker M) "…" annotation |
plain, medium size (role reduced) |
| Textual end-decorator | plain, body size (role body) |
| Comment-label chord / group / row | plain, medium size (role reduced) |
| Note annotation | plain, medium size (role reduced) |
Dynamics D) annotation |
plain, medium size (role reduced) |
| PLAY/FORM top/bottom label box | plain, medium size (role reduced) |
| PLAY/FORM prose | plain, body size (role body) |
TEXT) prose |
plain, body size (role body) |
The size role decides how the prefixes #/##/### scale around the
default (see §3.1 table). In both roles the default is the medium size and
### goes BELOW it (small); role reduced differs only in how far #
scales above (in the reference rendering 1.33× vs 1.5×).
User markup (§3) always overrides the default. For example a
comment-label starts plain: chord"sub" is rendered roman, while
chord"*sub*" turns on italic via markup above that default.
6. Parsing rules¶
6.1 Disambiguation of […] and ( by role¶
In neumaRk the delimiter […] is shared among several constructs
(polychord, volta, end-decorator, comment-label, note annotation,
grace block, etc.). The disambiguation is positional and defined
in the specs of the individual constructs. Once the parser has
identified the role of the […], the content is interpreted according to
the rules of this document.
A ( is a round frame only in the exact form ("…") (§2.1); elsewhere it
keeps the role of its construct (slur, optional group, duration, context
object, play directive).
6.2 Single-line source, multi-line content¶
The containers "…", […], ["…"], ("…") occupy a single source line:
they do not admit newline characters in the file. A container opened and not
closed within its measure (a ", [ or (" with no closing before the
barline) reads the rest of the measure as text: on the N), A), D) and
M) rows the parser reports it with W133 (on N) the notes that follow
in the measure are lost: c4"abc d e f | keeps only c4); on the chord row
the token is not recognized (E102).
The content, however, can be multi-line: the line break is
expressed with the ; separator (§3bis), not with a newline in the
source. This keeps a single N) line column-aligned with the other lines
of the datapack even when the annotation renders on multiple lines.
The prose of PLAY) / FORM) admits line continuation (see
related documents).
6.3 Empty containers¶
[] and "" are literals: they are not interpreted as containers.
7. Edge cases¶
7.1 Markup in reference names¶
Inside [NAME] of references in PLAY) / FORM), the NAME is
interpreted as text. The internal markup is applied in display
but removed before matching with the defined markers:
PLAY) [**Solo**] [A]
[**Solo**] looks for a marker named "Solo" and renders it in bold if
found. If the NAME contains unclosed or malformed markup, the
fallback is literal matching including the markup characters.
7.2 Spaces around emphasis and underline delimiters¶
Unlike standard Markdown, neumaRk does not require that the
delimiters (*, __) be "tight" with respect to the content: * foo *
is equivalent to *foo*, __ foo __ is equivalent to __foo__. The
choice reduces common errors and keeps the markup more tolerant.
7.3 Different frames in the same document¶
In constructs that follow the frame model the forms mix freely, even on the same line. The choice is guided only by the intended graphics.
C) | C"freely" | F[swing] | G["a tempo"] | C("rit.") |
8. Summary¶
| Axis | Values | Decides |
|---|---|---|
| Container | "…" / ["…"] ([…]) / ("…") |
no frame / box / round frame |
| Construct | marker, label, annotation, … | default size + weight + italic |
| User markup | *…*, **…**, __…__, # …, […=>url], etc. |
explicit override |
The three axes are orthogonal. None of the three constrains the others.
9. Diagnostics¶
| Code | Condition |
|---|---|
| E102 | Unclosed container on a chord row (§6.2) |
| W133 | Container not closed within the measure on N), A), D), M): the rest of the measure is text (§6.2) |
| W175 | Link with a non-http/https or malformed address: the text stays (§3quater) |
| W176 | Empty link [text=>] / [=>] (§3quater) |
| W177 | In D), parentheses outside a ("…") frame: ignored (§2.1) |
The diagnostics of [^…] references (W162–W166) are in neumaRk_footnotes.md.
This document defines the unified text formatting system of neumaRk, applicable to all textual containers of the language.