Skip to content

Latest commit

 

History

History
1022 lines (737 loc) · 57.6 KB

book.md

File metadata and controls

1022 lines (737 loc) · 57.6 KB

Книга Джедая

May the Force be with you.

Содержание

Парсер

Общие сведения

Парсер полностью реализован в виде одной обработки ПарсерВстроенногоЯзыка.epf, которая предоставляет три механизма:

  • Разбор исходного кода на встроенном языке платформы 1С:Предприятие 8.3 с результатом в виде AST.
  • Встроенный механизм обхода AST для плагинов.
  • Встроенный механизм замен участков текста в исходнике.

На платформе 1С:Предприятие 8.3 создать экземпляр парсера можно следующим образом:

Путь = "C:\git\bsparser\build\ПарсерВстроенногоЯзыка.epf";
Парсер = ВнешниеОбработки.Создать(Путь, Ложь);

В скрипте на OneScript создать экземпляр парсера можно следующим образом:

ПодключитьСценарий("C:\git\bsparser\src\ПарсерВстроенногоЯзыка\Ext\ObjectModule.bsl", "ПарсерВстроенногоЯзыка");
Парсер = Новый ПарсерВстроенногоЯзыка;

Экземпляры плагинов и бакендов создаются аналогично.

Типичный сценарий работы с плагинами, которые регистрируют ошибки:

Плагины = Новый Массив;
Плагины.Добавить(Новый ДетекторНеиспользуемыхПеременных);
Плагины.Добавить(Новый ДетекторПропущенныхТочекСЗапятой);
Парсер.Пуск(Исходник, Плагины);
Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
  Сообщить(СтрШаблон("%1 [стр: %2; кол: %3]", Ошибка.Текст, Ошибка.НомерСтрокиНачала, Ошибка.НомерКолонкиНачала))
КонецЦикла;

Типичный сценарий работы с плагинами, которые генерируют текст:

Результаты = ПарсерВстроенногоЯзыка.Пуск(Исходник, Плагин);
Сообщить(СтрСоединить(Результаты));

Типичный сценарий работы с плагинами, которые регистрируют замены:

Плагины = Новый Массив;
Плагины.Добавить(Новый РасстановкаПропущенныхТочекСЗапятой);
Плагины.Добавить(Новый ЗаменаНеканоничныхКлючевыхСлов);
Парсер.Пуск(Исходник, Плагины);
ИсправленныйИсходник = Парсер.ВыполнитьЗамены();
Сообщить(ИсправленныйИсходник);

Типичный сценарий работы с бакендом:

АСД = Парсер.Разобрать(Исходник);
Текст = Бакенд.Посетить(Парсер, АСД);
Сообщить(Текст);

Программный интерфейс парсера

Основные методы

Данные методы составляют основной программный интерфейс для внешней по отношению к парсеру системы. Позволяют выполнить разбор исходного кода, обход AST с плагинами и выполнить модификацию исходного кода по сгенерированным плагинами заменам.

Чаще всего используется метод Пуск(), который заключает в себе типовую последовательность вызовов Разобрать(), Подключить(), Посетить() и Очистить(). Рабочие примеры использования данных методов смотрите в скриптах в папке /oscript

Пуск(Исходник, Плагины, Параметры, Окружение): Массив

Выполняет разбор исходника в указанном окружении, обход AST с указанными плагинами и их параметрами и возвращает результаты (обычно текстовые) работы плагинов.

Параметры:

  • Исходник: Строка -- исходный код для анализа.
  • Плагины: Массив или Обработка -- плагины, с которыми будет выполняться обход AST.
  • Параметры: Соответствие (необязательный) -- параметры плагинов.
  • Окружение: Структура (необязательный) -- окружение (контекст), в котором будет выполняться разбор исходника.

Пример с одним плагином:

Исходник = ЧтениеТекста.Прочитать();

Парсер = ВнешниеОбработки.Создать(ПарсерПуть, Ложь);

Плагин = ВнешниеОбработки.Создать(ПлагинПуть, Ложь);

Парсер.Пуск(Исходник, Плагин); // тут выполняется синтаксический анализ и обход АСД с плагинами

Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
    Сообщить(Ошибка.Текст); // вывод найденных парсером и плагином ошибок
КонецЦикла;

Пример с набором плагинов:

Исходник = ЧтениеТекста.Прочитать();

Парсер = ВнешниеОбработки.Создать(ПарсерПуть, Ложь);

Плагины = Новый Массив;
Плагины.Добавить(ВнешниеОбработки.Создать(Плагин1Путь, Ложь));
Плагины.Добавить(ВнешниеОбработки.Создать(Плагин2Путь, Ложь));

