Beispiel #1
0
def try_decide_less_weak(lhs, rhs):
    """Weak decision procedure returning True, False, or None."""
    assert is_code(lhs), lhs
    assert is_code(rhs), rhs

    # Try simple cases.
    if lhs is BOT or lhs is rhs or rhs is TOP:
        return True
    if lhs is TOP and rhs is BOT:
        return False

    # Destructure JOIN.
    if is_join(lhs):
        return trool_all(try_decide_less_weak(i, rhs) for i in iter_join(lhs))
    if is_join(rhs):
        # This requires we give up at unreduced terms.
        return trool_any(try_decide_less_weak(lhs, i) for i in iter_join(rhs))

    # Destructure ABS.
    while is_abs(lhs) or is_abs(rhs):
        lhs = unabstract(lhs)
        rhs = unabstract(rhs)
    assert lhs is not rhs, lhs

    # Destructure APP.
    lhs_head, lhs_args = unapply(lhs)
    rhs_head, rhs_args = unapply(rhs)

    # Give up at unreduced terms.
    if is_abs(lhs_head) or is_abs(rhs_head):
        if len(lhs_args) == len(rhs_args):
            if try_decide_less_weak(lhs_head, rhs_head) is True:
                if all(try_decide_less_weak(i, j) is True
                       for i, j in zip(lhs_args, rhs_args)):
                    return True
        return None
    if lhs_args and not is_var(lhs_head):
        return None
    if rhs_args and not is_var(rhs_head):
        return None

    # Distinguish solvable terms.
    if is_var(lhs_head) and is_var(rhs_head):
        if lhs_head is not rhs_head or len(lhs_args) != len(rhs_args):
            return False
        return trool_all(
            try_decide_less_weak(i, j)
            for i, j in zip(lhs_args, rhs_args)
        )

    # Distinguish quoted terms.
    if is_quote(lhs_head) and is_quote(rhs_head):
        return try_decide_equal(lhs_head[1], rhs_head[1])

    # Anything else is incomparable.
    return False
Beispiel #2
0
def iter_equations(test_id, suites=None):
    assert isinstance(test_id, str), test_id
    for code, comment, message in iter_test_cases(test_id, suites):
        if is_app(code) and is_app(code[1]) and code[1][1] is EQUAL:
            lhs = code[1][2]
            rhs = code[2]
            if is_quote(lhs) and is_quote(rhs):
                lhs = link(compile_(lhs[1]))
                rhs = link(compile_(rhs[1]))
                example = lhs, rhs, message
                if comment and parse_xfail(comment, test_id):
                    example = pytest.mark.xfail(example)
                yield example
Beispiel #3
0
def abstract(body):
    """Abstract one de Bruijn variable and simplify."""
    if body is TOP:
        return body
    elif body is BOT:
        return body
    elif is_atom(body):
        return ABS(body)
    elif is_nvar(body):
        return ABS(body)
    elif is_ivar(body):
        return ABS(body)
    elif is_abs(body):
        return ABS(body)
    elif is_join(body):
        lhs = abstract(body[1])
        rhs = abstract(body[2])
        return join(lhs, rhs)
    elif is_app(body):
        if body[2] is IVAR(0) and is_const(body[1]):
            # Eta contract.
            return decrement_rank(body[1])
        else:
            return ABS(body)
    elif is_quote(body):
        return ABS(body)
    else:
        raise ValueError(body)
    raise UnreachableError(body)
Beispiel #4
0
def _try_decrement_rank(code, min_rank):
    if is_atom(code):
        return code
    elif is_nvar(code):
        return code
    elif is_ivar(code):
        rank = code[1]
        if rank < min_rank:
            return code
        elif rank == min_rank:
            raise CannotDecrementRank
        return IVAR(rank - 1)
    elif is_app(code):
        lhs = _try_decrement_rank(code[1], min_rank)
        rhs = _try_decrement_rank(code[2], min_rank)
        return APP(lhs, rhs)
    elif is_abs(code):
        return ABS(_try_decrement_rank(code[1], min_rank + 1))
    elif is_join(code):
        lhs = _try_decrement_rank(code[1], min_rank)
        rhs = _try_decrement_rank(code[2], min_rank)
        return JOIN(lhs, rhs)
    elif is_quote(code):
        return code
    else:
        raise ValueError(code)
    raise UnreachableError((code, min_rank))
