程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> 仿查詢分析器的C#計算器——3.詞法分析

仿查詢分析器的C#計算器——3.詞法分析

編輯:關於C#

承接上一篇,這一篇講如何把表達式轉換成記號對象,這裡就涉及到了編譯原理中的詞法分析。關於編譯原理我不想多講,畢竟我自己也不 怎麼熟悉,現在只知道其中有個有限自動機的概念。不管什麼概念,用代碼實現才是最終目標。

因為不清楚字符串中到底包含什麼字符,只能一個個字符進行處理,采用循環一次次向後取一個字符進行判斷。這裡建立一個TokenFactory 記號“工廠”類,由這個類負責對表達式進行分析並“生產”出TokenRecord對象。其中包括兩個方法, LexicalAnalysis和ProduceToken。LexicalAnalysis用於詞法分析,分析到符合規則的記號對象後調用ProduceToken方法,“生產 ”出對應的TokenRecord對象。這裡偷了一點懶,把所有方法全部寫成了static,這樣就不用實例化多個子類了。

從這個類衍生出多個子類:

TokenKeywordFactory:用於處理關鍵字

TokenSymbolFactory:用於處理運算符

TokenStringFactory:用於處理字符串

TokenNumberFactory:用於處理數字

類圖如下:

分析表達式的入口只有一個,就是TokenFactory中的LexicalAnalysis。TokenFactory類的代碼如下:

internal class TokenFactory
    {
        /// <summary>
        /// 產生記號對象
        /// </summary>
        /// <param name="TokenWord">記號對應的字符串</param>
        /// <param name="Index">記號開始的列序號</param>
        /// <returns>記號對象</returns>
        protected static TokenRecord ProduceToken(string TokenWord, int Index)
        {
            throw new Exception("必須在子類中實現方法");
        }

        /// <summary>
        /// 詞法分析
        /// </summary>
        /// <param name="TokenList">記號對象列表</param>
        /// <param name="Code">表達式</param>
        /// <param name="Index">列序號</param>
        public static void LexicalAnalysis(List<TokenRecord> TokenList, string Code, ref int Index)
        {
            char chrTemp;//臨時字符

            for (int intIndex = 0; intIndex < Code.Length; intIndex++)
            {
                chrTemp = Code[intIndex];//獲取當前字符

                //關鍵字分析
                if (char.IsLetter(chrTemp))//如果當前字符為字母,進行關鍵字處理
                {
                     TokenKeywordFactory.LexicalAnalysis(TokenList, Code, ref intIndex);
                }
                else if (chrTemp.Equals('\'') || chrTemp.Equals('"'))//如果是字符串分隔符,進行字符串處理
                {
                    TokenStringFactory.LexicalAnalysis(TokenList, Code, ref intIndex);
                }
                else if (char.IsDigit(chrTemp))//數值處理
                {
                    TokenNumberFactory.LexicalAnalysis(TokenList, Code, ref intIndex);
                }
                else if (TokenSymbolFactory.GetSymbolDictionary().ContainsKey(chrTemp.ToString()))//運算符處理
                {
                    //有些運算符為雙字符,但這裡所有的雙字符運算符的前一個字符都有對應的單字符運算符,可以 只考慮一個字符
                    TokenSymbolFactory.LexicalAnalysis(TokenList, Code, ref intIndex);
                }
                else if (chrTemp.Equals(' '))
                {
                    //如果是空格,則忽略不處理
                }
                else//錯誤處理
                {
                    //拋出錯誤
                    throw new SyntaxException(intIndex, 1, "包含不合法字符:" + chrTemp.ToString());
                }
            }//for
        }//LexicalAnalysis

        /// <summary>
        /// 獲取操作記號字典
        /// </summary>
        /// <param name="OperateTokenType">操作記號類型</param>
        /// <returns>操作記號字典</returns>
        /// <remarks>Author:Alex Leo; Date:2008-5-19; Remark:基於本地文件TokenRecord.xml;</remarks>
        internal static SortedDictionary<string, string> GetOperateTokenDictionary(OperateTokenTypeEnum OperateTokenType)
        {
            SortedDictionary<string, string> myDictionary = new SortedDictionary<string, string>();

            if (myDictionary.Count == 0)
            {
                XmlDocument myDoc = new XmlDocument();
                myDoc.Load("./TokenRecord.xml");
                XmlNodeList KeywordList = myDoc.SelectNodes(string.Format("TokenRecord/{0}/Token",
                    Enum.GetName(typeof(OperateTokenTypeEnum),OperateTokenType)));

                foreach (XmlNode Node in KeywordList)
                {
                    myDictionary.Add(Node.Attributes["Word"].Value, Node.Attributes ["Class"].Value);
                }
            }

            return myDictionary;
        }//GetOperateTokenDictionary

    }//classTokenFactory

