return false;
}
+// Advance to the terminal '/' of a namelist group.
+static void SkipNamelistGroup(IoStatementState &io) {
+ while (auto ch{io.GetNextNonBlank()}) {
+ io.HandleRelativePosition(1);
+ if (*ch == '/') {
+ break;
+ } else if (*ch == '\'' || *ch == '"') {
+ // Skip quoted character literal
+ char32_t quote{*ch};
+ while (true) {
+ if ((ch = io.GetCurrentChar())) {
+ io.HandleRelativePosition(1);
+ if (*ch == quote) {
+ break;
+ }
+ } else if (!io.AdvanceRecord()) {
+ return;
+ }
+ }
+ }
+ }
+}
+
bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) {
IoStatementState &io{*cookie};
io.CheckFormattedStmtType<Direction::Input>("InputNamelist");
IoErrorHandler &handler{io.GetIoErrorHandler()};
auto *listInput{io.get_if<ListDirectedStatementState<Direction::Input>>()};
RUNTIME_CHECK(handler, listInput != nullptr);
- // Check the group header
+ // Find this namelist group's header in the input
io.BeginReadingRecord();
- std::optional<char32_t> next{io.GetNextNonBlank()};
- if (!next || *next != '&') {
- handler.SignalError(
- "NAMELIST input group does not begin with '&' (at '%lc')", *next);
- return false;
- }
- io.HandleRelativePosition(1);
+ std::optional<char32_t> next;
char name[nameBufferSize];
- if (!GetLowerCaseName(io, name, sizeof name)) {
- handler.SignalError("NAMELIST input group has no name");
- return false;
- }
RUNTIME_CHECK(handler, group.groupName != nullptr);
- if (std::strcmp(group.groupName, name) != 0) {
- handler.SignalError(
- "NAMELIST input group name '%s' is not the expected '%s'", name,
- group.groupName);
- return false;
+ while (true) {
+ next = io.GetNextNonBlank();
+ while (next && *next != '&') {
+ // Extension: comment lines without ! before namelist groups
+ if (!io.AdvanceRecord()) {
+ next.reset();
+ } else {
+ next = io.GetNextNonBlank();
+ }
+ }
+ if (!next || *next != '&') {
+ handler.SignalError(
+ "NAMELIST input group does not begin with '&' (at '%lc')", *next);
+ return false;
+ }
+ io.HandleRelativePosition(1);
+ if (!GetLowerCaseName(io, name, sizeof name)) {
+ handler.SignalError("NAMELIST input group has no name");
+ return false;
+ }
+ if (std::strcmp(group.groupName, name) == 0) {
+ break; // found it
+ }
+ SkipNamelistGroup(io);
}
// Read the group's items
while (true) {
EXPECT_EQ(*bDesc->ZeroBasedIndexedElement<int>(1), -2);
}
-TEST(NamelistTypes, ScalarSubstring) {
+TEST(NamelistTests, ScalarSubstring) {
OwningPtr<Descriptor> scDesc{MakeArray<TypeCategory::Character, 1>(
std::vector<int>{}, std::vector<std::string>{"abcdefgh"}, 8)};
const NamelistGroup::Item items[]{{"a", *scDesc}};
EXPECT_EQ(got, expect);
}
-TEST(NamelistTypes, ArraySubstring) {
+TEST(NamelistTests, ArraySubstring) {
OwningPtr<Descriptor> scDesc{
MakeArray<TypeCategory::Character, 1>(std::vector<int>{2},
std::vector<std::string>{"abcdefgh", "ijklmnop"}, 8)};
EXPECT_EQ(got, expect);
}
+TEST(NamelistTests, Skip) {
+ OwningPtr<Descriptor> scDesc{
+ MakeArray<TypeCategory::Integer, static_cast<int>(sizeof(int))>(
+ std::vector<int>{}, std::vector<int>{-1})};
+ const NamelistGroup::Item items[]{{"j", *scDesc}};
+ const NamelistGroup group{"nml", 1, items};
+ static char t1[]{"&skip a='str''ing'/&nml j=123/"};
+ 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 input with skipping";
+ char out[20];
+ 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(OutputNamelist)(outCookie, group));
+ ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";
+ std::string got{out, sizeof out};
+ static const std::string expect{"&NML J= 123/ "};
+ EXPECT_EQ(got, expect);
+}
+
// TODO: Internal NAMELIST error tests