Skip to content

Commit

Permalink
Part 10
Browse files Browse the repository at this point in the history
  • Loading branch information
rspivak committed Aug 5, 2016
1 parent 19a5e5e commit 6f74208
Show file tree
Hide file tree
Showing 6 changed files with 1,001 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ Source code for the series **Let's Build A Simple Interpreter**
+ [Let's Build A Simple Interpreter. Part 7.](http://ruslanspivak.com/lsbasi-part7/)
+ [Let's Build A Simple Interpreter. Part 8.](http://ruslanspivak.com/lsbasi-part8/)
+ [Let's Build A Simple Interpreter. Part 9.](http://ruslanspivak.com/lsbasi-part9/)
+ [Let's Build A Simple Interpreter. Part 10.](http://ruslanspivak.com/lsbasi-part10/)
169 changes: 169 additions & 0 deletions part10/python/genastdot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
###############################################################################
# AST visualizer - generates a DOT file for Graphviz. #
# #
# To generate an image from the DOT file run $ dot -Tpng -o ast.png ast.dot #
# #
###############################################################################
import argparse
import textwrap

from spi import Lexer, Parser, NodeVisitor


class ASTVisualizer(NodeVisitor):
def __init__(self, parser):
self.parser = parser
self.ncount = 1
self.dot_header = [textwrap.dedent("""\
digraph astgraph {
node [shape=circle, fontsize=12, fontname="Courier", height=.1];
ranksep=.3;
edge [arrowsize=.5]
""")]
self.dot_body = []
self.dot_footer = ['}']

def visit_Program(self, node):
s = ' node{} [label="Program"]\n'.format(self.ncount)
self.dot_body.append(s)
node._num = self.ncount
self.ncount += 1

self.visit(node.block)

s = ' node{} -> node{}\n'.format(node._num, node.block._num)
self.dot_body.append(s)

def visit_Block(self, node):
s = ' node{} [label="Block"]\n'.format(self.ncount)
self.dot_body.append(s)
node._num = self.ncount
self.ncount += 1

for declaration in node.declarations:
self.visit(declaration)
self.visit(node.compound_statement)

for decl_node in node.declarations:
s = ' node{} -> node{}\n'.format(node._num, decl_node._num)
self.dot_body.append(s)

s = ' node{} -> node{}\n'.format(
node._num,
node.compound_statement._num
)
self.dot_body.append(s)

def visit_VarDecl(self, node):
s = ' node{} [label="VarDecl"]\n'.format(self.ncount)
self.dot_body.append(s)
node._num = self.ncount
self.ncount += 1

self.visit(node.var_node)
s = ' node{} -> node{}\n'.format(node._num, node.var_node._num)
self.dot_body.append(s)

self.visit(node.type_node)
s = ' node{} -> node{}\n'.format(node._num, node.type_node._num)
self.dot_body.append(s)

def visit_Type(self, node):
s = ' node{} [label="{}"]\n'.format(self.ncount, node.token.value)
self.dot_body.append(s)
node._num = self.ncount
self.ncount += 1

def visit_Num(self, node):
s = ' node{} [label="{}"]\n'.format(self.ncount, node.token.value)
self.dot_body.append(s)
node._num = self.ncount
self.ncount += 1

def visit_BinOp(self, node):
s = ' node{} [label="{}"]\n'.format(self.ncount, node.op.value)
self.dot_body.append(s)
node._num = self.ncount
self.ncount += 1

self.visit(node.left)
self.visit(node.right)

for child_node in (node.left, node.right):
s = ' node{} -> node{}\n'.format(node._num, child_node._num)
self.dot_body.append(s)

def visit_UnaryOp(self, node):
s = ' node{} [label="unary {}"]\n'.format(self.ncount, node.op.value)
self.dot_body.append(s)
node._num = self.ncount
self.ncount += 1

self.visit(node.expr)
s = ' node{} -> node{}\n'.format(node._num, node.expr._num)
self.dot_body.append(s)

def visit_Compound(self, node):
s = ' node{} [label="Compound"]\n'.format(self.ncount)
self.dot_body.append(s)
node._num = self.ncount
self.ncount += 1

for child in node.children:
self.visit(child)
s = ' node{} -> node{}\n'.format(node._num, child._num)
self.dot_body.append(s)

def visit_Assign(self, node):
s = ' node{} [label="{}"]\n'.format(self.ncount, node.op.value)
self.dot_body.append(s)
node._num = self.ncount
self.ncount += 1

self.visit(node.left)
self.visit(node.right)

for child_node in (node.left, node.right):
s = ' node{} -> node{}\n'.format(node._num, child_node._num)
self.dot_body.append(s)

def visit_Var(self, node):
s = ' node{} [label="{}"]\n'.format(self.ncount, node.value)
self.dot_body.append(s)
node._num = self.ncount
self.ncount += 1

def visit_NoOp(self, node):
s = ' node{} [label="NoOp"]\n'.format(self.ncount)
self.dot_body.append(s)
node._num = self.ncount
self.ncount += 1

def gendot(self):
tree = self.parser.parse()
self.visit(tree)
return ''.join(self.dot_header + self.dot_body + self.dot_footer)


def main():
argparser = argparse.ArgumentParser(
description='Generate an AST DOT file.'
)
argparser.add_argument(
'fname',
help='Pascal source file'
)
args = argparser.parse_args()
fname = args.fname
text = open(fname, 'r').read()

lexer = Lexer(text)
parser = Parser(lexer)
viz = ASTVisualizer(parser)
content = viz.gendot()
print(content)


if __name__ == '__main__':
main()
22 changes: 22 additions & 0 deletions part10/python/part10.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
PROGRAM Part10;
VAR
number : INTEGER;
a, b, c, x : INTEGER;
y : REAL;

BEGIN {Part10}
BEGIN
number := 2;
a := number;
b := 10 * a + 10 * number DIV 4;
c := a - - b
END;
x := 11;
y := 20 / 7 + 3.14;
{ writeln('a = ', a); }
{ writeln('b = ', b); }
{ writeln('c = ', c); }
{ writeln('number = ', number); }
{ writeln('x = ', x); }
{ writeln('y = ', y); }
END. {Part10}
10 changes: 10 additions & 0 deletions part10/python/part10ast.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
PROGRAM Part10AST;
VAR
a, b : INTEGER;
y : REAL;

BEGIN {Part10AST}
a := 2;
b := 10 * a + 10 * a DIV 4;
y := 20 / 7 + 3.14;
END. {Part10AST}
Loading

0 comments on commit 6f74208

Please sign in to comment.