在LexicalAnalysis方法中,只需要判斷當前字符是否符合一定的起始規則,如果符合起始規則,就交給對應的工廠類去處理。

下面用例子來講吧,比如123.3*2-(24+34),分析成記號對象如下:

記號對象

對應表達式

TokenValue

123.3

TokenMultiply

*

TokenValue

2

TokenMinus

-

TokenLeftBracket

(

TokenValue

24

TokenPlus

+

TokenValue

34

TokenRightBracket

)

這裡的處理過程是

1.取字符“1”,轉到TokenNumberFactory,把分析取到的字符串“123.3”轉換為TokenNumber並存到TokenList中

2.取字符“*”,轉到TokenSymbolFactory,把“*”轉換成TokenMultiply並存到TokenList中

3.取字符“2”,轉到TokenNumberFactory,把分析取到的字符串“2”轉換為TokenNumber並存到TokenList中

4.取字符“- ”,轉到TokenSymbolFactory,把“-”轉換成TokenMinus並存到TokenList中

5.取字符“( ”,轉到TokenSymbolFactory,把“(”轉換成TokenLeftBracket並存到TokenList中

6.取字符“2”,轉到TokenNumberFactory,把分析取到的字符串“24”轉換為TokenNumber並存到TokenList中

7.取字符“+”,轉到TokenSymbolFactory,把“+”轉換成TokenPlus並存到TokenList中

8.取字符“3”,轉到TokenNumberFactory,把分析取到的字符串“34”轉換為TokenNumber並存到TokenList中

9.取字符“) ”,轉到TokenSymbolFactory,把“)”轉換成TokenRightBracket並存到TokenList中

至於各個工廠類中怎麼分析提取出對應的字符串,則有各自不同的規則。如果符合規則就繼續向後分析,否則代表分析結束,然後從源字符 串中截取開始分析的序號到結束分析的序號之間的字符串即可。這裡的Index參數相當於C++中的指針,指示當前分析到哪一個字符。因為各個 “工廠”類需要在分析完一個記號後將指針後移,這裡就將Index設置為ref類型。

另一個方法GetOperateTokenDictionary是用來獲取記號字典的,字典的Key是運算符和關鍵字,Value是對應的類名稱。在分析中遇到運算 符和關鍵字的時候,通過查詢字典就可以獲取對應的類名稱,然後通過反射生成類的實例,這樣就可以靈活將操作符和類對應起來。字典的來 源是本地的一個XML文件,當新增一個操作符的時候,到XML文件裡注冊一下,程序就可以識別出新操作符了,“工廠”類不需要做 任何修改。如果需要修改操作符,可以直接在XML文件裡面修改,程序也能識別,比如把mid改成substring,程序照樣可以運行。這就是 “依賴注入”的實際應用。

這裡需要使用的XML如下:

