changeset 23:c7ebd52fe5cb draft

Document list merging behavior of combinators and add inverse OneOf primitives
author Lewin Bormann <lbo@spheniscida.de>
date Tue, 21 May 2019 00:55:46 +0200
parents 9fe660fc8efe
children 4e31b0326501
files pcombinators/combinators.py pcombinators/primitives.py
diffstat 2 files changed, 25 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/pcombinators/combinators.py	Tue May 21 00:03:40 2019 +0200
+++ b/pcombinators/combinators.py	Tue May 21 00:55:46 2019 +0200
@@ -162,12 +162,23 @@
 
 
 class AtomicSequence(_Sequence):
-    """Execute a series of parsers after each other. All must succeed. Result is list of results of the parsers."""
+    """Execute a series of parsers after each other. All must succeed. Result
+    is a merged list of results of the parsers.
+
+    This means that if your parser returns a list and you want to not merge it with the results of other
+    parsers in the repetition, you should wrap the list inside another list. E.g.:
+
+    (List() >> (lambda l: [l])) + Skip(String("<separator>")) + (List() >> (lambda l: [l]))"""
     _atomic = True
 
 class OptimisticSequence(_Sequence):
     """Execute a series of parsers after each other, as far as possible
-    (until the first parser fails). Result is list of results of the parsers."""
+    (until the first parser fails). Result is a merged list of results of the parsers.
+
+    This means that if your parser returns a list and you want to not merge it with the results of other
+    parsers in the repetition, you should wrap the list inside another list. E.g.:
+
+    (List() >> (lambda l: [l])) + Skip(String("<separator>")) + (List() >> (lambda l: [l]))"""
     _atomic = False
 
 class _Repeat(Parser):
@@ -202,7 +213,7 @@
 
 class Repeat(_Repeat):
     """Expect up to `repeat` matches of a parser. -1 means indefinitely many matches.
-    Result is list of results of the parsers."""
+    Result is a merged list of results of the parsers."""
     _strict = False
 
 def Maybe(p):
--- a/pcombinators/primitives.py	Tue May 21 00:03:40 2019 +0200
+++ b/pcombinators/primitives.py	Tue May 21 00:55:46 2019 +0200
@@ -39,8 +39,9 @@
         return (None, st)
 
 class OneOf(Parser):
-    """Parse characters in the given set. Result is string or None, if none were parsed."""
+    """Parse a character in the given set. Result is string or None, if none were parsed."""
     _set = None
+    _inverse = False
 
     def __init__(self, s):
         """
@@ -51,11 +52,15 @@
         self._set = set(s)
 
     def parse(self, st):
-        if not st.finished() and st.peek() in self._set:
+        if not st.finished() and (self._inverse ^ (st.peek() in self._set)):
             return st.next(), st
         else:
             return None, st
 
+class NoneOf(OneOf):
+    """Parse a character not in the set. Result is string."""
+    _inverse = True
+
 class Regex(Parser):
     """Parse a string using a regular expression. The result is either the
     string or a tuple with all matched groups. Result is string."""
@@ -89,6 +94,10 @@
     Result is string."""
     return ConcatenateResults(Repeat(OneOf(s), -1))
 
+def NoneInSet(s):
+    """Inverse of CharSet (parse as long as character is not in set). Result is string."""
+    return ConcatenateResults(Repeat(NoneOf(s), -1))
+
 # See section below for optimized versions of the following parsers.
 
 def CanonicalInteger():