sass4 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +13 -0
  3. data/AGENTS.md +534 -0
  4. data/CODE_OF_CONDUCT.md +10 -0
  5. data/CONTRIBUTING.md +148 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +242 -0
  8. data/VERSION +1 -0
  9. data/VERSION_NAME +1 -0
  10. data/bin/sass +13 -0
  11. data/bin/sass-convert +12 -0
  12. data/bin/scss +13 -0
  13. data/extra/sass-spec-ref.sh +40 -0
  14. data/extra/update_watch.rb +13 -0
  15. data/init.rb +18 -0
  16. data/lib/sass/cache_stores/base.rb +88 -0
  17. data/lib/sass/cache_stores/chain.rb +34 -0
  18. data/lib/sass/cache_stores/filesystem.rb +60 -0
  19. data/lib/sass/cache_stores/memory.rb +46 -0
  20. data/lib/sass/cache_stores/null.rb +25 -0
  21. data/lib/sass/cache_stores.rb +15 -0
  22. data/lib/sass/callbacks.rb +67 -0
  23. data/lib/sass/css.rb +407 -0
  24. data/lib/sass/deprecation.rb +55 -0
  25. data/lib/sass/engine.rb +1236 -0
  26. data/lib/sass/environment.rb +236 -0
  27. data/lib/sass/error.rb +198 -0
  28. data/lib/sass/exec/base.rb +188 -0
  29. data/lib/sass/exec/sass_convert.rb +283 -0
  30. data/lib/sass/exec/sass_scss.rb +436 -0
  31. data/lib/sass/exec.rb +9 -0
  32. data/lib/sass/features.rb +48 -0
  33. data/lib/sass/importers/base.rb +182 -0
  34. data/lib/sass/importers/deprecated_path.rb +51 -0
  35. data/lib/sass/importers/filesystem.rb +221 -0
  36. data/lib/sass/importers.rb +23 -0
  37. data/lib/sass/logger/base.rb +47 -0
  38. data/lib/sass/logger/delayed.rb +50 -0
  39. data/lib/sass/logger/log_level.rb +45 -0
  40. data/lib/sass/logger.rb +17 -0
  41. data/lib/sass/media.rb +210 -0
  42. data/lib/sass/plugin/compiler.rb +552 -0
  43. data/lib/sass/plugin/configuration.rb +134 -0
  44. data/lib/sass/plugin/generic.rb +15 -0
  45. data/lib/sass/plugin/merb.rb +48 -0
  46. data/lib/sass/plugin/rack.rb +60 -0
  47. data/lib/sass/plugin/rails.rb +47 -0
  48. data/lib/sass/plugin/staleness_checker.rb +199 -0
  49. data/lib/sass/plugin.rb +134 -0
  50. data/lib/sass/railtie.rb +10 -0
  51. data/lib/sass/repl.rb +57 -0
  52. data/lib/sass/root.rb +7 -0
  53. data/lib/sass/script/css_lexer.rb +33 -0
  54. data/lib/sass/script/css_parser.rb +36 -0
  55. data/lib/sass/script/functions.rb +3103 -0
  56. data/lib/sass/script/lexer.rb +518 -0
  57. data/lib/sass/script/parser.rb +1164 -0
  58. data/lib/sass/script/tree/funcall.rb +314 -0
  59. data/lib/sass/script/tree/interpolation.rb +220 -0
  60. data/lib/sass/script/tree/list_literal.rb +119 -0
  61. data/lib/sass/script/tree/literal.rb +49 -0
  62. data/lib/sass/script/tree/map_literal.rb +64 -0
  63. data/lib/sass/script/tree/node.rb +119 -0
  64. data/lib/sass/script/tree/operation.rb +149 -0
  65. data/lib/sass/script/tree/selector.rb +26 -0
  66. data/lib/sass/script/tree/string_interpolation.rb +125 -0
  67. data/lib/sass/script/tree/unary_operation.rb +69 -0
  68. data/lib/sass/script/tree/variable.rb +57 -0
  69. data/lib/sass/script/tree.rb +16 -0
  70. data/lib/sass/script/value/arg_list.rb +36 -0
  71. data/lib/sass/script/value/base.rb +258 -0
  72. data/lib/sass/script/value/bool.rb +35 -0
  73. data/lib/sass/script/value/callable.rb +25 -0
  74. data/lib/sass/script/value/color.rb +704 -0
  75. data/lib/sass/script/value/function.rb +19 -0
  76. data/lib/sass/script/value/helpers.rb +298 -0
  77. data/lib/sass/script/value/list.rb +135 -0
  78. data/lib/sass/script/value/map.rb +70 -0
  79. data/lib/sass/script/value/null.rb +44 -0
  80. data/lib/sass/script/value/number.rb +564 -0
  81. data/lib/sass/script/value/string.rb +138 -0
  82. data/lib/sass/script/value.rb +13 -0
  83. data/lib/sass/script.rb +66 -0
  84. data/lib/sass/scss/css_parser.rb +61 -0
  85. data/lib/sass/scss/parser.rb +1343 -0
  86. data/lib/sass/scss/rx.rb +134 -0
  87. data/lib/sass/scss/static_parser.rb +351 -0
  88. data/lib/sass/scss.rb +14 -0
  89. data/lib/sass/selector/abstract_sequence.rb +112 -0
  90. data/lib/sass/selector/comma_sequence.rb +195 -0
  91. data/lib/sass/selector/pseudo.rb +291 -0
  92. data/lib/sass/selector/sequence.rb +661 -0
  93. data/lib/sass/selector/simple.rb +124 -0
  94. data/lib/sass/selector/simple_sequence.rb +348 -0
  95. data/lib/sass/selector.rb +327 -0
  96. data/lib/sass/shared.rb +76 -0
  97. data/lib/sass/source/map.rb +209 -0
  98. data/lib/sass/source/position.rb +39 -0
  99. data/lib/sass/source/range.rb +41 -0
  100. data/lib/sass/stack.rb +140 -0
  101. data/lib/sass/supports.rb +225 -0
  102. data/lib/sass/tree/at_root_node.rb +83 -0
  103. data/lib/sass/tree/charset_node.rb +22 -0
  104. data/lib/sass/tree/comment_node.rb +82 -0
  105. data/lib/sass/tree/content_node.rb +9 -0
  106. data/lib/sass/tree/css_import_node.rb +68 -0
  107. data/lib/sass/tree/debug_node.rb +18 -0
  108. data/lib/sass/tree/directive_node.rb +59 -0
  109. data/lib/sass/tree/each_node.rb +24 -0
  110. data/lib/sass/tree/error_node.rb +18 -0
  111. data/lib/sass/tree/extend_node.rb +43 -0
  112. data/lib/sass/tree/for_node.rb +36 -0
  113. data/lib/sass/tree/function_node.rb +44 -0
  114. data/lib/sass/tree/if_node.rb +52 -0
  115. data/lib/sass/tree/import_node.rb +75 -0
  116. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  117. data/lib/sass/tree/media_node.rb +48 -0
  118. data/lib/sass/tree/mixin_def_node.rb +38 -0
  119. data/lib/sass/tree/mixin_node.rb +52 -0
  120. data/lib/sass/tree/node.rb +240 -0
  121. data/lib/sass/tree/prop_node.rb +162 -0
  122. data/lib/sass/tree/return_node.rb +19 -0
  123. data/lib/sass/tree/root_node.rb +44 -0
  124. data/lib/sass/tree/rule_node.rb +153 -0
  125. data/lib/sass/tree/supports_node.rb +38 -0
  126. data/lib/sass/tree/trace_node.rb +33 -0
  127. data/lib/sass/tree/variable_node.rb +36 -0
  128. data/lib/sass/tree/visitors/base.rb +72 -0
  129. data/lib/sass/tree/visitors/check_nesting.rb +173 -0
  130. data/lib/sass/tree/visitors/convert.rb +350 -0
  131. data/lib/sass/tree/visitors/cssize.rb +362 -0
  132. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  133. data/lib/sass/tree/visitors/extend.rb +64 -0
  134. data/lib/sass/tree/visitors/perform.rb +572 -0
  135. data/lib/sass/tree/visitors/set_options.rb +139 -0
  136. data/lib/sass/tree/visitors/to_css.rb +440 -0
  137. data/lib/sass/tree/warn_node.rb +18 -0
  138. data/lib/sass/tree/while_node.rb +18 -0
  139. data/lib/sass/util/multibyte_string_scanner.rb +151 -0
  140. data/lib/sass/util/normalized_map.rb +122 -0
  141. data/lib/sass/util/subset_map.rb +109 -0
  142. data/lib/sass/util/test.rb +9 -0
  143. data/lib/sass/util.rb +1137 -0
  144. data/lib/sass/version.rb +120 -0
  145. data/lib/sass.rb +102 -0
  146. data/rails/init.rb +1 -0
  147. metadata +283 -0