<?xml version="1.0" encoding="utf-8" ?>
<TokenRecord>
  <TokenKeyword>
    <!--以下字符串處理函數記號對象-->
    <Token Word="mid" Class="TokenMid" Example="mid('abcdefg',2,3) = 'bcd'" />
    <Token Word="left" Class="TokenLeft" Example="left('abcdefg',5) = 'abcde'" />
    <Token Word="right" Class="TokenRight" Example="right('abcdefg',5) = 'cdefg'" />
    <Token Word="string" Class="TokenToString" Example="string(53.6) = '53.6'" />
    <!--以下為數學運算記號對象-->
    <Token Word="round" Class="TokenRound" Example="round(0.12345,3) = 0.123" />
    <Token Word="abs" Class="TokenAbs" Example="abs(-5) = 5" />
    <Token Word="max" Class="TokenMax" Example="max(3,5) = 5" />
    <Token Word="min" Class="TokenMin" Example="min(3,5) = 3" />
    <!--如果希望取余采用VB的Mod函數,形如Mod(5.4,2) = 1.4,將TokenMod修改為繼承自TokenMethod即可,此時用%必須形如% (5.4,2)-->
    <!--<Token Word="mod" Class="TokenMod" Example="5.4 mod 2 = 1.4,mod後必須帶空格" />-->
    <Token Word="pow" Class="TokenPow" Example="pow(2,3) = 8" />
    <!--以下為三角函數記號對象,均采用角度計算而非弧度-->
    <Token Word="sin" Class="TokenSin" Example="sin(30) = 0.5" />
    <Token Word="asin" Class="TokenAsin" Example="asin(0.5) = 30" />
    <Token Word="cos" Class="TokenCos" Example="cos(60) = 0.5" />
    <Token Word="acos" Class="TokenAcos" Example="acos(0.5) = 60" />
    <Token Word="tan" Class="TokenTan" Example="tan(45) = 1" />
    <Token Word="atan" Class="TokenAtan" Example="atan(1) = 45" />
    <Token Word="atan2" Class="TokenAtan2" Example="atan2(30,30) = 45" />
    <!--以下為邏輯運算記號對象,可以同時表示為關鍵字和運算符,因為它們的格式一致,都為true operate false-->
    <Token Word="and" Class="TokenAnd" Example="true and false = false" />
    <Token Word="or" Class="TokenOr" Example="true or false = true" />
    <Token Word="not" Class="TokenNot" Example="not true = false" />
    <Token Word="xor" Class="TokenXor" Example="true xor false = true" />
    <!--以下為常量記號對象-->
    <Token Word="pi" Class="TokenValue" Example="pi*1 = 3.1415926" />
    <Token Word="e" Class="TokenValue" Example="e*1 = 2.71828" />
    <Token Word="true" Class="TokenValue" Example="true and false = false" />
    <Token Word="false" Class="TokenValue" Example="true and false = false" />
    <!--以下為其他記號對象-->
    <Token Word="if" Class="TokenIf" Example="if(3>5,12,24) = 24" />
  </TokenKeyword>
  <TokenSymbol>
    <!--以下為分隔符-->
    <Token Word="(" Class="TokenLeftBracket" Example="2*(5-3) = 4" />
    <Token Word=")" Class="TokenRightBracket" Example="2*(5-3) = 4" />
    <Token Word="," Class="TokenComma" Example="left('abcdefg',5) = 'abcde'" />
    <!--以下為數學運算符-->
    <Token Word="+" Class="TokenPlus" Example="2+3 = 5 或 'abc' + 'def' = 'abcdef'" />
    <Token Word="-" Class="TokenMinus" Example="5-3 = 2" />
    <Token Word="*" Class="TokenMultiply" Example="3*4 = 12" />
    <Token Word="/" Class="TokenDivide" Example="8/2 = 4" />
    <Token Word="%" Class="TokenMod" Example="5.4%2 = 1.4" />
    <!--如果希望求冪采用VB的^算法,形如2^3 = 8,將TokenPow修改為繼承自TokenArithmetic即可,此時用pow則必須輸入2 pow 3, pow後必須帶空格-->
    <!--<Token Word="^" Class="TokenPow" Example="^(2,3) = 8" />-->
    <!--以下為比較運算符-->
    <Token Word="=" Class="TokenEqual" Example="if(3=5,12,24) = 24" />
    <Token Word="==" Class="TokenEqual" Example="if(3==5,12,24) = 24" />
    <Token Word="&lt;&gt;" Class="TokenNotEqual" Example="if(3&lt;&gt;5,12,24) = 12" />
    <Token Word="!=" Class="TokenNotEqual" Example="if(3!=5,12,24) = 12" />
    <Token Word="&gt;" Class="TokenGreatThan" Example="if(3&gt;5,12,24) = 24" />
    <Token Word="&gt;=" Class="TokenGreatOrEqual" Example="if(3&gt;=5,12,24) = 24" />
    <Token Word="&lt;" Class="TokenLessThan" Example="if(3&lt;5,12,24) = 12" />
    <Token Word="&lt;=" Class="TokenLessOrEqual" Example="if(3&lt;=5,12,24) = 12" />
    <!--以下為邏輯運算符,未定義短路操作,可自行實現-->
    <Token Word="!" Class="TokenNot" Example="!true = false" />
    <Token Word="&amp;" Class="TokenAnd" Example="true &amp; false = false" />
    <Token Word="&amp;&amp;" Class="TokenAnd" Example="true &amp;&amp; false = false" />
    <Token Word="|" Class="TokenOr" Example="true | false = true" />
    <Token Word="||" Class="TokenOr" Example="true || false = true" />
  </TokenSymbol>
