| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998 |
- {
- This file is part of the Free Component Library
- Copyright (c) 2010-2014 by the Free Pascal development team
- SQL source lexical scanner
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- {$mode objfpc}
- {$h+}
- unit fpsqlscanner;
- interface
- uses SysUtils, Classes, Contnrs;
- resourcestring
- SErrInvalidCharacter = 'Invalid character ''%s''';
- SErrOpenString = 'string exceeds end of line';
- SErrIncludeFileNotFound = 'Could not find include file ''%s''';
- SErrIfXXXNestingLimitReached = 'Nesting of $IFxxx too deep';
- SErrInvalidPPElse = '$ELSE without matching $IFxxx';
- SErrInvalidPPEndif = '$ENDIF without matching $IFxxx';
- SInvalidHexadecimalNumber = 'Invalid hexadecimal number';
- SErrInvalidNonEqual = 'SyntaxError: != or !== expected';
- SBarExpected = '| character expected';
- type
- TSQLToken = (tsqlUnknown,
- // Specials
- tsqlEOF,tsqlWhiteSpace,
- tsqlString {string literal},
- tsqlIdentifier {a table etc name},
- tsqlSymbolString {a string containing symbols/punctuation marks; only rarely used - e.g. in SET TERM ^ ;},
- tsqlIntegerNumber,tsqlFloatNumber,tsqlComment,
- tsqlBraceOpen,tsqlBraceClose,tsqlSquareBraceOpen,tsqlSquareBraceClose,
- tsqlPlaceHolder {question mark},
- tsqlCOMMA,tsqlCOLON,tsqlDOT,tsqlSEMICOLON,
- tsqlGT,tsqlLT,tsqlPLUS,tsqlMINUS,tsqlMUL,tsqlDIV,tsqlConcatenate,
- tsqlEQ,tsqlGE,tsqlLE,tsqlNE,
- { Reserved words/keywords start here. They must be last }
- { Note: if adding before tsqlALL or after tsqlWHEN please update FirstKeyword/LastKeyword }
- tsqlALL, tsqlAND, tsqlANY, tsqlASC, tsqlASCENDING, tsqlAVG, tsqlALTER, tsqlAdd, tsqlActive, tsqlAction, tsqlAs,tsqlAt, tsqlAuto, tsqlAfter,tsqlAdmin,
- tsqlBETWEEN, tsqlBinary, tsqlBY, tsqlBLOB, tsqlBegin, tsqlBefore,
- tsqlCOLLATE, tsqlCONTAINING, tsqlCOUNT, tsqlCREATE, tsqlCOLUMN, tsqlCONSTRAINT, tsqlChar,tsqlCHARACTER, tsqlCHECK, tsqlComputed,tsqlCASCADE, tsqlCast, tsqlCommit,tsqlConnect,tsqlCache,tsqlConditional,tsqlCString,
- tsqlDESC, tsqlDESCENDING, tsqlDISTINCT, tsqlDEFAULT, tsqlDELETE, tsqlDO, tsqlDouble, tsqlDECLARE, tsqlDROP, tsqlDomain, tsqlDecimal, tsqlDate,tsqlDatabase,
- tsqlESCAPE, tsqlEXISTS, tsqlELSE, tsqlException, tsqlExternal, tsqlExecute, tsqlEnd,tsqlExit,tsqlEntrypoint,tsqlExtract,
- tsqlFROM, tsqlFULL, tsqlFOREIGN, tsqlFOR, tsqlFUNCTION, tsqlFLOAT, tsqlFile,tsqlFreeIt,
- tsqlGenerator, tsqlGROUP, tsqlGenID,tsqlGDSCODE,tsqlGrant,
- tsqlHAVING,
- tsqlIF, tsqlIN, tsqlINNER, tsqlINSERT, tsqlINT, tsqlINTEGER, tsqlINTO, tsqlIS, tsqlINDEX, tsqlInactive,
- tsqlJOIN,
- tsqlKEY,
- tsqlLEFT, tsqlLIKE, tsqlLength,
- tsqlMAX, tsqlMIN, tsqlMERGE, tsqlManual, tsqlModuleName,
- tsqlNOT, tsqlNULL, tsqlNUMERIC , tsqlNChar, tsqlNATIONAL,tsqlNO, tsqlNatural,
- tsqlOFF {not an FB reserved word; used in isql scripts}, tsqlON, tsqlOR, tsqlORDER, tsqlOUTER, tsqlOption,
- tsqlPrecision, tsqlPRIMARY, tsqlProcedure, tsqlPosition, tsqlPlan, tsqlPassword, tsqlPage,tsqlPages,tsqlPageSize,tsqlPostEvent,tsqlPrivileges,tsqlPublic,
- tsqlRIGHT, tsqlROLE, tsqlReferences, tsqlRollBack, tsqlRelease, tsqlretain, tsqlReturningValues,tsqlReturns, tsqlrevoke,
- tsqlSELECT, tsqlSET, tsqlSINGULAR, tsqlSOME, tsqlSTARTING, tsqlSUM, tsqlSKIP,tsqlSUBTYPE,tsqlSize,tsqlSegment, tsqlSORT, tsqlSnapShot,tsqlSchema,tsqlShadow,tsqlSuspend,tsqlSQLCode,tsqlSmallint,
- tSQLTABLE, tsqlText, tsqlTrigger, tsqlTime, tsqlTimeStamp, tsqlType, tsqlTo, tsqlTransaction, tsqlThen,
- tsqlUNION, tsqlUPDATE, tsqlUPPER, tsqlUNIQUE, tsqlUSER,
- tsqlValue, tsqlVALUES, tsqlVARIABLE, tsqlVIEW, tsqlVARCHAR,TSQLVARYING,
- tsqlWHERE, tsqlWITH, tsqlWHILE, tsqlWork, tsqlWhen
- );
- TSQLTokens = set of TSQLToken;
- const
- FirstKeyword = tsqlAll;
- LastKeyWord = tsqlWhen;
- sqlComparisons = [tsqleq,tsqlGE,tsqlLE,tsqlNE,tsqlGT,tsqlLT,tsqlIn,tsqlIS,
- tsqlbetween,tsqlLike,tsqlContaining,tsqlStarting,tsqlNOT];
- sqlInvertableComparisons = [tsqlLike,tsqlContaining,tsqlStarting,tsqlIN,tsqlIS, tsqlBetween];
- // Strings that represent tokens in TSQLToken
- TokenInfos: array[TSQLToken] of string = ('unknown',
- // Specials
- 'EOF','whitespace',
- 'String',
- 'identifier',
- 'symbol string',
- 'integer number','float number', 'comment',
- '(',')', '[',']',
- '?',',',':','.',';','>','<',
- '+','-','*','/','||',
- '=','>=','<=','<>',
- // Identifiers last:
- 'ALL', 'AND', 'ANY', 'ASC', 'ASCENDING', 'AVG', 'ALTER', 'ADD','ACTIVE','ACTION', 'AS', 'AT', 'AUTO', 'AFTER', 'ADMIN',
- 'BETWEEN', 'BINARY', 'BY', 'BLOB','BEGIN', 'BEFORE',
- 'COLLATE', 'CONTAINING', 'COUNT', 'CREATE', 'COLUMN', 'CONSTRAINT', 'CHAR','CHARACTER','CHECK', 'COMPUTED','CASCADE','CAST', 'COMMIT', 'CONNECT', 'CACHE','CONDITIONAL', 'CSTRING',
- 'DESC', 'DESCENDING', 'DISTINCT', 'DEFAULT', 'DELETE', 'DO', 'DOUBLE', 'DECLARE', 'DROP', 'DOMAIN', 'DECIMAL', 'DATE','DATABASE',
- 'ESCAPE', 'EXISTS', 'ELSE', 'EXCEPTION', 'EXTERNAL','EXECUTE', 'END','EXIT','ENTRY_POINT','EXTRACT',
- 'FROM', 'FULL','FOREIGN', 'FOR', 'FUNCTION', 'FLOAT','FILE', 'FREE_IT',
- 'GENERATOR', 'GROUP', 'GEN_ID','GDSCODE','GRANT',
- 'HAVING',
- 'IF', 'IN', 'INNER', 'INSERT', 'INT', 'INTEGER', 'INTO', 'IS', 'INDEX', 'INACTIVE',
- 'JOIN',
- 'KEY',
- 'LEFT', 'LIKE', 'LENGTH',
- 'MAX', 'MIN', 'MERGE', 'MANUAL', 'MODULE_NAME',
- 'NOT', 'NULL', 'NUMERIC','NCHAR','NATIONAL', 'NO', 'NATURAL',
- 'OFF', 'ON', 'OR', 'ORDER', 'OUTER', 'OPTION',
- 'PRECISION', 'PRIMARY', 'PROCEDURE','POSITION','PLAN', 'PASSWORD','PAGE','PAGES','PAGE_SIZE','POST_EVENT','PRIVILEGES','PUBLIC',
- 'RIGHT', 'ROLE', 'REFERENCES', 'ROLLBACK','RELEASE', 'RETAIN', 'RETURNING_VALUES', 'RETURNS','REVOKE',
- 'SELECT', 'SET', 'SINGULAR', 'SOME', 'STARTING', 'SUM', 'SKIP','SUB_TYPE', 'SIZE', 'SEGMENT', 'SORT', 'SNAPSHOT','SCHEMA','SHADOW','SUSPEND','SQLCODE','SMALLINT',
- 'TABLE', 'TEXT', 'TRIGGER', 'TIME', 'TIMESTAMP', 'TYPE', 'TO', 'TRANSACTION', 'THEN',
- 'UNION', 'UPDATE', 'UPPER', 'UNIQUE', 'USER',
- 'VALUE','VALUES','VARIABLE', 'VIEW','VARCHAR','VARYING',
- 'WHERE', 'WITH', 'WHILE','WORK','WHEN'
- );
- Type
- TLineReader = class
- public
- function IsEOF: Boolean; virtual; abstract;
- function ReadLine: string; virtual; abstract;
- end;
- { TStreamLineReader }
- TStreamLineReader = class(TLineReader)
- private
- FStream : TStream;
- Buffer : Array[0..1024] of Byte;
- FBufPos,
- FBufLen : Integer;
- procedure FillBuffer;
- public
- Constructor Create(AStream : TStream);
- function IsEOF: Boolean; override;
- function ReadLine: string; override;
- end;
- TFileLineReader = class(TLineReader)
- private
- FTextFile: Text;
- FileOpened: Boolean;
- public
- constructor Create(const AFilename: string);
- destructor Destroy; override;
- function IsEOF: Boolean; override;
- function ReadLine: string; override;
- end;
- ESQLScannerError = class(Exception);
- { TSQLScanner }
- TSQLScannerOption = (soReturnComments,
- soReturnWhiteSpace,
- soBackslashEscapes,
- soNoDoubleDelimIsChar,
- soDoubleQuoteStringLiteral, // Default: single quote is string literal
- soSingleQuoteIdentifier, // Default: double quote is identifier. Ignored if soDoubleQuoteStringLiteral is not specified
- soBackQuoteIdentifier // Default: double quote is identifier
- );
- TSQLScannerOptions = Set of TSQLScannerOption;
- TSQLScanner = class
- private
- FOptions: TSQLScannerOptions;
- FReturnComments: Boolean;
- FReturnWhiteSpace: Boolean;
- FSourceFile: TLineReader;
- FSourceFilename: string;
- FCurRow: Integer;
- FCurToken: TSQLToken;
- FCurTokenString: string;
- FCurLine: string;
- TokenStr: PChar;
- FSourceStream : TStream;
- FOwnSourceFile : Boolean;
- FKeyWords : TFPHashList;
- FExclude : TStringList;
- function CommentDiv: TSQLToken;
- // Used to parse out an identifier/name and store it in the list of identifiers
- function DoIdentifier : TSQLToken;
- // Used to parse out a string containing symbols
- function DoSymbolString : TSQLToken;
- function DoMultiLineComment: TSQLToken;
- function DoNumericLiteral: TSQLToken;
- function DoSingleLineComment: TSQLToken;
- function DoStringLiteral: TSQLToken;
- function DoWhiteSpace: TSQLToken;
- // Reads a new line into TokenStr and returns true
- // If no new lines, returns false
- function FetchLine: Boolean;
- function GetCurColumn: Integer;
- function GetExcludeKeywords: TStrings;
- function ReadUnicodeEscape: WideChar;
- procedure SetExcludeKeywords(const AValue: TStrings);
- procedure Setoptions(const AValue: TSQLScannerOptions);
- procedure ClearKeywords(Sender: TObject);
- protected
- Procedure BuildKeyWords; virtual;
- procedure Error(const Msg: string);overload;
- procedure Error(const Msg: string; Args: array of Const);overload;
- public
- constructor Create(ALineReader: TLineReader);
- constructor Create(AStream : TStream);
- destructor Destroy; override;
- procedure OpenFile(const AFilename: string);
- Function FetchToken: TSQLToken;
- Function IsEndOfLine : Boolean;
- Property Options : TSQLScannerOptions Read FOptions Write Setoptions;
- property SourceFile: TLineReader read FSourceFile;
- property CurFilename: string read FSourceFilename;
- property CurLine: string read FCurLine;
- property CurRow: Integer read FCurRow;
- property CurColumn: Integer read GetCurColumn;
- property CurToken: TSQLToken read FCurToken;
- property CurTokenString: string read FCurTokenString;
- Property ExcludeKeywords : TStrings Read GetExcludeKeywords Write SetExcludeKeywords;
- end;
- implementation
- Var
- // Keeps track of identifiers used
- IdentifierTokens : array[FirstKeyword..LastKeyWord] of TSQLToken;
- IdentifierTokensOK : Boolean;
- Resourcestring
- SErrUnknownToken = 'Unknown token: %s';
- Procedure BuildIdentifierTokens;
- Var
- T : TSQLToken;
- begin
- For T:=FirstKeyword to LastKeyWord do
- IdentifierTokens[T]:=T;
- end;
- constructor TFileLineReader.Create(const AFilename: string);
- begin
- inherited Create;
- Assign(FTextFile, AFilename);
- Reset(FTextFile);
- FileOpened := true;
- end;
- destructor TFileLineReader.Destroy;
- begin
- if FileOpened then
- Close(FTextFile);
- inherited Destroy;
- end;
- function TFileLineReader.IsEOF: Boolean;
- begin
- Result := EOF(FTextFile);
- end;
- function TFileLineReader.ReadLine: string;
- begin
- ReadLn(FTextFile, Result);
- end;
- constructor TSQLScanner.Create(ALineReader: TLineReader);
- begin
- inherited Create;
- FSourceFile := ALineReader;
- FKeywords:=TFPHashList.Create;
- end;
- constructor TSQLScanner.Create(AStream: TStream);
- begin
- FSourceStream:=ASTream;
- FOwnSourceFile:=True;
- Create(TStreamLineReader.Create(AStream));
- end;
- destructor TSQLScanner.Destroy;
- begin
- If FOwnSourceFile then
- FSourceFile.Free;
- FreeAndNil(FKeywords);
- inherited Destroy;
- end;
- procedure TSQLScanner.OpenFile(const AFilename: string);
- begin
- FSourceFile := TFileLineReader.Create(AFilename);
- FOwnSourceFile:=True;
- FSourceFilename := AFilename;
- end;
- procedure TSQLScanner.Error(const Msg: string);
- begin
- raise ESQLScannerError.Create(Msg);
- end;
- procedure TSQLScanner.Error(const Msg: string; Args: array of Const);
- begin
- raise ESQLScannerError.CreateFmt(Msg, Args);
- end;
- function TSQLScanner.FetchLine: Boolean;
- begin
- if FSourceFile.IsEOF then
- begin
- FCurLine := '';
- TokenStr := nil;
- Result := false;
- end else
- begin
- FCurLine := FSourceFile.ReadLine;
- TokenStr := PChar(CurLine);
- Result := true;
- Inc(FCurRow);
- end;
- end;
- function TSQLScanner.DoWhiteSpace : TSQLToken;
- begin
- Result:=tsqlWhitespace;
- repeat
- Inc(TokenStr);
- if TokenStr[0] = #0 then
- if not FetchLine then
- begin
- FCurToken := Result;
- exit;
- end;
- until not (TokenStr[0] in [#9, ' ']);
- end;
- function TSQLScanner.DoSingleLineComment : TSQLToken;
- Var
- TokenStart : PChar;
- Len : Integer;
- begin
- Inc(TokenStr);
- TokenStart := TokenStr;
- while TokenStr[0] <> #0 do
- Inc(TokenStr);
- Len:=TokenStr-TokenStart;
- SetLength(FCurTokenString, Len);
- if (Len>0) then
- Move(TokenStart^,FCurTokenString[1],Len);
- Result := tsqlComment;
- end;
- function TSQLScanner.DoMultiLineComment : TSQLToken;
- Var
- TokenStart : PChar;
- Len,OLen : Integer;
- PrevToken : Char;
- begin
- Inc(TokenStr);
- TokenStart := TokenStr;
- FCurTokenString := '';
- OLen:= 0;
- PrevToken:=#0;
- while Not ((TokenStr[0]='/') and (PrevToken='*')) do
- begin
- if (TokenStr[0]=#0) then
- begin
- Len:=TokenStr-TokenStart+1;
- SetLength(FCurTokenString,OLen+Len);
- if Len>1 then
- Move(TokenStart^,FCurTokenString[OLen+1],Len-1);
- Inc(OLen,Len);
- FCurTokenString[OLen]:=#10;
- if not FetchLine then
- begin
- Result := tsqlEOF;
- FCurToken := Result;
- exit;
- end;
- TokenStart := TokenStr;
- PrevToken:=#0;
- end
- else
- begin
- PrevToken:=TokenStr[0];
- Inc(TokenStr);
- end;
- end;
- Len:=TokenStr-TokenStart-1; // -1 for *
- SetLength(FCurTokenString, Olen+Len);
- if (Len>0) then
- begin
- Move(TokenStart^, FCurTokenString[Olen + 1], Len);
- end;
- If TokenStr[0]<>#0 then
- Inc(TokenStr);
- Result := tsqlComment;
- end;
- function TSQLScanner.CommentDiv : TSQLToken;
- begin
- FCurTokenString := '';
- Inc(TokenStr);
- if (TokenStr[0]='*') then
- Result:=DoMultiLineComment
- else
- Result:=tsqlDiv;
- end;
- Function TSQLScanner.ReadUnicodeEscape : WideChar;
- Var
- S : String;
- I : Integer;
- begin
- S:='0000';
- For I:=1 to 4 do
- begin
- Inc(TokenStr);
- Case TokenStr[0] of
- '0'..'9','A'..'F','a'..'f' :
- S[i]:=Upcase(TokenStr[0]);
- else
- Error(SErrInvalidCharacter, [TokenStr[0]]);
- end;
- end;
- // Takes care of conversion... This needs improvement !!
- Result:=WideChar(StrToInt('$'+S));
- end;
- procedure TSQLScanner.SetExcludeKeywords(const AValue: TStrings);
- begin
- With ExcludeKeywords do
- begin
- Clear;
- AddStrings(AValue);
- end;
- end;
- procedure TSQLScanner.Setoptions(const AValue: TSQLScannerOptions);
- Const
- F = [soDoubleQuoteStringLiteral,soSingleQuoteIdentifier];
- begin
- FOptions:=AValue;
- if ((Foptions * F) = [soSingleQuoteIdentifier]) then
- Exclude(FOptions,soSingleQuoteIdentifier);
- end;
- procedure TSQLScanner.BuildKeyWords;
- Var
- I : TSQLToken;
- begin
- If Not IdentifierTokensOK then
- BuildIdentifierTokens;
- If FKeywords.Count>0 then
- FKeywords.Clear;
- for I:=FirstKeyword to LastKeyword do
- if (not Assigned(FExclude)) or (FExclude.IndexOf(TokenInfos[I])=-1) then
- FKeywords.Add(TokenInfos[I],@IdentifierTokens[i]);
- end;
- function TSQLScanner.DoStringLiteral: TSQLToken;
- Var
- Delim : Char;
- TokenStart : PChar;
- Len,OLen : Integer;
- S : String;
- Procedure AppendBufToTokenString(DoNextToken : Boolean);
- begin
- SetLength(FCurTokenString, OLen + Len+Length(S));
- if Len > 0 then
- Move(TokenStart^, FCurTokenString[OLen + 1], Len);
- If Length(S)>0 then
- Move(S[1],FCurTokenString[OLen + Len+1],Length(S));
- Inc(OLen, Len+Length(S));
- If DoNextToken then
- Inc(TokenStr);
- TokenStart := TokenStr+1;
- end;
- Function CheckTokenBuf : Boolean;
- begin
- Result:=(TokenStr[0]<>#0);
- If Not Result then
- begin
- S:='';
- Len:=TokenStr-TokenStart;
- AppendBufToTokenString(False);
- Result:=FetchLine;
- TokenStart:=TokenStr;
- end;
- end;
- begin
- Delim:=TokenStr[0];
- Inc(TokenStr);
- TokenStart := TokenStr;
- OLen := 0;
- FCurTokenString := '';
- while not (TokenStr[0]=#0) do
- begin
- If (TokenStr[0]=Delim) then
- begin
- if (not (soNoDoubleDelimIsChar in options)) and (TokenStr[1]=Delim) then
- begin
- S:=Delim;
- Len := TokenStr - TokenStart;
- AppendBufToTokenString(True);
- end
- else
- Break;
- end
- else if (TokenStr[0]='\') and (soBackSlashEscapes in Options) then
- begin
- // Save length
- Len := TokenStr - TokenStart;
- Inc(TokenStr);
- if not CheckTokenBuf then
- Error(SErrOpenString);
- // Read escaped token
- Case TokenStr[0] of
- '"' : S:='"';
- '''' : S:='''';
- 't' : S:=#9;
- 'b' : S:=#8;
- 'n' : S:=#10;
- 'r' : S:=#13;
- 'f' : S:=#12;
- '\' : S:='\';
- '/' : S:='/';
- 'u' : begin
- S:=ReadUniCodeEscape;
- end;
- else
- Error(SErrInvalidCharacter, [TokenStr[0]]);
- end;
- AppendBufToTokenString(False);
- end;
- Inc(TokenStr);
- if not CheckTokenBuf then
- Error(SErrOpenString);
- end;
- if Not CheckTokenBuf then
- Error(SErrOpenString);
- S:='';
- Len := TokenStr - TokenStart;
- AppendBufToTokenString(True);
- Result := tsqlString;
- end;
- function TSQLScanner.DoNumericLiteral :TSQLToken;
- Var
- TokenStart : PChar;
- Len : Integer;
- isFloat : boolean;
- begin
- TokenStart := TokenStr;
- IsFloat:=False;
- while true do
- begin
- Inc(TokenStr);
- case TokenStr[0] of
- 'x':
- If (TokenStart[0]='0') and ((TokenStr-TokenStart)=1) then
- begin
- Inc(TokenStr);
- while Upcase(TokenStr[0]) in ['0'..'9','A'..'F'] do
- Inc(TokenStr);
- end
- else
- Error(SInvalidHexadecimalNumber);
- '.':
- begin
- isfloat:=true;
- if TokenStr[1] in ['0'..'9', 'e', 'E'] then
- begin
- Inc(TokenStr);
- repeat
- Inc(TokenStr);
- until not (TokenStr[0] in ['0'..'9', 'e', 'E','-','+']);
- end;
- break;
- end;
- '0'..'9': ;
- 'e', 'E':
- begin
- isFloat:=true;
- Inc(TokenStr);
- if TokenStr[0] in ['-','+'] then
- Inc(TokenStr);
- while TokenStr[0] in ['0'..'9'] do
- Inc(TokenStr);
- break;
- end;
- else
- break;
- end;
- end;
- Len:=TokenStr-TokenStart;
- Setlength(FCurTokenString, Len);
- if (Len>0) then
- Move(TokenStart^,FCurTokenString[1],Len);
- If IsFloat then
- Result := tsqlFloatNumber
- else
- Result:=tsqlIntegerNumber;
- end;
- function TSQLScanner.DoIdentifier : TSQLToken;
- Var
- TokenStart:PChar;
- Len : Integer;
- {I : TSQLToken;}
- S : ShortString;
- P : ^TSQLToken;
- begin
- Result:=tsqlIdentifier;
- TokenStart := TokenStr;
- repeat
- Inc(TokenStr);
- If (TokenStr[0]='\') and (TokenStr[1]='u') then
- until not (TokenStr[0] in ['A'..'Z', 'a'..'z', '0'..'9', '_','$']);
- Len:=(TokenStr-TokenStart);
- SetLength(FCurTokenString,Len);
- if Len > 0 then
- Move(TokenStart^,FCurTokenString[1],Len);
- S:=UpperCase(FCurTokenString);
- // Check if this is a keyword or identifier
- // to do: Optimize this!
- If FKeyWords.Count=0 then
- BuildKeyWords;
- P:=FKeyWords.Find(S);
- If (P<>Nil) then
- Result:=P^; //keyword found
- { I:=FirstKeyword;
- While (Result=tsqlIdentifier) and (I<=Lastkeyword) do
- begin
- if (S=TokenInfos[i]) then
- begin
- Result := i;
- FCurToken := Result;
- exit;
- end;
- I:=Succ(I);
- end;}
- end;
- function TSQLScanner.DoSymbolString : TSQLToken;
- Var
- Len : Integer;
- P : ^TSQLToken;
- TokenStart : PChar;
- begin
- Result:=tsqlUnknown;
- // Get "word" finalized by end of string, space/tab/line ending.
- TokenStart:=TokenStr;
- repeat
- Inc(TokenStr);
- until (TokenStr[0] in [#0, #9, #10, #13, ' ']);
- Len:=(TokenStr-TokenStart);
- if Len > 0 then
- begin
- result:=tsqlSymbolString;
- SetLength(FCurTokenString,Len);
- Move(TokenStart^,FCurTokenString[1],Len);
- // Check if this is a keyword or identifier/literal
- // Probably not (due to naming rules) but it doesn't hurt
- If FKeyWords.Count=0 then
- BuildKeyWords;
- P:=FKeyWords.Find(FCurTokenString); //case-sensitive search
- If (P<>Nil) then
- Result:=P^; //keyword found, just in case
- end;
- end;
- function TSQLScanner.FetchToken: TSQLToken;
- begin
- Repeat
- if TokenStr = nil then
- if not FetchLine then
- begin
- Result := tsqlEOF;
- FCurToken := Result;
- exit;
- end;
- FCurTokenString := '';
- case TokenStr[0] of
- #0: // Empty line
- begin
- FetchLine;
- Result := tsqlWhitespace;
- end;
- '/' :
- Result:=CommentDiv;
- #9, ' ',#10,#13:
- Result := DoWhiteSpace;
- '''':
- begin
- Result:=DoStringLiteral;
- if (soSingleQuoteIdentifier in Options) then
- result:=tsqlIdentifier;
- end;
- '"':
- begin
- Result:=DoStringLiteral;
- If (soDoubleQuoteStringLiteral in options) then
- Result:=tsqlString
- else
- Result:=tsqlIdentifier;
- end;
- '`':
- begin
- Result:=DoStringLiteral;
- If (soBackQuoteIdentifier in options) then
- Result:=tsqlIdentifier
- else
- Error(SErrUnknownToken,['`']);
- end;
- '0'..'9':
- Result:=DoNumericLiteral;
- '?':
- begin
- Inc(TokenStr);
- Result:=tsqlPlaceHolder;
- end;
- '!':
- begin
- Inc(TokenStr);
- If TokenStr[0]='>' then
- Result:=tsqlLE
- else if (TokenStr[0]='<') then
- Result:=tsqlGE
- else if (TokenStr[0]='=') then
- Result:=tsqlNE
- else
- Result:=tsqlUnknown;
- Inc(TokenStr);
- end;
- '|':
- begin
- Inc(TokenStr);
- If Tokenstr[0]='|' then
- begin
- Inc(TokenStr);
- Result := tsqlConcatenate
- end
- else
- Error(SBarExpected);
- end;
- '(':
- begin
- Inc(TokenStr);
- Result := tsqlBraceOpen;
- end;
- ')':
- begin
- Inc(TokenStr);
- Result := tsqlBraceClose;
- end;
- '[':
- begin
- Inc(TokenStr);
- Result := tsqlSquareBraceOpen;
- end;
- ']':
- begin
- Inc(TokenStr);
- Result := tsqlSquareBraceClose;
- end;
- '*':
- begin
- Inc(TokenStr);
- Result := tsqlMul;
- end;
- '+':
- begin
- Inc(TokenStr);
- Result := tsqlPlus;
- end;
- ',':
- begin
- Inc(TokenStr);
- Result := tsqlComma;
- end;
- '-':
- begin
- Inc(TokenStr);
- If (TokenStr[0]='-') then
- begin
- Inc(TokenStr);
- Result:=DoSingleLineComment
- end
- else if (TokenStr[0] in ['0'..'9']) then
- begin
- Result:=DoNumericLiteral;
- If (Result in [tsqlIntegerNumber,tsqlFloatNumber]) then
- FCurTokenString:='-'+FCurTokenString;
- end
- else
- Result := tsqlMinus;
- end;
- '.':
- begin
- Inc(TokenStr);
- Result := tsqlDot;
- end;
- ':':
- begin
- Inc(TokenStr);
- Result := tsqlColon;
- end;
- ';':
- begin
- Inc(TokenStr);
- Result := tsqlSemicolon;
- end;
- '<':
- begin
- Inc(TokenStr);
- if TokenStr[0] = '>' then
- begin
- Inc(TokenStr);
- Result := tsqlNE;
- end
- else if (TokenStr[0] = '=') then
- begin
- Inc(TokenStr);
- Result := tsqlLE;
- end
- else
- Result := tsqlLT;
- end;
- '=':
- begin
- Inc(TokenStr);
- Result := tsqleQ;
- end;
- '>':
- begin
- Inc(TokenStr);
- if TokenStr[0] = '=' then
- begin
- Inc(TokenStr);
- Result:=tsqlGE;
- end
- else
- Result := tsqlGT;
- end;
- 'a'..'z',
- 'A'..'Z':
- Result:=DoIdentifier;
- else
- // Symbol of some sort
- Result:=DoSymbolString;
- //Error(SErrUnknownToken,[TokenStr[0]]);
- end; // Case
- Until (Not (Result in [tsqlComment,tsqlWhitespace])) or
- ((Result=tsqlComment) and (soReturnComments in options)) or
- ((Result=tsqlWhiteSpace) and (soReturnWhiteSpace in Options));
- FCurToken:=Result;
- end;
- function TSQLScanner.IsEndOfLine: Boolean;
- begin
- Result:=(TokenStr=Nil) or (TokenStr[0] in [#0,#10,#13]);
- end;
- function TSQLScanner.GetCurColumn: Integer;
- begin
- Result := TokenStr - PChar(FCurLine);
- end;
- Procedure TSQLScanner.ClearKeywords(Sender : TObject);
- begin
- If Assigned(FKeywords) then
- FKeywords.Clear;
- end;
- function TSQLScanner.GetExcludeKeywords: TStrings;
- begin
- If FExclude=Nil then
- begin
- FExclude:=TStringList.Create;
- FExclude.Duplicates:=dupIgnore;
- FExclude.Sorted:=true;
- FExclude.OnChange:=@ClearKeywords;
- end;
- Result:=FExclude;
- end;
- { TStreamLineReader }
- constructor TStreamLineReader.Create(AStream: TStream);
- begin
- FStream:=AStream;
- FBufPos:=0;
- FBufLen:=0;
- end;
- function TStreamLineReader.IsEOF: Boolean;
- begin
- Result:=(FBufPos>=FBufLen);
- If Result then
- begin
- FillBuffer;
- Result:=(FBufLen=0);
- end;
- end;
- procedure TStreamLineReader.FillBuffer;
- begin
- FBufLen:=FStream.Read(Buffer,SizeOf(Buffer)-1);
- Buffer[FBufLen]:=0;
- FBufPos:=0;
- end;
- function TStreamLineReader.ReadLine: string;
- Var
- FPos,OLen,Len: Integer;
- PRun : PByte;
- begin
- FPos:=FBufPos;
- SetLength(Result,0);
- Repeat
- PRun:=@Buffer[FBufPos];
- While (FBufPos<FBufLen) and Not (PRun^ in [10,13]) do
- begin
- Inc(PRun);
- Inc(FBufPos);
- end;
- If (FBufPos=FBufLen) then
- begin
- Len:=FBufPos-FPos;
- If (Len>0) then
- begin
- Olen:=Length(Result);
- SetLength(Result,OLen+Len);
- Move(Buffer[FPos],Result[OLen+1],Len);
- end;
- FillBuffer;
- FPos:=FBufPos;
- end;
- until (FBufPos=FBufLen) or (PRun^ in [10,13]);
- Len:=FBufPos-FPos+1;
- If (Len>0) then
- begin
- Olen:=Length(Result);
- SetLength(Result,OLen+Len);
- Move(Buffer[FPos],Result[OLen+1],Len);
- end;
- If (PRun^ in [10,13]) and (FBufPos<FBufLen) then
- begin
- Inc(FBufPos);
- // Check #13#10
- If (PRun^=13) then
- begin
- If (FBufPos=FBufLen) then
- FillBuffer;
- If (FBufPos<FBufLen) and (Buffer[FBufpos]=10) then
- begin
- Inc(FBufPos);
- Result:=Result+#10;
- end;
- end;
- end;
- end;
- end.
|