Ваш код будет работать, но способ его работы может быть неожиданным и не очень общим.
В действительности, else
в вашем синтаксисе вводит в заблуждение. Можно было бы ожидать
if (condition1) { statement1; }
else if (condition2) { statement2; }
else { statement3; }
только для проверки condition2
, если condition1
ложно, и для выполнения statement3
только если оба условия ложны. Но ваш код не закорачивает. Каждый тест оценивается, и предложение else
является безусловным. Глядя на код, кажется, что else
не существует.
Однако код работает. Это почему? Это потому, что это выполняется задом наперед. По сути, оценка происходит так, как если бы вышесказанное было написано;
{ statement3; }
if (condition2) { statement2; }
if (condition1) { statement1; }
Это работает только потому, что все операторы ограничены установкой значения z
. Таким образом, последний оператор, который выполняет, выигрывает, и выполнение оценки в обратном направлении дает точно правильный ответ.
Так что все в порядке, если вы понимаете, как это работает. Но он не очень общий и совсем не эффективный.
Во-первых, как это работает. Грубо говоря, контекстно-свободная грамматика может представлять список двумя способами:
left_list : element | left_list element
right_list: element | element right_list
Эти две формулировки распознают одни и те же входные данные, но они представляют разные синтаксические анализаторы. В случае операторов мы называем их левой и правой ассоциативными. В случае списка разница заключается в том, как элементы добавляются в список: в left_list
последовательные элементы добавляются в конец списка, а в right_list
они добавляются в начало.
Если вы добавили действия для создания списка в вышеупомянутые произведения, вам нужно будет протолкнуть element
в конец left_list
, но сдвинуть element
в начало right_list
. Но дело не только в том, к какой стороне списка добавляется новый элемент. Это также порядок оценки побочных эффектов.
Важным выводом является то, что внутренний список полностью составлен в том месте, в котором он появляется в производстве. В случае left_list
эта конструкция происходит еще до того, как element
будет даже проанализирована, поэтому побочные эффекты в действии left_list
оцениваются слева направо. В случае right_list
построение происходит в конце, поэтому все оценки складываются до тех пор, пока окончательное производство right_list: element
не будет уменьшено. Только тогда можно будет оценить другие действия, поскольку они вытолкнуты из стека.
(Это не влияет на порядок побочных эффектов в действиях для element
. element
s всегда обрабатываются слева направо.)
В вашей грамматике есть два типа списка: сама программа и операторы if
. Программа представляет собой список правил слева, а побочные эффекты заключаются в действиях правил, которые оцениваются слева направо. Но операторы if
являются правыми списками предложений (где рекурсия заканчивается другим элементом, предложением else
), и побочные эффекты присутствуют в действиях списка. Вот почему он оценивает задом наперед.
Теперь, если намерение состоит в том, чтобы просто проанализировать входные данные один раз и оценить его, довольно ясно, что синтаксический анализ всего выражения и затем отбрасывание всего, кроме первого успешного условия, не совсем эффективен. Было бы намного лучше просто остановиться, когда возникнет истинное состояние. Кроме того, оценка слева направо и остановка, когда найдено истинное условие, позволит избежать проблем, когда побочные эффекты не просто устраняют предыдущие. Поэтому более общее решение не будет опираться на обратную оценку.
Кроме того, правильное рекурсивное производство действительно использует стек. Если у оператора if
есть много предложений, он будет использовать много стека анализатора. Может даже превышать ограничение стека синтаксического анализатора.