</TokenRecord>

接下來介紹各個工廠類。首先是關鍵字工廠TokenKeywordFactory。當TokenFactory分析到英文字母的時候,把任務轉交給 TokenKeywordFactory。該類從分析得到的第一個英文字母開始向後分析,如果後面還是英文字母或者數字,則繼續向後分析,否則結束分析。 在這裡關鍵字允許包含數字,但第一個字符必須是英文字母。分析結束後,截取分析得到的字符串,然後到交給ProduceToken方法產生一個 TokenRecord類的實例。

在ProduceToken方法中,首先判斷傳入的字符串是否存在於關鍵字字典中,如果不存在則報錯,如果存在則用反射產生一個對應類的實例。 其中有些關鍵字是常量,所以進行了特殊處理,需要設置記號對象的值和類型。

以上工作完成後,調整分析指針的位置,回到TokenFactory類,執行後續分析。TokenKeywordFactory的代碼如下:

/// <summary>
    /// 關鍵字工廠
    /// </summary>
    /// <remarks>Author:Alex Leo</remarks>
    internal class TokenKeywordFactory : TokenFactory
    {
        private static SortedDictionary<string, string> m_DictionaryKeyword = new SortedDictionary<string, string>();
        /// <summary>
        /// 獲取關鍵字字典
        /// </summary>
        /// <returns>關鍵字字典</returns>
        /// <remarks>Author:Alex Leo; Date:2008-5-19;</remarks>
        internal static SortedDictionary<string, string> GetKeywordDictionary()
        {
            if (m_DictionaryKeyword.Count == 0)
            {
                m_DictionaryKeyword = TokenFactory.GetOperateTokenDictionary (OperateTokenTypeEnum.TokenKeyword);
            }

            return m_DictionaryKeyword;
        }


        /// <summary>
        /// 詞法分析
        /// </summary>
        /// <param name="TokenList">記號對象列表</param>
        /// <param name="Code">源表達式</param>
        /// <param name="Index">分析序號</param>
        /// <remarks>Author:Alex Leo</remarks>
        public static new void LexicalAnalysis(List<TokenRecord> TokenList, string Code, ref int Index)
        {
            int intIndexCurrent = Index;//當前序號
            bool blnContinue = true;
            //string strTempChar = "";//獲取臨時字符
            char chrTemp;
            string strTempWord = "";//獲取臨時字符串

            while (blnContinue && intIndexCurrent < Code.Length)
            {
                chrTemp = Code[intIndexCurrent];
                //關鍵字支持大小寫字母及數字,但不允許以數字開頭
                if (char.IsLetter(chrTemp) || char.IsDigit(chrTemp))
                {
                    intIndexCurrent += 1;//把當前序號後移
                }
                else
                {
                    blnContinue = false;
                }
            }

            strTempWord = Code.Substring(Index, intIndexCurrent - Index);//獲取臨時詞
            TokenRecord Token = ProduceToken(strTempWord, Index);
            TokenList.Add(Token);

            Index = intIndexCurrent - 1;//設置指針到讀取結束位置
        }

        /// <summary>
        /// 產生記號對象
        /// </summary>
        /// <param name="TokenWord">分析得到的單詞</param>
        /// <param name="Index">當前序號</param>
        /// <returns>記號對象</returns>
        /// <remarks>Author:Alex Leo</remarks>
        protected static new TokenRecord ProduceToken(string TokenWord, int Index)
        {
            TokenRecord Token;

            if (GetKeywordDictionary().ContainsKey(TokenWord.ToLower()))
            {
                string strFullClassName = "ConExpress.Calculator." + GetKeywordDictionary() [TokenWord.ToLower()];
                Type TokenType = Type.GetType(strFullClassName);
                Token = (TokenRecord)Activator.CreateInstance(TokenType, new object[] { Index, TokenWord.Length });
                //對常數的特殊處理
                switch (TokenWord.ToLower())
                {
                    case "pi"://以下為常量記號對象
                        Token.TokenValueType = typeof(double);
                        Token.TokenValue = Math.PI;
                        break;
                    case "e":
                        Token.TokenValueType = typeof(double);
                        Token.TokenValue = Math.E;
                        break;
                    case "true":
                        Token.TokenValueType = typeof(bool);
                        Token.TokenValue = true;
                        break;
                    case "false":
                        Token.TokenValueType = typeof(bool);
                        Token.TokenValue = false;
                        break;
                    default:
                        break;
                }
            }
            else
            {
                //錯誤字符串,拋出錯誤,語法錯誤
                throw new SyntaxException(Index, TokenWord.Length, "未知表達式:" + TokenWord);
            }

            return Token;
        }//ProduceToken
    }//class TokenKeywordFactory

