Главная » Программирование для UNIX » Препроцессоры tbl и eqn

0

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

рительной обработки. На самом  деле troff является языком Ассемблера для наборной машины, а eqn и tbl компилируют в него.

Первой появилась eqn. Тогда yacc был впервые использован не для языка программирования.1 Потом  пришло время tbl, по сути своей она похожа на  eqn, а вот синтаксисы у них  не родственны друг  другу. Программа tbl не использует yacc, так  как ее грамматика настолько проста, что это не имеет  смысла.

Возможность создания каналов в UNIX подводит к мысли о разумности создания отдельных программ. Благодаря каналам происходит не только разбиение задач на  части  (что  необходимо в  любом  случае – размер самой  troff практически равен максимально допустимому для  программы на PDP-11), сокращается обмен данными между частями, а также между программистами. Последнее очень  важно, ведь это означает, что для «изготовления» препроцессора не надо иметь доступ  к исходным текстам. Кроме  того, применение каналов избавляет от огромных промежуточных файлов, если только компоненты не запускаются по отдельности специально, для отладки.

Когда   отдельные программы сообщаются  через  каналы,  возникают проблемы. Из-за  очень   большого количества входных и  выходных данных несколько ухудшается производительность: обе программы – и eqn и tbl  – обычно  вызывают увеличение объема данных от входа  к выходу в 8 раз. Еще важнее, что информация движется только в одном направлении. Например, не существует способа, которым eqn могла бы выяснить текущий  кегль,  что  приводит к некоторой  громоздкости языка. И наконец,  сообщения об ошибках: часто  бывает нелегко както связать диагностику, полученную в  troff, с ситуацией, возникшей в eqn или tbl и вызвавшей появление этого сообщения.

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

Таблицы

Начнем с краткого описания tbl,  поскольку  первое, что  будет  пред ставлено в данном разделе, – это таблица из документа о hoc. Программа tbl считывает файлы ввода  или стандартный ввод  и конвертирует текст между командами .TS (table start – начало таблицы) и .TE (table end – конец таблицы) в команды troff для вывода таблицы, выравнивая  столбцы и заботясь обо всех  типографских деталях. Строки .TS и

.TE также копируются, поэтому макропакет может дать  им соответствующие определения, например сохранять таблицу на одной странице и отключить опцию обтекания текста.

1        Маловероятно, что eqn появилась бы на свет, если бы в нужный момент под рукой не оказалось yacc.

Для того чтобы  создавать сложные таблицы, надо  обратиться к руко водству  по tbl, а пока приведем один  пример, которого будет  вполне достаточно, чтобы  пояснить основные свойства. Таблица взята из  документа по hoc:

.TS

center, box; c s

lfCW  l.

\fBTable  1:\fP Operators,  in  decreasing order  of  precedence

.sp  .5

^             exponentiation (\s–1FORTRAN\s0  **), right  associative

! \–          (unary) logical  and arithmetic negation

* /      multiplication,  division

+ \–          addition,  subtraction

> >=        relational  operators: greater,  greater or  equal,

< <=        less, less or  equal,

\&== !=   equal, not  equal  (all same precedence)

&&                     logical AND  (both  operands  always  evaluated)

||     logical OR  (both  operands  always  evaluated)

\&=          assignment,  right associative

.TE

так создается таблица, представленная ниже.

Table 1: Operators, in decreasing order of precedence

^             exponentiation (FORTRAN **), right associative

! –           (unary) logical and arithmetic negation

* /      multiplication, division

+ –           addition, subtraction

> >=        relational operators: greater, greater or equal,

< <=           less, less or equal,

== !=          equal, not  equal (all same precedence)

&&                     logical AND (both operands always evaluated)

||     logical OR (both operands always evaluated)

=              assignment, right associative

Слова   перед   точкой  с  запятой  (center,  box)  описывают  глобальные свойства таблицы: горизонтально центрировать ее на странице и нарисовать рамку вокруг нее.  Также возможны doublebox (двойная рамка), allbox (сетка, каждый элемент в рамке) и expand (растянуть по ширине страницы).

