Язык программирования Перфо (Исходный код) — различия между версиями

Материал из ТХАБ.РФ
Перейти к: навигация, поиск
м
 
(нет различий)

Текущая версия на 21:04, 5 августа 2020

// интерпретатор языка программирования Перфо
// русскоязычный вариант языка похожего на Scheme
// однако, унифицированный с языком Перфолента
/*
 Описание языка:  
 - есть 3 типа сущностей  Идентификаторы, Значения и Списки
 - значениями могут быть простые типы: строки, символы, числа, даты, булево и неопределено, 
   а так же созданные во время выполнения объекты и делегаты действий
 - синтаксис значений такой же, как в языке Перфолента: 
   "строка" - строка, "С"с - символ, 4.537 - число, '23.07.2020' - дата, Истина - булево, Неопределено    
 - любой другой набор символов не содержащий пробельных символов является идентификатором: А1, $SYM, !!!Ошибка!!!
 - списки состоят из любых сущностей разделенных пробелами и заключенных в круглые скобки: (+ 3 4 5)
 - действие это список, первым элементом которого является идентификатор, а остальные элементы являются параметрами действия
 (ИдентификаторДействия Параметр1 Параметр2 ... ПараметрН), например, (мин 34 9 100)
 - идентификатор может быть вычисляемым ((Идент "ИдентификаторДействия") Параметр1 Параметр2 ... ПараметрН)
 - параметр действия может быть любой сущностью или последовательностью
 - последовательность это список состоящий из любых сущностей, у которого первый элемент НЕ идентификатор
 (Значение1 Значение2 Значение3) или ((Действие1) (Действие2) (Действие3)) или ((Действие1) (Действие2) Значение1)
 - оператор это специальная форма действия, в которой смысл и назначение параметров, 
   а так же их последовательность выполнения определены стандартом языка,
   например, (Если (Условие) (СписокДействий1) (СписокДействий2))
   или (Перем ИмяПеременной Значение)
 Возвращаемые значения:
 - значение возвращает само себя
 - идентификатор возвращает сопоставленное ему значение, в том числе, делегат действия, идентификатор или неопределено
 - действие может вернуть значение, делегат действия, идентификатор или неопределено
 - оператор возвращает то, что определено стандартом языка
 - последовательность возвращает результат последнего элемента
 
*/
#ТипСборки КонсольноеПриложение
#ИспользоватьСтандартнуюБиблиотеку
ИмпортИмён Промкод.Перфолента.Консоль
//***************************
&ВидноВсем
Программа Перфо
   
   //стандартное окружение хранит значения и действий
   //заданные стандартом языка
   Поле СтандартноеОкружение тип клОкружение = ЗаполнитьСтандартноеОкружение()
   
   //глобальное окружение хранит значения переменных и определения действий
   //в глобальном контексте выполнения программы
   Поле ГлобОкружение тип клОкружение = Новый клОкружение(СтандартноеОкружение)
   
   //оператор (Отладка Истина) включает вывод сообщений об ошибках в консоль
   //при выполнении скрипта запущенного из командной строки
   //если отладка выключена, то консоль просто закроется и программа вернет код ошибки окружению ОС
   Поле Отладка тип Булево = Ложь 
   
   //используем функцию Старт, а не процедуру, что бы вернуть операционной системе код возврата
   //---------------------------
   Функция Старт() тип Целое
   
       Если ЭтаПрограмма.КоличествоАргументовКоманднойСтроки > 0
           
           // выполняем скрипт из файла
           
           Файл = СокрЛП(ЭтаПрограмма.ПолучитьАргументКоманднойСтроки(0))
           Если ФС.ФайлСуществует(Файл) 
               Попытка
                   ТекстПрограммы = ФС.ПрочитатьТекст(Файл)    
                   Результат = Выполнить(Компилировать(ТекстПрограммы))
               Исключение Ош
                   Если Отладка
                       ВыводСтроки "Ошибка выполнения программы (код 1): "+Ош.ОписаниеОшибки 
                       Пауза
                   КонецЕсли    
                   Возврат 1 //ошибка при выполнении программы
               КонецПопытки
           Иначе
               Если Отладка
                   ВыводСтроки "Ошибка (код 2): Не найден файл программы "+Файл 
                   Пауза
               КонецЕсли    
               Возврат 2 //ошибка: файл программы не найден
           КонецЕсли
           Если Отладка
               ПереносСтроки
               Пауза
           КонецЕсли    
           Возврат 0 //ошибок нет
           
       Иначе
       
           // интерактивная работа
       
           ВыводПС("Перфо - простой интерпретатор русского языка похожего на Scheme")
           Цикл
               ПереносСтроки
               Вывод("Перфо> ")
               ТекстПрограммы = ПрочитатьСтроку()
               Попытка
                   Результат = Выполнить(Компилировать(ТекстПрограммы))
                   Если Результат ЭтоНе Неопределено
                       Вывод(РезультатВСтроку(Результат))
                   КонецЕсли
               Исключение Ош
                   ВыводПС("Ошибка: "+Ош.ОписаниеОшибки)
               КонецПопытки
           КонецЦикла
           
       КонецЕсли
       
   КонецФункции
  
   //запускает на выполнение текст программы с установкой переменной ЭтотОбъект
   //используется для запуска скрипта из других программ
   //---------------------------
   &ВидноВсем
   Функция ВыполнитьПрограмму(ТекстПрограммы тип Строка, ЗначениеПеременнойЭтотОбъект тип Объект = Неопределено) тип Строка 
       Попытка
           ГлобОкружение.ДобавитьДействие("этотобъект", ЗначениеПеременнойЭтотОбъект)
           Результат = Выполнить(Компилировать(ТекстПрограммы))
           Возврат РезультатВСтроку(Результат)
       Исключение Ош
           Если Отладка
               Возврат "Ошибка: "+Ош.ОписаниеОшибки 
           КонецЕсли    
           Возврат Неопределено
       КонецПопытки
   КонецФункции 

