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.
Files changed (257) hide show
  1. checksums.yaml +13 -5
  2. data/.yardopts +1 -1
  3. data/CODE_OF_CONDUCT.md +1 -1
  4. data/CONTRIBUTING.md +1 -146
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +25 -39
  7. data/Rakefile +274 -0
  8. data/VERSION +1 -1
  9. data/VERSION_DATE +1 -1
  10. data/lib/sass.rb +3 -3
  11. data/lib/sass/cache_stores/filesystem.rb +2 -2
  12. data/lib/sass/cache_stores/memory.rb +5 -4
  13. data/lib/sass/callbacks.rb +2 -2
  14. data/lib/sass/css.rb +12 -12
  15. data/lib/sass/engine.rb +44 -62
  16. data/lib/sass/environment.rb +7 -35
  17. data/lib/sass/error.rb +14 -14
  18. data/lib/sass/exec/base.rb +14 -3
  19. data/lib/sass/exec/sass_convert.rb +6 -20
  20. data/lib/sass/exec/sass_scss.rb +29 -5
  21. data/lib/sass/features.rb +2 -3
  22. data/lib/sass/importers/filesystem.rb +6 -11
  23. data/lib/sass/logger.rb +3 -8
  24. data/lib/sass/logger/base.rb +2 -19
  25. data/lib/sass/plugin.rb +2 -3
  26. data/lib/sass/plugin/compiler.rb +67 -48
  27. data/lib/sass/plugin/configuration.rb +3 -3
  28. data/lib/sass/plugin/merb.rb +1 -1
  29. data/lib/sass/plugin/rack.rb +3 -3
  30. data/lib/sass/plugin/staleness_checker.rb +3 -3
  31. data/lib/sass/railtie.rb +1 -1
  32. data/lib/sass/script.rb +3 -3
  33. data/lib/sass/script/css_parser.rb +15 -5
  34. data/lib/sass/script/functions.rb +121 -337
  35. data/lib/sass/script/lexer.rb +36 -102
  36. data/lib/sass/script/parser.rb +153 -529
  37. data/lib/sass/script/tree/funcall.rb +34 -42
  38. data/lib/sass/script/tree/interpolation.rb +26 -171
  39. data/lib/sass/script/tree/list_literal.rb +8 -23
  40. data/lib/sass/script/tree/map_literal.rb +2 -2
  41. data/lib/sass/script/tree/node.rb +3 -3
  42. data/lib/sass/script/tree/operation.rb +16 -43
  43. data/lib/sass/script/tree/string_interpolation.rb +43 -64
  44. data/lib/sass/script/tree/variable.rb +1 -1
  45. data/lib/sass/script/value.rb +0 -2
  46. data/lib/sass/script/value/arg_list.rb +1 -1
  47. data/lib/sass/script/value/base.rb +9 -27
  48. data/lib/sass/script/value/color.rb +18 -26
  49. data/lib/sass/script/value/helpers.rb +18 -44
  50. data/lib/sass/script/value/list.rb +14 -35
  51. data/lib/sass/script/value/map.rb +2 -2
  52. data/lib/sass/script/value/number.rb +16 -26
  53. data/lib/sass/script/value/string.rb +1 -30
  54. data/lib/sass/scss.rb +2 -0
  55. data/lib/sass/scss/css_parser.rb +3 -7
  56. data/lib/sass/scss/parser.rb +78 -196
  57. data/lib/sass/scss/rx.rb +14 -7
  58. data/lib/sass/scss/script_lexer.rb +15 -0
  59. data/lib/sass/scss/script_parser.rb +25 -0
  60. data/lib/sass/scss/static_parser.rb +55 -38
  61. data/lib/sass/selector.rb +10 -7
  62. data/lib/sass/selector/abstract_sequence.rb +12 -15
  63. data/lib/sass/selector/comma_sequence.rb +6 -24
  64. data/lib/sass/selector/pseudo.rb +6 -19
  65. data/lib/sass/selector/sequence.rb +16 -14
  66. data/lib/sass/selector/simple.rb +7 -9
  67. data/lib/sass/selector/simple_sequence.rb +12 -16
  68. data/lib/sass/shared.rb +1 -1
  69. data/lib/sass/source/map.rb +9 -7
  70. data/lib/sass/source/position.rb +4 -4
  71. data/lib/sass/stack.rb +3 -23
  72. data/lib/sass/tree/charset_node.rb +1 -1
  73. data/lib/sass/tree/comment_node.rb +1 -1
  74. data/lib/sass/tree/function_node.rb +3 -2
  75. data/lib/sass/tree/node.rb +3 -5
  76. data/lib/sass/tree/prop_node.rb +58 -49
  77. data/lib/sass/tree/rule_node.rb +8 -15
  78. data/lib/sass/tree/visitors/check_nesting.rb +23 -19
  79. data/lib/sass/tree/visitors/convert.rb +13 -15
  80. data/lib/sass/tree/visitors/cssize.rb +15 -4
  81. data/lib/sass/tree/visitors/deep_copy.rb +2 -2
  82. data/lib/sass/tree/visitors/extend.rb +14 -10
  83. data/lib/sass/tree/visitors/perform.rb +18 -29
  84. data/lib/sass/tree/visitors/set_options.rb +2 -2
  85. data/lib/sass/tree/visitors/to_css.rb +47 -77
  86. data/lib/sass/util.rb +311 -98
  87. data/lib/sass/util/cross_platform_random.rb +19 -0
  88. data/lib/sass/util/multibyte_string_scanner.rb +133 -127
  89. data/lib/sass/util/normalized_map.rb +8 -1
  90. data/lib/sass/util/ordered_hash.rb +192 -0
  91. data/lib/sass/version.rb +6 -2
  92. data/test/sass/cache_test.rb +131 -0
  93. data/test/sass/callbacks_test.rb +61 -0
  94. data/test/sass/compiler_test.rb +236 -0
  95. data/test/sass/conversion_test.rb +2171 -0
  96. data/test/sass/css2sass_test.rb +526 -0
  97. data/test/sass/data/hsl-rgb.txt +319 -0
  98. data/test/sass/encoding_test.rb +219 -0
  99. data/test/sass/engine_test.rb +3400 -0
  100. data/test/sass/exec_test.rb +86 -0
  101. data/test/sass/extend_test.rb +1719 -0
  102. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  103. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  104. data/test/sass/functions_test.rb +1984 -0
  105. data/test/sass/importer_test.rb +421 -0
  106. data/test/sass/logger_test.rb +58 -0
  107. data/test/sass/mock_importer.rb +49 -0
  108. data/test/sass/more_results/more1.css +9 -0
  109. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  110. data/test/sass/more_results/more_import.css +29 -0
  111. data/test/sass/more_templates/_more_partial.sass +2 -0
  112. data/test/sass/more_templates/more1.sass +23 -0
  113. data/test/sass/more_templates/more_import.sass +11 -0
  114. data/test/sass/plugin_test.rb +556 -0
  115. data/test/sass/results/alt.css +4 -0
  116. data/test/sass/results/basic.css +9 -0
  117. data/test/sass/results/cached_import_option.css +3 -0
  118. data/test/sass/results/compact.css +5 -0
  119. data/test/sass/results/complex.css +86 -0
  120. data/test/sass/results/compressed.css +1 -0
  121. data/test/sass/results/expanded.css +19 -0
  122. data/test/sass/results/filename_fn.css +3 -0
  123. data/test/sass/results/if.css +3 -0
  124. data/test/sass/results/import.css +31 -0
  125. data/test/sass/results/import_charset.css +5 -0
  126. data/test/sass/results/import_charset_1_8.css +5 -0
  127. data/test/sass/results/import_charset_ibm866.css +5 -0
  128. data/test/sass/results/import_content.css +1 -0
  129. data/test/sass/results/line_numbers.css +49 -0
  130. data/test/sass/results/mixins.css +95 -0
  131. data/test/sass/results/multiline.css +24 -0
  132. data/test/sass/results/nested.css +22 -0
  133. data/test/sass/results/options.css +1 -0
  134. data/test/sass/results/parent_ref.css +13 -0
  135. data/test/sass/results/script.css +16 -0
  136. data/test/sass/results/scss_import.css +31 -0
  137. data/test/sass/results/scss_importee.css +2 -0
  138. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  139. data/test/sass/results/subdir/subdir.css +3 -0
  140. data/test/sass/results/units.css +11 -0
  141. data/test/sass/results/warn.css +0 -0
  142. data/test/sass/results/warn_imported.css +0 -0
  143. data/test/sass/script_conversion_test.rb +306 -0
  144. data/test/sass/script_test.rb +1206 -0
  145. data/test/sass/scss/css_test.rb +1281 -0
  146. data/test/sass/scss/rx_test.rb +160 -0
  147. data/test/sass/scss/scss_test.rb +4147 -0
  148. data/test/sass/scss/test_helper.rb +37 -0
  149. data/test/sass/source_map_test.rb +1055 -0
  150. data/test/sass/superselector_test.rb +210 -0
  151. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  152. data/test/sass/templates/_double_import_loop2.sass +1 -0
  153. data/test/sass/templates/_filename_fn_import.scss +11 -0
  154. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  155. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  156. data/test/sass/templates/_imported_content.sass +3 -0
  157. data/test/sass/templates/_partial.sass +2 -0
  158. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  159. data/test/sass/templates/alt.sass +16 -0
  160. data/test/sass/templates/basic.sass +23 -0
  161. data/test/sass/templates/bork1.sass +2 -0
  162. data/test/sass/templates/bork2.sass +2 -0
  163. data/test/sass/templates/bork3.sass +2 -0
  164. data/test/sass/templates/bork4.sass +2 -0
  165. data/test/sass/templates/bork5.sass +3 -0
  166. data/test/sass/templates/cached_import_option.scss +3 -0
  167. data/test/sass/templates/compact.sass +17 -0
  168. data/test/sass/templates/complex.sass +305 -0
  169. data/test/sass/templates/compressed.sass +15 -0
  170. data/test/sass/templates/double_import_loop1.sass +1 -0
  171. data/test/sass/templates/expanded.sass +17 -0
  172. data/test/sass/templates/filename_fn.scss +18 -0
  173. data/test/sass/templates/if.sass +11 -0
  174. data/test/sass/templates/import.sass +12 -0
  175. data/test/sass/templates/import_charset.sass +9 -0
  176. data/test/sass/templates/import_charset_1_8.sass +6 -0
  177. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  178. data/test/sass/templates/import_content.sass +4 -0
  179. data/test/sass/templates/importee.less +2 -0
  180. data/test/sass/templates/importee.sass +19 -0
  181. data/test/sass/templates/line_numbers.sass +13 -0
  182. data/test/sass/templates/mixin_bork.sass +5 -0
  183. data/test/sass/templates/mixins.sass +76 -0
  184. data/test/sass/templates/multiline.sass +20 -0
  185. data/test/sass/templates/nested.sass +25 -0
  186. data/test/sass/templates/nested_bork1.sass +2 -0
  187. data/test/sass/templates/nested_bork2.sass +2 -0
  188. data/test/sass/templates/nested_bork3.sass +2 -0
  189. data/test/sass/templates/nested_bork4.sass +2 -0
  190. data/test/sass/templates/nested_import.sass +2 -0
  191. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  192. data/test/sass/templates/options.sass +2 -0
  193. data/test/sass/templates/parent_ref.sass +25 -0
  194. data/test/sass/templates/same_name_different_ext.sass +2 -0
  195. data/test/sass/templates/same_name_different_ext.scss +1 -0
  196. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  197. data/test/sass/templates/script.sass +101 -0
  198. data/test/sass/templates/scss_import.scss +12 -0
  199. data/test/sass/templates/scss_importee.scss +1 -0
  200. data/test/sass/templates/single_import_loop.sass +1 -0
  201. data/test/sass/templates/subdir/import_up1.scss +1 -0
  202. data/test/sass/templates/subdir/import_up2.scss +1 -0
  203. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  204. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  205. data/test/sass/templates/subdir/subdir.sass +6 -0
  206. data/test/sass/templates/units.sass +11 -0
  207. data/test/sass/templates/warn.sass +3 -0
  208. data/test/sass/templates/warn_imported.sass +4 -0
  209. data/test/sass/test_helper.rb +8 -0
  210. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  211. data/test/sass/util/normalized_map_test.rb +51 -0
  212. data/test/sass/util/subset_map_test.rb +91 -0
  213. data/test/sass/util_test.rb +438 -0
  214. data/test/sass/value_helpers_test.rb +179 -0
  215. data/test/test_helper.rb +110 -0
  216. data/vendor/listen/CHANGELOG.md +1 -0
  217. data/vendor/listen/CONTRIBUTING.md +38 -0
  218. data/vendor/listen/Gemfile +20 -0
  219. data/vendor/listen/Guardfile +8 -0
  220. data/vendor/listen/LICENSE +20 -0
  221. data/vendor/listen/README.md +349 -0
  222. data/vendor/listen/Rakefile +5 -0
  223. data/vendor/listen/Vagrantfile +96 -0
  224. data/vendor/listen/lib/listen.rb +54 -0
  225. data/vendor/listen/lib/listen/adapter.rb +327 -0
  226. data/vendor/listen/lib/listen/adapters/bsd.rb +75 -0
  227. data/vendor/listen/lib/listen/adapters/darwin.rb +48 -0
  228. data/vendor/listen/lib/listen/adapters/linux.rb +81 -0
  229. data/vendor/listen/lib/listen/adapters/polling.rb +58 -0
  230. data/vendor/listen/lib/listen/adapters/windows.rb +91 -0
  231. data/vendor/listen/lib/listen/directory_record.rb +406 -0
  232. data/vendor/listen/lib/listen/listener.rb +323 -0
  233. data/vendor/listen/lib/listen/turnstile.rb +32 -0
  234. data/vendor/listen/lib/listen/version.rb +3 -0
  235. data/vendor/listen/listen.gemspec +28 -0
  236. data/vendor/listen/spec/listen/adapter_spec.rb +149 -0
  237. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  238. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  239. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  240. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  241. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  242. data/vendor/listen/spec/listen/directory_record_spec.rb +1250 -0
  243. data/vendor/listen/spec/listen/listener_spec.rb +258 -0
  244. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  245. data/vendor/listen/spec/listen_spec.rb +67 -0
  246. data/vendor/listen/spec/spec_helper.rb +25 -0
  247. data/vendor/listen/spec/support/adapter_helper.rb +666 -0
  248. data/vendor/listen/spec/support/directory_record_helper.rb +57 -0
  249. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  250. data/vendor/listen/spec/support/listeners_helper.rb +179 -0
  251. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  252. metadata +217 -76
  253. data/extra/sass-spec-ref.sh +0 -40
  254. data/lib/sass/deprecation.rb +0 -55
  255. data/lib/sass/logger/delayed.rb +0 -50
  256. data/lib/sass/script/value/callable.rb +0 -25
  257. data/lib/sass/script/value/function.rb +0 -19
