#+TITLE: sdp document abstraction
#+AUTHOR: Ralph Amissah
#+EMAIL: ralph.amissah@gmail.com
#+STARTUP: indent
#+LANGUAGE: en
#+OPTIONS: H:3 num:nil toc:t \n:nil @:t ::t |:t ^:nil _:nil -:t f:t *:t <:t
#+OPTIONS: TeX:t LaTeX:t skip:nil d:nil todo:t pri:nil tags:not-in-toc
#+OPTIONS: author:nil email:nil creator:nil timestamp:nil
#+PROPERTY: header-args :padline no :exports code :noweb yes
#+EXPORT_SELECT_TAGS: export
#+EXPORT_EXCLUDE_TAGS: noexport
#+FILETAGS: :sdp:rel:ao:
#+TAGS: assert(a) class(c) debug(d) mixin(m) sdp(s) tangle(T) template(t) WEB(W) noexport(n)

[[./sdp.org][sdp]]  [[./][org/]]
* 1. Document Abstraction                                  :abstract:process:
Process markup document, create document abstraction.

** 0. ao abstract doc source:                       :ao_abstract_doc_source:

#+BEGIN_SRC d :tangle ../src/sdp/ao_abstract_doc_source.d
/++
  document abstraction:
  abstraction of sisu markup for downstream processing
  ao_abstract_doc_source.d
+/
template SiSUdocAbstraction() {
  /+ ↓ abstraction imports +/
  <<abs_top_imports>>
  /+ ↓ abstraction mixins +/
  <<abs_top_mixins>>
  /+ ↓ abstraction struct init +/
  <<abs_top_init_struct>>
  /+ ↓ abstract marked up document +/
  auto SiSUdocAbstraction(Src,Make,Meta,Opt)(
    Src                  markup_sourcefile_content,
    Make                 dochead_make_aa,
    Meta                 dochead_meta_aa,
    Opt                  opt_action_bool,
  ) {
    auto rgx = Rgx();
    debug(asserts){
      static assert(is(typeof(markup_sourcefile_content) == char[][]));
      static assert(is(typeof(dochead_make_aa)           == string[string][string]));
      static assert(is(typeof(dochead_meta_aa)           == string[string][string]));
      static assert(is(typeof(opt_action_bool)           == bool[string]));
    }
    /+ ↓ abstraction init +/
    <<abs_init_rest>>
    /+ abstraction init ↑ +/
    /+ ↓ loop markup document/text line by line +/
    srcDocLoop:
    foreach (line; markup_sourcefile_content) {
      /+ ↓ markup document/text line by line +/
      <<abs_in_loop_body_00>>
      if (type["code"] == TriState.on) {
        <<abs_in_loop_body_00_code_block>>
      } else if (!matchFirst(line, rgx.skip_from_regular_parse)) {
        /+ object other than "code block" object
           (includes regular text paragraph, headings & blocks other than code) +/
        <<abs_in_loop_body_non_code_obj>>
        } else {
          /+ not within a block group +/
          <<abs_in_loop_body_open_block_obj_assert>>
          if (matchFirst(line, rgx.block_open)) {
            <<abs_in_loop_body_open_block_obj>>
          } else if (!line.empty) {
            /+ line not empty +/
            /+ non blocks (headings, paragraphs) & closed blocks +/
            <<abs_in_loop_body_not_block_obj>>
          } else if (type["blocks"] == TriState.closing) {
            /+ line empty, with blocks flag +/
            <<abs_in_loop_body_not_block_obj_line_empty_blocks_flags>>
          } else {
            /+ line.empty, post contents, empty variables: +/
            <<abs_in_loop_body_not_block_obj_line_empty>>
          } // close else for line empty
        } // close else for not the above
      } // close after non code, other blocks or regular text
      <<abs_in_loop_body_01>>
    } /+ ← closed: loop markup document/text line by line +/
    /+ ↓ post loop markup document/text +/
    <<abs_post>>
    /+ post loop markup document/text ↑ +/
  } /+ ← closed: abstract doc source +/
  /+ ↓ abstraction functions +/
  <<abs_functions_object_reset>>
  <<abs_functions_header_set_common>>
  <<abs_functions_ocn_status>>
  <<abs_functions_block>>
  <<abs_functions_block_code>>
  <<abs_functions_block_biblio>>
  // <<abs_functions_block_glossary>>
  <<abs_functions_block_poem>>
  <<abs_functions_block_group>>
  <<abs_functions_block_block>>
  <<abs_functions_block_quote>>
  <<abs_functions_block_table>>
  <<abs_functions_block_line_status_empty>>
  <<abs_functions_book_index>>
  <<abs_functions_heading>>
  <<abs_functions_para>>
  /+ abstraction functions ↑ +/
  /+ ↓ abstraction function emitters +/
  <<ao_emitters_ocn>>
  /+ +/
  <<ao_emitters_obj_inline_markup_munge>>
  <<ao_emitters_obj_inline_markup>>
  <<ao_emitters_obj_inline_markup_and_anchor_tags>>
  <<ao_emitters_obj_inline_markup_table_of_contents>>
  <<ao_emitters_obj_inline_markup_private>>
  <<ao_emitters_obj_inline_markup_heading_numbering_segment_anchor_tags>>
  <<ao_emitters_obj_inline_markup_close>>
  /+ +/
  <<ao_emitters_obj_attributes>>
  <<ao_emitters_obj_attributes_public>>
  <<ao_emitters_obj_attributes_private>>
  <<ao_emitters_obj_attributes_private_an_attribute>>
  <<ao_emitters_obj_attributes_private_json>>
  <<ao_emitters_obj_attributes_private_close>>
  /+ +/
  <<ao_emitters_book_index_nugget>>
  <<ao_emitters_book_index_report_indented>>
  <<ao_emitters_book_index_report_section>>
  /+ +/
  <<ao_emitters_endnotes>>
  /+ +/
  <<ao_emitters_bibliography>>
  /+ +/
  <<ao_emitters_metadata>>
  /+ abstraction functions emitters ↑ +/
  /+ ↓ abstraction functions assertions +/
  <<abs_functions_assertions>>
  /+ abstraction functions assertions ↑ +/
} /+ ← closed: template SiSUdocAbstraction +/
#+END_SRC

** 1. _pre loop processing_                                            :pre:
*** imports                                                       :imports:
[[./ao_defaults.org][ao_defaults]]

#+name: abs_top_imports
#+BEGIN_SRC d
import
  ao_object_setter,
  ao_defaults,
  ao_rgx,
  output_hub;
private import
  std.algorithm,
  std.array,
  std.container,
  std.exception,
  std.file,
  std.getopt,
  std.json,
  std.path,
  std.process,
  std.range,
  std.regex,
  std.stdio,
  std.string,
  std.traits,
  std.typecons,
  std.uni,
  std.utf,
  std.conv : to;
#+END_SRC

*** mixins                                                         :mixins:

#+name: abs_top_mixins
#+BEGIN_SRC d
mixin ObjectSetter;
mixin InternalMarkup;
mixin SiSUrgxInit;
#+END_SRC

*** initialize                                                 :initialize:
**** initialize general

#+name: abs_top_init_struct
#+BEGIN_SRC d
/+ initialize +/
ObjGenericComposite[][string] the_table_of_contents_section;
ObjGenericComposite[] the_document_head_section, the_document_body_section, the_bibliography_section, the_glossary_section, the_blurb_section;
ObjGenericComposite[] the_dom_tail_section;
string[string] an_object, processing;
string an_object_key;
string[] anchor_tags;
string anchor_tag_;
string segment_anchor_tag_that_object_belongs_to;
string segment_anchor_tag_that_object_belongs_to_uri;
auto note_section = NotesSection();
/+ enum +/
enum State { off, on }
enum TriState { off, on, closing }
enum DocStructMarkupHeading {
  h_sect_A,
  h_sect_B,
  h_sect_C,
  h_sect_D,
  h_text_1,
  h_text_2,
  h_text_3,
  h_text_4,
  h_text_5, // extra level, drop
  content_non_header
} // header section A-D; header text 1-4
enum DocStructCollapsedHeading { lv0, lv1, lv2, lv3, lv4, lv5, lv6, lv7 }
/+ biblio variables +/
string biblio_tag_name, biblio_tag_entry, st;
string[] biblio_arr_json;
string biblio_entry_str_json;
JSONValue[] bib_arr_json;
int bib_entry;
/+ counters +/
int cntr, previous_count, previous_length;
int[string] line_occur;
string[] html_segnames=["toc"];
int html_segnames_ptr=0;
int html_segnames_ptr_cntr=0;
int verse_line, heading_ptr;
/+ paragraph attributes +/
int[string] indent;
bool bullet = true;
string content_non_header = "8";
auto obj_im = ObjInlineMarkup();
auto obj_att = ObjAttributes();
/+ ocn +/
int obj_cite_number, obj_cite_number_;
auto object_citation_number = OCNemitter();
int[] dom_markedup = [ 0, 0, 0, 0, 0, 0, 0, 0, 0,];
int[] dom_markedup_buffer = [ 0, 0, 0, 0, 0, 0, 0, 0, 0,];
int[] dom_collapsed = [ 0, 0, 0, 0, 0, 0, 0, 0, 0,];
int[] dom_collapsed_buffer = [ 0, 0, 0, 0, 0, 0, 0, 0, 0,];
enum DomTags { none, open, close, close_and_open, open_still, }
#+END_SRC

**** initialize heading ancestors

#+name: abs_top_init_struct
#+BEGIN_SRC d
void heading_ancestors(O)(
  auto ref O         obj,
  ref string[]       lv_ancestors,
) {
  switch (obj.heading_lev_markup) {
  case 0:
    lv_ancestors[0] = to!string(obj.text);
    foreach(k; 1..8) {
      lv_ancestors[k] = "";
    }
    goto default;
  case 1:
    lv_ancestors[1] = to!string(obj.text);
    foreach(k; 2..8) {
      lv_ancestors[k] = "";
    }
    goto default;
  case 2:
    lv_ancestors[2] = to!string(obj.text);
    foreach(k; 3..8) {
      lv_ancestors[k] = "";
    }
    goto default;
  case 3:
    lv_ancestors[3] = to!string(obj.text);
    foreach(k; 4..8) {
      lv_ancestors[k] = "";
    }
    goto default;
  case 4:
    lv_ancestors[4] = to!string(obj.text);
    foreach(k; 5..8) {
      lv_ancestors[k] = "";
    }
    goto default;
  case 5:
    lv_ancestors[5] = to!string(obj.text);
    foreach(k; 6..8) {
      lv_ancestors[k] = "";
    }
    goto default;
  case 6:
    lv_ancestors[6] = to!string(obj.text);
    lv_ancestors[7] = "";
    goto default;
  case 7:
    lv_ancestors[7] = to!string(obj.text);
    goto default;
  default:
    obj.heading_ancestors_text = lv_ancestors.dup;
  }
}
#+END_SRC

**** initialize dom markup tags

#+name: abs_top_init_struct
#+BEGIN_SRC d
auto dom_set_markup_tags(int[] dom, int lev) {
  foreach (i; 0 .. 8) {
    if (i < lev) {
      if (dom[i] == DomTags.open
         || dom[i] == DomTags.close_and_open
      ) {
        dom[i] = DomTags.open_still;
      } else if (dom[i] == DomTags.close) {
        dom[i] = DomTags.none;
      }
    } else if (i == lev) {
      if (lev  == 0
        && dom[i] == DomTags.open_still
      ) {
        dom[i] = DomTags.close;
      } else if (dom[i] == DomTags.open
        || dom[i] == DomTags.open_still
        || dom[i] == DomTags.close_and_open
      ) {
        dom[i] = DomTags.close_and_open;
      } else {
        dom[i] = DomTags.open;
      }
    } else if (i > lev) {
      if (dom[i] == DomTags.close) {
        dom[i] = DomTags.none;
      } else if (dom[i] == DomTags.open
        || dom[i] == DomTags.open_still
        || dom[i] == DomTags.close_and_open
      ) {
        dom[i] = DomTags.close;
      }
    }
  }
  debug(dom) {
    writeln(lev, ": ", dom);
  }
  return dom;
}
#+END_SRC

**** initialize dom collapsed tags

#+name: abs_top_init_struct
#+BEGIN_SRC d
auto dom_set_collapsed_tags(int[] dom, int lev) {
  foreach (i; 0 .. 8) {
    if (i < lev) {
      if (dom[i] == DomTags.open
         || dom[i] == DomTags.close_and_open
      ) {
        dom[i] = DomTags.open_still;
      } else if (dom[i] == DomTags.close) {
        dom[i] = DomTags.none;
      }
    } else if (i == lev) {
      if (lev  == 0
        && dom[i] == DomTags.open_still
      ) {
        dom[i] = DomTags.close;
      } else if (dom[i] == DomTags.open
        || dom[i] == DomTags.open_still
        || dom[i] == DomTags.close_and_open
      ) {
        dom[i] = DomTags.close_and_open;
      } else {
        dom[i] = DomTags.open;
      }
    } else if (i > lev) {
      if (dom[i] == DomTags.close) {
        dom[i] = DomTags.none;
      } else if (dom[i] == DomTags.open
        || dom[i] == DomTags.open_still
        || dom[i] == DomTags.close_and_open
      ) {
        dom[i] = DomTags.close;
      }
    }
  }
  debug(dom) {
    writeln(lev, ": ", dom);
  }
  return dom;
}
#+END_SRC

**** initialize ocn emit

#+name: abs_top_init_struct
#+BEGIN_SRC d
int ocn_emit(int ocn_status_flag) {
  return object_citation_number.ocn_emitter(ocn_status_flag);
}
/+ book index variables +/
string book_idx_tmp;
string[][string][string] bookindex_unordered_hashes;
auto bookindex_extract_hash = BookIndexNuggetHash();
string[][string][string] bkidx_hash(
  string bookindex_section,
  int    obj_cite_number
) {
  return bookindex_extract_hash.bookindex_nugget_hash(bookindex_section, obj_cite_number);
}
/+ node +/
ObjGenericComposite comp_obj_heading, comp_obj_location, comp_obj_block, comp_obj_code, comp_obj_poem_ocn, comp_obj_comment;
auto node_construct = NodeStructureMetadata();
#+END_SRC

*** scope

#+name: abs_init_rest
#+BEGIN_SRC d
scope(success) {
}
scope(failure) {
}
scope(exit) {
  destroy(the_document_head_section);
  destroy(the_table_of_contents_section);
  destroy(the_document_body_section);
  destroy(the_bibliography_section);
  destroy(an_object);
  destroy(processing);
  destroy(biblio_arr_json);
}
#+END_SRC

*** init rest

#+name: abs_init_rest
#+BEGIN_SRC d
mixin SiSUrgxInitFlags;
mixin SiSUnode;
auto node_para_int_    = node_metadata_para_int;
auto node_para_str_    = node_metadata_para_str;
ObjGenericComposite comp_obj_heading_, comp_obj_para, comp_obj_toc;
line_occur = [
  "heading"  : 0,
  "para"     : 0,
  "glossary" : 0,
  "blurb"    : 0,
];
auto type = flags_type_init;
string[string] obj_cite_number_poem = [
  "start" : "",
  "end"   : ""
];
string[] lv_ancestors = [ "", "", "", "", "", "", "", "", ];
int[string] lv = [
  "lv" : State.off,
  "h0" : State.off,
  "h1" : State.off,
  "h2" : State.off,
  "h3" : State.off,
  "h4" : State.off,
  "h5" : State.off,
  "h6" : State.off,
  "h7" : State.off,
  "lev_int_collapsed" : 0,
];
int[string] collapsed_lev = [
  "h0" : State.off,
  "h1" : State.off,
  "h2" : State.off,
  "h3" : State.off,
  "h4" : State.off,
  "h5" : State.off,
  "h6" : State.off,
  "h7" : State.off
];
string[string] heading_match_str = [
  "h_A": "^(none)",
  "h_B": "^(none)",
  "h_C": "^(none)",
  "h_D": "^(none)",
  "h_1": "^(none)",
  "h_2": "^(none)",
  "h_3": "^(none)",
  "h_4": "^(none)"
];
auto heading_match_rgx = [
  "h_A": regex(r"^(none)"),
  "h_B": regex(r"^(none)"),
  "h_C": regex(r"^(none)"),
  "h_D": regex(r"^(none)"),
  "h_1": regex(r"^(none)"),
  "h_2": regex(r"^(none)"),
  "h_3": regex(r"^(none)"),
  "h_4": regex(r"^(none)")
];
string _anchor_tag;
string toc_txt_;
an_object["glossary_nugget"] = "";
an_object["blurb_nugget"] = "";
comp_obj_heading_                       = comp_obj_heading_.init;
comp_obj_heading_.use                   = "frontmatter";
comp_obj_heading_.is_of                 = "para";
comp_obj_heading_.is_a                  = "heading";
comp_obj_heading_.text                  = "Table of Contents";
comp_obj_heading_.ocn                   = 0;
comp_obj_heading_.obj_cite_number       = "";
comp_obj_heading_.segment_anchor_tag    = "toc";
comp_obj_heading_.marked_up_level       = "1";
comp_obj_heading_.heading_lev_markup    = 4;
comp_obj_heading_.heading_lev_collapsed = 1;
comp_obj_heading_.parent_ocn            = 1;
comp_obj_heading_.parent_lev_markup     = 0;
comp_obj_heading_.ptr_html_segnames     = html_segnames_ptr;
comp_obj_heading_.anchor_tags           = ["toc"];
auto toc_head                           = comp_obj_heading_;
html_segnames_ptr_cntr++;
the_table_of_contents_section = [
  "seg": [toc_head],
  "scroll": [toc_head],
];
auto mkup = InlineMarkup();
auto munge = ObjInlineMarkupMunge();
string[][string] lev4_subtoc;
#+END_SRC

** 2. _loop: process document body_ [+6]                              :loop:
*** loop scope                                                      :scope:

#+name: abs_in_loop_body_00
#+BEGIN_SRC d
/+ scope +/
scope(exit) {
}
scope(failure) {
  stderr.writefln(
    "%s\n%s\n%s:%s failed here:\n  line: %s",
    __MODULE__, __FUNCTION__,
    __FILE__, __LINE__,
    line,
  );
}
line = replaceAll(line, rgx.true_dollar, "$$$$");
  // dollar represented as $$ needed to stop submatching on $
  // (substitutions using ${identifiers} must take into account (i.e. happen earlier))
debug(source) {                                  // source lines
  writeln(line);
}
debug(srclines) {
  if (!line.empty) {                             // source lines, not empty
    writefln(
      "* %s",
      line
    );
  }
}
#+END_SRC

*** check whether obj_cite_number is on or turned off                 :ocn:

#+name: abs_in_loop_body_00
#+BEGIN_SRC d
if (!line.empty) {
  _check_ocn_status_(line, type);
}
#+END_SRC

