Сам себе Microsoft

от автора

Мы уже рассмотрели способ создания встраиваемого скриптового движка на основе CodeDom.Compiler и класса CSharpCodeProvider. Поставим теперь более амбициозную задачу, где не будем полагаться на готовый компилятор. Будем писать свой собственный генератор, который строит MSIL-код «на лету» и исполняет его.
Для начала, попробуем сложить два числа и распечатать результат как бы на C#, но не пользуясь его языковыми конструкциями:

using System; using System.IO; using System.Reflection; using System.Reflection.Emit;  namespace ConsoleApplication2 {     class Program     {         static void Main(string[] args)         {             Type[] par = new Type[] { typeof(Int32), typeof(Int32) };             DynamicMethod func = new DynamicMethod("AddTwoValues", typeof(Int32), par, false);             ILGenerator il = func.GetILGenerator();              // это то же самое как: int AddTwoValues(int x, int y) { return x+y; }             il.Emit(OpCodes.Ldarg_0);             il.Emit(OpCodes.Ldarg_1);             il.Emit(OpCodes.Add);             il.Emit(OpCodes.Ret);              Object[] param = new Object[] { 13, 12 };   // готовим параметры 12 и 13              int iRet=(int)func.Invoke(null, param);     // выполняем              // выводим что получилось             Console.WriteLine("{0}+{1}={2}", param[0], param[1], iRet);           }     } }

Запускаем на исполнение:
image

Итак, в простейшем случае мы уже смогли сгенерировать свой собственный MSIL-код для сложения двух чисел. На этом уже вполне можно было бы написать приложение с функциональностью старого калькулятора «Электроника», который с 80-х годов прошлого века пылится у меня в столе.

Теперь добавим в сгенерированный код вызовы функций, например, пусть параметры распечатываются непосредственно в том коде, который создавался нами «на лету»:

using System; using System.IO; using System.Reflection; using System.Reflection.Emit;  namespace ConsoleApplication2 {     class Program     {         static void Main(string[] args)         {             Type[] par = new Type[] { typeof(Int32), typeof(Int32) };             DynamicMethod func = new DynamicMethod("AddTwoValues", typeof(Int32), par, false);             ILGenerator il = func.GetILGenerator();              MethodInfo fnWriteLine = typeof(Console).                                      GetMethod("WriteLine", new Type[] { typeof(Int32) });              il.Emit(OpCodes.Ldarg_0);             il.Emit(OpCodes.Call, fnWriteLine);             il.Emit(OpCodes.Ldarg_1);             il.Emit(OpCodes.Call, fnWriteLine);              il.Emit(OpCodes.Ldarg_0);             il.Emit(OpCodes.Ldarg_1);             il.Emit(OpCodes.Add);             il.Emit(OpCodes.Ret);              Object[] param = new Object[] { 13, 12 };   // готовим параметры 12 и 13              int iRet=(int)func.Invoke(null, param);     // выполняем              // выводим что получилось             Console.WriteLine("{0}+{1}={2}", param[0], param[1], iRet);         }     } }

Запускаем на исполнение:
image

Итак, теперь мы умеем генерировать MSIL-код таким образом, что из него можно вызывать функции. Обратите внимание, что мы генерируем непосредственно MSIL в памяти без всяких промежуточных файлов. И в нашей власти теперь всё, мы можем менять генерацию кода как угодно – например, для нотификации чего-либо вставлять через строчку вызов какого-либо callback-метода или что-то ещё.
Что нас ещё отделяет от написания собственного языка программирования? Да только формальный синтаксис и его парсер! Если мы напишем соответствующий парсер, то ничто нам не сможет помешать создать свой собственный язык программирования, например, с таким синтаксисом:

ЕСЛИ КЛИЕНТ АКТИВЕН
НАЧАЛО БЛОКА
КУПИТЬ БИЛЕТ НА ПОЕЗД 120 на 31/12/2017 КУПЕЙНЫЙ НИЖНЕЕ
РАСПЕЧАТАТЬ БЛАНК
КОНЕЦ БЛОКА

О парсере и формальном синтаксисе разговор пойдёт в следующий статье.
ссылка на оригинал статьи https://habrahabr.ru/post/327580/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *