From c3a2d82d3ed1f0dacb79321132a2d220807cf04f Mon Sep 17 00:00:00 2001 From: peter klausler Date: Wed, 21 Feb 2018 12:12:52 -0800 Subject: [PATCH] [flang] Support localizable formatted messages. Original-commit: flang-compiler/f18@e974321494b4fcecdb9bd0a5f82f0624c1a33696 Reviewed-on: https://github.com/flang-compiler/f18/pull/14 --- flang/lib/parser/message.cc | 56 +++++++++++++++++++++++++++++++++++++++- flang/lib/parser/message.h | 43 ++++++++++++++++++++---------- flang/lib/parser/parse-state.h | 18 +++++++++++++ flang/lib/parser/preprocessor.cc | 23 ++++++++++------- flang/lib/parser/prescan.cc | 7 ++++- flang/lib/parser/prescan.h | 1 + flang/lib/parser/token-parsers.h | 16 +++++------- 7 files changed, 130 insertions(+), 34 deletions(-) diff --git a/flang/lib/parser/message.cc b/flang/lib/parser/message.cc index 67591c2..e3d4927 100644 --- a/flang/lib/parser/message.cc +++ b/flang/lib/parser/message.cc @@ -1,4 +1,7 @@ #include "message.h" +#include +#include +#include namespace Fortran { namespace parser { @@ -10,12 +13,63 @@ std::ostream &operator<<(std::ostream &o, const MessageFixedText &t) { 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(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_; } diff --git a/flang/lib/parser/message.h b/flang/lib/parser/message.h index abefb60..aa28151 100644 --- a/flang/lib/parser/message.h +++ b/flang/lib/parser/message.h @@ -30,7 +30,7 @@ public: 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}; @@ -43,6 +43,28 @@ constexpr MessageFixedText operator""_en_US(const char str[], size_t n) { 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; @@ -52,6 +74,11 @@ public: 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; @@ -61,26 +88,16 @@ public: } 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_; }; diff --git a/flang/lib/parser/parse-state.h b/flang/lib/parser/parse-state.h index e6a79d3..14f7cf32 100644 --- a/flang/lib/parser/parse-state.h +++ b/flang/lib/parser/parse-state.h @@ -127,12 +127,30 @@ 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 &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_; } diff --git a/flang/lib/parser/preprocessor.cc b/flang/lib/parser/preprocessor.cc index d07be68..f7461d1 100644 --- a/flang/lib/parser/preprocessor.cc +++ b/flang/lib/parser/preprocessor.cc @@ -466,13 +466,14 @@ bool Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { } 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")) { @@ -533,11 +534,13 @@ bool Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { 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") { @@ -569,15 +572,16 @@ bool Preprocessor::Directive(const TokenSequence &dir, Prescanner *prescanner) { 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; } @@ -615,7 +619,8 @@ bool Preprocessor::SkipDisabledConditionalCode(const std::string &dirName, } } } - prescanner->Complain("#"_en_US) += dirName + ": missing #endif"; + prescanner->Complain( + MessageFormattedText("#%s: missing #endif"_en_US, dirName.data())); return false; } diff --git a/flang/lib/parser/prescan.cc b/flang/lib/parser/prescan.cc index 0e93d36..76c6488 100644 --- a/flang/lib/parser/prescan.cc +++ b/flang/lib/parser/prescan.cc @@ -92,6 +92,10 @@ Message &Prescanner::Complain(MessageFixedText text) { return messages_->Put({GetCurrentProvenance(), text}); } +Message &Prescanner::Complain(MessageFormattedText &&text) { + return messages_->Put({GetCurrentProvenance(), std::move(text)}); +} + void Prescanner::NextLine() { void *vstart{static_cast(const_cast(lineStart_))}; void *v{std::memchr(vstart, '\n', limit_ - lineStart_)}; @@ -436,7 +440,8 @@ bool Prescanner::IncludeLine(const char *p) { 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; } diff --git a/flang/lib/parser/prescan.h b/flang/lib/parser/prescan.h index eb66194..50198f0 100644 --- a/flang/lib/parser/prescan.h +++ b/flang/lib/parser/prescan.h @@ -50,6 +50,7 @@ public: std::optional NextTokenizedLine(); Provenance GetCurrentProvenance() const { return GetProvenance(at_); } Message &Complain(MessageFixedText); + Message &Complain(MessageFormattedText &&); private: void BeginSourceLine(const char *at) { diff --git a/flang/lib/parser/token-parsers.h b/flang/lib/parser/token-parsers.h index 4b8ea9f..9e7135f 100644 --- a/flang/lib/parser/token-parsers.h +++ b/flang/lib/parser/token-parsers.h @@ -72,11 +72,7 @@ public: 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}; } @@ -103,7 +99,7 @@ public: 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 Parse(ParseState *state) const { auto at = state->GetLocation(); @@ -112,10 +108,10 @@ public: } const char *p{str_}; std::optional 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 } } @@ -133,7 +129,7 @@ public: } else if (*ch == tolower(*p)) { ch.reset(); } else { - (state->PutMessage(at, "expected '"_en_US) += str_) += '\''; + state->PutMessage(at, MessageExpectedText{str_, bytes_}); return {}; } } @@ -142,7 +138,7 @@ public: private: const char *const str_; - const size_t length_{std::numeric_limits::max()}; + const size_t bytes_{std::numeric_limits::max()}; }; constexpr TokenStringMatch operator""_tok(const char str[], size_t n) { -- 2.7.4