*** [#A] separate regular markup text from code blocks [+5]
**** code blocks                                              :block:code:

#+name: abs_in_loop_body_00_code_block
#+BEGIN_SRC d
/+ block object: code +/
_code_block_(line, an_object, type);
continue;
#+END_SRC

**** non code objects (other blocks or regular text) [+4]       :non_code:
***** in section (biblio, glossary, blurb) (block group) [+1] :block:active:
****** within section: biblio                                   :biblio:

#+name: abs_in_loop_body_non_code_obj
#+BEGIN_SRC d
if ((matchFirst(line, rgx.heading_biblio)
|| (type["biblio_section"] == State.on
&& (!matchFirst(line, rgx.heading_blurb_glossary))))
&& (!matchFirst(line, rgx.heading))
&& (!matchFirst(line, rgx.comment))) {
  /+ within section (block object): biblio +/
  type["glossary_section"] = State.off;
  type["biblio_section"] = State.on;
  type["blurb_section"] = State.off;
  if (opt_action_bool["backmatter"] && opt_action_bool["section_biblio"]) {
    _biblio_block_(line, type, bib_entry, biblio_entry_str_json, biblio_arr_json); //
    debug(bibliobuild) {
      writeln("-  ", biblio_entry_str_json);
      writeln("-> ", biblio_arr_json.length);
    }
  }
  continue;
#+END_SRC

****** within section: glossary                               :glossary:

if there is a glossary section you need to:
- extract it
- create standard headings
- markup contents in standard way like regular paragraphs
  - need indentation and regular paragraph inline markup
- reconstitute the document with the glossary section following the endnotes

#+name: abs_in_loop_body_non_code_obj
#+BEGIN_SRC d
} else if ((matchFirst(line, rgx.heading_glossary)
|| (type["glossary_section"] == State.on
&& (!matchFirst(line, rgx.heading_biblio_blurb))))
&& (!matchFirst(line, rgx.heading))
&& (!matchFirst(line, rgx.comment))) {
  /+ within section (block object): glossary +/
  debug(glossary) {
    writeln(__LINE__);
    writeln(line);
  }
  // _glossary_block_(line, type);
  type["glossary_section"] = State.on;
  type["biblio_section"] = State.off;
  type["blurb_section"] = State.off;
  if (opt_action_bool["backmatter"] && opt_action_bool["section_glossary"]) {
    indent=[
      "hang_position" : 0,
      "base_position" : 0,
    ];
    bullet = false;
    type["para"] = State.on;
    line_occur["para"] = State.off;
    an_object_key="glossary_nugget"; //
    if (matchFirst(line, rgx.heading_glossary)) {
      comp_obj_heading_                       = comp_obj_heading_.init;
      comp_obj_heading_.use                   = "backmatter";
      comp_obj_heading_.is_of                 = "para";
      comp_obj_heading_.is_a                  = "heading";
      comp_obj_heading_.text                  = "Glossary";
      comp_obj_heading_.ocn                   = 0;
      comp_obj_heading_.obj_cite_number       = "";
      comp_obj_heading_.marked_up_level       = "B";
      comp_obj_heading_.heading_lev_markup    = 1;
      comp_obj_heading_.heading_lev_collapsed = 1;
      comp_obj_heading_.parent_ocn            = 1;
      comp_obj_heading_.parent_lev_markup     = 0;
      the_glossary_section                    ~= comp_obj_heading_;
      comp_obj_heading_                       = comp_obj_heading_.init;
      comp_obj_heading_.use                   = "backmatter";
      comp_obj_heading_.is_of                 = "para";
      comp_obj_heading_.is_a                  = "heading";
      comp_obj_heading_.text                  = "Glossary";
      comp_obj_heading_.ocn                   = 0;
      comp_obj_heading_.obj_cite_number       = "";
      comp_obj_heading_.segment_anchor_tag    = "glossary";
      comp_obj_heading_.marked_up_level       = "1";
      comp_obj_heading_.heading_lev_markup    = 4;
      comp_obj_heading_.heading_lev_collapsed = 2;
      comp_obj_heading_.parent_ocn            = 1;
      comp_obj_heading_.parent_lev_markup     = 0;
      comp_obj_heading_.anchor_tags           = ["glossary"];
      the_glossary_section                    ~= comp_obj_heading_;
    } else {
      _para_match_(line, an_object, an_object_key, indent, bullet, type, line_occur);
      comp_obj_para                       = comp_obj_para.init;
      comp_obj_para.use                   = "backmatter";
      comp_obj_para.is_of                 = "para";
      comp_obj_para.is_a                  = "glossary";
      comp_obj_para.text                  = to!string(line).strip;
      comp_obj_para.ocn                   = 0;
      comp_obj_para.obj_cite_number       = "";
      comp_obj_para.indent_hang           = indent["hang_position"];
      comp_obj_para.indent_base           = indent["base_position"];
      comp_obj_para.bullet                = bullet;
      the_glossary_section                ~= comp_obj_para;
    }
    type["ocn_status"] = TriState.off;
  }
  continue;
#+END_SRC

****** within section: blurb                                     :blurb:

if there is a blurb section you need to:
- extract it
- create standard headings (or use line provided in 1~ heading)
- markup contents in standard way like regular paragraphs
  - need regular paragraph inline markup
- reconstitute the document with the blurb section at the very end of the doucment

#+name: abs_in_loop_body_non_code_obj
#+BEGIN_SRC d
} else if ((matchFirst(line, rgx.heading_blurb)
|| (type["blurb_section"] == State.on
&& (!matchFirst(line, rgx.heading_biblio_glossary))))
&& (!matchFirst(line, rgx.heading))
&& (!matchFirst(line, rgx.comment))) {
  /+ within section (block object): blurb +/
  debug(blurb) {
    writeln(__LINE__);
    writeln(line);
  }
  type["glossary_section"] = State.off;
  type["biblio_section"] = State.off;
  type["blurb_section"] = State.on;
  if (opt_action_bool["backmatter"] && opt_action_bool["section_blurb"]) {
    indent=[
      "hang_position" : 0,
      "base_position" : 0,
    ];
    bullet = false;
    type["para"] = State.on;
    line_occur["para"] = State.off;
    an_object_key="blurb_nugget";
    if (matchFirst(line, rgx.heading_blurb)) {
      comp_obj_heading_                       = comp_obj_heading_.init;
      comp_obj_heading_.use                   = "backmatter";
      comp_obj_heading_.is_of                 = "para";
      comp_obj_heading_.is_a                  = "heading";
      comp_obj_heading_.text                  = "Blurb";
      comp_obj_heading_.ocn                   = 0;
      comp_obj_heading_.obj_cite_number       = "";
      comp_obj_heading_.marked_up_level       = "B";
      comp_obj_heading_.heading_lev_markup    = 1;
      comp_obj_heading_.heading_lev_collapsed = 1;
      comp_obj_heading_.parent_ocn            = 1;
      comp_obj_heading_.parent_lev_markup     = 0;
      the_blurb_section                       ~= comp_obj_heading_;
      comp_obj_heading_                       = comp_obj_heading_.init;
      comp_obj_heading_.use                   = "backmatter";
      comp_obj_heading_.is_of                 = "para";
      comp_obj_heading_.is_a                  = "heading";
      comp_obj_heading_.text                  = "Blurb";
      comp_obj_heading_.ocn                   = 0;
      comp_obj_heading_.obj_cite_number       = "";
      comp_obj_heading_.segment_anchor_tag    = "blurb";
      comp_obj_heading_.marked_up_level       = "1";
      comp_obj_heading_.heading_lev_markup    = 4;
      comp_obj_heading_.heading_lev_collapsed = 2;
      comp_obj_heading_.parent_ocn            = 1;
      comp_obj_heading_.parent_lev_markup     = 0;
      comp_obj_heading_.anchor_tags           = ["blurb"];
      the_blurb_section                       ~= comp_obj_heading_;
    } else if ((matchFirst(line, rgx.heading))
    && (opt_action_bool["backmatter"] && opt_action_bool["section_blurb"])) {
      comp_obj_heading_                       = comp_obj_heading_.init;
      comp_obj_heading_.use                   = "backmatter";
      comp_obj_heading_.is_of                 = "para";
      comp_obj_heading_.is_a                  = "heading";
      comp_obj_heading_.text                  = to!string(line);
      comp_obj_heading_.ocn                   = 0;
      comp_obj_heading_.obj_cite_number       = "";
      comp_obj_heading_.segment_anchor_tag    = "blurb";
      comp_obj_heading_.marked_up_level       = to!string(an_object["lev"]);
      comp_obj_heading_.heading_lev_markup    = to!int(an_object["lev_markup_number"]);    // make int, remove need to conv
      comp_obj_heading_.heading_lev_collapsed = to!int(an_object["lev_collapsed_number"]); // make int, remove need to conv
      comp_obj_heading_.parent_ocn            = 1;
      comp_obj_heading_.parent_lev_markup     = 0;
      the_blurb_section                   ~= comp_obj_heading_;
    } else {
      _para_match_(line, an_object, an_object_key, indent, bullet, type, line_occur);
      comp_obj_para                       = comp_obj_para.init;
      comp_obj_para.use                   = "backmatter";
      comp_obj_para.is_of                 = "para";
      comp_obj_para.is_a                  = "blurb";
      comp_obj_para.text                  = to!string(line).strip;
      comp_obj_para.ocn                   = 0;
      comp_obj_para.obj_cite_number       = "";
      comp_obj_para.indent_hang           = indent["hang_position"];
      comp_obj_para.indent_base           = indent["base_position"];
      comp_obj_para.bullet                = bullet;
      the_blurb_section                   ~= comp_obj_para;
    }
    type["ocn_status"] = TriState.off;
  }
  continue;
#+END_SRC

***** in blocks [+1]                                       :block:active:
****** within block: poem                                         :poem:

#+name: abs_in_loop_body_non_code_obj
#+BEGIN_SRC d
} else if (type["poem"] == TriState.on) {
  /+ within block object: poem +/
  _poem_block_(line, an_object, type, cntr, obj_cite_number_poem, dochead_make_aa);
  continue;
#+END_SRC

****** within block: group                                       :group:

#+name: abs_in_loop_body_non_code_obj
#+BEGIN_SRC d
/+ within block object: group +/
} else if (type["group"] == TriState.on) {
  /+ within block object: group +/
  _group_block_(line, an_object, type);
  continue;
#+END_SRC

****** within block: block                                       :block:

#+name: abs_in_loop_body_non_code_obj
#+BEGIN_SRC d
} else if (type["block"] == TriState.on) {
  /+ within block object: block +/
  _block_block_(line, an_object, type);
  continue;
#+END_SRC

****** within block: quote                                       :quote:

#+name: abs_in_loop_body_non_code_obj
#+BEGIN_SRC d
} else if (type["quote"] == TriState.on) {
  /+ within block object: quote +/
  _quote_block_(line, an_object, type);
  continue;
#+END_SRC

****** within block: table                                       :table:

#+name: abs_in_loop_body_non_code_obj
#+BEGIN_SRC d
} else if (type["table"] == TriState.on) {
  /+ within block object: table +/
  _table_block_(line, an_object, type);
  continue;
#+END_SRC

***** not identified as being within block group (could still be, or not) [+3]
****** assert

#+name: abs_in_loop_body_open_block_obj_assert
#+BEGIN_SRC d
assert(
  (type["blocks"] == TriState.off)
  || (type["blocks"] == TriState.closing),
  "block status: none or closed"
);
assertions_flag_types_block_status_none_or_closed(type);
#+END_SRC

****** block open

#+name: abs_in_loop_body_open_block_obj
#+BEGIN_SRC d
if (matchFirst(line, (rgx.block_poem_open))) {
  /+ poem to verse exceptions! +/
  object_reset(an_object);
  processing.remove("verse");
  obj_cite_number_poem["start"] = to!string(obj_cite_number);
}
_start_block_(line, type, obj_cite_number_poem);
continue;
#+END_SRC

****** line not empty [+2]
******* asserts                                                :assert:

#+name: abs_in_loop_body_not_block_obj
#+BEGIN_SRC d
assert(
  !line.empty,
  "line tested, line not empty surely"
);
assert(
  (type["blocks"] == TriState.off)
  || (type["blocks"] == TriState.closing),
  "code block status: none or closed"
);
if (type["blocks"] == TriState.closing) {
  // blocks closed, unless followed by book index
  debug(check) {                           // block
    writeln(__LINE__);
    writeln(line);
  }
  assert(
    matchFirst(line, rgx.book_index)
    || matchFirst(line, rgx.book_index_open)
    || type["book_index"] == State.on
  );
}
#+END_SRC

******* book index                                          :bookindex:

#+name: abs_in_loop_body_not_block_obj
#+BEGIN_SRC d
if ((matchFirst(line, rgx.book_index))
|| (matchFirst(line, rgx.book_index_open))
|| (type["book_index"] == State.on ))  {
  /+ book_index +/
  _book_index_(line, book_idx_tmp, an_object, type, opt_action_bool);
#+END_SRC

******* not book index [+1]

#+name: abs_in_loop_body_not_block_obj
#+BEGIN_SRC d
} else {
  /+ not book_index +/
#+END_SRC

******** matched: comment                              :comment:match:

#+name: abs_in_loop_body_not_block_obj
#+BEGIN_SRC d
  an_object_key="body_nugget";
  if (auto m = matchFirst(line, rgx.comment)) {
    /+ matched comment +/
    debug(comment) {
      writeln(line);
    }
    an_object[an_object_key] ~= line ~= "\n";
    comp_obj_comment                   = comp_obj_comment.init;
    comp_obj_comment.use               = "comment";
    comp_obj_comment.is_of             = "comment";
    comp_obj_comment.is_a              = "comment";
    comp_obj_comment.text              = an_object[an_object_key].strip;
    the_document_body_section          ~= comp_obj_comment;
    _common_reset_(line_occur, an_object, type);
    processing.remove("verse");
    ++cntr;
#+END_SRC

******** flag not set & line not exist: heading or para :heading:paragraph:

#+name: abs_in_loop_body_not_block_obj
#+BEGIN_SRC d
  } else if (((line_occur["para"] == State.off)
  && (line_occur["heading"] == State.off))
  && ((type["para"] == State.off)
  && (type["heading"] == State.off))) {
    /+ heading or para but neither flag nor line exists +/
    if ((dochead_make_aa["make"]["headings"].length > 2)
    && (type["make_headings"] == State.off)) {
      /+ heading found +/
      _heading_found_(line, dochead_make_aa["make"]["headings"], heading_match_str, heading_match_rgx, type);
    }
    if ((type["make_headings"] == State.on)
    && ((line_occur["para"] == State.off)
    && (line_occur["heading"] == State.off))
    && ((type["para"] == State.off)
    && (type["heading"] == State.off))) {
      /+ heading make set +/
      _heading_make_set_(line, line_occur, heading_match_rgx, type);
    }
    /+ TODO node info: all headings identified at this point,
       - extract node info here??
       - how long can it wait?
       - should be incorporated in composite objects
       - should happen before endnote links set (they need to be moved down?)
      // node_construct.node_emitter_heading segment anchor tag
    +/
    if (matchFirst(line, rgx.heading)) {
      /+ heading match +/
      _heading_matched_(line, line_occur, an_object, an_object_key, lv, collapsed_lev, type, dochead_meta_aa);
    } else if (line_occur["para"] == State.off) {
      /+ para match +/
      an_object_key="body_nugget";
      _para_match_(line, an_object, an_object_key, indent, bullet, type, line_occur);
    }
#+END_SRC

******** line exist: heading                                 :heading:

#+name: abs_in_loop_body_not_block_obj
#+BEGIN_SRC d
  } else if (line_occur["heading"] > State.off) {
    /+ heading +/
    debug(heading) {
      writeln(line);
    }
    an_object[an_object_key] ~= line ~= "\n";
    ++line_occur["heading"];
#+END_SRC

******** line exist: para                                       :para:

#+name: abs_in_loop_body_not_block_obj
#+BEGIN_SRC d
  } else if (line_occur["para"] > State.off) {
    /+ paragraph +/
    debug(para) {
      writeln(line);
    }
    an_object[an_object_key] ~= " " ~ line;
    ++line_occur["para"];
  }
}
#+END_SRC

****** line empty, with block flag

#+name: abs_in_loop_body_not_block_obj_line_empty_blocks_flags
#+BEGIN_SRC d
_block_flag_line_empty_(
  line,
  an_object,
  the_document_body_section,
  bookindex_unordered_hashes,
  obj_cite_number,
  comp_obj_heading,
  cntr,
  type,
  obj_cite_number_poem,
  dochead_make_aa
);
#+END_SRC

****** line empty [+1]
******* assert line empty                                      :assert:

#+name: abs_in_loop_body_not_block_obj_line_empty
#+BEGIN_SRC d
assert(
  line.empty,
  "line should be empty"
);
assert(
  (type["blocks"] == State.off),
  "code block status: none"
);
#+END_SRC

******* heading object                                 :heading:object:

#+name: abs_in_loop_body_not_block_obj_line_empty
#+BEGIN_SRC d
if ((type["heading"] == State.on)
&& (line_occur["heading"] > State.off)) {
  /+ heading object (current line empty) +/
  obj_cite_number = (to!int(an_object["lev_markup_number"]) == 0)
  ? (ocn_emit(3))
  : (obj_cite_number = ocn_emit(type["ocn_status"]));
  an_object["bookindex_nugget"] =
    ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
  bookindex_unordered_hashes =
    bkidx_hash(an_object["bookindex_nugget"], obj_cite_number);
  an_object["is"] = "heading";
  an_object_key="body_nugget";
  auto substantive_object_and_anchor_tags_tuple =
    obj_im.obj_inline_markup_and_anchor_tags(an_object, an_object_key, dochead_make_aa);
  an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
  anchor_tags = substantive_object_and_anchor_tags_tuple[1];
  if (to!int(an_object["lev_markup_number"]) == 4) {
    segment_anchor_tag_that_object_belongs_to = anchor_tags[0];
    segment_anchor_tag_that_object_belongs_to_uri = anchor_tags[0] ~ ".fnSuffix";
    anchor_tag_ = anchor_tags[0];
  } else if (to!int(an_object["lev_markup_number"]) > 4) {
    segment_anchor_tag_that_object_belongs_to = anchor_tag_;
    segment_anchor_tag_that_object_belongs_to_uri = anchor_tag_ ~ ".fnSuffix#" ~ to!string(obj_cite_number);
  } else if (to!int(an_object["lev_markup_number"]) < 4) {
    segment_anchor_tag_that_object_belongs_to = "";
    segment_anchor_tag_that_object_belongs_to_uri = "";
  }
  /+ (incrementally build toc) table of contents here! +/
  _anchor_tag=to!string(obj_cite_number);
  the_table_of_contents_section = obj_im.table_of_contents_gather_headings(
    an_object,
    dochead_make_aa,
    segment_anchor_tag_that_object_belongs_to,
    _anchor_tag,
    lev4_subtoc,
    the_table_of_contents_section,
  );
  if (an_object["lev_markup_number"] == "4") {
    html_segnames ~= segment_anchor_tag_that_object_belongs_to;
    html_segnames_ptr = html_segnames_ptr_cntr;
    html_segnames_ptr_cntr++;
  }
  auto comp_obj_heading =
    node_construct.node_emitter_heading(
      an_object["substantive"],                     // string
      an_object["lev"],                             // string
      an_object["lev_markup_number"],               // string
      an_object["lev_collapsed_number"],            // string
      segment_anchor_tag_that_object_belongs_to,    // string
      obj_cite_number,                              // int
      cntr,                                         // int
      heading_ptr,                                  // int
      lv_ancestors,                                 // string[]
      an_object["is"],                              // string
      html_segnames_ptr,                            // int
    );
  ++heading_ptr;
  debug(segments) {
    writeln(an_object["lev_markup_number"]);
    writeln(segment_anchor_tag_that_object_belongs_to);
  }
  the_document_body_section ~= comp_obj_heading;
  // track previous heading and make assertions
  debug(objectrelated1) { // check
    writeln(line);
  }
  _common_reset_(line_occur, an_object, type);
  an_object.remove("lev");
  an_object.remove("lev_markup_number");
  processing.remove("verse");
  ++cntr;
#+END_SRC

******* paragraph object                             :paragraph:object:

#+name: abs_in_loop_body_not_block_obj_line_empty
#+BEGIN_SRC d
} else if ((type["para"] == State.on) && (line_occur["para"] > State.off)) {
  /+ paragraph object (current line empty) +/
  obj_cite_number = ocn_emit(type["ocn_status"]);
  an_object["bookindex_nugget"] =
    ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
  bookindex_unordered_hashes =
    bkidx_hash(an_object["bookindex_nugget"], obj_cite_number);
  an_object["is"] = "para";
  auto comp_obj_heading =
    node_construct.node_location_emitter(
      content_non_header,
      segment_anchor_tag_that_object_belongs_to,
      obj_cite_number,
      cntr,
      heading_ptr-1,
      an_object["is"],
    );
  auto substantive_object_and_anchor_tags_tuple =
    obj_im.obj_inline_markup_and_anchor_tags(an_object, an_object_key, dochead_make_aa);
  an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
  anchor_tags = substantive_object_and_anchor_tags_tuple[1];
  comp_obj_para                       = comp_obj_para.init;
  comp_obj_para.use                   = "body";
  comp_obj_para.is_of                 = "para";
  comp_obj_para.is_a                  = "para";
  comp_obj_para.text                  = to!string(an_object["substantive"]).strip;
  comp_obj_para.ocn                   = obj_cite_number;
  comp_obj_para.obj_cite_number       = (obj_cite_number==0) ? "" : to!string(obj_cite_number);
  comp_obj_para.indent_hang           = indent["hang_position"];
  comp_obj_para.indent_base           = indent["base_position"];
  comp_obj_para.bullet                = bullet;
  comp_obj_para.anchor_tags           = anchor_tags;
  the_document_body_section           ~= comp_obj_para;
  _common_reset_(line_occur, an_object, type);
  indent=[
    "hang_position" : 0,
    "base_position" : 0,
  ];
  bullet = false;
  processing.remove("verse");
  ++cntr;
} else {
  assert(
    line == null,
    "line variable should be empty, should not occur"
  );
  // check what happens when paragraph separated by 2 newlines
}
#+END_SRC

*** regular text objects                                   :text:paragraph:

#+name: abs_in_loop_body_01
#+BEGIN_SRC d
/+ unless (the_document_body_section.length == 0) ? +/
if (the_document_body_section.length > 0) {
  if (((the_document_body_section[$-1].is_a == "para")
  || (the_document_body_section[$-1].is_a == "heading")
  || (the_document_body_section[$-1].is_a == "group"))
  && (the_document_body_section.length > previous_length)
) {
    if ((the_document_body_section[$-1].is_a == "heading")
    && (the_document_body_section[$-1].heading_lev_markup < 5)) {
      type["biblio_section"] = State.off;
      type["glossary_section"] = State.off;
      type["blurb_section"] = State.off;
    }
    previous_length = to!int(the_document_body_section.length);
    if (match(
      the_document_body_section[$-1].text,
      rgx.inline_notes_delimiter_al_regular_number_note
    )) {
      previous_count=to!int(the_document_body_section.length -1);
      note_section.gather_notes_for_endnote_section(
        the_document_body_section,
        segment_anchor_tag_that_object_belongs_to,
        to!int(the_document_body_section.length-1),
      );
    }
  }
}
#+END_SRC

** 3. _post main-loop processing_                                       :post:
/+
  Backmatter:
  - endnotes
  - glossary
  - bibliography / references
  - book index
  - blurb
+/

*** tie up preparation of document sections
**** endnotes section (scroll & seg)                            :endnotes:

#+name: abs_post
#+BEGIN_SRC d
auto en_tuple =
  note_section.endnote_objects(obj_cite_number, opt_action_bool);
static assert(!isTypeTuple!(en_tuple));
auto the_endnotes_section = en_tuple[0];
obj_cite_number = en_tuple[1];
debug(endnotes) {
  writefln(
    "%s %s",
    __LINE__,
    the_endnotes_section.length
  );
  foreach (o; the_endnotes_section) {
    writeln(o);
  }
}
#+END_SRC

**** no glossary section?                                       :glossary:

#+name: abs_post
#+BEGIN_SRC d
if (an_object["glossary_nugget"].length == 0) {
  comp_obj_heading_                       = comp_obj_heading_.init;
  comp_obj_heading_.use                   = "empty";
  comp_obj_heading_.is_of                 = "para";
  comp_obj_heading_.is_a                  = "heading";
  comp_obj_heading_.text                  = "(skip) there is no Glossary section";
  comp_obj_heading_.ocn                   = 0;
  comp_obj_heading_.obj_cite_number       = "";
  comp_obj_heading_.marked_up_level       = "B";
  comp_obj_heading_.heading_lev_markup    = 1;
  comp_obj_heading_.heading_lev_collapsed = 1;
  comp_obj_heading_.parent_ocn            = 1;
  comp_obj_heading_.parent_lev_markup     = 0;
  the_glossary_section                    ~= comp_obj_heading_;
} else {
  writeln("gloss");
}
debug(glossary) {
  foreach (gloss; the_glossary_section) {
    writeln(gloss.text);
  }
}
#+END_SRC

**** bibliography section (objects)                         :bibliography:

#+name: abs_post
#+BEGIN_SRC d
auto biblio_unsorted_incomplete = biblio_arr_json.dup;
auto biblio = Bibliography();
auto biblio_ordered =
  biblio._bibliography_(biblio_unsorted_incomplete, bib_arr_json);
#+END_SRC

#+name: abs_post
#+BEGIN_SRC d
if (biblio_ordered.length > 0) {
  comp_obj_heading_                       = comp_obj_heading_.init;
  comp_obj_heading_.use                   = "backmatter";
  comp_obj_heading_.is_of                 = "para";
  comp_obj_heading_.is_a                  = "heading";
  comp_obj_heading_.text                  = "Bibliography";
  comp_obj_heading_.ocn                   = 0;
  comp_obj_heading_.obj_cite_number       = "";
  comp_obj_heading_.marked_up_level       = "B";
  comp_obj_heading_.heading_lev_markup    = 1;
  comp_obj_heading_.heading_lev_collapsed = 1;
  comp_obj_heading_.parent_ocn            = 1;
  comp_obj_heading_.parent_lev_markup     = 0;
  the_bibliography_section                ~= comp_obj_heading_;
  // ---
  comp_obj_heading_                       = comp_obj_heading_.init;
  comp_obj_heading_.use                   = "backmatter";
  comp_obj_heading_.is_of                 = "para";
  comp_obj_heading_.is_a                  = "heading";
  comp_obj_heading_.text                  = "Bibliography";
  comp_obj_heading_.ocn                   = 0;
  comp_obj_heading_.obj_cite_number       = "";
  comp_obj_heading_.segment_anchor_tag    = "bibliography";
  comp_obj_heading_.marked_up_level       = "1";
  comp_obj_heading_.heading_lev_markup    = 4;
  comp_obj_heading_.heading_lev_collapsed = 2;
  comp_obj_heading_.parent_ocn            = 1;
  comp_obj_heading_.parent_lev_markup     = 0;
  comp_obj_heading_.anchor_tags           = ["bibliography"];
  the_bibliography_section                ~= comp_obj_heading_;
} else {
  comp_obj_heading_                       = comp_obj_heading_.init;
  comp_obj_heading_.use                   = "empty";
  comp_obj_heading_.is_of                 = "para";
  comp_obj_heading_.is_a                  = "heading";
  comp_obj_heading_.text                  = "(skip) there is no Bibliography";
  comp_obj_heading_.ocn                   = 0;
  comp_obj_heading_.obj_cite_number       = "";
  comp_obj_heading_.marked_up_level       = "B";
  comp_obj_heading_.heading_lev_markup    = 1;
  comp_obj_heading_.heading_lev_collapsed = 1;
  comp_obj_heading_.parent_ocn            = 1;
  comp_obj_heading_.parent_lev_markup     = 0;
  the_bibliography_section                ~= comp_obj_heading_;
}
#+END_SRC

***** format biblio string

#+name: abs_post
#+BEGIN_SRC d
string out_;
foreach (entry; biblio_ordered) {
  out_ = format(
    "%s \"%s\"%s%s%s%s%s%s%s%s%s.",
    ((entry["author"].str.empty) ? entry["editor"].str : entry["author"].str),
    entry["fulltitle"].str,
    ((entry["journal"].str.empty) ? "" : ", /{" ~ entry["journal"].str ~ "}/"),
    ((entry["volume"].str.empty) ? "" : ", " ~ entry["volume"].str),
    ((entry["in"].str.empty) ? "" : ", " ~ entry["in"].str),
    ((!(entry["author"].str.empty) && (!(entry["editor"].str.empty))) ? entry["editor"].str : ""),
    ", " ~ entry["year"].str,
    ((entry["pages"].str.empty) ? "" : ", " ~ entry["pages"].str),
    ((entry["publisher"].str.empty) ? "" : ", " ~ entry["publisher"].str),
    ((entry["place"].str.empty) ? "" : ", " ~ entry["place"].str),
    ((entry["url"].str.empty) ? "" : ", [" ~ entry["url"].str ~ "]"),
  );
  comp_obj_para                       = comp_obj_para.init;
  comp_obj_para.use                   = "backmatter";
  comp_obj_para.is_of                 = "para";
  comp_obj_para.is_a                  = "bibliography";
  comp_obj_para.text                  = to!string(out_).strip;
  comp_obj_para.ocn                   = 0;
  comp_obj_para.obj_cite_number       = "";
  comp_obj_para.indent_hang           = 0;
  comp_obj_para.indent_base           = 1;
  comp_obj_para.bullet                = bullet;
  comp_obj_para.anchor_tags           = anchor_tags;
  the_bibliography_section            ~= comp_obj_para;
}
#+END_SRC

#+name: abs_post
#+BEGIN_SRC d
debug(bibliosection) {
  foreach (o; the_bibliography_section) {
    writeln(o.text);
  }
}
#+END_SRC

***** bibliography components

auto biblio_entry_tags_jsonstr =  `{
  "is"                   : "",
  "sortby_deemed_author_year_title"  : "",
  "deemed_author"                    : "",
  "author_raw"                       : "",
  "author"                           : "",
  "author_arr"                       : [ "" ],
  "editor_raw"                       : "",
  "editor"                           : "",
  "editor_arr"                       : [ "" ],
  "title"                            : "",
  "subtitle"                         : "",
  "fulltitle"                        : "",
  "language"                         : "",
  "trans"                            : "",
  "src"                              : "",
  "journal"                          : "",
  "in"                               : "",
  "volume"                           : "",
  "edition"                          : "",
  "year"                             : "",
  "place"                            : "",
  "publisher"                        : "",
  "url"                              : "",
  "pages"                            : "",
  "note"                             : "",
  "short_name"                       : "",
  "id"                               : ""
}`; // is: book, article, magazine, newspaper, blog, other

**** bookindex section (scroll & seg)                         :book:index:

#+name: abs_post
#+BEGIN_SRC d
auto bi = BookIndexReportSection();
auto bi_tuple =
  bi.bookindex_build_abstraction_section(
    bookindex_unordered_hashes,
    obj_cite_number,
    segment_anchor_tag_that_object_belongs_to,
    opt_action_bool,
  );
static assert(!isTypeTuple!(bi_tuple));
auto the_bookindex_section = bi_tuple[0];
obj_cite_number = bi_tuple[1];
debug(bookindex) {
  foreach (bi_entry; the_bookindex_section["seg"]) {
    writeln(bi_entry);
  }
}
#+END_SRC

**** no blurb section?                                             :blurb:

#+name: abs_post
#+BEGIN_SRC d
if (an_object["blurb_nugget"].length == 0) {
  comp_obj_heading_                       = comp_obj_heading_.init;
  comp_obj_heading_.use                   = "empty";
  comp_obj_heading_.is_of                 = "para";
  comp_obj_heading_.is_a                  = "heading";
  comp_obj_heading_.text                  = "(skip) there is no Blurb section";
  comp_obj_heading_.ocn                   = 0;
  comp_obj_para.obj_cite_number           = "";
  comp_obj_heading_.segment_anchor_tag    = "";
  comp_obj_heading_.marked_up_level       = "B";
  comp_obj_heading_.heading_lev_markup    = 1;
  comp_obj_heading_.heading_lev_collapsed = 1;
  comp_obj_heading_.parent_ocn            = 1;
  comp_obj_heading_.parent_lev_markup     = 0;
  the_blurb_section                       ~= comp_obj_heading_;
}
debug(blurb) {
  foreach (blurb; the_blurb_section) {
    writeln(blurb.text);
  }
}
#+END_SRC

**** toc backmatter, table of contents backmatter (scroll & seg) :contents:

#+name: abs_post
#+BEGIN_SRC d
indent=[
  "hang_position" : 1,
  "base_position" : 1,
];
comp_obj_toc                       = comp_obj_toc.init;
comp_obj_toc.use                   = "frontmatter";
comp_obj_toc.is_of                 = "para";
comp_obj_toc.is_a                  = "toc";
comp_obj_toc.ocn                   = 0;
comp_obj_toc.obj_cite_number       = "";
comp_obj_toc.indent_hang           = indent["hang_position"];
comp_obj_toc.indent_base           = indent["base_position"];
comp_obj_toc.bullet                = false;
if (the_endnotes_section.length > 1) {
  toc_txt_ = format(
    "{ %s }%s../%s.fnSuffix",
    "Endnotes",
    mkup.mark_internal_site_lnk,
    "endnotes",               // segment_anchor_tag_that_object_belongs_to
  );
  toc_txt_= munge.url_links(toc_txt_);
  comp_obj_toc.text                       = to!string(toc_txt_).strip;
  the_table_of_contents_section["seg"]    ~= comp_obj_toc;
}
if (the_glossary_section.length > 1) {
  toc_txt_ = format(
    "{ %s }%s../%s.fnSuffixs",
    "Glossary",
    mkup.mark_internal_site_lnk,
    "glossary",               // segment_anchor_tag_that_object_belongs_to
  );
  toc_txt_= munge.url_links(toc_txt_);
  comp_obj_toc.text                       = to!string(toc_txt_).strip;
  the_table_of_contents_section["seg"]    ~= comp_obj_toc;
  toc_txt_ = format(
    "{ %s }#%s",
    "Glossary",
    "glossary",               // _anchor_tag
  );
  toc_txt_= munge.url_links(toc_txt_);
  comp_obj_toc.text                       = to!string(toc_txt_).strip;
  the_table_of_contents_section["scroll"] ~= comp_obj_toc;
}
if (the_bibliography_section.length > 1){
  toc_txt_ = format(
    "{ %s }%s../%s.fnSuffix",
    "Bibliography",
    mkup.mark_internal_site_lnk,
    "bibliography",           // segment_anchor_tag_that_object_belongs_to
  );
  toc_txt_= munge.url_links(toc_txt_);
  comp_obj_toc.text                       = to!string(toc_txt_).strip;
  the_table_of_contents_section["seg"]    ~= comp_obj_toc;

  toc_txt_ = format(
    "{ %s }#%s",
    "Bibliography",
    "bibliography",           // _anchor_tag
  );
  toc_txt_= munge.url_links(toc_txt_);
  comp_obj_toc.text                       = to!string(toc_txt_).strip;
  the_table_of_contents_section["scroll"] ~= comp_obj_toc;
}
if (the_bookindex_section["seg"].length > 1) {
  toc_txt_ = format(
    "{ %s }%s../%s.fnSuffix",
    "Book Index",
    mkup.mark_internal_site_lnk,
    "bookindex",              // segment_anchor_tag_that_object_belongs_to
  );
  toc_txt_= munge.url_links(toc_txt_);
  comp_obj_toc.text                       = to!string(toc_txt_).strip;
  the_table_of_contents_section["seg"]    ~= comp_obj_toc;
}
if (the_bookindex_section["scroll"].length > 1) {
  toc_txt_ = format(
    "{ %s }#%s",
    "Book Index",
    "bookindex",              // _anchor_tag
  );
  toc_txt_= munge.url_links(toc_txt_);
  comp_obj_toc.text                       = to!string(toc_txt_).strip;
  the_table_of_contents_section["scroll"] ~= comp_obj_toc;
}
if (the_blurb_section.length > 1) {
  toc_txt_ = format(
    "{ %s }%s../%s.fnSuffix",
    "Blurb",
    mkup.mark_internal_site_lnk,
    "blurb",                  // segment_anchor_tag_that_object_belongs_to
  );
  toc_txt_= munge.url_links(toc_txt_);
  comp_obj_toc.text                       = to!string(toc_txt_).strip;
  the_table_of_contents_section["seg"]    ~= comp_obj_toc;
  toc_txt_ = format(
    "{ %s }#%s",
    "Blurb",
    "blurb",                  // _anchor_tag
  );
  toc_txt_= munge.url_links(toc_txt_);
  comp_obj_toc.text                       = to!string(toc_txt_).strip;
  the_table_of_contents_section["scroll"] ~= comp_obj_toc;
}
debug(toc) {
  writefln(
    "%s %s",
    __LINE__,
    the_table_of_contents_section["seg"].length
  );
  foreach (toc_linked_heading; the_table_of_contents_section["seg"]) {
    writeln(mkup.indent_by_spaces_provided(toc_linked_heading.indent_hang), toc_linked_heading.text);
  }
}
debug(tocscroll) {
  writefln(
    "%s %s",
    __LINE__,
    the_table_of_contents_section["seg"].length
  );
  foreach (toc_linked_heading; the_table_of_contents_section["scroll"]) {
    writeln(mkup.indent_by_spaces_provided(toc_linked_heading.indent_hang), toc_linked_heading.text);
  }
}
#+END_SRC

**** doc head (separate document head from body, make space for toc)

#+name: abs_post
#+BEGIN_SRC d
the_document_head_section ~= the_document_body_section[0];
the_document_body_section=the_document_body_section[1..$];
#+END_SRC

*** TODO minor loops                                                 :post:
**** 1. loop: backmatter loop up to lev4: html_segnames, set backmatter pointers

could optimise by
- skipping second and third pass unless the output html seg or epub is being made!
NOTE there are issues attempting to do this on first pass
- as
  - backmatter is created out of sequence and
  - it is not certain which are present
it is quite neat to have all in one place as we have here:

#+name: abs_post
#+BEGIN_SRC d
if (the_endnotes_section.length > 1) {
  html_segnames ~= "endnotes";
  html_segnames_ptr = html_segnames_ptr_cntr;
  foreach (ref section; the_endnotes_section) {
    if (section.heading_lev_markup == 4) {
      section.ptr_html_segnames = html_segnames_ptr;
      break;
    }
  }
  html_segnames_ptr_cntr++;
}
if (the_glossary_section.length > 1) {
  html_segnames ~= "glossary";
  html_segnames_ptr = html_segnames_ptr_cntr;
  foreach (ref section; the_glossary_section) {
    if (section.heading_lev_markup == 4) {
      section.ptr_html_segnames = html_segnames_ptr;
      break;
    }
  }
  html_segnames_ptr_cntr++;
}
if (the_bibliography_section.length > 1) {
  html_segnames ~= "bibliography";
  html_segnames_ptr = html_segnames_ptr_cntr;
  foreach (ref section; the_bibliography_section) {
    if (section.heading_lev_markup == 4) {
      section.ptr_html_segnames = html_segnames_ptr;
      break;
    }
  }
  html_segnames_ptr_cntr++;
}
if (the_bookindex_section["scroll"].length > 1) {
  html_segnames ~= "bookindex";
  html_segnames_ptr = html_segnames_ptr_cntr;
  foreach (ref section; the_bookindex_section["scroll"]) {
    if (section.heading_lev_markup == 4) {
      section.ptr_html_segnames = html_segnames_ptr;
      break;
    }
  }
  foreach (ref section; the_bookindex_section["seg"]) {
    if (section.heading_lev_markup == 4) {
      section.ptr_html_segnames = html_segnames_ptr;
      break;
    }
  }
  html_segnames_ptr_cntr++;
}
if (the_blurb_section.length > 1) {
  html_segnames ~= "blurb";
  html_segnames_ptr = html_segnames_ptr_cntr;
  foreach (ref section; the_blurb_section) {
    if (section.heading_lev_markup == 4) {
      section.ptr_html_segnames = html_segnames_ptr;
      break;
    }
  }
  html_segnames_ptr_cntr++;
}
#+END_SRC

**** 2. loop: all objects structural relationships (sections, segments, objects)

needed for DOM structure, segnames & subtoc, backmatter pointers & unique image
list

if used minimally only for DOM structure, segnames, subtoc, could optimise by
- skipping second and third pass unless the output html seg or epub is being
  made!

or could conveniently be used more extensively for ancestors as well (though
this can be extracted earlier)

Build here:
- DOM structure
- ancestors and decendants

(as needed) up to document heading 1~, lev4 html:

during the third pass all previous and next segment names are known
next are not yet known for backmatter during the second pass

#+name: abs_post
#+BEGIN_SRC d
string[] _images;
auto extract_images(S)(S content_block) {
  string[] images_;
  if (auto m = matchAll(content_block, rgx.image)) {
    images_ ~= m.captures[1];
  }
  return images_;
}
foreach (ref obj; the_document_head_section) {
  if (obj.is_a == "heading") {
    debug(dom) {
      writeln(obj.text);
    }
    if ((opt_action_bool["html"])
    || (opt_action_bool["html_scroll"])
    || (opt_action_bool["html_seg"])
    || (opt_action_bool["epub"])) {
      obj.dom_markedup =
        dom_set_markup_tags(dom_markedup, obj.heading_lev_markup).dup;
      obj.dom_collapsed =
        dom_set_collapsed_tags(dom_collapsed, obj.heading_lev_collapsed).dup;
    }
    heading_ancestors(obj, lv_ancestors);
  }
}
if (the_table_of_contents_section["scroll"].length > 1) {
  dom_markedup_buffer = dom_markedup.dup;
  dom_collapsed_buffer = dom_collapsed.dup;
  foreach (ref obj; the_table_of_contents_section["scroll"]) {
    if (obj.is_a == "heading") {
      if (obj.heading_lev_markup == 4) {
        obj.segname_next = html_segnames[obj.ptr_html_segnames + 1];
        assert(obj.segment_anchor_tag == html_segnames[obj.ptr_html_segnames]);
      }
      if ((opt_action_bool["html"])
      || (opt_action_bool["html_scroll"])
      || (opt_action_bool["html_seg"])
      || (opt_action_bool["epub"])) {
        obj.dom_markedup =
          dom_set_markup_tags(dom_markedup, obj.heading_lev_markup).dup;
        obj.dom_collapsed =
          dom_set_collapsed_tags(dom_collapsed, obj.heading_lev_collapsed).dup;
      }
      heading_ancestors(obj, lv_ancestors);
    }
  }
  dom_markedup = dom_markedup_buffer.dup;
  dom_collapsed = dom_collapsed_buffer.dup;
  foreach (ref obj; the_table_of_contents_section["seg"]) {
    if (obj.is_a == "heading") {
      debug(dom) {
        writeln(obj.text);
      }
      if (obj.heading_lev_markup == 4) {
        obj.segname_next = html_segnames[obj.ptr_html_segnames + 1];
        assert(obj.segment_anchor_tag == html_segnames[obj.ptr_html_segnames]);
      }
      if ((opt_action_bool["html"])
      || (opt_action_bool["html_scroll"])
      || (opt_action_bool["html_seg"])
      || (opt_action_bool["epub"])) {
        obj.dom_markedup =
          dom_set_markup_tags(dom_markedup, obj.heading_lev_markup).dup;
        obj.dom_collapsed =
          dom_set_collapsed_tags(dom_collapsed, obj.heading_lev_collapsed).dup;
      }
      heading_ancestors(obj, lv_ancestors);
    }
  }
}
/+ multiple 1~ levels, loop through document body +/
if (the_document_body_section.length > 1) {
  foreach (ref obj; the_document_body_section) {
    if (obj.is_a == "heading") {
      debug(dom) {
        writeln(obj.text);
      }
      if (obj.heading_lev_markup == 4) {
        obj.lev4_subtoc = lev4_subtoc[obj.segment_anchor_tag];
        obj.segname_prev = html_segnames[obj.ptr_html_segnames - 1];
        if (html_segnames.length > obj.ptr_html_segnames + 1) {
          obj.segname_next = html_segnames[obj.ptr_html_segnames + 1];
        }
        assert(obj.segment_anchor_tag == html_segnames[obj.ptr_html_segnames]);
      }
      if ((opt_action_bool["html"])
      || (opt_action_bool["html_scroll"])
      || (opt_action_bool["html_seg"])
      || (opt_action_bool["epub"])) {
        obj.dom_markedup =
          dom_set_markup_tags(dom_markedup, obj.heading_lev_markup).dup;
        obj.dom_collapsed =
          dom_set_collapsed_tags(dom_collapsed, obj.heading_lev_collapsed).dup;
      }
      heading_ancestors(obj, lv_ancestors);
    } else if (obj.is_a == "para") {
       _images ~= extract_images(obj.text);
    }
  }
}
auto images=uniq(_images.sort());
/+ optional only one 1~ level +/
if (the_endnotes_section.length > 1) {
  dom_markedup_buffer = dom_markedup.dup;
  dom_collapsed_buffer = dom_collapsed.dup;
  dom_markedup = dom_markedup_buffer.dup;
  dom_collapsed = dom_collapsed_buffer.dup;
  foreach (ref obj; the_endnotes_section) {
    if (obj.is_a == "heading") {
      debug(dom) {
        writeln(obj.text);
      }
      if (obj.heading_lev_markup == 4) {
        obj.segname_prev = html_segnames[obj.ptr_html_segnames - 1];
        if (html_segnames.length > obj.ptr_html_segnames + 1) {
          obj.segname_next = html_segnames[obj.ptr_html_segnames + 1];
        }
        assert(obj.segment_anchor_tag == html_segnames[obj.ptr_html_segnames]);
      }
      if ((opt_action_bool["html"])
      || (opt_action_bool["html_scroll"])
      || (opt_action_bool["html_seg"])
      || (opt_action_bool["epub"])) {
        obj.dom_markedup =
          dom_set_markup_tags(dom_markedup, obj.heading_lev_markup).dup;
        obj.dom_collapsed =
          dom_set_collapsed_tags(dom_collapsed, obj.heading_lev_collapsed).dup;
      }
      heading_ancestors(obj, lv_ancestors);
    }
  }
}
/+ optional only one 1~ level +/
if (the_glossary_section.length > 1) {
  foreach (ref obj; the_glossary_section) {
    if (obj.is_a == "heading") {
      debug(dom) {
        writeln(obj.text);
      }
      if (obj.heading_lev_markup == 4) {
        obj.segname_prev = html_segnames[obj.ptr_html_segnames - 1];
        if (html_segnames.length > obj.ptr_html_segnames + 1) {
          obj.segname_next = html_segnames[obj.ptr_html_segnames + 1];
        }
        assert(obj.segment_anchor_tag == html_segnames[obj.ptr_html_segnames]);
      }
      if ((opt_action_bool["html"])
      || (opt_action_bool["html_scroll"])
      || (opt_action_bool["html_seg"])
      || (opt_action_bool["epub"])) {
        obj.dom_markedup =
          dom_set_markup_tags(dom_markedup, obj.heading_lev_markup).dup;
        obj.dom_collapsed =
          dom_set_collapsed_tags(dom_collapsed, obj.heading_lev_collapsed).dup;
      }
      heading_ancestors(obj, lv_ancestors);
    }
  }
}
/+ optional only one 1~ level +/
if (the_bibliography_section.length > 1) {
  foreach (ref obj; the_bibliography_section) {
    if (obj.is_a == "heading") {
      debug(dom) {
        writeln(obj.text);
      }
      if (obj.heading_lev_markup == 4) {
        obj.segname_prev = html_segnames[obj.ptr_html_segnames - 1];
        if (html_segnames.length > obj.ptr_html_segnames + 1) {
          obj.segname_next = html_segnames[obj.ptr_html_segnames + 1];
        }
        assert(obj.segment_anchor_tag == html_segnames[obj.ptr_html_segnames]);
      }
      if ((opt_action_bool["html"])
      || (opt_action_bool["html_scroll"])
      || (opt_action_bool["html_seg"])
      || (opt_action_bool["epub"])) {
        obj.dom_markedup =
          dom_set_markup_tags(dom_markedup, obj.heading_lev_markup).dup;
        obj.dom_collapsed =
          dom_set_collapsed_tags(dom_collapsed, obj.heading_lev_collapsed).dup;
      }
      heading_ancestors(obj, lv_ancestors);
    }
  }
}
/+ optional only one 1~ level +/
if (the_bookindex_section["scroll"].length > 1) {
  dom_markedup_buffer = dom_markedup.dup;
  dom_collapsed_buffer = dom_collapsed.dup;
  foreach (ref obj; the_bookindex_section["scroll"]) {
    if (obj.is_a == "heading") {
      debug(dom) {
      // writeln(obj.text);
      }
      if (obj.heading_lev_markup == 4) {
        obj.segname_prev = html_segnames[obj.ptr_html_segnames - 1];
        if (html_segnames.length > obj.ptr_html_segnames + 1) {
          obj.segname_next = html_segnames[obj.ptr_html_segnames + 1];
        }
        assert(obj.segment_anchor_tag == html_segnames[obj.ptr_html_segnames]);
      }
      if ((opt_action_bool["html"])
      || (opt_action_bool["html_scroll"])
      || (opt_action_bool["html_seg"])
      || (opt_action_bool["epub"])) {
        obj.dom_markedup =
          dom_set_markup_tags(dom_markedup, obj.heading_lev_markup).dup;
        obj.dom_collapsed =
          dom_set_collapsed_tags(dom_collapsed, obj.heading_lev_collapsed).dup;
      }
      heading_ancestors(obj, lv_ancestors);
    }
  }
  dom_markedup = dom_markedup_buffer.dup;
  dom_collapsed = dom_collapsed_buffer.dup;
  foreach (ref obj; the_bookindex_section["seg"]) {
    if (obj.is_a == "heading") {
      debug(dom) {
        writeln(obj.text);
      }
      if (obj.heading_lev_markup == 4) {
        obj.segname_prev = html_segnames[obj.ptr_html_segnames - 1];
        if (html_segnames.length > obj.ptr_html_segnames + 1) {
          obj.segname_next = html_segnames[obj.ptr_html_segnames + 1];
        }
        assert(obj.segment_anchor_tag == html_segnames[obj.ptr_html_segnames]);
      }
      if ((opt_action_bool["html"])
      || (opt_action_bool["html_scroll"])
      || (opt_action_bool["html_seg"])
      || (opt_action_bool["epub"])) {
        obj.dom_markedup =
          dom_set_markup_tags(dom_markedup, obj.heading_lev_markup).dup;
        obj.dom_collapsed =
          dom_set_collapsed_tags(dom_collapsed, obj.heading_lev_collapsed).dup;
      }
      heading_ancestors(obj, lv_ancestors);
    }
  }
}
/+ optional only one 1~ level +/
if (the_blurb_section.length > 1) {
  foreach (ref obj; the_blurb_section) {
    if (obj.is_a == "heading") {
      debug(dom) {
        writeln(obj.text);
      }
      if (obj.heading_lev_markup == 4) {
        obj.segname_prev = html_segnames[obj.ptr_html_segnames - 1];
        if (html_segnames.length > obj.ptr_html_segnames + 1) {
          obj.segname_next = html_segnames[obj.ptr_html_segnames + 1];
        }
        assert(obj.segment_anchor_tag == html_segnames[obj.ptr_html_segnames]);
      }
      if ((opt_action_bool["html"])
      || (opt_action_bool["html_scroll"])
      || (opt_action_bool["html_seg"])
      || (opt_action_bool["epub"])) {
        obj.dom_markedup =
          dom_set_markup_tags(dom_markedup, obj.heading_lev_markup).dup;
        obj.dom_collapsed =
          dom_set_collapsed_tags(dom_collapsed, obj.heading_lev_collapsed).dup;
      }
      heading_ancestors(obj, lv_ancestors);
    }
  }
  /+ TODO
    - note create/insert heading object sole purpose eof close all open tags
      sort out:
      - obj.dom_markedup = dom_markedup;
      - obj.dom_collapsed = dom_collapsed;
  +/
  dom_markedup = dom_set_markup_tags(dom_markedup, 0);
  dom_collapsed = dom_set_collapsed_tags(dom_collapsed, 0);
  comp_obj_heading_                       = comp_obj_heading_.init;
  comp_obj_heading_.use                   = "empty";
  comp_obj_heading_.is_of                 = "para";
  comp_obj_heading_.is_a                  = "heading";
  // comp_obj_heading_.text                  = "(skip) this is the DOM tail";
  comp_obj_heading_.ocn                   = 0;
  comp_obj_para.obj_cite_number           = "";
  comp_obj_heading_.segment_anchor_tag    = "";
  comp_obj_heading_.marked_up_level       = "";
  comp_obj_heading_.heading_lev_markup    = 9;
  comp_obj_heading_.heading_lev_collapsed = 9;
  comp_obj_heading_.parent_ocn            = 0;
  comp_obj_heading_.parent_lev_markup     = 0;
  comp_obj_heading_.dom_markedup          = dom_markedup.dup;
  comp_obj_heading_.dom_collapsed         = dom_collapsed.dup;
  the_dom_tail_section                    ~= comp_obj_heading_;
}
#+END_SRC

** 4. _return document tuple_                                         :post:
*** _the document_                                               :document:

#+name: abs_post
#+BEGIN_SRC d
auto document_the = [
  "head":             the_document_head_section,
  "toc_seg":          the_table_of_contents_section["seg"],
  "toc_scroll":       the_table_of_contents_section["scroll"],
  /+ substantive/body: +/
  "body":             the_document_body_section,
  /+ backmatter: +/
  "endnotes":         the_endnotes_section,
  "glossary":         the_glossary_section,
  "bibliography":     the_bibliography_section,
  "bookindex_scroll": the_bookindex_section["scroll"],
  "bookindex_seg":    the_bookindex_section["seg"],
  "blurb":            the_blurb_section,
  /+ dom tail only +/
  "tail":             the_dom_tail_section,
];
#+END_SRC

*** document _section keys_ sequence

#+name: abs_post
#+BEGIN_SRC d
string[][string] document_section_keys_sequenced = [
  "seg":    ["head", "toc_seg", "body",],
  "scroll": ["head", "toc_scroll", "body",]
];
if (document_the["endnotes"].length > 1) {
  document_section_keys_sequenced["seg"]    ~= "endnotes";
  document_section_keys_sequenced["scroll"] ~= "endnotes";
}
if (document_the["glossary"].length > 1) {
  document_section_keys_sequenced["seg"]    ~= "glossary";
  document_section_keys_sequenced["scroll"] ~= "glossary";
}
if (document_the["bibliography"].length > 1) {
  document_section_keys_sequenced["seg"]    ~= "bibliography";
  document_section_keys_sequenced["scroll"] ~= "bibliography";
}
if (document_the["bookindex_seg"].length > 1) {
  document_section_keys_sequenced["seg"]    ~= "bookindex_seg";
}
if (document_the["bookindex_scroll"].length > 1) {
  document_section_keys_sequenced["scroll"] ~= "bookindex_scroll";
}
if (document_the["blurb"].length > 1) {
  document_section_keys_sequenced["seg"]    ~= "blurb";
  document_section_keys_sequenced["scroll"] ~= "blurb";
}
if ((opt_action_bool["html"])
|| (opt_action_bool["html_scroll"])
|| (opt_action_bool["html_seg"])
|| (opt_action_bool["epub"])) {
  document_section_keys_sequenced["seg"]    ~= "tail";
  document_section_keys_sequenced["scroll"] ~= "tail";
}
#+END_SRC

*** clean out structure

#+name: abs_post
#+BEGIN_SRC d
destroy(the_document_head_section);
destroy(the_table_of_contents_section);
destroy(the_document_body_section);
destroy(the_endnotes_section);
destroy(the_glossary_section);
destroy(the_bibliography_section);
destroy(the_bookindex_section);
destroy(the_blurb_section);
#+END_SRC

*** [#A] _return document tuple_                               :return:tuple:

#+name: abs_post
#+BEGIN_SRC d
auto t = tuple(
  document_the,
  document_section_keys_sequenced,
  html_segnames,
  images,
);
return t;
#+END_SRC

** 5. Functions                                          :abstract:function:

functions used in document abstraction

*** set & resets                                                    :reset:
**** object reset: remove (clean)                          :object:remove:

#+name: abs_functions_object_reset
#+BEGIN_SRC d
auto object_reset(O)(ref O an_object) {
  debug(asserts){
    static assert(is(typeof(an_object) == string[string]));
  }
  an_object.remove("body_nugget");
  an_object.remove("substantive");
  an_object.remove("is");
  an_object.remove("attrib");
  an_object.remove("bookindex_nugget");
}
#+END_SRC

**** set, initialize or re-initialize                                :set:

#+name: abs_functions_header_set_common
#+BEGIN_SRC d
auto _common_reset_(L,O,T)(
  ref L line_occur,
  ref O an_object,
  ref T type
) {
  debug(asserts){
    static assert(is(typeof(line_occur) == int[string]));
    static assert(is(typeof(an_object)  == string[string]));
    static assert(is(typeof(type)       == int[string]));
  }
  line_occur["heading"] = State.off;
  line_occur["para"]    = State.off;
  type["heading"]       = State.off;
  type["para"]          = State.off;
  object_reset(an_object);
}
#+END_SRC

*** check obj_cite_number status in document                          :ocn:

#+name: abs_functions_ocn_status
#+BEGIN_SRC d
void _check_ocn_status_(L,T)(
  L     line,
  ref T type
) {
  debug(asserts){
    static assert(is(typeof(line) == char[]));
    static assert(is(typeof(type) == int[string]));
  }
  auto rgx = Rgx();
  if ((!line.empty) && (type["ocn_status_multi_obj"] == TriState.off)) {
    /+ not multi-line object, check whether obj_cite_number is on or turned off +/
    if (matchFirst(line, rgx.obj_cite_number_block_marks)) {
      /+ switch off obj_cite_number +/
      if (matchFirst(line, rgx.obj_cite_number_off_block)) {
        type["ocn_status_multi_obj"] = TriState.on;
        debug(ocnoff) {
          writeln(line);
        }
      }
      if (matchFirst(line, rgx.obj_cite_number_off_block_dh)) {
        type["ocn_status_multi_obj"] = TriState.closing;
        debug(ocnoff) {
          writeln(line);
        }
      }
    } else {
      if (type["ocn_status_multi_obj"] == TriState.off) {
        if (matchFirst(line, rgx.obj_cite_number_off)) {
          type["ocn_status"] = TriState.on;
        } else if (matchFirst(line, rgx.obj_cite_number_off_dh)) {
          type["ocn_status"] = TriState.closing;
        } else {
          type["ocn_status"] = TriState.off;
        }
      } else {
        type["ocn_status"] =
          type["ocn_status_multi_obj"];
      }
    }
  } else if ((!line.empty) && (type["ocn_status_multi_obj"] > TriState.off)) {
    if (matchFirst(line, rgx.obj_cite_number_off_block_close)) {
      type["ocn_status_multi_obj"] = TriState.off;
      type["ocn_status"] = TriState.off;
      debug(ocnoff) {
        writeln(line);
      }
    }
  }
}
#+END_SRC

*** block                                                           :block:
**** block start (open) block                                      :start:
***** function open for block starts

#+name: abs_functions_block
#+BEGIN_SRC d
void _start_block_(L,T,N)(
  L     line,
  ref T type,
  N     obj_cite_number_poem
) {
  debug(asserts){
    static assert(is(typeof(line)                 == char[]));
    static assert(is(typeof(type)                 == int[string]));
    static assert(is(typeof(obj_cite_number_poem) == string[string]));
  }
#+END_SRC

***** block (various) curly open                                  :curly:

#+name: abs_functions_block
#+BEGIN_SRC d
  auto rgx = Rgx();
  if (matchFirst(line, rgx.block_curly_code_open)) {
    /+ curly code open +/
    debug(code) {                              // code (curly) open
      writefln(
        "* [code curly] %s",
        line
      );
    }
    type["blocks"] = TriState.on;
    type["code"] = TriState.on;
    type["curly_code"] = TriState.on;
  } else if (matchFirst(line, rgx.block_curly_poem_open)) {
    /+ curly poem open +/
    debug(poem) {                              // poem (curly) open
      writefln(
        "* [poem curly] %s",
        line
      );
    }
    obj_cite_number_poem["start"] =
      to!string(obj_cite_number);
    type["blocks"] = TriState.on;
    type["verse_new"] = State.on;
    type["poem"] = TriState.on;
    type["curly_poem"] = TriState.on;
  } else if (matchFirst(line, rgx.block_curly_group_open)) {
    /+ curly group open +/
    debug(group) {                             // group (curly) open
      writefln(
        "* [group curly] %s",
        line
      );
    }
    type["blocks"] = TriState.on;
    type["group"] = TriState.on;
    type["curly_group"] = TriState.on;
  } else if (matchFirst(line, rgx.block_curly_block_open)) {
    /+ curly block open +/
    debug(block) {                             // block (curly) open
      writefln(
        "* [block curly] %s",
        line
      );
    }
    type["blocks"] = TriState.on;
    type["block"] = TriState.on;
    type["curly_block"] = TriState.on;
  } else if (matchFirst(line, rgx.block_curly_quote_open)) {
    /+ curly quote open +/
    debug(quote) {                             // quote (curly) open
      writefln(
        "* [quote curly] %s",
        line
      );
    }
    type["blocks"] = TriState.on;
    type["quote"] = TriState.on;
    type["curly_quote"] = TriState.on;
  } else if (matchFirst(line, rgx.block_curly_table_open)) {
    /+ curly table open +/
    debug(table) {                             // table (curly) open
      writefln(
        "* [table curly] %s",
        line
      );
    }
    type["blocks"] = TriState.on;
    type["table"] = TriState.on;
    type["curly_table"] = TriState.on;
#+END_SRC

***** block (various) tic open                                      :tic:

#+name: abs_functions_block
#+BEGIN_SRC d
  } else if (matchFirst(line, rgx.block_tic_code_open)) {
    /+ tic code open +/
    debug(code) {                              // code (tic) open
      writefln(
        "* [code tic] %s",
        line
      );
    }
    type["blocks"] = TriState.on;
    type["code"] = TriState.on;
    type["tic_code"] = TriState.on;
  } else if (matchFirst(line, rgx.block_tic_poem_open)) {
    /+ tic poem open +/
    debug(poem) {                              // poem (tic) open
      writefln(
        "* [poem tic] %s",
        line
      );
    }
    obj_cite_number_poem["start"] = to!string(obj_cite_number);
    type["blocks"] = TriState.on;
    type["verse_new"] = State.on;
    type["poem"] = TriState.on;
    type["tic_poem"] = TriState.on;
  } else if (matchFirst(line, rgx.block_tic_group_open)) {
    /+ tic group open +/
    debug(group) {                             // group (tic) open
      writefln(
        "* [group tic] %s",
        line
      );
    }
    type["blocks"] = TriState.on;
    type["group"] = TriState.on;
    type["tic_group"] = TriState.on;
  } else if (matchFirst(line, rgx.block_tic_block_open)) {
    /+ tic block open +/
    debug(block) {                             // block (tic) open
      writefln(
        "* [block tic] %s",
        line
      );
    }
    type["blocks"] = TriState.on;
    type["block"] = TriState.on;
    type["tic_block"] = TriState.on;
  } else if (matchFirst(line, rgx.block_tic_quote_open)) {
    /+ tic quote open +/
    debug(quote) {                             // quote (tic) open
      writefln(
        "* [quote tic] %s",
        line
      );
    }
    type["blocks"] = TriState.on;
    type["quote"] = TriState.on;
    type["tic_quote"] = TriState.on;
  } else if (matchFirst(line, rgx.block_tic_table_open)) {
    /+ tic table open +/
    debug(table) {                             // table (tic) open
      writefln(
        "* [table tic] %s",
        line
      );
    }
    type["blocks"] = TriState.on;
    type["table"] = TriState.on;
    type["tic_table"] = TriState.on;
  }
#+END_SRC

***** function close for block starts

#+name: abs_functions_block
#+BEGIN_SRC d
}
#+END_SRC

**** block continue (an open block)                             :continue:
***** code block (special status, deal with first)                 :code:

#+name: abs_functions_block_code
#+BEGIN_SRC d
void _code_block_(L,O,T)(
  ref L line,
  ref O an_object,
  ref T type
) {
  debug(asserts){
    static assert(is(typeof(line)      == char[]));
    static assert(is(typeof(an_object) == string[string]));
    static assert(is(typeof(type)      == int[string]));
  }
  auto rgx = Rgx();
  if (type["curly_code"] == TriState.on) {
    if (matchFirst(line, rgx.block_curly_code_close)) {
      debug(code) {                                    // code (curly) close
        writeln(line);
      }
      type["blocks"] = TriState.closing;
      type["code"] = TriState.closing;
      type["curly_code"] = TriState.off;
    } else {
      debug(code) {                                    // code (curly) line
        writeln(line);
      }
      an_object[an_object_key] ~= line ~= "\n";        // code (curly) line
    }
  } else if (type["tic_code"] == TriState.on) {
    if (matchFirst(line, rgx.block_tic_close)) {
      debug(code) {                                    // code (tic) close
        writeln(line);
      }
      type["blocks"] = TriState.closing;
      type["code"] = TriState.closing;
      type["tic_code"] = TriState.off;
    } else {
      debug(code) {                                    // code (tic) line
        writeln(line);
      }
      an_object[an_object_key] ~= line ~= "\n";        // code (tic) line
    }
  }
}
#+END_SRC

***** biblio block                                               :biblio:
****** biblio tag map

#+name: abs_functions_block_biblio
#+BEGIN_SRC d
final string biblio_tag_map(A)(A abr) {
  debug(asserts){
    static assert(is(typeof(abr) == string));
  }
  auto btm = [
    "au"                               : "author_raw",
    "ed"                               : "editor_raw",
    "ti"                               : "fulltitle",
    "lng"                              : "language",
    "jo"                               : "journal",
    "vol"                              : "volume",
    "edn"                              : "edition",
    "yr"                               : "year",
    "pl"                               : "place",
    "pb"                               : "publisher",
    "pub"                              : "publisher",
    "pg"                               : "pages",
    "pgs"                              : "pages",
    "sn"                               : "short_name"
  ];
  return btm[abr];
}
#+END_SRC

******* +consider+

#+name: none
#+BEGIN_SRC d
final string biblio_tag_map_(A)(A abr) {
  debug(asserts){
    static assert(is(typeof(abr) == string));
  }
  string name;
  switch (abr) {
  case "au":
    name="author_raw";
    break;
  case "ed":
    name="editor_raw";
    break;
  case "ti":
    name="fulltitle";
    break;
  case "lng":
    name="language";
    break;
  case "jo":
    name="journal";
    break;
  case "vol":
    name="volume";
    break;
  case "edn":
    name="edition";
    break;
  case "yr":
    name="year";
    break;
  case "pl":
    name="place";
    break;
  case "pb":
    name="publisher";
    break;
  case "pub":
    name="publisher";
    break;
  case "pg":
    name="pages";
    break;
  case "pgs":
    name="pages";
    break;
  case "sn":
    name="short_name";
    break;
  default:
    name=abr;
    break;
  }
  return name;
}
#+END_SRC

****** biblio block

#+name: abs_functions_block_biblio
#+BEGIN_SRC d
void _biblio_block_(
  char[] line,
  ref int[string] type,
  ref int bib_entry,
  ref string biblio_entry_str_json,
  ref string[] biblio_arr_json
) {
  mixin SiSUbiblio;
  auto jsn = BibJsnStr();
  auto rgx = Rgx();
  if (matchFirst(line, rgx.heading_biblio)) {
    type["biblio_section"] = TriState.on;
    type["blurb_section"] = State.off;
    type["glossary_section"] = State.off;
  }
  if (line.empty) {
    debug {
      debug(biblioblock) {
        writeln("---");
      }
      debug(biblioblockinclude) {
        writeln(biblio_entry_str_json.length);
      }
    }
    if ((bib_entry == State.off)
    && (biblio_entry_str_json.empty)) {
      bib_entry = State.on;
      biblio_entry_str_json = jsn.biblio_entry_tags_jsonstr;
    } else if (!(biblio_entry_str_json.empty)) {
      bib_entry = State.off;
      if (!(biblio_entry_str_json == jsn.biblio_entry_tags_jsonstr)) {
        auto biblio_entry = parseJSON(biblio_entry_str_json);
        if (biblio_entry["fulltitle"].str.empty) {
          writeln("check problem entry (Title missing): ", biblio_entry_str_json);
        } else if ((biblio_entry["author_raw"].str.empty) && (biblio_entry["editor_raw"].str.empty)) {
          writeln("check problem entry (No author and no editor): ", biblio_entry_str_json);
        } else {
          biblio_arr_json ~= biblio_entry_str_json;
        }
        biblio_entry_str_json = jsn.biblio_entry_tags_jsonstr;
      }
    } else { // CHECK ERROR
      writeln("?? 2. ERROR ", biblio_entry_str_json, "??");
      biblio_entry_str_json = "";
    }
  } else if (matchFirst(line, rgx.biblio_tags)) {
    debug(biblioblock) {
      writeln(line);
    }
    auto bt = match(line, rgx.biblio_tags);
    bib_entry = State.off;
    st=to!string(bt.captures[1]);
    auto header_tag_value=to!string(bt.captures[2]);
    JSONValue j = parseJSON(biblio_entry_str_json);
    biblio_tag_name = (match(st, rgx.biblio_abbreviations))
      ? (biblio_tag_map(st))
      : st;
    j.object[biblio_tag_name] = header_tag_value;
    debug(bibliounsortedcheckduplicates) {
      writeln(biblio_tag_name, ": ", header_tag_value);
      writeln("--");
    }
    switch (biblio_tag_name) {
    case "author_raw": // author_arr author (fn sn)
      j["author_arr"] =
        split(header_tag_value, rgx.arr_delimiter);
      string tmp;
      biblioAuthorLoop:
      foreach (au; j["author_arr"].array) {
        if (auto x = match(au.str, rgx.name_delimiter)) {
          tmp ~= x.captures[2] ~ " " ~ x.captures[1] ~ ", ";
        } else {
          tmp ~= au.str;
        }
      }
      tmp = replace(tmp, rgx.trailing_comma, "");
      j["author"].str = tmp;
      goto default;
    case "editor_raw": // editor_arr editor (fn sn)
      j["editor_arr"] =
        split(header_tag_value, rgx.arr_delimiter);
      string tmp;
      biblioEditorLoop:
      foreach (ed; j["editor_arr"].array) {
        if (auto x = match(ed.str, rgx.name_delimiter)) {
          tmp ~= x.captures[2] ~ " " ~ x.captures[1] ~ ", ";
        } else {
          tmp ~= ed.str;
        }
      }
      tmp = replace(tmp, rgx.trailing_comma, "");
      j["editor"].str = tmp;
      goto default;
    case "fulltitle": // title & subtitle
      goto default;
    default:
      break;
    }
    auto s = j.toString();
    debug(biblio1) {
      writefln(
        "* %s: %s\n%s",
        biblio_tag_name,
        biblio_tag_entry,
        j[biblio_tag_name]
      );
    }
    if ((match(line, rgx.comment))) {
      writeln("ERROR", line, "COMMENT");
      writeln("ERROR", s, "%%");
    }
    if (!(match(line, rgx.comment))) {
      debug(biblioblockinclude) {
        writeln(line);
      }
      biblio_entry_str_json = s;
    } else {
      biblio_entry_str_json = "";
    }
    header_tag_value="";
  }
}
#+END_SRC

***** TODO poem block, verse objects                         :poem:verse:

why extra object stuff only in poem/verse?

#+name: abs_functions_block_poem
#+BEGIN_SRC d
void _poem_block_(L,O,T,C,N,Ma)(
  L     line,
  ref O an_object,
  ref T type,
  ref C cntr,
  N     obj_cite_number_poem,
  Ma    dochead_make_aa,
) {
  debug(asserts){
    static assert(is(typeof(line)                 == char[]));
    static assert(is(typeof(an_object)            == string[string]));
    static assert(is(typeof(type)                 == int[string]));
    static assert(is(typeof(cntr)                 == int));
    static assert(is(typeof(obj_cite_number_poem) == string[string]));
    static assert(is(typeof(dochead_make_aa)      == string[string][string]));
  }
  auto rgx = Rgx();
  if (type["curly_poem"] == TriState.on) {
    if (matchFirst(line, rgx.block_curly_poem_close)) {
      an_object[an_object_key]="verse";
      debug(poem) {                               // poem (curly) close
        writefln(
          "* [poem curly] %s",
          line
        );
      }
      if (processing.length > 0) {
        an_object[an_object_key] = processing["verse"];
      }
      debug(poem) {                               // poem (curly) close
        writeln(__LINE__);
        writefln(
          "* %s %s",
          obj_cite_number,
          line
        );
      }
      if (an_object.length > 0) {
        debug(poem) {                             // poem (curly) close
          writeln(
            obj_cite_number,
            an_object[an_object_key]
          );
        }
        an_object["is"] = "verse";
        auto substantive_object_and_anchor_tags_tuple =
          obj_im.obj_inline_markup_and_anchor_tags(an_object, an_object_key, dochead_make_aa);
        an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
        anchor_tags = substantive_object_and_anchor_tags_tuple[1];
        comp_obj_block                            = comp_obj_block.init;
        comp_obj_block.use                        = "body";
        comp_obj_block.is_of                      = "block";
        comp_obj_block.is_a                       = "verse";
        comp_obj_block.ocn                        = obj_cite_number;
        comp_obj_block.obj_cite_number            = (obj_cite_number==0) ? "" : to!string(obj_cite_number);
        comp_obj_block.text                       = an_object["substantive"];
        the_document_body_section                 ~= comp_obj_block;
        object_reset(an_object);
        processing.remove("verse");
        ++cntr;
      }
      obj_cite_number_poem["end"] =
        to!string(obj_cite_number);
      type["blocks"] = TriState.closing;
      type["poem"] = TriState.closing;
      type["curly_poem"] = TriState.off;
    } else {
      processing["verse"] ~= line ~= "\n";
      if (type["verse_new"] == State.on) {
        obj_cite_number =
          ocn_emit(type["ocn_status"]);
        type["verse_new"] = State.off;
      } else if (matchFirst(line, rgx.newline_eol_delimiter_only)) {
        verse_line = TriState.off;
        type["verse_new"] = State.on;
      }
      if (type["verse_new"] == State.on) {
        verse_line=1;
        an_object[an_object_key] = processing["verse"];
        debug(poem) {                          // poem verse
          writefln(
            "* %s curly\n%s",
            obj_cite_number,
            an_object[an_object_key]
          );
        }
        processing.remove("verse");
        an_object["is"] = "verse";
        auto comp_obj_location = node_construct.node_location_emitter(
          content_non_header,
          segment_anchor_tag_that_object_belongs_to,
          obj_cite_number,
          cntr,
          heading_ptr-1,
          an_object["is"]
        );
        auto substantive_object_and_anchor_tags_tuple =
          obj_im.obj_inline_markup_and_anchor_tags(an_object, an_object_key, dochead_make_aa);
        an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
        anchor_tags = substantive_object_and_anchor_tags_tuple[1];
        comp_obj_block                            = comp_obj_block.init;
        comp_obj_block.use                        = "body";
        comp_obj_block.is_of                      = "block";
        comp_obj_block.is_a                       = "verse";
        comp_obj_block.ocn                        = obj_cite_number;
        comp_obj_block.obj_cite_number            = (obj_cite_number==0) ? "" : to!string(obj_cite_number);
        comp_obj_block.text                       = an_object["substantive"];
        the_document_body_section                 ~= comp_obj_block;
        object_reset(an_object);
        processing.remove("verse");
        ++cntr;
      }
    }
  } else if (type["tic_poem"] == TriState.on) {
    if (auto m = matchFirst(line, rgx.block_tic_close)) { // tic_poem_close
      an_object[an_object_key]="verse";
      debug(poem) {                                       // poem (curly) close
        writefln(
          "* [poem tic] %s",
          line
        );
      }
      if (processing.length > 0) {
        an_object[an_object_key] = processing["verse"];
      }
      if (an_object.length > 0) {
        debug(poem) {                                     // poem (tic) close
          writeln(__LINE__);
          writeln(obj_cite_number, line);
        }
        processing.remove("verse");
        an_object["is"] = "verse";
        auto substantive_object_and_anchor_tags_tuple =
          obj_im.obj_inline_markup_and_anchor_tags(an_object, an_object_key, dochead_make_aa);
        an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
        anchor_tags = substantive_object_and_anchor_tags_tuple[1];
        comp_obj_block                            = comp_obj_block.init;
        comp_obj_block.use                        = "body";
        comp_obj_block.is_of                      = "block";
        comp_obj_block.is_a                       = "verse";
        comp_obj_block.ocn                        = obj_cite_number;
        comp_obj_block.obj_cite_number            = (obj_cite_number==0) ? "" : to!string(obj_cite_number);
        comp_obj_block.text                       = an_object["substantive"];
        the_document_body_section                 ~= comp_obj_block;
        obj_cite_number_poem["end"]               = to!string(obj_cite_number);
        object_reset(an_object);
        processing.remove("verse");
        ++cntr;
      }
      type["blocks"] = TriState.closing;
      type["poem"] = TriState.closing;
      type["tic_poem"] = TriState.off;
    } else {
      processing["verse"] ~= line ~= "\n";
      if (type["verse_new"] == State.on) {
        obj_cite_number =
          ocn_emit(type["ocn_status"]);
        type["verse_new"] = State.off;
      } else if (matchFirst(line, rgx.newline_eol_delimiter_only)) {
        type["verse_new"] = State.on;
        verse_line = TriState.off;
      }
      if (type["verse_new"] == State.on) {
        verse_line=1;
        an_object[an_object_key] = processing["verse"];
        debug(poem) {                            // poem (tic) close
          writefln(
            "* %s tic\n%s",
            obj_cite_number,
            an_object[an_object_key]
          );
        }
        processing.remove("verse");
        an_object["is"] = "verse";
        auto comp_obj_location =
          node_construct.node_location_emitter(
            content_non_header,
            segment_anchor_tag_that_object_belongs_to,
            obj_cite_number,
            cntr,
            heading_ptr-1,
            an_object["is"]
          );
        auto substantive_object_and_anchor_tags_tuple =
          obj_im.obj_inline_markup_and_anchor_tags(an_object, an_object_key, dochead_make_aa);
        an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
        anchor_tags = substantive_object_and_anchor_tags_tuple[1];
        comp_obj_block                            = comp_obj_block.init;
        comp_obj_block.use                        = "body";
        comp_obj_block.is_of                      = "block";
        comp_obj_block.is_a                       = "verse";
        comp_obj_block.ocn                        = obj_cite_number;
        comp_obj_block.obj_cite_number            = (obj_cite_number==0) ? "" : to!string(obj_cite_number);
        comp_obj_block.text                       = an_object["substantive"];
        the_document_body_section                 ~= comp_obj_block;
        object_reset(an_object);
        processing.remove("verse");
        ++cntr;
      }
    }
  }
}
#+END_SRC

***** group block                                                 :group:

#+name: abs_functions_block_group
#+BEGIN_SRC d
void _group_block_(L,O,T)(
  ref L line,
  ref O an_object,
  ref T type
) {
  debug(asserts){
    static assert(is(typeof(line)      == char[]));
    static assert(is(typeof(an_object) == string[string]));
    static assert(is(typeof(type)      == int[string]));
  }
  auto rgx = Rgx();
  if (type["curly_group"] == State.on) {
    if (matchFirst(line, rgx.block_curly_group_close)) {
      debug(group) {                              // group (curly) close
        writeln(line);
      }
      type["blocks"] = TriState.closing;
      type["group"] = TriState.closing;
      type["curly_group"] = TriState.off;
    } else {
      debug(group) {                              // group
        writeln(line);
      }
      an_object[an_object_key] ~= line ~= "\n";   // build group array (or string)
    }
  } else if (type["tic_group"] == TriState.on) {
    if (matchFirst(line, rgx.block_tic_close)) {
      debug(group) {                              // group (tic) close
        writeln(line);
      }
      type["blocks"] = TriState.closing;
      type["group"] = TriState.closing;
      type["tic_group"] = TriState.off;
    } else {
      debug(group) {                              // group
        writeln(line);
      }
      an_object[an_object_key] ~= line ~= "\n";   // build group array (or string)
    }
  }
}
#+END_SRC

***** block block                                                 :block:

#+name: abs_functions_block_block
#+BEGIN_SRC d
void _block_block_(L,O,T)(
  ref L line,
  ref O an_object,
  ref T type
) {
  debug(asserts){
    static assert(is(typeof(line)      == char[]));
    static assert(is(typeof(an_object) == string[string]));
    static assert(is(typeof(type)      == int[string]));
  }
  auto rgx = Rgx();
  if (type["curly_block"] == TriState.on) {
    if (matchFirst(line, rgx.block_curly_block_close)) {
      debug(block) {                             // block (curly) close
        writeln(line);
      }
      type["blocks"] = TriState.closing;
      type["block"] = TriState.closing;
      type["curly_block"] = TriState.off;
    } else {
      debug(block) {                             // block
        writeln(line);
      }
      an_object[an_object_key] ~= line ~= "\n";   // build block array (or string)
    }
  } else if (type["tic_block"] == TriState.on) {
    if (matchFirst(line, rgx.block_tic_close)) {
      debug(block) {                              // block (tic) close
        writeln(line);
      }
      type["blocks"] = TriState.closing;
      type["block"] = TriState.closing;
      type["tic_block"] = TriState.off;
    } else {
      debug(block) {                             // block
        writeln(line);
      }
      an_object[an_object_key] ~= line ~= "\n";   // build block array (or string)
    }
  }
}
#+END_SRC

***** quote block                                                 :quote:

#+name: abs_functions_block_quote
#+BEGIN_SRC d
void _quote_block_(L,O,T)(
  ref L line,
  ref O an_object,
  ref T type
) {
  debug(asserts){
    static assert(is(typeof(line)      == char[]));
    static assert(is(typeof(an_object) == string[string]));
    static assert(is(typeof(type)      == int[string]));
  }
  auto rgx = Rgx();
  if (type["curly_quote"] == TriState.on) {
    if (matchFirst(line, rgx.block_curly_quote_close)) {
      debug(quote) {                              // quote (curly) close
        writeln(line);
      }
      type["blocks"] = TriState.closing;
      type["quote"] = TriState.closing;
      type["curly_quote"] = TriState.off;
    } else {
      debug(quote) {
        writeln(line);
      }
      an_object[an_object_key] ~= line ~= "\n";   // build quote array (or string)
    }
  } else if (type["tic_quote"] == TriState.on) {
    if (matchFirst(line, rgx.block_tic_close)) {
      debug(quote) {                              // quote (tic) close
        writeln(line);
      }
      type["blocks"] = TriState.closing;
      type["quote"] = TriState.closing;
      type["tic_quote"] = TriState.off;
    } else {
      debug(quote) {
        writeln(line);
      }
      an_object[an_object_key] ~= line ~= "\n";   // build quote array (or string)
    }
  }
}
#+END_SRC

***** table block                                                 :table:

#+name: abs_functions_block_table
#+BEGIN_SRC d
void _table_block_(L,O,T)(
  ref L line,
  ref O an_object,
  ref T type
) {
  debug(asserts){
    static assert(is(typeof(line)      == char[]));
    static assert(is(typeof(an_object) == string[string]));
    static assert(is(typeof(type)      == int[string]));
  }
  auto rgx = Rgx();
  if (type["curly_table"] == TriState.on) {
    if (matchFirst(line, rgx.block_curly_table_close)) {
      debug(table) {                           // table (curly) close
        writeln(line);
      }
      type["blocks"] = TriState.closing;
      type["table"] = TriState.closing;
      type["curly_table"] = TriState.off;
    } else {
      debug(table) {                           // table
        writeln(line);
      }
      an_object[an_object_key] ~= line ~= "\n";           // build table array (or string)
    }
  } else if (type["tic_table"] == TriState.on) {
    if (matchFirst(line, rgx.block_tic_close)) {
      debug(table) {                           // table (tic) close
        writeln(line);
      }
      type["blocks"] = TriState.closing;
      type["table"] = TriState.closing;
      type["tic_table"] = TriState.off;
    } else {
      debug(table) {                           // table
        writeln(line);
      }
      an_object[an_object_key] ~= line ~= "\n";           // build table array (or string)
    }
  }
}
#+END_SRC

**** block end (close an open block): line empty, block flag       :close:

#+name: abs_functions_block_line_status_empty
#+BEGIN_SRC d
void _block_flag_line_empty_(
  char[]                       line,
  ref string[string]           an_object,
  ref ObjGenericComposite[]    the_document_body_section,
  ref string[][string][string] bookindex_unordered_hashes,
  ref int                      obj_cite_number,
  ref ObjGenericComposite      _comp_obj_heading,
  ref int                      cntr,
  ref int[string]              type,
  string[string]               obj_cite_number_poem,
  string[string][string]       dochead_make_aa,
) {
  // line.empty, post contents, empty variables ---------------
  assert(
    line.empty,
    "line should be empty"
  );
  assert(
    (type["blocks"] == TriState.closing),
    "code block status: closed"
  );
  assertions_flag_types_block_status_none_or_closed(type);
  if (type["code"] == TriState.closing) {
    obj_cite_number =
      ocn_emit(type["ocn_status"]);
    an_object["bookindex_nugget"] =
      ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
    bookindex_unordered_hashes =
      bkidx_hash(an_object["bookindex_nugget"], obj_cite_number);
    an_object["is"] = "code";
    auto comp_obj_location =
      node_construct.node_location_emitter(
        content_non_header,
        segment_anchor_tag_that_object_belongs_to,
        obj_cite_number,
        cntr,
        heading_ptr-1,
        an_object["is"]
      );
    auto substantive_object_and_anchor_tags_tuple =
      obj_im.obj_inline_markup_and_anchor_tags(an_object, an_object_key, dochead_make_aa);
    an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
    anchor_tags = substantive_object_and_anchor_tags_tuple[1];
    comp_obj_code                             = comp_obj_code.init;
    comp_obj_code.use                         = "body";
    comp_obj_code.is_of                       = "block";
    comp_obj_code.is_a                        = "code";
    comp_obj_code.ocn                         = obj_cite_number;
    comp_obj_code.obj_cite_number             = (obj_cite_number==0) ? "" : to!string(obj_cite_number);
    comp_obj_code.text                        = an_object["substantive"];
    the_document_body_section                 ~= comp_obj_code;
    object_reset(an_object);
    processing.remove("verse");
    ++cntr;
    type["blocks"] = TriState.off;
    type["code"] = TriState.off;
  } else if (type["poem"] == TriState.closing) {
    an_object["bookindex_nugget"] =
      ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
    bookindex_unordered_hashes =
      bkidx_hash(an_object["bookindex_nugget"], obj_cite_number);
    an_object["is"] = "verse"; // check also
    auto comp_obj_location =
      node_construct.node_location_emitter(
        content_non_header,
        segment_anchor_tag_that_object_belongs_to,
        obj_cite_number,
        cntr,
        heading_ptr-1,
        an_object["is"]
      );
    comp_obj_poem_ocn                         = comp_obj_poem_ocn.init;
    comp_obj_poem_ocn.use                     = "body";
    comp_obj_poem_ocn.is_of                   = "block";
    comp_obj_poem_ocn.is_a                    = "poem";
    comp_obj_poem_ocn.ocn                     = obj_cite_number;
    comp_obj_poem_ocn.obj_cite_number         = (obj_cite_number_poem["start"], obj_cite_number_poem["end"]);
    comp_obj_poem_ocn.text                    = ""; // an_object["substantive"];
    the_document_body_section                 ~= comp_obj_poem_ocn;
    object_reset(an_object);
    processing.remove("verse");
    type["blocks"] = TriState.off;
    type["poem"] = TriState.off;
  } else if (type["table"] == TriState.closing) {
    obj_cite_number =
      ocn_emit(type["ocn_status"]);
    an_object["bookindex_nugget"] =
      ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
    bookindex_unordered_hashes =
      bkidx_hash(an_object["bookindex_nugget"], obj_cite_number);
    an_object["is"] = "table";
    auto comp_obj_location =
      node_construct.node_location_emitter(
        content_non_header,
        segment_anchor_tag_that_object_belongs_to,
        obj_cite_number,
        cntr,
        heading_ptr-1,
        an_object["is"]
      );
    auto substantive_object_and_anchor_tags_tuple =
      obj_im.obj_inline_markup_and_anchor_tags(an_object, an_object_key, dochead_make_aa);
    an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
    anchor_tags = substantive_object_and_anchor_tags_tuple[1];
    comp_obj_block                            = comp_obj_block.init;
    comp_obj_block.use                        = "body";
    comp_obj_block.is_of                      = "block";
    comp_obj_block.is_a                       = "table";
    comp_obj_block.ocn                        = obj_cite_number;
    comp_obj_block.obj_cite_number            = (obj_cite_number==0) ? "" : to!string(obj_cite_number);
    comp_obj_block.text                       = an_object["substantive"];
    the_document_body_section                 ~= comp_obj_block;
    object_reset(an_object);
    processing.remove("verse");
    ++cntr;
    type["blocks"] = TriState.off;
    type["table"] = TriState.off;
  } else if (type["group"] == TriState.closing) {
    obj_cite_number =
      ocn_emit(type["ocn_status"]);
    an_object["bookindex_nugget"] =
      ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
    bookindex_unordered_hashes =
      bkidx_hash(an_object["bookindex_nugget"], obj_cite_number);
    an_object["is"] = "group";
    auto comp_obj_location =
      node_construct.node_location_emitter(
        content_non_header,
        segment_anchor_tag_that_object_belongs_to,
        obj_cite_number,
        cntr,
        heading_ptr-1,
        an_object["is"]
      );
    auto substantive_object_and_anchor_tags_tuple =
      obj_im.obj_inline_markup_and_anchor_tags(an_object, an_object_key, dochead_make_aa);
    an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
    anchor_tags = substantive_object_and_anchor_tags_tuple[1];
    comp_obj_block                            = comp_obj_block.init;
    comp_obj_block.use                        = "body";
    comp_obj_block.is_of                      = "block";
    comp_obj_block.is_a                       = "group";
    comp_obj_block.ocn                        = obj_cite_number;
    comp_obj_block.obj_cite_number            = (obj_cite_number==0) ? "" : to!string(obj_cite_number);
    comp_obj_block.text                       = an_object["substantive"];
    the_document_body_section                 ~= comp_obj_block;
    object_reset(an_object);
    processing.remove("verse");
    ++cntr;
    type["blocks"] = TriState.off;
    type["group"] = TriState.off;
  } else if (type["block"] == TriState.closing) {
    obj_cite_number = ocn_emit(type["ocn_status"]);
    an_object["bookindex_nugget"] =
      ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
    bookindex_unordered_hashes =
      bkidx_hash(an_object["bookindex_nugget"], obj_cite_number);
    an_object["is"] = "block";
    auto comp_obj_location =
      node_construct.node_location_emitter(
        content_non_header,
        segment_anchor_tag_that_object_belongs_to,
        obj_cite_number,
        cntr,
        heading_ptr-1,
        an_object["is"]
       );
    auto substantive_object_and_anchor_tags_tuple =
      obj_im.obj_inline_markup_and_anchor_tags(an_object, an_object_key, dochead_make_aa);
    an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
    anchor_tags = substantive_object_and_anchor_tags_tuple[1];
    comp_obj_block                            = comp_obj_block.init;
    comp_obj_block.use                        = "body";
    comp_obj_block.is_of                      = "block";
    comp_obj_block.is_a                       = "block";
    comp_obj_block.ocn                        = obj_cite_number;
    comp_obj_block.obj_cite_number            = (obj_cite_number==0) ? "" : to!string(obj_cite_number);
    comp_obj_block.text                       = an_object["substantive"];
    the_document_body_section                 ~= comp_obj_block;
    object_reset(an_object);
    processing.remove("verse");
    ++cntr;
    type["blocks"] = TriState.off;
    type["block"] = TriState.off;
  } else if (type["quote"] == TriState.closing) {
    obj_cite_number =
      ocn_emit(type["ocn_status"]);
    an_object["bookindex_nugget"] =
      ("bookindex_nugget" in an_object) ? an_object["bookindex_nugget"] : "";
    bookindex_unordered_hashes =
      bkidx_hash(an_object["bookindex_nugget"], obj_cite_number);
    an_object["is"] = "quote";
    auto comp_obj_location =
      node_construct.node_location_emitter(
        content_non_header,
        segment_anchor_tag_that_object_belongs_to,
        obj_cite_number,
        cntr,
        heading_ptr-1,
        an_object["is"]
      );
    auto substantive_object_and_anchor_tags_tuple =
      obj_im.obj_inline_markup_and_anchor_tags(an_object, an_object_key, dochead_make_aa);
    an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
    anchor_tags = substantive_object_and_anchor_tags_tuple[1];
    comp_obj_block                            = comp_obj_block.init;
    comp_obj_block.use                        = "body";
    comp_obj_block.is_of                      = "block";
    comp_obj_block.is_a                       = "quote";
    comp_obj_block.ocn                        = obj_cite_number;
    comp_obj_block.obj_cite_number            = (obj_cite_number==0) ? "" : to!string(obj_cite_number);
    comp_obj_block.text                       = an_object["substantive"];
    the_document_body_section                 ~= comp_obj_block;
    object_reset(an_object);
    processing.remove("verse");
    ++cntr;
    type["blocks"] = TriState.off;
    type["quote"] = TriState.off;
  }
}
#+END_SRC

*** book index                                                  :bookindex:

#+name: abs_functions_book_index
#+BEGIN_SRC d
auto _book_index_(L,I,O,T,B)(
  L      line,
  ref I  book_idx_tmp,
  ref O  an_object,
  ref T  type,
  B      opt_action_bool,
) {
  debug(asserts){
    static assert(is(typeof(line)            == char[]));
    static assert(is(typeof(book_idx_tmp)    == string));
    static assert(is(typeof(an_object)       == string[string]));
    static assert(is(typeof(type)            == int[string]));
    static assert(is(typeof(opt_action_bool) == bool[string]));
  }
  auto rgx = Rgx();
  if (auto m = match(line, rgx.book_index)) {
    /+ match book_index +/
    debug(bookindexmatch) {                       // book index
      writefln(
        "* [bookindex] %s\n",
        to!string(m.captures[1]),
      );
    }
    an_object["bookindex_nugget"] = to!string(m.captures[1]);
  } else if (auto m = match(line, rgx.book_index_open))  {
    /+ match open book_index +/
    type["book_index"] = State.on;
    if (opt_action_bool["backmatter"] && opt_action_bool["section_bookindex"]) {
      book_idx_tmp = to!string(m.captures[1]);
      debug(bookindexmatch) {                       // book index
        writefln(
          "* [bookindex] %s\n",
          book_idx_tmp,
        );
      }
    }
  } else if (type["book_index"] == State.on )  {
    /+ book_index flag set +/
    if (auto m = match(line, rgx.book_index_close))  {
      type["book_index"] = State.off;
      if (opt_action_bool["backmatter"] && opt_action_bool["section_bookindex"]) {
        an_object["bookindex_nugget"] = book_idx_tmp ~ to!string(m.captures[1]);
        debug(bookindexmatch) {                     // book index
          writefln(
            "* [bookindex] %s\n",
            book_idx_tmp,
          );
        }
      }
      book_idx_tmp = "";
    } else {
      if (opt_action_bool["backmatter"] && opt_action_bool["section_bookindex"]) {
        book_idx_tmp ~= line;
      }
    }
  }
}
#+END_SRC

*** heading or paragraph                                :heading:paragraph:
**** heading found                                               :heading:

#+name: abs_functions_heading
#+BEGIN_SRC d
auto _heading_found_(L,X,H,R,T)(
  L     line,
  X     dochead_make_identify_unmarked_headings,
  ref H heading_match_str,
  ref R heading_match_rgx,
  ref T type
) {
  debug(asserts){
    static assert(is(typeof(line)                                    == char[]));
    static assert(is(typeof(dochead_make_identify_unmarked_headings) == string));
    static assert(is(typeof(heading_match_str)                       == string[string]));
    static assert(is(typeof(heading_match_rgx)                       == Regex!(char)[string]));
    static assert(is(typeof(type)                                    == int[string]));
  }
  auto rgx = Rgx();
  if ((dochead_make_identify_unmarked_headings.length > 2)
  && (type["make_headings"] == State.off)) {
    /+ headings found +/
    debug(headingsfound) {
      writeln(dochead_make_identify_unmarked_headings);
    }
    char[][] make_headings_spl =
      split(
        cast(char[]) dochead_make_identify_unmarked_headings,
        rgx.make_heading_delimiter);
    debug(headingsfound) {
      writeln(make_headings_spl.length);
      writeln(make_headings_spl);
    }
    switch (make_headings_spl.length) {
    case 7 :
      if (!empty(make_headings_spl[6])) {
        heading_match_str["h_4"] =
          "^(" ~ to!string(make_headings_spl[6]) ~ ")";
        heading_match_rgx["h_4"] =
          regex(heading_match_str["h_4"]);
      }
      goto case;
    case 6 :
      if (!empty(make_headings_spl[5])) {
        heading_match_str["h_3"] =
          "^(" ~ to!string(make_headings_spl[5]) ~ ")";
        heading_match_rgx["h_3"] =
          regex(heading_match_str["h_3"]);
      }
      goto case;
    case 5 :
      if (!empty(make_headings_spl[4])) {
        heading_match_str["h_2"] =
          "^(" ~ to!string(make_headings_spl[4]) ~ ")";
        heading_match_rgx["h_2"] =
          regex(heading_match_str["h_2"]);
      }
      goto case;
    case 4 :
      if (!empty(make_headings_spl[3])) {
        heading_match_str["h_1"] =
          "^(" ~ to!string(make_headings_spl[3]) ~ ")";
        heading_match_rgx["h_1"] =
          regex(heading_match_str["h_1"]);
      }
      goto case;
    case 3 :
      if (!empty(make_headings_spl[2])) {
        heading_match_str["h_D"] =
          "^(" ~ to!string(make_headings_spl[2]) ~ ")";
        heading_match_rgx["h_D"] =
          regex(heading_match_str["h_D"]);
      }
      goto case;
    case 2 :
      if (!empty(make_headings_spl[1])) {
        heading_match_str["h_C"] =
          "^(" ~ to!string(make_headings_spl[1]) ~ ")";
        heading_match_rgx["h_C"] =
          regex(heading_match_str["h_C"]);
      }
      goto case;
    case 1 :
      if (!empty(make_headings_spl[0])) {
        heading_match_str["h_B"] =
          "^(" ~ to!string(make_headings_spl[0]) ~ ")";
        heading_match_rgx["h_B"] =
          regex(heading_match_str["h_B"]);
      }
      break;
    default:
      break;
    }
    type["make_headings"] = State.on;
  }
}
#+END_SRC

**** heading make set                                       :heading:

#+name: abs_functions_heading
#+BEGIN_SRC d
auto _heading_make_set_(L,C,R,T)(
  L     line,
  C     line_occur,
  ref R heading_match_rgx,
  ref T type
) {
  debug(asserts){
    static assert(is(typeof(line)              == char[]));
    static assert(is(typeof(line_occur)        == int[string]));
    static assert(is(typeof(heading_match_rgx) == Regex!(char)[string]));
    static assert(is(typeof(type)              == int[string]));
  }
  if ((type["make_headings"] == State.on)
  && ((line_occur["para"] == State.off)
  && (line_occur["heading"] == State.off))
  && ((type["para"] == State.off)
  && (type["heading"] == State.off))) {
    /+ heading make set +/
    if (matchFirst(line, heading_match_rgx["h_B"])) {
      line = "B~ " ~ line;
      debug(headingsfound) {
        writeln(line);
      }
    }
    if (matchFirst(line, heading_match_rgx["h_C"])) {
      line = "C~ " ~ line;
      debug(headingsfound) {
        writeln(line);
      }
    }
    if (matchFirst(line, heading_match_rgx["h_D"])) {
      line = "D~ " ~ line;
      debug(headingsfound) {
        writeln(line);
      }
    }
    if (matchFirst(line, heading_match_rgx["h_1"])) {
      line = "1~ " ~ line;
      debug(headingsfound) {
        writeln(line);
      }
    }
    if (matchFirst(line, heading_match_rgx["h_2"])) {
      line = "2~ " ~ line;
      debug(headingsfound) {
        writeln(line);
      }
    }
    if (matchFirst(line, heading_match_rgx["h_3"])) {
      line = "3~ " ~ line;
      debug(headingsfound) {
        writeln(line);
      }
    }
    if (matchFirst(line, heading_match_rgx["h_4"])) {
      line = "4~ " ~ line;
      debug(headingsfound) {
        writeln(line);
      }
    }
  }
}
#+END_SRC

**** heading match                                               :heading:

#+name: abs_functions_heading
#+BEGIN_SRC d
auto _heading_matched_(L,C,O,K,Lv,Lc,T,Me)(
  ref L  line,
  ref C  line_occur,
  ref O  an_object,
  ref K  an_object_key,
  ref Lv lv,
  ref Lc collapsed_lev,
  ref T  type,
  ref Me dochead_meta_aa,
) {
  debug(asserts){
    static assert(is(typeof(line)            == char[]));
    static assert(is(typeof(line_occur)      == int[string]));
    static assert(is(typeof(an_object)       == string[string]));
    static assert(is(typeof(an_object_key)   == string));
    static assert(is(typeof(lv)              == int[string]));
    static assert(is(typeof(collapsed_lev)   == int[string]));
    static assert(is(typeof(type)            == int[string]));
    static assert(is(typeof(dochead_meta_aa) == string[string][string]));
  }
  auto rgx = Rgx();
  if (auto m = match(line, rgx.heading)) {
    /+ heading match +/
    type["heading"] = State.on;
    if (match(line, rgx.heading_seg_and_above)) {
      type["biblio_section"] = State.off;
      type["glossary_section"] = State.off;
      type["blurb_section"] = State.off;
    }
    type["para"] = State.off;
    ++line_occur["heading"];
    an_object[an_object_key] ~= line ~= "\n";
    an_object["lev"] ~= m.captures[1];
    assertions_doc_structure(an_object, lv); // includes most of the logic for collapsed levels
    switch (an_object["lev"]) {
    case "A":
      an_object[an_object_key]=replaceFirst(an_object[an_object_key],
        rgx.variable_doc_title, (dochead_meta_aa["title"]["full"] ~ ","));
      an_object[an_object_key]=replaceFirst(an_object[an_object_key],
        rgx.variable_doc_author, dochead_meta_aa["creator"]["author"]);
      collapsed_lev["h0"] = 0;
      an_object["lev_collapsed_number"] =
        to!string(collapsed_lev["h0"]);
      lv["lv"] = DocStructMarkupHeading.h_sect_A;
      ++lv["h0"];
      lv["h1"] = State.off;
      lv["h2"] = State.off;
      lv["h3"] = State.off;
      lv["h4"] = State.off;
      lv["h5"] = State.off;
      lv["h6"] = State.off;
      lv["h7"] = State.off;
      goto default;
    case "B":
      collapsed_lev["h1"] = collapsed_lev["h0"] + 1;
      an_object["lev_collapsed_number"] =
        to!string(collapsed_lev["h1"]);
      lv["lv"] = DocStructMarkupHeading.h_sect_B;
      ++lv["h1"];
      lv["h2"] = State.off;
      lv["h3"] = State.off;
      lv["h4"] = State.off;
      lv["h5"] = State.off;
      lv["h6"] = State.off;
      lv["h7"] = State.off;
      goto default;
    case "C":
      collapsed_lev["h2"] = collapsed_lev["h1"] + 1;
      an_object["lev_collapsed_number"] =
        to!string(collapsed_lev["h2"]);
      lv["lv"] = DocStructMarkupHeading.h_sect_C;
      ++lv["h2"];
      lv["h3"] = State.off;
      lv["h4"] = State.off;
      lv["h5"] = State.off;
      lv["h6"] = State.off;
      lv["h7"] = State.off;
      goto default;
    case "D":
      collapsed_lev["h3"] = collapsed_lev["h2"] + 1;
      an_object["lev_collapsed_number"] =
        to!string(collapsed_lev["h3"]);
      lv["lv"] = DocStructMarkupHeading.h_sect_D;
      ++lv["h3"];
      lv["h4"] = State.off;
      lv["h5"] = State.off;
      lv["h6"] = State.off;
      lv["h7"] = State.off;
      goto default;
    case "1":
      if (lv["h3"] > State.off) {
        collapsed_lev["h4"] = collapsed_lev["h3"] + 1;
      } else if (lv["h2"] > State.off) {
        collapsed_lev["h4"] = collapsed_lev["h2"] + 1;
      } else if (lv["h1"] > State.off) {
        collapsed_lev["h4"] = collapsed_lev["h1"] + 1;
      } else if (lv["h0"] > State.off) {
        collapsed_lev["h4"] = collapsed_lev["h0"] + 1;
      }
      an_object["lev_collapsed_number"] =
        to!string(collapsed_lev["h4"]);
      lv["lv"] = DocStructMarkupHeading.h_text_1;
      ++lv["h4"];
      lv["h5"] = State.off;
      lv["h6"] = State.off;
      lv["h7"] = State.off;
      goto default;
    case "2":
      if (lv["h5"] > State.off) {
        an_object["lev_collapsed_number"] =
          to!string(collapsed_lev["h5"]);
      } else if (lv["h4"] > State.off) {
        collapsed_lev["h5"] = collapsed_lev["h4"] + 1;
        an_object["lev_collapsed_number"] =
          to!string(collapsed_lev["h5"]);
      }
      lv["lv"] = DocStructMarkupHeading.h_text_2;
      ++lv["h5"];
      lv["h6"] = State.off;
      lv["h7"] = State.off;
      goto default;
    case "3":
      if (lv["h6"] > State.off) {
        an_object["lev_collapsed_number"] =
          to!string(collapsed_lev["h6"]);
      } else if (lv["h5"] > State.off) {
        collapsed_lev["h6"] = collapsed_lev["h5"] + 1;
        an_object["lev_collapsed_number"] =
          to!string(collapsed_lev["h6"]);
      }
      lv["lv"] = DocStructMarkupHeading.h_text_3;
      ++lv["h6"];
      lv["h7"] = State.off;
      goto default;
    case "4":
      if (lv["h7"] > State.off) {
        an_object["lev_collapsed_number"] =
          to!string(collapsed_lev["h7"]);
      } else if (lv["h6"] > State.off) {
        collapsed_lev["h7"] = collapsed_lev["h6"] + 1;
        an_object["lev_collapsed_number"] =
          to!string(collapsed_lev["h7"]);
      }
      lv["lv"] = DocStructMarkupHeading.h_text_4;
      ++lv["h7"];
      goto default;
    default:
      an_object["lev_markup_number"] = to!string(lv["lv"]);
    }
    debug(heading) {                         // heading
      writeln(strip(line));
    }
  }
}
#+END_SRC

**** para match                                                     :para:

#+name: abs_functions_para
#+BEGIN_SRC d
auto _para_match_(L,O,K,I,B,T,C)(
  ref L  line,
  ref O  an_object,
  ref K  an_object_key,
  ref I  indent,
  ref B  bullet,
  ref T  type,
  ref C  line_occur,
) {
  debug(asserts){
    static assert(is(typeof(line)          == char[]));
    static assert(is(typeof(an_object)     == string[string]));
    static assert(is(typeof(an_object_key) == string));
    static assert(is(typeof(indent)        == int[string]));
    static assert(is(typeof(bullet)        == bool));
    static assert(is(typeof(type)          == int[string]));
    static assert(is(typeof(line_occur)    == int[string]));
  }
  auto rgx = Rgx();
  if (line_occur["para"] == State.off) {
    /+ para matches +/
    type["para"] = State.on;
    an_object[an_object_key] ~= line;        // body_nugget
    indent=[
      "hang_position" : 0,
      "base_position" : 0,
    ];
    bullet = false;
    if (auto m = matchFirst(line, rgx.para_indent)) {
      debug(paraindent) {                    // para indent
        writeln(line);
      }
      indent["hang_position"] = to!int(m.captures[1]);
      indent["base_position"] = 0;
    } else if (matchFirst(line, rgx.para_bullet)) {
      debug(parabullet) {                    // para bullet
        writeln(line);
      }
      bullet = true;
    } else if (auto m = matchFirst(line, rgx.para_indent_hang)) {
      debug(paraindenthang) {                // para indent hang
        writeln(line);
      }
      indent=[
        "hang_position" : to!int(m.captures[1]),
        "base_position" : to!int(m.captures[2]),
      ];
    } else if (auto m = matchFirst(line, rgx.para_bullet_indent)) {
      debug(parabulletindent) {              // para bullet indent
        writeln(line);
      }
      indent=[
        "hang_position" : to!int(m.captures[1]),
        "base_position" : 0,
      ];
      bullet = true;
    }
    ++line_occur["para"];
  }
}
#+END_SRC

*** function emitters                                            :emitters:
**** object                                                       :object:
***** ocn                                                           :ocn:

#+name: ao_emitters_ocn
#+BEGIN_SRC d
struct OCNemitter {
  int obj_cite_number, obj_cite_number_;
  int ocn_emitter(int ocn_status_flag)
  in { assert(ocn_status_flag <= 3); }
  body {
    if (ocn_status_flag == 3) {
      obj_cite_number = obj_cite_number_ = 1;
    } else {
      obj_cite_number=(ocn_status_flag == 0)
      ? ++obj_cite_number_
      : 0;
    }
    assert(obj_cite_number >= 0);
    return obj_cite_number;
  }
  invariant() {
  }
}
#+END_SRC

***** object inline markup munge                          :markup:inline:

#+name: ao_emitters_obj_inline_markup_munge
#+BEGIN_SRC d
struct ObjInlineMarkupMunge {
  string[string] obj_txt;
  int n_foot, n_foot_reg, n_foot_sp_asterisk, n_foot_sp_plus;
  string asterisks_;
  string obj_txt_out, tail, note;
  auto rgx = Rgx();
  auto mkup = InlineMarkup();
  private auto initialize_note_numbers() {
    n_foot = 0;
    n_foot_reg = 0;
    n_foot_sp_asterisk = 0;
    n_foot_sp_plus = 0;
  }
  string url_links(Ot)(Ot obj_txt_in) {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
    /+ url matched +/
    if (auto m = matchAll(obj_txt_in, rgx.inline_url)) {
      /+ link: naked url: http://url +/
      if (match(obj_txt_in, rgx.inline_link_naked_url)) {
        obj_txt_in =
          replaceAll(
            obj_txt_in,
            rgx.inline_link_naked_url,
            ("$1" ~ mkup.lnk_o ~ " $2 " ~ mkup.lnk_c ~  mkup.url_o ~ "$2" ~  mkup.url_c ~ "$3")            // ("$1{ $2 }$2$3")
          );
      }
      /+ link with helper for endnote including the url:
           {~^ link which includes url as footnote }http://url
         maps to:
           { link which includes url as footnote }http://url~{ { http://url }http://url }~
      +/
      if (match(obj_txt_in, rgx.inline_link_endnote_url_helper)) {
        obj_txt_in =
          replaceAll(
            obj_txt_in,
            rgx.inline_link_endnote_url_helper_punctuated,
            (mkup.lnk_o ~ " $1 " ~ mkup.lnk_c ~ mkup.url_o ~ "$2" ~ mkup.url_c ~ "~{ " ~ mkup.lnk_o ~ " $2 " ~ mkup.lnk_c ~ mkup.url_o ~ "$2" ~ mkup.url_c ~  " }~$3") // ("{ $1 }$2~{ { $2 }$2 }~$3")
          );
        obj_txt_in =
          replaceAll(
            obj_txt_in,
            rgx.inline_link_endnote_url_helper,
            (mkup.lnk_o ~ " $1 " ~ mkup.lnk_c ~ mkup.url_o ~ "$2" ~ mkup.url_c ~ "~{ " ~ mkup.lnk_o ~ " $2 " ~ mkup.lnk_c ~ mkup.url_o ~ "$2" ~ mkup.url_c ~  " }~")   // ("{ $1 }$2~{ { $2 }$2 }~")
          );
      }
      /+ link with regular markup:
         { linked text or image }http://url
      +/
      if (match(obj_txt_in, rgx.inline_link_markup_regular)) {
        obj_txt_in =
          replaceAll(
            obj_txt_in,
            rgx.inline_link_markup_regular,
            ("$1" ~ mkup.lnk_o ~ " $2 " ~ mkup.lnk_c ~  mkup.url_o ~ "$3" ~  mkup.url_c ~ "$4")            // ("$1{ $2 }$3$4")
          );
      }
    }
    return obj_txt_in;
  }
  string footnotes_endnotes_markup_and_number_or_stars(Ot)(Ot obj_txt_in) {                                // here endnotes are marked up
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
    /+ endnotes (regular) +/
    obj_txt_in =
      replaceAll(
        obj_txt_in,
        rgx.inline_notes_curly,
        (mkup.en_a_o ~ " $1" ~ mkup.en_a_c)
      );
    if (match(obj_txt_in, rgx.inline_notes_al_gen)) {
      if (auto m = matchAll(obj_txt_in, rgx.inline_text_and_note_al_)) {
        foreach(n; m) {
          if (match(to!string(n.hit), rgx.inline_al_delimiter_open_symbol_star)) {
            ++n_foot_sp_asterisk;
            asterisks_ = "*";
            n_foot=n_foot_sp_asterisk;
            obj_txt_out ~=
              (replaceFirst(
                to!string(n.hit),
                rgx.inline_al_delimiter_open_symbol_star,
                (mkup.en_a_o ~ replicate(asterisks_, n_foot_sp_asterisk) ~ " ")
              ) ~ "\n");
          } else if (match(to!string(n.hit), rgx.inline_al_delimiter_open_regular)) {
            ++n_foot_reg;
            n_foot=n_foot_reg;
            obj_txt_out ~=
              (replaceFirst(
                to!string(n.hit),
                rgx.inline_al_delimiter_open_regular,
                (mkup.en_a_o ~ to!string(n_foot) ~ " ")
              ) ~ "\n");
          } else {
            obj_txt_out ~= to!string(n.hit) ~ "\n";
          }
        }
      }
    } else {
      obj_txt_out = obj_txt_in;
    }
    return obj_txt_out;
  }
  private auto object_notes_(string obj_txt_in)
  in { }
  body {
    obj_txt_out = "";
    tail = "";
    /+ special endnotes +/
    obj_txt_in = replaceAll(
      obj_txt_in,
      rgx.inline_notes_curly_sp_asterisk,
      (mkup.en_a_o ~ "*" ~ " $1" ~ mkup.en_a_c)
    );
    obj_txt_in =
      replaceAll(
        obj_txt_in,
        rgx.inline_notes_curly_sp_plus,
        (mkup.en_a_o ~ "+" ~ " $1" ~ mkup.en_a_c)
      );
    /+ url matched +/
    if (auto m = matchAll(obj_txt_in, rgx.inline_url)) {
      obj_txt_in = url_links(obj_txt_in);
    }
    obj_txt_out = footnotes_endnotes_markup_and_number_or_stars(obj_txt_in);
    debug(footnotes) {
      writeln(obj_txt_out, tail);
    }
    obj_txt_out = obj_txt_out ~ tail;
    debug(footnotesdone) {
      foreach(m; matchAll(obj_txt_out,
      (mkup.en_a_o ~ `\s*(.+?)` ~ mkup.en_a_c))) {
        writeln(m.captures[1]);
        writeln(m.hit);
      }
    }
    return obj_txt_out;
  }
  string para(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    obj_txt["munge"]=obj_txt_in;
    obj_txt["munge"]=replaceFirst(obj_txt["munge"], rgx.para_attribs, "");
    obj_txt["munge"]=replaceFirst(obj_txt["munge"], rgx.obj_cite_number_off_all, "");
    obj_txt["munge"]=object_notes_(obj_txt["munge"]);
    debug(munge) {
      writeln(__LINE__);
      writeln(obj_txt_in);
      writeln(__LINE__);
      writeln(to!string(obj_txt["munge"]));
    }
    return obj_txt["munge"];
  }
  string heading(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    obj_txt["munge"]=obj_txt_in;
    obj_txt["munge"]=replaceFirst(obj_txt["munge"], rgx.heading, "");
    obj_txt["munge"]=replaceFirst(obj_txt["munge"], rgx.obj_cite_number_off_all, "");
    obj_txt["munge"]=strip(obj_txt["munge"]);
    obj_txt["munge"]=object_notes_(obj_txt["munge"]);
    debug(munge) {
      writeln(__LINE__);
      writeln(obj_txt_in);
      writeln(__LINE__);
      writeln(to!string(obj_txt["munge"]));
    }
    return obj_txt["munge"];
  }
  invariant() {
  }
  /+ revisit +/
  string code(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    obj_txt["munge"]=obj_txt_in;
    return obj_txt["munge"];
  }
  invariant() {
  }
  string group(string obj_txt_in)
  in { }
  body {
    obj_txt["munge"]=obj_txt_in;
    obj_txt["munge"]=object_notes_(obj_txt["munge"]);
    return obj_txt["munge"];
  }
  invariant() {
  }
  string block(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    obj_txt["munge"]=obj_txt_in;
    obj_txt["munge"]=object_notes_(obj_txt["munge"]);
    return obj_txt["munge"];
  }
  invariant() {
  }
  string verse(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    obj_txt["munge"]=obj_txt_in;
    obj_txt["munge"]=object_notes_(obj_txt["munge"]);
    return obj_txt["munge"];
  }
  invariant() {
  }
  string quote(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    obj_txt["munge"]=obj_txt_in;
    return obj_txt["munge"];
  }
  invariant() {
  }
  string table(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    obj_txt["munge"]=obj_txt_in;
    return obj_txt["munge"];
  }
  invariant() {
  }
  string comment(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    obj_txt["munge"]=obj_txt_in;
    return obj_txt["munge"];
  }
  invariant() {
  }
}
#+END_SRC

***** toc, tags, object inline markup                     :markup:inline:
****** open

#+name: ao_emitters_obj_inline_markup
#+BEGIN_SRC d
struct ObjInlineMarkup {
  auto rgx = Rgx();
  auto munge = ObjInlineMarkupMunge();
  string[string] obj_txt;
#+END_SRC

****** object inline markup and anchor tags              :markup:inline:

#+name: ao_emitters_obj_inline_markup_and_anchor_tags
#+BEGIN_SRC d
  auto obj_inline_markup_and_anchor_tags(O,K,Ma)(
    O  obj_,
    K  obj_key_,
    Ma dochead_make_aa
  )
  in {
    debug(asserts){
      static assert(is(typeof(obj_)            == string[string]));
      static assert(is(typeof(obj_key_)        == string));
      static assert(is(typeof(dochead_make_aa) == string[string][string]));
    }
  }
  body {
    obj_txt["munge"]=obj_[obj_key_].dup;
    obj_txt["munge"]=(match(obj_["is"], ctRegex!(`verse|code`)))
    ? obj_txt["munge"]
    : strip(obj_txt["munge"]);
    static __gshared string[] anchor_tags_ = [];
    switch (obj_["is"]) {
    case "heading":
      static __gshared string anchor_tag = "";
      // TODO WORK ON, you still need to ensure that level 1 anchor_tags are unique
      obj_txt["munge"]=_configured_auto_heading_numbering_and_segment_anchor_tags(obj_txt["munge"], obj_, dochead_make_aa);
      obj_txt["munge"]=_make_segment_anchor_tags_if_none_provided(obj_txt["munge"], obj_["lev"]);
      if (auto m = match(obj_txt["munge"], rgx.heading_anchor_tag)) {
        anchor_tag = m.captures[1];
        anchor_tags_ ~=anchor_tag;
      } else if (obj_["lev"] == "1") {
        writeln("heading anchor tag missing: ", obj_txt["munge"]);
      }
      obj_txt["munge"]=munge.heading(obj_txt["munge"]);
      break;
    case "para":
      obj_txt["munge"]=munge.para(obj_txt["munge"]);
      break;
    case "code":
      obj_txt["munge"]=munge.code(obj_txt["munge"]);
      break;
    case "group":
      obj_txt["munge"]=munge.group(obj_txt["munge"]);
      break;
    case "block":
      obj_txt["munge"]=munge.block(obj_txt["munge"]);
      break;
    case "verse":
      obj_txt["munge"]=munge.verse(obj_txt["munge"]);
      break;
    case "quote":
      obj_txt["munge"]=munge.quote(obj_txt["munge"]);
      break;
    case "table":
      obj_txt["munge"]=munge.table(obj_txt["munge"]);
      break;
    case "comment":
      obj_txt["munge"]=munge.comment(obj_txt["munge"]);
      break;
    case "doc_end_reset":
      munge.initialize_note_numbers();
      break;
    default:
      break;
    }
    auto t = tuple(
     obj_txt["munge"],
     anchor_tags_,
    );
    anchor_tags_=[];
    return t;
  }
  invariant() {
  }
#+END_SRC

****** toc, table of contents build, gather headings     :markup:inline:

#+name: ao_emitters_obj_inline_markup_table_of_contents
#+BEGIN_SRC d
  auto _clean_heading_toc_(Toc)(
    Toc heading_toc_,
  ) {
   debug(asserts){
     static assert(is(typeof(heading_toc_) == char[]));
   }
   auto m = matchFirst(cast(char[]) heading_toc_, rgx.heading);
   heading_toc_ =
     replaceAll(
       m.post,
       rgx.inline_notes_curly_gen,
       ("")
     );
   return heading_toc_;
  };
  auto table_of_contents_gather_headings(O,Ma,Ts,Ta,X,Toc)(
    O     obj_,
    Ma    dochead_make_aa,
    Ts    segment_anchor_tag_that_object_belongs_to,
    Ta    _anchor_tag,
    ref X lev4_subtoc,
    Toc   the_table_of_contents_section,
  )
  in {
    debug(asserts){
      static assert(is(typeof(obj_)                                      == string[string]));
      static assert(is(typeof(dochead_make_aa)                           == string[string][string]));
      static assert(is(typeof(segment_anchor_tag_that_object_belongs_to) == string));
      static assert(is(typeof(_anchor_tag)                               == string));
      static assert(is(typeof(lev4_subtoc)                               == string[][string]));
      static assert(is(typeof(the_table_of_contents_section)             == ObjGenericComposite[][string]));
    }
  }
  body {
    ObjGenericComposite comp_obj_toc;
    mixin InternalMarkup;
    auto mkup = InlineMarkup();
    char[] heading_toc_ = to!(char[])(obj_["body_nugget"].dup.strip);
    heading_toc_ = _clean_heading_toc_(heading_toc_);
    auto attrib="";
    string toc_txt_, subtoc_txt_;
    int[string] indent;
    if (to!int(obj_["lev_markup_number"]) > 0) {
      indent=[
        "hang_position" : to!int(obj_["lev_markup_number"]),
        "base_position" : to!int(obj_["lev_markup_number"]),
      ];
      toc_txt_ = format(
        "{ %s }#%s",
        heading_toc_,
        _anchor_tag,
      );
      toc_txt_= munge.url_links(toc_txt_);
      comp_obj_toc                       = comp_obj_toc.init;
      comp_obj_toc.use                   = "frontmatter";
      comp_obj_toc.is_of                 = "para";
      comp_obj_toc.is_a                  = "toc";
      comp_obj_toc.ocn                   = 0;
      comp_obj_toc.obj_cite_number       = "";
      comp_obj_toc.indent_hang           = indent["hang_position"];
      comp_obj_toc.indent_base           = indent["base_position"];
      comp_obj_toc.bullet                = false;
      comp_obj_toc.text                  = to!string(toc_txt_).strip;
      the_table_of_contents_section["scroll"] ~= comp_obj_toc;
    } else {
      indent=[
        "hang_position" : 0,
        "base_position" : 0,
      ];
      comp_obj_toc                       = comp_obj_toc.init;
      comp_obj_toc.use                   = "frontmatter";
      comp_obj_toc.is_of                 = "para";
      comp_obj_toc.is_a                  = "toc";
      comp_obj_toc.ocn                   = 0;
      comp_obj_toc.obj_cite_number       = "";
      comp_obj_toc.indent_hang           = indent["hang_position"];
      comp_obj_toc.indent_base           = indent["base_position"];
      comp_obj_toc.bullet                = false;
      comp_obj_toc.text                  = "Table of Contents";
      the_table_of_contents_section["scroll"] ~= comp_obj_toc;
    }
    comp_obj_toc                       = comp_obj_toc.init;
    comp_obj_toc.use                   = "frontmatter";
    comp_obj_toc.is_of                 = "para";
    comp_obj_toc.is_a                  = "toc";
    comp_obj_toc.ocn                   = 0;
    comp_obj_toc.obj_cite_number       = "";
    comp_obj_toc.bullet                = false;
    switch (to!int(obj_["lev_markup_number"])) {
    case 0:
      indent=[
        "hang_position" : 0,
        "base_position" : 0,
      ];
      toc_txt_ = "{ Table of Contents }" ~ mkup.mark_internal_site_lnk ~ "../toc.fnSuffix";
      toc_txt_= munge.url_links(toc_txt_);
      comp_obj_toc.indent_hang             = indent["hang_position"];
      comp_obj_toc.indent_base             = indent["base_position"];
      comp_obj_toc.text                    = to!string(toc_txt_).strip;
      the_table_of_contents_section["seg"] ~= comp_obj_toc;
      break;
    case 1: .. case 3:
      indent=[
        "hang_position" : to!int(obj_["lev_markup_number"]),
        "base_position" : to!int(obj_["lev_markup_number"]),
      ];
      toc_txt_ = format(
        "%s",
        heading_toc_,
      );
      toc_txt_= munge.url_links(toc_txt_);
      comp_obj_toc.indent_hang             = indent["hang_position"];
      comp_obj_toc.indent_base             = indent["base_position"];
      comp_obj_toc.text                    = to!string(toc_txt_).strip;
      the_table_of_contents_section["seg"] ~= comp_obj_toc;
      break;
    case 4:
      toc_txt_ = format(
        "{ %s }%s../%s.fnSuffix",
        heading_toc_,
        mkup.mark_internal_site_lnk,
        segment_anchor_tag_that_object_belongs_to,
      );
      lev4_subtoc[segment_anchor_tag_that_object_belongs_to] = [];
      toc_txt_= munge.url_links(toc_txt_);
      indent=[
        "hang_position" : to!int(obj_["lev_markup_number"]),
        "base_position" : to!int(obj_["lev_markup_number"]),
      ];
      comp_obj_toc.indent_hang             = indent["hang_position"];
      comp_obj_toc.indent_base             = indent["base_position"];
      comp_obj_toc.text                    = to!string(toc_txt_).strip;
      the_table_of_contents_section["seg"] ~= comp_obj_toc;
      break;
    case 5: .. case 7:
      toc_txt_ = format(
        "{ %s }%s../%s.fnSuffix#%s",
        heading_toc_,
        mkup.mark_internal_site_lnk,
        segment_anchor_tag_that_object_belongs_to,
        _anchor_tag,
      );
      subtoc_txt_ = format(
        "{ %s }#%s",
        heading_toc_,
        _anchor_tag,
      );
      lev4_subtoc[segment_anchor_tag_that_object_belongs_to] ~= obj_["lev_markup_number"] ~ "~ " ~ to!string(subtoc_txt_).strip;
      toc_txt_= munge.url_links(toc_txt_);
      indent=[
        "hang_position" : to!int(obj_["lev_markup_number"]),
        "base_position" : to!int(obj_["lev_markup_number"]),
      ];
      comp_obj_toc.indent_hang             = indent["hang_position"];
      comp_obj_toc.indent_base             = indent["base_position"];
      comp_obj_toc.text                    = to!string(toc_txt_).strip;
      the_table_of_contents_section["seg"] ~= comp_obj_toc;
      break;
    default:
      break;
    }
    return the_table_of_contents_section;
  }
  invariant() {
  }
#+END_SRC

****** private:

#+name: ao_emitters_obj_inline_markup_private
#+BEGIN_SRC d
private:
#+END_SRC

******* make heading number and segment anchor tags if instructed :markup:inline:segment:anchor:tags:

#+name: ao_emitters_obj_inline_markup_heading_numbering_segment_anchor_tags
#+BEGIN_SRC d
  static string _configured_auto_heading_numbering_and_segment_anchor_tags(M,O,Ma)(
    M  munge_,
    O  obj_,
    Ma dochead_make_aa
  ) {
    debug(asserts){
      static assert(is(typeof(munge_)          == string));
      static assert(is(typeof(obj_)            == string[string]));
      static assert(is(typeof(dochead_make_aa) == string[string][string]));
    }
    if (dochead_make_aa["make"]["num_top"].length > 0) {
      if (!(match(munge_, rgx.heading_anchor_tag))) {
        static __gshared int heading_num_top_level=9;
        static __gshared int heading_num_depth=2;
        static __gshared int heading_num_0 = 0;
        static __gshared int heading_num_1 = 0;
        static __gshared int heading_num_2 = 0;
        static __gshared int heading_num_3 = 0;
        static __gshared string heading_number_auto_composite = "";
        if (heading_num_top_level==9) {
          if (dochead_make_aa["make"]["num_depth"].length > 0) {
            heading_num_depth = to!uint(dochead_make_aa["make"]["num_depth"]);
          }
          switch (dochead_make_aa["make"]["num_top"]) {
          case "A":
            break;
          case "B":
            heading_num_top_level=1;
            break;
          case "C":
            heading_num_top_level=2;
            break;
          case "D":
            heading_num_top_level=3;
            break;
          case "1":
            heading_num_top_level=4;
            break;
          case "2":
            heading_num_top_level=5;
            break;
          case "3":
            heading_num_top_level=6;
            break;
          case "4":
            heading_num_top_level=7;
            break;
          default:
            break;
          }
        }
        /+ num_depth minimum 0 (1.) default 2 (1.1.1) max 3 (1.1.1.1) implement +/
        if (heading_num_top_level > to!uint(obj_["lev_markup_number"])) {
          heading_num_0 = 0;
          heading_num_1 = 0;
          heading_num_2 = 0;
          heading_num_3 = 0;
        } else if (heading_num_top_level == to!uint(obj_["lev_markup_number"])) {
          heading_num_0 ++;
          heading_num_1 = 0;
          heading_num_2 = 0;
          heading_num_3 = 0;
        } else if (heading_num_top_level == (to!uint(obj_["lev_markup_number"]) - 1)) {
          heading_num_1 ++;
          heading_num_2 = 0;
          heading_num_3 = 0;
        } else if (heading_num_top_level == (to!uint(obj_["lev_markup_number"]) - 2)) {
          heading_num_2 ++;
          heading_num_3 = 0;
        } else if (heading_num_top_level == (to!uint(obj_["lev_markup_number"]) - 3)) {
          heading_num_3 ++;
        }
        if (heading_num_3 > 0) {
          heading_number_auto_composite =
            (heading_num_depth == 3)
            ? ( to!string(heading_num_0) ~ "." ~
                  to!string(heading_num_1) ~ "." ~
                  to!string(heading_num_2) ~ "." ~
                  to!string(heading_num_3)
                )
            : "";
        } else if (heading_num_2 > 0) {
          heading_number_auto_composite =
            ((heading_num_depth >= 2)
            && (heading_num_depth <= 3))
            ?  ( to!string(heading_num_0) ~ "." ~
                  to!string(heading_num_1) ~ "." ~
                  to!string(heading_num_2)
                )
            : "";
        } else if (heading_num_1 > 0) {
          heading_number_auto_composite =
            ((heading_num_depth >= 1)
            && (heading_num_depth <= 3))
            ? ( to!string(heading_num_0) ~ "." ~
                  to!string(heading_num_1)
                )
            : "";
        } else if (heading_num_0 > 0) {
          heading_number_auto_composite =
            ((heading_num_depth >= 0)
            && (heading_num_depth <= 3))
            ?  (to!string(heading_num_0))
            : "";
        } else {
          heading_number_auto_composite = "";
        }
        debug(heading_number_auto) {
          writeln(heading_number_auto_composite);
        }
        if (!empty(heading_number_auto_composite)) {
          munge_=replaceFirst(munge_, rgx.heading,
            "$1~$2 " ~ heading_number_auto_composite ~ ". ");
          munge_=replaceFirst(munge_, rgx.heading_marker_missing_tag,
            "$1~" ~ heading_number_auto_composite ~ " ");
        }
      }
    }
    return munge_;
  }
#+END_SRC

******** unittests

#+name: ao_emitters_obj_inline_markup_heading_numbering_segment_anchor_tags
#+BEGIN_SRC d
#+END_SRC

******* make segment anchor tags if not provided :markup:inline:segment:anchor:tags:

#+name: ao_emitters_obj_inline_markup_heading_numbering_segment_anchor_tags
#+BEGIN_SRC d

  static string _make_segment_anchor_tags_if_none_provided(M,Lv)(M munge_, Lv lev_) {
    debug(asserts){
      static assert(is(typeof(munge_) == string));
      static assert(is(typeof(lev_)   == string));
    }
    if (!(match(munge_, rgx.heading_anchor_tag))) { // if (anchor_tags_.length == 0) {
      if (match(munge_, rgx.heading_identify_anchor_tag)) {
        if (auto m = match(munge_, rgx.heading_extract_named_anchor_tag)) {
          munge_=replaceFirst(munge_, rgx.heading_marker_missing_tag,
            "$1~" ~ toLower(m.captures[1]) ~ "_"  ~ m.captures[2] ~ " ");
        } else if (auto m = match(munge_, rgx.heading_extract_unnamed_anchor_tag)) {
          munge_=replaceFirst(munge_, rgx.heading_marker_missing_tag,
            "$1~" ~ "s" ~ m.captures[1] ~ " ");
        }
      } else if (lev_ == "1") { // (if not successful) manufacture a unique anchor tag for lev=="1"
        static __gshared int heading_num_lev1 = 0;
        heading_num_lev1 ++;
        munge_=replaceFirst(munge_, rgx.heading_marker_missing_tag,
          "$1~" ~ "x" ~ to!string(heading_num_lev1) ~ " ");
      }
    }
    return munge_;
  }
#+END_SRC

******** unittests

#+name: ao_emitters_obj_inline_markup_heading_numbering_segment_anchor_tags
#+BEGIN_SRC d
  unittest {
    string txt_lev="1";
    string txt_in, txt_out;

    txt_in = "1~copyright Copyright";
    txt_out ="1~copyright Copyright";
    assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);

    txt_in = "1~ 6. Writing Copyright Licenses";
    txt_out ="1~s6 6. Writing Copyright Licenses";
    assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);

    txt_in= "1~ 1. Reinforcing trends";
    txt_out= "1~s1 1. Reinforcing trends";
    assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);

    txt_in= "1~ 11 SCIENCE AS A COMMONS";
    txt_out= "1~s11 11 SCIENCE AS A COMMONS";
    assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);

    txt_in= "1~ Chapter 1";
    txt_out="1~chapter_1 Chapter 1";
    assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);

    txt_in= "1~ Chapter 1.";
    txt_out="1~chapter_1 Chapter 1.";
    assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);

    txt_in= "1~ Chapter 1: Done";
    txt_out="1~chapter_1 Chapter 1: Done";
    assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);

    txt_in=  "1~ Chapter 11 - The Battle Over the Institutional Ecology of the Digital Environment";
    txt_out= "1~chapter_11 Chapter 11 - The Battle Over the Institutional Ecology of the Digital Environment";
    assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);

    txt_in= "1~ CHAPTER I.";
    txt_out="1~x1 CHAPTER I.";
    assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);

    txt_in= "1~ CHAPTER II.";
    txt_out="1~x2 CHAPTER II.";
    assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);
  }
#+END_SRC

****** close

#+name: ao_emitters_obj_inline_markup_close
#+BEGIN_SRC d
}
#+END_SRC

***** object attrib                                          :attributes:
****** attributes structure open, public

#+name: ao_emitters_obj_attributes
#+BEGIN_SRC d
struct ObjAttributes {
  string[string] _obj_attrib;
#+END_SRC

****** attributes structure open, public

#+name: ao_emitters_obj_attributes_public
#+BEGIN_SRC d
  string obj_attributes(Oi,OR,OH)(
    Oi obj_is_,
    OR obj_raw,
    OH _comp_obj_heading,
  )
  in {
    debug(asserts){
      static assert(is(typeof(obj_is_)           == string));
      static assert(is(typeof(obj_raw)           == string));
      static assert(is(typeof(_comp_obj_heading) == ObjGenericComposite));
    }
  }
  body {
    scope(exit) {
      destroy(obj_is_);
      destroy(obj_raw);
      destroy(_comp_obj_heading);
    }
    _obj_attrib["json"] ="{";
    switch (obj_is_) {
    case "heading":
      _obj_attrib["json"] ~= _heading(obj_raw);
      break;
    case "para":
      _obj_attrib["json"] ~= _para_and_blocks(obj_raw)
      ~ _para(obj_raw);
      break;
    case "code":
      _obj_attrib["json"] ~= _code(obj_raw);
      break;
    case "group":
      _obj_attrib["json"] ~= _para_and_blocks(obj_raw)
      ~ _group(obj_raw);
      break;
    case "block":
      _obj_attrib["json"] ~= _para_and_blocks(obj_raw)
      ~ _block(obj_raw);
      break;
    case "verse":
      _obj_attrib["json"] ~= _verse(obj_raw);
      break;
    case "quote":
      _obj_attrib["json"] ~= _quote(obj_raw);
      break;
    case "table":
      _obj_attrib["json"] ~= _table(obj_raw);
      break;
    case "comment":
      _obj_attrib["json"] ~= _comment(obj_raw);
      break;
    default:
      _obj_attrib["json"] ~= _para(obj_raw);
      break;
    }
    _obj_attrib["json"] ~=" }";
    _obj_attrib["json"]=_set_additional_values_parse_as_json(_obj_attrib["json"], obj_is_, _comp_obj_heading);
    debug(structattrib) {
      if (oa_j["is"].str() == "heading") {
        writeln(_obj_attrib["json"]);
        writeln(
          "is: ", oa_j["is"].str(),
          "; obj_cite_number: ", oa_j["obj_cite_number"].integer()
        );
      }
    }
    return _obj_attrib["json"];
  }
  invariant() {
  }
#+END_SRC

****** private

#+name: ao_emitters_obj_attributes_private
#+BEGIN_SRC d
  private:
  string _obj_attributes;
#+END_SRC

******* attrubutes
******** para and block

#+name: ao_emitters_obj_attributes_private_an_attribute
#+BEGIN_SRC d
  string _para_and_blocks(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    if (matchFirst(obj_txt_in, rgx.para_bullet)) {
      _obj_attributes =" \"bullet\": \"true\","
      ~ " \"indent_hang\": 0,"
      ~ " \"indent_base\": 0,";
    } else if (auto m = matchFirst(obj_txt_in, rgx.para_bullet_indent)) {
      _obj_attributes =" \"bullet\": \"true\","
      ~ " \"indent_hang\": " ~ to!string(m.captures[1]) ~ ","
      ~ " \"indent_base\": " ~ to!string(m.captures[1]) ~ ",";
    } else if (auto m = matchFirst(obj_txt_in, rgx.para_indent_hang)) {
      _obj_attributes =" \"bullet\": \"false\","
      ~ " \"indent_hang\": " ~ to!string(m.captures[1]) ~ ","
      ~ " \"indent_base\": " ~  to!string(m.captures[2]) ~ ",";
    } else if (auto m = matchFirst(obj_txt_in, rgx.para_indent)) {
      _obj_attributes =" \"bullet\": \"false\","
      ~ " \"indent_hang\": " ~ to!string(m.captures[1]) ~ ","
      ~ " \"indent_base\": " ~ to!string(m.captures[1]) ~ ",";
    } else {
      _obj_attributes =" \"bullet\": \"false\","
      ~ " \"indent_hang\": 0,"
      ~ " \"indent_base\": 0,";
    }
    return _obj_attributes;
  }
#+END_SRC

******** para

#+name: ao_emitters_obj_attributes_private_an_attribute
#+BEGIN_SRC d
  string _para(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    _obj_attributes = " \"use\": \"content\","
    ~ " \"of\": \"para\","
    ~ " \"is\": \"para\"";
    return _obj_attributes;
  }
  invariant() {
  }
#+END_SRC

******** heading

#+name: ao_emitters_obj_attributes_private_an_attribute
#+BEGIN_SRC d
  string _heading(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    _obj_attributes = " \"use\": \"content\","
    ~ " \"of\": \"para\","
    ~ " \"is\": \"heading\"";
    return _obj_attributes;
  }
  invariant() {
  }
#+END_SRC

******** code

#+name: ao_emitters_obj_attributes_private_an_attribute
#+BEGIN_SRC d
  string _code(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    _obj_attributes = " \"use\": \"content\","
    ~ " \"of\": \"block\","
    ~ " \"is\": \"code\"";
    return _obj_attributes;
  }
  invariant() {
  }
#+END_SRC

******** group

#+name: ao_emitters_obj_attributes_private_an_attribute
#+BEGIN_SRC d
  string _group(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    _obj_attributes = " \"use\": \"content\","
    ~ " \"of\": \"block\","
    ~ " \"is\": \"group\"";
    return _obj_attributes;
  }
  invariant() {
  }
#+END_SRC

******** block

#+name: ao_emitters_obj_attributes_private_an_attribute
#+BEGIN_SRC d
  string _block(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    _obj_attributes = " \"use\": \"content\","
    ~ " \"of\": \"block\","
    ~ " \"is\": \"block\"";
    return _obj_attributes;
  }
  invariant() {
  }
#+END_SRC

******** verse

#+name: ao_emitters_obj_attributes_private_an_attribute
#+BEGIN_SRC d
  string _verse(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    _obj_attributes = " \"use\": \"content\","
    ~ " \"of\": \"block\","
    ~ " \"is\": \"verse\"";
    return _obj_attributes;
  }
  invariant() {
  }
#+END_SRC

******** quote

#+name: ao_emitters_obj_attributes_private_an_attribute
#+BEGIN_SRC d
  string _quote(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    _obj_attributes = " \"use\": \"content\","
    ~ " \"of\": \"block\","
    ~ " \"is\": \"quote\"";
    return _obj_attributes;
  }
  invariant() {
  }
#+END_SRC

******** table

#+name: ao_emitters_obj_attributes_private_an_attribute
#+BEGIN_SRC d
  string _table(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    _obj_attributes = " \"use\": \"content\","
    ~ " \"of\": \"block\","
    ~ " \"is\": \"table\"";
    return _obj_attributes;
  }
  invariant() {
  }
#+END_SRC

******** comment

#+name: ao_emitters_obj_attributes_private_an_attribute
#+BEGIN_SRC d
  string _comment(Ot)(Ot obj_txt_in)
  in {
    debug(asserts){
      static assert(is(typeof(obj_txt_in) == string));
    }
  }
  body {
    _obj_attributes = " \"use\": \"comment\","
    ~ " \"of\": \"comment\","
    ~ " \"is\": \"comment\"";
    return _obj_attributes;
  }
  invariant() {
  }
#+END_SRC

******* set additional attribute values, parse as json

#+name: ao_emitters_obj_attributes_private_json
#+BEGIN_SRC d
  string _set_additional_values_parse_as_json(OA,Oi,OH)(
    OA _obj_attrib,
    Oi obj_is_,
    OH _comp_obj_heading,
  ) {
    debug(asserts){
      static assert(is(typeof(_obj_attrib)       == string));
      static assert(is(typeof(obj_is_)           == string));
      static assert(is(typeof(_comp_obj_heading) == ObjGenericComposite));
    }
    JSONValue oa_j = parseJSON(_obj_attrib);
    assert(
      (oa_j.type == JSON_TYPE.OBJECT)
    );
    if (obj_is_ == "heading") {
      oa_j.object["obj_cite_number"] = _comp_obj_heading.ocn;
      oa_j.object["lev_markup_number"] = _comp_obj_heading.heading_lev_markup;
      oa_j.object["lev_collapsed_number"] = _comp_obj_heading.heading_lev_collapsed;
      oa_j.object["heading_ptr"] =
        _comp_obj_heading.ptr_heading;
      oa_j.object["doc_object_ptr"] =
        _comp_obj_heading.ptr_doc_object;
    }
    oa_j.object["parent_obj_cite_number"] = _comp_obj_heading.parent_ocn;
    oa_j.object["parent_lev_markup_number"] = _comp_obj_heading.parent_lev_markup;
    _obj_attrib = oa_j.toString();
    return _obj_attrib;
  }
#+END_SRC

****** close

#+name: ao_emitters_obj_attributes_private_close
#+BEGIN_SRC d
}
#+END_SRC

