Skip to content

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:

  1. Container — the text sits between quotes; the frame around it is optional: "…" (none), ["…"] (box, shorthand […]), ("…") (round frame). See §2.1.
  2. Default style — determined by the hosting construct (marker, comment-label, annotation, prose, etc.).
  3. 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 the M) line (sections and annotations, see neumaRk_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 in M). 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: marker M) [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: #hashtag is literal, # title opens 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 in PLAY) / 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 of PLAY)/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 and L) 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 and A)/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), write c4["a[^1]"]; in PLAY)/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[^*]")

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:// and https://, 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.