Class: Coradoc::AsciiDoc::Parser::BlockAssembler

Inherits:
Object
  • Object
show all
Defined in:
lib/coradoc/asciidoc/parser/block_assembler.rb

Overview

Single Responsibility: Assemble block AST from metadata hints Takes metadata analysis and input text, returns proper AST structure

Class Method Summary collapse

Class Method Details

.assemble(input, metadata) ⇒ Hash?

Main entry point: assemble block AST from input and metadata.

The result hash always includes :delimiter and :lines. :title and :attribute_list are included only when present in the metadata. The previous 4-arm ‘case pattern` switch collapsed into this single method once it became clear the only difference between arms was which optional keys appeared in the result hash.

Parameters:

  • input (String)

    The input text to parse

  • metadata (Hash)

    Analysis from MetadataDetector

Returns:

  • (Hash, nil)

    AST hash, or nil if metadata is nil



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/coradoc/asciidoc/parser/block_assembler.rb', line 20

def self.assemble(input, )
  return nil unless 

  lines = input.lines
  delimiter_line = [:delimiter_line]
  delimiter = [:delimiter][:delimiter]

  result = {
    delimiter: delimiter,
    lines: extract_block_lines(lines, delimiter_line, delimiter)
  }
  result[:title] = [:title][:text] if [:title]
  result[:attribute_list] = parse_attribute_list([:attributes]) if [:attributes]
  result
end

.extract_block_lines(lines, delimiter_line, delimiter) ⇒ Array<Hash>

Helper: Extract content between delimiters

Parameters:

  • lines (Array<String>)

    All lines of input

  • delimiter_line (Integer)

    Line number of opening delimiter

  • delimiter (String)

    The delimiter string (e.g., “****”)

Returns:

  • (Array<Hash>)

    Array of => “…”, :line_break => “n”



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/coradoc/asciidoc/parser/block_assembler.rb', line 41

def self.extract_block_lines(lines, delimiter_line, delimiter)
  block_lines = []

  # Start from line after opening delimiter
  i = delimiter_line + 1

  # Collect lines until closing delimiter
  while i < lines.length
    line = lines[i]

    # Check if this is the closing delimiter
    break if line.strip == delimiter

    stripped = line.strip
    if nested_delimiter?(stripped) && stripped != delimiter
      nested_delimiter = stripped
      nested_lines = extract_block_lines(lines, i, nested_delimiter)

      block_lines << { block: { delimiter: nested_delimiter, lines: nested_lines } }

      i += nested_lines.length + 1
    elsif line.strip.empty?
      # Handle empty lines vs content lines
      block_lines << { line_break: "\n" }
    else
      # Remove trailing newline for processing
      text = line.chomp
      block_lines << { text: text, line_break: "\n" }
    end

    i += 1
  end

  block_lines
end

.nested_delimiter?(str) ⇒ Boolean

Returns:

  • (Boolean)


77
78
79
80
81
82
83
84
85
# File 'lib/coradoc/asciidoc/parser/block_assembler.rb', line 77

def self.nested_delimiter?(str)
  return false if str.length < 2

  char = str[0]
  return false unless ['-', '*', '=', '_', '+'].include?(char)
  return false unless str.chars.all? { |c| c == char }

  (char == '-' && str.length == 2) || str.length >= 4
end

.parse_attribute_list(attr_meta) ⇒ Hash

Parse attribute list to match expected AST structure

Parameters:

  • attr_meta (Hash)

    Attribute metadata from detector

Returns:

  • (Hash)

    Properly formatted attribute_list hash



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/coradoc/asciidoc/parser/block_assembler.rb', line 90

def self.parse_attribute_list(attr_meta)
  return nil unless attr_meta

  # Get the raw content (without brackets)
  content = attr_meta[:content]
  attr_content = content[1...-1] # Remove [ and ]

  # Get attributes array from metadata
  attributes = attr_meta[:attributes]

  # Build attribute_array in expected format
  attribute_array = attributes.map do |attr|
    # Check if it's a named attribute (key=value)
    if attr.include?('=')
      key, value = attr.split('=', 2)
      { named: { named_key: key.strip, named_value: value.strip } }
    else
      # Positional attribute
      { positional: attr.strip }
    end
  end

  {
    attr_content: attr_content,
    attribute_array: attribute_array
  }
end