Example #1
0
 def enter_scope(self, new_scope_name):
     self.symbol_table = SymbolTable(self.symbol_table, new_scope_name)
Example #2
0
 def __init__(self):
     self.symbol_table = SymbolTable(None, "global-scope")
     self.current_type = None
Example #3
0
class TypeChecker(NodeVisitor):
    def log_error(self, message, lineno):
        print("Error: {}: line {}".format(message, lineno))

    def log_precision_loss_if_needed(self, type1, type2, lineno):
        if type1 == 'int' and type2 == 'float':
            print("Warning: possible precision loss: assigning float to int: line {}".format(lineno))

    def enter_scope(self, new_scope_name):
        self.symbol_table = SymbolTable(self.symbol_table, new_scope_name)

    def leave_scope(self):
        self.symbol_table = self.symbol_table.getParentScope()

    def __init__(self):
        self.symbol_table = SymbolTable(None, "global-scope")
        self.current_type = None

    def visit_Declaration(self, node):
        self.current_type = node.type_name
        self.visit(node.initialisations)
        self.current_type = None

    def visit_Initialisation(self, node):
        initialisation_type = self.visit(node.expression)
        definition = self.symbol_table.searchInScopes(node.name)
        if definition is not None and isinstance(definition, FunctionSymbol):
            message = "Function identifier '{}' used as a variable".format(node.name)
            self.log_error(message, node.lineno)
        elif self.symbol_table.get(node.name):
            message = "Variable '{}' already declared".format(node.name)
            self.log_error(message, node.lineno)
        elif ttype['='][self.current_type][initialisation_type] is None:
            message = "Assignment of {} to {}".format(initialisation_type, self.current_type)
            self.log_error(message, node.lineno)
        else:
            self.log_precision_loss_if_needed(self.current_type, initialisation_type, node.lineno)
            self.symbol_table.put(node.name, VariableSymbol(node.name, self.current_type))

    def visit_JustID(self, node):
        id_type = self.symbol_table.searchInScopes(node.identifier)
        if id_type is None:
            message = "Usage of undeclared variable '{}'".format(node.identifier)
            self.log_error(message, node.lineno)
            return None
        return id_type.type

    def visit_LabeledInstruction(self, node):
        if self.symbol_table.has_label(node.label):
            message = "Label '{}' already in use in this scope ".format(node.label)
            self.log_error(message, node.lineno)
        else:
            self.symbol_table.putLabel(node.label)
        self.visit(node.instruction)

    def visit_Assignment(self, node):
        expression_type = self.visit(node.expression)
        variable_definition = self.symbol_table.searchInScopes(node.name)
        if expression_type is not None:
            if variable_definition is None:
                message = "Variable '{}' undefined in current scope".format(node.name)
                self.log_error(message, node.lineno)
            elif ttype['='][variable_definition.type][expression_type] is None:
                message = "Assignment of {} to {}".format(expression_type, variable_definition.type)
                self.log_error(message, node.lineno)
            else:
                self.log_precision_loss_if_needed(variable_definition.type, expression_type, node.lineno)

    def visit_ChoiceInstruction(self, node):
        self.visit(node.condition)
        self.visit(node.if_instruction)
        if node.else_instruction is not None:
            self.visit(node.else_instruction)

    def visit_RepeatInstruction(self, node):
        self.enter_scope("loop-scope")
        self.visit(node.instructions)
        self.leave_scope()
        self.visit(node.condition)

    def visit_WhileInstruction(self, node):
        self.visit(node.condition)
        self.enter_scope("loop-scope")
        self.visit(node.instruction)
        self.leave_scope()

    def visit_ReturnInstruction(self, node):
        function_scope = self.symbol_table.get_current_function_scope()
        if function_scope is None:
            self.log_error("return instruction outside a function", node.lineno)
        else:
            function_type = function_scope.function_symbol.type
            return_type = self.visit(node.expression)
            if return_type is not None:
                if ttype['='][function_type][return_type] is None:
                    message = "Improper returned type, expected {}, got {}".format(function_type, return_type)
                    self.log_error(message, node.lineno)
                else:
                    self.log_precision_loss_if_needed(function_type, return_type, node.lineno)

    def visit_loop_instruction(self, instruction, lineno):
        if self.symbol_table.get_current_loop_scope() is None:
            message = "{} instruction outside a loop".format(instruction)
            self.log_error(message, lineno)

    def visit_ContinueInstruction(self, node):
        self.visit_loop_instruction("continue", node.lineno)

    def visit_BreakInstruction(self, node):
        self.visit_loop_instruction("break", node.lineno)

    def visit_BinExpr(self, node):
        left_type = self.visit(node.left)
        right_type = self.visit(node.right)
        return_type = ttype[node.op][left_type][right_type]
        if return_type is None:
            message = "Illegal operation, {} {} {}".format(left_type, node.op, right_type)
            self.log_error(message, node.lineno)
        return return_type


    def visit_Integer(self, node):
        return 'int'

    def visit_Float(self, node):
        return 'float'

    def visit_String(self, node):
        return "string"

    def visit_FunctionDefinition(self, node):
        symbol = self.symbol_table.searchInScopes(node.function_name)
        if isinstance(symbol, FunctionSymbol):
            message = "Redefinition of function '{}'".format(node.function_name)
            self.log_error(message, node.lineno)
        elif isinstance(symbol, VariableSymbol):
            message = "Variable identifier '{}' used as function name".format(node.function_name)
            self.log_error(message, node.lineno)
        else:
            return_instructions = node.compound_instr.flatten()
            if not return_instructions:
                message = "Missing return statement in function '{}' returning {}".format(node.function_name, node.return_type)
                self.log_error(message, node.compound_instr.end_lineno)
            function_symbol = FunctionSymbol(node.function_name, node.return_type)
            self.symbol_table.put(node.function_name, function_symbol)
            self.enter_scope("function-scope")
            self.symbol_table.function_symbol = function_symbol

            self.visit(node.arguments)
            self.visit(node.compound_instr)

            self.leave_scope()

    def visit_FunctionArgument(self, node):
        if self.symbol_table.get(node.argument) is not None:
            self.log_error("Redefinition of an argument {0}.".format(node.argument), node.lineno)
        else:
            self.symbol_table.put(node.argument, VariableSymbol(node.argument, node.argument_type))
            self.symbol_table.function_symbol.add_argument(node.argument_type)

    def visit_FunctionCall(self, node):
        definition = self.symbol_table.searchInScopes(node.function_name)
        if definition is None or not isinstance(definition, FunctionSymbol):
            message = "Call of undefined function: '{}'".format(node.function_name)
            self.log_error(message, node.lineno)
        else:
            if len(node.arguments) != len(definition.arguments):
                message = "Improper number of args in {} call".format(node.function_name)
                self.log_error(message, node.lineno)
            else:
                for i in range(len(node.arguments)):
                    arg_type = definition.arguments[i]
                    given_type = self.visit(node.arguments[i])
                    if ttype['='][arg_type][given_type] is None:
                        message = "Improper type of args in {} call".format(node.function_name)
                        self.log_error(message, node.lineno)
                        break
                    self.log_precision_loss_if_needed(arg_type, given_type, node.lineno)
        return None if definition is None else definition.type

    def visit_CompoundInstruction(self, node):
        self.enter_scope("compound-instr_scope")
        self.visit(node.instructions)
        self.leave_scope()