**** book index                                               :book:index:
***** book index nugget hash                                :hash:nugget:

#+name: ao_emitters_book_index_nugget
#+BEGIN_SRC d
struct BookIndexNuggetHash {
  string main_term, sub_term, sub_term_bits;
  int obj_cite_number_offset, obj_cite_number_endpoint;
  string[] obj_cite_numbers;
  string[][string][string] bi;
  string[][string][string] hash_nugget;
  string[] bi_main_terms_split_arr;
  string[][string][string] bookindex_nugget_hash(BI,N)(
    BI bookindex_section,
    N  obj_cite_number
  )
  in {
    debug(asserts){
      static assert(is(typeof(bookindex_section) == string));
      static assert(is(typeof(obj_cite_number)   == int));
    }
    debug(bookindexraw) {
      if (!bookindex_section.empty) {
        writeln(
          "* [bookindex] ",
          "[", to!string(obj_cite_number), "] ", bookindex_section
        );
      }
    }
  }
  body {
    auto rgx = Rgx();
    if (!bookindex_section.empty) {
      auto bi_main_terms_split_arr =
        split(bookindex_section, rgx.bi_main_terms_split);
      foreach (bi_main_terms_content; bi_main_terms_split_arr) {
        auto bi_main_term_and_rest =
          split(bi_main_terms_content, rgx.bi_main_term_plus_rest_split);
        if (auto m = match(
          bi_main_term_and_rest[0],
          rgx.bi_term_and_obj_cite_numbers_match)
        ) {
          main_term = strip(m.captures[1]);
          obj_cite_number_offset = to!int(m.captures[2]);
          obj_cite_number_endpoint=(obj_cite_number + obj_cite_number_offset);
          obj_cite_numbers ~= (to!string(obj_cite_number) ~ "-" ~ to!string(obj_cite_number_endpoint));
        } else {
          main_term = strip(bi_main_term_and_rest[0]);
          obj_cite_numbers ~= to!string(obj_cite_number);
        }
        bi[main_term]["_a"] ~= obj_cite_numbers;
        obj_cite_numbers=null;
        if (bi_main_term_and_rest.length > 1) {
          auto bi_sub_terms_split_arr =
            split(
              bi_main_term_and_rest[1],
              rgx.bi_sub_terms_plus_obj_cite_number_offset_split
            );
          foreach (sub_terms_bits; bi_sub_terms_split_arr) {
            if (auto m = match(sub_terms_bits, rgx.bi_term_and_obj_cite_numbers_match)) {
              sub_term = strip(m.captures[1]);
              obj_cite_number_offset = to!int(m.captures[2]);
              obj_cite_number_endpoint=(obj_cite_number + obj_cite_number_offset);
              obj_cite_numbers ~= (to!string(obj_cite_number) ~ " - " ~ to!string(obj_cite_number_endpoint));
            } else {
              sub_term = strip(sub_terms_bits);
              obj_cite_numbers ~= to!string(obj_cite_number);
            }
            if (!empty(sub_term)) {
              bi[main_term][sub_term] ~= obj_cite_numbers;
            }
            obj_cite_numbers=null;
          }
        }
      }
    }
    hash_nugget = bi;
    return hash_nugget;
  }
  invariant() {
  }
}
#+END_SRC

