📜 ⬆️ ⬇️

Microsoft itself

We have already considered the way to create an embedded script engine based on CodeDom.Compiler and the CSharpCodeProvider class. We now pose a more ambitious task, where we will not rely on a ready-made compiler. We will write our own generator, which builds MSIL-code "on the fly" and executes it.

To begin with, let's try to add two numbers and print the result as if in C #, but without using its language constructs:

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); } } } 

Run for execution:

image
')
So, in the simplest case, we were able to generate our own MSIL code to add two numbers. On this, it would be quite possible to write an application with the functionality of the old “Electronics” calculator, which has been gathering dust in my desk since the 80s of the last century.
Now let's add function calls to the generated code, for example, let the parameters be printed directly in the code that we created on the fly:
 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); } } } 

Run for execution:
image
So, now we can generate MSIL code in such a way that functions can be called from it. Please note that we generate MSIL directly in memory without any intermediate files. And now everything is in our power, we can change the code generation in any way — for example, to notify something via a line to invoke a callback method or something else.
What else separates us from writing your own programming language? Yes, only the formal syntax and its parser! If we write the corresponding parser, then nothing can prevent us from creating our own programming language, for example, with the following syntax:



120 31/12/2017



On the parser and the formal syntax, the conversation will go into the next article.
Arkady Pchelintsev, project architect

Source: https://habr.com/ru/post/327580/


All Articles