**** Merged from MCS ****
[mono-project.git] / mcs / class / System.Data / Mono.Data.SqlExpressions / Tokenizer.cs
blobc3b4eb1faf80962043dfe9c94a5cc1d4857dd40e
1 //
2 // SqlWhereClauseTokenizer.cs
3 //
4 // Author:
5 // Juraj Skripsky (juraj@hotfeet.ch)
6 //
7 // (C) 2004 HotFeet GmbH (http://www.hotfeet.ch)
8 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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.
33 using System;
34 using System.Data;
35 using System.IO;
36 using System.Text;
37 using System.Collections;
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",
47 Token.TRUE, "true",
48 Token.FALSE, "false",
49 Token.NULL, "null",
51 Token.PARENT, "parent",
52 Token.CHILD, "child",
54 Token.IS, "is",
55 Token.IN, "in",
56 Token.LIKE, "like",
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",
66 Token.IIF, "iif",
67 Token.SUBSTRING, "substring",
68 Token.ISNULL, "isnull",
69 Token.LEN, "len",
70 Token.TRIM, "trim",
71 Token.CONVERT, "convert"
73 private char[] input;
74 private int pos;
76 private int tok;
77 private object val;
79 static Tokenizer ()
81 for (int i = 0; i < tokens.Length; i += 2)
82 tokenMap.Add (tokens [i + 1], tokens [i]);
85 public Tokenizer (string strInput)
87 input = strInput.ToCharArray ();
88 pos = 0;
91 private char Current() {
92 return input [pos];
95 private char Next() {
96 if (pos + 1 >= input.Length)
97 return (char)0;
98 return input [pos + 1];
101 private void MoveNext() {
102 pos++;
105 private void SkipWhiteSpace ()
107 while (Char.IsWhiteSpace (Current ()))
108 MoveNext ();
111 private object ReadNumber ()
113 StringBuilder sb = new StringBuilder ();
114 sb.Append (Current ());
116 char next;
117 while (Char.IsDigit (next = Next ()) || next == '.') {
118 sb.Append (next);
119 MoveNext ();
122 string str = sb.ToString ();
124 if (str.IndexOf(".") == -1)
125 return Int64.Parse (str);
126 else
127 return double.Parse (str);
130 private char ProcessEscapes(char c)
132 if (c == '\\') {
133 MoveNext();
134 c = Next();
135 switch (c) {
136 case 'n':
137 c = '\n';
138 break;
139 case 'r':
140 c = '\r';
141 break;
142 case 't':
143 c = '\t';
144 break;
146 case '\\':
147 c = '\\';
148 break;
150 default:
151 throw new SyntaxErrorException (String.Format ("Invalid escape sequence: '\\{0}'.", c));
154 return c;
157 private string ReadString (char terminator)
159 StringBuilder sb = new StringBuilder ();
160 char next;
161 while ((next = Next ()) != terminator) {
162 sb.Append (ProcessEscapes (next));
163 MoveNext ();
165 MoveNext ();
167 return sb.ToString ();
170 private string ReadIdentifier ()
172 StringBuilder sb = new StringBuilder ();
173 sb.Append (Current ());
175 char next;
176 while ((next = Next ()) == '_' || Char.IsLetterOrDigit (next) || next == '\\') {
177 sb.Append (ProcessEscapes (next));
178 MoveNext ();
181 return sb.ToString ();
184 private int ParseIdentifier ()
186 string strToken = ReadIdentifier ();
187 object tokenObj = tokenMap[strToken.ToLower()];
189 if(tokenObj != null)
190 return (int)tokenObj;
192 val = strToken;
193 return Token.Identifier;
196 private int ParseToken ()
198 char cur;
199 switch (cur = Current ()) {
200 case '(':
201 return Token.PAROPEN;
203 case ')':
204 return Token.PARCLOSE;
206 case '.':
207 return Token.DOT;
209 case ',':
210 return Token.COMMA;
212 case '+':
213 return Token.PLUS;
215 case '-':
216 return Token.MINUS;
218 case '*':
219 return Token.MUL;
221 case '/':
222 return Token.DIV;
224 case '%':
225 return Token.MOD;
227 case '=':
228 return Token.EQ;
230 case '<':
231 return Token.LT;
233 case '>':
234 return Token.GT;
236 case '[':
237 val = ReadString (']');
238 return Token.Identifier;
240 case '#':
241 string date = ReadString ('#');
242 val = DateTime.Parse (date);
243 return Token.DateLiteral;
245 case '\'':
246 case '\"':
247 val = ReadString (cur);
248 return Token.StringLiteral;
250 default:
251 if (Char.IsDigit (cur)) {
252 val = ReadNumber ();
253 return Token.NumberLiteral;
254 } else if (Char.IsLetter (cur) || cur == '_')
255 return ParseIdentifier ();
256 break;
258 throw new Exception ("invalid token: '" + cur + "'");
261 ///////////////////////////
262 // yyParser.yyInput methods
263 ///////////////////////////
265 /** move on to next token.
266 @return false if positioned beyond tokens.
267 @throws IOException on input error.
269 public bool advance ()
271 val = null;
272 tok = -1;
274 try {
275 SkipWhiteSpace();
276 tok = ParseToken();
277 MoveNext();
278 return true;
280 } catch(IndexOutOfRangeException) {
281 return false;
285 /** classifies current token.
286 Should not be called if advance() returned false.
287 @return current %token or single character.
289 public int token ()
291 return tok;
294 /** associated with current token.
295 Should not be called if advance() returned false.
296 @return value for token().
298 public Object value ()
300 return val;