haml 3.1.0.alpha.14 → 3.1.0.alpha.17

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 (222) hide show
  1. data/EDGE_GEM_VERSION +1 -1
  2. data/VERSION +1 -1
  3. data/lib/haml.rb +3 -2
  4. data/lib/haml/exec.rb +0 -226
  5. data/lib/sass.rb +8 -0
  6. data/lib/sass/plugin.rb +8 -0
  7. data/lib/sass/rails2_shim.rb +9 -0
  8. data/lib/sass/rails3_shim.rb +16 -0
  9. data/vendor/sass/CONTRIBUTING +3 -0
  10. data/vendor/sass/MIT-LICENSE +20 -0
  11. data/vendor/sass/README.md +201 -0
  12. data/vendor/sass/Rakefile +363 -0
  13. data/vendor/sass/TODO +39 -0
  14. data/vendor/sass/VERSION +1 -0
  15. data/vendor/sass/VERSION_NAME +1 -0
  16. data/vendor/sass/bin/css2sass +13 -0
  17. data/vendor/sass/bin/sass +8 -0
  18. data/vendor/sass/bin/sass-convert +7 -0
  19. data/vendor/sass/doc-src/FAQ.md +35 -0
  20. data/vendor/sass/doc-src/INDENTED_SYNTAX.md +210 -0
  21. data/vendor/sass/doc-src/SASS_CHANGELOG.md +1878 -0
  22. data/vendor/sass/doc-src/SASS_REFERENCE.md +1713 -0
  23. data/vendor/sass/doc-src/SCSS_FOR_SASS_USERS.md +155 -0
  24. data/vendor/sass/ext/extconf.rb +10 -0
  25. data/vendor/sass/extra/update_watch.rb +13 -0
  26. data/vendor/sass/init.rb +18 -0
  27. data/vendor/sass/lib/sass.rb +71 -0
  28. data/vendor/sass/lib/sass/cache_store.rb +208 -0
  29. data/vendor/sass/lib/sass/callbacks.rb +66 -0
  30. data/vendor/sass/lib/sass/css.rb +294 -0
  31. data/vendor/sass/lib/sass/engine.rb +792 -0
  32. data/vendor/sass/lib/sass/environment.rb +143 -0
  33. data/vendor/sass/lib/sass/error.rb +201 -0
  34. data/vendor/sass/lib/sass/exec.rb +619 -0
  35. data/vendor/sass/lib/sass/importers.rb +22 -0
  36. data/vendor/sass/lib/sass/importers/base.rb +138 -0
  37. data/vendor/sass/lib/sass/importers/filesystem.rb +121 -0
  38. data/vendor/sass/lib/sass/less.rb +363 -0
  39. data/vendor/sass/lib/sass/plugin.rb +126 -0
  40. data/vendor/sass/lib/sass/plugin/compiler.rb +346 -0
  41. data/vendor/sass/lib/sass/plugin/configuration.rb +123 -0
  42. data/vendor/sass/lib/sass/plugin/generic.rb +15 -0
  43. data/vendor/sass/lib/sass/plugin/merb.rb +48 -0
  44. data/vendor/sass/lib/sass/plugin/rack.rb +47 -0
  45. data/vendor/sass/lib/sass/plugin/rails.rb +41 -0
  46. data/vendor/sass/lib/sass/plugin/staleness_checker.rb +145 -0
  47. data/vendor/sass/lib/sass/railtie.rb +8 -0
  48. data/vendor/sass/lib/sass/repl.rb +58 -0
  49. data/vendor/sass/lib/sass/root.rb +7 -0
  50. data/vendor/sass/lib/sass/script.rb +63 -0
  51. data/vendor/sass/lib/sass/script/bool.rb +18 -0
  52. data/vendor/sass/lib/sass/script/color.rb +491 -0
  53. data/vendor/sass/lib/sass/script/css_lexer.rb +29 -0
  54. data/vendor/sass/lib/sass/script/css_parser.rb +31 -0
  55. data/vendor/sass/lib/sass/script/funcall.rb +76 -0
  56. data/vendor/sass/lib/sass/script/functions.rb +852 -0
  57. data/vendor/sass/lib/sass/script/interpolation.rb +70 -0
  58. data/vendor/sass/lib/sass/script/lexer.rb +337 -0
  59. data/vendor/sass/lib/sass/script/literal.rb +236 -0
  60. data/vendor/sass/lib/sass/script/node.rb +112 -0
  61. data/vendor/sass/lib/sass/script/number.rb +423 -0
  62. data/vendor/sass/lib/sass/script/operation.rb +90 -0
  63. data/vendor/sass/lib/sass/script/parser.rb +392 -0
  64. data/vendor/sass/lib/sass/script/string.rb +67 -0
  65. data/vendor/sass/lib/sass/script/string_interpolation.rb +93 -0
  66. data/vendor/sass/lib/sass/script/unary_operation.rb +57 -0
  67. data/vendor/sass/lib/sass/script/variable.rb +48 -0
  68. data/vendor/sass/lib/sass/scss.rb +17 -0
  69. data/vendor/sass/lib/sass/scss/css_parser.rb +51 -0
  70. data/vendor/sass/lib/sass/scss/parser.rb +838 -0
  71. data/vendor/sass/lib/sass/scss/rx.rb +126 -0
  72. data/vendor/sass/lib/sass/scss/sass_parser.rb +11 -0
  73. data/vendor/sass/lib/sass/scss/script_lexer.rb +15 -0
  74. data/vendor/sass/lib/sass/scss/script_parser.rb +25 -0
  75. data/vendor/sass/lib/sass/scss/static_parser.rb +40 -0
  76. data/vendor/sass/lib/sass/selector.rb +361 -0
  77. data/vendor/sass/lib/sass/selector/abstract_sequence.rb +62 -0
  78. data/vendor/sass/lib/sass/selector/comma_sequence.rb +82 -0
  79. data/vendor/sass/lib/sass/selector/sequence.rb +236 -0
  80. data/vendor/sass/lib/sass/selector/simple.rb +113 -0
  81. data/vendor/sass/lib/sass/selector/simple_sequence.rb +135 -0
  82. data/vendor/sass/lib/sass/shared.rb +78 -0
  83. data/vendor/sass/lib/sass/tree/comment_node.rb +128 -0
  84. data/vendor/sass/lib/sass/tree/debug_node.rb +36 -0
  85. data/vendor/sass/lib/sass/tree/directive_node.rb +75 -0
  86. data/vendor/sass/lib/sass/tree/extend_node.rb +65 -0
  87. data/vendor/sass/lib/sass/tree/for_node.rb +67 -0
  88. data/vendor/sass/lib/sass/tree/if_node.rb +81 -0
  89. data/vendor/sass/lib/sass/tree/import_node.rb +124 -0
  90. data/vendor/sass/lib/sass/tree/mixin_def_node.rb +60 -0
  91. data/vendor/sass/lib/sass/tree/mixin_node.rb +123 -0
  92. data/vendor/sass/lib/sass/tree/node.rb +490 -0
  93. data/vendor/sass/lib/sass/tree/prop_node.rb +220 -0
  94. data/vendor/sass/lib/sass/tree/root_node.rb +125 -0
  95. data/vendor/sass/lib/sass/tree/rule_node.rb +273 -0
  96. data/vendor/sass/lib/sass/tree/variable_node.rb +39 -0
  97. data/vendor/sass/lib/sass/tree/warn_node.rb +42 -0
  98. data/vendor/sass/lib/sass/tree/while_node.rb +48 -0
  99. data/vendor/sass/lib/sass/util.rb +700 -0
  100. data/vendor/sass/lib/sass/util/subset_map.rb +101 -0
  101. data/vendor/sass/lib/sass/version.rb +109 -0
  102. data/vendor/sass/rails/init.rb +1 -0
  103. data/vendor/sass/sass.gemspec +32 -0
  104. data/vendor/sass/test/sass/cache_test.rb +74 -0
  105. data/vendor/sass/test/sass/callbacks_test.rb +61 -0
  106. data/vendor/sass/test/sass/conversion_test.rb +1210 -0
  107. data/vendor/sass/test/sass/css2sass_test.rb +364 -0
  108. data/vendor/sass/test/sass/data/hsl-rgb.txt +319 -0
  109. data/vendor/sass/test/sass/engine_test.rb +2305 -0
  110. data/vendor/sass/test/sass/extend_test.rb +1348 -0
  111. data/vendor/sass/test/sass/functions_test.rb +565 -0
  112. data/vendor/sass/test/sass/importer_test.rb +104 -0
  113. data/vendor/sass/test/sass/less_conversion_test.rb +632 -0
  114. data/vendor/sass/test/sass/mock_importer.rb +49 -0
  115. data/vendor/sass/test/sass/more_results/more1.css +9 -0
  116. data/vendor/sass/test/sass/more_results/more1_with_line_comments.css +26 -0
  117. data/vendor/sass/test/sass/more_results/more_import.css +29 -0
  118. data/vendor/sass/test/sass/more_templates/_more_partial.sass +2 -0
  119. data/vendor/sass/test/sass/more_templates/more1.sass +23 -0
  120. data/vendor/sass/test/sass/more_templates/more_import.sass +11 -0
  121. data/vendor/sass/test/sass/plugin_test.rb +430 -0
  122. data/vendor/sass/test/sass/results/alt.css +4 -0
  123. data/vendor/sass/test/sass/results/basic.css +9 -0
  124. data/vendor/sass/test/sass/results/compact.css +5 -0
  125. data/vendor/sass/test/sass/results/complex.css +86 -0
  126. data/vendor/sass/test/sass/results/compressed.css +1 -0
  127. data/vendor/sass/test/sass/results/expanded.css +19 -0
  128. data/vendor/sass/test/sass/results/import.css +31 -0
  129. data/vendor/sass/test/sass/results/line_numbers.css +49 -0
  130. data/vendor/sass/test/sass/results/mixins.css +95 -0
  131. data/vendor/sass/test/sass/results/multiline.css +24 -0
  132. data/vendor/sass/test/sass/results/nested.css +22 -0
  133. data/vendor/sass/test/sass/results/options.css +1 -0
  134. data/vendor/sass/test/sass/results/parent_ref.css +13 -0
  135. data/vendor/sass/test/sass/results/script.css +16 -0
  136. data/vendor/sass/test/sass/results/scss_import.css +31 -0
  137. data/vendor/sass/test/sass/results/scss_importee.css +2 -0
  138. data/vendor/sass/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  139. data/vendor/sass/test/sass/results/subdir/subdir.css +3 -0
  140. data/vendor/sass/test/sass/results/units.css +11 -0
  141. data/vendor/sass/test/sass/results/warn.css +0 -0
  142. data/vendor/sass/test/sass/results/warn_imported.css +0 -0
  143. data/vendor/sass/test/sass/script_conversion_test.rb +254 -0
  144. data/vendor/sass/test/sass/script_test.rb +470 -0
  145. data/vendor/sass/test/sass/scss/css_test.rb +897 -0
  146. data/vendor/sass/test/sass/scss/rx_test.rb +156 -0
  147. data/vendor/sass/test/sass/scss/scss_test.rb +1088 -0
  148. data/vendor/sass/test/sass/scss/test_helper.rb +37 -0
  149. data/vendor/sass/test/sass/templates/_partial.sass +2 -0
  150. data/vendor/sass/test/sass/templates/alt.sass +16 -0
  151. data/vendor/sass/test/sass/templates/basic.sass +23 -0
  152. data/vendor/sass/test/sass/templates/bork1.sass +2 -0
  153. data/vendor/sass/test/sass/templates/bork2.sass +2 -0
  154. data/vendor/sass/test/sass/templates/bork3.sass +2 -0
  155. data/vendor/sass/test/sass/templates/bork4.sass +2 -0
  156. data/vendor/sass/test/sass/templates/compact.sass +17 -0
  157. data/vendor/sass/test/sass/templates/complex.sass +305 -0
  158. data/vendor/sass/test/sass/templates/compressed.sass +15 -0
  159. data/vendor/sass/test/sass/templates/expanded.sass +17 -0
  160. data/vendor/sass/test/sass/templates/import.sass +12 -0
  161. data/vendor/sass/test/sass/templates/importee.less +2 -0
  162. data/vendor/sass/test/sass/templates/importee.sass +19 -0
  163. data/vendor/sass/test/sass/templates/line_numbers.sass +13 -0
  164. data/vendor/sass/test/sass/templates/mixin_bork.sass +5 -0
  165. data/vendor/sass/test/sass/templates/mixins.sass +76 -0
  166. data/vendor/sass/test/sass/templates/multiline.sass +20 -0
  167. data/vendor/sass/test/sass/templates/nested.sass +25 -0
  168. data/vendor/sass/test/sass/templates/nested_bork1.sass +2 -0
  169. data/vendor/sass/test/sass/templates/nested_bork2.sass +2 -0
  170. data/vendor/sass/test/sass/templates/nested_bork3.sass +2 -0
  171. data/vendor/sass/test/sass/templates/nested_bork4.sass +2 -0
  172. data/vendor/sass/test/sass/templates/nested_mixin_bork.sass +6 -0
  173. data/vendor/sass/test/sass/templates/options.sass +2 -0
  174. data/vendor/sass/test/sass/templates/parent_ref.sass +25 -0
  175. data/vendor/sass/test/sass/templates/script.sass +101 -0
  176. data/vendor/sass/test/sass/templates/scss_import.scss +11 -0
  177. data/vendor/sass/test/sass/templates/scss_importee.scss +1 -0
  178. data/vendor/sass/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  179. data/vendor/sass/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  180. data/vendor/sass/test/sass/templates/subdir/subdir.sass +6 -0
  181. data/vendor/sass/test/sass/templates/units.sass +11 -0
  182. data/vendor/sass/test/sass/templates/warn.sass +3 -0
  183. data/vendor/sass/test/sass/templates/warn_imported.sass +4 -0
  184. data/vendor/sass/test/sass/test_helper.rb +8 -0
  185. data/vendor/sass/test/sass/util/subset_map_test.rb +91 -0
  186. data/vendor/sass/test/sass/util_test.rb +275 -0
  187. data/vendor/sass/test/test_helper.rb +64 -0
  188. data/vendor/sass/vendor/fssm/LICENSE +20 -0
  189. data/vendor/sass/vendor/fssm/README.markdown +55 -0
  190. data/vendor/sass/vendor/fssm/Rakefile +59 -0
  191. data/vendor/sass/vendor/fssm/VERSION.yml +5 -0
  192. data/vendor/sass/vendor/fssm/example.rb +9 -0
  193. data/vendor/sass/vendor/fssm/fssm.gemspec +77 -0
  194. data/vendor/sass/vendor/fssm/lib/fssm.rb +33 -0
  195. data/vendor/sass/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
  196. data/vendor/sass/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
  197. data/vendor/sass/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
  198. data/vendor/sass/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
  199. data/vendor/sass/vendor/fssm/lib/fssm/monitor.rb +26 -0
  200. data/vendor/sass/vendor/fssm/lib/fssm/path.rb +91 -0
  201. data/vendor/sass/vendor/fssm/lib/fssm/pathname.rb +502 -0
  202. data/vendor/sass/vendor/fssm/lib/fssm/state/directory.rb +57 -0
  203. data/vendor/sass/vendor/fssm/lib/fssm/state/file.rb +24 -0
  204. data/vendor/sass/vendor/fssm/lib/fssm/support.rb +63 -0
  205. data/vendor/sass/vendor/fssm/lib/fssm/tree.rb +176 -0
  206. data/vendor/sass/vendor/fssm/profile/prof-cache.rb +40 -0
  207. data/vendor/sass/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
  208. data/vendor/sass/vendor/fssm/profile/prof-pathname.rb +68 -0
  209. data/vendor/sass/vendor/fssm/profile/prof-plain-pathname.html +988 -0
  210. data/vendor/sass/vendor/fssm/profile/prof.html +2379 -0
  211. data/vendor/sass/vendor/fssm/spec/path_spec.rb +75 -0
  212. data/vendor/sass/vendor/fssm/spec/root/duck/quack.txt +0 -0
  213. data/vendor/sass/vendor/fssm/spec/root/file.css +0 -0
  214. data/vendor/sass/vendor/fssm/spec/root/file.rb +0 -0
  215. data/vendor/sass/vendor/fssm/spec/root/file.yml +0 -0
  216. data/vendor/sass/vendor/fssm/spec/root/moo/cow.txt +0 -0
  217. data/vendor/sass/vendor/fssm/spec/spec_helper.rb +14 -0
  218. data/vendor/sass/yard/callbacks.rb +29 -0
  219. data/vendor/sass/yard/default/fulldoc/html/css/common.sass +26 -0
  220. data/vendor/sass/yard/default/layout/html/footer.erb +12 -0
  221. data/vendor/sass/yard/inherited_hash.rb +41 -0
  222. metadata +219 -2
