move common code to __init__

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

View File

@ -2,7 +2,7 @@ import arpeggio
from arpeggio.cleanpeg import ParserPEG
from typing import List, Tuple, Sequence, Optional, Union
from common import grammar, JobShopVisitor, JobShopProblem
from . import grammar, JobShopVisitor, JobShopProblem
class JobShop2Visitor(JobShopVisitor):
pass
@ -17,9 +17,9 @@ def parse_jobshop2_file(filename: Union[str, bytes]) -> JobShopProblem:
return arpeggio.visit_parse_tree(parse_tree, JobShop2Visitor())
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__":
main()