Click here to Skip to main content
15,884,099 members
Articles / Programming Languages / MSIL

Using Reflection.Emit to Precompile Expressions to MSIL

Rate me:
Please Sign up or sign in to vote.
5.00/5 (31 votes)
6 Jan 2009CPOL6 min read 68K   807   84  
The classes in this project allow you to parse text expressions entered by a user and compile them to a .NET assembly. This assembly can be executed on the fly, or saved to a DLL.
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;
using System.Diagnostics;
using System.Diagnostics.SymbolStore;

namespace MSILParser
{
    /// <summary>
    /// MSILParser implements the abstract RuleParser class and compiles 
    /// the statement to a dynamic method
    /// </summary>
    public class MsilParser : RuleParser
    {
        private ILGenerator il;
        public delegate TReturn ExpressionInvoker<TReturn>();
       
        /// <summary>
        /// This is just a test function that supplies variables with values
        /// </summary>
        public static double GetVar(string var)
        {
            switch (var)
            {
                case "a": return 100;
                case "b": return 200;
                case "c": return 300;
                case "d": return 400;
                default: return 500;
            }
        }
        
        /// <summary>
        /// Builds and returns a dynamic assembly
        /// </summary>
        public Type CompileMsil(string expr)
        {

            // Build the dynamic assembly
            string assemblyName = "Expression";
            string modName = "expression.dll";
            string typeName = "Expression";
            string methodName = "RunExpression";
            AssemblyName name = new AssemblyName(assemblyName);
            AppDomain domain = System.Threading.Thread.GetDomain();
            AssemblyBuilder builder = domain.DefineDynamicAssembly(
              name, AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder module = builder.DefineDynamicModule
              (modName, true);
            TypeBuilder typeBuilder = module.DefineType(typeName,
              TypeAttributes.Public | TypeAttributes.Class);
            MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName,
              MethodAttributes.HideBySig | MethodAttributes.Static
              | MethodAttributes.Public,
              typeof(Object), new Type[] {  });

            // Create the ILGenerator to insert code into our method body
            ILGenerator ilGenerator = methodBuilder.GetILGenerator();
            this.il = ilGenerator;

            // Parse the expression.  This will insert MSIL instructions
            this.Run(expr);

            // Finish the method by boxing the result as Double
            this.il.Emit(OpCodes.Conv_R8);
            this.il.Emit(OpCodes.Box, typeof(Double));
            this.il.Emit(OpCodes.Ret);

            // Create and save the Assembly and return the type
            Type myClass = typeBuilder.CreateType();
            builder.Save(modName);
            return myClass;
        }

        #region Semantic Actions for Parse Functions
        
        protected override void matchAnd()
        {
            this.il.Emit(OpCodes.And);
        }

        protected override void matchOr()
        {
            this.il.Emit(OpCodes.Or);
        }

        protected override void matchAdd()
        {
            this.il.Emit(OpCodes.Add);
        }

        protected override void matchComma()
        {
            throw new NotImplementedException();
        }

        protected override void matchDiv()
        {
            this.il.Emit(OpCodes.Div);
        }

        protected override void matchDot()
        {
            throw new NotImplementedException();
        }

        protected override void matchEqual()
        {
            this.il.Emit(OpCodes.Ceq);
        }

        protected override void matchFunc()
        {
            throw new NotImplementedException();
        }

        protected override void matchGE()
        {
            throw new NotImplementedException();
        }

        protected override void matchGT()
        {
            this.il.Emit(OpCodes.Cgt);
        }

        protected override void matchLE()
        {
            throw new NotImplementedException();
        }

        protected override void matchLT()
        {
            this.il.Emit(OpCodes.Clt);
        }

        protected override void matchMod()
        {
            throw new NotImplementedException();
        }

        protected override void matchMult()
        {
            this.il.Emit(OpCodes.Mul);
        }

        protected override void matchNeg()
        {
            this.il.Emit(OpCodes.Neg);
        }

        protected override void matchNegate()
        {
            throw new NotImplementedException();
        }

        protected override void matchNotEqual()
        {
            throw new NotImplementedException();
        }

        protected override void matchParen()
        {
            // No action required
        }

        protected override void matchSub()
        {
            this.il.Emit(OpCodes.Sub);
        }

        protected override void matchVal()
        {
            switch (this.tokenValue.Type)
            {
                case VariantType.BOOL:
                    this.il.Emit(OpCodes.Ldc_I4, Convert.ToInt32(this.tokenValue.Value));
                    break;
                case VariantType.DOUBLE:
                    this.il.Emit(OpCodes.Ldc_R8, Convert.ToDouble(this.tokenValue.Value));
                    //this.il.Emit(OpCodes.Box, typeof(Double));
                    break;
                case VariantType.INT:
                    this.il.Emit(OpCodes.Ldc_R8, Convert.ToDouble(this.tokenValue.Value));
                    break;
                case VariantType.STRING:
                    this.il.Emit(OpCodes.Ldstr, Convert.ToString(this.tokenValue.Value));
                    break;
                default:
                    throw new Exception("Invalid TokenType");
            }
        }

        protected override void matchVar()
        {
            string s = tokenValue.ToString();
            il.Emit(OpCodes.Ldstr, s);
            il.Emit(OpCodes.Call, typeof(MsilParser).GetMethod("GetVar", new Type[] { typeof(string) }));
        }

        protected override void matchXor()
        {
            this.il.Emit(OpCodes.Xor);
        }
        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Stephen Marsh has over 10 years of experience developing enterprise applications built on the .Net framework. He specializes in building expert systems that serve the financial industry.

Comments and Discussions