@@ -0,0 +1,64 @@
1
+ module Sass::Script::Tree
2
+ # A class representing a map literal. When resolved, this returns a
3
+ # {Sass::Script::Node::Map}.
4
+ class MapLiteral < Node
5
+ # The key/value pairs that make up this map node. This isn't a Hash so that
6
+ # we can detect key collisions once all the keys have been performed.
7
+ #
8
+ # @return [Array<(Node, Node)>]
9
+ attr_reader :pairs
10
+
11
+ # Creates a new map literal.
12
+ #
13
+ # @param pairs [Array<(Node, Node)>] See \{#pairs}
14
+ def initialize(pairs)
15
+ @pairs = pairs
16
+ end
17
+
18
+ # @see Node#children
19
+ def children
20
+ @pairs.flatten
21
+ end
22
+
23
+ # @see Node#to_sass
24
+ def to_sass(opts = {})
25
+ return "()" if pairs.empty?
26
+
27
+ to_sass = lambda do |value|
28
+ if value.is_a?(ListLiteral) && value.separator == :comma
29
+ "(#{value.to_sass(opts)})"
30
+ else
31
+ value.to_sass(opts)
32
+ end
33
+ end
34
+
35
+ "(" + pairs.map {|(k, v)| "#{to_sass[k]}: #{to_sass[v]}"}.join(', ') + ")"
36
+ end
37
+ alias_method :inspect, :to_sass
38
+
39
+ # @see Node#deep_copy
40
+ def deep_copy
41
+ node = dup
42
+ node.instance_variable_set('@pairs',
43
+ pairs.map {|(k, v)| [k.deep_copy, v.deep_copy]})
44
+ node
45
+ end
46
+
47
+ protected
48
+
49
+ # @see Node#_perform
50
+ def _perform(environment)
51
+ keys = Set.new
52
+ map = Sass::Script::Value::Map.new(Hash[pairs.map do |(k, v)|
53
+ k, v = k.perform(environment), v.perform(environment)
54
+ if keys.include?(k)
55
+ raise Sass::SyntaxError.new("Duplicate key #{k.inspect} in map #{to_sass}.")
56
+ end
57
+ keys << k
58
+ [k, v]
59
+ end])
60
+ map.options = options
61
+ map
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,119 @@
1
+ module Sass::Script::Tree
2
+ # The abstract superclass for SassScript parse tree nodes.
3
+ #
4
+ # Use \{#perform} to evaluate a parse tree.
5
+ class Node
6
+ # The options hash for this node.
7
+ #
8
+ # @return [{Symbol => Object}]
9
+ attr_reader :options
10
+
11
+ # The line of the document on which this node appeared.
12
+ #
13
+ # @return [Integer]
14
+ attr_accessor :line
15
+
16
+ # The source range in the document on which this node appeared.
17
+ #
18
+ # @return [Sass::Source::Range]
19
+ attr_accessor :source_range
20
+
21
+ # The file name of the document on which this node appeared.
22
+ #
23
+ # @return [String]
24
+ attr_accessor :filename
25
+
26
+ # Sets the options hash for this node,
27
+ # as well as for all child nodes.
28
+ # See {file:SASS_REFERENCE.md#Options the Sass options documentation}.
29
+ #
30
+ # @param options [{Symbol => Object}] The options
31
+ def options=(options)
32
+ @options = options
33
+ children.each do |c|
34
+ if c.is_a? Hash
35
+ c.values.each {|v| v.options = options}
36
+ else
37
+ c.options = options
38
+ end
39
+ end
40
+ end
41
+
42
+ # Evaluates the node.
43
+ #
44
+ # \{#perform} shouldn't be overridden directly;
45
+ # instead, override \{#\_perform}.
46
+ #
47
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
48
+ # @return [Sass::Script::Value] The SassScript object that is the value of the SassScript
49
+ def perform(environment)
50
+ _perform(environment)
51
+ rescue Sass::SyntaxError => e
52
+ e.modify_backtrace(:line => line)
53
+ raise e
54
+ end
55
+
56
+ # Returns all child nodes of this node.
57
+ #
58
+ # @return [Array<Node>]
59
+ def children
60
+ Sass::Util.abstract(self)
61
+ end
62
+
63
+ # Returns the text of this SassScript expression.
64
+ #
65
+ # @options opts :quote [String]
66
+ # The preferred quote style for quoted strings. If `:none`, strings are
67
+ # always emitted unquoted.
68
+ #
69
+ # @return [String]
70
+ def to_sass(opts = {})
71
+ Sass::Util.abstract(self)
72
+ end
73
+
74
+ # Returns a deep clone of this node.
75
+ # The child nodes are cloned, but options are not.
76
+ #
77
+ # @return [Node]
78
+ def deep_copy
79
+ Sass::Util.abstract(self)
80
+ end
81
+
82
+ # Forces any division operations with number literals in this expression to
83
+ # do real division, rather than returning strings.
84
+ def force_division!
85
+ children.each {|c| c.force_division!}
86
+ end
87
+
88
+ protected
89
+
90
+ # Converts underscores to dashes if the :dasherize option is set.
91
+ def dasherize(s, opts)
92
+ if opts[:dasherize]
93
+ s.tr('_', '-')
94
+ else
95
+ s
96
+ end
97
+ end
98
+
99
+ # Evaluates this node.
100
+ # Note that all {Sass::Script::Value} objects created within this method
101
+ # should have their \{#options} attribute set, probably via \{#opts}.
102
+ #
103
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
104
+ # @return [Sass::Script::Value] The SassScript object that is the value of the SassScript
105
+ # @see #perform
106
+ def _perform(environment)
107
+ Sass::Util.abstract(self)
108
+ end
109
+
110
+ # Sets the \{#options} field on the given value and returns it.
111
+ #
112
+ # @param value [Sass::Script::Value]
113
+ # @return [Sass::Script::Value]
114
+ def opts(value)
115
+ value.options = options
116
+ value
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,149 @@
1
+ module Sass::Script::Tree
2
+ # A SassScript parse node representing a binary operation,
3
+ # such as `$a + $b` or `"foo" + 1`.
4
+ class Operation < Node
5
+ @@color_arithmetic_deprecation = Sass::Deprecation.new
6
+ @@unitless_equals_deprecation = Sass::Deprecation.new
7
+
8
+ attr_reader :operand1
9
+ attr_reader :operand2
10
+ attr_reader :operator
11
+
12
+ # @param operand1 [Sass::Script::Tree::Node] The parse-tree node
13
+ # for the right-hand side of the operator
14
+ # @param operand2 [Sass::Script::Tree::Node] The parse-tree node
15
+ # for the left-hand side of the operator
16
+ # @param operator [Symbol] The operator to perform.
17
+ # This should be one of the binary operator names in {Sass::Script::Lexer::OPERATORS}
18
+ def initialize(operand1, operand2, operator)
19
+ @operand1 = operand1
20
+ @operand2 = operand2
21
+ @operator = operator
22
+ super()
23
+ end
24
+
25
+ # @return [String] A human-readable s-expression representation of the operation
26
+ def inspect
27
+ "(#{@operator.inspect} #{@operand1.inspect} #{@operand2.inspect})"
28
+ end
29
+
30
+ # @see Node#to_sass
31
+ def to_sass(opts = {})
32
+ o1 = operand_to_sass @operand1, :left, opts
33
+ o2 = operand_to_sass @operand2, :right, opts
34
+ sep =
35
+ case @operator
36
+ when :comma; ", "
37
+ when :space; " "
38
+ else; " #{Sass::Script::Lexer::OPERATORS_REVERSE[@operator]} "
39
+ end
40
+ "#{o1}#{sep}#{o2}"
41
+ end
42
+
43
+ # Returns the operands for this operation.
44
+ #
45
+ # @return [Array<Node>]
46
+ # @see Node#children
47
+ def children
48
+ [@operand1, @operand2]
49
+ end
50
+
51
+ # @see Node#deep_copy
52
+ def deep_copy
53
+ node = dup
54
+ node.instance_variable_set('@operand1', @operand1.deep_copy)
55
+ node.instance_variable_set('@operand2', @operand2.deep_copy)
56
+ node
57
+ end
58
+
59
+ protected
60
+
61
+ # Evaluates the operation.
62
+ #
63
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
64
+ # @return [Sass::Script::Value] The SassScript object that is the value of the operation
65
+ # @raise [Sass::SyntaxError] if the operation is undefined for the operands
66
+ def _perform(environment)
67
+ value1 = @operand1.perform(environment)
68
+
69
+ # Special-case :and and :or to support short-circuiting.
70
+ if @operator == :and
71
+ return value1.to_bool ? @operand2.perform(environment) : value1
72
+ elsif @operator == :or
73
+ return value1.to_bool ? value1 : @operand2.perform(environment)
74
+ end
75
+
76
+ value2 = @operand2.perform(environment)
77
+
78
+ if (value1.is_a?(Sass::Script::Value::Null) || value2.is_a?(Sass::Script::Value::Null)) &&
79
+ @operator != :eq && @operator != :neq
80
+ raise Sass::SyntaxError.new(
81
+ "Invalid null operation: \"#{value1.inspect} #{@operator} #{value2.inspect}\".")
82
+ end
83
+
84
+ begin
85
+ result = opts(value1.send(@operator, value2))
86
+ rescue NoMethodError => e
87
+ raise e unless e.name.to_s == @operator.to_s
88
+ raise Sass::SyntaxError.new("Undefined operation: \"#{value1} #{@operator} #{value2}\".")
89
+ end
90
+
91
+ warn_for_color_arithmetic(value1, value2)
92
+ warn_for_unitless_equals(value1, value2, result)
93
+
94
+ result
95
+ end
96
+
97
+ private
98
+
99
+ def warn_for_color_arithmetic(value1, value2)
100
+ return unless @operator == :plus || @operator == :times || @operator == :minus ||
101
+ @operator == :div || @operator == :mod
102
+
103
+ if value1.is_a?(Sass::Script::Value::Number)
104
+ return unless value2.is_a?(Sass::Script::Value::Color)
105
+ elsif value1.is_a?(Sass::Script::Value::Color)
106
+ return unless value2.is_a?(Sass::Script::Value::Color) || value2.is_a?(Sass::Script::Value::Number)
107
+ else
108
+ return
109
+ end
110
+
111
+ @@color_arithmetic_deprecation.warn(filename, line, <<WARNING)
112
+ The operation `#{value1} #{@operator} #{value2}` is deprecated and will be an error in future versions.
113
+ Consider using Sass's color functions instead.
114
+ https://sass-lang.com/documentation/Sass/Script/Functions.html#other_color_functions
115
+ WARNING
116
+ end
117
+
118
+ def warn_for_unitless_equals(value1, value2, result)
119
+ return unless @operator == :eq || @operator == :neq
120
+ return unless value1.is_a?(Sass::Script::Value::Number)
121
+ return unless value2.is_a?(Sass::Script::Value::Number)
122
+ return unless value1.unitless? != value2.unitless?
123
+ return unless result == (if @operator == :eq
124
+ Sass::Script::Value::Bool::TRUE
125
+ else
126
+ Sass::Script::Value::Bool::FALSE
127
+ end)
128
+
129
+ operation = "#{value1.to_sass} #{@operator == :eq ? '==' : '!='} #{value2.to_sass}"
130
+ future_value = @operator == :neq
131
+ @@unitless_equals_deprecation.warn(filename, line, <<WARNING)
132
+ The result of `#{operation}` will be `#{future_value}` in future releases of Sass.
133
+ Unitless numbers will no longer be equal to the same numbers with units.
134
+ WARNING
135
+ end
136
+
137
+ def operand_to_sass(op, side, opts)
138
+ return "(#{op.to_sass(opts)})" if op.is_a?(Sass::Script::Tree::ListLiteral)
139
+ return op.to_sass(opts) unless op.is_a?(Operation)
140
+
141
+ pred = Sass::Script::Parser.precedence_of(@operator)
142
+ sub_pred = Sass::Script::Parser.precedence_of(op.operator)
143
+ assoc = Sass::Script::Parser.associative?(@operator)
144
+ return "(#{op.to_sass(opts)})" if sub_pred < pred ||
145
+ (side == :right && sub_pred == pred && !assoc)
146
+ op.to_sass(opts)
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,26 @@
1
+ module Sass::Script::Tree
2
+ # A SassScript node that will resolve to the current selector.
3
+ class Selector < Node
4
+ def initialize; end
5
+
6
+ def children
7
+ []
8
+ end
9
+
10
+ def to_sass(opts = {})
11
+ '&'
12
+ end
13
+
14
+ def deep_copy
15
+ dup
16
+ end
17
+
18
+ protected
19
+
20
+ def _perform(environment)
21
+ selector = environment.selector
22
+ return opts(Sass::Script::Value::Null.new) unless selector
23
+ opts(selector.to_sass_script)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,125 @@
1
+ module Sass::Script::Tree
2
+ # A SassScript object representing `#{}` interpolation within a string.
3
+ #
4
+ # @see Interpolation
5
+ class StringInterpolation < Node
6
+ # @return [Literal] The string literal before this interpolation.
7
+ attr_reader :before
8
+
9
+ # @return [Node] The SassScript within the interpolation
10
+ attr_reader :mid
11
+
12
+ # @return [StringInterpolation, Literal]
13
+ # The string literal or string interpolation before this interpolation.
14
+ attr_reader :after
15
+
16
+ # Whether this is a CSS string or a CSS identifier. The difference is that
17
+ # strings are written with double-quotes, while identifiers aren't.
18
+ #
19
+ # String interpolations are only ever identifiers if they're quote-like
20
+ # functions such as `url()`.
21
+ #
22
+ # @return [Symbol] `:string` or `:identifier`
23
+ def type
24
+ @before.value.type
25
+ end
26
+
27
+ # Returns the quote character that should be used to wrap a Sass
28
+ # representation of this interpolation.
29
+ def quote
30
+ quote_for(self) || '"'
31
+ end
32
+
33
+ # Interpolation in a string is of the form `"before #{mid} after"`,
34
+ # where `before` and `after` may include more interpolation.
35
+ #
36
+ # @param before [StringInterpolation, Literal] See {StringInterpolation#before}
37
+ # @param mid [Node] See {StringInterpolation#mid}
38
+ # @param after [Literal] See {StringInterpolation#after}
39
+ def initialize(before, mid, after)
40
+ @before = before
41
+ @mid = mid
42
+ @after = after
43
+ end
44
+
45
+ # @return [String] A human-readable s-expression representation of the interpolation
46
+ def inspect
47
+ "(string_interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
48
+ end
49
+
50
+ # @see Node#to_sass
51
+ def to_sass(opts = {})
52
+ quote = type == :string ? opts[:quote] || quote_for(self) || '"' : :none
53
+ opts = opts.merge(:quote => quote)
54
+
55
+ res = ""
56
+ res << quote if quote != :none
57
+ res << _to_sass(before, opts)
58
+ res << '#{' << @mid.to_sass(opts.merge(:quote => nil)) << '}'
59
+ res << _to_sass(after, opts)
60
+ res << quote if quote != :none
61
+ res
62
+ end
63
+
64
+ # Returns the three components of the interpolation, `before`, `mid`, and `after`.
65
+ #
66
+ # @return [Array<Node>]
67
+ # @see #initialize
68
+ # @see Node#children
69
+ def children
70
+ [@before, @mid, @after].compact
71
+ end
72
+
73
+ # @see Node#deep_copy
74
+ def deep_copy
75
+ node = dup
76
+ node.instance_variable_set('@before', @before.deep_copy) if @before
77
+ node.instance_variable_set('@mid', @mid.deep_copy)
78
+ node.instance_variable_set('@after', @after.deep_copy) if @after
79
+ node
80
+ end
81
+
82
+ protected
83
+
84
+ # Evaluates the interpolation.
85
+ #
86
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
87
+ # @return [Sass::Script::Value::String]
88
+ # The SassScript string that is the value of the interpolation
89
+ def _perform(environment)
90
+ res = ""
91
+ before = @before.perform(environment)
92
+ res << before.value
93
+ mid = @mid.perform(environment)
94
+ res << (mid.is_a?(Sass::Script::Value::String) ? mid.value : mid.to_s(:quote => :none))
95
+ res << @after.perform(environment).value
96
+ opts(Sass::Script::Value::String.new(res, before.type))
97
+ end
98
+
99
+ private
100
+
101
+ def _to_sass(string_or_interp, opts)
102
+ result = string_or_interp.to_sass(opts)
103
+ opts[:quote] == :none ? result : result.slice(1...-1)
104
+ end
105
+
106
+ def quote_for(string_or_interp)
107
+ if string_or_interp.is_a?(Sass::Script::Tree::Literal)
108
+ return nil if string_or_interp.value.value.empty?
109
+ return '"' if string_or_interp.value.value.include?("'")
110
+ return "'" if string_or_interp.value.value.include?('"')
111
+ return nil
112
+ end
113
+
114
+ # Double-quotes take precedence over single quotes.
115
+ before_quote = quote_for(string_or_interp.before)
116
+ return '"' if before_quote == '"'
117
+ after_quote = quote_for(string_or_interp.after)
118
+ return '"' if after_quote == '"'
119
+
120
+ # Returns "'" if either or both insist on single quotes, and nil
121
+ # otherwise.
122
+ before_quote || after_quote
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,69 @@
1
+ module Sass::Script::Tree
2
+ # A SassScript parse node representing a unary operation,
3
+ # such as `-$b` or `not true`.
4
+ #
5
+ # Currently only `-`, `/`, and `not` are unary operators.
6
+ class UnaryOperation < Node
7
+ # @return [Symbol] The operation to perform
8
+ attr_reader :operator
9
+
10
+ # @return [Script::Node] The parse-tree node for the object of the operator
11
+ attr_reader :operand
12
+
13
+ # @param operand [Script::Node] See \{#operand}
14
+ # @param operator [Symbol] See \{#operator}
15
+ def initialize(operand, operator)
16
+ @operand = operand
17
+ @operator = operator
18
+ super()
19
+ end
20
+
21
+ # @return [String] A human-readable s-expression representation of the operation
22
+ def inspect
23
+ "(#{@operator.inspect} #{@operand.inspect})"
24
+ end
25
+
26
+ # @see Node#to_sass
27
+ def to_sass(opts = {})
28
+ operand = @operand.to_sass(opts)
29
+ if @operand.is_a?(Operation) ||
30
+ (@operator == :minus &&
31
+ (operand =~ Sass::SCSS::RX::IDENT) == 0)
32
+ operand = "(#{@operand.to_sass(opts)})"
33
+ end
34
+ op = Sass::Script::Lexer::OPERATORS_REVERSE[@operator]
35
+ op + (op =~ /[a-z]/ ? " " : "") + operand
36
+ end
37
+
38
+ # Returns the operand of the operation.
39
+ #
40
+ # @return [Array<Node>]
41
+ # @see Node#children
42
+ def children
43
+ [@operand]
44
+ end
45
+
46
+ # @see Node#deep_copy
47
+ def deep_copy
48
+ node = dup
49
+ node.instance_variable_set('@operand', @operand.deep_copy)
50
+ node
51
+ end
52
+
53
+ protected
54
+
55
+ # Evaluates the operation.
56
+ #
57
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
58
+ # @return [Sass::Script::Value] The SassScript object that is the value of the operation
59
+ # @raise [Sass::SyntaxError] if the operation is undefined for the operand
60
+ def _perform(environment)
61
+ operator = "unary_#{@operator}"
62
+ value = @operand.perform(environment)
63
+ value.send(operator)
64
+ rescue NoMethodError => e
65
+ raise e unless e.name.to_s == operator.to_s
66
+ raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{value}\".")
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,57 @@
1
+ module Sass::Script::Tree
2
+ # A SassScript parse node representing a variable.
3
+ class Variable < Node
4
+ # The name of the variable.
5
+ #
6
+ # @return [String]
7
+ attr_reader :name
8
+
9
+ # The underscored name of the variable.
10
+ #
11
+ # @return [String]
12
+ attr_reader :underscored_name
13
+
14
+ # @param name [String] See \{#name}
15
+ def initialize(name)
16
+ @name = name
17
+ @underscored_name = name.tr("-", "_")
18
+ super()
19
+ end
20
+
21
+ # @return [String] A string representation of the variable
22
+ def inspect(opts = {})
23
+ "$#{dasherize(name, opts)}"
24
+ end
25
+ alias_method :to_sass, :inspect
26
+
27
+ # Returns an empty array.
28
+ #
29
+ # @return [Array<Node>] empty
30
+ # @see Node#children
31
+ def children
32
+ []
33
+ end
34
+
35
+ # @see Node#deep_copy
36
+ def deep_copy
37
+ dup
38
+ end
39
+
40
+ protected
41
+
42
+ # Evaluates the variable.
43
+ #
44
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
45
+ # @return [Sass::Script::Value] The SassScript object that is the value of the variable
46
+ # @raise [Sass::SyntaxError] if the variable is undefined
47
+ def _perform(environment)
48
+ val = environment.var(name)
49
+ raise Sass::SyntaxError.new("Undefined variable: \"$#{name}\".") unless val
50
+ if val.is_a?(Sass::Script::Value::Number) && val.original
51
+ val = val.dup
52
+ val.original = nil
53
+ end
54
+ val
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,16 @@
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'
15
+ require 'sass/script/tree/map_literal'
16
+ require 'sass/script/tree/selector'
@@ -0,0 +1,36 @@
1
+ module Sass::Script::Value
2
+ # A SassScript object representing a variable argument list. This works just
3
+ # like a normal list, but can also contain keyword arguments.
4
+ #
5
+ # The keyword arguments attached to this list are unused except when this is
6
+ # passed as a glob argument to a function or mixin.
7
+ class ArgList < List
8
+ # Whether \{#keywords} has been accessed. If so, we assume that all keywords
9
+ # were valid for the function that created this ArgList.
10
+ #
11
+ # @return [Boolean]
12
+ attr_accessor :keywords_accessed
13
+
14
+ # Creates a new argument list.
15
+ #
16
+ # @param value [Array<Value>] See \{List#value}.
17
+ # @param keywords [Hash<String, Value>, NormalizedMap<Value>] See \{#keywords}
18
+ # @param separator [String] See \{List#separator}.
19
+ def initialize(value, keywords, separator)
20
+ super(value, separator: separator)
21
+ if keywords.is_a?(Sass::Util::NormalizedMap)
22
+ @keywords = keywords
23
+ else
24
+ @keywords = Sass::Util::NormalizedMap.new(keywords)
25
+ end
26
+ end
27
+
28
+ # The keyword arguments attached to this list.
29
+ #
30
+ # @return [NormalizedMap<Value>]
31
+ def keywords
32
+ @keywords_accessed = true
33
+ @keywords
34
+ end
35
+ end
36
+ end