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__":