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}