metanorma-iso 3.1.2 → 3.1.4

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.
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <grammar xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
3
- <!-- VERSION v2.1.0 -->
3
+ <!-- VERSION v2.1.2 -->
4
4
 
5
5
  <!--
6
6
  ALERT: cannot have root comments, because of https://github.com/metanorma/metanorma/issues/437
@@ -123,6 +123,10 @@ the type attribute defaults to `review` for reviews</a:documentation>
123
123
  <a:documentation>Notes whose scope is the current block</a:documentation>
124
124
  </ref>
125
125
  </zeroOrMore>
126
+ <ref name="BlockSource">
127
+ <a:documentation>Bibliographic source for the information in the list.
128
+ Sources are currently only rendered in metanorma-plateau</a:documentation>
129
+ </ref>
126
130
  </define>
127
131
  <define name="OlBody">
128
132
  <optional>
@@ -140,6 +144,10 @@ the type attribute defaults to `review` for reviews</a:documentation>
140
144
  <a:documentation>Notes whose scope is the current block</a:documentation>
141
145
  </ref>
142
146
  </zeroOrMore>
147
+ <ref name="BlockSource">
148
+ <a:documentation>Bibliographic source for the information in the list.
149
+ Sources are currently only rendered in metanorma-plateau</a:documentation>
150
+ </ref>
143
151
  </define>
144
152
  <define name="OlAttributes">
145
153
  <a:documentation>NOTE: `start` attribute is not included by default, because of problems it raises with DOC output</a:documentation>
@@ -188,6 +196,10 @@ the type attribute defaults to `review` for reviews</a:documentation>
188
196
  <a:documentation>Notes whose scope is the current block</a:documentation>
189
197
  </ref>
190
198
  </zeroOrMore>
199
+ <ref name="BlockSource">
200
+ <a:documentation>Bibliographic source for the information in the list.
201
+ Sources are currently only rendered in metanorma-plateau</a:documentation>
202
+ </ref>
191
203
  </define>
192
204
  <define name="dt">
193
205
  <element name="dt">
@@ -871,6 +883,12 @@ titlecase, or lowercase</a:documentation>
871
883
  </oneOrMore>
872
884
  </element>
873
885
  </define>
886
+ <define name="ParagraphFnBody" combine="interleave">
887
+ <ref name="BlockSource">
888
+ <a:documentation>Bibliographic source for the information in the paragraph
889
+ parargaph sources are currently only rendered in metanorma-plateau</a:documentation>
890
+ </ref>
891
+ </define>
874
892
  <define name="BasicBlock" combine="choice">
875
893
  <ref name="columnbreak"/>
876
894
  </define>
@@ -2233,6 +2251,11 @@ used in document amendments</a:documentation>
2233
2251
  <ref name="RequiredId"/>
2234
2252
  <ref name="NumberingAttributes"/>
2235
2253
  <ref name="BlockAttributes"/>
2254
+ <optional>
2255
+ <attribute name="type">
2256
+ <a:documentation>Semantic classification of note</a:documentation>
2257
+ </attribute>
2258
+ </optional>
2236
2259
  <oneOrMore>
2237
2260
  <choice>
2238
2261
  <a:documentation>Content of the term note</a:documentation>
@@ -67,14 +67,6 @@
67
67
  </group>
68
68
  </element>
69
69
  </define>
70
- <define name="editorialgroup">
71
- <element name="editorialgroup">
72
- <ref name="ISOProjectGroup"/>
73
- </element>
74
- <optional>
75
- <ref name="approvalgroup"/>
76
- </optional>
77
- </define>
78
70
  </include>
79
71
  <define name="BibDataExtensionType" combine="interleave">
80
72
  <optional>
@@ -112,33 +104,6 @@ Used for legacy ISO documents</a:documentation>
112
104
  <ref name="DocumentType"/>
113
105
  </element>
114
106
  </define>
