Exemplo n.º 1
0
    def _Call(self, t):
        self._dispatch(t.func)
        # Check that t.func is actually a name
        func_node_class = t.func.__class__.__name__
        is_attribute = False
        if func_node_class == "AttributeRefNode":
            is_attribute = True
            name_node = t.func.attribute
        elif func_node_class in ("NameNode", "TypeNode"):
            name_node = t.func
        else:
            raise InvalidNameError("{1}: Cannot call a {0}".format(func_node_class, t.lineno))

        # Check that this name is actually callable
        # Also, if t.func is a type, then this is a constructor call
        func_sym = self._symbol_table.get(name_node.value, namespace=name_node.namespace)
        func_sym_class = func_sym.__class__.__name__
        if func_sym_class == "TypeSymbol":
            t.is_constructor = True
        elif func_sym_class != "FunctionSymbol":
            raise InvalidNameError("{1}: Cannot call an instance of {0}".format(func_sym_class, t.lineno))

        self._dispatch(t.args)

        if t.is_constructor:
            type_sym = func_sym
            # Use the class's constructor method symbol instead
            func_sym = self._resolve_attribute(type_sym.full_name, util.CONSTRUCTOR_NAME)
            t.type = type_sym.full_name
            # Replace NameNode with TypeNode for the type being constructed
            if func_node_class == "NameNode":
                new_node = nodes.TypeNode(type_sym.name, type_sym.namespace)
                new_node.type = type_sym.full_name
                new_node.lineno = t.lineno
                if is_attribute:
                    t.func.attribute = new_node
                else:
                    t.func = new_node
            if func_sym is None:
                # There's no init method to check, so we're done here
                return
        else:
            t.type = func_sym.return_type

        # Check that arg types match param_types in function symbol
        if len(func_sym.param_types) != len(t.args):
            raise ParameterCountError(
                "{0}: expected {1} argument(s) for function {2}, "
                "found {3}".format(
                    t.lineno, len(func_sym.param_types), symbols.stringify_full_name(func_sym.full_name), len(t.args)
                )
            )
        for i, (param_type, arg) in enumerate(zip(func_sym.param_types, t.args)):
            if param_type not in self._get_ancestor_types(arg.type):
                raise InconsistentTypeError(
                    "{3}: expected type {0} for function argument {1}, "
                    "found {2}".format(
                        symbols.stringify_full_name(param_type), i + 1, symbols.stringify_full_name(arg.type), t.lineno
                    )
                )
Exemplo n.º 2
0
    def _BinaryOp(self, t):
        self._dispatch(t.left)
        self._dispatch(t.right)

        boolean_ops = ("or", "and")
        comparison_ops = ("<", ">", "<=", ">=", "==", "!=", "is")
        arithmetic_ops = ("+", "-", "*", "/", "%")
        bool_sym = self._symbol_table.get("bool", symbol_type=symbols.TypeSymbol)
        bool_type = bool_sym.full_name
        int_type = self._symbol_table.get("int", symbol_type=symbols.TypeSymbol).full_name
        float_sym = self._symbol_table.get("float", symbol_type=symbols.TypeSymbol)
        float_type = float_sym.full_name
        str_type = self._symbol_table.get("str", symbol_type=symbols.TypeSymbol).full_name
        numeric_types = set([int_type, float_type])
        op = t.operator
        if op in boolean_ops:
            t.type = bool_type
            # Coerce each side to bool if numeric or string
            bool_node = nodes.NameNode(bool_sym.name, bool_sym.namespace)
            if t.left.type in (int_type, float_type, str_type):
                new_node = nodes.CallNode(func=bool_node, args=[t.left], is_constructor=True)
                new_node.type = bool_type
                t.left = new_node
            if t.right.type in (int_type, float_type, str_type):
                new_node = nodes.CallNode(func=bool_node, args=[t.right], is_constructor=True)
                new_node.type = bool_type
                t.right = new_node
        elif op in comparison_ops:
            t.type = bool_type
            if op not in ("==", "!=", "is") and not (t.left.type in numeric_types and t.right.type in numeric_types):
                raise InvalidTypeError(
                    "{0}: either {1} or {2} types cannot take part in an "
                    "arithmetic operation".format(
                        t.lineno, symbols.stringify_full_name(t.left.type), symbols.stringify_full_name(t.right.type)
                    )
                )
        elif op in arithmetic_ops:
            if not (t.left.type in numeric_types and t.right.type in numeric_types):
                raise InvalidTypeError(
                    "{0}: either {1} or {2} types cannot take part in an "
                    "arithmetic operation".format(
                        t.lineno, symbols.stringify_full_name(t.left.type), symbols.stringify_full_name(t.right.type)
                    )
                )
            if t.left.type != t.right.type:
                t.type = float_type
                # Coerce int to float (we only have 2 numeric types)
                float_node = nodes.NameNode(float_sym.name, float_sym.namespace)
                if t.left.type == int_type:
                    new_node = nodes.CallNode(func=float_node, args=[t.left], is_constructor=True)
                    new_node.type = float_type
                    t.left = new_node
                else:
                    new_node = nodes.CallNode(func=float_node, args=[t.right], is_constructor=True)
                    new_node.type = float_type
                    t.right = new_node
            else:
                t.type = t.left.type
        else:
            raise Error("{1}: Unknown binary operation: {0}".format(op, t.lineno))
