В стандарте С++, где он указывает протокол интервалов для замены описателей категорий исходным кодом, который он представляет?

Если вы зададите вопрос, который считается слишком ничтожным, я долгое время пытался оправдать (как один пример того, что встречается во всем стандарте в разных контекстах) следующее определение integer literal в §2.14.2 стандарта С++ 11, особенно в отношении одной детали, наличие пробелов в самой синтаксической нотации.

(Обратите внимание, что этот пример - определение целочисленного литерала - не является точкой моего вопроса. Пункт моего вопроса - спросить о нотации описания синтаксиса, используемой самим стандартом С++, особенно в отношении пробелов между имена грамматической категории. Приведенный здесь пример - определение целочисленного литерала - специально выбран только потому, что он действует как простой и понятный пример.)

(Сокращенное для заключения, из п. 2.2.14):

integer-literal:
    decimal-literal integer-suffix_opt

decimal-literal:
    nonzero-digit
    decimal-literal digit

nonzero-digit и digit, как ожидалось, [0] 1... 9). (Примечание. Вышеупомянутый текст выделен курсивом в стандарте.)

Это все имеет смысл для меня, если предположить, что SPACE между описаниями описания синтаксиса decimal-literal и digit понимается как НЕ присутствующий в фактическом исходном коде, но присутствует только в самом описании синтаксиса, поскольку он появляется здесь, в разделе §2.14.2.

Это соглашение - помещение пробела между описаниями категорий в пределах обозначений, где понимается, что пространство не должно присутствовать в исходном коде, - используется в других местах спецификации. Пример здесь - это просто четкий случай, когда пространство явно не должно присутствовать в исходном коде. (См. Дополнение к этому вопросу для контрпримеров из стандарта, где пробелы или другие разделители /s должны присутствовать или не являются обязательными для описания описаний категорий, когда эти описатели категорий заменяются фактическими токенами в исходном коде.)

Опять же, рискуя быть ничтожным, я не могу найти нигде в стандарте утверждение соглашения о том, что пробелы НЕ должны присутствовать в исходном коде при интерпретации обозначений, таких как в этом примере.

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

В синтаксической нотации, используемой в этом Международном стандарте, синтаксический категории обозначаются курсивом, а буквальные слова и символов в типе постоянной ширины. Альтернативы перечислены на отдельных линий, за исключением нескольких случаев, когда отмечен длинный набор альтернатив по фразе "один из".

Я не был бы таким ничтожным; однако, я считаю, что обозначения, используемые в стандарте, несколько сложны, поэтому я хотел бы четко рассказать обо всех деталях. Я ценю любого, кто хочет потратить время, чтобы наполнить меня этим.

ДОБАВЛЕНИЕ В ответ на комментарии, в которых претензия сделана аналогично "очевидно, что пробелы не должны включаться в конечный исходный код, поэтому нет необходимости, чтобы стандарт явно указывал это",: Я выбрал тривиальный пример в этом вопросе, где это очевидно. В стандарте есть много случаев, когда это не очевидно без а. априорное знание языка (на мой взгляд), например, § 8.0.4, обсуждение "const" и "volatile":

cv-qualifier-seq:
    cv-qualifier cv-qualifier-seq_opt

... Обратите внимание на противоположное предположение здесь (пробел или другой разделитель или разделители требуется в конечном исходном коде), но это невозможно сделать из самой синтаксической нотации.

Существуют также случаи, когда пространство необязательно, например:

noptr-abstract-declarator:
    noptr-abstract-declarator_opt parameters-and-qualifiers

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

Я подозреваю, что комментарии по этим строкам - "это очевидно, так, что это должно быть" - являются результатом того, что выбранный мной пример настолько очевиден. Именно поэтому я выбрал этот пример.

Ответ 1

Как вы говорите, в стандарте говорится:

буквальные слова и символы в формате постоянной ширины

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


Хорошо, это был ответ "желающего языкового юриста"; кроме того, он не работает, потому что он не работает во всех постановках, которые имеют форму:

