В шаблоне посетителя метод посещения обычно недействителен, поэтому я не могу получить код выражения из LHS и RHS. Хранение общих глобальных переменных не вариант, так как генерация кода выражения является рекурсивной, поэтому может стереть предыдущие значения, хранящиеся в переменных.
Вам нужно получать дочерние атрибуты, когда они посещаются, удерживать любые нужные вам атрибуты и обеспечивать, чтобы они оставались у вас, когда они вам нужны. Это может сделать внутреннюю структуру вашего посетителя немного более сложной, но это, безусловно, возможно. Генерация кода определенно является распространенным использованием шаблона Visitor.
Обычно вам не нужно держаться за атрибуты, но вам нужно держаться за промежуточные результаты и объединять их в другие промежуточные результаты при посещении других объектов. Я думаю, что это так, но взаимодействие достаточно сложное, чтобы немного запутать.
Я не эксперт по Object Pascal, поэтому вместо того, чтобы пытаться писать настоящий код, я просто опишу, как бы я справился с этим.
В этом случае я бы, вероятно, использовал стек, содержащий промежуточные результаты.
Порядок обхода можно задать в методах принятия узлов, в методах посещения посетителя или во внешнем итераторе. Для простоты я буду предполагать его в методах принятия.
В методе accept простых объектов вы просто выполняете стандарт visitor.visit(this)
(как бы вы это ни говорили в Object Pascal).
В методе посещения для более простых объектов, таких как TASTBooleanConstant
, вы вызываете соответствующий метод, в данном случае FBuilder.CreateConstant
со значениями, которые вы извлекаете из объекта и помещаете результат этого метода в стек посетителя.
В методе accept более сложного объекта, такого как TASTBinaryExpression
, вы сначала вызываете методы accept для потомков, а затем выполняете стандарт visitor.visit(this)
, гарантируя, что потомки посещаются первыми.
Затем, поскольку дети были посещены первыми, их результаты должны быть в стеке при вызове метода посещения сложного объекта. В этом методе посещения вы должны вытолкнуть соответствующие результаты из стека в локальные переменные, вызвать соответствующий метод FBuilder.CreateXxx
, основанный на том, какой у вас оператор, передав эти значения в качестве параметров, и поместить результат в стек.
Для объектов TASTUnaryExpression
это будет аналогично, но в методе accept есть только один дочерний элемент, и только один промежуточный результат выскочит из стека и будет использоваться в методе посещения.
В вашем клиентском коде вы создаете посетителя и вызываете метод accept вашего верхнего узла, передавая посетителю в качестве аргумента. После того, как вся рекурсия сделана, стек должен содержать только конечный результат, а класс посетителя должен предоставить метод getResult
, позволяющий клиенту получить его.
Извините, это так затянуто - в коде это может быть яснее, но, надеюсь, это даст вам представление о том, как с этим справиться.
Хорошим ресурсом для изучения того, как рефакторинг для введения шаблонов в существующий код, является книга Джошуа Кериевского Рефакторинг к шаблонам .