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 DirectiveNode ExtendNode VariableNode WarnNode RootNode WhileNode RuleNode MixinDefNode 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/literal.rb lib/sass/script/node.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 Files Callbacks 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/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_85_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, :extend, :import, :media]
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", }
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 787
787:       def self.expected(scanner, expected, line)
788:         pos = scanner.pos
789: 
790:         after = scanner.string[0...pos]
791:         # Get rid of whitespace between pos and the last token,
792:         # but only if there's a newline in there
793:         after.gsub!(/\s*\n\s*$/, '')
794:         # Also get rid of stuff before the last newline
795:         after.gsub!(/.*\n/, '')
796:         after = "..." + after[-15..-1] if after.size > 18
797: 
798:         was = scanner.rest.dup
799:         # Get rid of whitespace between pos and the next token,
800:         # but only if there's a newline in there
801:         was.gsub!(/^\s*\n\s*/, '')
802:         # Also get rid of stuff after the next newline
803:         was.gsub!(/\n.*/, '')
804:         was = was[0...15] + "..." if was.size > 18
805: 
806:         raise Sass::SyntaxError.new(
807:           "Invalid CSS after \"#{after}\": expected #{expected}, was \"#{was}\"",
808:           :line => line)
809:       end

@private

[Source]

     # File lib/sass/scss/parser.rb, line 731
731:       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 681
681:       def _interp_string(type)
682:         return unless start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, false]])
683:         res = [start]
684: 
685:         mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, true]]
686:         # @scanner[2].empty? means we've started an interpolated section
687:         while @scanner[2] == '#{'
688:           @scanner.pos -= 2 # Don't consume the #{
689:           res.last.slice!(-2..-1)
690:           res << expr!(:interpolation) << tok(mid_re)
691:         end
692:         res
693:       end

[Source]

     # File lib/sass/scss/parser.rb, line 409
409:       def _selector
410:         # The combinator here allows the "> E" hack
411:         return unless val = combinator || simple_selector_sequence
412:         nl = str{ss}.include?("\n")
413:         res = []
414:         res << val
415:         res << "\n" if nl
416: 
417:         while val = combinator || simple_selector_sequence
418:           res << val
419:           res << "\n" if str{ss}.include?("\n")
420:         end
421:         Selector::Sequence.new(res.compact)
422:       end

[Source]

     # File lib/sass/scss/parser.rb, line 494
494:       def attrib
495:         return unless tok(/\[/)
496:         ss
497:         ns, name = attrib_name!
498:         ss
499: 
500:         if op = tok(/=/) ||
501:             tok(INCLUDES) ||
502:             tok(DASHMATCH) ||
503:             tok(PREFIXMATCH) ||
504:             tok(SUFFIXMATCH) ||
505:             tok(SUBSTRINGMATCH)
506:           @expected = "identifier or string"
507:           ss
508:           if val = tok(IDENT)
509:             val = [val]
510:           else
511:             val = expr!(:interp_string)
512:           end
513:           ss
514:         end
515:         tok(/\]/)
516: 
517:         Selector::Attribute.new(merge(name), merge(ns), op, merge(val))
518:       end

[Source]

     # File lib/sass/scss/parser.rb, line 520
520:       def attrib_name!
521:         if name_or_ns = interp_ident
522:           # E, E|E
523:           if tok(/\|(?!=)/)
524:             ns = name_or_ns
525:             name = interp_ident
526:           else
527:             name = name_or_ns
528:           end
529:         else
530:           # *|E or |E
531:           ns = [tok(/\*/) || ""]
532:           tok!(/\|/)
533:           name = expr!(:interp_ident)
534:         end
535:         return ns, name
536:       end

[Source]

     # File lib/sass/scss/parser.rb, line 310
310:       def block(node, context)
311:         node.has_children = true
312:         tok!(/\{/)
313:         block_contents(node, context)
314:         tok!(/\}/)
315:         node
316:       end

[Source]

     # File lib/sass/scss/parser.rb, line 329
329:       def block_child(context)
330:         return variable || directive || ruleset if context == :stylesheet
331:         variable || directive || declaration_or_ruleset
332:       end

A block may contain declarations and/or rulesets

[Source]

     # File lib/sass/scss/parser.rb, line 319
319:       def block_contents(node, context)
320:         block_given? ? yield : ss_comments(node)
321:         node << (child = block_child(context))
322:         while tok(/;/) || (child && child.has_children)
323:           block_given? ? yield : ss_comments(node)
324:           node << (child = block_child(context))
325:         end
326:         node
327:       end

[Source]

     # File lib/sass/scss/parser.rb, line 462
462:       def class_selector
463:         return unless tok(/\./)
464:         @expected = "class name"
465:         Selector::Class.new(merge(expr!(:interp_ident)))
466:       end

[Source]

     # File lib/sass/scss/parser.rb, line 424
424:       def combinator
425:         tok(PLUS) || tok(GREATER) || tok(TILDE)
426:       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 570
570:       def declaration
571:         # This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
572:         if s = tok(/[:\*\.]|\#(?!\{)/)
573:           @use_property_exception = s !~ /[\.\#]/
574:           name = [s, str{ss}, *expr!(:interp_ident)]
575:         else
576:           return unless name = interp_ident
577:           name = [name] if name.is_a?(String)
578:         end
579:         if comment = tok(COMMENT)
580:           name << comment
581:         end
582:         ss
583: 
584:         tok!(/:/)
585:         space, value = value!
586:         ss
587:         require_block = tok?(/\{/)
588: 
589:         node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new))
590: 
591:         return node unless require_block
592:         nested_properties! node, space
593:       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 349
349:       def declaration_or_ruleset
350:         pos = @scanner.pos
351:         line = @line
352:         old_use_property_exception, @use_property_exception =
353:           @use_property_exception, false
354:         begin
355:           decl = declaration
356:           unless decl && decl.has_children
357:             # We want an exception if it's not there,
358:             # but we don't want to consume if it is
359:             tok!(/[;}]/) unless tok?(/[;}]/)
360:           end
361:           return decl
362:         rescue Sass::SyntaxError => decl_err
363:         end
364: 
365:         @line = line
366:         @scanner.pos = pos
367: 
368:         begin
369:           return ruleset
370:         rescue Sass::SyntaxError => ruleset_err
371:           raise @use_property_exception ? decl_err : ruleset_err
372:         end
373:       ensure
374:         @use_property_exception = old_use_property_exception
375:       end

[Source]

     # File lib/sass/scss/parser.rb, line 103
103:       def directive
104:         return unless tok(/@/)
105:         name = tok!(IDENT)
106:         ss
107: 
108:         if dir = special_directive(name)
109:           return dir
110:         end
111: 
112:         val = str do
113:           # Most at-rules take expressions (e.g. @import),
114:           # but some (e.g. @page) take selector-like arguments
115:           expr || selector
116:         end
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 474
474:       def element_name
475:         return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
476:         if tok(/\|/)
477:           @expected = "element name or *"
478:           ns = name
479:           name = interp_ident || tok!(/\*/)
480:         end
481: 
482:         if name == '*'
483:           Selector::Universal.new(merge(ns))
484:         else
485:           Selector::Element.new(merge(name), merge(ns))
486:         end
487:       end

[Source]

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

[Source]

     # File lib/sass/scss/parser.rb, line 782
782:       def expected(name)
783:         self.class.expected(@scanner, @expected || name, @line)
784:       end

[Source]

     # File lib/sass/scss/parser.rb, line 634
634:       def expr
635:         return unless t = term
636:         res = [t, str{ss}]
637: 
638:         while (o = operator) && (t = term)
639:           res << o << t << str{ss}
640:         end
641: 
642:         res
643:       end

[Source]

     # File lib/sass/scss/parser.rb, line 764
