vendor/sass/lib/sass/scss/parser.rb in haml-3.2.0.alpha.8 vs vendor/sass/lib/sass/scss/parser.rb in haml-3.2.0.alpha.10
- old
+ new
@@ -7,14 +7,16 @@
# It parses a string of code into a tree of {Sass::Tree::Node}s.
class Parser
# @param str [String, StringScanner] The source document to parse.
# Note that `Parser` *won't* raise a nice error message if this isn't properly parsed;
# for that, you should use the higher-level {Sass::Engine} or {Sass::CSS}.
+ # @param filename [String] The name of the file being parsed. Used for warnings.
# @param line [Fixnum] The line on which the source string appeared,
- # if it's part of another document
- def initialize(str, line = 1)
+ # if it's part of another document.
+ def initialize(str, filename, line = 1)
@template = str
+ @filename = filename
@line = line
@strs = []
end
# Parses an SCSS document.
@@ -85,18 +87,32 @@
return unless tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
ss
end
def process_comment(text, node)
- single_line = text =~ /^\/\//
- pre_str = single_line ? "" : @scanner.
- string[0...@scanner.pos].
- reverse[/.*?\*\/(.*?)($|\Z)/, 1].
- reverse.gsub(/[^\s]/, ' ')
- text = text.sub(/^\s*\/\//, '/*').gsub(/^\s*\/\//, ' *') + ' */' if single_line
- comment = Sass::Tree::CommentNode.new(pre_str + text, single_line)
- comment.line = @line - text.count("\n")
+ silent = text =~ /^\/\//
+ line = @line - text.count("\n")
+ if loud = text =~ %r{^/[/*]!}
+ value = Sass::Engine.parse_interp(text, line, @scanner.pos - text.size, :filename => @filename)
+ value[0].slice!(2) # get rid of the "!"
+ else
+ value = [text]
+ end
+
+ if silent
+ value = Sass::Util.with_extracted_values(value) do |str|
+ str.sub(/^\s*\/\//, '/*').gsub(/^\s*\/\//, ' *') + ' */'
+ end
+ else
+ value.unshift(@scanner.
+ string[0...@scanner.pos].
+ reverse[/.*?\*\/(.*?)($|\Z)/, 1].
+ reverse.gsub(/[^\s]/, ' '))
+ end
+
+ comment = Sass::Tree::CommentNode.new(value, silent, loud)
+ comment.line = line
node << comment
end
DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,
:each, :while, :if, :else, :extend, :import, :media, :charset]
@@ -485,25 +501,34 @@
return expr unless e = element_name || id_selector || class_selector ||
attrib || negation || pseudo || parent_selector || interpolation_selector
res = [e]
# The tok(/\*/) allows the "E*" hack
- while v = element_name || id_selector || class_selector ||
- attrib || negation || pseudo || interpolation_selector ||
- (tok(/\*/) && Selector::Universal.new(nil))
+ while v = id_selector || class_selector || attrib || negation || pseudo ||
+ interpolation_selector || (tok(/\*/) && Selector::Universal.new(nil))
res << v
end
- if tok?(/&/)
- begin
- expected('"{"')
- rescue Sass::SyntaxError => e
- e.message << "\n\n" << <<MESSAGE
-In Sass 3, the parent selector & can only be used where element names are valid,
-since it could potentially be replaced by an element name.
+ pos = @scanner.pos
+ line = @line
+ if sel = str? {simple_selector_sequence}
+ @scanner.pos = pos
+ @line = line
+
+ if sel =~ /^&/
+ begin
+ throw_error {expected('"{"')}
+ rescue Sass::SyntaxError => e
+ e.message << "\n\n\"#{sel}\" may only be used at the beginning of a selector."
+ raise e
+ end
+ else
+ Sass::Util.sass_warn(<<MESSAGE)
+DEPRECATION WARNING:
+On line #{@line}#{" of \"#{@filename}\"" if @filename}, after "#{self.class.prior_snippet(@scanner)}"
+Starting in Sass 3.2, "#{sel}" may only be used at the beginning of a selector.
MESSAGE
- raise e
end
end
Selector::SimpleSequence.new(res)
end
@@ -636,15 +661,13 @@
ss
tok!(/:/)
space, value = value!
ss
- important = tok(IMPORTANT)
- ss
require_block = tok?(/\{/)
- node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, !!important, :new))
+ node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new))
return node unless require_block
nested_properties! node, space
end
@@ -745,11 +768,11 @@
end
res
end
def interp_ident(start = IDENT)
- return unless val = tok(start) || interpolation
+ return unless val = tok(start) || interpolation || tok(IDENT_HYPHEN_INTERP)
res = [val]
while val = tok(NAME) || interpolation
res << val
end
res
@@ -765,13 +788,19 @@
@strs.last
ensure
@strs.pop
end
- def str?
+ def str?(&block)
+ pos = @scanner.pos
+ line = @line
@strs.push ""
- yield && @strs.last
+ throw_error(&block) && @strs.last
+ rescue Sass::SyntaxError => e
+ @scanner.pos = pos
+ @line = line
+ nil
ensure
@strs.pop
end
def node(node)
@@ -846,10 +875,17 @@
def err(msg)
throw(:_sass_parser_error, true) if @throw_error
raise Sass::SyntaxError.new(msg, :line => @line)
end
+ def throw_error
+ old_throw_error, @throw_error = @throw_error, false
+ yield
+ ensure
+ @throw_error = old_throw_error
+ end
+
def catch_error(&block)
old_throw_error, @throw_error = @throw_error, true
pos = @scanner.pos
line = @line
expected = @expected
@@ -875,30 +911,34 @@
end
end
# @private
def self.expected(scanner, expected, line)
- pos = scanner.pos
-
- after = scanner.string[0...pos]
- # Get rid of whitespace between pos and the last token,
- # but only if there's a newline in there
- after.gsub!(/\s*\n\s*$/, '')
- # Also get rid of stuff before the last newline
- after.gsub!(/.*\n/, '')
- after = "..." + after[-15..-1] if after.size > 18
-
was = scanner.rest.dup
# Get rid of whitespace between pos and the next token,
# but only if there's a newline in there
was.gsub!(/^\s*\n\s*/, '')
# Also get rid of stuff after the next newline
was.gsub!(/\n.*/, '')
was = was[0...15] + "..." if was.size > 18
raise Sass::SyntaxError.new(
- "Invalid CSS after \"#{after}\": expected #{expected}, was \"#{was}\"",
+ "Invalid CSS after \"#{prior_snippet(scanner)}\": expected #{expected}, was \"#{was}\"",
:line => line)
+ end
+
+ # @private
+ def self.prior_snippet(scanner)
+ pos = scanner.pos
+
+ after = scanner.string[0...pos]
+ # Get rid of whitespace between pos and the last token,
+ # but only if there's a newline in there
+ after.gsub!(/\s*\n\s*$/, '')
+ # Also get rid of stuff before the last newline
+ after.gsub!(/.*\n/, '')
+ after = "..." + after[-15..-1] if after.size > 18
+ after
end
# Avoid allocating lots of new strings for `#tok`.
# This is important because `#tok` is called all the time.
NEWLINE = "\n"