sass 3.3.0.alpha.149 → 3.3.0.alpha.162

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/REVISION +1 -1
  2. data/VERSION +1 -1
  3. data/VERSION_DATE +1 -1
  4. data/lib/sass/css.rb +1 -1
  5. data/lib/sass/engine.rb +4 -4
  6. data/lib/sass/environment.rb +1 -1
  7. data/lib/sass/exec.rb +1 -1
  8. data/lib/sass/media.rb +15 -15
  9. data/lib/sass/script.rb +32 -7
  10. data/lib/sass/script/css_lexer.rb +2 -2
  11. data/lib/sass/script/css_parser.rb +1 -1
  12. data/lib/sass/script/functions.rb +246 -232
  13. data/lib/sass/script/lexer.rb +24 -32
  14. data/lib/sass/script/parser.rb +84 -65
  15. data/lib/sass/script/tree.rb +14 -0
  16. data/lib/sass/script/tree/funcall.rb +242 -0
  17. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +30 -13
  18. data/lib/sass/script/tree/list_literal.rb +65 -0
  19. data/lib/sass/script/tree/literal.rb +46 -0
  20. data/lib/sass/script/{node.rb → tree/node.rb} +10 -10
  21. data/lib/sass/script/{operation.rb → tree/operation.rb} +16 -27
  22. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +4 -4
  23. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +7 -8
  24. data/lib/sass/script/tree/variable.rb +56 -0
  25. data/lib/sass/script/value.rb +10 -0
  26. data/lib/sass/script/{arg_list.rb → value/arg_list.rb} +5 -20
  27. data/lib/sass/script/value/base.rb +222 -0
  28. data/lib/sass/script/{bool.rb → value/bool.rb} +2 -2
  29. data/lib/sass/script/{color.rb → value/color.rb} +22 -20
  30. data/lib/sass/script/{list.rb → value/list.rb} +15 -28
  31. data/lib/sass/script/{null.rb → value/null.rb} +3 -3
  32. data/lib/sass/script/{number.rb → value/number.rb} +19 -19
  33. data/lib/sass/script/{string.rb → value/string.rb} +7 -7
  34. data/lib/sass/scss/parser.rb +14 -4
  35. data/lib/sass/selector.rb +26 -26
  36. data/lib/sass/selector/abstract_sequence.rb +1 -1
  37. data/lib/sass/selector/simple.rb +6 -7
  38. data/lib/sass/source/position.rb +13 -0
  39. data/lib/sass/supports.rb +4 -4
  40. data/lib/sass/tree/comment_node.rb +3 -3
  41. data/lib/sass/tree/css_import_node.rb +7 -7
  42. data/lib/sass/tree/debug_node.rb +2 -2
  43. data/lib/sass/tree/directive_node.rb +2 -2
  44. data/lib/sass/tree/each_node.rb +2 -2
  45. data/lib/sass/tree/extend_node.rb +4 -4
  46. data/lib/sass/tree/for_node.rb +4 -4
  47. data/lib/sass/tree/function_node.rb +4 -4
  48. data/lib/sass/tree/media_node.rb +3 -3
  49. data/lib/sass/tree/mixin_def_node.rb +4 -4
  50. data/lib/sass/tree/mixin_node.rb +6 -6
  51. data/lib/sass/tree/prop_node.rb +23 -15
  52. data/lib/sass/tree/return_node.rb +2 -2
  53. data/lib/sass/tree/rule_node.rb +3 -3
  54. data/lib/sass/tree/variable_node.rb +2 -2
  55. data/lib/sass/tree/visitors/convert.rb +2 -2
  56. data/lib/sass/tree/visitors/deep_copy.rb +5 -5
  57. data/lib/sass/tree/visitors/perform.rb +7 -7
  58. data/lib/sass/tree/visitors/set_options.rb +6 -6
  59. data/lib/sass/tree/visitors/to_css.rb +1 -1
  60. data/lib/sass/tree/warn_node.rb +2 -2
  61. data/lib/sass/tree/while_node.rb +2 -2
  62. data/lib/sass/util.rb +2 -2
  63. data/test/sass/engine_test.rb +6 -6
  64. data/test/sass/functions_test.rb +20 -20
  65. data/test/sass/plugin_test.rb +2 -2
  66. data/test/sass/script_test.rb +38 -29
  67. data/test/test_helper.rb +1 -1
  68. metadata +23 -19
  69. data/lib/sass/script/funcall.rb +0 -238
  70. data/lib/sass/script/literal.rb +0 -221
  71. data/lib/sass/script/variable.rb +0 -58
