haml 2.2.24 → 3.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of haml might be problematic. Click here for more details.

Files changed (168) hide show
  1. data/.yardopts +0 -1
  2. data/README.md +91 -151
  3. data/REMEMBER +11 -1
  4. data/Rakefile +73 -55
  5. data/VERSION +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/bin/css2sass +7 -1
  8. data/bin/sass-convert +7 -0
  9. data/extra/haml-mode.el +2 -1
  10. data/lib/haml/buffer.rb +22 -4
  11. data/lib/haml/engine.rb +5 -1
  12. data/lib/haml/exec.rb +231 -46
  13. data/lib/haml/filters.rb +19 -8
  14. data/lib/haml/helpers.rb +47 -20
  15. data/lib/haml/helpers/action_view_extensions.rb +2 -4
  16. data/lib/haml/helpers/action_view_mods.rb +11 -8
  17. data/lib/haml/helpers/xss_mods.rb +13 -2
  18. data/lib/haml/html.rb +179 -48
  19. data/lib/haml/html/erb.rb +141 -0
  20. data/lib/haml/precompiler.rb +40 -15
  21. data/lib/haml/railtie.rb +1 -5
  22. data/lib/haml/root.rb +3 -0
  23. data/lib/haml/template.rb +4 -14
  24. data/lib/haml/util.rb +120 -30
  25. data/lib/haml/version.rb +25 -2
  26. data/lib/sass.rb +5 -1
  27. data/lib/sass/callbacks.rb +50 -0
  28. data/lib/sass/css.rb +40 -191
  29. data/lib/sass/engine.rb +170 -74
  30. data/lib/sass/environment.rb +8 -2
  31. data/lib/sass/error.rb +163 -25
  32. data/lib/sass/files.rb +31 -28
  33. data/lib/sass/plugin.rb +268 -87
  34. data/lib/sass/plugin/rails.rb +9 -4
  35. data/lib/sass/repl.rb +1 -1
  36. data/lib/sass/script.rb +31 -29
  37. data/lib/sass/script/bool.rb +1 -0
  38. data/lib/sass/script/color.rb +290 -23
  39. data/lib/sass/script/css_lexer.rb +22 -0
  40. data/lib/sass/script/css_parser.rb +28 -0
  41. data/lib/sass/script/funcall.rb +22 -3
  42. data/lib/sass/script/functions.rb +523 -33
  43. data/lib/sass/script/interpolation.rb +42 -0
  44. data/lib/sass/script/lexer.rb +169 -52
  45. data/lib/sass/script/literal.rb +58 -9
  46. data/lib/sass/script/node.rb +79 -1
  47. data/lib/sass/script/number.rb +20 -5
  48. data/lib/sass/script/operation.rb +49 -3
  49. data/lib/sass/script/parser.rb +162 -28
  50. data/lib/sass/script/string.rb +50 -2
  51. data/lib/sass/script/unary_operation.rb +25 -2
  52. data/lib/sass/script/variable.rb +21 -4
  53. data/lib/sass/scss.rb +14 -0
  54. data/lib/sass/scss/css_parser.rb +39 -0
  55. data/lib/sass/scss/parser.rb +683 -0
  56. data/lib/sass/scss/rx.rb +112 -0
  57. data/lib/sass/scss/script_lexer.rb +13 -0
  58. data/lib/sass/scss/script_parser.rb +25 -0
  59. data/lib/sass/tree/comment_node.rb +69 -27
  60. data/lib/sass/tree/debug_node.rb +7 -2
  61. data/lib/sass/tree/directive_node.rb +41 -35
  62. data/lib/sass/tree/for_node.rb +6 -0
  63. data/lib/sass/tree/if_node.rb +13 -1
  64. data/lib/sass/tree/import_node.rb +52 -27
  65. data/lib/sass/tree/mixin_def_node.rb +18 -0
  66. data/lib/sass/tree/mixin_node.rb +41 -6
  67. data/lib/sass/tree/node.rb +197 -70
  68. data/lib/sass/tree/prop_node.rb +152 -57
  69. data/lib/sass/tree/root_node.rb +118 -0
  70. data/lib/sass/tree/rule_node.rb +193 -96
  71. data/lib/sass/tree/variable_node.rb +9 -5
  72. data/lib/sass/tree/while_node.rb +4 -0
  73. data/test/benchmark.rb +5 -5
  74. data/test/haml/engine_test.rb +147 -10
  75. data/test/haml/{rhtml/_av_partial_1.rhtml → erb/_av_partial_1.erb} +1 -1
  76. data/test/haml/{rhtml/_av_partial_2.rhtml → erb/_av_partial_2.erb} +1 -1
  77. data/test/haml/{rhtml/action_view.rhtml → erb/action_view.erb} +1 -1
  78. data/test/haml/{rhtml/standard.rhtml → erb/standard.erb} +0 -0
  79. data/test/haml/helper_test.rb +91 -24
  80. data/test/haml/html2haml/erb_tests.rb +410 -0
  81. data/test/haml/html2haml_test.rb +210 -66
  82. data/test/haml/results/filters.xhtml +1 -1
  83. data/test/haml/results/just_stuff.xhtml +2 -0
  84. data/test/haml/spec_test.rb +44 -0
  85. data/test/haml/template_test.rb +22 -2
  86. data/test/haml/templates/helpers.haml +0 -13
  87. data/test/haml/templates/just_stuff.haml +2 -0
  88. data/test/haml/util_test.rb +48 -0
  89. data/test/sass/callbacks_test.rb +61 -0
  90. data/test/sass/conversion_test.rb +884 -0
  91. data/test/sass/css2sass_test.rb +99 -18
  92. data/test/sass/data/hsl-rgb.txt +319 -0
  93. data/test/sass/engine_test.rb +1049 -131
  94. data/test/sass/functions_test.rb +398 -47
  95. data/test/sass/more_results/more_import.css +1 -1
  96. data/test/sass/more_templates/more_import.sass +3 -3
  97. data/test/sass/plugin_test.rb +184 -10
  98. data/test/sass/results/compact.css +1 -1
  99. data/test/sass/results/complex.css +5 -5
  100. data/test/sass/results/compressed.css +1 -1
  101. data/test/sass/results/expanded.css +1 -1
  102. data/test/sass/results/import.css +3 -1
  103. data/test/sass/results/mixins.css +12 -12
  104. data/test/sass/results/nested.css +1 -1
  105. data/test/sass/results/options.css +1 -0
  106. data/test/sass/results/parent_ref.css +4 -4
  107. data/test/sass/results/script.css +3 -3
  108. data/test/sass/results/scss_import.css +15 -0
  109. data/test/sass/results/scss_importee.css +2 -0
  110. data/test/sass/script_conversion_test.rb +153 -0
  111. data/test/sass/script_test.rb +137 -70
  112. data/test/sass/scss/css_test.rb +811 -0
  113. data/test/sass/scss/rx_test.rb +156 -0
  114. data/test/sass/scss/scss_test.rb +871 -0
  115. data/test/sass/scss/test_helper.rb +37 -0
  116. data/test/sass/templates/alt.sass +2 -2
  117. data/test/sass/templates/bork1.sass +2 -0
  118. data/test/sass/templates/bork3.sass +2 -0
  119. data/test/sass/templates/bork4.sass +2 -0
  120. data/test/sass/templates/import.sass +4 -4
  121. data/test/sass/templates/importee.sass +3 -3
  122. data/test/sass/templates/line_numbers.sass +1 -1
  123. data/test/sass/templates/mixin_bork.sass +5 -0
  124. data/test/sass/templates/mixins.sass +2 -2
  125. data/test/sass/templates/nested_bork1.sass +2 -0
  126. data/test/sass/templates/nested_bork2.sass +2 -0
  127. data/test/sass/templates/nested_bork3.sass +2 -0
  128. data/test/sass/templates/nested_bork4.sass +2 -0
  129. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  130. data/test/sass/templates/options.sass +2 -0
  131. data/test/sass/templates/parent_ref.sass +2 -2
  132. data/test/sass/templates/script.sass +69 -69
  133. data/test/sass/templates/scss_import.scss +10 -0
  134. data/test/sass/templates/scss_importee.scss +1 -0
  135. data/test/sass/templates/units.sass +10 -10
  136. data/test/test_helper.rb +20 -8
  137. data/vendor/fssm/LICENSE +20 -0
  138. data/vendor/fssm/README.markdown +55 -0
  139. data/vendor/fssm/Rakefile +59 -0
  140. data/vendor/fssm/VERSION.yml +5 -0
  141. data/vendor/fssm/example.rb +9 -0
  142. data/vendor/fssm/fssm.gemspec +77 -0
  143. data/vendor/fssm/lib/fssm.rb +33 -0
  144. data/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
  145. data/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
  146. data/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
  147. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
  148. data/vendor/fssm/lib/fssm/monitor.rb +26 -0
  149. data/vendor/fssm/lib/fssm/path.rb +91 -0
  150. data/vendor/fssm/lib/fssm/pathname.rb +502 -0
  151. data/vendor/fssm/lib/fssm/state/directory.rb +57 -0
  152. data/vendor/fssm/lib/fssm/state/file.rb +24 -0
  153. data/vendor/fssm/lib/fssm/support.rb +63 -0
  154. data/vendor/fssm/lib/fssm/tree.rb +176 -0
  155. data/vendor/fssm/profile/prof-cache.rb +40 -0
  156. data/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
  157. data/vendor/fssm/profile/prof-pathname.rb +68 -0
  158. data/vendor/fssm/profile/prof-plain-pathname.html +988 -0
  159. data/vendor/fssm/profile/prof.html +2379 -0
  160. data/vendor/fssm/spec/path_spec.rb +75 -0
  161. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  162. data/vendor/fssm/spec/root/file.css +0 -0
  163. data/vendor/fssm/spec/root/file.rb +0 -0
  164. data/vendor/fssm/spec/root/file.yml +0 -0
  165. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  166. data/vendor/fssm/spec/spec_helper.rb +14 -0
  167. metadata +94 -14
  168. data/test/sass/templates/bork.sass +0 -2
