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
@@ -0,0 +1,14 @@
1
+ # The module containing nodes in the SassScript parse tree. These nodes are
2
+ # all subclasses of {Sass::Script::Tree::Node}.
3
+ module Sass::Script::Tree
4
+ end
5
+
6
+ require 'sass/script/tree/node'
7
+ require 'sass/script/tree/variable'
8
+ require 'sass/script/tree/funcall'
9
+ require 'sass/script/tree/operation'
10
+ require 'sass/script/tree/unary_operation'
11
+ require 'sass/script/tree/interpolation'
12
+ require 'sass/script/tree/string_interpolation'
13
+ require 'sass/script/tree/literal'
14
+ require 'sass/script/tree/list_literal'
@@ -0,0 +1,242 @@
1
+ require 'sass/script/functions'
2
+
3
+ module Sass::Script::Tree
4
+ # A SassScript parse node representing a function call.
5
+ #
6
+ # A function call either calls one of the functions in
7
+ # {Sass::Script::Functions}, or if no function with the given name exists it
8
+ # returns a string representation of the function call.
9
+ class Funcall < Node
10
+ # The name of the function.
11
+ #
12
+ # @return [String]
13
+ attr_reader :name
14
+
15
+ # The arguments to the function.
16
+ #
17
+ # @return [Array<Node>]
18
+ attr_reader :args
19
+
20
+ # The keyword arguments to the function.
21
+ #
22
+ # @return [{String => Node}]
23
+ attr_reader :keywords
24
+
25
+ # The splat argument for this function, if one exists.
26
+ #
27
+ # @return [Node?]
28
+ attr_accessor :splat
29
+
30
+ # @param name [String] See \{#name}
31
+ # @param args [Array<Node>] See \{#args}
32
+ # @param splat [Node] See \{#splat}
33
+ # @param keywords [{String => Node}] See \{#keywords}
34
+ def initialize(name, args, keywords, splat)
35
+ @name = name
36
+ @args = args
37
+ @keywords = keywords
38
+ @splat = splat
39
+ super()
40
+ end
41
+
42
+ # @return [String] A string representation of the function call
43
+ def inspect
44
+ args = @args.map {|a| a.inspect}.join(', ')
45
+ keywords = Sass::Util.hash_to_a(@keywords).
46
+ map {|k, v| "$#{k}: #{v.inspect}"}.join(', ')
47
+ if self.splat
48
+ splat = (args.empty? && keywords.empty?) ? "" : ", "
49
+ splat = "#{splat}#{self.splat.inspect}..."
50
+ end
51
+ "#{name}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
52
+ end
53
+
54
+ # @see Node#to_sass
55
+ def to_sass(opts = {})
56
+ arg_to_sass = lambda do |arg|
57
+ sass = arg.to_sass(opts)
58
+ sass = "(#{sass})" if arg.is_a?(Sass::Script::Tree::ListLiteral) && arg.separator == :comma
59
+ sass
60
+ end
61
+
62
+ args = @args.map(&arg_to_sass).join(', ')
63
+ keywords = Sass::Util.hash_to_a(@keywords).
64
+ map {|k, v| "$#{dasherize(k, opts)}: #{arg_to_sass[v]}"}.join(', ')
65
+ if self.splat
66
+ splat = (args.empty? && keywords.empty?) ? "" : ", "
67
+ splat = "#{splat}#{arg_to_sass[self.splat]}..."
68
+ end
69
+ "#{dasherize(name, opts)}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
70
+ end
71
+
72
+ # Returns the arguments to the function.
73
+ #
74
+ # @return [Array<Node>]
75
+ # @see Node#children
76
+ def children
77
+ res = @args + @keywords.values
78
+ res << @splat if @splat
79
+ res
80
+ end
81
+
82
+ # @see Node#deep_copy
83
+ def deep_copy
84
+ node = dup
85
+ node.instance_variable_set('@args', args.map {|a| a.deep_copy})
86
+ node.instance_variable_set('@keywords', Hash[keywords.map {|k, v| [k, v.deep_copy]}])
87
+ node
88
+ end
89
+
90
+ protected
91
+
92
+ # Evaluates the function call.
93
+ #
94
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
95
+ # @return [Sass::Script::Value] The SassScript object that is the value of the function call
96
+ # @raise [Sass::SyntaxError] if the function call raises an ArgumentError
97
+ def _perform(environment)
98
+ args = @args.map {|a| a.perform(environment)}
99
+ splat = @splat.perform(environment) if @splat
100
+ if fn = environment.function(@name)
101
+ keywords = Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
102
+ return perform_sass_fn(fn, args, keywords, splat)
103
+ end
104
+
105
+ ruby_name = @name.tr('-', '_')
106
+ args = construct_ruby_args(ruby_name, args, splat, environment)
107
+
108
+ unless Sass::Script::Functions.callable?(ruby_name)
109
+ opts(to_literal(args))
110
+ else
111
+ local_environment = Sass::Environment.new(environment.global_env, environment.options)
112
+ opts(Sass::Script::Functions::EvaluationContext.new(local_environment).send(ruby_name, *args))
113
+ end
114
+ rescue ArgumentError => e
115
+ message = e.message
116
+
117
+ # If this is a legitimate Ruby-raised argument error, re-raise it.
118
+ # Otherwise, it's an error in the user's stylesheet, so wrap it.
119
+ if Sass::Util.rbx?
120
+ # Rubinius has a different error report string than vanilla Ruby. It
121
+ # also doesn't put the actual method for which the argument error was
122
+ # thrown in the backtrace, nor does it include `send`, so we look for
123
+ # `_perform`.
124
+ if e.message =~ /^method '([^']+)': given (\d+), expected (\d+)/
125
+ error_name, given, expected = $1, $2, $3
126
+ raise e if error_name != ruby_name || e.backtrace[0] !~ /:in `_perform'$/
127
+ message = "wrong number of arguments (#{given} for #{expected})"
128
+ end
129
+ elsif Sass::Util.jruby?
130
+ if Sass::Util.jruby1_6?
131
+ should_maybe_raise = e.message =~ /^wrong number of arguments \((\d+) for (\d+)\)/ &&
132
+ # The one case where JRuby does include the Ruby name of the function
133
+ # is manually-thrown ArgumentErrors, which are indistinguishable from
134
+ # legitimate ArgumentErrors. We treat both of these as
135
+ # Sass::SyntaxErrors even though it can hide Ruby errors.
136
+ e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
137
+ else
138
+ should_maybe_raise = e.message =~ /^wrong number of arguments calling `[^`]+` \((\d+) for (\d+)\)/
139
+ given, expected = $1, $2
140
+ end
141
+
142
+ if should_maybe_raise
143
+ # JRuby 1.7 includes __send__ before send and _perform.
144
+ trace = e.backtrace.dup
145
+ raise e if !Sass::Util.jruby1_6? && trace.shift !~ /:in `__send__'$/
146
+
147
+ # JRuby (as of 1.7.2) doesn't put the actual method
148
+ # for which the argument error was thrown in the backtrace, so we
149
+ # detect whether our send threw an argument error.
150
+ if !(trace[0] =~ /:in `send'$/ && trace[1] =~ /:in `_perform'$/)
151
+ raise e
152
+ elsif !Sass::Util.jruby1_6?
153
+ # JRuby 1.7 doesn't use standard formatting for its ArgumentErrors.
154
+ message = "wrong number of arguments (#{given} for #{expected})"
155
+ end
156
+ end
157
+ elsif e.message =~ /^wrong number of arguments \(\d+ for \d+\)/ &&
158
+ e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
159
+ raise e
160
+ end
161
+ raise Sass::SyntaxError.new("#{message} for `#{name}'")
162
+ end
163
+
164
+ # Compass historically overrode this before it changed name to {#to\_value}.
165
+ # We should get rid of it in the future.
166
+ def to_literal(args)
167
+ to_value(args)
168
+ end
169
+
170
+ # This method is factored out from `_perform` so that compass can override
171
+ # it with a cross-browser implementation for functions that require vendor prefixes
172
+ # in the generated css.
173
+ def to_value(args)
174
+ Sass::Script::Value::String.new("#{name}(#{args.join(', ')})")
175
+ end
176
+
177
+ private
178
+
179
+ def construct_ruby_args(name, args, splat, environment)
180
+ args += splat.to_a if splat
181
+
182
+ # If variable arguments were passed, there won't be any explicit keywords.
183
+ if splat.is_a?(Sass::Script::Value::ArgList)
184
+ kwargs_size = splat.keywords.size
185
+ splat.keywords_accessed = false
186
+ else
187
+ kwargs_size = @keywords.size
188
+ end
189
+
190
+ unless signature = Sass::Script::Functions.signature(name.to_sym, args.size, kwargs_size)
191
+ return args if @keywords.empty?
192
+ raise Sass::SyntaxError.new("Function #{name} doesn't support keyword arguments")
193
+ end
194
+ keywords = splat.is_a?(Sass::Script::Value::ArgList) ? splat.keywords :
195
+ Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
196
+
197
+ # If the user passes more non-keyword args than the function expects,
198
+ # but it does expect keyword args, Ruby's arg handling won't raise an error.
199
+ # Since we don't want to make functions think about this,
200
+ # we'll handle it for them here.
201
+ if signature.var_kwargs && !signature.var_args && args.size > signature.args.size
202
+ raise Sass::SyntaxError.new(
203
+ "#{args[signature.args.size].inspect} is not a keyword argument for `#{name}'")
204
+ elsif keywords.empty?
205
+ return args
206
+ end
207
+
208
+ args = args + signature.args[args.size..-1].map do |argname|
209
+ if keywords.has_key?(argname)
210
+ keywords.delete(argname)
211
+ else
212
+ raise Sass::SyntaxError.new("Function #{name} requires an argument named $#{argname}")
213
+ end
214
+ end
215
+
216
+ if keywords.size > 0
217
+ if signature.var_kwargs
218
+ args << keywords
219
+ else
220
+ argname = keywords.keys.sort.first
221
+ if signature.args.include?(argname)
222
+ raise Sass::SyntaxError.new("Function #{name} was passed argument $#{argname} both by position and by name")
223
+ else
224
+ raise Sass::SyntaxError.new("Function #{name} doesn't have an argument named $#{argname}")
225
+ end
226
+ end
227
+ end
228
+
229
+ args
230
+ end
231
+
232
+ def perform_sass_fn(function, args, keywords, splat)
233
+ Sass::Tree::Visitors::Perform.perform_arguments(function, args, keywords, splat) do |env|
234
+ val = catch :_sass_return do
235
+ function.tree.each {|c| Sass::Tree::Visitors::Perform.visit(c, env)}
236
+ raise Sass::SyntaxError.new("Function #{@name} finished without @return")
237
+ end
238
+ val
239
+ end
240
+ end
241
+ end
242
+ end
@@ -1,19 +1,36 @@
1
- module Sass::Script
1
+ module Sass::Script::Tree
2
2
  # A SassScript object representing `#{}` interpolation outside a string.
