Макроязыки (2022)
(Про что не будет разговора)
Программирование, обусловленное средой — изначально среда представлена готовым набором объектов и команд над ними
- DSL-языки
Языки склейки — shell, perl,и т. п. — от командной строки к сценарию
Контекстно обусловленная обработка данных — как правило, текстов, или domain-specific:
- Чистая теория: НАМ
Функциональная модель (term-rewrite): РЕФАЛ, Плэнер, более современный — Pure (сайт Pure)
Императивная модель на основе регулярных выражений: 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 для обозначения конца файла.
Если в вашем дистрибутиве есть команда rlwrap, можно получить более полноценный REPL (с историей, подсказкой, поиском и т. п.) примерно так:
rlwrap -S "m4> " m4
Макроопределение и макроподстановка
define(AUTHOR, W. Shakespeare) `AUTHOR' is AUTHOR
- Однако
define(AUTHOR, Me) define(AUTHOR, A. Maclean) `AUTHOR' is AUTHOR or Me?
Почему? (dumpdef) И это не заработает:
define(AUTHOR, W. Shakespeare) define(AUTHOR, A. Maclean) `AUTHOR' is AUTHOR or W. Shakespeare?
Почему? (dumpdef) + пробел в имени макро, чтобы раскрыть, нужно применить indir()
Так что лучше брать макрос в кавычки всегда, и обмазывать dnl от лишних переводов строки
define(`AUTHOR', Me)dnl define(`AUTHOR', A. Maclean)dnl `AUTHOR' is A. Maclean or Me? Answer: AUTHOR!
Откуда переводы строки?
А кто просил их удалять? ☺
Специальный макро: delete up to new line:
qwe qwe dnl define(`a', b) define(`a', b) dnl
- Вот так и делать
Вложенность
Кавычки раскрываются по одной паре за подстановку:
define(`definenum', `define(`num', `99')') num definenum num
- В то время как
define(`definenum', define(`num', `99')) num
Почему? Внутренний define() сработал сразу (dumpdef) Тройное раскрытие:
define(`definenum', `define(`num', `define(`deep', 100500)'99)') deep num definenum deep num deep num
Условные операторы
На пространство имён
define(`macro', `--$1--')dnl ifdef(`macro', QQ, QKRQ) QQ undefine(`macro')dnl ifdef(`macro', QQ, QKRQ) QKRQ
Только на сопоставление
define(`a', b)dnl ifelse(a, b, c, d) ifelse(a, w, c, d) ifelse(a, w, c)
- (последняя строка — краткая форма) Многоместные: проверяется первая тройка параметров, если нет, она отбрасывается, и процесс повторяется
ifelse(a, `a', 1, a, `b', 2, 3) define(`a', `b') ifelse(a, `a', 1, a, `b', 2, 3) define(`a', `c') ifelse(a, `a', 1, a, `b', 2, 3)
Типы данных
eval() — вычисление арифметических выражений
translit(), index(), substr(), len(), regex(), … — строки
translit(`Highgest leet of all', `etl', 371)
- (кавычки можно не ставить, но мало ли: слева точно литералы, а справа может быть и макро)
Параметрические макросы и циклы
Параметры — это просто $№ в теле макроподствновки
$# — количество, $* — все сразу, $@ — все сразу, но закавыченные (без дальнейшей макроподстановки)
define(`NONTERM', non-terminal)dnl define(`param', `All: $* All-quoted: $@ Number: $#; First: $1, Second: $2')dnl param(one, two by two, NONTERM, three) param(`one', `two by two', `NONTERM', `three') param() param
Особенность: macro — 0 параметров, macro() — 1 пустой параметр:
Цикл == рекурсия! {{{ define(`len',`ifelse($1,,0,`eval(1+len(substr($1,1)))')')dnl len(qw qw) len() len
Цикл по параметрам (shift):
shift(1, 2, 3, 4) define(`reverse', `ifelse(`$#', `0', , `$#', `1', ``$1'', `reverse(shift($@)), `$1'')')
Остальные циклы (forloop, foreach cмоделированы в соотв. библиотеках)
Управление пространством имён и контексты
GNU m4:
- запрет на распознавание некоторых встроенных макросов без скобок
- макросы, имена которых нельзя распознать (но можно вызвать или посмотреть определение)
define(`dit', `DIT!') dit define(`$O', `dit dit dit') $O defn(`$O') indir(`$O')
Например, имитация массивов
- Временное переопределение, стек и суперпозиция определений
incr(9) pushdef(`incr', `eval(2+`$1')') incr(9) popdef(`incr') incr(9)
Потоки вывода
Часть текста можно перенаправить в синтетический поток, по окончании работы 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)
- …
Макровзрыв
- Контекста: актуально конечная последовательность вместо цикла
декартово произведение всех макроподстановок ⇒ для n вложенных макросов объём Kn.
Пример: configure.ac
Мозга
Д/З
Прощёлкать примеры из лекции и какую-нибудь методичку (например, эту) по M4; иметь под рукой учебник (его тоже можно при желании прочитать и прощёлкать)
Скачать генератор домашнего задания prog1.m4 и запустить его так ($RANDOM выдаст случайный номер, также можно выбрать любой номер, кроме 1337, самому):
m4 -DSEED=$RANDOM formgen.m4`
По выбранному номеру будет сгенерирован текст задания. В задании требуется обработать с помощью m4 текстовый файл с произвольными параметрическими формулами (с использованием стандартной математической библиотеки), чтобы он превратился в программу на Си или на Паскале, которая их вычисляет. Программа, конечно, должна быть работающая ☺.
- Задания бывают разной сложности — от 4 до 6 баллов.
- В заданиях требуется сгенерировать программу любо на Си, либо на Паскале.
Решение — программа на m4
Присылать на почту uneexlectures@cs.msu.ru
В виде приложенного файла с расширением .m4
С обязательным указанием номера варианта
В поле «Subject» должно присутствовать словосочетание «Курс ПП»