// sass.hpp must go before all system headers to get the // __EXTENSIONS__ fix on Solaris. #include "sass.hpp" #include "parser.hpp" namespace Sass { using namespace Prelexer; using namespace Constants; ComplexSelectorObj Parser::parseComplexSelector(bool chroot) { NESTING_GUARD(nestings); lex < block_comment >(); advanceToNextToken(); ComplexSelectorObj sel = SASS_MEMORY_NEW(ComplexSelector, pstate); if (peek < end_of_file >()) return sel; while (true) { lex < block_comment >(); advanceToNextToken(); // check for child (+) combinator if (lex < exactly < selector_combinator_child > >()) { sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::CHILD, peek_newline())); } // check for general sibling (~) combinator else if (lex < exactly < selector_combinator_general > >()) { sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::GENERAL, peek_newline())); } // check for adjecant sibling (+) combinator else if (lex < exactly < selector_combinator_adjacent > >()) { sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::ADJACENT, peek_newline())); } // check if we can parse a compound selector else if (CompoundSelectorObj compound = parseCompoundSelector()) { sel->append(compound); } else { break; } } if (sel->empty()) return {}; // check if we parsed any parent references sel->chroots(sel->has_real_parent_ref() || chroot); sel->update_pstate(pstate); return sel; } SelectorListObj Parser::parseSelectorList(bool chroot) { bool reloop; bool had_linefeed = false; NESTING_GUARD(nestings); SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate); if (peek_css< alternatives < end_of_file, exactly <'{'>, exactly <','> > >()) { css_error("Invalid CSS", " after ", ": expected selector, was "); } do { reloop = false; had_linefeed = had_linefeed || peek_newline(); if (peek_css< alternatives < class_char < selector_list_delims > > >()) break; // in case there are superfluous commas at the end // now parse the complex selector ComplexSelectorObj complex = parseComplexSelector(chroot); if (complex.isNull()) return list.detach(); complex->hasPreLineFeed(had_linefeed); had_linefeed = false; while (peek_css< exactly<','> >()) { lex< css_comments >(false); // consume everything up and including the comma separator reloop = lex< exactly<','> >() != 0; // remember line break (also between some commas) had_linefeed = had_linefeed || peek_newline(); // remember line break (also between some commas) } list->append(complex); } while (reloop); while (lex_css< kwd_optional >()) { list->is_optional(true); } // update for end position list->update_pstate(pstate); return list.detach(); } // parse one compound selector, which is basically // a list of simple selectors (directly adjacent) // lex them exactly (without skipping white-space) CompoundSelectorObj Parser::parseCompoundSelector() { // init an empty compound selector wrapper CompoundSelectorObj seq = SASS_MEMORY_NEW(CompoundSelector, pstate); // skip initial white-space lex < block_comment >(); advanceToNextToken(); if (lex< exactly<'&'> >(false)) { // ToDo: check the conditions and try to simplify flag passing if (!allow_parent) error("Parent selectors aren't allowed here."); // Create and append a new parent selector object seq->hasRealParent(true); } // parse list while (true) { // remove all block comments // leaves trailing white-space lex < block_comment >(); // parse parent selector if (lex< exactly<'&'> >(false)) { // parent selector only allowed at start // upcoming Sass may allow also trailing SourceSpan state(pstate); sass::string found("&"); if (lex < identifier >()) { found += sass::string(lexed); } sass::string sel(seq->hasRealParent() ? "&" : ""); if (!seq->empty()) { sel = seq->last()->to_string({ NESTED, 5 }); } // ToDo: parser should throw parser exceptions error("Invalid CSS after \"" + sel + "\": expected \"{\", was \"" + found + "\"\n\n" "\"" + found + "\" may only be used at the beginning of a compound selector."); } // parse functional else if (match < re_functional >()) { seq->append(parse_simple_selector()); } // parse type selector else if (lex< re_type_selector >(false)) { seq->append(SASS_MEMORY_NEW(TypeSelector, pstate, lexed)); } // peek for abort conditions else if (peek< spaces >()) break; else if (peek< end_of_file >()) { break; } else if (peek_css < class_char < selector_combinator_ops > >()) break; else if (peek_css < class_char < complex_selector_delims > >()) break; // otherwise parse another simple selector else { SimpleSelectorObj sel = parse_simple_selector(); if (!sel) return {}; seq->append(sel); } } // EO while true if (seq && !peek_css>>()) { seq->hasPostLineBreak(peek_newline()); } // We may have set hasRealParent if (seq && seq->empty() && !seq->hasRealParent()) return {}; return seq; } }