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 Negation Id Pseudo Attribute Interpolation Element Node Operation Literal UnaryOperation StringInterpolation Funcall Variable Interpolation Lexer CssLexer Number String Bool Parser Parser CssParser EvaluationContext StaticParser SassParser CssParser Node DebugNode IfNode CommentNode ForNode PropNode MixinNode DirectiveNode VariableNode RootNode WarnNode ExtendNode RuleNode MixinDefNode WhileNode 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 ScriptParser ScriptLexer 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_61_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 => "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 762
762:       def self.expected(scanner, expected, line)
763:         pos = scanner.pos
764: 
765:         after = scanner.string[0...pos]
766:         # Get rid of whitespace between pos and the last token,
767:         # but only if there's a newline in there
768:         after.gsub!(/\s*\n\s*$/, '')
769:         # Also get rid of stuff before the last newline
770:         after.gsub!(/.*\n/, '')
771:         after = "..." + after[-15..-1] if after.size > 18
772: 
773:         was = scanner.rest.dup
774:         # Get rid of whitespace between pos and the next token,
775:         # but only if there's a newline in there
776:         was.gsub!(/^\s*\n\s*/, '')
777:         # Also get rid of stuff after the next newline
778:         was.gsub!(/\n.*/, '')
779:         was = was[0...15] + "..." if was.size > 18
780: 
781:         raise Sass::SyntaxError.new(
782:           "Invalid CSS after \"#{after}\": expected #{expected}, was \"#{was}\"",
783:           :line => line)
784:       end

@private

[Source]

     # File lib/sass/scss/parser.rb, line 706
706:       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 656
656:       def _interp_string(type)
657:         return unless start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, false]])
658:         res = [start]
659: 
660:         mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, true]]
661:         # @scanner[2].empty? means we've started an interpolated section
662:         while @scanner[2] == '#{'
663:           @scanner.pos -= 2 # Don't consume the #{
664:           res.last.slice!(-2..-1)
665:           res << expr!(:interpolation) << tok(mid_re)
666:         end
667:         res
668:       end

[Source]

     # File lib/sass/scss/parser.rb, line 384
384:       def _selector
385:         # The combinator here allows the "> E" hack
386:         return unless val = combinator || simple_selector_sequence
387:         nl = str{ss}.include?("\n")
388:         res = []
389:         res << val
390:         res << "\n" if nl
391: 
392:         while val = combinator || simple_selector_sequence
393:           res << val
394:           res << "\n" if str{ss}.include?("\n")
395:         end
396:         Selector::Sequence.new(res.compact)
397:       end

[Source]

     # File lib/sass/scss/parser.rb, line 469
469:       def attrib
470:         return unless tok(/\[/)
471:         ss
472:         ns, name = attrib_name!
473:         ss
474: 
475:         if op = tok(/=/) ||
476:             tok(INCLUDES) ||
477:             tok(DASHMATCH) ||
478:             tok(PREFIXMATCH) ||
479:             tok(SUFFIXMATCH) ||
480:             tok(SUBSTRINGMATCH)
481:           @expected = "identifier or string"
482:           ss
483:           if val = tok(IDENT)
484:             val = [val]
485:           else
486:             val = expr!(:interp_string)
487:           end
488:           ss
489:         end
490:         tok(/\]/)
491: 
492:         Selector::Attribute.new(merge(name), merge(ns), op, merge(val))
493:       end

[Source]

     # File lib/sass/scss/parser.rb, line 495
495:       def attrib_name!
496:         if name_or_ns = interp_ident
497:           # E, E|E
498:           if tok(/\|(?!=)/)
499:             ns = name_or_ns
500:             name = interp_ident
501:           else
502:             name = name_or_ns
503:           end
504:         else
505:           # *|E or |E
506:           ns = [tok(/\*/) || ""]
507:           tok!(/\|/)
508:           name = expr!(:interp_ident)
509:         end
510:         return ns, name
511:       end

[Source]

     # File lib/sass/scss/parser.rb, line 296
296:       def block(node, context)
297:         node.has_children = true
298:         tok!(/\{/)
299:         block_contents(node, context)
300:         tok!(/\}/)
301:         node
302:       end

[Source]

     # File lib/sass/scss/parser.rb, line 315
315:       def block_child(context)
316:         return variable || directive || ruleset if context == :stylesheet
317:         variable || directive || declaration_or_ruleset
318:       end

A block may contain declarations and/or rulesets

[Source]

     # File lib/sass/scss/parser.rb, line 305
305:       def block_contents(node, context)
306:         block_given? ? yield : ss_comments(node)
307:         node << (child = block_child(context))
308:         while tok(/;/) || (child && child.has_children)
309:           block_given? ? yield : ss_comments(node)
310:           node << (child = block_child(context))
311:         end
312:         node
313:       end

[Source]

     # File lib/sass/scss/parser.rb, line 437
437:       def class_selector
438:         return unless tok(/\./)
439:         @expected = "class name"
440:         Selector::Class.new(merge(expr!(:interp_ident)))
441:       end

[Source]

     # File lib/sass/scss/parser.rb, line 399
399:       def combinator
400:         tok(PLUS) || tok(GREATER) || tok(TILDE)
401:       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 545
545:       def declaration
546:         # This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
547:         if s = tok(/[:\*\.]|\#(?!\{)/)
548:           @use_property_exception = s !~ /[\.\#]/
549:           name = [s, str{ss}, *expr!(:interp_ident)]
550:         else
551:           return unless name = interp_ident
552:           name = [name] if name.is_a?(String)
553:         end
554:         if comment = tok(COMMENT)
555:           name << comment
556:         end
557:         ss
558: 
559:         tok!(/:/)
560:         space, value = value!
561:         ss
562:         require_block = tok?(/\{/)
563: 
564:         node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new))
565: 
566:         return node unless require_block
567:         nested_properties! node, space
568:       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 335
335:       def declaration_or_ruleset
336:         pos = @scanner.pos
337:         line = @line
338:         old_use_property_exception, @use_property_exception =
339:           @use_property_exception, false
340:         begin
341:           decl = declaration
342:           unless decl && decl.has_children
343:             # We want an exception if it's not there,
344:             # but we don't want to consume if it is
345:             tok!(/[;}]/) unless tok?(/[;}]/)
346:           end
347:           return decl
348:         rescue Sass::SyntaxError => decl_err
349:         end
350: 
351:         @line = line
352:         @scanner.pos = pos
353: 
354:         begin
355:           return ruleset
356:         rescue Sass::SyntaxError => ruleset_err
357:           raise @use_property_exception ? decl_err : ruleset_err
358:         end
359:       ensure
360:         @use_property_exception = old_use_property_exception
361:       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 449
449:       def element_name
450:         return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
451:         if tok(/\|/)
452:           @expected = "element name or *"
453:           ns = name
454:           name = interp_ident || tok!(/\*/)
455:         end
456: 
457:         if name == '*'
458:           Selector::Universal.new(merge(ns))
459:         else
460:           Selector::Element.new(merge(name), merge(ns))
461:         end
462:       end

[Source]

     # File lib/sass/scss/parser.rb, line 186
186:       def else_block(node)
187:         return node unless tok(/@else/)
188:         ss
189:         else_node = block(
190:           Sass::Tree::IfNode.new((sass_script(:parse) if tok(/if/))),
191:           :directive)
192:         node.add_else(else_node)
193:         ss
194:         else_block(node)
195:       end

[Source]

     # File lib/sass/scss/parser.rb, line 757
757:       def expected(name)
758:         self.class.expected(@scanner, @expected || name, @line)
759:       end

[Source]

     # File lib/sass/scss/parser.rb, line 609
609:       def expr
610:         return unless t = term
611:         res = [t, str{ss}]
612: 
613:         while (o = operator) && (t = term)
614:           res << o << t << str{ss}
615:         end
616: 
617:         res
618:       end

[Source]

     # File lib/sass/scss/parser.rb, line 739
739:       def expr!(name)
740:         (e = send(name)) && (return e)
741:         expected(EXPR_NAMES[name] || name.to_s)
742:       end

[Source]

     # File lib/sass/scss/parser.rb, line 197
197:       def extend_directive
198:         node(Sass::Tree::ExtendNode.new(expr!(:selector)))
199:       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 637
637:       def function
638:         return unless name = tok(FUNCTION)
639:         if name == "expression(" || name == "calc("
640:           str, _ = Haml::Shared.balance(@scanner, ?(, ?), 1)
641:           [name, str]
642:         else
643:           [name, str{ss}, expr, tok!(/\)/)]
644:         end
645:       end

[Source]

     # File lib/sass/scss/parser.rb, line 443
443:       def id_selector
444:         return unless tok(/#(?!\{)/)
445:         @expected = "id name"
446:         Selector::Id.new(merge(expr!(:interp_name)))
447:       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:         ss
183:         else_block(node)
184:       end

[Source]

     # File lib/sass/scss/parser.rb, line 201
201:       def import_directive
202:         @expected = "string or url()"
203:         arg = tok(STRING) || tok!(URI)
204:         path = @scanner[1] || @scanner[2] || @scanner[3]
205:         ss
206: 
207:         media = str {media_query_list}.strip
208: 
209:         if !media.strip.empty? || use_css_import?
210:           return node(Sass::Tree::DirectiveNode.new("@import #{arg} #{media}".strip))
211:         end
212: 
213:         node(Sass::Tree::ImportNode.new(path.strip))
214:       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 670
670:       def interp_ident(start = IDENT)
671:         return unless val = tok(start) || interpolation
672:         res = [val]
673:         while val = tok(NAME) || interpolation
674:           res << val
675:         end
676:         res
677:       end

[Source]

     # File lib/sass/scss/parser.rb, line 679
679:       def interp_name
680:         interp_ident NAME
681:       end

[Source]

     # File lib/sass/scss/parser.rb, line 652
652:       def interp_string
653:         _interp_string(:double) || _interp_string(:single)
654:       end

[Source]

     # File lib/sass/scss/parser.rb, line 647
647:       def interpolation
648:         return unless tok(INTERP_START)
649:         sass_script(:parse_interpolated)
650:       end

[Source]

     # File lib/sass/scss/parser.rb, line 464
464:       def interpolation_selector
465:         return unless script = interpolation
466:         Selector::Interpolation.new(script)
467:       end

[Source]

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

[Source]

     # File lib/sass/scss/parser.rb, line 253
253:       def media_expr
254:         return unless tok(/\(/)
255:         ss
256:         @expected = "media feature (e.g. min-device-width, color)"
257:         tok!(IDENT)
258:         ss
259: 
260:         if tok(/:/)
261:           ss; expr!(:expr)
262:         end
263:         tok!(/\)/)
264:         ss
265: 
266:         true
267:       end

[Source]

     # File lib/sass/scss/parser.rb, line 235
235:       def media_query
236:         if tok(/only|not/i)
237:           ss
238:           @expected = "media type (e.g. print, screen)"
239:           tok!(IDENT)
240:           ss
241:         elsif !tok(IDENT) && !media_expr
242:           return
243:         end
244: 
245:         ss
246:         while tok(/and/i)
247:           ss; expr!(:media_expr); ss
248:         end
249: 
250:         true
251:       end

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

[Source]

     # File lib/sass/scss/parser.rb, line 224
224:       def media_query_list
225:         return unless media_query
226: 
227:         ss
228:         while tok(/,/)
229:           ss; expr!(:media_query); ss
230:         end
231: 
232:         true
233:       end

[Source]

     # File lib/sass/scss/parser.rb, line 716
716:       def merge(arr)
717:         arr && Haml::Util.merge_adjacent_strings([arr].flatten)
718:       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 536
536:       def negation
537:         return unless tok(NOT)
538:         ss
539:         @expected = "selector"
540:         sel = element_name || id_selector || class_selector || attrib || expr!(:pseudo)
541:         tok!(/\)/)
542:         Selector::Negation.new(sel)
543:       end

[Source]

     # File lib/sass/scss/parser.rb, line 597
597:       def nested_properties!(node, space)
598:         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
599: 
600:         @use_property_exception = true
601:         @expected = 'expression (e.g. 1px, bold) or "{"'
602:         block(node, :property)
603:       end

[Source]

     # File lib/sass/scss/parser.rb, line 698
698:       def node(node)
699:         node.line = @line
700:         node
701:       end

[Source]

     # File lib/sass/scss/parser.rb, line 279
279:       def operator
280:         # Many of these operators (all except / and ,)
281:         # are disallowed by the CSS spec,
282:         # but they're included here for compatibility
283:         # with some proprietary MS properties
284:         str {ss if tok(/[\/,:.=]/)}
285:       end

[Source]

     # File lib/sass/scss/parser.rb, line 432
432:       def parent_selector
433:         return unless tok(/&/)
434:         Selector::Parent.new
435:       end

[Source]

     # File lib/sass/scss/parser.rb, line 586
586:       def plain_value
587:         return unless tok(/:/)
588:         space = !str {ss}.empty?
589:         @use_property_exception ||= space || !tok?(IDENT)
590: 
591:         expression = expr
592:         expression << tok(IMPORTANT) if expression
593:         # expression, space, value
594:         return expression, space, expression || [""]
595:       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 513
513:       def pseudo
514:         return unless s = tok(/::?/)
515:         @expected = "pseudoclass or pseudoelement"
516:         name = expr!(:interp_ident)
517:         if tok(/\(/)
518:           ss
519:           arg = expr!(:pseudo_expr)
520:           tok!(/\)/)
521:         end
522:         Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
523:       end

[Source]

     # File lib/sass/scss/parser.rb, line 525
525:       def pseudo_expr
526:         return unless e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
527:           interp_string || tok(IDENT) || interpolation
528:         res = [e, str{ss}]
529:         while e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
530:             interp_string || tok(IDENT) || interpolation
531:           res << e << str{ss}
532:         end
533:         res
534:       end

[Source]

     # File lib/sass/scss/parser.rb, line 291
