001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 the ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package eu.cqse.check.framework.shallowparser.languages.abap;
018
019import static eu.cqse.check.framework.scanner.ETokenType.*;
020import static eu.cqse.check.framework.shallowparser.languages.abap.AbapShallowParser.EAbapParserStates.DECLARATIONS;
021import static eu.cqse.check.framework.shallowparser.languages.abap.AbapShallowParser.EAbapParserStates.IN_TYPE;
022import static eu.cqse.check.framework.shallowparser.languages.abap.AbapShallowParser.EAbapParserStates.STATEMENTS;
023import static eu.cqse.check.framework.shallowparser.languages.abap.AbapShallowParser.EAbapParserStates.TOPLEVEL;
024
025import java.util.EnumSet;
026import java.util.Set;
027
028import eu.cqse.check.framework.scanner.ETokenType.ETokenClass;
029import org.conqat.lib.commons.region.Region;
030import org.conqat.lib.commons.string.StringUtils;
031
032import eu.cqse.check.framework.scanner.ETokenType;
033import eu.cqse.check.framework.scanner.IToken;
034import eu.cqse.check.framework.shallowparser.SubTypeNames;
035import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType;
036import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;
037import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase;
038
039/**
040 * Shallow parser for ABAP. The following links are useful for writing the
041 * parser:
042 * <ul>
043 * <li><a href="http://help.sap.com/abapdocu_702/en/">ABAP Keyword
044 * Documentation</a></li>
045 * <li><a href=
046 * "http://help.sap.com/abapdocu_702/en/abenabap_statements_overview.htm" >ABAP
047 * Statements Overview</a></li>
048 * </ul>
049 */
050public class AbapShallowParser extends ShallowParserBase<AbapShallowParser.EAbapParserStates> {
051
052        /**
053         * Since there are no reserved words in ABAP, one can use statement names as
054         * identifiers. Therefore, whenever we expect an identifier, it's not safe to
055         * rely on the Scanner's categorization of identifiers, and we should allow
056         * keywords and operators as well. Example for operator: {@link ETokenType#MOD}.
057         */
058        private static final EnumSet<ETokenClass> IDENTIFIER_LIKE = EnumSet.of(ETokenClass.IDENTIFIER, ETokenClass.KEYWORD,
059                        ETokenClass.OPERATOR);
060
061        /** Tokens that can introduce a simple statement. */
062        private static final EnumSet<ETokenType> SIMPLE_STATEMENT_START_TOKENS = EnumSet.of(ADD, ADD_CORRESPONDING, APPEND,
063                        ASSERT, ASSIGN, AUTHORITY_CHECK, BACK, BREAK_POINT, CALL, CASE, CHECK, CLEAR, CLOSE, COLLECT, COMMIT,
064                        COMMUNICATION, COMPUTE, CONCATENATE, CONDENSE, CONSTANTS, CONTEXTS, CONTINUE, CONTROLS, CONVERT, CREATE,
065                        DATA, DELETE, DEMAND, DESCRIBE, DETAIL, DIVIDE, DIVIDE_CORRESPONDING, DO, EDITOR_CALL, ENHANCEMENT_POINT,
066                        EXISTS, EXIT, EXPORT, EXTRACT, FETCH, FIELDS, FIELD_GROUPS, FIELD_SYMBOLS, FIND, FORMAT, FREE, GENERATE,
067                        GET, HIDE, IDENTIFIER, IF, IMPORT, INCLUDE, INFOTYPES, INPUT, INSERT, LEAVE, LOAD, LOCAL, LOG_POINT,
068                        MAXIMUM, MESSAGE, MINIMUM, MODIFY, MODULE, MOVE, MOVE_CORRESPONDING, MULTIPLY_CORRESPONDING, MULTIPLY, NAME,
069                        NEW_LINE, NEW_PAGE, NEW_SECTION, OPEN, OVERLAY, PACK, PACKAGE, PARAMETERS, PERFORM, POSITION, PRINT_CONTROL,
070                        PUT, RAISE, RANGES, READ, REFRESH, REJECT, REPLACE, RESERVE, RESUME, RETURN, ROLLBACK, SCROLL, SEARCH, SET,
071                        SHIFT, SKIP, SORT, SPLIT, STATICS, STOP, SUBMIT, SUBTRACT, SUBTRACT_CORRESPONDING, SUM, SUMMARY, SUMMING,
072                        SUPPLY, SUPPRESS, SYNTAX_CHECK, TRANSFER, TRANSLATE, TRUNCATE, TYPES, ULINE, UNASSIGN, UNPACK, UPDATE,
073                        VERSION, WAIT, WINDOW, WRITE, NEW);
074
075        /**
076         * Tokens that identify when to filter out an INCLUDE statement. We only want to
077         * find statements of type <code>INCLUDE foobar</code> where foobar is another
078         * ABAP source object, but not <code>INCLUDE TYPE foobar</code> or
079         * <code>INCLUDE STRUCTURE foobar</code>. Since one may name their included
080         * source object like an ABAP keyword, we need to have a blacklist approach
081         * here.
082         */
083        public static final Set<ETokenType> INCLUDE_FILTER_TOKEN_TYPES = EnumSet.of(ETokenType.STRUCTURE, ETokenType.TYPE);
084
085        /**
086         * Set of keywords that start an event block (without keywords that require a
087         * preceding "at")
088         */
089        private static final EnumSet<ETokenType> EVENT_BLOCKS = EnumSet.of(INITIALIZATION, START_OF_SELECTION,
090                        END_OF_SELECTION, TOP_OF_PAGE, END_OF_PAGE, LOAD_OF_PROGRAM);
091
092        /**
093         * Set of keywords that end an event block (possibly indicating the start of the
094         * next one)
095         */
096        private static final EnumSet<ETokenType> EVENT_BLOCKS_END = EnumSet.of(AT, FORM, CLASS, INTERFACE);
097
098        // all tokens that start a new event block also end the current one.
099        static {
100                EVENT_BLOCKS_END.addAll(EVENT_BLOCKS);
101        }
102
103        /** The states used in this parser. */
104        public enum EAbapParserStates {
105
106                /**
107                 * Top level state used for parsing constructs that are not nested in other
108                 * constructs.
109                 */
110                TOPLEVEL,
111
112                /**
113                 * Type state used for parsing constructs that are direct children of a REPORT
114                 * or PROGRAM.
115                 */
116                IN_TYPE,
117
118                /**
119                 * A state to recognize declarations within classes. As many constructs are
120                 * allowed both top-level and in declarations, many rules are registered for
121                 * both.
122                 */
123                DECLARATIONS,
124
125                /**
126                 * A state to recognize statements, i.e. plain code in functions, etc.
127                 */
128                STATEMENTS
129        }
130
131        /** Constructor. */
132        public AbapShallowParser() {
133                super(EAbapParserStates.class, TOPLEVEL);
134
135                createMetaRules();
136                createTopLevelRules();
137                createTypeRules();
138                createAttributeRules();
139                createMethodRules();
140                createModuleRules();
141                createStatementRules();
142
143                inAnyState().sequence(DOT).createNode(EShallowEntityType.STATEMENT, SubTypeNames.EMPTY_STATEMENT).endNode();
144        }
145
146        /** Rules for parsing elements that are only expected top-level. */
147        private void createTopLevelRules() {
148
149                // since the report is not really a method, its statements are still
150                // parsed in the toplevel scope, not the statement scope
151                inState(TOPLEVEL).sequence(EnumSet.of(REPORT, PROGRAM)).optional(COLON).sequence(IDENTIFIER_LIKE)
152                                .createNode(EShallowEntityType.TYPE, 0, -1).skipTo(DOT).parseUntilOrEof(IN_TYPE);
153
154                inState(TOPLEVEL, STATEMENTS, IN_TYPE).sequence(EnumSet.of(SELECTION_SCREEN))
155                                .createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT).endNode();
156
157                // top-of-page during line-selection is different from "top-of-page"
158                RecognizerBase<EAbapParserStates> topsRecognizer = inState(TOPLEVEL, IN_TYPE)
159                                .sequence(TOP_OF_PAGE, DURING, LINE_SELECTION)
160                                .createNode(EShallowEntityType.METHOD, SubTypeNames.TOP_OF_PAGE_DURING_LINE_SELECTION).skipTo(DOT)
161                                .parseUntilOrEof(STATEMENTS);
162                endMethodEntityOnEventBlock(EVENT_BLOCKS_END, topsRecognizer);
163
164                // basic event blocks without "at"
165                RecognizerBase<EAbapParserStates> eventBlockRecognizer = inState(TOPLEVEL, IN_TYPE).sequence(EVENT_BLOCKS)
166                                .createNode(EShallowEntityType.METHOD, 0, -1).skipTo(DOT).parseUntilOrEof(STATEMENTS);
167                endMethodEntityOnEventBlock(EVENT_BLOCKS_END, eventBlockRecognizer);
168
169                createGetBlockRules();
170
171                createAtBlockRules();
172        }
173
174        /**
175         * Creates rules constructs starting with "GET". These can either be statements
176         * or event handlers.
177         */
178        private void createGetBlockRules() {
179                // "get reference of" and consorts
180                inState(TOPLEVEL, STATEMENTS, IN_TYPE).sequence(GET, ETokenClass.KEYWORD)
181                                .createNode(EShallowEntityType.STATEMENT, new int[] { 0, 1 }).skipTo(DOT).endNode();
182
183                // get ... late event handler
184                RecognizerBase<EAbapParserStates> getLateEventBlockRecognizer = inState(TOPLEVEL, IN_TYPE)
185                                .sequence(GET, IDENTIFIER, LATE).createNode(EShallowEntityType.METHOD, SubTypeNames.GET_LATE, 1)
186                                .skipTo(DOT).parseUntilOrEof(STATEMENTS);
187                endMethodEntityOnEventBlock(EVENT_BLOCKS_END, getLateEventBlockRecognizer);
188
189                // get ... event handler
190                RecognizerBase<EAbapParserStates> getEventBlockRecognizer = inState(TOPLEVEL, IN_TYPE).sequence(GET, IDENTIFIER)
191                                .createNode(EShallowEntityType.METHOD, 0, 1).skipTo(DOT).parseUntilOrEof(STATEMENTS);
192                endMethodEntityOnEventBlock(EVENT_BLOCKS_END, getEventBlockRecognizer);
193        }
194
195        /** Creates parsing rules for event handlers starting with "AT". */
196        private void createAtBlockRules() {
197                // at selection screen event handler (named)
198                RecognizerBase<EAbapParserStates> selectionScreenBlockRecognizer = inState(TOPLEVEL, IN_TYPE)
199                                .sequence(AT, SELECTION_SCREEN).skipTo(DOT).createNode(EShallowEntityType.METHOD,
200                                                SubTypeNames.AT_SELECTION_SCREEN, new Region(0, -2, StringUtils.SPACE))
201                                .parseUntilOrEof(STATEMENTS);
202                endMethodEntityOnEventBlock(EVENT_BLOCKS_END, selectionScreenBlockRecognizer);
203
204                // at PF<..> event handler (named)
205                RecognizerBase<EAbapParserStates> atPfBlockRecognizer = inState(TOPLEVEL, IN_TYPE).sequence(AT, PF_EVENT)
206                                .createNode(EShallowEntityType.METHOD, SubTypeNames.PF_EVENT, new Region(1, 1)).skipTo(DOT)
207                                .parseUntilOrEof(STATEMENTS);
208                endMethodEntityOnEventBlock(EVENT_BLOCKS_END, atPfBlockRecognizer);
209
210                // at ... event handler (anonymous)
211                RecognizerBase<EAbapParserStates> atEventBlockRecognizer = inState(TOPLEVEL, IN_TYPE)
212                                .sequence(AT, EnumSet.of(LINE_SELECTION, USER_COMMAND))
213                                .createNode(EShallowEntityType.METHOD, new int[] { 0, 1 }).skipTo(DOT).parseUntilOrEof(STATEMENTS);
214                endMethodEntityOnEventBlock(EVENT_BLOCKS_END, atEventBlockRecognizer);
215        }
216
217        /**
218         * Ends the given recognizer if it either hits one of the given event block end
219         * tokens or a get event block or a module block.
220         */
221        private static void endMethodEntityOnEventBlock(EnumSet<ETokenType> eventBlocksEnd,
222                        RecognizerBase<EAbapParserStates> eventBlockRecognizer) {
223                eventBlockRecognizer.sequenceBefore(eventBlocksEnd).endNode();
224                eventBlockRecognizer.sequenceBefore(GET, IDENTIFIER).endNode();
225                eventBlockRecognizer.preCondition(new ModuleBlockStartRecognizer()).endNode();
226        }
227
228        /** Rules for parsing of meta elements. */
229        private void createMetaRules() {
230
231                inState(DECLARATIONS).sequence(EnumSet.of(PUBLIC, PROTECTED, PRIVATE), SECTION, DOT)
232                                .createNode(EShallowEntityType.META, SubTypeNames.VISIBILITY, 0).endNode();
233
234                // TypePools/Tables with colon
235                inAnyState().sequence(EnumSet.of(TYPE_POOLS, TABLES)).sequence(COLON).createNode(EShallowEntityType.META, 0, 2)
236                                .skipTo(DOT).endNode();
237                // TypePools/Tables without colon (need two rules to get the the name
238                // right)
239                inAnyState().sequence(EnumSet.of(TYPE_POOLS, TABLES)).createNode(EShallowEntityType.META, 0, 1).skipTo(DOT)
240                                .endNode();
241
242                inAnyState().sequence(DEFINE).createNode(EShallowEntityType.META, SubTypeNames.MACRO)
243                                .skipTo(END_OF_DEFINITION, DOT).endNode();
244
245                inState(DECLARATIONS).sequence(EnumSet.of(INTERFACES, ALIASES)).createNode(EShallowEntityType.META, 0)
246                                .skipTo(DOT).endNode();
247
248                inState(IN_TYPE).sequence(INCLUDE)
249                                .notPreCondition(createRecognizer(start -> start.sequence(INCLUDE_FILTER_TOKEN_TYPES)))
250                                .createNode(EShallowEntityType.META, 0).skipTo(DOT).endNode();
251        }
252
253        /** Rules for parsing classes, interfaces, types, and events. */
254        private void createTypeRules() {
255
256                createClassRules();
257
258                // interfaces
259                RecognizerBase<EAbapParserStates> interfaceAlternative = inState(TOPLEVEL, DECLARATIONS).sequence(INTERFACE,
260                                IDENTIFIER);
261                interfaceAlternative.sequence(EnumSet.of(LOAD, DEFERRED, LOCAL))
262                                .createNode(EShallowEntityType.TYPE, SubTypeNames.INTERFACE_PUBLICATION, 1).skipTo(DOT).endNode();
263                interfaceAlternative.createNode(EShallowEntityType.TYPE, SubTypeNames.INTERFACE_DEFINITION, 1).skipTo(DOT)
264                                .parseUntil(DECLARATIONS).sequence(ENDINTERFACE, DOT).endNode();
265
266        }
267
268        /** Rules for parsing ABAP OO classes. */
269        private void createClassRules() {
270                RecognizerBase<EAbapParserStates> classDefinitionAlternative = inState(TOPLEVEL, DECLARATIONS, IN_TYPE)
271                                .sequence(CLASS, IDENTIFIER, DEFINITION);
272                classDefinitionAlternative.sequence(EnumSet.of(LOAD, DEFERRED, LOCAL))
273                                .createNode(EShallowEntityType.TYPE, SubTypeNames.CLASS_PUBLICATION, 1).skipTo(DOT).endNode();
274                classDefinitionAlternative.createNode(EShallowEntityType.TYPE, SubTypeNames.CLASS_DEFINITION, 1).skipTo(DOT)
275                                .parseUntil(DECLARATIONS).sequence(ENDCLASS, DOT).endNode();
276
277                inState(TOPLEVEL, DECLARATIONS, IN_TYPE).sequence(CLASS, IDENTIFIER, IMPLEMENTATION)
278                                .createNode(EShallowEntityType.TYPE, SubTypeNames.CLASS_IMPLEMENTATION, 1).skipTo(DOT)
279                                .parseUntil(DECLARATIONS).sequence(ENDCLASS, DOT).endNode();
280        }
281
282        /** Rules for parsing attributes. */
283        private void createAttributeRules() {
284                // Match BEGIN OF COMMON PART declarations as single entity
285                inState(TOPLEVEL, DECLARATIONS, IN_TYPE).sequence(DATA, BEGIN, OF, COMMON, PART).optional(IDENTIFIER_LIKE)
286                                .createNode(EShallowEntityType.ATTRIBUTE, 0, -1).skipTo(END, OF, COMMON, PART).skipTo(DOT).endNode();
287
288                // Match record type declaration as single entity
289                inState(TOPLEVEL, DECLARATIONS, IN_TYPE).sequence(EnumSet.of(TYPES, CONSTANTS, DATA, STATICS, CLASS_DATA))
290                                .optional(COLON).sequence(BEGIN, OF, IDENTIFIER_LIKE).createNode(EShallowEntityType.ATTRIBUTE, 0, -1)
291                                .skipTo(END, OF).skipTo(DOT).endNode();
292
293                // types, events, class events
294                inState(TOPLEVEL, DECLARATIONS, IN_TYPE).sequence(EnumSet.of(EVENTS, CLASS_EVENTS))
295                                .createNode(EShallowEntityType.ATTRIBUTE, 0).skipTo(DOT).endNode();
296
297                inState(TOPLEVEL, DECLARATIONS, IN_TYPE)
298                                .sequence(EnumSet.of(CONSTANTS, NODES, STATICS, DATA, TYPES, FIELD_GROUPS, CLASS_DATA, RANGES,
299                                                SELECT_OPTIONS, INFOTYPES))
300                                .optional(COLON).sequence(IDENTIFIER_LIKE).createNode(EShallowEntityType.ATTRIBUTE, 0, -1).skipTo(DOT)
301                                .endNode();
302
303                inState(TOPLEVEL, DECLARATIONS, IN_TYPE).sequence(FIELD_SYMBOLS).optional(COLON).sequence(LT, IDENTIFIER_LIKE)
304                                .createNode(EShallowEntityType.ATTRIBUTE, 0, -1).skipTo(DOT).endNode();
305
306                inState(TOPLEVEL, DECLARATIONS, IN_TYPE)
307                                // The "parameter" keyword is the obsolete older form of
308                                // "parameters"
309                                .sequence(EnumSet.of(PARAMETER, PARAMETERS)).optional(COLON).sequence(IDENTIFIER_LIKE)
310                                .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.PARAMETERS, -1).skipTo(DOT).endNode();
311        }
312
313        /**
314         * Rules for parsing methods, functions, and forms.
315         *
316         * We handle colons here to catch method declarations like <code>form:
317         * foo.</foo> as that's a declaration for form "foo" not ":foo".
318         */
319        private void createMethodRules() {
320                inState(DECLARATIONS).sequence(EnumSet.of(METHODS, CLASS_METHODS))
321                                .skipBefore(
322                                                EnumSet.of(ABSTRACT, DOT, RETURNING, IMPORTING, EXPORTING, REDEFINITION, CHANGING, EXCEPTIONS))
323                                .createNode(EShallowEntityType.METHOD, SubTypeNames.METHOD_DECLARATION, new Region(1, -1)).skipTo(DOT)
324                                .endNode();
325
326                inState(DECLARATIONS).sequence(METHOD).optional(COLON).markStart().skipBefore(EnumSet.of(DOT, BY))
327                                .createNode(EShallowEntityType.METHOD, SubTypeNames.METHOD_IMPLEMENTATION, new Region(0, -1))
328                                .skipTo(DOT).parseUntil(STATEMENTS).sequence(ENDMETHOD, DOT).endNode();
329                inState(TOPLEVEL, STATEMENTS).sequence(METHOD).optional(COLON).markStart().skipBefore(EnumSet.of(DOT, BY))
330                                .createNode(EShallowEntityType.METHOD, SubTypeNames.METHOD_IMPLEMENTATION, new Region(0, -1))
331                                .skipTo(DOT).parseUntil(STATEMENTS).sequence(ENDMETHOD, DOT).endNode();
332                inState(TOPLEVEL, IN_TYPE).sequence(FORM).optional(COLON).sequence(IDENTIFIER_LIKE)
333                                .createNode(EShallowEntityType.METHOD, SubTypeNames.FORM, -1).skipTo(DOT).parseUntil(STATEMENTS)
334                                .sequence(ENDFORM, DOT).endNode();
335
336                inState(TOPLEVEL, DECLARATIONS).sequence(FUNCTION).optional(COLON).markStart()
337                                .skipBefore(EnumSet.of(DOT, IMPORTING, EXPORTING, TABLES, CHANGING, RAISING, EXCEPTIONS))
338                                .createNode(EShallowEntityType.METHOD, SubTypeNames.FUNCTION, new Region(0, -1)).skipTo(DOT)
339                                .parseUntil(STATEMENTS).sequence(ENDFUNCTION, DOT).endNode();
340
341                RecognizerBase<EAbapParserStates> toplevelStartOfSelection = inState(IN_TYPE)
342                                .sequenceBefore(EnumSet.complementOf(EVENT_BLOCKS_END))
343                                .sequenceBefore(EnumSet.complementOf(EnumSet.of(MODULE)))
344                                .createNode(EShallowEntityType.METHOD, SubTypeNames.START_OF_SELECTION, SubTypeNames.START_OF_SELECTION)
345                                .parseUntilOrEof(STATEMENTS);
346                endMethodEntityOnEventBlock(EVENT_BLOCKS_END, toplevelStartOfSelection);
347
348                RecognizerBase<EAbapParserStates> toplevelStartOfSelectionWithModule = inState(IN_TYPE).sequenceBefore(MODULE)
349                                .notPreCondition(new ModuleBlockStartRecognizer())
350                                .createNode(EShallowEntityType.METHOD, SubTypeNames.START_OF_SELECTION, SubTypeNames.START_OF_SELECTION)
351                                .parseUntilOrEof(STATEMENTS);
352                endMethodEntityOnEventBlock(EVENT_BLOCKS_END, toplevelStartOfSelectionWithModule);
353
354        }
355
356        /** Rules for parsing modules. */
357        private void createModuleRules() {
358                RecognizerBase<EAbapParserStates> moduleRecognizer = inState(TOPLEVEL, IN_TYPE)
359                                .preCondition(new ModuleBlockStartRecognizer()).sequence(MODULE).markStart()
360                                .skipBefore(EnumSet.of(INPUT, OUTPUT, DOT));
361                moduleRecognizer.sequence(INPUT, DOT).createNode(EShallowEntityType.METHOD, SubTypeNames.MODULE_INPUT, 0)
362                                .parseUntil(STATEMENTS).sequence(ENDMODULE, DOT).endNode();
363                moduleRecognizer.sequence(OUTPUT, DOT).createNode(EShallowEntityType.METHOD, SubTypeNames.MODULE_OUTPUT, 0)
364                                .parseUntil(STATEMENTS).sequence(ENDMODULE, DOT).endNode();
365                moduleRecognizer.sequence(DOT).createNode(EShallowEntityType.METHOD, SubTypeNames.MODULE_INPUT, 0)
366                                .parseUntil(STATEMENTS).sequence(ENDMODULE, DOT).endNode();
367        }
368
369        /** Rules for parsing statements. */
370        private void createStatementRules() {
371
372                // special rule that matches assignments to variables that have the same
373                // name as keywords.
374                inState(STATEMENTS).sequence(ETokenClass.KEYWORD, EQ).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT)
375                                .endNode();
376
377                createConditionalStatementRules();
378
379                // on change
380                RecognizerBase<EAbapParserStates> changeAlternative = inAnyState().sequence(ON, CHANGE, OF)
381                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.ON_CHANGE).skipTo(DOT).parseUntil(STATEMENTS)
382                                .sequenceBefore(EnumSet.of(ELSE, ENDON));
383                changeAlternative.sequence(ENDON, DOT).endNode();
384                changeAlternative.endNodeWithContinuation();
385                inState(TOPLEVEL, STATEMENTS).sequence(IDENTIFIER_LIKE, ETokenType.LPAREN)
386                                .createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT).endNode();
387
388                createLoopStatementRules();
389
390                // try/catch
391                RecognizerBase<EAbapParserStates> tryAlternative = inState(TOPLEVEL, STATEMENTS)
392                                .sequence(EnumSet.of(TRY, CATCH, CLEANUP)).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT)
393                                .parseUntil(STATEMENTS).sequenceBefore(EnumSet.of(ENDTRY, CATCH, ENDCATCH, CLEANUP));
394                tryAlternative.sequence(EnumSet.of(ENDTRY, ENDCATCH), DOT).endNode();
395                tryAlternative.endNodeWithContinuation();
396
397                createSelectRules();
398
399                // exec
400                inState(TOPLEVEL, STATEMENTS).sequence(EXEC, SQL)
401                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.NATIVE_SQL).skipTo(ENDEXEC, DOT).endNode();
402
403                // simple statements that start with a field symbol, e.g.
404                // "<fs>-foo = 12."
405                inState(TOPLEVEL, STATEMENTS).sequence(LT).createNode(EShallowEntityType.STATEMENT, new Region(0, 2))
406                                .skipTo(DOT).endNode();
407
408                // Match record type declarations as a single entity, although they
409                // contain multiple dots.
410                inState(TOPLEVEL, STATEMENTS).sequence(EnumSet.of(TYPES, CONSTANTS, DATA, STATICS)).optional(COLON)
411                                .sequence(BEGIN, OF, IDENTIFIER_LIKE).createNode(EShallowEntityType.STATEMENT, 0, -1).skipTo(END, OF)
412                                .skipTo(DOT).endNode();
413
414                inState(TOPLEVEL, STATEMENTS).sequence(SIMPLE_STATEMENT_START_TOKENS)
415                                .createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT).endNode();
416        }
417
418        /** Creates parsing rules for conditional statements. */
419        private void createConditionalStatementRules() {
420                // if/elseif
421                RecognizerBase<EAbapParserStates> ifAlternative = inState(TOPLEVEL, STATEMENTS).sequence(EnumSet.of(IF, ELSEIF))
422                                .sequenceBefore(EnumSet.complementOf(EnumSet.of(ARROW))).createNode(EShallowEntityType.STATEMENT, 0)
423                                .skipTo(DOT).parseUntil(STATEMENTS).sequenceBefore(EnumSet.of(ELSEIF, ELSE, ENDIF));
424                ifAlternative.sequence(ENDIF, DOT).endNode();
425                ifAlternative.endNodeWithContinuation();
426
427                // else
428                inState(TOPLEVEL, STATEMENTS).sequence(ELSE).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT)
429                                .parseUntil(STATEMENTS).sequence(EnumSet.of(ENDIF, ENDON), DOT).endNode();
430
431                // case/when
432                inState(TOPLEVEL, STATEMENTS).sequence(CASE).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT)
433                                .parseUntil(STATEMENTS).sequence(ENDCASE, DOT).endNode();
434                // we parse when as meta, so we add no additional nesting
435                inState(STATEMENTS).sequence(WHEN).createNode(EShallowEntityType.META, 0).skipTo(DOT).endNode();
436        }
437
438        /** Creates parsing rules for loops and loop-like constructs. */
439        private void createLoopStatementRules() {
440                // loops
441                inState(TOPLEVEL, STATEMENTS).sequence(LOOP).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT)
442                                .parseUntil(STATEMENTS).sequence(ENDLOOP, DOT).endNode();
443                inState(TOPLEVEL, STATEMENTS).sequence(DO).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT)
444                                .parseUntil(STATEMENTS).sequence(ENDDO, DOT).endNode();
445                inState(TOPLEVEL, STATEMENTS).sequence(WHILE).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT)
446                                .parseUntil(STATEMENTS).sequence(ENDWHILE, DOT).endNode();
447                inState(STATEMENTS).sequence(AT).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT).parseUntil(STATEMENTS)
448                                .sequence(ENDAT, DOT).endNode();
449
450                // loop likes
451                inAnyState().sequence(PROVIDE).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT).parseUntil(STATEMENTS)
452                                .sequence(ENDPROVIDE, DOT).endNode();
453                inAnyState().sequence(ENHANCEMENT).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT)
454                                .parseUntil(STATEMENTS).sequence(ENDENHANCEMENT, DOT).endNode();
455                inAnyState().sequence(ENHANCEMENT_SECTION).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DOT)
456                                .parseUntil(STATEMENTS).sequence(END_ENHANCEMENT_SECTION, DOT).endNode();
457        }
458
459        /**
460         * Creates the parsing rules for the select clause. This is tricky, because the
461         * rules whether a select block or a single statement select is expected, are
462         * not trivial.
463         */
464        private void createSelectRules() {
465                RecognizerBase<EAbapParserStates> selectAlternative = inState(TOPLEVEL, STATEMENTS).sequence(SELECT);
466                selectAlternative.sequence(LPAREN).createNode(EShallowEntityType.STATEMENT, "method call")
467                                .skipToWithNesting(RPAREN, LPAREN, RPAREN).skipTo(DOT).endNode();
468                selectAlternative.subRecognizer(new SingleSelectRecognizer(), 1, 1)
469                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.SINGLE_SELECT).endNode();
470                selectAlternative.createNode(EShallowEntityType.STATEMENT, SubTypeNames.SELECT_BLOCK).skipTo(DOT)
471                                .parseUntil(STATEMENTS).sequence(ENDSELECT, DOT).endNode();
472        }
473
474        /** {@inheritDoc} */
475        @Override
476        protected boolean isFilteredToken(IToken token, IToken previousToken) {
477                return super.isFilteredToken(token, previousToken) || token.getType() == PRAGMA_DIRECTIVE;
478        }
479}