class HamdownCore::ElementParser

Constants

ELEMENT_REGEXP
NEW_ATTRIBUTE_BEGIN
OBJECT_REF_BEGIN
OLD_ATTRIBUTE_BEGIN

Public Class Methods

new(line_parser) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 11
def initialize(line_parser)
  @line_parser = line_parser
end

Public Instance Methods

parse(text) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 17
def parse(text)
  m = text.match(ELEMENT_REGEXP)
  unless m
    syntax_error!('Invalid element declaration')
  end

  element = Ast::Element.new
  element.filename = @line_parser.filename
  element.lineno = @line_parser.lineno
  element.tag_name = m[1]
  element.static_class, element.static_id = parse_class_and_id(m[2])
  rest = m[3] || ''

  element.old_attributes, element.new_attributes, element.object_ref, rest = parse_attributes(rest)
  element.nuke_inner_whitespace, element.nuke_outer_whitespace, rest = parse_nuke_whitespace(rest)
  element.self_closing, rest = parse_self_closing(rest)
  element.oneline_child = ScriptParser.new(@line_parser).parse(rest)

  element
end

Private Instance Methods

parse_attributes(rest) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 63
def parse_attributes(rest)
  old_attributes = nil
  new_attributes = nil
  object_ref = nil

  loop do
    case rest[0]
    when OLD_ATTRIBUTE_BEGIN
      if old_attributes
        break
      end
      old_attributes, rest = parse_old_attributes(rest)
    when NEW_ATTRIBUTE_BEGIN
      if new_attributes
        break
      end
      new_attributes, rest = parse_new_attributes(rest)
    when OBJECT_REF_BEGIN
      if object_ref
        break
      end
      object_ref, rest = parse_object_ref(rest)
    else
      break
    end
  end

  [old_attributes, new_attributes, object_ref, rest]
end
parse_class_and_id(class_and_id) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 40
def parse_class_and_id(class_and_id)
  classes = []
  id = ''
  scanner = StringScanner.new(class_and_id)
  until scanner.eos?
    unless scanner.scan(/([#.])([-:_a-zA-Z0-9]+)/)
      syntax_error!('Illegal element: classes and ids must have values.')
    end
    case scanner[1]
    when '.'
      classes << scanner[2]
    when '#'
      id = scanner[2]
    end
  end

  [classes.join(' '), id]
end
parse_new_attribute_list(text) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 130
def parse_new_attribute_list(text)
  s = StringScanner.new(text)
  attributes = []
  until s.eos?
    name = scan_key(s)
    s.skip(/\s*/)

    if scan_operator(s)
      s.skip(/\s*/)
      value = scan_value(s)
    else
      value = 'true'
    end
    spaces = s.scan(/\s*/)
    line_count = spaces.count("\n")

    attributes << "#{name.inspect} => #{value},#{"\n" * line_count}"
  end
  attributes.join
end
parse_new_attributes(text) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 111
def parse_new_attributes(text)
  text = text.dup
  s = StringScanner.new(text)
  s.pos = 1
  depth = 1
  loop do
    pre_pos = s.pos
    depth = Utils.balance(s, '(', ')', depth)
    if depth == 0
      t = s.string.byteslice(pre_pos...s.pos - 1)
      return [parse_new_attribute_list(t), s.rest]
    elsif @line_parser.has_next?
      text << "\n" << @line_parser.next_line
    else
      syntax_error!('Unmatched paren')
    end
  end
end
parse_nuke_whitespace(rest) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 214
def parse_nuke_whitespace(rest)
  m = rest.match(/\A(><|<>|[><])(.*)\z/)
  if m
    nuke_whitespace = m[1]
    [
      nuke_whitespace.include?('<'),
      nuke_whitespace.include?('>'),
      m[2],
    ]
  else
    [false, false, rest]
  end
end
parse_object_ref(text) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 203
def parse_object_ref(text)
  s = StringScanner.new(text)
  s.pos = 1
  depth = Utils.balance(s, '[', ']')
  if depth == 0
    [s.pre_match[1, s.pre_match.size - 1], s.rest]
  else
    syntax_error!('Unmatched brackets for object reference')
  end
end
parse_old_attributes(text) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 93
def parse_old_attributes(text)
  text = text.dup
  s = StringScanner.new(text)
  s.pos = 1
  depth = 1
  loop do
    depth = Utils.balance(s, '{', '}', depth)
    if depth == 0
      attr = s.pre_match + s.matched
      return [attr[1, attr.size - 2], s.rest]
    elsif /,\s*\z/ === text && @line_parser.has_next?
      text << "\n" << @line_parser.next_line
    else
      syntax_error!('Unmatched brace')
    end
  end
end
parse_self_closing(rest) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 228
def parse_self_closing(rest)
  if rest[0] == '/'
    if rest.size > 1
      syntax_error!("Self-closing tags can't have content")
    end
    [true, '']
  else
    [false, rest]
  end
end
scan_key(scanner) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 151
def scan_key(scanner)
  scanner.scan(/[-:\w]+/).tap do |name|
    unless name
      syntax_error!('Invalid attribute list (missing attribute name)')
    end
  end
end
scan_operator(scanner) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 159
def scan_operator(scanner)
  scanner.skip(/=/)
end
scan_quoted_value(scanner, quote) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 172
def scan_quoted_value(scanner, quote)
  re = /((?:\\.|\#(?!\{)|[^#{quote}\\#])*)(#{quote}|#\{)/
  pos = scanner.pos
  loop do
    unless scanner.scan(re)
      syntax_error!('Invalid attribute list (mismatched quotation)')
    end
    if scanner[2] == quote
      break
    end
    depth = Utils.balance(scanner, '{', '}')
    if depth != 0
      syntax_error!('Invalid attribute list (mismatched interpolation)')
    end
  end
  str = scanner.string.byteslice(pos - 1..scanner.pos - 1)

  # Even if the quote is single, string interpolation is performed in Haml.
  str[0] = '"'
  str[-1] = '"'
  str
end
scan_value(scanner) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 163
def scan_value(scanner)
  quote = scanner.scan(/["']/)
  if quote
    scan_quoted_value(scanner, quote)
  else
    scan_variable_value(scanner)
  end
end
scan_variable_value(scanner) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 195
def scan_variable_value(scanner)
  scanner.scan(/(@@?|\$)?\w+/).tap do |var|
    unless var
      syntax_error!('Invalid attribute list (invalid variable name)')
    end
  end
end
syntax_error!(message) click to toggle source
# File lib/hamdown_core/element_parser.rb, line 239
def syntax_error!(message)
  raise Error.new(message, @line_parser.lineno)
end