Материал: LabWork2

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Генератор лексических анализаторов Flex

Существуют различные программные средства для решения задачи построения лексических анализаторов. Наиболее известным из них является Lex (в более поздних версиях –

Flex).

Программный инструментарий Flex позволяет определить лексический анализатор с помощью регулярных выражений для описания шаблонов токенов. Входные обозначения для Flex обычно называют языком Flex, а сам инструмент – компилятором Flex. Компилятор Flex преобразует входные шаблоны в конечный автомат и генерирует код (в файле с именем lex.yy.c), имитирующий данный автомат.

Рисунок 3. Схема использования Flex

На рисунке 3 показаны схема использования Flex и команды, соответствующие каждому этапу генерирования лексического анализатора. Входной файл input.l написан на языке Flex и описывает генерируемый лексический анализатор. Компилятор Flex преобразует input.l в программу на языке программирования Си (файл с именем lex.yy.c). При компиляции lex.yy.c необходимо прилинковать библиотеку Flex (-lfl). Этот файл компилируется в файл с именем а.out как обычно. Выход компилятора Си представляет собой работающий лексический анализатор, который на основе потока входных символов выдаёт поток токенов.

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

Структура программы на языке Flex имеет следующий вид:

Объявления

%%

Правила трансляции

%%

Вспомогательные функции

Обязательным является наличие правил трансляции, а, следовательно, и символов %% перед ними. Правила могут и отсутствовать в файле, но %% должны присутствовать всё равно.

Пример самого короткого файла на языке Flex:

%%

11

В этом случае входной поток просто посимвольно копируется в выходной. По умолчанию, входным является стандартный входной поток (stdin), а выходным – стандартный выходной

(stdout).

Раздел объявлений может включать объявления переменных, именованные константы и регулярные определения (например, digit [0-9] – регулярное выражение, описывающее множество цифр от 0 до 9). Кроме того, в разделе объявлений может помещаться символьный блок, содержащий определения на Си. Символьный блок всегда начинается с %{ и заканчивается %}. Весь код символьного блока полностью копируется в начало генерируемого файла исходного кода лексического анализатора.

Второй раздел содержит правила трансляции вида

Шаблон { Действие }

Каждый шаблон является регулярным выражением, которое может использовать регулярные определения из раздела объявлений. Действия представляют собой фрагменты кода, обычно написанные на языке программирования Си, хотя существуют и разновидности Flex для других языков программирования.

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

Листинг 2. Пример программы для подсчёта символов, слов и строк во введённом тексте

%{

int chars = 0; int words = 0; int lines = 0; %}

%%

[a-zA-Z]+ { words++; chars += strlen(yytext); }

\n

{ chars++;

lines++; }

.

{ chars++;

}

%%

 

 

int main(int argc, char **argv)

{

yylex();

printf("%8d%8d%8d\n", lines, words, chars); return 0;

}

Влистинге 2 определены все три раздела программы на Flex.

Впервом разделе объявлены три переменных-счётчика для символов, слов и строк,

соответственно. Эта часть кода будет полностью скопирована в файл lex.yy.c.

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

быть пробелов, табуляций и т.п., поскольку Flex рассматривает любую строку, начинающуюся с пробела, как код, который нужно скопировать в файл lex.yy.c.

В данном примере определены три шаблона:

1)[a-zA-Z]+ соответствует слову текста. В соответствии с этим шаблоном слово может содержать прописные и заглавные буквы латинского алфавита. А знак + означает, что слово может состоять из одного или нескольких символов, описанных перед +. В случае

12

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

2)\n соответствует символу перевода строки. В случае совпадения входного потока с данным шаблоном происходит увеличение счётчиков для символов и строк на 1;

3). является шаблоном для любого входного символа.

 

В функции main вызывается yylex() – функция, непосредственно выполняющая

лексический анализ входного текста.

 

Ниже приведены команды для компиляции и запуска программы на языке Flex для

подсчёта символов, слов и строк в тексте, введённом с клавиатуры.

$

flex words.l

 

 

$

gcc lex.yy.c -lfl

 

$

./a.out

 

 

To be, or not to be: that is the question

 

1

10

42

В таблице 4 перечислены специальные символы, использующиеся в регулярных выражениях (шаблонах) Flex.

Таблица 4. Специальные символы, использующиеся в регулярных выражениях

Flex

Символ шаблона

 

 

Значение

 

 

 

 

 

 

.

Соответствует любому символу, кроме \n

 

 

 

 

[]

Класс символов, соответствующий любому из символов, описанных

 

внутри скобок. Знак '-' указывает на диапазон символов. Например,

 

[0-9] означает то же самое, что и [0123456789], [a-z] – любая

 

прописная буква латинского алфавита, [A-z] – все заглавные и

 