***** book index (sort &) report indented               :report:indented:

#+name: ao_emitters_book_index_report_indented
#+BEGIN_SRC d
struct BookIndexReportIndent {
  int mkn, skn;
  auto bookindex_report_indented(BI)(
    BI bookindex_unordered_hashes
  ) {
    debug(asserts){
      static assert(is(typeof(bookindex_unordered_hashes) == string[][string][string]));
    }
    auto mainkeys=
      bookindex_unordered_hashes.byKey.array.sort().release;
    foreach (mainkey; mainkeys) {
      debug(bookindex) {
        writeln(mainkey);
      }
      auto subkeys=
        bookindex_unordered_hashes[mainkey].byKey.array.sort().release;
      foreach (subkey; subkeys) {
        debug(bookindex) {
          writeln("  ", subkey);
          writeln("    ", to!string(
            bookindex_unordered_hashes[mainkey][subkey]
          ));
        }
        ++skn;
      }
      ++mkn;
    }
  }
}
#+END_SRC

***** book index (sort &) report section                 :report:section:
****** book index struct open

#+name: ao_emitters_book_index_report_section
#+BEGIN_SRC d
struct BookIndexReportSection {
  int  mkn, skn;
  auto rgx = Rgx();
  auto munge = ObjInlineMarkupMunge();
#+END_SRC

****** bookindex write section

#+name: ao_emitters_book_index_report_section
#+BEGIN_SRC d
  auto bookindex_write_section(BI)(
    BI bookindex_unordered_hashes
  ) {
    debug(asserts){
      static assert(is(typeof(bookindex_unordered_hashes) == string[][string][string]));
    }
    auto mainkeys=bookindex_unordered_hashes.byKey.array.sort().release;
    foreach (mainkey; mainkeys) {
      write("_0_1 !{", mainkey, "}! ");
      foreach (ref_; bookindex_unordered_hashes[mainkey]["_a"]) {
        auto go = replaceAll(ref_, rgx.book_index_go, "$1");
        write(" {", ref_, "}#", go, ", ");
      }
      writeln(" \\\\");
      bookindex_unordered_hashes[mainkey].remove("_a");
      auto subkeys=
        bookindex_unordered_hashes[mainkey].byKey.array.sort().release;
      foreach (subkey; subkeys) {
        write("  ", subkey, ", ");
        foreach (ref_; bookindex_unordered_hashes[mainkey][subkey]) {
          auto go = replaceAll(ref_, rgx.book_index_go, "$1");
          write(" {", ref_, "}#", go, ", ");
        }
        writeln(" \\\\");
        ++skn;
      }
      ++mkn;
    }
  }
#+END_SRC

****** book index (sort &) build section                :report:section:

#+name: ao_emitters_book_index_report_section
#+BEGIN_SRC d
  auto bookindex_build_abstraction_section(BI,N,Ta,B)(
    BI bookindex_unordered_hashes,
    N  obj_cite_number,
    Ta segment_anchor_tag_that_object_belongs_to,
    B  opt_action_bool,
  ) {
    debug(asserts){
      static assert(is(typeof(bookindex_unordered_hashes)                == string[][string][string]));
      static assert(is(typeof(obj_cite_number)                           == int));
      static assert(is(typeof(segment_anchor_tag_that_object_belongs_to) == string));
      static assert(is(typeof(opt_action_bool)                           == bool[string]));
    }
    mixin SiSUnode;
    mixin InternalMarkup;
    auto mkup = InlineMarkup();
    string type_is;
    string lev;
    int heading_lev_markup, heading_lev_collapsed;
    string attrib;
    int[string] indent;
    auto mainkeys =
      bookindex_unordered_hashes.byKey.array.sort().release;
    ObjGenericComposite[][string] bookindex_section;
    ObjGenericComposite comp_obj_heading_, comp_obj_para;
    auto node_para_int_ = node_metadata_para_int;
    auto node_para_str_ = node_metadata_para_str;
    if ((mainkeys.length > 0)
    && (opt_action_bool["backmatter"] && opt_action_bool["section_bookindex"])) {
      string bi_tmp_seg, bi_tmp_scroll;
      string[] bi_tmp_tags;
      comp_obj_heading_                       = comp_obj_heading_.init;
      comp_obj_heading_.use                   = "backmatter";
      comp_obj_heading_.is_of                 = "para";
      comp_obj_heading_.is_a                  = "heading";
      comp_obj_heading_.text                  = "Book Index";
      comp_obj_heading_.ocn                   = 0;
      comp_obj_heading_.obj_cite_number       = "";
      comp_obj_heading_.marked_up_level       = "B";
      comp_obj_heading_.heading_lev_markup    = 1;
      comp_obj_heading_.heading_lev_collapsed = 1;
      comp_obj_heading_.parent_ocn            = 1;
      comp_obj_heading_.parent_lev_markup     = 0;
      bookindex_section["scroll"]             ~= comp_obj_heading_;
      bookindex_section["seg"]                ~= comp_obj_heading_;
      ++obj_cite_number;
      ++mkn;
      comp_obj_heading_                       = comp_obj_heading_.init;
      comp_obj_heading_.use                   = "backmatter";
      comp_obj_heading_.is_of                 = "para";
      comp_obj_heading_.is_a                  = "heading";
      comp_obj_heading_.text                  = "Index";
      comp_obj_heading_.ocn                   = 0;
      comp_obj_heading_.obj_cite_number       = "";
      comp_obj_heading_.segment_anchor_tag    = "bookindex";
      comp_obj_heading_.marked_up_level       = "1";
      comp_obj_heading_.heading_lev_markup    = 4;
      comp_obj_heading_.heading_lev_collapsed = 2;
      comp_obj_heading_.parent_ocn            = 1;
      comp_obj_heading_.parent_lev_markup     = 0;
      comp_obj_heading_.anchor_tags           = ["bookindex"];
      bookindex_section["scroll"]             ~= comp_obj_heading_;
      bookindex_section["seg"]                ~= comp_obj_heading_;
      ++obj_cite_number;
      ++mkn;
      import std.array : appender;
      auto buffer = appender!(char[])();
      string[dchar] transTable = [' ' : "_"];
      foreach (mainkey; mainkeys) {
        bi_tmp_tags = [""];
        bi_tmp_scroll = "!{" ~ mainkey ~ "}! ";
        buffer.clear();
        bi_tmp_tags ~= translate(mainkey, transTable);
        bi_tmp_seg = "!{" ~ mainkey ~ "}! ";
        foreach (ref_; bookindex_unordered_hashes[mainkey]["_a"]) {
          auto go = replaceAll(ref_, rgx.book_index_go, "$1");
          bi_tmp_scroll ~= munge.url_links(" {" ~ ref_ ~ "}#" ~ go ~ ", ");
          bi_tmp_seg ~= (segment_anchor_tag_that_object_belongs_to.empty)
          ? munge.url_links(" {" ~ ref_ ~ "}#" ~ go ~ ", ")
          : munge.url_links(" {" ~ ref_ ~ "}" ~ mkup.mark_internal_site_lnk ~ "../" ~ segment_anchor_tag_that_object_belongs_to ~ ".fnSuffix#" ~ go ~ ", ");
        }
        bi_tmp_scroll ~= " \\\\\n    ";
        bi_tmp_seg ~= " \\\\\n    ";
        bookindex_unordered_hashes[mainkey].remove("_a");
        auto subkeys =
          bookindex_unordered_hashes[mainkey].byKey.array.sort().release;
        foreach (subkey; subkeys) {
          bi_tmp_scroll ~= subkey ~ ", ";
          buffer.clear();
          bi_tmp_tags ~= translate(subkey, transTable);
          bi_tmp_seg ~= subkey ~ ", ";
          foreach (ref_; bookindex_unordered_hashes[mainkey][subkey]) {
            auto go = replaceAll(ref_, rgx.book_index_go, "$1");
            bi_tmp_scroll ~= munge.url_links(" {" ~ ref_ ~ "}#" ~ go ~ ", ");
            bi_tmp_seg ~= (segment_anchor_tag_that_object_belongs_to.empty)
            ? munge.url_links(" {" ~ ref_ ~ "}#" ~ go ~ ", ")
            : munge.url_links(" {" ~ ref_ ~ "}" ~ mkup.mark_internal_site_lnk ~ "../" ~ segment_anchor_tag_that_object_belongs_to ~ ".fnSuffix#" ~ go ~ ", ");
          }
          bi_tmp_scroll ~= " \\\\\n    ";
          bi_tmp_seg ~= " \\\\\n    ";
          ++skn;
        }
        bi_tmp_scroll = replaceFirst(bi_tmp_scroll, rgx.trailing_linebreak, "");
        bi_tmp_seg = replaceFirst(bi_tmp_seg, rgx.trailing_linebreak, "");
        comp_obj_para                       = comp_obj_para.init;
        comp_obj_para.use                   = "backmatter";
        comp_obj_para.is_of                 = "para";
        comp_obj_para.is_a                  = "bookindex";
        comp_obj_para.text                  = to!string(bi_tmp_scroll).strip;
        comp_obj_para.ocn                   = obj_cite_number;
        comp_obj_para.obj_cite_number       = (obj_cite_number==0) ? "" : to!string(obj_cite_number);
        comp_obj_para.anchor_tags           = bi_tmp_tags;
        comp_obj_para.indent_hang           = 0;
        comp_obj_para.indent_base           = 1;
        comp_obj_para.bullet                = false;
        bookindex_section["scroll"]         ~= comp_obj_para;
        comp_obj_para.text                  = to!string(bi_tmp_seg).strip;
        bookindex_section["seg"]            ~= comp_obj_para;
        ++obj_cite_number;
        ++mkn;
      }
    } else {                              // no book index, (figure out what to do here)
      comp_obj_heading_                       = comp_obj_heading_.init;
      comp_obj_heading_.text                  = "(skip) there is no Book Index";
      comp_obj_heading_.ocn                   = 0;
      comp_obj_heading_.obj_cite_number       = "";
      comp_obj_heading_.marked_up_level       = "B";
      comp_obj_heading_.heading_lev_markup    = 1;
      comp_obj_heading_.heading_lev_collapsed = 1;
      comp_obj_heading_.parent_ocn            = 1;
      comp_obj_heading_.parent_lev_markup     = 0;
      bookindex_section["scroll"]             ~= comp_obj_heading_;
      bookindex_section["seg"]                ~= comp_obj_heading_;
    }
    auto t = tuple(bookindex_section, obj_cite_number);
    return t;
  }
#+END_SRC

****** book index struct close

#+name: ao_emitters_book_index_report_section
#+BEGIN_SRC d
}
#+END_SRC

