unit tcscanner;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, typinfo, fpcunit, testregistry, pscanner;

type

  { TTestTokenFinder }

  TTestTokenFinder = class(TTestCase)
  Published
    Procedure TestFind;
  end;

  { TTestStreamLineReader }


  TTestStreamLineReader = class(TTestCase)
  Private
    FReader: TStreamLineReader;
  Protected
    procedure NewSource(Const Source : string);
    Procedure TestLine(Const ALine : String; ExpectEOF : Boolean = True);
    procedure TearDown; override;
  Published
    Procedure TestCreate;
    Procedure TestEOF;
    Procedure TestEmptyLine;
    Procedure TestEmptyLineCR;
    Procedure TestEmptyLineLF;
    Procedure TestEmptyLineCRLF;
    Procedure TestEmptyLineLFCR;
    Procedure TestOneLine;
    Procedure TestTwoLines;
  end;

  { TTestingPascalScanner }

  TTestingPascalScanner = Class(TPascalScanner)
  private
    FDoSpecial: Boolean;
  protected
    function HandleMacro(AIndex: integer): TToken;override;
  Public
    Property DoSpecial : Boolean Read FDoSpecial Write FDoSpecial;
  end;

  { TTestScanner }
  TTestScanner= class(TTestCase)
  Private
    FLI: String;
    FScanner : TPascalScanner;
    FResolver : TStreamResolver;
  protected
    procedure SetUp; override;
    procedure TearDown; override;
    Function TokenToString(tk : TToken) : string;
    Procedure AssertEquals(Msg : String; Expected,Actual : TToken); overload;
    Procedure AssertEquals(Msg : String; Expected,Actual : TModeSwitch); overload;
    Procedure AssertEquals(Msg : String; Expected,Actual : TModeSwitches); overload;
    procedure NewSource(Const Source : string; DoClear : Boolean = True);
    Procedure DoTestToken(t : TToken; Const ASource : String; Const CheckEOF : Boolean = True);
    Procedure TestToken(t : TToken; Const ASource : String; Const CheckEOF : Boolean = True);
    Procedure TestTokens(t : array of TToken; Const ASource : String; Const CheckEOF : Boolean = True;Const DoClear : Boolean = True);
    Property LastIDentifier : String Read FLI Write FLi;
    Property Scanner : TPascalScanner Read FScanner;
  published
    Procedure TestEmpty;
    procedure TestEOF;
    procedure TestWhitespace;
    procedure TestComment1;
    procedure TestComment2;
    procedure TestComment3;
    procedure TestComment4;
    procedure TestComment5;
    procedure TestNestedComment1;
    procedure TestNestedComment2;
    procedure TestNestedComment3;
    procedure TestNestedComment4;
    procedure TestNestedComment5;
    procedure TestIdentifier;
    procedure TestSelf;
    procedure TestSelfNoToken;
    procedure TestString;
    procedure TestNumber;
    procedure TestChar;
    procedure TestCharString;
    procedure TestBraceOpen;
    procedure TestBraceClose;
    procedure TestMul;
    procedure TestPlus;
    procedure TestComma;
    procedure TestMinus;
    procedure TestDot;
    procedure TestDivision;
    procedure TestColon;
    procedure TestSemicolon;
    procedure TestLessThan;
    procedure TestEqual;
    procedure TestGreaterThan;
    procedure TestAt;
    procedure TestSquaredBraceOpen;
    procedure TestSquaredBraceClose;
    procedure TestCaret;
    procedure TestBackslash;
    procedure TestDotDot;
    procedure TestAssign;
    procedure TestAssignPlus;
    procedure TestAssignMinus;
    procedure TestAssignMul;
    procedure TestAssignDivision;
    procedure TestNotEqual;
    procedure TestLessEqualThan;
    procedure TestGreaterEqualThan;
    procedure TestPower;
    procedure TestSymmetricalDifference;
    procedure TestAbsolute;
    procedure TestAnd;
    procedure TestArray;
    procedure TestAs;
    procedure TestAsm;
    procedure TestBegin;
    procedure TestBitpacked;
    procedure TestCase;
    procedure TestClass;
    procedure TestConst;
    procedure TestConstructor;
    procedure TestDestructor;
    procedure TestDispinterface;
    procedure TestDiv;
    procedure TestDo;
    procedure TestDownto;
    procedure TestElse;
    procedure TestEnd;
    procedure TestExcept;
    procedure TestExports;
    procedure TestFalse;
    procedure TestFile;
    procedure TestFinalization;
    procedure TestFinally;
    procedure TestFor;
    procedure TestFunction;
    procedure TestGeneric;
    procedure TestGoto;
    Procedure TestHelper;
    procedure TestIf;
    procedure TestImplementation;
    procedure TestIn;
    procedure TestInherited;
    procedure TestInitialization;
    procedure TestInline;
    procedure TestInterface;
    procedure TestIs;
    procedure TestLabel;
    procedure TestLibrary;
    procedure TestMod;
    procedure TestNil;
    procedure TestNot;
    procedure TestObject;
    procedure TestOf;
    procedure TestOn;
    procedure TestOperator;
    procedure TestOr;
    procedure TestPacked;
    procedure TestProcedure;
    procedure TestProgram;
    procedure TestProperty;
    procedure TestRaise;
    procedure TestRecord;
    procedure TestRepeat;
    procedure TestResourceString;
    procedure TestSet;
    procedure TestShl;
    procedure TestShr;
    procedure TestShlC;
    procedure TestShrC;
    procedure TestSpecialize;
    procedure TestThen;
    procedure TestThreadvar;
    procedure TestTo;
    procedure TestTrue;
    procedure TestTry;
    procedure TestType;
    procedure TestUnit;
    procedure TestUntil;
    procedure TestUses;
    procedure TestVar;
    procedure TestWhile;
    procedure TestWith;
    procedure TestXor;
    procedure TestLineEnding;
    procedure TestTab;
    Procedure TestEscapedKeyWord;
    Procedure TestTokenSeries;
    Procedure TestTokenSeriesNoWhiteSpace;
    Procedure TestTokenSeriesComments;
    Procedure TestTokenSeriesNoComments;
    Procedure TestDefine0;
    procedure TestDefine0Spaces;
    procedure TestDefine0Spaces2;
    procedure TestDefine01;
    Procedure TestDefine1;
    Procedure TestDefine2;
    Procedure TestDefine21;
    procedure TestDefine22;
    Procedure TestDefine3;
    Procedure TestDefine4;
    Procedure TestDefine5;
    Procedure TestDefine6;
    Procedure TestDefine7;
    Procedure TestDefine8;
    Procedure TestDefine9;
    Procedure TestDefine10;
    Procedure TestDefine11;
    Procedure TestDefine12;
    Procedure TestDefine13;
    Procedure TestDefine14;
    Procedure TestInclude;
    Procedure TestInclude2;
    Procedure TestUnDefine1;
    Procedure TestMacro1;
    procedure TestMacro2;
    procedure TestMacro3;
    procedure TestMacroHandling;
    procedure TestIFDefined;
    procedure TestIFUnDefined;
    procedure TestIFAnd;
    procedure TestIFAndShortEval;
    procedure TestIFOr;
    procedure TestIFOrShortEval;
    procedure TestIFXor;
    procedure TestIFAndOr;
    procedure TestIFEqual;
    procedure TestIFNotEqual;
    procedure TestIFGreaterThan;
    procedure TestIFGreaterEqualThan;
    procedure TestIFLesserThan;
    procedure TestIFLesserEqualThan;
    procedure TestIFDefinedElseIf;
    procedure TestIfError;
    Procedure TestModeSwitch;
    Procedure TestOperatorIdentifier;
    Procedure TestUTF8BOM;
  end;

