class Markascend::Parser

Constants

NON_PARA_START
REC_BLOCK_STARTS
REC_START

Public Class Methods

new(env, src) click to toggle source
# File lib/markascend/parser.rb, line 17
def initialize env, src
  @src = StringScanner.new src
  @env = env
  @top_level = @env.srcs.empty?
  @env.srcs.push @src
end

Public Instance Methods

parse() click to toggle source
# File lib/markascend/parser.rb, line 24
def parse
  @out = []
  while parse_new_line or parse_rec_block or parse_hx or parse_block_code or parse_paragraph
  end
  unless @src.eos?
    @env.warn 'reached end of input'
  end
  @env.srcs.pop

  @out.map! do |(node, content)|
    case node
    when :footnode_id_ref
      if content < 1 or content > @env.footnotes.size
        raise "footnote not defined: #{content}"
      end
      %Q|<a href="#footnote-#{content}">#{content}</a>|
    when :footnode_acronym_ref
      unless index = @env.footnotes.find_index{|k, _| k == content }
        raise "footnote note defined: #{content}"
      end
      %Q|<a href="#footnote-#{index + 1}">#{content}</a>|
    else
      node
    end
  end
  @out.join
end
parse_block_code() click to toggle source
# File lib/markascend/parser.rb, line 147
def parse_block_code
  if lang = @src.scan(/\|\ *(?!\d)\w*\ *\n/)
    lang = lang[1..-1].strip
    if lang.empty? and @env.hi
      lang = @env.hi
    end
    block = @src.scan(/
      (
        \ *\n      # empty line
      |
        \ {2,}.*\n # line indented equal to 2 or more than 2
      )*
    /x)
    block.gsub!(/^  /, '')
    block.rstrip!
    @out << (::Markascend.hilite block, lang)
    true
  end
end
parse_hx() click to toggle source
# File lib/markascend/parser.rb, line 105
def parse_hx
  hx = @src.scan /h[1-6](\#\w+(-\w+)*)?\ /
  return unless hx
  hx.strip!

  # fiddle id
  if @env.sandbox
    if @env.toc
      id = "-#{@env.toc.size}"
    end
  else
    if hx.size > 2
      id = hx[3..-1]
    elsif @env.toc
      id = "-#{@env.toc.size}"
    end
  end
  if id
    id_attr = %Q{ id="#{id}"}
  end
  hx = hx[0...2]

  @out << "<#{hx}#{id_attr}>"
  line, block = scan_line_and_block
  if line
    out = []
    LineUnit.new(@env, line, block).parse(out)
    out.pop if out.last == :"<br>"
    out.each do |token|
      @out << token
    end
    if id and @env.toc
      @env.toc[id] = [hx[1].to_i, out.join]
    end
  end
  @out << "</#{hx}>"

  # consume one more empty line if possible
  @src.scan /\ *\n/ if (!block or block.empty?)
  true
end
parse_new_line() click to toggle source
# File lib/markascend/parser.rb, line 56
def parse_new_line
  if @src.scan(/\ *\n/)
    @out << '<br>'
    true
  end
end
parse_paragraph() click to toggle source
# File lib/markascend/parser.rb, line 167
def parse_paragraph
  @src.match?(/\ */)
  indent = @src.matched_size
  line, block = scan_line_and_block
  if line
    @out << :"<p>" if @top_level
    LineUnit.new(@env, line, block).parse(@out)

    # same indent and not matching rec/code blocks
    while (@src.match?(/\ */); @src.matched_size) == indent and !@src.match?(NON_PARA_START)
      line, block = scan_line_and_block
      break unless line
      LineUnit.new(@env, line, block).parse(@out)
    end
    # consume one more empty line if possible
    @src.scan /\ *\n/
    # delete back last <br>
    @out.pop if @out.last == :"<br>"
    @out << :"</p>" if @top_level
    true
  end
end
parse_rec_block() click to toggle source
# File lib/markascend/parser.rb, line 63
def parse_rec_block
  return unless @src.match? REC_START

  # first elem, scans the whole of following string:
  # |
  #   + line 1 of the li. an embed \macro
  #       macro content
  #     line 2 of the li.
  #     line 3 of the li.
  #
  # NOTE that first line is always not indented
  line, block = scan_line_and_block 2
  return unless line
  rec_start = line[REC_START]
  wrapper_begin, elem_begin, elem_end, wrapper_end =
    case rec_start
    when '+ '; ['<ol>', '<li>', '</li>', '</ol>']
    when '- '; ['<ul>', '<li>', '</li>', '</ul>']
    when '> '; ['', '<quote>', '</quote>', '']
    end
  elems = ["#{line[2..-1]}#{block}"]

  # followed up elems
  block_start_re = REC_BLOCK_STARTS[rec_start]
  while @src.match?(block_start_re)
    line, block = scan_line_and_block 2
    break unless line
    elems << "#{line[2..-1]}#{block}"
  end

  # generate
  @out << wrapper_begin
  elems.each do |elem|
    @out << elem_begin
    elem.rstrip!
    @out << Parser.new(@env, elem).parse
    @out << elem_end
  end
  @out << wrapper_end
  true
end
scan_line_and_block(undent=:all) click to toggle source
# File lib/markascend/parser.rb, line 190
def scan_line_and_block undent=:all
  if line = @src.scan(/.+?(?:\n|\z)/)
    block = @src.scan(/
      (?:\ *\n)*        # leading empty lines
      (?<indent>\ {2,})
      .*?(?:\n|\z)      # first line content
      (
        \g<indent>\ *
        .*?(?:\n|\z)    # rest line content
        |
        (?:\ *\n)*      # rest empty line
      )*
    /x)
    block = nil if block =~ /\A\s*\z/
    # undent block
    if block
      if undent == :all
        /
          (?:\ *\n)*
          (?<indent>\ {2,})
        /x =~ block
        block.gsub! /^#{indent}/, ''
      else
        block.gsub! /^\ \ /, ''
      end
    end
  end
  [line, block]
end
warnings() click to toggle source
# File lib/markascend/parser.rb, line 52
def warnings
  @env.warnings
end