coradoc 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.docker/Dockerfile +19 -0
- data/.docker/Makefile +35 -0
- data/.docker/docker-compose.yml +14 -0
- data/.docker/readme.md +61 -0
- data/.hound.yml +5 -0
- data/.rubocop.yml +10 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/Makefile +1 -0
- data/README.md +69 -0
- data/Rakefile +7 -0
- data/coradoc.gemspec +38 -0
- data/docker-compose.yml +1 -0
- data/lib/coradoc/document/admonition.rb +11 -0
- data/lib/coradoc/document/attribute.rb +27 -0
- data/lib/coradoc/document/author.rb +11 -0
- data/lib/coradoc/document/base.rb +17 -0
- data/lib/coradoc/document/bibdata.rb +24 -0
- data/lib/coradoc/document/block.rb +34 -0
- data/lib/coradoc/document/header.rb +11 -0
- data/lib/coradoc/document/list.rb +14 -0
- data/lib/coradoc/document/paragraph.rb +19 -0
- data/lib/coradoc/document/revision.rb +11 -0
- data/lib/coradoc/document/section.rb +28 -0
- data/lib/coradoc/document/table.rb +20 -0
- data/lib/coradoc/document/text_element.rb +22 -0
- data/lib/coradoc/document/title.rb +33 -0
- data/lib/coradoc/document.rb +46 -0
- data/lib/coradoc/legacy_parser.rb +200 -0
- data/lib/coradoc/oscal.rb +85 -0
- data/lib/coradoc/parser/asciidoc/base.rb +84 -0
- data/lib/coradoc/parser/asciidoc/bibdata.rb +19 -0
- data/lib/coradoc/parser/asciidoc/content.rb +143 -0
- data/lib/coradoc/parser/asciidoc/header.rb +30 -0
- data/lib/coradoc/parser/asciidoc/section.rb +60 -0
- data/lib/coradoc/parser/base.rb +32 -0
- data/lib/coradoc/parser.rb +11 -0
- data/lib/coradoc/transformer.rb +178 -0
- data/lib/coradoc/version.rb +5 -0
- data/lib/coradoc.rb +19 -0
- data/todo.md +10 -0
- metadata +174 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module Coradoc
|
2
|
+
module Document
|
3
|
+
class Title
|
4
|
+
attr_reader :id, :content, :line_break
|
5
|
+
|
6
|
+
def initialize(content, level, options = {})
|
7
|
+
@level_str = level
|
8
|
+
@content = content.to_s
|
9
|
+
@id = options.fetch(:id, nil).to_s
|
10
|
+
@line_break = options.fetch(:line_break, "")
|
11
|
+
end
|
12
|
+
|
13
|
+
def level
|
14
|
+
@level ||= level_from_string
|
15
|
+
end
|
16
|
+
|
17
|
+
alias :text :content
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :level_str
|
22
|
+
|
23
|
+
def level_from_string
|
24
|
+
case @level_str.length
|
25
|
+
when 2 then :heading_two
|
26
|
+
when 3 then :heading_three
|
27
|
+
when 4 then :heading_four
|
28
|
+
else :unknown
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "coradoc/document/title"
|
2
|
+
require "coradoc/document/block"
|
3
|
+
require "coradoc/document/section"
|
4
|
+
require "coradoc/document/attribute"
|
5
|
+
require "coradoc/document/admonition"
|
6
|
+
require "coradoc/document/text_element"
|
7
|
+
require "coradoc/document/author"
|
8
|
+
require "coradoc/document/revision"
|
9
|
+
require "coradoc/document/header"
|
10
|
+
require "coradoc/document/bibdata"
|
11
|
+
require "coradoc/document/paragraph"
|
12
|
+
require "coradoc/document/table"
|
13
|
+
require "coradoc/document/list"
|
14
|
+
|
15
|
+
module Coradoc
|
16
|
+
module Document
|
17
|
+
class << self
|
18
|
+
attr_reader :header, :bibdata, :sections
|
19
|
+
|
20
|
+
def from_adoc(filename)
|
21
|
+
ast = Coradoc::Parser.parse(filename)
|
22
|
+
Coradoc::Transformer.transform(ast)
|
23
|
+
end
|
24
|
+
|
25
|
+
def from_ast(elements)
|
26
|
+
@sections = []
|
27
|
+
|
28
|
+
elements.each do |element|
|
29
|
+
if element.is_a?(Coradoc::Document::Bibdata)
|
30
|
+
@bibdata = element
|
31
|
+
end
|
32
|
+
|
33
|
+
if element.is_a?(Coradoc::Document::Header)
|
34
|
+
@header = element
|
35
|
+
end
|
36
|
+
|
37
|
+
if element.is_a?(Coradoc::Document::Section)
|
38
|
+
@sections << element
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
self
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require "parslet"
|
2
|
+
require "parslet/convenience"
|
3
|
+
|
4
|
+
module Coradoc
|
5
|
+
class LegacyParser < Parslet::Parser
|
6
|
+
root :document
|
7
|
+
|
8
|
+
# Basic Elements
|
9
|
+
rule(:space) { match('\s') }
|
10
|
+
rule(:space?) { spaces.maybe }
|
11
|
+
rule(:spaces) { space.repeat(1) }
|
12
|
+
rule(:empty_line) { match("^\n") }
|
13
|
+
|
14
|
+
rule(:endline) { newline | any.absent? }
|
15
|
+
rule(:newline) { match["\r\n"].repeat(1) }
|
16
|
+
rule(:line_ending) { match("[\n]") }
|
17
|
+
|
18
|
+
rule(:inline_element) { text }
|
19
|
+
rule(:text) { match("[^\n]").repeat(1) }
|
20
|
+
rule(:digits) { match("[0-9]").repeat(1) }
|
21
|
+
rule(:word) { match("[a-zA-Z0-9_-]").repeat(1) }
|
22
|
+
rule(:special_character) { match("^[*_:=-]") | str("[#") }
|
23
|
+
|
24
|
+
rule(:text_line) do
|
25
|
+
special_character.absent? >>
|
26
|
+
match("[^\n]").repeat(1).as(:text) >>
|
27
|
+
line_ending.as(:break)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Common Helpers
|
31
|
+
rule(:words) { word >> (space? >> word).repeat }
|
32
|
+
rule(:email) { word >> str("@") >> word >> str(".") >> word }
|
33
|
+
|
34
|
+
# Document
|
35
|
+
rule(:document) do
|
36
|
+
(
|
37
|
+
bibdata.repeat(1).as(:bibdata) |
|
38
|
+
section.as(:section) |
|
39
|
+
header.as(:header) |
|
40
|
+
block_with_title.as(:block) |
|
41
|
+
empty_line.repeat(1) |
|
42
|
+
any.as(:unparsed)
|
43
|
+
).repeat(1).as(:document)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Header
|
47
|
+
rule(:header) do
|
48
|
+
match("=") >> space? >> text.as(:title) >> newline >>
|
49
|
+
author.maybe.as(:author) >> revision.maybe.as(:revision)
|
50
|
+
end
|
51
|
+
|
52
|
+
rule(:author) do
|
53
|
+
words.as(:first_name) >> str(",") >> space? >> words.as(:last_name) >>
|
54
|
+
space? >> str("<") >> email.as(:email) >> str(">") >> endline
|
55
|
+
end
|
56
|
+
|
57
|
+
rule(:revision) do
|
58
|
+
(word >> (str(".") >> word).maybe).as(:number) >>
|
59
|
+
str(",") >> space? >> word.as(:date) >>
|
60
|
+
str(":") >> space? >> words.as(:remark) >> newline
|
61
|
+
end
|
62
|
+
|
63
|
+
# Bibdata
|
64
|
+
rule(:bibdata) do
|
65
|
+
str(":") >> attribute_name.as(:key) >> str(":") >>
|
66
|
+
space? >> attribute_value.as(:value) >> endline
|
67
|
+
end
|
68
|
+
|
69
|
+
# Section
|
70
|
+
rule(:section) do
|
71
|
+
heading.as(:title) >>
|
72
|
+
(list.as(:list) |
|
73
|
+
blocks.as(:blocks) |
|
74
|
+
paragraphs.as(:paragraphs)).maybe
|
75
|
+
end
|
76
|
+
|
77
|
+
# Heading
|
78
|
+
rule(:heading) do
|
79
|
+
(anchor_name >> newline).maybe >>
|
80
|
+
match("=").repeat(2, 8).as(:level) >>
|
81
|
+
space? >> text.as(:text) >> endline.as(:break)
|
82
|
+
end
|
83
|
+
|
84
|
+
rule(:anchor_name) { str("[#") >> keyword.as(:name) >> str("]") }
|
85
|
+
|
86
|
+
# List
|
87
|
+
rule(:list) do
|
88
|
+
unnumbered_list.as(:unnumbered) |
|
89
|
+
definition_list.as(:definition) | numbered_list.as(:numbered)
|
90
|
+
end
|
91
|
+
|
92
|
+
rule(:numbered_list) { nlist_item.repeat(1) }
|
93
|
+
rule(:unnumbered_list) { ulist_item.repeat(1) }
|
94
|
+
rule(:definition_list) { dlist_item.repeat(1) }
|
95
|
+
|
96
|
+
rule(:nlist_item) { match("\.") >> space >> text_line }
|
97
|
+
rule(:ulist_item) { match("\\*") >> space >> text_line }
|
98
|
+
rule(:dlist_item) do
|
99
|
+
str("term") >> space >> digits >> str("::") >> space >> text_line
|
100
|
+
end
|
101
|
+
|
102
|
+
# Block
|
103
|
+
rule(:block) { simple_block | open_block }
|
104
|
+
rule(:attribute_name) { keyword }
|
105
|
+
rule(:attribute_value) { text | str("") }
|
106
|
+
rule(:keyword) { match("[a-zA-Z0-9_-]").repeat(1) }
|
107
|
+
rule(:blocks) { block.repeat(1) >> (newline >> block.repeat(1)).maybe }
|
108
|
+
|
109
|
+
rule(:block_title) { str(".") >> text.as(:title) >> line_ending }
|
110
|
+
rule(:block_type) { str("[") >> keyword.as(:type) >> str("]") >> newline }
|
111
|
+
|
112
|
+
rule(:block_attribute) do
|
113
|
+
str("[") >> keyword.as(:key) >>
|
114
|
+
str("=") >> keyword.as(:value) >> str("]")
|
115
|
+
end
|
116
|
+
|
117
|
+
rule(:simple_block) do
|
118
|
+
block_attribute.as(:attributes) >> newline >>
|
119
|
+
text_line.repeat(1).as(:lines)
|
120
|
+
end
|
121
|
+
|
122
|
+
rule(:open_block) do
|
123
|
+
block_title >>
|
124
|
+
block_type >>
|
125
|
+
str("--").as(:delimiter) >> newline >>
|
126
|
+
text_line.repeat.as(:lines) >>
|
127
|
+
str("--") >> line_ending
|
128
|
+
end
|
129
|
+
|
130
|
+
rule(:example_block) do
|
131
|
+
block_title >>
|
132
|
+
block_type >>
|
133
|
+
str("====").as(:delimiter) >> newline >>
|
134
|
+
text_line.repeat(1).as(:lines) >>
|
135
|
+
str("====") >> newline
|
136
|
+
end
|
137
|
+
|
138
|
+
rule(:sidebar_block) do
|
139
|
+
block_title >>
|
140
|
+
block_type.maybe >>
|
141
|
+
str("****").as(:delimiter) >> newline >>
|
142
|
+
text_line.repeat(1).as(:lines) >>
|
143
|
+
str("****") >> newline
|
144
|
+
end
|
145
|
+
|
146
|
+
rule(:source_block) do
|
147
|
+
block_title >>
|
148
|
+
str("----").as(:delimiter) >> newline >>
|
149
|
+
text_line.repeat(1).as(:lines) >>
|
150
|
+
str("----") >> newline
|
151
|
+
end
|
152
|
+
|
153
|
+
rule(:quote_block) do
|
154
|
+
block_title >>
|
155
|
+
str("____").as(:delimiter) >> newline >>
|
156
|
+
text_line.repeat.as(:lines) >>
|
157
|
+
str("____") >> newline
|
158
|
+
end
|
159
|
+
|
160
|
+
rule(:block_with_title) do
|
161
|
+
example_block | quote_block |
|
162
|
+
sidebar_block | source_block | open_block |
|
163
|
+
(block_title >> text_line.repeat(1).as(:lines))
|
164
|
+
end
|
165
|
+
|
166
|
+
# Paragraph
|
167
|
+
rule(:paragraphs) do
|
168
|
+
paragraph >> (line_ending.repeat(1) >> paragraph).repeat.maybe
|
169
|
+
end
|
170
|
+
|
171
|
+
rule(:paragraph) { admonitions.repeat(1) | text_line.repeat(1) }
|
172
|
+
|
173
|
+
# Admonition
|
174
|
+
rule(:admonition_type) do
|
175
|
+
(str("NOTE") |
|
176
|
+
str("TIP") |
|
177
|
+
str("EDITOR") |
|
178
|
+
str("DANGER") |
|
179
|
+
str("CAUTION") |
|
180
|
+
str("WARNING") |
|
181
|
+
str("IMPORTANT")).as(:type)
|
182
|
+
end
|
183
|
+
|
184
|
+
rule(:admonitions) { admonition.as(:admonition).repeat(1) }
|
185
|
+
rule(:admonition) { inline_admonition | block_admonition }
|
186
|
+
|
187
|
+
rule(:inline_admonition) do
|
188
|
+
admonition_type >> str(":") >> space? >> text_line >> newline
|
189
|
+
end
|
190
|
+
|
191
|
+
rule(:block_admonition) do
|
192
|
+
str("[") >> admonition_type >> str("]") >> newline >> text_line >> newline
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.parse(filename)
|
196
|
+
content = File.read(filename)
|
197
|
+
new.parse_with_debug(content)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Coradoc
|
4
|
+
class Oscal
|
5
|
+
attr_reader :_doc
|
6
|
+
|
7
|
+
def initialize(document)
|
8
|
+
@_doc = document
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.to_oscal(document)
|
12
|
+
new(document).to_oscal
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_oscal
|
16
|
+
{"metadata" => _doc.bibdata.to_hash, "groups" => sections_as_groups}
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Organizational controls
|
22
|
+
def sections_as_groups
|
23
|
+
_doc.sections.map do |section|
|
24
|
+
Hash.new.tap do |hash|
|
25
|
+
hash["id"] = section.id
|
26
|
+
hash["title"] = section.title&.content
|
27
|
+
hash["controls"] = build_oscal_controls(section.sections)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Clause 5.1
|
33
|
+
def build_oscal_controls(sections)
|
34
|
+
sections.map do |section|
|
35
|
+
Hash.new.tap do |hash|
|
36
|
+
hash["id"] = section.id
|
37
|
+
hash["props"] = build_oscal_props(section.glossaries.items)
|
38
|
+
hash["parts"] = build_oscal_parts(section.sections)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Control, Purpose, Guidance
|
44
|
+
def build_oscal_parts(sections)
|
45
|
+
sections.map do |section|
|
46
|
+
Hash.new.tap do |hash|
|
47
|
+
hash["id"] = section.id
|
48
|
+
hash["name"] = section.title&.text
|
49
|
+
hash["prose"] = build_oscal_prose(section.content)
|
50
|
+
hash["parts"] = build_oscal_sub_parts(section.contents)
|
51
|
+
end.compact
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def build_oscal_sub_parts(contents)
|
56
|
+
if contents.length > 1
|
57
|
+
parts = contents.select do |content|
|
58
|
+
content if content.is_a?(Coradoc::Document::Paragraph)
|
59
|
+
end
|
60
|
+
|
61
|
+
parts.map do |part|
|
62
|
+
Hash.new.tap do |hash|
|
63
|
+
hash["id"] = part.id
|
64
|
+
hash["prose"] = part.texts.join(" ")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def build_oscal_props(attributes)
|
71
|
+
attributes.map do |attribute|
|
72
|
+
Hash.new.tap do |hash|
|
73
|
+
hash["name"] = attribute.key.to_s.downcase
|
74
|
+
hash["value"] = attribute.value
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def build_oscal_prose(paragraph)
|
80
|
+
if paragraph
|
81
|
+
paragraph.texts.join(" ")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Coradoc
|
2
|
+
module Parser
|
3
|
+
module Asciidoc
|
4
|
+
module Base
|
5
|
+
def space?
|
6
|
+
space.maybe
|
7
|
+
end
|
8
|
+
|
9
|
+
def space
|
10
|
+
match('\s').repeat(1)
|
11
|
+
end
|
12
|
+
|
13
|
+
def text
|
14
|
+
match("[^\n]").repeat(1)
|
15
|
+
end
|
16
|
+
|
17
|
+
def line_ending
|
18
|
+
match("[\n]")
|
19
|
+
end
|
20
|
+
|
21
|
+
def endline
|
22
|
+
newline | any.absent?
|
23
|
+
end
|
24
|
+
|
25
|
+
def newline
|
26
|
+
match["\r\n"].repeat(1)
|
27
|
+
end
|
28
|
+
|
29
|
+
# def line_break
|
30
|
+
# match["\r\n"]
|
31
|
+
# end
|
32
|
+
|
33
|
+
def keyword
|
34
|
+
(match("[a-zA-Z0-9_-]") | str(".")).repeat(1)
|
35
|
+
end
|
36
|
+
|
37
|
+
# def text_line
|
38
|
+
# special_character.absent? >>
|
39
|
+
# match("[^\n]").repeat(1).as(:text) >>
|
40
|
+
# line_ending.as(:break)
|
41
|
+
# end
|
42
|
+
|
43
|
+
# rule(:space) { match('\s') }
|
44
|
+
# rule(:space?) { spaces.maybe }
|
45
|
+
# rule(:spaces) { space.repeat(1) }
|
46
|
+
def empty_line
|
47
|
+
match("^\n")
|
48
|
+
end
|
49
|
+
#
|
50
|
+
|
51
|
+
#
|
52
|
+
# rule(:inline_element) { text }
|
53
|
+
# rule(:text) { match("[^\n]").repeat(1) }
|
54
|
+
def digits
|
55
|
+
match("[0-9]").repeat(1)
|
56
|
+
end
|
57
|
+
|
58
|
+
def word
|
59
|
+
match("[a-zA-Z0-9_-]").repeat(1)
|
60
|
+
end
|
61
|
+
|
62
|
+
def words
|
63
|
+
word >> (space? >> word).repeat
|
64
|
+
end
|
65
|
+
|
66
|
+
def email
|
67
|
+
word >> str("@") >> word >> str(".") >> word
|
68
|
+
end
|
69
|
+
|
70
|
+
def attribute_name
|
71
|
+
match("[a-zA-Z0-9_-]").repeat(1)
|
72
|
+
end
|
73
|
+
|
74
|
+
def attribute_value
|
75
|
+
text | str("")
|
76
|
+
end
|
77
|
+
|
78
|
+
def special_character
|
79
|
+
match("^[*_:=-]") | str("[#") | str("[[")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Coradoc
|
2
|
+
module Parser
|
3
|
+
module Asciidoc
|
4
|
+
module Bibdata
|
5
|
+
include Coradoc::Parser::Asciidoc::Base
|
6
|
+
|
7
|
+
# Bibdata
|
8
|
+
def bibdatas
|
9
|
+
bibdata.repeat(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
def bibdata
|
13
|
+
str(":") >> attribute_name.as(:key) >> str(":") >>
|
14
|
+
space? >> attribute_value.as(:value) >> line_ending
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Coradoc
|
2
|
+
module Parser
|
3
|
+
module Asciidoc
|
4
|
+
module Content
|
5
|
+
include Coradoc::Parser::Asciidoc::Base
|
6
|
+
|
7
|
+
def paragraph
|
8
|
+
text_line.repeat(1)
|
9
|
+
end
|
10
|
+
|
11
|
+
def glossaries
|
12
|
+
glossary.repeat(1)
|
13
|
+
end
|
14
|
+
|
15
|
+
# List
|
16
|
+
def list
|
17
|
+
unnumbered_list.as(:unnumbered) |
|
18
|
+
definition_list.as(:definition) | numbered_list.as(:numbered)
|
19
|
+
end
|
20
|
+
|
21
|
+
def contents
|
22
|
+
(
|
23
|
+
example_block.as(:example) |
|
24
|
+
list.as(:list) |
|
25
|
+
table.as(:table) |
|
26
|
+
highlight.as(:highlight) |
|
27
|
+
glossaries.as(:glossaries) |
|
28
|
+
paragraph.as(:paragraph) | empty_line
|
29
|
+
).repeat(1)
|
30
|
+
end
|
31
|
+
|
32
|
+
def example_block
|
33
|
+
str("[example]") >> newline >>
|
34
|
+
str("=").repeat(4).capture(:delimiter) >> newline >>
|
35
|
+
dynamic do |source, context|
|
36
|
+
(str(context.captures[:delimiter]).absent? >> text.as(:text) >> endline).repeat(1) >>
|
37
|
+
str(context.captures[:delimiter]) >> endline
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def highlight
|
42
|
+
text_id >> newline >>
|
43
|
+
underline >> highlight_text >> newline
|
44
|
+
end
|
45
|
+
|
46
|
+
def underline
|
47
|
+
str("[underline]") | str("[.underline]")
|
48
|
+
end
|
49
|
+
|
50
|
+
def highlight_text
|
51
|
+
str("#") >> words.as(:text) >> str("#")
|
52
|
+
end
|
53
|
+
|
54
|
+
# Table
|
55
|
+
def table
|
56
|
+
block_title >>
|
57
|
+
str("|===") >> line_ending >>
|
58
|
+
table_row.repeat(1).as(:rows) >>
|
59
|
+
str("|===") >> line_ending
|
60
|
+
end
|
61
|
+
|
62
|
+
def table_row
|
63
|
+
(literal_space? >> str("|") >> (cell_content | empty_cell_content)).
|
64
|
+
repeat(1).as(:cols) >> line_ending
|
65
|
+
end
|
66
|
+
|
67
|
+
# Extended
|
68
|
+
def word
|
69
|
+
(match("[a-zA-Z0-9_-]") | str(".") | str("*") | match("@")).repeat(1)
|
70
|
+
end
|
71
|
+
|
72
|
+
def empty_cell_content
|
73
|
+
str("|").absent? >> literal_space.as(:text)
|
74
|
+
end
|
75
|
+
|
76
|
+
def cell_content
|
77
|
+
str("|").absent? >> literal_space? >> words.as(:text)
|
78
|
+
end
|
79
|
+
|
80
|
+
def literal_space
|
81
|
+
(match[' '] | match[' \t']).repeat(1)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Override
|
85
|
+
def literal_space?
|
86
|
+
literal_space.maybe
|
87
|
+
end
|
88
|
+
|
89
|
+
def block_title
|
90
|
+
str(".") >> text.as(:title) >> line_ending
|
91
|
+
end
|
92
|
+
|
93
|
+
# Text
|
94
|
+
def text_line
|
95
|
+
(asciidoc_char_with_id.absent? | text_id) >> literal_space? >>
|
96
|
+
text.as(:text) >> line_ending.as(:break)
|
97
|
+
end
|
98
|
+
|
99
|
+
def asciidoc_char
|
100
|
+
match("^[*_:=-]")
|
101
|
+
end
|
102
|
+
|
103
|
+
def asciidoc_char_with_id
|
104
|
+
asciidoc_char | str("[#") | str("[[")
|
105
|
+
end
|
106
|
+
|
107
|
+
def text_id
|
108
|
+
str("[[") >> keyword.as(:id) >> str("]]") |
|
109
|
+
str("[#") >> keyword.as(:id) >> str("]")
|
110
|
+
end
|
111
|
+
|
112
|
+
def glossary
|
113
|
+
keyword.as(:key) >> str("::") >> space? >>
|
114
|
+
text.as(:value) >> line_ending.as(:break)
|
115
|
+
end
|
116
|
+
|
117
|
+
def numbered_list
|
118
|
+
nlist_item.repeat(1)
|
119
|
+
end
|
120
|
+
|
121
|
+
def unnumbered_list
|
122
|
+
(ulist_item >> newline.maybe).repeat(1)
|
123
|
+
end
|
124
|
+
|
125
|
+
def definition_list
|
126
|
+
dlist_item.repeat(1)
|
127
|
+
end
|
128
|
+
|
129
|
+
def nlist_item
|
130
|
+
match("\.") >> space >> text_line
|
131
|
+
end
|
132
|
+
|
133
|
+
def ulist_item
|
134
|
+
match("\\*") >> space >> text_line
|
135
|
+
end
|
136
|
+
|
137
|
+
def dlist_item
|
138
|
+
str("term") >> space >> digits >> str("::") >> space >> text_line
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "coradoc/parser/asciidoc/base"
|
2
|
+
|
3
|
+
module Coradoc
|
4
|
+
module Parser
|
5
|
+
module Asciidoc
|
6
|
+
module Header
|
7
|
+
include Coradoc::Parser::Asciidoc::Base
|
8
|
+
|
9
|
+
# Header
|
10
|
+
def header
|
11
|
+
match("=") >> space? >> text.as(:title) >> newline >>
|
12
|
+
author.maybe.as(:author) >> revision.maybe.as(:revision)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Author
|
16
|
+
def author
|
17
|
+
words.as(:first_name) >> str(",") >> space? >> words.as(:last_name) >>
|
18
|
+
space? >> str("<") >> email.as(:email) >> str(">") >> endline
|
19
|
+
end
|
20
|
+
|
21
|
+
# Revision
|
22
|
+
def revision
|
23
|
+
(word >> (str(".") >> word).maybe).as(:number) >>
|
24
|
+
str(",") >> space? >> word.as(:date) >>
|
25
|
+
str(":") >> space? >> words.as(:remark) >> newline
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "coradoc/parser/asciidoc/base"
|
2
|
+
require "coradoc/parser/asciidoc/content"
|
3
|
+
|
4
|
+
module Coradoc
|
5
|
+
module Parser
|
6
|
+
module Asciidoc
|
7
|
+
module Section
|
8
|
+
include Coradoc::Parser::Asciidoc::Base
|
9
|
+
include Coradoc::Parser::Asciidoc::Content
|
10
|
+
|
11
|
+
def section_block(level = 2)
|
12
|
+
section_id.maybe >>
|
13
|
+
section_title(level).as(:title) >>
|
14
|
+
contents.as(:contents).maybe
|
15
|
+
end
|
16
|
+
|
17
|
+
# Section id
|
18
|
+
def section_id
|
19
|
+
(str("[[") >> keyword.as(:id) >> str("]]") |
|
20
|
+
str("[#") >> keyword.as(:id) >> str("]")) >> newline
|
21
|
+
end
|
22
|
+
|
23
|
+
# Heading
|
24
|
+
def section_title(level = 2, max_level = 8)
|
25
|
+
match("=").repeat(level, max_level).as(:level) >>
|
26
|
+
space? >> text.as(:text) >> endline.as(:break)
|
27
|
+
end
|
28
|
+
|
29
|
+
# section
|
30
|
+
def section
|
31
|
+
section_block >> second_level_section.repeat.maybe.as(:sections)
|
32
|
+
end
|
33
|
+
|
34
|
+
def sub_section(level)
|
35
|
+
newline.maybe >> section_block(level)
|
36
|
+
end
|
37
|
+
|
38
|
+
def second_level_section
|
39
|
+
sub_section(3) >> third_level_section.repeat.maybe.as(:sections)
|
40
|
+
end
|
41
|
+
|
42
|
+
def third_level_section
|
43
|
+
sub_section(4) >> fourth_level_section.repeat.maybe.as(:sections)
|
44
|
+
end
|
45
|
+
|
46
|
+
def fourth_level_section
|
47
|
+
sub_section(5) >> fifth_level_section.repeat.maybe.as(:sections)
|
48
|
+
end
|
49
|
+
|
50
|
+
def fifth_level_section
|
51
|
+
sub_section(6) >> sixth_level_section.repeat.maybe.as(:sections)
|
52
|
+
end
|
53
|
+
|
54
|
+
def sixth_level_section
|
55
|
+
sub_section(7) >> sub_section(8).repeat.maybe.as(:sections)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|