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