* Support for generics

git-svn-id: trunk@19621 -
This commit is contained in:
michael 2011-11-11 15:08:17 +00:00
parent 9324726374
commit 5ea8e65ea2
5 changed files with 176 additions and 62 deletions

View File

@ -1439,16 +1439,47 @@ procedure GetTypes(pe:TPasElement; lindent:integer);
if assigned(pc) then if assigned(pc) then
begin begin
s:=GetIndent(indent); s:=GetIndent(indent);
write(s,pc.Name,'='); if (pc.ObjKind=okGeneric) then
begin
write(s,'generic ',pc.Name);
for l:=0 to pc.GenericTemplateTypes.Count-1 do
begin
if l=0 then
Write('<')
else
Write(',');
Write(TPasGenericTemplateType(pc.GenericTemplateTypes[l]).Name);
end;
Write('> = ');
end
else
write(s,pc.Name,' = ');
if pc.IsPacked then write('packed '); if pc.IsPacked then write('packed ');
case pc.ObjKind of case pc.ObjKind of
okObject:write('Object'); okObject:write('Object');
okClass:write('Class'); okClass:write('Class');
okInterface:write('Interface'); okInterface:write('Interface');
okGeneric:write('class');
okspecialize : write('specialize');
end; end;
if assigned(pc.AncestorType) and (pc.AncestorType.ElementTypeName <> '') then if assigned(pc.AncestorType) and (pc.AncestorType.ElementTypeName <> '') then
write('(',pc.AncestorType.Name,')'); begin
if pc.ObjKind<>okspecialize then
write('(',pc.AncestorType.Name,')')
else
begin
write(' ',pc.AncestorType.Name);
for l:=0 to pc.GenericTemplateTypes.Count-1 do
begin
if l=0 then
Write('<')
else
Write(',');
Write(TPasGenericTemplateType(pc.GenericTemplateTypes[l]).Name);
end;
Write('>');
end;
end;
if pc.IsForward or pc.IsShortDefinition then //pparser.pp: 3417 :class(anchestor); is allowed ! if pc.IsForward or pc.IsShortDefinition then //pparser.pp: 3417 :class(anchestor); is allowed !
begin begin
writeln(';'); writeln(';');
@ -1562,6 +1593,7 @@ procedure GetTypes(pe:TPasElement; lindent:integer);
vars.free; vars.free;
end end
else writeln;//(';'); //x=class(y); else writeln;//(';'); //x=class(y);
writeln(s,'end;'); writeln(s,'end;');
end; end;
end; end;

View File

@ -147,6 +147,14 @@ interface
Procedure externalnameProc; external name 'aname'; Procedure externalnameProc; external name 'aname';
Procedure externallibnameProc; external 'alibrary' name 'aname'; Procedure externallibnameProc; external 'alibrary' name 'aname';
Type
generic TFPGListEnumerator<T> = class(TObject)
protected
FList: TFPSList;
FPosition: Integer;
function GetCurrent: T;
end;
TFPGListEnumeratorSpec = specialize TFPGListEnumerator<T>;
Implementation Implementation

View File

