Example #1
0
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))
Example #2
0
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))
Example #3
0
 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))
Example #4
0
 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))
Example #5
0
 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
Example #6
0
 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)
Example #7
0
 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)
Example #8
0
 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
Example #9
0
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
Example #10
0
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
Example #11
0
 def __init__(self, *matchers):
     super(_BaseCombiner, self).__init__()
     self._args(matchers=lmap(coerce_, matchers))
Example #12
0
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)
Example #13
0
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)
Example #14
0
 def __init__(self, *matchers):
     super(_BaseCombiner, self).__init__()
     self._args(matchers=lmap(coerce_, matchers))