Пример #1
0
def close(code_block, env):
    """@types: list[ast.AST], LinkedList -> LinkedList"""
    for e in code_block:
        if IS(e, FunctionDef):
            c = Closure(e, nil)
            env = ext(e.name, [c], env)
        elif IS(e, ClassDef):
            c = ClassType(e.name, e.bases, e.body, nil, e)
            env = ext(e.name, [c], env)
        # here we also need Import and Assign
        # Assign is complicated
    return env
Пример #2
0
def mergeEnv(env1, env2):
    ret = nil
    for p1 in env1:
        p2 = assq(first(p1), env2)
        if p2 != None:
            ret = ext(first(p1), union([rest(p1), rest(p2)]), ret)
    return ret
Пример #3
0
def bind(target, infered_value, env):
    if IS(target, Name) or IS(target, str):
        u = infered_value
        putInfo(target, u)
        return ext(getId(target), u, env)
    elif IS(target, Attribute):
        ast_name = target.value
        infered_targets = infer(ast_name, env, nil)
        for obj in infered_targets:
            if IS(obj, (ClassType, ObjType)):
                obj.attrs[target.attr] = infered_value
            elif IS(obj, AttrType):
                obj.obj.attrs[target.attr] = infered_value
            else:
                error("Syntax error: wrong target type in assignment: ", obj, type(obj))
        return env

    elif IS(target, Tuple) or IS(target, List):
        infered_values = infered_value
        target_to_value = defaultdict(list)
        for infered_value in infered_values:
            if IS(infered_value, TupleType) or IS(infered_value, List):
                debug("infered key, value:", infered_value)
                if len(target.elts) == len(infered_value.elts):
                    for i in xrange(len(infered_value.elts)):
                        target_to_value[target.elts[i]].extend(infered_value.elts[i])
                elif len(target.elts) < len(infered_value.elts):
                    putInfo(target, ValueError("too many values to unpack"))
                else:
                    putInfo(target, ValueError("too few values to unpack"))
            else:
                putInfo(target, TypeError("non-iterable object"))
        for key, value in target_to_value.iteritems():
            debug("binding %s to %s:" % (key, value))
            env = bind(key, value, env)
        return env

    elif IS(target, ast.Subscript):
        error("Syntax error: Subscript type is not supported in assignment: ", target)
        return env
    else:
        putInfo(target, SyntaxError("not assignable"))
        return env
Пример #4
0
def invokeClosure(call, actualParams, clo, env, stk):
    """
    @types: ast.Call, list[ast.AST], Closure, LinkedList, LinkedList -> list[Type]
    """
    debug("invoking closure", clo.func, "with args", actualParams)
    debug(clo.func.body)

    func = clo.func
    fenv = clo.env
    pos = nil
    kwarg = nil

    # bind positionals first
    poslen = min(len(func.args.args), len(actualParams))
    for i in xrange(poslen):
        t = infer(actualParams[i], env, stk)
        pos = bind(func.args.args[i], t, pos)

    # put extra positionals into vararg if provided
    # report error and go on otherwise
    if len(actualParams) > len(func.args.args):
        if func.args.vararg == None:
            err = TypeError("excess arguments to function")
            putInfo(call, err)
            return [err]
        else:
            ts = []
            for i in xrange(len(func.args.args), len(actualParams)):
                t = infer(actualParams[i], env, stk)
                ts = ts + t
            pos = bind(func.args.vararg, ts, pos)

    # bind keywords, collect kwarg
    ids = map(getId, func.args.args)
    for k in call.keywords:
        ts = infer(k.value, env, stk)
        tloc1 = lookup(k.arg, pos)
        if tloc1 != None:
            putInfo(call, TypeError("multiple values for keyword argument", k.arg, tloc1))
        elif k.arg not in ids:
            kwarg = bind(k.arg, ts, kwarg)
        else:
            pos = bind(k.arg, ts, pos)

    # put extras in kwarg or report them
    # bind call.keywords to func.args.kwarg
    if kwarg != nil:
        if func.args.kwarg != None:
            pos = bind(func.args.kwarg, [DictType(reverse(kwarg))], pos)
        else:
            putInfo(call, TypeError("unexpected keyword arguements", kwarg))
    elif func.args.kwarg != None:
        pos = bind(func.args.kwarg, [DictType(nil)], pos)

    # bind defaults, avoid overwriting bound vars
    # types for defaults are already inferred when the function was defined
    i = len(func.args.args) - len(func.args.defaults)
    _ = len(func.args.args)
    for j in xrange(len(clo.defaults)):
        tloc = lookup(getId(func.args.args[i]), pos)
        if tloc == None:
            pos = bind(func.args.args[i], clo.defaults[j], pos)
            i += 1

    # finish building the input type
    fromtype = maplist(lambda p: SimplePair(first(p), typeOnly(rest(p))), pos)

    # check whether the same call site is on stack with same input types
    # if so, we are back to a loop, terminate
    if onStack(call, fromtype, stk):
        return [bottomType]

    # push the call site onto the stack and analyze the function body
    stk = ext(call, fromtype, stk)
    fenv = append(pos, fenv)
    to = infer(func.body, fenv, stk)

    # record the function type
    putInfo(func, FuncType(reverse(fromtype), to))
    return to
