zeoslib  UNKNOWN
 All Files
ZExprParser.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { Expression Parser classes and interfaces }
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 ZExprParser;
53 
54 interface
55 
56 {$I ZCore.inc}
57 
58 uses SysUtils, Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} Contnrs,
59  ZCompatibility, ZVariant, ZTokenizer;
60 
61 type
62  {** Define types of expression tokens. }
63  TZExpressionTokenType = (
64  ttUnknown, ttLeftBrace, ttRightBrace, ttLeftSquareBrace,
65  ttRightSquareBrace, ttPlus, ttMinus, ttStar, ttSlash, ttProcent, ttPower,
66  ttEqual, ttNotEqual, ttMore, ttLess, ttEqualMore, ttEqualLess,
67  ttAnd, ttOr, ttXor, ttIs, ttNull, ttNot, ttLike, ttNotLike, ttIsNull,
68  ttIsNotNull, ttComma, ttUnary, ttFunction, ttVariable, ttConstant
69  );
70 
71  {** Defines a parser exception. }
72  TZParseError = class (Exception);
73 
74  {** Defines an expression token holder. }
75  TZExpressionToken = class (TObject)
76  private
77  FTokenType: TZExpressionTokenType;
78  FValue: TZVariant;
79  public
80  constructor Create(TokenType: TZExpressionTokenType; const Value: TZVariant);
81 
82  property TokenType: TZExpressionTokenType read FTokenType write FTokenType;
83  property Value: TZVariant read FValue write FValue;
84  end;
85 
86  {** Implements an expression parser class. }
87  TZExpressionParser = class (TObject)
88  private
89  FTokenizer: IZTokenizer;
90  FExpression: string;
91  FInitialTokens: TObjectList;
92  FTokenIndex: Integer;
93  FResultTokens: TObjectList;
94  FVariables: TStrings;
95 
96  function HasMoreTokens: Boolean;
97  function GetToken: TZExpressionToken;
98  function GetNextToken: TZExpressionToken;
99  procedure ShiftToken;
100  function CheckTokenTypes(
101  TokenTypes: array of TZExpressionTokenType): Boolean;
102 
103  procedure TokenizeExpression;
104 
105  procedure SyntaxAnalyse;
106  procedure SyntaxAnalyse1;
107  procedure SyntaxAnalyse2;
108  procedure SyntaxAnalyse3;
109  procedure SyntaxAnalyse4;
110  procedure SyntaxAnalyse5;
111  procedure SyntaxAnalyse6;
112  public
113  constructor Create(Tokenizer: IZTokenizer);
114  destructor Destroy; override;
115 
116  procedure Parse(Expression: string);
117  procedure Clear;
118 
119  property Tokenizer: IZTokenizer read FTokenizer write FTokenizer;
120  property Expression: string read FExpression write Parse;
121  property ResultTokens: TObjectList read FResultTokens;
122  property Variables: TStrings read FVariables;
123  end;
124 
125 implementation
126 
127 uses ZSysUtils, ZMessages;
128 
129 { TZExpressionToken }
130 
131 {**
132  Creates an expression token object.
133  @param TokenType a type of the token.
134  @param Value a token value.
135 }
136 constructor TZExpressionToken.Create(TokenType: TZExpressionTokenType;
137  const Value: TZVariant);
138 begin
139  FTokenType := TokenType;
140  FValue := Value;
141 end;
142 
143 const
144  {** Defines a list of operators. }
145  OperatorTokens: array[0..24] of string = (
146  '(', ')', '[', ']', '+', '-', '*', '/', '%', '^',
147  '=', '<>', '!=', '>', '<', '>=', '<=',
148  'AND', 'OR', 'XOR', 'NOT', 'IS', 'NULL', 'LIKE', ','
149  );
150 
151  {** Defines a list of operator codes. }
152  OperatorCodes: array[0..24] of TZExpressionTokenType = (
153  ttLeftBrace, ttRightBrace, ttLeftSquareBrace, ttRightSquareBrace,
154  ttPlus, ttMinus, ttStar, ttSlash, ttProcent, ttPower, ttEqual, ttNotEqual,
155  ttNotEqual, ttMore, ttLess, ttEqualMore, ttEqualLess, ttAnd, ttOr, ttXor,
156  ttNot, ttIs, ttNull, ttLike, ttComma
157  );
158 
159 { TZExpressionParser }
160 
161 {**
162  Creates this expression parser object.
163  @param Tokenizer an expression tokenizer.
164 }
165 constructor TZExpressionParser.Create(Tokenizer: IZTokenizer);
166 begin
167  FTokenizer := Tokenizer;
168  FExpression := '';
169  FInitialTokens := TObjectList.Create;
170  FTokenIndex := 0;
171  FResultTokens := TObjectList.Create;
172  FVariables := TStringList.Create;
173 end;
174 
175 {**
176  Destroyes this object and cleanups the memory.
177 }
178 destructor TZExpressionParser.Destroy;
179 begin
180  FreeAndNil(FInitialTokens);
181  FreeAndNil(FResultTokens);
182  FreeAndNil(FVariables);
183  FTokenizer := Nil;
184  inherited Destroy;
185 end;
186 
187 {**
188  Clears parsing result.
189 }
190 procedure TZExpressionParser.Clear;
191 begin
192  FExpression := '';
193  FInitialTokens.Clear;
194  FResultTokens.Clear;
195  FTokenIndex := 0;
196  FVariables.Clear;
197 end;
198 
199 {**
200  Sets a new expression string and parses it into internal byte code.
201  @param expression a new expression string.
202 }
203 procedure TZExpressionParser.Parse(Expression: string);
204 begin
205  Clear;
206  FExpression := Trim(Expression);
207  if FExpression <> '' then
208  begin
209  TokenizeExpression;
210  SyntaxAnalyse;
211  if HasMoreTokens then
212  begin
213  raise TZParseError.Create(
214  Format(SSyntaxErrorNear, [SoftVarManager.GetAsString(GetToken.Value)]));
215  end;
216  end;
217 end;
218 
219 {**
220  Checks are there more tokens for processing.
221  @return <code>TRUE</code> if some tokens are present.
222 }
223 function TZExpressionParser.HasMoreTokens: Boolean;
224 begin
225  Result := FTokenIndex < FInitialTokens.Count;
226 end;
227 
228 {**
229  Gets the current token object.
230  @param tokens a collection of tokens.
231  @returns the current token object.
232 }
233 function TZExpressionParser.GetToken: TZExpressionToken;
234 begin
235  if FTokenIndex < FInitialTokens.Count then
236  Result := TZExpressionToken(FInitialTokens[FTokenIndex])
237  else
238  Result := nil;
239 end;
240 
241 {**
242  Gets the next token object.
243  @param tokens a collection of tokens.
244  @returns the next token object.
245 }
246 function TZExpressionParser.GetNextToken: TZExpressionToken;
247 begin
248  if (FTokenIndex + 1) < FInitialTokens.Count then
249  Result := TZExpressionToken(FInitialTokens[FTokenIndex + 1])
250  else
251  Result := nil;
252 end;
253 
254 {**
255  Shifts the current token object.
256 }
257 procedure TZExpressionParser.ShiftToken;
258 begin
259  Inc(FTokenIndex);
260 end;
261 
262 {**
263  Checks available token types with token types from the list.
264  If they match it shifts the tokens.
265  @param TokenTypes a list of token types to compare.
266  @return <code>True</code> if token types match.
267 }
268 function TZExpressionParser.CheckTokenTypes(
269  TokenTypes: array of TZExpressionTokenType): Boolean;
270 var
271  I: Integer;
272  Temp: TZExpressionToken;
273 begin
274  Result := False;
275  for I := Low(TokenTypes) to High(TokenTypes) do
276  begin
277  if (FTokenIndex + I) < FInitialTokens.Count then
278  begin
279  Temp := TZExpressionToken(FInitialTokens[FTokenIndex + I]);
280  Result := Temp.TokenType = TokenTypes[I];
281  end
282  else
283  Result := False;
284 
285  if not Result then
286  Break;
287  end;
288  if Result then
289  Inc(FTokenIndex, Length(TokenTypes));
290 end;
291 
292 {**
293  Tokenizes the given expression and prepares an initial tokens list.
294 }
295 procedure TZExpressionParser.TokenizeExpression;
296 var
297  I: Integer;
298  TokenIndex: Integer;
299  Temp: string;
300  Tokens: TStrings;
301  TokenType: TZExpressionTokenType;
302  TokenValue: TZVariant;
303 begin
304  Tokens := FTokenizer.TokenizeBufferToList(FExpression,
305  [toSkipWhitespaces, toSkipComments, toSkipEOF, toDecodeStrings]);
306  try
307  TokenIndex := 0;
308 
309  while TokenIndex < Tokens.Count do
310  begin
311  TokenType := ttUnknown;
312  TokenValue := NullVariant;
313  case TZTokenType({$IFDEF FPC}Pointer({$ENDIF}
314  Tokens.Objects[TokenIndex]{$IFDEF FPC}){$ENDIF}) of
315  ttKeyword:
316  begin
317  Temp := UpperCase(Tokens[TokenIndex]);
318  if Temp = 'TRUE' then
319  begin
320  TokenType := ttConstant;
321  TokenValue:= EncodeBoolean(True);
322  end
323  else if Temp = 'FALSE' then
324  begin
325  TokenType := ttConstant;
326  TokenValue:= EncodeBoolean(False);
327  end
328  else
329  begin
330  for I := Low(OperatorTokens) to High(OperatorTokens) do
331  begin
332  if OperatorTokens[I] = Temp then
333  begin
334  TokenType := OperatorCodes[I];
335  Break;
336  end;
337  end;
338  end;
339  end;
340  ttWord:
341  begin
342  TokenType := ttVariable;
343  Temp := Tokens[TokenIndex];
344  if FVariables.IndexOf(Temp) < 0 then
345  FVariables.Add(Temp);
346  TokenValue:= EncodeString(Temp);
347  end;
348  ttInteger:
349  begin
350  TokenType := ttConstant;
351  TokenValue:= EncodeInteger(StrToInt64(Tokens[TokenIndex]));
352  end;
353  ttFloat:
354  begin
355  TokenType := ttConstant;
356  TokenValue:= EncodeFloat(SqlStrToFloat(AnsiString(Tokens[TokenIndex])));
357  end;
358  ttQuoted:
359  begin
360  TokenType := ttConstant;
361  TokenValue:= EncodeString(Tokens[TokenIndex]);
362  end;
363  ttSymbol:
364  begin
365  Temp := Tokens[TokenIndex];
366  for I := Low(OperatorTokens) to High(OperatorTokens) do
367  begin
368  if Temp = OperatorTokens[I] then
369  begin
370  TokenType := OperatorCodes[I];
371  Break;
372  end;
373  end;
374  end;
375  ttTime,ttDate,ttDateTime:
376  begin
377  TokenType := ttConstant;
378  TokenValue:= EncodeDateTime(StrToDateTime(Tokens[TokenIndex]));
379  end;
380  end;
381 
382  if TokenType = ttUnknown then
383  raise TZParseError.Create(Format(SUnknownSymbol, [Tokens[TokenIndex]]));
384 
385  Inc(TokenIndex);
386  FInitialTokens.Add(TZExpressionToken.Create(TokenType, TokenValue));
387  end;
388  finally
389  Tokens.Free;
390  end;
391 end;
392 
393 {**
394  Performs a syntax analyze at level 0.
395 }
396 procedure TZExpressionParser.SyntaxAnalyse;
397 var
398  Token: TZExpressionToken;
399 begin
400  if not HasMoreTokens then
401  raise TZParseError.Create(SUnexpectedExprEnd);
402 
403  SyntaxAnalyse1;
404  while HasMoreTokens do
405  begin
406  Token := GetToken;
407  if not (Token.TokenType in [ttAnd, ttOr, ttXor]) then
408  Break;
409  ShiftToken;
410  SyntaxAnalyse1;
411  FResultTokens.Add(TZExpressionToken.Create(Token.TokenType, NullVariant));
412  end;
413 end;
414 
415 {**
416  Performs a syntax analyze at level 1.
417 }
418 procedure TZExpressionParser.SyntaxAnalyse1;
419 var
420  Token: TZExpressionToken;
421 begin
422  if not HasMoreTokens then
423  raise TZParseError.Create(SUnexpectedExprEnd);
424 
425  Token := GetToken;
426  if Token.TokenType = ttNot then
427  begin
428  ShiftToken;
429  SyntaxAnalyse2;
430  FResultTokens.Add(TZExpressionToken.Create(Token.TokenType, NullVariant));
431  end
432  else
433  SyntaxAnalyse2;
434 end;
435 
436 {**
437  Performs a syntax analyze at level 2.
438 }
439 procedure TZExpressionParser.SyntaxAnalyse2;
440 var
441  Token: TZExpressionToken;
442 begin
443  if not HasMoreTokens then
444  raise TZParseError.Create(SUnexpectedExprEnd);
445 
446  SyntaxAnalyse3;
447  while HasMoreTokens do
448  begin
449  Token := GetToken;
450  if not (Token.TokenType in [ttEqual, ttNotEqual, ttMore, ttLess,
451  ttEqualMore, ttEqualLess]) then
452  Break;
453  ShiftToken;
454  SyntaxAnalyse3;
455  FResultTokens.Add(TZExpressionToken.Create(Token.TokenType, NullVariant));
456  end;
457 end;
458 
459 {**
460  Performs a syntax analyze at level 3.
461 }
462 procedure TZExpressionParser.SyntaxAnalyse3;
463 var
464  Token: TZExpressionToken;
465 begin
466  if not HasMoreTokens then
467  raise TZParseError.Create(SUnexpectedExprEnd);
468 
469  SyntaxAnalyse4;
470  while HasMoreTokens do
471  begin
472  Token := GetToken;
473  if Token.TokenType in [ttPlus, ttMinus, ttLike] then
474  begin
475  ShiftToken;
476  SyntaxAnalyse4;
477  FResultTokens.Add(TZExpressionToken.Create(Token.TokenType, NullVariant));
478  end
479  else if CheckTokenTypes([ttNot, ttLike]) then
480  begin
481  SyntaxAnalyse4;
482  FResultTokens.Add(TZExpressionToken.Create(ttNotLike, NullVariant));
483  end
484  else if CheckTokenTypes([ttIs, ttNull]) then
485  begin
486  FResultTokens.Add(TZExpressionToken.Create(ttIsNull, NullVariant));
487  end
488  else if CheckTokenTypes([ttIs, ttNot, ttNull]) then
489  begin
490  FResultTokens.Add(TZExpressionToken.Create(ttIsNotNull, NullVariant));
491  end
492  else
493  Break;
494  end;
495 end;
496 
497 {**
498  Performs a syntax analyze at level 4.
499 }
500 procedure TZExpressionParser.SyntaxAnalyse4;
501 var
502  Token: TZExpressionToken;
503 begin
504  if not HasMoreTokens then
505  raise TZParseError.Create(SUnexpectedExprEnd);
506 
507  SyntaxAnalyse5;
508  while HasMoreTokens do
509  begin
510  Token := GetToken;
511  if not (Token.TokenType in [ttStar, ttSlash, ttProcent]) then
512  Break;
513  ShiftToken;
514  SyntaxAnalyse5;
515  FResultTokens.Add(TZExpressionToken.Create(Token.TokenType, NullVariant));
516  end;
517 end;
518 
519 {**
520  Performs a syntax analyze at level 5.
521 }
522 procedure TZExpressionParser.SyntaxAnalyse5;
523 var
524  Token: TZExpressionToken;
525 begin
526  if not HasMoreTokens then
527  raise TZParseError.Create(SUnexpectedExprEnd);
528 
529  SyntaxAnalyse6;
530  while HasMoreTokens do
531  begin
532  Token := GetToken;
533  if Token.TokenType <> ttPower then
534  Break;
535  ShiftToken;
536  SyntaxAnalyse6;
537  FResultTokens.Add(TZExpressionToken.Create(Token.TokenType, NullVariant));
538  end;
539 end;
540 
541 {**
542  Performs a syntax analyze at level 6.
543 }
544 procedure TZExpressionParser.SyntaxAnalyse6;
545 var
546  ParamsCount: Integer;
547  Unary, Token: TZExpressionToken;
548  Primitive, NextToken: TZExpressionToken;
549  Temp: TZVariant;
550 begin
551  if not HasMoreTokens then
552  raise TZParseError.Create(SUnexpectedExprEnd);
553 
554  Unary := GetToken;
555  if Unary.TokenType = ttPlus then
556  begin
557  Unary := nil;
558  ShiftToken;
559  end
560  else if Unary.TokenType = ttMinus then
561  begin
562  Unary.TokenType := ttUnary;
563  ShiftToken;
564  end
565  else
566  Unary := nil;
567 
568  if not HasMoreTokens then
569  raise TZParseError.Create(SUnexpectedExprEnd);
570 
571  Primitive := GetToken;
572  NextToken := GetNextToken;
573  if (Primitive.TokenType = ttVariable) and (NextToken <> nil)
574  and (NextToken.TokenType = ttLeftBrace) then
575  Primitive.TokenType := ttFunction;
576 
577  if Primitive.TokenType in [ttConstant, ttVariable] then
578  begin
579  ShiftToken;
580  FResultTokens.Add(TZExpressionToken.Create(
581  Primitive.TokenType, Primitive.Value));
582  end
583  else if Primitive.TokenType = ttLeftBrace then
584  begin
585  ShiftToken;
586  SyntaxAnalyse;
587  if not HasMoreTokens then
588  raise TZParseError.Create(SUnexpectedExprEnd);
589  Primitive := GetToken;
590  if Primitive.TokenType <> ttRightBrace then
591  raise TZParseError.Create(SRightBraceExpected);
592  ShiftToken;
593  end
594  else if Primitive.TokenType = ttFunction then
595  begin
596  ShiftToken;
597  Token := GetToken;
598  if Token.TokenType <> ttLeftBrace then
599  raise TZParseError.Create(SInternalError);
600  ParamsCount := 0;
601  repeat
602  ShiftToken;
603  Token := GetToken;
604  if (Token = nil) or (Token.TokenType = ttRightBrace) then
605  Break;
606  Inc(ParamsCount);
607  SyntaxAnalyse;
608  Token := GetToken;
609  until (Token = nil) or (Token.TokenType <> ttComma);
610 
611  if not HasMoreTokens then
612  raise TZParseError.Create(SUnexpectedExprEnd);
613  if Token.TokenType <> ttRightBrace then
614  raise TZParseError.Create(SRightBraceExpected);
615  ShiftToken;
616 
617  Temp:= EncodeInteger(ParamsCount);
618  FResultTokens.Add(TZExpressionToken.Create(ttConstant, Temp));
619  FResultTokens.Add(TZExpressionToken.Create(Primitive.TokenType,
620  Primitive.Value));
621  end
622  else
623  raise TZParseError.Create(SSyntaxError);
624 
625  if Unary <> nil then
626  FResultTokens.Add(TZExpressionToken.Create(Unary.TokenType, NullVariant));
627 end;
628 
629 end.