@@ -0,0 +1,42 @@
1
+ module Sass::Script
2
+ class Interpolation < Node
3
+ def initialize(before, mid, after, wb, wa)
4
+ @before = before
5
+ @mid = mid
6
+ @after = after
7
+ @whitespace_before = wb
8
+ @whitespace_after = wa
9
+ end
10
+
11
+ def inspect
12
+ "(interpolation #{@before.inspect} #{@mid.inspect} #{after.inspect})"
13
+ end
14
+
15
+ def to_sass
16
+ res = ""
17
+ res << @before.to_sass if @before
18
+ res << ' ' if @before && @whitespace_before
19
+ res << '#{' << @mid.to_sass << '}'
20
+ res << ' ' if @after && @whitespace_after
21
+ res << @after.to_sass if @after
22
+ res
23
+ end
24
+
25
+ def children
26
+ [@before, @mid, @after].compact
27
+ end
28
+
29
+ protected
30
+
31
+ def _perform(environment)
32
+ res = ""
33
+ res << @before.perform(environment).to_s if @before
34
+ res << " " if @before && @whitespace_before
35
+ val = @mid.perform(environment)
36
+ res << (val.is_a?(Sass::Script::String) ? val.value : val.to_s)
37
+ res << " " if @after && @whitespace_after
38
+ res << @after.perform(environment).to_s if @after
39
+ Sass::Script::String.new(res)
40
+ end
41
+ end
42
+ end
@@ -1,3 +1,5 @@
1
+ require 'sass/scss/rx'
2
+
1
3
  require 'strscan'
