支持用戶自定義變量的PowerBuilder表達式求值

支持用戶自定義變量的PowerBuilder表達式求值


湖南省郵電技術中心
張俊飛、譚培莎

---- 在一些應用程序開發中,需要處理最終用戶輸入的表達式求值問題。一般情況下,需要程序開發人員運用編譯原理中的詞法分析、語法分析與語義分析技術,掃描用戶輸入的符號串並用解釋執行的方法計算表達式的值。PowerBuilder提供的describe函數,具有強大的表達式分析能力,爲用戶表達式的求值提供強有力的支持。但是,describe函數不支持用戶自定義變量,對具有語法錯誤的表達式,沒有提供具體的出錯原因和錯誤位置。本文給出一個在實際應用中的解決辦法,以供有此需求的其他開發人員參考。


---- 在一個用PowerBuilder作爲開發工具的概預算編制軟件開發過程中,用戶提出這樣一個需求:用戶輸入一表達式後系統能自動計算其值,且表達式中可含有函數、用戶自定義變量以及註釋等。


---- 形如:"π/4*(D2-d2){備註:圓環面積}+sin(xyz{自定義變量xyz需求解})-[row%]{自定義變量row%不需求解}"。面對這樣一個複雜的表達式如何進行求解呢。


---- PowerBuilder獨有的數據窗口功能強大,並提供了豐富的數據窗口函數,而求解這一複雜表達式的就是使用Describe()這一數據窗口函數來實現的。


---- Describe()函數通常用來獲取數據窗口對象屬性值及數據窗口對象中的各個對象的屬性值。但它還有一個功能---計算表達式。它可以計算指定行或列的值,返回結果。其語法格式爲:dwcontrol.Describe (Evaluate ( '表達式',行號)). 例如在以下的表達式中使用Describe函數計算數據窗口中第三行Salary域的值,根據Salary的取值是否大於100000來決定返回值是255或是0。



ls_ret = dw_1.Describe( "Evaluate
('If(salary > 100000, 255, 0)', 3)")

---- 特殊的,如果指定行號爲0,則僅返回表達式的值,而不管其所屬行.


---- 但是這一函數, 只能計算僅含數字、算符、函數的簡單的表達式,要利用它來進行自定義表達式的求解則還要對表達式進行必要的過濾、替換等預處理.


---- 自定義表達式的求解理論上是一個詞法分析的過程.即對輸入的字符串進行分析,區分出變量、函數、常數、算符、界符、保留字。然後替換變量進行計算。


---- 還是以形如:"π/4*(D2 -d2){備註:圓環面積}+sin(xyz{自定義變量xyz需求解})-[row%]{自定義變量row%不需求解}"的表達式爲例。


---- 假設表達式中的自定義變量的合法形式爲:以字母開頭,由字母和數字組成的序列。表達式中的註釋用特殊符號容納,例如大括號{}。


---- 求解工作先通過三步預處理(分別用消除表達式中的空格f_skip_space()、消除表達式中的註釋f_trim_notes()、語法分析與變量替換f_parse_express()三個函數來完成),再調用describe函數對變換後不含用戶自定義變量的表達式求值。


---- F_skip_space():入口爲原始字符串,返回去掉空格及其它不可見字符的新串。


---- 函數體中用一個循環逐個讀入字符,並將ASC值大於32的字符累加到某一string類型的變量中,循環完成後該變量容納的即爲去掉空格的新字符串。


---- F_trim_notes():入口爲去掉空格的新字符串,返回去掉註釋後的新串。


---- 函數體中設一整型層次計數器li_level初始值爲0,用一個循環掃描輸入串,一旦遇到'{'則li_level加一;若遇到'}'則li_level減一;若li_level=0則將該字符累加到某一string類型的變量中。若在循環體中有li_level<1的情況,或在循環結束後li_level< 0,則說明括符左右不匹配,應給予提示,並中止分析。循環正常結束後,變量的值即爲去掉註釋後的新串。

---- F_parse_expression():該函數是表達式分析的主體部分。經過以上兩步後表達式中已剔除了空格和註釋。該函數要判斷表達式是否合法,包括左右括號是否匹配,算符位置是否正確,函數名稱是否正確等。同時,要到相應的變量表獲取用戶自定義變量的值,替換該變量。形成最終的僅含函數、算符、界符、數字的合法表達式,返回供Describe()函數計算。


---- 該函數可包含三個入口參數:as_exp接收輸入的經過兩次處理的表達式;as_reserved接收不需替換的變量,如列名等,用[a][b]形式表示;al_flag決定從哪個數據庫表中查找變量值。第三個參數主要是從通用性角度考慮,如果自定義變量來源僅有一張表可無需定義該參數。


---- 函數體中用到一些判斷,這些判斷可用一些函數來實現。例如判斷某個字符是否爲有效字符(a-z或A-Z),判斷某個字符是否爲運算符('+','-','*','/','^'), 判斷某個字符是否爲數字(0-9),判斷某個字符是否爲保留字('[int][sin][cos][tan][log][logten] [exp][pi][fact][abs][sign][sqrt]')等。


---- 函數體主要由一個(do while)外層大循環嵌套一個(if_else_endif)條件判斷,再嵌套一個(do while)內層循環加一些判斷組成。外層循環的條件是假設一指針從表達式的第一個字符開始順序後移,直到指向最後一個字符。如果當前指針指向的是一數字或括號,則累加到某一string類型的變量ls_result中,並分左右括號對integer型的變量li_level加1或減1來表示括號的嵌套層次。如果當前指針指向的是一字符,則用內層循環讀取該字符後的字符串到一string類型的變量ls_identifier中,同時使指針後移,直到遇到算符、界符或表達式結束符,循環結束。Ls_identifier中保存的是一變量名或函數名。取下一字符,如果是'(',那麼,ls_identifer中保存的應是一函數名,並檢查是否爲合法的保留字;如果不是'(',則判斷as_reserverd中有無該變量,若有則不需要解釋(如數據窗口的列名),直接保留標識符,將其加到ls_result中,若需要解釋,則根據al_flag到不同的數據庫表提取相應的值,並對結果做相應判斷(如爲空,值取0)加到ls_result中。外層循環結束後,根據li_level的值是否爲0決定表達式中的括號是否匹配。


---- 最終,如正常結束返回結果串ls_result,即爲僅含數字、算符、函數、列名的簡單字符串,可由Describe()函數進行計算。

發佈了7 篇原創文章 · 獲贊 0 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章