예제 #1
0
def check_overridden_signatures(table):
    for c in table:
        if table[c].parent is None:
            continue
        for field in table[c].fields:
            cur = table[c].parent
            while cur is not None:
                if field in table[cur].fields:
                    raise CompilationError(
                        ErrorType.variableOverloading,
                        f'Field with name "{field}" already exists in the superclass',
                        table[c].lineno)
                cur = table[cur].parent
        for method in table[c].methods:
            method_params = list(table[c].methods[method].params.items())
            method_ret = table[c].methods[method].return_type
            method_is_public = table[c].methods[method].is_public
            cur = table[c].parent
            while cur is not None:
                if method in table[cur].methods:
                    if (method_params != list(
                            table[cur].methods[method].params.items())
                            or method_ret !=
                            table[cur].methods[method].return_type
                            or method_is_public !=
                            table[cur].methods[method].is_public):
                        raise CompilationError(
                            ErrorType.methodInBaseWithDifferentSignature,
                            f'Method with name "{method}" already exists in the superclass ',
                            'with a different signature',
                            table[c].methods[method].lineno)
                    table[cur].methods[method].is_virtual = True
                cur = table[cur].parent
예제 #2
0
 def visit_method_parameter(self, node, table, *args):
     if node.cur_id == 'this':
         raise CompilationError(ErrorType.invalidName,
                                '"this" is not a valid parameter name',
                                node.lineno)
     if node.cur_id in table:
         raise CompilationError(ErrorType.dublicatedParam,
                                'Duplicate parameter name: ' + node.cur_id,
                                node.lineno)
     table[node.cur_id] = node.cur_type
예제 #3
0
 def visit_var_declaration(self, node, table, check_table=None, *args):
     if node.varid == 'this':
         raise CompilationError(ErrorType.invalidVariableName,
                                '"this" is not a valid variable name',
                                node.lineno)
     if node.varid in table or (check_table and node.varid in check_table):
         raise CompilationError(ErrorType.dublicatedVariable,
                                'Duplicate variable name: ' + node.varid,
                                node.lineno)
     table[node.varid] = node.vartype
예제 #4
0
 def assert_is_subclass(self, node_type, expected_type, lineno):
     if expected_type in self.ATOMS or node_type in self.ATOMS:
         if node_type != expected_type:
             raise CompilationError(ErrorType.wrongType, f'Expected type "{expected_type}", got "{node_type}"', lineno)
     t = node_type
     while True:
         if t == expected_type:
             return
         t = self.table[t].parent
         if t is None:
             raise CompilationError(ErrorType.wrongType, f'Expected type "{expected_type}", got "{node_type}"', lineno)
예제 #5
0
 def resolve_method(self, class_name, method_name, lineno):
     if class_name not in self.table:
         raise CompilationError(ErrorType.noMethodsExist, f'Type "{class_name}" has no methods', lineno)
     class_table = self.table[class_name]
     current = class_name
     while True:
         if method_name in class_table.methods:
             return (class_table.methods[method_name], current)
         current = class_table.parent
         class_table = self.table.get(current)
         if class_table is None:
             raise CompilationError(ErrorType.undefinedMethod, f'Undefined method: {class_name}.{method_name}', lineno)
예제 #6
0
 def visit_call_expression(self, node, *args):
     obj = self.visit(node.obj)
     method, method_owner = self.resolve_method(obj, node.method, node.lineno)
     if not method.is_public and method_owner != self.class_name:
         raise CompilationError(ErrorType.privateMethod, f'Method {method_owner}.{node.method} is private', node.lineno)
     node.method_owner = method_owner
     if len(method.params) != len(node.args):
         raise CompilationError(ErrorType.wrongArgument, f'Expected {len(method.params)} argument{"s" if len(method.params) != 1 else ""} '
                                f'for {obj}.{node.method}', node.lineno)
     for arg, param in zip(node.args, method.params.values()):
         arg = self.visit(arg)
         self.assert_is_subclass(arg, param, node.lineno)
     return method.return_type
예제 #7
0
 def p_mainclass(self, p):
     '''mainclass : CLASS ID LPARBR PUBLIC STATIC VOID ID LPAREN ID LPARSQ RPARSQ ID RPAREN LPARBR statement RPARBR RPARBR'''
     if p[7] != 'main':
         raise CompilationError(ErrorType.wrongMainMethod,
                                'Wrong main method name', p.lineno(7))
     p[0] = mj_ast.MainClass(p[2], p[15])
     p[0].lineno = p.lineno(2)
예제 #8
0
 def p_term_number(self, p):
     '''term : NUMBER'''
     number = int(p[1])
     if not constexpr_eval.is_mj_int(number):
         raise CompilationError(ErrorType.invalidInt,
                                'Too large for an integer', p.lineno(1))
     p[0] = mj_ast.IntLiteral(number)
