Railroad diagrams

edit

A RailroadDiagram draws the rules of a grammar as railroad diagrams, also called syntax diagrams: each rule is a track from a start marker to an end marker, through terminals and nonterminals, with branches for the choices and loops for the repetitions (Mermaid railroad-beta, railroad-ebnf-beta, railroad-abnf-beta and railroad-peg-beta):

var diagram = new RailroadDiagram { Title = "Numbers" };
diagram.AddRule("number", RailroadExpression.Sequence(
    RailroadExpression.Optional(RailroadExpression.Choice(RailroadExpression.Terminal("+"), RailroadExpression.Terminal("-"))),
    RailroadExpression.OneOrMore(RailroadExpression.Nonterminal("digit")),
    RailroadExpression.Optional(RailroadExpression.Sequence(RailroadExpression.Terminal("."), RailroadExpression.ZeroOrMore(RailroadExpression.Nonterminal("digit"))))));
diagram.AddRule("digit", RailroadExpression.Special("a digit from 0 to 9"));

A railroad diagram of numbers

Expressions

A RailroadRule has a name and the RailroadExpression it stands for, which the static methods of RailroadExpression create:

ExpressionDrawing
TerminalA literal text in a rounded box
NonterminalA reference to a rule in a box
SequenceExpressions one after the other
ChoiceAlternatives one below the other, between two branches
OptionalA bypass above the expression
RepetitionA loop below the expression, and a bypass when it can be skipped
SpecialA description, ? text ?, in a box

A repetition has a Minimum and a Maximum; other bounds than zero or one to unbounded, such as the 2*4 of ABNF, are written below the loop.

Mermaid syntax

var diagram = (RailroadDiagram)MermaidDocument.Parse("""
    railroad-ebnf-beta
    title "Numbers"
    number = [ "+" | "-" ] digit+ ( "." digit* )? ;
    digit = ? a digit from 0 to 9 ? ;
    """).Content;

The keyword sets the Notation, which the writer uses too. Every rule ends with a semicolon.

NotationKeywordRuleChoiceOptionalRepetitionComments
Constructorsrailroad-betaname = expression ;choice(a, b)optional(a)zeroOrMore(a), oneOrMore(a)/* */
EBNFrailroad-ebnf-betaname = ... ; or ::=a \| ba?, [ a ]a*, { a }, a+/* */, (* *)
ABNFrailroad-abnf-betaname = ... ;a / b[ a ], *1a*a, 1*a, 2*4a, 3a; text
PEGrailroad-peg-betaName <- ... ;a / ba?a*, a+# text

As in Mermaid, an EBNF exception a - b is drawn as a sequence with a - terminal, the predicates &a and !a of PEG and its . are special sequences, the numeric values of ABNF (%x30-39) are terminals, and an identifier cannot start with title. In ABNF, a semicolon ends a rule only at the end of its line: followed by text, it starts a comment. The writer throws an ArgumentException for an expression the notation cannot write, such as bounded repetitions out of ABNF, or special sequences in ABNF.