class ParseError < Exception
end
#词法分析类
class Lex
def initialize(strExp)
@strExp = strExp.gsub(/\s+/, '')
end
def nextToken
if @strExp =~ /^\d+/
@strExp = $'
$&.to_i
elsif @strExp =~ /^(\+|-|\*|\/|\(|\))/
@strExp = $'
$&
elsif @strExp =~ /^$/
nil
else
raise ParseError.new('Unknown character ==> ' + @strExp)
end
end
end
#语法分析和求值在这里一勺烩了
class Syntax
def parse(&procToken)
@procToken = procToken
nextToken
val = exp
matchType(NilClass)
return val
end
private
def nextToken
@currentToken = @procToken.call
end
def matchStr(t)
if @currentToken.is_a?(String) and @currentToken == t
nextToken
return t
else
requireError(t)
end
end
def matchType(t)
tmp = @currentToken
if @currentToken.is_a?(t)
nextToken
return tmp
else
requireError(t.class)
end
end
def requireError(*a)
raise ParseError.new( "require '#{a.join(',')}', " + "token='#{@currentToken}'")
end
def exp
val = term
while TRUE
if @currentToken == '+'
matchStr('+')
val += term
elsif @currentToken == '-'
matchStr('-')
val -= term
else
break
end
end
return val
end
def term
val = factor
while TRUE
if @currentToken == '*'
matchStr('*')
val *= factor
elsif @currentToken == '/'
matchStr('/')
d = factor
raise '除数为零' if d == 0
val /= d
else
break
end
end
return val
end
def factor
if @currentToken.is_a?(Integer)
return matchType(Integer)
elsif @currentToken == '('
matchStr('(')
val = exp
matchStr(')')
return val
else
requireError(['Integer', '('])
end
end
end
#为了方便使用定义一个包装函数
def parseExp(exp)
l = Lex.new(exp)
s = Syntax.new
s.parse { l.nextToken }
end
#测试代码
EXPS = %w{
100
(1)
1+2
1*3
1+2-3
5-2-1
5-(2-1)
1-2*3
(1-2)*3
4/2
(1+2)*(3+4)
(1+2)*(3+(4*2))
1+(2+3
1+2+
1++
4/(1-1)
}
EXPS.each do
|x|
begin
puts "#{x}=#{parseExp(x)}"
rescue Exception => e
puts "*Error* #{e}(#{e.class}), exp=#{x}"
end
end