bison マニュアルの中間記法電卓(StringTokenizer 未使用)
「^」にも対応
#!/usr/bin/env ruby require 'tdp' require 'tdp/utils' class Calc include TDParser include TDPUtils def expr1 rule(:expr2) - ((token("+")|token("-")) - rule(:expr2))*0 >> proc{|x| x[1].inject(x[0]){|n, y| case y[0] when "+" n + y[1] when "-" n - y[1] end } } end def expr2 rule(:expr3) - ((token("*")|token("/")) - rule(:expr3))*0 >> proc{|x| x[1].inject(x[0]){|n, y| case y[0] when "*" n * y[1] when "/" n / y[1] end } } end def expr3 token("-") - rule(:expr3) >> proc {|x| -x[1] } | rule(:expr4) >> proc {|x| x[0] } end def expr4 rule(:prim) - (token("^") - rule(:expr3))*(0..1) >> proc{|x| x[1].inject(x[0]){|n, y| n ** y[1] } } end def prim token(:NUM) >> proc {|x| x[0].value.to_f } | token("(") - rule(:expr1) - token(")") >> proc{|x| x[1] } end def input rule(:expr1) >> proc {|x| puts "\t#{x}"} end def parse(f) f.each do |line| input.parse {|x| tokens = lex(line); tokens.each {|t| x.yield(t)}} end end def lex(line) @q = [] line.strip! until line.empty? do case line when /\A\s+/, /\A\#.*/ ; when /\A\d+(\.\d+)*/ @q.push Token.new(:NUM, $&.to_f) when /\A./ @q.push $& else raise RuntimeError, 'must not happen' end line = $' end return @q end end parser = Calc.new if ARGV[0] then File.open( ARGV[0] ) do |f| parser.parse f end else parser.parse $stdin end
で、
1 + 2 3.0 1-2 -1.0 2*3 6.0 10/3 3.33333333333333 -56+2 -54.0 -2^2 -4.0 2^2^3 256.0 -(1+2*3) -7.0 1+-1 0.0 2^-1 0.5 4 + 4.5 - (34/(8*3+-3)) 6.88095238095238