1
0
Fork 0
mirror of https://github.com/haml/haml.git synced 2022-11-09 12:33:31 -05:00

Merge AttributeCompiler into Compiler

This commit is contained in:
Takashi Kokubun 2015-03-28 20:03:50 +09:00
parent c6465523cc
commit c91944b307
8 changed files with 156 additions and 213 deletions

View file

@ -1,141 +0,0 @@
require 'ripper'
require 'hamlit/filter'
require 'hamlit/concerns/balanceable'
# AttributeCompiler compiles only old-style attribute, which is
# surrounded by brackets.
module Hamlit
class AttributeCompiler < Hamlit::Filter
include Concerns::Balanceable
TYPE_POSITION = 1
def on_haml_attrs(*exps)
attrs = []
exps.map do |exp|
case exp
when /\A{.+}\Z/
attrs += compile_attribute(exp)
else
attrs << compile(exp)
end
end
[:haml, :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.slice(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

View file

@ -2,7 +2,6 @@ require 'hamlit/compilers/attributes'
require 'hamlit/compilers/doctype' require 'hamlit/compilers/doctype'
require 'hamlit/compilers/dynamic' require 'hamlit/compilers/dynamic'
require 'hamlit/compilers/filter' require 'hamlit/compilers/filter'
require 'hamlit/compilers/new_attribute'
require 'hamlit/compilers/preserve' require 'hamlit/compilers/preserve'
require 'hamlit/compilers/script' require 'hamlit/compilers/script'
require 'hamlit/compilers/text' require 'hamlit/compilers/text'
@ -14,7 +13,6 @@ module Hamlit
include Compilers::Doctype include Compilers::Doctype
include Compilers::Dynamic include Compilers::Dynamic
include Compilers::Filter include Compilers::Filter
include Compilers::NewAttribute
include Compilers::Preserve include Compilers::Preserve
include Compilers::Script include Compilers::Script
include Compilers::Text include Compilers::Text

View file

@ -1,17 +1,14 @@
require 'hamlit/compilers/new_attribute'
require 'hamlit/compilers/old_attribute'
module Hamlit module Hamlit
module Compilers module Compilers
module Attributes module Attributes
def on_haml_attrs(*exps) include Compilers::NewAttribute
attrs = [] include Compilers::OldAttribute
exps.each do |exp|
case exp
when /\A(.+)\Z/
attrs += compile_new_attribute(exp)
else
attrs << compile(exp)
end
end
def on_haml_attrs(*attrs)
attrs = compile_attributes(attrs)
attrs = join_ids(attrs) attrs = join_ids(attrs)
attrs = combine_classes(attrs) attrs = combine_classes(attrs)
attrs = pull_class_first(attrs) attrs = pull_class_first(attrs)
@ -20,6 +17,21 @@ module Hamlit
private private
def compile_attributes(exps)
attrs = []
exps.each do |exp|
case exp
when /\A\(.+\)\Z/
attrs += compile_new_attribute(exp)
when /\A{.+}\Z/
attrs += compile_old_attribute(exp)
else
attrs << compile(exp)
end
end
attrs
end
def pull_class_first(attrs) def pull_class_first(attrs)
class_attrs = filter_attrs(attrs, 'class') class_attrs = filter_attrs(attrs, 'class')
combine_classes(class_attrs) + (attrs - class_attrs) combine_classes(class_attrs) + (attrs - class_attrs)

View file

@ -1,6 +1,6 @@
require 'ripper' require 'ripper'
# NewAttributeCompiler compiles new-style attributes, which is # This module compiles new-style attributes, which is
# surrounded by parentheses. # surrounded by parentheses.
module Hamlit module Hamlit
module Compilers module Compilers
@ -9,7 +9,7 @@ module Hamlit
def compile_new_attribute(str) def compile_new_attribute(str)
str = str.gsub(/\A\(|\)\Z/, '') str = str.gsub(/\A\(|\)\Z/, '')
attrs = parse_attributes(str) attrs = parse_new_attributes(str)
attrs.map do |key, value| attrs.map do |key, value|
[:html, :attr, key, [:dynamic, value]] [:html, :attr, key, [:dynamic, value]]
end end
@ -17,7 +17,7 @@ module Hamlit
private private
def parse_attributes(str) def parse_new_attributes(str)
attributes = {} attributes = {}
while str.length > 0 while str.length > 0

View file

@ -0,0 +1,130 @@
require 'ripper'
require 'hamlit/concerns/balanceable'
# This module compiles only old-style attribute, which is
# surrounded by brackets.
# FIXME: remove duplicated code with NewAttribute
module Hamlit
module Compilers
module OldAttribute
include Concerns::Balanceable
TYPE_POSITION = 1
def compile_old_attribute(str)
tokens = Ripper.lex(str)
attrs = parse_old_attributes(tokens)
flatten_attributes(attrs).map do |key, value|
[:html, :attr, key, [:dynamic, value]]
end
end
private
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_old_attributes(tokens)
tokens = tokens.slice(1..-2) # strip braces
attributes = {}
while tokens && tokens.any?
key = read_hash_key!(tokens)
val = read_hash_value!(tokens)
attributes[key] = val if key && val
hash_skip_tokens!(tokens, :on_sp)
raise SyntaxError if tokens.any? && hash_type_of(tokens.shift) != :on_comma
end
attributes
end
def read_hash_key!(tokens)
hash_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 hash_type_of(tokens.shift) != :on_tstring_end
str
end
def read_hash_value!(tokens)
result = ''
hash_skip_tokens!(tokens, :on_sp)
if hash_type_of(tokens.first) == :on_lbrace
hash = fetch_balanced_braces(tokens)
hash.length.times { tokens.shift }
return parse_old_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 hash_skip_tokens!(tokens, *types)
while types.include?(hash_type_of(tokens.first))
tokens.shift
end
end
def assert_rocket!(tokens, *types)
hash_skip_tokens!(tokens, :on_sp)
(row, col), type, str = tokens.shift
raise SyntaxError unless type == :on_op && str == '=>'
end
def hash_type_of(token)
return nil unless token
token[TYPE_POSITION]
end
end
end
end

View file

@ -1,5 +1,4 @@
require 'temple' require 'temple'
require 'hamlit/attribute_compiler'
require 'hamlit/compiler' require 'hamlit/compiler'
require 'hamlit/html/pretty' require 'hamlit/html/pretty'
require 'hamlit/html/ugly' require 'hamlit/html/ugly'
@ -17,7 +16,6 @@ module Hamlit
use Multiline use Multiline
use Parser use Parser
use AttributeCompiler
use Compiler use Compiler
use :Html, -> { create(html_compiler) } use :Html, -> { create(html_compiler) }
filter :Escapable filter :Escapable

View file

@ -579,7 +579,7 @@ describe "haml" do
end end
# FIXME: it requires attribute sorter # FIXME: it requires attribute sorter
pending "Ruby-style tag with a CSS class and 'class' as a variable attribute" do specify "Ruby-style tag with a CSS class and 'class' as a variable attribute" do
haml = %q{.hello{:class => var}} haml = %q{.hello{:class => var}}
html = %q{<div class='hello world'></div>} html = %q{<div class='hello world'></div>}
locals = {:var=>"world"} locals = {:var=>"world"}

View file

@ -1,54 +0,0 @@
describe Hamlit::AttributeCompiler do
describe '#call' do
it 'does not alter normal attrs' do
assert_compile(
[:haml,
:attrs,
[:html, :attr, 'id', [:static, 'foo']],
[:html, :attr, 'class', [:static, 'bar']]],
[:haml,
:attrs,
[:html, :attr, 'id', [:static, 'foo']],
[:html, :attr, 'class', [:static, 'bar']]],
)
end
it 'does not alter news-tyle attributes' do
assert_compile(
[:haml,
:attrs,
'(a=2)'],
[:haml,
:attrs,
'(a=2)'],
)
end
it 'convers only string' do
assert_compile(
[:haml,
:attrs,
[:html, :attr, 'id', [:static, 'foo']],
'{ foo: "bar" }',
[:html, :attr, 'class', [:static, 'bar']]],
[:haml,
:attrs,
[:html, :attr, 'id', [:static, 'foo']],
[:html, :attr, 'foo', [:dynamic, '"bar"']],
[:html, :attr, 'class', [:static, 'bar']]],
)
end
it 'converts nested attributes' do
assert_compile(
[:haml,
:attrs,
'{ a: { b: {}, c: "d" }, e: "f" }'],
[:haml,
:attrs,
[:html, :attr, 'a-c', [:dynamic, '"d"']],
[:html, :attr, 'e', [:dynamic, '"f"']]],
)
end
end
end