cap-lab22-samy/MiniC/TP03/MiniCTypingVisitor.py

239 lines
7.9 KiB
Python

# Visitor to *typecheck* MiniC files
from typing import List
from MiniCVisitor import MiniCVisitor
from MiniCParser import MiniCParser
from Lib.Errors import MiniCInternalError, MiniCTypeError
from enum import Enum
class BaseType(Enum):
Float, Integer, Boolean, String = range(4)
# Basic Type Checking for MiniC programs.
class MiniCTypingVisitor(MiniCVisitor):
def __init__(self):
self._memorytypes = dict() # id -> types
# For now, we don't have real functions ...
self._current_function = "main"
def _raise(self, ctx, for_what, *types):
raise MiniCTypeError(
'In function {}: Line {} col {}: invalid type for {}: {}'.format(
self._current_function,
ctx.start.line, ctx.start.column, for_what,
' and '.join(t.name.lower() for t in types)))
def _assertSameType(self, ctx, for_what, *types):
if not all(types[0] == t for t in types):
raise MiniCTypeError(
'In function {}: Line {} col {}: type mismatch for {}: {}'.format(
self._current_function,
ctx.start.line, ctx.start.column, for_what,
' and '.join(t.name.lower() for t in types)))
def _raiseNonType(self, ctx, message):
raise MiniCTypeError(
'In function {}: Line {} col {}: {}'.format(
self._current_function,
ctx.start.line, ctx.start.column, message))
# type declaration
def visitVarDecl(self, ctx) -> None:
ttype = self.visit(ctx.typee())
ids = self.visit(ctx.id_l())
for i in ids:
if i in self._memorytypes:
self._raiseNonType(ctx,"Variable {} already declared".format(i))
self._memorytypes[i] = ttype
def visitBasicType(self, ctx):
assert ctx.mytype is not None
if ctx.mytype.type == MiniCParser.INTTYPE:
return BaseType.Integer
elif ctx.mytype.type == MiniCParser.FLOATTYPE:
return BaseType.Float
if ctx.mytype.type == MiniCParser.STRINGTYPE:
return BaseType.String
elif ctx.mytype.type == MiniCParser.BOOLTYPE:
return BaseType.Boolean
else:
raise MiniCInternalError("Unknown type '%s'" % ctx.mytype)
def visitIdList(self, ctx) -> List[str]:
l = self.visit(ctx.id_l())
l.append(ctx.ID().getText())
return l
def visitIdListBase(self, ctx) -> List[str]:
return [ctx.ID().getText()]
# typing visitors for expressions, statements !
# visitors for atoms --> type
def visitParExpr(self, ctx):
return self.visit(ctx.expr())
def visitIntAtom(self, ctx):
return BaseType.Integer
def visitFloatAtom(self, ctx):
return BaseType.Float
def visitBooleanAtom(self, ctx):
return BaseType.Boolean
def visitIdAtom(self, ctx):
try:
return self._memorytypes[ctx.getText()]
except KeyError:
self._raiseNonType(ctx,
"Undefined variable {}".format(ctx.getText()))
def visitStringAtom(self, ctx):
return BaseType.String
# now visit expr
def visitAtomExpr(self, ctx):
return self.visit(ctx.atom())
def visitOrExpr(self, ctx):
fstT = self.visit(ctx.expr(0))
sndT = self.visit(ctx.expr(1))
if(fstT == sndT):
if(fstT == BaseType.Boolean):
return BaseType.Boolean
else:
self._raise(ctx,"boolean operator", fstT)
else:
self._raise(ctx,"boolean operator", fstT,sndT)
def visitAndExpr(self, ctx):
fstT = self.visit(ctx.expr(0))
sndT = self.visit(ctx.expr(1))
if(fstT == sndT):
if(fstT == BaseType.Boolean):
return BaseType.Boolean
else:
self._raise(ctx,"boolean operator", fstT)
else:
self._raise(ctx,"boolean operator", fstT,sndT)
def visitEqualityExpr(self, ctx):
fstT = self.visit(ctx.expr(0))
sndT = self.visit(ctx.expr(1))
if(fstT == sndT):
return BaseType.Boolean
else:
self._assertSameType(ctx,"equality operator", fstT,sndT)
def visitRelationalExpr(self, ctx):
fstT = self.visit(ctx.expr(0))
sndT = self.visit(ctx.expr(1))
if(fstT == sndT):
if(fstT in [BaseType.Float, BaseType.Integer]):
return BaseType.Boolean
else:
self._raise(ctx, "comparaison operator", fstT)
else:
self._raise(ctx,"comparaison operator", fstT,sndT)
def visitAdditiveExpr(self, ctx):
assert ctx.myop is not None
fstT = self.visit(ctx.expr(0))
sndT = self.visit(ctx.expr(1))
if(fstT == sndT):
if(fstT in [BaseType.Float, BaseType.Integer]):
return fstT
elif (fstT == BaseType.String and ctx.myop.type == MiniCParser.PLUS):
return BaseType.String
else:
self._raise(ctx, "additive operands", fstT,sndT)
else:
self._raise(ctx,"additive operator", fstT,sndT)
def visitMultiplicativeExpr(self, ctx):
fstT = self.visit(ctx.expr(0))
sndT = self.visit(ctx.expr(1))
if(fstT == sndT):
if(not fstT in [BaseType.Float, BaseType.Integer]):
self._raise(ctx, "multiplicative operands", fstT)
else:
return fstT
else:
self._raise(ctx,"multiplicative operands", fstT,sndT)
def visitNotExpr(self, ctx):
fstT = self.visit(ctx.expr())
if(fstT == BaseType.Boolean):
return BaseType.Boolean
else:
self._raise(ctx,"not operator", fstT)
def visitUnaryMinusExpr(self, ctx):
fstT = self.visit(ctx.expr())
if(fstT in [BaseType.Integer,BaseType.Float]):
return fstT
else:
self._raise(ctx,"minus operator", fstT)
# visit statements
def visitPrintlnintStat(self, ctx):
etype = self.visit(ctx.expr())
if etype != BaseType.Integer:
self._raise(ctx, 'println_int statement', etype)
def visitPrintlnfloatStat(self, ctx):
etype = self.visit(ctx.expr())
if etype != BaseType.Float:
self._raise(ctx, 'println_float statement', etype)
def visitPrintlnboolStat(self, ctx):
etype = self.visit(ctx.expr())
if etype != BaseType.Boolean:
self._raise(ctx, 'println_bool statement', etype)
def visitPrintlnstringStat(self, ctx):
etype = self.visit(ctx.expr())
if etype != BaseType.String:
self._raise(ctx, 'println_string statement', etype)
def visitAssignStat(self, ctx):
theid = ctx.ID().getText()
ttyp = self.visit(ctx.expr())
if(not theid in self._memorytypes):
self._raiseNonType(ctx,"Undefined variable {}".format(theid))
if(self._memorytypes[theid] != ttyp):
self._assertSameType(ctx,theid, self._memorytypes[theid],ttyp)
def visitWhileStat(self, ctx):
cond = self.visit(ctx.expr())
if(cond != BaseType.Boolean):
self._raise(ctx, "while condition", cond)
self.visit(ctx.body)
def visitIfStat(self, ctx):
cond = self.visit(ctx.expr())
if(cond != BaseType.Boolean):
self._raise(ctx, "if condition", cond)
self.visit(ctx.then_block)
if(ctx.else_block != None):
self.visit(ctx.else_block)
def visitForStat(self, ctx):
if(ctx.init_stat != None):
self.visit(ctx.init_stat)
if(ctx.cond != None):
cond = self.visit(ctx.cond)
if(cond != BaseType.Boolean):
self._raise(ctx, "for condition", cond)
if(ctx.loop_stat != None):
self.visit(ctx.loop_stat)
self.visit(ctx.stat_block())