现在的位置: 首页 > 综合 > 正文

支持用户自定义变量的PowerBuilder表达式求值

2013年10月01日 ⁄ 综合 ⁄ 共 2762字 ⁄ 字号 评论关闭
支持用户自定义变量的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()函数进行计算。

抱歉!评论已关闭.