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"