Как отмечалось выше, в действиях правил могут быть вызваны любые библиотечные функции системы программирования C и прикладные функции, определенные в секции подпрограмм. Кроме этого генератор LEX предоставляет стандартные функции
yymore,
yyless и
reject, которые также могут быть вызваны в действиях правил. Они обеспечивают специальные возможности по обработке внутренних переменных и управление входным потоком.
В обычной ситуации содержимое
символьного массива yytext, адресованного внутренней
переменной yytext, обновляется всякий раз, когда лексический анализатор распознает во входном потоке очередную лексему, соответствующую регулярному выражению правила. Однако, в некоторых случаях возникает потребность добавить к текущему содержимому массива yytext следующую последовательность символов входного потока. Для достижения этого результата в действии правила можно использовать стандартную функцию
yymore. Она не имеет аргументов и обеспечивает накопление символьной информации в массиве yytext, а также соответствующим образом увеличивает значение переменной yyleng, которая фиксирует его длину.
Другая стандартная функция
yyless позволяет получить обратный эффект. Она применяется, когда возникает потребность сохранить в массиве yytext не все, а только необходимое число символов распознанной последовательности. Количество сохраняемых символов устанавливает целочисленный аргумент функции
yyless. Остальные символы возвращаются обратно во входной поток для последующей обработки. При этом соответствующим образом редуцируется содержимое массива yytext и уменьшается значение переменной yyleng.
Совместное использование стандартных функций yymore и yyless иллюстрирует следующий пример лексического правила, которое обеспечивает обработку входной символьной информации, заключенной в кавычки, заменяя при этом ограничительные кавычки на апострофы.
%%
\"([^\\\n]|\\\")*\" {/* Обработка экранированной кавычки */
if(yytext[yyleng - 2] == '\\') {
yyless(yyleng - 1);
yyleng--;
yymore();
} /* if */
else { /* Обработка внешних кавычек */
yytext[0] = yytext[yyleng] - 1] = '\";
printf("%s", yytext);
} /* else */
}
Это правило ориентировано на обработку любой строки текста, ограниченной кавычками, в которой могут присутствовать внутренние кавычки, экранированные символом обратной дробной черты (\” ). Экранирование необходимо, когда внутренние кавычки являются частью текста и не должны рассматриваться как ограничитель строки, имеющий специальный смысл. Например, в следующей строке внутренний символ экранированной кавычки используется для обозначения размера в дюймах:
"Монитор марки NEC MultiSync XE17 имеет размер 17\" по диагонали."
При обработке этой строки по правилу рассматриваемого примера, сначала распознаются все символы от начальной до экранированной кавычки, включительно. Они заполняют массив yytext, который обрабатывается по альтернативе if действия правила. В процессе этой обработки вызов функции yyless возвращает во входной поток кавычку, завершающую массив yytext, неявно уменьшая его длину на один символ. Затем длина массива yytext уже явным образом уменьшается еще на один символ, чтобы исключит экранирующий символ обратной дробной черты, но уже без возврата его во входной поток. Действие правила по альтернативе if завершает вызов функции yymore, которая обеспечивает дополнение массива yytext оставшимися символами входной строки при следующем обращении к данному правилу. Таким образом, после выполнения действия по альтернативе if в массиве yytext будут находиться первые 49 символов рассматриваемой строки:
"Монитор марки NEC MultiSync XE17 имеет размер 17
В тоже время с учетом возврата кавычки во входном потоке остаются для последующей обработки 16 конечных символов исходной строки:
" по диагонали."
Конец исходной строки, также как ее начало соответствуют регулярному выражению рассматриваемого правила. Поэтому конечные символы строки добавляются к текущему содержимому массива yytext. Таким образом, в результате двукратного применения данного правила в массиве yytext будут сосредоточены все символы исходной строки, кроме экранирующей дробной черты перед внутренними кавычками:
"Монитор марки NEC MultiSync XE17 имеет размер 17" по диагонали."
Содержимое массива yytext обрабатывается по альтернативе else действия правила, обеспечивая стандартный вывод всех символов строки между ограничительными кавычками и замену ограничительных кавычек на апострофы. В результате исходная строка преобразуется к следующему виду:
'Монитор марки NEC MultiSync XE17 имеет размер 17" по диагонали.'
Следует отметить, что исходная (с кавычками) и результирующая (с апострофами) строки будут одинаково интерпретироваться командным процессором sh из OS UNIX, который игнорирует специальный смысл металитер внутри апострофов, но требует экранирования многих металитер внутри кавычек, чтобы исключить их специальный смысл. Кроме того, рассмотренное правило может быть практически полезно для преобразования символьных строк из формата языка C в формат языка Pascal.
Последняя из декларированных выше стандартных функций генератор LEX, функция
reject, которая обеспечивает принудительный переход к обработке полученных символов входного потока по следующему подходящему правилу. При этом символы, полученные по текущему правилу, в действии которого специфицирован вызов функции reject, возвращаются во входной поток и становятся доступны для последующей обработки.
Функция reject обычно применяется для поиска перекрывающихся объектов входного потока. Например, следующие правила обеспечивает поиск и отображение через дефис всех двухбуквенных сочетаний соседних букв во входном слове, которые начинаются на каждой букве:
%%
[a-zA-Z][a-zA-Z] {
printf("%s", yytext);
putchar('-');
reject();
}
[a-zA-Z]/[a-zA-Z] ;
. printf("\b ");
Первое правило отображает в потоке стандартного вывода текущую пару букв и символ дефис после нее, используя библиотечные функции printf и putchar системы программирования C. Функция reject в действии этого правила обеспечивает принудительный переход к обработке их по второму правилу с постусловием и пустым действием, которое исключает первую букву пары. Таким образом, обработка оставшейся части входного слова будет продолжена по первому правилу со второй буквы текущей пары. Последнее правило необходимо, чтобы подавить отображение дефиса, когда достигнут конец входного слова. Для этого в действии правила предусмотрен вызов библиотечной функции printf системы программирования C, аргумент которой обеспечивает возврат на одну позицию и заменяет пробелом текущий символ стандартного вывода.
В частности, при обработке по этим правилам входного слова SCANNER будут выделены следующие 6 пар символов: SC-CA-AN-NN-NE-ER. Если из блока действий первого правила исключить вызов функции reject, то при обработке того же входного слова будут обнаружены только 3 символьные пары: SC-AN-NE.
В заключение следует отметить, что во многих версиях генератор LEX стандартная функция reject реализована в форме макроса (оператора) REJECT. В этих версиях рассмотренный пример остается корректным при замене функции reject оператором REJECT.