Source code for curry.expressions

'''
Building Curry expressions from Python.

``expr`` is the typed builder: it types the expression over the schemes of
the front end, converts Python values by the expected type, and supplies
the class dictionaries (see :mod:`curry.typecheck.builder`).  ``raw_expr``
is the untyped builder, which converts by the Python type alone and checks
only the arity; it serves the runtime and the tests of the runtime.  The
markers of this module (:class:`anchor`, :class:`choice`, :class:`cons`,
:data:`fail`, :class:`free`, :data:`nil`, :class:`ref`, :class:`typed`,
:class:`unboxed`) describe the parts of an expression that are no Python
value.
'''

from . import backends, config, icurry, objects, utility
from .common import T_FUNC
from .exceptions import CurryTypeError
from .utility import strings, visitation
import collections.abc, itertools, numbers, weakref

__all__ = [
    'anchor', 'choice', 'cons', 'describe', 'expr', 'fail', 'fwd', 'free'
  , 'nil', 'raw_expr', 'ref', 'result_type', 'typed', 'typeof', 'unboxed'
  , 'untyped_expr'
  ]

[docs] class anchor(object): ''' Used with :func:`expr` to create an anchor in an expression. This can be used to construct nonlinear expressions. When no argument is supplied, the anchor name is automatically generated from the sequence _1, _2, ..., according to its position in a left-to-right, depth-first traversal. ''' def __init__(self, value, name=None): self.value = value self.name = name
[docs] class ref(object): ''' Used with :func:`expr` to create a reference to a named subexpression. This can be used to construct nonlinear expressions. The argument is optional when the expression contains exactly one anchor at the point where the reference appears. ''' def __init__(self, name=None): self.name = name
[docs] class cons(object): ''' Used with :func:`expr` to place one or more list constructors into a Curry expression. ''' def __init__(self, head, tail0, *tail): self.head = head if tail: self.tail = cons(tail0, *tail) else: self.tail = tail0
class _nilcls(object): pass nil = _nilcls() ''' Used with :func:`expr` to place a list terminator into a Curry expression. ''' del _nilcls
[docs] class unboxed(object): ''' Used with :func:`expr` to place the unboxed payload of a primitive into a Curry expression: ``[Prelude.Int, curry.unboxed(3)]`` is the node ``<Int 3>``, and ``Prelude.Char`` and ``Prelude.Float`` take the marker the same way. A plain Python value builds the same node, so the marker is seldom needed. The payload must fit the primitive: a Python ``int`` under ``Int``, a ``str`` of length one under ``Char``, a ``float`` under ``Float``; another value is a ``CurryTypeError`` from both builders (:func:`unboxed_payload`). Anywhere else the marker is a ``CurryTypeError`` at construction, from both builders: alone, in a list or a tuple, or as the argument of another symbol, a node is expected, and an unboxed value in its place is an ill-formed node, which ended the process on the C++ backend (issue #107). An item of an iterator is converted when the list is demanded, so the marker there is an ``EvaluationError`` at that time. ''' def __init__(self, value): if not isinstance(value, icurry.IUnboxedLiteral): raise CurryTypeError('expected an unboxed literal, got %r' % value) self.value = value def __repr__(self): return 'curry.unboxed(%r)' % (self.value,)
# The primitive of a payload: its name, the Python type the payload must # have, and the example of the messages. _PRIMITIVES = { 'Int': (int, 'a Python int', 3) , 'Char': (str, 'a str of length one', 'a') , 'Float': (float, 'a Python float', 2.5) } def primitive_name(info): '''The type name of a primitive info table: Int, Char or Float.''' return 'Int' if info.is_int else 'Char' if info.is_char else 'Float' def unboxed_fits(primitive, value): ''' Whether ``value`` is the payload of the primitive named ``primitive``: a Python ``int`` under ``Int`` (a ``bool`` is not one), a ``str`` of length one under ``Char``, a ``float`` under ``Float``. Another value makes a node whose bits mean another value on the C++ backend, and a node the Python backend cannot evaluate. ''' pytype = _PRIMITIVES[primitive][0] if not isinstance(value, pytype) or isinstance(value, bool): return False return primitive != 'Char' or len(value) == 1 def unboxed_payload(info, value, where=None): ''' The payload of ``curry.unboxed`` under the primitive ``info``, checked: the value itself, or a ``CurryTypeError`` when it does not fit (:func:`unboxed_fits`). ``where`` is the text of the position in the typed builder, or None in the untyped builder. ''' primitive = primitive_name(info) if not unboxed_fits(primitive, value): raise CurryTypeError(unboxed_message(value, where, primitive)) return value def unboxed_message(value, where=None, primitive=None): ''' The message of an :class:`unboxed` marker outside the payload of a primitive, or, with ``primitive`` given, of a payload that does not fit that primitive. ``where`` is the text of the position in the typed builder (``'the expression'`` for the marker alone), or None in the untyped builder, which does not know the position. ''' if primitive is not None: _, pytype, example = _PRIMITIVES[primitive] place = '' if where is None else ' at ' + where return ( 'curry.unboxed(%r) is not the payload of %s %s%s; the payload of ' '%s %s is %s, as in [Prelude.%s, curry.unboxed(%r)]' % ( value, _article(primitive), primitive, place , _article(primitive), primitive, pytype, primitive, example ) ) if isinstance(value, str): primitive = 'Char' elif isinstance(value, float): primitive = 'Float' else: primitive = 'Int' if where is None: place = 'outside a primitive' elif where == 'the expression': place = 'alone' else: place = 'at ' + where return ( 'curry.unboxed(%r) stands %s; the marker is the payload of an Int, ' 'Char or Float, as in [Prelude.%s, curry.unboxed(%r)]' % (value, place, primitive, value) ) def _article(primitive): return 'an' if primitive == 'Int' else 'a' class _setgrd(object): '''Used with :func:`expr` to place a set guard into a Curry expression.''' def __init__(self, sid, value=None): if value is None: sid, value = 0, sid # shift right self.sid = int(sid) self.value = value class _failcls(object): pass fail = _failcls() '''Used with :func:`expr` to place a failure into a Curry expression.''' del _failcls class _strictconstr(object): '''Used with :func:`expr` to place a strict constraint into a Curry expression.''' def __init__(self, value, pair): self.value = value self.pair = pair class _nonstrictconstr(object): '''Used with :func:`expr` to place a nonstrict constraint into a Curry expression.''' def __init__(self, value, pair): self.value = value self.pair = pair class _valuebinding(object): '''Used with :func:`expr` to place a value binding into a Curry expression.''' def __init__(self, value, pair): self.value = value self.pair = pair
[docs] class free(object): ''' Used with :func:`expr` to place a free variable into a Curry expression. Through ``expr``, one marker is one variable. The builder makes one node for the marker, a call of ``Prelude.unknown`` applied to the ``Data`` dictionary of its type, and reuses it for every occurrence of the marker, in that expression and in later ones. The first rewrite step on the node creates the variable with a fresh id and forwards the node to it. The node belongs to one interpreter state: ``curry.reset`` installs a new state, and the marker becomes a new variable there. ``exprtype`` fixes the type of the variable, in Curry syntax; without it the type comes from the context, and a variable whose type stays polymorphic gets the dictionary of ``Bool``, which ``unknown`` never reads. Through ``raw_expr``, every occurrence is a separate ``Free`` node with the id ``vid``, 0 by default; that form serves the tests of the runtime. ''' def __init__(self, vid=0, exprtype=None): self.vid = int(vid) self.exprtype = exprtype # The shared node and a weak reference to the interpreter state it was # built under. See ExpressionBuilder.unknown. self._node = None self._istate = None def _shared(self, interp): ''' The shared node of this marker under the current state of ``interp``, or None. The key is the state, not the interpreter: ``Interpreter.reset`` installs a new state with a new id factory, so a node built under the old state would share an id with the variables of the new one. ''' istate = interp.backend.get_interpreter_state(interp) if self._istate is not None and self._istate() is istate: return self._node def _share(self, interp, node): istate = interp.backend.get_interpreter_state(interp) self._istate = weakref.ref(istate) self._node = node
[docs] class fwd(object): '''Used with :func:`expr` to place a forward node into a Curry expression.''' def __init__(self, value): self.value = value
[docs] class choice(object): ''' Used with :func:`expr` to place a choice into a Curry expression. Through ``expr``, the choice is a call of ``Prelude.?``, whose rewrite step draws a fresh id from the interpreter; ``cid`` is not used. Through ``raw_expr``, it is a ``Choice`` node with the id ``cid``, 0 by default. ''' def __init__(self, cid, lhs, rhs=None): if rhs is None: cid, lhs, rhs = 0, cid, lhs # shift right self.cid = int(cid) self.lhs = lhs self.rhs = rhs
[docs] class typed(object): ''' Used with :func:`expr` to annotate a part of an expression with its type: ``curry.typed(value, 'T')`` converts ``value`` at the type ``T``, in Curry syntax, and unifies ``T`` with the type the context expects. So ``curry.typed('a', 'String')`` is the string ``"a"``, and ``curry.typed(node, '[Int]')`` states the type of a large value instead of a walk of its content. ``raw_expr`` builds the value and ignores the annotation. ''' def __init__(self, value, exprtype): self.value = value self.exprtype = exprtype
[docs] @utility.formatDocstring(config.python_package_name()) def expr(interp, *args, **kwds): ''' Builds a Curry expression. The expression is typed before it is built (see :mod:`{0}.typecheck.builder`): the type schemes of the symbols are read from the FlatCurry interfaces of their modules, the arguments are unified with the parameter types, the class constraints are defaulted with the table of the PAKCS REPL, and the class dictionaries are supplied. So ``expr(curry.symbol('Prelude.+'), 1, 2)`` builds the call of the method at ``Int``, and ``expr(P.show, [P.Just, 1])`` finds the ``Show`` instance of ``Maybe Int``. A typing failure raises :class:`CurryTypeError <{0}.exceptions.CurryTypeError>` at construction; nothing is evaluated. The flag ``typed_expr`` turns the typing off. The arguments specify the expression to build. Each positional argument must be directly convertible to Curry or describe a node. A Python value converts by the type its position expects: * ``bool``: Converted to ``Prelude.Bool``. * ``int`` ``Prelude.Int``; ``Prelude.Float`` under ``Float``; the ``fromInt`` conversion under another ``Num`` instance. * ``float`` Converted to ``Prelude.Float``. * ``str`` Under ``[Char]`` one string of any length. Elsewhere a string of length one is a ``Prelude.Char``, and another string is ``[Char]``. * ``list`` Converted to a Curry list, in a loop. Every element takes the element type, so ``[1, 2.5]`` is ``[Float]``. * ``tuple`` Converted to a Curry tuple. * ``iterator`` Lazily converted to a Curry list. The element type is fixed when the expression is built; an item that does not convert ends the evaluation with an ``EvaluationError``. * ``None`` An error that names the expected type. * a Curry node Typed by a walk of its content, up to 100000 nodes; ``curry.typed(node, 'T')`` states the type instead. A node alone passes through untouched. Any (possibly nested) sequence whose first element is an instance of :class:`NodeInfo <{0}.objects.CurryNodeInfo>` specifies a node. The remaining arguments are recursively converted to Curry expressions to form the successors list. Thus, given suitable definitions, it is possible to build the Curry list ``[0,1,2]`` with the following code: expr([Cons, 0, [Cons, 1, [Cons, 2, Nil]]]) Fewer arguments than the symbol takes give a partial application; more arguments go through ``Prelude.apply`` when the result is a function. A symbol without a type scheme (a module without a FlatCurry interface, or a built-in of Sprite's own Prelude) is built untyped when it stands alone, as the goal of a saved module does, and is an error when it is applied to arguments; :func:`typeof` refuses it either way. Several special symbols are provided. See :class:`anchor`, :class:`choice`, :class:`cons`, :data:`fail`, :class:`free`, :data:`nil`, :class:`ref`, :class:`typed`, and :class:`unboxed`. A :class:`free` marker becomes a call of ``Prelude.unknown``, one node per marker, and a :class:`choice` marker a call of ``Prelude.?``, so the runtime assigns the ids. ``raw_expr`` builds the raw ``Free`` and ``Choice`` nodes instead. An :class:`unboxed` marker stands only as the payload of a primitive, ``[Prelude.Int, curry.unboxed(3)]``; anywhere else it is a ``CurryTypeError``. Args: interp: An interpreter object. *args: Positional arguments used to construct expressions. **kwds: Keyword arguments specifying subexpressions that may be referenced via ``ref``. Each keyword specifies the name of an anchor. See the examples below. exprtype: Keyword-only argument. The type of the expression in Curry syntax, for example ``'Maybe Float'``. Its type variables are rigid. target: Reserved keyword-only argument. If a target is supplied, then it will be rewritten with the specified expression. Otherwise a new node is created. Returns: A Curry expression. Its type is recorded, so :func:`typeof` answers for it, and ``eval`` converts its values by it. ''' exprtype = kwds.pop('exprtype', None) if interp.flags.get('typed_expr', True): from .typecheck import builder target = kwds.pop('target', None) return builder.build(interp, args, kwds, exprtype=exprtype, target=target) return _build(interp, args, kwds, raw=False)
[docs] def raw_expr(interp, *args, **kwds): ''' The untyped builder. Equivalent to :func:`expr` without the types: every Python value converts by its Python type, only the arity of a symbol is checked, no dictionary is supplied, a :class:`free` marker becomes a raw ``Free`` node with the id of the marker, a new node per occurrence, and a :class:`choice` marker a raw ``Choice`` node with the id of the marker. Fewer arguments than the arity of a symbol give a partial application; a function applied to more goes through ``Prelude.apply``, one node per surplus argument, as a point-free definition needs; a constructor applied to more is an error. An expression that is not well typed evaluates with undefined behaviour. ''' return _build(interp, args, kwds, raw=True)
[docs] def untyped_expr(interp, *args, **kwds): ''' The untyped builder with the markers of :func:`expr`: a :class:`free` marker is its shared ``unknown`` node and a :class:`choice` marker a call of ``Prelude.?``. This is :func:`expr` with the flag ``typed_expr`` off. The interpreter uses it for the expressions it builds around compiled code, whose types the front end checked. ''' kwds.pop('exprtype', None) return _build(interp, args, kwds, raw=False)
[docs] def typeof(interp, e, defaulted=False): ''' The type of an expression in Curry syntax. Without ``defaulted`` the undefaulted scheme, what ``:type`` prints: ``typeof(expr(plus, 1, 2))`` is ``Num a => a``. With ``defaulted`` the type after the table of the PAKCS REPL: ``Int``. ``e`` is a node :func:`expr` returned, any other Curry node (typed by its content), a symbol, a description of :func:`describe`, or any argument :func:`expr` accepts. ''' from .typecheck import builder return builder.typeof(interp, e, defaulted)
[docs] def describe(interp, *args, **kwds): ''' The description of an expression: the arguments of :func:`expr` as a tree, typed only when a consumer asks for its node. The DSL builds descriptions at every operator and types them once at the boundary, so that an outer context fixes the types of the inner parts. A description is an argument of :func:`expr` and of ``eval``; ``str`` prints it in Curry syntax; its method ``typeof`` gives its type. The keyword ``exprtype`` states the type of the description; the other keywords are its anchors. See :class:`Description <curry.typecheck.builder.Description>`. ''' from .typecheck import builder exprtype = kwds.pop('exprtype', None) if 'target' in kwds: raise TypeError('describe() takes no target; pass it to expr or to build') return builder.Description(interp, args, kwds, exprtype=exprtype)
[docs] def result_type(interp, node): '''The defaulted type :func:`expr` recorded for a node, or None.''' from .typecheck import builder return builder.result_type(interp, node)
def _build(interp, args, kwds, raw): builder = ExpressionBuilder(interp, raw=raw) target = kwds.pop('target', None) for anchorname, subexpr in kwds.items(): subexpr = anchor(subexpr, name=anchorname) builder(subexpr) assert anchorname in builder.anchors builder.target = target expr = builder(*args) return builder.fixrefs(expr) def fix_references(expr, brokenrefs, anchors): ''' Replaces the placeholders of the references that were built before their anchors. ``brokenrefs`` maps the id of a placeholder to the key of its anchor in ``anchors``. The walk enters every node once and goes on at the anchor after a replacement. A replacement closes a cycle through the anchor, and a second reference to the anchor led the walk into the cycle again, without end (issue #122). A node reached only through a replaced slot was never entered, so a placeholder below it stayed. ''' if brokenrefs: from .backends.py.graph.walkexpr import walk entered = set() for state in walk(expr): cursor = state.cursor if isinstance(cursor, backends.Node): key = brokenrefs.get(cursor.id()) if key is not None: node = _anchor_node(key, brokenrefs, anchors) parent = state.parent if parent is None: # The root is a placeholder. The trivial cycle a=a forwards it # to itself. cursor.forward_to(node) else: parent.set_successor(state.realpath[-1], node) state.spine[-1] = cursor = node if cursor.id() not in entered: entered.add(cursor.id()) state.push() return expr def _anchor_node(key, brokenrefs, anchors): ''' The node of the anchor ``key``. An anchor whose node is a placeholder is an alias of another anchor, and the chain of aliases is followed. A chain that returns to itself is the trivial cycle a=a: its placeholder is forwarded to itself. ''' node = anchors[key] seen = set() while node.id() in brokenrefs and node.id() not in seen: seen.add(node.id()) node = anchors[brokenrefs[node.id()]] if node.id() in seen: node.forward_to(node) return node # The dictionary argument of Prelude.unknown. The body of unknown declares a # free variable and returns it; it never reads the dictionary. The untyped # builder does not know the type of a marker, so the dictionary of Bool # stands in. UNKNOWN_DICTIONARY = 'Prelude._inst#Prelude.Data#Prelude.Bool' class ExpressionBuilder(object): ''' Implementation of ``raw_expr``, and of ``expr`` with the flag ``typed_expr`` off. With ``raw`` set, a free marker becomes a raw ``Free`` node and a choice marker a raw ``Choice`` node, as the tests of the runtime expect. Otherwise the markers become calls of ``Prelude.unknown`` and ``Prelude.?``, so that the runtime assigns the ids (see :class:`free` and :class:`choice`). A :class:`typed` marker builds its value; the annotation is ignored. ''' def __init__(self, interp, raw=False): self.interp = interp self.raw = raw self.counter = itertools.count(1) self.anchors = {} self.brokenrefs = {} self.target = None self._mknode = interp.backend.make_node self.prelude = self.interp.prelude self.fsyms = self.interp.backend.fundamental_symbols def fixrefs(self, expr): return fix_references(expr, self.brokenrefs, self.anchors) @visitation.dispatch.on('arg') def __call__(self, arg, *args, **kwds): if hasattr(arg, 'rvalue'): # handle Variable return self(arg.rvalue, *args, **kwds) raise TypeError( 'cannot build a Curry expression from type %r' % type(arg).__name__ ) @__call__.when((str, bytes)) # Char or [Char]. def __call__(self, arg, *trailing): arg = strings.ensure_str(arg) if trailing: raise CurryTypeError('invalid arguments after %r' % arg) if len(arg) == 1: return self._mknode(self.prelude.Char, str(arg), target=self.target) else: return self(list(arg)) @__call__.when(list) def __call__(self, lst, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % lst) if len(lst) and isinstance(lst[0], (objects.CurryNodeInfo, backends.InfoTable)): return self(*lst) else: Cons = self.prelude.Cons Nil = self.prelude.Nil sentinel = object() seq = iter(lst) f = lambda x,g: [Cons, self(x), g()] if x is not sentinel else Nil g = lambda: f(next(seq, sentinel), g) return self(g()) @__call__.when(tuple) def __call__(self, tup, *trailing): if trailing: raise CurryTypeError('invalid arguments after %s' % repr(tup)) n = len(tup) if n == 0: typename = 'Prelude.()' elif n == 1: raise CurryTypeError("Curry has no 1-tuple.") else: typename = 'Prelude.(%s)' % (','*(n-1)) return self._mknode( self.interp.symbol(typename), *map(self, tup), target=self.target ) @__call__.when(bool) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % arg) if arg: return self._mknode(self.prelude.True_, target=self.target) else: return self._mknode(self.prelude.False_, target=self.target) @__call__.when(numbers.Integral) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % arg) return self._mknode(self.prelude.Int, int(arg), target=self.target) @__call__.when(numbers.Real) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % arg) return self._mknode( self.prelude.Float, float(arg), target=self.target ) @__call__.when(anchor) def __call__(self, arg, *trailing): if arg.name is None: while True: anchorname = '_%s' % next(self.counter) if anchorname not in self.anchors: break else: anchorname = arg.name if trailing: raise CurryTypeError('invalid arguments after anchor %r' % anchorname) if anchorname in self.anchors: raise ValueError('multiple definitions of anchor %r' % anchorname) self.anchors[anchorname] = None subexpr = self(arg.value) self.anchors[anchorname] = subexpr return subexpr @__call__.when(ref) def __call__(self, arg, *trailing): if arg.name is None: if len(self.anchors) != 1: raise ValueError( "invalid use of unqualified 'ref' with %s anchors defined" % len(self.anchors) ) anchorname = next(iter(self.anchors)) else: anchorname = arg.name if trailing: raise CurryTypeError('invalid arguments after ref %r' % anchorname) target = self.anchors.get(anchorname) if target is None: # The placeholder must be a fresh node that can be forwarded: fixrefs # forwards it to itself for the trivial cycle a=a. A failure will not # do, because on the C++ backend failures are one shared node that is # too small to forward. placeholder = self(fwd(fail)) self.brokenrefs[placeholder.id()] = anchorname return placeholder else: return target @__call__.when(collections.abc.Iterator) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % arg) pygen = self.prelude._biGenerator arg = (self(x) for x in arg) return self._mknode(pygen, arg, target=self.target) @__call__.when((objects.CurryNodeInfo, backends.InfoTable)) def __call__(self, ti, *args): info = getattr(ti, 'info', ti) arity = info.arity if info.is_primitive and len(args) == 1 and isinstance(args[0], unboxed): # A boxed literal over its unboxed payload, [Int, unboxed(3)]: the one # place of the marker. The payload must fit the primitive. See the # handler of unboxed below. payload = unboxed_payload(info, args[0].value) return self._mknode(ti, payload, target=self.target) if len(args) <= arity: partial_info = self.fsyms.PartApplic if len(args) < arity else None return self._mknode( ti, *map(lambda s: self(s), args), target=self.target , partial_info=partial_info ) # More arguments than the arity. A function may return a function, as a # point-free definition does, so the surplus goes through Prelude.apply, # one node per argument, as the typed builder does. A constructor # returns data: the application is an error here, not at the node. if info.tag != T_FUNC: # The sentence of the typed builder (typecheck.errors.ArityError) # without the scheme: this builder knows the arity alone. raise CurryTypeError( '%s takes %d argument%s, %d given' % (info.name, arity, '' if arity == 1 else 's', len(args)) ) head, rest = args[:arity], args[arity:] apply_ = self.prelude.apply node = self._mknode(ti, *map(lambda s: self(s), head)) for i, extra in enumerate(rest): node = self._mknode( apply_, node, self(extra) , target=self.target if i == len(rest) - 1 else None ) return node @__call__.when(backends.Node) def __call__(self, node, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r node' % node.info.name) if self.target is not None: return self._mknode(self.fsyms.Fwd, node, target=self.target) return node @__call__.when(unboxed) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after unboxed %r' % arg.value) # The symbol handler takes the marker under a primitive before it gets # here, so this marker stands alone, in a list or a tuple, or as the # argument of another symbol: a node is expected there, and the raw # value in its place is an ill-formed node, which the C++ backend # dereferenced at construction (issue #107). raise CurryTypeError(unboxed_message(arg.value)) @__call__.when(typed) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % 'typed') return self(arg.value) @__call__.when(cons) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % 'cons') return self._mknode( self.prelude.Cons, self(arg.head), self(arg.tail) , target=self.target ) @__call__.when(type(nil)) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % 'nil') return self._mknode(self.prelude.Nil, target=self.target) @__call__.when(_setgrd) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % '_setgrd') return self._mknode( self.fsyms.SetGuard , arg.sid, self(arg.value), target=self.target ) @__call__.when(type(fail)) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % 'fail') return self._mknode(self.fsyms.Failure, target=self.target) @__call__.when(_strictconstr) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % '_strictconstr') return self._mknode( self.fsyms.StrictConstraint , self(arg.value), self(arg.pair), target=self.target ) @__call__.when(_nonstrictconstr) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % '_nonstrictconstr') return self._mknode( self.fsyms.NonStrictConstraint , self(arg.value), self(arg.pair), target=self.target ) @__call__.when(_valuebinding) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % '_valuebinding') return self._mknode( self.fsyms.ValueBinding , self(arg.value), self(arg.pair), target=self.target ) @__call__.when(free) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % 'free') self.count_freevar() if self.raw: return self._mknode( self.fsyms.Free , arg.vid , self._mknode(self.prelude.Unit) , target=self.target ) node = self.unknown(arg) if self.target is not None: return self._mknode(self.fsyms.Fwd, node, target=self.target) return node def unknown(self, marker): ''' The shared node of a free marker: a call of ``Prelude.unknown`` applied to a ``Data`` dictionary, made at the first occurrence of the marker under this interpreter and reused at every later one. The rewrite step of ``unknown`` creates the variable with a fresh id, registers it with the evaluation, and forwards this node to it. So one marker is one variable, on both backends, and a later goal that holds the marker holds the variable (see RuntimeState.set_goal). ''' node = marker._shared(self.interp) if node is None: dictionary = self.interp.symbol(UNKNOWN_DICTIONARY) partial_info = self.fsyms.PartApplic if dictionary.info.arity else None node = self._mknode( self.prelude.unknown , self._mknode(dictionary, partial_info=partial_info) ) marker._share(self.interp, node) return node def count_freevar(self): ''' Counts a free variable made outside an evaluation, so that ``RuntimeState.set_goal`` registers the variables a goal already holds. See ``InterpreterState.external_freevars`` of either backend. ''' istate = self.interp.backend.get_interpreter_state(self.interp) istate.external_freevars += 1 @__call__.when(fwd) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % 'fwd') return self._mknode( self.fsyms.Fwd, self(arg.value), target=self.target ) @__call__.when(choice) def __call__(self, arg, *trailing): if trailing: raise CurryTypeError('invalid arguments after %r' % 'choice') if self.raw: return self._mknode( self.fsyms.Choice, arg.cid, self(arg.lhs), self(arg.rhs) , target=self.target ) return self._mknode( getattr(self.prelude, '?'), self(arg.lhs), self(arg.rhs) , target=self.target )