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 #include "color_maps.hpp" 7 #include "util_string.hpp" 8 9 // Notes about delayed: some ast nodes can have delayed evaluation so 10 // they can preserve their original semantics if needed. This is most 11 // prominently exhibited by the division operation, since it is not 12 // only a valid operation, but also a valid css statement (i.e. for 13 // fonts, as in `16px/24px`). When parsing lists and expression we 14 // unwrap single items from lists and other operations. A nested list 15 // must not be delayed, only the items of the first level sometimes 16 // are delayed (as with argument lists). To achieve this we need to 17 // pass status to the list parser, so this can be set correctly. 18 // Another case with delayed values are colors. In compressed mode 19 // only processed values get compressed (other are left as written). 20 21 22 namespace Sass { 23 using namespace Constants; 24 using namespace Prelexer; 25 26 Parser(SourceData * source,Context & ctx,Backtraces traces,bool allow_parent)27 Parser::Parser(SourceData* source, Context& ctx, Backtraces traces, bool allow_parent) : 28 SourceSpan(source), 29 ctx(ctx), 30 source(source), 31 begin(source->begin()), 32 position(source->begin()), 33 end(source->end()), 34 before_token(0, 0), 35 after_token(0, 0), 36 pstate(source->getSourceSpan()), 37 traces(traces), 38 indentation(0), 39 nestings(0), 40 allow_parent(allow_parent) 41 { 42 Block_Obj root = SASS_MEMORY_NEW(Block, pstate); 43 stack.push_back(Scope::Root); 44 block_stack.push_back(root); 45 root->is_root(true); 46 } 47 advanceToNextToken()48 void Parser::advanceToNextToken() { 49 lex < css_comments >(false); 50 // advance to position 51 pstate.position += pstate.offset; 52 pstate.offset.column = 0; 53 pstate.offset.line = 0; 54 } 55 parse_selector(SourceData * source,Context & ctx,Backtraces traces,bool allow_parent)56 SelectorListObj Parser::parse_selector(SourceData* source, Context& ctx, Backtraces traces, bool allow_parent) 57 { 58 Parser p(source, ctx, traces, allow_parent); 59 // ToDo: remap the source-map entries somehow 60 return p.parseSelectorList(false); 61 } 62 peek_newline(const char * start)63 bool Parser::peek_newline(const char* start) 64 { 65 return peek_linefeed(start ? start : position) 66 && ! peek_css<exactly<'{'>>(start); 67 } 68 69 /* main entry point to parse root block */ parse()70 Block_Obj Parser::parse() 71 { 72 73 // consume unicode BOM 74 read_bom(); 75 76 // scan the input to find invalid utf8 sequences 77 const char* it = utf8::find_invalid(position, end); 78 79 // report invalid utf8 80 if (it != end) { 81 pstate.position += Offset::init(position, it); 82 traces.push_back(Backtrace(pstate)); 83 throw Exception::InvalidSass(pstate, traces, "Invalid UTF-8 sequence"); 84 } 85 86 // create a block AST node to hold children 87 Block_Obj root = SASS_MEMORY_NEW(Block, pstate, 0, true); 88 89 // check seems a bit esoteric but works 90 if (ctx.resources.size() == 1) { 91 // apply headers only on very first include 92 ctx.apply_custom_headers(root, getPath(), pstate); 93 } 94 95 // parse children nodes 96 block_stack.push_back(root); 97 parse_block_nodes(true); 98 block_stack.pop_back(); 99 100 // update final position 101 root->update_pstate(pstate); 102 103 if (position != end) { 104 css_error("Invalid CSS", " after ", ": expected selector or at-rule, was "); 105 } 106 107 return root; 108 } 109 110 111 // convenience function for block parsing 112 // will create a new block ad-hoc for you 113 // this is the base block parsing function parse_css_block(bool is_root)114 Block_Obj Parser::parse_css_block(bool is_root) 115 { 116 117 // parse comments before block 118 // lex < optional_css_comments >(); 119 120 // lex mandatory opener or error out 121 if (!lex_css < exactly<'{'> >()) { 122 css_error("Invalid CSS", " after ", ": expected \"{\", was "); 123 } 124 // create new block and push to the selector stack 125 Block_Obj block = SASS_MEMORY_NEW(Block, pstate, 0, is_root); 126 block_stack.push_back(block); 127 128 if (!parse_block_nodes(is_root)) css_error("Invalid CSS", " after ", ": expected \"}\", was "); 129 130 if (!lex_css < exactly<'}'> >()) { 131 css_error("Invalid CSS", " after ", ": expected \"}\", was "); 132 } 133 134 // update for end position 135 // this seems to be done somewhere else 136 // but that fixed selector schema issue 137 // block->update_pstate(pstate); 138 139 // parse comments after block 140 // lex < optional_css_comments >(); 141 142 block_stack.pop_back(); 143 144 return block; 145 } 146 147 // convenience function for block parsing 148 // will create a new block ad-hoc for you 149 // also updates the `in_at_root` flag parse_block(bool is_root)150 Block_Obj Parser::parse_block(bool is_root) 151 { 152 return parse_css_block(is_root); 153 } 154 155 // the main block parsing function 156 // parses stuff between `{` and `}` parse_block_nodes(bool is_root)157 bool Parser::parse_block_nodes(bool is_root) 158 { 159 160 // loop until end of string 161 while (position < end) { 162 163 // we should be able to refactor this 164 parse_block_comments(); 165 lex < css_whitespace >(); 166 167 if (lex < exactly<';'> >()) continue; 168 if (peek < end_of_file >()) return true; 169 if (peek < exactly<'}'> >()) return true; 170 171 if (parse_block_node(is_root)) continue; 172 173 parse_block_comments(); 174 175 if (lex_css < exactly<';'> >()) continue; 176 if (peek_css < end_of_file >()) return true; 177 if (peek_css < exactly<'}'> >()) return true; 178 179 // illegal sass 180 return false; 181 } 182 // return success 183 return true; 184 } 185 186 // parser for a single node in a block 187 // semicolons must be lexed beforehand parse_block_node(bool is_root)188 bool Parser::parse_block_node(bool is_root) { 189 190 Block_Obj block = block_stack.back(); 191 192 parse_block_comments(); 193 194 // throw away white-space 195 // includes line comments 196 lex < css_whitespace >(); 197 198 Lookahead lookahead_result; 199 200 // also parse block comments 201 202 // first parse everything that is allowed in functions 203 if (lex < variable >(true)) { block->append(parse_assignment()); } 204 else if (lex < kwd_err >(true)) { block->append(parse_error()); } 205 else if (lex < kwd_dbg >(true)) { block->append(parse_debug()); } 206 else if (lex < kwd_warn >(true)) { block->append(parse_warning()); } 207 else if (lex < kwd_if_directive >(true)) { block->append(parse_if_directive()); } 208 else if (lex < kwd_for_directive >(true)) { block->append(parse_for_directive()); } 209 else if (lex < kwd_each_directive >(true)) { block->append(parse_each_directive()); } 210 else if (lex < kwd_while_directive >(true)) { block->append(parse_while_directive()); } 211 else if (lex < kwd_return_directive >(true)) { block->append(parse_return_directive()); } 212 213 // parse imports to process later 214 else if (lex < kwd_import >(true)) { 215 Scope parent = stack.empty() ? Scope::Rules : stack.back(); 216 if (parent != Scope::Function && parent != Scope::Root && parent != Scope::Rules && parent != Scope::Media) { 217 if (! peek_css< uri_prefix >(position)) { // this seems to go in ruby sass 3.4.20 218 error("Import directives may not be used within control directives or mixins."); 219 } 220 } 221 // this puts the parsed doc into sheets 222 // import stub will fetch this in expand 223 Import_Obj imp = parse_import(); 224 // if it is a url, we only add the statement 225 if (!imp->urls().empty()) block->append(imp); 226 // process all resources now (add Import_Stub nodes) 227 for (size_t i = 0, S = imp->incs().size(); i < S; ++i) { 228 block->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i])); 229 } 230 } 231 232 else if (lex < kwd_extend >(true)) { 233 Lookahead lookahead = lookahead_for_include(position); 234 if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was "); 235 SelectorListObj target; 236 if (!lookahead.has_interpolants) { 237 LOCAL_FLAG(allow_parent, false); 238 auto selector = parseSelectorList(true); 239 auto extender = SASS_MEMORY_NEW(ExtendRule, pstate, selector); 240 extender->isOptional(selector && selector->is_optional()); 241 block->append(extender); 242 } 243 else { 244 LOCAL_FLAG(allow_parent, false); 245 auto selector = parse_selector_schema(lookahead.found, true); 246 auto extender = SASS_MEMORY_NEW(ExtendRule, pstate, selector); 247 // A schema is not optional yet, check once it is evaluated 248 // extender->isOptional(selector && selector->is_optional()); 249 block->append(extender); 250 } 251 252 } 253 254 // selector may contain interpolations which need delayed evaluation 255 else if ( 256 !(lookahead_result = lookahead_for_selector(position)).error && 257 !lookahead_result.is_custom_property 258 ) 259 { 260 block->append(parse_ruleset(lookahead_result)); 261 } 262 263 // parse multiple specific keyword directives 264 else if (lex < kwd_media >(true)) { block->append(parseMediaRule()); } 265 else if (lex < kwd_at_root >(true)) { block->append(parse_at_root_block()); } 266 else if (lex < kwd_include_directive >(true)) { block->append(parse_include_directive()); } 267 else if (lex < kwd_content_directive >(true)) { block->append(parse_content_directive()); } 268 else if (lex < kwd_supports_directive >(true)) { block->append(parse_supports_directive()); } 269 else if (lex < kwd_mixin >(true)) { block->append(parse_definition(Definition::MIXIN)); } 270 else if (lex < kwd_function >(true)) { block->append(parse_definition(Definition::FUNCTION)); } 271 272 // ignore the @charset directive for now 273 else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); } 274 275 else if (lex < exactly < else_kwd >>(true)) { error("Invalid CSS: @else must come after @if"); } 276 277 // generic at keyword (keep last) 278 else if (lex< at_keyword >(true)) { block->append(parse_directive()); } 279 280 else if (is_root && stack.back() != Scope::AtRoot /* && block->is_root() */) { 281 lex< css_whitespace >(); 282 if (position >= end) return true; 283 css_error("Invalid CSS", " after ", ": expected 1 selector or at-rule, was "); 284 } 285 // parse a declaration 286 else 287 { 288 // ToDo: how does it handle parse errors? 289 // maybe we are expected to parse something? 290 Declaration_Obj decl = parse_declaration(); 291 decl->tabs(indentation); 292 block->append(decl); 293 // maybe we have a "sub-block" 294 if (peek< exactly<'{'> >()) { 295 if (decl->is_indented()) ++ indentation; 296 // parse a propset that rides on the declaration's property 297 stack.push_back(Scope::Properties); 298 decl->block(parse_block()); 299 stack.pop_back(); 300 if (decl->is_indented()) -- indentation; 301 } 302 } 303 // something matched 304 return true; 305 } 306 // EO parse_block_nodes 307 308 // parse imports inside the parse_import()309 Import_Obj Parser::parse_import() 310 { 311 Import_Obj imp = SASS_MEMORY_NEW(Import, pstate); 312 sass::vector<std::pair<sass::string,Function_Call_Obj>> to_import; 313 bool first = true; 314 do { 315 while (lex< block_comment >()); 316 if (lex< quoted_string >()) { 317 to_import.push_back(std::pair<sass::string,Function_Call_Obj>(sass::string(lexed), {})); 318 } 319 else if (lex< uri_prefix >()) { 320 Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate); 321 Function_Call_Obj result = SASS_MEMORY_NEW(Function_Call, pstate, sass::string("url"), args); 322 323 if (lex< quoted_string >()) { 324 ExpressionObj quoted_url = parse_string(); 325 args->append(SASS_MEMORY_NEW(Argument, quoted_url->pstate(), quoted_url)); 326 } 327 else if (String_Obj string_url = parse_url_function_argument()) { 328 args->append(SASS_MEMORY_NEW(Argument, string_url->pstate(), string_url)); 329 } 330 else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) { 331 ExpressionObj braced_url = parse_list(); // parse_interpolated_chunk(lexed); 332 args->append(SASS_MEMORY_NEW(Argument, braced_url->pstate(), braced_url)); 333 } 334 else { 335 error("malformed URL"); 336 } 337 if (!lex< exactly<')'> >()) error("URI is missing ')'"); 338 to_import.push_back(std::pair<sass::string, Function_Call_Obj>("", result)); 339 } 340 else { 341 if (first) error("@import directive requires a url or quoted path"); 342 else error("expecting another url or quoted path in @import list"); 343 } 344 first = false; 345 } while (lex_css< exactly<','> >()); 346 347 if (!peek_css< alternatives< exactly<';'>, exactly<'}'>, end_of_file > >()) { 348 List_Obj import_queries = parse_media_queries(); 349 imp->import_queries(import_queries); 350 } 351 352 for(auto location : to_import) { 353 if (location.second) { 354 imp->urls().push_back(location.second); 355 } 356 // check if custom importers want to take over the handling 357 else if (!ctx.call_importers(unquote(location.first), getPath(), pstate, imp)) { 358 // nobody wants it, so we do our import 359 ctx.import_url(imp, location.first, getPath()); 360 } 361 } 362 363 return imp; 364 } 365 parse_definition(Definition::Type which_type)366 Definition_Obj Parser::parse_definition(Definition::Type which_type) 367 { 368 sass::string which_str(lexed); 369 if (!lex< identifier >()) error("invalid name in " + which_str + " definition"); 370 sass::string name(Util::normalize_underscores(lexed)); 371 if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not")) 372 { error("Invalid function name \"" + name + "\"."); } 373 SourceSpan source_position_of_def = pstate; 374 Parameters_Obj params = parse_parameters(); 375 if (which_type == Definition::MIXIN) stack.push_back(Scope::Mixin); 376 else stack.push_back(Scope::Function); 377 Block_Obj body = parse_block(); 378 stack.pop_back(); 379 return SASS_MEMORY_NEW(Definition, source_position_of_def, name, params, body, which_type); 380 } 381 parse_parameters()382 Parameters_Obj Parser::parse_parameters() 383 { 384 Parameters_Obj params = SASS_MEMORY_NEW(Parameters, pstate); 385 if (lex_css< exactly<'('> >()) { 386 // if there's anything there at all 387 if (!peek_css< exactly<')'> >()) { 388 do { 389 if (peek< exactly<')'> >()) break; 390 params->append(parse_parameter()); 391 } while (lex_css< exactly<','> >()); 392 } 393 if (!lex_css< exactly<')'> >()) { 394 css_error("Invalid CSS", " after ", ": expected \")\", was "); 395 } 396 } 397 return params; 398 } 399 parse_parameter()400 Parameter_Obj Parser::parse_parameter() 401 { 402 if (peek< alternatives< exactly<','>, exactly< '{' >, exactly<';'> > >()) { 403 css_error("Invalid CSS", " after ", ": expected variable (e.g. $foo), was "); 404 } 405 while (lex< alternatives < spaces, block_comment > >()); 406 lex < variable >(); 407 sass::string name(Util::normalize_underscores(lexed)); 408 SourceSpan pos = pstate; 409 ExpressionObj val; 410 bool is_rest = false; 411 while (lex< alternatives < spaces, block_comment > >()); 412 if (lex< exactly<':'> >()) { // there's a default value 413 while (lex< block_comment >()); 414 val = parse_space_list(); 415 } 416 else if (lex< exactly< ellipsis > >()) { 417 is_rest = true; 418 } 419 return SASS_MEMORY_NEW(Parameter, pos, name, val, is_rest); 420 } 421 parse_arguments()422 Arguments_Obj Parser::parse_arguments() 423 { 424 Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate); 425 if (lex_css< exactly<'('> >()) { 426 // if there's anything there at all 427 if (!peek_css< exactly<')'> >()) { 428 do { 429 if (peek< exactly<')'> >()) break; 430 args->append(parse_argument()); 431 } while (lex_css< exactly<','> >()); 432 } 433 if (!lex_css< exactly<')'> >()) { 434 css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); 435 } 436 } 437 return args; 438 } 439 parse_argument()440 Argument_Obj Parser::parse_argument() 441 { 442 if (peek< alternatives< exactly<','>, exactly< '{' >, exactly<';'> > >()) { 443 css_error("Invalid CSS", " after ", ": expected \")\", was "); 444 } 445 if (peek_css< sequence < exactly< hash_lbrace >, exactly< rbrace > > >()) { 446 position += 2; 447 css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); 448 } 449 450 Argument_Obj arg; 451 if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) { 452 lex_css< variable >(); 453 sass::string name(Util::normalize_underscores(lexed)); 454 SourceSpan p = pstate; 455 lex_css< exactly<':'> >(); 456 ExpressionObj val = parse_space_list(); 457 arg = SASS_MEMORY_NEW(Argument, p, val, name); 458 } 459 else { 460 bool is_arglist = false; 461 bool is_keyword = false; 462 ExpressionObj val = parse_space_list(); 463 List* l = Cast<List>(val); 464 if (lex_css< exactly< ellipsis > >()) { 465 if (val->concrete_type() == Expression::MAP || ( 466 (l != NULL && l->separator() == SASS_HASH) 467 )) is_keyword = true; 468 else is_arglist = true; 469 } 470 arg = SASS_MEMORY_NEW(Argument, pstate, val, "", is_arglist, is_keyword); 471 } 472 return arg; 473 } 474 parse_assignment()475 Assignment_Obj Parser::parse_assignment() 476 { 477 sass::string name(Util::normalize_underscores(lexed)); 478 SourceSpan var_source_position = pstate; 479 if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement"); 480 if (peek_css< alternatives < exactly<';'>, end_of_file > >()) { 481 css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); 482 } 483 ExpressionObj val; 484 Lookahead lookahead = lookahead_for_value(position); 485 if (lookahead.has_interpolants && lookahead.found) { 486 val = parse_value_schema(lookahead.found); 487 } else { 488 val = parse_list(); 489 } 490 bool is_default = false; 491 bool is_global = false; 492 while (peek< alternatives < default_flag, global_flag > >()) { 493 if (lex< default_flag >()) is_default = true; 494 else if (lex< global_flag >()) is_global = true; 495 } 496 return SASS_MEMORY_NEW(Assignment, var_source_position, name, val, is_default, is_global); 497 } 498 499 // a ruleset connects a selector and a block parse_ruleset(Lookahead lookahead)500 StyleRuleObj Parser::parse_ruleset(Lookahead lookahead) 501 { 502 NESTING_GUARD(nestings); 503 // inherit is_root from parent block 504 Block_Obj parent = block_stack.back(); 505 bool is_root = parent && parent->is_root(); 506 // make sure to move up the the last position 507 lex < optional_css_whitespace >(false, true); 508 // create the connector object (add parts later) 509 StyleRuleObj ruleset = SASS_MEMORY_NEW(StyleRule, pstate); 510 // parse selector static or as schema to be evaluated later 511 if (lookahead.parsable) { 512 ruleset->selector(parseSelectorList(false)); 513 } 514 else { 515 SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate); 516 auto sc = parse_selector_schema(lookahead.position, false); 517 ruleset->schema(sc); 518 ruleset->selector(list); 519 } 520 // then parse the inner block 521 stack.push_back(Scope::Rules); 522 ruleset->block(parse_block()); 523 stack.pop_back(); 524 // update for end position 525 ruleset->update_pstate(pstate); 526 ruleset->block()->update_pstate(pstate); 527 // need this info for coherence checks 528 ruleset->is_root(is_root); 529 // return AST Node 530 return ruleset; 531 } 532 533 // parse a selector schema that will be evaluated in the eval stage 534 // uses a string schema internally to do the actual schema handling 535 // in the eval stage we will be re-parse it into an actual selector parse_selector_schema(const char * end_of_selector,bool chroot)536 Selector_Schema_Obj Parser::parse_selector_schema(const char* end_of_selector, bool chroot) 537 { 538 NESTING_GUARD(nestings); 539 // move up to the start 540 lex< optional_spaces >(); 541 const char* i = position; 542 // selector schema re-uses string schema implementation 543 String_Schema* schema = SASS_MEMORY_NEW(String_Schema, pstate); 544 // the selector schema is pretty much just a wrapper for the string schema 545 Selector_Schema_Obj selector_schema = SASS_MEMORY_NEW(Selector_Schema, pstate, schema); 546 selector_schema->connect_parent(chroot == false); 547 548 // process until end 549 while (i < end_of_selector) { 550 // try to parse multiple interpolants 551 if (const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, end_of_selector)) { 552 // accumulate the preceding segment if the position has advanced 553 if (i < p) { 554 sass::string parsed(i, p); 555 String_Constant_Obj str = SASS_MEMORY_NEW(String_Constant, pstate, parsed); 556 pstate.position += Offset(parsed); 557 str->update_pstate(pstate); 558 schema->append(str); 559 } 560 561 // skip over all nested inner interpolations up to our own delimiter 562 const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, end_of_selector); 563 // check if the interpolation never ends of only contains white-space (error out) 564 if (!j || peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { 565 position = p+2; 566 css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); 567 } 568 // pass inner expression to the parser to resolve nested interpolations 569 LocalOption<const char*> partEnd(end, j); 570 LocalOption<const char*> partBeg(position, p + 2); 571 ExpressionObj interpolant = parse_list(); 572 // set status on the list expression 573 interpolant->is_interpolant(true); 574 // schema->has_interpolants(true); 575 // add to the string schema 576 schema->append(interpolant); 577 // advance parser state 578 pstate.position.add(p+2, j); 579 // advance position 580 i = j; 581 } 582 // no more interpolants have been found 583 // add the last segment if there is one 584 else { 585 // make sure to add the last bits of the string up to the end (if any) 586 if (i < end_of_selector) { 587 sass::string parsed(i, end_of_selector); 588 String_Constant_Obj str = SASS_MEMORY_NEW(String_Constant, pstate, parsed); 589 pstate.position += Offset(parsed); 590 str->update_pstate(pstate); 591 i = end_of_selector; 592 schema->append(str); 593 } 594 // exit loop 595 } 596 } 597 // EO until eos 598 599 // update position 600 position = i; 601 602 // update for end position 603 selector_schema->update_pstate(pstate); 604 schema->update_pstate(pstate); 605 606 after_token = before_token = pstate.position; 607 608 // return parsed result 609 return selector_schema.detach(); 610 } 611 // EO parse_selector_schema 612 parse_charset_directive()613 void Parser::parse_charset_directive() 614 { 615 lex < 616 sequence < 617 quoted_string, 618 optional_spaces, 619 exactly <';'> 620 > 621 >(); 622 } 623 624 // called after parsing `kwd_include_directive` parse_include_directive()625 Mixin_Call_Obj Parser::parse_include_directive() 626 { 627 // lex identifier into `lexed` var 628 lex_identifier(); // may error out 629 // normalize underscores to hyphens 630 sass::string name(Util::normalize_underscores(lexed)); 631 // create the initial mixin call object 632 Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, pstate, name, Arguments_Obj{}); 633 // parse mandatory arguments 634 call->arguments(parse_arguments()); 635 // parse using and optional block parameters 636 bool has_parameters = lex< kwd_using >() != nullptr; 637 638 if (has_parameters) { 639 if (!peek< exactly<'('> >()) css_error("Invalid CSS", " after ", ": expected \"(\", was "); 640 } else { 641 if (peek< exactly<'('> >()) css_error("Invalid CSS", " after ", ": expected \";\", was "); 642 } 643 644 if (has_parameters) call->block_parameters(parse_parameters()); 645 646 // parse optional block 647 if (peek < exactly <'{'> >()) { 648 call->block(parse_block()); 649 } 650 else if (has_parameters) { 651 css_error("Invalid CSS", " after ", ": expected \"{\", was "); 652 } 653 // return ast node 654 return call.detach(); 655 } 656 // EO parse_include_directive 657 658 parse_simple_selector()659 SimpleSelectorObj Parser::parse_simple_selector() 660 { 661 lex < css_comments >(false); 662 if (lex< class_name >()) { 663 return SASS_MEMORY_NEW(ClassSelector, pstate, lexed); 664 } 665 else if (lex< id_name >()) { 666 return SASS_MEMORY_NEW(IDSelector, pstate, lexed); 667 } 668 else if (lex< alternatives < variable, number, static_reference_combinator > >()) { 669 return SASS_MEMORY_NEW(TypeSelector, pstate, lexed); 670 } 671 else if (peek< pseudo_not >()) { 672 return parse_negated_selector2(); 673 } 674 else if (peek< re_pseudo_selector >()) { 675 return parse_pseudo_selector(); 676 } 677 else if (peek< exactly<':'> >()) { 678 return parse_pseudo_selector(); 679 } 680 else if (lex < exactly<'['> >()) { 681 return parse_attribute_selector(); 682 } 683 else if (lex< placeholder >()) { 684 return SASS_MEMORY_NEW(PlaceholderSelector, pstate, lexed); 685 } 686 else { 687 css_error("Invalid CSS", " after ", ": expected selector, was "); 688 } 689 // failed 690 return {}; 691 } 692 parse_negated_selector2()693 PseudoSelectorObj Parser::parse_negated_selector2() 694 { 695 lex< pseudo_not >(); 696 sass::string name(lexed); 697 SourceSpan nsource_position = pstate; 698 SelectorListObj negated = parseSelectorList(true); 699 if (!lex< exactly<')'> >()) { 700 error("negated selector is missing ')'"); 701 } 702 name.erase(name.size() - 1); 703 704 PseudoSelector* sel = SASS_MEMORY_NEW(PseudoSelector, nsource_position, name.substr(1)); 705 sel->selector(negated); 706 return sel; 707 } 708 709 // Helper to clean binominal string BothAreSpaces(char lhs,char rhs)710 bool BothAreSpaces(char lhs, char rhs) { return isspace(lhs) && isspace(rhs); } 711 712 // a pseudo selector often starts with one or two colons 713 // it can contain more selectors inside parentheses parse_pseudo_selector()714 SimpleSelectorObj Parser::parse_pseudo_selector() { 715 716 // Lex one or two colon characters 717 if (lex<pseudo_prefix>()) { 718 sass::string colons(lexed); 719 // Check if it is a pseudo element 720 bool element = colons.size() == 2; 721 722 if (lex< sequence< 723 // we keep the space within the name, strange enough 724 // ToDo: refactor output to schedule the space for it 725 // or do we really want to keep the real white-space? 726 sequence< identifier, optional < block_comment >, exactly<'('> > 727 > >()) 728 { 729 730 sass::string name(lexed); 731 name.erase(name.size() - 1); 732 SourceSpan p = pstate; 733 734 // specially parse nth-child pseudo selectors 735 if (lex_css < sequence < binomial, word_boundary >>()) { 736 sass::string parsed(lexed); // always compacting binominals (as dart-sass) 737 parsed.erase(std::unique(parsed.begin(), parsed.end(), BothAreSpaces), parsed.end()); 738 String_Constant_Obj arg = SASS_MEMORY_NEW(String_Constant, pstate, parsed); 739 PseudoSelector* pseudo = SASS_MEMORY_NEW(PseudoSelector, p, name, element); 740 if (lex < sequence < css_whitespace, insensitive < of_kwd >>>(false)) { 741 pseudo->selector(parseSelectorList(true)); 742 } 743 pseudo->argument(arg); 744 if (lex_css< exactly<')'> >()) { 745 return pseudo; 746 } 747 } 748 else { 749 if (peek_css< exactly<')'>>() && Util::equalsLiteral("nth-", name.substr(0, 4))) { 750 css_error("Invalid CSS", " after ", ": expected An+B expression, was "); 751 } 752 753 sass::string unvendored = Util::unvendor(name); 754 755 if (unvendored == "not" || unvendored == "matches" || unvendored == "current" || unvendored == "any" || unvendored == "has" || unvendored == "host" || unvendored == "host-context" || unvendored == "slotted") { 756 if (SelectorListObj wrapped = parseSelectorList(true)) { 757 if (wrapped && lex_css< exactly<')'> >()) { 758 PseudoSelector* pseudo = SASS_MEMORY_NEW(PseudoSelector, p, name, element); 759 pseudo->selector(wrapped); 760 return pseudo; 761 } 762 } 763 } else { 764 String_Schema_Obj arg = parse_css_variable_value(); 765 PseudoSelector* pseudo = SASS_MEMORY_NEW(PseudoSelector, p, name, element); 766 pseudo->argument(arg); 767 768 if (lex_css< exactly<')'> >()) { 769 return pseudo; 770 } 771 } 772 } 773 774 } 775 // EO if pseudo selector 776 777 else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) { 778 return SASS_MEMORY_NEW(PseudoSelector, pstate, lexed, element); 779 } 780 else if (lex < pseudo_prefix >()) { 781 css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was "); 782 } 783 784 } 785 else { 786 lex < identifier >(); // needed for error message? 787 css_error("Invalid CSS", " after ", ": expected selector, was "); 788 } 789 790 791 css_error("Invalid CSS", " after ", ": expected \")\", was "); 792 793 // unreachable statement 794 return {}; 795 } 796 re_attr_sensitive_close(const char * src)797 const char* Parser::re_attr_sensitive_close(const char* src) 798 { 799 return alternatives < exactly<']'>, exactly<'/'> >(src); 800 } 801 re_attr_insensitive_close(const char * src)802 const char* Parser::re_attr_insensitive_close(const char* src) 803 { 804 return sequence < insensitive<'i'>, re_attr_sensitive_close >(src); 805 } 806 parse_attribute_selector()807 AttributeSelectorObj Parser::parse_attribute_selector() 808 { 809 SourceSpan p = pstate; 810 if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector"); 811 sass::string name(lexed); 812 if (lex_css< re_attr_sensitive_close >()) { 813 return SASS_MEMORY_NEW(AttributeSelector, p, name, "", String_Obj{}); 814 } 815 else if (lex_css< re_attr_insensitive_close >()) { 816 char modifier = lexed.begin[0]; 817 return SASS_MEMORY_NEW(AttributeSelector, p, name, "", String_Obj{}, modifier); 818 } 819 if (!lex_css< alternatives< exact_match, class_match, dash_match, 820 prefix_match, suffix_match, substring_match > >()) { 821 error("invalid operator in attribute selector for " + name); 822 } 823 sass::string matcher(lexed); 824 825 String_Obj value; 826 if (lex_css< identifier >()) { 827 value = SASS_MEMORY_NEW(String_Constant, p, lexed); 828 } 829 else if (lex_css< quoted_string >()) { 830 value = parse_interpolated_chunk(lexed, true); // needed! 831 } 832 else { 833 error("expected a string constant or identifier in attribute selector for " + name); 834 } 835 836 if (lex_css< re_attr_sensitive_close >()) { 837 return SASS_MEMORY_NEW(AttributeSelector, p, name, matcher, value, 0); 838 } 839 else if (lex_css< re_attr_insensitive_close >()) { 840 char modifier = lexed.begin[0]; 841 return SASS_MEMORY_NEW(AttributeSelector, p, name, matcher, value, modifier); 842 } 843 error("unterminated attribute selector for " + name); 844 return {}; // to satisfy compilers (error must not return) 845 } 846 847 /* parse block comment and add to block */ parse_block_comments(bool store)848 void Parser::parse_block_comments(bool store) 849 { 850 Block_Obj block = block_stack.back(); 851 852 while (lex< block_comment >()) { 853 bool is_important = lexed.begin[2] == '!'; 854 // flag on second param is to skip loosely over comments 855 String_Obj contents = parse_interpolated_chunk(lexed, true, false); 856 if (store) block->append(SASS_MEMORY_NEW(Comment, pstate, contents, is_important)); 857 } 858 } 859 parse_declaration()860 Declaration_Obj Parser::parse_declaration() { 861 String_Obj prop; 862 bool is_custom_property = false; 863 if (lex< sequence< optional< exactly<'*'> >, identifier_schema > >()) { 864 const sass::string property(lexed); 865 is_custom_property = property.compare(0, 2, "--") == 0; 866 prop = parse_identifier_schema(); 867 } 868 else if (lex< sequence< optional< exactly<'*'> >, identifier, zero_plus< block_comment > > >()) { 869 const sass::string property(lexed); 870 is_custom_property = property.compare(0, 2, "--") == 0; 871 prop = SASS_MEMORY_NEW(String_Constant, pstate, lexed); 872 } 873 else { 874 css_error("Invalid CSS", " after ", ": expected \"}\", was "); 875 } 876 bool is_indented = true; 877 const sass::string property(lexed); 878 if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + escape_string(property) + "\" must be followed by a ':'"); 879 if (!is_custom_property && match< sequence< optional_css_comments, exactly<';'> > >()) error("style declaration must contain a value"); 880 if (match< sequence< optional_css_comments, exactly<'{'> > >()) is_indented = false; // don't indent if value is empty 881 if (is_custom_property) { 882 return SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, parse_css_variable_value(), false, true); 883 } 884 lex < css_comments >(false); 885 if (peek_css< static_value >()) { 886 return SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, parse_static_value()/*, lex<kwd_important>()*/); 887 } 888 else { 889 ExpressionObj value; 890 Lookahead lookahead = lookahead_for_value(position); 891 if (lookahead.found) { 892 if (lookahead.has_interpolants) { 893 value = parse_value_schema(lookahead.found); 894 } else { 895 value = parse_list(DELAYED); 896 } 897 } 898 else { 899 value = parse_list(DELAYED); 900 if (List* list = Cast<List>(value)) { 901 if (!list->is_bracketed() && list->length() == 0 && !peek< exactly <'{'> >()) { 902 css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); 903 } 904 } 905 } 906 lex < css_comments >(false); 907 Declaration_Obj decl = SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, value/*, lex<kwd_important>()*/); 908 decl->is_indented(is_indented); 909 decl->update_pstate(pstate); 910 return decl; 911 } 912 } 913 parse_map()914 ExpressionObj Parser::parse_map() 915 { 916 NESTING_GUARD(nestings); 917 ExpressionObj key = parse_list(); 918 List_Obj map = SASS_MEMORY_NEW(List, pstate, 0, SASS_HASH); 919 920 // it's not a map so return the lexed value as a list value 921 if (!lex_css< exactly<':'> >()) 922 { return key; } 923 924 List_Obj l = Cast<List>(key); 925 if (l && l->separator() == SASS_COMMA) { 926 css_error("Invalid CSS", " after ", ": expected \")\", was "); 927 } 928 929 ExpressionObj value = parse_space_list(); 930 931 map->append(key); 932 map->append(value); 933 934 while (lex_css< exactly<','> >()) 935 { 936 // allow trailing commas - #495 937 if (peek_css< exactly<')'> >(position)) 938 { break; } 939 940 key = parse_space_list(); 941 942 if (!(lex< exactly<':'> >())) 943 { css_error("Invalid CSS", " after ", ": expected \":\", was "); } 944 945 value = parse_space_list(); 946 947 map->append(key); 948 map->append(value); 949 } 950 951 SourceSpan ps = map->pstate(); 952 ps.offset = pstate.position - ps.position + pstate.offset; 953 map->pstate(ps); 954 955 return map; 956 } 957 parse_bracket_list()958 ExpressionObj Parser::parse_bracket_list() 959 { 960 NESTING_GUARD(nestings); 961 // check if we have an empty list 962 // return the empty list as such 963 if (peek_css< list_terminator >(position)) 964 { 965 // return an empty list (nothing to delay) 966 return SASS_MEMORY_NEW(List, pstate, 0, SASS_SPACE, false, true); 967 } 968 969 bool has_paren = peek_css< exactly<'('> >() != NULL; 970 971 // now try to parse a space list 972 ExpressionObj list = parse_space_list(); 973 // if it's a singleton, return it (don't wrap it) 974 if (!peek_css< exactly<','> >(position)) { 975 List_Obj l = Cast<List>(list); 976 if (!l || l->is_bracketed() || has_paren) { 977 List_Obj bracketed_list = SASS_MEMORY_NEW(List, pstate, 1, SASS_SPACE, false, true); 978 bracketed_list->append(list); 979 return bracketed_list; 980 } 981 l->is_bracketed(true); 982 return l; 983 } 984 985 // if we got so far, we actually do have a comma list 986 List_Obj bracketed_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_COMMA, false, true); 987 // wrap the first expression 988 bracketed_list->append(list); 989 990 while (lex_css< exactly<','> >()) 991 { 992 // check for abort condition 993 if (peek_css< list_terminator >(position) 994 ) { break; } 995 // otherwise add another expression 996 bracketed_list->append(parse_space_list()); 997 } 998 // return the list 999 return bracketed_list; 1000 } 1001 1002 // parse list returns either a space separated list, 1003 // a comma separated list or any bare expression found. 1004 // so to speak: we unwrap items from lists if possible here! parse_list(bool delayed)1005 ExpressionObj Parser::parse_list(bool delayed) 1006 { 1007 NESTING_GUARD(nestings); 1008 return parse_comma_list(delayed); 1009 } 1010 1011 // will return singletons unwrapped parse_comma_list(bool delayed)1012 ExpressionObj Parser::parse_comma_list(bool delayed) 1013 { 1014 NESTING_GUARD(nestings); 1015 // check if we have an empty list 1016 // return the empty list as such 1017 if (peek_css< list_terminator >(position)) 1018 { 1019 // return an empty list (nothing to delay) 1020 return SASS_MEMORY_NEW(List, pstate, 0); 1021 } 1022 1023 // now try to parse a space list 1024 ExpressionObj list = parse_space_list(); 1025 // if it's a singleton, return it (don't wrap it) 1026 if (!peek_css< exactly<','> >(position)) { 1027 // set_delay doesn't apply to list children 1028 // so this will only undelay single values 1029 if (!delayed) list->set_delayed(false); 1030 return list; 1031 } 1032 1033 // if we got so far, we actually do have a comma list 1034 List_Obj comma_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_COMMA); 1035 // wrap the first expression 1036 comma_list->append(list); 1037 1038 while (lex_css< exactly<','> >()) 1039 { 1040 // check for abort condition 1041 if (peek_css< list_terminator >(position) 1042 ) { break; } 1043 // otherwise add another expression 1044 comma_list->append(parse_space_list()); 1045 } 1046 // return the list 1047 return comma_list; 1048 } 1049 // EO parse_comma_list 1050 1051 // will return singletons unwrapped parse_space_list()1052 ExpressionObj Parser::parse_space_list() 1053 { 1054 NESTING_GUARD(nestings); 1055 ExpressionObj disj1 = parse_disjunction(); 1056 // if it's a singleton, return it (don't wrap it) 1057 if (peek_css< space_list_terminator >(position) 1058 ) { 1059 return disj1; } 1060 1061 List_Obj space_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_SPACE); 1062 space_list->append(disj1); 1063 1064 while ( 1065 !(peek_css< space_list_terminator >(position)) && 1066 peek_css< optional_css_whitespace >() != end 1067 ) { 1068 // the space is parsed implicitly? 1069 space_list->append(parse_disjunction()); 1070 } 1071 // return the list 1072 return space_list; 1073 } 1074 // EO parse_space_list 1075 1076 // parse logical OR operation parse_disjunction()1077 ExpressionObj Parser::parse_disjunction() 1078 { 1079 NESTING_GUARD(nestings); 1080 advanceToNextToken(); 1081 SourceSpan state(pstate); 1082 // parse the left hand side conjunction 1083 ExpressionObj conj = parse_conjunction(); 1084 // parse multiple right hand sides 1085 sass::vector<ExpressionObj> operands; 1086 while (lex_css< kwd_or >()) 1087 operands.push_back(parse_conjunction()); 1088 // if it's a singleton, return it directly 1089 if (operands.size() == 0) return conj; 1090 // fold all operands into one binary expression 1091 ExpressionObj ex = fold_operands(conj, operands, { Sass_OP::OR }); 1092 state.offset = pstate.position - state.position + pstate.offset; 1093 ex->pstate(state); 1094 return ex; 1095 } 1096 // EO parse_disjunction 1097 1098 // parse logical AND operation parse_conjunction()1099 ExpressionObj Parser::parse_conjunction() 1100 { 1101 NESTING_GUARD(nestings); 1102 advanceToNextToken(); 1103 SourceSpan state(pstate); 1104 // parse the left hand side relation 1105 ExpressionObj rel = parse_relation(); 1106 // parse multiple right hand sides 1107 sass::vector<ExpressionObj> operands; 1108 while (lex_css< kwd_and >()) { 1109 operands.push_back(parse_relation()); 1110 } 1111 // if it's a singleton, return it directly 1112 if (operands.size() == 0) return rel; 1113 // fold all operands into one binary expression 1114 ExpressionObj ex = fold_operands(rel, operands, { Sass_OP::AND }); 1115 state.offset = pstate.position - state.position + pstate.offset; 1116 ex->pstate(state); 1117 return ex; 1118 } 1119 // EO parse_conjunction 1120 1121 // parse comparison operations parse_relation()1122 ExpressionObj Parser::parse_relation() 1123 { 1124 NESTING_GUARD(nestings); 1125 advanceToNextToken(); 1126 SourceSpan state(pstate); 1127 // parse the left hand side expression 1128 ExpressionObj lhs = parse_expression(); 1129 sass::vector<ExpressionObj> operands; 1130 sass::vector<Operand> operators; 1131 // if it's a singleton, return it (don't wrap it) 1132 while (peek< alternatives < 1133 kwd_eq, 1134 kwd_neq, 1135 kwd_gte, 1136 kwd_gt, 1137 kwd_lte, 1138 kwd_lt 1139 > >(position)) 1140 { 1141 // is directly adjancent to expression? 1142 bool left_ws = peek < css_comments >() != NULL; 1143 // parse the operator 1144 enum Sass_OP op 1145 = lex<kwd_eq>() ? Sass_OP::EQ 1146 : lex<kwd_neq>() ? Sass_OP::NEQ 1147 : lex<kwd_gte>() ? Sass_OP::GTE 1148 : lex<kwd_lte>() ? Sass_OP::LTE 1149 : lex<kwd_gt>() ? Sass_OP::GT 1150 : lex<kwd_lt>() ? Sass_OP::LT 1151 // we checked the possibilities on top of fn 1152 : Sass_OP::EQ; 1153 // is directly adjacent to expression? 1154 bool right_ws = peek < css_comments >() != NULL; 1155 operators.push_back({ op, left_ws, right_ws }); 1156 operands.push_back(parse_expression()); 1157 } 1158 // we are called recursively for list, so we first 1159 // fold inner binary expression which has delayed 1160 // correctly set to zero. After folding we also unwrap 1161 // single nested items. So we cannot set delay on the 1162 // returned result here, as we have lost nestings ... 1163 ExpressionObj ex = fold_operands(lhs, operands, operators); 1164 state.offset = pstate.position - state.position + pstate.offset; 1165 ex->pstate(state); 1166 return ex; 1167 } 1168 // parse_relation 1169 1170 // parse expression valid for operations 1171 // called from parse_relation 1172 // called from parse_for_directive 1173 // called from parse_media_expression 1174 // parse addition and subtraction operations parse_expression()1175 ExpressionObj Parser::parse_expression() 1176 { 1177 NESTING_GUARD(nestings); 1178 advanceToNextToken(); 1179 SourceSpan state(pstate); 1180 // parses multiple add and subtract operations 1181 // NOTE: make sure that identifiers starting with 1182 // NOTE: dashes do NOT count as subtract operation 1183 ExpressionObj lhs = parse_operators(); 1184 // if it's a singleton, return it (don't wrap it) 1185 if (!(peek_css< exactly<'+'> >(position) || 1186 // condition is a bit mysterious, but some combinations should not be counted as operations 1187 (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) || 1188 (peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) || 1189 peek< sequence < zero_plus < exactly <'-' > >, identifier > >(position)) 1190 { return lhs; } 1191 1192 sass::vector<ExpressionObj> operands; 1193 sass::vector<Operand> operators; 1194 bool left_ws = peek < css_comments >() != NULL; 1195 while ( 1196 lex_css< exactly<'+'> >() || 1197 1198 ( 1199 ! peek_css< sequence < zero_plus < exactly <'-' > >, identifier > >(position) 1200 && lex_css< sequence< negate< digit >, exactly<'-'> > >() 1201 ) 1202 1203 ) { 1204 1205 bool right_ws = peek < css_comments >() != NULL; 1206 operators.push_back({ lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB, left_ws, right_ws }); 1207 operands.push_back(parse_operators()); 1208 left_ws = peek < css_comments >() != NULL; 1209 } 1210 1211 if (operands.size() == 0) return lhs; 1212 ExpressionObj ex = fold_operands(lhs, operands, operators); 1213 state.offset = pstate.position - state.position + pstate.offset; 1214 ex->pstate(state); 1215 return ex; 1216 } 1217 1218 // parse addition and subtraction operations parse_operators()1219 ExpressionObj Parser::parse_operators() 1220 { 1221 NESTING_GUARD(nestings); 1222 advanceToNextToken(); 1223 SourceSpan state(pstate); 1224 ExpressionObj factor = parse_factor(); 1225 // if it's a singleton, return it (don't wrap it) 1226 sass::vector<ExpressionObj> operands; // factors 1227 sass::vector<Operand> operators; // ops 1228 // lex operations to apply to lhs 1229 const char* left_ws = peek < css_comments >(); 1230 while (lex_css< class_char< static_ops > >()) { 1231 const char* right_ws = peek < css_comments >(); 1232 switch(*lexed.begin) { 1233 case '*': operators.push_back({ Sass_OP::MUL, left_ws != 0, right_ws != 0 }); break; 1234 case '/': operators.push_back({ Sass_OP::DIV, left_ws != 0, right_ws != 0 }); break; 1235 case '%': operators.push_back({ Sass_OP::MOD, left_ws != 0, right_ws != 0 }); break; 1236 default: throw std::runtime_error("unknown static op parsed"); 1237 } 1238 operands.push_back(parse_factor()); 1239 left_ws = peek < css_comments >(); 1240 } 1241 // operands and operators to binary expression 1242 ExpressionObj ex = fold_operands(factor, operands, operators); 1243 state.offset = pstate.position - state.position + pstate.offset; 1244 ex->pstate(state); 1245 return ex; 1246 } 1247 // EO parse_operators 1248 1249 1250 // called from parse_operators 1251 // called from parse_value_schema parse_factor()1252 ExpressionObj Parser::parse_factor() 1253 { 1254 NESTING_GUARD(nestings); 1255 lex < css_comments >(false); 1256 if (lex_css< exactly<'('> >()) { 1257 // parse_map may return a list 1258 ExpressionObj value = parse_map(); 1259 // lex the expected closing parenthesis 1260 if (!lex_css< exactly<')'> >()) error("unclosed parenthesis"); 1261 // expression can be evaluated 1262 return value; 1263 } 1264 else if (lex_css< exactly<'['> >()) { 1265 // explicit bracketed 1266 ExpressionObj value = parse_bracket_list(); 1267 // lex the expected closing square bracket 1268 if (!lex_css< exactly<']'> >()) error("unclosed squared bracket"); 1269 return value; 1270 } 1271 // string may be interpolated 1272 // if (lex< quoted_string >()) { 1273 // return &parse_string(); 1274 // } 1275 else if (peek< ie_property >()) { 1276 return parse_ie_property(); 1277 } 1278 else if (peek< ie_keyword_arg >()) { 1279 return parse_ie_keyword_arg(); 1280 } 1281 else if (peek< sequence < calc_fn_call, exactly <'('> > >()) { 1282 return parse_calc_function(); 1283 } 1284 else if (lex < functional_schema >()) { 1285 return parse_function_call_schema(); 1286 } 1287 else if (lex< identifier_schema >()) { 1288 String_Obj string = parse_identifier_schema(); 1289 if (String_Schema* schema = Cast<String_Schema>(string)) { 1290 if (lex < exactly < '(' > >()) { 1291 schema->append(parse_list()); 1292 lex < exactly < ')' > >(); 1293 } 1294 } 1295 return string; 1296 } 1297 else if (peek< sequence< uri_prefix, W, real_uri_value > >()) { 1298 return parse_url_function_string(); 1299 } 1300 else if (peek< re_functional >()) { 1301 return parse_function_call(); 1302 } 1303 else if (lex< exactly<'+'> >()) { 1304 Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::PLUS, parse_factor()); 1305 if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed()); 1306 return ex; 1307 } 1308 else if (lex< exactly<'-'> >()) { 1309 Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::MINUS, parse_factor()); 1310 if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed()); 1311 return ex; 1312 } 1313 else if (lex< exactly<'/'> >()) { 1314 Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::SLASH, parse_factor()); 1315 if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed()); 1316 return ex; 1317 } 1318 else if (lex< sequence< kwd_not > >()) { 1319 Unary_Expression* ex = SASS_MEMORY_NEW(Unary_Expression, pstate, Unary_Expression::NOT, parse_factor()); 1320 if (ex && ex->operand()) ex->is_delayed(ex->operand()->is_delayed()); 1321 return ex; 1322 } 1323 else { 1324 return parse_value(); 1325 } 1326 } 1327 number_has_zero(const sass::string & parsed)1328 bool number_has_zero(const sass::string& parsed) 1329 { 1330 size_t L = parsed.length(); 1331 return !( (L > 0 && parsed.substr(0, 1) == ".") || 1332 (L > 1 && parsed.substr(0, 2) == "0.") || 1333 (L > 1 && parsed.substr(0, 2) == "-.") || 1334 (L > 2 && parsed.substr(0, 3) == "-0.") ); 1335 } 1336 lexed_number(const SourceSpan & pstate,const sass::string & parsed)1337 Number* Parser::lexed_number(const SourceSpan& pstate, const sass::string& parsed) 1338 { 1339 Number* nr = SASS_MEMORY_NEW(Number, 1340 pstate, 1341 sass_strtod(parsed.c_str()), 1342 "", 1343 number_has_zero(parsed)); 1344 nr->is_interpolant(false); 1345 nr->is_delayed(true); 1346 return nr; 1347 } 1348 lexed_percentage(const SourceSpan & pstate,const sass::string & parsed)1349 Number* Parser::lexed_percentage(const SourceSpan& pstate, const sass::string& parsed) 1350 { 1351 Number* nr = SASS_MEMORY_NEW(Number, 1352 pstate, 1353 sass_strtod(parsed.c_str()), 1354 "%", 1355 true); 1356 nr->is_interpolant(false); 1357 nr->is_delayed(true); 1358 return nr; 1359 } 1360 lexed_dimension(const SourceSpan & pstate,const sass::string & parsed)1361 Number* Parser::lexed_dimension(const SourceSpan& pstate, const sass::string& parsed) 1362 { 1363 size_t L = parsed.length(); 1364 size_t num_pos = parsed.find_first_not_of(" \n\r\t"); 1365 if (num_pos == sass::string::npos) num_pos = L; 1366 size_t unit_pos = parsed.find_first_not_of("-+0123456789.", num_pos); 1367 if (parsed[unit_pos] == 'e' && is_number(parsed[unit_pos+1]) ) { 1368 unit_pos = parsed.find_first_not_of("-+0123456789.", ++ unit_pos); 1369 } 1370 if (unit_pos == sass::string::npos) unit_pos = L; 1371 const sass::string& num = parsed.substr(num_pos, unit_pos - num_pos); 1372 Number* nr = SASS_MEMORY_NEW(Number, 1373 pstate, 1374 sass_strtod(num.c_str()), 1375 Token(number(parsed.c_str())), 1376 number_has_zero(parsed)); 1377 nr->is_interpolant(false); 1378 nr->is_delayed(true); 1379 return nr; 1380 } 1381 lexed_hex_color(const SourceSpan & pstate,const sass::string & parsed)1382 Value* Parser::lexed_hex_color(const SourceSpan& pstate, const sass::string& parsed) 1383 { 1384 Color_RGBA* color = NULL; 1385 if (parsed[0] != '#') { 1386 return SASS_MEMORY_NEW(String_Quoted, pstate, parsed); 1387 } 1388 // chop off the '#' 1389 sass::string hext(parsed.substr(1)); 1390 if (parsed.length() == 4) { 1391 sass::string r(2, parsed[1]); 1392 sass::string g(2, parsed[2]); 1393 sass::string b(2, parsed[3]); 1394 color = SASS_MEMORY_NEW(Color_RGBA, 1395 pstate, 1396 static_cast<double>(strtol(r.c_str(), NULL, 16)), 1397 static_cast<double>(strtol(g.c_str(), NULL, 16)), 1398 static_cast<double>(strtol(b.c_str(), NULL, 16)), 1399 1, // alpha channel 1400 parsed); 1401 } 1402 else if (parsed.length() == 5) { 1403 sass::string r(2, parsed[1]); 1404 sass::string g(2, parsed[2]); 1405 sass::string b(2, parsed[3]); 1406 sass::string a(2, parsed[4]); 1407 color = SASS_MEMORY_NEW(Color_RGBA, 1408 pstate, 1409 static_cast<double>(strtol(r.c_str(), NULL, 16)), 1410 static_cast<double>(strtol(g.c_str(), NULL, 16)), 1411 static_cast<double>(strtol(b.c_str(), NULL, 16)), 1412 static_cast<double>(strtol(a.c_str(), NULL, 16)) / 255, 1413 parsed); 1414 } 1415 else if (parsed.length() == 7) { 1416 sass::string r(parsed.substr(1,2)); 1417 sass::string g(parsed.substr(3,2)); 1418 sass::string b(parsed.substr(5,2)); 1419 color = SASS_MEMORY_NEW(Color_RGBA, 1420 pstate, 1421 static_cast<double>(strtol(r.c_str(), NULL, 16)), 1422 static_cast<double>(strtol(g.c_str(), NULL, 16)), 1423 static_cast<double>(strtol(b.c_str(), NULL, 16)), 1424 1, // alpha channel 1425 parsed); 1426 } 1427 else if (parsed.length() == 9) { 1428 sass::string r(parsed.substr(1,2)); 1429 sass::string g(parsed.substr(3,2)); 1430 sass::string b(parsed.substr(5,2)); 1431 sass::string a(parsed.substr(7,2)); 1432 color = SASS_MEMORY_NEW(Color_RGBA, 1433 pstate, 1434 static_cast<double>(strtol(r.c_str(), NULL, 16)), 1435 static_cast<double>(strtol(g.c_str(), NULL, 16)), 1436 static_cast<double>(strtol(b.c_str(), NULL, 16)), 1437 static_cast<double>(strtol(a.c_str(), NULL, 16)) / 255, 1438 parsed); 1439 } 1440 color->is_interpolant(false); 1441 color->is_delayed(false); 1442 return color; 1443 } 1444 color_or_string(const sass::string & lexed) const1445 Value* Parser::color_or_string(const sass::string& lexed) const 1446 { 1447 if (auto color = name_to_color(lexed)) { 1448 auto c = SASS_MEMORY_NEW(Color_RGBA, color); 1449 c->is_delayed(true); 1450 c->pstate(pstate); 1451 c->disp(lexed); 1452 return c; 1453 } else { 1454 return SASS_MEMORY_NEW(String_Constant, pstate, lexed); 1455 } 1456 } 1457 1458 // parse one value for a list parse_value()1459 ExpressionObj Parser::parse_value() 1460 { 1461 lex< css_comments >(false); 1462 if (lex< ampersand >()) 1463 { 1464 if (match< ampersand >()) { 1465 warning("In Sass, \"&&\" means two copies of the parent selector. You probably want to use \"and\" instead.", pstate); 1466 } 1467 return SASS_MEMORY_NEW(Parent_Reference, pstate); } 1468 1469 if (lex< kwd_important >()) 1470 { return SASS_MEMORY_NEW(String_Constant, pstate, "!important"); } 1471 1472 // parse `10%4px` into separated items and not a schema 1473 if (lex< sequence < percentage, lookahead < number > > >()) 1474 { return lexed_percentage(lexed); } 1475 1476 if (lex< sequence < number, lookahead< sequence < op, number > > > >()) 1477 { return lexed_number(lexed); } 1478 1479 // string may be interpolated 1480 if (lex< sequence < quoted_string, lookahead < exactly <'-'> > > >()) 1481 { return parse_string(); } 1482 1483 if (const char* stop = peek< value_schema >()) 1484 { return parse_value_schema(stop); } 1485 1486 // string may be interpolated 1487 if (lex< quoted_string >()) 1488 { return parse_string(); } 1489 1490 if (lex< kwd_true >()) 1491 { return SASS_MEMORY_NEW(Boolean, pstate, true); } 1492 1493 if (lex< kwd_false >()) 1494 { return SASS_MEMORY_NEW(Boolean, pstate, false); } 1495 1496 if (lex< kwd_null >()) 1497 { return SASS_MEMORY_NEW(Null, pstate); } 1498 1499 if (lex< identifier >()) { 1500 return color_or_string(lexed); 1501 } 1502 1503 if (lex< percentage >()) 1504 { return lexed_percentage(lexed); } 1505 1506 // match hex number first because 0x000 looks like a number followed by an identifier 1507 if (lex< sequence < alternatives< hex, hex0 >, negate < exactly<'-'> > > >()) 1508 { return lexed_hex_color(lexed); } 1509 1510 if (lex< hexa >()) 1511 { return lexed_hex_color(lexed); } 1512 1513 if (lex< sequence < exactly <'#'>, identifier > >()) 1514 { return SASS_MEMORY_NEW(String_Quoted, pstate, lexed); } 1515 1516 // also handle the 10em- foo special case 1517 // alternatives < exactly < '.' >, .. > -- `1.5em-.75em` is split into a list, not a binary expression 1518 if (lex< sequence< dimension, optional< sequence< exactly<'-'>, lookahead< alternatives < space > > > > > >()) 1519 { return lexed_dimension(lexed); } 1520 1521 if (lex< sequence< static_component, one_plus< strict_identifier > > >()) 1522 { return SASS_MEMORY_NEW(String_Constant, pstate, lexed); } 1523 1524 if (lex< number >()) 1525 { return lexed_number(lexed); } 1526 1527 if (lex< variable >()) 1528 { return SASS_MEMORY_NEW(Variable, pstate, Util::normalize_underscores(lexed)); } 1529 1530 css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); 1531 1532 // unreachable statement 1533 return {}; 1534 } 1535 1536 // this parses interpolation inside other strings 1537 // means the result should later be quoted again parse_interpolated_chunk(Token chunk,bool constant,bool css)1538 String_Obj Parser::parse_interpolated_chunk(Token chunk, bool constant, bool css) 1539 { 1540 const char* i = chunk.begin; 1541 // see if there any interpolants 1542 const char* p = constant ? find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end) : 1543 find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, chunk.end); 1544 1545 if (!p) { 1546 String_Quoted* str_quoted = SASS_MEMORY_NEW(String_Quoted, pstate, sass::string(i, chunk.end), 0, false, false, true, css); 1547 if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*'); 1548 return str_quoted; 1549 } 1550 1551 String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate, 0, css); 1552 schema->is_interpolant(true); 1553 while (i < chunk.end) { 1554 p = constant ? find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end) : 1555 find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, chunk.end); 1556 if (p) { 1557 if (i < p) { 1558 // accumulate the preceding segment if it's nonempty 1559 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(i, p), css)); 1560 } 1561 // we need to skip anything inside strings 1562 // create a new target in parser/prelexer 1563 if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2; 1564 css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); 1565 } 1566 const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, chunk.end); // find the closing brace 1567 if (j) { --j; 1568 // parse the interpolant and accumulate it 1569 LocalOption<const char*> partEnd(end, j); 1570 LocalOption<const char*> partBeg(position, p + 2); 1571 ExpressionObj interp_node = parse_list(); 1572 interp_node->is_interpolant(true); 1573 schema->append(interp_node); 1574 i = j; 1575 } 1576 else { 1577 // throw an error if the interpolant is unterminated 1578 error("unterminated interpolant inside string constant " + chunk.to_string()); 1579 } 1580 } 1581 else { // no interpolants left; add the last segment if nonempty 1582 // check if we need quotes here (was not sure after merge) 1583 if (i < chunk.end) schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(i, chunk.end), css)); 1584 break; 1585 } 1586 ++ i; 1587 } 1588 1589 return schema.detach(); 1590 } 1591 parse_css_variable_value()1592 String_Schema_Obj Parser::parse_css_variable_value() 1593 { 1594 String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); 1595 sass::vector<char> brackets; 1596 while (true) { 1597 if ( 1598 (brackets.empty() && lex< css_variable_top_level_value >(false)) || 1599 (!brackets.empty() && lex< css_variable_value >(false)) 1600 ) { 1601 Token str(lexed); 1602 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, str)); 1603 } else if (ExpressionObj tok = lex_interpolation()) { 1604 if (String_Schema* s = Cast<String_Schema>(tok)) { 1605 if (s->empty()) break; 1606 schema->concat(s); 1607 } else { 1608 schema->append(tok); 1609 } 1610 } else if (lex< quoted_string >()) { 1611 ExpressionObj tok = parse_string(); 1612 if (tok.isNull()) break; 1613 if (String_Schema* s = Cast<String_Schema>(tok)) { 1614 if (s->empty()) break; 1615 schema->concat(s); 1616 } else { 1617 schema->append(tok); 1618 } 1619 } else if (lex< alternatives< exactly<'('>, exactly<'['>, exactly<'{'> > >()) { 1620 const char opening_bracket = *(position - 1); 1621 brackets.push_back(opening_bracket); 1622 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(1, opening_bracket))); 1623 } else if (const char *match = peek< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >()) { 1624 if (brackets.empty()) break; 1625 const char closing_bracket = *(match - 1); 1626 if (brackets.back() != Util::opening_bracket_for(closing_bracket)) { 1627 sass::string message = ": expected \""; 1628 message += Util::closing_bracket_for(brackets.back()); 1629 message += "\", was "; 1630 css_error("Invalid CSS", " after ", message); 1631 } 1632 lex< alternatives< exactly<')'>, exactly<']'>, exactly<'}'> > >(); 1633 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(1, closing_bracket))); 1634 brackets.pop_back(); 1635 } else { 1636 break; 1637 } 1638 } 1639 1640 if (!brackets.empty()) { 1641 sass::string message = ": expected \""; 1642 message += Util::closing_bracket_for(brackets.back()); 1643 message += "\", was "; 1644 css_error("Invalid CSS", " after ", message); 1645 } 1646 1647 if (schema->empty()) error("Custom property values may not be empty."); 1648 return schema.detach(); 1649 } 1650 parse_static_value()1651 ValueObj Parser::parse_static_value() 1652 { 1653 lex< static_value >(); 1654 Token str(lexed); 1655 // static values always have trailing white- 1656 // space and end delimiter (\s*[;]$) included 1657 --pstate.offset.column; 1658 --after_token.column; 1659 --str.end; 1660 --position; 1661 1662 return color_or_string(str.time_wspace());; 1663 } 1664 parse_string()1665 String_Obj Parser::parse_string() 1666 { 1667 return parse_interpolated_chunk(Token(lexed)); 1668 } 1669 parse_ie_property()1670 String_Obj Parser::parse_ie_property() 1671 { 1672 lex< ie_property >(); 1673 Token str(lexed); 1674 const char* i = str.begin; 1675 // see if there any interpolants 1676 const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(str.begin, str.end); 1677 if (!p) { 1678 return SASS_MEMORY_NEW(String_Quoted, pstate, sass::string(str.begin, str.end)); 1679 } 1680 1681 String_Schema* schema = SASS_MEMORY_NEW(String_Schema, pstate); 1682 while (i < str.end) { 1683 p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, str.end); 1684 if (p) { 1685 if (i < p) { 1686 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(i, p))); // accumulate the preceding segment if it's nonempty 1687 } 1688 if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2; 1689 css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); 1690 } 1691 const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, str.end); // find the closing brace 1692 if (j) { 1693 // parse the interpolant and accumulate it 1694 LocalOption<const char*> partEnd(end, j); 1695 LocalOption<const char*> partBeg(position, p + 2); 1696 ExpressionObj interp_node = parse_list(); 1697 interp_node->is_interpolant(true); 1698 schema->append(interp_node); 1699 i = j; 1700 } 1701 else { 1702 // throw an error if the interpolant is unterminated 1703 error("unterminated interpolant inside IE function " + str.to_string()); 1704 } 1705 } 1706 else { // no interpolants left; add the last segment if nonempty 1707 if (i < str.end) { 1708 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(i, str.end))); 1709 } 1710 break; 1711 } 1712 } 1713 return schema; 1714 } 1715 parse_ie_keyword_arg()1716 String_Obj Parser::parse_ie_keyword_arg() 1717 { 1718 String_Schema_Obj kwd_arg = SASS_MEMORY_NEW(String_Schema, pstate, 3); 1719 if (lex< variable >()) { 1720 kwd_arg->append(SASS_MEMORY_NEW(Variable, pstate, Util::normalize_underscores(lexed))); 1721 } else { 1722 lex< alternatives< identifier_schema, identifier > >(); 1723 kwd_arg->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); 1724 } 1725 lex< exactly<'='> >(); 1726 kwd_arg->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); 1727 if (peek< variable >()) kwd_arg->append(parse_list()); 1728 else if (lex< number >()) { 1729 sass::string parsed(lexed); 1730 Util::normalize_decimals(parsed); 1731 kwd_arg->append(lexed_number(parsed)); 1732 } 1733 else if (peek < ie_keyword_arg_value >()) { kwd_arg->append(parse_list()); } 1734 return kwd_arg; 1735 } 1736 parse_value_schema(const char * stop)1737 String_Schema_Obj Parser::parse_value_schema(const char* stop) 1738 { 1739 // initialize the string schema object to add tokens 1740 String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); 1741 1742 if (peek<exactly<'}'>>()) { 1743 css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); 1744 } 1745 1746 const char* e; 1747 const char* ee = end; 1748 end = stop; 1749 size_t num_items = 0; 1750 bool need_space = false; 1751 while (position < stop) { 1752 // parse space between tokens 1753 if (lex< spaces >() && num_items) { 1754 need_space = true; 1755 } 1756 if (need_space) { 1757 need_space = false; 1758 // schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " ")); 1759 } 1760 if ((e = peek< re_functional >()) && e < stop) { 1761 schema->append(parse_function_call()); 1762 } 1763 // lex an interpolant /#{...}/ 1764 else if (lex< exactly < hash_lbrace > >()) { 1765 // Try to lex static expression first 1766 if (peek< exactly< rbrace > >()) { 1767 css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); 1768 } 1769 ExpressionObj ex; 1770 if (lex< re_static_expression >()) { 1771 ex = SASS_MEMORY_NEW(String_Constant, pstate, lexed); 1772 } else { 1773 ex = parse_list(true); 1774 } 1775 ex->is_interpolant(true); 1776 schema->append(ex); 1777 if (!lex < exactly < rbrace > >()) { 1778 css_error("Invalid CSS", " after ", ": expected \"}\", was "); 1779 } 1780 } 1781 // lex some string constants or other valid token 1782 // Note: [-+] chars are left over from i.e. `#{3}+3` 1783 else if (lex< alternatives < exactly<'%'>, exactly < '-' >, exactly < '+' > > >()) { 1784 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); 1785 } 1786 // lex a quoted string 1787 else if (lex< quoted_string >()) { 1788 // need_space = true; 1789 // if (schema->length()) schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " ")); 1790 // else need_space = true; 1791 schema->append(parse_string()); 1792 if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) { 1793 // need_space = true; 1794 } 1795 if (peek < exactly < '-' > >()) break; 1796 } 1797 else if (lex< identifier >()) { 1798 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, lexed)); 1799 if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) { 1800 // need_space = true; 1801 } 1802 } 1803 // lex (normalized) variable 1804 else if (lex< variable >()) { 1805 sass::string name(Util::normalize_underscores(lexed)); 1806 schema->append(SASS_MEMORY_NEW(Variable, pstate, name)); 1807 } 1808 // lex percentage value 1809 else if (lex< percentage >()) { 1810 schema->append(lexed_percentage(lexed)); 1811 } 1812 // lex dimension value 1813 else if (lex< dimension >()) { 1814 schema->append(lexed_dimension(lexed)); 1815 } 1816 // lex number value 1817 else if (lex< number >()) { 1818 schema->append(lexed_number(lexed)); 1819 } 1820 // lex hex color value 1821 else if (lex< sequence < hex, negate < exactly < '-' > > > >()) { 1822 schema->append(lexed_hex_color(lexed)); 1823 } 1824 else if (lex< sequence < exactly <'#'>, identifier > >()) { 1825 schema->append(SASS_MEMORY_NEW(String_Quoted, pstate, lexed)); 1826 } 1827 // lex a value in parentheses 1828 else if (peek< parenthese_scope >()) { 1829 schema->append(parse_factor()); 1830 } 1831 else { 1832 break; 1833 } 1834 ++num_items; 1835 } 1836 if (position != stop) { 1837 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, sass::string(position, stop))); 1838 position = stop; 1839 } 1840 end = ee; 1841 return schema; 1842 } 1843 1844 // this parses interpolation outside other strings 1845 // means the result must not be quoted again later parse_identifier_schema()1846 String_Obj Parser::parse_identifier_schema() 1847 { 1848 Token id(lexed); 1849 const char* i = id.begin; 1850 // see if there any interpolants 1851 const char* p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(id.begin, id.end); 1852 if (!p) { 1853 return SASS_MEMORY_NEW(String_Constant, pstate, sass::string(id.begin, id.end)); 1854 } 1855 1856 String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); 1857 while (i < id.end) { 1858 p = find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, id.end); 1859 if (p) { 1860 if (i < p) { 1861 // accumulate the preceding segment if it's nonempty 1862 const char* o = position; position = i; 1863 schema->append(parse_value_schema(p)); 1864 position = o; 1865 } 1866 // we need to skip anything inside strings 1867 // create a new target in parser/prelexer 1868 if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p; 1869 css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); 1870 } 1871 const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, id.end); // find the closing brace 1872 if (j) { 1873 // parse the interpolant and accumulate it 1874 LocalOption<const char*> partEnd(end, j); 1875 LocalOption<const char*> partBeg(position, p + 2); 1876 ExpressionObj interp_node = parse_list(DELAYED); 1877 interp_node->is_interpolant(true); 1878 schema->append(interp_node); 1879 // schema->has_interpolants(true); 1880 i = j; 1881 } 1882 else { 1883 // throw an error if the interpolant is unterminated 1884 error("unterminated interpolant inside interpolated identifier " + id.to_string()); 1885 } 1886 } 1887 else { // no interpolants left; add the last segment if nonempty 1888 if (i < end) { 1889 const char* o = position; position = i; 1890 schema->append(parse_value_schema(id.end)); 1891 position = o; 1892 } 1893 break; 1894 } 1895 } 1896 return schema ? schema.detach() : 0; 1897 } 1898 1899 // calc functions should preserve arguments parse_calc_function()1900 Function_Call_Obj Parser::parse_calc_function() 1901 { 1902 lex< identifier >(); 1903 sass::string name(lexed); 1904 SourceSpan call_pos = pstate; 1905 lex< exactly<'('> >(); 1906 SourceSpan arg_pos = pstate; 1907 const char* arg_beg = position; 1908 parse_list(); 1909 const char* arg_end = position; 1910 lex< skip_over_scopes < 1911 exactly < '(' >, 1912 exactly < ')' > 1913 > >(); 1914 1915 Argument_Obj arg = SASS_MEMORY_NEW(Argument, arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end))); 1916 Arguments_Obj args = SASS_MEMORY_NEW(Arguments, arg_pos); 1917 args->append(arg); 1918 return SASS_MEMORY_NEW(Function_Call, call_pos, name, args); 1919 } 1920 parse_url_function_string()1921 String_Obj Parser::parse_url_function_string() 1922 { 1923 sass::string prefix(""); 1924 if (lex< uri_prefix >()) { 1925 prefix = sass::string(lexed); 1926 } 1927 1928 lex < optional_spaces >(); 1929 String_Obj url_string = parse_url_function_argument(); 1930 1931 sass::string suffix(""); 1932 if (lex< real_uri_suffix >()) { 1933 suffix = sass::string(lexed); 1934 } 1935 1936 sass::string uri(""); 1937 if (url_string) { 1938 uri = url_string->to_string({ NESTED, 5 }); 1939 } 1940 1941 if (String_Schema* schema = Cast<String_Schema>(url_string)) { 1942 String_Schema_Obj res = SASS_MEMORY_NEW(String_Schema, pstate); 1943 res->append(SASS_MEMORY_NEW(String_Constant, pstate, prefix)); 1944 res->append(schema); 1945 res->append(SASS_MEMORY_NEW(String_Constant, pstate, suffix)); 1946 return res; 1947 } else { 1948 sass::string res = prefix + uri + suffix; 1949 return SASS_MEMORY_NEW(String_Constant, pstate, res); 1950 } 1951 } 1952 parse_url_function_argument()1953 String_Obj Parser::parse_url_function_argument() 1954 { 1955 const char* p = position; 1956 1957 sass::string uri(""); 1958 if (lex< real_uri_value >(false)) { 1959 uri = lexed.to_string(); 1960 } 1961 1962 if (peek< exactly< hash_lbrace > >()) { 1963 const char* pp = position; 1964 // TODO: error checking for unclosed interpolants 1965 while (pp && peek< exactly< hash_lbrace > >(pp)) { 1966 pp = sequence< interpolant, real_uri_value >(pp); 1967 } 1968 if (!pp) return {}; 1969 position = pp; 1970 return parse_interpolated_chunk(Token(p, position)); 1971 } 1972 else if (uri != "") { 1973 sass::string res = Util::rtrim(uri); 1974 return SASS_MEMORY_NEW(String_Constant, pstate, res); 1975 } 1976 1977 return {}; 1978 } 1979 parse_function_call()1980 Function_Call_Obj Parser::parse_function_call() 1981 { 1982 lex< identifier >(); 1983 sass::string name(lexed); 1984 1985 if (Util::normalize_underscores(name) == "content-exists" && stack.back() != Scope::Mixin) 1986 { error("Cannot call content-exists() except within a mixin."); } 1987 1988 SourceSpan call_pos = pstate; 1989 Arguments_Obj args = parse_arguments(); 1990 return SASS_MEMORY_NEW(Function_Call, call_pos, name, args); 1991 } 1992 parse_function_call_schema()1993 Function_Call_Obj Parser::parse_function_call_schema() 1994 { 1995 String_Obj name = parse_identifier_schema(); 1996 SourceSpan source_position_of_call = pstate; 1997 Arguments_Obj args = parse_arguments(); 1998 1999 return SASS_MEMORY_NEW(Function_Call, source_position_of_call, name, args); 2000 } 2001 parse_content_directive()2002 Content_Obj Parser::parse_content_directive() 2003 { 2004 SourceSpan call_pos = pstate; 2005 Arguments_Obj args = parse_arguments(); 2006 2007 return SASS_MEMORY_NEW(Content, call_pos, args); 2008 } 2009 parse_if_directive(bool else_if)2010 If_Obj Parser::parse_if_directive(bool else_if) 2011 { 2012 stack.push_back(Scope::Control); 2013 SourceSpan if_source_position = pstate; 2014 bool root = block_stack.back()->is_root(); 2015 ExpressionObj predicate = parse_list(); 2016 Block_Obj block = parse_block(root); 2017 Block_Obj alternative; 2018 2019 // only throw away comment if we parse a case 2020 // we want all other comments to be parsed 2021 if (lex_css< elseif_directive >()) { 2022 alternative = SASS_MEMORY_NEW(Block, pstate); 2023 alternative->append(parse_if_directive(true)); 2024 } 2025 else if (lex_css< kwd_else_directive >()) { 2026 alternative = parse_block(root); 2027 } 2028 stack.pop_back(); 2029 return SASS_MEMORY_NEW(If, if_source_position, predicate, block, alternative); 2030 } 2031 parse_for_directive()2032 ForRuleObj Parser::parse_for_directive() 2033 { 2034 stack.push_back(Scope::Control); 2035 SourceSpan for_source_position = pstate; 2036 bool root = block_stack.back()->is_root(); 2037 lex_variable(); 2038 sass::string var(Util::normalize_underscores(lexed)); 2039 if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive"); 2040 ExpressionObj lower_bound = parse_expression(); 2041 bool inclusive = false; 2042 if (lex< kwd_through >()) inclusive = true; 2043 else if (lex< kwd_to >()) inclusive = false; 2044 else error("expected 'through' or 'to' keyword in @for directive"); 2045 ExpressionObj upper_bound = parse_expression(); 2046 Block_Obj body = parse_block(root); 2047 stack.pop_back(); 2048 return SASS_MEMORY_NEW(ForRule, for_source_position, var, lower_bound, upper_bound, body, inclusive); 2049 } 2050 2051 // helper to parse a var token lex_variable()2052 Token Parser::lex_variable() 2053 { 2054 // peek for dollar sign first 2055 if (!peek< exactly <'$'> >()) { 2056 css_error("Invalid CSS", " after ", ": expected \"$\", was "); 2057 } 2058 // we expect a simple identifier as the call name 2059 if (!lex< sequence < exactly <'$'>, identifier > >()) { 2060 lex< exactly <'$'> >(); // move pstate and position up 2061 css_error("Invalid CSS", " after ", ": expected identifier, was "); 2062 } 2063 // return object 2064 return lexed; 2065 } 2066 // helper to parse identifier lex_identifier()2067 Token Parser::lex_identifier() 2068 { 2069 // we expect a simple identifier as the call name 2070 if (!lex< identifier >()) { // ToDo: pstate wrong? 2071 css_error("Invalid CSS", " after ", ": expected identifier, was "); 2072 } 2073 // return object 2074 return lexed; 2075 } 2076 parse_each_directive()2077 EachRuleObj Parser::parse_each_directive() 2078 { 2079 stack.push_back(Scope::Control); 2080 SourceSpan each_source_position = pstate; 2081 bool root = block_stack.back()->is_root(); 2082 sass::vector<sass::string> vars; 2083 lex_variable(); 2084 vars.push_back(Util::normalize_underscores(lexed)); 2085 while (lex< exactly<','> >()) { 2086 if (!lex< variable >()) error("@each directive requires an iteration variable"); 2087 vars.push_back(Util::normalize_underscores(lexed)); 2088 } 2089 if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive"); 2090 ExpressionObj list = parse_list(); 2091 Block_Obj body = parse_block(root); 2092 stack.pop_back(); 2093 return SASS_MEMORY_NEW(EachRule, each_source_position, vars, list, body); 2094 } 2095 2096 // called after parsing `kwd_while_directive` parse_while_directive()2097 WhileRuleObj Parser::parse_while_directive() 2098 { 2099 stack.push_back(Scope::Control); 2100 bool root = block_stack.back()->is_root(); 2101 // create the initial while call object 2102 WhileRuleObj call = SASS_MEMORY_NEW(WhileRule, pstate, ExpressionObj{}, Block_Obj{}); 2103 // parse mandatory predicate 2104 ExpressionObj predicate = parse_list(); 2105 List_Obj l = Cast<List>(predicate); 2106 if (!predicate || (l && !l->length())) { 2107 css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ", false); 2108 } 2109 call->predicate(predicate); 2110 // parse mandatory block 2111 call->block(parse_block(root)); 2112 // return ast node 2113 stack.pop_back(); 2114 // return ast node 2115 return call.detach(); 2116 } 2117 2118 parseCssMediaQueries()2119 sass::vector<CssMediaQuery_Obj> Parser::parseCssMediaQueries() 2120 { 2121 sass::vector<CssMediaQuery_Obj> result; 2122 do { 2123 if (auto query = parseCssMediaQuery()) { 2124 result.push_back(query); 2125 } 2126 } while (lex<exactly<','>>()); 2127 return result; 2128 } 2129 parseIdentifier()2130 sass::string Parser::parseIdentifier() 2131 { 2132 if (lex < identifier >(false)) { 2133 return sass::string(lexed); 2134 } 2135 return sass::string(); 2136 } 2137 parseCssMediaQuery()2138 CssMediaQuery_Obj Parser::parseCssMediaQuery() 2139 { 2140 CssMediaQuery_Obj result = SASS_MEMORY_NEW(CssMediaQuery, pstate); 2141 lex<css_comments>(false); 2142 2143 // Check if any tokens are to parse 2144 if (!peek_css<exactly<'('>>()) { 2145 2146 sass::string token1(parseIdentifier()); 2147 lex<css_comments>(false); 2148 2149 if (token1.empty()) { 2150 return {}; 2151 } 2152 2153 sass::string token2(parseIdentifier()); 2154 lex<css_comments>(false); 2155 2156 if (Util::equalsLiteral("and", token2)) { 2157 result->type(token1); 2158 } 2159 else { 2160 if (token2.empty()) { 2161 result->type(token1); 2162 } 2163 else { 2164 result->modifier(token1); 2165 result->type(token2); 2166 } 2167 2168 if (lex < kwd_and >()) { 2169 lex<css_comments>(false); 2170 } 2171 else { 2172 return result; 2173 } 2174 2175 } 2176 2177 } 2178 2179 sass::vector<sass::string> queries; 2180 2181 do { 2182 lex<css_comments>(false); 2183 2184 if (lex<exactly<'('>>()) { 2185 // In dart sass parser returns a pure string 2186 if (lex < skip_over_scopes < exactly < '(' >, exactly < ')' > > >()) { 2187 sass::string decl("(" + sass::string(lexed)); 2188 queries.push_back(decl); 2189 } 2190 // Should be: parseDeclarationValue; 2191 if (!lex<exactly<')'>>()) { 2192 // Should we throw an error here? 2193 } 2194 } 2195 } while (lex < kwd_and >()); 2196 2197 result->features(queries); 2198 2199 if (result->features().empty()) { 2200 if (result->type().empty()) { 2201 return {}; 2202 } 2203 } 2204 2205 return result; 2206 } 2207 2208 2209 // EO parse_while_directive parseMediaRule()2210 MediaRule_Obj Parser::parseMediaRule() 2211 { 2212 MediaRule_Obj rule = SASS_MEMORY_NEW(MediaRule, pstate); 2213 stack.push_back(Scope::Media); 2214 rule->schema(parse_media_queries()); 2215 parse_block_comments(false); 2216 rule->block(parse_css_block()); 2217 stack.pop_back(); 2218 return rule; 2219 } 2220 parse_media_queries()2221 List_Obj Parser::parse_media_queries() 2222 { 2223 advanceToNextToken(); 2224 List_Obj queries = SASS_MEMORY_NEW(List, pstate, 0, SASS_COMMA); 2225 if (!peek_css < exactly <'{'> >()) queries->append(parse_media_query()); 2226 while (lex_css < exactly <','> >()) queries->append(parse_media_query()); 2227 queries->update_pstate(pstate); 2228 return queries.detach(); 2229 } 2230 2231 // Expression* Parser::parse_media_query() parse_media_query()2232 Media_Query_Obj Parser::parse_media_query() 2233 { 2234 advanceToNextToken(); 2235 Media_Query_Obj media_query = SASS_MEMORY_NEW(Media_Query, pstate); 2236 if (lex < kwd_not >()) { media_query->is_negated(true); lex < css_comments >(false); } 2237 else if (lex < kwd_only >()) { media_query->is_restricted(true); lex < css_comments >(false); } 2238 2239 if (lex < identifier_schema >()) media_query->media_type(parse_identifier_schema()); 2240 else if (lex < identifier >()) media_query->media_type(parse_interpolated_chunk(lexed)); 2241 else media_query->append(parse_media_expression()); 2242 2243 while (lex_css < kwd_and >()) media_query->append(parse_media_expression()); 2244 if (lex < identifier_schema >()) { 2245 String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); 2246 if (media_query->media_type()) { 2247 schema->append(media_query->media_type()); 2248 schema->append(SASS_MEMORY_NEW(String_Constant, pstate, " ")); 2249 } 2250 schema->append(parse_identifier_schema()); 2251 media_query->media_type(schema); 2252 } 2253 while (lex_css < kwd_and >()) media_query->append(parse_media_expression()); 2254 2255 media_query->update_pstate(pstate); 2256 2257 return media_query; 2258 } 2259 parse_media_expression()2260 Media_Query_ExpressionObj Parser::parse_media_expression() 2261 { 2262 if (lex < identifier_schema >()) { 2263 String_Obj ss = parse_identifier_schema(); 2264 return SASS_MEMORY_NEW(Media_Query_Expression, pstate, ss, ExpressionObj{}, true); 2265 } 2266 if (!lex_css< exactly<'('> >()) { 2267 error("media query expression must begin with '('"); 2268 } 2269 ExpressionObj feature; 2270 if (peek_css< exactly<')'> >()) { 2271 error("media feature required in media query expression"); 2272 } 2273 feature = parse_expression(); 2274 ExpressionObj expression; 2275 if (lex_css< exactly<':'> >()) { 2276 expression = parse_list(DELAYED); 2277 } 2278 if (!lex_css< exactly<')'> >()) { 2279 error("unclosed parenthesis in media query expression"); 2280 } 2281 return SASS_MEMORY_NEW(Media_Query_Expression, feature->pstate(), feature, expression); 2282 } 2283 2284 // lexed after `kwd_supports_directive` 2285 // these are very similar to media blocks parse_supports_directive()2286 SupportsRuleObj Parser::parse_supports_directive() 2287 { 2288 SupportsConditionObj cond = parse_supports_condition(/*top_level=*/true); 2289 // create the ast node object for the support queries 2290 SupportsRuleObj query = SASS_MEMORY_NEW(SupportsRule, pstate, cond); 2291 // additional block is mandatory 2292 // parse inner block 2293 query->block(parse_block()); 2294 // return ast node 2295 return query; 2296 } 2297 2298 // parse one query operation 2299 // may encounter nested queries parse_supports_condition(bool top_level)2300 SupportsConditionObj Parser::parse_supports_condition(bool top_level) 2301 { 2302 lex < css_whitespace >(); 2303 SupportsConditionObj cond; 2304 if ((cond = parse_supports_negation())) return cond; 2305 if ((cond = parse_supports_operator(top_level))) return cond; 2306 if ((cond = parse_supports_interpolation())) return cond; 2307 return cond; 2308 } 2309 parse_supports_negation()2310 SupportsConditionObj Parser::parse_supports_negation() 2311 { 2312 if (!lex < kwd_not >()) return {}; 2313 SupportsConditionObj cond = parse_supports_condition_in_parens(/*parens_required=*/true); 2314 return SASS_MEMORY_NEW(SupportsNegation, pstate, cond); 2315 } 2316 parse_supports_operator(bool top_level)2317 SupportsConditionObj Parser::parse_supports_operator(bool top_level) 2318 { 2319 SupportsConditionObj cond = parse_supports_condition_in_parens(/*parens_required=*/top_level); 2320 if (cond.isNull()) return {}; 2321 2322 while (true) { 2323 SupportsOperation::Operand op = SupportsOperation::OR; 2324 if (lex < kwd_and >()) { op = SupportsOperation::AND; } 2325 else if(!lex < kwd_or >()) { break; } 2326 2327 lex < css_whitespace >(); 2328 SupportsConditionObj right = parse_supports_condition_in_parens(/*parens_required=*/true); 2329 2330 // SupportsCondition* cc = SASS_MEMORY_NEW(SupportsCondition, *static_cast<SupportsCondition*>(cond)); 2331 cond = SASS_MEMORY_NEW(SupportsOperation, pstate, cond, right, op); 2332 } 2333 return cond; 2334 } 2335 parse_supports_interpolation()2336 SupportsConditionObj Parser::parse_supports_interpolation() 2337 { 2338 if (!lex < interpolant >()) return {}; 2339 2340 String_Obj interp = parse_interpolated_chunk(lexed); 2341 if (!interp) return {}; 2342 2343 return SASS_MEMORY_NEW(Supports_Interpolation, pstate, interp); 2344 } 2345 2346 // TODO: This needs some major work. Although feature conditions 2347 // look like declarations their semantics differ significantly parse_supports_declaration()2348 SupportsConditionObj Parser::parse_supports_declaration() 2349 { 2350 SupportsCondition* cond; 2351 // parse something declaration like 2352 ExpressionObj feature = parse_expression(); 2353 ExpressionObj expression; 2354 if (lex_css< exactly<':'> >()) { 2355 expression = parse_list(DELAYED); 2356 } 2357 if (!feature || !expression) error("@supports condition expected declaration"); 2358 cond = SASS_MEMORY_NEW(SupportsDeclaration, 2359 feature->pstate(), 2360 feature, 2361 expression); 2362 // ToDo: maybe we need an additional error condition? 2363 return cond; 2364 } 2365 parse_supports_condition_in_parens(bool parens_required)2366 SupportsConditionObj Parser::parse_supports_condition_in_parens(bool parens_required) 2367 { 2368 SupportsConditionObj interp = parse_supports_interpolation(); 2369 if (interp != nullptr) return interp; 2370 2371 if (!lex < exactly <'('> >()) { 2372 if (parens_required) { 2373 css_error("Invalid CSS", " after ", ": expected @supports condition (e.g. (display: flexbox)), was ", /*trim=*/false); 2374 } else { 2375 return {}; 2376 } 2377 } 2378 lex < css_whitespace >(); 2379 2380 SupportsConditionObj cond = parse_supports_condition(/*top_level=*/false); 2381 if (cond.isNull()) cond = parse_supports_declaration(); 2382 if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration"); 2383 2384 lex < css_whitespace >(); 2385 return cond; 2386 } 2387 parse_at_root_block()2388 AtRootRuleObj Parser::parse_at_root_block() 2389 { 2390 stack.push_back(Scope::AtRoot); 2391 SourceSpan at_source_position = pstate; 2392 Block_Obj body; 2393 At_Root_Query_Obj expr; 2394 Lookahead lookahead_result; 2395 if (lex_css< exactly<'('> >()) { 2396 expr = parse_at_root_query(); 2397 } 2398 if (peek_css < exactly<'{'> >()) { 2399 lex <optional_spaces>(); 2400 body = parse_block(true); 2401 } 2402 else if ((lookahead_result = lookahead_for_selector(position)).found) { 2403 StyleRuleObj r = parse_ruleset(lookahead_result); 2404 body = SASS_MEMORY_NEW(Block, r->pstate(), 1, true); 2405 body->append(r); 2406 } 2407 AtRootRuleObj at_root = SASS_MEMORY_NEW(AtRootRule, at_source_position, body); 2408 if (!expr.isNull()) at_root->expression(expr); 2409 stack.pop_back(); 2410 return at_root; 2411 } 2412 parse_at_root_query()2413 At_Root_Query_Obj Parser::parse_at_root_query() 2414 { 2415 if (peek< exactly<')'> >()) error("at-root feature required in at-root expression"); 2416 2417 if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) { 2418 css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was "); 2419 } 2420 2421 ExpressionObj feature = parse_list(); 2422 if (!lex_css< exactly<':'> >()) error("style declaration must contain a value"); 2423 ExpressionObj expression = parse_list(); 2424 List_Obj value = SASS_MEMORY_NEW(List, feature->pstate(), 1); 2425 2426 if (expression->concrete_type() == Expression::LIST) { 2427 value = Cast<List>(expression); 2428 } 2429 else value->append(expression); 2430 2431 At_Root_Query_Obj cond = SASS_MEMORY_NEW(At_Root_Query, 2432 value->pstate(), 2433 feature, 2434 value); 2435 if (!lex_css< exactly<')'> >()) error("unclosed parenthesis in @at-root expression"); 2436 return cond; 2437 } 2438 parse_directive()2439 AtRuleObj Parser::parse_directive() 2440 { 2441 AtRuleObj directive = SASS_MEMORY_NEW(AtRule, pstate, lexed); 2442 String_Schema_Obj val = parse_almost_any_value(); 2443 // strip left and right if they are of type string 2444 directive->value(val); 2445 if (peek< exactly<'{'> >()) { 2446 directive->block(parse_block()); 2447 } 2448 return directive; 2449 } 2450 lex_interpolation()2451 ExpressionObj Parser::lex_interpolation() 2452 { 2453 if (lex < interpolant >(true) != NULL) { 2454 return parse_interpolated_chunk(lexed, true); 2455 } 2456 return {}; 2457 } 2458 lex_interp_uri()2459 ExpressionObj Parser::lex_interp_uri() 2460 { 2461 // create a string schema by lexing optional interpolations 2462 return lex_interp< re_string_uri_open, re_string_uri_close >(); 2463 } 2464 lex_interp_string()2465 ExpressionObj Parser::lex_interp_string() 2466 { 2467 ExpressionObj rv; 2468 if ((rv = lex_interp< re_string_double_open, re_string_double_close >())) return rv; 2469 if ((rv = lex_interp< re_string_single_open, re_string_single_close >())) return rv; 2470 return rv; 2471 } 2472 lex_almost_any_value_chars()2473 ExpressionObj Parser::lex_almost_any_value_chars() 2474 { 2475 const char* match = 2476 lex < 2477 one_plus < 2478 alternatives < 2479 exactly <'>'>, 2480 sequence < 2481 exactly <'\\'>, 2482 any_char 2483 >, 2484 sequence < 2485 negate < 2486 sequence < 2487 exactly < url_kwd >, 2488 exactly <'('> 2489 > 2490 >, 2491 neg_class_char < 2492 almost_any_value_class 2493 > 2494 >, 2495 sequence < 2496 exactly <'/'>, 2497 negate < 2498 alternatives < 2499 exactly <'/'>, 2500 exactly <'*'> 2501 > 2502 > 2503 >, 2504 sequence < 2505 exactly <'\\'>, 2506 exactly <'#'>, 2507 negate < 2508 exactly <'{'> 2509 > 2510 >, 2511 sequence < 2512 exactly <'!'>, 2513 negate < 2514 alpha 2515 > 2516 > 2517 > 2518 > 2519 >(false); 2520 if (match) { 2521 return SASS_MEMORY_NEW(String_Constant, pstate, lexed); 2522 } 2523 return {}; 2524 } 2525 lex_almost_any_value_token()2526 ExpressionObj Parser::lex_almost_any_value_token() 2527 { 2528 ExpressionObj rv; 2529 if (*position == 0) return {}; 2530 if ((rv = lex_almost_any_value_chars())) return rv; 2531 // if ((rv = lex_block_comment())) return rv; 2532 // if ((rv = lex_single_line_comment())) return rv; 2533 if ((rv = lex_interp_string())) return rv; 2534 if ((rv = lex_interp_uri())) return rv; 2535 if ((rv = lex_interpolation())) return rv; 2536 if (lex< alternatives< hex, hex0 > >()) 2537 { return lexed_hex_color(lexed); } 2538 return rv; 2539 } 2540 parse_almost_any_value()2541 String_Schema_Obj Parser::parse_almost_any_value() 2542 { 2543 2544 String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate); 2545 if (*position == 0) return {}; 2546 lex < spaces >(false); 2547 ExpressionObj token = lex_almost_any_value_token(); 2548 if (!token) return {}; 2549 schema->append(token); 2550 if (*position == 0) { 2551 schema->rtrim(); 2552 return schema.detach(); 2553 } 2554 2555 while ((token = lex_almost_any_value_token())) { 2556 schema->append(token); 2557 } 2558 2559 lex < css_whitespace >(); 2560 2561 schema->rtrim(); 2562 2563 return schema.detach(); 2564 } 2565 parse_warning()2566 WarningRuleObj Parser::parse_warning() 2567 { 2568 if (stack.back() != Scope::Root && 2569 stack.back() != Scope::Function && 2570 stack.back() != Scope::Mixin && 2571 stack.back() != Scope::Control && 2572 stack.back() != Scope::Rules) { 2573 error("Illegal nesting: Only properties may be nested beneath properties."); 2574 } 2575 return SASS_MEMORY_NEW(WarningRule, pstate, parse_list(DELAYED)); 2576 } 2577 parse_error()2578 ErrorRuleObj Parser::parse_error() 2579 { 2580 if (stack.back() != Scope::Root && 2581 stack.back() != Scope::Function && 2582 stack.back() != Scope::Mixin && 2583 stack.back() != Scope::Control && 2584 stack.back() != Scope::Rules) { 2585 error("Illegal nesting: Only properties may be nested beneath properties."); 2586 } 2587 return SASS_MEMORY_NEW(ErrorRule, pstate, parse_list(DELAYED)); 2588 } 2589 parse_debug()2590 DebugRuleObj Parser::parse_debug() 2591 { 2592 if (stack.back() != Scope::Root && 2593 stack.back() != Scope::Function && 2594 stack.back() != Scope::Mixin && 2595 stack.back() != Scope::Control && 2596 stack.back() != Scope::Rules) { 2597 error("Illegal nesting: Only properties may be nested beneath properties."); 2598 } 2599 return SASS_MEMORY_NEW(DebugRule, pstate, parse_list(DELAYED)); 2600 } 2601 parse_return_directive()2602 Return_Obj Parser::parse_return_directive() 2603 { 2604 // check that we do not have an empty list (ToDo: check if we got all cases) 2605 if (peek_css < alternatives < exactly < ';' >, exactly < '}' >, end_of_file > >()) 2606 { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } 2607 return SASS_MEMORY_NEW(Return, pstate, parse_list()); 2608 } 2609 lookahead_for_selector(const char * start)2610 Lookahead Parser::lookahead_for_selector(const char* start) 2611 { 2612 // init result struct 2613 Lookahead rv = Lookahead(); 2614 // get start position 2615 const char* p = start ? start : position; 2616 // match in one big "regex" 2617 rv.error = p; 2618 if (const char* q = 2619 peek < 2620 re_selector_list 2621 >(p) 2622 ) { 2623 bool could_be_property = peek< sequence< exactly<'-'>, exactly<'-'> > >(p) != 0; 2624 bool could_be_escaped = false; 2625 while (p < q) { 2626 // did we have interpolations? 2627 if (*p == '#' && *(p+1) == '{') { 2628 rv.has_interpolants = true; 2629 p = q; break; 2630 } 2631 // A property that's ambiguous with a nested selector is interpreted as a 2632 // custom property. 2633 if (*p == ':' && !could_be_escaped) { 2634 rv.is_custom_property = could_be_property || p+1 == q || peek< space >(p+1); 2635 } 2636 could_be_escaped = *p == '\\'; 2637 ++ p; 2638 } 2639 // store anyway } 2640 2641 2642 // ToDo: remove 2643 rv.error = q; 2644 rv.position = q; 2645 // check expected opening bracket 2646 // only after successful matching 2647 if (peek < exactly<'{'> >(q)) rv.found = q; 2648 // else if (peek < end_of_file >(q)) rv.found = q; 2649 else if (peek < exactly<'('> >(q)) rv.found = q; 2650 // else if (peek < exactly<';'> >(q)) rv.found = q; 2651 // else if (peek < exactly<'}'> >(q)) rv.found = q; 2652 if (rv.found || *p == 0) rv.error = 0; 2653 } 2654 2655 rv.parsable = ! rv.has_interpolants; 2656 2657 // return result 2658 return rv; 2659 2660 } 2661 // EO lookahead_for_selector 2662 2663 // used in parse_block_nodes and parse_special_directive 2664 // ToDo: actual usage is still not really clear to me? lookahead_for_include(const char * start)2665 Lookahead Parser::lookahead_for_include(const char* start) 2666 { 2667 // we actually just lookahead for a selector 2668 Lookahead rv = lookahead_for_selector(start); 2669 // but the "found" rules are different 2670 if (const char* p = rv.position) { 2671 // check for additional abort condition 2672 if (peek < exactly<';'> >(p)) rv.found = p; 2673 else if (peek < exactly<'}'> >(p)) rv.found = p; 2674 } 2675 // return result 2676 return rv; 2677 } 2678 // EO lookahead_for_include 2679 2680 // look ahead for a token with interpolation in it 2681 // we mostly use the result if there is an interpolation 2682 // everything that passes here gets parsed as one schema 2683 // meaning it will not be parsed as a space separated list lookahead_for_value(const char * start)2684 Lookahead Parser::lookahead_for_value(const char* start) 2685 { 2686 // init result struct 2687 Lookahead rv = Lookahead(); 2688 // get start position 2689 const char* p = start ? start : position; 2690 // match in one big "regex" 2691 if (const char* q = 2692 peek < 2693 non_greedy < 2694 alternatives < 2695 // consume whitespace 2696 block_comment, // spaces, 2697 // main tokens 2698 sequence < 2699 interpolant, 2700 optional < 2701 quoted_string 2702 > 2703 >, 2704 identifier, 2705 variable, 2706 // issue #442 2707 sequence < 2708 parenthese_scope, 2709 interpolant, 2710 optional < 2711 quoted_string 2712 > 2713 > 2714 >, 2715 sequence < 2716 // optional_spaces, 2717 alternatives < 2718 // end_of_file, 2719 exactly<'{'>, 2720 exactly<'}'>, 2721 exactly<';'> 2722 > 2723 > 2724 > 2725 >(p) 2726 ) { 2727 if (p == q) return rv; 2728 while (p < q) { 2729 // did we have interpolations? 2730 if (*p == '#' && *(p+1) == '{') { 2731 rv.has_interpolants = true; 2732 p = q; break; 2733 } 2734 ++ p; 2735 } 2736 // store anyway 2737 // ToDo: remove 2738 rv.position = q; 2739 // check expected opening bracket 2740 // only after successful matching 2741 if (peek < exactly<'{'> >(q)) rv.found = q; 2742 else if (peek < exactly<';'> >(q)) rv.found = q; 2743 else if (peek < exactly<'}'> >(q)) rv.found = q; 2744 } 2745 2746 // return result 2747 return rv; 2748 } 2749 // EO lookahead_for_value 2750 read_bom()2751 void Parser::read_bom() 2752 { 2753 size_t skip = 0; 2754 sass::string encoding; 2755 bool utf_8 = false; 2756 switch ((unsigned char)position[0]) { 2757 case 0xEF: 2758 skip = check_bom_chars(position, end, utf_8_bom, 3); 2759 encoding = "UTF-8"; 2760 utf_8 = true; 2761 break; 2762 case 0xFE: 2763 skip = check_bom_chars(position, end, utf_16_bom_be, 2); 2764 encoding = "UTF-16 (big endian)"; 2765 break; 2766 case 0xFF: 2767 skip = check_bom_chars(position, end, utf_16_bom_le, 2); 2768 skip += (skip ? check_bom_chars(position, end, utf_32_bom_le, 4) : 0); 2769 encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)"); 2770 break; 2771 case 0x00: 2772 skip = check_bom_chars(position, end, utf_32_bom_be, 4); 2773 encoding = "UTF-32 (big endian)"; 2774 break; 2775 case 0x2B: 2776 skip = check_bom_chars(position, end, utf_7_bom_1, 4) 2777 | check_bom_chars(position, end, utf_7_bom_2, 4) 2778 | check_bom_chars(position, end, utf_7_bom_3, 4) 2779 | check_bom_chars(position, end, utf_7_bom_4, 4) 2780 | check_bom_chars(position, end, utf_7_bom_5, 5); 2781 encoding = "UTF-7"; 2782 break; 2783 case 0xF7: 2784 skip = check_bom_chars(position, end, utf_1_bom, 3); 2785 encoding = "UTF-1"; 2786 break; 2787 case 0xDD: 2788 skip = check_bom_chars(position, end, utf_ebcdic_bom, 4); 2789 encoding = "UTF-EBCDIC"; 2790 break; 2791 case 0x0E: 2792 skip = check_bom_chars(position, end, scsu_bom, 3); 2793 encoding = "SCSU"; 2794 break; 2795 case 0xFB: 2796 skip = check_bom_chars(position, end, bocu_1_bom, 3); 2797 encoding = "BOCU-1"; 2798 break; 2799 case 0x84: 2800 skip = check_bom_chars(position, end, gb_18030_bom, 4); 2801 encoding = "GB-18030"; 2802 break; 2803 default: break; 2804 } 2805 if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding); 2806 position += skip; 2807 } 2808 check_bom_chars(const char * src,const char * end,const unsigned char * bom,size_t len)2809 size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len) 2810 { 2811 size_t skip = 0; 2812 if (src + len > end) return 0; 2813 for (size_t i = 0; i < len; ++i, ++skip) { 2814 if ((unsigned char) src[i] != bom[i]) return 0; 2815 } 2816 return skip; 2817 } 2818 2819 fold_operands(ExpressionObj base,sass::vector<ExpressionObj> & operands,Operand op)2820 ExpressionObj Parser::fold_operands(ExpressionObj base, sass::vector<ExpressionObj>& operands, Operand op) 2821 { 2822 for (size_t i = 0, S = operands.size(); i < S; ++i) { 2823 base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), op, base, operands[i]); 2824 } 2825 return base; 2826 } 2827 fold_operands(ExpressionObj base,sass::vector<ExpressionObj> & operands,sass::vector<Operand> & ops,size_t i)2828 ExpressionObj Parser::fold_operands(ExpressionObj base, sass::vector<ExpressionObj>& operands, sass::vector<Operand>& ops, size_t i) 2829 { 2830 if (String_Schema* schema = Cast<String_Schema>(base)) { 2831 // return schema; 2832 if (schema->has_interpolants()) { 2833 if (i + 1 < operands.size() && ( 2834 (ops[0].operand == Sass_OP::EQ) 2835 || (ops[0].operand == Sass_OP::ADD) 2836 || (ops[0].operand == Sass_OP::DIV) 2837 || (ops[0].operand == Sass_OP::MUL) 2838 || (ops[0].operand == Sass_OP::NEQ) 2839 || (ops[0].operand == Sass_OP::LT) 2840 || (ops[0].operand == Sass_OP::GT) 2841 || (ops[0].operand == Sass_OP::LTE) 2842 || (ops[0].operand == Sass_OP::GTE) 2843 )) { 2844 ExpressionObj rhs = fold_operands(operands[i], operands, ops, i + 1); 2845 rhs = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[0], schema, rhs); 2846 return rhs; 2847 } 2848 // return schema; 2849 } 2850 } 2851 2852 if (operands.size() > Constants::MaxCallStack) { 2853 // XXX: this is never hit via spec tests 2854 sass::ostream stm; 2855 stm << "Stack depth exceeded max of " << Constants::MaxCallStack; 2856 error(stm.str()); 2857 } 2858 2859 for (size_t S = operands.size(); i < S; ++i) { 2860 if (String_Schema* schema = Cast<String_Schema>(operands[i])) { 2861 if (schema->has_interpolants()) { 2862 if (i + 1 < S) { 2863 // this whole branch is never hit via spec tests 2864 ExpressionObj rhs = fold_operands(operands[i+1], operands, ops, i + 2); 2865 rhs = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], schema, rhs); 2866 base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, rhs); 2867 return base; 2868 } 2869 base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, operands[i]); 2870 return base; 2871 } else { 2872 base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, operands[i]); 2873 } 2874 } else { 2875 base = SASS_MEMORY_NEW(Binary_Expression, base->pstate(), ops[i], base, operands[i]); 2876 } 2877 Binary_Expression* b = Cast<Binary_Expression>(base.ptr()); 2878 if (b && ops[i].operand == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { 2879 base->is_delayed(true); 2880 } 2881 } 2882 // nested binary expression are never to be delayed 2883 if (Binary_Expression* b = Cast<Binary_Expression>(base)) { 2884 if (Cast<Binary_Expression>(b->left())) base->set_delayed(false); 2885 if (Cast<Binary_Expression>(b->right())) base->set_delayed(false); 2886 } 2887 return base; 2888 } 2889 error(sass::string msg)2890 void Parser::error(sass::string msg) 2891 { 2892 traces.push_back(Backtrace(pstate)); 2893 throw Exception::InvalidSass(pstate, traces, msg); 2894 } 2895 2896 // print a css parsing error with actual context information from parsed source css_error(const sass::string & msg,const sass::string & prefix,const sass::string & middle,const bool trim)2897 void Parser::css_error(const sass::string& msg, const sass::string& prefix, const sass::string& middle, const bool trim) 2898 { 2899 int max_len = 18; 2900 const char* end = this->end; 2901 while (*end != 0) ++ end; 2902 const char* pos = peek < optional_spaces >(); 2903 if (!pos) pos = position; 2904 2905 const char* last_pos(pos); 2906 if (last_pos > begin) { 2907 utf8::prior(last_pos, begin); 2908 } 2909 // backup position to last significant char 2910 while (trim && last_pos > begin&& last_pos < end) { 2911 if (!Util::ascii_isspace(static_cast<unsigned char>(*last_pos))) break; 2912 utf8::prior(last_pos, begin); 2913 } 2914 2915 bool ellipsis_left = false; 2916 const char* pos_left(last_pos); 2917 const char* end_left(last_pos); 2918 2919 if (*pos_left) utf8::next(pos_left, end); 2920 if (*end_left) utf8::next(end_left, end); 2921 while (pos_left > begin) { 2922 if (utf8::distance(pos_left, end_left) >= max_len) { 2923 utf8::prior(pos_left, begin); 2924 ellipsis_left = *(pos_left) != '\n' && 2925 *(pos_left) != '\r'; 2926 utf8::next(pos_left, end); 2927 break; 2928 } 2929 2930 const char* prev = pos_left; 2931 utf8::prior(prev, begin); 2932 if (*prev == '\r') break; 2933 if (*prev == '\n') break; 2934 pos_left = prev; 2935 } 2936 if (pos_left < begin) { 2937 pos_left = begin; 2938 } 2939 2940 bool ellipsis_right = false; 2941 const char* end_right(pos); 2942 const char* pos_right(pos); 2943 while (end_right < end) { 2944 if (utf8::distance(pos_right, end_right) > max_len) { 2945 ellipsis_left = *(pos_right) != '\n' && 2946 *(pos_right) != '\r'; 2947 break; 2948 } 2949 if (*end_right == '\r') break; 2950 if (*end_right == '\n') break; 2951 utf8::next(end_right, end); 2952 } 2953 // if (*end_right == 0) end_right ++; 2954 2955 sass::string left(pos_left, end_left); 2956 sass::string right(pos_right, end_right); 2957 size_t left_subpos = left.size() > 15 ? left.size() - 15 : 0; 2958 size_t right_subpos = right.size() > 15 ? right.size() - 15 : 0; 2959 if (left_subpos && ellipsis_left) left = ellipsis + left.substr(left_subpos); 2960 if (right_subpos && ellipsis_right) right = right.substr(right_subpos) + ellipsis; 2961 // now pass new message to the more generic error function 2962 error(msg + prefix + quote(left) + middle + quote(right)); 2963 } 2964 2965 } 2966