From b3653683272f4418d2ff5070d3b6f3b304da216f Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Tue, 5 Mar 2024 22:08:30 +0100 Subject: [PATCH 1/9] check if regular label is valid regex and labels in regulation exist --- eBCSgen/Parsing/ParseBCSL.py | 12 +++++++++++- eBCSgen/Regulations/ConcurrentFree.py | 7 +++++++ eBCSgen/Regulations/Conditional.py | 6 ++++++ eBCSgen/Regulations/Ordered.py | 7 +++++++ eBCSgen/Regulations/Programmed.py | 8 ++++++++ eBCSgen/Regulations/Regular.py | 14 ++++++++++++++ 6 files changed, 53 insertions(+), 1 deletion(-) diff --git a/eBCSgen/Parsing/ParseBCSL.py b/eBCSgen/Parsing/ParseBCSL.py index e70646c..5e0dd91 100644 --- a/eBCSgen/Parsing/ParseBCSL.py +++ b/eBCSgen/Parsing/ParseBCSL.py @@ -26,6 +26,7 @@ from eBCSgen.Core.Model import Model from eBCSgen.Errors.ComplexParsingError import ComplexParsingError from eBCSgen.Errors.UnspecifiedParsingError import UnspecifiedParsingError +from eBCSgen.Errors.RegulationParsingError import RegulationParsingError def load_TS_from_json(json_file: str) -> TransitionSystem: @@ -213,7 +214,10 @@ def regulation_def(self, matches): def regular(self, matches): re = "".join(matches[1:]) # might raise exception - regex.compile(re) + try: + regex.compile(re) + except regex.error as e: + raise RegulationParsingError(f"Invalid regular expression: {re}. Error: {e}") return Regular(re) def programmed(self, matches): @@ -526,6 +530,7 @@ class TreeToObjects(Transformer): def __init__(self): super(TreeToObjects, self).__init__() self.params = set() + self.labels = set() """ A transformer which is called on a tree in a bottom-up manner and transforms all subtrees/tokens it encounters. @@ -577,6 +582,8 @@ def rule(self, matches): lhs, arrow, rhs, rate = matches else: lhs, arrow, rhs = matches + if label: + self.labels.add(label) agents = tuple(lhs.seq + rhs.seq) mid = lhs.counter compartments = lhs.comp + rhs.comp @@ -671,6 +678,9 @@ def model(self, matches): elif key == "regulation": if regulation: raise UnspecifiedParsingError("Multiple regulations") + # check if regulation is in label + if not value.check_labels(self.labels): + raise RegulationParsingError("Regulation label not found") regulation = value params = self.params - set(definitions) diff --git a/eBCSgen/Regulations/ConcurrentFree.py b/eBCSgen/Regulations/ConcurrentFree.py index ad5022e..876933b 100644 --- a/eBCSgen/Regulations/ConcurrentFree.py +++ b/eBCSgen/Regulations/ConcurrentFree.py @@ -22,3 +22,10 @@ def filter(self, current_state, candidates): if p_rule and non_p_rule: del candidates[non_p_rule.pop()] return candidates + + def check_labels(self, model_labels): + for tuple in self.regulation: + for label in tuple: + if label not in model_labels: + return False + return True \ No newline at end of file diff --git a/eBCSgen/Regulations/Conditional.py b/eBCSgen/Regulations/Conditional.py index a11998f..f987b84 100644 --- a/eBCSgen/Regulations/Conditional.py +++ b/eBCSgen/Regulations/Conditional.py @@ -19,6 +19,12 @@ def __repr__(self): def filter(self, current_state, candidates): agents = set(current_state.content.value) return {rule: values for rule, values in candidates.items() if not self.check_intersection(rule.label, agents)} + + def check_labels(self, model_labels): + for label in self.regulation: + if label not in model_labels: + return False + return True def check_intersection(self, label, agents): if label not in self.regulation: diff --git a/eBCSgen/Regulations/Ordered.py b/eBCSgen/Regulations/Ordered.py index bb37018..541bbae 100644 --- a/eBCSgen/Regulations/Ordered.py +++ b/eBCSgen/Regulations/Ordered.py @@ -38,3 +38,10 @@ def filter(self, current_state, candidates): return candidates last_rule = current_state.memory.history[-1] return {rule: values for rule, values in candidates.items() if not (last_rule, rule.label) in self.regulation} + + def check_labels(self, model_labels): + for tuple in self.regulation: + for label in tuple: + if label not in model_labels: + return False + return True \ No newline at end of file diff --git a/eBCSgen/Regulations/Programmed.py b/eBCSgen/Regulations/Programmed.py index e5580e5..723521d 100644 --- a/eBCSgen/Regulations/Programmed.py +++ b/eBCSgen/Regulations/Programmed.py @@ -24,3 +24,11 @@ def filter(self, current_state, candidates): if last_rule in self.regulation: return {rule: values for rule, values in candidates.items() if rule.label in self.regulation[last_rule]} return candidates + + def check_labels(self, model_labels): + for key, value in self.regulation.items(): + if key not in model_labels: + return False + if value > model_labels: + return False + return True \ No newline at end of file diff --git a/eBCSgen/Regulations/Regular.py b/eBCSgen/Regulations/Regular.py index 2b8f859..c3106a0 100644 --- a/eBCSgen/Regulations/Regular.py +++ b/eBCSgen/Regulations/Regular.py @@ -23,3 +23,17 @@ def filter(self, current_state, candidates): path = "".join(current_state.memory.history) return {rule: values for rule, values in candidates.items() if self.regulation.fullmatch(path + rule.label, partial=True) is not None} + + def check_labels(self, model_labels): + positions = [False] * len(self.regulation.pattern) + for label in model_labels: + match = regex.search(label, self.regulation.pattern) + while match is not None: + for i in range(match.start(), match.end()): + positions[i] = True + match = regex.search(label, self.regulation.pattern, pos=match.end()) + + for i, position in enumerate(positions): + if not position and self.regulation.pattern[i] not in ".*+?^${}()[]|\\": + return False + return True From cdb010a8a9aabc2981e1a16e3ac5e6fa930fb1e2 Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Wed, 6 Mar 2024 11:32:27 +0100 Subject: [PATCH 2/9] check all possible permutations of the labels in the regex --- eBCSgen/Regulations/Regular.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/eBCSgen/Regulations/Regular.py b/eBCSgen/Regulations/Regular.py index c3106a0..5748926 100644 --- a/eBCSgen/Regulations/Regular.py +++ b/eBCSgen/Regulations/Regular.py @@ -1,4 +1,5 @@ import regex +from itertools import permutations from eBCSgen.Regulations.Base import BaseRegulation @@ -25,15 +26,13 @@ def filter(self, current_state, candidates): if self.regulation.fullmatch(path + rule.label, partial=True) is not None} def check_labels(self, model_labels): - positions = [False] * len(self.regulation.pattern) - for label in model_labels: - match = regex.search(label, self.regulation.pattern) - while match is not None: - for i in range(match.start(), match.end()): - positions[i] = True - match = regex.search(label, self.regulation.pattern, pos=match.end()) - - for i, position in enumerate(positions): - if not position and self.regulation.pattern[i] not in ".*+?^${}()[]|\\": - return False + patterns = self.regulation.pattern[1:-1].split('|') + # Generate all permutations of the label in the set + all_permutations = [''.join(p) for i in range(len(model_labels)) for p in permutations(model_labels, i+1)] + # Check if the regex matches any permutation + for pattern in patterns: + subregex = regex.compile(pattern) + if any(subregex.search(p) for p in all_permutations): + continue + return False return True From 2e41d8278dc9e71a9d9d844c9e7a006feff54a13 Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Wed, 6 Mar 2024 12:31:12 +0100 Subject: [PATCH 3/9] new error class for regulation errors --- eBCSgen/Errors/RegulationParsingError.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 eBCSgen/Errors/RegulationParsingError.py diff --git a/eBCSgen/Errors/RegulationParsingError.py b/eBCSgen/Errors/RegulationParsingError.py new file mode 100644 index 0000000..554976b --- /dev/null +++ b/eBCSgen/Errors/RegulationParsingError.py @@ -0,0 +1,6 @@ +class RegulationParsingError(Exception): + def __init__(self, error): + self.error = error + + def __str__(self): + return "Error while parsing the regulation:\n\n{}".format(self.error) From ab5c6553d2111c02655dba26aae4a1edb03bebdd Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Mon, 11 Mar 2024 13:46:12 +0100 Subject: [PATCH 4/9] refactor check_labels in Programmed for clarity and correct subset logic --- eBCSgen/Regulations/Programmed.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eBCSgen/Regulations/Programmed.py b/eBCSgen/Regulations/Programmed.py index 723521d..a36153a 100644 --- a/eBCSgen/Regulations/Programmed.py +++ b/eBCSgen/Regulations/Programmed.py @@ -26,9 +26,9 @@ def filter(self, current_state, candidates): return candidates def check_labels(self, model_labels): - for key, value in self.regulation.items(): - if key not in model_labels: + for rule_label, successors_labels in self.regulation.items(): + if rule_label not in model_labels: return False - if value > model_labels: + if not successors_labels.issubset(model_labels): return False return True \ No newline at end of file From 80e3b1f027a1590cb635fe78b5e78752ade412a9 Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Mon, 11 Mar 2024 14:34:04 +0100 Subject: [PATCH 5/9] moved raising RegulationParsingError to individual check_labels methods with information about missing label(s) --- eBCSgen/Parsing/ParseBCSL.py | 5 ++--- eBCSgen/Regulations/ConcurrentFree.py | 3 ++- eBCSgen/Regulations/Conditional.py | 3 ++- eBCSgen/Regulations/Ordered.py | 3 ++- eBCSgen/Regulations/Programmed.py | 6 ++++-- eBCSgen/Regulations/Regular.py | 3 ++- 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/eBCSgen/Parsing/ParseBCSL.py b/eBCSgen/Parsing/ParseBCSL.py index 5e0dd91..947ef9c 100644 --- a/eBCSgen/Parsing/ParseBCSL.py +++ b/eBCSgen/Parsing/ParseBCSL.py @@ -679,9 +679,8 @@ def model(self, matches): if regulation: raise UnspecifiedParsingError("Multiple regulations") # check if regulation is in label - if not value.check_labels(self.labels): - raise RegulationParsingError("Regulation label not found") - regulation = value + if value.check_labels(self.labels): + regulation = value params = self.params - set(definitions) return Model(rules, inits, definitions, params, regulation) diff --git a/eBCSgen/Regulations/ConcurrentFree.py b/eBCSgen/Regulations/ConcurrentFree.py index 876933b..282f48f 100644 --- a/eBCSgen/Regulations/ConcurrentFree.py +++ b/eBCSgen/Regulations/ConcurrentFree.py @@ -1,3 +1,4 @@ +from eBCSgen.Errors.RegulationParsingError import RegulationParsingError from eBCSgen.Regulations.Base import BaseRegulation @@ -27,5 +28,5 @@ def check_labels(self, model_labels): for tuple in self.regulation: for label in tuple: if label not in model_labels: - return False + raise RegulationParsingError(f"Label {label} in concurrent-free regulation not present in model") return True \ No newline at end of file diff --git a/eBCSgen/Regulations/Conditional.py b/eBCSgen/Regulations/Conditional.py index f987b84..6f6914b 100644 --- a/eBCSgen/Regulations/Conditional.py +++ b/eBCSgen/Regulations/Conditional.py @@ -1,3 +1,4 @@ +from eBCSgen.Errors.RegulationParsingError import RegulationParsingError from eBCSgen.Regulations.Base import BaseRegulation @@ -23,7 +24,7 @@ def filter(self, current_state, candidates): def check_labels(self, model_labels): for label in self.regulation: if label not in model_labels: - return False + raise RegulationParsingError(f"Label {label} in conditional regulation not present in model") return True def check_intersection(self, label, agents): diff --git a/eBCSgen/Regulations/Ordered.py b/eBCSgen/Regulations/Ordered.py index 541bbae..5bae8e8 100644 --- a/eBCSgen/Regulations/Ordered.py +++ b/eBCSgen/Regulations/Ordered.py @@ -1,3 +1,4 @@ +from eBCSgen.Errors.RegulationParsingError import RegulationParsingError from eBCSgen.Regulations.Base import BaseRegulation @@ -43,5 +44,5 @@ def check_labels(self, model_labels): for tuple in self.regulation: for label in tuple: if label not in model_labels: - return False + raise RegulationParsingError(f"Label {label} in programmed regulation not present in model") return True \ No newline at end of file diff --git a/eBCSgen/Regulations/Programmed.py b/eBCSgen/Regulations/Programmed.py index a36153a..8492053 100644 --- a/eBCSgen/Regulations/Programmed.py +++ b/eBCSgen/Regulations/Programmed.py @@ -1,3 +1,4 @@ +from eBCSgen.Errors.RegulationParsingError import RegulationParsingError from eBCSgen.Regulations.Base import BaseRegulation @@ -28,7 +29,8 @@ def filter(self, current_state, candidates): def check_labels(self, model_labels): for rule_label, successors_labels in self.regulation.items(): if rule_label not in model_labels: - return False + raise RegulationParsingError(f"Label {rule_label} in programmed regulation not present in model") if not successors_labels.issubset(model_labels): - return False + missing_labels = successors_labels - model_labels + raise RegulationParsingError(f"Label(s) {missing_labels} in programmed regulation not present in model") return True \ No newline at end of file diff --git a/eBCSgen/Regulations/Regular.py b/eBCSgen/Regulations/Regular.py index 5748926..f1bb5cc 100644 --- a/eBCSgen/Regulations/Regular.py +++ b/eBCSgen/Regulations/Regular.py @@ -1,5 +1,6 @@ import regex from itertools import permutations +from eBCSgen.Errors.RegulationParsingError import RegulationParsingError from eBCSgen.Regulations.Base import BaseRegulation @@ -34,5 +35,5 @@ def check_labels(self, model_labels): subregex = regex.compile(pattern) if any(subregex.search(p) for p in all_permutations): continue - return False + raise RegulationParsingError(f"Label in programmed regulation not present in model") return True From 76c63b29c33e34b0f244ee318d16968b05d03af0 Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Mon, 11 Mar 2024 21:55:08 +0100 Subject: [PATCH 6/9] delimiter for regular regulation introduced --- Testing/models/regulation5.txt | 2 +- eBCSgen/Parsing/ParseBCSL.py | 2 +- eBCSgen/Regulations/Regular.py | 18 ++++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Testing/models/regulation5.txt b/Testing/models/regulation5.txt index fbf34f7..ae05ba3 100644 --- a/Testing/models/regulation5.txt +++ b/Testing/models/regulation5.txt @@ -1,3 +1,3 @@ #! regulation type regular -(r1_Sr1_Tr2|r1_Tr1_Sr2) +(r1_S;r1_T;r2|r1_T;r1_S;r2) diff --git a/eBCSgen/Parsing/ParseBCSL.py b/eBCSgen/Parsing/ParseBCSL.py index 947ef9c..a4fdb07 100644 --- a/eBCSgen/Parsing/ParseBCSL.py +++ b/eBCSgen/Parsing/ParseBCSL.py @@ -189,7 +189,7 @@ def to_side(self): REGULATIONS_GRAMMAR = """ regulation_def: "type" ( regular | programmed | ordered | concurrent_free | conditional ) - !regular: "regular" _NL+ (DIGIT|LETTER| "+" | "*" | "(" | ")" | "[" | "]" | "_" | "|" | "&")+ _NL* + !regular: "regular" _NL+ (DIGIT|LETTER| "+" | "*" | "(" | ")" | "[" | "]" | "_" | "|" | "&" | ";")+ _NL* programmed: "programmed" _NL+ (successors _NL+)* successors _NL* successors: CNAME ":" "{" CNAME ("," CNAME)* "}" diff --git a/eBCSgen/Regulations/Regular.py b/eBCSgen/Regulations/Regular.py index f1bb5cc..7cf1666 100644 --- a/eBCSgen/Regulations/Regular.py +++ b/eBCSgen/Regulations/Regular.py @@ -1,5 +1,4 @@ import regex -from itertools import permutations from eBCSgen.Errors.RegulationParsingError import RegulationParsingError from eBCSgen.Regulations.Base import BaseRegulation @@ -27,13 +26,16 @@ def filter(self, current_state, candidates): if self.regulation.fullmatch(path + rule.label, partial=True) is not None} def check_labels(self, model_labels): - patterns = self.regulation.pattern[1:-1].split('|') - # Generate all permutations of the label in the set - all_permutations = [''.join(p) for i in range(len(model_labels)) for p in permutations(model_labels, i+1)] - # Check if the regex matches any permutation + patterns = self.regulation.pattern[1:-1].split("|") + subpaterns_set = set() for pattern in patterns: - subregex = regex.compile(pattern) - if any(subregex.search(p) for p in all_permutations): + subpaterns_set = subpaterns_set.union(set(pattern.split(";"))) + + for subpattern in subpaterns_set: + subregex = regex.compile(subpattern) + if any(subregex.search(label) for label in model_labels): continue - raise RegulationParsingError(f"Label in programmed regulation not present in model") + raise RegulationParsingError( + f"Label in programmed regulation not present in model" + ) return True From 4450e422f7f308f381f26486f823c9991f96f554 Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Fri, 15 Mar 2024 20:53:55 +0100 Subject: [PATCH 7/9] regex pattern to subpattern correction --- eBCSgen/Regulations/Regular.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eBCSgen/Regulations/Regular.py b/eBCSgen/Regulations/Regular.py index 7cf1666..7b7497b 100644 --- a/eBCSgen/Regulations/Regular.py +++ b/eBCSgen/Regulations/Regular.py @@ -26,7 +26,7 @@ def filter(self, current_state, candidates): if self.regulation.fullmatch(path + rule.label, partial=True) is not None} def check_labels(self, model_labels): - patterns = self.regulation.pattern[1:-1].split("|") + patterns = self.regulation.pattern.replace("(", "").replace(")", "").split("|") subpaterns_set = set() for pattern in patterns: subpaterns_set = subpaterns_set.union(set(pattern.split(";"))) From ddd26feabe75681084ef68dac5c336f818e34608 Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Fri, 15 Mar 2024 21:31:30 +0100 Subject: [PATCH 8/9] regex grammar extended --- eBCSgen/Parsing/ParseBCSL.py | 53 +++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/eBCSgen/Parsing/ParseBCSL.py b/eBCSgen/Parsing/ParseBCSL.py index a4fdb07..03fb182 100644 --- a/eBCSgen/Parsing/ParseBCSL.py +++ b/eBCSgen/Parsing/ParseBCSL.py @@ -3,7 +3,7 @@ import numpy as np from numpy import inf from copy import deepcopy -from lark import Lark, Transformer, Tree +from lark import Lark, Token, Transformer, Tree from lark import UnexpectedCharacters, UnexpectedToken from lark.load_grammar import _TERMINAL_NAMES import regex @@ -189,7 +189,7 @@ def to_side(self): REGULATIONS_GRAMMAR = """ regulation_def: "type" ( regular | programmed | ordered | concurrent_free | conditional ) - !regular: "regular" _NL+ (DIGIT|LETTER| "+" | "*" | "(" | ")" | "[" | "]" | "_" | "|" | "&" | ";")+ _NL* + !regular: "regular" _NL+ expression _NL* programmed: "programmed" _NL+ (successors _NL+)* successors _NL* successors: CNAME ":" "{" CNAME ("," CNAME)* "}" @@ -203,6 +203,40 @@ def to_side(self): context: CNAME ":" "{" rate_complex ("," rate_complex)* "}" """ +REGEX_GRAMMAR = r""" + !?expression: term ("|" term)* + + ?term: factor+ + + ?factor: primary quantifier? + + !quantifier: "??" + | "*?" + | "+?" + | "*+" + | "++" + | "?+" + | "*" + | "+" + | "?" + | "{NUMBER,NUMBER}" + | "{NUMBER}" + + !primary: "(" expression ")" + | "[" REGEX_CHAR "-" REGEX_CHAR "]" + | "[" REGEX_CHAR* "]" + | SPECIAL_CHAR + | ESCAPED_CHAR + | "." + | REGEX_CHAR + + SPECIAL_CHAR: "^" | "$" | "&" + + ESCAPED_CHAR: "\\" ("w"|"W"|"d"|"D"|"s"|"S"|"b"|"B"|"A"|"Z"|"G"|"."|"^"|"["|"]"|"("|")"|"{"|"}"|"?"|"*"|"+"|"|"|"\\") + + REGEX_CHAR: /[^\\^$().*+?{}\[\]|]/ +""" + class TransformRegulations(Transformer): def regulation(self, matches): @@ -212,13 +246,23 @@ def regulation_def(self, matches): return matches[0] def regular(self, matches): - re = "".join(matches[1:]) - # might raise exception + re = self.tree_to_regex_string(matches[1]) try: regex.compile(re) except regex.error as e: raise RegulationParsingError(f"Invalid regular expression: {re}. Error: {e}") return Regular(re) + + def tree_to_regex_string(self, tree): + regex_string = "" + + if isinstance(tree, Token): + return tree.value + elif isinstance(tree, Tree): + for child in tree.children: + regex_string += self.tree_to_regex_string(child) + + return regex_string def programmed(self, matches): successors = {k: v for x in matches for k, v in x.items()} @@ -695,6 +739,7 @@ def __init__(self, start): + COMPLEX_GRAMMAR + EXTENDED_GRAMMAR + REGULATIONS_GRAMMAR + + REGEX_GRAMMAR ) self.parser = Lark( grammar, parser="lalr", propagate_positions=False, maybe_placeholders=False From 3ca20b35be1359038c670ed09b1ed9cf9f398f2c Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Mon, 18 Mar 2024 07:32:58 +0100 Subject: [PATCH 9/9] remove tree_to_string duplicity by moving tree_to_string to utils.py --- eBCSgen/Core/Formula.py | 2 +- eBCSgen/Core/Rate.py | 13 +------------ eBCSgen/Parsing/ParseBCSL.py | 14 ++------------ eBCSgen/utils.py | 14 ++++++++++++++ 4 files changed, 18 insertions(+), 25 deletions(-) create mode 100644 eBCSgen/utils.py diff --git a/eBCSgen/Core/Formula.py b/eBCSgen/Core/Formula.py index 853a415..a68e2a9 100644 --- a/eBCSgen/Core/Formula.py +++ b/eBCSgen/Core/Formula.py @@ -1,7 +1,7 @@ from lark import Transformer, Tree from eBCSgen.Errors.ComplexOutOfScope import ComplexOutOfScope -from eBCSgen.Core.Rate import tree_to_string +from eBCSgen.utils import tree_to_string class Formula: diff --git a/eBCSgen/Core/Rate.py b/eBCSgen/Core/Rate.py index 648aeae..ba6acfd 100644 --- a/eBCSgen/Core/Rate.py +++ b/eBCSgen/Core/Rate.py @@ -4,6 +4,7 @@ from sortedcontainers import SortedList from eBCSgen.TS.State import Vector +from eBCSgen.utils import tree_to_string STATIC_MATH = """{}""" @@ -252,15 +253,3 @@ def fix_operator(self, node, matches): operator = self.operators[matches[1].type] return Tree(node, [matches[0], Token(matches[1].type, operator), matches[2]]) - -def tree_to_string(tree): - """ - Recursively constructs a list form given lark tree. - - :param tree: given lark tree - :return: list of components - """ - if type(tree) == Tree: - return sum(list(map(tree_to_string, tree.children)), []) - else: - return [str(tree)] diff --git a/eBCSgen/Parsing/ParseBCSL.py b/eBCSgen/Parsing/ParseBCSL.py index 03fb182..61308cd 100644 --- a/eBCSgen/Parsing/ParseBCSL.py +++ b/eBCSgen/Parsing/ParseBCSL.py @@ -27,6 +27,7 @@ from eBCSgen.Errors.ComplexParsingError import ComplexParsingError from eBCSgen.Errors.UnspecifiedParsingError import UnspecifiedParsingError from eBCSgen.Errors.RegulationParsingError import RegulationParsingError +from eBCSgen.utils import tree_to_string def load_TS_from_json(json_file: str) -> TransitionSystem: @@ -246,23 +247,12 @@ def regulation_def(self, matches): return matches[0] def regular(self, matches): - re = self.tree_to_regex_string(matches[1]) + re = "".join(tree_to_string(matches[1])) try: regex.compile(re) except regex.error as e: raise RegulationParsingError(f"Invalid regular expression: {re}. Error: {e}") return Regular(re) - - def tree_to_regex_string(self, tree): - regex_string = "" - - if isinstance(tree, Token): - return tree.value - elif isinstance(tree, Tree): - for child in tree.children: - regex_string += self.tree_to_regex_string(child) - - return regex_string def programmed(self, matches): successors = {k: v for x in matches for k, v in x.items()} diff --git a/eBCSgen/utils.py b/eBCSgen/utils.py new file mode 100644 index 0000000..11e6001 --- /dev/null +++ b/eBCSgen/utils.py @@ -0,0 +1,14 @@ +from lark import Tree + + +def tree_to_string(tree): + """ + Recursively constructs a list form given lark tree. + + :param tree: given lark tree + :return: list of components + """ + if type(tree) == Tree: + return sum(list(map(tree_to_string, tree.children)), []) + else: + return [str(tree)]