예제 #9
0
 def p_statement_print(self, p):
     '''statement : ID DOT ID DOT ID LPAREN expression RPAREN SEMICOL'''
     if p[1] != 'System' or p[3] != 'out' or p[5] != 'println':
         raise CompilationError(
             ErrorType.onlyPrintlnCall,
             'Only System.out.println can be called this way', p.lineno(1))
     p[0] = mj_ast.PrintStatement(p[7])
     p[0].lineno = p.lineno(5)
예제 #10
0
 def dfs(c):
     if c in visited and c not in finalized:
         raise CompilationError(ErrorType.cycleInheritance,
                                'Inheritance loop in class ' + c,
                                table[c].lineno)
     visited.add(c)
     if table[c].parent:
         dfs(table[c].parent)
     finalized.add(c)
예제 #11
0
 def resolve_id(self, name, lineno):
     if not self.class_name or not self.method_name:
         raise CompilationError(ErrorType.undefinedVar, 'Undefined variable: ' + name, lineno)
     if name == 'this':
         return mj_ast.PARAM, self.class_name
     current = self.class_name
     class_table = self.table[current]
     if name in class_table.methods[self.method_name].vars:
         return mj_ast.LOCAL, class_table.methods[self.method_name].vars[name]
     if name in class_table.methods[self.method_name].params:
         return mj_ast.PARAM, class_table.methods[self.method_name].params[name]
     while True:
         if name in class_table.fields:
             return (mj_ast.FIELD, current), class_table.fields[name]
         current = class_table.parent
         class_table = self.table.get(current)
         if class_table is None:
             raise CompilationError(ErrorType.undefinedVar, 'Undefined variable: ' + name, lineno)
예제 #12
0
 def visit_method_declaration(self, node, table, *args):
     if node.name in table:
         raise CompilationError(ErrorType.dublicatedMethod,
                                'Duplicate method name: ' + node.name,
                                node.lineno)
     table[node.name] = method_table = MethodSymbolTable(
         node.is_public, node.return_type, node.lineno)
     for param in node.argseq:
         self.visit(param, method_table.params)
     for var in node.vardecl:
         self.visit(var, method_table.vars, method_table.params)
예제 #13
0
 def visit_class_declaration(self, node, *args):
     if node.name in self.table:
         raise CompilationError(ErrorType.dublicatedClass,
                                'Duplicate class: ' + node.name,
                                node.lineno)
     self.table[node.name] = class_table = ClassSymbolTable(
         node.parent, node.lineno)
     for var in node.vardecl:
         self.visit(var, class_table.fields)
     for method in node.methoddecl:
         self.visit(method, class_table.methods)
예제 #14
0
 def p_term(self, p):
     '''term : term DOT ID
             | term DOT ID LPAREN paramseq RPAREN'''
     if len(p) == 4:
         if p[3] != 'length':
             raise CompilationError(ErrorType.onlyLengthAccess,
                                    'Only length can be accessed this way',
                                    p.lineno(3))
         p[0] = mj_ast.LengthExpression(p[1])
     else:
         p[0] = mj_ast.CallExpression(p[1], p[3], p[5])
     p[0].lineno = p.lineno(3)
예제 #15
0
def check_inheritance_loops(table):
    visited = set()
    finalized = set()

    def dfs(c):
        if c in visited and c not in finalized:
            raise CompilationError(ErrorType.cycleInheritance,
                                   'Inheritance loop in class ' + c,
                                   table[c].lineno)
        visited.add(c)
        if table[c].parent:
            dfs(table[c].parent)
        finalized.add(c)

    for c in table:
        if table[c].parent:
            if table[c].parent not in table:
                raise CompilationError(ErrorType.classNotFound,
                                       'No such class: ' + table[c].parent,
                                       table[c].lineno)
        if c in finalized:
            continue
        dfs(c)
예제 #16
0
 def p_error(self, p):
     if p is None:
         raise CompilationError(ErrorType.endOfFile,
                                'Unexpected end of file')
     raise CompilationError(ErrorType.unexpectedToken,
                            f'Unexpected token "{p.value}"', p.lineno)
예제 #17
0
 def visit_assign_statement(self, node, *args):
     if node.obj == 'this':
         raise CompilationError(ErrorType.assignToThis, f'Cannot assign to this', node.lineno)
     node.type, obj = self.resolve_id(node.obj, node.lineno)
     val = self.visit(node.value)
     self.assert_is_subclass(val, obj, node.lineno)
예제 #18
0
 def t_error(self, t):
     raise CompilationError(ErrorType.illegalCharacter, f'Illegal character "{t.value[0]}"', t.lineno)
예제 #19
0
 def assert_type_exists(self, node_type, lineno):
     if node_type not in self.ATOMS and node_type not in self.table:
         raise CompilationError(ErrorType.nonexistentType, f'Type "{node_type}" does not exist', lineno)