Как в 1С с помощью VBScript.RegExp извлечь имя функции и признак Экспорт

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке вспомогательных инструментов — анализаторов кода, генераторов документации или построителей деревьев вызовов — часто требуется программно разобрать заголовок процедуры или функции 1С. Типичная задача состоит в том, чтобы получить имя метода, список его параметров и определить, является ли метод экспортным (содержит ли ключевое слово Экспорт).

Для решения этой задачи в среде Windows традиционно используется COM-объект VBScript.RegExp. Разберем типичные ошибки при составлении регулярных выражений, посмотрим на рабочие шаблоны и проанализируем ограничения данного подхода.

Анализ ошибки в исходном регулярном выражении

Рассмотрим шаблон, с которого началась проблема:

ШаблонФункции.Pattern = "\s*?(\S+?)\([\S\s]*?\)\s*?(экспорт)*?.*";

При таком шаблоне имя функции попадает в первую группу захвата (SubMatches), а группа с ключевым словом экспорт возвращает пустое значение. Выясним причину такого поведения:

  1. Избыточность и конфликт ленивых квантификаторов: конструкция \s*? перед группой и ленивый квантификатор *? на самой группе (экспорт)*? заставляют движок сопоставить ноль вхождений слова «экспорт». Так как после группы стоит жадный хвост .*, движок просто «проглатывает» слово «экспорт» в остаток строки, не захватывая его в группу.
  2. Пробелы перед открывающей скобкой: шаблон \S+?\( ожидает открывающую скобку сразу после идентификатора. Если в коде перед скобкой стоит один или несколько пробелов (например, МояФункция (Параметр)), ленивый шаблон \S+? может отработать некорректно.
  3. Регистрозависимость: в платформе 1С ключевые слова могут быть написаны в любом регистре (Экспорт, ЭКСПОРТ, экспорт). Без включенного свойства IgnoreCase поиск может завершиться неудачей.

Исправление шаблона для заголовка функции

Скорректируем квантификаторы, сделаем группу экспорта опциональной с помощью знака ? (одно или ноль вхождений) и добавим обработку возможных пробелов перед скобками параметров.


РегЭксп = Новый COMОбъект("VBScript.RegExp");
РегЭксп.IgnoreCase = Истина;
РегЭксп.Global = Истина;
РегЭксп.Multiline = Истина;

// Шаблон для строки вида "ИмяМетода(Параметры) Экспорт"
РегЭксп.Pattern = "^\s*([a-zA-Zа-яА-ЯёЁ_][a-zA-Zа-яА-ЯёЁ0-9_]*)\s*\(([\s\S]*?)\)\s*(экспорт)?";

Разберем структуру этого выражения по шагам:

  1. ^\s* — начало строки с возможными ведущими пробелами или отступами.
  2. ([a-zA-Zа-яА-ЯёЁ_][a-zA-Zа-яА-ЯёЁ0-9_]*) — первая подмаска (SubMatches[0]), строго захватывающая корректный идентификатор 1С (начинается с буквы или подчеркивания, не содержит спецсимволов).
  3. \s*\( — возможные пробелы перед открывающей скобкой и сама скобка.
  4. ([\s\S]*?) — вторая подмаска (SubMatches[1]), извлекающая тело параметров метода (включая многострочные переносы).
  5. \)\s* — закрывающая скобка и пробелы после нее.
  6. (экспорт)? — третья подмаска (SubMatches[2]), опционально захватывающая ключевое слово. Если метод экспортный, эта группа будет содержать строку «Экспорт» (или «экспорт»), иначе останется пустой.

Пример чтения результатов разбора заголовка

Посмотрим на практический пример обработки строки в 1С:


РегЭксп = Новый COMОбъект("VBScript.RegExp");
РегЭксп.IgnoreCase = Истина;
РегЭксп.Global = Ложь;
РегЭксп.Pattern = "^\s*([a-zA-Zа-яА-ЯёЁ_][a-zA-Zа-яА-ЯёЁ0-9_]*)\s*\(([\s\S]*?)\)\s*(экспорт)?";

СтрокаТеста = "РассчитатьСумму(Знач Сумма, СтавкаНДС = 20) Экспорт // комментарий";
Совпадения = РегЭксп.Execute(СтрокаТеста);

Если Совпадения.Count > 0 Тогда
    Результат = Совпадения.Item(0);
    ИмяМетода = Результат.SubMatches.Item(0);
    Параметры = Результат.SubMatches.Item(1);
    ПризнакЭкспорт = (Результат.SubMatches.Count > 2 И ЗначениеЗаполнено(Результат.SubMatches.Item(2)));
    
    Сообщить("Имя: " + ИмяМетода);
    Сообщить("Параметры: " + Параметры);
    Сообщить("Экспортный: " + ?(ПризнакЭкспорт, "Да", "Нет"));
КонецЕсли;

Комплексный разбор модуля на процедуры, функции и комментарии

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

Рассмотрим пример шаблонов для лексического разбиения текста модуля:


idТекст = ".*";
idПР = "[ \t]*";
idСтрокаКомментария = "(?://" + idТекст + "$)";
idФункция = "функция([\S\s]*?)конецфункции";
idПроцедура = "процедура([\S\s]*?)конецпроцедуры";
idЛитералСтрока = "(""[^""]*""(?:""[^""]*"")*(?:""[^""]*\s*?|[^""]*(?:""[^""]*"")*"")*?)";
idКомментарий = "(" + idСтрокаКомментария + "(?:\n" + idПР + idСтрокаКомментария + ")*)";

// Объединенный шаблон разбора
idРазборМодуля = idКомментарий + "|" + idЛитералСтрока + "|" + idПроцедура + "|" + idФункция;

Перед анализом структуры процедур рекомендуется выполнить первичную очистку текста:

  1. Удалить однострочные комментарии (начинающиеся с //).
  2. Заменить строковые литералы на пустые заглушки, чтобы скобки внутри строк вида Строка = "Текст (в скобках)" не ломали подсчет параметров.
  3. Извлечь блоки между Процедура ... КонецПроцедуры и Функция ... КонецФункции.

Ограничения VBScript.RegExp и синтаксиса 1С

Проанализируем важные технические нюансы, с которыми можно столкнуться при использовании регулярных выражений для синтаксического анализа кода 1С:

  1. Привязка к Windows: объект VBScript.RegExp доступен исключительно в среде Windows. При выполнении кода на сервере под управлением Linux вызов Новый COMОбъект("VBScript.RegExp") вызовет исключение.
  2. Устаревший стандарт регулярных выражений: движок VBScript.RegExp реализует спецификацию ECMA-262 3rd Edition. В нем отсутствуют ретроспективные проверки (lookbehind), именованные группы захвата и поддержка классов Unicode.
  3. Асинхронные методы и директивы: современные модули 1С содержат директивы компиляции (&НаСервере, &НаКлиенте) и модификаторы асинхронности (Асинх Процедура / Async Procedure). Шаблон должен учитывать возможность наличия таких ключевых слов перед именем метода.
  4. Катастрофический бэктрекинг: применение сложных конструкций с ленивыми квантификаторами вида [\s\S]*? на больших текстах модулей (более нескольких мегабайт) может приводить к зависанию процесса или переполнению стека. Для полноценного анализа сложных графов вызовов предпочтительнее использовать специализированные парсеры (построение AST) или анализировать выгрузку конфигурации в файлы.
← На главную