Вредные советы по использованию процессора m4

На самом деле это единственный раздел про unix на этом форуме

Модераторы: /dev/random, Модераторы разделов

Ответить
Аватара пользователя
olecya
Сообщения: 901
ОС: debian, fedora (i3-wm)

Вредные советы по использованию процессора m4

Сообщение olecya »

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

Существует два варианта вызова. Первый может работать в потоке.
Но вот беда в этом варианте можно только определять макросы

Код: Выделить всё

cat - | m4 -Done=1 -Dtwo=2
one
1
two
2
И все
Второй вариант предусматривает написание скриптов но подключить стандартный
ввод так чтобы он обрабатывался построчно набором команд не так то просто. Я испробовала
разные способы включая именованные каналы и все таки нашла приемлемый вариант через чтение
из стандартного потока ошибок. Все можно записать в файл, потом подключить скрипт и
вызвать функцию readline но так будет наглядней.

Код: Выделить всё

m4 -Q <<\eof
divert(`1')dnl
see you!
divert`'dnl
q - exit from calc
define(`q', `undivert`'m4exit')dnl
define(`plus', `+')dnl
define(`minos', `-')dnl
define(`times', `*')dnl
define(`divide', `/')dnl
define(`ou', `0')dnl
define(`one', `1')dnl
define(`two', `2')dnl
define(`three', `3')dnl
define(`four', `4')dnl
define(`five', `5')dnl
define(`six', `6')dnl
define(`seven', `7')dnl
define(`Z_0', ``ou'')dnl
define(`Z_1', ``one'')dnl
define(`Z_2', ``two'')dnl
define(`Z_3', ``three'')dnl
define(`Z_4', ``four'')dnl
define(`Z_5', ``five'')dnl
define(`Z_6', ``six'')dnl
define(`Z_7', ``seven'')dnl
define(`readline', `regexp(eval(esyscmd(`head -1 <&2')), `.*', `Z_\&')
ifelse(1, 1,`$0')')dnl
readline
eof
Коротко о программе:
Это вроде отложенного вызова defer в golang
или atexit в Си

Код: Выделить всё

divert(`1')dnl
see you!
divert`'dnl
выход из программы по нажатию клавиши "q"

Код: Выделить всё

define(`q', `undivert`'m4exit')dnl
определение макросов, некоторые пришлось экранировать дважды
чтобы избежать прямой подстановки.

Код: Выделить всё

define(`plus', `+')dnl
...
Главный виновник.

Код: Выделить всё

define(`readline', `regexp(eval(esyscmd(`head -1 <&2')), `.*', `Z_\&')
ifelse(1, 1,`$0')')dnl
Определение рекурсивной функции из двух строчек, которая в каждом цикле
вызывает команду оболочки head -1 </dev/stderr через встроенную функцию esyscmd
которая помещает на ее место считанную строку, далее процессор подставляет
в строку определенные макросы в виде математического знака и чисел,
далее функция eval выполняет математическую операцию,
функция regexp к полученному результату подставляет префикс Z_ что бы избежать
бесконечного переопределения цифр и строк и в конечном итоге процессор подставляет
макросы экранированные дважды опять таки чтобы избежать переопределения.

Результат выглядит просто но на самом деле на пути к этому мне пришлось научиться
выводить строку посимвольно и столбиком и в строчку через пробел,
освоить бесконечные и терминированные циклы, есть уже написанные функции могущие разбить
строку по любому разделителю вроде сишной strtok, с выводом до рекурсивного цикла и после,
как говорится в хаскеле с правой и левой сверткой.
m4_calc.png
Но вот помогите разобраться если мы читаем из stderr
а пишем по всему в stdin то как это работает? Я если честно просто уже от беспомощности
случайно набрела на этот вариант методом тыка. lsof говорит что дескриптор 2 универсальный
Спасибо сказали:
Ответить