class Calcp
prechigh
nonassoc UMINUS
left '*' '/'
left '+' '-'
preclow
options no_result_var
rule
target: exp
| { result = [] }
exp: exp '+' exp { val[0] + val[2] + [lambda {|a, b| a + b }] }
| exp '-' exp { val[0] + val[2] + [lambda {|a, b| a - b }] }
| exp '*' exp { val[0] + val[2] + [lambda {|a, b| a * b }] }
| exp '/' exp { val[0] + val[2] + [lambda {|a, b| a / b }] }
| '(' exp ')' { val[1] }
| '-' NUMBER =UMINUS { [val[1], lambda {|a| -a }] }
| NUMBER { [val[0]] }
end
---- header
require 'strscan'
require 'readline'
---- inner
class Scanner
def initialize(src)
@ss = StringScanner.new(src)
end
def scan
until @ss.eos?
if tok = @ss.scan(/\A\s+/)
elsif tok = @ss.scan(/\A\d+/)
yield :NUMBER, tok.to_f
elsif tok = @ss.scan(/\A.|\n/o)
yield tok, tok
end
end
yield false, '$'
end
end
def parse(str)
scanner = Scanner.new(str)
rpn = yyparse(scanner, :scan)
stack = []
while i = rpn.shift
if i.kind_of? Proc
args = (1..i.arity).map { stack.pop }
stack.push(i.call(*args))
else
stack.push(i)
end
end
stack[0]
end
---- footer
parser = Calcp.new
while buf = Readline.readline('> ', true)
print('=> ', parser.parse(buf).inspect, "\n")
end
~/work$ racc calc.y
~/work$ ruby calc.tab.rb
> 1 + 2 * 3 / 4 - 5 + ( -7 * 8 )
=> -52.6666666666667