2
4
 
3
5
  module Sass
@@ -6,6 +8,8 @@ module Sass
6
8
  # It takes a raw string and converts it to individual tokens
7
9
  # that are easier to parse.
8
10
  class Lexer
11
+ include Sass::SCSS::RX
12
+
9
13
  # A struct containing information about an individual token.
10
14
  #
11
15
  # `type`: \[`Symbol`\]
@@ -19,7 +23,21 @@ module Sass
19
23
  #
20
24
  # `offset`: \[`Fixnum`\]
21
25
  # : The number of bytes into the line the SassScript token appeared.
22
- Token = Struct.new(:type, :value, :line, :offset)
26
+ #
27
+ # `pos`: \[`Fixnum`\]
28
+ # : The scanner position at which the SassScript token appeared.
29
+ Token = Struct.new(:type, :value, :line, :offset, :pos)
30
+
31
+ # The line number of the lexer's current position.
32
+ #
33
+ # @return [Fixnum]
34
+ attr_reader :line
35
+
36
+ # The number of bytes into the current line
37
+ # of the lexer's current position.
38
+ #
39
+ # @return [Fixnum]
40
+ attr_reader :offset
23
41
 
24
42
  # A hash from operator strings to the corresponding token types.
25
43
  # @private
@@ -30,6 +48,7 @@ module Sass
30
48
  '/' => :div,
