haml 5.2.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 +15 -15
- data/.gitignore +16 -16
- data/.yardopts +0 -3
- data/CHANGELOG.md +168 -4
- data/FAQ.md +1 -1
- data/Gemfile +21 -10
- data/MIT-LICENSE +1 -1
- data/README.md +22 -34
- data/REFERENCE.md +95 -159
- data/Rakefile +15 -82
- 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 -36
- data/lib/haml/ambles.rb +20 -0
- data/lib/haml/attribute_builder.rb +127 -184
- data/lib/haml/attribute_compiler.rb +90 -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 +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 -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 +158 -23
- data/lib/haml/rails_helpers.rb +53 -0
- data/lib/haml/rails_template.rb +62 -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 +19 -15
- data/lib/haml/version.rb +1 -2
- data/lib/haml/whitespace.rb +8 -0
- data/lib/haml.rb +8 -20
- metadata +188 -50
- 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,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
|
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]
|
14
|
+
|
6
15
|
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]
|
16
|
+
def initialize(identity, options)
|
17
|
+
@identity = identity
|
18
|
+
@quote = options[:attr_quote]
|
19
|
+
@format = options[:format]
|
16
20
|
@escape_attrs = options[:escape_attrs]
|
17
|
-
@hyphenate_data_attrs = options[:hyphenate_data_attrs]
|
18
21
|
end
|
19
22
|
|
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)]
|
23
|
+
def compile(node)
|
24
|
+
hashes = []
|
25
|
+
if node.value[:object_ref] != :nil || !AttributeParser.available?
|
26
|
+
return runtime_compile(node)
|
29
27
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
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
|
36
32
|
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
|
-
}]
|
33
|
+
static_compile(node.value[:attributes], hashes)
|
43
34
|
end
|
44
35
|
|
45
36
|
private
|
46
37
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
key
|
67
|
-
|
68
|
-
|
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)
|
69
64
|
end
|
70
65
|
end
|
66
|
+
temple
|
71
67
|
end
|
72
68
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# Compiles attribute values with the similar key to Temple expression.
|
93
|
-
#
|
94
|
-
# @param values [Array<AttributeValue>] whose `key`s are partially or fully the same from left.
|
95
|
-
# @return [Array] Temple expression
|
96
|
-
def compile_attribute_values(values)
|
97
|
-
if values.map(&:key).uniq.size == 1
|
98
|
-
compile_attribute(values.first.key, values)
|
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]]
|
99
73
|
else
|
100
|
-
|
74
|
+
temple << [:html, :attr, key, [:dynamic, build_code]]
|
101
75
|
end
|
102
76
|
end
|
103
77
|
|
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)
|
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]]
|
135
82
|
else
|
136
|
-
|
83
|
+
temple << [:html, :attr, key, [:dynamic, build_code]]
|
137
84
|
end
|
138
85
|
end
|
139
86
|
|
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
|
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(', ')})"
|
155
90
|
|
156
|
-
|
157
|
-
|
158
|
-
compile_id_or_class_attribute(key, values)
|
91
|
+
if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
|
92
|
+
temple << [:static, eval(build_code).to_s]
|
159
93
|
else
|
160
|
-
|
94
|
+
temple << [:dynamic, build_code]
|
161
95
|
end
|
162
96
|
end
|
163
97
|
|
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
|
98
|
+
def compile_boolean!(temple, key, values)
|
99
|
+
exp = literal_for(values.last)
|
202
100
|
|
203
|
-
|
204
|
-
|
205
|
-
|
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
|
206
108
|
else
|
207
|
-
|
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
|
+
]
|
208
116
|
end
|
209
117
|
end
|
210
118
|
|
211
|
-
def
|
212
|
-
@
|
213
|
-
"_haml_attribute_compiler#{@unique_name += 1}"
|
119
|
+
def compile_common!(temple, key, values)
|
120
|
+
temple << [:html, :attr, key, [:fescape, @escape_attrs, values.last]]
|
214
121
|
end
|
215
122
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
when :static
|
220
|
-
to_literal(attr.value)
|
221
|
-
when :dynamic
|
222
|
-
attr.value
|
223
|
-
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(', ')})"
|
224
126
|
end
|
225
127
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
case value
|
230
|
-
when true, false
|
231
|
-
value.to_s
|
232
|
-
else
|
233
|
-
Haml::Util.inspect_obj(value)
|
234
|
-
end
|
128
|
+
def literal_for(value)
|
129
|
+
type, exp = value
|
130
|
+
type == :static ? "#{exp.inspect}.freeze" : exp
|
235
131
|
end
|
236
132
|
end
|
237
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
|