Skip to content

Using pyrailroad as a library

Building a diagram

If you are familiar with tabatkins' railroad-diagrams, building a diagram should be a straightforward task, with a few differences for two elements (Optional and ZeroOrMore).

A really simple diagram would be:

from pyrailroad.elements import Diagram, Terminal
d = Diagram(Terminal("foo"))

To write it out as SVG, do:

# Write an SVG file without stylesheet
with open('foo.svg', 'w') as f:
    d.write_svg(f.write)
or
# Write an SVG file with a CSS styleshzeet included
with open('foo.svg', 'w') as f:
    d.write_standalone(f.write)

To crate a text-format diagram instead, do:

from sys import stdout
d.write_text(stdout.write)

Summary of elements used in a diagram

Base elements

Diagram

The constructor for Diagram takes any element, a dictionary of parameters (to override the diagram class) and an optional type.

By default, a Diagram will always include a Start and an End when printed even if they were omitted.

Examples

  • Text element

    from pyrailroad.elements import Diagram
    d = Diagram("foo")
    

    foo

  • A Terminal again (explicitly called), with the sql type

    from pyrailroad.elements import Diagram, Terminal
    d = Diagram(Terminal("foo"), type="sql")
    

    foo

  • Multiple diagram elements, with the complex type and a parameter override

    from pyrailroad.elements import Diagram
    d = Diagram("foo", "bar", parameters={'diagram_class': 'custom'},type="complex")
    

    foo bar

Start

This is the element that starts a diagram at the top left, you'd usually only use one. The constructor for Start takes a type (same as Diagram), an optional label and an optional dictionary of parameters to override char_width.

Examples

  • Simple

    from pyrailroad.elements import Diagram, Start
    d = Diagram(Start(label="foo"))
    

    foo

  • Customized

    from pyrailroad.elements import Diagram, Start
    d = Diagram(Start(label="foo", parameters={"char_width": 25}, type="sql"))
    

    foo

End

This is the opposite end of the diagram. The constructor is similar to Start but doesn't have a label. You can pass a dictionary of parameters, but they have no effect on End (this is kept for practical coding reasons so that all elements can take the full parameters dictionary recursively and use only what they need).

Examples

  • Simple

    from pyrailroad.elements import Diagram, End
    d = Diagram(End())
    

  • Customized

    from pyrailroad.elements import Diagram, End
    d = Diagram(End(type="sql"))
    

Text elements

Terminal

In a grammar, a Terminal is a symbol that cannot be replaced by other symbols of the vocabulary. Typically, this will be keywords for your language/tool, or literals.

The constructor for Terminal takes a text, an optional href that will be converted to a link in SVG only, an optional title, a optional class for SVG styling and an optional dictionary of parameters to override char_width.

Examples

  • Simple

    from pyrailroad.elements import Diagram, Terminal
    d = Diagram(Terminal("foo"))
    

    foo

  • Customized

    from pyrailroad.elements import Diagram, Terminal
    d = Diagram(Terminal("foo"), Terminal("bar", href="https://github.com", parameters={"char_width": 25}, title="This is a terminal with a link", cls="term"))
    

    foo bar This is a terminal with a link

NonTerminal

By opposition, a NonTerminal is a symbol that is replaceable (and likely defined in another diagram).

The constructor for NonTerminal takes a text, an optional href that will be converted to a link in SVG only, an optional title, a optional class for SVG styling and an optional dictionary of parameters to override char_width.

  • Simple

    from pyrailroad.elements import Diagram, NonTerminal
    d = Diagram(NonTerminal("foo"))
    

    foo

  • Customized

    from pyrailroad.elements import Diagram, NonTerminal
    d = Diagram(NonTerminal("foo"), NonTerminal("bar", href="https://github.com", parameters={"char_width": 25}, title="This is a non-terminal with a link", cls="term"))
    

    foo bar This is a non-terminal with a link

Comment

A comment is a neutral element that is best used to label branches in some of the block elements (repeat lines for example).

The constructor for Comment takes a text, an optional href that will be converted to a link in SVG only, an optional title, a optional class for SVG styling and an optional dictionary of parameters to override comment_char_width.

  • Simple

    from pyrailroad.elements import Diagram, Comment
    d = Diagram(Comment("foo"))
    

    foo

  • Customized

    from pyrailroad.elements import Diagram, Comment
    d = Diagram(Comment("foo"), Comment("bar", href="https://github.com", parameters={"comment_char_width": 25}, title="This is a comment with a link", cls="term"))
    

    foo bar This is a comment with a link

Arrow

The arrow element is an addition in pyrailroad over railroad-diagram that lets you add an arrow to a line to help read a longer or more complex diagram.

The constructor for Arrow takes an optional direction (and like End, a parameters dictionary that has no effect).

