Class: Coradoc::Markdown::Parser::BlockParser

Inherits:
Parslet::Parser
  • Object
show all
Defined in:
lib/coradoc/markdown/parser/block_parser.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.parse(filename) ⇒ Object



727
728
729
730
731
732
# File 'lib/coradoc/markdown/parser/block_parser.rb', line 727

def self.parse(filename)
  content = File.read(filename)
  new.parse(content)
rescue Parslet::ParseFailed => e
  puts e.parse_failure_cause.ascii_tree
end

.parse_with_processing(content) ⇒ Object

Parse with AST post-processing (escape sequences, etc.)



735
736
737
738
739
740
741
# File 'lib/coradoc/markdown/parser/block_parser.rb', line 735

def self.parse_with_processing(content)
  ast = new.parse(content)
  AstProcessor.process(ast)
rescue Parslet::ParseFailed => e
  puts e.parse_failure_cause.ascii_tree
  nil
end

Instance Method Details

#block_quote_contObject

This implements laziness, which is context-sensitive: “only applies to lines that would have been continuations of paragraphs had they been prepended with block quote markers” means we actually must be inside of a continueable paragraph.

Cannot be a ‘rule` as usual with `dynamic`.



189
190
191
192
193
194
195
196
197
198
# File 'lib/coradoc/markdown/parser/block_parser.rb', line 189

def block_quote_cont
  dynamic do |_src, ctx|
    # puts "BQDYN in #{ctx.captures[:block]}"
    block_quote_marker | if ctx.captures[:block] == :paragraph
                           paragraph_interrupt.absent? >> paragraph_continued_line.present?
                         else
                           any.absent? >> any.present? # never match
                         end
  end
end

#code_fence_infoObject



129
130
131
132
133
134
135
136
137
# File 'lib/coradoc/markdown/parser/block_parser.rb', line 129

def code_fence_info
  # NOTE: Uses dynamic block for context-dependent fence character detection
  # This handles both backtick (`) and tilde (~) fenced code blocks
  dynamic do |_src, ctx|
    char = line_char
    char = str('`').absent? >> char if ctx.captures[:fence].to_s.chr == '`'
    char.repeat(1).as(:info).maybe
  end
end

#consume_fenced_indentObject



153
154
155
156
157
158
159
160
161
162
# File 'lib/coradoc/markdown/parser/block_parser.rb', line 153

def consume_fenced_indent
  dynamic do |_src, ctx|
    indent = ctx.captures[:fence_indent].to_s.length
    if indent.positive?
      str(' ').repeat(0, indent)
    else
      any.present?
    end
  end
end

#continuationObject

Block nesting is the tricky part, but Parslet’s ‘dynamic` and `scope` make it possible to be aware of what blocks we’re already in, and implement a check for whether we’re still inside of those blocks on the beginning of every line. The rules that match the line run inside of the innermost parser expression, but this way they are aware of where they’re nested at runtime.

‘continuation` MUST NOT be a `rule`, otherwise gets cached in a failure state and prevents nested alternatives from working



43
44
45
46
47
48
49
50
51
52
# File 'lib/coradoc/markdown/parser/block_parser.rb', line 43

def continuation
  dynamic do |_src, ctx|
    # puts "parsing continuation at #{src.line_and_column} (#{src.bytepos}) with #{ctx.captures[:cont]}"
    if ctx.captures.key?(:cont)
      ctx.captures[:cont].ignore
    else
      any.present?
    end
  end
end

#debug(msg) ⇒ Object

NOTE: Debug method for parser development. Outputs current parse position and capture context. Only called during parser debugging sessions.



13
14
15
16
17
18
19
# File 'lib/coradoc/markdown/parser/block_parser.rb', line 13

def debug(msg)
  dynamic do |src, ctx|
    puts "#{msg} @ #{src.line_and_column}:"
    pp ctx.captures
    any.present? | any.absent?
  end
end

#open_block(kind, cont_rule) ⇒ Object



54
55
56
57
58
59
60
61
62
63
# File 'lib/coradoc/markdown/parser/block_parser.rb', line 54

def open_block(kind, cont_rule)
  dynamic do |_src, ctx|
    parent_scope = ctx.captures.current.parent
    ctx.captures[:cont] = cont_rule
    ctx.captures[:cont] = parent_scope[:cont] >> cont_rule if parent_scope.key?(:cont)
    ctx.captures[:block] = kind
    # puts "starting block #{kind} at #{src.line_and_column} (#{src.bytepos}): #{ctx.captures[:cont]}"
    any.present? | any.absent?
  end
end

#thematic_break_char(c) ⇒ Object



95
96
97
# File 'lib/coradoc/markdown/parser/block_parser.rb', line 95

def thematic_break_char(c)
  (str(c) >> whitespace.repeat).repeat(3)
end