module Haml::AttributeParser

def available?

Returns:
  • (Boolean) - - return true if AttributeParser.parse can be used.
def available?
  defined?(Ripper) && Temple::StaticAnalyzer.available?
end

def each_attribute(hash_literal, &block)

Parameters:
  • block (Proc) -- - that takes [String, String] as arguments
  • hash_literal (String) --
def each_attribute(hash_literal, &block)
  all_tokens = Ripper.lex(hash_literal.strip)
  all_tokens = all_tokens[1...-1] || [] # strip tokens for brackets
  each_balanced_tokens(all_tokens) do |tokens|
    key   = shift_key!(tokens)
    value = tokens.map {|t| t[2] }.join.strip
    block.call(key, value)
  end
end

def each_balanced_tokens(tokens, &block)

Parameters:
  • block (Proc) -- - that takes balanced Ripper tokens as arguments
  • tokens (Array) -- - Ripper tokens
def each_balanced_tokens(tokens, &block)
  attr_tokens = []
  open_tokens = Hash.new { |h, k| h[k] = 0 }
  tokens.each do |token|
    case token[TYPE]
    when :on_comma
      if open_tokens.values.all?(&:zero?)
        block.call(attr_tokens)
        attr_tokens = []
        next
      end
    when :on_lbracket
      open_tokens[:array] += 1
    when :on_rbracket
      open_tokens[:array] -= 1
    when :on_lbrace
      open_tokens[:block] += 1
    when :on_rbrace
      open_tokens[:block] -= 1
    when :on_lparen
      open_tokens[:paren] += 1
    when :on_rparen
      open_tokens[:paren] -= 1
    when :on_embexpr_beg
      open_tokens[:embexpr] += 1
    when :on_embexpr_end
      open_tokens[:embexpr] -= 1
    when *IGNORED_TYPES
      next if attr_tokens.empty?
    end
    attr_tokens << token
  end
  block.call(attr_tokens) unless attr_tokens.empty?
end

def expect_string_end!(token)

Parameters:
  • token (Array) -- - Ripper token
def expect_string_end!(token)
  if token[TYPE] != :on_tstring_end
    raise UnexpectedTokenError
  end
end

def hash_literal?(exp)

Returns:
  • (Boolean) - - Return true if exp is a single Hash literal

Parameters:
  • exp (String) -- - Ruby expression
def hash_literal?(exp)
  return false if Temple::StaticAnalyzer.syntax_error?(exp)
  sym, body = Ripper.sexp(exp)
  sym == :program && body.is_a?(Array) && body.size == 1 && body[0] && body[0][0] == :hash
end

def parse(exp)

Returns:
  • (Hash, nil) - - Return parsed attribute Hash whose values are Ruby literals, or return nil if argument is not a single Hash literal.

Parameters:
  • exp (String) -- - Old attributes literal or Hash literal generated from new attributes.
def parse(exp)
  return nil unless hash_literal?(exp)
  hash = {}
  each_attribute(exp) do |key, value|
    hash[key] = value
  end
  hash
rescue UnexpectedTokenError, UnexpectedKeyError
  nil
end

def shift_hash_rocket!(tokens)

Parameters:
  • tokens (Array) -- - Ripper tokens
def shift_hash_rocket!(tokens)
  until tokens.empty?
    _, type, str = tokens.shift
    break if type == :on_op && str == '=>'
  end
end

def shift_key!(tokens)

Returns:
  • (String) - - attribute name in String

Parameters:
  • tokens (Array) -- - Ripper tokens. Scanned tokens will be destructively removed from this argument.
def shift_key!(tokens)
  while !tokens.empty? && IGNORED_TYPES.include?(tokens.first[TYPE])
    tokens.shift # ignore spaces
  end
  _, type, first_text = tokens.shift
  case type
  when :on_label # `key:`
    first_text.tr(':', '')
  when :on_symbeg # `:key =>`, `:'key' =>` or `:"key" =>`
    key = tokens.shift[TEXT]
    if first_text != ':' # `:'key'` or `:"key"`
      expect_string_end!(tokens.shift)
    end
    shift_hash_rocket!(tokens)
    key
  when :on_tstring_beg # `"key":`, `'key':` or `"key" =>`
    key = tokens.shift[TEXT]
    next_token = tokens.shift
    if next_token[TYPE] != :on_label_end # on_label_end is `":` or `':`, so `"key" =>`
      expect_string_end!(next_token)
      shift_hash_rocket!(tokens)
    end
    key
  else
    raise UnexpectedKeyError.new("unexpected token is given!: #{first_text} (#{type})")
  end
end