Class Sass::SCSS::Parser
In: lib/sass/scss/parser.rb
Parent: Object
Haml::Util Engine Color SyntaxError UnitConversionError StandardError AbstractSequence CommaSequence Sequence SimpleSequence Simple Parent Universal Class SelectorPseudoClass Id Pseudo Attribute Interpolation Element Node Operation Literal UnaryOperation StringInterpolation Funcall Interpolation Variable Lexer CssLexer Number Bool String Parser Parser CssParser EvaluationContext StaticParser SassParser CssParser Node DebugNode IfNode CommentNode ForNode PropNode MixinNode CharsetNode DirectiveNode VariableNode WarnNode ExtendNode RootNode WhileNode MixinDefNode RuleNode Enumerable ImportNode Merb::BootLoader MerbBootLoader Repl CSS Environment Rack StalenessChecker lib/sass/repl.rb lib/sass/css.rb lib/sass/environment.rb lib/sass/error.rb lib/sass/engine.rb lib/sass/selector/simple_sequence.rb lib/sass/selector/abstract_sequence.rb lib/sass/selector/sequence.rb lib/sass/selector/comma_sequence.rb lib/sass/selector/simple.rb lib/sass/selector.rb Selector lib/sass/script/css_parser.rb lib/sass/script/lexer.rb lib/sass/script/color.rb lib/sass/script/string.rb lib/sass/script/unary_operation.rb lib/sass/script/variable.rb lib/sass/script/funcall.rb lib/sass/script/string_interpolation.rb lib/sass/script/operation.rb lib/sass/script/bool.rb lib/sass/script/parser.rb lib/sass/script/node.rb lib/sass/script/literal.rb lib/sass/script/interpolation.rb lib/sass/script/css_lexer.rb lib/sass/script/number.rb lib/sass/script/functions.rb Functions Script lib/sass/scss/sass_parser.rb lib/sass/scss/static_parser.rb lib/sass/scss/parser.rb lib/sass/scss/css_parser.rb ScriptLexer ScriptParser RX SCSS Callbacks Files lib/sass/tree/while_node.rb lib/sass/tree/if_node.rb lib/sass/tree/mixin_def_node.rb lib/sass/tree/debug_node.rb lib/sass/tree/root_node.rb lib/sass/tree/for_node.rb lib/sass/tree/import_node.rb lib/sass/tree/prop_node.rb lib/sass/tree/node.rb lib/sass/tree/comment_node.rb lib/sass/tree/extend_node.rb lib/sass/tree/charset_node.rb lib/sass/tree/mixin_node.rb lib/sass/tree/warn_node.rb lib/sass/tree/directive_node.rb lib/sass/tree/rule_node.rb lib/sass/tree/variable_node.rb Tree lib/sass/plugin/rack.rb lib/sass/plugin/staleness_checker.rb lib/sass/plugin/merb.rb Plugin Sass dot/m_86_0.png

The parser for SCSS. It parses a string of code into a tree of {Sass::Tree::Node}s.

Methods

Included Modules

Sass::SCSS::RX

Constants

DIRECTIVES = Set[:mixin, :include, :debug, :warn, :for, :while, :if, :else, :extend, :import, :media, :charset]
EXPR_NAMES = { :media_query => "media query (e.g. print, screen, print and screen)", :media_expr => "media expression (e.g. (min-device-width: 800px)))", :pseudo_expr => "expression (e.g. fr, 2n+1)", :interp_ident => "identifier", :interp_name => "identifier", :expr => "expression (e.g. 1px, bold)", :selector_comma_sequence => "selector", :simple_selector_sequence => "selector", :import_arg => "file to import (string or url())", }
TOK_NAMES = Haml::Util.to_hash( Sass::SCSS::RX.constants.map {|c| [Sass::SCSS::RX.const_get(c), c.downcase]}). merge(IDENT => "identifier", /[;}]/ => '";"')

Public Class methods

@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 line [Fixnum] The line on which the source string appeared,

  if it's part of another document

[Source]

    # File lib/sass/scss/parser.rb, line 14
