# begin: ragel

begin

%%{

machine bel;

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

action start_list {
  trace('LIST start_list')
  @list_opened = true
  @incomplete[:list] = list()
}

action stop_list {
  trace('LIST stop_list')
  @list_closed = true
}

action add_string {
  trace('LIST add_string')
  string = @buffers.delete(:string)
  item = list_item(string, complete: string.complete)
  @incomplete[:list] <<= item
}

action add_ident {
  trace('LIST add_ident')
  ident = @buffers.delete(:ident)
  item = list_item(ident, complete: ident.complete)
  @incomplete[:list] <<= item
}

action list_end {
  trace('LIST list_end')
  if @list_opened && @list_closed
    list = @incomplete.delete(:list)
    list.complete = true
  elsif !@list_closed
    list = @incomplete.delete(:list)
    list.complete = false
  end
  @buffers[:list] = list
}

action a_list_eof {
  trace('LIST a_list_eof')
  list = @incomplete.delete(:list)
  string = @buffers.delete(:string)
  unless string.nil?
    item = list_item(string, complete: string.complete)
    list <<= item
  end
  ident = @buffers.delete(:ident)
  unless ident.nil?
    item = list_item(ident, complete: ident.complete)
    list <<= item
  end
  if @list_opened && @list_closed
    list.complete = true
  else
    list.complete = false
  end
  @buffers[:list] = list
}

action list_node_eof {
  trace('LIST list_node_eof')
  list = @incomplete.delete(:list)
  string = @buffers.delete(:string)
  item = list_item(string, complete: string.complete)
  list <<= item
  list.complete = false
  yield list
}

action yield_list {
  trace('LIST yield_list')
  yield @buffers[:list]
}

START_LIST = '{' SP* >start_list;
END_LIST = '}' %stop_list;

string_item =
  a_string
  %add_string
  ;

ident_item =
  an_ident
  %add_ident
  ;

item =
  string_item |
  ident_item
  ;

list_item_0 =
  item
  ;

list_item_n =
  COMMA_DELIM
  item
  ;

items =
  list_item_0
  list_item_n*
  SP*
  ;

a_list =
  START_LIST
  items
  END_LIST
  %list_end
  @eof(a_list_eof)
  ;

list_node :=
  START_LIST
  items?
  @eof(list_node_eof)
  END_LIST?
  @eof(list_node_eof)
  NL?
  %list_end
  %yield_list
  ;

}%%

end

# end: ragel

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

module BELParser

module Parsers
  module Common
    module List

      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_opened  = false
          @list_closed  = 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::Common::List.parse(line) { |obj|
    puts obj.inspect
  }
end

end

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