module SafeCalc
Constants
- TOKEN_PATTERN
Public Instance Methods
Source
# File lib/safe_calc.rb, line 13 def evaluate(expression) tokens = tokenize(expression) return { error: "Invalid expression" } if tokens.nil? || tokens.empty? result, index = parse_expression(tokens, 0) return { error: "Invalid expression" } if result.nil? return { error: "Invalid expression" } unless index == tokens.length { value: result } end
Source
# File lib/safe_calc.rb, line 45 def parse_expression(tokens, index) left, index = parse_term(tokens, index) return [nil, index] if left.nil? while index < tokens.length && %w[+ -].include?(tokens[index]) op = tokens[index] index += 1 right, index = parse_term(tokens, index) return [nil, index] if right.nil? left = op == "+" ? left + right : left - right end [left, index] end
Source
# File lib/safe_calc.rb, line 83 def parse_power(tokens, index) left, index = parse_unary(tokens, index) return [nil, index] if left.nil? if index < tokens.length && tokens[index] == "**" index += 1 right, index = parse_power(tokens, index) return [nil, index] if right.nil? left = left**right end [left, index] end
Source
# File lib/safe_calc.rb, line 111 def parse_primary(tokens, index) return [nil, index] if index >= tokens.length token = tokens[index] if token == "(" value, index = parse_expression(tokens, index + 1) return [nil, index] if value.nil? return [nil, index + 1] unless index < tokens.length && tokens[index] == ")" return [value, index + 1] end return [nil, index] unless token.match?(/\A\d+(?:\.\d+)?\z/) [token.include?(".") ? token.to_f : token.to_i, index + 1] end
Source
# File lib/safe_calc.rb, line 60 def parse_term(tokens, index) left, index = parse_power(tokens, index) return [nil, index] if left.nil? while index < tokens.length && %w[* / %].include?(tokens[index]) op = tokens[index] index += 1 right, index = parse_power(tokens, index) return [nil, index] if right.nil? left = case op when "*" then left * right when "/" then return [nil, index] if right.zero? left / right when "%" then return [nil, index] if right.zero? left % right end end [left, index] end
Source
# File lib/safe_calc.rb, line 97 def parse_unary(tokens, index) return [nil, index] if index >= tokens.length if tokens[index] == "-" index += 1 value, index = parse_unary(tokens, index) return [nil, index] if value.nil? return [-value, index] end parse_primary(tokens, index) end
Source
# File lib/safe_calc.rb, line 24 def tokenize(expression) text = expression.to_s.strip return nil if text.empty? tokens = [] pos = 0 while pos < text.length match = TOKEN_PATTERN.match(text, pos) return nil unless match token = match[0] return nil unless token.match?(%r{\A(?:\d+(?:\.\d+)?|\*\*|[+\-*\/%\(\)]|\s+)\z}m) tokens << token unless token.strip.empty? pos = match.end(0) end return nil if pos < text.length tokens end