comparison cmd2.py @ 153:5c5c458a6b70

parsing going well
author catherine@dellzilla
date Fri, 21 Nov 2008 14:33:15 -0500
parents 693d11072e8e
children 606ad25c7f7e
comparison
equal deleted inserted replaced
152:693d11072e8e 153:5c5c458a6b70
197 """Lists single-key shortcuts available.""" 197 """Lists single-key shortcuts available."""
198 result = "\n".join('%s: %s' % (sc[0], sc[1]) for sc in self.shortcuts.items()) 198 result = "\n".join('%s: %s' % (sc[0], sc[1]) for sc in self.shortcuts.items())
199 self.stdout.write("Single-key shortcuts for other commands:\n%s\n" % (result)) 199 self.stdout.write("Single-key shortcuts for other commands:\n%s\n" % (result))
200 200
201 commentGrammars = pyparsing.Or([pyparsing.pythonStyleComment, pyparsing.cStyleComment]) 201 commentGrammars = pyparsing.Or([pyparsing.pythonStyleComment, pyparsing.cStyleComment])
202 commentGrammars.addParseAction(lambda x: '')
202 commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(pyparsing.stringEnd) 203 commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(pyparsing.stringEnd)
203 terminators = [';', '\n\n'] 204 terminators = [';', '\n\n']
204 argSeparatorPattern = pyparsing.Word(pyparsing.printables)('command') \ 205 multilineCommands = []
205 + pyparsing.SkipTo(pyparsing.StringEnd())('args')
206 filenamePattern = pyparsing.Word(pyparsing.alphanums + '#$-_~{},.!:\\/')
207 integerPattern = pyparsing.Word(pyparsing.nums).setParseAction( lambda s,l,t: [ int(t[0]) ] )
208 pipePattern = pyparsing.Literal('|')('pipe') + pyparsing.restOfLine('pipeTo')
209 redirectOutPattern = (pyparsing.Literal('>>') ^ '>')('output') \
210 + pyparsing.Optional(filenamePattern)('outputTo')
211 redirectInPattern = pyparsing.Literal('<')('input') \
212 + pyparsing.Optional(filenamePattern)('inputFrom')
213 punctuationPattern = pipePattern ^ redirectInPattern ^ redirectOutPattern
214
215 206
216 def _init_parser(self): 207 def _init_parser(self):
217 ''' 208 '''
218 >>> c = Cmd() 209 >>> c = Cmd()
210 >>> c.multilineCommands = ['multiline']
211 >>> c.caseInsensitive = True
212 >>> c._init_parser()
219 >>> print c.parser.parseString('barecommand').dump() 213 >>> print c.parser.parseString('barecommand').dump()
220 >>> print c.parser.parseString('command with args').dump() 214 ['barecommand', '']
215 - args:
216 - command: barecommand
217 - statement: ['barecommand', '']
218 - args:
219 - command: barecommand
220 >>> print c.parser.parseString('COMmand with args').dump()
221 ['command', ' with args']
222 - args: with args
223 - command: command
224 - statement: ['command', ' with args']
225 - args: with args
226 - command: command
221 >>> print c.parser.parseString('command with args and terminator; and suffix').dump() 227 >>> print c.parser.parseString('command with args and terminator; and suffix').dump()
228 ['command', 'with args and terminator', ';', ' and suffix']
229 - args: with args and terminator
230 - command: command
231 - statement: ['command', 'with args and terminator', ';']
232 - args: with args and terminator
233 - command: command
234 - terminator: ;
235 - suffix: and suffix
236 - terminator: ;
237 >>> print c.parser.parseString('simple | piped').dump()
238 ['simple', '', '|', ' piped']
239 - args:
240 - command: simple
241 - pipeDest: piped
242 - statement: ['simple', '']
243 - args:
244 - command: simple
222 >>> print c.parser.parseString('command with args, terminator;sufx | piped').dump() 245 >>> print c.parser.parseString('command with args, terminator;sufx | piped').dump()
223 >>> print c.parser.parseString('simple | piped').dump() 246 ['command', 'with args, terminator', ';', 'sufx', '|', ' piped']
247 - args: with args, terminator
248 - command: command
249 - pipeDest: piped
250 - statement: ['command', 'with args, terminator', ';']
251 - args: with args, terminator
252 - command: command
253 - terminator: ;
254 - suffix: sufx
255 - terminator: ;
224 >>> print c.parser.parseString('output into > afile.txt').dump() 256 >>> print c.parser.parseString('output into > afile.txt').dump()
257 ['output', ' into', '>', ' afile.txt']
258 - args: into
259 - command: output
260 - output: >
261 - outputDest: afile.txt
262 - statement: ['output', ' into']
263 - args: into
264 - command: output
225 >>> print c.parser.parseString('output into;sufx | pipethrume plz > afile.txt').dump() 265 >>> print c.parser.parseString('output into;sufx | pipethrume plz > afile.txt').dump()
266 ['output', 'into', ';', 'sufx', '|', ' pipethrume plz', '>', ' afile.txt']
267 - args: into
268 - command: output
269 - output: >
270 - outputDest: afile.txt
271 - pipeDest: pipethrume plz
272 - statement: ['output', 'into', ';']
273 - args: into
274 - command: output
275 - terminator: ;
276 - suffix: sufx
277 - terminator: ;
226 >>> print c.parser.parseString('output to paste buffer >> ').dump() 278 >>> print c.parser.parseString('output to paste buffer >> ').dump()
279 ['output', ' to paste buffer', '>>', '']
280 - args: to paste buffer
281 - command: output
282 - output: >>
283 - outputDest:
284 - statement: ['output', ' to paste buffer']
285 - args: to paste buffer
286 - command: output
227 >>> print c.parser.parseString('ignore the /* commented | > */ stuff;').dump() 287 >>> print c.parser.parseString('ignore the /* commented | > */ stuff;').dump()
228 >>> print c.parser.parseString('do not parse > when formally terminated;').dump() 288 ['ignore', 'the /* commented | > */ stuff', ';', '']
229 >>> print c.parser.parseString('do not parse > when formally terminated;').dump() 289 - args: the /* commented | > */ stuff
230 ''' 290 - command: ignore
291 - statement: ['ignore', 'the /* commented | > */ stuff', ';']
292 - args: the /* commented | > */ stuff
293 - command: ignore
294 - terminator: ;
295 - suffix:
296 - terminator: ;
297 >>> print c.parser.parseString('has > inside;').dump()
298 ['has', '> inside', ';', '']
299 - args: > inside
300 - command: has
301 - statement: ['has', '> inside', ';']
302 - args: > inside
303 - command: has
304 - terminator: ;
305 - suffix:
306 - terminator: ;
307 >>> print c.parser.parseString('multiline has > inside an unfinished command').dump()
308 ['multiline', 'has > inside an unfinished command']
309 - multilineCommand: multiline
310 >>> print c.parser.parseString('multiline has > inside;').dump()
311 ['multiline', 'has > inside', ';', '']
312 - args: has > inside
313 - multilineCommand: multiline
314 - statement: ['multiline', 'has > inside', ';']
315 - args: has > inside
316 - multilineCommand: multiline
317 - terminator: ;
318 - suffix:
319 - terminator: ;
320 >>> print c.parser.parseString('multiline command /* with comment in progress;').dump()
321 ['multiline', 'command /* with comment in progress;']
322 - multilineCommand: multiline
323 >>> print c.parser.parseString('multiline command /* with comment complete */ is done;').dump()
324 ['multiline', 'command /* with comment complete */ is done', ';', '']
325 - args: command /* with comment complete */ is done
326 - multilineCommand: multiline
327 - statement: ['multiline', 'command /* with comment complete */ is done', ';']
328 - args: command /* with comment complete */ is done
329 - multilineCommand: multiline
330 - terminator: ;
331 - suffix:
332 - terminator: ;
333 '''
231 outputParser = pyparsing.oneOf(['>>','>'])('output') 334 outputParser = pyparsing.oneOf(['>>','>'])('output')
232 terminatorParser = pyparsing.oneOf(self.terminators)('terminator') 335 terminatorParser = pyparsing.oneOf(self.terminators)('terminator')
233 stringEnd = pyparsing.stringEnd ^ '\nEOF' 336 stringEnd = pyparsing.stringEnd ^ '\nEOF'
234 command = pyparsing.Word(pyparsing.printables)('command') 337 multilineCommand = pyparsing.Or([pyparsing.Keyword(c, caseless=self.caseInsensitive) for c in self.multilineCommands])('multilineCommand')
338 oneLineCommand = pyparsing.Word(pyparsing.printables)('command')
339 afterElements = \
340 pyparsing.Optional('|' + pyparsing.SkipTo(outputParser ^ stringEnd)('pipeDest')) + \
341 pyparsing.Optional(outputParser + pyparsing.SkipTo(stringEnd)('outputDest'))
235 if self.caseInsensitive: 342 if self.caseInsensitive:
236 command.setParseAction(lambda x: x[0].lower()) 343 multilineCommand.setParseAction(lambda x: x[0].lower())
237 statementParser = \ 344 oneLineCommand.setParseAction(lambda x: x[0].lower())
238 (command + 345 self.parser = (
239 pyparsing.SkipTo(terminatorParser)('args') + 346 (((multilineCommand ^ oneLineCommand) + pyparsing.SkipTo(terminatorParser)('args') + terminatorParser)('statement') +
240 terminatorParser 347 pyparsing.SkipTo(outputParser ^ '|' ^ stringEnd)('suffix') + afterElements)
241 )('statement') ^ \ 348 ^
242 (command + 349 multilineCommand + pyparsing.SkipTo(pyparsing.stringEnd)
243 pyparsing.SkipTo(terminatorParser ^ '|' ^ outputParser ^ stringEnd)('args') + 350 ^
244 pyparsing.Optional(terminatorParser) 351 ((oneLineCommand + pyparsing.SkipTo(terminatorParser ^ stringEnd ^ '|' ^ outputParser)('args'))('statement') +
245 )('statement') 352 afterElements)
353 )
246 self.commentGrammars.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).setParseAction(lambda x: '') 354 self.commentGrammars.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).setParseAction(lambda x: '')
247 self.commentInProgress.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).ignore(pyparsing.cStyleComment) 355 self.commentInProgress.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).ignore(pyparsing.cStyleComment)
248 self.parser = statementParser + \
249 pyparsing.SkipTo(outputParser ^ '|' ^ stringEnd)('suffix') + \
250 pyparsing.Optional('|' + pyparsing.SkipTo(outputParser ^ stringEnd)('pipeDest')) + \
251 pyparsing.Optional(outputParser + pyparsing.SkipTo(stringEnd)('outputDest'))
252 self.parser.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).ignore(self.commentGrammars).ignore(self.commentInProgress) 356 self.parser.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).ignore(self.commentGrammars).ignore(self.commentInProgress)
253 357
254 def parsed(self, s, assumeComplete=False): 358 def parsed(self, s):
255 pass 359 result = self.parser.parseString(s)
256 ''' 360 result['command'] = result.multilineCommand or result.command
257 >>> c = Cmd() 361 result['cleanArgs'] = self.commentGrammars.transformString(result.args)
258 >>> r = c.parsed('quotes "are > ignored" < inp.txt') 362 result['statement'] = ' '.join(result.statement)
259 >>> r.statement, r.input, r.inputFrom, r.output, r.outputFrom
260 ('quotes "are > ignored" ', '<', 'inp.txt', '', '')
261 >>> r = c.parsed('very complex; < from.txt >> to.txt etc.')
262 >>> r.statement, r.terminator, r.input, r.inputFrom, r.output, r.outputTo
263 ('very complex;', ';', '<', 'from.txt', '>>', 'to.txt')
264 >>> c.parsed('nothing to parse').statement
265 'nothing to parse'
266 >>> r = c.parsed('ignore > within a terminated statement; > out.txt')
267 >>> r.statement, r.terminator, r.input, r.inputFrom, r.output, r.outputTo
268 ('ignore > within a terminated statement;', ';', '', '', '>', 'out.txt')
269 >>> r = c.parsed('send it to | sort | wc')
270 >>> r.statement, r.pipe, r.pipeTo
271 ('send it to ', '|', ' sort | wc')
272 >>> r = c.parsed('got from < thisfile.txt plus blah blah')
273 >>> r.statement, r.input, r.inputFrom
274 ('got from ', '<', 'thisfile.txt')
275 '''
276 if isinstance(s, pyparsing.ParseResults):
277 return s
278 result = (pyparsing.SkipTo(pyparsing.StringEnd()))("fullStatement").parseString(s)
279 s = self.commentGrammars.transformString(s)
280 command = s.split()[0]
281 if self.caseInsensitive:
282 command = command.lower()
283 result['command'] = command
284 if command in self.noSpecialParse:
285 result['statement'] = s
286 return result
287
288 if s[0] in self.shortcuts:
289 s = self.shortcuts[s[0]] + ' ' + s[1:]
290 result['statement'] = s
291 result['parseable'] = s
292 result += parseSearchResults(self.terminatorPattern, s)
293 if result.terminator:
294 result['statement'] = result.upToIncluding
295 result['unterminated'] = result.before
296 result['parseable'] = result.after
297 else:
298 # does not catch output marks
299 if (not assumeComplete) and (command in self.multilineCommands):
300 return result # don't bother with the rest, we're still collecting input
301 result += parseSearchResults(self.punctuationPattern, s)
302 result['statement'] = result['unterminated'] = result.before
303 result += parseSearchResults(self.pipePattern, result.parseable)
304 result += parseSearchResults(self.redirectInPattern, result.parseable)
305 result += parseSearchResults(self.redirectOutPattern, result.parseable)
306 result += parseSearchResults(self.argSeparatorPattern, result.statement)
307 if self.caseInsensitive:
308 result['command'] = result.command.lower()
309 result['statement'] = '%s %s' % (result.command, result.args)
310 return result 363 return result
311 364
312 def extractCommand(self, statement): 365 def extractCommand(self, statement):
313 try: 366 try:
314 (command, args) = statement.split(None,1) 367 (command, args) = statement.split(None,1)
822 def tearDown(self): 875 def tearDown(self):
823 if self.CmdApp: 876 if self.CmdApp:
824 self.outputTrap.tearDown() 877 self.outputTrap.tearDown()
825 878
826 if __name__ == '__main__': 879 if __name__ == '__main__':
827 doctest.testmod() 880 doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE)