[flang-commits] [flang] 0ab1708 - [flang] Support substring references in NAMELIST input

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Tue Jan 18 11:24:24 PST 2022


Author: Peter Klausler
Date: 2022-01-18T11:24:17-08:00
New Revision: 0ab170803fc1d019b730761a0f94d7c3557b2ea0

URL: https://github.com/llvm/llvm-project/commit/0ab170803fc1d019b730761a0f94d7c3557b2ea0
DIFF: https://github.com/llvm/llvm-project/commit/0ab170803fc1d019b730761a0f94d7c3557b2ea0.diff

LOG: [flang] Support substring references in NAMELIST input

Implements substring references into potentially partial CHARACTER
scalars and array elements in NAMELIST input.

Differential Revision: https://reviews.llvm.org/D117576

Added: 
    

Modified: 
    flang/runtime/namelist.cpp
    flang/unittests/Runtime/Namelist.cpp

Removed: 
    


################################################################################
diff  --git a/flang/runtime/namelist.cpp b/flang/runtime/namelist.cpp
index 0afdee5cfe94e..2804679db01e2 100644
--- a/flang/runtime/namelist.cpp
+++ b/flang/runtime/namelist.cpp
@@ -225,6 +225,67 @@ static bool HandleSubscripts(IoStatementState &io, Descriptor &desc,
   return false;
 }
 
+static bool HandleSubstring(
+    IoStatementState &io, Descriptor &desc, const char *name) {
+  IoErrorHandler &handler{io.GetIoErrorHandler()};
+  auto pair{desc.type().GetCategoryAndKind()};
+  if (!pair || pair->first != TypeCategory::Character) {
+    handler.SignalError("Substring reference to non-character item '%s'", name);
+    return false;
+  }
+  int kind{pair->second};
+  SubscriptValue chars{static_cast<SubscriptValue>(desc.ElementBytes()) / kind};
+  // Allow for blanks in substring bounds; they're nonstandard, but not
+  // ambiguous within the parentheses.
+  io.HandleRelativePosition(1); // skip '('
+  std::optional<SubscriptValue> lower, upper;
+  std::optional<char32_t> ch{io.GetNextNonBlank()};
+  if (ch) {
+    if (*ch == ':') {
+      lower = 1;
+    } else {
+      lower = GetSubscriptValue(io);
+      ch = io.GetNextNonBlank();
+    }
+  }
+  if (ch && ch == ':') {
+    io.HandleRelativePosition(1);
+    ch = io.GetNextNonBlank();
+    if (ch) {
+      if (*ch == ')') {
+        upper = chars;
+      } else {
+        upper = GetSubscriptValue(io);
+        ch = io.GetNextNonBlank();
+      }
+    }
+  }
+  if (ch && *ch == ')') {
+    io.HandleRelativePosition(1);
+    if (lower && upper) {
+      if (*lower > *upper) {
+        // An empty substring, whatever the values are
+        desc.raw().elem_len = 0;
+        return true;
+      }
+      if (*lower >= 1 || *upper <= chars) {
+        // Offset the base address & adjust the element byte length
+        desc.raw().elem_len = (*upper - *lower + 1) * kind;
+        desc.set_base_addr(reinterpret_cast<void *>(
+            reinterpret_cast<char *>(desc.raw().base_addr) +
+            kind * (*lower - 1)));
+        return true;
+      }
+    }
+    handler.SignalError(
+        "Bad substring bounds for NAMELIST input group item '%s'", name);
+  } else {
+    handler.SignalError(
+        "Bad substring (missing ')') for NAMELIST input group item '%s'", name);
+  }
+  return false;
+}
+
 static bool HandleComponent(IoStatementState &io, Descriptor &desc,
     const Descriptor &source, const char *name) {
   IoErrorHandler &handler{io.GetIoErrorHandler()};
@@ -319,19 +380,36 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) {
     StaticDescriptor<maxRank, true, 16> staticDesc[2];
     int whichStaticDesc{0};
     next = io.GetCurrentChar();
+    bool hadSubscripts{false};
+    bool hadSubstring{false};
     if (next && (*next == '(' || *next == '%')) {
       do {
         Descriptor &mutableDescriptor{staticDesc[whichStaticDesc].descriptor()};
         whichStaticDesc ^= 1;
         if (*next == '(') {
-          if (!(HandleSubscripts(
-                  io, mutableDescriptor, *useDescriptor, name))) {
+          if (!hadSubstring && (hadSubscripts || useDescriptor->rank() == 0)) {
+            mutableDescriptor = *useDescriptor;
+            mutableDescriptor.raw().attribute = CFI_attribute_pointer;
+            if (!HandleSubstring(io, mutableDescriptor, name)) {
+              return false;
+            }
+            hadSubstring = true;
+          } else if (hadSubscripts) {
+            handler.SignalError("Multiple sets of subscripts for item '%s' in "
+                                "NAMELIST group '%s'",
+                name, group.groupName);
+            return false;
+          } else if (!HandleSubscripts(
+                         io, mutableDescriptor, *useDescriptor, name)) {
             return false;
           }
+          hadSubscripts = true;
         } else {
           if (!HandleComponent(io, mutableDescriptor, *useDescriptor, name)) {
             return false;
           }
+          hadSubscripts = false;
+          hadSubstring = false;
         }
         useDescriptor = &mutableDescriptor;
         next = io.GetCurrentChar();

diff  --git a/flang/unittests/Runtime/Namelist.cpp b/flang/unittests/Runtime/Namelist.cpp
index fd5ee6be1b791..4770e26048de9 100644
--- a/flang/unittests/Runtime/Namelist.cpp
+++ b/flang/unittests/Runtime/Namelist.cpp
@@ -136,8 +136,8 @@ TEST(NamelistTests, Subscripts) {
   const NamelistGroup::Item items[]{{"a", *aDesc}};
   const NamelistGroup group{"justa", 1, items};
   static char t1[]{"&justa A(0,1:-1:-2)=1 2/"};
-  StaticDescriptor<1, true> statDescs[2];
-  Descriptor &internalDesc{statDescs[0].descriptor()};
+  StaticDescriptor<1, true> statDesc;
+  Descriptor &internalDesc{statDesc.descriptor()};
   internalDesc.Establish(TypeCode{CFI_type_char},
       /*elementBytes=*/std::strlen(t1), t1, 0, nullptr, CFI_attribute_pointer);
   auto inCookie{IONAME(BeginInternalArrayListInput)(
@@ -189,4 +189,61 @@ TEST(NamelistTests, ShortArrayInput) {
   EXPECT_EQ(*bDesc->ZeroBasedIndexedElement<int>(1), -2);
 }
 
+TEST(NamelistTypes, ScalarSubstring) {
+  OwningPtr<Descriptor> scDesc{MakeArray<TypeCategory::Character, 1>(
+      std::vector<int>{}, std::vector<std::string>{"abcdefgh"}, 8)};
+  const NamelistGroup::Item items[]{{"a", *scDesc}};
+  const NamelistGroup group{"justa", 1, items};
+  static char t1[]{"&justa A(2:5)='BCDE'/"};
+  StaticDescriptor<1, true> statDesc;
+  Descriptor &internalDesc{statDesc.descriptor()};
+  internalDesc.Establish(TypeCode{CFI_type_char},
+      /*elementBytes=*/std::strlen(t1), t1, 0, nullptr, CFI_attribute_pointer);
+  auto inCookie{IONAME(BeginInternalArrayListInput)(
+      internalDesc, nullptr, 0, __FILE__, __LINE__)};
+  ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));
+  ASSERT_EQ(IONAME(EndIoStatement)(inCookie), IostatOk)
+      << "namelist scalar substring input";
+  char out[32];
+  internalDesc.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/sizeof out,
+      out, 0, nullptr, CFI_attribute_pointer);
+  auto outCookie{IONAME(BeginInternalArrayListOutput)(
+      internalDesc, nullptr, 0, __FILE__, __LINE__)};
+  ASSERT_TRUE(IONAME(SetDelim)(outCookie, "apostrophe", 10));
+  ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));
+  ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";
+  std::string got{out, sizeof out};
+  static const std::string expect{"&JUSTA A= 'aBCDEfgh'/           "};
+  EXPECT_EQ(got, expect);
+}
+
+TEST(NamelistTypes, ArraySubstring) {
+  OwningPtr<Descriptor> scDesc{
+      MakeArray<TypeCategory::Character, 1>(std::vector<int>{2},
+          std::vector<std::string>{"abcdefgh", "ijklmnop"}, 8)};
+  const NamelistGroup::Item items[]{{"a", *scDesc}};
+  const NamelistGroup group{"justa", 1, items};
+  static char t1[]{"&justa A(:)(2:5)='BCDE' 'JKLM'/"};
+  StaticDescriptor<1, true> statDesc;
+  Descriptor &internalDesc{statDesc.descriptor()};
+  internalDesc.Establish(TypeCode{CFI_type_char},
+      /*elementBytes=*/std::strlen(t1), t1, 0, nullptr, CFI_attribute_pointer);
+  auto inCookie{IONAME(BeginInternalArrayListInput)(
+      internalDesc, nullptr, 0, __FILE__, __LINE__)};
+  ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));
+  ASSERT_EQ(IONAME(EndIoStatement)(inCookie), IostatOk)
+      << "namelist scalar substring input";
+  char out[40];
+  internalDesc.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/sizeof out,
+      out, 0, nullptr, CFI_attribute_pointer);
+  auto outCookie{IONAME(BeginInternalArrayListOutput)(
+      internalDesc, nullptr, 0, __FILE__, __LINE__)};
+  ASSERT_TRUE(IONAME(SetDelim)(outCookie, "apostrophe", 10));
+  ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));
+  ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";
+  std::string got{out, sizeof out};
+  static const std::string expect{"&JUSTA A= 'aBCDEfgh' 'iJKLMnop'/        "};
+  EXPECT_EQ(got, expect);
+}
+
 // TODO: Internal NAMELIST error tests


        


More information about the flang-commits mailing list