Example #1
0
class ASTWalker(NodeVisitor):

    def visit_Assignment(self, n):
        if self.st.get_function(n.var):
            raise SemanticError("function '%s' is not assignable" % n.var)

        left = self.st.get_identifier(n.var)
        if left is None:
            raise SemanticError("use of undeclared identifier '%s'" % n.var)

        right = self.visit(n.expr)
        if left.type != right.type:
            raise SemanticError("assigning to '%s' from incompatible type '%s'" % (left.type, right.type))

        srcId = None
        if isinstance(n.expr, (Assignment, Identifier, Constant)):
            self.st.update_variable(n.var, left.type, right.value)
            srcId = right.name

        outasm = []
        add(outasm, right.asm)
        add(outasm, asm.AssignToVariable(n.var, srcId, self.st))
        return Info(name=n.var, type=left.type, value=right.value, asm=outasm)

    def visit_BinaryOp(self, n):
        left = self.visit(n.lexpr)
        right = self.visit(n.rexpr)

        id1 = left.name if isinstance(n.lexpr, (Assignment, Identifier, Constant)) else None
        id2 = right.name if isinstance(n.rexpr, (Assignment, Identifier, Constant)) else None

        operators = {
            "+": asm.Plus,
            "-": asm.Minus,
            "*": asm.Times,
            "/": asm.Divide,
            "%": asm.Modulo,
            "!": asm.Not,
            "&&": asm.And,
            "||": asm.Or,
            "<": asm.Lesser,
            ">": asm.Greater,
            "<=": asm.LesserOrEqual,
            ">=": asm.GreaterOrEqual,
            "==": asm.Equal,
            "!=": asm.NotEqual,
        }

        if left.type in [T.Integer, T.Character, T.String] and right.type in [T.Integer, T.Character, T.String] and left.type != right.type:
            raise SemanticError("invalid types of operands to binary expression '%s' ('%s' and '%s')" % (n.op, left.type, right.type))

        outasm = []
        add(outasm, left.asm)
        add(outasm, right.asm)
        add(outasm, operators[n.op](id1, id2, self.st))
        return Info(type=T.Integer, asm=outasm)

    def visit_Break(self, n):
        if not self.st.in_loop():
            raise SemanticError("'break' statement not in loop statement")
        return Info(asm=asm.Break())

    def visit_Cast(self, n, internal=False):
        cast = self.visit(n.type)
        expr = self.visit(n.expr)
        id = expr.name if isinstance(n.expr, (Assignment, Identifier, Constant)) else None

        if expr.type in [T.Character] and cast.type in [T.Integer, T.Character, T.String]:
            pass
        elif expr.type in [T.Integer] and cast.type in [T.Integer, T.Character]:
            pass
        elif expr.type in [T.String] and cast.type in [T.String]:
            pass
        elif internal and expr.type in [T.String] and cast.type in [T.Integer]:
            pass
        else:
            raise SemanticError("cast from '%s' to '%s' is not allowed" % (expr.type, cast.type))

        outasm = []
        add(outasm, expr.asm)
        add(outasm, asm.Cast(id, cast.type, self.st))
        return Info(type=cast.type, value=expr.value, asm=outasm)

    def visit_Compound(self, n):
        output = []
        type = None
        for ch in n:
            ch_visited = self.visit(ch)
            add(output, ch_visited.asm)
            if isinstance(ch, Return):
                type = ch_visited.type
        return Info(type=type, asm=asm.Compound(output))

    def visit_Constant(self, n):
        def is_int32(value):
            return int(value) >= INT.minint and int(value) <= INT.maxint

        type = self.visit(n.type)
        if type.type in [T.Integer] and not is_int32(n.value):
            raise SemanticError("integer constant '%s' is too large for its type" % n.value)

        name = self.st.insert_to_global(n.value, type.type)
        return Info(name=name, type=type.type, value=n.value)

    def visit_Continue(self, n):
        if not self.st.in_loop():
            raise SemanticError("'continue' statement not in loop statement")
        return Info(asm=asm.Continue())

    def visit_Empty(self, n):
        return Info(asm=asm.Empty())

    def visit_For(self, n):
        scope = self.st.insert_nonfunction_scope()
        self.st.enter_scope(scope)
        self.st.enter_loop()
        start = self.visit(UnaryOp('+', n.start)) if isinstance(n.start, (Constant, Identifier)) else self.visit(n.start)
        test = self.visit_Cast(Cast(Type('int'), n.test), True) if isinstance(n.test, (Assignment, Constant, Identifier)) else self.visit(n.test)
        if test.type not in [T.Integer, T.Character, T.String]:
            raise SemanticError("statement requires expression of scalar type ('%s' invalid)" % test.type)
        next = self.visit(UnaryOp('+', n.next)) if isinstance(n.next, (Constant, Identifier)) else self.visit(n.next)
        body = self.visit(n.body)
        self.st.leave_loop()
        self.st.leave_scope()

        start_asm = []
        add(start_asm, start.asm)
        test_asm = []
        add(test_asm, test.asm)
        next_asm = []
        add(next_asm, next.asm)
        return Info(asm=asm.For(start_asm, test_asm, next_asm, body.asm, self.st))

    def visit_FunctionCall(self, n):
        def fce_param_types(fce_scope):
            types = []
            for param in sorted(fce_scope):
                if param.startswith("#arg#"):
                    types.append(fce_scope[param].type)
            return types

        def arg_param_types(args):
            types = []
            for param in args:
                types.append(param.type)
            return types

        if self.st.get_identifier(n.name):
            raise SemanticError("called variable '%s' is not a function" % n.name)
        fce = self.st.get_function(n.name)
        if fce is None:
            raise SemanticError("implicit declaration of function '%s' is invalid in VYPe13" % n.name)

        fce_types = fce_param_types(fce.scope)
        arg_list = [(arg, self.visit(arg)) for arg in n.args]
        arg_types = arg_param_types([arg[1] for arg in arg_list])
        if n.name == "print":
            if len(n.args) == 0:
                raise SemanticError("calling function '%s' with incompatible parameter types" % (n.name))
        else:
            if fce_types != arg_types:
                if fce_types == ['void'] and arg_types == [None]:
                    pass
                else:
                    raise SemanticError("calling function '%s' with incompatible parameter types" % (n.name))

        call_list = []
        call_name = []
        for arg in arg_list:
            add(call_list, arg[1].asm)
            if isinstance(arg[0], (Assignment, Identifier, Constant)):
                call_name.append(arg[1].name)
            else:
                call_name.append(None)

        add(call_list, asm.Call(n.name, call_name, self.st))
        return Info(name=n.name, type=fce.type, asm=call_list)

    def visit_FunctionDefinition(self, n):
        def fce_param_types(fce_scope):
            types = []
            for param in sorted(fce_scope):
                if param.startswith("#arg#"):
                    types.append(fce_scope[param].type)
            return types

        def def_param_types(param_list):
            types = []
            for param in param_list:
                types.append(self.visit(param.type).type)
            return types

        def def_param_names(param_list):
            names = []
            for param in param_list:
                if param.name is not None:
                    names.append(param.name)
            return names

        if self.st.get_identifier(n.name):
            raise SemanticError("redefinition of variable '%s' as a function" % n.name)
        fce = self.st.get_function(n.name)
        type = self.visit(n.type)
        if n.name == "main" and type.type != T.Integer:
            raise SemanticError("return type of '%s' is not '%s' but '%s'" % (n.name, T.Integer, type.type))
        param_names = def_param_names(n.args)

        if fce is None:
            self.st.insert_function(n.name, type.type)
            self.st.enter_scope(n.name)
            for arg, i in zip(n.args, range(len(n.args))):
                self.visit_FunctionParam(arg, i)
            param_names = asm.GetParams(param_names, self.st)
            self.st.set_function_def(n.name, False)
            if n.code is not None:
                code = self.visit(n.code)
                self.st.set_function_def(n.name, True)
            self.st.leave_scope()
        else:
            if fce.scope is None:
                raise SemanticError("redefinition of '%s' as different kind of symbol" % (n.name))

            self.st.enter_scope(n.name)

            fce_types = fce_param_types(fce.scope)
            def_types = def_param_types(n.args)
            if fce_types != def_types or fce.type != type.type:
                raise SemanticError("conflicting types for function '%s'" % (n.name))

            if n.code is None:
                raise SemanticError("redeclaration of function '%s'" % (n.name))
            elif fce.defined is False and n.code is not None:
                for arg, i in zip(n.args, range(len(n.args))):
                    self.visit_FunctionParam(arg, i)
                param_names = asm.GetParams(param_names, self.st)
                code = self.visit(n.code)
                self.st.set_function_def(n.name, True)
            elif fce.defined is True and n.code is not None:
                raise SemanticError("redefinition of function '%s'" % (n.name))

            self.st.leave_scope()

        if n.code is not None and code.type != type.type:
            if code.type is not None:
                raise SemanticError("returning '%s' from function '%s' with result type '%s'" % (code.type, n.name, type.type))

        if n.code is not None:
            return Info(name=n.name, type=type.type, asm=asm.FunctionDefinition(n.name, param_names, code.asm, self.st))
        else:
            return Info(name=n.name, type=type.type, asm=asm.Empty())

    def visit_FunctionParam(self, n, pos = None):
        type = self.visit(n.type)
        self.st.insert_arg_type(type.type, pos)
        if n.name is not None:
            self.st.insert_variable(n.name, type.type, position = pos)
        return Info(name=n.name, type=type.type)

    def visit_Identifier(self, n):
        id = self.st.get_identifier(n.name)
        if id is None:
            raise SemanticError("use of undeclared identifier '%s'" % n.name)
        return Info(name=n.name, type=id.type, value=id.value)

    def visit_If(self, n):
        test = self.visit_Cast(Cast(Type('int'), n.test), True) if isinstance(n.test, (Assignment, Constant, Identifier)) else self.visit(n.test)
        if test.type not in [T.Integer, T.Character, T.String]:
            raise SemanticError("statement requires expression of scalar type ('%s' invalid)" % test.type)

        scope = self.st.insert_nonfunction_scope()
        self.st.enter_scope(scope)
        true = self.visit(n.true)
        self.st.leave_scope()

        scope = self.st.insert_nonfunction_scope()
        self.st.enter_scope(scope)
        false = self.visit(n.false)
        self.st.leave_scope()

        test_asm = []
        add(test_asm, test.asm)
        if isinstance(n.false, Empty):
            return Info(asm=asm.IfOnly(test_asm, true.asm, self.st))
        else:
            return Info(asm=asm.IfElse(test_asm, true.asm, false.asm, self.st))

    def visit_Program(self, n):
        self.st = SymbolTable()
        outasm = [self.visit(ch).asm for ch in n]
        if self.st.get_function('main') is None:
            raise SemanticError("undefined reference to 'main'")
        return Info(asm=asm.Program(outasm, self.st.get_globals(finalize=True)))

    def visit_Return(self, n):
        if n.expr is not None:
            expr = self.visit(n.expr)
            id = expr.name if isinstance(n.expr, (Assignment, Identifier, Constant)) else None
            outasm = []
            add(outasm, expr.asm)
            add(outasm, asm.Return(id, self.st))
            return Info(type=expr.type, value=expr.value, asm=outasm)
        else:
            return Info(type=T.Void, asm=asm.ReturnEmpty())

    def visit_Type(self, n):
        if n == Type("int"):
            type = T.Integer
        elif n == Type("char"):
            type = T.Character
        elif n == Type("string"):
            type = T.String
        elif n == Type("void"):
            type = T.Void
        else:
            type = T.Nothing

        return Info(type=type)

    def visit_UnaryOp(self, n):
        expr = self.visit(n.expr)
        id = expr.name if isinstance(n.expr, (Assignment, Identifier, Constant)) else None
        outasm = []
        add(outasm, expr.asm)
        if n.op == '-':
             add(outasm, asm.UnaryMinus(id, self.st))
        elif n.op == '!':
             add(outasm, asm.Not(id, self.st))
        elif n.op == '+':
             add(outasm, asm.UnaryPlus(id, self.st))
        return Info(type=expr.type, asm=outasm)

    def visit_VariableDeclaration(self, n):
        type = self.visit(n.type)

        vars = []
        for i in n.vardecls:
            vars.append(self.visit_VariableInitialize(i, type.type))

        vars_asm = []
        for var in vars:
            add(vars_asm, var.asm)

        return Info(type=type.type, asm=vars_asm)

    def visit_VariableInitialize(self, n, type):
        if self.st.get_function(n.name) or self.st.insert_variable(n.name, type) is False:
            raise SemanticError("redefinition of function '%s' as a variable" % (n.name))

        value = None
        opasm = []
        add(opasm, asm.DeclareVariable(n.name, self.st))
        expr = self.visit(n.value)
        add(opasm, expr.asm)
        if n.value is not None:
            if expr.type != type:
                raise SemanticError("initializing '%s' with an expression of incompatible type '%s'" % (type, expr.type))

            if isinstance(n.value, (Identifier, Constant)):
                self.st.update_variable(n.name, type, expr.value)
                value = expr.value

            if isinstance(n.value, Constant):
                add(opasm, asm.InitialiseVariable(n.name, expr.name, self.st))
            elif isinstance(n.value, Identifier):
                add(opasm, asm.InitialiseVariable(n.name, expr.name, self.st))
                add(opasm, asm.AssignToVariable(n.name, expr.name, self.st))
            else:
                add(opasm, asm.AssignToVariable(n.name, None, self.st))
        else:
            add(opasm, asm.InitialiseVariable(n.name, None, self.st))

        return Info(name=n.name, type=type, value=value, asm=opasm)

    def visit_While(self, n):
        scope = self.st.insert_nonfunction_scope()
        self.st.enter_scope(scope)
        self.st.enter_loop()
        test = self.visit_Cast(Cast(Type('int'), n.test), True) if isinstance(n.test, (Assignment, Constant, Identifier)) else self.visit(n.test)
        body = self.visit(n.body)
        self.st.leave_loop()
        self.st.leave_scope()

        if test.type not in [T.Integer, T.Character, T.String]:
            raise SemanticError("statement requires expression of scalar type ('%s' invalid)" % test.type)
        test_asm = []
        add(test_asm, test.asm)
        return Info(asm=asm.While(test_asm, body.asm, self.st))
Example #2
0
 def visit_Program(self, n):
     self.st = SymbolTable()
     outasm = [self.visit(ch).asm for ch in n]
     if self.st.get_function('main') is None:
         raise SemanticError("undefined reference to 'main'")
     return Info(asm=asm.Program(outasm, self.st.get_globals(finalize=True)))