metanorma-ietf 1.0.10 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/macos.yml +3 -2
  3. data/.github/workflows/ubuntu.yml +3 -2
  4. data/.github/workflows/windows.yml +4 -3
  5. data/README.adoc +9 -0
  6. data/lib/asciidoctor/ietf/basicdoc.rng +1045 -0
  7. data/lib/asciidoctor/ietf/biblio.rng +1142 -0
  8. data/lib/asciidoctor/ietf/blocks.rb +76 -0
  9. data/lib/asciidoctor/ietf/converter.rb +211 -0
  10. data/lib/asciidoctor/ietf/front.rb +143 -0
  11. data/lib/asciidoctor/ietf/ietf.rng +882 -0
  12. data/lib/asciidoctor/ietf/isodoc.rng +514 -0
  13. data/lib/asciidoctor/ietf/isostandard.rng +860 -0
  14. data/lib/asciidoctor/ietf/reqt.rng +171 -0
  15. data/lib/asciidoctor/ietf/validate.rb +84 -0
  16. data/lib/isodoc/ietf/blocks.rb +215 -0
  17. data/lib/isodoc/ietf/cleanup.rb +220 -0
  18. data/lib/isodoc/ietf/footnotes.rb +70 -0
  19. data/lib/isodoc/ietf/front.rb +232 -0
  20. data/lib/isodoc/ietf/inline.rb +136 -0
  21. data/lib/isodoc/ietf/metadata.rb +62 -0
  22. data/lib/isodoc/ietf/references.rb +129 -0
  23. data/lib/isodoc/ietf/reqt.rb +74 -0
  24. data/lib/isodoc/ietf/rfc_convert.rb +60 -0
  25. data/lib/isodoc/ietf/section.rb +162 -0
  26. data/lib/isodoc/ietf/table.rb +43 -0
  27. data/lib/isodoc/ietf/terms.rb +65 -0
  28. data/lib/metanorma-ietf.rb +2 -1
  29. data/lib/metanorma/ietf/processor.rb +16 -37
  30. data/lib/metanorma/ietf/version.rb +1 -1
  31. data/metanorma-ietf.gemspec +3 -3
  32. metadata +36 -36
  33. data/Gemfile.lock +0 -327
  34. data/lib/asciidoctor/rfc.rb +0 -8
  35. data/lib/asciidoctor/rfc/common/base.rb +0 -544
  36. data/lib/asciidoctor/rfc/common/front.rb +0 -120
  37. data/lib/asciidoctor/rfc/common/validate.rb +0 -31
  38. data/lib/asciidoctor/rfc/v2/base.rb +0 -380
  39. data/lib/asciidoctor/rfc/v2/blocks.rb +0 -299
  40. data/lib/asciidoctor/rfc/v2/converter.rb +0 -60
  41. data/lib/asciidoctor/rfc/v2/front.rb +0 -69
  42. data/lib/asciidoctor/rfc/v2/inline_anchor.rb +0 -111
  43. data/lib/asciidoctor/rfc/v2/lists.rb +0 -135
  44. data/lib/asciidoctor/rfc/v2/table.rb +0 -116
  45. data/lib/asciidoctor/rfc/v2/validate.rng +0 -716
  46. data/lib/asciidoctor/rfc/v3/base.rb +0 -330
  47. data/lib/asciidoctor/rfc/v3/blocks.rb +0 -246
  48. data/lib/asciidoctor/rfc/v3/converter.rb +0 -62
  49. data/lib/asciidoctor/rfc/v3/front.rb +0 -122
  50. data/lib/asciidoctor/rfc/v3/inline_anchor.rb +0 -89
  51. data/lib/asciidoctor/rfc/v3/lists.rb +0 -176
  52. data/lib/asciidoctor/rfc/v3/svg.rng +0 -9081
  53. data/lib/asciidoctor/rfc/v3/table.rb +0 -65
  54. data/lib/asciidoctor/rfc/v3/validate.rng +0 -2143
