表达式编译计算器(上)
ken j vb 加油站
概述
由于某种原因, 近日编写了一个使用编译原理实现的表达式计算器。 使用该程序可以不但可以计算普通的数字和字符串表达式, 而且支持一些vb 的常用函数及极好的可扩展性。 例如:
录入字符串: len("1234") + 10/2 结果: 9
录入字符串: mid("1234",2) & cstr(10/2) 结果: 2345
在该程序的设计中, 使用了编译的基本概念。所以可以极其方便得加入新的语法, 一个可能的例子就是实现宏替换, 作为一个演示, 我在该类中也提供了一个宏替换的接口。
语法
编译说穿了是对一个数据流进行逐字的分析, 把分析过程看做是不断的读取数据, 是实现程序的一个基本思想。现在让我们自顶而下地对表达式进行分析, 该语法图如下:

注意: 其中宏替换的字符是在键盘左上角和 ~ 一起的那个。完整的语法图应该还包括其他几个定义, 没有在上图表现出来。
只要理解了语法, 实现就方便了。
实现
表达式
作为一个语法实现的例子, 下面是实现表达式定义的函数:
public function getexp(str1 as string) as string
dim val1, val2, op1, op2, val3
dim newval
val1 = getvalue(str1) ' 得到一个值
newval = val1
op1 = readop(str1) ' 取下一个操作字符
do while op1 <> "" ' 直到没有任何操作符才结束
val2 = getvalue(str1) ' 取操作符后面的值
op2 = readop(str1) ' 取下一个操作符, 目的是实现 * / 的优先操作。
do while op2 <> ""
if (op2 = "*" or op2 = "/") and (op1 = "-" or op1 = "+") then
val3 = getvalue(str1)
val2 = compstr(val2, op2, val3) 'compstr 函数完成计算操作
op2 = readop(str1)
else
exit do
end if
loop
newval = compstr(newval, op1, val2)
op1 = op2
loop
getexp = newval ' 返回计算结果
end function
值
private function getvalue(str1 as string) as string
dim c1 as string
c1 = readchar(str1) '取一个字符
do while c1 = " " and c1 <> ""
c1 = readchar(str1) '去掉空格
loop
select case c1
case """" ' 如果是", 认为是一个字符串
getvalue = readstring(str1) '读入一个字符串
curtype = "string" ' 为以后的类型检查准备
case "("
getvalue = getexp(str1) ' 如果是括号中的话, 认为是另一个表达式
c1 = readchar(str1) '下面的代码是检查括号匹配
do while c1 <> "" and c1 = " "
c1 = readchar(str1)
loop
if c1 <> ")" then errorstr = "需要 )"
case "0" to "9"
getvalue = c1 & readnumber(str1) '读如数字串
curtype = "number"
case "`"
getvalue = readfield(str1) ' 进行宏处理
case else
retchar str1, c1 '如果是其他, 把该字符退回
getvalue = readfunction(str1) '进入函数处理程序
end select
end function
两个基本的函数
下面的两个函数实现了最基本的从表达式中取一个字符或退回一个字符。
' readchar: 取出一个字符,
' 参数: str1 要处理的表达式字符串
' 返回: 第一个字符
' str1 成为被取后的字符串
private function readchar(str1 as string) as string
readchar = ""
if str1 = "" then exit function
readchar = left(str1, 1)
str1 = right(str1, len(str1) - 1)
end function
' retchar退回一个字符
' 参数: str1 处理的字符串
' achar 要退回的字符
' 返回: str1 :被退回的字符串
private function retchar(str1 as string, achar as string) as string
str1 = achar & str1
retchar = str1
end function
数字
' 读入数字
' 参数: str1 处理的字符串
' 返回: 取到的数字, "" 为错误!
' str1 :处理后字符串
private function readnumber(str1 as string) as string
dim c1 as string
dim rets as string
rets = ""
c1 = readchar(str1)
do while c1 <> ""
if c1 >= "0" and c1 <= "9" then
rets = rets & c1
else
retchar str1, c1
exit do
end if
c1 = readchar(str1)
loop
readnumber = rets
end function