764:       def expr!(name)
765:         (e = send(name)) && (return e)
766:         expected(EXPR_NAMES[name] || name.to_s)
767:       end

[Source]

     # File lib/sass/scss/parser.rb, line 211
211:       def extend_directive
212:         node(Sass::Tree::ExtendNode.new(expr!(:selector)))
213:       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 662
662:       def function
663:         return unless name = tok(FUNCTION)
664:         if name == "expression(" || name == "calc("
665:           str, _ = Haml::Shared.balance(@scanner, ?(, ?), 1)
666:           [name, str]
667:         else
668:           [name, str{ss}, expr, tok!(/\)/)]
669:         end
670:       end

[Source]

     # File lib/sass/scss/parser.rb, line 468
468:       def id_selector
469:         return unless tok(/#(?!\{)/)
470:         @expected = "id name"
471:         Selector::Id.new(merge(expr!(:interp_name)))
472:       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:         ss
184: 
185:         else_block(node) ||
186:           begin
187:             # Backtrack in case there are any comments we want to parse
188:             @scanner.pos = pos
189:             node
190:           end
191:       end

[Source]

     # File lib/sass/scss/parser.rb, line 215
215:       def import_directive
216:         @expected = "string or url()"
217:         arg = tok(STRING) || (uri = tok!(URI))
218:         path = @scanner[1] || @scanner[2] || @scanner[3]
219:         ss
220: 
221:         media = str {media_query_list}.strip
222: 
223:         if uri || path =~ /^http:\/\// || !media.strip.empty? || use_css_import?
224:           return node(Sass::Tree::DirectiveNode.new("@import #{arg} #{media}".strip))
225:         end
226: 
227:         node(Sass::Tree::ImportNode.new(path.strip))
228:       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 695
695:       def interp_ident(start = IDENT)
696:         return unless val = tok(start) || interpolation
697:         res = [val]
698:         while val = tok(NAME) || interpolation
699:           res << val
700:         end
701:         res
702:       end

[Source]

     # File lib/sass/scss/parser.rb, line 704
704:       def interp_name
705:         interp_ident NAME
706:       end

[Source]

     # File lib/sass/scss/parser.rb, line 677
677:       def interp_string
678:         _interp_string(:double) || _interp_string(:single)
679:       end

[Source]

     # File lib/sass/scss/parser.rb, line 672
672:       def interpolation
673:         return unless tok(INTERP_START)
674:         sass_script(:parse_interpolated)
675:       end

[Source]

     # File lib/sass/scss/parser.rb, line 489
489:       def interpolation_selector
490:         return unless script = interpolation
491:         Selector::Interpolation.new(script)
492:       end

[Source]

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

[Source]

     # File lib/sass/scss/parser.rb, line 267
267:       def media_expr
268:         return unless tok(/\(/)
269:         ss
270:         @expected = "media feature (e.g. min-device-width, color)"
271:         tok!(IDENT)
272:         ss
273: 
274:         if tok(/:/)
275:           ss; expr!(:expr)
276:         end
277:         tok!(/\)/)
278:         ss
279: 
280:         true
281:       end

[Source]

     # File lib/sass/scss/parser.rb, line 249
249:       def media_query
250:         if tok(/only|not/i)
251:           ss
252:           @expected = "media type (e.g. print, screen)"
253:           tok!(IDENT)
254:           ss
255:         elsif !tok(IDENT) && !media_expr
256:           return
257:         end
258: 
259:         ss
260:         while tok(/and/i)
261:           ss; expr!(:media_expr); ss
262:         end
263: 
264:         true
265:       end

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

[Source]

     # File lib/sass/scss/parser.rb, line 238
238:       def media_query_list
239:         return unless media_query
240: 
241:         ss
242:         while tok(/,/)
243:           ss; expr!(:media_query); ss
244:         end
245: 
246:         true
247:       end

[Source]

     # File lib/sass/scss/parser.rb, line 741
741:       def merge(arr)
742:         arr && Haml::Util.merge_adjacent_strings([arr].flatten)
743:       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 561
561:       def negation
562:         return unless name = tok(NOT) || tok(MOZ_ANY)
563:         ss
564:         @expected = "selector"
565:         sel = selector_comma_sequence
566:         tok!(/\)/)
567:         Selector::SelectorPseudoClass.new(name[1...-1], sel)
568:       end