**** (end)notes section                                 :endnotes:section:

#+name: ao_emitters_endnotes
#+BEGIN_SRC d
struct NotesSection {
  string[string] object_notes;
  int previous_count;
  int mkn;
  auto rgx = Rgx();
#+END_SRC

***** gather notes for endnote section struct open

#+name: ao_emitters_endnotes
#+BEGIN_SRC d
  private auto gather_notes_for_endnote_section(
    ObjGenericComposite[] contents_am,
    string                segment_anchor_tag_that_object_belongs_to,
    int                   cntr,
  )
  in {
    // endnotes/ footnotes for
    // doc objects other than paragraphs & headings
    // various forms of grouped text
    assert((contents_am[cntr].is_a == "para")
    || (contents_am[cntr].is_a == "heading")
    || (contents_am[cntr].is_a == "group"));
    assert(cntr >= previous_count);
    previous_count=cntr;
    assert(
      match(contents_am[cntr].text,
      rgx.inline_notes_delimiter_al_regular_number_note)
    );
  }
  body {
    mixin InternalMarkup;
    auto mkup = InlineMarkup();
    auto munge = ObjInlineMarkupMunge();
    foreach(
      m;
      matchAll(
        contents_am[cntr].text,
        rgx.inline_notes_delimiter_al_regular_number_note
      )
    ) {
      debug(endnotes_build) {
        writeln(
          "{^{", m.captures[1], ".}^}" ~ mkup.mark_internal_site_lnk ~ "../", segment_anchor_tag_that_object_belongs_to, ".fnSuffix#noteref_\n  ", m.captures[1], " ",
          m.captures[2]); // sometimes need segment name (segmented html & epub)
      }
      // TODO NEXT you need anchor for segments at this point ->
      object_notes["anchor"] ~= "#note_" ~ m.captures[1] ~ "』";
      object_notes["notes"] ~= (segment_anchor_tag_that_object_belongs_to.empty)
      ? (munge.url_links(
          "{^{" ~ m.captures[1] ~ ".}^}#noteref_" ~
          m.captures[1]) ~ " " ~ m.captures[2] ~ "』"
        )
      : (munge.url_links(
          "{^{" ~ m.captures[1] ~ ".}^}" ~ mkup.mark_internal_site_lnk ~ "../" ~
          segment_anchor_tag_that_object_belongs_to ~ ".fnSuffix#noteref_" ~
          m.captures[1]) ~ " " ~ m.captures[2] ~ "』"
        );
    }
    return object_notes;
  }
#+END_SRC

***** gathered notes

#+name: ao_emitters_endnotes
#+BEGIN_SRC d
  private auto gathered_notes()
  in {
  }
  body {
    string[][string] endnotes_;
    if (object_notes.length > 1) {
      endnotes_["notes"] = (split(object_notes["notes"], rgx.break_string))[0..$-1];
      endnotes_["anchor"] = (split(object_notes["anchor"], rgx.break_string))[0..$-1];
    } else {
      endnotes_["notes"] = [];
      endnotes_["anchor"] = [];
    }
    return endnotes_;
  }
#+END_SRC

***** endnote objects

#+name: ao_emitters_endnotes
#+BEGIN_SRC d
  private auto endnote_objects(
    int            obj_cite_number,
    bool[string]   opt_action_bool,
  )
  in {
  }
  body {
    mixin SiSUnode;
    ObjGenericComposite[] the_endnotes_section;
    auto endnotes_ = gathered_notes();
    string type_is;
    string lev, lev_markup_number, lev_collapsed_number;
    string attrib;
    int[string] indent;
    ObjGenericComposite comp_obj_heading_;
    if ((endnotes_["notes"].length > 0)
    && (opt_action_bool["backmatter"] && opt_action_bool["section_endnotes"])) {
      comp_obj_heading_                       = comp_obj_heading_.init;
      comp_obj_heading_.use                   = "backmatter";
      comp_obj_heading_.is_of                 = "para";
      comp_obj_heading_.is_a                  = "heading";
      comp_obj_heading_.text                  = "Endnotes";
      comp_obj_heading_.ocn                   = 0;
      comp_obj_heading_.obj_cite_number       = "";
      comp_obj_heading_.marked_up_level       = "B";
      comp_obj_heading_.heading_lev_markup    = 1;
      comp_obj_heading_.heading_lev_collapsed = 1;
      comp_obj_heading_.parent_ocn            = 1;
      comp_obj_heading_.parent_lev_markup     = 0;
      the_endnotes_section                    ~= comp_obj_heading_;
      ++obj_cite_number;
      ++mkn;
      comp_obj_heading_                       = comp_obj_heading_.init;
      comp_obj_heading_.use                   = "backmatter";
      comp_obj_heading_.is_of                 = "para";
      comp_obj_heading_.is_a                  = "heading";
      comp_obj_heading_.text                  = "Endnotes";
      comp_obj_heading_.ocn                   = 0;
      comp_obj_heading_.obj_cite_number       = "";
      comp_obj_heading_.segment_anchor_tag    = "endnotes";
      comp_obj_heading_.marked_up_level       = "1";
      comp_obj_heading_.heading_lev_markup    = 4;
      comp_obj_heading_.heading_lev_collapsed = 2;
      comp_obj_heading_.parent_ocn            = 1;
      comp_obj_heading_.parent_lev_markup     = 0;
      comp_obj_heading_.anchor_tags           = ["endnotes"];
      the_endnotes_section                    ~= comp_obj_heading_;
      ++obj_cite_number;
      ++mkn;
    } else {
      comp_obj_heading_                       = comp_obj_heading_.init;
      comp_obj_heading_.use                   = "empty";
      comp_obj_heading_.is_of                 = "para";
      comp_obj_heading_.is_a                  = "heading";
      comp_obj_heading_.text                  = "(skip) there are no Endnotes";
      comp_obj_heading_.ocn                   = 0;
      comp_obj_heading_.obj_cite_number       = "";
      comp_obj_heading_.marked_up_level       = "B";
      comp_obj_heading_.heading_lev_markup    = 1;
      comp_obj_heading_.heading_lev_collapsed = 1;
      comp_obj_heading_.parent_ocn            = 1;
      comp_obj_heading_.parent_lev_markup     = 0;
      the_endnotes_section                    ~= comp_obj_heading_;
    }
    if (opt_action_bool["backmatter"] && opt_action_bool["section_endnotes"]) {
      ObjGenericComposite comp_obj_endnote_;
      comp_obj_endnote_                       = comp_obj_endnote_.init;
      comp_obj_endnote_.use                   = "backmatter";
      comp_obj_endnote_.is_of                 = "para";
      comp_obj_endnote_.is_a                  = "endnote";
      comp_obj_endnote_.ocn                   = 0;
      comp_obj_heading_.obj_cite_number       = "";
      comp_obj_endnote_.indent_hang           = 0;
      comp_obj_endnote_.indent_base           = 0;
      comp_obj_endnote_.bullet                = false;
      foreach (i, endnote; endnotes_["notes"]) {
        auto     m                            = (matchFirst(endnote, rgx.note_ref));
        string   notenumber                   = to!string(m.captures[1]);
        string   anchor_tag                   = "note_" ~ notenumber;
        comp_obj_endnote_.anchor_tags         ~= [ endnotes_["anchor"][i] ];
        comp_obj_endnote_.text                = endnote.strip;
        the_endnotes_section                  ~= comp_obj_endnote_;
      }
    }
    auto t = tuple(the_endnotes_section, obj_cite_number);
    return t;
  }
#+END_SRC

***** gather notes for endnote section struct close

#+name: ao_emitters_endnotes
#+BEGIN_SRC d
}
#+END_SRC