291:       def ruleset
292:         return unless rules = selector_sequence
293:         block(node(Sass::Tree::RuleNode.new(rules.flatten.compact)), :ruleset)
294:       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 708
708:       def sass_script(*args)
709:         parser = self.class.sass_script_parser.new(@scanner, @line,
710:           @scanner.pos - (@scanner.string[0...@scanner.pos].rindex("\n") || 0))
711:         result = parser.send(*args)
712:         @line = parser.line
713:         result
714:       end

[Source]

     # File lib/sass/scss/parser.rb, line 379
379:       def selector
380:         return unless sel = _selector
381:         sel.to_a
382:       end

[Source]

     # File lib/sass/scss/parser.rb, line 363
363:       def selector_sequence
364:         if sel = tok(STATIC_SELECTOR)
365:           return [sel]
366:         end
367: 
368:         rules = []
369:         return unless v = selector
370:         rules.concat v
371: 
372:         while tok(/,/)
373:           rules << ',' << str {ss}
374:           rules.concat expr!(:selector)
375:         end
376:         rules
377:       end

[Source]

     # File lib/sass/scss/parser.rb, line 403
403:       def simple_selector_sequence
404:         # This allows for stuff like http://www.w3.org/TR/css3-animations/#keyframes-
405:         return expr unless e = element_name || id_selector || class_selector ||
406:           attrib || negation || pseudo || parent_selector || interpolation_selector
407:         res = [e]
408: 
409:         # The tok(/\*/) allows the "E*" hack
410:         while v = element_name || id_selector || class_selector ||
411:             attrib || negation || pseudo || interpolation_selector ||
412:             (tok(/\*/) && Selector::Universal.new(nil))
413:           res << v
414:         end
415: 
416:         if tok?(/&/)
417:           begin
418:             expected('"{"')
419:           rescue Sass::SyntaxError => e
420:             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"
421:             raise e
422:           end
423:         end
424: 
425:         Selector::SimpleSequence.new(res)
426:       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 683
683:       def str
684:         @strs.push ""
685:         yield
686:         @strs.last
687:       ensure
688:         @strs.pop
689:       end

[Source]

     # File lib/sass/scss/parser.rb, line 691
691:       def str?
692:         @strs.push ""
693:         yield && @strs.last
694:       ensure
695:         @strs.pop
696:       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 620
620:       def term
621:         unless e = tok(NUMBER) ||
622:             tok(URI) ||
623:             function ||
624:             interp_string ||
625:             tok(UNICODERANGE) ||
626:             tok(IDENT) ||
627:             tok(HEXCOLOR) ||
628:             interpolation
629: 
630:           return unless op = unary_operator
631:           @expected = "number or function"
632:           return [op, tok(NUMBER) || expr!(:function)]
633:         end
634:         e
635:       end

[Source]

     # File lib/sass/scss/parser.rb, line 786
786:       def tok(rx)
787:         res = @scanner.scan(rx)
788:         if res
789:           @line += res.count("\n")
790:           @expected = nil
791:           if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
792:             @strs.each {|s| s << res}
793:           end
794:         end
795: 
796:         res
797:       end

[Source]

     # File lib/sass/scss/parser.rb, line 744
744:       def tok!(rx)
745:         (t = tok(rx)) && (return t)
746:         name = TOK_NAMES[rx]
747: 
748:         unless name
749:           # Display basic regexps as plain old strings
750:           string = rx.source.gsub(/\\(.)/, '\1')
751:           name = rx.source == Regexp.escape(string) ? string.inspect : rx.inspect
752:         end
753: 
754:         expected(name)
755:       end

[Source]

     # File lib/sass/scss/parser.rb, line 735
735:       def tok?(rx)
736:         @scanner.match?(rx)
737:       end

[Source]

     # File lib/sass/scss/parser.rb, line 287
287:       def unary_operator
288:         tok(/[+-]/)
289:       end

[Source]

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

[Source]

     # File lib/sass/scss/parser.rb, line 570
570:       def value!
571:         space = !str {ss}.empty?
572:         @use_property_exception ||= space || !tok?(IDENT)
573: 
574:         return true, Sass::Script::String.new("") if tok?(/\{/)
575:         # This is a bit of a dirty trick:
576:         # if the value is completely static,
577:         # we don't parse it at all, and instead return a plain old string
578:         # containing the value.
579:         # This results in a dramatic speed increase.
580:         if val = tok(STATIC_VALUE)
581:           return space, Sass::Script::String.new(val.strip)
582:         end
583:         return space, sass_script(:parse)
584:       end

[Source]

     # File lib/sass/scss/parser.rb, line 269
269:       def variable
270:         return unless tok(/\$/)
271:         name = tok!(IDENT)
272:         ss; tok!(/:/); ss
273: 
274:         expr = sass_script(:parse)
275:         guarded = tok(DEFAULT)
276:         node(Sass::Tree::VariableNode.new(name, expr, guarded))
277:       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]