Каталог решений - Как сделать простой парсер сложных запросов

Как сделать простой парсер сложных запросов

Как сделать простой парсер сложных запросов

В наличии

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

Категория:

Описание

Как сделать простой парсер сложных запросов.

Первым делом скажу, что опишу не столько парсер запросов, сколько анализатор круглых скобок (). Все очень просто, каждый вложенный запрос берется в круглые скобки, поэтому нам остается проверить на наличие внутри скобок ключевого слова «ВЫБРАТЬ».

Для поиска скобок и ключевого слова частично был задействован механизм регулярных выражений. Все вложенные запросы в Дереве значений добавляются дочерними узлами к ветви запроса. Имя ветви – это имя вложенного запроса. Все параметры запроса будут, для удобства, копироваться в ветви вложенных запросов.

Для распарсивания используется процедура РазобратьЗапрос(), описанная ниже. Обратная процедура СобратьЗапрос() работает следующим образом. В текущем запросе вложенные запросы заменяются запросами из дочерних ветвей дерева запросов. Замена происходит только на текущем уровне, без рекурсии в глубину или на вершину, поэтому сборка происходит последовательно, начиная с нижнего уровня. Это сделано умышленно, давая возможность на каждом уровне проверить после сборки правильность выполнения запроса. Основное требование перед сборкой вложенных запросов – наличие тех же возвращаемых переменных которые были при разборке.

Процедуры и функции я тщательно прокомментировал, так что трудностей с кодом не должно возникнуть. Данный механизм был использован в сторонних консолях запросов. http://forum.infostart.ru/forum24/topic20163/message861068/#message861068
и для управляемых форм http://forum.infostart.ru/forum24/topic61272/message934313/#message934313
В статье приводится код адаптированный для управляемых форм.

