Skip to content

Instantly share code, notes, and snippets.

@maxfischer2781
Created June 27, 2021 17:33
Show Gist options
  • Save maxfischer2781/cf8b3887f09172804d6600ce19fc3416 to your computer and use it in GitHub Desktop.
Save maxfischer2781/cf8b3887f09172804d6600ce19fc3416 to your computer and use it in GitHub Desktop.
Railroad for a left-recursive PyParsing grammar
import pyparsing as pp
from pyparsing.diagram import to_railroad, railroad_to_html
import tempfile
import webbrowser
pp.ParserElement.enable_left_recursion()
# named references
expr = pp.Forward().setName("expr")
add_sub = pp.Forward().setName("add_sub")
mul_div = pp.Forward().setName("mul_div")
power = pp.Forward().setName("power")
terminal = pp.Forward().setName("terminal")
# concrete rules
number = pp.Word(pp.nums).setParseAction(lambda t: int(t[0]))
signed = ('+' - expr) | ('-' - expr).setParseAction(lambda t: -t[1])
group = pp.Suppress('(') - expr - pp.Suppress(')')
add_sub <<= (
(add_sub + '+' - mul_div).setParseAction(lambda t: t[0] + t[2])
| (add_sub + '-' - mul_div).setParseAction(lambda t: t[0] - t[2])
| mul_div
)
mul_div <<= (
(mul_div + '*' - power).setParseAction(lambda t: t[0] * t[2])
| (mul_div + '/' - power).setParseAction(lambda t: t[0] / t[2])
| power
)
power <<= (
(terminal + '^' - power).setParseAction(lambda t: t[0] ** t[2])
| terminal
)
terminal <<= number | signed | group
expr <<= add_sub
with tempfile.NamedTemporaryFile(mode="w", suffix=".html") as tf:
tf.write(railroad_to_html(to_railroad(expr)))
webbrowser.open_new_tab(f"file://{tf.name}")
input()
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
.railroad-heading {
font-family: monospace;
}
</style>
</head>
<body>
<div class="railroad-group">
<h1 class="railroad-heading">expr</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">add&#95;sub</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">add_sub</h1>
<div class="railroad-description"></div>
<div class="railroad-svg">
<svg class="railroad-diagram" height="88" viewBox="0 0 788.5 88" width="788.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 38v20m10 -20v20m-10 -10h20"></path></g><g>
<path d="M40 48h0.0"></path><path d="M748.5 48h0.0"></path><path d="M40.0 48a10 10 0 0 0 10 -10v-8a10 10 0 0 1 10 -10h559.0"></path><path d="M639.0 68h89.5a10 10 0 0 0 10 -10v0a10 10 0 0 1 10 -10"></path><path d="M40.0 48h10"></path><g>
<path d="M50.0 48h0.0"></path><path d="M619.0 48h0.0"></path><path d="M50.0 48a10 10 0 0 0 10 -10v0a10 10 0 0 1 10 -10h254.5"></path><path d="M344.5 68h254.5a10 10 0 0 0 10 -10v0a10 10 0 0 1 10 -10"></path><path d="M50.0 48h10"></path><g>
<path d="M60.0 48h10.0"></path><path d="M314.5 48h10.0"></path><g>
<path d="M70.0 48h0.0"></path><path d="M215.0 48h0.0"></path><g>
<path d="M70.0 48h0.0"></path><path d="M215.0 48h0.0"></path><g class="non-terminal ">
<path d="M70.0 48h0.0"></path><path d="M149.5 48h0.0"></path><rect height="22" width="79.5" x="70.0" y="37"></rect><text x="109.75" y="52">add&#95;sub</text></g><path d="M149.5 48h10"></path><path d="M159.5 48h10"></path><g class="terminal ">
<path d="M169.5 48h0.0"></path><path d="M215.0 48h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="169.5" y="37"></rect><text x="192.25" y="52">'+'</text></g></g></g><path d="M215.0 48h10"></path><path d="M225.0 48h10"></path><g class="non-terminal ">
<path d="M235.0 48h0.0"></path><path d="M314.5 48h0.0"></path><rect height="22" width="79.5" x="235.0" y="37"></rect><text x="274.75" y="52">mul&#95;div</text></g></g><path d="M324.5 48a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path><path d="M324.5 28a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path><g>
<path d="M344.5 48h10.0"></path><path d="M599.0 48h10.0"></path><g>
<path d="M354.5 48h0.0"></path><path d="M499.5 48h0.0"></path><g>
<path d="M354.5 48h0.0"></path><path d="M499.5 48h0.0"></path><g class="non-terminal ">
<path d="M354.5 48h0.0"></path><path d="M434.0 48h0.0"></path><rect height="22" width="79.5" x="354.5" y="37"></rect><text x="394.25" y="52">add&#95;sub</text></g><path d="M434.0 48h10"></path><path d="M444.0 48h10"></path><g class="terminal ">
<path d="M454.0 48h0.0"></path><path d="M499.5 48h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="454.0" y="37"></rect><text x="476.75" y="52">'-'</text></g></g></g><path d="M499.5 48h10"></path><path d="M509.5 48h10"></path><g class="non-terminal ">
<path d="M519.5 48h0.0"></path><path d="M599.0 48h0.0"></path><rect height="22" width="79.5" x="519.5" y="37"></rect><text x="559.25" y="52">mul&#95;div</text></g></g><path d="M609.0 48h10"></path></g><path d="M619.0 48a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path><path d="M619.0 20a10 10 0 0 1 10 10v8a10 10 0 0 0 10 10"></path><g class="non-terminal ">
<path d="M639.0 48h10.0"></path><path d="M728.5 48h10.0"></path><rect height="22" width="79.5" x="649.0" y="37"></rect><text x="688.75" y="52">mul&#95;div</text></g><path d="M738.5 48h10"></path></g><path d="M 748.5 48 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">mul_div</h1>
<div class="railroad-description"></div>
<div class="railroad-svg">
<svg class="railroad-diagram" height="88" viewBox="0 0 737.5 88" width="737.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 38v20m10 -20v20m-10 -10h20"></path></g><g>
<path d="M40 48h0.0"></path><path d="M697.5 48h0.0"></path><path d="M40.0 48a10 10 0 0 0 10 -10v-8a10 10 0 0 1 10 -10h525.0"></path><path d="M605.0 68h72.5a10 10 0 0 0 10 -10v0a10 10 0 0 1 10 -10"></path><path d="M40.0 48h10"></path><g>
<path d="M50.0 48h0.0"></path><path d="M585.0 48h0.0"></path><path d="M50.0 48a10 10 0 0 0 10 -10v0a10 10 0 0 1 10 -10h237.5"></path><path d="M327.5 68h237.5a10 10 0 0 0 10 -10v0a10 10 0 0 1 10 -10"></path><path d="M50.0 48h10"></path><g>
<path d="M60.0 48h10.0"></path><path d="M297.5 48h10.0"></path><g>
<path d="M70.0 48h0.0"></path><path d="M215.0 48h0.0"></path><g>
<path d="M70.0 48h0.0"></path><path d="M215.0 48h0.0"></path><g class="non-terminal ">
<path d="M70.0 48h0.0"></path><path d="M149.5 48h0.0"></path><rect height="22" width="79.5" x="70.0" y="37"></rect><text x="109.75" y="52">mul&#95;div</text></g><path d="M149.5 48h10"></path><path d="M159.5 48h10"></path><g class="terminal ">
<path d="M169.5 48h0.0"></path><path d="M215.0 48h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="169.5" y="37"></rect><text x="192.25" y="52">'&#42;'</text></g></g></g><path d="M215.0 48h10"></path><path d="M225.0 48h10"></path><g class="non-terminal ">
<path d="M235.0 48h0.0"></path><path d="M297.5 48h0.0"></path><rect height="22" width="62.5" x="235.0" y="37"></rect><text x="266.25" y="52">power</text></g></g><path d="M307.5 48a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path><path d="M307.5 28a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path><g>
<path d="M327.5 48h10.0"></path><path d="M565.0 48h10.0"></path><g>
<path d="M337.5 48h0.0"></path><path d="M482.5 48h0.0"></path><g>
<path d="M337.5 48h0.0"></path><path d="M482.5 48h0.0"></path><g class="non-terminal ">
<path d="M337.5 48h0.0"></path><path d="M417.0 48h0.0"></path><rect height="22" width="79.5" x="337.5" y="37"></rect><text x="377.25" y="52">mul&#95;div</text></g><path d="M417.0 48h10"></path><path d="M427.0 48h10"></path><g class="terminal ">
<path d="M437.0 48h0.0"></path><path d="M482.5 48h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="437.0" y="37"></rect><text x="459.75" y="52">'/'</text></g></g></g><path d="M482.5 48h10"></path><path d="M492.5 48h10"></path><g class="non-terminal ">
<path d="M502.5 48h0.0"></path><path d="M565.0 48h0.0"></path><rect height="22" width="62.5" x="502.5" y="37"></rect><text x="533.75" y="52">power</text></g></g><path d="M575.0 48h10"></path></g><path d="M585.0 48a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path><path d="M585.0 20a10 10 0 0 1 10 10v8a10 10 0 0 0 10 10"></path><g class="non-terminal ">
<path d="M605.0 48h10.0"></path><path d="M677.5 48h10.0"></path><rect height="22" width="62.5" x="615.0" y="37"></rect><text x="646.25" y="52">power</text></g><path d="M687.5 48h10"></path></g><path d="M 697.5 48 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">power</h1>
<div class="railroad-description"></div>
<div class="railroad-svg">
<svg class="railroad-diagram" height="80" viewBox="0 0 484.0 80" width="484.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 30v20m10 -20v20m-10 -10h20"></path></g><g>
<path d="M40 40h0.0"></path><path d="M444.0 40h0.0"></path><path d="M40.0 40a10 10 0 0 0 10 -10v0a10 10 0 0 1 10 -10h246.0"></path><path d="M326.0 60h98.0a10 10 0 0 0 10 -10v0a10 10 0 0 1 10 -10"></path><path d="M40.0 40h10"></path><g>
<path d="M50.0 40h10.0"></path><path d="M296.0 40h10.0"></path><g>
<path d="M60.0 40h0.0"></path><path d="M213.5 40h0.0"></path><g>
<path d="M60.0 40h0.0"></path><path d="M213.5 40h0.0"></path><g class="non-terminal ">
<path d="M60.0 40h0.0"></path><path d="M148.0 40h0.0"></path><rect height="22" width="88.0" x="60.0" y="29"></rect><text x="104.0" y="44">terminal</text></g><path d="M148.0 40h10"></path><path d="M158.0 40h10"></path><g class="terminal ">
<path d="M168.0 40h0.0"></path><path d="M213.5 40h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="168.0" y="29"></rect><text x="190.75" y="44">'^'</text></g></g></g><path d="M213.5 40h10"></path><path d="M223.5 40h10"></path><g class="non-terminal ">
<path d="M233.5 40h0.0"></path><path d="M296.0 40h0.0"></path><rect height="22" width="62.5" x="233.5" y="29"></rect><text x="264.75" y="44">power</text></g></g><path d="M306.0 40a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path><path d="M306.0 20a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path><g class="non-terminal ">
<path d="M326.0 40h10.0"></path><path d="M424.0 40h10.0"></path><rect height="22" width="88.0" x="336.0" y="29"></rect><text x="380.0" y="44">terminal</text></g><path d="M434.0 40h10"></path></g><path d="M 444.0 40 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">terminal</h1>
<div class="railroad-description"></div>
<div class="railroad-svg">
<svg class="railroad-diagram" height="103" viewBox="0 0 824.5 103" width="824.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 45v20m10 -20v20m-10 -10h20"></path></g><g>
<path d="M40 55h0.0"></path><path d="M784.5 55h0.0"></path><path d="M40.0 55a10 10 0 0 0 10 -10v-8a10 10 0 0 1 10 -10h448.5"></path><path d="M528.5 82h236.0a10 10 0 0 0 10 -10v-7a10 10 0 0 1 10 -10"></path><path d="M40.0 55h10"></path><g>
<path d="M50.0 55h0.0"></path><path d="M508.5 55h0.0"></path><path d="M50.0 55a10 10 0 0 0 10 -10v0a10 10 0 0 1 10 -10h89.5"></path><path d="M179.5 83h309.0a10 10 0 0 0 10 -10v-8a10 10 0 0 1 10 -10"></path><path d="M50.0 55h10"></path><g class="terminal ">
<path d="M60.0 55h10.0"></path><path d="M149.5 55h10.0"></path><rect height="22" rx="10" ry="10" width="79.5" x="70.0" y="44"></rect><text x="109.75" y="59">W:(0-9)</text></g><path d="M159.5 55a10 10 0 0 1 10 10v8a10 10 0 0 0 10 10"></path><path d="M159.5 35a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path><g>
<path d="M179.5 55h0.0"></path><path d="M498.5 55h0.0"></path><path d="M179.5 55a10 10 0 0 0 10 -10v0a10 10 0 0 1 10 -10h129.5"></path><path d="M349.0 75h129.5a10 10 0 0 0 10 -10v0a10 10 0 0 1 10 -10"></path><path d="M179.5 55h10"></path><g>
<path d="M189.5 55h10.0"></path><path d="M319.0 55h10.0"></path><g>
<path d="M199.5 55h0.0"></path><path d="M245.0 55h0.0"></path><g class="terminal ">
<path d="M199.5 55h0.0"></path><path d="M245.0 55h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="199.5" y="44"></rect><text x="222.25" y="59">'+'</text></g></g><path d="M245.0 55h10"></path><path d="M255.0 55h10"></path><g class="non-terminal ">
<path d="M265.0 55h0.0"></path><path d="M319.0 55h0.0"></path><rect height="22" width="54.0" x="265.0" y="44"></rect><text x="292.0" y="59">expr</text></g></g><path d="M329.0 55a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path><path d="M329.0 35a10 10 0 0 1 10 10v0a10 10 0 0 0 10 10"></path><g>
<path d="M349.0 55h10.0"></path><path d="M478.5 55h10.0"></path><g>
<path d="M359.0 55h0.0"></path><path d="M404.5 55h0.0"></path><g class="terminal ">
<path d="M359.0 55h0.0"></path><path d="M404.5 55h0.0"></path><rect height="22" rx="10" ry="10" width="45.5" x="359.0" y="44"></rect><text x="381.75" y="59">'-'</text></g></g><path d="M404.5 55h10"></path><path d="M414.5 55h10"></path><g class="non-terminal ">
<path d="M424.5 55h0.0"></path><path d="M478.5 55h0.0"></path><rect height="22" width="54.0" x="424.5" y="44"></rect><text x="451.5" y="59">expr</text></g></g><path d="M488.5 55h10"></path></g><path d="M498.5 55h10"></path></g><path d="M508.5 55a10 10 0 0 1 10 10v7a10 10 0 0 0 10 10"></path><path d="M508.5 27a10 10 0 0 1 10 10v8a10 10 0 0 0 10 10"></path><g>
<path d="M528.5 55h10.0"></path><path d="M764.5 55h10.0"></path><g>
<path d="M538.5 55h0.0"></path><path d="M678.5 55h0.0"></path><g>
<path d="M538.5 55h0.0"></path><path d="M678.5 55h0.0"></path><g>
<path d="M538.5 55h0.0"></path><path d="M604.5 55h0.0"></path><g>
<path d="M538.5 55h0.0"></path><path d="M604.5 55h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="66" x="538.5" y="36"></rect><g class="terminal ">
<path d="M538.5 55h10.25"></path><path d="M594.25 55h10.25"></path><rect height="22" rx="10" ry="10" width="45.5" x="548.75" y="44"></rect><text x="571.5" y="59">'('</text></g><g class="non-terminal ">
<path d="M538.5 28h0.0"></path><path d="M604.5 28h0.0"></path><text class="comment" x="571.5" y="33">Suppress</text></g></g></g><path d="M604.5 55h10"></path><path d="M614.5 55h10"></path><g class="non-terminal ">
<path d="M624.5 55h0.0"></path><path d="M678.5 55h0.0"></path><rect height="22" width="54.0" x="624.5" y="44"></rect><text x="651.5" y="59">expr</text></g></g></g><path d="M678.5 55h10"></path><path d="M688.5 55h10"></path><g>
<path d="M698.5 55h0.0"></path><path d="M764.5 55h0.0"></path><rect class="group-box" height="38" rx="10" ry="10" width="66" x="698.5" y="36"></rect><g class="terminal ">
<path d="M698.5 55h10.25"></path><path d="M754.25 55h10.25"></path><rect height="22" rx="10" ry="10" width="45.5" x="708.75" y="44"></rect><text x="731.5" y="59">')'</text></g><g class="non-terminal ">
<path d="M698.5 28h0.0"></path><path d="M764.5 28h0.0"></path><text class="comment" x="731.5" y="33">Suppress</text></g></g></g><path d="M774.5 55h10"></path></g><path d="M 784.5 55 h 20 m -10 -10 v 20 m 10 -20 v 20"></path></g></svg>
</div>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment