001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright (c) 2009-2017 CQSE GmbH                                        |
004|                                                                          |
005+-------------------------------------------------------------------------*/
006package eu.cqse.check.framework.shallowparser.languages.cobol;
007
008import static eu.cqse.check.framework.scanner.ETokenType.*;
009import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.ATTRIBUTE;
010import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.META;
011import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.METHOD;
012import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.MODULE;
013import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.STATEMENT;
014import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.TYPE;
015import static eu.cqse.check.framework.shallowparser.languages.cobol.ECobolParserState.IN_STATEMENT;
016import static eu.cqse.check.framework.shallowparser.languages.cobol.ECobolParserState.TOP_LEVEL;
017import static eu.cqse.check.framework.shallowparser.languages.cobol.EStatementEndType.SCOPE;
018import static eu.cqse.check.framework.shallowparser.languages.cobol.EStatementEndType.SIMPLE;
019
020import java.util.EnumSet;
021import java.util.List;
022
023import org.conqat.lib.commons.region.Region;
024
025import eu.cqse.check.framework.postprocessor.cobol.SectionParagraphToMethodProcessor;
026import eu.cqse.check.framework.scanner.ETokenType;
027import eu.cqse.check.framework.scanner.IToken;
028import eu.cqse.check.framework.shallowparser.SubTypeNames;
029import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType;
030import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;
031import eu.cqse.check.framework.shallowparser.framework.ShallowEntity;
032import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase;
033
034/**
035 * Shallow parser for the Cobol language. It considers the following language
036 * reference documentation links:
037 * <code>http://publibfp.boulder.ibm.com/epubs/pdf/igy6lr10.pdf</code>
038 * <code>https://open-cobol.sourceforge.io/guides/OpenCOBOL%20Programmers%20Guide.pdf</code>
039 * <code>http://www.cs.vu.nl/grammarware/cobol</code> We try to make it
040 * compatible with the fixed and free form versions that Cobol code may be
041 * written in.
042 */
043public class CobolShallowParser extends ShallowParserBase<ECobolParserState> {
044
045        /** Identifier and string tokens */
046        private static final EnumSet<ETokenType> IDENTIFIER_AND_STRING_LITERALS = EnumSet.of(IDENTIFIER, STRING_LITERAL);
047
048        /** Identifier and numeric literal tokens */
049        private static final EnumSet<ETokenType> IDENTIFIER_AND_NUMERIC_LITERALS = EnumSet.of(IDENTIFIER, INTEGER_LITERAL,
050                        FLOATING_POINT_LITERAL);
051
052        /** Identifier and literals tokens */
053        private static final EnumSet<ETokenType> IDENTIFIER_AND_LITERALS = EnumSet.of(IDENTIFIER, INTEGER_LITERAL,
054                        FLOATING_POINT_LITERAL, STRING_LITERAL);
055
056        /** Identifier and literals tokens */
057        private static final EnumSet<ETokenType> IDENTIFIER_LITERALS_AND_ZERO = EnumSet.of(IDENTIFIER, INTEGER_LITERAL,
058                        FLOATING_POINT_LITERAL, STRING_LITERAL, ZERO);
059
060        /** Literal types */
061        private static final EnumSet<ETokenType> LITERAL_TYPES = EnumSet.of(STRING_LITERAL, INTEGER_LITERAL,
062                        FLOATING_POINT_LITERAL);
063
064        /**
065         * Apparently, compiler options such as PGMNAME can also be used as variable
066         * names.
067         */
068        private static final EnumSet<ETokenType> VALID_VARIABLE_NAMES = EnumSet.of(IDENTIFIER, COMPILER_OPTIONS);
069
070        /** Constant subtype name for section */
071        public static final String SECTION_SUBTYPE_NAME = "section";
072
073        /** Constant subtype name for paragraph */
074        public static final String PARAGRAPH_SUBTYPE_NAME = "paragraph";
075
076        /** Constructor. */
077        public CobolShallowParser() {
078                super(ECobolParserState.class, TOP_LEVEL);
079                createTopLevelRules();
080        }
081
082        /**
083         * Create rules for matching divisions. Though there are 4 divisions, we take
084         * only the ID division as being in top level and everything afterwards are
085         * parsed as belonging to a program (or module), method, class, factory or an
086         * object body so the parsed form looks like modern trendy languages.
087         */
088        private void createTopLevelRules() {
089                createRuleForDivision(TOP_LEVEL, EnumSet.of(ID, IDENTIFICATION));
090
091                createRuleForProgramType(PROGRAM_ID, MODULE, PROGRAM, true);
092                // Enterprise COBOL for z/OS adds more entities that can be identified
093                createRuleForProgramType(CLASS_ID, TYPE, CLASS, true);
094                createRuleForProgramType(METHOD_ID, METHOD, ETokenType.METHOD, true);
095                createRuleForProgramType(FUNCTION_ID, METHOD, ETokenType.METHOD, true);
096                createRuleForProgramType(OBJECT, TYPE, OBJECT, false);
097                createRuleForProgramType(FACTORY, TYPE, FACTORY, false);
098
099                createRulesForEnvironmentDivision();
100                createRulesForDataDivision();
101                createRulesForProcedureDivision();
102                createRulesForCompilerDirectiveStatements();
103        }
104
105        /**
106         * Create rule to identify a program, method, factory, object or class.
107         * Factories and objects do not need to have names like programs, methods and
108         * classes in Enterprise COBOL for z/OS
109         */
110        private void createRuleForProgramType(ETokenType startType, EShallowEntityType entityType, ETokenType endType,
111                        boolean canIncludeEndIdentifier) {
112                RecognizerBase<ECobolParserState> recognizer = inAnyState().sequence(startType, DOT);
113                if (canIncludeEndIdentifier) {
114                        recognizer = recognizer.sequence(EnumSet.of(IDENTIFIER, STRING_LITERAL));
115                        if (startType == PROGRAM_ID) {
116                                recognizer = recognizer.optional(IS).optional(EnumSet.of(RECURSIVE, COMMON, INITIAL)).skipTo(DOT);
117                        } else if (startType == CLASS_ID) {
118                                recognizer = recognizer.optional(INHERITS, IDENTIFIER).sequence(DOT);
119                        } else {
120                                recognizer = recognizer.sequence(DOT);
121                        }
122
123                        // To have the test{@link
124                        // CobolShallowParserTest#testMinimalProgramStructure} not return
125                        // an incompletely parsed node program node (since there is no end
126                        // node), we use parseUntilOrEof()
127                        recognizer = recognizer.createNode(entityType, 2).parseUntilOrEof(TOP_LEVEL);
128                } else {
129                        recognizer = recognizer.createNode(entityType, 0).parseUntilOrEof(TOP_LEVEL);
130                }
131
132                recognizer.sequence(END, endType, DOT).endNode();
133                if (canIncludeEndIdentifier) {
134                        recognizer.sequence(END, endType, IDENTIFIER, DOT).endNode();
135                }
136        }
137
138        /**
139         * Create rule for a division.
140         */
141        private void createRuleForDivision(ECobolParserState state, Object division) {
142                inState(state).sequence(division, DIVISION).createNode(META, 1, 0).skipTo(DOT).endNode();
143        }
144
145        /**
146         * Create rules for paragraphs and sections in the environment division.
147         */
148        private void createRulesForEnvironmentDivision() {
149                createRuleForDivision(TOP_LEVEL, ENVIRONMENT);
150
151                completeRule(inState(TOP_LEVEL).sequence(CONFIGURATION, SECTION).createNode(META, new Region(0, 1)), null,
152                                SIMPLE);
153
154                RecognizerBase<ECobolParserState> sourceComputerSubRecognizer = createRecognizer(
155                                start -> start.sequence(IDENTIFIER).optional(WITH).optional(DEBUGGING, MODE));
156                RecognizerBase<ECobolParserState> sourceComputerRecognizer = inState(TOP_LEVEL).sequence(SOURCE_COMPUTER, DOT)
157                                .createNode(META, 0).optionalSubRecognizer(sourceComputerSubRecognizer);
158                completeRule(sourceComputerRecognizer, null, SIMPLE);
159
160                createRulesForObjectComputer();
161                createRulesForSpecialNamesParagraph();
162                createRulesForRepositoryParagraph();
163
164                // CONSTRAINTS, CLASS-ATTRIBUTES & ASSEMBLY-ATTRIBUTES paragraphs
165                completeRule(inState(TOP_LEVEL).sequence(EnumSet.of(CONSTRAINTS, ASSEMBLY_ATTRIBUTES, CLASS_ATTRIBUTES))
166                                .createNode(META, 0), null, SIMPLE);
167
168                completeRule(
169                                inState(TOP_LEVEL).sequence(CUSTOM_ATTRIBUTE).createNode(META, 0).optional(IS).sequence(IDENTIFIER),
170                                null, SIMPLE);
171
172                createRulesForIOSection();
173        }
174
175        /**
176         * Create rules for REPOSITORY paragraph. Rules here match 'import' statements.
177         */
178        private void createRulesForRepositoryParagraph() {
179                completeRule(inState(TOP_LEVEL).sequence(REPOSITORY).createNode(META, 0), null, SIMPLE);
180
181                completeRule(inState(TOP_LEVEL).sequence(INTERFACE, IDENTIFIER).createNode(META, 0, 1)
182                                .subRecognizer(getRepositoryInterfaceClassSubRecognizer()), null, SIMPLE);
183                completeRule(inState(TOP_LEVEL).sequence(EnumSet.of(PROGRAM, PROPERTY, DELEGATE, ENUM), IDENTIFIER)
184                                .createNode(META, 0, 1).optional(AS, LITERAL_TYPES), null, SIMPLE);
185
186                RecognizerBase<ECobolParserState> functionNameRecognizer = createRecognizer(
187                                start -> start.sequence(AS, LITERAL_TYPES));
188                functionNameRecognizer.sequence(INTRINSIC);
189                RecognizerBase<ECobolParserState> functionSubRecognizer = createRecognizer(
190                                start -> start.sequence(ALL, INTRINSIC));
191                functionSubRecognizer.repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER))
192                                .optionalSubRecognizer(functionNameRecognizer);
193                RecognizerBase<ECobolParserState> functionRecognizer = inState(TOP_LEVEL).sequence(FUNCTION).createNode(META, 0)
194                                .subRecognizer(functionSubRecognizer);
195                completeRule(functionRecognizer, null, SIMPLE);
196        }
197
198        /**
199         * Create rules for SPECIAL-NAMES paragraph.
200         */
201        private void createRulesForSpecialNamesParagraph() {
202                completeRule(inState(TOP_LEVEL).sequence(SPECIAL_NAMES).createNode(META, 0), null, SIMPLE);
203
204                createRuleForSwitchClause();
205                createRuleForAlphabetClause();
206                createRuleForSymbolicClause();
207                createRuleForClassClauses();
208
209                RecognizerBase<ECobolParserState> currencySubrecognizer = createRecognizer(
210                                start -> start.optional(WITH).sequence(PICTURE, SYMBOL, STRING_LITERAL));
211                RecognizerBase<ECobolParserState> currencyRecognizer = inState(TOP_LEVEL).sequence(CURRENCY).optional(SIGN)
212                                .optional(IS).sequence(STRING_LITERAL).createNode(META, 0, 3)
213                                .optionalSubRecognizer(currencySubrecognizer);
214                completeRule(currencyRecognizer, null, SIMPLE);
215
216                completeRule(
217                                inState(TOP_LEVEL).sequence(DECIMAL_POINT).createNode(META, 0, "comma").optional(IS).sequence(COMMA),
218                                null, SIMPLE);
219                completeRule(inState(TOP_LEVEL).sequence(XML_SCHEMA, IDENTIFIER).optional(IS).sequence(IDENTIFIER_AND_LITERALS)
220                                .createNode(META, 0, 1), null, SIMPLE);
221                completeRule(inState(TOP_LEVEL).sequence(NUMERIC, SIGN).optional(IS).sequence(TRAILING, SEPARATE)
222                                .createNode(META, new Region(0, 1)), null, SIMPLE);
223                completeRule(inState(TOP_LEVEL).sequence(CALL_CONVENTION, INTEGER_LITERAL).createNode(META, 0).optional(IS)
224                                .sequence(IDENTIFIER), null, SIMPLE);
225                completeRule(inState(TOP_LEVEL).sequence(CONSOLE).createNode(META, 0).optional(IS).sequence(CRT), null, SIMPLE);
226                completeRule(inState(TOP_LEVEL).sequence(CURSOR).createNode(META, 0).optional(IS).sequence(IDENTIFIER), null,
227                                SIMPLE);
228                completeRule(inState(TOP_LEVEL).sequence(CRT, STATUS).createNode(META, new Region(0, 1)).optional(IS)
229                                .sequence(IDENTIFIER), null, SIMPLE);
230        }
231
232        /**
233         * Creates a rule for the CLASS clause as used in the SPECIAL-NAMES & REPOSITORY
234         * paragraphs.
235         */
236        private void createRuleForClassClauses() {
237                RecognizerBase<ECobolParserState> classRecognizer = inState(TOP_LEVEL).sequence(CLASS, IDENTIFIER)
238                                .createNode(META, 0, 1);
239                classRecognizer.sequence(IS).repeatedSubRecognizer(getLiteralThroughAlsoRecognizer(false));
240                classRecognizer.sequence(IMPLEMENTS).repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER));
241                classRecognizer.subRecognizer(getRepositoryInterfaceClassSubRecognizer());
242
243                completeRule(classRecognizer, null, SIMPLE);
244        }
245
246        /**
247         * Returns a sub recognizer for matching the INTERFACE & CLASS clauses in the
248         * REPOSITORY paragraph.
249         */
250        private RecognizerBase<ECobolParserState> getRepositoryInterfaceClassSubRecognizer() {
251                RecognizerBase<ECobolParserState> expandsRecognizer = createRecognizer(
252                                start -> start.sequence(EXPANDS, IDENTIFIER, USING, IDENTIFIER));
253                return createRecognizer(start -> start.optional(AS, LITERAL_TYPES).optionalSubRecognizer(expandsRecognizer));
254        }
255
256        /**
257         * Create rule for SWITCH clause.
258         */
259        private void createRuleForSwitchClause() {
260                RecognizerBase<ECobolParserState> isIdentifierRecognizer = createRecognizer(
261                                start -> start.optional(IS).sequence(IDENTIFIER));
262                RecognizerBase<ECobolParserState> statusRecognizer = createRecognizer(start -> start
263                                .sequence(EnumSet.of(ON, OFF)).optional(STATUS).optionalSubRecognizer(isIdentifierRecognizer));
264                RecognizerBase<ECobolParserState> switchSubRecognizer = createRecognizer(
265                                start -> start.optionalSubRecognizer(isIdentifierRecognizer).optionalSubRecognizer(statusRecognizer)
266                                                .optionalSubRecognizer(statusRecognizer));
267                RecognizerBase<ECobolParserState> switchRecognizer = inState(TOP_LEVEL).sequence(EnumSet.of(SWITCH_0, SWITCH_1,
268                                SWITCH_2, SWITCH_3, SWITCH_4, SWITCH_5, SWITCH_6, SWITCH_7, SWITCH_8, IDENTIFIER))
269                                .subRecognizer(switchSubRecognizer).createNode(META, "special-name", 0);
270
271                completeRule(switchRecognizer, null, SIMPLE);
272        }
273
274        /**
275         * Create rule for SYMBOLIC clause
276         */
277        private void createRuleForSymbolicClause() {
278                RecognizerBase<ECobolParserState> symbolicSubRecognizer = createRecognizer(start -> start.optional(COMMA)
279                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER)).sequence(EnumSet.of(IS, ARE))
280                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(INTEGER_LITERAL)));
281                RecognizerBase<ECobolParserState> symbolicRecognizer = inState(TOP_LEVEL).sequence(SYMBOLIC).createNode(META, 0)
282                                .optional(CHARACTERS).repeatedSubRecognizer(symbolicSubRecognizer).optional(IN, IDENTIFIER);
283
284                completeRule(symbolicRecognizer, null, SIMPLE);
285        }
286
287        /**
288         * Create rule for the ALPHABET clause.
289         */
290        private void createRuleForAlphabetClause() {
291                RecognizerBase<ECobolParserState> alphabetRecognizer = inState(TOP_LEVEL).sequence(ALPHABET, IDENTIFIER)
292                                .createNode(META, 0, 1).sequence(IS);
293                alphabetRecognizer.sequence(EnumSet.of(STANDARD_1, STANDARD_2, NATIVE, ASCII, EBCDIC));
294                alphabetRecognizer.repeatedSubRecognizer(getLiteralThroughAlsoRecognizer(true));
295
296                completeRule(alphabetRecognizer, null, SIMPLE);
297        }
298
299        /**
300         * Returns a sub recognizer for LITERAL...THROUGH...ALSO pattern
301         */
302        private RecognizerBase<ECobolParserState> getLiteralThroughAlsoRecognizer(boolean includeAlsoRecognizer) {
303                RecognizerBase<ECobolParserState> alsoRecognizer = createRecognizer(
304                                start -> start.optional(COMMA).sequence(ALSO, LITERAL_TYPES));
305                RecognizerBase<ECobolParserState> literalSubRecognizer = createRecognizer(start -> {
306                        RecognizerBase<ECobolParserState> prefix = start.sequence(LITERAL_TYPES);
307                        prefix.sequence(EnumSet.of(THROUGH, THRU), LITERAL_TYPES);
308                        if (includeAlsoRecognizer) {
309                                prefix.repeatedSubRecognizer(alsoRecognizer);
310                        }
311                });
312                return createRecognizer(start -> start.optional(COMMA).subRecognizer(literalSubRecognizer));
313        }
314
315        /**
316         * Create rules for the OBJECT-COMPUTER paragraph.
317         */
318        private void createRulesForObjectComputer() {
319                RecognizerBase<ECobolParserState> segmentLimitRecognizer = createRecognizer(start -> start
320                                .sequence(SEGMENT_LIMIT).optional(IS).sequence(EnumSet.of(INTEGER_LITERAL, FLOATING_POINT_LITERAL)));
321                RecognizerBase<ECobolParserState> programRecognizer = createRecognizer(start -> start.optional(PROGRAM)
322                                .optional(COLLATING).sequence(SEQUENCE).optional(IS).sequence(IDENTIFIER));
323                RecognizerBase<ECobolParserState> memoryRecognizer = createRecognizer(start -> start.sequence(MEMORY)
324                                .optional(SIZE).sequence(INTEGER_LITERAL, EnumSet.of(WORDS, CHARACTERS, MODULES)));
325                RecognizerBase<ECobolParserState> objectComputerRecognizer = inState(TOP_LEVEL).sequence(OBJECT_COMPUTER, DOT)
326                                .createNode(META, 0).optional(IDENTIFIER).optionalSubRecognizer(memoryRecognizer)
327                                .optionalSubRecognizer(programRecognizer).optionalSubRecognizer(segmentLimitRecognizer);
328
329                completeRule(objectComputerRecognizer, null, SIMPLE);
330        }
331
332        /**
333         * Create rules for the IO Section in the environment division.
334         */
335        private void createRulesForIOSection() {
336                completeRule(inState(TOP_LEVEL).sequence(INPUT_OUTPUT, SECTION).createNode(META, 0), null, SIMPLE);
337
338                completeRule(inState(TOP_LEVEL).sequence(EnumSet.of(FILE_CONTROL, I_O_CONTROL)).createNode(META, 0), null,
339                                SIMPLE);
340
341                inState(TOP_LEVEL).sequence(SELECT).optional(OPTIONAL).markStart().sequence(IDENTIFIER)
342                                .createNode(STATEMENT, "select", 0).skipTo(DOT).endNode();
343
344                createRuleForRerunClause();
345                RecognizerBase<ECobolParserState> sameRecognizer = inState(TOP_LEVEL).sequence(SAME).createNode(META, 0)
346                                .optional(EnumSet.of(RECORD, SORT, SORT_MERGE)).optional(AREA).optional(FOR).sequence(IDENTIFIER)
347                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER));
348                completeRule(sameRecognizer, null, SIMPLE);
349
350                RecognizerBase<ECobolParserState> multipleFileSubRecognizer = createRecognizer(
351                                start -> start.optional(COMMA).sequence(IDENTIFIER).optional(POSITION, INTEGER_LITERAL));
352                RecognizerBase<ECobolParserState> multipleFileRecognizer = inState(TOP_LEVEL).sequence(MULTIPLE, FILE)
353                                .createNode(META, new Region(0, 1)).optional(TAPE).optional(CONTAINS)
354                                .repeatedSubRecognizer(multipleFileSubRecognizer);
355                completeRule(multipleFileRecognizer, null, SIMPLE);
356
357                createRuleForApplyClause();
358        }
359
360        /**
361         * Create rule for APPLY clause.
362         */
363        private void createRuleForApplyClause() {
364                RecognizerBase<ECobolParserState> applyRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(APPLY)
365                                .createNode(META, 0);
366                applyRecognizer.sequence(REORG_CRITERIA).optional(TO).sequence(IDENTIFIER).optional(ON).sequence(IDENTIFIER);
367                applyRecognizer.sequence(EnumSet.of(CORE_INDEX, RECORD_OVERFLOW, WRITE_ONLY)).optional(ON)
368                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER));
369                completeRule(applyRecognizer, null, SIMPLE);
370        }
371
372        /**
373         * Create rule for RERUN CLAUSE.
374         */
375        private void createRuleForRerunClause() {
376                RecognizerBase<ECobolParserState> everyIntegerRecognizer = createRecognizer(start -> {
377                        RecognizerBase<ECobolParserState> prefix = start.sequence(INTEGER_LITERAL);
378                        prefix.sequence(RECORDS).optional(OF).sequence(IDENTIFIER);
379                        prefix.sequence(CLOCK_UNITS);
380                });
381
382                RecognizerBase<ECobolParserState> everyRecognizer = createRecognizer(start -> {
383                        RecognizerBase<ECobolParserState> prefix = start.sequence(EVERY);
384                        prefix.sequence(IDENTIFIER);
385                        prefix.sequence(END).optional(OF).sequence(EnumSet.of(REEL, UNIT)).optional(OF).sequence(IDENTIFIER);
386                        prefix.subRecognizer(everyIntegerRecognizer);
387                });
388
389                completeRule(inState(TOP_LEVEL).sequence(RERUN).createNode(META, 0).optional(ON, IDENTIFIER)
390                                .subRecognizer(everyRecognizer), null, SIMPLE);
391        }
392
393        /**
394         * Create rules for paragraphs and sections in the data division. These are
395         * mainly program variables declarations.
396         */
397        private void createRulesForDataDivision() {
398                createRuleForDivision(TOP_LEVEL, DATA);
399
400                completeRule(inState(TOP_LEVEL).sequence(EnumSet.of(FILE, WORKING_STORAGE, LOCAL_STORAGE, THREAD_LOCAL_STORAGE,
401                                OBJECT_STORAGE, LINKAGE, REPORT, SCREEN), SECTION).createNode(META, 0, 1), null, SIMPLE);
402
403                createRuleForFileDescriptionEntry();
404                createRuleForDataDescriptionEntries();
405        }
406
407        /**
408         * Returns the USAGE clause recognizer used in DATA description.
409         */
410        private RecognizerBase<ECobolParserState> getUsageClauseRecognizer() {
411                RecognizerBase<ECobolParserState> objectRecognizer = createRecognizer(start -> {
412                        RecognizerBase<ECobolParserState> prefix = start.sequence(REFERENCE);
413                        prefix.sequence(IDENTIFIER);
414                        prefix.sequence(FACTORY).optional(OF).sequence(EnumSet.of(ACTIVE_CLASS, IDENTIFIER))
415                                        .optional(EnumSet.of(ONLY, EVENT));
416                });
417                RecognizerBase<ECobolParserState> usageSubRecognizer = createRecognizer(start -> {
418                        RecognizerBase<ECobolParserState> prefix = start.optional(IS);
419                        prefix.sequence(EnumSet.of(BINARY, CHARACTER, COMPUTATIONAL, COMP, COMPUTATIONAL_1, COMP_1, COMPUTATIONAL_2,
420                                        COMP_2, COMPUTATIONAL_3, COMP_3, COMPUTATIONAL_4, COMP_4, COMPUTATIONAL_5, COMP_5, COMPUTATIONAL_X,
421                                        COMP_X, CONDITIONAL_VALUE, DECIMAL, DISPLAY, EVENT_POINTER, FLOAT_SHORT, FLOAT_LONG, INDEX,
422                                        MONITOR_POINTER, MUTEX_POINTER, NATIONAL, PACKED_DECIMAL, POINTER, PROCEDURE_POINTER,
423                                        SEMAPHORE_POINTER, STRING, THREAD_POINTER, IDENTIFIER));
424                        prefix.sequence(PROGRAM_POINTER).optional(TO).optional(IDENTIFIER);
425                        prefix.sequence(OBJECT).optionalSubRecognizer(objectRecognizer);
426                        prefix.sequence(EnumSet.of(BINARY_CHAR, BINARY_SHORT, BINARY_LONG, BINARY_DOUBLE),
427                                        EnumSet.of(SIGNED, UNSIGNED));
428                });
429                return createRecognizer(start -> start.optional(USAGE).optional(IS).subRecognizer(usageSubRecognizer));
430        }
431
432        /**
433         * Returns the OCCURS clause recognizer for DATA description.
434         */
435        private RecognizerBase<ECobolParserState> getOccursClauseRecognizer() {
436                RecognizerBase<ECobolParserState> indexedRecognizer = createRecognizer(start -> start.sequence(INDEXED)
437                                .optional(BY).sequence(IDENTIFIER).repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER)));
438                RecognizerBase<ECobolParserState> ascendDescendRecognizer = createRecognizer(
439                                start -> start.sequence(EnumSet.of(ASCENDING, DESCENDING)).optional(KEY).optional(IS)
440                                                .sequence(IDENTIFIER).repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER)));
441
442                return createRecognizer(start -> start.sequence(OCCURS).optional(INTEGER_LITERAL, TO)
443                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(EnumSet.of(ANY, INTEGER_LITERAL))).optional(TIMES)
444                                .optionalSubRecognizer(getDependingRecognizer()).repeatedSubRecognizer(ascendDescendRecognizer)
445                                .repeatedSubRecognizer(indexedRecognizer));
446        }
447
448        /**
449         * Returns the DEPENDING sub recognizer used in data division and the GO TO verb
450         * in procedure division
451         */
452        private RecognizerBase<ECobolParserState> getDependingRecognizer() {
453                return createRecognizer(start -> start.sequence(DEPENDING).optional(ON).sequence(IDENTIFIER));
454        }
455
456        /**
457         * Create rules for record and level description entries. These are majorly data
458         * variables.
459         */
460        private void createRuleForDataDescriptionEntries() {
461                RecognizerBase<ECobolParserState> renameRecognizer = createRecognizer(
462                                start -> start.sequence(RENAMES, IDENTIFIER).optional(EnumSet.of(THROUGH, THRU), IDENTIFIER));
463                RecognizerBase<ECobolParserState> classModifierRecognizer = createRecognizer(
464                                start -> start.sequence(EnumSet.of(IDENTIFIER, PUBLIC, PRIVATE, PROTECTED, INTERNAL)));
465                RecognizerBase<ECobolParserState> blankRecognizer = createRecognizer(
466                                start -> start.sequence(BLANK).optional(WHEN).sequence(EnumSet.of(ZERO, ZEROS, ZEROES)));
467                RecognizerBase<ECobolParserState> justRecognizer = createRecognizer(
468                                start -> start.sequence(EnumSet.of(JUSTIFIED, JUST), RIGHT));
469                RecognizerBase<ECobolParserState> syncRecognizer = createRecognizer(
470                                start -> start.sequence(EnumSet.of(SYNC, SYNCHRONIZED)).optional(EnumSet.of(LEFT, RIGHT)));
471                RecognizerBase<ECobolParserState> redefinesRecognizer = createRecognizer(
472                                start -> start.sequence(REDEFINES, IDENTIFIER));
473                RecognizerBase<ECobolParserState> dataDescriptionRecognizer = inState(TOP_LEVEL).sequence(INTEGER_LITERAL)
474                                .sequence(EnumSet.of(FILLER, IDENTIFIER)).createNode(ATTRIBUTE, 1)
475                                .optionalSubRecognizer(redefinesRecognizer).subRecognizer(getIsClauseRecognizer(), 0, 4)
476                                .optionalSubRecognizer(getUsageClauseRecognizer()).optionalSubRecognizer(getOccursClauseRecognizer())
477                                .optionalSubRecognizer(getSignRecognizer()).optionalSubRecognizer(syncRecognizer)
478                                .optionalSubRecognizer(justRecognizer).optionalSubRecognizer(blankRecognizer)
479                                .optionalSubRecognizer(getPropertyClauseRecognizer()).optionalSubRecognizer(classModifierRecognizer)
480                                .optional(ANY, LENGTH).optional(PICTURE_CLAUSE).optionalSubRecognizer(renameRecognizer)
481                                .optionalSubRecognizer(getValuesIsAreRecognizer());
482
483                completeRule(dataDescriptionRecognizer, null, SIMPLE);
484        }
485
486        /**
487         * Returns the IS clause recognizer for DATA description.
488         */
489        private RecognizerBase<ECobolParserState> getIsClauseRecognizer() {
490                return createRecognizer(start -> start.optional(IS)
491                                .sequence(EnumSet.of(EXTERNAL, GLOBAL, TYPEDEF, THREAD_LOCAL)).optional(BY, LITERAL_TYPES));
492        }
493
494        /**
495         * Returns the VALUE clause recognizer
496         * <code>https://supportline.microfocus.com/documentation/books/nx50/lrpubb.htm</code>
497         */
498        private RecognizerBase<ECobolParserState> getValuesIsAreRecognizer() {
499                RecognizerBase<ECobolParserState> arithmeticRecognizer = createRecognizer(start -> {
500                        RecognizerBase<ECobolParserState> prefix = start.sequence(EnumSet.of(PLUS, MINUS, MULT, DIV, AND, OR));
501                        prefix.sequence(EnumSet.of(INTEGER_LITERAL, NEXT));
502                        prefix.sequence(EnumSet.of(START, LENGTH)).optional(OF).sequence(IDENTIFIER);
503                });
504
505                RecognizerBase<ECobolParserState> whenRecognizer = createRecognizer(start -> start.optional(WHEN).optional(SET)
506                                .optional(TO).sequence(FALSE).optional(IS).sequence(LITERAL_TYPES));
507
508                RecognizerBase<ECobolParserState> literalRecognizer = createRecognizer(start -> {
509                        RecognizerBase<ECobolParserState> prefix = start.optional(COMMA);
510                        prefix.optional(LITERAL_TYPES).sequence(EnumSet.of(THROUGH, THRU), LITERAL_TYPES)
511                                        .optionalSubRecognizer(whenRecognizer);
512                        prefix.subRecognizer(arithmeticRecognizer);
513                        prefix.repeatedSubRecognizer(getRepeatedSequenceRecognizer(LITERAL_TYPES)).sequence(FROM)
514                                        .skipNested(LPAREN, RPAREN).optional(TO).skipNested(LPAREN, RPAREN);
515                });
516
517                RecognizerBase<ECobolParserState> isAreRecognizer = createRecognizer(start -> {
518                        RecognizerBase<ECobolParserState> prefix = start.optional(EnumSet.of(IS, ARE));
519                        prefix.sequence(NEXT).optionalSubRecognizer(arithmeticRecognizer);
520                        prefix.sequence(EnumSet.of(START, LENGTH)).optional(OF).sequence(IDENTIFIER)
521                                        .optionalSubRecognizer(arithmeticRecognizer);
522                        prefix.sequence(LITERAL_TYPES).repeatedSubRecognizer(literalRecognizer);
523                });
524
525                return createRecognizer(start -> start.sequence(EnumSet.of(VALUE, VALUES)).subRecognizer(isAreRecognizer));
526        }
527
528        /**
529         * Returns the SIGN clause recognizer
530         */
531        private RecognizerBase<ECobolParserState> getSignRecognizer() {
532                RecognizerBase<ECobolParserState> separateCharRecognizer = createRecognizer(
533                                start -> start.sequence(SEPARATE).optional(CHARACTER));
534                return createRecognizer(start -> start.sequence(SIGN).optional(IS).sequence(EnumSet.of(LEADING, TRAILING))
535                                .optionalSubRecognizer(separateCharRecognizer));
536        }
537
538        /**
539         * Create sub recognizer for the PROPERTY clause
540         */
541        private RecognizerBase<ECobolParserState> getPropertyClauseRecognizer() {
542                RecognizerBase<ECobolParserState> getSetRecognizer = createRecognizer(
543                                start -> start.optional(WITH).sequence(NO, EnumSet.of(GET, SET)));
544                return createRecognizer(start -> start.sequence(PROPERTY).optionalSubRecognizer(getSetRecognizer));
545        }
546
547        /**
548         * Create rule for the file description ENTRY.
549         */
550        private void createRuleForFileDescriptionEntry() {
551                RecognizerBase<ECobolParserState> lineRecognizer = createRecognizer(
552                                start -> start.optional(LINES).optional(AT).sequence(EnumSet.of(TOP, BOTTOM), IDENTIFIER_AND_LITERALS));
553                RecognizerBase<ECobolParserState> dataRecognizer = createRecognizer(
554                                start -> start.sequence(DATA).sequence(EnumSet.of(RECORD, RECORDS)).optional(EnumSet.of(IS, ARE))
555                                                .sequence(IDENTIFIER).repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER)));
556                RecognizerBase<ECobolParserState> recordingRecognizer = createRecognizer(start -> start.sequence(RECORDING)
557                                .optional(MODE).optional(IS).sequence(EnumSet.of(FIXED, VARIABLE, IDENTIFIER)));
558                RecognizerBase<ECobolParserState> valueOfRecognizer = createRecognizer(
559                                start -> start.sequence(VALUE, OF, EnumSet.of(FILE_ID, IDENTIFIER)).optional(IS)
560                                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER_AND_LITERALS)));
561                RecognizerBase<ECobolParserState> recordRecognizer = createRecognizer(
562                                start -> start.sequence(RECORD).subRecognizer(getContainsRecognizer()));
563                RecognizerBase<ECobolParserState> blockRecognizer = createRecognizer(
564                                start -> start.sequence(BLOCK).subRecognizer(getContainsRecognizer()));
565                RecognizerBase<ECobolParserState> descriptionRecognizer = inState(TOP_LEVEL)
566                                .sequence(EnumSet.of(FD, SD), IDENTIFIER).createNode(META, 0, 1)
567                                .subRecognizer(getIsClauseRecognizer(), 0, 3).optionalSubRecognizer(blockRecognizer)
568                                .optionalSubRecognizer(recordRecognizer).optionalSubRecognizer(getLabelClauseRecognizer())
569                                .optionalSubRecognizer(valueOfRecognizer).optionalSubRecognizer(recordingRecognizer)
570                                .optionalSubRecognizer(dataRecognizer).optionalSubRecognizer(linageClauseRecognizer())
571                                .optionalSubRecognizer(lineRecognizer).optionalSubRecognizer(lineRecognizer)
572                                .optionalSubRecognizer(getCodeSetClauseRecognizer());
573
574                completeRule(descriptionRecognizer, null, SIMPLE);
575        }
576
577        /**
578         * Returns the CODE-SET clause recognizer.
579         */
580        private RecognizerBase<ECobolParserState> getCodeSetClauseRecognizer() {
581                RecognizerBase<ECobolParserState> codeSetSubRecognizer = createRecognizer(
582                                start -> start.sequence(FOR).repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER)));
583
584                return createRecognizer(start -> start.sequence(CODE_SET).optional(IS).sequence(IDENTIFIER)
585                                .optionalSubRecognizer(codeSetSubRecognizer));
586        }
587
588        /**
589         * Returns the LINAGE clause recognizer
590         */
591        private RecognizerBase<ECobolParserState> linageClauseRecognizer() {
592                RecognizerBase<ECobolParserState> linageSubRecognizer = createRecognizer(
593                                start -> start.optional(WITH).sequence(FOOTING).optional(AT).sequence(IDENTIFIER_AND_LITERALS));
594                return createRecognizer(start -> start.sequence(LINAGE).optional(IS).sequence(IDENTIFIER_AND_LITERALS)
595                                .optional(LINES).optionalSubRecognizer(linageSubRecognizer));
596        }
597
598        /**
599         * Returns the LABEL clause recognizer
600         */
601        private RecognizerBase<ECobolParserState> getLabelClauseRecognizer() {
602                RecognizerBase<ECobolParserState> labelSubRecognizer = createRecognizer(start -> {
603                        start.sequence(EnumSet.of(STANDARD, OMITTED));
604                        start.repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER));
605                });
606                return createRecognizer(start -> start.sequence(LABEL, EnumSet.of(RECORD, RECORDS))
607                                .optional(EnumSet.of(IS, ARE)).subRecognizer(labelSubRecognizer));
608        }
609
610        /**
611         * Returns the CONTAINS clause recognizer used in DATA divisions
612         */
613        private RecognizerBase<ECobolParserState> getContainsRecognizer() {
614                RecognizerBase<ECobolParserState> fromToRecognizer = createRecognizer(start -> start.optional(FROM)
615                                .optional(INTEGER_LITERAL).optional(TO, INTEGER_LITERAL).optional(CHARACTERS));
616                RecognizerBase<ECobolParserState> containsSubRecognizer = createRecognizer(
617                                start -> start.optional(IS).sequence(VARYING).optional(IN).optional(SIZE)
618                                                .optionalSubRecognizer(fromToRecognizer).optionalSubRecognizer(getDependingRecognizer()));
619                return createRecognizer(
620                                start -> start.optional(CONTAINS).optional(INTEGER_LITERAL, TO).sequence(INTEGER_LITERAL)
621                                                .optional(EnumSet.of(RECORDS, CHARACTERS)).optionalSubRecognizer(containsSubRecognizer));
622        }
623
624        /**
625         * Create rules for the paragraphs, sections and statements in the procedure
626         * division. These are executable program statements.
627         */
628        private void createRulesForProcedureDivision() {
629                createRuleForDivision(TOP_LEVEL, PROCEDURE);
630                createSimpleVerbRules();
631                createConditionRules();
632                createLoopVerbRules();
633                createRulesForFileIOVerbs();
634                createTryCatchFinallyVerbsRules();
635                createRuleForSortVerb();
636                createRuleForMergeVerb();
637
638                // paragraphs and sections
639                inState(TOP_LEVEL, IN_STATEMENT).sequence(IDENTIFIER, DOT).preCondition(new ParagraphRecognizer())
640                                .createNode(STATEMENT, PARAGRAPH_SUBTYPE_NAME, 0).endNode();
641                inState(TOP_LEVEL, IN_STATEMENT).sequence(IDENTIFIER, SECTION, DOT)
642                                .createNode(STATEMENT, SECTION_SUBTYPE_NAME, 0).endNode();
643                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(EnumSet.of(EXEC, EXECUTE))
644                                .createNode(STATEMENT, SubTypeNames.EXECUTE).skipTo(END_EXEC), null, SIMPLE);
645        }
646
647        /**
648         * Create rule for SORT verb.
649         */
650        private void createRuleForSortVerb() {
651                RecognizerBase<ECobolParserState> withRecognizer = createRecognizer(
652                                start -> start.optional(WITH).sequence(DUPLICATES).optional(IN).optional(ORDER));
653                RecognizerBase<ECobolParserState> collatingRecognizer = createRecognizer(
654                                start -> start.optional(COLLATING).sequence(SEQUENCE).optional(IS)
655                                                .optional(EnumSet.of(STANDARD_1, STANDARD_2, NATIVE, EBCDIC, STRING_LITERAL, INTEGER_LITERAL)));
656                RecognizerBase<ECobolParserState> onRecognizer = createRecognizer(
657                                start -> start.optional(COMMA).optional(ON).sequence(EnumSet.of(ASCENDING, DESCENDING)).optional(KEY)
658                                                .optional(IS).repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER)));
659
660                RecognizerBase<ECobolParserState> sortRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(SORT, IDENTIFIER)
661                                .createNode(STATEMENT, 0).repeatedSubRecognizer(onRecognizer).optionalSubRecognizer(withRecognizer)
662                                .optionalSubRecognizer(collatingRecognizer)
663                                .optionalSubRecognizer(getIOUsingGivingRecognizer(INPUT, USING))
664                                .optionalSubRecognizer(getIOUsingGivingRecognizer(OUTPUT, GIVING));
665                completeRule(sortRecognizer, null, SIMPLE);
666        }
667
668        /**
669         * Returns the INPUT-OUTPUT USING-GIVING sub-recognizer for the SORT verb.
670         */
671        private RecognizerBase<ECobolParserState> getIOUsingGivingRecognizer(ETokenType inputOutputToken,
672                        ETokenType usingGiving) {
673                RecognizerBase<ECobolParserState> throughRecognizer = createRecognizer(
674                                start -> start.sequence(EnumSet.of(THROUGH, THRU), IDENTIFIER));
675                RecognizerBase<ECobolParserState> result = createRecognizer(start -> start.sequence(inputOutputToken, PROCEDURE)
676                                .optional(IS).sequence(IDENTIFIER).optionalSubRecognizer(throughRecognizer));
677                result.sequence(usingGiving).repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER));
678
679                return result;
680        }
681
682        /**
683         * Create rule for MERGE verb.
684         */
685        private void createRuleForMergeVerb() {
686                RecognizerBase<ECobolParserState> onRecognizer = createRecognizer(
687                                start -> start.optional(COMMA).optional(ON).sequence(EnumSet.of(ASCENDING, DESCENDING)).optional(KEY)
688                                                .optional(IS).repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER_AND_LITERALS)));
689                RecognizerBase<ECobolParserState> collatingRecognizer = createRecognizer(
690                                start -> start.optional(COLLATING).sequence(SEQUENCE).optional(IS)
691                                                .sequence(EnumSet.of(STANDARD_1, STANDARD_2, NATIVE, EBCDIC, STRING_LITERAL, INTEGER_LITERAL)));
692                RecognizerBase<ECobolParserState> usingRecognizer = createRecognizer(
693                                start -> start.sequence(USING, IDENTIFIER_AND_STRING_LITERALS)
694                                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER_AND_STRING_LITERALS)));
695                RecognizerBase<ECobolParserState> outputRecognizer = createRecognizer(
696                                start -> start.sequence(OUTPUT, PROCEDURE).optional(IS).sequence(IDENTIFIER_AND_STRING_LITERALS)
697                                                .optional(EnumSet.of(THROUGH, THRU), IDENTIFIER_AND_STRING_LITERALS));
698                outputRecognizer.sequence(GIVING)
699                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER_AND_STRING_LITERALS));
700
701                RecognizerBase<ECobolParserState> mergeRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(MERGE, IDENTIFIER)
702                                .createNode(STATEMENT, 0).repeatedSubRecognizer(onRecognizer).optionalSubRecognizer(collatingRecognizer)
703                                .optionalSubRecognizer(usingRecognizer).optionalSubRecognizer(outputRecognizer);
704                completeRule(mergeRecognizer, null, SIMPLE);
705        }
706
707        /**
708         * Create rules for single statements. As opposed to simple statements, a single
709         * statement has only 2 or maximum of 3 verbs in a sequence.
710         */
711        private void createSingleVerbRules(EShallowEntityType entityType, Object subType, Object... statement) {
712                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(statement).createNode(entityType, subType), null,
713                                SIMPLE);
714        }
715
716        /**
717         * Create rules for compiler directive statements. These may be found in the
718         * normal flow with other executable statements.
719         */
720        private void createRulesForCompilerDirectiveStatements() {
721                createSingleVerbRules(STATEMENT, 0, EJECT);
722                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(BASIS, IDENTIFIER_AND_LITERALS).createNode(STATEMENT, 0),
723                                null, SIMPLE);
724
725                EnumSet<ETokenType> moreCompilerOptionTokens = EnumSet.of(CURRENCY, DATA, DBCS, LIST, OBJECT, SIZE, SOURCE,
726                                SPACE, SQL, TEST, CICS, NAME, NOSOURCE, NOLIST, NOMAP, MAP);
727                RecognizerBase<ECobolParserState> compilerOptionsRecognizer = createRecognizer(start -> {
728                        RecognizerBase<ECobolParserState> comma = start.optional(COMMA);
729                        comma.sequence(COMPILER_OPTIONS);
730                        comma.sequence(moreCompilerOptionTokens);
731                });
732                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(EnumSet.of(CBL, PROCESS)).createNode(STATEMENT, 0)
733                                .repeatedSubRecognizer(compilerOptionsRecognizer), null, SIMPLE);
734                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(CONTROL)
735                                .subRecognizer(getRepeatedSequenceRecognizer(EnumSet.of(SOURCE, NOSOURCE, LIST, NOLIST, MAP, NOMAP)), 1,
736                                                Integer.MAX_VALUE)
737                                .createNode(STATEMENT, 0), null, SIMPLE);
738
739                createRuleForCopyVerb();
740                RecognizerBase<ECobolParserState> replaceRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(REPLACE)
741                                .createNode(STATEMENT, 0);
742                replaceRecognizer.sequence(OFF);
743                replaceRecognizer.repeatedSubRecognizer(getPseudoTextPartialWordRecognizer());
744                completeRule(replaceRecognizer, null, SIMPLE);
745
746                createSingleVerbRules(STATEMENT, 0, INSERT, INTEGER_LITERAL);
747
748                createRulesForMoreCompilerDirectives();
749        }
750
751        /**
752         * Creates rules for more compiler directives
753         */
754        private void createRulesForMoreCompilerDirectives() {
755                RecognizerBase<ECobolParserState> textRecognizer = getRepeatedSequenceRecognizer(IDENTIFIER_AND_LITERALS);
756                createSingleVerbRules(STATEMENT, 0, TITLE, STRING_LITERAL);
757                createSingleVerbRules(STATEMENT, 0, EnumSet.of(SKIP1, SKIP2, SKIP3));
758                createSingleVerbRules(STATEMENT, 0, EnumSet.of(READY, RESET), TRACE);
759
760                RecognizerBase<ECobolParserState> dollarDisplayRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
761                                .sequence(DOLLAR_DISPLAY).createNode(STATEMENT, 0);
762                dollarDisplayRecognizer.sequence(VCS, EQ, STRING_LITERAL);
763                dollarDisplayRecognizer.repeatedSubRecognizer(textRecognizer);
764                completeRule(dollarDisplayRecognizer, null, SIMPLE);
765
766                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(MINUS, INC).createNode(STATEMENT, 1)
767                                .repeatedSubRecognizer(textRecognizer), null, SIMPLE);
768                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(PLUS, PLUS, INCLUDE).createNode(STATEMENT, 2)
769                                .repeatedSubRecognizer(textRecognizer), null, SIMPLE);
770
771                createIfRule(DOLLAR_IF, EnumSet.of(DOLLAR_ELSE), DOLLAR_END, STATEMENT);
772                createElseRule(EnumSet.of(DOLLAR_ELSE), DOLLAR_END, STATEMENT);
773        }
774
775        /**
776         * Create rules for COPY verb.
777         */
778        private void createRuleForCopyVerb() {
779                RecognizerBase<ECobolParserState> ofInRecognizer = createRecognizer(
780                                start -> start.sequence(EnumSet.of(OF, IN), IDENTIFIER_AND_LITERALS));
781                RecognizerBase<ECobolParserState> replacingRecognizer = createRecognizer(
782                                start -> start.sequence(REPLACING).repeatedSubRecognizer(getPseudoTextPartialWordRecognizer()));
783                RecognizerBase<ECobolParserState> copyRecognizer = inState(TOP_LEVEL).sequence(COPY, IDENTIFIER_AND_LITERALS)
784                                .createNode(STATEMENT, 0).optionalSubRecognizer(ofInRecognizer).optional(SUPPRESS)
785                                .optionalSubRecognizer(replacingRecognizer);
786                completeRule(copyRecognizer, null, SIMPLE);
787        }
788
789        /**
790         * Returns recognizer for matching pseudo text and partial word syntax for use
791         * in COPY and REPLACE verbs.
792         */
793        private RecognizerBase<ECobolParserState> getPseudoTextPartialWordRecognizer() {
794                RecognizerBase<ECobolParserState> replacingOperandRecognizer = createRecognizer(start -> {
795                        RecognizerBase<ECobolParserState> prefix = start.optional(COMMA);
796                        prefix.sequence(IDENTIFIER_AND_LITERALS);
797                        prefix.sequence(EQ, EQ).skipTo(EQ, EQ);
798                });
799                return createRecognizer(start -> start.optional(COMMA).optional(EnumSet.of(LEADING, TRAILING))
800                                .subRecognizer(replacingOperandRecognizer).sequence(BY).subRecognizer(replacingOperandRecognizer));
801        }
802
803        /**
804         * Create Rules for File IO verbs
805         */
806        private void createRulesForFileIOVerbs() {
807                createRuleForOpenVerb();
808                createRuleForRewriteVerb();
809                createRuleForDeleteVerb();
810                createRuleForStartVerb();
811                createRuleForCloseVerb();
812                createRuleForWriteVerb();
813                createRuleForReadVerb();
814        }
815
816        /**
817         * Create rule for OPEN verb.
818         */
819        private void createRuleForOpenVerb() {
820                RecognizerBase<ECobolParserState> sharingPhraseSubRecognizer = createRecognizer(start -> {
821                        RecognizerBase<ECobolParserState> prefix = start.optional(WITH);
822                        prefix.sequence(EnumSet.of(ALL, NO)).optional(OTHER);
823                        prefix.sequence(READ, ONLY);
824                });
825                RecognizerBase<ECobolParserState> sharingPhraseRecognizer = createRecognizer(
826                                start -> start.sequence(SHARING).subRecognizer(sharingPhraseSubRecognizer));
827
828                RecognizerBase<ECobolParserState> openSubRecognizer = createRecognizer(start -> {
829                        RecognizerBase<ECobolParserState> prefix = start.optional(COMMA);
830                        prefix.sequence(INPUT).optionalSubRecognizer(sharingPhraseRecognizer)
831                                        .repeatedSubRecognizer(getOpenVerbFileSubrecognizer(true, true));
832                        prefix.sequence(OUTPUT).optionalSubRecognizer(sharingPhraseRecognizer)
833                                        .repeatedSubRecognizer(getOpenVerbFileSubrecognizer(false, true));
834                        prefix.sequence(EnumSet.of(I_O, EXTEND)).optionalSubRecognizer(sharingPhraseRecognizer)
835                                        .repeatedSubRecognizer(getOpenVerbFileSubrecognizer(false, false));
836                });
837
838                RecognizerBase<ECobolParserState> openRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(OPEN)
839                                .createNode(STATEMENT, 0).repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER))
840                                .repeatedSubRecognizer(openSubRecognizer);
841
842                completeRule(openRecognizer, null, SIMPLE);
843        }
844
845        /**
846         * Returns the file sub-recognizer for the OPEN verb.
847         */
848        private RecognizerBase<ECobolParserState> getOpenVerbFileSubrecognizer(boolean includeReserved,
849                        boolean includeNoRewind) {
850                RecognizerBase<ECobolParserState> withRecognizer = createRecognizer(start -> {
851                        RecognizerBase<ECobolParserState> prefix = start.optional(WITH);
852                        if (includeNoRewind) {
853                                prefix.sequence(NO, REWIND);
854                        }
855                        prefix.sequence(LOCK);
856                });
857
858                return createRecognizer(start -> {
859                        RecognizerBase<ECobolParserState> prefix = start.optional(COMMA);
860                        prefix.sequence(IDENTIFIER);
861                        if (includeReserved) {
862                                prefix.sequence(REVERSED);
863                        }
864                        prefix.subRecognizer(withRecognizer);
865                });
866        }
867
868        /**
869         * Create rule for CLOSE verb.
870         */
871        private void createRuleForCloseVerb() {
872                RecognizerBase<ECobolParserState> withRecognizer = createRecognizer(start -> {
873                        RecognizerBase<ECobolParserState> prefix = start.optional(WITH);
874                        prefix.sequence(NO, REWIND);
875                        prefix.sequence(LOCK);
876                        prefix.sequence(DISP);
877                });
878
879                RecognizerBase<ECobolParserState> reelUnitWithRecognizer = createRecognizer(start -> {
880                        RecognizerBase<ECobolParserState> prefix = start.optional(WITH);
881                        prefix.sequence(LOCK);
882                        prefix.optional(NO).sequence(REWIND);
883                });
884
885                RecognizerBase<ECobolParserState> reelUnitRecognizer = createRecognizer(start -> {
886                        RecognizerBase<ECobolParserState> prefix = start.sequence(EnumSet.of(REEL, UNIT));
887                        prefix.optional(FOR).sequence(REMOVAL);
888                        prefix.subRecognizer(reelUnitWithRecognizer);
889                });
890
891                RecognizerBase<ECobolParserState> fileRecognizer = createRecognizer(start -> start.optional(COMMA)
892                                .sequence(IDENTIFIER).optionalSubRecognizer(reelUnitRecognizer).optionalSubRecognizer(withRecognizer));
893                RecognizerBase<ECobolParserState> closeRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(CLOSE)
894                                .createNode(STATEMENT, 0).repeatedSubRecognizer(fileRecognizer);
895                completeRule(closeRecognizer, null, SIMPLE);
896        }
897
898        /**
899         * Create rules for READ verb.
900         */
901        private void createRuleForReadVerb() {
902                RecognizerBase<ECobolParserState> readRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(READ, IDENTIFIER)
903                                .createNode(STATEMENT, 0, 1).skipAny(EnumSet.of(NEXT, PREVIOUS, RECORD));
904                RecognizerBase<ECobolParserState> intoRecognizer = readRecognizer.sequence(INTO, IDENTIFIER)
905                                .skipAny(EnumSet.of(WITH, KEPT, NO, LOCK, WAIT, IGNORE));
906                RecognizerBase<ECobolParserState> readWithKeyRecognizer = readRecognizer
907                                .skipAny(EnumSet.of(WITH, KEPT, NO, LOCK, WAIT, IGNORE)).sequence(KEY).optional(IS)
908                                .sequence(IDENTIFIER);
909                RecognizerBase<ECobolParserState> intoWithKeyRecognizer = intoRecognizer.sequence(KEY).optional(IS)
910                                .sequence(IDENTIFIER);
911                readRecognizer = readRecognizer.skipAny(EnumSet.of(WITH, KEPT, ETokenType.NO, LOCK, WAIT));
912
913                completeReadImperativeRule(intoWithKeyRecognizer);
914                completeReadImperativeRule(readWithKeyRecognizer);
915                completeReadImperativeRule(intoRecognizer);
916                completeReadImperativeRule(readRecognizer);
917                completeRule(intoRecognizer, null, SIMPLE);
918                completeRule(readRecognizer, null, SIMPLE);
919        }
920
921        /**
922         * Complete syntax for read rules
923         */
924        private void completeReadImperativeRule(RecognizerBase<ECobolParserState> recognizer) {
925                RecognizerBase<ECobolParserState> notAtSubRecognizer = createRecognizer(
926                                start -> start.optional(NOT).optional(AT).sequence(END).parseOnce(IN_STATEMENT));
927                completeRuleWithConditionalClause(recognizer, notAtSubRecognizer, END_READ);
928                completeRuleWithConditionalClause(recognizer, getNotInvalidKeyClauseSubRecognizer(), END_READ);
929                completeRule(recognizer, END_READ, SIMPLE);
930        }
931
932        /**
933         * Create rules for WRITE verb.
934         */
935        private void createRuleForWriteVerb() {
936                RecognizerBase<ECobolParserState> writeRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
937                                .sequence(WRITE, EnumSet.of(IDENTIFIER, FLOATING_POINT_LITERAL)).createNode(STATEMENT, 0, 1)
938                                .optional(FROM, IDENTIFIER);
939                RecognizerBase<ECobolParserState> withAdvancingRecognizer = writeRecognizer
940                                .skipAny(EnumSet.of(BEFORE, AFTER, ADVANCING, POSITIONING))
941                                .sequence(EnumSet.of(IDENTIFIER, INTEGER_LITERAL, PAGE, TAB, FORMFEED))
942                                .optional(EnumSet.of(LINE, LINES));
943                RecognizerBase<ECobolParserState> notAtRecognizer = createRecognizer(start -> start.optional(NOT).optional(AT)
944                                .sequence(EnumSet.of(EOP, END_OF_PAGE)).parseOnce(IN_STATEMENT));
945                completeRuleWithConditionalClause(withAdvancingRecognizer, notAtRecognizer, END_WRITE);
946                completeRuleWithConditionalClause(writeRecognizer, getNotInvalidKeyClauseSubRecognizer(), END_WRITE);
947                completeRule(withAdvancingRecognizer, null, SIMPLE);
948        }
949
950        /**
951         * Get a sub-recognizer for the (NOT) INVALID KEY clause recognizer.
952         */
953        private RecognizerBase<ECobolParserState> getNotInvalidKeyClauseSubRecognizer() {
954                return createRecognizer(start -> start.optional(NOT).sequence(INVALID).optional(KEY).parseOnce(IN_STATEMENT));
955        }
956
957        /**
958         * Completes a recognizer rule that ends with one or more conditional clauses
959         */
960        private static void completeRuleWithConditionalClause(RecognizerBase<ECobolParserState> recognizer,
961                        RecognizerBase<ECobolParserState> subRecognizer, ETokenType endTokenType) {
962                RecognizerBase<ECobolParserState> firstRecognizer = recognizer
963                                .preCondition(new BoundedByOppositeClausesRecognizer()).subRecognizer(subRecognizer);
964                RecognizerBase<ECobolParserState> secondRecognizer = firstRecognizer.subRecognizer(subRecognizer);
965                RecognizerBase<ECobolParserState> thirdRecognizer = secondRecognizer.subRecognizer(subRecognizer);
966                thirdRecognizer.preCondition(new ConditionalClauseWithEndScopeRecognizer(endTokenType))
967                                .sequence(EnumSet.of(endTokenType, DOT)).endNode();
968                thirdRecognizer.endNode();
969                secondRecognizer.preCondition(new ConditionalClauseWithEndScopeRecognizer(endTokenType))
970                                .sequence(EnumSet.of(endTokenType, DOT)).endNode();
971                secondRecognizer.endNode();
972                firstRecognizer.preCondition(new ConditionalClauseWithEndScopeRecognizer(endTokenType))
973                                .sequence(EnumSet.of(endTokenType, DOT)).endNode();
974                firstRecognizer.endNode();
975        }
976
977        /**
978         * Completes the rule provided in a recognizer depending on the statement end
979         * type. For SIMPLE verbs, matches endToken. For IMPERATIVE verbs, parses once
980         * IN_STATEMENT while for SCOPE verbs, checks if outside of scope and parses
981         * like IMPERATIVE verb otherwise parses until endToken.
982         */
983        private static void completeRule(RecognizerBase<ECobolParserState> recognizer, ETokenType endToken,
984                        EStatementEndType statementEndType) {
985                switch (statementEndType) {
986                case SIMPLE:
987                        if (endToken != null) {
988                                recognizer.sequence(endToken).endNode();
989                        }
990
991                        // Ensures that statements are properly closed with a DOT (if
992                        // present). This fixes the dot-close-all scope bug as well as
993                        // causes the code to be correctly displayed in the code perspective
994                        // (especially in pretty-print mode).
995                        recognizer.notPreCondition(new OutOfScopeRecognizer()).sequenceBefore(DOT).endNode();
996                        recognizer.preCondition(new OutOfScopeRecognizer()).sequence(DOT).endNode();
997                        recognizer.endNode();
998                        break;
999                case IMPERATIVE:
1000                        recognizer.parseOnce(IN_STATEMENT).endNode();
1001                        break;
1002                case SCOPE:
1003                default:
1004                        // Extra check we are not outside a scope. Fall-through is intended
1005                        recognizer.preCondition(new OutOfScopeRecognizer()).parseOnce(IN_STATEMENT).endNode();
1006                        RecognizerBase<ECobolParserState> finalRecognizer = recognizer.notPreCondition(new OutOfScopeRecognizer())
1007                                        .parseUntil(IN_STATEMENT);
1008                        if (endToken != null) {
1009                                finalRecognizer.sequence(endToken).endNode();
1010                        }
1011
1012                        // Not 'eating' the dot to fix the DOT-close all scope bug
1013                        finalRecognizer.sequenceBefore(DOT).endNode();
1014                }
1015        }
1016
1017        /**
1018         * Create rules for simple (non-block) verbs.
1019         */
1020        private void createSimpleVerbRules() {
1021                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(RAISE).optional(IDENTIFIER).createNode(STATEMENT, 0),
1022                                null, SIMPLE);
1023
1024                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(RELEASE, IDENTIFIER_AND_LITERALS)
1025                                .optional(FROM, IDENTIFIER_AND_LITERALS).createNode(STATEMENT, 0), null, SIMPLE);
1026
1027                completeRule(inState(TOP_LEVEL, IN_STATEMENT)
1028                                .sequence(UNLOCK, IDENTIFIER_AND_STRING_LITERALS, EnumSet.of(RECORD, RECORDS)).createNode(STATEMENT, 0),
1029                                null, SIMPLE);
1030
1031                createRuleForInspectVerb();
1032                createRuleForServiceVerb();
1033                createRuleForInitializeVerb();
1034                createRuleForComputeVerb();
1035                createRuleForAcceptVerb();
1036                createRuleForDisplayVerb();
1037                createRulesForCallVerb();
1038                createRuleForExamineVerb();
1039                createRuleForExhibitVerb();
1040                createRuleForArithmeticVerbs(ADD, END_ADD, TO);
1041                createRuleForArithmeticVerbs(SUBTRACT, END_SUBTRACT, FROM);
1042                createRuleForArithmeticVerbs(MULTIPLY, END_MULTIPLY, BY);
1043                createRuleForArithmeticVerbs(DIVIDE, END_DIVIDE, EnumSet.of(INTO, BY));
1044                createRuleForReturnVerb();
1045                createRuleForOnVerb();
1046                createSingleVerbRules(STATEMENT, 0, CONTINUE);
1047                createSingleVerbRules(STATEMENT, 0, COMMIT);
1048                createSingleVerbRules(STATEMENT, 0, ROLLBACK);
1049                createMoreRulesForSimpleVerbs();
1050        }
1051
1052        /**
1053         * Create rule for INSPECT verb.
1054         */
1055        private void createRuleForInspectVerb() {
1056                RecognizerBase<ECobolParserState> tallyingForRecognizer = createRecognizer(start -> {
1057                        RecognizerBase<ECobolParserState> comma = start.optional(COMMA);
1058                        comma.subRecognizer(getAllLeadingFirstRecognizer());
1059                        comma.subRecognizer(getCharactersRecognizer());
1060                });
1061                RecognizerBase<ECobolParserState> forRecognizer = createRecognizer(
1062                                start -> start.sequence(FOR).repeatedSubRecognizer(tallyingForRecognizer));
1063                RecognizerBase<ECobolParserState> tallyingSubRecognizer = createRecognizer(
1064                                start -> start.optional(COMMA).sequence(IDENTIFIER).repeatedSubRecognizer(forRecognizer));
1065                RecognizerBase<ECobolParserState> tallyingRecognizer = createRecognizer(
1066                                start -> start.sequence(TALLYING).repeatedSubRecognizer(tallyingSubRecognizer));
1067
1068                RecognizerBase<ECobolParserState> replacingSubRecognizer = createRecognizer(start -> {
1069                        RecognizerBase<ECobolParserState> comma = start.optional(COMMA);
1070                        comma.subRecognizer(getCharactersRecognizer());
1071                        comma.subRecognizer(getAllLeadingFirstRecognizer());
1072                });
1073                RecognizerBase<ECobolParserState> replacingRecognizer = createRecognizer(
1074                                start -> start.sequence(REPLACING).repeatedSubRecognizer(replacingSubRecognizer));
1075
1076                RecognizerBase<ECobolParserState> convertingRecognizer = createRecognizer(
1077                                start -> start.sequence(CONVERTING, IDENTIFIER_AND_LITERALS, TO, IDENTIFIER_AND_LITERALS)
1078                                                .optionalSubRecognizer(getBeforeAfterRecognizer()));
1079
1080                RecognizerBase<ECobolParserState> inspectRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
1081                                .sequence(INSPECT, IDENTIFIER).createNode(STATEMENT, 0);
1082                inspectRecognizer.subRecognizer(tallyingRecognizer).optionalSubRecognizer(replacingRecognizer);
1083                inspectRecognizer.subRecognizer(replacingRecognizer);
1084                inspectRecognizer.subRecognizer(convertingRecognizer);
1085
1086                completeRule(inspectRecognizer, null, SIMPLE);
1087        }
1088
1089        /**
1090         * Returns the (ALL, LEADING, FIRST) sub recognizer for the INSPECT verb
1091         */
1092        private RecognizerBase<ECobolParserState> getAllLeadingFirstRecognizer() {
1093                RecognizerBase<ECobolParserState> identifierLiteralRecognizer = createRecognizer(
1094                                start -> start.optional(COMMA).sequence(IDENTIFIER_AND_LITERALS).optional(BY, IDENTIFIER_AND_LITERALS)
1095                                                .repeatedSubRecognizer(getBeforeAfterRecognizer()));
1096
1097                return createRecognizer(start -> start.sequence(EnumSet.of(ALL, LEADING, FIRST))
1098                                .repeatedSubRecognizer(identifierLiteralRecognizer));
1099        }
1100
1101        /**
1102         * Returns the CHARACTERS sub recognizer for the INSPECT verb
1103         */
1104        private RecognizerBase<ECobolParserState> getCharactersRecognizer() {
1105                return createRecognizer(start -> start.sequence(CHARACTERS).optional(BY, IDENTIFIER_AND_LITERALS)
1106                                .repeatedSubRecognizer(getBeforeAfterRecognizer()));
1107        }
1108
1109        /**
1110         * Returns the (BEFORE, AFTER) sub recognizer for the INSPECT verb
1111         */
1112        private RecognizerBase<ECobolParserState> getBeforeAfterRecognizer() {
1113                return createRecognizer(start -> start.optional(COMMA).sequence(EnumSet.of(BEFORE, AFTER)).optional(INITIAL)
1114                                .sequence(IDENTIFIER_AND_LITERALS));
1115        }
1116
1117        /**
1118         * Create rule for the ON verb.
1119         */
1120        private void createRuleForOnVerb() {
1121                RecognizerBase<ECobolParserState> everyRecognizer = createRecognizer(
1122                                start -> start.sequence(AND, EVERY, IDENTIFIER_AND_LITERALS));
1123                RecognizerBase<ECobolParserState> untilRecognizer = createRecognizer(
1124                                start -> start.sequence(UNTIL, IDENTIFIER_AND_LITERALS));
1125                RecognizerBase<ECobolParserState> elseOtherwiseRecognizer = createRecognizer(
1126                                start -> start.sequence(EnumSet.of(ELSE, OTHERWISE)).parseOnce(IN_STATEMENT));
1127                RecognizerBase<ECobolParserState> onRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
1128                                .sequence(ON, IDENTIFIER_AND_LITERALS).createNode(STATEMENT, 0).optionalSubRecognizer(everyRecognizer)
1129                                .optionalSubRecognizer(untilRecognizer).parseOnce(IN_STATEMENT)
1130                                .optionalSubRecognizer(elseOtherwiseRecognizer);
1131                completeRule(onRecognizer, null, SIMPLE);
1132        }
1133
1134        /**
1135         * Create rule for SERVICE verb.
1136         */
1137        private void createRuleForServiceVerb() {
1138                RecognizerBase<ECobolParserState> serviceRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(SERVICE)
1139                                .createNode(STATEMENT, 0);
1140                serviceRecognizer.sequence(RELOAD, IDENTIFIER);
1141                serviceRecognizer.sequence(LABEL);
1142                completeRule(serviceRecognizer, null, SIMPLE);
1143        }
1144
1145        /**
1146         * Create rule for RETURN verb.
1147         */
1148        private void createRuleForReturnVerb() {
1149                RecognizerBase<ECobolParserState> returnRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
1150                                .sequence(RETURN, IDENTIFIER).createNode(STATEMENT, 0).optional(RECORD).optional(INTO, IDENTIFIER);
1151                completeRuleWithConditionalClause(returnRecognizer, createOnAtClauseSubRecognizer(END), END_RETURN);
1152                completeRule(returnRecognizer, END_RETURN, SIMPLE);
1153        }
1154
1155        /**
1156         * Create rule for Arithmetic verbs: ADD, SUBTRACT, MULTIPLY and DIVIDE.
1157         */
1158        private void createRuleForArithmeticVerbs(ETokenType startVerb, ETokenType endVerb, Object... qualifyVerbs) {
1159                RecognizerBase<ECobolParserState> identifierRoundedRecognizer = createRecognizer(
1160                                start -> start.optional(COMMA).sequence(IDENTIFIER_AND_NUMERIC_LITERALS).optional(ROUNDED));
1161                RecognizerBase<ECobolParserState> givingRecognizer = createRecognizer(
1162                                start -> start.sequence(GIVING).repeatedSubRecognizer(identifierRoundedRecognizer));
1163                RecognizerBase<ECobolParserState> arithmeticRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(startVerb)
1164                                .createNode(STATEMENT, 0).optional(EnumSet.of(CORRESPONDING, CORR))
1165                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER_AND_NUMERIC_LITERALS))
1166                                .optional(qualifyVerbs).repeatedSubRecognizer(identifierRoundedRecognizer)
1167                                .optionalSubRecognizer(givingRecognizer).optional(REMAINDER, IDENTIFIER);
1168
1169                completeRuleWithConditionalClause(arithmeticRecognizer, createOnAtClauseSubRecognizer(SIZE, ERROR), endVerb);
1170                completeRule(arithmeticRecognizer, endVerb, SIMPLE);
1171        }
1172
1173        /**
1174         * Create rule for EXHIBIT verb.
1175         */
1176        private void createRuleForExhibitVerb() {
1177                RecognizerBase<ECobolParserState> exhibitRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(EXHIBIT)
1178                                .createNode(STATEMENT, 0);
1179                exhibitRecognizer.sequence(NAMED).repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER_AND_LITERALS));
1180                exhibitRecognizer.sequence(CHANGED).optional(NAMED)
1181                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER_AND_LITERALS));
1182                exhibitRecognizer.repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER_AND_LITERALS));
1183                completeRule(exhibitRecognizer, null, SIMPLE);
1184        }
1185
1186        /**
1187         * Create rule for EXAMINE verb.
1188         */
1189        private void createRuleForExamineVerb() {
1190                RecognizerBase<ECobolParserState> tallyingRecognizer = createRecognizer(start -> {
1191                        RecognizerBase<ECobolParserState> prefix = start.sequence(TALLYING);
1192                        prefix.sequence(UNTIL, FIRST, IDENTIFIER_AND_LITERALS).optional(REPLACING, BY, IDENTIFIER_AND_LITERALS);
1193                        prefix.sequence(EnumSet.of(ALL, LEADING), IDENTIFIER_AND_LITERALS).optional(REPLACING, BY,
1194                                        IDENTIFIER_AND_STRING_LITERALS);
1195                });
1196
1197                RecognizerBase<ECobolParserState> replacingRecognizer = createRecognizer(start -> {
1198                        RecognizerBase<ECobolParserState> prefix = start.sequence(REPLACING);
1199                        prefix.sequence(UNTIL, FIRST, IDENTIFIER_AND_LITERALS, BY, IDENTIFIER_AND_LITERALS);
1200                        prefix.sequence(EnumSet.of(ALL, LEADING, FIRST), IDENTIFIER_AND_LITERALS, BY, IDENTIFIER_AND_LITERALS);
1201                });
1202
1203                RecognizerBase<ECobolParserState> examineRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
1204                                .sequence(EXAMINE, IDENTIFIER).createNode(STATEMENT, 0);
1205                examineRecognizer.subRecognizer(tallyingRecognizer);
1206                examineRecognizer.subRecognizer(replacingRecognizer);
1207
1208                completeRule(examineRecognizer, null, SIMPLE);
1209        }
1210
1211        /**
1212         * Create rule for REWRITE verb.
1213         */
1214        private void createRuleForRewriteVerb() {
1215                RecognizerBase<ECobolParserState> rewriteRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
1216                                .sequence(REWRITE, IDENTIFIER).createNode(STATEMENT, 0, 1).optional(FROM, IDENTIFIER);
1217                completeRuleWithConditionalClause(rewriteRecognizer, getNotInvalidKeyClauseSubRecognizer(), END_REWRITE);
1218                completeRule(rewriteRecognizer, END_REWRITE, SIMPLE);
1219        }
1220
1221        /**
1222         * Create rule for DELETE verb
1223         */
1224        private void createRuleForDeleteVerb() {
1225                RecognizerBase<ECobolParserState> baseDeleteRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(DELETE);
1226
1227                completeRule(baseDeleteRecognizer.sequence(FILE).createNode(STATEMENT, new Region(0, 1))
1228                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER)), null, SIMPLE);
1229
1230                RecognizerBase<ECobolParserState> withRecordRecognizer = baseDeleteRecognizer.sequence(IDENTIFIER)
1231                                .createNode(STATEMENT, 0, 1).optional(RECORD);
1232                completeRuleWithConditionalClause(withRecordRecognizer, getNotInvalidKeyClauseSubRecognizer(), END_DELETE);
1233                completeRule(withRecordRecognizer, END_DELETE, SIMPLE);
1234        }
1235
1236        /**
1237         * Create rules for INITIALIZE verb
1238         */
1239        private void createRuleForInitializeVerb() {
1240                EnumSet<ETokenType> categoryNameTokens = EnumSet.of(ALPHABETIC, ALPHANUMERIC, ALPHANUMERIC_EDITED, DATA_POINTER,
1241                                DBCS, NATIONAL, NATIONAL_EDITED, NUMERIC, NUMERIC_EDITED, OBJECT_REFERENCE, PROGRAM_POINTER);
1242                EnumSet<ETokenType> categoryNamesWithALLToken = EnumSet.copyOf(categoryNameTokens);
1243                categoryNamesWithALLToken.add(ALL);
1244                RecognizerBase<ECobolParserState> byRecognizer = createRecognizer(start -> start.optional(COMMA)
1245                                .optional(categoryNameTokens).optional(DATA).sequence(BY, IDENTIFIER_AND_LITERALS));
1246                RecognizerBase<ECobolParserState> replacingRecognizer = createRecognizer(
1247                                start -> start.optional(THEN).sequence(REPLACING).repeatedSubRecognizer(byRecognizer));
1248                RecognizerBase<ECobolParserState> toValueRecognizer = createRecognizer(
1249                                start -> start.sequence(categoryNamesWithALLToken).optional(TO).sequence(IDENTIFIER));
1250                RecognizerBase<ECobolParserState> identifierRecognizer = createRecognizer(
1251                                start -> start.optional(COMMA).sequence(IDENTIFIER));
1252                RecognizerBase<ECobolParserState> initializeRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(INITIALIZE)
1253                                .createNode(STATEMENT, 0).repeatedSubRecognizer(identifierRecognizer).optional(WITH).optional(FILLER)
1254                                .optionalSubRecognizer(toValueRecognizer).optionalSubRecognizer(replacingRecognizer)
1255                                .optional(THEN, TO, DEFAULT);
1256
1257                completeRule(initializeRecognizer, null, SIMPLE);
1258        }
1259
1260        /**
1261         * Create rules for COMPUTE verb.
1262         */
1263        private void createRuleForComputeVerb() {
1264                RecognizerBase<ECobolParserState> identifierRoundedRecognizer = createRecognizer(
1265                                start -> start.optional(COMMA).sequence(IDENTIFIER).optional(ROUNDED));
1266                RecognizerBase<ECobolParserState> computeRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(COMPUTE)
1267                                .createNode(STATEMENT, 0).repeatedSubRecognizer(identifierRoundedRecognizer)
1268                                .sequence(EnumSet.of(EQ, EQUAL)).subRecognizer(getArithmeticExpressionRecognizer(false));
1269                completeRuleWithConditionalClause(computeRecognizer, createOnAtClauseSubRecognizer(SIZE, ERROR), END_COMPUTE);
1270                completeRule(computeRecognizer, END_COMPUTE, SIMPLE);
1271        }
1272
1273        /**
1274         * Returns the AT sub-recognizer used by the ACCEPT and DISPLAY verbs
1275         */
1276        private RecognizerBase<ECobolParserState> getAtRecognizer() {
1277                RecognizerBase<ECobolParserState> columnRecognizer = createRecognizer(start -> start
1278                                .sequence(EnumSet.of(COLUMN, COL)).optional(NUMBER).sequence(IDENTIFIER_AND_NUMERIC_LITERALS));
1279
1280                return createRecognizer(start -> {
1281                        RecognizerBase<ECobolParserState> prefix = start.optional(AT);
1282                        prefix.sequence(LINE).optional(NUMBER).sequence(IDENTIFIER_AND_NUMERIC_LITERALS)
1283                                        .optionalSubRecognizer(columnRecognizer);
1284                        prefix.sequence(IDENTIFIER_AND_NUMERIC_LITERALS);
1285                });
1286        }
1287
1288        /**
1289         * Return the MODE sub-recognizer for the ACCEPT and DISPLAY verbs.
1290         */
1291        private RecognizerBase<ECobolParserState> getModeRecognizer() {
1292                return createRecognizer(start -> start.sequence(MODE).optional(IS).sequence(BLOCK));
1293        }
1294
1295        /**
1296         * Returns the WITH sub-recognizer for ACCEPT DISPLAY verbs.
1297         */
1298        private RecognizerBase<ECobolParserState> getWithRecognizer(boolean includeTokensForAcceptVerb) {
1299                RecognizerBase<ECobolParserState> withSubRecognizer = createRecognizer(start -> {
1300                        RecognizerBase<ECobolParserState> prefix = start.optional(COMMA);
1301                        prefix.sequence(EnumSet.of(BELL, BEEP, BLINK, GRID, HIGHLIGHT, LOWLIGHT, LEFTLINE, OVERLINE, REVERSE_VIDEO,
1302                                        UNDERLINE));
1303
1304                        if (includeTokensForAcceptVerb) {
1305                                prefix.sequence(EnumSet.of(AUTO, AUTO_SKIP, FULL, LENGTH_CHECK, REQUIRED, EMPTY_CHECK, SECURE, NO_ECHO,
1306                                                LEFT_JUSTIFY, RIGHT_JUSTIFY, SPACE_FILL, TRAILING_SIGN, UPDATE, UPPER, LOWER, ZERO_FILL));
1307                                prefix.sequence(PROMPT).optional(CHARACTER).optional(IS).optional(IDENTIFIER_AND_LITERALS);
1308                                prefix.sequence(EnumSet.of(TIMEOUT, TIME_OUT), AFTER, IDENTIFIER_AND_LITERALS);
1309                        }
1310
1311                        prefix.sequence(ERASE, EnumSet.of(EOL, EOS));
1312                        prefix.sequence(
1313                                        EnumSet.of(SIZE, FOREGROUND_COLOR, FOREGROUND_COLOUR, BACKGROUND_COLOR, BACKGROUND_COLOUR, CONTROL))
1314                                        .optional(IS).sequence(IDENTIFIER_AND_LITERALS);
1315                        prefix.sequence(BLANK, EnumSet.of(SCREEN, LINE));
1316                        prefix.sequence(NO, ADVANCING);
1317                });
1318
1319                return createRecognizer(start -> start.sequence(WITH).repeatedSubRecognizer(withSubRecognizer));
1320        }
1321
1322        /**
1323         * Create rule for ACCEPT verb.
1324         */
1325        private void createRuleForAcceptVerb() {
1326                RecognizerBase<ECobolParserState> fromRecognizer = createRecognizer(start -> {
1327                        RecognizerBase<ECobolParserState> prefix = start.sequence(FROM);
1328                        prefix.sequence(EnumSet.of(IDENTIFIER, DAY_OF_WEEK, TIME, CRT));
1329                        prefix.sequence(EnumSet.of(DATE, DAY)).optional(INTEGER_LITERAL);
1330                        prefix.sequence(LINE, NUMBER);
1331                        prefix.sequence(USER, NAME);
1332                        prefix.sequence(ESCAPE, KEY);
1333                        prefix.sequence(EXCEPTION, STATUS);
1334                });
1335
1336                RecognizerBase<ECobolParserState> acceptRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
1337                                .sequence(ACCEPT, IDENTIFIER).createNode(STATEMENT, 0).optionalSubRecognizer(getAtRecognizer())
1338                                .optionalSubRecognizer(fromRecognizer).optionalSubRecognizer(getModeRecognizer())
1339                                .optionalSubRecognizer(getWithRecognizer(true));
1340
1341                completeRuleWithConditionalClause(acceptRecognizer,
1342                                createOnAtClauseSubRecognizer(EnumSet.of(EXCEPTION, ESCAPE)), END_ACCEPT);
1343                completeRule(acceptRecognizer, END_ACCEPT, SIMPLE);
1344        }
1345
1346        /**
1347         * Create rules for DISPLAY verb.
1348         */
1349        private void createRuleForDisplayVerb() {
1350                RecognizerBase<ECobolParserState> uponRecognizer = createRecognizer(
1351                                start -> start.sequence(UPON, EnumSet.of(CRT, CRT_UNDER, IDENTIFIER)));
1352
1353                RecognizerBase<ECobolParserState> displaySubRecognizer = createRecognizer(start -> start.optional(COMMA)
1354                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER_AND_LITERALS))
1355                                .optionalSubRecognizer(getAtRecognizer()).optionalSubRecognizer(uponRecognizer)
1356                                .optionalSubRecognizer(getModeRecognizer()).optionalSubRecognizer(getWithRecognizer(false)));
1357                RecognizerBase<ECobolParserState> displayRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(DISPLAY)
1358                                .createNode(STATEMENT, 0).repeatedSubRecognizer(displaySubRecognizer);
1359
1360                completeRuleWithConditionalClause(displayRecognizer, createOnAtClauseSubRecognizer(EXCEPTION), END_DISPLAY);
1361                completeRule(displayRecognizer, END_DISPLAY, SIMPLE);
1362        }
1363
1364        /**
1365         * Create rule for XML Verb.
1366         */
1367        private void createRuleForXMLVerb() {
1368                RecognizerBase<ECobolParserState> xmlRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
1369                                .sequence(XML, EnumSet.of(GENERATE, PARSE), IDENTIFIER).createNode(STATEMENT, 0, 1);
1370
1371                RecognizerBase<ECobolParserState> countRecognizer = createRecognizer(
1372                                start -> start.sequence(COUNT).optional(IN).sequence(IDENTIFIER));
1373                RecognizerBase<ECobolParserState> generateRecognizer = xmlRecognizer.sequence(FROM, IDENTIFIER)
1374                                .optionalSubRecognizer(countRecognizer);
1375                RecognizerBase<ECobolParserState> parseRecognizer = xmlRecognizer.sequence(PROCESSING, PROCEDURE).optional(IS)
1376                                .sequence(IDENTIFIER).optional(EnumSet.of(THROUGH, THRU), IDENTIFIER);
1377
1378                completeRuleWithConditionalClause(generateRecognizer, createOnAtClauseSubRecognizer(EXCEPTION), END_XML);
1379                completeRule(generateRecognizer, END_XML, SIMPLE);
1380                completeRuleWithConditionalClause(parseRecognizer, createOnAtClauseSubRecognizer(EXCEPTION), END_XML);
1381                completeRule(parseRecognizer, END_XML, SIMPLE);
1382        }
1383
1384        /**
1385         * Create rule for INVOKE verb.
1386         */
1387        private void createRuleForInvokeVerb() {
1388                RecognizerBase<ECobolParserState> identifierRecognizer = createRecognizer(start -> {
1389                        RecognizerBase<ECobolParserState> prefix = start.sequence(IDENTIFIER);
1390                        prefix.sequence(AS, EnumSet.of(OBJECT, IDENTIFIER), IDENTIFIER_AND_LITERALS);
1391                        prefix.sequence(IDENTIFIER_AND_LITERALS);
1392                });
1393                RecognizerBase<ECobolParserState> invokeRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(INVOKE)
1394                                .createNode(STATEMENT, 0).subRecognizer(identifierRecognizer)
1395                                .optionalSubRecognizer(getUsingSubrecognizer()).optionalSubRecognizer(getGivingReturningRecognizer());
1396
1397                completeRule(invokeRecognizer, null, SIMPLE);
1398        }
1399
1400        /**
1401         * Create rule for CHAIN verb.
1402         */
1403        private void createRuleForChainVerb() {
1404                RecognizerBase<ECobolParserState> chainRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
1405                                .sequence(CHAIN, IDENTIFIER_AND_LITERALS).createNode(STATEMENT, 0)
1406                                .optionalSubRecognizer(getUsingSubrecognizer());
1407                completeRule(chainRecognizer, END_CHAIN, SIMPLE);
1408        }
1409
1410        /**
1411         * Create rules for CALL verb
1412         */
1413        private void createRulesForCallVerb() {
1414                RecognizerBase<ECobolParserState> identifierNumeralsRecognizer = createRecognizer(start -> start
1415                                .sequence(EnumSet.of(IDENTIFIER, STRING_LITERAL), AS, EnumSet.of(NESTED, IDENTIFIER, STRING_LITERAL)));
1416                identifierNumeralsRecognizer.sequence(IDENTIFIER_AND_LITERALS, IDENTIFIER_AND_LITERALS);
1417                identifierNumeralsRecognizer.sequence(EnumSet.of(NESTED, IDENTIFIER, STRING_LITERAL));
1418                identifierNumeralsRecognizer.sequence(COMPILER_OPTIONS);
1419                RecognizerBase<ECobolParserState> baseCallRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(CALL)
1420                                .createNode(STATEMENT, 0).subRecognizer(identifierNumeralsRecognizer);
1421                baseCallRecognizer = baseCallRecognizer.optionalSubRecognizer(getUsingSubrecognizer())
1422                                .optionalSubRecognizer(getGivingReturningRecognizer());
1423                completeCallRules(baseCallRecognizer);
1424        }
1425
1426        /**
1427         * Returns the GIVING-RETURNING sub recognizer used by the CALL and INVOKE
1428         * verbs.
1429         */
1430        private RecognizerBase<ECobolParserState> getGivingReturningRecognizer() {
1431                return createRecognizer(start -> {
1432                        RecognizerBase<ECobolParserState> prefix = start.sequence(EnumSet.of(GIVING, RETURNING));
1433                        prefix.sequence(ADDRESS, OF, IDENTIFIER);
1434                        prefix.optional(INTO).sequence(IDENTIFIER);
1435                });
1436        }
1437
1438        /**
1439         * Returns the USING recognizer for the CALL, CHAIN AND INVOKE verbs.
1440         */
1441        private RecognizerBase<ECobolParserState> getUsingSubrecognizer() {
1442                RecognizerBase<ECobolParserState> repeatedUsingSubRecognizer = createRecognizer(start -> {
1443                        RecognizerBase<ECobolParserState> prefix = start.optional(COMMA);
1444                        prefix.sequence(EnumSet.of(ADDRESS, LENGTH), OF, IDENTIFIER);
1445                        prefix.sequence(INTEGER_LITERAL, SIZE).optional(IS).sequence(INTEGER_LITERAL);
1446                        prefix.sequence(EnumSet.of(STRING_LITERAL, OMITTED));
1447                        prefix.subRecognizer(getArithmeticExpressionRecognizer(true));
1448                });
1449                RecognizerBase<ECobolParserState> usingRecognizer = createRecognizer(
1450                                start -> start.optional(COMMA).optional(BY).optional(EnumSet.of(REFERENCE, CONTENT, VALUE))
1451                                                .optional(EnumSet.of(ADDRESS, LENGTH), OF).repeatedSubRecognizer(repeatedUsingSubRecognizer));
1452
1453                return createRecognizer(start -> start.sequence(USING).repeatedSubRecognizer(usingRecognizer));
1454        }
1455
1456        /**
1457         * Complete a set of CALL recognizer rules.
1458         */
1459        private void completeCallRules(RecognizerBase<ECobolParserState> recognizer) {
1460                completeRuleWithConditionalClause(recognizer, createOnAtClauseSubRecognizer(EnumSet.of(EXCEPTION, OVERFLOW)),
1461                                END_CALL);
1462                completeRule(recognizer, END_CALL, SIMPLE);
1463        }
1464
1465        /**
1466         * Creates conditional clause sub-recognizer for statements like OVERFLOW,
1467         * EXCEPTION and END, statements which conform to the syntax (NOT) ON|AT
1468         * STATEMENT IMPERATIVE_STATEMENT.
1469         */
1470        private RecognizerBase<ECobolParserState> createOnAtClauseSubRecognizer(Object... statement) {
1471                return createRecognizer(
1472                                start -> start.optional(NOT).optional(EnumSet.of(ON, AT)).sequence(statement).parseOnce(IN_STATEMENT));
1473        }
1474
1475        /**
1476         * Create rules for simple (non-block) verbs.
1477         */
1478        private void createMoreRulesForSimpleVerbs() {
1479                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(NEXT, SENTENCE).createNode(STATEMENT, new Region(0, 1)),
1480                                null, SIMPLE);
1481
1482                inState(TOP_LEVEL, IN_STATEMENT).sequence(INSPECT, IDENTIFIER, EnumSet.of(TALLYING, REPLACING))
1483                                .createNode(STATEMENT, 0).endNode();
1484
1485                createRuleForAlterVerb();
1486                createRuleForMoveVerb();
1487                createRuleForStopVerb();
1488                createRuleForSetVerb();
1489                createRuleForExitVerb();
1490                createRuleForStringVerb();
1491                createRuleForUnstringVerb();
1492                createRuleForGobackVerb();
1493                createRuleForTransformVerb();
1494                createRuleForUseVerb();
1495                createRuleForWaitVerb();
1496                createRuleForChainVerb();
1497                createRuleForInvokeVerb();
1498                createRuleForXMLVerb();
1499                createRuleForEntryVerb();
1500
1501                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(ENTER).createNode(STATEMENT, 0)
1502                                .sequence(EnumSet.of(IDENTIFIER, STRING_LITERAL)).optional(EnumSet.of(IDENTIFIER, STRING_LITERAL)),
1503                                null, SIMPLE);
1504
1505                inState(TOP_LEVEL, IN_STATEMENT).sequence(LIST).createNode(STATEMENT, 0).skipTo(SEMICOLON).endNode();
1506
1507                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(CANCEL).createNode(STATEMENT, 0)
1508                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER_AND_LITERALS)), null, SIMPLE);
1509        }
1510
1511        /**
1512         * Create rule for ENTRY verb.
1513         */
1514        private void createRuleForEntryVerb() {
1515                RecognizerBase<ECobolParserState> givingReturningRecognizer = createRecognizer(
1516                                start -> start.sequence(EnumSet.of(GIVING, RETURNING), IDENTIFIER));
1517                RecognizerBase<ECobolParserState> repeatedRecognizer = createRecognizer(
1518                                start -> start.sequence(REPEATED).optional(INTEGER_LITERAL, TO, INTEGER_LITERAL));
1519                RecognizerBase<ECobolParserState> referenceValueRecognizer = createRecognizer(start -> {
1520                        RecognizerBase<ECobolParserState> prefix = start.optional(COMMA);
1521                        prefix.sequence(IDENTIFIER).optional(DELIMITED).optional(BY, SIZE);
1522                        prefix.sequence(ANY);
1523                });
1524                RecognizerBase<ECobolParserState> usingSubRecognizer = createRecognizer(start -> start.optional(BY)
1525                                .sequence(EnumSet.of(REFERENCE, VALUE)).repeatedSubRecognizer(referenceValueRecognizer));
1526                RecognizerBase<ECobolParserState> usingRecognizer = createRecognizer(start -> start.sequence(USING)
1527                                .repeatedSubRecognizer(usingSubRecognizer).optionalSubRecognizer(repeatedRecognizer));
1528                RecognizerBase<ECobolParserState> entryRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
1529                                .sequence(ENTRY, STRING_LITERAL).createNode(STATEMENT, 0).optional(IDENTIFIER)
1530                                .optionalSubRecognizer(usingRecognizer).optionalSubRecognizer(givingReturningRecognizer);
1531
1532                completeRule(entryRecognizer, null, SIMPLE);
1533        }
1534
1535        /**
1536         * Create rule for START verb.
1537         */
1538        private void createRuleForStartVerb() {
1539                RecognizerBase<ECobolParserState> contentRecognizer = createRecognizer(
1540                                start -> start.optional(BY).sequence(CONTENT));
1541                RecognizerBase<ECobolParserState> usingRecognizer = createRecognizer(
1542                                start -> start.sequence(USING).optionalSubRecognizer(contentRecognizer).sequence(IDENTIFIER));
1543                RecognizerBase<ECobolParserState> returningRecognizer = createRecognizer(
1544                                start -> start.sequence(RETURNING).optional(INTO).sequence(IDENTIFIER));
1545                RecognizerBase<ECobolParserState> identifiedRecognizer = createRecognizer(
1546                                start -> start.sequence(IDENTIFIED).optional(BY).sequence(IDENTIFIER_AND_LITERALS));
1547                RecognizerBase<ECobolParserState> statusRecognizer = createRecognizer(
1548                                start -> start.sequence(STATUS).optional(IS).sequence(IDENTIFIER));
1549
1550                RecognizerBase<ECobolParserState> withRecognizer = createRecognizer(
1551                                start -> start.optional(WITH).sequence(SIZE, IDENTIFIER_AND_LITERALS));
1552                RecognizerBase<ECobolParserState> keyConditionalRecognizer = createRecognizer(
1553                                start -> start.sequence(KEY).skipTo(IDENTIFIER).optionalSubRecognizer(withRecognizer));
1554
1555                // By syntax, I should break keyConditionalRecognizer as an alternate
1556                // and separate path with the getNotInvalidKeyClauseSubRecognizer() but
1557                // it does not work.
1558                RecognizerBase<ECobolParserState> startRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(START)
1559                                .createNode(STATEMENT, 0).repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER_AND_LITERALS))
1560                                .optionalSubRecognizer(usingRecognizer).optionalSubRecognizer(returningRecognizer)
1561                                .optionalSubRecognizer(identifiedRecognizer).optionalSubRecognizer(statusRecognizer)
1562                                .optionalSubRecognizer(keyConditionalRecognizer);
1563
1564                completeRuleWithConditionalClause(startRecognizer, getNotInvalidKeyClauseSubRecognizer(), END_START);
1565                completeRuleWithConditionalClause(startRecognizer, createOnAtClauseSubRecognizer(EXCEPTION), END_START);
1566                completeRule(startRecognizer, END_START, SIMPLE);
1567        }
1568
1569        /**
1570         * Create rule for WAIT verb.
1571         */
1572        private void createRuleForWaitVerb() {
1573                RecognizerBase<ECobolParserState> returningRecognizer = createRecognizer(
1574                                start -> start.sequence(RETURNING).optional(INTO).sequence(IDENTIFIER));
1575                RecognizerBase<ECobolParserState> statusRecognizer = createRecognizer(
1576                                start -> start.sequence(STATUS).optional(IS).sequence(IDENTIFIER));
1577                RecognizerBase<ECobolParserState> waitRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(WAIT)
1578                                .createNode(STATEMENT, 0).optional(FOR).sequence(IDENTIFIER).optionalSubRecognizer(returningRecognizer)
1579                                .optionalSubRecognizer(statusRecognizer);
1580                completeRuleWithConditionalClause(waitRecognizer, createOnAtClauseSubRecognizer(EXCEPTION), END_WAIT);
1581                completeRule(waitRecognizer, END_WAIT, SIMPLE);
1582        }
1583
1584        /**
1585         * Create rule for USE verb.
1586         */
1587        private void createRuleForUseVerb() {
1588                RecognizerBase<ECobolParserState> procedureRecognizer = createRecognizer(start -> {
1589                        RecognizerBase<ECobolParserState> onRecognizer = start.optional(ON);
1590                        onRecognizer.sequence(EnumSet.of(INPUT, OUTPUT, I_O, EXTEND));
1591                        onRecognizer.repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER_AND_STRING_LITERALS));
1592                });
1593                RecognizerBase<ECobolParserState> givingRecognizer = createRecognizer(start -> start
1594                                .sequence(GIVING, IDENTIFIER_AND_STRING_LITERALS).optional(IDENTIFIER_AND_STRING_LITERALS));
1595                RecognizerBase<ECobolParserState> useRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(USE)
1596                                .createNode(STATEMENT, 0).optional(GLOBAL).sequence(AFTER).optional(STANDARD);
1597                useRecognizer.sequence(EnumSet.of(EXCEPTION, ERROR), PROCEDURE).subRecognizer(procedureRecognizer)
1598                                .optionalSubRecognizer(givingRecognizer);
1599                useRecognizer.sequence(EnumSet.of(BEGINNING, ENDING), EnumSet.of(FILE, REEL, UNIT), LABEL, PROCEDURE)
1600                                .subRecognizer(procedureRecognizer);
1601                completeRule(useRecognizer, null, SIMPLE);
1602        }
1603
1604        /**
1605         * Create rule for TRANSFORM verb.
1606         */
1607        private void createRuleForTransformVerb() {
1608                RecognizerBase<ECobolParserState> transformRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
1609                                .sequence(TRANSFORM, IDENTIFIER).createNode(STATEMENT, 0).optional(CHARACTERS);
1610                transformRecognizer.sequence(FROM, IDENTIFIER_AND_STRING_LITERALS, TO, IDENTIFIER_AND_STRING_LITERALS);
1611                completeRule(transformRecognizer, null, SIMPLE);
1612        }
1613
1614        /**
1615         * Create rule for GOBACK verb
1616         */
1617        private void createRuleForGobackVerb() {
1618                RecognizerBase<ECobolParserState> givingReturningRecognizer = createRecognizer(start -> {
1619                        RecognizerBase<ECobolParserState> prefix = start.sequence(EnumSet.of(GIVING, RETURNING));
1620                        prefix.optional(ADDRESS, OF).sequence(IDENTIFIER);
1621                        prefix.sequence(INTEGER_LITERAL);
1622                });
1623                RecognizerBase<ECobolParserState> goBackRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(GOBACK)
1624                                .createNode(STATEMENT, 0);
1625                goBackRecognizer.optionalSubRecognizer(givingReturningRecognizer);
1626                completeRule(goBackRecognizer, null, SIMPLE);
1627        }
1628
1629        /**
1630         * Create rules for the ALTER verb
1631         */
1632        private void createRuleForAlterVerb() {
1633                RecognizerBase<ECobolParserState> repeatedRecognizer = createRecognizer(
1634                                start -> start.optional(COMMA).sequence(IDENTIFIER, TO).optional(PROCEED, TO).sequence(IDENTIFIER));
1635                RecognizerBase<ECobolParserState> alterRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(ALTER)
1636                                .createNode(STATEMENT, 0).repeatedSubRecognizer(repeatedRecognizer);
1637                completeRule(alterRecognizer, null, SIMPLE);
1638        }
1639
1640        /**
1641         * Create rule for the MOVE verb
1642         */
1643        private void createRuleForMoveVerb() {
1644                RecognizerBase<ECobolParserState> moveRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(MOVE)
1645                                .createNode(STATEMENT, 0).skipTo(TO).sequence(VALID_VARIABLE_NAMES).skipNested(LPAREN, RPAREN);
1646                RecognizerBase<ECobolParserState> rightOperandRecognizer = createRecognizer(
1647                                start -> start.optional(COMMA).sequence(IDENTIFIER).skipNested(LPAREN, RPAREN));
1648                moveRecognizer.repeatedSubRecognizer(rightOperandRecognizer);
1649                completeRule(moveRecognizer, null, SIMPLE);
1650        }
1651
1652        /**
1653         * Create rules for STOP verb.
1654         */
1655        private void createRuleForStopVerb() {
1656                RecognizerBase<ECobolParserState> stopRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(STOP)
1657                                .createNode(STATEMENT, 0);
1658                RecognizerBase<ECobolParserState> withLiteralRecognizer = stopRecognizer
1659                                .sequence(EnumSet.of(STRING_LITERAL, INTEGER_LITERAL));
1660                RecognizerBase<ECobolParserState> baseRunRecognizer = stopRecognizer.sequence(RUN);
1661                RecognizerBase<ECobolParserState> runRecognizer = baseRunRecognizer.sequence(EnumSet.of(GIVING, RETURNING));
1662                runRecognizer.sequence(ADDRESS, OF, IDENTIFIER);
1663                runRecognizer.sequence(INTEGER_LITERAL, SIZE).optional(IS).sequence(INTEGER_LITERAL);
1664                runRecognizer.sequence(EnumSet.of(INTEGER_LITERAL, IDENTIFIER));
1665
1666                completeRule(runRecognizer, null, SIMPLE);
1667                completeRule(baseRunRecognizer, null, SIMPLE);
1668                completeRule(withLiteralRecognizer, null, SIMPLE);
1669        }
1670
1671        /**
1672         * Create rules for the STRING verb
1673         */
1674        private void createRuleForStringVerb() {
1675                RecognizerBase<ECobolParserState> leftOperandRecognizer = createRecognizer(
1676                                start -> start.optional(COMMA).sequence(IDENTIFIER_AND_LITERALS));
1677                EnumSet<ETokenType> rightOperandTokens = EnumSet.copyOf(IDENTIFIER_AND_LITERALS);
1678                rightOperandTokens.add(SIZE);
1679                RecognizerBase<ECobolParserState> stringRecognizer = createRecognizer(
1680                                start -> start.repeatedSubRecognizer(leftOperandRecognizer).sequence(DELIMITED).optional(BY)
1681                                                .sequence(rightOperandTokens));
1682                RecognizerBase<ECobolParserState> baseStringRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(STRING)
1683                                .createNode(STATEMENT, 0).repeatedSubRecognizer(stringRecognizer).sequence(INTO, IDENTIFIER)
1684                                .optional(WITH).optional(POINTER, IDENTIFIER);
1685                completeRuleWithConditionalClause(baseStringRecognizer, createOnAtClauseSubRecognizer(OVERFLOW), END_STRING);
1686                completeRule(baseStringRecognizer, END_STRING, SIMPLE);
1687        }
1688
1689        /**
1690         * Create rules for the UNSTRING verb
1691         */
1692        private void createRuleForUnstringVerb() {
1693                RecognizerBase<ECobolParserState> orAllRecognizer = createRecognizer(
1694                                start -> start.optional(COMMA).sequence(OR).optional(ALL).sequence(IDENTIFIER_AND_LITERALS));
1695                RecognizerBase<ECobolParserState> delimitedByRecognizer = createRecognizer(start -> start.sequence(DELIMITED)
1696                                .skipAny(EnumSet.of(BY, ALL)).sequence(IDENTIFIER_AND_LITERALS).repeatedSubRecognizer(orAllRecognizer));
1697                RecognizerBase<ECobolParserState> intoRecognizer = createRecognizer(start -> {
1698                        RecognizerBase<ECobolParserState> prefix = start.optional(COMMA);
1699                        prefix.sequence(EnumSet.of(DELIMITER, COUNT)).optional(IN).sequence(IDENTIFIER);
1700                        prefix.sequence(IDENTIFIER);
1701                });
1702                RecognizerBase<ECobolParserState> withPointerRecognizer = createRecognizer(
1703                                start -> start.optional(WITH).sequence(POINTER, IDENTIFIER));
1704                RecognizerBase<ECobolParserState> tallyingRecognizer = createRecognizer(
1705                                start -> start.sequence(TALLYING).optional(IN).sequence(IDENTIFIER));
1706                RecognizerBase<ECobolParserState> unstringRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
1707                                .sequence(UNSTRING, IDENTIFIER).createNode(STATEMENT, 0, 1);
1708                unstringRecognizer = unstringRecognizer.optionalSubRecognizer(delimitedByRecognizer).sequence(INTO)
1709                                .repeatedSubRecognizer(intoRecognizer).optionalSubRecognizer(withPointerRecognizer)
1710                                .optionalSubRecognizer(tallyingRecognizer);
1711
1712                completeRuleWithConditionalClause(unstringRecognizer, createOnAtClauseSubRecognizer(OVERFLOW), END_UNSTRING);
1713                completeRule(unstringRecognizer, END_UNSTRING, SIMPLE);
1714        }
1715
1716        /**
1717         * Create rules for SET verb
1718         */
1719        private void createRuleForSetVerb() {
1720                RecognizerBase<ECobolParserState> baseSetRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(SET)
1721                                .createNode(STATEMENT, 0);
1722                RecognizerBase<ECobolParserState> leftAndRightRepetitionRecognizer = createRecognizer(start -> start
1723                                .repeatedSubRecognizer(getLeftOperandForSetVerb()).subRecognizer(getRightOperandForSetVerb()));
1724                baseSetRecognizer.repeatedSubRecognizer(leftAndRightRepetitionRecognizer);
1725                completeRule(baseSetRecognizer, null, SIMPLE);
1726        }
1727
1728        /**
1729         * Gets the recognizer for the right operand of a SET verb.
1730         */
1731        private RecognizerBase<ECobolParserState> getRightOperandForSetVerb() {
1732                return createRecognizer(start -> {
1733                        RecognizerBase<ECobolParserState> prefix = start.sequence(EnumSet.of(TO, UP, DOWN));
1734                        prefix.sequence(EnumSet.of(STRING_LITERAL, ON, OFF, TRUE, FALSE, NULL, NULLS));
1735                        prefix.sequence(ADDRESS, OF, IDENTIFIER);
1736                        prefix.sequence(BY).optional(LENGTH, OF).sequence(IDENTIFIER_AND_NUMERIC_LITERALS);
1737                        prefix.sequence(ENTRY, IDENTIFIER_AND_LITERALS);
1738                        prefix.optional(NOT).sequence(EnumSet.of(BROWSING, READING, WRITING), CONVERTING, FROM,
1739                                        EnumSet.of(BROWSING, WRITING));
1740                        prefix.optional(NOT).sequence(EnumSet.of(BROWSING, READING, WRITING));
1741                        prefix.repeatedSubRecognizer(getArithmeticExpressionRecognizer(true));
1742                });
1743        }
1744
1745        /**
1746         * Return a sub recognizer for matching repeated sequence of tokens with or
1747         * without a comma separator.
1748         */
1749        private RecognizerBase<ECobolParserState> getRepeatedSequenceRecognizer(Object sequence) {
1750                return createRecognizer(start -> start.optional(COMMA).sequence(sequence));
1751        }
1752
1753        /**
1754         * Gets the recognizer for the left operand of a SET verb.
1755         */
1756        private RecognizerBase<ECobolParserState> getLeftOperandForSetVerb() {
1757                return createRecognizer(start -> {
1758                        start.optional(COMMA).sequence(EnumSet.of(ADDRESS, SIZE, CONTENT)).optional(OF).sequence(IDENTIFIER);
1759                        start.subRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER));
1760                });
1761        }
1762
1763        /**
1764         * Creates parsing rules for EXIT verb.
1765         */
1766        private void createRuleForExitVerb() {
1767                RecognizerBase<ECobolParserState> exitProgramRecognizer = createRecognizer(start -> {
1768                        RecognizerBase<ECobolParserState> prefix = start.sequence(EnumSet.of(GIVING, RETURNING));
1769                        prefix.optional(ADDRESS, OF).sequence(IDENTIFIER);
1770                        prefix.sequence(INTEGER_LITERAL);
1771                });
1772                RecognizerBase<ECobolParserState> exitRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(EXIT)
1773                                .createNode(STATEMENT, 0);
1774                exitRecognizer.sequence(PERFORM).optional(CYCLE);
1775                exitRecognizer.sequence(EnumSet.of(FUNCTION, ETokenType.METHOD, PARAGRAPH, SECTION));
1776                exitRecognizer.sequence(PROGRAM).optionalSubRecognizer(exitProgramRecognizer);
1777                completeRule(exitRecognizer, null, SIMPLE);
1778        }
1779
1780        /**
1781         * Create rules for condition statements.
1782         */
1783        private void createConditionRules() {
1784                createIfAndElseRules();
1785
1786                // Search statement
1787                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(EVALUATE).createNode(STATEMENT, SubTypeNames.EVALUATE),
1788                                END_EVALUATE, SCOPE);
1789
1790                // Evaluate statement
1791                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(SEARCH).optional(ALL).sequence(IDENTIFIER)
1792                                .skipAny(EnumSet.of(VARYING, IDENTIFIER, AT, END)).createNode(STATEMENT, 0), END_SEARCH, SCOPE);
1793
1794                // Child-when statements for search and evaluate statements
1795                completeRule(inState(IN_STATEMENT).sequence(WHEN).optional(OTHER).createNode(STATEMENT, 0, 1), null, SIMPLE);
1796        }
1797
1798        /**
1799         * Create rules for loop verbs.
1800         */
1801        private void createLoopVerbRules() {
1802                createPerformRules();
1803
1804                RecognizerBase<ECobolParserState> gotoRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(GO)
1805                                .createNode(STATEMENT, SubTypeNames.GOTO).optional(TO).optional(IDENTIFIER);
1806                RecognizerBase<ECobolParserState> gotoDependingRecognizer = gotoRecognizer
1807                                .repeatedSubRecognizer(getRepeatedSequenceRecognizer(IDENTIFIER))
1808                                .subRecognizer(getDependingRecognizer());
1809
1810                completeRule(gotoDependingRecognizer, null, SIMPLE);
1811                completeRule(gotoRecognizer, null, SIMPLE);
1812        }
1813
1814        /**
1815         * Create rules for the various perform statements
1816         */
1817        private void createPerformRules() {
1818                RecognizerBase<ECobolParserState> performRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(PERFORM);
1819                RecognizerBase<ECobolParserState> identifierRecognizer = performRecognizer.sequence(IDENTIFIER)
1820                                .createNode(STATEMENT, 0, 1);
1821                RecognizerBase<ECobolParserState> throughRecognizer = identifierRecognizer.sequence(EnumSet.of(THRU, THROUGH),
1822                                (IDENTIFIER));
1823                completePerformRule(throughRecognizer, SIMPLE);
1824                completePerformRule(identifierRecognizer, SIMPLE);
1825                completePerformRule(performRecognizer.createNode(STATEMENT, 0), SCOPE);
1826        }
1827
1828        /**
1829         * Complete a rule for the perform verb.
1830         */
1831        private void completePerformRule(RecognizerBase<ECobolParserState> recognizer, EStatementEndType statementEndType) {
1832                EnumSet<ETokenType> skippedTokens = EnumSet.of(WITH, TEST, BEFORE, AFTER);
1833                RecognizerBase<ECobolParserState> timesRecognizer = recognizer.sequence(EnumSet.of(IDENTIFIER, INTEGER_LITERAL),
1834                                TIMES);
1835                RecognizerBase<ECobolParserState> untilRecognizer = recognizer.skipAny(skippedTokens).sequence(UNTIL)
1836                                .subRecognizer(getConditionSubRecognizer());
1837                RecognizerBase<ECobolParserState> varyingUntilRecognizer = recognizer.skipAny(skippedTokens).sequence(VARYING)
1838                                .skipTo(UNTIL).subRecognizer(getConditionSubRecognizer());
1839                RecognizerBase<ECobolParserState> repeatedAfterUntilRecognizer = createRecognizer(
1840                                start -> start.sequence(AFTER).skipTo(UNTIL).subRecognizer(getConditionSubRecognizer()));
1841                RecognizerBase<ECobolParserState> varyingUntilAfterRecognizer = varyingUntilRecognizer
1842                                .repeatedSubRecognizer(repeatedAfterUntilRecognizer);
1843
1844                completeRule(varyingUntilAfterRecognizer, END_PERFORM, statementEndType);
1845                completeRule(varyingUntilRecognizer, END_PERFORM, statementEndType);
1846                completeRule(untilRecognizer, END_PERFORM, statementEndType);
1847                completeRule(timesRecognizer, END_PERFORM, statementEndType);
1848                if (statementEndType == SIMPLE) {
1849                        recognizer.notPreCondition(new OutOfScopeRecognizer()).sequenceBefore(END_PERFORM).endNode();
1850                }
1851                completeRule(recognizer, END_PERFORM, statementEndType);
1852        }
1853
1854        /**
1855         * Get the recognizer to match arithmetic expressions. A comma is not an
1856         * arithmetic operator but its included because as a Cobol separator, it helps
1857         * to match repeated identifiers or expressions
1858         */
1859        private RecognizerBase<ECobolParserState> getArithmeticExpressionRecognizer(boolean includeComma) {
1860                EnumSet<ETokenType> operatorTokens = EnumSet.of(PLUS, MINUS, DIV, MULT, B_AND, B_OR, B_NOT, B_XOR);
1861                EnumSet<ETokenType> operandTokens = EnumSet.copyOf(IDENTIFIER_LITERALS_AND_ZERO);
1862                operandTokens.addAll(EnumSet.of(NULL, NULLS, SELF));
1863                operandTokens.remove(STRING_LITERAL);
1864                if (includeComma) {
1865                        operandTokens.add(COMMA);
1866                }
1867                RecognizerBase<ECobolParserState> operatorBeforeExpressionRecognizer = createRecognizer(start -> {
1868                        RecognizerBase<ECobolParserState> prefix = start.sequence(operatorTokens);
1869                        prefix.sequence(operandTokens);
1870                        prefix.skipNested(LPAREN, RPAREN);
1871                });
1872
1873                RecognizerBase<ECobolParserState> repeatedExpressionRecognizer = createRecognizer(start -> {
1874                        start.subRecognizer(operatorBeforeExpressionRecognizer);
1875                        start.skipNested(LPAREN, RPAREN);
1876                });
1877
1878                return createRecognizer(start -> {
1879                        start.sequence(operandTokens).repeatedSubRecognizer(repeatedExpressionRecognizer);
1880                        start.skipNested(LPAREN, RPAREN).repeatedSubRecognizer(repeatedExpressionRecognizer);
1881                });
1882        }
1883
1884        /**
1885         * Returns a basic condition sub recognizer.
1886         */
1887        private RecognizerBase<ECobolParserState> getConditionSubRecognizer() {
1888                EnumSet<ETokenType> leftOperandTokens = EnumSet.copyOf(IDENTIFIER_LITERALS_AND_ZERO);
1889                leftOperandTokens.addAll(EnumSet.of(NULL, NULLS, SELF));
1890                EnumSet<ETokenType> rightOperandTokens = EnumSet.copyOf(leftOperandTokens);
1891                rightOperandTokens.addAll(EnumSet.of(NUMERIC, ALPHABETIC, ALPHABETIC_UPPER, ALPHABETIC_LOWER, DBCS, KANJI));
1892
1893                RecognizerBase<ECobolParserState> singleOperandRecognizer = createRecognizer(
1894                                start -> start.sequence(leftOperandTokens));
1895
1896                return createRecognizer(start -> start.optionalSubRecognizer(getArithmeticExpressionRecognizer(false))
1897                                .optionalSubRecognizer(singleOperandRecognizer));
1898        }
1899
1900        /**
1901         * Create rules for if-related condition statements
1902         */
1903        private void createIfAndElseRules() {
1904                createIfRule(IF, EnumSet.of(ELSE, OTHERWISE), END_IF, STATEMENT);
1905                createElseRule(EnumSet.of(ELSE, OTHERWISE), END_IF, STATEMENT);
1906        }
1907
1908        /**
1909         * Create rules for the ELSE-statement and $ELSE-meta entities
1910         */
1911        private void createElseRule(EnumSet<ETokenType> elseOtherwise, ETokenType endToken, EShallowEntityType entityType) {
1912                RecognizerBase<ECobolParserState> elseOtherwiseRecognizer = inState(TOP_LEVEL, IN_STATEMENT)
1913                                .sequence(elseOtherwise).createNode(entityType, 0).parseUntil(IN_STATEMENT);
1914
1915                elseOtherwiseRecognizer.sequenceBefore(elseOtherwise).endNode();
1916                elseOtherwiseRecognizer.sequence(endToken).endNode();
1917                elseOtherwiseRecognizer.sequenceBefore(DOT).endNode();
1918        }
1919
1920        /**
1921         * Create the IF-statement and $IF-meta rules.
1922         */
1923        private void createIfRule(ETokenType ifType, EnumSet<ETokenType> elseOtherwise, ETokenType endIfType,
1924                        EShallowEntityType entityType) {
1925                RecognizerBase<ECobolParserState> ifRecognizer = inState(TOP_LEVEL, IN_STATEMENT).sequence(ifType)
1926                                .createNode(entityType, 0).parseUntil(IN_STATEMENT);
1927                ifRecognizer.sequenceBefore(elseOtherwise).endNodeWithContinuation();
1928
1929                // Precondition ensures that we are parsing an if-block without an else.
1930                ifRecognizer.preCondition(new NoElseRecognizer(ifType, endIfType)).sequence(endIfType).endNode();
1931                ifRecognizer.sequenceBefore(DOT).endNode();
1932        }
1933
1934        /**
1935         * Create rules for the TRY ... CATCH... FINALLY Verbs.
1936         */
1937        private void createTryCatchFinallyVerbsRules() {
1938                completeRule(inState(TOP_LEVEL, IN_STATEMENT).sequence(TRY).createNode(STATEMENT, 0), END_TRY, SCOPE);
1939
1940                inState(TOP_LEVEL, IN_STATEMENT).sequence(CATCH, IDENTIFIER).createNode(STATEMENT, 0).parseUntil(IN_STATEMENT)
1941                                .sequenceBefore(EnumSet.of(CATCH, FINALLY, END_TRY, DOT)).endNode();
1942
1943                inState(TOP_LEVEL, IN_STATEMENT).sequence(FINALLY).createNode(STATEMENT, 0).parseUntil(IN_STATEMENT)
1944                                .sequenceBefore(EnumSet.of(END_TRY, DOT)).endNode();
1945        }
1946
1947        /** {@inheritDoc} */
1948        @Override
1949        public List<ShallowEntity> parseTopLevel(List<IToken> tokens) {
1950                return new SectionParagraphToMethodProcessor(tokens).postProcess(super.parseTopLevel(tokens));
1951        }
1952}