3
3
  #
4
4
  # @see StringInterpolation
5
5
  class Interpolation < Node
6
+ # @return [Node] The SassScript before the interpolation
7
+ attr_reader :before
8
+
9
+ # @return [Node] The SassScript within the interpolation
10
+ attr_reader :mid
11
+
12
+ # @return [Node] The SassScript after the interpolation
13
+ attr_reader :after
14
+
15
+ # @return [Boolean] Whether there was whitespace between `before` and `#{`
16
+ attr_reader :whitespace_before
17
+
18
+ # @return [Boolean] Whether there was whitespace between `}` and `after`
19
+ attr_reader :whitespace_after
20
+
21
+ # @return [Boolean] Whether the original format of the interpolation was
22
+ # plain text, not an interpolation. This is used when converting back to
23
+ # SassScript.
24
+ attr_reader :originally_text
25
+
6
26
  # Interpolation in a property is of the form `before #{mid} after`.
7
27
  #
8
- # @param before [Node] The SassScript before the interpolation
9
- # @param mid [Node] The SassScript within the interpolation
10
- # @param after [Node] The SassScript after the interpolation
11
- # @param wb [Boolean] Whether there was whitespace between `before` and `#{`
12
- # @param wa [Boolean] Whether there was whitespace between `}` and `after`
13
- # @param originally_text [Boolean]
14
- # Whether the original format of the interpolation was plain text,
15
- # not an interpolation.
16
- # This is used when converting back to SassScript.
28
+ # @param before [Node] See {#before}
29
+ # @param mid [Node] See {#mid}
30
+ # @param after [Node] See {#after}
31
+ # @param wb [Boolean] See {#whitespace\_before}
32
+ # @param wa [Boolean] See {#whitespace\_after}
33
+ # @param originally_text [Boolean] See {#originally\_text}
17
34
  def initialize(before, mid, after, wb, wa, originally_text = false)