&НаКлиенте
Процедура РазобратьЗапрос(Команда)

    // строка запроса
    мТекущаяСтрока = ЭтаФорма.Элементы.ДеревоЗапросов.ТекущиеДанные;

    //сам текст запроса
    ПолныйТекстЗапроса = мТекущаяСтрока.ТекстЗапроса;

    //Формирование массива с адресами скобок и ключевого слова «ВЫБРАТЬ»
    Массив = ПозиционныйМассив(ПолныйТекстЗапроса);
    
    //формирование дочерних ветвей строки нашего запроса
    строки = мТекущаяСтрока.ПолучитьЭлементы();
    строки.Очистить();
        
    Для индекс = 0 по Массив.ВГраница() цикл
        если Массив[индекс][1] = «(» тогда
            индекс = СозданиеВетви(Массив, индекс, мТекущаяСтрока, ПолныйТекстЗапроса);    
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

&НаКлиенте
Процедура СобратьЗапрос(Команда)
    мТекущаяСтрока = ЭтаФорма.Элементы.ДеревоЗапросов.ТекущиеДанные;
    ПолныйТекстЗапроса = мТекущаяСтрока.ТекстЗапроса;
    
    //Формирование массива с с адресами скобок и ключевого слова «ВЫБРАТЬ»
    Массив = ПозиционныйМассив(ПолныйТекстЗапроса);
    
    строки = мТекущаяСтрока.ПолучитьЭлементы();
    РазмерМассива = строки.Количество();
    Если РазмерМассива > 0 тогда
        //формируем массив вложенных запросов
        Фрагмент = Новый Массив(РазмерМассива) ;  
        номерВетви = 0;
        Для каждого строка из строки цикл
            Фрагмент[номерВетви] = строка.ТекстЗапроса;    
            номерВетви = номерВетви + 1;
        КонецЦикла;
        
        //заменяем подзапрос в запросе на запрос из массива дочерних запросов
        номерВетви = 0;
        сдвиг = 0;
        длинаТекста = СтрДлина(ПолныйТекстЗапроса);
        Для индекс = 0 по Массив.ВГраница() цикл
            если Массив[индекс][1] = «(» тогда
                //поиск индекса соответствующей закрывающейся скобки
                индексВозврата = ВГлубину(Массив, индекс);                 
                
                //замена вложеного запроса
                ПолныйТекстЗапроса = Лев(ПолныйТекстЗапроса,Массив[индекс][0]+1+сдвиг) + Фрагмент[номерВетви] + Прав(ПолныйТекстЗапроса, длинаТекста-Массив[индексВозврата][0]);
                
                //вычисление смещения адресации
                сдвиг = сдвиг + СтрДлина(Фрагмент[номерВетви]) — (Массив[индексВозврата][0]-Массив[индекс][0]-1);
                
                номерВетви = номерВетви + 1;
                индекс = индексВозврата;    
            КонецЕсли;
        КонецЦикла;
        
        мТекущаяСтрока.ТекстЗапроса = ПолныйТекстЗапроса;
    КонецЕсли;
    
КонецПроцедуры

&НаКлиенте
Функция ПозиционныйМассив(ПолныйТекстЗапроса)
    //формирование массивов с адресами скобок и ключевого слова
    Открытие = МассивВхождений(ПолныйТекстЗапроса, «\(«);     
    Закрытие = МассивВхождений(ПолныйТекстЗапроса, «\)»);     
    Выбрать = МассивВхождений(ПолныйТекстЗапроса, «ВЫБРАТЬ»);
    
    //объединение трех массивов в один с сортировкой адресов по возрастанию    
    Массив = СложитьМассивы(Открытие, Закрытие);
    Массив = СложитьМассивы(Массив, Выбрать);

    //удаление информации о не значимых скобках
    УбратьВсеДоСкобки(Массив);
    удалено = 1;
    Пока удалено > 0 цикл
        удалено = УбратьЛишниеСкобки(Массив);
    КонецЦикла;
    
    Возврат Массив;

КонецФункции // ПозиционныйМассив()

&НаКлиенте
Функция МассивВхождений(текст, подстрока, предел = Неопределено)
     //для поиска используем регулярные выражения
RegExp = Новый COMОбъект(«VBScript.RegExp»);
    RegExp.IgnoreCase = Истина; //Игнорировать регистр
    RegExp.Global = Истина; //Поиск всех вхождений шаблона
    RegExp.MultiLine = Истина; //Многострочный режим
    
    RegExp.Pattern = подстрока;
    Matches=RegExp.Execute(текст);
    ЧислоВхождений=Matches.Count();

    //определяемся с длиной массива вхождений
    если предел = Неопределено тогда
        предел = ЧислоВхождений;
    иначеесли ЧислоВхождений < предел тогда
        предел = ЧислоВхождений;
    КонецЕсли;

    Если предел > 0 тогда
        //формирование массива с адресами вхождений подстроки
        Массив = Новый Массив(предел,2);     
        Для СубСчетчик = 0 По предел-1 Цикл
            SubMatch = Matches.Item(СубСчетчик);
            Массив[СубСчетчик][0] = SubMatch.FirstIndex;    //адрес
            Массив[СубСчетчик][1] = SubMatch.Value;    //значение
        КонецЦикла;
    иначе
        Массив = Неопределено;
    КонецЕсли;
    
    Возврат Массив;
КонецФункции // МассивВхождений()

// объединение массивов с сортировкой по возрастанию
&НаКлиенте
Функция СложитьМассивы(Массив1, Массив2)
    Если Массив1 = Неопределено тогда
        ВГраница1 = -1;
    иначе
        ВГраница1 = Массив1.ВГраница();
    КонецЕсли;
    
    Если Массив2 = Неопределено тогда
        ВГраница2 = -1;
    иначе
        ВГраница2 = Массив2.ВГраница();
    КонецЕсли;
    
    РазмерМассива = ВГраница1+ВГраница2+2;
    Если РазмерМассива > 0 тогда
        Массив = Новый Массив(ВГраница1+ВГраница2+2,2);
        индекс1 = 0;
        индекс2 = 0;
        Для индекс = 0 по Массив.ВГраница() цикл
            если индекс1 > ВГраница1 тогда
                Массив[индекс][0] = Массив2[индекс2][0];
                Массив[индекс][1] = Массив2[индекс2][1];
                индекс2 = индекс2+1;
            иначеесли индекс2 > ВГраница2 тогда
                Массив[индекс][0] = Массив1[индекс1][0];
                Массив[индекс][1] = Массив1[индекс1][1];
                индекс1 = индекс1+1;
            иначеесли Массив1[индекс1][0] < Массив2[индекс2][0] тогда
                Массив[индекс][0] = Массив1[индекс1][0];
                Массив[индекс][1] = Массив1[индекс1][1];
                индекс1 = индекс1+1;
            иначе
                Массив[индекс][0] = Массив2[индекс2][0];
                Массив[индекс][1] = Массив2[индекс2][1];
                индекс2 = индекс2+1;
            КонецЕсли;
            
        КонецЦикла;
        Возврат Массив;
    иначе
        Возврат Неопределено;
    КонецЕсли;
КонецФункции // СложитьМассивы()

//убираем все до первой открывающейся скобки
&НаКлиенте
Функция УбратьВсеДоСкобки(Массив)
        индекс = 0;
    удалено = 0;
    
    Пока индекс         если Массив[индекс][1] = «(» тогда
            Прервать;    
        иначе
            Массив.Удалить(индекс);
            удалено = удалено + 1;
        КонецЕсли;
    КонецЦикла;

    Возврат удалено;    
КонецФункции // УбратьВсеДоСкобки()

// удаление пар скобок не содержащих ключевого слова «ВЫБРАТЬ»
&НаКлиенте
Функция УбратьЛишниеСкобки(Массив)
        индекс = 0;
    удалено = 0;
    
    Пока индекс         если Массив[индекс][1] = «(» и Массив[индекс+1][1] = «)» тогда
            Массив.Удалить(индекс);
            Массив.Удалить(индекс);
            удалено = удалено + 1;
        иначе
            индекс = индекс + 1;
        КонецЕсли;
    КонецЦикла;

    Возврат удалено;    
КонецФункции // УбратьЛишниеСкобки()

// рекурсивная функция для создания ветвей вложенных запросов
&НаКлиенте
Функция СозданиеВетви(Массив, ВходящийИндекс, Родитель, ПолныйТекстЗапроса)
    Ветви = Родитель.ПолучитьЭлементы();
    мТекущаяСтрока = Ветви.Добавить();                 //ветвь подзапроса
    СкопироватьПараметры(Родитель, мТекущаяСтрока); //копирование параметров запроса
    
    Для индекс = ВходящийИндекс+1 по Массив.ВГраница() цикл
        если Массив[индекс][1] = «(» тогда
            //если у текущего запроса есть вложеный запрос, то делаем рекурсивно дочернюю ветвь
            индекс = СозданиеВетви(Массив, индекс, мТекущаяСтрока, ПолныйТекстЗапроса);    
        иначеесли Массив[индекс][1] = «)» тогда
            //встречен конец текущего запроса
            Прервать;
        КонецЕсли;
    КонецЦикла;

    запрос = Сред(ПолныйТекстЗапроса, Массив[ВходящийИндекс][0]+2, Массив[индекс][0]-Массив[ВходящийИндекс][0]-1);

    //формирование имени подзапроса
    если индекс < Массив.ВГраница() тогда
        //фрагмент между закрывающейся и следующей скобкой
        строка = Сред(ПолныйТекстЗапроса, Массив[индекс][0]+1, Массив[индекс+1][0]-Массив[индекс][0]);
    иначе
        //фрагмент запроса за закрывающейся скобкой
        строка = Сред(ПолныйТекстЗапроса, Массив[индекс][0]+1, СтрДлина(ПолныйТекстЗапроса)-Массив[индекс][0]);
    КонецЕсли;
    
    имя = НайтиИмя(строка);
    
    если имя = «БезИмени» тогда
        имя = имя+индекс;
    КонецЕсли;
    
    мТекущаяСтрока.ИмяЗапроса = имя;                 
    мТекущаяСтрока.ТекстЗапроса = запрос;
    Возврат индекс;    
КонецФункции // СозданиеВетви()

&НаКлиенте
Процедура СкопироватьПараметры(Родитель, мТекущаяСтрока)
    мТекущаяСтрока.ПараметрыЗапроса.Очистить();
    Для каждого парам из Родитель.ПараметрыЗапроса цикл
        новыйПарам = мТекущаяСтрока.ПараметрыЗапроса.Добавить();
        новыйПарам.ИмяПараметра = парам.ИмяПараметра;
        новыйПарам.СпособУстановки = парам.СпособУстановки;
        новыйПарам.ЗначениеПараметра = парам.ЗначениеПараметра;
        новыйПарам.ТипЗначения = парам.ТипЗначения;
    КонецЦикла;
КонецПроцедуры

//вычисление имени подзапроса по фрагменту за закрывающейся скобкой
&НаКлиенте
Функция НайтиИмя(текст)
    если СтрДлина(СокрЛП(текст)) > 0 тогда
        //анализ связки пробел-неПробел до 3 элементов
        //идея следующая
        //если 0-й адрес за закрывающейся скобкой ключевое слово «КАК»
        //значит имя между 1-ым и 2-ым адресами
        Массив = МассивВхождений(текст, «\s\S», 3);
        если Массив = Неопределено тогда
            имя = «БезИмени»;
        иначеесли Массив.ВГраница() = 2 тогда
            как = ВРег(СокрЛП(Сред(текст, Массив[0][0]+1, Массив[1][0]-Массив[0][0])));
            если как = «КАК» тогда
                имя = СокрЛП(Сред(текст, Массив[1][0]+2, Массив[2][0]-Массив[1][0]-1));
            иначе
                имя = «БезИмени»;
            КонецЕсли;    
        иначеесли Массив.ВГраница() = 1 тогда
            имя = СокрЛП(Сред(текст, Массив[1][0]+2, СтрДлина(текст)-Массив[1][0]-1));
        иначе
            имя = «БезИмени»;
        КонецЕсли;
    иначе
        имя = «БезИмени»;
    КонецЕсли;
    
    Возврат имя;
КонецФункции // НайтиИмя()

//рекурсивная функция для поиска закрывающейся скобки, соответствующей скобке и индексом «ВходящийИндекс»
&НаКлиенте
Функция ВГлубину(Массив, ВходящийИндекс)
    Для индекс = ВходящийИндекс+1 по Массив.ВГраница() цикл
        если Массив[индекс][1] = «(» тогда
            индекс = ВГлубину(Массив, индекс);    
        иначеесли Массив[индекс][1] = «)» тогда
            Прервать;
        КонецЕсли;
    КонецЦикла;
    
    Возврат индекс;
КонецФункции // ВГлубину()

has been added to your cart:
Оформление заказа