1 {*********************************************************}
3 { Zeos Database Objects }
4 { Expression Parser classes and interfaces }
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 {********************************************************@}
58 uses SysUtils, Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} Contnrs,
59 ZCompatibility, ZVariant, ZTokenizer;
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
71 {** Defines a parser exception. }
72 TZParseError = class (Exception);
74 {** Defines an expression token holder. }
75 TZExpressionToken = class (TObject)
77 FTokenType: TZExpressionTokenType;
80 constructor Create(TokenType: TZExpressionTokenType; const Value: TZVariant);
82 property TokenType: TZExpressionTokenType read FTokenType write FTokenType;
83 property Value: TZVariant read FValue write FValue;
86 {** Implements an expression parser class. }
87 TZExpressionParser = class (TObject)
89 FTokenizer: IZTokenizer;
91 FInitialTokens: TObjectList;
93 FResultTokens: TObjectList;
96 function HasMoreTokens: Boolean;
97 function GetToken: TZExpressionToken;
98 function GetNextToken: TZExpressionToken;
100 function CheckTokenTypes(
101 TokenTypes: array of TZExpressionTokenType): Boolean;
103 procedure TokenizeExpression;
105 procedure SyntaxAnalyse;
106 procedure SyntaxAnalyse1;
107 procedure SyntaxAnalyse2;
108 procedure SyntaxAnalyse3;
109 procedure SyntaxAnalyse4;
110 procedure SyntaxAnalyse5;
111 procedure SyntaxAnalyse6;
113 constructor Create(Tokenizer: IZTokenizer);
114 destructor Destroy; override;
116 procedure Parse(Expression: string);
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;
127 uses ZSysUtils, ZMessages;
129 { TZExpressionToken }
132 Creates an expression token object.
133 @param TokenType a type of the token.
134 @param Value a token value.
136 constructor TZExpressionToken.Create(TokenType: TZExpressionTokenType;
137 const Value: TZVariant);
139 FTokenType := TokenType;
144 {** Defines a list of operators. }
145 OperatorTokens: array[0..24] of string = (
146 '(', ')', '[', ']', '+', '-', '*', '/', '%', '^',
147 '=', '<>', '!=', '>', '<', '>=', '<=',
148 'AND', 'OR', 'XOR', 'NOT', 'IS', 'NULL', 'LIKE', ','
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
159 { TZExpressionParser }
162 Creates this expression parser object.
163 @param Tokenizer an expression tokenizer.
165 constructor TZExpressionParser.Create(Tokenizer: IZTokenizer);
167 FTokenizer := Tokenizer;
169 FInitialTokens := TObjectList.Create;
171 FResultTokens := TObjectList.Create;
172 FVariables := TStringList.Create;
176 Destroyes this object and cleanups the memory.
178 destructor TZExpressionParser.Destroy;
180 FreeAndNil(FInitialTokens);
181 FreeAndNil(FResultTokens);
182 FreeAndNil(FVariables);
188 Clears parsing result.
190 procedure TZExpressionParser.Clear;
193 FInitialTokens.Clear;
200 Sets a new expression string and parses it into internal byte code.
201 @param expression a new expression string.
203 procedure TZExpressionParser.Parse(Expression: string);
206 FExpression := Trim(Expression);
207 if FExpression <> '' then
211 if HasMoreTokens then
213 raise TZParseError.Create(
214 Format(SSyntaxErrorNear, [SoftVarManager.GetAsString(GetToken.Value)]));
220 Checks are there more tokens for processing.
221 @return <code>TRUE</code> if some tokens are present.
223 function TZExpressionParser.HasMoreTokens: Boolean;
225 Result := FTokenIndex < FInitialTokens.Count;
229 Gets the current token object.
230 @param tokens a collection of tokens.
231 @returns the current token object.
233 function TZExpressionParser.GetToken: TZExpressionToken;
235 if FTokenIndex < FInitialTokens.Count then
236 Result := TZExpressionToken(FInitialTokens[FTokenIndex])
242 Gets the next token object.
243 @param tokens a collection of tokens.
244 @returns the next token object.
246 function TZExpressionParser.GetNextToken: TZExpressionToken;
248 if (FTokenIndex + 1) < FInitialTokens.Count then
249 Result := TZExpressionToken(FInitialTokens[FTokenIndex + 1])
255 Shifts the current token object.
257 procedure TZExpressionParser.ShiftToken;
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.
268 function TZExpressionParser.CheckTokenTypes(
269 TokenTypes: array of TZExpressionTokenType): Boolean;
272 Temp: TZExpressionToken;
275 for I := Low(TokenTypes) to High(TokenTypes) do
277 if (FTokenIndex + I) < FInitialTokens.Count then
279 Temp := TZExpressionToken(FInitialTokens[FTokenIndex + I]);
280 Result := Temp.TokenType = TokenTypes[I];
289 Inc(FTokenIndex, Length(TokenTypes));
293 Tokenizes the given expression and prepares an initial tokens list.
295 procedure TZExpressionParser.TokenizeExpression;
301 TokenType: TZExpressionTokenType;
302 TokenValue: TZVariant;
304 Tokens := FTokenizer.TokenizeBufferToList(FExpression,
305 [toSkipWhitespaces, toSkipComments, toSkipEOF, toDecodeStrings]);
309 while TokenIndex < Tokens.Count do
311 TokenType := ttUnknown;
312 TokenValue := NullVariant;
313 case TZTokenType({$IFDEF FPC}Pointer({$ENDIF}
314 Tokens.Objects[TokenIndex]{$IFDEF FPC}){$ENDIF}) of
317 Temp := UpperCase(Tokens[TokenIndex]);
318 if Temp = 'TRUE' then
320 TokenType := ttConstant;
321 TokenValue:= EncodeBoolean(True);
323 else if Temp = 'FALSE' then
325 TokenType := ttConstant;
326 TokenValue:= EncodeBoolean(False);
330 for I := Low(OperatorTokens) to High(OperatorTokens) do
332 if OperatorTokens[I] = Temp then
334 TokenType := OperatorCodes[I];
342 TokenType := ttVariable;
343 Temp := Tokens[TokenIndex];
344 if FVariables.IndexOf(Temp) < 0 then
345 FVariables.Add(Temp);
346 TokenValue:= EncodeString(Temp);
350 TokenType := ttConstant;
351 TokenValue:= EncodeInteger(StrToInt64(Tokens[TokenIndex]));
355 TokenType := ttConstant;
356 TokenValue:= EncodeFloat(SqlStrToFloat(AnsiString(Tokens[TokenIndex])));
360 TokenType := ttConstant;
361 TokenValue:= EncodeString(Tokens[TokenIndex]);
365 Temp := Tokens[TokenIndex];
366 for I := Low(OperatorTokens) to High(OperatorTokens) do
368 if Temp = OperatorTokens[I] then
370 TokenType := OperatorCodes[I];
375 ttTime,ttDate,ttDateTime:
377 TokenType := ttConstant;
378 TokenValue:= EncodeDateTime(StrToDateTime(Tokens[TokenIndex]));
382 if TokenType = ttUnknown then
383 raise TZParseError.Create(Format(SUnknownSymbol, [Tokens[TokenIndex]]));
386 FInitialTokens.Add(TZExpressionToken.Create(TokenType, TokenValue));
394 Performs a syntax analyze at level 0.
396 procedure TZExpressionParser.SyntaxAnalyse;
398 Token: TZExpressionToken;
400 if not HasMoreTokens then
401 raise TZParseError.Create(SUnexpectedExprEnd);
404 while HasMoreTokens do
407 if not (Token.TokenType in [ttAnd, ttOr, ttXor]) then
411 FResultTokens.Add(TZExpressionToken.Create(Token.TokenType, NullVariant));
416 Performs a syntax analyze at level 1.
418 procedure TZExpressionParser.SyntaxAnalyse1;
420 Token: TZExpressionToken;
422 if not HasMoreTokens then
423 raise TZParseError.Create(SUnexpectedExprEnd);
426 if Token.TokenType = ttNot then
430 FResultTokens.Add(TZExpressionToken.Create(Token.TokenType, NullVariant));
437 Performs a syntax analyze at level 2.
439 procedure TZExpressionParser.SyntaxAnalyse2;
441 Token: TZExpressionToken;
443 if not HasMoreTokens then
444 raise TZParseError.Create(SUnexpectedExprEnd);
447 while HasMoreTokens do
450 if not (Token.TokenType in [ttEqual, ttNotEqual, ttMore, ttLess,
451 ttEqualMore, ttEqualLess]) then
455 FResultTokens.Add(TZExpressionToken.Create(Token.TokenType, NullVariant));
460 Performs a syntax analyze at level 3.
462 procedure TZExpressionParser.SyntaxAnalyse3;
464 Token: TZExpressionToken;
466 if not HasMoreTokens then
467 raise TZParseError.Create(SUnexpectedExprEnd);
470 while HasMoreTokens do
473 if Token.TokenType in [ttPlus, ttMinus, ttLike] then
477 FResultTokens.Add(TZExpressionToken.Create(Token.TokenType, NullVariant));
479 else if CheckTokenTypes([ttNot, ttLike]) then
482 FResultTokens.Add(TZExpressionToken.Create(ttNotLike, NullVariant));
484 else if CheckTokenTypes([ttIs, ttNull]) then
486 FResultTokens.Add(TZExpressionToken.Create(ttIsNull, NullVariant));
488 else if CheckTokenTypes([ttIs, ttNot, ttNull]) then
490 FResultTokens.Add(TZExpressionToken.Create(ttIsNotNull, NullVariant));
498 Performs a syntax analyze at level 4.
500 procedure TZExpressionParser.SyntaxAnalyse4;
502 Token: TZExpressionToken;
504 if not HasMoreTokens then
505 raise TZParseError.Create(SUnexpectedExprEnd);
508 while HasMoreTokens do
511 if not (Token.TokenType in [ttStar, ttSlash, ttProcent]) then
515 FResultTokens.Add(TZExpressionToken.Create(Token.TokenType, NullVariant));
520 Performs a syntax analyze at level 5.
522 procedure TZExpressionParser.SyntaxAnalyse5;
524 Token: TZExpressionToken;
526 if not HasMoreTokens then
527 raise TZParseError.Create(SUnexpectedExprEnd);
530 while HasMoreTokens do
533 if Token.TokenType <> ttPower then
537 FResultTokens.Add(TZExpressionToken.Create(Token.TokenType, NullVariant));
542 Performs a syntax analyze at level 6.
544 procedure TZExpressionParser.SyntaxAnalyse6;
546 ParamsCount: Integer;
547 Unary, Token: TZExpressionToken;
548 Primitive, NextToken: TZExpressionToken;
551 if not HasMoreTokens then
552 raise TZParseError.Create(SUnexpectedExprEnd);
555 if Unary.TokenType = ttPlus then
560 else if Unary.TokenType = ttMinus then
562 Unary.TokenType := ttUnary;
568 if not HasMoreTokens then
569 raise TZParseError.Create(SUnexpectedExprEnd);
571 Primitive := GetToken;
572 NextToken := GetNextToken;
573 if (Primitive.TokenType = ttVariable) and (NextToken <> nil)
574 and (NextToken.TokenType = ttLeftBrace) then
575 Primitive.TokenType := ttFunction;
577 if Primitive.TokenType in [ttConstant, ttVariable] then
580 FResultTokens.Add(TZExpressionToken.Create(
581 Primitive.TokenType, Primitive.Value));
583 else if Primitive.TokenType = ttLeftBrace then
587 if not HasMoreTokens then
588 raise TZParseError.Create(SUnexpectedExprEnd);
589 Primitive := GetToken;
590 if Primitive.TokenType <> ttRightBrace then
591 raise TZParseError.Create(SRightBraceExpected);
594 else if Primitive.TokenType = ttFunction then
598 if Token.TokenType <> ttLeftBrace then
599 raise TZParseError.Create(SInternalError);
604 if (Token = nil) or (Token.TokenType = ttRightBrace) then
609 until (Token = nil) or (Token.TokenType <> ttComma);
611 if not HasMoreTokens then
612 raise TZParseError.Create(SUnexpectedExprEnd);
613 if Token.TokenType <> ttRightBrace then
614 raise TZParseError.Create(SRightBraceExpected);
617 Temp:= EncodeInteger(ParamsCount);
618 FResultTokens.Add(TZExpressionToken.Create(ttConstant, Temp));
619 FResultTokens.Add(TZExpressionToken.Create(Primitive.TokenType,
623 raise TZParseError.Create(SSyntaxError);
626 FResultTokens.Add(TZExpressionToken.Create(Unary.TokenType, NullVariant));