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