Skip to content

Commit 120ad25

Browse files
authored
[flang][runtime] Extension: NAMELIST input may omit terminal '/' (#76476)
... when it is followed eventually by the '&' that begins the next NAMELIST input group. This is a gfortran extension.
1 parent 571ad73 commit 120ad25

File tree

4 files changed

+30
-11
lines changed

4 files changed

+30
-11
lines changed

flang/docs/Extensions.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,9 @@ end
315315
* When a file included via an `INCLUDE` line or `#include` directive
316316
has a continuation marker at the end of its last line in free form,
317317
Fortran line continuation works.
318+
* A `NAMELIST` input group may omit its trailing `/` character if
319+
it is followed by another `NAMELIST` input group.
320+
* A `NAMELIST` input group may begin with either `&` or `$`.
318321

319322
### Extensions supported when enabled by options
320323

flang/runtime/edit-input.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ namespace Fortran::runtime::io {
2121
static inline bool IsCharValueSeparator(const DataEdit &edit, char32_t ch) {
2222
char32_t comma{
2323
edit.modes.editingFlags & decimalComma ? char32_t{';'} : char32_t{','}};
24-
return ch == ' ' || ch == '\t' || ch == '/' || ch == comma;
24+
return ch == ' ' || ch == '\t' || ch == comma || ch == '/' ||
25+
(edit.IsNamelist() && (ch == '&' || ch == '$'));
2526
}
2627

2728
static bool CheckCompleteListDirectedField(
@@ -917,6 +918,10 @@ static bool EditListDirectedCharacterInput(
917918
case '/':
918919
isSep = true;
919920
break;
921+
case '&':
922+
case '$':
923+
isSep = edit.IsNamelist();
924+
break;
920925
case ',':
921926
isSep = !(edit.modes.editingFlags & decimalComma);
922927
break;

flang/runtime/io-stmt.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,12 @@ std::optional<char32_t> IoStatementState::NextInField(
580580
case '*':
581581
case '\n': // for stream access
582582
return std::nullopt;
583+
case '&':
584+
case '$':
585+
if (edit.IsNamelist()) {
586+
return std::nullopt;
587+
}
588+
break;
583589
case ',':
584590
if (!(edit.modes.editingFlags & decimalComma)) {
585591
return std::nullopt;

flang/runtime/namelist.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ bool IONAME(OutputNamelist)(Cookie cookie, const NamelistGroup &group) {
8282

8383
static constexpr bool IsLegalIdStart(char32_t ch) {
8484
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_' ||
85-
ch == '@' || ch == '$';
85+
ch == '@';
8686
}
8787

8888
static constexpr bool IsLegalIdChar(char32_t ch) {
@@ -378,12 +378,13 @@ static bool HandleComponent(IoStatementState &io, Descriptor &desc,
378378
return false;
379379
}
380380

381-
// Advance to the terminal '/' of a namelist group.
381+
// Advance to the terminal '/' of a namelist group or leading '&'/'$'
382+
// of the next.
382383
static void SkipNamelistGroup(IoStatementState &io) {
383384
std::size_t byteCount{0};
384385
while (auto ch{io.GetNextNonBlank(byteCount)}) {
385386
io.HandleRelativePosition(byteCount);
386-
if (*ch == '/') {
387+
if (*ch == '/' || *ch == '&' || *ch == '$') {
387388
break;
388389
} else if (*ch == '\'' || *ch == '"') {
389390
// Skip quoted character literal
@@ -418,7 +419,7 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) {
418419
std::size_t byteCount{0};
419420
while (true) {
420421
next = io.GetNextNonBlank(byteCount);
421-
while (next && *next != '&') {
422+
while (next && *next != '&' && *next != '$') {
422423
// Extension: comment lines without ! before namelist groups
423424
if (!io.AdvanceRecord()) {
424425
next.reset();
@@ -430,9 +431,10 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) {
430431
handler.SignalEnd();
431432
return false;
432433
}
433-
if (*next != '&') {
434+
if (*next != '&' && *next != '$') {
434435
handler.SignalError(
435-
"NAMELIST input group does not begin with '&' (at '%lc')", *next);
436+
"NAMELIST input group does not begin with '&' or '$' (at '%lc')",
437+
*next);
436438
return false;
437439
}
438440
io.HandleRelativePosition(byteCount);
@@ -448,7 +450,7 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) {
448450
// Read the group's items
449451
while (true) {
450452
next = io.GetNextNonBlank(byteCount);
451-
if (!next || *next == '/') {
453+
if (!next || *next == '/' || *next == '&' || *next == '$') {
452454
break;
453455
}
454456
if (!GetLowerCaseName(io, name, sizeof name)) {
@@ -540,12 +542,15 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) {
540542
io.HandleRelativePosition(byteCount);
541543
}
542544
}
543-
if (!next || *next != '/') {
545+
if (next && *next == '/') {
546+
io.HandleRelativePosition(byteCount);
547+
} else if (*next && (*next == '&' || *next == '$')) {
548+
// stop at beginning of next group
549+
} else {
544550
handler.SignalError(
545551
"No '/' found after NAMELIST group '%s'", group.groupName);
546552
return false;
547553
}
548-
io.HandleRelativePosition(byteCount);
549554
return true;
550555
}
551556

@@ -565,7 +570,7 @@ bool IsNamelistNameOrSlash(IoStatementState &io) {
565570
// TODO: how to deal with NaN(...) ambiguity?
566571
return ch && (*ch == '=' || *ch == '(' || *ch == '%');
567572
} else {
568-
return *ch == '/';
573+
return *ch == '/' || *ch == '&' || *ch == '$';
569574
}
570575
}
571576
}

0 commit comments

Comments
 (0)