Node immutability improved
This commit is contained in:
+15
-13
@@ -454,9 +454,10 @@ begin
|
||||
begin
|
||||
for var expr in rootNode.AsBlockExpression.Expressions do
|
||||
begin
|
||||
if expr is TVariableDeclarationNode then
|
||||
// Use interface 'is' check
|
||||
if (expr.Kind = akVariableDeclaration) then
|
||||
begin
|
||||
var decl := expr as TVariableDeclarationNode;
|
||||
var decl := expr.AsVariableDeclaration;
|
||||
definitions.Add(decl.Identifier.Name, decl.Initializer);
|
||||
end
|
||||
// Handle macro definitions inside the block
|
||||
@@ -468,9 +469,10 @@ begin
|
||||
end;
|
||||
end;
|
||||
end
|
||||
else if rootNode is TVariableDeclarationNode then // Handle single definition
|
||||
// Use interface 'is' check
|
||||
else if rootNode.Kind = akVariableDeclaration then // Handle single definition
|
||||
begin
|
||||
var decl := rootNode as TVariableDeclarationNode;
|
||||
var decl := rootNode.AsVariableDeclaration;
|
||||
definitions.Add(decl.Identifier.Name, decl.Initializer);
|
||||
end
|
||||
// Handle a single macro definition
|
||||
@@ -828,7 +830,7 @@ begin
|
||||
TAst.VarDecl(
|
||||
TAst.Identifier('closeColumn'),
|
||||
TAst.FunctionCall(TAst.Keyword('Close'), [TAst.Identifier('ohlcvSeries')])
|
||||
// TAst.MemberAccess(TAst.Identifier('ohlcvSeries'), TAst.Identifier('Close'))
|
||||
// TAst.MemberAccess(TAst.Identifier('ohlcvSeries'), TAst.Identifier('Close'))
|
||||
),
|
||||
TAst.Indexer(TAst.Identifier('closeColumn'), TAst.Constant(1))
|
||||
]
|
||||
@@ -1348,14 +1350,14 @@ begin
|
||||
|
||||
FWorkspace.Build(FCurrUnboundAst, TPointF.Create(X, Y));
|
||||
//
|
||||
// if FCurrAst <> nil then
|
||||
// begin
|
||||
// FWorkspace.Build(FCurrAst, TPointF.Create(X, Y));
|
||||
// end
|
||||
// else if FCurrUnboundAst <> nil then
|
||||
// begin
|
||||
// FWorkspace.Build(FCurrUnboundAst, TPointF.Create(X, Y));
|
||||
// end;
|
||||
// if FCurrAst <> nil then
|
||||
// begin
|
||||
// FWorkspace.Build(FCurrAst, TPointF.Create(X, Y));
|
||||
// end
|
||||
// else if FCurrUnboundAst <> nil then
|
||||
// begin
|
||||
// FWorkspace.Build(FCurrUnboundAst, TPointF.Create(X, Y));
|
||||
// end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -134,22 +134,33 @@ end;
|
||||
|
||||
function TUpvalueAnalyzer.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
|
||||
var
|
||||
N: TLambdaExpressionNode;
|
||||
newParams: TArray<IIdentifierNode>;
|
||||
newBody: IAstNode;
|
||||
i: Integer;
|
||||
begin
|
||||
N := (Node as TLambdaExpressionNode);
|
||||
|
||||
// A lambda creates a new lexical scope, inheriting from the current one.
|
||||
FCurrentDescriptor := TScope.CreateDescriptor(FCurrentDescriptor);
|
||||
try
|
||||
// Define the lambda's parameters within its new scope.
|
||||
// We use TTypes.Unknown as type inference hasn't run yet.
|
||||
for var param in N.Parameters do
|
||||
FCurrentDescriptor.Define(Accept(param).AsIdentifier.Name, TTypes.Unknown);
|
||||
// Manually visit parameters to define them
|
||||
var oldParams := Node.Parameters;
|
||||
SetLength(newParams, Length(oldParams));
|
||||
for i := 0 to High(oldParams) do
|
||||
begin
|
||||
// Accept(param) will call VisitIdentifier, which does its job.
|
||||
// We *must* use the result of Accept.
|
||||
newParams[i] := Accept(oldParams[i]).AsIdentifier;
|
||||
FCurrentDescriptor.Define(newParams[i].Name, TTypes.Unknown);
|
||||
end;
|
||||
|
||||
// Traverse the lambda body within the new scope context.
|
||||
N.Body := Accept(N.Body); // Manual traversal
|
||||
newBody := Accept(Node.Body); // Manual traversal
|
||||
|
||||
Result := N;
|
||||
// Rebuild node if changed
|
||||
if (newParams = Node.Parameters) and (newBody = Node.Body) then
|
||||
Result := Node
|
||||
else
|
||||
// Use factory, pass through other properties (which are nil/false at this stage)
|
||||
Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, Node.StaticType);
|
||||
finally
|
||||
// Restore the parent scope after leaving the lambda.
|
||||
FCurrentDescriptor := FCurrentDescriptor.Parent;
|
||||
@@ -159,31 +170,40 @@ end;
|
||||
function TUpvalueAnalyzer.VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode;
|
||||
var
|
||||
scopeDeclarations: TDictionary<string, IVariableDeclarationNode>;
|
||||
N: TVariableDeclarationNode;
|
||||
newInitializer: IAstNode;
|
||||
newIdentifier: IIdentifierNode;
|
||||
begin
|
||||
N := (Node as TVariableDeclarationNode);
|
||||
|
||||
// Traverse the initializer first. It's evaluated in the current scope
|
||||
// before the new variable is defined.
|
||||
if Assigned(N.Initializer) then
|
||||
N.Initializer := Accept(N.Initializer);
|
||||
if Assigned(Node.Initializer) then
|
||||
newInitializer := Accept(Node.Initializer)
|
||||
else
|
||||
newInitializer := nil;
|
||||
|
||||
// Traverse the identifier
|
||||
Accept(N.Identifier);
|
||||
// This calls VisitIdentifier, but VisitIdentifier is just an analyzer, it returns the same node.
|
||||
newIdentifier := Accept(Node.Identifier).AsIdentifier;
|
||||
|
||||
// After processing the initializer, define the variable in the current scope.
|
||||
// We use TTypes.Unknown as type inference hasn't run yet.
|
||||
FCurrentDescriptor.Define(N.Identifier.Name, TTypes.Unknown);
|
||||
FCurrentDescriptor.Define(newIdentifier.Name, TTypes.Unknown);
|
||||
|
||||
// Map this declaration node to its scope and name for later lookup.
|
||||
// --- Rebuild Node ---
|
||||
// Use CoW, check if anything changed
|
||||
if (newInitializer = Node.Initializer) and (newIdentifier = Node.Identifier) then
|
||||
Result := Node
|
||||
else
|
||||
// Use factory, pass through other properties (which are nil/false at this stage)
|
||||
Result := TAst.VarDecl(newIdentifier, newInitializer, Node.StaticType, Node.IsBoxed);
|
||||
|
||||
// Map this *new* declaration node to its scope and name for later lookup.
|
||||
if not FDeclarationMap.TryGetValue(FCurrentDescriptor, scopeDeclarations) then
|
||||
begin
|
||||
scopeDeclarations := TDictionary<string, IVariableDeclarationNode>.Create;
|
||||
FDeclarationMap.Add(FCurrentDescriptor, scopeDeclarations);
|
||||
end;
|
||||
scopeDeclarations.Add(N.Identifier.Name, N);
|
||||
|
||||
Result := N;
|
||||
// IMPORTANT: Store the *new* node (Result)
|
||||
scopeDeclarations.Add(newIdentifier.Name, Result.AsVariableDeclaration);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
+54
-47
@@ -41,7 +41,8 @@ type
|
||||
function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
|
||||
// --- Transformation Logic ---
|
||||
|
||||
// --- Transformation Logic (Restored) ---
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
|
||||
|
||||
// --- Standard Traversal (Use inherited) ---
|
||||
@@ -69,6 +70,7 @@ implementation
|
||||
uses
|
||||
System.Generics.Defaults,
|
||||
System.Character,
|
||||
Myc.Ast.Types, // Added for TTypes.Unknown
|
||||
Myc.Data.Keyword;
|
||||
|
||||
type
|
||||
@@ -172,6 +174,7 @@ end;
|
||||
function TAstBinder.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
|
||||
begin
|
||||
// --- Transformation: Keyword-as-Function ---
|
||||
// This transformation must happen *before* type checking.
|
||||
if Node.Callee.Kind = akKeyword then
|
||||
begin
|
||||
var keywordNode := Node.Callee.AsKeyword;
|
||||
@@ -180,22 +183,24 @@ begin
|
||||
'Keyword :%s expects exactly one argument (the record/map), but got %d',
|
||||
[keywordNode.Value.Name, Length(Node.Arguments)]);
|
||||
|
||||
// Manually visit the argument, as we are replacing this node
|
||||
// Manually visit the argument (which is the base)
|
||||
var baseNode := Accept(Node.Arguments[0]);
|
||||
var memberAccessNode := TAst.MemberAccess(baseNode, keywordNode);
|
||||
// Manually visit the keyword (which is the member)
|
||||
var memberNode := Accept(keywordNode).AsKeyword;
|
||||
|
||||
// Visit the *new* node to bind it
|
||||
// Create the new MemberAccess node
|
||||
var memberAccessNode := TAst.MemberAccess(baseNode, memberNode);
|
||||
|
||||
// Visit the *new* node to bind it (though it has no bindable symbols)
|
||||
Result := Accept(memberAccessNode);
|
||||
exit;
|
||||
end;
|
||||
|
||||
// --- Default: Bind as a standard function call ---
|
||||
// Use the inherited implementation to visit children (Callee, Arguments)
|
||||
// and mutate their properties in place.
|
||||
Result := inherited VisitFunctionCall(Node);
|
||||
|
||||
// Set metadata for *this* node
|
||||
(Node as TFunctionCallNode).IsTailCall := False; // Default, TCO (Phase 5) will set this
|
||||
// Set metadata for *this* node (IsTailCall is set later by TCO)
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitConstant(const Node: IConstantNode): IAstNode;
|
||||
@@ -233,11 +238,14 @@ end;
|
||||
function TAstBinder.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
|
||||
var
|
||||
i: integer;
|
||||
N: TLambdaExpressionNode;
|
||||
adr: TResolvedAddress;
|
||||
scopeDescriptor: IScopeDescriptor;
|
||||
upvalues: TArray<TResolvedAddress>;
|
||||
hasNestedLambdas: Boolean;
|
||||
lastNestedLambdaCount: Integer;
|
||||
newParams: TArray<IIdentifierNode>;
|
||||
newBody: IAstNode;
|
||||
begin
|
||||
N := (Node as TLambdaExpressionNode);
|
||||
|
||||
// We do *not* call inherited, as we must manage the scope manually.
|
||||
|
||||
FUpvalueStack.Push(TUpvalueMapping.Create(TResolvedAddressComparer.Create));
|
||||
@@ -248,26 +256,24 @@ begin
|
||||
FCurrentDescriptor.Define('<self>');
|
||||
|
||||
// Define parameters
|
||||
for i := 0 to High(N.Parameters) do
|
||||
SetLength(newParams, Length(Node.Parameters));
|
||||
for i := 0 to High(Node.Parameters) do
|
||||
begin
|
||||
var paramNode := N.Parameters[i]; // This is a TIdentifierNode
|
||||
var paramNode := Node.Parameters[i]; // This is a TIdentifierNode
|
||||
var slotIndex := FCurrentDescriptor.Define(paramNode.Name);
|
||||
adr := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
|
||||
|
||||
// --- Replace Node ---
|
||||
// Create a new TBoundIdentifierNode (implementation is in Myc.Ast)
|
||||
var boundParamNode := TAst.Identifier(paramNode.Name, adr);
|
||||
// Replace it in the parameter array
|
||||
N.Parameters[i] := boundParamNode;
|
||||
// Create a new TIdentifierNode
|
||||
newParams[i] := TAst.Identifier(paramNode.Name, adr, TTypes.Unknown);
|
||||
end;
|
||||
|
||||
// Visit the body *within the new scope*
|
||||
var lastNestedLambdaCount := FNestedLambdaCount;
|
||||
N.Body := Accept(N.Body);
|
||||
N.HasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount;
|
||||
lastNestedLambdaCount := FNestedLambdaCount;
|
||||
newBody := Accept(Node.Body);
|
||||
hasNestedLambdas := FNestedLambdaCount > lastNestedLambdaCount;
|
||||
|
||||
// Save the descriptor for the evaluator
|
||||
N.ScopeDescriptor := FCurrentDescriptor;
|
||||
scopeDescriptor := FCurrentDescriptor;
|
||||
finally
|
||||
ExitScope;
|
||||
end;
|
||||
@@ -283,19 +289,18 @@ begin
|
||||
)
|
||||
);
|
||||
|
||||
var uvArr: TArray<TResolvedAddress>;
|
||||
SetLength(uvArr, Length(sortedPairs));
|
||||
for i := 0 to High(uvArr) do
|
||||
uvArr[i] := sortedPairs[i].Key;
|
||||
N.Upvalues := uvArr;
|
||||
SetLength(upvalues, Length(sortedPairs));
|
||||
for i := 0 to High(upvalues) do
|
||||
upvalues[i] := sortedPairs[i].Key;
|
||||
|
||||
finally
|
||||
FUpvalueStack.Pop;
|
||||
end;
|
||||
|
||||
inc(FNestedLambdaCount);
|
||||
// Type will be set by TypeChecker
|
||||
Result := Node;
|
||||
|
||||
// Create new immutable node using the factory
|
||||
Result := TAst.LambdaExpr(newParams, newBody, scopeDescriptor, upvalues, hasNestedLambdas);
|
||||
end;
|
||||
|
||||
function TAstBinder.VisitRecurNode(const Node: IRecurNode): IAstNode;
|
||||
@@ -310,8 +315,9 @@ var
|
||||
symbol: TResolvedSymbol;
|
||||
adr: TResolvedAddress;
|
||||
begin
|
||||
// We only bind nodes that are the base parser type.
|
||||
if Node.Kind = akIdentifier then
|
||||
// We only bind nodes that are the base parser type
|
||||
// (i.e., address is unresolved).
|
||||
if Node.Address.Kind = akUnresolved then
|
||||
begin
|
||||
symbol := FCurrentDescriptor.FindSymbol(Node.Name);
|
||||
adr := symbol.Address;
|
||||
@@ -344,12 +350,11 @@ begin
|
||||
// else: It was already an upvalue (e.g. nested lambda), adr is correct.
|
||||
|
||||
// --- Replace Node ---
|
||||
// Create the new TBoundIdentifierNode (implementation is in Myc.Ast)
|
||||
Result := TAst.Identifier(Node.Name, adr);
|
||||
Result := TAst.Identifier(Node.Name, adr, TTypes.Unknown); // TypeChecker will set type
|
||||
end
|
||||
else
|
||||
begin
|
||||
// It's already an akBoundIdentifier (or other specialized type), just return it.
|
||||
// It's already bound (e.g., a parameter), just return it.
|
||||
Result := Node;
|
||||
end;
|
||||
end;
|
||||
@@ -358,29 +363,31 @@ function TAstBinder.VisitVariableDeclaration(const Node: IVariableDeclarationNod
|
||||
var
|
||||
slotIndex: Integer;
|
||||
address: TResolvedAddress;
|
||||
N: TVariableDeclarationNode;
|
||||
isBoxed: Boolean;
|
||||
newInitializer: IAstNode;
|
||||
newIdentifier: IIdentifierNode;
|
||||
begin
|
||||
N := (Node as TVariableDeclarationNode);
|
||||
|
||||
if not IsValidIdentifier(N.Identifier.Name) then
|
||||
raise Exception.CreateFmt('Invalid identifier name: "%s".', [N.Identifier.Name]);
|
||||
if not IsValidIdentifier(Node.Identifier.Name) then
|
||||
raise Exception.CreateFmt('Invalid identifier name: "%s".', [Node.Identifier.Name]);
|
||||
|
||||
// 1. Visit initializer *first*
|
||||
if Assigned(N.Initializer) then
|
||||
N.Initializer := Accept(N.Initializer);
|
||||
if Assigned(Node.Initializer) then
|
||||
newInitializer := Accept(Node.Initializer)
|
||||
else
|
||||
newInitializer := nil;
|
||||
|
||||
// 2. Define variable in *current* scope
|
||||
slotIndex := FCurrentDescriptor.Define(N.Identifier.Name);
|
||||
slotIndex := FCurrentDescriptor.Define(Node.Identifier.Name);
|
||||
address := TResolvedAddress.Create(akLocalOrParent, 0, slotIndex);
|
||||
|
||||
// 3. --- Replace Node ---
|
||||
// Replace the TIdentifierNode with a new TBoundIdentifierNode
|
||||
N.Identifier := TAst.Identifier(N.Identifier.Name, address);
|
||||
// 3. Create the new bound identifier
|
||||
newIdentifier := TAst.Identifier(Node.Identifier.Name, address, TTypes.Unknown);
|
||||
|
||||
// 4. Mutate this declaration node
|
||||
N.IsBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node);
|
||||
// 4. Check if this declaration should be boxed
|
||||
isBoxed := (FBoxedDeclarations <> nil) and FBoxedDeclarations.Contains(Node);
|
||||
|
||||
Result := Node;
|
||||
// 5. Create the new immutable node
|
||||
Result := TAst.VarDecl(newIdentifier, newInitializer, TTypes.Unknown, isBoxed);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -184,29 +184,23 @@ end;
|
||||
|
||||
function TAstTCO.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
|
||||
var
|
||||
N: TLambdaExpressionNode;
|
||||
newParams: TArray<IIdentifierNode>;
|
||||
newBody: IAstNode;
|
||||
begin
|
||||
N := (Node as TLambdaExpressionNode);
|
||||
|
||||
// Parameters are not in tail position (handled by AcceptParameters)
|
||||
FNextIsTail := False;
|
||||
newParams := AcceptParameters(N.Parameters);
|
||||
newParams := AcceptParameters(Node.Parameters);
|
||||
|
||||
// The body of a lambda is *always* a tail position (relative to the lambda)
|
||||
FNextIsTail := True;
|
||||
newBody := Accept(N.Body);
|
||||
newBody := Accept(Node.Body);
|
||||
|
||||
if (newParams = N.Parameters) and (newBody = N.Body) then
|
||||
if (newParams = Node.Parameters) and (newBody = Node.Body) then
|
||||
Result := Node
|
||||
else
|
||||
begin
|
||||
Result := TLambdaExpressionNode.Create(newParams, newBody, N.StaticType);
|
||||
// Copy runtime properties
|
||||
(Result as TLambdaExpressionNode).ScopeDescriptor := N.ScopeDescriptor;
|
||||
(Result as TLambdaExpressionNode).Upvalues := N.Upvalues;
|
||||
(Result as TLambdaExpressionNode).HasNestedLambdas := N.HasNestedLambdas;
|
||||
// Rebuild using factory and copy properties via interface
|
||||
Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, Node.StaticType);
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -245,26 +239,23 @@ end;
|
||||
function TAstTCO.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
|
||||
var
|
||||
isTailCall: Boolean;
|
||||
N: TFunctionCallNode;
|
||||
newCallee: IAstNode;
|
||||
newArgs: TArray<IAstNode>;
|
||||
begin
|
||||
isTailCall := FIsTailStack.Peek;
|
||||
N := (Node as TFunctionCallNode);
|
||||
|
||||
// Arguments are not in tail position
|
||||
FNextIsTail := False;
|
||||
newCallee := Accept(N.Callee);
|
||||
newArgs := AcceptNodes(N.Arguments);
|
||||
newCallee := Accept(Node.Callee);
|
||||
newArgs := AcceptNodes(Node.Arguments);
|
||||
|
||||
// CoW check: Create a new node only if children changed OR IsTailCall needs update
|
||||
if (newCallee = N.Callee) and (newArgs = N.Arguments) and (isTailCall = N.IsTailCall) then
|
||||
if (newCallee = Node.Callee) and (newArgs = Node.Arguments) and (isTailCall = Node.IsTailCall) then
|
||||
Result := Node
|
||||
else
|
||||
begin
|
||||
Result := TFunctionCallNode.Create(newCallee, newArgs, N.StaticType);
|
||||
// Mutate the *new* node with the TCO status
|
||||
(Result as TFunctionCallNode).IsTailCall := isTailCall;
|
||||
// Use factory to create new node, passing the *new* TCO status
|
||||
Result := TAst.FunctionCall(newCallee, newArgs, Node.StaticType, isTailCall);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
+17
-26
@@ -73,7 +73,6 @@ implementation
|
||||
|
||||
uses
|
||||
Myc.Data.Keyword;
|
||||
// Myc.Ast.Binding.Nodes; // Removed
|
||||
|
||||
{ TAstDumper }
|
||||
|
||||
@@ -146,7 +145,7 @@ procedure TAstDumper.VisitIdentifier(const Node: IIdentifierNode);
|
||||
var
|
||||
adr: TResolvedAddress;
|
||||
begin
|
||||
adr := Node.AsIdentifier.Address;
|
||||
adr := Node.Address;
|
||||
|
||||
if adr.Kind <> akUnresolved then
|
||||
LogFmt('Identifier: %s -> %s', [Node.Name, FormatAddress(adr)])
|
||||
@@ -209,38 +208,35 @@ procedure TAstDumper.VisitLambdaExpression(const Node: ILambdaExpressionNode);
|
||||
var
|
||||
upvalueAddr: TResolvedAddress;
|
||||
pair: TPair<string, Integer>;
|
||||
boundNode: TLambdaExpressionNode;
|
||||
begin
|
||||
boundNode := Node as TLambdaExpressionNode;
|
||||
|
||||
if Assigned(boundNode.ScopeDescriptor) then
|
||||
if Assigned(Node.ScopeDescriptor) then
|
||||
begin
|
||||
LogFmt('LambdaExpression (HasNested: %s)', [boundNode.HasNestedLambdas.ToString(TUseBoolStrs.True)]);
|
||||
LogFmt('LambdaExpression (HasNested: %s)', [Node.HasNestedLambdas.ToString(TUseBoolStrs.True)]);
|
||||
Indent;
|
||||
|
||||
Log('Parameters:');
|
||||
Indent;
|
||||
for var param in boundNode.Parameters do
|
||||
for var param in Node.Parameters do
|
||||
param.Accept(Self);
|
||||
Unindent;
|
||||
|
||||
LogFmt('Upvalues (%d):', [Length(boundNode.Upvalues)]);
|
||||
LogFmt('Upvalues (%d):', [Length(Node.Upvalues)]);
|
||||
Indent;
|
||||
for upvalueAddr in boundNode.Upvalues do
|
||||
for upvalueAddr in Node.Upvalues do
|
||||
Log(FormatAddress(upvalueAddr));
|
||||
Unindent;
|
||||
|
||||
Log('Scope Descriptor:');
|
||||
Indent;
|
||||
if Assigned(boundNode.ScopeDescriptor) then
|
||||
for pair in boundNode.ScopeDescriptor.Symbols do
|
||||
if Assigned(Node.ScopeDescriptor) then
|
||||
for pair in Node.ScopeDescriptor.Symbols do
|
||||
LogFmt('"%s" -> Slot %d', [pair.Key, pair.Value])
|
||||
else
|
||||
Log('(none)');
|
||||
Unindent;
|
||||
|
||||
Log('Body:');
|
||||
boundNode.Body.Accept(Self);
|
||||
Node.Body.Accept(Self);
|
||||
|
||||
Unindent;
|
||||
end
|
||||
@@ -262,17 +258,15 @@ end;
|
||||
procedure TAstDumper.VisitFunctionCall(const Node: IFunctionCallNode);
|
||||
var
|
||||
arg: IAstNode;
|
||||
N: TFunctionCallNode;
|
||||
begin
|
||||
N := (Node as TFunctionCallNode);
|
||||
LogFmt('FunctionCall (IsTailCall: %s)', [N.IsTailCall.ToString(TUseBoolStrs.True)]);
|
||||
LogFmt('FunctionCall (IsTailCall: %s)', [Node.IsTailCall.ToString(TUseBoolStrs.True)]);
|
||||
|
||||
Indent;
|
||||
Log('Callee:');
|
||||
N.Callee.Accept(Self);
|
||||
LogFmt('Arguments (%d):', [Length(N.Arguments)]);
|
||||
Node.Callee.Accept(Self);
|
||||
LogFmt('Arguments (%d):', [Length(Node.Arguments)]);
|
||||
Indent;
|
||||
for arg in N.Arguments do
|
||||
for arg in Node.Arguments do
|
||||
arg.Accept(Self);
|
||||
Unindent;
|
||||
Unindent;
|
||||
@@ -331,18 +325,15 @@ begin
|
||||
end;
|
||||
|
||||
procedure TAstDumper.VisitVariableDeclaration(const Node: IVariableDeclarationNode);
|
||||
var
|
||||
N: TVariableDeclarationNode;
|
||||
begin
|
||||
N := (Node as TVariableDeclarationNode);
|
||||
LogFmt('VariableDeclaration (IsBoxed: %s)', [N.IsBoxed.ToString(TUseBoolStrs.True)]);
|
||||
LogFmt('VariableDeclaration (IsBoxed: %s)', [Node.IsBoxed.ToString(TUseBoolStrs.True)]);
|
||||
|
||||
Indent;
|
||||
N.Identifier.Accept(Self);
|
||||
if Assigned(N.Initializer) then
|
||||
Node.Identifier.Accept(Self);
|
||||
if Assigned(Node.Initializer) then
|
||||
begin
|
||||
Log('Initializer:');
|
||||
N.Initializer.Accept(Self);
|
||||
Node.Initializer.Accept(Self);
|
||||
end;
|
||||
Unindent;
|
||||
end;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -91,12 +91,30 @@ var
|
||||
left, right: IAstNode;
|
||||
nodeType: IStaticType;
|
||||
begin
|
||||
// Get the type *before* replacing the node (it's an IAstTypedNode)
|
||||
nodeType := Node.AsTypedNode.StaticType;
|
||||
|
||||
// --- Transformation: Keyword-as-Function ---
|
||||
if Node.Callee.Kind = akKeyword then
|
||||
begin
|
||||
var keywordNode := Node.Callee.AsKeyword;
|
||||
if Length(Node.Arguments) <> 1 then
|
||||
raise EArgumentException.CreateFmt(
|
||||
'Keyword :%s expects exactly one argument (the record/map), but got %d',
|
||||
[keywordNode.Value.Name, Length(Node.Arguments)]);
|
||||
|
||||
// Visit the base node
|
||||
var baseNode := Accept(Node.Arguments[0]);
|
||||
|
||||
// Create the new MemberAccess node, preserving the type
|
||||
Result := TAst.MemberAccess(baseNode, keywordNode, nodeType);
|
||||
exit;
|
||||
end;
|
||||
|
||||
// --- Optimization: Operator Folding ---
|
||||
if Node.Callee.Kind = akIdentifier then
|
||||
begin
|
||||
calleeIdentifier := Node.Callee.AsIdentifier;
|
||||
// Get the type *before* replacing the node (it's an IAstTypedNode)
|
||||
nodeType := Node.AsTypedNode.StaticType;
|
||||
|
||||
if (Length(Node.Arguments) = 2) then
|
||||
begin
|
||||
|
||||
@@ -237,18 +237,26 @@ type
|
||||
{$region 'private'}
|
||||
function GetParameters: TArray<IIdentifierNode>;
|
||||
function GetBody: IAstNode;
|
||||
function GetScopeDescriptor: IScopeDescriptor;
|
||||
function GetUpvalues: TArray<TResolvedAddress>;
|
||||
function GetHasNestedLambdas: Boolean;
|
||||
{$endregion}
|
||||
property Parameters: TArray<IIdentifierNode> read GetParameters;
|
||||
property Body: IAstNode read GetBody;
|
||||
property ScopeDescriptor: IScopeDescriptor read GetScopeDescriptor;
|
||||
property Upvalues: TArray<TResolvedAddress> read GetUpvalues;
|
||||
property HasNestedLambdas: Boolean read GetHasNestedLambdas;
|
||||
end;
|
||||
|
||||
IFunctionCallNode = interface(IAstTypedNode)
|
||||
{$region 'private'}
|
||||
function GetCallee: IAstNode;
|
||||
function GetArguments: TArray<IAstNode>;
|
||||
function GetIsTailCall: Boolean;
|
||||
{$endregion}
|
||||
property Callee: IAstNode read GetCallee;
|
||||
property Arguments: TArray<IAstNode> read GetArguments;
|
||||
property IsTailCall: Boolean read GetIsTailCall;
|
||||
end;
|
||||
|
||||
// A node representing a macro call.
|
||||
@@ -281,9 +289,11 @@ type
|
||||
{$region 'private'}
|
||||
function GetIdentifier: IIdentifierNode;
|
||||
function GetInitializer: IAstNode;
|
||||
function GetIsBoxed: Boolean;
|
||||
{$endregion}
|
||||
property Identifier: IIdentifierNode read GetIdentifier;
|
||||
property Initializer: IAstNode read GetInitializer;
|
||||
property IsBoxed: Boolean read GetIsBoxed;
|
||||
end;
|
||||
|
||||
IAssignmentNode = interface(IAstTypedNode)
|
||||
|
||||
@@ -111,7 +111,7 @@ begin
|
||||
|
||||
// Get the type from the descriptor (which was populated by Binder/RTL)
|
||||
symbol := FCurrentDescriptor.FindSymbol(Node.Name);
|
||||
adr := Node.AsIdentifier.Address;
|
||||
adr := Node.Address;
|
||||
|
||||
// Create a new node, copying the address and assigning the inferred type
|
||||
Result := TAst.Identifier(Node.Name, adr, symbol.StaticType);
|
||||
@@ -135,9 +135,7 @@ function TTypeChecker.VisitVariableDeclaration(const Node: IVariableDeclarationN
|
||||
var
|
||||
initType: IStaticType;
|
||||
newInitializer, newIdent: IAstNode;
|
||||
boundIdent: IIdentifierNode;
|
||||
adr: TResolvedAddress;
|
||||
N: TVariableDeclarationNode;
|
||||
begin
|
||||
// 1. Visit Initializer first (if it exists)
|
||||
if Assigned(Node.Initializer) then
|
||||
@@ -151,23 +149,24 @@ begin
|
||||
else
|
||||
initType := TTypes.Unknown; // (def fib)
|
||||
|
||||
// 3. Get the address from the bound identifier (SAFE CAST)
|
||||
boundIdent := Node.Identifier;
|
||||
adr := boundIdent.AsIdentifier.Address;
|
||||
// 3. Get the address from the bound identifier
|
||||
adr := Node.Identifier.Address;
|
||||
|
||||
// 4. Update the type in the scope descriptor (which was set to Unknown by the binder).
|
||||
if initType.Kind <> stUnknown then
|
||||
FCurrentDescriptor.UpdateType(adr.SlotIndex, initType);
|
||||
|
||||
// 5. Create the new (typed) identifier node
|
||||
newIdent := TAst.Identifier(boundIdent.Name, adr, initType);
|
||||
newIdent := TAst.Identifier(Node.Identifier.Name, adr, initType);
|
||||
|
||||
// 6. Create the new VariableDeclaration node
|
||||
Result := TVariableDeclarationNode.Create(newIdent.AsIdentifier, newInitializer, initType);
|
||||
|
||||
// 7. Copy runtime flags (IsBoxed)
|
||||
N := (Result as TVariableDeclarationNode);
|
||||
N.IsBoxed := (Node as TVariableDeclarationNode).IsBoxed;
|
||||
// 6. Create the new VariableDeclaration node using the factory
|
||||
Result :=
|
||||
TAst.VarDecl(
|
||||
newIdent.AsIdentifier,
|
||||
newInitializer,
|
||||
initType,
|
||||
Node.IsBoxed // 7. Copy runtime flags (IsBoxed) via interface
|
||||
);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitAssignment(const Node: IAssignmentNode): IAstNode;
|
||||
@@ -206,7 +205,6 @@ end;
|
||||
|
||||
function TTypeChecker.VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode;
|
||||
var
|
||||
boundNode: TLambdaExpressionNode;
|
||||
newParams: TArray<IIdentifierNode>;
|
||||
newBody: IAstNode;
|
||||
bodyType, methodType: IStaticType;
|
||||
@@ -214,22 +212,20 @@ var
|
||||
i: Integer;
|
||||
savedDescriptor: IScopeDescriptor;
|
||||
begin
|
||||
boundNode := (Node as TLambdaExpressionNode);
|
||||
|
||||
// 1. Enter the lambda's scope (which Binder already created)
|
||||
savedDescriptor := FCurrentDescriptor;
|
||||
FCurrentDescriptor := boundNode.ScopeDescriptor;
|
||||
FCurrentDescriptor := Node.ScopeDescriptor;
|
||||
try
|
||||
// 2. Visit parameters (they are already bound, just need typing)
|
||||
SetLength(newParams, Length(boundNode.Parameters));
|
||||
SetLength(paramTypes, Length(boundNode.Parameters));
|
||||
SetLength(newParams, Length(Node.Parameters));
|
||||
SetLength(paramTypes, Length(Node.Parameters));
|
||||
|
||||
for i := 0 to High(boundNode.Parameters) do
|
||||
for i := 0 to High(Node.Parameters) do
|
||||
begin
|
||||
// Parameters are leaves, but we must *replace* them with typed versions
|
||||
// (even if they are just TTypes.Unknown for now, for type inference placeholders)
|
||||
var paramIdent := boundNode.Parameters[i];
|
||||
var paramAdr := paramIdent.AsIdentifier.Address;
|
||||
var paramIdent := Node.Parameters[i];
|
||||
var paramAdr := paramIdent.Address;
|
||||
var newParam := TAst.Identifier(paramIdent.Name, paramAdr);
|
||||
|
||||
newParams[i] := newParam;
|
||||
@@ -237,7 +233,7 @@ begin
|
||||
end;
|
||||
|
||||
// 3. Visit the body to infer its return type
|
||||
newBody := Accept(boundNode.Body);
|
||||
newBody := Accept(Node.Body);
|
||||
bodyType := newBody.AsTypedNode.StaticType;
|
||||
|
||||
// 4. Create the final method type
|
||||
@@ -251,14 +247,8 @@ begin
|
||||
FCurrentDescriptor := savedDescriptor;
|
||||
end;
|
||||
|
||||
// 7. Create the new (typed) lambda node
|
||||
Result := TLambdaExpressionNode.Create(newParams, newBody, methodType);
|
||||
|
||||
// 8. Copy runtime properties
|
||||
var newLambda := (Result as TLambdaExpressionNode);
|
||||
newLambda.ScopeDescriptor := boundNode.ScopeDescriptor;
|
||||
newLambda.Upvalues := boundNode.Upvalues;
|
||||
newLambda.HasNestedLambdas := boundNode.HasNestedLambdas;
|
||||
// 7. Create the new (typed) lambda node using the factory
|
||||
Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, methodType);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitFunctionCall(const Node: IFunctionCallNode): IAstNode;
|
||||
@@ -300,11 +290,14 @@ begin
|
||||
else if calleeType.Kind <> TStaticTypeKind.stUnknown then
|
||||
raise ETypeException.CreateFmt('Cannot invoke type %s as a function.', [calleeType.ToString]);
|
||||
|
||||
// 4. Create the new (typed) call node
|
||||
Result := TFunctionCallNode.Create(newCallee, newArgs, retType);
|
||||
|
||||
// 5. Copy runtime properties
|
||||
(Result as TFunctionCallNode).IsTailCall := (Node as TFunctionCallNode).IsTailCall;
|
||||
// 4. Create the new (typed) call node using the factory
|
||||
Result :=
|
||||
TAst.FunctionCall(
|
||||
newCallee,
|
||||
newArgs,
|
||||
retType,
|
||||
Node.IsTailCall // 5. Copy runtime properties
|
||||
);
|
||||
end;
|
||||
|
||||
function TTypeChecker.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
|
||||
@@ -508,17 +501,14 @@ var
|
||||
valType: IStaticType;
|
||||
scalarKind: TScalar.TKind;
|
||||
allScalar: Boolean;
|
||||
oldNode: TGenericRecordLiteralNode; // Parser creates this type
|
||||
newFields: TArray<TRecordFieldLiteral>;
|
||||
begin
|
||||
oldNode := (Node as TGenericRecordLiteralNode);
|
||||
|
||||
// 1. Visit all child nodes first to infer their types
|
||||
SetLength(newFields, Length(oldNode.Fields));
|
||||
for i := 0 to High(oldNode.Fields) do
|
||||
SetLength(newFields, Length(Node.Fields));
|
||||
for i := 0 to High(Node.Fields) do
|
||||
begin
|
||||
newFields[i].Key := Accept(oldNode.Fields[i].Key).AsKeyword;
|
||||
newFields[i].Value := Accept(oldNode.Fields[i].Value);
|
||||
newFields[i].Key := Accept(Node.Fields[i].Key).AsKeyword;
|
||||
newFields[i].Value := Accept(Node.Fields[i].Value);
|
||||
end;
|
||||
|
||||
SetLength(scalarDefFields, Length(newFields));
|
||||
|
||||
+21
-32
@@ -527,22 +527,17 @@ function TAstTransformer.VisitLambdaExpression(const Node: ILambdaExpressionNode
|
||||
var
|
||||
newParams: TArray<IIdentifierNode>;
|
||||
newBody: IAstNode;
|
||||
N: TLambdaExpressionNode;
|
||||
begin
|
||||
N := (Node as TLambdaExpressionNode); // This cast is valid
|
||||
newParams := AcceptParameters(N.Parameters);
|
||||
newBody := Accept(N.Body);
|
||||
// No longer cast to concrete class, use interface
|
||||
newParams := AcceptParameters(Node.Parameters);
|
||||
newBody := Accept(Node.Body);
|
||||
|
||||
if (newParams = N.Parameters) and (newBody = N.Body) then
|
||||
if (newParams = Node.Parameters) and (newBody = Node.Body) then
|
||||
Result := Node
|
||||
else
|
||||
begin
|
||||
// Use TAst factory
|
||||
Result := TAst.LambdaExpr(newParams, newBody, N.StaticType);
|
||||
// Copy runtime properties (cast is valid)
|
||||
(Result as TLambdaExpressionNode).ScopeDescriptor := N.ScopeDescriptor;
|
||||
(Result as TLambdaExpressionNode).Upvalues := N.Upvalues;
|
||||
(Result as TLambdaExpressionNode).HasNestedLambdas := N.HasNestedLambdas;
|
||||
// Use TAst factory and copy properties via interface getters
|
||||
Result := TAst.LambdaExpr(newParams, newBody, Node.ScopeDescriptor, Node.Upvalues, Node.HasNestedLambdas, Node.StaticType);
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -550,20 +545,17 @@ function TAstTransformer.VisitFunctionCall(const Node: IFunctionCallNode): IAstN
|
||||
var
|
||||
newCallee: IAstNode;
|
||||
newArgs: TArray<IAstNode>;
|
||||
N: TFunctionCallNode;
|
||||
begin
|
||||
N := (Node as TFunctionCallNode); // This cast is valid
|
||||
newCallee := Accept(N.Callee);
|
||||
newArgs := AcceptNodes(N.Arguments);
|
||||
// No longer cast to concrete class, use interface
|
||||
newCallee := Accept(Node.Callee);
|
||||
newArgs := AcceptNodes(Node.Arguments);
|
||||
|
||||
if (newCallee = N.Callee) and (newArgs = N.Arguments) then
|
||||
if (newCallee = Node.Callee) and (newArgs = Node.Arguments) then
|
||||
Result := Node
|
||||
else
|
||||
begin
|
||||
// Use TAst factory
|
||||
Result := TAst.FunctionCall(newCallee, newArgs, N.StaticType);
|
||||
// Copy runtime properties (cast is valid)
|
||||
(Result as TFunctionCallNode).IsTailCall := N.IsTailCall;
|
||||
// Use TAst factory and copy properties via interface getters
|
||||
Result := TAst.FunctionCall(newCallee, newArgs, Node.StaticType, Node.IsTailCall);
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -597,20 +589,17 @@ function TAstTransformer.VisitVariableDeclaration(const Node: IVariableDeclarati
|
||||
var
|
||||
newIdent: IIdentifierNode;
|
||||
newInit: IAstNode;
|
||||
N: TVariableDeclarationNode;
|
||||
begin
|
||||
N := (Node as TVariableDeclarationNode); // This cast is valid
|
||||
newIdent := Accept(N.Identifier).AsIdentifier;
|
||||
newInit := Accept(N.Initializer); // Accept handles nil
|
||||
// No longer cast to concrete class, use interface
|
||||
newIdent := Accept(Node.Identifier).AsIdentifier;
|
||||
newInit := Accept(Node.Initializer); // Accept handles nil
|
||||
|
||||
if (newIdent = N.Identifier) and (newInit = N.Initializer) then
|
||||
if (newIdent = Node.Identifier) and (newInit = Node.Initializer) then
|
||||
Result := Node
|
||||
else
|
||||
begin
|
||||
// Use TAst factory
|
||||
Result := TAst.VarDecl(newIdent, newInit, N.StaticType);
|
||||
// Copy runtime properties (cast is valid)
|
||||
(Result as TVariableDeclarationNode).IsBoxed := N.IsBoxed;
|
||||
// Use TAst factory and copy properties via interface getters
|
||||
Result := TAst.VarDecl(newIdent, newInit, Node.StaticType, Node.IsBoxed);
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -708,12 +697,12 @@ end;
|
||||
|
||||
function TAstTransformer.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
|
||||
var
|
||||
N: TRecordLiteralNode;
|
||||
N: IRecordLiteralNode; // Use interface
|
||||
i: Integer;
|
||||
newFields: TArray<TRecordFieldLiteral>;
|
||||
hasChanged: Boolean;
|
||||
begin
|
||||
N := (Node as TRecordLiteralNode); // This cast is valid
|
||||
N := Node;
|
||||
SetLength(newFields, Length(N.Fields));
|
||||
hasChanged := False;
|
||||
|
||||
@@ -737,7 +726,7 @@ begin
|
||||
if N is TGenericRecordLiteralNode then
|
||||
(Result as TGenericRecordLiteralNode).GenericDefinition := (N as TGenericRecordLiteralNode).GenericDefinition
|
||||
else
|
||||
(Result as TRecordLiteralNode).Definition := N.Definition;
|
||||
(Result as TRecordLiteralNode).Definition := (N as TRecordLiteralNode).Definition;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
+158
-69
@@ -9,7 +9,7 @@ uses
|
||||
Myc.Data.Scalar,
|
||||
Myc.Data.Value,
|
||||
Myc.Data.Keyword,
|
||||
Myc.Ast.Nodes, // Contains TAstNodeKind, IAstNode, etc.
|
||||
Myc.Ast.Nodes,
|
||||
Myc.Ast.Scope,
|
||||
Myc.Ast.Types;
|
||||
|
||||
@@ -63,11 +63,17 @@ type
|
||||
const AThenBranch, AElseBranch: IAstNode;
|
||||
const AStaticType: IStaticType = nil
|
||||
): ITernaryExpressionNode; static;
|
||||
|
||||
// Updated Factory Signature
|
||||
class function LambdaExpr(
|
||||
const AParameters: TArray<IIdentifierNode>;
|
||||
const ABody: IAstNode;
|
||||
const AScopeDescriptor: IScopeDescriptor = nil;
|
||||
const AUpvalues: TArray<TResolvedAddress> = nil;
|
||||
const AHasNestedLambdas: Boolean = False;
|
||||
const AStaticType: IStaticType = nil
|
||||
): ILambdaExpressionNode; static;
|
||||
|
||||
class function MacroDef(
|
||||
const AName: IIdentifierNode;
|
||||
const AParameters: TArray<IIdentifierNode>;
|
||||
@@ -76,22 +82,30 @@ type
|
||||
class function Quasiquote(const AExpression: IAstNode): IQuasiquoteNode; static;
|
||||
class function Unquote(const AExpression: IAstNode): IUnquoteNode; static;
|
||||
class function UnquoteSplicing(const AExpression: IQuasiquoteNode): IUnquoteSplicingNode; static;
|
||||
|
||||
// Updated Factory Signature
|
||||
class function FunctionCall(
|
||||
const ACallee: IAstNode;
|
||||
const AArguments: TArray<IAstNode>;
|
||||
const AStaticType: IStaticType = nil
|
||||
const AStaticType: IStaticType = nil;
|
||||
const AIsTailCall: Boolean = False
|
||||
): IFunctionCallNode; static;
|
||||
|
||||
class function MacroExpansionNode(
|
||||
const AOriginalCallNode: IFunctionCallNode;
|
||||
const AExpandedBody: IAstNode
|
||||
): IMacroExpansionNode; static;
|
||||
class function Recur(const AArguments: array of IAstNode; const AStaticType: IStaticType = nil): IRecurNode; static;
|
||||
class function Block(const AExpressions: array of IAstNode; const AStaticType: IStaticType = nil): IBlockExpressionNode; static;
|
||||
|
||||
// Updated Factory Signature
|
||||
class function VarDecl(
|
||||
const AIdentifier: IIdentifierNode;
|
||||
AInitializer: IAstNode = nil;
|
||||
const AStaticType: IStaticType = nil
|
||||
const AStaticType: IStaticType = nil;
|
||||
const AIsBoxed: Boolean = False
|
||||
): IVariableDeclarationNode; static;
|
||||
|
||||
class function Assign(
|
||||
const AIdentifier: IIdentifierNode;
|
||||
const AValue: IAstNode;
|
||||
@@ -172,62 +186,8 @@ type
|
||||
property StaticType: IStaticType read GetStaticType;
|
||||
end;
|
||||
|
||||
TLambdaExpressionNode = class(TAstTypedNode, ILambdaExpressionNode)
|
||||
private
|
||||
FParameters: TArray<IIdentifierNode>;
|
||||
FBody: IAstNode;
|
||||
FScopeDescriptor: IScopeDescriptor;
|
||||
FUpvalues: TArray<TResolvedAddress>;
|
||||
FHasNestedLambdas: Boolean;
|
||||
function GetParameters: TArray<IIdentifierNode>;
|
||||
function GetBody: IAstNode;
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AParameters: TArray<IIdentifierNode>; const ABody: IAstNode; const AStaticType: IStaticType);
|
||||
destructor Destroy; override;
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
function AsLambdaExpression: ILambdaExpressionNode; override;
|
||||
property Body: IAstNode read FBody write FBody;
|
||||
property Parameters: TArray<IIdentifierNode> read FParameters;
|
||||
property ScopeDescriptor: IScopeDescriptor read FScopeDescriptor write FScopeDescriptor;
|
||||
property Upvalues: TArray<TResolvedAddress> read FUpvalues write FUpvalues;
|
||||
property HasNestedLambdas: Boolean read FHasNestedLambdas write FHasNestedLambdas;
|
||||
end;
|
||||
|
||||
TFunctionCallNode = class(TAstTypedNode, IFunctionCallNode)
|
||||
private
|
||||
FCallee: IAstNode;
|
||||
FArguments: TArray<IAstNode>;
|
||||
FIsTailCall: Boolean;
|
||||
function GetCallee: IAstNode;
|
||||
function GetArguments: TArray<IAstNode>;
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const ACallee: IAstNode; const AArguments: TArray<IAstNode>; const AStaticType: IStaticType);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
function AsFunctionCall: IFunctionCallNode; override;
|
||||
property Callee: IAstNode read FCallee;
|
||||
property Arguments: TArray<IAstNode> read FArguments;
|
||||
property IsTailCall: Boolean read FIsTailCall write FIsTailCall;
|
||||
end;
|
||||
|
||||
TVariableDeclarationNode = class(TAstTypedNode, IVariableDeclarationNode)
|
||||
private
|
||||
FIdentifier: IIdentifierNode;
|
||||
FInitializer: IAstNode;
|
||||
FIsBoxed: Boolean;
|
||||
function GetIdentifier: IIdentifierNode;
|
||||
function GetInitializer: IAstNode;
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(const AIdentifier: IIdentifierNode; AInitializer: IAstNode; const AStaticType: IStaticType);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
function AsVariableDeclaration: IVariableDeclarationNode; override;
|
||||
property Identifier: IIdentifierNode read FIdentifier write FIdentifier;
|
||||
property Initializer: IAstNode read FInitializer write FInitializer;
|
||||
property IsBoxed: Boolean read FIsBoxed write FIsBoxed;
|
||||
end;
|
||||
|
||||
// TRecordLiteralNode is still needed in the interface
|
||||
// for TGenericRecordLiteralNode
|
||||
TRecordLiteralNode = class(TAstTypedNode, IRecordLiteralNode)
|
||||
private
|
||||
FFields: TArray<TRecordFieldLiteral>;
|
||||
@@ -256,6 +216,77 @@ uses
|
||||
System.Generics.Defaults;
|
||||
|
||||
type
|
||||
// --- Concrete Class Definitions moved to implementation ---
|
||||
|
||||
TLambdaExpressionNode = class(TAstTypedNode, ILambdaExpressionNode)
|
||||
private
|
||||
FParameters: TArray<IIdentifierNode>;
|
||||
FBody: IAstNode;
|
||||
FScopeDescriptor: IScopeDescriptor;
|
||||
FUpvalues: TArray<TResolvedAddress>;
|
||||
FHasNestedLambdas: Boolean;
|
||||
function GetParameters: TArray<IIdentifierNode>;
|
||||
function GetBody: IAstNode;
|
||||
function GetScopeDescriptor: IScopeDescriptor;
|
||||
function GetUpvalues: TArray<TResolvedAddress>;
|
||||
function GetHasNestedLambdas: Boolean;
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
const AParameters: TArray<IIdentifierNode>;
|
||||
const ABody: IAstNode;
|
||||
const AStaticType: IStaticType;
|
||||
const AScopeDescriptor: IScopeDescriptor;
|
||||
const AUpvalues: TArray<TResolvedAddress>;
|
||||
const AHasNestedLambdas: Boolean
|
||||
);
|
||||
destructor Destroy; override;
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
function AsLambdaExpression: ILambdaExpressionNode; override;
|
||||
end;
|
||||
|
||||
TFunctionCallNode = class(TAstTypedNode, IFunctionCallNode)
|
||||
private
|
||||
FCallee: IAstNode;
|
||||
FArguments: TArray<IAstNode>;
|
||||
FIsTailCall: Boolean;
|
||||
function GetCallee: IAstNode;
|
||||
function GetArguments: TArray<IAstNode>;
|
||||
function GetIsTailCall: Boolean;
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
const ACallee: IAstNode;
|
||||
const AArguments: TArray<IAstNode>;
|
||||
const AStaticType: IStaticType;
|
||||
const AIsTailCall: Boolean
|
||||
);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
function AsFunctionCall: IFunctionCallNode; override;
|
||||
end;
|
||||
|
||||
TVariableDeclarationNode = class(TAstTypedNode, IVariableDeclarationNode)
|
||||
private
|
||||
FIdentifier: IIdentifierNode;
|
||||
FInitializer: IAstNode;
|
||||
FIsBoxed: Boolean;
|
||||
function GetIdentifier: IIdentifierNode;
|
||||
function GetInitializer: IAstNode;
|
||||
function GetIsBoxed: Boolean;
|
||||
function GetKind: TAstNodeKind; override;
|
||||
public
|
||||
constructor Create(
|
||||
const AIdentifier: IIdentifierNode;
|
||||
AInitializer: IAstNode;
|
||||
const AStaticType: IStaticType;
|
||||
const AIsBoxed: Boolean
|
||||
);
|
||||
function Accept(const Visitor: IAstVisitor): TDataValue; override;
|
||||
function AsVariableDeclaration: IVariableDeclarationNode; override;
|
||||
end;
|
||||
|
||||
// --- Other internal node classes ---
|
||||
|
||||
TConstantNode = class(TAstTypedNode, IConstantNode)
|
||||
private
|
||||
FValue: TDataValue;
|
||||
@@ -700,15 +731,18 @@ end;
|
||||
class function TAst.FunctionCall(
|
||||
const ACallee: IAstNode;
|
||||
const AArguments: TArray<IAstNode>;
|
||||
const AStaticType: IStaticType = nil
|
||||
const AStaticType: IStaticType = nil;
|
||||
const AIsTailCall: Boolean = False
|
||||
): IFunctionCallNode;
|
||||
begin
|
||||
// Updated to call new constructor
|
||||
Result :=
|
||||
TFunctionCallNode.Create(
|
||||
ACallee,
|
||||
AArguments,
|
||||
if AStaticType <> nil then AStaticType
|
||||
else TTypes.Unknown
|
||||
else TTypes.Unknown,
|
||||
AIsTailCall
|
||||
);
|
||||
end;
|
||||
|
||||
@@ -769,15 +803,22 @@ end;
|
||||
class function TAst.LambdaExpr(
|
||||
const AParameters: TArray<IIdentifierNode>;
|
||||
const ABody: IAstNode;
|
||||
const AScopeDescriptor: IScopeDescriptor = nil;
|
||||
const AUpvalues: TArray<TResolvedAddress> = nil;
|
||||
const AHasNestedLambdas: Boolean = False;
|
||||
const AStaticType: IStaticType = nil
|
||||
): ILambdaExpressionNode;
|
||||
begin
|
||||
// Updated to call new constructor
|
||||
Result :=
|
||||
TLambdaExpressionNode.Create(
|
||||
AParameters,
|
||||
ABody,
|
||||
if AStaticType <> nil then AStaticType
|
||||
else TTypes.Unknown
|
||||
else TTypes.Unknown,
|
||||
AScopeDescriptor,
|
||||
AUpvalues,
|
||||
AHasNestedLambdas
|
||||
);
|
||||
end;
|
||||
|
||||
@@ -898,15 +939,18 @@ end;
|
||||
class function TAst.VarDecl(
|
||||
const AIdentifier: IIdentifierNode;
|
||||
AInitializer: IAstNode = nil;
|
||||
const AStaticType: IStaticType = nil
|
||||
const AStaticType: IStaticType = nil;
|
||||
const AIsBoxed: Boolean = False
|
||||
): IVariableDeclarationNode;
|
||||
begin
|
||||
// Updated to call new constructor
|
||||
Result :=
|
||||
TVariableDeclarationNode.Create(
|
||||
AIdentifier,
|
||||
AInitializer,
|
||||
if AStaticType <> nil then AStaticType
|
||||
else TTypes.Unknown
|
||||
else TTypes.Unknown,
|
||||
AIsBoxed
|
||||
);
|
||||
end;
|
||||
|
||||
@@ -1350,11 +1394,21 @@ end;
|
||||
|
||||
{ TLambdaExpressionNode }
|
||||
|
||||
constructor TLambdaExpressionNode.Create(const AParameters: TArray<IIdentifierNode>; const ABody: IAstNode; const AStaticType: IStaticType);
|
||||
constructor TLambdaExpressionNode.Create(
|
||||
const AParameters: TArray<IIdentifierNode>;
|
||||
const ABody: IAstNode;
|
||||
const AStaticType: IStaticType;
|
||||
const AScopeDescriptor: IScopeDescriptor;
|
||||
const AUpvalues: TArray<TResolvedAddress>;
|
||||
const AHasNestedLambdas: Boolean
|
||||
);
|
||||
begin
|
||||
inherited Create(AStaticType);
|
||||
FParameters := AParameters;
|
||||
FBody := ABody;
|
||||
FScopeDescriptor := AScopeDescriptor;
|
||||
FUpvalues := AUpvalues;
|
||||
FHasNestedLambdas := AHasNestedLambdas;
|
||||
end;
|
||||
|
||||
destructor TLambdaExpressionNode.Destroy;
|
||||
@@ -1383,6 +1437,21 @@ begin
|
||||
Result := FParameters;
|
||||
end;
|
||||
|
||||
function TLambdaExpressionNode.GetScopeDescriptor: IScopeDescriptor;
|
||||
begin
|
||||
Result := FScopeDescriptor;
|
||||
end;
|
||||
|
||||
function TLambdaExpressionNode.GetUpvalues: TArray<TResolvedAddress>;
|
||||
begin
|
||||
Result := FUpvalues;
|
||||
end;
|
||||
|
||||
function TLambdaExpressionNode.GetHasNestedLambdas: Boolean;
|
||||
begin
|
||||
Result := FHasNestedLambdas;
|
||||
end;
|
||||
|
||||
function TLambdaExpressionNode.GetKind: TAstNodeKind;
|
||||
begin
|
||||
Result := akLambdaExpression;
|
||||
@@ -1514,12 +1583,17 @@ end;
|
||||
|
||||
{ TFunctionCallNode }
|
||||
|
||||
constructor TFunctionCallNode.Create(const ACallee: IAstNode; const AArguments: TArray<IAstNode>; const AStaticType: IStaticType);
|
||||
constructor TFunctionCallNode.Create(
|
||||
const ACallee: IAstNode;
|
||||
const AArguments: TArray<IAstNode>;
|
||||
const AStaticType: IStaticType;
|
||||
const AIsTailCall: Boolean
|
||||
);
|
||||
begin
|
||||
inherited Create(AStaticType);
|
||||
FCallee := ACallee;
|
||||
FArguments := AArguments;
|
||||
FIsTailCall := False;
|
||||
FIsTailCall := AIsTailCall;
|
||||
end;
|
||||
|
||||
function TFunctionCallNode.Accept(const Visitor: IAstVisitor): TDataValue;
|
||||
@@ -1542,6 +1616,11 @@ begin
|
||||
Result := FCallee;
|
||||
end;
|
||||
|
||||
function TFunctionCallNode.GetIsTailCall: Boolean;
|
||||
begin
|
||||
Result := FIsTailCall;
|
||||
end;
|
||||
|
||||
function TFunctionCallNode.GetKind: TAstNodeKind;
|
||||
begin
|
||||
Result := akFunctionCall;
|
||||
@@ -1644,12 +1723,17 @@ end;
|
||||
|
||||
{ TVariableDeclarationNode }
|
||||
|
||||
constructor TVariableDeclarationNode.Create(const AIdentifier: IIdentifierNode; AInitializer: IAstNode; const AStaticType: IStaticType);
|
||||
constructor TVariableDeclarationNode.Create(
|
||||
const AIdentifier: IIdentifierNode;
|
||||
AInitializer: IAstNode;
|
||||
const AStaticType: IStaticType;
|
||||
const AIsBoxed: Boolean
|
||||
);
|
||||
begin
|
||||
inherited Create(AStaticType);
|
||||
FIdentifier := AIdentifier;
|
||||
FInitializer := AInitializer;
|
||||
FIsBoxed := False;
|
||||
FIsBoxed := AIsBoxed;
|
||||
end;
|
||||
|
||||
function TVariableDeclarationNode.Accept(const Visitor: IAstVisitor): TDataValue;
|
||||
@@ -1672,6 +1756,11 @@ begin
|
||||
Result := FInitializer;
|
||||
end;
|
||||
|
||||
function TVariableDeclarationNode.GetIsBoxed: Boolean;
|
||||
begin
|
||||
Result := FIsBoxed;
|
||||
end;
|
||||
|
||||
function TVariableDeclarationNode.GetKind: TAstNodeKind;
|
||||
begin
|
||||
Result := akVariableDeclaration;
|
||||
|
||||
Reference in New Issue
Block a user