Skip to content

Commit 36555f1

Browse files
committed
Factorize the Ada file regular expression and turn free functions into class-methods
Do this to share as much logic as possible between test drivers.
1 parent 6bf5ad8 commit 36555f1

File tree

3 files changed

+116
-114
lines changed

3 files changed

+116
-114
lines changed

testsuite/drivers/base_driver.py

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,6 @@
2020
)
2121

2222

23-
_flag_pattern = re.compile(r"--\s*FLAG\s*(\((\d+)\))?\s*(.*)")
24-
_noflag_pattern = re.compile(r"--\s*NOFLAG")
25-
_ada_source_encodings = ['utf-8', 'iso-8859-1']
26-
27-
28-
def read_ada_file(file_name: str) -> tuple[list[str], str]:
29-
"""
30-
Read the given Ada file with an automatic encoding detection strategy.
31-
Returns a tuple containing the lines of the file and its encoding.
32-
33-
:param file_name: The Ada file to read.
34-
"""
35-
with open(file_name, mode='rb') as ada_file:
36-
ada_bytes = ada_file.read()
37-
for encoding in _ada_source_encodings:
38-
try:
39-
lines = ada_bytes.decode(encoding).split('\n')
40-
lines = lines[:-1] if not lines[-1] else lines
41-
return (lines, encoding)
42-
except ValueError as _:
43-
pass
44-
45-
4623
class Flags:
4724
"""
4825
Represents the flags in the source files.
@@ -125,6 +102,11 @@ class BaseDriver(DiffTestDriver):
125102
Common code for all test drivers.
126103
"""
127104

105+
flag_pattern = re.compile(r"--\s*FLAG\s*(\((\d+)\))?\s*(.*)")
106+
noflag_pattern = re.compile(r"--\s*NOFLAG")
107+
ada_file_pattern = r"[a-zA-Z][a-zA-Z0-9_\.\-]*\.(adb|ads|ada|ada_spec)"
108+
ada_source_encodings = ['utf-8', 'iso-8859-1']
109+
128110
perf_supported = False
129111
flag_checking_supported = False
130112

@@ -398,13 +380,13 @@ def count_source_flags(self) -> tuple[Flags, Flags]:
398380
# For each file, read it and parse the annotations
399381
for ada_file_name in ada_sources:
400382
base_name = P.basename(ada_file_name)
401-
ada_lines, _ = read_ada_file(ada_file_name)
383+
ada_lines, _ = self.read_ada_file(ada_file_name)
402384
for line_num, line in enumerate(ada_lines, 1):
403-
flag_search = _flag_pattern.search(line)
385+
flag_search = self.flag_pattern.search(line)
404386
if flag_search:
405387
count = int(flag_search.group(2)) if flag_search.group(2) else 1
406388
flag_annotations.add_flags(base_name, [line_num] * count)
407-
elif _noflag_pattern.search(line):
389+
elif self.noflag_pattern.search(line):
408390
noflag_annotations.add_flag(base_name, line_num)
409391

410392
# Return the tuple result
@@ -508,7 +490,7 @@ def add_missing_flags(self, missing_flags: Flags) -> None:
508490
if missing_flags.has_flags(base_name):
509491
# Get the file lines and encoding
510492
file_lines = []
511-
ada_lines, file_encoding = read_ada_file(ada_file_name)
493+
ada_lines, file_encoding = self.read_ada_file(ada_file_name)
512494
file_lines = [line.rstrip() for line in ada_lines]
513495

514496
# For each missing flag we add in in the file lines
@@ -520,7 +502,7 @@ def add_missing_flags(self, missing_flags: Flags) -> None:
520502
code, comment = split[0], split[1].strip() if len(split) == 2 else ""
521503

522504
# If there is already a "FLAG" annotation we have to count the number of it then rewrite it
523-
flag_search = _flag_pattern.search(f"-- {comment}")
505+
flag_search = self.flag_pattern.search(f"-- {comment}")
524506
if flag_search:
525507
count += int(flag_search.group(2) if flag_search.group(2) else 1)
526508
comment = flag_search.group(3)
@@ -536,6 +518,24 @@ def add_missing_flags(self, missing_flags: Flags) -> None:
536518
for line in file_lines:
537519
print(line, file=ada_file)
538520