TokenSymbolFactory運算符工廠類的分析過程和TokenKeywordFactory基本相似。但是運算符一般只包含一個字符或者兩個字符,就不需要 一直向後分析了,只需要判斷運算符字典中是否存在對應的項即可。另外有些操作符的第一個字符是重復的,比如“>”和 “>=”,這時候就需要判斷“>”之後是否還存在“=”。如果向後再截取一個字符,在字典中也存在 對應項,則按雙字符運算符處理,否則就是單字符運算符。TokenSymbolFactory的代碼如下:

/// <summary>
    /// 運算符工廠
    /// </summary>
    /// <remarks>Author:Alex Leo</remarks>
    class TokenSymbolFactory : TokenFactory
    {

        private static SortedDictionary<string, string> m_DictionarySymbol = new SortedDictionary<string, string>();
        /// <summary>
        /// 獲取運算符字典
        /// </summary>
        /// <returns>運算符字典</returns>
        /// <remarks>Author:Alex Leo; Date:2008-5-19;</remarks>
        internal static SortedDictionary<string, string> GetSymbolDictionary()
        {
            if (m_DictionarySymbol.Count == 0)
            {
                m_DictionarySymbol = TokenFactory.GetOperateTokenDictionary (OperateTokenTypeEnum.TokenSymbol);
            }

            return m_DictionarySymbol;
        }

        /// <summary>
        /// 詞法分析
        /// </summary>
        /// <param name="TokenList">記號對象列表</param>
        /// <param name="Code">源表達式</param>
        /// <param name="Index">分析序號</param>
        /// <remarks>Author:Alex Leo</remarks>
        public new static void LexicalAnalysis(List<TokenRecord> TokenList, string Code, ref int Index)
        {
            string strTempWord;
            if ((Index < Code.Length - 1) && GetSymbolDictionary().ContainsKey(Code.Substring(Index, 2)))//如果是雙字節操作符
            {
                strTempWord = Code.Substring(Index, 2);
                Index += 1;//指針後移一位
            }
            else
            {
                strTempWord = Code.Substring(Index, 1);
            }

            TokenRecord Token = ProduceToken(strTempWord, Index);
            TokenList.Add(Token);
        }

        /// <summary>
        /// 產生記號對象
        /// </summary>
        /// <param name="TokenWord">分析得到的單詞</param>
        /// <param name="Index">當前序號</param>
        /// <returns>記號對象</returns>
        /// <remarks>Author:Alex Leo</remarks>
        protected new static TokenRecord ProduceToken(string TokenWord, int Index)
        {
            TokenRecord Token;

            if (GetSymbolDictionary().ContainsKey(TokenWord.ToLower()))
            {
                string strFullClassName = "ConExpress.Calculator." + GetSymbolDictionary() [TokenWord.ToLower()];
                Type TokenType = Type.GetType(strFullClassName);
                Token = (TokenRecord)Activator.CreateInstance(TokenType, new object[] { Index, TokenWord.Length });
            }
            else
            {
                //錯誤字符串,拋出錯誤,語法錯誤
                throw new SyntaxException(Index, TokenWord.Length, "未知表達式:" + TokenWord);
            }

            return Token;
        }//ProduceToken
    }//class TokenSymbolFactory

