From c913f9dbc58a6797038c3d1df77e667f9ff80258 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Mon, 15 Sep 2025 10:39:39 +0200 Subject: [PATCH] Scope Index optimized --- ASTPlayground/ASTPlayground.dproj | 2 +- Src/AST/Myc.Ast.Debugger.pas | 1 + Src/AST/Myc.Ast.Evaluator.pas | 60 +------------------------------ Src/AST/Myc.Ast.Scope.pas | 41 ++++++++++++++++----- 4 files changed, 35 insertions(+), 69 deletions(-) diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj index 4b373f7..b988ee1 100644 --- a/ASTPlayground/ASTPlayground.dproj +++ b/ASTPlayground/ASTPlayground.dproj @@ -4,7 +4,7 @@ 20.3 FMX True - Debug + Release Win64 ASTPlayground 2 diff --git a/Src/AST/Myc.Ast.Debugger.pas b/Src/AST/Myc.Ast.Debugger.pas index 8254ddb..d31e7a3 100644 --- a/Src/AST/Myc.Ast.Debugger.pas +++ b/Src/AST/Myc.Ast.Debugger.pas @@ -22,6 +22,7 @@ type procedure Unindent; procedure AppendLine(const S: string); procedure ShowScope; + protected // Overridden to provide the debug-specific visitor factory. function CreateVisitorFactory: TVisitorFactory; override; diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index 27256e8..5416721 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -21,18 +21,11 @@ type private FScope: IExecutionScope; protected - function IsTruthy(const AValue: TDataValue): Boolean; + function IsTruthy(const AValue: TDataValue): Boolean; inline; // Returns a closure that can create the correct type of visitor for a lambda's body. function CreateVisitorFactory: TVisitorFactory; virtual; - // Creates the callable closure for a lambda expression. No longer virtual. - function CreateLambdaClosure( - const Node: ILambdaExpressionNode; - const ACapturedCells: TArray; - const AClosureScope: IExecutionScope - ): TDataValue; - property Scope: IExecutionScope read FScope; public constructor Create(const AScope: IExecutionScope); @@ -128,57 +121,6 @@ begin end; end; -function TEvaluatorVisitor.CreateLambdaClosure( - const Node: ILambdaExpressionNode; - const ACapturedCells: TArray; - const AClosureScope: IExecutionScope -): TDataValue; -var - closureValue: TDataValue; - visitorFactory: TVisitorFactory; -begin - // Get the appropriate factory via virtual dispatch. - visitorFactory := CreateVisitorFactory(); - - closureValue := - TDataValue.TFunc( - function(const ArgValues: TArray): TDataValue - var - lambdaScope: IExecutionScope; - bodyVisitor: IAstVisitor; - i: Integer; - adr: TResolvedAddress; - begin - if (Length(ArgValues) <> Length(Node.Parameters)) then - raise EArgumentException - .CreateFmt('Argument count mismatch: expected %d, got %d', [Length(Node.Parameters), Length(ArgValues)]); - - lambdaScope := TExecutionScope.Create(AClosureScope, Node.ScopeDescriptor, ACapturedCells); - - adr.Kind := akLocalOrParent; - adr.ScopeDepth := 0; - - // Set 'Self' (slot 0) to the captured closureValue for recursion. - adr.SlotIndex := 0; - lambdaScope.Cell[adr].Value := closureValue; - - // Populate the actual parameters. - for i := 0 to High(ArgValues) do - begin - adr.SlotIndex := Node.Parameters[i].Address.SlotIndex; - lambdaScope.Cell[adr].Value := ArgValues[i]; - end; - - // Use the injected factory to create the visitor for the lambda's body. - bodyVisitor := visitorFactory(lambdaScope); - - Result := Node.Body.Accept(bodyVisitor); - end - ); - - Result := closureValue; -end; - function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; var capturedCells: TArray; diff --git a/Src/AST/Myc.Ast.Scope.pas b/Src/AST/Myc.Ast.Scope.pas index 068bb67..d143dc8 100644 --- a/Src/AST/Myc.Ast.Scope.pas +++ b/Src/AST/Myc.Ast.Scope.pas @@ -13,13 +13,16 @@ type TExecutionScope = class(TInterfacedObject, IExecutionScope) private FParent: IExecutionScope; + FDescriptor: IScopeDescriptor; FValues: TArray; // Stores references to the value cells captured by a closure. FCapturedUpvalues: TArray; FNameToIndex: TDictionary; procedure DumpScope(const ABuilder: TStringBuilder; AIndent: Integer); + procedure NeedNameToIndex; function GetParent: IExecutionScope; function GetCell(const Address: TResolvedAddress): IValueCell; + function GetNameToIndex: TDictionary; public constructor Create( AParent: IExecutionScope = nil; @@ -30,7 +33,7 @@ type procedure Clear; function Dump: string; procedure Define(const Name: string; const Value: TDataValue); - property NameToIndex: TDictionary read FNameToIndex; + property NameToIndex: TDictionary read GetNameToIndex; property Parent: IExecutionScope read FParent; property Values: TArray read FValues; end; @@ -73,15 +76,12 @@ var begin inherited Create; FParent := AParent; + FDescriptor := ADescriptor; FValues := []; - FNameToIndex := TDictionary.Create; FCapturedUpvalues := ACapturedUpvalues; // Store upvalues if ADescriptor <> nil then begin - for var item in ADescriptor.Symbols do - FNameToIndex.AddOrSetValue(item.Key, item.Value); - slotCount := ADescriptor.SlotCount; SetLength(FValues, slotCount); for i := 0 to slotCount - 1 do @@ -91,7 +91,7 @@ end; destructor TExecutionScope.Destroy; begin - FNameToIndex.Free; + Clear; inherited Destroy; end; @@ -134,13 +134,15 @@ end; procedure TExecutionScope.Clear; begin FValues := []; - FNameToIndex.Clear; + FreeAndNil(FNameToIndex); end; -procedure TExecutionScope.Define(const Name: string; const Value: TDataValue); // <-- Changed from TAstValue +procedure TExecutionScope.Define(const Name: string; const Value: TDataValue); var index: Integer; begin + NeedNameToIndex; + if FNameToIndex.ContainsKey(Name) then raise Exception.CreateFmt('Variable "%s" is already defined in this scope.', [Name]); @@ -157,8 +159,10 @@ var indentStr: string; sortedPairs: TArray>; begin + NeedNameToIndex; + indentStr := ''.PadLeft(AIndent); - if (FNameToIndex.Count > 0) then + if FNameToIndex.Count > 0 then begin sortedPairs := FNameToIndex.ToArray; TArray.Sort>( @@ -197,4 +201,23 @@ begin end; end; +function TExecutionScope.GetNameToIndex: TDictionary; +begin + NeedNameToIndex; + Result := FNameToIndex; +end; + +procedure TExecutionScope.NeedNameToIndex; +begin + if FNameToIndex = nil then + begin + FNameToIndex := TDictionary.Create; + if FDescriptor <> nil then + begin + for var item in FDescriptor.Symbols do + FNameToIndex.AddOrSetValue(item.Key, item.Value); + end; + end; +end; + end.