Beispiel #5
0
def _compute_step(code):
    assert not is_normal(code)
    if is_app(code):
        fun = code[1]
        arg = code[2]
        if is_abs(fun):
            assert not is_linear(fun), fun
            assert not is_linear(arg), arg
            body = fun[1]
            return substitute(body, arg, 0, True)
        if is_normal(fun):
            arg = _compute_step(arg)
        else:
            fun = _compute_step(fun)
        return app(fun, arg)
    elif is_join(code):
        lhs = _compute_step(code[1])  # Relies on prioritized sorting.
        rhs = code[2]
        return join(lhs, rhs)
    elif is_abs(code):
        body = _compute_step(code[1])
        return abstract(body)
    elif is_quote(code):
        body = _compute_step(code[1])
        return QUOTE(body)
    else:
        raise ValueError(code)
    raise UnreachableError(code)
Beispiel #6
0
def dump(code, f):
    head = code
    args = []
    while is_app(head):
        args.append(head[2])
        head = head[1]
    if is_nvar(head):
        _dump_head_argc(SYMB_TO_INT[_NVAR], 1 + len(args), f)
        _dump_raw_bytes(head[1], f)
    elif is_ivar(head):
        _dump_head_argc(SYMB_TO_INT[_IVAR], 1 + len(args), f)
        _dump_raw_bytes(str(head[1]), f)
    elif is_join(head):
        args.append(head[2])
        args.append(head[1])
        _dump_head_argc(SYMB_TO_INT[_JOIN], len(args), f)
    elif is_quote(head):
        args.append(head[1])
        _dump_head_argc(SYMB_TO_INT[_QUOTE], len(args), f)
    elif is_abs(head):
        args.append(head[1])
        _dump_head_argc(SYMB_TO_INT[_ABS], len(args), f)
    elif is_fun(head):
        args.append(head[2])
        args.append(head[1])
        _dump_head_argc(SYMB_TO_INT[_FUN], len(args), f)
    else:
        try:
            head = SYMB_TO_INT[head]
        except KeyError:
            raise ValueError('Failed to serialize code: {}'.format(code))
        _dump_head_argc(head, len(args), f)
    for arg in reversed(args):
        dump(arg, f)
Beispiel #7
0
def _reduce(code, nonlinear):
    if is_app(code):
        return _app(code[1], code[2], nonlinear)
    elif is_join(code):
        return _join(code[1], code[2], nonlinear)
    elif is_quote(code):
        return QUOTE(_reduce(code[1], False))
    elif is_atom(code) or is_nvar(code):
        return code
    else:
        raise NotImplementedError(code)
Beispiel #8
0
def occurs(code, rank):
    if is_atom(code) or is_nvar(code) or is_quote(code):
        return False
    elif is_ivar(code):
        return code[1] == rank
    elif is_app(code):
        return occurs(code[1], rank) or occurs(code[2], rank)
    elif is_abs(code):
        return occurs(code[1], rank + 1)
    elif is_join(code):
        return occurs(code[1], rank) or occurs(code[2], rank)
    else:
        raise ValueError(code)
    raise UnreachableError((code, rank))
Beispiel #9
0
def substitute(body, value, rank, budget):
    """Substitute value for IVAR(rank) in body, decremeting higher IVARs.

    This is linear-eager, and will be lazy about nonlinear
    substitutions.

    """
    assert budget in (True, False), budget
    if is_atom(body):
        return body
    elif is_nvar(body):
        return body
    elif is_ivar(body):
        if body[1] == rank:
            return value
        elif body[1] > rank:
            return IVAR(body[1] - 1)
        else:
            return body
    elif is_app(body):
        lhs = body[1]
        rhs = body[2]
        linear = (is_cheap_to_copy(value) or is_const(lhs, rank) or
                  is_const(rhs, rank))
        if linear or budget:
            # Eager substitution.
            if not linear:
                budget = False
            lhs = substitute(lhs, value, rank, False)
            rhs = substitute(rhs, value, rank, False)
            return app(lhs, rhs)
        else:
            # Lazy substitution.
            return APP(ABS(body), value)
    elif is_abs(body):
        body = substitute(body[1], increment_rank(value, 0), rank + 1, budget)
        return abstract(body)
    elif is_join(body):
        lhs = substitute(body[1], value, rank, budget)
        rhs = substitute(body[2], value, rank, budget)
        return join(lhs, rhs)
    elif is_quote(body):
        return body
    else:
        raise ValueError(body)
    raise UnreachableError((body, value, rank, budget))
