9#ifndef FORTRAN_COMMON_FORMAT_H_
10#define FORTRAN_COMMON_FORMAT_H_
13#include "flang/Common/Fortran-consts.h"
41ENUM_CLASS(TokenKind, None, A, B, BN, BZ, D, DC, DP, DT, E, EN, ES, EX, F, G, I,
42 L, O, P, RC, RD, RN, RP, RU, RZ, S, SP, SS, T, TL, TR, X, Z, Colon, Slash,
45 Star, LParen, RParen, Comma, Point, Sign,
49template <
typename CHAR =
char>
class FormatValidator {
52 FormatValidator(
const CHAR *format,
size_t length, Reporter reporter,
53 IoStmtKind stmt = IoStmtKind::None)
54 : format_{format}, end_{format + length}, reporter_{reporter},
55 stmt_{stmt}, cursor_{format - 1} {
60 int maxNesting()
const {
return maxNesting_; }
63 common::EnumSet<TokenKind, TokenKind_enumSize> itemsWithLeadingInts_{
64 TokenKind::A, TokenKind::B, TokenKind::D, TokenKind::DT, TokenKind::E,
65 TokenKind::EN, TokenKind::ES, TokenKind::EX, TokenKind::F, TokenKind::G,
66 TokenKind::I, TokenKind::L, TokenKind::O, TokenKind::P, TokenKind::X,
67 TokenKind::Z, TokenKind::Slash, TokenKind::LParen};
70 Token &set_kind(TokenKind kind) {
74 Token &set_offset(
int offset) {
78 Token &set_length(
int length) {
83 TokenKind kind()
const {
return kind_; }
84 int offset()
const {
return offset_; }
85 int length()
const {
return length_; }
87 bool IsSet() {
return kind_ != TokenKind::None; }
90 TokenKind kind_{TokenKind::None};
95 void ReportWarning(
const char *text) { ReportWarning(text, token_); }
97 const char *text, Token &token,
const char *arg =
nullptr) {
99 text, arg ? arg : argString_, token.offset(), token.length(),
false};
100 reporterExit_ |= reporter_(msg);
103 void ReportError(
const char *text) { ReportError(text, token_); }
104 void ReportError(
const char *text, Token &token,
const char *arg =
nullptr) {
105 if (suppressMessageCascade_) {
108 formatHasErrors_ =
true;
109 suppressMessageCascade_ =
true;
111 text, arg ? arg : argString_, token.offset(), token.length(),
true};
112 reporterExit_ |= reporter_(msg);
115 void SetLength() { SetLength(token_); }
116 void SetLength(Token &token) {
117 token.set_length(cursor_ - format_ - token.offset() + (cursor_ < end_));
121 CHAR LookAheadChar();
122 void Advance(TokenKind);
125 void check_r(
bool allowed =
true);
128 bool check_d(
bool checkScaleFactor =
false);
132 const CHAR *
const format_;
133 const CHAR *
const end_;
137 const CHAR *cursor_{};
138 const CHAR *laCursor_{};
139 Token previousToken_{};
142 Token scaleFactorToken_{};
143 int64_t integerValue_{-1};
144 int64_t knrValue_{-1};
145 int64_t scaleFactorValue_{};
147 char argString_[3]{};
148 bool formatHasErrors_{
false};
149 bool unterminatedFormatError_{
false};
150 bool suppressMessageCascade_{
false};
151 bool reporterExit_{
false};
155template <
typename CHAR>
static inline bool IsWhite(CHAR c) {
163 return c ==
' ' || c ==
'\t' || c ==
'\v';
166template <
typename CHAR> CHAR FormatValidator<CHAR>::NextChar() {
167 for (++cursor_; cursor_ < end_; ++cursor_) {
168 if (!IsWhite(*cursor_)) {
169 return toupper(*cursor_);
176template <
typename CHAR> CHAR FormatValidator<CHAR>::LookAheadChar() {
177 for (laCursor_ = cursor_ + 1; laCursor_ < end_; ++laCursor_) {
178 if (!IsWhite(*laCursor_)) {
179 return toupper(*laCursor_);
187template <
typename CHAR>
void FormatValidator<CHAR>::Advance(TokenKind tk) {
192template <
typename CHAR>
void FormatValidator<CHAR>::NextToken() {
196 previousToken_ = token_;
198 token_.set_kind(TokenKind::None);
199 token_.set_offset(cursor_ - format_);
200 token_.set_length(1);
201 if (c ==
'_' && integerValue_ >= 0) {
202 ReportError(
"Kind parameter '_' character in format expression");
218 const CHAR *lastCursor;
220 bool overflow{
false};
222 lastValue = integerValue_;
223 lastCursor = cursor_;
224 integerValue_ = 10 * integerValue_ + c -
'0';
225 if (lastValue > integerValue_) {
229 }
while (c >=
'0' && c <=
'9');
230 cursor_ = lastCursor;
231 token_.set_kind(TokenKind::UnsignedInteger);
234 ReportError(
"Integer overflow in format expression");
237 if (LookAheadChar() !=
'H') {
241 if (laCursor_ + integerValue_ < end_) {
242 token_.set_kind(TokenKind::String);
243 cursor_ = laCursor_ + integerValue_;
245 token_.set_kind(TokenKind::None);
249 if (stmt_ == IoStmtKind::Read) {
250 ReportError(
"'H' edit descriptor in READ format expression");
251 }
else if (token_.kind() == TokenKind::None) {
252 ReportError(
"Unterminated 'H' edit descriptor");
254 ReportWarning(
"Legacy 'H' edit descriptor");
259 token_.set_kind(TokenKind::A);
262 switch (LookAheadChar()) {
264 Advance(TokenKind::BN);
267 Advance(TokenKind::BZ);
270 token_.set_kind(TokenKind::B);
275 switch (LookAheadChar()) {
277 Advance(TokenKind::DC);
280 Advance(TokenKind::DP);
283 Advance(TokenKind::DT);
286 token_.set_kind(TokenKind::D);
291 switch (LookAheadChar()) {
293 Advance(TokenKind::EN);
296 Advance(TokenKind::ES);
299 Advance(TokenKind::EX);
302 token_.set_kind(TokenKind::E);
307 token_.set_kind(TokenKind::F);
310 token_.set_kind(TokenKind::G);
313 token_.set_kind(TokenKind::I);
316 token_.set_kind(TokenKind::L);
319 token_.set_kind(TokenKind::O);
322 token_.set_kind(TokenKind::P);
325 switch (LookAheadChar()) {
327 Advance(TokenKind::RC);
330 Advance(TokenKind::RD);
333 Advance(TokenKind::RN);
336 Advance(TokenKind::RP);
339 Advance(TokenKind::RU);
342 Advance(TokenKind::RZ);
345 token_.set_kind(TokenKind::None);
350 switch (LookAheadChar()) {
352 Advance(TokenKind::SP);
355 Advance(TokenKind::SS);
358 token_.set_kind(TokenKind::S);
363 switch (LookAheadChar()) {
365 Advance(TokenKind::TL);
368 Advance(TokenKind::TR);
371 token_.set_kind(TokenKind::T);
376 token_.set_kind(TokenKind::X);
379 token_.set_kind(TokenKind::Z);
383 token_.set_kind(TokenKind::Sign);
386 token_.set_kind(TokenKind::Slash);
389 token_.set_kind(TokenKind::LParen);
392 token_.set_kind(TokenKind::RParen);
395 token_.set_kind(TokenKind::Point);
398 token_.set_kind(TokenKind::Colon);
401 token_.set_kind(TokenKind::Backslash);
404 token_.set_kind(TokenKind::Dollar);
407 token_.set_kind(LookAheadChar() ==
'(' ? TokenKind::Star : TokenKind::None);
410 token_.set_kind(TokenKind::Comma);
411 CHAR laChar = LookAheadChar();
413 Advance(TokenKind::Comma);
414 token_.set_offset(cursor_ - format_);
415 ReportError(
"Unexpected ',' in format expression");
416 }
else if (laChar ==
')') {
417 ReportError(
"Unexpected ',' before ')' in format expression");
423 for (++cursor_; cursor_ < end_; ++cursor_) {
425 if (
auto nc{cursor_ + 1}; nc < end_ && *nc != c) {
426 token_.set_kind(TokenKind::String);
433 if (stmt_ == IoStmtKind::Read &&
434 previousToken_.kind() != TokenKind::DT) {
435 ReportError(
"String edit descriptor in READ format expression");
436 }
else if (token_.kind() != TokenKind::String) {
437 ReportError(
"Unterminated string");
441 if (cursor_ >= end_ && !unterminatedFormatError_) {
442 suppressMessageCascade_ =
false;
443 ReportError(
"Unterminated format expression");
444 unterminatedFormatError_ =
true;
446 token_.set_kind(TokenKind::None);
453template <
typename CHAR>
void FormatValidator<CHAR>::check_r(
bool allowed) {
454 if (!allowed && knrValue_ >= 0) {
455 ReportError(
"Repeat specifier before '%s' edit descriptor", knrToken_);
456 }
else if (knrValue_ == 0) {
457 ReportError(
"'%s' edit descriptor repeat specifier must be positive",
463template <
typename CHAR>
bool FormatValidator<CHAR>::check_w() {
464 if (token_.kind() == TokenKind::UnsignedInteger) {
465 wValue_ = integerValue_;
467 if (*argString_ ==
'A' || stmt_ == IoStmtKind::Read) {
469 ReportError(
"'%s' edit descriptor 'w' value must be positive");
470 }
else if (*argString_ ==
'L') {
471 ReportWarning(
"'%s' edit descriptor 'w' value should be positive");
477 if (*argString_ !=
'A' && *argString_ !=
'L') {
478 ReportWarning(
"Expected '%s' edit descriptor 'w' value");
483template <
typename CHAR>
void FormatValidator<CHAR>::check_m() {
484 if (token_.kind() != TokenKind::Point) {
488 if (token_.kind() != TokenKind::UnsignedInteger) {
489 ReportError(
"Expected '%s' edit descriptor 'm' value after '.'");
492 if ((stmt_ == IoStmtKind::Print || stmt_ == IoStmtKind::Write) &&
493 wValue_ > 0 && integerValue_ > wValue_) {
494 ReportError(
"'%s' edit descriptor 'm' value is greater than 'w' value");
500template <
typename CHAR>
501bool FormatValidator<CHAR>::check_d(
bool checkScaleFactor) {
502 if (token_.kind() != TokenKind::Point) {
503 ReportError(
"Expected '%s' edit descriptor '.d' value");
507 if (token_.kind() != TokenKind::UnsignedInteger) {
508 ReportError(
"Expected '%s' edit descriptor 'd' value after '.'");
511 if (checkScaleFactor) {
519template <
typename CHAR>
void FormatValidator<CHAR>::check_k() {
522 if (stmt_ != IoStmtKind::Print && stmt_ != IoStmtKind::Write) {
525 if (!scaleFactorToken_.IsSet()) {
531 const int64_t d{integerValue_};
532 const int64_t k{scaleFactorValue_};
534 if (d == 0 && k == 0) {
537 if (k <= 0 && !(-d < k)) {
538 ReportError(
"Negative scale factor k (from kP) and width d in a '%s' "
539 "edit descriptor must satisfy '-d < k'");
540 }
else if (k > 0 && !(k < d + 2)) {
541 ReportError(
"Positive scale factor k (from kP) and width d in a '%s' "
542 "edit descriptor must satisfy 'k < d+2'");
546template <
typename CHAR>
void FormatValidator<CHAR>::check_e() {
547 if (token_.kind() != TokenKind::E) {
551 if (token_.kind() != TokenKind::UnsignedInteger) {
552 ReportError(
"Expected '%s' edit descriptor 'e' value after 'E'");
558template <
typename CHAR>
bool FormatValidator<CHAR>::Check() {
560 ReportError(
"Empty format expression");
561 return formatHasErrors_;
564 if (token_.kind() != TokenKind::LParen) {
565 ReportError(
"Format expression must have an initial '('");
566 return formatHasErrors_;
572 bool hasDataEditDesc{
false};
579 while (!reporterExit_) {
583 bool commaRequired{
true};
585 if (token_.kind() == TokenKind::Sign) {
589 if (token_.kind() == TokenKind::UnsignedInteger) {
591 knrValue_ = integerValue_;
594 if (signToken.IsSet() && (knrValue_ < 0 || token_.kind() != TokenKind::P)) {
595 argString_[0] = format_[signToken.offset()];
597 ReportError(
"Unexpected '%s' in format expression", signToken);
601 argString_[0] = toupper(format_[token_.offset()]);
602 argString_[1] = token_.length() > 1 ? toupper(*cursor_) : 0;
604 switch (token_.kind()) {
607 hasDataEditDesc =
true;
617 hasDataEditDesc =
true;
627 bool isD{token_.kind() == TokenKind::D};
628 hasDataEditDesc =
true;
639 case TokenKind::EX: {
642 bool isE{token_.kind() == TokenKind::E};
643 hasDataEditDesc =
true;
646 if (check_w() && check_d(isE)) {
653 hasDataEditDesc =
true;
661 }
else if (token_.kind() == TokenKind::Point && check_d() &&
662 token_.kind() == TokenKind::E) {
663 ReportError(
"A 'G0' edit descriptor must not have an 'e' value");
665 if (token_.kind() == TokenKind::UnsignedInteger) {
673 hasDataEditDesc =
true;
680 hasDataEditDesc =
true;
683 if (token_.kind() == TokenKind::String) {
686 if (token_.kind() == TokenKind::LParen) {
689 if (token_.kind() == TokenKind::Sign) {
692 if (token_.kind() != TokenKind::UnsignedInteger) {
694 "Expected integer constant in 'DT' edit descriptor v-list");
698 }
while (token_.kind() == TokenKind::Comma);
699 if (token_.kind() != TokenKind::RParen) {
700 ReportError(
"Expected ',' or ')' in 'DT' edit descriptor v-list");
701 while (cursor_ < end_ && token_.kind() != TokenKind::RParen) {
708 case TokenKind::String:
710 if (knrValue_ >= 0) {
711 ReportError(
"Repeat specifier before character string edit descriptor",
739 ReportError(
"'P' edit descriptor must have a scale factor");
741 scaleFactorToken_ = knrToken_;
742 if (signToken.IsSet() && format_[signToken.offset()] ==
'-') {
743 scaleFactorValue_ = -knrValue_;
745 scaleFactorValue_ = knrValue_;
750 const CHAR *saveCursor{cursor_};
752 if (token_.kind() == TokenKind::UnsignedInteger) {
755 switch (token_.kind()) {
763 commaRequired =
false;
767 cursor_ = saveCursor;
777 if (integerValue_ <= 0) {
778 ReportError(
"'%s' edit descriptor must have a positive position value");
784 if (knrValue_ == 0) {
785 ReportError(
"'X' edit descriptor must have a positive position value",
787 }
else if (knrValue_ < 0) {
789 "'X' edit descriptor must have a positive position value");
793 case TokenKind::Colon:
796 commaRequired =
false;
799 case TokenKind::Slash:
801 commaRequired =
false;
804 case TokenKind::Backslash:
806 ReportWarning(
"Non-standard '\\' edit descriptor");
809 case TokenKind::Dollar:
811 ReportWarning(
"Non-standard '$' edit descriptor");
814 case TokenKind::Star:
818 ReportError(
"Nested unlimited format item list");
821 if (knrValue_ >= 0) {
823 "Repeat specifier before unlimited format item list", knrToken_);
825 hasDataEditDesc =
false;
828 case TokenKind::LParen:
829 if (knrValue_ == 0) {
830 ReportError(
"List repeat specifier must be positive", knrToken_);
832 if (++nestLevel > maxNesting_) {
833 maxNesting_ = nestLevel;
836 case TokenKind::RParen:
837 if (knrValue_ >= 0) {
838 ReportError(
"Unexpected integer constant", knrToken_);
841 if (nestLevel == 0) {
843 return formatHasErrors_;
845 if (nestLevel == 1 && starToken.IsSet() && !hasDataEditDesc) {
846 SetLength(starToken);
848 "Unlimited format item list must contain a data edit descriptor",
853 }
while (token_.kind() == TokenKind::RParen);
854 if (nestLevel == 0 && starToken.IsSet()) {
855 ReportError(
"Character in format after unlimited format item list");
858 case TokenKind::Comma:
859 if (knrValue_ >= 0) {
860 ReportError(
"Unexpected integer constant", knrToken_);
862 if (suppressMessageCascade_ || reporterExit_) {
867 ReportError(
"Unexpected '%s' in format expression");
872 switch (token_.kind()) {
873 case TokenKind::Colon:
874 case TokenKind::Slash:
875 case TokenKind::RParen:
876 suppressMessageCascade_ =
false;
878 case TokenKind::LParen:
879 case TokenKind::Comma:
880 suppressMessageCascade_ =
false;
883 case TokenKind::Sign:
884 case TokenKind::None:
885 if (cursor_ >= end_) {
886 return formatHasErrors_;
892 const char *s{
"Expected ',' or ')' in format expression"};
893 if (previousToken_.kind() == TokenKind::UnsignedInteger &&
894 previousToken_.length() > 1 &&
895 itemsWithLeadingInts_.test(token_.kind())) {
905 return formatHasErrors_;
Definition: bit-population-count.h:20