sass 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CONTRIBUTING +1 -1
- data/MIT-LICENSE +2 -2
- data/README.md +29 -17
- data/Rakefile +43 -9
- data/VERSION +1 -1
- data/VERSION_DATE +1 -0
- data/VERSION_NAME +1 -1
- data/bin/sass +6 -1
- data/bin/sass-convert +6 -1
- data/bin/scss +6 -1
- data/ext/mkrf_conf.rb +27 -0
- data/lib/sass/cache_stores/base.rb +7 -3
- data/lib/sass/cache_stores/chain.rb +3 -2
- data/lib/sass/cache_stores/filesystem.rb +5 -7
- data/lib/sass/cache_stores/memory.rb +1 -1
- data/lib/sass/cache_stores/null.rb +2 -2
- data/lib/sass/callbacks.rb +2 -1
- data/lib/sass/css.rb +168 -53
- data/lib/sass/engine.rb +502 -174
- data/lib/sass/environment.rb +151 -111
- data/lib/sass/error.rb +7 -7
- data/lib/sass/exec.rb +176 -60
- data/lib/sass/features.rb +40 -0
- data/lib/sass/importers/base.rb +46 -7
- data/lib/sass/importers/deprecated_path.rb +51 -0
- data/lib/sass/importers/filesystem.rb +113 -30
- data/lib/sass/importers.rb +1 -0
- data/lib/sass/logger/base.rb +30 -0
- data/lib/sass/logger/log_level.rb +45 -0
- data/lib/sass/logger.rb +12 -0
- data/lib/sass/media.rb +213 -0
- data/lib/sass/plugin/compiler.rb +194 -104
- data/lib/sass/plugin/configuration.rb +18 -25
- data/lib/sass/plugin/merb.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +37 -11
- data/lib/sass/plugin.rb +10 -13
- data/lib/sass/railtie.rb +2 -1
- data/lib/sass/repl.rb +5 -6
- data/lib/sass/script/css_lexer.rb +8 -4
- data/lib/sass/script/css_parser.rb +5 -2
- data/lib/sass/script/functions.rb +1547 -618
- data/lib/sass/script/lexer.rb +122 -72
- data/lib/sass/script/parser.rb +304 -135
- data/lib/sass/script/tree/funcall.rb +306 -0
- data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +43 -13
- data/lib/sass/script/tree/list_literal.rb +77 -0
- data/lib/sass/script/tree/literal.rb +45 -0
- data/lib/sass/script/tree/map_literal.rb +64 -0
- data/lib/sass/script/{node.rb → tree/node.rb} +30 -12
- data/lib/sass/script/{operation.rb → tree/operation.rb} +33 -21
- data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +14 -4
- data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +21 -9
- data/lib/sass/script/tree/variable.rb +57 -0
- data/lib/sass/script/tree.rb +15 -0
- data/lib/sass/script/value/arg_list.rb +36 -0
- data/lib/sass/script/value/base.rb +238 -0
- data/lib/sass/script/value/bool.rb +40 -0
- data/lib/sass/script/{color.rb → value/color.rb} +256 -74
- data/lib/sass/script/value/deprecated_false.rb +55 -0
- data/lib/sass/script/value/helpers.rb +155 -0
- data/lib/sass/script/value/list.rb +128 -0
- data/lib/sass/script/value/map.rb +70 -0
- data/lib/sass/script/value/null.rb +49 -0
- data/lib/sass/script/{number.rb → value/number.rb} +115 -62
- data/lib/sass/script/{string.rb → value/string.rb} +9 -11
- data/lib/sass/script/value.rb +12 -0
- data/lib/sass/script.rb +35 -9
- data/lib/sass/scss/css_parser.rb +2 -12
- data/lib/sass/scss/parser.rb +657 -230
- data/lib/sass/scss/rx.rb +17 -12
- data/lib/sass/scss/static_parser.rb +37 -6
- data/lib/sass/scss.rb +0 -1
- data/lib/sass/selector/abstract_sequence.rb +35 -3
- data/lib/sass/selector/comma_sequence.rb +29 -14
- data/lib/sass/selector/sequence.rb +371 -74
- data/lib/sass/selector/simple.rb +28 -13
- data/lib/sass/selector/simple_sequence.rb +163 -36
- data/lib/sass/selector.rb +138 -36
- data/lib/sass/shared.rb +3 -5
- data/lib/sass/source/map.rb +196 -0
- data/lib/sass/source/position.rb +39 -0
- data/lib/sass/source/range.rb +41 -0
- data/lib/sass/stack.rb +126 -0
- data/lib/sass/supports.rb +228 -0
- data/lib/sass/tree/at_root_node.rb +82 -0
- data/lib/sass/tree/comment_node.rb +34 -29
- data/lib/sass/tree/content_node.rb +9 -0
- data/lib/sass/tree/css_import_node.rb +60 -0
- data/lib/sass/tree/debug_node.rb +3 -3
- data/lib/sass/tree/directive_node.rb +33 -3
- data/lib/sass/tree/each_node.rb +9 -9
- data/lib/sass/tree/extend_node.rb +20 -6
- data/lib/sass/tree/for_node.rb +6 -6
- data/lib/sass/tree/function_node.rb +12 -4
- data/lib/sass/tree/if_node.rb +2 -15
- data/lib/sass/tree/import_node.rb +11 -5
- data/lib/sass/tree/media_node.rb +27 -11
- data/lib/sass/tree/mixin_def_node.rb +15 -4
- data/lib/sass/tree/mixin_node.rb +27 -7
- data/lib/sass/tree/node.rb +69 -35
- data/lib/sass/tree/prop_node.rb +47 -31
- data/lib/sass/tree/return_node.rb +4 -3
- data/lib/sass/tree/root_node.rb +20 -4
- data/lib/sass/tree/rule_node.rb +37 -26
- data/lib/sass/tree/supports_node.rb +38 -0
- data/lib/sass/tree/trace_node.rb +33 -0
- data/lib/sass/tree/variable_node.rb +10 -4
- data/lib/sass/tree/visitors/base.rb +5 -8
- data/lib/sass/tree/visitors/check_nesting.rb +67 -52
- data/lib/sass/tree/visitors/convert.rb +134 -53
- data/lib/sass/tree/visitors/cssize.rb +245 -51
- data/lib/sass/tree/visitors/deep_copy.rb +102 -0
- data/lib/sass/tree/visitors/extend.rb +68 -0
- data/lib/sass/tree/visitors/perform.rb +331 -105
- data/lib/sass/tree/visitors/set_options.rb +125 -0
- data/lib/sass/tree/visitors/to_css.rb +259 -95
- data/lib/sass/tree/warn_node.rb +3 -3
- data/lib/sass/tree/while_node.rb +3 -3
- data/lib/sass/util/cross_platform_random.rb +19 -0
- data/lib/sass/util/multibyte_string_scanner.rb +157 -0
- data/lib/sass/util/normalized_map.rb +130 -0
- data/lib/sass/util/ordered_hash.rb +192 -0
- data/lib/sass/util/subset_map.rb +11 -2
- data/lib/sass/util/test.rb +9 -0
- data/lib/sass/util.rb +565 -39
- data/lib/sass/version.rb +27 -15
- data/lib/sass.rb +39 -4
- data/test/sass/cache_test.rb +15 -0
- data/test/sass/compiler_test.rb +223 -0
- data/test/sass/conversion_test.rb +901 -107
- data/test/sass/css2sass_test.rb +94 -0
- data/test/sass/engine_test.rb +1059 -164
- data/test/sass/exec_test.rb +86 -0
- data/test/sass/extend_test.rb +933 -837
- data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
- data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
- data/test/sass/functions_test.rb +995 -136
- data/test/sass/importer_test.rb +338 -18
- data/test/sass/logger_test.rb +58 -0
- data/test/sass/more_results/more_import.css +2 -2
- data/test/sass/plugin_test.rb +114 -30
- data/test/sass/results/cached_import_option.css +3 -0
- data/test/sass/results/filename_fn.css +3 -0
- data/test/sass/results/import.css +2 -2
- data/test/sass/results/import_charset.css +1 -0
- data/test/sass/results/import_charset_1_8.css +1 -0
- data/test/sass/results/import_charset_ibm866.css +1 -0
- data/test/sass/results/import_content.css +1 -0
- data/test/sass/results/script.css +1 -1
- data/test/sass/results/scss_import.css +2 -2
- data/test/sass/results/units.css +2 -2
- data/test/sass/script_conversion_test.rb +43 -1
- data/test/sass/script_test.rb +380 -36
- data/test/sass/scss/css_test.rb +257 -75
- data/test/sass/scss/scss_test.rb +2322 -110
- data/test/sass/source_map_test.rb +887 -0
- data/test/sass/templates/_cached_import_option_partial.scss +1 -0
- data/test/sass/templates/_double_import_loop2.sass +1 -0
- data/test/sass/templates/_filename_fn_import.scss +11 -0
- data/test/sass/templates/_imported_content.sass +3 -0
- data/test/sass/templates/_same_name_different_partiality.scss +1 -0
- data/test/sass/templates/bork5.sass +3 -0
- data/test/sass/templates/cached_import_option.scss +3 -0
- data/test/sass/templates/double_import_loop1.sass +1 -0
- data/test/sass/templates/filename_fn.scss +18 -0
- data/test/sass/templates/import_charset.sass +2 -0
- data/test/sass/templates/import_charset_1_8.sass +2 -0
- data/test/sass/templates/import_charset_ibm866.sass +2 -0
- data/test/sass/templates/import_content.sass +4 -0
- data/test/sass/templates/same_name_different_ext.sass +2 -0
- data/test/sass/templates/same_name_different_ext.scss +1 -0
- data/test/sass/templates/same_name_different_partiality.scss +1 -0
- data/test/sass/templates/single_import_loop.sass +1 -0
- data/test/sass/templates/subdir/import_up1.scss +1 -0
- data/test/sass/templates/subdir/import_up2.scss +1 -0
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
- data/test/sass/util/normalized_map_test.rb +51 -0
- data/test/sass/util_test.rb +183 -0
- data/test/sass/value_helpers_test.rb +181 -0
- data/test/test_helper.rb +45 -5
- data/vendor/listen/CHANGELOG.md +228 -0
- data/vendor/listen/CONTRIBUTING.md +38 -0
- data/vendor/listen/Gemfile +30 -0
- data/vendor/listen/Guardfile +8 -0
- data/vendor/{fssm → listen}/LICENSE +1 -1
- data/vendor/listen/README.md +315 -0
- data/vendor/listen/Rakefile +47 -0
- data/vendor/listen/Vagrantfile +96 -0
- data/vendor/listen/lib/listen/adapter.rb +214 -0
- data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
- data/vendor/listen/lib/listen/adapters/darwin.rb +85 -0
- data/vendor/listen/lib/listen/adapters/linux.rb +113 -0
- data/vendor/listen/lib/listen/adapters/polling.rb +67 -0
- data/vendor/listen/lib/listen/adapters/windows.rb +87 -0
- data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
- data/vendor/listen/lib/listen/directory_record.rb +371 -0
- data/vendor/listen/lib/listen/listener.rb +225 -0
- data/vendor/listen/lib/listen/multi_listener.rb +143 -0
- data/vendor/listen/lib/listen/turnstile.rb +28 -0
- data/vendor/listen/lib/listen/version.rb +3 -0
- data/vendor/listen/lib/listen.rb +40 -0
- data/vendor/listen/listen.gemspec +22 -0
- data/vendor/listen/spec/listen/adapter_spec.rb +183 -0
- data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
- data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
- data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
- data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
- data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
- data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
- data/vendor/listen/spec/listen/directory_record_spec.rb +1225 -0
- data/vendor/listen/spec/listen/listener_spec.rb +169 -0
- data/vendor/listen/spec/listen/multi_listener_spec.rb +174 -0
- data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
- data/vendor/listen/spec/listen_spec.rb +73 -0
- data/vendor/listen/spec/spec_helper.rb +21 -0
- data/vendor/listen/spec/support/adapter_helper.rb +629 -0
- data/vendor/listen/spec/support/directory_record_helper.rb +55 -0
- data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
- data/vendor/listen/spec/support/listeners_helper.rb +156 -0
- data/vendor/listen/spec/support/platform_helper.rb +15 -0
- metadata +344 -271
- data/lib/sass/less.rb +0 -382
- data/lib/sass/script/bool.rb +0 -18
- data/lib/sass/script/funcall.rb +0 -162
- data/lib/sass/script/list.rb +0 -76
- data/lib/sass/script/literal.rb +0 -245
- data/lib/sass/script/variable.rb +0 -54
- data/lib/sass/scss/sass_parser.rb +0 -11
- data/test/sass/less_conversion_test.rb +0 -653
- data/vendor/fssm/README.markdown +0 -55
- data/vendor/fssm/Rakefile +0 -59
- data/vendor/fssm/VERSION.yml +0 -5
- data/vendor/fssm/example.rb +0 -9
- data/vendor/fssm/fssm.gemspec +0 -77
- data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
- data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
- data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
- data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
- data/vendor/fssm/lib/fssm/monitor.rb +0 -26
- data/vendor/fssm/lib/fssm/path.rb +0 -91
- data/vendor/fssm/lib/fssm/pathname.rb +0 -502
- data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
- data/vendor/fssm/lib/fssm/state/file.rb +0 -24
- data/vendor/fssm/lib/fssm/support.rb +0 -63
- data/vendor/fssm/lib/fssm/tree.rb +0 -176
- data/vendor/fssm/lib/fssm.rb +0 -33
- data/vendor/fssm/profile/prof-cache.rb +0 -40
- data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
- data/vendor/fssm/profile/prof-pathname.rb +0 -68
- data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
- data/vendor/fssm/profile/prof.html +0 -2379
- data/vendor/fssm/spec/path_spec.rb +0 -75
- data/vendor/fssm/spec/root/duck/quack.txt +0 -0
- data/vendor/fssm/spec/root/file.css +0 -0
- data/vendor/fssm/spec/root/file.rb +0 -0
- data/vendor/fssm/spec/root/file.yml +0 -0
- data/vendor/fssm/spec/root/moo/cow.txt +0 -0
- data/vendor/fssm/spec/spec_helper.rb +0 -14
data/lib/sass/script/parser.rb
CHANGED
@@ -3,7 +3,7 @@ require 'sass/script/lexer'
|
|
3
3
|
module Sass
|
4
4
|
module Script
|
5
5
|
# The parser for SassScript.
|
6
|
-
# It parses a string of code into a tree of {Script::Node}s.
|
6
|
+
# It parses a string of code into a tree of {Script::Tree::Node}s.
|
7
7
|
class Parser
|
8
8
|
# The line number of the parser's current position.
|
9
9
|
#
|
@@ -12,11 +12,18 @@ module Sass
|
|
12
12
|
@lexer.line
|
13
13
|
end
|
14
14
|
|
15
|
+
# The column number of the parser's current position.
|
16
|
+
#
|
17
|
+
# @return [Fixnum]
|
18
|
+
def offset
|
19
|
+
@lexer.offset
|
20
|
+
end
|
21
|
+
|
15
22
|
# @param str [String, StringScanner] The source text to parse
|
16
23
|
# @param line [Fixnum] The line on which the SassScript appears.
|
17
|
-
# Used for error reporting
|
18
|
-
# @param offset [Fixnum] The
|
19
|
-
# Used for error reporting
|
24
|
+
# Used for error reporting and sourcemap building
|
25
|
+
# @param offset [Fixnum] The character (not byte) offset where the script starts in the line.
|
26
|
+
# Used for error reporting and sourcemap building
|
20
27
|
# @param options [{Symbol => Object}] An options hash;
|
21
28
|
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
|
22
29
|
def initialize(str, line, offset, options = {})
|
@@ -29,7 +36,7 @@ module Sass
|
|
29
36
|
# which signals the end of an interpolated segment,
|
30
37
|
# it returns rather than throwing an error.
|
31
38
|
#
|
32
|
-
# @return [Script::Node] The root node of the parse tree
|
39
|
+
# @return [Script::Tree::Node] The root node of the parse tree
|
33
40
|
# @raise [Sass::SyntaxError] if the expression isn't valid SassScript
|
34
41
|
def parse_interpolated
|
35
42
|
expr = assert_expr :expr
|
@@ -43,7 +50,7 @@ module Sass
|
|
43
50
|
|
44
51
|
# Parses a SassScript expression.
|
45
52
|
#
|
46
|
-
# @return [Script::Node] The root node of the parse tree
|
53
|
+
# @return [Script::Tree::Node] The root node of the parse tree
|
47
54
|
# @raise [Sass::SyntaxError] if the expression isn't valid SassScript
|
48
55
|
def parse
|
49
56
|
expr = assert_expr :expr
|
@@ -58,8 +65,8 @@ module Sass
|
|
58
65
|
# Parses a SassScript expression,
|
59
66
|
# ending it when it encounters one of the given identifier tokens.
|
60
67
|
#
|
61
|
-
# @param [#include?(String)] A set of strings that delimit the expression.
|
62
|
-
# @return [Script::Node] The root node of the parse tree
|
68
|
+
# @param tokens [#include?(String)] A set of strings that delimit the expression.
|
69
|
+
# @return [Script::Tree::Node] The root node of the parse tree
|
63
70
|
# @raise [Sass::SyntaxError] if the expression isn't valid SassScript
|
64
71
|
def parse_until(tokens)
|
65
72
|
@stop_at = tokens
|
@@ -74,21 +81,26 @@ module Sass
|
|
74
81
|
|
75
82
|
# Parses the argument list for a mixin include.
|
76
83
|
#
|
77
|
-
# @return [(Array<Script::Node>,
|
78
|
-
#
|
79
|
-
#
|
84
|
+
# @return [(Array<Script::Tree::Node>,
|
85
|
+
# {String => Script::Tree::Node},
|
86
|
+
# Script::Tree::Node,
|
87
|
+
# Script::Tree::Node)]
|
88
|
+
# The root nodes of the positional arguments, keyword arguments, and
|
89
|
+
# splat argument(s). Keyword arguments are in a hash from names to values.
|
80
90
|
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
|
81
91
|
def parse_mixin_include_arglist
|
82
92
|
args, keywords = [], {}
|
83
93
|
if try_tok(:lparen)
|
84
|
-
args, keywords = mixin_arglist
|
94
|
+
args, keywords, splat, kwarg_splat = mixin_arglist
|
85
95
|
assert_tok(:rparen)
|
86
96
|
end
|
87
97
|
assert_done
|
88
98
|
|
89
99
|
args.each {|a| a.options = @options}
|
90
100
|
keywords.each {|k, v| v.options = @options}
|
91
|
-
|
101
|
+
splat.options = @options if splat
|
102
|
+
kwarg_splat.options = @options if kwarg_splat
|
103
|
+
return args, keywords, splat, kwarg_splat
|
92
104
|
rescue Sass::SyntaxError => e
|
93
105
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
94
106
|
raise e
|
@@ -96,17 +108,19 @@ module Sass
|
|
96
108
|
|
97
109
|
# Parses the argument list for a mixin definition.
|
98
110
|
#
|
99
|
-
# @return [Array<Script::Node
|
111
|
+
# @return [(Array<Script::Tree::Node>, Script::Tree::Node)]
|
112
|
+
# The root nodes of the arguments, and the splat argument.
|
100
113
|
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
|
101
114
|
def parse_mixin_definition_arglist
|
102
|
-
args = defn_arglist!(false)
|
115
|
+
args, splat = defn_arglist!(false)
|
103
116
|
assert_done
|
104
117
|
|
105
118
|
args.each do |k, v|
|
106
119
|
k.options = @options
|
107
120
|
v.options = @options if v
|
108
121
|
end
|
109
|
-
|
122
|
+
splat.options = @options if splat
|
123
|
+
return args, splat
|
110
124
|
rescue Sass::SyntaxError => e
|
111
125
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
112
126
|
raise e
|
@@ -114,17 +128,40 @@ module Sass
|
|
114
128
|
|
115
129
|
# Parses the argument list for a function definition.
|
116
130
|
#
|
117
|
-
# @return [Array<Script::Node
|
131
|
+
# @return [(Array<Script::Tree::Node>, Script::Tree::Node)]
|
132
|
+
# The root nodes of the arguments, and the splat argument.
|
118
133
|
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
|
119
134
|
def parse_function_definition_arglist
|
120
|
-
args = defn_arglist!(true)
|
135
|
+
args, splat = defn_arglist!(true)
|
121
136
|
assert_done
|
122
137
|
|
123
138
|
args.each do |k, v|
|
124
139
|
k.options = @options
|
125
140
|
v.options = @options if v
|
126
141
|
end
|
127
|
-
|
142
|
+
splat.options = @options if splat
|
143
|
+
return args, splat
|
144
|
+
rescue Sass::SyntaxError => e
|
145
|
+
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
146
|
+
raise e
|
147
|
+
end
|
148
|
+
|
149
|
+
# Parse a single string value, possibly containing interpolation.
|
150
|
+
# Doesn't assert that the scanner is finished after parsing.
|
151
|
+
#
|
152
|
+
# @return [Script::Tree::Node] The root node of the parse tree.
|
153
|
+
# @raise [Sass::SyntaxError] if the string isn't valid SassScript
|
154
|
+
def parse_string
|
155
|
+
unless (peek = @lexer.peek) &&
|
156
|
+
(peek.type == :string ||
|
157
|
+
(peek.type == :funcall && peek.value.downcase == 'url'))
|
158
|
+
lexer.expected!("string")
|
159
|
+
end
|
160
|
+
|
161
|
+
expr = assert_expr :funcall
|
162
|
+
expr.options = @options
|
163
|
+
@lexer.unpeek!
|
164
|
+
expr
|
128
165
|
rescue Sass::SyntaxError => e
|
129
166
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
130
167
|
raise e
|
@@ -133,7 +170,7 @@ module Sass
|
|
133
170
|
# Parses a SassScript expression.
|
134
171
|
#
|
135
172
|
# @overload parse(str, line, offset, filename = nil)
|
136
|
-
# @return [Script::Node] The root node of the parse tree
|
173
|
+
# @return [Script::Tree::Node] The root node of the parse tree
|
137
174
|
# @see Parser#initialize
|
138
175
|
# @see Parser#parse
|
139
176
|
def self.parse(*args)
|
@@ -160,7 +197,7 @@ module Sass
|
|
160
197
|
PRECEDENCE.each_with_index do |e, i|
|
161
198
|
return i if Array(e).include?(op)
|
162
199
|
end
|
163
|
-
raise "[BUG] Unknown operator #{op}"
|
200
|
+
raise "[BUG] Unknown operator #{op.inspect}"
|
164
201
|
end
|
165
202
|
|
166
203
|
# Returns whether or not the given operation is associative.
|
@@ -177,15 +214,20 @@ module Sass
|
|
177
214
|
# sub is the name of the production beneath it,
|
178
215
|
# and ops is a list of operators for this precedence level
|
179
216
|
def production(name, sub, *ops)
|
180
|
-
class_eval <<RUBY
|
217
|
+
class_eval <<RUBY, __FILE__, __LINE__ + 1
|
181
218
|
def #{name}
|
182
|
-
interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect})
|
219
|
+
interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect})
|
220
|
+
return interp if interp
|
183
221
|
return unless e = #{sub}
|
184
|
-
while tok =
|
185
|
-
interp = try_op_before_interp(tok, e)
|
186
|
-
|
187
|
-
|
188
|
-
|
222
|
+
while tok = try_toks(#{ops.map {|o| o.inspect}.join(', ')})
|
223
|
+
if interp = try_op_before_interp(tok, e)
|
224
|
+
other_interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}, interp)
|
225
|
+
return interp unless other_interp
|
226
|
+
return other_interp
|
227
|
+
end
|
228
|
+
|
229
|
+
start_pos = source_position
|
230
|
+
e = node(Tree::Operation.new(e, assert_expr(#{sub.inspect}), tok.type), start_pos)
|
189
231
|
end
|
190
232
|
e
|
191
233
|
end
|
@@ -193,14 +235,13 @@ RUBY
|
|
193
235
|
end
|
194
236
|
|
195
237
|
def unary(op, sub)
|
196
|
-
class_eval <<RUBY
|
238
|
+
class_eval <<RUBY, __FILE__, __LINE__ + 1
|
197
239
|
def unary_#{op}
|
198
240
|
return #{sub} unless tok = try_tok(:#{op})
|
199
|
-
interp = try_op_before_interp(tok)
|
200
|
-
|
201
|
-
|
202
|
-
op
|
203
|
-
op
|
241
|
+
interp = try_op_before_interp(tok)
|
242
|
+
return interp if interp
|
243
|
+
start_pos = source_position
|
244
|
+
node(Tree::UnaryOperation.new(assert_expr(:unary_#{op}), :#{op}), start_pos)
|
204
245
|
end
|
205
246
|
RUBY
|
206
247
|
end
|
@@ -208,19 +249,61 @@ RUBY
|
|
208
249
|
|
209
250
|
private
|
210
251
|
|
252
|
+
def source_position
|
253
|
+
Sass::Source::Position.new(line, offset)
|
254
|
+
end
|
255
|
+
|
256
|
+
def range(start_pos, end_pos = source_position)
|
257
|
+
Sass::Source::Range.new(start_pos, end_pos, @options[:filename], @options[:importer])
|
258
|
+
end
|
259
|
+
|
211
260
|
# @private
|
212
261
|
def lexer_class; Lexer; end
|
213
262
|
|
263
|
+
def map
|
264
|
+
start_pos = source_position
|
265
|
+
e = interpolation
|
266
|
+
return unless e
|
267
|
+
return list e, start_pos unless @lexer.peek && @lexer.peek.type == :colon
|
268
|
+
|
269
|
+
pair = map_pair(e)
|
270
|
+
map = node(Sass::Script::Tree::MapLiteral.new([pair]), start_pos)
|
271
|
+
while try_tok(:comma)
|
272
|
+
pair = map_pair
|
273
|
+
return map unless pair
|
274
|
+
map.pairs << pair
|
275
|
+
end
|
276
|
+
map
|
277
|
+
end
|
278
|
+
|
279
|
+
def map_pair(key = nil)
|
280
|
+
return unless key ||= interpolation
|
281
|
+
assert_tok :colon
|
282
|
+
return key, assert_expr(:interpolation)
|
283
|
+
end
|
284
|
+
|
214
285
|
def expr
|
215
|
-
|
216
|
-
|
217
|
-
return unless e
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
286
|
+
start_pos = source_position
|
287
|
+
e = interpolation
|
288
|
+
return unless e
|
289
|
+
list e, start_pos
|
290
|
+
end
|
291
|
+
|
292
|
+
def list(first, start_pos)
|
293
|
+
return first unless @lexer.peek && @lexer.peek.type == :comma
|
294
|
+
|
295
|
+
list = node(Sass::Script::Tree::ListLiteral.new([first], :comma), start_pos)
|
296
|
+
while (tok = try_tok(:comma))
|
297
|
+
element_before_interp = list.elements.length == 1 ? list.elements.first : list
|
298
|
+
if (interp = try_op_before_interp(tok, element_before_interp))
|
299
|
+
other_interp = try_ops_after_interp([:comma], :expr, interp)
|
300
|
+
return interp unless other_interp
|
301
|
+
return other_interp
|
302
|
+
end
|
303
|
+
return list unless (e = interpolation)
|
304
|
+
list.elements << e
|
222
305
|
end
|
223
|
-
|
306
|
+
list
|
224
307
|
end
|
225
308
|
|
226
309
|
production :equals, :interpolation, :single_eq
|
@@ -228,47 +311,57 @@ RUBY
|
|
228
311
|
def try_op_before_interp(op, prev = nil)
|
229
312
|
return unless @lexer.peek && @lexer.peek.type == :begin_interpolation
|
230
313
|
wb = @lexer.whitespace?(op)
|
231
|
-
str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
|
232
|
-
|
233
|
-
interp =
|
234
|
-
|
314
|
+
str = literal_node(Script::Value::String.new(Lexer::OPERATORS_REVERSE[op.type]),
|
315
|
+
op.source_range)
|
316
|
+
interp = node(
|
317
|
+
Script::Tree::Interpolation.new(prev, str, nil, wb, !:wa, :originally_text),
|
318
|
+
(prev || str).source_range.start_pos)
|
235
319
|
interpolation(interp)
|
236
320
|
end
|
237
321
|
|
238
|
-
def try_ops_after_interp(ops, name)
|
322
|
+
def try_ops_after_interp(ops, name, prev = nil)
|
239
323
|
return unless @lexer.after_interpolation?
|
240
|
-
|
241
|
-
|
324
|
+
op = try_toks(*ops)
|
325
|
+
return unless op
|
326
|
+
interp = try_op_before_interp(op, prev)
|
327
|
+
return interp if interp
|
242
328
|
|
243
329
|
wa = @lexer.whitespace?
|
244
|
-
str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
|
330
|
+
str = literal_node(Script::Value::String.new(Lexer::OPERATORS_REVERSE[op.type]),
|
331
|
+
op.source_range)
|
245
332
|
str.line = @lexer.line
|
246
|
-
interp =
|
247
|
-
|
248
|
-
|
333
|
+
interp = node(
|
334
|
+
Script::Tree::Interpolation.new(prev, str, assert_expr(name), !:wb, wa, :originally_text),
|
335
|
+
(prev || str).source_range.start_pos)
|
336
|
+
interp
|
249
337
|
end
|
250
338
|
|
251
339
|
def interpolation(first = space)
|
252
340
|
e = first
|
253
|
-
while interp = try_tok(:begin_interpolation)
|
341
|
+
while (interp = try_tok(:begin_interpolation))
|
254
342
|
wb = @lexer.whitespace?(interp)
|
255
|
-
line = @lexer.line
|
256
343
|
mid = parse_interpolated
|
257
344
|
wa = @lexer.whitespace?
|
258
|
-
e =
|
259
|
-
|
345
|
+
e = node(
|
346
|
+
Script::Tree::Interpolation.new(e, mid, space, wb, wa),
|
347
|
+
(e || mid).source_range.start_pos)
|
260
348
|
end
|
261
349
|
e
|
262
350
|
end
|
263
351
|
|
264
352
|
def space
|
265
|
-
|
266
|
-
|
353
|
+
start_pos = source_position
|
354
|
+
e = or_expr
|
355
|
+
return unless e
|
267
356
|
arr = [e]
|
268
|
-
while e = or_expr
|
357
|
+
while (e = or_expr)
|
269
358
|
arr << e
|
270
359
|
end
|
271
|
-
arr.size == 1
|
360
|
+
if arr.size == 1
|
361
|
+
arr.first
|
362
|
+
else
|
363
|
+
node(Sass::Script::Tree::ListLiteral.new(arr, :space), start_pos)
|
364
|
+
end
|
272
365
|
end
|
273
366
|
|
274
367
|
production :or_expr, :and_expr, :or
|
@@ -288,104 +381,127 @@ RUBY
|
|
288
381
|
return if @stop_at && @stop_at.include?(@lexer.peek.value)
|
289
382
|
|
290
383
|
name = @lexer.next
|
291
|
-
if color = Color::
|
292
|
-
|
384
|
+
if (color = Sass::Script::Value::Color::COLOR_NAMES[name.value.downcase])
|
385
|
+
literal_node(Sass::Script::Value::Color.new(color), name.source_range)
|
386
|
+
elsif name.value == "true"
|
387
|
+
literal_node(Sass::Script::Value::Bool.new(true), name.source_range)
|
388
|
+
elsif name.value == "false"
|
389
|
+
literal_node(Sass::Script::Value::Bool.new(false), name.source_range)
|
390
|
+
elsif name.value == "null"
|
391
|
+
literal_node(Sass::Script::Value::Null.new, name.source_range)
|
392
|
+
else
|
393
|
+
literal_node(Sass::Script::Value::String.new(name.value, :identifier), name.source_range)
|
293
394
|
end
|
294
|
-
node(Script::String.new(name.value, :identifier))
|
295
395
|
end
|
296
396
|
|
297
397
|
def funcall
|
298
|
-
|
299
|
-
|
398
|
+
tok = try_tok(:funcall)
|
399
|
+
return raw unless tok
|
400
|
+
args, keywords, splat, kwarg_splat = fn_arglist
|
300
401
|
assert_tok(:rparen)
|
301
|
-
node(Script::Funcall.new(tok.value, args, keywords)
|
402
|
+
node(Script::Tree::Funcall.new(tok.value, args, keywords, splat, kwarg_splat),
|
403
|
+
tok.source_range.start_pos, source_position)
|
302
404
|
end
|
303
405
|
|
304
406
|
def defn_arglist!(must_have_parens)
|
305
407
|
if must_have_parens
|
306
408
|
assert_tok(:lparen)
|
307
409
|
else
|
308
|
-
return [] unless try_tok(:lparen)
|
410
|
+
return [], nil unless try_tok(:lparen)
|
309
411
|
end
|
310
|
-
return [] if try_tok(:rparen)
|
412
|
+
return [], nil if try_tok(:rparen)
|
311
413
|
|
312
414
|
res = []
|
415
|
+
splat = nil
|
313
416
|
must_have_default = false
|
314
417
|
loop do
|
315
|
-
line = @lexer.line
|
316
|
-
offset = @lexer.offset + 1
|
317
418
|
c = assert_tok(:const)
|
318
|
-
var = Script::Variable.new(c.value)
|
319
|
-
if
|
419
|
+
var = node(Script::Tree::Variable.new(c.value), c.source_range)
|
420
|
+
if try_tok(:colon)
|
320
421
|
val = assert_expr(:space)
|
321
422
|
must_have_default = true
|
423
|
+
elsif try_tok(:splat)
|
424
|
+
splat = var
|
425
|
+
break
|
322
426
|
elsif must_have_default
|
323
|
-
raise SyntaxError.new(
|
427
|
+
raise SyntaxError.new(
|
428
|
+
"Required argument #{var.inspect} must come before any optional arguments.")
|
324
429
|
end
|
325
430
|
res << [var, val]
|
326
431
|
break unless try_tok(:comma)
|
327
432
|
end
|
328
433
|
assert_tok(:rparen)
|
329
|
-
res
|
434
|
+
return res, splat
|
330
435
|
end
|
331
436
|
|
332
437
|
def fn_arglist
|
333
|
-
arglist(:
|
438
|
+
arglist(:equals, "function argument")
|
334
439
|
end
|
335
440
|
|
336
441
|
def mixin_arglist
|
337
|
-
arglist(:
|
442
|
+
arglist(:interpolation, "mixin argument")
|
338
443
|
end
|
339
444
|
|
340
|
-
def arglist(
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
@lexer.expected!("comma") unless name.is_a?(Variable)
|
345
|
-
assert_tok(:colon)
|
346
|
-
keywords = {name.underscored_name => assert_expr(subexpr, EXPR_NAMES[type])}
|
347
|
-
end
|
445
|
+
def arglist(subexpr, description)
|
446
|
+
args = []
|
447
|
+
keywords = Sass::Util::NormalizedMap.new
|
448
|
+
e = send(subexpr)
|
348
449
|
|
349
|
-
unless
|
350
|
-
|
351
|
-
|
352
|
-
|
450
|
+
return [args, keywords] unless e
|
451
|
+
|
452
|
+
splat = nil
|
453
|
+
loop do
|
454
|
+
if @lexer.peek && @lexer.peek.type == :colon
|
455
|
+
name = e
|
456
|
+
@lexer.expected!("comma") unless name.is_a?(Tree::Variable)
|
457
|
+
assert_tok(:colon)
|
458
|
+
value = assert_expr(subexpr, description)
|
459
|
+
|
460
|
+
if keywords[name.name]
|
461
|
+
raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once")
|
462
|
+
end
|
463
|
+
|
464
|
+
keywords[name.name] = value
|
465
|
+
else
|
466
|
+
if try_tok(:splat)
|
467
|
+
return args, keywords, splat, e if splat
|
468
|
+
splat, e = e, nil
|
469
|
+
elsif splat
|
470
|
+
raise SyntaxError.new("Only keyword arguments may follow variable arguments (...).")
|
471
|
+
elsif !keywords.empty?
|
472
|
+
raise SyntaxError.new("Positional arguments must come before keyword arguments.")
|
473
|
+
end
|
353
474
|
|
354
|
-
|
355
|
-
if keywords
|
356
|
-
if other_keywords[name.underscored_name]
|
357
|
-
raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once")
|
475
|
+
args << e if e
|
358
476
|
end
|
359
|
-
return other_args, keywords.merge(other_keywords)
|
360
|
-
else
|
361
|
-
return [e, *other_args], other_keywords
|
362
|
-
end
|
363
|
-
end
|
364
477
|
|
365
|
-
|
366
|
-
|
367
|
-
unless try_tok(:colon)
|
368
|
-
return_tok!
|
369
|
-
return
|
478
|
+
return args, keywords, splat unless try_tok(:comma)
|
479
|
+
e = assert_expr(subexpr, description)
|
370
480
|
end
|
371
|
-
name = var[1]
|
372
|
-
value = interpolation
|
373
|
-
return {name => value} unless try_tok(:comma)
|
374
|
-
{name => value}.merge(assert_expr(:keyword_arglist))
|
375
481
|
end
|
376
482
|
|
377
483
|
def raw
|
378
|
-
|
379
|
-
|
484
|
+
tok = try_tok(:raw)
|
485
|
+
return special_fun unless tok
|
486
|
+
literal_node(Script::Value::String.new(tok.value), tok.source_range)
|
380
487
|
end
|
381
488
|
|
382
489
|
def special_fun
|
383
|
-
|
384
|
-
|
490
|
+
start_pos = source_position
|
491
|
+
tok = try_tok(:special_fun)
|
492
|
+
return paren unless tok
|
493
|
+
first = literal_node(Script::Value::String.new(tok.value.first),
|
494
|
+
start_pos, start_pos.after(tok.value.first))
|
385
495
|
Sass::Util.enum_slice(tok.value[1..-1], 2).inject(first) do |l, (i, r)|
|
386
|
-
|
387
|
-
|
388
|
-
|
496
|
+
end_pos = i.source_range.end_pos
|
497
|
+
end_pos = end_pos.after(r) if r
|
498
|
+
node(
|
499
|
+
Script::Tree::Interpolation.new(
|
500
|
+
l, i,
|
501
|
+
r && literal_node(Script::Value::String.new(r),
|
502
|
+
i.source_range.end_pos, end_pos),
|
503
|
+
false, false),
|
504
|
+
start_pos, end_pos)
|
389
505
|
end
|
390
506
|
end
|
391
507
|
|
@@ -393,39 +509,49 @@ RUBY
|
|
393
509
|
return variable unless try_tok(:lparen)
|
394
510
|
was_in_parens = @in_parens
|
395
511
|
@in_parens = true
|
396
|
-
|
397
|
-
e =
|
512
|
+
start_pos = source_position
|
513
|
+
e = map
|
514
|
+
end_pos = source_position
|
398
515
|
assert_tok(:rparen)
|
399
|
-
return e || node(
|
516
|
+
return e || node(Sass::Script::Tree::ListLiteral.new([], nil), start_pos, end_pos)
|
400
517
|
ensure
|
401
518
|
@in_parens = was_in_parens
|
402
519
|
end
|
403
520
|
|
404
521
|
def variable
|
405
|
-
|
406
|
-
|
522
|
+
start_pos = source_position
|
523
|
+
c = try_tok(:const)
|
524
|
+
return string unless c
|
525
|
+
node(Tree::Variable.new(*c.value), start_pos)
|
407
526
|
end
|
408
527
|
|
409
528
|
def string
|
410
|
-
|
411
|
-
return
|
412
|
-
|
529
|
+
first = try_tok(:string)
|
530
|
+
return number unless first
|
531
|
+
str = literal_node(first.value, first.source_range)
|
532
|
+
return str unless try_tok(:begin_interpolation)
|
413
533
|
mid = parse_interpolated
|
414
534
|
last = assert_expr(:string)
|
415
|
-
|
416
|
-
interp.line = line
|
417
|
-
interp
|
535
|
+
node(Tree::StringInterpolation.new(str, mid, last), first.source_range.start_pos)
|
418
536
|
end
|
419
537
|
|
420
538
|
def number
|
421
|
-
|
539
|
+
tok = try_tok(:number)
|
540
|
+
return selector unless tok
|
422
541
|
num = tok.value
|
423
542
|
num.original = num.to_s unless @in_parens
|
424
|
-
num
|
543
|
+
literal_node(num, tok.source_range.start_pos)
|
544
|
+
end
|
545
|
+
|
546
|
+
def selector
|
547
|
+
tok = try_tok(:selector)
|
548
|
+
return literal unless tok
|
549
|
+
node(tok.value, tok.source_range.start_pos)
|
425
550
|
end
|
426
551
|
|
427
552
|
def literal
|
428
|
-
|
553
|
+
t = try_tok(:color)
|
554
|
+
return literal_node(t.value, t.source_range) if t
|
429
555
|
end
|
430
556
|
|
431
557
|
# It would be possible to have unified #assert and #try methods,
|
@@ -436,20 +562,36 @@ RUBY
|
|
436
562
|
:default => "expression (e.g. 1px, bold)",
|
437
563
|
:mixin_arglist => "mixin argument",
|
438
564
|
:fn_arglist => "function argument",
|
565
|
+
:splat => "...",
|
439
566
|
}
|
440
567
|
|
441
568
|
def assert_expr(name, expected = nil)
|
442
|
-
|
569
|
+
e = send(name)
|
570
|
+
return e if e
|
443
571
|
@lexer.expected!(expected || EXPR_NAMES[name] || EXPR_NAMES[:default])
|
444
572
|
end
|
445
573
|
|
446
|
-
def assert_tok(
|
447
|
-
|
574
|
+
def assert_tok(name)
|
575
|
+
# Avoids an array allocation caused by argument globbing in assert_toks.
|
576
|
+
t = try_tok(name)
|
577
|
+
return t if t
|
578
|
+
@lexer.expected!(Lexer::TOKEN_NAMES[name] || name.to_s)
|
579
|
+
end
|
580
|
+
|
581
|
+
def assert_toks(*names)
|
582
|
+
t = try_toks(*names)
|
583
|
+
return t if t
|
448
584
|
@lexer.expected!(names.map {|tok| Lexer::TOKEN_NAMES[tok] || tok}.join(" or "))
|
449
585
|
end
|
450
586
|
|
451
|
-
def try_tok(
|
452
|
-
|
587
|
+
def try_tok(name)
|
588
|
+
# Avoids an array allocation caused by argument globbing in the try_toks method.
|
589
|
+
peeked = @lexer.peek
|
590
|
+
peeked && name == peeked.type && @lexer.next
|
591
|
+
end
|
592
|
+
|
593
|
+
def try_toks(*names)
|
594
|
+
peeked = @lexer.peek
|
453
595
|
peeked && names.include?(peeked.type) && @lexer.next
|
454
596
|
end
|
455
597
|
|
@@ -458,8 +600,35 @@ RUBY
|
|
458
600
|
@lexer.expected!(EXPR_NAMES[:default])
|
459
601
|
end
|
460
602
|
|
461
|
-
|
462
|
-
|
603
|
+
# @overload node(value, source_range)
|
604
|
+
# @param value [Sass::Script::Value::Base]
|
605
|
+
# @param source_range [Sass::Source::Range]
|
606
|
+
# @overload node(value, start_pos, end_pos = source_position)
|
607
|
+
# @param value [Sass::Script::Value::Base]
|
608
|
+
# @param start_pos [Sass::Source::Position]
|
609
|
+
# @param end_pos [Sass::Source::Position]
|
610
|
+
def literal_node(value, source_range_or_start_pos, end_pos = source_position)
|
611
|
+
node(Sass::Script::Tree::Literal.new(value), source_range_or_start_pos, end_pos)
|
612
|
+
end
|
613
|
+
|
614
|
+
# @overload node(node, source_range)
|
615
|
+
# @param node [Sass::Script::Tree::Node]
|
616
|
+
# @param source_range [Sass::Source::Range]
|
617
|
+
# @overload node(node, start_pos, end_pos = source_position)
|
618
|
+
# @param node [Sass::Script::Tree::Node]
|
619
|
+
# @param start_pos [Sass::Source::Position]
|
620
|
+
# @param end_pos [Sass::Source::Position]
|
621
|
+
def node(node, source_range_or_start_pos, end_pos = source_position)
|
622
|
+
source_range =
|
623
|
+
if source_range_or_start_pos.is_a?(Sass::Source::Range)
|
624
|
+
source_range_or_start_pos
|
625
|
+
else
|
626
|
+
range(source_range_or_start_pos, end_pos)
|
627
|
+
end
|
628
|
+
|
629
|
+
node.line = source_range.start_pos.line
|
630
|
+
node.source_range = source_range
|
631
|
+
node.filename = @options[:filename]
|
463
632
|
node
|
464
633
|
end
|
465
634
|
end
|