drnic-haml 2.3.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 (190) hide show
  1. data/.yardopts +5 -0
  2. data/CONTRIBUTING +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +347 -0
  5. data/REVISION +1 -0
  6. data/Rakefile +371 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/css2sass +7 -0
  10. data/bin/haml +9 -0
  11. data/bin/html2haml +7 -0
  12. data/bin/sass +8 -0
  13. data/extra/haml-mode.el +663 -0
  14. data/extra/sass-mode.el +205 -0
  15. data/extra/update_watch.rb +13 -0
  16. data/init.rb +8 -0
  17. data/lib/haml.rb +40 -0
  18. data/lib/haml/buffer.rb +307 -0
  19. data/lib/haml/engine.rb +301 -0
  20. data/lib/haml/error.rb +22 -0
  21. data/lib/haml/exec.rb +470 -0
  22. data/lib/haml/filters.rb +341 -0
  23. data/lib/haml/helpers.rb +560 -0
  24. data/lib/haml/helpers/action_view_extensions.rb +40 -0
  25. data/lib/haml/helpers/action_view_mods.rb +176 -0
  26. data/lib/haml/herb.rb +96 -0
  27. data/lib/haml/html.rb +308 -0
  28. data/lib/haml/precompiler.rb +997 -0
  29. data/lib/haml/shared.rb +78 -0
  30. data/lib/haml/template.rb +51 -0
  31. data/lib/haml/template/patch.rb +58 -0
  32. data/lib/haml/template/plugin.rb +71 -0
  33. data/lib/haml/util.rb +244 -0
  34. data/lib/haml/version.rb +64 -0
  35. data/lib/sass.rb +24 -0
  36. data/lib/sass/css.rb +423 -0
  37. data/lib/sass/engine.rb +491 -0
  38. data/lib/sass/environment.rb +79 -0
  39. data/lib/sass/error.rb +162 -0
  40. data/lib/sass/files.rb +133 -0
  41. data/lib/sass/plugin.rb +170 -0
  42. data/lib/sass/plugin/merb.rb +57 -0
  43. data/lib/sass/plugin/rails.rb +23 -0
  44. data/lib/sass/repl.rb +58 -0
  45. data/lib/sass/script.rb +55 -0
  46. data/lib/sass/script/bool.rb +17 -0
  47. data/lib/sass/script/color.rb +183 -0
  48. data/lib/sass/script/funcall.rb +50 -0
  49. data/lib/sass/script/functions.rb +199 -0
  50. data/lib/sass/script/lexer.rb +191 -0
  51. data/lib/sass/script/literal.rb +177 -0
  52. data/lib/sass/script/node.rb +14 -0
  53. data/lib/sass/script/number.rb +381 -0
  54. data/lib/sass/script/operation.rb +45 -0
  55. data/lib/sass/script/parser.rb +222 -0
  56. data/lib/sass/script/string.rb +12 -0
  57. data/lib/sass/script/unary_operation.rb +34 -0
  58. data/lib/sass/script/variable.rb +31 -0
  59. data/lib/sass/tree/comment_node.rb +84 -0
  60. data/lib/sass/tree/debug_node.rb +30 -0
  61. data/lib/sass/tree/directive_node.rb +70 -0
  62. data/lib/sass/tree/for_node.rb +48 -0
  63. data/lib/sass/tree/if_node.rb +54 -0
  64. data/lib/sass/tree/import_node.rb +69 -0
  65. data/lib/sass/tree/mixin_def_node.rb +29 -0
  66. data/lib/sass/tree/mixin_node.rb +48 -0
  67. data/lib/sass/tree/node.rb +252 -0
  68. data/lib/sass/tree/prop_node.rb +106 -0
  69. data/lib/sass/tree/root_node.rb +56 -0
  70. data/lib/sass/tree/rule_node.rb +220 -0
  71. data/lib/sass/tree/variable_node.rb +34 -0
  72. data/lib/sass/tree/while_node.rb +31 -0
  73. data/rails/init.rb +1 -0
  74. data/test/benchmark.rb +99 -0
  75. data/test/haml/engine_test.rb +1129 -0
  76. data/test/haml/helper_test.rb +282 -0
  77. data/test/haml/html2haml_test.rb +258 -0
  78. data/test/haml/markaby/standard.mab +52 -0
  79. data/test/haml/mocks/article.rb +6 -0
  80. data/test/haml/results/content_for_layout.xhtml +12 -0
  81. data/test/haml/results/eval_suppressed.xhtml +9 -0
  82. data/test/haml/results/filters.xhtml +62 -0
  83. data/test/haml/results/helpers.xhtml +93 -0
  84. data/test/haml/results/helpful.xhtml +10 -0
  85. data/test/haml/results/just_stuff.xhtml +68 -0
  86. data/test/haml/results/list.xhtml +12 -0
  87. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  88. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  89. data/test/haml/results/original_engine.xhtml +20 -0
  90. data/test/haml/results/partial_layout.xhtml +5 -0
  91. data/test/haml/results/partials.xhtml +21 -0
  92. data/test/haml/results/render_layout.xhtml +3 -0
  93. data/test/haml/results/silent_script.xhtml +74 -0
  94. data/test/haml/results/standard.xhtml +162 -0
  95. data/test/haml/results/tag_parsing.xhtml +23 -0
  96. data/test/haml/results/very_basic.xhtml +5 -0
  97. data/test/haml/results/whitespace_handling.xhtml +89 -0
  98. data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  99. data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  100. data/test/haml/rhtml/action_view.rhtml +62 -0
  101. data/test/haml/rhtml/standard.rhtml +54 -0
  102. data/test/haml/spec_test.rb +44 -0
  103. data/test/haml/template_test.rb +217 -0
  104. data/test/haml/templates/_av_partial_1.haml +9 -0
  105. data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
  106. data/test/haml/templates/_av_partial_2.haml +5 -0
  107. data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
  108. data/test/haml/templates/_layout.erb +3 -0
  109. data/test/haml/templates/_layout_for_partial.haml +3 -0
  110. data/test/haml/templates/_partial.haml +8 -0
  111. data/test/haml/templates/_text_area.haml +3 -0
  112. data/test/haml/templates/action_view.haml +47 -0
  113. data/test/haml/templates/action_view_ugly.haml +47 -0
  114. data/test/haml/templates/breakage.haml +8 -0
  115. data/test/haml/templates/content_for_layout.haml +8 -0
  116. data/test/haml/templates/eval_suppressed.haml +11 -0
  117. data/test/haml/templates/filters.haml +66 -0
  118. data/test/haml/templates/helpers.haml +95 -0
  119. data/test/haml/templates/helpful.haml +11 -0
  120. data/test/haml/templates/just_stuff.haml +83 -0
  121. data/test/haml/templates/list.haml +12 -0
  122. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  123. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  124. data/test/haml/templates/original_engine.haml +17 -0
  125. data/test/haml/templates/partial_layout.haml +3 -0
  126. data/test/haml/templates/partialize.haml +1 -0
  127. data/test/haml/templates/partials.haml +12 -0
  128. data/test/haml/templates/render_layout.haml +2 -0
  129. data/test/haml/templates/silent_script.haml +40 -0
  130. data/test/haml/templates/standard.haml +42 -0
  131. data/test/haml/templates/standard_ugly.haml +42 -0
  132. data/test/haml/templates/tag_parsing.haml +21 -0
  133. data/test/haml/templates/very_basic.haml +4 -0
  134. data/test/haml/templates/whitespace_handling.haml +87 -0
  135. data/test/haml/util_test.rb +92 -0
  136. data/test/linked_rails.rb +12 -0
  137. data/test/sass/css2sass_test.rb +294 -0
  138. data/test/sass/engine_test.rb +956 -0
  139. data/test/sass/functions_test.rb +126 -0
  140. data/test/sass/more_results/more1.css +9 -0
  141. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  142. data/test/sass/more_results/more_import.css +29 -0
  143. data/test/sass/more_templates/_more_partial.sass +2 -0
  144. data/test/sass/more_templates/more1.sass +23 -0
  145. data/test/sass/more_templates/more_import.sass +11 -0
  146. data/test/sass/plugin_test.rb +229 -0
  147. data/test/sass/results/alt.css +4 -0
  148. data/test/sass/results/basic.css +9 -0
  149. data/test/sass/results/compact.css +5 -0
  150. data/test/sass/results/complex.css +87 -0
  151. data/test/sass/results/compressed.css +1 -0
  152. data/test/sass/results/expanded.css +19 -0
  153. data/test/sass/results/import.css +29 -0
  154. data/test/sass/results/line_numbers.css +49 -0
  155. data/test/sass/results/mixins.css +95 -0
  156. data/test/sass/results/multiline.css +24 -0
  157. data/test/sass/results/nested.css +22 -0
  158. data/test/sass/results/parent_ref.css +13 -0
  159. data/test/sass/results/script.css +16 -0
  160. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  161. data/test/sass/results/subdir/subdir.css +3 -0
  162. data/test/sass/results/units.css +11 -0
  163. data/test/sass/script_test.rb +261 -0
  164. data/test/sass/templates/_partial.sass +2 -0
  165. data/test/sass/templates/alt.sass +16 -0
  166. data/test/sass/templates/basic.sass +23 -0
  167. data/test/sass/templates/bork1.sass +2 -0
  168. data/test/sass/templates/bork2.sass +2 -0
  169. data/test/sass/templates/bork3.sass +2 -0
  170. data/test/sass/templates/compact.sass +17 -0
  171. data/test/sass/templates/complex.sass +307 -0
  172. data/test/sass/templates/compressed.sass +15 -0
  173. data/test/sass/templates/expanded.sass +17 -0
  174. data/test/sass/templates/import.sass +11 -0
  175. data/test/sass/templates/importee.sass +19 -0
  176. data/test/sass/templates/line_numbers.sass +13 -0
  177. data/test/sass/templates/mixins.sass +76 -0
  178. data/test/sass/templates/multiline.sass +20 -0
  179. data/test/sass/templates/nested.sass +25 -0
  180. data/test/sass/templates/nested_bork1.sass +2 -0
  181. data/test/sass/templates/nested_bork2.sass +2 -0
  182. data/test/sass/templates/nested_bork3.sass +2 -0
  183. data/test/sass/templates/parent_ref.sass +25 -0
  184. data/test/sass/templates/script.sass +101 -0
  185. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  186. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  187. data/test/sass/templates/subdir/subdir.sass +6 -0
  188. data/test/sass/templates/units.sass +11 -0
  189. data/test/test_helper.rb +44 -0
  190. metadata +298 -0
