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.
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 {};
}
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()};
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;
if (result) {
state->set_anyConformanceViolation();
if (state->warnOnDeprecatedUsage()) {
- state->PutMessage(at, "deprecated usage"_en_US);
+ state->Say(at, "deprecated usage"_en_US);
}
}
return result;
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') ||
}
}
-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') ||
}
}
-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);
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;
}
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'};
}
}
-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'};
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{}};
}
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') {
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 {
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
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;
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 &);
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} {}
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) {
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;
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 {
void Emit(std::ostream &, const char *prefix = nullptr,
bool echoSourceLines = true) const;
+ bool AnyFatalError() const;
+
private:
const CookedSource &cooked_;
list_type messages_;
}
}
- 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_});
}
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) {
.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);
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)
.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
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 {
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_;
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);
// 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;
}
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;
}
}
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;
}
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;
}
}
} 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);
}
} 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);
}
} 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();
}
} 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();
}
} 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;
}
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;
}
}
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));
}
}
}
}
}
- prescanner->Error(
- MessageFormattedText("#%s: missing #endif"_en_US, dirName.data()),
+ prescanner->Say(
+ MessageFormattedText("#%s: missing #endif"_err_en_US, dirName.data()),
provenance);
}
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;
}
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])) {
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;
++*atToken;
} else {
*error = Message{token.GetTokenProvenance(tokens - 1),
- "')' missing from expression"_en_US};
+ "')' missing from expression"_err_en_US};
return 0;
}
break;
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) {
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;
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:
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;
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;
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();
TokenSequence tokens{dir, allSources.AddCompilerInsertion(dir).start()};
tokens.Emit(&cooked_);
}
- return !anyFatalErrors_;
}
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;
return {std::move(tokens)};
}
-Message &Prescanner::Error(Message &&message) {
- anyFatalErrors_ = true;
- return messages_.Put(std::move(message));
-}
-
-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));
-}
+void Prescanner::Say(Message &&message) { messages_.Put(std::move(message)); }
-Message &Prescanner::Complain(MessageFixedText text, Provenance p) {
- return messages_.Put({p, text});
+void Prescanner::Say(MessageFixedText text, Provenance p) {
+ 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() {
}
if (*at_ == '\n') {
if (!inPreprocessorDirective_) {
- Error("incomplete character literal"_en_US, GetProvenance(start));
+ Say("incomplete character literal"_err_en_US, GetProvenance(start));
}
break;
}
}
if (*at_ == '\n') {
if (!inPreprocessorDirective_) {
- Error("incomplete Hollerith literal"_en_US, GetProvenance(start));
+ Say("incomplete Hollerith literal"_err_en_US, GetProvenance(start));
}
} else {
NextChar();
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_)};
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;
}
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;
}
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;
}
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) {
Prescanner &AddCompilerDirectiveSentinel(const std::string &);
- bool Prescan(ProvenanceRange);
+ void Prescan(ProvenanceRange);
void Statement();
void NextLine();
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 {
return {at};
}
}
- state->PutMessage(at, messageText_);
+ state->Say(at, messageText_);
return {};
}
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
}
}
}
- state->PutMessage(at, MessageExpectedText{chars_, bytes_});
+ state->Say(at, MessageExpectedText{chars_, bytes_});
return {};
}
if (!state->inFixedForm()) {
state->set_anyConformanceViolation();
if (state->warnOnNonstandardUsage()) {
- state->PutMessage("expected space"_en_US);
+ state->Say("expected space"_err_en_US);
}
}
}
} else if (**at == ToLowerCaseLetter(*p)) {
at.reset();
} else {
- state->PutMessage(start, MessageExpectedText{str_, bytes_});
+ state->Say(start, MessageExpectedText{str_, bytes_});
return {};
}
}
}
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 != '\\') {
}
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)}) {
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';
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);
}
}
} else {
- state->PutMessage(at, "bad escaped character"_en_US);
+ state->Say(at, "bad escaped character"_err_en_US);
}
return {Result::Escaped(ch)};
}
}
state->set_anyConformanceViolation();
if (state->warnOnNonstandardUsage()) {
- state->PutMessage("nonstandard usage"_en_US);
+ state->Say("nonstandard usage"_en_US);
}
return true;
}
}
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 {};
}
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;
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};
}
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;
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;
WalkTupleElements(tuple, separator);
}
- void EndSubprogram() {
- structureComponents_.clear();
- }
+ void EndSubprogram() { structureComponents_.clear(); }
std::ostream &out_;
int indent_{0};
}
}
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);
}
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 {};
}
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()) {
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;
}
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;
}