#!/usr/bin/ruby # Translation of: https://rosettacode.org/wiki/Arithmetic_evaluation#JavaScript func evalArithmeticExp(s) { func evalExp(s) { func operate(s, op) { s.split(op).map{|c| Number(c) }.reduce(op); } func add(s) { operate(s.sub(/^\+/,'').sub(/\++/,'+'), '+'); } func subtract(s) { s.gsub!(/(\+-|-\+)/,'-'); if (s ~~ /--/) { return(add(s.sub(/--/,'+'))); } var b = s.split('-'); b.len == 3 ? (-1*Number(b[1]) - Number(b[2])) : operate(s, '-'); } s.gsub!(/[()]/,'').gsub!(/-\+/, '-'); var reM = /\*/; var reMD = %r"(\d+\.?\d*\s*[*/]\s*[+-]?\d+\.?\d*)"; var reA = /\d\+/; var reAS = /(-?\d+\.?\d*\s*[+-]\s*[+-]?\d+\.?\d*)/; while (var match = reMD.match(s)) { match[0] ~~ reM ? s.sub!(reMD, operate(match[0], '*').float.to_s) : s.sub!(reMD, operate(match[0], '/').float.to_s); } while (var match = reAS.match(s)) { match[0] ~~ reA ? s.sub!(reAS, add(match[0]).float.to_s) : s.sub!(reAS, subtract(match[0]).float.to_s); } return s; } var rePara = /(\([^\(\)]*\))/; s.split!.join!('').sub!(/^\+/,''); while (var match = s.match(rePara)) { s.sub!(rePara, evalExp(match[0])); } return Number(evalExp(s)); } # ## MAIN # [ ['2+3' => 5], ['-4-3' => -7], ['-+2+3/4' => -1.25], ['2*3-4' => 2], ['2*(3+4)+2/4' => 2/4 + 14], ['2*-3--4+-0.25' => -2.25], ['2 * (3 + (4 * 5 + (6 * 7) * 8) - 9) * 10' => 7000], ].each { |arr| var (expr, res) = arr... var num = evalArithmeticExp(expr) num == res || ( die "Error occurred on expression '#{expr}': got '#{num}' instead of '#{res}'\n" ) "%-45s == %10g\n".printf(expr, num) }