Exemplo n.º 3
0
 def _Assignment(self, t):
     self._dispatch(t.target)
     self._dispatch(t.value)
     ancestor_types = self._get_ancestor_types(t.value.type)
     if t.target.type not in ancestor_types:
         raise InconsistentTypeError(
             "{2}: Target type {0} not compatible with "
             "value type {1}".format(
                 symbols.stringify_full_name(t.target.type),
                 symbols.stringify_full_name(t.value.type),
                 t.target.lineno,
             )
         )
Exemplo n.º 4
0
    def _BinaryOp(self, t):
        op = t.operator
        if op in ('==', '!='):
            # value equality
            if op == '!=':
                self.write('!')
            self.write('(')
            self.dispatch(t.left)
            self.write(').equals(')
            self.dispatch(t.right)
            self.write(')')
            return

        if op == 'is':
            # object equality
            op = '=='
        if op == 'and':
            op = '&&'
        elif op == 'or':
            op = '||'
        expr_type = symbols.stringify_full_name(t.type)
        self.write('(({0}) ('.format(convert_type(expr_type)))
        self.dispatch(t.left)
        self.write(' {0} '.format(op))
        self.dispatch(t.right)
        self.write('))')
Exemplo n.º 5
0
 def _Subscript(self, t):
     self._dispatch(t.value)
     # Check that we can actually index into t.value
     if ((), "list") not in self._get_ancestor_types(t.value.type):
         raise InvalidTypeError(
             "{1}: Type {0} is not subscriptable -- only type list".format(
                 symbols.stringify_full_name(t.value.type), t.lineno
             )
         )
     self._dispatch(t.index)
     # Check that the index is actually an integer
     if ((), "int") not in self._get_ancestor_types(t.index.type):
         raise InvalidTypeError(
             "{2}: Invalid subscript type {0} for index {1}".format(
                 symbols.stringify_full_name(t.index.type), t.index, t.lineno
             )
         )
     # An iterable can contain elements of any type
     t.type = ((), "object")
Exemplo n.º 6
0
 def _ClassDef(self, t):
     self._dispatch(t.name)
     if t.base:
         self._dispatch(t.base)
     self._dispatch(t.body)
     for stmt in t.body:
         if (
             stmt.__class__.__name__ == "FunctionDefNode"
             and stmt.name.value == util.CONSTRUCTOR_NAME
             and stmt.return_type.type != t.name.type
         ):
             raise InconsistentTypeError(
                 "{0}: constructor return type {1} does not match "
                 "class name {2}".format(
                     t.lineno,
                     symbols.stringify_full_name(stmt.return_type.type),
                     symbols.stringify_full_name(t.name.type),
                 )
             )
Exemplo n.º 7
0
 def _For(self, t):
     # "enhanced for" loop
     self._dispatch(t.target)
     self._dispatch(t.iterable)
     # Check that t.iterable is an instance of list or set
     if not (set(self._get_ancestor_types(t.iterable.type)) & set([((), "list"), ((), "set")])):
         raise InvalidTypeError(
             "{1}: expected an iterable (list, set), found {0}".format(
                 symbols.stringify_full_name(t.iterable.type), t.lineno
             )
         )
     for item in getattr(t.iterable, "elts", []):
         ancestor_types = self._get_ancestor_types(item.type)
         if t.target.type not in ancestor_types:
             raise InconsistentTypeError(
                 "{0}: expected type {1}, found type {2}".format(
                     item.lineno, symbols.stringify_full_name(t.target.type), symbols.stringify_full_name(item.type)
                 )
             )
     self._dispatch(t.body)
