* Patch from Mattias Gaertner:

jswriter: more compact try..catch

pasresolver:
- mark function calls without ()
- "with type do ;"
- constructor call store TPasType
- mark if a constructor call creates a new
  instance or is a normal call
- same for destructor
- fixed checking assign operator types
- more tests

fppas2js:
- convert implicit calls in Pascal to explicit calls in JS
- built in procedure "exit" and "exit(value)"
- if loopvar is used afterwards append  if($loopend>i)i--;
- classes
  - declare using createClass, needs rtl magic
  - constructor
  - destructor
  - vars
  - ancestor
  - virtual, override, abstract
  - "is" operator
  - "as" operator
  - call inherited "inherited;", "inherited funcname;"
- dynamic arrays
  - init as "arr = []"
  - SetLength(arr,newlength)
  - length(arr)
- try..except, on .. do, raise
- insert default values in calls

git-svn-id: trunk@35383 -
This commit is contained in:
michael 2017-02-04 11:26:59 +00:00
parent a9888eba70
commit 393b4caba2
11 changed files with 2907 additions and 772 deletions

View File

@ -1051,39 +1051,27 @@ begin
Indent; Indent;
WriteJS(El.Block); WriteJS(El.Block);
Undent; Undent;
If C then Write('}');
Write('} ')
else
begin
Writeln('}');
end;
If (El is TJSTryCatchFinallyStatement) or (El is TJSTryCatchStatement) then If (El is TJSTryCatchFinallyStatement) or (El is TJSTryCatchStatement) then
begin begin
Write('catch ('+El.Ident); Write(' catch');
if El.Ident<>'' then Write(' ('+El.Ident+')');
If C then If C then
Write(') {') Write(' {')
else else
Writeln(') {'); Writeln(' {');
FSkipBrackets:=True;
Indent; Indent;
WriteJS(El.BCatch); WriteJS(El.BCatch);
Undent; Undent;
If C then Write('}');
if (El is TJSTryCatchFinallyStatement) then
Write('} ')
else
Write('}')
else
begin
Writeln('');
Writeln('}');
end;
end; end;
If (El is TJSTryCatchFinallyStatement) or (El is TJSTryFinallyStatement) then If (El is TJSTryCatchFinallyStatement) or (El is TJSTryFinallyStatement) then
begin begin
If C then If C then
Write('finally {') Write(' finally {')
else else
Writeln('finally {'); Writeln(' finally {');
Indent; Indent;
FSkipBrackets:=True; FSkipBrackets:=True;
WriteJS(El.BFinally); WriteJS(El.BFinally);

View File

