Spec-Zone.ru › GCC 12 CPP

1.3 Токенизация

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

В случае последовательности символов, имеющей более одного возможного токенизации, препроцессор действует жадно. Он всегда делает каждый токен, начиная слева, максимально большим перед переходом к следующему токену. Например, a+++++b интерпретируется как a ++ ++ + b, а не как a ++ + ++ b, даже если последняя токенизация могла бы быть частью допустимой программы C, а первая – нет.

После того, как входной файл разбивается на токены, границы токенов никогда не меняются, за исключением случая, когда оператор предварительной обработки ‘##’ используется для склеивания токенов. См. Конкатенацию. Например,

#define foo() bar
foo()baz
     → bar baz
not
     → barbaz

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

Токены предварительной обработки делятся на пять основных категорий: идентификаторы, числа предварительной обработки, строковые литералы, разделители и другие. Идентификатор такой же, как и идентификатор в C: любая последовательность букв, цифр или подчеркиваний, начинающаяся с буквы или подчеркивания. Ключевые слова C не имеют значения для препроцессора; они являются обычными идентификаторами. Вы можете определить макрос, имя которого является ключевым словом, например. Единственный идентификатор, который можно считать ключевым словом предварительной обработки, это defined. См. Определено.

Это в основном относится к другим языкам, использующим препроцессор C. Однако некоторые ключевые слова C++ имеют значение даже в препроцессоре. См. Именованные операторы C++.

В стандарте C 1999 года идентификаторы могут содержать буквы, которые не являются частью «основного набора символов исходного текста», по усмотрению реализации (например, буквы с диакритическими знаками, греческие буквы или китайские идеограммы). Это можно сделать с помощью расширенного набора символов или escape-последовательностей ‘\u’ и ‘\U’.

В качестве расширения GCC обрабатывает ‘$’ как букву. Это сделано для совместимости с некоторыми системами, такими как VMS, где ‘$’ обычно используется в именах системно определенных функций и объектов. ‘$’ не является буквой в строгом режиме соответствия или если вы укажете опцию -$. См. Вызов.

Число предварительной обработки имеет довольно странное определение. Эта категория включает все обычные целочисленные и вещественные константы, ожидаемые от C, но также и ряд других вещей, которые первоначально могут не распознаваться как числа. Формально, числа предварительной обработки начинаются с необязательной точки, обязательной десятичной цифры, а затем продолжаются любой последовательностью букв, цифр, подчеркиваний, точек и показателей степени. Показатели степени – это двухсимвольные последовательности ‘e+’, ‘e-’, ‘E+’, ‘E-’, ‘p+’, ‘p-’, ‘P+’, и ‘P-’. (Показатели степени, начинающиеся с ‘p’ или ‘P’, используются для шестнадцатеричных вещественных констант.)

Цель этого необычного определения заключается в изоляции препроцессора от всей сложности числовых констант. Ему не нужно различать лексически допустимые и недопустимые вещественные числа, что сложно. Это определение также позволяет разделить идентификатор в любой позиции и получить ровно два токена, которые затем могут быть склеены оператором ‘##’.

Числа предварительной обработки могут привести к неправильной интерпретации программ. Например, 0xE+12 – это число предварительной обработки, которое не переводится ни в одну допустимую числовую константу, поэтому возникает синтаксическая ошибка. Это не означает 0xE + 12, что, возможно, вы имели в виду.

Строковые литералы – это строковые константы, символьные константы и имена файлов заголовков (аргумент ‘#include’).2 Строковые константы и символьные константы просты: "…" или '…'. В обоих случаях вложенные кавычки должны быть экранированы обратной косой чертой: '\'' – это символьная константа для ‘'’.

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

Имена файлов заголовков либо выглядят как строковые константы "…", либо записываются в угловых скобках вместо этого, <…>. В любом случае обратная косая черта является обычным символом. Нет способа экранировать закрывающую кавычку или угловую скобку. Препроцессор ищет файл заголовка в разных местах в зависимости от используемой формы. См. Операция включения.

Ни один строковый литерал не может выходить за пределы конца строки. Вместо этого можно использовать продолженные строки или конкатенацию строковых констант.

Разделители – это все обычные знаки препинания, имеющие значение для C и C++. Все, кроме трех символов препинания в ASCII, являются разделителями C. Исключения – ‘@’, ‘$’ и ‘`’. Кроме того, все двух- и трехсимвольные операторы являются разделителями. Также существует шесть диграфов, которые стандарт C++ называет альтернативными токенами, которые просто представляют собой альтернативные способы написания других разделителей. Это вторая попытка обойти отсутствие знаков препинания в устаревших системах. В отличие от триграфов, это не имеет негативных последствий, но не охватывает такой большой объем. Диграфы и соответствующие обычные разделители:

Digraph:        <%  %>  <:  :>  %:  %:%:
Punctuator:      {   }   [   ]   #    ##

Любой другой одиночный байт считается «другим» и передается на вывод препроцессора без изменений. Компилятор C почти наверняка отклонит исходный код, содержащий токены «другие». В ASCII единственными символами «другие» являются ‘@’, ‘$’, ‘`’ и управляющие символы, отличные от NUL (все биты равны нулю). (Обратите внимание, что ‘$’ обычно считается буквой.) Все байты с установленным старшим битом (числовой диапазон 0x7F–0xFF), которые не были успешно интерпретированы как часть расширенного символа в кодировке ввода, также являются «другими» в данной реализации.

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

#define X^@1
#define X 1

(где ‘^@’ – это ASCII NUL). В строковых или символьных константах NUL сохраняются. В последнем двух случаях препроцессор выводит сообщение об ошибке.

Примечания

(2)

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

Далее: Язык предварительной обработки, Предыдущее: Первичная обработка, Вверх: Обзор [Оглавление][Индекс]

© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-12.1.0/cpp/Tokenization.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API