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