fpsqlscanner.pp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. {
  2. This file is part of the Free Component Library
  3. Copyright (c) 2010-2014 by the Free Pascal development team
  4. SQL source lexical scanner
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$mode objfpc}
  12. {$h+}
  13. unit fpsqlscanner;
  14. interface
  15. uses SysUtils, Classes, Contnrs;
  16. resourcestring
  17. SErrInvalidCharacter = 'Invalid character ''%s''';
  18. SErrOpenString = 'string exceeds end of line';
  19. SErrIncludeFileNotFound = 'Could not find include file ''%s''';
  20. SErrIfXXXNestingLimitReached = 'Nesting of $IFxxx too deep';
  21. SErrInvalidPPElse = '$ELSE without matching $IFxxx';
  22. SErrInvalidPPEndif = '$ENDIF without matching $IFxxx';
  23. SInvalidHexadecimalNumber = 'Invalid hexadecimal number';
  24. SErrInvalidNonEqual = 'SyntaxError: != or !== expected';
  25. SBarExpected = '| character expected';
  26. type
  27. TSQLToken = (tsqlUnknown,
  28. // Specials
  29. tsqlEOF,tsqlWhiteSpace,
  30. tsqlString {string literal},
  31. tsqlIdentifier {a table etc name},
  32. tsqlSymbolString {a string containing symbols/punctuation marks; only rarely used - e.g. in SET TERM ^ ;},
  33. tsqlIntegerNumber,tsqlFloatNumber,tsqlComment,
  34. tsqlBraceOpen,tsqlBraceClose,tsqlSquareBraceOpen,tsqlSquareBraceClose,
  35. tsqlPlaceHolder {question mark},
  36. tsqlCOMMA,tsqlCOLON,tsqlDOT,tsqlSEMICOLON,
  37. tsqlGT,tsqlLT,tsqlPLUS,tsqlMINUS,tsqlMUL,tsqlDIV,tsqlConcatenate,
  38. tsqlEQ,tsqlGE,tsqlLE,tsqlNE,
  39. { Reserved words/keywords start here. They must be last }
  40. { Note: if adding before tsqlALL or after tsqlWHEN please update FirstKeyword/LastKeyword }
  41. tsqlALL, tsqlAND, tsqlANY, tsqlASC, tsqlASCENDING, tsqlAVG, tsqlALTER, tsqlAdd, tsqlActive, tsqlAction, tsqlAs,tsqlAt, tsqlAuto, tsqlAfter,tsqlAdmin,
  42. tsqlBETWEEN, tsqlBinary, tsqlBY, tsqlBLOB, tsqlBegin, tsqlBefore,
  43. tsqlCOLLATE, tsqlCONTAINING, tsqlCOUNT, tsqlCREATE, tsqlCOLUMN, tsqlCONSTRAINT, tsqlChar,tsqlCHARACTER, tsqlCHECK, tsqlComputed,tsqlCASCADE, tsqlCast, tsqlCommit,tsqlConnect,tsqlCache,tsqlConditional,tsqlCString,
  44. tsqlDESC, tsqlDESCENDING, tsqlDISTINCT, tsqlDEFAULT, tsqlDELETE, tsqlDO, tsqlDouble, tsqlDECLARE, tsqlDROP, tsqlDomain, tsqlDecimal, tsqlDate,tsqlDatabase,
  45. tsqlESCAPE, tsqlEXISTS, tsqlELSE, tsqlException, tsqlExternal, tsqlExecute, tsqlEnd,tsqlExit,tsqlEntrypoint,tsqlExtract,
  46. tsqlFROM, tsqlFULL, tsqlFOREIGN, tsqlFOR, tsqlFUNCTION, tsqlFLOAT, tsqlFile,tsqlFreeIt,
  47. tsqlGenerator, tsqlGROUP, tsqlGenID,tsqlGDSCODE,tsqlGrant,
  48. tsqlHAVING,
  49. tsqlIF, tsqlIN, tsqlINNER, tsqlINSERT, tsqlINT, tsqlINTEGER, tsqlINTO, tsqlIS, tsqlINDEX, tsqlInactive,
  50. tsqlJOIN,
  51. tsqlKEY,
  52. tsqlLEFT, tsqlLIKE, tsqlLength,
  53. tsqlMAX, tsqlMIN, tsqlMERGE, tsqlManual, tsqlModuleName,
  54. tsqlNOT, tsqlNULL, tsqlNUMERIC , tsqlNChar, tsqlNATIONAL,tsqlNO, tsqlNatural,
  55. tsqlOFF {not an FB reserved word; used in isql scripts}, tsqlON, tsqlOR, tsqlORDER, tsqlOUTER, tsqlOption,
  56. tsqlPrecision, tsqlPRIMARY, tsqlProcedure, tsqlPosition, tsqlPlan, tsqlPassword, tsqlPage,tsqlPages,tsqlPageSize,tsqlPostEvent,tsqlPrivileges,tsqlPublic,
  57. tsqlRIGHT, tsqlROLE, tsqlReferences, tsqlRollBack, tsqlRelease, tsqlretain, tsqlReturningValues,tsqlReturns, tsqlrevoke,
  58. tsqlSELECT, tsqlSET, tsqlSINGULAR, tsqlSOME, tsqlSTARTING, tsqlSUM, tsqlSKIP,tsqlSUBTYPE,tsqlSize,tsqlSegment, tsqlSORT, tsqlSnapShot,tsqlSchema,tsqlShadow,tsqlSuspend,tsqlSQLCode,tsqlSmallint,
  59. tSQLTABLE, tsqlText, tsqlTrigger, tsqlTime, tsqlTimeStamp, tsqlType, tsqlTo, tsqlTransaction, tsqlThen,
  60. tsqlUNION, tsqlUPDATE, tsqlUPPER, tsqlUNIQUE, tsqlUSER,
  61. tsqlValue, tsqlVALUES, tsqlVARIABLE, tsqlVIEW, tsqlVARCHAR,TSQLVARYING,
  62. tsqlWHERE, tsqlWITH, tsqlWHILE, tsqlWork, tsqlWhen
  63. );
  64. TSQLTokens = set of TSQLToken;
  65. const
  66. FirstKeyword = tsqlAll;
  67. LastKeyWord = tsqlWhen;
  68. sqlComparisons = [tsqleq,tsqlGE,tsqlLE,tsqlNE,tsqlGT,tsqlLT,tsqlIn,tsqlIS,
  69. tsqlbetween,tsqlLike,tsqlContaining,tsqlStarting,tsqlNOT];
  70. sqlInvertableComparisons = [tsqlLike,tsqlContaining,tsqlStarting,tsqlIN,tsqlIS, tsqlBetween];
  71. // Strings that represent tokens in TSQLToken
  72. TokenInfos: array[TSQLToken] of string = ('unknown',
  73. // Specials
  74. 'EOF','whitespace',
  75. 'String',
  76. 'identifier',
  77. 'symbol string',
  78. 'integer number','float number', 'comment',
  79. '(',')', '[',']',
  80. '?',',',':','.',';','>','<',
  81. '+','-','*','/','||',
  82. '=','>=','<=','<>',
  83. // Identifiers last:
  84. 'ALL', 'AND', 'ANY', 'ASC', 'ASCENDING', 'AVG', 'ALTER', 'ADD','ACTIVE','ACTION', 'AS', 'AT', 'AUTO', 'AFTER', 'ADMIN',
  85. 'BETWEEN', 'BINARY', 'BY', 'BLOB','BEGIN', 'BEFORE',
  86. 'COLLATE', 'CONTAINING', 'COUNT', 'CREATE', 'COLUMN', 'CONSTRAINT', 'CHAR','CHARACTER','CHECK', 'COMPUTED','CASCADE','CAST', 'COMMIT', 'CONNECT', 'CACHE','CONDITIONAL', 'CSTRING',
  87. 'DESC', 'DESCENDING', 'DISTINCT', 'DEFAULT', 'DELETE', 'DO', 'DOUBLE', 'DECLARE', 'DROP', 'DOMAIN', 'DECIMAL', 'DATE','DATABASE',
  88. 'ESCAPE', 'EXISTS', 'ELSE', 'EXCEPTION', 'EXTERNAL','EXECUTE', 'END','EXIT','ENTRY_POINT','EXTRACT',
  89. 'FROM', 'FULL','FOREIGN', 'FOR', 'FUNCTION', 'FLOAT','FILE', 'FREE_IT',
  90. 'GENERATOR', 'GROUP', 'GEN_ID','GDSCODE','GRANT',
  91. 'HAVING',
  92. 'IF', 'IN', 'INNER', 'INSERT', 'INT', 'INTEGER', 'INTO', 'IS', 'INDEX', 'INACTIVE',
  93. 'JOIN',
  94. 'KEY',
  95. 'LEFT', 'LIKE', 'LENGTH',
  96. 'MAX', 'MIN', 'MERGE', 'MANUAL', 'MODULE_NAME',
  97. 'NOT', 'NULL', 'NUMERIC','NCHAR','NATIONAL', 'NO', 'NATURAL',
  98. 'OFF', 'ON', 'OR', 'ORDER', 'OUTER', 'OPTION',
  99. 'PRECISION', 'PRIMARY', 'PROCEDURE','POSITION','PLAN', 'PASSWORD','PAGE','PAGES','PAGE_SIZE','POST_EVENT','PRIVILEGES','PUBLIC',
  100. 'RIGHT', 'ROLE', 'REFERENCES', 'ROLLBACK','RELEASE', 'RETAIN', 'RETURNING_VALUES', 'RETURNS','REVOKE',
  101. 'SELECT', 'SET', 'SINGULAR', 'SOME', 'STARTING', 'SUM', 'SKIP','SUB_TYPE', 'SIZE', 'SEGMENT', 'SORT', 'SNAPSHOT','SCHEMA','SHADOW','SUSPEND','SQLCODE','SMALLINT',
  102. 'TABLE', 'TEXT', 'TRIGGER', 'TIME', 'TIMESTAMP', 'TYPE', 'TO', 'TRANSACTION', 'THEN',
  103. 'UNION', 'UPDATE', 'UPPER', 'UNIQUE', 'USER',
  104. 'VALUE','VALUES','VARIABLE', 'VIEW','VARCHAR','VARYING',
  105. 'WHERE', 'WITH', 'WHILE','WORK','WHEN'
  106. );
  107. Type
  108. TLineReader = class
  109. public
  110. function IsEOF: Boolean; virtual; abstract;
  111. function ReadLine: string; virtual; abstract;
  112. end;
  113. { TStreamLineReader }
  114. TStreamLineReader = class(TLineReader)
  115. private
  116. FStream : TStream;
  117. Buffer : Array[0..1024] of Byte;
  118. FBufPos,
  119. FBufLen : Integer;
  120. procedure FillBuffer;
  121. public
  122. Constructor Create(AStream : TStream);
  123. function IsEOF: Boolean; override;
  124. function ReadLine: string; override;
  125. end;
  126. TFileLineReader = class(TLineReader)
  127. private
  128. FTextFile: Text;
  129. FileOpened: Boolean;
  130. public
  131. constructor Create(const AFilename: string);
  132. destructor Destroy; override;
  133. function IsEOF: Boolean; override;
  134. function ReadLine: string; override;
  135. end;
  136. ESQLScannerError = class(Exception);
  137. { TSQLScanner }
  138. TSQLScannerOption = (soReturnComments,
  139. soReturnWhiteSpace,
  140. soBackslashEscapes,
  141. soNoDoubleDelimIsChar,
  142. soDoubleQuoteStringLiteral, // Default: single quote is string literal
  143. soSingleQuoteIdentifier, // Default: double quote is identifier. Ignored if soDoubleQuoteStringLiteral is not specified
  144. soBackQuoteIdentifier // Default: double quote is identifier
  145. );
  146. TSQLScannerOptions = Set of TSQLScannerOption;
  147. TSQLScanner = class
  148. private
  149. FOptions: TSQLScannerOptions;
  150. FReturnComments: Boolean;
  151. FReturnWhiteSpace: Boolean;
  152. FSourceFile: TLineReader;
  153. FSourceFilename: string;
  154. FCurRow: Integer;
  155. FCurToken: TSQLToken;
  156. FCurTokenString: string;
  157. FCurLine: string;
  158. TokenStr: PChar;
  159. FSourceStream : TStream;
  160. FOwnSourceFile : Boolean;
  161. FKeyWords : TFPHashList;
  162. FExclude : TStringList;
  163. function CommentDiv: TSQLToken;
  164. // Used to parse out an identifier/name and store it in the list of identifiers
  165. function DoIdentifier : TSQLToken;
  166. // Used to parse out a string containing symbols
  167. function DoSymbolString : TSQLToken;
  168. function DoMultiLineComment: TSQLToken;
  169. function DoNumericLiteral: TSQLToken;
  170. function DoSingleLineComment: TSQLToken;
  171. function DoStringLiteral: TSQLToken;
  172. function DoWhiteSpace: TSQLToken;
  173. // Reads a new line into TokenStr and returns true
  174. // If no new lines, returns false
  175. function FetchLine: Boolean;
  176. function GetCurColumn: Integer;
  177. function GetExcludeKeywords: TStrings;
  178. function ReadUnicodeEscape: WideChar;
  179. procedure SetExcludeKeywords(const AValue: TStrings);
  180. procedure Setoptions(const AValue: TSQLScannerOptions);
  181. procedure ClearKeywords(Sender: TObject);
  182. protected
  183. Procedure BuildKeyWords; virtual;
  184. procedure Error(const Msg: string);overload;
  185. procedure Error(const Msg: string; Args: array of Const);overload;
  186. public
  187. constructor Create(ALineReader: TLineReader);
  188. constructor Create(AStream : TStream);
  189. destructor Destroy; override;
  190. procedure OpenFile(const AFilename: string);
  191. Function FetchToken: TSQLToken;
  192. Function IsEndOfLine : Boolean;
  193. Property Options : TSQLScannerOptions Read FOptions Write Setoptions;
  194. property SourceFile: TLineReader read FSourceFile;
  195. property CurFilename: string read FSourceFilename;
  196. property CurLine: string read FCurLine;
  197. property CurRow: Integer read FCurRow;
  198. property CurColumn: Integer read GetCurColumn;
  199. property CurToken: TSQLToken read FCurToken;
  200. property CurTokenString: string read FCurTokenString;
  201. Property ExcludeKeywords : TStrings Read GetExcludeKeywords Write SetExcludeKeywords;
  202. end;
  203. implementation
  204. Var
  205. // Keeps track of identifiers used
  206. IdentifierTokens : array[FirstKeyword..LastKeyWord] of TSQLToken;
  207. IdentifierTokensOK : Boolean;
  208. Resourcestring
  209. SErrUnknownToken = 'Unknown token: %s';
  210. Procedure BuildIdentifierTokens;
  211. Var
  212. T : TSQLToken;
  213. begin
  214. For T:=FirstKeyword to LastKeyWord do
  215. IdentifierTokens[T]:=T;
  216. end;
  217. constructor TFileLineReader.Create(const AFilename: string);
  218. begin
  219. inherited Create;
  220. Assign(FTextFile, AFilename);
  221. Reset(FTextFile);
  222. FileOpened := true;
  223. end;
  224. destructor TFileLineReader.Destroy;
  225. begin
  226. if FileOpened then
  227. Close(FTextFile);
  228. inherited Destroy;
  229. end;
  230. function TFileLineReader.IsEOF: Boolean;
  231. begin
  232. Result := EOF(FTextFile);
  233. end;
  234. function TFileLineReader.ReadLine: string;
  235. begin
  236. ReadLn(FTextFile, Result);
  237. end;
  238. constructor TSQLScanner.Create(ALineReader: TLineReader);
  239. begin
  240. inherited Create;
  241. FSourceFile := ALineReader;
  242. FKeywords:=TFPHashList.Create;
  243. end;
  244. constructor TSQLScanner.Create(AStream: TStream);
  245. begin
  246. FSourceStream:=ASTream;
  247. FOwnSourceFile:=True;
  248. Create(TStreamLineReader.Create(AStream));
  249. end;
  250. destructor TSQLScanner.Destroy;
  251. begin
  252. If FOwnSourceFile then
  253. FSourceFile.Free;
  254. FreeAndNil(FKeywords);
  255. inherited Destroy;
  256. end;
  257. procedure TSQLScanner.OpenFile(const AFilename: string);
  258. begin
  259. FSourceFile := TFileLineReader.Create(AFilename);
  260. FOwnSourceFile:=True;
  261. FSourceFilename := AFilename;
  262. end;
  263. procedure TSQLScanner.Error(const Msg: string);
  264. begin
  265. raise ESQLScannerError.Create(Msg);
  266. end;
  267. procedure TSQLScanner.Error(const Msg: string; Args: array of Const);
  268. begin
  269. raise ESQLScannerError.CreateFmt(Msg, Args);
  270. end;
  271. function TSQLScanner.FetchLine: Boolean;
  272. begin
  273. if FSourceFile.IsEOF then
  274. begin
  275. FCurLine := '';
  276. TokenStr := nil;
  277. Result := false;
  278. end else
  279. begin
  280. FCurLine := FSourceFile.ReadLine;
  281. TokenStr := PChar(CurLine);
  282. Result := true;
  283. Inc(FCurRow);
  284. end;
  285. end;
  286. function TSQLScanner.DoWhiteSpace : TSQLToken;
  287. begin
  288. Result:=tsqlWhitespace;
  289. repeat
  290. Inc(TokenStr);
  291. if TokenStr[0] = #0 then
  292. if not FetchLine then
  293. begin
  294. FCurToken := Result;
  295. exit;
  296. end;
  297. until not (TokenStr[0] in [#9, ' ']);
  298. end;
  299. function TSQLScanner.DoSingleLineComment : TSQLToken;
  300. Var
  301. TokenStart : PChar;
  302. Len : Integer;
  303. begin
  304. Inc(TokenStr);
  305. TokenStart := TokenStr;
  306. while TokenStr[0] <> #0 do
  307. Inc(TokenStr);
  308. Len:=TokenStr-TokenStart;
  309. SetLength(FCurTokenString, Len);
  310. if (Len>0) then
  311. Move(TokenStart^,FCurTokenString[1],Len);
  312. Result := tsqlComment;
  313. end;
  314. function TSQLScanner.DoMultiLineComment : TSQLToken;
  315. Var
  316. TokenStart : PChar;
  317. Len,OLen : Integer;
  318. PrevToken : Char;
  319. begin
  320. Inc(TokenStr);
  321. TokenStart := TokenStr;
  322. FCurTokenString := '';
  323. OLen:= 0;
  324. PrevToken:=#0;
  325. while Not ((TokenStr[0]='/') and (PrevToken='*')) do
  326. begin
  327. if (TokenStr[0]=#0) then
  328. begin
  329. Len:=TokenStr-TokenStart+1;
  330. SetLength(FCurTokenString,OLen+Len);
  331. if Len>1 then
  332. Move(TokenStart^,FCurTokenString[OLen+1],Len-1);
  333. Inc(OLen,Len);
  334. FCurTokenString[OLen]:=#10;
  335. if not FetchLine then
  336. begin
  337. Result := tsqlEOF;
  338. FCurToken := Result;
  339. exit;
  340. end;
  341. TokenStart := TokenStr;
  342. PrevToken:=#0;
  343. end
  344. else
  345. begin
  346. PrevToken:=TokenStr[0];
  347. Inc(TokenStr);
  348. end;
  349. end;
  350. Len:=TokenStr-TokenStart-1; // -1 for *
  351. SetLength(FCurTokenString, Olen+Len);
  352. if (Len>0) then
  353. begin
  354. Move(TokenStart^, FCurTokenString[Olen + 1], Len);
  355. end;
  356. If TokenStr[0]<>#0 then
  357. Inc(TokenStr);
  358. Result := tsqlComment;
  359. end;
  360. function TSQLScanner.CommentDiv : TSQLToken;
  361. begin
  362. FCurTokenString := '';
  363. Inc(TokenStr);
  364. if (TokenStr[0]='*') then
  365. Result:=DoMultiLineComment
  366. else
  367. Result:=tsqlDiv;
  368. end;
  369. Function TSQLScanner.ReadUnicodeEscape : WideChar;
  370. Var
  371. S : String;
  372. I : Integer;
  373. begin
  374. S:='0000';
  375. For I:=1 to 4 do
  376. begin
  377. Inc(TokenStr);
  378. Case TokenStr[0] of
  379. '0'..'9','A'..'F','a'..'f' :
  380. S[i]:=Upcase(TokenStr[0]);
  381. else
  382. Error(SErrInvalidCharacter, [TokenStr[0]]);
  383. end;
  384. end;
  385. // Takes care of conversion... This needs improvement !!
  386. Result:=WideChar(StrToInt('$'+S));
  387. end;
  388. procedure TSQLScanner.SetExcludeKeywords(const AValue: TStrings);
  389. begin
  390. With ExcludeKeywords do
  391. begin
  392. Clear;
  393. AddStrings(AValue);
  394. end;
  395. end;
  396. procedure TSQLScanner.Setoptions(const AValue: TSQLScannerOptions);
  397. Const
  398. F = [soDoubleQuoteStringLiteral,soSingleQuoteIdentifier];
  399. begin
  400. FOptions:=AValue;
  401. if ((Foptions * F) = [soSingleQuoteIdentifier]) then
  402. Exclude(FOptions,soSingleQuoteIdentifier);
  403. end;
  404. procedure TSQLScanner.BuildKeyWords;
  405. Var
  406. I : TSQLToken;
  407. begin
  408. If Not IdentifierTokensOK then
  409. BuildIdentifierTokens;
  410. If FKeywords.Count>0 then
  411. FKeywords.Clear;
  412. for I:=FirstKeyword to LastKeyword do
  413. if (not Assigned(FExclude)) or (FExclude.IndexOf(TokenInfos[I])=-1) then
  414. FKeywords.Add(TokenInfos[I],@IdentifierTokens[i]);
  415. end;
  416. function TSQLScanner.DoStringLiteral: TSQLToken;
  417. Var
  418. Delim : Char;
  419. TokenStart : PChar;
  420. Len,OLen : Integer;
  421. S : String;
  422. Procedure AppendBufToTokenString(DoNextToken : Boolean);
  423. begin
  424. SetLength(FCurTokenString, OLen + Len+Length(S));
  425. if Len > 0 then
  426. Move(TokenStart^, FCurTokenString[OLen + 1], Len);
  427. If Length(S)>0 then
  428. Move(S[1],FCurTokenString[OLen + Len+1],Length(S));
  429. Inc(OLen, Len+Length(S));
  430. If DoNextToken then
  431. Inc(TokenStr);
  432. TokenStart := TokenStr+1;
  433. end;
  434. Function CheckTokenBuf : Boolean;
  435. begin
  436. Result:=(TokenStr[0]<>#0);
  437. If Not Result then
  438. begin
  439. S:='';
  440. Len:=TokenStr-TokenStart;
  441. AppendBufToTokenString(False);
  442. Result:=FetchLine;
  443. TokenStart:=TokenStr;
  444. end;
  445. end;
  446. begin
  447. Delim:=TokenStr[0];
  448. Inc(TokenStr);
  449. TokenStart := TokenStr;
  450. OLen := 0;
  451. FCurTokenString := '';
  452. while not (TokenStr[0]=#0) do
  453. begin
  454. If (TokenStr[0]=Delim) then
  455. begin
  456. if (not (soNoDoubleDelimIsChar in options)) and (TokenStr[1]=Delim) then
  457. begin
  458. S:=Delim;
  459. Len := TokenStr - TokenStart;
  460. AppendBufToTokenString(True);
  461. end
  462. else
  463. Break;
  464. end
  465. else if (TokenStr[0]='\') and (soBackSlashEscapes in Options) then
  466. begin
  467. // Save length
  468. Len := TokenStr - TokenStart;
  469. Inc(TokenStr);
  470. if not CheckTokenBuf then
  471. Error(SErrOpenString);
  472. // Read escaped token
  473. Case TokenStr[0] of
  474. '"' : S:='"';
  475. '''' : S:='''';
  476. 't' : S:=#9;
  477. 'b' : S:=#8;
  478. 'n' : S:=#10;
  479. 'r' : S:=#13;
  480. 'f' : S:=#12;
  481. '\' : S:='\';
  482. '/' : S:='/';
  483. 'u' : begin
  484. S:=ReadUniCodeEscape;
  485. end;
  486. else
  487. Error(SErrInvalidCharacter, [TokenStr[0]]);
  488. end;
  489. AppendBufToTokenString(False);
  490. end;
  491. Inc(TokenStr);
  492. if not CheckTokenBuf then
  493. Error(SErrOpenString);
  494. end;
  495. if Not CheckTokenBuf then
  496. Error(SErrOpenString);
  497. S:='';
  498. Len := TokenStr - TokenStart;
  499. AppendBufToTokenString(True);
  500. Result := tsqlString;
  501. end;
  502. function TSQLScanner.DoNumericLiteral :TSQLToken;
  503. Var
  504. TokenStart : PChar;
  505. Len : Integer;
  506. isFloat : boolean;
  507. begin
  508. TokenStart := TokenStr;
  509. IsFloat:=False;
  510. while true do
  511. begin
  512. Inc(TokenStr);
  513. case TokenStr[0] of
  514. 'x':
  515. If (TokenStart[0]='0') and ((TokenStr-TokenStart)=1) then
  516. begin
  517. Inc(TokenStr);
  518. while Upcase(TokenStr[0]) in ['0'..'9','A'..'F'] do
  519. Inc(TokenStr);
  520. end
  521. else
  522. Error(SInvalidHexadecimalNumber);
  523. '.':
  524. begin
  525. isfloat:=true;
  526. if TokenStr[1] in ['0'..'9', 'e', 'E'] then
  527. begin
  528. Inc(TokenStr);
  529. repeat
  530. Inc(TokenStr);
  531. until not (TokenStr[0] in ['0'..'9', 'e', 'E','-','+']);
  532. end;
  533. break;
  534. end;
  535. '0'..'9': ;
  536. 'e', 'E':
  537. begin
  538. isFloat:=true;
  539. Inc(TokenStr);
  540. if TokenStr[0] in ['-','+'] then
  541. Inc(TokenStr);
  542. while TokenStr[0] in ['0'..'9'] do
  543. Inc(TokenStr);
  544. break;
  545. end;
  546. else
  547. break;
  548. end;
  549. end;
  550. Len:=TokenStr-TokenStart;
  551. Setlength(FCurTokenString, Len);
  552. if (Len>0) then
  553. Move(TokenStart^,FCurTokenString[1],Len);
  554. If IsFloat then
  555. Result := tsqlFloatNumber
  556. else
  557. Result:=tsqlIntegerNumber;
  558. end;
  559. function TSQLScanner.DoIdentifier : TSQLToken;
  560. Var
  561. TokenStart:PChar;
  562. Len : Integer;
  563. {I : TSQLToken;}
  564. S : ShortString;
  565. P : ^TSQLToken;
  566. begin
  567. Result:=tsqlIdentifier;
  568. TokenStart := TokenStr;
  569. repeat
  570. Inc(TokenStr);
  571. If (TokenStr[0]='\') and (TokenStr[1]='u') then
  572. until not (TokenStr[0] in ['A'..'Z', 'a'..'z', '0'..'9', '_','$']);
  573. Len:=(TokenStr-TokenStart);
  574. SetLength(FCurTokenString,Len);
  575. if Len > 0 then
  576. Move(TokenStart^,FCurTokenString[1],Len);
  577. S:=UpperCase(FCurTokenString);
  578. // Check if this is a keyword or identifier
  579. // to do: Optimize this!
  580. If FKeyWords.Count=0 then
  581. BuildKeyWords;
  582. P:=FKeyWords.Find(S);
  583. If (P<>Nil) then
  584. Result:=P^; //keyword found
  585. { I:=FirstKeyword;
  586. While (Result=tsqlIdentifier) and (I<=Lastkeyword) do
  587. begin
  588. if (S=TokenInfos[i]) then
  589. begin
  590. Result := i;
  591. FCurToken := Result;
  592. exit;
  593. end;
  594. I:=Succ(I);
  595. end;}
  596. end;
  597. function TSQLScanner.DoSymbolString : TSQLToken;
  598. Var
  599. Len : Integer;
  600. P : ^TSQLToken;
  601. TokenStart : PChar;
  602. begin
  603. Result:=tsqlUnknown;
  604. // Get "word" finalized by end of string, space/tab/line ending.
  605. TokenStart:=TokenStr;
  606. repeat
  607. Inc(TokenStr);
  608. until (TokenStr[0] in [#0, #9, #10, #13, ' ']);
  609. Len:=(TokenStr-TokenStart);
  610. if Len > 0 then
  611. begin
  612. result:=tsqlSymbolString;
  613. SetLength(FCurTokenString,Len);
  614. Move(TokenStart^,FCurTokenString[1],Len);
  615. // Check if this is a keyword or identifier/literal
  616. // Probably not (due to naming rules) but it doesn't hurt
  617. If FKeyWords.Count=0 then
  618. BuildKeyWords;
  619. P:=FKeyWords.Find(FCurTokenString); //case-sensitive search
  620. If (P<>Nil) then
  621. Result:=P^; //keyword found, just in case
  622. end;
  623. end;
  624. function TSQLScanner.FetchToken: TSQLToken;
  625. begin
  626. Repeat
  627. if TokenStr = nil then
  628. if not FetchLine then
  629. begin
  630. Result := tsqlEOF;
  631. FCurToken := Result;
  632. exit;
  633. end;
  634. FCurTokenString := '';
  635. case TokenStr[0] of
  636. #0: // Empty line
  637. begin
  638. FetchLine;
  639. Result := tsqlWhitespace;
  640. end;
  641. '/' :
  642. Result:=CommentDiv;
  643. #9, ' ',#10,#13:
  644. Result := DoWhiteSpace;
  645. '''':
  646. begin
  647. Result:=DoStringLiteral;
  648. if (soSingleQuoteIdentifier in Options) then
  649. result:=tsqlIdentifier;
  650. end;
  651. '"':
  652. begin
  653. Result:=DoStringLiteral;
  654. If (soDoubleQuoteStringLiteral in options) then
  655. Result:=tsqlString
  656. else
  657. Result:=tsqlIdentifier;
  658. end;
  659. '`':
  660. begin
  661. Result:=DoStringLiteral;
  662. If (soBackQuoteIdentifier in options) then
  663. Result:=tsqlIdentifier
  664. else
  665. Error(SErrUnknownToken,['`']);
  666. end;
  667. '0'..'9':
  668. Result:=DoNumericLiteral;
  669. '?':
  670. begin
  671. Inc(TokenStr);
  672. Result:=tsqlPlaceHolder;
  673. end;
  674. '!':
  675. begin
  676. Inc(TokenStr);
  677. If TokenStr[0]='>' then
  678. Result:=tsqlLE
  679. else if (TokenStr[0]='<') then
  680. Result:=tsqlGE
  681. else if (TokenStr[0]='=') then
  682. Result:=tsqlNE
  683. else
  684. Result:=tsqlUnknown;
  685. Inc(TokenStr);
  686. end;
  687. '|':
  688. begin
  689. Inc(TokenStr);
  690. If Tokenstr[0]='|' then
  691. begin
  692. Inc(TokenStr);
  693. Result := tsqlConcatenate
  694. end
  695. else
  696. Error(SBarExpected);
  697. end;
  698. '(':
  699. begin
  700. Inc(TokenStr);
  701. Result := tsqlBraceOpen;
  702. end;
  703. ')':
  704. begin
  705. Inc(TokenStr);
  706. Result := tsqlBraceClose;
  707. end;
  708. '[':
  709. begin
  710. Inc(TokenStr);
  711. Result := tsqlSquareBraceOpen;
  712. end;
  713. ']':
  714. begin
  715. Inc(TokenStr);
  716. Result := tsqlSquareBraceClose;
  717. end;
  718. '*':
  719. begin
  720. Inc(TokenStr);
  721. Result := tsqlMul;
  722. end;
  723. '+':
  724. begin
  725. Inc(TokenStr);
  726. Result := tsqlPlus;
  727. end;
  728. ',':
  729. begin
  730. Inc(TokenStr);
  731. Result := tsqlComma;
  732. end;
  733. '-':
  734. begin
  735. Inc(TokenStr);
  736. If (TokenStr[0]='-') then
  737. begin
  738. Inc(TokenStr);
  739. Result:=DoSingleLineComment
  740. end
  741. else if (TokenStr[0] in ['0'..'9']) then
  742. begin
  743. Result:=DoNumericLiteral;
  744. If (Result in [tsqlIntegerNumber,tsqlFloatNumber]) then
  745. FCurTokenString:='-'+FCurTokenString;
  746. end
  747. else
  748. Result := tsqlMinus;
  749. end;
  750. '.':
  751. begin
  752. Inc(TokenStr);
  753. Result := tsqlDot;
  754. end;
  755. ':':
  756. begin
  757. Inc(TokenStr);
  758. Result := tsqlColon;
  759. end;
  760. ';':
  761. begin
  762. Inc(TokenStr);
  763. Result := tsqlSemicolon;
  764. end;
  765. '<':
  766. begin
  767. Inc(TokenStr);
  768. if TokenStr[0] = '>' then
  769. begin
  770. Inc(TokenStr);
  771. Result := tsqlNE;
  772. end
  773. else if (TokenStr[0] = '=') then
  774. begin
  775. Inc(TokenStr);
  776. Result := tsqlLE;
  777. end
  778. else
  779. Result := tsqlLT;
  780. end;
  781. '=':
  782. begin
  783. Inc(TokenStr);
  784. Result := tsqleQ;
  785. end;
  786. '>':
  787. begin
  788. Inc(TokenStr);
  789. if TokenStr[0] = '=' then
  790. begin
  791. Inc(TokenStr);
  792. Result:=tsqlGE;
  793. end
  794. else
  795. Result := tsqlGT;
  796. end;
  797. 'a'..'z',
  798. 'A'..'Z':
  799. Result:=DoIdentifier;
  800. else
  801. // Symbol of some sort
  802. Result:=DoSymbolString;
  803. //Error(SErrUnknownToken,[TokenStr[0]]);
  804. end; // Case
  805. Until (Not (Result in [tsqlComment,tsqlWhitespace])) or
  806. ((Result=tsqlComment) and (soReturnComments in options)) or
  807. ((Result=tsqlWhiteSpace) and (soReturnWhiteSpace in Options));
  808. FCurToken:=Result;
  809. end;
  810. function TSQLScanner.IsEndOfLine: Boolean;
  811. begin
  812. Result:=(TokenStr=Nil) or (TokenStr[0] in [#0,#10,#13]);
  813. end;
  814. function TSQLScanner.GetCurColumn: Integer;
  815. begin
  816. Result := TokenStr - PChar(FCurLine);
  817. end;
  818. Procedure TSQLScanner.ClearKeywords(Sender : TObject);
  819. begin
  820. If Assigned(FKeywords) then
  821. FKeywords.Clear;
  822. end;
  823. function TSQLScanner.GetExcludeKeywords: TStrings;
  824. begin
  825. If FExclude=Nil then
  826. begin
  827. FExclude:=TStringList.Create;
  828. FExclude.Duplicates:=dupIgnore;
  829. FExclude.Sorted:=true;
  830. FExclude.OnChange:=@ClearKeywords;
  831. end;
  832. Result:=FExclude;
  833. end;
  834. { TStreamLineReader }
  835. constructor TStreamLineReader.Create(AStream: TStream);
  836. begin
  837. FStream:=AStream;
  838. FBufPos:=0;
  839. FBufLen:=0;
  840. end;
  841. function TStreamLineReader.IsEOF: Boolean;
  842. begin
  843. Result:=(FBufPos>=FBufLen);
  844. If Result then
  845. begin
  846. FillBuffer;
  847. Result:=(FBufLen=0);
  848. end;
  849. end;
  850. procedure TStreamLineReader.FillBuffer;
  851. begin
  852. FBufLen:=FStream.Read(Buffer,SizeOf(Buffer)-1);
  853. Buffer[FBufLen]:=0;
  854. FBufPos:=0;
  855. end;
  856. function TStreamLineReader.ReadLine: string;
  857. Var
  858. FPos,OLen,Len: Integer;
  859. PRun : PByte;
  860. begin
  861. FPos:=FBufPos;
  862. SetLength(Result,0);
  863. Repeat
  864. PRun:=@Buffer[FBufPos];
  865. While (FBufPos<FBufLen) and Not (PRun^ in [10,13]) do
  866. begin
  867. Inc(PRun);
  868. Inc(FBufPos);
  869. end;
  870. If (FBufPos=FBufLen) then
  871. begin
  872. Len:=FBufPos-FPos;
  873. If (Len>0) then
  874. begin
  875. Olen:=Length(Result);
  876. SetLength(Result,OLen+Len);
  877. Move(Buffer[FPos],Result[OLen+1],Len);
  878. end;
  879. FillBuffer;
  880. FPos:=FBufPos;
  881. end;
  882. until (FBufPos=FBufLen) or (PRun^ in [10,13]);
  883. Len:=FBufPos-FPos+1;
  884. If (Len>0) then
  885. begin
  886. Olen:=Length(Result);
  887. SetLength(Result,OLen+Len);
  888. Move(Buffer[FPos],Result[OLen+1],Len);
  889. end;
  890. If (PRun^ in [10,13]) and (FBufPos<FBufLen) then
  891. begin
  892. Inc(FBufPos);
  893. // Check #13#10
  894. If (PRun^=13) then
  895. begin
  896. If (FBufPos=FBufLen) then
  897. FillBuffer;
  898. If (FBufPos<FBufLen) and (Buffer[FBufpos]=10) then
  899. begin
  900. Inc(FBufPos);
  901. Result:=Result+#10;
  902. end;
  903. end;
  904. end;
  905. end;
  906. end.