[Python编程(第4版)].(Programming.Python.4th.Edition).Mark.Lutz.文字版

(yzsuai) #1

def Term(self):
if self.lex.token == 'num':
leaf = NumNode(self.lex.match('num'))
return leaf
elif self.lex.token == 'var':
leaf = VarNode(self.lex.value, self.lex.start)
self.lex.scan()
return leaf
elif self.lex.token == '(':
self.lex.scan()
tree = self.Expr()
self.lex.match(')')
return tree
else:
raise SyntaxError()


#################################################################################


self-test code: use my parser, parser1's tester


#################################################################################


if name == 'main':
import testparser
testparser.test(Parser, 'parser2') # run with Parser class here


Notice the way we handle undefined name exceptions in errorCheck. When exceptions
are derived from the built-in Exception class, their instances automatically return the
arguments passed to the exception constructor call as a tuple in their args attribute—
convenient for use in string formatting here.


Also notice that the new parser reuses the same scanner module as well. To catch errors
raised by the scanner, it also imports the specific classes that identify the scanner’s
exceptions. Both the scanner and the parser can raise exceptions on errors (lexical
errors, syntax errors, and undefined name errors). They’re caught at the top level of
the parser, and they end the current parse. There’s no need to set and check status flags
to terminate the recursion. Since math is done using integers, floating-point numbers,
and Python’s operators, there’s usually no need to trap numeric overflow or underflow
errors. But as is, the parser doesn’t handle errors such as division by zero—such Python
exceptions make the parser system exit with a Python stack trace and message. Un-
covering the cause and fix for this is left as suggested exercise.


When parser2 is run as a top-level program, we get the same test code output as
for parser1. In fact, it reuses the very same test code—both parsers pass in their parser
class object to testparser.test. And since classes are also objects, we can also pass
this version of the parser to testparser’s interactive loop:
testparser.interact(parser2.Parser).


The new parser’s external behavior is identical to that of the original, so I won’t repeat
all its output here (run this live for a firsthand look). Of note, though, this parser sup-
ports both use as a top-level script, and package imports from other directories, such
as the PyTree viewer we’ll use in a moment. Python 3.X no longer searches a module’s


Custom Language Parsers| 1453
Free download pdf