metanorma-nist 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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/.hound.yml +3 -0
  3. data/.rubocop.yml +10 -0
  4. data/.travis.yml +16 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +25 -0
  8. data/README.adoc +358 -0
  9. data/bin/console +14 -0
  10. data/bin/rspec +18 -0
  11. data/bin/setup +8 -0
  12. data/lib/asciidoctor/nist.rb +7 -0
  13. data/lib/asciidoctor/nist/biblio.rng +921 -0
  14. data/lib/asciidoctor/nist/converter.rb +271 -0
  15. data/lib/asciidoctor/nist/front.rb +150 -0
  16. data/lib/asciidoctor/nist/isodoc.rng +1063 -0
  17. data/lib/asciidoctor/nist/isostandard.rng +1071 -0
  18. data/lib/asciidoctor/nist/nist.rng +196 -0
  19. data/lib/isodoc/nist/html/commerce-logo-color.png +0 -0
  20. data/lib/isodoc/nist/html/deptofcommerce.png +0 -0
  21. data/lib/isodoc/nist/html/header.html +163 -0
  22. data/lib/isodoc/nist/html/html_nist_intro.html +46 -0
  23. data/lib/isodoc/nist/html/html_nist_titlepage.html +140 -0
  24. data/lib/isodoc/nist/html/htmlstyle.scss +1160 -0
  25. data/lib/isodoc/nist/html/logo.png +0 -0
  26. data/lib/isodoc/nist/html/nist.scss +749 -0
  27. data/lib/isodoc/nist/html/scripts.html +82 -0
  28. data/lib/isodoc/nist/html/scripts.pdf.html +70 -0
  29. data/lib/isodoc/nist/html/word_nist_intro.html +142 -0
  30. data/lib/isodoc/nist/html/word_nist_titlepage.html +247 -0
  31. data/lib/isodoc/nist/html/wordstyle.scss +1134 -0
  32. data/lib/isodoc/nist/html_convert.rb +454 -0
  33. data/lib/isodoc/nist/i18n-en.yaml +3 -0
  34. data/lib/isodoc/nist/metadata.rb +116 -0
  35. data/lib/isodoc/nist/pdf_convert.rb +456 -0
  36. data/lib/isodoc/nist/word_convert.rb +472 -0
  37. data/lib/metanorma-nist.rb +11 -0
  38. data/lib/metanorma/nist.rb +7 -0
  39. data/lib/metanorma/nist/processor.rb +43 -0
  40. data/lib/metanorma/nist/version.rb +5 -0
  41. data/metanorma-nist.gemspec +44 -0
  42. metadata +310 -0
