metanorma-ietf 1.0.10 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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