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, AT, B, BN, BZ, D, DC, DP, DT, E, EN, ES, EX, F,
117 G, I, L, LZ, LZP, LZS, O, P, RC, RD, RN, RP, RU, RZ, S, SP, SS, T, TL, TR,
121 Star, LParen, RParen, Comma, Point, Sign,
125template <
typename CHAR =
char>
class FormatValidator {
128 FormatValidator(
const CHAR *format,
size_t length, Reporter reporter,
129 IoStmtKind stmt = IoStmtKind::None)
130 : format_{format}, end_{format + length}, reporter_{reporter},
131 stmt_{stmt}, cursor_{format - 1} {
136 int maxNesting()
const {
return maxNesting_; }
139 common::EnumSet<TokenKind, TokenKind_enumSize> itemsWithLeadingInts_{
140 TokenKind::A, TokenKind::AT, TokenKind::B, TokenKind::D, TokenKind::DT,
141 TokenKind::E, TokenKind::EN, TokenKind::ES, TokenKind::EX, TokenKind::F,
142 TokenKind::G, TokenKind::I, TokenKind::L, TokenKind::O, TokenKind::P,
143 TokenKind::X, TokenKind::Z, TokenKind::Slash, TokenKind::LParen};
146 Token &set_kind(TokenKind kind) {
150 Token &set_offset(
int offset) {
154 Token &set_length(
int length) {
159 TokenKind kind()
const {
return kind_; }
160 int offset()
const {
return offset_; }
161 int length()
const {
return length_; }
163 bool IsSet() {
return kind_ != TokenKind::None; }
166 TokenKind kind_{TokenKind::None};
171 void ReportWarning(
const char *text) { ReportWarning(text, token_); }
173 const char *text, Token &token,
const char *arg =
nullptr) {
175 text, arg ? arg : argString_, token.offset(), token.length(),
false};
176 reporterExit_ |= reporter_(msg);
179 void ReportError(
const char *text) { ReportError(text, token_); }
180 void ReportError(
const char *text, Token &token,
const char *arg =
nullptr) {
181 if (suppressMessageCascade_) {
184 formatHasErrors_ =
true;
185 suppressMessageCascade_ =
true;
187 text, arg ? arg : argString_, token.offset(), token.length(),
true};
188 reporterExit_ |= reporter_(msg);
191 void SetLength() { SetLength(token_); }
192 void SetLength(Token &token) {
193 token.set_length(cursor_ - format_ - token.offset() + (cursor_ < end_));
197 CHAR LookAheadChar();
198 void Advance(TokenKind);
201 void check_r(
bool allowed =
true);
204 bool check_d(
bool checkScaleFactor =
false);
208 const CHAR *
const format_;
209 const CHAR *
const end_;
213 const CHAR *cursor_{};
214 const CHAR *laCursor_{};
215 Token previousToken_{};
218 Token scaleFactorToken_{};
219 std::int64_t integerValue_{-1};
220 std::int64_t knrValue_{-1};
221 std::int64_t scaleFactorValue_{};
222 std::int64_t wValue_{-1};
223 char argString_[4]{};
224 bool formatHasErrors_{
false};
225 bool unterminatedFormatError_{
false};
226 bool suppressMessageCascade_{
false};
227 bool reporterExit_{
false};
231template <
typename CHAR>
static inline bool IsWhite(CHAR c) {
239 return c ==
' ' || c ==
'\t' || c ==
'\v';
242template <
typename CHAR> CHAR FormatValidator<CHAR>::NextChar() {
243 for (++cursor_; cursor_ < end_; ++cursor_) {
244 if (!IsWhite(*cursor_)) {
245 return toupper(*cursor_);
252template <
typename CHAR> CHAR FormatValidator<CHAR>::LookAheadChar() {
253 for (laCursor_ = cursor_ + 1; laCursor_ < end_; ++laCursor_) {
254 if (!IsWhite(*laCursor_)) {
255 return toupper(*laCursor_);
263template <
typename CHAR>
void FormatValidator<CHAR>::Advance(TokenKind tk) {
268template <
typename CHAR>
void FormatValidator<CHAR>::NextToken() {
272 previousToken_ = token_;
274 token_.set_kind(TokenKind::None);
275 token_.set_offset(cursor_ - format_);
276 token_.set_length(1);
277 if (c ==
'_' && integerValue_ >= 0) {
278 ReportError(
"Kind parameter '_' character in format expression");
293 const CHAR *lastCursor{};
295 bool overflow{
false};
297 lastCursor = cursor_;
299 overflow = MulOverflow(
300 static_cast<std::int64_t
>(10), integerValue_, integerValue_);
303 overflow = AddOverflow(
304 integerValue_,
static_cast<std::int64_t
>(c -
'0'), integerValue_);
307 }
while (c >=
'0' && c <=
'9');
308 cursor_ = lastCursor;
309 token_.set_kind(TokenKind::UnsignedInteger);
312 ReportError(
"Integer overflow in format expression");
315 if (LookAheadChar() !=
'H') {
319 if (laCursor_ + integerValue_ < end_) {
320 token_.set_kind(TokenKind::String);
321 cursor_ = laCursor_ + integerValue_;
323 token_.set_kind(TokenKind::None);
327 if (stmt_ == IoStmtKind::Read) {
328 ReportError(
"'H' edit descriptor in READ format expression");
329 }
else if (token_.kind() == TokenKind::None) {
330 ReportError(
"Unterminated 'H' edit descriptor");
332 ReportWarning(
"Legacy 'H' edit descriptor");
337 if (LookAheadChar() ==
'T') {
338 Advance(TokenKind::AT);
340 token_.set_kind(TokenKind::A);
344 switch (LookAheadChar()) {
346 Advance(TokenKind::BN);
349 Advance(TokenKind::BZ);
352 token_.set_kind(TokenKind::B);
357 switch (LookAheadChar()) {
359 Advance(TokenKind::DC);
362 Advance(TokenKind::DP);
365 Advance(TokenKind::DT);
368 token_.set_kind(TokenKind::D);
373 switch (LookAheadChar()) {
375 Advance(TokenKind::EN);
378 Advance(TokenKind::ES);
381 Advance(TokenKind::EX);
384 token_.set_kind(TokenKind::E);
389 token_.set_kind(TokenKind::F);
392 token_.set_kind(TokenKind::G);
395 token_.set_kind(TokenKind::I);
398 switch (LookAheadChar()) {
401 Advance(TokenKind::LZ);
402 switch (LookAheadChar()) {
404 Advance(TokenKind::LZS);
407 Advance(TokenKind::LZP);
414 token_.set_kind(TokenKind::L);
419 token_.set_kind(TokenKind::O);
422 token_.set_kind(TokenKind::P);
425 switch (LookAheadChar()) {
427 Advance(TokenKind::RC);
430 Advance(TokenKind::RD);
433 Advance(TokenKind::RN);
436 Advance(TokenKind::RP);
439 Advance(TokenKind::RU);
442 Advance(TokenKind::RZ);
445 token_.set_kind(TokenKind::None);
450 switch (LookAheadChar()) {
452 Advance(TokenKind::SP);
455 Advance(TokenKind::SS);
458 token_.set_kind(TokenKind::S);
463 switch (LookAheadChar()) {
465 Advance(TokenKind::TL);
468 Advance(TokenKind::TR);
471 token_.set_kind(TokenKind::T);
476 token_.set_kind(TokenKind::X);
479 token_.set_kind(TokenKind::Z);
483 token_.set_kind(TokenKind::Sign);
486 token_.set_kind(TokenKind::Slash);
489 token_.set_kind(TokenKind::LParen);
492 token_.set_kind(TokenKind::RParen);
495 token_.set_kind(TokenKind::Point);
498 token_.set_kind(TokenKind::Colon);
501 token_.set_kind(TokenKind::Backslash);
504 token_.set_kind(TokenKind::Dollar);
507 token_.set_kind(LookAheadChar() ==
'(' ? TokenKind::Star : TokenKind::None);
510 token_.set_kind(TokenKind::Comma);
511 CHAR laChar = LookAheadChar();
513 Advance(TokenKind::Comma);
514 token_.set_offset(cursor_ - format_);
515 ReportError(
"Unexpected ',' in format expression");
516 }
else if (laChar ==
')') {
517 ReportError(
"Unexpected ',' before ')' in format expression");
523 for (++cursor_; cursor_ < end_; ++cursor_) {
525 if (
auto nc{cursor_ + 1}; nc < end_ && *nc != c) {
526 token_.set_kind(TokenKind::String);
533 if (token_.kind() != TokenKind::String) {
534 ReportError(
"Unterminated string");
535 }
else if (stmt_ == IoStmtKind::Read &&
536 previousToken_.kind() != TokenKind::DT) {
537 ReportWarning(
"String edit descriptor in READ format expression");
541 if (cursor_ >= end_ && !unterminatedFormatError_) {
542 suppressMessageCascade_ =
false;
543 ReportError(
"Unterminated format expression");
544 unterminatedFormatError_ =
true;
546 token_.set_kind(TokenKind::None);
553template <
typename CHAR>
void FormatValidator<CHAR>::check_r(
bool allowed) {
554 if (!allowed && knrValue_ >= 0) {
555 ReportError(
"Repeat specifier before '%s' edit descriptor", knrToken_);
556 }
else if (knrValue_ == 0) {
557 ReportError(
"'%s' edit descriptor repeat specifier must be positive",
563template <
typename CHAR>
bool FormatValidator<CHAR>::check_w() {
564 if (token_.kind() == TokenKind::UnsignedInteger) {
565 wValue_ = integerValue_;
567 if (*argString_ ==
'A' || stmt_ == IoStmtKind::Read) {
569 ReportError(
"'%s' edit descriptor 'w' value must be positive");
570 }
else if (*argString_ ==
'L') {
571 ReportWarning(
"'%s' edit descriptor 'w' value should be positive");
577 if (*argString_ !=
'A' && *argString_ !=
'L') {
578 ReportWarning(
"Expected '%s' edit descriptor 'w' value");
583template <
typename CHAR>
void FormatValidator<CHAR>::check_m() {
584 if (token_.kind() != TokenKind::Point) {
588 if (token_.kind() != TokenKind::UnsignedInteger) {
589 ReportError(
"Expected '%s' edit descriptor 'm' value after '.'");
592 if ((stmt_ == IoStmtKind::Print || stmt_ == IoStmtKind::Write) &&
593 wValue_ > 0 && integerValue_ > wValue_) {
594 ReportError(
"'%s' edit descriptor 'm' value is greater than 'w' value");
600template <
typename CHAR>
601bool FormatValidator<CHAR>::check_d(
bool checkScaleFactor) {
602 if (token_.kind() != TokenKind::Point) {
603 ReportError(
"Expected '%s' edit descriptor '.d' value");
607 if (token_.kind() != TokenKind::UnsignedInteger) {
608 ReportError(
"Expected '%s' edit descriptor 'd' value after '.'");
611 if (checkScaleFactor) {
619template <
typename CHAR>
void FormatValidator<CHAR>::check_k() {
622 if (stmt_ != IoStmtKind::Print && stmt_ != IoStmtKind::Write) {
625 if (!scaleFactorToken_.IsSet()) {
631 const int64_t d{integerValue_};
632 const int64_t k{scaleFactorValue_};
634 if (d == 0 && k == 0) {
637 if (k <= 0 && !(-d < k)) {
638 ReportError(
"Negative scale factor k (from kP) and width d in a '%s' "
639 "edit descriptor must satisfy '-d < k'");
640 }
else if (k > 0 && !(k < d + 2)) {
641 ReportError(
"Positive scale factor k (from kP) and width d in a '%s' "
642 "edit descriptor must satisfy 'k < d+2'");
646template <
typename CHAR>
void FormatValidator<CHAR>::check_e() {
647 if (token_.kind() != TokenKind::E) {
651 if (token_.kind() != TokenKind::UnsignedInteger) {
652 ReportError(
"Expected '%s' edit descriptor 'e' value after 'E'");
658template <
typename CHAR>
bool FormatValidator<CHAR>::Check() {
660 ReportError(
"Empty format expression");
661 return formatHasErrors_;
664 if (token_.kind() != TokenKind::LParen) {
665 ReportError(
"Format expression must have an initial '('");
666 return formatHasErrors_;
672 bool hasDataEditDesc{
false};
679 while (!reporterExit_) {
683 bool commaRequired{
true};
685 if (token_.kind() == TokenKind::Sign) {
689 if (token_.kind() == TokenKind::UnsignedInteger) {
691 knrValue_ = integerValue_;
694 if (signToken.IsSet() && (knrValue_ < 0 || token_.kind() != TokenKind::P)) {
695 argString_[0] = format_[signToken.offset()];
697 ReportError(
"Unexpected '%s' in format expression", signToken);
701 argString_[0] = toupper(format_[token_.offset()]);
702 if (token_.length() > 2) {
706 const CHAR *mid{format_ + token_.offset() + 1};
707 while (mid < cursor_ && IsWhite(*mid)) {
710 argString_[1] = toupper(*mid);
711 argString_[2] = toupper(*cursor_);
713 argString_[1] = token_.length() > 1 ? toupper(*cursor_) : 0;
717 switch (token_.kind()) {
720 hasDataEditDesc =
true;
727 hasDataEditDesc =
true;
730 if (token_.kind() == TokenKind::UnsignedInteger) {
731 ReportError(
"'AT' edit descriptor does not accept a width value");
734 suppressMessageCascade_ =
false;
736 if (stmt_ == IoStmtKind::Read) {
737 ReportError(
"'AT' edit descriptor must not be used for input");
739 suppressMessageCascade_ =
false;
747 hasDataEditDesc =
true;
757 bool isD{token_.kind() == TokenKind::D};
758 hasDataEditDesc =
true;
769 case TokenKind::EX: {
772 bool isE{token_.kind() == TokenKind::E};
773 hasDataEditDesc =
true;
776 if (check_w() && check_d(isE)) {
783 hasDataEditDesc =
true;
791 }
else if (token_.kind() == TokenKind::Point && check_d() &&
792 token_.kind() == TokenKind::E) {
793 ReportError(
"A 'G0' edit descriptor must not have an 'e' value");
795 if (token_.kind() == TokenKind::UnsignedInteger) {
803 hasDataEditDesc =
true;
810 hasDataEditDesc =
true;
813 if (token_.kind() == TokenKind::String) {
816 if (token_.kind() == TokenKind::LParen) {
819 if (token_.kind() == TokenKind::Sign) {
822 if (token_.kind() != TokenKind::UnsignedInteger) {
824 "Expected integer constant in 'DT' edit descriptor v-list");
828 }
while (token_.kind() == TokenKind::Comma);
829 if (token_.kind() != TokenKind::RParen) {
830 ReportError(
"Expected ',' or ')' in 'DT' edit descriptor v-list");
831 while (cursor_ < end_ && token_.kind() != TokenKind::RParen) {
838 case TokenKind::String:
840 if (knrValue_ >= 0) {
841 ReportError(
"Repeat specifier before character string edit descriptor",
873 ReportError(
"'P' edit descriptor must have a scale factor");
875 scaleFactorToken_ = knrToken_;
876 if (signToken.IsSet() && format_[signToken.offset()] ==
'-') {
877 scaleFactorValue_ = -knrValue_;
879 scaleFactorValue_ = knrValue_;
884 const CHAR *saveCursor{cursor_};
886 if (token_.kind() == TokenKind::UnsignedInteger) {
889 switch (token_.kind()) {
897 commaRequired =
false;
901 cursor_ = saveCursor;
911 if (integerValue_ <= 0) {
912 ReportError(
"'%s' edit descriptor must have a positive position value");
918 if (knrValue_ == 0) {
919 ReportError(
"'X' edit descriptor must have a positive position value",
921 }
else if (knrValue_ < 0) {
923 "'X' edit descriptor must have a positive position value");
927 case TokenKind::Colon:
930 commaRequired =
false;
933 case TokenKind::Slash:
935 commaRequired =
false;
938 case TokenKind::Backslash:
940 ReportWarning(
"Non-standard '\\' edit descriptor");
943 case TokenKind::Dollar:
945 ReportWarning(
"Non-standard '$' edit descriptor");
948 case TokenKind::Star:
952 ReportError(
"Nested unlimited format item list");
955 if (knrValue_ >= 0) {
957 "Repeat specifier before unlimited format item list", knrToken_);
959 hasDataEditDesc =
false;
962 case TokenKind::LParen:
963 if (knrValue_ == 0) {
964 ReportError(
"List repeat specifier must be positive", knrToken_);
966 if (++nestLevel > maxNesting_) {
967 maxNesting_ = nestLevel;
969 if (LookAheadChar() ==
')') {
970 ReportError(
"Nested parenthesized format item list is empty");
973 case TokenKind::RParen:
974 if (knrValue_ >= 0) {
975 ReportError(
"Unexpected integer constant", knrToken_);
978 if (nestLevel == 0) {
980 return formatHasErrors_;
982 if (nestLevel == 1 && starToken.IsSet() && !hasDataEditDesc) {
983 SetLength(starToken);
985 "Unlimited format item list must contain a data edit descriptor",
990 }
while (token_.kind() == TokenKind::RParen);
991 if (nestLevel == 0 && starToken.IsSet()) {
992 ReportError(
"Character in format after unlimited format item list");
995 case TokenKind::Comma:
996 if (knrValue_ >= 0) {
997 ReportError(
"Unexpected integer constant", knrToken_);
999 if (suppressMessageCascade_ || reporterExit_) {
1004 ReportError(
"Unexpected '%s' in format expression");
1009 switch (token_.kind()) {
1010 case TokenKind::Colon:
1011 case TokenKind::Slash:
1012 case TokenKind::RParen:
1013 suppressMessageCascade_ =
false;
1015 case TokenKind::LParen:
1016 case TokenKind::Comma:
1017 suppressMessageCascade_ =
false;
1020 case TokenKind::Sign:
1021 case TokenKind::None:
1022 if (cursor_ >= end_) {
1023 return formatHasErrors_;
1028 if (commaRequired) {
1029 const char *s{
"Expected ',' or ')' in format expression"};
1030 if (previousToken_.kind() == TokenKind::UnsignedInteger &&
1031 previousToken_.length() > 1 &&
1032 itemsWithLeadingInts_.test(token_.kind())) {
1042 return formatHasErrors_;
Definition bit-population-count.h:20