18
35
  @before = before
19
36
  @mid = mid
@@ -64,16 +81,16 @@ module Sass::Script
64
81
  # Evaluates the interpolation.
65
82
  #
66
83
  # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
67
- # @return [Sass::Script::String] The SassScript string that is the value of the interpolation
84
+ # @return [Sass::Script::Value::String] The SassScript string that is the value of the interpolation
68
85
  def _perform(environment)
69
86
  res = ""
70
87
  res << @before.perform(environment).to_s if @before
71
88
  res << " " if @before && @whitespace_before
72
89
  val = @mid.perform(environment)
73
- res << (val.is_a?(Sass::Script::String) ? val.value : val.to_s)
90
+ res << (val.is_a?(Sass::Script::Value::String) ? val.value : val.to_s)
74
91
  res << " " if @after && @whitespace_after
75
92
  res << @after.perform(environment).to_s if @after
76
- opts(Sass::Script::String.new(res))
93
+ opts(Sass::Script::Value::String.new(res))
77
94
  end
78
95
  end
79
96
  end
@@ -0,0 +1,65 @@
1
+ module Sass::Script::Tree
2
+ # A parse tree node representing a list literal. When resolved, this returns a
3
+ # {Sass::Tree::Value::List}.
4
+ class ListLiteral < Node
5
+ # The parse nodes for members of this list.
6
+ #
7
+ # @return [Array<Node>]
8
+ attr_reader :elements
9
+
10
+ # The operator separating the values of the list. Either `:comma` or
11
+ # `:space`.
12
+ #
13
+ # @return [Symbol]
14
+ attr_reader :separator
15
+
16
+ # Creates a new list literal.
17
+ #
18
+ # @param elements [Array<Node>] See \{#elements}
19
+ # @param separator [Symbol] See \{#separator}
20
+ def initialize(elements, separator)
21
+ @elements = elements
22
+ @separator = separator
23
+ end
24
+
25
+ # @see Node#children
26
+ def children; elements; end
27
+
28
+ # @see Node#to_sass
29
+ def to_sass(opts = {})
30
+ return "()" if elements.empty?
31
+ precedence = Sass::Script::Parser.precedence_of(separator)
32
+ elements.map do |v|
33
+ if v.is_a?(ListLiteral) && Sass::Script::Parser.precedence_of(v.separator) <= precedence
34
+ "(#{v.to_sass(opts)})"
35
+ else
36
+ v.to_sass(opts)
37
+ end
38
+ end.join(separator == :space ? ' ' : ', ')
39
+ end
40
+
41
+ # @see Node#deep_copy
42
+ def deep_copy
43
+ node = dup
44
+ node.instance_variable_set('@elements', elements.map {|e| e.deep_copy})
45
+ node
46
+ end
47
+
48
+ def inspect
49
+ "(#{elements.map {|e| e.inspect}.join(separator == :space ? ' ' : ', ')})"
50
+ end
51
+
52
+ protected
53
+
54
+ def _perform(environment)
55
+ list = Sass::Script::Value::List.new(
56
+ elements.map {|e| e.perform(environment)},
57
+ separator)
58
+ list.line = line
59
+ list.source_range = source_range
60
+ list.filename = filename
61
+ list.options = self.options
62
+ list
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,46 @@
1
+ module Sass::Script::Tree
2
+ # The parse tree node for a literal scalar value. This wraps an instance of
3
+ # {Sass::Script::Value::Base}.
4
+ #
5
+ # List literals should use {ListLiteral} instead.
6
+ class Literal < Node
7
+ # The wrapped value.
8
+ #
9
+ # @return [Sass::Script::Value::Base]
10
+ attr_reader :value
11
+
12
+ # Creates a new literal value.
13
+ #
14
+ # @param [Sass::Script::Value::Base] see {#value}
15
+ def initialize(value)
16
+ @value = value
17
+ end
18
+
19
+ # @see Node#children
20
+ def children; []; end
21
+
22
+ # @see Node#to_sass
23
+ def to_sass(opts = {}); value.to_sass(opts); end
24
+
25
+ # @see Node#deep_copy
26
+ def deep_copy; dup; end
27
+
28
+ # @see Node#options=
29
+ def options=(options)
30
+ value.options = options
31
+ end
32
+
33
+ def inspect
34
+ value.inspect
35
+ end
36
+
37
+ protected
38
+
39
+ def _perform(environment)
40
+ value.line = line
41
+ value.source_range = source_range
42
+ value.filename = filename
43
+ value
44
+ end
45
+ end
46
+ end
@@ -1,4 +1,4 @@
1
- module Sass::Script
1
+ module Sass::Script::Tree
2
2
  # The abstract superclass for SassScript parse tree nodes.
