diff options
author | Paul McGuire <ptmcg@users.noreply.github.com> | 2019-11-18 22:41:40 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-11-18 22:41:40 -0600 |
commit | 0b398062710dc00b952636bcf7b7933f74f125da (patch) | |
tree | b3f8a2300d1a1669a94afbf90b7915915df8e406 /pyparsing/util.py | |
parent | bea48a41d40f1c37bea7a718cc06e9b858c8ccbf (diff) | |
download | pyparsing-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.py | 171 |
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 |