* A `#define` directive intermixed with continuation lines can't
define a macro that's invoked earlier in the same continued statement.
-Behavior that is not consistent to all extant compilers but which
+Behavior that is not consistent over all extant compilers but which
probably should be uncontroversial:
-----------------------------------
* Invoked macro names can straddle a Fortran line continuation.
std::optional<resultType> Parse(ParseState &state) const {
Messages messages{std::move(state.messages())};
ParseState backtrack{state};
+ state.set_anyTokenMatched(false);
std::optional<resultType> result{parser_.Parse(state)};
- bool anyTokenMatched{state.tokensMatched() > backtrack.tokensMatched()};
- bool passed{result.has_value()};
- bool keepNewMessages{passed || anyTokenMatched};
- if (keepNewMessages) {
+ bool emitMessage{false};
+ if (result.has_value()) {
messages.Annex(state.messages());
- }
- if (!passed) {
- if (keepNewMessages) {
- backtrack.set_tokensMatched(state.tokensMatched());
- if (state.anyDeferredMessages()) {
- backtrack.set_anyDeferredMessages(true);
- }
+ if (backtrack.anyTokenMatched()) {
+ state.set_anyTokenMatched();
+ }
+ } else if (state.anyTokenMatched()) {
+ messages.Annex(state.messages());
+ backtrack.set_anyTokenMatched();
+ if (state.anyDeferredMessages()) {
+ backtrack.set_anyDeferredMessages(true);
}
state = std::move(backtrack);
+ } else {
+ emitMessage = true;
}
state.messages() = std::move(messages);
- if (!keepNewMessages) {
+ if (emitMessage) {
state.Say(text_);
}
return result;
typename std::decay<decltype(parser)>::type::resultType>);
result = parser.Parse(state);
if (!result.has_value()) {
- state.CombineFailedParses(prevState, backtrack.tokensMatched());
+ state.CombineFailedParses(std::move(prevState));
ParseRest<J + 1>(result, state, backtrack);
}
}
state.messages().Restore(std::move(messages));
return bx;
}
- state.CombineFailedParses(paState, backtrack.tokensMatched());
+ state.CombineFailedParses(std::move(paState));
state.messages().Restore(std::move(messages));
std::optional<resultType> result;
return result;
}
messages.Annex(state.messages());
bool hadDeferredMessages{state.anyDeferredMessages()};
- auto tokensMatched{state.tokensMatched()};
+ bool anyTokenMatched{state.anyTokenMatched()};
state = std::move(backtrack);
state.set_deferMessages(true);
std::optional<resultType> bx{pb_.Parse(state)};
state.messages() = std::move(messages);
state.set_deferMessages(originallyDeferred);
- if (state.tokensMatched() == backtrack.tokensMatched()) {
- state.set_tokensMatched(tokensMatched);
+ if (anyTokenMatched) {
+ state.set_anyTokenMatched();
}
if (hadDeferredMessages) {
state.set_anyDeferredMessages();
u_);
}
-void MessageExpectedText::Incorporate(const MessageExpectedText &that) {
- std::visit(common::visitors{[&](SetOfChars &s1, const SetOfChars &s2) {
- s1 = s1.Union(s2);
- },
- [](const auto &, const auto &) {}},
+bool MessageExpectedText::Merge(const MessageExpectedText &that) {
+ return std::visit(common::visitors{[](SetOfChars &s1, const SetOfChars &s2) {
+ s1 = s1.Union(s2);
+ return true;
+ },
+ [](const auto &, const auto &) { return false; }},
u_, that.u_);
}
}
}
-void Message::Incorporate(Message &that) {
- std::visit(common::visitors{[&](MessageExpectedText &e1,
- const MessageExpectedText &e2) {
- e1.Incorporate(e2);
- },
- [](const auto &, const auto &) {}},
- text_, that.text_);
+bool Message::Merge(const Message &that) {
+ return AtSameLocation(that) &&
+ (!that.attachment_.get() ||
+ attachment_.get() == that.attachment_.get()) &&
+ std::visit(common::visitors{[](MessageExpectedText &e1,
+ const MessageExpectedText &e2) {
+ return e1.Merge(e2);
+ },
+ [](const auto &, const auto &) { return false; }},
+ text_, that.text_);
}
void Message::Attach(Message *m) {
location_, that.location_);
}
-void Messages::Incorporate(Messages &that) {
+bool Messages::Merge(const Message &msg) {
+ if (msg.IsMergeable()) {
+ for (auto &m : messages_) {
+ if (m.Merge(msg)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void Messages::Merge(Messages &&that) {
if (messages_.empty()) {
*this = std::move(that);
- } else if (!that.messages_.empty()) {
- last_->Incorporate(*that.last_);
+ } else {
+ while (!that.messages_.empty()) {
+ if (Merge(that.messages_.front())) {
+ that.messages_.pop_front();
+ } else {
+ messages_.splice_after(
+ last_, that.messages_, that.messages_.before_begin());
+ ++last_;
+ }
+ }
+ that.ResetLastPointer();
}
}
// on a constant text or a set of characters.
class MessageExpectedText {
public:
- MessageExpectedText(const char *s, std::size_t n)
- : u_{CharBlock{s, n == std::string::npos ? std::strlen(s) : n}} {}
+ MessageExpectedText(const char *s, std::size_t n) {
+ if (n == std::string::npos) {
+ n = std::strlen(s);
+ }
+ if (n == 1) {
+ // Treat a one-character string as a singleton set for better merging.
+ u_ = SetOfChars{*s};
+ } else {
+ u_ = CharBlock{s, n};
+ }
+ }
constexpr explicit MessageExpectedText(CharBlock cb) : u_{cb} {}
constexpr explicit MessageExpectedText(char ch) : u_{SetOfChars{ch}} {}
constexpr explicit MessageExpectedText(SetOfChars set) : u_{set} {}
MessageExpectedText &operator=(MessageExpectedText &&) = default;
std::string ToString() const;
- void Incorporate(const MessageExpectedText &);
+ bool Merge(const MessageExpectedText &);
private:
std::variant<CharBlock, SetOfChars> u_;
// corresponding ProvenanceRange.
void ResolveProvenances(const CookedSource &);
- void Incorporate(Message &);
+ bool IsMergeable() const {
+ return std::holds_alternative<MessageExpectedText>(text_);
+ }
+ bool Merge(const Message &);
private:
bool AtSameLocation(const Message &) const;
Messages(Messages &&that) : messages_{std::move(that.messages_)} {
if (!messages_.empty()) {
last_ = that.last_;
- that.last_ = that.messages_.before_begin();
+ that.ResetLastPointer();
}
}
Messages &operator=(Messages &&that) {
messages_ = std::move(that.messages_);
if (messages_.empty()) {
- last_ = messages_.before_begin();
+ ResetLastPointer();
} else {
last_ = that.last_;
- that.last_ = that.messages_.before_begin();
+ that.ResetLastPointer();
}
return *this;
}
if (!that.messages_.empty()) {
messages_.splice_after(last_, that.messages_);
last_ = that.last_;
- that.last_ = that.messages_.before_begin();
+ that.ResetLastPointer();
}
}
*this = std::move(that);
}
- void Incorporate(Messages &);
+ bool Merge(const Message &);
+ void Merge(Messages &&);
void Copy(const Messages &);
void ResolveProvenances(const CookedSource &);
void Emit(std::ostream &, const CookedSource &cooked,
bool AnyFatalError() const;
private:
+ void ResetLastPointer() { last_ = messages_.before_begin(); }
+
std::forward_list<Message> messages_;
std::forward_list<Message>::iterator last_{messages_.before_begin()};
};
anyConformanceViolation_{that.anyConformanceViolation_},
deferMessages_{that.deferMessages_},
anyDeferredMessages_{that.anyDeferredMessages_},
- tokensMatched_{that.tokensMatched_} {}
+ anyTokenMatched_{that.anyTokenMatched_} {}
ParseState(ParseState &&that)
: p_{that.p_}, limit_{that.limit_}, messages_{std::move(that.messages_)},
context_{std::move(that.context_)}, userState_{that.userState_},
anyConformanceViolation_{that.anyConformanceViolation_},
deferMessages_{that.deferMessages_},
anyDeferredMessages_{that.anyDeferredMessages_},
- tokensMatched_{that.tokensMatched_} {}
+ anyTokenMatched_{that.anyTokenMatched_} {}
ParseState &operator=(const ParseState &that) {
p_ = that.p_, limit_ = that.limit_, context_ = that.context_;
userState_ = that.userState_, inFixedForm_ = that.inFixedForm_;
anyConformanceViolation_ = that.anyConformanceViolation_;
deferMessages_ = that.deferMessages_;
anyDeferredMessages_ = that.anyDeferredMessages_;
- tokensMatched_ = that.tokensMatched_;
+ anyTokenMatched_ = that.anyTokenMatched_;
return *this;
}
ParseState &operator=(ParseState &&that) {
anyConformanceViolation_ = that.anyConformanceViolation_;
deferMessages_ = that.deferMessages_;
anyDeferredMessages_ = that.anyDeferredMessages_;
- tokensMatched_ = that.tokensMatched_;
+ anyTokenMatched_ = that.anyTokenMatched_;
return *this;
}
return *this;
}
- std::size_t tokensMatched() const { return tokensMatched_; }
- ParseState &set_tokensMatched(std::size_t n) {
- tokensMatched_ = n;
- return *this;
- }
- ParseState &TokenMatched() {
- ++tokensMatched_;
+ bool anyTokenMatched() const { return anyTokenMatched_; }
+ ParseState &set_anyTokenMatched(bool yes = true) {
+ anyTokenMatched_ = yes;
return *this;
}
return remain;
}
- void CombineFailedParses(ParseState &prev, std::size_t origTokensMatched) {
- if (prev.tokensMatched_ > origTokensMatched) {
- if (tokensMatched_ > origTokensMatched) {
- if (prev.p_ == p_) {
- prev.messages_.Incorporate(messages_);
- prev.anyDeferredMessages_ |= anyDeferredMessages_;
- }
- if (prev.p_ >= p_) {
- *this = std::move(prev);
- }
- } else {
- *this = std::move(prev);
+ void CombineFailedParses(ParseState &&prev) {
+ if (prev.anyTokenMatched_) {
+ if (!anyTokenMatched_ || prev.p_ > p_) {
+ anyTokenMatched_ = true;
+ p_ = prev.p_;
+ messages_ = std::move(prev.messages_);
+ } else if (prev.p_ == p_) {
+ messages_.Merge(std::move(prev.messages_));
}
}
+ anyDeferredMessages_ |= prev.anyDeferredMessages_;
+ anyConformanceViolation_ |= prev.anyConformanceViolation_;
+ anyErrorRecovery_ |= prev.anyErrorRecovery_;
}
private:
bool anyConformanceViolation_{false};
bool deferMessages_{false};
bool anyDeferredMessages_{false};
- std::size_t tokensMatched_{0};
+ bool anyTokenMatched_{false};
// NOTE: Any additions or modifications to these data members must also be
// reflected in the copy and move constructors defined at the top of this
// class definition!
if (*at_ == '\n') {
return false;
}
+ const char *start{at_};
if (*at_ == '\'' || *at_ == '"') {
QuotedCharacterLiteral(tokens);
preventHollerith_ = false;
} while (IsDecimalDigit(*at_));
if ((*at_ == 'h' || *at_ == 'H') && n > 0 && n < maxHollerith &&
!preventHollerith_) {
- Hollerith(tokens, n);
+ Hollerith(tokens, n, start);
} else if (*at_ == '.') {
while (IsDecimalDigit(EmitCharAndAdvance(tokens, *at_))) {
}
inCharLiteral_ = false;
}
-void Prescanner::Hollerith(TokenSequence &tokens, int count) {
+void Prescanner::Hollerith(
+ TokenSequence &tokens, int count, const char *start) {
inCharLiteral_ = true;
CHECK(*at_ == 'h' || *at_ == 'H');
EmitChar(tokens, 'H');
- const char *start{at_}, *end{at_ + 1};
while (count-- > 0) {
if (PadOutCharacterLiteral(tokens)) {
} else if (*at_ == '\n') {
- Say("incomplete Hollerith literal"_err_en_US,
- GetProvenanceRange(start, end));
+ Say("possible truncated Hollerith literal"_en_US,
+ GetProvenanceRange(start, at_));
break;
} else {
- end = at_ + 1;
NextChar();
EmitChar(tokens, *at_);
// Multi-byte character encodings should count as single characters.
bool NextToken(TokenSequence &);
bool ExponentAndKind(TokenSequence &);
void QuotedCharacterLiteral(TokenSequence &);
- void Hollerith(TokenSequence &, int);
+ void Hollerith(TokenSequence &, int count, const char *start);
bool PadOutCharacterLiteral(TokenSequence &);
bool SkipCommentLine();
bool IsFixedFormCommentLine(const char *) const;
if (std::optional<const char *> at{state.PeekAtNextChar()}) {
if (set_.Has(**at)) {
state.UncheckedAdvance();
+ state.set_anyTokenMatched();
return at;
}
}
return {};
}
}
- state.TokenMatched();
+ state.set_anyTokenMatched();
if (IsLegalInIdentifier(p[-1])) {
return spaceCheck.Parse(state);
} else {