Beispiel #10
0
def is_normal(code):
    """Returns whether code is in linear normal form."""
    if is_atom(code) or is_nvar(code) or is_ivar(code):
        return True
    elif is_abs(code):
        return is_normal(code[1])
    elif is_app(code):
        if is_abs(code[1]):
            return False
        return is_normal(code[1]) and is_normal(code[2])
    elif is_join(code):
        return is_normal(code[1]) and is_normal(code[2])
    elif is_quote(code):
        return is_normal(code[1])
    else:
        raise ValueError(code)
    raise UnreachableError(code)
Beispiel #11
0
def try_cast_code(x):
    """Weak oracle closing x to type CODE.

    Inputs:
        x : code in linear normal form
    Returns:
        TOP, BOT, QUOTE(...), APP(QQUOTE, ...), APP(APP(QAPP, ...), ...),
        or None

    """
    assert x is not None
    if x is TOP or x is BOT or is_quote(x):
        return x
    if is_app(x):
        if x[1] is QQUOTE:
            return x
        if is_app(x[1]) and x[1][1] is QAPP:
            return x
    return None
Beispiel #12
0
def anonymize(code, var, rank):
    """Convert a nominal variable to a de Bruijn variable."""
    if code is var:
        return IVAR(rank)
    elif is_atom(code) or is_nvar(code) or is_quote(code):
        return code
    elif is_ivar(code):
        return code if code[1] < rank else IVAR(code[1] + 1)
    elif is_abs(code):
        body = anonymize(code[1], var, rank + 1)
        return abstract(body)
    elif is_app(code):
        lhs = anonymize(code[1], var, rank)
        rhs = anonymize(code[2], var, rank)
        return app(lhs, rhs)
    elif is_join(code):
        lhs = anonymize(code[1], var, rank)
        rhs = anonymize(code[2], var, rank)
        return join(lhs, rhs)
    else:
        raise ValueError(code)
Beispiel #13
0
def compile_(code):
    if is_atom(code):
        return code
    elif is_nvar(code):
        return code
    elif is_app(code):
        x = compile_(code[1])
        y = compile_(code[2])
        return APP(x, y)
    elif is_join(code):
        x = compile_(code[1])
        y = compile_(code[2])
        return JOIN(x, y)
    elif is_quote(code):
        arg = compile_(code[1])
        return QUOTE(arg)
    elif is_fun(code):
        var = code[1]
        body = compile_(code[2])
        return abstract(var, body)
    else:
        raise ValueError('Cannot compile_: {}'.format(code))
Beispiel #14
0
def _is_linear(code):
    """
    Returns:
        either None if code is nonlinear, else a pair (L, N) of frozensets,
        where L is the set of free IVARs appearing exactly once,
        and N is the set of free IVARs appearing multiply.
    """
    if is_atom(code):
        return EMPTY_SET, EMPTY_SET
    elif is_nvar(code):
        return EMPTY_SET, EMPTY_SET
    elif is_ivar(code):
        rank = code[1]
        return frozenset([rank]), EMPTY_SET
    elif is_app(code):
        lhs = _is_linear(code[1])
        rhs = _is_linear(code[2])
        if lhs is None or rhs is None:
            return None
        return lhs[0] | rhs[0], lhs[1] | rhs[1] | (lhs[0] & rhs[0])
    elif is_abs(code):
        body = _is_linear(code[1])
        if body is None or 0 in body[1]:
            return None
        return (
            frozenset(r - 1 for r in body[0] if r),
            frozenset(r - 1 for r in body[1]),
        )
    elif is_join(code):
        lhs = _is_linear(code[1])
        rhs = _is_linear(code[2])
        if lhs is None or rhs is None:
            return None
        return lhs[0] | rhs[0], lhs[1] | rhs[1]
    elif is_quote(code):
        return EMPTY_SET, EMPTY_SET
    else:
        raise ValueError(code)
    raise UnreachableError(code)
