Почему анализатор, сгенерированный ANTLR, повторно использует объекты контекста?
Это не так.Каждый вызов функции в вашем исходном коде будет соответствовать ровно одному FunctionCallContext
объекту, и он будет уникальным.Они должны были бы быть, даже для двух полностью идентичных вызовов функций, потому что они также содержат метаданные, например, где в источнике появляется вызов функции - и это, очевидно, будет отличаться между вызовами, даже если все остальное одинаково.
Чтобы проиллюстрировать это, рассмотрим следующий исходный код:
function f(x) {
return f(x);
}
print(f(x));
Это создаст дерево, содержащее ровно два объекта FunctionCallContext
- один для строки 2 и один для строки 4. Оба они будут обаотличаться - у них обоих будут дочерние узлы, ссылающиеся на имя функции f
и аргумент x
, но у них будет другая информация о местоположении и другой хэш-код - как и у дочерних узлов.Здесь ничего не используется повторно.
Что может вызвать проблему?
Тот факт, что вы видите один и тот же узел несколько раз, просто связан с тем, что выВы посещаете одну и ту же часть настоящего несколько раз.Это совершенно нормальная вещь для вашего варианта использования, но в вашем случае это вызывает проблему, потому что вы сохранили изменяемые данные в объекте, предполагая, что вы получите свежий FunctionCall
объект каждый раз, когда вызов функции происходит при запускевремя - а не каждый раз, когда в исходном коде появляется вызов функции.
Это не то, как работают деревья разбора (они представляют структуру исходного кода, а не последовательность вызовов, которые могут произойти во время выполнения),поэтому вы не можете использовать FunctionCallContext
объекты для хранения информации о конкретном вызове функции во время выполнения.В общем, я бы посчитал плохой идеей вставлять изменяемое состояние в объекты контекста.
Вместо этого вы должны поместить изменяемое состояние в объект посетителя.Для вашей конкретной проблемы это означает наличие стека вызовов, содержащего локальные переменные каждого вызова функции времени выполнения.Каждый раз, когда функция начинает выполнение, вы можете поместить кадр в стек, и каждый раз, когда функция выходит, вы можете выдвинуть ее.Таким образом, вершина стека всегда будет содержать локальные переменные выполняемой в данный момент функции.
PS: это не связано с вашей проблемой, но обычные правила приоритета в арифметических выражениях таковы, что+
имеет тот же приоритет, что и -
, а *
имеет тот же приоритет, что и /
.В вашей грамматике приоритет /
больше, чем *
, а приоритет -
выше +
.Это означает, что, например, 9 * 5 / 3
будет иметь значение 5
, когда оно должно быть 15
(при условии обычных правил для целочисленной арифметики).
Чтобы исправить это +
и -
, а также *
и /
должны быть частью одного и того же правила, поэтому они имеют одинаковый приоритет:
| left=numericexpression op=('*'|'/') right=numericexpression #MulOrDiv
| left=numericexpression op=('+'|'-') right=numericexpression #PlusOrMinus