[flang-commits] [flang] [flang][runtime] Extension: NAMELIST input may omit terminal '/' (PR #76476)
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Sun Dec 31 12:36:41 PST 2023
https://github.com/klausler updated https://github.com/llvm/llvm-project/pull/76476
>From b8deb47eae1c17b2c9e7457889d2f5b3103510c0 Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
Date: Wed, 27 Dec 2023 15:49:18 -0800
Subject: [PATCH] [flang][runtime] Extension: NAMELIST input may omit terminal
'/'
... when it is followed eventually by the '&' that begins the next
NAMELIST input group. This is a gfortran extension. While here,
also support '$' as a substitute for '&' before a NAMELIST input
group name.
Fixes llvm-test-suite/Fortran/gfortran/regression/namelist_21.f90
and .../namelist_37.f90.
---
flang/docs/Extensions.md | 3 +++
flang/runtime/edit-input.cpp | 7 ++++++-
flang/runtime/io-stmt.cpp | 2 ++
flang/runtime/namelist.cpp | 25 +++++++++++++++----------
4 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md
index 6c6588025a392d..ab040b61703c8b 100644
--- a/flang/docs/Extensions.md
+++ b/flang/docs/Extensions.md
@@ -315,6 +315,9 @@ end
* When a file included via an `INCLUDE` line or `#include` directive
has a continuation marker at the end of its last line in free form,
Fortran line continuation works.
+* A `NAMELIST` input group may omit its trailing `/` character if
+ it is followed by another `NAMELIST` input group.
+* A `NAMELIST` input group may begin with either `&` or `$`.
### Extensions supported when enabled by options
diff --git a/flang/runtime/edit-input.cpp b/flang/runtime/edit-input.cpp
index c4fa186e289db2..0fa6368ee591c0 100644
--- a/flang/runtime/edit-input.cpp
+++ b/flang/runtime/edit-input.cpp
@@ -21,7 +21,8 @@ namespace Fortran::runtime::io {
static inline bool IsCharValueSeparator(const DataEdit &edit, char32_t ch) {
char32_t comma{
edit.modes.editingFlags & decimalComma ? char32_t{';'} : char32_t{','}};
- return ch == ' ' || ch == '\t' || ch == '/' || ch == comma;
+ return ch == ' ' || ch == '\t' || ch == comma || ch == '/' ||
+ (edit.IsNamelist() && (ch == '&' || ch == '$'));
}
static bool CheckCompleteListDirectedField(
@@ -917,6 +918,10 @@ static bool EditListDirectedCharacterInput(
case '/':
isSep = true;
break;
+ case '&':
+ case '$':
+ isSep = edit.IsNamelist();
+ break;
case ',':
isSep = !(edit.modes.editingFlags & decimalComma);
break;
diff --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp
index 921c6e625edb5d..ffa8cd940c98f1 100644
--- a/flang/runtime/io-stmt.cpp
+++ b/flang/runtime/io-stmt.cpp
@@ -573,6 +573,8 @@ std::optional<char32_t> IoStatementState::NextInField(
case ' ':
case '\t':
case '/':
+ case '&':
+ case '$':
case '(':
case ')':
case '\'':
diff --git a/flang/runtime/namelist.cpp b/flang/runtime/namelist.cpp
index 61815a7cc8a403..d9908bf7089ac2 100644
--- a/flang/runtime/namelist.cpp
+++ b/flang/runtime/namelist.cpp
@@ -82,7 +82,7 @@ bool IONAME(OutputNamelist)(Cookie cookie, const NamelistGroup &group) {
static constexpr bool IsLegalIdStart(char32_t ch) {
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_' ||
- ch == '@' || ch == '$';
+ ch == '@';
}
static constexpr bool IsLegalIdChar(char32_t ch) {
@@ -378,12 +378,13 @@ static bool HandleComponent(IoStatementState &io, Descriptor &desc,
return false;
}
-// Advance to the terminal '/' of a namelist group.
+// Advance to the terminal '/' of a namelist group or leading '&'/'$'
+// of the next.
static void SkipNamelistGroup(IoStatementState &io) {
std::size_t byteCount{0};
while (auto ch{io.GetNextNonBlank(byteCount)}) {
io.HandleRelativePosition(byteCount);
- if (*ch == '/') {
+ if (*ch == '/' || *ch == '&' || *ch == '$') {
break;
} else if (*ch == '\'' || *ch == '"') {
// Skip quoted character literal
@@ -418,7 +419,7 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) {
std::size_t byteCount{0};
while (true) {
next = io.GetNextNonBlank(byteCount);
- while (next && *next != '&') {
+ while (next && *next != '&' && *next != '$') {
// Extension: comment lines without ! before namelist groups
if (!io.AdvanceRecord()) {
next.reset();
@@ -430,9 +431,10 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) {
handler.SignalEnd();
return false;
}
- if (*next != '&') {
+ if (*next != '&' && *next != '$') {
handler.SignalError(
- "NAMELIST input group does not begin with '&' (at '%lc')", *next);
+ "NAMELIST input group does not begin with '&' or '$' (at '%lc')",
+ *next);
return false;
}
io.HandleRelativePosition(byteCount);
@@ -448,7 +450,7 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) {
// Read the group's items
while (true) {
next = io.GetNextNonBlank(byteCount);
- if (!next || *next == '/') {
+ if (!next || *next == '/' || *next == '&' || *next == '$') {
break;
}
if (!GetLowerCaseName(io, name, sizeof name)) {
@@ -540,12 +542,15 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) {
io.HandleRelativePosition(byteCount);
}
}
- if (!next || *next != '/') {
+ if (next && *next == '/') {
+ io.HandleRelativePosition(byteCount);
+ } else if (*next && (*next == '&' || *next == '$')) {
+ // stop at beginning of next group
+ } else {
handler.SignalError(
"No '/' found after NAMELIST group '%s'", group.groupName);
return false;
}
- io.HandleRelativePosition(byteCount);
return true;
}
@@ -565,7 +570,7 @@ bool IsNamelistNameOrSlash(IoStatementState &io) {
// TODO: how to deal with NaN(...) ambiguity?
return ch && (*ch == '=' || *ch == '(' || *ch == '%');
} else {
- return *ch == '/';
+ return *ch == '/' || *ch == '&' || *ch == '$';
}
}
}
More information about the flang-commits
mailing list