haml 5.0.4 → 6.0.0
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 +40 -0
- data/.gitignore +16 -15
- data/CHANGELOG.md +62 -1
- data/Gemfile +18 -14
- data/MIT-LICENSE +2 -2
- data/README.md +4 -5
- data/REFERENCE.md +46 -12
- data/Rakefile +93 -103
- 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 -30
- data/lib/haml/ambles.rb +20 -0
- data/lib/haml/attribute_builder.rb +140 -128
- data/lib/haml/attribute_compiler.rb +86 -181
- data/lib/haml/attribute_parser.rb +86 -124
- 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 +64 -298
- data/lib/haml/dynamic_merger.rb +67 -0
- data/lib/haml/engine.rb +43 -219
- data/lib/haml/error.rb +29 -27
- data/lib/haml/escapable.rb +6 -42
- 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 +55 -378
- data/lib/haml/force_escapable.rb +29 -0
- data/lib/haml/helpers.rb +4 -696
- 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 +208 -43
- data/lib/haml/rails_helpers.rb +51 -0
- data/lib/haml/rails_template.rb +55 -0
- data/lib/haml/railtie.rb +7 -40
- data/lib/haml/ruby_expression.rb +32 -0
- data/lib/haml/string_splitter.rb +20 -0
- data/lib/haml/template.rb +15 -33
- data/lib/haml/temple_line_counter.rb +2 -0
- data/lib/haml/util.rb +23 -21
- data/lib/haml/version.rb +1 -1
- data/lib/haml.rb +8 -19
- metadata +222 -50
- data/.gitmodules +0 -3
- data/.travis.yml +0 -54
- data/.yardopts +0 -23
- data/TODO +0 -24
- data/benchmark.rb +0 -66
- data/bin/haml +0 -9
- data/lib/haml/.gitattributes +0 -1
- data/lib/haml/buffer.rb +0 -235
- data/lib/haml/exec.rb +0 -348
- data/lib/haml/generator.rb +0 -41
- data/lib/haml/helpers/action_view_extensions.rb +0 -59
- data/lib/haml/helpers/action_view_mods.rb +0 -129
- data/lib/haml/helpers/action_view_xss_mods.rb +0 -59
- data/lib/haml/helpers/safe_erubi_template.rb +0 -19
- data/lib/haml/helpers/safe_erubis_template.rb +0 -32
- data/lib/haml/helpers/xss_mods.rb +0 -110
- data/lib/haml/options.rb +0 -273
- data/lib/haml/plugin.rb +0 -34
- data/lib/haml/sass_rails_filter.rb +0 -46
- data/lib/haml/template/options.rb +0 -26
- data/lib/haml/temple_engine.rb +0 -121
- 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,223 +1,128 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require 'haml/attribute_builder'
|
|
2
3
|
require 'haml/attribute_parser'
|
|
4
|
+
require 'haml/ruby_expression'
|
|
3
5
|
|
|
4
6
|
module Haml
|
|
5
7
|
class AttributeCompiler
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
# @return [String] A Ruby literal of value.
|
|
11
|
-
def to_literal
|
|
12
|
-
case type
|
|
13
|
-
when :static
|
|
14
|
-
Haml::Util.inspect_obj(value)
|
|
15
|
-
when :dynamic
|
|
16
|
-
value
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Returns a script to render attributes on runtime.
|
|
22
|
-
#
|
|
23
|
-
# @param attributes [Hash]
|
|
24
|
-
# @param object_ref [String,:nil]
|
|
25
|
-
# @param dynamic_attributes [DynamicAttributes]
|
|
26
|
-
# @return [String] Attributes rendering code
|
|
27
|
-
def self.runtime_build(attributes, object_ref, dynamic_attributes)
|
|
28
|
-
"_hamlout.attributes(#{Haml::Util.inspect_obj(attributes)}, #{object_ref},#{dynamic_attributes.to_literal})"
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# @param options [Haml::Options]
|
|
32
|
-
def initialize(options)
|
|
33
|
-
@is_html = [:html4, :html5].include?(options[:format])
|
|
34
|
-
@attr_wrapper = options[:attr_wrapper]
|
|
8
|
+
def initialize(identity, options)
|
|
9
|
+
@identity = identity
|
|
10
|
+
@quote = options[:attr_quote]
|
|
11
|
+
@format = options[:format]
|
|
35
12
|
@escape_attrs = options[:escape_attrs]
|
|
36
|
-
@hyphenate_data_attrs = options[:hyphenate_data_attrs]
|
|
37
13
|
end
|
|
38
14
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# @param dynamic_attributes [DynamicAttributes]
|
|
44
|
-
# @return [Array] Temple expression
|
|
45
|
-
def compile(attributes, object_ref, dynamic_attributes)
|
|
46
|
-
if object_ref != :nil || !AttributeParser.available?
|
|
47
|
-
return [:dynamic, AttributeCompiler.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)
|
|
48
19
|
end
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
end
|
|
54
|
-
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
|
|
55
24
|
end
|
|
56
|
-
|
|
57
|
-
AttributeBuilder.verify_attribute_names!(attribute_values.map(&:key))
|
|
58
|
-
|
|
59
|
-
[:multi, *group_values_for_sort(attribute_values).map { |value_group|
|
|
60
|
-
compile_attribute_values(value_group)
|
|
61
|
-
}]
|
|
25
|
+
static_compile(node.value[:attributes], hashes)
|
|
62
26
|
end
|
|
63
27
|
|
|
64
28
|
private
|
|
65
29
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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)
|
|
77
59
|
end
|
|
78
60
|
end
|
|
61
|
+
temple
|
|
79
62
|
end
|
|
80
63
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def build_attribute_values(attributes, parsed_hashes)
|
|
88
|
-
[].tap do |attribute_values|
|
|
89
|
-
attributes.each do |key, static_value|
|
|
90
|
-
attribute_values << AttributeValue.new(:static, key, static_value)
|
|
91
|
-
end
|
|
92
|
-
parsed_hashes.each do |parsed_hash|
|
|
93
|
-
parsed_hash.each do |key, dynamic_value|
|
|
94
|
-
attribute_values << AttributeValue.new(:dynamic, key, dynamic_value)
|
|
95
|
-
end
|
|
96
|
-
end
|
|
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]]
|
|
68
|
+
else
|
|
69
|
+
temple << [:html, :attr, key, [:dynamic, build_code]]
|
|
97
70
|
end
|
|
98
71
|
end
|
|
99
72
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def compile_attribute_values(values)
|
|
105
|
-
if values.map(&:key).uniq.size == 1
|
|
106
|
-
compile_attribute(values.first.key, values)
|
|
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]]
|
|
107
77
|
else
|
|
108
|
-
|
|
78
|
+
temple << [:html, :attr, key, [:dynamic, build_code]]
|
|
109
79
|
end
|
|
110
80
|
end
|
|
111
81
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
hash_content = values.group_by(&:key).map do |key, values_for_key|
|
|
116
|
-
"#{frozen_string(key)} => #{merged_value(key, values_for_key)}"
|
|
117
|
-
end.join(', ')
|
|
118
|
-
[:dynamic, "_hamlout.attributes({ #{hash_content} }, nil)"]
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Renders attribute values statically.
|
|
122
|
-
#
|
|
123
|
-
# @param values [Array<AttributeValue>]
|
|
124
|
-
# @return [Array] Temple expression
|
|
125
|
-
def static_build(values)
|
|
126
|
-
hash_content = values.group_by(&:key).map do |key, values_for_key|
|
|
127
|
-
"#{frozen_string(key)} => #{merged_value(key, values_for_key)}"
|
|
128
|
-
end.join(', ')
|
|
129
|
-
|
|
130
|
-
arguments = [@is_html, @attr_wrapper, @escape_attrs, @hyphenate_data_attrs]
|
|
131
|
-
code = "::Haml::AttributeBuilder.build_attributes"\
|
|
132
|
-
"(#{arguments.map { |a| Haml::Util.inspect_obj(a) }.join(', ')}, { #{hash_content} })"
|
|
133
|
-
[:static, eval(code).to_s]
|
|
134
|
-
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(', ')})"
|
|
135
85
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
# @return [String]
|
|
139
|
-
def merged_value(key, values)
|
|
140
|
-
if values.size == 1
|
|
141
|
-
values.first.to_literal
|
|
86
|
+
if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
|
|
87
|
+
temple << [:static, eval(build_code).to_s]
|
|
142
88
|
else
|
|
143
|
-
|
|
89
|
+
temple << [:dynamic, build_code]
|
|
144
90
|
end
|
|
145
91
|
end
|
|
146
92
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def frozen_string(str)
|
|
150
|
-
"#{Haml::Util.inspect_obj(str)}.freeze"
|
|
151
|
-
end
|
|
93
|
+
def compile_boolean!(temple, key, values)
|
|
94
|
+
exp = literal_for(values.last)
|
|
152
95
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return static_build(values)
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
case key
|
|
164
|
-
when 'id', 'class'
|
|
165
|
-
compile_id_or_class_attribute(key, values)
|
|
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
|
|
166
103
|
else
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
# @return [Array] Temple expression
|
|
174
|
-
def compile_id_or_class_attribute(id_or_class, values)
|
|
175
|
-
var = unique_name
|
|
176
|
-
[:multi,
|
|
177
|
-
[:code, "#{var} = (#{merged_value(id_or_class, values)})"],
|
|
178
|
-
[:case, var,
|
|
179
|
-
['Hash, Array', runtime_build([AttributeValue.new(:dynamic, id_or_class, var)])],
|
|
180
|
-
['false, nil', [:multi]],
|
|
181
|
-
[:else, [:multi,
|
|
182
|
-
[:static, " #{id_or_class}=#{@attr_wrapper}"],
|
|
183
|
-
[:escape, @escape_attrs, [:dynamic, var]],
|
|
184
|
-
[:static, @attr_wrapper]],
|
|
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]]],
|
|
185
110
|
]
|
|
186
|
-
|
|
187
|
-
]
|
|
111
|
+
end
|
|
188
112
|
end
|
|
189
113
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
# @return [Array] Temple expression
|
|
193
|
-
def compile_common_attribute(key, values)
|
|
194
|
-
var = unique_name
|
|
195
|
-
[:multi,
|
|
196
|
-
[:code, "#{var} = (#{merged_value(key, values)})"],
|
|
197
|
-
[:case, var,
|
|
198
|
-
['Hash', runtime_build([AttributeValue.new(:dynamic, key, var)])],
|
|
199
|
-
['true', true_value(key)],
|
|
200
|
-
['false, nil', [:multi]],
|
|
201
|
-
[:else, [:multi,
|
|
202
|
-
[:static, " #{key}=#{@attr_wrapper}"],
|
|
203
|
-
[:escape, @escape_attrs, [:dynamic, var]],
|
|
204
|
-
[:static, @attr_wrapper]],
|
|
205
|
-
]
|
|
206
|
-
],
|
|
207
|
-
]
|
|
114
|
+
def compile_common!(temple, key, values)
|
|
115
|
+
temple << [:html, :attr, key, [:fescape, @escape_attrs, values.last]]
|
|
208
116
|
end
|
|
209
117
|
|
|
210
|
-
def
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
else
|
|
214
|
-
[:static, " #{key}=#{@attr_wrapper}#{key}#{@attr_wrapper}"]
|
|
215
|
-
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(', ')})"
|
|
216
121
|
end
|
|
217
122
|
|
|
218
|
-
def
|
|
219
|
-
|
|
220
|
-
"
|
|
123
|
+
def literal_for(value)
|
|
124
|
+
type, exp = value
|
|
125
|
+
type == :static ? "#{exp.inspect}.freeze" : exp
|
|
221
126
|
end
|
|
222
127
|
end
|
|
223
128
|
end
|
|
@@ -1,148 +1,110 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'ripper'
|
|
4
|
-
rescue LoadError
|
|
5
|
-
end
|
|
2
|
+
require 'haml/ruby_expression'
|
|
6
3
|
|
|
7
4
|
module Haml
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class UnexpectedKeyError < StandardError; end
|
|
5
|
+
class AttributeParser
|
|
6
|
+
class ParseSkip < StandardError
|
|
7
|
+
end
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
def self.parse(text)
|
|
10
|
+
self.new.parse(text)
|
|
11
|
+
end
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
def parse(text)
|
|
14
|
+
exp = wrap_bracket(text)
|
|
15
|
+
return if RubyExpression.syntax_error?(exp)
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
23
22
|
end
|
|
23
|
+
hash
|
|
24
|
+
rescue ParseSkip
|
|
25
|
+
nil
|
|
26
|
+
end
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
# @return [Hash<String, String>,nil] - Return parsed attribute Hash whose values are Ruby literals, or return nil if argument is not a single Hash literal.
|
|
27
|
-
def parse(exp)
|
|
28
|
-
return nil unless hash_literal?(exp)
|
|
28
|
+
private
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
def wrap_bracket(text)
|
|
31
|
+
text = text.strip
|
|
32
|
+
return text if text[0] == '{'
|
|
33
|
+
"{#{text}}"
|
|
34
|
+
end
|
|
35
|
+
|
|
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)
|
|
33
54
|
end
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
55
|
+
key
|
|
56
|
+
else
|
|
57
|
+
raise ParseSkip
|
|
37
58
|
end
|
|
59
|
+
end
|
|
38
60
|
|
|
39
|
-
|
|
61
|
+
def assert_type!(token, type)
|
|
62
|
+
raise ParseSkip if token[1] != type
|
|
63
|
+
end
|
|
40
64
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
sym, body = Ripper.sexp(exp)
|
|
46
|
-
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 == '=>'
|
|
47
69
|
end
|
|
70
|
+
end
|
|
48
71
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
while !tokens.empty? && IGNORED_TYPES.include?(tokens.first[TYPE])
|
|
53
|
-
tokens.shift # ignore spaces
|
|
54
|
-
end
|
|
72
|
+
def each_attr(tokens)
|
|
73
|
+
attr_tokens = []
|
|
74
|
+
open_tokens = Hash.new { |h, k| h[k] = 0 }
|
|
55
75
|
|
|
56
|
-
|
|
76
|
+
tokens.each do |token|
|
|
77
|
+
_, type, _ = token
|
|
57
78
|
case type
|
|
58
|
-
when :
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
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
|
|
64
84
|
end
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
when :
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
raise UnexpectedTokenError
|
|
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?
|
|
84
103
|
end
|
|
85
|
-
end
|
|
86
104
|
|
|
87
|
-
|
|
88
|
-
def shift_hash_rocket!(tokens)
|
|
89
|
-
until tokens.empty?
|
|
90
|
-
_, type, str = tokens.shift
|
|
91
|
-
break if type == :on_op && str == '=>'
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
# @param [String] hash_literal
|
|
96
|
-
# @param [Proc] block - that takes [String, String] as arguments
|
|
97
|
-
def each_attribute(hash_literal, &block)
|
|
98
|
-
all_tokens = Ripper.lex(hash_literal.strip)
|
|
99
|
-
all_tokens = all_tokens[1...-1] || [] # strip tokens for brackets
|
|
100
|
-
|
|
101
|
-
each_balanced_tokens(all_tokens) do |tokens|
|
|
102
|
-
key = shift_key!(tokens)
|
|
103
|
-
value = tokens.map {|t| t[2] }.join.strip
|
|
104
|
-
block.call(key, value)
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# @param [Array] tokens - Ripper tokens
|
|
109
|
-
# @param [Proc] block - that takes balanced Ripper tokens as arguments
|
|
110
|
-
def each_balanced_tokens(tokens, &block)
|
|
111
|
-
attr_tokens = []
|
|
112
|
-
open_tokens = Hash.new { |h, k| h[k] = 0 }
|
|
113
|
-
|
|
114
|
-
tokens.each do |token|
|
|
115
|
-
case token[TYPE]
|
|
116
|
-
when :on_comma
|
|
117
|
-
if open_tokens.values.all?(&:zero?)
|
|
118
|
-
block.call(attr_tokens)
|
|
119
|
-
attr_tokens = []
|
|
120
|
-
next
|
|
121
|
-
end
|
|
122
|
-
when :on_lbracket
|
|
123
|
-
open_tokens[:array] += 1
|
|
124
|
-
when :on_rbracket
|
|
125
|
-
open_tokens[:array] -= 1
|
|
126
|
-
when :on_lbrace
|
|
127
|
-
open_tokens[:block] += 1
|
|
128
|
-
when :on_rbrace
|
|
129
|
-
open_tokens[:block] -= 1
|
|
130
|
-
when :on_lparen
|
|
131
|
-
open_tokens[:paren] += 1
|
|
132
|
-
when :on_rparen
|
|
133
|
-
open_tokens[:paren] -= 1
|
|
134
|
-
when :on_embexpr_beg
|
|
135
|
-
open_tokens[:embexpr] += 1
|
|
136
|
-
when :on_embexpr_end
|
|
137
|
-
open_tokens[:embexpr] -= 1
|
|
138
|
-
when *IGNORED_TYPES
|
|
139
|
-
next if attr_tokens.empty?
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
attr_tokens << token
|
|
143
|
-
end
|
|
144
|
-
block.call(attr_tokens) unless attr_tokens.empty?
|
|
105
|
+
attr_tokens << token
|
|
145
106
|
end
|
|
107
|
+
yield(attr_tokens) unless attr_tokens.empty?
|
|
146
108
|
end
|
|
147
109
|
end
|
|
148
110
|
end
|