7.4 Стратегия реализации

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

Мы знаем, что должен делать ooc. Как написать препроцессор? По соображениям эффективности нам, возможно, в конце концов, придётся прибегнуть к платформе, подобной lex и yacc, но первая реализация будет намного дешевле, если мы используем строковый язык программирования, подобный awk или perl. Если это заработает, мы будем знать необходимые алгоритмы и структуры данных, и должно быть легко переписать их в более эффективной реализации; для выполнения работы можно было бы даже использовать что-то вроде транслятора из awk в C. С дешёвой первой реализацией мы, однако, прежде всего сможем убедиться, что наша идея осуществима и в первую очередь удобна.

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

Первая реализация ooc действительно работала таким образом, но оказалась трудной для изменения. Существует гораздо лучший способ: простой язык отчётов с заменой текста, циклами и условиями, который используется, чтобы выразить шаблон, и который интерпретируется ooc для создания вывода.

Более подробно данная реализация поясняется  в приложении B. Язык отчёта определяется как часть руководства ooc в приложении C. Язык отчёта содержит около двадцати пяти замен, десять циклов, два условных оператора и способ вызова отчёта в виде части другого отчёта. В качестве примера показано, как могли бы быть сгенерированы селекторы:

 

`{if `newmeta 1

  `{%-

    `result `method ( `{() `const `type `_ `name `}, ) { `n

`t      const struct `meta * class = classOf(self); `n

       `%casts

`t     assert(class -> `method ); `n

`t     `{ifnot `result void return `} \

           class -> `method ( `{() `name `}, ); `n

    } `n

  `}

`}

 

Внутри отчётов ooc считает интересными все те слова, которые начинаются с обратного апострофа; группы начинаются с `{ и заканчиваться `} и являются либо циклами или условными операторами; вызов отчёта начинается с `%; все другие слова, начинающиеся с обратного апострофа, заменяются информацией из базы данных.

`{if использует следующие два слова и выполняет остальную часть группы, если слова равны. `newmeta будет заменяться на 1, если ooc работает с описанием класса, который определяет новый метакласс. Поэтому селекторы генерируются только для нового метакласса.

`{%- это цикл по динамически компонуемым методам в описании класса. `method заменяется текущим именем метода; `result — текущий тип результата.

`{() представляет собой цикл по параметрам текущего метода. Назначение `const, `type и `name должно быть достаточно очевидно: это части описания текущего параметра. `_ подчеркивание, если текущий параметр является объектом текущего класса. `} это маленькая хитрость: создаётся запятая, если есть ещё один параметр, и завершается цикл, как и в случае с любым другим маркером, начинающимся с `}.

`%casts вызывает другой отчёт, casts, который отвечает за импорт параметров объектов. На данный момент этот отчёт выглядит примерно следующим образом:

 

% casts            // оператор %casts

`{()               // импорт

  `{if `_ _

`t  `const struct `cast * `name = _ `name ; `n

  `}

`}n

`{if `linkage %    // если компоновка статическая, необходимо проверить

  `%checks

`}

 

Строка, начинающаяся с %, предшествует отчётам в файле отчёта и привносит имя отчёта. Остальная часть отчёта casts должна быть понятна: `cast относится к имени класса объекта параметра, а `linkage тип компоновки текущего метода, то есть один из символов разделов в описании класса. Мы создаём локальную переменную чтобы разыменовать параметр, если он является объектом в текущем классе. `}n ещё одна хитрость: создаётся символ новой строки, если для группы было что-либо сгенерировано.

%casts также несёт ответственность за проверку всех объектов, поставляемых в качестве параметров в метод со статической компоновкой. Так как селекторы имеют подобную проблему, мы используем самостоятельные проверки отчётов, которые могут быть вызваны из отчёта для генерации селекторов:

 

% checks        // проверяем все объектные параметры

`{()

  `{ifnot `cast `

`t assert( `name ); `n

  `}fi

`}n

 

До следующей главы мы можем защититься от нулевых указателей по крайней мере вызовом assert(). Для объектов необходим этот тест: ` заменяется пустотой, то есть мы генерируем assert(), если видим объект параметра из произвольного класса.

Два слова ещё не были объяснены: `t генерирует табуляцию, а `n генерирует символ новой строки. Мы хотим генерировать удобочитаемые программы C; Таким образом, надо внимательно следить, сколько генерируется пробелов. Чтобы можно было идентифицировать отчёты, основанные на их собственных структурах управления в группах, ooc не будет генерировать начальные пробелы и подряд будет генерироваться только одна пустая строка. `t следует использованы для получения отступа, а для того, чтобы разбить вывод на строки, следует использовать `n. (* Эксперименты со средствами форматирования исходного кода cb и indent не дали приемлемых результатов. Слова 't и 'n доставляют лишь незначительное неудобство и мониторинг генерации начальных пробелов и последовательных переводов строк не сильно усложняют генератор отчетов.)

 

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