31
49
  '%' => :mod,
32
50
  '=' => :single_eq,
51
+ ':' => :colon,
33
52
  '(' => :lparen,
34
53
  ')' => :rparen,
35
54
  ',' => :comma,
@@ -44,24 +63,63 @@ module Sass
44
63
  '<' => :lt,
45
64
  '#{' => :begin_interpolation,
46
65
  '}' => :end_interpolation,
66
+ ';' => :semicolon,
67
+ '{' => :lcurly,
47
68
  }
48
69
 
70
+ # @private
71
+ OPERATORS_REVERSE = Haml::Util.map_hash(OPERATORS) {|k, v| [v, k]}
72
+
73
+ # @private
74
+ TOKEN_NAMES = Haml::Util.map_hash(OPERATORS_REVERSE) {|k, v| [k, v.inspect]}.merge({
75
+ :const => "variable (e.g. $foo)",
76
+ :ident => "identifier (e.g. middle)",
77
+ :bool => "boolean (e.g. true, false)",
78
+ })
79
+
49
80
  # A list of operator strings ordered with longer names first
50
81
  # so that `>` and `<` don't clobber `>=` and `<=`.
51
82
  # @private
52
83
  OP_NAMES = OPERATORS.keys.sort_by {|o| -o.size}
53
84
 
85
+ # A sub-list of {OP_NAMES} that only includes operators
86
+ # with identifier names.
87
+ # @private
88
+ IDENT_OP_NAMES = OP_NAMES.select {|k, v| k =~ /^\w+/}
89
+
54
90
  # A hash of regular expressions that are used for tokenizing.
55
91
  # @private