Парсер.Пуск(Исходник, Плагины); // тут выполняется синтаксический анализ и обход АСД с плагинами

Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
    Сообщить(Ошибка.Текст); // вывод найденных парсером и плагинами ошибок
КонецЦикла;

Пример с параметрами плагинов:

Исходник = ЧтениеТекста.Прочитать();

Парсер = ВнешниеОбработки.Создать(ПарсерПуть, Ложь);
Плагин1 = ВнешниеОбработки.Создать(Плагин1Путь, Ложь);
Плагин2 = ВнешниеОбработки.Создать(Плагин2Путь, Ложь);

Плагины = Новый Массив;
Плагины.Добавить(Плагин1);
Плагины.Добавить(Плагин2);

Параметры = Новый Соответствие; // у каждого плагина своя структура параметров
Параметры[Плагин1.ЭтотОбъект] = Новый Структура("Параметр1, Параметр2", 1, 2);
Параметры[Плагин2.ЭтотОбъект] = Новый Структура("Параметр1, Параметр2", 3, 4)

Парсер.Пуск(Исходник, Плагины, Параметры); // тут выполняется синтаксический анализ и обход АСД с плагинами

Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
  Сообщить(Ошибка.Текст); // вывод найденных парсером и плагинами ошибок
КонецЦикла;

Пример с окружением:

Исходник = ЧтениеТекста.Прочитать();

Парсер = ВнешниеОбработки.Создать(ПарсерПуть, Ложь);
Узлы = Парсер.Узлы();

Плагин = ВнешниеОбработки.Создать(ПлагинПуть, Ложь));

Окружение = Парсер.Окружение();

// например, контекст формы:
Элемент = Узлы.ЭлементОкружения.Добавить();
Элемент.Имя = "ЭтаФорма";
ПроизвольноеОкружение.Переменные.Вставить("ЭтаФорма", Элемент);
// ...
Элемент = Узлы.ЭлементОкружения.Добавить();
Элемент.Имя = "РеквизитФормыВЗначение";
ПроизвольноеОкружение.Методы.Вставить("РеквизитФормыВЗначение", Элемент);
// ...

Парсер.Пуск(Исходник, Плагины,, Окружение);

Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
  Сообщить(Ошибка.Текст); // вывод найденных парсером и плагином ошибок
КонецЦикла;
Разобрать(Исходник, Окружение): Структура

Выполняет разбор исходника в указанном окружении и возвращает AST.

Параметры:

  • Исходник: Строка -- исходный код для анализа.
  • Окружение: Структура (необязательный) -- окружение (контекст), в котором будет выполняться разбор исходника.

Пример:

Исходник = ЧтениеТекста.Прочитать();

Парсер = ВнешниеОбработки.Создать(ПарсерПуть, Ложь);
Узлы = Парсер.Узлы();

Окружение = Парсер.Окружение();

// например, контекст формы:
Элемент = Узлы.ЭлементОкружения.Добавить();
Элемент.Имя = "ЭтаФорма";
ПроизвольноеОкружение.Переменные.Вставить("ЭтаФорма", Элемент);
// ...
Элемент = Узлы.ЭлементОкружения.Добавить();
Элемент.Имя = "РеквизитФормыВЗначение";
ПроизвольноеОкружение.Методы.Вставить("РеквизитФормыВЗначение", Элемент);
// ...

АСД = Парсер.Разобрать(Исходник, Окружение);

Сообщить(АСД.Объявления.Количество());
Подключить(Плагины)

Подключает плагины к парсеру.

Параметры:

  • Плагины: Массив или Обработка -- плагины для подключения к парсеру.

Пример:

Парсер = ВнешниеОбработки.Создать(ПарсерПуть, Ложь);
Плагин = ВнешниеОбработки.Создать(ПлагинПуть, Ложь);

АСД = Парсер.Разобрать(Исходник); // тут выполняется синтаксический анализ
Парсер.Подключить(Плагин); // тут плагин подключается к парсеру
Парсер.Посетить(АСД); // тут выполняется обход АСД с вызовом подписок плагина

Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
    Сообщить(Ошибка.Текст); // вывод найденных парсером и плагином ошибок
КонецЦикла;
Посетить(Модуль, Параметры)

Выполняет обход AST с подключенными плагинами и указанными параметрами плагинов.

Параметры:

  • Модуль: Структура -- АСД для обхода.
  • Параметры: Соответствие (необязательный) -- параметры плагинов, с которыми нужно выполнить обход.

Пример:

Исходник = ЧтениеТекста.Прочитать();

Парсер = ВнешниеОбработки.Создать(ПарсерПуть, Ложь);
Плагин = ВнешниеОбработки.Создать(ПлагинПуть, Ложь);

