B.5 Генерация отчётов — report.awk

Предыдущая  Содержание  Следующая V*D*V

report.awk содержит функции для загрузки отчёты из файлов и генерации выходных данных из отчётов. Это единственный модуль, который выполняет распечатку в поток стандартного вывода; поэтому отслеживание номеров строк происходит в этом модуле. Доступна простая функция puts(), чтобы распечатать строку с символом перевода строки.

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

Отчёты генерируются вызовом gen() с указанием имени отчёта. Приложены определённое усилия, чтобы избежать эмиссии начальных пробелов или более, чем одной пустой строки подряд: глобальная переменная newLine равна 0 в крайнем левом положении или 1, как только что-то было распечатано; внутренняя функция lf() распечатывает перевод строки и уменьшает newLine на 1. Пробелы добавляются только если newLine равна 1, то есть если мы внутри строки. Переводы строк добавляются только если newLine не -1, то есть если только что не был сделан перевод строки.

Генерация отчёта основывается на нескольких простых наблюдениях: очень просто прочитать строки отчёта, использовать split(), чтобы разделить их на слова используя одиночные пробелы или знаки табуляции, и сохранить их в большом массиве Token[]. Другой массив Report[] содержит для каждого имени отчёта индекс его первого слова в Token[]. Внутренняя функция endReport() проверяет, что количество фигурных скобок в каждом отчёте сбалансировано, и завершает каждый отчёт дополнительной закрывающей фигурной скобкой.

Если одиночный пробел или символ табуляции используется в качестве символа разделения, и если мы генерируем единственный пробел для пустого слова, отчёт сильно похож на сгенерированный вывод: два пробела представляют один пробел на выходе. Генерация будет достаточно эффективна, если мы сможем быстро идентифицировать слова, которые будут оставлены без изменений (они не начинаются с одинарного обратного апострофа), и если у нас есть быстрый способ искать замены (они хранятся в массиве Replace[], проиндексированного по словам, которые будут заменяться). Элементы Replace[] главным образом установлены функциями, определёнными в parse.awk, которые просматривают базу данных: setClass(), setMethod() и setDeclarator() устанавливают информацию, описанную в таблице в конце страницы руководства ooc в разделе C.1.

Группы реализовать просто. При чтении строк отчёта после каждого слова, начинающегося с `{, то есть в начале каждой группы, мы сохраняем индекс слова после соответствующей `}, то есть индекс после окончания группы. Это требует поддержания стека открытых фигурных скобок, но этот стек может быть сохранён в тех же местах, где мы позже сохраним индексы соответствующих закрывающих фигурных скобок.

Генератор отчётов работает рекурсивно. Содержание группы интерпретируется вызовом genGroup(), который возвращается при достижении закрывающей фигурной скобки. В цикле можно сделать так много вызовов, как необходимо, и в конечном счёте мы продолжаем выполнение до следующего индекса позиции после группы. На глобальном уровне каждый отчёт завершается одним дополнительным маркером закрывающей фигурной скобки. Группы `{if так же просты:

 

OOC_Report_Generation_-_report_1

 

Если сравнение удаётся, мы рекурсивно выполняем группу. В любом случае выполнение продолжается после группы. Для `{else у нас есть следующее расположение:

 

OOC_Report_Generation_-_report_2

 

Если сравнение удаётся, рекурсивно выполняем его группу. Впоследствии мы можем следовать за обоими индексными значениями, или можем принять меры к тому, чтобы группа `{else всегда пропускалась, когда с нею встречаются непосредственно. Если сравнение не удаётся, и если индекс после `{if указывает на `{else, мы переходим к `{else и рекурсивно выполняем эту группу. Впоследствии мы проследуем за индексом, как обычно.

Маркер завершения `} может содержать произвольный текст после фигурной скобки. Однако есть два особых случая. Цикл по параметрам метода вызывает genGroup() с дополнительным параметром more, установленным в 1, пока всё ещё есть необработанные параметры метода. Если more установлен, genGroup() выдаёт запятую и пробел для завершающего маркера `},. Это упрощает генерацию списков параметров.

Другим специальным маркером завершения является `}n, который приводит к дополнительному переводу строки, если было выведено что-нибудь для группы. genGroup() возвращает значение истинности, указывающее, был ли он завершен маркером `}n, или нет. Такие функции, как genLoopMethods(), которые управляют циклом вызовов genGroup(), возвращают значение genGroup(), если был вход в цикл и ложь в противном случае. Наконец, genGroup() выдаст обязательный дополнительный перевод строки, если функция цикла возвратит истину, то есть если был вход в цикл и он завершился `}n. Это упрощает размещение блоков в сгенерированном коде.

Модуль отладки report.dbg принимает имя файла, например c.rep, и загружает отчёты из этого файла. Если  имя отчёта будет допустимым, он выведет на экран отчёт посимвольно. Если all или reports, покажет все отчёты.

 

Предыдущая  Содержание  Следующая