0001 # -*- coding: iso-8859-1 -*- 0002 """A lexical analyzer class for simple shell-like syntaxes.""" 0003 0004 # Module and documentation by Eric S. Raymond, 21 Dec 1998 0005 # Input stacking and error message cleanup added by ESR, March 2000 0006 # push_source() and pop_source() made explicit by ESR, January 2001. 0007 # Posix compliance, split(), string arguments, and 0008 # iterator interface by Gustavo Niemeyer, April 2003. 0009 0010 import os.path 0011 import sys 0012 from collections import deque 0013 0014 try: 0015 from cStringIO import StringIO 0016 except ImportError: 0017 from StringIO import StringIO 0018 0019 __all__ = ["shlex", "split"] 0020 0021 class shlex: 0022 "A lexical analyzer class for simple shell-like syntaxes." 0023 def __init__(self, instream=None, infile=None, posix=False): 0024 if isinstance(instream, basestring): 0025 instream = StringIO(instream) 0026 if instream is not None: 0027 self.instream = instream 0028 self.infile = infile 0029 else: 0030 self.instream = sys.stdin 0031 self.infile = None 0032 self.posix = posix 0033 if posix: 0034 self.eof = None 0035 else: 0036 self.eof = '' 0037 self.commenters = '#' 0038 self.wordchars = ('abcdfeghijklmnopqrstuvwxyz' 0039 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_') 0040 if self.posix: 0041 self.wordchars += ('ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ' 0042 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ') 0043 self.whitespace = ' \t\r\n' 0044 self.whitespace_split = False 0045 self.quotes = '\'"' 0046 self.escape = '\\' 0047 self.escapedquotes = '"' 0048 self.state = ' ' 0049 self.pushback = deque() 0050 self.lineno = 1 0051 self.debug = 0 0052 self.token = '' 0053 self.filestack = deque() 0054 self.source = None 0055 if self.debug: 0056 print 'shlex: reading from %s, line %d' \ 0057 % (self.instream, self.lineno) 0058 0059 def push_token(self, tok): 0060 "Push a token onto the stack popped by the get_token method" 0061 if self.debug >= 1: 0062 print "shlex: pushing token " + repr(tok) 0063 self.pushback.appendleft(tok) 0064 0065 def push_source(self, newstream, newfile=None): 0066 "Push an input source onto the lexer's input source stack." 0067 if isinstance(newstream, basestring): 0068 newstream = StringIO(newstream) 0069 self.filestack.appendleft((self.infile, self.instream, self.lineno)) 0070 self.infile = newfile 0071 self.instream = newstream 0072 self.lineno = 1 0073 if self.debug: 0074 if newfile is not None: 0075 print 'shlex: pushing to file %s' % (self.infile,) 0076 else: 0077 print 'shlex: pushing to stream %s' % (self.instream,) 0078 0079 def pop_source(self): 0080 "Pop the input source stack." 0081 self.instream.close() 0082 (self.infile, self.instream, self.lineno) = self.filestack.popleft() 0083 if self.debug: 0084 print 'shlex: popping to %s, line %d' \ 0085 % (self.instream, self.lineno) 0086 self.state = ' ' 0087 0088 def get_token(self): 0089 "Get a token from the input stream (or from stack if it's nonempty)" 0090 if self.pushback: 0091 tok = self.pushback.popleft() 0092 if self.debug >= 1: 0093 print "shlex: popping token " + repr(tok) 0094 return tok 0095 # No pushback. Get a token. 0096 raw = self.read_token() 0097 # Handle inclusions 0098 if self.source is not None: 0099 while raw == self.source: 0100 spec = self.sourcehook(self.read_token()) 0101 if spec: 0102 (newfile, newstream) = spec 0103 self.push_source(newstream, newfile) 0104 raw = self.get_token() 0105 # Maybe we got EOF instead? 0106 while raw == self.eof: 0107 if not self.filestack: 0108 return self.eof 0109 else: 0110 self.pop_source() 0111 raw = self.get_token() 0112 # Neither inclusion nor EOF 0113 if self.debug >= 1: 0114 if raw != self.eof: 0115 print "shlex: token=" + repr(raw) 0116 else: 0117 print "shlex: token=EOF" 0118 return raw 0119 0120 def read_token(self): 0121 quoted = False 0122 escapedstate = ' ' 0123 while True: 0124 nextchar = self.instream.read(1) 0125 if nextchar == '\n': 0126 self.lineno = self.lineno + 1 0127 if self.debug >= 3: 0128 print "shlex: in state", repr(self.state), \ 0129 "I see character:", repr(nextchar) 0130 if self.state is None: 0131 self.token = '' # past end of file 0132 break 0133 elif self.state == ' ': 0134 if not nextchar: 0135 self.state = None # end of file 0136 break 0137 elif nextchar in self.whitespace: 0138 if self.debug >= 2: 0139 print "shlex: I see whitespace in whitespace state" 0140 if self.token or (self.posix and quoted): 0141 break # emit current token 0142 else: 0143 continue 0144 elif nextchar in self.commenters: 0145 self.instream.readline() 0146 self.lineno = self.lineno + 1 0147 elif self.posix and nextchar in self.escape: 0148 escapedstate = 'a' 0149 self.state = nextchar 0150 elif nextchar in self.wordchars: 0151 self.token = nextchar 0152 self.state = 'a' 0153 elif nextchar in self.quotes: 0154 if not self.posix: 0155 self.token = nextchar 0156 self.state = nextchar 0157 elif self.whitespace_split: 0158 self.token = nextchar 0159 self.state = 'a' 0160 else: 0161 self.token = nextchar 0162 if self.token or (self.posix and quoted): 0163 break # emit current token 0164 else: 0165 continue 0166 elif self.state in self.quotes: 0167 quoted = True 0168 if not nextchar: # end of file 0169 if self.debug >= 2: 0170 print "shlex: I see EOF in quotes state" 0171 # XXX what error should be raised here? 0172 raise ValueError, "No closing quotation" 0173 if nextchar == self.state: 0174 if not self.posix: 0175 self.token = self.token + nextchar 0176 self.state = ' ' 0177 break 0178 else: 0179 self.state = 'a' 0180 elif self.posix and nextchar in self.escape and \ 0181 self.state in self.escapedquotes: 0182 escapedstate = self.state 0183 self.state = nextchar 0184 else: 0185 self.token = self.token + nextchar 0186 elif self.state in self.escape: 0187 if not nextchar: # end of file 0188 if self.debug >= 2: 0189 print "shlex: I see EOF in escape state" 0190 # XXX what error should be raised here? 0191 raise ValueError, "No escaped character" 0192 # In posix shells, only the quote itself or the escape 0193 # character may be escaped within quotes. 0194 if escapedstate in self.quotes and \ 0195 nextchar != self.state and nextchar != escapedstate: 0196 self.token = self.token + self.state 0197 self.token = self.token + nextchar 0198 self.state = escapedstate 0199 elif self.state == 'a': 0200 if not nextchar: 0201 self.state = None # end of file 0202 break 0203 elif nextchar in self.whitespace: 0204 if self.debug >= 2: 0205 print "shlex: I see whitespace in word state" 0206 self.state = ' ' 0207 if self.token or (self.posix and quoted): 0208 break # emit current token 0209 else: 0210 continue 0211 elif nextchar in self.commenters: 0212 self.instream.readline() 0213 self.lineno = self.lineno + 1 0214 if self.posix: 0215 self.state = ' ' 0216 if self.token or (self.posix and quoted): 0217 break # emit current token 0218 else: 0219 continue 0220 elif self.posix and nextchar in self.quotes: 0221 self.state = nextchar 0222 elif self.posix and nextchar in self.escape: 0223 escapedstate = 'a' 0224 self.state = nextchar 0225 elif nextchar in self.wordchars or nextchar in self.quotes \ 0226 or self.whitespace_split: 0227 self.token = self.token + nextchar 0228 else: 0229 self.pushback.appendleft(nextchar) 0230 if self.debug >= 2: 0231 print "shlex: I see punctuation in word state" 0232 self.state = ' ' 0233 if self.token: 0234 break # emit current token 0235 else: 0236 continue 0237 result = self.token 0238 self.token = '' 0239 if self.posix and not quoted and result == '': 0240 result = None 0241 if self.debug > 1: 0242 if result: 0243 print "shlex: raw token=" + repr(result) 0244 else: 0245 print "shlex: raw token=EOF" 0246 return result 0247 0248 def sourcehook(self, newfile): 0249 "Hook called on a filename to be sourced." 0250 if newfile[0] == '"': 0251 newfile = newfile[1:-1] 0252 # This implements cpp-like semantics for relative-path inclusion. 0253 if isinstance(self.infile, basestring) and not os.path.isabs(newfile): 0254 newfile = os.path.join(os.path.dirname(self.infile), newfile) 0255 return (newfile, open(newfile, "r")) 0256 0257 def error_leader(self, infile=None, lineno=None): 0258 "Emit a C-compiler-like, Emacs-friendly error-message leader." 0259 if infile is None: 0260 infile = self.infile 0261 if lineno is None: 0262 lineno = self.lineno 0263 return "\"%s\", line %d: " % (infile, lineno) 0264 0265 def __iter__(self): 0266 return self 0267 0268 def next(self): 0269 token = self.get_token() 0270 if token == self.eof: 0271 raise StopIteration 0272 return token 0273 0274 def split(s, comments=False): 0275 lex = shlex(s, posix=True) 0276 lex.whitespace_split = True 0277 if not comments: 0278 lex.commenters = '' 0279 return list(lex) 0280 0281 if __name__ == '__main__': 0282 if len(sys.argv) == 1: 0283 lexer = shlex() 0284 else: 0285 file = sys.argv[1] 0286 lexer = shlex(open(file), file) 0287 while 1: 0288 tt = lexer.get_token() 0289 if tt: 0290 print "Token: " + repr(tt) 0291 else: 0292 break 0293
Generated by PyXR 0.9.4