def Literals(*matchers): ''' A series of literals, joined with `Or`. ''' # I considered implementing this by extending Literal() itself, but # that would have meant putting "Or-like" functionality in Literal, # and I felt it better to keep the base matchers reasonably orthogonal. return Or(*lmap(Literal, matchers))
def __clone_node(self, args, kargs): ''' Before cloning, drop any Delayed from args and kargs. Afterwards, check if this is a Delaed instance and, if so, return the contents. This helps keep the number of Delayed instances from exploding. ''' args = lmap(self.__drop, args) kargs = dict((key, self.__drop(kargs[key])) for key in kargs) return self.__drop(self._clone(self._node, args, kargs))
def __init__(self, pairs, flags=0, alphabet=None, engine=None, factory=None): require_engine(engine) self.__regex = RegexObject(parse_groups(lmap(lambda x: x[0], pairs), engine, flags=flags, alphabet=alphabet), engine=engine, factory=factory) self.__actions = list(map(lambda x: x[1], pairs)) # this is implied by a test in Python 3.2 self.scanner = self.__regex
def mkchr(char, range, invert=False): from lepl.matchers.core import Literal from lepl.matchers.derived import Map from lepl.regexp.core import Character intervals = lmap(lambda x: (x, x), range) if invert: # this delays call to invert until after creation of self func = lambda _: Character(self.invert(intervals), self) else: func = lambda _: Character(intervals, self) return Map(Literal(char), func)
def clone_tree(i, head, reversed, mapping, delayed, clone, duplicate=False): ''' Clone a tree of matchers. This clones all the matchers in a linearised set, except for the `Delayed()` instances, which are re-created without their contents (these are set later, to connect the trees into the final matcher DAG). `i` is the index of the tree (0 for the first tree, which cannot be part of a loop itself). It is passed to the clone function. `head` is the root of the tree. `reversed` are the tree nodes in postorder `mapping` is a map from old to new node of all the nodes created. For `Delayed()` instances, if `duplicate=True`, then the new node is just one of possibly many copies. `clone` is the function used to create a new node instance. `duplicate` controls how `Delayed()` instances are handled. If true then a new instance is created for each one. This does not preserve the graph, but is used by memoisation. ''' def rewrite(value): try: if value in mapping: return mapping[value] except TypeError: pass return value n = len(reversed) for (j, node) in zip(count(n, -1), reversed): if isinstance(node, Delayed): if duplicate or node not in mapping: mapping[node] = clone(i, -j, node, (), {}) delayed.append((node, mapping[node])) else: if node not in mapping: (args, kargs) = node._constructor_args() args = lmap(rewrite, args) kargs = dict( (name, rewrite(value)) for (name, value) in kargs.items()) copy = clone(i, j, node, args, kargs) mapping[node] = copy
def clone_tree(i, head, reversed, mapping, delayed, clone, duplicate=False): ''' Clone a tree of matchers. This clones all the matchers in a linearised set, except for the `Delayed()` instances, which are re-created without their contents (these are set later, to connect the trees into the final matcher DAG). `i` is the index of the tree (0 for the first tree, which cannot be part of a loop itself). It is passed to the clone function. `head` is the root of the tree. `reversed` are the tree nodes in postorder `mapping` is a map from old to new node of all the nodes created. For `Delayed()` instances, if `duplicate=True`, then the new node is just one of possibly many copies. `clone` is the function used to create a new node instance. `duplicate` controls how `Delayed()` instances are handled. If true then a new instance is created for each one. This does not preserve the graph, but is used by memoisation. ''' def rewrite(value): try: if value in mapping: return mapping[value] except TypeError: pass return value n = len(reversed) for (j, node) in zip(count(n, -1), reversed): if isinstance(node, Delayed): if duplicate or node not in mapping: mapping[node] = clone(i, -j, node, (), {}) delayed.append((node, mapping[node])) else: if node not in mapping: (args, kargs) = node._constructor_args() args = lmap(rewrite, args) kargs = dict((name, rewrite(value)) for (name, value) in kargs.items()) copy = clone(i, j, node, args, kargs) mapping[node] = copy
def __init__(self, *matchers): super(_BaseCombiner, self).__init__() self._args(matchers=lmap(coerce_, matchers))
def Apply(matcher, function, raw=False, args=False): ''' Apply an arbitrary function to the results of the matcher (**>**, **>=**). Apply can be used via the standard operators by placing ``>`` (or ``>=`` to set ``raw=True``, or ``*`` to set ``args=True``) to the right of a matcher. If the function is a `TransformationWrapper` it is used directly. Otherwise a `TransformationWrapper` is constructed via the `raw` and `args` parameters, as described below. **Note:** The creation of named pairs (when a string argument is used) behaves more like a mapping than a single function invocation. If several values are present, several pairs will be created. **Note:** There is an asymmetry in the default values of *raw* and *args*. If the identity function is used with the default settings then a list of results is passed as a single argument (``args=False``). That is then returned (by the identity) as a list, which is wrapped in an additional list (``raw=False``), giving an extra level of grouping. This is necessary because Python's ``list()`` is an identity for lists, but we want it to add an extra level of grouping so that nested S-expressions are easy to generate. See also `Map`. :Parameters: matcher The matcher whose results will be modified. function The modification to apply. If a `Transformation`, this is used directly. If a string is given, named pairs will be created (and raw and args ignored). Otherwise the function should expect a list of results (unless ``args=True`` in which case the list is supplied as ``*args``) and can return any value (unless ``raw=True`` in which case it should return a list). raw If True the results are used directly. Otherwise they are wrapped in a list. The default is False --- a list is added. This is set to true if the target function is an `ApplyRaw` instance. args If True, the results are passed to the function as separate arguments (Python's '*args' behaviour). The default is False --- the results are passed inside a list). This is set to true if the target function is an `ApplyArgs` instance. ''' raw = raw or (type(function) is type and issubclass(function, ApplyRaw)) args = args or (type(function) is type and issubclass(function, ApplyArgs)) if isinstance(function, TransformationWrapper): apply = function else: if isinstance(function, basestring): function = lambda results, f=function: \ lmap(lambda x: (f, x), results) raw = True args = False if args: if raw: function = lambda results, f=function: f(*results) else: function = lambda results, f=function: [f(*results)] else: if not raw: function = lambda results, f=function: [f(results)] def apply(stream_in, matcher): (results, stream_out) = matcher() return (function(results), stream_out) return Transform(matcher, apply)