beattyml1/codgen

View on GitHub
lib/codgen/statement.rb

Summary

Maintainability
B
6 hrs
Test Coverage
require_relative 'constants'
require_relative 'logger'

# Not currently used but will be for an up coming feature that used be implemented in an older version
module Codgen
  class Statement
    include RegularExpressions
    def initialize(operator=nil)
      @words = Array.new
      @operator = operator
    end

    def evaluate(get_val)
      if @operator == nil
        expand
      end

      current_val = nil
      @words.each do |child|
        current_val = accumulate(current_val, child, @operator, get_val)
      end
      current_val
    end

    attr_reader :operator
    attr_reader :words

    def accumulate(before, child, operator, get_val)
      if operator == nil && child.is_a?(Statement)
        accumulate(before, child, child.operator, get_val)
      else
        value = child.is_a?(String) ? get_val.call(child) : child.evaluate(get_val)
        case operator
          when :and
            before = before != nil ? before : true
            before && value
          when :or
            before = before != nil ? before : false
            before || value
          when :not
            !value
          else
            value
        end
      end
    end

    def expand
      expand_not
      expand_and
      expand_or
    end

    def parse(text)
      count = -1
      text.each_char do |current_char|
        count+=1
        if current_char == ' '
          next 1
        end

        if current_char.index(IDENTIFIER_REGEX)
          if @words.length > 0 && @words[-1].index(IDENTIFIER_REGEX)
            @words[-1] = @words[-1] + current_char
          else
            @words.push(current_char)
          end
        elsif current_char.index(/[&|!]/)
          @words.push(current_char)
        elsif current_char == '('
          @words.push(Statement.new)
          count += @words[-1].parse(text[count...text.length])
        elsif current_char == ')'
          return count
        end
      end
    end

    def expand_not
      new_words = Array.new
      @words.each do |word|
        if word.is_a?(Statement)
          new_words.push(word)
        elsif word.index(/not|!/)
          if new_words[-1].is_a?(Statement) && new_words[-1].operator == :not
            new_words.delete_at(-1)
          else
            new_words.push(Statement.new(:not))
          end
        else
          if new_words[-1].is_a?(Statement) && new_words[-1].operator == :not && new_words[-1].words.count == 0
            new_words[-1].words.push(word)
          else
            new_words.push(word)
          end
        end
      end
      @words = new_words
    end

    def expand_and
      expand_two_sided(:and, /and|&/)
    end

    def expand_or
      expand_two_sided(:or, /or|\|/)
    end

    def expand_two_sided(operator, regex)
      new_words = Array.new
      index = -1
      skip = false
      @words.each do |word|
        index+=1
        if skip
          skip = false
          next
        end
        if word.is_a?(Statement)
          new_words.push(word)
        elsif word.index(regex)
          if new_words.count > 0 && @words.count > index + 1
            replace_with_statement(new_words, operator)
            skip = true
            next
          else
            throw 'Operator must have a left and a right side'
          end
        else
          new_words.push(word)
        end
      end
      @words = new_words
    end

    def replace_with_statement(new_words, operator)
      left = new_words[-1]
      new_words.delete_at(-1)
      new_words.push(Statement.new(operator))
      new_words[-1].words.push(left)
      new_words[-1].words.push(@words[index+1])
    end
  end
end