Click here to Skip to main content
15,886,199 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 68.1K   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;


namespace MSILParser
{
    public enum VariantType
    {
        BOOL = 1, INT, DOUBLE, STRING, VAR, NULL
    }

    /// <summary>
    /// Variant is a generic value object that handles different data types
    /// Its functions allow you to compare, multiply, divide, etc... values
    /// of different types. Data types allowed are bool, int, double and string
    /// </summary>
    public class Variant
    {
        private bool _boolVal;
        private int _intVal;
        private double _doubleVal;
        private string _stringVal;
        private VariantType _type;

        // list management variables
        public Variant next = null;
        public Variant first = null;

        // Bit mask constants for comparision results.
        public const int crLT = 1;
        public const int crEQ = 2;
        public const int crGT = 4;
        public const int crNE = 8; // Used when comparing booleans or strings.
        public const int crError = -1;

        #region Constructors
        // Constructors:  One constuctor for each type of variant
        public Variant(bool val)
        {
            _boolVal = val;
            _type = VariantType.BOOL;
        }
        public Variant(int val)
        {
            _intVal = val;
            _type = VariantType.INT;
        }
        public Variant(double val)
        {
            _doubleVal = val;
            _type = VariantType.DOUBLE;
        }
        public Variant(string val)
        {
            if (val == null || val == "")
            {
                _type = VariantType.NULL;

                _stringVal = "";

            }
            else
            {
                _stringVal = val;
                _type = VariantType.STRING;
            }
        }
        public Variant(VariantType p, string s)
        {
            if (p == VariantType.VAR)
            {
                _type = p;
                _stringVal = s;
            }
            else throw new Exception("Invalid variant type");
        }

        #endregion

        #region Accessors
        // Returns the value of the variant depending on is current type
        /***************************************************
        *                  VALUE
        ***************************************************/
        public virtual Object Value
        {
            get
            {
                switch (_type)
                {
                    case VariantType.INT:
                        return (Object)_intVal;
                    case VariantType.BOOL:
                        return (Object)_boolVal;
                    case VariantType.DOUBLE:
                        return (Object)_doubleVal;
                    case VariantType.STRING:
                    case VariantType.VAR:
                    case VariantType.NULL:
                        return (Object)_stringVal;
                    default:
                        throw new Exception("Invalid variant type");
                }
            }
        }

        /***************************************************
        *                  TYPE
        ***************************************************/
        public VariantType Type
        {
            get { return _type; }
        }
        #endregion

        /***************************************************
        *                  TO STRING
        ***************************************************/
        public override String ToString()
        {
            switch (_type)
            {
                case VariantType.BOOL:
                    return _boolVal.ToString();
                case VariantType.INT:
                    return _intVal.ToString();
                case VariantType.DOUBLE:
                    return _doubleVal.ToString();
                case VariantType.STRING:
                case VariantType.VAR:
                    return _stringVal;
                case VariantType.NULL:
                    return "null";
                default:
                    return "";
            }
        }

        /***************************************************
        *                  IS LIST TYPE
        ***************************************************/
        public bool IsListType
        {
            get { return first != null; }
        }

        # region Functions called by Arithmetic1Operation
        /// <summary>
        /// Performs multiplication on two variant objects and returns the result as
        /// a new variant. Only doubles and ints can be multiplied.
        /// </summary>
        /***************************************************
        *                  MULTIPLY
        ***************************************************/
        public static Variant Mult(Variant var1, Variant var2)
        {
            int bitComb = BitCombination(var1, var2);
            Variant result = null;

            if ((bitComb & 306) != 0)               // bool, string, Assy, Part
                throw new Exception("Could not be multiplied due to variant types.");
            else if (ConvertsToInt(var1, var2))     // all other types
                result = new Variant(Convert.ToInt32(var1.Value) * Convert.ToInt32(var2.Value));
            else if (ConvertsToDbl(var1, var2))     // all other types
                result = new Variant(Convert.ToDouble(var1.Value) * Convert.ToDouble(var2.Value));
            else
                throw new Exception("Could not be multiplied due to variant types.");

            return result;
        }
        /***************************************************
        *                  DIVIDE
        ***************************************************/
        public static Variant Div(Variant var1, Variant var2)
        {
            int bitComb = BitCombination(var1, var2);
            Variant result = null;

            if ((bitComb & 306) != 0)               // bool, string, Assy, Part
                throw new Exception("Could not be divided due to variant types.");
            else if (ConvertsToInt(var1, var2))     // all other types
                result = new Variant(Convert.ToInt32(var1.Value) / Convert.ToInt32(var2.Value));
            else if (ConvertsToDbl(var1, var2))     // all other types
                result = new Variant(Convert.ToDouble(var1.Value) / Convert.ToDouble(var2.Value));
            else
                throw new Exception("Could not be divided due to variant types.");

            return result;
        }
        /***************************************************
        *                  MODULUS
        ***************************************************/
        public static Variant Mod(Variant var1, Variant var2)
        {
            int bitComb = BitCombination(var1, var2);
            Variant result = null;

            if ((bitComb & 306) != 0)               // bool, string, Assy, Part
                throw new Exception("Unable to perform operation due to variant types.");
            else if (ConvertsToInt(var1, var2))     // all other types
                result = new Variant(Convert.ToInt32(var1.Value) % Convert.ToInt32(var2.Value));
            else if (ConvertsToDbl(var1, var2))     // all other types
                result = new Variant(Convert.ToDouble(var1.Value) % Convert.ToDouble(var2.Value));
            else
                throw new Exception("Unable to perform operation due to variant types.");

            return result;
        }
        /***************************************************
        *                  NEGATE
        ***************************************************/
        public static Variant Negate(Variant var1)
        {
            Variant result = null;
            if (var1.Type == VariantType.BOOL)
                result = new Variant(!(bool)var1.Value);
            else
                throw new Exception("Could not negate because variant was not boolean.");

            return result;
        }
        #endregion

