From 6218526811aab102d133ad8aeb283887468fa051 Mon Sep 17 00:00:00 2001 From: IanCa Date: Wed, 1 Feb 2023 17:43:43 -0600 Subject: [PATCH] Add a 'not in line' symbol to search --- hed/models/expression_parser.py | 19 +++++++--- tests/models/test_expression_parser.py | 49 ++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/hed/models/expression_parser.py b/hed/models/expression_parser.py index 26881cc03..af1f81928 100644 --- a/hed/models/expression_parser.py +++ b/hed/models/expression_parser.py @@ -7,9 +7,6 @@ def __init__(self, group, tag): self.group = group # todo: rename tag: children if not isinstance(tag, list): - from hed import HedString - if isinstance(tag, HedString): - breakHere = 3 new_tags = [tag] else: new_tags = tag.copy() @@ -68,6 +65,7 @@ class Token: Wildcard = 10 ExactMatch = 11 ExactMatchEnd = 12 + NotInLine = 13 # Not currently a token. In development and may become one. def __init__(self, text): tokens = { @@ -86,6 +84,7 @@ def __init__(self, text): "???": Token.Wildcard, # Any Group "{": Token.ExactMatch, # Nothing else "}": Token.ExactMatchEnd, # Nothing else + "@": Token.NotInLine } self.kind = tokens.get(text, Token.Tag) self.text = text @@ -105,6 +104,10 @@ def __init__(self, token, left=None, right=None): self.right = right self.token = token self._match_mode = "/" in token.text + self._must_not_be_in_line = False + if token.text.startswith("@"): + self._must_not_be_in_line = True + token.text = token.text[1:] if token.text.startswith('"') and token.text.endswith('"') and len(token.text) > 2: self._match_mode = 1 token.text = token.text[1:-1] @@ -129,6 +132,13 @@ def handle_expr(self, hed_group, exact=False): else: groups_found = hed_group.find_tags_with_term(self.token.text, recursive=True, include_groups=2) + if self._must_not_be_in_line: + # If we found this, and it cannot be in the line. + if groups_found: + groups_found = [] + else: + groups_found = [(None, group) for group in hed_group.get_all_groups()] + # If we're checking for all groups, also need to add parents. if exact: all_found_groups = [search_result(group, tag) for tag, group in groups_found] @@ -172,6 +182,7 @@ def handle_expr(self, hed_group, exact=False): # finally simplify the list and remove duplicates. return return_list + def __str__(self): output_str = "(" if self.left: @@ -398,7 +409,7 @@ def _parse(self, expression_string): def _tokenize(self, expression_string): grouping_re = r"\[\[|\[|\]\]|\]|}|{" paren_re = r"\)|\(|~" - word_re = r"\?+|@|\band\b|\bor\b|,|[\"_\-a-zA-Z0-9/.^#\*]+" + word_re = r"\?+|\band\b|\bor\b|,|[\"_\-a-zA-Z0-9/.^#\*@]+" re_string = fr"({grouping_re}|{paren_re}|{word_re})" token_re = re.compile(re_string) diff --git a/tests/models/test_expression_parser.py b/tests/models/test_expression_parser.py index be9b4195c..045b61ab1 100644 --- a/tests/models/test_expression_parser.py +++ b/tests/models/test_expression_parser.py @@ -651,18 +651,53 @@ def test_logical_negation(self): hed_string = HedString("A, C") self.assertEqual(bool(expression.search_hed_string(hed_string)), False) - def test_kay(self): + def test_not_in_line(self): test_strings = { - "Event, (Event, Sensory-event)": True, + "A": True, + "B": False, + "C": True, + "A, B": False, + "A, C": True, + "B, C": False, + "A, B, C": False, + "D, A, B": False, + "A, B, (C)": False, + "(A, B, (C))": False, + "(A, B, (C)), D": False, + "(A, B, (C)), (D), E": False, } - self.base_test("Event", test_strings) + self.base_test("@B", test_strings) + def test_not_in_line2(self): test_strings = { - "Event, (Event, Sensory-event), Event": True, + "A": False, + "B": False, + "C": True, + "A, B": False, + "A, C": True, + "B, C": False, + "A, B, C": False, + "D, A, B": False, + "A, B, (C)": False, + "(A, B, (C))": False, + "(A, B, (C)), D": False, + "(A, B, (C)), (D), E": False, } - self.base_test("Event and Sensory-event", test_strings) + self.base_test("@B and C", test_strings) + def test_not_in_line3(self): test_strings = { - "Sensory-event, (Event, Sensory-event), Event": True, + "A": True, + "B": True, + "C": False, + "A, B": True, + "A, C": False, + "B, C": True, + "A, B, C": True, + "D, A, B": True, + "A, B, (C)": True, + "(A, B, (C))": True, + "(A, B, (C)), D": True, + "(A, B, (C)), (D), E": True, } - self.base_test("[Sensory-event]", test_strings) \ No newline at end of file + self.base_test("@C or B", test_strings) \ No newline at end of file