@ -57,7 +57,8 @@
- defaultexpr - defaultexpr
- is and as operator - is and as operator
- nil - nil
- constructor result type - constructor result type, rrfNewInstance
- destructor call type: rrfFreeInstance
- type cast - type cast
- class of - class of
- class method, property, var, const - class method, property, var, const
@ -93,8 +94,10 @@
- built-in functions high, low for range type and arrays - built-in functions high, low for range type and arrays
- procedure type - procedure type
- method type - method type
- function without params: mark if call or address, rrfImplicitCallWithoutParams
ToDo: ToDo:
- overloads
- char constant #0, #10, #13, UTF-8 char - char constant #0, #10, #13, UTF-8 char
- const TArrayValues - const TArrayValues
- classes - TPasClassType - classes - TPasClassType
@ -102,6 +105,7 @@
- nested types - nested types
- check if constant is longint or int64 - check if constant is longint or int64
- for..in..do - for..in..do
- class forward and pointer type must check type section before other scopes
- pointer TPasPointerType - pointer TPasPointerType
- records - TPasRecordType, - records - TPasRecordType,
- variant - TPasVariant - variant - TPasVariant
@ -127,6 +131,20 @@
Debug flags: -d<x> Debug flags: -d<x>
VerbosePasResolver VerbosePasResolver
Notes:
Functions and function types without parameters:
property P read f; // use function f, not its result
f. // implicit resolve f once if param less function or function type
f[] // implicit resolve f once if a param less function or function type
@f; use function f, not its result
@p.f; @ operator applies to f, not p
@f(); @ operator applies to result of f
f(); use f's result
FuncVar:=Func; if mode=objfpc: incompatible
if mode=delphi: implicit addr of function f, not yet implemented
if f=g then : can implicit resolve each side once, at the moment: always implicit
p(f), f as var parameter: always implicit, thus incompatible
} }
unit PasResolver; unit PasResolver;
@ -429,11 +447,11 @@ type
procedure SetElement(AValue: TPasElement); procedure SetElement(AValue: TPasElement);
public public
Owner: TObject; // e.g. a TPasResolver Owner: TObject; // e.g. a TPasResolver
Next: TResolveData; Next: TResolveData; // TPasResolver uses this for its memory chain
CustomData: TObject; CustomData: TObject; // not used by TPasResolver, free for your extension
constructor Create; virtual; constructor Create; virtual;
destructor Destroy; override; destructor Destroy; override;
property Element: TPasElement read FElement write SetElement; property Element: TPasElement read FElement write SetElement;// Element.CustomData=Self
end; end;
TResolveDataClass = class of TResolveData; TResolveDataClass = class of TResolveData;
@ -621,6 +639,7 @@ type
NeedTmpVar: boolean; NeedTmpVar: boolean;
Expr: TPasExpr; Expr: TPasExpr;
Scope: TPasScope; Scope: TPasScope;
OnlyTypeMembers: boolean;
class function IsStoredInElement: boolean; override; class function IsStoredInElement: boolean; override;
class function FreeOnPop: boolean; override; class function FreeOnPop: boolean; override;
procedure IterateElements(const aName: string; StartScope: TPasScope; procedure IterateElements(const aName: string; StartScope: TPasScope;
@ -709,12 +728,19 @@ type
end; end;
TResolvedReferenceFlag = ( TResolvedReferenceFlag = (
rrfCallWithoutParams, // a TPrimitiveExpr is a call without params rrfDotScope, // found reference via a dot scope (TPasDotIdentifierScope)
rrfNewInstance, // constructor call (without it call a constructor as normal method) rrfImplicitCallWithoutParams, // a TPrimitiveExpr is an implicit call without params
rrfNewInstance, // constructor call (without it call constructor as normal method)
rrfFreeInstance, // destructor call (without it call destructor as normal method)
rrfVMT // use VMT for call rrfVMT // use VMT for call
); );
TResolvedReferenceFlags = set of TResolvedReferenceFlag; TResolvedReferenceFlags = set of TResolvedReferenceFlag;
{ TResolvedRefContext }
TResolvedRefContext = Class
end;
{ TResolvedReference - CustomData for normal references } { TResolvedReference - CustomData for normal references }
TResolvedReference = Class(TResolveData) TResolvedReference = Class(TResolveData)
@ -722,12 +748,20 @@ type
FDeclaration: TPasElement; FDeclaration: TPasElement;
procedure SetDeclaration(AValue: TPasElement); procedure SetDeclaration(AValue: TPasElement);
public public
WithExprScope: TPasWithExprScope;
Flags: TResolvedReferenceFlags; Flags: TResolvedReferenceFlags;
Context: TResolvedRefContext;
WithExprScope: TPasWithExprScope;// if set, this reference used a With-block expression.
destructor Destroy; override; destructor Destroy; override;
property Declaration: TPasElement read FDeclaration write SetDeclaration; property Declaration: TPasElement read FDeclaration write SetDeclaration;
end; end;
{ TResolvedRefCtxConstructor }
TResolvedRefCtxConstructor = Class(TResolvedRefContext)
public
Typ: TPasType; // e.g. TPasClassType
end;
TPasResolverResultFlag = ( TPasResolverResultFlag = (
rrfReadable, rrfReadable,
rrfWritable rrfWritable
@ -782,10 +816,13 @@ type
GetCallResult: TOnGetCallResult; GetCallResult: TOnGetCallResult;
end; end;
{ TPRFindData }
TPRFindData = record TPRFindData = record
ErrorPosEl: TPasElement; ErrorPosEl: TPasElement;
Found: TPasElement; Found: TPasElement;
ElScope, StartScope: TPasScope; ElScope: TPasScope; // Where Found was found
StartScope: TPasScope; // where the searched started
end; end;
PPRFindData = ^TPRFindData; PPRFindData = ^TPRFindData;
@ -931,6 +968,7 @@ type
procedure ConvertRangeToFirstValue(var ResolvedEl: TPasResolverResult); procedure ConvertRangeToFirstValue(var ResolvedEl: TPasResolverResult);
function IsCharLiteral(const Value: string): boolean; virtual; function IsCharLiteral(const Value: string): boolean; virtual;
protected protected
// built-in functions
function OnGetCallCompatibility_Length(Proc: TResElDataBuiltInProc; function OnGetCallCompatibility_Length(Proc: TResElDataBuiltInProc;
Expr: TPasExpr; RaiseOnError: boolean): integer; virtual; Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
procedure OnGetCallResult_Length(Proc: TResElDataBuiltInProc; procedure OnGetCallResult_Length(Proc: TResElDataBuiltInProc;
@ -1051,6 +1089,8 @@ type
function CheckProcArgCompatibility(Arg1, Arg2: TPasArgument): boolean; function CheckProcArgCompatibility(Arg1, Arg2: TPasArgument): boolean;
function CheckCanBeLHS(const ResolvedEl: TPasResolverResult; function CheckCanBeLHS(const ResolvedEl: TPasResolverResult;
ErrorOnFalse: boolean; ErrorEl: TPasElement): boolean; ErrorOnFalse: boolean; ErrorEl: TPasElement): boolean;
function CheckAssignCompatibility(const LHS, RHS: TPasElement;
RaiseOnIncompatible: boolean = true): integer;
function CheckAssignCompatibility(const LHS, RHS: TPasResolverResult; function CheckAssignCompatibility(const LHS, RHS: TPasResolverResult;
ErrorEl: TPasElement; RaiseOnIncompatible: boolean): integer; ErrorEl: TPasElement; RaiseOnIncompatible: boolean): integer;
function CheckEqualCompatibility(const LHS, RHS: TPasResolverResult; function CheckEqualCompatibility(const LHS, RHS: TPasResolverResult;
@ -1065,6 +1105,8 @@ type
function GetPasClassAncestor(ClassEl: TPasClassType; SkipAlias: boolean): TPasType; function GetPasClassAncestor(ClassEl: TPasClassType; SkipAlias: boolean): TPasType;
function ResolveAliasType(aType: TPasType): TPasType; function ResolveAliasType(aType: TPasType): TPasType;
function ExprIsAddrTarget(El: TPasExpr): boolean; function ExprIsAddrTarget(El: TPasExpr): boolean;
function GetLastExprIdentifier(El: TPasExpr): TPasExpr;
function GetReference_NewInstanceClass(Ref: TResolvedReference): TPasClassType;
public public
property BaseType[bt: TResolverBaseType]: TPasUnresolvedSymbolRef read GetBaseType; property BaseType[bt: TResolverBaseType]: TPasUnresolvedSymbolRef read GetBaseType;
property BaseTypeStringIndex: TResolverBaseType read FBaseTypeStringIndex write FBaseTypeStringIndex; property BaseTypeStringIndex: TResolverBaseType read FBaseTypeStringIndex write FBaseTypeStringIndex;
@ -1662,6 +1704,7 @@ end;
destructor TResolvedReference.Destroy; destructor TResolvedReference.Destroy;
begin begin
Declaration:=nil; Declaration:=nil;
FreeAndNil(Context);
inherited Destroy; inherited Destroy;
end; end;
@ -2591,16 +2634,10 @@ begin
end; end;
procedure TPasResolver.FinishConstDef(El: TPasConst); procedure TPasResolver.FinishConstDef(El: TPasConst);
var
TypeResolved, ExprResolved: TPasResolverResult;
begin begin
ResolveExpr(El.Expr); ResolveExpr(El.Expr);
if El.VarType<>nil then if El.VarType<>nil then
begin CheckAssignCompatibility(El,El.Expr,true);
ComputeElement(El,TypeResolved,[]);
ComputeElement(El.Expr,ExprResolved,[rcReturnFuncResult]);
CheckAssignCompatibility(TypeResolved,ExprResolved,El.Expr,true)
end;
end; end;
procedure TPasResolver.FinishProcedure; procedure TPasResolver.FinishProcedure;
@ -2779,6 +2816,7 @@ begin
Proc.ProcType.IsOfObject:=true; Proc.ProcType.IsOfObject:=true;
ProcScope:=TopScope as TPasProcedureScope; ProcScope:=TopScope as TPasProcedureScope;
ClassScope:=Scopes[ScopeCount-2] as TPasClassScope; ClassScope:=Scopes[ScopeCount-2] as TPasClassScope;
ProcScope.ClassScope:=ClassScope;
FindData:=Default(TFindOverloadProcData); FindData:=Default(TFindOverloadProcData);
FindData.Proc:=Proc; FindData.Proc:=Proc;
FindData.Args:=Proc.ProcType.Args; FindData.Args:=Proc.ProcType.Args;
@ -2971,15 +3009,9 @@ begin
end; end;
procedure TPasResolver.FinishVariable(El: TPasVariable); procedure TPasResolver.FinishVariable(El: TPasVariable);
var
TypeResolved, ExprResolved: TPasResolverResult;
begin begin
if El.Expr<>nil then if El.Expr<>nil then
begin CheckAssignCompatibility(El,El.Expr,true);
ComputeElement(El,TypeResolved,[]);
ComputeElement(El.Expr,ExprResolved,[rcReturnFuncResult]);
CheckAssignCompatibility(TypeResolved,ExprResolved,El.Expr,true);
end;
end; end;
procedure TPasResolver.FinishPropertyOfClass(PropEl: TPasProperty); procedure TPasResolver.FinishPropertyOfClass(PropEl: TPasProperty);
@ -3288,15 +3320,9 @@ begin
end; end;
procedure TPasResolver.FinishArgument(El: TPasArgument); procedure TPasResolver.FinishArgument(El: TPasArgument);
var
TypeResolved, ExprResolved: TPasResolverResult;
begin begin
if (El.ArgType<>nil) and (El.ValueExpr<>nil) then if (El.ArgType<>nil) and (El.ValueExpr<>nil) then
begin CheckAssignCompatibility(El,El.ValueExpr,true);
ComputeElement(El,TypeResolved,[]);
ComputeElement(El.ValueExpr,ExprResolved,[rcReturnFuncResult]);
CheckAssignCompatibility(TypeResolved,ExprResolved,El.ValueExpr,true);
end;
end; end;
procedure TPasResolver.FinishAncestors(aClass: TPasClassType); procedure TPasResolver.FinishAncestors(aClass: TPasClassType);
@ -3536,6 +3562,8 @@ var
WithScope: TPasWithScope; WithScope: TPasWithScope;
WithExprScope: TPasWithExprScope; WithExprScope: TPasWithExprScope;
ExprScope: TPasScope; ExprScope: TPasScope;
OnlyTypeMembers: Boolean;
ClassEl: TPasClassType;
begin begin
OldScopeCount:=ScopeCount; OldScopeCount:=ScopeCount;
WithScope:=TPasWithScope(CreateScope(El,TPasWithScope)); WithScope:=TPasWithScope(CreateScope(El,TPasWithScope));
@ -3555,10 +3583,28 @@ begin
RaiseMsg(nExprTypeMustBeClassOrRecordTypeGot,sExprTypeMustBeClassOrRecordTypeGot, RaiseMsg(nExprTypeMustBeClassOrRecordTypeGot,sExprTypeMustBeClassOrRecordTypeGot,
[BaseTypeNames[ExprResolved.BaseType]],ErrorEl); [BaseTypeNames[ExprResolved.BaseType]],ErrorEl);
OnlyTypeMembers:=false;
if TypeEl.ClassType=TPasRecordType then if TypeEl.ClassType=TPasRecordType then
ExprScope:=TPasRecordType(TypeEl).CustomData as TPasRecordScope begin
ExprScope:=TPasRecordType(TypeEl).CustomData as TPasRecordScope;
if ExprResolved.IdentEl is TPasType then
// e.g. with TPoint do PointInCircle
OnlyTypeMembers:=true;
end
else if TypeEl.ClassType=TPasClassType then else if TypeEl.ClassType=TPasClassType then
ExprScope:=TPasClassType(TypeEl).CustomData as TPasClassScope begin
ExprScope:=TPasClassType(TypeEl).CustomData as TPasClassScope;
if ExprResolved.IdentEl is TPasType then
// e.g. with TFPMemoryImage do FindHandlerFromExtension()
OnlyTypeMembers:=true;
end
else if TypeEl.ClassType=TPasClassOfType then
begin
// e.g. with ImageClass do FindHandlerFromExtension()
ClassEl:=ResolveAliasType(TPasClassOfType(TypeEl).DestType) as TPasClassType;
ExprScope:=ClassEl.CustomData as TPasClassScope;
OnlyTypeMembers:=true;
end
else else
RaiseMsg(nExprTypeMustBeClassOrRecordTypeGot,sExprTypeMustBeClassOrRecordTypeGot, RaiseMsg(nExprTypeMustBeClassOrRecordTypeGot,sExprTypeMustBeClassOrRecordTypeGot,
[TypeEl.ElementTypeName],ErrorEl); [TypeEl.ElementTypeName],ErrorEl);
@ -3568,6 +3614,7 @@ begin
WithExprScope.Expr:=Expr; WithExprScope.Expr:=Expr;
WithExprScope.Scope:=ExprScope; WithExprScope.Scope:=ExprScope;
WithExprScope.NeedTmpVar:=not (ExprResolved.IdentEl is TPasType); WithExprScope.NeedTmpVar:=not (ExprResolved.IdentEl is TPasType);
WithExprScope.OnlyTypeMembers:=OnlyTypeMembers;
WithScope.ExpressionScopes.Add(WithExprScope); WithScope.ExpressionScopes.Add(WithExprScope);
PushScope(WithExprScope); PushScope(WithExprScope);
end; end;
@ -3582,6 +3629,7 @@ end;
procedure TPasResolver.ResolveImplAssign(El: TPasImplAssign); procedure TPasResolver.ResolveImplAssign(El: TPasImplAssign);
var var
LeftResolved, RightResolved: TPasResolverResult; LeftResolved, RightResolved: TPasResolverResult;
Flags: TPasResolverComputeFlags;
begin begin
ResolveExpr(El.left); ResolveExpr(El.left);
ResolveExpr(El.right); ResolveExpr(El.right);
@ -3592,13 +3640,11 @@ begin
ComputeElement(El.left,LeftResolved,[rcSkipTypeAlias]); ComputeElement(El.left,LeftResolved,[rcSkipTypeAlias]);
CheckCanBeLHS(LeftResolved,true,El.left); CheckCanBeLHS(LeftResolved,true,El.left);
// compute RHS // compute RHS
ComputeElement(El.right,RightResolved,[rcSkipTypeAlias]); Flags:=[rcSkipTypeAlias,rcReturnFuncResult];
//writeln('TPasResolver.ResolveImplAssign Left=',GetResolverResultDesc(LeftResolved),' rcReturnFuncResult=',rcReturnFuncResult in Flags);
if RightResolved.BaseType=btProc then // ToDo: Delphi also uses left side to decide whether use function reference or function result
begin ComputeElement(El.right,RightResolved,Flags);
// ToDo: Delphi also uses left side to decide whether use function reference or function result //writeln('TPasResolver.ResolveImplAssign Right=',GetResolverResultDesc(RightResolved));
ComputeProcWithoutParams(RightResolved,El.right);
end;
case El.Kind of case El.Kind of
akDefault: akDefault:
@ -3661,17 +3707,21 @@ procedure TPasResolver.ResolveImplRaise(El: TPasImplRaise);
var var
ResolvedEl: TPasResolverResult; ResolvedEl: TPasResolverResult;
begin begin
ResolveExpr(El.ExceptObject); if El.ExceptObject<>nil then
ResolveExpr(El.ExceptAddr); begin
ComputeElement(El.ExceptObject,ResolvedEl,[rcSkipTypeAlias,rcReturnFuncResult]); ResolveExpr(El.ExceptObject);
if (ResolvedEl.IdentEl=nil) then ComputeElement(El.ExceptObject,ResolvedEl,[rcSkipTypeAlias,rcReturnFuncResult]);
RaiseMsg(nXExpectedButYFound,sXExpectedButYFound, if (ResolvedEl.IdentEl=nil) then
['variable',ResolvedEl.TypeEl.ElementTypeName],El.ExceptObject); RaiseMsg(nXExpectedButYFound,sXExpectedButYFound,
if (ResolvedEl.IdentEl.ClassType<>TPasVariable) ['variable',ResolvedEl.TypeEl.ElementTypeName],El.ExceptObject);
and (ResolvedEl.IdentEl.ClassType<>TPasArgument) then if (ResolvedEl.IdentEl.ClassType<>TPasVariable)
RaiseMsg(nXExpectedButYFound,sXExpectedButYFound, and (ResolvedEl.IdentEl.ClassType<>TPasArgument) then
['variable',ResolvedEl.IdentEl.ElementTypeName],El.ExceptObject); RaiseMsg(nXExpectedButYFound,sXExpectedButYFound,
CheckIsClass(El.ExceptObject,ResolvedEl); ['variable',ResolvedEl.IdentEl.ElementTypeName],El.ExceptObject);
CheckIsClass(El.ExceptObject,ResolvedEl);
end;
if El.ExceptAddr<>nil then
ResolveExpr(El.ExceptAddr);
end; end;
procedure TPasResolver.ResolveExpr(El: TPasExpr); procedure TPasResolver.ResolveExpr(El: TPasExpr);
@ -3730,6 +3780,8 @@ var
BuiltInProc: TResElDataBuiltInProc; BuiltInProc: TResElDataBuiltInProc;
begin begin
DeclEl:=FindElementWithoutParams(aName,FindData,El,false); DeclEl:=FindElementWithoutParams(aName,FindData,El,false);
Ref:=CreateReference(DeclEl,El,@FindData);
CheckFoundElement(FindData,Ref);
if DeclEl is TPasProcedure then if DeclEl is TPasProcedure then
begin begin
// identifier is a proc and args brackets are missing // identifier is a proc and args brackets are missing
@ -3755,8 +3807,6 @@ begin
BuiltInProc.GetCallCompatibility(BuiltInProc,El,true); BuiltInProc.GetCallCompatibility(BuiltInProc,El,true);
end; end;
end; end;
Ref:=CreateReference(DeclEl,El,@FindData);
CheckFoundElement(FindData,Ref);
end; end;
procedure TPasResolver.ResolveInherited(El: TInheritedExpr); procedure TPasResolver.ResolveInherited(El: TInheritedExpr);
@ -3766,14 +3816,17 @@ var
DeclProc, AncestorProc: TPasProcedure; DeclProc, AncestorProc: TPasProcedure;
begin begin
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
writeln('TPasResolver.ResolveInheritedDefault El.Parent=',GetTreeDesc(El.Parent)); writeln('TPasResolver.ResolveInherited El.Parent=',GetTreeDesc(El.Parent));
{$ENDIF} {$ENDIF}
if (El.Parent.ClassType=TBinaryExpr) if (El.Parent.ClassType=TBinaryExpr)
and (TBinaryExpr(El.Parent).OpCode=eopNone) then and (TBinaryExpr(El.Parent).OpCode=eopNone) then
begin begin
// e.g. 'inherited Proc;'
ResolveInheritedCall(TBinaryExpr(El.Parent)); ResolveInheritedCall(TBinaryExpr(El.Parent));
exit; exit;
end; end;
// 'inherited;' without expression
CheckTopScope(TPasProcedureScope); CheckTopScope(TPasProcedureScope);
ProcScope:=TPasProcedureScope(TopScope); ProcScope:=TPasProcedureScope(TopScope);
if ProcScope.ClassScope=nil then if ProcScope.ClassScope=nil then
@ -3782,11 +3835,11 @@ begin
AncestorScope:=ProcScope.ClassScope.AncestorScope; AncestorScope:=ProcScope.ClassScope.AncestorScope;
if AncestorScope=nil then if AncestorScope=nil then
begin begin
// 'inherited;' without ancestor is ignored // 'inherited;' without ancestor class is silently ignored
exit; exit;
end; end;
// search in ancestor // search ancestor in element, i.e. 'inherited' expression
DeclProc:=ProcScope.DeclarationProc; DeclProc:=ProcScope.DeclarationProc;
DeclProcScope:=DeclProc.CustomData as TPasProcedureScope; DeclProcScope:=DeclProc.CustomData as TPasProcedureScope;
AncestorProc:=DeclProcScope.OverriddenProc; AncestorProc:=DeclProcScope.OverriddenProc;
@ -3799,7 +3852,7 @@ begin
end end
else else
begin begin
// 'inherited;' without ancestor is ignored // 'inherited;' without ancestor method is silently ignored
exit; exit;
end; end;
end; end;
@ -3942,6 +3995,7 @@ begin
end end
else if LeftResolved.TypeEl=nil then else if LeftResolved.TypeEl=nil then
begin begin
// illegal qualifier, see below
end end
else if LeftResolved.TypeEl.ClassType=TPasClassType then else if LeftResolved.TypeEl.ClassType=TPasClassType then
begin begin
@ -4631,17 +4685,12 @@ begin
exit; exit;
end; end;
ComputeElement(Bin.left,LeftResolved,Flags); ComputeElement(Bin.left,LeftResolved,Flags+[rcReturnFuncResult]);
ComputeElement(Bin.right,RightResolved,Flags); ComputeElement(Bin.right,RightResolved,Flags+[rcReturnFuncResult]);
// ToDo: check operator overloading // ToDo: check operator overloading
//writeln('TPasResolver.ComputeBinaryExpr ',OpcodeStrings[Bin.OpCode],' Left=',GetResolverResultDesc(LeftResolved),' Right=',GetResolverResultDesc(RightResolved)); //writeln('TPasResolver.ComputeBinaryExpr ',OpcodeStrings[Bin.OpCode],' Left=',GetResolverResultDesc(LeftResolved),' Right=',GetResolverResultDesc(RightResolved));
if LeftResolved.BaseType=btProc then
ComputeProcWithoutParams(LeftResolved,Bin.left);
if RightResolved.BaseType=btProc then
ComputeProcWithoutParams(RightResolved,Bin.right);
if Bin.OpCode in [eopEqual,eopNotEqual] then if Bin.OpCode in [eopEqual,eopNotEqual] then
begin begin
if CheckEqualCompatibility(LeftResolved,RightResolved,Bin,true)=cIncompatible then if CheckEqualCompatibility(LeftResolved,RightResolved,Bin,true)=cIncompatible then
@ -5112,10 +5161,12 @@ var
Proc: TPasProcedure; Proc: TPasProcedure;
aClass: TPasClassType; aClass: TPasClassType;
ResolvedTypeEl: TPasResolverResult; ResolvedTypeEl: TPasResolverResult;
Ref: TResolvedReference;
begin begin
if Params.Value.CustomData is TResolvedReference then if Params.Value.CustomData is TResolvedReference then
begin begin
DeclEl:=TResolvedReference(Params.Value.CustomData).Declaration; Ref:=TResolvedReference(Params.Value.CustomData);
DeclEl:=Ref.Declaration;
if DeclEl.ClassType=TPasUnresolvedSymbolRef then if DeclEl.ClassType=TPasUnresolvedSymbolRef then
begin begin
if DeclEl.CustomData.ClassType=TResElDataBuiltInProc then if DeclEl.CustomData.ClassType=TResElDataBuiltInProc then
@ -5130,7 +5181,7 @@ begin
end end
else if DeclEl.CustomData.ClassType=TResElDataBaseType then else if DeclEl.CustomData.ClassType=TResElDataBaseType then
begin begin
// type case to base type // type cast to base type
SetResolverValueExpr(ResolvedEl, SetResolverValueExpr(ResolvedEl,
TResElDataBaseType(DeclEl.CustomData).BaseType, TResElDataBaseType(DeclEl.CustomData).BaseType,
TPasUnresolvedSymbolRef(DeclEl),Params.Params[0],[rrfReadable]); TPasUnresolvedSymbolRef(DeclEl),Params.Params[0],[rrfReadable]);
@ -5140,6 +5191,7 @@ begin
end end
else else
begin begin
// normal identifier (not built-in)
ComputeElement(DeclEl,ResolvedEl,Flags-[rcReturnFuncResult]); ComputeElement(DeclEl,ResolvedEl,Flags-[rcReturnFuncResult]);
if ResolvedEl.BaseType=btProc then if ResolvedEl.BaseType=btProc then
begin begin
@ -5151,10 +5203,11 @@ begin
if Proc is TPasFunction then if Proc is TPasFunction then
// function call => return result // function call => return result
ComputeElement(TPasFunction(Proc).FuncType.ResultEl,ResolvedEl,Flags-[rcReturnFuncResult]) ComputeElement(TPasFunction(Proc).FuncType.ResultEl,ResolvedEl,Flags-[rcReturnFuncResult])
else if Proc.ClassType=TPasConstructor then else if (Proc.ClassType=TPasConstructor)
and (rrfNewInstance in Ref.Flags) then
begin begin
// constructor call -> return value of type class // new instance call -> return value of type class
aClass:=Proc.Parent as TPasClassType; aClass:=GetReference_NewInstanceClass(Ref);
SetResolverValueExpr(ResolvedEl,btContext,aClass,Params.Value,[rrfReadable]); SetResolverValueExpr(ResolvedEl,btContext,aClass,Params.Value,[rrfReadable]);
end end
else else
@ -5208,9 +5261,8 @@ procedure TPasResolver.ComputeProcWithoutParams(
var var
aClass: TPasClassType; aClass: TPasClassType;
Proc: TPasProcedure; Proc: TPasProcedure;
Ref: TResolvedReference;
begin begin
if ExprIsAddrTarget(Expr) then exit;
if ResolvedEl.IdentEl=nil then if ResolvedEl.IdentEl=nil then
RaiseNotYetImplemented(20160928183455,Expr,GetResolverResultDesc(ResolvedEl)); RaiseNotYetImplemented(20160928183455,Expr,GetResolverResultDesc(ResolvedEl));
if not (ResolvedEl.IdentEl is TPasProcedure) then if not (ResolvedEl.IdentEl is TPasProcedure) then
@ -5221,13 +5273,22 @@ begin
RaiseMsg(nWrongNumberOfParametersForCallTo,sWrongNumberOfParametersForCallTo, RaiseMsg(nWrongNumberOfParametersForCallTo,sWrongNumberOfParametersForCallTo,
[GetProcDesc(Proc.ProcType)],Expr); [GetProcDesc(Proc.ProcType)],Expr);
Expr:=GetLastExprIdentifier(Expr);
if ExprIsAddrTarget(Expr) then exit;
Ref:=nil;
if Expr.CustomData is TResolvedReference then if Expr.CustomData is TResolvedReference then
Include(TResolvedReference(Expr.CustomData).Flags,rrfCallWithoutParams); begin
Ref:=TResolvedReference(Expr.CustomData);
Include(Ref.Flags,rrfImplicitCallWithoutParams);
end;
if (ResolvedEl.IdentEl is TPasFunction) then if (ResolvedEl.IdentEl is TPasFunction) then
ComputeElement(TPasFunction(ResolvedEl.IdentEl).FuncType.ResultEl,ResolvedEl,[]) ComputeElement(TPasFunction(ResolvedEl.IdentEl).FuncType.ResultEl,ResolvedEl,[])
else if ResolvedEl.IdentEl.ClassType=TPasConstructor then else if (ResolvedEl.IdentEl.ClassType=TPasConstructor)
and (Ref<>nil) and (rrfNewInstance in Ref.Flags) then
begin begin
aClass:=Proc.Parent as TPasClassType; // new instance call -> return value of type class
aClass:=GetReference_NewInstanceClass(Ref);
SetResolverValueExpr(ResolvedEl,btContext,aClass,Expr,[rrfReadable]); SetResolverValueExpr(ResolvedEl,btContext,aClass,Expr,[rrfReadable]);
end end
else else
@ -5998,6 +6059,8 @@ var
Data: TPRFindData; Data: TPRFindData;
begin begin
Result:=FindElementWithoutParams(AName,Data,ErrorPosEl,NoProcsWithArgs); Result:=FindElementWithoutParams(AName,Data,ErrorPosEl,NoProcsWithArgs);
if Data.Found=nil then exit; // forward type: class-of or ^
CheckFoundElement(Data,nil);
if (Data.StartScope<>nil) and (Data.StartScope.ClassType=TPasWithExprScope) if (Data.StartScope<>nil) and (Data.StartScope.ClassType=TPasWithExprScope)
and TPasWithExprScope(Data.StartScope).NeedTmpVar then and TPasWithExprScope(Data.StartScope).NeedTmpVar then
RaiseInternalError(20160923111727); // caller forgot to handle "With", use the other FindElementWithoutParams instead RaiseInternalError(20160923111727); // caller forgot to handle "With", use the other FindElementWithoutParams instead
@ -6035,8 +6098,6 @@ begin
// proc needs parameters // proc needs parameters
RaiseMsg(nWrongNumberOfParametersForCallTo, RaiseMsg(nWrongNumberOfParametersForCallTo,
sWrongNumberOfParametersForCallTo,[GetProcDesc(TPasProcedure(Result).ProcType)],ErrorPosEl); sWrongNumberOfParametersForCallTo,[GetProcDesc(TPasProcedure(Result).ProcType)],ErrorPosEl);
CheckFoundElement(Data,nil);
end; end;
procedure TPasResolver.IterateElements(const aName: string; procedure TPasResolver.IterateElements(const aName: string;
@ -6064,12 +6125,29 @@ var
Proc: TPasProcedure; Proc: TPasProcedure;
Context: TPasElement; Context: TPasElement;
FoundContext: TPasClassType; FoundContext: TPasClassType;
StartScope: TPasScope;
OnlyTypeMembers: Boolean;
TypeEl: TPasType;
begin begin
//writeln('TPasResolver.CheckFoundElOnStartScope StartScope=',FindData.StartScope.ClassName,' ',FindData.StartScope is TPasDotIdentifierScope,' ',(FindData.StartScope is TPasDotIdentifierScope) StartScope:=FindData.StartScope;
// and TPasDotIdentifierScope(FindData.StartScope).OnlyTypeMembers, OnlyTypeMembers:=false;
if (StartScope is TPasDotIdentifierScope) then
begin
OnlyTypeMembers:=TPasDotIdentifierScope(StartScope).OnlyTypeMembers;
Include(Ref.Flags,rrfDotScope);
end
else if StartScope.ClassType=TPasWithExprScope then
begin
OnlyTypeMembers:=TPasWithExprScope(StartScope).OnlyTypeMembers;
Include(Ref.Flags,rrfDotScope);
end;
//writeln('TPasResolver.CheckFoundElOnStartScope StartScope=',StartScope.ClassName,
// ' ',StartScope is TPasDotIdentifierScope,
// ' ',(StartScope is TPasDotIdentifierScope)
// and TPasDotIdentifierScope(StartScope).OnlyTypeMembers,
// ' FindData.Found=',GetObjName(FindData.Found)); // ' FindData.Found=',GetObjName(FindData.Found));
if (FindData.StartScope is TPasDotIdentifierScope) if OnlyTypeMembers then
and TPasDotIdentifierScope(FindData.StartScope).OnlyTypeMembers then
begin begin
//writeln('TPasResolver.CheckFoundElOnStartScope ',GetObjName(FindData.Found),' ',(FindData.Found is TPasVariable) //writeln('TPasResolver.CheckFoundElOnStartScope ',GetObjName(FindData.Found),' ',(FindData.Found is TPasVariable)
// and (vmClass in TPasVariable(FindData.Found).VarModifiers)); // and (vmClass in TPasVariable(FindData.Found).VarModifiers));
@ -6096,8 +6174,8 @@ begin
Proc:=TPasProcedure(FindData.Found); Proc:=TPasProcedure(FindData.Found);
if Proc.IsVirtual or Proc.IsOverride then if Proc.IsVirtual or Proc.IsOverride then
begin begin
if (FindData.StartScope.ClassType=TPasDotClassScope) if (StartScope.ClassType=TPasDotClassScope)
and TPasDotClassScope(FindData.StartScope).InheritedExpr then and TPasDotClassScope(StartScope).InheritedExpr then
begin begin
// call directly // call directly
if Proc.IsAbstract then if Proc.IsAbstract then
@ -6106,16 +6184,69 @@ begin
end end
else else
begin begin
// call via method table // call via virtual method table
if Ref<>nil then if Ref<>nil then
Ref.Flags:=Ref.Flags+[rrfVMT]; Ref.Flags:=Ref.Flags+[rrfVMT];
end; end;
end; end;
if (FindData.Found.ClassType=TPasConstructor)
and (FindData.StartScope.ClassType=TPasDotClassScope) // constructor: NewInstance or normal call
and TPasDotClassScope(FindData.StartScope).OnlyTypeMembers // it is a NewInstance iff the scope is a class, e.g. TObject.Create
if (Proc.ClassType=TPasConstructor)
and OnlyTypeMembers
and (Ref<>nil) then and (Ref<>nil) then
begin
Ref.Flags:=Ref.Flags+[rrfNewInstance]; Ref.Flags:=Ref.Flags+[rrfNewInstance];
// store the class in Ref.Context
if Ref.Context<>nil then
RaiseInternalError(20170131141936);
Ref.Context:=TResolvedRefCtxConstructor.Create;
if StartScope is TPasDotClassScope then
TypeEl:=TPasDotClassScope(StartScope).ClassScope.Element as TPasType
else if (StartScope is TPasWithExprScope) and (TPasWithExprScope(StartScope).Scope is TPasClassScope) then
TypeEl:=TPasClassScope(TPasWithExprScope(StartScope).Scope).Element as TPasType
else
RaiseInternalError(20170131150855,GetObjName(StartScope));
TResolvedRefCtxConstructor(Ref.Context).Typ:=TypeEl;
end;
{$IFDEF VerbosePasResolver}
if (Proc.ClassType=TPasConstructor) then
begin
write('TPasResolver.CheckFoundElement ',GetObjName(Proc));
if Ref=nil then
write(' no ref!')
else
begin
write(' rrfNewInstance=',rrfNewInstance in Ref.Flags,
' StartScope=',GetObjName(StartScope),
' OnlyTypeMembers=',OnlyTypeMembers);
end;
writeln;
end;
{$ENDIF}
// destructor: FreeInstance or normal call
// it is a normal call if 'inherited'
if (Proc.ClassType=TPasDestructor) and (Ref<>nil) then
if ((StartScope.ClassType<>TPasDotClassScope)
or (not TPasDotClassScope(StartScope).InheritedExpr)) then
Ref.Flags:=Ref.Flags+[rrfFreeInstance];
{$IFDEF VerbosePasResolver}
if (Proc.ClassType=TPasDestructor) then
begin
write('TPasResolver.CheckFoundElement ',GetObjName(Proc));
if Ref=nil then
write(' no ref!')
else
begin
write(' rrfFreeInstance=',rrfFreeInstance in Ref.Flags,
' StartScope=',GetObjName(StartScope));
if StartScope.ClassType=TPasDotClassScope then
write(' InheritedExpr=',TPasDotClassScope(StartScope).InheritedExpr);
end;
writeln;
end;
{$ENDIF}
end; end;
// check class visibility // check class visibility
@ -6886,6 +7017,16 @@ begin
RaiseMsg(nVariableIdentifierExpected,sVariableIdentifierExpected,[],ErrorEl); RaiseMsg(nVariableIdentifierExpected,sVariableIdentifierExpected,[],ErrorEl);
end; end;
function TPasResolver.CheckAssignCompatibility(const LHS, RHS: TPasElement;
RaiseOnIncompatible: boolean): integer;
var
LeftResolved, RightResolved: TPasResolverResult;
begin
ComputeElement(LHS,LeftResolved,[]);
ComputeElement(RHS,RightResolved,[rcReturnFuncResult]);
Result:=CheckAssignCompatibility(LeftResolved,RightResolved,RHS,RaiseOnIncompatible);
end;
function TPasResolver.CheckAssignCompatibility(const LHS, function TPasResolver.CheckAssignCompatibility(const LHS,
RHS: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean RHS: TPasResolverResult; ErrorEl: TPasElement; RaiseOnIncompatible: boolean
): integer; ): integer;
@ -6894,7 +7035,7 @@ var
begin begin
// check if the RHS can be converted to LHS // check if the RHS can be converted to LHS
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
writeln('TPasResolver.CheckAssignCompatibility '); writeln('TPasResolver.CheckAssignCompatibility START LHS='+GetResolverResultDesc(LHS)+' RHS='+GetResolverResultDesc(RHS));
{$ENDIF} {$ENDIF}
if LHS.TypeEl=nil then if LHS.TypeEl=nil then
begin begin
@ -6966,7 +7107,7 @@ begin
end; end;
end; end;
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
writeln('TPasResolver.CheckAssignCompatibility LHS='+GetResolverResultDesc(LHS)+' RHS='+GetResolverResultDesc(RHS)); writeln('TPasResolver.CheckAssignCompatibility incompatible LHS='+GetResolverResultDesc(LHS)+' RHS='+GetResolverResultDesc(RHS));
{$ENDIF} {$ENDIF}
if not RaiseOnIncompatible then if not RaiseOnIncompatible then
exit(cIncompatible); exit(cIncompatible);
@ -7194,8 +7335,6 @@ begin
MustFitExactly:=Param.Access in [argVar, argOut]; MustFitExactly:=Param.Access in [argVar, argOut];
ComputeElement(Expr,ExprResolved,ComputeFlags); ComputeElement(Expr,ExprResolved,ComputeFlags);
if ExprResolved.BaseType=btProc then
ComputeProcWithoutParams(ExprResolved,Expr);
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
writeln('TPasResolver.CheckParamCompatibility Expr=',GetTreeDesc(Expr,2),' ResolvedExpr=',GetResolverResultDesc(ExprResolved)); writeln('TPasResolver.CheckParamCompatibility Expr=',GetTreeDesc(Expr,2),' ResolvedExpr=',GetResolverResultDesc(ExprResolved));
@ -7213,7 +7352,9 @@ begin
RaiseMsg(nVariableIdentifierExpected,sVariableIdentifierExpected,[],Expr); RaiseMsg(nVariableIdentifierExpected,sVariableIdentifierExpected,[],Expr);
exit; exit;
end; end;
end; end
else if ExprResolved.BaseType=btProc then
ComputeProcWithoutParams(ExprResolved,Expr);
ComputeElement(Param,ParamResolved,ComputeFlags); ComputeElement(Param,ParamResolved,ComputeFlags);
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
@ -7269,7 +7410,7 @@ begin
exit(cExact); exit(cExact);
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
//writeln('TPasResolver.CheckCustomTypeCompatibility SrcTypeEl=',GetObjName(RTypeEl),' DstTypeEl=',GetObjName(LTypeEl)); writeln('TPasResolver.CheckCustomTypeCompatibility LTypeEl=',GetObjName(LTypeEl),' RTypeEl=',GetObjName(RTypeEl));
{$ENDIF} {$ENDIF}
if LTypeEl.ClassType=TPasClassType then if LTypeEl.ClassType=TPasClassType then
begin begin
@ -7591,6 +7732,8 @@ procedure TPasResolver.ComputeElement(El: TPasElement; out
var var
DeclEl: TPasElement; DeclEl: TPasElement;
aClass: TPasClassType; aClass: TPasClassType;
Ref: TResolvedReference;
Proc: TPasProcedure;
begin begin
ResolvedEl:=Default(TPasResolverResult); ResolvedEl:=Default(TPasResolverResult);
{$IFDEF VerbosePasResolver} {$IFDEF VerbosePasResolver}
@ -7605,20 +7748,30 @@ begin
begin begin
if not (El.CustomData is TResolvedReference) then if not (El.CustomData is TResolvedReference) then
RaiseNotYetImplemented(20160922163658,El,'Value="'+TPrimitiveExpr(El).Value+'" CustomData='+GetObjName(El.CustomData)+' '+GetElementSourcePosStr(El)); RaiseNotYetImplemented(20160922163658,El,'Value="'+TPrimitiveExpr(El).Value+'" CustomData='+GetObjName(El.CustomData)+' '+GetElementSourcePosStr(El));
ComputeElement(TResolvedReference(El.CustomData).Declaration,ResolvedEl,Flags-[rcReturnFuncResult]); Ref:=TResolvedReference(El.CustomData);
ComputeElement(Ref.Declaration,ResolvedEl,Flags-[rcReturnFuncResult]);
//writeln('TPasResolver.ComputeElement TPrimitiveExpr "',TPrimitiveExpr(El).Value,'" ',GetResolverResultDesc(ResolvedEl),' rcReturnFuncResult=',rcReturnFuncResult in Flags);
if (ResolvedEl.BaseType=btProc) and (rcReturnFuncResult in Flags) then if (ResolvedEl.BaseType=btProc) and (rcReturnFuncResult in Flags) then
begin begin
// a proc and implicit call without params is allowed -> check if possible
if rcConstant in Flags then if rcConstant in Flags then
RaiseConstantExprExp(El); RaiseConstantExprExp(El);
Include(TResolvedReference(El.CustomData).Flags,rrfCallWithoutParams); Proc:=ResolvedEl.IdentEl as TPasProcedure;
if ResolvedEl.IdentEl is TPasFunction then if (Proc.ProcType.Args.Count=0)
// function => return result or (TPasArgument(Proc.ProcType.Args[0]).ValueExpr<>nil) then
ComputeElement(TPasFunction(ResolvedEl.IdentEl).FuncType.ResultEl,ResolvedEl,Flags-[rcReturnFuncResult])
else if ResolvedEl.IdentEl.ClassType=TPasConstructor then
begin begin
// constructor -> return value of type class // parameter less proc -> implicit call
aClass:=ResolvedEl.IdentEl.Parent as TPasClassType; Include(Ref.Flags,rrfImplicitCallWithoutParams);
SetResolverValueExpr(ResolvedEl,btContext,aClass,TPrimitiveExpr(El),[rrfReadable]); if ResolvedEl.IdentEl is TPasFunction then
// function => return result
ComputeElement(TPasFunction(ResolvedEl.IdentEl).FuncType.ResultEl,ResolvedEl,Flags-[rcReturnFuncResult])
else if (ResolvedEl.IdentEl.ClassType=TPasConstructor)
and (rrfNewInstance in Ref.Flags) then
begin
// new instance constructor -> return value of type class
aClass:=GetReference_NewInstanceClass(Ref);
SetResolverValueExpr(ResolvedEl,btContext,aClass,TPrimitiveExpr(El),[rrfReadable]);
end;
end; end;
end; end;
end; end;
@ -7657,8 +7810,72 @@ begin
else else
RaiseNotYetImplemented(20160926194756,El); RaiseNotYetImplemented(20160926194756,El);
end end
else if El.ClassType=TSelfExpr then
begin
if rcConstant in Flags then
RaiseConstantExprExp(El);
ComputeElement(TResolvedReference(El.CustomData).Declaration,ResolvedEl,Flags);
end
else if El.ClassType=TBoolConstExpr then
SetResolverValueExpr(ResolvedEl,btBoolean,FBaseTypes[btBoolean],TBoolConstExpr(El),[rrfReadable])
else if El.ClassType=TBinaryExpr then else if El.ClassType=TBinaryExpr then
ComputeBinaryExpr(TBinaryExpr(El),ResolvedEl,Flags) ComputeBinaryExpr(TBinaryExpr(El),ResolvedEl,Flags)
else if El.ClassType=TUnaryExpr then
begin
if TUnaryExpr(El).OpCode=eopAddress then
ComputeElement(TUnaryExpr(El).Operand,ResolvedEl,Flags-[rcReturnFuncResult])
else
ComputeElement(TUnaryExpr(El).Operand,ResolvedEl,Flags);
{$IFDEF VerbosePasResolver}
writeln('TPasResolver.ComputeElement Unary Kind=',TUnaryExpr(El).Kind,' OpCode=',TUnaryExpr(El).OpCode,' OperandResolved=',GetResolverResultDesc(ResolvedEl),' ',GetElementSourcePosStr(El));
{$ENDIF}
case TUnaryExpr(El).OpCode of
eopAdd, eopSubtract:
if ResolvedEl.BaseType in (btAllInteger+btAllFloats) then
exit
else
RaiseMsg(nIllegalQualifier,sIllegalQualifier,[OpcodeStrings[TUnaryExpr(El).OpCode]],El);
eopNot:
if ResolvedEl.BaseType in (btAllInteger+btAllBooleans) then
exit
else
RaiseMsg(nIllegalQualifier,sIllegalQualifier,[OpcodeStrings[TUnaryExpr(El).OpCode]],El);
eopAddress:
if (ResolvedEl.BaseType=btProc) and (ResolvedEl.IdentEl is TPasProcedure) then
begin
SetResolverValueExpr(ResolvedEl,btContext,ResolvedEl.TypeEl,TUnaryExpr(El).Operand,[rrfReadable]);
exit;
end
else
RaiseMsg(nIllegalQualifier,sIllegalQualifier,[OpcodeStrings[TUnaryExpr(El).OpCode]],El);
end;
RaiseNotYetImplemented(20160926142426,El);
end
else if El.ClassType=TParamsExpr then
case TParamsExpr(El).Kind of
pekArrayParams:
ComputeArrayParams(TParamsExpr(El),ResolvedEl,Flags);
pekFuncParams:
ComputeFuncParams(TParamsExpr(El),ResolvedEl,Flags);
pekSet:
ComputeSetParams(TParamsExpr(El),ResolvedEl,Flags);
else
RaiseNotYetImplemented(20161010184559,El);
end
else if El.ClassType=TInheritedExpr then
begin
// writeln('TPasResolver.ComputeElement TInheritedExpr El.CustomData=',GetObjName(El.CustomData));
if El.CustomData is TResolvedReference then
begin
// "inherited;"
DeclEl:=TResolvedReference(El.CustomData).Declaration as TPasProcedure;
SetResolverIdentifier(ResolvedEl,btProc,DeclEl,
TPasProcedure(DeclEl).ProcType,[]);
end
else
// no ancestor proc
SetResolverIdentifier(ResolvedEl,btBuiltInProc,nil,nil,[]);
end
else if El.ClassType=TPasAliasType then else if El.ClassType=TPasAliasType then
begin begin
// e.g. 'type a = b' -> compute b // e.g. 'type a = b' -> compute b
@ -7767,37 +7984,6 @@ begin
ResolvedEl.IdentEl:=El; ResolvedEl.IdentEl:=El;
ResolvedEl.Flags:=[]; ResolvedEl.Flags:=[];
end end
else if El.ClassType=TUnaryExpr then
begin
if TUnaryExpr(El).OpCode=eopAddress then
ComputeElement(TUnaryExpr(El).Operand,ResolvedEl,Flags-[rcReturnFuncResult])
else
ComputeElement(TUnaryExpr(El).Operand,ResolvedEl,Flags);
{$IFDEF VerbosePasResolver}
writeln('TPasResolver.ComputeElement Unary Kind=',TUnaryExpr(El).Kind,' OpCode=',TUnaryExpr(El).OpCode,' OperandResolved=',GetResolverResultDesc(ResolvedEl),' ',GetElementSourcePosStr(El));
{$ENDIF}
case TUnaryExpr(El).OpCode of
eopAdd, eopSubtract:
if ResolvedEl.BaseType in (btAllInteger+btAllFloats) then
exit
else
RaiseMsg(nIllegalQualifier,sIllegalQualifier,[OpcodeStrings[TUnaryExpr(El).OpCode]],El);
eopNot:
if ResolvedEl.BaseType in (btAllInteger+btAllBooleans) then
exit
else
RaiseMsg(nIllegalQualifier,sIllegalQualifier,[OpcodeStrings[TUnaryExpr(El).OpCode]],El);
eopAddress:
if (ResolvedEl.BaseType=btProc) and (ResolvedEl.IdentEl is TPasProcedure) then
begin
SetResolverValueExpr(ResolvedEl,btContext,ResolvedEl.TypeEl,TUnaryExpr(El).Operand,[rrfReadable]);
exit;
end
else
RaiseMsg(nIllegalQualifier,sIllegalQualifier,[OpcodeStrings[TUnaryExpr(El).OpCode]],El);
end;
RaiseNotYetImplemented(20160926142426,El);
end
else if El.ClassType=TPasResultElement then else if El.ClassType=TPasResultElement then
begin begin
if rcConstant in Flags then if rcConstant in Flags then
@ -7810,47 +7996,17 @@ begin
SetResolverIdentifier(ResolvedEl,btModule,El,nil,[]) SetResolverIdentifier(ResolvedEl,btModule,El,nil,[])
else if El.ClassType=TNilExpr then else if El.ClassType=TNilExpr then
SetResolverValueExpr(ResolvedEl,btNil,FBaseTypes[btNil],TNilExpr(El),[rrfReadable]) SetResolverValueExpr(ResolvedEl,btNil,FBaseTypes[btNil],TNilExpr(El),[rrfReadable])
else if El.ClassType=TSelfExpr then
begin
if rcConstant in Flags then
RaiseConstantExprExp(El);
ComputeElement(TResolvedReference(El.CustomData).Declaration,ResolvedEl,Flags);
end
else if El.ClassType=TBoolConstExpr then
SetResolverValueExpr(ResolvedEl,btBoolean,FBaseTypes[btBoolean],TBoolConstExpr(El),[rrfReadable])
else if El.ClassType=TParamsExpr then
case TParamsExpr(El).Kind of
pekArrayParams:
ComputeArrayParams(TParamsExpr(El),ResolvedEl,Flags);
pekFuncParams:
ComputeFuncParams(TParamsExpr(El),ResolvedEl,Flags);
pekSet:
ComputeSetParams(TParamsExpr(El),ResolvedEl,Flags);
else
RaiseNotYetImplemented(20161010184559,El);
end
else if El is TPasProcedure then else if El is TPasProcedure then
begin begin
SetResolverIdentifier(ResolvedEl,btProc,El,TPasProcedure(El).ProcType,[]); SetResolverIdentifier(ResolvedEl,btProc,El,TPasProcedure(El).ProcType,[]);
if El is TPasFunction then if El is TPasFunction then
Include(ResolvedEl.Flags,rrfReadable); Include(ResolvedEl.Flags,rrfReadable);
// Note: the readability of TPasConstructor depends on the context
end end
else if El is TPasProcedureType then else if El is TPasProcedureType then
SetResolverIdentifier(ResolvedEl,btContext,El,TPasProcedureType(El),[]) SetResolverIdentifier(ResolvedEl,btContext,El,TPasProcedureType(El),[])
else if El.ClassType=TPasArrayType then else if El.ClassType=TPasArrayType then
SetResolverIdentifier(ResolvedEl,btContext,El,TPasArrayType(El),[]) SetResolverIdentifier(ResolvedEl,btContext,El,TPasArrayType(El),[])
else if El.ClassType=TInheritedExpr then
begin
if El.CustomData is TResolvedReference then
begin
DeclEl:=TResolvedReference(El.CustomData).Declaration as TPasProcedure;
SetResolverIdentifier(ResolvedEl,btProc,DeclEl,
TPasProcedure(DeclEl).ProcType,[]);
end
else
// no ancestor proc
SetResolverIdentifier(ResolvedEl,btBuiltInProc,nil,nil,[]);
end
else else
RaiseNotYetImplemented(20160922163705,El); RaiseNotYetImplemented(20160922163705,El);
end; end;
@ -7896,18 +8052,19 @@ begin
end; end;
function TPasResolver.ExprIsAddrTarget(El: TPasExpr): boolean; function TPasResolver.ExprIsAddrTarget(El: TPasExpr): boolean;
// returns true if El is the last element of an @ operator expression { returns true if El is
// e.g. the OnClick in '@p().o[].OnClick' a) the last element of an @ operator expression
// or '@s[]' e.g. '@p().o[].El' or '@El[]'
b) an accessor function, e.g. property P read El;
}
var var
Parent: TPasElement; Parent: TPasElement;
Prop: TPasProperty;
begin begin
Result:=false; Result:=false;
if El=nil then exit; if El=nil then exit;
if (El.ClassType=TParamsExpr) or (El.ClassType=TPrimitiveExpr) if not ((El.ClassType=TParamsExpr) or (El.ClassType=TPrimitiveExpr)
or (El.ClassType=TSelfExpr) then or (El.ClassType=TSelfExpr)) then
// these are possible endings of a @ expression
else
exit; exit;
repeat repeat
Parent:=El.Parent; Parent:=El.Parent;
@ -7924,12 +8081,36 @@ begin
begin begin
if TParamsExpr(Parent).Value<>El then exit; if TParamsExpr(Parent).Value<>El then exit;
end end
else else if Parent.ClassType=TPasProperty then
begin
Prop:=TPasProperty(Parent);
Result:=(Prop.ReadAccessor=El) or (Prop.WriteAccessor=El) or (Prop.StoredAccessor=El);
exit;
end
else
exit; exit;
El:=TPasExpr(Parent); El:=TPasExpr(Parent);
until false; until false;
end; end;
function TPasResolver.GetLastExprIdentifier(El: TPasExpr): TPasExpr;
begin
Result:=El;
while Result<>nil do
begin
if Result is TParamsExpr then
Result:=TParamsExpr(Result).Value
else if Result is TBinaryExpr then
Result:=TBinaryExpr(Result).right;
end;
end;
function TPasResolver.GetReference_NewInstanceClass(Ref: TResolvedReference
): TPasClassType;
begin
Result:=(Ref.Context as TResolvedRefCtxConstructor).Typ as TPasClassType;
end;
function TPasResolver.CheckSrcIsADstType(const ResolvedSrcType, function TPasResolver.CheckSrcIsADstType(const ResolvedSrcType,
ResolvedDestType: TPasResolverResult; ErrorEl: TPasElement): integer; ResolvedDestType: TPasResolverResult; ErrorEl: TPasElement): integer;
// finds distance between classes SrcType and DestType // finds distance between classes SrcType and DestType

View File

@ -283,6 +283,9 @@ type
Element: TPasExpr; AOpCode: TExprOpCode); Element: TPasExpr; AOpCode: TExprOpCode);
procedure AddParamsToBinaryExprChain(var ChainFirst, ChainLast: TPasExpr; procedure AddParamsToBinaryExprChain(var ChainFirst, ChainLast: TPasExpr;
Params: TParamsExpr); Params: TParamsExpr);
{$IFDEF VerbosePasParser}
procedure WriteBinaryExprChain(Prefix: string; First, Last: TPasExpr);
{$ENDIF}
function CreateUnaryExpr(AParent : TPasElement; AOperand: TPasExpr; AOpCode: TExprOpCode): TUnaryExpr; function CreateUnaryExpr(AParent : TPasElement; AOperand: TPasExpr; AOpCode: TExprOpCode): TUnaryExpr;
function CreateArrayValues(AParent : TPasElement): TArrayValues; function CreateArrayValues(AParent : TPasElement): TArrayValues;
function CreateFunctionType(const AName, AResultName: String; AParent: TPasElement; function CreateFunctionType(const AName, AResultName: String; AParent: TPasElement;
@ -2701,7 +2704,12 @@ begin
NextToken; NextToken;
if CurToken = tkColon then if CurToken = tkColon then
begin begin
Result.VarType := ParseType(Result,Scanner.CurSourcePos); Scanner.ForceCaret:=True;
try
Result.VarType := ParseType(Result,Scanner.CurSourcePos);
finally
Scanner.ForceCaret:=False;
end;
{ if Result.VarType is TPasRangeType then { if Result.VarType is TPasRangeType then
Ungettoken; // Range type stops on token after last range token} Ungettoken; // Range type stops on token after last range token}
end end
@ -2870,7 +2878,12 @@ begin
TypeName := CurTokenString; TypeName := CurTokenString;
NamePos:=Scanner.CurSourcePos; NamePos:=Scanner.CurSourcePos;
ExpectToken(tkEqual); ExpectToken(tkEqual);
Result:=ParseType(Parent,NamePos,TypeName,True); Scanner.ForceCaret:=True;
try
Result:=ParseType(Parent,NamePos,TypeName,True);
finally
Scanner.ForceCaret:=False;
end;
end; end;
function TPasParser.GetVariableValueAndLocation(Parent: TPasElement; out function TPasParser.GetVariableValueAndLocation(Parent: TPasElement; out
@ -2994,9 +3007,13 @@ begin
if CurToken=tkComma then if CurToken=tkComma then
ExpectIdentifier; ExpectIdentifier;
Until (CurToken=tkColon); Until (CurToken=tkColon);
Scanner.ForceCaret:=False;
try
VarType := ParseComplexType(VarEl);
finally
Scanner.ForceCaret:=False;
end;
// read type // read type
VarType := ParseComplexType(VarEl);
for i := OldListCount to VarList.Count - 1 do for i := OldListCount to VarList.Count - 1 do
begin begin
VarEl:=TPasVariable(VarList[i]); VarEl:=TPasVariable(VarList[i]);
@ -3254,16 +3271,10 @@ function TPasParser.CheckProcedureArgs(Parent: TPasElement; Args: TFPList;
begin begin
NextToken; NextToken;
Result:=(Curtoken=tkbraceOpen); case CurToken of
if not Result then tkBraceOpen:
begin
if Mandatory then
ParseExc(nParserExpectedLBracketColon,SParserExpectedLBracketColon)
else
UngetToken;
end
else
begin begin
Result:=true;
NextToken; NextToken;
if (CurToken<>tkBraceClose) then if (CurToken<>tkBraceClose) then
begin begin
@ -3271,6 +3282,17 @@ begin
ParseArgList(Parent, Args, tkBraceClose); ParseArgList(Parent, Args, tkBraceClose);
end; end;
end; end;
tkSemicolon,tkColon,tkof,tkis,tkIdentifier:
begin
Result:=false;
if Mandatory then
ParseExc(nParserExpectedLBracketColon,SParserExpectedLBracketColon)
else
UngetToken;
end
else
ParseExcTokenError(';');
end;
end; end;
procedure TPasParser.HandleProcedureModifier(Parent: TPasElement; pm: TProcedureModifier); procedure TPasParser.HandleProcedureModifier(Parent: TPasElement; pm: TProcedureModifier);
@ -5039,7 +5061,7 @@ begin
// chain not yet full => inconsistency // chain not yet full => inconsistency
RaiseInternal; RaiseInternal;
Last.right:=CreateBinaryExpr(Last,Last.right,Element,AOpCode); Last.right:=CreateBinaryExpr(Last,Last.right,Element,AOpCode);
ChainLast:=Last; ChainLast:=Last.right;
end end
else else
begin begin
@ -5085,6 +5107,68 @@ begin
end; end;
end; end;
{$IFDEF VerbosePasParser}
procedure TPasParser.WriteBinaryExprChain(Prefix: string; First, Last: TPasExpr
);
var
i: Integer;
begin
if First=nil then
begin
write(Prefix,'First=nil');
if Last=nil then
writeln('=Last')
else
begin
writeln(', ERROR Last=',Last.ClassName);
ParseExcSyntaxError;
end;
end
else if Last=nil then
begin
writeln(Prefix,'ERROR Last=nil First=',First.ClassName);
ParseExcSyntaxError;
end
else if First is TBinaryExpr then
begin
i:=0;
while First is TBinaryExpr do
begin
writeln(Prefix,Space(i*2),'bin.left=',TBinaryExpr(First).left.ClassName);
if First=Last then break;
First:=TBinaryExpr(First).right;
inc(i);
end;
if First<>Last then
begin
writeln(Prefix,Space(i*2),'ERROR Last is not last in chain');
ParseExcSyntaxError;
end;
if not (Last is TBinaryExpr) then
begin
writeln(Prefix,Space(i*2),'ERROR Last is not TBinaryExpr: ',Last.ClassName);
ParseExcSyntaxError;
end;
if TBinaryExpr(Last).right=nil then
begin
writeln(Prefix,Space(i*2),'ERROR Last.right=nil');
ParseExcSyntaxError;
end;
writeln(Prefix,Space(i*2),'last.right=',TBinaryExpr(Last).right.ClassName);
end
else if First=Last then
writeln(Prefix,'First=Last=',First.ClassName)
else
begin
write(Prefix,'ERROR First=',First.ClassName);
if Last<>nil then
writeln(' Last=',Last.ClassName)
else
writeln(' Last=nil');
end;
end;
{$ENDIF}
function TPasParser.CreateUnaryExpr(AParent: TPasElement; AOperand: TPasExpr; function TPasParser.CreateUnaryExpr(AParent: TPasElement; AOperand: TPasExpr;
AOpCode: TExprOpCode): TUnaryExpr; AOpCode: TExprOpCode): TUnaryExpr;
begin begin

View File

@ -403,6 +403,7 @@ type
TPascalScanner = class TPascalScanner = class
private private
FCurrentModeSwitches: TModeSwitches; FCurrentModeSwitches: TModeSwitches;
FForceCaret: Boolean;
FLastMsg: string; FLastMsg: string;
FLastMsgArgs: TMessageArgs; FLastMsgArgs: TMessageArgs;
FLastMsgNumber: integer; FLastMsgNumber: integer;
@ -420,6 +421,7 @@ type
FOptions: TPOptions; FOptions: TPOptions;
FLogEvents: TPScannerLogEvents; FLogEvents: TPScannerLogEvents;
FOnLog: TPScannerLogHandler; FOnLog: TPScannerLogHandler;
FPreviousToken: TToken;
FSkipComments: Boolean; FSkipComments: Boolean;
FSkipWhiteSpace: Boolean; FSkipWhiteSpace: Boolean;
TokenStr: PChar; TokenStr: PChar;
@ -484,6 +486,7 @@ type
property CurToken: TToken read FCurToken; property CurToken: TToken read FCurToken;
property CurTokenString: string read FCurTokenString; property CurTokenString: string read FCurTokenString;
Property PreviousToken : TToken Read FPreviousToken;
property Defines: TStrings read FDefines; property Defines: TStrings read FDefines;
property Macros: TStrings read FMacros; property Macros: TStrings read FMacros;
@ -497,6 +500,7 @@ type
property LastMsgPattern: string read FLastMsgPattern write FLastMsgPattern; property LastMsgPattern: string read FLastMsgPattern write FLastMsgPattern;
property LastMsgArgs: TMessageArgs read FLastMsgArgs write FLastMsgArgs; property LastMsgArgs: TMessageArgs read FLastMsgArgs write FLastMsgArgs;
Property CurrentModeSwitches : TModeSwitches Read FCurrentModeSwitches Write FCurrentModeSwitches; Property CurrentModeSwitches : TModeSwitches Read FCurrentModeSwitches Write FCurrentModeSwitches;
Property ForceCaret : Boolean Read FForceCaret Write FForceCaret;
end; end;
const const
@ -1262,6 +1266,7 @@ function TPascalScanner.FetchToken: TToken;
var var
IncludeStackItem: TIncludeStackItem; IncludeStackItem: TIncludeStackItem;
begin begin
FPreviousToken:=FCurToken;
while true do while true do
begin begin
Result := DoFetchToken; Result := DoFetchToken;
@ -1403,9 +1408,14 @@ begin
OldLength:=0; OldLength:=0;
FCurTokenString := ''; FCurTokenString := '';
while TokenStr[0] in ['#', ''''] do while TokenStr[0] in ['^','#', ''''] do
begin begin
case TokenStr[0] of case TokenStr[0] of
'^' :
begin
TokenStart := TokenStr;
Inc(TokenStr);
end;
'#': '#':
begin begin
TokenStart := TokenStr; TokenStart := TokenStr;
@ -2173,8 +2183,14 @@ begin
end; end;
'^': '^':
begin begin
if ForceCaret or
(PreviousToken in [tkIdentifier,tkNil,tkOperator,tkBraceClose,tkSquaredBraceClose,tkCARET]) then
begin
Inc(TokenStr); Inc(TokenStr);
Result := tkCaret; Result := tkCaret;
end
else
Result:=DoFetchTextToken;
end; end;
'\': '\':
begin begin

View File

@ -37,6 +37,7 @@ type
Procedure EndClass(AEnd : String = 'end'); Procedure EndClass(AEnd : String = 'end');
Procedure AddMember(S : String); Procedure AddMember(S : String);
Procedure ParseClass; Procedure ParseClass;
Procedure ParseClassFail(Msg: string; MsgNumber: integer);
Procedure DoParseClass(FromSpecial : Boolean = False); Procedure DoParseClass(FromSpecial : Boolean = False);
procedure SetUp; override; procedure SetUp; override;
procedure TearDown; override; procedure TearDown; override;
@ -92,6 +93,7 @@ type
procedure TestHintFieldUninmplemented; procedure TestHintFieldUninmplemented;
Procedure TestMethodSimple; Procedure TestMethodSimple;
Procedure TestMethodSimpleComment; Procedure TestMethodSimpleComment;
Procedure TestMethodWithDotFails;
Procedure TestClassMethodSimple; Procedure TestClassMethodSimple;
Procedure TestClassMethodSimpleComment; Procedure TestClassMethodSimpleComment;
Procedure TestConstructor; Procedure TestConstructor;
@ -329,6 +331,23 @@ begin
DoParseClass(False); DoParseClass(False);
end; end;
procedure TTestClassType.ParseClassFail(Msg: string; MsgNumber: integer);
var
ok: Boolean;
begin
ok:=false;
try
ParseClass;
except
on E: EParserError do
begin
AssertEquals('Expected {'+Msg+'}, but got msg {'+Parser.LastMsg+'}',MsgNumber,Parser.LastMsgNumber);
ok:=true;
end;
end;
AssertEquals('Missing parser error {'+Msg+'} ('+IntToStr(MsgNumber)+')',true,ok);
end;
procedure TTestClassType.DoParseClass(FromSpecial: Boolean); procedure TTestClassType.DoParseClass(FromSpecial: Boolean);
begin begin
EndClass; EndClass;
@ -363,7 +382,6 @@ begin
AssertNull('No helperfortype if not helper',TheClass.HelperForType); AssertNull('No helperfortype if not helper',TheClass.HelperForType);
if TheClass.Members.Count>0 then if TheClass.Members.Count>0 then
FMember1:=TObject(TheClass.Members[0]) as TPaselement; FMember1:=TObject(TheClass.Members[0]) as TPaselement;
end; end;
procedure TTestClassType.SetUp; procedure TTestClassType.SetUp;
@ -409,6 +427,7 @@ procedure TTestClassType.AssertProperty(P: TPasProperty;
AVisibility: TPasMemberVisibility; AName, ARead, AWrite, AStored, AVisibility: TPasMemberVisibility; AName, ARead, AWrite, AStored,
AImplements: String; AArgCount: Integer; ADefault, ANodefault: Boolean); AImplements: String; AArgCount: Integer; ADefault, ANodefault: Boolean);
begin begin
AssertEquals('Property Name',AName,P.Name);
AssertEquals(P.Name+': Visibility',AVisibility,P.Visibility); AssertEquals(P.Name+': Visibility',AVisibility,P.Visibility);
Assertequals(P.Name+': No args',AArgCount,P.Args.Count); Assertequals(P.Name+': No args',AArgCount,P.Args.Count);
Assertequals(P.Name+': Read accessor',ARead,P.ReadAccessorName); Assertequals(P.Name+': Read accessor',ARead,P.ReadAccessorName);
@ -768,6 +787,12 @@ begin
AssertEquals('Comment','c'+sLineBreak,Method1.DocComment); AssertEquals('Comment','c'+sLineBreak,Method1.DocComment);
end; end;
procedure TTestClassType.TestMethodWithDotFails;
begin
AddMember('Procedure DoSomething.Stupid');
ParseClassFail('Expected ";"',nParserExpectTokenError);
end;
procedure TTestClassType.TestClassMethodSimple; procedure TTestClassType.TestClassMethodSimple;
begin begin
AddMember('Class Procedure DoSomething'); AddMember('Class Procedure DoSomething');

File diff suppressed because it is too large Load Diff

View File

@ -1211,8 +1211,6 @@ procedure TTestStatementParser.TestCaseElseNoSemicolon;
Var Var
C : TPasImplCaseOf; C : TPasImplCaseOf;
S : TPasImplCaseStatement; S : TPasImplCaseStatement;
B : TPasImplbeginBlock;
begin begin
DeclareVar('integer'); DeclareVar('integer');
TestStatement(['case a of','1 : dosomething;','2 : dosomethingmore','else','a:=1;','end;']); TestStatement(['case a of','1 : dosomething;','2 : dosomethingmore','else','a:=1;','end;']);

View File

@ -30,7 +30,7 @@
<RunParams> <RunParams>
<local> <local>
<FormatVersion Value="1"/> <FormatVersion Value="1"/>
<CommandLineParams Value="--suite=TTestExpressions.TestUnaryDoubleDeref"/> <CommandLineParams Value="--suite=TTestStatementParser.TestCaseElseNoSemicolon"/>
</local> </local>
</RunParams> </RunParams>
<RequiredPackages Count="1"> <RequiredPackages Count="1">

File diff suppressed because it is too large Load Diff

View File

@ -108,7 +108,6 @@ type
Procedure TestMemberExpressionArrayTwoDim; Procedure TestMemberExpressionArrayTwoDim;
Procedure TestVariable; Procedure TestVariable;
Procedure TestArrayVariable; Procedure TestArrayVariable;
procedure TestClassDecleration;
end; end;
{ TTestStatementConverter } { TTestStatementConverter }
@ -374,6 +373,7 @@ Var
I : TJSUnaryPostPlusPlusExpression; I : TJSUnaryPostPlusPlusExpression;
C : TJSRelationalExpressionLE; C : TJSRelationalExpressionLE;
VS: TJSVariableStatement; VS: TJSVariableStatement;
LoopEndVar: String;
begin begin
// For I:=1 to 100 do a:=b; // For I:=1 to 100 do a:=b;
@ -385,24 +385,27 @@ begin
F.Body:=CreateAssignStatement(); F.Body:=CreateAssignStatement();
L:=TJSStatementList(Convert(F,TJSStatementList)); L:=TJSStatementList(Convert(F,TJSStatementList));
// Should be a list of two statements: // Should be a list of two statements:
// i:=1; // var $loopend1=100;
// for(var $loopend=100; i<=$loopend; i++){ a:=b; } // for(i=1; i<=$loopend1; i++){ a:=b; }
A:=TJSSimpleAssignStatement(AssertElement('First in list is Init statement',TJSSimpleAssignStatement,L.A));
AssertIdentifier('Init statement LHS is loop variable',A.LHS,'i'); // "var $loopend1=100"
AssertLiteral('Init statement RHS is start value',A.Expr,1); LoopEndVar:=DefaultLoopEndVarName+'1';
VS:=TJSVariableStatement(AssertElement('First in list is var '+LoopEndVar,TJSVariableStatement,L.A));
VD:=TJSVarDeclaration(AssertElement('var '+LoopEndVar,TJSVarDeclaration,VS.A));
AssertEquals('Correct name for '+LoopEndVar,LoopEndVar,VD.Name);
AssertLiteral('Correct end value',VD.Init,100);
E:=TJSForStatement(AssertElement('Second in list is "for" statement',TJSForStatement,L.B)); E:=TJSForStatement(AssertElement('Second in list is "for" statement',TJSForStatement,L.B));
// "var $loopend=100" // i:=1
VS:=TJSVariableStatement(AssertElement('var '+LoopEndVarName,TJSVariableStatement,E.Init)); A:=TJSSimpleAssignStatement(AssertElement('Init statement',TJSSimpleAssignStatement,E.Init));
VD:=TJSVarDeclaration(AssertElement('var '+LoopEndVarName,TJSVarDeclaration,VS.A)); AssertIdentifier('Init statement LHS is loop variable',A.LHS,'i');
AssertEquals('Correct name for '+LoopEndVarName,LoopEndVarName,VD.Name); AssertLiteral('Init statement RHS is start value',A.Expr,1);
AssertLiteral('Correct end value',VD.Init,100);
// i<=$loopend // i<=$loopend1
C:=TJSRelationalExpressionLE(AssertElement('Condition is <= expression',TJSRelationalExpressionLE,E.Cond)); C:=TJSRelationalExpressionLE(AssertElement('Condition is <= expression',TJSRelationalExpressionLE,E.Cond));
AssertIdentifier('Cond LHS is loop variable',C.A,'i'); AssertIdentifier('Cond LHS is loop variable',C.A,'i');
AssertIdentifier('Cond RHS is '+LoopEndVarName,C.B,LoopEndVarName); AssertIdentifier('Cond RHS is '+LoopEndVar,C.B,LoopEndVar);
// i++ // i++
I:=TJSUnaryPostPlusPlusExpression(AssertElement('Increment is ++ statement',TJSUnaryPostPlusPlusExpression,E.Incr)); I:=TJSUnaryPostPlusPlusExpression(AssertElement('Increment is ++ statement',TJSUnaryPostPlusPlusExpression,E.Incr));
@ -422,6 +425,7 @@ Var
I : TJSUnaryPostMinusMinusExpression; I : TJSUnaryPostMinusMinusExpression;
C : TJSRelationalExpressionGE; C : TJSRelationalExpressionGE;
VS: TJSVariableStatement; VS: TJSVariableStatement;
LoopEndVar: String;
begin begin
// For I:=100 downto 1 do a:=b; // For I:=100 downto 1 do a:=b;
@ -435,24 +439,27 @@ begin
L:=TJSStatementList(Convert(F,TJSStatementList)); L:=TJSStatementList(Convert(F,TJSStatementList));
// Should be a list of two statements: // Should be a list of two statements:
// i:=100; // var $loopend1=1;
// for(var $loopend=1; i>=$loopend; i--){ a:=b; } // for(i=100; i>=$loopend1; i--){ a:=b; }
A:=TJSSimpleAssignStatement(AssertElement('First in list is Init statement',TJSSimpleAssignStatement,L.A));
AssertIdentifier('Init statement LHS is loop variable',A.LHS,'i'); // "var $loopend1=1"
AssertLiteral('Init statement RHS is start value',A.Expr,100); LoopEndVar:=DefaultLoopEndVarName+'1';
VS:=TJSVariableStatement(AssertElement('var '+LoopEndVar,TJSVariableStatement,L.A));
VD:=TJSVarDeclaration(AssertElement('var '+LoopEndVar,TJSVarDeclaration,VS.A));
AssertEquals('Correct name for '+LoopEndVar,LoopEndVar,VD.Name);
AssertLiteral('Correct end value',VD.Init,1);
E:=TJSForStatement(AssertElement('Second in list is "for" statement',TJSForStatement,L.B)); E:=TJSForStatement(AssertElement('Second in list is "for" statement',TJSForStatement,L.B));
// "var $loopend=1" // i=100;
VS:=TJSVariableStatement(AssertElement('var '+LoopEndVarName,TJSVariableStatement,E.Init)); A:=TJSSimpleAssignStatement(AssertElement('First in list is Init statement',TJSSimpleAssignStatement,E.Init));
VD:=TJSVarDeclaration(AssertElement('var '+LoopEndVarName,TJSVarDeclaration,VS.A)); AssertIdentifier('Init statement LHS is loop variable',A.LHS,'i');
AssertEquals('Correct name for '+LoopEndVarName,LoopEndVarName,VD.Name); AssertLiteral('Init statement RHS is start value',A.Expr,100);
AssertLiteral('Correct end value',VD.Init,1);
// i>=$loopend // i>=$loopend1
C:=TJSRelationalExpressionGE(AssertElement('Condition is >= expression',TJSRelationalExpressionGE,E.Cond)); C:=TJSRelationalExpressionGE(AssertElement('Condition is >= expression',TJSRelationalExpressionGE,E.Cond));
AssertIdentifier('Cond LHS is loop variable',C.A,'i'); AssertIdentifier('Cond LHS is loop variable',C.A,'i');
AssertIdentifier('Cond RHS is '+LoopEndVarName,C.B,LoopEndVarName); AssertIdentifier('Cond RHS is '+LoopEndVar,C.B,LoopEndVar);
// i-- // i--
I:=TJSUnaryPostMinusMinusExpression(AssertElement('Increment is -- statement',TJSUnaryPostMinusMinusExpression,E.Incr)); I:=TJSUnaryPostMinusMinusExpression(AssertElement('Increment is -- statement',TJSUnaryPostMinusMinusExpression,E.Incr));
@ -596,7 +603,7 @@ Procedure TTestStatementConverter.TestTryExceptStatement;
Var Var
T : TPasImplTry; T : TPasImplTry;
F : TPasImplTryExcept; F : TPasImplTryExcept;
El : TJSTryFinallyStatement; El : TJSTryCatchStatement;
L : TJSStatementList; L : TJSStatementList;
begin begin
@ -605,7 +612,7 @@ begin
T.AddElement(CreateAssignStatement('a','b')); T.AddElement(CreateAssignStatement('a','b'));
F:=T.AddExcept; F:=T.AddExcept;
F.AddElement(CreateAssignStatement('b','c')); F.AddElement(CreateAssignStatement('b','c'));
El:=TJSTryFinallyStatement(Convert(T,TJSTryCatchStatement)); El:=TJSTryCatchStatement(Convert(T,TJSTryCatchStatement));
L:=AssertListStatement('try..except block is statement list',El.Block); L:=AssertListStatement('try..except block is statement list',El.Block);
AssertAssignStatement('Correct assignment in try..except block',L.A,'a','b'); AssertAssignStatement('Correct assignment in try..except block',L.A,'a','b');
AssertNull('No second statement',L.B); AssertNull('No second statement',L.B);
@ -621,7 +628,7 @@ Var
T : TPasImplTry; T : TPasImplTry;
F : TPasImplTryExcept; F : TPasImplTryExcept;
O : TPasImplExceptOn; O : TPasImplExceptOn;
El : TJSTryFinallyStatement; El : TJSTryCatchStatement;
L : TJSStatementList; L : TJSStatementList;
I : TJSIfStatement; I : TJSIfStatement;
IC : TJSRelationalExpressionInstanceOf; IC : TJSRelationalExpressionInstanceOf;
@ -647,7 +654,7 @@ begin
O:=F.AddExceptOn('E','Exception'); O:=F.AddExceptOn('E','Exception');
O.Body:=CreateAssignStatement('b','c'); O.Body:=CreateAssignStatement('b','c');
// Convert // Convert
El:=TJSTryFinallyStatement(Convert(T,TJSTryCatchStatement)); El:=TJSTryCatchStatement(Convert(T,TJSTryCatchStatement));
AssertEquals('Correct exception object name',lowercase(DefaultJSExceptionObject),String(El.Ident)); AssertEquals('Correct exception object name',lowercase(DefaultJSExceptionObject),String(El.Ident));
L:=AssertListStatement('try..except block is statement list',El.BCatch); L:=AssertListStatement('try..except block is statement list',El.BCatch);
AssertNull('No second statement',L.B); AssertNull('No second statement',L.B);
@ -669,7 +676,7 @@ Var
T : TPasImplTry; T : TPasImplTry;
F : TPasImplTryExcept; F : TPasImplTryExcept;
O : TPasImplExceptOn; O : TPasImplExceptOn;
El : TJSTryFinallyStatement; El : TJSTryCatchStatement;
L : TJSStatementList; L : TJSStatementList;
I : TJSIfStatement; I : TJSIfStatement;
IC : TJSRelationalExpressionInstanceOf; IC : TJSRelationalExpressionInstanceOf;
@ -695,7 +702,7 @@ begin
O:=F.AddExceptOn('E','Exception'); O:=F.AddExceptOn('E','Exception');
O.Body:=TPasImplRaise.Create('',Nil); O.Body:=TPasImplRaise.Create('',Nil);
// Convert // Convert
El:=TJSTryFinallyStatement(Convert(T,TJSTryCatchStatement)); El:=TJSTryCatchStatement(Convert(T,TJSTryCatchStatement));
AssertEquals('Correct exception object name',lowercase(DefaultJSExceptionObject),String(El.Ident)); AssertEquals('Correct exception object name',lowercase(DefaultJSExceptionObject),String(El.Ident));
L:=AssertListStatement('try..except block is statement list',El.BCatch); L:=AssertListStatement('try..except block is statement list',El.BCatch);
AssertNull('No second statement',L.B); AssertNull('No second statement',L.B);
@ -756,6 +763,7 @@ begin
AssertNotNull('Convert returned a result',E); AssertNotNull('Convert returned a result',E);
if not (E is TJSUnary) then if not (E is TJSUnary) then
Fail('Do not have unary class, but: '+E.ClassName); Fail('Do not have unary class, but: '+E.ClassName);
AssertEquals('TTestExpressionConverter.TestUnaryExpression: wrong class',AClass.ClassName,E.ClassName);
Result:=TJSUnary(E); Result:=TJSUnary(E);
end; end;
@ -1186,27 +1194,7 @@ begin
A:=TJSArrayLiteral(AssertElement('Init is array literal',TJSArrayLiteral,VD.Init)); A:=TJSArrayLiteral(AssertElement('Init is array literal',TJSArrayLiteral,VD.Init));
AssertEquals('No elements',0,A.Elements.Count); AssertEquals('No elements',0,A.Elements.Count);
end; end;
procedure TTestExpressionConverter.TestClassDecleration;
var
C: TPasClassType;
Decl: TPasDeclarations;
Sl: TJSStatementList;
Uni: TJSUnary;
Asi: TJSSimpleAssignStatement;
pex: TJSPrimaryExpressionIdent;
Call: TJSCallExpression;
begin
Decl:=TPasDeclarations.Create('',Nil);
C:=TPasClassType.Create('myclass',Nil);
Decl.Declarations.Add(c);
Sl:=TJSStatementList(Convert(Decl,TJSStatementList));
Uni:=TJSUnary(AssertElement('Sl.A is TJSUnary',TJSUnary,Sl.A));
Asi:=TJSSimpleAssignStatement(AssertElement('Sl.A is TJSUnary',TJSSimpleAssignStatement,Uni.A));
pex:=TJSPrimaryExpressionIdent(AssertElement('Asi.LHS is TJSPrimaryExpressionIdent',TJSPrimaryExpressionIdent,Asi.LHS));
AssertEquals('Correct name','myclass',String(pex.Name));
Call:=TJSCallExpression(AssertElement('Asi.Expr is TJSCallExpression',TJSCallExpression,Asi.Expr));
if Call=nil then ;
end;
procedure TTestTestConverter.TestEmpty; procedure TTestTestConverter.TestEmpty;
begin begin
AssertNotNull('Have converter',Converter); AssertNotNull('Have converter',Converter);

View File

@ -112,6 +112,7 @@ type
function GetDottedIdentifier(El: TJSElement): string; function GetDottedIdentifier(El: TJSElement): string;
procedure CheckSource(Msg,Statements, InitStatements: string); procedure CheckSource(Msg,Statements, InitStatements: string);
procedure CheckDiff(Msg, Expected, Actual: string); procedure CheckDiff(Msg, Expected, Actual: string);
procedure WriteSource(aFilename: string; Row: integer = 0; Col: integer = 0);
property PasProgram: TPasProgram Read FPasProgram; property PasProgram: TPasProgram Read FPasProgram;
property Modules[Index: integer]: TTestEnginePasResolver read GetModules; property Modules[Index: integer]: TTestEnginePasResolver read GetModules;
property ModuleCount: integer read GetModuleCount; property ModuleCount: integer read GetModuleCount;
@ -154,6 +155,7 @@ type
Procedure TestProcedureWithoutParams; Procedure TestProcedureWithoutParams;
Procedure TestPrgProcVar; Procedure TestPrgProcVar;
Procedure TestProcTwoArgs; Procedure TestProcTwoArgs;
Procedure TestProc_DefaultValue;
Procedure TestUnitProcVar; Procedure TestUnitProcVar;
Procedure TestFunctionResult; Procedure TestFunctionResult;
// ToDo: overloads // ToDo: overloads
@ -163,11 +165,12 @@ type
Procedure TestAssignFunctionResult; Procedure TestAssignFunctionResult;
Procedure TestFunctionResultInCondition; Procedure TestFunctionResultInCondition;
Procedure TestExit; Procedure TestExit;
// ToDo: Procedure TestBreak;
// ToDo: Procedure TestContinue;
// ToDo: TestString; SetLength,Length,[],char
// ToDo: pass by reference // ToDo: pass by reference
// ToDo: procedure type
// ToDo: enums // ToDo: enums
// statements // statements
@ -179,30 +182,43 @@ type
Procedure TestVarRecord; Procedure TestVarRecord;
Procedure TestForLoop; Procedure TestForLoop;
Procedure TestForLoopInFunction; Procedure TestForLoopInFunction;
Procedure TestForLoop_ReadVarAfter;
Procedure TestForLoop_Nested;
Procedure TestRepeatUntil; Procedure TestRepeatUntil;
Procedure TestAsmBlock; Procedure TestAsmBlock;
Procedure TestTryFinally; Procedure TestTryFinally;
// ToDo: try..except Procedure TestTryExcept;
Procedure TestCaseOf; Procedure TestCaseOf;
Procedure TestCaseOf_UseSwitch; Procedure TestCaseOf_UseSwitch;
Procedure TestCaseOfNoElse; Procedure TestCaseOfNoElse;
Procedure TestCaseOfNoElse_UseSwitch; Procedure TestCaseOfNoElse_UseSwitch;
Procedure TestCaseOfRange; Procedure TestCaseOfRange;
// arrays
Procedure TestArray;
// classes // classes
// ToDo: var Procedure TestClass_TObjectDefaultConstructor;
// ToDo: inheritance Procedure TestClass_TObjectConstructorWithParams;
// ToDo: constructor Procedure TestClass_Var;
Procedure TestClass_Method;
Procedure TestClass_Inheritance;
Procedure TestClass_AbstractMethod;
Procedure TestClass_CallInherited_NoParams;
Procedure TestClass_CallInherited_WithParams;
// ToDo: Procedure TestClass_CallInheritedConstructor;
// ToDo: overload
// ToDo: second constructor // ToDo: second constructor
// ToDo: call another constructor within a constructor // ToDo: call another constructor within a constructor
// ToDo: newinstance // ToDo: call class.classmethod
// ToDo: BeforeDestruction // ToDo: call instance.classmethod
// ToDo: AfterConstruction // ToDo: property
// ToDo: event // ToDo: event
// ToDo: class of // ToDo: class of
// ToDo: call classof.classmethod
// ToDo: arrays // ToDo: procedure type
end; end;
function LinesToStr(Args: array of const): string; function LinesToStr(Args: array of const): string;
@ -428,6 +444,8 @@ begin
end; end;
procedure TTestModule.ParseModule; procedure TTestModule.ParseModule;
var
Row, Col: integer;
begin begin
FFirstPasStatement:=nil; FFirstPasStatement:=nil;
try try
@ -436,22 +454,20 @@ begin
except except
on E: EParserError do on E: EParserError do
begin begin
WriteSource(E.Filename,E.Row,E.Column);
writeln('ERROR: TTestModule.ParseModule Parser: '+E.ClassName+':'+E.Message writeln('ERROR: TTestModule.ParseModule Parser: '+E.ClassName+':'+E.Message
+' File='+Scanner.CurFilename +' '+E.Filename+'('+IntToStr(E.Row)+','+IntToStr(E.Column)+')'
+' LineNo='+IntToStr(Scanner.CurRow)
+' Col='+IntToStr(Scanner.CurColumn)
+' Line="'+Scanner.CurLine+'"' +' Line="'+Scanner.CurLine+'"'
); );
raise E; raise E;
end; end;
on E: EPasResolve do on E: EPasResolve do
begin begin
Engine.UnmangleSourceLineNumber(E.PasElement.SourceLinenumber,Row,Col);
WriteSource(E.PasElement.SourceFilename,Row,Col);
writeln('ERROR: TTestModule.ParseModule PasResolver: '+E.ClassName+':'+E.Message writeln('ERROR: TTestModule.ParseModule PasResolver: '+E.ClassName+':'+E.Message
+' File='+Scanner.CurFilename +' '+E.PasElement.SourceFilename
+' LineNo='+IntToStr(Scanner.CurRow) +'('+IntToStr(Row)+','+IntToStr(Col)+')');
+' Col='+IntToStr(Scanner.CurColumn)
+' Line="'+Scanner.CurLine+'"'
);
raise E; raise E;
end; end;
on E: Exception do on E: Exception do
@ -582,7 +598,7 @@ var
FunBody: TJSFunctionBody; FunBody: TJSFunctionBody;
InitName: String; InitName: String;
begin begin
FJSModule:=FConverter.ConvertPasElement(Module,nil) as TJSSourceElements; FJSModule:=FConverter.ConvertPasElement(Module,Engine) as TJSSourceElements;
FJSSource:=TStringList.Create; FJSSource:=TStringList.Create;
FJSSource.Text:=JSToStr(JSModule); FJSSource.Text:=JSToStr(JSModule);
writeln('TTestModule.ConvertModule JS:'); writeln('TTestModule.ConvertModule JS:');
@ -809,6 +825,34 @@ begin
until false; until false;
end; end;
procedure TTestModule.WriteSource(aFilename: string; Row: integer; Col: integer
);
var
LR: TLineReader;
CurRow: Integer;
Line: String;
begin
LR:=FileResolver.FindSourceFile(aFilename);
writeln('Testcode:-File="',aFilename,'"----------------------------------:');
if LR=nil then
writeln('Error: file not loaded: "',aFilename,'"')
else
begin
CurRow:=0;
while not LR.IsEOF do
begin
inc(CurRow);
Line:=LR.ReadLine;
if (Row=CurRow) then
begin
write('*');
Line:=LeftStr(Line,Col-1)+'|'+copy(Line,Col,length(Line));
end;
writeln(Format('%:4d: ',[CurRow]),Line);
end;
end;
end;
procedure TTestModule.TestEmptyProgram; procedure TTestModule.TestEmptyProgram;
begin begin
StartProgram(false); StartProgram(false);
@ -1347,7 +1391,7 @@ begin
Add('end;'); Add('end;');
Add('begin'); Add('begin');
ConvertProgram; ConvertProgram;
CheckSource('TestUnitImplVar', CheckSource('TestExit',
LinesToStr([ // statements LinesToStr([ // statements
'this.proca = function () {', 'this.proca = function () {',
' return;', ' return;',
@ -1379,7 +1423,7 @@ begin
Add(' v2:longint = 3;'); Add(' v2:longint = 3;');
Add(' v3:string = ''abc'';'); Add(' v3:string = ''abc'';');
ConvertUnit; ConvertUnit;
CheckSource('TestUnitImplVar', CheckSource('TestUnitImplVars',
LinesToStr([ // statements LinesToStr([ // statements
'var $impl = {', 'var $impl = {',
'};', '};',
@ -1401,7 +1445,7 @@ begin
Add(' v2:longint = 4;'); Add(' v2:longint = 4;');
Add(' v3:string = ''abc'';'); Add(' v3:string = ''abc'';');
ConvertUnit; ConvertUnit;
CheckSource('TestUnitImplVar', CheckSource('TestUnitImplConsts',
LinesToStr([ // statements LinesToStr([ // statements
'var $impl = {', 'var $impl = {',
'};', '};',
@ -1426,7 +1470,7 @@ begin
Add('initialization'); Add('initialization');
Add(' r.i:=3;'); Add(' r.i:=3;');
ConvertUnit; ConvertUnit;
CheckSource('TestUnitImplVar', CheckSource('TestUnitImplRecord',
LinesToStr([ // statements LinesToStr([ // statements
'var $impl = {', 'var $impl = {',
'};', '};',
@ -1458,6 +1502,49 @@ begin
])); ]));
end; end;
procedure TTestModule.TestProc_DefaultValue;
begin
StartProgram(false);
Add('procedure p1(i: longint = 1);');
Add('begin');
Add('end;');
Add('procedure p2(i: longint = 1; c: char = ''a'');');
Add('begin');
Add('end;');
Add('procedure p3(d: double = 1.0; b: boolean = false; s: string = ''abc'');');
Add('begin');
Add('end;');
Add('begin');
Add(' p1;');
Add(' p1();');
Add(' p1(11);');
Add(' p2;');
Add(' p2();');
Add(' p2(12);');
Add(' p2(13,''b'');');
Add(' p3();');
ConvertProgram;
CheckSource('TestProc_DefaultValue',
LinesToStr([ // statements
'this.p1 = function (i) {',
'};',
'this.p2 = function (i,c) {',
'};',
'this.p3 = function (d,b,s) {',
'};'
]),
LinesToStr([ // this.$main
' this.p1(1);',
' this.p1(1);',
' this.p1(11);',
' this.p2(1,"a");',
' this.p2(1,"a");',
' this.p2(12,"a");',
' this.p2(13,"b");',
' this.p3(1.0,false,"abc");'
]));
end;
procedure TTestModule.TestFunctionInt; procedure TTestModule.TestFunctionInt;
begin begin
StartProgram(false); StartProgram(false);
@ -1467,7 +1554,7 @@ begin
Add('end;'); Add('end;');
Add('begin'); Add('begin');
ConvertProgram; ConvertProgram;
CheckSource('TestProcTwoArgs', CheckSource('TestFunctionInt',
LinesToStr([ // statements LinesToStr([ // statements
'this.test = function (a) {', 'this.test = function (a) {',
' var result = 0;', ' var result = 0;',
@ -1489,7 +1576,7 @@ begin
Add('end;'); Add('end;');
Add('begin'); Add('begin');
ConvertProgram; ConvertProgram;
CheckSource('TestProcTwoArgs', CheckSource('TestFunctionString',
LinesToStr([ // statements LinesToStr([ // statements
'this.test = function (a) {', 'this.test = function (a) {',
' var result = "";', ' var result = "";',
@ -1538,7 +1625,7 @@ begin
Add(' j:=j+i;'); Add(' j:=j+i;');
Add(' end;'); Add(' end;');
ConvertProgram; ConvertProgram;
CheckSource('TestVarRecord', CheckSource('TestForLoop',
LinesToStr([ // statements LinesToStr([ // statements
'this.i = 0;', 'this.i = 0;',
'this.j = 0;', 'this.j = 0;',
@ -1547,10 +1634,11 @@ begin
LinesToStr([ // this.$main LinesToStr([ // this.$main
' this.j = 0;', ' this.j = 0;',
' this.n = 3;', ' this.n = 3;',
' this.i = 1;', ' var $loopend1 = this.n;',
' for (var $loopend = this.n; (this.i <= $loopend); this.i++) {', ' for (this.i = 1; (this.i <= $loopend1); this.i++) {',
' this.j = (this.j + this.i);', ' this.j = (this.j + this.i);',
' };' ' };',
' if ((this.i > $loopend1)) this.i--;'
])); ]));
end; end;
@ -1570,15 +1658,15 @@ begin
Add('begin'); Add('begin');
Add(' SumNumbers(3);'); Add(' SumNumbers(3);');
ConvertProgram; ConvertProgram;
CheckSource('TestVarRecord', CheckSource('TestForLoopInFunction',
LinesToStr([ // statements LinesToStr([ // statements
'this.sumnumbers = function (n) {', 'this.sumnumbers = function (n) {',
' var result = 0;', ' var result = 0;',
' var i = 0;', ' var i = 0;',
' var j = 0;', ' var j = 0;',
' j = 0;', ' j = 0;',
' i = 1;', ' var $loopend1 = n;',
' for (var $loopend = n; (i <= $loopend); i++) {', ' for (i = 1; (i <= $loopend1); i++) {',
' j = (j + i);', ' j = (j + i);',
' };', ' };',
' return result;', ' return result;',
@ -1589,6 +1677,69 @@ begin
])); ]));
end; end;
procedure TTestModule.TestForLoop_ReadVarAfter;
begin
StartProgram(false);
Add('var');
Add(' i: longint;');
Add('begin');
Add(' for i:=1 to 2 do ;');
Add(' if i=3 then ;');
ConvertProgram;
CheckSource('TestForLoop',
LinesToStr([ // statements
'this.i = 0;'
]),
LinesToStr([ // this.$main
' var $loopend1 = 2;',
' for (this.i = 1; (this.i <= $loopend1); this.i++);',
' if((this.i>$loopend1))this.i--;',
' if ((this.i==3)){} ;'
]));
end;
procedure TTestModule.TestForLoop_Nested;
begin
StartProgram(false);
Add('function SumNumbers(n: longint): longint;');
Add('var');
Add(' i, j, k: longint;');
Add('begin');
Add(' k:=0;');
Add(' for i:=1 to n do');
Add(' begin');
Add(' for j:=1 to i do');
Add(' begin');
Add(' k:=k+i;');
Add(' end;');
Add(' end;');
Add('end;');
Add('begin');
Add(' SumNumbers(3);');
ConvertProgram;
CheckSource('TestForLoopInFunction',
LinesToStr([ // statements
'this.sumnumbers = function (n) {',
' var result = 0;',
' var i = 0;',
' var j = 0;',
' var k = 0;',
' k = 0;',
' var $loopend1 = n;',
' for (i = 1; (i <= $loopend1); i++) {',
' var $loopend2 = i;',
' for (j = 1; (j <= $loopend2); j++) {',
' k = (k + i);',
' };',
' };',
' return result;',
'};'
]),
LinesToStr([ // this.$main
' this.sumnumbers(3);'
]));
end;
procedure TTestModule.TestRepeatUntil; procedure TTestModule.TestRepeatUntil;
begin begin
StartProgram(false); StartProgram(false);
@ -1603,7 +1754,7 @@ begin
Add(' j:=j+i;'); Add(' j:=j+i;');
Add(' until i>=n'); Add(' until i>=n');
ConvertProgram; ConvertProgram;
CheckSource('TestVarRecord', CheckSource('TestRepeatUntil',
LinesToStr([ // statements LinesToStr([ // statements
'this.i = 0;', 'this.i = 0;',
'this.j = 0;', 'this.j = 0;',
@ -1635,7 +1786,7 @@ begin
Add(' end;'); Add(' end;');
Add(' i:=4;'); Add(' i:=4;');
ConvertProgram; ConvertProgram;
CheckSource('TestAsm', CheckSource('TestAsmBlock',
LinesToStr([ // statements LinesToStr([ // statements
'this.i = 0;' 'this.i = 0;'
]), ]),
@ -1661,7 +1812,7 @@ begin
Add(' i:=3'); Add(' i:=3');
Add(' end;'); Add(' end;');
ConvertProgram; ConvertProgram;
CheckSource('TestVarRecord', CheckSource('TestTryFinally',
LinesToStr([ // statements LinesToStr([ // statements
'this.i = 0;' 'this.i = 0;'
]), ]),
@ -1675,6 +1826,69 @@ begin
])); ]));
end; end;
procedure TTestModule.TestTryExcept;
begin
StartProgram(false);
Add('type');
Add(' TObject = class end;');
Add(' Exception = class Msg: string; end;');
Add(' EInvalidCast = class(Exception) end;');
Add('var i: longint;');
Add('begin');
Add(' try');
Add(' i:=1;');
Add(' except');
Add(' i:=2');
Add(' end;');
Add(' try');
Add(' i:=3;');
Add(' except');
Add(' raise;');
Add(' end;');
Add(' try');
Add(' i:=4;');
Add(' except');
Add(' on EInvalidCast do');
Add(' raise;');
Add(' on E: Exception do');
Add(' if E.msg='''' then');
Add(' raise E;');
Add(' end;');
ConvertProgram;
CheckSource('TestTryExcept',
LinesToStr([ // statements
'rtl.createClass(this, "tobject", null, function () {',
'});',
'rtl.createClass(this, "exception", this.tobject, function () {',
' this.msg = "";',
'});',
'rtl.createClass(this, "einvalidcast", this.exception, function () {',
'});',
'this.i = 0;'
]),
LinesToStr([ // this.$main
'try {',
' this.i = 1;',
'} catch {',
' this.i = 2;',
'};',
'try {',
' this.i = 3;',
'} catch (exceptobject) {',
' throw exceptobject;',
'};',
'try {',
' this.i = 4;',
'} catch (exceptobject) {',
' if (this.einvalidcast.isPrototypeOf(exceptobject)) throw exceptobject;',
' if (this.exception.isPrototypeOf(exceptobject)) {',
' var e = exceptobject;',
' if ((e.msg == "")) throw e;',
' };',
'};'
]));
end;
procedure TTestModule.TestCaseOf; procedure TTestModule.TestCaseOf;
begin begin
StartProgram(false); StartProgram(false);
@ -1687,7 +1901,7 @@ begin
Add(' i:=4'); Add(' i:=4');
Add(' end;'); Add(' end;');
ConvertProgram; ConvertProgram;
CheckSource('TestVarRecord', CheckSource('TestCaseOf',
LinesToStr([ // statements LinesToStr([ // statements
'this.i = 0;' 'this.i = 0;'
]), ]),
@ -1712,7 +1926,7 @@ begin
Add(' i:=4'); Add(' i:=4');
Add(' end;'); Add(' end;');
ConvertProgram; ConvertProgram;
CheckSource('TestVarRecord', CheckSource('TestCaseOf_UseSwitch',
LinesToStr([ // statements LinesToStr([ // statements
'this.i = 0;' 'this.i = 0;'
]), ]),
@ -1738,7 +1952,7 @@ begin
Add(' 1: begin i:=2; i:=3; end;'); Add(' 1: begin i:=2; i:=3; end;');
Add(' end;'); Add(' end;');
ConvertProgram; ConvertProgram;
CheckSource('TestVarRecord', CheckSource('TestCaseOfNoElse',
LinesToStr([ // statements LinesToStr([ // statements
'this.i = 0;' 'this.i = 0;'
]), ]),
@ -1761,7 +1975,7 @@ begin
Add(' 1: begin i:=2; i:=3; end;'); Add(' 1: begin i:=2; i:=3; end;');
Add(' end;'); Add(' end;');
ConvertProgram; ConvertProgram;
CheckSource('TestVarRecord', CheckSource('TestCaseOfNoElse_UseSwitch',
LinesToStr([ // statements LinesToStr([ // statements
'this.i = 0;' 'this.i = 0;'
]), ]),
@ -1787,7 +2001,7 @@ begin
Add(' else ;'); Add(' else ;');
Add(' end;'); Add(' end;');
ConvertProgram; ConvertProgram;
CheckSource('TestVarRecord', CheckSource('TestCaseOfRange',
LinesToStr([ // statements LinesToStr([ // statements
'this.i = 0;' 'this.i = 0;'
]), ]),
@ -1798,6 +2012,390 @@ begin
])); ]));
end; end;
procedure TTestModule.TestClass_TObjectDefaultConstructor;
begin
StartProgram(false);
Add('type');
Add(' TObject = class');
Add(' public');
Add(' constructor Create;');
Add(' destructor Destroy;');
Add(' end;');
Add('constructor TObject.Create;');
Add('begin end;');
Add('destructor TObject.Destroy;');
Add('begin end;');
Add('var o: TObject;');
Add('begin');
Add(' o:=TObject.Create;');
Add(' o.Destroy;');
ConvertProgram;
CheckSource('TestClass_TObjectDefaultConstructor',
LinesToStr([ // statements
'rtl.createClass(this,"tobject",null,function(){',
' this.create = function(){',
' };',
' this.destroy = function(){',
' };',
'});',
'this.o = null;'
]),
LinesToStr([ // this.$main
'this.o = this.tobject.$create("create");',
'this.o.$destroy("destroy");'
]));
end;
procedure TTestModule.TestClass_TObjectConstructorWithParams;
begin
StartProgram(false);
Add('type');
Add(' TObject = class');
Add(' public');
Add(' constructor Create(p: longint);');
Add(' end;');
Add('constructor TObject.Create(p: longint);');
Add('begin end;');
Add('var o: TObject;');
Add('begin');
Add(' o:=TObject.Create(3);');
ConvertProgram;
CheckSource('TestClass_TObjectConstructorWithParams',
LinesToStr([ // statements
'rtl.createClass(this,"tobject",null,function(){',
' this.create = function(p){',
' };',
'});',
'this.o = null;'
]),
LinesToStr([ // this.$main
'this.o = this.tobject.$create("create",[3]);'
]));
end;
procedure TTestModule.TestClass_Var;
begin
StartProgram(false);
Add('type');
Add(' TObject = class');
Add(' public');
Add(' i: longint;');
Add(' constructor Create(p: longint);');
Add(' end;');
Add('constructor TObject.Create(p: longint);');
Add('begin');
Add(' i:=p+3');
Add('end;');
Add('var o: TObject;');
Add('begin');
Add(' o:=TObject.Create(4);');
Add(' o.i:=o.i+5;');
ConvertProgram;
CheckSource('TestClass_Var',
LinesToStr([ // statements
'rtl.createClass(this,"tobject",null,function(){',
' this.i = 0;',
' this.create = function(p){',
' this.i = (p+3);',
' };',
'});',
'this.o = null;'
]),
LinesToStr([ // this.$main
'this.o = this.tobject.$create("create",[4]);',
'this.o.i = (this.o.i + 5);'
]));
end;
procedure TTestModule.TestClass_Method;
begin
StartProgram(false);
Add('type');
Add(' TObject = class');
Add(' public');
Add(' i: longint;');
Add(' Sub: TObject;');
Add(' constructor Create;');
Add(' function GetIt(p: longint): TObject;');
Add(' end;');
Add('constructor TObject.Create; begin end;');
Add('function TObject.GetIt(p: longint): TObject;');
Add('begin');
Add(' Self.i:=p+3;');
Add(' Result:=Self.Sub;');
Add('end;');
Add('var o: TObject;');
Add('begin');
Add(' o:=TObject.Create;');
Add(' o.GetIt(4);');
Add(' o.Sub.Sub:=nil;');
Add(' o.Sub.GetIt(5);');
Add(' o.Sub.GetIt(6).Sub:=nil;');
Add(' o.Sub.GetIt(7).GetIt(8);');
Add(' o.Sub.GetIt(9).Sub.GetIt(10);');
ConvertProgram;
CheckSource('TestClass_Method',
LinesToStr([ // statements
'rtl.createClass(this,"tobject",null,function(){',
' this.i = 0;',
' this.sub = null;',
' this.create = function(){',
' };',
' this.getit = function(p){',
' var result = null;',
' this.i = (p + 3);',
' result = this.sub;',
' return result;',
' };',
'});',
'this.o = null;'
]),
LinesToStr([ // this.$main
'this.o = this.tobject.$create("create");',
'this.o.getit(4);',
'this.o.sub.sub=null;',
'this.o.sub.getit(5);',
'this.o.sub.getit(6).sub=null;',
'this.o.sub.getit(7).getit(8);',
'this.o.sub.getit(9).sub.getit(10);'
]));
end;
procedure TTestModule.TestClass_Inheritance;
begin
StartProgram(false);
Add('type');
Add(' TObject = class');
Add(' public');
Add(' constructor Create;');
Add(' end;');
Add(' TClassA = class');
Add(' end;');
Add(' TClassB = class(TObject)');
Add(' procedure ProcB;');
Add(' end;');
Add('constructor TObject.Create; begin end;');
Add('procedure TClassB.ProcB; begin end;');
Add('var');
Add(' o: TObject;');
Add(' a: TClassA;');
Add(' b: TClassB;');
Add('begin');
Add(' o:=TObject.Create;');
Add(' a:=TClassA.Create;');
Add(' b:=TClassB.Create;');
Add(' if o is TClassA then ;');
Add(' b:=o as TClassB;');
Add(' (o as TClassB).ProcB;');
ConvertProgram;
CheckSource('TestClass_Inheritance',
LinesToStr([ // statements
'rtl.createClass(this,"tobject",null,function(){',
' this.create = function () {',
' };',
'});',
'rtl.createClass(this,"tclassa",this.tobject,function(){',
'});',
'rtl.createClass(this,"tclassb",this.tobject,function(){',
' this.procb = function () {',
' };',
'});',
'this.o = null;',
'this.a = null;',
'this.b = null;'
]),
LinesToStr([ // this.$main
'this.o = this.tobject.$create("create");',
'this.a = this.tclassa.$create("create");',
'this.b = this.tclassb.$create("create");',
'if (this.tclassa.isPrototypeOf(this.o)) {',
'};',
'this.b = rtl.as(this.o, this.tclassb);',
'rtl.as(this.o, this.tclassb).procb();'
]));
end;
procedure TTestModule.TestClass_AbstractMethod;
begin
StartProgram(false);
Add('type');
Add(' TObject = class');
Add(' public');
Add(' procedure DoIt; virtual; abstract;');
Add(' end;');
Add('begin');
ConvertProgram;
CheckSource('TestClass_AbstractMethod',
LinesToStr([ // statements
'rtl.createClass(this,"tobject",null,function(){',
'});'
]),
LinesToStr([ // this.$main
''
]));
end;
procedure TTestModule.TestClass_CallInherited_NoParams;
begin
StartProgram(false);
Add('type');
Add(' TObject = class');
Add(' procedure DoAbstract; virtual; abstract;');
Add(' procedure DoVirtual; virtual;');
Add(' procedure DoIt;');
Add(' end;');
Add(' TA = class');
Add(' procedure DoAbstract; override;');
Add(' procedure DoVirtual; override;');
Add(' procedure DoSome;');
Add(' end;');
Add('procedure TObject.DoVirtual;');
Add('begin');
Add(' inherited; // call non existing ancestor -> ignore silently');
Add('end;');
Add('procedure TObject.DoIt;');
Add('begin');
Add('end;');
Add('procedure TA.DoAbstract;');
Add('begin');
Add(' inherited DoVirtual; // call TObject.DoVirtual');
Add('end;');
Add('procedure TA.DoVirtual;');
Add('begin');
Add(' inherited; // call TObject.DoVirtual');
Add(' inherited DoVirtual; // call TObject.DoVirtual');
Add(' inherited DoVirtual(); // call TObject.DoVirtual');
Add(' DoIt;');
Add(' DoIt();');
Add('end;');
Add('procedure TA.DoSome;');
Add('begin');
Add(' inherited; // call non existing ancestor method -> silently ignore');
Add('end;');
Add('begin');
ConvertProgram;
CheckSource('TestClass_CallInherited_NoParams',
LinesToStr([ // statements
'rtl.createClass(this,"tobject",null,function(){',
' this.dovirtual = function () {',
' };',
' this.doit = function () {',
' };',
'});',
'rtl.createClass(this, "ta", this.tobject, function () {',
' this.doabstract = function () {',
' pas.program.tobject.dovirtual.call(this);',
' };',
' this.dovirtual = function () {',
' pas.program.tobject.dovirtual.apply(this, arguments);',
' pas.program.tobject.dovirtual.call(this);',
' pas.program.tobject.dovirtual.call(this);',
' this.doit();',
' this.doit();',
' };',
' this.dosome = function () {',
' };',
'});'
]),
LinesToStr([ // this.$main
''
]));
end;
procedure TTestModule.TestClass_CallInherited_WithParams;
begin
StartProgram(false);
Add('type');
Add(' TObject = class');
Add(' procedure DoAbstract(a: longint; b: longint = 0); virtual; abstract;');
Add(' procedure DoVirtual(a: longint; b: longint = 0); virtual;');
Add(' procedure DoIt(a: longint; b: longint = 0);');
Add(' procedure DoIt2(a: longint = 1; b: longint = 2);');
Add(' end;');
Add(' TA = class');
Add(' procedure DoAbstract(a: longint; b: longint = 0); override;');
Add(' procedure DoVirtual(a: longint; b: longint = 0); override;');
Add(' end;');
Add('procedure TObject.DoVirtual(a: longint; b: longint = 0);');
Add('begin');
Add('end;');
Add('procedure TObject.DoIt(a: longint; b: longint = 0);');
Add('begin');
Add('end;');
Add('procedure TObject.DoIt2(a: longint; b: longint = 0);');
Add('begin');
Add('end;');
Add('procedure TA.DoAbstract(a: longint; b: longint = 0);');
Add('begin');
Add(' inherited DoVirtual(a,b); // call TObject.DoVirtual(a,b)');
Add(' inherited DoVirtual(a); // call TObject.DoVirtual(a,0)');
Add('end;');
Add('procedure TA.DoVirtual(a: longint; b: longint = 0);');
Add('begin');
Add(' inherited; // call TObject.DoVirtual(a,b)');
Add(' inherited DoVirtual(a,b); // call TObject.DoVirtual(a,b)');
Add(' inherited DoVirtual(a); // call TObject.DoVirtual(a,0)');
Add(' DoIt(a,b);');
Add(' DoIt(a);');
Add(' DoIt2(a);');
Add(' DoIt2;');
Add('end;');
Add('begin');
ConvertProgram;
CheckSource('TestClass_CallInherited_WithParams',
LinesToStr([ // statements
'rtl.createClass(this,"tobject",null,function(){',
' this.dovirtual = function (a,b) {',
' };',
' this.doit = function (a,b) {',
' };',
' this.doit2 = function (a,b) {',
' };',
'});',
'rtl.createClass(this, "ta", this.tobject, function () {',
' this.doabstract = function (a,b) {',
' pas.program.tobject.dovirtual.call(this,a,b);',
' pas.program.tobject.dovirtual.call(this,a,0);',
' };',
' this.dovirtual = function (a,b) {',
' pas.program.tobject.dovirtual.apply(this, arguments);',
' pas.program.tobject.dovirtual.call(this,a,b);',
' pas.program.tobject.dovirtual.call(this,a,0);',
' this.doit(a,b);',
' this.doit(a,0);',
' this.doit2(a,2);',
' this.doit2(1,2);',
' };',
'});'
]),
LinesToStr([ // this.$main
''
]));
end;
procedure TTestModule.TestArray;
begin
StartProgram(false);
Add('type');
Add(' TArrayInt = array of longint;');
Add('var');
Add(' a: TArrayInt;');
Add('begin');
Add(' SetLength(a,3);');
Add(' a[0]:=4;');
Add(' a[1]:=length(a)+a[0];');
ConvertProgram;
CheckSource('TestArray',
LinesToStr([ // statements
'this.a = [];'
]),
LinesToStr([ // this.$main
'rtl.setArrayLength(this.a,3,0);',
'this.a[0]=4;',
'this.a[1]=(rtl.length(this.a)+this.a[0]);'
]));
end;
Initialization Initialization
RegisterTests([TTestModule]); RegisterTests([TTestModule]);
end. end.