* Initial check-in

git-svn-id: trunk@14859 -
This commit is contained in:
michael 2010-02-04 15:18:07 +00:00
parent 82f0d61ad9
commit 59bd32b7ea
16 changed files with 10204 additions and 0 deletions

15
.gitattributes vendored
View File

@ -1980,6 +1980,21 @@ packages/fcl-image/src/pngcomn.pp svneol=native#text/plain
packages/fcl-image/src/pscanvas.pp svneol=native#text/plain
packages/fcl-image/src/targacmn.pp svneol=native#text/plain
packages/fcl-image/src/xwdfile.pp svneol=native#text/plain
packages/fcl-js/Makefile svneol=native#text/plain
packages/fcl-js/Makefile.fpc svneol=native#text/plain
packages/fcl-js/README.TXT svneol=native#text/plain
packages/fcl-js/fpmake.pp svneol=native#text/plain
packages/fcl-js/src/jsbase.pp svneol=native#text/plain
packages/fcl-js/src/jsparser.pp svneol=native#text/plain
packages/fcl-js/src/jsscanner.pp svneol=native#text/plain
packages/fcl-js/src/jstree.pp svneol=native#text/plain
packages/fcl-js/tests/tcparser.pp svneol=native#text/plain
packages/fcl-js/tests/tcscanner.pp svneol=native#text/plain
packages/fcl-js/tests/testjs.ico -text
packages/fcl-js/tests/testjs.lpi svneol=native#text/plain
packages/fcl-js/tests/testjs.lpr svneol=native#text/plain
packages/fcl-js/tests/testjs.manifest svneol=native#text/plain
packages/fcl-js/tests/testjs.rc svneol=native#text/plain
packages/fcl-json/Makefile svneol=native#text/plain
packages/fcl-json/Makefile.fpc svneol=native#text/plain
packages/fcl-json/examples/confdemo.lpi svneol=native#text/plain

2354
packages/fcl-js/Makefile Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
#
# Makefile.fpc for Javascript Parser
#
[package]
name=fcl-js
version=2.5.1
[target]
units=jsbase jstree jsscanner jsparser
[install]
fpcpackage=y
[default]
fpcdir=../..
[compiler]
includedir=src
sourcedir=src
[shared]
build=n
[rules]
.NOTPARALLEL:

View File

@ -0,0 +1,23 @@
This is a package that contains a Javascript Scanner/parser/Syntax tree.
The following units are defined:
jsbase: the definition of Javascript values. Used to represent constant values.
jstree: The Javascript syntax tree elements. Used in the parser to describe a source program
jsscanner: the Javascript scanner. Currently not yet unicode-enabled.
jsparser: the Javascript parser. Builds a complete javascript syntax tree.
The tests directory contains a set of FPCUnit tests to test the scanner and parser.
It needs Lazarus to run.
Todo:
- Add more tests.
- Unicode support.
- Runtime-engine ?
The idea for the tree elements and the parser come from the Libsee library,
written by David Leonard.
Enjoy!
Michael.

37
packages/fcl-js/fpmake.pp Normal file
View File

@ -0,0 +1,37 @@
{$ifndef ALLPACKAGES}
{$mode objfpc}{$H+}
program fpmake;
uses fpmkunit;
Var
P : TPackage;
T : TTarget;
begin
With Installer do
begin
{$endif ALLPACKAGES}
P:=AddPackage('fcl-js');
{$ifdef ALLPACKAGES}
P.Directory:='fcl-js';
{$endif ALLPACKAGES}
P.Version:='1.0';
P.Author := 'Michael Van Canneyt';
P.License := 'LGPL with FPC modification';
P.HomepageURL := 'www.freepascal.org';
P.Email := 'michael@freepascal.org';
P.Description := 'Javascript scanner/parser/syntax tree units';
P.SourcePath.Add('src');
P.IncludePath.Add('src');
T:=P.Targets.AddUnit('jsbase.pp');
T:=P.Targets.AddUnit('jstree.pp');
T:=P.Targets.AddUnit('jsscanner.pp');
T:=P.Targets.AddUnit('jsparser.pp');
{$ifndef ALLPACKAGES}
Run;
end;
end.
{$endif ALLPACKAGES}

View File

