Skip to content

Instantly share code, notes, and snippets.

@takehiko
Created May 23, 2011 20:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takehiko/987505 to your computer and use it in GitHub Desktop.
Save takehiko/987505 to your computer and use it in GitHub Desktop.
plus/minus/times/divide parser
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
# plus/minus/times/divide parser (rational numbers ready)
# by takehikom (http://d.hatena.ne.jp/takehikom/)
# ruby pmtd-parser-rational.rb
# ruby -d pmtd-parser-rational.rb
# see also: http://getnews.jp/archives/114382
# http://umashika-news.jp/archives/51836921.html
if RUBY_VERSION < "1.9"
$KCODE = "u"
require "jcode"
end
require "rational"
class Rational
def to_s
num = numerator
den = denominator
if den == 1
num.to_s
else
"#{num}/#{den}"
end
end
end
# http://raa.ruby-lang.org/project/syntax
# http://raa.ruby-lang.org/cache/syntax/syntax.rb
# http://jp.rubyist.net/magazine/?0008-RLR
require "./syntax.rb"
class PmtdParser
INF = +1.0/0
LOOP0 = 0..INF
LOOP1 = 1..INF
def initialize
# 式
@expr = Syntax::Pass.new()
# 数
@num = (("0".."9") * LOOP1).qualify{|x|
val = Rational(x.join.to_i)
debug_print("num", x, val)
val
}
# 括弧
@prim = @num.qualify{|x|
val = x
debug_print("prim_num", x, val)
val
} | ("(" + @expr + ")").qualify{|x|
val = x[1]
debug_print("prim_par", x, val)
val
}
# 連接(かけ算)
@prim2 = (@prim + ((" " | "・" | "") + @prim) * LOOP0).qualify{|x|
val = x[0]
x[1].each{|y|
val *= y[1]
}
debug_print("prim2", x, val)
val
}
# 符号
@prim3 = @prim2.qualify{|x|
val = x
debug_print("prim3_num", x, val)
val
} | (("+" | "-") + @prim2).qualify{|x|
val = x[1]
val = -val if x[0] == "-"
debug_print("prim3_sign", x, val)
val
}
# 乗除
@expr2 = (@prim3 + (("*" | "/" | "×" | "÷") + @prim3) * LOOP0).qualify{|x|
val = x[0]
x[1].each{|y|
case y[0]
when "*", "×", " ", "・", ""
val *= y[1]
when "/", "÷"
val /= y[1]
end
}
debug_print("expr2", x, val)
val
}
# 式(加減)
@expr << (@expr2 + (("+" | "-") + @expr2) * LOOP0).qualify{|x|
val = x[0]
x[1].each{|y|
case y[0]
when "+"
val += y[1]
when "-"
val -= y[1]
end
}
debug_print("expr", x, val)
val
}
end
def start(str)
str = str.tr("0-9()+-", "0-9()+-")
print "#{str}="
puts if $DEBUG
puts((@expr === RandomAccessStream.new(str)).to_s)
end
def debug_print(name, x, val)
return unless $DEBUG
puts "#{name}: x=#{x.inspect}:#{x.class}, val=#{val}"
end
private :debug_print
end
if __FILE__ == $0
if ARGV[0]
PmtdParser.new.start(ARGV.shift)
else
expr = "(63÷21)×18-27+53"
PmtdParser.new.start(expr)
expr = expr.split(//u).reverse.join.tr("()", ")(")
PmtdParser.new.start(expr)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment