own directory on the import search path, though, so we have to use package-relative
import syntax for the latter case, and import from another directory when testing
interactively:
C:\...\PP4E\Lang\Parser> parser2.py
parser2 <class '__main__.Parser'>
5.0
...rest is same as for parser1...
C:\...PP4E\Lang\Parser> python
>>> import parser2
from .scanner import Scanner, SyntaxError, LexicalError # from PyTree
ValueError: Attempted relative import in non-package
C:\...\PP4E\Lang\Parser> cd ..
C:\...\PP4E\Lang> Parser\parser2.py
parser2 <class '__main__.Parser'>
5.0
...rest is same as for parser1...
C:\...\PP4E\Lang> python
>>> from Parser import parser2
>>> x = parser2.Parser()
>>> x.parse('1 + 2 * 3 + 4')
11
>>> import Parser.testparser
>>> Parser.testparser.interact(parser2.Parser)
<class 'Parser.parser2.Parser'>
Enter=> 4 * 3 + 5
17
Enter=> stop
>>>
Using full package import paths in parser2 instead of either package-relative or un-
qualified imports:
from PP4E.Lang.Parser import scanner
would suffice for all three use cases—script, and both same and other directory
imports—but requires the path to be set properly, and seems overkill for importing a
file in the same directory as the importer.
Parse Tree Structure
Really, the only tangible difference with this latest parser is that it builds and uses trees
to evaluate an expression internally instead of evaluating as it parses. The intermediate
representation of an expression is a tree of class instances, whose shape reflects the
order of operator evaluation. This parser also has logic to print an indented listing of
the constructed parse tree if the traceme attribute is set to True (or 1 ). Indentation gives
the nesting of subtrees, and binary operators list left subtrees first. For example:
C:\...\PP4E\Lang>
>>> from Parser import parser2
1454 | Chapter 19: Text and Language