mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-14 05:19:30 +02:00
1050 lines
23 KiB
ObjectPascal
1050 lines
23 KiB
ObjectPascal
{
|
|
$Id$
|
|
This file is part of the Free Component Library
|
|
|
|
Pascal source lexical scanner
|
|
Copyright (c) 2003 by
|
|
Areca Systems GmbH / Sebastian Guenther, sg@freepascal.org
|
|
|
|
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.
|
|
|
|
**********************************************************************}
|
|
|
|
|
|
unit PScanner;
|
|
|
|
interface
|
|
|
|
uses SysUtils, Classes;
|
|
|
|
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';
|
|
|
|
type
|
|
|
|
TToken = (
|
|
tkEOF,
|
|
tkWhitespace,
|
|
tkComment,
|
|
tkIdentifier,
|
|
tkString,
|
|
tkNumber,
|
|
tkChar,
|
|
// Simple (one-character) tokens
|
|
tkBraceOpen, // '('
|
|
tkBraceClose, // ')'
|
|
tkMul, // '*'
|
|
tkPlus, // '+'
|
|
tkComma, // ','
|
|
tkMinus, // '-'
|
|
tkDot, // '.'
|
|
tkDivision, // '/'
|
|
tkColon, // ':'
|
|
tkSemicolon, // ';'
|
|
tkLessThan, // '<'
|
|
tkEqual, // '='
|
|
tkGreaterThan, // '>'
|
|
tkAt, // '@'
|
|
tkSquaredBraceOpen, // '['
|
|
tkSquaredBraceClose,// ']'
|
|
tkCaret, // '^'
|
|
// Two-character tokens
|
|
tkDotDot, // '..'
|
|
tkAssign, // ':='
|
|
tkNotEqual, // '<>'
|
|
// Reserved words
|
|
tkabsolute,
|
|
tkand,
|
|
tkarray,
|
|
tkas,
|
|
tkasm,
|
|
tkbegin,
|
|
tkbreak,
|
|
tkcase,
|
|
tkclass,
|
|
tkconst,
|
|
tkconstructor,
|
|
tkcontinue,
|
|
tkdestructor,
|
|
tkdispose,
|
|
tkdiv,
|
|
tkdo,
|
|
tkdownto,
|
|
tkelse,
|
|
tkend,
|
|
tkexcept,
|
|
tkexit,
|
|
tkexports,
|
|
tkfalse,
|
|
tkfinalization,
|
|
tkfinally,
|
|
tkfor,
|
|
tkfunction,
|
|
tkgoto,
|
|
tkif,
|
|
tkimplementation,
|
|
tkin,
|
|
tkinherited,
|
|
tkinitialization,
|
|
tkinline,
|
|
tkinterface,
|
|
tkis,
|
|
tklabel,
|
|
tklibrary,
|
|
tkmod,
|
|
tknew,
|
|
tknil,
|
|
tknot,
|
|
tkobject,
|
|
tkof,
|
|
tkon,
|
|
tkoperator,
|
|
tkor,
|
|
tkpacked,
|
|
tkprocedure,
|
|
tkprogram,
|
|
tkproperty,
|
|
tkraise,
|
|
tkrecord,
|
|
tkrepeat,
|
|
tkResourceString,
|
|
tkself,
|
|
tkset,
|
|
tkshl,
|
|
tkshr,
|
|
// tkstring,
|
|
tkthen,
|
|
tkto,
|
|
tktrue,
|
|
tktry,
|
|
tktype,
|
|
tkunit,
|
|
tkuntil,
|
|
tkuses,
|
|
tkvar,
|
|
tkwhile,
|
|
tkwith,
|
|
tkxor);
|
|
|
|
TLineReader = class
|
|
public
|
|
function IsEOF: Boolean; virtual; abstract;
|
|
function ReadLine: String; virtual; abstract;
|
|
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;
|
|
|
|
TFileResolver = class
|
|
private
|
|
FIncludePaths: TStringList;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure AddIncludePath(const APath: String);
|
|
function FindSourceFile(const AName: String): TLineReader;
|
|
function FindIncludeFile(const AName: String): TLineReader;
|
|
end;
|
|
|
|
EScannerError = class(Exception);
|
|
EFileNotFoundError = class(Exception);
|
|
|
|
TPascalScannerPPSkipMode = (ppSkipNone, ppSkipIfBranch, ppSkipElseBranch,
|
|
ppSkipAll);
|
|
|
|
TPascalScanner = class
|
|
private
|
|
FFileResolver: TFileResolver;
|
|
FCurSourceFile: TLineReader;
|
|
FCurFilename: String;
|
|
FCurRow: Integer;
|
|
FCurToken: TToken;
|
|
FCurTokenString: String;
|
|
FCurLine: String;
|
|
FDefines: TStrings;
|
|
TokenStr: PChar;
|
|
FIncludeStack: TList;
|
|
|
|
// Preprocessor $IFxxx skipping data
|
|
PPSkipMode: TPascalScannerPPSkipMode;
|
|
PPIsSkipping: Boolean;
|
|
PPSkipStackIndex: Integer;
|
|
PPSkipModeStack: array[0..255] of TPascalScannerPPSkipMode;
|
|
PPIsSkippingStack: array[0..255] of Boolean;
|
|
|
|
function GetCurColumn: Integer;
|
|
protected
|
|
procedure Error(const Msg: String);
|
|
procedure Error(const Msg: String; Args: array of Const);
|
|
function DoFetchToken: TToken;
|
|
public
|
|
constructor Create(AFileResolver: TFileResolver);
|
|
destructor Destroy; override;
|
|
procedure OpenFile(const AFilename: String);
|
|
function FetchToken: TToken;
|
|
|
|
property FileResolver: TFileResolver read FFileResolver;
|
|
property CurSourceFile: TLineReader read FCurSourceFile;
|
|
property CurFilename: String read FCurFilename;
|
|
|
|
property CurLine: String read FCurLine;
|
|
property CurRow: Integer read FCurRow;
|
|
property CurColumn: Integer read GetCurColumn;
|
|
|
|
property CurToken: TToken read FCurToken;
|
|
property CurTokenString: String read FCurTokenString;
|
|
|
|
property Defines: TStrings read FDefines;
|
|
end;
|
|
|
|
const
|
|
TokenInfos: array[TToken] of String = (
|
|
'EOF',
|
|
'Whitespace',
|
|
'Comment',
|
|
'Identifier',
|
|
'String',
|
|
'Number',
|
|
'Character',
|
|
'(',
|
|
')',
|
|
'*',
|
|
'+',
|
|
',',
|
|
'-',
|
|
'.',
|
|
'/',
|
|
':',
|
|
';',
|
|
'<',
|
|
'=',
|
|
'>',
|
|
'@',
|
|
'[',
|
|
']',
|
|
'^',
|
|
'..',
|
|
':=',
|
|
'<>',
|
|
// Reserved words
|
|
'absolute',
|
|
'and',
|
|
'array',
|
|
'as',
|
|
'asm',
|
|
'begin',
|
|
'break',
|
|
'case',
|
|
'class',
|
|
'const',
|
|
'constructor',
|
|
'continue',
|
|
'destructor',
|
|
'dispose',
|
|
'div',
|
|
'do',
|
|
'downto',
|
|
'else',
|
|
'end',
|
|
'except',
|
|
'exit',
|
|
'exports',
|
|
'false',
|
|
'finalization',
|
|
'finally',
|
|
'for',
|
|
'function',
|
|
'goto',
|
|
'if',
|
|
'implementation',
|
|
'in',
|
|
'inherited',
|
|
'initialization',
|
|
'inline',
|
|
'interface',
|
|
'is',
|
|
'label',
|
|
'library',
|
|
'mod',
|
|
'new',
|
|
'nil',
|
|
'not',
|
|
'object',
|
|
'of',
|
|
'on',
|
|
'operator',
|
|
'or',
|
|
'packed',
|
|
'procedure',
|
|
'program',
|
|
'property',
|
|
'raise',
|
|
'record',
|
|
'repeat',
|
|
'resourcestring',
|
|
'self',
|
|
'set',
|
|
'shl',
|
|
'shr',
|
|
// 'string',
|
|
'then',
|
|
'to',
|
|
'true',
|
|
'try',
|
|
'type',
|
|
'unit',
|
|
'until',
|
|
'uses',
|
|
'var',
|
|
'while',
|
|
'with',
|
|
'xor'
|
|
);
|
|
|
|
|
|
implementation
|
|
|
|
type
|
|
TIncludeStackItem = class
|
|
SourceFile: TLineReader;
|
|
Filename: String;
|
|
Token: TToken;
|
|
TokenString: String;
|
|
Line: String;
|
|
Row: Integer;
|
|
TokenStr: PChar;
|
|
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 TFileResolver.Create;
|
|
begin
|
|
inherited Create;
|
|
FIncludePaths := TStringList.Create;
|
|
end;
|
|
|
|
destructor TFileResolver.Destroy;
|
|
begin
|
|
FIncludePaths.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TFileResolver.AddIncludePath(const APath: String);
|
|
begin
|
|
FIncludePaths.Add(IncludeTrailingPathDelimiter(APath));
|
|
end;
|
|
|
|
function TFileResolver.FindSourceFile(const AName: String): TLineReader;
|
|
begin
|
|
if not FileExists(AName) then
|
|
Raise EFileNotFoundError.create(Aname)
|
|
else
|
|
try
|
|
Result := TFileLineReader.Create(AName);
|
|
except
|
|
Result := nil;
|
|
end;
|
|
end;
|
|
|
|
function TFileResolver.FindIncludeFile(const AName: String): TLineReader;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := nil;
|
|
try
|
|
Result := TFileLineReader.Create(AName);
|
|
except
|
|
for i := 0 to FIncludePaths.Count - 1 do
|
|
try
|
|
Result := TFileLineReader.Create(FIncludePaths[i] + AName);
|
|
break;
|
|
except
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
constructor TPascalScanner.Create(AFileResolver: TFileResolver);
|
|
begin
|
|
inherited Create;
|
|
FFileResolver := AFileResolver;
|
|
FIncludeStack := TList.Create;
|
|
FDefines := TStringList.Create;
|
|
end;
|
|
|
|
destructor TPascalScanner.Destroy;
|
|
begin
|
|
FDefines.Free;
|
|
// Dont' free the first element, because it is CurSourceFile
|
|
while FIncludeStack.Count > 1 do
|
|
begin
|
|
TFileResolver(FIncludeStack[1]).Free;
|
|
FIncludeStack.Delete(1);
|
|
end;
|
|
FIncludeStack.Free;
|
|
|
|
CurSourceFile.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TPascalScanner.OpenFile(const AFilename: String);
|
|
begin
|
|
FCurSourceFile := FileResolver.FindSourceFile(AFilename);
|
|
FCurFilename := AFilename;
|
|
end;
|
|
|
|
function TPascalScanner.FetchToken: TToken;
|
|
var
|
|
IncludeStackItem: TIncludeStackItem;
|
|
begin
|
|
while True do
|
|
begin
|
|
Result := DoFetchToken;
|
|
if FCurToken = tkEOF then
|
|
if FIncludeStack.Count > 0 then
|
|
begin
|
|
CurSourceFile.Free;
|
|
IncludeStackItem :=
|
|
TIncludeStackItem(FIncludeStack[FIncludeStack.Count - 1]);
|
|
FIncludeStack.Delete(FIncludeStack.Count - 1);
|
|
FCurSourceFile := IncludeStackItem.SourceFile;
|
|
FCurFilename := IncludeStackItem.Filename;
|
|
FCurToken := IncludeStackItem.Token;
|
|
FCurTokenString := IncludeStackItem.TokenString;
|
|
FCurLine := IncludeStackItem.Line;
|
|
FCurRow := IncludeStackItem.Row;
|
|
TokenStr := IncludeStackItem.TokenStr;
|
|
IncludeStackItem.Free;
|
|
Result := FCurToken;
|
|
end else
|
|
break
|
|
else
|
|
if not PPIsSkipping then
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
procedure TPascalScanner.Error(const Msg: String);
|
|
begin
|
|
raise EScannerError.Create(Msg);
|
|
end;
|
|
|
|
procedure TPascalScanner.Error(const Msg: String; Args: array of Const);
|
|
begin
|
|
raise EScannerError.CreateFmt(Msg, Args);
|
|
end;
|
|
|
|
function TPascalScanner.DoFetchToken: TToken;
|
|
|
|
function FetchLine: Boolean;
|
|
begin
|
|
if CurSourceFile.IsEOF then
|
|
begin
|
|
FCurLine := '';
|
|
TokenStr := nil;
|
|
Result := False;
|
|
end else
|
|
begin
|
|
FCurLine := CurSourceFile.ReadLine;
|
|
TokenStr := PChar(CurLine);
|
|
Result := True;
|
|
Inc(FCurRow);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
TokenStart, CurPos: PChar;
|
|
i: TToken;
|
|
OldLength, SectionLength, NestingLevel, Index: Integer;
|
|
Directive, Param: String;
|
|
IncludeStackItem: TIncludeStackItem;
|
|
begin
|
|
if TokenStr = nil then
|
|
if not FetchLine then
|
|
begin
|
|
Result := tkEOF;
|
|
FCurToken := Result;
|
|
exit;
|
|
end;
|
|
|
|
FCurTokenString := '';
|
|
|
|
case TokenStr[0] of
|
|
#0: // Empty line
|
|
begin
|
|
FetchLine;
|
|
Result := tkWhitespace;
|
|
end;
|
|
#9, ' ':
|
|
begin
|
|
Result := tkWhitespace;
|
|
repeat
|
|
Inc(TokenStr);
|
|
if TokenStr[0] = #0 then
|
|
if not FetchLine then
|
|
begin
|
|
FCurToken := Result;
|
|
exit;
|
|
end;
|
|
until not (TokenStr[0] in [#9, ' ']);
|
|
end;
|
|
'#':
|
|
begin
|
|
TokenStart := TokenStr;
|
|
Inc(TokenStr);
|
|
if TokenStr[0] = '$' then
|
|
begin
|
|
Inc(TokenStr);
|
|
repeat
|
|
Inc(TokenStr);
|
|
until not (TokenStr[0] in ['0'..'9', 'A'..'F', 'a'..'F']);
|
|
end else
|
|
repeat
|
|
Inc(TokenStr);
|
|
until not (TokenStr[0] in ['0'..'9']);
|
|
|
|
SectionLength := TokenStr - TokenStart;
|
|
SetLength(FCurTokenString, SectionLength);
|
|
if SectionLength > 0 then
|
|
Move(TokenStart^, FCurTokenString[1], SectionLength);
|
|
Result := tkChar;
|
|
end;
|
|
'$':
|
|
begin
|
|
TokenStart := TokenStr;
|
|
repeat
|
|
Inc(TokenStr);
|
|
until not (TokenStr[0] in ['0'..'9', 'A'..'F', 'a'..'F']);
|
|
SectionLength := TokenStr - TokenStart;
|
|
SetLength(FCurTokenString, SectionLength);
|
|
if SectionLength > 0 then
|
|
Move(TokenStart^, FCurTokenString[1], SectionLength);
|
|
Result := tkNumber;
|
|
end;
|
|
'%':
|
|
begin
|
|
TokenStart := TokenStr;
|
|
repeat
|
|
Inc(TokenStr);
|
|
until not (TokenStr[0] in ['0','1']);
|
|
SectionLength := TokenStr - TokenStart;
|
|
SetLength(FCurTokenString, SectionLength);
|
|
if SectionLength > 0 then
|
|
Move(TokenStart^, FCurTokenString[1], SectionLength);
|
|
Result := tkNumber;
|
|
end;
|
|
'''':
|
|
begin
|
|
Inc(TokenStr);
|
|
TokenStart := TokenStr;
|
|
OldLength := 0;
|
|
FCurTokenString := '';
|
|
|
|
while True do
|
|
begin
|
|
if TokenStr[0] = '''' then
|
|
if TokenStr[1] = '''' then
|
|
begin
|
|
SectionLength := TokenStr - TokenStart + 1;
|
|
SetLength(FCurTokenString, OldLength + SectionLength);
|
|
if SectionLength > 1 then
|
|
Move(TokenStart^, FCurTokenString[OldLength + 1],
|
|
SectionLength);
|
|
Inc(OldLength, SectionLength);
|
|
Inc(TokenStr);
|
|
TokenStart := TokenStr;
|
|
end else
|
|
break;
|
|
|
|
if TokenStr[0] = #0 then
|
|
Error(SErrOpenString);
|
|
|
|
Inc(TokenStr);
|
|
end;
|
|
|
|
SectionLength := TokenStr - TokenStart;
|
|
SetLength(FCurTokenString, OldLength + SectionLength);
|
|
if SectionLength > 0 then
|
|
Move(TokenStart^, FCurTokenString[OldLength + 1], SectionLength);
|
|
|
|
Inc(TokenStr);
|
|
Result := tkString;
|
|
end;
|
|
'(':
|
|
begin
|
|
Inc(TokenStr);
|
|
if TokenStr[0] = '*' then
|
|
begin
|
|
// Old-style multi-line comment
|
|
Inc(TokenStr);
|
|
while (TokenStr[0] <> '*') or (TokenStr[1] <> ')') do
|
|
begin
|
|
if TokenStr[0] = #0 then
|
|
begin
|
|
if not FetchLine then
|
|
begin
|
|
Result := tkEOF;
|
|
FCurToken := Result;
|
|
exit;
|
|
end;
|
|
end else
|
|
Inc(TokenStr);
|
|
end;
|
|
Inc(TokenStr, 2);
|
|
Result := tkComment;
|
|
end else
|
|
Result := tkBraceOpen;
|
|
end;
|
|
')':
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkBraceClose;
|
|
end;
|
|
'*':
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkMul;
|
|
end;
|
|
'+':
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkPlus;
|
|
end;
|
|
',':
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkComma;
|
|
end;
|
|
'-':
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkMinus;
|
|
end;
|
|
'.':
|
|
begin
|
|
Inc(TokenStr);
|
|
if TokenStr[0] = '.' then
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkDotDot;
|
|
end else
|
|
Result := tkDot;
|
|
end;
|
|
'/':
|
|
begin
|
|
Inc(TokenStr);
|
|
if TokenStr[0] = '/' then // Single-line comment
|
|
begin
|
|
Inc(TokenStr);
|
|
TokenStart := TokenStr;
|
|
FCurTokenString := '';
|
|
while TokenStr[0] <> #0 do
|
|
Inc(TokenStr);
|
|
SectionLength := TokenStr - TokenStart;
|
|
SetLength(FCurTokenString, SectionLength);
|
|
if SectionLength > 0 then
|
|
Move(TokenStart^, FCurTokenString[1], SectionLength);
|
|
Result := tkComment;
|
|
//WriteLn('Einzeiliger Kommentar: "', CurTokenString, '"');
|
|
end else
|
|
Result := tkDivision;
|
|
end;
|
|
'0'..'9':
|
|
begin
|
|
TokenStart := TokenStr;
|
|
while True do
|
|
begin
|
|
Inc(TokenStr);
|
|
case TokenStr[0] of
|
|
'.':
|
|
begin
|
|
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
|
|
Inc(TokenStr);
|
|
if TokenStr[0] = '-' then
|
|
Inc(TokenStr);
|
|
while TokenStr[0] in ['0'..'9'] do
|
|
Inc(TokenStr);
|
|
break;
|
|
end;
|
|
else
|
|
break;
|
|
end;
|
|
end;
|
|
SectionLength := TokenStr - TokenStart;
|
|
SetLength(FCurTokenString, SectionLength);
|
|
if SectionLength > 0 then
|
|
Move(TokenStart^, FCurTokenString[1], SectionLength);
|
|
Result := tkNumber;
|
|
end;
|
|
':':
|
|
begin
|
|
Inc(TokenStr);
|
|
if TokenStr[0] = '=' then
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkAssign;
|
|
end else
|
|
Result := tkColon;
|
|
end;
|
|
';':
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkSemicolon;
|
|
end;
|
|
'<':
|
|
begin
|
|
Inc(TokenStr);
|
|
if TokenStr[0] = '>' then
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkNotEqual;
|
|
end else
|
|
Result := tkLessThan;
|
|
end;
|
|
'=':
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkEqual;
|
|
end;
|
|
'>':
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkGreaterThan;
|
|
end;
|
|
'@':
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkAt;
|
|
end;
|
|
'[':
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkSquaredBraceOpen;
|
|
end;
|
|
']':
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkSquaredBraceClose;
|
|
end;
|
|
'^':
|
|
begin
|
|
Inc(TokenStr);
|
|
Result := tkCaret;
|
|
end;
|
|
'{': // Multi-line comment
|
|
begin
|
|
Inc(TokenStr);
|
|
TokenStart := TokenStr;
|
|
FCurTokenString := '';
|
|
OldLength := 0;
|
|
NestingLevel := 0;
|
|
while (TokenStr[0] <> '}') or (NestingLevel > 0) do
|
|
begin
|
|
if TokenStr[0] = #0 then
|
|
begin
|
|
SectionLength := TokenStr - TokenStart + 1;
|
|
SetLength(FCurTokenString, OldLength + SectionLength);
|
|
if SectionLength > 1 then
|
|
Move(TokenStart^, FCurTokenString[OldLength + 1],
|
|
SectionLength - 1);
|
|
Inc(OldLength, SectionLength);
|
|
FCurTokenString[OldLength] := #10;
|
|
if not FetchLine then
|
|
begin
|
|
Result := tkEOF;
|
|
FCurToken := Result;
|
|
exit;
|
|
end;
|
|
TokenStart := TokenStr;
|
|
end else
|
|
begin
|
|
if TokenStr[0] = '{' then
|
|
Inc(NestingLevel)
|
|
else if TokenStr[0] = '}' then
|
|
Dec(NestingLevel);
|
|
Inc(TokenStr);
|
|
end;
|
|
end;
|
|
SectionLength := TokenStr - TokenStart;
|
|
SetLength(FCurTokenString, OldLength + SectionLength);
|
|
if SectionLength > 0 then
|
|
Move(TokenStart^, FCurTokenString[OldLength + 1], SectionLength);
|
|
Inc(TokenStr);
|
|
Result := tkComment;
|
|
//WriteLn('Kommentar: "', CurTokenString, '"');
|
|
if (Length(CurTokenString) > 0) and (CurTokenString[1] = '$') then
|
|
begin
|
|
TokenStart := @CurTokenString[2];
|
|
CurPos := TokenStart;
|
|
while (CurPos[0] <> ' ') and (CurPos[0] <> #0) do
|
|
Inc(CurPos);
|
|
SectionLength := CurPos - TokenStart;
|
|
SetLength(Directive, SectionLength);
|
|
if SectionLength > 0 then
|
|
begin
|
|
Move(TokenStart^, Directive[1], SectionLength);
|
|
Directive := UpperCase(Directive);
|
|
if CurPos[0] <> #0 then
|
|
begin
|
|
TokenStart := CurPos + 1;
|
|
CurPos := TokenStart;
|
|
while CurPos[0] <> #0 do
|
|
Inc(CurPos);
|
|
SectionLength := CurPos - TokenStart;
|
|
SetLength(Param, SectionLength);
|
|
if SectionLength > 0 then
|
|
Move(TokenStart^, Param[1], SectionLength);
|
|
end else
|
|
Param := '';
|
|
// WriteLn('Direktive: "', Directive, '", Param: "', Param, '"');
|
|
if (Directive = 'I') or (Directive = 'INCLUDE') then
|
|
begin
|
|
if not PPIsSkipping then
|
|
begin
|
|
IncludeStackItem := TIncludeStackItem.Create;
|
|
IncludeStackItem.SourceFile := CurSourceFile;
|
|
IncludeStackItem.Filename := CurFilename;
|
|
IncludeStackItem.Token := CurToken;
|
|
IncludeStackItem.TokenString := CurTokenString;
|
|
IncludeStackItem.Line := CurLine;
|
|
IncludeStackItem.Row := CurRow;
|
|
IncludeStackItem.TokenStr := TokenStr;
|
|
FIncludeStack.Add(IncludeStackItem);
|
|
FCurSourceFile := FileResolver.FindIncludeFile(Param);
|
|
if not Assigned(CurSourceFile) then
|
|
Error(SErrIncludeFileNotFound, [Param]);
|
|
FCurFilename := Param;
|
|
FCurRow := 0;
|
|
end;
|
|
end else if Directive = 'DEFINE' then
|
|
begin
|
|
if not PPIsSkipping then
|
|
begin
|
|
Param := UpperCase(Param);
|
|
if Defines.IndexOf(Param) < 0 then
|
|
Defines.Add(Param);
|
|
end;
|
|
end else if Directive = 'UNDEF' then
|
|
begin
|
|
if not PPIsSkipping then
|
|
begin
|
|
Param := UpperCase(Param);
|
|
Index := Defines.IndexOf(Param);
|
|
if Index >= 0 then
|
|
Defines.Delete(Index);
|
|
end;
|
|
end else if Directive = 'IFDEF' then
|
|
begin
|
|
if PPSkipStackIndex = High(PPSkipModeStack) then
|
|
Error(SErrIfXXXNestingLimitReached);
|
|
PPSkipModeStack[PPSkipStackIndex] := PPSkipMode;
|
|
PPIsSkippingStack[PPSkipStackIndex] := PPIsSkipping;
|
|
Inc(PPSkipStackIndex);
|
|
if PPIsSkipping then
|
|
begin
|
|
PPSkipMode := ppSkipAll;
|
|
PPIsSkipping := True;
|
|
end else
|
|
begin
|
|
Param := UpperCase(Param);
|
|
Index := Defines.IndexOf(Param);
|
|
if Index < 0 then
|
|
begin
|
|
PPSkipMode := ppSkipIfBranch;
|
|
PPIsSkipping := True;
|
|
end else
|
|
PPSkipMode := ppSkipElseBranch;
|
|
end;
|
|
end else if Directive = 'IFNDEF' then
|
|
begin
|
|
if PPSkipStackIndex = High(PPSkipModeStack) then
|
|
Error(SErrIfXXXNestingLimitReached);
|
|
PPSkipModeStack[PPSkipStackIndex] := PPSkipMode;
|
|
PPIsSkippingStack[PPSkipStackIndex] := PPIsSkipping;
|
|
Inc(PPSkipStackIndex);
|
|
if PPIsSkipping then
|
|
begin
|
|
PPSkipMode := ppSkipAll;
|
|
PPIsSkipping := True;
|
|
end else
|
|
begin
|
|
Param := UpperCase(Param);
|
|
Index := Defines.IndexOf(Param);
|
|
if Index >= 0 then
|
|
begin
|
|
PPSkipMode := ppSkipIfBranch;
|
|
PPIsSkipping := True;
|
|
end else
|
|
PPSkipMode := ppSkipElseBranch;
|
|
end;
|
|
end else if Directive = 'IFOPT' then
|
|
begin
|
|
if PPSkipStackIndex = High(PPSkipModeStack) then
|
|
Error(SErrIfXXXNestingLimitReached);
|
|
PPSkipModeStack[PPSkipStackIndex] := PPSkipMode;
|
|
PPIsSkippingStack[PPSkipStackIndex] := PPIsSkipping;
|
|
Inc(PPSkipStackIndex);
|
|
if PPIsSkipping then
|
|
begin
|
|
PPSkipMode := ppSkipAll;
|
|
PPIsSkipping := True;
|
|
end else
|
|
begin
|
|
{ !!!: Currently, options are not supported, so they are just
|
|
assumed as not being set. }
|
|
PPSkipMode := ppSkipIfBranch;
|
|
PPIsSkipping := True;
|
|
end;
|
|
end else if Directive = 'IF' then
|
|
begin
|
|
if PPSkipStackIndex = High(PPSkipModeStack) then
|
|
Error(SErrIfXXXNestingLimitReached);
|
|
PPSkipModeStack[PPSkipStackIndex] := PPSkipMode;
|
|
PPIsSkippingStack[PPSkipStackIndex] := PPIsSkipping;
|
|
Inc(PPSkipStackIndex);
|
|
if PPIsSkipping then
|
|
begin
|
|
PPSkipMode := ppSkipAll;
|
|
PPIsSkipping := True;
|
|
end else
|
|
begin
|
|
{ !!!: Currently, expressions are not supported, so they are
|
|
just assumed as evaluating to false. }
|
|
PPSkipMode := ppSkipIfBranch;
|
|
PPIsSkipping := True;
|
|
end;
|
|
end else if Directive = 'ELSE' then
|
|
begin
|
|
if PPSkipStackIndex = 0 then
|
|
Error(SErrInvalidPPElse);
|
|
if PPSkipMode = ppSkipIfBranch then
|
|
PPIsSkipping := False
|
|
else if PPSkipMode = ppSkipElseBranch then
|
|
PPIsSkipping := True;
|
|
end else if Directive = 'ENDIF' then
|
|
begin
|
|
if PPSkipStackIndex = 0 then
|
|
Error(SErrInvalidPPEndif);
|
|
Dec(PPSkipStackIndex);
|
|
PPSkipMode := PPSkipModeStack[PPSkipStackIndex];
|
|
PPIsSkipping := PPIsSkippingStack[PPSkipStackIndex];
|
|
end;
|
|
end else
|
|
Directive := '';
|
|
end;
|
|
end;
|
|
'A'..'Z', 'a'..'z', '_':
|
|
begin
|
|
TokenStart := TokenStr;
|
|
repeat
|
|
Inc(TokenStr);
|
|
until not (TokenStr[0] in ['A'..'Z', 'a'..'z', '0'..'9', '_']);
|
|
SectionLength := TokenStr - TokenStart;
|
|
SetLength(FCurTokenString, SectionLength);
|
|
if SectionLength > 0 then
|
|
Move(TokenStart^, FCurTokenString[1], SectionLength);
|
|
|
|
// Check if this is a keyword or identifier
|
|
// !!!: Optimize this!
|
|
for i := tkAbsolute to tkXOR do
|
|
if CompareText(CurTokenString, TokenInfos[i]) = 0 then
|
|
begin
|
|
Result := i;
|
|
FCurToken := Result;
|
|
exit;
|
|
end;
|
|
|
|
Result := tkIdentifier;
|
|
end;
|
|
else
|
|
Error(SErrInvalidCharacter, [TokenStr[0]]);
|
|
end;
|
|
|
|
FCurToken := Result;
|
|
end;
|
|
|
|
function TPascalScanner.GetCurColumn: Integer;
|
|
begin
|
|
Result := TokenStr - PChar(CurLine);
|
|
end;
|
|
|
|
end.
|
|
|
|
|
|
{
|
|
$Log$
|
|
Revision 1.6 2004-05-01 20:08:51 marco
|
|
* Exception on file not found
|
|
|
|
Revision 1.5 2003/10/25 16:24:29 michael
|
|
+ FPC also accepts binary numbers starting with %
|
|
|
|
Revision 1.4 2003/09/02 13:26:06 mattias
|
|
MG: added IF directive skipping
|
|
|
|
Revision 1.3 2003/04/04 08:01:55 michael
|
|
+ Patch from Jeff Pohlmeyer to read less than and larger than
|
|
|
|
Revision 1.2 2003/03/27 16:32:48 sg
|
|
* Added $IFxxx support
|
|
* Lots of small fixes
|
|
|
|
Revision 1.1 2003/03/13 21:47:42 sg
|
|
* First version as part of FCL
|
|
|
|
}
|