coradoc-adoc 2.0.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.
Files changed (217) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/lib/coradoc/asciidoc/model/admonition.rb +37 -0
  4. data/lib/coradoc/asciidoc/model/anchorable.rb +64 -0
  5. data/lib/coradoc/asciidoc/model/attached.rb +26 -0
  6. data/lib/coradoc/asciidoc/model/attribute.rb +22 -0
  7. data/lib/coradoc/asciidoc/model/attribute_list/matchers.rb +45 -0
  8. data/lib/coradoc/asciidoc/model/attribute_list.rb +230 -0
  9. data/lib/coradoc/asciidoc/model/attribute_list_attribute.rb +11 -0
  10. data/lib/coradoc/asciidoc/model/audio.rb +44 -0
  11. data/lib/coradoc/asciidoc/model/author.rb +36 -0
  12. data/lib/coradoc/asciidoc/model/base.rb +141 -0
  13. data/lib/coradoc/asciidoc/model/bibliography.rb +37 -0
  14. data/lib/coradoc/asciidoc/model/bibliography_entry.rb +38 -0
  15. data/lib/coradoc/asciidoc/model/block/core.rb +139 -0
  16. data/lib/coradoc/asciidoc/model/block/example.rb +14 -0
  17. data/lib/coradoc/asciidoc/model/block/listing.rb +14 -0
  18. data/lib/coradoc/asciidoc/model/block/literal.rb +14 -0
  19. data/lib/coradoc/asciidoc/model/block/open.rb +14 -0
  20. data/lib/coradoc/asciidoc/model/block/pass.rb +14 -0
  21. data/lib/coradoc/asciidoc/model/block/quote.rb +14 -0
  22. data/lib/coradoc/asciidoc/model/block/reviewer_comment.rb +14 -0
  23. data/lib/coradoc/asciidoc/model/block/side.rb +14 -0
  24. data/lib/coradoc/asciidoc/model/block/source_code.rb +14 -0
  25. data/lib/coradoc/asciidoc/model/block.rb +21 -0
  26. data/lib/coradoc/asciidoc/model/break.rb +33 -0
  27. data/lib/coradoc/asciidoc/model/comment_block.rb +33 -0
  28. data/lib/coradoc/asciidoc/model/comment_line.rb +30 -0
  29. data/lib/coradoc/asciidoc/model/content_list.rb +334 -0
  30. data/lib/coradoc/asciidoc/model/document.rb +197 -0
  31. data/lib/coradoc/asciidoc/model/document_attributes.rb +43 -0
  32. data/lib/coradoc/asciidoc/model/glossaries.rb +11 -0
  33. data/lib/coradoc/asciidoc/model/header.rb +57 -0
  34. data/lib/coradoc/asciidoc/model/highlight.rb +11 -0
  35. data/lib/coradoc/asciidoc/model/image/block_image/attribute_list.rb +23 -0
  36. data/lib/coradoc/asciidoc/model/image/block_image.rb +25 -0
  37. data/lib/coradoc/asciidoc/model/image/core/attribute_list.rb +43 -0
  38. data/lib/coradoc/asciidoc/model/image/core.rb +72 -0
  39. data/lib/coradoc/asciidoc/model/image/inline_image.rb +17 -0
  40. data/lib/coradoc/asciidoc/model/image.rb +14 -0
  41. data/lib/coradoc/asciidoc/model/include.rb +66 -0
  42. data/lib/coradoc/asciidoc/model/inline/anchor.rb +41 -0
  43. data/lib/coradoc/asciidoc/model/inline/attribute_reference.rb +25 -0
  44. data/lib/coradoc/asciidoc/model/inline/base.rb +15 -0
  45. data/lib/coradoc/asciidoc/model/inline/bold.rb +38 -0
  46. data/lib/coradoc/asciidoc/model/inline/cross_reference.rb +29 -0
  47. data/lib/coradoc/asciidoc/model/inline/cross_reference_arg.rb +15 -0
  48. data/lib/coradoc/asciidoc/model/inline/footnote.rb +34 -0
  49. data/lib/coradoc/asciidoc/model/inline/hard_line_break.rb +24 -0
  50. data/lib/coradoc/asciidoc/model/inline/highlight.rb +36 -0
  51. data/lib/coradoc/asciidoc/model/inline/italic.rb +38 -0
  52. data/lib/coradoc/asciidoc/model/inline/link.rb +46 -0
  53. data/lib/coradoc/asciidoc/model/inline/monospace.rb +39 -0
  54. data/lib/coradoc/asciidoc/model/inline/quotation.rb +25 -0
  55. data/lib/coradoc/asciidoc/model/inline/small.rb +25 -0
  56. data/lib/coradoc/asciidoc/model/inline/span.rb +38 -0
  57. data/lib/coradoc/asciidoc/model/inline/stem.rb +24 -0
  58. data/lib/coradoc/asciidoc/model/inline/strikethrough.rb +39 -0
  59. data/lib/coradoc/asciidoc/model/inline/subscript.rb +33 -0
  60. data/lib/coradoc/asciidoc/model/inline/superscript.rb +33 -0
  61. data/lib/coradoc/asciidoc/model/inline/underline.rb +25 -0
  62. data/lib/coradoc/asciidoc/model/inline.rb +31 -0
  63. data/lib/coradoc/asciidoc/model/line_break.rb +11 -0
  64. data/lib/coradoc/asciidoc/model/list/core.rb +61 -0
  65. data/lib/coradoc/asciidoc/model/list/definition.rb +27 -0
  66. data/lib/coradoc/asciidoc/model/list/definition_item.rb +43 -0
  67. data/lib/coradoc/asciidoc/model/list/item.rb +72 -0
  68. data/lib/coradoc/asciidoc/model/list/nestable.rb +14 -0
  69. data/lib/coradoc/asciidoc/model/list/ordered.rb +34 -0
  70. data/lib/coradoc/asciidoc/model/list/unordered.rb +34 -0
  71. data/lib/coradoc/asciidoc/model/list.rb +29 -0
  72. data/lib/coradoc/asciidoc/model/named_attribute.rb +12 -0
  73. data/lib/coradoc/asciidoc/model/paragraph.rb +59 -0
  74. data/lib/coradoc/asciidoc/model/rejected_positional_attribute.rb +12 -0
  75. data/lib/coradoc/asciidoc/model/resolvable.rb +71 -0
  76. data/lib/coradoc/asciidoc/model/resolver.rb +430 -0
  77. data/lib/coradoc/asciidoc/model/reviewer_note.rb +54 -0
  78. data/lib/coradoc/asciidoc/model/revision.rb +47 -0
  79. data/lib/coradoc/asciidoc/model/section.rb +109 -0
  80. data/lib/coradoc/asciidoc/model/serialization/asciidoc_adapter.rb +28 -0
  81. data/lib/coradoc/asciidoc/model/serialization/asciidoc_mapping.rb +42 -0
  82. data/lib/coradoc/asciidoc/model/serialization/asciidoc_mapping_rule.rb +41 -0
  83. data/lib/coradoc/asciidoc/model/serialization/asciidoc_transform.rb +211 -0
  84. data/lib/coradoc/asciidoc/model/serialization/errors.rb +57 -0
  85. data/lib/coradoc/asciidoc/model/serialization.rb +39 -0
  86. data/lib/coradoc/asciidoc/model/spacing.rb +282 -0
  87. data/lib/coradoc/asciidoc/model/table.rb +44 -0
  88. data/lib/coradoc/asciidoc/model/table_cell.rb +122 -0
  89. data/lib/coradoc/asciidoc/model/table_row.rb +26 -0
  90. data/lib/coradoc/asciidoc/model/tag.rb +36 -0
  91. data/lib/coradoc/asciidoc/model/term.rb +48 -0
  92. data/lib/coradoc/asciidoc/model/text_element.rb +66 -0
  93. data/lib/coradoc/asciidoc/model/title.rb +85 -0
  94. data/lib/coradoc/asciidoc/model/video/attribute_list.rb +43 -0
  95. data/lib/coradoc/asciidoc/model/video.rb +49 -0
  96. data/lib/coradoc/asciidoc/model.rb +75 -0
  97. data/lib/coradoc/asciidoc/parse_error.rb +161 -0
  98. data/lib/coradoc/asciidoc/parser/admonition.rb +26 -0
  99. data/lib/coradoc/asciidoc/parser/attribute_list.rb +110 -0
  100. data/lib/coradoc/asciidoc/parser/base.rb +159 -0
  101. data/lib/coradoc/asciidoc/parser/bibliography.rb +31 -0
  102. data/lib/coradoc/asciidoc/parser/block.rb +186 -0
  103. data/lib/coradoc/asciidoc/parser/block_assembler.rb +183 -0
  104. data/lib/coradoc/asciidoc/parser/cache.rb +155 -0
  105. data/lib/coradoc/asciidoc/parser/citation.rb +32 -0
  106. data/lib/coradoc/asciidoc/parser/content.rb +76 -0
  107. data/lib/coradoc/asciidoc/parser/document_attributes.rb +27 -0
  108. data/lib/coradoc/asciidoc/parser/fix_files.rb +76 -0
  109. data/lib/coradoc/asciidoc/parser/header.rb +31 -0
  110. data/lib/coradoc/asciidoc/parser/inline.rb +199 -0
  111. data/lib/coradoc/asciidoc/parser/list.rb +130 -0
  112. data/lib/coradoc/asciidoc/parser/metadata_detector.rb +164 -0
  113. data/lib/coradoc/asciidoc/parser/paragraph.rb +64 -0
  114. data/lib/coradoc/asciidoc/parser/section.rb +62 -0
  115. data/lib/coradoc/asciidoc/parser/stem.rb +19 -0
  116. data/lib/coradoc/asciidoc/parser/table.rb +166 -0
  117. data/lib/coradoc/asciidoc/parser/term.rb +70 -0
  118. data/lib/coradoc/asciidoc/parser/text.rb +156 -0
  119. data/lib/coradoc/asciidoc/parser.rb +10 -0
  120. data/lib/coradoc/asciidoc/serializer/adoc_serializer.rb +86 -0
  121. data/lib/coradoc/asciidoc/serializer/element_registry.rb +95 -0
  122. data/lib/coradoc/asciidoc/serializer/fallback_serializer.rb +21 -0
  123. data/lib/coradoc/asciidoc/serializer/formatter.rb +144 -0
  124. data/lib/coradoc/asciidoc/serializer/registrations.rb +108 -0
  125. data/lib/coradoc/asciidoc/serializer/serialization_context.rb +238 -0
  126. data/lib/coradoc/asciidoc/serializer/serializers/admonition.rb +19 -0
  127. data/lib/coradoc/asciidoc/serializer/serializers/attribute.rb +23 -0
  128. data/lib/coradoc/asciidoc/serializer/serializers/attribute_list.rb +40 -0
  129. data/lib/coradoc/asciidoc/serializer/serializers/attribute_list_attribute.rb +18 -0
  130. data/lib/coradoc/asciidoc/serializer/serializers/audio.rb +33 -0
  131. data/lib/coradoc/asciidoc/serializer/serializers/author.rb +20 -0
  132. data/lib/coradoc/asciidoc/serializer/serializers/base.rb +152 -0
  133. data/lib/coradoc/asciidoc/serializer/serializers/bibliography.rb +35 -0
  134. data/lib/coradoc/asciidoc/serializer/serializers/bibliography_entry.rb +24 -0
  135. data/lib/coradoc/asciidoc/serializer/serializers/block/core.rb +70 -0
  136. data/lib/coradoc/asciidoc/serializer/serializers/block/example.rb +17 -0
  137. data/lib/coradoc/asciidoc/serializer/serializers/block/listing.rb +22 -0
  138. data/lib/coradoc/asciidoc/serializer/serializers/block/literal.rb +17 -0
  139. data/lib/coradoc/asciidoc/serializer/serializers/block/open.rb +22 -0
  140. data/lib/coradoc/asciidoc/serializer/serializers/block/pass.rb +17 -0
  141. data/lib/coradoc/asciidoc/serializer/serializers/block/quote.rb +17 -0
  142. data/lib/coradoc/asciidoc/serializer/serializers/block/reviewer_comment.rb +17 -0
  143. data/lib/coradoc/asciidoc/serializer/serializers/block/side.rb +22 -0
  144. data/lib/coradoc/asciidoc/serializer/serializers/block/source_code.rb +22 -0
  145. data/lib/coradoc/asciidoc/serializer/serializers/block.rb +23 -0
  146. data/lib/coradoc/asciidoc/serializer/serializers/break.rb +18 -0
  147. data/lib/coradoc/asciidoc/serializer/serializers/comment_block.rb +22 -0
  148. data/lib/coradoc/asciidoc/serializer/serializers/comment_line.rb +22 -0
  149. data/lib/coradoc/asciidoc/serializer/serializers/document.rb +65 -0
  150. data/lib/coradoc/asciidoc/serializer/serializers/document_attributes.rb +21 -0
  151. data/lib/coradoc/asciidoc/serializer/serializers/header.rb +24 -0
  152. data/lib/coradoc/asciidoc/serializer/serializers/highlight.rb +23 -0
  153. data/lib/coradoc/asciidoc/serializer/serializers/image/core.rb +30 -0
  154. data/lib/coradoc/asciidoc/serializer/serializers/image.rb +14 -0
  155. data/lib/coradoc/asciidoc/serializer/serializers/include.rb +19 -0
  156. data/lib/coradoc/asciidoc/serializer/serializers/inline/anchor.rb +20 -0
  157. data/lib/coradoc/asciidoc/serializer/serializers/inline/attribute_reference.rb +20 -0
  158. data/lib/coradoc/asciidoc/serializer/serializers/inline/bold.rb +26 -0
  159. data/lib/coradoc/asciidoc/serializer/serializers/inline/cross_reference.rb +30 -0
  160. data/lib/coradoc/asciidoc/serializer/serializers/inline/cross_reference_arg.rb +20 -0
  161. data/lib/coradoc/asciidoc/serializer/serializers/inline/footnote.rb +24 -0
  162. data/lib/coradoc/asciidoc/serializer/serializers/inline/hard_line_break.rb +20 -0
  163. data/lib/coradoc/asciidoc/serializer/serializers/inline/highlight.rb +26 -0
  164. data/lib/coradoc/asciidoc/serializer/serializers/inline/italic.rb +26 -0
  165. data/lib/coradoc/asciidoc/serializer/serializers/inline/link.rb +38 -0
  166. data/lib/coradoc/asciidoc/serializer/serializers/inline/monospace.rb +26 -0
  167. data/lib/coradoc/asciidoc/serializer/serializers/inline/quotation.rb +21 -0
  168. data/lib/coradoc/asciidoc/serializer/serializers/inline/small.rb +20 -0
  169. data/lib/coradoc/asciidoc/serializer/serializers/inline/span.rb +35 -0
  170. data/lib/coradoc/asciidoc/serializer/serializers/inline/stem.rb +23 -0
  171. data/lib/coradoc/asciidoc/serializer/serializers/inline/strikethrough.rb +29 -0
  172. data/lib/coradoc/asciidoc/serializer/serializers/inline/subscript.rb +29 -0
  173. data/lib/coradoc/asciidoc/serializer/serializers/inline/superscript.rb +26 -0
  174. data/lib/coradoc/asciidoc/serializer/serializers/inline/underline.rb +20 -0
  175. data/lib/coradoc/asciidoc/serializer/serializers/inline.rb +32 -0
  176. data/lib/coradoc/asciidoc/serializer/serializers/line_break.rb +18 -0
  177. data/lib/coradoc/asciidoc/serializer/serializers/list/core.rb +47 -0
  178. data/lib/coradoc/asciidoc/serializer/serializers/list/definition.rb +35 -0
  179. data/lib/coradoc/asciidoc/serializer/serializers/list/definition_item.rb +38 -0
  180. data/lib/coradoc/asciidoc/serializer/serializers/list/item.rb +120 -0
  181. data/lib/coradoc/asciidoc/serializer/serializers/list/ordered.rb +24 -0
  182. data/lib/coradoc/asciidoc/serializer/serializers/list/unordered.rb +29 -0
  183. data/lib/coradoc/asciidoc/serializer/serializers/list.rb +19 -0
  184. data/lib/coradoc/asciidoc/serializer/serializers/named_attribute.rb +22 -0
  185. data/lib/coradoc/asciidoc/serializer/serializers/paragraph.rb +65 -0
  186. data/lib/coradoc/asciidoc/serializer/serializers/reviewer_note.rb +28 -0
  187. data/lib/coradoc/asciidoc/serializer/serializers/revision.rb +26 -0
  188. data/lib/coradoc/asciidoc/serializer/serializers/section.rb +37 -0
  189. data/lib/coradoc/asciidoc/serializer/serializers/table.rb +24 -0
  190. data/lib/coradoc/asciidoc/serializer/serializers/table_cell.rb +75 -0
  191. data/lib/coradoc/asciidoc/serializer/serializers/table_row.rb +24 -0
  192. data/lib/coradoc/asciidoc/serializer/serializers/tag.rb +19 -0
  193. data/lib/coradoc/asciidoc/serializer/serializers/term.rb +20 -0
  194. data/lib/coradoc/asciidoc/serializer/serializers/text_element.rb +23 -0
  195. data/lib/coradoc/asciidoc/serializer/serializers/title.rb +55 -0
  196. data/lib/coradoc/asciidoc/serializer/serializers/video.rb +33 -0
  197. data/lib/coradoc/asciidoc/serializer/spacing_strategy.rb +70 -0
  198. data/lib/coradoc/asciidoc/serializer.rb +75 -0
  199. data/lib/coradoc/asciidoc/transform/from_core_model.rb +502 -0
  200. data/lib/coradoc/asciidoc/transform/from_core_model_registrations.rb +126 -0
  201. data/lib/coradoc/asciidoc/transform/registry.rb +146 -0
  202. data/lib/coradoc/asciidoc/transform/to_core_model.rb +564 -0
  203. data/lib/coradoc/asciidoc/transform/to_core_model_registrations.rb +257 -0
  204. data/lib/coradoc/asciidoc/transform.rb +13 -0
  205. data/lib/coradoc/asciidoc/transformer/block_rules.rb +101 -0
  206. data/lib/coradoc/asciidoc/transformer/header_rules.rb +91 -0
  207. data/lib/coradoc/asciidoc/transformer/inline_rules.rb +179 -0
  208. data/lib/coradoc/asciidoc/transformer/list_rules.rb +131 -0
  209. data/lib/coradoc/asciidoc/transformer/misc_rules.rb +196 -0
  210. data/lib/coradoc/asciidoc/transformer/structural_rules.rb +216 -0
  211. data/lib/coradoc/asciidoc/transformer/text_rules.rb +107 -0
  212. data/lib/coradoc/asciidoc/transformer.rb +406 -0
  213. data/lib/coradoc/asciidoc/version.rb +7 -0
  214. data/lib/coradoc/asciidoc.rb +148 -0
  215. data/lib/coradoc/util/asciidoc.rb +71 -0
  216. data/lib/coradoc/util.rb +8 -0
  217. metadata +343 -0
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'parslet'
4
+ require 'parslet/convenience'
5
+
6
+ module Coradoc
7
+ module AsciiDoc
8
+ module Parser
9
+ # Autoload parser modules - they will be loaded when included in Base class below
10
+ autoload :Admonition, 'coradoc/asciidoc/parser/admonition'
11
+ autoload :AttributeList, 'coradoc/asciidoc/parser/attribute_list'
12
+ autoload :Bibliography, 'coradoc/asciidoc/parser/bibliography'
13
+ autoload :Block, 'coradoc/asciidoc/parser/block'
14
+ autoload :Citation, 'coradoc/asciidoc/parser/citation'
15
+ autoload :Content, 'coradoc/asciidoc/parser/content'
16
+ autoload :DocumentAttributes, 'coradoc/asciidoc/parser/document_attributes'
17
+ autoload :Header, 'coradoc/asciidoc/parser/header'
18
+ autoload :Inline, 'coradoc/asciidoc/parser/inline'
19
+ autoload :List, 'coradoc/asciidoc/parser/list'
20
+ autoload :Paragraph, 'coradoc/asciidoc/parser/paragraph'
21
+ autoload :Section, 'coradoc/asciidoc/parser/section'
22
+ autoload :Table, 'coradoc/asciidoc/parser/table'
23
+ autoload :Term, 'coradoc/asciidoc/parser/term'
24
+ autoload :Text, 'coradoc/asciidoc/parser/text'
25
+ autoload :Stem, 'coradoc/asciidoc/parser/stem'
26
+
27
+ class Base < Parslet::Parser
28
+ include Admonition
29
+ include AttributeList
30
+ include Bibliography
31
+ include Block
32
+ include Citation
33
+ include Content
34
+ include DocumentAttributes
35
+ include Header
36
+ include Inline
37
+ include List
38
+ include Paragraph
39
+ include Section
40
+ include Table
41
+ include Term
42
+ include Text
43
+ include Stem
44
+
45
+ root :document
46
+
47
+ rule(:document) do
48
+ (
49
+ header.as(:header) |
50
+ document_attributes |
51
+ section |
52
+ admonition_line |
53
+ bib_entry |
54
+ block |
55
+ block_image |
56
+ comment_block |
57
+ comment_line |
58
+ include_directive |
59
+ list |
60
+ table.as(:table) |
61
+ page_break.as(:page_break) |
62
+ paragraph |
63
+ tag |
64
+ empty_line.as(:line_break) |
65
+ any.as(:unparsed)
66
+ ).repeat.as(:document)
67
+ end
68
+
69
+ # Parse an AsciiDoc file
70
+ # @param filename [String] The filename of the Asciidoc file to parse
71
+ # @return [Hash] The parsed AST object
72
+ def self.parse_file(filename)
73
+ parse(File.read(filename))
74
+ end
75
+
76
+ # Parse an AsciiDoc string
77
+ # @param string [String] The Asciidoc string to parse
78
+ # @return [Hash] The parsed AST object
79
+ def self.parse(string)
80
+ new.parse(string)
81
+ rescue Parslet::ParseFailed => e
82
+ warn e.parse_failure_cause.ascii_tree
83
+ end
84
+
85
+ def rule_dispatch(rule_name, *args, **kwargs)
86
+ @dispatch_data ||= {}
87
+ dispatch_key = [rule_name, args, kwargs.to_a.sort]
88
+ dispatch_hash = dispatch_key.hash.abs
89
+ unless @dispatch_data.key?(dispatch_hash)
90
+ alias_name = :"#{rule_name}_#{dispatch_hash}"
91
+ Coradoc::AsciiDoc::Parser::Base.class_exec do
92
+ rule(alias_name) do
93
+ send(rule_name, *args, **kwargs)
94
+ end
95
+ end
96
+ @dispatch_data[dispatch_hash] = alias_name
97
+ end
98
+ dispatch_method = @dispatch_data[dispatch_hash]
99
+ send(dispatch_method)
100
+ end
101
+
102
+ def self.config(key)
103
+ # NOTE: These are internal dispatch configuration options for the parser:
104
+ # - add_dispatch: Enables automatic method dispatching
105
+ # - with_params: Supports parameterized rule invocation
106
+ c = {
107
+ add_dispatch: true,
108
+ with_params: true
109
+ }
110
+
111
+ raise ArgumentError, "Unknown config key: #{key}. Available keys: #{c.keys.join(', ')}" unless c.key?(key)
112
+
113
+ c[key]
114
+ end
115
+
116
+ # Collect parser methods from all parser modules (excluding Base, Cache, and FixFiles)
117
+ # Base is the parser class, Cache is a utility class, FixFiles is a utility module
118
+ parser_constants = Coradoc::AsciiDoc::Parser.constants - %i[Base Cache FixFiles]
119
+ parser_methods = parser_constants.each_with_object({}) do |const, acc|
120
+ rule_names = Coradoc::AsciiDoc::Parser.const_get(const).instance_methods
121
+ rule_names.each do |rule_name|
122
+ acc[rule_name] ||= []
123
+ acc[rule_name] << const
124
+ end
125
+ end
126
+
127
+ # Warn about duplicated parser methods:
128
+ parser_methods.each do |rule_name, defn_sites|
129
+ count = defn_sites.length
130
+ if count > 1
131
+ defn_site_constants = defn_sites.map { |const| Coradoc::AsciiDoc::Parser.const_get(const) }
132
+ Coradoc::Logger.warn "Parser method '#{rule_name}' is defined #{count} times in #{defn_site_constants.join(', ')}"
133
+ end
134
+ end
135
+
136
+ parser_methods.each_key do |rule_name|
137
+ params = Coradoc::AsciiDoc::Parser::Base.instance_method(rule_name).parameters
138
+ if config(:add_dispatch) && params == []
139
+ alias_name = :"alias_nondispatch_#{rule_name}"
140
+ Coradoc::AsciiDoc::Parser::Base.class_exec do
141
+ alias_method alias_name, rule_name
142
+ rule(rule_name) do
143
+ send(alias_name)
144
+ end
145
+ end
146
+ elsif config(:add_dispatch) && config(:with_params)
147
+ alias_name = :"alias_dispatch_#{rule_name}"
148
+ Coradoc::AsciiDoc::Parser::Base.class_exec do
149
+ alias_method alias_name, rule_name
150
+ define_method(rule_name) do |*args, **kwargs|
151
+ rule_dispatch(alias_name, *args, **kwargs)
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Parser
6
+ module Bibliography
7
+ def bibliography
8
+ (element_id.maybe >>
9
+ str("[bibliography]\n") >>
10
+ str('== ') >> match("[^\n]").repeat(1).as(:title) >> str("\n") >>
11
+ bib_entry.repeat(1).as(:entries)
12
+ ).as(:bibliography)
13
+ end
14
+
15
+ def bib_entry
16
+ (match('^*') >> str(' [[[') >>
17
+ match('[^,\[\]\n]').repeat(1).as(:anchor_name) >>
18
+ (str(',') >>
19
+ match('[^\]\n]').repeat(1).as(:document_id)
20
+ ).maybe >>
21
+ str(']]]') >>
22
+ (text_line.repeat(0, 1) >>
23
+ text_line.repeat(0)
24
+ ).as(:ref_text).maybe >>
25
+ line_ending.repeat(1).as(:line_break).maybe
26
+ ).as(:bibliography_entry)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Parser
6
+ module Block
7
+ # AsciiDoc Block Delimiter Patterns:
8
+ # - "4+" means 4 or more identical characters
9
+ # - "2+" means 2 or more identical characters
10
+ #
11
+ # Block types by delimiter character:
12
+ # - "=" (4+): Example block (====, =====, etc.)
13
+ # - "-" (4+): Source/listing block (----, -----, etc.)
14
+ # - "-" (2): Open block (--)
15
+ # - "_" (4+): Quote block (____, _____, etc.)
16
+ # - "*" (4+): Sidebar block (****, *****, etc.)
17
+ # - "+" (4+): Pass block (++++, +++++, etc.)
18
+ # - "." (4+): Literal block (...., ....., etc.)
19
+ #
20
+ # Table delimiters:
21
+ # - "|===" defines table boundaries
22
+ # - "|" separates cells within the table
23
+
24
+ def block(n_deep = 3)
25
+ (example_block(n_deep) |
26
+ sidebar_block(n_deep) |
27
+ source_block(n_deep) |
28
+ quote_block(n_deep) |
29
+ pass_block(n_deep) |
30
+ open_block(n_deep)).as(:block)
31
+ end
32
+
33
+ def reviewer_note_block(_n_deep = 3)
34
+ # Match blocks with reviewer attribute
35
+ # This should only match when attribute_list contains reviewer=
36
+ # For now, we'll make it not match anything specific
37
+ # The block() method will handle these cases
38
+ str('').absent? # Never matches - placeholder for future implementation
39
+ end
40
+
41
+ def example_block(n_deep)
42
+ block_style(n_deep, '=', 4)
43
+ end
44
+
45
+ def pass_block(n_deep)
46
+ block_style(n_deep, '+', 4, :pass)
47
+ end
48
+
49
+ def quote_block(n_deep)
50
+ block_style(n_deep, '_', 4)
51
+ end
52
+
53
+ def sidebar_block(n_deep)
54
+ block_style(n_deep, '*', 4)
55
+ end
56
+
57
+ def source_block(n_deep)
58
+ block_style(n_deep, '-', 4, verbatim: true)
59
+ end
60
+
61
+ # Open block: exactly 2 dashes (cannot nest within itself)
62
+ def open_block(n_deep)
63
+ block_style_exact(n_deep, '-', 2)
64
+ end
65
+
66
+ def block_title
67
+ str('.') >> space.absent? >> text.as(:title) >> newline
68
+ end
69
+
70
+ def block_type(type)
71
+ (line_start? >> str('[') >> str('[').absent? >>
72
+ str(type).as(:type) >>
73
+ str(']')) >> newline # |
74
+ end
75
+
76
+ def block_content(n_deep = 3)
77
+ c = block_image |
78
+ list |
79
+ text_line(false, unguarded: true) |
80
+ empty_line.as(:line_break)
81
+ c |= block(n_deep - 1) if n_deep.positive?
82
+ c.repeat(1)
83
+ end
84
+
85
+ # Block delimiter: 4+ identical characters (or 2 for open block)
86
+ # NOTE: repeat(4,) means 4 or more (not exactly 4)
87
+ def block_delimiter
88
+ line_start? >>
89
+ ((str('*') |
90
+ str('=') |
91
+ str('_') |
92
+ str('+') |
93
+ str('-')).repeat(4) | # 4+ characters for most blocks
94
+ str('-').repeat(2, 2)) >> # Exactly 2 for open block
95
+ newline
96
+ end
97
+
98
+ def element_attributes
99
+ block_title.maybe >>
100
+ element_id.maybe >>
101
+ (attribute_list >> newline).maybe >>
102
+ block_title.maybe >>
103
+ newline.maybe >>
104
+ (attribute_list >> newline).maybe >>
105
+ element_id.maybe
106
+ end
107
+
108
+ # Block style parser with variable delimiter length
109
+ # @param n_deep [Integer] Nesting depth for nested blocks
110
+ # @param delimiter [String] The delimiter character ("=", "-", "_", "*", "+")
111
+ # @param repeater [Integer] Minimum number of delimiter characters (default: 4)
112
+ # @param type [Symbol] Block type for special handling (e.g., :pass)
113
+ def block_style(n_deep = 3, delimiter = '*', repeater = 4, type = nil, verbatim: false)
114
+ # repeat(repeater,) means repeater or more characters
115
+ current_delimiter = str(delimiter).repeat(repeater).capture(:delimit)
116
+ closing_delimiter = dynamic do |_s, c|
117
+ str(c.captures[:delimit].to_s.strip)
118
+ end
119
+
120
+ # Create a block content parser that respects the closing delimiter
121
+ # This prevents nested blocks from consuming the closing delimiter
122
+ block_content_with_closing = dynamic do |_s, c|
123
+ delim_str = c.captures[:delimit].to_s.strip
124
+ closing_pattern = str(delim_str) >> newline
125
+
126
+ # Build content that doesn't match the closing delimiter
127
+ content = block_image | list | text_line(false, unguarded: true,
128
+ verbatim: verbatim) | empty_line.as(:line_break)
129
+ if n_deep.positive?
130
+ # For nested blocks, also prevent them from consuming the closing delimiter
131
+ content |= block(n_deep - 1)
132
+ end
133
+
134
+ # Each content element must not start with the closing delimiter
135
+ (closing_pattern.absent? >> content).repeat(1)
136
+ end
137
+
138
+ element_attributes >>
139
+ (line_start? >> attribute_list >> newline).maybe >>
140
+ line_start? >>
141
+ current_delimiter.as(:delimiter) >> newline >>
142
+ if type == :pass
143
+ (text_line(false, unguarded: true, verbatim: verbatim) | empty_line.as(:line_break)).repeat(1).as(:lines)
144
+ else
145
+ # Use dynamic block content that respects closing delimiter
146
+ block_content_with_closing.as(:lines)
147
+ end >>
148
+ line_start? >>
149
+ closing_delimiter >> newline
150
+ end
151
+
152
+ # Block style parser with EXACT delimiter length (for open blocks)
153
+ # Open blocks use exactly 2 dashes and cannot nest within themselves
154
+ def block_style_exact(n_deep = 3, delimiter = '-', exact_chars = 2, type = nil)
155
+ current_delimiter = str(delimiter).repeat(exact_chars, exact_chars).capture(:delimit)
156
+ closing_delimiter = dynamic do |_s, c|
157
+ str(c.captures[:delimit].to_s.strip)
158
+ end
159
+
160
+ # Create a block content parser that respects the closing delimiter
161
+ block_content_with_closing = dynamic do |_s, c|
162
+ delim_str = c.captures[:delimit].to_s.strip
163
+ closing_pattern = str(delim_str) >> newline
164
+
165
+ content = block_image | list | text_line(false, unguarded: true) | empty_line.as(:line_break)
166
+ content |= block(n_deep - 1) if n_deep.positive?
167
+
168
+ (closing_pattern.absent? >> content).repeat(1)
169
+ end
170
+
171
+ element_attributes >>
172
+ (line_start? >> attribute_list >> newline).maybe >>
173
+ line_start? >>
174
+ current_delimiter.as(:delimiter) >> newline >>
175
+ if type == :pass
176
+ (text_line(false, unguarded: true) | empty_line.as(:line_break)).repeat(1).as(:lines)
177
+ else
178
+ block_content_with_closing.as(:lines)
179
+ end >>
180
+ line_start? >>
181
+ closing_delimiter >> newline
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Parser
6
+ # Single Responsibility: Assemble block AST from metadata hints
7
+ # Takes metadata analysis and input text, returns proper AST structure
8
+ class BlockAssembler
9
+ # Main entry point: assemble block AST from input and metadata
10
+ # @param input [String] The input text to parse
11
+ # @param metadata_analysis [Hash] Analysis from MetadataDetector
12
+ # @return [Hash] AST hash {:block => {...}}
13
+ def self.assemble(input, metadata_analysis)
14
+ return nil unless metadata_analysis
15
+
16
+ pattern = metadata_analysis[:pattern]
17
+
18
+ # Delegate to pattern-specific methods (Open/Closed Principle)
19
+ case pattern
20
+ when :title_attr_delim
21
+ assemble_title_attr_delim(input, metadata_analysis)
22
+ when :title_delim
23
+ assemble_title_delim(input, metadata_analysis)
24
+ when :attr_delim
25
+ assemble_attr_delim(input, metadata_analysis)
26
+ when :plain_delim
27
+ assemble_plain_delim(input, metadata_analysis)
28
+ end
29
+ end
30
+
31
+ # Handle: Title + Attribute + Delimiter pattern
32
+ # @param input [String] The input text
33
+ # @param metadata [Hash] Metadata analysis
34
+ # @return [Hash] Complete block hash
35
+ def self.assemble_title_attr_delim(input, metadata)
36
+ lines = input.lines
37
+ delimiter_line = metadata[:delimiter_line]
38
+ delimiter = metadata[:delimiter][:delimiter]
39
+
40
+ # Extract components
41
+ title_text = metadata[:title][:text]
42
+ attr_list = parse_attribute_list(metadata[:attributes])
43
+
44
+ # Extract block content
45
+ block_lines = extract_block_lines(lines, delimiter_line, delimiter)
46
+
47
+ {
48
+ title: title_text,
49
+ attribute_list: attr_list,
50
+ delimiter: delimiter,
51
+ lines: block_lines
52
+ }
53
+ end
54
+
55
+ # Handle: Title + Delimiter pattern (no attributes)
56
+ # @param input [String] The input text
57
+ # @param metadata [Hash] Metadata analysis
58
+ # @return [Hash] Block hash without attributes
59
+ def self.assemble_title_delim(input, metadata)
60
+ lines = input.lines
61
+ delimiter_line = metadata[:delimiter_line]
62
+ delimiter = metadata[:delimiter][:delimiter]
63
+
64
+ # Extract components
65
+ title_text = metadata[:title][:text]
66
+
67
+ # Extract block content
68
+ block_lines = extract_block_lines(lines, delimiter_line, delimiter)
69
+
70
+ {
71
+ title: title_text,
72
+ delimiter: delimiter,
73
+ lines: block_lines
74
+ }
75
+ end
76
+
77
+ # Handle: Attribute + Delimiter pattern (no title)
78
+ # @param input [String] The input text
79
+ # @param metadata [Hash] Metadata analysis
80
+ # @return [Hash] Block hash without title
81
+ def self.assemble_attr_delim(input, metadata)
82
+ lines = input.lines
83
+ delimiter_line = metadata[:delimiter_line]
84
+ delimiter = metadata[:delimiter][:delimiter]
85
+
86
+ # Extract components
87
+ attr_list = parse_attribute_list(metadata[:attributes])
88
+
89
+ # Extract block content
90
+ block_lines = extract_block_lines(lines, delimiter_line, delimiter)
91
+
92
+ {
93
+ attribute_list: attr_list,
94
+ delimiter: delimiter,
95
+ lines: block_lines
96
+ }
97
+ end
98
+
99
+ # Handle: Just Delimiter pattern
100
+ # @param input [String] The input text
101
+ # @param metadata [Hash] Metadata analysis
102
+ # @return [Hash] Minimal block hash
103
+ def self.assemble_plain_delim(input, metadata)
104
+ lines = input.lines
105
+ delimiter_line = metadata[:delimiter_line]
106
+ delimiter = metadata[:delimiter][:delimiter]
107
+
108
+ # Extract block content
109
+ block_lines = extract_block_lines(lines, delimiter_line, delimiter)
110
+
111
+ {
112
+ delimiter: delimiter,
113
+ lines: block_lines
114
+ }
115
+ end
116
+
117
+ # Helper: Extract content between delimiters
118
+ # @param lines [Array<String>] All lines of input
119
+ # @param delimiter_line [Integer] Line number of opening delimiter
120
+ # @param delimiter [String] The delimiter string (e.g., "****")
121
+ # @return [Array<Hash>] Array of {:text => "...", :line_break => "\n"}
122
+ def self.extract_block_lines(lines, delimiter_line, delimiter)
123
+ block_lines = []
124
+
125
+ # Start from line after opening delimiter
126
+ i = delimiter_line + 1
127
+
128
+ # Collect lines until closing delimiter
129
+ while i < lines.length
130
+ line = lines[i]
131
+
132
+ # Check if this is the closing delimiter
133
+ break if line.strip == delimiter
134
+
135
+ # Handle empty lines vs content lines
136
+ if line.strip.empty?
137
+ block_lines << { line_break: "\n" }
138
+ else
139
+ # Remove trailing newline for processing
140
+ text = line.chomp
141
+ block_lines << { text: text, line_break: "\n" }
142
+ end
143
+
144
+ i += 1
145
+ end
146
+
147
+ block_lines
148
+ end
149
+
150
+ # Parse attribute list to match expected AST structure
151
+ # @param attr_meta [Hash] Attribute metadata from detector
152
+ # @return [Hash] Properly formatted attribute_list hash
153
+ def self.parse_attribute_list(attr_meta)
154
+ return nil unless attr_meta
155
+
156
+ # Get the raw content (without brackets)
157
+ content = attr_meta[:content]
158
+ attr_content = content[1...-1] # Remove [ and ]
159
+
160
+ # Get attributes array from metadata
161
+ attributes = attr_meta[:attributes]
162
+
163
+ # Build attribute_array in expected format
164
+ attribute_array = attributes.map do |attr|
165
+ # Check if it's a named attribute (key=value)
166
+ if attr.include?('=')
167
+ key, value = attr.split('=', 2)
168
+ { named: { named_key: key.strip, named_value: value.strip } }
169
+ else
170
+ # Positional attribute
171
+ { positional: attr.strip }
172
+ end
173
+ end
174
+
175
+ {
176
+ attr_content: attr_content,
177
+ attribute_array: attribute_array
178
+ }
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end