haml 5.2.2 → 6.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +3 -0
- data/.github/workflows/test.yml +13 -14
- data/.gitignore +16 -16
- data/.yardopts +0 -3
- data/CHANGELOG.md +116 -3
- data/Gemfile +18 -11
- data/MIT-LICENSE +1 -1
- data/README.md +17 -23
- data/REFERENCE.md +69 -145
- data/Rakefile +48 -81
- data/bin/bench +66 -0
- data/bin/console +11 -0
- data/bin/ruby +3 -0
- data/bin/setup +7 -0
- data/bin/stackprof +27 -0
- data/bin/test +24 -0
- data/exe/haml +6 -0
- data/ext/haml/extconf.rb +10 -0
- data/ext/haml/haml.c +537 -0
- data/ext/haml/hescape.c +108 -0
- data/ext/haml/hescape.h +20 -0
- data/haml.gemspec +39 -37
- data/lib/haml/ambles.rb +20 -0
- data/lib/haml/attribute_builder.rb +134 -179
- data/lib/haml/attribute_compiler.rb +85 -194
- data/lib/haml/attribute_parser.rb +92 -126
- data/lib/haml/cli.rb +154 -0
- data/lib/haml/compiler/children_compiler.rb +155 -0
- data/lib/haml/compiler/comment_compiler.rb +51 -0
- data/lib/haml/compiler/doctype_compiler.rb +46 -0
- data/lib/haml/compiler/script_compiler.rb +114 -0
- data/lib/haml/compiler/silent_script_compiler.rb +24 -0
- data/lib/haml/compiler/tag_compiler.rb +76 -0
- data/lib/haml/compiler.rb +63 -296
- data/lib/haml/dynamic_merger.rb +67 -0
- data/lib/haml/engine.rb +48 -227
- data/lib/haml/error.rb +5 -4
- data/lib/haml/escape.rb +13 -0
- data/lib/haml/escape_any.rb +21 -0
- data/lib/haml/filters/base.rb +12 -0
- data/lib/haml/filters/cdata.rb +20 -0
- data/lib/haml/filters/coffee.rb +17 -0
- data/lib/haml/filters/css.rb +33 -0
- data/lib/haml/filters/erb.rb +10 -0
- data/lib/haml/filters/escaped.rb +22 -0
- data/lib/haml/filters/javascript.rb +33 -0
- data/lib/haml/filters/less.rb +20 -0
- data/lib/haml/filters/markdown.rb +11 -0
- data/lib/haml/filters/plain.rb +29 -0
- data/lib/haml/filters/preserve.rb +22 -0
- data/lib/haml/filters/ruby.rb +10 -0
- data/lib/haml/filters/sass.rb +15 -0
- data/lib/haml/filters/scss.rb +15 -0
- data/lib/haml/filters/text_base.rb +25 -0
- data/lib/haml/filters/tilt_base.rb +59 -0
- data/lib/haml/filters.rb +54 -378
- data/lib/haml/force_escape.rb +29 -0
- data/lib/haml/helpers.rb +3 -697
- data/lib/haml/html.rb +22 -0
- data/lib/haml/identity.rb +13 -0
- data/lib/haml/object_ref.rb +35 -0
- data/lib/haml/parser.rb +157 -22
- data/lib/haml/rails_helpers.rb +53 -0
- data/lib/haml/rails_template.rb +57 -0
- data/lib/haml/railtie.rb +3 -46
- data/lib/haml/ruby_expression.rb +32 -0
- data/lib/haml/string_splitter.rb +140 -0
- data/lib/haml/template.rb +15 -34
- data/lib/haml/temple_line_counter.rb +2 -1
- data/lib/haml/util.rb +18 -15
- data/lib/haml/version.rb +1 -2
- data/lib/haml/whitespace.rb +8 -0
- data/lib/haml.rb +8 -20
- metadata +211 -55
- data/.gitmodules +0 -3
- data/TODO +0 -24
- data/benchmark.rb +0 -70
- data/bin/haml +0 -9
- data/lib/haml/.gitattributes +0 -1
- data/lib/haml/buffer.rb +0 -182
- data/lib/haml/escapable.rb +0 -77
- data/lib/haml/exec.rb +0 -347
- data/lib/haml/generator.rb +0 -42
- data/lib/haml/helpers/action_view_extensions.rb +0 -60
- data/lib/haml/helpers/action_view_mods.rb +0 -132
- data/lib/haml/helpers/action_view_xss_mods.rb +0 -60
- data/lib/haml/helpers/safe_erubi_template.rb +0 -20
- data/lib/haml/helpers/safe_erubis_template.rb +0 -33
- data/lib/haml/helpers/xss_mods.rb +0 -114
- data/lib/haml/options.rb +0 -273
- data/lib/haml/plugin.rb +0 -54
- data/lib/haml/sass_rails_filter.rb +0 -47
- data/lib/haml/template/options.rb +0 -27
- data/lib/haml/temple_engine.rb +0 -124
- data/yard/default/.gitignore +0 -1
- data/yard/default/fulldoc/html/css/common.sass +0 -15
- data/yard/default/layout/html/footer.erb +0 -12
@@ -1,237 +1,128 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
require 'haml/attribute_builder'
|
3
3
|
require 'haml/attribute_parser'
|
4
|
+
require 'haml/ruby_expression'
|
4
5
|
|
5
6
|
module Haml
|
6
7
|
class AttributeCompiler
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
# @param options [Haml::Options]
|
13
|
-
def initialize(options)
|
14
|
-
@is_html = [:html4, :html5].include?(options[:format])
|
15
|
-
@attr_wrapper = options[:attr_wrapper]
|
8
|
+
def initialize(identity, options)
|
9
|
+
@identity = identity
|
10
|
+
@quote = options[:attr_quote]
|
11
|
+
@format = options[:format]
|
16
12
|
@escape_attrs = options[:escape_attrs]
|
17
|
-
@hyphenate_data_attrs = options[:hyphenate_data_attrs]
|
18
13
|
end
|
19
14
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# @param dynamic_attributes [Haml::Parser::DynamicAttributes]
|
25
|
-
# @return [Array] Temple expression
|
26
|
-
def compile(attributes, object_ref, dynamic_attributes)
|
27
|
-
if object_ref != :nil || !AttributeParser.available?
|
28
|
-
return [:dynamic, compile_runtime_build(attributes, object_ref, dynamic_attributes)]
|
15
|
+
def compile(node)
|
16
|
+
hashes = []
|
17
|
+
if node.value[:object_ref] != :nil || !AttributeParser.available?
|
18
|
+
return runtime_compile(node)
|
29
19
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
hash
|
20
|
+
[node.value[:dynamic_attributes].new, node.value[:dynamic_attributes].old].compact.each do |attribute_str|
|
21
|
+
hash = AttributeParser.parse(attribute_str)
|
22
|
+
return runtime_compile(node) if hash.nil? || hash.any? { |_key, value| value.empty? }
|
23
|
+
hashes << hash
|
36
24
|
end
|
37
|
-
|
38
|
-
AttributeBuilder.verify_attribute_names!(attribute_values.map(&:key))
|
39
|
-
|
40
|
-
[:multi, *group_values_for_sort(attribute_values).map { |value_group|
|
41
|
-
compile_attribute_values(value_group)
|
42
|
-
}]
|
25
|
+
static_compile(node.value[:attributes], hashes)
|
43
26
|
end
|
44
27
|
|
45
28
|
private
|
46
29
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
"::Haml::AttributeBuilder.build(#{
|
56
|
-
end
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
# @param attributes [{ String => String }]
|
77
|
-
# @param parsed_hashes [{ String => String }]
|
78
|
-
# @return [Array<AttributeValue>]
|
79
|
-
def build_attribute_values(attributes, parsed_hashes)
|
80
|
-
[].tap do |attribute_values|
|
81
|
-
attributes.each do |key, static_value|
|
82
|
-
attribute_values << AttributeValue.new(:static, key, static_value)
|
83
|
-
end
|
84
|
-
parsed_hashes.each do |parsed_hash|
|
85
|
-
parsed_hash.each do |key, dynamic_value|
|
86
|
-
attribute_values << AttributeValue.new(:dynamic, key, dynamic_value)
|
87
|
-
end
|
30
|
+
def runtime_compile(node)
|
31
|
+
attrs = []
|
32
|
+
attrs.unshift(node.value[:attributes].inspect) if node.value[:attributes] != {}
|
33
|
+
|
34
|
+
args = [
|
35
|
+
@escape_attrs.inspect, "#{@quote.inspect}.freeze", @format.inspect,
|
36
|
+
'::Haml::AttributeBuilder::BOOLEAN_ATTRIBUTES', node.value[:object_ref],
|
37
|
+
] + attrs
|
38
|
+
[:html, :attrs, [:dynamic, "::Haml::AttributeBuilder.build(#{args.join(', ')}, #{node.value[:dynamic_attributes].to_literal})"]]
|
39
|
+
end
|
40
|
+
|
41
|
+
def static_compile(static_hash, dynamic_hashes)
|
42
|
+
temple = [:html, :attrs]
|
43
|
+
keys = [*static_hash.keys, *dynamic_hashes.map(&:keys).flatten].uniq.sort
|
44
|
+
keys.each do |key|
|
45
|
+
values = [[:static, static_hash[key]], *dynamic_hashes.map { |h| [:dynamic, h[key]] }]
|
46
|
+
values.select! { |_, exp| exp != nil }
|
47
|
+
|
48
|
+
case key
|
49
|
+
when 'id'
|
50
|
+
compile_id!(temple, key, values)
|
51
|
+
when 'class'
|
52
|
+
compile_class!(temple, key, values)
|
53
|
+
when 'data', 'aria'
|
54
|
+
compile_data!(temple, key, values)
|
55
|
+
when *AttributeBuilder::BOOLEAN_ATTRIBUTES, /\Adata-/, /\Aaria-/
|
56
|
+
compile_boolean!(temple, key, values)
|
57
|
+
else
|
58
|
+
compile_common!(temple, key, values)
|
88
59
|
end
|
89
60
|
end
|
61
|
+
temple
|
90
62
|
end
|
91
63
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
def compile_attribute_values(values)
|
97
|
-
if values.map(&:key).uniq.size == 1
|
98
|
-
compile_attribute(values.first.key, values)
|
64
|
+
def compile_id!(temple, key, values)
|
65
|
+
build_code = attribute_builder(:id, values)
|
66
|
+
if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
|
67
|
+
temple << [:html, :attr, key, [:static, eval(build_code).to_s]]
|
99
68
|
else
|
100
|
-
|
69
|
+
temple << [:html, :attr, key, [:dynamic, build_code]]
|
101
70
|
end
|
102
71
|
end
|
103
72
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
"#{frozen_string(key)} => #{merged_value(key, values_for_key)}"
|
109
|
-
end.join(', ')
|
110
|
-
arguments = [@is_html, @attr_wrapper, @escape_attrs, @hyphenate_data_attrs].map(&method(:to_literal)).join(', ')
|
111
|
-
[:dynamic, "::Haml::AttributeBuilder.build({ #{hash_content} }, nil, #{arguments})"]
|
112
|
-
end
|
113
|
-
|
114
|
-
# Renders attribute values statically.
|
115
|
-
#
|
116
|
-
# @param values [Array<AttributeValue>]
|
117
|
-
# @return [Array] Temple expression
|
118
|
-
def static_build(values)
|
119
|
-
hash_content = values.group_by(&:key).map do |key, values_for_key|
|
120
|
-
"#{frozen_string(key)} => #{merged_value(key, values_for_key)}"
|
121
|
-
end.join(', ')
|
122
|
-
|
123
|
-
arguments = [@is_html, @attr_wrapper, @escape_attrs, @hyphenate_data_attrs]
|
124
|
-
code = "::Haml::AttributeBuilder.build_attributes"\
|
125
|
-
"(#{arguments.map(&method(:to_literal)).join(', ')}, { #{hash_content} })"
|
126
|
-
[:static, eval(code).to_s]
|
127
|
-
end
|
128
|
-
|
129
|
-
# @param key [String]
|
130
|
-
# @param values [Array<AttributeValue>]
|
131
|
-
# @return [String]
|
132
|
-
def merged_value(key, values)
|
133
|
-
if values.size == 1
|
134
|
-
attr_literal(values.first)
|
73
|
+
def compile_class!(temple, key, values)
|
74
|
+
build_code = attribute_builder(:class, values)
|
75
|
+
if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
|
76
|
+
temple << [:html, :attr, key, [:static, eval(build_code).to_s]]
|
135
77
|
else
|
136
|
-
|
78
|
+
temple << [:html, :attr, key, [:dynamic, build_code]]
|
137
79
|
end
|
138
80
|
end
|
139
81
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
"#{to_literal(str)}.freeze"
|
144
|
-
end
|
145
|
-
|
146
|
-
# Compiles attribute values for one key to Temple expression that generates ` key='value'`.
|
147
|
-
#
|
148
|
-
# @param key [String]
|
149
|
-
# @param values [Array<AttributeValue>]
|
150
|
-
# @return [Array] Temple expression
|
151
|
-
def compile_attribute(key, values)
|
152
|
-
if values.all? { |v| Temple::StaticAnalyzer.static?(attr_literal(v)) }
|
153
|
-
return static_build(values)
|
154
|
-
end
|
82
|
+
def compile_data!(temple, key, values)
|
83
|
+
args = [@escape_attrs.inspect, "#{@quote.inspect}.freeze", values.map { |v| literal_for(v) }]
|
84
|
+
build_code = "::Haml::AttributeBuilder.build_#{key}(#{args.join(', ')})"
|
155
85
|
|
156
|
-
|
157
|
-
|
158
|
-
compile_id_or_class_attribute(key, values)
|
86
|
+
if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
|
87
|
+
temple << [:static, eval(build_code).to_s]
|
159
88
|
else
|
160
|
-
|
89
|
+
temple << [:dynamic, build_code]
|
161
90
|
end
|
162
91
|
end
|
163
92
|
|
164
|
-
|
165
|
-
|
166
|
-
# @return [Array] Temple expression
|
167
|
-
def compile_id_or_class_attribute(id_or_class, values)
|
168
|
-
var = unique_name
|
169
|
-
[:multi,
|
170
|
-
[:code, "#{var} = (#{merged_value(id_or_class, values)})"],
|
171
|
-
[:case, var,
|
172
|
-
['Hash, Array', runtime_build([AttributeValue.new(:dynamic, id_or_class, var)])],
|
173
|
-
['false, nil', [:multi]],
|
174
|
-
[:else, [:multi,
|
175
|
-
[:static, " #{id_or_class}=#{@attr_wrapper}"],
|
176
|
-
[:escape, Escapable::EscapeSafeBuffer.new(@escape_attrs), [:dynamic, var]],
|
177
|
-
[:static, @attr_wrapper]],
|
178
|
-
]
|
179
|
-
],
|
180
|
-
]
|
181
|
-
end
|
182
|
-
|
183
|
-
# @param key [String] Not "id" or "class"
|
184
|
-
# @param values [Array<AttributeValue>]
|
185
|
-
# @return [Array] Temple expression
|
186
|
-
def compile_common_attribute(key, values)
|
187
|
-
var = unique_name
|
188
|
-
[:multi,
|
189
|
-
[:code, "#{var} = (#{merged_value(key, values)})"],
|
190
|
-
[:case, var,
|
191
|
-
['Hash', runtime_build([AttributeValue.new(:dynamic, key, var)])],
|
192
|
-
['true', true_value(key)],
|
193
|
-
['false, nil', [:multi]],
|
194
|
-
[:else, [:multi,
|
195
|
-
[:static, " #{key}=#{@attr_wrapper}"],
|
196
|
-
[:escape, Escapable::EscapeSafeBuffer.new(@escape_attrs), [:dynamic, var]],
|
197
|
-
[:static, @attr_wrapper]],
|
198
|
-
]
|
199
|
-
],
|
200
|
-
]
|
201
|
-
end
|
93
|
+
def compile_boolean!(temple, key, values)
|
94
|
+
exp = literal_for(values.last)
|
202
95
|
|
203
|
-
|
204
|
-
|
205
|
-
|
96
|
+
if Temple::StaticAnalyzer.static?(exp)
|
97
|
+
value = eval(exp)
|
98
|
+
case value
|
99
|
+
when true then temple << [:html, :attr, key, @format == :xhtml ? [:static, key] : [:multi]]
|
100
|
+
when false, nil
|
101
|
+
else temple << [:html, :attr, key, [:fescape, @escape_attrs, [:static, value.to_s]]]
|
102
|
+
end
|
206
103
|
else
|
207
|
-
|
104
|
+
var = @identity.generate
|
105
|
+
temple << [
|
106
|
+
:case, "(#{var} = (#{exp}))",
|
107
|
+
['true', [:html, :attr, key, @format == :xhtml ? [:static, key] : [:multi]]],
|
108
|
+
['false, nil', [:multi]],
|
109
|
+
[:else, [:multi, [:static, " #{key}=#{@quote}"], [:fescape, @escape_attrs, [:dynamic, var]], [:static, @quote]]],
|
110
|
+
]
|
208
111
|
end
|
209
112
|
end
|
210
113
|
|
211
|
-
def
|
212
|
-
@
|
213
|
-
"_haml_attribute_compiler#{@unique_name += 1}"
|
114
|
+
def compile_common!(temple, key, values)
|
115
|
+
temple << [:html, :attr, key, [:fescape, @escape_attrs, values.last]]
|
214
116
|
end
|
215
117
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
when :static
|
220
|
-
to_literal(attr.value)
|
221
|
-
when :dynamic
|
222
|
-
attr.value
|
223
|
-
end
|
118
|
+
def attribute_builder(type, values)
|
119
|
+
args = [@escape_attrs.inspect, *values.map { |v| literal_for(v) }]
|
120
|
+
"::Haml::AttributeBuilder.build_#{type}(#{args.join(', ')})"
|
224
121
|
end
|
225
122
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
case value
|
230
|
-
when true, false
|
231
|
-
value.to_s
|
232
|
-
else
|
233
|
-
Haml::Util.inspect_obj(value)
|
234
|
-
end
|
123
|
+
def literal_for(value)
|
124
|
+
type, exp = value
|
125
|
+
type == :static ? "#{exp.inspect}.freeze" : exp
|
235
126
|
end
|
236
127
|
end
|
237
128
|
end
|
@@ -1,150 +1,116 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
begin
|
4
|
-
require 'ripper'
|
5
|
-
rescue LoadError
|
6
|
-
end
|
7
|
-
require 'temple/static_analyzer'
|
2
|
+
require 'haml/ruby_expression'
|
8
3
|
|
9
4
|
module Haml
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
class UnexpectedKeyError < StandardError; end
|
14
|
-
|
15
|
-
# Indices in Ripper tokens
|
16
|
-
TYPE = 1
|
17
|
-
TEXT = 2
|
18
|
-
|
19
|
-
IGNORED_TYPES = %i[on_sp on_ignored_nl].freeze
|
20
|
-
|
21
|
-
class << self
|
22
|
-
# @return [Boolean] - return true if AttributeParser.parse can be used.
|
23
|
-
def available?
|
24
|
-
defined?(Ripper) && Temple::StaticAnalyzer.available?
|
25
|
-
end
|
5
|
+
class AttributeParser
|
6
|
+
class ParseSkip < StandardError
|
7
|
+
end
|
26
8
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
9
|
+
# @return [TrueClass, FalseClass] - return true if AttributeParser.parse can be used.
|
10
|
+
def self.available?
|
11
|
+
# TruffleRuby doesn't have Ripper.lex
|
12
|
+
defined?(Ripper) && Ripper.respond_to?(:lex) && Temple::StaticAnalyzer.available?
|
13
|
+
end
|
31
14
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
36
|
-
hash
|
37
|
-
rescue UnexpectedTokenError, UnexpectedKeyError
|
38
|
-
nil
|
39
|
-
end
|
15
|
+
def self.parse(text)
|
16
|
+
self.new.parse(text)
|
17
|
+
end
|
40
18
|
|
41
|
-
|
19
|
+
def parse(text)
|
20
|
+
exp = wrap_bracket(text)
|
21
|
+
return if Temple::StaticAnalyzer.syntax_error?(exp)
|
42
22
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
sym == :program && body.is_a?(Array) && body.size == 1 && body[0] && body[0][0] == :hash
|
23
|
+
hash = {}
|
24
|
+
tokens = Ripper.lex(exp)[1..-2] || []
|
25
|
+
each_attr(tokens) do |attr_tokens|
|
26
|
+
key = parse_key!(attr_tokens)
|
27
|
+
hash[key] = attr_tokens.map { |t| t[2] }.join.strip
|
49
28
|
end
|
29
|
+
hash
|
30
|
+
rescue ParseSkip
|
31
|
+
nil
|
32
|
+
end
|
50
33
|
|
51
|
-
|
52
|
-
# @return [String] - attribute name in String
|
53
|
-
def shift_key!(tokens)
|
54
|
-
while !tokens.empty? && IGNORED_TYPES.include?(tokens.first[TYPE])
|
55
|
-
tokens.shift # ignore spaces
|
56
|
-
end
|
34
|
+
private
|
57
35
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
key = tokens.shift[TEXT]
|
64
|
-
if first_text != ':' # `:'key'` or `:"key"`
|
65
|
-
expect_string_end!(tokens.shift)
|
66
|
-
end
|
67
|
-
shift_hash_rocket!(tokens)
|
68
|
-
key
|
69
|
-
when :on_tstring_beg # `"key":`, `'key':` or `"key" =>`
|
70
|
-
key = tokens.shift[TEXT]
|
71
|
-
next_token = tokens.shift
|
72
|
-
if next_token[TYPE] != :on_label_end # on_label_end is `":` or `':`, so `"key" =>`
|
73
|
-
expect_string_end!(next_token)
|
74
|
-
shift_hash_rocket!(tokens)
|
75
|
-
end
|
76
|
-
key
|
77
|
-
else
|
78
|
-
raise UnexpectedKeyError.new("unexpected token is given!: #{first_text} (#{type})")
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# @param [Array] token - Ripper token
|
83
|
-
def expect_string_end!(token)
|
84
|
-
if token[TYPE] != :on_tstring_end
|
85
|
-
raise UnexpectedTokenError
|
86
|
-
end
|
87
|
-
end
|
36
|
+
def wrap_bracket(text)
|
37
|
+
text = text.strip
|
38
|
+
return text if text[0] == '{'
|
39
|
+
"{#{text}}"
|
40
|
+
end
|
88
41
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
42
|
+
def parse_key!(tokens)
|
43
|
+
_, type, str = tokens.shift
|
44
|
+
case type
|
45
|
+
when :on_sp
|
46
|
+
parse_key!(tokens)
|
47
|
+
when :on_label
|
48
|
+
str.tr(':', '')
|
49
|
+
when :on_symbeg
|
50
|
+
_, _, key = tokens.shift
|
51
|
+
assert_type!(tokens.shift, :on_tstring_end) if str != ':'
|
52
|
+
skip_until_hash_rocket!(tokens)
|
53
|
+
key
|
54
|
+
when :on_tstring_beg
|
55
|
+
_, _, key = tokens.shift
|
56
|
+
next_token = tokens.shift
|
57
|
+
unless next_token[1] == :on_label_end
|
58
|
+
assert_type!(next_token, :on_tstring_end)
|
59
|
+
skip_until_hash_rocket!(tokens)
|
94
60
|
end
|
61
|
+
key
|
62
|
+
else
|
63
|
+
raise ParseSkip
|
95
64
|
end
|
65
|
+
end
|
96
66
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
all_tokens = Ripper.lex(hash_literal.strip)
|
101
|
-
all_tokens = all_tokens[1...-1] || [] # strip tokens for brackets
|
67
|
+
def assert_type!(token, type)
|
68
|
+
raise ParseSkip if token[1] != type
|
69
|
+
end
|
102
70
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
end
|
71
|
+
def skip_until_hash_rocket!(tokens)
|
72
|
+
until tokens.empty?
|
73
|
+
_, type, str = tokens.shift
|
74
|
+
break if type == :on_op && str == '=>'
|
108
75
|
end
|
76
|
+
end
|
109
77
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
attr_tokens = []
|
114
|
-
open_tokens = Hash.new { |h, k| h[k] = 0 }
|
78
|
+
def each_attr(tokens)
|
79
|
+
attr_tokens = []
|
80
|
+
open_tokens = Hash.new { |h, k| h[k] = 0 }
|
115
81
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
when :on_lbracket
|
125
|
-
open_tokens[:array] += 1
|
126
|
-
when :on_rbracket
|
127
|
-
open_tokens[:array] -= 1
|
128
|
-
when :on_lbrace
|
129
|
-
open_tokens[:block] += 1
|
130
|
-
when :on_rbrace
|
131
|
-
open_tokens[:block] -= 1
|
132
|
-
when :on_lparen
|
133
|
-
open_tokens[:paren] += 1
|
134
|
-
when :on_rparen
|
135
|
-
open_tokens[:paren] -= 1
|
136
|
-
when :on_embexpr_beg
|
137
|
-
open_tokens[:embexpr] += 1
|
138
|
-
when :on_embexpr_end
|
139
|
-
open_tokens[:embexpr] -= 1
|
140
|
-
when *IGNORED_TYPES
|
141
|
-
next if attr_tokens.empty?
|
82
|
+
tokens.each do |token|
|
83
|
+
_, type, _ = token
|
84
|
+
case type
|
85
|
+
when :on_comma
|
86
|
+
if open_tokens.values.all?(&:zero?)
|
87
|
+
yield(attr_tokens)
|
88
|
+
attr_tokens = []
|
89
|
+
next
|
142
90
|
end
|
143
|
-
|
144
|
-
|
91
|
+
when :on_lbracket
|
92
|
+
open_tokens[:array] += 1
|
93
|
+
when :on_rbracket
|
94
|
+
open_tokens[:array] -= 1
|
95
|
+
when :on_lbrace
|
96
|
+
open_tokens[:block] += 1
|
97
|
+
when :on_rbrace
|
98
|
+
open_tokens[:block] -= 1
|
99
|
+
when :on_lparen
|
100
|
+
open_tokens[:paren] += 1
|
101
|
+
when :on_rparen
|
102
|
+
open_tokens[:paren] -= 1
|
103
|
+
when :on_embexpr_beg
|
104
|
+
open_tokens[:embexpr] += 1
|
105
|
+
when :on_embexpr_end
|
106
|
+
open_tokens[:embexpr] -= 1
|
107
|
+
when :on_sp
|
108
|
+
next if attr_tokens.empty?
|
145
109
|
end
|
146
|
-
|
110
|
+
|
111
|
+
attr_tokens << token
|
147
112
|
end
|
113
|
+
yield(attr_tokens) unless attr_tokens.empty?
|
148
114
|
end
|
149
115
|
end
|
150
116
|
end
|