**** bibliography                                           :bibliography:
***** biblio struct open

#+name: ao_emitters_bibliography
#+BEGIN_SRC d
struct Bibliography {
#+END_SRC

***** biblio

#+name: ao_emitters_bibliography
#+BEGIN_SRC d
  public JSONValue[] _bibliography_(Bi,BJ)(
    ref Bi biblio_unsorted_incomplete,
    ref BJ bib_arr_json
  )
  in {
    debug(asserts){
      static assert(is(typeof(biblio_unsorted_incomplete) == string[]));
      static assert(is(typeof(bib_arr_json)               == JSONValue[]));
    }
 }
  body {
    JSONValue[] biblio_unsorted =
      _biblio_unsorted_complete_(biblio_unsorted_incomplete, bib_arr_json);
    JSONValue[] biblio_sorted__ = biblio_sort(biblio_unsorted);
    biblio_debug(biblio_sorted__);
    debug(biblio0) {
      writeln("---");
      writeln("unsorted incomplete: ", biblio_unsorted_incomplete.length);
      writeln("json:                ", bib_arr_json.length);
      writeln("unsorted:            ", biblio_unsorted.length);
      writeln("sorted:              ", biblio_sorted__.length);
      int cntr;
      int[7] x;
      while (cntr < x.length) {
        writeln(cntr, ": ", biblio_sorted__[cntr]["fulltitle"]);
        cntr++;
      }
    }
    return biblio_sorted__;
  }
#+END_SRC

***** biblio unsorted complete

#+name: ao_emitters_bibliography
#+BEGIN_SRC d
  final private JSONValue[] _biblio_unsorted_complete_(Bi,BJ)(
    Bi     biblio_unordered,
    ref BJ bib_arr_json
  ) {
    debug(asserts){
      static assert(is(typeof(biblio_unordered) == string[]));
      static assert(is(typeof(bib_arr_json)     == JSONValue[]));
    }
    foreach (bibent; biblio_unordered) {
      // update bib to include deemed_author, needed for:
      // sort_bibliography_array_by_deemed_author_year_title
      // either: sort on multiple fields, or; create such sort field
      JSONValue j = parseJSON(bibent);
      if (!empty(j["fulltitle"].str)) {
        if (!empty(j["author_raw"].str)) {
          j["deemed_author"]=j["author_arr"][0];
        } else if (!empty(j["editor_raw"].str)) {
          j["deemed_author"]=j["editor_arr"][0];
        }
        j["sortby_deemed_author_year_title"] = (
          j["deemed_author"].str ~
           "; " ~
           j["year"].str ~
           "; "  ~
           j["fulltitle"].str
        );
      }
      bib_arr_json ~= j;
    }
    JSONValue[] biblio_unsorted_array_of_json_objects =
      bib_arr_json.dup;
    return biblio_unsorted_array_of_json_objects;
  }
#+END_SRC

***** biblio sort

#+name: ao_emitters_bibliography
#+BEGIN_SRC d
  final private JSONValue[] biblio_sort(BJ)(BJ biblio_unordered) {
    debug(asserts){
      static assert(is(typeof(biblio_unordered) == JSONValue[]));
    }
    JSONValue[] biblio_sorted_;
    biblio_sorted_ =
      sort!((a, b){
        return ((a["sortby_deemed_author_year_title"].str) < (b["sortby_deemed_author_year_title"].str));
      })(biblio_unordered).array;
    debug(bibliosorted) {
      foreach (j; biblio_sorted_) {
        if (!empty(j["fulltitle"].str)) {
          writeln(j["sortby_deemed_author_year_title"]);
        }
      }
    }
    return biblio_sorted_;
  }
#+END_SRC

***** biblio debug

#+name: ao_emitters_bibliography
#+BEGIN_SRC d
  void biblio_debug(BJ)(BJ biblio_sorted) {
    debug(asserts){
      static assert(is(typeof(biblio_sorted) == JSONValue[]));
    }
    debug(biblio0) {
      foreach (j; biblio_sorted) {
        if (!empty(j["fulltitle"].str)) {
          writeln(j["sortby_deemed_author_year_title"]);
        }
      }
    }
  }
#+END_SRC

***** biblio struct close

#+name: ao_emitters_bibliography
#+BEGIN_SRC d
}
#+END_SRC

