sass 3.7.4 → 4.0.0.alpha.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 +13 -5
- data/.yardopts +1 -1
- data/CODE_OF_CONDUCT.md +1 -1
- data/CONTRIBUTING.md +1 -146
- data/MIT-LICENSE +1 -1
- data/README.md +25 -39
- data/Rakefile +274 -0
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/lib/sass.rb +3 -3
- data/lib/sass/cache_stores/filesystem.rb +2 -2
- data/lib/sass/cache_stores/memory.rb +5 -4
- data/lib/sass/callbacks.rb +2 -2
- data/lib/sass/css.rb +12 -12
- data/lib/sass/engine.rb +44 -62
- data/lib/sass/environment.rb +7 -35
- data/lib/sass/error.rb +14 -14
- data/lib/sass/exec/base.rb +14 -3
- data/lib/sass/exec/sass_convert.rb +6 -20
- data/lib/sass/exec/sass_scss.rb +29 -5
- data/lib/sass/features.rb +2 -3
- data/lib/sass/importers/filesystem.rb +6 -11
- data/lib/sass/logger.rb +3 -8
- data/lib/sass/logger/base.rb +2 -19
- data/lib/sass/plugin.rb +2 -3
- data/lib/sass/plugin/compiler.rb +67 -48
- data/lib/sass/plugin/configuration.rb +3 -3
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/rack.rb +3 -3
- data/lib/sass/plugin/staleness_checker.rb +3 -3
- data/lib/sass/railtie.rb +1 -1
- data/lib/sass/script.rb +3 -3
- data/lib/sass/script/css_parser.rb +15 -5
- data/lib/sass/script/functions.rb +121 -337
- data/lib/sass/script/lexer.rb +36 -102
- data/lib/sass/script/parser.rb +153 -529
- data/lib/sass/script/tree/funcall.rb +34 -42
- data/lib/sass/script/tree/interpolation.rb +26 -171
- data/lib/sass/script/tree/list_literal.rb +8 -23
- data/lib/sass/script/tree/map_literal.rb +2 -2
- data/lib/sass/script/tree/node.rb +3 -3
- data/lib/sass/script/tree/operation.rb +16 -43
- data/lib/sass/script/tree/string_interpolation.rb +43 -64
- data/lib/sass/script/tree/variable.rb +1 -1
- data/lib/sass/script/value.rb +0 -2
- data/lib/sass/script/value/arg_list.rb +1 -1
- data/lib/sass/script/value/base.rb +9 -27
- data/lib/sass/script/value/color.rb +18 -26
- data/lib/sass/script/value/helpers.rb +18 -44
- data/lib/sass/script/value/list.rb +14 -35
- data/lib/sass/script/value/map.rb +2 -2
- data/lib/sass/script/value/number.rb +16 -26
- data/lib/sass/script/value/string.rb +1 -30
- data/lib/sass/scss.rb +2 -0
- data/lib/sass/scss/css_parser.rb +3 -7
- data/lib/sass/scss/parser.rb +78 -196
- data/lib/sass/scss/rx.rb +14 -7
- data/lib/sass/scss/script_lexer.rb +15 -0
- data/lib/sass/scss/script_parser.rb +25 -0
- data/lib/sass/scss/static_parser.rb +55 -38
- data/lib/sass/selector.rb +10 -7
- data/lib/sass/selector/abstract_sequence.rb +12 -15
- data/lib/sass/selector/comma_sequence.rb +6 -24
- data/lib/sass/selector/pseudo.rb +6 -19
- data/lib/sass/selector/sequence.rb +16 -14
- data/lib/sass/selector/simple.rb +7 -9
- data/lib/sass/selector/simple_sequence.rb +12 -16
- data/lib/sass/shared.rb +1 -1
- data/lib/sass/source/map.rb +9 -7
- data/lib/sass/source/position.rb +4 -4
- data/lib/sass/stack.rb +3 -23
- data/lib/sass/tree/charset_node.rb +1 -1
- data/lib/sass/tree/comment_node.rb +1 -1
- data/lib/sass/tree/function_node.rb +3 -2
- data/lib/sass/tree/node.rb +3 -5
- data/lib/sass/tree/prop_node.rb +58 -49
- data/lib/sass/tree/rule_node.rb +8 -15
- data/lib/sass/tree/visitors/check_nesting.rb +23 -19
- data/lib/sass/tree/visitors/convert.rb +13 -15
- data/lib/sass/tree/visitors/cssize.rb +15 -4
- data/lib/sass/tree/visitors/deep_copy.rb +2 -2
- data/lib/sass/tree/visitors/extend.rb +14 -10
- data/lib/sass/tree/visitors/perform.rb +18 -29
- data/lib/sass/tree/visitors/set_options.rb +2 -2
- data/lib/sass/tree/visitors/to_css.rb +47 -77
- data/lib/sass/util.rb +311 -98
- data/lib/sass/util/cross_platform_random.rb +19 -0
- data/lib/sass/util/multibyte_string_scanner.rb +133 -127
- data/lib/sass/util/normalized_map.rb +8 -1
- data/lib/sass/util/ordered_hash.rb +192 -0
- data/lib/sass/version.rb +6 -2
- data/test/sass/cache_test.rb +131 -0
- data/test/sass/callbacks_test.rb +61 -0
- data/test/sass/compiler_test.rb +236 -0
- data/test/sass/conversion_test.rb +2171 -0
- data/test/sass/css2sass_test.rb +526 -0
- data/test/sass/data/hsl-rgb.txt +319 -0
- data/test/sass/encoding_test.rb +219 -0
- data/test/sass/engine_test.rb +3400 -0
- data/test/sass/exec_test.rb +86 -0
- data/test/sass/extend_test.rb +1719 -0
- data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
- data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
- data/test/sass/functions_test.rb +1984 -0
- data/test/sass/importer_test.rb +421 -0
- data/test/sass/logger_test.rb +58 -0
- data/test/sass/mock_importer.rb +49 -0
- data/test/sass/more_results/more1.css +9 -0
- data/test/sass/more_results/more1_with_line_comments.css +26 -0
- data/test/sass/more_results/more_import.css +29 -0
- data/test/sass/more_templates/_more_partial.sass +2 -0
- data/test/sass/more_templates/more1.sass +23 -0
- data/test/sass/more_templates/more_import.sass +11 -0
- data/test/sass/plugin_test.rb +556 -0
- data/test/sass/results/alt.css +4 -0
- data/test/sass/results/basic.css +9 -0
- data/test/sass/results/cached_import_option.css +3 -0
- data/test/sass/results/compact.css +5 -0
- data/test/sass/results/complex.css +86 -0
- data/test/sass/results/compressed.css +1 -0
- data/test/sass/results/expanded.css +19 -0
- data/test/sass/results/filename_fn.css +3 -0
- data/test/sass/results/if.css +3 -0
- data/test/sass/results/import.css +31 -0
- data/test/sass/results/import_charset.css +5 -0
- data/test/sass/results/import_charset_1_8.css +5 -0
- data/test/sass/results/import_charset_ibm866.css +5 -0
- data/test/sass/results/import_content.css +1 -0
- data/test/sass/results/line_numbers.css +49 -0
- data/test/sass/results/mixins.css +95 -0
- data/test/sass/results/multiline.css +24 -0
- data/test/sass/results/nested.css +22 -0
- data/test/sass/results/options.css +1 -0
- data/test/sass/results/parent_ref.css +13 -0
- data/test/sass/results/script.css +16 -0
- data/test/sass/results/scss_import.css +31 -0
- data/test/sass/results/scss_importee.css +2 -0
- data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
- data/test/sass/results/subdir/subdir.css +3 -0
- data/test/sass/results/units.css +11 -0
- data/test/sass/results/warn.css +0 -0
- data/test/sass/results/warn_imported.css +0 -0
- data/test/sass/script_conversion_test.rb +306 -0
- data/test/sass/script_test.rb +1206 -0
- data/test/sass/scss/css_test.rb +1281 -0
- data/test/sass/scss/rx_test.rb +160 -0
- data/test/sass/scss/scss_test.rb +4147 -0
- data/test/sass/scss/test_helper.rb +37 -0
- data/test/sass/source_map_test.rb +1055 -0
- data/test/sass/superselector_test.rb +210 -0
- data/test/sass/templates/_cached_import_option_partial.scss +1 -0
- data/test/sass/templates/_double_import_loop2.sass +1 -0
- data/test/sass/templates/_filename_fn_import.scss +11 -0
- data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
- data/test/sass/templates/_imported_charset_utf8.sass +4 -0
- data/test/sass/templates/_imported_content.sass +3 -0
- data/test/sass/templates/_partial.sass +2 -0
- data/test/sass/templates/_same_name_different_partiality.scss +1 -0
- data/test/sass/templates/alt.sass +16 -0
- data/test/sass/templates/basic.sass +23 -0
- data/test/sass/templates/bork1.sass +2 -0
- data/test/sass/templates/bork2.sass +2 -0
- data/test/sass/templates/bork3.sass +2 -0
- data/test/sass/templates/bork4.sass +2 -0
- data/test/sass/templates/bork5.sass +3 -0
- data/test/sass/templates/cached_import_option.scss +3 -0
- data/test/sass/templates/compact.sass +17 -0
- data/test/sass/templates/complex.sass +305 -0
- data/test/sass/templates/compressed.sass +15 -0
- data/test/sass/templates/double_import_loop1.sass +1 -0
- data/test/sass/templates/expanded.sass +17 -0
- data/test/sass/templates/filename_fn.scss +18 -0
- data/test/sass/templates/if.sass +11 -0
- data/test/sass/templates/import.sass +12 -0
- data/test/sass/templates/import_charset.sass +9 -0
- data/test/sass/templates/import_charset_1_8.sass +6 -0
- data/test/sass/templates/import_charset_ibm866.sass +11 -0
- data/test/sass/templates/import_content.sass +4 -0
- data/test/sass/templates/importee.less +2 -0
- data/test/sass/templates/importee.sass +19 -0
- data/test/sass/templates/line_numbers.sass +13 -0
- data/test/sass/templates/mixin_bork.sass +5 -0
- data/test/sass/templates/mixins.sass +76 -0
- data/test/sass/templates/multiline.sass +20 -0
- data/test/sass/templates/nested.sass +25 -0
- data/test/sass/templates/nested_bork1.sass +2 -0
- data/test/sass/templates/nested_bork2.sass +2 -0
- data/test/sass/templates/nested_bork3.sass +2 -0
- data/test/sass/templates/nested_bork4.sass +2 -0
- data/test/sass/templates/nested_import.sass +2 -0
- data/test/sass/templates/nested_mixin_bork.sass +6 -0
- data/test/sass/templates/options.sass +2 -0
- data/test/sass/templates/parent_ref.sass +25 -0
- data/test/sass/templates/same_name_different_ext.sass +2 -0
- data/test/sass/templates/same_name_different_ext.scss +1 -0
- data/test/sass/templates/same_name_different_partiality.scss +1 -0
- data/test/sass/templates/script.sass +101 -0
- data/test/sass/templates/scss_import.scss +12 -0
- data/test/sass/templates/scss_importee.scss +1 -0
- data/test/sass/templates/single_import_loop.sass +1 -0
- data/test/sass/templates/subdir/import_up1.scss +1 -0
- data/test/sass/templates/subdir/import_up2.scss +1 -0
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
- data/test/sass/templates/subdir/subdir.sass +6 -0
- data/test/sass/templates/units.sass +11 -0
- data/test/sass/templates/warn.sass +3 -0
- data/test/sass/templates/warn_imported.sass +4 -0
- data/test/sass/test_helper.rb +8 -0
- data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
- data/test/sass/util/normalized_map_test.rb +51 -0
- data/test/sass/util/subset_map_test.rb +91 -0
- data/test/sass/util_test.rb +438 -0
- data/test/sass/value_helpers_test.rb +179 -0
- data/test/test_helper.rb +110 -0
- data/vendor/listen/CHANGELOG.md +1 -0
- data/vendor/listen/CONTRIBUTING.md +38 -0
- data/vendor/listen/Gemfile +20 -0
- data/vendor/listen/Guardfile +8 -0
- data/vendor/listen/LICENSE +20 -0
- data/vendor/listen/README.md +349 -0
- data/vendor/listen/Rakefile +5 -0
- data/vendor/listen/Vagrantfile +96 -0
- data/vendor/listen/lib/listen.rb +54 -0
- data/vendor/listen/lib/listen/adapter.rb +327 -0
- data/vendor/listen/lib/listen/adapters/bsd.rb +75 -0
- data/vendor/listen/lib/listen/adapters/darwin.rb +48 -0
- data/vendor/listen/lib/listen/adapters/linux.rb +81 -0
- data/vendor/listen/lib/listen/adapters/polling.rb +58 -0
- data/vendor/listen/lib/listen/adapters/windows.rb +91 -0
- data/vendor/listen/lib/listen/directory_record.rb +406 -0
- data/vendor/listen/lib/listen/listener.rb +323 -0
- data/vendor/listen/lib/listen/turnstile.rb +32 -0
- data/vendor/listen/lib/listen/version.rb +3 -0
- data/vendor/listen/listen.gemspec +28 -0
- data/vendor/listen/spec/listen/adapter_spec.rb +149 -0
- data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
- data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
- data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
- data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
- data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
- data/vendor/listen/spec/listen/directory_record_spec.rb +1250 -0
- data/vendor/listen/spec/listen/listener_spec.rb +258 -0
- data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
- data/vendor/listen/spec/listen_spec.rb +67 -0
- data/vendor/listen/spec/spec_helper.rb +25 -0
- data/vendor/listen/spec/support/adapter_helper.rb +666 -0
- data/vendor/listen/spec/support/directory_record_helper.rb +57 -0
- data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
- data/vendor/listen/spec/support/listeners_helper.rb +179 -0
- data/vendor/listen/spec/support/platform_helper.rb +15 -0
- metadata +217 -76
- data/extra/sass-spec-ref.sh +0 -40
- data/lib/sass/deprecation.rb +0 -55
- data/lib/sass/logger/delayed.rb +0 -50
- data/lib/sass/script/value/callable.rb +0 -25
- data/lib/sass/script/value/function.rb +0 -19
data/lib/sass/script/lexer.rb
CHANGED
@@ -19,13 +19,13 @@ module Sass
|
|
19
19
|
# `source_range`: \[`Sass::Source::Range`\]
|
20
20
|
# : The range in the source file in which the token appeared.
|
21
21
|
#
|
22
|
-
# `pos`: \[`
|
22
|
+
# `pos`: \[`Fixnum`\]
|
23
23
|
# : The scanner position at which the SassScript token appeared.
|
24
24
|
Token = Struct.new(:type, :value, :source_range, :pos)
|
25
25
|
|
26
26
|
# The line number of the lexer's current position.
|
27
27
|
#
|
28
|
-
# @return [
|
28
|
+
# @return [Fixnum]
|
29
29
|
def line
|
30
30
|
return @line unless @tok
|
31
31
|
@tok.source_range.start_pos.line
|
@@ -34,7 +34,7 @@ module Sass
|
|
34
34
|
# The number of bytes into the current line
|
35
35
|
# of the lexer's current position (1-based).
|
36
36
|
#
|
37
|
-
# @return [
|
37
|
+
# @return [Fixnum]
|
38
38
|
def offset
|
39
39
|
return @offset unless @tok
|
40
40
|
@tok.source_range.start_pos.offset
|
@@ -51,8 +51,6 @@ module Sass
|
|
51
51
|
':' => :colon,
|
52
52
|
'(' => :lparen,
|
53
53
|
')' => :rparen,
|
54
|
-
'[' => :lsquare,
|
55
|
-
']' => :rsquare,
|
56
54
|
',' => :comma,
|
57
55
|
'and' => :and,
|
58
56
|
'or' => :or,
|
@@ -73,8 +71,9 @@ module Sass
|
|
73
71
|
OPERATORS_REVERSE = Sass::Util.map_hash(OPERATORS) {|k, v| [v, k]}
|
74
72
|
|
75
73
|
TOKEN_NAMES = Sass::Util.map_hash(OPERATORS_REVERSE) {|k, v| [k, v.inspect]}.merge(
|
76
|
-
|
77
|
-
|
74
|
+
:const => "variable (e.g. $foo)",
|
75
|
+
:ident => "identifier (e.g. middle)",
|
76
|
+
:special_fun => '")"')
|
78
77
|
|
79
78
|
# A list of operator strings ordered with longer names first
|
80
79
|
# so that `>` and `<` don't clobber `>=` and `<=`.
|
@@ -82,7 +81,7 @@ module Sass
|
|
82
81
|
|
83
82
|
# A sub-list of {OP_NAMES} that only includes operators
|
84
83
|
# with identifier names.
|
85
|
-
IDENT_OP_NAMES = OP_NAMES.select {|k,
|
84
|
+
IDENT_OP_NAMES = OP_NAMES.select {|k, v| k =~ /^\w+/}
|
86
85
|
|
87
86
|
PARSEABLE_NUMBER = /(?:(\d*\.\d+)|(\d+))(?:[eE]([+-]?\d+))?(#{UNIT})?/
|
88
87
|
|
@@ -144,12 +143,12 @@ module Sass
|
|
144
143
|
}
|
145
144
|
|
146
145
|
# @param str [String, StringScanner] The source text to lex
|
147
|
-
# @param line [
|
146
|
+
# @param line [Fixnum] The 1-based line on which the SassScript appears.
|
148
147
|
# Used for error reporting and sourcemap building
|
149
|
-
# @param offset [
|
148
|
+
# @param offset [Fixnum] The 1-based character (not byte) offset in the line in the source.
|
150
149
|
# Used for error reporting and sourcemap building
|
151
150
|
# @param options [{Symbol => Object}] An options hash;
|
152
|
-
# see {file:SASS_REFERENCE.md#
|
151
|
+
# see {file:SASS_REFERENCE.md#options the Sass options documentation}
|
153
152
|
def initialize(str, line, offset, options)
|
154
153
|
@scanner = str.is_a?(StringScanner) ? str : Sass::Util::MultibyteStringScanner.new(str)
|
155
154
|
@line = line
|
@@ -157,8 +156,13 @@ module Sass
|
|
157
156
|
@options = options
|
158
157
|
@interpolation_stack = []
|
159
158
|
@prev = nil
|
160
|
-
|
161
|
-
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns whether or not there's whitespace before the given token.
|
162
|
+
#
|
163
|
+
# @return [Boolean]
|
164
|
+
def whitespace_before?(tok)
|
165
|
+
@scanner.string[0...tok.pos] =~ /\s\Z/
|
162
166
|
end
|
163
167
|
|
164
168
|
# Moves the lexer forward one token.
|
@@ -171,33 +175,6 @@ module Sass
|
|
171
175
|
tok
|
172
176
|
end
|
173
177
|
|
174
|
-
# Returns whether or not there's whitespace before the next token.
|
175
|
-
#
|
176
|
-
# @return [Boolean]
|
177
|
-
def whitespace?(tok = @tok)
|
178
|
-
if tok
|
179
|
-
@scanner.string[0...tok.pos] =~ /\s\Z/
|
180
|
-
else
|
181
|
-
@scanner.string[@scanner.pos, 1] =~ /^\s/ ||
|
182
|
-
@scanner.string[@scanner.pos - 1, 1] =~ /\s\Z/
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
# Returns the given character.
|
187
|
-
#
|
188
|
-
# @return [String]
|
189
|
-
def char(pos = @scanner.pos)
|
190
|
-
@scanner.string[pos, 1]
|
191
|
-
end
|
192
|
-
|
193
|
-
# Consumes and returns single raw character from the input stream.
|
194
|
-
#
|
195
|
-
# @return [String]
|
196
|
-
def next_char
|
197
|
-
unpeek!
|
198
|
-
scan(/./)
|
199
|
-
end
|
200
|
-
|
201
178
|
# Returns the next token without moving the lexer forward.
|
202
179
|
#
|
203
180
|
# @return [Token] The next token
|
@@ -208,11 +185,11 @@ module Sass
|
|
208
185
|
# Rewinds the underlying StringScanner
|
209
186
|
# to before the token returned by \{#peek}.
|
210
187
|
def unpeek!
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
188
|
+
if @tok
|
189
|
+
@scanner.pos = @tok.pos
|
190
|
+
@line = @tok.source_range.start_pos.line
|
191
|
+
@offset = @tok.source_range.start_pos.offset
|
192
|
+
end
|
216
193
|
end
|
217
194
|
|
218
195
|
# @return [Boolean] Whether or not there's more source text to lex.
|
@@ -222,11 +199,6 @@ module Sass
|
|
222
199
|
@scanner.eos? && @tok.nil?
|
223
200
|
end
|
224
201
|
|
225
|
-
# @return [Boolean] Whether or not the last token lexed was `:end_interpolation`.
|
226
|
-
def after_interpolation?
|
227
|
-
@prev && @prev.type == :end_interpolation
|
228
|
-
end
|
229
|
-
|
230
202
|
# Raise an error to the effect that `name` was expected in the input stream
|
231
203
|
# and wasn't found.
|
232
204
|
#
|
@@ -252,32 +224,13 @@ module Sass
|
|
252
224
|
@scanner.string[old_pos...new_pos]
|
253
225
|
end
|
254
226
|
|
255
|
-
|
256
|
-
# block if it returns `nil` or `false`.
|
257
|
-
def try
|
258
|
-
old_pos = @scanner.pos
|
259
|
-
old_line = @line
|
260
|
-
old_offset = @offset
|
261
|
-
old_interpolation_stack = @interpolation_stack.dup
|
262
|
-
old_prev = @prev
|
263
|
-
old_tok = @tok
|
264
|
-
old_next_tok = @next_tok
|
265
|
-
|
266
|
-
result = yield
|
267
|
-
return result if result
|
227
|
+
private
|
268
228
|
|
269
|
-
|
270
|
-
|
271
|
-
@
|
272
|
-
@interpolation_stack = old_interpolation_stack
|
273
|
-
@prev = old_prev
|
274
|
-
@tok = old_tok
|
275
|
-
@next_tok = old_next_tok
|
276
|
-
nil
|
229
|
+
# @return [Boolean] Whether or not the last token lexed was `:end_interpolation`.
|
230
|
+
def after_interpolation?
|
231
|
+
@prev && @prev.type == :end_interpolation
|
277
232
|
end
|
278
233
|
|
279
|
-
private
|
280
|
-
|
281
234
|
def read_token
|
282
235
|
if (tok = @next_tok)
|
283
236
|
@next_tok = nil
|
@@ -299,14 +252,10 @@ module Sass
|
|
299
252
|
end
|
300
253
|
|
301
254
|
def token
|
302
|
-
if after_interpolation?
|
303
|
-
interp_type, interp_value =
|
255
|
+
if after_interpolation? && (interp = @interpolation_stack.pop)
|
256
|
+
interp_type, interp_value = interp
|
304
257
|
if interp_type == :special_fun
|
305
258
|
return special_fun_body(interp_value)
|
306
|
-
elsif interp_type.nil?
|
307
|
-
if @scanner.string[@scanner.pos - 1] == '}' && scan(REGULAR_EXPRESSIONS[:ident])
|
308
|
-
return [@scanner[2] ? :funcall : :ident, Sass::Util.normalize_ident_escapes(@scanner[1], start: false)]
|
309
|
-
end
|
310
259
|
else
|
311
260
|
raise "[BUG]: Unknown interp_type #{interp_type}" unless interp_type == :string
|
312
261
|
return string(interp_value, true)
|
@@ -324,12 +273,13 @@ module Sass
|
|
324
273
|
|
325
274
|
def _variable(rx)
|
326
275
|
return unless scan(rx)
|
327
|
-
|
276
|
+
|
277
|
+
[:const, @scanner[2]]
|
328
278
|
end
|
329
279
|
|
330
280
|
def ident
|
331
281
|
return unless scan(REGULAR_EXPRESSIONS[:ident])
|
332
|
-
[@scanner[2] ? :funcall : :ident,
|
282
|
+
[@scanner[2] ? :funcall : :ident, @scanner[1]]
|
333
283
|
end
|
334
284
|
|
335
285
|
def string(re, open)
|
@@ -385,9 +335,7 @@ MESSAGE
|
|
385
335
|
|
386
336
|
value = (@scanner[1] ? @scanner[1].to_f : @scanner[2].to_i) * (minus ? -1 : 1)
|
387
337
|
value *= 10**@scanner[3].to_i if @scanner[3]
|
388
|
-
|
389
|
-
units = Sass::Util::normalize_ident_escapes(units) if units
|
390
|
-
script_number = Script::Value::Number.new(value, Array(units))
|
338
|
+
script_number = Script::Value::Number.new(value, Array(@scanner[4]))
|
391
339
|
[:number, script_number]
|
392
340
|
end
|
393
341
|
|
@@ -406,20 +354,15 @@ MESSAGE
|
|
406
354
|
# IDs in properties are used in the Basic User Interface Module
|
407
355
|
# (http://www.w3.org/TR/css3-ui/).
|
408
356
|
return unless scan(REGULAR_EXPRESSIONS[:id])
|
409
|
-
if @scanner[0] =~ /^\#[0-9a-fA-F]+$/ &&
|
410
|
-
(@scanner[0].length == 4 || @scanner[0].length == 5 ||
|
411
|
-
@scanner[0].length == 7 || @scanner[0].length == 9)
|
357
|
+
if @scanner[0] =~ /^\#[0-9a-fA-F]+$/ && (@scanner[0].length == 4 || @scanner[0].length == 7)
|
412
358
|
return [:color, Script::Value::Color.from_hex(@scanner[0])]
|
413
359
|
end
|
414
|
-
[:ident,
|
360
|
+
[:ident, @scanner[0]]
|
415
361
|
end
|
416
362
|
|
417
363
|
def color
|
418
364
|
return unless @scanner.match?(REGULAR_EXPRESSIONS[:color])
|
419
|
-
unless @scanner[0].length == 4 || @scanner[0].length ==
|
420
|
-
@scanner[0].length == 7 || @scanner[0].length == 9
|
421
|
-
return
|
422
|
-
end
|
365
|
+
return unless @scanner[0].length == 4 || @scanner[0].length == 7
|
423
366
|
script_color = Script::Value::Color.from_hex(scan(REGULAR_EXPRESSIONS[:color]))
|
424
367
|
[:color, script_color]
|
425
368
|
end
|
@@ -427,15 +370,6 @@ MESSAGE
|
|
427
370
|
def selector
|
428
371
|
start_pos = source_position
|
429
372
|
return unless scan(REGULAR_EXPRESSIONS[:selector])
|
430
|
-
|
431
|
-
if @scanner.peek(1) == '&'
|
432
|
-
filename = @options[:filename]
|
433
|
-
Sass::Util.sass_warn <<MESSAGE
|
434
|
-
WARNING on line #{line}, column #{offset}#{" of #{filename}" if filename}:
|
435
|
-
In Sass, "&&" means two copies of the parent selector. You probably want to use "and" instead.
|
436
|
-
MESSAGE
|
437
|
-
end
|
438
|
-
|
439
373
|
script_selector = Script::Tree::Selector.new
|
440
374
|
script_selector.source_range = range(start_pos)
|
441
375
|
[:selector, script_selector]
|
@@ -473,7 +407,7 @@ MESSAGE
|
|
473
407
|
end
|
474
408
|
|
475
409
|
def special_val
|
476
|
-
return unless scan(
|
410
|
+
return unless scan(/!important/i)
|
477
411
|
[:string, Script::Value::String.new("!important")]
|
478
412
|
end
|
479
413
|
|
data/lib/sass/script/parser.rb
CHANGED
@@ -7,33 +7,28 @@ module Sass
|
|
7
7
|
class Parser
|
8
8
|
# The line number of the parser's current position.
|
9
9
|
#
|
10
|
-
# @return [
|
10
|
+
# @return [Fixnum]
|
11
11
|
def line
|
12
12
|
@lexer.line
|
13
13
|
end
|
14
14
|
|
15
15
|
# The column number of the parser's current position.
|
16
16
|
#
|
17
|
-
# @return [
|
17
|
+
# @return [Fixnum]
|
18
18
|
def offset
|
19
19
|
@lexer.offset
|
20
20
|
end
|
21
21
|
|
22
22
|
# @param str [String, StringScanner] The source text to parse
|
23
|
-
# @param line [
|
23
|
+
# @param line [Fixnum] The line on which the SassScript appears.
|
24
24
|
# Used for error reporting and sourcemap building
|
25
|
-
# @param offset [
|
25
|
+
# @param offset [Fixnum] The character (not byte) offset where the script starts in the line.
|
26
26
|
# Used for error reporting and sourcemap building
|
27
|
-
# @param options [{Symbol => Object}] An options hash;
|
28
|
-
# {file:SASS_REFERENCE.md#
|
29
|
-
# This supports an additional `:allow_extra_text` option that controls
|
30
|
-
# whether the parser throws an error when extra text is encountered
|
31
|
-
# after the parsed construct.
|
27
|
+
# @param options [{Symbol => Object}] An options hash;
|
28
|
+
# see {file:SASS_REFERENCE.md#options the Sass options documentation}
|
32
29
|
def initialize(str, line, offset, options = {})
|
33
30
|
@options = options
|
34
|
-
@allow_extra_text = options.delete(:allow_extra_text)
|
35
31
|
@lexer = lexer_class.new(str, line, offset, options)
|
36
|
-
@stop_at = nil
|
37
32
|
end
|
38
33
|
|
39
34
|
# Parses a SassScript expression within an interpolated segment (`#{}`).
|
@@ -50,9 +45,7 @@ module Sass
|
|
50
45
|
start_pos = Sass::Source::Position.new(line, offset - 2)
|
51
46
|
expr = assert_expr :expr
|
52
47
|
assert_tok :end_interpolation
|
53
|
-
expr = Sass::Script::Tree::Interpolation.new(
|
54
|
-
nil, expr, nil, false, false, :warn_for_color => warn_for_color)
|
55
|
-
check_for_interpolation expr
|
48
|
+
expr = Sass::Script::Tree::Interpolation.new(expr, warn_for_color)
|
56
49
|
expr.options = @options
|
57
50
|
node(expr, start_pos)
|
58
51
|
rescue Sass::SyntaxError => e
|
@@ -68,7 +61,6 @@ module Sass
|
|
68
61
|
expr = assert_expr :expr
|
69
62
|
assert_done
|
70
63
|
expr.options = @options
|
71
|
-
check_for_interpolation expr
|
72
64
|
expr
|
73
65
|
rescue Sass::SyntaxError => e
|
74
66
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
@@ -78,7 +70,7 @@ module Sass
|
|
78
70
|
# Parses a SassScript expression,
|
79
71
|
# ending it when it encounters one of the given identifier tokens.
|
80
72
|
#
|
81
|
-
# @param tokens [#include?(String
|
73
|
+
# @param tokens [#include?(String)] A set of strings that delimit the expression.
|
82
74
|
# @return [Script::Tree::Node] The root node of the parse tree
|
83
75
|
# @raise [Sass::SyntaxError] if the expression isn't valid SassScript
|
84
76
|
def parse_until(tokens)
|
@@ -86,7 +78,6 @@ module Sass
|
|
86
78
|
expr = assert_expr :expr
|
87
79
|
assert_done
|
88
80
|
expr.options = @options
|
89
|
-
check_for_interpolation expr
|
90
81
|
expr
|
91
82
|
rescue Sass::SyntaxError => e
|
92
83
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
@@ -110,26 +101,10 @@ module Sass
|
|
110
101
|
end
|
111
102
|
assert_done
|
112
103
|
|
113
|
-
args.each
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
keywords.each do |_, v|
|
119
|
-
check_for_interpolation v
|
120
|
-
v.options = @options
|
121
|
-
end
|
122
|
-
|
123
|
-
if splat
|
124
|
-
check_for_interpolation splat
|
125
|
-
splat.options = @options
|
126
|
-
end
|
127
|
-
|
128
|
-
if kwarg_splat
|
129
|
-
check_for_interpolation kwarg_splat
|
130
|
-
kwarg_splat.options = @options
|
131
|
-
end
|
132
|
-
|
104
|
+
args.each {|a| a.options = @options}
|
105
|
+
keywords.each {|k, v| v.options = @options}
|
106
|
+
splat.options = @options if splat
|
107
|
+
kwarg_splat.options = @options if kwarg_splat
|
133
108
|
return args, keywords, splat, kwarg_splat
|
134
109
|
rescue Sass::SyntaxError => e
|
135
110
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
@@ -146,20 +121,10 @@ module Sass
|
|
146
121
|
assert_done
|
147
122
|
|
148
123
|
args.each do |k, v|
|
149
|
-
check_for_interpolation k
|
150
124
|
k.options = @options
|
151
|
-
|
152
|
-
if v
|
153
|
-
check_for_interpolation v
|
154
|
-
v.options = @options
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
if splat
|
159
|
-
check_for_interpolation splat
|
160
|
-
splat.options = @options
|
125
|
+
v.options = @options if v
|
161
126
|
end
|
162
|
-
|
127
|
+
splat.options = @options if splat
|
163
128
|
return args, splat
|
164
129
|
rescue Sass::SyntaxError => e
|
165
130
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
@@ -176,20 +141,10 @@ module Sass
|
|
176
141
|
assert_done
|
177
142
|
|
178
143
|
args.each do |k, v|
|
179
|
-
check_for_interpolation k
|
180
144
|
k.options = @options
|
181
|
-
|
182
|
-
if v
|
183
|
-
check_for_interpolation v
|
184
|
-
v.options = @options
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
if splat
|
189
|
-
check_for_interpolation splat
|
190
|
-
splat.options = @options
|
145
|
+
v.options = @options if v
|
191
146
|
end
|
192
|
-
|
147
|
+
splat.options = @options if splat
|
193
148
|
return args, splat
|
194
149
|
rescue Sass::SyntaxError => e
|
195
150
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
@@ -209,7 +164,6 @@ module Sass
|
|
209
164
|
end
|
210
165
|
|
211
166
|
expr = assert_expr :funcall
|
212
|
-
check_for_interpolation expr
|
213
167
|
expr.options = @options
|
214
168
|
@lexer.unpeek!
|
215
169
|
expr
|
@@ -267,20 +221,8 @@ module Sass
|
|
267
221
|
def production(name, sub, *ops)
|
268
222
|
class_eval <<RUBY, __FILE__, __LINE__ + 1
|
269
223
|
def #{name}
|
270
|
-
interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect})
|
271
|
-
return interp if interp
|
272
224
|
return unless e = #{sub}
|
273
|
-
|
274
|
-
while tok = peek_toks(#{ops.map {|o| o.inspect}.join(', ')})
|
275
|
-
return e if @stop_at && @stop_at.include?(tok.type)
|
276
|
-
@lexer.next
|
277
|
-
|
278
|
-
if interp = try_op_before_interp(tok, e)
|
279
|
-
other_interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}, interp)
|
280
|
-
return interp unless other_interp
|
281
|
-
return other_interp
|
282
|
-
end
|
283
|
-
|
225
|
+
while tok = try_toks(#{ops.map {|o| o.inspect}.join(', ')})
|
284
226
|
e = node(Tree::Operation.new(e, assert_expr(#{sub.inspect}), tok.type),
|
285
227
|
e.source_range.start_pos)
|
286
228
|
end
|
@@ -293,8 +235,6 @@ RUBY
|
|
293
235
|
class_eval <<RUBY, __FILE__, __LINE__ + 1
|
294
236
|
def unary_#{op}
|
295
237
|
return #{sub} unless tok = try_tok(:#{op})
|
296
|
-
interp = try_op_before_interp(tok)
|
297
|
-
return interp if interp
|
298
238
|
start_pos = source_position
|
299
239
|
node(Tree::UnaryOperation.new(assert_expr(:unary_#{op}), :#{op}), start_pos)
|
300
240
|
end
|
@@ -317,7 +257,7 @@ RUBY
|
|
317
257
|
|
318
258
|
def map
|
319
259
|
start_pos = source_position
|
320
|
-
e =
|
260
|
+
e = space
|
321
261
|
return unless e
|
322
262
|
return list e, start_pos unless @lexer.peek && @lexer.peek.type == :colon
|
323
263
|
|
@@ -327,20 +267,19 @@ RUBY
|
|
327
267
|
pair = map_pair
|
328
268
|
return map unless pair
|
329
269
|
map.pairs << pair
|
330
|
-
map.source_range.end_pos = map.pairs.last.last.source_range.end_pos
|
331
270
|
end
|
332
271
|
map
|
333
272
|
end
|
334
273
|
|
335
274
|
def map_pair(key = nil)
|
336
|
-
return unless key ||=
|
275
|
+
return unless key ||= space
|
337
276
|
assert_tok :colon
|
338
|
-
return key, assert_expr(:
|
277
|
+
return key, assert_expr(:space)
|
339
278
|
end
|
340
279
|
|
341
280
|
def expr
|
342
281
|
start_pos = source_position
|
343
|
-
e =
|
282
|
+
e = space
|
344
283
|
return unless e
|
345
284
|
list e, start_pos
|
346
285
|
end
|
@@ -348,133 +287,16 @@ RUBY
|
|
348
287
|
def list(first, start_pos)
|
349
288
|
return first unless @lexer.peek && @lexer.peek.type == :comma
|
350
289
|
|
351
|
-
list = node(Sass::Script::Tree::ListLiteral.new([first],
|
352
|
-
while
|
353
|
-
|
354
|
-
if (interp = try_op_before_interp(tok, element_before_interp))
|
355
|
-
other_interp = try_ops_after_interp([:comma], :expr, interp)
|
356
|
-
return interp unless other_interp
|
357
|
-
return other_interp
|
358
|
-
end
|
359
|
-
return list unless (e = interpolation)
|
290
|
+
list = node(Sass::Script::Tree::ListLiteral.new([first], :comma), start_pos)
|
291
|
+
while try_tok(:comma)
|
292
|
+
return list unless (e = space)
|
360
293
|
list.elements << e
|
361
294
|
list.source_range.end_pos = list.elements.last.source_range.end_pos
|
362
295
|
end
|
363
296
|
list
|
364
297
|
end
|
365
298
|
|
366
|
-
production :equals, :
|
367
|
-
|
368
|
-
def try_op_before_interp(op, prev = nil, after_interp = false)
|
369
|
-
return unless @lexer.peek && @lexer.peek.type == :begin_interpolation
|
370
|
-
unary = !prev && !after_interp
|
371
|
-
wb = @lexer.whitespace?(op)
|
372
|
-
str = literal_node(Script::Value::String.new(Lexer::OPERATORS_REVERSE[op.type]),
|
373
|
-
op.source_range)
|
374
|
-
|
375
|
-
deprecation =
|
376
|
-
case op.type
|
377
|
-
when :comma; :potential
|
378
|
-
when :div, :single_eq; :none
|
379
|
-
when :plus; unary ? :none : :immediate
|
380
|
-
when :minus; @lexer.whitespace?(@lexer.peek) ? :immediate : :none
|
381
|
-
else; :immediate
|
382
|
-
end
|
383
|
-
|
384
|
-
interp = node(
|
385
|
-
Script::Tree::Interpolation.new(
|
386
|
-
prev, str, nil, wb, false, :originally_text => true, :deprecation => deprecation),
|
387
|
-
(prev || str).source_range.start_pos)
|
388
|
-
interpolation(first: interp)
|
389
|
-
end
|
390
|
-
|
391
|
-
def try_ops_after_interp(ops, name, prev = nil)
|
392
|
-
return unless @lexer.after_interpolation?
|
393
|
-
op = peek_toks(*ops)
|
394
|
-
return unless op
|
395
|
-
return if @stop_at && @stop_at.include?(op.type)
|
396
|
-
@lexer.next
|
397
|
-
|
398
|
-
interp = try_op_before_interp(op, prev, :after_interp)
|
399
|
-
return interp if interp
|
400
|
-
|
401
|
-
wa = @lexer.whitespace?
|
402
|
-
str = literal_node(Script::Value::String.new(Lexer::OPERATORS_REVERSE[op.type]),
|
403
|
-
op.source_range)
|
404
|
-
str.line = @lexer.line
|
405
|
-
|
406
|
-
deprecation =
|
407
|
-
case op.type
|
408
|
-
when :comma; :potential
|
409
|
-
when :div, :single_eq; :none
|
410
|
-
when :minus; @lexer.whitespace?(op) ? :immediate : :none
|
411
|
-
else; :immediate
|
412
|
-
end
|
413
|
-
interp = node(
|
414
|
-
Script::Tree::Interpolation.new(
|
415
|
-
prev, str, assert_expr(name), false, wa,
|
416
|
-
:originally_text => true, :deprecation => deprecation),
|
417
|
-
(prev || str).source_range.start_pos)
|
418
|
-
interp
|
419
|
-
end
|
420
|
-
|
421
|
-
def interpolation(first: nil, inner: :space)
|
422
|
-
e = first || send(inner)
|
423
|
-
while (interp = try_tok(:begin_interpolation))
|
424
|
-
wb = @lexer.whitespace?(interp)
|
425
|
-
char_before = @lexer.char(interp.pos - 1)
|
426
|
-
mid = without_stop_at {assert_expr :expr}
|
427
|
-
assert_tok :end_interpolation
|
428
|
-
wa = @lexer.whitespace?
|
429
|
-
char_after = @lexer.char
|
430
|
-
|
431
|
-
after = send(inner)
|
432
|
-
before_deprecation = e.is_a?(Script::Tree::Interpolation) ? e.deprecation : :none
|
433
|
-
after_deprecation = after.is_a?(Script::Tree::Interpolation) ? after.deprecation : :none
|
434
|
-
|
435
|
-
deprecation =
|
436
|
-
if before_deprecation == :immediate || after_deprecation == :immediate ||
|
437
|
-
# Warn for #{foo}$var and #{foo}(1) but not #{$foo}1.
|
438
|
-
(after && !wa && char_after =~ /[$(]/) ||
|
439
|
-
# Warn for $var#{foo} and (a)#{foo} but not a#{foo}.
|
440
|
-
(e && !wb && is_unsafe_before?(e, char_before))
|
441
|
-
:immediate
|
442
|
-
else
|
443
|
-
:potential
|
444
|
-
end
|
445
|
-
|
446
|
-
e = node(
|
447
|
-
Script::Tree::Interpolation.new(e, mid, after, wb, wa, :deprecation => deprecation),
|
448
|
-
(e || interp).source_range.start_pos)
|
449
|
-
end
|
450
|
-
e
|
451
|
-
end
|
452
|
-
|
453
|
-
# Returns whether `expr` is unsafe to include before an interpolation.
|
454
|
-
#
|
455
|
-
# @param expr [Node] The expression to check.
|
456
|
-
# @param char_before [String] The character immediately before the
|
457
|
-
# interpolation being checked (and presumably the last character of
|
458
|
-
# `expr`).
|
459
|
-
# @return [Boolean]
|
460
|
-
def is_unsafe_before?(expr, char_before)
|
461
|
-
return char_before == ')' if is_safe_value?(expr)
|
462
|
-
|
463
|
-
# Otherwise, it's only safe if it was another interpolation.
|
464
|
-
!expr.is_a?(Script::Tree::Interpolation)
|
465
|
-
end
|
466
|
-
|
467
|
-
# Returns whether `expr` is safe as the value immediately before an
|
468
|
-
# interpolation.
|
469
|
-
#
|
470
|
-
# It's safe as long as the previous expression is an identifier or number,
|
471
|
-
# or a list whose last element is also safe.
|
472
|
-
def is_safe_value?(expr)
|
473
|
-
return is_safe_value?(expr.elements.last) if expr.is_a?(Script::Tree::ListLiteral)
|
474
|
-
return false unless expr.is_a?(Script::Tree::Literal)
|
475
|
-
expr.value.is_a?(Script::Value::Number) ||
|
476
|
-
(expr.value.is_a?(Script::Value::String) && expr.value.type == :identifier)
|
477
|
-
end
|
299
|
+
production :equals, :space, :single_eq
|
478
300
|
|
479
301
|
def space
|
480
302
|
start_pos = source_position
|
@@ -487,7 +309,7 @@ RUBY
|
|
487
309
|
if arr.size == 1
|
488
310
|
arr.first
|
489
311
|
else
|
490
|
-
node(Sass::Script::Tree::ListLiteral.new(arr,
|
312
|
+
node(Sass::Script::Tree::ListLiteral.new(arr, :space), start_pos)
|
491
313
|
end
|
492
314
|
end
|
493
315
|
|
@@ -504,136 +326,51 @@ RUBY
|
|
504
326
|
unary :not, :ident
|
505
327
|
|
506
328
|
def ident
|
507
|
-
return
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
elsif
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
elsif name.value == "null"
|
518
|
-
literal_node(Sass::Script::Value::Null.new, name.source_range)
|
329
|
+
return funcall unless (first = @lexer.peek)
|
330
|
+
|
331
|
+
contents = []
|
332
|
+
if first.type == :ident
|
333
|
+
return if @stop_at && @stop_at.include?(first.value)
|
334
|
+
contents << @lexer.next.value
|
335
|
+
elsif first.type == :begin_interpolation
|
336
|
+
@lexer.next # Move through :begin_interpolation
|
337
|
+
contents << assert_expr(:expr)
|
338
|
+
assert_tok(:end_interpolation)
|
519
339
|
else
|
520
|
-
|
340
|
+
return funcall
|
521
341
|
end
|
522
|
-
end
|
523
342
|
|
524
|
-
|
525
|
-
|
526
|
-
next unless tok = try_tok(:funcall)
|
527
|
-
next unless %w[min max].include?(tok.value.downcase)
|
528
|
-
next unless contents = min_max_contents
|
529
|
-
node(array_to_interpolation(["#{tok.value}(", *contents]),
|
530
|
-
tok.source_range.start_pos, source_position)
|
531
|
-
end || funcall
|
532
|
-
end
|
343
|
+
while (tok = @lexer.peek)
|
344
|
+
break if @lexer.whitespace_before?(tok)
|
533
345
|
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
if tok = try_tok(:number)
|
538
|
-
result << tok.value.to_s
|
539
|
-
elsif value = min_max_interpolation
|
540
|
-
result << value
|
541
|
-
elsif value = min_max_calc
|
542
|
-
result << value.value
|
543
|
-
elsif value = min_max_function ||
|
544
|
-
min_max_parens ||
|
545
|
-
nested_min_max
|
546
|
-
result.concat value
|
547
|
-
else
|
548
|
-
return
|
346
|
+
if tok.type == :ident
|
347
|
+
contents << @lexer.next.value
|
348
|
+
next
|
549
349
|
end
|
550
350
|
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
elsif tok = try_tok(:plus) || try_tok(:minus) || try_tok(:times) || try_tok(:div)
|
555
|
-
result << " #{Lexer::OPERATORS_REVERSE[tok.type]} "
|
556
|
-
elsif allow_comma && try_tok(:comma)
|
557
|
-
result << ", "
|
558
|
-
else
|
559
|
-
return
|
560
|
-
end
|
351
|
+
break unless try_tok(:begin_interpolation)
|
352
|
+
contents << assert_expr(:expr)
|
353
|
+
assert_tok(:end_interpolation)
|
561
354
|
end
|
562
|
-
end
|
563
355
|
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
expr = without_stop_at {assert_expr :expr}
|
569
|
-
assert_tok :end_interpolation
|
570
|
-
expr
|
356
|
+
if contents.length > 1 || contents.first.is_a?(Sass::Script::Tree::Node)
|
357
|
+
return node(
|
358
|
+
Sass::Script::Tree::StringInterpolation.new(contents, :identifier),
|
359
|
+
first.source_range.start_pos)
|
571
360
|
end
|
572
|
-
end
|
573
361
|
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
@lexer.next.value
|
587
|
-
end
|
588
|
-
|
589
|
-
def min_max_parens
|
590
|
-
return unless try_tok :lparen
|
591
|
-
return unless contents = min_max_contents(allow_comma: false)
|
592
|
-
['(', *contents]
|
593
|
-
end
|
594
|
-
|
595
|
-
def nested_min_max
|
596
|
-
return unless tok = peek_tok(:funcall)
|
597
|
-
return unless %w[min max].include?(tok.value.downcase)
|
598
|
-
@lexer.next
|
599
|
-
return unless contents = min_max_contents
|
600
|
-
[tok.value, '(', *contents]
|
601
|
-
end
|
602
|
-
|
603
|
-
def declaration_value
|
604
|
-
result = []
|
605
|
-
brackets = []
|
606
|
-
loop do
|
607
|
-
result << @lexer.str do
|
608
|
-
until @lexer.done? ||
|
609
|
-
peek_toks(:begin_interpolation,
|
610
|
-
:end_interpolation,
|
611
|
-
:lcurly,
|
612
|
-
:lparen,
|
613
|
-
:lsquare,
|
614
|
-
:rparen,
|
615
|
-
:rsquare)
|
616
|
-
@lexer.next || @lexer.next_char
|
617
|
-
end
|
618
|
-
end
|
619
|
-
|
620
|
-
if try_tok(:begin_interpolation)
|
621
|
-
result << assert_expr(:expr)
|
622
|
-
assert_tok :end_interpolation
|
623
|
-
elsif tok = try_toks(:lcurly, :lparen, :lsquare)
|
624
|
-
brackets << case tok.type
|
625
|
-
when :lcurly; :end_interpolation
|
626
|
-
when :lparen; :rparen
|
627
|
-
when :lsquare; :rsquare
|
628
|
-
end
|
629
|
-
result << Lexer::OPERATORS_REVERSE[tok.type]
|
630
|
-
elsif brackets.empty?
|
631
|
-
return result
|
632
|
-
else
|
633
|
-
bracket = brackets.pop
|
634
|
-
assert_tok bracket
|
635
|
-
result << Lexer::OPERATORS_REVERSE[bracket]
|
636
|
-
end
|
362
|
+
if (color = Sass::Script::Value::Color::COLOR_NAMES[first.value.downcase])
|
363
|
+
literal_node(Sass::Script::Value::Color.new(color, first.value), first.source_range)
|
364
|
+
elsif first.value == "true"
|
365
|
+
literal_node(Sass::Script::Value::Bool.new(true), first.source_range)
|
366
|
+
elsif first.value == "false"
|
367
|
+
literal_node(Sass::Script::Value::Bool.new(false), first.source_range)
|
368
|
+
elsif first.value == "null"
|
369
|
+
literal_node(Sass::Script::Value::Null.new, first.source_range)
|
370
|
+
else
|
371
|
+
literal_node(
|
372
|
+
Sass::Script::Value::String.new(first.value, :identifier),
|
373
|
+
first.source_range)
|
637
374
|
end
|
638
375
|
end
|
639
376
|
|
@@ -652,31 +389,29 @@ RUBY
|
|
652
389
|
else
|
653
390
|
return [], nil unless try_tok(:lparen)
|
654
391
|
end
|
392
|
+
return [], nil if try_tok(:rparen)
|
655
393
|
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
raise SyntaxError.new(
|
672
|
-
"Required argument #{var.inspect} must come before any optional arguments.")
|
673
|
-
end
|
674
|
-
res << [var, val]
|
675
|
-
break unless try_tok(:comma)
|
394
|
+
res = []
|
395
|
+
splat = nil
|
396
|
+
must_have_default = false
|
397
|
+
loop do
|
398
|
+
c = assert_tok(:const)
|
399
|
+
var = node(Script::Tree::Variable.new(c.value), c.source_range)
|
400
|
+
if try_tok(:colon)
|
401
|
+
val = assert_expr(:space)
|
402
|
+
must_have_default = true
|
403
|
+
elsif try_tok(:splat)
|
404
|
+
splat = var
|
405
|
+
break
|
406
|
+
elsif must_have_default
|
407
|
+
raise SyntaxError.new(
|
408
|
+
"Required argument #{var.inspect} must come before any optional arguments.")
|
676
409
|
end
|
677
|
-
|
678
|
-
|
410
|
+
res << [var, val]
|
411
|
+
break unless try_tok(:comma)
|
679
412
|
end
|
413
|
+
assert_tok(:rparen)
|
414
|
+
return res, splat
|
680
415
|
end
|
681
416
|
|
682
417
|
def fn_arglist
|
@@ -684,41 +419,44 @@ RUBY
|
|
684
419
|
end
|
685
420
|
|
686
421
|
def mixin_arglist
|
687
|
-
arglist(:
|
422
|
+
arglist(:space, "mixin argument")
|
688
423
|
end
|
689
424
|
|
690
425
|
def arglist(subexpr, description)
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
splat = nil
|
695
|
-
while (e = send(subexpr))
|
696
|
-
if @lexer.peek && @lexer.peek.type == :colon
|
697
|
-
name = e
|
698
|
-
@lexer.expected!("comma") unless name.is_a?(Tree::Variable)
|
699
|
-
assert_tok(:colon)
|
700
|
-
value = assert_expr(subexpr, description)
|
701
|
-
|
702
|
-
if keywords[name.name]
|
703
|
-
raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once")
|
704
|
-
end
|
426
|
+
args = []
|
427
|
+
keywords = Sass::Util::NormalizedMap.new
|
428
|
+
e = send(subexpr)
|
705
429
|
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
430
|
+
return [args, keywords] unless e
|
431
|
+
|
432
|
+
splat = nil
|
433
|
+
loop do
|
434
|
+
if @lexer.peek && @lexer.peek.type == :colon
|
435
|
+
name = e
|
436
|
+
@lexer.expected!("comma") unless name.is_a?(Tree::Variable)
|
437
|
+
assert_tok(:colon)
|
438
|
+
value = assert_expr(subexpr, description)
|
439
|
+
|
440
|
+
if keywords[name.name]
|
441
|
+
raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once")
|
442
|
+
end
|
443
|
+
|
444
|
+
keywords[name.name] = value
|
445
|
+
else
|
446
|
+
if try_tok(:splat)
|
447
|
+
return args, keywords, splat, e if splat
|
448
|
+
splat, e = e, nil
|
449
|
+
elsif splat
|
450
|
+
raise SyntaxError.new("Only keyword arguments may follow variable arguments (...).")
|
451
|
+
elsif !keywords.empty?
|
452
|
+
raise SyntaxError.new("Positional arguments must come before keyword arguments.")
|
717
453
|
end
|
718
454
|
|
719
|
-
|
455
|
+
args << e if e
|
720
456
|
end
|
721
|
-
|
457
|
+
|
458
|
+
return args, keywords, splat unless try_tok(:comma)
|
459
|
+
e = assert_expr(subexpr, description)
|
722
460
|
end
|
723
461
|
end
|
724
462
|
|
@@ -730,73 +468,32 @@ RUBY
|
|
730
468
|
|
731
469
|
def special_fun
|
732
470
|
first = try_tok(:special_fun)
|
733
|
-
return
|
734
|
-
str = literal_node(first.value, first.source_range)
|
735
|
-
return str unless try_tok(:string_interpolation)
|
736
|
-
mid = without_stop_at {assert_expr :expr}
|
737
|
-
assert_tok :end_interpolation
|
738
|
-
last = assert_expr(:special_fun)
|
739
|
-
node(
|
740
|
-
Tree::Interpolation.new(str, mid, last, false, false),
|
741
|
-
first.source_range.start_pos)
|
742
|
-
end
|
743
|
-
|
744
|
-
def square_list
|
745
|
-
start_pos = source_position
|
746
|
-
return paren unless try_tok(:lsquare)
|
747
|
-
|
748
|
-
without_stop_at do
|
749
|
-
space_start_pos = source_position
|
750
|
-
e = interpolation(inner: :or_expr)
|
751
|
-
separator = nil
|
752
|
-
if e
|
753
|
-
elements = [e]
|
754
|
-
while (e = interpolation(inner: :or_expr))
|
755
|
-
elements << e
|
756
|
-
end
|
471
|
+
return paren unless first
|
757
472
|
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
e = if elements.length == 1
|
762
|
-
elements.first
|
763
|
-
else
|
764
|
-
node(
|
765
|
-
Sass::Script::Tree::ListLiteral.new(elements, separator: :space),
|
766
|
-
space_start_pos)
|
767
|
-
end
|
768
|
-
elements = [e]
|
769
|
-
|
770
|
-
while (e = space)
|
771
|
-
elements << e
|
772
|
-
break unless try_tok(:comma)
|
773
|
-
end
|
774
|
-
separator = :comma
|
775
|
-
else
|
776
|
-
separator = :space if elements.length > 1
|
777
|
-
end
|
778
|
-
else
|
779
|
-
elements = []
|
780
|
-
end
|
473
|
+
unless try_tok(:string_interpolation)
|
474
|
+
return literal_node(first.value, first.source_range)
|
475
|
+
end
|
781
476
|
|
782
|
-
|
783
|
-
|
477
|
+
contents = [first.value.value]
|
478
|
+
begin
|
479
|
+
contents << assert_expr(:expr)
|
480
|
+
assert_tok :end_interpolation
|
481
|
+
contents << assert_tok(:special_fun).value.value
|
482
|
+
end while try_tok(:string_interpolation)
|
784
483
|
|
785
|
-
|
786
|
-
|
787
|
-
|
484
|
+
node(
|
485
|
+
Tree::StringInterpolation.new(contents, :identifier),
|
486
|
+
first.source_range.start_pos)
|
788
487
|
end
|
789
488
|
|
790
489
|
def paren
|
791
490
|
return variable unless try_tok(:lparen)
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
e || node(Sass::Script::Tree::ListLiteral.new([]), start_pos, end_pos)
|
799
|
-
end
|
491
|
+
start_pos = source_position
|
492
|
+
e = map
|
493
|
+
e.force_division! if e
|
494
|
+
end_pos = source_position
|
495
|
+
assert_tok(:rparen)
|
496
|
+
e || node(Sass::Script::Tree::ListLiteral.new([], nil), start_pos, end_pos)
|
800
497
|
end
|
801
498
|
|
802
499
|
def variable
|
@@ -809,12 +506,21 @@ RUBY
|
|
809
506
|
def string
|
810
507
|
first = try_tok(:string)
|
811
508
|
return number unless first
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
509
|
+
|
510
|
+
unless try_tok(:string_interpolation)
|
511
|
+
return literal_node(first.value, first.source_range)
|
512
|
+
end
|
513
|
+
|
514
|
+
contents = [first.value.value]
|
515
|
+
begin
|
516
|
+
contents << assert_expr(:expr)
|
517
|
+
assert_tok :end_interpolation
|
518
|
+
contents << assert_tok(:string).value.value
|
519
|
+
end while try_tok(:string_interpolation)
|
520
|
+
|
521
|
+
node(
|
522
|
+
Tree::StringInterpolation.new(contents, first.value.type),
|
523
|
+
first.source_range.start_pos)
|
818
524
|
end
|
819
525
|
|
820
526
|
def number
|
@@ -845,8 +551,7 @@ RUBY
|
|
845
551
|
:default => "expression (e.g. 1px, bold)",
|
846
552
|
:mixin_arglist => "mixin argument",
|
847
553
|
:fn_arglist => "function argument",
|
848
|
-
:splat => "..."
|
849
|
-
:special_fun => '")"',
|
554
|
+
:splat => "..."
|
850
555
|
}
|
851
556
|
|
852
557
|
def assert_expr(name, expected = nil)
|
@@ -868,42 +573,20 @@ RUBY
|
|
868
573
|
@lexer.expected!(names.map {|tok| Lexer::TOKEN_NAMES[tok] || tok}.join(" or "))
|
869
574
|
end
|
870
575
|
|
871
|
-
def
|
576
|
+
def try_tok(name)
|
872
577
|
# Avoids an array allocation caused by argument globbing in the try_toks method.
|
873
578
|
peeked = @lexer.peek
|
874
|
-
peeked && name == peeked.type &&
|
875
|
-
end
|
876
|
-
|
877
|
-
def peek_toks(*names)
|
878
|
-
peeked = @lexer.peek
|
879
|
-
peeked && names.include?(peeked.type) && peeked
|
880
|
-
end
|
881
|
-
|
882
|
-
def try_tok(name)
|
883
|
-
peek_tok(name) && @lexer.next
|
579
|
+
peeked && name == peeked.type && @lexer.next
|
884
580
|
end
|
885
581
|
|
886
582
|
def try_toks(*names)
|
887
|
-
|
583
|
+
peeked = @lexer.peek
|
584
|
+
peeked && names.include?(peeked.type) && @lexer.next
|
888
585
|
end
|
889
586
|
|
890
587
|
def assert_done
|
891
|
-
if @
|
892
|
-
|
893
|
-
# StringScanner is pointing to the end of the parsed text.
|
894
|
-
@lexer.unpeek!
|
895
|
-
else
|
896
|
-
return if @lexer.done?
|
897
|
-
@lexer.expected!(EXPR_NAMES[:default])
|
898
|
-
end
|
899
|
-
end
|
900
|
-
|
901
|
-
def without_stop_at
|
902
|
-
old_stop_at = @stop_at
|
903
|
-
@stop_at = nil
|
904
|
-
yield
|
905
|
-
ensure
|
906
|
-
@stop_at = old_stop_at
|
588
|
+
return if @lexer.done?
|
589
|
+
@lexer.expected!(EXPR_NAMES[:default])
|
907
590
|
end
|
908
591
|
|
909
592
|
# @overload node(value, source_range)
|
@@ -937,65 +620,6 @@ RUBY
|
|
937
620
|
node.filename = @options[:filename]
|
938
621
|
node
|
939
622
|
end
|
940
|
-
|
941
|
-
# Converts an array of strings and expressions to a string interoplation
|
942
|
-
# object.
|
943
|
-
#
|
944
|
-
# @param array [Array<Script::Tree:Node | String>]
|
945
|
-
# @return [Script::Tree::StringInterpolation]
|
946
|
-
def array_to_interpolation(array)
|
947
|
-
Sass::Util.merge_adjacent_strings(array).reverse.inject(nil) do |after, value|
|
948
|
-
if value.is_a?(::String)
|
949
|
-
literal = Sass::Script::Tree::Literal.new(
|
950
|
-
Sass::Script::Value::String.new(value))
|
951
|
-
next literal unless after
|
952
|
-
Sass::Script::Tree::StringInterpolation.new(literal, after.mid, after.after)
|
953
|
-
else
|
954
|
-
Sass::Script::Tree::StringInterpolation.new(
|
955
|
-
Sass::Script::Tree::Literal.new(
|
956
|
-
Sass::Script::Value::String.new('')),
|
957
|
-
value,
|
958
|
-
after || Sass::Script::Tree::Literal.new(
|
959
|
-
Sass::Script::Value::String.new('')))
|
960
|
-
end
|
961
|
-
end
|
962
|
-
end
|
963
|
-
|
964
|
-
# Checks a script node for any immediately-deprecated interpolations, and
|
965
|
-
# emits warnings for them.
|
966
|
-
#
|
967
|
-
# @param node [Sass::Script::Tree::Node]
|
968
|
-
def check_for_interpolation(node)
|
969
|
-
nodes = [node]
|
970
|
-
until nodes.empty?
|
971
|
-
node = nodes.pop
|
972
|
-
unless node.is_a?(Sass::Script::Tree::Interpolation) &&
|
973
|
-
node.deprecation == :immediate
|
974
|
-
nodes.concat node.children
|
975
|
-
next
|
976
|
-
end
|
977
|
-
|
978
|
-
interpolation_deprecation(node)
|
979
|
-
end
|
980
|
-
end
|
981
|
-
|
982
|
-
# Emits a deprecation warning for an interpolation node.
|
983
|
-
#
|
984
|
-
# @param node [Sass::Script::Tree::Node]
|
985
|
-
def interpolation_deprecation(interpolation)
|
986
|
-
return if @options[:_convert]
|
987
|
-
location = "on line #{interpolation.line}"
|
988
|
-
location << " of #{interpolation.filename}" if interpolation.filename
|
989
|
-
Sass::Util.sass_warn <<WARNING
|
990
|
-
DEPRECATION WARNING #{location}:
|
991
|
-
\#{} interpolation near operators will be simplified in a future version of Sass.
|
992
|
-
To preserve the current behavior, use quotes:
|
993
|
-
|
994
|
-
#{interpolation.to_quoted_equivalent.to_sass}
|
995
|
-
|
996
|
-
You can use the sass-convert command to automatically fix most cases.
|
997
|
-
WARNING
|
998
|
-
end
|
999
623
|
end
|
1000
624
|
end
|
1001
625
|
end
|