Exemplo n.º 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
Exemplo n.º 2
0
def approximate_var(code, direction, rank):
    """Locally approximate wrt one variable."""
    assert is_code(code), code
    assert direction is TOP or direction is BOT, direction
    assert isinstance(rank, int) and rank >= 0, rank
    result = set()
    if not occurs(code, rank):
        result.add(code)
    elif is_ivar(code):
        assert code[1] == rank, code
        result.add(code)
        result.add(direction)
    elif is_app(code):
        for lhs in approximate_var(code[1], direction, rank):
            for rhs in approximate_var(code[2], direction, rank):
                result.add(app(lhs, rhs))
    elif is_abs(code):
        for body in approximate_var(code[1], direction, rank + 1):
            result.add(abstract(body))
    elif is_join(code):
        for lhs in approximate_var(code[1], direction, rank):
            for rhs in approximate_var(code[2], direction, rank):
                result.add(join(lhs, rhs))
    else:
        raise ValueError(code)
    return tuple(sorted(result, key=complexity))
Exemplo n.º 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)
Exemplo n.º 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))
Exemplo n.º 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)
Exemplo n.º 6
0
def _print_tiny(code, tokens):
    if code is TOP:
        tokens.append('T')
    elif code is BOT:
        tokens.append('_')
    elif is_ivar(code):
        rank = code[1]
        assert rank <= 9
        tokens.append(str(rank))
    elif is_abs(code):
        tokens.append('^')
        _print_tiny(code[1], tokens)
    elif is_app(code):
        head, args = unapply(code)
        tokens.append('(')
        _print_tiny(head, tokens)
        for arg in reversed(args):
            _print_tiny(arg, tokens)
        tokens.append(')')
    elif is_join(code):
        tokens.append('[')
        terms = list(iter_join(code))
        _print_tiny(terms[0], tokens)
        for term in terms[1:]:
            tokens.append('|')
            _print_tiny(term, tokens)
        tokens.append(']')
    else:
        raise NotImplementedError(code)
Exemplo n.º 7
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)
Exemplo n.º 8
0
def iter_join(code):
    """Destructs JOIN and BOT terms."""
    if is_join(code):
        for term in iter_join(code[1]):
            yield term
        for term in iter_join(code[2]):
            yield term
    elif code is not BOT:
        yield code
Exemplo n.º 9
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)
Exemplo n.º 10
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))
Exemplo n.º 11
0
def decide_less_normal(x, y):
    """Strong oracle for Scott ordering on normal codes.

    Inputs:
        x, y : normal codes.
    Returns:
        True, False, or None

    """
    if x is BOT or y is TOP or x is y:
        return True
    if is_join(x):
        return TROOL_AND[decide_less_normal(x[1], y),
                         decide_less_normal(x[2], y)]
    if is_join(y):
        return TROOL_OR[decide_less_normal(x, y[1]),
                        decide_less_normal(x, y[2])]
    if x is TOP or y is BOT:
        return False
    if is_app(x) and is_app(y):
        return TROOL_AND[decide_less_normal(x[1], y[1]),
                         decide_less_normal(x[2], y[2])]
    return False
Exemplo n.º 12
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))
Exemplo n.º 13
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)
Exemplo n.º 14
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)
Exemplo n.º 15
0
def try_abstract(var, body):
    """Returns \\var.body if var occurs in body, else None."""
    if not is_nvar(var):
        raise NotImplementedError('Only variables can be abstracted')
    if body is var:
        return I  # Rule I
    elif is_app(body):
        lhs = body[1]
        rhs = body[2]
        lhs_abs = try_abstract(var, lhs)
        rhs_abs = try_abstract(var, rhs)
        if lhs_abs is None:
            if rhs_abs is None:
                return None  # Rule K
            elif rhs_abs is I:
                return lhs  # Rule eta
            else:
                return APP(APP(B, lhs), rhs_abs)  # Rule B
        else:
            if rhs_abs is None:
                return APP(APP(C, lhs_abs), rhs)  # Rule C
            else:
                return APP(APP(S, lhs_abs), rhs_abs)  # Rule S
    elif is_join(body):
        lhs = body[1]
        rhs = body[2]
        lhs_abs = try_abstract(var, lhs)
        rhs_abs = try_abstract(var, rhs)
        if lhs_abs is None:
            if rhs_abs is None:
                return None  # Rule K
            else:
                return JOIN(APP(K, lhs), rhs_abs)  # Rule JOIN-K
        else:
            if rhs_abs is None:
                return JOIN(lhs_abs, APP(K, rhs))  # Rule JOIN-K
            else:
                return JOIN(lhs_abs, rhs_abs)  # Rule JOIN
    else:
        return None  # Rule K
Exemplo n.º 16
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))
Exemplo n.º 17
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)
Exemplo n.º 18
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)
Exemplo n.º 19
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))
Exemplo n.º 20
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))
Exemplo n.º 21
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)
Exemplo n.º 22
0
def is_linear(code):
    if is_app(code) or is_join(code):
        return is_linear(code[1]) and is_linear(code[2])
    return code in LINEAR_ATOMS
Exemplo n.º 23
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))