implementation

{ TTestingPascalScanner }

function TTestingPascalScanner.HandleMacro(AIndex: integer): TToken;
begin
  if DoSpecial then
    begin
    Result:=tkIdentifier;
    SetCurTokenstring('somethingweird');
    end
  else
    Result:=inherited HandleMacro(AIndex);
end;

{ TTestTokenFinder }

procedure TTestTokenFinder.TestFind;

Var
  tk,tkr : TToken;
  S : string;
  B : Boolean;

begin
  For tk:=tkAbsolute to tkXor do
    begin
    S:=tokenInfos[tk];
    B:=IsNamedToken(S,tkr);
    AssertEquals('Token '+S+' is a token',true,B);
    AssertEquals('Token '+S+' returns correct token',Ord(tk),Ord(tkr));
    end;
end;

{ TTestStreamLineReader }

procedure TTestStreamLineReader.NewSource(Const Source: string);
begin
  FReader:=TStringStreamLineReader.Create('afile',Source);
end;

procedure TTestStreamLineReader.TestLine(const ALine: String; ExpectEOF: Boolean);
begin
  AssertNotNull('Have reader',FReader);
  AssertEquals('Reading source line',ALine,FReader.ReadLine);
  if ExpectEOF then
    AssertEquals('End of file reached',True,FReader.IsEOF);
end;

procedure TTestStreamLineReader.TearDown;
begin
  inherited TearDown;
  If Assigned(FReader) then
    FreeAndNil(Freader);
end;

procedure TTestStreamLineReader.TestCreate;
begin
  FReader:=TStreamLineReader.Create('afile');
  AssertEquals('Correct filename','afile',FReader.FileName);
  AssertEquals('Initially empty',True,FReader.isEOF);
end;

procedure TTestStreamLineReader.TestEOF;
begin
  NewSource('');
  AssertEquals('Empty stream',True,FReader.IsEOF);
end;

procedure TTestStreamLineReader.TestEmptyLine;
begin
  NewSource('');
  TestLine('');
end;