14:       def initialize(str, line = 1)
15:         @template = str
16:         @line = line
17:         @strs = []
18:       end

Private Class methods

@private

[Source]

     # File lib/sass/scss/parser.rb, line 819
819:       def self.expected(scanner, expected, line)
820:         pos = scanner.pos
821: 
822:         after = scanner.string[0...pos]
823:         # Get rid of whitespace between pos and the last token,
824:         # but only if there's a newline in there
825:         after.gsub!(/\s*\n\s*$/, '')
826:         # Also get rid of stuff before the last newline
827:         after.gsub!(/.*\n/, '')
828:         after = "..." + after[-15..-1] if after.size > 18
829: 
830:         was = scanner.rest.dup
831:         # Get rid of whitespace between pos and the next token,
832:         # but only if there's a newline in there
833:         was.gsub!(/^\s*\n\s*/, '')
834:         # Also get rid of stuff after the next newline
835:         was.gsub!(/\n.*/, '')
836:         was = was[0...15] + "..." if was.size > 18
837: 
838:         raise Sass::SyntaxError.new(
839:           "Invalid CSS after \"#{after}\": expected #{expected}, was \"#{was}\"",
840:           :line => line)
841:       end

@private

[Source]

     # File lib/sass/scss/parser.rb, line 762
762:       def self.sass_script_parser; @sass_script_parser; end

Public Instance methods

Parses an SCSS document.

@return [Sass::Tree::RootNode] The root node of the document tree @raise [Sass::SyntaxError] if there‘s a syntax error in the document

[Source]

    # File lib/sass/scss/parser.rb, line 24
24:       def parse
25:         init_scanner!
26:         root = stylesheet
27:         expected("selector or at-rule") unless @scanner.eos?
28:         root
29:       end

Parses an identifier with interpolation. Note that this won‘t assert that the identifier takes up the entire input string; it‘s meant to be used with `StringScanner`s as part of other parsers.

@return [Array<String, Sass::Script::Node>, nil]

  The interpolated identifier, or nil if none could be parsed

[Source]

    # File lib/sass/scss/parser.rb, line 37
37:       def parse_interp_ident
38:         init_scanner!
39:         interp_ident
40:       end

Private Instance methods

[Source]

     # File lib/sass/scss/parser.rb, line 712
712:       def _interp_string(type)
713:         return unless start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, false]])
714:         res = [start]
715: 
716:         mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, true]]
717:         # @scanner[2].empty? means we've started an interpolated section
718:         while @scanner[2] == '#{'
719:           @scanner.pos -= 2 # Don't consume the #{
720:           res.last.slice!(-2..-1)
721:           res << expr!(:interpolation) << tok(mid_re)
722:         end
723:         res
724:       end

[Source]

     # File lib/sass/scss/parser.rb, line 441
441:       def _selector
442:         # The combinator here allows the "> E" hack
443:         return unless val = combinator || simple_selector_sequence
444:         nl = str{ss}.include?("\n")
445:         res = []
446:         res << val
447:         res << "\n" if nl
448: 
449:         while val = combinator || simple_selector_sequence
450:           res << val
451:           res << "\n" if str{ss}.include?("\n")
452:         end
453:         Selector::Sequence.new(res.compact)
454:       end

[Source]

     # File lib/sass/scss/parser.rb, line 526
526:       def attrib
527:         return unless tok(/\[/)
528:         ss
529:         ns, name = attrib_name!
530:         ss
531: 
532:         if op = tok(/=/) ||
533:             tok(INCLUDES) ||
534:             tok(DASHMATCH) ||
535:             tok(PREFIXMATCH) ||
536:             tok(SUFFIXMATCH) ||
537:             tok(SUBSTRINGMATCH)
538:           @expected = "identifier or string"
539:           ss
540:           if val = tok(IDENT)
541:             val = [val]
542:           else
543:             val = expr!(:interp_string)
544:           end
545:           ss
546:         end
547:         tok(/\]/)
548: 
549:         Selector::Attribute.new(merge(name), merge(ns), op, merge(val))
550:       end

[Source]

     # File lib/sass/scss/parser.rb, line 552