Следующие строки (до точки) описывают формат частей таблицы, которыми в данном  случае являются строка названия и тело  таблицы. Первая спецификация относится к первой строке таблицы, вторая –

ко второй, а последняя применяется ко всем оставшимся строкам. Для Table 1 есть только две строки спецификации, поэтому вторая из них  применяется ко  всем  строкам таблицы, кроме первой. Форматирование обозначается следующими символами: c  – для элементов, центрированных в столбце, r и l – для выравнивания по правому и левому краю соответственно и n – для числового выравнивания по десятичной точке. Символ s задает «стягивание» столбцов, в данном случае «c  s» означает, что надо центрировать название относительно всей таблицы, объединив второй и первый столбцы.

Шрифт можно указывать для  столбца; определение lfCW выводит столбец, выровненный по левому краю, шрифтом CW.

За информацией о формате следует собственно текст таблицы. Столбцы  разделены  символами  табуляции. Некоторые команды  troff, например .sp, воспринимаются и внутри таблиц. (Обратите внимание на использованную  дважды  последовательность \&: незащищенные  начальные знаки – и = в столбцах сообщают tbl, что в этом месте следует провести линии через  таблицу.)

Программа tbl  создает множество разных  таблиц, отличающихся от представленной в этом простом примере: она вставляет текст в рамки, вертикально выравнивает заголовки  столбцов и т. д.  Самый простой способ  использовать эту  программу для сложных  таблиц –  поискать похожий пример в томе 2А справочного руководства по UNIX  и адаптировать его к ситуации.

Математические выражения

Второй  препроцессор troff – это программа eqn, преобразующая язык,  описывающий математические выражения, в команды troff для выво да.  Она автоматически обрабатывает изменения шрифта и размера, а также  предоставляет названия стандартных  математических симво лов.  Входные данные eqn обычно  находятся между строками .EQ и .EN, аналогично строкам .TS и .TE программы tbl. Например,

.EQ

x sub  i

.EN

выводит xi. Если  используется макропакет ms, то выражение выводится как выключенная формула,1 при этом необязательный аргумент .EQ указывает номер выражения. Например, формула интеграла Коши

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

f(ζ)  =

1            -∫ f(z) dz

(9.1)

——–

i

C

———–

z – ζ

записывается как

.EQ  (9.1)

f( zeta   ) ~=~ 1  over  {2 pi  i}  int  from  C f(z) over  {z – zeta}  dz

.EN

Язык eqn основывается на записи озвучивания математических выражений. Отличие  произнесенного вслух математического  выражения от ввода  eqn заключается в том,  что фигурные скобки {} используются в eqn как круглые скобки – они  аннулируют правила, определяющие приоритетность по умолчанию, а вот обычные скобки не имеют  специального значения. Зато  у пробелов оно есть. Обратите внимание на то,

что в примере, приведенном выше, первая ζ (дзета) окружена пробела-

ми: дело в том,  что такие ключевые слова, как zeta и over распознают-

ся,  только если  они заключены в пробелы или фигурные скобки (при  этом ни пробелы, ни фигурные скобки в выводе не присутствуют). Если требуется, чтобы  в выводе был пробел, используйте символ тильды

~, как в выражении ~=~. Фигурные скобки можно получить при  помощи "{" и "}".

Существует несколько классов ключевых слов  eqn.  Названия гречес ких букв  записываются  в  нижнем или  верхнем регистре, например

lambda и LAMBDA (λ и Λ). Другим математическим символам даны  назва-

ния, такие как sum (сумма), int (интеграл), infinity (бесконечность) и

grad (градиент): Σ, ∫, ∞,∇. Существуют позиционные операторы, такие

как sub, sup, from, to и over:

i = 0

1

2       ——

записывается как

sum from i=0  to  infinity  x sub  i sup  2 ~–>~ 1 over  {2  pi}

Есть такие операторы, как sqrt и изменяемые по высоте скобки и т. д. Также eqn умеет  создавать столбцы и матрицы объектов. Существуют и  команды для  управления размерами,  шрифтами и  позициями, на тот случай, если установленные по умолчанию не подходят.