procedure TTestStreamLineReader.TestEmptyLineCR;
begin
  NewSource(#13);
  TestLine('');
end;

procedure TTestStreamLineReader.TestEmptyLineLF;
begin
  NewSource(#10);
  TestLine('');
end;

procedure TTestStreamLineReader.TestEmptyLineCRLF;
begin
  NewSource(#13#10);
  TestLine('');
end;

procedure TTestStreamLineReader.TestEmptyLineLFCR;
begin
  NewSource(#10#13);
  TestLine('',False);
  TestLine('');
end;

procedure TTestStreamLineReader.TestOneLine;

Const
    S = 'a line with text';
begin
  NewSource(S);
  TestLine(S);
end;

procedure TTestStreamLineReader.TestTwoLines;
Const
    S = 'a line with text';
begin
  NewSource(S+sLineBreak+S);
  TestLine(S,False);
  TestLine(S);
end;

{ ---------------------------------------------------------------------
  TTestScanner
  ---------------------------------------------------------------------}

procedure TTestScanner.SetUp;
begin
  FResolver:=TStreamResolver.Create;
  FResolver.OwnsStreams:=True;
  FScanner:=TTestingPascalScanner.Create(FResolver);
  // Do nothing
end; 

procedure TTestScanner.TearDown; 
begin
  FreeAndNil(FScanner);
  FreeAndNil(FResolver);
end;

function TTestScanner.TokenToString(tk: TToken): string;
begin
  Result:=GetEnumName(TypeInfo(TToken),Ord(tk));
end;

procedure TTestScanner.AssertEquals(Msg: String; Expected, Actual: TToken);
begin
  AssertEquals(Msg,TokenToString(Expected),TokenToString(Actual));
end;

procedure TTestScanner.AssertEquals(Msg: String; Expected, Actual: TModeSwitch);
begin
  AssertEquals(Msg,GetEnumName(TypeInfo(TModeSwitch),Ord(Expected)),
                   GetEnumName(TypeInfo(TModeSwitch),Ord(Actual)))
end;

procedure TTestScanner.AssertEquals(Msg: String; Expected, Actual: TModeSwitches);

  Function ToString(S : TModeSwitches) : String;

  Var
    M : TModeSwitch;

  begin
    Result:='';
    For M in TModeswitch do
      if M in S then
        begin
        If (Result<>'') then
          Result:=Result+', ';
        Result:=Result+GetEnumName(TypeInfo(TModeSwitch), Ord(M));
        end;
  end;

begin
  AssertEquals(Msg,ToString(Expected),ToString(Actual));
end;

procedure TTestScanner.NewSource(const Source: string; DoClear : Boolean = True);
begin
  if DoClear then
    FResolver.Clear;
  FResolver.AddStream('afile.pp',TStringStream.Create(Source));
  Writeln('// '+TestName);
  Writeln(Source);
//  FreeAndNil(FScanner);
//  FScanner:=TTestingPascalScanner.Create(FResolver);
  FScanner.OpenFile('afile.pp');
end;

procedure TTestScanner.DoTestToken(t: TToken; const ASource: String;
  const CheckEOF: Boolean);

Var
  tk : ttoken;

begin
  NewSource(ASource);
  tk:=FScanner.FetchToken;
  AssertEquals('Read token equals expected token.',t,tk);
  if CheckEOF then
    begin
    tk:=FScanner.FetchToken;
    if (tk=tkLineEnding) and not (t in [tkEOF,tkLineEnding]) then
      tk:=FScanner.FetchToken;
    AssertEquals('EOF reached.',tkEOF,FScanner.FetchToken);
    end;
end;

procedure TTestScanner.TestToken(t: TToken; const ASource: String;
  const CheckEOF: Boolean);
Var
  S : String;
begin
  DoTestToken(t,ASource);
  if (ASource<>'') then
    begin
    S:=ASource;
    S[1]:=Upcase(S[1]);
    DoTestToken(t,S);
    end;
  DoTestToken(t,UpperCase(ASource));
  DoTestToken(t,LowerCase(ASource),CheckEOF);
end;

procedure TTestScanner.TestTokens(t: array of TToken; const ASource: String;
  const CheckEOF: Boolean; const DoClear: Boolean);
Var
  tk : ttoken;
  i : integer;

begin
  NewSource(ASource,DoClear);
  For I:=Low(t) to High(t) do
    begin
    tk:=FScanner.FetchToken;
    AssertEquals(Format('Read token %d equals expected token.',[i]),t[i],tk);
    if tk=tkIdentifier then
      LastIdentifier:=FScanner.CurtokenString;
    end;
  if CheckEOF then
    begin
    tk:=FScanner.FetchToken;
    if (tk=tkLineEnding) then
      tk:=FScanner.FetchToken;
    AssertEquals('EOF reached.',tkEOF,FScanner.FetchToken);
    end;
end;

procedure TTestScanner.TestEmpty;
begin
  AssertNotNull('Have Scanner',Scanner);
  AssertTrue('Options is empty',[]=Scanner.Options);
  AssertEquals('FPC modes is default',FPCModeSwitches,Scanner.CurrentModeSwitches);
end;

procedure TTestScanner.TestEOF;
begin
  TestToken(tkEOF,'')
end;

procedure TTestScanner.TestWhitespace;

begin
  TestToken(tkWhitespace,' ');
  TestToken(tkWhitespace,' ');
end;


procedure TTestScanner.TestComment1;

begin
  TestToken(tkComment,'{ comment }');
end;


procedure TTestScanner.TestComment2;

begin
  TestToken(tkComment,'(* comment *)');
end;


procedure TTestScanner.TestComment3;

begin
  TestToken(tkComment,'//');
end;

procedure TTestScanner.TestComment4;

begin
  DoTestToken(tkComment,'(* abc *)',False);
  AssertEquals('Correct comment',' abc ',Scanner.CurTokenString);
end;

procedure TTestScanner.TestComment5;

begin
  DoTestToken(tkComment,'(* abc'+LineEnding+'def *)',False);
  AssertEquals('Correct comment',' abc'+LineEnding+'def ',Scanner.CurTokenString);
end;

procedure TTestScanner.TestNestedComment1;
begin
  TestToken(tkComment,'// { comment } ');
end;

procedure TTestScanner.TestNestedComment2;
begin
  TestToken(tkComment,'(* { comment } *)');
end;

procedure TTestScanner.TestNestedComment3;
begin
  TestToken(tkComment,'{ { comment } }');
end;

procedure TTestScanner.TestNestedComment4;
begin
  TestToken(tkComment,'{ (* comment *) }');
end;

procedure TTestScanner.TestNestedComment5;
begin
  TestToken(tkComment,'(* (* comment *) *)');
end;


procedure TTestScanner.TestIdentifier;

begin
  TestToken(tkIdentifier,'identifier');
end;


procedure TTestScanner.TestString;

begin
  TestToken(pscanner.tkString,'''A string''');
end;

procedure TTestScanner.TestCharString;

begin
  TestToken(pscanner.tkChar,'''A''');
end;

procedure TTestScanner.TestNumber;

begin
  TestToken(tkNumber,'123');
end;


procedure TTestScanner.TestChar;

begin
  TestToken(pscanner.tkChar,'#65 ', false);
end;


procedure TTestScanner.TestBraceOpen;

begin
  TestToken(tkBraceOpen,'(');
end;


procedure TTestScanner.TestBraceClose;

begin
  TestToken(tkBraceClose,')');
end;


procedure TTestScanner.TestMul;

begin
  TestToken(tkMul,'*');
end;


procedure TTestScanner.TestPlus;

begin
  TestToken(tkPlus,'+');
end;


procedure TTestScanner.TestComma;

begin
  TestToken(tkComma,',');
end;


procedure TTestScanner.TestMinus;

begin
  TestToken(tkMinus,'-');
end;


procedure TTestScanner.TestDot;

begin
  TestToken(tkDot,'.');
end;


procedure TTestScanner.TestDivision;

begin
  TestToken(tkDivision,'/');
end;


procedure TTestScanner.TestColon;

begin
  TestToken(tkColon,':');
end;


procedure TTestScanner.TestSemicolon;

begin
  TestToken(tkSemicolon,';');
end;


procedure TTestScanner.TestLessThan;

begin
  TestToken(tkLessThan,'<');
end;


procedure TTestScanner.TestEqual;

begin
  TestToken(tkEqual,'=');
end;


procedure TTestScanner.TestGreaterThan;

begin
  TestToken(tkGreaterThan,'>');
end;


procedure TTestScanner.TestAt;

begin
  TestToken(tkAt,'@');
end;


procedure TTestScanner.TestSquaredBraceOpen;

begin
  TestToken(tkSquaredBraceOpen,'[');
end;


procedure TTestScanner.TestSquaredBraceClose;

begin
  TestToken(tkSquaredBraceClose,']');
end;


procedure TTestScanner.TestCaret;

begin
  TestToken(tkCaret,'^');
end;


procedure TTestScanner.TestBackslash;

begin
  TestToken(tkBackslash,'\');
end;


procedure TTestScanner.TestDotDot;

begin
  TestToken(tkDotDot,'..');
end;


procedure TTestScanner.TestAssign;

begin
  TestToken(tkAssign,':=');
end;

procedure TTestScanner.TestAssignPlus;
begin
  TestTokens([tkPlus,tkEqual],'+=');
  FScanner.Options:=[po_cassignments];
  TestToken(tkAssignPlus,'+=');
end;

procedure TTestScanner.TestAssignMinus;
begin
  TestTokens([tkMinus,tkEqual],'-=');
  FScanner.Options:=[po_cassignments];
  TestToken(tkAssignMinus,'-=');
end;

procedure TTestScanner.TestAssignMul;
begin
  TestTokens([tkMul,tkEqual],'*=');
  FScanner.Options:=[po_cassignments];
  TestToken(tkAssignMul,'*=');
end;

procedure TTestScanner.TestAssignDivision;
begin
  TestTokens([tkDivision,tkEqual],'/=');
  FScanner.Options:=[po_cassignments];
  TestToken(tkAssignDivision,'/=');
end;


procedure TTestScanner.TestNotEqual;

begin
  TestToken(tkNotEqual,'<>');
end;


procedure TTestScanner.TestLessEqualThan;

begin
  TestToken(tkLessEqualThan,'<=');
end;


procedure TTestScanner.TestGreaterEqualThan;

begin
  TestToken(tkGreaterEqualThan,'>=');
end;


procedure TTestScanner.TestPower;

begin
  TestToken(tkPower,'**');
end;


procedure TTestScanner.TestSymmetricalDifference;

begin
  TestToken(tkSymmetricalDifference,'><');
end;


procedure TTestScanner.TestAbsolute;

begin
  TestToken(tkabsolute,'absolute');
end;


procedure TTestScanner.TestAnd;

begin
  TestToken(tkand,'and');
end;


procedure TTestScanner.TestArray;

begin
  TestToken(tkarray,'array');
end;


procedure TTestScanner.TestAs;

begin
  TestToken(tkas,'as');
end;


procedure TTestScanner.TestAsm;

begin
  TestToken(tkasm,'asm');
end;


procedure TTestScanner.TestBegin;

begin
  TestToken(tkbegin,'begin');
end;


procedure TTestScanner.TestBitpacked;

begin
  TestToken(tkbitpacked,'bitpacked');
end;


procedure TTestScanner.TestCase;

begin
  TestToken(tkcase,'case');
end;


procedure TTestScanner.TestClass;

begin
  TestToken(tkclass,'class');
end;


procedure TTestScanner.TestConst;

begin
  TestToken(tkconst,'const');
end;


procedure TTestScanner.TestConstructor;

begin
  TestToken(tkconstructor,'constructor');
end;


procedure TTestScanner.TestDestructor;

begin
  TestToken(tkdestructor,'destructor');
end;

procedure TTestScanner.TestDispinterface;
begin
  TestToken(tkdispinterface,'dispinterface');
end;

procedure TTestScanner.TestDiv;

begin
  TestToken(tkdiv,'div');
end;


procedure TTestScanner.TestDo;

begin
  TestToken(tkdo,'do');
end;


procedure TTestScanner.TestDownto;

begin
  TestToken(tkdownto,'downto');
end;


procedure TTestScanner.TestElse;

begin
  TestToken(tkelse,'else');
end;


procedure TTestScanner.TestEnd;

begin
  TestToken(tkend,'end');
end;


procedure TTestScanner.TestExcept;

begin
  TestToken(tkexcept,'except');
end;


procedure TTestScanner.TestExports;

begin
  TestToken(tkexports,'exports');
end;


procedure TTestScanner.TestFalse;

begin
  TestToken(tkfalse,'false');
end;


procedure TTestScanner.TestFile;

begin
  TestToken(tkfile,'file');
end;


procedure TTestScanner.TestFinalization;

begin
  TestToken(tkfinalization,'finalization');
end;


procedure TTestScanner.TestFinally;

begin
  TestToken(tkfinally,'finally');
end;


procedure TTestScanner.TestFor;

begin
  TestToken(tkfor,'for');
end;


procedure TTestScanner.TestFunction;

begin
  TestToken(tkfunction,'function');
end;


procedure TTestScanner.TestGeneric;

begin
  TestToken(tkgeneric,'generic');
end;


procedure TTestScanner.TestGoto;

begin
  TestToken(tkgoto,'goto');
end;

procedure TTestScanner.TestHelper;
begin
  TestToken(tkIdentifier,'helper');
end;


procedure TTestScanner.TestIf;

begin
  TestToken(tkif,'if');
end;


procedure TTestScanner.TestImplementation;

begin
  TestToken(tkimplementation,'implementation');
end;


procedure TTestScanner.TestIn;

begin
  TestToken(tkin,'in');
end;


procedure TTestScanner.TestInherited;

begin
  TestToken(tkinherited,'inherited');
end;


procedure TTestScanner.TestInitialization;

begin
  TestToken(tkinitialization,'initialization');
end;


procedure TTestScanner.TestInline;

begin
  TestToken(tkinline,'inline');
end;


procedure TTestScanner.TestInterface;

begin
  TestToken(tkinterface,'interface');
end;


procedure TTestScanner.TestIs;

begin
  TestToken(tkis,'is');
end;


procedure TTestScanner.TestLabel;

begin
  TestToken(tklabel,'label');
end;


procedure TTestScanner.TestLibrary;

begin
  TestToken(tklibrary,'library');
end;


procedure TTestScanner.TestMod;

begin
  TestToken(tkmod,'mod');
end;


procedure TTestScanner.TestNil;

begin
  TestToken(tknil,'nil');
end;


procedure TTestScanner.TestNot;

begin
  TestToken(tknot,'not');
end;


procedure TTestScanner.TestObject;

begin
  TestToken(tkobject,'object');
end;


procedure TTestScanner.TestOf;

begin
  TestToken(tkof,'of');
end;


procedure TTestScanner.TestOn;

begin
  TestToken(tkIdentifier,'on');
end;


procedure TTestScanner.TestOperator;

begin
  Scanner.SetTokenOption(toOperatorToken);
  TestToken(tkoperator,'operator');
end;


procedure TTestScanner.TestOr;

begin
  TestToken(tkor,'or');
end;


procedure TTestScanner.TestPacked;

begin
  TestToken(tkpacked,'packed');
end;


procedure TTestScanner.TestProcedure;

begin
  TestToken(tkprocedure,'procedure');
end;


procedure TTestScanner.TestProgram;

begin
  TestToken(tkprogram,'program');
end;


procedure TTestScanner.TestProperty;

begin
  TestToken(tkproperty,'property');
end;


procedure TTestScanner.TestRaise;

begin
  TestToken(tkraise,'raise');
end;


procedure TTestScanner.TestRecord;

begin
  TestToken(tkrecord,'record');
end;


procedure TTestScanner.TestRepeat;

begin
  TestToken(tkrepeat,'repeat');
end;


procedure TTestScanner.TestResourceString;

begin
  TestToken(tkResourceString,'resourcestring');
end;


procedure TTestScanner.TestSelf;

begin
  FScanner.Options:=FScanner.Options + [po_selftoken];
  TestToken(tkself,'self');
end;

procedure TTestScanner.TestSelfNoToken;
begin
  TestToken(tkIdentifier,'self');
end;


procedure TTestScanner.TestSet;

begin
  TestToken(tkset,'set');
end;


procedure TTestScanner.TestShl;

begin
  TestToken(tkshl,'shl');
end;


procedure TTestScanner.TestShr;

begin
  TestToken(tkshr,'shr');
end;

procedure TTestScanner.TestShlC;
begin
  TestToken(tkshl,'<<');
end;

procedure TTestScanner.TestShrC;
begin
  TestToken(tkshr,'>>');
end;


procedure TTestScanner.TestSpecialize;

begin
  TestToken(tkspecialize,'specialize');
end;


procedure TTestScanner.TestThen;

begin
  TestToken(tkthen,'then');
end;


procedure TTestScanner.TestThreadvar;

begin
  TestToken(tkthreadvar,'threadvar');
end;


procedure TTestScanner.TestTo;

begin
  TestToken(tkto,'to');
end;


procedure TTestScanner.TestTrue;

begin
  TestToken(tktrue,'true');
end;


procedure TTestScanner.TestTry;

begin
  TestToken(tktry,'try');
end;


procedure TTestScanner.TestType;

begin
  TestToken(tktype,'type');
end;


procedure TTestScanner.TestUnit;

begin
  TestToken(tkunit,'unit');
end;


procedure TTestScanner.TestUntil;

begin
  TestToken(tkuntil,'until');
end;


procedure TTestScanner.TestUses;

begin
  TestToken(tkuses,'uses');
end;


procedure TTestScanner.TestVar;

begin
  TestToken(tkvar,'var');
end;


procedure TTestScanner.TestWhile;

begin
  TestToken(tkwhile,'while');
end;


procedure TTestScanner.TestWith;

begin
  TestToken(tkwith,'with');
end;


procedure TTestScanner.TestXor;

begin
  TestToken(tkxor,'xor');
end;


procedure TTestScanner.TestLineEnding;

begin
  TestToken(tkLineEnding,#10);
end;


procedure TTestScanner.TestTab;

begin
  TestToken(tkTab,#9);
end;

procedure TTestScanner.TestEscapedKeyWord;
begin
  TestToken(tkIdentifier,'&xor');
end;

procedure TTestScanner.TestTokenSeries;
begin
  TestTokens([tkin,tkWhitespace,tkOf,tkWhiteSpace,tkthen,tkWhiteSpace,tkIdentifier],'in of then aninteger')
end;

procedure TTestScanner.TestTokenSeriesNoWhiteSpace;
begin
  FScanner.SkipWhiteSpace:=True;
  TestTokens([tkin,tkOf,tkthen,tkIdentifier],'in of then aninteger')
end;

procedure TTestScanner.TestTokenSeriesComments;
begin
  TestTokens([tkin,tkWhitespace,tkOf,tkWhiteSpace,tkComment,tkWhiteSpace,tkIdentifier],'in of {then} aninteger')
end;

procedure TTestScanner.TestTokenSeriesNoComments;
begin
  FScanner.SkipComments:=True;
  TestTokens([tkin,tkWhitespace,tkOf,tkWhiteSpace,tkWhiteSpace,tkIdentifier],'in of {then} aninteger')
end;

procedure TTestScanner.TestDefine0;
begin
  TestTokens([tkComment],'{$DEFINE NEVER}');
  AssertTrue('Define not defined', FSCanner.Defines.IndexOf('NEVER')<>-1);
end;

procedure TTestScanner.TestDefine0Spaces;
begin
  TestTokens([tkComment],'{$DEFINE  NEVER}');
  AssertTrue('Define not defined',FSCanner.Defines.IndexOf('NEVER')<>-1);
end;

procedure TTestScanner.TestDefine0Spaces2;
begin
  TestTokens([tkComment],'{$DEFINE NEVER }');
  AssertTrue('Define not defined',FSCanner.Defines.IndexOf('NEVER')<>-1);
end;

procedure TTestScanner.TestDefine01;
begin
  TestTokens([tkComment],'(*$DEFINE NEVER*)');
  AssertTrue('Define not defined',FSCanner.Defines.IndexOf('NEVER')<>-1);
end;

procedure TTestScanner.TestDefine1;
begin
  TestTokens([tkComment],'{$IFDEF NEVER} of {$ENDIF}');
end;

procedure TTestScanner.TestDefine2;

begin
  FSCanner.Defines.Add('ALWAYS');
  TestTokens([tkComment,tkWhitespace,tkOf,tkWhitespace,tkcomment],'{$IFDEF ALWAYS comment} of {$ENDIF}');
end;

procedure TTestScanner.TestDefine21;
begin
  FSCanner.Defines.Add('ALWAYS');
  TestTokens([tkComment,tkWhitespace,tkOf,tkWhitespace,tkcomment],'(*$IFDEF ALWAYS*) of (*$ENDIF*)');
end;

procedure TTestScanner.TestDefine22;
begin
  FSCanner.Defines.Add('ALWAYS');
  // No whitespace. Test border of *)
  TestTokens([tkComment,tkOf,tkWhitespace,tkcomment],'(*$IFDEF ALWAYS*)of (*$ENDIF*)');
end;

procedure TTestScanner.TestDefine3;
begin
  FSCanner.Defines.Add('ALWAYS');
  TestTokens([tkComment,tkWhitespace,tkOf,tkWhitespace,tkcomment],'{$IFDEF ALWAYS} of {$ELSE} in {$ENDIF}');
end;

procedure TTestScanner.TestDefine4;
begin
  TestTokens([tkComment,tkWhitespace,tkin,tkWhitespace,tkcomment],'{$IFDEF ALWAYS} of {$ELSE} in {$ENDIF}');
end;

procedure TTestScanner.TestDefine5;
begin
  FScanner.SkipComments:=True;
  TestTokens([tkLineEnding],'{$IFDEF NEVER} of {$ENDIF}');
end;

procedure TTestScanner.TestDefine6;

begin
  FSCanner.Defines.Add('ALWAYS');
  FScanner.SkipComments:=True;
  TestTokens([tkWhitespace,tkOf,tkWhitespace],'{$IFDEF ALWAYS} of {$ENDIF}');
end;

procedure TTestScanner.TestDefine7;
begin
  FSCanner.Defines.Add('ALWAYS');
  FScanner.SkipComments:=True;
  TestTokens([tkWhitespace,tkOf,tkWhitespace],'{$IFDEF ALWAYS} of {$ELSE} in {$ENDIF}');
end;

procedure TTestScanner.TestDefine8;
begin
  FScanner.SkipComments:=True;
  TestTokens([tkWhitespace,tkin,tkWhitespace],'{$IFDEF ALWAYS} of {$ELSE} in {$ENDIF}');
end;

procedure TTestScanner.TestDefine9;
begin
  FScanner.SkipWhiteSpace:=True;
  TestTokens([],'{$IFDEF NEVER} of {$ENDIF}');
end;

procedure TTestScanner.TestDefine10;

begin
  FSCanner.Defines.Add('ALWAYS');
  FScanner.SkipComments:=True;
  TestTokens([tkWhitespace,tkOf,tkWhitespace],'{$IFDEF ALWAYS} of {$ENDIF}');
end;

procedure TTestScanner.TestDefine11;
begin
  FSCanner.Defines.Add('ALWAYS');
  FScanner.SkipComments:=True;
  FScanner.SkipWhiteSpace:=True;
  TestTokens([tkOf],'{$IFDEF ALWAYS} of {$ELSE} in {$ENDIF}');
end;

procedure TTestScanner.TestDefine12;
begin
  FScanner.SkipComments:=True;
  FScanner.SkipWhiteSpace:=True;
  TestTokens([tkin],'{$IFDEF ALWAYS} of {$ELSE} in {$ENDIF}');
end;

procedure TTestScanner.TestDefine13;
begin
  FScanner.SkipComments:=True;
  FScanner.SkipWhiteSpace:=True;
  TestTokens([tkin],'{$IFDEF ALWAYS} }; ą è {$ELSE} in {$ENDIF}');
end;

procedure TTestScanner.TestDefine14;
Const
   Source = '{$ifdef NEVER_DEFINED}' +sLineBreak+
            'type'+sLineBreak+
            '  TNPEventModel = ('+sLineBreak+
            '  NPEventModelCarbon = 0,'+sLineBreak+
            '  NPEventModelCocoa = 1'+sLineBreak+
            '}; // yes, this is an error... except this code should never be included.'+sLineBreak+
            'ą'+sLineBreak+
            '|'+sLineBreak+
            '{$endif}'+sLineBreak+
            ''+sLineBreak+
            'begin'+sLineBreak+
            'end.'+sLineBreak;
begin
  NewSource(Source,True);
  While FScanner.fetchToken<>tkEOF do

end;

procedure TTestScanner.TestInclude;
begin
  FResolver.AddStream('myinclude.inc',TStringStream.Create('if true then'));
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkIf,tkTrue,tkThen],'{$I myinclude.inc}',True,False);
end;

procedure TTestScanner.TestInclude2;
begin
  FResolver.AddStream('myinclude.inc',TStringStream.Create('if true then'));
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkIf,tkTrue,tkThen,tkElse],'{$I myinclude.inc} else',True,False);
end;

procedure TTestScanner.TestUnDefine1;
begin
  FSCanner.Defines.Add('ALWAYS');
  TestTokens([tkComment],'{$UNDEF ALWAYS}');
  AssertEquals('No more define',-1,FScanner.Defines.INdexOf('ALWAYS'));
end;

procedure TTestScanner.TestMacro1;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkbegin,tkend,tkDot],'{$MACRO on}{$DEFINE MM:=begin end.}'#13#10'MM',True,False);
end;

procedure TTestScanner.TestMacro2;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkbegin,tkend,tkDot],'{$MACRO on}{$DEFINE MM:=begin end}'#13#10'MM .',True,False);
end;

procedure TTestScanner.TestMacro3;
begin
  FScanner.SkipComments:=True;
  FScanner.SkipWhiteSpace:=True;
  TestTokens([tkof],'{$MACRO on}{$DEFINE MM:=begin end}'#13#10'{$IFDEF MM} of {$ELSE} in {$ENDIF}');
end;

procedure TTestScanner.TestMacroHandling;
begin
  TTestingPascalScanner(FScanner).DoSpecial:=True;
  FScanner.SkipComments:=True;
  FScanner.SkipWhiteSpace:=True;
  TestTokens([tkIdentifier],'{$MACRO on}{$DEFINE MM:=begin end}'#13#10'MM');
  AssertEQuals('Correct identifier', 'somethingweird',LastIdentifier);
end;

procedure TTestScanner.TestIFDefined;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkbegin,tkend,tkDot],'{$DEFINE A}{$IF defined(A)}begin{$ENDIF}end.',True,False);
end;

procedure TTestScanner.TestIFUnDefined;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkbegin,tkend,tkDot],'{$IF undefined(A)}begin{$ENDIF}end.',True,False);
end;

