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 BeginStatementAndAdvance(); 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 BeginStatementAndAdvance(); 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 BeginStatementAndAdvance(); 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 const char *bad{nullptr}; 250 for (; *at_ != '\n' && column_ <= 6; ++at_) { 251 if (*at_ == '\t') { 252 ++at_; 253 column_ = 7; 254 break; 255 } 256 if (*at_ != ' ' && 257 !(*at_ == '0' && column_ == 6)) { // '0' in column 6 becomes space 258 EmitChar(token, *at_); 259 if (!bad && !IsDecimalDigit(*at_)) { 260 bad = at_; 261 } 262 ++outCol; 263 } 264 ++column_; 265 } 266 if (outCol > 1) { 267 if (bad && !preprocessor_.IsNameDefined(token.CurrentOpenToken())) { 268 Say(GetProvenance(bad), 269 "Character in fixed-form label field must be a digit"_en_US); 270 } 271 token.CloseToken(); 272 } 273 SkipToNextSignificantCharacter(); 274 if (IsDecimalDigit(*at_)) { 275 Say(GetProvenance(at_), 276 "Label digit is not in fixed-form label field"_en_US); 277 } 278 } 279 280 void Prescanner::SkipToEndOfLine() { 281 while (*at_ != '\n') { 282 ++at_, ++column_; 283 } 284 } 285 286 bool Prescanner::MustSkipToEndOfLine() const { 287 if (inFixedForm_ && column_ > fixedFormColumnLimit_ && !tabInCurrentLine_) { 288 return true; // skip over ignored columns in right margin (73:80) 289 } else if (*at_ == '!' && !inCharLiteral_) { 290 return true; // inline comment goes to end of source line 291 } else { 292 return false; 293 } 294 } 295 296 void Prescanner::NextChar() { 297 CHECK(*at_ != '\n'); 298 ++at_, ++column_; 299 while (at_[0] == '\xef' && at_[1] == '\xbb' && at_[2] == '\xbf') { 300 // UTF-8 byte order mark - treat this file as UTF-8 301 at_ += 3; 302 encoding_ = Encoding::UTF_8; 303 } 304 SkipToNextSignificantCharacter(); 305 } 306 307 // Skip everything that should be ignored until the next significant 308 // character is reached; handles C-style comments in preprocessing 309 // directives, Fortran ! comments, stuff after the right margin in 310 // fixed form, and all forms of line continuation. 311 void Prescanner::SkipToNextSignificantCharacter() { 312 if (inPreprocessorDirective_) { 313 SkipCComments(); 314 } else { 315 bool mightNeedSpace{false}; 316 if (MustSkipToEndOfLine()) { 317 SkipToEndOfLine(); 318 } else { 319 mightNeedSpace = *at_ == '\n'; 320 } 321 for (; Continuation(mightNeedSpace); mightNeedSpace = false) { 322 if (MustSkipToEndOfLine()) { 323 SkipToEndOfLine(); 324 } 325 } 326 if (*at_ == '\t') { 327 tabInCurrentLine_ = true; 328 } 329 } 330 } 331 332 void Prescanner::SkipCComments() { 333 while (true) { 334 if (IsCComment(at_)) { 335 if (const char *after{SkipCComment(at_)}) { 336 column_ += after - at_; 337 // May have skipped over one or more newlines; relocate the start of 338 // the next line. 339 nextLine_ = at_ = after; 340 NextLine(); 341 } else { 342 // Don't emit any messages about unclosed C-style comments, because 343 // the sequence /* can appear legally in a FORMAT statement. There's 344 // no ambiguity, since the sequence */ cannot appear legally. 345 break; 346 } 347 } else if (inPreprocessorDirective_ && at_[0] == '\\' && at_ + 2 < limit_ && 348 at_[1] == '\n' && nextLine_ < limit_) { 349 BeginSourceLineAndAdvance(); 350 } else { 351 break; 352 } 353 } 354 } 355 356 void Prescanner::SkipSpaces() { 357 while (*at_ == ' ' || *at_ == '\t') { 358 NextChar(); 359 } 360 insertASpace_ = false; 361 } 362 363 const char *Prescanner::SkipWhiteSpace(const char *p) { 364 while (*p == ' ' || *p == '\t') { 365 ++p; 366 } 367 return p; 368 } 369 370 const char *Prescanner::SkipWhiteSpaceAndCComments(const char *p) const { 371 while (true) { 372 if (*p == ' ' || *p == '\t') { 373 ++p; 374 } else if (IsCComment(p)) { 375 if (const char *after{SkipCComment(p)}) { 376 p = after; 377 } else { 378 break; 379 } 380 } else { 381 break; 382 } 383 } 384 return p; 385 } 386 387 const char *Prescanner::SkipCComment(const char *p) const { 388 char star{' '}, slash{' '}; 389 p += 2; 390 while (star != '*' || slash != '/') { 391 if (p >= limit_) { 392 return nullptr; // signifies an unterminated comment 393 } 394 star = slash; 395 slash = *p++; 396 } 397 return p; 398 } 399 400 bool Prescanner::NextToken(TokenSequence &tokens) { 401 CHECK(at_ >= start_ && at_ < limit_); 402 if (InFixedFormSource()) { 403 SkipSpaces(); 404 } else { 405 if (*at_ == '/' && IsCComment(at_)) { 406 // Recognize and skip over classic C style /*comments*/ when 407 // outside a character literal. 408 if (features_.ShouldWarn(LanguageFeature::ClassicCComments)) { 409 Say(GetProvenance(at_), "nonstandard usage: C-style comment"_en_US); 410 } 411 SkipCComments(); 412 } 413 if (*at_ == ' ' || *at_ == '\t') { 414 // Compress free-form white space into a single space character. 415 const auto theSpace{at_}; 416 char previous{at_ <= start_ ? ' ' : at_[-1]}; 417 NextChar(); 418 SkipSpaces(); 419 if (*at_ == '\n') { 420 // Discard white space at the end of a line. 421 } else if (!inPreprocessorDirective_ && 422 (previous == '(' || *at_ == '(' || *at_ == ')')) { 423 // Discard white space before/after '(' and before ')', unless in a 424 // preprocessor directive. This helps yield space-free contiguous 425 // names for generic interfaces like OPERATOR( + ) and 426 // READ ( UNFORMATTED ), without misinterpreting #define f (notAnArg). 427 // This has the effect of silently ignoring the illegal spaces in 428 // the array constructor ( /1,2/ ) but that seems benign; it's 429 // hard to avoid that while still removing spaces from OPERATOR( / ) 430 // and OPERATOR( // ). 431 } else { 432 // Preserve the squashed white space as a single space character. 433 tokens.PutNextTokenChar(' ', GetProvenance(theSpace)); 434 tokens.CloseToken(); 435 return true; 436 } 437 } 438 } 439 if (insertASpace_) { 440 tokens.PutNextTokenChar(' ', spaceProvenance_); 441 insertASpace_ = false; 442 } 443 if (*at_ == '\n') { 444 return false; 445 } 446 const char *start{at_}; 447 if (*at_ == '\'' || *at_ == '"') { 448 QuotedCharacterLiteral(tokens, start); 449 preventHollerith_ = false; 450 } else if (IsDecimalDigit(*at_)) { 451 int n{0}, digits{0}; 452 static constexpr int maxHollerith{256 /*lines*/ * (132 - 6 /*columns*/)}; 453 do { 454 if (n < maxHollerith) { 455 n = 10 * n + DecimalDigitValue(*at_); 456 } 457 EmitCharAndAdvance(tokens, *at_); 458 ++digits; 459 if (InFixedFormSource()) { 460 SkipSpaces(); 461 } 462 } while (IsDecimalDigit(*at_)); 463 if ((*at_ == 'h' || *at_ == 'H') && n > 0 && n < maxHollerith && 464 !preventHollerith_) { 465 Hollerith(tokens, n, start); 466 } else if (*at_ == '.') { 467 while (IsDecimalDigit(EmitCharAndAdvance(tokens, *at_))) { 468 } 469 ExponentAndKind(tokens); 470 } else if (ExponentAndKind(tokens)) { 471 } else if (digits == 1 && n == 0 && (*at_ == 'x' || *at_ == 'X') && 472 inPreprocessorDirective_) { 473 do { 474 EmitCharAndAdvance(tokens, *at_); 475 } while (IsHexadecimalDigit(*at_)); 476 } else if (IsLetter(*at_)) { 477 // Handles FORMAT(3I9HHOLLERITH) by skipping over the first I so that 478 // we don't misrecognize I9HOLLERITH as an identifier in the next case. 479 EmitCharAndAdvance(tokens, *at_); 480 } else if (at_[0] == '_' && (at_[1] == '\'' || at_[1] == '"')) { 481 EmitCharAndAdvance(tokens, *at_); 482 QuotedCharacterLiteral(tokens, start); 483 } 484 preventHollerith_ = false; 485 } else if (*at_ == '.') { 486 char nch{EmitCharAndAdvance(tokens, '.')}; 487 if (!inPreprocessorDirective_ && IsDecimalDigit(nch)) { 488 while (IsDecimalDigit(EmitCharAndAdvance(tokens, *at_))) { 489 } 490 ExponentAndKind(tokens); 491 } else if (nch == '.' && EmitCharAndAdvance(tokens, '.') == '.') { 492 EmitCharAndAdvance(tokens, '.'); // variadic macro definition ellipsis 493 } 494 preventHollerith_ = false; 495 } else if (IsLegalInIdentifier(*at_)) { 496 do { 497 } while (IsLegalInIdentifier(EmitCharAndAdvance(tokens, *at_))); 498 if (*at_ == '\'' || *at_ == '"') { 499 QuotedCharacterLiteral(tokens, start); 500 } 501 preventHollerith_ = false; 502 } else if (*at_ == '*') { 503 if (EmitCharAndAdvance(tokens, '*') == '*') { 504 EmitCharAndAdvance(tokens, '*'); 505 } else { 506 // Subtle ambiguity: 507 // CHARACTER*2H declares H because *2 is a kind specifier 508 // DATAC/N*2H / is repeated Hollerith 509 preventHollerith_ = !slashInCurrentStatement_; 510 } 511 } else { 512 char ch{*at_}; 513 if (ch == '(' || ch == '[') { 514 ++delimiterNesting_; 515 } else if ((ch == ')' || ch == ']') && delimiterNesting_ > 0) { 516 --delimiterNesting_; 517 } 518 char nch{EmitCharAndAdvance(tokens, ch)}; 519 preventHollerith_ = false; 520 if ((nch == '=' && 521 (ch == '<' || ch == '>' || ch == '/' || ch == '=' || ch == '!')) || 522 (ch == nch && 523 (ch == '/' || ch == ':' || ch == '*' || ch == '#' || ch == '&' || 524 ch == '|' || ch == '<' || ch == '>')) || 525 (ch == '=' && nch == '>')) { 526 // token comprises two characters 527 EmitCharAndAdvance(tokens, nch); 528 } else if (ch == '/') { 529 slashInCurrentStatement_ = true; 530 } 531 } 532 tokens.CloseToken(); 533 return true; 534 } 535 536 bool Prescanner::ExponentAndKind(TokenSequence &tokens) { 537 char ed{ToLowerCaseLetter(*at_)}; 538 if (ed != 'e' && ed != 'd') { 539 return false; 540 } 541 EmitCharAndAdvance(tokens, ed); 542 if (*at_ == '+' || *at_ == '-') { 543 EmitCharAndAdvance(tokens, *at_); 544 } 545 while (IsDecimalDigit(*at_)) { 546 EmitCharAndAdvance(tokens, *at_); 547 } 548 if (*at_ == '_') { 549 while (IsLegalInIdentifier(EmitCharAndAdvance(tokens, *at_))) { 550 } 551 } 552 return true; 553 } 554 555 void Prescanner::QuotedCharacterLiteral( 556 TokenSequence &tokens, const char *start) { 557 char quote{*at_}; 558 const char *end{at_ + 1}; 559 inCharLiteral_ = true; 560 const auto emit{[&](char ch) { EmitChar(tokens, ch); }}; 561 const auto insert{[&](char ch) { EmitInsertedChar(tokens, ch); }}; 562 bool isEscaped{false}; 563 bool escapesEnabled{features_.IsEnabled(LanguageFeature::BackslashEscapes)}; 564 while (true) { 565 if (*at_ == '\\') { 566 if (escapesEnabled) { 567 isEscaped = !isEscaped; 568 } else { 569 // The parser always processes escape sequences, so don't confuse it 570 // when escapes are disabled. 571 insert('\\'); 572 } 573 } else { 574 isEscaped = false; 575 } 576 EmitQuotedChar(static_cast<unsigned char>(*at_), emit, insert, false, 577 Encoding::LATIN_1); 578 while (PadOutCharacterLiteral(tokens)) { 579 } 580 if (*at_ == '\n') { 581 if (!inPreprocessorDirective_) { 582 Say(GetProvenanceRange(start, end), 583 "Incomplete character literal"_err_en_US); 584 } 585 break; 586 } 587 end = at_ + 1; 588 NextChar(); 589 if (*at_ == quote && !isEscaped) { 590 // A doubled unescaped quote mark becomes a single instance of that 591 // quote character in the literal (later). There can be spaces between 592 // the quotes in fixed form source. 593 EmitChar(tokens, quote); 594 inCharLiteral_ = false; // for cases like print *, '...'!comment 595 NextChar(); 596 if (InFixedFormSource()) { 597 SkipSpaces(); 598 } 599 if (*at_ != quote) { 600 break; 601 } 602 inCharLiteral_ = true; 603 } 604 } 605 inCharLiteral_ = false; 606 } 607 608 void Prescanner::Hollerith( 609 TokenSequence &tokens, int count, const char *start) { 610 inCharLiteral_ = true; 611 CHECK(*at_ == 'h' || *at_ == 'H'); 612 EmitChar(tokens, 'H'); 613 while (count-- > 0) { 614 if (PadOutCharacterLiteral(tokens)) { 615 } else if (*at_ == '\n') { 616 Say(GetProvenanceRange(start, at_), 617 "Possible truncated Hollerith literal"_en_US); 618 break; 619 } else { 620 NextChar(); 621 // Each multi-byte character encoding counts as a single character. 622 // No escape sequences are recognized. 623 // Hollerith is always emitted to the cooked character 624 // stream in UTF-8. 625 DecodedCharacter decoded{DecodeCharacter( 626 encoding_, at_, static_cast<std::size_t>(limit_ - at_), false)}; 627 if (decoded.bytes > 0) { 628 EncodedCharacter utf8{ 629 EncodeCharacter<Encoding::UTF_8>(decoded.codepoint)}; 630 for (int j{0}; j < utf8.bytes; ++j) { 631 EmitChar(tokens, utf8.buffer[j]); 632 } 633 at_ += decoded.bytes - 1; 634 } else { 635 Say(GetProvenanceRange(start, at_), 636 "Bad character in Hollerith literal"_err_en_US); 637 break; 638 } 639 } 640 } 641 if (*at_ != '\n') { 642 NextChar(); 643 } 644 inCharLiteral_ = false; 645 } 646 647 // In fixed form, source card images must be processed as if they were at 648 // least 72 columns wide, at least in character literal contexts. 649 bool Prescanner::PadOutCharacterLiteral(TokenSequence &tokens) { 650 while (inFixedForm_ && !tabInCurrentLine_ && at_[1] == '\n') { 651 if (column_ < fixedFormColumnLimit_) { 652 tokens.PutNextTokenChar(' ', spaceProvenance_); 653 ++column_; 654 return true; 655 } 656 if (!FixedFormContinuation(false /*no need to insert space*/) || 657 tabInCurrentLine_) { 658 return false; 659 } 660 CHECK(column_ == 7); 661 --at_; // point to column 6 of continuation line 662 column_ = 6; 663 } 664 return false; 665 } 666 667 bool Prescanner::IsFixedFormCommentLine(const char *start) const { 668 const char *p{start}; 669 if (IsFixedFormCommentChar(*p) || *p == '%' || // VAX %list, %eject, &c. 670 ((*p == 'D' || *p == 'd') && 671 !features_.IsEnabled(LanguageFeature::OldDebugLines))) { 672 return true; 673 } 674 bool anyTabs{false}; 675 while (true) { 676 if (*p == ' ') { 677 ++p; 678 } else if (*p == '\t') { 679 anyTabs = true; 680 ++p; 681 } else if (*p == '0' && !anyTabs && p == start + 5) { 682 ++p; // 0 in column 6 must treated as a space 683 } else { 684 break; 685 } 686 } 687 if (!anyTabs && p >= start + fixedFormColumnLimit_) { 688 return true; 689 } 690 if (*p == '!' && !inCharLiteral_ && (anyTabs || p != start + 5)) { 691 return true; 692 } 693 return *p == '\n'; 694 } 695 696 const char *Prescanner::IsFreeFormComment(const char *p) const { 697 p = SkipWhiteSpaceAndCComments(p); 698 if (*p == '!' || *p == '\n') { 699 return p; 700 } else { 701 return nullptr; 702 } 703 } 704 705 std::optional<std::size_t> Prescanner::IsIncludeLine(const char *start) const { 706 const char *p{SkipWhiteSpace(start)}; 707 for (char ch : "include"s) { 708 if (ToLowerCaseLetter(*p++) != ch) { 709 return std::nullopt; 710 } 711 } 712 p = SkipWhiteSpace(p); 713 if (*p == '"' || *p == '\'') { 714 return {p - start}; 715 } 716 return std::nullopt; 717 } 718 719 void Prescanner::FortranInclude(const char *firstQuote) { 720 const char *p{firstQuote}; 721 while (*p != '"' && *p != '\'') { 722 ++p; 723 } 724 char quote{*p}; 725 std::string path; 726 for (++p; *p != '\n'; ++p) { 727 if (*p == quote) { 728 if (p[1] != quote) { 729 break; 730 } 731 ++p; 732 } 733 path += *p; 734 } 735 if (*p != quote) { 736 Say(GetProvenanceRange(firstQuote, p), 737 "malformed path name string"_err_en_US); 738 return; 739 } 740 p = SkipWhiteSpace(p + 1); 741 if (*p != '\n' && *p != '!') { 742 const char *garbage{p}; 743 for (; *p != '\n' && *p != '!'; ++p) { 744 } 745 Say(GetProvenanceRange(garbage, p), 746 "excess characters after path name"_en_US); 747 } 748 std::string buf; 749 llvm::raw_string_ostream error{buf}; 750 Provenance provenance{GetProvenance(nextLine_)}; 751 AllSources &allSources{cooked_.allSources()}; 752 const SourceFile *currentFile{allSources.GetSourceFile(provenance)}; 753 if (currentFile) { 754 allSources.PushSearchPathDirectory(DirectoryName(currentFile->path())); 755 } 756 const SourceFile *included{allSources.Open(path, error)}; 757 if (currentFile) { 758 allSources.PopSearchPathDirectory(); 759 } 760 if (!included) { 761 Say(provenance, "INCLUDE: %s"_err_en_US, error.str()); 762 } else if (included->bytes() > 0) { 763 ProvenanceRange includeLineRange{ 764 provenance, static_cast<std::size_t>(p - nextLine_)}; 765 ProvenanceRange fileRange{ 766 allSources.AddIncludedFile(*included, includeLineRange)}; 767 Prescanner{*this}.set_encoding(included->encoding()).Prescan(fileRange); 768 } 769 } 770 771 const char *Prescanner::IsPreprocessorDirectiveLine(const char *start) const { 772 const char *p{start}; 773 for (; *p == ' '; ++p) { 774 } 775 if (*p == '#') { 776 if (inFixedForm_ && p == start + 5) { 777 return nullptr; 778 } 779 } else { 780 p = SkipWhiteSpace(p); 781 if (*p != '#') { 782 return nullptr; 783 } 784 } 785 return SkipWhiteSpace(p + 1); 786 } 787 788 bool Prescanner::IsNextLinePreprocessorDirective() const { 789 return IsPreprocessorDirectiveLine(nextLine_) != nullptr; 790 } 791 792 bool Prescanner::SkipCommentLine(bool afterAmpersand) { 793 if (nextLine_ >= limit_) { 794 if (afterAmpersand && prescannerNesting_ > 0) { 795 // A continuation marker at the end of the last line in an 796 // include file inhibits the newline for that line. 797 SkipToEndOfLine(); 798 omitNewline_ = true; 799 } 800 return false; 801 } 802 auto lineClass{ClassifyLine(nextLine_)}; 803 if (lineClass.kind == LineClassification::Kind::Comment) { 804 NextLine(); 805 return true; 806 } else if (inPreprocessorDirective_) { 807 return false; 808 } else if (lineClass.kind == 809 LineClassification::Kind::ConditionalCompilationDirective || 810 lineClass.kind == LineClassification::Kind::PreprocessorDirective) { 811 // Allow conditional compilation directives (e.g., #ifdef) to affect 812 // continuation lines. 813 // Allow other preprocessor directives, too, except #include 814 // (when it does not follow '&'), #define, and #undef (because 815 // they cannot be allowed to affect preceding text on a 816 // continued line). 817 preprocessor_.Directive(TokenizePreprocessorDirective(), this); 818 return true; 819 } else if (afterAmpersand && 820 (lineClass.kind == LineClassification::Kind::IncludeDirective || 821 lineClass.kind == LineClassification::Kind::IncludeLine)) { 822 SkipToEndOfLine(); 823 omitNewline_ = true; 824 skipLeadingAmpersand_ = true; 825 return false; 826 } else { 827 return false; 828 } 829 } 830 831 const char *Prescanner::FixedFormContinuationLine(bool mightNeedSpace) { 832 if (nextLine_ >= limit_) { 833 return nullptr; 834 } 835 tabInCurrentLine_ = false; 836 char col1{*nextLine_}; 837 if (InCompilerDirective()) { 838 // Must be a continued compiler directive. 839 if (!IsFixedFormCommentChar(col1)) { 840 return nullptr; 841 } 842 int j{1}; 843 for (; j < 5; ++j) { 844 char ch{directiveSentinel_[j - 1]}; 845 if (ch == '\0') { 846 break; 847 } 848 if (ch != ToLowerCaseLetter(nextLine_[j])) { 849 return nullptr; 850 } 851 } 852 for (; j < 5; ++j) { 853 if (nextLine_[j] != ' ') { 854 return nullptr; 855 } 856 } 857 char col6{nextLine_[5]}; 858 if (col6 != '\n' && col6 != '\t' && col6 != ' ' && col6 != '0') { 859 if (nextLine_[6] != ' ' && mightNeedSpace) { 860 insertASpace_ = true; 861 } 862 return nextLine_ + 6; 863 } 864 return nullptr; 865 } else { 866 // Normal case: not in a compiler directive. 867 if (col1 == '&' && 868 features_.IsEnabled( 869 LanguageFeature::FixedFormContinuationWithColumn1Ampersand)) { 870 // Extension: '&' as continuation marker 871 if (features_.ShouldWarn( 872 LanguageFeature::FixedFormContinuationWithColumn1Ampersand)) { 873 Say(GetProvenance(nextLine_), "nonstandard usage"_en_US); 874 } 875 return nextLine_ + 1; 876 } 877 if (col1 == '\t' && nextLine_[1] >= '1' && nextLine_[1] <= '9') { 878 tabInCurrentLine_ = true; 879 return nextLine_ + 2; // VAX extension 880 } 881 if (col1 == ' ' && nextLine_[1] == ' ' && nextLine_[2] == ' ' && 882 nextLine_[3] == ' ' && nextLine_[4] == ' ') { 883 char col6{nextLine_[5]}; 884 if (col6 != '\n' && col6 != '\t' && col6 != ' ' && col6 != '0') { 885 return nextLine_ + 6; 886 } 887 } 888 if (IsImplicitContinuation()) { 889 return nextLine_; 890 } 891 } 892 return nullptr; // not a continuation line 893 } 894 895 const char *Prescanner::FreeFormContinuationLine(bool ampersand) { 896 const char *p{nextLine_}; 897 if (p >= limit_) { 898 return nullptr; 899 } 900 p = SkipWhiteSpace(p); 901 if (InCompilerDirective()) { 902 if (*p++ != '!') { 903 return nullptr; 904 } 905 for (const char *s{directiveSentinel_}; *s != '\0'; ++p, ++s) { 906 if (*s != ToLowerCaseLetter(*p)) { 907 return nullptr; 908 } 909 } 910 p = SkipWhiteSpace(p); 911 if (*p == '&') { 912 if (!ampersand) { 913 insertASpace_ = true; 914 } 915 return p + 1; 916 } else if (ampersand) { 917 return p; 918 } else { 919 return nullptr; 920 } 921 } else { 922 if (*p == '&') { 923 return p + 1; 924 } else if (*p == '!' || *p == '\n' || *p == '#') { 925 return nullptr; 926 } else if (ampersand || IsImplicitContinuation()) { 927 if (p > nextLine_) { 928 --p; 929 } else { 930 insertASpace_ = true; 931 } 932 return p; 933 } else { 934 return nullptr; 935 } 936 } 937 } 938 939 bool Prescanner::FixedFormContinuation(bool mightNeedSpace) { 940 // N.B. We accept '&' as a continuation indicator in fixed form, too, 941 // but not in a character literal. 942 if (*at_ == '&' && inCharLiteral_) { 943 return false; 944 } 945 do { 946 if (const char *cont{FixedFormContinuationLine(mightNeedSpace)}) { 947 BeginSourceLine(cont); 948 column_ = 7; 949 NextLine(); 950 return true; 951 } 952 } while (SkipCommentLine(false /* not after ampersand */)); 953 return false; 954 } 955 956 bool Prescanner::FreeFormContinuation() { 957 const char *p{at_}; 958 bool ampersand{*p == '&'}; 959 if (ampersand) { 960 p = SkipWhiteSpace(p + 1); 961 } 962 if (*p != '\n') { 963 if (inCharLiteral_) { 964 return false; 965 } else if (*p != '!' && 966 features_.ShouldWarn(LanguageFeature::CruftAfterAmpersand)) { 967 Say(GetProvenance(p), "missing ! before comment after &"_en_US); 968 } 969 } 970 do { 971 if (const char *cont{FreeFormContinuationLine(ampersand)}) { 972 BeginSourceLine(cont); 973 NextLine(); 974 return true; 975 } 976 } while (SkipCommentLine(ampersand)); 977 return false; 978 } 979 980 // Implicit line continuation allows a preprocessor macro call with 981 // arguments to span multiple lines. 982 bool Prescanner::IsImplicitContinuation() const { 983 return !inPreprocessorDirective_ && !inCharLiteral_ && 984 delimiterNesting_ > 0 && nextLine_ < limit_ && 985 ClassifyLine(nextLine_).kind == LineClassification::Kind::Source; 986 } 987 988 bool Prescanner::Continuation(bool mightNeedFixedFormSpace) { 989 if (*at_ == '\n' || *at_ == '&') { 990 if (inFixedForm_) { 991 return FixedFormContinuation(mightNeedFixedFormSpace); 992 } else { 993 return FreeFormContinuation(); 994 } 995 } else { 996 return false; 997 } 998 } 999 1000 std::optional<Prescanner::LineClassification> 1001 Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const { 1002 const char *p{start}; 1003 char col1{*p++}; 1004 if (!IsFixedFormCommentChar(col1)) { 1005 return std::nullopt; 1006 } 1007 char sentinel[5], *sp{sentinel}; 1008 int column{2}; 1009 for (; column < 6; ++column, ++p) { 1010 if (*p != ' ') { 1011 if (*p == '\n' || *p == '\t') { 1012 break; 1013 } 1014 if (sp == sentinel + 1 && sentinel[0] == '$' && IsDecimalDigit(*p)) { 1015 // OpenMP conditional compilation line: leave the label alone 1016 break; 1017 } 1018 *sp++ = ToLowerCaseLetter(*p); 1019 } 1020 } 1021 if (column == 6) { 1022 if (*p == ' ' || *p == '\t' || *p == '0') { 1023 ++p; 1024 } else { 1025 // This is a Continuation line, not an initial directive line. 1026 return std::nullopt; 1027 } 1028 } 1029 if (sp == sentinel) { 1030 return std::nullopt; 1031 } 1032 *sp = '\0'; 1033 if (const char *ss{IsCompilerDirectiveSentinel(sentinel)}) { 1034 std::size_t payloadOffset = p - start; 1035 return {LineClassification{ 1036 LineClassification::Kind::CompilerDirective, payloadOffset, ss}}; 1037 } 1038 return std::nullopt; 1039 } 1040 1041 std::optional<Prescanner::LineClassification> 1042 Prescanner::IsFreeFormCompilerDirectiveLine(const char *start) const { 1043 char sentinel[8]; 1044 const char *p{SkipWhiteSpace(start)}; 1045 if (*p++ != '!') { 1046 return std::nullopt; 1047 } 1048 for (std::size_t j{0}; j + 1 < sizeof sentinel; ++p, ++j) { 1049 if (*p == '\n') { 1050 break; 1051 } 1052 if (*p == ' ' || *p == '\t' || *p == '&') { 1053 if (j == 0) { 1054 break; 1055 } 1056 sentinel[j] = '\0'; 1057 p = SkipWhiteSpace(p + 1); 1058 if (*p == '!') { 1059 break; 1060 } 1061 if (const char *sp{IsCompilerDirectiveSentinel(sentinel)}) { 1062 std::size_t offset = p - start; 1063 return {LineClassification{ 1064 LineClassification::Kind::CompilerDirective, offset, sp}}; 1065 } 1066 break; 1067 } 1068 sentinel[j] = ToLowerCaseLetter(*p); 1069 } 1070 return std::nullopt; 1071 } 1072 1073 Prescanner &Prescanner::AddCompilerDirectiveSentinel(const std::string &dir) { 1074 std::uint64_t packed{0}; 1075 for (char ch : dir) { 1076 packed = (packed << 8) | (ToLowerCaseLetter(ch) & 0xff); 1077 } 1078 compilerDirectiveBloomFilter_.set(packed % prime1); 1079 compilerDirectiveBloomFilter_.set(packed % prime2); 1080 compilerDirectiveSentinels_.insert(dir); 1081 return *this; 1082 } 1083 1084 const char *Prescanner::IsCompilerDirectiveSentinel( 1085 const char *sentinel) const { 1086 std::uint64_t packed{0}; 1087 std::size_t n{0}; 1088 for (; sentinel[n] != '\0'; ++n) { 1089 packed = (packed << 8) | (sentinel[n] & 0xff); 1090 } 1091 if (n == 0 || !compilerDirectiveBloomFilter_.test(packed % prime1) || 1092 !compilerDirectiveBloomFilter_.test(packed % prime2)) { 1093 return nullptr; 1094 } 1095 const auto iter{compilerDirectiveSentinels_.find(std::string(sentinel, n))}; 1096 return iter == compilerDirectiveSentinels_.end() ? nullptr : iter->c_str(); 1097 } 1098 1099 constexpr bool IsDirective(const char *match, const char *dir) { 1100 for (; *match; ++match) { 1101 if (*match != ToLowerCaseLetter(*dir++)) { 1102 return false; 1103 } 1104 } 1105 return true; 1106 } 1107 1108 Prescanner::LineClassification Prescanner::ClassifyLine( 1109 const char *start) const { 1110 if (inFixedForm_) { 1111 if (std::optional<LineClassification> lc{ 1112 IsFixedFormCompilerDirectiveLine(start)}) { 1113 return std::move(*lc); 1114 } 1115 if (IsFixedFormCommentLine(start)) { 1116 return {LineClassification::Kind::Comment}; 1117 } 1118 } else { 1119 if (std::optional<LineClassification> lc{ 1120 IsFreeFormCompilerDirectiveLine(start)}) { 1121 return std::move(*lc); 1122 } 1123 if (const char *bang{IsFreeFormComment(start)}) { 1124 return {LineClassification::Kind::Comment, 1125 static_cast<std::size_t>(bang - start)}; 1126 } 1127 } 1128 if (std::optional<std::size_t> quoteOffset{IsIncludeLine(start)}) { 1129 return {LineClassification::Kind::IncludeLine, *quoteOffset}; 1130 } 1131 if (const char *dir{IsPreprocessorDirectiveLine(start)}) { 1132 if (IsDirective("if", dir) || IsDirective("elif", dir) || 1133 IsDirective("else", dir) || IsDirective("endif", dir)) { 1134 return {LineClassification::Kind::ConditionalCompilationDirective}; 1135 } else if (IsDirective("include", dir)) { 1136 return {LineClassification::Kind::IncludeDirective}; 1137 } else if (IsDirective("define", dir) || IsDirective("undef", dir)) { 1138 return {LineClassification::Kind::DefinitionDirective}; 1139 } else { 1140 return {LineClassification::Kind::PreprocessorDirective}; 1141 } 1142 } 1143 return {LineClassification::Kind::Source}; 1144 } 1145 1146 void Prescanner::SourceFormChange(std::string &&dir) { 1147 if (dir == "!dir$ free") { 1148 inFixedForm_ = false; 1149 } else if (dir == "!dir$ fixed") { 1150 inFixedForm_ = true; 1151 } 1152 } 1153 } // namespace Fortran::parser 1154