Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
###############################################################################
# #
# Parse Tree visualizer #
# #
# To generate an image from the DOT file run: #
# $ dot -Tpng -o parsetree.png parsetree.dot #
# #
###############################################################################
import argparse
import textwrap
from spi import PLUS, MINUS, MUL, DIV, INTEGER, LPAREN, RPAREN, Lexer
class Node(object):
def __init__(self, name):
self.name = name
self.children = []
def add(self, node):
self.children.append(node)
class RuleNode(Node):
pass
class TokenNode(Node):
pass
class Parser(object):
"""Parses the input and builds a parse tree."""
def __init__(self, lexer):
self.lexer = lexer
# set current token to the first token taken from the input
self.current_token = self.lexer.get_next_token()
# Parse tree root
self.root = None
self.current_node = None
def error(self):
raise Exception('Invalid syntax')
def eat(self, token_type):
# compare the current token type with the passed token
# type and if they match then "eat" the current token
# and assign the next token to the self.current_token,
# otherwise raise an exception.
if self.current_token.type == token_type:
self.current_node.add(TokenNode(self.current_token.value))
self.current_token = self.lexer.get_next_token()
else:
self.error()
def factor(self):
"""factor : INTEGER | LPAREN expr RPAREN"""
node = RuleNode('factor')
self.current_node.add(node)
_save = self.current_node
self.current_node = node
token = self.current_token
if token.type == INTEGER:
self.eat(INTEGER)
elif token.type == LPAREN:
self.eat(LPAREN)
self.expr()
self.eat(RPAREN)
self.current_node = _save
def term(self):
"""term : factor ((MUL | DIV) factor)*"""
node = RuleNode('term')
self.current_node.add(node)
_save = self.current_node
self.current_node = node
self.factor()
while self.current_token.type in (MUL, DIV):
token = self.current_token
if token.type == MUL:
self.eat(MUL)
elif token.type == DIV:
self.eat(DIV)
self.factor()
self.current_node = _save
def expr(self):
"""
expr : term ((PLUS | MINUS) term)*
term : factor ((MUL | DIV) factor)*
factor : INTEGER | LPAREN expr RPAREN
"""
node = RuleNode('expr')
if self.root is None:
self.root = node
else:
self.current_node.add(node)
_save = self.current_node
self.current_node = node
self.term()
while self.current_token.type in (PLUS, MINUS):
token = self.current_token
if token.type == PLUS:
self.eat(PLUS)
elif token.type == MINUS:
self.eat(MINUS)
self.term()
self.current_node = _save
def parse(self):
self.expr()
return self.root
class ParseTreeVisualizer(object):
def __init__(self, parser):
self.parser = parser
self.ncount = 1
self.dot_header = [textwrap.dedent("""\
digraph astgraph {
node [shape=none, fontsize=12, fontname="Courier", height=.1];
ranksep=.3;
edge [arrowsize=.5]
""")]
self.dot_body = []
self.dot_footer = ['}']
def bfs(self, node):
ncount = 1
queue = []
queue.append(node)
s = ' node{} [label="{}"]\n'.format(ncount, node.name)
self.dot_body.append(s)
node._num = ncount
ncount += 1
while queue:
node = queue.pop(0)
for child_node in node.children:
s = ' node{} [label="{}"]\n'.format(ncount, child_node.name)
self.dot_body.append(s)
child_node._num = ncount
ncount += 1
s = ' node{} -> node{}\n'.format(node._num, child_node._num)
self.dot_body.append(s)
queue.append(child_node)
def gendot(self):
tree = self.parser.parse()
self.bfs(tree)
return ''.join(self.dot_header + self.dot_body + self.dot_footer)
def main():
argparser = argparse.ArgumentParser(
description='Generate a Parse Tree DOT file.'
)
argparser.add_argument(
'text',
help='Arithmetic expression (in quotes): "1 + 2 * 3"'
)
args = argparser.parse_args()
text = args.text
lexer = Lexer(text)
parser = Parser(lexer)
viz = ParseTreeVisualizer(parser)
content = viz.gendot()
print(content)
if __name__ == '__main__':
main()