Static specialization
This commit is contained in:
+46
-30
@@ -1,4 +1,8 @@
|
|||||||
# Monomorphisierung
|
Hier ist der vollständig aktualisierte Projektplan, der die Verwaltung des Caches als Environment-spezifische Aufgabe (statt global) korrekt abbildet.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Monomorphisierung (Revidierter Plan v2)
|
||||||
|
|
||||||
09.11.2025 19:10
|
09.11.2025 19:10
|
||||||
|
|
||||||
@@ -17,66 +21,78 @@ Diese Strategie macht die spezialisierten `TBinaryExpressionNode` und `TUnaryExp
|
|||||||
|
|
||||||
## Ergebnis & Implementierungsplan
|
## Ergebnis & Implementierungsplan
|
||||||
|
|
||||||
### 1. Neuer AST-Knoten: `IStaticCallNode`
|
### 1. Erweiterung: `IFunctionCallNode`
|
||||||
Als Ersatz für `TBinaryExpressionNode` und zur Darstellung aller monomorphisierten Aufrufe wird `IStaticCallNode` in `Myc.Ast.Nodes` eingeführt.
|
Anstatt einen neuen Knotentyp einzuführen, wird der bestehende `IFunctionCallNode` in `Myc.Ast.Nodes` erweitert, um *beide* Aufrufpfade (dynamisch und statisch) zu repräsentieren.
|
||||||
|
|
||||||
* Er ersetzt `IFunctionCallNode`, wenn ein Aufruf statisch gebunden werden kann.
|
* Er erhält ein neues Property: **`StaticTarget: TDataValue.TFunc`**.
|
||||||
* Er speichert einen direkten Verweis auf die auszuführende Implementierung (z.B. einen Funktionspointer, eine Implementierungs-ID oder einen delegierten `TDataValue.TFunc`).
|
* **Dynamischer Pfad (Default):** Wenn **`StaticTarget = nil`**, wird der Knoten wie bisher über den `vkMethod`-Pfad ausgewertet (dynamischer Dispatch über den `Callee`-Knoten).
|
||||||
* Er speichert die statisch bekannten Typen der Argumente und des Rückgabewerts.
|
* **Statischer Pfad (Optimiert):** Wenn **`StaticTarget <> nil`**, ignoriert der Evaluator den `Callee`-Knoten und ruft stattdessen das **`StaticTarget`** direkt mit den ausgewerteten Argumenten auf.
|
||||||
|
* Die `TAst.FunctionCall`-Factory in `Myc.Ast.pas` wird um einen optionalen **`AStaticTarget`**-Parameter erweitert.
|
||||||
|
|
||||||
### 2. Neue Compiler-Phase: `TStaticSpecializer`
|
### 2. Neue Compiler-Phase: `TStaticSpecializer`
|
||||||
Ein neuer `TAstTransformer` namens `TStaticSpecializer` wird implementiert. Er läuft *nach* dem `TTypeChecker` (Phase 3) und *ersetzt* den `TAstLowerer` (Phase 4).
|
Ein neuer `TAstTransformer` namens `TStaticSpecializer` wird implementiert. Er läuft *nach* dem `TTypeChecker` (Phase 3) und *ersetzt* den `TAstLowerer` (Phase 4).
|
||||||
|
|
||||||
|
* **Konstruktor:** Der `TStaticSpecializer` erhält im Konstruktor eine Referenz auf den `IEnvironment`-spezifischen Monomorphisierungs-Cache (siehe Punkt 3).
|
||||||
* **`VisitFunctionCall`-Logik:** Dies ist die Kernmethode.
|
* **`VisitFunctionCall`-Logik:** Dies ist die Kernmethode.
|
||||||
1. Sie prüft, ob der `Callee` des `IFunctionCallNode` ein `IIdentifierNode` ist (z.B. `+`, `Abs`, `my-func`).
|
1. Sie prüft, ob der `Callee` des `IFunctionCallNode` ein `IIdentifierNode` ist (z.B. `+`, `Abs`, `my-func`).
|
||||||
2. Sie prüft, ob *alle* Argumenttypen (`newArgs[i].StaticType`) statisch bekannt sind (d.h. nicht `stUnknown`).
|
2. Sie prüft, ob *alle* Argumenttypen (`newArgs[i].StaticType`) statisch bekannt sind (d.h. nicht `stUnknown`).
|
||||||
3. **Statischer Pfad (Ja):** Der `TStaticSpecializer` fragt den "Monomorphisierungs-Cache" nach einer spezialisierten Implementierung ab. Bei Erfolg ersetzt er den `IFunctionCallNode` durch einen `IStaticCallNode`.
|
3. **Statischer Pfad (Ja):** Der `TStaticSpecializer` fragt den ihm übergebenen **Environment-Cache** nach einer spezialisierten `TDataValue.TFunc` ab. Bei Erfolg **klont** er den `IFunctionCallNode` (via CoW) und setzt dessen **`StaticTarget`**-Property auf die gefundene Funktion.
|
||||||
4. **Dynamischer Pfad (Nein):** Der `IFunctionCallNode` wird als Fallback beibehalten.
|
4. **Dynamischer Pfad (Nein):** Der `IFunctionCallNode` wird (ggf. geklont, falls Kindknoten sich änderten) mit **`StaticTarget = nil`** zurückgegeben.
|
||||||
|
|
||||||
### 3. Der Monomorphisierungs-Cache
|
### 3. Der Environment-spezifische Cache
|
||||||
Der `TStaticSpecializer` verwaltet einen globalen Cache (z.B. `TDictionary`), der bereits spezialisierte Funktionen vorhält.
|
Das **`IEnvironment`** verwaltet einen **Instanz-spezifischen** Cache (z.B. `TDictionary`), der bereits spezialisierte Funktionen vorhält. Dieser Cache ist *nicht* global oder statisch.
|
||||||
|
|
||||||
|
* Jede `IEnvironment`-Instanz (z.B. eine für Produktion, eine für Tests) hat ihren eigenen, isolierten Cache, der an ihre `RootScope` und RTL gebunden ist.
|
||||||
|
* Die `TEnvironment`-Implementierung (in `Myc.Ast.Environment.pas`) wird um dieses `TDictionary`-Feld erweitert.
|
||||||
|
* Der `TStaticSpecializer` erhält eine Referenz auf diesen Cache bei seiner Erstellung (z.B. über den Konstruktor).
|
||||||
* **Schlüssel:** `(Funktions-ID, TArray<IStaticType>)`.
|
* **Schlüssel:** `(Funktions-ID, TArray<IStaticType>)`.
|
||||||
* *Funktions-ID*: Ein eindeutiger Bezeichner für den Callee (z.B. die `TResolvedAddress` der `def`-Deklaration oder ein Hash des `ILambdaExpressionNode`).
|
* *Funktions-ID*: Ein eindeutiger Bezeichner für den Callee, gebilded aus der `TResolvedAddress`, die innerhalb des Environments einen eindeutigen Schlüssel darstellt.
|
||||||
* **Wert:** Die `IStaticCallNode`-Implementierungsdetails (z.B. der Funktionspointer).
|
* **Wert:** Die spezialisierte `TDataValue.TFunc`.
|
||||||
|
|
||||||
### 4. Cache-Miss-Strategie (Inlining & User-Code)
|
### 4. Cache-Miss-Strategie (Inlining & User-Code)
|
||||||
Wenn der `TStaticSpecializer` einen statisch auflösbaren Aufruf (z.B. `(my-func 10)`) findet, der noch nicht im Cache ist (Cache Miss):
|
Wenn der `TStaticSpecializer` einen statisch auflösbaren Aufruf (z.B. `(my-func 10)`) findet, der noch nicht im **Environment-Cache** ist (Cache Miss):
|
||||||
|
|
||||||
1. Er holt den AST-Body der Zielfunktion (z.B. `(+ x x)` aus `(def my-func (fn [x] (+ x x)))`).
|
1. Er holt den AST-Body der Zielfunktion (z.B. `(+ x x)` aus `(def my-func (fn [x] (+ x x)))`).
|
||||||
2. Er instanziiert diesen Body, indem er das Wissen über die Argumenttypen (z.B. `x = stOrdinal`) anwendet.
|
2. Er instanziiert diesen Body, indem er das Wissen über die Argumenttypen (z.B. `x = stOrdinal`) anwendet.
|
||||||
3. Er lässt diesen neuen, instanziierten AST-Body (`(+ <x:Ordinal> <x:Ordinal>)`) rekursiv durch die relevanten Compiler-Phasen laufen (mindestens `TypeCheck` und `Specialize`).
|
3. Er lässt diesen neuen, instanziierten AST-Body (`(+ <x:Ordinal> <x:Ordinal>)`) rekursiv durch die relevanten Compiler-Phasen laufen (mindestens `TypeCheck` und `Specialize`).
|
||||||
4. Der `Specialize`-Pass wandelt den Body (z.B. `(+ <x:Ordinal> <x:Ordinal>)`) rekursiv in einen `IStaticCallNode` auf die RTL-Funktion `@TRtlFunctions.Add_Ordinal_Ordinal` um.
|
4. Der `Specialize`-Pass wandelt den Body (z.B. `(+ <x:Ordinal> <x:Ordinal>)`) rekursiv in einen *neuen* `IFunctionCallNode` um, dessen **`StaticTarget`** auf die RTL-Funktion `@TRtlFunctions.Add_Ordinal_Ordinal` zeigt.
|
||||||
5. Das Ergebnis (der `IStaticCallNode`, der den optimierten Body repräsentiert) wird im Cache gespeichert.
|
5. Das Ergebnis (die `TDataValue.TFunc`, die den optimierten Body repräsentiert) wird im **Environment-Cache** gespeichert.
|
||||||
6. Der ursprüngliche `IFunctionCallNode` `(my-func 10)` wird durch den neuen `IStaticCallNode` ersetzt.
|
6. Der ursprüngliche `IFunctionCallNode` `(my-func 10)` wird durch einen Klon ersetzt, dessen **`StaticTarget`** auf die soeben kompilierte Funktion zeigt.
|
||||||
|
|
||||||
### 5. RTL-Erweiterung
|
### 5. RTL-Erweiterung
|
||||||
* `Myc.Ast.RTL.Core` wird um statisch typisierte Implementierungen (z.B. `class function Add_Ordinal_Ordinal(A, B: Int64): Int64; static;`) erweitert.
|
* `Myc.Ast.RTL.Core` wird um statisch typisierte Implementierungen (z.B. `class function Add_Ordinal_Ordinal(A, B: Int64): Int64; static;`) erweitert.
|
||||||
* Die `TRtlRegistry` wird angepasst, um diese statischen Signaturen zu indizieren und als "Bootstrap" für den Monomorphisierungs-Cache bereitzustellen.
|
* Die `TRtlRegistry` wird angepasst, um diese statischen Signaturen zu indizieren und als "Bootstrap" für den Monomorphisierungs-Cache bereitzustellen.
|
||||||
|
|
||||||
### 6. Evaluator-Anpassung (`TEvaluatorVisitor`)
|
### 6. Evaluator-Anpassung (`TEvaluatorVisitor`)
|
||||||
* `VisitStaticCallNode` wird implementiert. Diese Methode ist hochoptimiert:
|
* `VisitFunctionCall` wird modifiziert, um beide Pfade zu behandeln:
|
||||||
1. Wertet die Argument-Nodes aus (die bereits statisch typgeprüft sind).
|
1. **Statischer Pfad:** `if Assigned(Node.StaticTarget) then`
|
||||||
2. Marshallt die `TDataValue`-Ergebnisse direkt in die erwarteten nativen Typen (z.B. `AsScalar.Value.AsInt64`).
|
* Wertet die Argument-Nodes aus (mit `Assert(arg.IsTyped)`).
|
||||||
3. Ruft den im `IStaticCallNode` gespeicherten Funktionspointer direkt auf.
|
* Marshallt die `TDataValue`-Ergebnisse direkt in die erwarteten nativen Typen.
|
||||||
4. Wrappt das native Ergebnis (z.B. `Int64`) zurück in ein `TDataValue`.
|
* Ruft das **`Node.StaticTarget`** direkt auf.
|
||||||
* `VisitFunctionCall` behandelt nur noch den dynamischen `vkMethod`-Fallback.
|
* Wrappt das Ergebnis zurück in ein `TDataValue`.
|
||||||
|
* Ruft `HandleTCO` auf (falls das **`StaticTarget`** ein `recur` war).
|
||||||
|
2. **Dynamischer Pfad:** `else`
|
||||||
|
* Behält die bestehende Logik (`vkMethod`-Lookup) und die TCO-Thunk-Erzeugung bei (`if Node.IsTailCall then ...`).
|
||||||
* `VisitBinaryExpression` und `VisitUnaryExpression` werden entfernt.
|
* `VisitBinaryExpression` und `VisitUnaryExpression` werden entfernt.
|
||||||
|
|
||||||
### 7. Auswirkungen auf TCO (Tail Call Optimization)
|
### 7. Auswirkungen auf TCO (Tail Call Optimization)
|
||||||
* Die TCO bleibt für `recur` und dynamische `fn`-Aufrufe (die `IFunctionCallNode` bleiben) voll funktionsfähig und erzeugt `TThunk`s.
|
* Die TCO bleibt für `recur` und *dynamische* `fn`-Aufrufe (die `IFunctionCallNode` mit **`StaticTarget = nil`** bleiben) voll funktionsfähig und erzeugt `TThunk`s.
|
||||||
* Ein `IStaticCallNode` (z.B. `(Abs x)`) in einer Tail-Position wird *nicht* per TCO optimiert. Er ist per Definition keine Rekursion. Der `TEvaluatorVisitor` führt ihn direkt aus und beendet damit korrekt die Trampolin-Schleife.
|
* Ein `IFunctionCallNode` mit gesetztem **`StaticTarget`** (z.B. `(Abs x)`) in einer Tail-Position wird *nicht* per TCO optimiert. Er ist per Definition keine Rekursion. Der `TEvaluatorVisitor` führt ihn direkt aus und beendet damit korrekt die Trampolin-Schleife.
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
* `IStaticCallNode` in `Myc.Ast.Nodes` definieren und `TAst`-Factory-Methode erstellen.
|
* `IFunctionCallNode` in `Myc.Ast.Nodes` um **`StaticTarget: TDataValue.TFunc`** erweitern.
|
||||||
* `IAstVisitor` (und alle `TAstVisitor...`-Basisklassen) um `VisitStaticCallNode` erweitern.
|
* `TFunctionCallNode` (Implementierungsklasse) um Feld, Konstruktorparameter und Getter erweitern.
|
||||||
* `TBinaryExpressionNode`, `TUnaryExpressionNode` (und ihre `Visit...`-Methoden) aus allen Units (`Nodes`, `Visitor`, `Dumper`, `Evaluator`, `Lowerer`, `Json`) entfernen.
|
* `TAst.FunctionCall`-Factory in `Myc.Ast.pas` um optionalen **`AStaticTarget`**-Parameter erweitern.
|
||||||
|
* `TAstTransformer.VisitFunctionCall` in `Myc.Ast.Visitor` anpassen, um **`StaticTarget`** bei CoW zu kopieren.
|
||||||
|
* `TBinaryExpressionNode`, `TUnaryExpressionNode` (und ihre `Visit...`-Methoden) aus allen Units (`Nodes`, `Visitor`, `Dumper`, `Evaluator`, `Lowerer`, `Json`, `Fmx.AstEditor.Node`) entfernen.
|
||||||
* `TAstLowerer` aus dem Kompilierungsprozess in `TEnvironment.Compile` entfernen.
|
* `TAstLowerer` aus dem Kompilierungsprozess in `TEnvironment.Compile` entfernen.
|
||||||
* `Myc.Ast.RTL.Core` um statisch typisierte Funktionsvarianten für alle Operatoren und gängige Funktionen (Abs, Trunc etc.) ergänzen.
|
* `Myc.Ast.RTL.Core` um statisch typisierte Funktionsvarianten für alle Operatoren und gängige Funktionen (Abs, Trunc etc.) ergänzen.
|
||||||
* `TRtlRegistry` erweitern, um diese statischen Signaturen zu indizieren.
|
* `TRtlRegistry` erweitern, um diese statischen Signaturen zu indizieren.
|
||||||
|
* **`IEnvironment`** (und Implementierung) um einen Member für den Monomorphisierungs-Cache erweitern.
|
||||||
* `TStaticSpecializer` als neuen `TAstTransformer`-Pass implementieren.
|
* `TStaticSpecializer` als neuen `TAstTransformer`-Pass implementieren.
|
||||||
* `TStaticSpecializer.VisitFunctionCall` mit der Logik für statische/dynamische Pfade implementieren.
|
* **`TStaticSpecializer`-Konstruktor** erweitern, um den Cache vom `IEnvironment` entgegenzunehmen.
|
||||||
* Monomorphisierungs-Cache (Lookup und "Cache Miss"-Rekursion) im `TStaticSpecializer` implementieren.
|
* `TStaticSpecializer.VisitFunctionCall` mit der Logik für statische/dynamische Pfade implementieren (Klonen des `IFunctionCallNode` mit gesetztem **`StaticTarget`**).
|
||||||
|
* Monomorphisierungs-Cache (Lookup und "Cache Miss"-Rekursion) im `TStaticSpecializer` implementieren (unter Verwendung des **Environment-Caches**).
|
||||||
* `TEnvironment.Compile` aktualisieren, um den `TStaticSpecializer` anstelle des `TAstLowerer` aufzurufen.
|
* `TEnvironment.Compile` aktualisieren, um den `TStaticSpecializer` anstelle des `TAstLowerer` aufzurufen.
|
||||||
* `TEvaluatorVisitor.VisitStaticCallNode` für den direkten Aufruf der spezialisierten Funktionen implementieren.
|
* `TEvaluatorVisitor.VisitFunctionCall` modifizieren, um den statischen Pfad (`if Assigned(Node.StaticTarget)`) zu implementieren.
|
||||||
@@ -252,10 +252,12 @@ type
|
|||||||
function GetCallee: IAstNode;
|
function GetCallee: IAstNode;
|
||||||
function GetArguments: TArray<IAstNode>;
|
function GetArguments: TArray<IAstNode>;
|
||||||
function GetIsTailCall: Boolean;
|
function GetIsTailCall: Boolean;
|
||||||
|
function GetStaticTarget: TDataValue.TFunc;
|
||||||
{$endregion}
|
{$endregion}
|
||||||
property Callee: IAstNode read GetCallee;
|
property Callee: IAstNode read GetCallee;
|
||||||
property Arguments: TArray<IAstNode> read GetArguments;
|
property Arguments: TArray<IAstNode> read GetArguments;
|
||||||
property IsTailCall: Boolean read GetIsTailCall;
|
property IsTailCall: Boolean read GetIsTailCall;
|
||||||
|
property StaticTarget: TDataValue.TFunc read GetStaticTarget;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// A node representing a macro call.
|
// A node representing a macro call.
|
||||||
|
|||||||
@@ -546,7 +546,6 @@ var
|
|||||||
newCallee: IAstNode;
|
newCallee: IAstNode;
|
||||||
newArgs: TArray<IAstNode>;
|
newArgs: TArray<IAstNode>;
|
||||||
begin
|
begin
|
||||||
// No longer cast to concrete class, use interface
|
|
||||||
newCallee := Accept(Node.Callee);
|
newCallee := Accept(Node.Callee);
|
||||||
newArgs := AcceptNodes(Node.Arguments);
|
newArgs := AcceptNodes(Node.Arguments);
|
||||||
|
|
||||||
@@ -555,7 +554,7 @@ begin
|
|||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
// Use TAst factory and copy properties via interface getters
|
// Use TAst factory and copy properties via interface getters
|
||||||
Result := TAst.FunctionCall(newCallee, newArgs, Node.StaticType, Node.IsTailCall);
|
Result := TAst.FunctionCall(newCallee, newArgs, Node.StaticType, Node.IsTailCall, Node.StaticTarget);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
+18
-6
@@ -87,7 +87,8 @@ type
|
|||||||
const ACallee: IAstNode;
|
const ACallee: IAstNode;
|
||||||
const AArguments: TArray<IAstNode>;
|
const AArguments: TArray<IAstNode>;
|
||||||
const AStaticType: IStaticType = nil;
|
const AStaticType: IStaticType = nil;
|
||||||
const AIsTailCall: Boolean = False
|
const AIsTailCall: Boolean = False;
|
||||||
|
const AStaticTarget: TDataValue.TFunc = nil
|
||||||
): IFunctionCallNode; static;
|
): IFunctionCallNode; static;
|
||||||
|
|
||||||
class function MacroExpansionNode(
|
class function MacroExpansionNode(
|
||||||
@@ -227,16 +228,19 @@ type
|
|||||||
FCallee: IAstNode;
|
FCallee: IAstNode;
|
||||||
FArguments: TArray<IAstNode>;
|
FArguments: TArray<IAstNode>;
|
||||||
FIsTailCall: Boolean;
|
FIsTailCall: Boolean;
|
||||||
|
FStaticTarget: TDataValue.TFunc;
|
||||||
function GetCallee: IAstNode;
|
function GetCallee: IAstNode;
|
||||||
function GetArguments: TArray<IAstNode>;
|
function GetArguments: TArray<IAstNode>;
|
||||||
function GetIsTailCall: Boolean;
|
function GetIsTailCall: Boolean;
|
||||||
|
function GetStaticTarget: TDataValue.TFunc;
|
||||||
function GetKind: TAstNodeKind; override;
|
function GetKind: TAstNodeKind; override;
|
||||||
public
|
public
|
||||||
constructor Create(
|
constructor Create(
|
||||||
const ACallee: IAstNode;
|
const ACallee: IAstNode;
|
||||||
const AArguments: TArray<IAstNode>;
|
const AArguments: TArray<IAstNode>;
|
||||||
const AStaticType: IStaticType;
|
const AStaticType: IStaticType;
|
||||||
const AIsTailCall: Boolean
|
const AIsTailCall: Boolean;
|
||||||
|
const AStaticTarget: TDataValue.TFunc
|
||||||
);
|
);
|
||||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||||
function AsFunctionCall: IFunctionCallNode; override;
|
function AsFunctionCall: IFunctionCallNode; override;
|
||||||
@@ -734,17 +738,18 @@ class function TAst.FunctionCall(
|
|||||||
const ACallee: IAstNode;
|
const ACallee: IAstNode;
|
||||||
const AArguments: TArray<IAstNode>;
|
const AArguments: TArray<IAstNode>;
|
||||||
const AStaticType: IStaticType = nil;
|
const AStaticType: IStaticType = nil;
|
||||||
const AIsTailCall: Boolean = False
|
const AIsTailCall: Boolean = False;
|
||||||
|
const AStaticTarget: TDataValue.TFunc = nil
|
||||||
): IFunctionCallNode;
|
): IFunctionCallNode;
|
||||||
begin
|
begin
|
||||||
// Updated to call new constructor
|
|
||||||
Result :=
|
Result :=
|
||||||
TFunctionCallNode.Create(
|
TFunctionCallNode.Create(
|
||||||
ACallee,
|
ACallee,
|
||||||
AArguments,
|
AArguments,
|
||||||
if AStaticType <> nil then AStaticType
|
if AStaticType <> nil then AStaticType
|
||||||
else TTypes.Unknown,
|
else TTypes.Unknown,
|
||||||
AIsTailCall
|
AIsTailCall,
|
||||||
|
AStaticTarget
|
||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -1593,13 +1598,15 @@ constructor TFunctionCallNode.Create(
|
|||||||
const ACallee: IAstNode;
|
const ACallee: IAstNode;
|
||||||
const AArguments: TArray<IAstNode>;
|
const AArguments: TArray<IAstNode>;
|
||||||
const AStaticType: IStaticType;
|
const AStaticType: IStaticType;
|
||||||
const AIsTailCall: Boolean
|
const AIsTailCall: Boolean;
|
||||||
|
const AStaticTarget: TDataValue.TFunc
|
||||||
);
|
);
|
||||||
begin
|
begin
|
||||||
inherited Create(AStaticType);
|
inherited Create(AStaticType);
|
||||||
FCallee := ACallee;
|
FCallee := ACallee;
|
||||||
FArguments := AArguments;
|
FArguments := AArguments;
|
||||||
FIsTailCall := AIsTailCall;
|
FIsTailCall := AIsTailCall;
|
||||||
|
FStaticTarget := AStaticTarget;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TFunctionCallNode.Accept(const Visitor: IAstVisitor): TDataValue;
|
function TFunctionCallNode.Accept(const Visitor: IAstVisitor): TDataValue;
|
||||||
@@ -1632,6 +1639,11 @@ begin
|
|||||||
Result := akFunctionCall;
|
Result := akFunctionCall;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TFunctionCallNode.GetStaticTarget: TDataValue.TFunc;
|
||||||
|
begin
|
||||||
|
Result := FStaticTarget;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TMacroExpansionNode }
|
{ TMacroExpansionNode }
|
||||||
|
|
||||||
constructor TMacroExpansionNode.Create(const ACallNode: IFunctionCallNode; const AExpandedBody: IAstTypedNode);
|
constructor TMacroExpansionNode.Create(const ACallNode: IFunctionCallNode; const AExpandedBody: IAstTypedNode);
|
||||||
|
|||||||
Reference in New Issue
Block a user