АСД = Парсер.Разобрать(Исходник); // тут выполняется синтаксический анализ
Парсер.Подключить(Плагин); // тут плагин подключается к парсеру
Парсер.Посетить(АСД); // тут выполняется обход АСД с вызовом подписок плагина

Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
    Сообщить(Ошибка.Текст); // вывод найденных парсером и плагином ошибок
КонецЦикла;
Токенизировать(Исходник): ТаблицаЗначений

Выполняет лексический анализ и возвращает таблицу токенов.

Параметры:

  • Исходник: Строка -- исходный код для анализа.

Пример:

Исходник = ЧтениеТекста.Прочитать();

Парсер = ВнешниеОбработки.Создать(ПарсерПуть, Ложь);

ТаблицаТокенов = Парсер.Токенизировать(Исходник); // тут выполняется лексический анализ

Для Каждого ДанныеТокена Из ТаблицаТокенов Цикл
    Сообщить(ДанныеТокена.Токен); // вывод всех токенов
КонецЦикла;
ВыполнитьЗамены(): Строка

Возвращает исходник после выполнения на нем замен участков текста.

Пример:

Исходник = ЧтениеТекста.Прочитать();

Парсер = ВнешниеОбработки.Создать(ПарсерПуть, Ложь);
Плагин = ВнешниеОбработки.Создать(ПлагинПуть, Ложь);

Парсер.Пуск(Исходник, Плагин); // тут плагин наполняет таблицу замен

ИсправленныйИсходник = Парсер.ВыполнитьЗамены(); // тут по таблице замен последовательно заменяются участки текста в исходнике.

Сообщить(ИсправленныйИсходник)
Очистить()

Очищает в парсере таблицы токенов и узлов. Так освобождается память и парсер подготавливается к следующему разбору. Перед разбором таблицы улов не очищаются, т.к. могут быть наполнены извне при формировании внешнего окружения. Метод Пуск() уже включает этот вызов.

Пример:

ПарсерВстроенногоЯзыка = Новый ПарсерВстроенногоЯзыка;
АСД = ПарсерВстроенногоЯзыка.Разобрать(Исходник);
Сайт = ГенераторДокументации.Посетить(ПарсерВстроенногоЯзыка, АСД);
ПарсерВстроенногоЯзыка.Очистить();

Дополнительные методы

Данные методы в основном используются в плагинах и вызываются один раз в процедуре Открыть(). Методы предоставляют различную вспомогательную информацию и таблицы, в которых плагины могут регистрировать ошибки и замены.

Примеры использования данных методов смотрите в плагинах в папке /plugins

Узлы(): Структура

Возвращает структуру таблиц, хранящих узлы. На каждый тип узла отдельная таблица в одноименном свойстве структуры.

Пример (фрагмент кода плагина):

Перем Узлы;

Процедура Открыть(Парсер, Параметры) Экспорт
    Узлы = Парсер.Узлы();
КонецПроцедуры

Функция Закрыть() Экспорт
    Для Каждого Объявление Из Узлы.ОбъявлениеМетода Цикл
        Сообщить(Объявление.Сигнатура.Имя);
    КонецЦикла;
    Возврат Неопределено;
КонецФункции
КлючевыеСлова(): Структура

Возвращает перечисление допустимых ключевых слов.

Пример (фрагмент кода плагина):

Перем ТаблицаТокенов, КлючевыеСлова;

Процедура Открыть(Парсер, Параметры) Экспорт
    ТаблицаТокенов = Парсер.ТаблицаТокенов();
    КлючевыеСлова = Парсер.КлючевыеСлова();
КонецПроцедуры

Функция Закрыть() Экспорт
    Для Каждого ДанныеТокена Из ТаблицаТокенов Цикл
        Если ДанныеТокена.Токен = КлючевыеСлова.ИначеЕсли Тогда
            // ...
        КонецЕсли;
    КонецЦикла;
    Возврат Неопределено;
КонецФункции
Токены(): Структура

Возвращает перечисление допустимых токенов.

Пример (фрагмент кода плагина):

Перем ТаблицаТокенов, Токены;

Процедура Открыть(Парсер, Параметры) Экспорт
    ТаблицаТокенов = Парсер.ТаблицаТокенов();
    Токены = Парсер.Токены();
КонецПроцедуры

Функция Закрыть() Экспорт
    Для Каждого ДанныеТокена Из ТаблицаТокенов Цикл
        Если ДанныеТокена.Токен = Токены.ЗнакСложения Тогда
          // ...
        КонецЕсли;
    КонецЦикла;
    Возврат Неопределено;
КонецФункции
Типы(): Структура

Возвращает перечисление допустимых типов узлов AST.

Пример (фрагмент кода плагина):

Перем Типы;

Процедура Открыть(Парсер, Параметры) Экспорт
  Типы = Парсер.Типы();
КонецПроцедуры