прописные буквы латинского алфавита, а также 6 знаков пунктуации,

 

находящихся между Z и a в таблице ASCII. Если символ '-' или ']'

 

указан в качестве первого символа после открывающейся квадратной

 

скобки, значит он включается в описываемый класс символов.

 

Управляющие (escape) последовательности языка Си также могут

 

указываться внутри квадратных скобок, например, \t.

 

^

Внутри квадратных скобок используется как отрицание, например,

 

регулярное

выражение

[^\t\n]

соответствует

любой

 

последовательности символов, не содержащей табуляций и

 

переводов строки.

 

 

 

 

Если просто используется в начале шаблона, то означает начало

 

строки.

 

 

 

 

$

При использовании в конце регулярного выражения означает конец

 

строки.

 

 

 

 

{}

Если в фигурных скобках указаны два числа, то они

 

интерпретируются как минимальное и максимальное количество

 

повторений шаблона, предшествующего скобкам. Например, A{1,3}

13

Таблица 4. Специальные символы, использующиеся в регулярных выражениях

Flex

Символ шаблона

 

 

Значение

 

 

 

 

 

соответствует повторению буквы А от одного до трёх раз, а 0{5}

 

00000. Если внутри скобок находится имя регулярного определения,

 

то это просто обращение к данному определению по его имени.

\

Используется в escape-последовательностях языка Си и для задания

 

метасимволов, например, \* – символ ‘*’ в отличие от * (см. ниже).

*

Повторение регулярного выражения, указанного до *, 0 или более

 

раз. Например, [ \t]* соответствует регулярному выражению для

 

пробелов и/или табуляций, отсутствующих или повторяющихся

 

несколько раз.

 

 

 

+

Повторение регулярного выражения, указанного до +, один или более

 

раз. Например, [0-9]+ соответствует строкам 1, 111 или 123456.

?

Соответствует повторению регулярного выражения, указанного до ?,

 

0 или 1 раз. Например, -?[0-9]+ соответствует знаковым числам с

 

необязательным минусом перед числом.

 

 

|

Оператор «или». Например, true|false соответствует любой из

 

двух строк.

 

 

 

()

Используются для группировки нескольких регулярных выражений в

 

одно.

Например,

a(bc|de)

соответствует

входным

 

последовательностям: abc или ade.

 

 

/

Так называемый присоединенный контекст. Например, регулярное

 

выражение 0/1 соответствует 0 во входной строке 01, но не

 

соответствует ничему в строках 0 или 02.

 

“ ”

Любое символы в кавычках рассматриваются как строка символов.

 

Метасимволы, такие как \*, теряют своё значение и

 

интерпретируются как два символа: \ и *.

 

Лексический анализатор на языке Flex для грамматики из предыдущего раздела представлен в листинге 3.

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

%option имя_опции

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

--имя_опции

Для отключения опции перед её именем следует указать «no», как в случае с noyywrap. Полный список допустимых опций можно найти в документации по Flex [6, 8].

Первые версии генератора лексических анализаторов Lex вызывали функцию yywrap() при достижении конца входного потока yyin. В случае, если нужно было продолжить анализ входного текста из другого файла, yywrap возвращала 0 для продолжения сканирования. В противном случае возвращалась 1.

14

Листинг 3. Лексический анализатор на языке Flex

%option noyywrap yylineno %{

#include <stdio.h> int ch;

%}

digit[0-9] letter[a-zA-Z] delim[();] oper[<>=]

ws[ \t\n]

%%

for { printf("KEYWORD (%d, %d): %s\n", yylineno, ch, yytext); ch += yyleng;

}

do { printf("KEYWORD (%d, %d): %s\n", yylineno, ch, yytext); ch += yyleng;

}

("_"|{letter})("_"|{letter}|{digit})* {

printf("IDENTIFIER (%d, %d): %s\n", yylineno, ch, yytext); ch += yyleng;

}

[-+]?({digit}*\.{digit}+|{digit}+\.|{digit}+) ([eE][-+]?{digit}+)?[flFL]? {

printf("NUMBER (%d, %d): %s\n", yylineno, ch, yytext); ch += yyleng;

}

{oper} { printf("OPERATION (%d, %d): %s\n", yylineno, ch, yytext); ch += yyleng;

}

":=" { printf("OPERATION (%d, %d): %s\n", yylineno, ch, yytext); ch += yyleng;

}

{delim} { printf("DELIMITER (%d, %d): %s\n", yylineno, ch, yytext); ch += yyleng;

}

{ws}+ { ch += yyleng; }

. { printf("Unknown character (%d, %d): %s\n", yylineno, ch, yytext); ch += yyleng;

}

%%

int main(int argc, char **argv)

{

if(argc < 2)

15

Источник: https://studfile.net/preview/16527600/