From 8d90a81c62b9a3f9b525a459b743fee1a4f9544a Mon Sep 17 00:00:00 2001 From: Gabe Fierro Date: Wed, 29 Nov 2023 11:22:19 -0700 Subject: [PATCH] capture errors in the token stream --- buildingmotif/label_parsing.py | 19 ++++++++++++++++++- tests/unit/test_label_parsing.py | 24 ++++++++++++++++++++---- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/buildingmotif/label_parsing.py b/buildingmotif/label_parsing.py index 8c5b0ad1d..9a13c078e 100644 --- a/buildingmotif/label_parsing.py +++ b/buildingmotif/label_parsing.py @@ -59,6 +59,23 @@ class TokenResult: length: int error: Optional[str] = None + def __eq__(self, other): + """ + Compare two token results on every + field except for the error field. + """ + if not isinstance(other, TokenResult): + return False + return ( + self.value == other.value + and self.token == other.token + and self.length == other.length + ) + + +# null token result +ErrorTokenResult = TokenResult(None, Null(), 0, None) + # type definition for the output of a parser function @dataclass(frozen=True) @@ -189,7 +206,7 @@ def parser(target): results = [] while True: part = seq_parser(target) - if not part: + if not part or part[0].value is None: break results.extend(part) # add up the length of all the tokens diff --git a/tests/unit/test_label_parsing.py b/tests/unit/test_label_parsing.py index c58913de4..877b96e16 100644 --- a/tests/unit/test_label_parsing.py +++ b/tests/unit/test_label_parsing.py @@ -1,8 +1,11 @@ +from typing import List + from buildingmotif.label_parsing import ( COMMON_ABBREVIATIONS, COMMON_EQUIP_ABBREVIATIONS_BRICK, Constant, Delimiter, + ErrorTokenResult, Identifier, Null, TokenResult, @@ -23,6 +26,13 @@ from buildingmotif.namespaces import BRICK +def _parse_result_empty(result: List[TokenResult]): + if len(result) == 0: + return True + if len(result) == 1 and result[0].value is None: + return True + + def test_ensure_token(): assert ensure_token(Identifier, "abc") == Identifier( "abc" @@ -43,7 +53,9 @@ def test_string_parser(): TokenResult("abc", Identifier("abc"), 3) ], "Should parse the string" # test that it does not parse the string if it is not at the beginning - assert parser("0abc") == [], "Should not parse the string" + assert parser("0abc") == [ + ErrorTokenResult + ], f"Should not parse the string {parser('0abc')}" def test_substring_n_parser(): @@ -53,7 +65,7 @@ def test_substring_n_parser(): TokenResult("ab", Identifier("ab"), 2) ], "Should parse the string" # test that it pulls the correct number of characters when there are less than it needs - assert parser("a") == [], "Should not parse the string" + assert parser("a") == [ErrorTokenResult], "Should not parse the string" # test that it pulls the correct number of characters when there are exactly the number it needs assert parser("ab") == [ TokenResult("ab", Identifier("ab"), 2) @@ -159,7 +171,8 @@ def test_sequence(): TokenResult("1", Identifier("1"), 1), ], "Should parse the string" assert parser("AHU1-") == [ - TokenResult("AHU", Constant(BRICK.Air_Handling_Unit), 3) + TokenResult("AHU", Constant(BRICK.Air_Handling_Unit), 3), + ErrorTokenResult, ], "Should not parse all of the string" @@ -176,6 +189,7 @@ def test_many(): assert parser("AHU1") == [ TokenResult("AHU", Constant(BRICK.Air_Handling_Unit), 3), TokenResult("1", Identifier("1"), 1), + ErrorTokenResult, ] # no delimiter at the end, so it should not parse the whole thing assert parser("AHU1/SP2") == [ @@ -184,6 +198,7 @@ def test_many(): TokenResult("/", Delimiter("/"), 1), TokenResult("SP", Constant(BRICK.Setpoint), 2), TokenResult("2", Identifier("2"), 1), + ErrorTokenResult, ] # test that it parses multiple sequences assert parser("AHU1/SP2/") == [ @@ -273,7 +288,8 @@ def test_parse(): result = parse(parser, "AHU1-") assert result.tokens == [ - TokenResult("AHU", Constant(BRICK.Air_Handling_Unit), 3) + TokenResult("AHU", Constant(BRICK.Air_Handling_Unit), 3), + ErrorTokenResult, ], "Should not parse all of the string" assert not result.success, "Should not parse the string"