115
- <define name="ISOProjectGroup">
116
- <zeroOrMore>
117
- <ref name="agency"/>
118
- </zeroOrMore>
119
- <oneOrMore>
120
- <ref name="technical-committee"/>
121
- </oneOrMore>
122
- <zeroOrMore>
123
- <ref name="subcommittee"/>
124
- </zeroOrMore>
125
- <zeroOrMore>
126
- <ref name="workgroup"/>
127
- </zeroOrMore>
128
- <optional>
129
- <ref name="secretariat"/>
130
- </optional>
131
- </define>
132
- <define name="approvalgroup">
133
- <element name="approvalgroup">
134
- <ref name="ISOProjectGroup"/>
135
- </element>
136
- </define>
137
- <define name="agency">
138
- <element name="agency">
139
- <text/>
140
- </element>
141
- </define>
142
107
  <define name="horizontal">
143
108
  <element name="horizontal">
144
109
  <data type="boolean"/>
@@ -179,21 +144,6 @@ Used for legacy ISO documents</a:documentation>
179
144
  <data type="int"/>
180
145
  </element>
181
146
  </define>
182
- <define name="subcommittee">
183
- <element name="subcommittee">
184
- <ref name="IsoWorkgroup"/>
185
- </element>
186
- </define>
187
- <define name="workgroup">
188
- <element name="workgroup">
189
- <ref name="IsoWorkgroup"/>
190
- </element>
191
- </define>
192
- <define name="secretariat">
193
- <element name="secretariat">
194
- <text/>
195
- </element>
196
- </define>
197
147
  <define name="stagename">
198
148
  <element name="stagename">
199
149
  <optional>
@@ -1,10 +1,11 @@
1
1
  require "metanorma-standoc"
2
2
  require_relative "validate_style"
3
+ require_relative "validate_numeric"
3
4
  require_relative "validate_requirements"
4
5
  require_relative "validate_section"
5
6
  require_relative "validate_title"
6
- require_relative "validate_image"
7
7
  require_relative "validate_list"
8
+ require_relative "validate_xref"
8
9
  require "nokogiri"
9
10
  require "jing"
10
11
  require "iev"
@@ -12,141 +13,27 @@ require "iev"
12
13
  module Metanorma
13
14
  module Iso
14
15
  class Converter < Standoc::Converter
16
+ COMMITTEE_XPATH = <<~XPATH.freeze
17
+ //contributor[role/description = 'committee']/organization/subdivision
18
+ XPATH
19
+
15
20
  def isosubgroup_validate(root)
16
- root.xpath("//technical-committee/@type").each do |t|
21
+ root.xpath("#{COMMITTEE_XPATH}[@type = 'Technical committee']/@subtype").each do |t|
17
22
  %w{TC PC JTC JPC}.include?(t.text) or
18
23
  @log.add("Document Attributes", nil,
19
24
  "invalid technical committee type #{t}")
20
25
  end
21
- root.xpath("//subcommittee/@type").each do |t|
26
+ root.xpath("#{COMMITTEE_XPATH}[@type = 'Subcommittee']/@subtype").each do |t|
22
27
  %w{SC JSC}.include?(t.text) or
23
28
  @log.add("Document Attributes", nil,
24
29
  "invalid subcommittee type #{t}")
25
30
  end
26
31
  end
27
32
 
