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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/lib/coradoc/asciidoc/model/admonition.rb +37 -0
- data/lib/coradoc/asciidoc/model/anchorable.rb +64 -0
- data/lib/coradoc/asciidoc/model/attached.rb +26 -0
- data/lib/coradoc/asciidoc/model/attribute.rb +22 -0
- data/lib/coradoc/asciidoc/model/attribute_list/matchers.rb +45 -0
- data/lib/coradoc/asciidoc/model/attribute_list.rb +230 -0
- data/lib/coradoc/asciidoc/model/attribute_list_attribute.rb +11 -0
- data/lib/coradoc/asciidoc/model/audio.rb +44 -0
- data/lib/coradoc/asciidoc/model/author.rb +36 -0
- data/lib/coradoc/asciidoc/model/base.rb +141 -0
- data/lib/coradoc/asciidoc/model/bibliography.rb +37 -0
- data/lib/coradoc/asciidoc/model/bibliography_entry.rb +38 -0
- data/lib/coradoc/asciidoc/model/block/core.rb +139 -0
- data/lib/coradoc/asciidoc/model/block/example.rb +14 -0
- data/lib/coradoc/asciidoc/model/block/listing.rb +14 -0
- data/lib/coradoc/asciidoc/model/block/literal.rb +14 -0
- data/lib/coradoc/asciidoc/model/block/open.rb +14 -0
- data/lib/coradoc/asciidoc/model/block/pass.rb +14 -0
- data/lib/coradoc/asciidoc/model/block/quote.rb +14 -0
- data/lib/coradoc/asciidoc/model/block/reviewer_comment.rb +14 -0
- data/lib/coradoc/asciidoc/model/block/side.rb +14 -0
- data/lib/coradoc/asciidoc/model/block/source_code.rb +14 -0
- data/lib/coradoc/asciidoc/model/block.rb +21 -0
- data/lib/coradoc/asciidoc/model/break.rb +33 -0
- data/lib/coradoc/asciidoc/model/comment_block.rb +33 -0
- data/lib/coradoc/asciidoc/model/comment_line.rb +30 -0
- data/lib/coradoc/asciidoc/model/content_list.rb +334 -0
- data/lib/coradoc/asciidoc/model/document.rb +197 -0
- data/lib/coradoc/asciidoc/model/document_attributes.rb +43 -0
- data/lib/coradoc/asciidoc/model/glossaries.rb +11 -0
- data/lib/coradoc/asciidoc/model/header.rb +57 -0
- data/lib/coradoc/asciidoc/model/highlight.rb +11 -0
- data/lib/coradoc/asciidoc/model/image/block_image/attribute_list.rb +23 -0
- data/lib/coradoc/asciidoc/model/image/block_image.rb +25 -0
- data/lib/coradoc/asciidoc/model/image/core/attribute_list.rb +43 -0
- data/lib/coradoc/asciidoc/model/image/core.rb +72 -0
- data/lib/coradoc/asciidoc/model/image/inline_image.rb +17 -0
- data/lib/coradoc/asciidoc/model/image.rb +14 -0
- data/lib/coradoc/asciidoc/model/include.rb +66 -0
- data/lib/coradoc/asciidoc/model/inline/anchor.rb +41 -0
- data/lib/coradoc/asciidoc/model/inline/attribute_reference.rb +25 -0
- data/lib/coradoc/asciidoc/model/inline/base.rb +15 -0
- data/lib/coradoc/asciidoc/model/inline/bold.rb +38 -0
- data/lib/coradoc/asciidoc/model/inline/cross_reference.rb +29 -0
- data/lib/coradoc/asciidoc/model/inline/cross_reference_arg.rb +15 -0
- data/lib/coradoc/asciidoc/model/inline/footnote.rb +34 -0
- data/lib/coradoc/asciidoc/model/inline/hard_line_break.rb +24 -0
- data/lib/coradoc/asciidoc/model/inline/highlight.rb +36 -0
- data/lib/coradoc/asciidoc/model/inline/italic.rb +38 -0
- data/lib/coradoc/asciidoc/model/inline/link.rb +46 -0
- data/lib/coradoc/asciidoc/model/inline/monospace.rb +39 -0
- data/lib/coradoc/asciidoc/model/inline/quotation.rb +25 -0
- data/lib/coradoc/asciidoc/model/inline/small.rb +25 -0
- data/lib/coradoc/asciidoc/model/inline/span.rb +38 -0
- data/lib/coradoc/asciidoc/model/inline/stem.rb +24 -0
- data/lib/coradoc/asciidoc/model/inline/strikethrough.rb +39 -0
- data/lib/coradoc/asciidoc/model/inline/subscript.rb +33 -0
- data/lib/coradoc/asciidoc/model/inline/superscript.rb +33 -0
- data/lib/coradoc/asciidoc/model/inline/underline.rb +25 -0
- data/lib/coradoc/asciidoc/model/inline.rb +31 -0
- data/lib/coradoc/asciidoc/model/line_break.rb +11 -0
- data/lib/coradoc/asciidoc/model/list/core.rb +61 -0
- data/lib/coradoc/asciidoc/model/list/definition.rb +27 -0
- data/lib/coradoc/asciidoc/model/list/definition_item.rb +43 -0
- data/lib/coradoc/asciidoc/model/list/item.rb +72 -0
- data/lib/coradoc/asciidoc/model/list/nestable.rb +14 -0
- data/lib/coradoc/asciidoc/model/list/ordered.rb +34 -0
- data/lib/coradoc/asciidoc/model/list/unordered.rb +34 -0
- data/lib/coradoc/asciidoc/model/list.rb +29 -0
- data/lib/coradoc/asciidoc/model/named_attribute.rb +12 -0
- data/lib/coradoc/asciidoc/model/paragraph.rb +59 -0
- data/lib/coradoc/asciidoc/model/rejected_positional_attribute.rb +12 -0
- data/lib/coradoc/asciidoc/model/resolvable.rb +71 -0
- data/lib/coradoc/asciidoc/model/resolver.rb +430 -0
- data/lib/coradoc/asciidoc/model/reviewer_note.rb +54 -0
- data/lib/coradoc/asciidoc/model/revision.rb +47 -0
- data/lib/coradoc/asciidoc/model/section.rb +109 -0
- data/lib/coradoc/asciidoc/model/serialization/asciidoc_adapter.rb +28 -0
- data/lib/coradoc/asciidoc/model/serialization/asciidoc_mapping.rb +42 -0
- data/lib/coradoc/asciidoc/model/serialization/asciidoc_mapping_rule.rb +41 -0
- data/lib/coradoc/asciidoc/model/serialization/asciidoc_transform.rb +211 -0
- data/lib/coradoc/asciidoc/model/serialization/errors.rb +57 -0
- data/lib/coradoc/asciidoc/model/serialization.rb +39 -0
- data/lib/coradoc/asciidoc/model/spacing.rb +282 -0
- data/lib/coradoc/asciidoc/model/table.rb +44 -0
- data/lib/coradoc/asciidoc/model/table_cell.rb +122 -0
- data/lib/coradoc/asciidoc/model/table_row.rb +26 -0
- data/lib/coradoc/asciidoc/model/tag.rb +36 -0
- data/lib/coradoc/asciidoc/model/term.rb +48 -0
- data/lib/coradoc/asciidoc/model/text_element.rb +66 -0
- data/lib/coradoc/asciidoc/model/title.rb +85 -0
- data/lib/coradoc/asciidoc/model/video/attribute_list.rb +43 -0
- data/lib/coradoc/asciidoc/model/video.rb +49 -0
- data/lib/coradoc/asciidoc/model.rb +75 -0
- data/lib/coradoc/asciidoc/parse_error.rb +161 -0
- data/lib/coradoc/asciidoc/parser/admonition.rb +26 -0
- data/lib/coradoc/asciidoc/parser/attribute_list.rb +110 -0
- data/lib/coradoc/asciidoc/parser/base.rb +159 -0
- data/lib/coradoc/asciidoc/parser/bibliography.rb +31 -0
- data/lib/coradoc/asciidoc/parser/block.rb +186 -0
- data/lib/coradoc/asciidoc/parser/block_assembler.rb +183 -0
- data/lib/coradoc/asciidoc/parser/cache.rb +155 -0
- data/lib/coradoc/asciidoc/parser/citation.rb +32 -0
- data/lib/coradoc/asciidoc/parser/content.rb +76 -0
- data/lib/coradoc/asciidoc/parser/document_attributes.rb +27 -0
- data/lib/coradoc/asciidoc/parser/fix_files.rb +76 -0
- data/lib/coradoc/asciidoc/parser/header.rb +31 -0
- data/lib/coradoc/asciidoc/parser/inline.rb +199 -0
- data/lib/coradoc/asciidoc/parser/list.rb +130 -0
- data/lib/coradoc/asciidoc/parser/metadata_detector.rb +164 -0
- data/lib/coradoc/asciidoc/parser/paragraph.rb +64 -0
- data/lib/coradoc/asciidoc/parser/section.rb +62 -0
- data/lib/coradoc/asciidoc/parser/stem.rb +19 -0
- data/lib/coradoc/asciidoc/parser/table.rb +166 -0
- data/lib/coradoc/asciidoc/parser/term.rb +70 -0
- data/lib/coradoc/asciidoc/parser/text.rb +156 -0
- data/lib/coradoc/asciidoc/parser.rb +10 -0
- data/lib/coradoc/asciidoc/serializer/adoc_serializer.rb +86 -0
- data/lib/coradoc/asciidoc/serializer/element_registry.rb +95 -0
- data/lib/coradoc/asciidoc/serializer/fallback_serializer.rb +21 -0
- data/lib/coradoc/asciidoc/serializer/formatter.rb +144 -0
- data/lib/coradoc/asciidoc/serializer/registrations.rb +108 -0
- data/lib/coradoc/asciidoc/serializer/serialization_context.rb +238 -0
- data/lib/coradoc/asciidoc/serializer/serializers/admonition.rb +19 -0
- data/lib/coradoc/asciidoc/serializer/serializers/attribute.rb +23 -0
- data/lib/coradoc/asciidoc/serializer/serializers/attribute_list.rb +40 -0
- data/lib/coradoc/asciidoc/serializer/serializers/attribute_list_attribute.rb +18 -0
- data/lib/coradoc/asciidoc/serializer/serializers/audio.rb +33 -0
- data/lib/coradoc/asciidoc/serializer/serializers/author.rb +20 -0
- data/lib/coradoc/asciidoc/serializer/serializers/base.rb +152 -0
- data/lib/coradoc/asciidoc/serializer/serializers/bibliography.rb +35 -0
- data/lib/coradoc/asciidoc/serializer/serializers/bibliography_entry.rb +24 -0
- data/lib/coradoc/asciidoc/serializer/serializers/block/core.rb +70 -0
- data/lib/coradoc/asciidoc/serializer/serializers/block/example.rb +17 -0
- data/lib/coradoc/asciidoc/serializer/serializers/block/listing.rb +22 -0
- data/lib/coradoc/asciidoc/serializer/serializers/block/literal.rb +17 -0
- data/lib/coradoc/asciidoc/serializer/serializers/block/open.rb +22 -0
- data/lib/coradoc/asciidoc/serializer/serializers/block/pass.rb +17 -0
- data/lib/coradoc/asciidoc/serializer/serializers/block/quote.rb +17 -0
- data/lib/coradoc/asciidoc/serializer/serializers/block/reviewer_comment.rb +17 -0
- data/lib/coradoc/asciidoc/serializer/serializers/block/side.rb +22 -0
- data/lib/coradoc/asciidoc/serializer/serializers/block/source_code.rb +22 -0
- data/lib/coradoc/asciidoc/serializer/serializers/block.rb +23 -0
- data/lib/coradoc/asciidoc/serializer/serializers/break.rb +18 -0
- data/lib/coradoc/asciidoc/serializer/serializers/comment_block.rb +22 -0
- data/lib/coradoc/asciidoc/serializer/serializers/comment_line.rb +22 -0
- data/lib/coradoc/asciidoc/serializer/serializers/document.rb +65 -0
- data/lib/coradoc/asciidoc/serializer/serializers/document_attributes.rb +21 -0
- data/lib/coradoc/asciidoc/serializer/serializers/header.rb +24 -0
- data/lib/coradoc/asciidoc/serializer/serializers/highlight.rb +23 -0
- data/lib/coradoc/asciidoc/serializer/serializers/image/core.rb +30 -0
- data/lib/coradoc/asciidoc/serializer/serializers/image.rb +14 -0
- data/lib/coradoc/asciidoc/serializer/serializers/include.rb +19 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/anchor.rb +20 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/attribute_reference.rb +20 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/bold.rb +26 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/cross_reference.rb +30 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/cross_reference_arg.rb +20 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/footnote.rb +24 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/hard_line_break.rb +20 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/highlight.rb +26 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/italic.rb +26 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/link.rb +38 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/monospace.rb +26 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/quotation.rb +21 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/small.rb +20 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/span.rb +35 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/stem.rb +23 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/strikethrough.rb +29 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/subscript.rb +29 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/superscript.rb +26 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline/underline.rb +20 -0
- data/lib/coradoc/asciidoc/serializer/serializers/inline.rb +32 -0
- data/lib/coradoc/asciidoc/serializer/serializers/line_break.rb +18 -0
- data/lib/coradoc/asciidoc/serializer/serializers/list/core.rb +47 -0
- data/lib/coradoc/asciidoc/serializer/serializers/list/definition.rb +35 -0
- data/lib/coradoc/asciidoc/serializer/serializers/list/definition_item.rb +38 -0
- data/lib/coradoc/asciidoc/serializer/serializers/list/item.rb +120 -0
- data/lib/coradoc/asciidoc/serializer/serializers/list/ordered.rb +24 -0
- data/lib/coradoc/asciidoc/serializer/serializers/list/unordered.rb +29 -0
- data/lib/coradoc/asciidoc/serializer/serializers/list.rb +19 -0
- data/lib/coradoc/asciidoc/serializer/serializers/named_attribute.rb +22 -0
- data/lib/coradoc/asciidoc/serializer/serializers/paragraph.rb +65 -0
- data/lib/coradoc/asciidoc/serializer/serializers/reviewer_note.rb +28 -0
- data/lib/coradoc/asciidoc/serializer/serializers/revision.rb +26 -0
- data/lib/coradoc/asciidoc/serializer/serializers/section.rb +37 -0
- data/lib/coradoc/asciidoc/serializer/serializers/table.rb +24 -0
- data/lib/coradoc/asciidoc/serializer/serializers/table_cell.rb +75 -0
- data/lib/coradoc/asciidoc/serializer/serializers/table_row.rb +24 -0
- data/lib/coradoc/asciidoc/serializer/serializers/tag.rb +19 -0
- data/lib/coradoc/asciidoc/serializer/serializers/term.rb +20 -0
- data/lib/coradoc/asciidoc/serializer/serializers/text_element.rb +23 -0
- data/lib/coradoc/asciidoc/serializer/serializers/title.rb +55 -0
- data/lib/coradoc/asciidoc/serializer/serializers/video.rb +33 -0
- data/lib/coradoc/asciidoc/serializer/spacing_strategy.rb +70 -0
- data/lib/coradoc/asciidoc/serializer.rb +75 -0
- data/lib/coradoc/asciidoc/transform/from_core_model.rb +502 -0
- data/lib/coradoc/asciidoc/transform/from_core_model_registrations.rb +126 -0
- data/lib/coradoc/asciidoc/transform/registry.rb +146 -0
- data/lib/coradoc/asciidoc/transform/to_core_model.rb +564 -0
- data/lib/coradoc/asciidoc/transform/to_core_model_registrations.rb +257 -0
- data/lib/coradoc/asciidoc/transform.rb +13 -0
- data/lib/coradoc/asciidoc/transformer/block_rules.rb +101 -0
- data/lib/coradoc/asciidoc/transformer/header_rules.rb +91 -0
- data/lib/coradoc/asciidoc/transformer/inline_rules.rb +179 -0
- data/lib/coradoc/asciidoc/transformer/list_rules.rb +131 -0
- data/lib/coradoc/asciidoc/transformer/misc_rules.rb +196 -0
- data/lib/coradoc/asciidoc/transformer/structural_rules.rb +216 -0
- data/lib/coradoc/asciidoc/transformer/text_rules.rb +107 -0
- data/lib/coradoc/asciidoc/transformer.rb +406 -0
- data/lib/coradoc/asciidoc/version.rb +7 -0
- data/lib/coradoc/asciidoc.rb +148 -0
- data/lib/coradoc/util/asciidoc.rb +71 -0
- data/lib/coradoc/util.rb +8 -0
- 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
|