# begin: ragel

begin

%%{

machine bel;

include 'common.rl';
include 'identifier.rl';
include 'string.rl';
include 'list.rl';

action add_name {
  trace('DEFINE_ANNOTATION add_name')
  name = @buffers.delete(:ident)
  @buffers[:annotation_definition_name] = name
}

action add_string_value {
  trace('DEFINE_ANNOTATION add_string_value')
  string_node = @buffers.delete(:string)

  case
  when @uri_domain
    leaf = domain(uri(string_node))
    leaf.complete = string_node.complete
  when @url_domain
    leaf = domain(url(string_node))
    leaf.complete = string_node.complete
  when @pattern_domain
    leaf = domain(pattern(string_node))
    leaf.complete = string_node.complete
  else
    leaf = domain(string_node)
    # defined as list, given string
    leaf.complete = false
  end
  @buffers[:annotation_definition_domain] = leaf
}

action add_list_value {
  trace('DEFINE_ANNOTATION add_list_value')
  list_node = @buffers.delete(:list)
  if @url_domain
    leaf = domain(url(list_node))
    # defined as url, given list
    leaf.completed = false
  elsif @pattern_domain
    leaf = domain(pattern(list_node))
    # defined as url, given pattern
    leaf.complete = false
  else
    leaf = domain(list_node)
    leaf.complete = list_node.complete
  end
  @buffers[:annotation_definition_domain] = leaf
}

action add_list_domain {
  trace('DEFINE_ANNOTATION add_list_domain')
  @list_domain = true
}

action add_uri_domain {
  trace('DEFINE_ANNOTATION add_uri_domain')
  @uri_domain = true
}

action add_url_domain {
  trace('DEFINE_ANNOTATION add_url_domain')
  @url_domain = true
}

action add_pattern_domain {
  trace('DEFINE_ANNOTATION add_pattern_domain')
  @pattern_domain = true
}

action define_annotation_end {
  trace('DEFINE_ANNOTATION define_annotation_end')
  annotation_definition_node = annotation_definition()

  name = @buffers.delete(:annotation_definition_name)
  annotation_definition_node <<= keyword(name)

  domain = @buffers.delete(:annotation_definition_domain)
  unless domain.nil?
    annotation_definition_node <<= domain
    annotation_definition_node.complete = domain.complete
  end
  @buffers[:annotation_definition] = annotation_definition_node
}

action yield_define_annotation {
  trace('DEFINE_ANNOTATION yield_define_annotation')
  yield @buffers[:annotation_definition]
}

action define_annotation_node_eof {
  trace('DEFINE_ANNOTATION define_annotation_node_eof')
  annotation_definition_node = annotation_definition()

  case
  when @uri_domain
    domain_node = domain(uri())
  when @url_domain
    domain_node = domain(url())
  when @pattern_domain
    domain_node = domain(pattern())
  else
    domain_node = domain()
  end

  domain_node.complete = false
  list_node = @buffers.delete(:list)
  unless list_node.nil?
    domain_node <<= list_node
    domain_node.complete = list_node.complete
  end
  string_node = @buffers.delete(:string)
  unless string_node.nil?
    domain_node <<= string_node
    domain_node.complete = string_node.complete
  end
  annotation_definition_node <<= domain_node
  annotation_definition_node.complete = domain_node.complete
  yield annotation_definition_node
}

string_value =
  a_string
  %add_string_value
  ;

list_value =
  a_list
  %add_list_value
  ;

value =
  string_value |
  list_value
  ;

list_domain =
  KW_LIST
  %add_list_domain
  ;

uri_domain =
  KW_URI
  %add_uri_domain
  ;

url_domain =
  KW_URL
  %add_url_domain
  ;

pattern_domain =
  KW_PATTERN
  %add_pattern_domain
  ;

domain =
  list_domain |
  uri_domain |
  url_domain |
  pattern_domain
  ;

define_annotation_node :=
  DEFINE_ANNOTATION
  SP+
  an_ident
  %add_name
  SP+
  KW_AS
  SP+
  domain
  SP+
  value
  @eof(define_annotation_node_eof)
  %define_annotation_end
  %yield_define_annotation
  NL
  ;

}%%

end

# end: ragel

require_relative '../ast/node' require_relative '../mixin/buffer' require_relative '../nonblocking_io_wrapper' require_relative '../tracer'

module BELParser

module Parsers
  module BELScript
    module DefineAnnotation

      class << self

        MAX_LENGTH = 1024 * 128 # 128K

        def parse(content)
          return nil unless content

          Parser.new(content).each do |obj|
            yield obj
          end
        end
      end

      private

      class Parser
        include Enumerable
        include BELParser::Parsers::Buffer
        include BELParser::Parsers::AST::Sexp
        include BELParser::Parsers::Tracer

        def initialize(content)
          @content = content
    # begin: ragel
          %% write data;
    # end: ragel
        end

        def each
          @buffers = {}
          @incomplete = {}
          @list_domain = false
          @url_domain = false
          @pattern_domain = false
          data     = @content.unpack('C*')
          p        = 0
          pe       = data.length
          eof      = data.length

    # begin: ragel
          %% write init;
          %% write exec;
    # end: ragel
        end
      end
    end
  end
end

end

if __FILE__ == $0

$stdin.each_line do |line|
  BELParser::Parsers::BELScript::DefineAnnotation.parse(line) { |obj|
    puts obj.inspect
  }
end

end

# vim: ft=ruby ts=2 sw=2: # encoding: utf-8