require 'slaw/parse/grammar_helpers' require 'slaw/grammars/za/act_nodes' require 'slaw/grammars/za/postprocess'

require 'slaw/grammars/terminals' require 'slaw/grammars/tables' require 'slaw/grammars/schedules' require 'slaw/grammars/inlines'

module Slaw

module Grammars
  module ZA
    grammar Act
      include Slaw::Parse::GrammarHelpers
      include Slaw::Grammars::ZA::Postprocess

      ########
      # major containers
      #
      # These are AKN's heirarchical containers which wrap actual content.

      rule act
        empty_line*
        preface:preface?
        preamble:preamble?
        body
        schedules:schedules_container? <Act>
      end

      rule preface
        !'PREAMBLE'
        !'BODY'
        ('PREFACE'i space? eol)?
        statements:(!'PREAMBLE' !'BODY' preface_statement)* <Preface>
      end

      rule preamble
        !'BODY'
        'PREAMBLE'i space? eol
        statements:(!'BODY' preamble_statement)* <Preamble>
      end

      rule body
        ('BODY' space? eol)?
        children:(chapter / part / subpart / section / subsection / generic_container)* <Body>
      end

      # chapter (parts allowed)
      rule chapter
        heading:chapter_heading
        children:(part_no_chapter / subpart / section / subsection / generic_container)*
        <Chapter>
      end

      # part (chapters allowed)
      rule part
        heading:part_heading
        children:(chapter_no_part / subpart / section / subsection / generic_container)*
        <Part>
      end

      # part (no chapters)
      rule part_no_chapter
        heading:part_heading
        children:(subpart / section / subsection / generic_container)*
        <Part>
      end

      # chapter (no parts)
      rule chapter_no_part
        heading:chapter_heading
        children:(subpart / section / subsection / generic_container)*
        <Chapter>
      end

      rule subpart
        heading:subpart_heading
        children:(section / subsection / generic_container)*
        <Subpart>
      end

      rule section
        section_title
        children:(subsection / generic_container)* <Section>
      end

      rule subsection
        space? subsection_prefix space? block_elements_with_inline <Subsection>
      end

      rule generic_container
        crossheading / block_elements
      end

      rule crossheading
        'CROSSHEADING ' inline_items:inline_items eol
        <Crossheading>
      end

      # An option inline block element, followed by consecutive non-structured content.
      # This is useful after a rule that can start a new container and have
      # the first element of the container on the same line, eg.:
      #
      # (1) subsection with content
      #     (a) list a...
      #
      rule block_elements_with_inline
        first_child:inline_block_element?
        children:block_element* <BlockElementsWithInline>
      end

      # Consecutive non-structured content. We allow many elements
      # here so that we wrap consecutive block elements in one <content> tag,
      # rather than multiple containers for each.
      rule block_elements
        block_element+ <BlockElements>
      end

      ##########
      # group elements
      # 
      # these are used externally and provide support when parsing just
      # a particular portion of a document

      rule chapters
        children:chapter+ <GroupNode>
      end

      rule chapters_no_parts
        children:chapter_no_part+ <GroupNode>
      end

      rule parts
        children:part+ <GroupNode>
      end

      rule parts_no_chapters
        children:part_no_chapter+ <GroupNode>
      end

      rule subparts
        children:subpart+ <GroupNode>
      end

      rule sections
        children:section+ <GroupNode>
      end

      ##########
      # headings

      rule chapter_heading
        space? chapter_heading_prefix heading:(newline? space? inline_items)? eol
        <ChapterHeading>
      end

      rule part_heading
        space? part_heading_prefix heading:(newline? space? inline_items)? eol
        <PartHeading>
      end

      rule subpart_heading
        space? subpart_heading_prefix heading:(newline? space? inline_items)? eol
        <SubpartHeading>
      end

      rule section_title
        section_title_1 / section_1_title
      end

      rule section_title_1
        &{ |s| options[:section_number_after_title] }
        # Section title
        # 1. Section content
        space? inline_items eol
        section_title_prefix whitespace <SectionTitleType1>
      end

      rule section_1_title
        # 1. Section title
        # Section content
        #
        # Additionally, the section title is optional.
        !{ |s| options[:section_number_after_title] }
        space? section_title_prefix section_title:section_title_content? eol?
        <SectionTitleType2>
      end

      rule section_title_content
        # don't match subsections, eg.
        #
        # 10. (1) subsection content...
        space !subsection_prefix inline_items eol
      end

      ##########
      # blocks of content inside containers

      rule block_element
        (table / blocklist / naked_statement)
      end

      # Block elements that don't have to appear at the start of a line.
      # ie. we don't need to guard against the start of a chapter, section, etc.
      rule inline_block_element
        (table / blocklist / inline_statement)
      end

      rule blocklist
        blocklist_item+ <Blocklist>
      end

      rule blocklist_item
        # TODO: this whitespace should probably be space, to allow empty blocklist items followed by plain text
        space? blocklist_item_prefix whitespace item_content:(!blocklist_item_prefix inline_items:inline_items? eol)? eol?
        <BlocklistItem>
      end

      rule blocklist_item_prefix
        ('(' letter_ordinal ')') / dotted_number_3
      end

      ##########
      # statements - single lines of content
      #
      # If a statement starts with a backslash, it's considered to have escaped the subsequent word,
      # and is ignored. This allows escaping of section headings, etc.

      rule naked_statement
        space? !body_hierarchy_prefix '\\'? inline_items:inline_items? eol
        <NakedStatement>
      end

      rule preface_statement
        space? !non_body_hierarchy_prefix
        content:(longtitle / ('\\'? inline_items:inline_items eol))
        <PrefaceStatement>
      end

      rule preamble_statement
        space? !non_body_hierarchy_prefix '\\'? inline_items eol
        <NakedStatement>
      end

      rule longtitle
        'LONGTITLE ' inline_items:inline_items eol
        <LongTitle>
      end

      ##########
      # prefixes

      rule part_heading_prefix
        'part'i space alphanums [ :-]*
      end

      rule subpart_heading_prefix
        'subpart'i num:(space alphanums)? [ :-]*
      end

      rule chapter_heading_prefix
        'chapter'i space alphanums [ :-]*
      end

      rule section_title_prefix
        number_letter '.'?
      end

      rule subsection_prefix
        # there are two subsection handling syntaxes:
        # 
        # (1) foo
        # (2A) foo
        #
        # and
        #
        # 8.2 for
        # 8.3 bar
        #
        # The second is less common, but this allows us to handle it.
        # Note that it is usually accompanied by a similar list number format:
        #
        # 8.2.1 item 1
        # 8.2.2 item 2
        #
        # which aren't subsections, but lists, so force the space at the end
        # of the number to catch this case.
        num:('(' number_letter ')')
        /
        num:dotted_number_2 '.'? (space / newline)
      end

      rule body_hierarchy_prefix
        # Text that indicates the start of a hierarchy element, in the body
        chapter_heading / part_heading / subpart_heading / section_title / schedule_title / subsection_prefix / crossheading
      end

      rule non_body_hierarchy_prefix
        # Text that indicates the start of a hierarchy element, in the preamble or preface
        chapter_heading / part_heading / subpart_heading / section_title / schedule_title / crossheading
      end

      include Slaw::Grammars::Inlines
      include Slaw::Grammars::Tables
      include Slaw::Grammars::Schedules
      include Slaw::Grammars::Terminals
    end
  end
end

end