TokenStringFactory字符串工廠類的分析是按照VB語法,只有引號需要轉義,用兩個引號即可,其他特殊字符不需要轉義。而且引號和 JavaScript一樣,可以用大寫或者小寫,只要兩端匹配就可以了。如果在單引號標記的字符串中包含雙引號,則不需要轉義,雙引號標記的字 符串中出現單引號也不需要轉義。字符串的分析是以引號作為界定,向後分析的過程中,如果遇到與起始引號相同的字符,判斷後面是否存在 重復引號,存在則轉義,不存在則結束分析。將兩個引號之間的字符串截取,創建一個TokenValue對象,結束分析。TokenStringFactory代碼 如下:

/// <summary>
    /// 字符串工廠
    /// </summary>
    /// <remarks>Author:Alex Leo</remarks>
    class TokenStringFactory : TokenFactory
    {
        /// <summary>
        /// 詞法分析
        /// </summary>
        /// <param name="TokenList">記號對象列表</param>
        /// <param name="Code">源表達式</param>
        /// <param name="Index">分析序號</param>
        /// <remarks>Author:Alex Leo</remarks>
        public new static void LexicalAnalysis(List<TokenRecord> TokenList, string Code, ref int Index)
        {
            string strQuotationMark = Code.Substring(Index, 1);//引號,可以是單引號,也可以是雙引號
            int intIndexCurrent = Index + 1;//指向後一個字符
            string strTempChar = "";//臨時字符
            StringBuilder strTempWord = new StringBuilder();//臨時字符串
            bool blnContinue = true;

            while (blnContinue && intIndexCurrent < Code.Length)//循環直到標志位置False或超出長度
            {
                strTempChar = Code.Substring(intIndexCurrent, 1);

                if (strTempChar.Equals(strQuotationMark))//如果是字符串分隔符
                {
                    if ((intIndexCurrent < Code.Length - 1)
                        && Code.Substring(intIndexCurrent + 1, 1).Equals(strQuotationMark))// 如果後面還是引號,則進行轉義,將兩個引號轉義為一個引號字符
                    {
                        strTempWord.Append(strQuotationMark);//添加一個引號字符到臨時字符串中
                        intIndexCurrent += 2;//向後移兩位
                    }
                    else
                    {
                        blnContinue = false;//遇到字符串結束引號,退出
                    }
                }
                else
                {
                    strTempWord.Append(strTempChar);//添加當前字符到臨時字符串中
                    intIndexCurrent += 1;//向後移一位
                }//if
            }//while

            TokenRecord Token = ProduceToken(strTempWord.ToString(), Index);
            TokenList.Add(Token);
            Index = intIndexCurrent;//指針移到當前指針,即字符串結束引號
        }//LexicalAnalysis

        /// <summary>
        /// 產生記號對象
        /// </summary>
        /// <param name="TokenWord">分析得到的單詞</param>
        /// <param name="Index">當前序號</param>
        /// <returns>記號對象</returns>
        /// <remarks>Author:Alex Leo</remarks>
        public new static TokenRecord ProduceToken(string TokenWord, int Index)
        {
            TokenRecord Token = new TokenValue(Index, TokenWord.Length);
            Token.TokenValue = TokenWord;
            Token.TokenValueType = typeof(string);
            return Token;
        }
    }//class TokenStringFactory

