asciidoctor-iso 0.6.1 → 0.7.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.
- checksums.yaml +4 -4
- data/.gitattributes +2 -0
- data/.travis.yml +5 -0
- data/Gemfile.lock +12 -10
- data/README.adoc +113 -16
- data/bin/rspec +18 -0
- data/lib/asciidoctor/iso/base.rb +30 -28
- data/lib/asciidoctor/iso/blocks.rb +33 -33
- data/lib/asciidoctor/iso/cleanup.rb +79 -33
- data/lib/asciidoctor/iso/cleanup_block.rb +71 -18
- data/lib/asciidoctor/iso/cleanup_ref.rb +35 -30
- data/lib/asciidoctor/iso/converter.rb +0 -3
- data/lib/asciidoctor/iso/front.rb +29 -16
- data/lib/asciidoctor/iso/html/isodoc.css +34 -0
- data/lib/asciidoctor/iso/html/wordstyle.css +138 -6
- data/lib/asciidoctor/iso/inline.rb +10 -22
- data/lib/asciidoctor/iso/isodoc.rng +66 -16
- data/lib/asciidoctor/iso/isostandard.rng +129 -15
- data/lib/asciidoctor/iso/lists.rb +49 -42
- data/lib/asciidoctor/iso/macros.rb +12 -8
- data/lib/asciidoctor/iso/section.rb +53 -37
- data/lib/asciidoctor/iso/table.rb +9 -1
- data/lib/asciidoctor/iso/utils.rb +18 -13
- data/lib/asciidoctor/iso/validate.rb +100 -24
- data/lib/asciidoctor/iso/validate_requirements.rb +106 -0
- data/lib/asciidoctor/iso/validate_section.rb +85 -65
- data/lib/asciidoctor/iso/validate_style.rb +68 -115
- data/lib/asciidoctor/iso/version.rb +1 -1
- data/spec/asciidoctor-iso/base_spec.rb +193 -0
- data/spec/asciidoctor-iso/blocks_spec.rb +426 -0
- data/spec/asciidoctor-iso/cleanup_spec.rb +687 -0
- data/spec/asciidoctor-iso/inline_spec.rb +159 -0
- data/spec/asciidoctor-iso/lists_spec.rb +189 -0
- data/spec/asciidoctor-iso/macros_spec.rb +20 -0
- data/spec/asciidoctor-iso/refs_spec.rb +194 -0
- data/spec/asciidoctor-iso/section_spec.rb +301 -0
- data/spec/asciidoctor-iso/table_spec.rb +307 -0
- data/spec/asciidoctor-iso/validate_spec.rb +749 -0
- data/spec/examples/english.yaml +69 -0
- data/spec/examples/rice.adoc +30 -28
- data/spec/examples/rice.doc +3035 -2865
- data/spec/examples/rice.html +281 -234
- data/spec/examples/rice.preview.html +30 -20
- data/spec/examples/rice.xml +250 -282
- data/spec/spec_helper.rb +87 -0
- metadata +17 -2
@@ -10,10 +10,10 @@ module Asciidoctor
|
|
10
10
|
@table_fn_number = "a"
|
11
11
|
noko do |xml|
|
12
12
|
xml.table **attr_code(table_attrs(node)) do |xml_table|
|
13
|
+
table_name(node, xml_table)
|
13
14
|
%i(head body foot).reject do |tblsec|
|
14
15
|
node.rows[tblsec].empty?
|
15
16
|
end
|
16
|
-
xml_table.name node.title if node.title?
|
17
17
|
table_head_body_and_foot node, xml_table
|
18
18
|
end
|
19
19
|
end
|
@@ -21,6 +21,14 @@ module Asciidoctor
|
|
21
21
|
|
22
22
|
private
|
23
23
|
|
24
|
+
def table_name(node, xml_table)
|
25
|
+
if node.title?
|
26
|
+
xml_table.name node.title
|
27
|
+
else
|
28
|
+
style_warning(node, "Table should have title", nil)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
24
32
|
def table_cell1(cell, thd)
|
25
33
|
if cell.style == :asciidoc
|
26
34
|
thd << cell.content
|
@@ -24,18 +24,33 @@ module Asciidoctor
|
|
24
24
|
(!n.respond_to?(:level) || n.level.positive?) &&
|
25
25
|
(!n.respond_to?(:context) || n.context != :section)
|
26
26
|
n = n.parent
|
27
|
-
return "Section: #{n.title}" if
|
27
|
+
return "Section: #{n.title}" if n&.respond_to?(:context) &&
|
28
|
+
n&.context == :section
|
28
29
|
end
|
29
30
|
"??"
|
30
31
|
end
|
31
32
|
|
32
33
|
def warning(node, msg, text)
|
34
|
+
return if @novalid
|
33
35
|
warntext = "asciidoctor: WARNING"\
|
34
36
|
"(#{current_location(node)}): #{msg}"
|
35
37
|
warntext += ": #{text}" if text
|
36
38
|
warn warntext
|
37
39
|
end
|
38
40
|
|
41
|
+
def flatten_rawtext_lines(node, result)
|
42
|
+
node.lines.each do |x|
|
43
|
+
if node.respond_to?(:context) && (node.context == :literal ||
|
44
|
+
node.context == :listing)
|
45
|
+
result << x.gsub(/</, "<").gsub(/>/, ">")
|
46
|
+
else
|
47
|
+
# strip not only HTML <tag>, and Asciidoc xrefs <<xref>>
|
48
|
+
result << x.gsub(/<[^>]*>+/, "")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
39
54
|
# if node contains blocks, flatten them into a single line;
|
40
55
|
# and extract only raw text
|
41
56
|
def flatten_rawtext(node)
|
@@ -43,16 +58,7 @@ module Asciidoctor
|
|
43
58
|
if node.respond_to?(:blocks) && node.blocks?
|
44
59
|
node.blocks.each { |b| result << flatten_rawtext(b) }
|
45
60
|
elsif node.respond_to?(:lines)
|
46
|
-
node
|
47
|
-
if node.respond_to?(:context) && (node.context == :literal ||
|
48
|
-
node.context == :listing)
|
49
|
-
result << x.gsub(/</, "<").gsub(/>/, ">")
|
50
|
-
else
|
51
|
-
# strip not only HTML tags <tag>,
|
52
|
-
# but also Asciidoc crossreferences <<xref>>
|
53
|
-
result << x.gsub(/<[^>]*>+/, "")
|
54
|
-
end
|
55
|
-
end
|
61
|
+
result = flatten_rawtext_lines(node, result)
|
56
62
|
elsif node.respond_to?(:text)
|
57
63
|
result << node.text.gsub(/<[^>]*>+/, "")
|
58
64
|
else
|
@@ -71,7 +77,7 @@ module Asciidoctor
|
|
71
77
|
nil
|
72
78
|
end
|
73
79
|
|
74
|
-
NOKOHEAD = <<~HERE
|
80
|
+
NOKOHEAD = <<~HERE.freeze
|
75
81
|
<!DOCTYPE html SYSTEM
|
76
82
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
77
83
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
@@ -105,7 +111,6 @@ module Asciidoctor
|
|
105
111
|
out.p { |p| p << node.content }
|
106
112
|
end
|
107
113
|
end
|
108
|
-
|
109
114
|
end
|
110
115
|
end
|
111
116
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require "asciidoctor/iso/utils"
|
2
2
|
require_relative "./validate_style.rb"
|
3
|
+
require_relative "./validate_requirements.rb"
|
3
4
|
require_relative "./validate_section.rb"
|
4
5
|
require "nokogiri"
|
5
6
|
require "jing"
|
@@ -19,39 +20,83 @@ module Asciidoctor
|
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
23
|
+
def title_main_validate(root)
|
24
|
+
title_main_en = root.at("//title-main[@language='en']")
|
25
|
+
title_main_fr = root.at("//title-main[@language='fr']")
|
26
|
+
if title_main_en.nil? && !title_main_fr.nil?
|
27
|
+
warn "No English Title!"
|
28
|
+
end
|
29
|
+
if !title_main_en.nil? && title_main_fr.nil?
|
30
|
+
warn "No French Title!"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
22
34
|
def title_part_validate(root)
|
23
35
|
title_part_en = root.at("//title-part[@language='en']")
|
24
36
|
title_part_fr = root.at("//title-part[@language='fr']")
|
25
|
-
|
26
|
-
warn
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
37
|
+
(title_part_en.nil? && !title_part_fr.nil?) &&
|
38
|
+
warn("No English Title Part!")
|
39
|
+
(!title_part_en.nil? && title_part_fr.nil?) &&
|
40
|
+
warn("No French Title Part!")
|
41
|
+
end
|
42
|
+
|
43
|
+
def title_subpart_validate(root)
|
44
|
+
subpart = root.at("//bibdata/docidentifier/project-number[@subpart]")
|
45
|
+
iec = root.at("//bibdata/contributor[role/@type = 'publisher']/"\
|
46
|
+
"organization[name = 'IEC']")
|
47
|
+
warn("Subpart defined on non-IEC document!") if subpart && !iec
|
31
48
|
end
|
32
49
|
|
33
50
|
def title_names_type_validate(root)
|
34
51
|
doctypes = /International\sStandard | Technical\sSpecification |
|
35
52
|
Publicly\sAvailable\sSpecification | Technical\sReport | Guide /xi
|
36
53
|
title_main_en = root.at("//title-main[@language='en']")
|
37
|
-
if doctypes.match?
|
54
|
+
if !title_main_en.nil? && doctypes.match?(title_main_en.text)
|
38
55
|
warn "Main Title may name document type"
|
39
56
|
end
|
40
57
|
title_intro_en = root.at("//title-intro[@language='en']")
|
41
58
|
if !title_intro_en.nil? && doctypes.match?(title_intro_en.text)
|
42
|
-
warn "
|
59
|
+
warn "Title Intro may name document type"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def title_first_level_validate(root)
|
64
|
+
root.xpath(SECTIONS_XPATH).each do |s|
|
65
|
+
title = s&.at("./title")&.text || s.name
|
66
|
+
s.xpath("./subsection | ./terms").each do |ss|
|
67
|
+
subtitle = ss.at("./title")
|
68
|
+
!subtitle.nil? && !subtitle&.text&.empty? ||
|
69
|
+
warn("#{title}: each first-level subclause must have a title")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def title_all_siblings(xpath, label)
|
75
|
+
notitle = false
|
76
|
+
withtitle = false
|
77
|
+
xpath.each do |s|
|
78
|
+
sublabel = s&.at("./title")&.text || s["id"]
|
79
|
+
title_all_siblings(s.xpath("./subsection | ./terms"), sublabel)
|
80
|
+
subtitle = s.at("./title")
|
81
|
+
notitle = notitle || (!subtitle || subtitle.text.empty?)
|
82
|
+
withtitle = withtitle || !subtitle&.text&.empty?
|
43
83
|
end
|
84
|
+
notitle && withtitle &&
|
85
|
+
warn("#{label}: all subclauses must have a title, or none")
|
44
86
|
end
|
45
87
|
|
46
88
|
def title_validate(root)
|
47
89
|
title_intro_validate(root)
|
90
|
+
title_main_validate(root)
|
48
91
|
title_part_validate(root)
|
92
|
+
title_subpart_validate(root)
|
49
93
|
title_names_type_validate(root)
|
94
|
+
title_first_level_validate(root)
|
95
|
+
title_all_siblings(root.xpath(SECTIONS_XPATH), "(top level)")
|
50
96
|
end
|
51
97
|
|
52
98
|
def onlychild_clause_validate(root)
|
53
|
-
|
54
|
-
root.xpath(q).each do |c|
|
99
|
+
root.xpath("//subsection").each do |c|
|
55
100
|
next unless c.xpath("../subsection").size == 1
|
56
101
|
title = c.at("./title")
|
57
102
|
location = c["id"] || c.text[0..60] + "..."
|
@@ -60,13 +105,6 @@ module Asciidoctor
|
|
60
105
|
end
|
61
106
|
end
|
62
107
|
|
63
|
-
def iso8601_validate(root)
|
64
|
-
root.xpath("//review/@date | //revision-date").each do |d|
|
65
|
-
/^\d{8}(T\d{4,6})?$/.match? d.text or
|
66
|
-
warn "ISO style: #{d.text} is not an ISO 8601 date"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
108
|
def isosubgroup_validate(root)
|
71
109
|
root.xpath("//technical-committee/@type").each do |t|
|
72
110
|
unless %w{TC PC JTC JPC}.include? t.text
|
@@ -80,15 +118,53 @@ module Asciidoctor
|
|
80
118
|
end
|
81
119
|
end
|
82
120
|
|
121
|
+
def see_xrefs_validate(root)
|
122
|
+
root.xpath("//xref").each do |t|
|
123
|
+
# does not deal with preceding text marked up
|
124
|
+
preceding = t.at("./preceding-sibling::text()[last()]")
|
125
|
+
next unless !preceding.nil? && /\bsee\s*$/mi.match?(preceding)
|
126
|
+
(target = root.at("//*[@id = '#{t['target']}']")) || next
|
127
|
+
if target&.at("./ancestor-or-self::*[@obligation = 'normative']")
|
128
|
+
warn "ISO: 'see #{t['target']}' is pointing to a normative section"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def see_erefs_validate(root)
|
134
|
+
root.xpath("//eref").each do |t|
|
135
|
+
preceding = t.at("./preceding-sibling::text()[last()]")
|
136
|
+
next unless !preceding.nil? && /\bsee\s*$/mi.match?(preceding)
|
137
|
+
target = root.at("//*[@id = '#{t['bibitemid']}']")
|
138
|
+
if target.at("./ancestor::references"\
|
139
|
+
"[title = 'Normative References']")
|
140
|
+
warn "ISO: 'see #{t}' is pointing to a normative reference"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def termdef_warn(text, re, term, msg)
|
146
|
+
re.match?(text) && warn("ISO style: #{term}: #{msg}")
|
147
|
+
end
|
148
|
+
|
149
|
+
def termdef_style(xmldoc)
|
150
|
+
xmldoc.xpath("//term").each do |t|
|
151
|
+
para = t.at("./definition") || return
|
152
|
+
term = t.at("./preferred").text
|
153
|
+
termdef_warn(para.text, /^(the|a)\b/i, term,
|
154
|
+
"term definition starts with article")
|
155
|
+
termdef_warn(para.text, /\.$/i, term,
|
156
|
+
"term definition ends with period")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
83
160
|
def content_validate(doc)
|
84
161
|
title_validate(doc.root)
|
85
162
|
isosubgroup_validate(doc.root)
|
86
|
-
|
87
|
-
normref_validate(doc.root)
|
88
|
-
symbols_validate(doc.root)
|
89
|
-
iso8601_validate(doc.root)
|
163
|
+
section_validate(doc)
|
90
164
|
onlychild_clause_validate(doc.root)
|
91
|
-
|
165
|
+
termdef_style(doc.root)
|
166
|
+
see_xrefs_validate(doc.root)
|
167
|
+
see_erefs_validate(doc.root)
|
92
168
|
end
|
93
169
|
|
94
170
|
def schema_validate(doc, filename)
|
@@ -112,7 +188,7 @@ module Asciidoctor
|
|
112
188
|
n.elements.each do |e|
|
113
189
|
e.traverse do |e1|
|
114
190
|
next unless e1.element?
|
115
|
-
e1.each { |k,
|
191
|
+
e1.each { |k, _v| e.delete(k) }
|
116
192
|
end
|
117
193
|
end
|
118
194
|
end
|
@@ -121,7 +197,7 @@ module Asciidoctor
|
|
121
197
|
|
122
198
|
def validate(doc)
|
123
199
|
content_validate(doc)
|
124
|
-
schema_validate(formattedstr_strip(doc.dup),
|
200
|
+
schema_validate(formattedstr_strip(doc.dup),
|
125
201
|
File.join(File.dirname(__FILE__), "isostandard.rng"))
|
126
202
|
end
|
127
203
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require "asciidoctor/iso/utils"
|
2
|
+
require "nokogiri"
|
3
|
+
require "pp"
|
4
|
+
|
5
|
+
module Asciidoctor
|
6
|
+
module ISO
|
7
|
+
module Validate
|
8
|
+
REQUIREMENT_RE_STR = <<~REGEXP.freeze
|
9
|
+
\\b
|
10
|
+
( shall | (is|are)_to |
|
11
|
+
(is|are)_required_(not_)?to |
|
12
|
+
has_to |
|
13
|
+
only\\b[^.,]+\\b(is|are)_permitted |
|
14
|
+
it_is_necessary |
|
15
|
+
(needs|need)_to |
|
16
|
+
(is|are)_not_(allowed | permitted |
|
17
|
+
acceptable | permissible) |
|
18
|
+
(is|are)_not_to_be |
|
19
|
+
(need|needs)_not |
|
20
|
+
do_not )
|
21
|
+
\\b
|
22
|
+
REGEXP
|
23
|
+
REQUIREMENT_RE =
|
24
|
+
Regexp.new(REQUIREMENT_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
|
25
|
+
Regexp::IGNORECASE)
|
26
|
+
|
27
|
+
def requirement(text)
|
28
|
+
text.split(/\.\s+/).each do |t|
|
29
|
+
return t if REQUIREMENT_RE.match? t
|
30
|
+
end
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
RECOMMENDATION_RE_STR = <<~REGEXP.freeze
|
35
|
+
\\b
|
36
|
+
should |
|
37
|
+
ought_(not_)?to |
|
38
|
+
it_is_(not_)?recommended_that
|
39
|
+
\\b
|
40
|
+
REGEXP
|
41
|
+
RECOMMENDATION_RE =
|
42
|
+
Regexp.new(RECOMMENDATION_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
|
43
|
+
Regexp::IGNORECASE)
|
44
|
+
|
45
|
+
def recommendation(text)
|
46
|
+
text.split(/\.\s+/).each do |t|
|
47
|
+
return t if RECOMMENDATION_RE.match? t
|
48
|
+
end
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
PERMISSION_RE_STR = <<~REGEXP.freeze
|
53
|
+
\\b
|
54
|
+
may |
|
55
|
+
(is|are)_(permitted | allowed | permissible ) |
|
56
|
+
it_is_not_required_that |
|
57
|
+
no\\b[^.,]+\\b(is|are)_required
|
58
|
+
\\b
|
59
|
+
REGEXP
|
60
|
+
PERMISSION_RE =
|
61
|
+
Regexp.new(PERMISSION_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
|
62
|
+
Regexp::IGNORECASE)
|
63
|
+
|
64
|
+
def permission(text)
|
65
|
+
text.split(/\.\s+/).each do |t|
|
66
|
+
return t if PERMISSION_RE.match? t
|
67
|
+
end
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
POSSIBILITY_RE_STR = <<~REGEXP.freeze
|
72
|
+
\\b
|
73
|
+
can | cannot | be_able_to |
|
74
|
+
there_is_a_possibility_of |
|
75
|
+
it_is_possible_to | be_unable_to |
|
76
|
+
there_is_no_possibility_of |
|
77
|
+
it_is_not_possible_to
|
78
|
+
\\b
|
79
|
+
REGEXP
|
80
|
+
POSSIBILITY_RE =
|
81
|
+
Regexp.new(POSSIBILITY_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
|
82
|
+
Regexp::IGNORECASE)
|
83
|
+
|
84
|
+
def possibility(text)
|
85
|
+
text.split(/\.\s+/).each { |t| return t if POSSIBILITY_RE.match? t }
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
89
|
+
def external_constraint(text)
|
90
|
+
text.split(/\.\s+/).each do |t|
|
91
|
+
return t if /\b(must)\b/xi.match? t
|
92
|
+
end
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def style_no_guidance(node, text, docpart)
|
97
|
+
r = requirement(text)
|
98
|
+
style_warning(node, "#{docpart} may contain requirement", r) if r
|
99
|
+
r = permission(text)
|
100
|
+
style_warning(node, "#{docpart} may contain permission", r) if r
|
101
|
+
r = recommendation(text)
|
102
|
+
style_warning(node, "#{docpart} may contain recommendation", r) if r
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -1,28 +1,40 @@
|
|
1
1
|
require "nokogiri"
|
2
|
-
require "pp"
|
3
2
|
|
4
3
|
module Asciidoctor
|
5
4
|
module ISO
|
6
5
|
module Validate
|
6
|
+
def section_validate(doc)
|
7
|
+
foreword_validate(doc.root)
|
8
|
+
normref_validate(doc.root)
|
9
|
+
symbols_validate(doc.root)
|
10
|
+
sections_sequence_validate(doc.root)
|
11
|
+
end
|
12
|
+
|
7
13
|
def foreword_validate(root)
|
8
|
-
f = root.at("//foreword")
|
14
|
+
f = root.at("//foreword") || return
|
9
15
|
s = f.at("./subsection")
|
10
16
|
warn "ISO style: foreword contains subsections" unless s.nil?
|
11
17
|
end
|
12
18
|
|
13
19
|
def normref_validate(root)
|
14
|
-
f = root.at("//references[title = 'Normative References']")
|
15
|
-
f.at("./references")
|
16
|
-
warn
|
20
|
+
f = root.at("//references[title = 'Normative References']") || return
|
21
|
+
f.at("./references") &&
|
22
|
+
warn("ISO style: normative references contains subsections")
|
17
23
|
end
|
18
24
|
|
25
|
+
ONE_SYMBOLS_WARNING = "ISO style: only one Symbols and Abbreviated "\
|
26
|
+
"Terms section in the standard".freeze
|
27
|
+
|
28
|
+
NON_DL_SYMBOLS_WARNING = "ISO style: Symbols and Abbreviated Terms can "\
|
29
|
+
"only contain a definition list".freeze
|
30
|
+
|
19
31
|
def symbols_validate(root)
|
20
|
-
f = root.
|
21
|
-
|
22
|
-
f.
|
32
|
+
f = root.xpath("//symbols-abbrevs")
|
33
|
+
f.empty? && return
|
34
|
+
(f.size == 1) || warn(ONE_SYMBOLS_WARNING)
|
35
|
+
f.first.elements.each do |e|
|
23
36
|
unless e.name == "dl"
|
24
|
-
warn
|
25
|
-
"a definition list"
|
37
|
+
warn(NON_DL_SYMBOLS_WARNING)
|
26
38
|
return
|
27
39
|
end
|
28
40
|
end
|
@@ -38,79 +50,87 @@ module Asciidoctor
|
|
38
50
|
end
|
39
51
|
|
40
52
|
# spec of permissible section sequence
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
53
|
+
# we skip normative references, it goes to end of list
|
54
|
+
SEQ =
|
55
|
+
[
|
56
|
+
{
|
57
|
+
msg: "Initial section must be (content) Foreword",
|
58
|
+
val: [{ tag: "foreword", title: "Foreword" }],
|
59
|
+
},
|
60
|
+
{
|
61
|
+
msg: "Prefatory material must be followed by (clause) Scope",
|
62
|
+
val: [{ tag: "introduction", title: "Introduction" },
|
63
|
+
{ tag: "clause", title: "Scope" }],
|
64
|
+
},
|
65
|
+
{
|
66
|
+
msg: "Prefatory material must be followed by (clause) Scope",
|
67
|
+
val: [{ tag: "clause", title: "Scope" }],
|
68
|
+
},
|
69
|
+
{
|
70
|
+
msg: "Normative References must be followed by "\
|
71
|
+
"Terms and Definitions",
|
72
|
+
val: [
|
73
|
+
{ tag: "terms", title: "Terms and Definitions" },
|
74
|
+
{
|
75
|
+
tag: "terms",
|
76
|
+
title: "Terms, Definitions, Symbols and Abbreviated Terms"
|
77
|
+
},
|
78
|
+
],
|
79
|
+
},
|
80
|
+
].freeze
|
81
|
+
|
82
|
+
SECTIONS_XPATH =
|
83
|
+
" //foreword | //introduction | //sections/terms | "\
|
84
|
+
"//symbols-abbrevs | "\
|
85
|
+
"//sections/clause | ./references | ./annex".freeze
|
66
86
|
|
67
87
|
def sections_sequence_validate(root)
|
68
|
-
f = root.xpath(
|
69
|
-
|
70
|
-
|
71
|
-
names = f.map { |s| { tag: s.name, title: s.at("./title").text } }
|
72
|
-
names = seqcheck(names, SEQ[0][:msg], SEQ[0][:val]) or return
|
88
|
+
f = root.xpath(SECTIONS_XPATH)
|
89
|
+
names = f.map { |s| { tag: s.name, title: s&.at("./title")&.text } }
|
90
|
+
names = seqcheck(names, SEQ[0][:msg], SEQ[0][:val]) || return
|
73
91
|
n = names[0]
|
74
|
-
names = seqcheck(names, SEQ[1][:msg], SEQ[1][:val])
|
92
|
+
names = seqcheck(names, SEQ[1][:msg], SEQ[1][:val]) || return
|
75
93
|
if n == { tag: "introduction", title: "Introduction" }
|
76
|
-
names = seqcheck(names, SEQ[2][:msg], SEQ[2][:val])
|
94
|
+
names = seqcheck(names, SEQ[2][:msg], SEQ[2][:val]) || return
|
77
95
|
end
|
78
|
-
names = seqcheck(names, SEQ[3][:msg], SEQ[3][:val])
|
96
|
+
names = seqcheck(names, SEQ[3][:msg], SEQ[3][:val]) || return
|
79
97
|
n = names.shift
|
80
|
-
if n == { tag: "
|
81
|
-
n = names.shift
|
98
|
+
if n == { tag: "symbols-abbrevs", title: nil }
|
99
|
+
n = names.shift || return
|
82
100
|
end
|
83
101
|
unless n
|
84
102
|
warn "ISO style: Document must contain at least one clause"
|
85
103
|
return
|
86
104
|
end
|
87
|
-
n[:tag] == "clause"
|
88
|
-
warn
|
89
|
-
n == { tag: "clause", title: "Scope" }
|
90
|
-
warn
|
91
|
-
n = names.shift
|
105
|
+
n[:tag] == "clause" ||
|
106
|
+
warn("ISO style: Document must contain clause after Terms and Definitions")
|
107
|
+
n == { tag: "clause", title: "Scope" } &&
|
108
|
+
warn("ISO style: Scope must occur before Terms and Definitions")
|
109
|
+
n = names.shift || return
|
92
110
|
while n[:tag] == "clause"
|
93
|
-
n[:title] == "Scope"
|
94
|
-
warn
|
95
|
-
n
|
96
|
-
warn "ISO style: Symbols and Abbreviations must occur "\
|
97
|
-
"right after Terms and Definitions"
|
98
|
-
n = names.shift or return
|
111
|
+
n[:title] == "Scope" &&
|
112
|
+
warn("ISO style: Scope must occur before Terms and Definitions")
|
113
|
+
n = names.shift || return
|
99
114
|
end
|
100
|
-
unless n[:tag] == "annex"
|
115
|
+
unless n[:tag] == "annex" || n[:tag] == "references"
|
101
116
|
warn "ISO style: Only annexes and references can follow clauses"
|
102
117
|
end
|
103
118
|
while n[:tag] == "annex"
|
104
|
-
n = names.shift
|
119
|
+
n = names.shift
|
120
|
+
if n.nil?
|
121
|
+
warn("ISO style: Document must include (references) "\
|
122
|
+
"Normative References")
|
123
|
+
return
|
124
|
+
end
|
105
125
|
end
|
106
|
-
n == { tag: "references", title: "Normative References" }
|
107
|
-
warn
|
108
|
-
|
126
|
+
n == { tag: "references", title: "Normative References" } ||
|
127
|
+
warn("ISO style: Document must include (references) "\
|
128
|
+
"Normative References")
|
109
129
|
n = names.shift
|
110
|
-
n == { tag: "references", title: "Bibliography" }
|
111
|
-
warn
|
112
|
-
names.empty?
|
113
|
-
warn
|
130
|
+
n == { tag: "references", title: "Bibliography" } ||
|
131
|
+
warn("ISO style: Final section must be (references) Bibliography")
|
132
|
+
names.empty? ||
|
133
|
+
warn("ISO style: There are sections after the final Bibliography")
|
114
134
|
end
|
115
135
|
end
|
116
136
|
end
|