1 //===-- lib/Parser/prescan.cpp --------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "prescan.h" 10 #include "preprocessor.h" 11 #include "token-sequence.h" 12 #include "flang/Common/idioms.h" 13 #include "flang/Parser/characters.h" 14 #include "flang/Parser/message.h" 15 #include "flang/Parser/source.h" 16 #include "llvm/Support/raw_ostream.h" 17 #include <cstddef> 18 #include <cstring> 19 #include <utility> 20 #include <vector> 21 22 namespace Fortran::parser { 23 24 using common::LanguageFeature; 25 26 static constexpr int maxPrescannerNesting{100}; 27 28 Prescanner::Prescanner(Messages &messages, CookedSource &cooked, 29 Preprocessor &preprocessor, common::LanguageFeatureControl lfc) 30 : messages_{messages}, cooked_{cooked}, preprocessor_{preprocessor}, 31 features_{lfc}, encoding_{cooked.allSources().encoding()} {} 32 33 Prescanner::Prescanner(const Prescanner &that) 34 : messages_{that.messages_}, cooked_{that.cooked_}, 35 preprocessor_{that.preprocessor_}, features_{that.features_}, 36 inFixedForm_{that.inFixedForm_}, 37 fixedFormColumnLimit_{that.fixedFormColumnLimit_}, 38 encoding_{that.encoding_}, prescannerNesting_{that.prescannerNesting_ + 39 1}, 40 skipLeadingAmpersand_{that.skipLeadingAmpersand_}, 41 compilerDirectiveBloomFilter_{that.compilerDirectiveBloomFilter_}, 42 compilerDirectiveSentinels_{that.compilerDirectiveSentinels_} {} 43 44 static inline constexpr bool IsFixedFormCommentChar(char ch) { 45 return ch == '!' || ch == '*' || ch == 'C' || ch == 'c'; 46 } 47 48 static void NormalizeCompilerDirectiveCommentMarker(TokenSequence &dir) { 49 char *p{dir.GetMutableCharData()}; 50 char *limit{p + dir.SizeInChars()}; 51 for (; p < limit; ++p) { 52 if (*p != ' ') { 53 CHECK(IsFixedFormCommentChar(*p)); 54 *p = '!'; 55 return; 56 } 57 } 58 DIE("compiler directive all blank"); 59 } 60 61 void Prescanner::Prescan(ProvenanceRange range) { 62 AllSources &allSources{cooked_.allSources()}; 63 startProvenance_ = range.start(); 64 std::size_t offset{0}; 65 const SourceFile *source{allSources.GetSourceFile(startProvenance_, &offset)}; 66 CHECK(source); 67 start_ = source->content().data() + offset; 68 limit_ = start_ + range.size(); 69 nextLine_ = start_; 70 const bool beganInFixedForm{inFixedForm_}; 71 if (prescannerNesting_ > maxPrescannerNesting) { 72 Say(GetProvenance(start_), 73 "too many nested INCLUDE/#include files, possibly circular"_err_en_US); 74 return; 75 } 76 while (nextLine_ < limit_) { 77 Statement(); 78 } 79 if (inFixedForm_ != beganInFixedForm) { 80 std::string dir{"!dir$ "}; 81 if (beganInFixedForm) { 82 dir += "fixed"; 83 } else { 84 dir += "free"; 85 } 86 dir += '\n'; 87 TokenSequence tokens{dir, allSources.AddCompilerInsertion(dir).start()}; 88 tokens.Emit(cooked_); 89 } 90 } 91 92 void Prescanner::Statement() { 93 TokenSequence tokens; 94 LineClassification line{ClassifyLine(nextLine_)}; 95 switch (line.kind) { 96 case LineClassification::Kind::Comment: 97 nextLine_ += line.payloadOffset; // advance to '!' or newline 98 NextLine(); 99 return; 100 case LineClassification::Kind::IncludeLine: 101 FortranInclude(nextLine_ + line.payloadOffset); 102 NextLine(); 103 return; 104 case LineClassification::Kind::ConditionalCompilationDirective: 105 case LineClassification::Kind::IncludeDirective: 106 case LineClassification::Kind::DefinitionDirective: 107 case LineClassification::Kind::PreprocessorDirective: 108 preprocessor_.Directive(TokenizePreprocessorDirective(), this); 109 return; 110 case LineClassification::Kind::CompilerDirective: 111 directiveSentinel_ = line.sentinel; 112 CHECK(InCompilerDirective()); 113 BeginSourceLineAndAdvance(); 114 if (inFixedForm_) { 115 CHECK(IsFixedFormCommentChar(*at_)); 116 } else { 117 while (*at_ == ' ' || *at_ == '\t') { 118 ++at_, ++column_; 119 } 120 CHECK(*at_ == '!'); 121 } 122 if (directiveSentinel_[0] == '$' && directiveSentinel_[1] == '\0') { 123 // OpenMP conditional compilation line. Remove the sentinel and then 124 // treat the line as if it were normal source. 125 at_ += 2, column_ += 2; 126 if (inFixedForm_) { 127 LabelField(tokens); 128 } else { 129 SkipSpaces(); 130 } 131 } else { 132 // Compiler directive. Emit normalized sentinel. 133 EmitChar(tokens, '!'); 134 ++at_, ++column_; 135 for (const char *sp{directiveSentinel_}; *sp != '\0'; 136 ++sp, ++at_, ++column_) { 137 EmitChar(tokens, *sp); 138 } 139 if (*at_ == ' ') { 140 EmitChar(tokens, ' '); 141 ++at_, ++column_; 142 } 143 tokens.CloseToken(); 144 } 145 break; 146 case LineClassification::Kind::Source: 147 BeginSourceLineAndAdvance(); 148 if (inFixedForm_) { 149 LabelField(tokens); 150 } else if (skipLeadingAmpersand_) { 151 skipLeadingAmpersand_ = false; 152 const char *p{SkipWhiteSpace(at_)}; 153 if (p < limit_ && *p == '&') { 154 column_ += ++p - at_; 155 at_ = p; 156 } 157 } else { 158 SkipSpaces(); 159 } 160 break; 161 } 162 163 while (NextToken(tokens)) { 164 } 165 166 Provenance newlineProvenance{GetCurrentProvenance()}; 167 if (std::optional<TokenSequence> preprocessed{ 168 preprocessor_.MacroReplacement(tokens, *this)}) { 169 // Reprocess the preprocessed line. Append a newline temporarily. 170 preprocessed->PutNextTokenChar('\n', newlineProvenance); 171 preprocessed->CloseToken(); 172 const char *ppd{preprocessed->ToCharBlock().begin()}; 173 LineClassification ppl{ClassifyLine(ppd)}; 174 preprocessed->RemoveLastToken(); // remove the newline 175 switch (ppl.kind) { 176 case LineClassification::Kind::Comment: 177 break; 178 case LineClassification::Kind::IncludeLine: 179 FortranInclude(ppd + ppl.payloadOffset); 180 break; 181 case LineClassification::Kind::ConditionalCompilationDirective: 182 case LineClassification::Kind::IncludeDirective: 183 case LineClassification::Kind::DefinitionDirective: 184 case LineClassification::Kind::PreprocessorDirective: 185 Say(preprocessed->GetProvenanceRange(), 186 "Preprocessed line resembles a preprocessor directive"_en_US); 187 preprocessed->ToLowerCase().Emit(cooked_); 188 break; 189 case LineClassification::Kind::CompilerDirective: 190 if (preprocessed->HasRedundantBlanks()) { 191 preprocessed->RemoveRedundantBlanks(); 192 } 193 NormalizeCompilerDirectiveCommentMarker(*preprocessed); 194 preprocessed->ToLowerCase(); 195 SourceFormChange(preprocessed->ToString()); 196 preprocessed->ClipComment(true /* skip first ! */).Emit(cooked_); 197 break; 198 case LineClassification::Kind::Source: 199 if (inFixedForm_) { 200 if (preprocessed->HasBlanks(/*after column*/ 6)) { 201 preprocessed->RemoveBlanks(/*after column*/ 6); 202 } 203 } else { 204 if (preprocessed->HasRedundantBlanks()) { 205 preprocessed->RemoveRedundantBlanks(); 206 } 207 } 208 preprocessed->ToLowerCase().ClipComment().Emit(cooked_); 209 break; 210 } 211 } else { 212 tokens.ToLowerCase(); 213 if (line.kind == LineClassification::Kind::CompilerDirective) { 214 SourceFormChange(tokens.ToString()); 215 } 216 tokens.Emit(cooked_); 217 } 218 if (omitNewline_) { 219 omitNewline_ = false; 220 } else { 221 cooked_.Put('\n', newlineProvenance); 222 } 223 directiveSentinel_ = nullptr; 224 } 225 226 TokenSequence Prescanner::TokenizePreprocessorDirective() { 227 CHECK(nextLine_ < limit_ && !inPreprocessorDirective_); 228 inPreprocessorDirective_ = true; 229 BeginSourceLineAndAdvance(); 230 TokenSequence tokens; 231 while (NextToken(tokens)) { 232 } 233 inPreprocessorDirective_ = false; 234 return tokens; 235 } 236 237 void Prescanner::NextLine() { 238 void *vstart{static_cast<void *>(const_cast<char *>(nextLine_))}; 239 void *v{std::memchr(vstart, '\n', limit_ - nextLine_)}; 240 if (!v) { 241 nextLine_ = limit_; 242 } else { 243 const char *nl{const_cast<const char *>(static_cast<char *>(v))}; 244 nextLine_ = nl + 1; 245 } 246 } 247 248 void Prescanner::LabelField(TokenSequence &token, int outCol) { 249 for (; *at_ != '\n' && column_ <= 6; ++at_) { 250 if (*at_ == '\t') { 251 ++at_; 252 column_ = 7; 253 break; 254 } 255 if (*at_ != ' ' && 256 !(*at_ == '0' && column_ == 6)) { // '0' in column 6 becomes space 257 EmitChar(token, *at_); 258 ++outCol; 259 } 260 ++column_; 261 } 262 if (outCol > 1) { 263 token.CloseToken(); 264 } 265 if (outCol < 7) { 266 if (outCol == 1) { 267 token.Put(" ", 6, sixSpaceProvenance_.start()); 268 } else { 269 for (; outCol < 7; ++outCol) { 270 token.PutNextTokenChar(' ', spaceProvenance_); 271 } 272 token.CloseToken(); 273 } 274 } 275 } 276 277 void Prescanner::SkipToEndOfLine() { 278 while (*at_ != '\n') { 279 ++at_, ++column_; 280 } 281 } 282 283 bool Prescanner::MustSkipToEndOfLine() const { 284 if (inFixedForm_ && column_ > fixedFormColumnLimit_ && !tabInCurrentLine_) { 285 return true; // skip over ignored columns in right margin (73:80) 286 } else if (*at_ == '!' && !inCharLiteral_) { 287 return true; // inline comment goes to end of source line 288 } else { 289 return false; 290 } 291 } 292 293 void Prescanner::NextChar() { 294 CHECK(*at_ != '\n'); 295 ++at_, ++column_; 296 while (at_[0] == '\xef' && at_[1] == '\xbb' && at_[2] == '\xbf') { 297 // UTF-8 byte order mark - treat this file as UTF-8 298 at_ += 3; 299 encoding_ = Encoding::UTF_8; 300 } 301 if (inPreprocessorDirective_) { 302 SkipCComments(); 303 } else { 304 bool mightNeedSpace{false}; 305 if (MustSkipToEndOfLine()) { 306 SkipToEndOfLine(); 307 } else { 308 mightNeedSpace = *at_ == '\n'; 309 } 310 for (; Continuation(mightNeedSpace); mightNeedSpace = false) { 311 if (MustSkipToEndOfLine()) { 312 SkipToEndOfLine(); 313 } 314 } 315 if (*at_ == '\t') { 316 tabInCurrentLine_ = true; 317 } 318 } 319 } 320 321 void Prescanner::SkipCComments() { 322 while (true) { 323 if (IsCComment(at_)) { 324 if (const char *after{SkipCComment(at_)}) { 325 column_ += after - at_; 326 // May have skipped over one or more newlines; relocate the start of 327 // the next line. 328 nextLine_ = at_ = after; 329 NextLine(); 330 } else { 331 // Don't emit any messages about unclosed C-style comments, because 332 // the sequence /* can appear legally in a FORMAT statement. There's 333 // no ambiguity, since the sequence */ cannot appear legally. 334 break; 335 } 336 } else if (inPreprocessorDirective_ && at_[0] == '\\' && at_ + 2 < limit_ && 337 at_[1] == '\n' && nextLine_ < limit_) { 338 BeginSourceLineAndAdvance(); 339 } else { 340 break; 341 } 342 } 343 } 344 345 void Prescanner::SkipSpaces() { 346 while (*at_ == ' ' || *at_ == '\t') { 347 NextChar(); 348 } 349 insertASpace_ = false; 350 } 351 352 const char *Prescanner::SkipWhiteSpace(const char *p) { 353 while (*p == ' ' || *p == '\t') { 354 ++p; 355 } 356 return p; 357 } 358 359 const char *Prescanner::SkipWhiteSpaceAndCComments(const char *p) const { 360 while (true) { 361 if (*p == ' ' || *p == '\t') { 362 ++p; 363 } else if (IsCComment(p)) { 364 if (const char *after{SkipCComment(p)}) { 365 p = after; 366 } else { 367 break; 368 } 369 } else { 370 break; 371 } 372 } 373 return p; 374 } 375 376 const char *Prescanner::SkipCComment(const char *p) const { 377 char star{' '}, slash{' '}; 378 p += 2; 379 while (star != '*' || slash != '/') { 380 if (p >= limit_) { 381 return nullptr; // signifies an unterminated comment 382 } 383 star = slash; 384 slash = *p++; 385 } 386 return p; 387 } 388 389 bool Prescanner::NextToken(TokenSequence &tokens) { 390 CHECK(at_ >= start_ && at_ < limit_); 391 if (InFixedFormSource()) { 392 SkipSpaces(); 393 } else { 394 if (*at_ == '/' && IsCComment(at_)) { 395 // Recognize and skip over classic C style /*comments*/ when 396 // outside a character literal. 397 if (features_.ShouldWarn(LanguageFeature::ClassicCComments)) { 398 Say(GetProvenance(at_), "nonstandard usage: C-style comment"_en_US); 399 } 400 SkipCComments(); 401 } 402 if (*at_ == ' ' || *at_ == '\t') { 403 // Compress free-form white space into a single space character. 404 const auto theSpace{at_}; 405 char previous{at_ <= start_ ? ' ' : at_[-1]}; 406 NextChar(); 407 SkipSpaces(); 408 if (*at_ == '\n') { 409 // Discard white space at the end of a line. 410 } else if (!inPreprocessorDirective_ && 411 (previous == '(' || *at_ == '(' || *at_ == ')')) { 412 // Discard white space before/after '(' and before ')', unless in a 413 // preprocessor directive. This helps yield space-free contiguous 414 // names for generic interfaces like OPERATOR( + ) and 415 // READ ( UNFORMATTED ), without misinterpreting #define f (notAnArg). 416 // This has the effect of silently ignoring the illegal spaces in 417 // the array constructor ( /1,2/ ) but that seems benign; it's 418 // hard to avoid that while still removing spaces from OPERATOR( / ) 419 // and OPERATOR( // ). 420 } else { 421 // Preserve the squashed white space as a single space character. 422 tokens.PutNextTokenChar(' ', GetProvenance(theSpace)); 423 tokens.CloseToken(); 424 return true; 425 } 426 } 427 } 428 if (insertASpace_) { 429 tokens.PutNextTokenChar(' ', spaceProvenance_); 430 insertASpace_ = false; 431 } 432 if (*at_ == '\n') { 433 return false; 434 } 435 const char *start{at_}; 436 if (*at_ == '\'' || *at_ == '"') { 437 QuotedCharacterLiteral(tokens, start); 438 preventHollerith_ = false; 439 } else if (IsDecimalDigit(*at_)) { 440 int n{0}, digits{0}; 441 static constexpr int maxHollerith{256 /*lines*/ * (132 - 6 /*columns*/)}; 442 do { 443 if (n < maxHollerith) { 444 n = 10 * n + DecimalDigitValue(*at_); 445 } 446 EmitCharAndAdvance(tokens, *at_); 447 ++digits; 448 if (InFixedFormSource()) { 449 SkipSpaces(); 450 } 451 } while (IsDecimalDigit(*at_)); 452 if ((*at_ == 'h' || *at_ == 'H') && n > 0 && n < maxHollerith && 453 !preventHollerith_) { 454 Hollerith(tokens, n, start); 455 } else if (*at_ == '.') { 456 while (IsDecimalDigit(EmitCharAndAdvance(tokens, *at_))) { 457 } 458 ExponentAndKind(tokens); 459 } else if (ExponentAndKind(tokens)) { 460 } else if (digits == 1 && n == 0 && (*at_ == 'x' || *at_ == 'X') && 461 inPreprocessorDirective_) { 462 do { 463 EmitCharAndAdvance(tokens, *at_); 464 } while (IsHexadecimalDigit(*at_)); 465 } else if (IsLetter(*at_)) { 466 // Handles FORMAT(3I9HHOLLERITH) by skipping over the first I so that 467 // we don't misrecognize I9HOLLERITH as an identifier in the next case. 468 EmitCharAndAdvance(tokens, *at_); 469 } else if (at_[0] == '_' && (at_[1] == '\'' || at_[1] == '"')) { 470 EmitCharAndAdvance(tokens, *at_); 471 QuotedCharacterLiteral(tokens, start); 472 } 473 preventHollerith_ = false; 474 } else if (*at_ == '.') { 475 char nch{EmitCharAndAdvance(tokens, '.')}; 476 if (!inPreprocessorDirective_ && IsDecimalDigit(nch)) { 477 while (IsDecimalDigit(EmitCharAndAdvance(tokens, *at_))) { 478 } 479 ExponentAndKind(tokens); 480 } else if (nch == '.' && EmitCharAndAdvance(tokens, '.') == '.') { 481 EmitCharAndAdvance(tokens, '.'); // variadic macro definition ellipsis 482 } 483 preventHollerith_ = false; 484 } else if (IsLegalInIdentifier(*at_)) { 485 do { 486 } while (IsLegalInIdentifier(EmitCharAndAdvance(tokens, *at_))); 487 if (*at_ == '\'' || *at_ == '"') { 488 QuotedCharacterLiteral(tokens, start); 489 preventHollerith_ = false; 490 } else { 491 // Subtle: Don't misrecognize labeled DO statement label as Hollerith 492 // when the loop control variable starts with 'H'. 493 preventHollerith_ = true; 494 } 495 } else if (*at_ == '*') { 496 if (EmitCharAndAdvance(tokens, '*') == '*') { 497 EmitCharAndAdvance(tokens, '*'); 498 } else { 499 // Subtle ambiguity: 500 // CHARACTER*2H declares H because *2 is a kind specifier 501 // DATAC/N*2H / is repeated Hollerith 502 preventHollerith_ = !slashInCurrentLine_; 503 } 504 } else { 505 char ch{*at_}; 506 if (ch == '(' || ch == '[') { 507 ++delimiterNesting_; 508 } else if ((ch == ')' || ch == ']') && delimiterNesting_ > 0) { 509 --delimiterNesting_; 510 } 511 char nch{EmitCharAndAdvance(tokens, ch)}; 512 preventHollerith_ = false; 513 if ((nch == '=' && 514 (ch == '<' || ch == '>' || ch == '/' || ch == '=' || ch == '!')) || 515 (ch == nch && 516 (ch == '/' || ch == ':' || ch == '*' || ch == '#' || ch == '&' || 517 ch == '|' || ch == '<' || ch == '>')) || 518 (ch == '=' && nch == '>')) { 519 // token comprises two characters 520 EmitCharAndAdvance(tokens, nch); 521 } else if (ch == '/') { 522 slashInCurrentLine_ = true; 523 } 524 } 525 tokens.CloseToken(); 526 return true; 527 } 528 529 bool Prescanner::ExponentAndKind(TokenSequence &tokens) { 530 char ed{ToLowerCaseLetter(*at_)}; 531 if (ed != 'e' && ed != 'd') { 532 return false; 533 } 534 EmitCharAndAdvance(tokens, ed); 535 if (*at_ == '+' || *at_ == '-') { 536 EmitCharAndAdvance(tokens, *at_); 537 } 538 while (IsDecimalDigit(*at_)) { 539 EmitCharAndAdvance(tokens, *at_); 540 } 541 if (*at_ == '_') { 542 while (IsLegalInIdentifier(EmitCharAndAdvance(tokens, *at_))) { 543 } 544 } 545 return true; 546 } 547 548 void Prescanner::QuotedCharacterLiteral( 549 TokenSequence &tokens, const char *start) { 550 char quote{*at_}; 551 const char *end{at_ + 1}; 552 inCharLiteral_ = true; 553 const auto emit{[&](char ch) { EmitChar(tokens, ch); }}; 554 const auto insert{[&](char ch) { EmitInsertedChar(tokens, ch); }}; 555 bool isEscaped{false}; 556 bool escapesEnabled{features_.IsEnabled(LanguageFeature::BackslashEscapes)}; 557 while (true) { 558 if (*at_ == '\\') { 559 if (escapesEnabled) { 560 isEscaped = !isEscaped; 561 } else { 562 // The parser always processes escape sequences, so don't confuse it 563 // when escapes are disabled. 564 insert('\\'); 565 } 566 } else { 567 isEscaped = false; 568 } 569 EmitQuotedChar(static_cast<unsigned char>(*at_), emit, insert, false, 570 Encoding::LATIN_1); 571 while (PadOutCharacterLiteral(tokens)) { 572 } 573 if (*at_ == '\n') { 574 if (!inPreprocessorDirective_) { 575 Say(GetProvenanceRange(start, end), 576 "Incomplete character literal"_err_en_US); 577 } 578 break; 579 } 580 end = at_ + 1; 581 NextChar(); 582 if (*at_ == quote && !isEscaped) { 583 // A doubled unescaped quote mark becomes a single instance of that 584 // quote character in the literal (later). There can be spaces between 585 // the quotes in fixed form source. 586 EmitChar(tokens, quote); 587 inCharLiteral_ = false; // for cases like print *, '...'!comment 588 NextChar(); 589 if (InFixedFormSource()) { 590 SkipSpaces(); 591 } 592 if (*at_ != quote) { 593 break; 594 } 595 inCharLiteral_ = true; 596 } 597 } 598 inCharLiteral_ = false; 599 } 600 601 void Prescanner::Hollerith( 602 TokenSequence &tokens, int count, const char *start) { 603 inCharLiteral_ = true; 604 CHECK(*at_ == 'h' || *at_ == 'H'); 605 EmitChar(tokens, 'H'); 606 while (count-- > 0) { 607 if (PadOutCharacterLiteral(tokens)) { 608 } else if (*at_ == '\n') { 609 Say(GetProvenanceRange(start, at_), 610 "Possible truncated Hollerith literal"_en_US); 611 break; 612 } else { 613 NextChar(); 614 // Each multi-byte character encoding counts as a single character. 615 // No escape sequences are recognized. 616 // Hollerith is always emitted to the cooked character 617 // stream in UTF-8. 618 DecodedCharacter decoded{DecodeCharacter( 619 encoding_, at_, static_cast<std::size_t>(limit_ - at_), false)}; 620 if (decoded.bytes > 0) { 621 EncodedCharacter utf8{ 622 EncodeCharacter<Encoding::UTF_8>(decoded.codepoint)}; 623 for (int j{0}; j < utf8.bytes; ++j) { 624 EmitChar(tokens, utf8.buffer[j]); 625 } 626 at_ += decoded.bytes - 1; 627 } else { 628 Say(GetProvenanceRange(start, at_), 629 "Bad character in Hollerith literal"_err_en_US); 630 break; 631 } 632 } 633 } 634 if (*at_ != '\n') { 635 NextChar(); 636 } 637 inCharLiteral_ = false; 638 } 639 640 // In fixed form, source card images must be processed as if they were at 641 // least 72 columns wide, at least in character literal contexts. 642 bool Prescanner::PadOutCharacterLiteral(TokenSequence &tokens) { 643 while (inFixedForm_ && !tabInCurrentLine_ && at_[1] == '\n') { 644 if (column_ < fixedFormColumnLimit_) { 645 tokens.PutNextTokenChar(' ', spaceProvenance_); 646 ++column_; 647 return true; 648 } 649 if (!FixedFormContinuation(false /*no need to insert space*/) || 650 tabInCurrentLine_) { 651 return false; 652 } 653 CHECK(column_ == 7); 654 --at_; // point to column 6 of continuation line 655 column_ = 6; 656 } 657 return false; 658 } 659 660 bool Prescanner::IsFixedFormCommentLine(const char *start) const { 661 const char *p{start}; 662 if (IsFixedFormCommentChar(*p) || *p == '%' || // VAX %list, %eject, &c. 663 ((*p == 'D' || *p == 'd') && 664 !features_.IsEnabled(LanguageFeature::OldDebugLines))) { 665 return true; 666 } 667 bool anyTabs{false}; 668 while (true) { 669 if (*p == ' ') { 670 ++p; 671 } else if (*p == '\t') { 672 anyTabs = true; 673 ++p; 674 } else if (*p == '0' && !anyTabs && p == start + 5) { 675 ++p; // 0 in column 6 must treated as a space 676 } else { 677 break; 678 } 679 } 680 if (!anyTabs && p >= start + fixedFormColumnLimit_) { 681 return true; 682 } 683 if (*p == '!' && !inCharLiteral_ && (anyTabs || p != start + 5)) { 684 return true; 685 } 686 return *p == '\n'; 687 } 688 689 const char *Prescanner::IsFreeFormComment(const char *p) const { 690 p = SkipWhiteSpaceAndCComments(p); 691 if (*p == '!' || *p == '\n') { 692 return p; 693 } else { 694 return nullptr; 695 } 696 } 697 698 std::optional<std::size_t> Prescanner::IsIncludeLine(const char *start) const { 699 const char *p{SkipWhiteSpace(start)}; 700 for (char ch : "include"s) { 701 if (ToLowerCaseLetter(*p++) != ch) { 702 return std::nullopt; 703 } 704 } 705 p = SkipWhiteSpace(p); 706 if (*p == '"' || *p == '\'') { 707 return {p - start}; 708 } 709 return std::nullopt; 710 } 711 712 void Prescanner::FortranInclude(const char *firstQuote) { 713 const char *p{firstQuote}; 714 while (*p != '"' && *p != '\'') { 715 ++p; 716 } 717 char quote{*p}; 718 std::string path; 719 for (++p; *p != '\n'; ++p) { 720 if (*p == quote) { 721 if (p[1] != quote) { 722 break; 723 } 724 ++p; 725 } 726 path += *p; 727 } 728 if (*p != quote) { 729 Say(GetProvenanceRange(firstQuote, p), 730 "malformed path name string"_err_en_US); 731 return; 732 } 733 p = SkipWhiteSpace(p + 1); 734 if (*p != '\n' && *p != '!') { 735 const char *garbage{p}; 736 for (; *p != '\n' && *p != '!'; ++p) { 737 } 738 Say(GetProvenanceRange(garbage, p), 739 "excess characters after path name"_en_US); 740 } 741 std::string buf; 742 llvm::raw_string_ostream error{buf}; 743 Provenance provenance{GetProvenance(nextLine_)}; 744 AllSources &allSources{cooked_.allSources()}; 745 const SourceFile *currentFile{allSources.GetSourceFile(provenance)}; 746 if (currentFile) { 747 allSources.PushSearchPathDirectory(DirectoryName(currentFile->path())); 748 } 749 const SourceFile *included{allSources.Open(path, error)}; 750 if (currentFile) { 751 allSources.PopSearchPathDirectory(); 752 } 753 if (!included) { 754 Say(provenance, "INCLUDE: %s"_err_en_US, error.str()); 755 } else if (included->bytes() > 0) { 756 ProvenanceRange includeLineRange{ 757 provenance, static_cast<std::size_t>(p - nextLine_)}; 758 ProvenanceRange fileRange{ 759 allSources.AddIncludedFile(*included, includeLineRange)}; 760 Prescanner{*this}.set_encoding(included->encoding()).Prescan(fileRange); 761 } 762 } 763 764 const char *Prescanner::IsPreprocessorDirectiveLine(const char *start) const { 765 const char *p{start}; 766 for (; *p == ' '; ++p) { 767 } 768 if (*p == '#') { 769 if (inFixedForm_ && p == start + 5) { 770 return nullptr; 771 } 772 } else { 773 p = SkipWhiteSpace(p); 774 if (*p != '#') { 775 return nullptr; 776 } 777 } 778 return SkipWhiteSpace(p + 1); 779 } 780 781 bool Prescanner::IsNextLinePreprocessorDirective() const { 782 return IsPreprocessorDirectiveLine(nextLine_) != nullptr; 783 } 784 785 bool Prescanner::SkipCommentLine(bool afterAmpersand) { 786 if (nextLine_ >= limit_) { 787 if (afterAmpersand && prescannerNesting_ > 0) { 788 // A continuation marker at the end of the last line in an 789 // include file inhibits the newline for that line. 790 SkipToEndOfLine(); 791 omitNewline_ = true; 792 } 793 return false; 794 } 795 auto lineClass{ClassifyLine(nextLine_)}; 796 if (lineClass.kind == LineClassification::Kind::Comment) { 797 NextLine(); 798 return true; 799 } else if (inPreprocessorDirective_) { 800 return false; 801 } else if (lineClass.kind == 802 LineClassification::Kind::ConditionalCompilationDirective || 803 lineClass.kind == LineClassification::Kind::PreprocessorDirective) { 804 // Allow conditional compilation directives (e.g., #ifdef) to affect 805 // continuation lines. 806 // Allow other preprocessor directives, too, except #include 807 // (when it does not follow '&'), #define, and #undef (because 808 // they cannot be allowed to affect preceding text on a 809 // continued line). 810 preprocessor_.Directive(TokenizePreprocessorDirective(), this); 811 return true; 812 } else if (afterAmpersand && 813 (lineClass.kind == LineClassification::Kind::IncludeDirective || 814 lineClass.kind == LineClassification::Kind::IncludeLine)) { 815 SkipToEndOfLine(); 816 omitNewline_ = true; 817 skipLeadingAmpersand_ = true; 818 return false; 819 } else { 820 return false; 821 } 822 } 823 824 const char *Prescanner::FixedFormContinuationLine(bool mightNeedSpace) { 825 if (nextLine_ >= limit_) { 826 return nullptr; 827 } 828 tabInCurrentLine_ = false; 829 char col1{*nextLine_}; 830 if (InCompilerDirective()) { 831 // Must be a continued compiler directive. 832 if (!IsFixedFormCommentChar(col1)) { 833 return nullptr; 834 } 835 int j{1}; 836 for (; j < 5; ++j) { 837 char ch{directiveSentinel_[j - 1]}; 838 if (ch == '\0') { 839 break; 840 } 841 if (ch != ToLowerCaseLetter(nextLine_[j])) { 842 return nullptr; 843 } 844 } 845 for (; j < 5; ++j) { 846 if (nextLine_[j] != ' ') { 847 return nullptr; 848 } 849 } 850 char col6{nextLine_[5]}; 851 if (col6 != '\n' && col6 != '\t' && col6 != ' ' && col6 != '0') { 852 if (nextLine_[6] != ' ' && mightNeedSpace) { 853 insertASpace_ = true; 854 } 855 return nextLine_ + 6; 856 } 857 return nullptr; 858 } else { 859 // Normal case: not in a compiler directive. 860 if (col1 == '&' && 861 features_.IsEnabled( 862 LanguageFeature::FixedFormContinuationWithColumn1Ampersand)) { 863 // Extension: '&' as continuation marker 864 if (features_.ShouldWarn( 865 LanguageFeature::FixedFormContinuationWithColumn1Ampersand)) { 866 Say(GetProvenance(nextLine_), "nonstandard usage"_en_US); 867 } 868 return nextLine_ + 1; 869 } 870 if (col1 == '\t' && nextLine_[1] >= '1' && nextLine_[1] <= '9') { 871 tabInCurrentLine_ = true; 872 return nextLine_ + 2; // VAX extension 873 } 874 if (col1 == ' ' && nextLine_[1] == ' ' && nextLine_[2] == ' ' && 875 nextLine_[3] == ' ' && nextLine_[4] == ' ') { 876 char col6{nextLine_[5]}; 877 if (col6 != '\n' && col6 != '\t' && col6 != ' ' && col6 != '0') { 878 return nextLine_ + 6; 879 } 880 } 881 if (delimiterNesting_ > 0) { 882 if (!IsFixedFormCommentChar(col1)) { 883 return nextLine_; 884 } 885 } 886 } 887 return nullptr; // not a continuation line 888 } 889 890 const char *Prescanner::FreeFormContinuationLine(bool ampersand) { 891 const char *p{nextLine_}; 892 if (p >= limit_) { 893 return nullptr; 894 } 895 p = SkipWhiteSpace(p); 896 if (InCompilerDirective()) { 897 if (*p++ != '!') { 898 return nullptr; 899 } 900 for (const char *s{directiveSentinel_}; *s != '\0'; ++p, ++s) { 901 if (*s != ToLowerCaseLetter(*p)) { 902 return nullptr; 903 } 904 } 905 p = SkipWhiteSpace(p); 906 if (*p == '&') { 907 if (!ampersand) { 908 insertASpace_ = true; 909 } 910 return p + 1; 911 } else if (ampersand) { 912 return p; 913 } else { 914 return nullptr; 915 } 916 } else { 917 if (*p == '&') { 918 return p + 1; 919 } else if (*p == '!' || *p == '\n' || *p == '#') { 920 return nullptr; 921 } else if (ampersand || delimiterNesting_ > 0) { 922 if (p > nextLine_) { 923 --p; 924 } else { 925 insertASpace_ = true; 926 } 927 return p; 928 } else { 929 return nullptr; 930 } 931 } 932 } 933 934 bool Prescanner::FixedFormContinuation(bool mightNeedSpace) { 935 // N.B. We accept '&' as a continuation indicator in fixed form, too, 936 // but not in a character literal. 937 if (*at_ == '&' && inCharLiteral_) { 938 return false; 939 } 940 do { 941 if (const char *cont{FixedFormContinuationLine(mightNeedSpace)}) { 942 BeginSourceLine(cont); 943 column_ = 7; 944 NextLine(); 945 return true; 946 } 947 } while (SkipCommentLine(false /* not after ampersand */)); 948 return false; 949 } 950 951 bool Prescanner::FreeFormContinuation() { 952 const char *p{at_}; 953 bool ampersand{*p == '&'}; 954 if (ampersand) { 955 p = SkipWhiteSpace(p + 1); 956 } 957 if (*p != '\n') { 958 if (inCharLiteral_) { 959 return false; 960 } else if (*p != '!' && 961 features_.ShouldWarn(LanguageFeature::CruftAfterAmpersand)) { 962 Say(GetProvenance(p), "missing ! before comment after &"_en_US); 963 } 964 } 965 do { 966 if (const char *cont{FreeFormContinuationLine(ampersand)}) { 967 BeginSourceLine(cont); 968 NextLine(); 969 return true; 970 } 971 } while (SkipCommentLine(ampersand)); 972 return false; 973 } 974 975 bool Prescanner::Continuation(bool mightNeedFixedFormSpace) { 976 if (*at_ == '\n' || *at_ == '&') { 977 if (inFixedForm_) { 978 return FixedFormContinuation(mightNeedFixedFormSpace); 979 } else { 980 return FreeFormContinuation(); 981 } 982 } else { 983 return false; 984 } 985 } 986 987 std::optional<Prescanner::LineClassification> 988 Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const { 989 const char *p{start}; 990 char col1{*p++}; 991 if (!IsFixedFormCommentChar(col1)) { 992 return std::nullopt; 993 } 994 char sentinel[5], *sp{sentinel}; 995 int column{2}; 996 for (; column < 6; ++column, ++p) { 997 if (*p != ' ') { 998 if (*p == '\n' || *p == '\t') { 999 break; 1000 } 1001 if (sp == sentinel + 1 && sentinel[0] == '$' && IsDecimalDigit(*p)) { 1002 // OpenMP conditional compilation line: leave the label alone 1003 break; 1004 } 1005 *sp++ = ToLowerCaseLetter(*p); 1006 } 1007 } 1008 if (column == 6) { 1009 if (*p == ' ' || *p == '\t' || *p == '0') { 1010 ++p; 1011 } else { 1012 // This is a Continuation line, not an initial directive line. 1013 return std::nullopt; 1014 } 1015 } 1016 if (sp == sentinel) { 1017 return std::nullopt; 1018 } 1019 *sp = '\0'; 1020 if (const char *ss{IsCompilerDirectiveSentinel(sentinel)}) { 1021 std::size_t payloadOffset = p - start; 1022 return {LineClassification{ 1023 LineClassification::Kind::CompilerDirective, payloadOffset, ss}}; 1024 } 1025 return std::nullopt; 1026 } 1027 1028 std::optional<Prescanner::LineClassification> 1029 Prescanner::IsFreeFormCompilerDirectiveLine(const char *start) const { 1030 char sentinel[8]; 1031 const char *p{SkipWhiteSpace(start)}; 1032 if (*p++ != '!') { 1033 return std::nullopt; 1034 } 1035 for (std::size_t j{0}; j + 1 < sizeof sentinel; ++p, ++j) { 1036 if (*p == '\n') { 1037 break; 1038 } 1039 if (*p == ' ' || *p == '\t' || *p == '&') { 1040 if (j == 0) { 1041 break; 1042 } 1043 sentinel[j] = '\0'; 1044 p = SkipWhiteSpace(p + 1); 1045 if (*p == '!') { 1046 break; 1047 } 1048 if (const char *sp{IsCompilerDirectiveSentinel(sentinel)}) { 1049 std::size_t offset = p - start; 1050 return {LineClassification{ 1051 LineClassification::Kind::CompilerDirective, offset, sp}}; 1052 } 1053 break; 1054 } 1055 sentinel[j] = ToLowerCaseLetter(*p); 1056 } 1057 return std::nullopt; 1058 } 1059 1060 Prescanner &Prescanner::AddCompilerDirectiveSentinel(const std::string &dir) { 1061 std::uint64_t packed{0}; 1062 for (char ch : dir) { 1063 packed = (packed << 8) | (ToLowerCaseLetter(ch) & 0xff); 1064 } 1065 compilerDirectiveBloomFilter_.set(packed % prime1); 1066 compilerDirectiveBloomFilter_.set(packed % prime2); 1067 compilerDirectiveSentinels_.insert(dir); 1068 return *this; 1069 } 1070 1071 const char *Prescanner::IsCompilerDirectiveSentinel( 1072 const char *sentinel) const { 1073 std::uint64_t packed{0}; 1074 std::size_t n{0}; 1075 for (; sentinel[n] != '\0'; ++n) { 1076 packed = (packed << 8) | (sentinel[n] & 0xff); 1077 } 1078 if (n == 0 || !compilerDirectiveBloomFilter_.test(packed % prime1) || 1079 !compilerDirectiveBloomFilter_.test(packed % prime2)) { 1080 return nullptr; 1081 } 1082 const auto iter{compilerDirectiveSentinels_.find(std::string(sentinel, n))}; 1083 return iter == compilerDirectiveSentinels_.end() ? nullptr : iter->c_str(); 1084 } 1085 1086 Prescanner::LineClassification Prescanner::ClassifyLine( 1087 const char *start) const { 1088 if (inFixedForm_) { 1089 if (std::optional<LineClassification> lc{ 1090 IsFixedFormCompilerDirectiveLine(start)}) { 1091 return std::move(*lc); 1092 } 1093 if (IsFixedFormCommentLine(start)) { 1094 return {LineClassification::Kind::Comment}; 1095 } 1096 } else { 1097 if (std::optional<LineClassification> lc{ 1098 IsFreeFormCompilerDirectiveLine(start)}) { 1099 return std::move(*lc); 1100 } 1101 if (const char *bang{IsFreeFormComment(start)}) { 1102 return {LineClassification::Kind::Comment, 1103 static_cast<std::size_t>(bang - start)}; 1104 } 1105 } 1106 if (std::optional<std::size_t> quoteOffset{IsIncludeLine(start)}) { 1107 return {LineClassification::Kind::IncludeLine, *quoteOffset}; 1108 } 1109 if (const char *dir{IsPreprocessorDirectiveLine(start)}) { 1110 if (std::memcmp(dir, "if", 2) == 0 || std::memcmp(dir, "elif", 4) == 0 || 1111 std::memcmp(dir, "else", 4) == 0 || std::memcmp(dir, "endif", 5) == 0) { 1112 return {LineClassification::Kind::ConditionalCompilationDirective}; 1113 } else if (std::memcmp(dir, "include", 7) == 0) { 1114 return {LineClassification::Kind::IncludeDirective}; 1115 } else if (std::memcmp(dir, "define", 6) == 0 || 1116 std::memcmp(dir, "undef", 5) == 0) { 1117 return {LineClassification::Kind::DefinitionDirective}; 1118 } else { 1119 return {LineClassification::Kind::PreprocessorDirective}; 1120 } 1121 } 1122 return {LineClassification::Kind::Source}; 1123 } 1124 1125 void Prescanner::SourceFormChange(std::string &&dir) { 1126 if (dir == "!dir$ free") { 1127 inFixedForm_ = false; 1128 } else if (dir == "!dir$ fixed") { 1129 inFixedForm_ = true; 1130 } 1131 } 1132 } // namespace Fortran::parser 1133