TokenNumberFactory數值工廠的分析最簡單,主要是判斷下一個字符是否是數字或者小數點。將分析得到的字符串轉換為double類型,然後 賦值給創建的TokenValue對象即可。TokenNumberFactory代碼如下:

/// <summary>
    /// 數值工廠
    /// </summary>
    /// <remarks>Author:Alex Leo</remarks>
    class TokenNumberFactory : TokenFactory
    {
        /// <summary>
        /// 詞法分析
        /// </summary>
        /// <param name="TokenList">記號對象列表</param>
        /// <param name="Code">源表達式</param>
        /// <param name="Index">分析序號</param>
        /// <remarks>Author:Alex Leo</remarks>
        public new static void LexicalAnalysis(List<TokenRecord> TokenList, string Code, ref int Index)
        {
            int intIndexCurrent = Index;//指向後一個字符
            bool blnContinue = true;
            char chrTemp;
            string strTempWord;

            while (blnContinue && intIndexCurrent < Code.Length)
            {
                chrTemp = Code[intIndexCurrent];
                if (char.IsDigit(chrTemp) || chrTemp.Equals('.'))
                {
                    intIndexCurrent += 1;
                }
                else
                {
                    blnContinue = false;
                }
            }//while

            strTempWord = Code.Substring(Index, intIndexCurrent - Index);//取得臨時字符串
            TokenRecord Token = ProduceToken(strTempWord, Index);
            TokenList.Add(Token);

            Index = intIndexCurrent - 1;//指針移到當前指針的前一位,然後賦值給循環指針
        }//LexicalAnalysis

        /// <summary>
        /// 產生記號對象
        /// </summary>
        /// <param name="TokenWord">分析得到的單詞</param>
        /// <param name="Index">當前序號</param>
        /// <returns>記號對象</returns>
        /// <remarks>Author:Alex Leo</remarks>
        public new static TokenRecord ProduceToken(string TokenWord, int Index)
        {
            TokenRecord Token = new TokenValue(Index + 1, TokenWord.Length);
            Token.TokenValueType = typeof(double);
            double dblValue;

            if (double.TryParse(TokenWord, out dblValue))
            {
                Token.TokenValue = dblValue;
            }
            else
            {
                throw new SyntaxException(Index, TokenWord.Length, "表達式 " + TokenWord + " 無 法轉換成數值。");
            }

            return Token;
        }//ProduceToken
    }//class TokenNumberFactory

通過這些“工廠”類就可以完成把表達式分析成記號對象列表,為程序理解表達式做好了基礎工作。在下一篇中會介紹如何將記 號對象分析成樹視圖,進而通過樹視圖逐級求解。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved