Node immutability improved
This commit is contained in:
@@ -71,7 +71,8 @@ uses
|
||||
Myc.Data.Keyword,
|
||||
Myc.Data.Decimal,
|
||||
Myc.Data.Series,
|
||||
Myc.Data.Scalar.JSON;
|
||||
Myc.Data.Scalar.JSON,
|
||||
Myc.Ast.Types; // Added for VisitRecordLiteral
|
||||
|
||||
// Helper type for TCO via trampolining.
|
||||
type
|
||||
@@ -177,25 +178,21 @@ end;
|
||||
|
||||
function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
|
||||
var
|
||||
boundNode: TLambdaExpressionNode; // Changed
|
||||
capturedCells: TArray<IValueCell>;
|
||||
i: Integer;
|
||||
sourceAddresses: TArray<TResolvedAddress>;
|
||||
closureScope: IExecutionScope;
|
||||
visitorFactory: TEvaluatorFactory;
|
||||
begin
|
||||
// Cast to the bound node to access binder-specific information.
|
||||
boundNode := Node as TLambdaExpressionNode; // Changed
|
||||
|
||||
// Create the closure by capturing the value cells of all upvalues from the current scope.
|
||||
sourceAddresses := boundNode.Upvalues;
|
||||
// Access binder-specific information via the interface
|
||||
sourceAddresses := Node.Upvalues;
|
||||
SetLength(capturedCells, Length(sourceAddresses));
|
||||
for i := 0 to High(sourceAddresses) do
|
||||
capturedCells[i] := FScope.Capture(sourceAddresses[i]);
|
||||
|
||||
// Memory optimization: a lambda's scope does not need to be kept alive as a parent
|
||||
// if it contains no nested lambdas that might need to capture from it later.
|
||||
if boundNode.HasNestedLambdas then
|
||||
if Node.HasNestedLambdas then
|
||||
closureScope := FScope
|
||||
else
|
||||
closureScope := nil;
|
||||
@@ -203,8 +200,8 @@ begin
|
||||
// Get a factory to create the correct visitor (e.g., debug or production) for the lambda body.
|
||||
visitorFactory := CreateVisitorFactory();
|
||||
|
||||
var scopeDescriptor := boundNode.ScopeDescriptor;
|
||||
var params := boundNode.Parameters;
|
||||
var scopeDescriptor := Node.ScopeDescriptor;
|
||||
var params := Node.Parameters;
|
||||
var cNode: ILambdaExpressionNode := Node;
|
||||
|
||||
// [unsafe] prevents a reference cycle since the closure captures itself for 'recur'.
|
||||
@@ -234,7 +231,7 @@ begin
|
||||
for i := 0 to High(ArgValues) do
|
||||
begin
|
||||
// Parameters in a bound lambda must be bound identifiers.
|
||||
adr.SlotIndex := params[i].AsIdentifier.Address.SlotIndex; // Changed
|
||||
adr.SlotIndex := params[i].Address.SlotIndex;
|
||||
lambdaScope[adr] := ArgValues[i];
|
||||
end;
|
||||
|
||||
@@ -274,7 +271,6 @@ function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDa
|
||||
var
|
||||
calleeValue: TDataValue;
|
||||
argValues: TArray<TDataValue>;
|
||||
boundNode: TFunctionCallNode; // Changed
|
||||
begin
|
||||
calleeValue := Node.Callee.Accept(Self);
|
||||
if calleeValue.Kind <> vkMethod then
|
||||
@@ -285,8 +281,7 @@ begin
|
||||
for var i := 0 to High(argNodes) do
|
||||
argValues[i] := argNodes[i].Accept(Self);
|
||||
|
||||
boundNode := Node as TFunctionCallNode; // Changed
|
||||
if boundNode.IsTailCall then
|
||||
if Node.IsTailCall then
|
||||
begin
|
||||
// This is a tail call. Return a thunk to be processed by the trampoline.
|
||||
Result := TDataValue.FromGeneric<TThunk>(TThunk.Create(calleeValue, argValues, false));
|
||||
@@ -334,7 +329,7 @@ var
|
||||
itemValue, lookbackValue, seriesVar: TDataValue;
|
||||
lookback: Int64;
|
||||
begin
|
||||
seriesVar := FScope[Node.Series.AsIdentifier.Address]; // Changed
|
||||
seriesVar := FScope[Node.Series.Address];
|
||||
itemValue := Node.Value.Accept(Self);
|
||||
lookback := -1;
|
||||
|
||||
@@ -368,7 +363,7 @@ begin
|
||||
// Evaluate the new value.
|
||||
Result := Node.Value.Accept(Self);
|
||||
// Assign it. The scope's SetValues implementation now handles boxing correctly.
|
||||
FScope[Node.Identifier.AsIdentifier.Address] := Result; // Changed
|
||||
FScope[Node.Identifier.Address] := Result;
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): TDataValue;
|
||||
@@ -405,7 +400,7 @@ end;
|
||||
function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): TDataValue;
|
||||
begin
|
||||
// The scope's GetValues implementation now handles unboxing automatically.
|
||||
Result := FScope[Node.AsIdentifier.Address];
|
||||
Result := FScope[Node.Address];
|
||||
end;
|
||||
|
||||
function TEvaluatorVisitor.VisitIndexer(const Node: IIndexerNode): TDataValue;
|
||||
@@ -492,22 +487,23 @@ end;
|
||||
function TEvaluatorVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
|
||||
var
|
||||
i: Integer;
|
||||
staticType: IStaticType;
|
||||
begin
|
||||
// Check which type the binder created
|
||||
if Node is TGenericRecordLiteralNode then // Changed
|
||||
staticType := Node.StaticType; // Get the type set by TypeChecker
|
||||
|
||||
if staticType.Kind = stGenericRecord then
|
||||
begin
|
||||
// --- NEW GENERIC PATH ---
|
||||
var boundNode := Node as TGenericRecordLiteralNode; // Changed
|
||||
var genFields: TArray<TPair<IKeyword, TDataValue>>;
|
||||
SetLength(genFields, Length(boundNode.Fields));
|
||||
SetLength(genFields, Length(Node.Fields));
|
||||
|
||||
// Evaluate all field values
|
||||
for i := 0 to High(boundNode.Fields) do
|
||||
for i := 0 to High(Node.Fields) do
|
||||
begin
|
||||
genFields[i] :=
|
||||
TPair<IKeyword, TDataValue>.Create(
|
||||
boundNode.Fields[i].Key.Value,
|
||||
boundNode.Fields[i].Value.Accept(Self) // Evaluate expression
|
||||
Node.Fields[i].Key.Value,
|
||||
Node.Fields[i].Value.Accept(Self) // Evaluate expression
|
||||
);
|
||||
end;
|
||||
|
||||
@@ -515,21 +511,21 @@ begin
|
||||
var dynRec := TDynamicRecord.Create(genFields);
|
||||
Result := TDataValue.FromGenericRecord(dynRec);
|
||||
end
|
||||
else if Node is TRecordLiteralNode then // Changed
|
||||
else if staticType.Kind = stRecord then
|
||||
begin
|
||||
// --- EXISTING SCALAR PATH ---
|
||||
var boundNode := Node as TRecordLiteralNode; // Changed
|
||||
var values: TArray<TScalar.TValue>;
|
||||
SetLength(values, Length(boundNode.Fields));
|
||||
SetLength(values, Length(Node.Fields));
|
||||
|
||||
for i := 0 to High(boundNode.Fields) do
|
||||
for i := 0 to High(Node.Fields) do
|
||||
begin
|
||||
var valData := boundNode.Fields[i].Value.Accept(Self);
|
||||
// Binder guarantees these are vkScalar
|
||||
var valData := Node.Fields[i].Value.Accept(Self);
|
||||
// TypeChecker guarantees these are vkScalar
|
||||
values[i] := valData.AsScalar.Value;
|
||||
end;
|
||||
|
||||
var rec := TScalarRecord.Create(boundNode.Definition, values);
|
||||
// Get the definition from the type
|
||||
var rec := TScalarRecord.Create(staticType.Definition, values);
|
||||
Result := TDataValue.FromRecord(rec);
|
||||
end
|
||||
else
|
||||
@@ -539,7 +535,6 @@ end;
|
||||
function TEvaluatorVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
|
||||
var
|
||||
address: TResolvedAddress;
|
||||
boundNode: TVariableDeclarationNode; // Changed
|
||||
begin
|
||||
// First, evaluate the initializer to get the variable's initial value.
|
||||
if Assigned(Node.Initializer) then
|
||||
@@ -547,12 +542,11 @@ begin
|
||||
else
|
||||
Result := TDataValue.Void;
|
||||
|
||||
// After binding, all declaration nodes are TVariableDeclarationNode
|
||||
boundNode := Node as TVariableDeclarationNode;
|
||||
address := boundNode.Identifier.AsIdentifier.Address;
|
||||
// Access properties via interface
|
||||
address := Node.Identifier.Address;
|
||||
|
||||
// Check the IsBoxed flag set by the binder.
|
||||
if boundNode.IsBoxed then
|
||||
if Node.IsBoxed then
|
||||
begin
|
||||
// This is a captured variable. Use the clean scope method to create the box.
|
||||
Assert(address.ScopeDepth = 0);
|
||||
@@ -641,7 +635,7 @@ var
|
||||
seriesValue: TDataValue;
|
||||
len: Int64;
|
||||
begin
|
||||
seriesValue := FScope[Node.Series.AsIdentifier.Address]; // Changed
|
||||
seriesValue := FScope[Node.Series.Address];
|
||||
|
||||
case seriesValue.Kind of
|
||||
vkSeries: len := seriesValue.AsSeries.Count;
|
||||
|
||||
Reference in New Issue
Block a user