class Calcp
prechigh
nonassoc UMINUS
left '*' '/'
left '+' '-'
preclow
options no_result_var
rule
target: exp
| { result = [] }
exp: exp '+' exp { val[0] + val[2] + ['+'] }
| exp '-' exp { val[0] + val[2] + ['-'] }
| exp '*' exp { val[0] + val[2] + ['*'] }
| exp '/' exp { val[0] + val[2] + ['/'] }
| '(' exp ')' { val[1] }
| '-' NUMBER =UMINUS { [val[1], :UM] }
| 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_i
elsif tok = @ss.scan(/\A.|\n/o)
yield tok, tok
end
end
yield false, '$'
end
end
def parse(str)
scanner = Scanner.new(str)
yyparse scanner, :scan
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 )
=> [1, 2, 3, "*", 4, "/", "+", 5, "-", 7, :UM, 8, "*", "+"]