from .....common import T_FREE
from ..... import inspect
from ... import graph
from ...eval import fairscheme
__all__ = [
'apply', 'apply_gnf', 'apply_hnf', 'apply_nf', 'cond', 'ensureNotFree'
]
[docs]
def apply(rts, _0):
# _0 (_Partapplic #missing term) arg
#
# The term is a function symbol followed zero or more arguments.
# No forward nodes or set guards may appear around the term.
partapplic = rts.variable(_0, 0)
partapplic.hnf()
missing, term = partapplic.target.successors
assert inspect.isa_unboxed_int(missing)
assert inspect.isa_func(term) or inspect.isa_ctor(term)
arg = _0.target.successors[1]
assert missing >= 1
# A partial application reached through set guards (a function value that
# is an argument of a set function, or that such an argument holds) keeps
# its arguments boxed: each goes under the guards crossed on the way to it
# (the box rule of the dissertation, chapter 4: a reference to a boxed
# expression is boxed). The application itself is not boxed, and neither
# is ``arg``, a successor of the redex. The C++ runtime has the same step
# (apply_step).
held = [rts.guard_held(t, partapplic.guards) for t in term.successors]
if missing == 1:
yield term.info
for t in held:
yield t
yield arg
else:
yield partapplic.info
yield missing-1
yield graph.Node(term, *(held+[arg]), partial=True)
[docs]
def apply_gnf(rts, _0):
'''
Implements ($##). The argument is normalized as by ($!!). When the
normalization completed, the step suspends on the free variables of the
normal form that have no generator, as the C++ runtime does
(applygnf_step in currylib/prelude/apply.cpp). A variable with a binding
is gone from the normal form, because N put the binding into the
expression; a variable with a generator carries information. So a
configuration suspended here waits for a variable that another one can
still bind, and ready() releases it then (show x after x =:= (3 ? 4)).
The check runs over the normal form and enters no generator. A check
over the argument before its normalization walked the generators of the
narrowed variables as well, where the fresh variables of the branches
this configuration did not take have no information and never get any,
so the step suspended for good on them: scenario (d) of example 21 on
the Python backend.
'''
replacement = list(_applyspecial(rts, _0, _normalize)) # Apply ($!!).
unbound = [
node for node in _nodes_outside_generators(replacement[2].target)
if inspect.isa_freevar(node) and not rts.has_generator(node)
]
if unbound:
rts.suspend(unbound)
else:
return replacement
def _nodes_outside_generators(root):
'''
The nodes of an expression, each once, without the generators of its free
variables. The successors of a free variable are its id and its
generator; the walk reads neither.
'''
stack = [root]
seen = set()
while stack:
node = stack.pop()
if not isinstance(node, graph.Node) or id(node) in seen:
continue
seen.add(id(node))
yield node
if node.info.tag != T_FREE:
stack.extend(node.successors)
[docs]
def apply_hnf(rts, _0):
'''
Implements ($!). A free variable is a head normal form: the function is
applied to the variable, as PAKCS does (id $! x gives x), where hnf alone
suspended on it (issue #112). The C++ runtime does the same
(applyhnf_step in currylib/prelude/apply.cpp).
'''
return _applyspecial(rts, _0, fairscheme.hnf_or_free)
[docs]
def apply_nf(rts, _0):
'''Implements ($!!).'''
return _applyspecial(rts, _0, _normalize)
[docs]
def cond(rts, _0):
Bool = rts.type('Prelude.Bool')
_1 = rts.variable(_0, 0)
_1.hnf(typedef=Bool)
if _1.info.tag:
yield rts.Fwd
yield _0.successors[1]
else:
yield rts.Failure
[docs]
def ensureNotFree(rts, _0):
_1 = fairscheme.hnf_or_free(rts, rts.variable(_0, 0))
if rts.is_void(_1.target):
rts.suspend(_1.target)
else:
yield rts.Fwd
yield _1
def _applyspecial(rts, _0, action, **kwds):
'''Apply a function with a special action on the argument.'''
partapplic = rts.variable(_0, 0)
partapplic.hnf()
term = partapplic.successors[1]
assert inspect.isa_func(term) # not a forward node or set guard
with rts.catch_control(nondet=rts.is_io(term)):
_1 = rts.variable(_0, 1)
transformed_arg = action(rts, _1, **kwds)
# The function and the argument keep the guards crossed on the way to
# them (rvalue), so the box rule reaches the application (apply).
yield rts.prelude.apply
yield partapplic.rvalue
yield transformed_arg
def _normalize(rts, var, **kwds):
if not fairscheme.N(rts, var, **kwds):
rts.unwind()
else:
return var