1
- #!/usr/bin/env python
1
+ #!/usr/bin/env python3
2
2
3
3
# Copyright (C) 2023 Intel Corporation
4
4
# Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
12
12
# List of available special tags:
13
13
# {{OPT}} - makes content in the same line as the tag optional
14
14
# {{IGNORE}} - ignores all content until the next successfully matched line or the end of the input
15
+ # {{NONDETERMINISTIC}} - order of match rules isn't important - each (non OPT) input line is paired with a match line
16
+ # in any order
15
17
# Special tags are mutually exclusive and are expected to be located at the start of a line.
16
18
#
17
19
20
22
import re
21
23
from enum import Enum
22
24
25
+ ## @brief print a sequence of lines
26
+ def print_lines (lines , hint = None ):
27
+ counter = 1
28
+ for l in lines :
29
+ hint_char = " "
30
+ if hint == counter - 1 :
31
+ hint_char = ">"
32
+ print ("{}{:4d}| {}" .format (hint_char , counter , l .strip ()))
33
+ counter += 1
34
+
23
35
24
36
## @brief print the whole content of input and match files
25
- def print_content (input_lines , match_lines , ignored_lines ):
26
- print ("--- Input Lines " + "-" * 64 )
27
- print ( "" . join ( input_lines ). strip () )
28
- print ("--- Match Lines " + "-" * 64 )
29
- print ( "" . join ( match_lines ). strip () )
30
- print ("--- Ignored Lines " + "-" * 62 )
31
- print ( "" . join ( ignored_lines ). strip () )
37
+ def print_content (input_lines , match_lines , ignored_lines , hint_input = None , hint_match = None ):
38
+ print ("------ Input Lines " + "-" * 61 )
39
+ print_lines ( input_lines , hint_input )
40
+ print ("------ Match Lines " + "-" * 61 )
41
+ print_lines ( match_lines , hint_match )
42
+ print ("------ Ignored Lines " + "-" * 59 )
43
+ print_lines ( ignored_lines )
32
44
print ("-" * 80 )
33
45
34
46
@@ -39,6 +51,24 @@ def print_incorrect_match(match_line, present, expected):
39
51
print ("expected: " + expected )
40
52
41
53
54
+ ## @brief print missing match line
55
+ def print_input_not_found (input_line , input ):
56
+ print ("Input line " + str (input_line ) + " has no match line" )
57
+ print ("is: " + input )
58
+
59
+
60
+ ## @brief print missing input line
61
+ def print_match_not_found (match_line , input ):
62
+ print ("Match line " + str (match_line ) + " has no input line" )
63
+ print ("is: " + input )
64
+
65
+
66
+ ## @brief print general syntax error
67
+ def print_error (text , match_line ):
68
+ print ("Line " + str (match_line ) + " encountered an error" )
69
+ print (text )
70
+
71
+
42
72
## @brief pattern matching script status values
43
73
class Status (Enum ):
44
74
INPUT_END = 1
@@ -63,6 +93,7 @@ def check_status(input_lines, match_lines):
63
93
class Tag (Enum ):
64
94
OPT = "{{OPT}}" # makes the line optional
65
95
IGNORE = "{{IGNORE}}" # ignores all input until next match or end of input file
96
+ NONDETERMINISTIC = "{{NONDETERMINISTIC}}" # switches on "deterministic mode"
66
97
COMMENT = "#" # comment - line ignored
67
98
68
99
@@ -88,32 +119,53 @@ def main():
88
119
)
89
120
90
121
ignored_lines = []
122
+ matched_lines = set ()
91
123
92
124
input_idx = 0
93
125
match_idx = 0
94
126
tags_in_effect = []
127
+ deterministic_mode = False
95
128
while True :
96
129
# check file status
97
130
status = check_status (input_lines [input_idx :], match_lines [match_idx :])
98
- if (status == Status .INPUT_AND_MATCH_END ) or (status == Status .MATCH_END and Tag .IGNORE in tags_in_effect ):
99
- # all lines matched or the last line in match file is an ignore tag
100
- sys .exit (0 )
101
- elif status == Status .MATCH_END :
102
- print_incorrect_match (match_idx + 1 , input_lines [input_idx ].strip (), "" );
103
- print_content (input_lines , match_lines , ignored_lines )
104
- sys .exit (1 )
105
- elif status == Status .INPUT_END :
106
- # If we get to the end of the input, but still have pending matches,
107
- # then that's a failure unless all pending matches are optional -
108
- # otherwise we're done
109
- while match_idx < len (match_lines ):
110
- if not (match_lines [match_idx ].startswith (Tag .OPT .value ) or
111
- match_lines [match_idx ].startswith (Tag .IGNORE .value )):
112
- print_incorrect_match (match_idx + 1 , "" , match_lines [match_idx ]);
113
- print_content (input_lines , match_lines , ignored_lines )
131
+ if deterministic_mode :
132
+ if status == Status .INPUT_END :
133
+ # Convert the list of seen matches to the list of unseen matches
134
+ remaining_matches = set (range (len (match_lines ))) - matched_lines
135
+ for m in remaining_matches :
136
+ line = match_lines [m ]
137
+ if line .startswith (Tag .OPT .value ) or line .startswith (Tag .NONDETERMINISTIC .value ):
138
+ continue
139
+ print_match_not_found (m + 1 , match_lines [m ])
140
+ print_content (input_lines , match_lines , ignored_lines , hint_match = m )
114
141
sys .exit (1 )
115
- match_idx += 1
116
- sys .exit (0 )
142
+
143
+ sys .exit (0 )
144
+ elif status == Status .MATCH_END :
145
+ print_input_not_found (input_idx + 1 , input_lines [input_idx ])
146
+ print_content (input_lines , match_lines , ignored_lines , hint_input = input_idx )
147
+ sys .exit (1 )
148
+ else :
149
+ if (status == Status .INPUT_AND_MATCH_END ) or (status == Status .MATCH_END and Tag .IGNORE in tags_in_effect ):
150
+ # all lines matched or the last line in match file is an ignore tag
151
+ sys .exit (0 )
152
+ elif status == Status .MATCH_END :
153
+ print_incorrect_match (input_idx + 1 , input_lines [input_idx ].strip (), "" )
154
+ print_content (input_lines , match_lines , ignored_lines , hint_input = input_idx )
155
+ sys .exit (1 )
156
+ elif status == Status .INPUT_END :
157
+ # If we get to the end of the input, but still have pending matches,
158
+ # then that's a failure unless all pending matches are optional -
159
+ # otherwise we're done
160
+ while match_idx < len (match_lines ):
161
+ if not (match_lines [match_idx ].startswith (Tag .OPT .value ) or
162
+ match_lines [match_idx ].startswith (Tag .IGNORE .value ) or
163
+ match_lines [match_idx ].startswith (Tag .NONDETERMINISTIC .value )):
164
+ print_incorrect_match (match_idx + 1 , "" , match_lines [match_idx ])
165
+ print_content (input_lines , match_lines , ignored_lines , hint_match = match_idx )
166
+ sys .exit (1 )
167
+ match_idx += 1
168
+ sys .exit (0 )
117
169
118
170
input_line = input_lines [input_idx ].strip () if input_idx < len (input_lines ) else ""
119
171
match_line = match_lines [match_idx ]
@@ -122,7 +174,15 @@ def main():
122
174
if match_line .startswith (Tag .OPT .value ):
123
175
tags_in_effect .append (Tag .OPT )
124
176
match_line = match_line [len (Tag .OPT .value ):]
177
+ elif match_line .startswith (Tag .NONDETERMINISTIC .value ) and not deterministic_mode :
178
+ deterministic_mode = True
179
+ match_idx = 0
180
+ input_idx = 0
181
+ continue
125
182
elif match_line .startswith (Tag .IGNORE .value ):
183
+ if deterministic_mode :
184
+ print_error (r"Can't use \{{IGNORE\}} in deterministic mode" )
185
+ sys .exit (2 )
126
186
tags_in_effect .append (Tag .IGNORE )
127
187
match_idx += 1
128
188
continue # line with ignore tag should be skipped
@@ -137,20 +197,29 @@ def main():
137
197
pattern += part
138
198
139
199
# match or process tags
140
- if re .fullmatch (pattern , input_line ):
141
- input_idx += 1
142
- match_idx += 1
143
- tags_in_effect = []
144
- elif Tag .OPT in tags_in_effect :
145
- match_idx += 1
146
- tags_in_effect .remove (Tag .OPT )
147
- elif Tag .IGNORE in tags_in_effect :
148
- ignored_lines .append (input_line + os .linesep )
149
- input_idx += 1
200
+ if deterministic_mode :
201
+ if re .fullmatch (pattern , input_line ) and match_idx not in matched_lines :
202
+ input_idx += 1
203
+ matched_lines .add (match_idx )
204
+ match_idx = 0
205
+ tags_in_effect = []
206
+ else :
207
+ match_idx += 1
150
208
else :
151
- print_incorrect_match (match_idx + 1 , input_line , match_line .strip ())
152
- print_content (input_lines , match_lines , ignored_lines )
153
- sys .exit (1 )
209
+ if re .fullmatch (pattern , input_line ):
210
+ input_idx += 1
211
+ match_idx += 1
212
+ tags_in_effect = []
213
+ elif Tag .OPT in tags_in_effect :
214
+ match_idx += 1
215
+ tags_in_effect .remove (Tag .OPT )
216
+ elif Tag .IGNORE in tags_in_effect :
217
+ ignored_lines .append (input_line + os .linesep )
218
+ input_idx += 1
219
+ else :
220
+ print_incorrect_match (match_idx + 1 , input_line , match_line .strip ())
221
+ print_content (input_lines , match_lines , ignored_lines , hint_match = match_idx , hint_input = input_idx )
222
+ sys .exit (1 )
154
223
155
224
156
225
if __name__ == "__main__" :
0 commit comments