вательских программ. Рассмотрим подробнее способы оформления этих
разделов.
Все строки, в которых занята первая позиция, относятся к
Lex-программе. Любая строка, не являющаяся частью правила или
действия, которая начинается с пробела или табуляции, копируется
в сгенерированную программу lex.yy.c - результат работы Lex.
Раздел определений Lex-программы
Определения, предназначенные для Lex, помещаются перед пер-
вым %%. Любая строка этого раздела, не содержащаяся между %{ и %}
и начинающаяся в первой колонке, является определением строки
подстановки Lex. Раздел определений Lexпрограммы может включать:
- начальные условия;
- определения;
- фрагменты программы пользователя;
- таблицы наборов символов;
- указатели host-языка;
- изменения размеров внутренних массивов;
- комментарии в формате host-языка.
НАЧАЛЬНЫЕ УСЛОВИЯ задаются в форме:
%START имя1 имя2 ...
Если начальные условия определены, то эта строка должна быть
первой в Lex-программе.
ОПРЕДЕЛЕНИЯ задаются в форме:
имя трансляция
В качестве разделителя используется один или более пробелов
или табуляций. Имя - как обычно, любая последовательность букв и
цифр, начинающаяся с буквы. Трансляция - это регулярное выраже-
ние (или его часть), которое будет подставлено всюду там, где
указано имя (смотрите третью строку этого примера).
ФРАГМЕНТЫ ПРОГРАММЫ ПОЛЬЗОВАТЕЛЯ указываются двумя способами:
- в виде "пробел фрагмент";
- в виде %{ строки фрагмента программы пользователя %}
Такая форма включения пользовательского фрагмента необходи-
ма для ввода, например, макроопределений Си, которые должны начи-
наться в первой колонке строки. Все строки фрагмента пользова-
тельской программы, размещенные в разделе определений, будут яв-
ляться внешними для любой функции программы lex.yy.c
ТАБЛИЦА НАБОРОВ СИМВОЛОВ задается в виде:
%T
целое_число строка_символов
.........
целое_число строка_символов
%T
Сгенерированная программа lex.yy.c осуществляет ввод-вывод
символов посредством библиотечных функций Lex с именами input,
output, unput. Таким образом, Lex помещает в yytext символы в
представлении, используемом в этих библиотечных функциях. Для
внутреннего использования символ представляется целым числом,
значение которого образовано набором битов, представляющих сим-
вол в конкретной ЭВМ. Пользователю предоставляется возможность
менять представление символов (целых констант) с помощью таблицы
наборов символов. Если таблица символов присутствует в разделе
определений, то любой символ, появляющийся либо во входном пото-
ке, либо в правилах, должен быть определен в таблице символов.
Символам нельзя назначать число 0 и число, большее числа, выде-
ленного для внутреннего представления символов конкретной ЭВМ.
УКАЗАТЕЛЬ host-языка имеет вид:
%C для Си;
%R для Ратфора.
Если указатель host-языка отсутствует, то по умолчанию при-
нимается Си.
ИЗМЕНЕНИЯ РАЗМЕРА ВНУТРЕННИХ МАССИВОВ задаются в форме:
%x число
"число" - новый размер массива;
"x" - одна из букв p - позиции;
n - состояния;
e - узлы дерева;
a - упакованные переходы;
k - упакованные классы символов;
o - массив выходных элементов.
Lex имеет внутренние таблицы, размеры которых ограничены.
При построении программы лексического анализа может произойти пе-
реполнение любой из этих таблиц, о чем Lex сообщает при построе-
нии лексического анализатора. Пользователю предоставляется воз-
можность изменить размеры таблиц (сокращая размеры одних и увели-
чивая размеры других) таким образом, чтобы они не переполнялись.
Естественно, эти изменения возможны лишь в пределах той памяти,
которая выделяется под процесс.
Ниже перечислены размеры таблиц, которые устанавливаются по
умолчанию:
p - позиций 1500;
n - состояний 300;
e - узлов 600;
a - упакованных переходов 1500;
k - упакованных классов символов 1000;
o - выходных элементов 1500.
КОММЕНТАРИИ в разделе определений задаются в форме host-язы-
ка и должны начинаться не с первой колонки строки.
Раздел правил
Все, что указано после первой пары %% и до конца Lexпрограм-
мы или до второй пары %%, если она указана, относится к разделу
правил. Раздел правил может содержать правила и фрагменты прог-
рамм. Фрагменты программ, содержащиеся в разделе правил, стано-
вятся частью функции yylex файла lex.yy.c, в которой осущес-
твляется выполнение действий активных правил. Фрагмент прогрммы
указывается следующим образом:
%{ строки фрагмента программы %}
Раздел правил может включать список активных и неактивных
(помеченных) правил. Активные и неактивные правила могут быть
указаны в любом порядке, в том числе быть "перемешанными" в спис-
ке. Активные правила выполняются всегда, неактивные только по
ссылке на них оператором BEGIN.
Активное правило имеет вид:
ВЫРАЖЕНИЕ ДЕЙСТВИЕ
Неактивное правило имеет вид:
<МЕТКА>ВЫРАЖЕНИЕ ДЕЙСТВИЕ
или
<СПИСОК МЕТОК>ВЫРАЖЕНИЕ ДЕЙСТВИЕ
где СПИСОК_МЕТОК имеет вид:
метка1,метка2,...
В качестве первого правила раздела правил может быть прави-
ло вида:
BEGIN МЕТКА;
В этом правиле отсутствует ВЫРАЖЕНИЕ, и первым действием в
разделе правил будет активизация помеченных правил. Для возвраще-
ния автомата в исходное состояние можно использовать действие:
BEGIN 0;
Важно отметить следующее. Если Lex-программа содержит актив-
ные и неактивные правила, то активные правила работают всегда.
Оператор "BEGIN МЕТКА;" просто расширяет список активных правил,
активируя помеченные меткой МЕТКА. А оператор "BEGIN 0;" удаляет
из списка активных правил все помеченные правила, которые до это-
го были активированы. Кроме того, если из помеченного и активно-
го в данный момент времени правила осуществляется действие BEGIN
МЕТКА, то из помеченных правил активными останутся только те, ко-
торые помечены меткой МЕТКА.
Действия в правилах Lex-программы
Действие можно представлять либо как оператор Lex, например,
"BEGIN МЕТКА;", либо как оператор Си. Если имеется необходимость
выполнить достаточно большой набор преобразований, то действие
оформляют как блок Си-программы (он начинается открывающей фигур-
ной скобкой и завершается закрывающей фигурной скобкой), содержа-
щий необходимые фрагменты.
Действие в правиле указывается через не менее, чем один про-
бел или табуляцию после выражения (обязательно в той же строке,
где и выражение), а его продолжение может быть указано в следую-
щих строках только в том случае, если действие оформлено как блок
Си-программы.
Область действия переменных, об'явленных внутри блока, рас-
пространяется только на этот блок. Внешними переменными для всех
действий будут являться только те переменные, которые об'явлены в
разделе определений Lex-программы.
Действия в правилах Lex-программы выполняются, если правило
активно, и если автомат распознает цепочку символов из входного
потока как соответствующую регулярному выражению данного правила.
Однако, одно действие выполняется всегда - оно заключается в ко-
пировании входного потока символов в выходной. Это копирование
осуществляется для всех входных строк, которые не соответствуют
правилам, преобразующим эти строки. Комбинация символов, не уч-
тенная в правилах и появившаяся на входе, будет напечатана на вы-
ходе. Можно сказать, что действие - это то, что делается вместо
копирования входного потока символов на выход.
Когда необходимо вывести или преобразовать текст, соответ-
ствующий некоторому регулярному выражению, используется внешний
массив символов, который формирует Lex. Называется он yytext и
доступен в действиях правил. Например:
[A-Z]+ printf("%s",yytext);
По этому правилу распознается слово, содержащее прописные
латинские буквы и выводится с помощью printf, если оно выделено.
Операция вывода распознанного выражения используется очень часто,
поэтому имеется сокращенная форма записи этого действия: [A-Z]+
ECHO;
Результат действия этого правила будет аналогичен результа-
ту предыдущего примера. В выходном файле lex.yy.c ECHO определе-
но как макроподстановка:
#define ECHO fprintf(yyout, "%s",yytext);
Когда необходимо знать длину обнаруженной последовательнос-
ти символов, используется счетчик найденных символов yyleng, ко-
торый также доступен в действиях. Например:
[A-Z]+ printf("%c",yytext[yyleng-1]);
В этом примере будет выводится последний символ слова, соот-
ветствующего регулярному выражению [A-Z]+.
Рассмотрим еще один пример:
[A-Z]+ {число_слов++;число_букв += yyleng;}
Здесь ведется подсчет числа распознанных слов и количества
символов во всех словах.
Порядок действий активных правил
Список правил Lex-программы может содержать активные иеак-
тивные правила, размещенные в любом порядке в разделе правил. В
процессе работы лексического анализатора список активных правил
может видоизменяться за счет действий оператора BEGIN. В процес-
се распознавания символов входного потока может оказаться так,
что одна цепочка символов будет удовлетворять нескольким прави-
лам и, следовательно, возникает проблема: действие какого прави-
ла должно выполняться?
Для разрешения этого противоречия можно использовать кванто-
вание (разбиение) регулярных выражений этих правил Lex-программы
на такие новые регулярные выражения, которые дадут, по возможнос-
ти, однозначное распознавание лексемы. Однако, когда это не сде-
лано, Lex использует определенный детерминированный механизм раз-
решения такого противоречия:
- выбирается действие того правила, которое распознает наи-