Как разбирается условный оператор? - PullRequest
0 голосов
/ 30 мая 2020

Итак, cppreference утверждает:

Выражение в середине условного оператора (между? И :) анализируется так, как если бы оно заключено в скобки: его приоритет относительно? : игнорируется.

Однако мне кажется, что часть выражения после оператора ':' также анализируется, как если бы она была заключена в круглые скобки. Я пытался реализовать тернарный оператор на своем языке программирования (и вы можете увидеть результаты синтаксического анализа выражений здесь ), и мой синтаксический анализатор делает вид, что часть выражения после ':' также заключена в скобки. Например, для выражения (1?1:0?2:0)-1 интерпретатор моего языка программирования выводит 0, и это, похоже, совместимо с C. Например, программа C:

#include <stdio.h>

int main() {
    printf("%d\n",(1?1:0?2:0)-1);
}

Выводит 0. Если бы я запрограммировал синтаксический анализатор своего языка программирования, который при синтаксическом анализе тернарных операторов просто берет первый уже проанализированный узел после ':' и принимает его в качестве третьего операнда для '?:', Он будет выводить то же, что и ((1?1:0)?2:0)-1 , то есть 1. Мой вопрос: всегда ли это (делая вид, что выражение после ':' заключено в скобки) будет совместимо с C?

1 Ответ

1 голос
/ 30 мая 2020

«Делает вид, что заключен в круглые скобки» - это своего рода описание скобок оператора. Но, конечно, это нужно интерпретировать относительно отношений предшествования (включая ассоциативность). Таким образом, в a-b*c и a*b-c вычитание эффективно действует так, как будто его аргументы заключены в круглые скобки, только левый аргумент обрабатывается таким образом в a-b-c, и именно оператор сравнения вызывает группировку в a<b-c и a-b<c.

Я уверен, что вы все это знаете, поскольку ваш синтаксический анализатор, похоже, работает во всех этих случаях, но я говорю это, потому что тернарный оператор правоассоциативен и имеет более низкий приоритет, чем любой другой оператор [Примечание 1]. Это означает, что псевдокобки, налагаемые приоритетом оператора, окружают правый аргумент (независимо от его доминирующего оператора, поскольку все операторы имеют более высокий приоритет), а также левый аргумент, если его доминирующий оператор не является другим условным оператором. Но этого не произойдет в C, где оператор запятой имеет более низкий приоритет и не будет заключен в воображаемые круглые скобки после :.

Важно понимать, что подразумевается под приоритет сложного оператора. Фактически, чтобы вычислить отношения приоритета, мы сначала сворачиваем оператор до простого ?:, которое включает заключенный (второй) аргумент. Это не «как если бы выражение было заключено в скобки», потому что заключено в скобки . Он заключен в круглые скобки между ? и :, которые в данном контексте синтаксически являются круглыми скобками c.

В этом смысле он очень похож на обычный анализ оператора индекса как постфиксного оператора, хотя скобки оператора нижнего индекса заключают второй аргумент. Приоритет оператора нижнего индекса логически определяется тем, что он считается одним [], абстрагируя содержащееся внутри выражение. Это то же самое, что и оператор вызова функции. Это бывает написано в круглых скобках, но точные символы не важны: можно представить альтернативный язык, на котором вызовы функций записываются с другими символами, например { и }. Это никак не повлияет на грамматику.

Может показаться странным думать о ? и : как о «скобках c», поскольку они не выглядят скобки c. Но синтаксический анализатор не видит формы символов. Он удовлетворен тем, что ему говорят, что a ( закрывается ) и, в этом случае, ? закрывается :. [Примечание 2]

Сказав все это, я попробовал ваш компилятор с условным выражением

d = 0 ? 0 : n / d

Он правильно анализирует это выражение, но скомпилированный код вычисляет n / d раньше проверка истинности d = 0. Условный оператор должен работать не так; в этом случае это приведет к непредвиденному исключению деления на 0. Условный оператор должен сначала вычислить свой левый аргумент, а затем вычислить ровно одно из двух других выражений.

Примечания:

  1. В C это не совсем правильно. Оператор запятой имеет более низкий приоритет, и существует более сложное взаимодействие с операторами присваивания, которые логически имеют такой же приоритет и также являются правоассоциативными.

  2. В C -подобных языках эти символы не используются ни для каких других целей, так что нормально рассматривать их как странно выглядящие круглые скобки и оставить все как есть. Но, как показывает случай с оператором вызова функции (или, если уж на то пошло, с унарным оператором -), иногда можно повторно использовать символы оператора для более чем одной цели.

    Из любопытства, совсем не обязательно, чтобы открывающая и закрывающая круглые скобки были разными символами, если они не используются для каких-либо других целей. Так, например, если | не используется в качестве символа оператора (как в C), вы можете использовать | a | для обозначения абсолютного значения a без создания каких-либо двусмысленностей.

    Точный анализ обстоятельств, при которых повторное использование символа приводит к фактической двусмысленности, выходит за рамки этого ответа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...