Процедура ПосетитьОператоры(Операторы) Экспорт
    Для Каждого Оператор Из Операторы Цикл
        Если Оператор.Тип = Типы.ОператорПрисваивания Тогда
          // ...
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры
Директивы(): Структура

Возвращает перечисление допустимых директив.

Пример (фрагмент кода плагина):

Перем Директивы;

Процедура Открыть(Парсер, Параметры) Экспорт
  Директивы = Парсер.Директивы();
КонецПроцедуры

Процедура ПосетитьОбъявлениеПеременнойМодуля(Объявление) Экспорт
    Если Объявление.Директивы.Количество() = 1
        И Объявление.Директивы[0].Директива = Директивы.НаКлиенте Тогда
        // ...
    КонецЕсли;
КонецПроцедуры
Аннотации(): Структура

Возвращает перечисление допустимых аннотаций.

Пример (фрагмент кода плагина):

Перем Аннотации;

Процедура Открыть(Парсер, Параметры) Экспорт
  Аннотации = Парсер.Аннотации();
КонецПроцедуры

Процедура ПосетитьОбъявлениеМетода(Объявление) Экспорт
    Аннотации = Объявление.Сигнатура.Аннотации;
    Если Аннотации.Количество() = 1
        И Аннотации[0].Аннотация = Аннотации.Вместо Тогда
        // ...
    КонецЕсли;
КонецПроцедуры
СимволыПрепроцессора(): Структура

Возвращает перечисление допустимых символов препроцессора.

Пример (фрагмент кода плагина):

Перем СимволыПрепроцессора;

Процедура Открыть(Парсер, Параметры) Экспорт
    СимволыПрепроцессора = Парсер.СимволыПрепроцессора();
КонецПроцедуры

Процедура ПосетитьВыражениеПрепроцессораСимвол(Выражение) Экспорт
    Если Выражение.Символ = СимволыПрепроцессора.ТолстыйКлиентОбычноеПриложение Тогда
        // ...
    КонецЕсли;
КонецПроцедуры
Стек(): Массив

Возвращает стек узлов, который меняется во время обхода AST и позволяет получить текущие родительские узлы в подписках плагинов.

Пример (фрагмент кода плагина):

Перем Стек;

Процедура Открыть(Парсер, Параметры) Экспорт
    Стек = Парсер.Стек();
КонецПроцедуры

Процедура ПосетитьВыражениеТернарное(Выражение) Экспорт
    Для Каждого Родитель Из Стек Цикл
        Если Родитель.Тип = Типы.ВыражениеТернарное Тогда
            Сообщить("Вложенный тернарный оператор");
            Прервать;
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры
Счетчики(): Соответствие

Возвращает счетчики родительских узлов, которые меняются во время обхода AST и позволяет получить количество текущих родительских узлов по типам в подписках плагинов.

Пример (фрагмент кода плагина):

Перем Счетчики;

Процедура Открыть(Парсер, Параметры) Экспорт
  Счетчики = Парсер.Счетчики();
КонецПроцедуры

Процедура ПосетитьВыражениеТернарное(Выражение) Экспорт
    Если Счетчики.ВыражениеТернарное > 0 Тогда
        Сообщить("Вложенный тернарный оператор");
    КонецЕсли;
КонецПроцедуры
Исходник(): Строка

Возвращает текущий исходник.

Пример (фрагмент кода плагина):

Перем Исходник, Токены, ТаблицаТокенов;

Процедура Открыть(Парсер, Параметры) Экспорт
    Исходник = Парсер.Исходник();
    Токены = Парсер.Токены();
    ТаблицаТокенов = Парсер.ТаблицаТокенов();
КонецПроцедуры

Процедура ПосетитьОбъявлениеМетода(ОбъявлениеМетода) Экспорт
    ДанныеСледующегоТокена = ТаблицаТокенов[ОбъявлениеМетода.Конец.Индекс + 1];
    Если ДанныеСледующегоТокена.Токен = Токены.Комментарий Тогда
        Сообщить(Сред(Исходник, ДанныеСледующегоТокена.Позиция, ДанныеСледующегоТокена.Длина));
    КонецЕсли;
КонецПроцедуры
ТаблицаТокенов(): ТаблицаЗначений

Возвращает текущую таблицу токенов, которая содержит подробную информацию о положении каждого токена в исходном коде. Каждый узел AST имеет поля Начало и Конец, которые хранят строки данной таблицы и представляют первый и последний токен узла. Например, получить номер первой строки узла можно так: НомерСтроки = Узел.Начало.НомерСтроки. Получить следующий за узлом токен можно так: ДанныеСледующегоТокена = ТаблицаТокенов[Узел.Конец.Индекс + 1].

