Call with something like:
> echo '11*19 + 3 * 9'|python exp2gviz.py -|dot -Earrowsize=0.4 -Nfixedsize=1 -Grankdir=BT -Tpng>gviz_out.png;feh gviz_out.png
To get something like:
import networkx | |
import pyparsing | |
import random | |
import click | |
import os | |
import sys | |
class ExpressionToGraph: | |
def __init__(self): | |
self._g = networkx.DiGraph() | |
number = pyparsing.Regex("[0-9]+").setParseAction(lambda s,loc,tok:self._add_node(int(tok[0]))) | |
self._parser = pyparsing.infixNotation(number, [(pyparsing.oneOf("* /"), 2, pyparsing.opAssoc.LEFT, lambda s, loc, tok:self._add_expression_node(tok[0], str(tok[0][1]))), | |
(pyparsing.oneOf("+ -"), 2, pyparsing.opAssoc.LEFT, lambda s, loc, tok:self._add_expression_node(tok[0], str(tok[0][1])))]).setParseAction(lambda s,loc,tok:tok[0]) | |
@property | |
def graph(self): | |
return self._g | |
def _add_node(self, node_label, shape="circle"): | |
node_id = self._get_node_id() | |
self._g.add_node(node_id, label = node_label, shape=shape) | |
return node_id | |
def _add_expression_node(self, operands, node_label): | |
p_node_id = operands[0] | |
for op_idx, op in filter(lambda x:x[1] in ["*","-","+","/"], enumerate(operands)): | |
new_node_id = self._add_node(op) | |
self._g.add_edge(p_node_id,new_node_id) | |
self._g.add_edge(operands[op_idx+1],new_node_id) | |
p_node_id=new_node_id | |
return new_node_id | |
@staticmethod | |
def _get_node_id(): | |
return "".join(["0123456789ABCDEF"[random.randint(0,15)] for k in range(0,8)]) | |
def __call__(self, expression): | |
self._parser.parseString(expression) | |
@click.command() | |
@click.argument("input_file", type=click.File()) | |
@click.option("-o", "--output_file", type=click.Path(exists=False), default=None) | |
def main(input_file, output_file): | |
m = ExpressionToGraph() | |
q = m(input_file.read()) | |
if output_file is None: | |
output_fd = sys.stdout | |
else: | |
output_fd = open(output_file, "w") | |
networkx.nx_pydot.write_dot(m.graph, output_fd) | |
if __name__ == "__main__": | |
main() |
appdirs==1.4.3 | |
backcall==0.2.0 | |
CacheControl==0.12.6 | |
certifi==2020.6.20 | |
chardet==3.0.4 | |
click==7.1.2 | |
colorama==0.4.3 | |
contextlib2==0.6.0 | |
cycler==0.10.0 | |
decorator==4.4.2 | |
distlib==0.3.0 | |
distro==1.4.0 | |
fancycompleter==0.9.1 | |
html5lib==1.0.1 | |
idna==2.8 | |
ipaddr==2.2.0 | |
ipython==7.19.0 | |
ipython-genutils==0.2.0 | |
jedi==0.17.2 | |
kiwisolver==1.3.1 | |
lockfile==0.12.2 | |
matplotlib==3.3.2 | |
msgpack==0.6.2 | |
networkx==2.5 | |
numpy==1.19.4 | |
packaging==20.3 | |
parso==0.7.1 | |
pdbpp==0.10.2 | |
pep517==0.8.2 | |
pexpect==4.8.0 | |
pickleshare==0.7.5 | |
Pillow==8.0.1 | |
progress==1.5 | |
prompt-toolkit==3.0.8 | |
ptyprocess==0.6.0 | |
pydot==1.4.1 | |
Pygments==2.7.2 | |
pyparsing==2.4.6 | |
PyQt5==5.15.1 | |
PyQt5-sip==12.8.1 | |
pyrepl==0.9.0 | |
python-dateutil==2.8.1 | |
pytoml==0.1.21 | |
requests==2.22.0 | |
retrying==1.3.3 | |
six==1.14.0 | |
traitlets==5.0.5 | |
urllib3==1.25.8 | |
wcwidth==0.2.5 | |
webencodings==0.5.1 | |
wmctrl==0.3 |