Skip to content

Commit 6f3cffd

Browse files
committed
Add part 17
1 parent fa90da7 commit 6f3cffd

37 files changed

+2330
-1
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from ActivationRecord import ActivationRecord
2+
from ScopedSymbolTable import ScopedSymbolTable
3+
4+
5+
class ARNode:
6+
def __init__(self, scope):
7+
self.scope: ScopedSymbolTable = scope
8+
self.ar_records: list[ActivationRecord] = []
9+
self.children: list[ARNode] = []
10+
11+
def __str__(self):
12+
return f'{self.scope.scope_name} {self.scope.scope_level} <- {self.scope.enclosing_scope.scope_name if self.scope.enclosing_scope else None}'
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from ActivationRecord import ActivationRecord
2+
from ARNode import ARNode
3+
4+
5+
class ARTree:
6+
def __init__(self):
7+
self.root: ARNode
8+
9+
def build_tree(self, scopes):
10+
"""
11+
Builds the tree from the list of scopes. The first scope in the list is
12+
built-in scope and the root of the tree. Its only child is second scope in the list
13+
- the global scope. Other scopes are children or ancestors of the global scope. It
14+
is guaranteed that that each scope is unique and that at least built-in and global
15+
scopes are present in the list.
16+
"""
17+
self.root = ARNode(scopes[0])
18+
19+
for scope in scopes[1:]:
20+
node = ARNode(scope)
21+
parent = self._find_parent(scope, self.root)
22+
parent.children.append(node)
23+
24+
def _find_parent(self, scope, root: ARNode) -> ARNode:
25+
"""
26+
Finds the parent of the given scope. It is guaranteed that the scope has a parent.
27+
"""
28+
29+
if scope.enclosing_scope == root.scope and scope.scope_level == root.scope.scope_level + 1:
30+
return root
31+
else:
32+
for child in root.children:
33+
node = self._find_parent(scope, child)
34+
if node:
35+
return node
36+
37+
return ARNode(None)
38+
39+
def _find_node(self, name: str, level: int, root: ARNode) -> ARNode:
40+
"""
41+
Finds the node with the given name and level. It is guaranteed that the node exists.
42+
"""
43+
if root.scope.scope_name == name and root.scope.scope_level == level:
44+
return root
45+
else:
46+
for child in root.children:
47+
node = self._find_node(name, level, child)
48+
if node:
49+
return node
50+
51+
return ARNode(None)
52+
53+
def push(self, AR: ActivationRecord) -> None:
54+
node = self._find_node(AR.scope_name, AR.nesting_level, self.root)
55+
node.ar_records.append(AR)
56+
57+
def __str__(self):
58+
return '\n'.join([i.__str__() for i in self.bf_traverse(self.root)])
59+
60+
def preorder_traverse(self, root: ARNode) -> list[ARNode]:
61+
"""
62+
Performs a pre-order traversal of the tree.
63+
Returns a list of nodes in the order they were visited.
64+
"""
65+
nodes = [root]
66+
67+
for child in root.children:
68+
nodes += self.preorder_traverse(child)
69+
70+
return nodes
71+
72+
def bf_traverse(self, root: ARNode) -> list[ARNode]:
73+
"""
74+
Performs a breadth-first traversal of the tree.
75+
Returns a list of nodes in the order they were visited.
76+
"""
77+
nodes = [root]
78+
queue = [root]
79+
80+
while queue:
81+
node = queue.pop(0)
82+
for child in node.children:
83+
nodes.append(child)
84+
queue.append(child)
85+
86+
return nodes
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from enum import Enum
2+
3+
4+
class ARType(Enum):
5+
PROGRAM = 'PROGRAM'
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
class AST(object):
2+
pass
3+
4+
5+
class BinOp(AST):
6+
def __init__(self, left, op, right):
7+
self.left = left
8+
self.token = self.op = op
9+
self.right = right
10+
11+
12+
class Num(AST):
13+
def __init__(self, token):
14+
self.token = token
15+
self.value = token.value
16+
17+
18+
class UnaryOp(AST):
19+
def __init__(self, op, expr):
20+
self.token = self.op = op
21+
self.expr = expr
22+
23+
24+
class Compound(AST):
25+
"""Represents a 'BEGIN ... END' block"""
26+
27+
def __init__(self):
28+
self.children = []
29+
30+
31+
class Assign(AST):
32+
def __init__(self, left, op, right):
33+
self.left = left
34+
self.token = self.op = op
35+
self.right = right
36+
37+
38+
class Var(AST):
39+
"""The Var node is constructed out of ID token."""
40+
41+
def __init__(self, token):
42+
self.token = token
43+
self.value = token.value
44+
45+
46+
class NoOp(AST):
47+
pass
48+
49+
50+
class Program(AST):
51+
def __init__(self, name, block):
52+
self.name = name
53+
self.block = block
54+
55+
56+
class Block(AST):
57+
def __init__(self, declarations, compound_statement):
58+
self.declarations = declarations
59+
self.compound_statement = compound_statement
60+
61+
62+
class VarDecl(AST):
63+
def __init__(self, var_node, type_node):
64+
self.var_node = var_node
65+
self.type_node = type_node
66+
67+
68+
class Type(AST):
69+
def __init__(self, token):
70+
self.token = token
71+
self.value = token.value
72+
73+
74+
class ProcedureDecl(AST):
75+
def __init__(self, proc_name, params, block_node):
76+
self.proc_name = proc_name
77+
self.params = params
78+
self.block_node = block_node
79+
80+
81+
class Param(AST):
82+
def __init__(self, var_node, type_node):
83+
self.var_node = var_node
84+
self.type_node = type_node
85+
86+
87+
class ProcedureCall(AST):
88+
def __init__(self, proc_name, actual_params, token):
89+
self.proc_name = proc_name
90+
self.actual_params = actual_params
91+
self.token = token
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
class ActivationRecord:
2+
def __init__(self, name, scope_name, type, nesting_level, execution_order):
3+
self.name = name
4+
self.scope_name = scope_name
5+
self.type = type
6+
self.nesting_level = nesting_level
7+
self.exdecution_order = execution_order
8+
self.members = {}
9+
10+
def __setitem__(self, key, value):
11+
self.members[key] = value
12+
13+
def __getitem__(self, key):
14+
return self.members[key]
15+
16+
def get(self, key):
17+
return self.members.get(key)
18+
19+
def __str__(self):
20+
lines = [
21+
'{level}: {type} {name} (execution order: {execution_order})'.format(
22+
level=self.nesting_level,
23+
type=self.type.name,
24+
name=self.name,
25+
execution_order=self.exdecution_order,
26+
),
27+
]
28+
for name, val in self.members.items():
29+
lines.append(f' {name:<20}: {val}')
30+
31+
s = '\n'.join(lines)
32+
return s
33+
34+
def __repr__(self):
35+
return self.__str__()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class CallStack:
2+
def __init__(self):
3+
self._records = []
4+
5+
def push(self, ar):
6+
self._records.append(ar)
7+
8+
def pop(self):
9+
return self._records.pop()
10+
11+
def peek(self):
12+
return self._records[-1]
13+
14+
def __str__(self):
15+
s = '\n'.join(repr(ar) for ar in reversed(self._records))
16+
s = f'CALL STACK\n{s}\n'
17+
return s
18+
19+
def __repr__(self):
20+
return self.__str__()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from enum import Enum
2+
3+
4+
class ErrorCode(Enum):
5+
UNEXPECTED_TOKEN = 'Unexpected token'
6+
ID_NOT_FOUND = 'Identifier not found'
7+
DUPLICATE_ID = 'Duplicate id found'
8+
WRONG_ARGUMENTS_NUMBER = 'Wrong number of arguments'
9+
10+
11+
class Error(Exception):
12+
def __init__(self, error_code=None, token=None, message=None):
13+
self.error_code = error_code
14+
self.token = token
15+
self.message = f'{self.__class__.__name__}: {message}'
16+
17+
def __str__(self):
18+
return self.message
19+
20+
21+
class LexerError(Error):
22+
pass
23+
24+
25+
class ParserError(Error):
26+
pass
27+
28+
29+
class SemanticError(Error):
30+
pass
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
from ActivationRecord import ActivationRecord
2+
from ARTree import ARTree
3+
from ARType import ARType
4+
from CallStack import CallStack
5+
from NodeVisitor import NodeVisitor
6+
from SemanticAnalyzer import SemanticAnalyzer
7+
from TokenType import TokenType
8+
9+
10+
class Interpreter(NodeVisitor):
11+
def __init__(self, text):
12+
self.semantic_analyzer = SemanticAnalyzer(text)
13+
self.call_stack = CallStack()
14+
self.ar_tree = ARTree()
15+
self.global_execution_order = 0
16+
17+
def visit_Program(self, node):
18+
program_name = node.name
19+
20+
AR = ActivationRecord(
21+
name=program_name,
22+
scope_name='global',
23+
type=ARType.PROGRAM,
24+
nesting_level=1,
25+
execution_order=self.global_execution_order,
26+
)
27+
28+
self.call_stack.push(AR)
29+
self.global_execution_order += 1
30+
31+
self.visit(node.block)
32+
33+
self.ar_tree.push(self.call_stack.pop())
34+
35+
def visit_Block(self, node):
36+
for declaration in node.declarations:
37+
self.visit(declaration)
38+
self.visit(node.compound_statement)
39+
40+
def visit_VarDecl(self, node):
41+
# Do nothing
42+
pass
43+
44+
def visit_Type(self, node):
45+
# Do nothing
46+
pass
47+
48+
def visit_BinOp(self, node):
49+
if node.op.type == TokenType.PLUS:
50+
return self.visit(node.left) + self.visit(node.right)
51+
elif node.op.type == TokenType.MINUS:
52+
return self.visit(node.left) - self.visit(node.right)
53+
elif node.op.type == TokenType.MUL:
54+
return self.visit(node.left) * self.visit(node.right)
55+
elif node.op.type == TokenType.INTEGER_DIV:
56+
return self.visit(node.left) // self.visit(node.right)
57+
elif node.op.type == TokenType.FLOAT_DIV:
58+
return float(self.visit(node.left)) / float(self.visit(node.right))
59+
60+
def visit_Num(self, node):
61+
return node.value
62+
63+
def visit_UnaryOp(self, node):
64+
op = node.op.type
65+
if op == TokenType.PLUS:
66+
return +self.visit(node.expr)
67+
elif op == TokenType.MINUS:
68+
return -self.visit(node.expr)
69+
70+
def visit_Compound(self, node):
71+
for child in node.children:
72+
self.visit(child)
73+
74+
def visit_Assign(self, node):
75+
var_name = node.left.value
76+
var_value = self.visit(node.right)
77+
78+
AR = self.call_stack.peek()
79+
AR[var_name] = var_value
80+
81+
def visit_Var(self, node):
82+
var_name = node.value
83+
84+
AR = self.call_stack.peek()
85+
var_value = AR.get(var_name)
86+
87+
return var_value
88+
89+
def visit_NoOp(self, node):
90+
pass
91+
92+
def visit_ProcedureDecl(self, node):
93+
pass
94+
95+
def visit_ProcedureCall(self, node):
96+
pass
97+
98+
def interpret(self):
99+
tree = self.semantic_analyzer.analyze()
100+
101+
if tree is None:
102+
return ''
103+
104+
self.ar_tree.build_tree(self.semantic_analyzer.scopes)
105+
106+
return self.visit(tree)

0 commit comments

Comments
 (0)