Click here to Skip to main content
15,892,298 members
Articles / Desktop Programming / Windows Forms

Evaluation Engine

Rate me:
Please Sign up or sign in to vote.
4.96/5 (164 votes)
1 Jun 2008CC (ASA 2.5)29 min read 306.3K   5K   376  
The Evaluation Engine is a parser and interpreter that can be used to build a Business Rules Engine. It allows for mathematical and boolean expressions, operand functions, variables, variable assignment, comments, and short-circuit evaluation. A syntax editor is also included.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace RulesCalculator
{
    public delegate void UpdateStatusDelegate(string Status, System.Drawing.Color BackColor);
    public delegate void DisplayErrorDelegate(string ErrorMsg);
    public delegate void TimingsDelegate(double TokenParseTime, double TotalTimeMS, int LoopCount, double TotalSeconds, double MIPS, double MIPM);

    public partial class Expression : UserControl
    {
        EvaluationEngine.Parser.Token token = null;

        public event UpdateStatusDelegate UpdateStatus;
        public event DisplayErrorDelegate DisplayError;
        public event TimingsDelegate Timings;

        public Expression()
        {
            InitializeComponent();
        }


        public void CreateTokens()
        {
            string ruleText = syntaxRichTextBox1.Text.Trim();
            if (String.IsNullOrEmpty(ruleText) == true) return;

            if (UpdateStatus != null) UpdateStatus("Processing, please wait...", Color.Green);

            if (token != null) token.Variables.Clear();
            token = new EvaluationEngine.Parser.Token(ruleText);

            ProcessTokens();
        }

        private void CallTimingEvent(double TokenParseTime, double TotalTimeMS, int LoopCount)
        {
            double totalSeconds = TotalTimeMS / 1000;
            double mips = (LoopCount / totalSeconds / 1000000);
            double mipm = mips * 60;

            if (Timings != null)
            {
                Timings(TokenParseTime, TotalTimeMS, LoopCount, totalSeconds, mips, mipm);
            }

        }

        private void ProcessTokens()
        {
            LoadListView(token);

            if (token.AnyErrors == true)
            {
                if (DisplayError != null) DisplayError("ERROR: " + token.LastErrorMessage);
                return;
            }

            if (token.Variables.Count > 0)
            {
                lblResult.Visible = false;
                lvVariables.Visible = true;
                grpVarResults.Visible = true;

                if (UpdateStatus != null) UpdateStatus("Successfully parsed the expression. Please specify the values for the " + token.Variables.Count.ToString() + " variables", SystemColors.ButtonFace);
                if (token.Variables.Count == 1)
                    this.lblResult.Text = "Successfully parsed the expression. Please specify the values for the " + token.Variables.Count.ToString() + " variable.";
                else
                    this.lblResult.Text = "Successfully parsed the expression. Please specify the values for the " + token.Variables.Count.ToString() + " variable.";

                this.tabControl1.SelectedTab = tabVariables;

                if (UpdateStatus != null) UpdateStatus("Success", SystemColors.ButtonFace);

                return;
            }
            else
            {
                lblResult.Visible = true;
                lvVariables.Visible = false;
                grpVarResults.Visible = false;
                butEvaluate_Click(null, null);
            }        

        }

        public EvaluationEngine.Parser.Token Token
        {
            get
            {
                return token;
            }
            set
            {
                token = value;
                syntaxRichTextBox1.Text = token.RuleSyntax;
                syntaxRichTextBox1.ProcessAllLines();

                ProcessTokens();
            }
        }


        private void LoadListView(EvaluationEngine.Parser.Token tokens)
        {
            listView1.Items.Clear();
            listView1.Columns.Clear();
            listView1.Columns.Add("Token");
            listView1.Columns.Add("Token Type");
            listView1.Columns.Add("Token Data Type");
            listView1.Columns.Add("Is Operator");
            listView1.Columns.Add("Is Operand");
            listView1.Columns.Add("In Operand Function");
            listView1.Columns.Add("Short Circuit");
            int colWidth = listView1.ClientSize.Width / 7;
            for (int i = 0; i < listView1.Columns.Count; i++) listView1.Columns[i].Width = colWidth;



            if (tokens.TokenItems != null)
            {
                foreach (EvaluationEngine.Parser.TokenItem item in tokens.TokenItems)
                {

                    ListViewItem lv = new ListViewItem(item.TokenName);

                    lv.SubItems.Add(item.TokenType.ToString());
                    lv.SubItems.Add(item.TokenDataType.ToString());
                    lv.SubItems.Add(item.TokenType == EvaluationEngine.Parser.TokenType.Token_Operator ? "true" : "false");
                    lv.SubItems.Add(item.TokenType == EvaluationEngine.Parser.TokenType.Token_Operand ? "true" : "false");
                    lv.SubItems.Add(item.InOperandFunction.ToString().ToLower());

                    if (item.CanShortCircuit == true)
                    {
                        lv.SubItems.Add("true");
                    }
                    else
                    {
                        lv.SubItems.Add("");
                    }

                    this.listView1.Items.Add(lv);
                }
            }

            listView2.Items.Clear();
            listView2.Columns.Clear();
            listView2.Columns.Add("Token");
            listView2.Columns.Add("Token Type");
            listView2.Columns.Add("Token Data Type");
            listView2.Columns.Add("Is Operator");
            listView2.Columns.Add("Is Operand");
            listView2.Columns.Add("In Operand Function");
            listView2.Columns.Add("Short Circuit");
            for (int i = 0; i < listView2.Columns.Count; i++) listView2.Columns[i].Width = colWidth;


            if (tokens.RPNQueue != null)
            {
                foreach (EvaluationEngine.Parser.TokenItem item in tokens.RPNQueue)
                {

                    ListViewItem lv = new ListViewItem(item.TokenName);

                    lv.SubItems.Add(item.TokenType.ToString());
                    lv.SubItems.Add(item.TokenDataType.ToString());
                    lv.SubItems.Add(item.TokenType == EvaluationEngine.Parser.TokenType.Token_Operator ? "true" : "false");
                    lv.SubItems.Add(item.TokenType == EvaluationEngine.Parser.TokenType.Token_Operand ? "true" : "false");
                    lv.SubItems.Add(item.InOperandFunction.ToString().ToLower());

                    if (item.CanShortCircuit == true)
                    {
                        lv.SubItems.Add("true");
                        this.listView2.Items.Add(lv);

                        
                        // loop throught and add the condition tokens
                        ListViewItem lv1 = null;
                        foreach (EvaluationEngine.Parser.TokenItem conditionItem in item.ShortCircuit.RPNCondition)
                        {
                            lv1 = new ListViewItem(conditionItem.TokenName);
                            lv1.SubItems.Add(conditionItem.TokenType.ToString());
                            lv1.SubItems.Add(conditionItem.TokenDataType.ToString());
                            lv1.SubItems.Add(conditionItem.TokenType == EvaluationEngine.Parser.TokenType.Token_Operator ? "true" : "false");
                            lv1.SubItems.Add(conditionItem.TokenType == EvaluationEngine.Parser.TokenType.Token_Operand ? "true" : "false");
                            lv1.SubItems.Add(conditionItem.InOperandFunction.ToString().ToLower());
                            lv1.SubItems.Add("");
                            lv1.BackColor = System.Drawing.Color.LightYellow;
                            this.listView2.Items.Add(lv1);
                        }
                       
                        // add a comma
                        lv1 = new ListViewItem(",");
                        lv1.SubItems.Add(EvaluationEngine.Parser.TokenType.Token_Operand_Function_Delimiter.ToString());
                        lv1.SubItems.Add(EvaluationEngine.Parser.TokenDataType.Token_DataType_None.ToString());
                        lv1.SubItems.Add("false");
                        lv1.SubItems.Add("false");
                        lv1.SubItems.Add("true");
                        this.listView2.Items.Add(lv1);

                        foreach (EvaluationEngine.Parser.TokenItem trueItem in item.ShortCircuit.RPNTrue)
                        {
                            lv1 = new ListViewItem(trueItem.TokenName);
                            lv1.SubItems.Add(trueItem.TokenType.ToString());
                            lv1.SubItems.Add(trueItem.TokenDataType.ToString());
                            lv1.SubItems.Add(trueItem.TokenType == EvaluationEngine.Parser.TokenType.Token_Operator ? "true" : "false");
                            lv1.SubItems.Add(trueItem.TokenType == EvaluationEngine.Parser.TokenType.Token_Operand ? "true" : "false");
                            lv1.SubItems.Add(trueItem.InOperandFunction.ToString().ToLower());
                            lv1.SubItems.Add("");
                            lv1.BackColor = System.Drawing.Color.LightGreen;
                            this.listView2.Items.Add(lv1);
                        }

                        // add a comma
                        lv1 = new ListViewItem(",");
                        lv1.SubItems.Add(EvaluationEngine.Parser.TokenType.Token_Operand_Function_Delimiter.ToString());
                        lv1.SubItems.Add(EvaluationEngine.Parser.TokenDataType.Token_DataType_None.ToString());
                        lv1.SubItems.Add("false");
                        lv1.SubItems.Add("false");
                        lv1.SubItems.Add("true");
                        this.listView2.Items.Add(lv1);

                        foreach (EvaluationEngine.Parser.TokenItem falseItem in item.ShortCircuit.RPNFalse)
                        {
                            lv1 = new ListViewItem(falseItem.TokenName);
                            lv1.SubItems.Add(falseItem.TokenType.ToString());
                            lv1.SubItems.Add(falseItem.TokenDataType.ToString());
                            lv1.SubItems.Add(falseItem.TokenType == EvaluationEngine.Parser.TokenType.Token_Operator ? "true" : "false");
                            lv1.SubItems.Add(falseItem.TokenType == EvaluationEngine.Parser.TokenType.Token_Operand ? "true" : "false");
                            lv1.SubItems.Add(falseItem.InOperandFunction.ToString().ToLower());
                            lv1.SubItems.Add("");
                            lv1.BackColor = System.Drawing.Color.LightPink;
                            this.listView2.Items.Add(lv1);
                        }

                    }
                    else
                    {
                        lv.SubItems.Add("");
                        this.listView2.Items.Add(lv);
                    }


                    
                }
            }


            // load the variables
            listView3.Items.Clear();
            listView3.Columns.Clear();
            listView3.Columns.Add("Variable");
            listView3.Columns.Add("Value");
            colWidth = listView1.ClientSize.Width / 2;
            for (int i = 0; i < listView3.Columns.Count; i++) listView3.Columns[i].Width = colWidth;

            lvVariables.Items.Clear();
            lvVariables.Columns.Clear();

            if (tokens.Variables != null)
            {
                lvVariables.Visible = true;

                for (int i = 0; i < tokens.Variables.Count; i++)
                {
                    ListViewItem lv = new ListViewItem(tokens.Variables[i].VariableName);

                    if (String.IsNullOrEmpty(tokens.Variables[i].VariableValue) == true)
                    {
                        lv.SubItems.Add("Double click to set the variable value");
                    }
                    else
                    {
                        lv.SubItems.Add("Single Value = " + tokens.Variables[i].VariableValue);
                    }

                    listView3.Items.Add(lv);


                    lvVariables.Columns.Add(tokens.Variables[i].VariableName);

                }


                lvVariables.Columns.Add("Result");

                colWidth = lvVariables.Width / (tokens.Variables.Count + 1);
                if (colWidth < 100) colWidth = 100;
                for (int i = 0; i < lvVariables.Columns.Count; i++) lvVariables.Columns[i].Width = colWidth - 5;
            }

        }

        private void butEvaluate_Click(object sender, EventArgs e)
        {
            // check that we have tokens
            if (token == null) return;
            if (token.AnyErrors == true)
            {
                if (DisplayError != null) DisplayError("ERROR: " + token.LastErrorMessage);
                return;
            }

            if (UpdateStatus != null) UpdateStatus("Processing, please wait...", Color.Green);

            // create the evaluation object
            EvaluationEngine.Evaluate.Evaluator eval = new EvaluationEngine.Evaluate.Evaluator(token);


            // run the evaluation
            string value = "";
            string ErrorMsg = "";
            if (eval.Evaluate(out value, out ErrorMsg) == false)
            {
                if (DisplayError != null) DisplayError("ERROR: " + ErrorMsg);
                return;
            }

            int displayCount = Convert.ToInt32(numericUpDown2.Value);
            if (token.Variables.Count > 0)
            {
                if (lvVariables.Items.Count < displayCount)
                {
                    AddResult(value);
                }
            }

            // set the ersults
            this.lblResult.Text = value;
            this.tabControl1.SelectedTab = tabResults;

            // update the interface
            double totalTime = token.TokenParseTime + eval.TokenEvalTime;
            CallTimingEvent(token.TokenParseTime, totalTime, 1);

            if (UpdateStatus != null) UpdateStatus("Success", SystemColors.ButtonFace);


        }

        private void butMultiple_Click(object sender, EventArgs e)
        {

            // check that we have tokens
            if (token == null) return;
            if (token.AnyErrors == true)
            {
                if (DisplayError != null) DisplayError("ERROR: " + token.LastErrorMessage);
                return;
            }

            if (UpdateStatus != null) UpdateStatus("Processing, please wait...", Color.Green);

            // create the evaluation object
            EvaluationEngine.Evaluate.Evaluator eval = new EvaluationEngine.Evaluate.Evaluator(token);

            this.Cursor = Cursors.WaitCursor;

            // run the evaluation
            string value = "";
            string ErrorMsg = "";

            int loopCount = Convert.ToInt32(numericUpDown1.Value);

            double totalEvaluation = 0;

            int displayCount = Convert.ToInt32(numericUpDown2.Value);
            
            for (int i = 0; i < loopCount; i++)
            {
                if (eval.Evaluate(out value, out ErrorMsg) == false)
                {
                    if (DisplayError != null) DisplayError("ERROR: " + ErrorMsg);
                    this.Cursor = Cursors.Default;
                    return;
                }
                totalEvaluation += eval.TokenEvalTime;


                if (token.Variables.Count > 0)
                {
                    if (lvVariables.Items.Count < displayCount)
                    {
                        AddResult(value);
                    }
                }
            }

            // set the results
            this.lblResult.Text = value;
            this.tabControl1.SelectedTab = tabResults;

            this.Cursor = Cursors.Default;

            // update the interface
            double totalTime = token.TokenParseTime + totalEvaluation;
            CallTimingEvent(token.TokenParseTime, totalTime, loopCount);

            if (UpdateStatus != null) UpdateStatus("Success", SystemColors.ButtonFace);

        }

        private void listView3_DoubleClick(object sender, EventArgs e)
        {
            if (listView3.SelectedItems.Count <= 0) return;

            // get the variable
            EvaluationEngine.Parser.Variable var = token.Variables[listView3.SelectedItems[0].Text];
            if (var == null)
            {
                MessageBox.Show("Failed to get the variable");
                return;
            }

            frmVariable frm = new frmVariable();
            frm.Variable = var;

            DialogResult result =  frm.ShowDialog(this);

            if (result == DialogResult.OK)
            {
                this.listView3.SelectedItems[0].SubItems[1].Text = "Single Value = " + var.VariableValue;                
            }

            frm.Close();
        }

        private void butClear_Click(object sender, EventArgs e)
        {
            lvVariables.Items.Clear();
        }

        private void AddResult(string Result)
        {
            ListViewItem item = new ListViewItem();

            for (int i = 0; i < token.Variables.Count; i++)
            {

                if (i == 0)
                {
                    //item.SubItems[i].Text = tokens.Variables[i].LastVariableValue;
                    item.SubItems[i].Text = token.Variables[i].VariableValue;
                }
                else
                {
                    //item.SubItems.Add(tokens.Variables[i].LastVariableValue);
                    item.SubItems.Add(token.Variables[i].VariableValue);
                }
                    
            }

            item.SubItems.Add(Result);

            lvVariables.Items.Add(item);
        }
    }
}

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 Creative Commons Attribution-ShareAlike 2.5 License


Written By
Software Developer (Senior)
United States United States
Developer

Comments and Discussions