552:       def attrib_name!
553:         if name_or_ns = interp_ident
554:           # E, E|E
555:           if tok(/\|(?!=)/)
556:             ns = name_or_ns
557:             name = interp_ident
558:           else
559:             name = name_or_ns
560:           end
561:         else
562:           # *|E or |E
563:           ns = [tok(/\*/) || ""]
564:           tok!(/\|/)
565:           name = expr!(:interp_ident)
566:         end
567:         return ns, name
568:       end

[Source]

     # File lib/sass/scss/parser.rb, line 336
336:       def block(node, context)
337:         node.has_children = true
338:         tok!(/\{/)
339:         block_contents(node, context)
340:         tok!(/\}/)
341:         node
342:       end

[Source]

     # File lib/sass/scss/parser.rb, line 355
355:       def block_child(context)
356:         return variable || directive || ruleset if context == :stylesheet
357:         variable || directive || declaration_or_ruleset
358:       end

A block may contain declarations and/or rulesets

[Source]

     # File lib/sass/scss/parser.rb, line 345
345:       def block_contents(node, context)
346:         block_given? ? yield : ss_comments(node)
347:         node << (child = block_child(context))
348:         while tok(/;/) || has_children?(child)
349:           block_given? ? yield : ss_comments(node)
350:           node << (child = block_child(context))
351:         end
352:         node
353:       end

[Source]

     # File lib/sass/scss/parser.rb, line 302
302:       def charset_directive
303:         tok! STRING
304:         name = @scanner[1] || @scanner[2]
305:         ss
306:         node(Sass::Tree::CharsetNode.new(name))
307:       end

[Source]

     # File lib/sass/scss/parser.rb, line 494
494:       def class_selector
495:         return unless tok(/\./)
496:         @expected = "class name"
497:         Selector::Class.new(merge(expr!(:interp_ident)))
498:       end

[Source]

     # File lib/sass/scss/parser.rb, line 456
456:       def combinator
457:         tok(PLUS) || tok(GREATER) || tok(TILDE)
458:       end

[Source]

     # File lib/sass/scss/parser.rb, line 147
147:       def debug_directive
148:         node(Sass::Tree::DebugNode.new(sass_script(:parse)))
149:       end

[Source]

     # File lib/sass/scss/parser.rb, line 602
602:       def declaration
603:         # This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
604:         if s = tok(/[:\*\.]|\#(?!\{)/)
605:           @use_property_exception = s !~ /[\.\#]/
606:           name = [s, str{ss}, *expr!(:interp_ident)]
607:         else
608:           return unless name = interp_ident
609:           name = [name] if name.is_a?(String)
610:         end
611:         if comment = tok(COMMENT)
612:           name << comment
613:         end
614:         ss
615: 
616:         tok!(/:/)
617:         space, value = value!
618:         ss
619:         require_block = tok?(/\{/)
620: 
621:         node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new))
622: 
623:         return node unless require_block
624:         nested_properties! node, space
625:       end

This is a nasty hack, and the only place in the parser that requires backtracking. The reason is that we can‘t figure out if certain strings are declarations or rulesets with fixed finite lookahead. For example, "foo:bar baz baz baz…" could be either a property or a selector.

To handle this, we simply check if it works as a property (which is the most common case) and, if it doesn‘t, try it as a ruleset.

We could eke some more efficiency out of this by handling some easy cases (first token isn‘t an identifier, no colon after the identifier, whitespace after the colon), but I‘m not sure the gains would be worth the added complexity.

[Source]

     # File lib/sass/scss/parser.rb, line 381
381:       def declaration_or_ruleset
382:         pos = @scanner.pos
383:         line = @line
384:         old_use_property_exception, @use_property_exception =
385:           @use_property_exception, false
386:         begin
387:           decl = declaration
388:           unless decl && decl.has_children
389:             # We want an exception if it's not there,
390:             # but we don't want to consume if it is
391:             tok!(/[;}]/) unless tok?(/[;}]/)
392:           end
393:           return decl
394:         rescue Sass::SyntaxError => decl_err
395:         end
396: 
397:         @line = line
398:         @scanner.pos = pos
399: 
400:         begin
401:           return ruleset
402:         rescue Sass::SyntaxError => ruleset_err
403:           raise @use_property_exception ? decl_err : ruleset_err
404:         end
405:       ensure
406:         @use_property_exception = old_use_property_exception
407:       end