procedure TTestScanner.TestIFAnd;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkbegin,tkend,tkDot],
    '{$DEFINE A}{$IF defined(A) and undefined(B)}begin{$ENDIF}end.',True,False);
end;

procedure TTestScanner.TestIFAndShortEval;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkbegin,tkend,tkDot],
    '{$UNDEFINE A}{$IF defined(A) and undefined(B)}wrong{$ELSE}begin{$ENDIF}end.',
    True,False);
end;

procedure TTestScanner.TestIFOr;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkbegin,tkend,tkDot],
    '{$DEFINE B}{$IF defined(A) or defined(B)}begin{$ENDIF}end.',True,False);
end;

procedure TTestScanner.TestIFOrShortEval;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkbegin,tkend,tkDot],
    '{$DEFINE A}{$IF defined(A) or defined(B)}begin{$ENDIF}end.',True,False);
end;

procedure TTestScanner.TestIFXor;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkbegin,tkend,tkDot],
    '{$DEFINE B}{$IF defined(A) xor defined(B)}begin{$ENDIF}end.',True,False);
end;

procedure TTestScanner.TestIFAndOr;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkbegin,tkend,tkDot],
     '{$IF   defined(A) and   defined(B) or   defined(C)}wrong1{$ENDIF}'+LineEnding
    +'{$IF   defined(A) and   defined(B) or undefined(C)}{$ELSE}wrong2{$ENDIF}'+LineEnding
    +'{$IF   defined(A) and undefined(B) or   defined(C)}wrong3{$ENDIF}'+LineEnding
    +'{$IF   defined(A) and undefined(B) or undefined(C)}{$ELSE}wrong4{$ENDIF}'+LineEnding
    +'{$IF undefined(A) and   defined(B) or   defined(C)}wrong5{$ENDIF}'+LineEnding
    +'{$IF undefined(A) and   defined(B) or undefined(C)}{$ELSE}wrong6{$ENDIF}'+LineEnding
    +'{$IF undefined(A) and undefined(B) or   defined(C)}{$ELSE}wrong7{$ENDIF}'+LineEnding
    +'{$IF undefined(A) and undefined(B) or undefined(C)}begin{$ENDIF}end.',
    True,False);
