7.4 Стратегия реализации |
Предыдущая Содержание Следующая |
Мы знаем, что должен делать 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 доставляют лишь незначительное неудобство и мониторинг генерации начальных пробелов и последовательных переводов строк не сильно усложняют генератор отчетов.)
|
Предыдущая Содержание Следующая |