9#ifndef FORTRAN_COMMON_FORMAT_H_
10#define FORTRAN_COMMON_FORMAT_H_
12#include "Fortran-consts.h"
37#define __has_builtin(x) 0
42static inline bool AddOverflow(
43 std::int64_t x, std::int64_t y, std::int64_t &result) {
44#if __has_builtin(__builtin_add_overflow)
45 return __builtin_add_overflow(x, y, &result);
48 const std::uint64_t ux{
static_cast<std::uint64_t
>(x)};
49 const std::uint64_t uy{
static_cast<std::uint64_t
>(y)};
50 const std::uint64_t uresult{ux + uy};
53 result =
static_cast<std::int64_t
>(uresult);
69static inline bool MulOverflow(
70 std::int64_t x, std::int64_t y, std::int64_t &result) {
71#if __has_builtin(__builtin_mul_overflow)
72 return __builtin_mul_overflow(x, y, &result);
75 const std::uint64_t ux{x < 0 ? (0 -
static_cast<std::uint64_t
>(x))
76 : static_cast<std::uint64_t>(x)};
77 const std::uint64_t uy{y < 0 ? (0 -
static_cast<std::uint64_t
>(y))
78 : static_cast<std::uint64_t>(y)};
79 const std::uint64_t uresult{ux * uy};
82 const bool isNegative = (x < 0) ^ (y < 0);
83 result = isNegative ? (0 - uresult) : uresult;
86 if (ux == 0 || uy == 0) {
95 (
static_cast<std::uint64_t
>(std::numeric_limits<std::int64_t>::max()) +
100 (
static_cast<std::uint64_t
>(std::numeric_limits<std::int64_t>::max())) /
116ENUM_CLASS(TokenKind, None, A, B, BN, BZ, D, DC, DP, DT, E, EN, ES, EX, F, G, I,
117 L, O, P, RC, RD, RN, RP, RU, RZ, S, SP, SS, T, TL, TR, X, Z, Colon, Slash,
120 Star, LParen, RParen, Comma, Point, Sign,
124template <
typename CHAR =
char>
class FormatValidator {
127 FormatValidator(
const CHAR *format,
size_t length, Reporter reporter,
128 IoStmtKind stmt = IoStmtKind::None)
129 : format_{format}, end_{format + length}, reporter_{reporter},
130 stmt_{stmt}, cursor_{format - 1} {
135 int maxNesting()
const {
return maxNesting_; }
138 common::EnumSet<TokenKind, TokenKind_enumSize> itemsWithLeadingInts_{
139 TokenKind::A, TokenKind::B, TokenKind::D, TokenKind::DT, TokenKind::E,
140 TokenKind::EN, TokenKind::ES, TokenKind::EX, TokenKind::F, TokenKind::G,
141 TokenKind::I, TokenKind::L, TokenKind::O, TokenKind::P, TokenKind::X,
142 TokenKind::Z, TokenKind::Slash, TokenKind::LParen};
145 Token &set_kind(TokenKind kind) {
149 Token &set_offset(
int offset) {
153 Token &set_length(
int length) {
158 TokenKind kind()
const {
return kind_; }
159 int offset()
const {
return offset_; }
160 int length()
const {
return length_; }
162 bool IsSet() {
return kind_ != TokenKind::None; }
165 TokenKind kind_{TokenKind::None};
170 void ReportWarning(
const char *text) { ReportWarning(text, token_); }
172 const char *text, Token &token,
const char *arg =
nullptr) {
174 text, arg ? arg : argString_, token.offset(), token.length(),
false};
175 reporterExit_ |= reporter_(msg);
178 void ReportError(
const char *text) { ReportError(text, token_); }
179 void ReportError(
const char *text, Token &token,
const char *arg =
nullptr) {
180 if (suppressMessageCascade_) {
183 formatHasErrors_ =
true;
184 suppressMessageCascade_ =
true;
186 text, arg ? arg : argString_, token.offset(), token.length(),
true};
187 reporterExit_ |= reporter_(msg);
190 void SetLength() { SetLength(token_); }
191 void SetLength(Token &token) {
192 token.set_length(cursor_ - format_ - token.offset() + (cursor_ < end_));
196 CHAR LookAheadChar();
197 void Advance(TokenKind);
200 void check_r(
bool allowed =
true);
203 bool check_d(
bool checkScaleFactor =
false);
207 const CHAR *
const format_;
208 const CHAR *
const end_;
212 const CHAR *cursor_{};
213 const CHAR *laCursor_{};
214 Token previousToken_{};
217 Token scaleFactorToken_{};
218 std::int64_t integerValue_{-1};
219 std::int64_t knrValue_{-1};
220 std::int64_t scaleFactorValue_{};
221 std::int64_t wValue_{-1};
222 char argString_[3]{};
223 bool formatHasErrors_{
false};
224 bool unterminatedFormatError_{
false};
225 bool suppressMessageCascade_{
false};
226 bool reporterExit_{
false};
230template <
typename CHAR>
static inline bool IsWhite(CHAR c) {
238 return c ==
' ' || c ==
'\t' || c ==
'\v';
241template <
typename CHAR> CHAR FormatValidator<CHAR>::NextChar() {
242 for (++cursor_; cursor_ < end_; ++cursor_) {
243 if (!IsWhite(*cursor_)) {
244 return toupper(*cursor_);
251template <
typename CHAR> CHAR FormatValidator<CHAR>::LookAheadChar() {
252 for (laCursor_ = cursor_ + 1; laCursor_ < end_; ++laCursor_) {
253 if (!IsWhite(*laCursor_)) {
254 return toupper(*laCursor_);
262template <
typename CHAR>
void FormatValidator<CHAR>::Advance(TokenKind tk) {
267template <
typename CHAR>
void FormatValidator<CHAR>::NextToken() {
271 previousToken_ = token_;
273 token_.set_kind(TokenKind::None);
274 token_.set_offset(cursor_ - format_);
275 token_.set_length(1);
276 if (c ==
'_' && integerValue_ >= 0) {
277 ReportError(
"Kind parameter '_' character in format expression");
292 const CHAR *lastCursor{};
294 bool overflow{
false};
296 lastCursor = cursor_;
298 overflow = MulOverflow(
299 static_cast<std::int64_t
>(10), integerValue_, integerValue_);
302 overflow = AddOverflow(
303 integerValue_,
static_cast<std::int64_t
>(c -
'0'), integerValue_);
306 }
while (c >=
'0' && c <=
'9');
307 cursor_ = lastCursor;
308 token_.set_kind(TokenKind::UnsignedInteger);
311 ReportError(
"Integer overflow in format expression");
314 if (LookAheadChar() !=
'H') {
318 if (laCursor_ + integerValue_ < end_) {
319 token_.set_kind(TokenKind::String);
320 cursor_ = laCursor_ + integerValue_;
322 token_.set_kind(TokenKind::None);
326 if (stmt_ == IoStmtKind::Read) {
327 ReportError(
"'H' edit descriptor in READ format expression");
328 }
else if (token_.kind() == TokenKind::None) {
329 ReportError(
"Unterminated 'H' edit descriptor");
331 ReportWarning(
"Legacy 'H' edit descriptor");
336 token_.set_kind(TokenKind::A);
339 switch (LookAheadChar()) {
341 Advance(TokenKind::BN);
344 Advance(TokenKind::BZ);
347 token_.set_kind(TokenKind::B);
352 switch (LookAheadChar()) {
354 Advance(TokenKind::DC);
357 Advance(TokenKind::DP);
360 Advance(TokenKind::DT);
363 token_.set_kind(TokenKind::D);
368 switch (LookAheadChar()) {
370 Advance(TokenKind::EN);
373 Advance(TokenKind::ES);
376 Advance(TokenKind::EX);
379 token_.set_kind(TokenKind::E);
384 token_.set_kind(TokenKind::F);
387 token_.set_kind(TokenKind::G);
390 token_.set_kind(TokenKind::I);
393 token_.set_kind(TokenKind::L);
396 token_.set_kind(TokenKind::O);
399 token_.set_kind(TokenKind::P);
402 switch (LookAheadChar()) {
404 Advance(TokenKind::RC);
407 Advance(TokenKind::RD);
410 Advance(TokenKind::RN);
413 Advance(TokenKind::RP);
416 Advance(TokenKind::RU);
419 Advance(TokenKind::RZ);
422 token_.set_kind(TokenKind::None);
427 switch (LookAheadChar()) {
429 Advance(TokenKind::SP);
432 Advance(TokenKind::SS);
435 token_.set_kind(TokenKind::S);
440 switch (LookAheadChar()) {
442 Advance(TokenKind::TL);
445 Advance(TokenKind::TR);
448 token_.set_kind(TokenKind::T);
453 token_.set_kind(TokenKind::X);
456 token_.set_kind(TokenKind::Z);
460 token_.set_kind(TokenKind::Sign);
463 token_.set_kind(TokenKind::Slash);
466 token_.set_kind(TokenKind::LParen);
469 token_.set_kind(TokenKind::RParen);
472 token_.set_kind(TokenKind::Point);
475 token_.set_kind(TokenKind::Colon);
478 token_.set_kind(TokenKind::Backslash);
481 token_.set_kind(TokenKind::Dollar);
484 token_.set_kind(LookAheadChar() ==
'(' ? TokenKind::Star : TokenKind::None);
487 token_.set_kind(TokenKind::Comma);
488 CHAR laChar = LookAheadChar();
490 Advance(TokenKind::Comma);
491 token_.set_offset(cursor_ - format_);
492 ReportError(
"Unexpected ',' in format expression");
493 }
else if (laChar ==
')') {
494 ReportError(
"Unexpected ',' before ')' in format expression");
500 for (++cursor_; cursor_ < end_; ++cursor_) {
502 if (
auto nc{cursor_ + 1}; nc < end_ && *nc != c) {
503 token_.set_kind(TokenKind::String);
510 if (token_.kind() != TokenKind::String) {
511 ReportError(
"Unterminated string");
512 }
else if (stmt_ == IoStmtKind::Read &&
513 previousToken_.kind() != TokenKind::DT) {
514 ReportWarning(
"String edit descriptor in READ format expression");
518 if (cursor_ >= end_ && !unterminatedFormatError_) {
519 suppressMessageCascade_ =
false;
520 ReportError(
"Unterminated format expression");
521 unterminatedFormatError_ =
true;
523 token_.set_kind(TokenKind::None);
530template <
typename CHAR>
void FormatValidator<CHAR>::check_r(
bool allowed) {
531 if (!allowed && knrValue_ >= 0) {
532 ReportError(
"Repeat specifier before '%s' edit descriptor", knrToken_);
533 }
else if (knrValue_ == 0) {
534 ReportError(
"'%s' edit descriptor repeat specifier must be positive",
540template <
typename CHAR>
bool FormatValidator<CHAR>::check_w() {
541 if (token_.kind() == TokenKind::UnsignedInteger) {
542 wValue_ = integerValue_;
544 if (*argString_ ==
'A' || stmt_ == IoStmtKind::Read) {
546 ReportError(
"'%s' edit descriptor 'w' value must be positive");
547 }
else if (*argString_ ==
'L') {
548 ReportWarning(
"'%s' edit descriptor 'w' value should be positive");
554 if (*argString_ !=
'A' && *argString_ !=
'L') {
555 ReportWarning(
"Expected '%s' edit descriptor 'w' value");
560template <
typename CHAR>
void FormatValidator<CHAR>::check_m() {
561 if (token_.kind() != TokenKind::Point) {
565 if (token_.kind() != TokenKind::UnsignedInteger) {
566 ReportError(
"Expected '%s' edit descriptor 'm' value after '.'");
569 if ((stmt_ == IoStmtKind::Print || stmt_ == IoStmtKind::Write) &&
570 wValue_ > 0 && integerValue_ > wValue_) {
571 ReportError(
"'%s' edit descriptor 'm' value is greater than 'w' value");
577template <
typename CHAR>
578bool FormatValidator<CHAR>::check_d(
bool checkScaleFactor) {
579 if (token_.kind() != TokenKind::Point) {
580 ReportError(
"Expected '%s' edit descriptor '.d' value");
584 if (token_.kind() != TokenKind::UnsignedInteger) {
585 ReportError(
"Expected '%s' edit descriptor 'd' value after '.'");
588 if (checkScaleFactor) {
596template <
typename CHAR>
void FormatValidator<CHAR>::check_k() {
599 if (stmt_ != IoStmtKind::Print && stmt_ != IoStmtKind::Write) {
602 if (!scaleFactorToken_.IsSet()) {
608 const int64_t d{integerValue_};
609 const int64_t k{scaleFactorValue_};
611 if (d == 0 && k == 0) {
614 if (k <= 0 && !(-d < k)) {
615 ReportError(
"Negative scale factor k (from kP) and width d in a '%s' "
616 "edit descriptor must satisfy '-d < k'");
617 }
else if (k > 0 && !(k < d + 2)) {
618 ReportError(
"Positive scale factor k (from kP) and width d in a '%s' "
619 "edit descriptor must satisfy 'k < d+2'");
623template <
typename CHAR>
void FormatValidator<CHAR>::check_e() {
624 if (token_.kind() != TokenKind::E) {
628 if (token_.kind() != TokenKind::UnsignedInteger) {
629 ReportError(
"Expected '%s' edit descriptor 'e' value after 'E'");
635template <
typename CHAR>
bool FormatValidator<CHAR>::Check() {
637 ReportError(
"Empty format expression");
638 return formatHasErrors_;
641 if (token_.kind() != TokenKind::LParen) {
642 ReportError(
"Format expression must have an initial '('");
643 return formatHasErrors_;
649 bool hasDataEditDesc{
false};
656 while (!reporterExit_) {
660 bool commaRequired{
true};
662 if (token_.kind() == TokenKind::Sign) {
666 if (token_.kind() == TokenKind::UnsignedInteger) {
668 knrValue_ = integerValue_;
671 if (signToken.IsSet() && (knrValue_ < 0 || token_.kind() != TokenKind::P)) {
672 argString_[0] = format_[signToken.offset()];
674 ReportError(
"Unexpected '%s' in format expression", signToken);
678 argString_[0] = toupper(format_[token_.offset()]);
679 argString_[1] = token_.length() > 1 ? toupper(*cursor_) : 0;
681 switch (token_.kind()) {
684 hasDataEditDesc =
true;
694 hasDataEditDesc =
true;
704 bool isD{token_.kind() == TokenKind::D};
705 hasDataEditDesc =
true;
716 case TokenKind::EX: {
719 bool isE{token_.kind() == TokenKind::E};
720 hasDataEditDesc =
true;
723 if (check_w() && check_d(isE)) {
730 hasDataEditDesc =
true;
738 }
else if (token_.kind() == TokenKind::Point && check_d() &&
739 token_.kind() == TokenKind::E) {
740 ReportError(
"A 'G0' edit descriptor must not have an 'e' value");
742 if (token_.kind() == TokenKind::UnsignedInteger) {
750 hasDataEditDesc =
true;
757 hasDataEditDesc =
true;
760 if (token_.kind() == TokenKind::String) {
763 if (token_.kind() == TokenKind::LParen) {
766 if (token_.kind() == TokenKind::Sign) {
769 if (token_.kind() != TokenKind::UnsignedInteger) {
771 "Expected integer constant in 'DT' edit descriptor v-list");
775 }
while (token_.kind() == TokenKind::Comma);
776 if (token_.kind() != TokenKind::RParen) {
777 ReportError(
"Expected ',' or ')' in 'DT' edit descriptor v-list");
778 while (cursor_ < end_ && token_.kind() != TokenKind::RParen) {
785 case TokenKind::String:
787 if (knrValue_ >= 0) {
788 ReportError(
"Repeat specifier before character string edit descriptor",
816 ReportError(
"'P' edit descriptor must have a scale factor");
818 scaleFactorToken_ = knrToken_;
819 if (signToken.IsSet() && format_[signToken.offset()] ==
'-') {
820 scaleFactorValue_ = -knrValue_;
822 scaleFactorValue_ = knrValue_;
827 const CHAR *saveCursor{cursor_};
829 if (token_.kind() == TokenKind::UnsignedInteger) {
832 switch (token_.kind()) {
840 commaRequired =
false;
844 cursor_ = saveCursor;
854 if (integerValue_ <= 0) {
855 ReportError(
"'%s' edit descriptor must have a positive position value");
861 if (knrValue_ == 0) {
862 ReportError(
"'X' edit descriptor must have a positive position value",
864 }
else if (knrValue_ < 0) {
866 "'X' edit descriptor must have a positive position value");
870 case TokenKind::Colon:
873 commaRequired =
false;
876 case TokenKind::Slash:
878 commaRequired =
false;
881 case TokenKind::Backslash:
883 ReportWarning(
"Non-standard '\\' edit descriptor");
886 case TokenKind::Dollar:
888 ReportWarning(
"Non-standard '$' edit descriptor");
891 case TokenKind::Star:
895 ReportError(
"Nested unlimited format item list");
898 if (knrValue_ >= 0) {
900 "Repeat specifier before unlimited format item list", knrToken_);
902 hasDataEditDesc =
false;
905 case TokenKind::LParen:
906 if (knrValue_ == 0) {
907 ReportError(
"List repeat specifier must be positive", knrToken_);
909 if (++nestLevel > maxNesting_) {
910 maxNesting_ = nestLevel;
913 case TokenKind::RParen:
914 if (knrValue_ >= 0) {
915 ReportError(
"Unexpected integer constant", knrToken_);
918 if (nestLevel == 0) {
920 return formatHasErrors_;
922 if (nestLevel == 1 && starToken.IsSet() && !hasDataEditDesc) {
923 SetLength(starToken);
925 "Unlimited format item list must contain a data edit descriptor",
930 }
while (token_.kind() == TokenKind::RParen);
931 if (nestLevel == 0 && starToken.IsSet()) {
932 ReportError(
"Character in format after unlimited format item list");
935 case TokenKind::Comma:
936 if (knrValue_ >= 0) {
937 ReportError(
"Unexpected integer constant", knrToken_);
939 if (suppressMessageCascade_ || reporterExit_) {
944 ReportError(
"Unexpected '%s' in format expression");
949 switch (token_.kind()) {
950 case TokenKind::Colon:
951 case TokenKind::Slash:
952 case TokenKind::RParen:
953 suppressMessageCascade_ =
false;
955 case TokenKind::LParen:
956 case TokenKind::Comma:
957 suppressMessageCascade_ =
false;
960 case TokenKind::Sign:
961 case TokenKind::None:
962 if (cursor_ >= end_) {
963 return formatHasErrors_;
969 const char *s{
"Expected ',' or ')' in format expression"};
970 if (previousToken_.kind() == TokenKind::UnsignedInteger &&
971 previousToken_.length() > 1 &&
972 itemsWithLeadingInts_.test(token_.kind())) {
982 return formatHasErrors_;
Definition bit-population-count.h:20