def visitPrimitive_type(self, ctx: MPParser.Primitive_typeContext): if ctx.INTEGER(): return IntType() elif ctx.BOOLEAN(): return BoolType() elif ctx.REAL(): return FloatType() elif ctx.STRING(): return StringType()
def init(self): return [ # from specification, section 7: Built-in Functions/Procedures Symbol("getInt", MType([], IntType()), CName(self.libName)), Symbol("putInt", MType([IntType()], VoidType()), CName(self.libName)), Symbol("putIntLn", MType([IntType()], VoidType()), CName(self.libName)), Symbol("getFloat", MType([], FloatType()), CName(self.libName)), Symbol("putFloat", MType([FloatType()], VoidType()), CName(self.libName)), Symbol("putFloatLn", MType([FloatType()], VoidType()), CName(self.libName)), Symbol("putBool", MType([BoolType()], VoidType()), CName(self.libName)), Symbol("putBoolLn", MType([BoolType()], VoidType()), CName(self.libName)), Symbol("putString", MType([StringType()], VoidType()), CName(self.libName)), Symbol("putStringLn", MType([StringType()], VoidType()), CName(self.libName)), Symbol("putLn", MType([], VoidType()), CName(self.libName)) ]
def visitUnaryOp(self, ast, param): ''' ast: UnaryOp param: List[Symbol] raise TypeMismatchInExpression not Bool => Bool - Int => Int - Float => Float => Type ''' op = ast.op.lower() expr = self.visit(ast.body, param) if op in ('not'): if not isinstance(expr, BoolType): raise TypeMismatchInExpression(ast) return BoolType() if op in ('-'): if not isinstance(expr, (IntType, FloatType)): raise TypeMismatchInExpression(ast) return expr
def visitBooleanLiteral(self, asttree, param): return BoolType()
class StaticChecker(BaseVisitor, Utils): global_envi = [ # from specification, section 7: Built-in Functions/Procedures Symbol("getInt", MType([], IntType())), Symbol("putInt", MType([IntType()], VoidType())), Symbol("putIntLn", MType([IntType()], VoidType())), Symbol("getFloat", MType([], FloatType())), Symbol("putFloat", MType([FloatType()], VoidType())), Symbol("putFloatLn", MType([FloatType()], VoidType())), Symbol("putBool", MType([BoolType()], VoidType())), Symbol("putBoolLn", MType([BoolType()], VoidType())), Symbol("putString", MType([StringType()], VoidType())), Symbol("putStringLn", MType([StringType()], VoidType())), Symbol("putLn", MType([], VoidType())) ] def __init__(self, ast): self.ast = ast def check(self): return self.visit(self.ast, StaticChecker.global_envi) def checkRedeclared(self, symbol, kind, env): res = self.lookup(symbol.name.lower(), env, lambda e: e.name.lower()) if res is not None: raise Redeclared(kind, symbol.name) def checkTypeCompatibility(self, lhs, rhs, error): # array check if isinstance(lhs, ArrayType): if not isinstance(rhs, ArrayType): raise error if lhs.lower != rhs.lower or \ lhs.upper != rhs.upper: raise error # self.checkTypeCompatibility(lhs.eleType, rhs.eleType, error) if not isinstance(lhs.eleType, type(rhs.eleType)): raise error # float/int coersion elif isinstance(lhs, FloatType): if not isinstance(rhs, (IntType, FloatType)): raise error # else elif not isinstance(lhs, type(rhs)): raise error def callBody(self, ast, env): ''' ast: CallStmt | CallExpr env: List[Symbol] raise TypeMismatchInStatement | TypeMismatchInExpression => Type: MType ; Used by CallStmt and CallExpr ; Both have exact structure difference only on ; Raising Error and Kind Expectation ''' callParam = [self.visit(x, env) for x in ast.param] mtype = self.visit( # visits Id ast.method, { 'env': env, 'kind': Function() if isinstance(ast, CallExpr) \ else Procedure(), } ) rightParam = mtype.partype # lazy init of Error error = TypeMismatchInExpression if isinstance(ast, CallExpr) \ else TypeMismatchInStatement if len(rightParam) != len(callParam): raise error(ast) # LHS’s are formal parameters and RHS’s are arguments for pair in zip(rightParam, callParam): lhs = pair[0] rhs = pair[1] self.checkTypeCompatibility(lhs, rhs, error(ast)) return mtype.rettype def loopBody(self, stmts, param): ''' stmt: List[Statement] param: { 'env': List[Symbol], 'inloop': Bool, 'rettype': Type } ''' env = param['env'] rettype = param['rettype'] outFlag = False for stmt in stmts: if outFlag: raise UnreachableStatement(stmt) if self.visit(stmt, { 'env': env, 'inloop': True, 'rettype': rettype }): outFlag = True return False def processStatement(self, stmts, param): returnFlag = False for stmt in stmts: if returnFlag: raise UnreachableStatement(stmt) if self.visit(stmt, param): returnFlag = True return returnFlag def visitProgram(self, ast, env): printDebug("======SCAN PROGRAM======") global_scope = reduce( lambda returnList, decl: [self.visit(decl, { 'env': returnList, 'scan': False })] + returnList, ast.decl, env[:]) printDebug("======GLOBAL======", env=global_scope) if not any( map( lambda symbol: isinstance(symbol.mtype, MType) and symbol. name.lower() == 'main' and isinstance( symbol.mtype.rettype, VoidType) and len( symbol.mtype.partype) == 0, global_scope)): raise NoEntryPoint() funcs = filter(lambda x: isinstance(x, FuncDecl), ast.decl) for func in funcs: self.visit(func, {'env': global_scope, 'scan': True}) for symbol in global_scope: if not isinstance(symbol.mtype, MType): continue if symbol.name.lower() == 'main' and \ isinstance(symbol.mtype.rettype, VoidType): continue if symbol.value == 0: if symbol in env: continue raise Unreachable( Procedure() if isinstance(symbol.mtype.rettype, VoidType) else Function(), symbol.name) return global_scope def visitFuncDecl(self, ast, param): ''' ast: FuncDecl param: { env: List[Symbol], # Global Reference Environment scan: Bool } raise Redeclared(Parameter) raise Redeclared(Variable) raise UnreachableStatement raise FunctionNotReturn => Symbol if not scan else None ''' env = param['env'] scan = param['scan'] if not scan: printDebug("FUNCDECL", env=env, stop=False) s = Symbol(ast.name.name, MType([x.varType for x in ast.param], ast.returnType)) kind = Procedure() if isinstance(ast.returnType, VoidType) \ else Function() self.checkRedeclared(s, kind, env) return s else: printDebug("========SCAN FUNC========") printDebug(str(ast)) try: # visits VarDecl -- throws Redeclared(Variable) parameter = reduce( lambda scope, vardecl: [self.visit(vardecl, {'env': scope})] + scope, ast.param, # env[:] # copy []) except Redeclared as e: raise Redeclared(Parameter(), e.n) printDebug("PARAM", env=parameter) # visits VarDecl -- throws Redeclared(Variable) local_scope = reduce( lambda scope, vardecl: [self.visit(vardecl, {'env': scope})] + scope, ast.local, parameter # for safety reason, copy ) printDebug("LOCAL_VAR", env=local_scope) # self.mergeGlobal2Local(local_scope, env) local_scope += env printDebug("LOCAL_ENV", env=local_scope, stop=False) # check in body if not self.processStatement(ast.body, { 'env': local_scope, 'inloop': False, 'rettype': ast.returnType }) and not isinstance(ast.returnType, VoidType): raise FunctionNotReturn(ast.name.name) def visitVarDecl(self, ast, param): ''' ast: VarDecl param: { env: List[Symbol] ~~scan: Bool~~ # ignore } => Symbol ''' # print(param, file=sys.stderr) env = param['env'] printDebug("VARDECL", env=env, stop=False) s = Symbol(ast.variable.name, ast.varType) self.checkRedeclared(s, Variable(), env) return s def visitIntType(self, asttree, param): return None def visitFloatType(self, asttree, param): return None def visitBoolType(self, asttree, param): return None def visitStringType(self, asttree, param): return None def visitVoidType(self, asttree, param): return None def visitArrayType(self, asttree, param): return None def visitBinaryOp(self, ast, param): ''' ast: BinaryOp param: list[Symbol] raise TypeMismatchInExpression (/) --> Float/Int:Float/Int => Float (+,-,*) --> Float:Float/Int => Float --> Float/Int:Float => Float --> Int:Int => Int (div,mod) --> Int:Int => Int (<,<=,=,>=,>,<>) --> Float:Float/Int => Bool --> Float/Int:Float => Bool --> Int:Int => Bool (and,or,andthen,orelse) --> Bool:Bool => Bool => Type ''' op = ast.op.lower() # visits (Id, BinaryOp, UnaryOp, CallExpr, ArrayCell) left_type = self.visit(ast.left, param) right_type = self.visit(ast.right, param) def deferType(acceptableTypes, returnType=None): if not isinstance(left_type, acceptableTypes): raise TypeMismatchInExpression(ast) if not isinstance(right_type, acceptableTypes): raise TypeMismatchInExpression(ast) if returnType is not None: return returnType if isinstance(left_type, FloatType) or \ isinstance(right_type, FloatType): return FloatType() if isinstance(left_type, type(right_type)): return left_type raise TypeMismatchInExpression(ast) if op in ('and', 'or', 'andthen', 'orelse'): return deferType((BoolType), BoolType()) if op in ('div', 'mod'): return deferType((IntType), IntType()) if op in ('+', '-', '*'): return deferType((IntType, FloatType)) if op in ('/'): return deferType((IntType, FloatType), FloatType()) if op in ('<', '<=', '=', '>=', '>', '<>'): return deferType((IntType, FloatType), BoolType()) def visitUnaryOp(self, ast, param): ''' ast: UnaryOp param: List[Symbol] raise TypeMismatchInExpression not Bool => Bool - Int => Int - Float => Float => Type ''' op = ast.op.lower() expr = self.visit(ast.body, param) if op in ('not'): if not isinstance(expr, BoolType): raise TypeMismatchInExpression(ast) return BoolType() if op in ('-'): if not isinstance(expr, (IntType, FloatType)): raise TypeMismatchInExpression(ast) return expr def visitCallExpr(self, ast, env): ''' ast: CallExpr ~ CallStmt env: list[Symbol] raise Undeclared(Function) raise TypeMismatchInExpression wrong param size wrong param type Array[n..m] of X --> Array[n..m] of X Float --> Float/Int X --> X => Type ''' return self.callBody(ast, env) def visitId(self, ast, param): ''' ast: Id param: List[Symbol] or { 'env': List[Symbol] 'kind': kind expectation } raise Undeclared => Type: Symbol.mtype ''' env = param['env'] if not isinstance(param, list) else param kind = param['kind'] if not isinstance(param, list) else Identifier() # type(res) == Symbol # printDebug("ID", env=env, stop=False) res = self.lookup(ast.name.lower(), env, lambda e: e.name.lower()) if res is None: raise Undeclared(kind, ast.name) if isinstance(kind, Identifier): if isinstance(res.mtype, MType): raise Undeclared(kind, ast.name) return res.mtype # param is dict if isinstance(kind, Function) or isinstance(kind, Procedure): # check if mtype -- aka function if not isinstance(res.mtype, MType): raise Undeclared(kind, ast.name) if isinstance(kind, Function): if isinstance(res.mtype.rettype, VoidType): raise Undeclared(kind, ast.name) res.value += 1 elif isinstance(kind, Procedure): if not isinstance(res.mtype.rettype, VoidType): raise Undeclared(kind, ast.name) res.value += 1 return res.mtype def visitArrayCell(self, ast, param): ''' ast: ArrayCell param: List[Symbol] raise TypeMismatchInExpression arr[idx] --> ArrayType[IntType] => ArrayType.eleType => Type ''' arr = self.visit(ast.arr, param) idx = self.visit(ast.idx, param) if not isinstance(idx, IntType) or \ not isinstance(arr, ArrayType): raise TypeMismatchInExpression(ast) return arr.eleType ''' Statements are passed with a dict() with simple params: env: List[Symbol] # local referencing environment inloop: Bool # in loop flag rettype: Type # return type of function/procedure Simple statements deal only with 'env', Continue/Break statements use 'inloop' to check for non in loop call Return statements use 'rettype' to check for type compatibility when return Function/Procedure statements pass all these params, For/While statements pass inloop as True when processing loop statements while preserving 'rettype If statements pass param to then/else statements With statements ... All other statements uses param as read only Return of statements are the status of the function/procedure whether it has returned or not For example: procedure main(); begin if (n and mask) then return 1; else return 0; end the corresponding AST Tree will be: Program([ FuncDecl(Id(main),[],VoidType(),[],[ If(BinaryOp(and,Id(n),Id(mask)),[ Return(Some(IntLiteral(1))) ],[ Return(Some(IntLiteral(0))) ]) ]) ]) The If in AST will return True as both of the branch returns. There is no else if statement, but this algorithm works as expect that in if statement, all branch returns means the function is ended The same logic can also be applied to for/while/with statements ''' def visitAssign(self, ast, param): ''' ast: Assign param: { 'env': List[Symbol] 'inloop': Bool } raise TypeMismatchInStatement Float := Float/Int Int := Int Bool := Bool // no String, Array => Returned? ''' env = param['env'] left_type = self.visit(ast.lhs, env) right_type = self.visit(ast.exp, env) if isinstance(left_type, (StringType, ArrayType)): raise TypeMismatchInStatement(ast) self.checkTypeCompatibility(left_type, right_type, TypeMismatchInStatement(ast)) return False def visitWith(self, ast, param): ''' ast: With param: { 'env': List[Symbol], 'inloop': Bool, 'rettype': Type } ''' env = param['env'] with_scope = reduce( lambda with_scope, decl: [self.visit(decl, {'env': with_scope})] + with_scope, ast.decl, []) with_scope += env return self.processStatement( ast.stmt, { 'env': with_scope, 'inloop': param['inloop'], 'rettype': param['rettype'] }) def visitIf(self, ast, param): ''' ast: If param: { 'env': List[Symbol] 'inloop': Bool 'rettype': Type } => Returned? ''' expr = self.visit(ast.expr, param['env']) if not isinstance(expr, BoolType): raise TypeMismatchInStatement(ast) thenReturnFlag = False for stmt in ast.thenStmt: if thenReturnFlag: raise UnreachableStatement(ast) if self.visit(stmt, param): thenReturnFlag = True elseReturnFlag = False for stmt in ast.elseStmt: if elseReturnFlag: raise UnreachableStatement(ast) if self.visit(stmt, param): elseReturnFlag = True return thenReturnFlag and elseReturnFlag def visitFor(self, ast, param): ''' ast: For param: { 'env': List[Symbol], 'rettype': Type } ''' env = param['env'] idType = self.visit(ast.id, env) expr1 = self.visit(ast.expr1, env) expr2 = self.visit(ast.expr2, env) if not isinstance(idType, IntType) or \ not isinstance(expr1, IntType) or \ not isinstance(expr2, IntType): raise TypeMismatchInStatement(ast) return self.loopBody(ast.loop, param) def visitContinue(self, ast, param): ''' ast: Continue param: { 'inloop': Bool } ''' if not param['inloop']: raise ContinueNotInLoop() return True def visitBreak(self, ast, param): ''' ast: Break param: { 'inloop': Bool } ''' if not param['inloop']: raise BreakNotInLoop() return True def visitReturn(self, ast, param): ''' ast: Return param: { 'env': List[Symbol] 'rettype': Type } ''' rettype = param['rettype'] env = param['env'] if isinstance(rettype, VoidType): if ast.expr is not None: raise TypeMismatchInStatement(ast) else: if ast.expr is None: raise TypeMismatchInStatement(ast) self.checkTypeCompatibility(rettype, self.visit(ast.expr, env), TypeMismatchInStatement(ast)) return True def visitWhile(self, ast, param): ''' ast: While param: { 'env': List[Symbol], 'inloop': Bool, 'rettype': Type } ''' env = param['env'] exp = self.visit(ast.exp, env) if not isinstance(exp, BoolType): raise TypeMismatchInStatement(ast) return self.loopBody(ast.sl, param) def visitCallStmt(self, ast, param): ''' ast: CallStmt param: { 'env': list[Symbol] } raise Undeclared(Procedure) raise TypeMismatchInStatement wrong param size wrong param type Array[n..m] of X --> Array[n..m] of X Float --> Float/Int X --> X => Returned? ''' self.callBody(ast, param['env']) # skips return return False def visitIntLiteral(self, asttree, param): return IntType() def visitFloatLiteral(self, asttree, param): return FloatType() def visitBooleanLiteral(self, asttree, param): return BoolType() def visitStringLiteral(self, asttree, param): return StringType()
def visitBooleanLiteral(self, ast, param): frame = param.frame return self.emit.emitPUSHICONST(str(ast.value).lower(), frame), BoolType()
def visitBinaryOp(self, ast, param): ''' ast: BinaryOp param: Access => code, type Rules: (/) --> Float/Int:Float/Int => Float (+,-,*) --> Float:Float/Int => Float --> Float/Int:Float => Float --> Int:Int => Int (div,mod) --> Int:Int => Int (<,<=,=,>=,>,<>) --> Float:Float/Int => Bool --> Float/Int:Float => Bool --> Int:Int => Bool (and,or,andthen,orelse) --> Bool:Bool => Bool ''' frame = param.frame sym = param.sym lhs, ltype = self.visit(ast.left, Access(frame, sym)) rhs, rtype = self.visit(ast.right, Access(frame, sym)) if isinstance(ltype, MType): ltype = ltype.rettype if isinstance(rtype, MType): rtype = rtype.rettype if (isinstance(ltype, type(rtype))): # because idiv returns int not float # while MP / returns float on all cases if ast.op == '/' and\ isinstance(ltype, IntType): lhs += self.emit.emitI2F(frame) rhs += self.emit.emitI2F(frame) intype = FloatType() rettype = FloatType() else: intype = ltype rettype = ltype else: # only Float:Int or Int:Float # I2F on Int if isinstance(ltype, IntType): lhs += self.emit.emitI2F(frame) if isinstance(rtype, IntType): rhs += self.emit.emitI2F(frame) intype = FloatType() rettype = FloatType() code = '' code += lhs code += rhs # self.emit.printout(lhs) # push left # self.emit.printout(rhs) # push right # generate code for each operator if ast.op in ('+', '-'): code += self.emit.emitADDOP(ast.op, intype, frame) elif ast.op in ('*', '/'): code += self.emit.emitMULOP(ast.op, intype, frame) elif ast.op in ('<', '<=', '=', '>=', '>', '<>'): code += self.emit.emitREOP(ast.op, intype, frame) rettype = BoolType() elif ast.op.lower() == 'div': code += self.emit.emitDIV(frame) elif ast.op.lower() == 'mod': code += self.emit.emitMOD(frame) elif ast.op.lower() == 'and': code += self.emit.emitANDOP(frame) elif ast.op.lower() == 'or': code += self.emit.emitOROP(frame) elif ast.op.lower() == 'andthen': code = '' falseLabel = frame.getNewLabel() endLabel = frame.getNewLabel() code += lhs code += self.emit.emitIFEQ(falseLabel, frame) code += rhs code += self.emit.emitIFEQ(falseLabel, frame) code += self.emit.emitPUSHICONST(1, frame) code += self.emit.emitGOTO(endLabel, frame) code += self.emit.emitLABEL(falseLabel, frame) code += self.emit.emitPUSHICONST(0, frame) code += self.emit.emitLABEL(endLabel, frame) rettype = BoolType() elif ast.op.lower() == 'orelse': code = '' trueLabel = frame.getNewLabel() endLabel = frame.getNewLabel() code += lhs # if lhs != 0 True code += self.emit.emitIFNE(trueLabel, frame) code += rhs # if rhs != 0 True code += self.emit.emitIFNE(trueLabel, frame) code += self.emit.emitPUSHICONST(0, frame) code += self.emit.emitGOTO(endLabel, frame) code += self.emit.emitLABEL(trueLabel, frame) code += self.emit.emitPUSHICONST(1, frame) code += self.emit.emitLABEL(endLabel, frame) rettype = BoolType() return code, rettype