Spec-Zone.ru › GCC 13 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 года идентификаторы могут содержать буквы, которые не являются частью «базового набора символов исходного текста», по усмотрению реализации (например, буквы с диакритическими знаками, греческие буквы или китайские идеограммы). Это можно сделать с помощью расширенного набора символов или эскейпов ‘\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-13.3.0/cpp/Tokenization.html

Spec-Zone.ru

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