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/scss/parser.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'strscan'
|
2
1
|
require 'set'
|
3
2
|
|
4
3
|
module Sass
|
@@ -6,14 +5,30 @@ module Sass
|
|
6
5
|
# The parser for SCSS.
|
7
6
|
# It parses a string of code into a tree of {Sass::Tree::Node}s.
|
8
7
|
class Parser
|
8
|
+
# Expose for the SASS parser.
|
9
|
+
attr_accessor :offset
|
10
|
+
|
9
11
|
# @param str [String, StringScanner] The source document to parse.
|
10
12
|
# Note that `Parser` *won't* raise a nice error message if this isn't properly parsed;
|
11
13
|
# for that, you should use the higher-level {Sass::Engine} or {Sass::CSS}.
|
12
|
-
# @param
|
13
|
-
#
|
14
|
-
|
14
|
+
# @param filename [String] The name of the file being parsed. Used for
|
15
|
+
# warnings and source maps.
|
16
|
+
# @param importer [Sass::Importers::Base] The importer used to import the
|
17
|
+
# file being parsed. Used for source maps.
|
18
|
+
# @param line [Fixnum] The 1-based line on which the source string appeared,
|
19
|
+
# if it's part of another document.
|
20
|
+
# @param offset [Fixnum] The 1-based character (not byte) offset in the line on
|
21
|
+
# which the source string starts. Used for error reporting and sourcemap
|
22
|
+
# building.
|
23
|
+
# @comment
|
24
|
+
# rubocop:disable ParameterLists
|
25
|
+
def initialize(str, filename, importer, line = 1, offset = 1)
|
26
|
+
# rubocop:enable ParameterLists
|
15
27
|
@template = str
|
28
|
+
@filename = filename
|
29
|
+
@importer = importer
|
16
30
|
@line = line
|
31
|
+
@offset = offset
|
17
32
|
@strs = []
|
18
33
|
end
|
19
34
|
|
@@ -32,28 +47,72 @@ module Sass
|
|
32
47
|
# Note that this won't assert that the identifier takes up the entire input string;
|
33
48
|
# it's meant to be used with `StringScanner`s as part of other parsers.
|
34
49
|
#
|
35
|
-
# @return [Array<String, Sass::Script::Node>, nil]
|
50
|
+
# @return [Array<String, Sass::Script::Tree::Node>, nil]
|
36
51
|
# The interpolated identifier, or nil if none could be parsed
|
37
52
|
def parse_interp_ident
|
38
53
|
init_scanner!
|
39
54
|
interp_ident
|
40
55
|
end
|
41
56
|
|
57
|
+
# Parses a media query list.
|
58
|
+
#
|
59
|
+
# @return [Sass::Media::QueryList] The parsed query list
|
60
|
+
# @raise [Sass::SyntaxError] if there's a syntax error in the query list,
|
61
|
+
# or if it doesn't take up the entire input string.
|
62
|
+
def parse_media_query_list
|
63
|
+
init_scanner!
|
64
|
+
ql = media_query_list
|
65
|
+
expected("media query list") unless @scanner.eos?
|
66
|
+
ql
|
67
|
+
end
|
68
|
+
|
69
|
+
# Parses an at-root query.
|
70
|
+
#
|
71
|
+
# @return [Array<String, Sass::Script;:Tree::Node>] The interpolated query.
|
72
|
+
# @raise [Sass::SyntaxError] if there's a syntax error in the query,
|
73
|
+
# or if it doesn't take up the entire input string.
|
74
|
+
def parse_at_root_query
|
75
|
+
init_scanner!
|
76
|
+
query = at_root_query
|
77
|
+
expected("@at-root query list") unless @scanner.eos?
|
78
|
+
query
|
79
|
+
end
|
80
|
+
|
81
|
+
# Parses a supports query condition.
|
82
|
+
#
|
83
|
+
# @return [Sass::Supports::Condition] The parsed condition
|
84
|
+
# @raise [Sass::SyntaxError] if there's a syntax error in the condition,
|
85
|
+
# or if it doesn't take up the entire input string.
|
86
|
+
def parse_supports_condition
|
87
|
+
init_scanner!
|
88
|
+
condition = supports_condition
|
89
|
+
expected("supports condition") unless @scanner.eos?
|
90
|
+
condition
|
91
|
+
end
|
92
|
+
|
42
93
|
private
|
43
94
|
|
44
95
|
include Sass::SCSS::RX
|
45
96
|
|
97
|
+
def source_position
|
98
|
+
Sass::Source::Position.new(@line, @offset)
|
99
|
+
end
|
100
|
+
|
101
|
+
def range(start_pos, end_pos = source_position)
|
102
|
+
Sass::Source::Range.new(start_pos, end_pos, @filename, @importer)
|
103
|
+
end
|
104
|
+
|
46
105
|
def init_scanner!
|
47
106
|
@scanner =
|
48
107
|
if @template.is_a?(StringScanner)
|
49
108
|
@template
|
50
109
|
else
|
51
|
-
|
110
|
+
Sass::Util::MultibyteStringScanner.new(@template.gsub("\r", ""))
|
52
111
|
end
|
53
112
|
end
|
54
113
|
|
55
114
|
def stylesheet
|
56
|
-
node = node(Sass::Tree::RootNode.new(@scanner.string))
|
115
|
+
node = node(Sass::Tree::RootNode.new(@scanner.string), source_position)
|
57
116
|
block_contents(node, :stylesheet) {s(node)}
|
58
117
|
end
|
59
118
|
|
@@ -87,34 +146,61 @@ module Sass
|
|
87
146
|
end
|
88
147
|
|
89
148
|
def process_comment(text, node)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
149
|
+
silent = text =~ %r{\A//}
|
150
|
+
loud = !silent && text =~ %r{\A/[/*]!}
|
151
|
+
line = @line - text.count("\n")
|
152
|
+
|
153
|
+
if silent
|
154
|
+
value = [text.sub(%r{\A\s*//}, '/*').gsub(%r{^\s*//}, ' *') + ' */']
|
155
|
+
else
|
156
|
+
value = Sass::Engine.parse_interp(
|
157
|
+
text, line, @scanner.pos - text.size, :filename => @filename)
|
158
|
+
value.unshift(@scanner.
|
159
|
+
string[0...@scanner.pos].
|
160
|
+
reverse[/.*?\*\/(.*?)($|\Z)/, 1].
|
161
|
+
reverse.gsub(/[^\s]/, ' '))
|
162
|
+
end
|
163
|
+
|
164
|
+
type = if silent
|
165
|
+
:silent
|
166
|
+
elsif loud
|
167
|
+
:loud
|
168
|
+
else
|
169
|
+
:normal
|
170
|
+
end
|
171
|
+
comment = Sass::Tree::CommentNode.new(value, type)
|
172
|
+
comment.line = line
|
98
173
|
node << comment
|
99
174
|
end
|
100
175
|
|
101
176
|
DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,
|
102
|
-
:each, :while, :if, :else, :extend, :import, :media, :charset
|
177
|
+
:each, :while, :if, :else, :extend, :import, :media, :charset, :content,
|
178
|
+
:_moz_document, :at_root]
|
179
|
+
|
180
|
+
PREFIXED_DIRECTIVES = Set[:supports]
|
103
181
|
|
104
182
|
def directive
|
183
|
+
start_pos = source_position
|
105
184
|
return unless tok(/@/)
|
106
185
|
name = tok!(IDENT)
|
107
186
|
ss
|
108
187
|
|
109
|
-
if dir = special_directive(name)
|
188
|
+
if (dir = special_directive(name, start_pos))
|
189
|
+
return dir
|
190
|
+
elsif (dir = prefixed_directive(name, start_pos))
|
110
191
|
return dir
|
111
192
|
end
|
112
193
|
|
113
194
|
# Most at-rules take expressions (e.g. @import),
|
114
|
-
# but some (e.g. @page) take selector-like arguments
|
115
|
-
|
116
|
-
val
|
117
|
-
|
195
|
+
# but some (e.g. @page) take selector-like arguments.
|
196
|
+
# Some take no arguments at all.
|
197
|
+
val = expr || selector
|
198
|
+
val = val ? ["@#{name} "] + Sass::Util.strip_string_array(val) : ["@#{name}"]
|
199
|
+
directive_body(val, start_pos)
|
200
|
+
end
|
201
|
+
|
202
|
+
def directive_body(value, start_pos)
|
203
|
+
node = Sass::Tree::DirectiveNode.new(value)
|
118
204
|
|
119
205
|
if tok(/\{/)
|
120
206
|
node.has_children = true
|
@@ -122,48 +208,65 @@ module Sass
|
|
122
208
|
tok!(/\}/)
|
123
209
|
end
|
124
210
|
|
125
|
-
node
|
211
|
+
node(node, start_pos)
|
126
212
|
end
|
127
213
|
|
128
|
-
def special_directive(name)
|
214
|
+
def special_directive(name, start_pos)
|
129
215
|
sym = name.gsub('-', '_').to_sym
|
130
|
-
DIRECTIVES.include?(sym) && send("#{sym}_directive")
|
216
|
+
DIRECTIVES.include?(sym) && send("#{sym}_directive", start_pos)
|
217
|
+
end
|
218
|
+
|
219
|
+
def prefixed_directive(name, start_pos)
|
220
|
+
sym = name.gsub(/^-[a-z0-9]+-/i, '').gsub('-', '_').to_sym
|
221
|
+
PREFIXED_DIRECTIVES.include?(sym) && send("#{sym}_directive", name, start_pos)
|
131
222
|
end
|
132
223
|
|
133
|
-
def mixin_directive
|
224
|
+
def mixin_directive(start_pos)
|
134
225
|
name = tok! IDENT
|
135
|
-
args = sass_script(:parse_mixin_definition_arglist)
|
226
|
+
args, splat = sass_script(:parse_mixin_definition_arglist)
|
136
227
|
ss
|
137
|
-
block(node(Sass::Tree::MixinDefNode.new(name, args)), :directive)
|
228
|
+
block(node(Sass::Tree::MixinDefNode.new(name, args, splat), start_pos), :directive)
|
138
229
|
end
|
139
230
|
|
140
|
-
def include_directive
|
231
|
+
def include_directive(start_pos)
|
141
232
|
name = tok! IDENT
|
142
|
-
args, keywords = sass_script(:parse_mixin_include_arglist)
|
233
|
+
args, keywords, splat, kwarg_splat = sass_script(:parse_mixin_include_arglist)
|
234
|
+
ss
|
235
|
+
include_node = node(
|
236
|
+
Sass::Tree::MixinNode.new(name, args, keywords, splat, kwarg_splat), start_pos)
|
237
|
+
if tok?(/\{/)
|
238
|
+
include_node.has_children = true
|
239
|
+
block(include_node, :directive)
|
240
|
+
else
|
241
|
+
include_node
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def content_directive(start_pos)
|
143
246
|
ss
|
144
|
-
node(Sass::Tree::
|
247
|
+
node(Sass::Tree::ContentNode.new, start_pos)
|
145
248
|
end
|
146
249
|
|
147
|
-
def function_directive
|
250
|
+
def function_directive(start_pos)
|
148
251
|
name = tok! IDENT
|
149
|
-
args = sass_script(:parse_function_definition_arglist)
|
252
|
+
args, splat = sass_script(:parse_function_definition_arglist)
|
150
253
|
ss
|
151
|
-
block(node(Sass::Tree::FunctionNode.new(name, args)), :function)
|
254
|
+
block(node(Sass::Tree::FunctionNode.new(name, args, splat), start_pos), :function)
|
152
255
|
end
|
153
256
|
|
154
|
-
def return_directive
|
155
|
-
node(Sass::Tree::ReturnNode.new(sass_script(:parse)))
|
257
|
+
def return_directive(start_pos)
|
258
|
+
node(Sass::Tree::ReturnNode.new(sass_script(:parse)), start_pos)
|
156
259
|
end
|
157
260
|
|
158
|
-
def debug_directive
|
159
|
-
node(Sass::Tree::DebugNode.new(sass_script(:parse)))
|
261
|
+
def debug_directive(start_pos)
|
262
|
+
node(Sass::Tree::DebugNode.new(sass_script(:parse)), start_pos)
|
160
263
|
end
|
161
264
|
|
162
|
-
def warn_directive
|
163
|
-
node(Sass::Tree::WarnNode.new(sass_script(:parse)))
|
265
|
+
def warn_directive(start_pos)
|
266
|
+
node(Sass::Tree::WarnNode.new(sass_script(:parse)), start_pos)
|
164
267
|
end
|
165
268
|
|
166
|
-
def for_directive
|
269
|
+
def for_directive(start_pos)
|
167
270
|
tok!(/\$/)
|
168
271
|
var = tok! IDENT
|
169
272
|
ss
|
@@ -177,31 +280,37 @@ module Sass
|
|
177
280
|
to = sass_script(:parse)
|
178
281
|
ss
|
179
282
|
|
180
|
-
block(node(Sass::Tree::ForNode.new(var, from, to, exclusive)), :directive)
|
283
|
+
block(node(Sass::Tree::ForNode.new(var, from, to, exclusive), start_pos), :directive)
|
181
284
|
end
|
182
285
|
|
183
|
-
def each_directive
|
286
|
+
def each_directive(start_pos)
|
184
287
|
tok!(/\$/)
|
185
|
-
|
288
|
+
vars = [tok!(IDENT)]
|
186
289
|
ss
|
290
|
+
while tok(/,/)
|
291
|
+
ss
|
292
|
+
tok!(/\$/)
|
293
|
+
vars << tok!(IDENT)
|
294
|
+
ss
|
295
|
+
end
|
187
296
|
|
188
297
|
tok!(/in/)
|
189
298
|
list = sass_script(:parse)
|
190
299
|
ss
|
191
300
|
|
192
|
-
block(node(Sass::Tree::EachNode.new(
|
301
|
+
block(node(Sass::Tree::EachNode.new(vars, list), start_pos), :directive)
|
193
302
|
end
|
194
303
|
|
195
|
-
def while_directive
|
304
|
+
def while_directive(start_pos)
|
196
305
|
expr = sass_script(:parse)
|
197
306
|
ss
|
198
|
-
block(node(Sass::Tree::WhileNode.new(expr)), :directive)
|
307
|
+
block(node(Sass::Tree::WhileNode.new(expr), start_pos), :directive)
|
199
308
|
end
|
200
309
|
|
201
|
-
def if_directive
|
310
|
+
def if_directive(start_pos)
|
202
311
|
expr = sass_script(:parse)
|
203
312
|
ss
|
204
|
-
node = block(node(Sass::Tree::IfNode.new(expr)), :directive)
|
313
|
+
node = block(node(Sass::Tree::IfNode.new(expr), start_pos), :directive)
|
205
314
|
pos = @scanner.pos
|
206
315
|
line = @line
|
207
316
|
ss
|
@@ -216,10 +325,11 @@ module Sass
|
|
216
325
|
end
|
217
326
|
|
218
327
|
def else_block(node)
|
328
|
+
start_pos = source_position
|
219
329
|
return unless tok(/@else/)
|
220
330
|
ss
|
221
331
|
else_node = block(
|
222
|
-
Sass::Tree::IfNode.new((sass_script(:parse) if tok(/if/))),
|
332
|
+
node(Sass::Tree::IfNode.new((sass_script(:parse) if tok(/if/))), start_pos),
|
223
333
|
:directive)
|
224
334
|
node.add_else(else_node)
|
225
335
|
pos = @scanner.pos
|
@@ -235,107 +345,273 @@ module Sass
|
|
235
345
|
end
|
236
346
|
end
|
237
347
|
|
238
|
-
def else_directive
|
348
|
+
def else_directive(start_pos)
|
239
349
|
err("Invalid CSS: @else must come after @if")
|
240
350
|
end
|
241
351
|
|
242
|
-
def extend_directive
|
243
|
-
|
352
|
+
def extend_directive(start_pos)
|
353
|
+
selector, selector_range = expr!(:selector_sequence)
|
354
|
+
optional = tok(OPTIONAL)
|
355
|
+
ss
|
356
|
+
node(Sass::Tree::ExtendNode.new(selector, !!optional, selector_range), start_pos)
|
244
357
|
end
|
245
358
|
|
246
|
-
def import_directive
|
359
|
+
def import_directive(start_pos)
|
247
360
|
values = []
|
248
361
|
|
249
362
|
loop do
|
250
363
|
values << expr!(:import_arg)
|
251
|
-
break if use_css_import?
|
364
|
+
break if use_css_import?
|
365
|
+
break unless tok(/,/)
|
366
|
+
ss
|
252
367
|
end
|
253
368
|
|
254
|
-
|
369
|
+
values
|
255
370
|
end
|
256
371
|
|
257
372
|
def import_arg
|
258
|
-
|
259
|
-
|
260
|
-
|
373
|
+
start_pos = source_position
|
374
|
+
return unless (str = tok(STRING)) || (uri = tok?(/url\(/i))
|
375
|
+
if uri
|
376
|
+
str = sass_script(:parse_string)
|
377
|
+
ss
|
378
|
+
media = media_query_list
|
379
|
+
ss
|
380
|
+
return node(Tree::CssImportNode.new(str, media.to_a), start_pos)
|
381
|
+
end
|
261
382
|
|
262
|
-
|
383
|
+
path = @scanner[1] || @scanner[2]
|
384
|
+
ss
|
263
385
|
|
264
|
-
|
265
|
-
|
386
|
+
media = media_query_list
|
387
|
+
if path =~ %r{^(https?:)?//} || media || use_css_import?
|
388
|
+
return node(Sass::Tree::CssImportNode.new(str, media.to_a), start_pos)
|
266
389
|
end
|
267
390
|
|
268
|
-
node(Sass::Tree::ImportNode.new(path.strip))
|
391
|
+
node(Sass::Tree::ImportNode.new(path.strip), start_pos)
|
269
392
|
end
|
270
393
|
|
271
394
|
def use_css_import?; false; end
|
272
395
|
|
273
|
-
def media_directive
|
274
|
-
|
275
|
-
block(node(Sass::Tree::MediaNode.new(val)), :directive)
|
396
|
+
def media_directive(start_pos)
|
397
|
+
block(node(Sass::Tree::MediaNode.new(expr!(:media_query_list).to_a), start_pos), :directive)
|
276
398
|
end
|
277
399
|
|
278
400
|
# http://www.w3.org/TR/css3-mediaqueries/#syntax
|
279
401
|
def media_query_list
|
280
|
-
|
402
|
+
query = media_query
|
403
|
+
return unless query
|
404
|
+
queries = [query]
|
281
405
|
|
282
406
|
ss
|
283
407
|
while tok(/,/)
|
284
|
-
ss; expr!(:media_query)
|
408
|
+
ss; queries << expr!(:media_query)
|
285
409
|
end
|
410
|
+
ss
|
286
411
|
|
287
|
-
|
412
|
+
Sass::Media::QueryList.new(queries)
|
288
413
|
end
|
289
414
|
|
290
415
|
def media_query
|
291
|
-
if
|
416
|
+
if (ident1 = interp_ident)
|
292
417
|
ss
|
293
|
-
|
294
|
-
tok!(IDENT)
|
418
|
+
ident2 = interp_ident
|
295
419
|
ss
|
296
|
-
|
297
|
-
|
420
|
+
if ident2 && ident2.length == 1 && ident2[0].is_a?(String) && ident2[0].downcase == 'and'
|
421
|
+
query = Sass::Media::Query.new([], ident1, [])
|
422
|
+
else
|
423
|
+
if ident2
|
424
|
+
query = Sass::Media::Query.new(ident1, ident2, [])
|
425
|
+
else
|
426
|
+
query = Sass::Media::Query.new([], ident1, [])
|
427
|
+
end
|
428
|
+
return query unless tok(/and/i)
|
429
|
+
ss
|
430
|
+
end
|
298
431
|
end
|
299
432
|
|
433
|
+
if query
|
434
|
+
expr = expr!(:media_expr)
|
435
|
+
else
|
436
|
+
expr = media_expr
|
437
|
+
return unless expr
|
438
|
+
end
|
439
|
+
query ||= Sass::Media::Query.new([], [], [])
|
440
|
+
query.expressions << expr
|
441
|
+
|
300
442
|
ss
|
301
443
|
while tok(/and/i)
|
302
|
-
ss; expr!(:media_expr)
|
444
|
+
ss; query.expressions << expr!(:media_expr)
|
303
445
|
end
|
304
446
|
|
305
|
-
|
447
|
+
query
|
306
448
|
end
|
307
449
|
|
308
|
-
def
|
450
|
+
def query_expr
|
451
|
+
interp = interpolation
|
452
|
+
return interp if interp
|
309
453
|
return unless tok(/\(/)
|
454
|
+
res = ['(']
|
310
455
|
ss
|
311
|
-
|
312
|
-
tok!(IDENT)
|
313
|
-
ss
|
456
|
+
res << sass_script(:parse)
|
314
457
|
|
315
458
|
if tok(/:/)
|
316
|
-
|
459
|
+
res << ': '
|
460
|
+
ss
|
461
|
+
res << sass_script(:parse)
|
317
462
|
end
|
318
|
-
tok!(/\)/)
|
463
|
+
res << tok!(/\)/)
|
319
464
|
ss
|
320
|
-
|
321
|
-
true
|
465
|
+
res
|
322
466
|
end
|
323
467
|
|
324
|
-
|
468
|
+
# Aliases allow us to use different descriptions if the same
|
469
|
+
# expression fails in different contexts.
|
470
|
+
alias_method :media_expr, :query_expr
|
471
|
+
alias_method :at_root_query, :query_expr
|
472
|
+
|
473
|
+
def charset_directive(start_pos)
|
325
474
|
tok! STRING
|
326
475
|
name = @scanner[1] || @scanner[2]
|
327
476
|
ss
|
328
|
-
node(Sass::Tree::CharsetNode.new(name))
|
477
|
+
node(Sass::Tree::CharsetNode.new(name), start_pos)
|
478
|
+
end
|
479
|
+
|
480
|
+
# The document directive is specified in
|
481
|
+
# http://www.w3.org/TR/css3-conditional/, but Gecko allows the
|
482
|
+
# `url-prefix` and `domain` functions to omit quotation marks, contrary to
|
483
|
+
# the standard.
|
484
|
+
#
|
485
|
+
# We could parse all document directives according to Mozilla's syntax,
|
486
|
+
# but if someone's using e.g. @-webkit-document we don't want them to
|
487
|
+
# think WebKit works sans quotes.
|
488
|
+
def _moz_document_directive(start_pos)
|
489
|
+
res = ["@-moz-document "]
|
490
|
+
loop do
|
491
|
+
res << str {ss} << expr!(:moz_document_function)
|
492
|
+
if (c = tok(/,/))
|
493
|
+
res << c
|
494
|
+
else
|
495
|
+
break
|
496
|
+
end
|
497
|
+
end
|
498
|
+
directive_body(res.flatten, start_pos)
|
499
|
+
end
|
500
|
+
|
501
|
+
def moz_document_function
|
502
|
+
val = interp_uri || _interp_string(:url_prefix) ||
|
503
|
+
_interp_string(:domain) || function(!:allow_var) || interpolation
|
504
|
+
return unless val
|
505
|
+
ss
|
506
|
+
val
|
507
|
+
end
|
508
|
+
|
509
|
+
def at_root_directive(start_pos)
|
510
|
+
if tok?(/\(/) && (expr = at_root_query)
|
511
|
+
return block(node(Sass::Tree::AtRootNode.new(expr), start_pos), :directive)
|
512
|
+
end
|
513
|
+
|
514
|
+
at_root_node = node(Sass::Tree::AtRootNode.new, start_pos)
|
515
|
+
rule_node = ruleset
|
516
|
+
return block(at_root_node, :stylesheet) unless rule_node
|
517
|
+
at_root_node << rule_node
|
518
|
+
at_root_node
|
519
|
+
end
|
520
|
+
|
521
|
+
def at_root_directive_list
|
522
|
+
return unless (first = tok(IDENT))
|
523
|
+
arr = [first]
|
524
|
+
ss
|
525
|
+
while (e = tok(IDENT))
|
526
|
+
arr << e
|
527
|
+
ss
|
528
|
+
end
|
529
|
+
arr
|
530
|
+
end
|
531
|
+
|
532
|
+
# http://www.w3.org/TR/css3-conditional/
|
533
|
+
def supports_directive(name, start_pos)
|
534
|
+
condition = expr!(:supports_condition)
|
535
|
+
node = Sass::Tree::SupportsNode.new(name, condition)
|
536
|
+
|
537
|
+
tok!(/\{/)
|
538
|
+
node.has_children = true
|
539
|
+
block_contents(node, :directive)
|
540
|
+
tok!(/\}/)
|
541
|
+
|
542
|
+
node(node, start_pos)
|
543
|
+
end
|
544
|
+
|
545
|
+
def supports_condition
|
546
|
+
supports_negation || supports_operator || supports_interpolation
|
547
|
+
end
|
548
|
+
|
549
|
+
def supports_negation
|
550
|
+
return unless tok(/not/i)
|
551
|
+
ss
|
552
|
+
Sass::Supports::Negation.new(expr!(:supports_condition_in_parens))
|
553
|
+
end
|
554
|
+
|
555
|
+
def supports_operator
|
556
|
+
cond = supports_condition_in_parens
|
557
|
+
return unless cond
|
558
|
+
while (op = tok(/and|or/i))
|
559
|
+
ss
|
560
|
+
cond = Sass::Supports::Operator.new(
|
561
|
+
cond, expr!(:supports_condition_in_parens), op)
|
562
|
+
end
|
563
|
+
cond
|
564
|
+
end
|
565
|
+
|
566
|
+
def supports_condition_in_parens
|
567
|
+
interp = supports_interpolation
|
568
|
+
return interp if interp
|
569
|
+
return unless tok(/\(/); ss
|
570
|
+
if (cond = supports_condition)
|
571
|
+
tok!(/\)/); ss
|
572
|
+
cond
|
573
|
+
else
|
574
|
+
name = sass_script(:parse)
|
575
|
+
tok!(/:/); ss
|
576
|
+
value = sass_script(:parse)
|
577
|
+
tok!(/\)/); ss
|
578
|
+
Sass::Supports::Declaration.new(name, value)
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
def supports_declaration_condition
|
583
|
+
return unless tok(/\(/); ss
|
584
|
+
supports_declaration_body
|
585
|
+
end
|
586
|
+
|
587
|
+
def supports_interpolation
|
588
|
+
interp = interpolation
|
589
|
+
return unless interp
|
590
|
+
ss
|
591
|
+
Sass::Supports::Interpolation.new(interp)
|
329
592
|
end
|
330
593
|
|
331
594
|
def variable
|
332
595
|
return unless tok(/\$/)
|
596
|
+
start_pos = source_position
|
333
597
|
name = tok!(IDENT)
|
334
598
|
ss; tok!(/:/); ss
|
335
599
|
|
336
600
|
expr = sass_script(:parse)
|
337
|
-
|
338
|
-
|
601
|
+
while tok(/!/)
|
602
|
+
flag_name = tok!(IDENT)
|
603
|
+
if flag_name == 'default'
|
604
|
+
guarded ||= true
|
605
|
+
elsif flag_name == 'global'
|
606
|
+
global ||= true
|
607
|
+
else
|
608
|
+
raise Sass::SyntaxError.new("Invalid flag \"!#{flag_name}\".", :line => @line)
|
609
|
+
end
|
610
|
+
ss
|
611
|
+
end
|
612
|
+
|
613
|
+
result = Sass::Tree::VariableNode.new(name, expr, guarded, global)
|
614
|
+
node(result, start_pos)
|
339
615
|
end
|
340
616
|
|
341
617
|
def operator
|
@@ -346,13 +622,12 @@ module Sass
|
|
346
622
|
str {ss if tok(/[\/,:.=]/)}
|
347
623
|
end
|
348
624
|
|
349
|
-
def unary_operator
|
350
|
-
tok(/[+-]/)
|
351
|
-
end
|
352
|
-
|
353
625
|
def ruleset
|
354
|
-
|
355
|
-
|
626
|
+
start_pos = source_position
|
627
|
+
rules, source_range = selector_sequence
|
628
|
+
return unless rules
|
629
|
+
block(node(
|
630
|
+
Sass::Tree::RuleNode.new(rules.flatten.compact, source_range), start_pos), :ruleset)
|
356
631
|
end
|
357
632
|
|
358
633
|
def block(node, context)
|
@@ -383,11 +658,11 @@ module Sass
|
|
383
658
|
def has_children?(child_or_array)
|
384
659
|
return false unless child_or_array
|
385
660
|
return child_or_array.last.has_children if child_or_array.is_a?(Array)
|
386
|
-
|
661
|
+
child_or_array.has_children
|
387
662
|
end
|
388
663
|
|
389
664
|
# This is a nasty hack, and the only place in the parser
|
390
|
-
# that requires backtracking.
|
665
|
+
# that requires a large amount of backtracking.
|
391
666
|
# The reason is that we can't figure out if certain strings
|
392
667
|
# are declarations or rulesets with fixed finite lookahead.
|
393
668
|
# For example, "foo:bar baz baz baz..." could be either a property
|
@@ -421,40 +696,46 @@ module Sass
|
|
421
696
|
end
|
422
697
|
|
423
698
|
def selector_sequence
|
424
|
-
|
425
|
-
|
699
|
+
start_pos = source_position
|
700
|
+
if (sel = tok(STATIC_SELECTOR, true))
|
701
|
+
return [sel], range(start_pos)
|
426
702
|
end
|
427
703
|
|
428
704
|
rules = []
|
429
|
-
|
705
|
+
v = selector
|
706
|
+
return unless v
|
430
707
|
rules.concat v
|
431
708
|
|
432
709
|
ws = ''
|
433
710
|
while tok(/,/)
|
434
711
|
ws << str {ss}
|
435
|
-
if v = selector
|
712
|
+
if (v = selector)
|
436
713
|
rules << ',' << ws
|
437
714
|
rules.concat v
|
438
715
|
ws = ''
|
439
716
|
end
|
440
717
|
end
|
441
|
-
rules
|
718
|
+
return rules, range(start_pos)
|
442
719
|
end
|
443
720
|
|
444
721
|
def selector
|
445
|
-
|
722
|
+
sel = _selector
|
723
|
+
return unless sel
|
446
724
|
sel.to_a
|
447
725
|
end
|
448
726
|
|
449
727
|
def selector_comma_sequence
|
450
|
-
|
728
|
+
sel = _selector
|
729
|
+
return unless sel
|
451
730
|
selectors = [sel]
|
452
731
|
ws = ''
|
453
732
|
while tok(/,/)
|
454
|
-
ws << str{ss}
|
455
|
-
if sel = _selector
|
733
|
+
ws << str {ss}
|
734
|
+
if (sel = _selector)
|
456
735
|
selectors << sel
|
457
|
-
|
736
|
+
if ws.include?("\n")
|
737
|
+
selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members)
|
738
|
+
end
|
458
739
|
ws = ''
|
459
740
|
end
|
460
741
|
end
|
@@ -463,54 +744,75 @@ module Sass
|
|
463
744
|
|
464
745
|
def _selector
|
465
746
|
# The combinator here allows the "> E" hack
|
466
|
-
|
467
|
-
|
747
|
+
val = combinator || simple_selector_sequence
|
748
|
+
return unless val
|
749
|
+
nl = str {ss}.include?("\n")
|
468
750
|
res = []
|
469
751
|
res << val
|
470
752
|
res << "\n" if nl
|
471
753
|
|
472
|
-
while val = combinator || simple_selector_sequence
|
754
|
+
while (val = combinator || simple_selector_sequence)
|
473
755
|
res << val
|
474
|
-
res << "\n" if str{ss}.include?("\n")
|
756
|
+
res << "\n" if str {ss}.include?("\n")
|
475
757
|
end
|
476
758
|
Selector::Sequence.new(res.compact)
|
477
759
|
end
|
478
760
|
|
479
761
|
def combinator
|
480
|
-
tok(PLUS) || tok(GREATER) || tok(TILDE)
|
762
|
+
tok(PLUS) || tok(GREATER) || tok(TILDE) || reference_combinator
|
763
|
+
end
|
764
|
+
|
765
|
+
def reference_combinator
|
766
|
+
return unless tok(/\//)
|
767
|
+
res = ['/']
|
768
|
+
ns, name = expr!(:qualified_name)
|
769
|
+
res << ns << '|' if ns
|
770
|
+
res << name << tok!(/\//)
|
771
|
+
res = res.flatten
|
772
|
+
res = res.join '' if res.all? {|e| e.is_a?(String)}
|
773
|
+
res
|
481
774
|
end
|
482
775
|
|
483
776
|
def simple_selector_sequence
|
484
|
-
#
|
485
|
-
|
486
|
-
|
777
|
+
# Returning expr by default allows for stuff like
|
778
|
+
# http://www.w3.org/TR/css3-animations/#keyframes-
|
779
|
+
|
780
|
+
start_pos = source_position
|
781
|
+
e = element_name || id_selector ||
|
782
|
+
class_selector || placeholder_selector || attrib || pseudo ||
|
783
|
+
parent_selector || interpolation_selector
|
784
|
+
return expr(!:allow_var) unless e
|
487
785
|
res = [e]
|
488
786
|
|
489
787
|
# The tok(/\*/) allows the "E*" hack
|
490
|
-
while v =
|
491
|
-
|
492
|
-
|
788
|
+
while (v = id_selector || class_selector || placeholder_selector ||
|
789
|
+
attrib || pseudo || interpolation_selector ||
|
790
|
+
(tok(/\*/) && Selector::Universal.new(nil)))
|
493
791
|
res << v
|
494
792
|
end
|
495
793
|
|
496
|
-
|
794
|
+
pos = @scanner.pos
|
795
|
+
line = @line
|
796
|
+
if (sel = str? {simple_selector_sequence})
|
797
|
+
@scanner.pos = pos
|
798
|
+
@line = line
|
497
799
|
begin
|
498
|
-
|
800
|
+
# If we see "*E", don't force a throw because this could be the
|
801
|
+
# "*prop: val" hack.
|
802
|
+
expected('"{"') if res.length == 1 && res[0].is_a?(Selector::Universal)
|
803
|
+
throw_error {expected('"{"')}
|
499
804
|
rescue Sass::SyntaxError => e
|
500
|
-
e.message << "\n\n"
|
501
|
-
In Sass 3, the parent selector & can only be used where element names are valid,
|
502
|
-
since it could potentially be replaced by an element name.
|
503
|
-
MESSAGE
|
805
|
+
e.message << "\n\n\"#{sel}\" may only be used at the beginning of a compound selector."
|
504
806
|
raise e
|
505
807
|
end
|
506
808
|
end
|
507
809
|
|
508
|
-
Selector::SimpleSequence.new(res)
|
810
|
+
Selector::SimpleSequence.new(res, tok(/!/), range(start_pos))
|
509
811
|
end
|
510
812
|
|
511
813
|
def parent_selector
|
512
814
|
return unless tok(/&/)
|
513
|
-
Selector::Parent.new
|
815
|
+
Selector::Parent.new(interp_ident(NAME) || [])
|
514
816
|
end
|
515
817
|
|
516
818
|
def class_selector
|
@@ -525,13 +827,15 @@ MESSAGE
|
|
525
827
|
Selector::Id.new(merge(expr!(:interp_name)))
|
526
828
|
end
|
527
829
|
|
830
|
+
def placeholder_selector
|
831
|
+
return unless tok(/%/)
|
832
|
+
@expected = "placeholder name"
|
833
|
+
Selector::Placeholder.new(merge(expr!(:interp_ident)))
|
834
|
+
end
|
835
|
+
|
528
836
|
def element_name
|
529
|
-
|
530
|
-
|
531
|
-
@expected = "element name or *"
|
532
|
-
ns = name
|
533
|
-
name = interp_ident || tok!(/\*/)
|
534
|
-
end
|
837
|
+
ns, name = Sass::Util.destructure(qualified_name(:allow_star_name))
|
838
|
+
return unless ns || name
|
535
839
|
|
536
840
|
if name == '*'
|
537
841
|
Selector::Universal.new(merge(ns))
|
@@ -540,9 +844,20 @@ MESSAGE
|
|
540
844
|
end
|
541
845
|
end
|
542
846
|
|
847
|
+
def qualified_name(allow_star_name = false)
|
848
|
+
name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
|
849
|
+
return unless name
|
850
|
+
return nil, name unless tok(/\|/)
|
851
|
+
|
852
|
+
return name, expr!(:interp_ident) unless allow_star_name
|
853
|
+
@expected = "identifier or *"
|
854
|
+
return name, interp_ident || tok!(/\*/)
|
855
|
+
end
|
856
|
+
|
543
857
|
def interpolation_selector
|
544
|
-
|
545
|
-
|
858
|
+
if (script = interpolation)
|
859
|
+
Selector::Interpolation.new(script)
|
860
|
+
end
|
546
861
|
end
|
547
862
|
|
548
863
|
def attrib
|
@@ -551,28 +866,26 @@ MESSAGE
|
|
551
866
|
ns, name = attrib_name!
|
552
867
|
ss
|
553
868
|
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
869
|
+
op = tok(/=/) ||
|
870
|
+
tok(INCLUDES) ||
|
871
|
+
tok(DASHMATCH) ||
|
872
|
+
tok(PREFIXMATCH) ||
|
873
|
+
tok(SUFFIXMATCH) ||
|
874
|
+
tok(SUBSTRINGMATCH)
|
875
|
+
if op
|
560
876
|
@expected = "identifier or string"
|
561
877
|
ss
|
562
|
-
|
563
|
-
val = [val]
|
564
|
-
else
|
565
|
-
val = expr!(:interp_string)
|
566
|
-
end
|
878
|
+
val = interp_ident || expr!(:interp_string)
|
567
879
|
ss
|
568
880
|
end
|
569
|
-
|
881
|
+
flags = interp_ident || interp_string
|
882
|
+
tok!(/\]/)
|
570
883
|
|
571
|
-
Selector::Attribute.new(merge(name), merge(ns), op, merge(val))
|
884
|
+
Selector::Attribute.new(merge(name), merge(ns), op, merge(val), merge(flags))
|
572
885
|
end
|
573
886
|
|
574
887
|
def attrib_name!
|
575
|
-
if name_or_ns = interp_ident
|
888
|
+
if (name_or_ns = interp_ident)
|
576
889
|
# E, E|E
|
577
890
|
if tok(/\|(?!=)/)
|
578
891
|
ns = name_or_ns
|
@@ -590,59 +903,89 @@ MESSAGE
|
|
590
903
|
end
|
591
904
|
|
592
905
|
def pseudo
|
593
|
-
|
906
|
+
s = tok(/::?/)
|
907
|
+
return unless s
|
594
908
|
@expected = "pseudoclass or pseudoelement"
|
595
909
|
name = expr!(:interp_ident)
|
596
910
|
if tok(/\(/)
|
597
911
|
ss
|
598
|
-
arg = expr!(:
|
912
|
+
arg = expr!(:pseudo_arg)
|
913
|
+
while tok(/,/)
|
914
|
+
arg << ',' << str {ss}
|
915
|
+
arg.concat expr!(:pseudo_arg)
|
916
|
+
end
|
599
917
|
tok!(/\)/)
|
600
918
|
end
|
601
919
|
Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
|
602
920
|
end
|
603
921
|
|
604
|
-
def
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
922
|
+
def pseudo_arg
|
923
|
+
# In the CSS spec, every pseudo-class/element either takes a pseudo
|
924
|
+
# expression or a selector comma sequence as an argument. However, we
|
925
|
+
# don't want to have to know which takes which, so we handle both at
|
926
|
+
# once.
|
927
|
+
#
|
928
|
+
# However, there are some ambiguities between the two. For instance, "n"
|
929
|
+
# could start a pseudo expression like "n+1", or it could start a
|
930
|
+
# selector like "n|m". In order to handle this, we must regrettably
|
931
|
+
# backtrack.
|
932
|
+
expr, sel = nil, nil
|
933
|
+
pseudo_err = catch_error do
|
934
|
+
expr = pseudo_expr
|
935
|
+
next if tok?(/[,)]/)
|
936
|
+
expr = nil
|
937
|
+
expected '")"'
|
611
938
|
end
|
612
|
-
|
939
|
+
|
940
|
+
return expr if expr
|
941
|
+
sel_err = catch_error {sel = selector}
|
942
|
+
return sel if sel
|
943
|
+
rethrow pseudo_err if pseudo_err
|
944
|
+
rethrow sel_err if sel_err
|
945
|
+
nil
|
613
946
|
end
|
614
947
|
|
615
|
-
def
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
948
|
+
def pseudo_expr_token
|
949
|
+
tok(PLUS) || tok(/[-*]/) || tok(NUMBER) || interp_string || tok(IDENT) || interpolation
|
950
|
+
end
|
951
|
+
|
952
|
+
def pseudo_expr
|
953
|
+
e = pseudo_expr_token
|
954
|
+
return unless e
|
955
|
+
res = [e, str {ss}]
|
956
|
+
while (e = pseudo_expr_token)
|
957
|
+
res << e << str {ss}
|
958
|
+
end
|
959
|
+
res
|
622
960
|
end
|
623
961
|
|
624
962
|
def declaration
|
625
963
|
# This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
|
626
|
-
|
964
|
+
name_start_pos = source_position
|
965
|
+
if (s = tok(/[:\*\.]|\#(?!\{)/))
|
627
966
|
@use_property_exception = s !~ /[\.\#]/
|
628
|
-
name = [s, str{ss}, *expr!(:interp_ident)]
|
967
|
+
name = [s, str {ss}, *expr!(:interp_ident)]
|
629
968
|
else
|
630
|
-
|
969
|
+
name = interp_ident
|
970
|
+
return unless name
|
631
971
|
name = [name] if name.is_a?(String)
|
632
972
|
end
|
633
|
-
if comment = tok(COMMENT)
|
973
|
+
if (comment = tok(COMMENT))
|
634
974
|
name << comment
|
635
975
|
end
|
976
|
+
name_end_pos = source_position
|
636
977
|
ss
|
637
978
|
|
638
979
|
tok!(/:/)
|
639
|
-
space, value = value!
|
640
|
-
|
641
|
-
important = tok(IMPORTANT)
|
980
|
+
value_start_pos, space, value = value!
|
981
|
+
value_end_pos = source_position
|
642
982
|
ss
|
643
983
|
require_block = tok?(/\{/)
|
644
984
|
|
645
|
-
node = node(Sass::Tree::PropNode.new(name.flatten.compact, value,
|
985
|
+
node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new),
|
986
|
+
name_start_pos, value_end_pos)
|
987
|
+
node.name_source_range = range(name_start_pos, name_end_pos)
|
988
|
+
node.value_source_range = range(value_start_pos, value_end_pos)
|
646
989
|
|
647
990
|
return node unless require_block
|
648
991
|
nested_properties! node, space
|
@@ -650,29 +993,29 @@ MESSAGE
|
|
650
993
|
|
651
994
|
def value!
|
652
995
|
space = !str {ss}.empty?
|
996
|
+
value_start_pos = source_position
|
653
997
|
@use_property_exception ||= space || !tok?(IDENT)
|
654
998
|
|
655
|
-
|
999
|
+
if tok?(/\{/)
|
1000
|
+
str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(""))
|
1001
|
+
str.line = source_position.line
|
1002
|
+
str.source_range = range(source_position)
|
1003
|
+
return value_start_pos, true, str
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
start_pos = source_position
|
656
1007
|
# This is a bit of a dirty trick:
|
657
1008
|
# if the value is completely static,
|
658
1009
|
# we don't parse it at all, and instead return a plain old string
|
659
1010
|
# containing the value.
|
660
1011
|
# This results in a dramatic speed increase.
|
661
|
-
if val = tok(STATIC_VALUE)
|
662
|
-
|
1012
|
+
if (val = tok(STATIC_VALUE, true))
|
1013
|
+
str = Sass::Script::Tree::Literal.new(Sass::Script::Value::String.new(val.strip))
|
1014
|
+
str.line = start_pos.line
|
1015
|
+
str.source_range = range(start_pos)
|
1016
|
+
return value_start_pos, space, str
|
663
1017
|
end
|
664
|
-
return space, sass_script(:parse)
|
665
|
-
end
|
666
|
-
|
667
|
-
def plain_value
|
668
|
-
return unless tok(/:/)
|
669
|
-
space = !str {ss}.empty?
|
670
|
-
@use_property_exception ||= space || !tok?(IDENT)
|
671
|
-
|
672
|
-
expression = expr
|
673
|
-
expression << tok(IMPORTANT) if expression
|
674
|
-
# expression, space, value
|
675
|
-
return expression, space, expression || [""]
|
1018
|
+
return value_start_pos, space, sass_script(:parse)
|
676
1019
|
end
|
677
1020
|
|
678
1021
|
def nested_properties!(node, space)
|
@@ -686,43 +1029,55 @@ MESSAGE
|
|
686
1029
|
block(node, :property)
|
687
1030
|
end
|
688
1031
|
|
689
|
-
def expr
|
690
|
-
|
691
|
-
|
1032
|
+
def expr(allow_var = true)
|
1033
|
+
t = term(allow_var)
|
1034
|
+
return unless t
|
1035
|
+
res = [t, str {ss}]
|
692
1036
|
|
693
|
-
while (o = operator) && (t = term)
|
694
|
-
res << o << t << str{ss}
|
1037
|
+
while (o = operator) && (t = term(allow_var))
|
1038
|
+
res << o << t << str {ss}
|
695
1039
|
end
|
696
1040
|
|
697
|
-
res
|
1041
|
+
res.flatten
|
698
1042
|
end
|
699
1043
|
|
700
|
-
def term
|
701
|
-
|
702
|
-
|
703
|
-
function ||
|
704
|
-
|
1044
|
+
def term(allow_var)
|
1045
|
+
e = tok(NUMBER) ||
|
1046
|
+
interp_uri ||
|
1047
|
+
function(allow_var) ||
|
1048
|
+
interp_string ||
|
705
1049
|
tok(UNICODERANGE) ||
|
706
|
-
|
707
|
-
tok(HEXCOLOR)
|
1050
|
+
interp_ident ||
|
1051
|
+
tok(HEXCOLOR) ||
|
1052
|
+
(allow_var && var_expr)
|
1053
|
+
return e if e
|
708
1054
|
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
1055
|
+
op = tok(/[+-]/)
|
1056
|
+
return unless op
|
1057
|
+
@expected = "number or function"
|
1058
|
+
[op,
|
1059
|
+
tok(NUMBER) || function(allow_var) || (allow_var && var_expr) || expr!(:interpolation)]
|
714
1060
|
end
|
715
1061
|
|
716
|
-
def function
|
717
|
-
|
1062
|
+
def function(allow_var)
|
1063
|
+
name = tok(FUNCTION)
|
1064
|
+
return unless name
|
718
1065
|
if name == "expression(" || name == "calc("
|
719
1066
|
str, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
|
720
1067
|
[name, str]
|
721
1068
|
else
|
722
|
-
[name, str{ss}, expr, tok!(/\)/)]
|
1069
|
+
[name, str {ss}, expr(allow_var), tok!(/\)/)]
|
723
1070
|
end
|
724
1071
|
end
|
725
1072
|
|
1073
|
+
def var_expr
|
1074
|
+
return unless tok(/\$/)
|
1075
|
+
line = @line
|
1076
|
+
var = Sass::Script::Tree::Variable.new(tok!(IDENT))
|
1077
|
+
var.line = line
|
1078
|
+
var
|
1079
|
+
end
|
1080
|
+
|
726
1081
|
def interpolation
|
727
1082
|
return unless tok(INTERP_START)
|
728
1083
|
sass_script(:parse_interpolated)
|
@@ -732,11 +1087,16 @@ MESSAGE
|
|
732
1087
|
_interp_string(:double) || _interp_string(:single)
|
733
1088
|
end
|
734
1089
|
|
1090
|
+
def interp_uri
|
1091
|
+
_interp_string(:uri)
|
1092
|
+
end
|
1093
|
+
|
735
1094
|
def _interp_string(type)
|
736
|
-
|
1095
|
+
start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[type][false])
|
1096
|
+
return unless start
|
737
1097
|
res = [start]
|
738
1098
|
|
739
|
-
mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[
|
1099
|
+
mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[type][true]
|
740
1100
|
# @scanner[2].empty? means we've started an interpolated section
|
741
1101
|
while @scanner[2] == '#{'
|
742
1102
|
@scanner.pos -= 2 # Don't consume the #{
|
@@ -747,14 +1107,22 @@ MESSAGE
|
|
747
1107
|
end
|
748
1108
|
|
749
1109
|
def interp_ident(start = IDENT)
|
750
|
-
|
1110
|
+
val = tok(start) || interpolation || tok(IDENT_HYPHEN_INTERP, true)
|
1111
|
+
return unless val
|
751
1112
|
res = [val]
|
752
|
-
while val = tok(NAME) || interpolation
|
1113
|
+
while (val = tok(NAME) || interpolation)
|
753
1114
|
res << val
|
754
1115
|
end
|
755
1116
|
res
|
756
1117
|
end
|
757
1118
|
|
1119
|
+
def interp_ident_or_var
|
1120
|
+
id = interp_ident
|
1121
|
+
return id if id
|
1122
|
+
var = var_expr
|
1123
|
+
return [var] if var
|
1124
|
+
end
|
1125
|
+
|
758
1126
|
def interp_name
|
759
1127
|
interp_ident NAME
|
760
1128
|
end
|
@@ -768,27 +1136,45 @@ MESSAGE
|
|
768
1136
|
end
|
769
1137
|
|
770
1138
|
def str?
|
1139
|
+
pos = @scanner.pos
|
1140
|
+
line = @line
|
1141
|
+
offset = @offset
|
771
1142
|
@strs.push ""
|
772
|
-
yield && @strs.last
|
1143
|
+
throw_error {yield} && @strs.last
|
1144
|
+
rescue Sass::SyntaxError
|
1145
|
+
@scanner.pos = pos
|
1146
|
+
@line = line
|
1147
|
+
@offset = offset
|
1148
|
+
nil
|
773
1149
|
ensure
|
774
1150
|
@strs.pop
|
775
1151
|
end
|
776
1152
|
|
777
|
-
def node(node)
|
778
|
-
node.line =
|
1153
|
+
def node(node, start_pos, end_pos = source_position)
|
1154
|
+
node.line = start_pos.line
|
1155
|
+
node.source_range = range(start_pos, end_pos)
|
779
1156
|
node
|
780
1157
|
end
|
781
1158
|
|
782
1159
|
@sass_script_parser = Class.new(Sass::Script::Parser)
|
783
1160
|
@sass_script_parser.send(:include, ScriptParser)
|
784
|
-
|
785
|
-
|
1161
|
+
|
1162
|
+
class << self
|
1163
|
+
# @private
|
1164
|
+
attr_accessor :sass_script_parser
|
1165
|
+
end
|
786
1166
|
|
787
1167
|
def sass_script(*args)
|
788
|
-
parser = self.class.sass_script_parser.new(@scanner, @line,
|
789
|
-
|
1168
|
+
parser = self.class.sass_script_parser.new(@scanner, @line, @offset,
|
1169
|
+
:filename => @filename, :importer => @importer)
|
790
1170
|
result = parser.send(*args)
|
1171
|
+
unless @strs.empty?
|
1172
|
+
# Convert to CSS manually so that comments are ignored.
|
1173
|
+
src = result.to_sass
|
1174
|
+
@strs.each {|s| s << src}
|
1175
|
+
end
|
791
1176
|
@line = parser.line
|
1177
|
+
@offset = parser.offset
|
792
1178
|
result
|
793
1179
|
rescue Sass::SyntaxError => e
|
794
1180
|
throw(:_sass_parser_error, true) if @throw_error
|
@@ -801,32 +1187,45 @@ MESSAGE
|
|
801
1187
|
|
802
1188
|
EXPR_NAMES = {
|
803
1189
|
:media_query => "media query (e.g. print, screen, print and screen)",
|
804
|
-
:
|
805
|
-
:
|
1190
|
+
:media_query_list => "media query (e.g. print, screen, print and screen)",
|
1191
|
+
:media_expr => "media expression (e.g. (min-device-width: 800px))",
|
1192
|
+
:at_root_query => "@at-root query (e.g. (without: media))",
|
1193
|
+
:at_root_directive_list => '* or identifier',
|
1194
|
+
:pseudo_arg => "expression (e.g. fr, 2n+1)",
|
806
1195
|
:interp_ident => "identifier",
|
807
1196
|
:interp_name => "identifier",
|
1197
|
+
:qualified_name => "identifier",
|
808
1198
|
:expr => "expression (e.g. 1px, bold)",
|
809
1199
|
:_selector => "selector",
|
810
1200
|
:selector_comma_sequence => "selector",
|
811
1201
|
:simple_selector_sequence => "selector",
|
812
1202
|
:import_arg => "file to import (string or url())",
|
1203
|
+
:moz_document_function => "matching function (e.g. url-prefix(), domain())",
|
1204
|
+
:supports_condition => "@supports condition (e.g. (display: flexbox))",
|
1205
|
+
:supports_condition_in_parens => "@supports condition (e.g. (display: flexbox))",
|
813
1206
|
}
|
814
1207
|
|
815
|
-
TOK_NAMES = Sass::Util.to_hash(
|
816
|
-
|
817
|
-
|
1208
|
+
TOK_NAMES = Sass::Util.to_hash(Sass::SCSS::RX.constants.map do |c|
|
1209
|
+
[Sass::SCSS::RX.const_get(c), c.downcase]
|
1210
|
+
end).merge(
|
1211
|
+
IDENT => "identifier",
|
1212
|
+
/[;}]/ => '";"',
|
1213
|
+
/\b(without|with)\b/ => '"with" or "without"'
|
1214
|
+
)
|
818
1215
|
|
819
1216
|
def tok?(rx)
|
820
1217
|
@scanner.match?(rx)
|
821
1218
|
end
|
822
1219
|
|
823
1220
|
def expr!(name)
|
824
|
-
|
1221
|
+
e = send(name)
|
1222
|
+
return e if e
|
825
1223
|
expected(EXPR_NAMES[name] || name.to_s)
|
826
1224
|
end
|
827
1225
|
|
828
1226
|
def tok!(rx)
|
829
|
-
|
1227
|
+
t = tok(rx)
|
1228
|
+
return t if t
|
830
1229
|
name = TOK_NAMES[rx]
|
831
1230
|
|
832
1231
|
unless name
|
@@ -848,14 +1247,23 @@ MESSAGE
|
|
848
1247
|
raise Sass::SyntaxError.new(msg, :line => @line)
|
849
1248
|
end
|
850
1249
|
|
1250
|
+
def throw_error
|
1251
|
+
old_throw_error, @throw_error = @throw_error, false
|
1252
|
+
yield
|
1253
|
+
ensure
|
1254
|
+
@throw_error = old_throw_error
|
1255
|
+
end
|
1256
|
+
|
851
1257
|
def catch_error(&block)
|
852
1258
|
old_throw_error, @throw_error = @throw_error, true
|
853
1259
|
pos = @scanner.pos
|
854
1260
|
line = @line
|
1261
|
+
offset = @offset
|
855
1262
|
expected = @expected
|
856
|
-
if catch(:_sass_parser_error
|
1263
|
+
if catch(:_sass_parser_error) {yield; false}
|
857
1264
|
@scanner.pos = pos
|
858
1265
|
@line = line
|
1266
|
+
@offset = offset
|
859
1267
|
@expected = expected
|
860
1268
|
{:pos => pos, :line => line, :expected => @expected, :block => block}
|
861
1269
|
end
|
@@ -864,10 +1272,10 @@ MESSAGE
|
|
864
1272
|
end
|
865
1273
|
|
866
1274
|
def rethrow(err)
|
867
|
-
if @
|
1275
|
+
if @throw_error
|
868
1276
|
throw :_sass_parser_error, err
|
869
1277
|
else
|
870
|
-
@scanner =
|
1278
|
+
@scanner = Sass::Util::MultibyteStringScanner.new(@scanner.string)
|
871
1279
|
@scanner.pos = err[:pos]
|
872
1280
|
@line = err[:line]
|
873
1281
|
@expected = err[:expected]
|
@@ -904,10 +1312,29 @@ MESSAGE
|
|
904
1312
|
# This is important because `#tok` is called all the time.
|
905
1313
|
NEWLINE = "\n"
|
906
1314
|
|
907
|
-
def tok(rx)
|
1315
|
+
def tok(rx, last_group_lookahead = false)
|
908
1316
|
res = @scanner.scan(rx)
|
909
1317
|
if res
|
910
|
-
|
1318
|
+
# This fixes https://github.com/nex3/sass/issues/104, which affects
|
1319
|
+
# Ruby 1.8.7 and REE. This fix is to replace the ?= zero-width
|
1320
|
+
# positive lookahead operator in the Regexp (which matches without
|
1321
|
+
# consuming the matched group), with a match that does consume the
|
1322
|
+
# group, but then rewinds the scanner and removes the group from the
|
1323
|
+
# end of the matched string. This fix makes the assumption that the
|
1324
|
+
# matched group will always occur at the end of the match.
|
1325
|
+
if last_group_lookahead && @scanner[-1]
|
1326
|
+
@scanner.pos -= @scanner[-1].length
|
1327
|
+
res.slice!(-@scanner[-1].length..-1)
|
1328
|
+
end
|
1329
|
+
|
1330
|
+
newline_count = res.count(NEWLINE)
|
1331
|
+
if newline_count > 0
|
1332
|
+
@line += newline_count
|
1333
|
+
@offset = res[res.rindex(NEWLINE)..-1].size
|
1334
|
+
else
|
1335
|
+
@offset += res.size
|
1336
|
+
end
|
1337
|
+
|
911
1338
|
@expected = nil
|
912
1339
|
if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
|
913
1340
|
@strs.each {|s| s << res}
|