Разбор кода 1С
Ниже приведен пример разбора кода 1С путем выделения из него строковых литералов (строки в двойных кавычках или начинающиеся |) и комментариев. Код использует новые строковые функции 1С:Предприятие (СтрНайти и др.) и был проверен в 1С:Предприятие 8.3 (8.3.6.2299). Пример на этот момент не имеет боевого применения, но содержит автотесты.
Функция РазборКода1С(ИсходнаяСтрокаКода1С, Комментарий)
//Разбирает строку с кодом 1С на код, строковые литералы (строки "в двойных кавычках" или с использованием символа | в начале строки) и комментарии (от // до конца строки).
//Возвращает массив, в котором чередуются код (четные элементы массива 0, 2, 4...) и строковые литералы (нечетные элементы массива 1, 3, 5...). Комментарий возвращает во втором параметре функции.
//Под кодом (там могут быть операторы, переменные, ключевые слова языка программирования и так далее) понимается всё, что не является текстовыми строками и при этом не является комментариями.
//Принимает на входе строку для разбора ИсходнаяСтрокаКода1С. Это строка в рамках текстового файла, то есть, не должно быть в ее середине переводов строки.
//Склеивание элементов массива и комментария без зазоров и добавления каких-либо символов всегда составляет исходную строку ИсходнаяСтрокаКода1С.
//См. тесты ТестРазборСтроки1С с примерами входных и возвращаемых значений.
Комментарий="";
стр=ИсходнаяСтрокаКода1С;
мРезультат=Новый Массив;
пДлина=СтрДлина(стр);
Если пДлина=0 Тогда
//Строка нулевой длины - возвращаем пустой массив.
Возврат мРезультат;
КонецЕсли;
пТруба=СтрНайти(стр, "|");
Если пТруба>0 и Не ПустаяСтрока(Лев(стр, пТруба-1)) Тогда
//Есть не пробельные символы до трубы - тогда труба не считается за начало строки.
пТруба=0;
КонецЕсли;
пКомментарий=СтрНайти(стр, "//");
пКавычка=СтрНайти(стр, """");
пБлижайшееЗначение=МеньшееНеНулевое(пТруба, пКомментарий, пКавычка);
пТекущаяПозиция=1;
Если пБлижайшееЗначение=Неопределено Тогда
мРезультат.Добавить(стр);
Возврат мРезультат;
ИначеЕсли пКомментарий=пБлижайшееЗначение Тогда
мРезультат.Добавить(Лев(стр, пКомментарий-1));
Комментарий = Сред(стр, пКомментарий);
Возврат мРезультат;
ИначеЕсли пТруба=пБлижайшееЗначение Тогда
мРезультат.Добавить(Лев(стр, пТруба-1)); // символы до трубы или пустая строка
// Ищем завершающую кавычку, начиная с позиции трубы
пКавычка2=СтрНайти(стр, """",,пТруба); //Закрывающая кавычка
Если пКавычка2=0 Тогда
мРезультат.Добавить(Сред(стр, пТруба)); // все символы после трубы включительно
Возврат мРезультат;
Иначе
Литерал=Сред(стр, пТруба, пКавычка2-пТруба+1);
мРезультат.Добавить(Литерал);
пТекущаяПозиция=пКавычка2+1;
КонецЕсли;
ИначеЕсли пКавычка=пБлижайшееЗначение Тогда
Если пТекущаяПозиция=пКавычка Тогда
мРезультат.Добавить("");
Иначе
мРезультат.Добавить(Лев(стр, пКавычка-пТекущаяПозиция)); // символы до кавычки или пустая строка
КонецЕсли;
// Ищем завершающую кавычку, начиная с позиции
пКавычка2=СтрНайти(стр, """",,пКавычка+1); //Закрывающая кавычка
Если пКавычка2=0 Тогда
мРезультат.Добавить(Сред(стр, пКавычка)); // все символы после кавычки включительно
Возврат мРезультат;
Иначе
Литерал=Сред(стр, пКавычка, пКавычка2-пКавычка+1);
мРезультат.Добавить(Литерал);
пТекущаяПозиция=пКавычка2+1;
КонецЕсли;
Иначе
ВызватьИсключение "Неправильное значение позиции разбора 1.";
КонецЕсли;
Пока пТекущаяПозиция<=пДлина Цикл
пКомментарий=СтрНайти(стр, "//",, пТекущаяПозиция);
пКавычка=СтрНайти(стр, """",, пТекущаяПозиция);
пБлижайшееЗначение=МеньшееНеНулевое(пКомментарий, пКавычка);;
Если пБлижайшееЗначение=Неопределено Тогда
Код=Сред(стр, пТекущаяПозиция);
Если Код<>"" Тогда
мРезультат.Добавить(); // Больше нет кавычек или комментариев - добавляем все что есть в качестве кода в массив.
КонецЕсли;
Возврат мРезультат;
ИначеЕсли пКомментарий=пБлижайшееЗначение Тогда
Если пТекущаяПозиция < пКомментарий Тогда
мРезультат.Добавить(Сред(стр, пТекущаяПозиция, пКомментарий-пТекущаяПозиция)); //остаток текста до комментария
КонецЕсли;
Комментарий=Сред(стр, пКомментарий);
Возврат мРезультат;
ИначеЕсли пКавычка=пБлижайшееЗначение Тогда
мРезультат.Добавить(Сред(стр, пТекущаяПозиция, пКавычка-пТекущаяПозиция)); //остаток текста до строки
Если пКавычка=пДлина Тогда
пКавычка2=пКавычка;
Иначе
пКавычка2=СтрНайти(стр, """", , пКавычка+1);
КонецЕсли;
Литерал = Сред(стр, пКавычка, пКавычка2-пКавычка+1);
мРезультат.Добавить(Литерал);
пТекущаяПозиция=пКавычка2+1;
Иначе
ВызватьИсключение "Неправильное значение позиции разбора 2.";
КонецЕсли;
КонецЦикла;
Возврат мРезультат;
КонецФункции
Функция СклеиваниеИсходной(мТокены, Комментарий)
стр="";
Для Каждого зн из мТокены Цикл
стр=стр+зн;
КонецЦикла;
стр=стр+Комментарий;
Возврат стр;
КонецФункции
Функция ТестРазборКода1С()
Комментарий="";
стр="";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(мТокены.Количество()=0, "тест 0.1");
Ожидается(Комментарий="", "тест 0.2");
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 0.3");
стр="ааа//ббб";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 1.0");
Ожидается(мТокены.Количество()=1, "тест 1.1");
Ожидается(мТокены[0]="ааа", "тест 1.2");
Ожидается(Комментарий="//ббб", "тест 1.3");
стр="//ббб";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 2.0");
Ожидается(мТокены.Количество()=1, "тест 2.1");
Ожидается(мТокены[0]="", "тест 2.2");
Ожидается(Комментарий="//ббб", "тест 2.3");
стр="ааа";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 3.0");
Ожидается(мТокены.Количество()=1, "тест 3.1");
Ожидается(мТокены[0]="ааа", "тест 3.2");
Ожидается(Комментарий="", "тест 3.3");
//Кавычка после признака комментария - за строковый литерал не считается
стр="ааа//ббб""";
Комментарий="";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 4.0");
Ожидается(мТокены.Количество()=1, "тест 4.1");
Ожидается(мТокены[0]="ааа", "тест 4.2");
Ожидается(Комментарий="//ббб""", "тест 4.3");
//Комментарий в кавычках
стр="""ааа//ббб""";
Комментарий="";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 5.0");
Ожидается(мТокены.Количество()=2, "тест 5.1");
Ожидается(мТокены[0]="", "тест 5.2");
Ожидается(мТокены[1]="""ааа//ббб""", "тест 5.3");
Ожидается(Комментарий="", "тест 5.4");
//Комментарий после трубы
стр="|ааа//ббб";
Комментарий="";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 6.0");
Ожидается(мТокены.Количество()=2, "тест 6.1");
Ожидается(мТокены[0]="", "тест 6.2");
Ожидается(мТокены[1]="|ааа//ббб", "тест 6.3");
Ожидается(Комментарий="", "тест 6.4");
//два литерала
стр="""ааа""+""ббб""//ввв";
Комментарий="";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 7.0");
Ожидается(мТокены.Количество()=4, "тест 7.1");
Ожидается(мТокены[0]="", "тест 7.2");
Ожидается(мТокены[1]="""ааа""", "тест 7.3");
Ожидается(мТокены[2]="+", "тест 7.4");
Ожидается(мТокены[3]="""ббб""", "тест 7.5");
Ожидается(Комментарий="//ввв", "тест 7.6");
//труба и литерал
стр=" |ааа"" + ""ббб"" //ввв";
Комментарий="";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 8.0");
Ожидается(мТокены.Количество()=5, "тест 8.1");
Ожидается(мТокены[0]=" ", "тест 8.2");
Ожидается(мТокены[1]="|ааа""", "тест 8.3");
Ожидается(мТокены[2]=" + ", "тест 8.4");
Ожидается(мТокены[3]="""ббб""", "тест 8.5");
Ожидается(мТокены[4]=" ", "тест 8.6");
Ожидается(Комментарий="//ввв", "тест 8.7");
//труба и литерал - признаки комментария внутри литералов, признак строки внутри комментариев
стр=" |а//аа"" + ""бб//б"" //вв""в";
Комментарий="";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 9.0");
Ожидается(мТокены.Количество()=5, "тест 9.1");
Ожидается(мТокены[0]=" ", "тест 9.2");
Ожидается(мТокены[1]="|а//аа""", "тест 9.3");
Ожидается(мТокены[2]=" + ", "тест 9.4");
Ожидается(мТокены[3]="""бб//б""", "тест 9.5");
Ожидается(мТокены[4]=" ", "тест 9.6");
Ожидается(Комментарий="//вв""в", "тест 9.7");
//три литерала
стр=" ""а//аа"" + ""бб//б"" + ""ввв""";
Комментарий="";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 10.0");
Ожидается(мТокены.Количество()=6, "тест 10.1");
Ожидается(мТокены[0]=" ", "тест 10.2");
Ожидается(мТокены[1]="""а//аа""", "тест 10.3");
Ожидается(мТокены[2]=" + ", "тест 10.4");
Ожидается(мТокены[3]="""бб//б""", "тест 10.5");
Ожидается(мТокены[4]=" + ", "тест 10.6");
Ожидается(мТокены[5]="""ввв""", "тест 10.7");
Ожидается(Комментарий="", "тест 10.8");
//удвоенная кавычка
стр=" ""ааа""""ббб"" //ввв";
Комментарий="";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 11.0");
Ожидается(мТокены.Количество()=5, "тест 11.1");
Ожидается(мТокены[0]=" ", "тест 11.2");
Ожидается(мТокены[1]="""ааа""", "тест 11.3");
Ожидается(мТокены[2]="", "тест 11.4");
Ожидается(мТокены[3]="""ббб""", "тест 11.5");
Ожидается(мТокены[4]=" ", "тест 11.6");
Ожидается(Комментарий="//ввв", "тест 11.7");
//незавершенная строка
стр=" ""ааа//ббб//ввв";
Комментарий="";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 12.0");
Ожидается(мТокены.Количество()=2, "тест 12.1");
Ожидается(мТокены[0]=" ", "тест 12.2");
Ожидается(мТокены[1]="""ааа//ббб//ввв", "тест 12.3");
//Комментарий
стр="//";
Комментарий="";
мТокены=РазборКода1С(стр, Комментарий);
Ожидается(СклеиваниеИсходной(мТокены, Комментарий)=стр, "тест 13.0");
Ожидается(мТокены.Количество()=1, "тест 13.1");
Ожидается(мТокены[0]="", "тест 13.2");
Ожидается(Комментарий="//", "тест 13.3");
КонецФункции
Функция Ожидается(прм_Результат, тест)
Если прм_Результат=Истина Тогда
Сообщить("Пройден "+тест);
Иначе
ВызватьИсключение("Провален "+тест);
КонецЕсли;
КонецФункции
Функция МеньшееНеНулевое(п1, п2=0, п3=0)
//Возвращает наименьшее ненулевое число из трех чисел.
//Если таковых нет, возвращает Неопределено.
пМеньшееНеНулевое=Неопределено;
Если п1>0 Тогда
пМеньшееНеНулевое=п1;
КонецЕсли;
Если пМеньшееНеНулевое=Неопределено Тогда
Если п2>0 Тогда
пМеньшееНеНулевое=п2;
КонецЕсли;
Иначе
Если п2>0 и п2<пМеньшееНеНулевое Тогда
пМеньшееНеНулевое=п2;
КонецЕсли;
КонецЕсли;
Если пМеньшееНеНулевое=Неопределено Тогда
Если п3>0 Тогда
пМеньшееНеНулевое=п3;
КонецЕсли;
Иначе
Если п3>0 и п3<пМеньшееНеНулевое Тогда
пМеньшееНеНулевое=п3;
КонецЕсли;
КонецЕсли;
Возврат пМеньшееНеНулевое;
КонецФункции
&НаКлиенте
Процедура КомандаТест(Команда)
ТестРазборКода1С();
КонецПроцедуры
&НаСервере
Процедура ТестСтроковыхФункцийНаСервере()
стр="ааа * ббб * ввв";
поз=СтрНайти(стр, "*",, 1); // 0 = недопустимое значение функции
Ожидается(поз=5, "тест 1.1");
поз=СтрНайти(стр, "*",, 4);
Ожидается(поз=5, "тест 1.2");
поз=СтрНайти(стр, "*",, 5);
Ожидается(поз=5, "тест 1.3");
поз=СтрНайти(стр, "*",, 6);
Ожидается(поз=11, "тест 1.4");
поз=СтрНайти(стр, "*",, 12);
Ожидается(поз=0, "тест 1.5");
поз=СтрНайти(стр, "*",, 15); // 16 = недопустимое значение функции
Ожидается(поз=0, "тест 1.6");
дл=СтрДлина(стр);
Ожидается(дл=15, "тест 2.1");
чч=СтрЧислоВхождений(стр, "*");
Ожидается(чч=2, "тест 2.2");
мСтроки=СтрРазделить(стр, "*", Истина);
Ожидается(мСтроки.Количество()=3, "тест 2.3");
Ожидается(мСтроки[0]="ааа ", "тест 2.4");
Ожидается(мСтроки[1]=" ббб ", "тест 2.5");
Ожидается(мСтроки[2]=" ввв", "тест 2.6");
мСтроки=СтрРазделить(стр, "#", Истина);
Ожидается(мСтроки.Количество()=1, "тест 2.7");
Ожидается(мСтроки[0]="ааа * ббб * ввв", "тест 2.8");
лл=Лев(стр, 0);
Ожидается(лл="", "тест 3.1");
КонецПроцедуры
&НаКлиенте
Процедура ТестСтроковыхФункций(Команда)
ТестСтроковыхФункцийНаСервере();
КонецПроцедуры