Колонки:

  • Индекс: Число -- индекс строки для удобства.
  • Токен: Строка -- токен из перечисления Токены.
  • НомерСтроки: Число -- номер строки, в которой находится токен.
  • НомерКолонки: Число -- номер колонки, в которой начинается токен.
  • Позиция: Число -- позиция в тексте, в которой начинается токен.
  • Длина: Число -- длина участка текста, который представляет токен.

Пример (фрагмент кода плагина):

Перем Исходник, Токены, ТаблицаТокенов;

Процедура Открыть(Парсер, Параметры) Экспорт
    Исходник = Парсер.Исходник();
    Токены = Парсер.Токены();
    ТаблицаТокенов = Парсер.ТаблицаТокенов();
КонецПроцедуры

Процедура ПосетитьОбъявлениеМетода(ОбъявлениеМетода) Экспорт
    ДанныеСледующегоТокена = ТаблицаТокенов[ОбъявлениеМетода.Конец.Индекс + 1];
    Если ДанныеСледующегоТокена.Токен = Токены.Комментарий Тогда
        Сообщить(Сред(Исходник, ДанныеСледующегоТокена.Позиция, ДанныеСледующегоТокена.Длина));
    КонецЕсли;
КонецПроцедуры
ТаблицаОшибок(): ТаблицаЗначений

Возвращает текущую таблицу ошибок, в которой плагины могут регистрировать ошибки.

Колонки:

  • Источник: Строка -- имя обработки, которая зарегистрировала ошибку.
  • Текст: Строка -- текст ошибки
  • ПозицияНачала: Число -- позиция в тексте, с которой начинается ошибочный участок.
  • ПозицияКонца: Число -- позиция в тексте, на которой заканчивается ошибочный участок.
  • ЕстьЗамена: Булево -- признак, что для ошибки есть замена (исправление) в таблице замен.
  • Код: Число -- код ошибки (для плагинов всегда 0).
  • НомерСтрокиНачала: Число -- номер строки, на которой начинается ошибочный участок.
  • НомерКолонкиНачала: Число -- номер колонки, с которой начинается ошибочный участок.
  • НомерСтрокиКонца: Число -- номер строки, на которой заканчивается ошибочный участок.
  • НомерКолонкиКонца: Число -- номер колонки, на которой заканчивается ошибочный участок.
  • МинутНаИсправление: Число -- примерная оценка затрат на исправление ошибки в минутах.
  • Серьезность: Строка -- произвольный текст на усмотрение разработчика.
  • Приоритет: Число -- произвольное число на усмотрение разработчика.
  • Правило: Строка -- произвольный текст на усмотрение разработчика.
  • Тип: Строка -- произвольный текст на усмотрение разработчика.

Пример (фрагмент кода плагина):

Перем Исходник, Токены, ТаблицаТокенов;

Процедура Открыть(Парсер, Параметры) Экспорт
    Исходник = Парсер.Исходник();
    Токены = Парсер.Токены();
    ТаблицаТокенов = Парсер.ТаблицаТокенов();
КонецПроцедуры

Процедура ПосетитьОбъявлениеМетода(ОбъявлениеМетода) Экспорт

    СледующийТокен = ТаблицаТокенов[ОбъявлениеМетода.Конец.Индекс + 1];

    Если СледующийТокен.Токен = Токены.Комментарий
        И СледующийТокен.НомерСтроки = ОбъявлениеМетода.Конец.НомерСтроки Тогда

        Комментарий = СокрП(Сред(Исходник, СледующийТокен.Позиция, СледующийТокен.Длина));
        ПравильныйКомментарий = СтрШаблон(" %1%2", ОбъявлениеМетода.Сигнатура.Имя, "()");

        Если Комментарий <> ПравильныйКомментарий Тогда
            Ошибка("Замыкающий комментарий неактуален", СледующийТокен);
        КонецЕсли;

    КонецЕсли;

КонецПроцедуры

Процедура Ошибка(Текст, ДанныеТокена)
  Ошибка = ТаблицаОшибок.Добавить();
  Ошибка.Источник = "ИмяЭтогоПлагина";
  Ошибка.Текст = Текст;
  Ошибка.ПозицияНачала = ДанныеТокена.Позиция;
  Ошибка.НомерСтрокиНачала = ДанныеТокена.НомерСтроки;
  Ошибка.НомерКолонкиНачала = ДанныеТокена.НомерКолонки;
  Ошибка.ПозицияКонца = ДанныеТокена.Позиция + ДанныеТокена.Длина;
  Ошибка.НомерСтрокиКонца = ДанныеТокена.НомерСтроки;
  Ошибка.НомерКолонкиКонца = ДанныеТокена.НомерКолонки + ДанныеТокена.Длина;
КонецПроцедуры
ТаблицаЗамен(): ТаблицаЗначений

Возвращает текущую таблицу замен, в которой плагины могут регистрировать замены.

