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.iec61131; 018 019import static eu.cqse.check.framework.scanner.ETokenType.ACTION; 020import static eu.cqse.check.framework.scanner.ETokenType.ALGORITHM; 021import static eu.cqse.check.framework.scanner.ETokenType.ALGORITHM_BLOCK; 022import static eu.cqse.check.framework.scanner.ETokenType.AUTOSTART; 023import static eu.cqse.check.framework.scanner.ETokenType.BEGIN; 024import static eu.cqse.check.framework.scanner.ETokenType.CASE; 025import static eu.cqse.check.framework.scanner.ETokenType.COLON; 026import static eu.cqse.check.framework.scanner.ETokenType.COMMA; 027import static eu.cqse.check.framework.scanner.ETokenType.CONSTANT; 028import static eu.cqse.check.framework.scanner.ETokenType.DO; 029import static eu.cqse.check.framework.scanner.ETokenType.ELEMENT; 030import static eu.cqse.check.framework.scanner.ETokenType.ELSE; 031import static eu.cqse.check.framework.scanner.ETokenType.ELSIF; 032import static eu.cqse.check.framework.scanner.ETokenType.END; 033import static eu.cqse.check.framework.scanner.ETokenType.END_ACTION; 034import static eu.cqse.check.framework.scanner.ETokenType.END_ALGORITHM; 035import static eu.cqse.check.framework.scanner.ETokenType.END_ALGORITHM_BLOCK; 036import static eu.cqse.check.framework.scanner.ETokenType.END_CASE; 037import static eu.cqse.check.framework.scanner.ETokenType.END_FOR; 038import static eu.cqse.check.framework.scanner.ETokenType.END_FUNCTION; 039import static eu.cqse.check.framework.scanner.ETokenType.END_FUNCTION_BLOCK; 040import static eu.cqse.check.framework.scanner.ETokenType.END_IF; 041import static eu.cqse.check.framework.scanner.ETokenType.END_IMPORT; 042import static eu.cqse.check.framework.scanner.ETokenType.END_OBJECT; 043import static eu.cqse.check.framework.scanner.ETokenType.END_ORGANIZATION_BLOCK; 044import static eu.cqse.check.framework.scanner.ETokenType.END_PROGRAM; 045import static eu.cqse.check.framework.scanner.ETokenType.END_STEP; 046import static eu.cqse.check.framework.scanner.ETokenType.END_STRUCT; 047import static eu.cqse.check.framework.scanner.ETokenType.END_TRANSITION; 048import static eu.cqse.check.framework.scanner.ETokenType.END_TYPE; 049import static eu.cqse.check.framework.scanner.ETokenType.END_VAR; 050import static eu.cqse.check.framework.scanner.ETokenType.END_WHILE; 051import static eu.cqse.check.framework.scanner.ETokenType.EQ; 052import static eu.cqse.check.framework.scanner.ETokenType.EVENT_ALGORITHM; 053import static eu.cqse.check.framework.scanner.ETokenType.EXIT; 054import static eu.cqse.check.framework.scanner.ETokenType.EXIT_TRANSITION; 055import static eu.cqse.check.framework.scanner.ETokenType.FLOATING_POINT_LITERAL; 056import static eu.cqse.check.framework.scanner.ETokenType.FOR; 057import static eu.cqse.check.framework.scanner.ETokenType.FUNCTION; 058import static eu.cqse.check.framework.scanner.ETokenType.FUNCTION_BLOCK; 059import static eu.cqse.check.framework.scanner.ETokenType.GO_ON_EXIT_TRANSITION; 060import static eu.cqse.check.framework.scanner.ETokenType.GO_ON_TRANSITION; 061import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER; 062import static eu.cqse.check.framework.scanner.ETokenType.IF; 063import static eu.cqse.check.framework.scanner.ETokenType.IMPORT; 064import static eu.cqse.check.framework.scanner.ETokenType.INITIAL_STEP; 065import static eu.cqse.check.framework.scanner.ETokenType.INTEGER_LITERAL; 066import static eu.cqse.check.framework.scanner.ETokenType.INTERFACE; 067import static eu.cqse.check.framework.scanner.ETokenType.INTERNAL; 068import static eu.cqse.check.framework.scanner.ETokenType.NON_RETAIN; 069import static eu.cqse.check.framework.scanner.ETokenType.OF; 070import static eu.cqse.check.framework.scanner.ETokenType.ON; 071import static eu.cqse.check.framework.scanner.ETokenType.ORGANIZATION_BLOCK; 072import static eu.cqse.check.framework.scanner.ETokenType.PLAUSIBILITY_FUNCTION; 073import static eu.cqse.check.framework.scanner.ETokenType.POSTUPDATE_ALGORITHM; 074import static eu.cqse.check.framework.scanner.ETokenType.PRIVATE; 075import static eu.cqse.check.framework.scanner.ETokenType.PROCESS_ALGORITHM; 076import static eu.cqse.check.framework.scanner.ETokenType.PROGRAM; 077import static eu.cqse.check.framework.scanner.ETokenType.PROTECTED; 078import static eu.cqse.check.framework.scanner.ETokenType.PUBLIC; 079import static eu.cqse.check.framework.scanner.ETokenType.REPEAT; 080import static eu.cqse.check.framework.scanner.ETokenType.RETAIN; 081import static eu.cqse.check.framework.scanner.ETokenType.RETURN; 082import static eu.cqse.check.framework.scanner.ETokenType.SEMICOLON; 083import static eu.cqse.check.framework.scanner.ETokenType.STEP; 084import static eu.cqse.check.framework.scanner.ETokenType.STRING_LITERAL; 085import static eu.cqse.check.framework.scanner.ETokenType.STRUCT; 086import static eu.cqse.check.framework.scanner.ETokenType.SYSTEM_OBJECT; 087import static eu.cqse.check.framework.scanner.ETokenType.SYSTEM_VAR; 088import static eu.cqse.check.framework.scanner.ETokenType.SYSTEM_VAR_DECL; 089import static eu.cqse.check.framework.scanner.ETokenType.SYSTEM_VAR_IN; 090import static eu.cqse.check.framework.scanner.ETokenType.THEN; 091import static eu.cqse.check.framework.scanner.ETokenType.TRANSITION; 092import static eu.cqse.check.framework.scanner.ETokenType.TYPE; 093import static eu.cqse.check.framework.scanner.ETokenType.UNTIL; 094import static eu.cqse.check.framework.scanner.ETokenType.VAR; 095import static eu.cqse.check.framework.scanner.ETokenType.VAR_ACCESS; 096import static eu.cqse.check.framework.scanner.ETokenType.VAR_CONFIG; 097import static eu.cqse.check.framework.scanner.ETokenType.VAR_EXTERNAL; 098import static eu.cqse.check.framework.scanner.ETokenType.VAR_GLOBAL; 099import static eu.cqse.check.framework.scanner.ETokenType.VAR_INPUT; 100import static eu.cqse.check.framework.scanner.ETokenType.VAR_IN_OUT; 101import static eu.cqse.check.framework.scanner.ETokenType.VAR_OUTPUT; 102import static eu.cqse.check.framework.scanner.ETokenType.VAR_TEMP; 103import static eu.cqse.check.framework.scanner.ETokenType.WHILE; 104import static eu.cqse.check.framework.scanner.ETokenType.WITH; 105import static eu.cqse.check.framework.scanner.ETokenType.XOR; 106 107import java.util.EnumSet; 108 109import eu.cqse.check.framework.scanner.ETokenType; 110import eu.cqse.check.framework.shallowparser.SubTypeNames; 111import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType; 112import eu.cqse.check.framework.shallowparser.framework.ExactIdentifierMatcher; 113import eu.cqse.check.framework.shallowparser.framework.RecognizerBase; 114import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase; 115import eu.cqse.check.framework.shallowparser.languages.iec61131.Iec61131ShallowParser.EIec61131ParserStates; 116 117/** 118 * Parser for the language IEC 61131-3 Structured Text. 119 * 120 * This parser does not (yet) support CONFIGURATION (see 121 * https://en.wikipedia.org/wiki/Structured_text), as we have no example code 122 * for this. 123 */ 124public class Iec61131ShallowParser extends ShallowParserBase<EIec61131ParserStates> { 125 126 /** The states used in this parser. */ 127 public enum EIec61131ParserStates { 128 129 /** Top-level state. */ 130 TOP_LEVEL, 131 132 /** In variable declarations. */ 133 IN_VARS, 134 135 /** In type declaration section. */ 136 IN_TYPES, 137 138 /** In methods, algorithms, etc. */ 139 IN_METHOD, 140 141 /** In case statement. */ 142 IN_CASE, 143 144 /** System var decl of SV Files. */ 145 IN_SYSTEM_VAR_DECL 146 } 147 148 /** 149 * A set of all token types that mark the beginning of a simple statement. 150 */ 151 private static final EnumSet<ETokenType> SIMPLE_STATEMENT_KEYWORDS = EnumSet.of(IDENTIFIER, STRING_LITERAL, RETURN, 152 EXIT); 153 154 /** A set of all token types that can be used as case literals. */ 155 private static final EnumSet<ETokenType> CASE_LITERALS = EnumSet.of(IDENTIFIER, INTEGER_LITERAL, 156 FLOATING_POINT_LITERAL); 157 158 /** Constructor. */ 159 public Iec61131ShallowParser() { 160 super(EIec61131ParserStates.class, EIec61131ParserStates.TOP_LEVEL); 161 162 createTopLevelRules(); 163 createInVariableRules(); 164 createInTypeDefinitionBlocksRules(); 165 createInMethodRules(); 166 createInSystemVarDeclRules(); 167 } 168 169 /** Creates the rules for parsing top-level elements. */ 170 private void createTopLevelRules() { 171 inState(EIec61131ParserStates.TOP_LEVEL).sequence(IMPORT).createNode(EShallowEntityType.META, 0) 172 .skipTo(END_IMPORT).endNode(); 173 174 inState(EIec61131ParserStates.TOP_LEVEL).sequence(ALGORITHM_BLOCK, IDENTIFIER) 175 .optional(WITH, INTERFACE, IDENTIFIER).createNode(EShallowEntityType.MODULE, 0, 1) 176 .parseUntil(EIec61131ParserStates.TOP_LEVEL).sequence(END_ALGORITHM_BLOCK).optional(SEMICOLON) 177 .endNode(); 178 179 inState(EIec61131ParserStates.TOP_LEVEL).sequence(EnumSet.of(VAR, SYSTEM_VAR, SYSTEM_VAR_IN, VAR_EXTERNAL)) 180 .createNode(EShallowEntityType.META, 0).parseUntil(EIec61131ParserStates.IN_VARS).sequence(END_VAR) 181 .optional(SEMICOLON).endNode(); 182 183 inState(EIec61131ParserStates.TOP_LEVEL).sequence(TYPE).createNode(EShallowEntityType.META, 0) 184 .parseUntil(EIec61131ParserStates.IN_TYPES).sequence(END_TYPE).optional(SEMICOLON).endNode(); 185 186 inState(EIec61131ParserStates.TOP_LEVEL).sequence(SYSTEM_OBJECT).createNode(EShallowEntityType.META, 0) 187 .parseUntil(EIec61131ParserStates.IN_VARS).sequence(END_OBJECT).endNode(); 188 189 createMethodLikeRule(ALGORITHM, END_ALGORITHM); 190 createMethodLikeRule(FUNCTION_BLOCK, END_FUNCTION_BLOCK); 191 createMethodLikeRule(ORGANIZATION_BLOCK, END_ORGANIZATION_BLOCK); 192 createMethodLikeRule(PROGRAM, END_PROGRAM); 193 194 inState(EIec61131ParserStates.TOP_LEVEL).sequence(PROCESS_ALGORITHM, IDENTIFIER, ON, IDENTIFIER) 195 .createNode(EShallowEntityType.METHOD, 0, 1).optional(AUTOSTART) 196 .parseUntil(EIec61131ParserStates.IN_METHOD).sequence(END_ALGORITHM).optional(SEMICOLON).endNode(); 197 198 createMethodLikeRule(EVENT_ALGORITHM, END_ALGORITHM); 199 200 inState(EIec61131ParserStates.TOP_LEVEL).sequence(EnumSet.of(EVENT_ALGORITHM, POSTUPDATE_ALGORITHM), IDENTIFIER) 201 .createNode(EShallowEntityType.METHOD, 0, 1).skipTo(WITH, IDENTIFIER).optional(XOR) 202 .repeated(COMMA, IDENTIFIER).parseUntil(EIec61131ParserStates.IN_METHOD).sequence(END_ALGORITHM) 203 .optional(SEMICOLON).endNode(); 204 205 inState(EIec61131ParserStates.TOP_LEVEL).sequence(EnumSet.of(FUNCTION, PLAUSIBILITY_FUNCTION), IDENTIFIER) 206 .optional(COLON, IDENTIFIER).createNode(EShallowEntityType.METHOD, 0, 1) 207 .parseUntil(EIec61131ParserStates.IN_METHOD).sequence(END_FUNCTION).optional(SEMICOLON).endNode(); 208 createMethodLikeRule(ACTION, END_ACTION); 209 210 inState(EIec61131ParserStates.TOP_LEVEL).sequence(SYSTEM_VAR_DECL).createNode(EShallowEntityType.TYPE, 0) 211 .parseUntil(EIec61131ParserStates.IN_SYSTEM_VAR_DECL).sequence(END).endNode(); 212 } 213 214 /** Creates a rule for method-like blocks. */ 215 private void createMethodLikeRule(Object startTokens, Object endTokens) { 216 inState(EIec61131ParserStates.TOP_LEVEL).sequence(startTokens, EnumSet.of(STRING_LITERAL, IDENTIFIER)) 217 .createNode(EShallowEntityType.METHOD, 0, 1).parseUntil(EIec61131ParserStates.IN_METHOD) 218 .sequence(endTokens).optional(SEMICOLON).endNode(); 219 } 220 221 /** Creates the rules for parsing within variable blocks. */ 222 private void createInVariableRules() { 223 inState(EIec61131ParserStates.IN_VARS).sequence(IDENTIFIER, COLON, STRUCT) 224 .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.VARIABLE, 0).skipTo(END_STRUCT) 225 .optional(SEMICOLON).endNode(); 226 227 inState(EIec61131ParserStates.IN_VARS).sequence(IDENTIFIER, COLON) 228 .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.VARIABLE, 0).skipTo(SEMICOLON).endNode(); 229 inState(EIec61131ParserStates.IN_VARS).sequence(SEMICOLON) 230 .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.EMPTY_STATEMENT, 0).endNode(); 231 } 232 233 /** Creates the rules for parsing within type-definition blocks. */ 234 private void createInTypeDefinitionBlocksRules() { 235 inState(EIec61131ParserStates.IN_TYPES).sequence(IDENTIFIER, COLON, STRUCT) 236 .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.TYPEDEF, 0).skipTo(END_STRUCT) 237 .optional(SEMICOLON).endNode(); 238 239 inState(EIec61131ParserStates.IN_TYPES).sequence(IDENTIFIER, COLON) 240 .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.TYPEDEF, 0).skipTo(SEMICOLON).endNode(); 241 } 242 243 /** Creates the rules for parsing within methods (statements). */ 244 private void createInMethodRules() { 245 inState(EIec61131ParserStates.IN_METHOD) 246 .sequence(EnumSet.of(VAR, VAR_INPUT, VAR_OUTPUT, VAR_IN_OUT, VAR_EXTERNAL, VAR_GLOBAL, VAR_ACCESS, 247 VAR_TEMP, VAR_CONFIG)) 248 .createNode(EShallowEntityType.META, 0).optional(EnumSet.of(CONSTANT, RETAIN, NON_RETAIN)) 249 .optional(EnumSet.of(PUBLIC, PROTECTED, PRIVATE, INTERNAL)).parseUntil(EIec61131ParserStates.IN_VARS) 250 .sequence(END_VAR).endNode(); 251 252 createCaseRule(); 253 createLoopRules(); 254 createIfElseRules(); 255 256 inState(EIec61131ParserStates.IN_METHOD) 257 .sequence(EnumSet.of(GO_ON_TRANSITION, TRANSITION, EXIT_TRANSITION, GO_ON_EXIT_TRANSITION)) 258 .createNode(EShallowEntityType.STATEMENT, 0).skipTo(END_TRANSITION).optional(SEMICOLON).endNode(); 259 260 inState(EIec61131ParserStates.IN_METHOD).sequence(ACTION, IDENTIFIER, COLON) 261 .createNode(EShallowEntityType.STATEMENT, 0, 1).parseUntil(EIec61131ParserStates.IN_METHOD) 262 .sequence(END_ACTION).optional(SEMICOLON).endNode(); 263 264 inState(EIec61131ParserStates.IN_METHOD).sequence(EnumSet.of(STEP, INITIAL_STEP), IDENTIFIER, COLON) 265 .createNode(EShallowEntityType.STATEMENT, 0, 1).parseUntil(EIec61131ParserStates.IN_METHOD) 266 .sequence(END_STEP).optional(SEMICOLON).endNode(); 267 268 // SCL annotations 269 inState(EIec61131ParserStates.IN_METHOD) 270 .sequence(IDENTIFIER, COLON, 271 EnumSet.of(IDENTIFIER, STRING_LITERAL, INTEGER_LITERAL, FLOATING_POINT_LITERAL)) 272 .createNode(EShallowEntityType.META, SubTypeNames.ANNOTATION, 0).endNode(); 273 inState(EIec61131ParserStates.IN_METHOD).sequence(new ExactIdentifierMatcher("TITLE"), EQ) 274 .skipAny(EnumSet.of(IDENTIFIER, STRING_LITERAL, INTEGER_LITERAL, FLOATING_POINT_LITERAL, OF)) 275 .createNode(EShallowEntityType.META, SubTypeNames.ANNOTATION, 0).endNode(); 276 277 // SCL BEGIN 278 inState(EIec61131ParserStates.IN_METHOD).sequence(BEGIN).createNode(EShallowEntityType.META, 0).endNode(); 279 280 createSimpleStatementRules(); 281 } 282 283 /** Creates the rules for parsing the case statement. */ 284 private void createCaseRule() { 285 inStatementStates().sequence(CASE).createNode(EShallowEntityType.STATEMENT, 0).skipTo(OF) 286 .parseUntil(EIec61131ParserStates.IN_CASE).sequence(END_CASE).optional(SEMICOLON).endNode(); 287 288 inState(EIec61131ParserStates.IN_CASE).repeated(CASE_LITERALS, COMMA).sequence(CASE_LITERALS, COLON) 289 .createNode(EShallowEntityType.META, SubTypeNames.CASE_LABEL).endNode(); 290 inState(EIec61131ParserStates.IN_CASE).sequence(ELSE) 291 .createNode(EShallowEntityType.META, SubTypeNames.CASE_ELSE).endNode(); 292 } 293 294 /** Returns the states that expect "plain" statements. */ 295 private RecognizerBase<EIec61131ParserStates> inStatementStates() { 296 return inState(EIec61131ParserStates.IN_METHOD, EIec61131ParserStates.IN_CASE); 297 } 298 299 /** Creates the rules for parsing loops. */ 300 private void createLoopRules() { 301 inStatementStates().sequence(FOR).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DO) 302 .parseUntil(EIec61131ParserStates.IN_METHOD).sequence(END_FOR).optional(SEMICOLON).endNode(); 303 304 inStatementStates().sequence(WHILE).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DO) 305 .parseUntil(EIec61131ParserStates.IN_METHOD).sequence(END_WHILE).optional(SEMICOLON).endNode(); 306 307 inStatementStates().sequence(REPEAT).createNode(EShallowEntityType.STATEMENT, 0) 308 .parseUntil(EIec61131ParserStates.IN_METHOD).sequence(UNTIL).skipTo(SEMICOLON).endNode(); 309 } 310 311 /** Create rules for parsing if/else structures. */ 312 private void createIfElseRules() { 313 RecognizerBase<EIec61131ParserStates> ifAlternative = inStatementStates().sequence(EnumSet.of(IF, ELSIF)) 314 .createNode(EShallowEntityType.STATEMENT, 0).skipTo(THEN).parseUntil(EIec61131ParserStates.IN_METHOD); 315 ifAlternative.sequenceBefore(EnumSet.of(ELSE, ELSIF)).endNodeWithContinuation(EIec61131ParserStates.IN_METHOD); 316 ifAlternative.sequence(END_IF).optional(SEMICOLON).endNode(); 317 318 // else is used in "if" (only use IN_METHOD here, as IN_CASE case is handled 319 // elsewhere) 320 inState(EIec61131ParserStates.IN_METHOD).sequence(ELSE).createNode(EShallowEntityType.STATEMENT, 0) 321 .parseUntil(EIec61131ParserStates.IN_METHOD).sequence(END_IF).optional(SEMICOLON).endNode(); 322 } 323 324 /** Create rules for parsing simple statements within methods. */ 325 private void createSimpleStatementRules() { 326 // simple statement 327 inStatementStates().sequence(SIMPLE_STATEMENT_KEYWORDS) 328 .createNode(EShallowEntityType.STATEMENT, SubTypeNames.SIMPLE_STATEMENT, 0).skipTo(SEMICOLON).endNode(); 329 330 // empty statement 331 inStatementStates().sequence(SEMICOLON).createNode(EShallowEntityType.STATEMENT, SubTypeNames.EMPTY_STATEMENT) 332 .endNode(); 333 } 334 335 /** Creates the rules for the system var decl section of SV files. */ 336 private void createInSystemVarDeclRules() { 337 // we parse these parts as attributes, as this makes it less likely to 338 // interface with structuring metrics. 339 inState(EIec61131ParserStates.IN_SYSTEM_VAR_DECL).sequence(IDENTIFIER) 340 .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.IEC_SV_VARIABLE, 0) 341 .skipBefore(EnumSet.of(SEMICOLON, ELEMENT)).optional(SEMICOLON).endNode(); 342 inState(EIec61131ParserStates.IN_SYSTEM_VAR_DECL).sequence(ELEMENT) 343 .createNode(EShallowEntityType.ATTRIBUTE, SubTypeNames.IEC_SV_ELEMENT) 344 .skipBefore(EnumSet.of(SEMICOLON, ELEMENT)).optional(SEMICOLON).endNode(); 345 } 346 347}