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}