@@ -16,26 +16,29 @@ module Sass
16
16
  # `value`: \[`Object`\]
17
17
  # : The Ruby object corresponding to the value of the token.
18
18
  #
19
- # `line`: \[`Fixnum`\]
20
- # : The line of the source file on which the token appears (1-based).
21
- #
22
- # `offset`: \[`Fixnum`\]
23
- # : The number of characters into the line the SassScript token appeared (1-based).
19
+ # `source_range`: [`Sass::Source::Range`\]
20
+ # : The range in the source file in which the token appeared.
24
21
  #
25
22
  # `pos`: \[`Fixnum`\]
26
23
  # : The scanner position at which the SassScript token appeared.
27
- Token = Struct.new(:type, :value, :line, :offset, :pos)
24
+ Token = Struct.new(:type, :value, :source_range, :pos)
28
25
 
29
26
  # The line number of the lexer's current position.
30
27
  #
31
28
  # @return [Fixnum]
32
- attr_reader :line
29
+ def line
30
+ return @line unless @tok
31
+ @tok.source_range.start_pos.line
32
+ end
33
33
 
34
34
  # The number of bytes into the current line
35
35
  # of the lexer's current position (1-based).
36
36
  #
37
37
  # @return [Fixnum]
38
- attr_reader :offset
38
+ def offset
39
+ return @offset unless @tok
40
+ @tok.source_range.start_pos.offset
41
+ end
39
42
 
40
43
  # A hash from operator strings to the corresponding token types.
41
44
  OPERATORS = {
@@ -174,8 +177,8 @@ module Sass
174
177
  def unpeek!
175
178
  if @tok
176
179
  @scanner.pos = @tok.pos
177
- @line = @tok.line
178
- @offset = @tok.offset
180
+ @line = @tok.source_range.start_pos.line
181
+ @offset = @tok.source_range.start_pos.offset
179
182
  end
180
183
  end
181
184
 
@@ -219,12 +222,11 @@ module Sass
219
222
 
220
223
  def read_token
221
224
  return if done?
225
+ start_pos = source_position
226
+ start_index = @scanner.pos
222
227
  return unless value = token
223
- type, val, size = value
224
- size ||= @scanner.matched_size
225
-
226
- val.line = @line if val.is_a?(Script::Node)
227
- Token.new(type, val, @line, @offset - size, @scanner.pos - size)
228
+ type, val = value
229
+ Token.new(type, val, range(start_pos), @scanner.pos - @scanner.matched_size)
228
230
  end
229
231
 
230
232
  def whitespace
@@ -259,7 +261,6 @@ module Sass
259
261
  end
260
262
 
261
263
  def string(re, open)
262
- start_pos = source_position
263
264
  return unless scan(STRING_REGULAR_EXPRESSIONS[[re, open]])
264
265
  if @scanner[2] == '#{' #'
265
266
  @scanner.pos -= 2 # Don't actually consume the #{
@@ -268,50 +269,41 @@ module Sass
268
269
  end
269
270
  str =
270
271
  if re == :uri
271
- Script::String.new("#{'url(' unless open}#{@scanner[1]}#{')' unless @scanner[2] == '#{'}")
272
+ Script::Value::String.new("#{'url(' unless open}#{@scanner[1]}#{')' unless @scanner[2] == '#{'}")
272
273
  else
273
- Script::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)
274
+ Script::Value::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)
274
275
  end
