Class: Precedent::Parser

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

Constant Summary collapse

BLANK_LINE =
/^\s*$/
COMMENT_LINE =
/^%/
FLUSH_LINE =
/^([^ ].+)$/
FLUSH_QUOTE =
/^    (.+)$/
FOOTNOTE_CONTINUE =
/^\^\s+(.+)$/
FOOTNOTE_START =
/^\^([^ ]+)\s+(.+)$/
HEADING =
/^(#+)\s+(.+)$/
INDENTED =
/^  (.+)$/
INDENTED_QUOTE =
/^      (.+)$/
METADATA =
/^([A-Z][[:ascii:]]*): (.+)$/
RAGGED_LEFT =
/^        (.+)$/
RULE_BODY =
/^\* \* \*\s*$/
RULE_QUOTE =
/^    \* \* \*\s*$/
@@inline_parser =

cached instance of the parser for inline elements

InlineParser.new

Instance Method Summary collapse

Instance Method Details

#build_block(type, first_content = nil) ⇒ Object



37
38
39
40
41
42
43
# File 'lib/precedent/parser.rb', line 37

def build_block(type, first_content=nil)
  if first_content
    { :type => type, :content => [first_content] }
  else
    { :type => type }
  end
end

#meta_value(value) ⇒ Object



112
113
114
115
116
117
118
119
120
121
# File 'lib/precedent/parser.rb', line 112

def meta_value(value)
  v = value.strip
  case v
  when /^\d+$/ then v.to_i
  when /^\d\d\d\d-\d\d-\d\d$/ then Date.parse(v)
  when /^true|yes$/i then true
  when /^false|no$/i then false
  else v
  end
end

#parse(input) ⇒ Object



11
12
13
# File 'lib/precedent/parser.rb', line 11

def parse(input)
  post_process(parse_blocks(input))
end

#parse_blocks(input) ⇒ Object



59
60
61
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/precedent/parser.rb', line 59

def parse_blocks(input)
  block_ended = false
  meta_ended = false

  blocks = []
  meta = {}
  out = {:meta => meta, :blocks => blocks}

  input.lines.each do |line|
    line.chomp!
    if BLANK_LINE =~ line
      block_ended = true
      meta_ended = true
    elsif COMMENT_LINE =~ line # skip
    elsif METADATA =~ line && !meta_ended
      meta[$1.downcase.to_sym] = meta_value($2)
    elsif block_ended || blocks.empty?
      # Start a new block-level element
      start_block(blocks, line)
      block_ended = false
    else
      blocks.last[:content] << line
    end
  end

  out
end

#post_process(raw_hash) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/precedent/parser.rb', line 15

def post_process(raw_hash)
  raw_blocks = raw_hash.delete(:blocks)
  document_blocks = raw_blocks.reduce(
    body: [], footnotes: []
  ) do |mem, block|
    content = block[:content]
    if content
      ast = @@inline_parser.parse(content.join(' ').gsub(/ +/, ' '))
      block.merge!(content: ast.build)
    end

    type = block[:type]
    if type == :footnote
      mem[:footnotes] << block
    else
      mem[:body] << block
    end
    mem
  end
  raw_hash.merge(document_blocks)
end

#start_block(blocks, line) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/precedent/parser.rb', line 87

def start_block(blocks, line)
  case line
  when RULE_QUOTE
    blocks << build_block(:rule_quote)
  when RULE_BODY
    blocks << build_block(:rule)
  when HEADING
    blocks << build_block(:heading, $2).merge(level: $1.length)
  when FOOTNOTE_START
    blocks << build_block(:footnote, $2).merge(marker: $1)
  when FOOTNOTE_CONTINUE
    blocks << build_block(:footnote, $1)
  when RAGGED_LEFT
    blocks << build_block(:ragged_left, $1)
  when INDENTED_QUOTE
    blocks << build_block(:indented_quote, $1)
  when FLUSH_QUOTE
    blocks << build_block(:flush_quote, $1)
  when INDENTED
    blocks << build_block(:indented, $1)
  else # Flush
    blocks << build_block(:flush, line)
  end
end