Common Intermediate Language и системное программирование в Microsoft .NET

Приложение B

Показывать лекцию целиком

Исходный код программы Integral

Исходный код программы Integral, демонстрирующей различные способы динамической генерации кода на примере вычисления определенного интеграла, состоит из двух файлов:

  • Expr.cs

    Содержит парсер арифметических выражений и классы для дерева абстрактного синтаксиса.

  • Integral.cs

    Содержит классы для динамической генерации кода и вычисления интеграла.

  • B.1. Expr.cs

    using System;
    using System.Globalization;
    using System.Reflection.Emit;
    using System.Text.RegularExpressions;
    
    public abstract class Expression
    {
        public abstract string GenerateCS();
        public abstract void GenerateCIL(ILGenerator il);
        public abstract double Evaluate(double x);
    }
    
    class UnaryExpression: Expression
    {
        private Expression a;
    
        public UnaryExpression(Expression a) { this.a = a; }
    
        public override string GenerateCS()
        {
            return "-("+a.GenerateCS()+")";
        }
    
        public override void GenerateCIL(ILGenerator il)
        {
            a.GenerateCIL(il);
            il.Emit(OpCodes.Neg);
        }
    
        public override double Evaluate(double x)
        {
            return -a.Evaluate(x);
        }
    }
    
    class BinaryExpression: Expression
    {
        private Expression a, b;
        private OpCode op;
    
        public BinaryExpression(Expression a, Expression b, OpCode op) 
        { 
            this.a = a;
            this.b = b;
            this.op = op;
        }
    
        private string opCs()
        {
            if (op.Equals(OpCodes.Add))
                return "+";
            else if (op.Equals(OpCodes.Sub))
                return "-";
            else if (op.Equals(OpCodes.Mul))
                return "*";
            else
                return "/";
        }
    
        public override string GenerateCS()
        {
            return "("+a.GenerateCS()+")"+opCs()+"("+b.GenerateCS()+")";
        }
    
        public override void GenerateCIL(ILGenerator il)
        {
            a.GenerateCIL(il);
            b.GenerateCIL(il);
            il.Emit(op);
        }
    
        public override double Evaluate(double x)
        {
            if (op.Equals(OpCodes.Add))
              return a.Evaluate(x) + b.Evaluate(x);
            else if (op.Equals(OpCodes.Sub))
                return a.Evaluate(x) - b.Evaluate(x);
            else if (op.Equals(OpCodes.Mul))
                return a.Evaluate(x) * b.Evaluate(x);
            else
                return a.Evaluate(x) / b.Evaluate(x);
        }
    }
    
    class ConstExpression: Expression
    {
        private double value;
    
        public ConstExpression(double value) { this.value = value; }
    
        public override string GenerateCS()
        {
            return value.ToString(new CultureInfo(""));
        }
    
        public override void GenerateCIL(ILGenerator il)
        {
            il.Emit(OpCodes.Ldc_R8,value);
        }
    
        public override double Evaluate(double x)
        {
            return value;
        }
    }
    
    class VariableExpression: Expression
    {
        public VariableExpression() { }
    
        public override string GenerateCS()
        {
            return "x";
        }
    
        public override void GenerateCIL(ILGenerator il)
        {
          il.Emit(OpCodes.Ldarg_1);
        }
    
        public override double Evaluate(double x)
        {
            return x;
        }
    }
    
    public class Parser
    {
        private const string REGEXP_NUMBER = "[0-9]+(.[0-9])?";
        private Match token;
    
        public Parser(string expr)
        {
            
            token = Regex.Match(expr,
                "x|" +            // identifier x
                REGEXP_NUMBER+"|"+     // floating-point numbers
                "\\+|\\-|\\*|/|"+      // arithmetic operators
                "\\(|\\)"           // parens
                );
        }
    
        public Expression Parse()
        {
            checkToken();
            Expression result = null;
            OpCode op = OpCodes.Add;
    
            if (isAddOp())
            {
            op = token.Value.Equals("-") ? OpCodes.Sub : OpCodes.Add;
                token = token.NextMatch();
            }
    
            result = parseTerm();
            if (op.Equals(OpCodes.Sub))
                result = new UnaryExpression(result);
    
            while (token.Success  isAddOp())
            {
            op = token.Value.Equals("-") ? OpCodes.Sub : OpCodes.Add;
                token = token.NextMatch();
                result = new BinaryExpression(result,parseTerm(),op);
            }
    
            return result;
        }
    
        private Expression parseTerm()
        {
            checkToken();
            Expression result = parseFactor();
    
            while (token.Success  isMulOp())
            {
            OpCode op = token.Value.Equals("*") ? 
               OpCodes.Mul : OpCodes.Div;
            token = token.NextMatch();
            result = new BinaryExpression(result,parseFactor(),op);
            }
    
            return result;
        }
    
        private Expression parseFactor()
        {
            checkToken();
            Expression result = null;
    
            if (isNumber())
            {
                IFormatProvider provider = new CultureInfo("");
                double val = Convert.ToDouble(token.Value,provider);
                result = new ConstExpression(val);
            }
            else if (token.Value.Equals("x"))
                result = new VariableExpression();
            else if (token.Value.Equals("("))
            {
                token = token.NextMatch();
                result = Parse();
    
                if (! token.Value.Equals(")"))
                    throwError();
            }
            else
                throwError();
    
            token = token.NextMatch();
    
            return result;
        }
    
        private void checkToken()
        {
            if (!token.Success)
                throwError();
        }
    
        private void throwError()
        {
            throw new Exception("syntax error");
        }
    
        private bool isNumber()
        {
            return Regex.IsMatch(token.Value,REGEXP_NUMBER);
        }
    
        private bool isAddOp()
        {
            return Regex.IsMatch(token.Value,"\\+|\\-");
        }
        private bool isMulOp()
        {
            return Regex.IsMatch(token.Value,"\\*|/");
        }
    }

    B.2. Integral.cs

    using System;
    using System.CodeDom.Compiler;
    using System.Reflection;
    using System.Reflection.Emit;
    using System.Threading;
    using Microsoft.CSharp;
    
    public abstract class Function
    {
      public abstract double Eval(double x);
    }
    
    public class TestFunction: Function
    {
        public override double Eval(double x)
        {
            return x * Math.Sin(x);
        }
    }
    
    public class InterpretingFunction: Function
    {
      private Expression expr;
    
      public InterpretingFunction(Expression expr)
      {
        this.expr = expr; 
      }
    
      public override double Eval(double x)
      {
        return expr.Evaluate(x);
      }
    }
    
    class MainClass
    {
      static Function CompileToCS(Expression expr)
      {
        ICodeCompiler compiler = 
                    new CSharpCodeProvider().CreateCompiler();
        CompilerParameters parameters = new CompilerParameters();
        parameters.ReferencedAssemblies.Add("System.dll");
        parameters.ReferencedAssemblies.Add("Integral.exe");
        parameters.GenerateInMemory = true;
        string e = expr.GenerateCS();
        string code = 
          "public class FunctionCS: Function\n"+
          "{\n"+
          "  public override double Eval(double x)\n"+
          "  {\n"+
          "    return "+e+";\n"+
          "  }\n"+
          "}\n";
        
        CompilerResults compilerResults =
          compiler.CompileAssemblyFromSource(parameters,code);
    
        Assembly assembly = compilerResults.CompiledAssembly;
        return assembly.CreateInstance("FunctionCS") as Function;
      }
    
      static Function CompileToCIL(Expression expr)
      {
        AppDomain appDomain = Thread.GetDomain();
        AssemblyName assemblyName = new AssemblyName();
        assemblyName.Name = "f";
    
        AssemblyBuilder assembly = 
          appDomain.DefineDynamicAssembly(
            assemblyName, 
            AssemblyBuilderAccess.RunAndSave
          );
    
        ModuleBuilder module = 
          assembly.DefineDynamicModule("f.dll", "f.dll");
    
        TypeBuilder typeBuilder = 
          module.DefineType(
            "FunctionCIL",
            TypeAttributes.Public | TypeAttributes.Class,
            typeof(Function)
          );
    
        ConstructorBuilder cons =
          typeBuilder.DefineConstructor(
            MethodAttributes.Public,
            CallingConventions.Standard,
            new Type[] { }
          );
    
        ILGenerator consIl = cons.GetILGenerator();
        consIl.Emit(OpCodes.Ldarg_0);
        consIl.Emit(OpCodes.Call,typeof(object).GetConstructor(new
                             Type[0]));
        consIl.Emit(OpCodes.Ret);
    
        MethodBuilder evalMethod =
          typeBuilder.DefineMethod(
            "Eval",
            MethodAttributes.Public | MethodAttributes.Virtual,
            typeof(double),
            new Type[] { typeof(double) }
          );
    
        ILGenerator il = evalMethod.GetILGenerator();
        expr.GenerateCIL(il);
        il.Emit(OpCodes.Ret);
    
        Type type = typeBuilder.CreateType();
    
        ConstructorInfo ctor = type.GetConstructor(new Type[0]);
        return ctor.Invoke(null) as Function;
      }
    
      static double Integrate(Function f, double a, double b, int n)
      {
        double h = (b-a)/n, sum = 0.0;
    
        for (int i = 0; i < n; i++)
          sum += h*f.Eval((i+0.5)*h);
    
        return sum;
      }
    
      static void Main()
      {
        int num = 10000000;
        double a = 0.0;
        double b = 10.0;
        string s = "2*x*x*x+3*x*x+4*x+5";
    
        Parser parser = new Parser(s);
        Expression expr = parser.Parse();
    
        DateTime t1 = DateTime.Now;
        Function f1 = new InterpretingFunction(expr);
        double s1 = Integrate(f1,a,b,num);
    
        DateTime t2 = DateTime.Now;
        Function f2 = CompileToCS(expr);
        DateTime t2_2 = DateTime.Now;
        double s2 = Integrate(f2,a,b,num);
    
        DateTime t3 = DateTime.Now;
        Function f3 = CompileToCIL(expr);
        DateTime t3_2 = DateTime.Now;
        double s3 = Integrate(f3,a,b,num);
    
        DateTime t4 = DateTime.Now;
    
        Console.WriteLine("Interpreter: "+s1+" ("+(t2-t1)+")");
        Console.WriteLine("C#:     "+s2+" ("+
          (t2_2-t2)+" + "+(t3-t2_2)+")");
        Console.WriteLine("CIL:     "+s3+" ("+
          (t3_2-t3)+" + "+(t4-t3_2)+")");
      }
    }
    Вернуться к учебному плану