diff options
-rw-r--r-- | docs/HowToUsePyparsing.rst | 58 | ||||
-rw-r--r-- | docs/_static/json.html | 231 | ||||
-rw-r--r-- | pyparsing/diagram/__init__.py | 16 | ||||
-rw-r--r-- | pyparsing/diagram/template.jinja2 | 23 | ||||
-rw-r--r-- | tests/test_diagram.py | 18 |
5 files changed, 338 insertions, 8 deletions
diff --git a/docs/HowToUsePyparsing.rst b/docs/HowToUsePyparsing.rst index 8d8582c..9af6862 100644 --- a/docs/HowToUsePyparsing.rst +++ b/docs/HowToUsePyparsing.rst @@ -1043,3 +1043,61 @@ Common string and token constants - ``restOfLine`` - all remaining printable characters up to but not including the next newline + +Generating Railroad Diagrams +============================ +Grammars are conventionally represented in what are called "railroad diagrams", which allow you to visually follow +the sequence of tokens in a grammar along lines which are a bit like train tracks. You might want to generate a +railroad diagram for your grammar in order to better understand it yourself, or maybe to communicate it to others. + +Usage +----- +To generate a railroad diagram in pyparsing, you first have to install pyparsing with the ``diagrams`` extra. +To do this, just run ``pip install pyparsing[diagrams]``, and make sure you add ``pyparsing[diagrams]`` to any +``setup.py`` or ``requirements.txt`` that specifies pyparsing as a dependency. + +Next, run :py:func:`pyparsing.diagrams.to_railroad` to convert your grammar into a form understood by the +`railroad-diagrams <https://github.com/tabatkins/railroad-diagrams/blob/gh-pages/README-py.md>`_ module, and then :py:func:`pyparsing.diagrams.railroad_to_html` to convert that into an HTML document. For example:: + + from pyparsing.diagram import to_railroad, railroad_to_html + + with open('output.html', 'w') as fp: + railroad = to_railroad(my_grammar) + fp.write(railroad_to_html(railroad)) + +This will result in the railroad diagram being written to ``output.html`` + +Example +------- +You can view an example railroad diagram generated from a pyparsing grammar `here <_static/json.html>`_. + +Customization +------------- +You can customize the resulting diagram in a few ways. + +Firstly, you can pass in additional keyword arguments to :py:func:`pyparsing.diagrams.to_railroad`, which will be passed +into the ``Diagram()`` constructor of the underlying library, as explained `here <https://github.com/tabatkins/railroad-diagrams/blob/gh-pages/README-py.md#diagrams>`_. + +Secondly, you can edit global options in the underlying library, by editing constants:: + + from pyparsing.diagram import to_railroad, railroad_to_html + import railroad + + railroad.DIAGRAM_CLASS = "my-custom-class" + my_railroad = to_railroad(my_grammar) + +These options are documented `here <https://github.com/tabatkins/railroad-diagrams/blob/gh-pages/README-py.md#options>`_. + +Finally, you can edit the HTML produced by :py:func:`pyparsing.diagrams.railroad_to_html` by passing in certain keyword +arguments that will be used in the HTML template. Currently, these are: + +- ``head``: A string containing HTML to use in the ``<head>`` tag. This might be a stylesheet or other metadata +- ``body``: A string containing HTML to use in the ``<body>`` tag, above the actual diagram. This might consist of a + heading, description, or JavaScript. + +If you want to provide a custom stylesheet using the ``head`` keyword, you can make use of the following CSS classes: + +- ``railroad-group``: A group containing everything relating to a given element group (ie something with a heading) +- ``railroad-heading``: The title for each group +- ``railroad-svg``: A div containing only the diagram SVG for each group +- ``railroad-description``: A div containing the group description (unused) diff --git a/docs/_static/json.html b/docs/_static/json.html new file mode 100644 index 0000000..7067e5e --- /dev/null +++ b/docs/_static/json.html @@ -0,0 +1,231 @@ +<!DOCTYPE html> +<html> +<head> + + <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap" rel="stylesheet"> + <style type="text/css"> + .railroad-heading { + font-family: 'Roboto', sans-serif; + } + </style> + +</head> +<body> + + + <div class="railroad-group"> + <h1 class="railroad-heading">Grammar</h1> + <div class="railroad-description"></div> + <div class="railroad-svg"> + <svg class="railroad-diagram" height="62" viewBox="0 0 179.5 62" width="179.5" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(.5 .5)"> +<style>/* <![CDATA[ */ + svg.railroad-diagram { + background-color:hsl(30,20%,95%); + } + svg.railroad-diagram path { + stroke-width:3; + stroke:black; + fill:rgba(0,0,0,0); + } + svg.railroad-diagram text { + font:bold 14px monospace; + text-anchor:middle; + } + svg.railroad-diagram text.label{ + text-anchor:start; + } + svg.railroad-diagram text.comment{ + font:italic 12px monospace; + } + svg.railroad-diagram rect{ + stroke-width:3; + stroke:black; + fill:hsl(120,100%,90%); + } + svg.railroad-diagram rect.group-box { + stroke: gray; + stroke-dasharray: 10 5; + fill: none; + } + +/* ]]> */ +</style><g> +<path d="M20 21v20m10 -20v20m-10 -10h20"></path></g><path d="M40 31h10"></path><g class="non-terminal"> +<path d="M50 31h0.0"></path><path d="M129.5 31h0.0"></path><rect height="22" width="79.5" x="50.0" y="20"></rect><text x="89.75" y="35">Group 1</text></g><path d="M129.5 31h10"></path><path d="M 139.5 31 h 20 m -10 -10 v 20 m 10 -20 v 20"></path></g></svg> + </div> + </div> + + <div class="railroad-group"> + <h1 class="railroad-heading">Group 1</h1> + <div class="railroad-description"></div> + <div class="railroad-svg"> + <svg class="railroad-diagram" height="166" viewBox="0 0 2205.0 166" width="2205.0" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(.5 .5)"> +<style>/* <![CDATA[ */ + svg.railroad-diagram { + background-color:hsl(30,20%,95%); + } + svg.railroad-diagram path { + stroke-width:3; + stroke:black; + fill:rgba(0,0,0,0); + } + svg.railroad-diagram text { + font:bold 14px monospace; + text-anchor:middle; + } + svg.railroad-diagram text.label{ + text-anchor:start; + } + svg.railroad-diagram text.comment{ + font:italic 12px monospace; + } + svg.railroad-diagram rect{ + stroke-width:3; + stroke:black; + fill:hsl(120,100%,90%); + } + svg.railroad-diagram rect.group-box { + stroke: gray; + stroke-dasharray: 10 5; + fill: none; + } + +/* ]]> */ +</style><g> +<path d="M20 93v20m10 -20v20m-10 -10h20"></path></g><path d="M40 103h10"></path><g> +<path d="M50 103h0.0"></path><path d="M2155.0 103h0.0"></path><rect class="group-box" height="110" rx="10" ry="10" width="2105.0" x="50.0" y="36"></rect><g> +<path d="M50.0 103h10.0"></path><path d="M2145.0 103h10.0"></path><g> +<path d="M60.0 103h0.0"></path><path d="M2059.0 103h0.0"></path><g> +<path d="M60.0 103h0.0"></path><path d="M126.0 103h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="66" x="60.0" y="84"></rect><g class="terminal"> +<path d="M60.0 103h10.25"></path><path d="M115.75 103h10.25"></path><rect height="22" rx="10" ry="10" width="45.5" x="70.25" y="92"></rect><text x="93.0" y="107">"{"</text></g><g> +<path d="M60.0 76h0.0"></path><path d="M126.0 76h0.0"></path><text class="comment" x="93.0" y="81">Suppress</text></g></g><path d="M126.0 103h10"></path><g> +<path d="M136.0 103h0.0"></path><path d="M2059.0 103h0.0"></path><path d="M136.0 103a10 10 0 0 0 10 -10v-39a10 10 0 0 1 10 -10"></path><g> +<path d="M156.0 44h1883.0"></path></g><path d="M2039.0 44a10 10 0 0 1 10 10v39a10 10 0 0 0 10 10"></path><path d="M136.0 103h20"></path><g> +<path d="M156.0 103h0.0"></path><path d="M2039.0 103h0.0"></path><g> +<path d="M156.0 103h0.0"></path><path d="M1019.5 103h0.0"></path><rect class="group-box" height="70" rx="10" ry="10" width="863.5" x="156.0" y="60"></rect><g> +<path d="M156.0 103h10.0"></path><path d="M1009.5 103h10.0"></path><g> +<path d="M166.0 103h0.0"></path><path d="M910.0 103h0.0"></path><g> +<path d="M166.0 103h0.0"></path><path d="M824.0 103h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="658.0" x="166.0" y="84"></rect><g> +<path d="M166.0 103h10.0"></path><path d="M814.0 103h10.0"></path><g class="terminal"> +<path d="M176.0 103h0.0"></path><path d="M748.5 103h0.0"></path><rect height="22" rx="10" ry="10" width="572.5" x="176.0" y="92"></rect><text x="462.25" y="107">Re:('"(?:[^"\\n\\r\\\\]|(?:"")|(?:\\\\(?:[^x]|x[0-9a-fA-F]+)))*')</text></g><path d="M748.5 103h10"></path><path d="M758.5 103h10"></path><g class="terminal"> +<path d="M768.5 103h0.0"></path><path d="M814.0 103h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="768.5" y="92"></rect><text x="791.25" y="107">"""</text></g></g><g> +<path d="M166.0 76h0.0"></path><path d="M400.0 76h0.0"></path><text class="comment" x="283.0" y="81">string enclosed in double quotes</text></g></g><path d="M824.0 103h10"></path><path d="M834.0 103h10"></path><g> +<path d="M844.0 103h0.0"></path><path d="M910.0 103h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="66" x="844.0" y="84"></rect><g class="terminal"> +<path d="M844.0 103h10.25"></path><path d="M899.75 103h10.25"></path><rect height="22" rx="10" ry="10" width="45.5" x="854.25" y="92"></rect><text x="877.0" y="107">":"</text></g><g> +<path d="M844.0 76h0.0"></path><path d="M910.0 76h0.0"></path><text class="comment" x="877.0" y="81">Suppress</text></g></g></g><path d="M910.0 103h10"></path><path d="M920.0 103h10"></path><g class="non-terminal"> +<path d="M930.0 103h0.0"></path><path d="M1009.5 103h0.0"></path><rect height="22" width="79.5" x="930.0" y="92"></rect><text x="969.75" y="107">Group 2</text></g></g></g><path d="M1019.5 103h10"></path><g> +<path d="M1029.5 103h0.0"></path><path d="M2039.0 103h0.0"></path><path d="M1029.5 103a10 10 0 0 0 10 -10v-31a10 10 0 0 1 10 -10"></path><g> +<path d="M1049.5 52h969.5"></path></g><path d="M2019.0 52a10 10 0 0 1 10 10v31a10 10 0 0 0 10 10"></path><path d="M1029.5 103h20"></path><g> +<path d="M1049.5 103h0.0"></path><path d="M2019.0 103h0.0"></path><path d="M1049.5 103h10"></path><g> +<path d="M1059.5 103h0.0"></path><path d="M2009.0 103h0.0"></path><g> +<path d="M1059.5 103h0.0"></path><path d="M1125.5 103h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="66" x="1059.5" y="84"></rect><g class="terminal"> +<path d="M1059.5 103h10.25"></path><path d="M1115.25 103h10.25"></path><rect height="22" rx="10" ry="10" width="45.5" x="1069.75" y="92"></rect><text x="1092.5" y="107">","</text></g><g> +<path d="M1059.5 76h0.0"></path><path d="M1125.5 76h0.0"></path><text class="comment" x="1092.5" y="81">Suppress</text></g></g><path d="M1125.5 103h10"></path><path d="M1135.5 103h10"></path><g> +<path d="M1145.5 103h0.0"></path><path d="M2009.0 103h0.0"></path><rect class="group-box" height="70" rx="10" ry="10" width="863.5" x="1145.5" y="60"></rect><g> +<path d="M1145.5 103h10.0"></path><path d="M1999.0 103h10.0"></path><g> +<path d="M1155.5 103h0.0"></path><path d="M1899.5 103h0.0"></path><g> +<path d="M1155.5 103h0.0"></path><path d="M1813.5 103h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="658.0" x="1155.5" y="84"></rect><g> +<path d="M1155.5 103h10.0"></path><path d="M1803.5 103h10.0"></path><g class="terminal"> +<path d="M1165.5 103h0.0"></path><path d="M1738.0 103h0.0"></path><rect height="22" rx="10" ry="10" width="572.5" x="1165.5" y="92"></rect><text x="1451.75" y="107">Re:('"(?:[^"\\n\\r\\\\]|(?:"")|(?:\\\\(?:[^x]|x[0-9a-fA-F]+)))*')</text></g><path d="M1738.0 103h10"></path><path d="M1748.0 103h10"></path><g class="terminal"> +<path d="M1758.0 103h0.0"></path><path d="M1803.5 103h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="1758.0" y="92"></rect><text x="1780.75" y="107">"""</text></g></g><g> +<path d="M1155.5 76h0.0"></path><path d="M1389.5 76h0.0"></path><text class="comment" x="1272.5" y="81">string enclosed in double quotes</text></g></g><path d="M1813.5 103h10"></path><path d="M1823.5 103h10"></path><g> +<path d="M1833.5 103h0.0"></path><path d="M1899.5 103h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="66" x="1833.5" y="84"></rect><g class="terminal"> +<path d="M1833.5 103h10.25"></path><path d="M1889.25 103h10.25"></path><rect height="22" rx="10" ry="10" width="45.5" x="1843.75" y="92"></rect><text x="1866.5" y="107">":"</text></g><g> +<path d="M1833.5 76h0.0"></path><path d="M1899.5 76h0.0"></path><text class="comment" x="1866.5" y="81">Suppress</text></g></g></g><path d="M1899.5 103h10"></path><path d="M1909.5 103h10"></path><g class="non-terminal"> +<path d="M1919.5 103h0.0"></path><path d="M1999.0 103h0.0"></path><rect height="22" width="79.5" x="1919.5" y="92"></rect><text x="1959.25" y="107">Group 2</text></g></g></g></g><path d="M2009.0 103h10"></path><path d="M1059.5 103a10 10 0 0 0 -10 10v15a10 10 0 0 0 10 10"></path><g> +<path d="M1059.5 138h949.5"></path></g><path d="M2009.0 138a10 10 0 0 0 10 -10v-15a10 10 0 0 0 -10 -10"></path></g><path d="M2019.0 103h20"></path></g></g><path d="M2039.0 103h20"></path></g></g><path d="M2059.0 103h10"></path><path d="M2069.0 103h10"></path><g> +<path d="M2079.0 103h0.0"></path><path d="M2145.0 103h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="66" x="2079.0" y="84"></rect><g class="terminal"> +<path d="M2079.0 103h10.25"></path><path d="M2134.75 103h10.25"></path><rect height="22" rx="10" ry="10" width="45.5" x="2089.25" y="92"></rect><text x="2112.0" y="107">"}"</text></g><g> +<path d="M2079.0 76h0.0"></path><path d="M2145.0 76h0.0"></path><text class="comment" x="2112.0" y="81">Suppress</text></g></g></g><g> +<path d="M50.0 28h0.0"></path><path d="M88.0 28h0.0"></path><text class="comment" x="69.0" y="33">Dict</text></g></g><path d="M2155.0 103h10"></path><path d="M 2165.0 103 h 20 m -10 -10 v 20 m 10 -20 v 20"></path></g></svg> + </div> + </div> + + <div class="railroad-group"> + <h1 class="railroad-heading">Group 2</h1> + <div class="railroad-description"></div> + <div class="railroad-svg"> + <svg class="railroad-diagram" height="422" viewBox="0 0 978.0 422" width="978.0" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(.5 .5)"> +<style>/* <![CDATA[ */ + svg.railroad-diagram { + background-color:hsl(30,20%,95%); + } + svg.railroad-diagram path { + stroke-width:3; + stroke:black; + fill:rgba(0,0,0,0); + } + svg.railroad-diagram text { + font:bold 14px monospace; + text-anchor:middle; + } + svg.railroad-diagram text.label{ + text-anchor:start; + } + svg.railroad-diagram text.comment{ + font:italic 12px monospace; + } + svg.railroad-diagram rect{ + stroke-width:3; + stroke:black; + fill:hsl(120,100%,90%); + } + svg.railroad-diagram rect.group-box { + stroke: gray; + stroke-dasharray: 10 5; + fill: none; + } + +/* ]]> */ +</style><g> +<path d="M20 45v20m10 -20v20m-10 -10h20"></path></g><g> +<path d="M40 55h0.0"></path><path d="M938.0 55h0.0"></path><path d="M40.0 55h20"></path><g> +<path d="M60.0 55h0.0"></path><path d="M918.0 55h0.0"></path><path d="M60.0 55h20"></path><g> +<path d="M80.0 55h0.0"></path><path d="M898.0 55h0.0"></path><path d="M80.0 55h20"></path><g> +<path d="M100.0 55h0.0"></path><path d="M878.0 55h0.0"></path><path d="M100.0 55h20"></path><g> +<path d="M120.0 55h0.0"></path><path d="M858.0 55h0.0"></path><path d="M120.0 55h20"></path><g> +<path d="M140.0 55h0.0"></path><path d="M838.0 55h0.0"></path><path d="M140.0 55h20"></path><g> +<path d="M160.0 55h0.0"></path><path d="M818.0 55h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="658.0" x="160.0" y="36"></rect><g> +<path d="M160.0 55h10.0"></path><path d="M808.0 55h10.0"></path><g class="terminal"> +<path d="M170.0 55h0.0"></path><path d="M742.5 55h0.0"></path><rect height="22" rx="10" ry="10" width="572.5" x="170.0" y="44"></rect><text x="456.25" y="59">Re:('"(?:[^"\\n\\r\\\\]|(?:"")|(?:\\\\(?:[^x]|x[0-9a-fA-F]+)))*')</text></g><path d="M742.5 55h10"></path><path d="M752.5 55h10"></path><g class="terminal"> +<path d="M762.5 55h0.0"></path><path d="M808.0 55h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="762.5" y="44"></rect><text x="785.25" y="59">"""</text></g></g><g> +<path d="M160.0 28h0.0"></path><path d="M394.0 28h0.0"></path><text class="comment" x="277.0" y="33">string enclosed in double quotes</text></g></g><path d="M818.0 55h20"></path><path d="M140.0 55a10 10 0 0 1 10 10v18a10 10 0 0 0 10 10"></path><g> +<path d="M160.0 93h146.0"></path><path d="M672.0 93h146.0"></path><path d="M306.0 93h20"></path><g class="terminal"> +<path d="M326.0 93h0.0"></path><path d="M652.0 93h0.0"></path><rect height="22" rx="10" ry="10" width="326.0" x="326.0" y="82"></rect><text x="489.0" y="97">real number with scientific notation</text></g><path d="M652.0 93h20"></path><path d="M306.0 93a10 10 0 0 1 10 10v10a10 10 0 0 0 10 10"></path><g class="terminal"> +<path d="M326.0 123h106.25"></path><path d="M545.75 123h106.25"></path><rect height="22" rx="10" ry="10" width="113.5" x="432.25" y="112"></rect><text x="489.0" y="127">real number</text></g><path d="M652.0 123a10 10 0 0 0 10 -10v-10a10 10 0 0 1 10 -10"></path><path d="M306.0 93a10 10 0 0 1 10 10v40a10 10 0 0 0 10 10"></path><g class="terminal"> +<path d="M326.0 153h93.5"></path><path d="M558.5 153h93.5"></path><rect height="22" rx="10" ry="10" width="139.0" x="419.5" y="142"></rect><text x="489.0" y="157">signed integer</text></g><path d="M652.0 153a10 10 0 0 0 10 -10v-40a10 10 0 0 1 10 -10"></path></g><path d="M818.0 93a10 10 0 0 0 10 -10v-18a10 10 0 0 1 10 -10"></path></g><path d="M838.0 55h20"></path><path d="M120.0 55a10 10 0 0 1 10 10v116a10 10 0 0 0 10 10"></path><g> +<path d="M140.0 191h299.25"></path><path d="M538.75 191h299.25"></path><rect class="group-box" height="38" rx="10" ry="10" width="99.5" x="439.25" y="172"></rect><g class="non-terminal"> +<path d="M439.25 191h10.0"></path><path d="M528.75 191h10.0"></path><rect height="22" width="79.5" x="449.25" y="180"></rect><text x="489.0" y="195">Group 1</text></g></g><path d="M838.0 191a10 10 0 0 0 10 -10v-116a10 10 0 0 1 10 -10"></path></g><path d="M858.0 55h20"></path><path d="M100.0 55a10 10 0 0 1 10 10v202a10 10 0 0 0 10 10"></path><g> +<path d="M120.0 277h100.5"></path><path d="M757.5 277h100.5"></path><rect class="group-box" height="94" rx="10" ry="10" width="537.0" x="220.5" y="218"></rect><g> +<path d="M220.5 277h10.0"></path><path d="M747.5 277h10.0"></path><g> +<path d="M230.5 277h0.0"></path><path d="M661.5 277h0.0"></path><g> +<path d="M230.5 277h0.0"></path><path d="M296.5 277h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="66" x="230.5" y="258"></rect><g class="terminal"> +<path d="M230.5 277h10.25"></path><path d="M286.25 277h10.25"></path><rect height="22" rx="10" ry="10" width="45.5" x="240.75" y="266"></rect><text x="263.5" y="281">"["</text></g><g> +<path d="M230.5 250h0.0"></path><path d="M296.5 250h0.0"></path><text class="comment" x="263.5" y="255">Suppress</text></g></g><path d="M296.5 277h10"></path><g> +<path d="M306.5 277h0.0"></path><path d="M661.5 277h0.0"></path><path d="M306.5 277a10 10 0 0 0 10 -10v-31a10 10 0 0 1 10 -10"></path><g> +<path d="M326.5 226h315.0"></path></g><path d="M641.5 226a10 10 0 0 1 10 10v31a10 10 0 0 0 10 10"></path><path d="M306.5 277h20"></path><g> +<path d="M326.5 277h0.0"></path><path d="M641.5 277h0.0"></path><g class="non-terminal"> +<path d="M326.5 277h0.0"></path><path d="M406.0 277h0.0"></path><rect height="22" width="79.5" x="326.5" y="266"></rect><text x="366.25" y="281">Group 2</text></g><path d="M406.0 277h10"></path><g> +<path d="M416.0 277h0.0"></path><path d="M641.5 277h0.0"></path><path d="M416.0 277a10 10 0 0 0 10 -10v-23a10 10 0 0 1 10 -10"></path><g> +<path d="M436.0 234h185.5"></path></g><path d="M621.5 234a10 10 0 0 1 10 10v23a10 10 0 0 0 10 10"></path><path d="M416.0 277h20"></path><g> +<path d="M436.0 277h0.0"></path><path d="M621.5 277h0.0"></path><path d="M436.0 277h10"></path><g> +<path d="M446.0 277h0.0"></path><path d="M611.5 277h0.0"></path><g> +<path d="M446.0 277h0.0"></path><path d="M512.0 277h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="66" x="446.0" y="258"></rect><g class="terminal"> +<path d="M446.0 277h10.25"></path><path d="M501.75 277h10.25"></path><rect height="22" rx="10" ry="10" width="45.5" x="456.25" y="266"></rect><text x="479.0" y="281">","</text></g><g> +<path d="M446.0 250h0.0"></path><path d="M512.0 250h0.0"></path><text class="comment" x="479.0" y="255">Suppress</text></g></g><path d="M512.0 277h10"></path><path d="M522.0 277h10"></path><g class="non-terminal"> +<path d="M532.0 277h0.0"></path><path d="M611.5 277h0.0"></path><rect height="22" width="79.5" x="532.0" y="266"></rect><text x="571.75" y="281">Group 2</text></g></g><path d="M611.5 277h10"></path><path d="M446.0 277a10 10 0 0 0 -10 10v7a10 10 0 0 0 10 10"></path><g> +<path d="M446.0 304h165.5"></path></g><path d="M611.5 304a10 10 0 0 0 10 -10v-7a10 10 0 0 0 -10 -10"></path></g><path d="M621.5 277h20"></path></g></g><path d="M641.5 277h20"></path></g></g><path d="M661.5 277h10"></path><path d="M671.5 277h10"></path><g> +<path d="M681.5 277h0.0"></path><path d="M747.5 277h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="66" x="681.5" y="258"></rect><g class="terminal"> +<path d="M681.5 277h10.25"></path><path d="M737.25 277h10.25"></path><rect height="22" rx="10" ry="10" width="45.5" x="691.75" y="266"></rect><text x="714.5" y="281">"]"</text></g><g> +<path d="M681.5 250h0.0"></path><path d="M747.5 250h0.0"></path><text class="comment" x="714.5" y="255">Suppress</text></g></g></g></g><path d="M858.0 277a10 10 0 0 0 10 -10v-202a10 10 0 0 1 10 -10"></path></g><path d="M878.0 55h20"></path><path d="M80.0 55a10 10 0 0 1 10 10v256a10 10 0 0 0 10 10"></path><g class="terminal"> +<path d="M100.0 331h353.5"></path><path d="M524.5 331h353.5"></path><rect height="22" rx="10" ry="10" width="71.0" x="453.5" y="320"></rect><text x="489.0" y="335">"true"</text></g><path d="M878.0 331a10 10 0 0 0 10 -10v-256a10 10 0 0 1 10 -10"></path></g><path d="M898.0 55h20"></path><path d="M60.0 55a10 10 0 0 1 10 10v286a10 10 0 0 0 10 10"></path><g class="terminal"> +<path d="M80.0 361h369.25"></path><path d="M528.75 361h369.25"></path><rect height="22" rx="10" ry="10" width="79.5" x="449.25" y="350"></rect><text x="489.0" y="365">"false"</text></g><path d="M898.0 361a10 10 0 0 0 10 -10v-286a10 10 0 0 1 10 -10"></path></g><path d="M918.0 55h20"></path><path d="M40.0 55a10 10 0 0 1 10 10v316a10 10 0 0 0 10 10"></path><g class="terminal"> +<path d="M60.0 391h393.5"></path><path d="M524.5 391h393.5"></path><rect height="22" rx="10" ry="10" width="71.0" x="453.5" y="380"></rect><text x="489.0" y="395">"null"</text></g><path d="M918.0 391a10 10 0 0 0 10 -10v-316a10 10 0 0 1 10 -10"></path></g><path d="M 938.0 55 h 20 m -10 -10 v 20 m 10 -20 v 20"></path></g></svg> + </div> + </div> + +</body> +</html>
\ No newline at end of file diff --git a/pyparsing/diagram/__init__.py b/pyparsing/diagram/__init__.py index 4721c78..ec354a0 100644 --- a/pyparsing/diagram/__init__.py +++ b/pyparsing/diagram/__init__.py @@ -30,9 +30,10 @@ def get_name(element: pyparsing.ParserElement, default: str = None) -> str: return getattr(element, "name", default) -def railroad_to_html(diagrams: typing.List[NamedDiagram]) -> str: +def railroad_to_html(diagrams: typing.List[NamedDiagram], **kwargs) -> str: """ Given a list of NamedDiagram, produce a single HTML string that visualises those diagrams + :params kwargs: kwargs to be passed in to the template """ data = [] for diagram in diagrams: @@ -40,17 +41,21 @@ def railroad_to_html(diagrams: typing.List[NamedDiagram]) -> str: diagram.diagram.writeSvg(io.write) data.append({"title": diagram.name, "text": "", "svg": io.getvalue()}) - return template.render(diagrams=data) + return template.render(diagrams=data, **kwargs) -def to_railroad(element: pyparsing.ParserElement) -> typing.List[NamedDiagram]: +def to_railroad( + element: pyparsing.ParserElement, diagram_kwargs: dict = {} +) -> typing.List[NamedDiagram]: """ Convert a pyparsing element tree into a list of diagrams. This is the recommended entrypoint to diagram creation if you want to access the Railroad tree before it is converted to HTML + :param diagram_kwargs: kwargs to pass to the Diagram() constructor """ diagram_element, subdiagrams = _to_diagram_element(element) diagram = NamedDiagram( - get_name(element, "Grammar"), railroad.Diagram(diagram_element) + get_name(element, "Grammar"), + railroad.Diagram(diagram_element, **diagram_kwargs), ) return [diagram, *subdiagrams.values()] @@ -71,6 +76,7 @@ def _to_diagram_element( element: pyparsing.ParserElement, diagrams=None, vertical: typing.Union[int, bool] = 5, + diagram_kwargs: dict = {}, ) -> typing.Tuple[railroad.DiagramItem, typing.Dict[int, NamedDiagram]]: """ Recursively converts a PyParsing Element to a railroad Element @@ -114,7 +120,7 @@ def _to_diagram_element( # At this point we create a new subdiagram, and add it to the dictionary of diagrams forward_element, forward_diagrams = _to_diagram_element(exprs[0], diagrams) - diagram = railroad.Diagram(forward_element) + diagram = railroad.Diagram(forward_element, **diagram_kwargs) diagrams.update(forward_diagrams) diagrams[el_id] = diagrams[el_id]._replace(diagram=diagram) diagram.format(20) diff --git a/pyparsing/diagram/template.jinja2 b/pyparsing/diagram/template.jinja2 index 0f62426..ff09bcd 100644 --- a/pyparsing/diagram/template.jinja2 +++ b/pyparsing/diagram/template.jinja2 @@ -1,10 +1,27 @@ <!DOCTYPE html> <html> +<head> + {% if not head %} + <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap" rel="stylesheet"> + <style type="text/css"> + .railroad-heading { + font-family: 'Roboto', sans-serif; + } + </style> + {% else %} + {{ hear | safe }} + {% endif %} +</head> <body> +{{ body | safe }} {% for diagram in diagrams %} - <h1>{{ diagram.title }}</h1> - <div>{{ diagram.text }}</div> - {{ diagram.svg }} + <div class="railroad-group"> + <h1 class="railroad-heading">{{ diagram.title }}</h1> + <div class="railroad-description">{{ diagram.text }}</div> + <div class="railroad-svg"> + {{ diagram.svg }} + </div> + </div> {% endfor %} </body> </html> diff --git a/tests/test_diagram.py b/tests/test_diagram.py index 843228b..a449bb0 100644 --- a/tests/test_diagram.py +++ b/tests/test_diagram.py @@ -1,6 +1,8 @@ import unittest from examples.jsonParser import jsonObject from examples.simpleBool import boolExpr +from examples.simpleSQL import simpleSQL +from examples.mozillaCalendarParser import calendars from pyparsing.diagram import to_railroad, railroad_to_html import tempfile import os @@ -36,3 +38,19 @@ class TestRailroadDiagrams(unittest.TestCase): if self.railroad_debug(): print(temp.name) + + def test_sql(self): + with self.get_temp() as temp: + railroad = to_railroad(simpleSQL) + temp.write(railroad_to_html(railroad)) + + if self.railroad_debug(): + print(temp.name) + + def test_calendars(self): + with self.get_temp() as temp: + railroad = to_railroad(calendars) + temp.write(railroad_to_html(railroad)) + + if self.railroad_debug(): + print(temp.name) |