-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparser.py
More file actions
131 lines (106 loc) · 4.67 KB
/
Copy pathparser.py
File metadata and controls
131 lines (106 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import re
from typing import Any
from schemas import ZoneDTO, ConnectionDTO, MapDataDTO
class MapParser:
"""Parses map file into a validated MapDataDTO"""
def __init__(self, filepath: str) -> None:
"""Initializes the class object"""
self.filepath: str = filepath
def parse(self) -> MapDataDTO:
"""Parses and validates a map file, generates MapDataDTO"""
raw_data: dict[str, Any] = {
"nb_drones": 0,
"start_hub": None,
"end_hub": None,
"hubs": {},
"connections": {}
}
meta_regex: re.Pattern[str] = re.compile(r"(\w+)=([^\s\]]+)")
seen_connections: set[frozenset[str]] = set()
try:
with open(self.filepath, "r", encoding="utf-8") as f:
for line_idx, line in enumerate(f, start=1):
line = line.strip()
if not line or line.startswith("#"):
continue
try:
self._process_line(
line, raw_data, meta_regex, seen_connections
)
except Exception as e:
raise SyntaxError(
f"Parsing Error in '{self.filepath}' on "
f"line {line_idx}: {e}"
) from e
except FileNotFoundError as e:
raise FileNotFoundError(
f"CRITICAL: Map file '{self.filepath}' could not be "
"located on the disk."
) from e
# Validating graph structures
if not raw_data["nb_drones"]:
raise ValueError("File execution failed: "
"Missing 'nb_drones' parameter.")
if not raw_data["start_hub"]:
raise ValueError("Network topology invalid: "
"Missing one unique 'start_hub'.")
if not raw_data["end_hub"]:
raise ValueError("Network topology invalid: "
"Missing one unique 'end_hub'.")
return MapDataDTO(**raw_data)
def _process_line(
self,
line: str,
data: dict[str, Any],
meta_regex: re.Pattern[str],
seen_conn: set[frozenset[str]]
) -> None:
"""Parses lines into target dicts"""
parts: list[str] = line.split("[", 1)
base_def: str = parts[0].strip()
metadata: dict[str, Any] = {}
if len(parts) > 1:
meta_str: str = parts[1].rstrip("]")
for match in meta_regex.finditer(meta_str):
key, value = match.groups()
metadata[key] = int(value) if value.isdigit() else value
tokens: list[str] = base_def.split()
prefix: str = tokens[0]
if prefix == "nb_drones:":
data["nb_drones"] = int(tokens[1])
elif prefix in ("start_hub:", "end_hub:", "hub:"):
name, x, y = tokens[1], int(tokens[2]), int(tokens[3])
zone_dto = ZoneDTO(name=name, x=x, y=y, **metadata)
if prefix == "start_hub:":
if data["start_hub"] is not None:
raise ValueError("Topology rule violation: "
"Multiple start hubs declared.")
data["start_hub"] = zone_dto
elif prefix == "end_hub:":
if data["end_hub"] is not None:
raise ValueError("Topology rule violation: "
"Multiple goal hubs declared.")
data["end_hub"] = zone_dto
else:
if name in data["hubs"]:
raise ValueError(f"Duplicate identifier: "
f"Hub '{name}' already declared.")
data["hubs"][name] = zone_dto
elif prefix == "connection:":
nodes: list[str] = tokens[1].split("-")
if len(nodes) != 2:
raise ValueError("Connection definitions must adhere "
"strictly to 'zone1-zone2'.")
z1, z2 = nodes[0], nodes[1]
# Enforce link deduplication logic using
# unordered immutable frozen sets
conn_pair: frozenset[str] = frozenset([z1, z2])
if conn_pair in seen_conn:
raise ValueError(f"Duplicate connections "
f"detected between '{z1}' and '{z2}'.")
seen_conn.add(conn_pair)
conn_name: str = f"{z1}-{z2}"
data["connections"][conn_name] = ConnectionDTO(
zone1=z1, zone2=z2, **metadata)
else:
raise ValueError(f"Unrecognized syntax prefix: '{prefix}'.")