#include "message.h"
+#include <cstdarg>
+#include <cstdio>
+#include <cstring>
namespace Fortran {
namespace parser {
return o;
}
+std::string MessageFixedText::ToString() const {
+ return std::string{str_, /*not in std::*/ strnlen(str_, bytes_)};
+}
+
+MessageFormattedText::MessageFormattedText(MessageFixedText text, ...) {
+ const char *p{text.str()};
+ std::string asString;
+ if (p[text.size()] != '\0') {
+ // not NUL-terminated
+ asString = text.ToString();
+ p = asString.data();
+ }
+ char buffer[256];
+ va_list ap;
+ va_start(ap, text);
+ vsnprintf(buffer, sizeof buffer, p, ap);
+ va_end(ap);
+ string_ = buffer;
+}
+
+MessageFixedText MessageExpectedText::AsMessageFixedText() const {
+ if (str_ != nullptr) {
+ return {str_, bytes_};
+ }
+ static char chars[256];
+ if (chars[1] == '\0') {
+ // one-time initialization of array used for permanant single-byte string
+ // pointers
+ for (size_t j{0}; j < sizeof chars; ++j) {
+ chars[j] = j;
+ }
+ }
+ return {&chars[static_cast<unsigned char>(singleton_)], 1};
+}
+
Provenance Message::Emit(
std::ostream &o, const AllSources &sources, bool echoSourceLine) const {
if (!context_ || context_->Emit(o, sources, false) != provenance_) {
sources.Identify(o, provenance_, "", echoSourceLine);
}
- o << " " << text_ << extra_ << '\n';
+ o << " ";
+ if (string_.empty()) {
+ if (isExpectedText_) {
+ std::string goal{text_.ToString()};
+ if (goal == "\n") {
+ o << "expected end of line"_en_US;
+ } else {
+ o << MessageFormattedText("expected '%s'"_en_US, goal.data())
+ .MoveString();
+ }
+ } else {
+ o << text_;
+ }
+ } else {
+ o << string_;
+ }
+ o << '\n';
return provenance_;
}
size_t size() const { return bytes_; }
bool empty() const { return bytes_ == 0; }
- std::string ToString() const { return std::string(str_, bytes_); }
+ std::string ToString() const;
private:
const char *str_{nullptr};
std::ostream &operator<<(std::ostream &, const MessageFixedText &);
+class MessageFormattedText {
+public:
+ MessageFormattedText(MessageFixedText, ...);
+ std::string MoveString() { return std::move(string_); }
+
+private:
+ std::string string_;
+};
+
+// Represents a formatted rendition of "expected '%s'"_en_US on a constant text.
+class MessageExpectedText {
+public:
+ MessageExpectedText(const char *s, size_t n) : str_{s}, bytes_{n} {}
+ explicit MessageExpectedText(char ch) : singleton_{ch} {}
+ MessageFixedText AsMessageFixedText() const;
+
+private:
+ const char *str_{nullptr};
+ char singleton_;
+ size_t bytes_{1};
+};
+
class Message;
using MessageContext = std::shared_ptr<Message>;
Message(const Message &) = default;
Message(Provenance p, MessageFixedText t, MessageContext c = nullptr)
: provenance_{p}, text_{t}, context_{c} {}
+ Message(Provenance p, MessageFormattedText &&s, MessageContext c = nullptr)
+ : provenance_{p}, string_{s.MoveString()}, context_{c} {}
+ Message(Provenance p, MessageExpectedText t, MessageContext c = nullptr)
+ : provenance_{p}, text_{t.AsMessageFixedText()},
+ isExpectedText_{true}, context_{c} {}
Message(Message &&) = default;
Message &operator=(const Message &that) = default;
Message &operator=(Message &&that) = default;
}
Provenance provenance() const { return provenance_; }
- MessageFixedText text() const { return text_; }
- std::string extra() const { return extra_; }
MessageContext context() const { return context_; }
- Message &operator+=(std::string s) {
- extra_ += s;
- return *this;
- }
- Message &operator+=(char ch) {
- extra_ += ch;
- return *this;
- }
-
Provenance Emit(
std::ostream &, const AllSources &, bool echoSourceLine = true) const;
private:
Provenance provenance_;
MessageFixedText text_;
- std::string extra_;
+ bool isExpectedText_{false}; // implies "expected '%s'"_en_US
+ std::string string_;
MessageContext context_;
};
}
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 &PutMessage(const char *at, MessageFixedText t) {
return PutMessage(GetProvenance(at), t);
}
+ Message &PutMessage(const char *at, MessageFormattedText &&t) {
+ return PutMessage(GetProvenance(at), std::move(t));
+ }
+ Message &PutMessage(const char *at, MessageExpectedText &&t) {
+ return PutMessage(GetProvenance(at), std::move(t));
+ }
Message &PutMessage(Provenance at, MessageFixedText t) {
return messages_.Put(Message{at, t, context_});
}
+ Message &PutMessage(Provenance at, MessageFormattedText &&t) {
+ return messages_.Put(Message{at, std::move(t), context_});
+ }
+ Message &PutMessage(Provenance at, MessageExpectedText &&t) {
+ return messages_.Put(Message{at, std::move(t), context_});
+ }
bool IsAtEnd() const { return p_ >= limit_; }
}
if (dirName == "ifdef" || dirName == "ifndef") {
if (nameToken.empty()) {
- prescanner->Complain("#"_en_US) += dirName + ": missing name";
+ prescanner->Complain(
+ MessageFormattedText("#%s: missing name"_en_US, dirName.data()));
return false;
}
j = SkipBlanks(dir, j + 1, tokens);
if (j != tokens) {
- prescanner->Complain("#"_en_US) +=
- dirName + ": excess tokens at end of directive";
+ prescanner->Complain(MessageFormattedText(
+ "#%s: excess tokens at end of directive"_en_US, dirName.data()));
return false;
}
if (IsNameDefined(nameToken) == (dirName == "ifdef")) {
return true;
}
if (dirName == "error") {
- prescanner->Complain("#error: "_en_US) += dir.ToString();
+ prescanner->Complain(
+ MessageFormattedText("#error: %s"_en_US, dir.ToString().data()));
return false;
}
if (dirName == "warning") {
- prescanner->Complain("#warning: "_en_US) += dir.ToString();
+ prescanner->Complain(
+ MessageFormattedText("#warning: %s"_en_US, dir.ToString().data()));
return true;
}
if (dirName == "include") {
std::stringstream error;
const SourceFile *included{allSources_->Open(include, &error)};
if (included == nullptr) {
- prescanner->Complain("#include: "_en_US) += error.str();
+ prescanner->Complain(
+ MessageFormattedText("#include: %s"_en_US, error.str().data()));
return false;
}
ProvenanceRange fileRange{
allSources_->AddIncludedFile(*included, dir.GetProvenanceRange())};
return Prescanner{*prescanner}.Prescan(fileRange);
}
- prescanner->Complain("#"_en_US) +=
- dirName + ": unknown or unimplemented directive";
+ prescanner->Complain(MessageFormattedText(
+ "#%s: unknown or unimplemented directive"_en_US, dirName.data()));
return false;
}
}
}
}
- prescanner->Complain("#"_en_US) += dirName + ": missing #endif";
+ prescanner->Complain(
+ MessageFormattedText("#%s: missing #endif"_en_US, dirName.data()));
return false;
}
return messages_->Put({GetCurrentProvenance(), text});
}
+Message &Prescanner::Complain(MessageFormattedText &&text) {
+ return messages_->Put({GetCurrentProvenance(), std::move(text)});
+}
+
void Prescanner::NextLine() {
void *vstart{static_cast<void *>(const_cast<char *>(lineStart_))};
void *v{std::memchr(vstart, '\n', limit_ - lineStart_)};
allSources->PopSearchPathDirectory();
}
if (included == nullptr) {
- messages_->Put({provenance, "INCLUDE: "_en_US}) += error.str();
+ messages_->Put({provenance,
+ MessageFormattedText("INCLUDE: %s"_en_US, error.str().data())});
anyFatalErrors_ = true;
return true;
}
std::optional<TokenSequence> NextTokenizedLine();
Provenance GetCurrentProvenance() const { return GetProvenance(at_); }
Message &Complain(MessageFixedText);
+ Message &Complain(MessageFormattedText &&);
private:
void BeginSourceLine(const char *at) {
result.reset();
}
if (!result) {
- if (good == '\n') {
- state->PutMessage(at, "expected end of line"_en_US);
- } else {
- state->PutMessage(at, "expected '"_en_US) += std::string{good} + '\'';
- }
+ state->PutMessage(at, MessageExpectedText{good});
}
return {result};
}
using resultType = Success;
constexpr TokenStringMatch(const TokenStringMatch &) = default;
constexpr TokenStringMatch(const char *str, size_t n)
- : str_{str}, length_{n} {}
+ : str_{str}, bytes_{n} {}
constexpr TokenStringMatch(const char *str) : str_{str} {}
std::optional<Success> Parse(ParseState *state) const {
auto at = state->GetLocation();
}
const char *p{str_};
std::optional<char> ch; // initially empty
- for (size_t j{0}; j < length_ && *p != '\0'; ++j, ++p) {
+ for (size_t j{0}; j < bytes_ && *p != '\0'; ++j, ++p) {
const auto spaceSkipping{*p == ' '};
if (spaceSkipping) {
- if (j + 1 == length_ || p[1] == ' ' || p[1] == '\0') {
+ if (j + 1 == bytes_ || p[1] == ' ' || p[1] == '\0') {
continue; // redundant; ignore
}
}
} else if (*ch == tolower(*p)) {
ch.reset();
} else {
- (state->PutMessage(at, "expected '"_en_US) += str_) += '\'';
+ state->PutMessage(at, MessageExpectedText{str_, bytes_});
return {};
}
}
private:
const char *const str_;
- const size_t length_{std::numeric_limits<size_t>::max()};
+ const size_t bytes_{std::numeric_limits<size_t>::max()};
};
constexpr TokenStringMatch operator""_tok(const char str[], size_t n) {