@@ -0,0 +1,220 @@
1
+ module IsoDoc::Ietf
2
+ class RfcConvert < ::IsoDoc::Convert
3
+ def cleanup(docxml)
4
+ image_cleanup(docxml)
5
+ figure_cleanup(docxml)
6
+ table_cleanup(docxml)
7
+ footnote_cleanup(docxml)
8
+ sourcecode_cleanup(docxml)
9
+ annotation_cleanup(docxml)
10
+ deflist_cleanup(docxml)
11
+ bookmark_cleanup(docxml)
12
+ aside_cleanup(docxml)
13
+ docxml
14
+ end
15
+
16
+ # TODO: insert <u>
17
+
18
+ def table_footnote_cleanup(docxml)
19
+ docxml.xpath("//table[descendant::fn]").each do |t|
20
+ t.xpath(".//fn").each do |a|
21
+ t << "<aside>#{a.remove.children}</aside>"
22
+ end
23
+ end
24
+ end
25
+
26
+ def figure_footnote_cleanup(docxml)
27
+ docxml.xpath("//figure[descendant::fn]").each do |t|
28
+ t.xpath(".//fn").each do |a|
29
+ t << "<aside>#{a.remove.children}</aside>"
30
+ end
31
+ end
32
+ end
33
+
34
+ def table_cleanup(docxml)
35
+ table_footnote_cleanup(docxml)
36
+ end
37
+
38
+ def figure_cleanup(docxml)
39
+ figure_postamble(docxml)
40
+ figure_wrap_artwork(docxml)
41
+ figure_unnest(docxml)
42
+ figure_footnote_cleanup(docxml)
43
+ end
44
+
45
+ def figure_wrap_artwork(docxml)
46
+ docxml.xpath("//artwork[not(parent::figure)] | "\
47
+ "//sourcecode[not(parent::figure)]").each do |a|
48
+ a.wrap("<figure></figure>")
49
+ end
50
+ end
51
+
52
+ def figure_unnest(docxml)
53
+ docxml.xpath("//figure[descendant::figure]").each do |f|
54
+ insert = f
55
+ f.xpath(".//figure").each do |a|
56
+ insert.next = a.remove
57
+ insert = insert.next_element
58
+ end
59
+ end
60
+ end
61
+
62
+ def figure_postamble(docxml)
63
+ make_postamble(docxml)
64
+ move_postamble(docxml)
65
+ move_preamble(docxml)
66
+ end
67
+
68
+ def make_postamble(docxml)
69
+ docxml.xpath("//figure").each do |f|
70
+ a = f&.at("./artwork | ./sourcecode") || next
71
+ name = f&.at("./name")&.remove
72
+ b = a&.xpath("./preceding-sibling::*")&.remove
73
+ c = a&.xpath("./following-sibling::*")&.remove
74
+ a = a.remove
75
+ name and f << name
76
+ b.empty? or f << "<preamble>#{b.to_xml}</preamble>"
77
+ a and f << a
78
+ c.empty? or f << "<postamble>#{c.to_xml}</postamble>"
79
+ end
80
+ end
81
+
82
+ def move_postamble(docxml)
83
+ docxml.xpath("//postamble").each do |p|
84
+ insert = p.parent
85
+ p.remove.elements.each do |e|
86
+ insert.next = e
87
+ insert = insert.next_element
88
+ end
89
+ end
90
+ end
91
+
92
+ def move_preamble(docxml)
93
+ docxml.xpath("//preamble").each do |p|
94
+ insert = p.parent
95
+ p.remove.elements.each do |e|
96
+ insert.previous = e
97
+ end
98
+ end
99
+ end
100
+
101
+ def footnote_cleanup(docxml)
102
+ fn = footnote_refs_cleanup(docxml)
103
+ endnotes = make_endnotes(docxml)
104
+ docxml.xpath("//section[descendant::fn] | "\
105
+ "//abstract[descendant::fn]").each do |s|
106
+ s.xpath(".//fn").each do |f|
107
+ ref = f.at(".//ref") and ref.replace("[#{fn[ref.text]}] ")
108
+ endnotes << f.remove.children
109
+ end
110
+ end
111
+ end
112
+
113
+ def footnote_refs_cleanup(docxml)
114
+ i = 0
115
+ fn = {}
116
+ docxml.xpath("//fnref").each do |f|
117
+ unless fn[f.text]
118
+ i = i + 1
119
+ fn[f.text] = i.to_s
120
+ end
121
+ f.replace(" [#{fn[f.text]}]")
122
+ end
123
+ fn
124
+ end
125
+
126
+ def make_endnotes(docxml)
127
+ return unless docxml.at("//fn")
128
+ endnotes = docxml.at("//back") or
129
+ docxml << "<back/>" and endnotes = docxml.at("//back")
130
+ endnotes << "<section><name>Endnotes</name></section>"
131
+ endnotes = docxml.at("//back/section[last()]")
132
+ end
133
+
134
+ def image_cleanup(docxml)
135
+ docxml.xpath("//t[descendant::artwork]").each do |t|
136
+ insert = t
137
+ t.xpath(".//artwork").each_with_index do |a, i|
138
+ insert.next = a.dup
139
+ insert = insert.next
140
+ a.replace("[IMAGE #{i+1}]")
141
+ end
142
+ end
143
+ end
144
+
145
+ # for markup in pseudocode
146
+ def sourcecode_cleanup(docxml)
147
+ docxml.xpath("//sourcecode").each do |s|
148
+ s.children = s.children.to_xml.gsub(%r{<br/>\n}, "\n").
149
+ gsub(%r{\s+(<t[ >])}, "\\1").gsub(%r{</t>\s+}, "</t>")
150
+ sourcecode_remove_markup(s)
151
+ s.children = "<![CDATA[#{s.children.to_xml.sub(/\A\n+/, "")}]]>"
152
+ end
153
+ end
154
+
155
+ def sourcecode_remove_markup(s)
156
+ s.traverse do |n|
157
+ next if n.text?
158
+ next if %w(name callout annotation note sourcecode).include? n.name
159
+ if n.name == "br" then n.replace("\n")
160
+ elsif n.name == "t" then n.replace("\n\n#{n.children}")
161
+ else
162
+ n.replace(n.children)
163
+ end
164
+ end
165
+ end
166
+
167
+ def annotation_cleanup(docxml)
168
+ docxml.xpath("//reference").each do |r|
169
+ next unless r&.next_element&.name == "aside"
170
+ aside = r.next_element
171
+ aside.name = "annotation"
172
+ aside.traverse do |n|
173
+ n.name == "t" and n.replace(n.children)
174
+ end
175
+ r << aside
176
+ end
177
+ docxml.xpath("//references/aside").each { |r| r.remove }
178
+ end
179
+
180
+ def deflist_cleanup(docxml)
181
+ dt_cleanup(docxml)
182
+ dd_cleanup(docxml)
183
+ end
184
+
185
+ def dt_cleanup(docxml)
186
+ docxml.xpath("//dt").each do |d|
187
+ d&.first_element_child&.name == "bookmark" and
188
+ d["anchor"] ||= d.first_element_child["anchor"]
189
+ d.xpath(".//t").each do |t|
190
+ d["anchor"] ||= t["anchor"]
191
+ t.replace(t.children)
192
+ end
193
+ end
194
+ end
195
+
196
+ def dd_cleanup(docxml)
197
+ docxml.xpath("//dd").each do |d|
198
+ d&.first_element_child&.name == "bookmark" and
199
+ d["anchor"] ||= d.first_element_child["anchor"]
200
+ end
201
+ end
202
+
203
+ def bookmark_cleanup(docxml)
204
+ docxml.xpath("//bookmark").each do |b|
205
+ b.remove
206
+ end
207
+ end
208
+
209
+ def aside_cleanup(docxml)
210
+ docxml.xpath("//t[descendant::aside] | //table[descendant::aside] | "\
211
+ "//figure[descendant::aside]").each do |p|
212
+ insert = p
213
+ p.xpath(".//aside").each do |a|
214
+ insert.next = a.remove
215
+ insert = insert.next_element
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,70 @@
1
+ module IsoDoc::Ietf
2
+ class RfcConvert < ::IsoDoc::Convert
3
+ def footnote_parse(node, out)
4
+ return table_footnote_parse(node, out) if @in_table || @in_figure
5
+ fn = node["reference"]
6
+ out.fnref fn
7
+ make_local_footnote(node, fn, out)
8
+ end
9
+
10
+ def make_local_footnote(node, fn, out)
11
+ return if @seen_footnote.include?(fn)
12
+ @in_footnote = true
13
+ out << make_generic_footnote_text(node, fn)
14
+ @in_footnote = false
15
+ @seen_footnote << fn
16
+ end
17
+
18
+ def make_generic_footnote_text(node, fnref)
19
+ first = node.first_element_child
20
+ noko do |xml|
21
+ xml.fn do |div|
22
+ xml.t **attr_code(anchor: first ? first["id"] : nil) do |div|
23
+ div.ref fnref
24
+ first.name == "p" and first.children.each { |n| parse(n, div) }
25
+ end
26
+ first.name == "p" and
27
+ node.elements.drop(1).each { |n| parse(n, xml) } or
28
+ node.children.each { |n| parse(n, xml) }
29
+ end
30
+ end.join
31
+ end
32
+
33
+ def table_footnote_parse(node, out)
34
+ fn = node["reference"]
35
+ tid = get_table_ancestor_id(node)
36
+ make_table_footnote_link(out, tid + fn, fn)
37
+ # do not output footnote text if we have already seen it for this table
38
+ return if @seen_footnote.include?(tid + fn)
39
+ @in_footnote = true
40
+ out.fn do |a|
41
+ a << make_table_footnote_text(node, tid + fn, fn)
42
+ end
43
+ @in_footnote = false
44
+ @seen_footnote << (tid + fn)
45
+ end
46
+
47
+ def make_table_footnote_link(out, fnid, fnref)
48
+ out << " [#{fnref}]"
49
+ end
50
+
51
+ def make_table_footnote_text(node, fnid, fnref)
52
+ first = node.first_element_child
53
+ noko do |xml|
54
+ xml.t **attr_code(anchor: first ? first["id"] : nil) do |div|
55
+ div << "[#{fnref}] "
56
+ first.name == "p" and first.children.each { |n| parse(n, div) }
57
+ end
58
+ first.name == "p" and
59
+ node.elements.drop(1).each { |n| parse(n, xml) } or
60
+ node.children.each { |n| parse(n, xml) }
61
+ end.join
62
+ end
63
+
64
+ def get_table_ancestor_id(node)
65
+ table = node.ancestors("table") || node.ancestors("figure")
66
+ return UUIDTools::UUID.random_create.to_s if table.empty?
67
+ table.last["id"]
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,232 @@
1
+ require "sterile"
2
+
3
+ module IsoDoc::Ietf
4
+ class RfcConvert < ::IsoDoc::Convert
5
+ def make_front(out, isoxml)
6
+ info(isoxml, out)
7
+ out.front do |front|
8
+ title isoxml, front
9
+ seriesinfo isoxml, front
10
+ author isoxml, front
11
+ date isoxml, front
12
+ area isoxml, front
13
+ workgroup isoxml, front
14
+ keyword isoxml, front
15
+ abstract isoxml, front
16
+ note isoxml, front
17
+ boilerplate isoxml, front
18
+ end
19
+ end
20
+
21
+ def info(isoxml, out)
22
+ @meta.keywords isoxml, out
23
+ @meta.areas isoxml, out
24
+ super
25
+ end
26
+
27
+ def title(isoxml, front)
28
+ title = @meta.get[:doctitle] or return
29
+ front.title title, **attr_code(abbrev: @meta.get[:docabbrev],
30
+ ascii: @meta.get[:docascii] ||
31
+ title.transliterate)
32
+ end
33
+
34
+ def seriesinfo(isoxml, front)
35
+ rfc_seriesinfo(isoxml, front) if @meta.get[:doctype] == "Rfc"
36
+ id_seriesinfo(isoxml, front) if @meta.get[:doctype] == "Internet Draft"
37
+ end
38
+
39
+ def seriesinfo_attr(isoxml)
40
+ attr_code(value: @meta.get[:docnumber] || "",
41
+ asciiValue: @meta.get[:docnumber]&.transliterate,
42
+ status: @meta.get[:stage],
43
+ stream: isoxml&.at(ns("//bibdata/series[@type = 'stream']/"\
44
+ "title"))&.text)
45
+ end
46
+
47
+ def rfc_seriesinfo(isoxml, front)
48
+ front.seriesInfo **seriesinfo_attr(isoxml).merge({name: "RFC",
49
+ asciiName: "RFC"})
50
+ i = isoxml&.at(ns("//bibdata/series[@type = 'intended']")) and
51
+ front.seriesInfo nil,
52
+ **attr_code(name: "", status: i&.at(ns("./title"))&.text,
53
+ value: i&.at(ns("./number"))&.text || "")
54
+ end
55
+
56
+ def id_seriesinfo(isoxml, front)
57
+ front.seriesInfo nil,
58
+ **seriesinfo_attr(isoxml).merge({name: "Internet-Draft",
59
+ asciiName: "Internet-Draft"})
60
+ i = isoxml&.at(ns("//bibdata/series[@type = 'intended']/title"))&.text and
61
+ front.seriesInfo **attr_code(name: "", value: "", status: i)
62
+ end
63
+
64
+ def author(isoxml, front)
65
+ isoxml.xpath(("//xmlns:bibdata/xmlns:contributor[xmlns:role/@type = "\
66
+ "'author' or xmlns:role/@type = 'editor']")).each do |c|
67
+ role = c.at(ns("./role/@type")).text == "editor" ? "editor" : nil
68
+ c.at("./organization") and org_author(c, role, front) or
69
+ person_author(c, role, front)
70
+ end
71
+ end
72
+
73
+ def person_author_attrs(c, role)
74
+ return {} if c.nil?
75
+ full = c&.at(ns("./completename"))&.text
76
+ init = c&.at(ns("./initial"))&.text ||
77
+ c&.xpath(ns("./forename")).map { |n| n.text[0] }.join(".")
78
+ init = nil if init.empty?
79
+ ret = attr_code(role: role, fullname: full, initials: init,
80
+ surname: c&.at(ns("./surname"))&.text)
81
+ pers_author_attrs1(ret, full, init, c)
82
+ end
83
+
84
+ def pers_author_attrs1(ret, full, init, c)
85
+ full and ret.merge!(attr_code(
86
+ asciiFullname: full&.transliterate,
87
+ asciiInitials: init&.transliterate,
88
+ asciiSurname: c&.at(ns("./surname"))&.text&.transliterate))
89
+ ret
90
+ end
91
+
92
+ def person_author(c, role, front)
93
+ front.author **person_author_attrs(c.at(ns("./person/name")), role) do |a|
94
+ org = c.at(ns("./person/affiliation/organization")) and
95
+ organization(org, a, c.document.at(ns("//showOnFrontPage")))
96
+ address(c.xpath(ns(".//address")),
97
+ c.at(ns(".//phone[not(@type = 'fax')]")),
98
+ c.at(ns(".//phone[@type = 'fax']")),
99
+ c.xpath(ns(".//email")), c.xpath(ns(".//uri")), a)
100
+ end
101
+ end
102
+
103
+ def org_author(c, role, front)
104
+ front.author **attr_code(role: role) do |a|
105
+ organization(c.at(ns("./organization")), a,
106
+ c.document.at(ns("//showOnFrontPage")))
107
+ address(c.at(ns(".//address")),
108
+ c.at(ns(".//phone[not(@type = 'fax')]")),
109
+ c.at(ns(".//phone[@type = 'fax']")),
110
+ c.at(ns(".//email")), c.at(ns(".//uri")), a)
111
+ end
112
+ end
113
+
114
+ def organization(org, out, show)
115
+ name = org.at(ns("./name"))&.text
116
+ out.organization name, **attr_code(
117
+ showOnFrontPage: show&.text, ascii: name&.transliterate,
118
+ asciiAbbrev: org&.at(ns("./abbreviation"))&.transliterate,
119
+ abbrev: org.at(ns("./abbreviation")))
120
+ end
121
+
122
+ def address(addr, phone, fax, email, uri, out)
123
+ return unless addr || phone || fax || email || uri
124
+ out.address do |a|
125
+ addr and postal(addr, a)
126
+ phone and a.phone phone.text
127
+ fax and a.facsimile fax.text
128
+ email and email(email, a)
129
+ uri and a.uri uri.text
130
+ end
131
+ end
132
+
133
+ def postal(addr, out)
134
+ if line = addr.at(ns("./formattedAddress"))
135
+ line.text.split(/\n/).each do |l|
136
+ out.postalLine l, **attr_code(ascii: l.transliterate)
137
+ end
138
+ else
139
+ out.postal do |p|
140
+ postal_detailed(addr, p)
141
+ end
142
+ end
143
+ end
144
+
145
+ def postal_detailed(addr, out)
146
+ addr.xpath(ns("./street")).each do |s|
147
+ out.street s.text, **attr_code(ascii: s.text.transliterate)
148
+ end
149
+ s = addr.at(ns("./city")) and
150
+ out.city s.text, **attr_code(ascii: s.text.transliterate)
151
+ s = addr.at(ns("./state")) and
152
+ out.region s.text, **attr_code(ascii: s.text.transliterate)
153
+ s = addr.at(ns("./country")) and
154
+ out.country s.text, **attr_code(ascii: s.text.transliterate)
155
+ s = addr.at(ns("./postcode")) and
156
+ out.code s.text, **attr_code(ascii: s.text.transliterate)
157
+ end
158
+
159
+ def email(email, out)
160
+ ascii = email.text.transliterate
161
+ out.email email.text,
162
+ **attr_code(ascii: ascii == email.text ? nil : ascii )
163
+ end
164
+
165
+ def date(isoxml, front)
166
+ date = @meta.get[:publisheddate] || @meta.get[:circulateddate] || return
167
+ date.gsub!(/T.*$/, "")
168
+ attr = date_attr(date) || return
169
+ front.date **attr_code(attr)
170
+ end
171
+
172
+ def date_attr(date)
173
+ return nil if date.nil?
174
+ if date.length == 4 && date =~ /^\d\d\d\d$/ then { year: date }
175
+ elsif date =~ /^\d\d\d\d-?\d\d$/
176
+ m = /^(?<year>\d\d\d\d)-(?<month>\d\d)$/.match date
177
+ { month: Date::MONTHNAMES[(m[:month]).to_i], year: m[:year] }
178
+ else
179
+ begin
180
+ d = Date.iso8601 date
181
+ { day: d.day.to_s.gsub(/^0/, ""), year: d.year,
182
+ month: Date::MONTHNAMES[d.month] }
183
+ rescue
184
+ nil
185
+ end
186
+ end
187
+ end
188
+
189
+ def area(isoxml, front)
190
+ @meta.get[:areas].each do |w|
191
+ front.area w
192
+ end
193
+ end
194
+
195
+ def workgroup(isoxml, front)
196
+ @meta.get[:wg].each do |w|
197
+ front.workgroup w
198
+ end
199
+ end
200
+
201
+ def keyword(isoxml, front)
202
+ @meta.get[:keywords].each do |kw|
203
+ front.keyword kw
204
+ end
205
+ end
206
+
207
+ def abstract(isoxml, front)
208
+ a = isoxml.at(ns("//preface/abstract | //preface/foreword")) || return
209
+ front.abstract **attr_code(anchor: a["id"]) do |abs|
210
+ a.children.reject { |c1| %w(title note).include? c1.name }.each do |c1|
211
+ parse(c1, abs)
212
+ end
213
+ end
214
+ end
215
+
216
+ def note(isoxml, front)
217
+ a = isoxml.at(ns("//preface/abstract/note | //preface/foreword/note")) or
218
+ return
219
+ front.note **attr_code(removeInRFC: a["removeInRFC"]) do |n|
220
+ title = a.at(ns("./name")) and n.name do |t|
221
+ title.children.each { |tt| parse(tt, t) }
222
+ end
223
+ a.children.reject { |c1| c1.name == "name" }.each do |c1|
224
+ parse(c1, n)
225
+ end
226
+ end
227
+ end
228
+
229
+ def boilerplate(isoxml, front)
230
+ end
231
+ end
232
+ end