1 | // sass.hpp must go before all system headers to get the |
2 | // __EXTENSIONS__ fix on Solaris. |
3 | #include "sass.hpp" |
4 | |
5 | #include "parser.hpp" |
6 | |
7 | namespace Sass { |
8 | |
9 | using namespace Prelexer; |
10 | using namespace Constants; |
11 | |
12 | ComplexSelectorObj Parser::parseComplexSelector(bool chroot) |
13 | { |
14 | |
15 | NESTING_GUARD(nestings); |
16 | |
17 | lex < block_comment >(); |
18 | advanceToNextToken(); |
19 | |
20 | ComplexSelectorObj sel = SASS_MEMORY_NEW(ComplexSelector, pstate); |
21 | |
22 | if (peek < end_of_file >()) return sel; |
23 | |
24 | while (true) { |
25 | |
26 | lex < block_comment >(); |
27 | advanceToNextToken(); |
28 | |
29 | // check for child (+) combinator |
30 | if (lex < exactly < selector_combinator_child > >()) { |
31 | sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::CHILD, peek_newline())); |
32 | } |
33 | // check for general sibling (~) combinator |
34 | else if (lex < exactly < selector_combinator_general > >()) { |
35 | sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::GENERAL, peek_newline())); |
36 | } |
37 | // check for adjecant sibling (+) combinator |
38 | else if (lex < exactly < selector_combinator_adjacent > >()) { |
39 | sel->append(SASS_MEMORY_NEW(SelectorCombinator, pstate, SelectorCombinator::ADJACENT, peek_newline())); |
40 | } |
41 | // check if we can parse a compound selector |
42 | else if (CompoundSelectorObj compound = parseCompoundSelector()) { |
43 | sel->append(element: compound); |
44 | } |
45 | else { |
46 | break; |
47 | } |
48 | } |
49 | |
50 | if (sel->empty()) return {}; |
51 | |
52 | // check if we parsed any parent references |
53 | sel->chroots(chroots__: sel->has_real_parent_ref() || chroot); |
54 | |
55 | sel->update_pstate(pstate); |
56 | |
57 | return sel; |
58 | |
59 | } |
60 | |
61 | SelectorListObj Parser::parseSelectorList(bool chroot) |
62 | { |
63 | |
64 | bool reloop; |
65 | bool had_linefeed = false; |
66 | NESTING_GUARD(nestings); |
67 | SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate); |
68 | |
69 | if (peek_css< alternatives < end_of_file, exactly <'{'>, exactly <','> > >()) { |
70 | css_error(msg: "Invalid CSS" , prefix: " after " , middle: ": expected selector, was " ); |
71 | } |
72 | |
73 | do { |
74 | reloop = false; |
75 | |
76 | had_linefeed = had_linefeed || peek_newline(); |
77 | |
78 | if (peek_css< alternatives < class_char < selector_list_delims > > >()) |
79 | break; // in case there are superfluous commas at the end |
80 | |
81 | // now parse the complex selector |
82 | ComplexSelectorObj complex = parseComplexSelector(chroot); |
83 | if (complex.isNull()) return list.detach(); |
84 | complex->hasPreLineFeed(hasPreLineFeed__: had_linefeed); |
85 | |
86 | had_linefeed = false; |
87 | |
88 | while (peek_css< exactly<','> >()) |
89 | { |
90 | lex< css_comments >(lazy: false); |
91 | // consume everything up and including the comma separator |
92 | reloop = lex< exactly<','> >() != 0; |
93 | // remember line break (also between some commas) |
94 | had_linefeed = had_linefeed || peek_newline(); |
95 | // remember line break (also between some commas) |
96 | } |
97 | list->append(element: complex); |
98 | |
99 | } while (reloop); |
100 | |
101 | while (lex_css< kwd_optional >()) { |
102 | list->is_optional(is_optional__: true); |
103 | } |
104 | |
105 | // update for end position |
106 | list->update_pstate(pstate); |
107 | |
108 | return list.detach(); |
109 | } |
110 | |
111 | // parse one compound selector, which is basically |
112 | // a list of simple selectors (directly adjacent) |
113 | // lex them exactly (without skipping white-space) |
114 | CompoundSelectorObj Parser::parseCompoundSelector() |
115 | { |
116 | // init an empty compound selector wrapper |
117 | CompoundSelectorObj seq = SASS_MEMORY_NEW(CompoundSelector, pstate); |
118 | |
119 | // skip initial white-space |
120 | lex < block_comment >(); |
121 | advanceToNextToken(); |
122 | |
123 | if (lex< exactly<'&'> >(lazy: false)) |
124 | { |
125 | // ToDo: check the conditions and try to simplify flag passing |
126 | if (!allow_parent) error(msg: "Parent selectors aren't allowed here." ); |
127 | // Create and append a new parent selector object |
128 | seq->hasRealParent(hasRealParent__: true); |
129 | } |
130 | |
131 | // parse list |
132 | while (true) |
133 | { |
134 | // remove all block comments |
135 | // leaves trailing white-space |
136 | lex < block_comment >(); |
137 | // parse parent selector |
138 | if (lex< exactly<'&'> >(lazy: false)) |
139 | { |
140 | // parent selector only allowed at start |
141 | // upcoming Sass may allow also trailing |
142 | SourceSpan state(pstate); |
143 | sass::string found("&" ); |
144 | if (lex < identifier >()) { |
145 | found += sass::string(lexed); |
146 | } |
147 | sass::string sel(seq->hasRealParent() ? "&" : "" ); |
148 | if (!seq->empty()) { sel = seq->last()->to_string(opt: { NESTED, 5 }); } |
149 | // ToDo: parser should throw parser exceptions |
150 | error(msg: "Invalid CSS after \"" + sel + "\": expected \"{\", was \"" + found + "\"\n\n" |
151 | "\"" + found + "\" may only be used at the beginning of a compound selector." ); |
152 | } |
153 | // parse functional |
154 | else if (match < re_functional >()) |
155 | { |
156 | seq->append(element: parse_simple_selector()); |
157 | } |
158 | |
159 | // parse type selector |
160 | else if (lex< re_type_selector >(lazy: false)) |
161 | { |
162 | seq->append(SASS_MEMORY_NEW(TypeSelector, pstate, lexed)); |
163 | } |
164 | // peek for abort conditions |
165 | else if (peek< spaces >()) break; |
166 | else if (peek< end_of_file >()) { break; } |
167 | else if (peek_css < class_char < selector_combinator_ops > >()) break; |
168 | else if (peek_css < class_char < complex_selector_delims > >()) break; |
169 | // otherwise parse another simple selector |
170 | else { |
171 | SimpleSelectorObj sel = parse_simple_selector(); |
172 | if (!sel) return {}; |
173 | seq->append(element: sel); |
174 | } |
175 | } |
176 | // EO while true |
177 | |
178 | if (seq && !peek_css<alternatives<end_of_file,exactly<'{'>>>()) { |
179 | seq->hasPostLineBreak(hasPostLineBreak__: peek_newline()); |
180 | } |
181 | |
182 | // We may have set hasRealParent |
183 | if (seq && seq->empty() && !seq->hasRealParent()) return {}; |
184 | |
185 | return seq; |
186 | } |
187 | |
188 | |
189 | } |
190 | |