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