Колонки:

  • Источник: Число -- имя обработки, которая зарегистрировала замену.
  • Текст: Строка -- фрагмент текста, на который нужно заменить указанный участок.
  • Позиция: Число -- позиция участка текста для замены.
  • Длина: Число -- длина участка текста для замены.

Пример (фрагмент кода плагина):

Перем Исходник, Токены, ТаблицаТокенов, ТаблицаЗамен;

Процедура Открыть(Парсер, Параметры) Экспорт
    Исходник = Парсер.Исходник();
    Токены = Парсер.Токены();
    ТаблицаТокенов = Парсер.ТаблицаТокенов();
    ТаблицаЗамен = Парсер.ТаблицаЗамен();
КонецПроцедуры

Процедура ПосетитьОбъявлениеМетода(ОбъявлениеМетода) Экспорт

    СледующийТокен = ТаблицаТокенов[ОбъявлениеМетода.Конец.Индекс + 1];

    Если СледующийТокен.Токен = Токены.Комментарий
        И СледующийТокен.НомерСтроки = ОбъявлениеМетода.Конец.НомерСтроки Тогда

        Комментарий = СокрП(Сред(Исходник, СледующийТокен.Позиция, СледующийТокен.Длина));
        ПравильныйКомментарий = СтрШаблон(" %1%2", ОбъявлениеМетода.Сигнатура.Имя, "()");

        Если Комментарий <> ПравильныйКомментарий Тогда
          Замена(ПравильныйКомментарий, СледующийТокен);
        КонецЕсли;

    КонецЕсли;

КонецПроцедуры

Процедура Замена(Текст, ДанныеТокена)
  НоваяЗамена = ТаблицаЗамен.Добавить();
  НоваяЗамена.Источник = "ИмяЭтогоПлагина";
  НоваяЗамена.Текст = Текст;
  НоваяЗамена.Позиция = ДанныеТокена.Позиция;
  НоваяЗамена.Длина = ДанныеТокена.Длина;
КонецПроцедуры

Продвинутые методы

Данные методы позволяют сформировать окружение, в контексте которого будет выполняться разбор исходного кода.

Пример формирования окружения смотрите в скрипте: /examples/oscript/test5.os

Пример формирования глобального окружения смотрите в обработке: /scope/global

Окружение(ВнешнееОкружение): Структура

Конструктор. Возвращает новое окружение.

Параметры:

  • ВнешнееОкружение: Структура -- внешнее окружение по отношению к создаваемому.
Доступность(Клиент, ВнешнееСоединение, МобильноеПриложение, МобильныйКлиент, МобильныйСервер, Сервер, ТолстыйКлиент, ТонкийКлиент, ВебКлиент, Интеграция): Структура

Конструктор. Возвращает новую структуру доступности.

Параметры:

  • Клиент: Булево -- признак доступности в одноименном контексте.
  • ВнешнееСоединение: Булево -- признак доступности в одноименном контексте.
  • МобильноеПриложение: Булево -- признак доступности в одноименном контексте.
  • МобильныйКлиент: Булево -- признак доступности в одноименном контексте.
  • МобильныйСервер: Булево -- признак доступности в одноименном контексте.
  • Сервер: Булево -- признак доступности в одноименном контексте.
  • ТолстыйКлиент: Булево -- признак доступности в одноименном контексте.
  • ТонкийКлиент: Булево -- признак доступности в одноименном контексте.
  • ВебКлиент: Булево -- признак доступности в одноименном контексте.
  • Интеграция: Булево -- признак доступности в одноименном контексте.

Служебные методы

Эти методы позволяют частично установить состояние парсера без выполнения разбора. Это необходимо при выполнении обхода кэшированных AST, чтобы плагинам была доступна соответствующая информация при инициализации.

УстановитьИсходник(Исходник)

Устанавливает исходник напрямую без выполнения анализа.

Параметры:

  • Исходник: Строка -- исходный код, который нужно установить в парсер.
УстановитьТаблицуТокенов(ТаблицаТокенов)

Устанавливает таблицу токенов напрямую без выполнения анализа.

Параметры:

  • ТаблицаТокенов: ТаблицаЗначений -- таблица токенов, которую нужно установить в парсер.
УстановитьТаблицуОшибок(ТаблицаОшибок)

Устанавливает таблицу ошибок напрямую без выполнения анализа.

Параметры:

  • ТаблицаОшибок: ТаблицаЗначений -- таблица ошибок, которую нужно установить в парсер.

Параметры

СтрогийРежим: Булево

Определяет поведение парсера при возникновении ошибочных ситуаций.

Плагины