[Source]

     # File lib/sass/scss/parser.rb, line 104
104:       def directive
105:         return unless tok(/@/)
106:         name = tok!(IDENT)
107:         ss
108: 
109:         if dir = special_directive(name)
110:           return dir
111:         end
112: 
113:         # Most at-rules take expressions (e.g. @import),
114:         # but some (e.g. @page) take selector-like arguments
115:         val = str {break unless expr}
116:         val ||= CssParser.new(@scanner, @line).parse_selector_string
117:         node = node(Sass::Tree::DirectiveNode.new("@#{name} #{val}".strip))
118: 
119:         if tok(/\{/)
120:           node.has_children = true
121:           block_contents(node, :directive)
122:           tok!(/\}/)
123:         end
124: 
125:         node
126:       end

[Source]

     # File lib/sass/scss/parser.rb, line 506
506:       def element_name
507:         return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
508:         if tok(/\|/)
509:           @expected = "element name or *"
510:           ns = name
511:           name = interp_ident || tok!(/\*/)
512:         end
513: 
514:         if name == '*'
515:           Selector::Universal.new(merge(ns))
516:         else
517:           Selector::Element.new(merge(name), merge(ns))
518:         end
519:       end

[Source]

     # File lib/sass/scss/parser.rb, line 195
195:       def else_block(node)
196:         return unless tok(/@else/)
197:         ss
198:         else_node = block(
199:           Sass::Tree::IfNode.new((sass_script(:parse) if tok(/if/))),
200:           :directive)
201:         node.add_else(else_node)
202:         pos = @scanner.pos
203:         line = @line
204:         ss
205: 
206:         else_block(node) ||
207:           begin
208:             # Backtrack in case there are any comments we want to parse
209:             @scanner.pos = pos
210:             @line = line
211:             node
212:           end
213:       end

[Source]

     # File lib/sass/scss/parser.rb, line 215
215:       def else_directive
216:         raise Sass::SyntaxError.new(
217:           "Invalid CSS: @else must come after @if", :line => @line)
218:       end

[Source]

     # File lib/sass/scss/parser.rb, line 814
814:       def expected(name)
815:         self.class.expected(@scanner, @expected || name, @line)
816:       end

[Source]

     # File lib/sass/scss/parser.rb, line 666
666:       def expr
667:         return unless t = term
668:         res = [t, str{ss}]
669: 
670:         while (o = operator) && (t = term)
671:           res << o << t << str{ss}
672:         end
673: 
674:         res
675:       end

[Source]

     # File lib/sass/scss/parser.rb, line 796
796:       def expr!(name)
797:         (e = send(name)) && (return e)
798:         expected(EXPR_NAMES[name] || name.to_s)
799:       end

[Source]

     # File lib/sass/scss/parser.rb, line 220
220:       def extend_directive
221:         node(Sass::Tree::ExtendNode.new(expr!(:selector)))
222:       end

[Source]

     # File lib/sass/scss/parser.rb, line 155
155:       def for_directive
156:         tok!(/\$/)
157:         var = tok! IDENT
158:         ss
159: 
160:         tok!(/from/)
161:         from = sass_script(:parse_until, Set["to", "through"])
162:         ss
163: 
164:         @expected = '"to" or "through"'
165:         exclusive = (tok(/to/) || tok!(/through/)) == 'to'
166:         to = sass_script(:parse)
167:         ss
168: 
169:         block(node(Sass::Tree::ForNode.new(var, from, to, exclusive)), :directive)
170:       end

[Source]

     # File lib/sass/scss/parser.rb, line 693
693:       def function
694:         return unless name = tok(FUNCTION)
695:         if name == "expression(" || name == "calc("
696:           str, _ = Haml::Shared.balance(@scanner, ?(, ?), 1)
697:           [name, str]
698:         else
699:           [name, str{ss}, expr, tok!(/\)/)]
700:         end
701:       end

