@@ -148,11 +148,43 @@ def close(self):
148
148
149
149
Command = namedtuple ('Command' , 'negated cmd args lineno' )
150
150
151
- LINE_PATTERN = re .compile (r'(?<=(?<!\S)@)(?P<negated>!?)(?P<cmd>[A-Za-z]+(?:-[A-Za-z]+)*)(?P<args>.*)$' )
151
+ # returns a generator out of the file object, which
152
+ # - removes `\\` then `\n` then a shared prefix with the previous line then optional whitespace;
153
+ # - keeps a line number (starting from 0) of the first line being concatenated.
154
+ def concat_multi_lines (f ):
155
+ lastline = None # set to the last line when the last line has a backslash
156
+ firstlineno = None
157
+ catenated = ''
158
+ for lineno , line in enumerate (f ):
159
+ line = line .rstrip ('\r \n ' )
160
+
161
+ # strip the common prefix from the current line if needed
162
+ if lastline is not None :
163
+ maxprefix = 0
164
+ for i in xrange (min (len (line ), len (lastline ))):
165
+ if line [i ] != lastline [i ]: break
166
+ maxprefix += 1
167
+ line = line [maxprefix :].lstrip ()
168
+
169
+ firstlineno = firstlineno or lineno
170
+ if line .endswith ('\\ ' ):
171
+ lastline = line [:- 1 ]
172
+ catenated += line [:- 1 ]
173
+ else :
174
+ yield firstlineno , catenated + line
175
+ lastline = None
176
+ firstlineno = None
177
+ catenated = ''
178
+
179
+ LINE_PATTERN = re .compile (r'''
180
+ (?<=(?<!\S)@)(?P<negated>!?)
181
+ (?P<cmd>[A-Za-z]+(?:-[A-Za-z]+)*)
182
+ (?P<args>.*)$
183
+ ''' , re .X )
152
184
def get_commands (template ):
153
185
with open (template , 'rUb' ) as f :
154
- for lineno , line in enumerate (f ):
155
- m = LINE_PATTERN .search (line . rstrip ( ' \r \n ' ) )
186
+ for lineno , line in concat_multi_lines (f ):
187
+ m = LINE_PATTERN .search (line )
156
188
if not m : continue
157
189
158
190
negated = (m .group ('negated' ) == '!' )
0 commit comments