예제 #1
0
        // This function will go through a pre-compiled expression and resolve all periods (e.g. "yem#y + 300#y" => "yem#y + 300/12")
        void ResolvePeriods(ref Calculator_Expression exp)
        {
            if (exp is BinaryExpression)        // if binary, look for footnote
            {
                if (exp._token.ID == TKID.FOOT) // if there is one, copy it to the parts and continue deeper
                {
                    if ((exp as BinaryExpression)._rgt is VariableExpression)
                    {
                        string foot = ((exp as BinaryExpression)._rgt as VariableExpression)._name;
                        if (periods.ContainsKey("#" + foot))  // got a period footnote
                        {
                            // consider better handling or better message
                            if (!((exp as BinaryExpression)._lft is Calculator_Expression))
                            {
                                throw new Exception("Period footnotes are only allowed on numbers.");
                            }

                            // reduce the usage count
                            _vars[foot]--;
                            // if no usage left, then remove variable
                            if (_vars[foot] == 0)
                            {
                                _vars.Remove(foot);
                            }

                            exp = (exp as BinaryExpression)._lft;
                            exp._token.Value = double.Parse(exp._token.Value.ToString()) * periods["#" + foot];
                        }
                        else
                        {
                            // consider better handling or better message
                            throw new Exception($"Unrecognised period footnote '{foot}'.");
                        }
                    }
                    else    // else dig deeper
                    {
                        ResolvePeriods(ref (exp as BinaryExpression)._lft);
                        ResolvePeriods(ref (exp as BinaryExpression)._rgt);
                    }
                }
                else    // else dig deeper
                {
                    ResolvePeriods(ref (exp as BinaryExpression)._lft);
                    ResolvePeriods(ref (exp as BinaryExpression)._rgt);
                }
            }
            else if (exp is UnaryExpression) // if not binary, just dig deeper
            {
                ResolvePeriods(ref (exp as UnaryExpression)._expr);
            }
            else if (exp is FunctionExpression)
            {
                for (int i = 0; i < (exp as FunctionExpression)._parms.Count; i++)
                {
                    Calculator_Expression expr = (exp as FunctionExpression)._parms[i];
                    ResolvePeriods(ref expr);
                    (exp as FunctionExpression)._parms[i] = expr;
                }
            }
        }
예제 #2
0
 // This function will go through a pre-compiled expression and propagate _level footnotes (e.g. "{yem + bch > 400}#1" => "{yem#1 + bch#1 > 400}")
 void PropagateFootnotes(ref Calculator_Expression exp)
 {
     if (exp is BinaryExpression)        // if binary, look for footnote
     {
         if (exp._token.ID == TKID.FOOT) // if there is one, copy it to the parts and continue deeper
         {
             string foot = (exp as BinaryExpression)._rgt._token.Value.ToString();
             exp = (exp as BinaryExpression)._lft;
             CopyFootnote(ref exp, foot);
             PropagateFootnotes(ref exp);
         }
         else    // else dig deeper
         {
             PropagateFootnotes(ref (exp as BinaryExpression)._lft);
             PropagateFootnotes(ref (exp as BinaryExpression)._rgt);
         }
     }
     else if (exp is UnaryExpression) // if not binary, just dig deeper
     {
         PropagateFootnotes(ref (exp as UnaryExpression)._expr);
     }
     else if (exp is FunctionExpression)
     {
         for (int i = 0; i < (exp as FunctionExpression)._parms.Count; i++)
         {
             Calculator_Expression expr = (exp as FunctionExpression)._parms[i];
             PropagateFootnotes(ref expr);
             (exp as FunctionExpression)._parms[i] = expr;
         }
     }
 }
 public override Calculator_Expression Optimize()
 {
     _expr = _expr.Optimize();
     return(_expr._token.Type == TKTYPE.LITERAL
         ? new Calculator_Expression(this.Evaluate(null))
         : this);
 }
 public override Calculator_Expression Optimize()
 {
     _lft = _lft.Optimize();
     _rgt = _rgt.Optimize();
     return(_lft._token.Type == TKTYPE.LITERAL && _rgt._token.Type == TKTYPE.LITERAL
         ? new Calculator_Expression(this.Evaluate(null))
         : this);
 }
예제 #5
0
        /// <summary>
        /// Evaluates an expression.
        /// </summary>
        /// <param name="operants">The formula variables with their values.</param>
        /// <param name="expression">The expression to evaluate.</param>
        /// <returns>The value of the expression.</returns>
        public double CalculateExpression(Dictionary <string, double> operands, Calculator_Expression expression)
        {
            var x = expression.Evaluate(operands);

            if (x is bool)
            {
                return((bool)x ? 1.0 : 0.0);
            }
            return((double)x);
        }
