haml 5.1.2 → 6.3.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 +3 -0
- data/.github/workflows/test.yml +36 -0
- data/.gitignore +16 -15
- data/.yardopts +0 -3
- data/CHANGELOG.md +189 -1
- data/FAQ.md +1 -1
- data/Gemfile +20 -12
- data/MIT-LICENSE +1 -1
- data/README.md +10 -17
- data/REFERENCE.md +129 -164
- data/Rakefile +15 -89
- 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/haml.gemspec +34 -35
- data/lib/haml/ambles.rb +20 -0
- data/lib/haml/attribute_builder.rb +131 -133
- data/lib/haml/attribute_compiler.rb +91 -182
- 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 +52 -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 -691
- 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 +190 -27
- data/lib/haml/rails_helpers.rb +53 -0
- data/lib/haml/rails_template.rb +62 -0
- data/lib/haml/railtie.rb +3 -41
- 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 +20 -16
- data/lib/haml/version.rb +1 -2
- data/lib/haml/whitespace.rb +8 -0
- data/lib/haml.rb +8 -20
- metadata +205 -53
- data/.gitmodules +0 -3
- data/.travis.yml +0 -97
- 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 -238
- data/lib/haml/escapable.rb +0 -50
- 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 -111
- data/lib/haml/options.rb +0 -273
- data/lib/haml/plugin.rb +0 -37
- data/lib/haml/sass_rails_filter.rb +0 -47
- data/lib/haml/template/options.rb +0 -27
- data/lib/haml/temple_engine.rb +0 -123
- 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,224 +1,133 @@
|
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
case type
|
14
|
-
when :static
|
15
|
-
Haml::Util.inspect_obj(value)
|
16
|
-
when :dynamic
|
17
|
-
value
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Returns a script to render attributes on runtime.
|
23
|
-
#
|
24
|
-
# @param attributes [Hash]
|
25
|
-
# @param object_ref [String,:nil]
|
26
|
-
# @param dynamic_attributes [DynamicAttributes]
|
27
|
-
# @return [String] Attributes rendering code
|
28
|
-
def self.runtime_build(attributes, object_ref, dynamic_attributes)
|
29
|
-
"_hamlout.attributes(#{Haml::Util.inspect_obj(attributes)}, #{object_ref},#{dynamic_attributes.to_literal})"
|
30
|
-
end
|
7
|
+
# The list of boolean attributes. You may add custom attributes to this constant.
|
8
|
+
BOOLEAN_ATTRIBUTES = %w[disabled readonly multiple checked autobuffer
|
9
|
+
autoplay controls loop selected hidden scoped async
|
10
|
+
defer reversed ismap seamless muted required
|
11
|
+
autofocus novalidate formnovalidate open pubdate
|
12
|
+
itemscope allowfullscreen default inert sortable
|
13
|
+
truespeed typemustmatch download]
|
31
14
|
|
32
|
-
|
33
|
-
def initialize(options)
|
34
|
-
@
|
35
|
-
@
|
15
|
+
class AttributeCompiler
|
16
|
+
def initialize(identity, options)
|
17
|
+
@identity = identity
|
18
|
+
@quote = options[:attr_quote]
|
19
|
+
@format = options[:format]
|
36
20
|
@escape_attrs = options[:escape_attrs]
|
37
|
-
@hyphenate_data_attrs = options[:hyphenate_data_attrs]
|
38
21
|
end
|
39
22
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
# @param dynamic_attributes [DynamicAttributes]
|
45
|
-
# @return [Array] Temple expression
|
46
|
-
def compile(attributes, object_ref, dynamic_attributes)
|
47
|
-
if object_ref != :nil || !AttributeParser.available?
|
48
|
-
return [:dynamic, AttributeCompiler.runtime_build(attributes, object_ref, dynamic_attributes)]
|
23
|
+
def compile(node)
|
24
|
+
hashes = []
|
25
|
+
if node.value[:object_ref] != :nil || !AttributeParser.available?
|
26
|
+
return runtime_compile(node)
|
49
27
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
-
hash
|
28
|
+
[node.value[:dynamic_attributes].new, node.value[:dynamic_attributes].old].compact.each do |attribute_str|
|
29
|
+
hash = AttributeParser.parse(attribute_str)
|
30
|
+
return runtime_compile(node) if hash.nil? || hash.any? { |_key, value| value.empty? }
|
31
|
+
hashes << hash
|
56
32
|
end
|
57
|
-
|
58
|
-
AttributeBuilder.verify_attribute_names!(attribute_values.map(&:key))
|
59
|
-
|
60
|
-
[:multi, *group_values_for_sort(attribute_values).map { |value_group|
|
61
|
-
compile_attribute_values(value_group)
|
62
|
-
}]
|
33
|
+
static_compile(node.value[:attributes], hashes)
|
63
34
|
end
|
64
35
|
|
65
36
|
private
|
66
37
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
38
|
+
def runtime_compile(node)
|
39
|
+
attrs = []
|
40
|
+
attrs.unshift(node.value[:attributes].inspect) if node.value[:attributes] != {}
|
41
|
+
|
42
|
+
args = [@escape_attrs.inspect, "#{@quote.inspect}.freeze", @format.inspect, node.value[:object_ref]] + attrs
|
43
|
+
[:html, :attrs, [:dynamic, "::Haml::AttributeBuilder.build(#{args.join(', ')}, #{node.value[:dynamic_attributes].to_literal})"]]
|
44
|
+
end
|
45
|
+
|
46
|
+
def static_compile(static_hash, dynamic_hashes)
|
47
|
+
temple = [:html, :attrs]
|
48
|
+
keys = [*static_hash.keys, *dynamic_hashes.map(&:keys).flatten].uniq.sort
|
49
|
+
keys.each do |key|
|
50
|
+
values = [[:static, static_hash[key]], *dynamic_hashes.map { |h| [:dynamic, h[key]] }]
|
51
|
+
values.select! { |_, exp| exp != nil }
|
52
|
+
|
53
|
+
case key
|
54
|
+
when 'id'
|
55
|
+
compile_id!(temple, key, values)
|
56
|
+
when 'class'
|
57
|
+
compile_class!(temple, key, values)
|
58
|
+
when 'data', 'aria'
|
59
|
+
compile_data!(temple, key, values)
|
60
|
+
when *BOOLEAN_ATTRIBUTES, /\Adata-/, /\Aaria-/
|
61
|
+
compile_boolean!(temple, key, values)
|
62
|
+
else
|
63
|
+
compile_common!(temple, key, values)
|
78
64
|
end
|
79
65
|
end
|
66
|
+
temple
|
80
67
|
end
|
81
68
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
def build_attribute_values(attributes, parsed_hashes)
|
89
|
-
[].tap do |attribute_values|
|
90
|
-
attributes.each do |key, static_value|
|
91
|
-
attribute_values << AttributeValue.new(:static, key, static_value)
|
92
|
-
end
|
93
|
-
parsed_hashes.each do |parsed_hash|
|
94
|
-
parsed_hash.each do |key, dynamic_value|
|
95
|
-
attribute_values << AttributeValue.new(:dynamic, key, dynamic_value)
|
96
|
-
end
|
97
|
-
end
|
69
|
+
def compile_id!(temple, key, values)
|
70
|
+
build_code = attribute_builder(:id, values)
|
71
|
+
if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
|
72
|
+
temple << [:html, :attr, key, [:static, eval(build_code).to_s]]
|
73
|
+
else
|
74
|
+
temple << [:html, :attr, key, [:dynamic, build_code]]
|
98
75
|
end
|
99
76
|
end
|
100
77
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
def compile_attribute_values(values)
|
106
|
-
if values.map(&:key).uniq.size == 1
|
107
|
-
compile_attribute(values.first.key, values)
|
78
|
+
def compile_class!(temple, key, values)
|
79
|
+
build_code = attribute_builder(:class, values)
|
80
|
+
if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
|
81
|
+
temple << [:html, :attr, key, [:static, eval(build_code).to_s]]
|
108
82
|
else
|
109
|
-
|
83
|
+
temple << [:html, :attr, key, [:dynamic, build_code]]
|
110
84
|
end
|
111
85
|
end
|
112
86
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
hash_content = values.group_by(&:key).map do |key, values_for_key|
|
117
|
-
"#{frozen_string(key)} => #{merged_value(key, values_for_key)}"
|
118
|
-
end.join(', ')
|
119
|
-
[:dynamic, "_hamlout.attributes({ #{hash_content} }, nil)"]
|
120
|
-
end
|
121
|
-
|
122
|
-
# Renders attribute values statically.
|
123
|
-
#
|
124
|
-
# @param values [Array<AttributeValue>]
|
125
|
-
# @return [Array] Temple expression
|
126
|
-
def static_build(values)
|
127
|
-
hash_content = values.group_by(&:key).map do |key, values_for_key|
|
128
|
-
"#{frozen_string(key)} => #{merged_value(key, values_for_key)}"
|
129
|
-
end.join(', ')
|
130
|
-
|
131
|
-
arguments = [@is_html, @attr_wrapper, @escape_attrs, @hyphenate_data_attrs]
|
132
|
-
code = "::Haml::AttributeBuilder.build_attributes"\
|
133
|
-
"(#{arguments.map { |a| Haml::Util.inspect_obj(a) }.join(', ')}, { #{hash_content} })"
|
134
|
-
[:static, eval(code).to_s]
|
135
|
-
end
|
87
|
+
def compile_data!(temple, key, values)
|
88
|
+
args = [@escape_attrs.inspect, "#{@quote.inspect}.freeze", values.map { |v| literal_for(v) }]
|
89
|
+
build_code = "::Haml::AttributeBuilder.build_#{key}(#{args.join(', ')})"
|
136
90
|
|
137
|
-
|
138
|
-
|
139
|
-
# @return [String]
|
140
|
-
def merged_value(key, values)
|
141
|
-
if values.size == 1
|
142
|
-
values.first.to_literal
|
91
|
+
if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
|
92
|
+
temple << [:static, eval(build_code).to_s]
|
143
93
|
else
|
144
|
-
|
94
|
+
temple << [:dynamic, build_code]
|
145
95
|
end
|
146
96
|
end
|
147
97
|
|
148
|
-
|
149
|
-
|
150
|
-
def frozen_string(str)
|
151
|
-
"#{Haml::Util.inspect_obj(str)}.freeze"
|
152
|
-
end
|
98
|
+
def compile_boolean!(temple, key, values)
|
99
|
+
exp = literal_for(values.last)
|
153
100
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
return static_build(values)
|
162
|
-
end
|
163
|
-
|
164
|
-
case key
|
165
|
-
when 'id', 'class'
|
166
|
-
compile_id_or_class_attribute(key, values)
|
101
|
+
if Temple::StaticAnalyzer.static?(exp)
|
102
|
+
value = eval(exp)
|
103
|
+
case value
|
104
|
+
when true then temple << [:html, :attr, key, @format == :xhtml ? [:static, key] : [:multi]]
|
105
|
+
when false, nil
|
106
|
+
else temple << [:html, :attr, key, [:fescape, @escape_attrs, [:static, value.to_s]]]
|
107
|
+
end
|
167
108
|
else
|
168
|
-
|
109
|
+
var = @identity.generate
|
110
|
+
temple << [
|
111
|
+
:case, "(#{var} = (#{exp}))",
|
112
|
+
['true', [:html, :attr, key, @format == :xhtml ? [:static, key] : [:multi]]],
|
113
|
+
['false, nil', [:multi]],
|
114
|
+
[:else, [:multi, [:static, " #{key}=#{@quote}"], [:fescape, @escape_attrs, [:dynamic, var]], [:static, @quote]]],
|
115
|
+
]
|
169
116
|
end
|
170
117
|
end
|
171
118
|
|
172
|
-
|
173
|
-
|
174
|
-
# @return [Array] Temple expression
|
175
|
-
def compile_id_or_class_attribute(id_or_class, values)
|
176
|
-
var = unique_name
|
177
|
-
[:multi,
|
178
|
-
[:code, "#{var} = (#{merged_value(id_or_class, values)})"],
|
179
|
-
[:case, var,
|
180
|
-
['Hash, Array', runtime_build([AttributeValue.new(:dynamic, id_or_class, var)])],
|
181
|
-
['false, nil', [:multi]],
|
182
|
-
[:else, [:multi,
|
183
|
-
[:static, " #{id_or_class}=#{@attr_wrapper}"],
|
184
|
-
[:escape, @escape_attrs, [:dynamic, var]],
|
185
|
-
[:static, @attr_wrapper]],
|
186
|
-
]
|
187
|
-
],
|
188
|
-
]
|
119
|
+
def compile_common!(temple, key, values)
|
120
|
+
temple << [:html, :attr, key, [:fescape, @escape_attrs, values.last]]
|
189
121
|
end
|
190
122
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
def compile_common_attribute(key, values)
|
195
|
-
var = unique_name
|
196
|
-
[:multi,
|
197
|
-
[:code, "#{var} = (#{merged_value(key, values)})"],
|
198
|
-
[:case, var,
|
199
|
-
['Hash', runtime_build([AttributeValue.new(:dynamic, key, var)])],
|
200
|
-
['true', true_value(key)],
|
201
|
-
['false, nil', [:multi]],
|
202
|
-
[:else, [:multi,
|
203
|
-
[:static, " #{key}=#{@attr_wrapper}"],
|
204
|
-
[:escape, @escape_attrs, [:dynamic, var]],
|
205
|
-
[:static, @attr_wrapper]],
|
206
|
-
]
|
207
|
-
],
|
208
|
-
]
|
209
|
-
end
|
210
|
-
|
211
|
-
def true_value(key)
|
212
|
-
if @is_html
|
213
|
-
[:static, " #{key}"]
|
214
|
-
else
|
215
|
-
[:static, " #{key}=#{@attr_wrapper}#{key}#{@attr_wrapper}"]
|
216
|
-
end
|
123
|
+
def attribute_builder(type, values)
|
124
|
+
args = [@escape_attrs.inspect, *values.map { |v| literal_for(v) }]
|
125
|
+
"::Haml::AttributeBuilder.build_#{type}(#{args.join(', ')})"
|
217
126
|
end
|
218
127
|
|
219
|
-
def
|
220
|
-
|
221
|
-
"
|
128
|
+
def literal_for(value)
|
129
|
+
type, exp = value
|
130
|
+
type == :static ? "#{exp.inspect}.freeze" : exp
|
222
131
|
end
|
223
132
|
end
|
224
133
|
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
|