zeoslib  UNKNOWN
 All Files
ZExprToken.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { String tokenizing classes for Expressions }
5 { }
6 { Originally written by Sergey Seroukhov }
7 { }
8 {*********************************************************}
9 
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
12 { }
13 { License Agreement: }
14 { }
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. }
20 { }
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. }
39 { }
40 { }
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) }
45 { }
46 { http://www.sourceforge.net/projects/zeoslib. }
47 { }
48 { }
49 { Zeos Development Group. }
50 {********************************************************@}
51 
52 unit ZExprToken;
53 
54 interface
55 
56 {$I ZCore.inc}
57 
58 uses
59  Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
60  ZSysUtils, ZTokenizer;
61 
62 type
63 
64  {** Implements an Expression-specific number state object. }
65  TZExpressionNumberState = class (TZNumberState)
66  public
67  function NextToken(Stream: TStream; FirstChar: Char;
68  Tokenizer: TZTokenizer): TZToken; override;
69  end;
70 
71  {** Implements an Expression-specific quote string state object. }
72  TZExpressionQuoteState = class (TZQuoteState)
73  public
74  function NextToken(Stream: TStream; FirstChar: Char;
75  Tokenizer: TZTokenizer): TZToken; override;
76 
77  function EncodeString(const Value: string; QuoteChar: Char): string; override;
78  function DecodeString(const Value: string; QuoteChar: Char): string; override;
79  end;
80 
81  {**
82  This state will either delegate to a comment-handling
83  state, or return a token with just a slash in it.
84  }
85  TZExpressionCommentState = class (TZCppCommentState)
86  public
87  function NextToken(Stream: TStream; FirstChar: Char;
88  Tokenizer: TZTokenizer): TZToken; override;
89  end;
90 
91  {** Implements a symbol state object. }
92  TZExpressionSymbolState = class (TZSymbolState)
93  public
94  constructor Create;
95  end;
96 
97  {** Implements a word state object. }
98  TZExpressionWordState = class (TZWordState)
99  public
100  constructor Create;
101  function NextToken(Stream: TStream; FirstChar: Char;
102  Tokenizer: TZTokenizer): TZToken; override;
103  end;
104 
105  {** Implements a default tokenizer object. }
106  TZExpressionTokenizer = class (TZTokenizer)
107  public
108  constructor Create;
109  end;
110 
111 implementation
112 uses ZCompatibility;
113 
114 const
115  {** List of keywords. }
116  Keywords: array [0..8] of string = (
117  'AND','OR','NOT','XOR','LIKE','IS','NULL','TRUE','FALSE'
118  );
119 
120 { TZExpressionNumberState }
121 
122 
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
125 //
126 // Instead, operations should use SizeOf(Type), like this:
127 // Stream.Read(LastChar, 1 * SizeOf(Char))
128 //
129 // This is unicode safe and ansi (Delphi under 2009) compatible
130 
131 {**
132  Return a number token from a reader.
133  @return a number token from a reader
134 }
135 function TZExpressionNumberState.NextToken(Stream: TStream; FirstChar: Char;
136  Tokenizer: TZTokenizer): TZToken;
137 var
138  TempChar: Char;
139  FloatPoint: Boolean;
140  LastChar: Char;
141 
142  function ReadDecDigits: string;
143  begin
144  Result := '';
145  LastChar := #0;
146  while Stream.Read(LastChar, 1 * SizeOf(Char)) > 0 do
147  begin
148  if CharInSet(LastChar, ['0'..'9']) then
149  begin
150  Result := Result + LastChar;
151  LastChar := #0;
152  end
153  else
154  begin
155  Stream.Seek(-(1 * SizeOf(Char)), soFromCurrent);
156  Break;
157  end;
158  end;
159  end;
160 
161 begin
162  FloatPoint := FirstChar = '.';
163  Result.Value := FirstChar;
164  Result.TokenType := ttUnknown;
165  LastChar := #0;
166 
167  { Reads the first part of the number before decimal point }
168  if not FloatPoint then
169  begin
170  Result.Value := Result.Value + ReadDecDigits;
171  FloatPoint := LastChar = '.';
172  if FloatPoint then
173  begin
174  Stream.Read(TempChar, 1 * SizeOf(Char));
175  Result.Value := Result.Value + TempChar;
176  end;
177  end;
178 
179  { Reads the second part of the number after decimal point }
180  if FloatPoint then
181  Result.Value := Result.Value + ReadDecDigits;
182 
183  { Reads a power part of the number }
184  if CharInSet(LastChar, ['e', 'E']) then
185  begin
186  Stream.Read(TempChar, 1 * SizeOf(Char));
187  Result.Value := Result.Value + TempChar;
188  FloatPoint := True;
189 
190  Stream.Read(TempChar, 1 * SizeOf(Char));
191  if CharInSet(TempChar, ['0'..'9', '-', '+']) then
192  Result.Value := Result.Value + TempChar + ReadDecDigits
193  else
194  begin
195  Result.Value := Copy(Result.Value, 1, Length(Result.Value) - 1);
196  Stream.Seek(-(2 * SizeOf(Char)), soFromCurrent);
197  end;
198  end;
199 
200  { Prepare the result }
201  if Result.Value = '.' then
202  begin
203  if Tokenizer.SymbolState <> nil then
204  Result := Tokenizer.SymbolState.NextToken(Stream, FirstChar, Tokenizer);
205  end
206  else
207  begin
208  if FloatPoint then
209  Result.TokenType := ttFloat
210  else
211  Result.TokenType := ttInteger;
212  end;
213 end;
214 
215 { TZExpressionSQLQuoteState }
216 
217 {**
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.
221 
222  @return a quoted string token from a reader
223 }
224 function TZExpressionQuoteState.NextToken(Stream: TStream;
225  FirstChar: Char; Tokenizer: TZTokenizer): TZToken;
226 var
227  ReadChar: Char;
228  LastChar: Char;
229 begin
230  if FirstChar = '"' then
231  Result.TokenType := ttWord
232  else
233  Result.TokenType := ttQuoted;
234  Result.Value := FirstChar;
235  LastChar := #0;
236 
237  while Stream.Read(ReadChar, 1 * SizeOf(Char)) > 0 do
238  begin
239  if (LastChar = FirstChar) and (ReadChar <> FirstChar) then
240  begin
241  Stream.Seek(-(1 * SizeOf(Char)), soFromCurrent);
242  Break;
243  end;
244  Result.Value := Result.Value + ReadChar;
245  if LastChar = '\' then
246  LastChar := #0
247  else if (LastChar = FirstChar) and (ReadChar = FirstChar) then
248  LastChar := #0
249  else
250  LastChar := ReadChar;
251  end;
252 end;
253 
254 {**
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.
259 }
260 function TZExpressionQuoteState.EncodeString(const Value: string;
261  QuoteChar: Char): string;
262 begin
263  if CharInSet(QuoteChar, ['''', '"']) then
264  Result := QuoteChar + EncodeCString(Value) + QuoteChar
265  else
266  Result := Value;
267 end;
268 
269 {**
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.
274 }
275 function TZExpressionQuoteState.DecodeString(const Value: string;
276  QuoteChar: Char): string;
277 begin
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))
281  else
282  Result := Value;
283 end;
284 
285 { TZExpressionCommentState }
286 
287 {**
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
291 }
292 function TZExpressionCommentState.NextToken(Stream: TStream;
293  FirstChar: Char; Tokenizer: TZTokenizer): TZToken;
294 var
295  ReadChar: Char;
296  ReadNum: Integer;
297 begin
298  Result.TokenType := ttUnknown;
299  Result.Value := FirstChar;
300 
301  if FirstChar = '/' then
302  begin
303  ReadNum := Stream.Read(ReadChar, 1 * SizeOf(Char));
304  if (ReadNum > 0) and (ReadChar = '*') then
305  begin
306  Result.TokenType := ttComment;
307  Result.Value := '/*' + GetMultiLineComment(Stream);
308  end
309  else
310  begin
311  if ReadNum > 0 then
312  Stream.Seek(-(1 * SizeOf(Char)), soFromCurrent);
313  end;
314  end;
315 
316  if (Result.TokenType = ttUnknown) and (Tokenizer.SymbolState <> nil) then
317  Result := Tokenizer.SymbolState.NextToken(Stream, FirstChar, Tokenizer);
318 end;
319 
320 { TZExpressionSymbolState }
321 
322 {**
323  Creates this Expression-specific symbol state object.
324 }
325 constructor TZExpressionSymbolState.Create;
326 begin
327  inherited Create;
328  Add('<=');
329  Add('>=');
330  Add('<>');
331  Add('!=');
332 end;
333 
334 { TZExpressionWordState }
335 
336 {**
337  Constructs this Expression-specific word state object.
338 }
339 constructor TZExpressionWordState.Create;
340 begin
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);
347 end;
348 
349 {**
350  Gets a word tokens or special operators.
351  @return a processed token.
352 }
353 function TZExpressionWordState.NextToken(Stream: TStream; FirstChar: Char;
354  Tokenizer: TZTokenizer): TZToken;
355 var
356  I: Integer;
357  Temp: string;
358 begin
359  Result := inherited NextToken(Stream, FirstChar, Tokenizer);
360  Temp := UpperCase(Result.Value);
361 
362  for I := Low(Keywords) to High(Keywords) do
363  begin
364  if Temp = Keywords[I] then
365  begin
366  Result.TokenType := ttKeyword;
367  Break;
368  end;
369  end;
370 end;
371 
372 { TZExpressionTokenizer }
373 
374 {**
375  Constructs a tokenizer with a default state table (as
376  described in the class comment).
377 }
378 constructor TZExpressionTokenizer.Create;
379 begin
380  WhitespaceState := TZWhitespaceState.Create;
381 
382  SymbolState := TZExpressionSymbolState.Create;
383  NumberState := TZExpressionNumberState.Create;
384  QuoteState := TZExpressionQuoteState.Create;
385  WordState := TZExpressionWordState.Create;
386  CommentState := TZExpressionCommentState.Create;
387 
388  SetCharacterState(#0, #32, WhitespaceState);
389  SetCharacterState(#33, #191, SymbolState);
390  SetCharacterState(#192, High(Char), WordState);
391 
392  SetCharacterState('a', 'z', WordState);
393  SetCharacterState('A', 'Z', WordState);
394  SetCharacterState('_', '_', WordState);
395 
396  SetCharacterState('0', '9', NumberState);
397  SetCharacterState('.', '.', NumberState);
398 
399  SetCharacterState('"', '"', QuoteState);
400  SetCharacterState('''', '''', QuoteState);
401 
402  SetCharacterState('/', '/', CommentState);
403 end;
404 
405 end.
406