From bb74d408dad0ebb9352536090e44318e77d055b4 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Thu, 4 Sep 2025 19:50:05 +0200 Subject: [PATCH] Binding --- ASTPlayground/ASTPlayground.dproj | 2 +- ASTPlayground/MainForm.pas | 2 +- Src/AST/Myc.Ast.Evaluator.pas | 24 ++++--- Src/AST/Myc.Ast.Nodes.pas | 7 +- Src/AST/Myc.Ast.Scope.pas | 48 ++++++-------- Src/AST/Myc.Ast.pas | 105 ------------------------------ 6 files changed, 39 insertions(+), 149 deletions(-) diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj index 88c6b6b..ad98492 100644 --- a/ASTPlayground/ASTPlayground.dproj +++ b/ASTPlayground/ASTPlayground.dproj @@ -4,7 +4,7 @@ 20.3 FMX True - Release + Debug Win64 ASTPlayground 2 diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas index b15c235..b770e99 100644 --- a/ASTPlayground/MainForm.pas +++ b/ASTPlayground/MainForm.pas @@ -567,7 +567,7 @@ begin if series.AsRecordSeries.Value.TotalCount >= smaSlowLength then begin // Update the 'current_series' value in the scope using the FAST index-based assignment - scope.AssignValue(seriesAddress, series); + scope[seriesAddress] := series; // Execute the PRE-BOUND call AST var resultValue := callAst.Accept(visitor); diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index 0500d4c..deb4bbe 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -294,7 +294,7 @@ var lookback: Int64; begin // The target series must have been resolved by the binder. - seriesVar := FScope.GetValue(Node.Series.Resolve); + seriesVar := FScope[Node.Series.Resolve]; itemValue := Node.Value.Accept(Self); lookback := -1; @@ -345,7 +345,7 @@ end; function TEvaluatorVisitor.VisitAssignment(const Node: IAssignmentNode): TAstValue; begin Result := Node.Value.Accept(Self); - FScope.AssignValue(Node.Identifier.Resolve, Result); + FScope[Node.Identifier.Resolve] := Result; end; function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TAstValue; @@ -378,7 +378,7 @@ end; function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TAstValue; begin - Result := FScope.GetValue(Node.Resolve) + Result := FScope[Node.Resolve]; end; function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TAstValue; @@ -475,7 +475,7 @@ begin else value := TAstValue.Void; - FScope.AssignValue(Node.Identifier.Resolve, value); + FScope[Node.Identifier.Resolve] := value; Result := value; end; @@ -491,7 +491,6 @@ var closure: IEvaluatorClosure; i: Integer; callScope: IExecutionScope; - innerVisitor: IAstVisitor; descriptor: IScopeDescriptor; begin calleeValue := Node.Callee.Accept(Self); @@ -519,15 +518,20 @@ begin if not Assigned(descriptor) then raise EParserError.Create('Lambda has no scope descriptor. Did the binder run?'); + var adr: TResolvedAddress; + adr.ScopeDepth := 0; + adr.SlotIndex := 0; + callScope := descriptor.Instantiate(closure.ClosureScope); - callScope.SetValueByIndex(0, calleeValue); + callScope[adr] := calleeValue; for i := 0 to Node.Arguments.Count - 1 do - callScope.SetValueByIndex(closure.Parameters[i].Resolve.SlotIndex, Node.Arguments[i].Accept(Self)); + begin + adr.SlotIndex := closure.Parameters[i].Resolve.SlotIndex; + callScope[adr] := Node.Arguments[i].Accept(Self); + end; - // 3. Führe den Körper der Funktion im neuen Scope aus. - innerVisitor := Self.CreateVisitorForScope(callScope); - Result := closure.Body.Accept(innerVisitor); + Result := closure.Body.Accept(CreateVisitorForScope(callScope)); end else raise EArgumentException.Create('Unknown closure implementation type.'); diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index 5967adf..02dda78 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -105,6 +105,8 @@ type IExecutionScope = interface {$region 'private'} function GetParent: IExecutionScope; + function GetValues(const Address: TResolvedAddress): TAstValue; + procedure SetValues(const Address: TResolvedAddress; const Value: TAstValue); {$endregion} procedure Clear; @@ -115,11 +117,10 @@ type // --- New index-based access --- procedure Define(const Name: string; const Value: TAstValue); - function GetValue(const Address: TResolvedAddress): TAstValue; - procedure AssignValue(const Address: TResolvedAddress; const Value: TAstValue); overload; - procedure SetValueByIndex(Index: Integer; const Value: TAstValue); function Dump: string; + + property Values[const Address: TResolvedAddress]: TAstValue read GetValues write SetValues; default; property Parent: IExecutionScope read GetParent; end; diff --git a/Src/AST/Myc.Ast.Scope.pas b/Src/AST/Myc.Ast.Scope.pas index 302e6ab..b6c452b 100644 --- a/Src/AST/Myc.Ast.Scope.pas +++ b/Src/AST/Myc.Ast.Scope.pas @@ -15,6 +15,8 @@ type FValues: TArray; FNameToIndex: TDictionary; 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; @@ -24,9 +26,6 @@ type procedure AssignValue(const Name: string; const Value: TAstValue); overload; function Dump: string; procedure Define(const Name: string; const Value: TAstValue); - function GetValue(const Address: TResolvedAddress): TAstValue; - procedure AssignValue(const Address: TResolvedAddress; const Value: TAstValue); overload; - procedure SetValueByIndex(Index: Integer; const Value: TAstValue); // <-- NEU public constructor Create(AParent: IExecutionScope = nil); constructor CreateFromDescriptor(const AParent: IExecutionScope; const ADescriptor: IScopeDescriptor); @@ -82,25 +81,6 @@ begin raise Exception.CreateFmt('Cannot assign to undeclared variable "%s".', [Name]); end; -procedure TExecutionScope.AssignValue(const Address: TResolvedAddress; const Value: TAstValue); -var - targetScope: IExecutionScope; - i: Integer; -begin - targetScope := Self; - for i := 1 to Address.ScopeDepth do - begin - if Assigned(targetScope) then - targetScope := targetScope.Parent - else - raise EInvalidOpException.Create('Invalid scope depth during assignment.'); - end; - - if Address.SlotIndex >= Length((targetScope as TExecutionScope).FValues) then - raise EInvalidOpException.Create('Invalid scope index during assignment.'); - (targetScope as TExecutionScope).FValues[Address.SlotIndex] := Value; -end; - procedure TExecutionScope.Clear; begin FValues := []; @@ -182,7 +162,7 @@ begin Result := False; end; -function TExecutionScope.GetValue(const Address: TResolvedAddress): TAstValue; +function TExecutionScope.GetValues(const Address: TResolvedAddress): TAstValue; var targetScope: TExecutionScope; i: Integer; @@ -209,13 +189,23 @@ begin Define(Name, Value); end; -procedure TExecutionScope.SetValueByIndex(Index: Integer; const Value: TAstValue); +procedure TExecutionScope.SetValues(const Address: TResolvedAddress; const Value: TAstValue); +var + targetScope: IExecutionScope; + i: Integer; begin - // This is a direct write to a slot in the current scope's value array. - // It's used for setting up a function call frame. - if (Index < 0) or (Index >= Length(FValues)) then - raise EArgumentException.CreateFmt('Index %d is out of bounds for scope with %d slots.', [Index, Length(FValues)]); - FValues[Index] := Value; + targetScope := Self; + for i := 1 to Address.ScopeDepth do + begin + if Assigned(targetScope) then + targetScope := targetScope.Parent + else + raise EInvalidOpException.Create('Invalid scope depth during assignment.'); + end; + + if Address.SlotIndex >= Length((targetScope as TExecutionScope).FValues) then + raise EInvalidOpException.Create('Invalid scope index during assignment.'); + (targetScope as TExecutionScope).FValues[Address.SlotIndex] := Value; end; end. diff --git a/Src/AST/Myc.Ast.pas b/Src/AST/Myc.Ast.pas index aa6b517..913ae5e 100644 --- a/Src/AST/Myc.Ast.pas +++ b/Src/AST/Myc.Ast.pas @@ -306,25 +306,6 @@ type // --- Binder Implementation --- - TSymbol = record - Name: string; - SlotIndex: Integer; - end; - - TSymbolTable = class - private - FParent: TSymbolTable; - FSymbols: TDictionary; - FNextSlotIndex: Integer; - procedure DefinePreResolved(const Name: string; Index: Integer); - public - constructor Create(AParent: TSymbolTable); - destructor Destroy; override; - procedure PopulateFromScope(const AScope: IExecutionScope); - function Define(const Name: string): TSymbol; - function Resolve(const Name: string; out Symbol: TSymbol; out Depth: Integer): Boolean; - end; - TBinder = class(TAstTraverser) private FCurrentDescriptor: IScopeDescriptor; @@ -824,92 +805,6 @@ begin Result := FSeries; end; -{ TSymbolTable } - -constructor TSymbolTable.Create(AParent: TSymbolTable); -begin - inherited Create; - FParent := AParent; - FSymbols := TDictionary.Create; - FNextSlotIndex := 0; -end; - -destructor TSymbolTable.Destroy; -begin - FSymbols.Free; - inherited; -end; - -procedure TSymbolTable.DefinePreResolved(const Name: string; Index: Integer); -var - symbol: TSymbol; -begin - // Defines a symbol with a specific, pre-determined index. - if not FSymbols.ContainsKey(Name) then - begin - symbol.Name := Name; - symbol.SlotIndex := Index; - FSymbols.Add(Name, symbol); - - // Ensure the next automatically assigned index is correct. - if (Index >= FNextSlotIndex) then - FNextSlotIndex := Index + 1; - end; -end; - -procedure TSymbolTable.PopulateFromScope(const AScope: IExecutionScope); -var - scopeImpl: TExecutionScope; - pair: TPair; -begin - // Iterate over the Key-Value pairs of the scope's dictionary to preserve the exact indices. - if not Assigned(AScope) then - Exit; - - scopeImpl := (AScope as TExecutionScope); - for pair in scopeImpl.NameToIndex do - DefinePreResolved(pair.Key, pair.Value); -end; - -function TSymbolTable.Define(const Name: string): TSymbol; -begin - Result.Name := Name; - Result.SlotIndex := FNextSlotIndex; - inc(FNextSlotIndex); - FSymbols.Add(Name, Result); -end; - -function TSymbolTable.Resolve(const Name: string; out Symbol: TSymbol; out Depth: Integer): Boolean; -var - currentScope: TSymbolTable; -begin - Depth := 0; - currentScope := Self; - while Assigned(currentScope) do - begin - if currentScope.FSymbols.TryGetValue(Name, Symbol) then - Exit(True); - - inc(Depth); - currentScope := currentScope.FParent; - end; - Result := False; -end; - -{ TBinder } - -// Helper function to recursively build the symbol table hierarchy -function CreateSymbolTableFromScope(AScope: IExecutionScope): TSymbolTable; -begin - if not Assigned(AScope) then - Exit(nil); - - // Recursively create the parent symbol table first - Result := TSymbolTable.Create(CreateSymbolTableFromScope(AScope.Parent)); - // Then populate the current level - Result.PopulateFromScope(AScope); -end; - { TBinder } constructor TBinder.Create(const AInitialScope: IExecutionScope);