56
92
  REGULAR_EXPRESSIONS = {
57
- :whitespace => /\s*/,
58
- :variable => /!(\w+)/,
59
- :ident => /(\\.|[^\s\\+\-*\/%(),=!])+/,
60
- :string_end => /((?:\\.|\#(?!\{)|[^"\\#])*)(?:"|(?=#\{))/,
93
+ :whitespace => /\s+/,
94
+ :comment => COMMENT,
95
+ :single_line_comment => SINGLE_LINE_COMMENT,
96
+ :variable => /([!\$])(#{IDENT})/,
97
+ :ident => IDENT,
61
98
  :number => /(-)?(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
62
- :color => /\##{"([0-9a-fA-F]{1,2})" * 3}|(#{Color::HTML4_COLORS.keys.join("|")})/,
99
+ :color => HEXCOLOR,
63
100
  :bool => /(true|false)\b/,
64
- :op => %r{(#{Regexp.union(*OP_NAMES.map{|s| Regexp.new(Regexp.escape(s) + (s =~ /\w$/ ? '(?:\b|$)' : ''))})})}
101
+ :ident_op => %r{(#{Regexp.union(*IDENT_OP_NAMES.map{|s| Regexp.new(Regexp.escape(s) + '(?:\b|$)')})})},
102
+ :op => %r{(#{Regexp.union(*OP_NAMES)})},
103
+ }
104
+
105
+ class << self
106
+ private
107
+ def string_re(open, close)
108
+ /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|(?=#\{))/
109
+ end
110
+ end
111
+
112
+ # A hash of regular expressions that are used for tokenizing strings.
113
+ #
114
+ # The key is a [Symbol, Boolean] pair.
115
+ # The symbol represents which style of quotation to use,
116
+ # while the boolean represents whether or not the string
117
+ # is following an interpolated segment.
118
+ STRING_REGULAR_EXPRESSIONS = {
119
+ [:double, false] => string_re('"', '"'),
120
+ [:single, false] => string_re("'", "'"),
121
+ [:double, true] => string_re('', '"'),
122
+ [:single, true] => string_re('', "'"),
65
123
  }
66
124
 
67
125
  # @param str [String, StringScanner] The source text to lex
@@ -69,11 +127,14 @@ module Sass
69
127
  # Used for error reporting
70
128
  # @param offset [Fixnum] The number of characters in on which the SassScript appears.
71
129
  # Used for error reporting
72
- def initialize(str, line, offset, filename)
130
+ # @param options [{Symbol => Object}] An options hash;
131
+ # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
132
+ def initialize(str, line, offset, options)
73
133
  @scanner = str.is_a?(StringScanner) ? str : StringScanner.new(str)
74
134
  @line = line
75
135
  @offset = offset
76
- @filename = filename
136
+ @options = options
137
+ @interpolation_stack = []
77
138
  @prev = nil
78
139
  end
79
140
 
@@ -87,6 +148,18 @@ module Sass
87
148
  return tok
88
149
  end
89
150
 
151
+ # Returns whether or not there's whitespace before the next token.
152
+ #
153
+ # @return [Boolean]
154
+ def whitespace?(tok = @tok)
155
+ if tok
156
+ @scanner.string[0...tok.pos] =~ /\s$/
157
+ else
158
+ @scanner.string[@scanner.pos, 1] =~ /^\s/ ||
159
+ @scanner.string[@scanner.pos - 1, 1] =~ /\s$/
160
+ end
161
+ end
162
+
90
163
  # Returns the next token without moving the lexer forward.
91
164
  #
92
165
  # @return [Token] The next token
@@ -94,96 +167,140 @@ module Sass
94
167
  @tok ||= read_token
95
168
  end
96
169
 
170
+ # Rewinds the underlying StringScanner
171
+ # to before the token returned by \{#peek}.
172
+ def unpeek!
173
+ @scanner.pos = @tok.pos if @tok
174
+ end
175
+
97
176
  # @return [Boolean] Whether or not there's more source text to lex.
98
177
  def done?
99
- whitespace unless after_interpolation?
178
+ whitespace unless after_interpolation? && @interpolation_stack.last
100
179
  @scanner.eos? && @tok.nil?
101
180
  end
102
181
 
182
+ def expected!(name)
183
+ unpeek!
184
+ Sass::SCSS::Parser.expected(@scanner, name, @line)
185
+ end
186
+
187
+ def str
188
+ old_pos = @tok ? @tok.pos : @scanner.pos
189
+ yield
190
+ new_pos = @tok ? @tok.pos : @scanner.pos
191
+ @scanner.string[old_pos...new_pos]
192
+ end
193
+
103
194
  private
104
195
 
105
196
  def read_token
106
197
  return if done?
198
+ return unless value = token
107
199
 
108
- value = token
109
- unless value
110
- raise SyntaxError.new("Syntax error in '#{@scanner.string}' at character #{current_position}.")
111
- end
112
- Token.new(value.first, value.last, @line, last_match_position)
200
+ value.last.line = @line if value.last.is_a?(Script::Node)
201
+ Token.new(value.first, value.last, @line,
202
+ current_position - @scanner.matched_size,
203
+ @scanner.pos - @scanner.matched_size)
113
204
  end
114
205
 
115
206
  def whitespace
116
- @scanner.scan(REGULAR_EXPRESSIONS[:whitespace])
207
+ nil while scan(REGULAR_EXPRESSIONS[:whitespace]) ||
208
+ scan(REGULAR_EXPRESSIONS[:comment]) ||
209
+ scan(REGULAR_EXPRESSIONS[:single_line_comment])
117
210
  end
118
211
 
119
212
  def token
120
- return string('') if after_interpolation?
121
- variable || string || number || color || bool || op || ident
213
+ if after_interpolation? && (interp_type = @interpolation_stack.pop)
214
+ return string(interp_type, true)
215
+ end
216
+
217
+ variable || string(:double, false) || string(:single, false) || number ||
218
+ color || bool || raw(URI) || raw(UNICODERANGE) || special_fun ||
219
+ ident_op || ident || op
122
220
  end
123
221
 
124
222
  def variable
125
- return unless @scanner.scan(REGULAR_EXPRESSIONS[:variable])
126
- [:const, @scanner[1]]
223
+ _variable(REGULAR_EXPRESSIONS[:variable])
127
224
  end
128
225
 
129
- def ident
130
- return unless s = @scanner.scan(REGULAR_EXPRESSIONS[:ident])
131
- [:ident, s.gsub(/\\(.)/, '\1')]
226
+ def _variable(rx)
227
+ line = @line
228
+ offset = @offset
229
+ return unless scan(rx)
230
+ if @scanner[1] == '!' && @scanner[2] != 'important'
231
+ Script.var_warning(@scanner[2], line, offset + 1, @options[:filename])
232
+ end
233
+
234
+ [:const, @scanner[2]]
132
235
  end
133
236
 
134
- def string(start_char = '"')
135
- return unless @scanner.scan(/#{start_char}#{REGULAR_EXPRESSIONS[:string_end]}/)
136
- [:string, Script::String.new(@scanner[1].gsub(/\\([^0-9a-f])/, '\1').gsub(/\\([0-9a-f]{1,4})/, "\\\\\\1"))]
237
+ def ident
238
+ return unless s = scan(REGULAR_EXPRESSIONS[:ident])
239
+ [:ident, s.gsub(/\\(.)/, '\1')]
137
240
  end
138
241
 
139
- def begin_interpolation
140
- @scanner.scan
242
+ def string(re, open)
243
+ return unless scan(STRING_REGULAR_EXPRESSIONS[[re, open]])
244
+ @interpolation_stack << re if @scanner[2].empty? # Started an interpolated section
245
+ [:string, Script::String.new(@scanner[1].gsub(/\\([^0-9a-f])/, '\1').gsub(/\\([0-9a-f]{1,4})/, "\\\\\\1"), :string)]
141
246
  end
142
247
 
143
248
  def number
144
- return unless @scanner.scan(REGULAR_EXPRESSIONS[:number])
249
+ return unless scan(REGULAR_EXPRESSIONS[:number])
145
250
  value = @scanner[2] ? @scanner[2].to_f : @scanner[3].to_i
146
251
  value = -value if @scanner[1]
147
252
  [:number, Script::Number.new(value, Array(@scanner[4]))]
148
253
  end
149
254
 
150
255
  def color
151
- return unless @scanner.scan(REGULAR_EXPRESSIONS[:color])
152
- value = if @scanner[4]
153
- color = Color::HTML4_COLORS[@scanner[4].downcase]
154
- else
155
- (1..3).map {|i| @scanner[i]}.map {|num| num.ljust(2, num).to_i(16)}
156
- end
256
+ return unless s = scan(REGULAR_EXPRESSIONS[:color])
257
+ value = s.scan(/^#(..?)(..?)(..?)$/).first.
258
+ map {|num| num.ljust(2, num).to_i(16)}
157
259
  [:color, Script::Color.new(value)]
158
260
  end
159
261
 
160
262
  def bool
161
- return unless s = @scanner.scan(REGULAR_EXPRESSIONS[:bool])
263
+ return unless s = scan(REGULAR_EXPRESSIONS[:bool])
162
264
  [:bool, Script::Bool.new(s == 'true')]
163
265
  end
164
266
 
165
- def op
166
- prev_chr = @scanner.string[@scanner.pos - 1].chr
167
- return unless op = @scanner.scan(REGULAR_EXPRESSIONS[:op])
168
- if @prev && op == '-' && prev_chr !~ /\s/ &&
169
- [:bool, :ident, :const].include?(@prev.type)
170
- warn(<<END)
171
- DEPRECATION WARNING:
172
- On line #{@line}, character #{last_match_position}#{" of '#{@filename}'" if @filename}
173
- - will be allowed as part of variable names in version 3.0.
174
- Please add whitespace to separate it from the previous token.
175
- END
176
- end
267
+ def special_fun
268
+ return unless str1 = scan(/(calc|expression|progid:[a-z\.]*)\(/i)
269
+ str2, _ = Haml::Shared.balance(@scanner, ?(, ?), 1)
270
+ c = str2.count("\n")
271
+ @line += c
272
+ @offset = (c == 0 ? @offset + str2.size : str2[/\n(.*)/, 1].size)
273
+ [:special_fun,
274
+ Haml::Util.merge_adjacent_strings(
275
+ [str1] + Sass::Engine.parse_interp(str2, @line, @options))]
276
+ end
177
277
 
278
+ def ident_op
279
+ return unless op = scan(REGULAR_EXPRESSIONS[:ident_op])
178
280
  [OPERATORS[op]]
179
281
  end
180
282
 
181
- def current_position
182
- @offset + @scanner.pos + 1
283
+ def op
284
+ return unless op = scan(REGULAR_EXPRESSIONS[:op])
285
+ @interpolation_stack << nil if op == :begin_interpolation
286
+ [OPERATORS[op]]
287
+ end
288
+
289
+ def raw(rx)
290
+ return unless val = scan(rx)
291
+ [:raw, val]
183
292
  end
184
293
 
185
- def last_match_position
186
- current_position - @scanner.matched_size
294
+ def scan(re)
295
+ return unless str = @scanner.scan(re)
296
+ c = str.count("\n")
297
+ @line += c
298
+ @offset = (c == 0 ? @offset + str.size : str[/\n(.*)/, 1].size)
299
+ str
300
+ end
301
+
302
+ def current_position
303
+ @offset + 1
187
304
  end
188
305
 
189
306
  def after_interpolation?
@@ -21,14 +21,32 @@ module Sass::Script
21
21
  # @param value [Object] The object for \{#value}
22
22
  def initialize(value = nil)
23
23
  @value = value
24
+ super()
24
25
  end
25
26
 
26
- # Evaluates the literal.
27
+ # Returns an empty array.
27
28
  #
28
- # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
29
- # @return [Literal] This literal
30
- def perform(environment)
31
- self
29
+ # @return [Array<Node>] empty
30
+ # @see Node#children
31
+ def children
32
+ []
33
+ end
34
+
35
+ # Returns the options hash for this node.
36
+ #
37
+ # @return [{Symbol => Object}]
38
+ # @raise [Sass::SyntaxError] if the options hash hasn't been set.
39
+ # This should only happen when the literal was created
40
+ # outside of the parser and \{#to\_s} was called on it
41
+ def options
42
+ opts = super
43
+ return opts if opts
44
+ raise Sass::SyntaxError.new(<<MSG)
45
+ The #options attribute is not set on this #{self.class}.
46
+ This error is probably occurring because #to_s was called
47
+ on this literal within a custom Sass function without first
48
+ setting the #option attribute.
49
+ MSG
32
50
  end
33
51
 
34
52
  # The SassScript `and` operation.
@@ -84,7 +102,7 @@ module Sass::Script
84
102
  Sass::Script::Bool.new(!to_bool)
85
103
  end
86
104
 
87
- # The SassScript default operation (e.g. `!a !b`, `"foo" "bar"`).
105
+ # The SassScript default operation (e.g. `$a $b`, `"foo" "bar"`).
88
106
  #
89
107
  # @param other [Literal] The right-hand side of the operator
90
108
  # @return [Script::String] A string containing both literals
@@ -93,7 +111,7 @@ module Sass::Script
93
111
  Sass::Script::String.new("#{self.to_s} #{other.to_s}")
94
112
  end
95
113
 
96
- # The SassScript `,` operation (e.g. `!a, !b`, `"foo", "bar"`).
114
+ # The SassScript `,` operation (e.g. `$a, $b`, `"foo", "bar"`).
97
115
  #
98
116
  # @param other [Literal] The right-hand side of the operator
99
117
  # @return [Script::String] A string containing both literals
@@ -108,6 +126,9 @@ module Sass::Script
108
126
  # @return [Script::String] A string containing both literals
109
127
  # without any separation
110
128
  def plus(other)
129
+ if other.is_a?(Sass::Script::String)
130
+ return Sass::Script::String.new(self.to_s + other.value, other.type)
131
+ end
111
132
  Sass::Script::String.new(self.to_s + other.to_s)
112
133
  end
113
134
 
@@ -129,7 +150,16 @@ module Sass::Script
129
150
  Sass::Script::String.new("#{self.to_s}/#{other.to_s}")
130
151
  end
131
152
 
132
- # The SassScript unary `-` operation (e.g. `-!a`).
153
+ # The SassScript unary `+` operation (e.g. `+$a`).
154
+ #
155
+ # @param other [Literal] The right-hand side of the operator
156
+ # @return [Script::String] A string containing the literal
157
+ # preceded by `"+"`
158
+ def unary_plus
159
+ Sass::Script::String.new("+#{self.to_s}")
160
+ end
161
+
162
+ # The SassScript unary `-` operation (e.g. `-$a`).
133
163
  #
134
164
  # @param other [Literal] The right-hand side of the operator
135
165
  # @return [Script::String] A string containing the literal
@@ -138,7 +168,7 @@ module Sass::Script
138
168
  Sass::Script::String.new("-#{self.to_s}")
139
169
  end
140
170
 
141
- # The SassScript unary `/` operation (e.g. `/!a`).
171
+ # The SassScript unary `/` operation (e.g. `/$a`).
142
172
  #
143
173
  # @param other [Literal] The right-hand side of the operator
144
174
  # @return [Script::String] A string containing the literal
@@ -173,5 +203,24 @@ module Sass::Script
173
203
 
174
204
  # @raise [Sass::SyntaxError] if this literal isn't an integer
175
205
  def assert_int!; to_i; end
206
+
207
+ # Returns the string representation of this literal
208
+ # as it would be output to the CSS document.
209
+ #
210
+ # @return [String]
211
+ def to_s
212
+ raise Sass::SyntaxError.new("[BUG] All subclasses of Sass::Literal must implement #to_s.")
213
+ end
214
+ alias_method :to_sass, :to_s
215
+
216
+ protected
217
+
218
+ # Evaluates the literal.
219
+ #
220
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
221
+ # @return [Literal] This literal
222
+ def _perform(environment)
223
+ self
224
+ end
176
225
  end
177
226
  end