001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 the ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package eu.cqse.check.framework.shallowparser.languages.fortran;
018
019import static eu.cqse.check.framework.scanner.ETokenType.ALLOCATE;
020import static eu.cqse.check.framework.scanner.ETokenType.ASYNCHRONOUS;
021import static eu.cqse.check.framework.scanner.ETokenType.BLOCK;
022import static eu.cqse.check.framework.scanner.ETokenType.CALL;
023import static eu.cqse.check.framework.scanner.ETokenType.CASE;
024import static eu.cqse.check.framework.scanner.ETokenType.CLASS;
025import static eu.cqse.check.framework.scanner.ETokenType.CLOSE;
026import static eu.cqse.check.framework.scanner.ETokenType.COLON;
027import static eu.cqse.check.framework.scanner.ETokenType.COMMA;
028import static eu.cqse.check.framework.scanner.ETokenType.COMMON;
029import static eu.cqse.check.framework.scanner.ETokenType.CONCURRENT;
030import static eu.cqse.check.framework.scanner.ETokenType.CONTAINS;
031import static eu.cqse.check.framework.scanner.ETokenType.CONTINUE;
032import static eu.cqse.check.framework.scanner.ETokenType.CRITICAL;
033import static eu.cqse.check.framework.scanner.ETokenType.CYCLE;
034import static eu.cqse.check.framework.scanner.ETokenType.DATA;
035import static eu.cqse.check.framework.scanner.ETokenType.DEALLOCATE;
036import static eu.cqse.check.framework.scanner.ETokenType.DEFAULT;
037import static eu.cqse.check.framework.scanner.ETokenType.DO;
038import static eu.cqse.check.framework.scanner.ETokenType.DOUBLE_COLON;
039import static eu.cqse.check.framework.scanner.ETokenType.ELEMENTAL;
040import static eu.cqse.check.framework.scanner.ETokenType.ELSE;
041import static eu.cqse.check.framework.scanner.ETokenType.ELSEIF;
042import static eu.cqse.check.framework.scanner.ETokenType.ELSEWHERE;
043import static eu.cqse.check.framework.scanner.ETokenType.END;
044import static eu.cqse.check.framework.scanner.ETokenType.ENDFILE;
045import static eu.cqse.check.framework.scanner.ETokenType.ENTRY;
046import static eu.cqse.check.framework.scanner.ETokenType.ENUM;
047import static eu.cqse.check.framework.scanner.ETokenType.ENUMERATOR;
048import static eu.cqse.check.framework.scanner.ETokenType.EOL;
049import static eu.cqse.check.framework.scanner.ETokenType.EQUIVALENCE;
050import static eu.cqse.check.framework.scanner.ETokenType.EXIT;
051import static eu.cqse.check.framework.scanner.ETokenType.EXTERNAL;
052import static eu.cqse.check.framework.scanner.ETokenType.FORALL;
053import static eu.cqse.check.framework.scanner.ETokenType.FUNCTION;
054import static eu.cqse.check.framework.scanner.ETokenType.GOTO;
055import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER;
056import static eu.cqse.check.framework.scanner.ETokenType.IF;
057import static eu.cqse.check.framework.scanner.ETokenType.IMPLICIT;
058import static eu.cqse.check.framework.scanner.ETokenType.INQUIRE;
059import static eu.cqse.check.framework.scanner.ETokenType.INTEGER_LITERAL;
060import static eu.cqse.check.framework.scanner.ETokenType.INTERFACE;
061import static eu.cqse.check.framework.scanner.ETokenType.IS;
062import static eu.cqse.check.framework.scanner.ETokenType.LOCK;
063import static eu.cqse.check.framework.scanner.ETokenType.LPAREN;
064import static eu.cqse.check.framework.scanner.ETokenType.MODULE;
065import static eu.cqse.check.framework.scanner.ETokenType.NAMELIST;
066import static eu.cqse.check.framework.scanner.ETokenType.NULLIFY;
067import static eu.cqse.check.framework.scanner.ETokenType.OPEN;
068import static eu.cqse.check.framework.scanner.ETokenType.PARAMETER;
069import static eu.cqse.check.framework.scanner.ETokenType.PAUSE;
070import static eu.cqse.check.framework.scanner.ETokenType.PRINT;
071import static eu.cqse.check.framework.scanner.ETokenType.PRIVATE;
072import static eu.cqse.check.framework.scanner.ETokenType.PROCEDURE;
073import static eu.cqse.check.framework.scanner.ETokenType.PROGRAM;
074import static eu.cqse.check.framework.scanner.ETokenType.PUBLIC;
075import static eu.cqse.check.framework.scanner.ETokenType.PURE;
076import static eu.cqse.check.framework.scanner.ETokenType.RECURSIVE;
077import static eu.cqse.check.framework.scanner.ETokenType.RETURN;
078import static eu.cqse.check.framework.scanner.ETokenType.REWIND;
079import static eu.cqse.check.framework.scanner.ETokenType.REWRITE;
080import static eu.cqse.check.framework.scanner.ETokenType.RPAREN;
081import static eu.cqse.check.framework.scanner.ETokenType.SELECT;
082import static eu.cqse.check.framework.scanner.ETokenType.SEMICOLON;
083import static eu.cqse.check.framework.scanner.ETokenType.SEQUENCE;
084import static eu.cqse.check.framework.scanner.ETokenType.STOP;
085import static eu.cqse.check.framework.scanner.ETokenType.SUBMODULE;
086import static eu.cqse.check.framework.scanner.ETokenType.SUBROUTINE;
087import static eu.cqse.check.framework.scanner.ETokenType.THEN;
088import static eu.cqse.check.framework.scanner.ETokenType.TYPE;
089import static eu.cqse.check.framework.scanner.ETokenType.UNLOCK;
090import static eu.cqse.check.framework.scanner.ETokenType.USE;
091import static eu.cqse.check.framework.scanner.ETokenType.VOLATILE;
092import static eu.cqse.check.framework.scanner.ETokenType.WAIT;
093import static eu.cqse.check.framework.scanner.ETokenType.WHERE;
094import static eu.cqse.check.framework.scanner.ETokenType.WHILE;
095import static eu.cqse.check.framework.scanner.ETokenType.WRITE;
096import static eu.cqse.check.framework.shallowparser.languages.fortran.FortranShallowParser.EFortranParserStates.IN_ENUM;
097import static eu.cqse.check.framework.shallowparser.languages.fortran.FortranShallowParser.EFortranParserStates.IN_METHOD;
098import static eu.cqse.check.framework.shallowparser.languages.fortran.FortranShallowParser.EFortranParserStates.IN_MODULE;
099import static eu.cqse.check.framework.shallowparser.languages.fortran.FortranShallowParser.EFortranParserStates.IN_SELECT;
100import static eu.cqse.check.framework.shallowparser.languages.fortran.FortranShallowParser.EFortranParserStates.IN_TYPE;
101import static eu.cqse.check.framework.shallowparser.languages.fortran.FortranShallowParser.EFortranParserStates.TOP_LEVEL;
102
103import java.util.EnumSet;
104
105import org.conqat.lib.commons.region.Region;
106
107import eu.cqse.check.framework.scanner.ETokenType;
108import eu.cqse.check.framework.shallowparser.SubTypeNames;
109import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType;
110import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;
111import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase;
112import eu.cqse.check.framework.shallowparser.languages.fortran.FortranShallowParser.EFortranParserStates;
113
114/**
115 * A shallow parser for Fortran.
116 * 
117 * This parser supports the free form of Fortran 2008.
118 */
119public class FortranShallowParser extends ShallowParserBase<EFortranParserStates> {
120
121        /** All possible states of a FortranShallowParser. */
122        public static enum EFortranParserStates {
123                /** Top-level state. */
124                TOP_LEVEL,
125
126                /** Inside a module. */
127                IN_MODULE,
128
129                /** Inside a type declaration. */
130                IN_TYPE,
131
132                /** Inside a enum declaration. */
133                IN_ENUM,
134
135                /** Inside a method's body. */
136                IN_METHOD,
137
138                /** Inside a select statement. */
139                IN_SELECT,
140        }
141
142        /** All tokens that indicate a simple statement. */
143        private static final EnumSet<ETokenType> STATEMENT_TOKENS = EnumSet.of(ALLOCATE, ASYNCHRONOUS, CALL, CLOSE, COMMON,
144                        CONTINUE, CYCLE, DATA, DEALLOCATE, ENDFILE, ENTRY, EQUIVALENCE, EXIT, EXTERNAL, GOTO, IDENTIFIER,
145                        INTEGER_LITERAL, INQUIRE, LOCK, NAMELIST, NULLIFY, OPEN, PAUSE, PARAMETER, PRINT, RETURN, REWIND, REWRITE,
146                        STOP, UNLOCK, WAIT, WRITE, VOLATILE);
147
148        /** All parser states in which statements can occur. */
149        private static final EFortranParserStates[] STATEMENT_STATES = { IN_MODULE, IN_METHOD };
150
151        /** All modifiers that can be used in front of a function declaration. */
152        private static final EnumSet<ETokenType> FUNCTION_MODIFIERS = EnumSet.of(MODULE, ELEMENTAL, PURE, RECURSIVE);
153
154        /** All modifiers that can be used in front of a procedure declaration. */
155        private static final EnumSet<ETokenType> PROCEDURE_MODIFIERS = EnumSet.of(MODULE);
156
157        /** All token types that can be used as a type. */
158        private static final EnumSet<ETokenType> TYPE_TOKENS = EnumSet.of(IDENTIFIER, TYPE, CLASS);
159
160        /** Constructor. */
161        public FortranShallowParser() {
162                super(EFortranParserStates.class, TOP_LEVEL);
163                createTopLevelRules();
164                createInModuleRules();
165                createInTypeRules();
166                createInEnumRule();
167                createMethodRules();
168                createStatementRules();
169                createAnyStateRules();
170        }
171
172        /**
173         * Create rules for parsing top-level constructs like modules or program.
174         */
175        private void createTopLevelRules() {
176                // module
177                inState(TOP_LEVEL).sequence(MODULE, IDENTIFIER).createNode(EShallowEntityType.MODULE, 0, 1).sequence(EOL)
178                                .parseUntil(IN_MODULE).sequence(END).optional(MODULE).optional(IDENTIFIER).endNode();
179
180                // program
181                inState(TOP_LEVEL).sequence(PROGRAM, IDENTIFIER).createNode(EShallowEntityType.METHOD, 0, -1).sequence(EOL)
182                                .parseUntil(IN_MODULE).sequence(END).optional(PROGRAM).optional(IDENTIFIER).endNode();
183
184                // submodule
185                inState(TOP_LEVEL).sequence(SUBMODULE).skipToWithNesting(IDENTIFIER, LPAREN, RPAREN)
186                                .createNode(EShallowEntityType.MODULE, 0, -1).sequence(EOL).parseUntil(IN_MODULE).sequence(END)
187                                .optional(SUBMODULE).optional(IDENTIFIER).endNode();
188        }
189
190        /** Create rules for parsing some meta information. */
191        private void createAnyStateRules() {
192                inAnyState().sequence(IMPLICIT).createNode(EShallowEntityType.META, 0).skipTo(EOL).endNode();
193
194                inAnyState().sequence(CONTAINS).createNode(EShallowEntityType.META, 0).endNode();
195
196                inState(IN_MODULE, IN_METHOD).sequence(USE).createNode(EShallowEntityType.META, 0).skipTo(EOL).endNode();
197        }
198
199        /** Create rules for parsing the body of a module. */
200        private void createInModuleRules() {
201                // visibility
202                inState(IN_MODULE).sequence(EnumSet.of(PUBLIC, PRIVATE)).createNode(EShallowEntityType.META, 0).skipTo(EOL)
203                                .endNode();
204
205                // interface with name
206                inState(IN_MODULE).sequence(INTERFACE, IDENTIFIER).createNode(EShallowEntityType.TYPE, 0, 1)
207                                .parseUntil(IN_MODULE).sequence(END).optional(INTERFACE).sequence(IDENTIFIER).endNode();
208
209                // interface with no name
210                inState(IN_MODULE).sequence(INTERFACE).createNode(EShallowEntityType.TYPE, 0).parseUntil(IN_MODULE)
211                                .sequence(END).optional(INTERFACE).endNode();
212
213                // type declaration
214                RecognizerBase<EFortranParserStates> typeAlternative = inState(IN_MODULE).sequence(TYPE);
215                typeAlternative.optional(DOUBLE_COLON).sequence(IDENTIFIER, EOL).createNode(EShallowEntityType.TYPE, 0, -2)
216                                .parseUntil(IN_TYPE).sequence(END).optional(TYPE).optional(IDENTIFIER).endNode();
217                typeAlternative.sequence(COMMA).skipBefore(EnumSet.of(DOUBLE_COLON, EOL))
218                                .sequence(DOUBLE_COLON, IDENTIFIER, EOL).createNode(EShallowEntityType.TYPE, 0, -2).parseUntil(IN_TYPE)
219                                .sequence(END).optional(TYPE).optional(IDENTIFIER).endNode();
220
221                // enum declaration
222                inState(IN_MODULE).sequence(ENUM).skipBefore(EnumSet.of(DOUBLE_COLON, EOL))
223                                .sequence(DOUBLE_COLON, IDENTIFIER, EOL).createNode(EShallowEntityType.TYPE, 0, -2).parseUntil(IN_ENUM)
224                                .sequence(END).optional(ENUM).optional(IDENTIFIER).endNode();
225        }
226
227        /** Create rules for parsing type bodies. */
228        private void createInTypeRules() {
229                // sequence
230                inState(IN_TYPE).sequence(SEQUENCE, EOL).createNode(EShallowEntityType.META, 0).endNode();
231
232                // attribute
233                inState(IN_TYPE).sequence(TYPE_TOKENS).skipBefore(EnumSet.of(DOUBLE_COLON, EOL))
234                                .sequence(DOUBLE_COLON, IDENTIFIER).createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.ATTRIBUTE, -1)
235                                .skipTo(EOL).endNode();
236
237                // procedure
238                inState(IN_TYPE).sequence(PROCEDURE, DOUBLE_COLON, IDENTIFIER).skipTo(EOL)
239                                .createNode(EShallowEntityType.METHOD, 0, 2).endNode();
240        }
241
242        /** Create rule for parsing an enum body. */
243        private void createInEnumRule() {
244                inState(IN_ENUM).sequence(ENUMERATOR).optional(DOUBLE_COLON).sequence(IDENTIFIER)
245                                .createNode(EShallowEntityType.ATTRIBUTE, 0, -1).skipTo(EOL).endNode();
246        }
247
248        /** Create rules for parsing method declarations. */
249        private void createMethodRules() {
250                // subroutine
251                inState(TOP_LEVEL, IN_MODULE, IN_METHOD).sequence(SUBROUTINE, IDENTIFIER)
252                                .createNode(EShallowEntityType.METHOD, 0, 1).skipTo(EOL).parseUntil(IN_METHOD).sequence(END)
253                                .optional(SUBROUTINE).optional(IDENTIFIER).endNode();
254
255                // function
256                inState(TOP_LEVEL, IN_MODULE, IN_METHOD).optional(FUNCTION_MODIFIERS, TYPE_TOKENS)
257                                .skipBefore(EnumSet.of(FUNCTION, EOL)).sequence(FUNCTION, IDENTIFIER)
258                                .createNode(EShallowEntityType.METHOD, -2, -1).skipTo(EOL).parseUntil(IN_METHOD).sequence(END)
259                                .optional(FUNCTION).optional(IDENTIFIER).endNode();
260
261                // variable declaration
262                inState(IN_MODULE, IN_METHOD).sequence(TYPE_TOKENS).skipBefore(EnumSet.of(DOUBLE_COLON, EOL))
263                                .sequence(DOUBLE_COLON, IDENTIFIER)
264                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.LOCAL_VARIABLE, -1).skipTo(EOL).endNode();
265
266                // procedure
267                inState(IN_MODULE, IN_TYPE).repeated(PROCEDURE_MODIFIERS).sequence(PROCEDURE, IDENTIFIER)
268                                .createNode(EShallowEntityType.METHOD, -2, -1).skipTo(EOL).endNode();
269        }
270
271        /**
272         * Create rules for parsing statements. All constructs are taken from
273         * http://de.wikibooks.org/wiki/Fortran:_Fortran_95:
274         * _Verzweigungen_und_Schleifen.
275         */
276        private void createStatementRules() {
277                createArithmeticIfRule();
278
279                createSingleLineConstructs(IF, WHERE, FORALL);
280
281                createLoopRules();
282                createSelectRules();
283
284                createElseIfRule();
285                createContinuedConstructRules(IF, ELSEIF, ELSE, THEN);
286                createContinuedConstructRules(WHERE, ELSEWHERE, ELSEWHERE, null);
287
288                createConcurrentRules();
289
290                createSimpleStatementRules();
291        }
292
293        /**
294         * Create rule for parsing the arithmetic if construct (e.g. 'if
295         * (&lt;arithmetic expression&gt;) 1, 2, 3')
296         */
297        private void createArithmeticIfRule() {
298                inState(STATEMENT_STATES).sequence(IF).skipNested(LPAREN, RPAREN)
299                                .sequence(INTEGER_LITERAL, COMMA, INTEGER_LITERAL, COMMA, INTEGER_LITERAL, EOL)
300                                .createNode(EShallowEntityType.STATEMENT, 0).endNode();
301        }
302
303        /**
304         * Create rules for parsing constructs with a condition in parenthesis and
305         * one statement in the same line for all given token types. A single line
306         * construct is e.g. 'if (&lt;condition&gt;) &lt;statement&gt;'
307         */
308        private void createSingleLineConstructs(ETokenType... constructTokens) {
309                for (ETokenType constructToken : constructTokens) {
310                        inState(STATEMENT_STATES).sequence(constructToken, LPAREN).skipToWithNesting(RPAREN, LPAREN, RPAREN)
311                                        .sequenceBefore(STATEMENT_TOKENS).createNode(EShallowEntityType.STATEMENT, 0).parseOnce(IN_METHOD)
312                                        .sequence(EOL).endNode();
313                }
314        }
315
316        /** Create rules for parsing block and critical sections. */
317        private void createConcurrentRules() {
318                inState(STATEMENT_STATES).sequence(BLOCK).createNode(EShallowEntityType.STATEMENT, 0).skipTo(EOL)
319                                .parseUntil(IN_METHOD).sequence(END).optional(BLOCK).endNode();
320
321                inState(STATEMENT_STATES).sequence(CRITICAL).createNode(EShallowEntityType.STATEMENT, 0).skipTo(EOL)
322                                .parseUntil(IN_METHOD).sequence(END).optional(CRITICAL).endNode();
323        }
324
325        /** Create rules for parsing a select statement. */
326        private void createSelectRules() {
327                finishConstructWithOptionalName(inState(STATEMENT_STATES).optional(IDENTIFIER, COLON).markStart()
328                                .sequence(SELECT).optional(EnumSet.of(CASE, TYPE)).sequence(LPAREN)
329                                .skipToWithNesting(RPAREN, LPAREN, RPAREN).sequence(EOL)
330                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.SELECT).parseUntil(IN_SELECT), SELECT);
331
332                RecognizerBase<EFortranParserStates> labelAlternative = inState(IN_SELECT)
333                                .sequence(EnumSet.of(CASE, TYPE, CLASS)).optional(IS).skipNested(LPAREN, RPAREN).optional(IDENTIFIER)
334                                .sequence(EOL).createNode(EShallowEntityType.META, 0).parseUntil(IN_METHOD);
335                labelAlternative.sequenceBefore(END).endNode();
336                labelAlternative.sequenceBefore(EnumSet.of(CASE, TYPE, CLASS)).endNodeWithContinuation();
337
338                inState(IN_SELECT).sequence(EnumSet.of(CASE, CLASS), DEFAULT).optional(IDENTIFIER).sequence(EOL)
339                                .createNode(EShallowEntityType.META, 0).parseUntil(IN_METHOD).sequenceBefore(END).endNode();
340        }
341
342        /** Create rules for parsing do and forall loops. */
343        private void createLoopRules() {
344                // do/do concurrent/do while loop
345                finishConstructWithOptionalName(
346                                inState(STATEMENT_STATES).optional(IDENTIFIER, COLON).markStart().sequence(DO)
347                                                .optional(EnumSet.of(CONCURRENT, WHILE))
348                                                .createNode(EShallowEntityType.STATEMENT, new Region(0, -1)).skipTo(EOL).parseUntil(IN_METHOD),
349                                DO);
350
351                // forall loop
352                finishConstructWithOptionalName(inState(STATEMENT_STATES).optional(IDENTIFIER, COLON).markStart()
353                                .sequence(FORALL, LPAREN).skipToWithNesting(RPAREN, LPAREN, RPAREN)
354                                .createNode(EShallowEntityType.STATEMENT, 0).sequence(EOL).parseUntil(IN_METHOD), FORALL);
355        }
356
357        /**
358         * Create a special rule for handling 'else if' that consists of two
359         * separate tokens.
360         */
361        private void createElseIfRule() {
362                RecognizerBase<EFortranParserStates> recognizer = inState(STATEMENT_STATES).sequence(ELSE, IF, LPAREN)
363                                .skipToWithNesting(RPAREN, LPAREN, RPAREN).sequence(THEN).optional(IDENTIFIER).sequence(EOL)
364                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.ELSE_IF_NOSPACE).parseUntil(IN_METHOD);
365                finishConstructWithOptionalName(recognizer, IF);
366                recognizer.sequenceBefore(EnumSet.of(ELSE, ELSEIF)).endNodeWithContinuation();
367        }
368
369        /**
370         * Create rules for parsing constructs than can be continued. The given
371         * begin token is the token that begins the construct for e.g. 'if'. The
372         * continuation token can continue a construct, e.g. 'else if'. The final
373         * token is the last unconditional continuation token, e.g. 'else'. The
374         * complete construct is ended with 'end &lt;beginToken&gt;'. The given
375         * suffix token must be recognized after the parenthesis of the construct
376         * e.g. 'then' in an 'if' construct. The suffix token can be null, if there
377         * is none. The construct can be prefixed by a construct name, e.g. 'name:if
378         * (...)'
379         */
380        private void createContinuedConstructRules(ETokenType beginToken, ETokenType continuationToken,
381                        ETokenType finalToken, ETokenType suffixToken) {
382                createContinuedConstructRuleWithName(beginToken, beginToken, EnumSet.of(continuationToken, finalToken),
383                                suffixToken, true);
384                createContinuedConstructRuleWithName(beginToken, continuationToken, EnumSet.of(continuationToken, finalToken),
385                                suffixToken, false);
386                createEndContinuedConstructRule(beginToken, finalToken);
387        }
388
389        /**
390         * Create a rule for parsing a statement of a construct that can be
391         * continued. The construct token is the token of the construct type, e.g.
392         * 'if'. The statement token is the beginning token of the construct
393         * statement, e.g. 'if' or 'else if'. The given continuation tokens are
394         * those, that can continue the current construct statement. The given
395         * suffix token must be recognized after the parenthesis of the construct
396         * e.g. 'then' in an 'if' construct. The suffix token can be null, if there
397         * is none. If nameInFront is true, an optional name label can prefix the
398         * construct. If it is false, the construct can be followed by a name.
399         */
400        private void createContinuedConstructRuleWithName(ETokenType constructToken, ETokenType partToken,
401                        EnumSet<ETokenType> continuationTokens, ETokenType suffixToken, boolean nameInFront) {
402                RecognizerBase<EFortranParserStates> recognizer = inState(STATEMENT_STATES);
403                if (nameInFront) {
404                        recognizer = recognizer.optional(IDENTIFIER, COLON);
405                }
406                recognizer = recognizer.markStart().sequence(partToken, LPAREN).skipToWithNesting(RPAREN, LPAREN, RPAREN);
407                if (suffixToken != null) {
408                        recognizer = recognizer.sequence(suffixToken);
409                }
410                if (!nameInFront) {
411                        recognizer = recognizer.optional(IDENTIFIER);
412                }
413                recognizer = recognizer.sequence(EOL).createNode(EShallowEntityType.STATEMENT, 0).parseUntil(IN_METHOD);
414
415                finishConstructWithOptionalName(recognizer, constructToken);
416                recognizer.sequenceBefore(continuationTokens).endNodeWithContinuation();
417        }
418
419        /**
420         * Create a rule for parsing the end of a continued construct. The construct
421         * token is the token of the construct type, e.g. 'if'. The final token is
422         * the beginning token of the statement, e.g. 'else'. An optional identifier
423         * can follow for named constructs.
424         */
425        private void createEndContinuedConstructRule(ETokenType constructToken, ETokenType finalToken) {
426                RecognizerBase<EFortranParserStates> finalAlternative = inState(STATEMENT_STATES).sequence(finalToken)
427                                .optional(IDENTIFIER).sequence(EOL).createNode(EShallowEntityType.STATEMENT, 0).parseUntil(IN_METHOD);
428
429                finishConstructWithOptionalName(finalAlternative, constructToken);
430        }
431
432        /**
433         * Finishes the given recognizer with 'end' and the optional given token
434         * type. An optional identifier can follow for named constructs.
435         */
436        private static void finishConstructWithOptionalName(RecognizerBase<EFortranParserStates> recognizer,
437                        ETokenType constructType) {
438                recognizer.sequence(END).optional(constructType).optional(IDENTIFIER).sequence(EOL).endNode();
439        }
440
441        /** Creates rules for parsing simple statements. */
442        private void createSimpleStatementRules() {
443                inState(STATEMENT_STATES).sequence(STATEMENT_TOKENS)
444                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.SIMPLE_STATEMENT, 0)
445                                .skipBefore(EnumSet.of(EOL, SEMICOLON)).optional(SEMICOLON).endNode();
446        }
447}