Skip to content

silverbulleters/bsl-preprocessor-parser

Repository files navigation

Препроцессор языка 1С

Данная библиотека объединяет в себе следующие инструменты:

  • парсер языка препроцессора 1С;
  • препроцессор языка 1С;
  • контекст областей препроцессора 1С.

Парсер языка препроцессора 1С

Библиотека описывает только грамматику языка препроцессора, рассматривая сам код 1С как обычный текст. Это позволяет сосредоточиться на самих инструкциях препроцессора и группировать их в логические группы, не отвлекаясь на разбор "чистого" кода 1С.

Для того чтобы проводить анализ построенного дерева разбора инструкций препроцессора, в простейшем случае необходимо:

  1. Создать новый класс и сделать его наследником класса PreprocessorParserBaseListener;
  2. Переопределить необходимые вам обработчики узлов (см. грамматику в папке antlr и класс ModuleListener)
  3. Создать обходчик дерева:
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);
  }
}

Препроцессор языка 1С

Представлен классом 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С и может быть без проблем выполнен.

Контекст областей препроцессора 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