        #region Functions called by Arithmetic2Operation
        /***************************************************
        *                  ADD
        ***************************************************/
        public static Variant Add(Variant var1, Variant var2)
        {
            int bitComb = BitCombination(var1, var2);
            Variant result = null;

            if ((bitComb & 290) != 0)               // bool, Assy, Part
                throw new Exception("Could not be added due to variant types.");
            else if (bitComb == 16)                 // strings
                result = new Variant(var1.Value.ToString() + var2.Value.ToString());
            else if (ConvertsToInt(var1, var2))     // all other types
                result = new Variant(Convert.ToInt32(var1.Value) + Convert.ToInt32(var2.Value));
            else if (ConvertsToDbl(var1, var2))     // all other types
                result = new Variant(Convert.ToDouble(var1.Value) + Convert.ToDouble(var2.Value));
            else
                throw new Exception("Could not be added due to variant types.");

            return result;
        }
        /***************************************************
        *                  SUBTRACT
        ***************************************************/
        public static Variant Sub(Variant var1, Variant var2)
        {
            int bitComb = BitCombination(var1, var2);
            Variant result = null;

            if ((bitComb & 306) != 0)               // bool, string, Assy, Part
                throw new Exception("Could not be subtracted due to variant types.");
            else if (ConvertsToInt(var1, var2))     // all other types
                result = new Variant(Convert.ToInt32(var1.Value) - Convert.ToInt32(var2.Value));
            else if (ConvertsToDbl(var1, var2))     // all other types
                result = new Variant(Convert.ToDouble(var1.Value) - Convert.ToDouble(var2.Value));
            else
                throw new Exception("Could not be subtracted due to variant types.");

            return result;
        }
        #endregion

        #region Functions called by CompareOperation
        /***************************************************
        *                  COMPARE LIST
        ***************************************************/
        private static bool compareList(Variant l, Variant r, int rslt)
        {
            // Both left and right arguments cannot be lists
            if (l.IsListType && r.IsListType)
                throw new Exception("Cannot compare a list to a list");

            // If neither are lists, just run the compare
            if (!l.IsListType && !r.IsListType)
                return (compare(l, r) == rslt);

            // Figure out which argument is the list and which is not
            Variant lst, nlst;
            lst = (l.IsListType) ? l : r;
            nlst = (l.IsListType) ? r : l;
            // Loop through each value in the list and compare it to
            // each non-list value.  If any one of the values in the
            // list are less then return true;
            while (lst != null)
            {
                int temp = compare(l, r);
                if (temp == rslt) return true;
                if (lst == l) lst = l = l.next;
                else if (lst == r) lst = r = r.next;
            }
            // None matched, so return false
            return false;
        }

        public static bool LessThan(Variant l, Variant r)
        {
            return compareList(l, r, crLT);
        }

        public static bool GreaterThan(Variant l, Variant r)
        {
            return compareList(l, r, crGT);
        }

        public static bool EqualTo(Variant l, Variant r)
        {
            return compareList(l, r, crEQ);
        }

        private static int compare(Variant lValue, Variant rValue)
        {
            int returnVal = 0;
            VariantType lType = lValue.Type;
            VariantType rType = rValue.Type;

            // BOOL and BOOL
            if (lType == VariantType.BOOL &&
                rType == VariantType.BOOL)
            {
                Console.WriteLine("BOTH SIDES BOOLEAN");
                if ((bool)lValue.Value == (bool)rValue.Value)
                    returnVal = crEQ;
                else
                    returnVal = crNE;
            }
            // BOOL and any other variant type (other than BOOL)
            else if (lType == VariantType.BOOL ||
                     rType == VariantType.BOOL)
            {
                returnVal = crNE;
            }
            // STRING and STRING
            else if (lType == VariantType.STRING &&
                     rType == VariantType.STRING)
            {
                returnVal = CompareString(lValue.Value.ToString(), rValue.Value.ToString());
            }
            // STRING and any other variant type (other than BOOL and STRING)
            else if (lType == VariantType.STRING ||
                     rType == VariantType.STRING)
            {
                Variant l = (lType == VariantType.STRING) ? lValue : rValue;
                Variant r = (lType == VariantType.STRING) ? rValue : lValue;

                if (r.Type == VariantType.INT ||
                    r.Type == VariantType.DOUBLE)
                {
                    returnVal = crNE;
                }
                else
                {
                    returnVal = CompareString(l.Value.ToString().Trim(), r.Value.ToString().Trim());
                }

                // switch return value if right param is type string
                if (rType == VariantType.STRING)
                {
                    if (returnVal == crGT)
                        returnVal = crLT;
                    else if (returnVal == crLT)
                        returnVal = crGT;
                }
            }
            // compare values as doubles
            else if (lType == VariantType.INT ||
                lType == VariantType.DOUBLE ||
                rType == VariantType.INT ||
                rType == VariantType.DOUBLE)
            {
                if (ConvertsToDbl(lValue, rValue))
                {
                    returnVal = CompareDouble(Convert.ToDouble(lValue.Value), Convert.ToDouble(rValue.Value));
                }
                else
                {
                    returnVal = crNE;
                }
            }
            // compare ASSYATTR, ASSYVALUE, PARTATTR, PARTVALUE
            else
            {
                if (ConvertsToDbl(lValue, rValue))
                {
                    returnVal = CompareDouble(Convert.ToDouble(lValue.Value), Convert.ToDouble(rValue.Value));
                }
                else
                {
                    string l = lValue.Value.ToString();
                    string r = rValue.Value.ToString();
                    returnVal = CompareString(l, r);
                }
            }

            return returnVal;
        }

