Рабочим названием платформы .NET было |
Опубликован: 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