xref: /llvm-project/flang/lib/Parser/token-sequence.cpp (revision 850d42fb145c636a3b56a7616c3e3c5c188c1916)
164ab3302SCarolineConcatto //===-- lib/Parser/token-sequence.cpp -------------------------------------===//
264ab3302SCarolineConcatto //
364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information.
564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
664ab3302SCarolineConcatto //
764ab3302SCarolineConcatto //===----------------------------------------------------------------------===//
864ab3302SCarolineConcatto 
97d60232bSKrzysztof Parzyszek #include "flang/Parser/token-sequence.h"
107d60232bSKrzysztof Parzyszek 
11cbc5d42fSPeter Klausler #include "prescan.h"
1264ab3302SCarolineConcatto #include "flang/Parser/characters.h"
13a0a1a4e5Speter klausler #include "flang/Parser/message.h"
148670e499SCaroline Concatto #include "llvm/Support/raw_ostream.h"
1564ab3302SCarolineConcatto 
1664ab3302SCarolineConcatto namespace Fortran::parser {
1764ab3302SCarolineConcatto 
1864ab3302SCarolineConcatto TokenSequence &TokenSequence::operator=(TokenSequence &&that) {
1964ab3302SCarolineConcatto   clear();
2064ab3302SCarolineConcatto   swap(that);
2164ab3302SCarolineConcatto   return *this;
2264ab3302SCarolineConcatto }
2364ab3302SCarolineConcatto 
2464ab3302SCarolineConcatto void TokenSequence::clear() {
2564ab3302SCarolineConcatto   start_.clear();
2664ab3302SCarolineConcatto   nextStart_ = 0;
2764ab3302SCarolineConcatto   char_.clear();
2864ab3302SCarolineConcatto   provenances_.clear();
2964ab3302SCarolineConcatto }
3064ab3302SCarolineConcatto 
3164ab3302SCarolineConcatto void TokenSequence::pop_back() {
32094b380cSpeter klausler   CHECK(!start_.empty());
33094b380cSpeter klausler   CHECK(nextStart_ > start_.back());
3464ab3302SCarolineConcatto   std::size_t bytes{nextStart_ - start_.back()};
3564ab3302SCarolineConcatto   nextStart_ = start_.back();
3664ab3302SCarolineConcatto   start_.pop_back();
3764ab3302SCarolineConcatto   char_.resize(nextStart_);
3864ab3302SCarolineConcatto   provenances_.RemoveLastBytes(bytes);
3964ab3302SCarolineConcatto }
4064ab3302SCarolineConcatto 
4164ab3302SCarolineConcatto void TokenSequence::shrink_to_fit() {
4264ab3302SCarolineConcatto   start_.shrink_to_fit();
4364ab3302SCarolineConcatto   char_.shrink_to_fit();
4464ab3302SCarolineConcatto   provenances_.shrink_to_fit();
4564ab3302SCarolineConcatto }
4664ab3302SCarolineConcatto 
4764ab3302SCarolineConcatto void TokenSequence::swap(TokenSequence &that) {
4864ab3302SCarolineConcatto   start_.swap(that.start_);
4964ab3302SCarolineConcatto   std::swap(nextStart_, that.nextStart_);
5064ab3302SCarolineConcatto   char_.swap(that.char_);
5164ab3302SCarolineConcatto   provenances_.swap(that.provenances_);
5264ab3302SCarolineConcatto }
5364ab3302SCarolineConcatto 
5464ab3302SCarolineConcatto std::size_t TokenSequence::SkipBlanks(std::size_t at) const {
5564ab3302SCarolineConcatto   std::size_t tokens{start_.size()};
5664ab3302SCarolineConcatto   for (; at < tokens; ++at) {
5764ab3302SCarolineConcatto     if (!TokenAt(at).IsBlank()) {
5864ab3302SCarolineConcatto       return at;
5964ab3302SCarolineConcatto     }
6064ab3302SCarolineConcatto   }
6164ab3302SCarolineConcatto   return tokens; // even if at > tokens
6264ab3302SCarolineConcatto }
6364ab3302SCarolineConcatto 
64*850d42fbSPeter Klausler std::optional<std::size_t> TokenSequence::SkipBlanksBackwards(
65*850d42fbSPeter Klausler     std::size_t at) const {
66*850d42fbSPeter Klausler   while (at-- > 0) {
67*850d42fbSPeter Klausler     if (!TokenAt(at).IsBlank()) {
68*850d42fbSPeter Klausler       return at;
69*850d42fbSPeter Klausler     }
70*850d42fbSPeter Klausler   }
71*850d42fbSPeter Klausler   return std::nullopt;
72*850d42fbSPeter Klausler }
73*850d42fbSPeter Klausler 
74cf2274b7Speter klausler // C-style /*comments*/ are removed from preprocessing directive
75cf2274b7Speter klausler // token sequences by the prescanner, but not C++ or Fortran
76cf2274b7Speter klausler // free-form line-ending comments (//...  and !...) because
77cf2274b7Speter klausler // ignoring them is directive-specific.
78cf2274b7Speter klausler bool TokenSequence::IsAnythingLeft(std::size_t at) const {
79cf2274b7Speter klausler   std::size_t tokens{start_.size()};
80cf2274b7Speter klausler   for (; at < tokens; ++at) {
81cf2274b7Speter klausler     auto tok{TokenAt(at)};
82cf2274b7Speter klausler     const char *end{tok.end()};
83cf2274b7Speter klausler     for (const char *p{tok.begin()}; p < end; ++p) {
84cf2274b7Speter klausler       switch (*p) {
85cf2274b7Speter klausler       case '/':
86cf2274b7Speter klausler         return p + 1 >= end || p[1] != '/';
87cf2274b7Speter klausler       case '!':
88cf2274b7Speter klausler         return false;
89cf2274b7Speter klausler       case ' ':
90cf2274b7Speter klausler         break;
91cf2274b7Speter klausler       default:
92cf2274b7Speter klausler         return true;
93cf2274b7Speter klausler       }
94cf2274b7Speter klausler     }
95cf2274b7Speter klausler   }
96cf2274b7Speter klausler   return false;
97cf2274b7Speter klausler }
98cf2274b7Speter klausler 
9964ab3302SCarolineConcatto void TokenSequence::Put(const TokenSequence &that) {
10064ab3302SCarolineConcatto   if (nextStart_ < char_.size()) {
10164ab3302SCarolineConcatto     start_.push_back(nextStart_);
10264ab3302SCarolineConcatto   }
10364ab3302SCarolineConcatto   int offset = char_.size();
10464ab3302SCarolineConcatto   for (int st : that.start_) {
10564ab3302SCarolineConcatto     start_.push_back(st + offset);
10664ab3302SCarolineConcatto   }
10764ab3302SCarolineConcatto   char_.insert(char_.end(), that.char_.begin(), that.char_.end());
10864ab3302SCarolineConcatto   nextStart_ = char_.size();
10964ab3302SCarolineConcatto   provenances_.Put(that.provenances_);
11064ab3302SCarolineConcatto }
11164ab3302SCarolineConcatto 
11264ab3302SCarolineConcatto void TokenSequence::Put(const TokenSequence &that, ProvenanceRange range) {
11364ab3302SCarolineConcatto   std::size_t offset{0};
11464ab3302SCarolineConcatto   std::size_t tokens{that.SizeInTokens()};
11564ab3302SCarolineConcatto   for (std::size_t j{0}; j < tokens; ++j) {
11664ab3302SCarolineConcatto     CharBlock tok{that.TokenAt(j)};
11764ab3302SCarolineConcatto     Put(tok, range.OffsetMember(offset));
11864ab3302SCarolineConcatto     offset += tok.size();
11964ab3302SCarolineConcatto   }
12064ab3302SCarolineConcatto   CHECK(offset == range.size());
12164ab3302SCarolineConcatto }
12264ab3302SCarolineConcatto 
12364ab3302SCarolineConcatto void TokenSequence::Put(
12464ab3302SCarolineConcatto     const TokenSequence &that, std::size_t at, std::size_t tokens) {
12564ab3302SCarolineConcatto   ProvenanceRange provenance;
12664ab3302SCarolineConcatto   std::size_t offset{0};
12764ab3302SCarolineConcatto   for (; tokens-- > 0; ++at) {
12864ab3302SCarolineConcatto     CharBlock tok{that.TokenAt(at)};
12964ab3302SCarolineConcatto     std::size_t tokBytes{tok.size()};
13064ab3302SCarolineConcatto     for (std::size_t j{0}; j < tokBytes; ++j) {
13164ab3302SCarolineConcatto       if (offset == provenance.size()) {
13264ab3302SCarolineConcatto         provenance = that.provenances_.Map(that.start_[at] + j);
13364ab3302SCarolineConcatto         offset = 0;
13464ab3302SCarolineConcatto       }
13564ab3302SCarolineConcatto       PutNextTokenChar(tok[j], provenance.OffsetMember(offset++));
13664ab3302SCarolineConcatto     }
13764ab3302SCarolineConcatto     CloseToken();
13864ab3302SCarolineConcatto   }
13964ab3302SCarolineConcatto }
14064ab3302SCarolineConcatto 
14164ab3302SCarolineConcatto void TokenSequence::Put(
14264ab3302SCarolineConcatto     const char *s, std::size_t bytes, Provenance provenance) {
14364ab3302SCarolineConcatto   for (std::size_t j{0}; j < bytes; ++j) {
14464ab3302SCarolineConcatto     PutNextTokenChar(s[j], provenance + j);
14564ab3302SCarolineConcatto   }
14664ab3302SCarolineConcatto   CloseToken();
14764ab3302SCarolineConcatto }
14864ab3302SCarolineConcatto 
14964ab3302SCarolineConcatto void TokenSequence::Put(const CharBlock &t, Provenance provenance) {
1501def98d9SKrzysztof Parzyszek   // Avoid t[0] if t is empty: it would create a reference to nullptr,
1511def98d9SKrzysztof Parzyszek   // which is UB.
1521def98d9SKrzysztof Parzyszek   const char *addr{t.size() ? &t[0] : nullptr};
1531def98d9SKrzysztof Parzyszek   Put(addr, t.size(), provenance);
15464ab3302SCarolineConcatto }
15564ab3302SCarolineConcatto 
15664ab3302SCarolineConcatto void TokenSequence::Put(const std::string &s, Provenance provenance) {
15764ab3302SCarolineConcatto   Put(s.data(), s.size(), provenance);
15864ab3302SCarolineConcatto }
15964ab3302SCarolineConcatto 
1608670e499SCaroline Concatto void TokenSequence::Put(llvm::raw_string_ostream &ss, Provenance provenance) {
16164ab3302SCarolineConcatto   Put(ss.str(), provenance);
16264ab3302SCarolineConcatto }
16364ab3302SCarolineConcatto 
16464ab3302SCarolineConcatto TokenSequence &TokenSequence::ToLowerCase() {
16564ab3302SCarolineConcatto   std::size_t tokens{start_.size()};
16664ab3302SCarolineConcatto   std::size_t chars{char_.size()};
16764ab3302SCarolineConcatto   std::size_t atToken{0};
16864ab3302SCarolineConcatto   for (std::size_t j{0}; j < chars;) {
16964ab3302SCarolineConcatto     std::size_t nextStart{atToken + 1 < tokens ? start_[++atToken] : chars};
1702a774411Sserge-sans-paille     char *p{&char_[j]};
1712a774411Sserge-sans-paille     char const *limit{char_.data() + nextStart};
1720e1fa917SLeandro Lupori     const char *lastChar{limit - 1};
17364ab3302SCarolineConcatto     j = nextStart;
1740e1fa917SLeandro Lupori     // Skip leading whitespaces
1750e1fa917SLeandro Lupori     while (p < limit - 1 && *p == ' ') {
1760e1fa917SLeandro Lupori       ++p;
1770e1fa917SLeandro Lupori     }
1780e1fa917SLeandro Lupori     // Find last non-whitespace char
1790e1fa917SLeandro Lupori     while (lastChar > p + 1 && *lastChar == ' ') {
1800e1fa917SLeandro Lupori       --lastChar;
1810e1fa917SLeandro Lupori     }
18264ab3302SCarolineConcatto     if (IsDecimalDigit(*p)) {
18364ab3302SCarolineConcatto       while (p < limit && IsDecimalDigit(*p)) {
18464ab3302SCarolineConcatto         ++p;
18564ab3302SCarolineConcatto       }
18664ab3302SCarolineConcatto       if (p >= limit) {
18764ab3302SCarolineConcatto       } else if (*p == 'h' || *p == 'H') {
18864ab3302SCarolineConcatto         // Hollerith
18964ab3302SCarolineConcatto         *p = 'h';
19064ab3302SCarolineConcatto       } else if (*p == '_') {
19164ab3302SCarolineConcatto         // kind-prefixed character literal (e.g., 1_"ABC")
19264ab3302SCarolineConcatto       } else {
19364ab3302SCarolineConcatto         // exponent
19464ab3302SCarolineConcatto         for (; p < limit; ++p) {
19564ab3302SCarolineConcatto           *p = ToLowerCaseLetter(*p);
19664ab3302SCarolineConcatto         }
19764ab3302SCarolineConcatto       }
1980e1fa917SLeandro Lupori     } else if (*lastChar == '\'' || *lastChar == '"') {
1990e1fa917SLeandro Lupori       if (*p == *lastChar) {
20064ab3302SCarolineConcatto         // Character literal without prefix
2010e1fa917SLeandro Lupori       } else if (p[1] == *lastChar) {
20264ab3302SCarolineConcatto         // BOZX-prefixed constant
20364ab3302SCarolineConcatto         for (; p < limit; ++p) {
20464ab3302SCarolineConcatto           *p = ToLowerCaseLetter(*p);
20564ab3302SCarolineConcatto         }
20664ab3302SCarolineConcatto       } else {
20764ab3302SCarolineConcatto         // Literal with kind-param prefix name (e.g., K_"ABC").
2080e1fa917SLeandro Lupori         for (; *p != *lastChar; ++p) {
20964ab3302SCarolineConcatto           *p = ToLowerCaseLetter(*p);
21064ab3302SCarolineConcatto         }
21164ab3302SCarolineConcatto       }
21264ab3302SCarolineConcatto     } else {
21364ab3302SCarolineConcatto       for (; p < limit; ++p) {
21464ab3302SCarolineConcatto         *p = ToLowerCaseLetter(*p);
21564ab3302SCarolineConcatto       }
21664ab3302SCarolineConcatto     }
21764ab3302SCarolineConcatto   }
21864ab3302SCarolineConcatto   return *this;
21964ab3302SCarolineConcatto }
22064ab3302SCarolineConcatto 
22164ab3302SCarolineConcatto bool TokenSequence::HasBlanks(std::size_t firstChar) const {
22264ab3302SCarolineConcatto   std::size_t tokens{SizeInTokens()};
22364ab3302SCarolineConcatto   for (std::size_t j{0}; j < tokens; ++j) {
22464ab3302SCarolineConcatto     if (start_[j] >= firstChar && TokenAt(j).IsBlank()) {
22564ab3302SCarolineConcatto       return true;
22664ab3302SCarolineConcatto     }
22764ab3302SCarolineConcatto   }
22864ab3302SCarolineConcatto   return false;
22964ab3302SCarolineConcatto }
23064ab3302SCarolineConcatto 
23164ab3302SCarolineConcatto bool TokenSequence::HasRedundantBlanks(std::size_t firstChar) const {
23264ab3302SCarolineConcatto   std::size_t tokens{SizeInTokens()};
23364ab3302SCarolineConcatto   bool lastWasBlank{false};
23464ab3302SCarolineConcatto   for (std::size_t j{0}; j < tokens; ++j) {
23564ab3302SCarolineConcatto     bool isBlank{TokenAt(j).IsBlank()};
23664ab3302SCarolineConcatto     if (isBlank && lastWasBlank && start_[j] >= firstChar) {
23764ab3302SCarolineConcatto       return true;
23864ab3302SCarolineConcatto     }
23964ab3302SCarolineConcatto     lastWasBlank = isBlank;
24064ab3302SCarolineConcatto   }
24164ab3302SCarolineConcatto   return false;
24264ab3302SCarolineConcatto }
24364ab3302SCarolineConcatto 
24464ab3302SCarolineConcatto TokenSequence &TokenSequence::RemoveBlanks(std::size_t firstChar) {
24564ab3302SCarolineConcatto   std::size_t tokens{SizeInTokens()};
24664ab3302SCarolineConcatto   TokenSequence result;
24764ab3302SCarolineConcatto   for (std::size_t j{0}; j < tokens; ++j) {
24864ab3302SCarolineConcatto     if (!TokenAt(j).IsBlank() || start_[j] < firstChar) {
24964ab3302SCarolineConcatto       result.Put(*this, j);
25064ab3302SCarolineConcatto     }
25164ab3302SCarolineConcatto   }
25264ab3302SCarolineConcatto   swap(result);
25364ab3302SCarolineConcatto   return *this;
25464ab3302SCarolineConcatto }
25564ab3302SCarolineConcatto 
25664ab3302SCarolineConcatto TokenSequence &TokenSequence::RemoveRedundantBlanks(std::size_t firstChar) {
25764ab3302SCarolineConcatto   std::size_t tokens{SizeInTokens()};
25864ab3302SCarolineConcatto   TokenSequence result;
25964ab3302SCarolineConcatto   bool lastWasBlank{false};
26064ab3302SCarolineConcatto   for (std::size_t j{0}; j < tokens; ++j) {
26164ab3302SCarolineConcatto     bool isBlank{TokenAt(j).IsBlank()};
26264ab3302SCarolineConcatto     if (!isBlank || !lastWasBlank || start_[j] < firstChar) {
26364ab3302SCarolineConcatto       result.Put(*this, j);
26464ab3302SCarolineConcatto     }
26564ab3302SCarolineConcatto     lastWasBlank = isBlank;
26664ab3302SCarolineConcatto   }
26764ab3302SCarolineConcatto   swap(result);
26864ab3302SCarolineConcatto   return *this;
26964ab3302SCarolineConcatto }
27064ab3302SCarolineConcatto 
271cbc5d42fSPeter Klausler TokenSequence &TokenSequence::ClipComment(
272cbc5d42fSPeter Klausler     const Prescanner &prescanner, bool skipFirst) {
27364ab3302SCarolineConcatto   std::size_t tokens{SizeInTokens()};
27464ab3302SCarolineConcatto   for (std::size_t j{0}; j < tokens; ++j) {
275cbc5d42fSPeter Klausler     CharBlock tok{TokenAt(j)};
276cbc5d42fSPeter Klausler     if (std::size_t blanks{tok.CountLeadingBlanks()};
277cbc5d42fSPeter Klausler         blanks < tok.size() && tok[blanks] == '!') {
278cbc5d42fSPeter Klausler       // Retain active compiler directive sentinels (e.g. "!dir$")
279259ce119SPeter Klausler       for (std::size_t k{j + 1}; k < tokens && tok.size() <= blanks + 5; ++k) {
280cbc5d42fSPeter Klausler         if (tok.begin() + tok.size() == TokenAt(k).begin()) {
281cbc5d42fSPeter Klausler           tok.ExtendToCover(TokenAt(k));
282cbc5d42fSPeter Klausler         } else {
283cbc5d42fSPeter Klausler           break;
284cbc5d42fSPeter Klausler         }
285cbc5d42fSPeter Klausler       }
286cbc5d42fSPeter Klausler       bool isSentinel{false};
287259ce119SPeter Klausler       if (tok.size() > blanks + 5) {
288259ce119SPeter Klausler         isSentinel = prescanner.IsCompilerDirectiveSentinel(&tok[blanks + 1])
289259ce119SPeter Klausler                          .has_value();
290cbc5d42fSPeter Klausler       }
291cbc5d42fSPeter Klausler       if (isSentinel) {
292cbc5d42fSPeter Klausler       } else if (skipFirst) {
29364ab3302SCarolineConcatto         skipFirst = false;
29464ab3302SCarolineConcatto       } else {
29564ab3302SCarolineConcatto         TokenSequence result;
29664ab3302SCarolineConcatto         if (j > 0) {
29764ab3302SCarolineConcatto           result.Put(*this, 0, j - 1);
29864ab3302SCarolineConcatto         }
29964ab3302SCarolineConcatto         swap(result);
30064ab3302SCarolineConcatto         return *this;
30164ab3302SCarolineConcatto       }
30264ab3302SCarolineConcatto     }
30364ab3302SCarolineConcatto   }
30464ab3302SCarolineConcatto   return *this;
30564ab3302SCarolineConcatto }
30664ab3302SCarolineConcatto 
30764ab3302SCarolineConcatto void TokenSequence::Emit(CookedSource &cooked) const {
30895526930SPeter Klausler   if (auto n{char_.size()}) {
30995526930SPeter Klausler     cooked.Put(&char_[0], n);
31064ab3302SCarolineConcatto     cooked.PutProvenanceMappings(provenances_);
31164ab3302SCarolineConcatto   }
31295526930SPeter Klausler }
31364ab3302SCarolineConcatto 
3143338ef93Speter klausler llvm::raw_ostream &TokenSequence::Dump(llvm::raw_ostream &o) const {
31564ab3302SCarolineConcatto   o << "TokenSequence has " << char_.size() << " chars; nextStart_ "
31664ab3302SCarolineConcatto     << nextStart_ << '\n';
31764ab3302SCarolineConcatto   for (std::size_t j{0}; j < start_.size(); ++j) {
31864ab3302SCarolineConcatto     o << '[' << j << "] @ " << start_[j] << " '" << TokenAt(j).ToString()
31964ab3302SCarolineConcatto       << "'\n";
32064ab3302SCarolineConcatto   }
3213338ef93Speter klausler   return o;
32264ab3302SCarolineConcatto }
32364ab3302SCarolineConcatto 
324f6ddfac4Speter klausler Provenance TokenSequence::GetCharProvenance(std::size_t offset) const {
325f6ddfac4Speter klausler   ProvenanceRange range{provenances_.Map(offset)};
326f6ddfac4Speter klausler   return range.start();
327f6ddfac4Speter klausler }
328f6ddfac4Speter klausler 
32964ab3302SCarolineConcatto Provenance TokenSequence::GetTokenProvenance(
33064ab3302SCarolineConcatto     std::size_t token, std::size_t offset) const {
331f6ddfac4Speter klausler   return GetCharProvenance(start_[token] + offset);
33264ab3302SCarolineConcatto }
33364ab3302SCarolineConcatto 
33464ab3302SCarolineConcatto ProvenanceRange TokenSequence::GetTokenProvenanceRange(
33564ab3302SCarolineConcatto     std::size_t token, std::size_t offset) const {
33664ab3302SCarolineConcatto   ProvenanceRange range{provenances_.Map(start_[token] + offset)};
33764ab3302SCarolineConcatto   return range.Prefix(TokenBytes(token) - offset);
33864ab3302SCarolineConcatto }
33964ab3302SCarolineConcatto 
34064ab3302SCarolineConcatto ProvenanceRange TokenSequence::GetIntervalProvenanceRange(
34164ab3302SCarolineConcatto     std::size_t token, std::size_t tokens) const {
34264ab3302SCarolineConcatto   if (tokens == 0) {
34364ab3302SCarolineConcatto     return {};
34464ab3302SCarolineConcatto   }
34564ab3302SCarolineConcatto   ProvenanceRange range{provenances_.Map(start_[token])};
34664ab3302SCarolineConcatto   while (--tokens > 0 &&
34764ab3302SCarolineConcatto       range.AnnexIfPredecessor(provenances_.Map(start_[++token]))) {
34864ab3302SCarolineConcatto   }
34964ab3302SCarolineConcatto   return range;
35064ab3302SCarolineConcatto }
35164ab3302SCarolineConcatto 
35264ab3302SCarolineConcatto ProvenanceRange TokenSequence::GetProvenanceRange() const {
35364ab3302SCarolineConcatto   return GetIntervalProvenanceRange(0, start_.size());
35464ab3302SCarolineConcatto }
355a0a1a4e5Speter klausler 
356a0a1a4e5Speter klausler const TokenSequence &TokenSequence::CheckBadFortranCharacters(
357e286ecfeSPeter Klausler     Messages &messages, const Prescanner &prescanner,
358e286ecfeSPeter Klausler     bool allowAmpersand) const {
359a0a1a4e5Speter klausler   std::size_t tokens{SizeInTokens()};
360a0a1a4e5Speter klausler   for (std::size_t j{0}; j < tokens; ++j) {
361a0a1a4e5Speter klausler     CharBlock token{TokenAt(j)};
362a0a1a4e5Speter klausler     char ch{token.FirstNonBlank()};
363a0a1a4e5Speter klausler     if (ch != ' ' && !IsValidFortranTokenCharacter(ch)) {
364297230acSPeter Klausler       if (ch == '!') {
365297230acSPeter Klausler         if (prescanner.IsCompilerDirectiveSentinel(token)) {
366297230acSPeter Klausler           continue;
367297230acSPeter Klausler         } else if (j + 1 < tokens &&
368297230acSPeter Klausler             prescanner.IsCompilerDirectiveSentinel(
369297230acSPeter Klausler                 TokenAt(j + 1))) { // !dir$, &c.
370297230acSPeter Klausler           ++j;
371297230acSPeter Klausler           continue;
372297230acSPeter Klausler         }
373e286ecfeSPeter Klausler       } else if (ch == '&' && allowAmpersand) {
374e286ecfeSPeter Klausler         continue;
375297230acSPeter Klausler       }
376297230acSPeter Klausler       if (ch < ' ' || ch >= '\x7f') {
377a0a1a4e5Speter klausler         messages.Say(GetTokenProvenanceRange(j),
378a0a1a4e5Speter klausler             "bad character (0x%02x) in Fortran token"_err_en_US, ch & 0xff);
379a0a1a4e5Speter klausler       } else {
380a0a1a4e5Speter klausler         messages.Say(GetTokenProvenanceRange(j),
381a0a1a4e5Speter klausler             "bad character ('%c') in Fortran token"_err_en_US, ch);
382a0a1a4e5Speter klausler       }
383a0a1a4e5Speter klausler     }
384a0a1a4e5Speter klausler   }
385a0a1a4e5Speter klausler   return *this;
386a0a1a4e5Speter klausler }
387094b380cSpeter klausler 
38886bee819SPeter Klausler bool TokenSequence::BadlyNestedParentheses() const {
389094b380cSpeter klausler   int nesting{0};
390094b380cSpeter klausler   std::size_t tokens{SizeInTokens()};
391094b380cSpeter klausler   for (std::size_t j{0}; j < tokens; ++j) {
392094b380cSpeter klausler     CharBlock token{TokenAt(j)};
3936fac3f7bSPeter Klausler     char ch{token.OnlyNonBlank()};
394094b380cSpeter klausler     if (ch == '(') {
395094b380cSpeter klausler       ++nesting;
396094b380cSpeter klausler     } else if (ch == ')') {
3976fac3f7bSPeter Klausler       if (nesting-- == 0) {
3986fac3f7bSPeter Klausler         break;
3996fac3f7bSPeter Klausler       }
400094b380cSpeter klausler     }
401094b380cSpeter klausler   }
40286bee819SPeter Klausler   return nesting != 0;
40386bee819SPeter Klausler }
40486bee819SPeter Klausler 
40586bee819SPeter Klausler const TokenSequence &TokenSequence::CheckBadParentheses(
40686bee819SPeter Klausler     Messages &messages) const {
40786bee819SPeter Klausler   if (BadlyNestedParentheses()) {
408094b380cSpeter klausler     // There's an error; diagnose it
40986bee819SPeter Klausler     std::size_t tokens{SizeInTokens()};
410094b380cSpeter klausler     std::vector<std::size_t> stack;
411094b380cSpeter klausler     for (std::size_t j{0}; j < tokens; ++j) {
412094b380cSpeter klausler       CharBlock token{TokenAt(j)};
4136fac3f7bSPeter Klausler       char ch{token.OnlyNonBlank()};
414094b380cSpeter klausler       if (ch == '(') {
415094b380cSpeter klausler         stack.push_back(j);
416094b380cSpeter klausler       } else if (ch == ')') {
417094b380cSpeter klausler         if (stack.empty()) {
418094b380cSpeter klausler           messages.Say(GetTokenProvenanceRange(j), "Unmatched ')'"_err_en_US);
419094b380cSpeter klausler           return *this;
420094b380cSpeter klausler         }
421094b380cSpeter klausler         stack.pop_back();
422094b380cSpeter klausler       }
423094b380cSpeter klausler     }
424094b380cSpeter klausler     CHECK(!stack.empty());
425094b380cSpeter klausler     messages.Say(
426094b380cSpeter klausler         GetTokenProvenanceRange(stack.back()), "Unmatched '('"_err_en_US);
427094b380cSpeter klausler   }
428094b380cSpeter klausler   return *this;
429094b380cSpeter klausler }
4301f879005STim Keith } // namespace Fortran::parser
431