В действительности, единственное существенное отличие этой последней версии парсера состоит в том, что он строит и использует деревья для вычисления выражения, вместо того чтобы вычислять его в ходе анализа. Промежуточное представление выражения является деревом экземпляров классов, форма которого отражает порядок выполнения операторов. В этом парсере имеется также логика, с помощью которой выводится листинг созданного дерева с отступами, если атрибут traceme установлен в значение True (или 1). Отступы указывают на вложенность поддеревьев, а для двухместных операторов сначала выводятся левые поддеревья. Например:
C:\…\PP4E\Lang> python
> >> from Parser import parser2
> >> p = parser2.Parser()
> >> p.traceme = True
> >> p.parse(‘5 + 4 * 2′)
[+]
…5
…[*]
….. 4
….. 2
13
При вычислении этого дерева метод apply рекурсивно вычисляет поддеревья и применяет корневые операторы к результатам. Здесь * вычисляется раньше +, так как находится ниже в дереве. Метод Factor поглощает подстроку * перед возвращением правого поддерева в Expr. Следующее дерево принимает иную форму:
> >> p.parse(‘5 * 4 — 2′)
[-]
…[*]
….. 5
….. 4
…2
18
В этом примере * вычисляется прежде -. Метод Factor выполняет обход подстроки выражений * и / перед возвращением результирующего левого поддерева в Expr. Следующий пример немного сложнее, но он следует тем же правилам:
> >> p.parse(‘1 + 3 * (2 * 3 + 4)’)
[+]
…1
…[*]
….. 3
….. [+]
……… [*]
……….. 2
……….. 3
…….. 4
31
Деревья состоят из вложенных экземпляров классов. С точки зрения ООП, это еще один способ применения композиции. Так как узлы дерева являются просто экземплярами классов, это дерево можно создать и вычислить вручную:
PlusNode( NumNode(1),
TimesNode( NumNode(3),
PlusNode( TimesNode(NumNode(2), NumNode(3)),
NumNode(4) ))).apply({})
Но точно так же можно позволить парсеру построить его (Python не настолько похож на Lisp, как вы, возможно, слышали).
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011