@@ -0,0 +1,67 @@
1
+ require 'sass/script/literal'
2
+
3
+ module Sass::Script
4
+ # A SassScript object representing a CSS string *or* a CSS identifier.
5
+ class String < Literal
6
+ # The Ruby value of the string.
7
+ #
8
+ # @return [String]
9
+ attr_reader :value
10
+
11
+ # Whether this is a CSS string or a CSS identifier.
12
+ # The difference is that strings are written with double-quotes,
13
+ # while identifiers aren't.
14
+ #
15
+ # @return [Symbol] `:string` or `:identifier`
16
+ attr_reader :type
17
+
18
+ # In addition to setting the \{#context} of the string,
19
+ # this sets the string to be an identifier if the context is `:equals`.
20
+ #
21
+ # @see Node#context=
22
+ def context=(context)
23
+ super
24
+ @type = :identifier if context == :equals
25
+ end
26
+
27
+ # Creates a new string.
28
+ #
29
+ # @param value [String] See \{#value}
30
+ # @param type [Symbol] See \{#type}
31
+ def initialize(value, type = :identifier)
32
+ super(value)
33
+ @type = type
34
+ end
35
+
36
+ # @see Literal#plus
37
+ def plus(other)
38
+ other_str = other.is_a?(Sass::Script::String) ? other.value : other.to_s
39
+ Sass::Script::String.new(self.value + other_str, self.type)
40
+ end
41
+
42
+ # @see Node#to_s
43
+ def to_s(opts = {})
44
+ if self.type == :identifier
45
+ return %q{""} if context == :equals && self.value.size == 0
46
+ return self.value.gsub("\n", " ")
47
+ end
48
+
49
+ return "\"#{value.gsub('"', "\\\"")}\"" if opts[:quote] == %q{"}
50
+ return "'#{value.gsub("'", "\\'")}'" if opts[:quote] == %q{'}
51
+ return "\"#{value}\"" unless value.include?('"')
52
+ return "'#{value}'" unless value.include?("'")
53
+ "\"#{value.gsub('"', "\\\"")}\"" #'
54
+ end
55
+
56
+ # @see Node#to_sass
57
+ def to_sass(opts = {})
58
+ if self.type == :identifier && context == :equals &&
59
+ self.value !~ Sass::SCSS::RX::URI &&
60
+ Sass::SCSS::RX.escape_ident(self.value).include?(?\\)
61
+ return "unquote(#{Sass::Script::String.new(self.value, :string).to_sass})"
62
+ else
63
+ return to_s
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,93 @@
1
+ module Sass::Script
2
+ # A SassScript object representing `#{}` interpolation within a string.
3
+ #
4
+ # @see Interpolation
5
+ class StringInterpolation < Node
6
+ # Interpolation in a string is of the form `"before #{mid} after"`,
7
+ # where `before` and `after` may include more interpolation.
8
+ #
9
+ # @param before [Node] The string before the interpolation
10
+ # @param mid [Node] The SassScript within the interpolation
11
+ # @param after [Node] The string after the interpolation
12
+ def initialize(before, mid, after)
13
+ @before = before
14
+ @mid = mid
15
+ @after = after
16
+ end
17
+
18
+ # @return [String] A human-readable s-expression representation of the interpolation
19
+ def inspect
20
+ "(string_interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
21
+ end
22
+
23
+ # @see Node#to_sass
24
+ def to_sass(opts = {})
25
+ # We can get rid of all of this when we remove the deprecated :equals context
26
+ before_unquote, before_quote_char, before_str = parse_str(@before.to_sass(opts))
27
+ after_unquote, after_quote_char, after_str = parse_str(@after.to_sass(opts))
28
+ unquote = before_unquote || after_unquote ||
29
+ (before_quote_char && !after_quote_char && !after_str.empty?) ||
30
+ (!before_quote_char && after_quote_char && !before_str.empty?)
31
+ quote_char =
32
+ if before_quote_char && after_quote_char && before_quote_char != after_quote_char
33
+ before_str.gsub!("\\'", "'")
34
+ before_str.gsub!('"', "\\\"")
35
+ after_str.gsub!("\\'", "'")
36
+ after_str.gsub!('"', "\\\"")
37
+ '"'
38
+ else
39
+ before_quote_char || after_quote_char
40
+ end
41
+
42
+ res = ""
43
+ res << 'unquote(' if unquote
44
+ res << quote_char if quote_char
45
+ res << before_str
46
+ res << '#{' << @mid.to_sass(opts) << '}'
47
+ res << after_str
48
+ res << quote_char if quote_char
49
+ res << ')' if unquote
50
+ res
51
+ end
52
+
53
+ # Returns the three components of the interpolation, `before`, `mid`, and `after`.
54
+ #
55
+ # @return [Array<Node>]
56
+ # @see #initialize
57
+ # @see Node#children
58
+ def children
59
+ [@before, @mid, @after].compact
60
+ end
61
+
62
+ protected
63
+
64
+ # Evaluates the interpolation.
65
+ #
66
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
67
+ # @return [Sass::Script::String] The SassScript string that is the value of the interpolation
68
+ def _perform(environment)
69
+ res = ""
70
+ before = @before.perform(environment)
71
+ res << before.value
72
+ mid = @mid.perform(environment)
73
+ res << (mid.is_a?(Sass::Script::String) ? mid.value : mid.to_s)
74
+ res << @after.perform(environment).value
75
+ opts(Sass::Script::String.new(res, before.type))
76
+ end
77
+
78
+ private
79
+
80
+ def parse_str(str)
81
+ case str
82
+ when /^unquote\((["'])(.*)\1\)$/
83
+ return true, $1, $2
84
+ when '""'
85
+ return false, nil, ""
86
+ when /^(["'])(.*)\1$/
87
+ return false, $1, $2
88
+ else
89
+ return false, nil, str
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,57 @@
1
+ module Sass::Script
2
+ # A SassScript parse node representing a unary operation,
3
+ # such as `-$b` or `not true`.
4
+ #
5
+ # Currently only `-`, `/`, and `not` are unary operators.
6
+ class UnaryOperation < Node
7
+ # @param operand [Script::Node] The parse-tree node
8
+ # for the object of the operator
9
+ # @param operator [Symbol] The operator to perform
10
+ def initialize(operand, operator)
11
+ @operand = operand
12
+ @operator = operator
13
+ super()
14
+ end
15
+
16
+ # @return [String] A human-readable s-expression representation of the operation
17
+ def inspect
18
+ "(#{@operator.inspect} #{@operand.inspect})"
19
+ end
20
+
21
+ # @see Node#to_sass
22
+ def to_sass(opts = {})
23
+ operand = @operand.to_sass(opts)
24
+ if @operand.is_a?(Operation) ||
25
+ (@operator == :minus &&
26
+ (operand =~ Sass::SCSS::RX::IDENT) == 0)
27
+ operand = "(#{@operand.to_sass(opts)})"
28
+ end
29
+ op = Lexer::OPERATORS_REVERSE[@operator]
30
+ op + (op =~ /[a-z]/ ? " " : "") + operand
31
+ end
32
+
33
+ # Returns the operand of the operation.
34
+ #
35
+ # @return [Array<Node>]
36
+ # @see Node#children
37
+ def children
38
+ [@operand]
39
+ end
40
+
41
+ protected
42
+
43
+ # Evaluates the operation.
44
+ #
45
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
46
+ # @return [Literal] The SassScript object that is the value of the operation
47
+ # @raise [Sass::SyntaxError] if the operation is undefined for the operand
48
+ def _perform(environment)
49
+ operator = "unary_#{@operator}"
50
+ literal = @operand.perform(environment)
51
+ literal.send(operator)
52
+ rescue NoMethodError => e
53
+ raise e unless e.name.to_s == operator.to_s
54
+ raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{literal}\".")
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,48 @@
1
+ module Sass
2
+ module Script
3
+ # A SassScript parse node representing a variable.
4
+ class Variable < Node
5
+ # The name of the variable.
6
+ #
7
+ # @return [String]
8
+ attr_reader :name
9
+
10
+ # @param name [String] See \{#name}
11
+ def initialize(name)
12
+ @name = name
13
+ super()
14
+ end
15
+
16
+ # @return [String] A string representation of the variable
17
+ def inspect(opts = {})
18
+ return "!important" if name == "important"
19
+ "$#{dasherize(name, opts)}"
20
+ end
21
+ alias_method :to_sass, :inspect
22
+
23
+ # Returns an empty array.
24
+ #
25
+ # @return [Array<Node>] empty
26
+ # @see Node#children
27
+ def children
28
+ []
29
+ end
30
+
31
+ protected
32
+
33
+ # Evaluates the variable.
34
+ #
35
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
36
+ # @return [Literal] The SassScript object that is the value of the variable
37
+ # @raise [Sass::SyntaxError] if the variable is undefined
38
+ def _perform(environment)
39
+ raise SyntaxError.new("Undefined variable: \"$#{name}\".") unless val = environment.var(name)
40
+ if val.is_a?(Number)
41
+ val = val.dup
42
+ val.original = nil
43
+ end
44
+ return val
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ require 'sass/scss/rx'
2
+ require 'sass/scss/script_lexer'
3
+ require 'sass/scss/script_parser'
4
+ require 'sass/scss/parser'
5
+ require 'sass/scss/sass_parser'
6
+ require 'sass/scss/static_parser'
7
+ require 'sass/scss/css_parser'
8
+
9
+ module Sass
10
+ # SCSS is the CSS syntax for Sass.
11
+ # It parses into the same syntax tree as Sass,
12
+ # and generates the same sort of output CSS.
13
+ #
14
+ # This module contains code for the parsing of SCSS.
15
+ # The evaluation is handled by the broader {Sass} module.
16
+ module SCSS; end
17
+ end
@@ -0,0 +1,51 @@
1
+ require 'sass/script/css_parser'
2
+
3
+ module Sass
4
+ module SCSS
5
+ # This is a subclass of {Parser} which only parses plain CSS.
6
+ # It doesn't support any Sass extensions, such as interpolation,
7
+ # parent references, nested selectors, and so forth.
8
+ # It does support all the same CSS hacks as the SCSS parser, though.
9
+ class CssParser < StaticParser
10
+ # Parse a selector, and return its value as a string.
11
+ #
12
+ # @return [String, nil] The parsed selector, or nil if no selector was parsed
13
+ # @raise [Sass::SyntaxError] if there's a syntax error in the selector
14
+ def parse_selector_string
15
+ init_scanner!
16
+ str {return unless selector}
17
+ end
18
+
19
+ private
20
+
21
+ def parent_selector; nil; end
22
+ def interpolation; nil; end
23
+ def interp_string; tok(STRING); end
24
+ def interp_ident(ident = IDENT); tok(ident); end
25
+ def use_css_import?; true; end
26
+
27
+ def special_directive(name)
28
+ return unless name == 'media' || name == 'import'
29
+ super
30
+ end
31
+
32
+ def block_child(context)
33
+ case context
34
+ when :ruleset
35
+ declaration
36
+ when :stylesheet
37
+ directive || ruleset
38
+ when :directive
39
+ directive || declaration_or_ruleset
40
+ end
41
+ end
42
+
43
+ def nested_properties!(node, space)
44
+ expected('expression (e.g. 1px, bold)');
45
+ end
46
+
47
+ @sass_script_parser = Class.new(Sass::Script::CssParser)
48
+ @sass_script_parser.send(:include, ScriptParser)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,838 @@
1
+ require 'strscan'
2
+ require 'set'
3
+
4
+ module Sass
5
+ module SCSS
6
+ # The parser for SCSS.
7
+ # It parses a string of code into a tree of {Sass::Tree::Node}s.
8
+ class Parser
9
+ # @param str [String, StringScanner] The source document to parse.
10
+ # Note that `Parser` *won't* raise a nice error message if this isn't properly parsed;
11
+ # for that, you should use the higher-level {Sass::Engine} or {Sass::CSS}.
12
+ # @param line [Fixnum] The line on which the source string appeared,
13
+ # if it's part of another document
14
+ def initialize(str, line = 1)
15
+ @template = str
16
+ @line = line
17
+ @strs = []
18
+ end
19
+
20
+ # Parses an SCSS document.
21
+ #
22
+ # @return [Sass::Tree::RootNode] The root node of the document tree
23
+ # @raise [Sass::SyntaxError] if there's a syntax error in the document
24
+ def parse
25
+ init_scanner!
26
+ root = stylesheet
27
+ expected("selector or at-rule") unless @scanner.eos?
28
+ root
29
+ end
30
+
31
+ # Parses an identifier with interpolation.
32
+ # Note that this won't assert that the identifier takes up the entire input string;
33
+ # it's meant to be used with `StringScanner`s as part of other parsers.
34
+ #
35
+ # @return [Array<String, Sass::Script::Node>, nil]
36
+ # The interpolated identifier, or nil if none could be parsed
37
+ def parse_interp_ident
38
+ init_scanner!
39
+ interp_ident
40
+ end
41
+
42
+ private
43
+
44
+ include Sass::SCSS::RX
45
+
46
+ def init_scanner!
47
+ @scanner =
48
+ if @template.is_a?(StringScanner)
49
+ @template
50
+ else
51
+ StringScanner.new(@template.gsub("\r", ""))
52
+ end
53
+ end
54
+
55
+ def stylesheet
56
+ node = node(Sass::Tree::RootNode.new(@scanner.string))
57
+ block_contents(node, :stylesheet) {s(node)}
58
+ end
59
+
60
+ def s(node)
61
+ while tok(S) || tok(CDC) || tok(CDO) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
62
+ next unless c
63
+ process_comment c, node
64
+ c = nil
65
+ end
66
+ true
67
+ end
68
+
69
+ def ss
70
+ nil while tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
71
+ true
72
+ end
73
+
74
+ def ss_comments(node)
75
+ while tok(S) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
76
+ next unless c
77
+ process_comment c, node
78
+ c = nil
79
+ end
80
+
81
+ true
82
+ end
83
+
84
+ def whitespace
85
+ return unless tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
86
+ ss
87
+ end
88
+
89
+ def process_comment(text, node)
90
+ single_line = text =~ /^\/\//
91
+ pre_str = single_line ? "" : @scanner.
92
+ string[0...@scanner.pos].
93
+ reverse[/.*?\*\/(.*?)($|\Z)/, 1].
94
+ reverse.gsub(/[^\s]/, ' ')
95
+ text = text.sub(/^\s*\/\//, '/*').gsub(/^\s*\/\//, ' *') + ' */' if single_line
96
+ comment = Sass::Tree::CommentNode.new(pre_str + text, single_line)
97
+ comment.line = @line - text.count("\n")
98
+ node << comment
99
+ end
100
+
101
+ DIRECTIVES = Set[:mixin, :include, :debug, :warn, :for, :while, :if, :extend, :import, :media]
102
+
103
+ def directive
104
+ return unless tok(/@/)
105
+ name = tok!(IDENT)
106
+ ss
107
+
108
+ if dir = special_directive(name)
109
+ return dir
110
+ end
111
+
112
+ # Most at-rules take expressions (e.g. @import),
113
+ # but some (e.g. @page) take selector-like arguments
114
+ val = str {break unless expr}
115
+ val ||= CssParser.new(@scanner, @line).parse_selector_string
116
+ node = node(Sass::Tree::DirectiveNode.new("@#{name} #{val}".strip))
117
+
118
+ if tok(/\{/)
119
+ node.has_children = true
120
+ block_contents(node, :directive)
121
+ tok!(/\}/)
122
+ end
123
+
124
+ node
125
+ end
126
+
127
+ def special_directive(name)
128
+ sym = name.gsub('-', '_').to_sym
129
+ DIRECTIVES.include?(sym) && send("#{sym}_directive")
130
+ end
131
+
132
+ def mixin_directive
133
+ name = tok! IDENT
134
+ args = sass_script(:parse_mixin_definition_arglist)
135
+ ss
136
+ block(node(Sass::Tree::MixinDefNode.new(name, args)), :directive)
137
+ end
138
+
139
+ def include_directive
140
+ name = tok! IDENT
141
+ args = sass_script(:parse_mixin_include_arglist)
142
+ ss
143
+ node(Sass::Tree::MixinNode.new(name, args))
144
+ end
145
+
146
+ def debug_directive
147
+ node(Sass::Tree::DebugNode.new(sass_script(:parse)))
148
+ end
149
+
150
+ def warn_directive
151
+ node(Sass::Tree::WarnNode.new(sass_script(:parse)))
152
+ end
153
+
154
+ def for_directive
155
+ tok!(/\$/)
156
+ var = tok! IDENT
157
+ ss
158
+
159
+ tok!(/from/)
160
+ from = sass_script(:parse_until, Set["to", "through"])
161
+ ss
162
+
163
+ @expected = '"to" or "through"'
164
+ exclusive = (tok(/to/) || tok!(/through/)) == 'to'
165
+ to = sass_script(:parse)
166
+ ss
167
+
168
+ block(node(Sass::Tree::ForNode.new(var, from, to, exclusive)), :directive)
169
+ end
170
+
171
+ def while_directive
172
+ expr = sass_script(:parse)
173
+ ss
174
+ block(node(Sass::Tree::WhileNode.new(expr)), :directive)
175
+ end
176
+
177
+ def if_directive
178
+ expr = sass_script(:parse)
179
+ ss
180
+ node = block(node(Sass::Tree::IfNode.new(expr)), :directive)
181
+ pos = @scanner.pos
182
+ ss
183
+
184
+ else_block(node) ||
185
+ begin
186
+ # Backtrack in case there are any comments we want to parse
187
+ @scanner.pos = pos
188
+ node
189
+ end
190
+ end
191
+
192
+ def else_block(node)
193
+ return unless tok(/@else/)
194
+ ss
195
+ else_node = block(
196
+ Sass::Tree::IfNode.new((sass_script(:parse) if tok(/if/))),
197
+ :directive)
198
+ node.add_else(else_node)
199
+ pos = @scanner.pos
200
+ ss
201
+
202
+ else_block(node) ||
203
+ begin
204
+ # Backtrack in case there are any comments we want to parse
205
+ @scanner.pos = pos
206
+ node
207
+ end
208
+ end
209
+
210
+ def extend_directive
211
+ node(Sass::Tree::ExtendNode.new(expr!(:selector)))
212
+ end
213
+
214
+ def import_directive
215
+ values = []
216
+
217
+ loop do
218
+ values << expr!(:import_arg)
219
+ break if use_css_import? || !tok(/,\s*/)
220
+ end
221
+
222
+ return values
223
+ end
224
+
225
+ def import_arg
226
+ return unless arg = tok(STRING) || (uri = tok!(URI))
227
+ path = @scanner[1] || @scanner[2] || @scanner[3]
228
+ ss
229
+
230
+ media = str {media_query_list}.strip
231
+
232
+ if uri || path =~ /^http:\/\// || !media.strip.empty? || use_css_import?
233
+ return node(Sass::Tree::DirectiveNode.new("@import #{arg} #{media}".strip))
234
+ end
235
+
236
+ node(Sass::Tree::ImportNode.new(path.strip))
237
+ end
238
+
239
+ def use_css_import?; false; end
240
+
241
+ def media_directive
242
+ val = str {media_query_list}.strip
243
+ block(node(Sass::Tree::DirectiveNode.new("@media #{val}")), :directive)
244
+ end
245
+
246
+ # http://www.w3.org/TR/css3-mediaqueries/#syntax
247
+ def media_query_list
248
+ return unless media_query
249
+
250
+ ss
251
+ while tok(/,/)
252
+ ss; expr!(:media_query); ss
253
+ end
254
+
255
+ true
256
+ end
257
+
258
+ def media_query
259
+ if tok(/only|not/i)
260
+ ss
261
+ @expected = "media type (e.g. print, screen)"
262
+ tok!(IDENT)
263
+ ss
264
+ elsif !tok(IDENT) && !media_expr
265
+ return
266
+ end
267
+
268
+ ss
269
+ while tok(/and/i)
270
+ ss; expr!(:media_expr); ss
271
+ end
272
+
273
+ true
274
+ end
275
+
276
+ def media_expr
277
+ return unless tok(/\(/)
278
+ ss
279
+ @expected = "media feature (e.g. min-device-width, color)"
280
+ tok!(IDENT)
281
+ ss
282
+
283
+ if tok(/:/)
284
+ ss; expr!(:expr)
285
+ end
286
+ tok!(/\)/)
287
+ ss
288
+
289
+ true
290
+ end
291
+
292
+ def variable
293
+ return unless tok(/\$/)
294
+ name = tok!(IDENT)
295
+ ss; tok!(/:/); ss
296
+
297
+ expr = sass_script(:parse)
298
+ guarded = tok(DEFAULT)
299
+ node(Sass::Tree::VariableNode.new(name, expr, guarded))
300
+ end
301
+
302
+ def operator
303
+ # Many of these operators (all except / and ,)
304
+ # are disallowed by the CSS spec,
305
+ # but they're included here for compatibility
306
+ # with some proprietary MS properties
307
+ str {ss if tok(/[\/,:.=]/)}
308
+ end
309
+
310
+ def unary_operator
311
+ tok(/[+-]/)
312
+ end
313
+
314
+ def ruleset
315
+ return unless rules = selector_sequence
316
+ block(node(Sass::Tree::RuleNode.new(rules.flatten.compact)), :ruleset)
317
+ end
318
+
319
+ def block(node, context)
320
+ node.has_children = true
321
+ tok!(/\{/)
322
+ block_contents(node, context)
323
+ tok!(/\}/)
324
+ node
325
+ end
326
+
327
+ # A block may contain declarations and/or rulesets
328
+ def block_contents(node, context)
329
+ block_given? ? yield : ss_comments(node)
330
+ node << (child = block_child(context))
331
+ while tok(/;/) || has_children?(child)
332
+ block_given? ? yield : ss_comments(node)
333
+ node << (child = block_child(context))
334
+ end
335
+ node
336
+ end
337
+
338
+ def block_child(context)
339
+ return variable || directive || ruleset if context == :stylesheet
340
+ variable || directive || declaration_or_ruleset
341
+ end
342
+
343
+ def has_children?(child_or_array)
344
+ return false unless child_or_array
345
+ return child_or_array.last.has_children if child_or_array.is_a?(Array)
346
+ return child_or_array.has_children
347
+ end
348
+
349
+ # This is a nasty hack, and the only place in the parser
350
+ # that requires backtracking.
351
+ # The reason is that we can't figure out if certain strings
352
+ # are declarations or rulesets with fixed finite lookahead.
353
+ # For example, "foo:bar baz baz baz..." could be either a property
354
+ # or a selector.
355
+ #
356
+ # To handle this, we simply check if it works as a property
357
+ # (which is the most common case)
358
+ # and, if it doesn't, try it as a ruleset.
359
+ #
360
+ # We could eke some more efficiency out of this
361
+ # by handling some easy cases (first token isn't an identifier,
362
+ # no colon after the identifier, whitespace after the colon),
363
+ # but I'm not sure the gains would be worth the added complexity.
364
+ def declaration_or_ruleset
365
+ pos = @scanner.pos
366
+ line = @line
367
+ old_use_property_exception, @use_property_exception =
368
+ @use_property_exception, false
369
+ begin
370
+ decl = declaration
371
+ unless decl && decl.has_children
372
+ # We want an exception if it's not there,
373
+ # but we don't want to consume if it is
374
+ tok!(/[;}]/) unless tok?(/[;}]/)
375
+ end
376
+ return decl
377
+ rescue Sass::SyntaxError => decl_err
378
+ end
379
+
380
+ @line = line
381
+ @scanner.pos = pos
382
+
383
+ begin
384
+ return ruleset
385
+ rescue Sass::SyntaxError => ruleset_err
386
+ raise @use_property_exception ? decl_err : ruleset_err
387
+ end
388
+ ensure
389
+ @use_property_exception = old_use_property_exception
390
+ end
391
+
392
+ def selector_sequence
393
+ if sel = tok(STATIC_SELECTOR)
394
+ return [sel]
395
+ end
396
+
397
+ rules = []
398
+ return unless v = selector
399
+ rules.concat v
400
+
401
+ while tok(/,/)
402
+ rules << ',' << str {ss}
403
+ rules.concat expr!(:selector)
404
+ end
405
+ rules
406
+ end
407
+
408
+ def selector
409
+ return unless sel = _selector
410
+ sel.to_a
411
+ end
412
+
413
+ def selector_comma_sequence
414
+ return unless sel = _selector
415
+ selectors = [sel]
416
+ while tok(/,/)
417
+ ws = str{ss}
418
+ selectors << expr!(:_selector)
419
+ selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members) if ws.include?("\n")
420
+ end
421
+ Selector::CommaSequence.new(selectors)
422
+ end
423
+
424
+ def _selector
425
+ # The combinator here allows the "> E" hack
426
+ return unless val = combinator || simple_selector_sequence
427
+ nl = str{ss}.include?("\n")
428
+ res = []
429
+ res << val
430
+ res << "\n" if nl
431
+
432
+ while val = combinator || simple_selector_sequence
433
+ res << val
434
+ res << "\n" if str{ss}.include?("\n")
435
+ end
436
+ Selector::Sequence.new(res.compact)
437
+ end
438
+
439
+ def combinator
440
+ tok(PLUS) || tok(GREATER) || tok(TILDE)
441
+ end
442
+
443
+ def simple_selector_sequence
444
+ # This allows for stuff like http://www.w3.org/TR/css3-animations/#keyframes-
445
+ return expr unless e = element_name || id_selector || class_selector ||
446
+ attrib || negation || pseudo || parent_selector || interpolation_selector
447
+ res = [e]
448
+
449
+ # The tok(/\*/) allows the "E*" hack
450
+ while v = element_name || id_selector || class_selector ||
451
+ attrib || negation || pseudo || interpolation_selector ||
452
+ (tok(/\*/) && Selector::Universal.new(nil))
453
+ res << v
454
+ end
455
+
456
+ if tok?(/&/)
457
+ begin
458
+ expected('"{"')
459
+ rescue Sass::SyntaxError => e
460
+ e.message << "\n\n" << <<MESSAGE
461
+ In Sass 3, the parent selector & can only be used where element names are valid,
462
+ since it could potentially be replaced by an element name.
463
+ MESSAGE
464
+ raise e
465
+ end
466
+ end
467
+
468
+ Selector::SimpleSequence.new(res)
469
+ end
470
+
471
+ def parent_selector
472
+ return unless tok(/&/)
473
+ Selector::Parent.new
474
+ end
475
+
476
+ def class_selector
477
+ return unless tok(/\./)
478
+ @expected = "class name"
479
+ Selector::Class.new(merge(expr!(:interp_ident)))
480
+ end
481
+
482
+ def id_selector
483
+ return unless tok(/#(?!\{)/)
484
+ @expected = "id name"
485
+ Selector::Id.new(merge(expr!(:interp_name)))
486
+ end
487
+
488
+ def element_name
489
+ return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
490
+ if tok(/\|/)
491
+ @expected = "element name or *"
492
+ ns = name
493
+ name = interp_ident || tok!(/\*/)
494
+ end
495
+
496
+ if name == '*'
497
+ Selector::Universal.new(merge(ns))
498
+ else
499
+ Selector::Element.new(merge(name), merge(ns))
500
+ end
501
+ end
502
+
503
+ def interpolation_selector
504
+ return unless script = interpolation
505
+ Selector::Interpolation.new(script)
506
+ end
507
+
508
+ def attrib
509
+ return unless tok(/\[/)
510
+ ss
511
+ ns, name = attrib_name!
512
+ ss
513
+
514
+ if op = tok(/=/) ||
515
+ tok(INCLUDES) ||
516
+ tok(DASHMATCH) ||
517
+ tok(PREFIXMATCH) ||
518
+ tok(SUFFIXMATCH) ||
519
+ tok(SUBSTRINGMATCH)
520
+ @expected = "identifier or string"
521
+ ss
522
+ if val = tok(IDENT)
523
+ val = [val]
524
+ else
525
+ val = expr!(:interp_string)
526
+ end
527
+ ss
528
+ end
529
+ tok(/\]/)
530
+
531
+ Selector::Attribute.new(merge(name), merge(ns), op, merge(val))
532
+ end
533
+
534
+ def attrib_name!
535
+ if name_or_ns = interp_ident
536
+ # E, E|E
537
+ if tok(/\|(?!=)/)
538
+ ns = name_or_ns
539
+ name = interp_ident
540
+ else
541
+ name = name_or_ns
542
+ end
543
+ else
544
+ # *|E or |E
545
+ ns = [tok(/\*/) || ""]
546
+ tok!(/\|/)
547
+ name = expr!(:interp_ident)
548
+ end
549
+ return ns, name
550
+ end
551
+
552
+ def pseudo
553
+ return unless s = tok(/::?/)
554
+ @expected = "pseudoclass or pseudoelement"
555
+ name = expr!(:interp_ident)
556
+ if tok(/\(/)
557
+ ss
558
+ arg = expr!(:pseudo_expr)
559
+ tok!(/\)/)
560
+ end
561
+ Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
562
+ end
563
+
564
+ def pseudo_expr
565
+ return unless e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
566
+ interp_string || tok(IDENT) || interpolation
567
+ res = [e, str{ss}]
568
+ while e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
569
+ interp_string || tok(IDENT) || interpolation
570
+ res << e << str{ss}
571
+ end
572
+ res
573
+ end
574
+
575
+ def negation
576
+ return unless name = tok(NOT) || tok(MOZ_ANY)
577
+ ss
578
+ @expected = "selector"
579
+ sel = selector_comma_sequence
580
+ tok!(/\)/)
581
+ Selector::SelectorPseudoClass.new(name[1...-1], sel)
582
+ end
583
+
584
+ def declaration
585
+ # This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
586
+ if s = tok(/[:\*\.]|\#(?!\{)/)
587
+ @use_property_exception = s !~ /[\.\#]/
588
+ name = [s, str{ss}, *expr!(:interp_ident)]
589
+ else
590
+ return unless name = interp_ident
591
+ name = [name] if name.is_a?(String)
592
+ end
593
+ if comment = tok(COMMENT)
594
+ name << comment
595
+ end
596
+ ss
597
+
598
+ tok!(/:/)
599
+ space, value = value!
600
+ ss
601
+ require_block = tok?(/\{/)
602
+
603
+ node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new))
604
+
605
+ return node unless require_block
606
+ nested_properties! node, space
607
+ end
608
+
609
+ def value!
610
+ space = !str {ss}.empty?
611
+ @use_property_exception ||= space || !tok?(IDENT)
612
+
613
+ return true, Sass::Script::String.new("") if tok?(/\{/)
614
+ # This is a bit of a dirty trick:
615
+ # if the value is completely static,
616
+ # we don't parse it at all, and instead return a plain old string
617
+ # containing the value.
618
+ # This results in a dramatic speed increase.
619
+ if val = tok(STATIC_VALUE)
620
+ return space, Sass::Script::String.new(val.strip)
621
+ end
622
+ return space, sass_script(:parse)
623
+ end
624
+
625
+ def plain_value
626
+ return unless tok(/:/)
627
+ space = !str {ss}.empty?
628
+ @use_property_exception ||= space || !tok?(IDENT)
629
+
630
+ expression = expr
631
+ expression << tok(IMPORTANT) if expression
632
+ # expression, space, value
633
+ return expression, space, expression || [""]
634
+ end
635
+
636
+ def nested_properties!(node, space)
637
+ raise Sass::SyntaxError.new(<<MESSAGE, :line => @line) unless space
638
+ Invalid CSS: a space is required between a property and its definition
639
+ when it has other properties nested beneath it.
640
+ MESSAGE
641
+
642
+ @use_property_exception = true
643
+ @expected = 'expression (e.g. 1px, bold) or "{"'
644
+ block(node, :property)
645
+ end
646
+
647
+ def expr
648
+ return unless t = term
649
+ res = [t, str{ss}]
650
+
651
+ while (o = operator) && (t = term)
652
+ res << o << t << str{ss}
653
+ end
654
+
655
+ res
656
+ end
657
+
658
+ def term
659
+ unless e = tok(NUMBER) ||
660
+ tok(URI) ||
661
+ function ||
662
+ tok(STRING) ||
663
+ tok(UNICODERANGE) ||
664
+ tok(IDENT) ||
665
+ tok(HEXCOLOR)
666
+
667
+ return unless op = unary_operator
668
+ @expected = "number or function"
669
+ return [op, tok(NUMBER) || expr!(:function)]
670
+ end
671
+ e
672
+ end
673
+
674
+ def function
675
+ return unless name = tok(FUNCTION)
676
+ if name == "expression(" || name == "calc("
677
+ str, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
678
+ [name, str]
679
+ else
680
+ [name, str{ss}, expr, tok!(/\)/)]
681
+ end
682
+ end
683
+
684
+ def interpolation
685
+ return unless tok(INTERP_START)
686
+ sass_script(:parse_interpolated)
687
+ end
688
+
689
+ def interp_string
690
+ _interp_string(:double) || _interp_string(:single)
691
+ end
692
+
693
+ def _interp_string(type)
694
+ return unless start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, false]])
695
+ res = [start]
696
+
697
+ mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, true]]
698
+ # @scanner[2].empty? means we've started an interpolated section
699
+ while @scanner[2] == '#{'
700
+ @scanner.pos -= 2 # Don't consume the #{
701
+ res.last.slice!(-2..-1)
702
+ res << expr!(:interpolation) << tok(mid_re)
703
+ end
704
+ res
705
+ end
706
+
707
+ def interp_ident(start = IDENT)
708
+ return unless val = tok(start) || interpolation
709
+ res = [val]
710
+ while val = tok(NAME) || interpolation
711
+ res << val
712
+ end
713
+ res
714
+ end
715
+
716
+ def interp_name
717
+ interp_ident NAME
718
+ end
719
+
720
+ def str
721
+ @strs.push ""
722
+ yield
723
+ @strs.last
724
+ ensure
725
+ @strs.pop
726
+ end
727
+
728
+ def str?
729
+ @strs.push ""
730
+ yield && @strs.last
731
+ ensure
732
+ @strs.pop
733
+ end
734
+
735
+ def node(node)
736
+ node.line = @line
737
+ node
738
+ end
739
+
740
+ @sass_script_parser = Class.new(Sass::Script::Parser)
741
+ @sass_script_parser.send(:include, ScriptParser)
742
+ # @private
743
+ def self.sass_script_parser; @sass_script_parser; end
744
+
745
+ def sass_script(*args)
746
+ parser = self.class.sass_script_parser.new(@scanner, @line,
747
+ @scanner.pos - (@scanner.string[0...@scanner.pos].rindex("\n") || 0))
748
+ result = parser.send(*args)
749
+ @line = parser.line
750
+ result
751
+ end
752
+
753
+ def merge(arr)
754
+ arr && Sass::Util.merge_adjacent_strings([arr].flatten)
755
+ end
756
+
757
+ EXPR_NAMES = {
758
+ :media_query => "media query (e.g. print, screen, print and screen)",
759
+ :media_expr => "media expression (e.g. (min-device-width: 800px)))",
760
+ :pseudo_expr => "expression (e.g. fr, 2n+1)",
761
+ :interp_ident => "identifier",
762
+ :interp_name => "identifier",
763
+ :expr => "expression (e.g. 1px, bold)",
764
+ :selector_comma_sequence => "selector",
765
+ :simple_selector_sequence => "selector",
766
+ :import_arg => "file to import (string or url())",
767
+ }
768
+
769
+ TOK_NAMES = Sass::Util.to_hash(
770
+ Sass::SCSS::RX.constants.map {|c| [Sass::SCSS::RX.const_get(c), c.downcase]}).
771
+ merge(IDENT => "identifier", /[;}]/ => '";"')
772
+
773
+ def tok?(rx)
774
+ @scanner.match?(rx)
775
+ end
776
+
777
+ def expr!(name)
778
+ (e = send(name)) && (return e)
779
+ expected(EXPR_NAMES[name] || name.to_s)
780
+ end
781
+
782
+ def tok!(rx)
783
+ (t = tok(rx)) && (return t)
784
+ name = TOK_NAMES[rx]
785
+
786
+ unless name
787
+ # Display basic regexps as plain old strings
788
+ string = rx.source.gsub(/\\(.)/, '\1')
789
+ name = rx.source == Regexp.escape(string) ? string.inspect : rx.inspect
790
+ end
791
+
792
+ expected(name)
793
+ end
794
+
795
+ def expected(name)
796
+ self.class.expected(@scanner, @expected || name, @line)
797
+ end
798
+
799
+ # @private
800
+ def self.expected(scanner, expected, line)
801
+ pos = scanner.pos
802
+
803
+ after = scanner.string[0...pos]
804
+ # Get rid of whitespace between pos and the last token,
805
+ # but only if there's a newline in there
806
+ after.gsub!(/\s*\n\s*$/, '')
807
+ # Also get rid of stuff before the last newline
808
+ after.gsub!(/.*\n/, '')
809
+ after = "..." + after[-15..-1] if after.size > 18
810
+
811
+ was = scanner.rest.dup
812
+ # Get rid of whitespace between pos and the next token,
813
+ # but only if there's a newline in there
814
+ was.gsub!(/^\s*\n\s*/, '')
815
+ # Also get rid of stuff after the next newline
816
+ was.gsub!(/\n.*/, '')
817
+ was = was[0...15] + "..." if was.size > 18
818
+
819
+ raise Sass::SyntaxError.new(
820
+ "Invalid CSS after \"#{after}\": expected #{expected}, was \"#{was}\"",
821
+ :line => line)
822
+ end
823
+
824
+ def tok(rx)
825
+ res = @scanner.scan(rx)
826
+ if res
827
+ @line += res.count("\n")
828
+ @expected = nil
829
+ if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
830
+ @strs.each {|s| s << res}
831
+ end
832
+ end
833
+
834
+ res
835
+ end
836
+ end
837
+ end
838
+ end