One of:
0 1 2 3 4 5 6 7 8 9

Я думаю, в действительности, ответ заключается в том, что пробелы не являются частью формальной грамматики, потому что она служит только для разделения токенов; кроме того, это утверждение в основном относится к самой грамматике, чьи жетоны разделены пробелами без этого пробела, являющегося токеном, за исключением того, что отступы в грамматике имеют значение, в отличие от отступов в программе.


Добавление для ответа на добавление

На самом деле не верно, что const и volatile нужно разделять пробелами. Они просто должны быть отдельными токенами. Пример:

#define A(x)x
A(const)A(volatile)A(int)A(x)A(;)

Снова, более серьезно, глава 2 (с особым упором на 2.2 и 2.5, но вы должны прочитать весь текст), описывают, как обрабатывается текст программы, чтобы создать поток токенов. Все правила, в которых вы претендуете на пустое пространство, должны быть проигнорированы, находятся в этой части грамматики, и все правила, в которых вы запрашиваете пробелы, могут не потребоваться.

Это действительно две отдельные грамматики, но лексическая грамматика обязательно неполна, потому что вам нужно рассмотреть работу препроцессора, чтобы применить ее.

Я считаю, что все, что я сказал, можно почерпнуть из стандарта. Вот несколько выдержек:

2.2 (3) Исходный файл разбивается на токены предварительной обработки (2.5) и последовательности символов пробела (включая комментарии) & hellip; Процесс деления символов исходных файлов на токены предварительной обработки зависит от контекста.

& hellip;

2.2 (7) Символы белого пространства, разделяющие токены, уже не являются значимыми. Каждый токен предварительной обработки преобразуется в токен. (2.7). Результирующие маркеры синтаксически и семантически анализируются и переводятся как единица перевода.

Я думаю, что все это дает понять, что есть два грамматики, один лексический - то есть он производит лексему (токен) из последовательности графем (символов) - и другой синтаксический - то есть, он создает абстрактное синтаксическое дерево из последовательности лексем (токенов). Ни в одном случае (с небольшим исключением, которое я получаю через минуту) пробел считается чем-то иным, чем что-то, что останавливает два лексема от столкновения друг с другом, если лексическая грамматика в противном случае допускает это. (См. Алгоритм в 2.5 (3).)

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

#define A(X)(X)

и

#define A (X)(X)

Директивы предварительной обработки имеют свои собственные правила синтаксического анализа, и это типично для определения:

<я > lparen:
  a ( символ, которому не сразу предшествует пробел

Это, я бы сказал, является исключением, которое доказывает правило [Примечание 1]. Тот факт, что необходимо сказать, что этому ( не предшествует белое пространство, показывает, что нормальное использование токена ( в синтаксическом правиле ничего не говорит о его бланкоспатическом контексте.

Итак, перефразируя Рэя Каммингса (а не Альберта Эйнштейна, как иногда утверждают), "время и белое пространство - это все, что отделяет один токен от другого". [Примечание 2]


[Примечание 1] Я использую фразу здесь в ее первоначальном юридическом смысле, согласно Цицерон.

[Примечание 2]:

"Время, - сказал Джордж, - почему я могу дать вам определение времени. Это то, что заставляет сразу все время".

Волна смеха шла вокруг маленькой группы людей.

"Совершенно так, - согласился химик." И, джентльмены, это не так забавно, как кажется. На самом деле это действительно не плохое научное определение. Время и пространство - это все, что отделяет одно событие от другого & hellip;

- От Человек, который освоил время, Рэй Каммингс, 1929, Ace Books. См. первая страница в книгах Google

Ответ 2

§2.7.1

Существует пять видов токенов: идентификаторы, ключевые слова, литералы, операторов и других разделителей. Заготовки, горизонтальные и вертикальные вкладки, новые строки, формы и комментарии (в совокупности - "пустое пространство" ), так как описанные ниже, игнорируются , за исключением того, что они служат для разделения токенов.

Итак, если литерал является токеном, а пробелы служат для разделения токенов, пробел между цифрами литерала будет интерпретироваться как два отдельных токена и, следовательно, не может быть частью одного и того же литерала.

Ответ 3

Я уверен, что в стандарте нет более прямого объяснения этого факта.

Используемая нотация достаточно похожа на типичный BNF, что они принимают многие из тех же общих условностей как само собой разумеющееся, в том числе тот факт, что пробелы в нотации не имеют значения после разделения токенов самого BNF - что если/когда пробелы имеет значение в исходном коде без разделения токенов, они будут включать в себя нотацию, чтобы указать его напрямую (например, для большинства директив предварительной обработки, new-line указывается напрямую:

# идентификатор ifdef new-line group opt

или

# включают < h- char -sequence > new-line

Вина за это, вероятно, восходит к стандарту Algol 68, который до сих пор зашел за рамки своих попыток точно определить синтаксис, который практически невозможно было читать без недельного обучения 1. С тех пор любое более поверхностное объяснение языка описания синтаксиса приводит к отказу на том основании, что он слишком сильно напоминает Алгол 68 и, несомненно, потерпит неудачу, потому что он слишком формальный, и никто никогда его не прочитает или не поймет.


1 Как это может быть так плохо, что вы спрашиваете? В основном это было так: они начали с официального описания английского языка описания синтаксиса. Это не использовалось для определения Algol 68, хотя оно использовалось для уточнения (еще более точно) другого языка описания синтаксиса. Этот второй язык описания синтаксиса затем использовался для указания синтаксиса самого алгоритма. Итак, вам нужно было изучить два отдельных языка описания синтаксиса, прежде чем вы могли бы сами начать читать синтаксис Algol 68. Как вы, несомненно, можете догадаться, почти никто никогда не делал.

Ответ 4

Стандарт фактически имеет две отдельные грамматики.

Грамматика препроцессора, описанная в разделах 2 и 16, определяет, как последовательность исходных символов преобразуется в последовательность токенов предварительной обработки и символов пробелов в фазах перевода 1-6. В некоторых из этих этапов и частей этой грамматики пробелы значительны.

Символы пробела, которые не являются частью токенов предварительной обработки, перестают быть значительными после фазы перевода 4. Стандарт явно говорит в начале фазы перевода 7, чтобы отменить пробельные символы между токенами предварительной обработки.

Языковая грамматика определяет, как последовательность токенов (преобразованных из токенов предварительной обработки) синтаксически и семантически интерпретируется на этапе 7 перевода. В этой грамматике нет такой вещи, как пробелы. (К этому моменту ' ' является символьным литералом, как и 'c'.)

В обоих грамматиках пространство между компонентами грамматики, видимыми в стандарте, не имеет ничего общего с исходными или исполняемыми символами пробела, оно просто для того, чтобы сделать текст разборчивым. Когда грамматика препроцессора зависит от пробелов, она произносит слова со словами:

c- char:

любой член исходного набора символов, кроме одиночной кавычки ', обратного слэша \ или символа новой строки

спусковое последовательность

универсального характера имя

и

Контроль строки:

...

# define идентификатор lparen identifier-list [opt] ) замена-список newline

...

lparen:

a ( символ, которому не сразу предшествует пробел

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

Другим важным правилом здесь является С++ 11 2.5p3:

Если входной поток проанализирован в токере предварительной обработки до заданного символа:

  • Если следующий символ начинает последовательность символов, которая может быть префиксом и начальной двойной кавычкой строкового литерала, например R", следующий токен предварительной обработки должен быть строковым литералом....

  • В противном случае, если следующие три символа <::, а последующий символ не является ни :, ни >, < рассматривается как токен препроцессора сам по себе, а не как первый символ альтернативный токен <:.

  • В противном случае следующий токен предварительной обработки - это самая длинная последовательность символов, которая может составлять токен предварительной обработки, даже если это приведет к сбою дальнейшего лексического анализа.

Таким образом, между токенами const и volatile должно быть пробелы, потому что в противном случае правило с самым длинным-токеном могло бы преобразовать это в токен идентификатора constvolatile.