Grammar reference
A compact, complete description of modl: the grammar, how a line is processed, the defaults, the error messages, and how attributes are drawn.
Grammar
In EBNF. Words are separated by runs of spaces or tabs. Reserved words are lowercase and case-sensitive.
source = line , { newline , line } ;
newline = "\n" | "\r\n" ;
line = [ statement ] , [ comment ] ;
comment = "#" , { any character } ;
(* only at the start of the line (after optional whitespace)
or directly after whitespace, and never inside a string;
a "#" inside a word is text *)
statement = background | edge-line | node-line ;
(* tried in this order; see "Processing a line" *)
background = color ; (* the only word on the line, unquoted *)
node-line = id , node-attrs ;
node-attrs = [ size ] , [ shape ] , [ color ] , [ url ] , [ text ] ;
edge-line = segment , "->" , segment , { "->" , segment } ;
(* empty segments are skipped; at least two must remain *)
segment = id , { word } ;
(* words after the id are attributes only in the last
non-empty segment, and ignored in every other one *)
edge-attrs = [ size ] , [ line-style ] , [ color ] , [ url ] , [ text ] ;
id = id-char , { id-char } | quoted-word ;
(* a quoted id may hold any characters but can't be empty *)
id-char = "A".."Z" | "a".."z" | "0".."9" | "_" | ":" | "-" ;
size = "tiny" | "small" | "normal" | "big" | "bigger" | "huge" ;
shape = "none" | "circle" | "square" | "diamond" | "triangle" | "hex" ;
line-style = "solid" | "dashed" | "dotted" | "bold" ;
color = "gray" | "black" | "white" | "red" | "orange" | "yellow"
| "green" | "teal" | "blue" | "purple" | "pink" | "brown" ;
url = ( "/" | "http://" | "https://" | "www." | "mailto:" ) , { non-space } ;
text = word , { word } ; (* rejoined with single spaces *)
word = plain-word | quoted-word ;
plain-word = non-space , { non-space } ;
(* never contains "->", which always separates words *)
quoted-word = string , { string | non-space } ;
(* the value is the word with the outer quotes of each
string removed; a quoted word is never a keyword or url *)
string = '"' , { any character but '"' } , '"'
| "'" , { any character but "'" } , "'" ;
(* opens only at the start of a word or right after another
string; a quote without a matching one later on the line
is a plain character *)
Attribute slots are matched greedily, left to right, each at most once. A word that doesn't fit the current or a later slot starts the text, and the text runs to the end of the line. Quoted words never fit a slot.
Processing a line
- Split into words. Read the line left to right, recognizing strings,
->and the comment. If the first non-blank character is#, the line is empty. Otherwise everything from the first whitespace-then-#outside a string onward is removed. Strings are found first, so->,#and whitespace inside them are text. - Skip blank lines.
- Background. If the line is exactly one unquoted color name, it becomes the background color, replacing any earlier background.
- Edge line. If the line contains
->outside strings:- Split on
->and drop segments without words. Fewer than two remaining is amalformed edge line. - From each segment except the last, take the first word as an id. An invalid id is reported and left out of the chain.
- The first word of the last segment is the final id. If it is invalid, the error is reported and the whole
line is skipped. The remaining words are parsed as
edge-attrs. - For each consecutive pair of ids, create a node for each id that doesn't exist yet, and add an edge with the attributes, applying defaults for missing ones.
- Split on
- Node line. Otherwise the first word is the id (an invalid id is reported and the line skipped). The
remaining words are parsed as
node-attrs. The node is created if needed, then each attribute that was given replaces the node's current value. Text is replaced only when new text is given, and an empty string (""or'') as the text clears it.
Lines with errors are skipped as described, and processing continues. The diagram always shows everything that parsed.
Defaults
| size | style | color | url | text / label | |
|---|---|---|---|---|---|
| Node | normal | circle | gray | none | none: the id is the label |
| Edge | normal | solid | gray | none | none: no label |
| Background | none: the editor's backdrop shows through | ||||
URL normalization
| Token starts with | Resulting link |
|---|---|
/ | https:// + the rest of the token |
www. | https:// + the token |
http://, https://, mailto: | the token unchanged |
Node URLs using http(s) are also tried as images. See image nodes.
Error messages
Errors appear in the box under the source and the status reads Parse errors. Line numbers count every line, including blank and comment lines, starting at 1.
| Message | Cause | |
|---|---|---|
Line N: invalid node id "x" |
The first word of a node line, or an id in an edge line, is unquoted and contains a character other than
letters, digits, _, : and -. |
try it |
Line N: empty node id |
An id is an empty string, "" or ''. |
try it |
Line N: malformed edge line |
The line contains -> but fewer than two non-empty segments, such as a -> or
-> b. |
try it |
Everything else is valid modl. Misplaced keywords, unknown words and extra URLs never cause errors; they become label text. See Gotchas.
How attributes are drawn
| Property | Value |
|---|---|
| Node box | 110 × 64 px times the size scale (tiny ¼, small ½, normal 1, big 2, bigger 4, huge 8) |
| Circle | radius 0.42 × the smaller box side |
| Square | the full box, corner radius 12 px × scale |
| Diamond, triangle, hex | fill the box; the triangle points up, and the hexagon has flat top and bottom |
| Node fill / outline | the node color at 18% / 85% opacity, outline 2.2 px |
| Node label | bold, max(10, 13 × scale) px, at most 22 characters; dark text on white, yellow and green nodes, light text on all others |
| Edge line | 2.2 px (bold 3.5 px); dashes 10/7 px, dots 2/7 px; arrowhead at the target in the edge color |
| Edge ends | pulled back from the node center by a fixed distance × scale: circle 35.2 px, square 37.4 px, diamond / triangle / hex 39.6 px, none 4 px. Image nodes use their exact outline. |
| Edge label | max(10, 12 × scale) px, at most 28 characters, at the midpoint offset 10 px to one side |
| Image node | natural size, or fitted into the box when a size is given; frame only when a shape is given; text below |
| Background | the color at full opacity, filling the canvas |
| Automatic layout | unplaced nodes on a circle of radius 0.32 × the smaller canvas side, in sorted id order |
Diagram state
The editor's URL fragment is h1= + base64url(UTF-8(JSON {"src": string, "pos": {id: [x, y]}})),
or a 256t CID that names content on HashBin. Details are on Sharing & positions.