1 /*
2  * Copyright 2015-2018 HuntLabs.cn
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 module hunt.sql.dialect.postgresql.parser.PGLexer;
17 
18 import  hunt.sql.parser.CharTypes;
19 import  hunt.sql.parser.Token;
20 
21 import hunt.collection;
22 import hunt.sql.parser;
23 import hunt.sql.util.DBType;
24 import hunt.logging;
25 
26 import std.concurrency : initOnce;
27 
28 public class PGLexer : Lexer {
29 
30     // public  static Keywords DEFAULT_PG_KEYWORDS;
31 
32     static Keywords DEFAULT_PG_KEYWORDS() {
33         __gshared Keywords inst;
34         return initOnce!inst(initKeyWords());
35     }
36 
37     private static Keywords initKeyWords() {
38         Map!(string, Token) map = new HashMap!(string, Token)();
39 
40         map.putAll(Keywords.DEFAULT_KEYWORDS.getKeywords());
41 
42         map.put("BEGIN", Token.BEGIN);
43         map.put("CASCADE", Token.CASCADE);
44         map.put("CONTINUE", Token.CONTINUE);
45         map.put("CURRENT", Token.CURRENT);
46         map.put("FETCH", Token.FETCH);
47         map.put("FIRST", Token.FIRST);
48 
49         map.put("IDENTITY", Token.IDENTITY);
50         map.put("LIMIT", Token.LIMIT);
51         map.put("NEXT", Token.NEXT);
52         map.put("NOWAIT", Token.NOWAIT);
53         map.put("OF", Token.OF);
54 
55         map.put("OFFSET", Token.OFFSET);
56         map.put("ONLY", Token.ONLY);
57         map.put("RECURSIVE", Token.RECURSIVE);
58         map.put("RESTART", Token.RESTART);
59 
60         map.put("RESTRICT", Token.RESTRICT);
61         map.put("RETURNING", Token.RETURNING);
62         map.put("ROW", Token.ROW);
63         map.put("ROWS", Token.ROWS);
64         map.put("SHARE", Token.SHARE);
65         map.put("SHOW", Token.SHOW);
66         map.put("START", Token.START);
67         
68         map.put("USING", Token.USING);
69         map.put("WINDOW", Token.WINDOW);
70         
71         map.put("TRUE", Token.TRUE);
72         map.put("FALSE", Token.FALSE);
73         map.put("ARRAY", Token.ARRAY);
74         map.put("IF", Token.IF);
75         map.put("TYPE", Token.TYPE);
76         map.put("ILIKE", Token.ILIKE);
77 
78         return new Keywords(map);
79     }
80 
81     public this(string input, SQLParserFeature[] features...){
82         super(input);
83         super.keywods = DEFAULT_PG_KEYWORDS;
84         super.dbType = DBType.POSTGRESQL.name;
85         foreach(SQLParserFeature feature ; features) {
86             config(feature, true);
87         }
88     }
89     
90     override protected void scanString() {
91         _mark = _pos;
92         bool hasSpecial = false;
93 
94         for (;;) {
95             // logDebug("ch : ",ch);
96             if (isEOF()) {
97                 lexError("unclosed.str.lit");
98                 return;
99             }
100 
101             ch = charAt(++_pos);
102 
103             if (ch == '\\') {
104                 scanChar();
105                 if (!hasSpecial) {
106                     initBuff(bufPos);
107                     arraycopy(_mark + 1, buf, 0, bufPos);
108                     hasSpecial = true;
109                 }
110 
111                 putChar('\\');
112                 switch (ch) {
113                     case '\0':
114                         putChar('\0');
115                         break;
116                     case '\'':
117                         putChar('\'');
118                         break;
119                     case '"':
120                         putChar('"');
121                         break;
122                     case 'b':
123                         putChar('\b');
124                         break;
125                     case 'n':
126                         putChar('\n');
127                         break;
128                     case 'r':
129                         putChar('\r');
130                         break;
131                     case 't':
132                         putChar('\t');
133                         break;
134                     case '\\':
135                         putChar('\\');
136                         break;
137                     case 'Z':
138                         putChar(cast(char) 0x1A); // ctrl + Z
139                         break;
140                     default:
141                         putChar(ch);
142                         break;
143                 }
144                 scanChar();
145             }
146 
147             if (ch == '\'') {
148                 scanChar();
149                 if (ch != '\'') {
150                     _token = Token.LITERAL_CHARS;
151                     break;
152                 } else {
153                     initBuff(bufPos);
154                     arraycopy(_mark + 1, buf, 0, bufPos);
155                     // logDebugf("bufPos :%d ,mark : %d , buf : %s",bufPos,_mark,buf);
156                     hasSpecial = true;
157                     putChar('\'');
158                     // putChar('\'');
159                     continue;
160                 }
161             }
162 
163             if (!hasSpecial) {
164                 bufPos++;
165                 continue;
166             }
167 
168             if (bufPos == buf.length) {
169                 putChar(ch);
170             } else {
171                 buf[bufPos++] = ch;
172             }
173         }
174 
175         if (!hasSpecial) {
176             _stringVal = subString(_mark + 1, bufPos);
177         } else {
178             _stringVal = cast(string)buf[0..bufPos];
179         }
180     }
181     
182     override public void scanSharp() {
183         scanChar();
184         if (ch == '>') {
185             scanChar();
186             if (ch == '>') {
187                 scanChar();
188                 _token = Token.POUNDGTGT;
189             } else {
190                 _token = Token.POUNDGT;
191             }
192         } else {
193             _token = Token.POUND;
194         }
195     }
196 
197     override protected void scanVariable_at() {
198         if (ch != '@') {
199             throw new ParserException("illegal variable. "  ~ info());
200         }
201 
202         _mark = _pos;
203         bufPos = 1;
204         char ch;
205 
206          char c1 = charAt(_pos + 1);
207         if (c1 == '@') {
208             _pos += 2;
209             _token = Token.MONKEYS_AT_AT;
210             this.ch = charAt(++_pos);
211             return;
212         } else if (c1 == '>') {
213             _pos += 2;
214             _token = Token.MONKEYS_AT_GT;
215             this.ch = charAt(++_pos);
216             return;
217         }
218 
219         for (;;) {
220             ch = charAt(++_pos);
221 
222             if (!CharTypes.isIdentifierChar(ch)) {
223                 break;
224             }
225 
226             bufPos++;
227             continue;
228         }
229 
230         this.ch = charAt(_pos);
231 
232         _stringVal = addSymbol();
233         _token = Token.VARIANT;
234     }
235 }