Разбор кода 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"); КонецПроцедуры &НаКлиенте Процедура ТестСтроковыхФункций(Команда) ТестСтроковыхФункцийНаСервере(); КонецПроцедуры