module Treetop

module Compiler
  grammar Metagrammar
    rule treetop_file
      requires:(space? require_statement)* spacing:space? module_or_grammar suffix:space? {
        def compile
          requires.text_value + spacing.text_value + module_or_grammar.compile + suffix.text_value
        end
      }
    end

    rule require_statement
      spacing:space? "require" [ \t]+ [^\n\r]+ [\n\r]
    end

    rule module_or_grammar
      module_declaration / grammar
    end

    rule module_declaration
      module_prefix:('module' space name:([A-Z] alphanumeric_char* ('::' [A-Z] alphanumeric_char*)*) space) module_contents:(module_declaration / grammar) suffix:(space 'end') {
        def compile
          module_prefix.text_value + module_contents.compile + suffix.text_value
        end

        def parser_name
          module_prefix.name.text_value+'::'+module_contents.parser_name
        end
      }
    end

    rule grammar
      'grammar' space grammar_name space ('do' space)? declaration_sequence space? 'end' <Grammar>
    end

    rule grammar_name
      ([A-Z] alphanumeric_char*)
    end

    rule declaration_sequence
      head:declaration tail:(space declaration)* <DeclarationSequence> {
        def declarations
          [head] + tail
        end

        def tail
          super.elements.map { |elt| elt.declaration }
        end
      }
      /
      '' {
        def compile(builder)
        end
      }
    end

    rule declaration
      parsing_rule / include_declaration
    end

    rule include_declaration
      'include' space [A-Z] (alphanumeric_char / '::')* {
        def compile(builder)
          builder << text_value
        end
      }
    end

    rule parsing_rule
      'rule' space nonterminal space ('do' space)? parsing_expression space 'end' <ParsingRule>
    end

    rule parsing_expression
      choice / sequence / primary
    end

    rule choice
      head:alternative tail:(space? '/' space? alternative)+ <Choice> {
        def alternatives
          [head] + tail
        end

        def tail
          super.elements.map {|elt| elt.alternative}
        end

        def parent_modules
          []
        end

        def inline_modules
          (alternatives.map {|alt| alt.inline_modules }).flatten
        end
      }
    end

    rule sequence
      sequence_body node_class_declarations <Sequence> {
        def sequence_elements
          [sequence_body.head] + tail
        end

        def tail
          sequence_body.tail
        end

        def parent_modules
          node_class_declarations.inline_modules
        end

        def inline_modules
          (sequence_elements.map {|elt| elt.inline_modules}).flatten +
          [sequence_element_accessor_module] +
          parent_modules
        end

        def inline_module_name
          node_class_declarations.inline_module_name
        end
      }
    end

    rule sequence_body
      variable_length_sequence_body / labeled_expression_sequence_body
    end

    rule variable_length_sequence_body
      head:optionally_labeled_sequence_primary tail:(space optionally_labeled_sequence_primary)+ {
        def tail
          super.elements.map {|elt| elt.optionally_labeled_sequence_primary }
        end
      }
    end

    rule labeled_expression_sequence_body
      labeled_sequence_primary {
        def head
          self
        end

        def tail
          []
        end
      }
    end

    rule alternative
      sequence / primary
    end

    rule primary
      prefix atomic {
        def compile(address, builder, parent_expression=nil)
          prefix.compile(address, builder, self)
        end

        def prefixed_expression
          atomic
        end

        def parent_modules
          []
        end

        def inline_modules
          atomic.inline_modules
        end

        def inline_module_name
          nil
        end
      }
      /
      prefix space? atomic:predicate_block {
        def compile(address, builder, parent_expression=nil)
          prefix.compile(address, builder, self)
        end
        def prefixed_expression
          atomic
        end
        def parent_modules
          []
        end
        def inline_modules
          []
        end
      }
      /
      atomic suffix node_class_declarations {
        def compile(address, builder, parent_expression=nil)
          STDERR.puts "Extraneous module ignored after suffix: #{input[interval].inspect}" if node_class_declarations.inline_modules.size > 0 && atomic.inline_modules.size > 0
          suffix.compile(address, builder, self)
        end

        def optional_expression
          atomic
        end

        def node_class_name
          node_class_declarations.node_class_name
        end

        def parent_modules
          node_class_declarations.inline_modules
        end

        def inline_modules
          atomic.inline_modules + parent_modules
        end

        def inline_module_name
          node_class_declarations.inline_module_name
        end
      }
      /
      atomic node_class_declarations {
        def compile(address, builder, parent_expression=nil)
          STDERR.puts "Extraneous module ignored with nested atomic: #{input[interval].inspect}" if node_class_declarations.inline_modules.size > 0 && atomic.inline_modules.size > 0
          atomic.compile(address, builder, self)
        end

        def node_class_name
          node_class_declarations.node_class_name
        end

        def parent_modules
          node_class_declarations.inline_modules
        end

        def inline_modules
          atomic.inline_modules + parent_modules
        end

        def inline_module_name
          node_class_declarations.inline_module_name
        end
      }
    end

    rule optionally_labeled_sequence_primary
      labeled_sequence_primary / unlabeled_sequence_primary
    end

    rule labeled_sequence_primary
      named_label sequence_primary {
        def compile(lexical_address, builder)
          sequence_primary.compile(lexical_address, builder)
        end

        def parent_modules
          []
        end

        def inline_modules
          sequence_primary.inline_modules
        end

        def label_name
          named_label.name
        end
      }
    end

    rule unlabeled_sequence_primary
      null_label sequence_primary {
        def compile(lexical_address, builder)
          sequence_primary.compile(lexical_address, builder)
        end

        def parent_modules
          []
        end

        def inline_modules
          sequence_primary.inline_modules
        end

        def label_name
          if sequence_primary.instance_of?(Nonterminal)
            sequence_primary.text_value
          else
            nil
          end
        end
      }
    end

    rule label
      named_label / null_label
    end

    rule named_label
      (alpha_char alphanumeric_char*) ':' {
        def name
          elements[0].text_value
        end
      }
    end

    rule null_label
      '' {
        def name
          nil
        end
      }
    end

    rule sequence_primary
      prefix atomic {
        def compile(lexical_address, builder)
          prefix.compile(lexical_address, builder, self)
        end

        def prefixed_expression
          elements[1]
        end

        def parent_modules
          []
        end

        def inline_modules
          atomic.inline_modules
        end

        def inline_module_name
          nil
        end
      }
      /
      prefix space? atomic:predicate_block {
        def compile(address, builder, parent_expression=nil)
          prefix.compile(address, builder, self)
        end
        def prefixed_expression
          atomic
        end
        def parent_modules
          []
        end
        def inline_modules
          []
        end
      }
      /
      atomic suffix {
        def compile(lexical_address, builder)
          suffix.compile(lexical_address, builder, self)
        end

        def node_class_name
          nil
        end

        def parent_modules
          []
        end

        def inline_modules
          atomic.inline_modules
        end

        def inline_module_name
          nil
        end
      }
      /
      atomic
    end

    rule suffix
      repetition_suffix / optional_suffix
    end

    rule optional_suffix
      '?' <Optional>
    end

    rule node_class_declarations
      node_class_expression trailing_inline_module {
        def node_class_name
          node_class_expression.node_class_name
        end

# !!!! cjh !!!!!

        def inline_modules
          trailing_inline_module.inline_modules
        end

        def inline_module
          trailing_inline_module.inline_module
        end

        def inline_module_name
          inline_module.module_name if inline_module
        end
      }
    end

    rule repetition_suffix
      '+' <OneOrMore> / '*' <ZeroOrMore> / occurrence_range
    end

    rule occurrence_range
      space? min:([0-9])* '..' max:([0-9])* <OccurrenceRange>
    end

    rule prefix
      '&' <AndPredicate> / '!' <NotPredicate> / '~' <TransientPrefix>
    end

    rule atomic
      terminal
      /
      nonterminal
      /
      parenthesized_expression
    end

    rule parenthesized_expression
      '(' space? parsing_expression space? ')' <ParenthesizedExpression> {
        def parent_modules
          []
        end
        def inline_modules
          parsing_expression.inline_modules
        end
      }
    end

    rule nonterminal
      !keyword_inside_grammar (alpha_char alphanumeric_char*) <Nonterminal>
    end

    rule terminal
      quoted_string / character_class / anything_symbol
    end

    rule quoted_string
      qs:(single_quoted_string / double_quoted_string) modifiers:([ir]*) <Terminal> {
        def string
          qs.text_value
        end
      }
    end

    rule double_quoted_string
      '"' string:(!'"' ("\\\\" / '\"' / .))* '"'
    end

    rule single_quoted_string
      "'" string:(!"'" ("\\\\" / "\\'" / .))* "'"
    end

    rule character_class
      '[' characters:(!']' ('\\' . / bracket_expression / !'\\' .))+ ']' <CharacterClass> {
        def characters
          super.text_value
        end
      }
    end

    rule bracket_expression
       '[:' '^'? (
         'alnum' / 'alpha' / 'blank' / 'cntrl' / 'digit' / 'graph' / 'lower' /
         'print' / 'punct' / 'space' / 'upper' / 'xdigit' / 'word'
       ) ':]'
    end

    rule anything_symbol
      '.' <AnythingSymbol>
    end

    rule node_class_expression
      space '<' (!'>' .)+ '>' {
        def node_class_name
          elements[2].text_value
        end
      }
      /
      '' {
        def node_class_name
          nil
        end
      }
    end

    rule trailing_inline_module
      space inline_module {
        def parent_modules
          []
        end
        def inline_modules
          [inline_module]
        end

        def inline_module_name
          inline_module.module_name
        end
      }
      /
      '' {
        def parent_modules
          []
        end
        def inline_modules
          []
        end

        def inline_module
          nil
        end

        def inline_module_name
          nil
        end
      }
    end

    rule predicate_block
      '' inline_module <PredicateBlock>
    end

    rule inline_module
      '{' (inline_module / ![{}] .)* '}' <InlineModule>
    end

    rule keyword_inside_grammar
      ('rule' / 'end') !non_space_char
    end

    rule non_space_char
      !space .
    end

    rule alpha_char
      [A-Za-z_]
    end

    rule alphanumeric_char
      alpha_char / [0-9]
    end

    rule space
      (white / comment_to_eol)+
    end

    rule comment_to_eol
      '#' (!"\n" .)*
    end

    rule white
      [ \t\n\r]
    end
  end
end

end