Assignment statement grammar (version 2)
Tree address code generation pass
ANTLR 4.x listener and visitor implementation for intermediate code generation (Three addresses code)
@author: Morteza Zakeri, (http://webpages.iust.ac.ir/morteza_zakeri/) @date: 20201017
- Compiler generator: ANTLR4.x
- Target language(s): Python3.x,
-Changelog:
-- v2.1.0
--- Add support for AST intermediate representation using module ast_pass
--- Change compiler_pass
module to three_address_code_pass
-- v2.0.0
--- Add attributes for grammar rules which are used to hold type and intermediate language_apps of rules.
- Reference: Compiler book by Dr. Saeed Parsa (http://parsa.iust.ac.ir/)
- Course website: http://parsa.iust.ac.ir/courses/compilers/
- Laboratory website: http://reverse.iust.ac.ir/
ThreeAddressCodeGenerator2Listener (AssignmentStatement2Listener)
Type checking and generating three address language_apps (optimizing number of temporary variables)
Source code in language_apps\assignment_statement_v2\three_address_code_pass.py
class ThreeAddressCodeGenerator2Listener(AssignmentStatement2Listener):
"""
Type checking and generating three address language_apps (optimizing number of temporary variables)
"""
def __init__(self):
print('Listener2 call!')
self.temp_counter = 0
def create_temp(self):
self.temp_counter += 1
return 'T' + str(self.temp_counter)
def remove_temp(self):
self.temp_counter -= 1
def get_temp(self):
return 'T' + str(self.temp_counter)
@classmethod
def is_temp(cls, variable):
if variable[0] == 'T':
return True
return False
# ------------------
# Rule number
def exitNumber_float(self, ctx: AssignmentStatement2Parser.Number_floatContext):
ctx.type_attr = 'float'
ctx.value_attr = float(ctx.getText())
def exitNumber_int(self, ctx: AssignmentStatement2Parser.Number_intContext):
ctx.type_attr = 'int'
ctx.value_attr = int(ctx.getText())
# ------------------
# Rule factor
def exitFact_expr(self, ctx: AssignmentStatement2Parser.Fact_exprContext):
ctx.type_attr = ctx.expr().type_attr
ctx.value_attr = ctx.expr().value_attr
def exitFact_id(self, ctx: AssignmentStatement2Parser.Fact_idContext):
ctx.type_attr = 'string'
ctx.value_attr = ctx.getText()
def exitFact_number(self, ctx: AssignmentStatement2Parser.Fact_numberContext):
ctx.type_attr = ctx.number().type_attr
ctx.value_attr = ctx.number().value_attr
# ------------------
# Rule term
def exitTerm_fact_mutiply(self, ctx: AssignmentStatement2Parser.Term_fact_mutiplyContext):
if ctx.term().type_attr != ctx.factor().type_attr:
print('Semantic error: Cannot multiply {0} and {1}'.format(ctx.term().type_attr, ctx.factor().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.term().value_attr * ctx.factor().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = ctx.term().value_attr * ctx.factor().value_attr
else:
ctx.type_attr = 'string'
if self.is_temp(ctx.term().value_attr):
ctx.value_attr = ctx.term().value_attr
if self.is_temp(ctx.factor().value_attr):
self.remove_temp()
elif self.is_temp(ctx.factor().value_attr):
ctx.value_attr = ctx.factor().value_attr
else:
ctx.value_attr = self.create_temp()
print('{0} = {1} * {2}'.format(ctx.value_attr, ctx.term().value_attr, ctx.factor().value_attr))
def exitTerm_fact_divide(self, ctx: AssignmentStatement2Parser.Term_fact_mutiplyContext):
if ctx.term().type_attr != ctx.factor().type_attr:
print('Semantic error: Cannot divide {0} and {1}'.format(ctx.term().type_attr, ctx.factor().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.term().value_attr / ctx.factor().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = int(ctx.term().value_attr / ctx.factor().value_attr)
else:
ctx.type_attr = 'string'
if self.is_temp(ctx.term().value_attr):
ctx.value_attr = ctx.term().value_attr
if self.is_temp(ctx.factor().value_attr):
self.remove_temp()
elif self.is_temp(ctx.factor().value_attr):
ctx.value_attr = ctx.factor().value_attr
else:
ctx.value_attr = self.create_temp()
print('{0} = {1} / {2}'.format(ctx.value_attr, ctx.term().value_attr, ctx.factor().value_attr))
def exitFactor3(self, ctx: AssignmentStatement2Parser.Factor3Context):
ctx.type_attr = ctx.factor().type_attr
ctx.value_attr = ctx.factor().value_attr
# ------------------
# Rule expr
def exitExpr_term_plus(self, ctx: AssignmentStatement2Parser.Expr_term_plusContext):
if ctx.expr().type_attr != ctx.term().type_attr:
print('Semantic error: Cannot plus {0} and {1}'.format(ctx.expr().type_attr, ctx.term().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.expr().value_attr + ctx.term().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = ctx.expr().value_attr + ctx.term().value_attr
else:
ctx.type_attr = 'string'
if self.is_temp(ctx.expr().value_attr):
ctx.value_attr = ctx.expr().value_attr
if self.is_temp(ctx.term().value_attr):
self.remove_temp()
elif self.is_temp(ctx.term().value_attr):
ctx.value_attr = ctx.term().value_attr
else:
ctx.value_attr = self.create_temp()
print('{0} = {1} + {2}'.format(ctx.value_attr, ctx.expr().value_attr, ctx.term().value_attr))
def exitExpr_term_minus(self, ctx: AssignmentStatement2Parser.Expr_term_minusContext):
if ctx.expr().type_attr != ctx.term().type_attr:
print('Semantic error: Cannot subtract {0} and {1}'.format(ctx.expr().type_attr, ctx.term().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.expr().value_attr - ctx.term().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = ctx.expr().value_attr - ctx.term().value_attr
else:
ctx.type_attr = 'string'
if self.is_temp(ctx.expr().value_attr):
ctx.value_attr = ctx.expr().value_attr
if self.is_temp(ctx.term().value_attr):
self.remove_temp()
elif self.is_temp(ctx.term().value_attr):
ctx.value_attr = ctx.term().value_attr
else:
ctx.value_attr = self.create_temp()
print('{0} = {1} - {2}'.format(ctx.value_attr, ctx.expr().value_attr, ctx.term().value_attr))
def exitTerm4(self, ctx: AssignmentStatement2Parser.Term4Context):
ctx.type_attr = ctx.term().type_attr
ctx.value_attr = ctx.term().value_attr
# ------------------
# Rule expr
def exitAssign(self, ctx: AssignmentStatement2Parser.AssignContext):
ctx.type_attr = ctx.expr().type_attr
ctx.value_attr = ctx.expr().value_attr
print('Assign statement: "{0} = {1}"\nAssign type: "{2}"'.format(ctx.ID().getText(), ctx.value_attr,
ctx.type_attr))
ThreeAddressCodeGenerator2Visitor (AssignmentStatement2Visitor)
Type checking and generating three address language_apps (optimizing number of temporary variables) Utilizing ANTLR 4.x Visitor mechanism
Source code in language_apps\assignment_statement_v2\three_address_code_pass.py
class ThreeAddressCodeGenerator2Visitor(AssignmentStatement2Visitor):
"""
Type checking and generating three address language_apps (optimizing number of temporary variables)
Utilizing ANTLR 4.x Visitor mechanism
"""
def __init__(self):
print('Visitor2 call!')
self.temp_counter = 0
def create_temp(self):
self.temp_counter += 1
return 'T' + str(self.temp_counter)
def remove_temp(self):
self.temp_counter -= 1
def get_temp(self):
return 'T' + str(self.temp_counter)
@classmethod
def is_temp(cls, variable):
if variable[0] == 'T':
return True
return False
def visitStart(self, ctx: AssignmentStatement2Parser.StartContext):
self.visit(tree=ctx.prog())
def visitProg(self, ctx: AssignmentStatement2Parser.ProgContext):
if ctx.getChildCount() == 2:
self.visit(tree=ctx.prog())
ctx.type_attr, ctx.value_attr = self.visit(tree=ctx.assign())
return ctx.type_attr, ctx.value_attr
def visitAssign(self, ctx: AssignmentStatement2Parser.AssignContext):
ctx.type_attr, ctx.value_attr = self.visit(tree=ctx.expr())
print('Assign statement: "{0} = {1}"\nAssign type: "{2}"'.format(ctx.ID().getText(), ctx.value_attr,
ctx.type_attr))
return ctx.type_attr, ctx.value_attr
# ------------------
# Rule expr
def visitExpr_term_plus(self, ctx: AssignmentStatement2Parser.Expr_term_plusContext):
ctx.expr().type_attr, ctx.expr().value_attr = self.visit(tree=ctx.expr())
ctx.term().type_attr, ctx.term().value_attr = self.visit(tree=ctx.term())
if ctx.expr().type_attr != ctx.term().type_attr:
print('Semantic error: Cannot plus {0} and {1}'.format(ctx.expr().type_attr, ctx.term().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.expr().value_attr + ctx.term().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = ctx.expr().value_attr + ctx.term().value_attr
else:
ctx.type_attr = 'string'
if self.is_temp(ctx.expr().value_attr):
ctx.value_attr = ctx.expr().value_attr
if self.is_temp(ctx.term().value_attr):
self.remove_temp()
elif self.is_temp(ctx.term().value_attr):
ctx.value_attr = ctx.term().value_attr
else:
ctx.value_attr = self.create_temp()
print('{0} = {1} + {2}'.format(ctx.value_attr, ctx.expr().value_attr, ctx.term().value_attr))
return ctx.type_attr, ctx.value_attr
def visitExpr_term_minus(self, ctx: AssignmentStatement2Parser.Expr_term_minusContext):
ctx.expr().type_attr, ctx.expr().value_attr = self.visit(tree=ctx.expr())
ctx.term().type_attr, ctx.term().value_attr = self.visit(tree=ctx.term())
if ctx.expr().type_attr != ctx.term().type_attr:
print('Semantic error: Cannot plus {0} and {1}'.format(ctx.expr().type_attr, ctx.term().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.expr().value_attr - ctx.term().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = ctx.expr().value_attr - ctx.term().value_attr
else:
ctx.type_attr = 'string'
if self.is_temp(ctx.expr().value_attr):
ctx.value_attr = ctx.expr().value_attr
if self.is_temp(ctx.term().value_attr):
self.remove_temp()
elif self.is_temp(ctx.term().value_attr):
ctx.value_attr = ctx.term().value_attr
else:
ctx.value_attr = self.create_temp()
print('{0} = {1} - {2}'.format(ctx.value_attr, ctx.expr().value_attr, ctx.term().value_attr))
return ctx.type_attr, ctx.value_attr
def visitTerm4(self, ctx: AssignmentStatement2Parser.Term4Context):
ctx.type_attr, ctx.value_attr = self.visit(ctx.term())
return ctx.type_attr, ctx.value_attr
# ------------------
# Rule term
def visitTerm_fact_mutiply(self, ctx: AssignmentStatement2Parser.Term_fact_mutiplyContext):
ctx.term().type_attr, ctx.term().value_attr = self.visit(tree=ctx.term())
ctx.factor().type_attr, ctx.factor().value_attr = self.visit(tree=ctx.factor())
if ctx.term().type_attr != ctx.factor().type_attr:
print('Semantic error: Cannot multiply {0} and {1}'.format(ctx.term().type_attr, ctx.factor().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.term().value_attr * ctx.factor().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = ctx.term().value_attr * ctx.factor().value_attr
else:
ctx.type_attr = 'string'
if self.is_temp(ctx.term().value_attr):
ctx.value_attr = ctx.term().value_attr
if self.is_temp(ctx.factor().value_attr):
self.remove_temp()
elif self.is_temp(ctx.factor().value_attr):
ctx.value_attr = ctx.factor().value_attr
else:
ctx.value_attr = self.create_temp()
print('{0} = {1} * {2}'.format(ctx.value_attr, ctx.term().value_attr, ctx.factor().value_attr))
return ctx.type_attr, ctx.value_attr
def visitTerm_fact_divide(self, ctx: AssignmentStatement2Parser.Term_fact_divideContext):
ctx.term().type_attr, ctx.term().value_attr = self.visit(tree=ctx.term())
ctx.factor().type_attr, ctx.factor().value_attr = self.visit(tree=ctx.factor())
if ctx.term().type_attr != ctx.factor().type_attr:
print('Semantic error: Cannot multiply {0} and {1}'.format(ctx.term().type_attr, ctx.factor().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.term().value_attr / ctx.factor().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = int(ctx.term().value_attr / ctx.factor().value_attr)
else:
ctx.type_attr = 'string'
if self.is_temp(ctx.term().value_attr):
ctx.value_attr = ctx.term().value_attr
if self.is_temp(ctx.factor().value_attr):
self.remove_temp()
elif self.is_temp(ctx.factor().value_attr):
ctx.value_attr = ctx.factor().value_attr
else:
ctx.value_attr = self.create_temp()
print('{0} = {1} / {2}'.format(ctx.value_attr, ctx.term().value_attr, ctx.factor().value_attr))
return ctx.type_attr, ctx.value_attr
def visitFactor3(self, ctx: AssignmentStatement2Parser.Factor3Context):
ctx.type_attr, ctx.value_attr = self.visit(tree=ctx.factor())
return ctx.type_attr, ctx.value_attr
# ------------------
# Rule factor
def visitFact_expr(self, ctx: AssignmentStatement2Parser.Fact_exprContext):
return self.visit(tree=ctx.expr())
def visitFact_id(self, ctx: AssignmentStatement2Parser.Fact_idContext):
return 'string', ctx.ID().getText()
def visitFact_number(self, ctx: AssignmentStatement2Parser.Fact_numberContext):
return self.visit(tree=ctx.number())
# ------------------
# Rule number
def visitNumber_float(self, ctx: AssignmentStatement2Parser.Number_floatContext):
return 'float', float(ctx.FLOAT().getText())
def visitNumber_int(self, ctx: AssignmentStatement2Parser.Number_intContext):
return 'int', int(ctx.INT().getText())
ThreeAddressCodeGeneratorListener (AssignmentStatement2Listener)
Type checking and generating three address language_apps (not optimized)
Source code in language_apps\assignment_statement_v2\three_address_code_pass.py
class ThreeAddressCodeGeneratorListener(AssignmentStatement2Listener):
"""
Type checking and generating three address language_apps (not optimized)
"""
def __init__(self):
print('Listener call!')
self.temp_counter = 0
def create_temp(self):
self.temp_counter += 1
return 'T' + str(self.temp_counter)
# ------------------
# Rule number
def exitNumber_float(self, ctx: AssignmentStatement2Parser.Number_floatContext):
ctx.type_attr = 'float'
ctx.value_attr = float(ctx.getText())
def exitNumber_int(self, ctx: AssignmentStatement2Parser.Number_intContext):
ctx.type_attr = 'int'
ctx.value_attr = int(ctx.getText())
# ------------------
# Rule factor
def exitFact_expr(self, ctx: AssignmentStatement2Parser.Fact_exprContext):
ctx.type_attr = ctx.expr().type_attr
ctx.value_attr = ctx.expr().value_attr
def exitFact_id(self, ctx: AssignmentStatement2Parser.Fact_idContext):
ctx.type_attr = 'string'
ctx.value_attr = str(ctx.getText())
def exitFact_number(self, ctx: AssignmentStatement2Parser.Fact_numberContext):
ctx.type_attr = ctx.number().type_attr
ctx.value_attr = ctx.number().value_attr
# ------------------
# Rule term
def exitTerm_fact_mutiply(self, ctx: AssignmentStatement2Parser.Term_fact_mutiplyContext):
if ctx.term().type_attr != ctx.factor().type_attr:
print('Semantic error: Cannot multiply {0} and {1}'.format(ctx.term().type_attr, ctx.factor().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.term().value_attr * ctx.factor().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = ctx.term().value_attr * ctx.factor().value_attr
else:
ctx.type_attr = 'string'
ctx.value_attr = self.create_temp()
print('{0} = {1} * {2}'.format(ctx.value_attr, ctx.term().value_attr, ctx.factor().value_attr))
def exitTerm_fact_divide(self, ctx: AssignmentStatement2Parser.Term_fact_mutiplyContext):
if ctx.term().type_attr != ctx.factor().type_attr:
print('Semantic error: Cannot divide {0} and {1}'.format(ctx.term().type_attr, ctx.factor().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.term().value_attr / ctx.factor().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = int(ctx.term().value_attr / ctx.factor().value_attr)
else:
ctx.type_attr = 'string'
ctx.value_attr = self.create_temp()
print('{0} = {1} / {2}'.format(ctx.value_attr, ctx.term().value_attr, ctx.factor().value_attr))
def exitFactor3(self, ctx: AssignmentStatement2Parser.Factor3Context):
ctx.type_attr = ctx.factor().type_attr
ctx.value_attr = ctx.factor().value_attr
# ------------------
# Rule expr
def exitExpr_term_plus(self, ctx: AssignmentStatement2Parser.Expr_term_plusContext):
if ctx.expr().type_attr != ctx.term().type_attr:
print('Semantic error: Cannot plus {0} and {1}'.format(ctx.expr().type_attr, ctx.term().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.expr().value_attr + ctx.term().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = ctx.expr().value_attr + ctx.term().value_attr
else:
ctx.type_attr = 'string'
ctx.value_attr = self.create_temp()
print('{0} = {1} + {2}'.format(ctx.value_attr, ctx.expr().value_attr, ctx.term().value_attr))
def exitExpr_term_minus(self, ctx: AssignmentStatement2Parser.Expr_term_minusContext):
if ctx.expr().type_attr != ctx.term().type_attr:
print('Semantic error: Cannot subtract {0} and {1}'.format(ctx.expr().type_attr, ctx.term().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.expr().value_attr - ctx.term().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = ctx.expr().value_attr - ctx.term().value_attr
else:
ctx.type_attr = 'string'
ctx.value_attr = self.create_temp()
print('{0} = {1} - {2}'.format(ctx.value_attr, ctx.expr().value_attr, ctx.term().value_attr))
def exitTerm4(self, ctx: AssignmentStatement2Parser.Term4Context):
ctx.type_attr = ctx.term().type_attr
ctx.value_attr = ctx.term().value_attr
# ------------------
# Rule expr
def exitAssign(self, ctx: AssignmentStatement2Parser.AssignContext):
ctx.type_attr = ctx.expr().type_attr
ctx.value_attr = ctx.expr().value_attr
print('Assign statement: "{0} = {1}"\nAssign type: "{2}"'.format(ctx.ID().getText(), ctx.value_attr,
ctx.type_attr))
ThreeAddressCodeGeneratorVisitor (AssignmentStatement2Visitor)
Type checking and generating three address language_apps (not optimized regarding to the number of temporary variables) Utilizing ANTLR 4.x Visitor mechanism
Source code in language_apps\assignment_statement_v2\three_address_code_pass.py
class ThreeAddressCodeGeneratorVisitor(AssignmentStatement2Visitor):
"""
Type checking and generating three address language_apps (not optimized regarding to the number of temporary variables)
Utilizing ANTLR 4.x Visitor mechanism
"""
def __init__(self):
print('Visitor call!')
self.temp_counter = 0
def create_temp(self):
self.temp_counter += 1
return 'T' + str(self.temp_counter)
def visitStart(self, ctx: AssignmentStatement2Parser.StartContext):
self.visit(tree=ctx.prog())
def visitProg(self, ctx: AssignmentStatement2Parser.ProgContext):
if ctx.getChildCount() == 2:
self.visit(tree=ctx.prog())
ctx.type_attr, ctx.value_attr = self.visit(tree=ctx.assign())
return ctx.type_attr, ctx.value_attr
def visitAssign(self, ctx: AssignmentStatement2Parser.AssignContext):
ctx.type_attr, ctx.value_attr = self.visit(tree=ctx.expr())
print('Assign statement: "{0} = {1}"\nAssign type: "{2}"'.format(ctx.ID().getText(), ctx.value_attr,
ctx.type_attr))
return ctx.type_attr, ctx.value_attr
# ------------------
# Rule expr
def visitExpr_term_plus(self, ctx: AssignmentStatement2Parser.Expr_term_plusContext):
ctx.expr().type_attr, ctx.expr().value_attr = self.visit(tree=ctx.expr())
ctx.term().type_attr, ctx.term().value_attr = self.visit(tree=ctx.term())
if ctx.expr().type_attr != ctx.term().type_attr:
print('Semantic error: Cannot plus {0} and {1}'.format(ctx.expr().type_attr, ctx.term().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.expr().value_attr + ctx.term().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = ctx.expr().value_attr + ctx.term().value_attr
else:
ctx.type_attr = 'string'
ctx.value_attr = self.create_temp()
print('{0} = {1} + {2}'.format(ctx.value_attr, ctx.expr().value_attr, ctx.term().value_attr))
return ctx.type_attr, ctx.value_attr
def visitExpr_term_minus(self, ctx: AssignmentStatement2Parser.Expr_term_minusContext):
ctx.expr().type_attr, ctx.expr().value_attr = self.visit(tree=ctx.expr())
ctx.term().type_attr, ctx.term().value_attr = self.visit(tree=ctx.term())
if ctx.expr().type_attr != ctx.term().type_attr:
print('Semantic error: Cannot plus {0} and {1}'.format(ctx.expr().type_attr, ctx.term().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.expr().value_attr - ctx.term().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = ctx.expr().value_attr - ctx.term().value_attr
else:
ctx.type_attr = 'string'
ctx.value_attr = self.create_temp()
print('{0} = {1} - {2}'.format(ctx.value_attr, ctx.expr().value_attr, ctx.term().value_attr))
return ctx.type_attr, ctx.value_attr
def visitTerm4(self, ctx: AssignmentStatement2Parser.Term4Context):
ctx.type_attr, ctx.value_attr = self.visit(ctx.term())
return ctx.type_attr, ctx.value_attr
# ------------------
# Rule term
def visitTerm_fact_mutiply(self, ctx: AssignmentStatement2Parser.Term_fact_mutiplyContext):
ctx.term().type_attr, ctx.term().value_attr = self.visit(tree=ctx.term())
ctx.factor().type_attr, ctx.factor().value_attr = self.visit(tree=ctx.factor())
if ctx.term().type_attr != ctx.factor().type_attr:
print('Semantic error: Cannot multiply {0} and {1}'.format(ctx.term().type_attr, ctx.factor().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.term().value_attr * ctx.factor().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = ctx.term().value_attr * ctx.factor().value_attr
else:
ctx.type_attr = 'string'
ctx.value_attr = self.create_temp()
print('{0} = {1} * {2}'.format(ctx.value_attr, ctx.term().value_attr, ctx.factor().value_attr))
return ctx.type_attr, ctx.value_attr
def visitTerm_fact_divide(self, ctx: AssignmentStatement2Parser.Term_fact_divideContext):
ctx.term().type_attr, ctx.term().value_attr = self.visit(tree=ctx.term())
ctx.factor().type_attr, ctx.factor().value_attr = self.visit(tree=ctx.factor())
if ctx.term().type_attr != ctx.factor().type_attr:
print('Semantic error: Cannot multiply {0} and {1}'.format(ctx.term().type_attr, ctx.factor().type_attr))
quit(-1)
else:
if ctx.term().type_attr == 'float':
ctx.type_attr = 'float'
ctx.value_attr = ctx.term().value_attr / ctx.factor().value_attr
elif ctx.term().type_attr == 'int':
ctx.type_attr = 'int'
ctx.value_attr = int(ctx.term().value_attr / ctx.factor().value_attr)
else:
ctx.type_attr = 'string'
ctx.value_attr = self.create_temp()
print('{0} = {1} / {2}'.format(ctx.value_attr, ctx.term().value_attr, ctx.factor().value_attr))
return ctx.type_attr, ctx.value_attr
def visitFactor3(self, ctx: AssignmentStatement2Parser.Factor3Context):
ctx.type_attr, ctx.value_attr = self.visit(tree=ctx.factor())
return ctx.type_attr, ctx.value_attr
# ------------------
# Rule factor
def visitFact_expr(self, ctx: AssignmentStatement2Parser.Fact_exprContext):
return self.visit(tree=ctx.expr())
def visitFact_id(self, ctx: AssignmentStatement2Parser.Fact_idContext):
return 'string', ctx.ID().getText()
def visitFact_number(self, ctx: AssignmentStatement2Parser.Fact_numberContext):
return self.visit(tree=ctx.number())
# ------------------
# Rule number
def visitNumber_float(self, ctx: AssignmentStatement2Parser.Number_floatContext):
return 'float', float(ctx.FLOAT().getText())
def visitNumber_int(self, ctx: AssignmentStatement2Parser.Number_intContext):
return 'int', int(ctx.INT().getText())
Abstract syntax tree (AST) generation pass
ANTLR 4.x listener and visitor implementation for intermediate code generation (abstract syntax trees)
@author: Morteza Zakeri, (http://webpages.iust.ac.ir/morteza_zakeri/) @date: 20201117
- Compiler generator: ANTRL4.x
- Target language(s): Python3.x,
-Changelog:
-- v2.1.0
--- Add support for AST visualization with dummy nodes
--- Add support for AST intermediate representation using module ast_pass
--- Change compiler_pass
module to three_address_code_pass
-- v2.0.0
--- Add attributes for grammar rules which are used to hold type and intermediate language_apps of rules.
- Reference: Compiler book by Dr. Saeed Parsa (http://parsa.iust.ac.ir/)
- Course website: http://parsa.iust.ac.ir/courses/compilers/
- Laboratory website: http://reverse.iust.ac.ir/
ASTListener (AssignmentStatement2Listener)
Source code in language_apps\assignment_statement_v2\abstract_syntax_tree_pass.py
class ASTListener(AssignmentStatement2Listener):
"""
"""
def __init__(self):
self.ast = AST() # Data structure for holding the abstract syntax tree
self.q = queue.Queue() # Use to print and visualize AST
self.g = nx.DiGraph() # Use to visualize AST
# self.q.empty()
# print('Q=', )
def print_tree(self, node=None, level=1):
if node is None:
# print()
return
# if not self.q.empty():
# print('Parent:', self.q.get().value)
# print('\t'*level, end='')
print()
while node is not None:
current_node = node
print(current_node.value, end='') # alt+196 = ───, alt+178=▓
if node.child is not None:
# self.q.put(node)
self.g.add_edge(current_node, node.child, edge_type='C', color='red')
self.q.put(node.child)
else:
tn = TreeNode(value='▓', child=None, brother=None)
self.g.add_edge(current_node, tn, edge_type='C', color='red')
node = node.brother
if node is not None:
print('\t───\t', end='')
self.g.add_edge(current_node, node, edge_type='B', color='blue')
else:
tn = TreeNode(value='▓', child=None, brother=None)
self.g.add_edge(current_node, tn, edge_type='B', color='blue')
if not self.q.empty():
self.print_tree(node=self.q.get(), level=level + 1)
def print_tree2(self, node=None):
pass
def exitAssign(self, ctx: AssignmentStatement2Parser.AssignContext):
idPntr = self.ast.make_node(value=ctx.ID().getText(), child=None, brother=ctx.expr().value_attr)
assPntr = self.ast.make_node(value=":=", child=idPntr, brother=None)
ctx.value_attr = assPntr
self.ast.root = assPntr
self.print_tree(node=self.ast.root, level=1)
def exitExpr_term_plus(self, ctx: AssignmentStatement2Parser.Expr_term_plusContext):
self.ast.add_brother(ctx.expr().value_attr, ctx.term().value_attr)
exprPntr = self.ast.make_node(value="+", child=ctx.expr().value_attr, brother=None)
ctx.value_attr = exprPntr
def exitExpr_term_minus(self, ctx: AssignmentStatement2Parser.Expr_term_plusContext):
self.ast.add_brother(ctx.expr().value_attr, ctx.term().value_attr)
exprPntr = self.ast.make_node(value="-", child=ctx.expr().value_attr, brother=None)
ctx.value_attr = exprPntr
def exitTerm4(self, ctx: AssignmentStatement2Parser.Term4Context):
ctx.value_attr = ctx.term().value_attr
# ----------------------
def exitTerm_fact_mutiply(self, ctx: AssignmentStatement2Parser.Term_fact_mutiplyContext):
self.ast.add_brother(ctx.term().value_attr, ctx.factor().value_attr)
termPntr = self.ast.make_node(value="*", child=ctx.term().value_attr, brother=None)
ctx.value_attr = termPntr
def exitTerm_fact_divide(self, ctx: AssignmentStatement2Parser.Term_fact_divideContext):
self.ast.add_brother(ctx.term().value_attr, ctx.factor().value_attr)
termPntr = self.ast.make_node(value="/", child=ctx.term().value_attr, brother=None)
ctx.value_attr = termPntr
def exitFactor3(self, ctx: AssignmentStatement2Parser.Factor3Context):
ctx.value_attr = ctx.factor().value_attr
# ---------------------
def exitFact_expr(self, ctx: AssignmentStatement2Parser.Fact_exprContext):
ctx.value_attr = ctx.expr().value_attr
def exitFact_id(self, ctx: AssignmentStatement2Parser.Fact_idContext):
idPntr = self.ast.make_node(value=ctx.ID().getText(), child=None, brother=None)
ctx.value_attr = idPntr
def exitFact_number(self, ctx: AssignmentStatement2Parser.Fact_numberContext):
ctx.value_attr = ctx.number().value_attr
# ----------------------
def exitNumber_float(self, ctx: AssignmentStatement2Parser.Number_floatContext):
numberPntr = self.ast.make_node(value=ctx.FLOAT().getText(), child=None, brother=None)
ctx.value_attr = numberPntr
def exitNumber_int(self, ctx: AssignmentStatement2Parser.Number_intContext):
numberPntr = self.ast.make_node(value=ctx.INT().getText(), child=None, brother=None)
ctx.value_attr = numberPntr
Main driver
Main script for grammar AssignmentStatement2 (version 2) Contains attributes for holding rule type and rule intermediate representations (AST and Three-addresses codes)
author
Morteza Zakeri, (http://webpages.iust.ac.ir/morteza_zakeri/)
date
20201029
- Compiler generator: ANTLR 4.x
- Target language(s): Python 3.8.x
Install pygraphviz
To draw AST as a binary tree you need to install the pygraphviz 1- download and install graphviz (for Windows/ Linux) 2- add graphviz to system path 3- install pygraphviz using the following command python -m pip install --global-option=build_ext --global-option="-IC:\Program Files\Graphviz\include" --global-option="-LC:\Program Files\Graphviz\lib" pygraphviz
Changelog
v2.1.1
- Add visualization with Graphviz.
v2.1.0
- Add support for AST intermediate representation using module
ast_pass
- Change
compiler_pass
module tothree_address_code_pass
v2.0.0
- Add attributes for grammar rules which are used to hold type and intermediate language_apps of rules.
Refs
- Reference: Compiler book by Dr. Saeed Parsa (http://parsa.iust.ac.ir/)
- Course website: http://parsa.iust.ac.ir/courses/compilers/
- Laboratory website: http://reverse.iust.ac.ir/
draw(g=None)
Draw abstract syntax tree
Parameters:
Name | Type | Description | Default |
---|---|---|---|
g |
nx.DiGraph) |
None |
Returns:
Type | Description |
---|---|
None |
Source code in language_apps\assignment_statement_v2\assignment_statement2main.py
def draw(g: nx.DiGraph = None):
"""
Draw abstract syntax tree
Args:
g (nx.DiGraph) :
Returns:
None
"""
pos = graphviz_layout(
G=g,
prog='dot',
# prog='circo',
)
# pos = hierarchy_pos(G=g,)
# pos = nx.kamada_kawai_layout(G=g)
# pos = nx.bipartite_layout(G=g, nodes=g.nodes)
# pos = nx.spectral_layout(G=g)
# pos = nx.spiral_layout(G=g)
# pos = nx.spiral_layout(G=g)
colors = [g[u][v]['color'] for u, v in g.edges]
nx.draw(g,
with_labels=False,
node_size=500,
node_color='black',
edge_color=colors,
pos=pos,
)
edge_labels = nx.get_edge_attributes(g, 'edge_type')
# print('#', edge_labels)
nx.draw_networkx_edge_labels(g, pos, edge_labels=edge_labels, )
node_labels = {}
for node in g.nodes():
# set the node name as the key and the label as its value
node_labels[node] = node.value
nx.draw_networkx_labels(g, pos, node_labels, font_size=12, font_color='w')
plt.savefig('../../docs/figs/ast4.png')
plt.show()
draw_graphviz(g=None)
Visualize abstract syntax tree with Graphviz
Arges:
g (nx.DiGraph): The abstract syntax tree to be converted to the dot file
Returns:
Type | Description |
---|---|
None |
References:
[1] https://graphviz.org/Gallery/directed/psg.html
Source code in language_apps\assignment_statement_v2\assignment_statement2main.py
def draw_graphviz(g: nx.DiGraph = None):
"""
Visualize abstract syntax tree with Graphviz
Arges:
g (nx.DiGraph): The abstract syntax tree to be converted to the dot file
Returns:
None
References:
[1] https://graphviz.org/Gallery/directed/psg.html
"""
pydot_graph = nx.drawing.nx_pydot.to_pydot(g)
# nx.drawing.nx_pydot.write_dot(func_graph, self.cfg_path + str(self.domain_name) + '.dot')
pydot_graph2 = pydot.Dot("", graph_type="digraph")
nid = 0
for u, v in g.edges:
if u.value == u'\u2593':
# u.value = 'NULL'
node_u = pydot.Node(name=f'node_id_{nid}', label=u.value, shape='box')
nid += 1
else:
node_u = pydot.Node(name=u.value, label=u.value, shape='box')
if v.value == u'\u2593':
# v.value = 'NULL'
node_v = pydot.Node(name=f'node_id_{nid}', label=v.value, shape='box')
nid += 1
else:
node_v = pydot.Node(name=v.value, label=v.value, shape='box')
print(u.value, v.value)
# edge_obj_dict = dict()
# edge_obj_dict.update({'color': g[u][v]['color']})
# edge_obj_dict.update({'label': g[u][v]['edge_type']})
edge_ = pydot.Edge(src=node_u, dst=node_v, color=g[u][v]['color'], label=g[u][v]['edge_type'])
pydot_graph2.add_node(node_u)
pydot_graph2.add_node(node_v)
pydot_graph2.add_edge(edge_)
pydot_graph2.write('../../docs/figs/ast2gv.dot', encoding='utf-8', )
# pydot_graph2.write_png('../../docs/figs/ast2.png')
result = subprocess.run(
['dot', '-Tpng',
'../../docs/figs/ast2gv.dot',
'-o',
'../../docs/figs/ast2gv.png'
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
print(result.returncode)
error_ = result.stderr.decode('utf-8')
print(error_)
hierarchy_pos(G, root=None, width=1.0, vert_gap=0.2, vert_loc=0, xcenter=0.5)
From Joel's answer at https://stackoverflow.com/a/29597209/2966723. Licensed under Creative Commons Attribution-Share Alike
If the graph is a tree this will return the positions to plot this in a hierarchical layout.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
G |
nx.Graph |
the graph (must be a tree) |
required |
root |
nx.Node |
the root node of current branch |
None |
width |
float |
horizontal space allocated for this branch - avoids overlap with other branches |
1.0 |
vert_gap |
float |
gap between levels of hierarchy |
0.2 |
vert_loc |
float |
vertical location of root |
0 |
xcenter |
float |
horizontal location of root |
0.5 |
Source code in language_apps\assignment_statement_v2\assignment_statement2main.py
def hierarchy_pos(G, root=None, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5):
"""
From Joel's answer at https://stackoverflow.com/a/29597209/2966723.
Licensed under Creative Commons Attribution-Share Alike
If the graph is a tree this will return the positions to plot this in a
hierarchical layout.
Args:
G (nx.Graph): the graph (must be a tree)
root (nx.Node): the root node of current branch
- if the tree is directed and this is not given, the root will be found and used
- if the tree is directed and this is given, then the positions will be just for the descendants of this node.
- if the tree is undirected and not given,then a random choice will be used.
width (float): horizontal space allocated for this branch - avoids overlap with other branches
vert_gap (float): gap between levels of hierarchy
vert_loc (float): vertical location of root
xcenter (float): horizontal location of root
"""
if not nx.is_tree(G):
raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')
if root is None:
if isinstance(G, nx.DiGraph):
root = next(iter(nx.topological_sort(G))) # allows back compatibility with nx version 1.11
else:
root = random.choice(list(G.nodes))
def _hierarchy_pos(G, root, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5, pos=None, parent=None):
"""
see hierarchy_pos docstring for most arguments
pos: a dict saying where all nodes go if they have been assigned
parent: parent of this branch. - only affects it if non-directed
"""
if pos is None:
pos = {root: (xcenter, vert_loc)}
else:
pos[root] = (xcenter, vert_loc)
children = list(G.neighbors(root))
if not isinstance(G, nx.DiGraph) and parent is not None:
children.remove(parent)
if len(children) != 0:
dx = width / len(children)
nextx = xcenter - width / 2 - dx / 2
for child in children:
nextx += dx
pos = _hierarchy_pos(
G,
child,
width=dx,
vert_gap=vert_gap,
vert_loc=vert_loc - vert_gap, xcenter=nextx,
pos=pos, parent=root
)
return pos
return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
main(args)
Create lexer and parser and execute AST listener
Parameters:
Name | Type | Description | Default |
---|---|---|---|
args |
Arg |
required |
Returns:
Type | Description |
---|---|
None |
Source code in language_apps\assignment_statement_v2\assignment_statement2main.py
def main(args):
"""
Create lexer and parser and execute AST listener
Args:
args (Arg):
Returns:
None
"""
# Step 1: Load input source into stream
stream = FileStream(args.file, encoding='utf8')
print('Input language_apps:\n{0}'.format(stream))
print('Result:')
# Step 2: Create an instance of AssignmentStLexer
lexer = AssignmentStatement2Lexer(stream)
# Step 3: Convert the input source into a list of tokens
token_stream = CommonTokenStream(lexer)
# Step 4: Create an instance of the AssignmentStParser
parser = AssignmentStatement2Parser(token_stream)
# Step 5: Create parse tree
parse_tree = parser.start()
# Step 6: Create an instance of AssignmentStListener
code_generator_listener = ThreeAddressCodeGeneratorListener()
# code_generator_listener = ThreeAddressCodeGenerator2Listener()
# ast_generator = ASTListener()
# Step 7(a): Walk parse tree with a customized listener (Automatically)
walker = ParseTreeWalker()
walker.walk(t=parse_tree, listener=code_generator_listener) # or
# walker.walk(t=parse_tree, listener=ast_generator)
# print('\nG=', ast_generator.g.edges)
# draw(g=ast_generator.g)
# draw_graphviz(g=ast_generator.g)
# Step 7(b): Walk parse tree with a customize visitor (Manually)
# code_generator_vistor = ThreeAddressCodeGeneratorVisitor()
# code_generator_vistor = ThreeAddressCodeGenerator2Visitor()
# code_generator_vistor.visitStart(ctx=parse_tree.getRuleContext())