Exemplo n.º 1
0
 def eval_expr(self, node: parser.Node) -> InterpreterValueAny:
     # TODO: Support referencing other static variables
     # Bitmask for 64-bit computation
     bitmask = 0xffffffffffffffff
     if node.i("number"):
         return self.create_int_value(int(node.data_strict))
     elif node.i("true"):
         return self.true
     elif node.i("false"):
         return self.false
     elif node.i("null"):
         return self.null
     elif node.of("+", "*", "^", "&", "|"):
         lhs = self.eval_expr(node[0])
         rhs = self.eval_expr(node[1])
         if not isinstance(lhs, InterpreterValueInteger) or not isinstance(
                 rhs, InterpreterValueInteger):
             raise typesys.TypingError(
                 node,
                 "Attempt to perform arithmetic on something that isn't an integers"
             )
         if node.i("+"):
             ret = lhs.value + rhs.value
         elif node.i("*"):
             ret = lhs.value * rhs.value
         elif node.i("^"):
             ret = lhs.value ^ rhs.value
         elif node.i("&"):
             ret = lhs.value & rhs.value
         elif node.i("|"):
             ret = lhs.value | rhs.value
         return self.create_int_value(ret & bitmask)
     elif node.of("and", "or"):
         lhs = self.eval_expr(node[0])
         rhs = self.eval_expr(node[1])
         if not isinstance(lhs, InterpreterValueBoolean) or not isinstance(
                 rhs, InterpreterValueBoolean):
             raise typesys.TypingError(
                 node,
                 "Attempt to perform logical operation on something that isn't an integers"
             )
         if node.i("and"):
             ret = lhs.value and rhs.value
         elif node.i("or"):
             ret = lhs.value or rhs.value
         return self.true if ret else self.false
     elif node.i("not"):
         val = self.eval_expr(node[0])
         if not isinstance(val, InterpreterValueBoolean):
             raise typesys.TypingError(
                 node,
                 "Attempt to perform logical operation on something that isn't an integers"
             )
         return self.false if val.value else self.true
     elif node.of(">=", "<=", "<", ">"):
         lhs = self.eval_expr(node[0])
         rhs = self.eval_expr(node[1])
         if not isinstance(lhs, InterpreterValueInteger) or not isinstance(
                 rhs, InterpreterValueInteger):
             raise typesys.TypingError(
                 node,
                 "Attempt to perform arithmetic on something that isn't an integers"
             )
         lhs_value = self.normalize_negative(lhs.value)
         rhs_value = self.normalize_negative(rhs.value)
         if node.i(">="):
             ret = lhs_value >= rhs_value
         elif node.i("<="):
             ret = lhs_value <= rhs_value
         elif node.i("<"):
             ret = lhs_value < rhs_value
         elif node.i(">"):
             ret = lhs_value > rhs_value
         else:
             raise ValueError("This is a compiler bug")
         return self.true if ret else self.false
     elif node.of("==", "!="):
         lhs = self.eval_expr(node[0])
         rhs = self.eval_expr(node[1])
         if not lhs.type.is_assignable_to(
                 rhs.type) and not rhs.type.is_assignable_to(lhs.type):
             raise typesys.TypingError(
                 node,
                 "Incomparable types: '%s' and '%s'" % (lhs.type, rhs.type))
         return self.true if lhs.equals(rhs) ^ (
             True if node.i("!=") else False) else self.false
     elif node.i("-") and len(node) == 2:
         lhs = self.eval_expr(node[0])
         rhs = self.eval_expr(node[1])
         if not isinstance(lhs, InterpreterValueInteger) or not isinstance(
                 rhs, InterpreterValueInteger):
             raise typesys.TypingError(
                 node,
                 "Attempt to perform arithmetic on something that isn't an integers"
             )
         return self.create_int_value((lhs.value - rhs.value) & bitmask)
     elif (node.i("-") and len(node) == 1) or node.i("~"):
         val = self.eval_expr(node[0])
         if not isinstance(val, InterpreterValueInteger):
             raise typesys.TypingError(
                 node, "Attempt to negate something that isn't an integer")
         return self.create_int_value(
             (-val.value if node.i("-") else ~val.value) & bitmask)
     elif node.i("/"):
         lhs = self.eval_expr(node[0])
         rhs = self.eval_expr(node[1])
         if not isinstance(lhs, InterpreterValueInteger) or not isinstance(
                 rhs, InterpreterValueInteger):
             raise typesys.TypingError(
                 node,
                 "Attempt to perform arithmetic on something that isn't an integers"
             )
         lhs_value = lhs.value
         rhs_value = rhs.value
         # Make sure, if our value is negative, we're dividing by a
         # negative rather than a very large value (due to two's
         # complement)
         lhs_value = self.normalize_negative(lhs_value)
         rhs_value = self.normalize_negative(rhs_value)
         res = lhs_value // rhs_value
         if res * rhs.value != lhs.value:
             # Python rounds toward negative infinity, whereas C
             # (and thus our language) rounds toward 0.
             if res < 0:
                 res += 1
         return self.create_int_value(res & bitmask)
     elif node.i("["):
         typ = self.program.types.decide_type(node, emitter.Scopes(), None)
         if not isinstance(typ, typesys.ArrayType):
             raise ValueError("This is a compiler bug.")
         # All of our typechecking has already been taken care of in
         # the logic in typesys there
         result_values: List[AbstractInterpreterValue] = []
         for child in node:
             result_values.append(self.eval_expr(child))
         return self.create_array_value(typ, result_values)
     elif node.i("arrinst"):
         typ = self.program.types.decide_type(node, emitter.Scopes(), None)
         if not isinstance(typ, typesys.ArrayType):
             raise ValueError("This is a compiler bug.")
         # Same thing, all of our typechecking is delegated to typesys
         array_len = self.eval_expr(node[1])
         if not isinstance(array_len, InterpreterValueInteger):
             raise typesys.TypingError(
                 node[1], "Type of array length must be an integer")
         if array_len.value > 1024:
             node.warn(
                 "Statically creating a very large array, this will make your bytecode file very large: %d"
                 % array_len.value)
         arrinst_result_values: List[AbstractInterpreterValue]
         if isinstance(typ.parent_type, typesys.IntType):
             arrinst_result_values = [self.create_int_value(0)
                                      ] * array_len.value
         elif isinstance(typ.parent_type, typesys.BoolType):
             arrinst_result_values = [self.false] * array_len.value
         else:
             arrinst_result_values = [self.null] * array_len.value
         return self.create_array_value(typ, arrinst_result_values)
     elif node.i("#"):
         val = self.eval_expr(node[0])
         if not isinstance(val, InterpreterValueArray):
             raise typesys.TypingError(
                 node[0],
                 "Cannot find length of something that isn't an array")
         return self.create_int_value(len(val.value))
     elif node.i("access"):
         lhs = self.eval_expr(node[0])
         rhs = self.eval_expr(node[1])
         if not isinstance(lhs, InterpreterValueArray):
             raise typesys.TypingError(
                 node[0],
                 "Can't access an element of something that isn't an array")
         if not isinstance(rhs, InterpreterValueInteger):
             raise typesys.TypingError(node[1],
                                       "Array indices must be integers")
         return lhs.value[rhs.value]
     else:
         node.compile_error("Cannot evaluate expression at compile-time")
