Опубликован: 28.06.2006 | Уровень: специалист | Доступ: платный | ВУЗ: Московский государственный технический университет им. Н.Э. Баумана
Дополнительный материал 3:

Приложение B

< Дополнительный материал 2 || Дополнительный материал 3

Исходный код программы 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)+")");
  }
}
< Дополнительный материал 2 || Дополнительный материал 3
Анастасия Булинкова
Анастасия Булинкова
Рабочим названием платформы .NET было
Bogdan Drumov
Bogdan Drumov
Молдова, Республика
Azamat Nurmanbetov
Azamat Nurmanbetov
Киргизия, Bishkek