Make a copy of the old ZipLib
[mono.git] / mcs / class / System.Data / Mono.Data.SqlExpressions / Tokenizer.cs
1 //
2 // SqlWhereClauseTokenizer.cs
3 //
4 // Author:
5 //   Juraj Skripsky (juraj@hotfeet.ch)
6 //
7 // (C) 2004 HotFeet GmbH (http://www.hotfeet.ch)
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Data;
35 using System.IO;
36 using System.Text;
37 using System.Collections;
38
39 namespace Mono.Data.SqlExpressions {
40         internal class Tokenizer : yyParser.yyInput {
41                 private static readonly IDictionary tokenMap = new Hashtable ();
42                 private static readonly Object [] tokens = {
43                         Token.AND, "and",
44                         Token.OR, "or",
45                         Token.NOT, "not",
46                         
47                         Token.TRUE, "true",
48                         Token.FALSE, "false",
49                         Token.NULL, "null",
50                         
51                         Token.PARENT, "parent",
52                         Token.CHILD, "child",
53                         
54                         Token.IS, "is",
55                         Token.IN, "in",
56                         Token.LIKE, "like",
57                         
58                         Token.COUNT, "count",
59                         Token.SUM, "sum",
60                         Token.AVG, "avg",
61                         Token.MAX, "max",
62                         Token.MIN, "min",
63                         Token.STDEV, "stdev",
64                         Token.VAR, "var",
65                         
66                         Token.IIF, "iif",
67                         Token.SUBSTRING, "substring",
68                         Token.ISNULL, "isnull",
69                         Token.LEN, "len",
70                         Token.TRIM, "trim",
71                         Token.CONVERT, "convert"
72                 };
73                 private char[] input;
74                 private int pos;
75
76                 private int tok;
77                 private object val;
78
79                 static Tokenizer ()
80                 {
81                         for (int i = 0; i < tokens.Length; i += 2)
82                                 tokenMap.Add (tokens [i + 1], tokens [i]);
83                 }
84
85                 public Tokenizer (string strInput)
86                 {
87                         input = strInput.ToCharArray ();
88                         pos = 0;
89                 }
90
91                 private char Current() {
92                         return input [pos];
93                 }
94
95                 private char Next() {
96                         if (pos + 1 >= input.Length)
97                                 return (char)0;
98                         return input [pos + 1];
99                 }
100
101                 private bool MoveNext() {
102                         pos++;
103                         if (pos >= input.Length)
104                                 return false;
105
106                         return true;
107                 }
108                 
109                 private bool SkipWhiteSpace ()
110                 {
111                         if (pos >= input.Length)
112                                 return false;
113
114                         while (Char.IsWhiteSpace (Current ())) {
115                                 if (!MoveNext ())
116                                         return false;
117                         }
118
119                         return true;
120                 }
121
122                 private object ReadNumber ()
123                 {
124                         StringBuilder sb = new StringBuilder ();
125                         sb.Append (Current ());
126
127                         char next;
128                         while (Char.IsDigit (next = Next ()) || next == '.') {
129                                 sb.Append (next);
130                                 if (!MoveNext ())
131                                         break;
132                         }
133
134                         string str = sb.ToString ();
135
136                         if (str.IndexOf(".") == -1)
137                                 return Int64.Parse (str);
138                         else
139                                 return double.Parse (str);
140                 }
141
142                 private char ProcessEscapes(char c)
143                 {
144                         if (c == '\\') {
145                                 if (MoveNext())
146                                         c = Next();
147                                 else
148                                         c = '\0';
149
150                                 switch (c) {
151                                 case 'n':
152                                         c = '\n';
153                                         break;
154                                 case 'r':
155                                         c = '\r';
156                                         break;
157                                 case 't':
158                                         c = '\t';
159                                         break;
160
161                                 case '\\':
162                                         c = '\\';
163                                         break;
164                                         
165                                 default:
166                                         throw new SyntaxErrorException (String.Format ("Invalid escape sequence: '\\{0}'.", c));
167                                 }
168                         }
169                         return c;
170                 }
171
172                 private string ReadString (char terminator)
173                 {
174                         return ReadString (terminator, false /* canEscape */);
175                 }
176
177                 private string ReadString (char terminator, 
178                                            bool canEscape // twice the terminator is not a terminator
179                                            )
180                 {
181                         bool terminated = false;
182                         StringBuilder sb = new StringBuilder ();
183                         while (MoveNext ()) {
184                                 if (Current () == terminator) {
185                                         if (Next () == terminator) {
186                                                 sb.Append (ProcessEscapes (Current ()));
187                                                 MoveNext ();
188                                                 continue;
189                                         }
190                                         terminated = true;
191                                         break;
192                                 }
193                                 sb.Append (ProcessEscapes (Current ()));
194                         }
195                         
196                         if (! terminated)
197                                 throw new SyntaxErrorException (String.Format ("invalid string at {0}{1}<--",
198                                                                                terminator,
199                                                                                sb.ToString ())
200                                                                 );
201                         return sb.ToString ();                  
202                 }
203
204                 private string ReadIdentifier ()
205                 {
206                         StringBuilder sb = new StringBuilder ();
207                         sb.Append (Current ());
208
209                         char next;
210                         while ((next = Next ()) == '_' || Char.IsLetterOrDigit (next) || next == '\\') {
211                                 sb.Append (ProcessEscapes (next));                              
212                                 if (!MoveNext ())
213                                         break;
214                         }
215
216                         return sb.ToString ();
217                 }
218
219                 private int ParseIdentifier ()
220                 {
221                         string strToken = ReadIdentifier ();
222                         object tokenObj = tokenMap[strToken.ToLower()];
223
224                         if(tokenObj != null)
225                                 return (int)tokenObj;
226                         
227                         val = strToken;
228                         return Token.Identifier;
229                 }
230
231                 private int ParseToken ()
232                 {
233                         char cur;
234                         switch (cur = Current ()) {
235                         case '(':
236                                 return Token.PAROPEN;
237
238                         case ')':
239                                 return Token.PARCLOSE;
240
241                         case '.':
242                                 return Token.DOT;
243
244                         case ',':
245                                 return Token.COMMA;
246
247                         case '+':
248                                 return Token.PLUS;
249
250                         case '-':
251                                 return Token.MINUS;
252
253                         case '*':
254                                 return Token.MUL;
255
256                         case '/':
257                                 return Token.DIV;
258
259                         case '%':
260                                 return Token.MOD;
261                                 
262                         case '=':
263                                 return Token.EQ;
264
265                         case '<':
266                                 return Token.LT;
267
268                         case '>':
269                                 return Token.GT;
270
271                         case '[':
272                                 val = ReadString (']');
273                                 return Token.Identifier;
274
275                         case '#':
276                                 string date = ReadString ('#');
277                                 val = DateTime.Parse (date);
278                                 return Token.DateLiteral;
279
280                         case '\'':
281                         case '\"':
282                                 val = ReadString (cur, true);
283                                 return Token.StringLiteral;
284
285                         default:
286                                 if (Char.IsDigit (cur)) {                               
287                                         val = ReadNumber ();
288                                         return Token.NumberLiteral;
289                                 } else if (Char.IsLetter (cur) || cur == '_')
290                                         return ParseIdentifier ();
291                                 break;
292                         }
293                         throw new SyntaxErrorException ("invalid token: '" + cur + "'");
294                 }
295
296                 ///////////////////////////
297                 // yyParser.yyInput methods
298                 ///////////////////////////
299
300                 /** move on to next token.
301                   @return false if positioned beyond tokens.
302                   @throws IOException on input error.
303                   */
304                 public bool advance ()
305                 {
306                         if (!SkipWhiteSpace())
307                                 return false;
308                         tok = ParseToken();
309                         MoveNext ();
310                         return true;
311                 }
312
313                 /** classifies current token.
314                   Should not be called if advance() returned false.
315                   @return current %token or single character.
316                   */
317                 public int token ()
318                 {
319                         return tok;
320                 }
321
322                 /** associated with current token.
323                   Should not be called if advance() returned false.
324                   @return value for token().
325                   */
326                 public Object value ()
327                 {
328                         return val;
329                 }
330         }
331 }