metanorma-bsi 0.0.1

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 (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.hound.yml +3 -0
  4. data/.rubocop.yml +14 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE +25 -0
  8. data/README.adoc +199 -0
  9. data/Rakefile +8 -0
  10. data/bin/rspec +18 -0
  11. data/lib/asciidoctor/bsi/basicdoc.rng +1131 -0
  12. data/lib/asciidoctor/bsi/biblio.rng +1235 -0
  13. data/lib/asciidoctor/bsi/bsi.rng +120 -0
  14. data/lib/asciidoctor/bsi/bsi_intro_en.xml +105 -0
  15. data/lib/asciidoctor/bsi/cleanup.rb +78 -0
  16. data/lib/asciidoctor/bsi/cleanup_ref.rb +183 -0
  17. data/lib/asciidoctor/bsi/converter.rb +83 -0
  18. data/lib/asciidoctor/bsi/front.rb +67 -0
  19. data/lib/asciidoctor/bsi/isodoc.rng +1870 -0
  20. data/lib/asciidoctor/bsi/isostandard.rng +477 -0
  21. data/lib/asciidoctor/bsi/reqt.rng +194 -0
  22. data/lib/asciidoctor/bsi/validate.rb +224 -0
  23. data/lib/asciidoctor/bsi/validate_list.rb +72 -0
  24. data/lib/asciidoctor/bsi/validate_requirement.rb +163 -0
  25. data/lib/isodoc/bsi/base_convert.rb +91 -0
  26. data/lib/isodoc/bsi/bsi.international-standard.xsl +6540 -0
  27. data/lib/isodoc/bsi/html/html_bsi_intro.html +8 -0
  28. data/lib/isodoc/bsi/html/html_bsi_titlepage.html +50 -0
  29. data/lib/isodoc/bsi/html/htmlstyle.css +968 -0
  30. data/lib/isodoc/bsi/html/htmlstyle.scss +699 -0
  31. data/lib/isodoc/bsi/html_convert.rb +56 -0
  32. data/lib/isodoc/bsi/i18n-en.yaml +56 -0
  33. data/lib/isodoc/bsi/i18n.rb +15 -0
  34. data/lib/isodoc/bsi/init.rb +24 -0
  35. data/lib/isodoc/bsi/metadata.rb +33 -0
  36. data/lib/isodoc/bsi/pdf_convert.rb +17 -0
  37. data/lib/isodoc/bsi/presentation_xml_convert.rb +72 -0
  38. data/lib/isodoc/bsi/sts_convert.rb +30 -0
  39. data/lib/isodoc/bsi/xref.rb +134 -0
  40. data/lib/metanorma-bsi.rb +15 -0
  41. data/lib/metanorma/bsi.rb +6 -0
  42. data/lib/metanorma/bsi/processor.rb +51 -0
  43. data/lib/metanorma/bsi/version.rb +6 -0
  44. data/metanorma-bsi.gemspec +47 -0
  45. data/spec/asciidoctor/base_spec.rb +778 -0
  46. data/spec/asciidoctor/blocks_spec.rb +553 -0
  47. data/spec/asciidoctor/cleanup_spec.rb +547 -0
  48. data/spec/asciidoctor/inline_spec.rb +176 -0
  49. data/spec/asciidoctor/lists_spec.rb +194 -0
  50. data/spec/asciidoctor/refs_spec.rb +318 -0
  51. data/spec/asciidoctor/section_spec.rb +382 -0
  52. data/spec/asciidoctor/validate_spec.rb +858 -0
  53. data/spec/assets/header.html +7 -0
  54. data/spec/assets/html.css +2 -0
  55. data/spec/assets/iso.xml +71 -0
  56. data/spec/assets/rice_image1.png +0 -0
  57. data/spec/assets/word.css +2 -0
  58. data/spec/assets/wordintro.html +4 -0
  59. data/spec/assets/xref_error.adoc +7 -0
  60. data/spec/isodoc/blocks_spec.rb +259 -0
  61. data/spec/isodoc/i18n_spec.rb +442 -0
  62. data/spec/isodoc/inline_spec.rb +287 -0
  63. data/spec/isodoc/iso_spec.rb +116 -0
  64. data/spec/isodoc/metadata_spec.rb +262 -0
  65. data/spec/isodoc/postproc_spec.rb +137 -0
  66. data/spec/isodoc/ref_spec.rb +376 -0
  67. data/spec/isodoc/section_spec.rb +467 -0
  68. data/spec/isodoc/terms_spec.rb +246 -0
  69. data/spec/isodoc/xref_spec.rb +1730 -0
  70. data/spec/metanorma/processor_spec.rb +76 -0
  71. data/spec/spec_helper.rb +291 -0
  72. data/spec/vcr_cassettes/iso-639.yml +182 -0
  73. data/spec/vcr_cassettes/isobib_get_639_1967.yml +136 -0
  74. data/spec/vcr_cassettes/multistandard.yml +352 -0
  75. metadata +343 -0
@@ -0,0 +1,194 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
3
+ <!--
4
+ Presupposes isodoc.rnc, is included in it
5
+ include "isodoc.rnc" { }
6
+ -->
7
+ <define name="requirement">
8
+ <element name="requirement">
9
+ <ref name="RequirementType"/>
10
+ </element>
11
+ </define>
12
+ <define name="recommendation">
13
+ <element name="recommendation">
14
+ <ref name="RequirementType"/>
15
+ </element>
16
+ </define>
17
+ <define name="permission">
18
+ <element name="permission">
19
+ <ref name="RequirementType"/>
20
+ </element>
21
+ </define>
22
+ <define name="RequirementType">
23
+ <optional>
24
+ <attribute name="obligation">
25
+ <ref name="ObligationType"/>
26
+ </attribute>
27
+ </optional>
28
+ <optional>
29
+ <attribute name="unnumbered">
30
+ <data type="boolean"/>
31
+ </attribute>
32
+ </optional>
33
+ <optional>
34
+ <attribute name="number"/>
35
+ </optional>
36
+ <optional>
37
+ <attribute name="subsequence"/>
38
+ </optional>
39
+ <optional>
40
+ <attribute name="keep-with-next">
41
+ <data type="boolean"/>
42
+ </attribute>
43
+ </optional>
44
+ <optional>
45
+ <attribute name="keep-lines-together">
46
+ <data type="boolean"/>
47
+ </attribute>
48
+ </optional>
49
+ <attribute name="id">
50
+ <data type="ID"/>
51
+ </attribute>
52
+ <optional>
53
+ <attribute name="filename"/>
54
+ </optional>
55
+ <optional>
56
+ <attribute name="model"/>
57
+ </optional>
58
+ <optional>
59
+ <attribute name="type"/>
60
+ </optional>
61
+ <optional>
62
+ <ref name="reqtitle"/>
63
+ </optional>
64
+ <optional>
65
+ <ref name="label"/>
66
+ </optional>
67
+ <optional>
68
+ <ref name="subject"/>
69
+ </optional>
70
+ <zeroOrMore>
71
+ <ref name="reqinherit"/>
72
+ </zeroOrMore>
73
+ <zeroOrMore>
74
+ <ref name="classification"/>
75
+ </zeroOrMore>
76
+ <zeroOrMore>
77
+ <choice>
78
+ <ref name="measurementtarget"/>
79
+ <ref name="specification"/>
80
+ <ref name="verification"/>
81
+ <ref name="import"/>
82
+ <ref name="description"/>
83
+ </choice>
84
+ </zeroOrMore>
85
+ <optional>
86
+ <ref name="reqt_references"/>
87
+ </optional>
88
+ <zeroOrMore>
89
+ <choice>
90
+ <ref name="requirement"/>
91
+ <ref name="recommendation"/>
92
+ <ref name="permission"/>
93
+ </choice>
94
+ </zeroOrMore>
95
+ </define>
96
+ <define name="reqtitle">
97
+ <element name="title">
98
+ <ref name="FormattedString"/>
99
+ </element>
100
+ </define>
101
+ <define name="label">
102
+ <element name="label">
103
+ <text/>
104
+ </element>
105
+ </define>
106
+ <define name="subject">
107
+ <element name="subject">
108
+ <text/>
109
+ </element>
110
+ </define>
111
+ <define name="reqinherit">
112
+ <element name="inherit">
113
+ <text/>
114
+ </element>
115
+ </define>
116
+ <define name="measurementtarget">
117
+ <element name="measurement-target">
118
+ <ref name="RequirementSubpart"/>
119
+ </element>
120
+ </define>
121
+ <define name="specification">
122
+ <element name="specification">
123
+ <ref name="RequirementSubpart"/>
124
+ </element>
125
+ </define>
126
+ <define name="verification">
127
+ <element name="verification">
128
+ <ref name="RequirementSubpart"/>
129
+ </element>
130
+ </define>
131
+ <define name="import">
132
+ <element name="import">
133
+ <ref name="RequirementSubpart"/>
134
+ </element>
135
+ </define>
136
+ <define name="description">
137
+ <element name="description">
138
+ <ref name="RequirementSubpart"/>
139
+ </element>
140
+ </define>
141
+ <define name="reqt_references">
142
+ <element name="references">
143
+ <oneOrMore>
144
+ <ref name="bibitem"/>
145
+ </oneOrMore>
146
+ </element>
147
+ </define>
148
+ <define name="RequirementSubpart">
149
+ <optional>
150
+ <attribute name="type"/>
151
+ </optional>
152
+ <optional>
153
+ <attribute name="exclude">
154
+ <data type="boolean"/>
155
+ </attribute>
156
+ </optional>
157
+ <optional>
158
+ <attribute name="keep-with-next">
159
+ <data type="boolean"/>
160
+ </attribute>
161
+ </optional>
162
+ <optional>
163
+ <attribute name="keep-lines-together">
164
+ <data type="boolean"/>
165
+ </attribute>
166
+ </optional>
167
+ <oneOrMore>
168
+ <ref name="BasicBlock"/>
169
+ </oneOrMore>
170
+ </define>
171
+ <define name="ObligationType">
172
+ <choice>
173
+ <value>requirement</value>
174
+ <value>recommendation</value>
175
+ <value>permission</value>
176
+ </choice>
177
+ </define>
178
+ <define name="classification">
179
+ <element name="classification">
180
+ <ref name="classification_tag"/>
181
+ <ref name="classification_value"/>
182
+ </element>
183
+ </define>
184
+ <define name="classification_tag">
185
+ <element name="tag">
186
+ <text/>
187
+ </element>
188
+ </define>
189
+ <define name="classification_value">
190
+ <element name="value">
191
+ <text/>
192
+ </element>
193
+ </define>
194
+ </grammar>
@@ -0,0 +1,224 @@
1
+ require_relative "./validate_requirement"
2
+ require_relative "./validate_list"
3
+
4
+ module Asciidoctor
5
+ module BSI
6
+ class Converter < ISO::Converter
7
+ # BSI 0:2016 9.4.1
8
+ def doctype_validate(xmldoc)
9
+ doctype = xmldoc&.at("//bibdata/ext/doctype")&.text
10
+ %w(specification management-systems-standard code-of-practice
11
+ guide method-of-test method-of-specifying vocabulary
12
+ classification).include? doctype or
13
+ @log.add("Document Attributes", nil,
14
+ "#{doctype} is not a recognised document type")
15
+ end
16
+
17
+ def validate(doc)
18
+ content_validate(doc)
19
+ schema_validate(formattedstr_strip(doc.dup),
20
+ File.join(File.dirname(__FILE__), "bsi.rng"))
21
+ end
22
+
23
+ def image_name_validate(xmldoc); end
24
+
25
+ def norm_ref_validate(doc); end
26
+
27
+ def style(node, text)
28
+ super
29
+ style_plusminus(node, text)
30
+ style_subscript(node)
31
+ end
32
+
33
+ # Rules for Structure 27.5
34
+ def style_plusminus(node, text)
35
+ style_regex(/\s*(?<num>\S+\u00b1\s*\S+)/,
36
+ "no space before plus-minus sign", node, text)
37
+ style_regex(/\s*(?<num>\S+\s*\u00b1\s+\S+)/,
38
+ "space after plus-minus sign", node, text)
39
+ end
40
+
41
+ # Rules for Structure 27.6
42
+ def style_subscript(node)
43
+ node.xpath(".//sub[.//sub]").each do |r|
44
+ style_warning(node, "Nested subscript", r)
45
+ end
46
+ node.xpath(".//m:msub[.//m:msub]", "m" => MATHML_NS).each do |r|
47
+ style_warning(node, "Nested subscript", r)
48
+ end
49
+ node.xpath(".//sup[.//sup]").each do |r|
50
+ style_warning(node, "Nested superscript", r)
51
+ end
52
+ node.xpath(".//m:msup[.//m:msup]", "m" => MATHML_NS).each do |r|
53
+ style_warning(node, "Nested superscript", r)
54
+ end
55
+ end
56
+
57
+ # protest decimal comma not decimal point
58
+ # TODO we will not yet override decimal comma warning if this is a National Annex to Eurocode (Rules for Structure Annex B)
59
+ def style_number(node, text)
60
+ style_two_regex_not_prev(
61
+ node, text, /^(?<num>-?[0-9]{4,}[,0-9]*)$/,
62
+ %r{\b(ISO|IEC|IEEE/|(in|January|February|March|April|May|June|August|September|October|November|December)\b)$},
63
+ "number not broken up in threes"
64
+ )
65
+ style_regex(/\b(?<num>[0-9]+,[0-9]+)/i,
66
+ "possible decimal comma", node, text)
67
+ style_regex(/\b(?<num>billions?)\b/i,
68
+ "ambiguous number", node, text)
69
+ ordinal_check(node)
70
+ end
71
+
72
+ # Rules for Structure 9.1
73
+ def ordinal_check(node)
74
+ node.xpath(".//sup").each do |s|
75
+ next unless %w(st nd rd th).include? s.text
76
+
77
+ style_warning(s, "text contains ordinal superscript", s.text)
78
+ end
79
+ end
80
+
81
+ def section_style(root)
82
+ super
83
+ @doctype = root.at("//bibdata/ext/doctype")&.text
84
+ @type = @doctype.gsub(/-/, " ").split.map(&:capitalize).join(" ")
85
+ informative_text_style(root)
86
+ normative_text_style(root)
87
+ avoid_must(root)
88
+ annex_validate(root)
89
+ end
90
+
91
+ def annex_validate(root)
92
+ annex_normative_validate(root)
93
+ annex_ordering_validate(root)
94
+ end
95
+
96
+ # Rules for Structure 20.2
97
+ def annex_normative_validate(root)
98
+ @doctype == "guide" and root.xpath("//annex[@obligation = 'normative']")
99
+ .each do |s|
100
+ style_warning(s, "Guide contains normative annex",
101
+ s&.at("./title")&.text)
102
+ end
103
+ end
104
+
105
+ # Rules for Structure 20.4
106
+ def annex_ordering_validate(root)
107
+ ids = citation_order(root.xpath("//annex"))
108
+ cite_order = ids.keys.sort { |a, b| ids[a] <=> ids[b] }
109
+ annex_order = root.xpath("//annex").map { |x| x["id"] }
110
+ cite_order.each_with_index do |a, i|
111
+ next if annex_order[i] == a
112
+
113
+ annex = root.at("//annex[@id = '#{a}']")
114
+ title = annex&.at("./title")&.text || "Annex #{a}"
115
+ style_warning(annex, "#{title} should be annex ##{i + 1}", nil)
116
+ end
117
+ end
118
+
119
+ BSI_PUBLISHER_XPATH =
120
+ "./contributor[role/@type = 'publisher']/"\
121
+ "organization[abbreviation = 'ISO' or abbreviation = 'IEC' or "\
122
+ "abbreviation = 'BSI' or abbreviation = 'CEN' or "\
123
+ "abbreviation = 'CENELEC']".freeze
124
+
125
+ NORM_BSI_WARN = "reference other than BSI, CEN, CENELEC, ISO, IEC "\
126
+ "not expected as normative".freeze
127
+
128
+ # Rules for Structure 10.2
129
+ def norm_bibitem_style(root)
130
+ root.xpath(NORM_BIBITEMS).each do |b|
131
+ b.at(BSI_PUBLISHER_XPATH).nil? and
132
+ @log.add("Style", b, "#{NORM_BSI_WARN}: #{b.text}")
133
+ status = b&.at("./status/stage")&.text
134
+ /^9.$/.match?(status) and
135
+ @log.add("Style", b,
136
+ "Do not cite withdrawn standards as normative: #{b.text}")
137
+ /^[0-5].$/.match?(status) and @log.add(
138
+ "Style", b,
139
+ "Do not cite unpublished standards as normative: #{b.text}"
140
+ )
141
+ end
142
+ end
143
+
144
+ # Rules for Structure 11.5.2
145
+ def title_names_type_validate(root)
146
+ super
147
+ doctypes = /British\sStandard/xi
148
+ title_main_en = title_lang_part(root, "main", "en")
149
+ !title_main_en.nil? && doctypes.match(title_main_en.text) and
150
+ @log.add("Style", title_main_en, "Main Title may name document type")
151
+ title_intro_en = title_lang_part(root, "intro", "en")
152
+ !title_intro_en.nil? && doctypes.match(title_intro_en.text) and
153
+ @log.add("Style", title_intro_en,
154
+ "Title Intro may name document type")
155
+ end
156
+
157
+ # Rules for Structure 16.5.5
158
+ def termdef_style(xmldoc)
159
+ super
160
+ xmldoc.xpath("//term").each do |t|
161
+ para = t.at("./definition") || return
162
+ term = t.at("./preferred").text.strip
163
+ termdef_warn(para.text, /^[A-Z]/, t, term,
164
+ "term definition starts with upper case character")
165
+ termdef_warn(para.text, /\b#{term.downcase}\b/i, t, term,
166
+ "term definition may be circular")
167
+ end
168
+ end
169
+
170
+ def content_validate(doc)
171
+ super
172
+ symbol_style(doc)
173
+ table_validate(doc)
174
+ listcount_validate(doc)
175
+ list_punctuation(doc)
176
+ end
177
+
178
+ # Rules for Structure 17.5
179
+ def symbol_style(xmldoc)
180
+ return if @novalid
181
+
182
+ xmldoc.xpath("//definitions//dd").each do |para|
183
+ term = para&.at("./preceding::dt[1]")&.text&.strip || "[symbol]"
184
+ termdef_warn(para.text, /^(the|a)\b/i, para, term,
185
+ "symbol definition starts with article")
186
+ termdef_warn(para.text, /\.$/i, para, term,
187
+ "symbol definition ends with period")
188
+ termdef_warn(para.text, /^[A-Z]/, para, term,
189
+ "symbol definition starts with upper case character")
190
+ termdef_warn(para.text, /\b#{term.downcase}\b/i, para, term,
191
+ "symbol definition may be circular")
192
+ end
193
+ end
194
+
195
+ # Rules for Structure 22.3.2
196
+ def subclause_validate(root)
197
+ return if @novalid
198
+
199
+ root.xpath("//clause/clause/clause/clause/clause/clause")
200
+ .each do |c|
201
+ style_warning(c, "Exceeds the maximum clause depth of 5", nil)
202
+ end
203
+ end
204
+
205
+ # Rules for Structure 29.6
206
+ def table_validate(xmldoc)
207
+ xmldoc.xpath("//td[not(./*) and normalize-space(.)=''] | "\
208
+ "//th[not(./*) and normalize-space(.)='']").each do |td|
209
+ style_warning(td, "Empty table cell", nil)
210
+ end
211
+ end
212
+
213
+ # TODO: we will not block foreword from National Annex to Eurocode (Rules for Structure G.6.1)
214
+ #
215
+ # TODO: we will not test location of commentaries (Rules for Structure 24.7)
216
+ #
217
+ # TODO: we will not check for space either side of mathematical sign (Rules for Structure 27.5)
218
+ #
219
+ # TODO: we will not check for conflict of ln and log_e or log lg log_10 (Rules for Structure 27.8)
220
+ #
221
+ # TODO: we will not check order of brackets in stem as {[( (Rules for Structure 27.9)
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,72 @@
1
+ module Asciidoctor
2
+ module BSI
3
+ class Converter < ISO::Converter
4
+ # Rules for Structure 23.3
5
+ def listcount_validate(doc)
6
+ return if @novalid
7
+
8
+ doc.xpath("//clause[not(.//clause)] | //annex").each do |c|
9
+ next if c.xpath(".//ol").empty?
10
+
11
+ ols = c.xpath(".//ol") - c.xpath(".//ul//ol | .//ol//ol")
12
+ ols.size > 3 and
13
+ style_warning(c, "More than 3 ordered lists in a numbered clause",
14
+ nil)
15
+ end
16
+ end
17
+
18
+ # Rules for Structure 23.5
19
+ def list_punctuation(doc)
20
+ return if @novalid
21
+
22
+ ((doc.xpath("//ol") - doc.xpath("//ul//ol | //ol//ol")) +
23
+ (doc.xpath("//ul") - doc.xpath("//ul//ul | //ol//ul"))).each do |list|
24
+ prec = list.previous_element
25
+ prec&.name == "p" or
26
+ style_warning(list, "All lists must be preceded by "\
27
+ "introductory phrase", nil)
28
+ list_punctuation1(list, prec.text)
29
+ end
30
+ end
31
+
32
+ def list_punctuation1(list, prectext)
33
+ entries = list.xpath(".//li")
34
+ case prectext.sub(/^.*?(\S)\s*$/, "\\1")
35
+ when ":"
36
+ list.xpath(".//li").each_with_index do |li, i|
37
+ list_semicolon_phrase(li, i == entries.size - 1)
38
+ end
39
+ when "." then entries.each { |li| list_full_sentence(li) }
40
+ else style_warning(list, "All lists must be preceded by "\
41
+ "colon or full stop", prectext)
42
+ end
43
+ end
44
+
45
+ def list_semicolon_phrase(elem, last)
46
+ text = elem.text.strip
47
+ text.match?(/^[^A-Za-z]*[a-z]/) or
48
+ style_warning(elem, "List entry after colon must start with "\
49
+ "lowercase letter", text)
50
+ punct = text.sub(/^.*?(\S)\s*$/, "\\1")
51
+ if last
52
+ punct == "." or style_warning(elem, "Final list entry after colon "\
53
+ "must end with full stop", text)
54
+ else
55
+ punct == ";" or style_warning(elem, "List entry after colon must "\
56
+ "end with semicolon", text)
57
+ end
58
+ end
59
+
60
+ def list_full_sentence(elem)
61
+ text = elem.text.strip
62
+ text.match?(/^[^A-Za-z]*[A-Z]/) or
63
+ style_warning(elem, "List entry after full stop must start with "\
64
+ "uppercase letter", text)
65
+ punct = text.sub(/^.*?(\S)\s*$/, "\\1")
66
+ punct == "." or
67
+ style_warning(elem, "List entry after full stop must "\
68
+ "end with full stop", text)
69
+ end
70
+ end
71
+ end
72
+ end