Binding
This commit is contained in:
@@ -88,8 +88,8 @@ type
|
||||
// The signature for a native Delphi function callable from the script.
|
||||
TNativeFunction = function(const Args: TArray<TAstValue>): TAstValue;
|
||||
|
||||
// Concrete implementation of the IEvaluatorClosure interface for script-defined functions.
|
||||
TClosureValue = class(TInterfacedObject, IEvaluatorClosure)
|
||||
// Concrete implementation of the TAstValue.IClosure interface for script-defined functions.
|
||||
TClosureValue = class(TInterfacedObject, TAstValue.IClosure)
|
||||
private
|
||||
FBody: IExpressionNode;
|
||||
FClosureScope: IExecutionScope;
|
||||
@@ -97,15 +97,14 @@ type
|
||||
function GetBody: IExpressionNode;
|
||||
function GetParameters: TArray<IIdentifierNode>;
|
||||
function GetClosureScope: IExecutionScope;
|
||||
function GetLambdaNode: ILambdaExpressionNode;
|
||||
public
|
||||
constructor Create(const ALambdaNode: ILambdaExpressionNode; const AClosureScope: IExecutionScope);
|
||||
property ClosureScope: IExecutionScope read GetClosureScope;
|
||||
property LambdaNode: ILambdaExpressionNode read GetLambdaNode;
|
||||
property LambdaNode: ILambdaExpressionNode read FLambdaNode;
|
||||
end;
|
||||
|
||||
// Concrete implementation of IEvaluatorClosure for native Delphi functions.
|
||||
TNativeClosure = class(TInterfacedObject, IEvaluatorClosure)
|
||||
// Concrete implementation of TAstValue.IClosure for native Delphi functions.
|
||||
TNativeClosure = class(TInterfacedObject, TAstValue.IClosure)
|
||||
private
|
||||
FMethod: TNativeFunction;
|
||||
function GetBody: IExpressionNode;
|
||||
@@ -196,7 +195,7 @@ end;
|
||||
|
||||
procedure RegisterNativeFunctions(const AScope: IExecutionScope);
|
||||
var
|
||||
closure: IEvaluatorClosure;
|
||||
closure: TAstValue.IClosure;
|
||||
begin
|
||||
// Use 'Define' to clearly state that we are adding a new variable
|
||||
// to the global scope before the binder runs.
|
||||
@@ -224,11 +223,6 @@ begin
|
||||
Result := FClosureScope;
|
||||
end;
|
||||
|
||||
function TClosureValue.GetLambdaNode: ILambdaExpressionNode;
|
||||
begin
|
||||
Result := FLambdaNode;
|
||||
end;
|
||||
|
||||
function TClosureValue.GetParameters: TArray<IIdentifierNode>;
|
||||
begin
|
||||
Result := FLambdaNode.Parameters;
|
||||
@@ -294,7 +288,7 @@ var
|
||||
lookback: Int64;
|
||||
begin
|
||||
// The target series must have been resolved by the binder.
|
||||
seriesVar := FScope[Node.Series.Resolve];
|
||||
seriesVar := FScope[Node.Series.Address];
|
||||
itemValue := Node.Value.Accept(Self);
|
||||
lookback := -1;
|
||||
|
||||
@@ -345,7 +339,7 @@ end;
|
||||
function TEvaluatorVisitor.VisitAssignment(const Node: IAssignmentNode): TAstValue;
|
||||
begin
|
||||
Result := Node.Value.Accept(Self);
|
||||
FScope[Node.Identifier.Resolve] := Result;
|
||||
FScope[Node.Identifier.Address] := Result;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TAstValue;
|
||||
@@ -378,7 +372,7 @@ end;
|
||||
|
||||
function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TAstValue;
|
||||
begin
|
||||
Result := FScope[Node.Resolve];
|
||||
Result := FScope[Node.Address];
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TAstValue;
|
||||
@@ -475,7 +469,7 @@ begin
|
||||
else
|
||||
value := TAstValue.Void;
|
||||
|
||||
FScope[Node.Identifier.Resolve] := value;
|
||||
FScope[Node.Identifier.Address] := value;
|
||||
|
||||
Result := value;
|
||||
end;
|
||||
@@ -488,10 +482,8 @@ end;
|
||||
function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TAstValue;
|
||||
var
|
||||
calleeValue: TAstValue;
|
||||
closure: IEvaluatorClosure;
|
||||
closure: TAstValue.IClosure;
|
||||
i: Integer;
|
||||
callScope: IExecutionScope;
|
||||
descriptor: IScopeDescriptor;
|
||||
begin
|
||||
calleeValue := Node.Callee.Accept(Self);
|
||||
|
||||
@@ -514,20 +506,20 @@ begin
|
||||
raise EArgumentException
|
||||
.CreateFmt('Argument count mismatch: expected %d, got %d', [Length(closure.Parameters), Node.Arguments.Count]);
|
||||
|
||||
descriptor := (closure as TClosureValue).LambdaNode.ScopeDescriptor;
|
||||
var descriptor := (closure as TClosureValue).LambdaNode.ScopeDescriptor;
|
||||
if not Assigned(descriptor) then
|
||||
raise EParserError.Create('Lambda has no scope descriptor. Did the binder run?');
|
||||
|
||||
var callScope := descriptor.CreateScope(closure.ClosureScope);
|
||||
|
||||
var adr: TResolvedAddress;
|
||||
adr.ScopeDepth := 0;
|
||||
adr.SlotIndex := 0;
|
||||
|
||||
callScope := descriptor.Instantiate(closure.ClosureScope);
|
||||
callScope[adr] := calleeValue;
|
||||
|
||||
for i := 0 to Node.Arguments.Count - 1 do
|
||||
begin
|
||||
adr.SlotIndex := closure.Parameters[i].Resolve.SlotIndex;
|
||||
adr.SlotIndex := closure.Parameters[i].Address.SlotIndex;
|
||||
callScope[adr] := Node.Arguments[i].Accept(Self);
|
||||
end;
|
||||
|
||||
|
||||
+30
-37
@@ -44,36 +44,30 @@ type
|
||||
ICreateSeriesNode = interface;
|
||||
IAddSeriesItemNode = interface;
|
||||
ISeriesLengthNode = interface;
|
||||
IEvaluatorClosure = interface;
|
||||
IExecutionScope = interface;
|
||||
IScopeDescriptor = interface;
|
||||
|
||||
// --- Concrete Type Definitions ---
|
||||
|
||||
TResolvedAddress = record
|
||||
ScopeDepth: Integer;
|
||||
SlotIndex: Integer;
|
||||
end;
|
||||
|
||||
IEvaluatorClosure = interface
|
||||
{$region 'private'}
|
||||
function GetBody: IExpressionNode;
|
||||
function GetClosureScope: IExecutionScope;
|
||||
function GetParameters: TArray<IIdentifierNode>;
|
||||
{$endregion}
|
||||
property Body: IExpressionNode read GetBody;
|
||||
property ClosureScope: IExecutionScope read GetClosureScope;
|
||||
property Parameters: TArray<IIdentifierNode> read GetParameters;
|
||||
end;
|
||||
|
||||
TAstValue = record
|
||||
private
|
||||
type
|
||||
IClosure = interface
|
||||
{$region 'private'}
|
||||
function GetBody: IExpressionNode;
|
||||
function GetClosureScope: IExecutionScope;
|
||||
function GetParameters: TArray<IIdentifierNode>;
|
||||
{$endregion}
|
||||
property Body: IExpressionNode read GetBody;
|
||||
property ClosureScope: IExecutionScope read GetClosureScope;
|
||||
property Parameters: TArray<IIdentifierNode> read GetParameters;
|
||||
end;
|
||||
|
||||
TVal<T> = class(TInterfacedObject)
|
||||
Value: T;
|
||||
constructor Create(const AValue: T);
|
||||
end;
|
||||
|
||||
private
|
||||
var
|
||||
FKind: TAstValueKind;
|
||||
FScalar: TScalar;
|
||||
@@ -84,14 +78,14 @@ type
|
||||
class operator Initialize(out Dest: TAstValue);
|
||||
class function Void: TAstValue; inline; static;
|
||||
class function FromScalar(const AValue: TScalar): TAstValue; inline; static;
|
||||
class function FromClosure(const AValue: IEvaluatorClosure): TAstValue; inline; static;
|
||||
class function FromClosure(const AValue: TAstValue.IClosure): TAstValue; inline; static;
|
||||
class function FromText(const AValue: String): TAstValue; inline; static;
|
||||
class function FromSeries(const [ref] AValue: TScalarSeries): TAstValue; static; inline;
|
||||
class function FromRecordSeries(const [ref] AValue: TScalarRecordSeries): TAstValue; static; inline;
|
||||
class function FromRecord(const [ref] AValue: TScalarRecord): TAstValue; static; inline;
|
||||
class function FromMemberSeries(const [ref] AValue: TScalarMemberSeries): TAstValue; static; inline;
|
||||
function AsScalar: TScalar; inline;
|
||||
function AsClosure: IEvaluatorClosure; inline;
|
||||
function AsClosure: TAstValue.IClosure; inline;
|
||||
function AsText: String; inline;
|
||||
function AsRecordSeries: TVal<TScalarRecordSeries>; inline;
|
||||
function AsRecord: TScalarRecord; inline;
|
||||
@@ -102,25 +96,24 @@ type
|
||||
property Kind: TAstValueKind read GetKind;
|
||||
end;
|
||||
|
||||
TResolvedAddress = record
|
||||
ScopeDepth: Integer;
|
||||
SlotIndex: Integer;
|
||||
end;
|
||||
|
||||
IExecutionScope = interface
|
||||
{$region 'private'}
|
||||
function GetParent: IExecutionScope;
|
||||
function GetValues(const Address: TResolvedAddress): TAstValue;
|
||||
procedure SetValues(const Address: TResolvedAddress; const Value: TAstValue);
|
||||
{$endregion}
|
||||
|
||||
procedure Define(const Name: string; const Value: TAstValue);
|
||||
function Dump: string;
|
||||
procedure Clear;
|
||||
|
||||
// --- Legacy name-based access ---
|
||||
function FindValue(const Name: string; out Value: TAstValue): Boolean;
|
||||
procedure SetValue(const Name: string; const Value: TAstValue);
|
||||
procedure AssignValue(const Name: string; const Value: TAstValue); overload;
|
||||
|
||||
// --- New index-based access ---
|
||||
procedure Define(const Name: string; const Value: TAstValue);
|
||||
|
||||
function Dump: string;
|
||||
|
||||
property Values[const Address: TResolvedAddress]: TAstValue read GetValues write SetValues; default;
|
||||
|
||||
property Parent: IExecutionScope read GetParent;
|
||||
end;
|
||||
|
||||
@@ -130,8 +123,7 @@ type
|
||||
function GetSlotCount: Integer;
|
||||
function GetSymbols: TDictionary<string, Integer>;
|
||||
{$endregion}
|
||||
function Instantiate(const AParent: IExecutionScope): IExecutionScope;
|
||||
procedure PopulateFromScope(const AScope: IExecutionScope);
|
||||
function CreateScope(const AParent: IExecutionScope): IExecutionScope;
|
||||
property Parent: IScopeDescriptor read GetParent;
|
||||
property SlotCount: Integer read GetSlotCount;
|
||||
property Symbols: TDictionary<string, Integer> read GetSymbols;
|
||||
@@ -173,11 +165,12 @@ type
|
||||
IIdentifierNode = interface(IExpressionNode)
|
||||
{$region 'private'}
|
||||
function GetIsResolved: Boolean;
|
||||
function GetAddress: TResolvedAddress;
|
||||
function GetName: string;
|
||||
{$endregion}
|
||||
function Resolve: TResolvedAddress;
|
||||
property IsResolved: Boolean read GetIsResolved;
|
||||
property Name: string read GetName;
|
||||
property IsResolved: Boolean read GetIsResolved;
|
||||
property Address: TResolvedAddress read GetAddress;
|
||||
end;
|
||||
|
||||
IBinaryExpressionNode = interface(IExpressionNode)
|
||||
@@ -337,11 +330,11 @@ begin
|
||||
Dest.FInterface := nil;
|
||||
end;
|
||||
|
||||
function TAstValue.AsClosure: IEvaluatorClosure;
|
||||
function TAstValue.AsClosure: TAstValue.IClosure;
|
||||
begin
|
||||
if (FKind <> avkClosure) then
|
||||
raise EInvalidCast.Create('Cannot read value as a Closure.');
|
||||
Result := IEvaluatorClosure(FInterface);
|
||||
Result := TAstValue.IClosure(FInterface);
|
||||
end;
|
||||
|
||||
function TAstValue.AsMemberSeries: TVal<TScalarMemberSeries>;
|
||||
@@ -388,7 +381,7 @@ begin
|
||||
Result := (FInterface as TVal<String>).Value;
|
||||
end;
|
||||
|
||||
class function TAstValue.FromClosure(const AValue: IEvaluatorClosure): TAstValue;
|
||||
class function TAstValue.FromClosure(const AValue: TAstValue.IClosure): TAstValue;
|
||||
begin
|
||||
Result.FKind := avkClosure;
|
||||
Result.FInterface := AValue;
|
||||
|
||||
+10
-57
@@ -17,19 +17,13 @@ type
|
||||
procedure DumpScope(const ABuilder: TStringBuilder; AIndent: Integer);
|
||||
function GetValues(const Address: TResolvedAddress): TAstValue;
|
||||
procedure SetValues(const Address: TResolvedAddress; const Value: TAstValue);
|
||||
protected
|
||||
// IExecutionScope
|
||||
function GetParent: IExecutionScope;
|
||||
public
|
||||
constructor Create(AParent: IExecutionScope = nil; const ADescriptor: IScopeDescriptor = nil);
|
||||
destructor Destroy; override;
|
||||
procedure Clear;
|
||||
function FindValue(const Name: string; out Value: TAstValue): Boolean;
|
||||
procedure SetValue(const Name: string; const Value: TAstValue);
|
||||
procedure AssignValue(const Name: string; const Value: TAstValue); overload;
|
||||
function Dump: string;
|
||||
procedure Define(const Name: string; const Value: TAstValue);
|
||||
public
|
||||
constructor Create(AParent: IExecutionScope = nil);
|
||||
constructor CreateFromDescriptor(const AParent: IExecutionScope; const ADescriptor: IScopeDescriptor);
|
||||
destructor Destroy; override;
|
||||
property NameToIndex: TDictionary<string, Integer> read FNameToIndex;
|
||||
property Parent: IExecutionScope read FParent;
|
||||
property Values: TArray<TAstValue> read FValues;
|
||||
@@ -42,20 +36,18 @@ uses
|
||||
|
||||
{ TExecutionScope }
|
||||
|
||||
constructor TExecutionScope.Create(AParent: IExecutionScope);
|
||||
constructor TExecutionScope.Create(AParent: IExecutionScope = nil; const ADescriptor: IScopeDescriptor = nil);
|
||||
begin
|
||||
inherited Create;
|
||||
FParent := AParent;
|
||||
FValues := [];
|
||||
FNameToIndex := TDictionary<string, Integer>.Create;
|
||||
end;
|
||||
|
||||
constructor TExecutionScope.CreateFromDescriptor(const AParent: IExecutionScope; const ADescriptor: IScopeDescriptor);
|
||||
begin
|
||||
inherited Create;
|
||||
FParent := AParent;
|
||||
FNameToIndex := TDictionary<string, Integer>.Create(ADescriptor.Symbols);
|
||||
SetLength(FValues, ADescriptor.SlotCount);
|
||||
if ADescriptor <> nil then
|
||||
begin
|
||||
for var item in ADescriptor.Symbols do
|
||||
FNameToIndex.AddOrSetValue(item.Key, item.Value);
|
||||
SetLength(FValues, ADescriptor.SlotCount);
|
||||
end;
|
||||
end;
|
||||
|
||||
destructor TExecutionScope.Destroy;
|
||||
@@ -69,18 +61,6 @@ begin
|
||||
Result := FParent;
|
||||
end;
|
||||
|
||||
procedure TExecutionScope.AssignValue(const Name: string; const Value: TAstValue);
|
||||
var
|
||||
index: Integer;
|
||||
begin
|
||||
if FNameToIndex.TryGetValue(Name, index) then
|
||||
FValues[index] := Value
|
||||
else if Assigned(FParent) then
|
||||
FParent.AssignValue(Name, Value)
|
||||
else
|
||||
raise Exception.CreateFmt('Cannot assign to undeclared variable "%s".', [Name]);
|
||||
end;
|
||||
|
||||
procedure TExecutionScope.Clear;
|
||||
begin
|
||||
FValues := [];
|
||||
@@ -145,23 +125,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TExecutionScope.FindValue(const Name: string; out Value: TAstValue): Boolean;
|
||||
var
|
||||
index: Integer;
|
||||
begin
|
||||
if FNameToIndex.TryGetValue(Name, index) then
|
||||
begin
|
||||
Value := FValues[index];
|
||||
Result := True;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if Assigned(FParent) then
|
||||
Result := FParent.FindValue(Name, Value)
|
||||
else
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
function TExecutionScope.GetValues(const Address: TResolvedAddress): TAstValue;
|
||||
var
|
||||
targetScope: TExecutionScope;
|
||||
@@ -179,16 +142,6 @@ begin
|
||||
Result := targetScope.Values[Address.SlotIndex];
|
||||
end;
|
||||
|
||||
procedure TExecutionScope.SetValue(const Name: string; const Value: TAstValue);
|
||||
var
|
||||
index: Integer;
|
||||
begin
|
||||
if FNameToIndex.TryGetValue(Name, index) then
|
||||
FValues[index] := Value
|
||||
else
|
||||
Define(Name, Value);
|
||||
end;
|
||||
|
||||
procedure TExecutionScope.SetValues(const Address: TResolvedAddress; const Value: TAstValue);
|
||||
var
|
||||
targetScope: IExecutionScope;
|
||||
|
||||
+16
-25
@@ -46,11 +46,8 @@ type
|
||||
): IAddSeriesItemNode; static;
|
||||
class function SeriesLength(const ASeries: IIdentifierNode): ISeriesLengthNode; static;
|
||||
|
||||
// --- Static method to run the binder ---
|
||||
// Traverses the AST and resolves all identifiers. Returns a scope descriptor for the script's top-level variables.
|
||||
class function CreateScopeDescriptor(const Parent: IScopeDescriptor): IScopeDescriptor; static;
|
||||
class function CreateBinding(const Scope: IExecutionScope): IScopeDescriptor; static;
|
||||
class function Bind(const RootNode: IExpressionNode; const ParentScope: IExecutionScope): IScopeDescriptor; static;
|
||||
class function CreateDescriptor(const Scope: IExecutionScope): IScopeDescriptor; static;
|
||||
class function Bind(const RootNode: IExpressionNode; const ParentScope: IExecutionScope): IExecutionScope; static;
|
||||
end;
|
||||
|
||||
// TAstTraverser provides a default AST traversal implementation.
|
||||
@@ -89,8 +86,9 @@ type
|
||||
destructor Destroy; override;
|
||||
function Define(const Name: string): Integer;
|
||||
function FindSymbol(const Name: string; out Index: Integer; out Depth: Integer): Boolean;
|
||||
function Instantiate(const AParent: IExecutionScope): IExecutionScope;
|
||||
function CreateScope(const AParent: IExecutionScope): IExecutionScope;
|
||||
procedure PopulateFromScope(const AScope: IExecutionScope);
|
||||
property Symbols: TDictionary<string, Integer> read FSymbols;
|
||||
end;
|
||||
|
||||
{ TAstNode }
|
||||
@@ -119,14 +117,15 @@ type
|
||||
FResolvedAddress: TResolvedAddress;
|
||||
function GetName: string;
|
||||
function GetIsResolved: Boolean; inline;
|
||||
function GetAddress: TResolvedAddress; inline;
|
||||
public
|
||||
constructor Create(AName: string);
|
||||
function Accept(const Visitor: IAstVisitor): TAstValue; override;
|
||||
|
||||
function Resolve: TResolvedAddress;
|
||||
property IsResolved: Boolean read GetIsResolved;
|
||||
|
||||
property Name: string read FName;
|
||||
property Address: TResolvedAddress read GetAddress;
|
||||
end;
|
||||
|
||||
{ TBinaryExpressionNode }
|
||||
@@ -371,16 +370,13 @@ begin
|
||||
Result := FSymbols;
|
||||
end;
|
||||
|
||||
function TScopeDescriptor.Instantiate(const AParent: IExecutionScope): IExecutionScope;
|
||||
function TScopeDescriptor.CreateScope(const AParent: IExecutionScope): IExecutionScope;
|
||||
begin
|
||||
Result := TExecutionScope.CreateFromDescriptor(AParent, Self);
|
||||
Result := TExecutionScope.Create(AParent, Self);
|
||||
end;
|
||||
|
||||
procedure TScopeDescriptor.PopulateFromScope(const AScope: IExecutionScope);
|
||||
begin
|
||||
if not Assigned(AScope) then
|
||||
Exit;
|
||||
|
||||
for var pair in (AScope as TExecutionScope).NameToIndex do
|
||||
if not FSymbols.ContainsKey(pair.Key) then
|
||||
FSymbols.Add(pair.Key, pair.Value);
|
||||
@@ -429,7 +425,7 @@ begin
|
||||
Result := FName;
|
||||
end;
|
||||
|
||||
function TIdentifierNode.Resolve: TResolvedAddress;
|
||||
function TIdentifierNode.GetAddress: TResolvedAddress;
|
||||
begin
|
||||
if not IsResolved then
|
||||
raise EInvalidOpException.Create('Identifier not bound');
|
||||
@@ -810,7 +806,7 @@ end;
|
||||
constructor TBinder.Create(const AInitialScope: IExecutionScope);
|
||||
begin
|
||||
inherited Create;
|
||||
FCurrentDescriptor := TAst.CreateBinding(AInitialScope);
|
||||
FCurrentDescriptor := TAst.CreateDescriptor(AInitialScope);
|
||||
end;
|
||||
|
||||
procedure TBinder.EnterScope;
|
||||
@@ -869,7 +865,7 @@ end;
|
||||
|
||||
{ TAst }
|
||||
|
||||
class function TAst.Bind(const RootNode: IExpressionNode; const ParentScope: IExecutionScope): IScopeDescriptor;
|
||||
class function TAst.Bind(const RootNode: IExpressionNode; const ParentScope: IExecutionScope): IExecutionScope;
|
||||
var
|
||||
binder: TBinder;
|
||||
visitor: IAstVisitor;
|
||||
@@ -884,7 +880,7 @@ begin
|
||||
// Traverse the AST. This annotates all nodes AND populates the new descriptor.
|
||||
RootNode.Accept(visitor);
|
||||
// Return the completed descriptor for the script's scope.
|
||||
Result := binder.FCurrentDescriptor;
|
||||
Result := binder.CurrentDescriptor.CreateScope(ParentScope);
|
||||
finally
|
||||
// Restore the binder's internal state.
|
||||
binder.ExitScope;
|
||||
@@ -930,22 +926,17 @@ begin
|
||||
Result := TBinaryExpressionNode.Create(ALeft, AOperator, ARight);
|
||||
end;
|
||||
|
||||
class function TAst.CreateBinding(const Scope: IExecutionScope): IScopeDescriptor;
|
||||
class function TAst.CreateDescriptor(const Scope: IExecutionScope): IScopeDescriptor;
|
||||
begin
|
||||
if Assigned(Scope) then
|
||||
if Scope is TExecutionScope then
|
||||
begin
|
||||
Result := CreateScopeDescriptor(CreateBinding(Scope.Parent));
|
||||
Result.PopulateFromScope(Scope);
|
||||
Result := TScopeDescriptor.Create(CreateDescriptor(Scope.Parent));
|
||||
(Result as TScopeDescriptor).PopulateFromScope(Scope);
|
||||
end
|
||||
else
|
||||
Result := TScopeDescriptor.Create(nil);
|
||||
end;
|
||||
|
||||
class function TAst.CreateScopeDescriptor(const Parent: IScopeDescriptor): IScopeDescriptor;
|
||||
begin
|
||||
Result := TScopeDescriptor.Create(Parent);
|
||||
end;
|
||||
|
||||
class function TAst.FunctionCall(const ACallee: IExpressionNode; const AArguments: array of IExpressionNode): IFunctionCallNode;
|
||||
begin
|
||||
Result := TFunctionCallNode.Create(ACallee, AArguments);
|
||||
|
||||
Reference in New Issue
Block a user