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.tsql;
018
019//TODO (DP): Leaving this here, as it comes in handy when working on the parser. Will remove it later.
020//import static eu.cqse.check.framework.scanner.ETokenType.*;
021//import static eu.cqse.check.framework.shallowparser.languages.tsql.TsqlShallowParser.ETsqlParserStates.*;
022//import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.*;
023//import static eu.cqse.check.framework.scanner.ETokenType.ETokenClass.*;
024import static eu.cqse.check.framework.scanner.ETokenType.ALL;
025import static eu.cqse.check.framework.scanner.ETokenType.BEGIN;
026import static eu.cqse.check.framework.scanner.ETokenType.BY;
027import static eu.cqse.check.framework.scanner.ETokenType.CREATE;
028import static eu.cqse.check.framework.scanner.ETokenType.DISTINCT;
029import static eu.cqse.check.framework.scanner.ETokenType.ELSE;
030import static eu.cqse.check.framework.scanner.ETokenType.END;
031import static eu.cqse.check.framework.scanner.ETokenType.EOF;
032import static eu.cqse.check.framework.scanner.ETokenType.EOL;
033import static eu.cqse.check.framework.scanner.ETokenType.EXCEPT;
034import static eu.cqse.check.framework.scanner.ETokenType.FOR;
035import static eu.cqse.check.framework.scanner.ETokenType.FROM;
036import static eu.cqse.check.framework.scanner.ETokenType.FUNCTION;
037import static eu.cqse.check.framework.scanner.ETokenType.GROUP;
038import static eu.cqse.check.framework.scanner.ETokenType.HAVING;
039import static eu.cqse.check.framework.scanner.ETokenType.IF;
040import static eu.cqse.check.framework.scanner.ETokenType.INTERSECT;
041import static eu.cqse.check.framework.scanner.ETokenType.INTO;
042import static eu.cqse.check.framework.scanner.ETokenType.LPAREN;
043import static eu.cqse.check.framework.scanner.ETokenType.OPTION;
044import static eu.cqse.check.framework.scanner.ETokenType.ORDER;
045import static eu.cqse.check.framework.scanner.ETokenType.PERCENT;
046import static eu.cqse.check.framework.scanner.ETokenType.RETURN;
047import static eu.cqse.check.framework.scanner.ETokenType.RETURNS;
048import static eu.cqse.check.framework.scanner.ETokenType.RPAREN;
049import static eu.cqse.check.framework.scanner.ETokenType.SELECT;
050import static eu.cqse.check.framework.scanner.ETokenType.SEMICOLON;
051import static eu.cqse.check.framework.scanner.ETokenType.TABLE;
052import static eu.cqse.check.framework.scanner.ETokenType.TOP;
053import static eu.cqse.check.framework.scanner.ETokenType.UNION;
054import static eu.cqse.check.framework.scanner.ETokenType.WHERE;
055import static eu.cqse.check.framework.scanner.ETokenType.WHILE;
056import static eu.cqse.check.framework.scanner.ETokenType.WITH;
057import static eu.cqse.check.framework.scanner.ETokenType.ETokenClass.KEYWORD;
058import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.META;
059import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.STATEMENT;
060import static eu.cqse.check.framework.shallowparser.languages.tsql.TsqlShallowParser.ETsqlParserStates.IN_BLOCK;
061import static eu.cqse.check.framework.shallowparser.languages.tsql.TsqlShallowParser.ETsqlParserStates.IN_METHOD;
062import static eu.cqse.check.framework.shallowparser.languages.tsql.TsqlShallowParser.ETsqlParserStates.TOP_LEVEL;
063
064import java.util.EnumSet;
065
066import org.conqat.lib.commons.region.Region;
067
068import eu.cqse.check.framework.scanner.ETokenType;
069import eu.cqse.check.framework.scanner.IToken;
070import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType;
071import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;
072import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase;
073
074/**
075 */
076public class TsqlShallowParser extends ShallowParserBase<TsqlShallowParser.ETsqlParserStates> {
077
078        /** The states used in this parser. */
079        public static enum ETsqlParserStates {
080                TOP_LEVEL, IN_METHOD, IN_BLOCK
081        }
082
083        private static final EnumSet<ETokenType> SELECT_STATEMENT_KEYWORDS = EnumSet.of(SELECT, WITH, ORDER, FOR, OPTION,
084                        UNION, ALL, EXCEPT, INTERSECT, DISTINCT, TOP, PERCENT, INTO, FROM, WHERE, GROUP, BY, HAVING);
085
086        private static final EnumSet<ETokenType> NO_SELECT_STATEMENT_KEYWORDS = EnumSet.copyOf(ETokenType.KEYWORDS);
087
088        {
089                NO_SELECT_STATEMENT_KEYWORDS.removeAll(SELECT_STATEMENT_KEYWORDS);
090        }
091
092        /** Constructor. */
093        public TsqlShallowParser() {
094                super(ETsqlParserStates.class, ETsqlParserStates.TOP_LEVEL);
095
096                createMethodRules();
097                createStatementRules();
098        }
099
100        /** Creates parser rules for statements. */
101        private void createStatementRules() {
102
103                createBlockStatementRule(IF);
104                createBlockStatementRule(ELSE);
105                createBlockStatementRule(WHILE);
106
107                inAnyState().sequence(BEGIN).createNode(STATEMENT, 0, new Region(1, -1)).parseUntil(IN_BLOCK).sequence(END)
108                                .endNode();
109
110                inAnyState().sequence(SELECT).createNode(STATEMENT, 0).skipBefore(NO_SELECT_STATEMENT_KEYWORDS).endNode();
111
112                inAnyState().markStart().sequence(KEYWORD).skipBefore(KEYWORD).createNode(STATEMENT, 0).endNode();
113
114                // TODO (DP): EOF is not recognized, so that the node is always
115                // incomplete. Should be STATEMENT, not META
116                inAnyState().sequence(KEYWORD).createNode(META, 0).skipBefore(EOF).endNode();
117
118        }
119
120        private void createBlockStatementRule(ETokenType blockStatementType) {
121                inAnyState().sequence(blockStatementType).skipNested(LPAREN, RPAREN).skipBefore(KEYWORD).sequence(BEGIN)
122                                .createNode(STATEMENT, 0).parseUntil(IN_BLOCK).sequence(END).endNode();
123        }
124
125        /** Creates parser rules for functions and procedures. */
126        private void createMethodRules() {
127                // TODO (DP): We might need to create an Identifier set, which copes
128                // with [FUNCTION] or [My cool table name]
129
130                RecognizerBase<ETsqlParserStates> methodStart = inState(TOP_LEVEL).sequence(CREATE).markStart()
131                                .sequence(FUNCTION).createNode(EShallowEntityType.METHOD, 0).skipTo(RETURNS);
132
133                // T-SQL Scalar Function Syntax
134                methodStart.skipTo(BEGIN).parseUntil(IN_METHOD).sequence(END).endNode();
135
136                // T-SQL Inline Table-Valued Function Syntax
137                methodStart.sequence(TABLE).skipTo(RETURN).parseUntil(IN_METHOD).sequence(END).endNode();
138        }
139
140        /** {@inheritDoc} */
141        @Override
142        protected boolean isFilteredToken(IToken token, IToken previousToken) {
143                return super.isFilteredToken(token, previousToken) || token.getType() == EOL || token.getType() == SEMICOLON;
144        }
145
146}