Beispiel #1
0
 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()
Beispiel #2
0
 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))
     ]
Beispiel #3
0
    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
Beispiel #4
0
 def visitBooleanLiteral(self, asttree, param):
     return BoolType()
Beispiel #5
0
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()
Beispiel #6
0
 def visitBooleanLiteral(self, ast, param):
     frame = param.frame
     return self.emit.emitPUSHICONST(str(ast.value).lower(),
                                     frame), BoolType()
Beispiel #7
0
    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