Source code for curry.backends.py.graph.variable

from . import indexing, utility
from .node import Node
from .... import common, inspect

# The evaluator (eval.fairscheme) imports this module, so it is imported here
# on first use.  The module-level name saves an import statement on every call
# to hnf.
fairscheme = None

def _import_fairscheme():
  global fairscheme
  from ..eval import fairscheme as module
  fairscheme = module
  return module

[docs] def variable(rts, parent, logicalpath=None): '''Creates an instance of Variable.''' if logicalpath is None: return Variable.from_root(rts, parent) else: return Variable.from_logicalpath(rts, parent, logicalpath)
[docs] class Variable(object): ''' Represents an ordinary (non-free) Curry variable. This objects tracks a root, target, path between them, and set guards crossed. It is the focal point when working with expressions in generated code. These objects help with accessing and head-normalizing subexpressions, getting case selectors, building replacement expressions, and rewriting. Attributes: root The function-labeled redex root. Dominates the target expression. target The Curry expression referenced by this variable. Typically an inductive position of the root function. realpath The real path from ``root`` to ``target``. guards A set containing the set guards crossed on the path from ``root`` to ``target``. rvalue The value to use when this variable appears in a RHS replacement expression. Any guards crossed along the path from root to target are inserted before the target. Example: The ``Prelude.head`` function is defined as follows:: head (a:as) = a Here, both 'a' and 'as' are variables. In addition, for the purpose of finding redexes, the compiler generates variables for unnamed inductive positions, such as (:) in this example. This might be compiled to the following ICurry:: var $1 $1 <- $0[0] case $1 of Prelude.[] -> exempt Prelude.: _ _ -> var $2 $2 <- $1[0] return $2 The generated Python code might use ``Variable`` as follows:: def step(rts, _0): # _0 = rts.variable(_0) # (1) _1 = None _1 = rts.variable(_0, 0) # (2) _1.hnf(typedef=ty_Nil) # (3) if _1.tag == 1: # (4) _0.rewrite(rts.Failure) # (5a) else: _2 = None _2 = rts.variable(_1, 0) _0.rewrite(rts.Fwd, _2) # (5b) At (1), the root variable, _0, is constructed. This will serve as the base for other variables, and for rewriting. This is in fact taken care of by the system before calling the step function. At (2), the variable corresponding to _1 in ICurry or ':' in the source code is defined. Step (3) head-normalizes the subexpression at _1. The tag is inspected at (4). At (5a,5b), the root is rewritten. When considering set functions, this step might need to insert set guards before _2. ''' __slots__ = ('rts', 'root', 'target', 'realpath', 'guards') def __init__(self, rts, root, target, realpath=None, guards=None): self.rts = rts self.root = root self.target = target self.realpath = realpath self.guards = set() if guards is None else guards
[docs] @staticmethod def from_root(rts, parent): root = getattr(parent, 'root', parent) return Variable(rts, root, root)
[docs] @staticmethod def from_logicalpath(rts, parent, logicalpath): if isinstance(parent, Variable): root = parent.root basetarget = parent.target basepath = parent.realpath baseguards = parent.guards else: root = getattr(parent, 'root', parent) basetarget = getattr(parent, 'target', parent) basepath = getattr(parent, 'realpath', None) baseguards = getattr(parent, 'guards', None) target, realpath, guards = indexing.realpath_parts( basetarget, logicalpath, True ) if basepath is not None: if type(basepath) is list: realpath = basepath + realpath else: realpath = utility.joinpath(basepath, realpath) if baseguards: guards.update(baseguards) assert inspect.isa_curry_expr(target) return Variable(rts, root, target, realpath, guards)
def __repr__(self): return '%s(realpath=%r, guards=%r' % ( type(self).__name__, self.realpath, self.guards ) @property def rvalue(self): '''Returns the target with the appropriate set guards inserted.''' return self.rts.guard(self.target, self.guards) # Item access. # ------------ def __getitem__(self, logicalpath): if logicalpath is None: return Variable.from_root(self.rts, self) return Variable.from_logicalpath(self.rts, self, logicalpath) def __setitem__(self, i, value): # Used only to construct cyclic expressions. assert self.successors[i] is None self.successors[i] = value # Properties. # ----------- # Putting these here simplifies the generated code. It is easier to work with # attributes of variables than bring in freestanding functions. @property def info(self): target = self.target if isinstance(target, Node): return target.info return inspect.info_of(target) @property def is_boxed(self): return inspect.is_boxed(self.target) @property def is_root(self): return self.root is self.target @property def successors(self): return self.target.successors @property def tag(self): target = self.target if isinstance(target, Node): return target.info.tag return inspect.tag_of(target) @property def typedef(self): return self.target.info.typedef @property def unboxed_value(self): return inspect.unboxed_value(self.target) # Methods. # -------- # As with the properties, these methods are easier to access in generated code # when they are attached to variables.
[docs] def extend(self): ''' Step over a forward node or set guard at the target position. This might be needed, for instance, after a rewrite step that modifies the target. ''' tag = self.tag if tag == common.T_SETGRD: sid, self.target = self.successors self.realpath.append(1) self.guards.add(sid) elif tag == common.T_FWD: self.target, = self.successors self.realpath.append(0)
[docs] def hnf(self, typedef=None, values=None): '''Head-normalizes this variable.''' return (fairscheme or _import_fairscheme()).hnf( self.rts, self, typedef, values )
[docs] def hnf_or_free(self, typedef=None, **kwds): return (fairscheme or _import_fairscheme()).hnf_or_free( self.rts, self, typedef, **kwds )
[docs] def replace_target(self, replacement): '''Replace the target by rewriting the root.''' utility.copy_spine(self.root, self.realpath, end=replacement, rewrite=self.root) self.target = replacement
[docs] def rewrite(self, nodeinfo, *args): '''Rewrite this variable.''' assert self.is_root assert inspect.isa_func(self.target) return self.target.rewrite(nodeinfo, *args)