@@ -0,0 +1,199 @@
1
+ module Sass::Script
2
+ # Methods in this module are accessible from the SassScript context.
3
+ # For example, you can write
4
+ #
5
+ # !color = hsl(120, 100%, 50%)
6
+ #
7
+ # and it will call {Sass::Script::Functions#hsl}.
8
+ #
9
+ # The following functions are provided:
10
+ #
11
+ # \{#hsl}
12
+ # : Converts an `hsl(hue, saturation, lightness)` triplet into a color.
13
+ #
14
+ # \{#percentage}
15
+ # : Converts a unitless number to a percentage.
16
+ #
17
+ # \{#round}
18
+ # : Rounds a number to the nearest whole number.
19
+ #
20
+ # \{#ceil}
21
+ # : Rounds a number up to the nearest whole number.
22
+ #
23
+ # \{#floor}
24
+ # : Rounds a number down to the nearest whole number.
25
+ #
26
+ # \{#abs}
27
+ # : Returns the absolute value of a number.
28
+ #
29
+ # You can add your own functions to this module,
30
+ # but there are a few things to keep in mind.
31
+ # First of all, the arguments passed are {Sass::Script::Literal} objects.
32
+ # Literal objects are also expected to be returned.
33
+ #
34
+ # Second, making Ruby functions accessible from Sass introduces the temptation
35
+ # to do things like database access within stylesheets.
36
+ # This temptation must be resisted.
37
+ # Keep in mind that Sass stylesheets are only compiled once
38
+ # at a somewhat indeterminate time
39
+ # and then left as static CSS files.
40
+ # Any dynamic CSS should be left in `<style>` tags in the HTML.
41
+ #
42
+ # Within one of the functions in this module,
43
+ # methods of {EvaluationContext} can be used.
44
+ module Functions
45
+ # The context in which methods in {Script::Functions} are evaluated.
46
+ # That means that all instance methods of {EvaluationContext}
47
+ # are available to use in functions.
48
+ class EvaluationContext
49
+ include Sass::Script::Functions
50
+
51
+ # The options hash for the {Sass::Engine} that is processing the function call
52
+ #
53
+ # @return [Hash<Symbol, Object>]
54
+ attr_reader :options
55
+
56
+ # @param options [Hash<Symbol, Object>] See \{#options}
57
+ def initialize(options)
58
+ @options = options
59
+ end
60
+ end
61
+
62
+ instance_methods.each { |m| undef_method m unless m.to_s =~ /^__/ }
63
+
64
+
65
+ # Creates a {Color} object from red, green, and blue values.
66
+ # @param red
67
+ # A number between 0 and 255 inclusive
68
+ # @param green
69
+ # A number between 0 and 255 inclusive
70
+ # @param blue
71
+ # A number between 0 and 255 inclusive
72
+ def rgb(red, green, blue)
73
+ [red.value, green.value, blue.value].each do |v|
74
+ next unless v < 0 || v > 255
75
+ raise ArgumentError.new("Color value #{v} must be between 0 and 255 inclusive")
76
+ end
77
+ Color.new([red.value, green.value, blue.value])
78
+ end
79
+
80
+ # Creates a {Color} object from hue, saturation, and lightness
81
+ # as per the CSS3 spec (http://www.w3.org/TR/css3-color/#hsl-color).
82
+ #
83
+ # @param hue [Number] The hue of the color.
84
+ # Should be between 0 and 360 degrees, inclusive
85
+ # @param saturation [Number] The saturation of the color.
86
+ # Must be between `0%` and `100%`, inclusive
87
+ # @param lightness [Number] The lightness of the color.
88
+ # Must be between `0%` and `100%`, inclusive
89
+ # @return [Color] The resulting color
90
+ # @raise [ArgumentError] if `saturation` or `lightness` are out of bounds
91
+ def hsl(hue, saturation, lightness)
92
+ original_s = saturation
93
+ original_l = lightness
94
+ # This algorithm is from http://www.w3.org/TR/css3-color#hsl-color
95
+ h, s, l = [hue, saturation, lightness].map { |a| a.value }
96
+ raise ArgumentError.new("Saturation #{s} must be between 0% and 100%") if s < 0 || s > 100
97
+ raise ArgumentError.new("Lightness #{l} must be between 0% and 100%") if l < 0 || l > 100
98
+
99
+ h = (h % 360) / 360.0
100
+ s /= 100.0
101
+ l /= 100.0
102
+
103
+ m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s
104
+ m1 = l * 2 - m2
105
+ Color.new([hue_to_rgb(m1, m2, h + 1.0/3),
106
+ hue_to_rgb(m1, m2, h),
107
+ hue_to_rgb(m1, m2, h - 1.0/3)].map { |c| (c * 0xff).round })
108
+ end
109
+
110
+ # Converts a decimal number to a percentage.
111
+ # For example:
112
+ #
113
+ # percentage(100px / 50px) => 200%
114
+ #
115
+ # @param value [Number] The decimal number to convert to a percentage
116
+ # @return [Number] The percentage
117
+ # @raise [ArgumentError] If `value` isn't a unitless number
118
+ def percentage(value)
119
+ unless value.is_a?(Sass::Script::Number) && value.unitless?
120
+ raise ArgumentError.new("#{value} is not a unitless number")
121
+ end
122
+ Sass::Script::Number.new(value.value * 100, ['%'])
123
+ end
124
+
125
+ # Rounds a number to the nearest whole number.
126
+ # For example:
127
+ #
128
+ # round(10.4px) => 10px
129
+ # round(10.6px) => 11px
130
+ #
131
+ # @param value [Number] The number
132
+ # @return [Number] The rounded number
133
+ # @raise [Sass::SyntaxError] if `value` isn't a number
134
+ def round(value)
135
+ numeric_transformation(value) {|n| n.round}
136
+ end
137
+
138
+ # Rounds a number up to the nearest whole number.
139
+ # For example:
140
+ #
141
+ # ciel(10.4px) => 11px
142
+ # ciel(10.6px) => 11px
143
+ #
144
+ # @param value [Number] The number
145
+ # @return [Number] The rounded number
146
+ # @raise [Sass::SyntaxError] if `value` isn't a number
147
+ def ceil(value)
148
+ numeric_transformation(value) {|n| n.ceil}
149
+ end
150
+
151
+ # Rounds down to the nearest whole number.
152
+ # For example:
153
+ #
154
+ # floor(10.4px) => 10px
155
+ # floor(10.6px) => 10px
156
+ #
157
+ # @param value [Number] The number
158
+ # @return [Number] The rounded number
159
+ # @raise [Sass::SyntaxError] if `value` isn't a number
160
+ def floor(value)
161
+ numeric_transformation(value) {|n| n.floor}
162
+ end
163
+
164
+ # Finds the absolute value of a number.
165
+ # For example:
166
+ #
167
+ # abs(10px) => 10px
168
+ # abs(-10px) => 10px
169
+ #
170
+ # @param value [Number] The number
171
+ # @return [Number] The absolute value
172
+ # @raise [Sass::SyntaxError] if `value` isn't a number
173
+ def abs(value)
174
+ numeric_transformation(value) {|n| n.abs}
175
+ end
176
+
177
+ private
178
+
179
+ # This method implements the pattern of transforming a numeric value into
180
+ # another numeric value with the same units.
181
+ # It yields a number to a block to perform the operation and return a number
182
+ def numeric_transformation(value)
183
+ unless value.is_a?(Sass::Script::Number)
184
+ calling_function = caller.first.scan(/`([^']+)'/).first.first
185
+ raise Sass::SyntaxError.new("#{value} is not a number for `#{calling_function}'")
186
+ end
187
+ Sass::Script::Number.new(yield(value.value), value.numerator_units, value.denominator_units)
188
+ end
189
+
190
+ def hue_to_rgb(m1, m2, h)
191
+ h += 1 if h < 0
192
+ h -= 1 if h > 1
193
+ return m1 + (m2 - m1) * h * 6 if h * 6 < 1
194
+ return m2 if h * 2 < 1
195
+ return m1 + (m2 - m1) * (2.0/3 - h) * 6 if h * 3 < 2
196
+ return m1
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,191 @@
1
+ require 'strscan'
2
+
3
+ module Sass
4
+ module Script
5
+ # The lexical analyzer for SassScript.
6
+ # It takes a raw string and converts it to individual tokens
7
+ # that are easier to parse.
8
+ class Lexer
9
+ # A struct containing information about an individual token.
10
+ #
11
+ # `type`: [{Symbol}]
12
+ # : The type of token.
13
+ #
14
+ # `value`: [{Object}]
15
+ # : The Ruby object corresponding to the value of the token.
16
+ #
17
+ # `line`: [{Fixnum}]
18
+ # : The line of the source file on which the token appears.
19
+ #
20
+ # `offset`: [{Fixnum}]
21
+ # : The number of bytes into the line the SassScript token appeared.
22
+ Token = Struct.new(:type, :value, :line, :offset)
23
+
24
+ # A hash from operator strings to the corresponding token types.
25
+ OPERATORS = {
26
+ '+' => :plus,
27
+ '-' => :minus,
28
+ '*' => :times,
29
+ '/' => :div,
30
+ '%' => :mod,
31
+ '=' => :single_eq,
32
+ '(' => :lparen,
33
+ ')' => :rparen,
34
+ ',' => :comma,
35
+ 'and' => :and,
36
+ 'or' => :or,
37
+ 'not' => :not,
38
+ '==' => :eq,
39
+ '!=' => :neq,
40
+ '>=' => :gte,
41
+ '<=' => :lte,
42
+ '>' => :gt,
43
+ '<' => :lt,
44
+ '#{' => :begin_interpolation,
45
+ '}' => :end_interpolation,
46
+ }
47
+
48
+ # A list of operator strings ordered with longer names first
49
+ # so that `>` and `<` don't clobber `>=` and `<=`.
50
+ OP_NAMES = OPERATORS.keys.sort_by {|o| -o.size}
51
+
52
+ # A hash of regular expressions that are used for tokenizing.
53
+ REGULAR_EXPRESSIONS = {
54
+ :whitespace => /\s*/,
55
+ :variable => /!(\w+)/,
56
+ :ident => /(\\.|\#\{|[^\s\\+\-*\/%(),=!])+/,
57
+ :string_end => /((?:\\.|\#[^{]|[^"\\#])*)(?:"|(?=#\{))/,
58
+ :number => /(-)?(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
59
+ :color => /\##{"([0-9a-fA-F]{1,2})" * 3}|(#{Color::HTML4_COLORS.keys.join("|")})/,
60
+ :bool => /(true|false)\b/,
61
+ :op => %r{(#{Regexp.union(*OP_NAMES.map{|s| Regexp.new(Regexp.escape(s) + (s =~ /\w$/ ? '(?:\b|$)' : ''))})})}
62
+ }
63
+
64
+ # @param str [String, StringScanner] The source text to lex
65
+ # @param line [Fixnum] The line on which the SassScript appears.
66
+ # Used for error reporting
67
+ # @param offset [Fixnum] The number of characters in on which the SassScript appears.
68
+ # Used for error reporting
69
+ def initialize(str, line, offset, filename)
70
+ @scanner = str.is_a?(StringScanner) ? str : StringScanner.new(str)
71
+ @line = line
72
+ @offset = offset
73
+ @filename = filename
74
+ @prev = nil
75
+ end
76
+
77
+ # Moves the lexer forward one token.
78
+ #
79
+ # @return [Token] The token that was moved past
80
+ def next
81
+ @tok ||= read_token
82
+ @tok, tok = nil, @tok
83
+ @prev = tok
84
+ return tok
85
+ end
86
+
87
+ # Returns the next token without moving the lexer forward.
88
+ #
89
+ # @return [Token] The next token
90
+ def peek
91
+ @tok ||= read_token
92
+ end
93
+
94
+ # @return [Boolean] Whether or not there's more source text to lex.
95
+ def done?
96
+ whitespace unless after_interpolation?
97
+ @scanner.eos? && @tok.nil?
98
+ end
99
+
100
+ private
101
+
102
+ def read_token
103
+ return if done?
104
+
105
+ value = token
106
+ unless value
107
+ raise SyntaxError.new("Syntax error in '#{@scanner.string}' at character #{current_position}.")
108
+ end
109
+ Token.new(value.first, value.last, @line, last_match_position)
110
+ end
111
+
112
+ def whitespace
113
+ @scanner.scan(REGULAR_EXPRESSIONS[:whitespace])
114
+ end
115
+
116
+ def token
117
+ return string('') if after_interpolation?
118
+ variable || string || number || color || bool || op || ident
119
+ end
120
+
121
+ def variable
122
+ return unless @scanner.scan(REGULAR_EXPRESSIONS[:variable])
123
+ [:const, @scanner[1]]
124
+ end
125
+
126
+ def ident
127
+ return unless s = @scanner.scan(REGULAR_EXPRESSIONS[:ident])
128
+ [:ident, s.gsub(/\\(.)/, '\1')]
129
+ end
130
+
131
+ def string(start_char = '"')
132
+ return unless @scanner.scan(/#{start_char}#{REGULAR_EXPRESSIONS[:string_end]}/)
133
+ [:string, Script::String.new(@scanner[1].gsub(/\\([^0-9a-f])/, '\1').gsub(/\\([0-9a-f]{1,4})/, "\\\\\\1"))]
134
+ end
135
+
136
+ def begin_interpolation
137
+ @scanner.scan
138
+ end
139
+
140
+ def number
141
+ return unless @scanner.scan(REGULAR_EXPRESSIONS[:number])
142
+ value = @scanner[2] ? @scanner[2].to_f : @scanner[3].to_i
143
+ value = -value if @scanner[1]
144
+ [:number, Script::Number.new(value, Array(@scanner[4]))]
145
+ end
146
+
147
+ def color
148
+ return unless @scanner.scan(REGULAR_EXPRESSIONS[:color])
149
+ value = if @scanner[4]
150
+ color = Color::HTML4_COLORS[@scanner[4].downcase]
151
+ else
152
+ (1..3).map {|i| @scanner[i]}.map {|num| num.ljust(2, num).to_i(16)}
153
+ end
154
+ [:color, Script::Color.new(value)]
155
+ end
156
+
157
+ def bool
158
+ return unless s = @scanner.scan(REGULAR_EXPRESSIONS[:bool])
159
+ [:bool, Script::Bool.new(s == 'true')]
160
+ end
161
+
162
+ def op
163
+ prev_chr = @scanner.string[@scanner.pos - 1].chr
164
+ return unless op = @scanner.scan(REGULAR_EXPRESSIONS[:op])
165
+ if @prev && op == '-' && prev_chr !~ /\s/ &&
166
+ [:bool, :ident, :const].include?(@prev.type)
167
+ warn(<<END)
168
+ DEPRECATION WARNING:
169
+ On line #{@line}, character #{last_match_position}#{" of '#{@filename}'" if @filename}
170
+ - will be allowed as part of variable names in version 2.4.
171
+ Please add whitespace to separate it from the previous token.
172
+ END
173
+ end
174
+
175
+ [OPERATORS[op]]
176
+ end
177
+
178
+ def current_position
179
+ @offset + @scanner.pos + 1
180
+ end
181
+
182
+ def last_match_position
183
+ current_position - @scanner.matched_size
184
+ end
185
+
186
+ def after_interpolation?
187
+ @prev && @prev.type == :end_interpolation
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,177 @@
1
+ module Sass::Script
2
+ # The abstract superclass for SassScript objects.
3
+ #
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
+ class Literal < Node
8
+ require 'sass/script/string'
9
+ require 'sass/script/number'
10
+ require 'sass/script/color'
11
+ require 'sass/script/bool'
12
+
13
+ # Returns the Ruby value of the literal.
14
+ # The type of this value varies based on the subclass.
15
+ #
16
+ # @return [Object]
17
+ attr_reader :value
18
+
19
+ # Creates a new literal.
20
+ #
21
+ # @param value [Object] The object for \{#value}
22
+ def initialize(value = nil)
23
+ @value = value
24
+ end
25
+
26
+ # Evaluates the literal.
27
+ #
28
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
29
+ # @return [Literal] This literal
30
+ def perform(environment)
31
+ self
32
+ end
33
+
34
+ # The SassScript `and` operation.
35
+ #
36
+ # @param other [Literal] The right-hand side of the operator
37
+ # @return [Literal] The result of a logical and:
38
+ # `other` if this literal isn't a false {Bool},
39
+ # and this literal otherwise
40
+ def and(other)
41
+ to_bool ? other : self
42
+ end
43
+
44
+ # The SassScript `or` operation.
45
+ #
46
+ # @param other [Literal] The right-hand side of the operator
47
+ # @return [Literal] The result of the logical or:
48
+ # this literal if it isn't a false {Bool},
49
+ # and `other` otherwise
50
+ def or(other)
51
+ to_bool ? self : other
52
+ end
53
+
54
+ # The SassScript `==` operation.
55
+ # **Note that this returns a {Sass::Script::Bool} object,
56
+ # not a Ruby boolean**.
57
+ #
58
+ # @param other [Literal] The right-hand side of the operator
59
+ # @return [Bool] True if this literal is the same as the other,
60
+ # false otherwise
61
+ def eq(other)
62
+ Sass::Script::Bool.new(self.class == other.class && self.value == other.value)
63
+ end
64
+
65
+ # The SassScript `!=` operation.
66
+ # **Note that this returns a {Sass::Script::Bool} object,
67
+ # not a Ruby boolean**.
68
+ #
69
+ # @param other [Literal] The right-hand side of the operator
70
+ # @return [Bool] False if this literal is the same as the other,
71
+ # true otherwise
72
+ def neq(other)
73
+ Sass::Script::Bool.new(!eq(other).to_bool)
74
+ end
75
+
76
+ # The SassScript `==` operation.
77
+ # **Note that this returns a {Sass::Script::Bool} object,
78
+ # not a Ruby boolean**.
79
+ #
80
+ # @param other [Literal] The right-hand side of the operator
81
+ # @return [Bool] True if this literal is the same as the other,
82
+ # false otherwise
83
+ def unary_not
84
+ Sass::Script::Bool.new(!to_bool)
85
+ end
86
+
87
+ # The SassScript default operation (e.g. `!a !b`, `"foo" "bar"`).
88
+ #
89
+ # @param other [Literal] The right-hand side of the operator
90
+ # @return [Script::String] A string containing both literals
91
+ # separated by a space
92
+ def concat(other)
93
+ Sass::Script::String.new("#{self.to_s} #{other.to_s}")
94
+ end
95
+
96
+ # The SassScript `,` operation (e.g. `!a, !b`, `"foo", "bar"`).
97
+ #
98
+ # @param other [Literal] The right-hand side of the operator
99
+ # @return [Script::String] A string containing both literals
100
+ # separated by `", "`
101
+ def comma(other)
102
+ Sass::Script::String.new("#{self.to_s}, #{other.to_s}")
103
+ end
104
+
105
+ # The SassScript `+` operation.
106
+ #
107
+ # @param other [Literal] The right-hand side of the operator
108
+ # @return [Script::String] A string containing both literals
109
+ # without any separation
110
+ def plus(other)
111
+ Sass::Script::String.new(self.to_s + other.to_s)
112
+ end
113
+
114
+ # The SassScript `-` operation.
115
+ #
116
+ # @param other [Literal] The right-hand side of the operator
117
+ # @return [Script::String] A string containing both literals
118
+ # separated by `"-"`
119
+ def minus(other)
120
+ Sass::Script::String.new("#{self.to_s}-#{other.to_s}")
121
+ end
122
+
123
+ # The SassScript `/` operation.
124
+ #
125
+ # @param other [Literal] The right-hand side of the operator
126
+ # @return [Script::String] A string containing both literals
127
+ # separated by `"/"`
128
+ def div(other)
129
+ Sass::Script::String.new("#{self.to_s}/#{other.to_s}")
130
+ end
131
+
132
+ # The SassScript unary `-` operation (e.g. `-!a`).
133
+ #
134
+ # @param other [Literal] The right-hand side of the operator
135
+ # @return [Script::String] A string containing the literal
136
+ # preceded by `"-"`
137
+ def unary_minus
138
+ Sass::Script::String.new("-#{self.to_s}")
139
+ end
140
+
141
+ # The SassScript unary `/` operation (e.g. `/!a`).
142
+ #
143
+ # @param other [Literal] The right-hand side of the operator
144
+ # @return [Script::String] A string containing the literal
145
+ # preceded by `"/"`
146
+ def unary_div
147
+ Sass::Script::String.new("/#{self.to_s}")
148
+ end
149
+
150
+ # @return [String] A readable representation of the literal
151
+ def inspect
152
+ value.inspect
153
+ end
154
+
155
+ # @return [Boolean] `true` (the Ruby boolean value)
156
+ def to_bool
157
+ true
158
+ end
159
+
160
+ # Compares this object with another.
161
+ #
162
+ # @param other [Object] The object to compare with
163
+ # @return [Boolean] Whether or not this literal is equivalent to `other`
164
+ def ==(other)
165
+ eq(other).to_bool
166
+ end
167
+
168
+ # @return [Fixnum] The integer value of this literal
169
+ # @raise [Sass::SyntaxError] if this literal isn't an integer
170
+ def to_i
171
+ raise Sass::SyntaxError.new("#{self.inspect} is not an integer.")
172
+ end
173
+
174
+ # @raise [Sass::SyntaxError] if this literal isn't an integer
175
+ def assert_int!; to_i; end
176
+ end
177
+ end