[Source]

     # File lib/sass/scss/parser.rb, line 360
360:       def has_children?(child_or_array)
361:         return false unless child_or_array
362:         return child_or_array.last.has_children if child_or_array.is_a?(Array)
363:         return child_or_array.has_children
364:       end

[Source]

     # File lib/sass/scss/parser.rb, line 500
500:       def id_selector
501:         return unless tok(/#(?!\{)/)
502:         @expected = "id name"
503:         Selector::Id.new(merge(expr!(:interp_name)))
504:       end

[Source]

     # File lib/sass/scss/parser.rb, line 178
178:       def if_directive
179:         expr = sass_script(:parse)
180:         ss
181:         node = block(node(Sass::Tree::IfNode.new(expr)), :directive)
182:         pos = @scanner.pos
183:         line = @line
184:         ss
185: 
186:         else_block(node) ||
187:           begin
188:             # Backtrack in case there are any comments we want to parse
189:             @scanner.pos = pos
190:             @line = line
191:             node
192:           end
193:       end

[Source]

     # File lib/sass/scss/parser.rb, line 235
235:       def import_arg
236:         return unless arg = tok(STRING) || (uri = tok!(URI))
237:         path = @scanner[1] || @scanner[2] || @scanner[3]
238:         ss
239: 
240:         media = str {media_query_list}.strip
241: 
242:         if uri || path =~ /^http:\/\// || !media.strip.empty? || use_css_import?
243:           return node(Sass::Tree::DirectiveNode.new("@import #{arg} #{media}".strip))
244:         end
245: 
246:         node(Sass::Tree::ImportNode.new(path.strip))
247:       end

[Source]

     # File lib/sass/scss/parser.rb, line 224
224:       def import_directive
225:         values = []
226: 
227:         loop do
228:           values << expr!(:import_arg)
229:           break if use_css_import? || !tok(/,\s*/)
230:         end
231: 
232:         return values
233:       end

[Source]

     # File lib/sass/scss/parser.rb, line 140
140:       def include_directive
141:         name = tok! IDENT
142:         args = sass_script(:parse_mixin_include_arglist)
143:         ss
144:         node(Sass::Tree::MixinNode.new(name, args))
145:       end

[Source]

    # File lib/sass/scss/parser.rb, line 46
46:       def init_scanner!
47:         @scanner =
48:           if @template.is_a?(StringScanner)
49:             @template
50:           else
51:             StringScanner.new(@template.gsub("\r", ""))
52:           end
53:       end

[Source]

     # File lib/sass/scss/parser.rb, line 726
726:       def interp_ident(start = IDENT)
727:         return unless val = tok(start) || interpolation
728:         res = [val]
729:         while val = tok(NAME) || interpolation
730:           res << val
731:         end
732:         res
733:       end

[Source]

     # File lib/sass/scss/parser.rb, line 735
735:       def interp_name
736:         interp_ident NAME
737:       end

[Source]

     # File lib/sass/scss/parser.rb, line 708
708:       def interp_string
709:         _interp_string(:double) || _interp_string(:single)
710:       end

[Source]

     # File lib/sass/scss/parser.rb, line 703
703:       def interpolation
704:         return unless tok(INTERP_START)
705:         sass_script(:parse_interpolated)
706:       end

[Source]

     # File lib/sass/scss/parser.rb, line 521
521:       def interpolation_selector
522:         return unless script = interpolation
523:         Selector::Interpolation.new(script)
524:       end

[Source]

     # File lib/sass/scss/parser.rb, line 251
251:       def media_directive
252:         val = str {media_query_list}.strip
253:         block(node(Sass::Tree::DirectiveNode.new("@media #{val}")), :directive)
254:       end

[Source]

     # File lib/sass/scss/parser.rb, line 286
286:       def media_expr
287:         return unless tok(/\(/)
288:         ss
289:         @expected = "media feature (e.g. min-device-width, color)"
290:         tok!(IDENT)
291:         ss
292: 
293:         if tok(/:/)
294:           ss; expr!(:expr)
295:         end
296:         tok!(/\)/)
297:         ss
298: 
299:         true
300:       end

[Source]

     # File lib/sass/scss/parser.rb, line 268
268:       def media_query
269:         if tok(/only|not/i)
270:           ss
271:           @expected = "media type (e.g. print, screen)"
272:           tok!(IDENT)
273:           ss
274:         elsif !tok(IDENT) && !media_expr
275:           return
276:         end
277: 
278:         ss
279:         while tok(/and/i)
280:           ss; expr!(:media_expr); ss
281:         end
282: 
283:         true
284:       end

www.w3.org/TR/css3-mediaqueries/#syntax

[Source]

     # File lib/sass/scss/parser.rb, line 257
257:       def media_query_list
258:         return unless media_query
259: 
260:         ss
261:         while tok(/,/)
262:           ss; expr!(:media_query); ss
263:         end
264: 
265:         true
266:       end

[Source]

     # File lib/sass/scss/parser.rb, line 772
772:       def merge(arr)
773:         arr && Haml::Util.merge_adjacent_strings([arr].flatten)
774:       end

[Source]

     # File lib/sass/scss/parser.rb, line 133
133:       def mixin_directive
134:         name = tok! IDENT
135:         args = sass_script(:parse_mixin_definition_arglist)
136:         ss
137:         block(node(Sass::Tree::MixinDefNode.new(name, args)), :directive)
138:       end

[Source]

     # File lib/sass/scss/parser.rb, line 593
593:       def negation
594:         return unless name = tok(NOT) || tok(MOZ_ANY)
595:         ss
596:         @expected = "selector"
597:         sel = selector_comma_sequence
598:         tok!(/\)/)
599:         Selector::SelectorPseudoClass.new(name[1...-1], sel)
600:       end