**** node structure metadata                     :structure:metadata:node:
***** metadata node struct open

#+name: ao_emitters_metadata
#+BEGIN_SRC d
struct NodeStructureMetadata {
  int lv, lv0, lv1, lv2, lv3, lv4, lv5, lv6, lv7;
  int obj_cite_number;
  int[string] p_; // p_ parent_
  auto rgx = Rgx();
#+END_SRC

***** TODO node metadata emitter

#+name: ao_emitters_metadata
#+BEGIN_SRC d
  ObjGenericComposite node_location_emitter(Lv,Ta,N,C,P,I)(
    Lv lev_markup_number,
    Ta segment_anchor_tag,
    N  obj_cite_number_,
    C  cntr_,
    P  ptr_,
    I  is_
  )
  in {
    debug(asserts){
      static assert(is(typeof(lev_markup_number)  == string));
      static assert(is(typeof(segment_anchor_tag) == string));
      static assert(is(typeof(obj_cite_number_)   == int));
      static assert(is(typeof(cntr_)              == int));
      static assert(is(typeof(ptr_)               == int));
      static assert(is(typeof(is_)                == string));
    }
    assert(is_ != "heading");
    assert(to!int(obj_cite_number_) >= 0);
  }
  body {
    assert(is_ != "heading"); // should not be necessary
    assert(to!int(obj_cite_number_) >= 0); // should not be necessary
    int obj_cite_number=to!int(obj_cite_number_);
    if (lv7 > State.off) {
      p_["lev_markup_number"] = DocStructMarkupHeading.h_text_4;
      p_["obj_cite_number"] = lv7;
    } else if (lv6 > State.off) {
      p_["lev_markup_number"] = DocStructMarkupHeading.h_text_3;
      p_["obj_cite_number"] = lv6;
    } else if (lv5 > State.off) {
      p_["lev_markup_number"] = DocStructMarkupHeading.h_text_2;
      p_["obj_cite_number"] = lv5;
    } else {
      p_["lev_markup_number"] = DocStructMarkupHeading.h_text_1;
      p_["obj_cite_number"] = lv4;
    }
    ObjGenericComposite comp_obj_location;
    comp_obj_location                       = comp_obj_location.init;
    comp_obj_location.is_a                  = is_;
    comp_obj_location.ocn                   = obj_cite_number_;
    comp_obj_location.segment_anchor_tag    = to!string(segment_anchor_tag);
    comp_obj_location.parent_ocn            = p_["obj_cite_number"];
    comp_obj_location.parent_lev_markup     = p_["lev_markup_number"];
    debug(node) {
      if (match(lev_markup_number, rgx.levels_numbered_headings)) {
        writeln("x ", to!string(_node));
      } else {
        writeln("- ", to!string(_node));
      }
    }
    assert(comp_obj_location.parent_lev_markup >= 4);
    assert(comp_obj_location.parent_lev_markup <= 7);
    assert(comp_obj_location.parent_ocn >= 0);
    return comp_obj_location;
  }
  invariant() {
  }
#+END_SRC

***** TODO node metadata emitter heading, (including most segnames & their pointers)

#+name: ao_emitters_metadata
#+BEGIN_SRC d
  ObjGenericComposite node_emitter_heading(T,L,Lm,Lc,Ta,N,C,P,LA,I,PSn)(
    T   _text,
    L   lev,
    Lm  lev_markup_number,
    Lc  lev_collapsed_number,
    Ta  segment_anchor_tag,
    N   obj_cite_number_,
    C   cntr_,
    P   ptr_,
    LA  lv_ancestors,
    I   is_,
    PSn html_segnames_ptr,
  )
  in {
    debug(asserts){
      static assert(is(typeof(_text)                == string));
      static assert(is(typeof(lev)                  == string));
      static assert(is(typeof(lev_markup_number)    == string));
      static assert(is(typeof(lev_collapsed_number) == string));
      static assert(is(typeof(segment_anchor_tag)   == string));
      static assert(is(typeof(obj_cite_number_)     == int));
      static assert(is(typeof(cntr_)                == int));
      static assert(is(typeof(ptr_)                 == int));
      static assert(is(typeof(lv_ancestors)         == string[]));
      static assert(is(typeof(is_)                  == string));
      static assert(is(typeof(html_segnames_ptr)    == int));
    }
    assert(is_ == "heading");
    assert(to!int(obj_cite_number_) >= 0);
    assert(
      match(lev_markup_number, rgx.levels_numbered),
      ("not a valid heading level: " ~ lev_markup_number ~ " at " ~ to!string(obj_cite_number_))
    );
    if (match(lev_markup_number, rgx.levels_numbered)) {
      if (to!int(lev_markup_number) == 0) {
        assert(to!int(obj_cite_number_) == 1);
      }
    }
  }
  body {
    int obj_cite_number = to!int(obj_cite_number_);
    switch (to!int(lev_markup_number)) {
    case 0:
      lv = DocStructMarkupHeading.h_sect_A;
      lv0 = obj_cite_number;
      lv1=0; lv2=0; lv3=0; lv4=0; lv5=0; lv6=0; lv7=0;
      p_["lev_markup_number"] = 0;
      p_["obj_cite_number"] = 0;
      break;
    case 1:
      lv = DocStructMarkupHeading.h_sect_B;
      lv1 = obj_cite_number;
      lv2=0; lv3=0; lv4=0; lv5=0; lv6=0; lv7=0;
      p_["lev_markup_number"] =
        DocStructMarkupHeading.h_sect_A;
      p_["obj_cite_number"] = lv0;
      break;
    case 2:
      lv = DocStructMarkupHeading.h_sect_C;
      lv2 = obj_cite_number;
      lv3=0; lv4=0; lv5=0; lv6=0; lv7=0;
      p_["lev_markup_number"] =
        DocStructMarkupHeading.h_sect_B;
      p_["obj_cite_number"] = lv1;
      break;
    case 3:
      lv = DocStructMarkupHeading.h_sect_D;
      lv3=obj_cite_number;
      lv4=0; lv5=0; lv6=0; lv7=0;
      p_["lev_markup_number"] =
        DocStructMarkupHeading.h_sect_C;
      p_["obj_cite_number"] = lv2;
      break;
    case 4:
      lv = DocStructMarkupHeading.h_text_1;
      lv4 = obj_cite_number;
      lv5=0; lv6=0; lv7=0;
      if (lv3 > State.off) {
        p_["lev_markup_number"] =
          DocStructMarkupHeading.h_sect_D;
        p_["obj_cite_number"] = lv3;
      } else if (lv2 > State.off) {
        p_["lev_markup_number"] =
          DocStructMarkupHeading.h_sect_C;
        p_["obj_cite_number"] = lv2;
      } else if (lv1 > State.off) {
        p_["lev_markup_number"] =
          DocStructMarkupHeading.h_sect_B;
        p_["obj_cite_number"] = lv1;
      } else {
        p_["lev_markup_number"] =
          DocStructMarkupHeading.h_sect_A;
        p_["obj_cite_number"] = lv0;
      }
      break;
    case 5:
      lv = DocStructMarkupHeading.h_text_2;
      lv5 = obj_cite_number;
      lv6=0; lv7=0;
      p_["lev_markup_number"] =
        DocStructMarkupHeading.h_text_1;
      p_["obj_cite_number"] = lv4;
      break;
    case 6:
      lv = DocStructMarkupHeading.h_text_3;
      lv6 = obj_cite_number;
      lv7=0;
      p_["lev_markup_number"] =
        DocStructMarkupHeading.h_text_2;
      p_["obj_cite_number"] = lv5;
      break;
    case 7:
      lv = DocStructMarkupHeading.h_text_4;
      lv7 = obj_cite_number;
      p_["lev_markup_number"] =
        DocStructMarkupHeading.h_text_3;
      p_["obj_cite_number"] = lv6;
      break;
    default:
      break;
    }
    ObjGenericComposite _comp_obj_heading_;
    _comp_obj_heading_                           = _comp_obj_heading_.init;
    _comp_obj_heading_.use                       = "body";
    _comp_obj_heading_.is_of                     = "para";
    _comp_obj_heading_.is_a                      = "heading";   //                   = is_; // check whether needed, constant???
    _comp_obj_heading_.text                      = to!string(_text).strip;
    _comp_obj_heading_.ocn                       = obj_cite_number_;
    _comp_obj_heading_.obj_cite_number           = (obj_cite_number==0) ? "" : to!string(obj_cite_number);
    _comp_obj_heading_.segment_anchor_tag        = to!string(segment_anchor_tag);
    _comp_obj_heading_.marked_up_level           = lev;
    _comp_obj_heading_.heading_lev_markup        = (!(lev_markup_number.empty) ? to!int(lev_markup_number) : 0);
    _comp_obj_heading_.heading_lev_collapsed     = (!(lev_collapsed_number.empty) ? to!int(lev_collapsed_number) : 0);
    _comp_obj_heading_.parent_ocn                = p_["obj_cite_number"];
    _comp_obj_heading_.parent_lev_markup         = p_["lev_markup_number"];
    _comp_obj_heading_.heading_ancestors_text    = lv_ancestors;
    _comp_obj_heading_.ptr_doc_object            = cntr_;
    _comp_obj_heading_.ptr_html_segnames         = ((lev_markup_number == "4") ? html_segnames_ptr : 0);
    _comp_obj_heading_.ptr_heading               = ptr_;
    debug(node) {
      if (match(lev_markup_number, rgx.levels_numbered_headings)) {
        writeln("* ", to!string(_node));
      }
    }
    debug(nodeheading) {
      if (match(lev_markup_number, rgx.levels_numbered_headings)) {
        writeln("* ", to!string(_node));
      }
    }
    assert(_comp_obj_heading_.parent_lev_markup <= 7);
    assert(_comp_obj_heading_.parent_ocn >= 0);
    if (match(lev_markup_number, rgx.levels_numbered_headings)) {
      assert(_comp_obj_heading_.heading_lev_markup <= 7);
      assert(_comp_obj_heading_.ocn >= 0);
      if (_comp_obj_heading_.parent_lev_markup > 0) {
        assert(_comp_obj_heading_.parent_lev_markup < _comp_obj_heading_.heading_lev_markup);
        if (_comp_obj_heading_.ocn != 0) {
          assert(_comp_obj_heading_.parent_ocn < _comp_obj_heading_.ocn);
        }
      }
      if (_comp_obj_heading_.heading_lev_markup == 0) {
        assert(_comp_obj_heading_.parent_lev_markup == DocStructMarkupHeading.h_sect_A);
      } else if  (_comp_obj_heading_.heading_lev_markup == DocStructMarkupHeading.h_sect_B) {
        assert(_comp_obj_heading_.parent_lev_markup == DocStructMarkupHeading.h_sect_A);
      } else if  (_comp_obj_heading_.heading_lev_markup == DocStructMarkupHeading.h_sect_C) {
        assert(_comp_obj_heading_.parent_lev_markup == DocStructMarkupHeading.h_sect_B);
      } else if  (_comp_obj_heading_.heading_lev_markup == DocStructMarkupHeading.h_sect_D) {
        assert(_comp_obj_heading_.parent_lev_markup == DocStructMarkupHeading.h_sect_C);
      } else if  (_comp_obj_heading_.heading_lev_markup == DocStructMarkupHeading.h_text_1) {
        assert(_comp_obj_heading_.parent_lev_markup <= DocStructMarkupHeading.h_sect_D);
      } else if  (_comp_obj_heading_.heading_lev_markup == DocStructMarkupHeading.h_text_2) {
        assert(_comp_obj_heading_.parent_lev_markup == DocStructMarkupHeading.h_text_1);
      } else if  (_comp_obj_heading_.heading_lev_markup == DocStructMarkupHeading.h_text_3) {
        assert(_comp_obj_heading_.parent_lev_markup == DocStructMarkupHeading.h_text_2);
      } else if  (_comp_obj_heading_.heading_lev_markup == DocStructMarkupHeading.h_text_4) {
        assert(_comp_obj_heading_.parent_lev_markup == DocStructMarkupHeading.h_text_3);
      } else if  (_comp_obj_heading_.heading_lev_markup == DocStructMarkupHeading.h_text_5) {
      }
    }
    return _comp_obj_heading_;
  }
  invariant() {
  }
#+END_SRC

***** metadata node struct close

#+name: ao_emitters_metadata
#+BEGIN_SRC d
}
#+END_SRC

*** function assertions                                        :assertions:
**** mixin template: assertions on markup document structure :doc_structure:

#+name: abs_functions_assertions
#+BEGIN_SRC d
auto assertions_doc_structure(O,Lv)(
  O  an_object,
  Lv lv
) {
  debug(asserts){
    static assert(is(typeof(an_object) == string[string]));
    static assert(is(typeof(lv)        == int[string]));
  }
  if (lv["h3"] > State.off) {
    assert(lv["h0"] > State.off);
    assert(lv["h1"] > State.off);
    assert(lv["h2"] > State.off);
  } else if (lv["h2"] > State.off) {
    assert(lv["h0"] > State.off);
    assert(lv["h1"] > State.off);
    assert(lv["h3"] == State.off);
  } else if (lv["h1"] > State.off) {
    assert(lv["h0"] > State.off);
    assert(lv["h2"] == State.off);
    assert(lv["h3"] == State.off);
  } else if (lv["h0"] > State.off) {
    assert(lv["h1"] == State.off);
    assert(lv["h2"] == State.off);
    assert(lv["h3"] == State.off);
  } else {
    assert(lv["h0"] == State.off);
    assert(lv["h1"] == State.off);
    assert(lv["h2"] == State.off);
    assert(lv["h3"] == State.off);
  }
  if (lv["h7"] > State.off) {
    assert(lv["h4"] > State.off);
    assert(lv["h5"] > State.off);
    assert(lv["h6"] > State.off);
  } else if (lv["h6"] > State.off) {
    assert(lv["h4"] > State.off);
    assert(lv["h5"] > State.off);
    assert(lv["h7"] == State.off);
  } else if (lv["h5"] > State.off) {
    assert(lv["h4"] > State.off);
    assert(lv["h6"] == State.off);
    assert(lv["h7"] == State.off);
  } else if (lv["h4"] > State.off) {
    assert(lv["h5"] == State.off);
    assert(lv["h6"] == State.off);
    assert(lv["h7"] == State.off);
  } else {
    assert(lv["h4"] == State.off);
    assert(lv["h5"] == State.off);
    assert(lv["h6"] == State.off);
    assert(lv["h7"] == State.off);
  }
  if (lv["h0"] == State.off) {
    assert(lv["h1"] == State.off);
    assert(lv["h2"] == State.off);
    assert(lv["h3"] == State.off);
    assert(lv["h4"] == State.off);
    assert(lv["h5"] == State.off);
    assert(lv["h6"] == State.off);
    assert(lv["h7"] == State.off);
  }
  if (lv["h1"] == State.off) {
    assert(lv["h2"] == State.off);
    assert(lv["h3"] == State.off);
  }
  if (lv["h2"] == State.off) {
    assert(lv["h3"] == State.off);
  }
  if (lv["h3"] == State.off) {
  }
  if (lv["h4"] == State.off) {
    assert(lv["h5"] == State.off);
    assert(lv["h6"] == State.off);
    assert(lv["h7"] == State.off);
  }
  if (lv["h5"] == State.off) {
    assert(lv["h6"] == State.off);
    assert(lv["h7"] == State.off);
  }
  if (lv["h6"] == State.off) {
    assert(lv["h7"] == State.off);
  }
  if (lv["h7"] == State.off) {
  }
  switch (to!string(an_object["lev"])) {
  case "A":
    if (lv["h0"] == State.off) {
      assert(lv["h1"] == State.off);
      assert(lv["h2"] == State.off);
      assert(lv["h3"] == State.off);
      assert(lv["h4"] == State.off);
      assert(lv["h5"] == State.off);
      assert(lv["h6"] == State.off);
      assert(lv["h7"] == State.off);
    } else {                       // (lv["h0"] > State.off)
      assert(lv["h0"] == State.off,"error should not enter level A a second time");
    }
    break;
  case "B":
    if (lv["h1"] == State.off) {
      assert(lv["h0"] > State.off);
      assert(lv["h2"] == State.off);
      assert(lv["h3"] == State.off);
    } else {                       // (lv["h1"] > State.off)
      assert(lv["h0"] > State.off);
      assert(lv["h1"] > State.off);
    }
    break;
  case "C":
    if (lv["h2"] == State.off) {
      assert(lv["h0"] > State.off);
      assert(lv["h1"] > State.off);
      assert(lv["h3"] == State.off);
    } else {                       // (lv["h2"] > State.off)
      assert(lv["h0"] > State.off);
      assert(lv["h1"] > State.off);
      assert(lv["h2"] > State.off);
    }
    break;
  case "D":
    if (lv["h3"] == State.off) {
      assert(lv["h0"] > State.off);
      assert(lv["h1"] > State.off);
      assert(lv["h2"] > State.off);
    } else {                      // (lv["h3"] > State.off)
      assert(lv["h0"] > State.off);
      assert(lv["h1"] > State.off);
      assert(lv["h2"] > State.off);
      assert(lv["h3"] > State.off);
    }
    break;
  case "1":
    if (lv["h4"] == State.off) {
      assert(lv["h0"] > State.off);
    } else {                      // (lv["h4"] > State.off)
      assert(lv["h0"] > State.off);
      assert(lv["h4"] > State.off);
    }
    break;
  case "2":
    if (lv["h5"] == State.off) {
      assert(lv["h0"] > State.off);
      assert(lv["h4"] > State.off);
    } else {                      // (lv["h5"] > State.off)
      assert(lv["h0"] > State.off);
      assert(lv["h4"] > State.off);
      assert(lv["h5"] > State.off);
    }
    break;
  case "3":
    if (lv["h6"] == State.off) {
      assert(lv["h0"] > State.off);
      assert(lv["h4"] > State.off);
      assert(lv["h5"] > State.off);
    } else {                      // (lv["h6"] > State.off)
      assert(lv["h0"] > State.off);
      assert(lv["h4"] > State.off);
      assert(lv["h5"] > State.off);
      assert(lv["h6"] > State.off);
    }
    break;
  case "4":
    if (lv["h7"] == State.off) {
      assert(lv["h0"] > State.off);
      assert(lv["h4"] > State.off);
      assert(lv["h5"] > State.off);
      assert(lv["h6"] > State.off);
    } else {                      // (lv["h7"] > State.off)
      assert(lv["h0"] > State.off);
      assert(lv["h4"] > State.off);
      assert(lv["h5"] > State.off);
      assert(lv["h6"] > State.off);
      assert(lv["h7"] > State.off);
    }
    break;
  default:
    break;
  }
}
#+END_SRC

**** mixin template: assertions on blocks                         :blocks:

#+name: abs_functions_assertions
#+BEGIN_SRC d
auto assertions_flag_types_block_status_none_or_closed(T)(T type) {
  debug(asserts){
    static assert(is(typeof(type) == int[string]));
  }
  assert(
    (type["code"] == TriState.off)
    || (type["code"] == TriState.closing),
    "code block status: off or closing");
  assert(
    (type["poem"] == TriState.off)
    || (type["poem"] == TriState.closing),
    "poem status: off or closing");
  assert(
    (type["table"] == TriState.off)
    || (type["table"] == TriState.closing),
    "table status: off or closing");
  assert(
    (type["group"] == TriState.off)
    || (type["group"] == TriState.closing),
    "group block status: off or closing");
  assert(
    (type["block"] == TriState.off)
    || (type["block"] == TriState.closing),
    "block status: off or closing");
}
#+END_SRC

* 2. Object Setter                                          :abstract:object:

set abstracted objects for downstream processing

** 0. ao object setter:                                   :ao_object_setter:

#+BEGIN_SRC d :tangle ../src/sdp/ao_object_setter.d
/++
  object setter:
  setting of sisu objects for downstream processing
  ao_object_setter.d
+/
template ObjectSetter() {
  /+ structs +/
  <<ao_structs_init>>
}
#+END_SRC

** 1. initialize structs                                            :struct:
*** heading attribute

#+name: ao_structs_init
#+BEGIN_SRC d
struct HeadingAttrib {
  string lev                            = "9";
  int    heading_lev_markup             = 9;
  int    heading_lev_collapsed          = 9;
  int[]  closes_lev_collapsed           = [];
  int[]  closes_lev_markup              = [];
  int    array_ptr                      = 0;
  int    heading_array_ptr_segments     = 0;
}
#+END_SRC

*** TODO composite object

#+name: ao_structs_init
#+BEGIN_SRC d
struct ObjGenericComposite {
  // size_t id;
  string                 use                          = "";
  string                 is_of                        = "";
  string                 is_a                         = "";
  string                 text                         = "";
  string                 obj_cite_number              = "";
  string[]               anchor_tags                  = [];
  string                 marked_up_level              = "9";
  int[]                  closes_lev_collapsed         = [];
  int[]                  closes_lev_markup            = [];
  int                    indent_base                  = 0;
  int                    indent_hang                  = 0;
  bool                   bullet                       = false;
  string                 syntax                       = "";
  int                    ocn                          = 0;
  string                 segment_anchor_tag           = "";
  string                 segname_prev                 = "";
  string                 segname_next                 = "";
  int                    parent_lev_markup            = 0;
  int                    parent_ocn                   = 0;
  int[]                  ancestors                    = [];
  int                    heading_lev_markup           = 9;
  int                    heading_lev_collapsed        = 9;
  int[]                  heading_closes_lev_collapsed = [];
  int[]                  heading_closes_lev_markup    = [];
  string[]               heading_ancestors_text       = [ "", "", "", "", "", "", "", "", ];
  int                    heading_array_ptr            = 0;
  int                    ptr_doc_object               = 0;
  int                    ptr_html_segnames            = 0;
  int                    ptr_heading                  = 0;
  int                    array_ptr                    = 0;
  int                    heading_array_ptr_segments   = 0;
  string[]               lev4_subtoc                  = [];
  string[string][string] node;
  int[]                  dom_markedup                 = [ 0, 0, 0, 0, 0, 0, 0, 0,];
  int[]                  dom_collapsed                = [ 0, 0, 0, 0, 0, 0, 0, 0,];
}
#+END_SRC

*** The Objects: generic composite object array

#+name: ao_structs_init
#+BEGIN_SRC d
struct TheObjects {
  ObjGenericComposite[] oca;
}
#+END_SRC