521+
@classmethod
522+
def read_ada_file(cls, file_name: str) -> tuple[list[str], str]:
523+
"""
524+
Read the given Ada file with an automatic encoding detection strategy.
525+
Returns a tuple containing the lines of the file and its encoding.
526+
527+
:param file_name: The Ada file to read.
528+
"""
529+
with open(file_name, mode='rb') as ada_file:
530+
ada_bytes = ada_file.read()
531+
for encoding in cls.ada_source_encodings:
532+
try:
533+
lines = ada_bytes.decode(encoding).split('\n')
534+
lines = lines[:-1] if not lines[-1] else lines
535+
return (lines, encoding)
536+
except ValueError as _:
537+
pass
538+
539539
def _define_lkql_executables(self) -> None:
540540
# If the mode is JIT
541541
if self.env.options.mode == "jit":

testsuite/drivers/checker_driver.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ class CheckerDriver(BaseDriver):
2828
perf_supported = True
2929
flag_checking_supported = True
3030

31+
_flag_line_pattern = re.compile(
32+
rf"^({BaseDriver.ada_file_pattern}):(\d+):\d+: rule violation: .*$"
33+
)
34+
3135
def run(self) -> None:
3236
args = []
3337

@@ -93,17 +97,12 @@ def run(self) -> None:
9397
self.output += f.read()
9498

9599
def parse_flagged_lines(self, output: str) -> Flags:
96-
# Compile the pattern to match a checker output
97-
pattern = re.compile(
98-
r"^([a-zA-Z][a-zA-Z0-9_\-]*\.(adb|ads)):(\d+):\d+: rule violation: .*$"
99-
)
100-
101100
# Prepare the result
102101
res = Flags()
103102

104103
# For each line of the output search the groups in the line
105104
for line in output.splitlines():
106-
search_result = pattern.search(line)
105+
search_result = self._flag_line_pattern.search(line)
107106
if search_result is not None:
108107
(file, _, line_num) = search_result.groups()
109108
res.add_flag(file, int(line_num))

testsuite/drivers/gnatcheck_driver.py

Lines changed: 83 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -6,79 +6,6 @@
66
from drivers.base_driver import BaseDriver, Flags
77

88

