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