Небольшие математические выражения, такие как log10(x), часто  помещают прямо в текст, а не в выключенную формулу. В eqn есть ключевое  слово  delim, которое задает пару  символов, заключающих выра-

жение, находящееся внутри строки, в скобки. Обычно  для  правого и

левого  ограничителей используется один и тот же символ, в большинстве случаев это знак доллара $. Но в hoc символ $ применяется для  ар-

гументов, поэтому для  примеров этой  книги в качестве ограничителя был выбран символ @. Подходит и %, но избегайте других символов: дело в том,  что очень  многие из  них  имеют специальное значение в огромном количестве программ, и при их использовании поведение программы может стать  абсолютно необъяснимым. (Авторы столкнулись с этим, когда писали данную главу.)

Итак, после того как задано

.EQ

delim  @@

.EN

находящееся внутри строки выражение, такое как ∑∞   , может быть

выведено следующим образом:

i = 0

in–line expression

такое  как @sum  from  i=0  to  infinity x sub  i@  может  быть выведено:

Этот  прием используют для  выведения  математических выражений внутри таблицы, так, например, сделано в документации по hoc:

.TS

center, box; c s  s

lfCW  n l.

\fBTable  3:\fP   Built–in  Constants

.sp  .5

DEG

57.29577951308232087680

180/  pi@,  degrees  per  radian

E

2.71828182845904523536

@e@,  base  of  natural  logarithms

GAMMA

0.57721566490153286060

@gamma@,  Euler–Mascheroni  constant

PHI

1.61803398874989484820

@(  sqrt  5  +1)/2@,  the  golden  ratio

PI

3.14159265358979323846

@pi@,  circular  transcendental  number

.TE

Внешний вид выходных данных представлен ниже. Видно, как tbl выравнивает числовой столбец по десятичной точке.

Table 3: Built-in Constants

DEG

57.29577951308232087680

180/π, degrees per radian

E

2.71828182845904523536

e, base of natural logarithms

GAMMA

0.57721566490153286060

γ, Euler-Mascheroni constant

PHI

1.61803398874989484820

(   5 +1)/2, the golden ratio

PI

3.14159265358979323846

π, circular transcendental number

И последнее. Поскольку eqn выводит курсивом любую буквенную строку,  которую он  не  распознает, ее  принято использовать для  печати курсивом обычных слов.  @Слово@, например, выводится как Слово. Но

будьте  внимательны: некоторые  общеупотребительные слова  (такие как from и to) eqn распознает и обрабатывает их специальным образом, к тому же она уничтожает пробелы, так что этот прием следует применять с осторожностью.

Получение выходных данных

Подготовив документ, надо  организовать все  препроцессоры и  саму  программу troff так, чтобы  получить выходные данные. Если  применялась только troff, введите

$ troff -ms имена5файлов                      (Или  -mm)

Если  же  использовался препроцессор, укажите имена файлов как аргументы для первой команды в конвейере, а остальные команды должны получать данные со своего стандартного ввода, как в

$ eqn имена5файлов | troff -ms

или

$ tbl  имена5файлов | eqn | troff -ms

Неудобно все время отслеживать, какой из препроцессоров необходим для вывода каждого конкретного документа. Авторы решили, что будет полезно написать программу (ее назвали doctype), которая трассировала бы соответствующую последовательность команд:

$ doctype  ch9.*

cat  ch9.1  ch9.2  ch9.3  ch9.4  | pic  | tbl  | eqn | troff  –ms

$ doctype  hoc.ms

cat  hoc.ms | tbl | eqn | troff –ms

$

Программа doctype  реализована средствами,  подробно описанными  в главе 4; в частности программа awk ищет последовательности команд препроцессоров и выводит командную строку для вызова тех  из них, которые нужны для  форматирования документа. Она также ищет команду .PP (абзац) макропакета ms.

$ cat  doctype

# doctype: синтезирует  командную  строку для  troff echo –n "cat  $* |  "

