mirror of
https://github.com/haml/haml.git
synced 2022-11-09 12:33:31 -05:00
139 lines
3 KiB
Ruby
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
|