예제 #6
0
        Calculator_Expression ParseAtom()
        {
            string id;
            Calculator_Expression x     = null;
            FunctionDefinition    fnDef = null;

            switch (_token.Type)
            {
            // literals
            case TKTYPE.LITERAL:
                x = new Calculator_Expression(_token);
                break;

            // identifiers
            case TKTYPE.IDENTIFIER:

                // get identifier
                id = (string)_token.Value;

                // look for functions
                if (_fnTbl.TryGetValue(id, out fnDef))
                {
                    var p    = GetParameters();
                    var pCnt = p == null ? 0 : p.Count;
                    if (fnDef.ParmMin != -1 && pCnt < fnDef.ParmMin)
                    {
                        Throw($"Too few parameters for function {id}.");
                    }
                    if (fnDef.ParmMax != -1 && pCnt > fnDef.ParmMax)
                    {
                        Throw($"Too many parameters for function {id}.");
                    }
                    x = new FunctionExpression(fnDef, p);
                    break;
                }

                if (!_vars.ContainsKey(id))
                {
                    _vars.Add(id, 1);                               // if variable does not exist, add with value 1
                }
                else
                {
                    _vars[id]++;                                    // else increase value by 1. At this point, value counts usage.
                }
                x = new VariableExpression(_vars, id);
                break;

            // sub-expressions
            case TKTYPE.GROUP:

                // anything other than opening parenthesis is illegal here
                if (_token.ID != TKID.OPEN)
                {
                    Throw("Expression expected.");
                }

                // get expression
                GetToken();
                x = ParseOr();

                // check that the parenthesis was closed
                if (_token.ID != TKID.CLOSE)
                {
                    Throw("Unbalanced parenthesis.");
                }

                break;

            // logical sub-expressions
            case TKTYPE.LGROUP:
                if (!isCond)
                {
                    Throw("Curly brackets are only allowed within condition formulas.");
                }
                // anything other than opening parenthesis is illegal here
                if (_token.ID != TKID.LOPEN)
                {
                    Throw("Expression expected.");
                }

                // get expression
                GetToken();
                x = ParseOr();

                // check that the parenthesis was closed
                if (_token.ID != TKID.LCLOSE)
                {
                    Throw("Unbalanced parenthesis.");
                }

                break;
            }

            // make sure we got something...
            if (x == null)
            {
                Throw($"Unexpected token '{_token.Value}' found in position {_ptr} of the formula.");
            }

            // done
            GetToken();
            return(x);
        }
예제 #7
0
 /// <summary>
 /// Evaluates an expression.
 /// </summary>
 /// <param name="expression">Expression to evaluate.</param>
 /// <returns>The value of the expression.</returns>
 public double Evaluate(Calculator_Expression expression)
 {
     return((double)expression.Evaluate(null));
 }