        private static int CompareDouble(double l, double r)
        {
            if (l == r)
                return crEQ;
            else if (l > r)
                return crGT;
            else if (l < r)
                return crLT;
            else
                return crNE;
        }

        private static int CompareString(string l, string r)
        {
            if (System.String.Compare(l, r) == 0)
                return crEQ;
            else if (System.String.Compare(l, r) > 0)
                return crGT;
            else if (System.String.Compare(l, r) < 0)
                return crLT;
            else
                return crNE;
        }
        #endregion

        #region Functions called by ConjunctionOperation
        public static Variant And(Variant var1, Variant var2)
        {
            Variant result = null;
            if (var1.Type == VariantType.BOOL && var2.Type == VariantType.BOOL)
            {
                if (((bool)var1.Value == true) && ((bool)var2.Value == true))
                    result = new Variant(true);
                else
                    result = new Variant(false);
            }
            else
                throw new Exception("Variants must both be boolean for 'and' operation.");
            return result;
        }
        public static Variant Or(Variant var1, Variant var2)
        {
            Variant result = null;
            if (var1.Type == VariantType.BOOL && var2.Type == VariantType.BOOL)
            {
                if (((bool)var1.Value == true) || ((bool)var2.Value == true))
                    result = new Variant(true);
                else
                    result = new Variant(false);
            }
            else
                throw new Exception("Variants must both be boolean for 'or' operation.");
            return result;
        }
        public static Variant Xor(Variant var1, Variant var2)
        {
            Variant result = null;
            if (var1.Type == VariantType.BOOL && var2.Type == VariantType.BOOL)
            {
                if (((bool)var1.Value == true) ^ ((bool)var2.Value == true))
                    result = new Variant(true);
                else
                    result = new Variant(false);
            }
            else
                throw new Exception("Variants must both be boolean for xor.");
            return result;
        }
        #endregion

        #region List Management
        public void addVariantToList(Variant newVariant)
        {
            // Steps to the end of the list.
            Variant variant = this;
            while (variant.next != null)
                variant = variant.next;

            // Puts the attached object as the new end of the list.
            variant.next = newVariant;

            // If "first" is null, this shuld be the first variant in list.
            if (first == null) first = this;

            if (newVariant.IsListType)
            {
                while (newVariant.next != null)
                {
                    newVariant = newVariant.next;
                    newVariant.first = first;
                }
            }
            variant.next.first = first;
        }
        public int listCount()
        {
            if (first == null)
                return 1;

            Variant variant = first;
            int counter = 1;
            while (variant.next != null)
            {
                variant = variant.next;
                counter++;
            }
            return counter;
        }
        #endregion

        #region Helper Functions
        public static int BitCombination(Variant phv1, Variant phv2)
        {
            double lBit = Math.Pow(2, Convert.ToDouble(phv1.Type));
            double rBit = Math.Pow(2, Convert.ToDouble(phv2.Type));
            return (Convert.ToInt32(lBit) | Convert.ToInt32(rBit));
        }

        private static bool ConvertsToInt(Variant phv1, Variant phv2)
        {
            if (phv1.Value.ToString().IndexOf(".") != -1 || phv2.Value.ToString().IndexOf(".") != -1)
                return false;

            try
            {
                Convert.ToInt32(phv1.Value);
                Convert.ToInt32(phv2.Value);
            }
            catch
            {
                return false;
            }

            return true;
        }

        private static bool ConvertsToDbl(Variant phv1)
        {
            try
            {
                Convert.ToDouble(phv1.Value);
            }
            catch
            {
                return false;
            }

            return true;
        }

        private static bool ConvertsToDbl(Variant phv1, Variant phv2)
        {
            try
            {
                Convert.ToDouble(phv1.Value);
                Convert.ToDouble(phv2.Value);
            }
            catch
            {
                return false;
            }

            return true;
        }
        #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