drnic-haml 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
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