Данная библиотека объединяет в себе следующие инструменты:
- парсер языка препроцессора 1С;
- препроцессор языка 1С;
- контекст областей препроцессора 1С.
Библиотека описывает только грамматику языка препроцессора, рассматривая сам код 1С как обычный текст. Это позволяет сосредоточиться на самих инструкциях препроцессора и группировать их в логические группы, не отвлекаясь на разбор "чистого" кода 1С.
Для того чтобы проводить анализ построенного дерева разбора инструкций препроцессора, в простейшем случае необходимо:
- Создать новый класс и сделать его наследником класса
PreprocessorParserBaseListener
; - Переопределить необходимые вам обработчики узлов (см. грамматику в папке antlr и класс
ModuleListener
) - Создать обходчик дерева:
class MyBaseListener extends PreprocessorParserBaseListener {
}
class Main {
public static void main(String[] args) {
var charStream = CharStreams.fromStream(inputStream); // где inputStream это текстовые данные модуля на языке 1С
var lexer = new PreprocessorParserTokens(charStream);
var tokenStream = new CommonTokenStream(lexer);
var preprocessor = new PreprocessorParser(tokenStream);
var module = preprocessor.module();
var listener = new MyBaseListener();
var walker = new ParseTreeWalker();
walker.walk(listener, module);
}
}
Представлен классом Preprocessor
. Данный класс на вход получает экземпляр контекста модуля 1С который содержит инструкции препроцессора
и перечень возможных контекстов выполнения кода (например тонкий клиент, веб-клиент и сервер), после чего совершает расчет инструкций препроцессора.
Пример использования:
public class Main {
public static void main(String[] args) {
var pathToFile = Path.of("module.bsl");
try (var inputStream = new FileInputStream(pathToFile.toFile())) { // читаем данные файла
var moduleContext = preprocessor.readModuleContext(inputStream); // парсим данные файла и создаем экземпляр контекста модуля
var preprocessor = new Preprocessor(); // создаем препроцессора
var resultCode = preprocessor.preprocessModule(moduleContext, // делаем расчет инструкций препроцессора
Set.of(ExecutionContext.WEB_CLIENT, ExecutionContext.THIN_CLIENT, ExecutionContext.THICK_CLIENT_MANAGED_APPLICATION));
} catch (IOException e) {
throw new RuntimeException();
}
}
}
Результатом работы препроцессора является соответствие вида контекст - текст модуля
, причем в тексте модуля содержится только код
доступный в соответствующем ему контексту выполнения. Недоступный в данном контексте код и все инструкции препроцессора заменяются на символ пробела,
для того чтобы обеспечить сохранность оригинальных номеров строк кода.
Препроцессор поддерживает расчет всех доступных команд препроцессора:
- Если-КонецЕсли (с учетом всех альтернативных веток типа
ИначеЕсли
иИначе
и вложенных условий); - вставка кода;
- удаление кода;
- область.
Так как препроцессор рассматривает код 1С в виде обычного текста, это позволяет обрабатывать инструкции препроцессора которые "рвут" те или иные операторы языка 1С. Например, такой код вызовет ошибку компиляции в конфигураторе, но будет успешно обработан данным препроцессором:
Процедура Тест()
Текст = "
|Начало
#Region РазрывСтроки
|Середина
#КонецОбласти
|Конец";
КонецПроцедуры
Результатом обработки будет следующий код:
Процедура Тест()
Текст = "
|Начало
|Середина
|Конец";
КонецПроцедуры
Данный "чистый" код является валидным с точки зрения 1С и может быть без проблем выполнен.
Представлен классом RegionContext
. Данный класс на вход получает экземпляр контекста модуля 1С который содержит области,
после чего совершает расчет принадлежности имен областей к конкретным именам методов, переменных и областей кода.
Результатом работы является набор соответствий следующих видов:
- имя метода -> список имен областей, в которых содержится метод с таким именем. Самым первым элементом списка является имя самой вложенной области;
- имя переменной -> список имен областей, в которых содержится переменная с таким именем. Самым первым элементом списка является имя самой вложенной области;
- диапазон -> список имен областей, в которых содержится код с переданным диапазоном. Самым первым элементом списка является имя самой вложенной области.
Пример использования:
public class Main {
public static void main(String[] args) {
var pathToFile = Path.of("module.bsl");
var preprocessor = new Preprocessor();
try (var inputStream = new FileInputStream(pathToFile.toFile())) {
moduleContext = preprocessor.readModuleContext(inputStream);
var regionContext = new RegionContext(moduleContext);
List<String> regionNames = regionContext.getRegionsByMethodName("МойМетод");
} catch (IOException e) {
throw new RuntimeException();
}
}
}
API данной библиотеки НЕ финализирован. Обратная совместимость между версиями пока НЕ гарантируется.
Все замечания и предложения приветствуются и рассматриваются.
LGPL V3