end;

procedure TTestScanner.TestIFEqual;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  FScanner.AddMacro('Version','30101');
  TestTokens([tkbegin,tkend,tkDot],
    '{$IF Version=30101}begin{$ENDIF}end.',True,False);
end;

procedure TTestScanner.TestIFNotEqual;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  FScanner.AddMacro('Version','30101');
  TestTokens([tkbegin,tkend,tkDot],
    '{$IF Version<>30000}begin{$ENDIF}end.',True,False);
end;

procedure TTestScanner.TestIFGreaterThan;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  FScanner.AddMacro('Version','30101');
  TestTokens([tkbegin,tkend,tkDot],
    '{$IF Version>30000}begin{$ENDIF}end.',True,False);
end;

procedure TTestScanner.TestIFGreaterEqualThan;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  FScanner.AddMacro('Version','30101');
  TestTokens([tkbegin,tkend,tkDot],
    '{$IF Version>=30000}begin{$ENDIF}end.',True,False);
end;

procedure TTestScanner.TestIFLesserThan;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  FScanner.AddMacro('Version','30101');
  TestTokens([tkbegin,tkend,tkDot],
    '{$IF Version<40000}begin{$ENDIF}end.',True,False);
end;

procedure TTestScanner.TestIFLesserEqualThan;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  FScanner.AddMacro('Version','30101');
  TestTokens([tkbegin,tkend,tkDot],
    '{$IF Version<=30101}begin{$ENDIF}end.',True,False);
