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,90 @@
1
+ require 'set'
2
+ require 'sass/script/string'
3
+ require 'sass/script/number'
4
+ require 'sass/script/color'
5
+ require 'sass/script/functions'
6
+ require 'sass/script/unary_operation'
7
+ require 'sass/script/interpolation'
8
+ require 'sass/script/string_interpolation'
9
+
10
+ module Sass::Script
11
+ # A SassScript parse node representing a binary operation,
12
+ # such as `$a + $b` or `"foo" + 1`.
13
+ class Operation < Node
14
+ attr_reader :operand1
15
+ attr_reader :operand2
16
+ attr_reader :operator
17
+
18
+ # @param operand1 [Script::Node] The parse-tree node
19
+ # for the right-hand side of the operator
20
+ # @param operand2 [Script::Node] The parse-tree node
21
+ # for the left-hand side of the operator
22
+ # @param operator [Symbol] The operator to perform.
23
+ # This should be one of the binary operator names in {Lexer::OPERATORS}
24
+ def initialize(operand1, operand2, operator)
25
+ @operand1 = operand1
26
+ @operand2 = operand2
27
+ @operator = operator
28
+ super()
29
+ end
30
+
31
+ # @return [String] A human-readable s-expression representation of the operation
32
+ def inspect
33
+ "(#{@operator.inspect} #{@operand1.inspect} #{@operand2.inspect})"
34
+ end
35
+
36
+ # @see Node#to_sass
37
+ def to_sass(opts = {})
38
+ pred = Sass::Script::Parser.precedence_of(@operator)
39
+ o1 = operand_to_sass pred, @operand1, opts
40
+ o2 = operand_to_sass pred, @operand2, opts
41
+ sep =
42
+ case @operator
43
+ when :comma; ", "
44
+ when :concat; " "
45
+ else; " #{Lexer::OPERATORS_REVERSE[@operator]} "
46
+ end
47
+ "#{o1}#{sep}#{o2}"
48
+ end
49
+
50
+ # Returns the operands for this operation.
51
+ #
52
+ # @return [Array<Node>]
53
+ # @see Node#children
54
+ def children
55
+ [@operand1, @operand2]
56
+ end
57
+
58
+ protected
59
+
60
+ # Evaluates the operation.
61
+ #
62
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
63
+ # @return [Literal] The SassScript object that is the value of the operation
64
+ # @raise [Sass::SyntaxError] if the operation is undefined for the operands
65
+ def _perform(environment)
66
+ literal1 = @operand1.perform(environment)
67
+ literal2 = @operand2.perform(environment)
68
+
69
+ if @operator == :concat && context == :equals
70
+ literal1 = Sass::Script::String.new(literal1.value) if literal1.is_a?(Sass::Script::String)
71
+ literal2 = Sass::Script::String.new(literal2.value) if literal2.is_a?(Sass::Script::String)
72
+ end
73
+
74
+ begin
75
+ opts(literal1.send(@operator, literal2))
76
+ rescue NoMethodError => e
77
+ raise e unless e.name.to_s == @operator.to_s
78
+ raise Sass::SyntaxError.new("Undefined operation: \"#{literal1} #{@operator} #{literal2}\".")
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def operand_to_sass(pred, op, opts = {})
85
+ return "(#{op.to_sass(opts)})" if op.is_a?(Operation) &&
86
+ Sass::Script::Parser.precedence_of(op.operator) < pred
87
+ op.to_sass(opts)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,392 @@
1
+ require 'sass/script/lexer'
2
+
3
+ module Sass
4
+ module Script
5
+ # The parser for SassScript.
6
+ # It parses a string of code into a tree of {Script::Node}s.
7
+ class Parser
8
+ # The line number of the parser's current position.
9
+ #
10
+ # @return [Fixnum]
11
+ def line
12
+ @lexer.line
13
+ end
14
+
15
+ # @param str [String, StringScanner] The source text to parse
16
+ # @param line [Fixnum] The line on which the SassScript appears.
17
+ # Used for error reporting
18
+ # @param offset [Fixnum] The number of characters in on which the SassScript appears.
19
+ # Used for error reporting
20
+ # @param options [{Symbol => Object}] An options hash;
21
+ # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
22
+ def initialize(str, line, offset, options = {})
23
+ @options = options
24
+ @lexer = lexer_class.new(str, line, offset, options)
25
+ end
26
+
27
+ # Parses a SassScript expression within an interpolated segment (`#{}`).
28
+ # This means that it stops when it comes across an unmatched `}`,
29
+ # which signals the end of an interpolated segment,
30
+ # it returns rather than throwing an error.
31
+ #
32
+ # @return [Script::Node] The root node of the parse tree
33
+ # @raise [Sass::SyntaxError] if the expression isn't valid SassScript
34
+ def parse_interpolated
35
+ expr = assert_expr :expr
36
+ assert_tok :end_interpolation
37
+ expr.options = @options
38
+ expr
39
+ rescue Sass::SyntaxError => e
40
+ e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
41
+ raise e
42
+ end
43
+
44
+ # Parses a SassScript expression.
45
+ #
46
+ # @return [Script::Node] The root node of the parse tree
47
+ # @raise [Sass::SyntaxError] if the expression isn't valid SassScript
48
+ def parse
49
+ expr = assert_expr :expr
50
+ assert_done
51
+ expr.options = @options
52
+ expr
53
+ rescue Sass::SyntaxError => e
54
+ e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
55
+ raise e
56
+ end
57
+
58
+ # Parses a SassScript expression,
59
+ # ending it when it encounters one of the given identifier tokens.
60
+ #
61
+ # @param [#include?(String)] A set of strings that delimit the expression.
62
+ # @return [Script::Node] The root node of the parse tree
63
+ # @raise [Sass::SyntaxError] if the expression isn't valid SassScript
64
+ def parse_until(tokens)
65
+ @stop_at = tokens
66
+ expr = assert_expr :expr
67
+ assert_done
68
+ expr.options = @options
69
+ expr
70
+ rescue Sass::SyntaxError => e
71
+ e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
72
+ raise e
73
+ end
74
+
75
+ # Parses the argument list for a mixin include.
76
+ #
77
+ # @return [Array<Script::Node>] The root nodes of the arguments.
78
+ # @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
79
+ def parse_mixin_include_arglist
80
+ args = []
81
+
82
+ if try_tok(:lparen)
83
+ args = arglist || args
84
+ assert_tok(:rparen)
85
+ end
86
+ assert_done
87
+
88
+ args.each {|a| a.options = @options}
89
+ args
90
+ rescue Sass::SyntaxError => e
91
+ e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
92
+ raise e
93
+ end
94
+
95
+ # Parses the argument list for a mixin definition.
96
+ #
97
+ # @return [Array<Script::Node>] The root nodes of the arguments.
98
+ # @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
99
+ def parse_mixin_definition_arglist
100
+ args = defn_arglist!(false)
101
+ assert_done
102
+
103
+ args.each do |k, v|
104
+ k.options = @options
105
+ v.options = @options if v
106
+ end
107
+ args
108
+ rescue Sass::SyntaxError => e
109
+ e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
110
+ raise e
111
+ end
112
+
113
+ # Parses a SassScript expression.
114
+ #
115
+ # @overload parse(str, line, offset, filename = nil)
116
+ # @return [Script::Node] The root node of the parse tree
117
+ # @see Parser#initialize
118
+ # @see Parser#parse
119
+ def self.parse(*args)
120
+ new(*args).parse
121
+ end
122
+
123
+ PRECEDENCE = [
124
+ :comma, :single_eq, :concat, :or, :and,
125
+ [:eq, :neq],
126
+ [:gt, :gte, :lt, :lte],
127
+ [:plus, :minus],
128
+ [:times, :div, :mod],
129
+ ]
130
+
131
+ class << self
132
+ # Returns an integer representing the precedence
133
+ # of the given operator.
134
+ # A lower integer indicates a looser binding.
135
+ #
136
+ # @private
137
+ def precedence_of(op)
138
+ PRECEDENCE.each_with_index do |e, i|
139
+ return i if Array(e).include?(op)
140
+ end
141
+ raise "[BUG] Unknown operator #{op}"
142
+ end
143
+
144
+ private
145
+
146
+ # Defines a simple left-associative production.
147
+ # name is the name of the production,
148
+ # sub is the name of the production beneath it,
149
+ # and ops is a list of operators for this precedence level
150
+ def production(name, sub, *ops)
151
+ class_eval <<RUBY
152
+ def #{name}
153
+ interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}) and return interp
154
+ return unless e = #{sub}
155
+ while tok = try_tok(#{ops.map {|o| o.inspect}.join(', ')})
156
+ interp = try_op_before_interp(tok, e) and return interp
157
+ line = @lexer.line
158
+ e = Operation.new(e, assert_expr(#{sub.inspect}), tok.type)
159
+ e.line = line
160
+ end
161
+ e
162
+ end
163
+ RUBY
164
+ end
165
+
166
+ def unary(op, sub)
167
+ class_eval <<RUBY
168
+ def unary_#{op}
169
+ return #{sub} unless tok = try_tok(:#{op})
170
+ interp = try_op_before_interp(tok) and return interp
171
+ line = @lexer.line
172
+ op = UnaryOperation.new(assert_expr(:unary_#{op}), :#{op})
173
+ op.line = line
174
+ op
175
+ end
176
+ RUBY
177
+ end
178
+ end
179
+
180
+ private
181
+
182
+ # @private
183
+ def lexer_class; Lexer; end
184
+
185
+ production :expr, :interpolation, :comma
186
+ production :equals, :interpolation, :single_eq
187
+
188
+ def try_op_before_interp(op, prev = nil)
189
+ return unless @lexer.peek && @lexer.peek.type == :begin_interpolation
190
+ wb = @lexer.whitespace?(op)
191
+ str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
192
+ str.line = @lexer.line
193
+ interp = Script::Interpolation.new(prev, str, nil, wb, !:wa, :originally_text)
194
+ interp.line = @lexer.line
195
+ interpolation(interp)
196
+ end
197
+
198
+ def try_ops_after_interp(ops, name)
199
+ return unless @lexer.after_interpolation?
200
+ return unless op = try_tok(*ops)
201
+ interp = try_op_before_interp(op) and return interp
202
+
203
+ wa = @lexer.whitespace?
204
+ str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
205
+ str.line = @lexer.line
206
+ interp = Script::Interpolation.new(nil, str, assert_expr(name), !:wb, wa, :originally_text)
207
+ interp.line = @lexer.line
208
+ return interp
209
+ end
210
+
211
+ def interpolation(first = concat)
212
+ e = first
213
+ while interp = try_tok(:begin_interpolation)
214
+ wb = @lexer.whitespace?(interp)
215
+ line = @lexer.line
216
+ mid = parse_interpolated
217
+ wa = @lexer.whitespace?
218
+ e = Script::Interpolation.new(e, mid, concat, wb, wa)
219
+ e.line = line
220
+ end
221
+ e
222
+ end
223
+
224
+ def concat
225
+ return unless e = or_expr
226
+ while sub = or_expr
227
+ e = node(Operation.new(e, sub, :concat))
228
+ end
229
+ e
230
+ end
231
+
232
+ production :or_expr, :and_expr, :or
233
+ production :and_expr, :eq_or_neq, :and
234
+ production :eq_or_neq, :relational, :eq, :neq
235
+ production :relational, :plus_or_minus, :gt, :gte, :lt, :lte
236
+ production :plus_or_minus, :times_div_or_mod, :plus, :minus
237
+ production :times_div_or_mod, :unary_plus, :times, :div, :mod
238
+
239
+ unary :plus, :unary_minus
240
+ unary :minus, :unary_div
241
+ unary :div, :unary_not # For strings, so /foo/bar works
242
+ unary :not, :ident
243
+
244
+ def ident
245
+ return funcall unless @lexer.peek && @lexer.peek.type == :ident
246
+ return if @stop_at && @stop_at.include?(@lexer.peek.value)
247
+
248
+ name = @lexer.next
249
+ if color = Color::HTML4_COLORS[name.value.downcase]
250
+ return node(Color.new(color))
251
+ end
252
+ node(Script::String.new(name.value, :identifier))
253
+ end
254
+
255
+ def funcall
256
+ return raw unless tok = try_tok(:funcall)
257
+ args = fn_arglist || []
258
+ assert_tok(:rparen)
259
+ node(Script::Funcall.new(tok.value, args))
260
+ end
261
+
262
+ def defn_arglist!(must_have_default)
263
+ return [] unless try_tok(:lparen)
264
+ return [] if try_tok(:rparen)
265
+ res = []
266
+ loop do
267
+ line = @lexer.line
268
+ offset = @lexer.offset + 1
269
+ c = assert_tok(:const)
270
+ var = Script::Variable.new(c.value)
271
+ if tok = (try_tok(:colon) || try_tok(:single_eq))
272
+ val = assert_expr(:concat)
273
+
274
+ if tok.type == :single_eq
275
+ val.context = :equals
276
+ val.options = @options
277
+ Script.equals_warning("mixin argument defaults", "$#{c.value}",
278
+ val.to_sass, false, line, offset, @options[:filename])
279
+ end
280
+ must_have_default = true
281
+ elsif must_have_default
282
+ raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
283
+ end
284
+ res << [var, val]
285
+ break unless try_tok(:comma)
286
+ end
287
+ assert_tok(:rparen)
288
+ res
289
+ end
290
+
291
+ def fn_arglist
292
+ return unless e = equals
293
+ return [e] unless try_tok(:comma)
294
+ [e, *assert_expr(:fn_arglist)]
295
+ end
296
+
297
+ def arglist
298
+ return unless e = interpolation
299
+ return [e] unless try_tok(:comma)
300
+ [e, *assert_expr(:arglist)]
301
+ end
302
+
303
+ def raw
304
+ return special_fun unless tok = try_tok(:raw)
305
+ node(Script::String.new(tok.value))
306
+ end
307
+
308
+ def special_fun
309
+ return paren unless tok = try_tok(:special_fun)
310
+ first = node(Script::String.new(tok.value.first))
311
+ Sass::Util.enum_slice(tok.value[1..-1], 2).inject(first) do |l, (i, r)|
312
+ Script::Interpolation.new(
313
+ l, i, r && node(Script::String.new(r)),
314
+ false, false)
315
+ end
316
+ end
317
+
318
+ def paren
319
+ return variable unless try_tok(:lparen)
320
+ was_in_parens = @in_parens
321
+ @in_parens = true
322
+ e = assert_expr(:expr)
323
+ assert_tok(:rparen)
324
+ return e
325
+ ensure
326
+ @in_parens = was_in_parens
327
+ end
328
+
329
+ def variable
330
+ return string unless c = try_tok(:const)
331
+ node(Variable.new(*c.value))
332
+ end
333
+
334
+ def string
335
+ return number unless first = try_tok(:string)
336
+ return first.value unless try_tok(:begin_interpolation)
337
+ line = @lexer.line
338
+ mid = parse_interpolated
339
+ last = assert_expr(:string)
340
+ interp = StringInterpolation.new(first.value, mid, last)
341
+ interp.line = line
342
+ interp
343
+ end
344
+
345
+ def number
346
+ return literal unless tok = try_tok(:number)
347
+ num = tok.value
348
+ num.original = num.to_s unless @in_parens
349
+ num
350
+ end
351
+
352
+ def literal
353
+ (t = try_tok(:color, :bool)) && (return t.value)
354
+ end
355
+
356
+ # It would be possible to have unified #assert and #try methods,
357
+ # but detecting the method/token difference turns out to be quite expensive.
358
+
359
+ EXPR_NAMES = {
360
+ :string => "string",
361
+ :default => "expression (e.g. 1px, bold)",
362
+ :arglist => "mixin argument",
363
+ :fn_arglist => "function argument",
364
+ }
365
+
366
+ def assert_expr(name)
367
+ (e = send(name)) && (return e)
368
+ @lexer.expected!(EXPR_NAMES[name] || EXPR_NAMES[:default])
369
+ end
370
+
371
+ def assert_tok(*names)
372
+ (t = try_tok(*names)) && (return t)
373
+ @lexer.expected!(names.map {|tok| Lexer::TOKEN_NAMES[tok] || tok}.join(" or "))
374
+ end
375
+
376
+ def try_tok(*names)
377
+ peeked = @lexer.peek
378
+ peeked && names.include?(peeked.type) && @lexer.next
379
+ end
380
+
381
+ def assert_done
382
+ return if @lexer.done?
383
+ @lexer.expected!(EXPR_NAMES[:default])
384
+ end
385
+
386
+ def node(node)
387
+ node.line = @lexer.line
388
+ node
389
+ end
390
+ end
391
+ end
392
+ end