Skip to content

Commit 626ad73

Browse files
committed
Add part 18
1 parent 6f3cffd commit 626ad73

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2337
-59
lines changed

part 14 - nested scopes/lesson/src/grammar.ebnf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
# Declaration
1010

1111
<declarations> ::= <VAR> { <variable_declaration> <SEMI> }+
12-
| { <PROCEDURE> <ID> {LPAREN <formal_parameter_list> RPAREN}* <SEMI> <block> <SEMI> }* # TODO: check grammar
12+
| { <PROCEDURE> <ID> {LPAREN <formal_parameter_list> RPAREN}* <SEMI> <block> <SEMI> }*
1313
| <empty>
1414

1515
<variable_declaration> ::= <ID> { <COMMA> <ID> }* <COLON> <type_spec>

part 16 - recognizing procedure calls/practice/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
3535
<assignment_statement> ::= <variable> <ASSIGN> <expression>
3636
37-
<procedure_call_statement> ::= <ID> <LPAREN> [ <expression> { <COMMA> <expression> }* ] <RPAREN> # TODO
37+
<procedure_call_statement> ::= <ID> <LPAREN> [ <expression> { <COMMA> <expression> }* ] <RPAREN>
3838
3939
<empty> ::= ''
4040

part 17 - call stack and activation records/lesson/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Part 16 - Practice
1+
# Part 17 - Practice
22

33
## Grammar
44

@@ -34,7 +34,7 @@
3434
3535
<assignment_statement> ::= <variable> <ASSIGN> <expression>
3636
37-
<procedure_call_statement> ::= <ID> <LPAREN> [ <expression> { <COMMA> <expression> }* ] <RPAREN> # TODO
37+
<procedure_call_statement> ::= <ID> <LPAREN> [ <expression> { <COMMA> <expression> }* ] <RPAREN>
3838
3939
<empty> ::= ''
4040
@@ -176,4 +176,4 @@ end. { Main }
176176

177177
## Reference
178178

179-
https://ruslanspivak.com/lsbasi-part16/
179+
https://ruslanspivak.com/lsbasi-part17/

part 17 - call stack and activation records/lesson/tmp.py

Lines changed: 0 additions & 54 deletions
This file was deleted.
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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from enum import Enum
2+
3+
4+
class ARType(Enum):
5+
PROGRAM = 'PROGRAM'
6+
PROCEDURE = 'PROCEDURE'
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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
92+
self.proc_symbol = None
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: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 __len__(self):
15+
return len(self._records)
16+
17+
@property
18+
def size(self):
19+
return self.__len__()
20+
21+
def __str__(self):
22+
s = '\n'.join(repr(ar) for ar in reversed(self._records))
23+
s = f'CALL STACK\n{s}\n'
24+
return s
25+
26+
def __repr__(self):
27+
return self.__str__()

0 commit comments

Comments
 (0)