[Source]

     # File lib/sass/scss/parser.rb, line 622
622:       def nested_properties!(node, space)
623:         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
624: 
625:         @use_property_exception = true
626:         @expected = 'expression (e.g. 1px, bold) or "{"'
627:         block(node, :property)
628:       end

[Source]

     # File lib/sass/scss/parser.rb, line 723
723:       def node(node)
724:         node.line = @line
725:         node
726:       end

[Source]

     # File lib/sass/scss/parser.rb, line 293
293:       def operator
294:         # Many of these operators (all except / and ,)
295:         # are disallowed by the CSS spec,
296:         # but they're included here for compatibility
297:         # with some proprietary MS properties
298:         str {ss if tok(/[\/,:.=]/)}
299:       end

[Source]

     # File lib/sass/scss/parser.rb, line 457
457:       def parent_selector
458:         return unless tok(/&/)
459:         Selector::Parent.new
460:       end

[Source]

     # File lib/sass/scss/parser.rb, line 611
611:       def plain_value
612:         return unless tok(/:/)
613:         space = !str {ss}.empty?
614:         @use_property_exception ||= space || !tok?(IDENT)
615: 
616:         expression = expr
617:         expression << tok(IMPORTANT) if expression
618:         # expression, space, value
619:         return expression, space, expression || [""]
620:       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 538
538:       def pseudo
539:         return unless s = tok(/::?/)
540:         @expected = "pseudoclass or pseudoelement"
541:         name = expr!(:interp_ident)
542:         if tok(/\(/)
543:           ss
544:           arg = expr!(:pseudo_expr)
545:           tok!(/\)/)
546:         end
547:         Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
548:       end

[Source]

     # File lib/sass/scss/parser.rb, line 550
550:       def pseudo_expr
551:         return unless e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
552:           interp_string || tok(IDENT) || interpolation
553:         res = [e, str{ss}]
554:         while e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
555:             interp_string || tok(IDENT) || interpolation
556:           res << e << str{ss}
557:         end
558:         res
559:       end

[Source]

     # File lib/sass/scss/parser.rb, line 305
305:       def ruleset
306:         return unless rules = selector_sequence
307:         block(node(Sass::Tree::RuleNode.new(rules.flatten.compact)), :ruleset)
308:       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 733
733:       def sass_script(*args)
734:         parser = self.class.sass_script_parser.new(@scanner, @line,
735:           @scanner.pos - (@scanner.string[0...@scanner.pos].rindex("\n") || 0))
736:         result = parser.send(*args)
737:         @line = parser.line
738:         result
739:       end

[Source]

     # File lib/sass/scss/parser.rb, line 393
393:       def selector
394:         return unless sel = _selector
395:         sel.to_a
396:       end

[Source]

     # File lib/sass/scss/parser.rb, line 398
398:       def selector_comma_sequence
399:         return unless sel = _selector
400:         selectors = [sel]
401:         while tok(/,/)
402:           ws = str{ss}
403:           selectors << expr!(:_selector)
404:           selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members) if ws.include?("\n")
405:         end
406:         Selector::CommaSequence.new(selectors)
407:       end

[Source]

     # File lib/sass/scss/parser.rb, line 377
377:       def selector_sequence
378:         if sel = tok(STATIC_SELECTOR)
379:           return [sel]
380:         end
381: 
382:         rules = []
383:         return unless v = selector
384:         rules.concat v
385: 
386:         while tok(/,/)
387:           rules << ',' << str {ss}
388:           rules.concat expr!(:selector)
389:         end
390:         rules
391:       end

