예제 #1
0
            //嘗試解析, 成功回傳 true, 結果輸出到 result
            //除錯模式下進行嚴格解析,失敗回傳 false, 錯誤信息輸出到 result (EMPTY (空的表達式) INVALIDEXPR (無效的表達式) IMBALBRACKET (括號不平衡))
            //否則一概視為字串
            //Try to evaluate the expression, will output error message if failed
            static public bool TryParse(string _expr, out string result)
            {
                //首先把表達式前後多餘的空白去掉
                //Remove unnecessary spaces
                string expr = _expr.Trim();

                //空白表達式是無效的
                //No empty expression or empty quotation allowed
                if (expr == "" || expr.Trim('"') == "")
                {
                    result = "INVALIDEXPR";
                    return(false);
                }

                //宣告用來暫存運算值的變量
                //Store intermediate results
                string imd;
                string imd2;
                int    tmp;

                //用大 try 包起整個過程, 如果出錯, 就表示表達式有問題, 回傳 INVALIDEXPR 錯誤
                try
                {
                    //先檢查表達式是否單純的變數名
                    //如果是的話就從變量環境 Variables 讀取其值
                    //If it is just a variable, reply with the corresponding value of the variable
                    if (Variables.Exist(expr))
                    {
                        result = Variables.Read(expr);
                        return(true);
                    }

                    //如果表達式是單純的布林值或整數值, 就直接返回
                    //Return directly the value of TRUE, FALSE or integers
                    if (expr.Trim().ToUpper() == "TRUE" || expr.Trim().ToUpper() == "FALSE" || Int32.TryParse(expr, out tmp))
                    {
                        //這裡是用來去掉整數開頭多餘的零的, 不過如果表達式單純由零組成, 會變成空白, 所以要檢查一下
                        if (expr.Trim().TrimStart('0') != "") //if it is not purely consists of zeros then we can trim off zeros
                        {
                            result = expr.Trim().TrimStart('0');
                        }
                        else // otherwise just return 0
                        {
                            result = "0";
                        }
                        return(true);
                    }

                    //然後如果表達式沒那麼單純的話, 就開始作進一步處理

                    //首先要把引號內的內容保護好, 因為它們是單純的字串, 不應該進行運算
                    //如果不保護的話, 像 "3+1" 就會被運算成 "4" 了
                    #region Quotation accounting
                    //First go through quotation marks to check which operators are in fact part
                    //of a string and should not be split/parsed

                    //這是用來記錄哪些字符是在引號保護之下的
                    List <int> InvalidOp = new List <int>();  //stores the index of operators that should be ignored

                    //檢查表達式是否含有引號, 沒有的話就跳過這部分
                    if (expr.Contains("\""))
                    {
                        //這是一個狀態變量, 表示當前的字符是否在引號之內
                        bool inStr = false;

                        //對表達式的每一個字符進行檢查
                        for (int i = 0; i < expr.Length; i++)
                        {
                            //如果是引號, 就變更狀態變量 inStr
                            //如果現在是在引號內 (inStr=true), 遇到引號就表示引號的範圍已結束,所以就改成 false
                            //反之亦然, 但是這樣的話引號就不能出現在字串之中了
                            //解決方法是利用一個特殊的系統變量 {QUOT}
                            if (expr[i] == '"')
                            {
                                inStr = !inStr;
                            }
                            else if (inStr)
                            {
                                InvalidOp.Add(i);
                            }
                        }
                    }

                    #endregion

                    //然後是括號, 先從最淺和最後出現的括號處理起, 歸遞利用 TryParse, 把最深的括號優先進行運算 (歸遞下降 Recursive Descent)
                    #region Parenthesis accounting

                    //先檢查是否有括號, 沒有的話就跳過這一部分
                    if (expr.Contains('('))
                    {
                        //記數用的變量, 開括號+1, 閉括號-1, 如果最後不是零就表示括號不平衡
                        int bracketCount = 0;

                        //用來暫存括號內的內容的變量
                        string content = "";

                        //狀態變量, 記錄現在是否在括號內
                        bool inBrac = false;

                        //一個一個字符地檢查
                        for (int i = 0; i < expr.Length; i++)
                        {
                            //如果是開括號, 而且不在引號保護之中
                            if (expr[i] == '(' && !InvalidOp.Contains(i))
                            {
                                //如果現在括號是平衡的, 清空內容 (比如 (3+1)+(2+1), 在碰第二個開括號時應該清掉 content 裡原有的 3+1)
                                if (bracketCount == 0)
                                {
                                    content = "";
                                }
                                //否則表示這個括號也在括號之中, 把它加進內容 (比如 ((3+1)+2), content = (3+1)+2 )
                                else
                                {
                                    content += "(";
                                }
                                //無論如何, 因為是開括號, 所以記數加一
                                bracketCount++;

                                //無論原來是不是在括號內, 開括號以後就必定是括號內了, 所以設狀態為 true
                                inBrac = true;
                            }
                            //如果是閉括號, 而且不在引號保護之中
                            else if (expr[i] == ')' && !InvalidOp.Contains(i))
                            {
                                //先記數減一
                                bracketCount--;

                                //如果關了這個括號後,記數平衡了, 就表示現在已經是括號外了
                                if (bracketCount == 0)
                                {
                                    inBrac = false;
                                }
                                //否則表示這個括號也在括號之中, 把它加進內容
                                else
                                {
                                    content += ")";
                                }
                            }
                            //對於其他的東西, 如果是在括號之中, 把它加進內容
                            else if (inBrac)
                            {
                                content += expr[i].ToString();
                            }
                        }

                        //整個表達式處理完畢後, 就檢查括號是否平衡
                        if (bracketCount == 0)  //brackets are balanced
                        {
                            //如果是的話, 就把最後記錄到的括號的內容進行運算, 再用其值替換掉本來的表達式, 然後再進行運算
                            if (TryParse(content, out imd))
                            {
                                return(TryParse(expr.Replace("(" + content + ")", imd), out result));
                            }
                        }
                        else
                        {
                            result = "IMBALBRACKET";
                            return(false);
                        }
                    }
                    #endregion

                    //逗號分割
                    if (expr.Contains(",") && !InvalidOp.Contains(expr.IndexOf(",")))
                    {
                        //先分割成左右兩部分
                        string LHS = expr.Split(',')[0];
                        string RHS = expr.Replace(LHS + ",", "").Trim();
                        LHS = LHS.Trim();

                        if (TryParse(LHS, out imd) && TryParse(RHS, out imd2))
                        {
                            result = expr.Replace(LHS, imd).Replace(RHS, imd2);
                            return(true);
                        }
                    }

                    //如果所有括號都處理掉了,就進行邏輯運算
                    #region Logical operators spliting and (include "|" operator for strings)

                    if (expr.Contains("&") && !InvalidOp.Contains(expr.IndexOf("&")))
                    {
                        if (TryParse(expr.Split('&')[0], out imd) && TryParse(expr.Replace(expr.Split('&')[0] + "&", ""), out imd2))
                        {
                            result = Convert.ToString(Convert.ToBoolean(imd) && Convert.ToBoolean(imd2));
                            return(true);
                        }
                    }

                    //除了邏輯或, 還可以是隨機運算符
                    if (expr.Contains("|") && !InvalidOp.Contains(expr.IndexOf("|")))
                    {
                        if (TryParse(expr.Split('|')[0], out imd) && TryParse(expr.Replace(expr.Split('|')[0] + "|", ""), out imd2))
                        {
                            bool chk1, chk2;
                            //看看是不是布林值
                            if (Boolean.TryParse(imd, out chk1) && Boolean.TryParse(imd2, out chk2))
                            {
                                result = Convert.ToString(Convert.ToBoolean(imd) || Convert.ToBoolean(imd2));
                                return(true);
                            }
                            //不是的話就是隨機運算符了
                            else
                            {
                                if (rndGen.NextDouble() >= rndGen.NextDouble())
                                {
                                    result = imd;
                                    return(true);
                                }
                                else
                                {
                                    result = imd2;
                                    return(true);
                                }
                            }
                        }
                    }

                    if (expr.Contains("=") && !InvalidOp.Contains(expr.IndexOf("=")))
                    {
                        //here we insert three branches to check for !=, >= and <=
                        if (expr.Split('=')[0].EndsWith("!"))   // !=
                        {
                            if (TryParse(expr.Split('=')[0].TrimEnd('!'), out imd) && TryParse(expr.Replace(expr.Split('=')[0] + "=", ""), out imd2))
                            {
                                result = Convert.ToString(imd != imd2);
                                return(true);
                            }
                        }

                        if (expr.Split('=')[0].EndsWith(">")) // >=
                        {
                            if (TryParse(expr.Split('=')[0].TrimEnd('>'), out imd) && TryParse(expr.Replace(expr.Split('=')[0] + "=", ""), out imd2))
                            {
                                result = Convert.ToString(Convert.ToInt32(imd) >= Convert.ToInt32(imd2));
                                return(true);
                            }
                        }

                        if (expr.Split('=')[0].EndsWith("<")) // <=
                        {
                            if (TryParse(expr.Split('=')[0].TrimEnd('<'), out imd) && TryParse(expr.Replace(expr.Split('=')[0] + "=", ""), out imd2))
                            {
                                result = Convert.ToString(Convert.ToInt32(imd) <= Convert.ToInt32(imd2));
                                return(true);
                            }
                        }


                        //nothing is preceding '=' so it is just a normal equality check
                        if (TryParse(expr.Split('=')[0], out imd) && TryParse(expr.Replace(expr.Split('=')[0] + "=", ""), out imd2))
                        {
                            result = Convert.ToString(imd == imd2);
                            return(true);
                        }
                    }

                    if (expr.Contains(">") && !InvalidOp.Contains(expr.IndexOf(">")))
                    {
                        if (TryParse(expr.Split('>')[0], out imd) && TryParse(expr.Replace(expr.Split('>')[0] + ">", ""), out imd2))
                        {
                            result = Convert.ToString(Convert.ToInt32(imd) > Convert.ToInt32(imd2));
                            return(true);
                        }
                    }

                    if (expr.Contains("<") && !InvalidOp.Contains(expr.IndexOf("<")))
                    {
                        if (TryParse(expr.Split('<')[0], out imd) && TryParse(expr.Replace(expr.Split('<')[0] + "<", ""), out imd2))
                        {
                            result = Convert.ToString(Convert.ToInt32(imd) < Convert.ToInt32(imd2));
                            return(true);
                        }
                    }
                    #endregion

                    //邏輯運算之後就是加減乘除的運算
                    #region String concatenation and arithmetic operators spliting

                    if (expr.Contains("+") && !InvalidOp.Contains(expr.IndexOf("+")))
                    {
                        //加號因為同時是算術加法和字串合併, 所以要先做檢查, 判斷何者適用
                        bool isStrCat = false;

                        //先分割成左右兩部分
                        string LHS = expr.Split('+')[0];
                        string RHS = expr.Replace(LHS + "+", "").Trim();
                        LHS = LHS.Trim();

                        //每一邊進行檢查, 如是包括在引號之中的, 表示是字串, 應該用字串合並
                        if (LHS.StartsWith("\"") && LHS.EndsWith("\""))
                        {
                            isStrCat = true;
                        }
                        if (RHS.StartsWith("\"") && RHS.EndsWith("\""))
                        {
                            isStrCat = true;
                        }

                        if (TryParse(LHS, out imd) && TryParse(RHS, out imd2))
                        {
                            if (isStrCat)
                            {
                                result = imd + imd2;
                                return(true);
                            }

                            //如果沒有明示是字串的話, 嘗試把兩邊都當成整數, 如果不行就表示是簡單的字串合併
                            try
                            {
                                result = Convert.ToString(Convert.ToInt32(imd) + Convert.ToInt32(imd2));
                            }
                            catch
                            {
                                result = imd + imd2;
                            }
                            return(true);
                        }
                    }

                    if (expr.Contains("-") && !InvalidOp.Contains(expr.IndexOf("-")))
                    {
                        if (TryParse(expr.Split('-')[0], out imd) && TryParse(expr.Replace(expr.Split('-')[0] + "-", ""), out imd2))
                        {
                            result = Convert.ToString(Convert.ToInt32(imd) - Convert.ToInt32(imd2));
                            return(true);
                        }
                    }

                    if (expr.Contains("*") && !InvalidOp.Contains(expr.IndexOf("*")))
                    {
                        if (TryParse(expr.Split('*')[0], out imd) && TryParse(expr.Replace(expr.Split('*')[0] + "*", ""), out imd2))
                        {
                            result = Convert.ToString(Convert.ToInt32(imd) * Convert.ToInt32(imd2));
                            return(true);
                        }
                    }

                    if (expr.Contains("/") && !InvalidOp.Contains(expr.IndexOf("/")))
                    {
                        if (TryParse(expr.Split('/')[0], out imd) && TryParse(expr.Replace(expr.Split('/')[0] + "/", ""), out imd2))
                        {
                            if (Convert.ToInt32(imd2) != 0)
                            {
                                result = Convert.ToString(Math.Round((double)Convert.ToInt32(imd) / Convert.ToInt32(imd2), 0));
                            }
                            else
                            {
                                result = "DivBy0";
                            }

                            return(true);
                        }
                    }

                    #endregion

                    //最後就是檢查邏輯"非"運算
                    //Negation spliting
                    if (expr.StartsWith("!"))
                    {
                        if (TryParse(expr.Trim('!'), out imd))
                        {
                            result = Convert.ToString(!Convert.ToBoolean(imd));
                            return(true);
                        }
                    }

                    //如果甚麼都不是, 就單純的認為是一個字串
                    //如果前後有引號包好的話, 就去掉引號
                    //前後有引號包好不一定就是單純的字串,例如 "ABC"+"DEF"
                    //所以放到最後才來檢查, 確保沒有漏掉任何運算
                    //If all things fail, treat as a simple string
                    //For properly quoted string, quotation marks are removed
                    if (expr.StartsWith("\"") && expr.EndsWith("\""))
                    {
                        result = expr.Trim('"');
                        return(true);
                    }
                    else
                    {
                        result = expr;
                        return(true);
                    }
                }
                catch
                {
                    //如果失敗的話就返回無效的表達式錯誤
                    result = "INVALIDEXPR";
                    return(false);
                }
            }