Beispiel #15
0
def substitute(var, defn, body):
    if not is_nvar(var):
        raise ValueError('Expected a nominal variable, got {}'.format(var))
    if is_atom(body):
        return body
    elif is_nvar(body):
        if body is var:
            return defn
        else:
            return body
    elif is_app(body):
        lhs = substitute(var, defn, body[1])
        rhs = substitute(var, defn, body[2])
        return APP(lhs, rhs)
    elif is_join(body):
        lhs = substitute(var, defn, body[1])
        rhs = substitute(var, defn, body[2])
        return JOIN(lhs, rhs)
    elif is_quote(body):
        arg = body[1]
        return QUOTE(substitute(var, defn, arg))
    else:
        raise ValueError(body)
Beispiel #16
0
def increment_rank(code, min_rank):
    """Increment rank of all IVARs in code."""
    if is_atom(code):
        return code
    elif is_nvar(code):
        return code
    elif is_ivar(code):
        rank = code[1]
        return IVAR(rank + 1) if rank >= min_rank else code
    elif is_abs(code):
        return ABS(increment_rank(code[1], min_rank + 1))
    elif is_app(code):
        lhs = increment_rank(code[1], min_rank)
        rhs = increment_rank(code[2], min_rank)
        return APP(lhs, rhs)
    elif is_join(code):
        lhs = increment_rank(code[1], min_rank)
        rhs = increment_rank(code[2], min_rank)
        return JOIN(lhs, rhs)
    elif is_quote(code):
        return code
    else:
        raise ValueError(code)
    raise UnreachableError((code, min_rank))
Beispiel #17
0
def approximate(code, direction):
    result = set()
    if is_atom(code) or is_var(code) or is_quote(code):
        result.add(code)
    elif is_app(code):
        if is_abs(code[1]):
            for fun_body in approximate_var(code[1][1], direction, 0):
                for lhs in approximate(abstract(fun_body), direction):
                    for rhs in approximate(code[2], direction):
                        result.add(app(lhs, rhs))
        else:
            for lhs in approximate(code[1], direction):
                for rhs in approximate(code[2], direction):
                    result.add(app(lhs, rhs))
    elif is_abs(code):
        for body in approximate(code[1], direction):
            result.add(abstract(body))
    elif is_join(code):
        for lhs in approximate(code[1], direction):
            for rhs in approximate(code[2], direction):
                result.add(join(lhs, rhs))
    else:
        raise ValueError(code)
    return tuple(sorted(result, key=complexity))
Beispiel #18
0
def _sample(head, context, nonlinear):
    """FIFO-scheduled sampler."""
    # Head reduce.
    while True:
        # LOG.debug('head = {}'.format(pretty(head)))
        PROFILE_COUNTERS[
            _sample, head[0] if isinstance(head, tuple) else head] += 1
        if is_app(head):
            context = context_push(context, head[2])
            head = head[1]
        elif is_nvar(head):
            yield head, context
            return
        elif is_join(head):
            for head in head[1:]:
                for continuation in _sample(head, context, nonlinear):
                    yield continuation
        elif is_quote(head):
            x = head[1]
            x = _reduce(x, False)
            head = QUOTE(x)
            yield head, context
            return
        elif head is TOP:
            yield TOP, EMPTY_CONTEXT
            return
        elif head is BOT:
            return
        elif head is I:
            head, context = context_pop(context)
        elif head is K:
            x, context = context_pop(context)
            y, context = context_pop(context, x)
            head = x
        elif head is B:
            x, context = context_pop(context)
            y, context = context_pop(context, x)
            z, context = context_pop(context, x, y)
            head = x
            context = context_push(context, _app(y, z, False))
        elif head is C:
            x, context = context_pop(context)
            y, context = context_pop(context, x)
            z, context = context_pop(context, x, y)
            head = x
            context = context_push(context, y)
            context = context_push(context, z)
        elif head is S:
            old_context = context
            x, context = context_pop(context)
            y, context = context_pop(context, x)
            z, context = context_pop(context, x, y)
            if nonlinear or is_nvar(z):
                head = x
                context = context_push(context, _app(y, z, False))
                context = context_push(context, z)
            else:
                yield head, old_context
                return
        elif head is EVAL:
            x, context = context_pop(context)
            x = _reduce(x, nonlinear)
            if is_quote(x):
                head = x[1]
            elif x is TOP:
                yield TOP, EMPTY_CONTEXT
                return
            elif x is BOT:
                return
            else:
                head = APP(EVAL, x)
                yield head, context
                return
        elif head is QQUOTE:
            x, context = context_pop(context)
            x = _reduce(x, nonlinear)
            if x is TOP:
                yield TOP, EMPTY_CONTEXT
                return
            elif x is BOT:
                return
            elif is_quote(x):
                head = QUOTE(x)
            else:
                head = APP(QQUOTE, x)
                yield head, context
                return
        elif head is QAPP:
            x, context = context_pop(context)
            y, context = context_pop(context, x)
            x = _reduce(x, nonlinear)
            y = _reduce(y, nonlinear)
            if is_quote(x) and is_quote(y):
                head = QUOTE(_app(x[1], y[1], False))
            else:
                head = APP(APP(QAPP, x), y)
                yield head, context
                return
        elif head in TRY_DECIDE:
            pred = head
            x, context = context_pop(context)
            y, context = context_pop(context, x)
            x = _reduce(x, nonlinear)
            y = _reduce(y, nonlinear)
            if x is TOP or y is TOP:
                yield TOP, EMPTY_CONTEXT
                return
            if x is BOT and y is BOT:
                return
            if pred is EQUAL:
                if (x is BOT and is_quote(y)) or (is_quote(x) and y is BOT):
                    return
            # FIXME This sometimes fails to err when it should; see (B3).
            # FIXME This could be stronger by nonlinearly reducing the
            #   arguments inside QUOTE(-) of the quoted terms; see (B4).
            answer = TRY_DECIDE[pred](try_unquote(x), try_unquote(y))
            head = TROOL_TO_CODE[answer]
            if head is None:
                head = APP(APP(pred, x), y)
                yield head, context
                return
        elif head in TRY_CAST:
            type_ = head
            x, context = context_pop(context)
            x = _reduce(x, nonlinear)
            while is_app(x) and x[1] is type_:
                x = x[2]
            head = TRY_CAST[type_](x)
            if head is None:
                head = APP(type_, x)
                yield head, context
                return
        else:
            raise NotImplementedError(head)
