Макроязыки (2021)
(Про что не будет разговора)
Программирование, обусловленное средой — изначально среда представлена готовым набором объектов и команд над ними
- DSL-языки
Языки склейки — shell, perl,и т. п. — от командной строки к сценарию
Контекстно обусловленная обработка данных — как правило, текстов, или domain-specific:
- Чистая теория: НАМ
Императивная модель на основе регулярных выражений: sed → awk → perl
- БНФ и им подобные
- …
Принцип макроподстановки
Задача: параметризовать текст.
- На входе:
Определения нетерминалов (макросов)
- Текст, содержащий эти нетерминалы
Принцип макроподстановки: нетрерминал заменяется на тело его определения
- На выходе — гладкий текст
Свойства области применения:
Часто: выходной текст похож на входной (cpp, *roff и т. п.; но: autoconf)
- Почти всегда: выходной текст воспроизводит структуру входного (текст как поток)
Все? А алгоритмическая полнота?
- Аллегирование
- Данные: по связыванию (как в ФП)
⇒ Параметризация определения и подстановки
- Контекст: имена макросов
- Данные: по связыванию (как в ФП)
- Действия, определяемые свойствами данных
- Пустой/непустой контекст, сравнение двух контекстов, …
- …в том числе возможность итерации
Проверка на наличие нетерминалов в макроподстановке ⇒ рекурсия!
Что может ещё потребоваться:
- Экранирование
- Параметризация + экранирование = передача макросов в качестве параметров
- Манипуляция несколькими потоками
Отказаться от построчной обработки
- Вообще отказаться от понимания текста как слова и т. п., DSL
Практические элементы:
- Лишние символы (например, красивое форматирование определений)
- Арифметика
- О/С
- Строковые функции
- Файлы и каталоги (в т. ч. globbing)
- …
Немного истории
General-purpose macro processor и Macro_(computer_science).
Самый популярный вариант — препроцессор Си и другие макронадстройки над языками, однако это DSL (и таких бездна).
Более общий вариант — маркосы в Lisp, но и там макроподстановка не замещает функциональную парадигму и служит в первую очередь повышению читаемости.
M4
Чуть ли не единственный из известных.
Macro Magic: M4 Complete Guide 2019 года
учебник в составе autoconf (обратите внимание: в autoconf кавычки переопределены в [ и ])
Принцип:
- На входе — текст, содержащий команды M4
- Каждая команда, по мере поступления, выполняется, подставляя вместо себя результат своего выполнения (операция макроподстановки)
- Этот результат может, в свою очередь, содержать команды M4, поэтому анализ текста возобновляется с позиции, в которую была сделана подстановка
На выходе — текст, не содержащий команд M4
Вот этот текст разумный http://mbreen.com/m4.html
Примеры просьба прощёлкивать! Если нет linux под рукой — можно воспользоваться любым online linux-окружением, например http://repl.it. При открытии редактора там в правой части запускается натуральная linux-консоль.
В ней можно запустить m4, но лучше что-нибудь вроде cat > o; echo "===="; m4 < o, чтобы ввод и вывод не перемешивались. Ввод заканчивается новой строкой и Ctrl+D для обозначения конца файла.
Макроопределение и макроподстановка
define(AUTHOR, W. Shakespeare) `AUTHOR' is AUTHOR
- Однако
define(AUTHOR, Me) define(AUTHOR, A. Maclean) `AUTHOR' is AUTHOR or Me?
- А это не заработает:
define(AUTHOR, W. Shakespeare) define(AUTHOR, A. Maclean) `AUTHOR' is AUTHOR
Так что лучше брать макрос в кавычки всегда, и обмазывать dnl от лишних переводов строки
define(`AUTHOR', Me)dnl define(`AUTHOR', A. Maclean)dnl `AUTHOR' is AUTHOR or Me?
Вложенность
define(`definenum', `define(`num', `99')') num definenum num
- В то время как
define(`definenum', define(`num', `99')) num
Условные операторы
На пространство имён
define(`macro', `--$1--')dnl ifdef(`macro', QQ, QKRQ) QQ undefine(`macro')dnl ifdef(`macro', QQ, QKRQ) QKRQ
also dumpdef
Только на сопоставление
define(`a', b)dnl ifelse(a, b, c, d) ifelse(a, B, c, d)
- и похитрее
define(`a', d)dnl define(`e', b)dnl ifelse(a, b, b-true, c, c-true, d, d-true, all-false) ifelse(e, b, b-true, c, c-true, d, d-true, all-false)
Типы данных
eval() — вычисление арифметических выражений
translit(), index(), substr(), len(), regex(), … — строки
translit(`Highgest leet of all', `etl', 371)
- (кавычки можно не ставить, но мало ли: слева точно литералы, а справа может быть и макро)
Параметрические макросы и циклы
Параметры — это просто $№ в теле макроподствновки
$# — количество, $* — все сразу, $@ — все сразу, но закавыченные (без дальнейшей макроподстановки)
define(`NONTERM', non-terminal)dnl define(`param', `All: $* All-quoted: $@ Number: $#; Second: $2')dnl param(one, two by two, NONTERM, three) param(`one', `two by two', `NONTERM', `three')
Цикл == рекурсия!
define(`len',`ifelse($1,,0,`eval(1+len(substr($1,1)))')')dnl len(qw qw) len() len
Остальные циклы (forloop, foreach cмоделированы в соотв. библиотеках)
Управление пространством имён и контексты
GNU m4:
- запрет на распознавание некоторых встроенных макросов без скобок
- макросы, имена которых нельзя распознать (но можно вызвать или посмотреть определение)
define(`dit', `DIT!') dit define(`$O', `dit dit dit') $O defn(`$O') indir(`$O')
Например, имитация массивов
- Временное переопределение, стек и суперпозиция определений
define(`eval2', `pushdef(`incr', `eval(2+`$1')')eval(`$1')popdef(`incr')') eval2(incr(2)+incr(3)) incr(3)
Потоки вывода
Часть текста можно перенаправить в синтетический поток, по окончании работы M4 они припишутся в конец в порядке нумерации (0 — основной поток)
one divert(3)dnl two three divert(1)dnl four five divert(0)dnl six seven
Поток -1 не направляется никуда (а определённые макросы остаются!), а ещё поток можно закрыть и вставить по месту с помощью undivert()
divert(-1) Здесь можно писать что угодно значение имеют только макроопределения define(`c', `divnum'> $1) divert(2) М. Ю. Лермонтов divert(0)dnl c(выхожу один) divert(1)dnl c(сквозь туман) divert(0)dnl c(я на дорогу) undivert(1)dnl c(кремнистый путь блестит)
Обратите внимание на отсутствие dnl в 6 строке
Если успеем
include()
- Набор библиотек
Поддержка примитивов склейки (syscmd(), esyscmd(), sysval)
- …
Макровзрыв
- Контекста: актуально конечная последовательность вместо цикла
Пример: configure.ac
Мозга
Д/З
Прощёлкать примеры из лекции и какую-нибудь методичку (например, эту) по M4; иметь под рукой учебник (его тоже можно при желании прочитать и прощёлкать)
Скачать генератор домашнего задания prog1.m4 и запустить его так:
m4 -DSEED=произвольный_номер formgen.m4
По выбранному номеру будет сгенерирован текст задания. В задании требуется обработать с помощью m4 текстовый файл с произвольными параметрическими формулами, чтобы он превратился в программу на Си или на Паскале, которая их вычисляет. Программа, конечно, должна быть работающая ☺.
- Задания бывают разной сложности — от 4 до 6 баллов
Решение — программа на m4
Присылать на почту frbrgeorge@gmail.com
В виде приложенного файла
С обязательным указанием номера варианта
В поле «Subject» должно присутствовать словосочетание «Курс ПП»