diff --git a/ASTPlayground/ASTPlayground.dpr b/ASTPlayground/ASTPlayground.dpr
index 347f1c1..f118608 100644
--- a/ASTPlayground/ASTPlayground.dpr
+++ b/ASTPlayground/ASTPlayground.dpr
@@ -5,7 +5,10 @@ uses
FMX.Forms,
MainForm in 'MainForm.pas' {Form1},
Myc.Ast.Evaluator in '..\Src\AST\Myc.Ast.Evaluator.pas',
- Myc.Ast.Printer in '..\Src\AST\Myc.Ast.Printer.pas';
+ Myc.Ast.Printer in '..\Src\AST\Myc.Ast.Printer.pas',
+ Myc.Ast.Types in '..\Src\AST\Myc.Ast.Types.pas',
+ Myc.Ast.Scope in '..\Src\AST\Myc.Ast.Scope.pas',
+ Myc.Ast.Closure in '..\Src\AST\Myc.Ast.Closure.pas';
{$R *.res}
diff --git a/ASTPlayground/ASTPlayground.dproj b/ASTPlayground/ASTPlayground.dproj
index 41ba722..1eff804 100644
--- a/ASTPlayground/ASTPlayground.dproj
+++ b/ASTPlayground/ASTPlayground.dproj
@@ -70,7 +70,7 @@
$(BDS)\bin\delphi_PROJECTICON.ico
$(BDS)\bin\delphi_PROJECTICNS.icns
ASTPlayground
- T:\Myc\Src\Data;T:\Myc\Src\AST;$(DCC_UnitSearchPath)
+ T:\Myc\Src\Data;T:\Myc\Src\AST;T:\Myc\Src;$(DCC_UnitSearchPath)
1031
CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
@@ -137,6 +137,9 @@
+
+
+
Base
diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas
index b0f3b52..6d19a6b 100644
--- a/ASTPlayground/MainForm.pas
+++ b/ASTPlayground/MainForm.pas
@@ -20,6 +20,7 @@ uses
FMX.Controls.Presentation,
Myc.Data.Types,
Myc.Ast,
+ Myc.Ast.Scope,
Myc.Ast.Evaluator,
Myc.Ast.Printer;
diff --git a/Src/AST/Myc.Ast.Closure.pas b/Src/AST/Myc.Ast.Closure.pas
new file mode 100644
index 0000000..b60b947
--- /dev/null
+++ b/Src/AST/Myc.Ast.Closure.pas
@@ -0,0 +1,124 @@
+unit Myc.Ast.Closure;
+
+interface
+
+uses
+ System.Classes,
+ System.Generics.Collections,
+ Myc.Data.Types,
+ Myc.Ast,
+ Myc.Ast.Scope;
+
+type
+ // A closure is a specific kind of method value that is defined by the evaluator.
+ // It holds the AST body and its captured scope.
+ IDataClosureValue = interface(IDataMethodValue)
+ ['{2704586B-E4BD-47AA-B05D-FD441AE6D818}']
+ {$region 'private'}
+ function GetBody: IExpressionNode;
+ function GetParameters: TList;
+ function GetClosureScope: TExecutionScope;
+ {$endregion}
+ property Body: IExpressionNode read GetBody;
+ property Parameters: TList read GetParameters;
+ property ClosureScope: TExecutionScope read GetClosureScope;
+ end;
+
+// Factory function to create a new closure value.
+function CreateDataClosureValue(
+ AMethodType: IDataMethodType;
+ ABody: IExpressionNode;
+ AParameters: TList;
+ AClosureScope: TExecutionScope
+): IDataClosureValue;
+
+implementation
+
+uses
+ System.SysUtils;
+
+type
+ { TDataClosureValueImpl }
+ // Concrete implementation of the IDataClosureValue interface.
+ TDataClosureValueImpl = class(TInterfacedObject, IDataValue, IDataClosureValue)
+ private
+ FMethodType: IDataMethodType;
+ FBody: IExpressionNode;
+ FParameters: TList;
+ FClosureScope: TExecutionScope;
+ // IDataValue
+ function GetDataType: IDataType;
+ function GetAsString: string;
+ // IDataMethodValue
+ function GetValue: TDataMethodProc;
+ // IDataClosureValue
+ function GetBody: IExpressionNode;
+ function GetParameters: TList;
+ function GetClosureScope: TExecutionScope;
+ public
+ constructor Create(
+ AMethodType: IDataMethodType;
+ ABody: IExpressionNode;
+ AParameters: TList;
+ ACClosureScope: TExecutionScope
+ );
+ end;
+
+function CreateDataClosureValue(
+ AMethodType: IDataMethodType;
+ ABody: IExpressionNode;
+ AParameters: TList;
+ AClosureScope: TExecutionScope
+): IDataClosureValue;
+begin
+ Result := TDataClosureValueImpl.Create(AMethodType, ABody, AParameters, AClosureScope);
+end;
+
+{ TDataClosureValueImpl }
+
+constructor TDataClosureValueImpl.Create(
+ AMethodType: IDataMethodType;
+ ABody: IExpressionNode;
+ AParameters: TList;
+ ACClosureScope: TExecutionScope
+);
+begin
+ inherited Create;
+ FMethodType := AMethodType;
+ FBody := ABody;
+ FParameters := AParameters; // Note: We are taking ownership of the list reference
+ FClosureScope := ACClosureScope;
+end;
+
+function TDataClosureValueImpl.GetAsString: string;
+begin
+ Result := '';
+end;
+
+function TDataClosureValueImpl.GetBody: IExpressionNode;
+begin
+ Result := FBody;
+end;
+
+function TDataClosureValueImpl.GetClosureScope: TExecutionScope;
+begin
+ Result := FClosureScope;
+end;
+
+function TDataClosureValueImpl.GetDataType: IDataType;
+begin
+ Result := FMethodType;
+end;
+
+function TDataClosureValueImpl.GetParameters: TList;
+begin
+ Result := FParameters;
+end;
+
+function TDataClosureValueImpl.GetValue: TDataMethodProc;
+begin
+ // This direct execution path is no longer used for closures.
+ raise ENotSupportedException.Create('Cannot get raw method proc from a closure object.');
+end;
+
+end.
diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas
index 03c55ec..87c8858 100644
--- a/Src/AST/Myc.Ast.Evaluator.pas
+++ b/Src/AST/Myc.Ast.Evaluator.pas
@@ -1,5 +1,3 @@
-// https://g.co/gemini/share/f8965e72f968
-
unit Myc.Ast.Evaluator;
interface
@@ -9,39 +7,11 @@ uses
System.Classes, // For TStrings
System.Generics.Collections,
Myc.Data.Types,
- Myc.Ast;
+ Myc.Ast,
+ Myc.Ast.Scope,
+ Myc.Ast.Closure;
type
- // Manages the scope of execution, holding variables and their values.
- TExecutionScope = class
- private
- FParent: TExecutionScope;
- FVariables: TDictionary;
- // Added for recursive dumping
- procedure DumpScope(const ABuilder: TStringBuilder; AIndent: Integer);
- public
- constructor Create(AParent: TExecutionScope = nil);
- destructor Destroy; override;
- function FindValue(const Name: string; out Value: IDataValue): Boolean;
- procedure SetValue(const Name: string; const Value: IDataValue);
- // Dumps the content of this scope and all parent scopes to a string.
- function Dump: string;
- end;
-
- // A closure is a specific kind of method value that is defined by the evaluator.
- // It holds the AST body and its captured scope.
- IDataClosureValue = interface(IDataMethodValue)
- ['{2704586B-E4BD-47AA-B05D-FD441AE6D818}']
- {$region 'private'}
- function GetBody: IExpressionNode;
- function GetParameters: TList;
- function GetClosureScope: TExecutionScope;
- {$endregion}
- property Body: IExpressionNode read GetBody;
- property Parameters: TList read GetParameters;
- property ClosureScope: TExecutionScope read GetClosureScope;
- end;
-
// TEvaluatorVisitor is the base implementation for evaluating an AST.
TEvaluatorVisitor = class(TInterfacedObject, IAstVisitor)
protected // Changed to protected to be accessible by descendants
@@ -91,145 +61,6 @@ type
implementation
-type
- { TDataClosureValueImpl }
- // Concrete implementation of the IDataClosureValue interface.
- TDataClosureValueImpl = class(TInterfacedObject, IDataValue, IDataClosureValue)
- private
- FMethodType: IDataMethodType;
- FBody: IExpressionNode;
- FParameters: TList;
- FClosureScope: TExecutionScope;
- // IDataValue
- function GetDataType: IDataType;
- function GetAsString: string;
- // IDataMethodValue
- function GetValue: TDataMethodProc;
- // IDataClosureValue
- function GetBody: IExpressionNode;
- function GetParameters: TList;
- function GetClosureScope: TExecutionScope;
- public
- constructor Create(
- AMethodType: IDataMethodType;
- ABody: IExpressionNode;
- AParameters: TList;
- ACClosureScope: TExecutionScope
- );
- end;
-
-constructor TDataClosureValueImpl.Create(
- AMethodType: IDataMethodType;
- ABody: IExpressionNode;
- AParameters: TList;
- ACClosureScope: TExecutionScope
-);
-begin
- inherited Create;
- FMethodType := AMethodType;
- FBody := ABody;
- FParameters := AParameters; // Note: We are taking ownership of the list reference
- FClosureScope := ACClosureScope;
-end;
-
-function TDataClosureValueImpl.GetAsString: string;
-begin
- Result := '';
-end;
-
-function TDataClosureValueImpl.GetBody: IExpressionNode;
-begin
- Result := FBody;
-end;
-
-function TDataClosureValueImpl.GetClosureScope: TExecutionScope;
-begin
- Result := FClosureScope;
-end;
-
-function TDataClosureValueImpl.GetDataType: IDataType;
-begin
- Result := FMethodType;
-end;
-
-function TDataClosureValueImpl.GetParameters: TList;
-begin
- Result := FParameters;
-end;
-
-function TDataClosureValueImpl.GetValue: TDataMethodProc;
-begin
- // This direct execution path is no longer used for closures.
- raise ENotSupportedException.Create('Cannot get raw method proc from a closure object.');
-end;
-
-{ TExecutionScope }
-
-constructor TExecutionScope.Create(AParent: TExecutionScope = nil);
-begin
- inherited Create;
- FParent := AParent;
- FVariables := TDictionary.Create;
-end;
-
-destructor TExecutionScope.Destroy;
-begin
- FVariables.Free;
- inherited Destroy;
-end;
-
-procedure TExecutionScope.DumpScope(const ABuilder: TStringBuilder; AIndent: Integer);
-var
- pair: TPair;
- indentStr: string;
-begin
- indentStr := ''.PadLeft(AIndent);
- if FVariables.Count > 0 then
- begin
- for pair in FVariables do
- ABuilder.AppendLine(indentStr + Format(' %s: %s', [pair.Key, pair.Value.AsString]));
- end
- else
- begin
- ABuilder.AppendLine(indentStr + ' (empty)');
- end;
-
- if Assigned(FParent) then
- begin
- ABuilder.AppendLine(indentStr + '[Parent Scope]');
- FParent.DumpScope(ABuilder, AIndent + 2);
- end;
-end;
-
-function TExecutionScope.Dump: string;
-var
- builder: TStringBuilder;
-begin
- builder := TStringBuilder.Create;
- try
- builder.AppendLine('[Current Scope]');
- DumpScope(builder, 0);
- Result := builder.ToString.TrimRight;
- finally
- builder.Free;
- end;
-end;
-
-function TExecutionScope.FindValue(const Name: string; out Value: IDataValue): Boolean;
-begin
- Result := FVariables.TryGetValue(Name, Value);
- if not Result and Assigned(FParent) then
- begin
- Result := FParent.FindValue(Name, Value);
- end;
-end;
-
-procedure TExecutionScope.SetValue(const Name: string; const Value: IDataValue);
-begin
- // This defines a variable in the current scope. It can shadow a parent variable.
- FVariables.AddOrSetValue(Name, Value);
-end;
-
{ TEvaluatorVisitor }
constructor TEvaluatorVisitor.Create(AScope: TExecutionScope);
@@ -252,7 +83,8 @@ begin
// Instead of creating an anonymous method, we now create a data object
// that holds all information required to execute the lambda later.
methodType := TDataType.MethodOf(TDataType.Ordinal, TDataType.Ordinal); // TODO: Infer this
- Result := TDataClosureValueImpl.Create(methodType, Node.Body, Node.Parameters, FScope);
+ // Use the factory function from the Myc.Ast.Closure unit.
+ Result := CreateDataClosureValue(methodType, Node.Body, Node.Parameters, FScope);
end;
function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): IDataValue;
diff --git a/Src/AST/Myc.Ast.Scope.pas b/Src/AST/Myc.Ast.Scope.pas
new file mode 100644
index 0000000..b06ac59
--- /dev/null
+++ b/Src/AST/Myc.Ast.Scope.pas
@@ -0,0 +1,97 @@
+unit Myc.Ast.Scope;
+
+interface
+
+uses
+ System.SysUtils,
+ System.Classes,
+ System.Generics.Collections,
+ Myc.Data.Types;
+
+type
+ // Manages the scope of execution, holding variables and their values.
+ TExecutionScope = class
+ private
+ FParent: TExecutionScope;
+ FVariables: TDictionary;
+ // Added for recursive dumping
+ procedure DumpScope(const ABuilder: TStringBuilder; AIndent: Integer);
+ public
+ constructor Create(AParent: TExecutionScope = nil);
+ destructor Destroy; override;
+ function FindValue(const Name: string; out Value: IDataValue): Boolean;
+ procedure SetValue(const Name: string; const Value: IDataValue);
+ // Dumps the content of this scope and all parent scopes to a string.
+ function Dump: string;
+ end;
+
+implementation
+
+{ TExecutionScope }
+
+constructor TExecutionScope.Create(AParent: TExecutionScope = nil);
+begin
+ inherited Create;
+ FParent := AParent;
+ FVariables := TDictionary.Create;
+end;
+
+destructor TExecutionScope.Destroy;
+begin
+ FVariables.Free;
+ inherited Destroy;
+end;
+
+procedure TExecutionScope.DumpScope(const ABuilder: TStringBuilder; AIndent: Integer);
+var
+ pair: TPair;
+ indentStr: string;
+begin
+ indentStr := ''.PadLeft(AIndent);
+ if (FVariables.Count > 0) then
+ begin
+ for pair in FVariables do
+ ABuilder.AppendLine(indentStr + Format(' %s: %s', [pair.Key, pair.Value.AsString]));
+ end
+ else
+ begin
+ ABuilder.AppendLine(indentStr + ' (empty)');
+ end;
+
+ if Assigned(FParent) then
+ begin
+ ABuilder.AppendLine(indentStr + '[Parent Scope]');
+ FParent.DumpScope(ABuilder, AIndent + 2);
+ end;
+end;
+
+function TExecutionScope.Dump: string;
+var
+ builder: TStringBuilder;
+begin
+ builder := TStringBuilder.Create;
+ try
+ builder.AppendLine('[Current Scope]');
+ DumpScope(builder, 0);
+ Result := builder.ToString.TrimRight;
+ finally
+ builder.Free;
+ end;
+end;
+
+function TExecutionScope.FindValue(const Name: string; out Value: IDataValue): Boolean;
+begin
+ Result := FVariables.TryGetValue(Name, Value);
+ if not Result and Assigned(FParent) then
+ begin
+ Result := FParent.FindValue(Name, Value);
+ end;
+end;
+
+procedure TExecutionScope.SetValue(const Name: string; const Value: IDataValue);
+begin
+ // This defines a variable in the current scope. It can shadow a parent variable.
+ FVariables.AddOrSetValue(Name, Value);
+end;
+
+end.