sassc-embedded 1.68.0 → 1.68.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -8
  3. data/lib/sassc/embedded/version.rb +1 -1
  4. data/lib/sassc/embedded.rb +8 -6
  5. data/vendor/github.com/sass/sassc-ruby/LICENSE.txt +22 -0
  6. data/vendor/github.com/sass/sassc-ruby/lib/sassc/dependency.rb +17 -0
  7. data/vendor/github.com/sass/sassc-ruby/lib/sassc/engine.rb +141 -0
  8. data/vendor/github.com/sass/sassc-ruby/lib/sassc/error.rb +37 -0
  9. data/vendor/github.com/sass/sassc-ruby/lib/sassc/functions_handler.rb +73 -0
  10. data/vendor/github.com/sass/sassc-ruby/lib/sassc/import_handler.rb +50 -0
  11. data/vendor/github.com/sass/sassc-ruby/lib/sassc/importer.rb +31 -0
  12. data/vendor/github.com/sass/sassc-ruby/lib/sassc/native/native_context_api.rb +147 -0
  13. data/vendor/github.com/sass/sassc-ruby/lib/sassc/native/native_functions_api.rb +159 -0
  14. data/vendor/github.com/sass/sassc-ruby/lib/sassc/native/sass2scss_api.rb +10 -0
  15. data/vendor/github.com/sass/sassc-ruby/lib/sassc/native/sass_input_style.rb +13 -0
  16. data/vendor/github.com/sass/sassc-ruby/lib/sassc/native/sass_output_style.rb +12 -0
  17. data/vendor/github.com/sass/sassc-ruby/lib/sassc/native/sass_value.rb +97 -0
  18. data/vendor/github.com/sass/sassc-ruby/lib/sassc/native/string_list.rb +10 -0
  19. data/vendor/github.com/sass/sassc-ruby/lib/sassc/native.rb +64 -0
  20. data/vendor/github.com/sass/sassc-ruby/lib/sassc/sass_2_scss.rb +9 -0
  21. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/functions.rb +8 -0
  22. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value/bool.rb +32 -0
  23. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value/color.rb +95 -0
  24. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value/list.rb +136 -0
  25. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value/map.rb +69 -0
  26. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value/number.rb +389 -0
  27. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value/string.rb +96 -0
  28. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value.rb +137 -0
  29. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value_conversion/base.rb +13 -0
  30. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value_conversion/bool.rb +13 -0
  31. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value_conversion/color.rb +18 -0
  32. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value_conversion/list.rb +25 -0
  33. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value_conversion/map.rb +21 -0
  34. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value_conversion/number.rb +13 -0
  35. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value_conversion/string.rb +17 -0
  36. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script/value_conversion.rb +69 -0
  37. data/vendor/github.com/sass/sassc-ruby/lib/sassc/script.rb +17 -0
  38. data/vendor/github.com/sass/sassc-ruby/lib/sassc/util/normalized_map.rb +117 -0
  39. data/vendor/github.com/sass/sassc-ruby/lib/sassc/util.rb +231 -0
  40. data/vendor/github.com/sass/sassc-ruby/lib/sassc/version.rb +5 -0
  41. data/vendor/github.com/sass/sassc-ruby/lib/sassc.rb +64 -0
  42. metadata +42 -18
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SassC::Script::Value::Map < SassC::Script::Value
4
+
5
+ # The Ruby hash containing the contents of this map.
6
+ # @return [Hash<Node, Node>]
7
+ attr_reader :value
8
+ alias_method :to_h, :value
9
+
10
+ # Creates a new map.
11
+ #
12
+ # @param hash [Hash<Node, Node>]
13
+ def initialize(hash)
14
+ super(hash)
15
+ end
16
+
17
+ # @see Value#options=
18
+ def options=(options)
19
+ super
20
+ value.each do |k, v|
21
+ k.options = options
22
+ v.options = options
23
+ end
24
+ end
25
+
26
+ # @see Value#separator
27
+ def separator
28
+ :comma unless value.empty?
29
+ end
30
+
31
+ # @see Value#to_a
32
+ def to_a
33
+ value.map do |k, v|
34
+ list = SassC::Script::Value::List.new([k, v], separator: :space)
35
+ list.options = options
36
+ list
37
+ end
38
+ end
39
+
40
+ # @see Value#eq
41
+ def eq(other)
42
+ SassC::Script::Value::Bool.new(other.is_a?(Map) && value == other.value)
43
+ end
44
+
45
+ def hash
46
+ @hash ||= value.hash
47
+ end
48
+
49
+ # @see Value#to_s
50
+ def to_s(opts = {})
51
+ raise SassC::SyntaxError.new("#{inspect} isn't a valid CSS value.")
52
+ end
53
+
54
+ def to_sass(opts = {})
55
+ return "()" if value.empty?
56
+
57
+ to_sass = lambda do |value|
58
+ if value.is_a?(List) && value.separator == :comma
59
+ "(#{value.to_sass(opts)})"
60
+ else
61
+ value.to_sass(opts)
62
+ end
63
+ end
64
+
65
+ "(#{value.map {|(k, v)| "#{to_sass[k]}: #{to_sass[v]}"}.join(', ')})"
66
+ end
67
+ alias_method :inspect, :to_sass
68
+
69
+ end
@@ -0,0 +1,389 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A SassScript object representing a number.
4
+ # SassScript numbers can have decimal values,
5
+ # and can also have units.
6
+ # For example, `12`, `1px`, and `10.45em`
7
+ # are all valid values.
8
+ #
9
+ # Numbers can also have more complex units, such as `1px*em/in`.
10
+ # These cannot be inputted directly in Sass code at the moment.
11
+
12
+ class SassC::Script::Value::Number < SassC::Script::Value
13
+
14
+ # The Ruby value of the number.
15
+ #
16
+ # @return [Numeric]
17
+ attr_reader :value
18
+
19
+ # A list of units in the numerator of the number.
20
+ # For example, `1px*em/in*cm` would return `["px", "em"]`
21
+ # @return [Array<String>]
22
+ attr_reader :numerator_units
23
+
24
+ # A list of units in the denominator of the number.
25
+ # For example, `1px*em/in*cm` would return `["in", "cm"]`
26
+ # @return [Array<String>]
27
+ attr_reader :denominator_units
28
+
29
+ # The original representation of this number.
30
+ # For example, although the result of `1px/2px` is `0.5`,
31
+ # the value of `#original` is `"1px/2px"`.
32
+ #
33
+ # This is only non-nil when the original value should be used as the CSS value,
34
+ # as in `font: 1px/2px`.
35
+ #
36
+ # @return [Boolean, nil]
37
+ attr_accessor :original
38
+
39
+ def self.precision
40
+ Thread.current[:sass_numeric_precision] || Thread.main[:sass_numeric_precision] || 10
41
+ end
42
+
43
+ # Sets the number of digits of precision
44
+ # For example, if this is `3`,
45
+ # `3.1415926` will be printed as `3.142`.
46
+ # The numeric precision is stored as a thread local for thread safety reasons.
47
+ # To set for all threads, be sure to set the precision on the main thread.
48
+ def self.precision=(digits)
49
+ Thread.current[:sass_numeric_precision] = digits.round
50
+ Thread.current[:sass_numeric_precision_factor] = nil
51
+ Thread.current[:sass_numeric_epsilon] = nil
52
+ end
53
+
54
+ # the precision factor used in numeric output
55
+ # it is derived from the `precision` method.
56
+ def self.precision_factor
57
+ Thread.current[:sass_numeric_precision_factor] ||= 10.0**precision
58
+ end
59
+
60
+ # Used in checking equality of floating point numbers. Any
61
+ # numbers within an `epsilon` of each other are considered functionally equal.
62
+ # The value for epsilon is one tenth of the current numeric precision.
63
+ def self.epsilon
64
+ Thread.current[:sass_numeric_epsilon] ||= 1 / (precision_factor * 10)
65
+ end
66
+
67
+ # Used so we don't allocate two new arrays for each new number.
68
+ NO_UNITS = []
69
+
70
+ # @param value [Numeric] The value of the number
71
+ # @param numerator_units [::String, Array<::String>] See \{#numerator\_units}
72
+ # @param denominator_units [::String, Array<::String>] See \{#denominator\_units}
73
+ def initialize(value, numerator_units = NO_UNITS, denominator_units = NO_UNITS)
74
+ numerator_units = [numerator_units] if numerator_units.is_a?(::String)
75
+ denominator_units = [denominator_units] if denominator_units.is_a?(::String)
76
+ super(value)
77
+ @numerator_units = numerator_units
78
+ @denominator_units = denominator_units
79
+ @options = nil
80
+ normalize!
81
+ end
82
+
83
+ def hash
84
+ [value, numerator_units, denominator_units].hash
85
+ end
86
+
87
+ # Hash-equality works differently than `==` equality for numbers.
88
+ # Hash-equality must be transitive, so it just compares the exact value,
89
+ # numerator units, and denominator units.
90
+ def eql?(other)
91
+ basically_equal?(value, other.value) && numerator_units == other.numerator_units &&
92
+ denominator_units == other.denominator_units
93
+ end
94
+
95
+ # @return [String] The CSS representation of this number
96
+ # @raise [Sass::SyntaxError] if this number has units that can't be used in CSS
97
+ # (e.g. `px*in`)
98
+ def to_s(opts = {})
99
+ return original if original
100
+ raise Sass::SyntaxError.new("#{inspect} isn't a valid CSS value.") unless legal_units?
101
+ inspect
102
+ end
103
+
104
+ # Returns a readable representation of this number.
105
+ #
106
+ # This representation is valid CSS (and valid SassScript)
107
+ # as long as there is only one unit.
108
+ #
109
+ # @return [String] The representation
110
+ def inspect(opts = {})
111
+ return original if original
112
+
113
+ value = self.class.round(self.value)
114
+ str = value.to_s
115
+
116
+ # Ruby will occasionally print in scientific notation if the number is
117
+ # small enough. That's technically valid CSS, but it's not well-supported
118
+ # and confusing.
119
+ str = ("%0.#{self.class.precision}f" % value).gsub(/0*$/, '') if str.include?('e')
120
+
121
+ # Sometimes numeric formatting will result in a decimal number with a trailing zero (x.0)
122
+ if str =~ /(.*)\.0$/
123
+ str = $1
124
+ end
125
+
126
+ # We omit a leading zero before the decimal point in compressed mode.
127
+ if @options && options[:style] == :compressed
128
+ str.sub!(/^(-)?0\./, '\1.')
129
+ end
130
+
131
+ unitless? ? str : "#{str}#{unit_str}"
132
+ end
133
+ alias_method :to_sass, :inspect
134
+
135
+ # @return [Integer] The integer value of the number
136
+ # @raise [Sass::SyntaxError] if the number isn't an integer
137
+ def to_i
138
+ super unless int?
139
+ value.to_i
140
+ end
141
+
142
+ # @return [Boolean] Whether or not this number is an integer.
143
+ def int?
144
+ basically_equal?(value % 1, 0.0)
145
+ end
146
+
147
+ # @return [Boolean] Whether or not this number has no units.
148
+ def unitless?
149
+ @numerator_units.empty? && @denominator_units.empty?
150
+ end
151
+
152
+ # Checks whether the number has the numerator unit specified.
153
+ #
154
+ # @example
155
+ # number = Sass::Script::Value::Number.new(10, "px")
156
+ # number.is_unit?("px") => true
157
+ # number.is_unit?(nil) => false
158
+ #
159
+ # @param unit [::String, nil] The unit the number should have or nil if the number
160
+ # should be unitless.
161
+ # @see Number#unitless? The unitless? method may be more readable.
162
+ def is_unit?(unit)
163
+ if unit
164
+ denominator_units.size == 0 && numerator_units.size == 1 && numerator_units.first == unit
165
+ else
166
+ unitless?
167
+ end
168
+ end
169
+
170
+ # @return [Boolean] Whether or not this number has units that can be represented in CSS
171
+ # (that is, zero or one \{#numerator\_units}).
172
+ def legal_units?
173
+ (@numerator_units.empty? || @numerator_units.size == 1) && @denominator_units.empty?
174
+ end
175
+
176
+ # Returns this number converted to other units.
177
+ # The conversion takes into account the relationship between e.g. mm and cm,
178
+ # as well as between e.g. in and cm.
179
+ #
180
+ # If this number has no units, it will simply return itself
181
+ # with the given units.
182
+ #
183
+ # An incompatible coercion, e.g. between px and cm, will raise an error.
184
+ #
185
+ # @param num_units [Array<String>] The numerator units to coerce this number into.
186
+ # See {\#numerator\_units}
187
+ # @param den_units [Array<String>] The denominator units to coerce this number into.
188
+ # See {\#denominator\_units}
189
+ # @return [Number] The number with the new units
190
+ # @raise [Sass::UnitConversionError] if the given units are incompatible with the number's
191
+ # current units
192
+ def coerce(num_units, den_units)
193
+ Number.new(if unitless?
194
+ value
195
+ else
196
+ value * coercion_factor(@numerator_units, num_units) /
197
+ coercion_factor(@denominator_units, den_units)
198
+ end, num_units, den_units)
199
+ end
200
+
201
+ # @param other [Number] A number to decide if it can be compared with this number.
202
+ # @return [Boolean] Whether or not this number can be compared with the other.
203
+ def comparable_to?(other)
204
+ operate(other, :+)
205
+ true
206
+ rescue Sass::UnitConversionError
207
+ false
208
+ end
209
+
210
+ # Returns a human readable representation of the units in this number.
211
+ # For complex units this takes the form of:
212
+ # numerator_unit1 * numerator_unit2 / denominator_unit1 * denominator_unit2
213
+ # @return [String] a string that represents the units in this number
214
+ def unit_str
215
+ rv = @numerator_units.sort.join("*")
216
+ if @denominator_units.any?
217
+ rv << "/"
218
+ rv << @denominator_units.sort.join("*")
219
+ end
220
+ rv
221
+ end
222
+
223
+ private
224
+
225
+ # @private
226
+ # @see Sass::Script::Number.basically_equal?
227
+ def basically_equal?(num1, num2)
228
+ self.class.basically_equal?(num1, num2)
229
+ end
230
+
231
+ # Checks whether two numbers are within an epsilon of each other.
232
+ # @return [Boolean]
233
+ def self.basically_equal?(num1, num2)
234
+ (num1 - num2).abs < epsilon
235
+ end
236
+
237
+ # @private
238
+ def self.round(num)
239
+ if num.is_a?(Float) && (num.infinite? || num.nan?)
240
+ num
241
+ elsif basically_equal?(num % 1, 0.0)
242
+ num.round
243
+ else
244
+ ((num * precision_factor).round / precision_factor).to_f
245
+ end
246
+ end
247
+
248
+ OPERATIONS = [:+, :-, :<=, :<, :>, :>=, :%]
249
+
250
+ def operate(other, operation)
251
+ this = self
252
+ if OPERATIONS.include?(operation)
253
+ if unitless?
254
+ this = this.coerce(other.numerator_units, other.denominator_units)
255
+ else
256
+ other = other.coerce(@numerator_units, @denominator_units)
257
+ end
258
+ end
259
+ # avoid integer division
260
+ value = :/ == operation ? this.value.to_f : this.value
261
+ result = value.send(operation, other.value)
262
+
263
+ if result.is_a?(Numeric)
264
+ Number.new(result, *compute_units(this, other, operation))
265
+ else # Boolean op
266
+ Bool.new(result)
267
+ end
268
+ end
269
+
270
+ def coercion_factor(from_units, to_units)
271
+ # get a list of unmatched units
272
+ from_units, to_units = sans_common_units(from_units, to_units)
273
+
274
+ if from_units.size != to_units.size || !convertable?(from_units | to_units)
275
+ raise Sass::UnitConversionError.new(
276
+ "Incompatible units: '#{from_units.join('*')}' and '#{to_units.join('*')}'.")
277
+ end
278
+
279
+ from_units.zip(to_units).inject(1) {|m, p| m * conversion_factor(p[0], p[1])}
280
+ end
281
+
282
+ def compute_units(this, other, operation)
283
+ case operation
284
+ when :*
285
+ [this.numerator_units + other.numerator_units,
286
+ this.denominator_units + other.denominator_units]
287
+ when :/
288
+ [this.numerator_units + other.denominator_units,
289
+ this.denominator_units + other.numerator_units]
290
+ else
291
+ [this.numerator_units, this.denominator_units]
292
+ end
293
+ end
294
+
295
+ def normalize!
296
+ return if unitless?
297
+ @numerator_units, @denominator_units =
298
+ sans_common_units(@numerator_units, @denominator_units)
299
+
300
+ @denominator_units.each_with_index do |d, i|
301
+ next unless convertable?(d) && (u = @numerator_units.find {|n| convertable?([n, d])})
302
+ @value /= conversion_factor(d, u)
303
+ @denominator_units.delete_at(i)
304
+ @numerator_units.delete_at(@numerator_units.index(u))
305
+ end
306
+ end
307
+
308
+ # This is the source data for all the unit logic. It's pre-processed to make
309
+ # it efficient to figure out whether a set of units is mutually compatible
310
+ # and what the conversion ratio is between two units.
311
+ #
312
+ # These come from http://www.w3.org/TR/2012/WD-css3-values-20120308/.
313
+ relative_sizes = [
314
+ {
315
+ "in" => Rational(1),
316
+ "cm" => Rational(1, 2.54),
317
+ "pc" => Rational(1, 6),
318
+ "mm" => Rational(1, 25.4),
319
+ "q" => Rational(1, 101.6),
320
+ "pt" => Rational(1, 72),
321
+ "px" => Rational(1, 96)
322
+ },
323
+ {
324
+ "deg" => Rational(1, 360),
325
+ "grad" => Rational(1, 400),
326
+ "rad" => Rational(1, 2 * Math::PI),
327
+ "turn" => Rational(1)
328
+ },
329
+ {
330
+ "s" => Rational(1),
331
+ "ms" => Rational(1, 1000)
332
+ },
333
+ {
334
+ "Hz" => Rational(1),
335
+ "kHz" => Rational(1000)
336
+ },
337
+ {
338
+ "dpi" => Rational(1),
339
+ "dpcm" => Rational(254, 100),
340
+ "dppx" => Rational(96)
341
+ }
342
+ ]
343
+
344
+ # A hash from each known unit to the set of units that it's mutually
345
+ # convertible with.
346
+ MUTUALLY_CONVERTIBLE = {}
347
+ relative_sizes.map do |values|
348
+ set = values.keys.to_set
349
+ values.keys.each {|name| MUTUALLY_CONVERTIBLE[name] = set}
350
+ end
351
+
352
+ # A two-dimensional hash from two units to the conversion ratio between
353
+ # them. Multiply `X` by `CONVERSION_TABLE[X][Y]` to convert it to `Y`.
354
+ CONVERSION_TABLE = {}
355
+ relative_sizes.each do |values|
356
+ values.each do |(name1, value1)|
357
+ CONVERSION_TABLE[name1] ||= {}
358
+ values.each do |(name2, value2)|
359
+ value = value1 / value2
360
+ CONVERSION_TABLE[name1][name2] = value.denominator == 1 ? value.to_i : value.to_f
361
+ end
362
+ end
363
+ end
364
+
365
+ def conversion_factor(from_unit, to_unit)
366
+ CONVERSION_TABLE[from_unit][to_unit]
367
+ end
368
+
369
+ def convertable?(units)
370
+ units = Array(units).to_set
371
+ return true if units.empty?
372
+ return false unless (mutually_convertible = MUTUALLY_CONVERTIBLE[units.first])
373
+ units.subset?(mutually_convertible)
374
+ end
375
+
376
+ def sans_common_units(units1, units2)
377
+ units2 = units2.dup
378
+ # Can't just use -, because we want px*px to coerce properly to px*mm
379
+ units1 = units1.map do |u|
380
+ j = units2.index(u)
381
+ next u unless j
382
+ units2.delete_at(j)
383
+ nil
384
+ end
385
+ units1.compact!
386
+ return units1, units2
387
+ end
388
+
389
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SassC::Script::Value::String < SassC::Script::Value
4
+
5
+ # The Ruby value of the string.
6
+ attr_reader :value
7
+
8
+ # Whether this is a CSS string or a CSS identifier.
9
+ # The difference is that strings are written with double-quotes,
10
+ # while identifiers aren't.
11
+ #
12
+ # @return [Symbol] `:string` or `:identifier`
13
+ attr_reader :type
14
+
15
+ # Returns the quoted string representation of `contents`.
16
+ #
17
+ # @options opts :quote [String]
18
+ # The preferred quote style for quoted strings. If `:none`, strings are
19
+ # always emitted unquoted. If `nil`, quoting is determined automatically.
20
+ # @options opts :sass [String]
21
+ # Whether to quote strings for Sass source, as opposed to CSS. Defaults to `false`.
22
+ def self.quote(contents, opts = {})
23
+ quote = opts[:quote]
24
+
25
+ # Short-circuit if there are no characters that need quoting.
26
+ unless contents =~ /[\n\\"']|\#\{/
27
+ quote ||= '"'
28
+ return "#{quote}#{contents}#{quote}"
29
+ end
30
+
31
+ if quote.nil?
32
+ if contents.include?('"')
33
+ if contents.include?("'")
34
+ quote = '"'
35
+ else
36
+ quote = "'"
37
+ end
38
+ else
39
+ quote = '"'
40
+ end
41
+ end
42
+
43
+ # Replace single backslashes with multiples.
44
+ contents = contents.gsub("\\", "\\\\\\\\")
45
+
46
+ # Escape interpolation.
47
+ contents = contents.gsub('#{', "\\\#{") if opts[:sass]
48
+
49
+ if quote == '"'
50
+ contents = contents.gsub('"', "\\\"")
51
+ else
52
+ contents = contents.gsub("'", "\\'")
53
+ end
54
+
55
+ contents = contents.gsub(/\n(?![a-fA-F0-9\s])/, "\\a").gsub("\n", "\\a ")
56
+ "#{quote}#{contents}#{quote}"
57
+ end
58
+
59
+ # Creates a new string.
60
+ #
61
+ # @param value [String] See \{#value}
62
+ # @param type [Symbol] See \{#type}
63
+ # @param deprecated_interp_equivalent [String?]
64
+ # If this was created via a potentially-deprecated string interpolation,
65
+ # this is the replacement expression that should be suggested to the user.
66
+ def initialize(value, type = :identifier)
67
+ super(value)
68
+ @type = type
69
+ end
70
+
71
+ # @see Value#plus
72
+ def plus(other)
73
+ if other.is_a?(SassC::Script::Value::String)
74
+ other_value = other.value
75
+ else
76
+ other_value = other.to_s(:quote => :none)
77
+ end
78
+ SassC::Script::Value::String.new(value + other_value, type)
79
+ end
80
+
81
+ # @see Value#to_s
82
+ def to_s(opts = {})
83
+ return @value.gsub(/\n\s*/, ' ') if opts[:quote] == :none || @type == :identifier
84
+ self.class.quote(value, opts)
85
+ end
86
+
87
+ # @see Value#to_sass
88
+ def to_sass(opts = {})
89
+ to_s(opts.merge(:sass => true))
90
+ end
91
+
92
+ def inspect
93
+ String.quote(value)
94
+ end
95
+
96
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The abstract superclass for SassScript objects.
4
+ # Many of these methods, especially the ones that correspond to SassScript operations,
5
+ # are designed to be overridden by subclasses which may change the semantics somewhat.
6
+ # The operations listed here are just the defaults.
7
+
8
+ class SassC::Script::Value
9
+
10
+ # Returns the pure Ruby value of the value.
11
+ # The type of this value varies based on the subclass.
12
+ attr_reader :value
13
+
14
+ # The source range in the document on which this node appeared.
15
+ attr_accessor :source_range
16
+
17
+ # Creates a new value.
18
+ def initialize(value = nil)
19
+ value.freeze unless value.nil? || value == true || value == false
20
+ @value = value
21
+ @options = nil
22
+ end
23
+
24
+ # Sets the options hash for this node,
25
+ # as well as for all child nodes.
26
+ # See the official Sass reference for options.
27
+ attr_writer :options
28
+
29
+ # Returns the options hash for this node.
30
+ # Raises SassC::SyntaxError if the value was created
31
+ # outside of the parser and \{#to\_s} was called on it
32
+ def options
33
+ return @options if @options
34
+ raise SassC::SyntaxError.new("The #options attribute is not set on this #{self.class}. This error is probably occurring because #to_s was called on this value within a custom Sass function without first setting the #options attribute.")
35
+ end
36
+
37
+ # Returns the hash code of this value. Two objects' hash codes should be
38
+ # equal if the objects are equal.
39
+ def hash
40
+ value.hash
41
+ end
42
+
43
+ # True if this Value is the same as `other`
44
+ def eql?(other)
45
+ self == other
46
+ end
47
+
48
+ # Returns a system inspect value for this object
49
+ def inspect
50
+ value.inspect
51
+ end
52
+
53
+ # Returns `true` (all Values are truthy)
54
+ def to_bool
55
+ true
56
+ end
57
+
58
+ # Compares this object to `other`
59
+ def ==(other)
60
+ self.class == other.class && value == other.value
61
+ end
62
+
63
+ # Returns the integer value of this value.
64
+ # Raises SassC::SyntaxError if this value doesn’t implment integer conversion.
65
+ def to_i
66
+ raise SassC::SyntaxError.new("#{inspect} is not an integer.")
67
+ end
68
+
69
+ # @raise [SassC::SyntaxError] if this value isn't an integer
70
+ def assert_int!; to_i; end
71
+
72
+ # Returns the separator for this value. For non-list-like values or the
73
+ # empty list, this will be `nil`. For lists or maps, it will be `:space` or `:comma`.
74
+ def separator
75
+ nil
76
+ end
77
+
78
+ # Whether the value is surrounded by square brackets. For non-list values,
79
+ # this will be `false`.
80
+ def bracketed
81
+ false
82
+ end
83
+
84
+ # Returns the value of this Value as an array.
85
+ # Single Values are considered the same as single-element arrays.
86
+ def to_a
87
+ [self]
88
+ end
89
+
90
+ # Returns the value of this value as a hash. Most values don't have hash
91
+ # representations, but [Map]s and empty [List]s do.
92
+ #
93
+ # @return [Hash<Value, Value>] This value as a hash
94
+ # @raise [SassC::SyntaxError] if this value doesn't have a hash representation
95
+ def to_h
96
+ raise SassC::SyntaxError.new("#{inspect} is not a map.")
97
+ end
98
+
99
+ # Returns the string representation of this value
100
+ # as it would be output to the CSS document.
101
+ #
102
+ # @options opts :quote [String]
103
+ # The preferred quote style for quoted strings. If `:none`, strings are
104
+ # always emitted unquoted.
105
+ # @return [String]
106
+ def to_s(opts = {})
107
+ SassC::Util.abstract(self)
108
+ end
109
+ alias_method :to_sass, :to_s
110
+
111
+ # Returns `false` (all Values are truthy)
112
+ def null?
113
+ false
114
+ end
115
+
116
+ # Creates a new list containing `contents` but with the same brackets and
117
+ # separators as this object, when interpreted as a list.
118
+ #
119
+ # @param contents [Array<Value>] The contents of the new list.
120
+ # @param separator [Symbol] The separator of the new list. Defaults to \{#separator}.
121
+ # @param bracketed [Boolean] Whether the new list is bracketed. Defaults to \{#bracketed}.
122
+ # @return [Sass::Script::Value::List]
123
+ def with_contents(contents, separator: self.separator, bracketed: self.bracketed)
124
+ SassC::Script::Value::List.new(contents, separator: separator, bracketed: bracketed)
125
+ end
126
+
127
+ protected
128
+
129
+ # Evaluates the value.
130
+ #
131
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
132
+ # @return [Value] This value
133
+ def _perform(environment)
134
+ self
135
+ end
136
+
137
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SassC
4
+ module Script
5
+ module ValueConversion
6
+ class Base
7
+ def initialize(value)
8
+ @value = value
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end