require File.dirname(__FILE__) << '/keyword_search/definition.rb'

module KeywordSearch

class ParseError < ::SyntaxError; end  

class << self

  %%{

    machine parser;

    action start {
      tokstart = p;
    }

    action key {
      key = word
      results[key] ||= []
    }

    action default {
      key = nil
    }

    action word {
      word = data[tokstart..p-1]
    }

    action value {
      (results[key || :default] ||= []) << [ word, positive_match ]
    }

    action negative_match {
      positive_match = false
    }

    action positive_match {
      positive_match = true
    }

    action quote { quotes += 1 }

    action unquote { quotes -= 1 }

    seperators = ' '+ | / *[,|] */ ;

    bareword = ( [^ '"(:] . [^ "):]* ) > start % word ; # allow apostrophes
    dquoted = '"' @ quote ( [^"]* > start % word ) :>> '"' @ unquote;
    squoted = '\'' @ quote ( [^']* > start % word ) :>> '\'' @ unquote;

    anyword = dquoted | squoted | bareword ;      

    anyvalue = anyword % value ;
    multivalues = anyvalue ( seperators anyvalue )* ;
    groupedvalues = '(' @ quote multivalues :>> ')' @ unquote;

    value = groupedvalues | anyvalue ;

    pair = bareword % key ':' value ;

    value_only = value > default ;

    match_mode = ('-' % negative_match | '+'? % positive_match ) ;

    definition = match_mode? <: ( pair | value_only ) ;

    definitions = definition ( ' '+ definition )*;

    main := ' '* definitions? ' '* 0
            @!{ raise ParseError, "At offset #{p}, near: '#{data[p,10]}'" };        

  }%%

  def search(input_string, definition=nil, &block)
    definition ||= Definition.new(&block)
    results = parse(input_string)
    results.each do |key, terms|
      definition.handle(key, terms)
    end
    results
  end

  #######
  private
  #######

  def parse(input) #:nodoc:
    data = input + ' '
    %% write data;
      p = 0
    eof = nil
    word = nil
      pe = data.length
      key = nil
    positive_match = nil
      tokstart = nil
      results = {}
      quotes = 0
    %% write init;
    %% write exec;
      unless quotes.zero?
        raise ParseError, "Unclosed quotes"
      end
      results
  end

end

end