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