3
3
  #
4
4
  # Use \{#perform} to evaluate a parse tree.
@@ -45,7 +45,7 @@ module Sass::Script
45
45
  # instead, override \{#\_perform}.
46
46
  #
47
47
  # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
48
- # @return [Literal] The SassScript object that is the value of the SassScript
48
+ # @return [Sass::Script::Value] The SassScript object that is the value of the SassScript
49
49
  def perform(environment)
50
50
  _perform(environment)
51
51
  rescue Sass::SyntaxError => e
@@ -87,23 +87,23 @@ module Sass::Script
87
87
  end
88
88
 
89
89
  # Evaluates this node.
90
- # Note that all {Literal} objects created within this method
90
+ # Note that all {Sass::Script::Value} objects created within this method
91
91
  # should have their \{#options} attribute set, probably via \{#opts}.
92
92
  #
93
93
  # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
94
- # @return [Literal] The SassScript object that is the value of the SassScript
94
+ # @return [Sass::Script::Value] The SassScript object that is the value of the SassScript
95
95
  # @see #perform
96
96
  def _perform(environment)
97
97
  Sass::Util.abstract(self)
98
98
  end
99
99
 
100
- # Sets the \{#options} field on the given literal and returns it
100
+ # Sets the \{#options} field on the given value and returns it.
101
101
  #
102
- # @param literal [Literal]
103
- # @return [Literal]
104
- def opts(literal)
105
- literal.options = options
106
- literal
102
+ # @param value [Sass::Script::Value]
103
+ # @return [Sass::Script::Value]
104
+ def opts(value)
105
+ value.options = options
106
+ value
107
107
  end
108
108
  end
109
109
  end