From 65342c99aae36c1e1be98346256c434b30208db2 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Tue, 25 Nov 2025 20:19:43 +0100 Subject: [PATCH] AST Identities --- ASTPlayground/Myc.Fmx.AstEditor.Node.pas | 108 +++++++++++++++++------ 1 file changed, 83 insertions(+), 25 deletions(-) diff --git a/ASTPlayground/Myc.Fmx.AstEditor.Node.pas b/ASTPlayground/Myc.Fmx.AstEditor.Node.pas index 55917f8..07df5a4 100644 --- a/ASTPlayground/Myc.Fmx.AstEditor.Node.pas +++ b/ASTPlayground/Myc.Fmx.AstEditor.Node.pas @@ -21,6 +21,7 @@ uses Myc.Data.Keyword, Myc.Ast.Nodes, Myc.Ast.Visitor, + Myc.Ast.Identities, // NEU: Für Casts Myc.Ast, Myc.Fmx.AstEditor.Workspace; @@ -597,7 +598,6 @@ begin end; // --- Visitor Implementations --- -// *** CHANGED: All visitor methods now create a Handler and inject it into a generic TAuraNode *** function TAstVisualizer.VisitConstant(const Node: IConstantNode): TAuraNode; var @@ -1298,7 +1298,8 @@ end; function TConstantNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := FNode; // Literals are immutable + // Reuse Identity: FNode.Identity.AsConstant + Result := TAst.Constant(FNode.Identity.AsConstant, FNode.StaticType); end; { TIdentifierNodeHandler } @@ -1327,9 +1328,8 @@ end; function TIdentifierNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - // Find the label text if needed, but for now, re-use FNode's data - // In a real editor, you'd fetch text from the TEdit/TLabel - Result := TAst.Identifier(FNode.Name); + // Reusing identity guarantees the same name and source location + Result := TAst.Identifier(FNode.Identity.AsNamed, FNode.Address, FNode.StaticType); end; { TKeywordNodeHandler } @@ -1360,7 +1360,7 @@ end; function TKeywordNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := FNode; // Keywords are literals + Result := TAst.Keyword(FNode.Identity.AsKeyword); end; { TBlockExpressionNodeHandler } @@ -1439,7 +1439,9 @@ begin else exprs[i] := TAst.Nop; // Use Nop as placeholder end; - Result := TAst.Block(exprs); + + // Reuse structural identity + Result := TAst.Block(FNode.Identity, exprs, FNode.StaticType); end; { TIfExpressionNodeHandler } @@ -1482,7 +1484,7 @@ begin else elseAst := nil; - Result := TAst.IfExpr(FConditionNode.CreateAst, FThenNode.CreateAst, elseAst); + Result := TAst.IfExpr(FNode.Identity, FConditionNode.CreateAst, FThenNode.CreateAst, elseAst, FNode.StaticType); end; { TTernaryExpressionNodeHandler } @@ -1519,7 +1521,7 @@ end; function TTernaryExpressionNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := TAst.TernaryExpr(FConditionNode.CreateAst, FThenNode.CreateAst, FElseNode.CreateAst); + Result := TAst.TernaryExpr(FNode.Identity, FConditionNode.CreateAst, FThenNode.CreateAst, FElseNode.CreateAst, FNode.StaticType); end; { TLambdaExpressionNodeHandler } @@ -1592,7 +1594,19 @@ end; function TLambdaExpressionNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin // Params are read from FNode (assuming they are not editable via UI labels for now) - Result := TAst.LambdaExpr(FNode.Parameters, FBodyNode.CreateAst); + // Reuse Identity & Metadata + Result := + TAst.LambdaExpr( + FNode.Identity, + FNode.Parameters, + FBodyNode.CreateAst, + FNode.Layout, + FNode.Descriptor, + FNode.Upvalues, + FNode.HasNestedLambdas, + FNode.IsPure, + FNode.StaticType + ); end; { TFunctionCallNodeHandler } @@ -1658,7 +1672,16 @@ begin for i := 0 to FArgumentNodes.Count - 1 do args[i] := FArgumentNodes[i].CreateAst; - Result := TAst.FunctionCall(FCalleeNode.CreateAst, args); + Result := + TAst.FunctionCall( + FNode.Identity, + FCalleeNode.CreateAst, + args, + FNode.StaticType, + FNode.IsTailCall, + FNode.StaticTarget, + FNode.IsTargetPure + ); end; { TMacroExpansionNodeHandler } @@ -1694,7 +1717,12 @@ end; function TMacroExpansionNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := TAst.MacroExpansionNode(FNode.CallNode, FExpandedBodyNode.CreateAst.AsTypedNode); + Result := + TAst.MacroExpansionNode( + FNode.Identity, + FNode.CallNode, + FExpandedBodyNode.CreateAst.AsTypedNode // TODO: Check if AsTypedNode is correct/safe + ); end; { TRecurNodeHandler } @@ -1755,7 +1783,7 @@ begin SetLength(args, FArgumentNodes.Count); for i := 0 to FArgumentNodes.Count - 1 do args[i] := FArgumentNodes[i].CreateAst; - Result := TAst.Recur(args); + Result := TAst.Recur(FNode.Identity, args, FNode.StaticType); end; { TVariableDeclarationNodeHandler } @@ -1806,7 +1834,7 @@ begin initAst := FInitializerNode.CreateAst else initAst := nil; - Result := TAst.VarDecl(FTargetNode.CreateAst, initAst); + Result := TAst.VarDecl(FNode.Identity, FTargetNode.CreateAst, initAst, FNode.StaticType, FNode.IsBoxed); end; { TAssignmentNodeHandler } @@ -1841,7 +1869,7 @@ end; function TAssignmentNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := TAst.Assign(FTargetNode.CreateAst, FValueNode.CreateAst); + Result := TAst.Assign(FNode.Identity, FTargetNode.CreateAst, FValueNode.CreateAst, FNode.StaticType); end; { TMacroDefinitionNodeHandler } @@ -1889,7 +1917,16 @@ end; function TMacroDefinitionNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := TAst.MacroDef(TAst.Identifier(FNode.Name.Name), FNode.Parameters, FBodyNode.CreateAst); + // Note: Identifier is recreated from raw name here, losing Location if we don't be careful. + // Ideally FNode.Name would be reused. + // For now, we reconstruct the MacroDef using the original identity and parameters. + Result := + TAst.MacroDef( + FNode.Identity, + FNode.Name, // Reuse Identifier Node + FNode.Parameters, // Reuse Parameters + FBodyNode.CreateAst + ); end; { TQuasiquoteNodeHandler } @@ -1925,7 +1962,7 @@ end; function TQuasiquoteNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := TAst.Quasiquote(FExpressionNode.CreateAst); + Result := TAst.Quasiquote(FNode.Identity, FExpressionNode.CreateAst); end; { TUnquoteNodeHandler } @@ -1967,7 +2004,7 @@ end; function TUnquoteNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := TAst.Unquote(FExpressionNode.CreateAst); + Result := TAst.Unquote(FNode.Identity, FExpressionNode.CreateAst); end; { TUnquoteSplicingNodeHandler } @@ -1995,6 +2032,7 @@ begin OwnerNode.Orientation := loVertical; titleLabel := OwnerNode.AddLabel(OwnerNode, 'Unquote-Splicing'); titleLabel.Font.Style := titleLabel.Font.Style + [TFontStyle.fsBold]; + // Note: FNode.Expression is a IQuasiquoteNode FExpressionNode := visu.CallAccept(FNode.Expression); finally OwnerNode.EndUpdate; @@ -2003,7 +2041,9 @@ end; function TUnquoteSplicingNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := TAst.UnquoteSplicing(FExpressionNode.CreateAst.AsQuasiquote); + // Note: We assume the child node is still a QuasiquoteNode. If user changed it to something else, + // this cast might fail or we need validation logic. + Result := TAst.UnquoteSplicing(FNode.Identity, FExpressionNode.CreateAst.AsQuasiquote); end; { TIndexerNodeHandler } @@ -2039,7 +2079,7 @@ end; function TIndexerNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := TAst.Indexer(FBaseNode.CreateAst, FIndexNode.CreateAst); + Result := TAst.Indexer(FNode.Identity, FBaseNode.CreateAst, FIndexNode.CreateAst, FNode.StaticType); end; { TMemberAccessNodeHandler } @@ -2074,7 +2114,8 @@ end; function TMemberAccessNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := TAst.MemberAccess(FBaseNode.CreateAst, TAst.Keyword(FNode.Member.Value.Name)); + // Reuse Identifier, Reuse Member Keyword (via TAst.Keyword with Identity) + Result := TAst.MemberAccess(FNode.Identity, FBaseNode.CreateAst, TAst.Keyword(FNode.Member.Identity.AsKeyword), FNode.StaticType); end; { TRecordLiteralNodeHandler } @@ -2145,10 +2186,12 @@ begin for pair in FFieldNodes do begin valueNode := pair.Value; + // Note: We construct a new keyword node here for the key, losing location unless we stored it. + // For simplicity, we use Creation Path for the Key, but Reuse Identity for the RecordLiteral itself. fields[i] := TRecordFieldLiteral.Create(TAst.Keyword(pair.Key.Name), valueNode.CreateAst); inc(i); end; - Result := TAst.RecordLiteral(fields); + Result := TAst.RecordLiteral(FNode.Identity, fields, FNode.ScalarDefinition, FNode.GenericDefinition, FNode.StaticType); end; { TCreateSeriesNodeHandler } @@ -2181,7 +2224,8 @@ end; function TCreateSeriesNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := TAst.CreateSeries(FNode.Definition); + // Identity.AsDefinition check needed? Or trust CreateSeries overload + Result := TAst.CreateSeries(FNode.Identity.AsDefinition, FNode.StaticType); end; { TAddSeriesItemNodeHandler } @@ -2229,7 +2273,21 @@ begin else lookbackAst := nil; - Result := TAst.AddSeriesItem(TAst.Identifier(FNode.Series.Name), FValueNode.CreateAst, lookbackAst); + // Reusing identifier identity requires access to it. + // Here we create a new identifier with same name but lose location of series ident. + // Optimization: FNode.Series is available, we can reuse it! + + // Wait, TAst.AddSeriesItem takes IIdentifierNode. We can just reuse FNode.Series? + // Yes, if the series name is not editable in UI. + + Result := + TAst.AddSeriesItem( + FNode.Identity, + FNode.Series, // Reuse original Series Identifier Node + FValueNode.CreateAst, + lookbackAst, + FNode.StaticType + ); end; { TSeriesLengthNodeHandler } @@ -2262,7 +2320,7 @@ end; function TSeriesLengthNodeHandler.ReconstructAst(OwnerNode: TAuraNode): IAstNode; begin - Result := TAst.SeriesLength(TAst.Identifier(FNode.Series.Name)); + Result := TAst.SeriesLength(FNode.Identity, FNode.Series, FNode.StaticType); end; { TNopNodeHandler }