From ee305066b729a8859cbbc038425d2e4d5623fefb Mon Sep 17 00:00:00 2001 From: SoniEx2 Date: Wed, 27 Nov 2019 22:12:51 -0300 Subject: Tweaked key matching syntax --- abdl.py | 35 +++++++++++++++++++++++++++-------- testing/test_abdl.py | 5 ++++- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/abdl.py b/abdl.py index 833aad3..84b7d4c 100644 --- a/abdl.py +++ b/abdl.py @@ -14,7 +14,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -"""A Boneless Datastructure Language, version 1.0.0. +"""A Boneless Datastructure Language, version 1.1.0. This is a language for matching mixed-type data-structures simiarly to how you'd match a string with regex. @@ -24,9 +24,9 @@ The Input Language: The input language is used for matching the input and setting up variables. An ABDL expression is made of tokens that can represent variables, literals, commands or parameters. It must start with - an arrow, which must be followed by a variable, - literal, parameter, regex or subtree. Additionally, variables may be followed by a literal, - parameter or regex. In turn, those may be followed by one or more type tests. + an arrow, which must be followed by a variable, literal, parameter, regex or key match. Additionally, + variables may be followed by a literal, parameter or regex. In turn, those may be followed by one + or more type tests. A variable is a string of alphanumeric characters, not starting with a digit. @@ -44,10 +44,10 @@ The Input Language: A type test is ``:`` followed by a parameter. A type test can be made "non-validating" by appending an ``?`` after the ``:``. - A subtree is an ABDL expression enclosed in ``(`` and ``)``, optionally prefixed with one or more type tests. - This matches keys. + A key match is an ABDL expression enclosed in ``[`` and ``]``, optionally prefixed with one or more type + tests. This matches keys. (The old syntax, ``(`` and ``)``, is deprecated.) - Example: + Examples: >>> for m in abdl.match("->X:?$dict->Y", {"foo": 1, "bar": {"baz": 2}}, {'dict': dict}): ... print(m['X'][0], m['Y'][0], m['Y'][1]) @@ -81,6 +81,24 @@ import re from collections.abc import Mapping, Sequence, Iterator, Set +class DeprecationError(Exception): + """Raised for deprecated features, if they are disabled. + + This class controls warning/error behaviour of deprecated features.""" + enable_key_match_compat = True + warn_key_match_compat = False + + @classmethod + def warn_all(cls): + cls.warn_key_match_compat = True + + @classmethod + def _on_keysubtree(cls, s, pos, toks): + if not cls.enable_key_match_compat: + raise cls("Use of deprecated key match compat feature", s, pos) + if cls.warn_key_match_compat: + print("Use of deprecated key match compat feature", s, pos) + class PatternError(Exception): """Raised for invalid input or output expressions.""" # TODO implement formatting @@ -315,7 +333,8 @@ def _build_syntax(): parameter = (Suppress("$") + skippable + identifier).setParseAction(lambda toks: [_Param(toks)]) ty = (Suppress(":") + skippable + Suppress("$") + identifier).setParseAction(lambda toks: [_Ty(toks)]) # support for objects-as-keys - keysubtree = (Suppress("(") + Group(ty[...] + subtree[1,...]) + (Suppress(")") | CharsNotIn("").setParseAction(PatternError._unexpected_tok) | StringEnd().setParseAction(PatternError._unexpected_tok)) + Optional("?", default="")).setParseAction(lambda toks: [_Subtree(toks)]) + keysubtree = (Suppress("(").setParseAction(DeprecationError._on_keysubtree) + Group(ty[...] + subtree[1,...]) + (Suppress(")") | CharsNotIn("").setParseAction(PatternError._unexpected_tok) | StringEnd().setParseAction(PatternError._unexpected_tok)) + Optional("?", default="")).setParseAction(lambda toks: [_Subtree(toks)]) + keysubtree |= (Suppress("[") + Group(ty[...] + subtree[1,...]) + (Suppress("]") | CharsNotIn("").setParseAction(PatternError._unexpected_tok) | StringEnd().setParseAction(PatternError._unexpected_tok)) + Optional("?", default="")).setParseAction(lambda toks: [_Subtree(toks)]) # represents key matching - switches from "key" to "value" tag = (identifier + Optional(parameter | re_literal | keysubtree) | parameter | str_literal | re_literal | keysubtree) + ty[...] + Empty().setParseAction(lambda: [_End()]) # arrow and tag or we give up diff --git a/testing/test_abdl.py b/testing/test_abdl.py index 9317643..080ab3f 100644 --- a/testing/test_abdl.py +++ b/testing/test_abdl.py @@ -11,6 +11,9 @@ import re import traceback + +abdl.DeprecationError.enable_key_match_compat = False + # use abdl's _pairs for consistency. pairs = abdl._pairs @@ -156,7 +159,7 @@ def test_multi_type_with_validation_errors(foo, pat): raise abdl.ValidationError assert all(LogAndCompare(pat.match(foo), deep(foo))) -@hypothesis.given(st.dictionaries(st.frozensets(st.text()), st.text()), st.just(abdl.compile("->(:?$sets->A)->D", {'sets': collections.abc.Set}))) +@hypothesis.given(st.dictionaries(st.frozensets(st.text()), st.text()), st.just(abdl.compile("->[:?$sets->A]->D", {'sets': collections.abc.Set}))) def test_subtree_partial(foo, pat): def deep(foo): for x in pairs(foo): -- cgit 1.4.1