1
0
Fork 0
mirror of https://github.com/haml/haml.git synced 2022-11-09 12:33:31 -05:00
haml--haml/lib/hamlit/attribute_compiler.rb
2015-03-17 23:54:47 +09:00

139 lines
3 KiB
Ruby

require 'ripper'
require 'hamlit/filter'
require 'hamlit/concerns/brace_reader'
module Hamlit
class AttributeCompiler < Hamlit::Filter
include Concerns::BraceReader
TYPE_POSITION = 1
def on_haml_attrs(*exps)
attrs = []
exps.map do |exp|
case exp
when String
attrs += compile_attribute(exp)
else
attrs << compile(exp)
end
end
[:html, :attrs, *attrs]
end
private
def compile_attribute(str)
tokens = Ripper.lex(str)
attrs = parse_attributes(tokens)
flatten_attributes(attrs).map do |key, value|
[:html, :attr, key, [:dynamic, value]]
end
end
def flatten_attributes(attributes)
flattened = {}
attributes.each do |key, value|
case value
when Hash
flatten_attributes(value).each do |k, v|
flattened["#{key}-#{k}"] = v
end
else
flattened[key] = value
end
end
flattened
end
# Parse brace-balanced tokens and return the result as hash
def parse_attributes(tokens)
tokens = tokens[1..-2] # strip braces
attributes = {}
while tokens && tokens.any?
key = read_key!(tokens)
val = read_value!(tokens)
attributes[key] = val if key && val
skip_tokens!(tokens, :on_sp)
raise SyntaxError if tokens.any? && type_of(tokens.shift) != :on_comma
end
attributes
end
def read_key!(tokens)
skip_tokens!(tokens, :on_sp)
(row, col), type, str = tokens.shift
case type
when :on_label
str.gsub!(/:\Z/, '')
when :on_symbeg
if %w[:" :'].include?(str)
str = read_string!(tokens)
else
(row, col), type, str = tokens.shift
end
assert_rocket!(tokens)
when :on_tstring_beg
str = read_string!(tokens)
assert_rocket!(tokens)
end
str
end
def read_string!(tokens)
(row, col), type, str = tokens.shift
return '' if type == :on_tstring_end
raise SyntaxError if type_of(tokens.shift) != :on_tstring_end
str
end
def read_value!(tokens)
result = ''
skip_tokens!(tokens, :on_sp)
if type_of(tokens.first) == :on_lbrace
hash = fetch_balanced_braces(tokens)
hash.length.times { tokens.shift }
return parse_attributes(hash)
end
while token = tokens.shift
(row, col), type, str = token
case type
when :on_sp
next
when :on_comma
tokens.unshift(token)
break
end
result += str
end
result
end
def skip_tokens!(tokens, *types)
while types.include?(type_of(tokens.first))
tokens.shift
end
end
def assert_rocket!(tokens, *types)
skip_tokens!(tokens, :on_sp)
(row, col), type, str = tokens.shift
raise SyntaxError unless type == :on_op && str == '=>'
end
def type_of(token)
return nil unless token
token[TYPE_POSITION]
end
end
end