#Область ФункцииКомпилятора
   //запускает на выполнение откомпилированную программу
   //---------------------------
   Функция Выполнить(СписокВыражений тип клСписок) тип Объект
       Результат = Неопределено
       Для Инд=0 По СписокВыражений.Количество-1
           Результат = Вычислить(СписокВыражений.ПоИндексу(Инд),ГлобОкружение)    
       КонецЦикла
       Возврат Результат
   КонецФункции    

   //---------------------------
   Функция Компилировать(ТекстПрограммы тип Строка) тип Объект
       ПредставлениеПрограммы = Новый клСписок
       Спис = РазобратьНаТокены(ТекстПрограммы)
       Пока Спис.Количество > 0
           ПредставлениеПрограммы.ВКонец(ОбработатьТокены(Спис))
       КонецЦикла
       Возврат ПредставлениеПрограммы
   КонецФункции	
   
   //---------------------------
   Функция РазобратьНаТокены(ТекстПрограммы тип Строка) тип клСписок
       Пт = Новый ПостроительТекста
       //выделим строки, символы и даты перед тем, как разделять по пробелам
       //т.к. в них бывают пробелы, табуляции и т.д...
       ДлинаТекста=СтрДлина(ТекстПрограммы)
       Если ДлинаТекста<2
           Пт{ТекстПрограммы}
       Иначе
           масСтроки=Новый Массив
           Символ14=""+Символ(14)
           МаксЦелое=2147483647
           Поз1=1
           Цикл
               Поз2=СтрНайти(ТекстПрограммы,"""",,Поз1)
               Поз3=СтрНайти(ТекстПрограммы,"'",,Поз1)
               Если Поз2=0 и Поз3=0
                   Пт{Сред(ТекстПрограммы,Поз1)}
                   Прервать
               КонецЕсли
               Поз2=?(Поз2=0,МаксЦелое,Поз2)
               Поз3=?(Поз3=0,МаксЦелое,Поз3)
               Если Поз2 < Поз3 
                   //нашли "
                   Пт{Сред(ТекстПрограммы,Поз1,Поз2-Поз1)}
                   Поз1=Поз2
                   Поз2=СтрНайти(ТекстПрограммы,"""",,Поз1+1)
~ПроверкаВторойКавычки:
                   Если Поз2=0
                       ВызватьИсключение "Строка не закрыта двойной кавычкой."
                   Иначе
                       Если ДлинаТекста > Поз2
                           Симв=Сред(ТекстПрограммы,Поз2+1,1)
                           Если НРег(Симв)="с" или НРег(Симв)="c" //рус и англ
                               //берем знак символа
                               Поз2++
                           ИначеЕсли Симв=""""
                               //берем продолжение строки
                               Поз2=СтрНайти(ТекстПрограммы,"""",,Поз2+2)
                               Перейти ~ПроверкаВторойКавычки:
                           ИначеЕсли Симв=")"
                               //ничего делать не надо
                           ИначеЕсли Символы.ЭтоПробельный(Симв)
                               //ничего делать не надо
                           Иначе
                               ВызватьИсключение "Не известный символ после строки."
                           КонецЕсли
                       КонецЕсли
                       Пт{Символ14+СокрЛП(масСтроки.Количество)}
                       масСтроки.Добавить(Сред(ТекстПрограммы,Поз1,Поз2-Поз1+1))
                       Поз1=Поз2+1
                   КонецЕсли
               Иначе
                   //нашли '
                   Пт{Сред(ТекстПрограммы,Поз1,Поз3-Поз1)}
                   Поз1=Поз3
                   Поз3=СтрНайти(ТекстПрограммы,"'",,Поз1+1)
                   Если Поз3=0
                       ВызватьИсключение "Дата не закрыта одинарной кавычкой"
                   Иначе
                       Если ДлинаТекста > Поз2
                           Симв=Сред(ТекстПрограммы,Поз2+1,1)
                           Если Симв=")"
                               //ничего делать не надо
                           ИначеЕсли Символы.ЭтоПробельный(Симв)
                               //ничего делать не надо
                           Иначе
                               ВызватьИсключение "Не известный символ после даты."
                           КонецЕсли
                       КонецЕсли
                       Пт{Символ14+СокрЛП(масСтроки.Количество)}
                       масСтроки.Добавить(Сред(ТекстПрограммы,Поз1,Поз3-Поз1+1))
                       Поз1=Поз3+1
                   КонецЕсли
               КонецЕсли
           КонецЦикла
       КонецЕсли
       ТекстПрограммы=Пт.ВСтроку
       //уберем комментарии и директивы препрцессора
       Пт = Новый ПостроительТекста
       Токены=ТекстПрограммы.Разделить({Символы.ВК,Символы.ПС},Ложь)
       Для Инд=0 По Токены.Количество-1
           Поз=Найти(Токены[Инд],"#") 
           Если Поз<>1
               Поз=Найти(Токены[Инд],"//") 
               Если Поз>0
                   Пт{Лев(Токены[Инд],Поз-1)}
               Иначе
                   Пт{Токены[Инд]}
               КонецЕсли
           КонецЕсли
       КонецЦикла
       
       //разделим на токены
       Токены=Пт.ВСтроку.Заменить("("," ( ").Заменить(")"," ) ").Разделить({" ",Символы.НПП,Символы.Таб,Символы.ВК,Символы.ПС},Ложь)
       Для Инд=0 По Токены.Количество-1
           Если Лев(Токены[Инд],1)=Символ14
               Токены[Инд]=масСтроки[Целое(Сред(Токены[Инд],2))]
           КонецЕсли
       КонецЦикла
       Возврат Новый клСписок(Токены)    
   КонецФункции	
   //---------------------------
   Функция ОбработатьТокены(Токены тип клСписок) тип Объект
       Если Токены.Количество=0 
           ВызватьИсключение "Неожиданный конец программы."
       КонецЕсли
       Токен = Токены.Извлечь(0)
       Если "(" = Токен
           Спис = Новый клСписок
           Цикл 
               Если Токены.Количество=0 
                   ВызватьИсключение "Ожидается символ )."
               КонецЕсли
               Прервать Если Токены.ПоИндексу(0) = ")"
               Спис.ВКонец(ОбработатьТокены(Токены))
           КонецЦикла
           Токены.Извлечь(0) //убираем )
           Возврат Спис
       ИначеЕсли ")" = Токен
           ВызватьИсключение "Неожиданный символ )."
       Иначе
           Возврат Атом(Токен)
       КонецЕсли
   КонецФункции	
   //---------------------------
   // Распознаёт неопределено, числа, строки, символы, булево, даты и идентификаторы
   Функция Атом(Токен тип Строка) тип Объект 
       Если НРег(Токен)="неопределено"
           //это Неопределено
           Возврат Неопределено
       КонецЕсли    
       Если Токен.НачинаетсяС("""") и Токен.ЗаканчиваетсяНа("""")
           //это строка
           Возврат Токен.Сред(2,СтрДлина(Токен)-2)
       КонецЕсли    
       Если СтрДлина(Токен)=4 и Токен.НачинаетсяС("""") и (Токен.ЗаканчиваетсяНа("""с") или Токен.ЗаканчиваетсяНа("""c")) //рус и англ
           //это символ
           Возврат Символ(Токен.Сред(2,1))
       КонецЕсли    
       Если Токен.НачинаетсяС("'") и Токен.ЗаканчиваетсяНа("'")
           Попытка
              //это дата
              Возврат Дата(Токен.Сред(2,СтрДлина(Токен)-2))
           КонецПопытки
       КонецЕсли    
       Бул=Ложь
       Чис=0ч
       //числа записываются через точку, но и через запятую сработает
       Если РаспознатьЧисло(СтрЗаменить(Токен,".",Сред(Строка(1.1),2,1)),Чис)
           //это число
           Возврат Чис
       ИначеЕсли РаспознатьБулево(Токен,Бул)
           //это булево
           Возврат Бул
       КонецЕсли
       //это какой-то идентификатор
       Возврат Новый клИдентификатор(Токен)
   КонецФункции	
   //---------------------------
   Функция РезультатВСтроку(Результат тип Объект) тип Строка 
       Если Результат Это Неопределено
           Возврат ""
       ИначеЕсли Результат ЭтоТип клСписок Для Спис
           Стр = "("
           Для Об Из Спис.Данные
               Стр &= ?(Стр = "(",""," ")+РезультатВСтроку(Об)
           КонецЦикла
           Возврат Стр+")"
       Иначе
           Возврат Строка(Результат)
       КонецЕсли    
   КонецФункции    
   
   //тут можно определить стандартные функции и значения языка
   //все эти значения переопределяемые во время выполнения
   //---------------------------
   Функция ЗаполнитьСтандартноеОкружение() тип клОкружение 
       Окруж = Новый клОкружение()
       Окруж.ДобавитьДействие{
           {"вкпс",        Символы.ВКПС}, 
           {"вк",          Символы.ВК}, 
           {"пс",          Символы.ПС}, 
           {"таб",         Символы.Таб}, 
           {"нпп",         Символы.НПП}, 
           {"упс",         Символы.УПС},
           {"+",           ПолучитьДелегат(,ОператорСМассивомОперандов,"ФункцияСМассивомОперандов")}, 
           {"-",           ПолучитьДелегат(,ОператорСМассивомОперандов,"ФункцияСМассивомОперандов")}, 
           {"*",           ПолучитьДелегат(,ОператорСМассивомОперандов,"ФункцияСМассивомОперандов")}, 
           {"/",           ПолучитьДелегат(,ОператорСМассивомОперандов,"ФункцияСМассивомОперандов")}, 
           {"^",           ПолучитьДелегат(,ОператорСМассивомОперандов,"ФункцияСМассивомОперандов")}, 
           {"**",          ПолучитьДелегат(,ОператорСМассивомОперандов,"ФункцияСМассивомОперандов")},
           {"макс",        ПолучитьДелегат(,ОператорСМассивомОперандов,"ФункцияСМассивомОперандов")},
           {"мин",         ПолучитьДелегат(,ОператорСМассивомОперандов,"ФункцияСМассивомОперандов")},
           
           {">",           ПолучитьДелегат(,ОператорСДвумяОперандами,"ФункцияСДвумяОперандами")}, 
           {"<",           ПолучитьДелегат(,ОператорСДвумяОперандами,"ФункцияСДвумяОперандами")}, 
           {">=",          ПолучитьДелегат(,ОператорСДвумяОперандами,"ФункцияСДвумяОперандами")}, 
           {"<=",          ПолучитьДелегат(,ОператорСДвумяОперандами,"ФункцияСДвумяОперандами")}, 
           {"=",           ПолучитьДелегат(,ОператорСДвумяОперандами,"ФункцияСДвумяОперандами")}, 
           {"<>",          ПолучитьДелегат(,ОператорСДвумяОперандами,"ФункцияСДвумяОперандами")},
           {"!=",          ПолучитьДелегат(,ОператорСДвумяОперандами,"ФункцияСДвумяОперандами")},
           
           {"помодулю",    ПолучитьДелегат(,ОператорСОднимОперандом,"ФункцияСОднимОперандом")},
           {"корень",      ПолучитьДелегат(,ОператорСОднимОперандом,"ФункцияСОднимОперандом")},
           {"синус",       ПолучитьДелегат(,ОператорСОднимОперандом,"ФункцияСОднимОперандом")},
           {"косинус",     ПолучитьДелегат(,ОператорСОднимОперандом,"ФункцияСОднимОперандом")}
           
           //{"append",      op.add},  
           //{"apply",       apply},
           //{"begin",       lambda *x: x[-1]},
           //{"car",         lambda x: x[0]},
           //{"cdr",         lambda x: x[1:]}, 
           //{"cons",        lambda x,y: [x] + y},
           //{"eq?",         op.is_}, 
           //{"equal?",      op.eq}, 
           //{"length",      len}, 
           //{"list",        lambda *x: list(x)}, 
           //{"list?",       lambda x: isinstance(x,list)}, 
           //{"map",         map},
           //{"not",         op.not_},
           //{"null?",       lambda x: x == []}, 
           //{"number?",     lambda x: isinstance(x, Number)},   
           //{"procedure?",  callable},
           //{"round",       round,
           //{"symbol?",     lambda x: isinstance(x, Symbol)}
       }
       Возврат Окруж
   КонецФункции    
       
   //---------------------------
   Функция Вычислить(Элемент тип Объект, Окруж тип клОкружение) тип Объект
       
       Если Элемент ЭтоТип клИдентификатор Для ТекИдентификатор 
           //получим значение переменной
           ТекОкруж = Окруж.НайтиОкружение(ТекИдентификатор)
           Если ТекОкруж Это Неопределено
               ВызватьИсключение "Не найден идентификатор "+ТекИдентификатор 
           Иначе
               Возврат ТекОкруж.ПолучитьДействие(ТекИдентификатор)
           КонецЕсли
       КонецЕсли
       Если Элемент ЭтоНеТип клСписок       
           //возвращаем атом
           Возврат Элемент           
       КонецЕсли
       
       //это точно список
       
       //?????? РУГАЕТСЯ
       //Спис = Элемент КАК клСписок
       
       Спис = ТипКТипу(Элемент,"клСписок")
       Если Спис.Количество=0
           Возврат Неопределено 
       КонецЕсли
       Перем ИД тип Строка = ""
       Если Спис.ПоИндексу(0) ЭтоТип клИдентификатор Для ТекИдентификатор
           //первый элемент это идентификатор
           ИД=НРег(ТекИдентификатор.ВСтроку)
       
       Иначе
           Рез = Вычислить(Спис.ПоИндексу(0), Окруж)
           Если Рез ЭтоТип клИдентификатор Для ТекИдентификатор
               //все-таки первый элемент это идентификатор
               ИД=НРег(ТекИдентификатор.ВСтроку)
               
           Иначе
               //обходим последовательность
               Кво=Спис.Количество
               Для Инд=1 По Кво-1
                   Рез=Вычислить(Спис.ПоИндексу(Инд), Окруж)
               КонецЦикла
               Возврат Рез
           КонецЕсли
       КонецЕсли
       
       Если ИД = "пауза"
           //выводим в консоль сообщение и ждем нажатия любой клавиши
           Если Спис.Количество<>1
               ВызватьИсключение "Оператор Пауза не имеет операндов." 
           КонецЕсли
           Пауза
           Возврат Неопределено
       
       ИначеЕсли ИД = "очистить"
           //очищаем экран консоли
           Если Спис.Количество<>1
               ВызватьИсключение "Оператор Очистить не имеет операндов." 
           КонецЕсли
           Очистить
           Возврат Неопределено
       
       ИначеЕсли ИД = "отладка"
           //включаем/выключаем режим отладки скрипта
           Если Спис.Количество<>2
               ВызватьИсключение "Не верное число операндов в операторе Отладка." 
           КонецЕсли
           Отладка=Булево(Вычислить(Спис.ПоИндексу(1), Окруж))
           Возврат Отладка
       
       ИначеЕсли ИД = "ввод"                 
           //вычисляем и выводим в консоль все приглашения
           //и ждем ввода строки в консоль и нажатия клавиши Ввод (Enter)
           Кво=Спис.Количество
           Для Инд=1 По Кво-1
               Вывод(РезультатВСтроку(Вычислить(Спис.ПоИндексу(Инд), Окруж)))
           КонецЦикла
           Возврат ПрочитатьСтроку()
       
       ИначеЕсли ИД = "вывод"                 
           //вычисляем и выводим в консоль все элементы списка
           Кво=Спис.Количество
           Для Инд=1 По Кво-1
               Вывод(РезультатВСтроку(Вычислить(Спис.ПоИндексу(Инд), Окруж)))
           КонецЦикла
           Возврат Неопределено
       
       ИначеЕсли ИД = "идент"
           //вычисляем операнд и превращаем результат в идентификатор
           Если Спис.Количество<>2
               ВызватьИсключение "Не верное число операндов в операторе Идент." 
           КонецЕсли
           Возврат Новый клИдентификатор(РезультатВСтроку(Вычислить(Спис.ПоИндексу(1), Окруж)))
           
       ИначеЕсли ИД = "если"                  
           //вычисляем условие
           //если условие выполнилось, то вычисляем Список1
           //иначе, вычисляем Список2
           //возвращаем результат того списка, который вычислялся
           //(Если (Условие) Список1 Список2)
           Если Спис.Количество<>4
               ВызватьИсключение "Не верное число операндов в операторе ?." 
           КонецЕсли
           Возврат Вычислить(?(Булево(Вычислить(Спис.ПоИндексу(1), Окруж)),Спис.ПоИндексу(2),Спис.ПоИндексу(3)), Окруж)
       
       ИначеЕсли ИД = "новый"                 
           //создать новый объект .Net из загруженных сборок 
           //пространство имён Промкод.Перфолента импортировано по умолчанию
           //например, (Новый Массив) или (Новый Структура "Вид,Количество", "Простой", 5)
           Если Спис.Количество<2
               ВызватьИсключение "Не верное число операндов в операторе Новый." 
           КонецЕсли
           //идентификатор типа объекта может вычисляться налету 
           //например, (Новый (Идент (+ "Мас" "сив")))
           Рез = Спис.ПоИндексу(1)
           Если Рез ЭтоНеТип клИдентификатор
               Рез = Вычислить(Рез, Окруж)
           КонецЕсли
           Если Рез ЭтоНеТип клИдентификатор
               ВызватьИсключение "Второй параметр в операторе Новый не является идентификатором." 
           КонецЕсли
           ТипОбъекта = НайтиТипОбъекта(Рез)
           Кво=Спис.Количество
           Массив Парамс[Кво-3] тип Объект
           Для Инд=2 По Кво-1
               Парамс[Инд-2]=Вычислить(Спис.ПоИндексу(Инд), Окруж)
           КонецЦикла
           Возврат System.Activator.CreateInstance(ТипОбъекта,Парамс)
       
       ИначеЕсли ИД = "перем"                 
           //определяем переменную
           //например, присвоим переменной А1 значение 100: (Перем А1 100)
           //переменная сохраняется в текущее окружение
           Если Спис.Количество<>3
               ВызватьИсключение "Не верное число операндов в операторе Перем." 
           КонецЕсли
           //идентификатор переменной может вычисляться налету 
           //например, (Перем (Идент (+ "А" "1")) 100)
           Рез = Спис.ПоИндексу(1)
           Если Рез ЭтоНеТип клИдентификатор
               Рез = Вычислить(Рез, Окруж)
           КонецЕсли
           Если Рез ЭтоНеТип клИдентификатор
               ВызватьИсключение "Второй параметр в операторе Перем не является идентификатором." 
           КонецЕсли
           Зн = Вычислить(Спис.ПоИндексу(2), Окруж)
           Окруж.ВставитьДействие(Рез, Зн)
           Возврат Неопределено
       
       ИначеЕсли ИД = "функ"                  
           //определение анонимной функции (Функ (ИдентификаторыПараметров...) ТелоФункции)
           //анонимная функция получает текущее окружение
           Если Спис.Количество<3
               ВызватьИсключение "Не верное число операндов в операторе Функция." 
           КонецЕсли
           Если Спис.ПоИндексу(1) ЭтоТип клСписок Для СписП
               СписокИдентификаторовПараметров = Новый клСписок
               Для Инд=0 По СписП.Количество-1
                   Рез = СписП.ПоИндексу(Инд)
                   Если Рез ЭтоНеТип клИдентификатор
                       Рез = Вычислить(Рез, Окруж)
                   КонецЕсли
                   Если Рез ЭтоНеТип клИдентификатор
                       ВызватьИсключение "Имя параметра № "+(Инд+1)+" в операторе Функция не является идентификатором." 
                   КонецЕсли
                   СписокИдентификаторовПараметров.ВКонец(Рез)
               КонецЦикла
               ТелоФункции = Новый клСписок
               Для Инд=2 По Спис.Количество-1
                   ТелоФункции.ВКонец(Спис.ПоИндексу(Инд))
               КонецЦикла
               Возврат Новый клФункцияПользователя(СписокИдентификаторовПараметров,ТелоФункции,Окруж)
           Иначе
               ВызватьИсключение "Второй операнд в операторе Функция не является списком параметров." 
           КонецЕсли
       
       ИначеЕсли ИД = "функция"                  
           //определение функции (Функция (ИмяФункции ИдентификаторыПараметров...) ТелоФункции)
           //функция получает текущее окружение
           Если Спис.Количество<3
               ВызватьИсключение "Не верное число операндов в операторе Функция." 
           КонецЕсли
           Если Спис.ПоИндексу(1) ЭтоТип клСписок Для СписП
               Перем ИмяФункции тип Строка = Неопределено
               СписокИдентификаторовПараметров = Новый клСписок
               Для Инд=0 По СписП.Количество-1
                   Рез = СписП.ПоИндексу(Инд)
                   Если Рез ЭтоНеТип клИдентификатор
                       Рез = Вычислить(Рез, Окруж)
                   КонецЕсли
                   Если Рез ЭтоНеТип клИдентификатор
                       Если Инд=0
                           ВызватьИсключение "Имя Функции не является идентификатором." 
                       Иначе
                           ВызватьИсключение "Имя параметра № "+Инд+" в операторе Функция не является идентификатором." 
                       КонецЕсли
                   КонецЕсли
                   Если Инд=0
                       ИмяФункции = Рез
                   Иначе
                       СписокИдентификаторовПараметров.ВКонец(Рез)
                   КонецЕсли
               КонецЦикла
               Если ИмяФункции = Неопределено
                   ВызватьИсключение "Имя функции неопределено." 
               КонецЕсли
               ТелоФункции = Новый клСписок
               Для Инд=2 По Спис.Количество-1
                   ТелоФункции.ВКонец(Спис.ПоИндексу(Инд))
               КонецЦикла
               ФункП = Новый клФункцияПользователя(СписокИдентификаторовПараметров,ТелоФункции,Окруж)
               Окруж.ВставитьДействие(ИмяФункции, ФункП)
               Возврат Неопределено
           Иначе
               ВызватьИсключение "Второй операнд в операторе Функция не является списком параметров." 
           КонецЕсли
       
       Иначе                                  
           //(Действие Параметры...)
           //остался только один вариант
           //надо выполнить действие из окружения
           Действие = Вычислить(ТекИдентификатор, Окруж)
           Кво=Спис.Количество
           Массив Элементы[Кво-2] тип Объект
           Для Инд=1 По Кво-1
               Элементы[Инд-1]=Вычислить(Спис.ПоИндексу(Инд), Окруж)
           КонецЦикла
           Возврат ВыполнитьДействие(Действие, ИД, Элементы, Окруж)
       КонецЕсли
       Возврат Элемент
   КонецФункции	
   
   //---------------------------
   Функция ВыполнитьДействие(Действие тип Объект, ИД тип Строка, Аргументы тип Объект[], Окруж тип клОкружение) тип Объект
       Если Действие ЭтоТип ФункцияСМассивомОперандов Для ФСМО
           Возврат ФСМО.Invoke(ИД,Объект(Аргументы))
           
       ИначеЕсли Действие ЭтоТип ФункцияСОднимОперандом Для ФСОО
           Возврат ФСОО.Invoke(ИД,Аргументы[0])
           
       ИначеЕсли Действие ЭтоТип ФункцияСДвумяОперандами Для ФСДО
           Возврат ФСДО.Invoke(ИД,Аргументы[0],Аргументы[1])
           
       ИначеЕсли Действие ЭтоТип клФункцияПользователя Для ФПЛЗ
           
           ОкружФункции = Новый клОкружение(ФПЛЗ.ИдентификаторыПараметров,Аргументы,ФПЛЗ.Окружение)
           Возврат Вычислить(ФПЛЗ.ТелоФункции,ОкружФункции)
       
       //??????? ИначеЕсли Действие ЭтоТип Тип("System.Delegate") Для ФСД
       //ошибка на слове Для
       
       ИначеЕсли Действие ЭтоТип System.Delegate Для ФСД
           Возврат ФСД.DynamicInvoke(Аргументы)
       
       ИначеЕсли Найти(ИД,".")>0
           //вызов метода объекта
           
       
       ИначеЕсли Аргументы.Количество>0
           ВызватьИсключение "Значение вызывается как действие."
           
       Иначе
           //просто значение
           Возврат Действие
       КонецЕсли
   КонецФункции	
   
#КонецОбласти      
     
#Область ВстроенныеФункцииЯзыка
   //---------------------------
   Функция ОператорСМассивомОперандов(ИмяФункции тип Строка, Парам Операнды тип Объект[]) тип Объект 
       Если Операнды Это Неопределено
           Возврат 0
       КонецЕсли
       Если Операнды.Количество=0
           ВызватьИсключение "Не достаточно операндов в Функции "+ИмяФункции+"."
       КонецЕсли
       Перем Рез тип Объект = Операнды[0]
       Для Инд=1 По Операнды.Количество-1
           Выбор Для НРег(ИмяФункции)
           Когда "+" Тогда    
               Рез += Операнды[Инд]
           Когда "-" Тогда    
               Рез -= Операнды[Инд]
           Когда "*" Тогда    
               Рез *= Операнды[Инд]
           Когда "/" Тогда    
               Рез /= Операнды[Инд]
           Когда "^","**" Тогда    
               Рез ^= Операнды[Инд]
           Когда "макс" Тогда    
               Если Операнды[Инд] > Рез 
                   Рез = Операнды[Инд]
               КонецЕсли
           Когда "мин" Тогда    
               Если Операнды[Инд] < Рез 
                   Рез = Операнды[Инд]
               КонецЕсли
           Иначе
               ВызватьИсключение "Функция "+ИмяФункции+" не определена."
           КонецВыбора
       КонецЦикла
       Возврат Рез
   КонецФункции 
   //---------------------------
   Функция ОператорСОднимОперандом(ИмяФункции тип Строка, Операнд тип Объект) тип Объект 
       Выбор Для НРег(ИмяФункции)
       Когда "помодулю" Тогда    
           Возврат Математика.ПоМодулю(Число(Операнд))
       Когда "синус" Тогда    
           Возврат Число(Математика.Синус(ДВещ(Операнд)))
       Когда "косинус" Тогда    
           Возврат Число(Математика.Синус(ДВещ(Операнд)))
       Когда "корень" Тогда    
           Возврат Число(Математика.Корень(ДВещ(Операнд)))
       Иначе
           ВызватьИсключение "Функция "+ИмяФункции+" не определена."
       КонецВыбора
   КонецФункции 
   //---------------------------
   Функция ОператорСДвумяОперандами(ИмяФункции тип Строка, Операнд1 тип Объект, Операнд2 тип Объект) тип Объект 
       Выбор Для НРег(ИмяФункции)
       Когда ">" Тогда    
           Возврат Операнд1 > Операнд2
       Когда "<" Тогда    
           Возврат Операнд1 < Операнд2
       Когда ">=" Тогда    
           Возврат Операнд1 >= Операнд2
       Когда "<=" Тогда    
           Возврат Операнд1 <= Операнд2
       Когда "=" Тогда    
           Возврат Операнд1 = Операнд2
       Когда "<>", "!=" Тогда    
           Возврат Операнд1 <> Операнд2
       Иначе
           ВызватьИсключение "Функция "+ИмяФункции+" не определена."
       КонецВыбора
   КонецФункции 
   
#КонецОбласти   
   //---------------------------
   Функция НайтиТипОбъекта(ИД тип Строка) тип Тип 
       ИмпортИмён System
       ИмпортИмён System.Reflection
       Перем НашлиТип тип Тип = Неопределено
       //ищем в загруженных сборках 
       Для Ассм тип Assembly Из AppDomain.CurrentDomain.GetAssemblies
           Для СИД тип Строка Из {ИД,"Промкод.Перфолента."+ИД}
               тп = Ассм.GetType(СИД, Ложь, Истина)
               Если тп ЭтоНе Неопределено
                   Если НашлиТип Это Неопределено
                       НашлиТип=тп
                       Прервать
                   Иначе
                       ВызватьИсключение "Указанный тип "+ИД+" существует в нескольких загруженных сборках."
                   КонецЕсли
               КонецЕсли
           КонецЦикла
       КонецЦикла
       Если НашлиТип Это Неопределено
           ВызватьИсключение "Указанный тип "+ИД+" НЕ найден в загруженных сборках."
       КонецЕсли
       Возврат НашлиТип
   КонецФункции	
     
КонецПрограммы    
//==========================================================================================
#Область ВспомогательныеКлассы
Делегат Функция ФункцияСМассивомОперандов(ИмяФункции тип Строка, Операнды тип Объект[]) тип Объект  
Делегат Функция ФункцияСОднимОперандом(ИмяФункции тип Строка, Операнд тип Объект) тип Объект
Делегат Функция ФункцияСДвумяОперандами(ИмяФункции тип Строка, Операнд1 тип Объект, Операнд2 тип Объект) тип Объект
//окружение хранит список действий и переменных
//***************************
Класс клОкружение 
   Поле _Окружение тип Структура = Новый Структура
   Поле _ВнешнееОкружение тип клОкружение
   //---------------------------
   &ВидноВсем 
   Конструктор(ВнешнееОкружение тип клОкружение = Неопределено)
       _ВнешнееОкружение = ВнешнееОкружение
   КонецКонструктора
   //---------------------------
   &ВидноВсем 
   Конструктор(Идентификаторы тип Строка[], Действия тип Объект[], ВнешнееОкружение тип клОкружение = Неопределено)
       Кво = Мин(Идентификаторы.Количество,Действия.Количество)
       Для Инд=0 По Кво-1
           _Окружение.Добавить(НРег(Идентификаторы[Инд]),Действия[Инд])
       КонецЦикла
       Если Идентификаторы.Количество > Действия.Количество
           //поддержим возможность передавать в функцию меньше параметров, чем задано в её определении 
           Для Инд=Кво По Идентификаторы.Количество-1
               _Окружение.Добавить(НРег(Идентификаторы[Инд]),Неопределено)
           КонецЦикла
       ИначеЕсли Идентификаторы.Количество < Действия.Количество
           //а вот лишние параметры это ошибка
           ВызватьИсключение "Число реальных параметров функции больше, чем формальных."
       КонецЕсли
       _ВнешнееОкружение = ВнешнееОкружение
   КонецКонструктора
   //---------------------------
   &ВидноВсем 
   Конструктор(Идентификаторы тип клСписок, Действия тип Объект[], ВнешнееОкружение тип клОкружение = Неопределено)
       Кво = Мин(Идентификаторы.Количество,Действия.Количество)
       Для Инд=0 По Кво-1
           _Окружение.Добавить(НРег(Идентификаторы.ПоИндексу(Инд)),Действия[Инд])
       КонецЦикла
       Если Идентификаторы.Количество > Действия.Количество
           //поддержим возможность передавать в функцию меньше параметров, чем задано в её определении 
           Для Инд=Кво По Идентификаторы.Количество-1
               _Окружение.Добавить(НРег(Идентификаторы.ПоИндексу(Инд)),Неопределено)
           КонецЦикла
       ИначеЕсли Идентификаторы.Количество < Действия.Количество
           //а вот лишние параметры это ошибка
           ВызватьИсключение "Число реальных параметров функции больше, чем формальных."
       КонецЕсли
       _ВнешнееОкружение = ВнешнееОкружение
   КонецКонструктора
   //---------------------------
   &ВидноВсем 
   Процедура ДобавитьДействие(Идентификатор тип Строка, Действие тип Объект)
       _Окружение.Добавить(НРег(Идентификатор),Действие)
   КонецПроцедуры
   //---------------------------
   &ВидноВсем 
   Функция НайтиОкружение(Идентификатор тип Строка) тип клОкружение
       Если _Окружение.СодержитКлюч(НРег(Идентификатор)) Тогда
       	Возврат ЭтотОбъект
       ИначеЕсли _ВнешнееОкружение Это Неопределено
       	Возврат Неопределено
       КонецЕсли
       Возврат _ВнешнееОкружение.НайтиОкружение(Идентификатор)
   КонецФункции    
   //---------------------------
   &ВидноВсем 
   Функция ПолучитьДействие(Идентификатор тип Строка) тип Объект
       Возврат _Окружение.Получить(НРег(Идентификатор))
   КонецФункции    
   //---------------------------
   &ВидноВсем 
   Процедура ВставитьДействие(Идентификатор тип Строка, Действие тип Объект)
       _Окружение.Вставить(НРег(Идентификатор), Действие)
   КонецПроцедуры    
КонецКласса
//функция определяемая пользователем
//***************************
Класс клФункцияПользователя
   &ВидноВсем 
   Поле ИдентификаторыПараметров тип клСписок
   &ВидноВсем 
   Поле ТелоФункции тип Объект
   &ВидноВсем 
   Поле Окружение тип клОкружение
   //---------------------------
   &ВидноВсем 
   Конструктор(пИдентификаторыПараметров тип клСписок, пТелоФункции тип Объект, пОкруж тип клОкружение)
       Присвоить ИдентификаторыПараметров, ТелоФункции, Окружение = пИдентификаторыПараметров, пТелоФункции, пОкруж
   КонецКонструктора
КонецКласса
//список хранит элементы находящиеся в круглых скобках
//***************************
Класс клСписок 
   
   &ВидноВсем 
   Поле Данные тип Массив = Новый Массив
   
   &ВидноВсем 
   Конструктор() 
   КонецКонструктора    
   
   &ВидноВсем 
   Конструктор(МассивСтрок тип Строка[]) 
       Данные.ДобавитьВсе(МассивСтрок)
   КонецКонструктора    
   
   &ВидноВсем
   Процедура ВКонец(Значение тип Объект)
       Данные.Добавить(Значение)    
   КонецПроцедуры
   
   &ВидноВсем
   Функция ПоИндексу(Индекс тип Целое) тип Объект 
       Возврат Данные.Получить(Индекс)
   КонецФункции 
   
   &ВидноВсем
   Функция Извлечь(Индекс тип Целое) тип Объект 
       Элемент = Данные.Получить(Индекс)
       Данные.Удалить(Индекс)
       Возврат Элемент
   КонецФункции 
   
   &ВидноВсем
   Функция Количество() тип Целое 
       Возврат Данные.Количество
   КонецФункции 
   
КонецКласса
//идентификатор хранит имя действия  
//***************************
Класс клИдентификатор
   
   Поле _ИД тип Строка    
   
   &ВидноВсем 
   Конструктор(ИД тип Строка) 
       _ИД=ИД
   КонецКонструктора    
   
   //переопределим функцию преобразования к строке
   &ВидноВсем, Переопределение
   Функция ToString() тип Строка 
       Возврат _ИД
   КонецФункции 
   
КонецКласса
#КонецОбласти    

См. также