001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright (c) 2009-2017 CQSE GmbH                                        |
004|                                                                          |
005+-------------------------------------------------------------------------*/
006package eu.cqse.check.framework.shallowparser.languages.swift;
007
008import static eu.cqse.check.framework.scanner.ETokenType.ARROW;
009import static eu.cqse.check.framework.scanner.ETokenType.AT;
010import static eu.cqse.check.framework.scanner.ETokenType.BOOLEAN_LITERAL;
011import static eu.cqse.check.framework.scanner.ETokenType.BREAK;
012import static eu.cqse.check.framework.scanner.ETokenType.CASE;
013import static eu.cqse.check.framework.scanner.ETokenType.CLASS;
014import static eu.cqse.check.framework.scanner.ETokenType.COLON;
015import static eu.cqse.check.framework.scanner.ETokenType.COMMA;
016import static eu.cqse.check.framework.scanner.ETokenType.CONTINUE;
017import static eu.cqse.check.framework.scanner.ETokenType.DEFAULT;
018import static eu.cqse.check.framework.scanner.ETokenType.DEINIT;
019import static eu.cqse.check.framework.scanner.ETokenType.DOT;
020import static eu.cqse.check.framework.scanner.ETokenType.ELSE;
021import static eu.cqse.check.framework.scanner.ETokenType.ENUM;
022import static eu.cqse.check.framework.scanner.ETokenType.EOL;
023import static eu.cqse.check.framework.scanner.ETokenType.EXCLAMATION;
024import static eu.cqse.check.framework.scanner.ETokenType.EXTENSION;
025import static eu.cqse.check.framework.scanner.ETokenType.FILEPRIVATE;
026import static eu.cqse.check.framework.scanner.ETokenType.FLOATING_POINT_LITERAL;
027import static eu.cqse.check.framework.scanner.ETokenType.FOR;
028import static eu.cqse.check.framework.scanner.ETokenType.FUNC;
029import static eu.cqse.check.framework.scanner.ETokenType.GT;
030import static eu.cqse.check.framework.scanner.ETokenType.GUARD;
031import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER;
032import static eu.cqse.check.framework.scanner.ETokenType.IF;
033import static eu.cqse.check.framework.scanner.ETokenType.IMPORT;
034import static eu.cqse.check.framework.scanner.ETokenType.IN;
035import static eu.cqse.check.framework.scanner.ETokenType.INIT;
036import static eu.cqse.check.framework.scanner.ETokenType.INTEGER_LITERAL;
037import static eu.cqse.check.framework.scanner.ETokenType.INTERNAL;
038import static eu.cqse.check.framework.scanner.ETokenType.LBRACE;
039import static eu.cqse.check.framework.scanner.ETokenType.LBRACK;
040import static eu.cqse.check.framework.scanner.ETokenType.LET;
041import static eu.cqse.check.framework.scanner.ETokenType.LPAREN;
042import static eu.cqse.check.framework.scanner.ETokenType.LT;
043import static eu.cqse.check.framework.scanner.ETokenType.OPEN;
044import static eu.cqse.check.framework.scanner.ETokenType.PREPROCESSOR_DIRECTIVE;
045import static eu.cqse.check.framework.scanner.ETokenType.PRIVATE;
046import static eu.cqse.check.framework.scanner.ETokenType.PROTOCOL;
047import static eu.cqse.check.framework.scanner.ETokenType.PUBLIC;
048import static eu.cqse.check.framework.scanner.ETokenType.QUESTION;
049import static eu.cqse.check.framework.scanner.ETokenType.RBRACE;
050import static eu.cqse.check.framework.scanner.ETokenType.RBRACK;
051import static eu.cqse.check.framework.scanner.ETokenType.REPEAT;
052import static eu.cqse.check.framework.scanner.ETokenType.RETURN;
053import static eu.cqse.check.framework.scanner.ETokenType.RPAREN;
054import static eu.cqse.check.framework.scanner.ETokenType.SELF;
055import static eu.cqse.check.framework.scanner.ETokenType.SEMICOLON;
056import static eu.cqse.check.framework.scanner.ETokenType.STRING_LITERAL;
057import static eu.cqse.check.framework.scanner.ETokenType.STRUCT;
058import static eu.cqse.check.framework.scanner.ETokenType.SUPER;
059import static eu.cqse.check.framework.scanner.ETokenType.SWITCH;
060import static eu.cqse.check.framework.scanner.ETokenType.THROW;
061import static eu.cqse.check.framework.scanner.ETokenType.VAR;
062import static eu.cqse.check.framework.scanner.ETokenType.WHILE;
063import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.METHOD;
064import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.STATEMENT;
065import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.TYPE;
066import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_EXPRESSION;
067import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_METHOD;
068import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.IN_TYPE;
069import static eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates.TOP_LEVEL;
070
071import java.util.Arrays;
072import java.util.EnumSet;
073
074import org.conqat.lib.commons.region.Region;
075
076import eu.cqse.check.framework.scanner.ETokenType;
077import eu.cqse.check.framework.scanner.IToken;
078import eu.cqse.check.framework.shallowparser.SubTypeNames;
079import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType;
080import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;
081import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase;
082import eu.cqse.check.framework.shallowparser.languages.base.EGenericParserStates;
083
084/**
085 * A shallow parser for the Swift programming language.
086 * 
087 * <b>Notes:</b> Currently lambdas are only recognized in simple statements.
088 */
089public class SwiftShallowParser extends ShallowParserBase<EGenericParserStates> {
090
091        /** PHP visibility modifiers. */
092        private static final EnumSet<ETokenType> VISIBILITY_MODIFIERS = EnumSet.of(OPEN, PUBLIC, INTERNAL, PRIVATE,
093                        FILEPRIVATE);
094
095        /** All token types that may stand at the beginning of new statement. */
096        private static final EnumSet<ETokenType> STATEMENT_START_TOKENS = EnumSet.of(IDENTIFIER, RETURN, BREAK, CONTINUE,
097                        THROW, SUPER, SELF, LPAREN, INTEGER_LITERAL, BOOLEAN_LITERAL, STRING_LITERAL, FLOATING_POINT_LITERAL);
098
099        /** All tokens that are valid statement separators. */
100        private static final EnumSet<ETokenType> STATEMENT_SEPARATORS = EnumSet.of(EOL, SEMICOLON, RBRACE);
101
102        /** Token types with are allowed for type definitions. */
103        private static final EnumSet<ETokenType> VALID_TYPE_TOKEN_TYPES = EnumSet.of(IDENTIFIER, QUESTION, DOT, LPAREN,
104                        RPAREN, ARROW, COMMA, LT, GT, LBRACK, RBRACK, EXCLAMATION, EOL);
105
106        /** Constructor. */
107        public SwiftShallowParser() {
108                super(EGenericParserStates.class, TOP_LEVEL);
109
110                createMetaRules();
111                createTypeRules();
112                createMethodRules();
113                createStatementRules();
114                createSubExpressionRules();
115
116                // remove any isolated EOLs
117                inAnyState().sequence(EOL);
118        }
119
120        /** Creates rules for meta elements. */
121        private void createMetaRules() {
122                inState(TOP_LEVEL).sequence(IMPORT).markStart().skipBefore(EOL)
123                                .createNode(EShallowEntityType.META, SubTypeNames.IMPORT, new Region(0, -1)).endNode();
124                inAnyState().sequence(AT, IDENTIFIER).skipNested(LPAREN, RPAREN)
125                                .createNode(EShallowEntityType.META, SubTypeNames.ANNOTATION, 1).endNode();
126
127                inAnyState().sequence(PREPROCESSOR_DIRECTIVE).skipTo(EOL)
128                                .createNode(EShallowEntityType.META, SubTypeNames.PREPROCESSOR_DIRECTIVE).endNode();
129        }
130
131        /** Creates sub expression rules. */
132        private void createSubExpressionRules() {
133                finishLambdaRule(
134                                inState(IN_EXPRESSION)
135                                                .sequence(LBRACE).skipBeforeWithNesting(EnumSet.of(IN, RBRACE),
136                                                                Arrays.asList(LPAREN, LBRACK, LBRACE), Arrays.asList(RPAREN, RBRACK, RBRACE))
137                                                .sequence(IN));
138                finishLambdaRule(inState(IN_EXPRESSION).sequence(LBRACE));
139        }
140
141        /** Finishes the given lambda rule. */
142        private static void finishLambdaRule(RecognizerBase<EGenericParserStates> lambdaRule) {
143                lambdaRule.createNode(EShallowEntityType.METHOD, SubTypeNames.LAMBDA).parseUntil(IN_METHOD).sequence(RBRACE)
144                                .endNode();
145        }
146
147        /** Creates the statement rules. */
148        private void createStatementRules() {
149                createLoopRules();
150                createConditionalRules();
151                createSimpleStatementRule();
152                createSwitchStatementRule();
153                createLocalVariableRule(VAR);
154                createLocalVariableRule(LET);
155        }
156
157        /** Creates a rule for a local variable declaration */
158        private void createLocalVariableRule(ETokenType keyword) {
159                inState(TOP_LEVEL, IN_METHOD).sequence(keyword)
160                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.LOCAL_VARIABLE, 1)
161                                .skipToWithNesting(EOL, Arrays.asList(LPAREN, LBRACE, LBRACK), Arrays.asList(RPAREN, RBRACE, RBRACK),
162                                                createSubExpressionRecognizer())
163                                .endNode();
164        }
165
166        /** Creates a rule for a switch/case statement. */
167        private void createSwitchStatementRule() {
168                inState(TOP_LEVEL, IN_METHOD).sequence(SWITCH).createNode(EShallowEntityType.STATEMENT, SubTypeNames.SWITCH)
169                                .skipNested(LPAREN, RPAREN, createSubExpressionRecognizer()).skipTo(LBRACE).parseUntil(IN_METHOD)
170                                .sequence(RBRACE).endNode();
171                inState(IN_METHOD).sequence(CASE).createNode(EShallowEntityType.META, SubTypeNames.CASE)
172                                .skipToWithNesting(COLON, LBRACK, RBRACK).endNode();
173                inState(IN_METHOD).sequence(DEFAULT).createNode(EShallowEntityType.META, SubTypeNames.DEFAULT).skipTo(COLON)
174                                .endNode();
175        }
176
177        /** Creates a simple statement rule. */
178        private void createSimpleStatementRule() {
179                inState(TOP_LEVEL, IN_METHOD).sequence(SEMICOLON)
180                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.EMPTY_STATEMENT).endNode();
181
182                RecognizerBase<EGenericParserStates> statementRule = inState(TOP_LEVEL, IN_METHOD);
183                finishSimpleStatementRule(statementRule.sequenceBefore(LPAREN));
184                finishSimpleStatementRule(statementRule.sequence(STATEMENT_START_TOKENS));
185
186        }
187
188        /** Finishes a simple statement rule. */
189        private void finishSimpleStatementRule(RecognizerBase<EGenericParserStates> statementRule) {
190                statementRule.createNode(EShallowEntityType.STATEMENT, SubTypeNames.SIMPLE_STATEMENT, 0)
191                                .skipBeforeWithNesting(STATEMENT_SEPARATORS, Arrays.asList(LPAREN, LBRACE, LBRACK),
192                                                Arrays.asList(RPAREN, RBRACE, RBRACK), createSubExpressionRecognizer())
193                                .optional(EnumSet.of(SEMICOLON, EOL)).endNode();
194        }
195
196        /** Creates rules for conditional statements. */
197        private void createConditionalRules() {
198                createConditionalRule(SubTypeNames.IF_LET, IF, LET);
199                createConditionalRule(SubTypeNames.IF_VAR, IF, VAR);
200                createConditionalRule(SubTypeNames.IF, IF);
201                createConditionalRule(SubTypeNames.ELSE_IF, ELSE, IF);
202                createConditionalRule(SubTypeNames.ELSE, ELSE);
203                createConditionalRule(SubTypeNames.GUARD, GUARD);
204        }
205
206        /** Creates a rule for a conditional. */
207        private void createConditionalRule(String subtype, Object... matchTerms) {
208                endWithPossibleContinuation(
209                                inState(TOP_LEVEL, IN_METHOD).sequence(matchTerms).skipToWithNesting(LBRACE, LPAREN, RPAREN)
210                                                .createNode(STATEMENT, subtype).parseUntil(IN_METHOD).sequence(RBRACE),
211                                EnumSet.of(ELSE));
212        }
213
214        /** Creates rules for loops */
215        private void createLoopRules() {
216                // repeat while
217                inState(TOP_LEVEL, IN_METHOD).sequence(REPEAT)
218                                .createNode(EShallowEntityType.STATEMENT, SubTypeNames.REPEAT_WHILE)
219                                .skipToWithNesting(LBRACE, LPAREN, RPAREN).parseUntil(IN_METHOD).sequence(RBRACE, WHILE).skipTo(EOL)
220                                .endNode();
221
222                createWhileRule(SubTypeNames.WHILE_LET, WHILE, LET);
223                createWhileRule(0, EnumSet.of(FOR, WHILE, REPEAT));
224        }
225
226        /** Creates a while rule */
227        private void createWhileRule(Object subtype, Object... matchTerms) {
228                inState(TOP_LEVEL, IN_METHOD).sequence(matchTerms).createNode(EShallowEntityType.STATEMENT, subtype)
229                                .skipToWithNesting(LBRACE, LPAREN, RPAREN).parseUntil(IN_METHOD).sequence(RBRACE).endNode();
230        }
231
232        /** Creates rules for types */
233        public void createTypeRules() {
234                createTypeRule(CLASS, SubTypeNames.CLASS);
235                createTypeRule(PROTOCOL, SubTypeNames.PROTOCOL);
236                createTypeRule(STRUCT, SubTypeNames.STRUCT);
237                createTypeRule(ENUM, SubTypeNames.ENUM);
238                createTypeRule(EXTENSION, SubTypeNames.EXTENSION);
239
240                createPropertyRule(VAR, SubTypeNames.ATTRIBUTE);
241                createPropertyRule(LET, SubTypeNames.ATTRIBUTE);
242                createPropertyRule(CASE, SubTypeNames.ENUM_LITERAL);
243        }
244
245        /** Creates a property rule */
246        private void createPropertyRule(ETokenType propertyKeyword, String subtype) {
247                inState(IN_TYPE).optional(VISIBILITY_MODIFIERS).markStart().sequence(propertyKeyword, IDENTIFIER)
248                                .createNode(EShallowEntityType.ATTRIBUTE, subtype, 1).skipToWithNesting(EOL, LBRACE, RBRACE).endNode();
249        }
250
251        /** Creates a type rule for the given keyword */
252        private void createTypeRule(ETokenType typeTokenType, String subTypeName) {
253                inState(TOP_LEVEL).optional(VISIBILITY_MODIFIERS).sequence(typeTokenType, IDENTIFIER)
254                                .createNode(TYPE, subTypeName, -1).skipTo(LBRACE).parseUntil(IN_TYPE).sequence(RBRACE).endNode();
255        }
256
257        /** Creates the method rules */
258        private void createMethodRules() {
259                finishMethodRule(inState(TOP_LEVEL, IN_TYPE).optional(VISIBILITY_MODIFIERS).sequence(FUNC, IDENTIFIER),
260                                SubTypeNames.METHOD);
261
262                createConstructorDestructorRule(INIT, SubTypeNames.CONSTRUCTOR);
263                createConstructorDestructorRule(DEINIT, SubTypeNames.DESTRUCTOR);
264
265        }
266
267        /** Creates a constructor/destructor rule. */
268        private void createConstructorDestructorRule(ETokenType keywordToken, String subtype) {
269                finishMethodRule(inState(IN_TYPE).optional(VISIBILITY_MODIFIERS).sequence(keywordToken), subtype);
270        }
271
272        /** Finishes the given method rule for the specified subtype */
273        private void finishMethodRule(RecognizerBase<EGenericParserStates> rule, String subtype) {
274                RecognizerBase<EGenericParserStates> methodHeaderRule = rule.createNode(METHOD, subtype, -1)
275                                .skipNested(LPAREN, RPAREN).optionalSubRecognizer(createReturnTypeRecognizer());
276                methodHeaderRule.repeated(EOL).sequence(LBRACE).parseUntil(IN_METHOD).sequence(RBRACE).endNode();
277                methodHeaderRule.endNode();
278        }
279
280        /** Matches a return type definition like -> SomeType? */
281        private RecognizerBase<EGenericParserStates> createReturnTypeRecognizer() {
282                return createRecognizer(start -> start.repeated(EOL).sequence(ARROW).repeated(VALID_TYPE_TOKEN_TYPES));
283        }
284
285        /** Creates a recognizer that matches lambdas within expressions. */
286        private RecognizerBase<EGenericParserStates> createSubExpressionRecognizer() {
287                return createRecognizer(start -> start.sequenceBefore(LBRACE).parseOnce(EGenericParserStates.IN_EXPRESSION));
288        }
289
290        /** {@inheritDoc} */
291        @Override
292        protected boolean isFilteredToken(IToken token, IToken previousToken) {
293                return super.isFilteredToken(token, previousToken)
294                                || (previousToken != null && previousToken.getType() == EOL && token.getType() == EOL);
295        }
296
297}