Exemplo n.º 2
0
    def decide_type(
            self,
            expr: parser.Node,
            scope: "emitter.Scopes",
            generic_type_context: Optional[GenericTypeContext],
            suppress_coercing_void_warning: bool = False) -> AbstractType:
        if expr.i("as"):
            return self.resolve_strict(expr[1], generic_type_context)
        elif expr.i("instanceof"):
            return self.bool_type
        elif expr.of("+", "*", "-", "^", "&", "|", "%",
                     "/") and len(expr) == 2:
            lhs_type = self.decide_type(expr[0], scope, generic_type_context)
            rhs_type = self.decide_type(expr[1], scope, generic_type_context)
            if not lhs_type.is_numerical():
                raise TypingError(expr[0],
                                  "Type %s is not numerical" % lhs_type)

            if not rhs_type.is_numerical():
                raise TypingError(expr[1],
                                  "Type %s is not numerical" % rhs_type)

            if not lhs_type.is_assignable_from(
                    rhs_type) or not lhs_type.is_assignable_to(rhs_type):
                raise TypingError(
                    expr,
                    "Types %s and %s are incompatible for arithmetic operation"
                    % (lhs_type, rhs_type))

            return lhs_type
        elif expr.of(">=", "<=", ">", "<"):
            lhs_type = self.decide_type(expr[0], scope, generic_type_context)
            rhs_type = self.decide_type(expr[1], scope, generic_type_context)
            if not lhs_type.is_numerical():
                raise TypingError(expr[0],
                                  "Type %s is not numerical" % lhs_type)

            if not rhs_type.is_numerical():
                raise TypingError(expr[1],
                                  "Type %s is not numerical" % rhs_type)

            if not lhs_type.is_assignable_from(
                    rhs_type) or not lhs_type.is_assignable_to(rhs_type):
                raise TypingError(
                    expr,
                    "Types %s and %s are incompatible for arithmetic comparison"
                    % (lhs_type, rhs_type))

            return self.bool_type
        elif expr.of("==", "!="):
            lhs_type = self.decide_type(expr[0], scope, generic_type_context)
            rhs_type = self.decide_type(expr[1], scope, generic_type_context)
            if not lhs_type.is_assignable_to(
                    rhs_type) and not rhs_type.is_assignable_to(lhs_type):
                raise TypingError(
                    expr,
                    "Incomparable types: '%s' and '%s'" % (lhs_type, rhs_type))
            return self.bool_type
        elif expr.i("number"):
            return self.int_type
        elif expr.of("and", "or"):
            lhs_type = self.decide_type(expr[0], scope, generic_type_context)
            rhs_type = self.decide_type(expr[1], scope, generic_type_context)
            if not lhs_type.is_boolean():
                raise TypingError(expr[0], "Type %s is not boolean" % lhs_type)

            if not rhs_type.is_boolean():
                raise TypingError(expr[0], "Type %s is not boolean" % rhs_type)

            return self.bool_type
        elif expr.i("not"):
            type = self.decide_type(expr[0], scope, generic_type_context)
            if not type.is_boolean():
                raise TypingError(expr[0], "Type %s is not boolean" % type)
            return self.bool_type
        elif expr.of("~", "-") and len(expr) == 1:
            type = self.decide_type(expr[0], scope, generic_type_context)
            if not type.is_numerical():
                raise TypingError(expr[0], "Type %s is not numerical" % type)
            return self.int_type
        elif expr.of("ident", ".") \
                and util.get_flattened(expr) is not None \
                and self.program.static_variables.has_variable(util.nonnull(util.get_flattened(expr))):
            return self.program.static_variables.resolve_variable(
                util.nonnull(util.get_flattened(expr))).type
        elif expr.i("ident"):
            return scope.resolve(expr.data_strict, expr).type
        elif expr.of("true", "false"):
            return self.bool_type
        elif expr.i("null"):
            return self.void_type
        elif expr.i("["):
            # For now, everything must be the same type, except for nulls
            # interspersed. Later this will change.
            current_type = None
            if len(expr) == 0:
                expr.compile_error(
                    "Zero-length arrays must be instantiated with the arbitrary-length instantiation syntax"
                )
            for child in expr:
                current_child_type: AbstractType = self.decide_type(
                    child, scope, generic_type_context)
                if current_type is None:
                    if not current_child_type.is_void():
                        current_type = current_child_type
                    else:
                        continue

                # This is to make the typechecker happy
                # We know this is safe because of the previous if-statement
                current_type_not_none: AbstractType = current_type

                while current_type_not_none.get_supertype(
                ) is not None and not current_child_type.is_assignable_to(
                        current_type_not_none):
                    current_type = current_type_not_none.get_supertype()
                if not current_child_type.is_assignable_to(
                        current_type_not_none):
                    raise TypingError(
                        child,
                        "Could not reconcile type %s" % (current_child_type))

            if current_type is None:
                expr.compile_error(
                    "Cannot have an array literal comprising only null values, use arbitrary-length instantiation syntax instead"
                )
                # Unreachable
                return None  # type: ignore
            else:
                return self.get_array_type(current_type)
        elif expr.i("arrinst"):
            return self.get_array_type(
                self.resolve_strict(expr[0], generic_type_context))
        elif expr.i("#"):
            if not self.decide_type(expr[0], scope,
                                    generic_type_context).is_array():
                raise TypingError(
                    expr[0],
                    "Can't decide length of something that isn't an array")
            return self.int_type
        elif expr.i("access"):
            lhs_type = self.decide_type(expr[0], scope, generic_type_context)
            if not lhs_type.is_array():
                raise TypingError(
                    expr,
                    "Attempt to access element of something that isn't an array"
                )
            assert isinstance(lhs_type, ArrayType)
            return lhs_type.parent_type
        elif expr.i("call"):
            # TODO: Move method call typechecking in here from emitter.py.
            signature = self.resolve_to_signature(expr[0], scope,
                                                  generic_type_context)
            if len(expr["typeargs"]) != (
                    len(signature.generic_type_context.arguments)
                    if signature.generic_type_context is not None else 0):
                expr.compile_error(
                    "Wrong number of type arguments (expected %s, got %s)" %
                    (len(signature.generic_type_context.arguments)
                     if signature.generic_type_context is not None else 0,
                     len(expr["typeargs"])))
            signature = signature.specialize([
                self.resolve_strict(typ, generic_type_context)
                for typ in expr["typeargs"]
            ], self.program, expr["typeargs"])
            if signature is not None:
                if (not suppress_coercing_void_warning) and isinstance(
                        signature.returntype, VoidType):
                    expr.warn("Coercing void")
                return signature.returntype
            if expr[0].i("."):
                dot_node = expr[0]
                lhs_type = self.decide_type(dot_node[0], scope)
                if not lhs_type.is_clazz():
                    raise TypingError(
                        dot_node[1],
                        "Attempt to call a method on non-class type '%s'" %
                        lhs_type)
                signature = lhs_type.method_signature(dot_node[1].data)
            if signature is None:
                expr.compile_error("Unknown method name '%s'" % expr[0].data)
            if (not suppress_coercing_void_warning) and isinstance(
                    signature.returntype, VoidType):
                expr.warn("Coercing void")
            return signature.returntype
        elif expr.i("new"):
            ret = self.resolve_strict(expr[0],
                                      generic_type_context,
                                      allow_raw=True)
            # Why not .is_class()? Because we can't instantiate generic type parameters.
            if not isinstance(ret, ClazzType):
                raise TypingError(expr[0], "Type %s is not a class" % ret)
            type_arguments: List[AbstractType] = []
            for argument in expr["typeargs"]:
                type_arguments.append(
                    self.resolve_strict(argument, generic_type_context))
            ret = ret.specialize(type_arguments, expr)
            return ret
        elif expr.i("."):
            # Ternary operator to satisfy typechecker (if-else statement would effectively require phi node which is ugly)
            lhs_typ: AbstractType = \
                (scope.resolve(expr[0].data_strict, expr[0]).type) if expr[0].i("ident") \
                else self.decide_type(expr[0], scope, generic_type_context)
            if not lhs_typ.is_clazz():
                expr.compile_error(
                    "Attempt to access an attribute of something that isn't a class"
                )
            assert isinstance(lhs_typ, ClazzType)
            return lhs_typ.type_of_property(expr[1].data_strict, expr)
        elif expr.i("string"):
            string_type = self.get_string_type()
            if string_type is None:
                expr.compile_error(
                    "Cannot use string literal without an implementation of stdlib.String"
                )
            return string_type
        elif expr.i("super"):
            # TODO: This is horribly ugly
            typ = scope.resolve("this", expr).type
            if not isinstance(typ, ClazzType):
                expr.compile_error("Variable `this` isn't a class type")
            parent_type = typ.get_supertype()
            if parent_type is None:
                expr.compile_error(
                    "Attempt to reference superclass in a class without a superclass"
                )
            return parent_type
        else:
            raise ValueError(
                "Expression not accounted for in typesys. This is a compiler bug."
            )