bison マニュアルの中間記法電卓

「^」にも対応

#!/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(:int)  >> proc{|x| x[0].value.to_f } |
    token(:real) >> proc{|x| x[0].value.to_f } |
    token("(") - rule(:expr1) - token(")") >> proc{|x| x[1] }
  end

  def parse(f)
    tokenizer = StringTokenizer[
      /\d+(?!\.\d)/ => :int,
      /\d+\.\d+/    => :real,
    ]

    f.each do |line|
      puts "\t#{expr1.parse(tokenizer.generate(line))}"
    end
  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