aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRalph Amissah <ralph.amissah@gmail.com>2026-04-22 13:44:39 -0400
committerRalph Amissah <ralph.amissah@gmail.com>2026-04-22 20:42:31 -0400
commit49fd1ba683dd36e3826cf0d48e950c26d5235c1c (patch)
tree3098726fea4d2724fffe3d79f2326af52385f648
parent0.19.0 (diff)
.ssp document abstraction as PEG parsable text
--show-abstraction flag to write .ssp document abstraction files - Add a new output mode that serializes the in-memory document abstraction (produced by spineAbstraction) to a human-readable, line-oriented text format (.ssp). This captures the full object model after parsing and abstraction but before output generation. - The .ssp format uses unambiguous line prefixes: @section { } - section boundaries (head/toc/body/endnotes/...) [N] type - object declaration with OCN .name: value - object properties (only non-defaults) | content - text content lines % comment - comments - New files: src/sisudoc/io_out/create_abstraction_txt.d Serializer module following the same template pattern as metadoc_show_summary.d. Walks doc.abstraction() section by section, writing metadata preamble (@meta, @make, @doc_has) then each object with its properties and text content. Output goes to {output_path}/{lang}/abstraction/{doc}.ssp - Changes to spine.d: - Add "show-abstraction" to opts initialization, getopt, and OptActions struct - Add show_abstraction to abstraction(), require_processing_files(), and meta_processing_general() so the flag triggers full document processing - Insert call at both spineAbstraction sites (parallel and serial branches), gated by show_abstraction flag, following the same pattern as show_config/show_summary/show_make - Tested against all 35 sample documents (including multilingual live-manual in 9 languages) - zero failures. Works standalone (--show-abstraction) or combined with other output flags (--show-abstraction --html --text). No effect on existing code paths when the flag is not used. Co-Authored-By: Anthropic Claude Opus 4.6 (1M context)
-rw-r--r--org/out_src_abstraction_peg_text.org314
-rw-r--r--org/spine.org21
-rw-r--r--src/sisudoc/io_out/create_abstraction_txt.d304
-rwxr-xr-xsrc/sisudoc/spine.d18
4 files changed, 657 insertions, 0 deletions
diff --git a/org/out_src_abstraction_peg_text.org b/org/out_src_abstraction_peg_text.org
new file mode 100644
index 0000000..597d8d4
--- /dev/null
+++ b/org/out_src_abstraction_peg_text.org
@@ -0,0 +1,314 @@
+-*- mode: org -*-
+#+TITLE: sisudoc spine (doc_reform) output pod source abstraction peg text
+#+DESCRIPTION: documents - structuring, publishing in multiple formats & search
+#+FILETAGS: :spine:output:source:pod:
+#+AUTHOR: Ralph Amissah
+#+EMAIL: [[mailto:ralph.amissah@gmail.com][ralph.amissah@gmail.com]]
+#+COPYRIGHT: Copyright (C) 2015 (continuously updated, current 2026) Ralph Amissah
+#+LANGUAGE: en
+#+STARTUP: content hideblocks hidestars noindent entitiespretty
+#+PROPERTY: header-args :exports code
+#+PROPERTY: header-args+ :noweb yes
+#+PROPERTY: header-args+ :results silent
+#+PROPERTY: header-args+ :cache no
+#+PROPERTY: header-args+ :padline no
+#+PROPERTY: header-args+ :mkdirp yes
+#+OPTIONS: H:3 num:nil toc:t \n:t ::t |:t ^:nil -:t f:t *:t
+
+- [[./doc-reform.org][doc-reform.org]] [[./][org/]]
+
+* (Object-Centric) Document Abstraction PEG text
+
+- Process markup document, create document abstraction
+
+** _module template_ :module:metadoc_from_src:
+
+rename source_abstraction_peg_txt.d
+
+#+HEADER: :tangle "../src/sisudoc/io_out/create_abstraction_txt.d"
+#+HEADER: :noweb yes
+#+BEGIN_SRC d
+<<doc_header_including_copyright_and_license>>
+module sisudoc.io_out.create_abstraction_txt;
+@safe:
+
+/+ ↓ write document abstraction as human-readable .ssp text file +/
+template spineAbstractionTxt() {
+ import std.conv : to;
+ import std.file;
+ import std.path;
+ import std.stdio;
+ import std.string;
+ import std.array;
+ import sisudoc.io_out.paths_output;
+
+ void spineAbstractionTxt(D)(D doc) {
+ auto doc_abstraction = doc.abstraction;
+ auto doc_matters = doc.matters;
+ string[] output;
+
+ /+ ↓ header comment +/
+ output ~= "% SiSU Document Abstraction v0.1";
+ output ~= "% Source: " ~ doc_matters.src.filename;
+ output ~= "";
+
+ /+ ↓ @meta block +/
+ output ~= "@meta {";
+ auto meta = doc_matters.conf_make_meta.meta;
+ if (meta.title_main.length > 0)
+ output ~= " title.main: " ~ meta.title_main;
+ if (meta.title_subtitle.length > 0)
+ output ~= " title.subtitle: " ~ meta.title_subtitle;
+ if (meta.title_full.length > 0)
+ output ~= " title.full: " ~ meta.title_full;
+ if (meta.title_language.length > 0)
+ output ~= " title.language: " ~ meta.title_language;
+ if (meta.creator_author.length > 0)
+ output ~= " creator.author: " ~ meta.creator_author;
+ if (meta.creator_author_surname.length > 0)
+ output ~= " creator.author_surname: " ~ meta.creator_author_surname;
+ if (meta.creator_author_surname_fn.length > 0)
+ output ~= " creator.author_surname_fn: " ~ meta.creator_author_surname_fn;
+ if (meta.creator_author_email.length > 0)
+ output ~= " creator.author_email: " ~ meta.creator_author_email;
+ if (meta.creator_illustrator.length > 0)
+ output ~= " creator.illustrator: " ~ meta.creator_illustrator;
+ if (meta.creator_translator.length > 0)
+ output ~= " creator.translator: " ~ meta.creator_translator;
+ if (meta.date_published.length > 0)
+ output ~= " date.published: " ~ meta.date_published;
+ if (meta.date_created.length > 0)
+ output ~= " date.created: " ~ meta.date_created;
+ if (meta.date_issued.length > 0)
+ output ~= " date.issued: " ~ meta.date_issued;
+ if (meta.date_available.length > 0)
+ output ~= " date.available: " ~ meta.date_available;
+ if (meta.date_modified.length > 0)
+ output ~= " date.modified: " ~ meta.date_modified;
+ if (meta.date_valid.length > 0)
+ output ~= " date.valid: " ~ meta.date_valid;
+ if (meta.rights_copyright.length > 0)
+ output ~= " rights.copyright: " ~ meta.rights_copyright;
+ if (meta.rights_license.length > 0)
+ output ~= " rights.license: " ~ meta.rights_license;
+ if (meta.classify_topic_register.length > 0)
+ output ~= " classify.topic_register: " ~ meta.classify_topic_register;
+ if (meta.classify_subject.length > 0)
+ output ~= " classify.subject: " ~ meta.classify_subject;
+ if (meta.classify_keywords.length > 0)
+ output ~= " classify.keywords: " ~ meta.classify_keywords;
+ if (meta.classify_loc.length > 0)
+ output ~= " classify.loc: " ~ meta.classify_loc;
+ if (meta.classify_dewey.length > 0)
+ output ~= " classify.dewey: " ~ meta.classify_dewey;
+ if (meta.identifier_isbn.length > 0)
+ output ~= " identifier.isbn: " ~ meta.identifier_isbn;
+ if (meta.identifier_oclc.length > 0)
+ output ~= " identifier.oclc: " ~ meta.identifier_oclc;
+ if (meta.language_document.length > 0)
+ output ~= " language.document: " ~ meta.language_document;
+ if (meta.notes_abstract.length > 0)
+ output ~= " notes.abstract: " ~ meta.notes_abstract;
+ if (meta.notes_description.length > 0)
+ output ~= " notes.description: " ~ meta.notes_description;
+ if (meta.notes_summary.length > 0)
+ output ~= " notes.summary: " ~ meta.notes_summary;
+ output ~= "}";
+ output ~= "";
+
+ /+ ↓ @make block +/
+ output ~= "@make {";
+ auto make = doc_matters.conf_make_meta.make;
+ if (make.doc_type.length > 0)
+ output ~= " doc_type: " ~ make.doc_type;
+ if (make.auto_num_top_at_level.length > 0)
+ output ~= " auto_num_top_at_level: " ~ make.auto_num_top_at_level;
+ output ~= " auto_num_top_lv: " ~ make.auto_num_top_lv.to!string;
+ output ~= " auto_num_depth: " ~ make.auto_num_depth.to!string;
+ output ~= "}";
+ output ~= "";
+
+ /+ ↓ @doc_has block +/
+ output ~= "@doc_has {";
+ output ~= " inline_links: " ~ doc_matters.has.inline_links.to!string;
+ output ~= " inline_notes_reg: " ~ doc_matters.has.inline_notes_reg.to!string;
+ output ~= " inline_notes_star: " ~ doc_matters.has.inline_notes_star.to!string;
+ output ~= " tables: " ~ doc_matters.has.tables.to!string;
+ output ~= " codeblocks: " ~ doc_matters.has.codeblocks.to!string;
+ output ~= " images: " ~ doc_matters.has.images.to!string;
+ output ~= " poems: " ~ doc_matters.has.poems.to!string;
+ output ~= " groups: " ~ doc_matters.has.groups.to!string;
+ output ~= " blocks: " ~ doc_matters.has.blocks.to!string;
+ output ~= " quotes: " ~ doc_matters.has.quotes.to!string;
+ output ~= "}";
+ output ~= "";
+
+ /+ ↓ document sections +/
+ string[] section_order = ["head", "toc", "body", "endnotes",
+ "glossary", "bibliography", "bookindex", "blurb"];
+
+ foreach (section; section_order) {
+ if (section !in doc_abstraction) continue;
+ auto section_objs = doc_abstraction[section];
+ if (section_objs.length == 0) continue;
+
+ output ~= "@" ~ section ~ " {";
+ output ~= "";
+
+ foreach (obj; section_objs) {
+ /+ ↓ object declaration line +/
+ string obj_decl = "[" ~ obj.metainfo.ocn.to!string ~ "] ";
+
+ if (obj.metainfo.is_a == "heading") {
+ string lev = obj.metainfo.marked_up_level;
+ obj_decl ~= "heading :" ~ lev;
+ if (obj.metainfo.identifier.length > 0) {
+ obj_decl ~= " " ~ obj.metainfo.identifier;
+ }
+ } else {
+ obj_decl ~= obj.metainfo.is_a;
+ }
+ output ~= obj_decl;
+
+ /+ ↓ properties (only non-default values) +/
+ if (obj.metainfo.is_of_part.length > 0)
+ output ~= ".part: " ~ obj.metainfo.is_of_part;
+ if (obj.metainfo.is_of_section.length > 0 && obj.metainfo.is_of_section != section)
+ output ~= ".section: " ~ obj.metainfo.is_of_section;
+ if (obj.metainfo.parent_ocn != 0)
+ output ~= ".parent: " ~ obj.metainfo.parent_ocn.to!string;
+ if (obj.metainfo.last_descendant_ocn != 0)
+ output ~= ".last_descendant: " ~ obj.metainfo.last_descendant_ocn.to!string;
+
+ /+ ↓ ancestors (only if non-zero) +/
+ bool has_ancestors = false;
+ foreach (a; obj.metainfo.markedup_ancestors) {
+ if (a != 0) { has_ancestors = true; break; }
+ }
+ if (has_ancestors) {
+ string anc;
+ foreach (i, a; obj.metainfo.markedup_ancestors) {
+ if (i > 0) anc ~= " ";
+ anc ~= a.to!string;
+ }
+ output ~= ".ancestors: " ~ anc;
+ }
+
+ if (obj.metainfo.dummy_heading)
+ output ~= ".dummy: true";
+ if (obj.metainfo.object_number_off)
+ output ~= ".ocn_off: true";
+
+ /+ ↓ text attributes +/
+ if (obj.attrib.indent_base != 0 || obj.attrib.indent_hang != 0)
+ output ~= ".indent: " ~ obj.attrib.indent_base.to!string
+ ~ " " ~ obj.attrib.indent_hang.to!string;
+ if (obj.attrib.bullet)
+ output ~= ".bullet: true";
+ if (obj.attrib.language.length > 0)
+ output ~= ".lang: " ~ obj.attrib.language;
+
+ /+ ↓ has flags +/
+ {
+ string[] has_flags;
+ if (obj.has.inline_links) has_flags ~= "links";
+ if (obj.has.inline_notes_reg) has_flags ~= "notes_reg";
+ if (obj.has.inline_notes_star) has_flags ~= "notes_star";
+ if (obj.has.images) has_flags ~= "images";
+ if (has_flags.length > 0)
+ output ~= ".has: " ~ has_flags.join(" ");
+ }
+
+ /+ ↓ table properties +/
+ if (obj.metainfo.is_a == "table" && obj.table.number_of_columns > 0) {
+ output ~= ".table_cols: " ~ obj.table.number_of_columns.to!string;
+ if (obj.table.column_widths.length > 0) {
+ string[] ws;
+ foreach (w; obj.table.column_widths) ws ~= w.to!string;
+ output ~= ".table_widths: " ~ ws.join(" ");
+ }
+ if (obj.table.heading)
+ output ~= ".table_header: true";
+ }
+
+ /+ ↓ code block properties +/
+ if (obj.metainfo.is_a == "code") {
+ if (obj.code_block.syntax.length > 0)
+ output ~= ".code_syntax: " ~ obj.code_block.syntax;
+ if (obj.code_block.linenumbers)
+ output ~= ".code_linenumbers: true";
+ }
+
+ /+ ↓ tag properties +/
+ if (obj.tags.in_segment_html.length > 0)
+ output ~= ".segment: " ~ obj.tags.in_segment_html;
+ if (obj.tags.anchor_tag_html.length > 0 && obj.tags.anchor_tag_html != obj.tags.in_segment_html)
+ output ~= ".anchor: " ~ obj.tags.anchor_tag_html;
+ if (obj.tags.segname_prev.length > 0)
+ output ~= ".segment_prev: " ~ obj.tags.segname_prev;
+ if (obj.tags.segname_next.length > 0)
+ output ~= ".segment_next: " ~ obj.tags.segname_next;
+
+ /+ ↓ text content +/
+ if (obj.text.length > 0) {
+ foreach (line; obj.text.split("\n")) {
+ output ~= "| " ~ line;
+ }
+ }
+
+ output ~= "";
+ }
+
+ output ~= "}";
+ output ~= "";
+ }
+
+ /+ ↓ write to file +/
+ auto out_pth = spineOutPaths!()(doc_matters.output_path, doc_matters.src.language);
+ string base_dir = "abstraction";
+ string base_pth = ((out_pth.output_base.chainPath(base_dir)).asNormalizedPath).array;
+ try {
+ if (!exists(base_pth)) {
+ base_pth.mkdirRecurse;
+ }
+ } catch (Exception ex) {
+ }
+ string out_file = ((base_pth.chainPath(
+ doc_matters.src.doc_uid_out ~ ".ssp")).asNormalizedPath).array;
+ if (doc_matters.opt.action.vox_gt_1) {
+ writeln(" ", out_file);
+ }
+ auto f = File(out_file, "w");
+ foreach (line; output) {
+ f.writeln(line);
+ }
+ }
+}
+#+END_SRC
+
+* org includes
+** project version
+
+#+NAME: spine_version
+#+HEADER: :noweb yes
+#+BEGIN_SRC emacs-lisp
+<<./sisudoc_spine_version_info_and_doc_header_including_copyright_and_license.org:spine_project_version()>>
+#+END_SRC
+
+** year
+
+#+NAME: year
+#+HEADER: :noweb yes
+#+BEGIN_SRC emacs-lisp
+<<./sisudoc_spine_version_info_and_doc_header_including_copyright_and_license.org:year()>>
+#+END_SRC
+
+** document header including copyright & license
+
+#+NAME: doc_header_including_copyright_and_license
+#+HEADER: :noweb yes
+#+BEGIN_SRC emacs-lisp
+<<./sisudoc_spine_version_info_and_doc_header_including_copyright_and_license.org:spine_doc_header_including_copyright_and_license()>>
+#+END_SRC
+
+* __END__
diff --git a/org/spine.org b/org/spine.org
index 40a89e9..1eef0a3 100644
--- a/org/spine.org
+++ b/org/spine.org
@@ -341,6 +341,7 @@ bool[string] opts = [
"pdf-init" : false,
"pod" : false,
"serial" : false,
+ "show-abstraction" : false,
"show-config" : false,
"show-curate" : false,
"show-curate-authors" : false,
@@ -476,6 +477,7 @@ auto helpInfo = getopt(args,
"section-toc", "table of contents (default)", &opts["section_toc"],
"serial", "serial processing", &opts["serial"],
"skip-output", "skip output", &opts["skip-output"],
+ "show-abstraction", "show document abstraction (write .ssp file)", &opts["show-abstraction"],
"show-config", "show config", &opts["show-config"],
"show-curate", "show curate", &opts["show-curate"],
"show-curate-authors", "show curate authors", &opts["show-curate-authors"],
@@ -694,6 +696,9 @@ struct OptActions {
@trusted bool show_config() {
return opts["show-config"];
}
+ @trusted bool show_abstraction() {
+ return opts["show-abstraction"];
+ }
@trusted bool show_curate() {
return opts["show-curate"];
}
@@ -942,6 +947,7 @@ struct OptActions {
@trusted bool abstraction() {
return (
opts["abstraction"]
+ || show_abstraction
|| concordance
|| source_or_pod
|| curate
@@ -968,6 +974,7 @@ struct OptActions {
|| latex
|| odt
|| manifest
+ || show_abstraction
|| show_make
|| show_metadata
|| show_summary
@@ -982,6 +989,7 @@ struct OptActions {
@trusted bool meta_processing_general() {
return (
opts["abstraction"]
+ || show_abstraction
|| curate
|| html
|| epub
@@ -1535,6 +1543,7 @@ if ((doc.matters.opt.action.debug_do)
<<spine_each_file_do_debugs_checkdoc_1>>
<<spine_each_file_do_debugs_checkdoc_2>>
<<spine_each_file_do_debugs_checkdoc_3>>
+<<spine_each_file_show_abstraction>>
<<spine_each_file_do_debugs_checkdoc_4>>
<<spine_each_file_do_debugs_checkdoc_5>>
#+END_SRC
@@ -1586,6 +1595,18 @@ if (doc.matters.opt.action.show_config) {
}
#+END_SRC
+***** show abstraction (PEG)
+--show-abstraction
+
+#+NAME: spine_each_file_show_abstraction
+#+BEGIN_SRC d
+/+ ↓ document abstraction text representation +/
+if (doc.matters.opt.action.show_abstraction) {
+ import sisudoc.io_out.create_abstraction_txt;
+ spineAbstractionTxt!()(doc);
+}
+#+END_SRC
+
***** abstraction curate :abstraction:curate:
- abstraction curate
diff --git a/src/sisudoc/io_out/create_abstraction_txt.d b/src/sisudoc/io_out/create_abstraction_txt.d
new file mode 100644
index 0000000..a1e8f12
--- /dev/null
+++ b/src/sisudoc/io_out/create_abstraction_txt.d
@@ -0,0 +1,304 @@
+/+
+- Name: SisuDoc Spine, Doc Reform [a part of]
+ - Description: documents, structuring, processing, publishing, search
+ - static content generator
+
+ - Author: Ralph Amissah
+ [ralph.amissah@gmail.com]
+
+ - Copyright: (C) 2015 (continuously updated, current 2026) Ralph Amissah, All Rights Reserved.
+
+ - License: AGPL 3 or later:
+
+ Spine (SiSU), a framework for document structuring, publishing and
+ search
+
+ Copyright (C) Ralph Amissah
+
+ This program is free software: you can redistribute it and/or modify it
+ under the terms of the GNU AFERO General Public License as published by the
+ Free Software Foundation, either version 3 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program. If not, see [https://www.gnu.org/licenses/].
+
+ If you have Internet connection, the latest version of the AGPL should be
+ available at these locations:
+ [https://www.fsf.org/licensing/licenses/agpl.html]
+ [https://www.gnu.org/licenses/agpl.html]
+
+ - Spine (by Doc Reform, related to SiSU) uses standard:
+ - docReform markup syntax
+ - standard SiSU markup syntax with modified headers and minor modifications
+ - docReform object numbering
+ - standard SiSU object citation numbering & system
+
+ - Homepages:
+ [https://www.sisudoc.org]
+ [https://www.doc-reform.org]
+
+ - Git
+ [https://git.sisudoc.org/]
+
++/
+module sisudoc.io_out.create_abstraction_txt;
+@safe:
+
+/+ ↓ write document abstraction as human-readable .ssp text file +/
+template spineAbstractionTxt() {
+ import std.conv : to;
+ import std.file;
+ import std.path;
+ import std.stdio;
+ import std.string;
+ import std.array;
+ import sisudoc.io_out.paths_output;
+
+ void spineAbstractionTxt(D)(D doc) {
+ auto doc_abstraction = doc.abstraction;
+ auto doc_matters = doc.matters;
+ string[] output;
+
+ /+ ↓ header comment +/
+ output ~= "% SiSU Document Abstraction v0.1";
+ output ~= "% Source: " ~ doc_matters.src.filename;
+ output ~= "";
+
+ /+ ↓ @meta block +/
+ output ~= "@meta {";
+ auto meta = doc_matters.conf_make_meta.meta;
+ if (meta.title_main.length > 0)
+ output ~= " title.main: " ~ meta.title_main;
+ if (meta.title_subtitle.length > 0)
+ output ~= " title.subtitle: " ~ meta.title_subtitle;
+ if (meta.title_full.length > 0)
+ output ~= " title.full: " ~ meta.title_full;
+ if (meta.title_language.length > 0)
+ output ~= " title.language: " ~ meta.title_language;
+ if (meta.creator_author.length > 0)
+ output ~= " creator.author: " ~ meta.creator_author;
+ if (meta.creator_author_surname.length > 0)
+ output ~= " creator.author_surname: " ~ meta.creator_author_surname;
+ if (meta.creator_author_surname_fn.length > 0)
+ output ~= " creator.author_surname_fn: " ~ meta.creator_author_surname_fn;
+ if (meta.creator_author_email.length > 0)
+ output ~= " creator.author_email: " ~ meta.creator_author_email;
+ if (meta.creator_illustrator.length > 0)
+ output ~= " creator.illustrator: " ~ meta.creator_illustrator;
+ if (meta.creator_translator.length > 0)
+ output ~= " creator.translator: " ~ meta.creator_translator;
+ if (meta.date_published.length > 0)
+ output ~= " date.published: " ~ meta.date_published;
+ if (meta.date_created.length > 0)
+ output ~= " date.created: " ~ meta.date_created;
+ if (meta.date_issued.length > 0)
+ output ~= " date.issued: " ~ meta.date_issued;
+ if (meta.date_available.length > 0)
+ output ~= " date.available: " ~ meta.date_available;
+ if (meta.date_modified.length > 0)
+ output ~= " date.modified: " ~ meta.date_modified;
+ if (meta.date_valid.length > 0)
+ output ~= " date.valid: " ~ meta.date_valid;
+ if (meta.rights_copyright.length > 0)
+ output ~= " rights.copyright: " ~ meta.rights_copyright;
+ if (meta.rights_license.length > 0)
+ output ~= " rights.license: " ~ meta.rights_license;
+ if (meta.classify_topic_register.length > 0)
+ output ~= " classify.topic_register: " ~ meta.classify_topic_register;
+ if (meta.classify_subject.length > 0)
+ output ~= " classify.subject: " ~ meta.classify_subject;
+ if (meta.classify_keywords.length > 0)
+ output ~= " classify.keywords: " ~ meta.classify_keywords;
+ if (meta.classify_loc.length > 0)
+ output ~= " classify.loc: " ~ meta.classify_loc;
+ if (meta.classify_dewey.length > 0)
+ output ~= " classify.dewey: " ~ meta.classify_dewey;
+ if (meta.identifier_isbn.length > 0)
+ output ~= " identifier.isbn: " ~ meta.identifier_isbn;
+ if (meta.identifier_oclc.length > 0)
+ output ~= " identifier.oclc: " ~ meta.identifier_oclc;
+ if (meta.language_document.length > 0)
+ output ~= " language.document: " ~ meta.language_document;
+ if (meta.notes_abstract.length > 0)
+ output ~= " notes.abstract: " ~ meta.notes_abstract;
+ if (meta.notes_description.length > 0)
+ output ~= " notes.description: " ~ meta.notes_description;
+ if (meta.notes_summary.length > 0)
+ output ~= " notes.summary: " ~ meta.notes_summary;
+ output ~= "}";
+ output ~= "";
+
+ /+ ↓ @make block +/
+ output ~= "@make {";
+ auto make = doc_matters.conf_make_meta.make;
+ if (make.doc_type.length > 0)
+ output ~= " doc_type: " ~ make.doc_type;
+ if (make.auto_num_top_at_level.length > 0)
+ output ~= " auto_num_top_at_level: " ~ make.auto_num_top_at_level;
+ output ~= " auto_num_top_lv: " ~ make.auto_num_top_lv.to!string;
+ output ~= " auto_num_depth: " ~ make.auto_num_depth.to!string;
+ output ~= "}";
+ output ~= "";
+
+ /+ ↓ @doc_has block +/
+ output ~= "@doc_has {";
+ output ~= " inline_links: " ~ doc_matters.has.inline_links.to!string;
+ output ~= " inline_notes_reg: " ~ doc_matters.has.inline_notes_reg.to!string;
+ output ~= " inline_notes_star: " ~ doc_matters.has.inline_notes_star.to!string;
+ output ~= " tables: " ~ doc_matters.has.tables.to!string;
+ output ~= " codeblocks: " ~ doc_matters.has.codeblocks.to!string;
+ output ~= " images: " ~ doc_matters.has.images.to!string;
+ output ~= " poems: " ~ doc_matters.has.poems.to!string;
+ output ~= " groups: " ~ doc_matters.has.groups.to!string;
+ output ~= " blocks: " ~ doc_matters.has.blocks.to!string;
+ output ~= " quotes: " ~ doc_matters.has.quotes.to!string;
+ output ~= "}";
+ output ~= "";
+
+ /+ ↓ document sections +/
+ string[] section_order = ["head", "toc", "body", "endnotes",
+ "glossary", "bibliography", "bookindex", "blurb"];
+
+ foreach (section; section_order) {
+ if (section !in doc_abstraction) continue;
+ auto section_objs = doc_abstraction[section];
+ if (section_objs.length == 0) continue;
+
+ output ~= "@" ~ section ~ " {";
+ output ~= "";
+
+ foreach (obj; section_objs) {
+ /+ ↓ object declaration line +/
+ string obj_decl = "[" ~ obj.metainfo.ocn.to!string ~ "] ";
+
+ if (obj.metainfo.is_a == "heading") {
+ string lev = obj.metainfo.marked_up_level;
+ obj_decl ~= "heading :" ~ lev;
+ if (obj.metainfo.identifier.length > 0) {
+ obj_decl ~= " " ~ obj.metainfo.identifier;
+ }
+ } else {
+ obj_decl ~= obj.metainfo.is_a;
+ }
+ output ~= obj_decl;
+
+ /+ ↓ properties (only non-default values) +/
+ if (obj.metainfo.is_of_part.length > 0)
+ output ~= ".part: " ~ obj.metainfo.is_of_part;
+ if (obj.metainfo.is_of_section.length > 0 && obj.metainfo.is_of_section != section)
+ output ~= ".section: " ~ obj.metainfo.is_of_section;
+ if (obj.metainfo.parent_ocn != 0)
+ output ~= ".parent: " ~ obj.metainfo.parent_ocn.to!string;
+ if (obj.metainfo.last_descendant_ocn != 0)
+ output ~= ".last_descendant: " ~ obj.metainfo.last_descendant_ocn.to!string;
+
+ /+ ↓ ancestors (only if non-zero) +/
+ bool has_ancestors = false;
+ foreach (a; obj.metainfo.markedup_ancestors) {
+ if (a != 0) { has_ancestors = true; break; }
+ }
+ if (has_ancestors) {
+ string anc;
+ foreach (i, a; obj.metainfo.markedup_ancestors) {
+ if (i > 0) anc ~= " ";
+ anc ~= a.to!string;
+ }
+ output ~= ".ancestors: " ~ anc;
+ }
+
+ if (obj.metainfo.dummy_heading)
+ output ~= ".dummy: true";
+ if (obj.metainfo.object_number_off)
+ output ~= ".ocn_off: true";
+
+ /+ ↓ text attributes +/
+ if (obj.attrib.indent_base != 0 || obj.attrib.indent_hang != 0)
+ output ~= ".indent: " ~ obj.attrib.indent_base.to!string
+ ~ " " ~ obj.attrib.indent_hang.to!string;
+ if (obj.attrib.bullet)
+ output ~= ".bullet: true";
+ if (obj.attrib.language.length > 0)
+ output ~= ".lang: " ~ obj.attrib.language;
+
+ /+ ↓ has flags +/
+ {
+ string[] has_flags;
+ if (obj.has.inline_links) has_flags ~= "links";
+ if (obj.has.inline_notes_reg) has_flags ~= "notes_reg";
+ if (obj.has.inline_notes_star) has_flags ~= "notes_star";
+ if (obj.has.images) has_flags ~= "images";
+ if (has_flags.length > 0)
+ output ~= ".has: " ~ has_flags.join(" ");
+ }
+
+ /+ ↓ table properties +/
+ if (obj.metainfo.is_a == "table" && obj.table.number_of_columns > 0) {
+ output ~= ".table_cols: " ~ obj.table.number_of_columns.to!string;
+ if (obj.table.column_widths.length > 0) {
+ string[] ws;
+ foreach (w; obj.table.column_widths) ws ~= w.to!string;
+ output ~= ".table_widths: " ~ ws.join(" ");
+ }
+ if (obj.table.heading)
+ output ~= ".table_header: true";
+ }
+
+ /+ ↓ code block properties +/
+ if (obj.metainfo.is_a == "code") {
+ if (obj.code_block.syntax.length > 0)
+ output ~= ".code_syntax: " ~ obj.code_block.syntax;
+ if (obj.code_block.linenumbers)
+ output ~= ".code_linenumbers: true";
+ }
+
+ /+ ↓ tag properties +/
+ if (obj.tags.in_segment_html.length > 0)
+ output ~= ".segment: " ~ obj.tags.in_segment_html;
+ if (obj.tags.anchor_tag_html.length > 0 && obj.tags.anchor_tag_html != obj.tags.in_segment_html)
+ output ~= ".anchor: " ~ obj.tags.anchor_tag_html;
+ if (obj.tags.segname_prev.length > 0)
+ output ~= ".segment_prev: " ~ obj.tags.segname_prev;
+ if (obj.tags.segname_next.length > 0)
+ output ~= ".segment_next: " ~ obj.tags.segname_next;
+
+ /+ ↓ text content +/
+ if (obj.text.length > 0) {
+ foreach (line; obj.text.split("\n")) {
+ output ~= "| " ~ line;
+ }
+ }
+
+ output ~= "";
+ }
+
+ output ~= "}";
+ output ~= "";
+ }
+
+ /+ ↓ write to file +/
+ auto out_pth = spineOutPaths!()(doc_matters.output_path, doc_matters.src.language);
+ string base_dir = "abstraction";
+ string base_pth = ((out_pth.output_base.chainPath(base_dir)).asNormalizedPath).array;
+ try {
+ if (!exists(base_pth)) {
+ base_pth.mkdirRecurse;
+ }
+ } catch (Exception ex) {
+ }
+ string out_file = ((base_pth.chainPath(
+ doc_matters.src.doc_uid_out ~ ".ssp")).asNormalizedPath).array;
+ if (doc_matters.opt.action.vox_gt_1) {
+ writeln(" ", out_file);
+ }
+ auto f = File(out_file, "w");
+ foreach (line; output) {
+ f.writeln(line);
+ }
+ }
+}
diff --git a/src/sisudoc/spine.d b/src/sisudoc/spine.d
index fcde22e..ceb9d6c 100755
--- a/src/sisudoc/spine.d
+++ b/src/sisudoc/spine.d
@@ -166,6 +166,7 @@ string program_name = "spine";
"pdf-init" : false,
"pod" : false,
"serial" : false,
+ "show-abstraction" : false,
"show-config" : false,
"show-curate" : false,
"show-curate-authors" : false,
@@ -287,6 +288,7 @@ string program_name = "spine";
"section-toc", "table of contents (default)", &opts["section_toc"],
"serial", "serial processing", &opts["serial"],
"skip-output", "skip output", &opts["skip-output"],
+ "show-abstraction", "show document abstraction (write .ssp file)", &opts["show-abstraction"],
"show-config", "show config", &opts["show-config"],
"show-curate", "show curate", &opts["show-curate"],
"show-curate-authors", "show curate authors", &opts["show-curate-authors"],
@@ -498,6 +500,9 @@ string program_name = "spine";
@trusted bool show_config() {
return opts["show-config"];
}
+ @trusted bool show_abstraction() {
+ return opts["show-abstraction"];
+ }
@trusted bool show_curate() {
return opts["show-curate"];
}
@@ -746,6 +751,7 @@ string program_name = "spine";
@trusted bool abstraction() {
return (
opts["abstraction"]
+ || show_abstraction
|| concordance
|| source_or_pod
|| curate
@@ -772,6 +778,7 @@ string program_name = "spine";
|| latex
|| odt
|| manifest
+ || show_abstraction
|| show_make
|| show_metadata
|| show_summary
@@ -786,6 +793,7 @@ string program_name = "spine";
@trusted bool meta_processing_general() {
return (
opts["abstraction"]
+ || show_abstraction
|| curate
|| html
|| epub
@@ -1303,6 +1311,11 @@ string program_name = "spine";
import sisudoc.meta.metadoc_show_config;
spineShowConfig!()(doc.matters);
}
+ /+ ↓ document abstraction text representation +/
+ if (doc.matters.opt.action.show_abstraction) {
+ import sisudoc.io_out.create_abstraction_txt;
+ spineAbstractionTxt!()(doc);
+ }
if (doc.matters.opt.action.curate) {
auto _hvst = spineMetaDocCurate!()(doc.matters, hvst);
if (
@@ -1402,6 +1415,11 @@ string program_name = "spine";
import sisudoc.meta.metadoc_show_config;
spineShowConfig!()(doc.matters);
}
+ /+ ↓ document abstraction text representation +/
+ if (doc.matters.opt.action.show_abstraction) {
+ import sisudoc.io_out.create_abstraction_txt;
+ spineAbstractionTxt!()(doc);
+ }
if (doc.matters.opt.action.curate) {
auto _hvst = spineMetaDocCurate!()(doc.matters, hvst);
if (