Exemplo n.º 8
0
    def _FunctionDef(self, t):
        self._dispatch(t.name)

        sym = self._symbol_table.get_by_qualified_name((t.name.namespace, t.name.value))
        self._dispatch(t.return_type)
        sym.return_type = (t.return_type.namespace, t.return_type.value)

        self._dispatch(t.params)
        self._dispatch(t.body)

        # Check that the return types matches the function declaration.
        # Look for return statement(s) in 'body'.
        return_types = self._get_return_types(t.body)
        if not (return_types or sym.return_type == ((), "void") or t.name.value == util.CONSTRUCTOR_NAME):
            raise InconsistentTypeError(
                "{0}: found no return type(s) for function {1}".format(
                    t.lineno, symbols.stringify_full_name(sym.full_name)
                )
            )
        if sym.return_type == ((), "void") and return_types:
            raise InconsistentTypeError(
                "{0}: found return type(s) for function {1}, "
                "expected none".format(t.lineno, symbols.stringify_full_name(sym.full_name))
            )
        for return_type in return_types:
            if return_type == util.NULL:
                continue
            ancestor_types = self._get_ancestor_types(return_type)
            if sym.return_type not in ancestor_types:
                raise InconsistentTypeError(
                    "{0}: expected return type {1} in definition of "
                    "function {2}, found return type {3}".format(
                        t.lineno,
                        symbols.stringify_full_name(sym.return_type),
                        t.name.value,
                        symbols.stringify_full_name(return_type),
                    )
                )
Exemplo n.º 9
0
 def _Type(self, t):
     sym = self._symbol_table.get(t.value, namespace=t.namespace, symbol_type=symbols.TypeSymbol)
     # Now that we've resolved this type symbol, we can update the namespace
     # and type for it and any type parameters
     t.namespace = sym.namespace
     t.type = sym.full_name
     self._dispatch(t.params)
     expected_param_count = util.COLLECTION_BASE_TYPES.get(t.type)
     if expected_param_count and expected_param_count != len(t.params):
         raise InvalidTypeError(
             "{0}: expected {1} parameter(s) for type {2}, "
             "found {3}".format(t.lineno, expected_param_count, symbols.stringify_full_name(t.type), len(t.params))
         )
     for param in t.params:
         param_sym = self._symbol_table.get(param.value, namespace=param.namespace, symbol_type=symbols.TypeSymbol)
         param.namespace = param_sym.namespace
         param.type = param_sym.full_name
Exemplo n.º 10
0
    def _UnaryOp(self, t):
        self._dispatch(t.operand)

        boolean_ops = ("not",)
        arithmetic_ops = ("+", "-")
        bool_sym = self._symbol_table.get("bool", symbol_type=symbols.TypeSymbol)
        bool_type = bool_sym.full_name
        int_type = self._symbol_table.get("int", symbol_type=symbols.TypeSymbol).full_name
        float_sym = self._symbol_table.get("float", symbol_type=symbols.TypeSymbol)
        float_type = float_sym.full_name
        str_type = self._symbol_table.get("str", symbol_type=symbols.TypeSymbol).full_name
        numeric_types = set([int_type, float_type])
        op = t.operator
        if op in boolean_ops:
            t.type = bool_type
            # Coerce operand to bool if numeric or string
            bool_node = nodes.NameNode(bool_sym.name, bool_sym.namespace)
            if t.operand.type in (int_type, float_type, str_type):
                new_node = nodes.CallNode(func=bool_node, args=[t.operand], is_constructor=True)
                new_node.type = bool_type
                t.operand = new_node
        elif op in arithmetic_ops:
            if t.operand.type not in numeric_types:
                raise InvalidTypeError(
                    "{0}: {1} type cannot take part in an "
                    "arithmetic operation".format(t.lineno, symbols.stringify_full_name(t.operand.type))
                )
            t.type = t.operand.type
        elif op.__class__.__name__ == "TypeNode":
            # Type cast
            self._dispatch(op)
            # op_ancestor_types = self._get_ancestor_types(op.type)
            # operand_ancestor_types = self._get_ancestor_types(t.operand.type)
            # if (op.type not in operand_ancestor_types and
            #    t.operand.type not in op_ancestor_types):
            #    raise InconsistentTypeError(
            #        '{0}: operator type {1} not incompatible with '
            #        'operand type {2}'.format(
            #            t.lineno, symbols.stringify_full_name(op.type),
            #            symbols.stringify_full_name(t.operand.type)))
            t.type = t.operand.type = op.type
        else:
            raise Error("{1}: Unknown unary operation: {0}".format(op, t.lineno))