[Source]

     # File lib/sass/scss/parser.rb, line 654
654:       def nested_properties!(node, space)
655:         raise Sass::SyntaxError.new("Invalid CSS: a space is required between a property and its definition\nwhen it has other properties nested beneath it.\n", :line => @line) unless space
656: 
657:         @use_property_exception = true
658:         @expected = 'expression (e.g. 1px, bold) or "{"'
659:         block(node, :property)
660:       end

[Source]

     # File lib/sass/scss/parser.rb, line 754
754:       def node(node)
755:         node.line = @line
756:         node
757:       end

[Source]

     # File lib/sass/scss/parser.rb, line 319
319:       def operator
320:         # Many of these operators (all except / and ,)
321:         # are disallowed by the CSS spec,
322:         # but they're included here for compatibility
323:         # with some proprietary MS properties
324:         str {ss if tok(/[\/,:.=]/)}
325:       end

[Source]

     # File lib/sass/scss/parser.rb, line 489
489:       def parent_selector
490:         return unless tok(/&/)
491:         Selector::Parent.new
492:       end

[Source]

     # File lib/sass/scss/parser.rb, line 643
643:       def plain_value
644:         return unless tok(/:/)
645:         space = !str {ss}.empty?
646:         @use_property_exception ||= space || !tok?(IDENT)
647: 
648:         expression = expr
649:         expression << tok(IMPORTANT) if expression
650:         # expression, space, value
651:         return expression, space, expression || [""]
652:       end

[Source]

    # File lib/sass/scss/parser.rb, line 89