9-
# --- GNATcheck output parsing functions
10-
11-
_flag_line_pattern = re.compile(
12-
r"^([a-zA-Z][a-zA-Z0-9_\.\-]*\.(adb|ads|ada|ada_spec)):(\d+):\d+: .*$"
13-
)
14-
15-
def _parse_full(output: str) -> Flags:
16-
"""
17-
Parse the full formatted gnatcheck output.
18-
"""
19-
# Prepare the result
20-
res = Flags()
21-
22-
# Parse the gnatcheck full output
23-
is_parsing = False
24-
for line in output.splitlines():
25-
if not is_parsing:
26-
is_parsing = "2. Exempted Coding Standard Violations" in line
27-
else:
28-
search_result = _flag_line_pattern.search(line)
29-
if search_result is not None:
30-
(file, _, line_num) = search_result.groups()
31-
res.add_flag(file, int(line_num))
32-
is_parsing = "5. Language violations" not in line
33-
34-
# Return the result
35-
return res
36-
37-
def _parse_short_and_brief(output: str) -> Flags:
38-
"""
39-
Parse the short formatted gnatcheck output.
40-
"""
41-
# Prepare the result
42-
res = Flags()
43-
44-
# Parse the output
45-
for line in output.splitlines():
46-
search_result = _flag_line_pattern.search(line)
47-
if search_result is not None:
48-
(file, _, line_num) = search_result.groups()
49-
res.add_flag(file, int(line_num))
50-
51-
# Return the result
52-
return res
53-
54-
def _parse_xml(output: str) -> Flags:
55-
"""
56-
Parse the xml formatted gnatcheck output.
57-
"""
58-
# Prepare the result
59-
res = Flags()
60-
61-
# Parse the xml result
62-
xml_tree = ET.fromstring(output)
63-
violations = xml_tree.find("violations")
64-
65-
# If the "violations" tag exists in the output, parse it as a full XML output
66-
if violations is not None:
67-
for violation in violations:
68-
file, line_num = violation.attrib["file"], int(violation.attrib["line"])
69-
res.add_flag(file, line_num)
70-
71-
# Else the ouput is a brief XML one
72-
else:
73-
for elem in xml_tree.findall("*"):
74-
if elem.tag in ("violation", "exemption-problem", "exempted-violation"):
75-
file, line_num = elem.attrib["file"], int(elem.attrib["line"])
76-
res.add_flag(file, line_num)
77-
78-
# Return the result
79-
return res
80-
81-
829
class GnatcheckDriver(BaseDriver):
8310
"""
8411
This driver runs gnatcheck with the given arguments and compares the output
@@ -191,12 +118,88 @@ class GnatcheckDriver(BaseDriver):
191118
"gnatkp": "gnatkp"
192119
}
193120
output_formats = set(['brief', 'full', 'short', 'xml'])
194-
parsers = {
195-
'full': _parse_full,
196-
'short': _parse_short_and_brief,
197-
'brief': _parse_short_and_brief,
198-
'xml': _parse_xml
199-
}
121+
122+
flag_line_pattern = re.compile(
123+
rf"^({BaseDriver.ada_file_pattern}):(\d+):\d+: (rule violation|warning|error): .*$"
124+
)
125+
126+
@classmethod
127+
def _parse_full(cls, output: str) -> Flags:
128+
"""
129+
Parse the full formatted GNATcheck output.
130+
"""
131+
# Prepare the result
132+
res = Flags()
133+
134+
# Parse the gnatcheck full output
135+
is_parsing = False
136+
for line in output.splitlines():
137+
if not is_parsing:
138+
is_parsing = "2. Exempted Coding Standard Violations" in line
139+
else:
140+
search_result = cls.flag_line_pattern.search(line)
141+
if search_result is not None:
142+
(file, _, line_num) = search_result.groups()
143+
res.add_flag(file, int(line_num))
144+
is_parsing = "5. Language violations" not in line
145+
146+
# Return the result
147+
return res
148+
149+
@classmethod
150+
def _parse_short_and_brief(cls, output: str) -> Flags:
151+
"""
152+
Parse the short formatted GNATcheck output.
153+
"""
154+
# Prepare the result
155+
res = Flags()
156+
157+
# Parse the output
158+
for line in output.splitlines():
159+
search_result = cls.flag_line_pattern.search(line)
160+
if search_result is not None:
161+
(file, _, line_num) = search_result.groups()
162+
res.add_flag(file, int(line_num))
163+
164+
# Return the result
165+
return res
166+
167+
@classmethod
168+
def _parse_xml(cls, output: str) -> Flags:
169+
"""
170+
Parse the XML formatted GNATcheck output.
171+
"""
172+
# Prepare the result
173+
res = Flags()
174+
175+
# Parse the xml result
176+
xml_tree = ET.fromstring(output)
177+
violations = xml_tree.find("violations")
178+
179+
# If the "violations" tag exists in the output, parse it as a full XML output
180+
if violations is not None:
181+
for violation in violations:
182+
file, line_num = violation.attrib["file"], int(violation.attrib["line"])
183+
res.add_flag(file, line_num)
184+
185+
# Else the ouput is a brief XML one
186+
else:
187+
for elem in xml_tree.findall("*"):
188+
if elem.tag in ("violation", "exemption-problem", "exempted-violation"):
189+
file, line_num = elem.attrib["file"], int(elem.attrib["line"])
190+
res.add_flag(file, line_num)
191+
192+
# Return the result
193+
return res
194+
195+
@classmethod
196+
def parsers(cls):
197+
return {
198+
'full': cls._parse_full,
199+
'short': cls._parse_short_and_brief,
200+
'brief': cls._parse_short_and_brief,
201+
'xml': cls._parse_xml
202+
}
200203

201204
@property
202205
def default_process_timeout(self):
@@ -525,4 +528,4 @@ def run_one_test(test_data: dict[str, any]) -> None:
525528

526529
def parse_flagged_lines(self, output: str, format: str) -> Flags:
527530
assert format in self.output_formats
528-
return self.parsers[format](output)
531+
return self.parsers()[format](output)

0 commit comments

Comments
 (0)