Skip to content

Instantly share code, notes, and snippets.

@takehiko
Created March 13, 2015 20:33
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/e4d5005f53b1fe973b7a to your computer and use it in GitHub Desktop.
Save takehiko/e4d5005f53b1fe973b7a to your computer and use it in GitHub Desktop.
6÷2(1+2)=1 and 6÷2(1+2)=9 using Racc
#!/usr/bin/env ruby
# $ ruby calc3.rb
# 1+2×3=7
# 3×(8+5×(6-2))÷2=42
# 2(1+2)-2=4
# 2 (1+2)÷2=3
# 2 - 2(1 + 2)=-4
# 2 / 2 (1 + 2)=0
# 2(1+2)(3+4)=42
# 2×(1+2)×(3+4)=42
# 6÷2(1+2)=1
# 6÷2×(1+2)=9
# $ ruby calc3.rb -q
# 1+2×3=7
# 3×(8+5×(6-2))÷2=42
# 2(1+2)-2=4
# 2 (1+2)÷2=3
# 2 - 2(1 + 2)=-4
# 2 / 2 (1 + 2)=3
# 2(1+2)(3+4)=42
# 2×(1+2)×(3+4)=42
# 6÷2(1+2)=9
# 6÷2×(1+2)=9
# Racc required (gem install racc)
def prepare_expr(s)
s.tr('()+×÷-', '()+*/-').
tr('0-9', '0-9').
gsub(/([\d\)])([\(])/, '\1 \2').
gsub(/\)(\d)/, ') \1').
gsub(/\s*([\+\*\/\-])\s*/, '\1').
gsub(/\s\s+/, ' ')
end
option_equal = false
while /^-/ =~ ARGV[0]
case ARGV.shift
when /^-q/
option_equal = true
end
end
system("rm calc.y") if test(?f, "calc.y")
system("rm calc.rb") if test(?f, "calc.rb")
open("calc.y", "w") do |f_out|
code = DATA.read
if option_equal
puts "set the same priority of concatenation as multiplication and division" if $DEBUG
code.sub!(/\' \'\n\s*left/m, "' '")
else
puts "prioritize concatenation over multiplication and division" if $DEBUG
end
f_out.print code
end
command = "racc -o calc.rb calc.y"
result = `#{command} 2>&1`
puts "execute: " + command if $DEBUG
print result if $DEBUG
require "./calc.rb"
calc = Calc.new()
expr_array = ARGV
if expr_array.empty?
expr_array = [
"1+2×3", "3×(8+5×(6-2))÷2",
"2(1+2)-2", "2 (1+2)÷2",
"2 - 2(1 + 2)", "2 / 2 (1 + 2)",
"2(1+2)(3+4)", "2×(1+2)×(3+4)",
"6÷2(1+2)", "6÷2×(1+2)",
]
end
expr_array.each do |expr|
puts "transform \"#{expr}\" into \"#{prepare_expr(expr)}\"" if $DEBUG
puts expr + "=" + calc.parse(prepare_expr(expr)).to_s
end
# http://mollifier.hatenablog.com/entry/20090823/p1
__END__
class Calc
token NUM
prechigh
left ' '
left '*' '/'
left '+' '-'
preclow
rule
expr : expr '+' expr { result = val[0] + val[2] }
| expr '-' expr { result = val[0] - val[2] }
| expr '*' expr { result = val[0] * val[2] }
| expr ' ' expr { result = val[0] * val[2] }
| expr '/' expr { result = val[0] / val[2] }
| prim { result = val[0] }
prim : NUM { result = val[0] }
| '(' expr ')' { result = val[1] }
end
---- inner
def parse(str)
@tokens = str.split(/([ \(\)\+\-\*\/])/).select{|c| !c.nil? && c != ""}
do_parse()
end
def next_token()
token = @tokens.shift()
case token
when /^\d+\z/
return [:NUM, token.to_i]
when nil
return nil
else
return [token, nil]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment