haml 5.2.2 → 6.0.0.beta.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 +1 -0
- data/.github/workflows/test.yml +13 -9
- data/.gitignore +16 -16
- data/CHANGELOG.md +13 -3
- data/Gemfile +18 -11
- data/MIT-LICENSE +1 -1
- data/README.md +13 -19
- data/Rakefile +95 -93
- 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 +135 -179
- data/lib/haml/attribute_compiler.rb +85 -194
- data/lib/haml/attribute_parser.rb +86 -126
- data/lib/haml/cli.rb +154 -0
- data/lib/haml/compiler/children_compiler.rb +126 -0
- data/lib/haml/compiler/comment_compiler.rb +39 -0
- data/lib/haml/compiler/doctype_compiler.rb +46 -0
- data/lib/haml/compiler/script_compiler.rb +116 -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 +42 -227
- data/lib/haml/error.rb +3 -52
- data/lib/haml/escapable.rb +6 -70
- 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 +49 -0
- data/lib/haml/filters.rb +54 -378
- data/lib/haml/force_escapable.rb +29 -0
- data/lib/haml/haml_error.rb +66 -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 +30 -0
- data/lib/haml/parser.rb +179 -49
- data/lib/haml/rails_helpers.rb +51 -0
- data/lib/haml/rails_template.rb +55 -0
- data/lib/haml/railtie.rb +7 -45
- data/lib/haml/ruby_expression.rb +32 -0
- data/lib/haml/string_splitter.rb +20 -0
- data/lib/haml/template.rb +15 -34
- data/lib/haml/temple_line_counter.rb +2 -1
- data/lib/haml/util.rb +17 -15
- data/lib/haml/version.rb +1 -2
- data/lib/haml.rb +8 -20
- metadata +211 -57
- data/.gitmodules +0 -3
- data/.yardopts +0 -22
- 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/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 || !Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
|
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) unless hash
|
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,110 @@
|
|
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
|
5
|
+
class AttributeParser
|
6
|
+
class ParseSkip < StandardError
|
7
|
+
end
|
14
8
|
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
def self.parse(text)
|
10
|
+
self.new.parse(text)
|
11
|
+
end
|
18
12
|
|
19
|
-
|
13
|
+
def parse(text)
|
14
|
+
exp = wrap_bracket(text)
|
15
|
+
return if RubyExpression.syntax_error?(exp)
|
20
16
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
17
|
+
hash = {}
|
18
|
+
tokens = Ripper.lex(exp)[1..-2] || []
|
19
|
+
each_attr(tokens) do |attr_tokens|
|
20
|
+
key = parse_key!(attr_tokens)
|
21
|
+
hash[key] = attr_tokens.map { |t| t[2] }.join.strip
|
25
22
|
end
|
23
|
+
hash
|
24
|
+
rescue ParseSkip
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
26
29
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
30
|
+
def wrap_bracket(text)
|
31
|
+
text = text.strip
|
32
|
+
return text if text[0] == '{'
|
33
|
+
"{#{text}}"
|
34
|
+
end
|
31
35
|
|
32
|
-
|
33
|
-
|
34
|
-
|
36
|
+
def parse_key!(tokens)
|
37
|
+
_, type, str = tokens.shift
|
38
|
+
case type
|
39
|
+
when :on_sp
|
40
|
+
parse_key!(tokens)
|
41
|
+
when :on_label
|
42
|
+
str.tr(':', '')
|
43
|
+
when :on_symbeg
|
44
|
+
_, _, key = tokens.shift
|
45
|
+
assert_type!(tokens.shift, :on_tstring_end) if str != ':'
|
46
|
+
skip_until_hash_rocket!(tokens)
|
47
|
+
key
|
48
|
+
when :on_tstring_beg
|
49
|
+
_, _, key = tokens.shift
|
50
|
+
next_token = tokens.shift
|
51
|
+
unless next_token[1] == :on_label_end
|
52
|
+
assert_type!(next_token, :on_tstring_end)
|
53
|
+
skip_until_hash_rocket!(tokens)
|
35
54
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
55
|
+
key
|
56
|
+
else
|
57
|
+
raise ParseSkip
|
39
58
|
end
|
59
|
+
end
|
40
60
|
|
41
|
-
|
61
|
+
def assert_type!(token, type)
|
62
|
+
raise ParseSkip if token[1] != type
|
63
|
+
end
|
42
64
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
sym, body = Ripper.sexp(exp)
|
48
|
-
sym == :program && body.is_a?(Array) && body.size == 1 && body[0] && body[0][0] == :hash
|
65
|
+
def skip_until_hash_rocket!(tokens)
|
66
|
+
until tokens.empty?
|
67
|
+
_, type, str = tokens.shift
|
68
|
+
break if type == :on_op && str == '=>'
|
49
69
|
end
|
70
|
+
end
|
50
71
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
while !tokens.empty? && IGNORED_TYPES.include?(tokens.first[TYPE])
|
55
|
-
tokens.shift # ignore spaces
|
56
|
-
end
|
72
|
+
def each_attr(tokens)
|
73
|
+
attr_tokens = []
|
74
|
+
open_tokens = Hash.new { |h, k| h[k] = 0 }
|
57
75
|
|
58
|
-
|
76
|
+
tokens.each do |token|
|
77
|
+
_, type, _ = token
|
59
78
|
case type
|
60
|
-
when :
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
expect_string_end!(tokens.shift)
|
79
|
+
when :on_comma
|
80
|
+
if open_tokens.values.all?(&:zero?)
|
81
|
+
yield(attr_tokens)
|
82
|
+
attr_tokens = []
|
83
|
+
next
|
66
84
|
end
|
67
|
-
|
68
|
-
|
69
|
-
when :
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
85
|
+
when :on_lbracket
|
86
|
+
open_tokens[:array] += 1
|
87
|
+
when :on_rbracket
|
88
|
+
open_tokens[:array] -= 1
|
89
|
+
when :on_lbrace
|
90
|
+
open_tokens[:block] += 1
|
91
|
+
when :on_rbrace
|
92
|
+
open_tokens[:block] -= 1
|
93
|
+
when :on_lparen
|
94
|
+
open_tokens[:paren] += 1
|
95
|
+
when :on_rparen
|
96
|
+
open_tokens[:paren] -= 1
|
97
|
+
when :on_embexpr_beg
|
98
|
+
open_tokens[:embexpr] += 1
|
99
|
+
when :on_embexpr_end
|
100
|
+
open_tokens[:embexpr] -= 1
|
101
|
+
when :on_sp
|
102
|
+
next if attr_tokens.empty?
|
79
103
|
end
|
80
|
-
end
|
81
104
|
|
82
|
-
|
83
|
-
def expect_string_end!(token)
|
84
|
-
if token[TYPE] != :on_tstring_end
|
85
|
-
raise UnexpectedTokenError
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# @param [Array] tokens - Ripper tokens
|
90
|
-
def shift_hash_rocket!(tokens)
|
91
|
-
until tokens.empty?
|
92
|
-
_, type, str = tokens.shift
|
93
|
-
break if type == :on_op && str == '=>'
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
# @param [String] hash_literal
|
98
|
-
# @param [Proc] block - that takes [String, String] as arguments
|
99
|
-
def each_attribute(hash_literal, &block)
|
100
|
-
all_tokens = Ripper.lex(hash_literal.strip)
|
101
|
-
all_tokens = all_tokens[1...-1] || [] # strip tokens for brackets
|
102
|
-
|
103
|
-
each_balanced_tokens(all_tokens) do |tokens|
|
104
|
-
key = shift_key!(tokens)
|
105
|
-
value = tokens.map {|t| t[2] }.join.strip
|
106
|
-
block.call(key, value)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
# @param [Array] tokens - Ripper tokens
|
111
|
-
# @param [Proc] block - that takes balanced Ripper tokens as arguments
|
112
|
-
def each_balanced_tokens(tokens, &block)
|
113
|
-
attr_tokens = []
|
114
|
-
open_tokens = Hash.new { |h, k| h[k] = 0 }
|
115
|
-
|
116
|
-
tokens.each do |token|
|
117
|
-
case token[TYPE]
|
118
|
-
when :on_comma
|
119
|
-
if open_tokens.values.all?(&:zero?)
|
120
|
-
block.call(attr_tokens)
|
121
|
-
attr_tokens = []
|
122
|
-
next
|
123
|
-
end
|
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?
|
142
|
-
end
|
143
|
-
|
144
|
-
attr_tokens << token
|
145
|
-
end
|
146
|
-
block.call(attr_tokens) unless attr_tokens.empty?
|
105
|
+
attr_tokens << token
|
147
106
|
end
|
107
|
+
yield(attr_tokens) unless attr_tokens.empty?
|
148
108
|
end
|
149
109
|
end
|
150
110
|
end
|