Examples

  • Arrow to the right

    from pyrailroad.elements import Diagram, Arrow
    d = Diagram("going", Arrow(), "right")
    

    going right

  • Arrow to the left

    from pyrailroad.elements import Diagram, Arrow
    d = Diagram("left", Arrow("left"), "going")
    

    left going

  • Undirected (similar to a Skip)

    from pyrailroad.elements import Diagram, Arrow
    d = Diagram("step on", Arrow("skip"), "no pets")
    

    step on no pets

Skip

This element lets you add spacing in your diagram by adding an empty line (in aStack, typically).

The constructor only takes an optional parameters dictionary.

Example

  • One Skip

    from pyrailroad.elements import Diagram, Stack, Skip
    d = Diagram(Stack("one", Skip(), "skip"))
    

    one skip

  • Three Skips

    from pyrailroad.elements import Diagram, Stack, Skip
    d = Diagram(Stack("three", Skip(), Skip(), Skip(), "skips"))
    

    three skips

Expression

The expression element is an addition in pyrailroad over railroad-diagram. It was added to deal with EBNF grammars having ambiguities in their notations which means the parser has to guess for some elements and it can't decide how to represent it other than as an expression. The best example is when a grammar contains a regular expression.

The constructor for Expression takes a text, an optional href that will be converted to a link in SVG only, an optional title, a optional class for SVG styling and an optional dictionary of parameters to override char_width.

Examples

  • Simple

    from pyrailroad.elements import Diagram, Expression
    d = Diagram(Expression("foo"))
    

    foo

  • Customized

    from pyrailroad.elements import Diagram, Expression
    d = Diagram(Expression("foo"), Expression("bar", href="https://github.com", parameters={"char_width": 25}, title="This is an expression with a link", cls="term"))
    

    foo bar This is an expression with a link

Block elements

Sequence

As the name says, this is a sequence of elements. This is a useful container when you build more complex diagrams that use the other block elements (like Stack, for example).

The constructor for Sequence takes one or more elements and an optional parameter dictionary.

Example

from pyrailroad.elements import Diagram, Sequence, Arrow
d = Diagram(Sequence("foo", Arrow(), "bar"))

foo bar

Stack

This allows you to stack elements vertically.

The constructor for Stack takes one or more elements and an optional parameter dictionary (to modify the arc radius of curves).

Examples

  • A simple stack

    from pyrailroad.elements import Diagram, Stack
    d = Diagram(Stack("foo", "bar"))
    

    foo bar

  • A customized stack with more elements

    from pyrailroad.elements import Diagram, Stack, Sequence, Terminal, NonTerminal
    d = Diagram(
            Stack(
                Sequence(NonTerminal("foo"), Terminal("bar")),
                Sequence(NonTerminal("baz"), Terminal("buzz")),
                parameters={'AR':20}
                )
            )
    

    foo bar baz buzz

OptionalSequence

This block is a sequence of elements where you must choose at least one element among the elements passed to it. If only one element is provided, it is turned into a Sequence.

The constructor for OptionalSequence takes one or more elements and an optional parameter dictionary (to modify the arc radius of curves).

Examples

  • A simple optional sequence

    from pyrailroad.elements import Diagram, OptionalSequence
    d = Diagram(OptionalSequence("foo", "bar"))
    

    foo bar

  • A customized optional sequence with more elements

    from pyrailroad.elements import Diagram, OptionalSequence, Sequence, Terminal, NonTerminal, Expression
    d = Diagram(
            OptionalSequence(
                Sequence(NonTerminal("foo"), Terminal("bar")),
                Sequence(NonTerminal("baz"), Terminal("buzz")),
                Expression("fizz"),
                parameters={'AR':20}
                )
            )
    

    foo bar baz buzz fizz

AlternatingSequence

This block means that the two arguments may repeat any number of times, by alternating between the two. You have to take at least one of the two elements.

The constructor for AlternatingSequence takes exactly two elements and an optional parameter dictionary (to modify the arc radius of curves).

Examples

  • A simple alternating sequence

    from pyrailroad.elements import Diagram, AlternatingSequence
    d = Diagram(AlternatingSequence("foo", "bar"))
    

    foo bar

  • A customized alternating sequence with more elements

    from pyrailroad.elements import Diagram, AlternatingSequence, Sequence, Terminal, NonTerminal
    d = Diagram(
            AlternatingSequence(
                Sequence(NonTerminal("foo"), Terminal("bar")),
                Sequence(NonTerminal("baz"), Terminal("buzz")),
                parameters={'AR':20}
                )
            )
    

    foo bar baz buzz

Choice

This block lets you choose one element among multiple elements.

The constructor for Choice takes one integer (index for the default element, starting at 0), one or more elements and an optional parameter dictionary (to modify the arc radius of curves or the vertical seperation between lines).

