From 92ddb2740eaa5dc6bb376307d0a588c5f2b32498 Mon Sep 17 00:00:00 2001 From: Trolli Schmittlauch Date: Thu, 29 Jun 2017 21:14:39 +0200 Subject: [PATCH 1/5] JobShopProblem now a list, renaming module contributes to #3 --- src/{JobShopParser => Parser}/__init__.py | 10 +++++----- .../jobshop1_parser.py => Parser/js1_style.py} | 0 .../jobshop2_parser.py => Parser/js2_style.py} | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename src/{JobShopParser => Parser}/__init__.py (94%) rename src/{JobShopParser/jobshop1_parser.py => Parser/js1_style.py} (100%) rename src/{JobShopParser/jobshop2_parser.py => Parser/js2_style.py} (92%) diff --git a/src/JobShopParser/__init__.py b/src/Parser/__init__.py similarity index 94% rename from src/JobShopParser/__init__.py rename to src/Parser/__init__.py index 654ecb6..1238fa4 100644 --- a/src/JobShopParser/__init__.py +++ b/src/Parser/__init__.py @@ -1,4 +1,4 @@ -from typing import List, Tuple, Sequence, Optional, Union +from typing import List, Tuple, Sequence, Optional, Union, Any import arpeggio from itertools import chain @@ -40,7 +40,7 @@ class ParseError(Exception): def __init__(self, message: str) -> None: self.message = message -class JobShopProblem: +class JobShopProblem(list): def __init__(self, jobs: int, machines: int, problem_data: List[List[Tuple[int, int]]], name: str = 'unnamed', description: str = '') -> None: # check plausibility of input @@ -51,12 +51,12 @@ class JobShopProblem: self.description = description self.name = name - self.problem_data = problem_data self.machines = machines self.jobs = jobs + super().__init__(problem_data) def __str__(self) -> str: - return self.name + return "JobShopProblem " + str(self.name) class JobShopVisitor(arpeggio.PTNodeVisitor): """contains visitor functions needed for both jobshop1 (list of instances) @@ -94,7 +94,7 @@ class JobShopVisitor(arpeggio.PTNodeVisitor): print("problem_data\nchildren:", children) # filter out newlines or other strings cleaned_data = list(filter(lambda x: type(x) is not str, children)) - problem_data: List[List[Tuple(int, int)]] = [ cleaned_data[i] for i in range(2, len(cleaned_data))] + problem_data: List[List] = [ cleaned_data[i] for i in range(2, len(cleaned_data))] problem = JobShopProblem(children[0], children[1], problem_data) if self.debug: print("\nreturning a", type(problem), "\n") diff --git a/src/JobShopParser/jobshop1_parser.py b/src/Parser/js1_style.py similarity index 100% rename from src/JobShopParser/jobshop1_parser.py rename to src/Parser/js1_style.py diff --git a/src/JobShopParser/jobshop2_parser.py b/src/Parser/js2_style.py similarity index 92% rename from src/JobShopParser/jobshop2_parser.py rename to src/Parser/js2_style.py index a9399e9..25ae6c5 100644 --- a/src/JobShopParser/jobshop2_parser.py +++ b/src/Parser/js2_style.py @@ -2,7 +2,7 @@ import arpeggio from arpeggio.cleanpeg import ParserPEG from typing import List, Tuple, Sequence, Optional, Union -from . import grammar, JobShopVisitor, JobShopProblem +from . import grammar, JobShopVisitor, JobShopProblem, ParseError class JobShop2Visitor(JobShopVisitor): @@ -34,7 +34,7 @@ def parse_jobshop2_file(filename: Union[str, bytes]) -> JobShopProblem: return parse_jobshop2_string(inputdata) def main(): - print(type(parse_jobshop2_file('../inputdata/jobshop2/ta13'))) + print(parse_jobshop2_file('../inputdata/jobshop2/ta13')) parser = ParserPEG(grammar, "job_shop2", skipws=False) From 72e58006045d2afd851aad763e3e611fb53c3db0 Mon Sep 17 00:00:00 2001 From: Trolli Schmittlauch Date: Thu, 29 Jun 2017 22:23:12 +0200 Subject: [PATCH 2/5] now *really* renamed functions and modules contributes to #3 --- src/Parser/js1_style.py | 8 ++++---- src/Parser/js2_style.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Parser/js1_style.py b/src/Parser/js1_style.py index 48047b2..950a6ff 100644 --- a/src/Parser/js1_style.py +++ b/src/Parser/js1_style.py @@ -33,23 +33,23 @@ class JobShop1Visitor(JobShopVisitor): def visit_trim_ws(self, node:arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> None: return None -def parse_jobshop1_string(inputdata: str) -> JobShopProblem: +def parse_string(inputdata: str) -> JobShopProblem: """parse string with jobshop1-formatted data (multiple problem instances) and return list of JobShopProblem s""" parse_tree = parser.parse(inputdata) return arpeggio.visit_parse_tree(parse_tree, JobShop1Visitor()) -def parse_jobshop1_file(filename: Union[str, bytes]) -> List[JobShopProblem]: +def parse_file(filename: Union[str, bytes]) -> List[JobShopProblem]: """Open file with jobshop1-formatted data (multiple problem instances), parse it and return list of JobShopProblem s""" with open(filename) as datafile: inputdata: str = datafile.read() - return parse_jobshop1_string(inputdata) + return parse_string(inputdata) def main(): - print(parse_jobshop1_file("../inputdata/jobshop1.txt")) + print(parse_file("../inputdata/jobshop1.txt")) parser = ParserPEG(grammar, "job_shop1", skipws=False) diff --git a/src/Parser/js2_style.py b/src/Parser/js2_style.py index 25ae6c5..30bb545 100644 --- a/src/Parser/js2_style.py +++ b/src/Parser/js2_style.py @@ -14,7 +14,7 @@ class JobShop2Visitor(JobShopVisitor): print("returning a", type(problem)) return problem -def parse_jobshop2_string(inputdata: str) -> JobShopProblem: +def parse_string(inputdata: str) -> JobShopProblem: """parse string of jobshop2-formatted data (single problem instance, no name & description) and return JobShopProblem Raises a ParseError exception on errors""" @@ -24,17 +24,17 @@ def parse_jobshop2_string(inputdata: str) -> JobShopProblem: raise ParseError("Error while parsing problem input data at line {}, col {}:\n Rules {} did not match.".format(e.line, e.col, e.rules)) return arpeggio.visit_parse_tree(parse_tree, JobShop2Visitor()) -def parse_jobshop2_file(filename: Union[str, bytes]) -> JobShopProblem: +def parse_file(filename: Union[str, bytes]) -> JobShopProblem: """Open file with jobshop2-formatted data (single problem instance, no name & description), parse it and return JobShopProblem Raises a ParseError exception on errors""" with open(filename) as datafile: inputdata: str = datafile.read() - return parse_jobshop2_string(inputdata) + return parse_string(inputdata) def main(): - print(parse_jobshop2_file('../inputdata/jobshop2/ta13')) + print(parse_file('../inputdata/jobshop2/ta13')) parser = ParserPEG(grammar, "job_shop2", skipws=False) From 3edd920b0d3169ad1270d8ad5c48993485aaa42c Mon Sep 17 00:00:00 2001 From: Trolli Schmittlauch Date: Thu, 29 Jun 2017 22:28:23 +0200 Subject: [PATCH 3/5] improve type signature of visitor --- src/Parser/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser/__init__.py b/src/Parser/__init__.py index 1238fa4..be1a923 100644 --- a/src/Parser/__init__.py +++ b/src/Parser/__init__.py @@ -94,7 +94,7 @@ class JobShopVisitor(arpeggio.PTNodeVisitor): print("problem_data\nchildren:", children) # filter out newlines or other strings cleaned_data = list(filter(lambda x: type(x) is not str, children)) - problem_data: List[List] = [ cleaned_data[i] for i in range(2, len(cleaned_data))] + problem_data: List[List[Tuple[int, int]]] = [ cleaned_data[i] for i in range(2, len(cleaned_data))] problem = JobShopProblem(children[0], children[1], problem_data) if self.debug: print("\nreturning a", type(problem), "\n") From 40eaca47aea78785f847ca4661941b31a191fd07 Mon Sep 17 00:00:00 2001 From: Trolli Schmittlauch Date: Fri, 30 Jun 2017 00:51:50 +0200 Subject: [PATCH 4/5] adjust code style to follow PEP8 --- src/Parser/__init__.py | 45 +++++++++++++++++++++++++---------------- src/Parser/js1_style.py | 32 ++++++++++++++++++++--------- src/Parser/js2_style.py | 23 +++++++++++++++------ 3 files changed, 68 insertions(+), 32 deletions(-) diff --git a/src/Parser/__init__.py b/src/Parser/__init__.py index be1a923..db00174 100644 --- a/src/Parser/__init__.py +++ b/src/Parser/__init__.py @@ -1,4 +1,4 @@ -from typing import List, Tuple, Sequence, Optional, Union, Any +from typing import List, Tuple import arpeggio from itertools import chain @@ -7,7 +7,8 @@ __all__ = ["jobshop1_parser", "jobshop2_parser"] grammar = """ # starting point for jobshop1 input file job_shop1 = skip_preface - # eat away lines of preface, until first problem_instance is encountered; then the list of instances start + # eat away lines of preface, until first problem_instance is + # encountered; then the list of instances start skip_preface = (!problem_instance r"[^\n]+" skip_preface) / (eol skip_preface) / instance_list instance_list = problem_instance (sep_line trim_ws eol problem_instance eol?)* eof_sep problem_instance = trim_ws "instance" ' ' instance_name trim_ws eol trim_ws eol sep_line description eol problem_data @@ -31,18 +32,23 @@ grammar = """ machine = nonneg_num duration = nonneg_num # task data for 1 job - job_data = ' '* machine ' '+ duration (' '+ machine ' '+ duration)* trim_ws eol + job_data = ' '* machine ' '+ duration (' '+ machine ' '+ duration)* trim_ws eol """ + class ParseError(Exception): """To be thrown when parsing goes wrong""" def __init__(self, message: str) -> None: self.message = message + class JobShopProblem(list): - def __init__(self, jobs: int, machines: int, problem_data: List[List[Tuple[int, int]]], name: str = 'unnamed', description: str = '') -> None: + def __init__( + self, jobs: int, machines: int, + problem_data: List[List[Tuple[int, int]]], + name: str = 'unnamed', description: str = '') -> None: # check plausibility of input if len(problem_data) != jobs: raise ParseError("Given number of jobs for problem \"{}\" differs from actual job data.".format(name)) @@ -58,47 +64,52 @@ class JobShopProblem(list): def __str__(self) -> str: return "JobShopProblem " + str(self.name) + class JobShopVisitor(arpeggio.PTNodeVisitor): """contains visitor functions needed for both jobshop1 (list of instances) and jobshop2 (single instance without name & description) input data""" - def visit_nonneg_num(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> int: + def visit_nonneg_num( + self, node: arpeggio.ParseTreeNode, + children: arpeggio.SemanticActionResults) -> int: if self.debug: print("Converting non-negative integer", node.value) return int(node.value) - - def visit_machine(self, node:arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> int: + + def visit_machine(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> int: return int(node.value) - def visit_duration(self, node:arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> int: + def visit_duration(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> int: return int(node.value) - def visit_num_machines(self, node:arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> int: + def visit_num_machines(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> int: return int(node.value) - def visit_num_jobs(self, node:arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> int: + def visit_num_jobs(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> int: return int(node.value) - def visit_job_data(self, node:arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> List[Tuple[int, int]]: + def visit_job_data(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> List[Tuple[int, int]]: if self.debug: print("Job data:\nnode:", type(node), "children:", children) job_numbers = list(filter(lambda x: type(x) is int, children)) # job data needs to consist out of pairs of numbers - if len(job_numbers) % 2 ==1: + if len(job_numbers) % 2 == 1: raise ParseError("Odd number of numbers in job data") # returns list of (duration, machine) tuples - return list(zip(job_numbers[1::2], job_numbers[0::2])) # [::2] returns only every second element of the list + # [::2] returns only every second element of the list + return list(zip(job_numbers[1::2], job_numbers[0::2])) - def visit_problem_data(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> JobShopProblem: + def visit_problem_data( + self, node: arpeggio.ParseTreeNode, + children: arpeggio.SemanticActionResults) -> JobShopProblem: if self.debug: print("problem_data\nchildren:", children) # filter out newlines or other strings cleaned_data = list(filter(lambda x: type(x) is not str, children)) - problem_data: List[List[Tuple[int, int]]] = [ cleaned_data[i] for i in range(2, len(cleaned_data))] + problem_data: List[List[Tuple[int, int]]] = [ + cleaned_data[i] for i in range(2, len(cleaned_data))] problem = JobShopProblem(children[0], children[1], problem_data) if self.debug: print("\nreturning a", type(problem), "\n") print("problem_data:", problem_data) return problem - - diff --git a/src/Parser/js1_style.py b/src/Parser/js1_style.py index 950a6ff..1d4906e 100644 --- a/src/Parser/js1_style.py +++ b/src/Parser/js1_style.py @@ -1,13 +1,16 @@ from arpeggio.cleanpeg import ParserPEG -from typing import List, Tuple, Sequence, Optional, Union +from typing import List, Union import arpeggio -from . import grammar, ParseError, JobShopProblem, JobShopVisitor +from . import grammar, JobShopProblem, JobShopVisitor + class JobShop1Visitor(JobShopVisitor): """instanciated for semantic analysis of parse_tree""" - def visit_problem_instance(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> JobShopProblem: + def visit_problem_instance( + self, node: arpeggio.ParseTreeNode, + children: arpeggio.SemanticActionResults) -> JobShopProblem: if self.debug: print("problem_instance\nchildren:", children) problem: JobShopProblem = children[3] @@ -15,30 +18,40 @@ class JobShop1Visitor(JobShopVisitor): problem.description = children[2] return problem - def visit_instance_list(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> List[JobShopProblem]: + def visit_instance_list( + self, node: arpeggio.ParseTreeNode, + children: arpeggio.SemanticActionResults) -> List[JobShopProblem]: if self.debug: print("instance_list\nchildren:", children) return list(filter(lambda x: isinstance(x, JobShopProblem), children)) - def visit_skip_preface(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> List[JobShopProblem]: + def visit_skip_preface( + self, node: arpeggio.ParseTreeNode, + children: arpeggio.SemanticActionResults) -> List[JobShopProblem]: if self.debug: print("skip_preface\nchildren:", children) return list(filter(lambda x: type(x) is list, children))[0] # ignore eol nodes - def visit_eol(self, node:arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> None: + def visit_eol( + self, node: arpeggio.ParseTreeNode, + children: arpeggio.SemanticActionResults) -> None: return None - + # ignore trim_ws nodes - def visit_trim_ws(self, node:arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> None: + def visit_trim_ws( + self, node: arpeggio.ParseTreeNode, + children: arpeggio.SemanticActionResults) -> None: return None + def parse_string(inputdata: str) -> JobShopProblem: """parse string with jobshop1-formatted data (multiple problem instances) and return list of JobShopProblem s""" parse_tree = parser.parse(inputdata) return arpeggio.visit_parse_tree(parse_tree, JobShop1Visitor()) + def parse_file(filename: Union[str, bytes]) -> List[JobShopProblem]: """Open file with jobshop1-formatted data (multiple problem instances), parse it and return list of JobShopProblem s""" @@ -46,11 +59,12 @@ def parse_file(filename: Union[str, bytes]) -> List[JobShopProblem]: with open(filename) as datafile: inputdata: str = datafile.read() return parse_string(inputdata) - + def main(): print(parse_file("../inputdata/jobshop1.txt")) + parser = ParserPEG(grammar, "job_shop1", skipws=False) if __name__ == "__main__": diff --git a/src/Parser/js2_style.py b/src/Parser/js2_style.py index 30bb545..9972aaf 100644 --- a/src/Parser/js2_style.py +++ b/src/Parser/js2_style.py @@ -1,12 +1,15 @@ import arpeggio from arpeggio.cleanpeg import ParserPEG -from typing import List, Tuple, Sequence, Optional, Union +from typing import Union from . import grammar, JobShopVisitor, JobShopProblem, ParseError + class JobShop2Visitor(JobShopVisitor): - def visit_job_shop2(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> JobShopProblem: + def visit_job_shop2( + self, node: arpeggio.ParseTreeNode, + children: arpeggio.SemanticActionResults) -> JobShopProblem: if self.debug: print("job_shop2:\nchildren:", children) problem = children[0] @@ -14,18 +17,24 @@ class JobShop2Visitor(JobShopVisitor): print("returning a", type(problem)) return problem + def parse_string(inputdata: str) -> JobShopProblem: - """parse string of jobshop2-formatted data (single problem instance, no name & description) + """parse string of jobshop2-formatted data + (single problem instance, no name & description) and return JobShopProblem Raises a ParseError exception on errors""" try: parse_tree = parser.parse(inputdata) except arpeggio.NoMatch as e: - raise ParseError("Error while parsing problem input data at line {}, col {}:\n Rules {} did not match.".format(e.line, e.col, e.rules)) + raise ParseError( + "Error while parsing problem input data at line {}, col {}:\n" + "Rules {} did not match.".format(e.line, e.col, e.rules)) return arpeggio.visit_parse_tree(parse_tree, JobShop2Visitor()) + def parse_file(filename: Union[str, bytes]) -> JobShopProblem: - """Open file with jobshop2-formatted data (single problem instance, no name & description), + """Open file with jobshop2-formatted data + (single problem instance, no name & description), parse it and return JobShopProblem Raises a ParseError exception on errors""" @@ -33,9 +42,11 @@ def parse_file(filename: Union[str, bytes]) -> JobShopProblem: inputdata: str = datafile.read() return parse_string(inputdata) + def main(): print(parse_file('../inputdata/jobshop2/ta13')) - + + parser = ParserPEG(grammar, "job_shop2", skipws=False) if __name__ == "__main__": From 4804a2a4d5ab4b25056546572a2917b4726aca18 Mon Sep 17 00:00:00 2001 From: Trolli Schmittlauch Date: Fri, 30 Jun 2017 11:45:50 +0200 Subject: [PATCH 5/5] adjust __all__ packet imports to new module names contributes to #3 --- src/Parser/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser/__init__.py b/src/Parser/__init__.py index db00174..c463b13 100644 --- a/src/Parser/__init__.py +++ b/src/Parser/__init__.py @@ -2,7 +2,7 @@ from typing import List, Tuple import arpeggio from itertools import chain -__all__ = ["jobshop1_parser", "jobshop2_parser"] +__all__ = ["js1_style", "js2_style"] grammar = """ # starting point for jobshop1 input file