From daea654360d791a41fc76ad7d579399af8d3ff57 Mon Sep 17 00:00:00 2001 From: David Novo Date: Mon, 14 Sep 2026 21:22:07 -0700 Subject: [PATCH] Support 'of object' and 'reference to' in procedural types Both variants were parsed correctly but then discarded, leaving the syntax tree unable to distinguish three types Delphi treats as distinct: procedure(...) tkProcedure procedure(...) of object tkMethod reference to procedure(...) tkInterface 'of object' produced a tree byte-identical to a plain procedural type, and 'reference to' produced no TYPE node at all - PARAMETERS hung directly off TYPEDECL. Functions were affected the same way. Both now carry a kind= attribute, following the existing interface / dispinterface precedent: AnonymousMethodType inlined NextToken at the point where the procedure or function token is current, so there was no hook to record which it was. Added AnonymousMethodKind, mirroring the existing MethodKind; its base implementation is just NextToken, so behaviour is unchanged for any existing subclass that does not override it. --- Source/DelphiAST.pas | 32 +++++++++++++++++++++++++++- Source/SimpleParser/SimpleParser.pas | 10 +++++++-- Test/Snippets/proceduraltypes.pas | 24 +++++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 Test/Snippets/proceduraltypes.pas diff --git a/Source/DelphiAST.pas b/Source/DelphiAST.pas index c908cc5..8b411e3 100644 --- a/Source/DelphiAST.pas +++ b/Source/DelphiAST.pas @@ -70,6 +70,8 @@ TPasSyntaxTreeBuilder = class(TmwSimplePasParEx) procedure AddressOp; override; procedure AlignmentParameter; override; procedure AnonymousMethod; override; + procedure AnonymousMethodKind; override; + procedure AnonymousMethodType; override; procedure ArrayBounds; override; procedure ArrayConstant; override; procedure ArrayDimension; override; @@ -175,6 +177,7 @@ TPasSyntaxTreeBuilder = class(TmwSimplePasParEx) procedure ParameterName; override; procedure PointerSymbol; override; procedure PointerType; override; + procedure ProceduralDirectiveOf; override; procedure ProceduralType; override; procedure ProcedureHeading; override; procedure ProcedureDeclarationSection; override; @@ -294,7 +297,8 @@ TStringStreamHelper = class helper for TStringStream type TAttributeValue = (atAsm, atTrue, atFunction, atProcedure, atClassOf, atClass, atConst, atConstructor, atDestructor, atEnum, atInterface, atNil, atNumeric, - atOut, atPointer, atName, atString, atSubRange, atVar, atDispInterface); + atOut, atPointer, atName, atString, atSubRange, atVar, atDispInterface, + atOfObject, atReferenceTo); var AttributeValues: array[TAttributeValue] of string; @@ -455,6 +459,26 @@ procedure TPasSyntaxTreeBuilder.AnonymousMethod; end; end; +procedure TPasSyntaxTreeBuilder.AnonymousMethodKind; +var + value: string; +begin + value := LowerCase(Lexer.Token); + DoHandleString(value); + FStack.Peek.SetAttribute(anName, value); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.AnonymousMethodType; +begin + FStack.Push(ntType).SetAttribute(anKind, AttributeValues[atReferenceTo]); + try + inherited; + finally + FStack.Pop; + end; +end; + procedure TPasSyntaxTreeBuilder.ArrayBounds; begin FStack.Push(ntBounds); @@ -1890,6 +1914,12 @@ procedure TPasSyntaxTreeBuilder.PositionalArgument; end; end; +procedure TPasSyntaxTreeBuilder.ProceduralDirectiveOf; +begin + FStack.Peek.SetAttribute(anKind, AttributeValues[atOfObject]); + inherited; +end; + procedure TPasSyntaxTreeBuilder.ProceduralType; begin FStack.Push(ntType).SetAttribute(anName, Lexer.Token); diff --git a/Source/SimpleParser/SimpleParser.pas b/Source/SimpleParser/SimpleParser.pas index d7efd2c..7ad46fa 100644 --- a/Source/SimpleParser/SimpleParser.pas +++ b/Source/SimpleParser/SimpleParser.pas @@ -242,6 +242,7 @@ TmwSimplePasPar = class(TObject) procedure AncestorIdList; virtual; procedure AncestorId; virtual; procedure AnonymousMethod; virtual; + procedure AnonymousMethodKind; virtual; procedure AnonymousMethodType; virtual; procedure ArrayConstant; virtual; procedure ArrayBounds; virtual; @@ -5591,6 +5592,11 @@ procedure TmwSimplePasPar.AnonymousMethod; Block; end; +procedure TmwSimplePasPar.AnonymousMethodKind; +begin + NextToken; +end; + procedure TmwSimplePasPar.AnonymousMethodType; begin ExpectedEx(ptReference); @@ -5598,13 +5604,13 @@ procedure TmwSimplePasPar.AnonymousMethodType; case TokenID of ptProcedure: begin - NextToken; + AnonymousMethodKind; if TokenID = ptRoundOpen then FormalParameterList; end; ptFunction: begin - NextToken; + AnonymousMethodKind; if TokenID = ptRoundOpen then FormalParameterList; Expected(ptColon); diff --git a/Test/Snippets/proceduraltypes.pas b/Test/Snippets/proceduraltypes.pas new file mode 100644 index 0000000..02d680a --- /dev/null +++ b/Test/Snippets/proceduraltypes.pas @@ -0,0 +1,24 @@ +unit proceduraltypes; + +interface + +type + TPlainProc = procedure(const aX: Integer); + TPlainFunc = function(const aX: Integer): Boolean; + + TMethodProc = procedure(const aX: Integer) of object; + TMethodFunc = function(const aX: Integer): Boolean of object; + + TAnonProc = reference to procedure(const aX: Integer); + TAnonFunc = reference to function(const aX: Integer): Boolean; + + TNoParamsProc = procedure; + TNoParamsMethod = procedure of object; + TNoParamsAnon = reference to procedure; + + TStdCallProc = procedure(const aX: Integer); stdcall; + TStdCallMethod = procedure(const aX: Integer) of object; stdcall; + +implementation + +end.