egrep  –h  ‘^\.(EQ|TS|\[|PS|IS|PP)’  $* | sort –u  |

awk ‘

/^\.PP/  { ms++  }

/^\.EQ/  { eqn++ }

/^\.TS/  { tbl++  }

/^\.PS/  { pic++ }

/^\.IS/ { ideal++  }

/^\.\[/  {  refer++  } END  {

if (refer > 0)  printf "refer | "

if (pic > 0)      printf "pic  | "

if  (ideal  >  0)  printf  "ideal  |  " if  (tbl >  0)      printf  "tbl |  "

if  (eqn  >  0)      printf  "eqn  |  " printf  "troff  "

if  (ms  >  0)  printf  "–ms" printf  "\n"

} ‘

$

(Параметр –h команды egrep вызывает уничтожение заголовков с именами файлов на каждой строке.) К сожалению, этот параметр доступен не во всех версиях системы. Входные данные сканируются, собирается информация о том, какие компоненты задействованы. После того как весь  ввод  просмотрен, он в надлежащем порядке обрабатывается для подготовки к выводу. Подробности обработки  определяются препроцессорами,  форматирующими troff-документы, пользователю же  остается только переложить работу  на машину, она сама  обо всем  позаботится.

Программа doctype  – это еще один пример (уже была  рассмотрена программа bundle) программы, создающей другую программу. Одно заме чание: пользователю приходится заново вводить строку для  оболочки; в одном из упражнений будет предложено исправить эту ситуацию.

Когда  дело доходит до выполнения настоящей команды troff, помните, что поведение troff определяется конкретной системой: в некоторых случаях она непосредственно управляет наборной машиной, тогда  как в других системах программа выводит информацию на устройство стандартного вывода, а затем другая специальная программа посылает данные на наборную машину.

Между прочим, первая версия представленной программы не использовала ни egrep, ни sort; awk сама  просматривала все входные данные. Но с большими документами такая программа работала очень  медленно, поэтому для быстроты поиска была  добавлена egrep, а для избавления от повторений (дубликатов) – sort  –u. В случае типового документа издержки на создание двух  дополнительных процессов для «просеивания» данных меньше, чем на выполнение awk для большого объема входных данных. Чтобы проиллюстрировать это утверждение, прове дено  сравнение между  doctype  и версией, которая использует только awk;  в качестве документа для  обработки было  выбрано содержимое этой главы (оригинал), содержащей порядка 52 000 символов:

$ time  awk ‘… doctype  without egrep  …’ ch9.*

cat  ch9.1  ch9.2  ch9.3  ch9.4  |  pic  | tbl  | eqn | troff –ms real             31.0

user               8.9 sys                 2.8

$ time  doctype  ch9.*

cat  ch9.1  ch9.2  ch9.3  ch9.4  | pic  | tbl  | eqn | troff  –ms

real

7.0

user

1.0

sys

$

2.3

Преимущество версии с тремя процессами очевидно. (Сравнение было проведено на машине, где работал всего один  пользователь, при  большой  загруженности системы  преимущество версии, содержащей eg– rep,  было бы  еще  нагляднее.) Обратите внимание,  что  сначала была  создана простая работающая версия, а потом  уже  она  оптимизировалась.

Упражнение 9.2. Как форматировалась эта глава? ~

Упражнение 9.3.  Если  в качестве символа ограничителя в eqn используется знак доллара, как получить этот символ в выводе? Подсказка: исследуйте кавычки  и предопределенные  (зарезервированные) слова  eqn. ~

Упражнение 9.4. Почему

$ `doctype имена5файлов`

не работает? Измените doctype  так, чтобы полученная команда не выводилась, а выполнялась. ~

Упражнение 9.5.  Значительны ли  издержки на  дополнительную команду cat в doctype? Перепишите doctype так, чтобы  исключить дополнительный процесс. Какая из версий проще? ~

Упражнение 9.6.  Что  лучше: использовать  doctype  или  записать командный файл, содержащий команды для  форматирования конкретного документа? ~

Упражнение 9.7. Поэкспериментируйте с различными комбинациями grep, egrep, fgrep, sed, awk и sort и создайте максимально быструю версию doctype. ~

Источник: Керниган Б., Пайк Р., UNIX. Программное окружение. – Пер. с англ. – СПб: Символ-Плюс, 2003. – 416 с., ил.

По теме:

  • Комментарии