Class: Eprayim::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/eprayim/parser.rb

Constant Summary collapse

HEAD_RULE =
/\A(?<level>=+)\s*(?<title>[^=#\n]*)(?:#(?<anchor>[^\n]*))?\s*/
BLOCK_RULES =
[
  [:head,   HEAD_RULE],
  [:hr,     /\A---+\n*/],
  [:code,   /\A```(?<code>.*?)```\s*$/m],
  [:quote,  /\A(>([^\n]*)\n*)+/m],
  [:list,   /\A(\* ([^\n]*)\n*(  [^\n]*\n*)*)+/],
  [:list,   /\A(\+ ([^\n]*)\n*(  [^\n]*\n*)*)+/],
]
INLINE_RULES =
[
  [:escape, /\A\\(?<char>[\*\\`\+_~])/],
  [:icode,  /\A`(?<content>.+?)`/],
  [:strong, /\A\*\*(?<content>.+?)\*\*/],
  [:bold,   /\A\*(?<content>.+?)\*/],
  [:deleted,/\A~(?<content>.+?)~/],
  [:italic, /\A_(?<content>.+?)_/],
  [:link,   /\A\[\s*(?<float>!|\$|\^)?(?<content>.+\s+)?(?<url>.+)\s*\]/],
]

Instance Method Summary collapse

Constructor Details

#initialize(input) ⇒ Parser




28
29
30
31
32
33
34
35
36
# File 'lib/eprayim/parser.rb', line 28

def initialize(input)
  @input = if input.nil? or input.is_a? String
    StringIO.new(input.to_s)
  elsif input.respond_to?(:read)
    input.clone
  else
    raise "#{input.inspect} should be a string."
  end
end

Instance Method Details

#parseObject



54
55
56
57
58
59
60
# File 'lib/eprayim/parser.rb', line 54

def parse
  elements = []
  while element = self.parse_block
    elements << element
  end
  return Element.new(:doc, *elements)
end

#parse_blockObject



87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/eprayim/parser.rb', line 87

def parse_block
  @text_tail = @input.read if @text_tail.nil?
  return nil if @text_tail.empty?
  BLOCK_RULES.each do |type, regex|
    m = regex.match(@text_tail)
    next if not m
    @text_tail = m.post_match
    return self.send("parse_#{type}", m.to_s, m)
  end
  p, @text_tail = @text_tail.split(/\n\r?\n\r?/, 2)
  @text_tail = '' if @text_tail.nil?
  return parse_para(p, nil)
end

#parse_inline(text) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/eprayim/parser.rb', line 62

def parse_inline(text)
  return [] if text.to_s.empty?
  i = 0
  while not text[i..-1].empty?
    INLINE_RULES.each do |type, regex|
      m = regex.match(text[i..-1])
      next if not m
      r = case type
      when :escape
        m[:char]
      when :link 
        parse_link(m.to_s, m)
      when :icode
        Element.new(:icode, m[:content])
      else
        Element.new(type, *parse_inline(m[:content]))
      end
      return [r, *parse_inline(m.post_match)] if i-1 < 0
      return [text[0..i-1], r, *parse_inline(m.post_match)]
    end
    i += 1
  end
  return [text[0..i]]
end

#peek_headObject

peek the title and anchor without reading all the content.



40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/eprayim/parser.rb', line 40

def peek_head
  return [@title, @anchor] if @title
  @input.rewind
  @input.each_line do |line|
    m = line.match HEAD_RULE
    if m and m[:level] == '='
      @title = m[:title].strip
      @anchor = m[:anchor].strip
      return [@title, @anchor]
    end
  end
  return [nil, nil]
end