@@ -0,0 +1,3 @@
1
+ clause: Section
2
+ annex: Appendix
3
+ draft_label: Revision
@@ -0,0 +1,116 @@
1
+ require "isodoc"
2
+
3
+ module IsoDoc
4
+ module NIST
5
+
6
+ class Metadata < IsoDoc::Metadata
7
+ def initialize(lang, script, labels)
8
+ super
9
+ set(:status, "XXX")
10
+ end
11
+
12
+ def title(isoxml, _out)
13
+ main = isoxml&.at(ns("//bibdata/title[@language='en']"))&.text
14
+ set(:doctitle, main)
15
+ end
16
+
17
+ def subtitle(isoxml, _out)
18
+ main = isoxml&.at(ns("//bibdata/subtitle[@language='en']"))&.text
19
+ set(:docsubtitle, main)
20
+ end
21
+
22
+ def author(isoxml, _out)
23
+ tc = isoxml.at(ns("//bibdata/editorialgroup/committee"))
24
+ set(:tc, tc.text.upcase) if tc
25
+ authors = isoxml.xpath(ns("//bibdata/contributor[role/@type = 'author' "\
26
+ "or xmlns:role/@type = 'editor']/person/name"))
27
+ set(:authors, extract_person_names(authors))
28
+ end
29
+
30
+ def extract_person_names(authors)
31
+ ret = []
32
+ authors.each do |a|
33
+ if a.at(ns("./completename"))
34
+ ret << a.at(ns("./completename")).text
35
+ else
36
+ fn = []
37
+ forenames = a.xpath(ns("./forename"))
38
+ forenames.each { |f| fn << f.text }
39
+ surname = a&.at(ns("./surname"))&.text
40
+ ret << fn.join(" ") + " " + surname
41
+ end
42
+ end
43
+ ret
44
+ end
45
+
46
+ def docid(isoxml, _out)
47
+ docnumber_node = isoxml.at(ns("//bibdata/docidentifier"))
48
+ docnumber = docnumber_node&.text
49
+ set(:docnumber, docnumber)
50
+ # TODO: for NIST SPs only!!!
51
+ docnumber and set(:docnumber_long,
52
+ docnumber.gsub("NIST SP", "NIST Special Publication"))
53
+ end
54
+
55
+ def status_abbr(status)
56
+ case status
57
+ when "working-draft" then "wd"
58
+ when "committee-draft" then "cd"
59
+ when "draft-standard" then "d"
60
+ else
61
+ ""
62
+ end
63
+ end
64
+
65
+ def draftinfo(draft, revdate)
66
+ draftinfo = ""
67
+ if draft
68
+ draftinfo = " #{@labels["draft_label"]} #{draft}"
69
+ #draftinfo += ", #{revdate}" if revdate
70
+ end
71
+ IsoDoc::Function::I18n::l10n(draftinfo, @lang, @script)
72
+ end
73
+
74
+ def version(isoxml, _out)
75
+ super
76
+ revdate = get[:revdate]
77
+ set(:revdate_monthyear, monthyr(revdate))
78
+ end
79
+
80
+ MONTHS = {
81
+ "01": "January",
82
+ "02": "February",
83
+ "03": "March",
84
+ "04": "April",
85
+ "05": "May",
86
+ "06": "June",
87
+ "07": "July",
88
+ "08": "August",
89
+ "09": "September",
90
+ "10": "October",
91
+ "11": "November",
92
+ "12": "December",
93
+ }.freeze
94
+
95
+ def monthyr(isodate)
96
+ m = /(?<yr>\d\d\d\d)-(?<mo>\d\d)/.match isodate
97
+ return isodate unless m && m[:yr] && m[:mo]
98
+ return "#{MONTHS[m[:mo].to_sym]} #{m[:yr]}"
99
+ end
100
+
101
+ def keywords(isoxml, _out)
102
+ keywords = []
103
+ isoxml.xpath(ns("//bibdata/keyword")).each do |kw|
104
+ keywords << kw.text
105
+ end
106
+ set(:keywords, keywords)
107
+ end
108
+
109
+ def url(xml, _out)
110
+ super
111
+ a = xml.at(ns("//bibdata/source[@type = 'email']")) and set(:email, a.text)
112
+ end
113
+
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,456 @@
1
+ require "isodoc"
2
+ require_relative "metadata"
3
+ require "fileutils"
4
+
5
+ module IsoDoc
6
+ module NIST
7
+ # A {Converter} implementation that generates PDF HTML output, and a
8
+ # document schema encapsulation of the document for validation
9
+ class PdfConvert < IsoDoc::PdfConvert
10
+ def initialize(options)
11
+ @libdir = File.dirname(__FILE__)
12
+ super
13
+ end
14
+ def convert1(docxml, filename, dir)
15
+ FileUtils.cp html_doc_path('logo.png'), "logo.png"
16
+ FileUtils.cp html_doc_path('commerce-logo-color.png'), "commerce-logo-color.png"
17
+ @files_to_delete << "logo.png"
18
+ @files_to_delete << "commerce-logo-color.png"
19
+ super
20
+ end
21
+
22
+ def default_fonts(options)
23
+ {
24
+ bodyfont: (options[:script] == "Hans" ? '"SimSun",serif' : '"Libre Baskerville",serif'),
25
+ headerfont: (options[:script] == "Hans" ? '"SimHei",sans-serif' : '"Libre Baskerville",serif'),
26
+ monospacefont: '"Space Mono",monospace'
27
+ }
28
+ end
29
+
30
+ def default_file_locations(_options)
31
+ {
32
+ htmlstylesheet: html_doc_path("htmlstyle.scss"),
33
+ htmlcoverpage: html_doc_path("html_nist_titlepage.html"),
34
+ htmlintropage: html_doc_path("html_nist_intro.html"),
35
+ scripts_pdf: html_doc_path("scripts.pdf.html"),
36
+ }
37
+ end
38
+
39
+ def metadata_init(lang, script, labels)
40
+ @meta = Metadata.new(lang, script, labels)
41
+ end
42
+
43
+ def html_head()
44
+ <<~HEAD.freeze
45
+ <title>{{ doctitle }}</title>
46
+ <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
47
+
48
+ <!--TOC script import-->
49
+ <script type="text/javascript" src="https://cdn.rawgit.com/jgallen23/toc/0.3.2/dist/toc.min.js"></script>
50
+
51
+ <!--Google fonts-->
52
+ <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,600,600i" rel="stylesheet">
53
+ <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i|Space+Mono:400,700" rel="stylesheet" />
54
+ <link href="https://fonts.googleapis.com/css?family=Libre+Baskerville:400,400i,700,700i" rel="stylesheet">
55
+
56
+ <!--Font awesome import for the link icon-->
57
+ <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.8/css/solid.css" integrity="sha384-v2Tw72dyUXeU3y4aM2Y0tBJQkGfplr39mxZqlTBDUZAb9BGoC40+rdFCG0m10lXk" crossorigin="anonymous">
58
+ <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.8/css/fontawesome.css" integrity="sha384-q3jl8XQu1OpdLgGFvNRnPdj5VIlCvgsDQTQB6owSOHWlAurxul7f+JpUOVdAiJ5P" crossorigin="anonymous">
59
+ <style class="anchorjs"></style>
60
+ HEAD
61
+ end
62
+
63
+ def make_body(xml, docxml)
64
+ body_attr = { lang: "EN-US", link: "blue", vlink: "#954F72", "xml:lang": "EN-US", class: "container" }
65
+ xml.body **body_attr do |body|
66
+ make_body1(body, docxml)
67
+ make_body2(body, docxml)
68
+ make_body3(body, docxml)
69
+ end
70
+ end
71
+
72
+ def html_toc(docxml)
73
+ docxml
74
+ end
75
+
76
+ def make_body3(body, docxml)
77
+ body.div **{ class: "main-section" } do |div3|
78
+ abstract docxml, div3
79
+ keywords docxml, div3
80
+ preface docxml, div3
81
+ middle docxml, div3
82
+ footnotes div3
83
+ comments div3
84
+ end
85
+ end
86
+
87
+ def abstract(isoxml, out)
88
+ f = isoxml.at(ns("//preface/abstract")) || return
89
+ page_break(out)
90
+ out.div **attr_code(id: f["id"]) do |s|
91
+ clause_name(nil, @abstract_lbl, s, class: "AbstractTitle")
92
+ f.elements.each { |e| parse(e, s) unless e.name == "title" }
93
+ end
94
+ end
95
+
96
+ def keywords(_docxml, out)
97
+ kw = @meta.get[:keywords]
98
+ kw.empty? and return
99
+ out.div **{ class: "Section3" } do |div|
100
+ clause_name(nil, "Keywords", div, class: "IntroTitle")
101
+ div.p kw.sort.join("; ")
102
+ end
103
+ end
104
+
105
+ FRONT_CLAUSE = "//*[parent::preface][not(local-name() = 'abstract')]".freeze
106
+
107
+ def preface(isoxml, out)
108
+ isoxml.xpath(ns(FRONT_CLAUSE)).each do |c|
109
+ foreword(isoxml, out) and next if c.name == "foreword"
110
+ next if skip_render(c, isoxml)
111
+ out.div **attr_code(id: c["id"]) do |s|
112
+ clause_name(get_anchors[c['id']][:label],
113
+ c&.at(ns("./title"))&.content, s, nil)
114
+ c.elements.reject { |c1| c1.name == "title" }.each do |c1|
115
+ parse(c1, s)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ def skip_render(c, isoxml)
122
+ return false unless c.name == "reviewernote"
123
+ status = isoxml&.at(ns("//bibdata/status"))&.text
124
+ return true if status.nil?
125
+ return ["published", "withdrawn"].include? status
126
+ end
127
+
128
+ def term_defs_boilerplate(div, source, term, preface)
129
+ if source.empty? && term.nil?
130
+ div << @no_terms_boilerplate
131
+ else
132
+ div << term_defs_boilerplate_cont(source, term)
133
+ end
134
+ end
135
+
136
+ def i18n_init(lang, script)
137
+ super
138
+ end
139
+
140
+ def fileloc(loc)
141
+ File.join(File.dirname(__FILE__), loc)
142
+ end
143
+
144
+ def cleanup(docxml)
145
+ super
146
+ term_cleanup(docxml)
147
+ requirement_cleanup(docxml)
148
+ end
149
+
150
+ def term_cleanup(docxml)
151
+ docxml.xpath("//p[@class = 'Terms']").each do |d|
152
+ h2 = d.at("./preceding-sibling::*[@class = 'TermNum'][1]")
153
+ h2.add_child("&nbsp;")
154
+ h2.add_child(d.remove)
155
+ end
156
+ docxml
157
+ end
158
+
159
+ def requirement_cleanup(docxml)
160
+ docxml.xpath("//div[@class = 'recommend'][title]").each do |d|
161
+ title = d.at("./title")
162
+ title.name = "b"
163
+ n = title.next_element
164
+ n&.children&.first&.add_previous_sibling(" ")
165
+ n&.children&.first&.add_previous_sibling(title.remove)
166
+ end
167
+ docxml
168
+ end
169
+
170
+ def figure_parse(node, out)
171
+ return pseudocode_parse(node, out) if node["type"] == "pseudocode"
172
+ super
173
+ end
174
+
175
+ def pseudocode_parse(node, out)
176
+ @in_figure = true
177
+ name = node.at(ns("./name"))
178
+ out.div **attr_code(id: node["id"], class: "pseudocode") do |div|
179
+ node.children.each do |n|
180
+ parse(n, div) unless n.name == "name"
181
+ end
182
+ figure_name_parse(node, div, name) if name
183
+ end
184
+ @in_figure = false
185
+ end
186
+
187
+ def dl_parse(node, out)
188
+ return glossary_parse(node, out) if node["type"] == "glossary"
189
+ super
190
+ end
191
+
192
+ def glossary_parse(node, out)
193
+ out.dl **attr_code(id: node["id"], class: "glossary") do |v|
194
+ node.elements.select { |n| dt_dd? n }.each_slice(2) do |dt, dd|
195
+ v.dt **attr_code(id: dt["id"]) do |term|
196
+ dt_parse(dt, term)
197
+ end
198
+ v.dd **attr_code(id: dd["id"]) do |listitem|
199
+ dd.children.each { |n| parse(n, listitem) }
200
+ end
201
+ end
202
+ end
203
+ node.elements.reject { |n| dt_dd? n }.each { |n| parse(n, out) }
204
+ end
205
+
206
+ def error_parse(node, out)
207
+ case node.name
208
+ when "nistvariable" then nistvariable_parse(node, out)
209
+ when "recommendation" then recommendation_parse(node, out)
210
+ when "requirement" then requirement_parse(node, out)
211
+ when "permission" then permission_parse(node, out)
212
+ when "errata" then errata_parse(node, out)
213
+ else
214
+ super
215
+ end
216
+ end
217
+
218
+ def nistvariable_parse(node, out)
219
+ out.span **{class: "nistvariable"} do |s|
220
+ node.children.each { |n| parse(n, s) }
221
+ end
222
+ end
223
+
224
+ def recommendation_parse(node, out)
225
+ name = node["type"]
226
+ out.div **{ class: "recommend" } do |t|
227
+ t.title { |b| b << "Recommendation #{get_anchors[node['id']][:label]}:" }
228
+ node.children.each do |n|
229
+ parse(n, t)
230
+ end
231
+ end
232
+ end
233
+
234
+ def requirement_parse(node, out)
235
+ name = node["type"]
236
+ out.div **{ class: "recommend" } do |t|
237
+ t.title { |b| b << "Requirement #{get_anchors[node['id']][:label]}:" }
238
+ node.children.each do |n|
239
+ parse(n, t)
240
+ end
241
+ end
242
+ end
243
+
244
+ def permission_parse(node, out)
245
+ name = node["type"]
246
+ out.div **{ class: "recommend" } do |t|
247
+ t.title { |b| b << "Permission #{get_anchors[node['id']][:label]}:" }
248
+ node.children.each do |n|
249
+ parse(n, t)
250
+ end
251
+ end
252
+ end
253
+
254
+ def errata_parse(node, out)
255
+ out.table **make_table_attr(node) do |t|
256
+ t.thead do |h|
257
+ h.tr do |tr|
258
+ %w(Date Type Change Pages).each do |hdr|
259
+ tr.th hdr
260
+ end
261
+ end
262
+ end
263
+ t.tbody do |b|
264
+ node.xpath(ns("./row")).each do |row|
265
+ b.tr do |tr|
266
+ tr.td do |td|
267
+ row&.at(ns("./date"))&.children.each do |n|
268
+ parse(n, td)
269
+ end
270
+ end
271
+ tr.td do |td|
272
+ row&.at(ns("./type"))&.children.each do |n|
273
+ parse(n, td)
274
+ end
275
+ end
276
+ tr.td do |td|
277
+ row&.at(ns("./change"))&.children.each do |n|
278
+ parse(n, td)
279
+ end
280
+ end
281
+ tr.td do |td|
282
+ row&.at(ns("./pages"))&.children.each do |n|
283
+ parse(n, td)
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
290
+ end
291
+
292
+ MIDDLE_CLAUSE = "//clause[parent::sections]|//terms[parent::sections]".freeze
293
+
294
+ def middle(isoxml, out)
295
+ middle_title(out)
296
+ clause isoxml, out
297
+ annex isoxml, out
298
+ bibliography isoxml, out
299
+ end
300
+
301
+ def bibliography(isoxml, out)
302
+ f = isoxml.at(ns("//bibliography/clause | //bibliography/references")) || return
303
+ page_break(out)
304
+ isoxml.xpath(ns("//bibliography/clause | //bibliography/references")).each do |f|
305
+ out.div do |div|
306
+ div.h1 **{ class: "Section3" } do |h1|
307
+ f&.at(ns("./title"))&.children.each { |n| parse(n, h1) }
308
+ end
309
+ f.elements.reject do |e|
310
+ ["reference", "title", "bibitem"].include? e.name
311
+ end.each { |e| parse(e, div) }
312
+ biblio_list(f, div, false)
313
+ end
314
+ end
315
+ end
316
+
317
+ def info(isoxml, out)
318
+ @meta.keywords isoxml, out
319
+ super
320
+ end
321
+
322
+ SECTIONS_XPATH =
323
+ "//foreword | //introduction | //reviewnote | //execsummary | //annex | "\
324
+ "//sections/clause | //bibliography/references | "\
325
+ "//bibliography/clause".freeze
326
+
327
+ def initial_anchor_names(d)
328
+ d.xpath("//xmlns:preface/child::*").each do |c|
329
+ preface_names(c)
330
+ end
331
+ sequential_asset_names(d.xpath("//xmlns:preface/child::*"))
332
+ clause_names(d, 0)
333
+ middle_section_asset_names(d)
334
+ termnote_anchor_names(d)
335
+ termexample_anchor_names(d)
336
+ end
337
+
338
+ def back_anchor_names(docxml)
339
+ docxml.xpath(ns("//annex")).each_with_index do |c, i|
340
+ annex_names(c, (65 + i).chr.to_s)
341
+ end
342
+ docxml.xpath(ns("//bibliography/clause | "\
343
+ "//bibliography/references")).each do |b|
344
+ preface_names(b)
345
+ end
346
+ docxml.xpath(ns("//bibitem[not(ancestor::bibitem)]")).each do |ref|
347
+ reference_names(ref)
348
+ end
349
+ end
350
+
351
+
352
+ def prefaceprefix(nodes)
353
+ i = 0
354
+ nodes.each do |n|
355
+ case n.name
356
+ when "executivesummary" then @anchors[n["id"]][:prefix] = "ES"
357
+ when "abstract" then @anchors[n["id"]][:prefix] = "ABS"
358
+ when "reviewernote" then @anchors[n["id"]][:prefix] = "NTR"
359
+ else
360
+ @anchors[n["id"]][:prefix] = "PR" + i.to_s
361
+ i += 1
362
+ end
363
+ end
364
+ end
365
+
366
+ def middle_section_asset_names(d)
367
+ prefaceprefix(d.xpath("//xmlns:preface/child::*"))
368
+ d.xpath("//xmlns:preface/child::*").each do |s|
369
+ hierarchical_asset_names(s, @anchors[s["id"]][:prefix])
370
+ end
371
+ d.xpath("//xmlns:sections/child::*").each do |s|
372
+ hierarchical_asset_names(s, @anchors[s["id"]][:label])
373
+ end
374
+ end
375
+
376
+ def hierarchical_asset_names(clause, num)
377
+ super
378
+ hierarchical_permission_names(clause, num)
379
+ hierarchical_requirement_names(clause, num)
380
+ hierarchical_recommendation_names(clause, num)
381
+ end
382
+
383
+ def hierarchical_permission_names(clause, num)
384
+ clause.xpath(ns(".//permission")).each_with_index do |t, i|
385
+ next if t["id"].nil? || t["id"].empty?
386
+ @anchors[t["id"]] = anchor_struct("#{num}.#{i + 1}",
387
+ t, "Permission", "permission")
388
+ end
389
+ end
390
+
391
+ def hierarchical_requirement_names(clause, num)
392
+ clause.xpath(ns(".//requirement")).each_with_index do |t, i|
393
+ next if t["id"].nil? || t["id"].empty?
394
+ @anchors[t["id"]] = anchor_struct("#{num}.#{i + 1}",
395
+ t, "Requirement", "requirement")
396
+ end
397
+ end
398
+
399
+ def hierarchical_recommendation_names(clause, num)
400
+ clause.xpath(ns(".//recommendation")).each_with_index do |t, i|
401
+ next if t["id"].nil? || t["id"].empty?
402
+ @anchors[t["id"]] = anchor_struct("#{num}.#{i + 1}",
403
+ t, "Recommendation", "recommendation")
404
+ end
405
+ end
406
+
407
+ def clause_names(docxml, sect_num)
408
+ q = "//xmlns:sections/child::*"
409
+ docxml.xpath(q).each_with_index do |c, i|
410
+ section_names(c, (i + sect_num), 1)
411
+ end
412
+ end
413
+
414
+ def get_linkend(node)
415
+ link = anchor_linkend(node, docid_l10n(node["target"] || "[#{node['citeas']}]"))
416
+ link += eref_localities(node.xpath(ns("./locality")), link)
417
+ contents = node.children.select { |c| c.name != "locality" }
418
+ return link if contents.nil? || contents.empty?
419
+ Nokogiri::XML::NodeSet.new(node.document, contents).to_xml
420
+ # so not <origin bibitemid="ISO7301" citeas="ISO 7301">
421
+ # <locality type="section"><reference>3.1</reference></locality></origin>
422
+ end
423
+
424
+ def load_yaml(lang, script)
425
+ y = if @i18nyaml then YAML.load_file(@i18nyaml)
426
+ elsif lang == "en"
427
+ YAML.load_file(File.join(File.dirname(__FILE__), "i18n-en.yaml"))
428
+ else
429
+ YAML.load_file(File.join(File.dirname(__FILE__), "i18n-en.yaml"))
430
+ end
431
+ super.merge(y)
432
+ end
433
+
434
+ def annex_name_lbl(clause, num)
435
+ l10n("<b>#{@annex_lbl} #{num}</b>")
436
+ end
437
+
438
+ def annex_name(annex, name, div)
439
+ div.h1 **{ class: "Annex" } do |t|
440
+ t << "#{get_anchors[annex['id']][:label]} &mdash; "
441
+ t.b do |b|
442
+ name&.children&.each { |c2| parse(c2, b) }
443
+ end
444
+ end
445
+ end
446
+
447
+ def hiersep
448
+ "-"
449
+ end
450
+
451
+
452
+
453
+ end
454
+ end
455
+ end
456
+