move common code to __init__

This commit is contained in:
Trolli Schmittlauch 2017-06-27 13:27:56 +02:00
parent eb9ca0ed3c
commit 59cf66506f
4 changed files with 95 additions and 96 deletions

View file

@ -0,0 +1,90 @@
from typing import List, Tuple, Sequence, Optional, Union
import arpeggio
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
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
description = r"[^\n]*"
instance_name = r"\w+"
sep_line = trim_ws plus_line trim_ws eol
# lines out of multiple + signs
plus_line = r"\+\+\++"
# EOF is a builtin rule matching end of file
eof_sep = trim_ws plus_line " EOF " plus_line trim_ws eol* EOF
# entry point for jobshop2 input files
job_shop2 = problem_data EOF
problem_data = trim_ws num_jobs ' ' num_machines eol job_data+
# used for skipping arbitrary number of non-breaking whitespace
trim_ws = r'[ \t]*'
# git may change line-endings on windows, so we have to match on both
eol = "\n" / "\r\n"
nonneg_num = r'\d+'
num_jobs = nonneg_num
num_machines = nonneg_num
machine = nonneg_num
duration = nonneg_num
# task data for 1 job
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:
self.description = description
self.name = name
self.problem_data = problem_data
self.machines = machines
self.jobs = jobs
def __str__(self) -> str:
return 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:
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:
return int(node.value)
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:
return int(node.value)
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]]:
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:
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]))
def visit_problem_data(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> JobShopProblem:
if self.debug:
print("problem_data\nchildren:", children)
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))]
return JobShopProblem(children[0], children[1], problem_data)

View file

@ -1,90 +0,0 @@
from typing import List, Tuple, Sequence, Optional, Union
import arpeggio
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
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
description = r"[^\n]*"
instance_name = r"\w+"
sep_line = trim_ws plus_line trim_ws eol
# lines out of multiple + signs
plus_line = r"\+\+\++"
# EOF is a builtin rule matching end of file
eof_sep = trim_ws plus_line " EOF " plus_line trim_ws eol* EOF
# entry point for jobshop2 input files
job_shop2 = problem_data EOF
problem_data = trim_ws num_jobs ' ' num_machines eol job_data+
# used for skipping arbitrary number of non-breaking whitespace
trim_ws = r'[ \t]*'
# git may change line-endings on windows, so we have to match on both
eol = "\n" / "\r\n"
nonneg_num = r'\d+'
num_jobs = nonneg_num
num_machines = nonneg_num
machine = nonneg_num
duration = nonneg_num
# task data for 1 job
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:
self.description = description
self.name = name
self.problem_data = problem_data
self.machines = machines
self.jobs = jobs
def __str__(self) -> str:
return 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:
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:
return int(node.value)
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:
return int(node.value)
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]]:
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:
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]))
def visit_problem_data(self, node: arpeggio.ParseTreeNode, children: arpeggio.SemanticActionResults) -> JobShopProblem:
if self.debug:
print("problem_data\nchildren:", children)
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))]
return JobShopProblem(children[0], children[1], problem_data)

View file

@ -2,8 +2,7 @@ from arpeggio.cleanpeg import ParserPEG
from typing import List, Tuple, Sequence, Optional, Union from typing import List, Tuple, Sequence, Optional, Union
import arpeggio import arpeggio
from common import grammar, ParseError, JobShopProblem, JobShopVisitor from . import grammar, ParseError, JobShopProblem, JobShopVisitor
#from . import JobShopProblem
class JobShop1Visitor(JobShopVisitor): class JobShop1Visitor(JobShopVisitor):
"""instanciated for semantic analysis of parse_tree""" """instanciated for semantic analysis of parse_tree"""
@ -45,7 +44,7 @@ def parse_jobshop1_file(filename: Union[str, bytes]) -> List[JobShopProblem]:
def main(): def main():
print(parse_jobshop1_file("inputdata/jobshop1.txt")) print(parse_jobshop1_file("../inputdata/jobshop1.txt"))
parser = ParserPEG(grammar, "job_shop1", skipws=False) parser = ParserPEG(grammar, "job_shop1", skipws=False)

View file

@ -2,7 +2,7 @@ import arpeggio
from arpeggio.cleanpeg import ParserPEG from arpeggio.cleanpeg import ParserPEG
from typing import List, Tuple, Sequence, Optional, Union from typing import List, Tuple, Sequence, Optional, Union
from common import grammar, JobShopVisitor, JobShopProblem from . import grammar, JobShopVisitor, JobShopProblem
class JobShop2Visitor(JobShopVisitor): class JobShop2Visitor(JobShopVisitor):
pass pass
@ -17,9 +17,9 @@ def parse_jobshop2_file(filename: Union[str, bytes]) -> JobShopProblem:
return arpeggio.visit_parse_tree(parse_tree, JobShop2Visitor()) return arpeggio.visit_parse_tree(parse_tree, JobShop2Visitor())
def main(): def main():
print(parse_jobshop2_file('./inputdata/jobshop2/ta13').problem_data) print(type(parse_jobshop2_file('../inputdata/jobshop2/ta13').problem_data))
parser = ParserPEG(grammar, "problem_data", skipws=False) parser = ParserPEG(grammar, "job_shop2", skipws=False)
if __name__ == "__main__": if __name__ == "__main__":
main() main()