Русский перевод - (C) А.Гавва
г.Львов
январь 2003г.
Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc.
Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.
Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that this permission notice may be stated in a translation approved by the Foundation.
Эта глава - вводная. Она рассказывает
о том, что такое GNU m4, как m4
появился, как читать и использовать эту
документацию, как запусть программу m4
на выполнение и как сообщать об
обнаруженных в этой программе ошибках.
Таким образом, эта глава содержит
некоторые подсказки которые будут
полезны при чтении остального материала
этого учебника.
Остальные главы учебника предоставляют
детальные сведения об особенностях
языка m4.
m4m4 - это макропроцессор который
осуществляет копирование данных с его
входа на выход, расширяя макросы по мере
их поступления. Макросы бывают строенными
или определяемыми пользователем, и
могут содержать любое количество
аргументов. Кроме выполнения макрорасширений
(макроподстановок), m4 имеет
встроенные функции для включения
именованых файлов, запуска команд UNIX,
выполнения целочисленной арифметики,
различного манипулирования текстом,
осуществления рекурсии, и т.д. m4
может быть использован как препроцессор
(front-end) какого-либо компилятора, или
как самостоятельный макропроцессор.
Макропроцессор m4 широко
используется во всех системах UNIX. Обычно,
о его наличии беспокоится достаточно
не большой процент пользователей.
Однако, есть и те кто используют его
часто. Растущая популярность пакета
GNU Autoconf, который требует наличия GNU m4
для генерации скриптов `configure',
стимулирует многих пользователей
устанавливать m4 даже если они
никогда не будут его реально использовать
для программирования. GNU m4 в
большинстве случаев совместим с версией
для системы System V Release 3, исключая
несколько небольших различий. Более
полная информация содержится в секции
Совместимость
с другими версиями m4.
Некоторые люди привыкают к использованию
m4. Сначала они используют m4
для решения простых задач, потом решаемы
задачи становятся более сложными, требуя
значительно больше времени на отладку
скриптов m4 чем на выполнение
непосредственной работы. Учтите, что
m4 может стать опасен для здоровья
некоторых заядлых программистов.
Упоминаемые здесь исторические сведения очень не полные и абсолютно не авторитарны. Для более полного написания этой секции, необходима помощь осведомленных пользователей.
Важным прародителем m4 был GPM.
См. C. Stratchey: "A General Purpose Macro generator",
Computer Journal 8,3 (1965), pp. 225 ff.. Кроме того,
GPM описан в классической книге
David Gries "Compiler Construction for Digital Computers".
В то время как GPM был чистым,
m4 был задуман как средство для
работы с истинной запутанностью реальной
жизни: макросы могли быть распознаны
как предопределенные, отброс пробелов
или символов завершения строки был
сделан более легким, множество конструкций
было встроено вместо использования
производных, и т.д.
В оригинале, m4 был разработан
как основа препроцессора для Rational
FORTRAN, таким образом, ratfor является
эквивалентом cpp.
m4Формат командной строки для запуска
m4, следующий:
m4 [option...] [macro-definitions...] [input-file...]
Все опции начинаются
с `-', или, при использовании опций
с длинными именами, с `--'. Длинные
имена опций могут быть записаны не
полностью, использование недвусмысленных
префиксов достаточно. m4 понимает
следующие опции:
--version
m4 завершается немедленно,
без чтения каких-либо входных файлов
input-files.
--help
m4
завершается немедленно, без чтения
каких-либо входных файлов input-files.
-G
--traditional
m4.
-E
--fatal-warnings
m4
при первой же выдаче предупреждающего
сообщения, рассматривая выдачу
предупреждающего сообщения как
фатальное.
-dflags
--debug=flags
-lnum
--arglength=num
-ofile
--error-output=file
-Idir
--include=dir
m4 необходимоть поиска
включаемых файлов в каталоге dir
когда включаемые файлы не найдены в
текущем каталоге. Для более полной
информации см. секцию Поиск
включаемых файлов.
-e
--interactive
m4 интерактивным.
Это подразумевает, что весь вывод не
будет буферизироваться, и прерывания
будут игнорироваться.
-s
--synclines
m4 используется как
препроцессор для какго-либо компилятора.
Имена исходных файлов и информация о
нумерации строк передаются директивами
вида `#line linenum
"filename"',
которые необходимым образом вставляются
в середину ввода. Подобные директивы
подразумевают, что следующие строки
порождены или расширены из содержимого
входного файла filename в строке
linenum. Часть `"filename"'
часто бывает опущена когда имя файла
не было изменено предыдущей директивой.
Указание директив синхронизации всегда
занимает самостоятельную строку. При
возникновении расхождений в синхронизации
в середине выводимой строки, ассоциируемая
директива синхронизации задерживается
до начала следующей сгенерированной
строки.
-P
--prefix-builtins
-WREGEXP
--word-regexp=REGEXP
m4. (Для более
полной информации см. секцию Изменение
лексической структуры слов).
-Hn
--hashsize=n
-Ln
--nesting-limit=n
m4
генерируются механически. Большинству
пользователей никогда не потребуется
использование этой опции. Если показанное
будет навязываться, то эта опция (которая
остается экспериментальной) пожет
исчезнуть. Эта опция не имеет
возможности прекратить бесконечные
циклы пересканирования, пока не будет
исчерпано доступное пространство
памяти или стека. Удачное использование
циклов пересканирования может вызвать
сложные и длительные вычисления m4
с полезными результатами. Установка
ограничений в этой области может
уменьшить мощность m4. Существует
множество патологических ситуаций.
Пример `define(`a', `a')a' является одним
из простейших (Для более полной информации
см. секцию Совместимость
с другими версиями m4). Ожидание
чтобы GNU m4 детектировал подобные
ситуации может показаться подобным
ожиданию чтобы система компиляции
детектировала и проверяла наличие
бесконечных циклов: в общем это весьма
трудная проблема, которая в общем
является неразрешимой!
-Q
--quiet
--silent
-B
-S
-T
m4 для System V, однако реально
они ничего не делают.
-Nn
--diversions=n
m4 где
присутствовало управление количеством
возможных отклонений которые согли
быть использованы одновременно. Реально,
эти опции ничего не делают, поскольку
теперь такой лимит больше не фиксируется.
Описание и удаление макро-определения может быть выполнено в командной строке с помощбю использования опций `-D' и `-U'. Это имеет следующий фомат:
-Dname
-Dname=value
--define=name
--define=name=value
-Uname
--undefine=name
-tname
--trace=name
-Ffile
--freeze-state file
-Rfile
--reload-state file
Остальныме аргументы командной строки принимаются как имена входных файлов. Если не указано ни одного имени входного файла, то производится чтение устройства стандартного ввода. Принимается имя файла `-', которое подразумевает устройство стандартного ввода.
Чтение входных файлов осуществляется в указанной последовательности. Устройство стандартного ввода может быть прочитано только однократно, таким образом, имя файла `-' может быть указано в командной строке не более одного раза.
Если в процессе использования GNU m4
у вас возникли проблемы, или вам
показалось, что вы обнаружили ошибку,
пожалуйста сообщите об этом. Перед тем
как сообщать об ошибке, убедитесь в том,
что вы действительно обнаружили реальную
ошибку. Внимательно перечитайте
документацию и убедитесь в том, что вы
можете сделать то, что вы пытаетесь.
Если после чтения документации остается
не понятным, можете-ли вы сделать то,
что вы пытаетесь сделать, то сообщите
об этом также, поскольку это указывает
на наличие ошибки в документации!
Перед тем как сообщать об ошибке или
попытке исправить ее самостоятельно,
попытайте изолировать ее в минимальном
(по размеру), насколько это возможно,
входном файле который способен
воспроизвести проблему. Затем, пошлите
нам входной файл и полученные вами
результаты работы m4. Также,
опишите то что вы ожидали получить как
результат работы m4; это поможет
нам определить содержится-ли проблема
в документации.
Как только вы получите точное
воспроизведение проблемы, пошлите через
e-mail по адресу (Internet) `bug-gnu-utils@prep.ai.mit.edu'
или (UUCP) `mit-eddie!prep.ai.mit.edu!bug-gnu-utils'. При
этом, пожалуйста, укажите номер
используемой вами версии m4. Вы
можете узнать эту информацию с помощью
команды `m4 --version'.
Сообщения не об ошибках, с предложениями также всегда приветствуются. Если вы имеете вопросы о чем-либо, что плохо описано в документации или затемняет использование средств, пожалуйста,сообщите об этом также.
Это руководство содержит множество
примеров ввода и вывода m4, для
того чтобы четко различать ввод, вывод
и сообщения об ошибках m4
используется простая нотация. Текст
примеров также отличается от обычного
текста и показан шрифтом с фиксированной
шириной, подобно следующему:
Это пример текста примера!
Для различия ввода от вывода, весь вывод
m4 предваряется строкой `=>',
а все сообщения об ошибках - строкой
`error-->'. Таким образом:
пример строки ввода =>строка вывода m4 error-->сообщение об ошибке
Поскольку макро-определения (макросы),
предопределенные в m4, описаны,
прототип вызова макроса будет показан
предоставляя информативно-наглядные
имена аргументов, например:
regexp(string, regexp, opt replacement)
В m4, все аргументы макросов
являются строками, но некоторые имеют
специальную интерпретацию, например
числа, имена файлов, регулярные выражения
и т.д.
Указвние `opt' перед третьим аргументом, говорит о том, что этот аргумент опционален, и если он отсутствует, то значение его содержимого будет пустой строкой. Указание многоточия (`...') в конце списка аргументов индицирует, что далее может следовать любое число аргументов
Этот документ согласованно пишет и
использует builtin без дефиса, как это
требовалось бы при написании английского
слова. Именно таким образом примитив
builtin записывается внутри m4.
В процессе чтения
ввода m4, он разделяет его в токены
(tokens). Токены могут быть именами,
строками в кавычках или любыми одиночными
символами, которые не являются частью
какого-либо имени или строки. Ввод m4
может также содержать комментарии.
Имя - это любая последовательность букв, цифр и символов подчеркивания _, не начинающаяся с цифры. Если имя имеет макро-определение, то оно будет субъектом для макро-расширения (см. секцию Как вызывать макросы).
Примерами допустимых имен могут служить: `foo', `_tmp', and `name01'.
Строки в кавычках (иначе, цитируемые строки) являются последовательностями символов, заключенных в кавычки ` и ', причем число начальных и завершающих кавычек сбалансировано. Значение строкового токена является текстом у которого один уровень кавычек отброшен. Таким образом:
`'
является пустой строкой, а
``quoted''
является строкой
`quoted'
Символы кавычек могут быть изменены в
любой момент, с помощью использования
встроенного макроса changequote. Для
более полной информации см. секцию
Изменение
символов кавычек.
Любой символ, который не является частью имени или строки в кавычках, является самостоятельным токеном.
Комментарии m4
нормально ограниченны символом `#'
и символом новой строки. Все символы
между символами ограничения комментариев
игнорируются, но содержимое всего
коментария (включая символы ограничения
комментариев) передается на вывод, то
есть, комментарии не отбрасываются m4.
Комментарии не могут быть вложенными, таким образом, первый же символ новой строки, после символа `#', завершает комментарий. Комментирующий эффект символа начала комментария может быть подавлен помещением этого символа в кавычки.
В любой момент времени, с помощью
встроенного макроса changecom, символы
ограничения комментариев могут быть
изменены на любую строку. Для более
полной информации см. секцию Изменение
ограничителей комментариев.
Эта глава демонстрирует макро-вызовы, макро-аргументы и то как трактуется макро-расширение.
Макро-вызовы имеют одну из форм
name
которая является макро-вызовом без единого аргумента, или
name(arg1, arg2, ..., argn)
которая является макро-вызовом с n аргументами. Макросы могут иметь любое количество аргументов. Все аргументы являются строками, но различные макросы могут интерпретировать свои артументы различными способами.
Открывающие скобки должны непосредственно сопровождаться name, без пробелов между ними. Если это не так, то макрос будет вызван без единого аргумента.
Для макро-вызова, который не должен иметь аргументов, не должны использоваться скобки. Макро-вызов
name()
является вызовом с одним аргументом, которым является пустая строка, а не вызовом без аргументов.
Одной из инноваций языка m4, в
сравнении с его предшественниками
(подобно Stratchey's GPM, например),
является возможность распознавания
макро-вызовы без использования каких-либо
специальных, предшествующих вызову
символов. Хотя это удобно в общем, такое
свойство может иногда быть источником
ложного, нежелательного макро-вызова.
Таким образом, GNU m4 предоставляет
некоторые механизмы или приемы для
предотвращения расценивания имен как
макро-вызовов.
Прежде всего, многие встроенные макросы не могут быть осмысленно вызваны без аргументов. Для любого из этих макросов, когда за открывающей скобкой непосредственно не следует имя параметра, вызов встроенного макроса не воспринимается. Это позволяет распутать большинство обычных случаев, подобных использованию `include' или `eval'. Позже, в этом документе, предложение "This macro is recognized only when given arguments" ссылается к этому специфическому соглашению.
Существует также опции командной
строки (--prefix-builtins или -P)
которые требуют, чтобы все имена
встроенных макросов предварялись `m4_'
(иначе, использовали `m4_' как
префикс), с целью правильного распознавания
встроенных макросов. Опция не имеет
эффекта для любого макроса, объявленного
пользователем. Например, с этой опцией,
можно написать m4_dnl и даже
m4_m4exit.
Если версия GNU m4, используемая
вами, содержит прекомпилированное
средство changeword, то она предлагает
большую гибкость в спецификации
синтаксиса как встроенных, так и
определяемых пользователем макро-имен.
Для более полной информации об этом
экспериментальном средстве см. секцию
Изменение
лексической структуры слов.
Естественно, самым простым способом предотвратить интерпретацию какого-либо имени как вызов существующего макроса является помещение этого имени в кавычки. Остальная часть этой секции немного глубже рассматривает то, как помещение в кавычки влияет на вызов макроса, и как помещение в кавычки может быть использовано для предотвращения макро-вызова.
Даже если помещение в кавычки выполняется обычно для всего имени макроса, это может быть сделано только для нескольких символов этого имени макроса. Возможно также заключение в кавычки пустой строки, но это будет работать только внутри имени. Например:
`divert' `d'ivert di`ver't div`'ert
все это дает строку `divert'. В то время как оба варианта:
`'divert divert`'
будет вызывать встроенный макрос divert.
Вывод оценки макроса всегда
пересканируется. Следующий пример будет
вызывать строку `de', точно также
если на ввод m4 будет выдано
`substr(abcde, 3, 2)':
define(`x', `substr(ab') define(`y', `cde, 3, 2)') x`'y
Строки, не помещенные в кавычки, с обоих
сторон строк, помещенных в кавычки,
являются субъектами, распознаваемыми
как имена макросов. В следующем примере,
помещение в кавычки пустой строки,
позволяет распознать макрос dnl
следующим образом:
define(`macro', `di$1') macro(v)`'dnl
Без использования кавычек, это даст строку `divdnl' сопровождаемую символом конца строки.
Помещение в кавычки может предотвратить распознавание конкатенации макро-расширения с окружающими символами как имени макроса. В этом примере:
define(`macro', `di$1') macro(v)`ert'
ввод будет создавать строку `divert'.
Если убрать кавычки, то вместо этого,
будет вызван встроенный макрос divert.
Когда имя распознано, и оно имеет макро-определение, то оно будет расширено как макрос.
Если имя сопровождается открывающейся скобкой, то, до того как выполняется вызов макроса, осуществляется накопление аргументов. Если указанных аргументов меньше, то отсутствующие аргументы трактуются как пустые строки. Если предоставлено больше аргументов, то лишние аргументы игнорируются.
Обычно m4 генерирует предупреждающие
сообщения, когда встроенные макросы
вызываются с не подходящим количеством
аргументов, но предупреждающие сообщения
могут быть подавлены опцией командной
строки `-Q'. Проверка количества
аргументов, для определяемых пользователем
макросов, отсутствует.
Обычно, макросы расширяются в процессе накопления аргументов, и все запятые, кавычки и скобки, которые могут быть показаны в результирующем расширенном тексте, будут также обеспечивать описание аргументов. Таким образом, если foo расширяется в `, b, c', то макро-вызов:
bar(a foo, d)
является макро-вызовом с четырьмя аргументами, которыми являются `a ', `b', `c' и `d'. Чтобы понять почему первый аргумент содержит пробел, следует иметь в виду, что предшествующие (лидирующие) не помещенные в кавычки пробелы никогда не являются частью аргумента, но последующие (завершающие) пробелы всегда являются частью аргумента.
Каждый аргумент не должен иметь предшествующие (лидирующие) пробелы которые не помещены в кавычки. Внутри каждого аргумента, все скобки, которые не помещены в кавычки, должны быть парными. Например, если foo является макросом,
foo(() (`(') `(')является вызовом макроса с одним аргументом, значением которого будет `() (() ('.
Существует общая практика помещать в кавычки все аргументы макроса, если нет уверенности в том, что необходима возможность расширения аргументов. Таким образом, в показанном выше примере со скобками, правильным способом выполнения этого будет:
foo(`() (() (')Однако, в некоторых случаях возникает необходимость не заключать в кавычки некоторые аргументы, и в этом нет ничего страшного. Это делает жизнь несколько труднее, если вы не очень внимательны.
Когда аргументы (если они есть) для макро-вызова накоплены, то происходит расширение макроса, и текст расширения возвращается обратно на вход (без помещения его в кавычки) и перечитывается. Таким образом, текст расширения одного макро-вызова может привести к вызову множества макросов, если вызовы включены (полностью или частично) в первое расширение макро-вызова.
Рассмотрим очень простой пример. Если foo будет расширен в `bar', а bar расширяется в`Hello world', то ввод
foo
сначала будет расширен в `bar', а затем, перечитан и расширен в `Hello world'.
Макросы могут быть определены, переопределены и удалны различными способами. Также, существует возможность переопределять любой макрос, без потери его первоначального значения, которое позже может быть восстановлено обратно.
Обычным способом определения
и переопределения макросов является
использование встроенного макроса
define:
define(name [, expansion])
который определяет, что имя name может быть расширено как expansion. Если expansion не указано, то считается, что расширение - пустое.
Встроенный макрос define имеет
пустое (void) расширение.
Следующий пример определяет, что макрос foo расширяется как текст `Hello World.'.
define(`foo', `Hello world.') => foo =>Hello world.
Появление пустой строки в выводе
обусловлено тем, что новая строка
(newline) не является частью макро-определения,
и, следовательно, она копируется в вывод.
Этого можно избежать путем использования
макроса dnl. Более полная информация
содержится в секции Удаление
пробелов из ввода.
Макрос define распознается только
при наличии параметров.
Макросы могут
иметь параметры. Аргумент с порядковым
номером n обозначается как $n
в тексте расширения, и заменяется
фактическим значением аргумента с
порядковым номером n, во время
расширения макроса. Рассмотрим пример
макроса с двумя аргументами. Этот макрос
осуществляет простое изменение порядка
следования двух аргументов.
define(`exch', `$2, $1') => exch(arg1, arg2) =>arg2, arg1
Это может быть использовано, например,
если вам нравится чтобы порядок следования
аргументов макроса define был
обратным.
define(`exch', `$2, $1') => define(exch(``expansion text'', ``macro'')) => macro =>expansion text
За объяснениями по использовинию двойных кавычек обратитесь к секции Помещение макро-аргументов в кавычки.
GNU m4 допускает, чтобы
числа, следующие за `$', состояли
из одной и более цифр, позволяя макросам
иметь любое количество аргументов. Это
не поддерживается реализациями m4
для UNIX, которые способны распознать
только одну цифру.
Как специальный случай, нулевой
аргумент, $0, всегда является
именем расширяемого макроса.
define(`test', ``Macro name: $0'') => test =>Macro name: test
Если необходимо, чтобы текст в кавычках появился как часть текста расширения, помните, что кавычки могут быть вложенными в строки заключенные в кавычки. Таким образом, в
define(`foo', `This is macro `foo'.') => foo =>This is macro foo.
указанное в тексте расширения `foo' не расширено, поскольку является строкой помещенной в кавычки, а не именем.
Существует специальная нотация для определения числа фактически переданных аргументов и для всех фактических аргументов (одновременно).
Число фактических аргументов
макро-вызова обозначается в тексте
расширения с помощью $#. Таким
образом, макрос, отображающий число
фактически переданных ему аргументов,
может иметь вид
define(`nargs', `$#') => nargs =>0 nargs() =>1 nargs(arg1, arg2, arg3) =>3
Для определения, в тексте расширения,
всех фактических аргументов (одновременно),
без помещения их в кавычки и разделяя
их запятыми, может быть использована
нотация $*. Например
define(`echo', `$*') => echo(arg1, arg2, arg3 , arg4) =>arg1,arg2,arg3 ,arg4
Часто каждый аргумент должен быть
помещен в кавычки, и нотация вида $@
может это обработать. Это подобно $*,
за исключением того, что в кавычки
помещается каждый аргумент. Простым
примером этого является:
define(`echo', `$@') => echo(arg1, arg2, arg3 , arg4) =>arg1,arg2,arg3 ,arg4
А куда делись кавычки? Естественно, они
были "съедены", когда расширенный
текст был перечитан m4. Для того,
чтобы продемонстрировать разницу,
попробуйте
define(`echo1', `$*') => define(`echo2', `$@') => define(`foo', `This is macro `foo'.') => echo1(foo) =>This is macro This is macro foo.. echo2(foo) =>This is macro foo.
Посмотрите секцию Трассировка макро-вызовов, если вы этого не поняли.
В тексте расширения, самостоятельный
знак `$', который не сопровождается
ничем что понимается m4, просто
копируется в макро-расширение, также
как и любой другой текст.
define(`foo', `$$$ hello $$$') => foo =>$$$ hello $$$
Если необходимо расширить макрос в
что-нибудь подобное `$12', поместите
пару кавычек после $. Это предохранит
m4 от интерпретации знака $
как обрашение к какому-либо аргументу.
Любое макро-определение может быть
удалено с помощью встроенного макроса
undefine:
undefine(name)
который удаляет макрос с именем name. Макро-имя name должно быть помещено в кавычки, поскольку, в противном случае, оно будет расширено.
Встроенный макрос undefine имеет
пустое (void) расширение.
foo =>foo define(`foo', `expansion text') => foo =>expansion text undefine(`foo') => foo =>foo
Не будет ошибкой если для имени name
не определено макро-расширение. В таком
случае, undefine просто ничего не
делает.
Встроенный макрос undefine
распознается только при наличии
параметров.
Есть
возможность осуществлять переименование
любого уже определенного макроса. Для
выполнения этого, необходимо использовать
встроенный макрос defn:
defn(name)
который осуществляет расширение заключенного в кавычки определения имени name. Если аргумент не является определенным макросом, то расширение будет пустым (void).
Если имя name является макросом,
определенным пользователем, то заключенное
в кавычки определение будет просто
заключенным в кавычки текстом расширения.
Если имя name является встроенным
макросом, то расширение будет специальным
токеном, который указывает на внутреннее
определение встроенного макроса. Этот
токен имеет смысл только как второй
аргумент для define (и pushdef),
и игнорируется в любом другом контексте.
Его номальное использование легче
понять на примере, который показывает
как переименовать undefine в zap:
define(`zap', defn(`undefine')) => zap(`undefine') => undefine(`zap') =>undefine(zap)
Таким способом, defn может быть
использован для копирования
макро-определений, а также определений
встроенных макросов. Даже когда
оригинальный макрос удален, другое имя
может быть использовано для доступа к
определению.
Встроенный макрос defn распознается
только при наличии параметров.
Существует возможность временного
переопределения макроса, с возвращением
предыдущего определения позже. Это
выполняется с помощью встроенных
макросов pushdef и popdef:
pushdef(name [, expansion]) popdef(name)
которые очень подобны встроенным
макросам define и undefine.
Работа этих макросов очень похожа на
работу стека. С помощью pushdef
макросы переопределяются временно,
поскольку pushdef заменяет существующее
определение имени name, сохраняя
предыдущее определение, перед тем как
устанавливает новое. Если предыдущее
определение не существует, то поведение
pushdef подобно define.
Если макрос имеет несколько определений
(из которых доступно только одно), то
самое последнее, "верхнее",
определение может быть удалено с помощью
popdef. Если предидущего определения
не существует, то поведение popdef
подобно undefine.
define(`foo', `Expansion one.') => foo =>Expansion one. pushdef(`foo', `Expansion two.') => foo =>Expansion two. popdef(`foo') => foo =>Expansion one. popdef(`foo') => foo =>foo
Если макрос имеет несколько определений
и переопределение выполнялось с помощью
define, то последнее, "верхнее",
определение заменяется новым
определением. Если определение удаляется
с помощью undefine, то удаляются все
определения, а не только "верхнее".
define(`foo', `Expansion one.') => foo =>Expansion one. pushdef(`foo', `Expansion two.') => foo =>Expansion two. define(`foo', `Second expansion two.') => foo =>Second expansion two. undefine(`foo') => foo =>foo
Существует возможность временного
переопределения встроенных макросов
с помощью pushdef и defn.
Макросы pushdef и popdef
распознаются только при наличии
параметров.
С помощью indir любой макрос
можетбыть вызван косвенно:
indir(name, ...)
что в результате приводит к вызову
макроса с именем name, которому
передаются все оставшиеся аргументы.
Это может быть использовано для вызова
макросов с "недопустимыми" именами
(define позволяет определять такие
имена):
define(`$$internal$macro', `Internal macro (name `$0')') => $$internal$macro =>$$internal$macro indir(`$$internal$macro') =>Internal macro (name $$internal$macro)
Смысл заключается в том, что большие
пакеты макросов могут иметь определения
приватных макросов, которые не должны
быть вызваны каким-либо случайным
образом. Такие приватные макросы могут
быть вызваны только с помощью
встроенного макроса indir.
Встроенные макросы могут быть вызваны
косвенно с помощью builtin:
builtin(name, ...)
что в результате приводит к вызову встроенного макроса с именем name, которому передаются все оставшиеся аргументы. Это может быть использовано когда имени name дано другое определение которое скрывает оригинал.
Макрос builtin распознается только
при наличии параметров.
Возможность использования макросов которые способны только расширяться в простой текст не достаточна. Нам необходима возможность выполнять различные расширения макросов, в зависимости от условий, возникающих во время выполнения Например, нам необходима возможность учитывать некоторые условия. Кроме того, нам необходима возможность использования каких-либо циклических конструкций, так, чтобы мы могли выполнять что-либо какое-то количество раз, или пока какое-либо условие оценивается как истинное.
В m4
существует два различных встроенных
макроса для обработки условий. Первым
из них является ifdef:
ifdef(name, string-1, opt string-2)
который предоставляет возможность
проверять определен какой-либо макрос
или нет. Если имя name является
определенным макросом, то ifdef
расширяется в строку string-1, а в
противном случае, в строку string-2.
Если string-2 опущена, то полагается
что ее значение - пустая строка (что
согласовано с нормальными правилами).
ifdef(`foo', ``foo' is defined', ``foo' is not defined') =>foo is not defined define(`foo', `') => ifdef(`foo', ``foo' is defined', ``foo' is not defined') =>foo is defined
Макрос ifdef распознается только
при наличии параметров.
Другим, более
мощным встроенным макросом, для обработки
условий, является ifelse. В зависимости
от числа принимаемых аргументов, он
может быть использован как средсство
вставки длинных комментариев, как
конструкция if-else или как
разветвитель:
ifelse(comment) ifelse(string-1, string-2, equal, opt not-equal) ifelse(string-1, string-2, equal, ...)
При использовании только одного
аргумента, ifelse просто отбрасывает
аргумент и не производит вывод. Это
общая идиома m4 для вставки блока
комментариев, используемая как
альтернатива использованию повторяющихся
dnl. Такое специальное использование
распознается в GNU m4, и, таким
образом, никогда не вызывает предупреждения
о нехватке аргументов.
Если ifelse вызывается с тремя
или четырьмя аргументами, то он будет
расширяться в equal, когда строки
string-1 и string-2 равны. В противном
случае, он расширяется в not-equal.
ifelse(foo, bar, `true') => ifelse(foo, foo, `true') =>true ifelse(foo, bar, `true', `false') =>false ifelse(foo, foo, `true', `false') =>true
Однако, ifelse способен
принимать более четырех аргументов.
При предоставлении более четырех
аргументов, ifelse работает подобно
инструкциям case или switch
традиционных языков программирования.
Если string-1 и string-2 равны, то
ifelse расширяется в equal. В
противном случае, процедура повторяется,
а первые три аргумента отбрасываются.
Пример такого вызова:
ifelse(foo, bar, `third', gnu, gnats, `sixth', `seventh') =>seventh
Обычно, вариант такого использования
будет несколько сложнее этого примера.
Наиболее часто ifelse используется
в макросах которые реализуют различные
циклы.
Макрос ifelse распознается только
при наличии параметров.
m4 не
обладает непосредственной поддержкой
циклов, но макросы могут быть рекурсивными.
На глубину вложенности рекурсии не
существует никаких ограничений, кроме
тех которые накладываются используемыми
оборудованием и опрерационной системой.
Циклы могут быть запрограммированы с помощью использования рекурсии и средств обработки условий, которые были описаны ранее.
Существует встроенный
макрос shift, который, кроме прочего,
может быть использован для итерации
фактических параметров макроса:
shift(...)
Он принимает любое число аргументов и расширяется списком всех своих аргументов, кроме первого отделенного запятой аргумента, с каждым аргументом заключенным в кавычки.
shift(bar) => shift(foo, bar, baz) =>bar,baz
Примером использования shift,
является макрос который изменяет порядок
следования его аргументов на
противоположный:
define(`reverse', `ifelse($#, 0, , $#, 1, ``$1'',
`reverse(shift($@)), `$1'')')
=>
reverse
=>
reverse(foo)
=>foo
reverse(foo, bar, gnats, and gnus)
=>and gnus, gnats, bar, foo
Хотя этот пример макроса не очень
интересен, он показывает простой способ
создания цикла с помощью shift,
ifelse и рекурсии.
Рассмотрим
простой пример циклического макроса
forloop. Он может быть применен,
например, для простой организации счета:
forloop(`i', 1, 8, `i ') =>1 2 3 4 5 6 7 8
Аргументы имеют имя для итерационной
переменной, стартовое значение, конечное
значение и текст для расширения в каждой
итерации. В этом макросе, макрос i
описан только внутри цикла. После
итерации цикла, он сохраняет предшествующее
значение.
Циклы могут быть вложенными, подобно
forloop(`i', 1, 4, `forloop(`j', 1, 8, `(i, j) ') ') =>(1, 1) (1, 2) (1, 3) (1, 4) (1, 5) (1, 6) (1, 7) (1, 8) =>(2, 1) (2, 2) (2, 3) (2, 4) (2, 5) (2, 6) (2, 7) (2, 8) =>(3, 1) (3, 2) (3, 3) (3, 4) (3, 5) (3, 6) (3, 7) (3, 8) =>(4, 1) (4, 2) (4, 3) (4, 4) (4, 5) (4, 6) (4, 7) (4, 8) =>
Реализация макроса цикла forloop
очень проста. Цикл forloop сам по
себе является оберткой, которая сохраняет
предшествующее определение первого
аргумента, вызывает внутренний макрос
_forloop и переустанавливает
сохраненное определение первого
аргумента.
Макрос _forloop однократно расширяет
четвертый аргумент и проверяет условие
завершения. Если условие завершения не
выполняется, то он инкрементирует
итерационную переменную (использование
предопределенного макроса incr,
see см. секцию Декремент
и инкремент), и выполняет следующую
рекурсию.
Рассмотрим более реальную реализацию
forloop:
define(`forloop',
`pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')')
define(`_forloop',
`$4`'ifelse($1, `$3', ,
`define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')')Следует заметить, что при использовании кавычек требуется внимательность. Только три макро-аргумента не заключены в кавычки, и для каждого аргумента это имеет свой собственный смысл. Попробуйте самостоятельно догадаться почему эти три аргумента не заключены в кавычки, и что произойдет, если их поместить в кавычки.
Теперь, даже несмотря на то, что показанные макросы работоспособны, они не совсем пригодны для повсеместного использования. В них отсутствует даже базовая обработка ошибок в случаях когда начальное значение меньше конечного значения, или когда первый аргумент не является именем. Исправление этих недостатков предоставляется читателю, в качестве упражнения.
При написании макросов для m4,
очень часто оказывается, что они работают
не так как ожидается (что свойственно
многим языкам программирования).
Макропроцессор m4 обеспечивает
некоторую поддержку в отладке макросов.
Если необходимо увидеть во что расширяется
какое-либо имя, можно использовать
встроенный макрос dumpdef:
dumpdef(...)
который принимает любое число аргументов. Если этот встроенный макрос вызван без указания аргументов, то он отображает определения всех известных имен, в противном случае, он отображает определения для указанных имен. Вывод распечатывается непосредственно в стандартный вывод ошибок.
Встроенный макрос dumpdef имеет
пустое (void) расширение.
define(`foo', `Hello world.') => dumpdef(`foo') error-->foo: `Hello world.' => dumpdef(`define') error-->define: <define> =>
Последний пример показывает как отображаются определения встроенных макросов.
Для получения более подробной информации по управлению особенностями отображения см. секцию Управление отладочным выводом.
С помощью встроенных макросов traceon
и traceoff, предоставляется возможность
трассировки макро-вызовов и макро-расширений:
traceon(...) traceoff(...)
Когда макросы traceon и traceoff
вызываются без указания каких-либо
аргументов, то они осуществляют,
соответственно, включение (on) или
выключение (off) трассировки для всех
определенных макросов. Когда эти макросы
вызываются с аргументами, то они
затрагивают только те макросы, имена
которых были указаны при вызове.
Встроенный макросы traceon и
traceoff имеют пустое (void) расширение.
Всякий раз, при вызове трассируемого макроса, после того как осуществлено накопление его аргументов, производится отображение вызова. Если расширение макро-вызова не пустое (void), то расширение может быть отображено после вызова. Вывод распечатывается непосредственно в стандартный вывод ошибок.
define(`foo', `Hello World.') => define(`echo', `$@') => traceon(`foo', `echo') => foo error-->m4trace: -1- foo -> `Hello World.' =>Hello World. echo(gnus, and gnats) error-->m4trace: -1- echo(`gnus', `and gnats') -> ``gnus',`and gnats'' =>gnus,and gnats
Числа между дефисами отображают глубину вложенности расширения. Каждый уровень вложенности упрощает расширение во внешнем уровне, но уровень вложенности увеличивается когда аргументы содержат макро-вызовы не помещенные в кавычки.
Для получения дополнительной информации по управлению отображением см. секцию Управление отладочным выводом.
Опция `-d'
командной строки запуска m4
управляет степенью детализации отладочной
информации, когда используются описанные
в предыдущей секции макросы.
В качестве флагов flags, сопровождающих эту опцию, может быть один или более нижеперечисленных флагов:
t
m4.
a
traceon.
e
traceon.
q
c
x
f
l
p
i
V
Если при указании опции `-d', то, по умолчанию, автоматически устанавливаются флаги `aeq'. Показанные в двух предыдущих секциях примеры подразумевают использование установки флагов по умолчанию.
Существует
встроенный макрос debugmode, который
позволяет управлять формой отладочного
вывода "на лету":
debugmode(opt flags)
Аргумент flags должен быть подмножеством букв, перечисленных выше. Как специальный случай, если аргумент начинается с `+', флаги добавляются к текущим флагам отладки flags, и если аргумент начинается с `и', то флаги удаляются. Если аргументы не указаны, то отладочные флаги установлены в отсутствующие (как будто опция `-d' не была указана), и, наконец, при "пустом" аргументе, флаги устанавливаются в значение по умолчанию.
Отладочный и трассировочный вывод может
быть перенаправлен в файлы как с помощью
использования опции `-o' командной
строки m4, так и с помощью
использования встроенного макроса
debugfile:
debugfile(opt filename)
отправит весь последующий отладочный
и трассировочный вывод в файл filename.
Если указано пустое имя файла filename,
то отладочный и трассировочный вывод
будет отброшен, а если макрос debugfile
вызван без аргументов, то отладочный и
трассировочный вывод будет отправлен
в стандартный вывод ошибок.
Эта глава описывает различные встроенные
макросы управляющие вводом m4.
Встроенный
макрос dnl, вплоть до первого
встреченного символа новой строки,
читает и отбрасывает все символы, включая
символ новой строки:
dnl
и он часто используется совместно с
макросом define, для удаления
символов новой строки которые сопровождают
вызовdefine. Таким образом
define(`foo', `Macro `foo'.')dnl A very simple macro, indeed. foo =>Macro foo.
отбрасывает ввод, вплоть до символа новой строки, и включая символ новой строки, что противоположно трактовке комментариев (см. секцию Комментарии).
Обычно, dnl немедленно сопровождается
символом новой строки или пробелами.
GNU m4 генерирует предупреждающее
сообщение когда dnl сопровождается
открытой скобкой. В этом случае,dnl
будет накапливать и обрабатывать все
аргументы, осуществляя поиск парной
закрывающей скобки. При этом будут иметь
место все предсказуемые побочные
эффекты, происходящие в результате
такого накопления. dnl не возвращает
вывода. Ввод, последующий за парной
закрывающей скобкой, вплоть до и включая
символ новой строки (на той строке где
обнаружена парная скобка), будет отброшен.
Символы
кавычек (иначе, символы цитирования),
установленные по умолчанию, могут быть
заменены с помощью встроенного макроса
changequote:
changequote(opt start, opt end)
где start является новым ограничителем
начала цитирования (стартовые кавычки),
а end является новым ограничителем
завершения цитирования (завершающие
кавычки). Если любой из аргументов
отсутствует, то вместо отсутствующего
аргумента используется соответствующий
символ по умолчанию (` или ').
Встроенный макрос changequote имеет
пустое (void) расширение.
changequote([, ]) => define([foo], [Macro [foo].]) => foo =>Macro foo.
Если односимвольные значения не желательны, то start и end могут быть любой длины.
changequote([[, ]]) => define([[foo]], [[Macro [[[foo]]].]]) => foo =>Macro [foo].
Изменение значений символов кавычек (цитирования) на пустые строки будет эффективно блокировать механизм помещения в кавычки (цитирования), не предоставляя способа поместить текст в кавычки (цитировать текст).
define(`foo', `Macro `FOO'.') => changequote(, ) => foo =>Macro `FOO'. `foo' =>`Macro `FOO'.'
В m4 не существует способа поместить
строку в кавычки (цитировать строку)
используя какие-либо непарные отсутствующие
кавычки, кроме как использовать
changequote для изменения текущих
кавычек.
Никакая строка в кавычках не должна начинаться с буквы или символа подчеркивания `_', поскольку они будут смешиваться с именами во вводе. Выполнение таких действий блокирует мехпнизм цитирования.
Ограничители
комментариев, принимаемые по умолчанию,
могут быть заменены с помощью встроенного
макросаchangecom:
changecom(opt start, opt end)
где start является новым ограничителем
начала комментария, а end является
новым ограничителем завершения
комментария. Если любой из аргументов
отсутствует, то вместо отсутствующего
аргумента используется соответствующий
символ по умолчанию (` и символ
новой строки). Ограничители комментариев
могут быть любой длины.
Встроенный макрос changecom имеет
пустое (void) расширение.
define(`comment', `COMMENT') => # A normal comment =># A normal comment changecom(`/*', `*/') => # Not a comment anymore =># Not a COMMENT anymore But: /* this is a comment now */ while this is not a comment =>But: /* this is a comment now */ while this is not a COMMENT
Примечательно, что комментарии копируются в вывод подобно строкам помещенным в кавычки. Если необходимо выполнять расширение текста внутри комментария, то можно поместить в кавычки ограничитель начала комментария.
Вызов макроса changecom без аргументов
полностью блокирует механизм
комментирования.
define(`comment', `COMMENT') => changecom => # Not a comment anymore =># Not a COMMENT anymore
Макросchangewordи вся ассоциируемая с ним функциональность является экспериментальными. Все это доступно только в том случае, если при запуске скриптаconfigure, в процессе инсталляции GNUm4, была указана опция--enable-changeword. Эта функциональность может быть изменена или даже удалена в будущем, поэтому на нее не стоит полагаться. Пожалуйста, направьте свои комментарии об этом по адресу, которому необходимо отправлять сообщения об ошибках.
Файл который будет обрабатываться m4
разделен на строки помещенные в кавычки,
слова (потенциальные имена макросов) и
простые токены (любые другие одиночные
символы). Изначально, любое слово описано
с помощью следующего регулярного
выражения:
[_a-zA-Z][_a-zA-Z0-9]*
Используя changeword, вы можете
изменить это регулярное выражение.
Ослабление лексических правил m4
может быть полезно (например) если вы
хотите попытаться транслировать файл
с числами:
changeword(`[_a-zA-Z0-9]+') define(1, 0) =>1
Ужесточение лексических правил - менее полезно, поскольку это, в основном, сделает некоторые встроенные макросы не доступными. Вы могли бы использовать это для предотвращения непредвиденных вызовов встроенных макросов, например:
define(`_indir', defn(`indir')) changeword(`_[_a-zA-Z0-9]*') esyscmd(foo) _indir(`esyscmd', `ls')
Поскольку m4 конструирует свои
слова посимвольно, то существует
ограничение на регулярные выражения,
которые могут быть переданы changeword.
Таким образом, если ваше регулярное
выражение принимает `foo', то оно
должно также принимать `f' и `fo'.
changeword имеет другую функцию.
Когда принятое регулярное выражение
содержит любые подвыражения в скобках,
тогда текст, за пределами первого из
них, отбрасывается до поиска символа.
Таким образом:
changecom(`/*', `*/') changeword(`#\([_a-zA-Z0-9]*\)') #esyscmd(ls)
Теперь, m4 требует метку `#'
в начале каждого вызова макроса, благодаря
чему m4 может быть использован
для препроцессирования shell-скриптов,
без получения "проглатывания"
команды shift, и "плоского"
текста, без потери различных общих слов.
Подстановка макросовm4 базирована
на тексте, в то время как TeX
базируется на токенах changeword
может изменить это различие. Например,
существует такая же идея представленная
в TeX и в m4. Сначала, версия
TeX:
\def\a{\message{Hello}}
\catcode`\@=0
\catcode`\\=12
=>@a
=>@bye
Теперь, версия m4:
define(a, `errprint(`Hello')') changeword(`@\([_a-zA-Z0-9]*\)') =>@a
В версии примера TeX, первая строка
описывает макрос a для печати
сообщения `Hello'. Вторая строка
описывает @ для использования
вместо \, как escape-символа. Третья
строка описывает \ как нормально
печатаемый символ, а не escape-символ.
Четвертая строка вызывает макрос a.
Таким образом, когда TeX выполняется
на этом файле, то он отображает сообщение
`Hello'.
Когда версия примера m4
пропускается через m4, то это
выводит `errprint(Hello)'. Смысл этого
заключается в том, что TeX выполняет
лексический анализ макро-определения
тогда, когда макрос определен. m4
выполняет только сохранение текста,
откладывая лексический анализ до момента
использования макроса.
Вы должны заметить, что использование
changeword будет значительно замедлять
m4 (раз в семь).
Существует
возможность сохранить некоторый текст
до того как будет обнаружено окончание
нормального ввода. Текст может быть
сохранен, для того чтобы он был вновь
прочитан m4 когда нормальный ввод
будет исчерпан. Эта особенность
используется обычно для инициализации
действий очистки перед нормальным
выходом, например, удаление временных
файлов.
Для сохранения текста ввода, необходимо
использовать встроенный макрос m4wrap:
m4wrap(string, ...)
который сохраняет строку string и остальные аргументы в сохранном месте, для того чтобы перечитать вновь, после достижения завершения ввода.
define(`cleanup', `This is the `cleanup' actions. ') => m4wrap(`cleanup') => This is the first and last normal input line. =>This is the first and last normal input line. ^D =>This is the cleanup actions.
Сохраненный ввод перечитывается только
когда будет обнаружено окончание
нормального ввода и при этом не будет
использован встроенный макрос m4exit,
для выхода из m4.
Существует возможность вызвать из
сохраненного текста m4wrap, но тогда
порядок, в котором будет перечитан
сохраненный текст, - не определен. Если
m4wrap не использован рекурсивно,
то сохраненные части текста будут
перечитаны в порядке, противоположном
порядку ихнего сохранения, т.е. последний
сохраненный читается первым (LIFO--last in,
first out).
m4 позволяет
включать именованные файлы в любой
точке ввода.
В m4
существует два встроенных макроса для
включения файлов:
include(filename) sinclude(filename)
Оба этих макроса позволяют m4
прочитать именованный файл filename.
Когда будет достигнут конец файла, то
ввод продолжится из предыдущего файла.
Расширением макросов include и
sinclude, таким образом, является
содержимое файла filename.
Отсутствие указанного файла будет
являться ошибкой. Если, при отсутствии
файлов, вы не хотите получать сообщения
об ошибках, то для включения файлов
может быть использован макрос sinclude,
который расширяется в содержимое файла,
если файл существует, или ни во что не
расширяется, когда файл отсутствует.
include(`no-such-file') => error-->30.include:2: m4: Cannot open no-such-file: No such file or directory sinclude(`no-such-file') =>
Предположим, что файл с именем `incl.m4' содержит следующие строки:
Include file start foo Include file end
Обычно, включение файла используется
для вставки содержимого файла внутрь
потока ввода. Содержимое файла будет
прочитано m4 и макро-вызовы,
расположенные в этом файле, будут
расширены:
define(`foo', `FOO') => include(`incl.m4') =>Include file start =>FOO =>Include file end =>
Факт того, что макросы include и
sinclude расширяются в содержимое
файла, может быть использован для
определения макросов которые оперируют
на целых файлах. Существует пример,
который определяет макрос `bar'
для расширения содержимого `incl.m4':
define(`bar', include(`incl.m4')) => This is `bar': >>>bar<<< =>This is bar: >>>Include file start =>foo =>Include file end =><<<
Такое использование макроса include
не тривиально, поскольку файлы могут
содержать строки в кавычках, запятые и
скобки, которые могут интерферировать
со способом работы парсера m4.
Встроенные макросы include и
sinclude распознаются только в случае
наличия аргументов.
GNU
m4 позволяет, чтобы поиск включаемых
файлов осуществлялся не только в текущем
рабочем каталоге, но и в других каталогах.
Если файл не найден в текущем каталоге, и имя файла не является абсолютным, то поиск указанного файла будет осуществляться используя определенные пути поиска. Сначала, поиск осуществляется в каталогах указываемых с помощью опции командной строки `-I', а порядок поиска определяется последовательностью перечисления каталогов. Затем, если установлена переменная окружения `M4PATH', которая содержит список каталогов поиска отделенных друг от друга символом точки с запятой, то поиск осуществляется в соответствии с последовательностью перечисления каталогов в этой переменной окружения.
Если автоматический поиск включаемых файлов вызывает проблемы, то применение отладочного флага `p' (см. секцию Управление отладочным выводом) может помочь изолировать проблему.
Перенаправление (diversions) является
способом временного сохранения вывода.
Вывод m4 может быть перенаправлен
(diverted) во временный файл в любой момент
времени, и вставлен заново в поток
вывода, путем отмены перенаправления
(undiverted), в любой момент времени позже.
Нумерованные перенаправления
(diversions) считаются от 0, перенаправление
(diversion) номер 0 является нормальным
потоком вывода. Число одновременных
перенаправлений (diversions) ограничено в
основном размерами памяти используемой
для их описания, поскольку GNU m4
пытается хранить перенаправления
(diversions) в пямяти. Однако, существует
предел для общего использования памяти
всеми перенаправлениями (diversions). В
настоящее время этот предел равен 512K.
При достижении этого максимума,
открывается временный файл для получения
содержимого наибольшего перенаправления
(diversion) находящегося в памяти, и освобождения
этой памяти для других перенаправлений
(diversions). Таким образом, теоретически
возможно, что число перенаправлений
(diversions) будет лимитировано количеством
доступных файловых дескрипторов.
Перенаправление вывода осуществляется
с помощью использования встроенного
макроса divert:
divert(opt number)
где число number является перенаправлением (diversion) которое будет использовано. Если число number отсутствует, то подразумевается, что оно нуль.
Встроенный макрос divert имеет
пустое (void) расширение.
Когда весь ввод m4 будет обработан,
то все существующие перенаправления
будут автоматически отменены (undiverted),
в соответствии с порядком нумерации.
divert(1) This text is diverted. divert => This text is not diverted. =>This text is not diverted. ^D => =>This text is diverted.
Последовательные вызовы divert с
одним и тем же аргументом не осуществляют
перезапись перенаправленного (diverted)
ранее текста, а добавляют к нему текст.
Если вывод перенаправлен (diverted) в не существующее перенаправление (diversion), то он просто отбрасывается. Это может быть использовано для подавления нежелательного вывода. Общим примером нежелательного вывода являются завершающие символы новой строки после макро-определения. Здесь показано как отменить их.
divert(-1) define(`foo', `Macro `foo'.') define(`bar', `Macro `bar'.') divert =>
Это является общей идиомой программирования
в m4.
Отмена пренаправления
текста может быть указана явно, с помощью
встроенного макроса undivert:
undivert(opt number, ...)
который отменяет перенаправление в соответствии с указанными аргументами, согласно порядка их указания. Если аргументы не указаны, то осуществляется отмена всех перенаправлений, в соответствии с порядком нумерации.
Встроенный макрос undivert имеет
пустое (void) расширение.
divert(1) This text is diverted. divert => This text is not diverted. =>This text is not diverted. undivert(1) => =>This text is diverted. =>
Примечательны две пустые строки. Одна
из них появляется из-за символа новой
строки, следующей за макросом undivert,
а вторая - из-за символа новой строки,
следующей за макросом divert! Подобно
этому, перенаправление часто начинается
с пустой строки.
Когда осуществляется отмена
перенапраления перенаправленного
текста, перечитывание ввода m4
не выолняется, а перенаправленный
текст непосредственно копируется в
текущий вывод, и, таким образом, нет
ошибок отмены перенаправления внутри
перенаправленного текста.
Когда осуществляется отмена перенапраления перенаправленного текста, перенаправленный тект отбрасывается из-за чего нет возможности использовать перенаправленный текст более одного раза.
divert(1) This text is diverted first. divert(0)undivert(1)dnl => =>This text is diverted first. undivert(1) => divert(1) This text is also diverted but not appended. divert(0)undivert(1)dnl => =>This text is also diverted but not appended.
Попытки отменить текущее перенаправление молчаливо игнорируются.
GNU
m4 позволяет отменять перенаправление
для именованных файлов. Для данного не
численного аргумента, в текущий вывод
будет скопировано без интерпретации
содержимое именованного файла. Это
дополняет встроенный макрос include
(см. секцию Включение
именованых файлов). Для иллюстрации
различий, предположим, что файл `foo'
содержит слово `bar':
define(`bar', `BAR') => undivert(`foo') =>bar => include(`foo') =>BAR =>
divnum
расширяется в номер текущего перенаправления (diversion).
Initial divnum =>Initial 0 divert(1) Diversion one: divnum divert(2) Diversion two: divnum divert => ^D => =>Diversion one: 1 => =>Diversion two: 2
Последний вызов макроса divert без
аргументов необходим, поскольку не
перенаправленный (undiverted) текст будет
иначе перенаправлять (be diverted) сам себя.
Часто, когда
вывод перенаправлен (diverted), не известно
будет-ли перенаправленный текст реально
необходим. Поскольку все не пустые
перенаправления возвращаются обратно
в основной поток вывода когда обнаружен
конец ввода, то неодходим метод для
отбрасывания перенаправления (diversion).
Если все перенаправления (diversions) должны
быть отброшены, то проще всего завершить
ввод m4 вызовом `divert(-1)'
который сопровождается явным `undivert':
divert(1) Diversion one: divnum divert(2) Diversion two: divnum divert(-1) undivert ^D
Не производится никакого вывода вообще.
Очистка выбранных перенаправлений (diversions) может быть выполнена следующими макросами:
define(`cleardivert', `pushdef(`_num', divnum)divert(-1)undivert($@)divert(_num)popdef(`_num')') =>
Вызов осуществляется подобно undivert,
но результатом будет очистка перенаправлений
(diversions), заданных аргументами. (Этот
макрос имеет одну мерзкую ошибку! Вы
должны попытаться увидеть если вы
обнаружили ее и скорректировали ее)
В m4 существует некоторое
количество встроенных макросов для
осуществления разного рода манипуляций
над текстом: извлечение подстроки,
поиск, замена и т.д.
Длина
строки может быть вычислена с помощью
встроенного макроса len:
len(string)
который расширяется в длину строки string, как десятичное число.
len() =>0 len(`abcdef') =>6
Встроенный макрос len распознается
только при наличии аргументов.
Поиск подстрок может быть
осуществлен с помощью встроенного
макроса index:
index(string, substring)
который расширяется в индекс первого
появления подстроки substring в строке
string. Первый символ в строке string
имеет индекс 0. Если подстрока substring
не обнаружена в строке string, то
макрос index расширяется в `-1'.
index(`gnus, gnats, and armadillos', `nat') =>7 index(`gnus, gnats, and armadillos', `dag') =>-1
Встроенный макрос index распознается
только приналичии аргументов.
Поиск
регулярных выражений осуществляется
с помощью встроенного макроса regexp:
regexp(string, regexp, opt replacement)
который осуществляет поиск регулярного выражения regexp в строке string. Для регулярных выражений используется такой же синтаксис как и в GNU Emacs. Для более полной информации обратитесь к секции "Syntax of Regular Expressions" в руководстве по GNU Emacs "The GNU Emacs Manual".
Если строка замены replacement опущена,
то макрос regexp расширяется в
индекс первого совпадения строки,
заданной регулярным выражением regexp,
в строку string. Если строка, заданная
регулярным выражением regexp, ни с
чем не совпадает в строке string, то
макрос regexp расширяется в -1.
regexp(`GNUs not Unix', `\<[a-z]\w+') =>5 regexp(`GNUs not Unix', `\<Q\w*') =>-1
Если строка замены replacement задана,
то макрос regexp заменяет расширение
в значение этого аргумента с `\n'
подставленным вместо текста совпадающего
с n-м подвыражением строки замены
regexp заключенным в скобки. Указание
`\&' соответствует всему тексту,
который совпадает с регулярным выражением
regexp.
regexp(`GNUs not Unix', `\w\(\w+\)$', `*** \& *** \1 ***') =>*** Unix *** nix ***
Встроенный макрос regexp распознается
только приналичии аргументов.
Извлечение
подстроки может быть осуществлено с
помощью встроенного макроса substr:
substr(string, from, opt length)
который расширяется в подстроку строки string, которая начинается в индексе from, и состоит из length символов, или продолжается вплоть до конца строки string, если параметр length отсутствует. Начальный индекс строки всегда 0.
substr(`gnus, gnats, and armadillos', 6) =>gnats, and armadillos substr(`gnus, gnats, and armadillos', 6, 5) =>gnats
Встроенный макрос substr распознается
только приналичии аргументов.
Трансляция
символов может быть выполнена с помощью
встроенного макроса translit:
translit(string, chars, replacement)
который расширяется в строку string, при этом каждый символ строки string, который обнаружен в строке chars транслируется в символ, с таким же индексом, из строки replacement.
Если строка replacement короче строки chars, то лишние символы удаляются из расширения. Если строка replacement не указана, то все символы строки string, которые присутствуют в строке chars будут удалены из расширения.
Обе строки, chars и replacement, могут содержать указания диапазонов символов, напримен, `a-z' (подразумевая все символы латинского алфавита в нижнем регистре) или `0-9' (подразумевая все цифры). Для включения символа дефиса `-' в строку chars или строку replacement, необходимо поместить его как первый или как последний символ в строке.
Не будет ошибкой если последний символ, указанный в диапазоне, будет `больше' чем первый. В этом случае, диапазон символов перечисляется в обратном порядке, то есть `9-0' подразумевает строку `9876543210'.
translit(`GNUs not Unix', `A-Z') =>s not nix translit(`GNUs not Unix', `a-z', `A-Z') =>GNUS NOT UNIX translit(`GNUs not Unix', `A-Z', `z-a') =>tmfs not fnix
Первый пример удаляет все символы верхнего регистра, второй, преобразует символы нижнего регистра в символы верхнего регистра, и третий, `отзеркаливает' все символы верхнего регистра, в процессе преобразования их в символы нижнего регистра. Первые два случая более общие.
Встроенный макрос translit
распознается только при наличии
аргументов.
Глобальная замена в строке может быть
выполнена с помощью встроенного макроса
patsubst:
patsubst(string, regexp, opt replacement)
который осуществляет поиск совпадений регулярного выражения regexp в строке string и осуществляет замену на строку replacement, при каждом обнаруженном совпадении. Для регулярных выражений используется такой же синтаксис как и в GNU Emacs.
Части строки string, которые не конвертированы в результате совпадения с регулярным выражением regexp, копируются в результат расширения. При обнаружении каждого совпадения, процесс поиска продолжается с конца совпадения, таким образом, символ из строки string никогда не будет подставлен дважды. Если регулярное выражение regexp совпадает со строкой нулевой длины, то начальная позиция для поиска инкрементируется, во избежание бесконечных циклов.
После выполнения замены, и вставки строки replacement в расширение, осуществляется подстановка `\n' вместо текста совпадающего с n-м подвыражением строки регулярного выражения regexp заключенного в скобки. Указание `\&' соответствует всему тексту, который совпадает с регулярным выражением regexp.
Строка replacement может отсутствовать. В этом случае, текст, совпадающий со строкой regexp, будет удален.
patsubst(`GNUs not Unix', `^', `OBS: ') =>OBS: GNUs not Unix patsubst(`GNUs not Unix', `\<', `OBS: ') =>OBS: GNUs OBS: not OBS: Unix patsubst(`GNUs not Unix', `\w*', `(\&)') =>(GNUs)() (not)() (Unix) patsubst(`GNUs not Unix', `\w+', `(\&)') =>(GNUs) (not) (Unix) patsubst(`GNUs not Unix', `[A-Z][a-z]+') =>GN not
Здесь показан более реалистичный пример,
котоорый осуществляет капитализацию
(первый символ в верхнем регистре, а
остальные - в нижнем) слова или целой
фразы, заменяя вызововы макросов upcase
и downcase в строках.
define(`upcase', `translit(`$*', `a-z', `A-Z')')dnl
define(`downcase', `translit(`$*', `A-Z', `a-z')')dnl
define(`capitalize1',
`regexp(`$1', `^\(\w\)\(\w*\)', `upcase(`\1')`'downcase(`\2')')')dnl
define(`capitalize',
`patsubst(`$1', `\w+', `capitalize1(`\&')')')dnl
capitalize(`GNUs not Unix')
=>Gnus Not Unix
Встроенный макрос patsubst распознается
только при наличии аргументов.
Форматирование вывода может быть
выполнено с помощью встроенного макроса
format:
format(format-string, ...)
который работает подобно функции printf
языка C. Первый аргумент является
форматируемой строкой, которая может
содержать `%' спецификации, а
расширением макроса format будет
форматированная строка.
Использование этого встроенного макроса лучше всего пояснить на нескольких примерах:
define(`foo', `The brown fox jumped over the lazy dog') => format(`The string "%s" is %d characters long', foo, len(foo)) =>The string "The brown fox jumped over the lazy dog" is 38 characters long
Использование встроенного макроса
forloop описывается в секции Циклы
и рекурсия, этот пример показывает
как использовать макрос format для
получения табулированного вывода.
forloop(`i', 1, 10, `format(`%6d squared is %10d ', i, eval(i**2))') => 1 squared is 1 => 2 squared is 4 => 3 squared is 9 => 4 squared is 16 => 5 squared is 25 => 6 squared is 36 => 7 squared is 49 => 8 squared is 64 => 9 squared is 81 => 10 squared is 100
Встроенный макрос format смоделирован
подобно функции `printf' языка
программирования ANSI C, и он поддерживает
обычные `%' спецификаторы: `c',
`s', `d', `o', `x',
`X', `u', `e', `E'
and `f'; он поддерживает указание
ширин и точности полей, а также модификаторы
`+', `-', ` ', `0',
`#', `h' and `l'. Для
получения информации, более детально
описывающей функционирование printf,
следует обратиться к руководству по
библиотеке языка программирования C.
Целочисленная
арифметика, включенная в m4,
использует C-подобный синтаксис. Как
согласованные сокращения, существуют
встроенные макросы для выполнения
операций простого инкрементирования
и декрементирования.
Инкремент и декремент целочисленных
значений поддерживается использованием
встроенных макросов incr и decr:
incr(number) decr(number)
которые расширяются в соответственно инкрементированное или декрементированное на единицу численное значение number.
incr(4) =>5 decr(7) =>6
Встроенные макросы incr и decr
распознаются только при наличии
аргументов.
Целочисленные выражения могут быть
оценены с помощью встроенного макроса
eval:
eval(expression, opt radix, opt width)
который расширяется в значение выражения expression.
Выражения могут содержать следующие знаки операций, которые перечисляются в порядке уменьшения приоритета.
-
**
* / %
+ -
<< >>
== != > >= < <=
!
~
&
^
|
&&
||
Все знаки операций, кроме возведения в степень, лево-ассоциированные.
Примечательно, что множество реализаций
m4 используют `^' как
альтернативный знак операции для
возведения в степень, в то время как
множество других реализаций используют
`^' как побитное "ИСКЛЮЧАЮЩЕЕ
ИЛИ" ("XOR"). GNU m4 изменил
его поведение: раньше `^'
использовался для возведения в степень,
а теперь - для вычисления побитного
"ИСКЛЮЧАЮЩЕЕ ИЛИ" ("XOR").
Числа без специального префикса являются десятичными. Простой префикс `0' указывает на то, что число восьмеричное. `0x' указывает шестнадцатеричное число. `0b' указывает двоичное число. `0r' указывает число имеющего основание системы счисления от 1 до 36: префикс немедленно сопровождается десятичным выражением указывающим основание системы счисления, после которого следует двоеточие, а затем, цифры являющиеся числом. Для любого основания системы счисления используются следующие цифры: `0', `1', `2', .... после `9', цифрами являются `a', `b' ... вплоть до `z'. Буквы верхнего и нижнего регистра могут быть использованы взаимозаменяемо в числах префикса и числах, префиксах и как цифры чисел.
При необходимости, для группирования
подвыражений могут быть использованы
скобки. Операции отношения возвращают
1, в случае результата "истина"
(True), и 0, в случае результата
"ложь" (False).
Здесь представлено несколько примеров
использования встроенного макроса
eval.
eval(-3 * 5) =>-15 eval(index(`Hello world', `llo') >= 0) =>1 define(`square', `eval(($1)**2)') => square(9) =>81 square(square(5)+1) =>676 define(`foo', `666') => eval(`foo'/6) error-->51.eval:14: m4: Bad expression in eval: foo/6 => eval(foo/6) =>111
Второй особенностью, которую показывает
последний пример, является то, что
встроенный макрос eval не обрабатывает
макро-имена, даже если они расширяются
в допустивые выражения (или часть
допустимого выражения). Следовательно,
все макросы должны быть расширены до
того как они будут переданы макросу
eval.
Если указано основание системы
счисления radix, то оно определяет
основание системы счисления, используемое
в расширении. По умолчанию, основание
системы счисления 10. Результат eval
всегда имеет знак. Аргумент width
специфицирует минимальную ширину
вывода. Результат дополняется нулями
для дополнения текста расширения до
требуемой ширины.
eval(666, 10) =>666 eval(666, 11) =>556 eval(666, 6) =>3030 eval(666, 6, 10) =>0000003030 eval(-666, 6, 10) =>-000003030
Следует заметить, что основание системы счисления radix не может быть больше чем 36.
Встроенный макрос eval распознается
только при наличии аргументов.
Существует несколько встроенных макросов
m4 которые позволяют запускать
на выполнение команды UNIX из m4.
Любая команда командного
интерпритатора shell может быть выполнена
с помощью использования встроенного
макроса syscmd:
syscmd(shell-command)
который запускает на выполнение команду shell-command как команду командного интерпритатора shell.
Встроенный макрос syscmd имеет
пустое (void) расширение, а не вывод
команды shell-command! Вывод или сообщения
об ошибках команды shell-command не
читаются m4 непосредственно. См.
секцию Чтение
вывода команд если вам необходимо
обрабатывать вывод команды.
Перед запуском команды, m4
"сбрасывает" свои буферы вывода.
По умолчанию, стандартный ввод, вывод
и вывод ошибок для shell-command являются
теми же самыми, что и для m4.
Встроенный макрос syscmd распознается
только при наличии аргументов.
Если необходимо
прочитать вывод команды UNIX, то необходимо
использовать встроенный макрос esyscmd:
esyscmd(shell-command)
который расширяет стандартный вывод команды командного интерпретатора shell-command.
Перед запуском команды, m4
"сбрасывает" свои буферы вывода.
По умолчанию, стандартный ввод и вывод
ошибок для shell-command являются теми
же самыми, что и для m4. Вывод
ошибок команды shell-command не является
частью расширения макроса: он появляется
как часть вывода ошибок m4.
Предположим, что вы находитесь в
каталоге `checks' дистрибутива GNU m4,
тогда:
define(`vice', `esyscmd(grep Vice ../COPYING)') => vice => Ty Coon, President of Vice =>
Примечательно, что расширение макроса
esyscmd имеет сопровождающие символы
новой строки.
Встроенный макрос esyscmd
распознается только при наличии
аргументов.
Для проверки результата завершения
команды командного интерпретатора,
можно использовать встроенный макрос
sysval:
sysval
который расширяется в статус завершения
последней выполненной с помощью макроса
syscmd или esyscmd команды
командного интерпретатора.
syscmd(`false') => ifelse(sysval, 0, zero, non-zero) =>non-zero syscmd(`true') => sysval =>0
Крманды
командного интерпретатора, которые
запускаются с помощью встроенных
макросов syscmd и/или esyscmd,
могут нуждаться во временных файлах
для вывода или каких-либо других нужд.
Существует встроенный макрос maketemp
позволяющий создавать имена для временных
файлов:
maketemp(template)
который расширяется в имя несуществующего
файла, созданного из строки template,
которая должна заканчиваться строкой
`XXXXXX'. Шесть символов X
будет заменено, как правило чем-либо
включающим идентификатор процесса m4,
что позволяет создать уникальное имя
файла.
maketemp(`/tmp/fooXXXXXX') =>/tmp/fooa07346 maketemp(`/tmp/fooXXXXXX') =>/tmp/fooa07346
Как показано в примере, несколько вызовов
макроса maketemp может быть расширено
в одну и ту же строку, поскольку критерием
выбора является существование файла.
Если файл не был создан до следующего
вызова, то два последовательных вызова
могут быть расширены в одно и то же имя
файла.
Встроенный макрос maketemp
распознается только при наличии
аргументов.
Эта глава описывает различные встроенные макросы, которые реально не могли принадлежать какой-либо из предыдущих глав.
Сообщения об ошибках могут быть напечатаны
с помощью использования встроенного
макроса errprint:
errprint(message, ...)
который просто печатает строку сообщения message и остальные аргументы в стандартный вывод ошибок.
Встроенный макрос errprint имеет
пустое (void) расширение.
errprint(`Illegal arguments to forloop ') error-->Illegal arguments to forloop =>
Сопровождающие символы новой строки
не печатаются автоматически, таким
образом, они должны обеспечиваться как
часть аргумента, как показано в примере
выше. (Клоны m4 для BSD выполняют
добавление сопровождающих символов
новой строки при каждом вызове макроса
errprint).
Для обеспечения возможности определения места возникновения ошибки существуют два полезных встроенных макроса:
__file__ __line__
которые соответственно расширяются в помещенное в кавычки имя текущего файла ввода и в номер строки, в этом файле.
errprint(`m4:'__file__:__line__: `Input error ') error-->m4:56.errprint:2: Input error =>
m4Если необходимо
выйти из m4, до того как будет
прочитан весь ввод, можно использовать
встроенный макрос m4exit:
m4exit(opt code)
который приводит к тому, что m4
завершает свою работу с кодом возврата
code. Если код возврата code не
указан, то код возврата m4 будет
нуль.
define(`fatal_error', `errprint(`m4: '__file__: __line__`: fatal error: $* ')m4exit(1)') => fatal_error(`This is a BAD one, buster') error-->m4: 57.m4exit: 5: fatal error: This is a BAD one, buster
После вызова этого макроса, m4
завершит свою работу с кодом возврата
1. Этот макрос предназначен только для
выхода по ошибке, поскольку нормальная
процедура выхода, в этом случае, не
выполняется, например, не выполняется
отмена перенаправления (undivert) для
перенаправленного (diverted) текста и не
выполняется перечитывание сохраненного
текста (См. секцию Сохранение
ввода).
Некоторые большие приложения m4
могут быть построены на основе общей
программной базы, которая содержит
сотни различных определений и множество
дополнительных ресурсоемких инициализаций.
Обычно, такая общая база хранится в
одном или нескольких файлах описаний,
которые перечисляются при каждом запуске
m4 перед файлами ввода пользователя,
или подключаются в файлах ввода
пользователя с помощью include.
Каждое повторное перечитывание общей
базы большого приложения может привести
к большим затратам времени. Для ускорения
запуска приложений использующих объемную
общую базу, GNU m4 предлагает
некоторые вспомогательные механизмы.
Предположим, что пользователь постоянно
повторяет использование:
m4 base.m4 input.m4
с несколько изменяющимся содержимым файла `input.m4', но неизменным содержимым в файле `base.m4'. Тогда, пользователю лучше один раз использовать:
m4 -F base.m4f base.m4
а во всех последующих запусках использовать:
m4 -R base.m4f input.m4
с различными изменениями ввода в
`input.m4'. Первый запуск m4,
содержащий опцию -F, осуществляет
только чтение и обработку файла `base.m4',
в котором выполняется определение
различных макросов приложения и прочие
вычисления, необходимые для какой-либо
общей начальной инициализации всего
приложения. После того как файл ввода
`base.m4' будет полностью обработан,
GNU m4 сгенерирует файл `base.m4f',
который называют "замороженным"
файлом, поскольку он содержит своеобразный
образ внутреннего состояния m4
после обработки файла ввода `base.m4'.
Последующие запуски m4, содержащие
опцию -R, способны загрузить в
память образ внутреннего состояния m4
после обработки файла ввода `base.m4'
из файла `base.m4f', и это осуществляется
перед тем как начать чтение любого
другого файла ввода. Такой подход
подразумевает, что вместо того чтобы
каждый раз начинать обработку большого
приложения с выполнения повторной
обработки одного и того же ввода m4,
ввод m4 будет читаться только
после восстановления состояния,
сохраненного в результате предыдущего
запуска. В нашем примере, после
восстановления состояния из файла
`base.m4f', эффект будет таким же самым
как и после повторной обработки файла
`base.m4'. Однако, восстановление
состояния из файла `base.m4f' выполняется
намного быстрее.
Только один "замороженный" файл
может быть создан или прочитан в
результате одного запуска m4.
Нельзя осуществить восстановление
внутреннего состояния m4 из двух
"замороженных" файлов одновременно.
Однако, "замороженные" файлы могут
быть инкрементно обновлены с помощью
совместного последовательного
использования опций -R и -F.
Например, команда запуска m4:
m4 file1.m4 file2.m4 file3.m4 file4.m4
может быть разделена на следующую
последовательность запусков m4,
накапливающую одинаковый вывод:
m4 -F file1.m4f file1.m4 m4 -R file1.m4f -F file2.m4f file2.m4 m4 -R file2.m4f -F file3.m4f file3.m4 m4 -R file3.m4f file4.m4
При этом необходима определенная
осторожность, поскольку не во всех
абсолютно случаях подобная попытка
будет выполняться одинаково корректно.
В частности, не обрабатываются атрибуты
трассировки макросов и не обрабатываются
текущие установки для changeword.
Также, взаимодействие некоторых опций
m4, которое использовалось при
одном запуске и не используемое для
следующего запуска, в настоящий момент
еще полностью не проанализировано. С
другой стороны, вы должны быть уверены
в том, что стек определений pushdef
обрабатывается корректно, а это
затрагивает undefine или переименование
встроенных макросов, а также изменение
вида строк для кавычек и комментариев.
Когда какой-либо запуск m4
"заморожен", то запрещена
автоматическая отмена перенаправления
(undiversion), которая осуществляется в конце
обработки. Вместо этого, все позитивно
нумерованные перенаправления (diversions)
сохраняются в "замороженном" файле.
Передается также номер активного
перенаправления (diversion).
Загружаемый "замороженный" файл
не обязан находиться в текущем каталоге.
Его поиск осуществляется также как и
поиск включаемых, с помощью include,
файлов (См. секцию Поиск
включаемых файлов).
"Замороженные" файлы могут быть
общими для целой архитектуры. Не будет
опасно записать "замороженный"
файл на одной машине и прочитать его на
другой машине, подразумевая, что вторая
машина использует ту же самую или более
новую версию GNU m4. Существуют
простые (редактируемые) текстовые файлы,
состоящие из директив, каждый из которых
начинается с прописной (большой) буквы
и заканчивается символом новой строки
(NL). В любом месте, где ожидается
появление директивы, символ #
вставляет строку комментария, пустые
строки - игнорируются. В следующем
описании, длина length всегда ссылается
на соответствующую строку string.
Все числа представлены как десятичные.
Директивами являются:
V number NL
C length1 , length2
NL string1
string2 NL
Q length1 , length2
NL string1
string2 NL
F length1 , length2
NL string1
string2 NL
pushdef,
определение для строки string1 как
расширяющееся в функцию, которая имеет
имя встроенного макроса как строку
string2.
T length1 , length2
NL string1
string2 NL
pushdef,
определение для строки string1 как
расширяющееся в текст, который указан
как строка string2.
D number, length
NL string
NL
m4.
m4Эта глава описывает различия
между этой реализацией m4 и
реализацией m4 в системе UNIX, в
частности, System V, Release 3.
Существуют также отличия от клонов
m4 для BSD. Здесь не делается попытка
обобщить эти отличия.
m4Эта версия m4 содержит
несколько свойств, которые отсутствуют
в версии m4 для System V. Все эти
дополнительные свойства могут быть
подавлены с помощью использования опции
командной строки `-G', если она не
будет отвергнута другими опциями
командной строки.
При использовании
$n нотации для макро-аргументов,
n может состоять из нескольки
цифр, в то время как версия m4 для
System V допускает использование только
одной цифры. Это позволяет макросам в
GNU m4 принимать любое число
аргументов, а не только девять (См.
секцию Аргументы
для макроса).
Поиск файлов,
включаемых с помощью использования
include и sinclude, осуществляется
в путях поиска, определяемых пользователем,
если они отсутствуют в текущем рабочем
каталоге. Пути поиска определяются с
помощью опции командной строки `-I'
и содержимым переменной окружения
`M4PATH' (См. секцию Поиск
включаемых файлов).
Аргументы макроса
undivert могут быть не числовыми, в
таком случае, именованный файл будет
включен в вывод без какой-либо
интерпретации (См. секцию Отмена
перенаправления (undiverting) вывода).
Поддержка
форматированного вывода осуществляется
с помощью встроенного макроса format,
работа которого спроектирована подобно
функции printf библиотеки языка C
(См. секцию Форматирование
вывода).
Поиск и подстановка
(замена) текста осуществляется с помощью
встроенных макросов regexp (См.
секцию Поиск
регулярного выражения) и patsubst
(См. секцию Подстановка
текста с помощью регулярного выражения).
Вывод команд командного
интерпретатора shell может быть прочитан
вm4 с помощью встроенного макроса
esyscmd (См. секцию Чтение
вывода команд).
С помощью использования
встроенного макроса builtin,
существует косвенный доступ к любому
встроенному макросу (См. секцию Косвенные
вызовы встроенных макросов).
Макросы могут быть
вызваны косвенно с помощью встроенного
макроса indir (См. секцию Косвенные
вызовы макросов).
Имя текущего файла
ввода и текущий номер строки ввода
может быть получен с помощью встроенных
макросов __file__ и __line__ (См.
секцию Печать
сообщений об ошибках).
С помощью встроенного
макроса dumpdef, можно управлять
форматом вывода, а с помощью встроенного
макроса debugmode можно управлять
отладочной трассировкой макросов (См.
секцию Управление
отладочным выводом).
С помощью встроенного макроса
debugfile, можно управлять назначением
вывода трассировки и отладки (См. секцию
Сохранение
отладочного вывода).
В дополнение к перечисленным выше
расширениям, GNU m4 реализовывает
следующие опции командной строки: `-F',
`-G', `-I', `-L', `-R',
`-V', `-W', `-d', `-l',
`-o' and `-t'. Действие этих
опций описывается в секции Запуск
m4.
Кроме того, стоит заметить, что
отладочные и трассировочные способности
GNU m4 более обширны, по сравнению
с другими версиями m4.
m4 для System V, отсутствующие
в GNU m4Версия m4 для System V содержит
некоторые особенности, которые еще не
реализованы в версии GNU m4.
Версия m4 для System V поддерживает
множество аргументов для встроенного
макроса defn. Это не реализовано
в версии GNU m4. Полезность такой
особенности для меня не очень очевидна.
Существует еще несколько несовместимостей
между этой реализацией m4 и версией
m4 для System V.
Версия GNU m4,
при перенаправлении (divertion) текста,
реализует синхронизацию строк отлично
от версии m4 для System V. GNU m4
выводит строки синхронизации в момент
перенаправления (divert) текста, в то время
как версия m4 для System V выводит
строки синхронизации в момент отмены
перенаправления (undivert) текста. Проблемой
является то, какие строки и имена файлов
будут прикреплены к тексту который был
или будет перенаправлен (diverted). Версия
m4 для System V полагает, что весь
перенаправленный (diverted) текст сгенерирован
из исходной строки содержащей вызов
undivert, в то время как версия GNU m4
полагает, что весь перенаправленный
(diverted) текст сгенерирован в момент
перенаправления (divert). Я ожидаю, что
опция строки синхронизации должна, в
основном, использоваться когда m4
используется как препроцессор какого-либо
компилятора. Если перенаправленная
(diverted) строка приводит к ошибке
компилятора, то сообщение об ошибке, в
основном, будет относиться к тому месту
где было выполнено перенаправление, а
не к месту где строка была вставлена.
Версия GNU m4 не делает попытки
запретить автоссылающиеся определения
вида:
define(`x', `x') define(`x', `x ')
По существу, нет ничего плохого в
определении того, что `x' будет
возвращать `x'. Плохой вещью
является то, что `x' будет
расширяться без кавычек. В m4,
кто-то может использовать макросы для
хранения строк, также как это делается
для переменных в других языках
программирования, для дальнейшей их
проверки с помощью:
ifelse(defn(`holder'), `value', ...)
В случаях подобных этому, запрещение
содержать собственное имя для макроса
будет бесполезным ограничением. Конечно,
это предоставляет для пользователей
GNU m4 веревку для "подвешивания"
самих себя! При более тщательном
программировании, можно избежать
пересканирование "зависаний",
несколько подобно тому как это делается
для бесконечных циклов при традиционном
программировании.
GNU m4
без опции `-G' будет определять,
что макро __gnu__ будет расширен в
пустую строку. В системах UNIX, GNU m4
без опции `-G' будет определять
макрос __unix__, в другом случае -
макрос unix. Оба макроса расширяются
в пустую строку.