Сущность библиотеки анализа кодаVivaCoreАвторы: Андрей Карпов, Евгений РыжковДата: 09.01.2008АннотацияСтатья знакомит разр...
Отличие библиотеки VivaCore от библиотеки OpenC++Основное отличие библиотеки VivaCore от OpenC++ заключается в том, что он...
В ближайшем будущем в библиотеке VivaCore планируется реализовать:собственный препроцессор (на основе The Wave C++ preproc...
16. форматирование кода (Ochre SourceStyler).Более подробно о применении технологии разбора кода можно узнать из фундамент...
Метапрограммирование - создание программ, которые создают другие программы как результатсвоей работы, либо изменяющие или ...
Рисунок 1 - Общая функциональная структура библиотеки VivaCore.Мы предлагаем рассмотреть функциональные блоки библиотеки в...
Рисунок 2 - Последовательность обработки кода.1) Подсистема ввода данных (Input subsystem)Библиотека VivaCore может коррек...
Лексический анализатор VivaCore разбирает текст программы на набор объектов типа Token (см.файл Token.h), которые содержат...
Си++. Если пользователю будет крайне необходимо работать именно с абст                                                    ...
Чтобы получить общее представление о работе грамматического анализатора, в качестве примераприведем дерево разбора, которо...
Рисунок 4.2. Представление заголовка функции.
Рисунок 4.3. Представление тела функции.
Рисунок 4.4. Представление тела функции.Следует упомянуть еще один важный компонент работы анализатора. Это получение инфо...
модифицировать ее. Например, используя такие функции как IsFunction, IsPointerType,IsBuiltInType можно легко идентифициров...
]    ]    Leaf:)][{    NonLeaf:[        PtreeIfStatement:[            LeafReserved:if            Leaf:(            PtreeIn...
]                 Leaf:;             ]         ]         PtreeReturnStatement:[             LeafReserved:return           ...
5) Обход дерева разбораДля разработчиков статических анализаторов кода (подробное введение в задачу - книга [6]) илисистем...
Начинается обработка узла, представляющего собой унарную операцию над некоторымвыражением и имеющего тип PtreeUnaryExpr. П...
Пример использования класса Environment, представленного объектом env для получения типаобъекта declTypeInfo:TypeInfo decl...
немаловажно, выбор XML в качестве формата хранения данных позволяет легче структурироватьинформацию и представлять ее в пр...
Сущность библиотеки анализа кода VivaCore
Сущность библиотеки анализа кода VivaCore
Сущность библиотеки анализа кода VivaCore
Сущность библиотеки анализа кода VivaCore
Сущность библиотеки анализа кода VivaCore
Сущность библиотеки анализа кода VivaCore
Upcoming SlideShare
Loading in …5
×

Сущность библиотеки анализа кода VivaCore

788 views
751 views

Published on

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

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
788
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
2
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Сущность библиотеки анализа кода VivaCore

  1. 1. Сущность библиотеки анализа кодаVivaCoreАвторы: Андрей Карпов, Евгений РыжковДата: 09.01.2008АннотацияСтатья знакомит разработчиков с библиотекой VivaCore, предпосылками ее создания,возможностями, структурой и областями применения. Данная статья была написана параллельнос разработкой библиотеки VivaCore, и поэтому отдельные детали ее конечной реализации могутотличаться от описанных здесь свойств. Но это не помешает разработчикам познакомиться собщими принципами работы библиотеки, механизмами анализа и обработки текстов программна языке Си и Си++.ВведениеVivaCore - это открытая библиотека для работы с Си и Си++ кодом. Библиотека предназначена дляреализации на ее основе систем рефакторинга кода, систем статического и динамическогоанализа, систем трансформации или оптимизации кода, расширений языка, подсистем подсветкисинтаксиса, систем построения документации по коду и других аналогичных инструментов.Идея разработки библиотеки возникла в процессе создания нашей командой статическогоанализатора кода Viva64 [1]. Инструмент Viva64 предназначен для диагностирования ошибок впрограммах на Си/Си++, связанных с особенностями переноса кода под 64-битные Windows-системы.В процессе разработки Viva64 наша команда столкнулась с отсутствием открытых библиотек,удобных для реализации подобных проектов. В качестве основы была выбрана библиотекаOpenC++ [2], и в целом мы остались довольны своим выбором. Но в ходе разработки статическогоанализатора наша команда внесла довольно большое количество исправлений иусовершенствований в библиотеку OpenC++. И теперь, когда разработка первых версий продуктаViva64 закончена, мы хотим предложить сторонним разработчикам наш переработанный вариантбиблиотеки OpenC++, которую мы назвали VivaCore. Мы считаем, что внесенные нами изменениямогут существенно облегчить жизнь разработчикам, собирающимся приступить к разработкепродуктов в области анализа или обработки Си/Си++ кода.Лицензия на библиотеку VivaCore позволяет свободно использовать, копировать, распространятьи модифицировать ее в бинарном виде или в виде исходного кода, как для коммерческого, так идля некоммерческого использования без каких-либо отчислений авторам библиотеки.Необходимо лишь указать авторов исходных библиотек (OpenC++ и VivaCore).Вы можете скачать библиотеку VivaCore по адресу - http://www.viva64.com/vivacore-download.php.
  2. 2. Отличие библиотеки VivaCore от библиотеки OpenC++Основное отличие библиотеки VivaCore от OpenC++ заключается в том, что она является живымпроектом и продолжает активно наращивать функциональность. Библиотека OpenC++, ксожалению, давно не развивается. Самое последнее изменение библиотеки датируется 2004годом. А последнее изменение, связанное с поддержкой новых ключевых слов, датируется 2003годом. Этим исправлением является неудачная попытка добавить тип данных wchar_t, чтоповлекло за собой внесение около пяти ошибок различного рода.Очевиден вопрос, почему мы предлагаем свою библиотеку, а не внесли свои изменения вOpenC++? У нас нет для этого достаточного количества свободных ресурсов. Было осуществленоогромное количество модификаций, и внести все имеющиеся изменения в OpenC++представляется сложной задачей. Многие из изменений носят специфический характер и могут невписываться в общую идеологию библиотеки OpenC++ и, значит, потребуют дополнительнойадаптации. Все это делает задачу обновления библиотеки OpenC++ весьма ресурсоемкой задачей,и с нашей точки зрения не очень целесообразной.Перечислим новые ключевые функциональные возможности, реализованные в библиотекеVivaCore, по сравнению с OpenC++: 1. Поддержан классический язык Си. Используется другой набор лексем, что дает возможность именовать переменные именем "class" или объявить функцию в следующем классическом Си стиле: PureC_Foo(ptr) char *ptr; { ... } 2. Проделана большая работа по поддержке специфики синтаксиса языка Си++, используемого при разработке в среде VisualStudio 2005/2008. Например, библиотека обрабатывает ключевые слова __noop, __if_exists, __ptr32, __pragma, __interface и так далее. 3. Поддержаны некоторые новые ключевые слова и иные конструкции, имеющиеся в новых стандартах языка. В частности, добавлено ключевое слово register и поддержан вызов шаблонных функций с использованием слова template: object.template foo<int>();. 4. Реализовано вычисление значений литеральных констант. 5. Библиотека адаптирована и оптимизирована для работы на 64-битных системах с помощью анализатора кода Viva64. 6. Исправлено большое количество ошибок и недочетов. Примерами может служить поддержка строковых литералов, разделенных пробелом (const wchar_t *str = L"begin"L"end") или разделенных на две строки косой чертой: const char *name = "Viva Core"; Другой пример - это корректная обработка выражений вида "bool r = a < 1 || b > (int) 2;", которые OpenC++ путает с шаблонами. И так далее. 7. Создан механизм начальной предобработки исходного текста, позволяющий реализовать некоторые специфические модификации кода.
  3. 3. В ближайшем будущем в библиотеке VivaCore планируется реализовать:собственный препроцессор (на основе The Wave C++ preprocessor library) вместо стороннего(например, препроцессора Visual Studio). Это позволит избежать ряда ошибок позиционированияпо номерам строк в коде, при выводе ошибок компиляции, а также обеспечить больший контрольнад процессом разбора и анализа кода;поддержать кодирование сложных типов, занимающих в кодированном виде более 127символов;простое приложение, демонстрирующие основные принципы использования библиотекиVivaCore.Необходимо отметить, что, не смотря на все указанные отличия библиотек OpenC++ и VivaCore, вних много общего, и поэтому документация по OpenC++ не утрачивает своей актуальности. Нашакоманда постарается уделить внимание документированию библиотеки VivaCore. Но посколькуэта документация будет в первую очередь затрагивать отличия и новые возможности,реализованные в библиотеке VivaCore, то знакомство с документацией по библиотеке OpenC++будет полезно в любом случае.Области применения библиотеки VivaCoreБиблиотека VivaCore может быть интересна компаниям и организациям, которые создают илипланируют создавать инструменты для работы с кодом. Перечислить все допустимые области иметоды применения, естественно, не возможно, но мы все-таки назовем ряд направлений, чтобыпоказать VivaCore под разными углами зрения. В скобках указаны продукты, относящиеся кданному классу решений. Не стоит считать, что они реализованы на основе VivaCore - это всеголишь примеры решений. Итак, с помощью VivaCore возможно разработать: 1. рефакторинг кода (VisualAssist, DevExpress Refactoring, JetBrains Resharper); 2. статические анализаторы общего и специализированного назначения (Viva64, lint, Gimpel Software PC-Lint, Microsoft FxCop, Parasoft C++test); 3. динамические анализаторы кода (Compuware BoundsChecker, AutomatedQA AQTime); 4. расширения языков Си/Си++, в том числе для поддержки метапрограммирования (OpenTS); 5. автоматизированное тестирование кода (Parasoft C++test) 6. трансформации кода, например, для оптимизации; 7. подсветку синтаксиса (Whole Tomato Software VisualAssist, любая современная среда разработки); 8. системы построения документации по коду (Synopsis, Doxygen); 9. высокоточное определение изменений в исходном коде или анализа эволюции изменений; 10. поиск дублирующегося кода на уровне грамматических конструкций языка; 11. подсчет метрик (C and C++ Code Counter - CCCC); 12. поддержку стандартов кодирования (Gimpel Software PC-Lint); 13. инструменты, облегчающие миграцию кода на другие программные и аппаратные платформы (Viva64); 14. автоматическую генерацию кода; 15. визуализаторы кода, системы построения диаграмм зависимостей (Source-Navigator);
  4. 4. 16. форматирование кода (Ochre SourceStyler).Более подробно о применении технологии разбора кода можно узнать из фундаментальной книгипо компиляторам [3]. Также рекомендуем ознакомиться с принципами анализа программ [4].Не следует путать VivaCore с профессиональными многофункциональными парсерами Си/Си++кода. Если пользователю нужен front-end парсер кода, полностью поддерживающийсовременный стандарт языка Си++ и позволяющий создавать свой компилятор подспецифическую платформу, то ему стоит обратить свое внимание на GCC или дорогиекоммерческие решения. Например, такие решения предоставляет Semantic Designs [5].Но если компания разрабатывает инструмент, требующий классического анализа Си/Си++ кода, торациональным решением будет являться использование удобной и открытой библиотеки кода,которой и является VivaCore.Основные терминыПрежде чем перейти к более подробному рассмотрению библиотеки VivaCore напомнимнекоторые термины, которые будут использоваться в процессе описания.Препроцессирование - механизм, просматривающий входной ".c/.cpp" файл, исполняющий в нёмдирективы препроцессора, включающий в него содержимое других файлов, указанных вдирективах #include и прочее. В результате получается файл, который не содержит директивпрепроцессора, все используемые макросы раскрыты, вместо директив #include подставленосодержимое соответствующих файлов. Файл с результатом препроцессирования обычно имеетсуффикс ".i". Результат препроцессирования называется единицей трансляции.Синтаксический анализ (парсинг) - это процесс анализа входной последовательности символов, сцелью разбора грамматической структуры. Обычно синтаксический анализ делится на два уровня:лексический анализ и грамматический анализ.Лексический анализ - процесс обработки входной последовательности символов с цельюполучения на выходе последовательности символов, называемых лексемами (или "токенами").Каждую лексему условно можно представить в виде структуры, содержащей тип лексемы и, еслинужно, соответствующее значение.Грамматический анализ (грамматический разбор) - это процесс сопоставления линейнойпоследовательности лексем (слов, лексем) языка с его формальной грамматикой. Результатомобычно является дерево разбора или абстрактное синтаксическое дерево.Абстрактное синтаксическое дерево (Abstract Syntax Tree - AST) — конечное, помеченное,ориентированное дерево, в котором внутренние вершины сопоставлены с операторами языкапрограммирования, а листья с соответствующими операндами. Таким образом, листья являютсяпустыми операторами и представляют только переменные и константы. Абстрактноесинтаксическое дерево отличается от дерева разбора (derivation tree - DT, parse tree - PT) тем, что внём отсутствуют узлы для тех синтаксических правил, которые не влияют на семантикупрограммы. Классическим примером такого отсутствия являются группирующие скобки, так как вAST группировка операндов явно задаётся структурой дерева.
  5. 5. Метапрограммирование - создание программ, которые создают другие программы как результатсвоей работы, либо изменяющие или дополняющие себя во время выполнения. Подметапрограммированием в рамках библиотеки VivaCore следует понимать возможностьрасширения синтаксиса и функциональности языка Си/Си++ с целью создания собственного языкапрограммирования. Созданные метапрограммы на этом языке программирования затем могутбыть транслированы с использованием VivaCore в код на языке Си/Си++ и скомпилированывнешним компилятором.Обход синтаксического дерева - обход по всем вершинам и листьям синтаксического дерева сцелью сбора информации различного рода, анализа или модификации.Общая структура библиотеки VivaCoreОбщая функциональная структура библиотеки VivaCore показана на рисунке 1. На данный моментбиблиотека рассчитана на плотную интеграцию с пользовательским приложением и представленав виде набора исходных текстов.
  6. 6. Рисунок 1 - Общая функциональная структура библиотеки VivaCore.Мы предлагаем рассмотреть функциональные блоки библиотеки в том порядке, в котором ониобрабатывают поступивший на вход исходный текст программы, как показано на рисунке 2. Мырассмотрим, какие действия выполняет функциональный блок, какую информацию он позволяетполучить и как его можно модифицировать для специфических целей.
  7. 7. Рисунок 2 - Последовательность обработки кода.1) Подсистема ввода данных (Input subsystem)Библиотека VivaCore может корректно использовать только исходный Си/Си++ код, обработанныйпрепроцессором. В дальнейшем рассматривается возможность использовать препроцессор из TheWave C++ preprocessor library, но в первой версии библиотеки это реализовано не будет. Дляполучения препроцессированного файла можно воспользоваться компилятором (например,Microsoft Visual C++) и получить обработанный файл, который обычно имеет расширение "i".В определенных случаях можно подать на вход необработанные Си/Си++ файлы, но в этом случаеработать с VivaCore следует не дальше уровня разбиения файла на лексемы. Этого вполне можетхватить для подсчета метрик или иных целей. Но пытаться строить и анализировать дереворазбора (PT) не стоит, поскольку результат, скорее всего, будет малопригоден для обработки.Имея препроцессированный код, пользователь может передать его подсистеме ввода данных ввиде файла или буфера в памяти. Назначение подсистемы ввода заключается в том, чтобырасположить переданные данные во внутренних структурах библиотеки VivaCore. Такжеподсистема ввода принимает конфигурационные данные, которые сообщают, что считатьсистемными, а что пользовательскими библиотеками.См. в коде: VivaConfiguration, ReadFile.2) Подсистема предварительной обработки кода (Preprocessorsubsystem)Хочется подчеркнуть, что данная подсистема не выполняет препроцессирование кода в егоклассическом понимании. Как было сказано ранее, препроцессированный код уже должен бытьподан на вход библиотеки VivaCore. Рассматриваемая подсистема служит для следующих задач: • Разбиение текста программы на строки и разбиение их на две логических группы. К первой группе относится системный код (код библиотек компилятора и так далее). Ко второй пользовательский код, который представляет интерес для анализа. В результате, разрабатывая статический анализатор, пользователь получает возможность решать, будет ли он анализировать код системных библиотек или нет. • Специализированная модификация текста программы в памяти. Примером может служить удаление из кода конструкций конкретной среды разработки, не имеющих отношения к языкам Си или Си++. Например, анализатор Viva64 при своей работе убирает такие ключевые конструкции, как SA_Success или SA_FormatString, имеющиеся в заголовочных файлах Visual Studio.См. в коде: VivaPreprocessor, CreateStringInfo, IsInterestingLine, GetLineNumberByPtr,PreprocessingItem, SkipUninterestingCode.3) Лексический анализатор (Lexer)Вот мы и добрались до тех уровней обработки данных, которые представляют практическийинтерес для разработчиков. Разобрав код на лексемы, пользователь имеет возможностьпосчитать многие метрики, реализовать специфический алгоритм подсветки синтаксиса вразличных приложениях.
  8. 8. Лексический анализатор VivaCore разбирает текст программы на набор объектов типа Token (см.файл Token.h), которые содержат информацию о типе лексемы, ее местонахождении в текстепрограммы и длину. Типы лексем перечислены в файле tokennames.h. Примеры типов лексем:CLASS - ключевое слово языка "class"WCHAR - ключевое слово языка "wchar_t"В случае необходимости пользователь может расширить набор лексем. Это может бытьвостребовано в случае поддержки специфического синтаксиса конкретной реализации языка илипри разработке своего языкового расширения.При добавлении лексем необходимо объявить их в файле tokennames.h и добавить в таблицы"table" или "tableC" в файле Lex.cc. Первая таблица предназначена для обработки Си++ файлов, авторая - для Си файлов. Это естественно, так как набор лексем в языке Си и Си++ различен.Например, в языке Си отсутствует лексема CLASS, так как в Си слово "class" не является ключевыми может обозначать имя переменной.При добавлении новых лексем следует проявить аккуратность и не забыть скорректироватьфункции isTypeSpecifier, optIntegralTypeOrClassSpec и так далее. Чтобы не пропустить важноеместо, лучше всего взять близкое по значению ключевое слово и найти все места, где в VivaCoreиспользуется соответствующая лексема.Получить набор лексем можно как в виде простого массива, так и выгрузив их в файл. Лексемыхранятся в массиве tokens_ в классе Lex. Возможно получить как массив целиком, так и перебратьлексемы по отдельности, используя функции GetToken, LookAhead, CanLookAhead.Пользователь может получить лексемы в виде неструктурированного текста или используяфункцию DumpEx в следующем отформатированном виде:258 LC_ID 5258 lc_id 591 [ 1262 6 193 ] 159 ; 1303 struct 6123 { 1282 char 442 * 1258 locale 6Пользователь также может экспортировать лексемы в формате XML файла.См. в коде: Token, Lex, TokenContainer.4) Грамматический анализатор (Parser)Грамматический анализатор предназначен для построения дерева разбора (derivation tree - DT),которое в дальнейшем может быть подвергнуто анализу и трансформации. Обратите внимание,что грамматический анализатор библиотеки VivaCore строит не абстрактное синтаксическоедерево (АST), а именно дерево разбора. Это позволяет более просто осуществить поддержкуметапрограммных конструкций, которые могут быть добавлены пользователем в язык Си или
  9. 9. Си++. Если пользователю будет крайне необходимо работать именно с абст абстрактнымсинтаксическим деревом, то мы надеемся, что будет несложно доработать анализатор так, чтобыон проходил полное дерево разбора и удалял узлы и листья, которые не используются вабстрактном синтаксисе.Построение дерева в библиотеке VivaCore происходит в функциях класса Parser. Узлами и происходитлистьями дерева являются объекты, классы которых наследуются от базовых классов NonLeaf иLeaf. На рисунке 3 показана часть иерархии классов, используемых для представления дерева. Рисунок 3. Часть иерархии классов, используемых для построения дерева разбора. классов,Как видно из рисунка, класс Ptree является базовым классом для всех остальных и служит дляорганизации единого интерфейса для работы с другими классами. В классе Ptree имеется наборчистых виртуальных функций, реализуемых в потомках. Например, функция "virtual bool IsLeaf()const = 0;" реализуется в классах NonLeaf и Leaf. Практически классы реализуют только этуфункцию и нужны для того, чтобы сделать иерархию классов более логичной и красивой.Поскольку работа с деревом занимает существенный объем библиотеки, то в Ptree имеетсябольшой набор функций для работы с узлами дерева. Для удобства эти функции являютсяаналогами функций работы со списками в языке Lisp. Вот некоторые из них: Car, Cdr, Cadr, Cddr,LastNth, Length, Eq.
  10. 10. Чтобы получить общее представление о работе грамматического анализатора, в качестве примераприведем дерево разбора, которое будет построено из следующего кода:int MyFoo(const float value){ if (value < 1.0) return sizeof(unsigned long *); return value * 4.0f < 10.0f ? 0 : 1;}К сожалению, целиком дерево разбора изобразить не удастся, поэтому изобразим его по частямна рисунках 4.1-4.4. Рисунок 4.1. Цветовые обозначения узлов семантического дерева.
  11. 11. Рисунок 4.2. Представление заголовка функции.
  12. 12. Рисунок 4.3. Представление тела функции.
  13. 13. Рисунок 4.4. Представление тела функции.Следует упомянуть еще один важный компонент работы анализатора. Это получение информациио типах различных объектов (функциях, переменных и так далее), что осуществляется в классе (функциях,Encoding. Информация о типе представляется в виде специально закодированной строки, сформатом которой можно познакомиться в файле Encoding.cc. В библиотеке существует такжеспециальный класс TypeInfo, позволяющий извлекать информацию о типах, а также Info,
  14. 14. модифицировать ее. Например, используя такие функции как IsFunction, IsPointerType,IsBuiltInType можно легко идентифицировать тип обрабатываемого элемента.Описание подходов к добавлению новых типов узлов или листьев является нетривиальнойзадачей и не может быть изложено в этой обзорной статье. Рациональным решением будетвыбор одного из классов, например PtreeExprStatement и просмотр всех мест в коде, гдепроисходит создание объектов данного класса, работа с ними и так далее.Полученное по завершению дерево разбора может быть сохранено в формате ".с/.cpp" файла,что, впрочем, имеет мало смысла. Эта возможность обретет смысл после изменения дереваразбора, которое может произойти на следующих этапах. Сохранив дерево сейчас в виде кодапрограммы, мы получим ровно то, что получили на входе. Впрочем, это может быть вполнеполезно для тестирования изменений, внесенных в лексер и парсер.Больший интерес представляет возможность сохранить дерево для дальнейшей обработки впроизвольном формате, реализованном пользователем. Примером может служить следующеетекстовое представление кода, который был приведен ранее:PtreeDeclaration:[ 0 NonLeaf:[ LeafINT:int ] PtreeDeclarator:[ Leaf:MyFoo Leaf:( NonLeaf:[ NonLeaf:[ NonLeaf:[ LeafCONST:const NonLeaf:[ LeafFLOAT:float ] ] PtreeDeclarator:[ Leaf:value ]
  15. 15. ] ] Leaf:)][{ NonLeaf:[ PtreeIfStatement:[ LeafReserved:if Leaf:( PtreeInfixExpr:[ LeafName:value Leaf:< Leaf:1.0 ] Leaf:) PtreeReturnStatement:[ LeafReserved:return PtreeSizeofExpr:[ Leaf:sizeof Leaf:( NonLeaf:[ NonLeaf:[ LeafUNSIGNED:unsigned LeafLONG:long ] PtreeDeclarator:[ Leaf:* ] ] Leaf:)
  16. 16. ] Leaf:; ] ] PtreeReturnStatement:[ LeafReserved:return PtreeCondExpr:[ PtreeInfixExpr:[ PtreeInfixExpr:[ LeafName:value Leaf:* Leaf:4.0f ] Leaf:< Leaf:10.0f ] Leaf:? Leaf:0 Leaf:: Leaf:1 ] Leaf:; ] ] Leaf:} }]]Данный формат показан просто для примера. На практике, скорее всего, потребуется сохранятьбольше информации и в более удобном для обработки формате - например, в формате XML.См. в коде: Parser, Ptree, Leaf, NonLeaf, Encoding, TypeInfo, Typeof, PtreeUtil.
  17. 17. 5) Обход дерева разбораДля разработчиков статических анализаторов кода (подробное введение в задачу - книга [6]) илисистем построения документации по коду наибольший интерес должен представлять этап обходадерева разбора, осуществляемый с использованием классов Walker, ClassWalker, ClassBodyWalker.Обход дерева разбора можно осуществлять несколько раз, что позволяет создавать системы,модифицирующие код за несколько проходов, или проводить анализ, учитывающий уженакопленные знания при предыдущих обходах дерева.Класс Walker служит для обхода базовых конструкций языка Си/Си++.Класс ClassWalker наследуется от класса Walker и добавляет функциональность, связанную соспецификой классов, присутствующих в языке Си++.Когда необходимо разобрать тело класса, то временно создаются и используются объекты классаClassBodyWalker.Если не вносить никаких изменений в библиотеку VivaCore, то будет происходить простой проходпо всем элементам дерева. При этом само дерево не будет изменяться.Если пользователь реализует функциональность, которая будет модифицировать вершиныдерева, библиотека может перестроить дерево. Для примера рассмотрим код, транслирующийунарные операции:Ptree* ClassWalker::TranslateUnary(Ptree* exp){ using namespace PtreeUtil; Ptree* unaryop = exp->Car(); Ptree* right = PtreeUtil::Second(exp); Ptree* right2 = Translate(right); if(right == right2) return exp; else return new (GC_QuickAlloc) PtreeUnaryExpr(unaryop, PtreeUtil::List(right2));}Обратите внимание, что если, транслируя выражение, стоящее справа от унарной операции,полученное дерево будет изменено, то будет изменен (пересоздан) и узел унарной операции. Чтов свою очередь может повлечь перестройку и вышестоящих узлов.Для наглядности рассмотрим этот пример более подробно.
  18. 18. Начинается обработка узла, представляющего собой унарную операцию над некоторымвыражением и имеющего тип PtreeUnaryExpr. Первым элементом в списке, который извлекается спомощью операции exp->Car(), является непосредственно унарная операция. Вторым элементом,извлеченным с помощью PtreeUtil::Second(exp), является выражение, к которому применяетсяунарная операция.Происходит трансляция выражения и результат помещается в переменную right2. Если этот адресотличается от имеющегося, то это означает, что выражение было изменено. В этом случаепроисходит создание нового объекта типа PtreeUnaryExpr, которое и будет возвращено изфункции TranslateUnary. В противном случае ничего не изменяется и возвращается тот же объект,что и поступил на вход.Если пользователю потребуется производить сбор информации при обходе дерева, илипроизводить его модификацию, то самым естественным образом будет наследоваться от классовClassWalker и ClassBodyWalker.Покажем пример, взятый из статического анализатора Viva64, в котором происходитспециализированный анализ при проходе через оператор "throw":Ptree* VivaWalker::TranslateThrow(Ptree *p) { Ptree *result = ClassWalker::TranslateThrow(p); Ptree* oprnd = PtreeUtil::Second(result); if (oprnd != NULL) { //if oprnd==NULL then this is "throw;". if (!CreateWiseType(oprnd)) { return result; } if (IsErrorActive(115) && !ApplyRuleN10(oprnd->m_wiseType.m_simpleType)) { AddError(VivaErrors::V115(), p, 115); } } return result;}Вначале с помощью ClassWalker::TranslateThrow(p) выполняется стандартная трансляция узла.После чего выполняется необходимый анализ. Все просто и очень изящно.Говоря об обходе дерева, следует также сказать об очень важном классе Environment,обеспечивающем получение информаций о типах различных объектов в различных областяхвидимости.
  19. 19. Пример использования класса Environment, представленного объектом env для получения типаобъекта declTypeInfo:TypeInfo declTypeInfo;if (env->Lookup(decl, declTypeInfo)) { ...}См. в коде: AbstractTranslatingWalker, Walker, ClassWalker, ClassBodyWalker, Class, Environment,Bind, Class, TemplateClass.6) Поддержка метапрограммированияМетапрограммирование основано на подходе к генерации кода, когда код программы не пишетсявручную, а создается автоматически программой-генератором на основе другой, более простойпрограммы. Такой подход приобретает смысл, если при программировании вырабатываютсяразличные дополнительные правила (более высокоуровневые парадигмы, выполнениетребований внешних библиотек, стереотипные методы реализации определенных функций). Приэтом часть кода теряет содержательный смысл и становится лишь механическим выполнениемправил. Когда эта часть становится значительной, возникает мысль задавать вручную лишьсодержательную часть, а остальное добавлять автоматически. Этим и занимается генератор.Иногда такой генератор необходим для трансляции придуманного языка в операторы языка Сиили Си++. В VivaCore имеется механизм для удобного создания расширений языка Си/Си++ набазе метаобъектов. Возможно менять или строить новые синтаксические деревья с цельюсохранения их в код на языке Си/Си++.Подробно познакомиться с парадигмой метапрограммирования и методами использованияметаобъектов можно в документации библиотеки OpenC++.См. в коде: Metaclass.7) Сохранение результатовКак уже говорилось, можно осуществлять сохранение необходимой информации на любом этапепроцесса обработки исходного кода внутри библиотеки VivaCore. В том числе мы упоминали, чтополученное и измененное дерево разбора можно сохранить в виде текста программы или любомином формате. Не будем повторяться. Так же понятно, что подойти к задаче сбора необходимойинформации, например при статическом анализе или подсчете метрик, можно разнымиспособами, и поэтому нет смысла перечислять способы реализации.Остановимся только на использовании формата XML, который уже неоднократно упоминался встатье. XML - это текстовый формат, предназначенный для хранения структурированных данныхдля обмена информацией между программами или разными подсистемами обработкиинформации. XML является упрощённым подмножеством языка SGML.Мы используем XML для экспорта различной информации в надежде, что это поможет стороннимразработчикам легче использовать библиотеку VivaCore в своих программных разработках надругих языках программирования. Например, это будет весьма удобно для C# - программ. И, что
  20. 20. немаловажно, выбор XML в качестве формата хранения данных позволяет легче структурироватьинформацию и представлять ее в привычной для программиста форме.ЗаключениеМы понимаем, что после прочтения этой статьи может возникнуть больше новых вопросов оVivaCore, чем было получено ответов. Но хорошей новостью будет то, что наша командаViva64.com всегда открыта к общению, и мы готовы обсудить возникшие вопросы и датьрекомендации по использованию VivaCore. Пишите нам!Библиографический список 1. Евгений Рыжков. Viva64 - что это и для кого? http://viva64.com/art-1-1-2081052208.html 2. OpenC++ library. http://www.viva64.com/go.php?url=16 3. Alfred V. Aho, Monica S. Lam, Ravi Sethi, Jeffrey D. Ullman. Compilers: Principles, Techniques, and Tools (2nd Edition). Addison Wesley, 2006, 1000 pages. 4. Flemming Nielson, Hanne R. Nielson, Chris Hankin. Principles of Program Analysis. Springer, 2004, 452 pages. 5. Semantic Designs site. http://www.viva64.com/go.php?url=19 6. Patrick Cousot. Static Analysis. Springer, 2001, 450 pages.

×