89:       def process_comment(text, node)
90:         single_line = text =~ /^\/\//
91:         pre_str = single_line ? "" : @scanner.
92:           string[0...@scanner.pos].
93:           reverse[/.*?\*\/(.*?)($|\Z)/, 1].
94:           reverse.gsub(/[^\s]/, ' ')
95:         text = text.sub(/^\s*\/\//, '/*').gsub(/^\s*\/\//, ' *') + ' */' if single_line
96:         comment = Sass::Tree::CommentNode.new(pre_str + text, single_line)
97:         comment.line = @line - text.count("\n")
98:         node << comment
99:       end

[Source]

     # File lib/sass/scss/parser.rb, line 570
570:       def pseudo
571:         return unless s = tok(/::?/)
572:         @expected = "pseudoclass or pseudoelement"
573:         name = expr!(:interp_ident)
574:         if tok(/\(/)
575:           ss
576:           arg = expr!(:pseudo_expr)
577:           tok!(/\)/)
578:         end
579:         Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
580:       end

[Source]

     # File lib/sass/scss/parser.rb, line 582
582:       def pseudo_expr
583:         return unless e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
584:           interp_string || tok(IDENT) || interpolation
585:         res = [e, str{ss}]
586:         while e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
587:             interp_string || tok(IDENT) || interpolation
588:           res << e << str{ss}
589:         end
590:         res
591:       end

[Source]

     # File lib/sass/scss/parser.rb, line 331
331:       def ruleset
332:         return unless rules = selector_sequence
333:         block(node(Sass::Tree::RuleNode.new(rules.flatten.compact)), :ruleset)
334:       end

[Source]

    # File lib/sass/scss/parser.rb, line 60
60:       def s(node)
61:         while tok(S) || tok(CDC) || tok(CDO) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
62:           next unless c
63:           process_comment c, node
64:           c = nil
65:         end
66:         true
67:       end

[Source]

     # File lib/sass/scss/parser.rb, line 764
764:       def sass_script(*args)
765:         parser = self.class.sass_script_parser.new(@scanner, @line,
766:           @scanner.pos - (@scanner.string[0...@scanner.pos].rindex("\n") || 0))
767:         result = parser.send(*args)
768:         @line = parser.line
769:         result
770:       end

[Source]

     # File lib/sass/scss/parser.rb, line 425
425:       def selector
426:         return unless sel = _selector
427:         sel.to_a
428:       end

[Source]

     # File lib/sass/scss/parser.rb, line 430
430:       def selector_comma_sequence
431:         return unless sel = _selector
432:         selectors = [sel]
433:         while tok(/,/)
434:           ws = str{ss}
435:           selectors << expr!(:_selector)
436:           selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members) if ws.include?("\n")
437:         end
438:         Selector::CommaSequence.new(selectors)
439:       end

[Source]

     # File lib/sass/scss/parser.rb, line 409
409:       def selector_sequence
410:         if sel = tok(STATIC_SELECTOR)
411:           return [sel]
412:         end
413: 
414:         rules = []
415:         return unless v = selector
416:         rules.concat v
417: 
418:         while tok(/,/)
419:           rules << ',' << str {ss}
420:           rules.concat expr!(:selector)
421:         end
422:         rules
423:       end

[Source]

     # File lib/sass/scss/parser.rb, line 460
460:       def simple_selector_sequence
461:         # This allows for stuff like http://www.w3.org/TR/css3-animations/#keyframes-
462:         return expr unless e = element_name || id_selector || class_selector ||
463:           attrib || negation || pseudo || parent_selector || interpolation_selector
464:         res = [e]
465: 
466:         # The tok(/\*/) allows the "E*" hack
467:         while v = element_name || id_selector || class_selector ||
468:             attrib || negation || pseudo || interpolation_selector ||
469:             (tok(/\*/) && Selector::Universal.new(nil))
470:           res << v
471:         end
472: 
473:         if tok?(/&/)
474:           begin
475:             expected('"{"')
476:           rescue Sass::SyntaxError => e
477:             e.message << "\n\n" << "In Sass 3, the parent selector & can only be used where element names are valid,\nsince it could potentially be replaced by an element name.\n"
478:             raise e
479:           end
480:         end
481: 
482:         Selector::SimpleSequence.new(res)
483:       end

[Source]

     # File lib/sass/scss/parser.rb, line 128
128:       def special_directive(name)
129:         sym = name.gsub('-', '_').to_sym
130:         DIRECTIVES.include?(sym) && send("#{sym}_directive")
131:       end

[Source]

    # File lib/sass/scss/parser.rb, line 69
69:       def ss
70:         nil while tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
71:         true
72:       end

[Source]

    # File lib/sass/scss/parser.rb, line 74