275
- str.source_range = range(start_pos)
276
276
  [:string, str]
277
277
  end
278
278
 
279
279
  def number
280
- start_pos = source_position
281
280
  return unless scan(REGULAR_EXPRESSIONS[:number])
282
281
  value = @scanner[2] ? @scanner[2].to_f : @scanner[3].to_i
283
282
  value = -value if @scanner[1]
284
- script_number = Script::Number.new(value, Array(@scanner[4]))
285
- script_number.source_range = range(start_pos)
283
+ script_number = Script::Value::Number.new(value, Array(@scanner[4]))
286
284
  [:number, script_number]
287
285
  end
288
286
 
289
287
  def color
290
- start_pos = source_position
291
288
  return unless s = scan(REGULAR_EXPRESSIONS[:color])
292
289
  raise Sass::SyntaxError.new(<<MESSAGE.rstrip) unless s.size == 4 || s.size == 7
293
290
  Colors must have either three or six digits: '#{s}'
294
291
  MESSAGE
295
292
  value = s.scan(/^#(..?)(..?)(..?)$/).first.
296
293
  map {|num| num.ljust(2, num).to_i(16)}
297
- script_color = Script::Color.new(value)
298
- script_color.source_range = range(start_pos)
294
+ script_color = Script::Value::Color.new(value)
299
295
  [:color, script_color]
300
296
  end
301
297
 
302
298
  def bool
303
- start_pos = source_position
304
299
  return unless s = scan(REGULAR_EXPRESSIONS[:bool])
305
- script_bool = Script::Bool.new(s == 'true')
306
- script_bool.source_range = range(start_pos)
300
+ script_bool = Script::Value::Bool.new(s == 'true')
307
301
  [:bool, script_bool]
308
302
  end
309
303
 
310
304
  def null
311
- start_pos = source_position
312
305
  return unless scan(REGULAR_EXPRESSIONS[:null])
313
- script_null = Script::Null.new
314
- script_null.source_range = range(start_pos)
306
+ script_null = Script::Value::Null.new
315
307
  [:null, script_null]
316
308
  end
317
309
 
@@ -331,7 +323,7 @@ MESSAGE
331
323
 
332
324
  def special_val
333
325
  return unless scan(/!important/i)
334
- [:string, Script::String.new("!important")]
326
+ [:string, Script::Value::String.new("!important")]
335
327
  end
336
328
 
337
329
  def ident_op
@@ -3,7 +3,7 @@ require 'sass/script/lexer'
3
3
  module Sass
4
4
  module Script
5
5
  # The parser for SassScript.
6
- # It parses a string of code into a tree of {Script::Node}s.
6
+ # It parses a string of code into a tree of {Script::Tree::Node}s.
7
7
  class Parser
8
8
  # The line number of the parser's current position.
9
9
  #
@@ -36,14 +36,12 @@ module Sass
36
36
  # which signals the end of an interpolated segment,
37
37
  # it returns rather than throwing an error.
38
38
  #
39
- # @return [Script::Node] The root node of the parse tree
39
+ # @return [Script::Tree::Node] The root node of the parse tree
40
40
  # @raise [Sass::SyntaxError] if the expression isn't valid SassScript
41
41
  def parse_interpolated
42
- start_pos = source_position
43
42
  expr = assert_expr :expr
44
43
  assert_tok :end_interpolation
45
44
  expr.options = @options
46
- expr.source_range = range(start_pos)
47
45
  expr
48
46
  rescue Sass::SyntaxError => e
49
47
  e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
@@ -52,14 +50,12 @@ module Sass
52
50
 
53
51
  # Parses a SassScript expression.
54
52
  #
55
- # @return [Script::Node] The root node of the parse tree
53
+ # @return [Script::Tree::Node] The root node of the parse tree
56
54
  # @raise [Sass::SyntaxError] if the expression isn't valid SassScript
57
55
  def parse
58
- start_pos = source_position
59
56
  expr = assert_expr :expr
60
57
  assert_done
61
58
  expr.options = @options
62
- expr.source_range = range(start_pos)
63
59
  expr
64
60
  rescue Sass::SyntaxError => e
65
61
  e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
@@ -70,7 +66,7 @@ module Sass
70
66
  # ending it when it encounters one of the given identifier tokens.
71
67
  #
72
68
  # @param [#include?(String)] A set of strings that delimit the expression.
73
- # @return [Script::Node] The root node of the parse tree
69
+ # @return [Script::Tree::Node] The root node of the parse tree
74
70
  # @raise [Sass::SyntaxError] if the expression isn't valid SassScript
75
71
  def parse_until(tokens)
76
72
  @stop_at = tokens
@@ -85,7 +81,7 @@ module Sass
85
81
 
86
82
  # Parses the argument list for a mixin include.
87
83
  #
88
- # @return [(Array<Script::Node>, {String => Script::Node}, Script::Node)]
84
+ # @return [(Array<Script::Tree::Node>, {String => Script::Tree::Node}, Script::Tree::Node)]
89
85
  # The root nodes of the positional arguments, keyword arguments, and
90
86
  # splat argument. Keyword arguments are in a hash from names to values.
91
87
  # @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
@@ -108,7 +104,7 @@ module Sass
108
104
 
109
105
  # Parses the argument list for a mixin definition.
110
106
  #
111
- # @return [(Array<Script::Node>, Script::Node)]
107
+ # @return [(Array<Script::Tree::Node>, Script::Tree::Node)]
112
108
  # The root nodes of the arguments, and the splat argument.
113
109
  # @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
114
110
  def parse_mixin_definition_arglist
@@ -128,7 +124,7 @@ module Sass
128
124
 
129
125
  # Parses the argument list for a function definition.
130
126
  #
131
- # @return [(Array<Script::Node>, Script::Node)]
127
+ # @return [(Array<Script::Tree::Node>, Script::Tree::Node)]
132
128
  # The root nodes of the arguments, and the splat argument.
133
129
  # @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
134
130
  def parse_function_definition_arglist
@@ -149,7 +145,7 @@ module Sass
149
145
  # Parse a single string value, possibly containing interpolation.
150
146
  # Doesn't assert that the scanner is finished after parsing.
151
147
  #
152
- # @return [Script::Node] The root node of the parse tree.
148
+ # @return [Script::Tree::Node] The root node of the parse tree.
153
149
  # @raise [Sass::SyntaxError] if the string isn't valid SassScript
154
150
  def parse_string
155
151
  unless (peek = @lexer.peek) &&
@@ -170,7 +166,7 @@ module Sass
170
166
  # Parses a SassScript expression.
171
167
  #
172
168
  # @overload parse(str, line, offset, filename = nil)
173
- # @return [Script::Node] The root node of the parse tree
169
+ # @return [Script::Tree::Node] The root node of the parse tree
174
170
  # @see Parser#initialize
175
171
  # @see Parser#parse
176
172
  def self.parse(*args)
@@ -225,9 +221,7 @@ module Sass
225
221
  end
226
222
 
227
223
  start_pos = source_position
228
- e = Operation.new(e, assert_expr(#{sub.inspect}), tok.type)
229
- e.line = start_pos.line
230
- e.source_range = range(start_pos)
224
+ e = node(Tree::Operation.new(e, assert_expr(#{sub.inspect}), tok.type), start_pos)
231
225
  end
232
226
  e
233
227
  end
@@ -239,10 +233,8 @@ RUBY
239
233
  def unary_#{op}
240
234
  return #{sub} unless tok = try_tok(:#{op})
241
235
  interp = try_op_before_interp(tok) and return interp
242
- line = @lexer.line
243
- op = UnaryOperation.new(assert_expr(:unary_#{op}), :#{op})
244
- op.line = line
245
- op
236
+ start_pos = source_position
237
+ node(Tree::UnaryOperation.new(assert_expr(:unary_#{op}), :#{op}), start_pos)
246
238
  end
247
239
  RUBY
248
240
  end
@@ -254,10 +246,6 @@ RUBY
254
246
  Sass::Source::Position.new(line, offset)
255
247
  end
256
248
 
257
- def token_start_position(token)
258
- Sass::Source::Position.new(token.line, token.offset)
259
- end
260
-
261
249
  def range(start_pos, end_pos=source_position)
262
250
  Sass::Source::Range.new(start_pos, end_pos, @options[:filename], @options[:importer])
263
251
  end
@@ -269,15 +257,15 @@ RUBY
269
257
  interp = try_ops_after_interp([:comma], :expr) and return interp
270
258
  start_pos = source_position
271
259
  return unless e = interpolation
272
- list = node(List.new([e], :comma), start_pos)
260
+ list = node(Sass::Script::Tree::ListLiteral.new([e], :comma), start_pos)
273
261
  while tok = try_tok(:comma)
274
262
  if interp = try_op_before_interp(tok, list)
275
263
  return interp unless other_interp = try_ops_after_interp([:comma], :expr, interp)
276
264
  return other_interp
277
265
  end
278
- list.value << assert_expr(:interpolation)
266
+ list.elements << assert_expr(:interpolation)
279
267
  end
280
- list.value.size == 1 ? list.value.first : list
268
+ list.elements.size == 1 ? list.elements.first : list
281
269
  end
282
270
 
283
271
  production :equals, :interpolation, :single_eq
@@ -285,10 +273,10 @@ RUBY
285
273
  def try_op_before_interp(op, prev = nil)
286
274
  return unless @lexer.peek && @lexer.peek.type == :begin_interpolation
287
275
  wb = @lexer.whitespace?(op)
288
- str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
289
- str.line = @lexer.line
290
- interp = Script::Interpolation.new(prev, str, nil, wb, !:wa, :originally_text)
291
- interp.line = @lexer.line
276
+ str = literal_node(Script::Value::String.new(Lexer::OPERATORS_REVERSE[op.type]), op.source_range)
277
+ interp = node(
278
+ Script::Tree::Interpolation.new(prev, str, nil, wb, !:wa, :originally_text),
279
+ (prev || str).source_range.start_pos)
292
280
  interpolation(interp)
293
281
  end
294
282
 
@@ -298,12 +286,11 @@ RUBY
298
286
  interp = try_op_before_interp(op, prev) and return interp
299
287
 
300
288
  wa = @lexer.whitespace?
301
- str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
289
+ str = literal_node(Script::Value::String.new(Lexer::OPERATORS_REVERSE[op.type]), op.source_range)
302
290
  str.line = @lexer.line
303
- start_pos = source_position
304
- interp = Script::Interpolation.new(prev, str, assert_expr(name), !:wb, wa, :originally_text)
305
- interp.line = @lexer.line
306
- interp.source_range = range(start_pos)
291
+ interp = node(
292
+ Script::Tree::Interpolation.new(prev, str, assert_expr(name), !:wb, wa, :originally_text),
293
+ (prev || str).source_range.start_pos)
307
294
  return interp
308
295
  end
309
296
 
@@ -314,8 +301,9 @@ RUBY
314
301
  line = @lexer.line
315
302
  mid = parse_interpolated
316
303
  wa = @lexer.whitespace?
317
- e = Script::Interpolation.new(e, mid, space, wb, wa)
318
- e.line = line
304
+ e = node(
305
+ Script::Tree::Interpolation.new(e, mid, space, wb, wa),
306
+ (e || mid).source_range.start_pos)
319
307
  end
320
308
  e
321
309
  end
@@ -327,7 +315,7 @@ RUBY
327
315
  while e = or_expr
328
316
  arr << e
329
317
  end
330
- arr.size == 1 ? arr.first : node(List.new(arr, :space), start_pos)
318
+ arr.size == 1 ? arr.first : node(Sass::Script::Tree::ListLiteral.new(arr, :space), start_pos)
331
319
  end
332
320
 
333
321
  production :or_expr, :and_expr, :or
@@ -347,17 +335,18 @@ RUBY
347
335
  return if @stop_at && @stop_at.include?(@lexer.peek.value)
348
336
 
349
337
  name = @lexer.next
350
- if color = Color::COLOR_NAMES[name.value.downcase]
351
- return node(Color.new(color), token_start_position(name), source_position)
338
+ if color = Sass::Script::Value::Color::COLOR_NAMES[name.value.downcase]
339
+ return literal_node(Sass::Script::Value::Color.new(color), name.source_range)
352
340
  end
353
- node(Script::String.new(name.value, :identifier), token_start_position(name), source_position)
341
+ literal_node(Script::Value::String.new(name.value, :identifier), name.source_range)
354
342
  end
355
343
 
356
344
  def funcall
357
345
  return raw unless tok = try_tok(:funcall)
358
346
  args, keywords, splat = fn_arglist || [[], {}]
359
347
  assert_tok(:rparen)
360
- node(Script::Funcall.new(tok.value, args, keywords, splat), token_start_position(tok), source_position)
348
+ node(Script::Tree::Funcall.new(tok.value, args, keywords, splat),
349
+ tok.source_range.start_pos, source_position)
361
350
  end
362
351
 
363
352
  def defn_arglist!(must_have_parens)
@@ -373,8 +362,7 @@ RUBY
373
362
  must_have_default = false
374
363
  loop do
375
364
  c = assert_tok(:const)
376
- var = Script::Variable.new(c.value)
377
- var.source_range = range(c.offset)
365
+ var = node(Script::Tree::Variable.new(c.value), c.source_range)
378
366
  if try_tok(:colon)
379
367
  val = assert_expr(:space)
380
368
  must_have_default = true
@@ -407,7 +395,7 @@ RUBY
407
395
  loop do
408
396
  if @lexer.peek && @lexer.peek.type == :colon
409
397
  name = e
410
- @lexer.expected!("comma") unless name.is_a?(Variable)
398
+ @lexer.expected!("comma") unless name.is_a?(Tree::Variable)
411
399
  assert_tok(:colon)
412
400
  value = assert_expr(subexpr, description)
413
401
 
@@ -431,17 +419,26 @@ RUBY
431
419
  end
432
420
 
433
421
  def raw
422
+ start_pos = source_position
434
423
  return special_fun unless tok = try_tok(:raw)
435
- node(Script::String.new(tok.value))
424
+ literal_node(Script::Value::String.new(tok.value), tok.source_range)
436
425
  end
437
426
 
438
427
  def special_fun
428
+ start_pos = source_position
439
429
  return paren unless tok = try_tok(:special_fun)
440
- first = node(Script::String.new(tok.value.first))
430
+ first = literal_node(Script::Value::String.new(tok.value.first),
431
+ start_pos, start_pos.after(tok.value.first))
441
432
  Sass::Util.enum_slice(tok.value[1..-1], 2).inject(first) do |l, (i, r)|
442
- Script::Interpolation.new(
443
- l, i, r && node(Script::String.new(r)),
444
- false, false)
433
+ end_pos = i.source_range.end_pos
434
+ end_pos = end_pos.after(r) if r
435
+ node(
436
+ Script::Tree::Interpolation.new(
437
+ l, i,
438
+ r && literal_node(Script::Value::String.new(r),
439
+ i.source_range.end_pos, end_pos),
440
+ false, false),
441
+ start_pos, end_pos)
445
442
  end
446
443
  end
447
444
 
@@ -451,8 +448,9 @@ RUBY
451
448
  @in_parens = true
452
449
  start_pos = source_position
453
450
  e = expr
451
+ end_pos = source_position
454
452
  assert_tok(:rparen)
455
- return e || node(List.new([], :space), start_pos)
453
+ return e || node(Sass::Script::Tree::ListLiteral.new([], :space), start_pos, end_pos)
456
454
  ensure
457
455
  @in_parens = was_in_parens
458
456
  end
@@ -460,31 +458,27 @@ RUBY
460
458
  def variable
461
459
  start_pos = source_position
462
460
  return string unless c = try_tok(:const)
463
- node(Variable.new(*c.value), start_pos, source_position)
461
+ node(Tree::Variable.new(*c.value), start_pos)
464
462
  end
465
463
 
466
464
  def string
467
465
  return number unless first = try_tok(:string)
468
- return first.value unless try_tok(:begin_interpolation)
469
- start_pos = source_position
470
- line = @lexer.line
466
+ str = literal_node(first.value, first.source_range)
467
+ return str unless try_tok(:begin_interpolation)
471
468
  mid = parse_interpolated
472
469
  last = assert_expr(:string)
473
- interp = StringInterpolation.new(first.value, mid, last)
474
- interp.line = line
475
- interp.source_range = range(start_pos)
476
- interp
470
+ node(Tree::StringInterpolation.new(str, mid, last), first.source_range.start_pos)
477
471
  end
478
472
 
479
473
  def number
480
474
  return literal unless tok = try_tok(:number)
481
475
  num = tok.value
482
476
  num.original = num.to_s unless @in_parens
483
- num
477
+ literal_node(num, tok.source_range.start_pos)
484
478
  end
485
479
 
486
480
  def literal
487
- (t = try_tok(:color, :bool, :null)) && (return t.value)
481
+ (t = try_tok(:color, :bool, :null)) && (return literal_node(t.value, t.source_range))
488
482
  end
489
483
 
490
484
  # It would be possible to have unified #assert and #try methods,
@@ -517,10 +511,35 @@ RUBY
517
511
  @lexer.expected!(EXPR_NAMES[:default])
518
512
  end
519
513
 
520
- def node(node, start_pos = source_position, end_pos = nil)
521
- node.line = start_pos.line
514
+ # @overload node(value, source_range)
515
+ # @param value [Sass::Script::Value::Base]
516
+ # @param source_range [Sass::Source::Range]
517
+ # @overload node(value, start_pos, end_pos = source_position)
518
+ # @param value [Sass::Script::Value::Base]
519
+ # @param start_pos [Sass::Source::Position]
520
+ # @param end_pos [Sass::Source::Position]
521
+ def literal_node(value, source_range_or_start_pos, end_pos = source_position)
522
+ node(Sass::Script::Tree::Literal.new(value), source_range_or_start_pos, end_pos)
523
+ end
524
+
525
+ # @overload node(node, source_range)
526
+ # @param node [Sass::Script::Tree::Node]
527
+ # @param source_range [Sass::Source::Range]
528
+ # @overload node(node, start_pos, end_pos = source_position)
529
+ # @param node [Sass::Script::Tree::Node]
530
+ # @param start_pos [Sass::Source::Position]
531
+ # @param end_pos [Sass::Source::Position]
532
+ def node(node, source_range_or_start_pos, end_pos = source_position)
533
+ source_range =
534
+ if source_range_or_start_pos.is_a?(Sass::Source::Range)
535
+ source_range_or_start_pos
536
+ else
537
+ range(source_range_or_start_pos, end_pos)
538
+ end
539
+
540
+ node.line = source_range.start_pos.line
541
+ node.source_range = source_range
522
542
  node.filename = @options[:filename]
523
- node.source_range = range(start_pos, end_pos) if end_pos
524
543
  node
525
544
  end
526
545
  end