summaryrefslogtreecommitdiff
path: root/pyparsing/util.py
diff options
context:
space:
mode:
authorPaul McGuire <ptmcg@users.noreply.github.com>2019-11-18 22:41:40 -0600
committerGitHub <noreply@github.com>2019-11-18 22:41:40 -0600
commit0b398062710dc00b952636bcf7b7933f74f125da (patch)
treeb3f8a2300d1a1669a94afbf90b7915915df8e406 /pyparsing/util.py
parentbea48a41d40f1c37bea7a718cc06e9b858c8ccbf (diff)
downloadpyparsing-git-0b398062710dc00b952636bcf7b7933f74f125da.tar.gz
Break up pyparsing.py monolith into sub-modules in a pyparsing package (#162)
* Break up pyparsing.py monolith into sub-modules in a pyparsing package * Convert relative imports to absolutes * Reference submodule pyparsing in setup.py modules * Remove recursive import of pyparsing from setup.py * Black updates * setup.py updates - packages vs. modules. use .dev1 for the version
Diffstat (limited to 'pyparsing/util.py')
-rw-r--r--pyparsing/util.py171
1 files changed, 171 insertions, 0 deletions
diff --git a/pyparsing/util.py b/pyparsing/util.py
new file mode 100644
index 0000000..376b9ae
--- /dev/null
+++ b/pyparsing/util.py
@@ -0,0 +1,171 @@
+# util.py
+import warnings
+import types
+import collections
+import itertools
+
+
+_bslash = chr(92)
+
+
+class __config_flags:
+ """Internal class for defining compatibility and debugging flags"""
+
+ _all_names = []
+ _fixed_names = []
+ _type_desc = "configuration"
+
+ @classmethod
+ def _set(cls, dname, value):
+ if dname in cls._fixed_names:
+ warnings.warn(
+ "{}.{} {} is {} and cannot be overridden".format(
+ cls.__name__,
+ dname,
+ cls._type_desc,
+ str(getattr(cls, dname)).upper(),
+ )
+ )
+ return
+ if dname in cls._all_names:
+ setattr(cls, dname, value)
+ else:
+ raise ValueError("no such {} {!r}".format(cls._type_desc, dname))
+
+ enable = classmethod(lambda cls, name: cls._set(name, True))
+ disable = classmethod(lambda cls, name: cls._set(name, False))
+
+
+def col(loc, strg):
+ """Returns current column within a string, counting newlines as line separators.
+ The first column is number 1.
+
+ Note: the default parsing behavior is to expand tabs in the input string
+ before starting the parsing process. See
+ :class:`ParserElement.parseString` for more
+ information on parsing strings containing ``<TAB>`` s, and suggested
+ methods to maintain a consistent view of the parsed string, the parse
+ location, and line and column positions within the parsed string.
+ """
+ s = strg
+ return 1 if 0 < loc < len(s) and s[loc - 1] == "\n" else loc - s.rfind("\n", 0, loc)
+
+
+def lineno(loc, strg):
+ """Returns current line number within a string, counting newlines as line separators.
+ The first line is number 1.
+
+ Note - the default parsing behavior is to expand tabs in the input string
+ before starting the parsing process. See :class:`ParserElement.parseString`
+ for more information on parsing strings containing ``<TAB>`` s, and
+ suggested methods to maintain a consistent view of the parsed string, the
+ parse location, and line and column positions within the parsed string.
+ """
+ return strg.count("\n", 0, loc) + 1
+
+
+def line(loc, strg):
+ """Returns the line of text containing loc within a string, counting newlines as line separators.
+ """
+ lastCR = strg.rfind("\n", 0, loc)
+ nextCR = strg.find("\n", loc)
+ return strg[lastCR + 1 : nextCR] if nextCR >= 0 else strg[lastCR + 1 :]
+
+
+class _UnboundedCache:
+ def __init__(self):
+ cache = {}
+ self.not_in_cache = not_in_cache = object()
+
+ def get(self, key):
+ return cache.get(key, not_in_cache)
+
+ def set(self, key, value):
+ cache[key] = value
+
+ def clear(self):
+ cache.clear()
+
+ def cache_len(self):
+ return len(cache)
+
+ self.get = types.MethodType(get, self)
+ self.set = types.MethodType(set, self)
+ self.clear = types.MethodType(clear, self)
+ self.__len__ = types.MethodType(cache_len, self)
+
+
+class _FifoCache:
+ def __init__(self, size):
+ self.not_in_cache = not_in_cache = object()
+ cache = collections.OrderedDict()
+
+ def get(self, key):
+ return cache.get(key, not_in_cache)
+
+ def set(self, key, value):
+ cache[key] = value
+ try:
+ while len(cache) > size:
+ cache.popitem(False)
+ except KeyError:
+ pass
+
+ def clear(self):
+ cache.clear()
+
+ def cache_len(self):
+ return len(cache)
+
+ self.get = types.MethodType(get, self)
+ self.set = types.MethodType(set, self)
+ self.clear = types.MethodType(clear, self)
+ self.__len__ = types.MethodType(cache_len, self)
+
+
+def _escapeRegexRangeChars(s):
+ # ~ escape these chars: ^-]
+ for c in r"\^-]":
+ s = s.replace(c, _bslash + c)
+ s = s.replace("\n", r"\n")
+ s = s.replace("\t", r"\t")
+ return str(s)
+
+
+def _collapseAndEscapeRegexRangeChars(s):
+ def is_consecutive(c):
+ c_int = ord(c)
+ is_consecutive.prev, prev = c_int, is_consecutive.prev
+ if c_int - prev > 1:
+ is_consecutive.value = next(is_consecutive.counter)
+ return is_consecutive.value
+
+ is_consecutive.prev = 0
+ is_consecutive.counter = itertools.count()
+ is_consecutive.value = -1
+
+ def escape_re_range_char(c):
+ return "\\" + c if c in r"\^-]" else c
+
+ ret = []
+ for _, chars in itertools.groupby(sorted(s), key=is_consecutive):
+ first = last = next(chars)
+ for c in chars:
+ last = c
+ if first == last:
+ ret.append(escape_re_range_char(first))
+ else:
+ ret.append(
+ "{}-{}".format(escape_re_range_char(first), escape_re_range_char(last))
+ )
+ return "".join(ret)
+
+
+def _flatten(L):
+ ret = []
+ for i in L:
+ if isinstance(i, list):
+ ret.extend(_flatten(i))
+ else:
+ ret.append(i)
+ return ret