74:       def ss_comments(node)
75:         while tok(S) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
76:           next unless c
77:           process_comment c, node
78:           c = nil
79:         end
80: 
81:         true
82:       end

[Source]

     # File lib/sass/scss/parser.rb, line 739
739:       def str
740:         @strs.push ""
741:         yield
742:         @strs.last
743:       ensure
744:         @strs.pop
745:       end

[Source]

     # File lib/sass/scss/parser.rb, line 747
747:       def str?
748:         @strs.push ""
749:         yield && @strs.last
750:       ensure
751:         @strs.pop
752:       end

[Source]

    # File lib/sass/scss/parser.rb, line 55
55:       def stylesheet
56:         node = node(Sass::Tree::RootNode.new(@scanner.string))
57:         block_contents(node, :stylesheet) {s(node)}
58:       end

[Source]

     # File lib/sass/scss/parser.rb, line 677
677:       def term
678:         unless e = tok(NUMBER) ||
679:             tok(URI) ||
680:             function ||
681:             tok(STRING) ||
682:             tok(UNICODERANGE) ||
683:             tok(IDENT) ||
684:             tok(HEXCOLOR)
685: 
686:           return unless op = unary_operator
687:           @expected = "number or function"
688:           return [op, tok(NUMBER) || expr!(:function)]
689:         end
690:         e
691:       end

[Source]

     # File lib/sass/scss/parser.rb, line 843
843:       def tok(rx)
844:         res = @scanner.scan(rx)
845:         if res
846:           @line += res.count("\n")
847:           @expected = nil
848:           if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
849:             @strs.each {|s| s << res}
850:           end
851:         end
852: 
853:         res
854:       end

[Source]

     # File lib/sass/scss/parser.rb, line 801
801:       def tok!(rx)
802:         (t = tok(rx)) && (return t)
803:         name = TOK_NAMES[rx]
804: 
805:         unless name
806:           # Display basic regexps as plain old strings
807:           string = rx.source.gsub(/\\(.)/, '\1')
808:           name = rx.source == Regexp.escape(string) ? string.inspect : rx.inspect
809:         end
810: 
811:         expected(name)
812:       end

[Source]

     # File lib/sass/scss/parser.rb, line 792
792:       def tok?(rx)
793:         @scanner.match?(rx)
794:       end

[Source]

     # File lib/sass/scss/parser.rb, line 327
327:       def unary_operator
328:         tok(/[+-]/)
329:       end

[Source]

     # File lib/sass/scss/parser.rb, line 249
249:       def use_css_import?; false; end

[Source]

     # File lib/sass/scss/parser.rb, line 627
627:       def value!
628:         space = !str {ss}.empty?
629:         @use_property_exception ||= space || !tok?(IDENT)
630: 
631:         return true, Sass::Script::String.new("") if tok?(/\{/)
632:         # This is a bit of a dirty trick:
633:         # if the value is completely static,
634:         # we don't parse it at all, and instead return a plain old string
635:         # containing the value.
636:         # This results in a dramatic speed increase.
637:         if val = tok(STATIC_VALUE)
638:           return space, Sass::Script::String.new(val.strip)
639:         end
640:         return space, sass_script(:parse)
641:       end

[Source]

     # File lib/sass/scss/parser.rb, line 309
309:       def variable
310:         return unless tok(/\$/)
311:         name = tok!(IDENT)
312:         ss; tok!(/:/); ss
313: 
314:         expr = sass_script(:parse)
315:         guarded = tok(DEFAULT)
316:         node(Sass::Tree::VariableNode.new(name, expr, guarded))
317:       end

[Source]

     # File lib/sass/scss/parser.rb, line 151
151:       def warn_directive
152:         node(Sass::Tree::WarnNode.new(sass_script(:parse)))
153:       end

[Source]

     # File lib/sass/scss/parser.rb, line 172
172:       def while_directive
173:         expr = sass_script(:parse)
174:         ss
175:         block(node(Sass::Tree::WhileNode.new(expr)), :directive)
176:       end

[Source]

    # File lib/sass/scss/parser.rb, line 84
84:       def whitespace
85:         return unless tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
86:         ss
87:       end

[Validate]