Пример #5
0
def invoke1(call, clo, env, stk):
    """@types: ast.Call, Callable, LinkedList, LinkedList -> ast.AST or Type
    """
    if clo == bottomType:
        return [bottomType]

    # Even if operator is not a closure, resolve the
    # arguments for partial information.
    if not IS(clo, (Closure, ClassType, AttrType)):
        # infer arguments even if it is not callable
        # (we don't know which method it is)
        debug("Unknown function or method, infering arguments", call.args)
        for a in call.args:
            infer(a, env, stk)
        for k in call.keywords:
            infer(k.value, env, stk)
        err = TypeError("calling non-callable", clo)
        putInfo(call, err)
        return [err]
    if IS(clo, ClassType):
        debug("creating instance of", clo)
        ctorargs = [infer(arg, env, stk) for arg in call.args]
        new_obj = ObjType(clo, ctorargs, clo.env, call)
        init_closures = new_obj.attrs.get("__init__", [])
        if len(init_closures):
            # we don't really care about this name,
            # we just don't want to collide with method's global symbols
            # TODO: generate a special name,
            #       that would represent a temporary object
            self_arg = get_self_arg_name(init_closures[0].func)
            ref_to_init = AttrType(init_closures, new_obj, self_arg)
            init_env = ext(self_arg.id, [new_obj], env)
            invoke1(call, ref_to_init, init_env, stk)
        return [new_obj]
    if IS(clo, AttrType):
        attr = clo
        if IS(attr.obj, ObjType):
            # add self to function call args
            actualParams = list(call.args)
            classtype = attr.obj.classtype
            saveMethodInvocationInfo(call, attr, env, stk)
            # TODO: @staticmethod, @classmethod
            if classtype.name != "module":
                actualParams.insert(0, attr.objT)
            types = []
            for closure in attr.clo:
                if IS(closure, Closure):
                    # Create new env for method
                    # On each call ClassType.env might be different
                    new_closure = Closure(closure.func, classtype.env)
                    debug("invoking method", closure.func.name, "with args", call.args)
                    types.extend(invokeClosure(call, actualParams, new_closure, env, stk))
                elif IS(closure, ClassType):
                    types.extend(invoke1(call, closure, env, stk))
                else:
                    types.append(TypeError("Callable type %s is not supported" % closure))
            return types
        elif IS(attr.obj, DictType):
            types = []
            for closure in attr.clo:
                if closure in attr.obj.iter_operations:
                    types.extend(closure(attr.obj.dict))
                else:
                    # we take the first version of infered arguments
                    # but ideally all must be processed
                    infered_args = [infer(arg, env, stk) for arg in call.args]
                    types.extend(closure(attr.obj.dict, *infered_args))
            return types
        else:
            err = TypeError("AttrType object is not supported for the invoke", attr)
            putInfo(call, err)
            return [err]
    return invokeClosure(call, call.args, clo, env, stk)
Пример #6
0
def infer(exp, env, stk):
    "@types: ast.AST|object, LinkedList, LinkedList -> list[Type]"
    debug("infering", exp, exp.__class__)
    assert exp is not None

    if IS(exp, Module):
        return infer(exp.body, env, stk)

    elif IS(exp, list):
        env = close(exp, env)
        (t, _) = inferSeq(exp, env, stk)  # env ignored (out of scope)
        return t

    elif IS(exp, Num):
        # we need objects, not types
        return [exp]  # [PrimType(type(exp.n))]

    elif IS(exp, Str):
        # we need objects, not types
        return [exp]  # [PrimType(type(exp.s))]

    elif IS(exp, Name):
        b = lookup(exp.id, env)
        debug("infering name:", b, env)
        if b != None:
            putInfo(exp, b)
            return b
        else:
            try:
                t = eval(exp.id)  # try use information from Python interpreter
                return [PrimType(t)]
            except NameError as _:
                putInfo(exp, UnknownType(exp))
                return [UnknownType(exp)]

    elif IS(exp, Lambda):
        c = Closure(exp, env)
        for d in exp.args.defaults:
            dt = infer(d, env, stk)
            c.defaults.append(dt)
        return [c]

    elif IS(exp, Call):
        return invoke(exp, env, stk)

    elif IS(exp, Attribute):
        t = infer(exp.value, env, stk)
        if t:
            attribs = []
            # find attr name in object and return it
            for o in t:
                if not IS(o, (ObjType, ClassType, DictType)):
                    attribs.append(TypeError("unknown object", o))
                    continue
                if exp.attr in o.attrs:
                    attribs.append(AttrType(o.attrs[exp.attr], o, exp.value))
                else:
                    attribs.append(TypeError("no such attribute", exp.attr))
            return attribs
        else:
            return [UnknownType(exp)]

    ## ignore complex types for now
    # elif IS(exp, List):
    #     eltTypes = []
    #     for e in exp.elts:
    #         t = infer(e, env, stk)
    #         eltTypes.append(t)
    #     return [Bind(ListType(eltTypes), exp)]

    # elif IS(exp, Tuple):
    #     eltTypes = []
    #     for e in exp.elts:
    #         t = infer(e, env, stk)
    #         eltTypes.append(t)
    #     return [Bind(TupleType(eltTypes), exp)]

    elif IS(exp, ObjType):
        return exp

    elif IS(exp, ast.List):
        infered_elts = flatten([infer(el, env, stk) for el in exp.elts])
        return [ListType(tuple(infered_elts))]

    elif IS(exp, ast.Dict):
        infered_keys = [infer(key, env, stk) for key in exp.keys]
        infered_values = [infer(value, env, stk) for value in exp.values]
        temp_dict = defaultdict(list)
        dic = nil
        for keys, value in zip(infered_keys, infered_values):
            for key in keys:
                try:
                    temp_dict[key] = value  # only the last value
                    # with the same key is stored
                except TypeError, _:  # unhashable instance
                    dic = ext(key, value, dic)

        for key, value in temp_dict.iteritems():
            dic = ext(key, value, dic)
        return [DictType(dic)]