Youtube-запись от 2025-09-05: https://youtu.be/RUsgBfP-BvQ
- Читает текст символ за символом.
- Ищет в нём регулярные выражения.
- Реагирует на сработавшие регулярные выражения.
Мой дядя самых честных правил, Когда не в шутку занемог, Он уважать себя заставил И лучше выдумать не мог.
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;
- Так можно превратить поток символов в поток токенов.
- И либо сразу обработать, либо передать дальше.
Токен — это «кусок контента с пояснением, что это»
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;
А генератор зачем?
Чтобы не писать код, конечно!
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, от которого зависит код
- Сами правила распознавания токенов
- Там конечный автомат, пережить можно
- Таблицы — это таблицы переходов в конечном автомате
- yylex(); —
- Читать «в лоб», конечно, не очень хочется
Генерим код как обычный текст
-
В самом начале пишем код для инициации статичного двумерного массива
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)