Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

2025-09-05

Flex — генератор сканеров текста (лексических анализаторов)

Youtube-запись от 2025-09-05: https://youtu.be/RUsgBfP-BvQ

Чем занят лексический анализатор?

  1. Читает текст символ за символом.
  2. Ищет в нём регулярные выражения.
  3. Реагирует на сработавшие регулярные выражения.

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

flowchart RL

sym([Символ]):::green
ok((•)):::black
pattern{{Шаблон}}:::yellow
do[Код]:::blue

sym ==> |что нашёл?| ok
ok -.-> |ничего| sym

ok -.-> |вот это| pattern
pattern -.-> |действуй!| do

classDef green fill:#B8BA46;
classDef yellow fill:#F9B618;
classDef black fill:#272727,color:#FBFAF3;
classDef blue fill:#2FA6E9,color:#FBFAF3;
Loading

Зачем?

  1. Так можно превратить поток символов в поток токенов.
  2. И либо сразу обработать, либо передать дальше.

Токен — это «кусок контента с пояснением, что это»

flowchart LR

sym(Символ):::green
subgraph Токен
sym_(Символ):::green
end
or((•)):::black
do{{Обработка}}:::blue
next{{Дальше}}:::red

sym ==> Токен --> or -.-> do
or -.-> |знает про токены| next

classDef green fill:#B8BA46;
classDef yellow fill:#F9B618;
classDef black fill:#272727,color:#FBFAF3;
classDef blue fill:#2FA6E9,color:#FBFAF3;
classDef red fill:#FE8019;
Loading

А генератор зачем?

Чтобы не писать код, конечно!

flex — простой и старый генератор лексических анализаторов

`man flex`
  • Жадный: берём самое длинное
  • Автомат: шаблоны задают переходы между состояниями

Как всегда, начнём с Фибоначчи

«Вначале было 1. Потом опять 1. Затем появилось 2. Дальше — 3. Потом 5. Следующим было 8. А потом 13. И вот перед нами уже 21. А что дальше? Дальше 34. И за ним 55. Что же впереди?»

Что мы можем сделать с этим текстом?

  • 00 • Превратить в поток токенов вида «целое число»

    %{
    #include <stdio.h>
    %}
    
    %%
    [0-9]+   { /* вот мы и нашли число */ }
    .|\n     ;
    %%
    
    int main(void) {
    	yylex();
    	return 0;
    }
    %:
    	flex -o $*.yy.c $*.l
    	gcc $*.yy.c -lfl -o $*.run
    	./*.run < data.txt
  • 01 • Напечатать эти числа

  • 02 • Сложить эти числа

  • 03 • Убедиться, что перед нами последовательность Фибоначчи

  • 04 • Вычислить следующий элемент этой последовательности

  • 05 • Напечатать не только числа, но и номера строк, в которых они найдены

    %option yylineno
  • 06 • А теперь ещё и номера символов в строке

    // это называется «встроенный хук»
    #define YY_USER_ACTION \
    	...
    	// используем yyleng
  • 07 • Превратить числа в код, создающий C-массив

    ...
    printf("#define SIZE %d\n", count);
  • 08 • Прогнать простые упражнения вида «A + B = [введи правильный ответ]»

    %option noyywrap
    
    FILE * fin = fopen("fibonacci.txt", "r");
    yyin = fin;
    ... // пригодится fflush(stdout);
    fclose(fin);

Мы не можем создать код, который на этапе компиляции зависит от содержания этого текста

  • Из текста нельзя взять типы данных
  • Такой enum, от которого зависит код
  • Сами правила распознавания токенов

Полезно заглянуть в lex.yy.c

  • Там конечный автомат, пережить можно
  • Таблицы — это таблицы переходов в конечном автомате
  • yylex(); —
  • Читать «в лоб», конечно, не очень хочется

Теперь пишем полезное

Из содержимого файла — двумерный массив (матрица) в C

Генерим код как обычный текст

  • В самом начале пишем код для инициации статичного двумерного массива

    printf("double arr[ROWS][COLS] = { ");
  • Реагируем на число: дописываем его в вывод

    printf("%s", yytext);
    • различаем ситуации «первое число в строке» и «число из середины строки»

      if (ccol == 0) { ... } else { ... }
    • перед первым числом открываем внутренний массив

      printf(" { ");  // да и сам вывод первого числа отличается от других
  • Реагируем на перенос строки: начинаем новый одномерный массив

    \n   { printf(" }, \n"); }
  • Больше ни на что не реагируем

    [ \t]+    ;
    .         ;
  • Когда всё обработали — заканчиваем код инициации

    yylex();
    printf(" };\n");
  • И выводим макросы для количества колонок и строк

    // чтобы это сделать, по коду размазываем счётчики
    printf("#define ROWS %d\n", rows);
    printf("#define COLS %d\n", cols);

Фишки

  • Первым срабатывает тот шаблон, что выше в коде
  • Есть стек состояний для поиска тоllкенов вида «один объект внутри другого»
  • Опции, хуки, манипуляция с потоком — всего в изобилии
  • Главное — встроен почти во все Linux-дистрибутивы (не то что re2c)