[flang] Revamp messaging. Add ""_err_en_US to denote fatal error messages.
Original-commit: flang-compiler/f18@7ddc807a1b Reviewed-on: https://github.com/flang-compiler/f18/pull/38 Tree-same-pre-rewrite: false
This commit is contained in:
@@ -29,7 +29,7 @@
|
||||
namespace Fortran {
|
||||
namespace parser {
|
||||
|
||||
// fail<A>("..."_en_US) returns a parser that never succeeds. It reports an
|
||||
// fail<A>("..."_err_en_US) returns a parser that never succeeds. It reports an
|
||||
// error message at the current position. The result type is unused,
|
||||
// but might have to be specified at the point of call for satisfy
|
||||
// the type checker. The state remains valid.
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
constexpr FailParser(const FailParser &) = default;
|
||||
constexpr explicit FailParser(MessageFixedText t) : text_{t} {}
|
||||
std::optional<A> Parse(ParseState *state) const {
|
||||
state->PutMessage(text_);
|
||||
state->Say(text_);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1188,7 +1188,7 @@ constexpr struct NextCh {
|
||||
constexpr NextCh() {}
|
||||
std::optional<const char *> Parse(ParseState *state) const {
|
||||
if (state->IsAtEnd()) {
|
||||
state->PutMessage("end of file"_en_US);
|
||||
state->Say("end of file"_err_en_US);
|
||||
return {};
|
||||
}
|
||||
const char *at{state->GetLocation()};
|
||||
@@ -1214,7 +1214,7 @@ public:
|
||||
if (result.has_value()) {
|
||||
state->set_anyConformanceViolation();
|
||||
if (state->warnOnNonstandardUsage()) {
|
||||
state->PutMessage(at, "nonstandard usage"_en_US);
|
||||
state->Say(at, "nonstandard usage"_en_US);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -1245,7 +1245,7 @@ public:
|
||||
if (result) {
|
||||
state->set_anyConformanceViolation();
|
||||
if (state->warnOnDeprecatedUsage()) {
|
||||
state->PutMessage(at, "deprecated usage"_en_US);
|
||||
state->Say(at, "deprecated usage"_en_US);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace parser {
|
||||
|
||||
enum class Encoding { UTF8, EUC_JP };
|
||||
|
||||
static inline constexpr bool IsUpperCaseLetter(char ch) {
|
||||
inline constexpr bool IsUpperCaseLetter(char ch) {
|
||||
if constexpr ('A' == static_cast<char>(0xc1)) {
|
||||
// EBCDIC
|
||||
return (ch >= 'A' && ch <= 'I') || (ch >= 'J' && ch <= 'R') ||
|
||||
@@ -24,7 +24,7 @@ static inline constexpr bool IsUpperCaseLetter(char ch) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline constexpr bool IsLowerCaseLetter(char ch) {
|
||||
inline constexpr bool IsLowerCaseLetter(char ch) {
|
||||
if constexpr ('a' == static_cast<char>(0x81)) {
|
||||
// EBCDIC
|
||||
return (ch >= 'a' && ch <= 'i') || (ch >= 'j' && ch <= 'r') ||
|
||||
@@ -34,40 +34,36 @@ static inline constexpr bool IsLowerCaseLetter(char ch) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline constexpr bool IsLetter(char ch) {
|
||||
inline constexpr bool IsLetter(char ch) {
|
||||
return IsUpperCaseLetter(ch) || IsLowerCaseLetter(ch);
|
||||
}
|
||||
|
||||
static inline constexpr bool IsDecimalDigit(char ch) {
|
||||
return ch >= '0' && ch <= '9';
|
||||
}
|
||||
inline constexpr bool IsDecimalDigit(char ch) { return ch >= '0' && ch <= '9'; }
|
||||
|
||||
static inline constexpr bool IsHexadecimalDigit(char ch) {
|
||||
inline constexpr bool IsHexadecimalDigit(char ch) {
|
||||
return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') ||
|
||||
(ch >= 'a' && ch <= 'f');
|
||||
}
|
||||
|
||||
static inline constexpr bool IsOctalDigit(char ch) {
|
||||
return ch >= '0' && ch <= '7';
|
||||
}
|
||||
inline constexpr bool IsOctalDigit(char ch) { return ch >= '0' && ch <= '7'; }
|
||||
|
||||
static inline constexpr bool IsLegalIdentifierStart(char ch) {
|
||||
inline constexpr bool IsLegalIdentifierStart(char ch) {
|
||||
return IsLetter(ch) || ch == '_' || ch == '@' || ch == '$';
|
||||
}
|
||||
|
||||
static inline constexpr bool IsLegalInIdentifier(char ch) {
|
||||
inline constexpr bool IsLegalInIdentifier(char ch) {
|
||||
return IsLegalIdentifierStart(ch) || IsDecimalDigit(ch);
|
||||
}
|
||||
|
||||
static inline constexpr char ToLowerCaseLetter(char ch) {
|
||||
inline constexpr char ToLowerCaseLetter(char ch) {
|
||||
return IsUpperCaseLetter(ch) ? ch - 'A' + 'a' : ch;
|
||||
}
|
||||
|
||||
static inline constexpr char ToLowerCaseLetter(char &&ch) {
|
||||
inline constexpr char ToLowerCaseLetter(char &&ch) {
|
||||
return IsUpperCaseLetter(ch) ? ch - 'A' + 'a' : ch;
|
||||
}
|
||||
|
||||
static inline std::string ToLowerCaseLetters(const std::string &str) {
|
||||
inline std::string ToLowerCaseLetters(const std::string &str) {
|
||||
std::string lowered{str};
|
||||
for (char &ch : lowered) {
|
||||
ch = ToLowerCaseLetter(ch);
|
||||
@@ -75,11 +71,11 @@ static inline std::string ToLowerCaseLetters(const std::string &str) {
|
||||
return lowered;
|
||||
}
|
||||
|
||||
static inline constexpr char ToUpperCaseLetter(char ch) {
|
||||
inline constexpr char ToUpperCaseLetter(char ch) {
|
||||
return IsLowerCaseLetter(ch) ? ch - 'a' + 'A' : ch;
|
||||
}
|
||||
|
||||
static inline constexpr char ToUpperCaseLetter(char &&ch) {
|
||||
inline constexpr char ToUpperCaseLetter(char &&ch) {
|
||||
return IsLowerCaseLetter(ch) ? ch - 'a' + 'A' : ch;
|
||||
}
|
||||
|
||||
@@ -91,19 +87,19 @@ static inline std::string ToUpperCaseLetters(const std::string &str) {
|
||||
return raised;
|
||||
}
|
||||
|
||||
static inline constexpr bool IsSameApartFromCase(char x, char y) {
|
||||
inline constexpr bool IsSameApartFromCase(char x, char y) {
|
||||
return ToLowerCaseLetter(x) == ToLowerCaseLetter(y);
|
||||
}
|
||||
|
||||
static inline constexpr char DecimalDigitValue(char ch) { return ch - '0'; }
|
||||
inline constexpr char DecimalDigitValue(char ch) { return ch - '0'; }
|
||||
|
||||
static inline constexpr char HexadecimalDigitValue(char ch) {
|
||||
inline constexpr char HexadecimalDigitValue(char ch) {
|
||||
return IsUpperCaseLetter(ch)
|
||||
? ch - 'A' + 10
|
||||
: IsLowerCaseLetter(ch) ? ch - 'a' + 10 : DecimalDigitValue(ch);
|
||||
}
|
||||
|
||||
static constexpr std::optional<char> BackslashEscapeValue(char ch) {
|
||||
constexpr std::optional<char> BackslashEscapeValue(char ch) {
|
||||
switch (ch) {
|
||||
// case 'a': return {'\a'}; pgf90 doesn't know about \a
|
||||
case 'b': return {'\b'};
|
||||
@@ -119,7 +115,7 @@ static constexpr std::optional<char> BackslashEscapeValue(char ch) {
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr std::optional<char> BackslashEscapeChar(char ch) {
|
||||
constexpr std::optional<char> BackslashEscapeChar(char ch) {
|
||||
switch (ch) {
|
||||
// case '\a': return {'a'}; pgf90 doesn't know about \a
|
||||
case '\b': return {'b'};
|
||||
|
||||
@@ -27,8 +27,8 @@ public:
|
||||
context->Emit(std::cout, cooked);
|
||||
}
|
||||
Provenance p{cooked.GetProvenance(state->GetLocation()).start()};
|
||||
cooked.allSources().Identify(std::cout, p, "");
|
||||
std::cout << " parser debug: " << std::string{str_, length_} << '\n';
|
||||
cooked.allSources().Identify(std::cout, p, "", true);
|
||||
std::cout << " parser debug: " << std::string{str_, length_} << "\n\n";
|
||||
return {Success{}};
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ std::string MessageFixedText::ToString() const {
|
||||
return std::string{str_, /*not in std::*/ strnlen(str_, bytes_)};
|
||||
}
|
||||
|
||||
MessageFormattedText::MessageFormattedText(MessageFixedText text, ...) {
|
||||
MessageFormattedText::MessageFormattedText(MessageFixedText text, ...)
|
||||
: isFatal_{text.isFatal()} {
|
||||
const char *p{text.str()};
|
||||
std::string asString;
|
||||
if (p[text.size()] != '\0') {
|
||||
@@ -59,13 +60,16 @@ Provenance Message::Emit(
|
||||
cooked.allSources().Identify(o, provenance, "", echoSourceLine);
|
||||
}
|
||||
o << " ";
|
||||
if (isFatal_) {
|
||||
o << "ERROR: ";
|
||||
}
|
||||
if (string_.empty()) {
|
||||
if (isExpectedText_) {
|
||||
std::string goal{text_.ToString()};
|
||||
if (goal == "\n") {
|
||||
o << "expected end of line"_en_US;
|
||||
o << "expected end of line"_err_en_US;
|
||||
} else {
|
||||
o << MessageFormattedText("expected '%s'"_en_US, goal.data())
|
||||
o << MessageFormattedText("expected '%s'"_err_en_US, goal.data())
|
||||
.MoveString();
|
||||
}
|
||||
} else {
|
||||
@@ -90,5 +94,14 @@ void Messages::Emit(
|
||||
msg.Emit(o, cooked_, echoSourceLines);
|
||||
}
|
||||
}
|
||||
|
||||
bool Messages::AnyFatalError() const {
|
||||
for (const auto &msg : messages_) {
|
||||
if (msg.isFatal()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace parser
|
||||
} // namespace Fortran
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
namespace Fortran {
|
||||
namespace parser {
|
||||
|
||||
// Use "..."_en_US literals to define the static text of a message.
|
||||
// Use "..."_err_en_US and "..."_en_US literals to define the static
|
||||
// text and fatality of a message.
|
||||
class MessageFixedText {
|
||||
public:
|
||||
MessageFixedText() {}
|
||||
constexpr MessageFixedText(const char str[], std::size_t n)
|
||||
: str_{str}, bytes_{n} {}
|
||||
constexpr MessageFixedText(
|
||||
const char str[], std::size_t n, bool isFatal = false)
|
||||
: str_{str}, bytes_{n}, isFatal_{isFatal} {}
|
||||
constexpr MessageFixedText(const MessageFixedText &) = default;
|
||||
MessageFixedText(MessageFixedText &&) = default;
|
||||
constexpr MessageFixedText &operator=(const MessageFixedText &) = default;
|
||||
@@ -30,16 +32,23 @@ public:
|
||||
const char *str() const { return str_; }
|
||||
std::size_t size() const { return bytes_; }
|
||||
bool empty() const { return bytes_ == 0; }
|
||||
bool isFatal() const { return isFatal_; }
|
||||
|
||||
std::string ToString() const;
|
||||
|
||||
private:
|
||||
const char *str_{nullptr};
|
||||
std::size_t bytes_{0};
|
||||
bool isFatal_{false};
|
||||
};
|
||||
|
||||
constexpr MessageFixedText operator""_en_US(const char str[], std::size_t n) {
|
||||
return MessageFixedText{str, n};
|
||||
return MessageFixedText{str, n, false /* not fatal */};
|
||||
}
|
||||
|
||||
constexpr MessageFixedText operator""_err_en_US(
|
||||
const char str[], std::size_t n) {
|
||||
return MessageFixedText{str, n, true /* fatal */};
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &, const MessageFixedText &);
|
||||
@@ -48,12 +57,15 @@ class MessageFormattedText {
|
||||
public:
|
||||
MessageFormattedText(MessageFixedText, ...);
|
||||
std::string MoveString() { return std::move(string_); }
|
||||
bool isFatal() const { return isFatal_; }
|
||||
|
||||
private:
|
||||
std::string string_;
|
||||
bool isFatal_{false};
|
||||
};
|
||||
|
||||
// Represents a formatted rendition of "expected '%s'"_en_US on a constant text.
|
||||
// Represents a formatted rendition of "expected '%s'"_err_en_US
|
||||
// on a constant text.
|
||||
class MessageExpectedText {
|
||||
public:
|
||||
MessageExpectedText(const char *s, std::size_t n) : str_{s}, bytes_{n} {}
|
||||
@@ -78,20 +90,23 @@ public:
|
||||
Message &operator=(Message &&that) = default;
|
||||
|
||||
Message(Provenance p, MessageFixedText t, MessageContext c = nullptr)
|
||||
: provenance_{p}, text_{t}, context_{c} {}
|
||||
: provenance_{p}, text_{t}, context_{c}, isFatal_{t.isFatal()} {}
|
||||
Message(Provenance p, MessageFormattedText &&s, MessageContext c = nullptr)
|
||||
: provenance_{p}, string_{s.MoveString()}, context_{c} {}
|
||||
: provenance_{p}, string_{s.MoveString()}, context_{c}, isFatal_{
|
||||
s.isFatal()} {}
|
||||
Message(Provenance p, MessageExpectedText t, MessageContext c = nullptr)
|
||||
: provenance_{p}, text_{t.AsMessageFixedText()},
|
||||
isExpectedText_{true}, context_{c} {}
|
||||
isExpectedText_{true}, context_{c}, isFatal_{true} {}
|
||||
|
||||
Message(const char *csl, MessageFixedText t, MessageContext c = nullptr)
|
||||
: cookedSourceLocation_{csl}, text_{t}, context_{c} {}
|
||||
: cookedSourceLocation_{csl}, text_{t}, context_{c}, isFatal_{t.isFatal()} {
|
||||
}
|
||||
Message(const char *csl, MessageFormattedText &&s, MessageContext c = nullptr)
|
||||
: cookedSourceLocation_{csl}, string_{s.MoveString()}, context_{c} {}
|
||||
: cookedSourceLocation_{csl}, string_{s.MoveString()}, context_{c},
|
||||
isFatal_{s.isFatal()} {}
|
||||
Message(const char *csl, MessageExpectedText t, MessageContext c = nullptr)
|
||||
: cookedSourceLocation_{csl}, text_{t.AsMessageFixedText()},
|
||||
isExpectedText_{true}, context_{c} {}
|
||||
isExpectedText_{true}, context_{c}, isFatal_{true} {}
|
||||
|
||||
bool operator<(const Message &that) const {
|
||||
if (cookedSourceLocation_ != nullptr) {
|
||||
@@ -106,6 +121,7 @@ public:
|
||||
Provenance provenance() const { return provenance_; }
|
||||
const char *cookedSourceLocation() const { return cookedSourceLocation_; }
|
||||
MessageContext context() const { return context_; }
|
||||
bool isFatal() const { return isFatal_; }
|
||||
|
||||
Provenance Emit(
|
||||
std::ostream &, const CookedSource &, bool echoSourceLine = true) const;
|
||||
@@ -114,9 +130,10 @@ private:
|
||||
Provenance provenance_;
|
||||
const char *cookedSourceLocation_{nullptr};
|
||||
MessageFixedText text_;
|
||||
bool isExpectedText_{false}; // implies "expected '%s'"_en_US
|
||||
bool isExpectedText_{false}; // implies "expected '%s'"_err_en_US
|
||||
std::string string_;
|
||||
MessageContext context_;
|
||||
bool isFatal_{false};
|
||||
};
|
||||
|
||||
class Messages {
|
||||
@@ -183,6 +200,8 @@ public:
|
||||
void Emit(std::ostream &, const char *prefix = nullptr,
|
||||
bool echoSourceLines = true) const;
|
||||
|
||||
bool AnyFatalError() const;
|
||||
|
||||
private:
|
||||
const CookedSource &cooked_;
|
||||
list_type messages_;
|
||||
|
||||
@@ -118,21 +118,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
Message &PutMessage(MessageFixedText t) { return PutMessage(p_, t); }
|
||||
Message &PutMessage(MessageFormattedText &&t) {
|
||||
return PutMessage(p_, std::move(t));
|
||||
}
|
||||
Message &PutMessage(MessageExpectedText &&t) {
|
||||
return PutMessage(p_, std::move(t));
|
||||
}
|
||||
Message &Say(MessageFixedText t) { return Say(p_, t); }
|
||||
Message &Say(MessageFormattedText &&t) { return Say(p_, std::move(t)); }
|
||||
Message &Say(MessageExpectedText &&t) { return Say(p_, std::move(t)); }
|
||||
|
||||
Message &PutMessage(const char *at, MessageFixedText t) {
|
||||
Message &Say(const char *at, MessageFixedText t) {
|
||||
return messages_.Put(Message{at, t, context_});
|
||||
}
|
||||
Message &PutMessage(const char *at, MessageFormattedText &&t) {
|
||||
Message &Say(const char *at, MessageFormattedText &&t) {
|
||||
return messages_.Put(Message{at, std::move(t), context_});
|
||||
}
|
||||
Message &PutMessage(const char *at, MessageExpectedText &&t) {
|
||||
Message &Say(const char *at, MessageExpectedText &&t) {
|
||||
return messages_.Put(Message{at, std::move(t), context_});
|
||||
}
|
||||
|
||||
|
||||
@@ -10,23 +10,21 @@
|
||||
namespace Fortran {
|
||||
namespace parser {
|
||||
|
||||
bool Parsing::Prescan(const std::string &path, Options options) {
|
||||
void Parsing::Prescan(const std::string &path, Options options) {
|
||||
options_ = options;
|
||||
|
||||
std::stringstream fileError;
|
||||
const auto *sourceFile = allSources_.Open(path, &fileError);
|
||||
if (sourceFile == nullptr) {
|
||||
ProvenanceRange range{allSources_.AddCompilerInsertion(path)};
|
||||
MessageFormattedText msg("%s"_en_US, fileError.str().data());
|
||||
MessageFormattedText msg("%s"_err_en_US, fileError.str().data());
|
||||
messages_.Put(Message(range.start(), std::move(msg)));
|
||||
anyFatalError_ = true;
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (sourceFile->bytes() == 0) {
|
||||
ProvenanceRange range{allSources_.AddCompilerInsertion(path)};
|
||||
messages_.Put(Message{range.start(), "file is empty"_en_US});
|
||||
anyFatalError_ = true;
|
||||
return false;
|
||||
messages_.Put(Message{range.start(), "file is empty"_err_en_US});
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &path : options.searchDirectories) {
|
||||
@@ -51,19 +49,11 @@ bool Parsing::Prescan(const std::string &path, Options options) {
|
||||
.AddCompilerDirectiveSentinel("dir$");
|
||||
ProvenanceRange range{
|
||||
allSources_.AddIncludedFile(*sourceFile, ProvenanceRange{})};
|
||||
anyFatalError_ = !prescanner.Prescan(range);
|
||||
if (anyFatalError_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prescanner.Prescan(range);
|
||||
cooked_.Marshal();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Parsing::DumpCookedChars(std::ostream &out) const {
|
||||
if (anyFatalError_) {
|
||||
return;
|
||||
}
|
||||
UserState userState;
|
||||
ParseState parseState{cooked_};
|
||||
parseState.set_inFixedForm(options_.isFixedForm).set_userState(&userState);
|
||||
@@ -74,10 +64,7 @@ void Parsing::DumpCookedChars(std::ostream &out) const {
|
||||
|
||||
void Parsing::DumpProvenance(std::ostream &out) const { cooked_.Dump(out); }
|
||||
|
||||
bool Parsing::Parse() {
|
||||
if (anyFatalError_) {
|
||||
return false;
|
||||
}
|
||||
void Parsing::Parse() {
|
||||
UserState userState;
|
||||
ParseState parseState{cooked_};
|
||||
parseState.set_inFixedForm(options_.isFixedForm)
|
||||
@@ -86,11 +73,34 @@ bool Parsing::Parse() {
|
||||
.set_warnOnDeprecatedUsage(options_.isStrictlyStandard)
|
||||
.set_userState(&userState);
|
||||
parseTree_ = program.Parse(&parseState);
|
||||
anyFatalError_ = parseState.anyErrorRecovery();
|
||||
CHECK(
|
||||
!parseState.anyErrorRecovery() || parseState.messages()->AnyFatalError());
|
||||
consumedWholeFile_ = parseState.IsAtEnd();
|
||||
finalRestingPlace_ = parseState.GetLocation();
|
||||
messages_.Annex(parseState.messages());
|
||||
return parseTree_.has_value() && !anyFatalError_;
|
||||
}
|
||||
|
||||
std::optional<Program> Parsing::ForTesting(
|
||||
std::string path, std::ostream &err) {
|
||||
Parsing parsing;
|
||||
parsing.Prescan(path, Options{});
|
||||
if (parsing.messages().AnyFatalError()) {
|
||||
parsing.messages().Emit(err);
|
||||
err << "could not scan " << path << '\n';
|
||||
return {};
|
||||
}
|
||||
parsing.Parse();
|
||||
parsing.messages().Emit(err);
|
||||
if (!parsing.consumedWholeFile()) {
|
||||
err << "f18 parser FAIL; final position: ";
|
||||
parsing.Identify(err, parsing.finalRestingPlace(), " ");
|
||||
return {};
|
||||
}
|
||||
if (parsing.messages().AnyFatalError()) {
|
||||
err << "could not parse " << path << '\n';
|
||||
return {};
|
||||
}
|
||||
return std::move(parsing.parseTree());
|
||||
}
|
||||
} // namespace parser
|
||||
} // namespace Fortran
|
||||
|
||||
@@ -36,12 +36,12 @@ public:
|
||||
bool consumedWholeFile() const { return consumedWholeFile_; }
|
||||
const char *finalRestingPlace() const { return finalRestingPlace_; }
|
||||
Messages &messages() { return messages_; }
|
||||
Program &parseTree() { return *parseTree_; }
|
||||
std::optional<Program> &parseTree() { return parseTree_; }
|
||||
|
||||
bool Prescan(const std::string &path, Options);
|
||||
void Prescan(const std::string &path, Options);
|
||||
void DumpCookedChars(std::ostream &) const;
|
||||
void DumpProvenance(std::ostream &) const;
|
||||
bool Parse();
|
||||
void Parse();
|
||||
|
||||
void Identify(std::ostream &o, const char *at, const std::string &prefix,
|
||||
bool echoSourceLine = false) const {
|
||||
@@ -49,12 +49,13 @@ public:
|
||||
o, cooked_.GetProvenance(at).start(), prefix, echoSourceLine);
|
||||
}
|
||||
|
||||
static std::optional<Program> ForTesting(std::string path, std::ostream &);
|
||||
|
||||
private:
|
||||
Options options_;
|
||||
AllSources allSources_;
|
||||
CookedSource cooked_{allSources_};
|
||||
Messages messages_{cooked_};
|
||||
bool anyFatalError_{false};
|
||||
bool consumedWholeFile_{false};
|
||||
const char *finalRestingPlace_{nullptr};
|
||||
std::optional<Program> parseTree_;
|
||||
|
||||
@@ -369,7 +369,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||
return;
|
||||
}
|
||||
if (dir.TokenAt(j).ToString() != "#") {
|
||||
prescanner->Error("missing '#'"_en_US, dir.GetTokenProvenance(j));
|
||||
prescanner->Say("missing '#'"_err_en_US, dir.GetTokenProvenance(j));
|
||||
return;
|
||||
}
|
||||
j = SkipBlanks(dir, j + 1, tokens);
|
||||
@@ -390,7 +390,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||
// TODO: implement #line
|
||||
} else if (dirName == "define") {
|
||||
if (nameToken.empty()) {
|
||||
prescanner->Error("#define: missing or invalid name"_en_US,
|
||||
prescanner->Say("#define: missing or invalid name"_err_en_US,
|
||||
dir.GetTokenProvenance(j < tokens ? j : tokens - 1));
|
||||
return;
|
||||
}
|
||||
@@ -408,8 +408,8 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||
isVariadic = true;
|
||||
} else {
|
||||
if (an.empty() || !IsLegalIdentifierStart(an[0])) {
|
||||
prescanner->Error(
|
||||
"#define: missing or invalid argument name"_en_US,
|
||||
prescanner->Say(
|
||||
"#define: missing or invalid argument name"_err_en_US,
|
||||
dir.GetTokenProvenance(j));
|
||||
return;
|
||||
}
|
||||
@@ -417,7 +417,7 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||
}
|
||||
j = SkipBlanks(dir, j + 1, tokens);
|
||||
if (j == tokens) {
|
||||
prescanner->Error("#define: malformed argument list"_en_US,
|
||||
prescanner->Say("#define: malformed argument list"_err_en_US,
|
||||
dir.GetTokenProvenance(tokens - 1));
|
||||
return;
|
||||
}
|
||||
@@ -426,20 +426,20 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||
break;
|
||||
}
|
||||
if (isVariadic || punc != ",") {
|
||||
prescanner->Error("#define: malformed argument list"_en_US,
|
||||
prescanner->Say("#define: malformed argument list"_err_en_US,
|
||||
dir.GetTokenProvenance(j));
|
||||
return;
|
||||
}
|
||||
j = SkipBlanks(dir, j + 1, tokens);
|
||||
if (j == tokens) {
|
||||
prescanner->Error("#define: malformed argument list"_en_US,
|
||||
prescanner->Say("#define: malformed argument list"_err_en_US,
|
||||
dir.GetTokenProvenance(tokens - 1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (std::set<std::string>(argName.begin(), argName.end()).size() !=
|
||||
argName.size()) {
|
||||
prescanner->Error("#define: argument names are not distinct"_en_US,
|
||||
prescanner->Say("#define: argument names are not distinct"_err_en_US,
|
||||
dir.GetTokenProvenance(dirOffset));
|
||||
return;
|
||||
}
|
||||
@@ -454,12 +454,12 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||
}
|
||||
} else if (dirName == "undef") {
|
||||
if (nameToken.empty()) {
|
||||
prescanner->Error("# missing or invalid name"_en_US,
|
||||
prescanner->Say("# missing or invalid name"_err_en_US,
|
||||
dir.GetTokenProvenance(tokens - 1));
|
||||
} else {
|
||||
j = SkipBlanks(dir, j + 1, tokens);
|
||||
if (j != tokens) {
|
||||
prescanner->Error("#undef: excess tokens at end of directive"_en_US,
|
||||
prescanner->Say("#undef: excess tokens at end of directive"_err_en_US,
|
||||
dir.GetTokenProvenance(j));
|
||||
} else {
|
||||
definitions_.erase(nameToken);
|
||||
@@ -467,16 +467,16 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||
}
|
||||
} else if (dirName == "ifdef" || dirName == "ifndef") {
|
||||
if (nameToken.empty()) {
|
||||
prescanner->Error(
|
||||
MessageFormattedText("#%s: missing name"_en_US, dirName.data()),
|
||||
prescanner->Say(
|
||||
MessageFormattedText("#%s: missing name"_err_en_US, dirName.data()),
|
||||
dir.GetTokenProvenance(tokens - 1));
|
||||
return;
|
||||
}
|
||||
j = SkipBlanks(dir, j + 1, tokens);
|
||||
if (j != tokens) {
|
||||
prescanner->Error(
|
||||
MessageFormattedText(
|
||||
"#%s: excess tokens at end of directive"_en_US, dirName.data()),
|
||||
prescanner->Say(MessageFormattedText(
|
||||
"#%s: excess tokens at end of directive"_err_en_US,
|
||||
dirName.data()),
|
||||
dir.GetTokenProvenance(j));
|
||||
} else if (IsNameDefined(nameToken) == (dirName == "ifdef")) {
|
||||
ifStack_.push(CanDeadElseAppear::Yes);
|
||||
@@ -493,15 +493,15 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||
}
|
||||
} else if (dirName == "else") {
|
||||
if (j != tokens) {
|
||||
prescanner->Error("#else: excess tokens at end of directive"_en_US,
|
||||
prescanner->Say("#else: excess tokens at end of directive"_err_en_US,
|
||||
dir.GetTokenProvenance(j));
|
||||
} else if (ifStack_.empty()) {
|
||||
prescanner->Error(
|
||||
"#else: not nested within #if, #ifdef, or #ifndef"_en_US,
|
||||
prescanner->Say(
|
||||
"#else: not nested within #if, #ifdef, or #ifndef"_err_en_US,
|
||||
dir.GetTokenProvenance(tokens - 1));
|
||||
} else if (ifStack_.top() != CanDeadElseAppear::Yes) {
|
||||
prescanner->Error(
|
||||
"#else: already appeared within this #if, #ifdef, or #ifndef"_en_US,
|
||||
prescanner->Say(
|
||||
"#else: already appeared within this #if, #ifdef, or #ifndef"_err_en_US,
|
||||
dir.GetTokenProvenance(tokens - 1));
|
||||
} else {
|
||||
ifStack_.pop();
|
||||
@@ -510,12 +510,12 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||
}
|
||||
} else if (dirName == "elif") {
|
||||
if (ifStack_.empty()) {
|
||||
prescanner->Error(
|
||||
"#elif: not nested within #if, #ifdef, or #ifndef"_en_US,
|
||||
prescanner->Say(
|
||||
"#elif: not nested within #if, #ifdef, or #ifndef"_err_en_US,
|
||||
dir.GetTokenProvenance(tokens - 1));
|
||||
} else if (ifStack_.top() != CanDeadElseAppear::Yes) {
|
||||
prescanner->Error("#elif: #else previously appeared within this "
|
||||
"#if, #ifdef, or #ifndef"_en_US,
|
||||
prescanner->Say("#elif: #else previously appeared within this "
|
||||
"#if, #ifdef, or #ifndef"_err_en_US,
|
||||
dir.GetTokenProvenance(tokens - 1));
|
||||
} else {
|
||||
ifStack_.pop();
|
||||
@@ -524,32 +524,32 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||
}
|
||||
} else if (dirName == "endif") {
|
||||
if (j != tokens) {
|
||||
prescanner->Error("#endif: excess tokens at end of directive"_en_US,
|
||||
prescanner->Say("#endif: excess tokens at end of directive"_err_en_US,
|
||||
dir.GetTokenProvenance(j));
|
||||
} else if (ifStack_.empty()) {
|
||||
prescanner->Error("#endif: no #if, #ifdef, or #ifndef"_en_US,
|
||||
prescanner->Say("#endif: no #if, #ifdef, or #ifndef"_err_en_US,
|
||||
dir.GetTokenProvenance(tokens - 1));
|
||||
} else {
|
||||
ifStack_.pop();
|
||||
}
|
||||
} else if (dirName == "error") {
|
||||
prescanner->Error(
|
||||
MessageFormattedText("#error: %s"_en_US, dir.ToString().data()),
|
||||
prescanner->Say(
|
||||
MessageFormattedText("#error: %s"_err_en_US, dir.ToString().data()),
|
||||
dir.GetTokenProvenance(dirOffset));
|
||||
} else if (dirName == "warning") {
|
||||
prescanner->Complain(
|
||||
prescanner->Say(
|
||||
MessageFormattedText("#warning: %s"_en_US, dir.ToString().data()),
|
||||
dir.GetTokenProvenance(dirOffset));
|
||||
} else if (dirName == "include") {
|
||||
if (j == tokens) {
|
||||
prescanner->Error("#include: missing name of file to include"_en_US,
|
||||
prescanner->Say("#include: missing name of file to include"_err_en_US,
|
||||
dir.GetTokenProvenance(tokens - 1));
|
||||
return;
|
||||
}
|
||||
std::string include;
|
||||
if (dir.TokenAt(j).ToString() == "<") {
|
||||
if (dir.TokenAt(tokens - 1).ToString() != ">") {
|
||||
prescanner->Error("#include: expected '>' at end of directive"_en_US,
|
||||
prescanner->Say("#include: expected '>' at end of directive"_err_en_US,
|
||||
dir.GetTokenProvenance(tokens - 1));
|
||||
return;
|
||||
}
|
||||
@@ -560,20 +560,20 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||
include.substr(include.size() - 1, 1) == "\"") {
|
||||
include = include.substr(1, include.size() - 2);
|
||||
} else {
|
||||
prescanner->Error("#include: expected name of file to include"_en_US,
|
||||
prescanner->Say("#include: expected name of file to include"_err_en_US,
|
||||
dir.GetTokenProvenance(j < tokens ? j : tokens - 1));
|
||||
return;
|
||||
}
|
||||
if (include.empty()) {
|
||||
prescanner->Error("#include: empty include file name"_en_US,
|
||||
prescanner->Say("#include: empty include file name"_err_en_US,
|
||||
dir.GetTokenProvenance(dirOffset));
|
||||
return;
|
||||
}
|
||||
std::stringstream error;
|
||||
const SourceFile *included{allSources_.Open(include, &error)};
|
||||
if (included == nullptr) {
|
||||
prescanner->Error(
|
||||
MessageFormattedText("#include: %s"_en_US, error.str().data()),
|
||||
prescanner->Say(
|
||||
MessageFormattedText("#include: %s"_err_en_US, error.str().data()),
|
||||
dir.GetTokenProvenance(dirOffset));
|
||||
return;
|
||||
}
|
||||
@@ -582,13 +582,11 @@ void Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) {
|
||||
}
|
||||
ProvenanceRange fileRange{
|
||||
allSources_.AddIncludedFile(*included, dir.GetProvenanceRange())};
|
||||
if (!Prescanner{*prescanner}.Prescan(fileRange)) {
|
||||
prescanner->set_anyFatalErrors();
|
||||
}
|
||||
Prescanner{*prescanner}.Prescan(fileRange);
|
||||
} else {
|
||||
prescanner->Error(
|
||||
MessageFormattedText(
|
||||
"#%s: unknown or unimplemented directive"_en_US, dirName.data()),
|
||||
prescanner->Say(MessageFormattedText(
|
||||
"#%s: unknown or unimplemented directive"_err_en_US,
|
||||
dirName.data()),
|
||||
dir.GetTokenProvenance(dirOffset));
|
||||
}
|
||||
}
|
||||
@@ -649,8 +647,8 @@ void Preprocessor::SkipDisabledConditionalCode(const std::string &dirName,
|
||||
}
|
||||
}
|
||||
}
|
||||
prescanner->Error(
|
||||
MessageFormattedText("#%s: missing #endif"_en_US, dirName.data()),
|
||||
prescanner->Say(
|
||||
MessageFormattedText("#%s: missing #endif"_err_en_US, dirName.data()),
|
||||
provenance);
|
||||
}
|
||||
|
||||
@@ -751,8 +749,8 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
|
||||
|
||||
std::size_t tokens{token.SizeInTokens()};
|
||||
if (*atToken >= tokens) {
|
||||
*error = Message{
|
||||
token.GetTokenProvenance(tokens - 1), "incomplete expression"_en_US};
|
||||
*error = Message{token.GetTokenProvenance(tokens - 1),
|
||||
"incomplete expression"_err_en_US};
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -770,7 +768,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
|
||||
if (consumed < t.size()) {
|
||||
*error = Message{token.GetTokenProvenance(opAt),
|
||||
MessageFormattedText(
|
||||
"uninterpretable numeric constant '%s'"_en_US, t.data())};
|
||||
"uninterpretable numeric constant '%s'"_err_en_US, t.data())};
|
||||
return 0;
|
||||
}
|
||||
} else if (IsLegalIdentifierStart(t[0])) {
|
||||
@@ -792,13 +790,13 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
|
||||
op = it->second;
|
||||
} else {
|
||||
*error = Message{token.GetTokenProvenance(tokens - 1),
|
||||
"operand expected in expression"_en_US};
|
||||
"operand expected in expression"_err_en_US};
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (precedence[op] < minimumPrecedence) {
|
||||
*error = Message{
|
||||
token.GetTokenProvenance(opAt), "operator precedence error"_en_US};
|
||||
token.GetTokenProvenance(opAt), "operator precedence error"_err_en_US};
|
||||
return 0;
|
||||
}
|
||||
++*atToken;
|
||||
@@ -813,7 +811,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
|
||||
++*atToken;
|
||||
} else {
|
||||
*error = Message{token.GetTokenProvenance(tokens - 1),
|
||||
"')' missing from expression"_en_US};
|
||||
"')' missing from expression"_err_en_US};
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
@@ -855,8 +853,8 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
|
||||
switch (op) {
|
||||
case POWER:
|
||||
if (left == 0 && right < 0) {
|
||||
*error =
|
||||
Message{token.GetTokenProvenance(opAt), "0 ** negative power"_en_US};
|
||||
*error = Message{
|
||||
token.GetTokenProvenance(opAt), "0 ** negative power"_err_en_US};
|
||||
return 0;
|
||||
}
|
||||
if (left == 0 || left == 1 || right == 1) {
|
||||
@@ -870,7 +868,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
|
||||
for (; right > 0; --right) {
|
||||
if ((power * left) / left != power) {
|
||||
*error = Message{token.GetTokenProvenance(opAt),
|
||||
"overflow in exponentation"_en_US};
|
||||
"overflow in exponentation"_err_en_US};
|
||||
return 0;
|
||||
}
|
||||
power *= left;
|
||||
@@ -882,45 +880,46 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
|
||||
return 0;
|
||||
}
|
||||
if ((left * right) / left != right) {
|
||||
*error = Message{
|
||||
token.GetTokenProvenance(opAt), "overflow in multiplication"_en_US};
|
||||
*error = Message{token.GetTokenProvenance(opAt),
|
||||
"overflow in multiplication"_err_en_US};
|
||||
}
|
||||
return left * right;
|
||||
case DIVIDE:
|
||||
if (right == 0) {
|
||||
*error =
|
||||
Message{token.GetTokenProvenance(opAt), "division by zero"_en_US};
|
||||
Message{token.GetTokenProvenance(opAt), "division by zero"_err_en_US};
|
||||
return 0;
|
||||
}
|
||||
return left / right;
|
||||
case MODULUS:
|
||||
if (right == 0) {
|
||||
*error = Message{token.GetTokenProvenance(opAt), "modulus by zero"_en_US};
|
||||
*error =
|
||||
Message{token.GetTokenProvenance(opAt), "modulus by zero"_err_en_US};
|
||||
return 0;
|
||||
}
|
||||
return left % right;
|
||||
case ADD:
|
||||
if ((left < 0) == (right < 0) && (left < 0) != (left + right < 0)) {
|
||||
*error =
|
||||
Message{token.GetTokenProvenance(opAt), "overflow in addition"_en_US};
|
||||
*error = Message{
|
||||
token.GetTokenProvenance(opAt), "overflow in addition"_err_en_US};
|
||||
}
|
||||
return left + right;
|
||||
case SUBTRACT:
|
||||
if ((left < 0) != (right < 0) && (left < 0) == (left - right < 0)) {
|
||||
*error = Message{
|
||||
token.GetTokenProvenance(opAt), "overflow in subtraction"_en_US};
|
||||
token.GetTokenProvenance(opAt), "overflow in subtraction"_err_en_US};
|
||||
}
|
||||
return left - right;
|
||||
case LEFTSHIFT:
|
||||
if (right < 0 || right > 64) {
|
||||
*error =
|
||||
Message{token.GetTokenProvenance(opAt), "bad left shift count"_en_US};
|
||||
*error = Message{
|
||||
token.GetTokenProvenance(opAt), "bad left shift count"_err_en_US};
|
||||
}
|
||||
return right >= 64 ? 0 : left << right;
|
||||
case RIGHTSHIFT:
|
||||
if (right < 0 || right > 64) {
|
||||
*error = Message{
|
||||
token.GetTokenProvenance(opAt), "bad right shift count"_en_US};
|
||||
token.GetTokenProvenance(opAt), "bad right shift count"_err_en_US};
|
||||
}
|
||||
return right >= 64 ? 0 : left >> right;
|
||||
case BITAND:
|
||||
@@ -939,7 +938,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token,
|
||||
case SELECT:
|
||||
if (*atToken >= tokens || token.TokenAt(*atToken).ToString() != ":") {
|
||||
*error = Message{token.GetTokenProvenance(opAt),
|
||||
"':' required in selection expression"_en_US};
|
||||
"':' required in selection expression"_err_en_US};
|
||||
return left;
|
||||
} else {
|
||||
++*atToken;
|
||||
@@ -983,10 +982,11 @@ bool Preprocessor::IsIfPredicateTrue(const TokenSequence &expr,
|
||||
std::optional<Message> error;
|
||||
bool result{ExpressionValue(expr4, 0, &atToken, &error) != 0};
|
||||
if (error.has_value()) {
|
||||
prescanner->Error(std::move(*error));
|
||||
prescanner->Say(std::move(*error));
|
||||
} else if (atToken < expr4.SizeInTokens()) {
|
||||
prescanner->Error(atToken == 0 ? "could not parse any expression"_en_US
|
||||
: "excess characters after expression"_en_US,
|
||||
prescanner->Say(atToken == 0
|
||||
? "could not parse any expression"_err_en_US
|
||||
: "excess characters after expression"_err_en_US,
|
||||
expr4.GetTokenProvenance(atToken));
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -42,7 +42,7 @@ static void NormalizeCompilerDirectiveCommentMarker(TokenSequence *dir) {
|
||||
CHECK(!"compiler directive all blank");
|
||||
}
|
||||
|
||||
bool Prescanner::Prescan(ProvenanceRange range) {
|
||||
void Prescanner::Prescan(ProvenanceRange range) {
|
||||
AllSources &allSources{cooked_.allSources()};
|
||||
ProvenanceRange around{allSources.GetContiguousRangeAround(range)};
|
||||
startProvenance_ = range.start();
|
||||
@@ -67,7 +67,6 @@ bool Prescanner::Prescan(ProvenanceRange range) {
|
||||
TokenSequence tokens{dir, allSources.AddCompilerInsertion(dir).start()};
|
||||
tokens.Emit(&cooked_);
|
||||
}
|
||||
return !anyFatalErrors_;
|
||||
}
|
||||
|
||||
void Prescanner::Statement() {
|
||||
@@ -131,7 +130,7 @@ void Prescanner::Statement() {
|
||||
FortranInclude(ppd + ppl.payloadOffset);
|
||||
break;
|
||||
case LineClassification::Kind::PreprocessorDirective:
|
||||
Complain("preprocessed line looks like a preprocessor directive"_en_US,
|
||||
Say("preprocessed line looks like a preprocessor directive"_en_US,
|
||||
preprocessed->GetProvenanceRange().start());
|
||||
preprocessed->ToLowerCase().Emit(&cooked_);
|
||||
break;
|
||||
@@ -169,31 +168,14 @@ TokenSequence Prescanner::TokenizePreprocessorDirective() {
|
||||
return {std::move(tokens)};
|
||||
}
|
||||
|
||||
Message &Prescanner::Error(Message &&message) {
|
||||
anyFatalErrors_ = true;
|
||||
return messages_.Put(std::move(message));
|
||||
void Prescanner::Say(Message &&message) { messages_.Put(std::move(message)); }
|
||||
|
||||
void Prescanner::Say(MessageFixedText text, Provenance p) {
|
||||
messages_.Put({p, text});
|
||||
}
|
||||
|
||||
Message &Prescanner::Error(MessageFixedText text, Provenance p) {
|
||||
anyFatalErrors_ = true;
|
||||
return messages_.Put({p, text});
|
||||
}
|
||||
|
||||
Message &Prescanner::Error(MessageFormattedText &&text, Provenance p) {
|
||||
anyFatalErrors_ = true;
|
||||
return messages_.Put({p, std::move(text)});
|
||||
}
|
||||
|
||||
Message &Prescanner::Complain(Message &&message) {
|
||||
return messages_.Put(std::move(message));
|
||||
}
|
||||
|
||||
Message &Prescanner::Complain(MessageFixedText text, Provenance p) {
|
||||
return messages_.Put({p, text});
|
||||
}
|
||||
|
||||
Message &Prescanner::Complain(MessageFormattedText &&text, Provenance p) {
|
||||
return messages_.Put({p, std::move(text)});
|
||||
void Prescanner::Say(MessageFormattedText &&text, Provenance p) {
|
||||
messages_.Put({p, std::move(text)});
|
||||
}
|
||||
|
||||
void Prescanner::NextLine() {
|
||||
@@ -424,7 +406,7 @@ void Prescanner::QuotedCharacterLiteral(TokenSequence *tokens) {
|
||||
}
|
||||
if (*at_ == '\n') {
|
||||
if (!inPreprocessorDirective_) {
|
||||
Error("incomplete character literal"_en_US, GetProvenance(start));
|
||||
Say("incomplete character literal"_err_en_US, GetProvenance(start));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -474,7 +456,7 @@ void Prescanner::Hollerith(TokenSequence *tokens, int count) {
|
||||
}
|
||||
if (*at_ == '\n') {
|
||||
if (!inPreprocessorDirective_) {
|
||||
Error("incomplete Hollerith literal"_en_US, GetProvenance(start));
|
||||
Say("incomplete Hollerith literal"_err_en_US, GetProvenance(start));
|
||||
}
|
||||
} else {
|
||||
NextChar();
|
||||
@@ -575,13 +557,13 @@ bool Prescanner::FortranInclude(const char *firstQuote) {
|
||||
path += *p;
|
||||
}
|
||||
if (*p != quote) {
|
||||
Error("malformed path name string"_en_US, GetProvenance(p));
|
||||
Say("malformed path name string"_err_en_US, GetProvenance(p));
|
||||
return true;
|
||||
}
|
||||
for (++p; *p == ' ' || *p == '\t'; ++p) {
|
||||
}
|
||||
if (*p != '\n' && *p != '!') {
|
||||
Complain("excess characters after path name"_en_US, GetProvenance(p));
|
||||
Say("excess characters after path name"_en_US, GetProvenance(p));
|
||||
}
|
||||
std::stringstream error;
|
||||
Provenance provenance{GetProvenance(lineStart_)};
|
||||
@@ -595,7 +577,7 @@ bool Prescanner::FortranInclude(const char *firstQuote) {
|
||||
allSources.PopSearchPathDirectory();
|
||||
}
|
||||
if (included == nullptr) {
|
||||
Error(MessageFormattedText("INCLUDE: %s"_en_US, error.str().data()),
|
||||
Say(MessageFormattedText("INCLUDE: %s"_err_en_US, error.str().data()),
|
||||
provenance);
|
||||
return true;
|
||||
}
|
||||
@@ -606,7 +588,7 @@ bool Prescanner::FortranInclude(const char *firstQuote) {
|
||||
provenance, static_cast<std::size_t>(p - lineStart_)};
|
||||
ProvenanceRange fileRange{
|
||||
allSources.AddIncludedFile(*included, includeLineRange)};
|
||||
anyFatalErrors_ |= !Prescanner{*this}.Prescan(fileRange);
|
||||
Prescanner{*this}.Prescan(fileRange);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -672,7 +654,7 @@ const char *Prescanner::FixedFormContinuationLine() {
|
||||
if (col1 == '&') {
|
||||
// Extension: '&' as continuation marker
|
||||
if (warnOnNonstandardUsage_) {
|
||||
Complain("nonstandard usage"_en_US, GetProvenance(lineStart_));
|
||||
Say("nonstandard usage"_en_US, GetProvenance(lineStart_));
|
||||
}
|
||||
return lineStart_ + 1;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,6 @@ public:
|
||||
Prescanner(Messages &, CookedSource &, Preprocessor &);
|
||||
Prescanner(const Prescanner &);
|
||||
|
||||
bool anyFatalErrors() const { return anyFatalErrors_; }
|
||||
void set_anyFatalErrors() { anyFatalErrors_ = true; }
|
||||
Messages &messages() const { return messages_; }
|
||||
|
||||
Prescanner &set_fixedForm(bool yes) {
|
||||
@@ -59,7 +57,7 @@ public:
|
||||
|
||||
Prescanner &AddCompilerDirectiveSentinel(const std::string &);
|
||||
|
||||
bool Prescan(ProvenanceRange);
|
||||
void Prescan(ProvenanceRange);
|
||||
void Statement();
|
||||
void NextLine();
|
||||
|
||||
@@ -69,12 +67,9 @@ public:
|
||||
TokenSequence TokenizePreprocessorDirective();
|
||||
Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
|
||||
|
||||
Message &Error(Message &&);
|
||||
Message &Error(MessageFixedText, Provenance);
|
||||
Message &Error(MessageFormattedText &&, Provenance);
|
||||
Message &Complain(Message &&);
|
||||
Message &Complain(MessageFixedText, Provenance);
|
||||
Message &Complain(MessageFormattedText &&, Provenance);
|
||||
void Say(Message &&);
|
||||
void Say(MessageFixedText, Provenance);
|
||||
void Say(MessageFormattedText &&, Provenance);
|
||||
|
||||
private:
|
||||
struct LineClassification {
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
return {at};
|
||||
}
|
||||
}
|
||||
state->PutMessage(at, messageText_);
|
||||
state->Say(at, messageText_);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -42,9 +42,10 @@ private:
|
||||
const MessageFixedText messageText_;
|
||||
};
|
||||
|
||||
constexpr auto letter = CharPredicateGuard{IsLetter, "expected letter"_en_US};
|
||||
constexpr auto letter =
|
||||
CharPredicateGuard{IsLetter, "expected letter"_err_en_US};
|
||||
constexpr auto digit =
|
||||
CharPredicateGuard{IsDecimalDigit, "expected digit"_en_US};
|
||||
CharPredicateGuard{IsDecimalDigit, "expected digit"_err_en_US};
|
||||
|
||||
// "xyz"_ch matches one instance of the characters x, y, or z without skipping
|
||||
// any spaces before or after. The parser returns the location of the character
|
||||
@@ -66,7 +67,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
state->PutMessage(at, MessageExpectedText{chars_, bytes_});
|
||||
state->Say(at, MessageExpectedText{chars_, bytes_});
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -100,7 +101,7 @@ static inline void MissingSpace(ParseState *state) {
|
||||
if (!state->inFixedForm()) {
|
||||
state->set_anyConformanceViolation();
|
||||
if (state->warnOnNonstandardUsage()) {
|
||||
state->PutMessage("expected space"_en_US);
|
||||
state->Say("expected space"_err_en_US);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,7 +172,7 @@ public:
|
||||
} else if (**at == ToLowerCaseLetter(*p)) {
|
||||
at.reset();
|
||||
} else {
|
||||
state->PutMessage(start, MessageExpectedText{str_, bytes_});
|
||||
state->Say(start, MessageExpectedText{str_, bytes_});
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -241,7 +242,7 @@ struct CharLiteralChar {
|
||||
}
|
||||
char ch{**och};
|
||||
if (ch == '\n') {
|
||||
state->PutMessage(at, "unclosed character constant"_en_US);
|
||||
state->Say(at, "unclosed character constant"_err_en_US);
|
||||
return {};
|
||||
}
|
||||
if (ch != '\\') {
|
||||
@@ -252,7 +253,7 @@ struct CharLiteralChar {
|
||||
}
|
||||
ch = **och;
|
||||
if (ch == '\n') {
|
||||
state->PutMessage(at, "unclosed character constant"_en_US);
|
||||
state->Say(at, "unclosed character constant"_err_en_US);
|
||||
return {};
|
||||
}
|
||||
if (std::optional<char> escChar{BackslashEscapeValue(ch)}) {
|
||||
@@ -262,7 +263,7 @@ struct CharLiteralChar {
|
||||
ch -= '0';
|
||||
for (int j = (ch > 3 ? 1 : 2); j-- > 0;) {
|
||||
static constexpr auto octalDigit =
|
||||
CharPredicateGuard{IsOctalDigit, "expected octal digit"_en_US};
|
||||
CharPredicateGuard{IsOctalDigit, "expected octal digit"_err_en_US};
|
||||
och = octalDigit.Parse(state);
|
||||
if (och.has_value()) {
|
||||
ch = 8 * ch + **och - '0';
|
||||
@@ -274,7 +275,7 @@ struct CharLiteralChar {
|
||||
ch = 0;
|
||||
for (int j = 0; j++ < 2;) {
|
||||
static constexpr auto hexDigit = CharPredicateGuard{
|
||||
IsHexadecimalDigit, "expected hexadecimal digit"_en_US};
|
||||
IsHexadecimalDigit, "expected hexadecimal digit"_err_en_US};
|
||||
och = hexDigit.Parse(state);
|
||||
if (och.has_value()) {
|
||||
ch = 16 * ch + HexadecimalDigitValue(**och);
|
||||
@@ -283,7 +284,7 @@ struct CharLiteralChar {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state->PutMessage(at, "bad escaped character"_en_US);
|
||||
state->Say(at, "bad escaped character"_err_en_US);
|
||||
}
|
||||
return {Result::Escaped(ch)};
|
||||
}
|
||||
@@ -314,7 +315,7 @@ static bool IsNonstandardUsageOk(ParseState *state) {
|
||||
}
|
||||
state->set_anyConformanceViolation();
|
||||
if (state->warnOnNonstandardUsage()) {
|
||||
state->PutMessage("nonstandard usage"_en_US);
|
||||
state->Say("nonstandard usage"_en_US);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -385,7 +386,7 @@ struct BOZLiteral {
|
||||
}
|
||||
|
||||
if (content.empty()) {
|
||||
state->PutMessage(start, "no digit in BOZ literal"_en_US);
|
||||
state->Say(start, "no digit in BOZ literal"_err_en_US);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -393,13 +394,13 @@ struct BOZLiteral {
|
||||
for (auto digit : content) {
|
||||
digit = HexadecimalDigitValue(digit);
|
||||
if ((digit >> *shift) > 0) {
|
||||
state->PutMessage(start, "bad digit in BOZ literal"_en_US);
|
||||
state->Say(start, "bad digit in BOZ literal"_err_en_US);
|
||||
return {};
|
||||
}
|
||||
std::uint64_t was{value};
|
||||
value <<= *shift;
|
||||
if ((value >> *shift) != was) {
|
||||
state->PutMessage(start, "excessive digits in BOZ literal"_en_US);
|
||||
state->Say(start, "excessive digits in BOZ literal"_err_en_US);
|
||||
return {};
|
||||
}
|
||||
value |= digit;
|
||||
@@ -431,7 +432,7 @@ struct DigitString {
|
||||
value += digitValue;
|
||||
}
|
||||
if (overflow) {
|
||||
state->PutMessage(*firstDigit, "overflow in decimal literal"_en_US);
|
||||
state->Say(*firstDigit, "overflow in decimal literal"_err_en_US);
|
||||
}
|
||||
return {value};
|
||||
}
|
||||
@@ -458,14 +459,14 @@ struct HollerithLiteral {
|
||||
if (state->encoding() == Encoding::EUC_JP) {
|
||||
std::optional<int> chBytes{EUC_JPCharacterBytes(p)};
|
||||
if (!chBytes.has_value()) {
|
||||
state->PutMessage(start, "bad EUC_JP characters in Hollerith"_en_US);
|
||||
state->Say(start, "bad EUC_JP characters in Hollerith"_err_en_US);
|
||||
return {};
|
||||
}
|
||||
bytes = *chBytes;
|
||||
} else if (state->encoding() == Encoding::UTF8) {
|
||||
std::optional<int> chBytes{UTF8CharacterBytes(p)};
|
||||
if (!chBytes.has_value()) {
|
||||
state->PutMessage(start, "bad UTF-8 characters in Hollerith"_en_US);
|
||||
state->Say(start, "bad UTF-8 characters in Hollerith"_err_en_US);
|
||||
return {};
|
||||
}
|
||||
bytes = *chBytes;
|
||||
@@ -473,8 +474,8 @@ struct HollerithLiteral {
|
||||
if (bytes == 1) {
|
||||
std::optional<const char *> at{nextCh.Parse(state)};
|
||||
if (!at.has_value() || !isprint(**at)) {
|
||||
state->PutMessage(
|
||||
start, "insufficient or bad characters in Hollerith"_en_US);
|
||||
state->Say(
|
||||
start, "insufficient or bad characters in Hollerith"_err_en_US);
|
||||
return {};
|
||||
}
|
||||
content += **at;
|
||||
|
||||
@@ -2124,9 +2124,7 @@ private:
|
||||
WalkTupleElements(tuple, separator);
|
||||
}
|
||||
|
||||
void EndSubprogram() {
|
||||
structureComponents_.clear();
|
||||
}
|
||||
void EndSubprogram() { structureComponents_.clear(); }
|
||||
|
||||
std::ostream &out_;
|
||||
int indent_{0};
|
||||
|
||||
@@ -141,7 +141,10 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options,
|
||||
}
|
||||
}
|
||||
Fortran::parser::Parsing parsing;
|
||||
if (!parsing.Prescan(path, options)) {
|
||||
parsing.Prescan(path, options);
|
||||
if (!parsing.messages().empty() &&
|
||||
(driver.warningsAreErrors || parsing.messages().AnyFatalError())) {
|
||||
std::cerr << driver.prefix << "could not scan " << path << '\n';
|
||||
parsing.messages().Emit(std::cerr, driver.prefix);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
@@ -153,29 +156,27 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options,
|
||||
parsing.DumpCookedChars(std::cout);
|
||||
return {};
|
||||
}
|
||||
if (!parsing.Parse()) {
|
||||
if (!parsing.consumedWholeFile()) {
|
||||
std::cerr << "f18 FAIL; final position: ";
|
||||
parsing.Identify(std::cerr, parsing.finalRestingPlace(), " ");
|
||||
}
|
||||
parsing.Parse();
|
||||
parsing.messages().Emit(std::cerr, driver.prefix);
|
||||
if (!parsing.consumedWholeFile()) {
|
||||
std::cerr << "f18 parser FAIL; final position: ";
|
||||
parsing.Identify(std::cerr, parsing.finalRestingPlace(), " ");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!parsing.messages().empty() &&
|
||||
(driver.warningsAreErrors || parsing.messages().AnyFatalError()) ||
|
||||
!parsing.parseTree().has_value()) {
|
||||
std::cerr << driver.prefix << "could not parse " << path << '\n';
|
||||
parsing.messages().Emit(std::cerr, driver.prefix);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (driver.measureTree) {
|
||||
MeasureParseTree(parsing.parseTree());
|
||||
MeasureParseTree(*parsing.parseTree());
|
||||
}
|
||||
if (driver.dumpUnparse) {
|
||||
Unparse(std::cout, parsing.parseTree(), driver.encoding,
|
||||
Unparse(std::cout, *parsing.parseTree(), driver.encoding,
|
||||
true /*capitalize*/);
|
||||
return {};
|
||||
}
|
||||
|
||||
parsing.messages().Emit(std::cerr, driver.prefix);
|
||||
if (driver.warningsAreErrors &&
|
||||
!parsing.messages().empty()) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (driver.parseOnly) {
|
||||
return {};
|
||||
}
|
||||
@@ -187,7 +188,7 @@ std::string CompileFortran(std::string path, Fortran::parser::Options options,
|
||||
static_cast<unsigned long>(getpid()));
|
||||
{ std::ofstream tmpSource;
|
||||
tmpSource.open(tmpSourcePath);
|
||||
Unparse(tmpSource, parsing.parseTree(), driver.encoding);
|
||||
Unparse(tmpSource, *parsing.parseTree(), driver.encoding);
|
||||
}
|
||||
|
||||
if (ParentProcess()) {
|
||||
|
||||
@@ -22,12 +22,9 @@ int main(int argc, char *const argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::string path{argv[1]};
|
||||
Parsing parsing;
|
||||
if (!parsing.Prescan(path, Options{}) || !parsing.Parse()) {
|
||||
std::cerr << "parse FAILED\n";
|
||||
parsing.messages().Emit(std::cerr);
|
||||
return EXIT_FAILURE;
|
||||
if (std::optional<Program> parseTree{Parsing::ForTesting(path, std::cerr)}) {
|
||||
DoSemanticAnalysis(parsing.messages().cooked(), *parseTree);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
DoSemanticAnalysis(parsing.messages().cooked(), parsing.parseTree());
|
||||
return EXIT_SUCCESS;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -15,12 +15,9 @@ int main(int argc, char *const argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::string path{argv[1]};
|
||||
Parsing parsing;
|
||||
if (!parsing.Prescan(path, Options{}) || !parsing.Parse()) {
|
||||
std::cerr << "parse FAILED\n";
|
||||
parsing.messages().Emit(std::cerr);
|
||||
return EXIT_FAILURE;
|
||||
if (std::optional<Program> parseTree{Parsing::ForTesting(path, std::cerr)}) {
|
||||
semantics::MakeTypes(std::cout, *parseTree);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
semantics::MakeTypes(std::cout, parsing.parseTree());
|
||||
return EXIT_SUCCESS;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user