xref: /llvm-project/flang/lib/Parser/io-parsers.cpp (revision 4ad7279392653c0bcf564799ffb3f7e20ed4ef00)
1 //===-- lib/Parser/io-parsers.cpp -----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // Per-type parsers for I/O statements and FORMAT
10 
11 #include "basic-parsers.h"
12 #include "expr-parsers.h"
13 #include "misc-parsers.h"
14 #include "stmt-parser.h"
15 #include "token-parsers.h"
16 #include "type-parser-implementation.h"
17 #include "flang/Parser/characters.h"
18 #include "flang/Parser/parse-tree.h"
19 
20 namespace Fortran::parser {
21 // R1201 io-unit -> file-unit-number | * | internal-file-variable
22 // R1203 internal-file-variable -> char-variable
23 // R905 char-variable -> variable
24 // "char-variable" is attempted first since it's not type constrained but
25 // syntactically ambiguous with "file-unit-number", which is constrained.
26 TYPE_PARSER(construct<IoUnit>(variable / lookAhead(space / ",);\n"_ch)) ||
27     construct<IoUnit>(fileUnitNumber) || construct<IoUnit>(star))
28 
29 // R1202 file-unit-number -> scalar-int-expr
30 TYPE_PARSER(construct<FileUnitNumber>(scalarIntExpr / !"="_tok))
31 
32 // R1204 open-stmt -> OPEN ( connect-spec-list )
33 TYPE_CONTEXT_PARSER("OPEN statement"_en_US,
34     construct<OpenStmt>(
35         "OPEN (" >> nonemptyList("expected connection specifications"_err_en_US,
36                         Parser<ConnectSpec>{}) /
37             ")"))
38 
39 // R1206 file-name-expr -> scalar-default-char-expr
40 constexpr auto fileNameExpr{scalarDefaultCharExpr};
41 
42 // R1205 connect-spec ->
43 //         [UNIT =] file-unit-number | ACCESS = scalar-default-char-expr |
44 //         ACTION = scalar-default-char-expr |
45 //         ASYNCHRONOUS = scalar-default-char-expr |
46 //         BLANK = scalar-default-char-expr |
47 //         DECIMAL = scalar-default-char-expr |
48 //         DELIM = scalar-default-char-expr |
49 //         ENCODING = scalar-default-char-expr | ERR = label |
50 //         FILE = file-name-expr | FORM = scalar-default-char-expr |
51 //         IOMSG = iomsg-variable | IOSTAT = scalar-int-variable |
52 //         NEWUNIT = scalar-int-variable | PAD = scalar-default-char-expr |
53 //         POSITION = scalar-default-char-expr | RECL = scalar-int-expr |
54 //         ROUND = scalar-default-char-expr | SIGN = scalar-default-char-expr |
55 //         STATUS = scalar-default-char-expr
56 //         @ | CARRIAGECONTROL = scalar-default-char-variable
57 //           | CONVERT = scalar-default-char-variable
58 //           | DISPOSE = scalar-default-char-variable
59 constexpr auto statusExpr{construct<StatusExpr>(scalarDefaultCharExpr)};
60 constexpr auto errLabel{construct<ErrLabel>(label)};
61 
62 TYPE_PARSER(first(construct<ConnectSpec>(maybe("UNIT ="_tok) >> fileUnitNumber),
63     construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
64         "ACCESS =" >> pure(ConnectSpec::CharExpr::Kind::Access),
65         scalarDefaultCharExpr)),
66     construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
67         "ACTION =" >> pure(ConnectSpec::CharExpr::Kind::Action),
68         scalarDefaultCharExpr)),
69     construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
70         "ASYNCHRONOUS =" >> pure(ConnectSpec::CharExpr::Kind::Asynchronous),
71         scalarDefaultCharExpr)),
72     construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
73         "BLANK =" >> pure(ConnectSpec::CharExpr::Kind::Blank),
74         scalarDefaultCharExpr)),
75     construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
76         "DECIMAL =" >> pure(ConnectSpec::CharExpr::Kind::Decimal),
77         scalarDefaultCharExpr)),
78     construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
79         "DELIM =" >> pure(ConnectSpec::CharExpr::Kind::Delim),
80         scalarDefaultCharExpr)),
81     construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
82         "ENCODING =" >> pure(ConnectSpec::CharExpr::Kind::Encoding),
83         scalarDefaultCharExpr)),
84     construct<ConnectSpec>("ERR =" >> errLabel),
85     construct<ConnectSpec>("FILE =" >> fileNameExpr),
86     extension<LanguageFeature::FileName>(
87         "nonstandard usage: NAME= in place of FILE="_port_en_US,
88         construct<ConnectSpec>("NAME =" >> fileNameExpr)),
89     construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
90         "FORM =" >> pure(ConnectSpec::CharExpr::Kind::Form),
91         scalarDefaultCharExpr)),
92     construct<ConnectSpec>("IOMSG =" >> msgVariable),
93     construct<ConnectSpec>("IOSTAT =" >> statVariable),
94     construct<ConnectSpec>(construct<ConnectSpec::Newunit>(
95         "NEWUNIT =" >> scalar(integer(variable)))),
96     construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
97         "PAD =" >> pure(ConnectSpec::CharExpr::Kind::Pad),
98         scalarDefaultCharExpr)),
99     construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
100         "POSITION =" >> pure(ConnectSpec::CharExpr::Kind::Position),
101         scalarDefaultCharExpr)),
102     construct<ConnectSpec>(
103         construct<ConnectSpec::Recl>("RECL =" >> scalarIntExpr)),
104     construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
105         "ROUND =" >> pure(ConnectSpec::CharExpr::Kind::Round),
106         scalarDefaultCharExpr)),
107     construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
108         "SIGN =" >> pure(ConnectSpec::CharExpr::Kind::Sign),
109         scalarDefaultCharExpr)),
110     construct<ConnectSpec>("STATUS =" >> statusExpr),
111     extension<LanguageFeature::Carriagecontrol>(
112         "nonstandard usage: CARRIAGECONTROL="_port_en_US,
113         construct<ConnectSpec>(
114             construct<ConnectSpec::CharExpr>("CARRIAGECONTROL =" >>
115                     pure(ConnectSpec::CharExpr::Kind::Carriagecontrol),
116                 scalarDefaultCharExpr))),
117     extension<LanguageFeature::Convert>(
118         "nonstandard usage: CONVERT="_port_en_US,
119         construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
120             "CONVERT =" >> pure(ConnectSpec::CharExpr::Kind::Convert),
121             scalarDefaultCharExpr))),
122     extension<LanguageFeature::Dispose>(
123         "nonstandard usage: DISPOSE="_port_en_US,
124         construct<ConnectSpec>(construct<ConnectSpec::CharExpr>(
125             "DISPOSE =" >> pure(ConnectSpec::CharExpr::Kind::Dispose),
126             scalarDefaultCharExpr)))))
127 
128 // R1209 close-spec ->
129 //         [UNIT =] file-unit-number | IOSTAT = scalar-int-variable |
130 //         IOMSG = iomsg-variable | ERR = label |
131 //         STATUS = scalar-default-char-expr
132 constexpr auto closeSpec{first(
133     construct<CloseStmt::CloseSpec>(maybe("UNIT ="_tok) >> fileUnitNumber),
134     construct<CloseStmt::CloseSpec>("IOSTAT =" >> statVariable),
135     construct<CloseStmt::CloseSpec>("IOMSG =" >> msgVariable),
136     construct<CloseStmt::CloseSpec>("ERR =" >> errLabel),
137     construct<CloseStmt::CloseSpec>("STATUS =" >> statusExpr))};
138 
139 // R1208 close-stmt -> CLOSE ( close-spec-list )
140 TYPE_CONTEXT_PARSER("CLOSE statement"_en_US,
141     construct<CloseStmt>("CLOSE" >> parenthesized(nonemptyList(closeSpec))))
142 
143 // R1210 read-stmt ->
144 //         READ ( io-control-spec-list ) [input-item-list] |
145 //         READ format [, input-item-list]
146 // The ambiguous READ(CVAR) is parsed as if CVAR were the unit.
147 // As Fortran doesn't have internal unformatted I/O, it should
148 // be parsed as if (CVAR) were a format; this is corrected by
149 // rewriting in semantics when we know that CVAR is character.
150 constexpr auto inputItemList{
151     extension<LanguageFeature::IOListLeadingComma>(
152         "nonstandard usage: leading comma in input item list"_port_en_US,
153         some("," >> inputItem)) || // legacy extension: leading comma
154     optionalList(inputItem)};
155 
156 TYPE_CONTEXT_PARSER("READ statement"_en_US,
157     construct<ReadStmt>("READ (" >>
158             construct<std::optional<IoUnit>>(maybe("UNIT ="_tok) >> ioUnit),
159         "," >> construct<std::optional<Format>>(format),
160         defaulted("," >> nonemptyList(ioControlSpec)) / ")", inputItemList) ||
161         construct<ReadStmt>(
162             "READ (" >> construct<std::optional<IoUnit>>(ioUnit),
163             construct<std::optional<Format>>(),
164             defaulted("," >> nonemptyList(ioControlSpec)) / ")",
165             inputItemList) ||
166         construct<ReadStmt>("READ" >> construct<std::optional<IoUnit>>(),
167             construct<std::optional<Format>>(),
168             parenthesized(nonemptyList(ioControlSpec)), inputItemList) ||
169         construct<ReadStmt>("READ" >> construct<std::optional<IoUnit>>(),
170             construct<std::optional<Format>>(format),
171             construct<std::list<IoControlSpec>>(), many("," >> inputItem)))
172 
173 // R1214 id-variable -> scalar-int-variable
174 constexpr auto idVariable{construct<IdVariable>(scalarIntVariable)};
175 
176 // R1213 io-control-spec ->
177 //         [UNIT =] io-unit | [FMT =] format | [NML =] namelist-group-name |
178 //         ADVANCE = scalar-default-char-expr |
179 //         ASYNCHRONOUS = scalar-default-char-constant-expr |
180 //         BLANK = scalar-default-char-expr |
181 //         DECIMAL = scalar-default-char-expr |
182 //         DELIM = scalar-default-char-expr | END = label | EOR = label |
183 //         ERR = label | ID = id-variable | IOMSG = iomsg-variable |
184 //         IOSTAT = scalar-int-variable | PAD = scalar-default-char-expr |
185 //         POS = scalar-int-expr | REC = scalar-int-expr |
186 //         ROUND = scalar-default-char-expr | SIGN = scalar-default-char-expr |
187 //         SIZE = scalar-int-variable
188 constexpr auto endLabel{construct<EndLabel>(label)};
189 constexpr auto eorLabel{construct<EorLabel>(label)};
190 TYPE_PARSER(first(construct<IoControlSpec>("UNIT =" >> ioUnit),
191     construct<IoControlSpec>("FMT =" >> format),
192     construct<IoControlSpec>("NML =" >> name),
193     construct<IoControlSpec>(
194         "ADVANCE =" >> construct<IoControlSpec::CharExpr>(
195                            pure(IoControlSpec::CharExpr::Kind::Advance),
196                            scalarDefaultCharExpr)),
197     construct<IoControlSpec>(construct<IoControlSpec::Asynchronous>(
198         "ASYNCHRONOUS =" >> scalarDefaultCharConstantExpr)),
199     construct<IoControlSpec>("BLANK =" >>
200         construct<IoControlSpec::CharExpr>(
201             pure(IoControlSpec::CharExpr::Kind::Blank), scalarDefaultCharExpr)),
202     construct<IoControlSpec>(
203         "DECIMAL =" >> construct<IoControlSpec::CharExpr>(
204                            pure(IoControlSpec::CharExpr::Kind::Decimal),
205                            scalarDefaultCharExpr)),
206     construct<IoControlSpec>("DELIM =" >>
207         construct<IoControlSpec::CharExpr>(
208             pure(IoControlSpec::CharExpr::Kind::Delim), scalarDefaultCharExpr)),
209     construct<IoControlSpec>("END =" >> endLabel),
210     construct<IoControlSpec>("EOR =" >> eorLabel),
211     construct<IoControlSpec>("ERR =" >> errLabel),
212     construct<IoControlSpec>("ID =" >> idVariable),
213     construct<IoControlSpec>("IOMSG = " >> msgVariable),
214     construct<IoControlSpec>("IOSTAT = " >> statVariable),
215     construct<IoControlSpec>("PAD =" >>
216         construct<IoControlSpec::CharExpr>(
217             pure(IoControlSpec::CharExpr::Kind::Pad), scalarDefaultCharExpr)),
218     construct<IoControlSpec>(
219         "POS =" >> construct<IoControlSpec::Pos>(scalarIntExpr)),
220     construct<IoControlSpec>(
221         "REC =" >> construct<IoControlSpec::Rec>(scalarIntExpr)),
222     construct<IoControlSpec>("ROUND =" >>
223         construct<IoControlSpec::CharExpr>(
224             pure(IoControlSpec::CharExpr::Kind::Round), scalarDefaultCharExpr)),
225     construct<IoControlSpec>("SIGN =" >>
226         construct<IoControlSpec::CharExpr>(
227             pure(IoControlSpec::CharExpr::Kind::Sign), scalarDefaultCharExpr)),
228     construct<IoControlSpec>(
229         "SIZE =" >> construct<IoControlSpec::Size>(scalarIntVariable))))
230 
231 // R1211 write-stmt -> WRITE ( io-control-spec-list ) [output-item-list]
232 constexpr auto outputItemList{
233     extension<LanguageFeature::IOListLeadingComma>(
234         "nonstandard usage: leading comma in output item list"_port_en_US,
235         some("," >> outputItem)) || // legacy: allow leading comma
236     optionalList(outputItem)};
237 
238 TYPE_CONTEXT_PARSER("WRITE statement"_en_US,
239     construct<WriteStmt>("WRITE (" >>
240             construct<std::optional<IoUnit>>(maybe("UNIT ="_tok) >> ioUnit),
241         "," >> construct<std::optional<Format>>(format),
242         defaulted("," >> nonemptyList(ioControlSpec)) / ")", outputItemList) ||
243         construct<WriteStmt>(
244             "WRITE (" >> construct<std::optional<IoUnit>>(ioUnit),
245             construct<std::optional<Format>>(),
246             defaulted("," >> nonemptyList(ioControlSpec)) / ")",
247             outputItemList) ||
248         construct<WriteStmt>("WRITE" >> construct<std::optional<IoUnit>>(),
249             construct<std::optional<Format>>(),
250             parenthesized(nonemptyList(ioControlSpec)), outputItemList))
251 
252 // R1212 print-stmt PRINT format [, output-item-list]
253 TYPE_CONTEXT_PARSER("PRINT statement"_en_US,
254     construct<PrintStmt>(
255         "PRINT" >> format, defaulted("," >> nonemptyList(outputItem))))
256 
257 // R1215 format -> default-char-expr | label | *
258 // deprecated(ASSIGN): | scalar-int-name
259 TYPE_PARSER(construct<Format>(label / !"_."_ch) ||
260     construct<Format>(expr / !"="_tok) || construct<Format>(star))
261 
262 // R1216 input-item -> variable | io-implied-do
263 TYPE_PARSER(construct<InputItem>(variable) ||
264     construct<InputItem>(indirect(inputImpliedDo)))
265 
266 // R1217 output-item -> expr | io-implied-do
267 TYPE_PARSER(construct<OutputItem>(expr) ||
268     construct<OutputItem>(indirect(outputImpliedDo)))
269 
270 // R1220 io-implied-do-control ->
271 //         do-variable = scalar-int-expr , scalar-int-expr [, scalar-int-expr]
272 constexpr auto ioImpliedDoControl{loopBounds(scalarIntExpr)};
273 
274 // R1218 io-implied-do -> ( io-implied-do-object-list , io-implied-do-control )
275 // R1219 io-implied-do-object -> input-item | output-item
276 TYPE_CONTEXT_PARSER("input implied DO"_en_US,
277     parenthesized(
278         construct<InputImpliedDo>(nonemptyList(inputItem / lookAhead(","_tok)),
279             "," >> ioImpliedDoControl)))
280 TYPE_CONTEXT_PARSER("output implied DO"_en_US,
281     parenthesized(construct<OutputImpliedDo>(
282         nonemptyList(outputItem / lookAhead(","_tok)),
283         "," >> ioImpliedDoControl)))
284 
285 // R1222 wait-stmt -> WAIT ( wait-spec-list )
286 TYPE_CONTEXT_PARSER("WAIT statement"_en_US,
287     "WAIT" >>
288         parenthesized(construct<WaitStmt>(nonemptyList(Parser<WaitSpec>{}))))
289 
290 // R1223 wait-spec ->
291 //         [UNIT =] file-unit-number | END = label | EOR = label | ERR = label |
292 //         ID = scalar-int-expr | IOMSG = iomsg-variable |
293 //         IOSTAT = scalar-int-variable
294 constexpr auto idExpr{construct<IdExpr>(scalarIntExpr)};
295 
296 TYPE_PARSER(first(construct<WaitSpec>(maybe("UNIT ="_tok) >> fileUnitNumber),
297     construct<WaitSpec>("END =" >> endLabel),
298     construct<WaitSpec>("EOR =" >> eorLabel),
299     construct<WaitSpec>("ERR =" >> errLabel),
300     construct<WaitSpec>("ID =" >> idExpr),
301     construct<WaitSpec>("IOMSG =" >> msgVariable),
302     construct<WaitSpec>("IOSTAT =" >> statVariable)))
303 
304 constexpr auto bareUnitNumberAsList{
305     applyFunction(singletonList<PositionOrFlushSpec>,
306         construct<PositionOrFlushSpec>(fileUnitNumber))};
307 constexpr auto positionOrFlushSpecList{
308     parenthesized(nonemptyList(positionOrFlushSpec)) || bareUnitNumberAsList};
309 
310 // R1224 backspace-stmt ->
311 //         BACKSPACE file-unit-number | BACKSPACE ( position-spec-list )
312 TYPE_CONTEXT_PARSER("BACKSPACE statement"_en_US,
313     construct<BackspaceStmt>("BACKSPACE" >> positionOrFlushSpecList))
314 
315 // R1225 endfile-stmt ->
316 //         ENDFILE file-unit-number | ENDFILE ( position-spec-list )
317 TYPE_CONTEXT_PARSER("ENDFILE statement"_en_US,
318     construct<EndfileStmt>("END FILE" >> positionOrFlushSpecList))
319 
320 // R1226 rewind-stmt -> REWIND file-unit-number | REWIND ( position-spec-list )
321 TYPE_CONTEXT_PARSER("REWIND statement"_en_US,
322     construct<RewindStmt>("REWIND" >> positionOrFlushSpecList))
323 
324 // R1227 position-spec ->
325 //         [UNIT =] file-unit-number | IOMSG = iomsg-variable |
326 //         IOSTAT = scalar-int-variable | ERR = label
327 // R1229 flush-spec ->
328 //         [UNIT =] file-unit-number | IOSTAT = scalar-int-variable |
329 //         IOMSG = iomsg-variable | ERR = label
330 TYPE_PARSER(
331     construct<PositionOrFlushSpec>(maybe("UNIT ="_tok) >> fileUnitNumber) ||
332     construct<PositionOrFlushSpec>("IOMSG =" >> msgVariable) ||
333     construct<PositionOrFlushSpec>("IOSTAT =" >> statVariable) ||
334     construct<PositionOrFlushSpec>("ERR =" >> errLabel))
335 
336 // R1228 flush-stmt -> FLUSH file-unit-number | FLUSH ( flush-spec-list )
337 TYPE_CONTEXT_PARSER("FLUSH statement"_en_US,
338     construct<FlushStmt>("FLUSH" >> positionOrFlushSpecList))
339 
340 // R1231 inquire-spec ->
341 //         [UNIT =] file-unit-number | FILE = file-name-expr |
342 //         ACCESS = scalar-default-char-variable |
343 //         ACTION = scalar-default-char-variable |
344 //         ASYNCHRONOUS = scalar-default-char-variable |
345 //         BLANK = scalar-default-char-variable |
346 //         DECIMAL = scalar-default-char-variable |
347 //         DELIM = scalar-default-char-variable |
348 //         ENCODING = scalar-default-char-variable |
349 //         ERR = label | EXIST = scalar-logical-variable |
350 //         FORM = scalar-default-char-variable |
351 //         FORMATTED = scalar-default-char-variable |
352 //         ID = scalar-int-expr | IOMSG = iomsg-variable |
353 //         IOSTAT = scalar-int-variable |
354 //         NAME = scalar-default-char-variable |
355 //         NAMED = scalar-logical-variable |
356 //         NEXTREC = scalar-int-variable | NUMBER = scalar-int-variable |
357 //         OPENED = scalar-logical-variable |
358 //         PAD = scalar-default-char-variable |
359 //         PENDING = scalar-logical-variable | POS = scalar-int-variable |
360 //         POSITION = scalar-default-char-variable |
361 //         READ = scalar-default-char-variable |
362 //         READWRITE = scalar-default-char-variable |
363 //         RECL = scalar-int-variable | ROUND = scalar-default-char-variable |
364 //         SEQUENTIAL = scalar-default-char-variable |
365 //         SIGN = scalar-default-char-variable |
366 //         SIZE = scalar-int-variable |
367 //         STREAM = scalar-default-char-variable |
368 //         STATUS = scalar-default-char-variable |
369 //         WRITE = scalar-default-char-variable
370 //         @ | CARRIAGECONTROL = scalar-default-char-variable
371 //           | CONVERT = scalar-default-char-variable
372 //           | DISPOSE = scalar-default-char-variable
373 TYPE_PARSER(first(construct<InquireSpec>(maybe("UNIT ="_tok) >> fileUnitNumber),
374     construct<InquireSpec>("FILE =" >> fileNameExpr),
375     construct<InquireSpec>(
376         "ACCESS =" >> construct<InquireSpec::CharVar>(
377                           pure(InquireSpec::CharVar::Kind::Access),
378                           scalarDefaultCharVariable)),
379     construct<InquireSpec>(
380         "ACTION =" >> construct<InquireSpec::CharVar>(
381                           pure(InquireSpec::CharVar::Kind::Action),
382                           scalarDefaultCharVariable)),
383     construct<InquireSpec>(
384         "ASYNCHRONOUS =" >> construct<InquireSpec::CharVar>(
385                                 pure(InquireSpec::CharVar::Kind::Asynchronous),
386                                 scalarDefaultCharVariable)),
387     construct<InquireSpec>("BLANK =" >>
388         construct<InquireSpec::CharVar>(pure(InquireSpec::CharVar::Kind::Blank),
389             scalarDefaultCharVariable)),
390     construct<InquireSpec>(
391         "DECIMAL =" >> construct<InquireSpec::CharVar>(
392                            pure(InquireSpec::CharVar::Kind::Decimal),
393                            scalarDefaultCharVariable)),
394     construct<InquireSpec>("DELIM =" >>
395         construct<InquireSpec::CharVar>(pure(InquireSpec::CharVar::Kind::Delim),
396             scalarDefaultCharVariable)),
397     construct<InquireSpec>(
398         "DIRECT =" >> construct<InquireSpec::CharVar>(
399                           pure(InquireSpec::CharVar::Kind::Direct),
400                           scalarDefaultCharVariable)),
401     construct<InquireSpec>(
402         "ENCODING =" >> construct<InquireSpec::CharVar>(
403                             pure(InquireSpec::CharVar::Kind::Encoding),
404                             scalarDefaultCharVariable)),
405     construct<InquireSpec>("ERR =" >> errLabel),
406     construct<InquireSpec>("EXIST =" >>
407         construct<InquireSpec::LogVar>(
408             pure(InquireSpec::LogVar::Kind::Exist), scalarLogicalVariable)),
409     construct<InquireSpec>("FORM =" >>
410         construct<InquireSpec::CharVar>(
411             pure(InquireSpec::CharVar::Kind::Form), scalarDefaultCharVariable)),
412     construct<InquireSpec>(
413         "FORMATTED =" >> construct<InquireSpec::CharVar>(
414                              pure(InquireSpec::CharVar::Kind::Formatted),
415                              scalarDefaultCharVariable)),
416     construct<InquireSpec>("ID =" >> idExpr),
417     construct<InquireSpec>("IOMSG =" >>
418         construct<InquireSpec::CharVar>(pure(InquireSpec::CharVar::Kind::Iomsg),
419             scalarDefaultCharVariable)),
420     construct<InquireSpec>("IOSTAT =" >>
421         construct<InquireSpec::IntVar>(pure(InquireSpec::IntVar::Kind::Iostat),
422             scalar(integer(variable)))),
423     construct<InquireSpec>("NAME =" >>
424         construct<InquireSpec::CharVar>(
425             pure(InquireSpec::CharVar::Kind::Name), scalarDefaultCharVariable)),
426     construct<InquireSpec>("NAMED =" >>
427         construct<InquireSpec::LogVar>(
428             pure(InquireSpec::LogVar::Kind::Named), scalarLogicalVariable)),
429     construct<InquireSpec>("NEXTREC =" >>
430         construct<InquireSpec::IntVar>(pure(InquireSpec::IntVar::Kind::Nextrec),
431             scalar(integer(variable)))),
432     construct<InquireSpec>("NUMBER =" >>
433         construct<InquireSpec::IntVar>(pure(InquireSpec::IntVar::Kind::Number),
434             scalar(integer(variable)))),
435     construct<InquireSpec>("OPENED =" >>
436         construct<InquireSpec::LogVar>(
437             pure(InquireSpec::LogVar::Kind::Opened), scalarLogicalVariable)),
438     construct<InquireSpec>("PAD =" >>
439         construct<InquireSpec::CharVar>(
440             pure(InquireSpec::CharVar::Kind::Pad), scalarDefaultCharVariable)),
441     construct<InquireSpec>("PENDING =" >>
442         construct<InquireSpec::LogVar>(
443             pure(InquireSpec::LogVar::Kind::Pending), scalarLogicalVariable)),
444     construct<InquireSpec>("POS =" >>
445         construct<InquireSpec::IntVar>(
446             pure(InquireSpec::IntVar::Kind::Pos), scalar(integer(variable)))),
447     construct<InquireSpec>(
448         "POSITION =" >> construct<InquireSpec::CharVar>(
449                             pure(InquireSpec::CharVar::Kind::Position),
450                             scalarDefaultCharVariable)),
451     construct<InquireSpec>("READ =" >>
452         construct<InquireSpec::CharVar>(
453             pure(InquireSpec::CharVar::Kind::Read), scalarDefaultCharVariable)),
454     construct<InquireSpec>(
455         "READWRITE =" >> construct<InquireSpec::CharVar>(
456                              pure(InquireSpec::CharVar::Kind::Readwrite),
457                              scalarDefaultCharVariable)),
458     construct<InquireSpec>("RECL =" >>
459         construct<InquireSpec::IntVar>(
460             pure(InquireSpec::IntVar::Kind::Recl), scalar(integer(variable)))),
461     construct<InquireSpec>("ROUND =" >>
462         construct<InquireSpec::CharVar>(pure(InquireSpec::CharVar::Kind::Round),
463             scalarDefaultCharVariable)),
464     construct<InquireSpec>(
465         "SEQUENTIAL =" >> construct<InquireSpec::CharVar>(
466                               pure(InquireSpec::CharVar::Kind::Sequential),
467                               scalarDefaultCharVariable)),
468     construct<InquireSpec>("SIGN =" >>
469         construct<InquireSpec::CharVar>(
470             pure(InquireSpec::CharVar::Kind::Sign), scalarDefaultCharVariable)),
471     construct<InquireSpec>("SIZE =" >>
472         construct<InquireSpec::IntVar>(
473             pure(InquireSpec::IntVar::Kind::Size), scalar(integer(variable)))),
474     construct<InquireSpec>(
475         "STREAM =" >> construct<InquireSpec::CharVar>(
476                           pure(InquireSpec::CharVar::Kind::Stream),
477                           scalarDefaultCharVariable)),
478     construct<InquireSpec>(
479         "STATUS =" >> construct<InquireSpec::CharVar>(
480                           pure(InquireSpec::CharVar::Kind::Status),
481                           scalarDefaultCharVariable)),
482     construct<InquireSpec>(
483         "UNFORMATTED =" >> construct<InquireSpec::CharVar>(
484                                pure(InquireSpec::CharVar::Kind::Unformatted),
485                                scalarDefaultCharVariable)),
486     construct<InquireSpec>("WRITE =" >>
487         construct<InquireSpec::CharVar>(pure(InquireSpec::CharVar::Kind::Write),
488             scalarDefaultCharVariable)),
489     extension<LanguageFeature::Carriagecontrol>(
490         "nonstandard usage: CARRIAGECONTROL="_port_en_US,
491         construct<InquireSpec>("CARRIAGECONTROL =" >>
492             construct<InquireSpec::CharVar>(
493                 pure(InquireSpec::CharVar::Kind::Carriagecontrol),
494                 scalarDefaultCharVariable))),
495     extension<LanguageFeature::Convert>(
496         "nonstandard usage: CONVERT="_port_en_US,
497         construct<InquireSpec>(
498             "CONVERT =" >> construct<InquireSpec::CharVar>(
499                                pure(InquireSpec::CharVar::Kind::Convert),
500                                scalarDefaultCharVariable))),
501     extension<LanguageFeature::Dispose>(
502         "nonstandard usage: DISPOSE="_port_en_US,
503         construct<InquireSpec>(
504             "DISPOSE =" >> construct<InquireSpec::CharVar>(
505                                pure(InquireSpec::CharVar::Kind::Dispose),
506                                scalarDefaultCharVariable)))))
507 
508 // R1230 inquire-stmt ->
509 //         INQUIRE ( inquire-spec-list ) |
510 //         INQUIRE ( IOLENGTH = scalar-int-variable ) output-item-list
511 TYPE_CONTEXT_PARSER("INQUIRE statement"_en_US,
512     "INQUIRE" >>
513         (construct<InquireStmt>(
514              parenthesized(nonemptyList(Parser<InquireSpec>{}))) ||
515             construct<InquireStmt>(construct<InquireStmt::Iolength>(
516                 parenthesized("IOLENGTH =" >> scalar(integer(variable))),
517                 nonemptyList(outputItem)))))
518 
519 // R1301 format-stmt -> FORMAT format-specification
520 // 13.2.1 allows spaces to appear "at any point" within a format specification
521 // without effect, except of course within a character string edit descriptor.
522 TYPE_CONTEXT_PARSER("FORMAT statement"_en_US,
523     construct<FormatStmt>("FORMAT" >> Parser<format::FormatSpecification>{}))
524 
525 // R1321 char-string-edit-desc
526 // N.B. C1313 disallows any kind parameter on the character literal.
527 constexpr auto charStringEditDesc{
528     space >> (charLiteralConstantWithoutKind || rawHollerithLiteral)};
529 
530 // R1303 format-items -> format-item [[,] format-item]...
531 constexpr auto formatItems{
532     nonemptySeparated(space >> Parser<format::FormatItem>{}, maybe(","_tok))};
533 
534 // R1306 r -> digit-string
535 constexpr DigitStringIgnoreSpaces repeat;
536 
537 // R1304 format-item ->
538 //         [r] data-edit-desc | control-edit-desc | char-string-edit-desc |
539 //         [r] ( format-items )
540 TYPE_PARSER(construct<format::FormatItem>(
541                 maybe(repeat), Parser<format::IntrinsicTypeDataEditDesc>{}) ||
542     construct<format::FormatItem>(
543         maybe(repeat), Parser<format::DerivedTypeDataEditDesc>{}) ||
544     construct<format::FormatItem>(Parser<format::ControlEditDesc>{}) ||
545     construct<format::FormatItem>(charStringEditDesc) ||
546     construct<format::FormatItem>(maybe(repeat), parenthesized(formatItems)))
547 
548 // R1302 format-specification ->
549 //         ( [format-items] ) | ( [format-items ,] unlimited-format-item )
550 // R1305 unlimited-format-item -> * ( format-items )
551 // minor extension: the comma is optional before the unlimited-format-item
552 TYPE_PARSER(parenthesized(construct<format::FormatSpecification>(
553                               defaulted(formatItems / maybe(","_tok)),
554                               "*" >> parenthesized(formatItems)) ||
555     construct<format::FormatSpecification>(defaulted(formatItems))))
556 // R1308 w -> digit-string
557 // R1309 m -> digit-string
558 // R1310 d -> digit-string
559 // R1311 e -> digit-string
560 constexpr auto width{repeat};
561 constexpr auto mandatoryWidth{construct<std::optional<int>>(width)};
562 constexpr auto digits{repeat};
563 constexpr auto noInt{construct<std::optional<int>>()};
564 constexpr auto mandatoryDigits{construct<std::optional<int>>("." >> width)};
565 
566 // The extra trailing spaces in the following quoted edit descriptor token
567 // parsers are intentional: they inhibit any spurious warnings about missing
568 // spaces in pedantic mode that would otherwise be emitted if the edit
569 // descriptor were followed by a character that could appear in an identifier.
570 
571 // R1307 data-edit-desc ->
572 //         I w [. m] | B w [. m] | O w [. m] | Z w [. m] | F w . d |
573 //         E w . d [E e] | EN w . d [E e] | ES w . d [E e] | EX w . d [E e] |
574 //         G w [. d [E e]] | L w | A [w] | D w . d |
575 //         DT [char-literal-constant] [( v-list )]
576 // (part 1 of 2)
577 TYPE_PARSER(construct<format::IntrinsicTypeDataEditDesc>(
578                 "I " >> pure(format::IntrinsicTypeDataEditDesc::Kind::I) ||
579                     "B " >> pure(format::IntrinsicTypeDataEditDesc::Kind::B) ||
580                     "O " >> pure(format::IntrinsicTypeDataEditDesc::Kind::O) ||
581                     "Z " >> pure(format::IntrinsicTypeDataEditDesc::Kind::Z),
582                 mandatoryWidth, maybe("." >> digits), noInt) ||
583     construct<format::IntrinsicTypeDataEditDesc>(
584         "F " >> pure(format::IntrinsicTypeDataEditDesc::Kind::F) ||
585             "D " >> pure(format::IntrinsicTypeDataEditDesc::Kind::D),
586         mandatoryWidth, mandatoryDigits, noInt) ||
587     construct<format::IntrinsicTypeDataEditDesc>(
588         "E " >> ("N " >> pure(format::IntrinsicTypeDataEditDesc::Kind::EN) ||
589                     "S " >> pure(format::IntrinsicTypeDataEditDesc::Kind::ES) ||
590                     "X " >> pure(format::IntrinsicTypeDataEditDesc::Kind::EX) ||
591                     pure(format::IntrinsicTypeDataEditDesc::Kind::E)),
592         mandatoryWidth, mandatoryDigits, maybe("E " >> digits)) ||
593     construct<format::IntrinsicTypeDataEditDesc>(
594         "G " >> pure(format::IntrinsicTypeDataEditDesc::Kind::G),
595         mandatoryWidth, mandatoryDigits, maybe("E " >> digits)) ||
596     construct<format::IntrinsicTypeDataEditDesc>(
597         "G " >> pure(format::IntrinsicTypeDataEditDesc::Kind::G) ||
598             "L " >> pure(format::IntrinsicTypeDataEditDesc::Kind::L),
599         mandatoryWidth, noInt, noInt) ||
600     construct<format::IntrinsicTypeDataEditDesc>(
601         "A " >> pure(format::IntrinsicTypeDataEditDesc::Kind::A), maybe(width),
602         noInt, noInt) ||
603     // PGI/Intel extension: omitting width (and all else that follows)
604     extension<LanguageFeature::AbbreviatedEditDescriptor>(
605         "nonstandard usage: abbreviated edit descriptor"_port_en_US,
606         construct<format::IntrinsicTypeDataEditDesc>(
607             "I " >> pure(format::IntrinsicTypeDataEditDesc::Kind::I) ||
608                 ("B "_tok / !letter /* don't occlude BN & BZ */) >>
609                     pure(format::IntrinsicTypeDataEditDesc::Kind::B) ||
610                 "O " >> pure(format::IntrinsicTypeDataEditDesc::Kind::O) ||
611                 "Z " >> pure(format::IntrinsicTypeDataEditDesc::Kind::Z) ||
612                 "F " >> pure(format::IntrinsicTypeDataEditDesc::Kind::F) ||
613                 ("D "_tok / !letter /* don't occlude DT, DC, & DP */) >>
614                     pure(format::IntrinsicTypeDataEditDesc::Kind::D) ||
615                 "E " >>
616                     ("N " >>
617                             pure(format::IntrinsicTypeDataEditDesc::Kind::EN) ||
618                         "S " >>
619                             pure(format::IntrinsicTypeDataEditDesc::Kind::ES) ||
620                         "X " >>
621                             pure(format::IntrinsicTypeDataEditDesc::Kind::EX) ||
622                         pure(format::IntrinsicTypeDataEditDesc::Kind::E)) ||
623                 "G " >> pure(format::IntrinsicTypeDataEditDesc::Kind::G) ||
624                 "L " >> pure(format::IntrinsicTypeDataEditDesc::Kind::L),
625             noInt, noInt, noInt)))
626 
627 // R1307 data-edit-desc (part 2 of 2)
628 // R1312 v -> [sign] digit-string
629 constexpr SignedDigitStringIgnoreSpaces scaleFactor;
630 TYPE_PARSER(construct<format::DerivedTypeDataEditDesc>(
631     "D T" >> defaulted(charLiteralConstantWithoutKind),
632     defaulted(parenthesized(nonemptyList(scaleFactor)))))
633 
634 // R1314 k -> [sign] digit-string
635 constexpr PositiveDigitStringIgnoreSpaces count;
636 
637 // R1313 control-edit-desc ->
638 //         position-edit-desc | [r] / | : | sign-edit-desc | k P |
639 //         blank-interp-edit-desc | round-edit-desc | decimal-edit-desc |
640 //         @ \ | $
641 // R1315 position-edit-desc -> T n | TL n | TR n | n X
642 // R1316 n -> digit-string
643 // R1317 sign-edit-desc -> SS | SP | S
644 // R1318 blank-interp-edit-desc -> BN | BZ
645 // R1319 round-edit-desc -> RU | RD | RZ | RN | RC | RP
646 // R1320 decimal-edit-desc -> DC | DP
647 TYPE_PARSER(construct<format::ControlEditDesc>(
648                 "T L " >> pure(format::ControlEditDesc::Kind::TL) ||
649                     "T R " >> pure(format::ControlEditDesc::Kind::TR) ||
650                     "T " >> pure(format::ControlEditDesc::Kind::T),
651                 count) ||
652     construct<format::ControlEditDesc>(count,
653         "X " >> pure(format::ControlEditDesc::Kind::X) ||
654             "/" >> pure(format::ControlEditDesc::Kind::Slash)) ||
655     construct<format::ControlEditDesc>(
656         "X " >> pure(format::ControlEditDesc::Kind::X) ||
657         "/" >> pure(format::ControlEditDesc::Kind::Slash)) ||
658     construct<format::ControlEditDesc>(
659         scaleFactor, "P " >> pure(format::ControlEditDesc::Kind::P)) ||
660     construct<format::ControlEditDesc>(
661         ":" >> pure(format::ControlEditDesc::Kind::Colon)) ||
662     "S " >> ("S " >> construct<format::ControlEditDesc>(
663                          pure(format::ControlEditDesc::Kind::SS)) ||
664                 "P " >> construct<format::ControlEditDesc>(
665                             pure(format::ControlEditDesc::Kind::SP)) ||
666                 construct<format::ControlEditDesc>(
667                     pure(format::ControlEditDesc::Kind::S))) ||
668     "B " >> ("N " >> construct<format::ControlEditDesc>(
669                          pure(format::ControlEditDesc::Kind::BN)) ||
670                 "Z " >> construct<format::ControlEditDesc>(
671                             pure(format::ControlEditDesc::Kind::BZ))) ||
672     "R " >> ("U " >> construct<format::ControlEditDesc>(
673                          pure(format::ControlEditDesc::Kind::RU)) ||
674                 "D " >> construct<format::ControlEditDesc>(
675                             pure(format::ControlEditDesc::Kind::RD)) ||
676                 "Z " >> construct<format::ControlEditDesc>(
677                             pure(format::ControlEditDesc::Kind::RZ)) ||
678                 "N " >> construct<format::ControlEditDesc>(
679                             pure(format::ControlEditDesc::Kind::RN)) ||
680                 "C " >> construct<format::ControlEditDesc>(
681                             pure(format::ControlEditDesc::Kind::RC)) ||
682                 "P " >> construct<format::ControlEditDesc>(
683                             pure(format::ControlEditDesc::Kind::RP))) ||
684     "D " >> ("C " >> construct<format::ControlEditDesc>(
685                          pure(format::ControlEditDesc::Kind::DC)) ||
686                 "P " >> construct<format::ControlEditDesc>(
687                             pure(format::ControlEditDesc::Kind::DP))) ||
688     extension<LanguageFeature::AdditionalFormats>(
689         "nonstandard usage: $ and \\ control edit descriptors"_port_en_US,
690         "$" >> construct<format::ControlEditDesc>(
691                    pure(format::ControlEditDesc::Kind::Dollar)) ||
692             "\\" >> construct<format::ControlEditDesc>(
693                         pure(format::ControlEditDesc::Kind::Backslash))))
694 } // namespace Fortran::parser
695