Germany | Finland | Saint Petersburg | Drive

Библиотекарь для QPILE за 15 минут

Опубликовано в M4

Макропроцессор M4 в роли библиотекаря для QPILE.

 

Мои тексты на QPILE состоят из двух частей. Первая - библиотека стандартных функций. Вторая - собственно алгоритм, главный модуль. Как подключить библиотеку, чтобы в главном модуле были доступны функции из нее?

  • Вариант первый - указать имя файла библиотеки в главном модуле с помощью инструкции INCLUDE. Способ рабочий, но имеет один важный недостаток. В портфель будут загружены ВСЕ функции из библиотеки. Не очень страшно, но ...
  • Вариант второй. Руками перенести в главный модуль все требуемые функции. Комментировать нечего - и так все понятно.
  • Вариант третий. Написать библиотекаря, который сам скомпонует портфель нужными функциями из библиотеки

Третьим вариантом и займёмся, как наиболее правильным и коротким.

 


Программист должен быть ленивым и пользоваться готовеньким. Готовенькое есть - препроцессор. Проиллюстрирую один из возможных вариантов создания библиотекаря. Всё не то чтобы просто, а крайне просто.

Итак, у нас есть главный модуль.

PROGRAM


a = 15
if x < Min(1,x)
        a = 100
end if
if x < Min(2,5*3)
        a = 200
end if
writeln("a.txt",a)


END_RPOGRAM

Чтобы он заработал, необходимо из библиотеки прилинковать к нему функцию Min()

Вот содержимое нашей библиотеки:

FUNC Min(p1,result)
if p1 - result < 0
     result = p1
end if
END FUNC

FUNC Round(a)
     result = Floor(a+0.5)
END FUNC

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

Библиотеку однажды придется немного подправить. Для демонстрации я выбрал самый простой вариант организации библиотекаря, который не есть самый красивый. В шапку библиотеки копируем следующий текст:

define(End_Program,`undivert(1)
END_PROGRAM')

define(END_FUNCTION,`END FUNC
d'ivert(0)''
)

А каждую функцию из библиотеки изменяем так:

define(Min, `m4$0($*)
divert(ifdef(`*m4$0',-1,1))
define(`*m4$0',`')
FUNC m4$0(p1,result))'


if p1 - result < 0
result = p1
end if

END_FUNCTION)

define(Round, 'm4$0($*)
divert(ifdef('*m4$0',-1,1))
define('*m4$0','')
FUNC m4$0(a))'

result = Floor(a+0.5)

END_FUNCTION)

Желтым фоном выделенно то, что требуется добавить в каждую функцию (документация). Min и Round - названия функции. Дальше идет несколько инструкций препроцессора. При желании и некотором навыке их можно упрятать, но пока пусть остаются. Если Вы заинтересовались, более симпатичное оформление пусть будет Вашим развлечением. C удовольствием познакомлюсь с Вашими идеями. Далее идёт сам текст функции на языке QPILE, который завершается конструкцией END_FUNCTION

Главный модуль не нуждается ни в каких изменениях, все изменения делаются только в библиотеке и единожды. После того как все функции в библиотеке были приведены в требуемый вид, просто объединяем библиотеку и главный модуль в один файл (либо связываем через макрос препроцессора include) и подаём его на обработку препроцессору M4.

На выходе получим:

a = 15
if x < m4Min(1,x)
a = 100
end if
if x < m4Min(2,5*3)
a = 200
end if
writeln("a.txt",a)

FUNC m4Min(p1,result))
if p1 - result < 0
result = p1
end if

END FUNC

END_PROGRAM

Полностью готовый портфель. Изменилось имя функции Min на m4Min, как в вызовах, так и в самом описании. Описания функции Round в сгенерированном портфеле нет, она и не требуется. Если включить в главный модуль вызов этой функции, она пристыкуется из библиотеки автоматически.

Что еще хорошего и полезного родине можно сделать при помощи препроцессора?

Средства малой автоматизации - в любом объёме и на любой вкус. Например, макрос Program позволяет расширить

Program(TEST,test)

в

PORTFOLIO_EX TEST;
DESCRIPTION test;
CLIENTS_LIST ALL_CLIENTS;
FIRMS_LIST FIRMID;
USE_CASE_SENSITIVE_CONSTANTS;
PROGRAM

Другой пример. Не составит труда написать макрос, задающий столбцы таблицы OWN. Процесс очевидный, нет смысла останавливаться.

Какие радости могут еще нас ожидать?

Можно расширить возможности QPILE стандартными средствами препроцессора. Например, часто нужна функция, возвращающая истину, если первый параметр больше второго и меньше третьего. Эту функцию можно написать на qpile и поместить в библиотеку ровно также, как мы это сделали с функцией Min. А можно оформить ее в виде макроса:

define(Between,(($2 <= $1) and ($1 <= $3)))

Теперь если в тексте встретится вызов функции Between, он будет заменен на inline-код прямо в тексте qpile, что в данном случае и короче, и быстрее, и переменные экономит (которых в qpile ограниченное количество)

Функция Round(), приведенная в начале статьи, тоже прекрасно выглядит в варианте макроса:

define(Round,Floor($1+0.5))

Следующая идея. Проверять препроцессором количество и правильность задания аргументов для библиотечных функций. Или подставлять недостающие, добавив тем самым недостающий в qpile функционал параметров функций по умолчанию.

Следующая идея - обфускатор. Написать его на M4 - дело одного дня. Писать не стал, ибо есть готовый

Следующая......... Подсказывайте.

Если есть интерес и идеи, буду рад обменяться опытом.

Добавить комментарий