module Haml::AttributeParser
def available?
-
(Boolean)
- - return true if AttributeParser.parse can be used.
def available? defined?(Ripper) && Temple::StaticAnalyzer.available? end
def each_attribute(hash_literal, &block)
-
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)
-
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)
-
token
(Array
) -- - Ripper token
def expect_string_end!(token) if token[TYPE] != :on_tstring_end raise UnexpectedTokenError end end
def hash_literal?(exp)
-
(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)
-
(Hash
- - Return parsed attribute Hash whose values are Ruby literals, or return nil if argument is not a single Hash literal., nil)
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)
-
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)
-
(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