Beispiel #19
0
def app(fun, arg):
    """Apply function to argument and linearly reduce."""
    if fun is TOP:
        return fun
    elif fun is BOT:
        return fun
    elif is_nvar(fun):
        return APP(fun, arg)
    elif is_ivar(fun):
        return APP(fun, arg)
    elif is_app(fun):
        # Try to reduce strict binary functions of quoted codes.
        if fun[1] in (QAPP, LESS, EQUAL):
            lhs = fun[2]
            rhs = arg
            if lhs is TOP or rhs is TOP:
                return TOP
            elif lhs is BOT:
                if rhs is BOT or is_quote(rhs):
                    return BOT
            elif is_quote(lhs):
                if rhs is BOT:
                    return BOT
                elif is_quote(rhs):
                    if fun[1] is QAPP:
                        return QUOTE(app(lhs[1], rhs[1]))
                    if fun[1] is LESS:
                        ans = try_decide_less(lhs[1], rhs[1])
                    elif fun[1] is EQUAL:
                        ans = try_decide_equal(lhs[1], rhs[1])
                    else:
                        raise UnreachableError(fun[1])
                    if ans is True:
                        return true
                    elif ans is False:
                        return false
        return APP(fun, arg)
    elif is_abs(fun):
        body = fun[1]
        return substitute(body, arg, 0, False)
    elif is_join(fun):
        lhs = app(fun[1], arg)
        rhs = app(fun[2], arg)
        return join(lhs, rhs)
    elif is_quote(fun):
        return APP(fun, arg)
    elif fun is EVAL:
        if arg is TOP:
            return TOP
        elif arg is BOT:
            return BOT
        elif is_quote(arg):
            return arg[1]
        else:
            return APP(fun, arg)
    elif fun is QAPP:
        if arg is TOP:
            return TOP
        else:
            return APP(fun, arg)
    elif fun is QQUOTE:
        if arg is TOP:
            return TOP
        elif arg is BOT:
            return BOT
        elif is_quote(arg):
            return QUOTE(QUOTE(arg[1]))
        else:
            return APP(fun, arg)
    elif fun is LESS:
        if arg is TOP:
            return TOP
        else:
            return APP(fun, arg)
    elif fun is EQUAL:
        if arg is TOP:
            return TOP
        else:
            return APP(fun, arg)
    else:
        raise ValueError(fun)
    raise UnreachableError((fun, arg))
Beispiel #20
0
def try_unquote(code):
    return code[1] if is_quote(code) else None