# begin: ragel

begin

%%{

machine bel;

include 'simple_statement.rl';

action statement_init {
  trace('NESTED_STATEMENT statement_init')
  @buffers[:statement_stack] = [ statement() ]
}

action inner_statement_init {
  trace('NESTED_STATEMENT inner_statement_init')
  @buffers[:statement_stack] << statement()
}

action ast_subject {
  trace('NESTED_STATEMENT ast_subject')
  subject_node = @buffers[:subject]
  stmt = @buffers[:statement_stack][-1] << subject_node
  @buffers[:statement_stack][-1] = stmt
}

action ast_relationship {
  trace('NESTED_STATEMENT ast_relationship')
  rel_node = @buffers[:relationship]
  stmt = @buffers[:statement_stack][-1] << rel_node
  @buffers[:statement_stack][-1] = stmt
}

action ast_object {
  trace('NESTED_STATEMENT ast_object')
  object_node = @buffers[:object]
  stmt = @buffers[:statement_stack][-1] << object_node
  @buffers[:statement_stack][-1] = stmt
}

action statement_object_statement {
  trace('NESTED_STATEMENT statement_object_statement')
  @buffers[:object] = @buffers[:statement_stack][-1]
}

action call_nested_statement {
  trace('NESTED_STATEMENT call_nested_statement')
  fcall inner_statement;
}

action fret {
  trace('NESTED_STATEMENT fret')
  inner_statement = @buffers[:statement_stack].pop
  @buffers[:object] = inner_statement
  obj_node = object(inner_statement)
  stmt = @buffers[:statement_stack][-1] << obj_node
  @buffers[:statement_stack][-1] = stmt
  nested_stmt = nested_statement(stmt)
  nested_stmt.complete = true
  @buffers[:nested_statement] = nested_stmt
  fret;
}

action nested_statement_comment {
  trace('NESTED_STATEMENT nested_statement_comment')
  comment = @buffers[:comment]
  @buffers[:nested_statement] =
    nested_statement(
      @buffers[:nested_statement].statement << comment)
}

action yield_nested_statement {
  trace('NESTED_STATEMENT yield_nested_statement')
  yield @buffers[:nested_statement]
}

inner_statement :=
  outer_term >inner_statement_init %statement_subject %ast_subject
  SP+
  relationship %relationship_end %ast_relationship
  SP+
  (
    outer_term @statement_object @ast_object |
    '(' @call_nested_statement ')'
  ) @fret;

outer_statement =
  outer_term >statement_init %statement_subject %ast_subject
  SP+
  relationship %relationship_end %ast_relationship
  SP+
  '(' @call_nested_statement
  ')'
  # ')' @ast_object
  ;

nested_statement :=
  outer_statement
  SP*
  comment? %nested_statement_comment %yield_nested_statement
  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 Expression
    module NestedStatement

      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 = {}
          eof         = :ignored
          stack       = []
          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::Expression::NestedStatement.parse(line) { |obj|
    puts obj.inspect
  }
end

end

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