diff --git a/src/JobShopParser/jobshop2_parser.py b/src/JobShopParser/jobshop2_parser.py deleted file mode 100644 index a9399e9..0000000 --- a/src/JobShopParser/jobshop2_parser.py +++ /dev/null @@ -1,42 +0,0 @@ -import arpeggio -from arpeggio.cleanpeg import ParserPEG -from typing import List, Tuple, Sequence, Optional, Union - -from . import grammar, JobShopVisitor, JobShopProblem - -class JobShop2Visitor(JobShopVisitor): - - def visit_job_shop2(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> JobShopProblem: - if self.debug: - print("job_shop2:\nchildren:", children) - problem = children[0] - if self.debug: - print("returning a", type(problem)) - return problem - -def parse_jobshop2_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""" - 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)) - return arpeggio.visit_parse_tree(parse_tree, JobShop2Visitor()) - -def parse_jobshop2_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) - -def main(): - print(type(parse_jobshop2_file('../inputdata/jobshop2/ta13'))) - -parser = ParserPEG(grammar, "job_shop2", skipws=False) - -if __name__ == "__main__": - main() diff --git a/src/JobShopParser/__init__.py b/src/Parser/__init__.py similarity index 69% rename from src/JobShopParser/__init__.py rename to src/Parser/__init__.py index 654ecb6..c463b13 100644 --- a/src/JobShopParser/__init__.py +++ b/src/Parser/__init__.py @@ -1,13 +1,14 @@ -from typing import List, Tuple, Sequence, Optional, Union +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 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: - def __init__(self, jobs: int, machines: int, problem_data: List[List[Tuple[int, int]]], name: str = 'unnamed', description: str = '') -> None: +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 if len(problem_data) != jobs: raise ParseError("Given number of jobs for problem \"{}\" differs from actual job data.".format(name)) @@ -51,54 +57,59 @@ 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) 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/JobShopParser/jobshop1_parser.py b/src/Parser/js1_style.py similarity index 56% rename from src/JobShopParser/jobshop1_parser.py rename to src/Parser/js1_style.py index 48047b2..1d4906e 100644 --- a/src/JobShopParser/jobshop1_parser.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,41 +18,52 @@ 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: - return None - - # ignore trim_ws nodes - def visit_trim_ws(self, node:arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> None: + def visit_eol( + self, node: arpeggio.ParseTreeNode, + children: arpeggio.SemanticActionResults) -> None: return None -def parse_jobshop1_string(inputdata: str) -> JobShopProblem: + # ignore trim_ws nodes + 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_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 new file mode 100644 index 0000000..9972aaf --- /dev/null +++ b/src/Parser/js2_style.py @@ -0,0 +1,53 @@ +import arpeggio +from arpeggio.cleanpeg import ParserPEG +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: + if self.debug: + print("job_shop2:\nchildren:", children) + problem = children[0] + if self.debug: + 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) + 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)) + 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), + parse it and return JobShopProblem + Raises a ParseError exception on errors""" + + with open(filename) as datafile: + 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__": + main()