1 {*********************************************************}
3 { Zeos Database Objects }
4 { String tokenizing classes for Expressions }
6 { Originally written by Sergey Seroukhov }
8 {*********************************************************}
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
13 { License Agreement: }
15 { This library is distributed in the hope that it will be }
16 { useful, but WITHOUT ANY WARRANTY; without even the }
17 { implied warranty of MERCHANTABILITY or FITNESS FOR }
18 { A PARTICULAR PURPOSE. See the GNU Lesser General }
19 { Public License for more details. }
21 { The source code of the ZEOS Libraries and packages are }
22 { distributed under the Library GNU General Public }
23 { License (see the file COPYING / COPYING.ZEOS) }
24 { with the following modification: }
25 { As a special exception, the copyright holders of this }
26 { library give you permission to link this library with }
27 { independent modules to produce an executable, }
28 { regardless of the license terms of these independent }
29 { modules, and to copy and distribute the resulting }
30 { executable under terms of your choice, provided that }
31 { you also meet, for each linked independent module, }
32 { the terms and conditions of the license of that module. }
33 { An independent module is a module which is not derived }
34 { from or based on this library. If you modify this }
35 { library, you may extend this exception to your version }
36 { of the library, but you are not obligated to do so. }
37 { If you do not wish to do so, delete this exception }
38 { statement from your version. }
41 { The project web site is located on: }
42 { http://zeos.firmos.at (FORUM) }
43 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
44 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
46 { http://www.sourceforge.net/projects/zeoslib. }
49 { Zeos Development Group. }
50 {********************************************************@}
59 Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
60 ZSysUtils, ZTokenizer;
64 {** Implements an Expression-specific number state object. }
65 TZExpressionNumberState = class (TZNumberState)
67 function NextToken(Stream: TStream; FirstChar: Char;
68 Tokenizer: TZTokenizer): TZToken; override;
71 {** Implements an Expression-specific quote string state object. }
72 TZExpressionQuoteState = class (TZQuoteState)
74 function NextToken(Stream: TStream; FirstChar: Char;
75 Tokenizer: TZTokenizer): TZToken; override;
77 function EncodeString(const Value: string; QuoteChar: Char): string; override;
78 function DecodeString(const Value: string; QuoteChar: Char): string; override;
82 This state will either delegate to a comment-handling
83 state, or return a token with just a slash in it.
85 TZExpressionCommentState = class (TZCppCommentState)
87 function NextToken(Stream: TStream; FirstChar: Char;
88 Tokenizer: TZTokenizer): TZToken; override;
91 {** Implements a symbol state object. }
92 TZExpressionSymbolState = class (TZSymbolState)
97 {** Implements a word state object. }
98 TZExpressionWordState = class (TZWordState)
101 function NextToken(Stream: TStream; FirstChar: Char;
102 Tokenizer: TZTokenizer): TZToken; override;
105 {** Implements a default tokenizer object. }
106 TZExpressionTokenizer = class (TZTokenizer)
115 {** List of keywords. }
116 Keywords: array [0..8] of string = (
117 'AND','OR','NOT','XOR','LIKE','IS','NULL','TRUE','FALSE'
120 { TZExpressionNumberState }
123 //gto: all operations on Streams should be done without presuming the size
124 // of the read var, like Stream.Read(LastChar, 1), to read 1 char
126 // Instead, operations should use SizeOf(Type), like this:
127 // Stream.Read(LastChar, 1 * SizeOf(Char))
129 // This is unicode safe and ansi (Delphi under 2009) compatible
132 Return a number token from a reader.
133 @return a number token from a reader
135 function TZExpressionNumberState.NextToken(Stream: TStream; FirstChar: Char;
136 Tokenizer: TZTokenizer): TZToken;
142 function ReadDecDigits: string;
146 while Stream.Read(LastChar, 1 * SizeOf(Char)) > 0 do
148 if CharInSet(LastChar, ['0'..'9']) then
150 Result := Result + LastChar;
155 Stream.Seek(-(1 * SizeOf(Char)), soFromCurrent);
162 FloatPoint := FirstChar = '.';
163 Result.Value := FirstChar;
164 Result.TokenType := ttUnknown;
167 { Reads the first part of the number before decimal point }
168 if not FloatPoint then
170 Result.Value := Result.Value + ReadDecDigits;
171 FloatPoint := LastChar = '.';
174 Stream.Read(TempChar, 1 * SizeOf(Char));
175 Result.Value := Result.Value + TempChar;
179 { Reads the second part of the number after decimal point }
181 Result.Value := Result.Value + ReadDecDigits;
183 { Reads a power part of the number }
184 if CharInSet(LastChar, ['e', 'E']) then
186 Stream.Read(TempChar, 1 * SizeOf(Char));
187 Result.Value := Result.Value + TempChar;
190 Stream.Read(TempChar, 1 * SizeOf(Char));
191 if CharInSet(TempChar, ['0'..'9', '-', '+']) then
192 Result.Value := Result.Value + TempChar + ReadDecDigits
195 Result.Value := Copy(Result.Value, 1, Length(Result.Value) - 1);
196 Stream.Seek(-(2 * SizeOf(Char)), soFromCurrent);
200 { Prepare the result }
201 if Result.Value = '.' then
203 if Tokenizer.SymbolState <> nil then
204 Result := Tokenizer.SymbolState.NextToken(Stream, FirstChar, Tokenizer);
209 Result.TokenType := ttFloat
211 Result.TokenType := ttInteger;
215 { TZExpressionSQLQuoteState }
218 Return a quoted string token from a reader. This method
219 will collect characters until it sees a match to the
220 character that the tokenizer used to switch to this state.
222 @return a quoted string token from a reader
224 function TZExpressionQuoteState.NextToken(Stream: TStream;
225 FirstChar: Char; Tokenizer: TZTokenizer): TZToken;
230 if FirstChar = '"' then
231 Result.TokenType := ttWord
233 Result.TokenType := ttQuoted;
234 Result.Value := FirstChar;
237 while Stream.Read(ReadChar, 1 * SizeOf(Char)) > 0 do
239 if (LastChar = FirstChar) and (ReadChar <> FirstChar) then
241 Stream.Seek(-(1 * SizeOf(Char)), soFromCurrent);
244 Result.Value := Result.Value + ReadChar;
245 if LastChar = '\' then
247 else if (LastChar = FirstChar) and (ReadChar = FirstChar) then
250 LastChar := ReadChar;
255 Encodes a string value.
256 @param Value a string value to be encoded.
257 @param QuoteChar a string quote character.
258 @returns an encoded string.
260 function TZExpressionQuoteState.EncodeString(const Value: string;
261 QuoteChar: Char): string;
263 if CharInSet(QuoteChar, ['''', '"']) then
264 Result := QuoteChar + EncodeCString(Value) + QuoteChar
270 Decodes a string value.
271 @param Value a string value to be decoded.
272 @param QuoteChar a string quote character.
273 @returns an decoded string.
275 function TZExpressionQuoteState.DecodeString(const Value: string;
276 QuoteChar: Char): string;
278 if (Length(Value) >= 2) and CharInSet(QuoteChar, ['''', '"'])
279 and (Value[1] = QuoteChar) and (Value[Length(Value)] = QuoteChar) then
280 Result := DecodeCString(Copy(Value, 2, Length(Value) - 2))
285 { TZExpressionCommentState }
288 Gets an Expression specific comments like /* */.
289 @return either just a slash token, or the results of
290 delegating to a comment-handling state
292 function TZExpressionCommentState.NextToken(Stream: TStream;
293 FirstChar: Char; Tokenizer: TZTokenizer): TZToken;
298 Result.TokenType := ttUnknown;
299 Result.Value := FirstChar;
301 if FirstChar = '/' then
303 ReadNum := Stream.Read(ReadChar, 1 * SizeOf(Char));
304 if (ReadNum > 0) and (ReadChar = '*') then
306 Result.TokenType := ttComment;
307 Result.Value := '/*' + GetMultiLineComment(Stream);
312 Stream.Seek(-(1 * SizeOf(Char)), soFromCurrent);
316 if (Result.TokenType = ttUnknown) and (Tokenizer.SymbolState <> nil) then
317 Result := Tokenizer.SymbolState.NextToken(Stream, FirstChar, Tokenizer);
320 { TZExpressionSymbolState }
323 Creates this Expression-specific symbol state object.
325 constructor TZExpressionSymbolState.Create;
334 { TZExpressionWordState }
337 Constructs this Expression-specific word state object.
339 constructor TZExpressionWordState.Create;
341 SetWordChars(#0, #191, False);
342 SetWordChars(#192, high(char), True);
343 SetWordChars('a', 'z', True);
344 SetWordChars('A', 'Z', True);
345 SetWordChars('0', '9', True);
346 SetWordChars('_', '_', True);
350 Gets a word tokens or special operators.
351 @return a processed token.
353 function TZExpressionWordState.NextToken(Stream: TStream; FirstChar: Char;
354 Tokenizer: TZTokenizer): TZToken;
359 Result := inherited NextToken(Stream, FirstChar, Tokenizer);
360 Temp := UpperCase(Result.Value);
362 for I := Low(Keywords) to High(Keywords) do
364 if Temp = Keywords[I] then
366 Result.TokenType := ttKeyword;
372 { TZExpressionTokenizer }
375 Constructs a tokenizer with a default state table (as
376 described in the class comment).
378 constructor TZExpressionTokenizer.Create;
380 WhitespaceState := TZWhitespaceState.Create;
382 SymbolState := TZExpressionSymbolState.Create;
383 NumberState := TZExpressionNumberState.Create;
384 QuoteState := TZExpressionQuoteState.Create;
385 WordState := TZExpressionWordState.Create;
386 CommentState := TZExpressionCommentState.Create;
388 SetCharacterState(#0, #32, WhitespaceState);
389 SetCharacterState(#33, #191, SymbolState);
390 SetCharacterState(#192, High(Char), WordState);
392 SetCharacterState('a', 'z', WordState);
393 SetCharacterState('A', 'Z', WordState);
394 SetCharacterState('_', '_', WordState);
396 SetCharacterState('0', '9', NumberState);
397 SetCharacterState('.', '.', NumberState);
399 SetCharacterState('"', '"', QuoteState);
400 SetCharacterState('''', '''', QuoteState);
402 SetCharacterState('/', '/', CommentState);