# 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())