28
- # ISO/IEC DIR 2, 15.5.3, 20.2
29
- # does not deal with preceding text marked up
30
- def see_xrefs_validate(root)
31
- @lang == "en" or return
32
- anchors = extract_anchor_norm(root)
33
- root.xpath("//xref").each do |t|
34
- preceding = t.at("./preceding-sibling::text()[last()]")
35
- !preceding.nil? &&
36
- /\b(see| refer to)\p{Zs}*\Z/mi.match(preceding) or next
37
- anchors[t["target"]] and
38
- @log.add("Style", t,
39
- "'see #{t['target']}' is pointing to a normative section")
40
- end
41
- end
42
-
43
- def extract_anchor_norm(root)
44
- nodes = root.xpath("//annex[@obligation = 'normative'] | " \
45
- "//references[@obligation = 'normative']")
46
- ret = nodes.each_with_object({}) do |n, m|
47
- n["anchor"] and m[n["anchor"]] = true
48
- end
49
- nodes.each do |n|
50
- n.xpath(".//*[@anchor]").each { |n1| ret[n1["anchor"]] = true }
51
- end
52
- ret
53
- end
54
-
55
- # ISO/IEC DIR 2, 15.5.3
56
- def see_erefs_validate(root)
57
- @lang == "en" or return
58
- bibitemids = extract_bibitem_anchors(root)
59
- root.xpath("//eref").each do |t|
60
- prec = t.at("./preceding-sibling::text()[last()]")
61
- !prec.nil? && /\b(see|refer to)\p{Zs}*\Z/mi.match(prec) or next
62
- unless target = bibitemids[t["bibitemid"]]
63
- #unless target = root.at("//bibitem[@anchor = '#{t['bibitemid']}']")
64
- @log.add("Bibliography", t,
65
- "'#{t} is not pointing to a real reference")
66
- next
67
- end
68
- #target.at("./ancestor::references[@normative = 'true']") and
69
- target[:norm] and
70
- @log.add("Style", t,
71
- "'see #{t}' is pointing to a normative reference")
72
- end
73
- end
74
-
75
- def extract_bibitem_anchors(root)
76
- ret = root.xpath("//references[@normative = 'true']//bibitem")
77
- .each_with_object({}) do |b, m|
78
- m[b["anchor"]] = { bib: b, norm: true }
79
- end
80
- root.xpath("//references[not(@normative = 'true')]//bibitem")
81
- .each do |b|
82
- ret[b["anchor"]] = { bib: b, norm: false }
83
- end
84
- ret
85
- end
86
-
87
- # ISO/IEC DIR 2, 10.4
88
- def locality_erefs_validate(root)
89
- root.xpath("//eref[descendant::locality]").each do |t|
90
- if /^(ISO|IEC)/.match?(t["citeas"]) &&
91
- !/: ?(\d+{4}|–)$/.match?(t["citeas"])
92
- @log.add("Style", t,
93
- "undated reference #{t['citeas']} should not contain " \
94
- "specific elements")
95
- end
96
- end
97
- end
98
-
99
33
  def termdef_warn(text, regex, elem, term, msg)
100
34
  regex.match(text) && @log.add("Style", elem, "#{term}: #{msg}")
101
35
  end
102
36
 
103
- # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-r-ref_clause3
104
- def term_xrefs_validate(xmldoc)
105
- termids = xmldoc
106
- .xpath("//sections/terms | //sections/clause[.//terms] | " \
107
- "//annex[.//terms]").each_with_object({}) do |t, m|
108
- t.xpath(".//*/@anchor").each { |a| m[a.text] = true }
109
- t.xpath(".//*/@id").each { |a| m[a.text] = true }
110
- t.name == "terms" and m[t["anchor"] || t["id"]] = true
111
- end
112
- xmldoc.xpath(".//xref").each do |x|
113
- term_xrefs_validate1(x, termids)
114
- end
115
- end
116
-
117
- def term_xrefs_validate1(xref, termids)
118
- closest_id = xref.xpath("./ancestor::*[@id]")&.last or return
119
- termids[xref["target"]] && !termids[closest_id["id"]] and
120
- @log.add("Style", xref,
121
- "only terms clauses can cross-reference terms clause " \
122
- "(#{xref['target']})")
123
- !termids[xref["target"]] && termids[closest_id["id"]] and
124
- @log.add("Style", xref,
125
- "non-terms clauses cannot cross-reference terms clause " \
126
- "(#{xref['target']})")
127
- end
128
-
129
- # require that all assets of a particular type be cross-referenced
130
- # within the document
131
- def xrefs_mandate_validate(xmldoc)
132
- xrefs_mandate_validate1(xmldoc, "//annex", "Annex")
133
- xrefs_mandate_validate1(xmldoc, "//table", "Table")
134
- xrefs_mandate_validate1(xmldoc, "//figure", "Figure")
135
- xrefs_mandate_validate1(xmldoc, "//formula", "Formula")
136
- end
137
-
138
- def xrefs_mandate_validate1(xmldoc, xpath, name)
139
- exc = %w(table note example figure).map { |x| "//#{x}#{xpath}" }
140
- .join(" | ")
141
- (xmldoc.xpath(xpath) - xmldoc.xpath(exc)).each do |x|
142
- x["unnumbered"] == "true" and next
143
- @doc_xrefs[x["anchor"]] or
144
- @log.add("Style", x, "#{name} #{x['anchor']} has not been " \
145
- "cross-referenced within document",
146
- severity: xpath == "//formula" ? 2 : 1)
147
- end
148
- end
149
-
150
37
  # ISO/IEC DIR 2, 16.5.6