end;

procedure TTestScanner.TestIFDefinedElseIf;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  FScanner.AddDefine('cpu32');
  TestTokens([tkconst,tkIdentifier,tkEqual,tkString,tkSemicolon,tkbegin,tkend,tkDot],
    'const platform = '+LineEnding
    +'{$if defined(cpu32)} ''x86'''+LineEnding
    +'{$elseif defined(cpu64)} ''x64'''+LineEnding
    +'{$else} {$error unknown platform} {$endif};'+LineEnding
    +'begin end.',True,False);
end;

procedure TTestScanner.TestIfError;
begin
  FScanner.SkipWhiteSpace:=True;
  FScanner.SkipComments:=True;
  TestTokens([tkprogram,tkIdentifier,tkSemicolon,tkbegin,tkend,tkDot],
    'program Project1;'+LineEnding
    +'begin'+LineEnding
    +'{$if sizeof(integer) <> 4} {$error wrong sizeof(integer)} {$endif}'+LineEnding
    +'end.',True,False);
end;

procedure TTestScanner.TestModeSwitch;

Const
   PlusMinus = [' ','+','-'];

Var
  M : TModeSwitch;
  C : Char;
begin
  For M in TModeSwitch do
    for C in PlusMinus do
      if SModeSwitchNames[M]<>'' then
        begin
        Scanner.CurrentModeSwitches:=[];
        NewSource('{$MODESWITCH '+SModeSwitchNames[M]+' '+C+'}');
        While not (Scanner.FetchToken=tkEOF) do;
        if C in [' ','+'] then
          AssertTrue(SModeSwitchNames[M]+C+' sets '+GetEnumName(TypeInfo(TModeSwitch),Ord(M)),M in Scanner.CurrentModeSwitches)
        else
          AssertFalse(SModeSwitchNames[M]+C+' removes '+GetEnumName(TypeInfo(TModeSwitch),Ord(M)),M in Scanner.CurrentModeSwitches);
        end;
end;

procedure TTestScanner.TestOperatorIdentifier;
begin
  Scanner.SetNonToken(tkoperator);
  TestToken(tkidentifier,'operator',True);
end;

procedure TTestScanner.TestUTF8BOM;

begin
  DoTestToken(tkLineEnding,#$EF+#$BB+#$BF);
end;

initialization
  RegisterTests([TTestTokenFinder,TTestStreamLineReader,TTestScanner]);
end.