Exemplo n.º 11
0
    def _AttributeRef(self, t):
        self._dispatch(t.value)
        self._dispatch(t.attribute)
        # check that t.attribute is actually an attribute of t.value
        attr_sym = self._resolve_attribute(t.value.type, t.attribute.value)
        if attr_sym is None:
            raise symbols.UnknownSymbolError(
                "{2}: {0} is not an attribute of type {1} or any of its "
                "ancestors".format(t.attribute.value, symbols.stringify_full_name(t.value.type), t.lineno)
            )

        # We skipped setting the attribute's namespace and type in _Name() so
        # that we could set it here
        t.attribute.namespace = attr_sym.namespace
        if attr_sym.__class__.__name__ == "FunctionSymbol":
            # If the attribute isn't a VariableSymbol or TypeSymbol,
            # type means nothing
            t.attribute.type = None
        else:
            t.attribute.type = attr_sym.var_type
        t.type = t.attribute.type
Exemplo n.º 12
0
    def _Call(self, t):
        # Translate qualified function name to Java name
        # - type casting: special handling
        # - builtin function: prepend GraphUtil
        # - constructor: prepend "new "
        # - attribute ref: map attr (namespace, name) to Java attr

        func_name_node = getattr(t.func, 'attribute', t.func)
        func_full_name = symbols.stringify_full_name(
            (func_name_node.namespace, func_name_node.value))
        # Special handling for casting
        if func_full_name == 'bool':
            self.write('Boolean.valueOf(')
            self.write('(')
            self.dispatch(t.args[0])
            self.write(') != 0 ? "true" : "false"')
            self.write(')')
            return
        if func_full_name == 'str':
            self.write('String.valueOf(')
            self.dispatch(t.args[0])
            self.write(')')
            return
        if func_full_name == 'int':
            self.write('Integer.valueOf(')
            self.write('(')
            self.dispatch(t.args[0])
            self.write(').toString()')
            self.write(')')
            return
        if func_full_name == 'float':
            self.write('Double.valueOf(')
            self.write('(')
            self.dispatch(t.args[0])
            self.write(').toString()')
            self.write(')')
            return
        if func_full_name == '__builtins.isinstance':
            self.write('(')
            self.dispatch(t.args[0])
            type_node = t.args[1]
            if type_node.__class__.__name__ == 'StringNode':
                value = eval(type_node.value)
            else:
                value = symbols.stringify_full_name(type_node.type)
            new_type = convert_type(value)
            self.write(' instanceof {0}'.format(new_type))
            self.write(')')
            return

        if t.is_constructor:
            self.write('(')
            self.write('new ')
            self.dispatch(func_name_node)
            self.write('(')
            interleave(lambda: self.write(', '), self.dispatch, t.args)
            self.write(')')
            self.write(')')
            return

        java_name = JAVA_NAME_MAP.get(func_full_name, func_name_node.value)
        if func_name_node.namespace == (util.BUILTINS_CLASS_NAME,):
            # Built-in functions have special handling
            self.write(java_name)
        elif func_name_node.namespace == ():
            # Top-level functions need to be qualified with the wrapper class
            self.write('{0}.{1}'.format(TARGET_PROGRAM_NAME, java_name))
        else:
            # If called function is an object attribute, generate object first
            if getattr(t.func, 'attribute', False):
                self.dispatch(t.func.value)
                self.write('.')
            self.write(java_name)
        self.write('(')
        interleave(lambda: self.write(', '), self.dispatch, t.args)
        self.write(')')