Permalink
Cannot retrieve contributors at this time
from spi import ( | |
Lexer, | |
Parser, | |
NodeVisitor, | |
BuiltinTypeSymbol, | |
VarSymbol, | |
ProcedureSymbol | |
) | |
class ScopedSymbolTable(object): | |
def __init__(self, scope_name, scope_level, enclosing_scope=None): | |
self._symbols = {} | |
self.scope_name = scope_name | |
self.scope_level = scope_level | |
self.enclosing_scope = enclosing_scope | |
def _init_builtins(self): | |
self.insert(BuiltinTypeSymbol('INTEGER')) | |
self.insert(BuiltinTypeSymbol('REAL')) | |
def __str__(self): | |
h1 = 'SCOPE (SCOPED SYMBOL TABLE)' | |
lines = ['\n', h1, '=' * len(h1)] | |
for header_name, header_value in ( | |
('Scope name', self.scope_name), | |
('Scope level', self.scope_level), | |
('Enclosing scope', | |
self.enclosing_scope.scope_name if self.enclosing_scope else None | |
) | |
): | |
lines.append('%-15s: %s' % (header_name, header_value)) | |
h2 = 'Scope (Scoped symbol table) contents' | |
lines.extend([h2, '-' * len(h2)]) | |
lines.extend( | |
('%7s: %r' % (key, value)) | |
for key, value in self._symbols.items() | |
) | |
lines.append('\n') | |
s = '\n'.join(lines) | |
return s | |
__repr__ = __str__ | |
def insert(self, symbol): | |
print('Insert: %s' % symbol.name) | |
self._symbols[symbol.name] = symbol | |
def lookup(self, name): | |
print('Lookup: %s. (Scope name: %s)' % (name, self.scope_name)) | |
# 'symbol' is either an instance of the Symbol class or None | |
symbol = self._symbols.get(name) | |
if symbol is not None: | |
return symbol | |
# recursively go up the chain and lookup the name | |
if self.enclosing_scope is not None: | |
return self.enclosing_scope.lookup(name) | |
class SemanticAnalyzer(NodeVisitor): | |
def __init__(self): | |
self.current_scope = None | |
def visit_Block(self, node): | |
for declaration in node.declarations: | |
self.visit(declaration) | |
self.visit(node.compound_statement) | |
def visit_Program(self, node): | |
print('ENTER scope: global') | |
global_scope = ScopedSymbolTable( | |
scope_name='global', | |
scope_level=1, | |
enclosing_scope=self.current_scope, # None | |
) | |
global_scope._init_builtins() | |
self.current_scope = global_scope | |
# visit subtree | |
self.visit(node.block) | |
print(global_scope) | |
self.current_scope = self.current_scope.enclosing_scope | |
print('LEAVE scope: global') | |
def visit_Compound(self, node): | |
for child in node.children: | |
self.visit(child) | |
def visit_NoOp(self, node): | |
pass | |
def visit_BinOp(self, node): | |
self.visit(node.left) | |
self.visit(node.right) | |
def visit_ProcedureDecl(self, node): | |
proc_name = node.proc_name | |
proc_symbol = ProcedureSymbol(proc_name) | |
self.current_scope.insert(proc_symbol) | |
print('ENTER scope: %s' % proc_name) | |
# Scope for parameters and local variables | |
procedure_scope = ScopedSymbolTable( | |
scope_name=proc_name, | |
scope_level=self.current_scope.scope_level + 1, | |
enclosing_scope=self.current_scope | |
) | |
self.current_scope = procedure_scope | |
# Insert parameters into the procedure scope | |
for param in node.params: | |
param_type = self.current_scope.lookup(param.type_node.value) | |
param_name = param.var_node.value | |
var_symbol = VarSymbol(param_name, param_type) | |
self.current_scope.insert(var_symbol) | |
proc_symbol.params.append(var_symbol) | |
self.visit(node.block_node) | |
print(procedure_scope) | |
self.current_scope = self.current_scope.enclosing_scope | |
print('LEAVE scope: %s' % proc_name) | |
def visit_VarDecl(self, node): | |
type_name = node.type_node.value | |
type_symbol = self.current_scope.lookup(type_name) | |
# We have all the information we need to create a variable symbol. | |
# Create the symbol and insert it into the symbol table. | |
var_name = node.var_node.value | |
var_symbol = VarSymbol(var_name, type_symbol) | |
self.current_scope.insert(var_symbol) | |
def visit_Assign(self, node): | |
# right-hand side | |
self.visit(node.right) | |
# left-hand side | |
self.visit(node.left) | |
def visit_Var(self, node): | |
var_name = node.value | |
var_symbol = self.current_scope.lookup(var_name) | |
if var_symbol is None: | |
raise Exception( | |
"Error: Symbol(identifier) not found '%s'" % var_name | |
) | |
if __name__ == '__main__': | |
text = """ | |
program Main; | |
var x, y: real; | |
procedure Alpha(a : integer); | |
var y : integer; | |
begin | |
x := a + x + y; | |
end; | |
begin { Main } | |
end. { Main } | |
""" | |
lexer = Lexer(text) | |
parser = Parser(lexer) | |
tree = parser.parse() | |
semantic_analyzer = SemanticAnalyzer() | |
try: | |
semantic_analyzer.visit(tree) | |
except Exception as e: | |
print(e) |