С помощью плагинов можно решать много различных задач. В частности:

  • Поиск ошибок в исходном коде.
  • Проверка исходного кода на соответствие стандартам как внешним так и внутренним.
  • Проверка исходного кода на соответствие требованиям безопасности.
  • Проверка оформления исходного кода.
  • Модификация исходного кода путем замен/вставки/удаления фрагментов.
  • Форматирование исходного кода.
  • Генерация документации по исходному коду.
  • Подготовка исходного кода к публикации (генерация html).
  • Сбор статистики по исходному коду.
  • Выполнение автоматизированных переименований и переводов.
  • Перевод исходного кода на другие языки программирования.
  • Обфускация исходного кода.
  • Интерпретация и компиляция.
  • Продвинутый автоматизированный поиск по исходному коду.

Плагин реализуется в виде одной обработки.

Для создания плагина нужно выполнить следующую последовательность шагов:

  1. Создать новую обработку.
  2. В модуль объекта обработки скопировать шаблон с сайта: bsparser
  3. Заглянуть в метод Подписки() в модуле парсера и выбрать те, которые нужны для решения задачи. Например, если нужно проверить все присваивания, то можно выбрать подписку "ПосетитьОператорПрисваивания".
  4. Добавить подписку в массив в методе Подписки() будущего плагина. Это указание парсеру что плагин реализует такую процедуру.
  5. Создать одноименную экспортную процедуру и написать в ней код, решающий задачу.
  6. Далее можно отладить плагин в консоли парсера (обработка Консоль.epf).
  7. Удалить лишние переменные, которые были скопированы из шаблона, но в итоге не пригодились.

Программный интерфейс плагина

Интерфейс плагина состоит из трех постоянных методов и набора методов-подписок.

Например, плагин может подписаться на посещение узла оператора присваивания. В этом случае он должен реализовать экспортный метод ПосетитьОператорПрисваивания(ОператорПрисваивания) и вернуть имя этого метода ПосетитьОператорПрисваивания в методе Подписки()

Открыть(Парсер, Параметры)

Вызывается один раз перед обходом AST; позволяет выполнить необходимую инициализацию плагина.

Параметры:

  • Парсер: Обработка -- экземпляр парсера, к кторому подключен плагин.
  • Параметры: Структура -- параметры плагина, переданные парсеру в методе Пуск() или Посетить().

Закрыть(): Произвольный

Вызывается один раз после обхода AST, позволяет плагину "подвести итоги" и вернуть результат своей работы. Некоторые плагины всю работу выполняют в данном методе.

Подписки(): Массив

Вызывается один раз при подключении плагинов. Возвращает список имен процедур-подписок, которые реализует данный плагин.

Минимальный шаблон плагина

Перем Результат;

// Будет вызвана один раз перед обходом AST.
// Тут можно получить необходимые перечисления и таблицы из парсера,
// и выполнить инициализацию плагина с учетом полученных параметров.
Процедура Открыть(Парсер, Параметры) Экспорт
    Результат = Новый Массив;
КонецПроцедуры

// Будет вызвана после полного обхода AST.
// Возвращает текстовый результат работы плагина, если он есть.
// Плагины, которые регистрируют ошибки и/или замены, могут вернуть Неопределено.
Функция Закрыть() Экспорт
    Возврат СтрСоединить(Результат);
КонецФункции

// Возвращает список процедур-подписок, которые будут вызываться визитером.
// Состав возможных подписок можно посмотреть в исходнике парсера в функции Подписки().
// Имена большинства подписок образуются добавлением префикса Посетить/Покинуть к имени типа узла.
// Справка по типам узлов находится по этому адресу: https://lead-tools.github.io/bsparser/
Функция Подписки() Экспорт
    Перем Подписки;
    Подписки = Новый Массив;
    Подписки.Добавить("ПосетитьОператорПрисваивания");
    Подписки.Добавить("ПокинутьОператорПрисваивания");
    Возврат Подписки;
КонецФункции

#Область РеализацияПодписок

// Описание структуры узла `ОператорПрисваивания`: https://lead-tools.github.io/bsparser/#ОператорПрисваивания

// Данная процедура будет вызвана при посещении узла AST перед посещением подчиненных ему узлов.
// Вызов подписок с префиксом `Посетить` отражает рекурсивный спуск визитера по AST.
// Сначала вызывается подписка на родительский узел, потом на этот, потом на подчиненный и так далее.
Процедура ПосетитьОператорПрисваивания(ОператорПрисваивания) Экспорт
    // ...
КонецПроцедуры

// Данная процедура будет вызвана при посещении узла AST после посещения подчиненных ему узлов.
// Вызов подписок с префиксом `Покинуть` отражает рекурсивный подъем визитера по AST.
// Сначала вызывается подписка на подчиненный узел, потом на этот, потом на родительский и так далее.
Процедура ПокинутьОператорПрисваивания(ОператорПрисваивания) Экспорт
    // ...
