1616
1717__all__ = ["shlex" , "split" , "quote" ]
1818
19+
20+ class Token (str ):
21+ "A string with startline and endline attributes."
22+ __slots__ = ['startline' , 'endline' ]
23+
24+ def __new__ (cls , token , startline , endline ):
25+ self = super ().__new__ (cls , token )
26+ self .startline = startline
27+ self .endline = endline
28+ return self
29+
30+
1931class shlex :
2032 "A lexical analyzer class for simple shell-like syntaxes."
2133 def __init__ (self , instream = None , infile = None , posix = False ,
@@ -129,6 +141,18 @@ def get_token(self):
129141 def read_token (self ):
130142 quoted = False
131143 escapedstate = ' '
144+ startline = endline = self .lineno
145+
146+ def start_token (s ):
147+ self .token = s
148+ nonlocal startline , endline
149+ startline = endline = self .lineno
150+
151+ def continue_token (s ):
152+ self .token += s
153+ nonlocal endline
154+ endline = self .lineno
155+
132156 while True :
133157 if self .punctuation_chars and self ._pushback_chars :
134158 nextchar = self ._pushback_chars .pop ()
@@ -140,7 +164,7 @@ def read_token(self):
140164 print ("shlex: in state %r I see character: %r" % (self .state ,
141165 nextchar ))
142166 if self .state is None :
143- self . token = '' # past end of file
167+ start_token ( '' ) # past end of file
144168 break
145169 elif self .state == ' ' :
146170 if not nextchar :
@@ -160,20 +184,20 @@ def read_token(self):
160184 escapedstate = 'a'
161185 self .state = nextchar
162186 elif nextchar in self .wordchars :
163- self . token = nextchar
187+ start_token ( nextchar )
164188 self .state = 'a'
165189 elif nextchar in self .punctuation_chars :
166- self . token = nextchar
190+ start_token ( nextchar )
167191 self .state = 'c'
168192 elif nextchar in self .quotes :
169193 if not self .posix :
170- self . token = nextchar
194+ start_token ( nextchar )
171195 self .state = nextchar
172196 elif self .whitespace_split :
173- self . token = nextchar
197+ start_token ( nextchar )
174198 self .state = 'a'
175199 else :
176- self . token = nextchar
200+ start_token ( nextchar )
177201 if self .token or (self .posix and quoted ):
178202 break # emit current token
179203 else :
@@ -187,7 +211,7 @@ def read_token(self):
187211 raise ValueError ("No closing quotation" )
188212 if nextchar == self .state :
189213 if not self .posix :
190- self . token += nextchar
214+ continue_token ( nextchar )
191215 self .state = ' '
192216 break
193217 else :
@@ -197,7 +221,7 @@ def read_token(self):
197221 escapedstate = self .state
198222 self .state = nextchar
199223 else :
200- self . token += nextchar
224+ continue_token ( nextchar )
201225 elif self .state in self .escape :
202226 if not nextchar : # end of file
203227 if self .debug >= 2 :
@@ -209,7 +233,7 @@ def read_token(self):
209233 if (escapedstate in self .quotes and
210234 nextchar != self .state and nextchar != escapedstate ):
211235 self .token += self .state
212- self . token += nextchar
236+ continue_token ( nextchar )
213237 self .state = escapedstate
214238 elif self .state in ('a' , 'c' ):
215239 if not nextchar :
@@ -234,7 +258,7 @@ def read_token(self):
234258 continue
235259 elif self .state == 'c' :
236260 if nextchar in self .punctuation_chars :
237- self . token += nextchar
261+ continue_token ( nextchar )
238262 else :
239263 if nextchar not in self .whitespace :
240264 self ._pushback_chars .append (nextchar )
@@ -247,7 +271,7 @@ def read_token(self):
247271 self .state = nextchar
248272 elif (nextchar in self .wordchars or nextchar in self .quotes
249273 or self .whitespace_split ):
250- self . token += nextchar
274+ continue_token ( nextchar )
251275 else :
252276 if self .punctuation_chars :
253277 self ._pushback_chars .append (nextchar )
@@ -264,6 +288,8 @@ def read_token(self):
264288 self .token = ''
265289 if self .posix and not quoted and result == '' :
266290 result = None
291+ else :
292+ result = Token (result , startline , endline )
267293 if self .debug > 1 :
268294 if result :
269295 print ("shlex: raw token=" + repr (result ))
0 commit comments