Exemplo n.º 1
0
def check_expr(expr, t, env):
    """
    Check whether the expression `expr` can be assigned type `t` under type
    environment `env`.

    We defer to the specific `check_X_expr` functions to determine which type
    assignment rule to try. Information about the structure of each AST node is
    contained in the thesis PDF.
    """

    assert isinstance(expr, ast.expr), \
           "Should be typechecking an expr node, not a " + cname(expr)
    assert isinstance(t, PType), \
           "Should be checking against a PType, not a " + cname(t)

    n = expr_template % expr.__class__.__name__

    t_debug("-- v Typechecking expr as " + str(t) + " v --\nExpr: " +
            str(expr) + "\nEnv: " + str(env))

    # if we get a KeyError, then we're inspecting an AST node that is not in
    # the subset of the language we're considering (note: the subset is
    # defined as whtaever there are check function definitions for).
    try:
        result = call_function(n, expr, t, env)
        t_debug("return: " + str(result) + "\n-- ^ Typechecking expr ^ --")
        return result
    except KeyError as e:
        t_debug("Found an expr not in the language subset. (" + str(e) + ")")
        return False
Exemplo n.º 2
0
    def __init__(self, targets, t, line, col=None):
        """
        Create a `TypeDec` node with the supplied parameters.

        #### Parameters
        - `targets`: list of identifiers (as `ast.Name` objects) having their
            types declared.
        - `t`: the type being assigned, as a PType or string. If a string is
            provided, it is parsed into the appropriate PType.
        - `line`: the (int) line number of the declaration in the source code.
        - `col`: [optional] the (int) column number of the declaration in the
            source code. If not provided, then the column number will just be
            set as `None`.
        """

        self.targets = targets
        self.lineno = line
        if col is not None:
            self.col_offset = col

        if type(t) == str:
            self.t = PType.from_str(t)
            assert self.t.__class__ == PType, \
                   ("Got a %s back from TypeSpecParser.parse, not a PType" %
                    cname(self.t.__class__))
        elif t.__class__ == PType:
            self.t = t
        else:
            assert False, ("t needs to be specified as str or PType, not " +
                           cname(t))

        # these are instance variables provided by AST nodes to allow traversal
        # / parsing of the nodes.
        self._fields = ("targets", "t")
        self._attributes = ("lineno", "col_offset")
Exemplo n.º 3
0
def _check_UnaryOp_expr(unop, t, env):
    """Unary Operations."""

    assert unop.__class__ is ast.UnaryOp
    
    op = unop.op
    e = unop.operand

    assert op.__class__ in unary_ops, "%s not in unary ops" % cname(op)

    # (Inv) assignment rule.
    if op.__class__ is ast.Invert and t == int_t:
        return check_expr(e, int_t, env)

    # (Uadd) assignment rule.
    elif op.__class__ in [ast.UAdd, ast.USub] and t in [int_t, float_t]:
        return check_expr(e, t, env)

    # (Not) assignment rule.
    elif op.__class__ is ast.Not and t == bool_t:
        return check_expr(e, bool_t, env)

    # No assignment rule found.
    else:
        return False
Exemplo n.º 4
0
def _check_BoolOp_expr(boolop, t, env):
    """Boolean Operations."""

    assert boolop.__class__ is ast.BoolOp

    op = boolop.op
    es = boolop.values

    assert op.__class__ in bool_ops, "%s not in bool ops" % cname(op)

    # (BoolOp) assignment rule.
    return all(check_expr(e, t, env) for e in es)
Exemplo n.º 5
0
    def __init__(self, targets, t, line, col = None):
        """
        Create a `TypeDec` node with the supplied parameters.

        #### Parameters
        - `targets`: list of identifiers (as `ast.Name` objects) having their
            types declared.
        - `t`: the type being assigned, as a PType or string. If a string is
            provided, it is parsed into the appropriate PType.
        - `line`: the (int) line number of the declaration in the source code.
        - `col`: [optional] the (int) column number of the declaration in the
            source code. If not provided, then the column number will just be
            set as `None`.
        """

        self.targets = targets
        self.lineno = line
        if col is not None:
            self.col_offset = col

        if type(t) == str:
            self.t = PType.from_str(t)
            assert self.t.__class__ == PType, \
                   ("Got a %s back from TypeSpecParser.parse, not a PType" %
                    cname(self.t.__class__))
        elif t.__class__ == PType:
            self.t = t
        else:
            assert False, ("t needs to be specified as str or PType, not " +
                           cname(t))


        # these are instance variables provided by AST nodes to allow traversal
        # / parsing of the nodes.
        self._fields = ("targets", "t")
        self._attributes = ("lineno", "col_offset")
Exemplo n.º 6
0
def infer_expr(e, env):
    """
    Use limited type inference to determine the type of AST expression `e` under
    type environment `env`.
    """

    assert isinstance(e, ast.expr), \
           "Should be inferring type of an expr node, not a " + cname(e)

    n = get_infer_expr_func_name(e.__class__.__name__)

    # If we get a KeyError, then we're trying to infer the type of an AST node
    # that is not in the very limited subset of the language that we're trying
    # to perform type inference on.

    return call_function(n, e, env)
Exemplo n.º 7
0
def _check_BinOp_expr(binop, t, env):
    """Binary Operations."""

    assert binop.__class__ is ast.BinOp

    e0 = binop.left
    e1 = binop.right
    op = binop.op

    assert op.__class__ in bin_ops, "%s not in binary ops" % cname(op)

    # Numeric Operations.
    if t == int_t or t == float_t:

        # (Arith) assignment rule.
        if op.__class__ in arith_ops:
            return check_expr(e0, t, env) and check_expr(e1, t, env)

        # (BitOp) assignment rule.
        elif op.__class__ in bit_ops and t == int_t:
            return check_expr(e0, int_t, env) and check_expr(e1, int_t, env)

    # String and List Operations.
    elif t == str_t or t == unicode_t or t.is_list():

        # (Str-Cat) assignment rule.
        if op.__class__ is ast.Add:
            return check_expr(e0, t, env) and check_expr(e1, t, env)

        # (Str-Rep) assignment rule.
        elif op.__class__ is ast.Mult:
            return any(check_expr(l, int_t, env) and check_expr(r, t, env) for
                       (l, r) in [(e0, e1), (e1, e0)])

        # (Str-Form) assignment rule.
        elif not t.is_list() and op.__class__ is ast.Mod:
            return check_expr(e0, t, env)

    # Tuple Operations.
    elif t.is_tuple():

        # (Tup-Cat) assignment rule.
        if op.__class__ is ast.Add:

            return any(check_expr(e0, t.tuple_slice(0, m), env) and
                       check_expr(e1, t.tuple_slice(m), env)
                       for m in range(t.tuple_len()))

        # (Tup-Rep) assignment rule.
        elif (op.__class__ is ast.Mult and
              any(e.__class__ is ast.Num for e in [e0,e1])):
            m = e0.n if e0.__class__ is ast.Num else e1.n
            e = e0 if e1.__class__ is ast.Num else e1

            e_len = int(t.tuple_len() / m)
            e_t = t.tuple_slice(0, e_len)

            return (type(m) is int and t.tuple_len() % m == 0 and
                    check_expr(e, e_t, env) and
                    all(e_t == t.tuple_slice(e_len*i, e_len*(i+1))
                        for i in range(1, m)))

    # No assignment rule found.
    return False