00001 {*********************************************************}
00002 { }
00003 { Zeos Database Objects }
00004 { String tokenizing classes for Sybase }
00005 { }
00006 { Originally written by Janos Fegyverneki }
00007 { }
00008 {*********************************************************}
00009
00010 {@********************************************************}
00011 { Copyright (c) 1999-2006 Zeos Development Group }
00012 { }
00013 { License Agreement: }
00014 { }
00015 { This library is distributed in the hope that it will be }
00016 { useful, but WITHOUT ANY WARRANTY; without even the }
00017 { implied warranty of MERCHANTABILITY or FITNESS FOR }
00018 { A PARTICULAR PURPOSE. See the GNU Lesser General }
00019 { Public License for more details. }
00020 { }
00021 { The source code of the ZEOS Libraries and packages are }
00022 { distributed under the Library GNU General Public }
00023 { License (see the file COPYING / COPYING.ZEOS) }
00024 { with the following modification: }
00025 { As a special exception, the copyright holders of this }
00026 { library give you permission to link this library with }
00027 { independent modules to produce an executable, }
00028 { regardless of the license terms of these independent }
00029 { modules, and to copy and distribute the resulting }
00030 { executable under terms of your choice, provided that }
00031 { you also meet, for each linked independent module, }
00032 { the terms and conditions of the license of that module. }
00033 { An independent module is a module which is not derived }
00034 { from or based on this library. If you modify this }
00035 { library, you may extend this exception to your version }
00036 { of the library, but you are not obligated to do so. }
00037 { If you do not wish to do so, delete this exception }
00038 { statement from your version. }
00039 { }
00040 { }
00041 { The project web site is located on: }
00042 { http:
00043 { http:
00044 { svn:
00045 { }
00046 { http:
00047 { http:
00048 { }
00049 { }
00050 { }
00051 { Zeos Development Group. }
00052 {********************************************************@}
00053
00054 unit ZSybaseToken;
00055
00056 interface
00057
00058 {$I ZParseSql.inc}
00059
00060 uses
00061 Classes, SysUtils, ZTokenizer, ZCompatibility, ZGenericSqlToken;
00062
00063 type
00064
00065 {** Implements a Sybase-specific number state object. }
00066 TZSybaseNumberState = class (TZNumberState)
00067 public
00068 function NextToken(Stream: TStream; FirstChar: Char;
00069 Tokenizer: TZTokenizer): TZToken; override;
00070 end;
00071
00072 {** Implements a Sybase-specific quote string state object. }
00073 TZSybaseQuoteState = class (TZQuoteState)
00074 public
00075 function NextToken(Stream: TStream; FirstChar: Char;
00076 Tokenizer: TZTokenizer): TZToken; override;
00077
00078 function EncodeString(const Value: string; QuoteChar: Char): string; override;
00079 function DecodeString(const Value: string; QuoteChar: Char): string; override;
00080 end;
00081
00082 {**
00083 This state will either delegate to a comment-handling
00084 state, or return a token with just a slash in it.
00085 }
00086 TZSybaseCommentState = class (TZCppCommentState)
00087 public
00088 function NextToken(Stream: TStream; FirstChar: Char;
00089 Tokenizer: TZTokenizer): TZToken; override;
00090 end;
00091
00092 {** Implements a symbol state object. }
00093 TZSybaseSymbolState = class (TZSymbolState)
00094 public
00095 constructor Create;
00096 end;
00097
00098 {** Implements a word state object. }
00099 TZSybaseWordState = class (TZGenericSQLWordState)
00100 public
00101 constructor Create;
00102 end;
00103
00104 {** Implements a default tokenizer object. }
00105 TZSybaseTokenizer = class (TZTokenizer)
00106 public
00107 constructor Create;
00108 end;
00109
00110 implementation
00111
00112 { TZSybaseNumberState }
00113
00114 {**
00115 Return a number token from a reader.
00116 @return a number token from a reader
00117 }
00118 function TZSybaseNumberState.NextToken(Stream: TStream; FirstChar: Char;
00119 Tokenizer: TZTokenizer): TZToken;
00120 var
00121 HexDecimal: Boolean;
00122 FloatPoint: Boolean;
00123 LastChar: Char;
00124
00125 function ReadHexDigits: string;
00126 begin
00127 Result := '';
00128 LastChar := #0;
00129 while Stream.Read(LastChar, 1) > 0 do
00130 begin
00131 if LastChar in ['0'..'9','a'..'f','A'..'F'] then
00132 begin
00133 Result := Result + LastChar;
00134 HexDecimal := HexDecimal or (LastChar in ['a'..'f','A'..'F']);
00135 LastChar := #0;
00136 end
00137 else
00138 begin
00139 Stream.Seek(-1, soFromCurrent);
00140 Break;
00141 end;
00142 end;
00143 end;
00144
00145 function ReadDecDigits: string;
00146 begin
00147 Result := '';
00148 LastChar := #0;
00149 while Stream.Read(LastChar, 1) > 0 do
00150 begin
00151 if LastChar in ['0'..'9'] then
00152 begin
00153 Result := Result + LastChar;
00154 LastChar := #0;
00155 end
00156 else
00157 begin
00158 Stream.Seek(-1, soFromCurrent);
00159 Break;
00160 end;
00161 end;
00162 end;
00163
00164 begin
00165 HexDecimal := False;
00166 FloatPoint := FirstChar = '.';
00167 LastChar := #0;
00168
00169 Result.Value := FirstChar;
00170 Result.TokenType := ttUnknown;
00171
00172 { Reads the first part of the number before decimal point }
00173 if not FloatPoint then
00174 begin
00175 Result.Value := Result.Value + ReadDecDigits;
00176 FloatPoint := (LastChar = '.') and not HexDecimal;
00177 if FloatPoint then
00178 begin
00179 Stream.Read(LastChar, 1);
00180 Result.Value := Result.Value + LastChar;
00181 end;
00182 end;
00183
00184 { Reads the second part of the number after decimal point }
00185 if FloatPoint then
00186 Result.Value := Result.Value + ReadDecDigits;
00187
00188 { Reads a power part of the number }
00189 if not HexDecimal and (LastChar in ['e','E']) then
00190 begin
00191 Stream.Read(LastChar, 1);
00192 Result.Value := Result.Value + LastChar;
00193 FloatPoint := True;
00194
00195 Stream.Read(LastChar, 1);
00196 if LastChar in ['0'..'9','-','+'] then
00197 Result.Value := Result.Value + LastChar + ReadDecDigits
00198 else
00199 begin
00200 Result.Value := Copy(Result.Value, 1, Length(Result.Value) - 1);
00201 Stream.Seek(-2, soFromCurrent);
00202 end;
00203 end;
00204
00205 { Reads the nexdecimal number }
00206 if (Result.Value = '0') and (LastChar in ['x','X']) then
00207 begin
00208 Stream.Read(LastChar, 1);
00209 Result.Value := Result.Value + LastChar + ReadHexDigits;
00210 HexDecimal := True;
00211 end;
00212
00213 { Prepare the result }
00214 if Result.Value = '.' then
00215 begin
00216 if Tokenizer.SymbolState <> nil then
00217 Result := Tokenizer.SymbolState.NextToken(Stream, FirstChar, Tokenizer);
00218 end
00219 else
00220 begin
00221 if HexDecimal then
00222 Result.TokenType := ttHexDecimal
00223 else if FloatPoint then
00224 Result.TokenType := ttFloat
00225 else Result.TokenType := ttInteger;
00226 end;
00227 end;
00228
00229 { TZSybaseQuoteState }
00230
00231 {**
00232 Return a quoted string token from a reader. This method
00233 will collect characters until it sees a match to the
00234 character that the tokenizer used to switch to this state.
00235
00236 @return a quoted string token from a reader
00237 }
00238 function TZSybaseQuoteState.NextToken(Stream: TStream; FirstChar: Char;
00239 Tokenizer: TZTokenizer): TZToken;
00240 var
00241 ReadChar: Char;
00242 LastChar: Char;
00243 begin
00244 Result.Value := FirstChar;
00245 LastChar := #0;
00246 while Stream.Read(ReadChar, 1) > 0 do
00247 begin
00248 if ((LastChar = FirstChar) and (ReadChar <> FirstChar)
00249 and (FirstChar <> '[')) or ((FirstChar = '[') and (LastChar = ']')) then
00250 begin
00251 Stream.Seek(-1, soFromCurrent);
00252 Break;
00253 end;
00254 Result.Value := Result.Value + ReadChar;
00255 if (LastChar = FirstChar) and (ReadChar = FirstChar) then
00256 LastChar := #0
00257 else LastChar := ReadChar;
00258 end;
00259
00260 if FirstChar in ['"', '['] then
00261 Result.TokenType := ttWord
00262 else Result.TokenType := ttQuoted;
00263 end;
00264
00265 {**
00266 Encodes a string value.
00267 @param Value a string value to be encoded.
00268 @param QuoteChar a string quote character.
00269 @returns an encoded string.
00270 }
00271 function TZSybaseQuoteState.EncodeString(const Value: string; QuoteChar: Char): string;
00272 begin
00273 if QuoteChar = '[' then
00274 Result := '[' + Value + ']'
00275 else if QuoteChar in [#39, '"'] then
00276 Result := QuoteChar + Value + QuoteChar
00277 else Result := Value;
00278 end;
00279
00280 {**
00281 Decodes a string value.
00282 @param Value a string value to be decoded.
00283 @param QuoteChar a string quote character.
00284 @returns an decoded string.
00285 }
00286 function TZSybaseQuoteState.DecodeString(const Value: string; QuoteChar: Char): string;
00287 begin
00288 Result := Value;
00289 if Length(Value) >= 2 then
00290 begin
00291 if (QuoteChar in [#39, '"']) and (Value[1] = QuoteChar)
00292 and (Value[Length(Value)] = QuoteChar) then
00293 begin
00294 if Length(Value) > 2 then
00295 Result := AnsiDequotedStr(Value, QuoteChar)
00296 else Result := '';
00297 end
00298 else if (QuoteChar = '[') and (Value[1] = QuoteChar)
00299 and (Value[Length(Value)] = ']') then
00300 Result := Copy(Value, 2, Length(Value) - 2)
00301 end;
00302 end;
00303
00304 { TZSybaseCommentState }
00305
00306 {**
00307 Gets a Sybase specific comments like # or .
00308 @return either just a slash token, or the results of
00309 delegating to a comment-handling state
00310 }
00311 function TZSybaseCommentState.NextToken(Stream: TStream; FirstChar: Char;
00312 Tokenizer: TZTokenizer): TZToken;
00313 var
00314 ReadChar: Char;
00315 ReadNum: Integer;
00316 begin
00317 Result.Value := FirstChar;
00318 Result.TokenType := ttUnknown;
00319
00320 if FirstChar = '-' then
00321 begin
00322 ReadNum := Stream.Read(ReadChar, 1);
00323 if (ReadNum > 0) and (ReadChar = '-') then
00324 begin
00325 Result.TokenType := ttComment;
00326 Result.Value := '--' + GetSingleLineComment(Stream);
00327 end
00328 else
00329 begin
00330 if ReadNum > 0 then
00331 Stream.Seek(-1, soFromCurrent);
00332 end;
00333 end
00334 else if FirstChar = '/' then
00335 begin
00336 ReadNum := Stream.Read(ReadChar, 1);
00337 if (ReadNum > 0) and (ReadChar = '*') then
00338 begin
00339 Result.TokenType := ttComment;
00340 Result.Value := '
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429