예제 #8
0
        // This function will go through a pre-compiled expression and try to super-optimize it
        void OptimizeExpression(ref Calculator_Expression exp)
        {
            if (exp is BinaryExpression)        // optimize BinaryExpressions
            {
                Calculator_Expression lft = (exp as BinaryExpression)._lft;
                Calculator_Expression rgt = (exp as BinaryExpression)._rgt;

                OptimizeExpression(ref (exp as BinaryExpression)._lft);
                OptimizeExpression(ref (exp as BinaryExpression)._rgt);


                // if this is a multiplication try to optimize:
                //  - multiplications by 0
                //  - multiplications by 1 (e.g. due to #m footnotes)
                //  - multiplications where the order can be changed to bring literals together (e.g. "2 * b * 3" = "2 * 3 * b" = "6 * b")
                if (exp._token.ID == TKID.MUL)
                {
                    if (lft._token.Type == TKTYPE.LITERAL)      // check if the left side is literal
                    {
                        if ((double)lft._token.Value == 1)      // if 1, return the right side
                        {
                            exp = rgt;
                            return;
                        }
                        if ((double)lft._token.Value == 0)      // if 0, return 0
                        {
                            exp = new Calculator_Expression(new Token(0.0, TKID.ATOM, TKTYPE.LITERAL));
                            return;
                        }
                        if (rgt._token.ID == TKID.MUL)          // check if the right side is also a multiplication, maybe we can move literals around
                        {
                            if ((rgt as BinaryExpression)._lft._token.Type == TKTYPE.LITERAL)
                            {
                                (exp as BinaryExpression)._lft._token.Value = (double)lft._token.Value * (double)(rgt as BinaryExpression)._lft._token.Value;
                                (exp as BinaryExpression)._rgt = (rgt as BinaryExpression)._rgt;
                            }
                            if ((rgt as BinaryExpression)._rgt._token.Type == TKTYPE.LITERAL)
                            {
                                (exp as BinaryExpression)._lft._token.Value = (double)lft._token.Value * (double)(rgt as BinaryExpression)._rgt._token.Value;
                                (exp as BinaryExpression)._rgt = (rgt as BinaryExpression)._lft;
                            }
                        }
                    }
                    if (rgt._token.Type == TKTYPE.LITERAL)      // check if the right side is literal
                    {
                        if ((double)rgt._token.Value == 1)      // if 1, return the right side
                        {
                            exp = lft;
                            return;
                        }
                        if ((double)rgt._token.Value == 0)      // if 0, return 0
                        {
                            exp = new Calculator_Expression(new Token(0.0, TKID.ATOM, TKTYPE.LITERAL));
                            return;
                        }
                        if (lft._token.ID == TKID.MUL)          // check if the left side is also a multiplication, maybe we can move literals around
                        {
                            if ((lft as BinaryExpression)._lft._token.Type == TKTYPE.LITERAL)
                            {
                                (exp as BinaryExpression)._rgt._token.Value = (double)rgt._token.Value * (double)(lft as BinaryExpression)._lft._token.Value;
                                (exp as BinaryExpression)._lft = (lft as BinaryExpression)._rgt;
                            }
                            if ((lft as BinaryExpression)._rgt._token.Type == TKTYPE.LITERAL)
                            {
                                (exp as BinaryExpression)._rgt._token.Value = (double)rgt._token.Value * (double)(lft as BinaryExpression)._rgt._token.Value;
                                (exp as BinaryExpression)._lft = (lft as BinaryExpression)._lft;
                            }
                        }
                    }
                }
            }
            exp.Optimize(); // finally, perform standard optimization again for any remaining calculations
        }
예제 #9
0
 // helper function for PropagateFootnotes
 void CopyFootnote(ref Calculator_Expression exp, string foot)
 {
     if (exp is VariableExpression)  // if variable, then copy footnote
     {
         // get the variable name
         string v = (exp as VariableExpression)._name;
         // reduce the usage count
         _vars[v]--;
         // if no usage left, then remove variable
         if (_vars[v] == 0)
         {
             _vars.Remove(v);
         }
         // build new name
         v += "#" + foot;
         // add new variable or increase usage count
         if (_vars.ContainsKey(v))
         {
             _vars[v]++;
         }
         else
         {
             _vars.Add(v, 1);
         }
         // make sure the token is also updated
         (exp as VariableExpression)._name = v;
     }
     else if (exp is BinaryExpression)   // else dig deeper
     {
         Calculator_Expression lft = (exp as BinaryExpression)._lft;
         Calculator_Expression rgt = (exp as BinaryExpression)._rgt;
         if (lft is VariableExpression || lft is BinaryExpression || lft is UnaryExpression || lft is FunctionExpression)
         {
             CopyFootnote(ref lft, foot);
         }
         if (rgt is VariableExpression || rgt is BinaryExpression || rgt is UnaryExpression || rgt is FunctionExpression)
         {
             CopyFootnote(ref rgt, foot);
         }
     }
     else if (exp is UnaryExpression)
     {
         Calculator_Expression expr = (exp as UnaryExpression)._expr;
         if (expr is VariableExpression || expr is BinaryExpression || expr is UnaryExpression || expr is FunctionExpression)
         {
             CopyFootnote(ref expr, foot);
         }
     }
     else if (exp is FunctionExpression)
     {
         for (int i = 0; i < (exp as FunctionExpression)._parms.Count; i++)
         {
             Calculator_Expression expr = (exp as FunctionExpression)._parms[i];
             if (expr is VariableExpression || expr is BinaryExpression || expr is UnaryExpression || expr is FunctionExpression)
             {
                 CopyFootnote(ref expr, foot);
             }
         }
     }
     else
     {
         Throw("Invalid use of footnote.");
     }
 }
 // ** ctor
 public BinaryExpression(Token tk, Calculator_Expression exprLeft, Calculator_Expression exprRight) : base(tk)
 {
     _lft = exprLeft;
     _rgt = exprRight;
 }
 // ** ctor
 public UnaryExpression(Token tk, Calculator_Expression expr) : base(tk)
 {
     _expr = expr;
 }