Examples

  • A simple choice

    from pyrailroad.elements import Diagram, Choice
    d = Diagram(Choice(0, "foo", "bar"))
    

    foo bar

  • A customized choice with more elements

    from pyrailroad.elements import Diagram, Choice, Sequence, Terminal, NonTerminal
    d = Diagram(
            Choice(
                1,
                Sequence(NonTerminal("foo"), Terminal("bar")),
                Sequence(NonTerminal("baz"), Terminal("buzz")),
                parameters={'VS':50, 'AR': 5}
                )
            )
    

    foo bar baz buzz

MultipleChoice

This block lets you choose one or more elements among multiple elements. Unlike Choice, more than one branch can be taken.

The constructor for MultipleChoice takes one integer (index for the default element, starting at 0), one of "any" (at least one branch must be taken) or "all" (all branches must be taken), one or more elements and an optional parameter dictionary (to modify the arc radius of curves or the vertical seperation between lines).

Examples

  • A simple alternating sequence

    from pyrailroad.elements import Diagram, MultipleChoice
    d = Diagram(MultipleChoice(0, "any", "foo", "bar"))
    

    foo bar take one or more branches, once each, in any order 1+

  • A customized alternating sequence with more elements

    from pyrailroad.elements import Diagram, MultipleChoice, Sequence, Terminal, NonTerminal
    d = Diagram(
            MultipleChoice(
                1, "all",
                Sequence(NonTerminal("foo"), Terminal("bar")),
                Sequence(NonTerminal("baz"), Terminal("buzz")),
                parameters={'VS':50, 'AR': 5}
                )
            )
    

    foo bar baz buzz take all branches, once each, in any order all

HorizontalChoice

This block lets you choose one element among multiple elements. Unlike Choice, items are stacked horizontally, there is no default choice.

The constructor for HorizontalChoice takes one or more elements and an optional parameter dictionary (to modify the arc radius of curves or the vertical seperation between lines).

Examples

  • A simple choice

    from pyrailroad.elements import Diagram, HorizontalChoice
    d = Diagram(HorizontalChoice("foo", "bar"))
    

    foo bar

  • A customized choice with more elements

    from pyrailroad.elements import Diagram, HorizontalChoice, Stack, Terminal, NonTerminal
    d = Diagram(
            HorizontalChoice(
                Stack(NonTerminal("foo"), Terminal("bar")),
                Stack(NonTerminal("baz"), Terminal("buzz")),
                parameters={'VS':50, 'AR': 5}
                )
            )
    

    foo bar baz buzz

optional

This pseudo-block is a shortcut for Choice(skip?, Skip(), element).

optional takes one element, an optional boolean (default: False) indicating if skipping is the default, and an optional parameter dictionary (to modify the arc radius of curves or the vertical seperation between lines).

Examples

  • optional with skip = False

    from pyrailroad.elements import Diagram, optional
    d = Diagram(optional("foo", False))
    

    foo

  • optional with skip = True and customization

    from pyrailroad.elements import Diagram, optional
    d = Diagram(optional("foo", True, parameters={'VS':50, 'AR': 5}))
    

    foo

OneOrMore

This lets you repeat an element.

The contructor for OneOrMore takes one item, an optional repeated item (for example a comment) and an optional parameter dictionary (to modify the arc radius of curves or the vertical seperation between lines).

Examples

  • OneOrMore with no repeat item

    from pyrailroad.elements import Diagram, OneOrMore
    d = Diagram(OneOrMore("foo"))
    

    foo

  • OneOrMore with a repeat item and customization

    from pyrailroad.elements import Diagram, OneOrMore, Comment
    d = Diagram(OneOrMore("foo", Comment("bar"), parameters={'VS':50, 'AR': 5}))
    

    foo bar

zero_or_more

This pseudo block is a shortcut for optional(OneOrMore(child, repeat), skip).

zero_or_more takes one element, an optional repeated item, an optional boolean (default: False) indicating if skipping is the default, and an optional parameter dictionary (to modify the arc radius of curves or the vertical seperation between lines).

Examples

  • zero_or_more with no repeat item

    from pyrailroad.elements import Diagram, zero_or_more
    d = Diagram(zero_or_more("foo"))
    

    foo

  • zero_or_more with a repeat item and customization

    from pyrailroad.elements import Diagram, zero_or_more, Comment
    d = Diagram(zero_or_more("foo", Comment("bar"), True, parameters={'VS':50, 'AR': 5}))
    

    foo bar

Group

This lets you highlight an element with a dashed outline.

The constructor for Group takes an element, an optioanl label and an optional parameter dictionary (to modify the arc radius of curves or the vertical seperation between lines).

Examples

  • A group with no label

    from pyrailroad.elements import Diagram, Choice, Group
    d = Diagram("foo", Group(Choice(0, "opt1", "opt2")) ,"bar")
    

    foo opt1 opt2 bar

  • A group with a label and customization

    from pyrailroad.elements import Diagram, Choice, Group
    d = Diagram("foo", Group(Choice(0, "opt1", "opt2"), "a group", parameters={'VS':50, 'AR': 5}) ,"bar")
    

    foo opt1 opt2 a group bar