class MathExpression::Parser

Constants

OPERATORS

Attributes

expression[R]

Public Class Methods

new(expression) click to toggle source
# File lib/math_expression/parser.rb, line 4
def initialize(expression)
  @expression = expression
end

Public Instance Methods

evaluate() click to toggle source
# File lib/math_expression/parser.rb, line 73
def evaluate
  stack = []
  to_postfix.each do |token|
    result = case token
    when '+'
      stack.pop + stack.pop
    when '-'
      first, second = stack.pop, stack.pop
      second - first
    when '*'
      stack.pop * stack.pop
    when '/'
      first, second = stack.pop, stack.pop
      second / first
    else
      token.to_f
    end
    stack.push(result)
  end
  stack.pop
end
to_postfix() click to toggle source
# File lib/math_expression/parser.rb, line 36
def to_postfix
  output_queue = []
  operator_stack = []

  to_tokens.each do |token|
    type, value = token[:type], token[:value]

    case type
    when :number
      output_queue << value
    when :operator
      loop do
        break if operator_stack.empty?
        break if left_parenthesis?(operator_stack.last)
        operator_on_stack_precedence = operator_precedence(operator_stack.last)
        current_operator_precedence = operator_precedence(value)
        break if operator_on_stack_precedence <= current_operator_precedence

        output_queue << operator_stack.pop
      end
      operator_stack.push(value)
    when :parenthesis
      if left_parenthesis?(value)
        operator_stack.push(value)
      else
        while !left_parenthesis?(operator_stack.last)
          output_queue << operator_stack.pop
        end
        if left_parenthesis?(operator_stack.last)
          operator_stack.pop
        end
      end
    end
  end
  (output_queue + operator_stack.reverse)
end
to_tokens() click to toggle source
# File lib/math_expression/parser.rb, line 8
def to_tokens
  tokens = []
  reading_number = []

  form_number = lambda do
    return if reading_number.empty?
    num = reading_number.join.to_f
    tokens << { type: :number, value: num }
    reading_number = []
  end

  printable_chars = expression.chars.reject { |c| c == ' ' }

  printable_chars.each do |character|
    if operator?(character)
      form_number.call
      tokens << { type: :operator, value: character }
    elsif parenthesis?(character)
      form_number.call
      tokens << { type: :parenthesis, value: character }
    else
      reading_number << character
    end
  end
  form_number.call
  tokens
end

Private Instance Methods

left_parenthesis?(token) click to toggle source
# File lib/math_expression/parser.rb, line 116
def left_parenthesis?(token)
  token == '('
end
operator?(token) click to toggle source
# File lib/math_expression/parser.rb, line 104
def operator?(token)
  OPERATORS.keys.include?(token)
end
operator_precedence(operator) click to toggle source
# File lib/math_expression/parser.rb, line 108
def operator_precedence(operator)
  OPERATORS.fetch(operator)
end
parenthesis?(token) click to toggle source
# File lib/math_expression/parser.rb, line 112
def parenthesis?(token)
  left_parenthesis?(token) || right_parenthesis?(token)
end
right_parenthesis?(token) click to toggle source
# File lib/math_expression/parser.rb, line 120
def right_parenthesis?(token)
  token == ')'
end