@@ -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`: \[`Integer`\]
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 [Integer]
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 [Integer]
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
- :const => "variable (e.g. $foo)",
77
- :ident => "identifier (e.g. middle)")
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, _v| k =~ /^\w+/}
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 [Integer] The 1-based line on which the SassScript appears.
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 [Integer] The 1-based character (not byte) offset in the line in the source.
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#Options the Sass options documentation}
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
- @tok = nil
161
- @next_tok = nil
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
- raise "[BUG] Can't unpeek before a queued token!" if @next_tok
212
- return unless @tok
213
- @scanner.pos = @tok.pos
214
- @line = @tok.source_range.start_pos.line
215
- @offset = @tok.source_range.start_pos.offset
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
- # Runs a block, and rewinds the state of the lexer to the beginning of the
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
- @scanner.pos = old_pos
270
- @line = old_line
271
- @offset = old_offset
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 = @interpolation_stack.pop
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
- [:const, Sass::Util.normalize_ident_escapes(@scanner[2])]
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, Sass::Util.normalize_ident_escapes(@scanner[1])]
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
- units = @scanner[4]
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, Sass::Util.normalize_ident_escapes(@scanner[0])]
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 == 5 ||
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(/!#{W}important/i)
410
+ return unless scan(/!important/i)
477
411
  [:string, Script::Value::String.new("!important")]
478
412
  end
479
413
 
@@ -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 [Integer]
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 [Integer]
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 [Integer] The line on which the SassScript appears.
23
+ # @param line [Fixnum] The line on which the SassScript appears.
24
24
  # Used for error reporting and sourcemap building
25
- # @param offset [Integer] The character (not byte) offset where the script starts in the line.
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; see
28
- # {file:SASS_REFERENCE.md#Options the Sass options documentation}.
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 | Symbol)] A set of strings or symbols that delimit the expression.
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 do |a|
114
- check_for_interpolation a
115
- a.options = @options
116
- end
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 = interpolation
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 ||= interpolation
275
+ return unless key ||= space
337
276
  assert_tok :colon
338
- return key, assert_expr(:interpolation)
277
+ return key, assert_expr(:space)
339
278
  end
340
279
 
341
280
  def expr
342
281
  start_pos = source_position
343
- e = interpolation
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], separator: :comma), start_pos)
352
- while (tok = try_tok(:comma))
353
- element_before_interp = list.elements.length == 1 ? list.elements.first : list
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, :interpolation, :single_eq
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, separator: :space), start_pos)
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 css_min_max unless @lexer.peek && @lexer.peek.type == :ident
508
- return if @stop_at && @stop_at.include?(@lexer.peek.value)
509
-
510
- name = @lexer.next
511
- if (color = Sass::Script::Value::Color::COLOR_NAMES[name.value.downcase])
512
- literal_node(Sass::Script::Value::Color.new(color, name.value), name.source_range)
513
- elsif name.value == "true"
514
- literal_node(Sass::Script::Value::Bool.new(true), name.source_range)
515
- elsif name.value == "false"
516
- literal_node(Sass::Script::Value::Bool.new(false), name.source_range)
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
- literal_node(Sass::Script::Value::String.new(name.value, :identifier), name.source_range)
340
+ return funcall
521
341
  end
522
- end
523
342
 
524
- def css_min_max
525
- @lexer.try do
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
- def min_max_contents(allow_comma: true)
535
- result = []
536
- loop do
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
- if try_tok(:rparen)
552
- result << ")"
553
- return result
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
- def min_max_interpolation
565
- without_stop_at do
566
- tok = try_tok(:begin_interpolation)
567
- return unless tok
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
- def min_max_function
575
- return unless tok = peek_tok(:funcall)
576
- return unless %w[calc env var].include?(tok.value.downcase)
577
- @lexer.next
578
- result = [tok.value, '(', *declaration_value, ')']
579
- assert_tok :rparen
580
- result
581
- end
582
-
583
- def min_max_calc
584
- return unless tok = peek_tok(:special_fun)
585
- return unless tok.value.value.downcase.start_with?("calc(")
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
- without_stop_at do
657
- res = []
658
- splat = nil
659
- must_have_default = false
660
- loop do
661
- break if peek_tok(:rparen)
662
- c = assert_tok(:const)
663
- var = node(Script::Tree::Variable.new(c.value), c.source_range)
664
- if try_tok(:colon)
665
- val = assert_expr(:space)
666
- must_have_default = true
667
- elsif try_tok(:splat)
668
- splat = var
669
- break
670
- elsif must_have_default
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
- assert_tok(:rparen)
678
- return res, splat
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(:interpolation, "mixin argument")
422
+ arglist(:space, "mixin argument")
688
423
  end
689
424
 
690
425
  def arglist(subexpr, description)
691
- without_stop_at do
692
- args = []
693
- keywords = Sass::Util::NormalizedMap.new
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
- keywords[name.name] = value
707
- else
708
- if try_tok(:splat)
709
- return args, keywords, splat, e if splat
710
- splat, e = e, nil
711
- elsif splat
712
- raise SyntaxError.new("Only keyword arguments may follow variable arguments (...).")
713
- elsif !keywords.empty?
714
- raise SyntaxError.new("Positional arguments must come before keyword arguments.")
715
- end
716
- args << e if e
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
- return args, keywords, splat unless try_tok(:comma)
455
+ args << e if e
720
456
  end
721
- return args, keywords
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 square_list unless first
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
- # If there's a comma after a space-separated list, it's actually a
759
- # space-separated list nested in a comma-separated list.
760
- if try_tok(:comma)
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
- assert_tok(:rsquare)
783
- end_pos = source_position
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
- node(Sass::Script::Tree::ListLiteral.new(elements, separator: separator, bracketed: true),
786
- start_pos, end_pos)
787
- end
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
- without_stop_at do
793
- start_pos = source_position
794
- e = map
795
- e.force_division! if e
796
- end_pos = source_position
797
- assert_tok(:rparen)
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
- str = literal_node(first.value, first.source_range)
813
- return str unless try_tok(:string_interpolation)
814
- mid = assert_expr :expr
815
- assert_tok :end_interpolation
816
- last = without_stop_at {assert_expr(:string)}
817
- node(Tree::StringInterpolation.new(str, mid, last), first.source_range.start_pos)
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 peek_tok(name)
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 && peeked
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
- peek_toks(*names) && @lexer.next
583
+ peeked = @lexer.peek
584
+ peeked && names.include?(peeked.type) && @lexer.next
888
585
  end
889
586
 
890
587
  def assert_done
891
- if @allow_extra_text
892
- # If extra text is allowed, just rewind the lexer so that the
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