[Source]

     # File lib/sass/scss/parser.rb, line 428
428:       def simple_selector_sequence
429:         # This allows for stuff like http://www.w3.org/TR/css3-animations/#keyframes-
430:         return expr unless e = element_name || id_selector || class_selector ||
431:           attrib || negation || pseudo || parent_selector || interpolation_selector
432:         res = [e]
433: 
434:         # The tok(/\*/) allows the "E*" hack
435:         while v = element_name || id_selector || class_selector ||
436:             attrib || negation || pseudo || interpolation_selector ||
437:             (tok(/\*/) && Selector::Universal.new(nil))
438:           res << v
439:         end
440: 
441:         if tok?(/&/)
442:           begin
443:             expected('"{"')
444:           rescue Sass::SyntaxError => e
445:             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"
446:             raise e
447:           end
448:         end
449: 
450:         Selector::SimpleSequence.new(res)
451:       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 708
708:       def str
709:         @strs.push ""
710:         yield
711:         @strs.last
712:       ensure
713:         @strs.pop
714:       end

[Source]

     # File lib/sass/scss/parser.rb, line 716
716:       def str?
717:         @strs.push ""
718:         yield && @strs.last
719:       ensure
720:         @strs.pop
721:       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 645
645:       def term
646:         unless e = tok(NUMBER) ||
647:             tok(URI) ||
648:             function ||
649:             interp_string ||
650:             tok(UNICODERANGE) ||
651:             tok(IDENT) ||
652:             tok(HEXCOLOR) ||
653:             interpolation
654: 
655:           return unless op = unary_operator
656:           @expected = "number or function"
657:           return [op, tok(NUMBER) || expr!(:function)]
658:         end
659:         e
660:       end

[Source]

     # File lib/sass/scss/parser.rb, line 811
811:       def tok(rx)
812:         res = @scanner.scan(rx)
813:         if res
814:           @line += res.count("\n")
815:           @expected = nil
816:           if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
817:             @strs.each {|s| s << res}
818:           end
819:         end
820: 
821:         res
822:       end

[Source]

     # File lib/sass/scss/parser.rb, line 769
769:       def tok!(rx)
770:         (t = tok(rx)) && (return t)
771:         name = TOK_NAMES[rx]
772: 
773:         unless name
774:           # Display basic regexps as plain old strings
775:           string = rx.source.gsub(/\\(.)/, '\1')
776:           name = rx.source == Regexp.escape(string) ? string.inspect : rx.inspect
777:         end
778: 
779:         expected(name)
780:       end

[Source]

     # File lib/sass/scss/parser.rb, line 760
760:       def tok?(rx)
761:         @scanner.match?(rx)
762:       end

[Source]

     # File lib/sass/scss/parser.rb, line 301
301:       def unary_operator
302:         tok(/[+-]/)
303:       end

[Source]

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

[Source]

     # File lib/sass/scss/parser.rb, line 595
595:       def value!
596:         space = !str {ss}.empty?
597:         @use_property_exception ||= space || !tok?(IDENT)
598: 
599:         return true, Sass::Script::String.new("") if tok?(/\{/)
600:         # This is a bit of a dirty trick:
601:         # if the value is completely static,
602:         # we don't parse it at all, and instead return a plain old string
603:         # containing the value.
604:         # This results in a dramatic speed increase.
605:         if val = tok(STATIC_VALUE)
606:           return space, Sass::Script::String.new(val.strip)
607:         end
608:         return space, sass_script(:parse)
609:       end

[Source]

     # File lib/sass/scss/parser.rb, line 283
283:       def variable
284:         return unless tok(/\$/)
285:         name = tok!(IDENT)
286:         ss; tok!(/:/); ss
287: 
288:         expr = sass_script(:parse)
289:         guarded = tok(DEFAULT)
290:         node(Sass::Tree::VariableNode.new(name, expr, guarded))
291:       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]