151
38
  def termdef_style(xmldoc)
152
39
  xmldoc.xpath("//term").each do |t|
@@ -182,6 +69,22 @@ module Metanorma
182
69
  iteration_validate(doc)
183
70
  end
184
71
 
72
+ # DRG directives 3.7; but anticipated by standoc
73
+ def subfigure_validate(xmldoc)
74
+ elems = { footnote: "fn", note: "note", key: "dl" }
75
+ xmldoc.xpath("//figure//figure").each do |f|
76
+ elems.each do |k, v|
77
+ f.xpath(".//#{v}").each do |n|
78
+ @log.add("Style", n, "#{k} is not permitted in a subfigure")
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ def figure_validate(xmldoc)
85
+ subfigure_validate(xmldoc)
86
+ end
87
+
185
88
  def content_validate(doc)
186
89
  super
187
90
  root = doc.root
@@ -200,14 +103,6 @@ module Metanorma
200
103
  list_punctuation(doc)
201
104
  end
202
105
 
203
- def iso_xref_validate(doc)
204
- see_xrefs_validate(doc)
205
- term_xrefs_validate(doc)
206
- xrefs_mandate_validate(doc)
207
- see_erefs_validate(doc)
208
- locality_erefs_validate(doc)
209
- end
210
-
211
106
  def bibitem_validate(xmldoc)
212
107
  xmldoc.xpath("//bibitem[date/on = '–']").each do |b|
213
108
  b.at("./note[@type = 'Unpublished-Status']") or