@ -0,0 +1,175 @@
unit jsbase;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
Type
TJSType = (jstUNDEFINED,jstNull,jstBoolean,jstNumber,jstString,jstObject,jstReference,JSTCompletion);
TJSString = WideString;
TJSNumber = Double;
{ TJSValue }
TJSValue = Class(TObject)
private
FValueType: TJSType;
FValue : Record
Case Integer of
0 : (P : Pointer);
1 : (F : TJSNumber);
2 : (I : Integer);
end;
procedure ClearValue(ANewValue: TJSType);
function GetAsBoolean: Boolean;
function GetAsCompletion: TObject;
function GetAsNumber: TJSNumber;
function GetAsObject: TObject;
function GetAsReference: TObject;
function GetAsString: TJSString;
function GetIsNull: Boolean;
function GetIsUndefined: Boolean;
procedure SetAsBoolean(const AValue: Boolean);
procedure SetAsCompletion(const AValue: TObject);
procedure SetAsNumber(const AValue: TJSNumber);
procedure SetAsObject(const AValue: TObject);
procedure SetAsReference(const AValue: TObject);
procedure SetAsString(const AValue: TJSString);
procedure SetIsNull(const AValue: Boolean);
procedure SetIsUndefined(const AValue: Boolean);
Public
Destructor Destroy; override;
Property ValueType : TJSType Read FValueType;
Property IsUndefined : Boolean Read GetIsUndefined Write SetIsUndefined;
Property IsNull : Boolean Read GetIsNull Write SetIsNull;
Property AsNumber : TJSNumber Read GetAsNumber Write SetAsNumber;
Property AsBoolean : Boolean Read GetAsBoolean Write SetAsBoolean;
Property AsObject : TObject Read GetAsObject Write SetAsObject;
Property AsString : TJSString Read GetAsString Write SetAsString;
Property AsReference : TObject Read GetAsReference Write SetAsReference;
Property AsCompletion : TObject Read GetAsCompletion Write SetAsCompletion;
end;
implementation
{ TJSValue }
function TJSValue.GetAsBoolean: Boolean;
begin
If (ValueType=jstBoolean) then
Result:=(FValue.I<>0)
else
Result:=False;
end;
function TJSValue.GetAsCompletion: TObject;
begin
Result:=TObject(FValue.P);
end;
function TJSValue.GetAsNumber: TJSNumber;
begin
If (ValueType=jstNumber) then
Result:=FValue.F;
end;
function TJSValue.GetAsObject: TObject;
begin
If (ValueType=jstObject) then
Result:=TObject(FValue.P);
end;
function TJSValue.GetAsReference: TObject;
begin
If (ValueType=jstReference) then
Result:=TObject(FValue.P);
end;
function TJSValue.GetAsString: TJSString;
begin
If (ValueType=jstString) then
Result:=String(FValue.P);
end;
function TJSValue.GetIsNull: Boolean;
begin
Result:=(ValueType=jstNull);
end;
function TJSValue.GetIsUndefined: Boolean;
begin
Result:=(fValueType=jstUndefined);
end;
procedure TJSValue.ClearValue(ANewValue : TJSType);
begin
Case FValueType of
jstString : String(FValue.P):='';
jstNumber : FValue.F:=0;
else
FValue.I:=0;
end;
FValueType:=ANewValue;
end;
procedure TJSValue.SetAsBoolean(const AValue: Boolean);
begin
ClearValue(jstBoolean);
FValue.I:=Ord(AValue);
end;
procedure TJSValue.SetAsCompletion(const AValue: TObject);
begin
ClearValue(jstBoolean);
FValue.P:=AValue;
end;
procedure TJSValue.SetAsNumber(const AValue: TJSNumber);
begin
ClearValue(jstNumber);
FValue.F:=AValue;
end;
procedure TJSValue.SetAsObject(const AValue: TObject);
begin
ClearValue(jstObject);
FValue.P:=AVAlue;
end;
procedure TJSValue.SetAsReference(const AValue: TObject);
begin
ClearValue(jstReference);
FValue.P:=AVAlue;
end;
procedure TJSValue.SetAsString(const AValue: TJSString);
begin
ClearValue(jstString);
String(FValue.P):=AValue;
end;
procedure TJSValue.SetIsNull(const AValue: Boolean);
begin
ClearValue(jstNull);
end;
procedure TJSValue.SetIsUndefined(const AValue: Boolean);
begin
ClearValue(jstUndefined);
end;
destructor TJSValue.Destroy;
begin
ClearValue(jstUndefined);
inherited Destroy;
end;
end.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,909 @@
{
This file is part of the Free Component Library
ECMAScript (JavaScript) 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 JSScanner;
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';
SInvalidHexadecimalNumber = 'Invalid decimal number';
SErrInvalidNonEqual = 'SyntaxError: != or !== expected';
type
TJSToken = (tjsUnknown,
// Specials
tjsEOF,tjsWhiteSpace,tjsChar,tjsString, tjsIdentifier,tjsNumber, tjsComment,tjsREGEX, tjsRESERVED,
tjsANDAND, tjsANDEQ,
tjsBraceOpen,tjsBraceClose,tjsSQuaredBraceOpen,tjsSQuaredBraceClose,tjsCurlyBraceOpen,tjsCurlyBraceClose,
tjsCOMMA,tjsCOLON, tjsDOT,tjsSEMICOLON, tjsASSIGN,tjsGT,tjsLT, tjsConditional,
tjsPLUS,tjsMINUS,tjsMUL,tjsDIV,tjsAnd,tjsOR, tjsInv, tjsMod, tjsXOR, tjsNot,
tjsEQ,
tjsGE,
tjsLE, tjsLSHIFT, tjsLSHIFTEQ,
tjsMINUSEQ, tjsMINUSMINUS, tjsMODEQ,tjsDIVEQ,tjsXOREq,
tjsNE,
tjsOREQ, tjsOROR,
tjsPLUSEQ, tjsPLUSPLUS,
tjsURSHIFT, tjsURSHIFTEQ,
tjsRSHIFT, tjsRSHIFTEQ,
tjsSEQ, tjsSNE, tjsMULEQ,
{ Reserved words start here. They must be last }
tjsBREAK,tjsCASE, tjsCATCH, tjsCONTINUE,
tjsDEFAULT, tjsDELETE, tjsDO,
tjsELSE,
tjsFalse, tjsFINALLY, tjsFOR, tjsFUNCTION,
tjsIF, tjsIN, tjsINSTANCEOF,
tjsNEW,tjsNULL,
tjsRETURN,
tjsSWITCH,
tjsTHIS, tjsTHROW, tjsTrue, tjsTRY, tjsTYPEOF,
tjsVAR, tjsVOID,
tjsWHILE, tjsWITH
);
const
FirstKeyword = tjsBreak;
LastKeyWord = tJSWith;
TokenInfos: array[TJSToken] of string = ('unknown',
// Specials
'EOF','whitespace','Char','String', 'identifier','number','comment','regular expression', 'reserved word',
'&&','&=',
'(',')','[',']','{','}',
',',':','.',';','=','>','<','?',
'+','-','*','/','&','|','~','%','^','!',
'==',
'>=',
'<=', '<<', '<<=',
'-=', '--', '%=', '/=','^=',
'!=',
'|=', '||',
'+=', '++',
'>>>', '>>>=',
'>>', '>>=',
'===', '!==', '*=',
// Identifiers last
'break','case','catch', 'continue',
'default','delete', 'do',
'else',
'false','finally', 'for', 'function',
'if', 'in', 'instanceof',
'new','null',
'return',
'switch',
'this', 'throw', 'true', 'try', 'typeof',
'var', 'void',
'while', 'with'
);
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;
EJSScannerError = class(Exception);
{ TJSScanner }
TJSScanner = class
private
FReturnComments: Boolean;
FReturnWhiteSpace: Boolean;
FSourceFile: TLineReader;
FSourceFilename: string;
FCurRow: Integer;
FCurToken: TJSToken;
FCurTokenString: string;
FCurLine: string;
FDefines: TStrings;
TokenStr: PChar;
FSourceStream : TStream;
FOwnSourceFile : Boolean;
function CommentDiv: TJSToken;
function DoIdentifier : TJSToken;
function DoMultiLineComment: TJSToken;
function DoNumericLiteral: TJSToken;
function DoSingleLineComment: TJSToken;
function DoStringLiteral: TJSToken;
function DoWhiteSpace: TJSToken;
function FetchLine: Boolean;
function GetCurColumn: Integer;
function ReadUnicodeEscape: WideChar;
protected
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: TJSToken;
Function IsEndOfLine : Boolean;
Property ReturnComments : Boolean Read FReturnComments Write FReturnComments;
Property ReturnWhiteSpace : Boolean Read FReturnWhiteSpace Write FReturnWhiteSpace;
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: TJSToken read FCurToken;
property CurTokenString: string read FCurTokenString;
end;
implementation
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 TJSScanner.Create(ALineReader: TLineReader);
begin
inherited Create;
FSourceFile := ALineReader;
end;
constructor TJSScanner.Create(AStream: TStream);
begin
FSourceStream:=ASTream;
FOwnSourceFile:=True;
Create(TStreamLineReader.Create(AStream));
end;
destructor TJSScanner.Destroy;
begin
If FOwnSourceFile then
FSourceFile.Free;
inherited Destroy;
end;
procedure TJSScanner.OpenFile(const AFilename: string);
begin
FSourceFile := TFileLineReader.Create(AFilename);
FSourceFilename := AFilename;
end;
procedure TJSScanner.Error(const Msg: string);
begin
raise EJSScannerError.Create(Msg);
end;
procedure TJSScanner.Error(const Msg: string; Args: array of Const);
begin
raise EJSScannerError.CreateFmt(Msg, Args);
end;
function TJSScanner.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 TJSScanner.DoWhiteSpace : TJSToken;
begin
Result:=tjsWhitespace;
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 TJSScanner.DoSingleLineComment : TJSToken;
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 := tjsComment;
end;
function TJSScanner.DoMultiLineComment : TJSToken;
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 := tjsEOF;
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
Move(TokenStart^, FCurTokenString[Olen + 1], Len);
Inc(TokenStr);
Result := tjsComment;
end;
function TJSScanner.CommentDiv : TJSToken;
begin
FCurTokenString := '';
Inc(TokenStr);
if (TokenStr[0] = '/') then // Single-line comment
Result:=DoSingleLineComment
else if (TokenStr[0]='*') then
Result:=DoMultiLineComment
else if (TokenStr[0] = '=') then // Single-line comment
begin
Result:=tjsDiveQ;
Inc(TokenStr)
end
else
Result:=tjsDiv;
end;
Function TJSScanner.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;
Function TJSScanner.DoStringLiteral : TJSToken;
Var
Delim : Char;
TokenStart : PChar;
Len,OLen,I : Integer;
S : String;
begin
Delim:=TokenStr[0];
Inc(TokenStr);
TokenStart := TokenStr;
OLen := 0;
FCurTokenString := '';
while not (TokenStr[0] in [#0,Delim]) do
begin
if (TokenStr[0]='\') then
begin
// Save length
Len := TokenStr - TokenStart;
Inc(TokenStr);
// Read escaped token
Case TokenStr[0] of
'"' : S:='"';
't' : S:=#9;
'b' : S:=#8;
'n' : S:=#10;
'r' : S:=#13;
'f' : S:=#12;
'\' : S:='\';
'/' : S:='/';
'u' : begin
S:=ReadUniCodeEscape;
end;
#0 : Error(SErrOpenString);
else
Error(SErrInvalidCharacter, [TokenStr[0]]);
end;
SetLength(FCurTokenString, OLen + Len+1+Length(S));
if Len > 0 then
Move(TokenStart^, FCurTokenString[OLen + 1], Len);
Move(S[1],FCurTokenString[OLen + Len+1],Length(S));
Inc(OLen, Len+Length(S));
// Next char
// Inc(TokenStr);
TokenStart := TokenStr+1;
end;
if TokenStr[0] = #0 then
Error(SErrOpenString);
Inc(TokenStr);
end;
if TokenStr[0] = #0 then
Error(SErrOpenString);
Len := TokenStr - TokenStart;
SetLength(FCurTokenString, OLen + Len);
if Len > 0 then
Move(TokenStart^, FCurTokenString[OLen+1], Len);
Inc(TokenStr);
Result := tjsString;
end;
function TJSScanner.DoNumericLiteral :TJSToken;
Var
TokenStart : PChar;
Len : Integer;
begin
TokenStart := TokenStr;
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
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] 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);
Result := tjsNumber;
end;
function TJSScanner.DoIdentifier : TJSToken;
Var
TokenStart:PChar;
Len : Integer;
I : TJSToken;
begin
Result:=tjsIdentifier;
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);
// Check if this is a keyword or identifier
// !!!: Optimize this!
I:=FirstKeyword;
While (Result=tjsIdentifier) and (I<=Lastkeyword) do
begin
if (CurTokenString=TokenInfos[i]) then
begin
Result := i;
FCurToken := Result;
exit;
end;
I:=Succ(I);
end
end;
function TJSScanner.FetchToken: TJSToken;
var
TokenStart, CurPos: PChar;
i: TJSToken;
OldLength, SectionLength, NestingLevel, Index: Integer;
begin
Repeat
if TokenStr = nil then
if not FetchLine then
begin
Result := tjsEOF;
FCurToken := Result;
exit;
end;
CurPos:=TokenStr;
FCurTokenString := '';
case TokenStr[0] of
#0: // Empty line
begin
FetchLine;
Result := tjsWhitespace;
end;
'/' :
Result:=CommentDiv;
#9, ' ':
Result := DoWhiteSpace;
'''','"':
Result:=DoStringLiteral;
'0'..'9':
Result:=DoNumericLiteral;
'&':
begin
Inc(TokenStr);
If Tokenstr[0]='&' then
begin
Inc(TokenStr);
Result := tjsAndAnd;
end
else If Tokenstr[0]='=' then
begin
Inc(TokenStr);
Result := tjsAndEQ;
end
else
Result := tjsAnd;
end;
'%':
begin
Inc(TokenStr);
If Tokenstr[0]='=' then
begin
Inc(TokenStr);
Result := tjsModEq;
end
else
Result := tjsMod;
end;
'^':
begin
Inc(TokenStr);
If (TokenStr[0]='=') then
begin
Result:=tjsXorEq;
Inc(tokenStr)
end
else
result:=tjsXOR;
end;
'|':
begin
Inc(TokenStr);
If Tokenstr[0]='|' then
begin
Inc(TokenStr);
Result := tjsOROR;
end
else If Tokenstr[0]='=' then
begin
Inc(TokenStr);
Result := tjsOREQ;
end
else
Result := tjsOR;
end;
'(':
begin
Inc(TokenStr);
Result := tjsBraceOpen;
end;
')':
begin
Inc(TokenStr);
Result := tjsBraceClose;
end;
'*':
begin
Inc(TokenStr);
If (TokenStr[0]='=') then
begin
Inc(TokenStr);
Result := tjsMulEq;
end
else
Result := tjsMul;
end;
'+':
begin
Inc(TokenStr);
If (TokenStr[0]='=') then
begin
Inc(TokenStr);
Result := tjsPlusEq;
end
else If (TokenStr[0]='+') then
begin
Inc(TokenStr);
Result := tjsPlusPlus;
end
else
Result := tjsPlus;
end;
',':
begin
Inc(TokenStr);
Result := tjsComma;
end;
'-':
begin
Inc(TokenStr);
If (TokenStr[0]='=') then
begin
Inc(TokenStr);
Result:=tjsMinusEq
end
else If (TokenStr[0]='-') then
begin
Inc(TokenStr);
Result:=tjsMinusMinus
end
else if (TokenStr[0] in ['0'..'9']) then
begin
Result:=DoNumericLiteral;
If (Result=tjsNumber) then
FCurTokenString:='-'+FCurTokenString;
end
else
Result := tjsMinus;
end;
'.':
begin
Inc(TokenStr);
Result := tjsDot;
end;
':':
begin
Inc(TokenStr);
Result := tjsColon;
end;
';':
begin
Inc(TokenStr);
Result := tjsSemicolon;
end;
'<':
begin
Inc(TokenStr);
if TokenStr[0] = '=' then
begin
Inc(TokenStr);
Result := tjsLE;
end
else if TokenStr[0] = '<' then
begin
Inc(TokenStr);
if (TokenStr[0] = '=') then
begin
Inc(TokenStr);
Result := tjsLShiftEq;
end
else
Result := tjsLShift;
end
else
Result := tjsLT;
end;
'=':
begin
Inc(TokenStr);
if (TokenStr[0]='=') then
begin
Inc(TokenStr);
If (TokenStr[0]='=') then
begin
Inc(TokenStr);
Result:=tjsSEQ;
end
else
Result:=tjsEQ;
end
else
Result := tjsAssign;
end;
'!':
begin
Inc(TokenStr);
if (TokenStr[0]='=') then
begin
Inc(TokenStr);
If (TokenStr[0]='=') then
begin
Inc(TokenStr);
Result:=tjsSNE;
end
else
Result:=tjsNE;
end
else
Result:=tjsNot;// Error(SErrInvalidNonEqual);
end;
'~':
begin
Inc(TokenStr);
Result:=tjsInv;
end;
'>':
begin
Inc(TokenStr);
if TokenStr[0] = '=' then
begin
Inc(TokenStr);
Result:=tjsGE;
end
else if TokenStr[0] = '>' then
begin
Inc(TokenStr);
if (TokenStr[0] = '>') then
begin
Inc(TokenStr);
if (TokenStr[0] = '=') then
begin
Inc(TokenStr);
Result:=tjsURSHIFTEQ;
end
else
Result:=tjsURSHIFT;
end
else if (TokenStr[0] = '=') then
begin
Inc(TokenStr);
Result:=tjsRSHIFTEq;
end
else
Result:=tjsRSHIFT;
end
else
Result := tjsGT;
end;
'[':
begin
Inc(TokenStr);
Result := tJSSquaredBraceOpen;
end;
']':
begin
Inc(TokenStr);
Result := tJSSquaredBraceClose;
end;
'{':
begin
Inc(TokenStr);
Result := tJSCurlyBraceOpen;
end;
'}':
begin
Inc(TokenStr);
Result := tJSCurlyBraceClose;
end;
else
Result:=DoIdentifier;
end; // Case
Until (Not (Result in [tjsComment,tjsWhitespace])) or
((Result=tjsComment) and ReturnComments) or
((Result=tjsWhiteSpace) and ReturnWhiteSpace);
FCurToken:=Result;
end;
function TJSScanner.IsEndOfLine: Boolean;
begin
Result:=(TokenStr=Nil) or (TokenStr[0] in [#0,#10,#13]);
end;
function TJSScanner.GetCurColumn: Integer;
begin
Result := TokenStr - PChar(CurLine);
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;
end;
until (FBufPos=FBufLen) or (PRun^ in [10,13]);
Len:=FBufPos-FPos;
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
Inc(FBufPos);
end;
end;
end;
end.

View File

@ -0,0 +1,963 @@
unit jstree;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, jsbase;
Type
TJSElementFlag = (elIsConst,elIsConstValid);
TJSElementFlags = set of TJSElementFlag;
TJSFunctionBody = Class;
{ TJSElement }
TJSObject = Class(TObject);
{ TJSFuncDef }
TJSFuncDef = Class(TJSObject)
private
FBody: TJSFunctionBody;
FCache: TJSObject;
FCommon: TJSObject;
FIsEmpty: Boolean;
FName: String;
FNext: TJSFuncDef;
FParams: TStrings;
FSec: TObject;
procedure SetParams(const AValue: TStrings);
Public
Constructor Create;
Destructor Destroy; override;
Property Params : TStrings Read FParams Write SetParams;
Property Body : TJSFunctionBody Read FBody Write FBody;
Property Name : String Read FName Write FName;
Property Common : TJSObject Read FCommon Write FCommon;
Property Cache : TJSObject Read FCache write FCache;
Property Next : TJSFuncDef Read FNext Write FNext;
Property IsEmpty : Boolean Read FIsEmpty Write FIsEmpty;
Property SecurityDomain : TObject Read FSec Write FSec;
end;
TJSString = WideString;
TJSElement = Class (TJSObject)
private
FFlags: TJSElementFlags;
FLine: Integer;
FRow: Integer;
FSource: String;
Public
Constructor Create(ALine,ARow : Integer; Const ASource : String = ''); virtual;
Property Source : String Read FSource Write FSource;
Property Row : Integer Read FRow Write FRow;
Property Line : Integer Read FLine Write FLine;
Property Flags : TJSElementFlags Read FFlags Write FFlags;
end;
TJSElementClass = Class of TJSElement;
{ TJSEmptyBlockStatement }
TJSEmptyBlockStatement = Class(TJSElement);
TJSEmptyStatement = Class(TJSElement);
{ TJSLiteral }
TJSLiteral = Class(TJSElement)
private
FValue: TJSValue;
Public
Constructor Create(ALine,ARow : Integer; Const ASource : String = ''); override;
Destructor Destroy; override;
Property Value : TJSValue Read FValue Write FValue;
end;
{ TJSStringLiteral }
TJSStringLiteral = Class(TJSElement)
private
FValue: TJSString;
Public
Property Value : TJSString Read FValue Write FValue;
end;
{ TJSRegularExpressionLiteral }
TJSRegularExpressionLiteral = Class(TJSElement)
private
FPattern: TJSValue;
FPatternFlags: TJSValue;
FArgv : Array[0..1] of TJSValue;
function GetA(AIndex : integer): TJSValue;
procedure SetA(AIndex : integer; const AValue: TJSValue);
Public
Constructor Create(ALine,ARow : Integer; Const ASource : String = ''); override;
Destructor Destroy; override;
Property Pattern : TJSValue Read FPattern Write FPattern;
Property PatternFlags : TJSValue Read FPatternFlags Write FPatternFlags;
Property Argv[AIndex : integer] : TJSValue Read GetA Write SetA;
end;
{ TJSPrimaryExpressionIdent }
TJSPrimaryExpressionIdent = Class(TJSElement)
private
FString: TJSString;
Public
Property AString : TJSString Read FString Write FString;
end;
TJSPrimaryExpressionThis = Class(TJSElement);
{ TJSArrayLiteralElement }
TJSArrayLiteralElement = Class(TCollectionItem)
private
FExpr: TJSelement;
FFindex: Integer;
Public
Destructor Destroy; override;
Property Expr : TJSelement Read FExpr Write FExpr;
Property ElementIndex : Integer Read FFindex Write FFIndex;
end;
{ TJSArrayLiteralElements }
TJSArrayLiteralElements = Class(TCollection)
private
function GetE(AIndex : Integer): TJSArrayLiteralElement;
procedure SetE(AIndex : Integer; const AValue: TJSArrayLiteralElement);
Public
Function AddElement : TJSArrayLiteralElement;
Property Elements[AIndex : Integer] : TJSArrayLiteralElement Read GetE Write SetE; default;
end;
{ TJSArrayLiteral }
TJSArrayLiteral = Class(TJSElement)
private
FElements: TJSArrayLiteralElements;
Public
Constructor Create(ALine,ARow : Integer; const ASource : String = ''); override;
Destructor Destroy; override;
Property Elements : TJSArrayLiteralElements Read FElements;
end;
{ TJSObjectLiteralElement }
TJSObjectLiteralElement = Class(TCollectionItem)
private
FExpr: TJSelement;
FName: TJSString;
Public
Destructor Destroy; override;
Property Expr : TJSelement Read FExpr Write FExpr;
Property Name : TJSString Read FName Write FName;
end;
{ TJSObjectLiteralElements }
TJSObjectLiteralElements = Class(TCollection)
private
function GetE(AIndex : Integer): TJSObjectLiteralElement;
procedure SetE(AIndex : Integer; const AValue: TJSObjectLiteralElement);
Public
Function AddElement : TJSObjectLiteralElement;
Property Elements[AIndex : Integer] : TJSObjectLiteralElement Read GetE Write SetE; default;
end;
{ TJSObjectLiteral }
TJSObjectLiteral = Class(TJSElement)
private
FElements: TJSObjectLiteralElements;
Public
Constructor Create(ALine,ARow : Integer; const ASource : String = ''); override;
Destructor Destroy; override;
Property Elements : TJSObjectLiteralElements Read FElements;
end;
{ TJSArguments }
TJSArguments = Class(TJSArrayLiteral);
{ TJSMemberExpression }
TJSMemberExpression = Class(TJSElement)
private
FMexpr: TJSElement;
Public
Destructor Destroy; override;
Property Mexpr : TJSElement Read FMexpr Write FMexpr;
end;
{ TJSNewMemberExpression }
TJSNewMemberExpression = Class(TJSMemberExpression)
private
FArgs: TJSArguments;
Public
Destructor Destroy; override;
Property Args : TJSArguments Read FArgs Write FArgs;
end;
{ TJSDotMemberExpression }
TJSDotMemberExpression = Class(TJSMemberExpression)
private
FName: TJSString;
Public
Property Name : TJSString Read FName Write FName;
end;
{ TJSBracketMemberExpression }
TJSBracketMemberExpression = Class(TJSMemberExpression)
private
FName: TJSElement;
Public
Destructor Destroy; override;
Property Name : TJSElement Read FName Write FName;
end;
{ TJSCallExpression }
TJSCallExpression = Class(TJSElement)
private
FArgs: TJSArguments;
FExpr: TJSElement;
Public
Destructor Destroy; override;
Property Expr : TJSElement Read FExpr Write FExpr;
Property Args : TJSArguments Read FArgs Write FArgs;
end;
{ TJSUnary }
TJSUnary = Class(TJSElement)
private
FA: TJSElement;
Public
Destructor Destroy; override;
Property A : TJSElement Read FA Write FA;
end;
{ TJSVariableStatement }
TJSVariableStatement = Class(TJSUnary);
TJSExpressionStatement = Class(TJSUnary);
TJSThrowStatement = Class(TJSUnary);
TJSUnaryExpression = Class(TJSUnary);
TJSUnaryDeleteExpression = Class(TJSUnaryExpression);
TJSUnaryVoidExpression = Class(TJSUnaryExpression);
TJSUnaryTypeOfExpression = Class(TJSUnaryExpression);
TJSUnaryPrePlusPlusExpression = Class(TJSUnaryExpression);
TJSUnaryPreMinusMinusExpression = Class(TJSUnaryExpression);
TJSUnaryPlusExpression = Class(TJSUnaryExpression);
TJSUnaryMinusExpression = Class(TJSUnaryExpression);
TJSUnaryInvExpression = Class(TJSUnaryExpression);
TJSUnaryNotExpression = Class(TJSUnaryExpression);
TJSUnaryPostPlusPlusExpression = Class(TJSUnaryExpression);
TJSUnaryPostMinusMinusExpression = Class(TJSUnaryExpression);
{ TJSBinary }
TJSBinary = Class(TJSElement)
private
FA: TJSElement;
FB: TJSElement;
Public
Destructor Destroy; override;
Property A : TJSElement Read FA Write FA;
Property B : TJSElement Read FB Write FB;
end;
{ TJSStatementList }
TJSStatementList = Class(TJSBinary); // A->first statement, B->next in list, chained.
TJSVariableDeclarationList = Class(TJSBinary);
TJSWithStatement = Class(TJSBinary); // A-> with expression, B->statement(s)
TJSLogicalOrExpression = Class (TJSBinary);
TJSLogicalAndExpression = Class (TJSBinary);
TJSBitwiseAndExpression = Class (TJSBinary);
TJSBitwiseOrExpression = Class (TJSBinary);
TJSBitwiseXOrExpression = Class (TJSBinary);
TJSEqualityExpression = Class (TJSBinary);
TJSEqualityExpressionEQ = Class(TJSEqualityExpression);
TJSEqualityExpressionNE = Class(TJSEqualityExpression);
TJSEqualityExpressionSEQ = Class(TJSEqualityExpression);
TJSEqualityExpressionSNE = Class(TJSEqualityExpression);
TJSRelationalExpression = Class(TJSBinary);
TJSRelationalExpressionLT = Class(TJSRelationalExpression);
TJSRelationalExpressionGT = Class(TJSRelationalExpression);
TJSRelationalExpressionLE = Class(TJSRelationalExpression);
TJSRelationalExpressionGE = Class(TJSRelationalExpression);
TJSRelationalExpressionIn = Class(TJSRelationalExpression);
TJSRelationalExpressionInstanceOf = Class(TJSRelationalExpression);
TJSShiftExpression = Class(TJSBinary);
TJSLShiftExpression = Class(TJSShiftExpression);
TJSRShiftExpression = Class(TJSShiftExpression);
TJSURShiftExpression = Class(TJSShiftExpression);
TJSAdditiveExpression = Class(TJSBinary);
TJSAdditiveExpressionPlus = Class(TJSAdditiveExpression);
TJSAdditiveExpressionMinus = Class(TJSAdditiveExpression);
TJSMultiplicativeExpression = Class(TJSBinary);
TJSMultiplicativeExpressionMul = Class(TJSMultiplicativeExpression);
TJSMultiplicativeExpressionDiv = Class(TJSMultiplicativeExpression);
TJSMultiplicativeExpressionMod = Class(TJSMultiplicativeExpression);
TJSCommaExpression = Class(TJSBinary);
{ TJSConditionalExpression }
TJSConditionalExpression = Class(TJSElement)
private
FA: TJSElement;
FB: TJSElement;
FC: TJSElement;
Public
Destructor Destroy; override;
Property A : TJSElement Read FA Write FA;
Property B : TJSElement Read FB Write FB;
Property C : TJSElement Read FC Write FC;
end;
{ TJSAssignStatement }
TJSAssignStatement = Class(TJSElement)
private
FExpr: TJSElement;
FLHS: TJSElement;
Public
Destructor Destroy; override;
Property Expr : TJSElement Read FExpr Write FExpr;
Property LHS : TJSElement Read FLHS Write FLHS;
end;
TJSSimpleAssignStatement = Class(TJSAssignStatement);
TJSMulEqAssignStatement = Class(TJSAssignStatement);
TJSDivEqAssignStatement = Class(TJSAssignStatement);
TJSModEqAssignStatement = Class(TJSAssignStatement);
TJSAddEqAssignStatement = Class(TJSAssignStatement);
TJSSubEqAssignStatement = Class(TJSAssignStatement);
TJSLShiftEqAssignStatement = Class(TJSAssignStatement);
TJSRShiftEqAssignStatement = Class(TJSAssignStatement);
TJSURShiftEqAssignStatement = Class(TJSAssignStatement);
TJSANDEqAssignStatement = Class(TJSAssignStatement);
TJSOREqAssignStatement = Class(TJSAssignStatement);
TJSXOREqAssignStatement = Class(TJSAssignStatement);
{ TJSVarDeclaration }
TJSVarDeclaration = Class(TJSElement)
private
FInit: TJSElement;
FName: String;
Public
Destructor Destroy; override;
Property Name : String Read FName Write FName;
Property Init : TJSElement Read FInit Write FInit;
end;
{ TJSIfStatement }
TJSIfStatement = Class(TJSElement)
private
FBFalse: TJSElement;
FBTrue: TJSElement;
FCond: TJSElement;
Public
Destructor Destroy; override;
Property Cond : TJSElement Read FCond Write FCond;
Property btrue : TJSElement Read FBTrue Write FBTrue;
Property bfalse : TJSElement Read FBFalse Write FBFalse;
end;
{ TJSWhileStatement }
{ TJSTargetStatement }
TJSTargetStatement = Class(TJSElement)
private
FTarget: Cardinal;
Public
Property Target : Cardinal Read FTarget Write FTarget;
end;
{ TJSBodyStatement }
TJSBodyStatement = Class(TJSTargetStatement)
private
FBody: TJSElement;
Public
Destructor Destroy; override;
Property body : TJSElement Read FBody Write FBody;
end;
{ TJSCondLoopStatement }
TJSCondLoopStatement = Class(TJSBodyStatement)
private
FCond: TJSElement;
Public
Destructor Destroy; override;
Property Cond : TJSElement Read FCond Write FCond;
end;
TJSWhileStatement = Class(TJSCondLoopStatement)
end;
{ TJSForStatement }
TJSForStatement = Class(TJSCondLoopStatement)
private
FIncr: TJSElement;
FInit: TJSElement;
Public
Destructor Destroy; override;
Property Incr : TJSElement Read FIncr Write FIncr;
Property Init : TJSElement Read FInit Write FInit;
end;
{ TJSForInStatement }
TJSForInStatement = Class(TJSBodyStatement)
private
FLhs: TJSElement;
FList: TJSElement;
Public
Destructor Destroy; override;
Property LHS : TJSElement Read FLHS Write FLHS;
Property List : TJSElement Read FList Write FList;
end;
TJSContinueStatement = Class(TJSTargetStatement);
TJSBreakStatement = Class(TJSTargetStatement);
{ TJSReturn }
TJSReturnStatement = Class(TJSElement)
private
FExpr: TJSElement;
Public
Destructor Destroy; override;
Property Expr : TJSElement Read FExpr Write FExpr;
end;
{ TJSCaseElement }
TJSCaseElement = Class(TCollectionItem)
private
FBody: TJSElement;
FExpr: TJSelement;
Public
Destructor Destroy; override;
Property Expr : TJSelement Read FExpr Write FExpr;
Property Body : TJSElement Read FBody Write FBody;
end;
{ TJSCaseElements }
TJSCaseElements = Class(TCollection)
private
function GetC(AIndex : Integer): TJSCaseElement;
procedure SetC(AIndex : Integer; const AValue: TJSCaseElement);
Public
Function AddCase : TJSCaseElement;
Property Cases[AIndex : Integer] : TJSCaseElement Read GetC Write SetC;
end;
{ TJSSwitch }
TJSSwitchStatement = Class(TJSTargetStatement)
private
FCases: TJSCaseElements;
FCond: TJSelement;
FDefault: TJSCaseElement;
Public
Constructor Create(ALine,ARow : Integer; const ASource : String = ''); override;
Destructor Destroy; override;
Property Cond : TJSelement Read FCond Write FCond;
Property Cases : TJSCaseElements Read FCases;
Property TheDefault : TJSCaseelement Read FDefault Write FDefault;
end;
{ TJSLabeledStatement }
TJSLabeledStatement = Class(TJSUnary)
private
FTarget: Integer;
Public
Property target: Integer Read FTarget Write FTarget;
end;
{ TJSTryStatement }
TJSTryStatement = Class(TJSElement)
private
FBCatch: TJSElement;
FBFinally: TJSElement;
FBlock: TJSElement;
FIdent: TJSString;
Public
Destructor Destroy; override;
Property Block : TJSElement Read FBlock Write FBlock;
Property BCatch : TJSElement Read FBCatch Write FBCatch;
Property BFinally : TJSElement Read FBFinally Write FBFinally;
Property Ident : TJSString Read FIdent Write FIDent;
end;
TJSTryCatchFinallyStatement = Class(TJSTryStatement);
TJSTryCatchStatement = Class(TJSTryStatement);
TJSTryFinallyStatement = Class(TJSTryStatement);
{ TJSFunction }
TJSFunctionDeclarationStatement = Class(TJSelement)
private
FFuncDef: TJSFuncDef;
Public
Destructor Destroy; override;
Property AFunction : TJSFuncDef Read FFuncDef Write FFuncDef;
end;
{ TJSFunctionBody }
TJSFunctionBody = Class(TJSUnary)
private
FisProgram: Boolean;
Public
Property isProgram : Boolean Read FisProgram Write FIsProgram;
end;
{ TJSElementNode }
TJSElementNode = Class(TCollectionItem)
private
FNode: TJSElement;
Public
Destructor Destroy; override;
Property Node : TJSElement Read FNode Write FNode;
end;
{ TJSElementNodes }
TJSElementNodes = Class(TCollection)
private
function GetN(AIndex : Integer): TJSElementNode;
procedure SetN(AIndex : Integer; const AValue: TJSElementNode);
Public
Function AddNode : TJSElementNode;
Property Nodes[AIndex : Integer] : TJSElementNode Read GetN Write SetN; default;
end;
{ TJSSourceElements }
TJSSourceElements = Class(TJSElement)
private
FFunctions: TJSElementNodes;
FStatements: TJSElementNodes;
FVars: TJSElementNodes;
Public
Constructor Create(ALine,ARow : Integer; const ASource : String = ''); override;
Destructor Destroy; override;
Property Statements : TJSElementNodes Read FStatements;
Property functions : TJSElementNodes Read FFunctions;
Property Vars : TJSElementNodes Read FVars;
end;
implementation
{ TJSElement }
constructor TJSElement.Create(ALine, ARow: Integer; Const ASource: String = '');
begin
FLine:=ALine;
FRow:=ARow;
FSource:=ASource;
end;
{ TJSRegularExpressionLiteral }
function TJSRegularExpressionLiteral.GetA(AIndex : integer): TJSValue;
begin
Result:=FArgv[AIndex];
end;
procedure TJSRegularExpressionLiteral.SetA(AIndex : integer;
const AValue: TJSValue);
begin
FArgv[AIndex]:=Avalue;
end;
constructor TJSRegularExpressionLiteral.Create(ALine, ARow: Integer;
const ASource: String);
begin
inherited Create(ALine, ARow, ASource);
FPattern:=TJSValue.Create;
FPatternFlags:=TJSValue.Create;
end;
destructor TJSRegularExpressionLiteral.Destroy;
begin
FreeAndNil(FPattern);
FreeAndNil(FPatternFlags);
Inherited Destroy;
end;
{ TJSArrayLiteralElements }
function TJSArrayLiteralElements.GetE(AIndex : Integer): TJSArrayLiteralElement;
begin
Result:=TJSArrayLiteralElement(Items[AIndex]);
end;
procedure TJSArrayLiteralElements.SetE(AIndex : Integer;
const AValue: TJSArrayLiteralElement);
begin
Items[AIndex]:=AValue;
end;
function TJSArrayLiteralElements.AddElement: TJSArrayLiteralElement;
begin
Result:=TJSArrayLiteralElement(Add);
end;
{ TJSArrayLiteral }
constructor TJSArrayLiteral.Create(ALine, ARow: Integer; Const ASource: String = '');
begin
inherited Create(ALine, ARow, ASource);
FElements:=TJSArrayLiteralElements.Create(TJSArrayLiteralElement);
end;
destructor TJSArrayLiteral.Destroy;
begin
FreeAndNil(FElements);
inherited Destroy;
end;
{ TJSObjectLiteralElements }
function TJSObjectLiteralElements.GetE(AIndex : Integer
): TJSObjectLiteralElement;
begin
Result:=TJSObjectLiteralElement(Items[AIndex]);
end;
procedure TJSObjectLiteralElements.SetE(AIndex : Integer;
const AValue: TJSObjectLiteralElement);
begin
Items[AIndex]:=AValue;
end;
function TJSObjectLiteralElements.AddElement: TJSObjectLiteralElement;
begin
Result:=TJSObjectLiteralElement(Add);
end;
{ TJSObjectLiteral }
constructor TJSObjectLiteral.Create(ALine, ARow: Integer; const ASource: String = '');
begin
inherited Create(ALine, ARow, ASource);
FElements:=TJSObjectLiteralElements.Create(TJSObjectLiteralElement);
end;
destructor TJSObjectLiteral.Destroy;
begin
FreeAndNil(FElements);
inherited Destroy;
end;
{ TJSObjectLiteralElement }
destructor TJSObjectLiteralElement.Destroy;
begin
FreeAndNil(Fexpr);
inherited Destroy;
end;
{ TJSArrayLiteralElement }
destructor TJSArrayLiteralElement.Destroy;
begin
FreeAndNil(Fexpr);
inherited Destroy;
end;
{ TJSNewMemberExpression }
destructor TJSNewMemberExpression.Destroy;
begin
FreeAndNil(FArgs);
inherited Destroy;
end;
{ TJSMemberExpression }
destructor TJSMemberExpression.Destroy;
begin
FreeAndNil(FMExpr);
inherited Destroy;
end;
{ TJSCallExpression }
destructor TJSCallExpression.Destroy;
begin
FreeAndNil(FExpr);
FreeAndNil(FArgs);
inherited Destroy;
end;
{ TJSUnary }
destructor TJSUnary.Destroy;
begin
FreeAndNil(FA);
inherited Destroy;
end;
{ TJSBinary }
destructor TJSBinary.Destroy;
begin
FreeAndNil(FB);
FreeAndNil(FA);
inherited Destroy;
end;
{ TJSConditionalExpression }
destructor TJSConditionalExpression.Destroy;
begin
FreeAndNil(FB);
FreeAndNil(FA);
FreeAndNil(FC);
inherited Destroy;
end;
{ TJSAssign }
destructor TJSAssignStatement.Destroy;
begin
FreeAndNil(FLHS);
FreeAndNil(FExpr);
inherited Destroy;
end;
{ TJSVarDeclaration }
destructor TJSVarDeclaration.Destroy;
begin
FreeAndNil(FInit);
inherited Destroy;
end;
{ TJSIfStatement }
destructor TJSIfStatement.Destroy;
begin
FreeAndNil(FCond);
FreeAndNil(FBTrue);
FreeAndNil(FBFalse);
inherited Destroy;
end;
{ TJSBodyStatement }
destructor TJSBodyStatement.Destroy;
begin
FreeAndNil(FBody);
inherited Destroy;
end;
{ TJSCondLoopStatement }
destructor TJSCondLoopStatement.Destroy;
begin
FreeAndNil(FCond);
inherited Destroy;
end;
{ TJSForStatement }
destructor TJSForStatement.Destroy;
begin
FreeAndNil(FIncr);
FreeAndNil(FInit);
inherited Destroy;
end;
{ TJSForInStatement }
destructor TJSForInStatement.Destroy;
begin
FreeAndNil(FList);
FreeAndNil(FLHS);
inherited Destroy;
end;
{ TJSReturn }
destructor TJSReturnStatement.Destroy;
begin
FreeAndNil(FExpr);
inherited Destroy;
end;
{ TJSCaseElement }
destructor TJSCaseElement.Destroy;
begin
FreeAndNil(FExpr);
FreeAndNil(FBody);
inherited Destroy;
end;
{ TJSSwitch }
constructor TJSSwitchStatement.Create(ALine, ARow: Integer; const ASource: String);
begin
inherited Create(ALine, ARow, ASource);
FCases:=TJSCaseElements.Create(TJSCaseElement);
end;
destructor TJSSwitchStatement.Destroy;
begin
FreeAndNil(FCases);
FreeAndNil(FDefault);
FreeAndNil(FCond);
inherited Destroy;
end;
{ TJSCaseElements }
function TJSCaseElements.GetC(AIndex : Integer): TJSCaseElement;
begin
Result:=TJSCaseElement(Items[AIndex]);
end;
procedure TJSCaseElements.SetC(AIndex : Integer; const AValue: TJSCaseElement);
begin
Items[AIndex]:=AValue;
end;
function TJSCaseElements.AddCase: TJSCaseElement;
begin
Result:=TJSCaseElement(Add);
end;
{ TJSTryStatement }
destructor TJSTryStatement.Destroy;
begin
FreeAndNil(FBlock);
FreeAndNil(FBCatch);
FreeAndNil(FBFinally);
inherited Destroy;
end;
{ TJSSourceElements }
constructor TJSSourceElements.Create(ALine, ARow: Integer; const ASource: String
);
begin
inherited Create(ALine, ARow, ASource);
FStatements:=TJSElementNodes.Create(TJSElementNode);
FFunctions:=TJSElementNodes.Create(TJSElementNode);
FVars:=TJSElementNodes.Create(TJSElementNode);
end;
destructor TJSSourceElements.Destroy;
begin
FreeAndNil(FStatements);
FreeAndNil(FFunctions);
FreeAndNil(FVars);
inherited Destroy;
end;
{ TJSElementNodes }
function TJSElementNodes.GetN(AIndex : Integer): TJSElementNode;
begin
Result:=TJSElementNode(Items[Aindex])
end;
procedure TJSElementNodes.SetN(AIndex : Integer; const AValue: TJSElementNode);
begin
Items[AIndex]:=Avalue;
end;
function TJSElementNodes.AddNode: TJSElementNode;
begin
Result:=TJSElementNode(Add);
end;
{ TJSFunction }
destructor TJSFunctionDeclarationStatement.Destroy;
begin
FreeAndNil(FFuncDef);
inherited Destroy;
end;
{ TJSElementNode }
destructor TJSElementNode.Destroy;
begin
//FreeAndNil(FNode);
inherited Destroy;
end;
{ TJSFuncDef }
procedure TJSFuncDef.SetParams(const AValue: TStrings);
begin
if FParams=AValue then exit;
FParams.Assign(AValue);
end;
constructor TJSFuncDef.Create;
begin
FParams:=TStringList.Create;
end;
destructor TJSFuncDef.Destroy;
begin
FreeAndNil(FParams);
inherited Destroy;
end;
{ TJSBracketMemberExpression }
destructor TJSBracketMemberExpression.Destroy;
begin
FreeAndNil(FName);
inherited Destroy;
end;
{ TJSLiteral }
constructor TJSLiteral.Create(ALine, ARow: Integer; const ASource: String);
begin
FValue:=TJSValue.Create;
inherited Create(ALine, ARow, ASource);
end;
destructor TJSLiteral.Destroy;
begin
FreeAndNil(FValue);
Inherited;
end;
end.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,983 @@
unit tcscanner;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Typinfo, fpcunit, testutils, testregistry, jsscanner;
type
{ TTestLineReader }
TTestLineReader = Class(TTestCase)
Private
FData: TStringStream;
FReader : TStreamLineReader;
protected
Procedure CreateReader(AInput : String);
procedure TearDown; override;
published
Procedure TestEmpty;
Procedure TestReadLine;
Procedure TestReadLines13;
Procedure TestReadLines10;
Procedure TestReadLines1310;
procedure TestReadLinesEOF13;
procedure TestReadLinesEOF10;
procedure TestReadLinesEOF1310;
procedure TestReadEmptyLines101010;
end;
{ TTestJSScanner }
TTestJSScanner = class(TTestCase)
Private
FStream : TStream;
FLineReader : TLineReader;
FScanner : TJSScanner;
FErrorSource : String;
procedure AssertEquals(AMessage: String; AExpected, AActual : TJSToken); overload;
procedure CheckToken(AToken: TJSToken; ASource: String);
procedure CheckTokens(ASource: String; ATokens: array of TJSToken);
procedure DoTestFloat(F: Double);
procedure DoTestFloat(F: Double; S: String);
procedure DoTestString(S: String);
procedure TestErrorSource;
protected
Function CreateScanner(AInput : String) : TJSScanner;
procedure FreeScanner;
procedure SetUp; override;
procedure TearDown; override;
Property Scanner : TJSScanner Read FScanner;
published
Procedure TestEmpty;
procedure TestAndAnd;
procedure TestAndEq;
procedure TestAssign;
procedure TestBraceClose;
procedure TestBraceOpen;
procedure TestColon;
procedure TestComma;
procedure TestCurlyBraceClose;
procedure TestCurlyBraceOpen;
procedure TestDiv;
procedure TestDiveq;
procedure TestXor;
procedure TestXoreq;
procedure TestDot;
procedure TestEq;
procedure TestGE;
procedure TestFalse;
procedure TestInv;
procedure TestNot;
procedure TestString;
procedure TestTrue;
procedure TestGreaterThan;
procedure TestLE;
procedure TestLessThan;
procedure TestLSHIFT;
procedure TestLSHIFTEQ;
procedure TestMinus;
procedure TestMinusEQ;
procedure TestMinusMinus;
procedure TestModeq;
procedure TestMul;
procedure TestNE;
procedure TestNSE;
procedure TestOREQ;
procedure TestOROR;
procedure TestPlus;
procedure TestPlusEq;
procedure TestPlusPlus;
procedure TestRShift;
procedure TestRShiftEq;
procedure TestSemicolon;
procedure TestSEq;
procedure TestSquaredBraceClose;
procedure TestSquaredBraceOpen;
procedure TestStarEq;
procedure TestURShift;
procedure TestURShiftEq;
procedure TestBreak;
procedure TestCase;
procedure TestCatch;
procedure TestContinue;
procedure TestDefault;
procedure TestDelete;
procedure TestDO;
procedure TestElse;
procedure TestFinally;
procedure TestFor;
procedure TestFunction;
procedure TestIf;
procedure TestIn;
procedure TestInstanceOf;
procedure TestNew;
procedure TestReturn;
procedure TestSwitch;
procedure TestThis;
procedure TestThrow;
procedure TestTry;
procedure TestTypeOf;
procedure TestVar;
procedure TestVoid;
procedure TestWhile;
procedure TestWith;
Procedure Test2Words;
procedure Test3Words;
procedure TestIdentifier;
procedure TestIdentifier2;
procedure TestIdentifier3;
procedure TestIdentifier4;
procedure TestIdentifier5;
procedure TestIdentifierDotIdentifier;
procedure TestEOLN;
procedure TestEOLN2;
procedure TestEOLN3;
procedure TestEOLN4;
procedure TestComment1;
procedure TestComment2;
procedure TestComment3;
procedure TestComment4;
procedure TestComment5;
procedure TestComment6;
procedure TestFloat;
procedure TestStringError;
procedure TestFloatError;
end;
implementation
Function TTestJSScanner.CreateScanner(AInput : String) : TJSScanner;
begin
FStream:=TStringStream.Create(AInput);
FLineReader:=TStreamLineReader.Create(Fstream);
FScanner:=TJSScanner.Create(FLineReader);
end;
procedure TTestJSScanner.FreeScanner;
begin
FreeAndNil(FScanner);
FreeAndNil(FLineReader);
FreeAndNil(FStream);
end;
procedure TTestJSScanner.SetUp;
begin
inherited SetUp;
end;
procedure TTestJSScanner.TestEmpty;
Var
J : TJSToken;
begin
CreateScanner('');
J:=Scanner.FetchToken;
If (J<>tjsEOF) then
Fail('Empty returns EOF');
end;
procedure TTestJSScanner.AssertEquals(AMessage : String; AExpected, AActual: TJSToken);
Var
J : TJSToken;
S,EN1,EN2 : String;
begin
If (AActual<>AExpected) then
begin
EN1:=GetEnumName(TypeINfo(TJSToken),Ord(AExpected));
EN2:=GetEnumName(TypeINfo(TJSToken),Ord(AActual));
S:=Format('%s : %s <> %s',[AMessage,EN1,EN2]);
Fail(S);
end;
end;
procedure TTestJSScanner.CheckToken(AToken : TJSToken; ASource : String);
Var
J : TJSToken;
EN2 : String;
begin
CreateScanner(ASource);
J:=Scanner.FetchToken;
EN2:=GetEnumName(TypeINfo(TJSToken),Ord(AToken));
AssertEquals(Format('Source %s should result in %s.',[ASource,EN2]),AToken,J);
end;
procedure TTestJSScanner.TestAndAnd;
begin
CheckToken(tjsAndAnd,'&&');
end;
procedure TTestJSScanner.TestAndEq;
begin
CheckToken(tjsAndEq,'&=');
end;
procedure TTestJSScanner.TestBraceOpen;
begin
CheckToken(tjsBraceOpen,'(');
end;
procedure TTestJSScanner.TestBraceClose;
begin
CheckToken(tjsBraceClose,')');
end;
procedure TTestJSScanner.TestSquaredBraceClose;
begin
CheckToken(tjsSquaredBraceClose,']');
end;
procedure TTestJSScanner.TestSquaredBraceOpen;
begin
CheckToken(tjssQuaredBraceOpen,'[');
end;
procedure TTestJSScanner.TestCurlyBraceOpen;
begin
CheckToken(tjsCurlyBraceOpen,'{');
end;
procedure TTestJSScanner.TestCurlyBraceClose;
begin
CheckToken(tjsCurlyBraceClose,'}');
end;
procedure TTestJSScanner.TestComma;
begin
CheckToken(tjsComma,',');
end;
procedure TTestJSScanner.TestColon;
begin
CheckToken(tjsColon,':');
end;
procedure TTestJSScanner.TestDot;
begin
CheckToken(tjsDot,'.');
end;
procedure TTestJSScanner.TestSemicolon;
begin
CheckToken(tjsSemicolon,';');
end;
procedure TTestJSScanner.TestAssign;
begin
CheckToken(tjsAssign,'=');
end;
procedure TTestJSScanner.TestGreaterThan;
begin
CheckToken(tjsGT,'>');
end;
procedure TTestJSScanner.TestLessThan;
begin
CheckToken(tjsLT,'<');
end;
procedure TTestJSScanner.TestPlus;
begin
CheckToken(tjsPlus,'+');
end;
procedure TTestJSScanner.TestMinus;
begin
CheckToken(tjsMinus,'-');
end;
procedure TTestJSScanner.TestMul;
begin
CheckToken(tjsMul,'*');
end;
procedure TTestJSScanner.TestDiv;
begin
CheckToken(tjsDiv,'/');
end;
procedure TTestJSScanner.TestEq;
begin
CheckToken(tjsEq,'==');
end;
procedure TTestJSScanner.TestGE;
begin
CheckToken(tjsGE,'>=');
end;
procedure TTestJSScanner.TestLE;
begin
CheckToken(tjsLE,'<=');
end;
procedure TTestJSScanner.TestLSHIFT;
begin
CheckToken(tjsLShift,'<<');
end;
procedure TTestJSScanner.TestLSHIFTEQ;
begin
CheckToken(tjsLShiftEq,'<<=');
end;
procedure TTestJSScanner.TestMinusEQ;
begin
CheckToken(tjsMinusEq,'-=');
end;
procedure TTestJSScanner.TestMinusMinus;
begin
CheckToken(tjsMinusMinus,'--');
end;
procedure TTestJSScanner.TestModeq;
begin
CheckToken(tjsModeq,'%=');
end;
procedure TTestJSScanner.TestDiveq;
begin
CheckToken(tjsDiveq,'/=');
end;
procedure TTestJSScanner.TestXor;
begin
CheckToken(tjsXOR,'^');
end;
procedure TTestJSScanner.TestXoreq;
begin
CheckToken(tjsXOREQ,'^=');
end;
procedure TTestJSScanner.TestNE;
begin
CheckToken(tjsNE,'!=');
end;
procedure TTestJSScanner.TestInv;
begin
CheckToken(tjsInv,'~');
end;
procedure TTestJSScanner.TestNot;
begin
CheckToken(tjsNot,'!');
end;
procedure TTestJSScanner.TestTrue;
begin
CheckToken(tjsTrue,'true');
end;
procedure TTestJSScanner.TestFalse;
begin
CheckToken(tjsFalse,'false');
end;
procedure TTestJSScanner.TestOREQ;
begin
CheckToken(tjsOREQ,'|=');
end;
procedure TTestJSScanner.TestOROR;
begin
CheckToken(tjsOROR,'||');
end;
procedure TTestJSScanner.TestPlusEq;
begin
CheckToken(tjsPlusEq,'+=');
end;
procedure TTestJSScanner.TestPlusPlus;
begin
CheckToken(tjsPlusPlus,'++');
end;
procedure TTestJSScanner.TestURShift;
begin
CheckToken(tjsURSHIFT,'>>>');
end;
procedure TTestJSScanner.TestURShiftEq;
begin
CheckToken(tjsURSHIFTEQ,'>>>=');
end;
procedure TTestJSScanner.TestRShift;
begin
CheckToken(tjsRSHIFT,'>>');
end;
procedure TTestJSScanner.TestRShiftEq;
begin
CheckToken(tjsRSHIFTEQ,'>>=');
end;
procedure TTestJSScanner.TestSEq;
begin
CheckToken(tjsSEQ,'===');
end;
procedure TTestJSScanner.TestNSE;
begin
CheckToken(tjsSNE,'!==');
end;
procedure TTestJSScanner.TestStarEq;
begin
CheckToken(tjsMulEq,'*=');
end;
procedure TTestJSScanner.TestBreak;
begin
CheckToken(tjsBreak,'break');
end;
procedure TTestJSScanner.TestCase;
begin
CheckToken(tjscase,'case');
end;
procedure TTestJSScanner.TestCatch;
begin
CheckToken(tjscatch,'catch');
end;
procedure TTestJSScanner.TestContinue;
begin
CheckToken(tjscontinue,'continue');
end;
procedure TTestJSScanner.TestDefault;
begin
CheckToken(tjsdefault,'default');
end;
procedure TTestJSScanner.TestDelete;
begin
CheckToken(tjsdelete,'delete');
end;
procedure TTestJSScanner.TestDO;
begin
CheckToken(tjsdo,'do');
end;
procedure TTestJSScanner.TestElse;
begin
CheckToken(tjselse,'else');
end;
procedure TTestJSScanner.TestFinally;
begin
CheckToken(tjsfinally,'finally');
end;
procedure TTestJSScanner.TestFor;
begin
CheckToken(tjsfor,'for');
end;
procedure TTestJSScanner.TestFunction;
begin
CheckToken(tjsfunction,'function');
end;
procedure TTestJSScanner.TestIf;
begin
CheckToken(tjsif,'if');
end;
procedure TTestJSScanner.TestIn;
begin
CheckToken(tjsin,'in');
end;
procedure TTestJSScanner.TestInstanceOf;
begin
CheckToken(tjsinstanceof,'instanceof');
end;
procedure TTestJSScanner.TestNew;
begin
CheckToken(tjsnew,'new');
end;
procedure TTestJSScanner.TestReturn;
begin
CheckToken(tjsreturn,'return');
end;
procedure TTestJSScanner.TestSwitch;
begin
CheckToken(tjsswitch,'switch');
end;
procedure TTestJSScanner.TestThis;
begin
CheckToken(tjsThis,'this');
end;
procedure TTestJSScanner.TestThrow;
begin
CheckToken(tjsThrow,'throw');
end;
procedure TTestJSScanner.TestTry;
begin
CheckToken(tjsTry,'try');
end;
procedure TTestJSScanner.TestTypeOf;
begin
CheckToken(tjstypeof,'typeof');
end;
procedure TTestJSScanner.TestVar;
begin
CheckToken(tjsvar,'var');
end;
procedure TTestJSScanner.TestVoid;
begin
CheckToken(tjsvoid,'void');
end;
procedure TTestJSScanner.TestWhile;
begin
CheckToken(tjswhile,'while');
end;
procedure TTestJSScanner.TestWith;
begin
CheckToken(tjswith,'with');
end;
procedure TTestJSScanner.CheckTokens(ASource : String; ATokens : Array of TJSToken);
Var
I : Integer;
J : TJSToken;
S : String;
begin
CreateScanner(ASource);
For I:=Low(ATokens) to High(ATokens) do
begin
J:=FScanner.FetchToken;
S:=GetEnumName(TypeINfo(TJSToken),Ord(ATokens[i]));
S:=Format('Source "%s", token %d (%s): expected %s',[ASource,I,FScanner.CurTokenString,S]);
AssertEquals(S,ATokens[i],J);
end;
end;
procedure TTestJSScanner.Test2Words;
begin
CheckTokens('with do',[tjsWith,tjsDo]);
end;
procedure TTestJSScanner.Test3Words;
begin
CheckTokens('with do for',[tjsWith,tjsDo,tjsFor]);
end;
procedure TTestJSScanner.TestIdentifier;
begin
CheckToken(tjsIdentifier,'something');
AssertEquals('Correct identifier','something',FScanner.CurTokenString);
end;
procedure TTestJSScanner.TestIdentifier2;
begin
CheckToken(tjsIdentifier,'_something');
AssertEquals('Correct identifier','_something',FScanner.CurTokenString);
end;
procedure TTestJSScanner.TestIdentifier3;
begin
CheckToken(tjsIdentifier,'$');
AssertEquals('Correct identifier','$',FScanner.CurTokenString);
end;
procedure TTestJSScanner.TestIdentifier4;
begin
CheckToken(tjsIdentifier,'_0');
AssertEquals('Correct identifier','_0',FScanner.CurTokenString);
end;
procedure TTestJSScanner.TestIdentifier5;
begin
CheckToken(tjsIdentifier,'$0');
AssertEquals('Correct identifier','$0',FScanner.CurTokenString);
end;
procedure TTestJSScanner.TestIdentifierDotIdentifier;
begin
CheckTokens('something.different',[tjsIdentifier,tjsdot,tjsIdentifier]);
// AssertEquals('Correct identifier','something',FScanner.CurTokenString);
end;
procedure TTestJSScanner.TestEOLN;
begin
CreateScanner('something');
FScanner.FetchToken;
AssertEquals('Got to end of line after reading single token at EOF',True,FScanner.IsEndOfLine);
// AssertEquals('Correct identifier','something',FScanner.CurTokenString);
end;
procedure TTestJSScanner.TestEOLN2;
begin
CreateScanner('something different');
FScanner.FetchToken;
AssertEquals('Not yet end of line after reading single token at EOF',False,FScanner.IsEndOfLine);
end;
procedure TTestJSScanner.TestEOLN3;
begin
CreateScanner('something'#13#10'different');
FScanner.FetchToken;
AssertEquals('End of line after reading single token',True,FScanner.IsEndOfLine);
end;
procedure TTestJSScanner.TestEOLN4;
begin
CreateScanner('something'#10'different');
FScanner.FetchToken;
AssertEquals('End of line after reading first token',True,FScanner.IsEndOfLine);
FScanner.FetchToken;
AssertEquals('End of line after reading second token',True,FScanner.IsEndOfLine);
end;
procedure TTestJSScanner.TestComment1;
begin
CreateScanner('// some comment string');
AssertEquals('Comment line is skipped',tjsEOF,FScanner.FetchToken);
end;
procedure TTestJSScanner.TestComment2;
begin
CreateScanner('// some comment string');
FScanner.ReturnComments:=True;
AssertEquals('Comment line is returned',tjsComment,FScanner.FetchToken);
AssertEquals('Comment contents is returned',' some comment string',FScanner.CurTokenString);
end;
procedure TTestJSScanner.TestComment3;
begin
CreateScanner('/* some comment string */');
AssertEquals('Comment line is skipped',tjsEOF,FScanner.FetchToken);
end;
procedure TTestJSScanner.TestComment4;
begin
CreateScanner('/* some comment string */');
FScanner.ReturnComments:=True;
AssertEquals('Comment line is returned',tjsComment,FScanner.FetchToken);
AssertEquals('Comment contents is returned',' some comment string ',FScanner.CurTokenString);
end;
procedure TTestJSScanner.TestComment5;
begin
CreateScanner('/* some nested comment // string */');
FScanner.ReturnComments:=True;
AssertEquals('Comment line is returned',tjsComment,FScanner.FetchToken);
AssertEquals('Comment contents is returned',' some nested comment // string ',FScanner.CurTokenString);
end;
procedure TTestJSScanner.TestComment6;
begin
CreateScanner('// /* some nested comment string */');
FScanner.ReturnComments:=True;
AssertEquals('Comment line is returned',tjsComment,FScanner.FetchToken);
AssertEquals('Comment contents is returned',' /* some nested comment string */',FScanner.CurTokenString);
end;
procedure TTestJSScanner.TearDown;
begin
FreeScanner;
end;
procedure TTestJSScanner.DoTestFloat(F : Double);
Var
S : String;
begin
Str(F,S);
DoTestFloat(F,S);
end;
procedure TTestJSScanner.DoTestFloat(F : Double; S : String);
Var
J : TJSToken;
C : Double;
I : integer;
V : String;
begin
CreateScanner(S);
J:=FScanner.FetchToken;
AssertEquals(S+' is a number',tjsNumber,J);
V:=FScanner.CurTokenString;
If (Copy(V,1,2)='0x') then
begin
Flush(output);
V:='$'+Copy(V,3,Length(V)-2);
C:=StrToInt(V);
end
else
begin
Val(V,C,I);
If (I<>0) then
Fail(FScanner.CurTokenString+' does not contain a float value');
end;
AssertEquals('Parsed float equals original float',F,C);
end;
procedure TTestJSScanner.TestFloat;
begin
DoTestFloat(1.2);
DoTestFloat(-1.2);
DoTestFloat(0);
DoTestFloat(1.2e1);
DoTestFloat(-1.2e1);
DoTestFloat(0);
DoTestFloat(1.2,'1.2');
DoTestFloat(-1.2,'-1.2');
DoTestFloat(0,'0.0');
DoTestFloat(255,'0xff')
end;
procedure TTestJSScanner.TestFloatError;
begin
FErrorSource:='1xz';
AssertException('Wrong float',EJSScannerError,@TestErrorSource);
end;
procedure TTestJSScanner.DoTestString(S: String);
Var
J : TJSToken;
T : String;
begin
CreateScanner(S);
J:=FScanner.FetchToken;
AssertEquals(S+' is a string',tjsString,J);
If (Length(S)>0) and (S[1] in ['"','''']) then
S:=Copy(S,2,Length(S)-2);
AssertEquals('Correct string is returned',S,FScanner.CurTokenString);
end;
procedure TTestJSScanner.TestString;
begin
DoTestString('"A string"');
DoTestString('""');
DoTestString('''''');
DoTestString('''A string''');
end;
procedure TTestJSScanner.TestErrorSource;
begin
CreateScanner(FErrorSource);
While (FScanner.FetchToken<>tjsEOF) do ;
end;
procedure TTestJSScanner.TestStringError;
begin
FErrorSource:='"A string';
AssertException('Unterminated string',EJSScannerError,@TestErrorSource);
FErrorSource:='''A string';
AssertException('Unterminated string',EJSScannerError,@TestErrorSource);
end;
{ TTestLineReader }
procedure TTestLineReader.CreateReader(AInput: String);
begin
FData:=TStringStream.Create(AInput);
FReader:=TStreamLineReader.Create(FData);
end;
procedure TTestLineReader.TearDown;
begin
FreeAndNil(FReader);
FreeAndNil(FData);
end;
procedure TTestLineReader.TestEmpty;
begin
CreateReader('');
AssertEquals('Empty reader returns EOF',True,FReader.IsEOF);
AssertEquals('Empty reader returns empty string','',FReader.ReadLine);
end;
procedure TTestLineReader.TestReadLine;
begin
CreateReader('Something');
AssertEquals('Reader with 1 line returns 1 line','Something',FReader.ReadLine);
AssertEquals('EOF true after reading line',True,FReader.IsEOF);
end;
procedure TTestLineReader.TestReadLines13;
begin
CreateReader('Something'#13'else');
AssertEquals('Reader with 2 lines returns 1st line','Something',FReader.ReadLine);
AssertEquals('Reader with 2 lines returns 2nd line','else',FReader.ReadLine);
AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
end;
procedure TTestLineReader.TestReadLines10;
begin
CreateReader('Something'#10'else');
AssertEquals('Reader with 2 lines returns 1st line','Something',FReader.ReadLine);
AssertEquals('Reader with 2 lines returns 2nd line','else',FReader.ReadLine);
AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
end;
procedure TTestLineReader.TestReadLines1310;
begin
CreateReader('Something'#13#10'else');
AssertEquals('Reader with 2 lines returns 1st line','Something',FReader.ReadLine);
AssertEquals('Reader with 2 lines returns 2nd line','else',FReader.ReadLine);
AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
end;
procedure TTestLineReader.TestReadLinesEOF13;
begin
CreateReader('Something'#13);
AssertEquals('Reader with 2 lines + CR returns 1st line','Something',FReader.ReadLine);
AssertEquals('Reader with 1 lines + CR returns empty 2nd line','',FReader.ReadLine);
AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
end;
procedure TTestLineReader.TestReadLinesEOF10;
begin
CreateReader('Something'#10);
AssertEquals('Reader with 2 lines + LF returns 1st line','Something',FReader.ReadLine);
AssertEquals('Reader with 1 lines + LF returns empty 2nd line','',FReader.ReadLine);
AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
end;
procedure TTestLineReader.TestReadLinesEOF1310;
begin
CreateReader('Something'#13#10);
AssertEquals('Reader with 2 lines + CRLF returns 1st line','Something',FReader.ReadLine);
AssertEquals('Reader with 1 lines + CRLF returns empty 2nd line','',FReader.ReadLine);
AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
end;
procedure TTestLineReader.TestReadEmptyLines101010;
begin
CreateReader('Something'#10#10#10);
AssertEquals('Reader with 1 line + LFLFLF returns 1st line','Something',FReader.ReadLine);
AssertEquals('EOF false after reading line 1',False,FReader.IsEOF);
AssertEquals('Reader with 1 line + LFLFLF returns empty 2nd line','',FReader.ReadLine);
AssertEquals('EOF false after reading line 2',False,FReader.IsEOF);
AssertEquals('Reader with 1 line + LFLFLF returns empty 3nd line','',FReader.ReadLine);
AssertEquals('EOF true after reading lines',True,FReader.IsEOF);
end;
initialization
RegisterTests([TTestLineReader,TTestJSScanner]);
end.

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

View File

@ -0,0 +1,261 @@
<?xml version="1.0"?>
<CONFIG>
<ProjectOptions>
<Version Value="7"/>
<General>
<MainUnit Value="0"/>
<TargetFileExt Value=""/>
<Icon Value="0"/>
<UseXPManifest Value="True"/>
<ActiveEditorIndexAtStart Value="1"/>
</General>
<VersionInfo>
<ProjectVersion Value=""/>
<Language Value=""/>
<CharSet Value=""/>
</VersionInfo>
<PublishOptions>
<Version Value="2"/>
<IgnoreBinaries Value="False"/>
<IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
<ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
<CommandLineParams Value="-a --format=plain"/>
<LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
</local>
</RunParams>
<RequiredPackages Count="2">
<Item1>
<PackageName Value="FPCUnitConsoleRunner"/>
</Item1>
<Item2>
<PackageName Value="FCL"/>
</Item2>
</RequiredPackages>
<Units Count="7">
<Unit0>
<Filename Value="testjs.lpr"/>
<IsPartOfProject Value="True"/>
<UnitName Value="testjs"/>
<CursorPos X="11" Y="7"/>
<TopLine Value="1"/>
<EditorIndex Value="0"/>
<UsageCount Value="201"/>
<Loaded Value="True"/>
</Unit0>
<Unit1>
<Filename Value="tcscanner.pp"/>
<IsPartOfProject Value="True"/>
<UnitName Value="tcscanner"/>
<CursorPos X="27" Y="427"/>
<TopLine Value="416"/>
<EditorIndex Value="2"/>
<UsageCount Value="201"/>
<Loaded Value="True"/>
</Unit1>
<Unit2>
<Filename Value="../src/jsbase.pp"/>
<IsPartOfProject Value="True"/>
<UnitName Value="jsbase"/>
<CursorPos X="1" Y="153"/>
<TopLine Value="132"/>
<EditorIndex Value="1"/>
<UsageCount Value="196"/>
<Loaded Value="True"/>
</Unit2>
<Unit3>
<Filename Value="../src/jsparser.pp"/>
<IsPartOfProject Value="True"/>
<UnitName Value="jsparser"/>
<CursorPos X="1" Y="1"/>
<TopLine Value="1"/>
<UsageCount Value="201"/>
</Unit3>
<Unit4>
<Filename Value="../src/jsscanner.pp"/>
<IsPartOfProject Value="True"/>
<UnitName Value="JSScanner"/>
<CursorPos X="1" Y="1"/>
<TopLine Value="1"/>
<UsageCount Value="201"/>
</Unit4>
<Unit5>
<Filename Value="../src/jstree.pp"/>
<IsPartOfProject Value="True"/>
<UnitName Value="jstree"/>
<CursorPos X="1" Y="1"/>
<TopLine Value="1"/>
<UsageCount Value="200"/>
</Unit5>
<Unit6>
<Filename Value="tcparser.pp"/>
<IsPartOfProject Value="True"/>
<UnitName Value="tcparser"/>
<CursorPos X="3" Y="2059"/>
<TopLine Value="2051"/>
<EditorIndex Value="4"/>
<UsageCount Value="97"/>
<Loaded Value="True"/>
</Unit6>
</Units>
<JumpHistory Count="30" HistoryIndex="29">
<Position1>
<Filename Value="tcparser.pp"/>
<Caret Line="1752" Column="1" TopLine="1730"/>
</Position1>
<Position2>
<Filename Value="tcparser.pp"/>
<Caret Line="1767" Column="32" TopLine="1733"/>
</Position2>
<Position3>
<Filename Value="tcparser.pp"/>
<Caret Line="1790" Column="30" TopLine="1766"/>
</Position3>
<Position4>
<Filename Value="tcparser.pp"/>
<Caret Line="1763" Column="26" TopLine="1736"/>
</Position4>
<Position5>
<Filename Value="tcparser.pp"/>
<Caret Line="1791" Column="1" TopLine="1758"/>
</Position5>
<Position6>
<Filename Value="tcparser.pp"/>
<Caret Line="114" Column="30" TopLine="93"/>
</Position6>
<Position7>
<Filename Value="tcparser.pp"/>
<Caret Line="1806" Column="23" TopLine="1798"/>
</Position7>
<Position8>
<Filename Value="tcparser.pp"/>
<Caret Line="1828" Column="3" TopLine="1819"/>
</Position8>
<Position9>
<Filename Value="../../../../source/fcl-js/jsparser.pp"/>
<Caret Line="515" Column="28" TopLine="507"/>
</Position9>
<Position10>
<Filename Value="tcparser.pp"/>
<Caret Line="1824" Column="24" TopLine="1803"/>
</Position10>
<Position11>
<Filename Value="tcparser.pp"/>
<Caret Line="1828" Column="17" TopLine="1803"/>
</Position11>
<Position12>
<Filename Value="tcparser.pp"/>
<Caret Line="1849" Column="34" TopLine="1820"/>
</Position12>
<Position13>
<Filename Value="tcparser.pp"/>
<Caret Line="118" Column="27" TopLine="96"/>
</Position13>
<Position14>
<Filename Value="tcparser.pp"/>
<Caret Line="1900" Column="29" TopLine="1888"/>
</Position14>
<Position15>
<Filename Value="tcparser.pp"/>
<Caret Line="1898" Column="12" TopLine="1877"/>
</Position15>
<Position16>
<Filename Value="tcparser.pp"/>
<Caret Line="1904" Column="80" TopLine="1883"/>
</Position16>
<Position17>
<Filename Value="tcparser.pp"/>
<Caret Line="1914" Column="29" TopLine="1883"/>
</Position17>
<Position18>
<Filename Value="tcparser.pp"/>
<Caret Line="1948" Column="18" TopLine="1914"/>
</Position18>
<Position19>
<Filename Value="tcparser.pp"/>
<Caret Line="20" Column="1" TopLine="1"/>
</Position19>
<Position20>
<Filename Value="tcparser.pp"/>
<Caret Line="2165" Column="1" TopLine="2124"/>
</Position20>
<Position21>
<Filename Value="tcparser.pp"/>
<Caret Line="121" Column="27" TopLine="99"/>
</Position21>
<Position22>
<Filename Value="tcparser.pp"/>
<Caret Line="1969" Column="3" TopLine="1960"/>
</Position22>
<Position23>
<Filename Value="tcparser.pp"/>
<Caret Line="1971" Column="46" TopLine="1950"/>
</Position23>
<Position24>
<Filename Value="tcparser.pp"/>
<Caret Line="1986" Column="14" TopLine="1961"/>
</Position24>
<Position25>
<Filename Value="tcparser.pp"/>
<Caret Line="1993" Column="33" TopLine="1960"/>
</Position25>
<Position26>
<Filename Value="../../../../source/fcl-js/jsparser.pp"/>
<Caret Line="1702" Column="24" TopLine="1685"/>
</Position26>
<Position27>
<Filename Value="tcparser.pp"/>
<Caret Line="124" Column="25" TopLine="100"/>
</Position27>
<Position28>
<Filename Value="tcparser.pp"/>
<Caret Line="2038" Column="10" TopLine="2035"/>
</Position28>
<Position29>
<Filename Value="tcparser.pp"/>
<Caret Line="2075" Column="56" TopLine="2051"/>
</Position29>
<Position30>
<Filename Value="../../../../source/fcl-js/jsparser.pp"/>
<Caret Line="1747" Column="29" TopLine="1740"/>
</Position30>
</JumpHistory>
</ProjectOptions>
<CompilerOptions>
<Version Value="8"/>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)/"/>
<OtherUnitFiles Value="/home/michael/source/fcl-js/;../"/>
</SearchPaths>
<CodeGeneration>
<Optimizations>
<OptimizationLevel Value="0"/>
</Optimizations>
</CodeGeneration>
<Other>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>
<Debugging>
<BreakPoints Count="1">
<Item1>
<Source Value="../jsscanner.pp"/>
<Line Value="717"/>
</Item1>
</BreakPoints>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@ -0,0 +1,29 @@
program testjs;
{$mode objfpc}{$H+}
uses
Classes, consoletestrunner, tcscanner, jsparser, jsscanner, jstree, jsbase,
tcparser;
type
{ TLazTestRunner }
TMyTestRunner = class(TTestRunner)
protected
// override the protected methods of TTestRunner to customize its behavior
end;
var
Application: TMyTestRunner;
{$IFDEF WINDOWS}{$R testjs.rc}{$ENDIF}
begin
Application := TMyTestRunner.Create(nil);
Application.Initialize;
Application.Title := 'FPCUnit Console test runner';
Application.Run;
Application.Free;
end.

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="CompanyName.ProductName.YourApp" type="win32"/>
<description>Your application description here.</description>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

View File

@ -0,0 +1 @@
MAINICON ICON "testjs.ico"