Как известно, текст любой программы (код) пишется прежде всего для того, чтобы быть скомпилированным, то есть для «прочтения» компилятором. Поэтому программист в первую очередь старается соблюдать синтаксические и семантические правила языка программирования. При этом зачастую, особенно в среде неопытных разработчиков, забывается, что после написания кода он будет ещё не раз прочитан человеком – как самим разработчиком, спустя некоторое время, так и членами его команды. Чем сложнее (архитектурно или алгоритмически) код, тем тяжелей его читать и тем больше времени требуется на понимание его назначения. Для того, чтобы значительно уменьшить это время, к правильному с точки зрения компилятора коду предъявляется множество дополнительных требований. В данной работе рассматриваются требования, касающиеся форматирования кода и именования переменных. Форматированием кода называется расстановка в нём пробельных символов (пробелов, табуляций и переводов строк) так, чтобы они подчёркивали его синтаксическую структуру. Пример отформатированного (рис. 1) и неотформатированного (рис. 2) кода показан на рисунке. Три приведённых фрагмента с точки зрения компилятора эквивалентны, но видно, как отсутствие пробелов сразу сказывается на простоте восприятия структуры программы. while (true) { if (counter > MAX_COUNT) { nLoops++;
counter %= MAX_COUNT;
if (nLoops > MAX_LOOPS) break; }
counter++; doWork (counter); }
| while(true) { if(counter>MAX_COUNT){ nLoops++; counter%=MAX_COUNT; if (nLoops> MAX_LOOPS) break; } counter ++; doWork(counter);
}
| while (true) { if (C > MC) { n++; C %= MC; if (n > ML) break; } C++; d (C); }
| Рис. 1. Стилистически верный код для стиля K&R.
| Рис. 2. Код без форматирования.
| Рис. 3. Код с «плохими» именами переменных. |
Аналогично, с точки зрения компилятора не важно, какая последовательность символов является названием (идентификатором) переменной, функции, класса. Для программиста же названия переменных являются основными ориентирами в коде, при условии, что они отражают назначение именуемого объекта в программе. «Плохие» имена переменных (как на рис. 3) могут свести на нет пользу от документирования кода, вызвать неочевидные эффекты скрытия других имён (name shadowing) и так далее. На первый взгляд, задача проверки стиля решена во многих программных продуктах, таких как cpplint, KWStyle, Vera++, cxxchecker. Однако каждый из них работает, проверяя соответствие программы заданному стилю (либо фиксированному для анализатора, либо настраиваемому). Значит, при использовании для автоматизированной проверки необходимо настроить анализатор на какой-то единственный стиль. Такой подход, например, в образовательном процессе, не эффективен: он не развивает у учащихся чувства стиля, не даёт осознания необходимости следования стилю. Напротив, учащиеся начинают считать эту проверку пустой формальностью, пользуются автоматическими форматировщиками перед сдачей программ. При создании же этих программ они пишут код на подобии приведённого на рис. 2 и 3. В результате, во-первых, снижается их собственная продуктивность (хотя они этого зачастую не понимают), а во-вторых, получаемый код низкого качества не может быть использован в реальных проектах без серьёзной модификации. Целью данной работы явилась разработка анализатора стиля, свободного от необходимости задавать проверяемый стиль. К создаваемому инструменту предъявлялись следующие требования: 1. Адаптивность, то есть возможность автоматического определения преобладающего в программе стиля, а затем нахождения отклонений от него. 2. Устойчивость к стилистическим ошибкам и возможность их диагностики. 3. Непривязанность к конкретному языку. Этого удалось добиться, разделив анализатор на модуль, осуществляющий сбор информации о программе и зависящий от языка (front end) и модуль, непосредственно выполняющий анализ, не зависящий от языка (back end). Как для анализа форматирования, так и для выделения имён переменных необходима синтаксическая информация о программе. Получение этой информации для выбранного языка C++ оказалось нетривиальной задачей, поскольку грамматика языка полна по Тьюрингу. Перед тем, как была найдена оптимальная библиотека для работы с программами на C++, пришлось сделать несколько попыток-прототипов. Первый прототип был основан на библиотеке VivaCore, от которой пришлось быстро отказаться в силу её проприетарности и ориентированности на компилятор MSVC. Вторая попытка заключалась в модификации исходного кода компилятора GCC для восстановления дерева разбора, которое не строится компилятором явно. Для этого производилась запись моментов входа и выхода компилятора в функции разбора различных нетерминалов, затем по полученной информации восстанавливалось дерево разбора. Наконец, в последней версии прототипа используется библиотека LibTooling компилятора Clang, предоставляющая оптимальный интерфейс ко всем необходимым структурам (синтаксическому дереву, таблице имен, таблице макросов и т.д.). В результате работы: 1. Разработан подход для адаптивного анализа стиля в произвольной программе. 2. Проанализированы существующие способы получения синтаксической информации о программах на языке C++. 3. Создан прототип, анализирующий стиль программы на языке C++. 4. Реализована интеграция созданного прототипа в систему автоматизированной проверки программ ejudge. В такой конфигурации успешный вердикт анализатора является условием допуска программы к проверке на основных тестах. Пример результата работы прототипа (скриншот HTML-вывода анализатора): Рис. 4. (слева): Стиль «K&R». Рис. 5. (посередине): Стиль «Whitesmiths». Рис. 6. (справа): Смешение стилей K&R, Allman и Whitesmiths. Результаты работы прототипа (аннотированная программа на C++) приведены на рисунках выше. Программа рис. 4 написана в стиле K&R, имена переменных в ней не отделяются от скобок вызова функций. После того, как программист решает изменить стиль на Whitesmiths (рис. 5), отделять имена функций от аргументов, но не отделять оператор сравнения, анализатор способен распознать новый стиль и указать на изменившиеся ошибки. Наконец, фрагмент на рис. 6 содержит признаки смешения стилей, поэтому большое количество промежутков отмечено, как ошибочные (красным). Работа выполнена на языке C++. Список литературы [1] Herb Sutter, Andrei Alexandrescu. C++ Coding Standards: 101 Rules, Guidelines, and Best Practices. Addison-Wesley. [2] Google Style Guide (https://code.google.com/p/google-styleguide). [3] Альфред В. Ахо, Моника С. Лам, Рави Сети, Джеффри Д. Ульман Компиляторы: принципы, технологии и инструментарий, 2 изд. — М.: Вильямс, 2008. [4] Todd L. Veldhuizen. C++ Templates are Turing Complete. [5] VivaCore, открытая библиотека анализа C/C++/C++11 кода. http://www.viva64.com/ru/vivacore-library. [6] GNU Compiler Collection, GNU C Compiler. http://gcc.gnu.org. [7] Clang LibTooling. http://clang.llvm.org/docs/Tooling.html. [8] Valgrind Home. http://valgrind.org. [9] Cppcheck – a tool for static C/C++ code analysis. http://cppcheck.sourceforge.net. [10] Cxxchecker – C++ source-code style check. http://gna.org/projects/cxxchecker. [11] Vera++ – programmable verification and analysis tool for C++. http://www.inspirel.com/vera. [12] KWStyle – The Source Style Checker. http://kitware.github.io/KWStyle. [13] Ejudge contest management system. https://ejudge.ru. Научный руководитель: И. Р. Дединский.
|