@@ -0,0 +1,128 @@
1
+ module Metanorma
2
+ module Iso
3
+ class Converter < Standoc::Converter
4
+ # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-s-quantity
5
+ def style_subscript(node)
6
+ style_subscript_proper(node)
7
+ style_subscript_mathml(node)
8
+ end
9
+
10
+ # Check HTML subscripts - only topmost level subs (no sub ancestors)
11
+ def style_subscript_proper(node)
12
+ node.xpath(".//sub[not(ancestor::sub)]").each do |x|
13
+ depth = calculate_subscript_depth(x)
14
+ depth < 2 and next # No warning for single level subscripts
15
+ if [2, 3].include?(depth)
16
+ style_warning(node, "may contain nested subscripts", x.to_xml)
17
+ else # depth >= 3
18
+ style_warning(node, "no more than 3 levels of subscript nesting allowed",
19
+ x.to_xml)
20
+ end
21
+ end
22
+ end
23
+
24
+ # Check MathML subscripts - only topmost level msubs (no msub ancestors)
25
+ def style_subscript_mathml(node)
26
+ node.xpath(".//m:msub[not(ancestor::m:msub)]",
27
+ "m" => MATHML_NS).each do |x|
28
+ depth = calculate_mathml_subscript_depth(x)
29
+ depth < 2 and next # No warning for single level subscripts
30
+ if [2, 3].include?(depth)
31
+ style_warning(node, "may contain nested subscripts", x.to_xml)
32
+ else # depth > 3
33
+ style_warning(node, "no more than 3 levels of subscript nesting allowed",
34
+ x.to_xml)
35
+ end
36
+ end
37
+ end
38
+
39
+ # ISO/IEC DIR 2, 9.1
40
+ # ISO/IEC DIR 2, Table B.1
41
+ # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-n-numbers
42
+ def style_number(node, text)
43
+ style_number_grouping(node, text)
44
+ style_regex(/(?:^|\p{Zs})(?<num>[0-9]+\.[0-9]+)(?!\.[0-9])/i,
45
+ "possible decimal point: mark up numbers with stem:[]", node, text)
46
+ @lang == "en" and style_regex(/\b(?<num>billions?)\b/i,
47
+ "ambiguous number", node, text)
48
+ style_regex(/(?:^|\p{Zs})(?<num>-[0-9][0-9,.]*)/i,
49
+ "hyphen instead of minus sign U+2212", node, text)
50
+ @novalid_number = true
51
+ end
52
+
53
+ def style_number_grouping(node, text)
54
+ if @validate_years
55
+ style_two_regex_not_prev(
56
+ node, text, /^(?<num>-?[0-9]{4,}[,0-9]*)\Z/,
57
+ %r{\b(ISO|IEC|IEEE|(in|January|February|March|April|May|June|August|September|October|November|December)\b)\Z},
58
+ "number not broken up in threes: mark up numbers with stem:[]"
59
+ )
60
+ else
61
+ style_two_regex_not_prev(
62
+ node, text, /^(?<num>-?(?:[0-9]{5,}[,0-9]*|[03-9]\d\d\d|1[0-8]\d\d|2[1-9]\d\d|20[5-9]\d))\Z/,
63
+ %r{\b(ISO|IEC|IEEE|\b)\Z},
64
+ "number not broken up in threes: mark up numbers with stem:[]"
65
+ )
66
+ end
67
+ end
68
+
69
+ # ISO/IEC DIR 2, 9.2.1
70
+ def style_percent(node, text)
71
+ style_regex(/\b(?<num>[0-9.,]+%)/,
72
+ "no space before percent sign", node, text)
73
+ style_regex(/\b(?<num>[0-9.,]+ \u00b1 [0-9,.]+ %)/,
74
+ "unbracketed tolerance before percent sign", node, text)
75
+ end
76
+
77
+ # leaving out as problematic: N J K C S T H h d B o E
78
+ SI_UNIT = "(m|cm|mm|km|μm|nm|g|kg|mgmol|cd|rad|sr|Hz|Hz|MHz|Pa|hPa|kJ|" \
79
+ "V|kV|W|MW|kW|F|μF|Ω|Wb|°C|lm|lx|Bq|Gy|Sv|kat|l|t|eV|u|Np|Bd|" \
80
+ "bit|kB|MB|Hart|nat|Sh|var)".freeze
81
+
82
+ # ISO/IEC DIR 2, 9.3
83
+ def style_units(node, text)
84
+ style_regex(/\b(?<num>[0-9][0-9,]*\p{Zs}+[\u00b0\u2032\u2033])/,
85
+ "space between number and degrees/minutes/seconds",
86
+ node, text)
87
+ style_regex(/\b(?<num>[0-9][0-9,]*#{SI_UNIT})\b/o,
88
+ "no space between number and SI unit", node, text)
89
+ style_non_std_units(node, text)
90
+ end
91
+
92
+ NONSTD_UNITS = {
93
+ sec: "s", mins: "min", hrs: "h", hr: "h", cc: "cm^3",
94
+ lit: "l", amp: "A", amps: "A", rpm: "r/min"
95
+ }.freeze
96
+
97
+ # ISO/IEC DIR 2, 9.3
98
+ def style_non_std_units(node, text)
99
+ NONSTD_UNITS.each do |k, v|
100
+ style_regex(/\b(?<num>[0-9][0-9,]*\p{Zs}+#{k})\b/,
101
+ "non-standard unit (should be #{v})", node, text)
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ def calculate_subscript_depth(sub_element)
108
+ sub_element.xpath(".//sub").empty? and return 1
109
+ max_depth = 1
110
+ sub_element.xpath(".//sub").each do |nested_sub|
111
+ depth = 1 + calculate_subscript_depth(nested_sub)
112
+ max_depth = [max_depth, depth].max
113
+ end
114
+ max_depth
115
+ end
116
+
117
+ def calculate_mathml_subscript_depth(msub_element)
118
+ msub_element.xpath(".//m:msub", "m" => MATHML_NS).empty? and return 1
119
+ max_depth = 1
120
+ msub_element.xpath(".//m:msub", "m" => MATHML_NS).each do |nested_msub|
121
+ depth = 1 + calculate_mathml_subscript_depth(nested_msub)
122
+ max_depth = [max_depth, depth].max
123
+ end
124
+ max_depth
125
+ end
126
+ end
127
+ end
128
+ end
@@ -94,17 +94,6 @@ module Metanorma
94
94
  style_problem_words(node, text)
95
95
  end
96
96
 
97
- # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-s-quantity
98
- def style_subscript(node)
99
- warning = "may contain nested subscripts (max 3 levels allowed)"
100
- node.xpath(".//sub[.//sub]").each do |x|
101
- style_warning(node, warning, x.to_xml)
102
- end
103
- node.xpath(".//m:msub[.//m:msub]", "m" => MATHML_NS).each do |x|
104
- style_warning(node, warning, x.to_xml)
105
- end
106
- end
107
-
108
97
  # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-s-need
109
98
  # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-s-might
110
99
  # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-s-family
@@ -120,44 +109,6 @@ module Metanorma
120
109
  style_regex(/\b(?<num>billions?)\b/i, "ambiguous number", node, text)
121
110
  end
122
111
 
123
- # ISO/IEC DIR 2, 9.1
124
- # ISO/IEC DIR 2, Table B.1
125
- # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-n-numbers
126
- def style_number(node, text)
127
- style_number_grouping(node, text)
128
- style_regex(/(?:^|\p{Zs})(?<num>[0-9]+\.[0-9]+)(?!\.[0-9])/i,
129
- "possible decimal point: mark up numbers with stem:[]", node, text)
130
- @lang == "en" and style_regex(/\b(?<num>billions?)\b/i,
131
- "ambiguous number", node, text)
132
- style_regex(/(?:^|\p{Zs})(?<num>-[0-9][0-9,.]*)/i,
133
- "hyphen instead of minus sign U+2212", node, text)
134
- @novalid_number = true
135
- end
136
-
137
- def style_number_grouping(node, text)
138
- if @validate_years
139
- style_two_regex_not_prev(
140
- node, text, /^(?<num>-?[0-9]{4,}[,0-9]*)\Z/,
141
- %r{\b(ISO|IEC|IEEE|(in|January|February|March|April|May|June|August|September|October|November|December)\b)\Z},
142
- "number not broken up in threes: mark up numbers with stem:[]"
143
- )
144
- else
145
- style_two_regex_not_prev(
146
- node, text, /^(?<num>-?(?:[0-9]{5,}[,0-9]*|[03-9]\d\d\d|1[0-8]\d\d|2[1-9]\d\d|20[5-9]\d))\Z/,
147
- %r{\b(ISO|IEC|IEEE|\b)\Z},
148
- "number not broken up in threes: mark up numbers with stem:[]"
149
- )
150
- end
151
- end
152
-
153
- # ISO/IEC DIR 2, 9.2.1
154
- def style_percent(node, text)
155
- style_regex(/\b(?<num>[0-9.,]+%)/,
156
- "no space before percent sign", node, text)
157
- style_regex(/\b(?<num>[0-9.,]+ \u00b1 [0-9,.]+ %)/,
158
- "unbracketed tolerance before percent sign", node, text)
159
- end
160
-
161
112
  # ISO/IEC DIR 2, 8.4
162
113
  # ISO/IEC DIR 2, 9.3
163
114
  def style_abbrev(node, text)
@@ -173,29 +124,6 @@ module Metanorma
173
124
  "V|kV|W|MW|kW|F|μF|Ω|Wb|°C|lm|lx|Bq|Gy|Sv|kat|l|t|eV|u|Np|Bd|" \
174
125
  "bit|kB|MB|Hart|nat|Sh|var)".freeze
175
126
 
176
- # ISO/IEC DIR 2, 9.3
177
- def style_units(node, text)
178
- style_regex(/\b(?<num>[0-9][0-9,]*\p{Zs}+[\u00b0\u2032\u2033])/,
179
- "space between number and degrees/minutes/seconds",
180
- node, text)
181
- style_regex(/\b(?<num>[0-9][0-9,]*#{SI_UNIT})\b/o,
182
- "no space between number and SI unit", node, text)
183
- style_non_std_units(node, text)
184
- end
185
-
186
- NONSTD_UNITS = {
187
- sec: "s", mins: "min", hrs: "h", hr: "h", cc: "cm^3",
188
- lit: "l", amp: "A", amps: "A", rpm: "r/min"
189
- }.freeze
190
-
191
- # ISO/IEC DIR 2, 9.3
192
- def style_non_std_units(node, text)
193
- NONSTD_UNITS.each do |k, v|
194
- style_regex(/\b(?<num>[0-9][0-9,]*\p{Zs}+#{k})\b/,
195
- "non-standard unit (should be #{v})", node, text)
196
- end
197
- end
198
-
199
127
  # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-p-and
200
128
  # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-p-andor
201
129
  def style_punct(node, text)
@@ -0,0 +1,130 @@
1
+ module Metanorma
2
+ module Iso
3
+ class Converter < Standoc::Converter
4
+ # ISO/IEC DIR 2, 15.5.3, 20.2
5
+ # does not deal with preceding text marked up
6
+ def see_xrefs_validate(root)
7
+ @lang == "en" or return
8
+ anchors = extract_anchor_norm(root)
9
+ root.xpath("//xref").each do |t|
10
+ preceding = t.at("./preceding-sibling::text()[last()]")
11
+ !preceding.nil? &&
12
+ /\b(see| refer to)\p{Zs}*\Z/mi.match(preceding) or next
13
+ anchors[t["target"]] and
14
+ @log.add("Style", t,
15
+ "'see #{t['target']}' is pointing to a normative section")
16
+ end
17
+ end
18
+
19
+ def extract_anchor_norm(root)
20
+ nodes = root.xpath("//annex[@obligation = 'normative'] | " \
21
+ "//references[@obligation = 'normative']")
22
+ ret = nodes.each_with_object({}) do |n, m|
23
+ n["anchor"] and m[n["anchor"]] = true
24
+ end
25
+ nodes.each do |n|
26
+ n.xpath(".//*[@anchor]").each { |n1| ret[n1["anchor"]] = true }
27
+ end
28
+ ret
29
+ end
30
+
31
+ # ISO/IEC DIR 2, 15.5.3
32
+ def see_erefs_validate(root)
33
+ @lang == "en" or return
34
+ bibitemids = extract_bibitem_anchors(root)
35
+ root.xpath("//eref").each do |t|
36
+ prec = t.at("./preceding-sibling::text()[last()]")
37
+ !prec.nil? && /\b(see|refer to)\p{Zs}*\Z/mi.match(prec) or next
38
+ unless target = bibitemids[t["bibitemid"]]
39
+ # unless target = root.at("//bibitem[@anchor = '#{t['bibitemid']}']")
40
+ @log.add("Bibliography", t,
41
+ "'#{t} is not pointing to a real reference")
42
+ next
43
+ end
44
+ target[:norm] and
45
+ @log.add("Style", t,
46
+ "'see #{t}' is pointing to a normative reference")
47
+ end
48
+ end
49
+
50
+ def extract_bibitem_anchors(root)
51
+ ret = root.xpath("//references[@normative = 'true']//bibitem")
52
+ .each_with_object({}) do |b, m|
53
+ m[b["anchor"]] = { bib: b, norm: true }
54
+ end
55
+ root.xpath("//references[not(@normative = 'true')]//bibitem")
56
+ .each do |b|
57
+ ret[b["anchor"]] = { bib: b, norm: false }
58
+ end
59
+ ret
60
+ end
61
+
62
+ # ISO/IEC DIR 2, 10.4
63
+ def locality_erefs_validate(root)
64
+ root.xpath("//eref[descendant::locality]").each do |t|
65
+ if /^(ISO|IEC)/.match?(t["citeas"]) &&
66
+ !/: ?(\d+{4}|–)$/.match?(t["citeas"])
67
+ @log.add("Style", t,
68
+ "undated reference #{t['citeas']} should not contain " \
69
+ "specific elements")
70
+ end
71
+ end
72
+ end
73
+
74
+ # https://www.iso.org/ISO-house-style.html#iso-hs-s-text-r-r-ref_clause3
75
+ def term_xrefs_validate(xmldoc)
76
+ termids = xmldoc
77
+ .xpath("//sections/terms | //sections/clause[.//terms] | " \
78
+ "//annex[.//terms]").each_with_object({}) do |t, m|
79
+ t.xpath(".//*/@anchor").each { |a| m[a.text] = true }
80
+ t.xpath(".//*/@id").each { |a| m[a.text] = true }
81
+ t.name == "terms" and m[t["anchor"] || t["id"]] = true
82
+ end
83
+ xmldoc.xpath(".//xref").each do |x|
84
+ term_xrefs_validate1(x, termids)
85
+ end
86
+ end
87
+
88
+ def term_xrefs_validate1(xref, termids)
89
+ closest_id = xref.xpath("./ancestor::*[@id]")&.last or return
90
+ termids[xref["target"]] && !termids[closest_id["id"]] and
91
+ @log.add("Style", xref,
92
+ "only terms clauses can cross-reference terms clause " \
93
+ "(#{xref['target']})")
94
+ !termids[xref["target"]] && termids[closest_id["id"]] and
95
+ @log.add("Style", xref,
96
+ "non-terms clauses cannot cross-reference terms clause " \
97
+ "(#{xref['target']})")
98
+ end
99
+
100
+ # require that all assets of a particular type be cross-referenced
101
+ # within the document
102
+ def xrefs_mandate_validate(xmldoc)
103
+ xrefs_mandate_validate1(xmldoc, "//annex", "Annex")
104
+ xrefs_mandate_validate1(xmldoc, "//table", "Table")
105
+ xrefs_mandate_validate1(xmldoc, "//figure", "Figure")
106
+ xrefs_mandate_validate1(xmldoc, "//formula", "Formula")
107
+ end
108
+
109
+ def xrefs_mandate_validate1(xmldoc, xpath, name)
110
+ exc = %w(table note example figure).map { |x| "//#{x}#{xpath}" }
111
+ .join(" | ")
112
+ (xmldoc.xpath(xpath) - xmldoc.xpath(exc)).each do |x|
113
+ x["unnumbered"] == "true" and next
114
+ @doc_xrefs[x["anchor"]] or
115
+ @log.add("Style", x, "#{name} #{x['anchor']} has not been " \
116
+ "cross-referenced within document",
117
+ severity: xpath == "//formula" ? 2 : 1)
118
+ end
119
+ end
120
+
121
+ def iso_xref_validate(doc)
122
+ see_xrefs_validate(doc)
123
+ term_xrefs_validate(doc)
124
+ xrefs_mandate_validate(doc)
125
+ see_erefs_validate(doc)
126
+ locality_erefs_validate(doc)
127
+ end
128
+ end
129
+ end
130
+ end