КонецПроцедуры

#КонецОбласти

Полный шаблон плагина

Перем Типы;
Перем Токены;
Перем Исходник;
Перем ТаблицаТокенов;
Перем ТаблицаОшибок;
Перем ТаблицаЗамен;
Перем Стек;
Перем Счетчики;
Перем Директивы;
Перем Аннотации;
Перем СимволыПрепроцессора;

Перем Результат;

Процедура Открыть(Парсер, Параметры) Экспорт
  
    Типы = Парсер.Типы();
    Токены = Парсер.Токены();
    Исходник = Парсер.Исходник();
    ТаблицаТокенов = Парсер.ТаблицаТокенов();
    ТаблицаОшибок = Парсер.ТаблицаОшибок();
    ТаблицаЗамен = Парсер.ТаблицаЗамен();
    Стек = Парсер.Стек();
    Счетчики = Парсер.Счетчики();
    Директивы = Парсер.Директивы();
    Аннотации = Парсер.Аннотации();
    СимволыПрепроцессора = Парсер.СимволыПрепроцессора();

    Результат = Новый Массив;

КонецПроцедуры

Функция Закрыть() Экспорт
    // ...
    Возврат СтрСоединить(Результат);
КонецФункции

Функция Подписки() Экспорт
    Перем Подписки;
    Подписки = Новый Массив;
    Подписки.Добавить("ПосетитьОператорПрисваивания");
    //Подписки.Добавить("ПокинутьОператорПрисваивания");
    Возврат Подписки;
КонецФункции

#Область РеализацияПодписок

Процедура ПосетитьОператорПрисваивания(ОператорПрисваивания) Экспорт
    Ошибка("Ошибка в операторе присваивания", ОператорПрисваивания.Начало, ОператорПрисваивания.Конец);
КонецПроцедуры // ПосетитьОператорПрисваивания()

//Процедура ПокинутьОператорПрисваивания(ОператорПрисваивания) Экспорт
//
//КонецПроцедуры // ПокинутьОператорПрисваивания()

#КонецОбласти

Процедура Ошибка(Текст, Начало, Конец = Неопределено, ЕстьЗамена = Ложь)
    Ошибка = ТаблицаОшибок.Добавить();
    Ошибка.Источник = "ИмяЭтогоПлагина";
    Ошибка.Текст = Текст;
    Ошибка.ПозицияНачала = Начало.Позиция;
    Ошибка.НомерСтрокиНачала = Начало.НомерСтроки;
    Ошибка.НомерКолонкиНачала = Начало.НомерКолонки;
    Если Конец = Неопределено Или Конец = Начало Тогда
        Ошибка.ПозицияКонца = Начало.Позиция + Начало.Длина;
        Ошибка.НомерСтрокиКонца = Начало.НомерСтроки;
        Ошибка.НомерКолонкиКонца = Начало.НомерКолонки + Начало.Длина;
    Иначе
        Ошибка.ПозицияКонца = Конец.Позиция + Конец.Длина;
        Ошибка.НомерСтрокиКонца = Конец.НомерСтроки;
        Ошибка.НомерКолонкиКонца = Конец.НомерКолонки + Конец.Длина;
    КонецЕсли;
    Ошибка.ЕстьЗамена = ЕстьЗамена;
КонецПроцедуры

Процедура Замена(Текст, Начало, Конец = Неопределено)
    НоваяЗамена = ТаблицаЗамен.Добавить();
    НоваяЗамена.Источник = "ИмяЭтогоПлагина";
    НоваяЗамена.Текст = Текст;
    НоваяЗамена.Позиция = Начало.Позиция;
    Если Конец = Неопределено Тогда
        НоваяЗамена.Длина = Начало.Длина;
    Иначе
        НоваяЗамена.Длина = Конец.Позиция + Конец.Длина - Начало.Позиция;
    КонецЕсли;
КонецПроцедуры

Процедура Вставка(Текст, Позиция)
    НоваяЗамена = ТаблицаЗамен.Добавить();
    НоваяЗамена.Источник = "ИмяЭтогоПлагина";
    НоваяЗамена.Текст = Текст;
    НоваяЗамена.Позиция = Позиция;
    НоваяЗамена.Длина = 0;
КонецПроцедуры

Бакенды

Бакенд реализуется в виде одной обработки.

Бакенды отличаются от плагинов тем, что не используют общий механизм обхода, а выполняют всю работу сами.

Программный интерфейс бакенда

Бакенд должен реализовать один метод:

Посетить(Парсер, АСД): Произвольный

Выполняет произвольную работу и возвращает произвольный результат.

  • Парсер: Обработка -- экземпляр парсера.
  • АСД: Структура -- абстрактное синтаксическое дерево.