module NQL
  grammar Syntax

    rule expression
      boolean / primary
    end

    rule boolean
      left:primary space coordinator:coordinator space right:expression {
        def to_ransack
          group = {g: [{m: coordinator.to_ransack}]}

          [left, right].each do |side|
            if side.is_node?(:boolean)
              group[:g][0].merge! side.to_ransack
            else
              group[:g][0][:c] ||= []
              group[:g][0][:c] << side.to_ransack
            end
          end

          group
        end

        def is_node?(node_type)
          node_type.to_sym == :boolean
        end
      }
    end

    rule primary
      (space comparison space / '(' space comparison space ')' / '(' space expression space ')') {
        def to_ransack
          detect_node.to_ransack
        end

        def detect_node
          self.send %w(comparison expression).detect { |m| self.respond_to? m }
        end

        def is_node?(node_type)
          detect_node.is_node?(node_type)
        end
      }
    end

    rule coordinator
      ('|' / '&') {
        def to_ransack
          coordinators = {'|' => 'or', '&' => 'and'}
          coordinators[text_value]
        end
      }
    end

    rule comparison
      variable:alphanumeric space comparator:comparator space value:text {
        def to_ransack
          hash = {a: {'0' => {name: self.variable.text_value.gsub('.', '_')}}, p: self.comparator.to_ransack, v: {'0' => {value: self.value.text_value}}}
          hash = {c: [hash]} if !parent || !parent.parent || text_value == parent.parent.text_value
          hash
        end

        def is_node?(node_type)
          node_type.to_sym == :comparison
        end
      }
    end

    rule comparator
      ('=' / '!=' / '>' / '>=' / '<' / '<=' / ':' / '!:' / '~')+ {
        def to_ransack
          comparators = {
            '=' => 'eq',
            '!=' => 'not_eq',
            '>' => 'gt',
            '>=' => 'gteq',
            '<' => 'lt',
            '<=' => 'lteq',
            ':' => 'cont',
            '!:' => 'not_cont',
            '~' => 'matches'
          }
          comparators[text_value]
        end
      }
    end

    rule text
      (alphanumeric / utf8 / symbol)+
      (space (alphanumeric / utf8 / symbol)+)*
    end

    rule alphanumeric
      [a-zA-Z0-9_.]+
    end

    rule space
      ' '*
    end

    rule symbol
      [><=+-\/\\@#$%!?:]
    end

    rule utf8
      [\u00c0\u00c1\u00c2\u00c3\u00c4\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d9\u00da\u00db\u00dc\u00e0\u00e1\u00e2\u00e3\u00e4\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f9\u00fa\u00fb\u00fc]
    end

  end
end