@ -45,6 +45,8 @@ resourcestring
SPasTreeObjectType = 'object'; SPasTreeObjectType = 'object';
SPasTreeClassType = 'class'; SPasTreeClassType = 'class';
SPasTreeInterfaceType = 'interface'; SPasTreeInterfaceType = 'interface';
SPasTreeGenericType = 'generic class';
SPasTreeSpecializedType = 'specialized class type';
SPasTreeArgument = 'argument'; SPasTreeArgument = 'argument';
SPasTreeProcedureType = 'procedure type'; SPasTreeProcedureType = 'procedure type';
SPasTreeResultElement = 'function result'; SPasTreeResultElement = 'function result';
@ -453,8 +455,8 @@ type
Variants: TList; // array of TPasVariant elements, may be nil! Variants: TList; // array of TPasVariant elements, may be nil!
end; end;
TPasGenericTemplateType = Class(TPasElement);
TPasObjKind = (okObject, okClass, okInterface); TPasObjKind = (okObject, okClass, okInterface, okGeneric, okSpecialize);
{ TPasClassType } { TPasClassType }
@ -475,8 +477,11 @@ type
ClassVars: TList; // class vars ClassVars: TList; // class vars
Modifiers: TStringList; Modifiers: TStringList;
Interfaces : TList; Interfaces : TList;
GenericTemplateTypes : TList;
end; end;
TArgumentAccess = (argDefault, argConst, argVar, argOut); TArgumentAccess = (argDefault, argConst, argVar, argOut);
{ TPasArgument } { TPasArgument }
@ -1012,7 +1017,7 @@ const
'default', 'private', 'protected', 'public', 'published', 'automated','strict private', 'strict protected'); 'default', 'private', 'protected', 'public', 'published', 'automated','strict private', 'strict protected');
ObjKindNames: array[TPasObjKind] of string = ( ObjKindNames: array[TPasObjKind] of string = (
'object', 'class', 'interface'); 'object', 'class', 'interface','class','class');
OpcodeStrings : Array[TExprOpCode] of string = OpcodeStrings : Array[TExprOpCode] of string =
('','+','-','*','/','div','mod','**', ('','+','-','*','/','div','mod','**',
@ -1081,6 +1086,8 @@ begin
okObject: Result := SPasTreeObjectType; okObject: Result := SPasTreeObjectType;
okClass: Result := SPasTreeClassType; okClass: Result := SPasTreeClassType;
okInterface: Result := SPasTreeInterfaceType; okInterface: Result := SPasTreeInterfaceType;
okGeneric : Result := SPasTreeGenericType;
okSpecialize : Result := SPasTreeSpecializedType;
end; end;
end; end;
@ -1366,6 +1373,8 @@ begin
Modifiers := TStringList.Create; Modifiers := TStringList.Create;
ClassVars := TList.Create; ClassVars := TList.Create;
Interfaces:= TList.Create; Interfaces:= TList.Create;
GenericTemplateTypes:=TList.Create;
end; end;
destructor TPasClassType.Destroy; destructor TPasClassType.Destroy;
@ -1380,6 +1389,9 @@ begin
Modifiers.Free; Modifiers.Free;
ClassVars.Free; ClassVars.Free;
Interfaces.Free; Interfaces.Free;
for i := 0 to GenericTemplateTypes.Count - 1 do
TPasElement(GenericTemplateTypes[i]).Release;
GenericTemplateTypes.Free;
inherited Destroy; inherited Destroy;
end; end;

View File

@ -30,6 +30,7 @@ resourcestring
SParserErrorAtToken = '%s at token "%s" in file %s at line %d column %d'; SParserErrorAtToken = '%s at token "%s" in file %s at line %d column %d';
SParserUngetTokenError = 'Internal error: Cannot unget more tokens, history buffer is full'; SParserUngetTokenError = 'Internal error: Cannot unget more tokens, history buffer is full';
SParserExpectTokenError = 'Expected "%s"'; SParserExpectTokenError = 'Expected "%s"';
SParserExpectToken2Error = 'Expected "%s" or "%s"';
SParserExpectedCommaRBracket = 'Expected "," or ")"'; SParserExpectedCommaRBracket = 'Expected "," or ")"';
SParserExpectedCommaSemicolon = 'Expected "," or ";"'; SParserExpectedCommaSemicolon = 'Expected "," or ";"';
SParserExpectedCommaColon = 'Expected "," or ":"'; SParserExpectedCommaColon = 'Expected "," or ":"';
@ -118,7 +119,9 @@ type
FTokenStringBuffer: array[0..1] of String; FTokenStringBuffer: array[0..1] of String;
FTokenBufferIndex: Integer; // current index in FTokenBuffer FTokenBufferIndex: Integer; // current index in FTokenBuffer
FTokenBufferSize: Integer; // maximum valid index in FTokenBuffer FTokenBufferSize: Integer; // maximum valid index in FTokenBuffer
procedure DoParseClassType(AType: TPasClassType; SourceFileName: String; SourceLineNumber: Integer);
procedure ParseExc(const Msg: String); procedure ParseExc(const Msg: String);
procedure ReadGenericArguments(List : TList;Parent : TPasElement; IsSpecialize : Boolean);
protected protected
function OpLevel(t: TToken): Integer; function OpLevel(t: TToken): Integer;
Function TokenToExprOp (AToken : TToken) : TExprOpCode; Function TokenToExprOp (AToken : TToken) : TExprOpCode;
@ -181,8 +184,7 @@ type
function ParseProcedureOrFunctionDecl(Parent: TPasElement; function ParseProcedureOrFunctionDecl(Parent: TPasElement;
ProcType: TProcType): TPasProcedure; ProcType: TProcType): TPasProcedure;
procedure ParseRecordDecl(Parent: TPasRecordType; IsNested: Boolean); // !!!: Optimize this. We have 3x the same wrapper code around it. procedure ParseRecordDecl(Parent: TPasRecordType; IsNested: Boolean); // !!!: Optimize this. We have 3x the same wrapper code around it.
function ParseClassDecl(Parent: TPasElement; const AClassName: String; function ParseClassDecl(Parent: TPasElement; const AClassName: String; AObjKind: TPasObjKind): TPasType;
AObjKind: TPasObjKind): TPasType;
procedure ParseProperty(Element:TPasElement); procedure ParseProperty(Element:TPasElement);
procedure ParseProcBeginBlock(Parent: TProcedureBody); procedure ParseProcBeginBlock(Parent: TProcedureBody);
procedure ParseStatement(Parent: TPasImplBlock; procedure ParseStatement(Parent: TPasImplBlock;
@ -1408,6 +1410,7 @@ var
i,j: Integer; i,j: Integer;
VarEl: TPasVariable; VarEl: TPasVariable;
PropEl : TPasProperty; PropEl : TPasProperty;
TypeName: String;
begin begin
CurBlock := declNone; CurBlock := declNone;
while True do while True do
@ -1592,6 +1595,26 @@ begin
ParseExc(SParserSyntaxError); ParseExc(SParserSyntaxError);
end; end;
end; end;
tkGeneric:
begin
if CurBlock <> declType then
ParseExc(SParserSyntaxError);
TypeName := ExpectIdentifier;
ClassEl := TPasClassType(Engine.CreateElement(TPasClassType,TypeName,Declarations, Scanner.CurFilename, Scanner.CurRow));
ClassEl.ObjKind:=okGeneric;
try
ReadGenericArguments(ClassEl.GenericTemplateTypes,ClassEl,False);
Except
List.Free;
Raise;
end;
ExpectToken(tkEqual);
ExpectToken(tkClass);
NextToken;
DoParseClassType(ClassEl, Scanner.CurFilename, Scanner.CurRow);
Declarations.Declarations.Add(ClassEl);
Declarations.Classes.Add(ClassEl)
end;
tkbegin: tkbegin:
begin begin
if Declarations is TProcedureBody then if Declarations is TProcedureBody then
@ -1705,6 +1728,23 @@ begin
end; end;
end; end;
procedure TPasParser.ReadGenericArguments(List : TList;Parent : TPasElement; IsSpecialize : Boolean);
Var
N : String;
begin
ExpectToken(tkLessThan);
repeat
N:=ExpectIdentifier;
List.Add(CreateElement(TPasGenericTemplateType,N,Parent));
NextToken;
if not (CurToken in [tkComma, tkGreaterThan]) then
ParseExc(Format(SParserExpectToken2Error,
[TokenInfos[tkComma], TokenInfos[tkGreaterThan]]));
until CurToken = tkGreaterThan;
end;
// Starts after the type name // Starts after the type name
function TPasParser.ParseTypeDecl(Parent: TPasElement): TPasType; function TPasParser.ParseTypeDecl(Parent: TPasElement): TPasType;
var var
@ -1935,6 +1975,16 @@ begin
raise; raise;
end; end;
end; end;
tkSpecialize:
begin
Result := TPasClassType(Engine.CreateElement(TPasClassType, TypeName,
Parent, Scanner.CurFilename, Scanner.CurRow));
TPasClassType(Result).ObjKind := okSpecialize;
TPasClassType(Result).AncestorType := ParseType(nil);
TPasClassType(Result).IsShortDefinition:=True;
ReadGenericArguments(TPasClassType(Result).GenericTemplateTypes,Result,True);
ExpectToken(tkSemicolon);
end;
else else
begin begin
UngetToken; UngetToken;
@ -3187,8 +3237,8 @@ begin
end; end;
// Starts after the "class" token // Starts after the "class" token
function TPasParser.ParseClassDecl(Parent: TPasElement; Procedure TPasParser.DoParseClassType(AType : TPasClassType; SourceFileName : String; SourceLineNumber : Integer);
const AClassName: String; AObjKind: TPasObjKind): TPasType;
var var
CurVisibility: TPasMemberVisibility; CurVisibility: TPasMemberVisibility;
@ -3204,7 +3254,7 @@ var
HasReturnValue:=false; HasReturnValue:=false;
ExpectIdentifier; ExpectIdentifier;
Name := CurTokenString; Name := CurTokenString;
Owner := CheckIfOverloaded(TPasClassType(Result), Name); Owner := CheckIfOverloaded(AType, Name);
case ProcType of case ProcType of
ptFunction: ptFunction:
begin begin
@ -3251,7 +3301,7 @@ var
if Owner.ClassType = TPasOverloadedProc then if Owner.ClassType = TPasOverloadedProc then
TPasOverloadedProc(Owner).Overloads.Add(Proc) TPasOverloadedProc(Owner).Overloads.Add(Proc)
else else
TPasClassType(Result).Members.Add(Proc); AType.Members.Add(Proc);
if HasReturnValue then if HasReturnValue then
pt := ptFunction pt := ptFunction
@ -3321,43 +3371,18 @@ var
end; end;
var var
s, SourceFilename: String; s: String;
i, SourceLinenumber: Integer; i: Integer;
VarList: TList; VarList: TList;
Element: TPasElement; Element: TPasElement;
isStrict: Boolean; isStrict: Boolean;
begin begin
isStrict:=False; isStrict:=False;
// Save current parsing position to get it correct in all cases
SourceFilename := Scanner.CurFilename;
SourceLinenumber := Scanner.CurRow;
NextToken;
if (AObjKind = okClass) and (CurToken = tkOf) then
begin
Result := TPasClassOfType(Engine.CreateElement(TPasClassOfType, AClassName,
Parent, SourceFilename, SourceLinenumber));
ExpectIdentifier;
UngetToken; // Only names are allowed as following type
TPasClassOfType(Result).DestType := ParseType(Result);
ExpectToken(tkSemicolon);
exit;
end;
Result := TPasClassType(Engine.CreateElement(TPasClassType, AClassName,
Parent, SourceFilename, SourceLinenumber));
try
TPasClassType(Result).ObjKind := AObjKind;
// nettism/new delphi features // nettism/new delphi features
if (CurToken = tkIdentifier) and (AObjKind = okClass) then begin if (CurToken = tkIdentifier) and (Atype.ObjKind in [okClass,okGeneric]) then begin
s := LowerCase(CurTokenString); s := LowerCase(CurTokenString);
if (s = 'sealed') or (s = 'abstract') then begin if (s = 'sealed') or (s = 'abstract') then begin
TPasClassType(Result).Modifiers.Add(s); AType.Modifiers.Add(s);
NextToken; NextToken;
end; end;
end; end;
@ -3365,12 +3390,12 @@ begin
// Parse ancestor list // Parse ancestor list
if CurToken = tkBraceOpen then if CurToken = tkBraceOpen then
begin begin
TPasClassType(Result).AncestorType := ParseType(nil); AType.AncestorType := ParseType(nil);
{$ifdef Inheritancewarnings} {$ifdef Inheritancewarnings}
s:=TPasClassType(Result).AncestorType.pathname; s:=AType.AncestorType.pathname;
if pos('#',s)=0 then if pos('#',s)=0 then
begin begin
writeln('Note: ', TPasClassType(Result).pathname,'''s ancestor ',s, ' at ',sourcefilename,':',sourcelinenumber,' cannot be resolved fully'); writeln('Note: ', AType.pathname,'''s ancestor ',s, ' at ',sourcefilename,':',sourcelinenumber,' cannot be resolved fully');
end; end;
{$endif} {$endif}
while True do while True do
@ -3383,22 +3408,22 @@ begin
//ExpectIdentifier; //ExpectIdentifier;
Element:=ParseType(Nil); // search interface. Element:=ParseType(Nil); // search interface.
if assigned(element) then if assigned(element) then
TPasClassType(Result).Interfaces.add(element); AType.Interfaces.add(element);
// !!!: Store interface name // !!!: Store interface name
end; end;
NextToken; NextToken;
end end
else else
TPasClassType(Result).isForward:=CurToken=tkSemicolon; Atype.isForward:=CurToken=tkSemicolon;
if CurToken = tkSemicolon then if CurToken = tkSemicolon then
TPasClassType(Result).IsShortDefinition:=true; AType.IsShortDefinition:=true;
if CurToken <> tkSemicolon then if CurToken <> tkSemicolon then
begin begin
if ( AObjKind = okInterface ) and ( CurToken = tkSquaredBraceOpen ) then if ( AType.ObjKind = okInterface ) and ( CurToken = tkSquaredBraceOpen ) then
begin begin
ExpectToken(tkString); ExpectToken(tkString);
TPasClassType(Result).InterfaceGUID := CurTokenString; AType.InterfaceGUID := CurTokenString;
ExpectToken(tkSquaredBraceClose); ExpectToken(tkSquaredBraceClose);
end; end;
CurVisibility := visDefault; CurVisibility := visDefault;
@ -3431,12 +3456,12 @@ begin
begin begin
VarList := TList.Create; VarList := TList.Create;
try try
ParseInlineVarDecl(Result, VarList, CurVisibility, False); ParseInlineVarDecl(AType, VarList, CurVisibility, False);
for i := 0 to VarList.Count - 1 do for i := 0 to VarList.Count - 1 do
begin begin
Element := TPasElement(VarList[i]); Element := TPasElement(VarList[i]);
Element.Visibility := CurVisibility; Element.Visibility := CurVisibility;
TPasClassType(Result).Members.Add(Element); AType.Members.Add(Element);
end; end;
finally finally
VarList.Free; VarList.Free;
@ -3470,20 +3495,53 @@ begin
tkProperty: tkProperty:
begin begin
ExpectIdentifier; ExpectIdentifier;
Element := CreateElement(TPasProperty, CurTokenString, Result, CurVisibility); Element := CreateElement(TPasProperty, CurTokenString, AType, CurVisibility);
TPasClassType(Result).Members.Add(Element); AType.Members.Add(Element);
ParseProperty(Element); ParseProperty(Element);
end; end;
tkVar: // vars (nettism/new delphi features) tkVar: // vars (nettism/new delphi features)
if AObjKind<>okClass then ExpectToken(tkSemicolon); if (not (AType.ObjKind in [okClass,okGeneric])) then
ExpectToken(tkSemicolon);
//todo: class vars //todo: class vars
end; // end case end; // end case
NextToken; NextToken;
end; end;
// Eat semicolon after class...end // Eat semicolon after class...end
CheckHint(result,true); CheckHint(AType,true);
// ExpectToken(tkSemicolon); // ExpectToken(tkSemicolon);
end; end;
end;
function TPasParser.ParseClassDecl(Parent: TPasElement; const AClassName: String; AObjKind: TPasObjKind): TPasType;
Var
SourcefileName : string;
SourceLineNumber : Integer;
begin
// Save current parsing position to get it correct in all cases
SourceFilename := Scanner.CurFilename;
SourceLinenumber := Scanner.CurRow;
NextToken;
if (AObjKind = okClass) and (CurToken = tkOf) then
begin
Result := TPasClassOfType(Engine.CreateElement(TPasClassOfType, AClassName,
Parent, SourceFilename, SourceLinenumber));
ExpectIdentifier;
UngetToken; // Only names are allowed as following type
TPasClassOfType(Result).DestType := ParseType(Result);
ExpectToken(tkSemicolon);
exit;
end;
Result := TPasClassType(Engine.CreateElement(TPasClassType, AClassName,
Parent, SourceFilename, SourceLinenumber));
try
TPasClassType(Result).ObjKind := AObjKind;
DoParseClassType(TPasClassType(Result),SourceFileName,SourceLineNumber);
except except
Result.Free; Result.Free;
raise; raise;

View File

@ -94,6 +94,7 @@ type
tkfinally, tkfinally,
tkfor, tkfor,
tkfunction, tkfunction,
tkgeneric,
tkgoto, tkgoto,
tkif, tkif,
tkimplementation, tkimplementation,
@ -125,6 +126,7 @@ type
tkset, tkset,
tkshl, tkshl,
tkshr, tkshr,
tkspecialize,
// tkstring, // tkstring,
tkthen, tkthen,
tkthreadvar, tkthreadvar,
@ -298,6 +300,7 @@ const
'finally', 'finally',
'for', 'for',
'function', 'function',
'generic',
'goto', 'goto',
'if', 'if',
'implementation', 'implementation',
@ -329,6 +332,7 @@ const
'set', 'set',
'shl', 'shl',
'shr', 'shr',
'specialize',
// 'string', // 'string',
'then', 'then',
'threadvar', 'threadvar',