From 40ed51aef8138ac833c094151813f0a58befd2c8 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Tue, 6 Jan 2026 11:37:18 +0100 Subject: [PATCH] ITupleNode signature change --- ASTPlayground/MainForm.pas | 2 +- KI/gemini.md | 3 +- Src/AST/Myc.Ast.Analysis.Purity.pas | 6 +- Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas | 11 ++- Src/AST/Myc.Ast.Compiler.Binder.pas | 14 +-- Src/AST/Myc.Ast.Compiler.Macros.pas | 43 ++++---- Src/AST/Myc.Ast.Compiler.Specializer.pas | 9 +- Src/AST/Myc.Ast.Compiler.TCO.pas | 22 +++-- Src/AST/Myc.Ast.Compiler.TypeChecker.pas | 99 ++++++++++--------- Src/AST/Myc.Ast.Dumper.pas | 22 +++-- Src/AST/Myc.Ast.Evaluator.pas | 88 +++++++++-------- Src/AST/Myc.Ast.JSON.pas | 27 +++-- Src/AST/Myc.Ast.Nodes.pas | 25 +---- Src/AST/Myc.Ast.Refactoring.Remove.pas | 12 ++- Src/AST/Myc.Ast.Script.Print.pas | 39 ++++---- Src/AST/Myc.Ast.Script.pas | 10 +- Src/AST/Myc.Ast.Visitor.pas | 18 ++-- .../Myc.Fmx.AstEditor.Handlers.Control.pas | 15 ++- Src/AST/Myc.Fmx.AstEditor.Handlers.Lists.pas | 10 +- Src/AST/Myc.Fmx.AstEditor.Handlers.pas | 33 ++++--- Test/AST/Test.Myc.Ast.Compiler.Macros.pas | 3 +- Test/AST/Test.Myc.Ast.Script.pas | 51 ++++++---- Test/Test.Myc.Ast.Compiler.Binder.pas | 48 +++++---- 23 files changed, 340 insertions(+), 270 deletions(-) diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas index faf2170..f5d2f76 100644 --- a/ASTPlayground/MainForm.pas +++ b/ASTPlayground/MainForm.pas @@ -918,7 +918,7 @@ begin try if rootNode.Kind = akBlockExpression then begin - for var expr in rootNode.AsBlockExpression.Expressions.ToArray do + for var expr in rootNode.AsBlockExpression.Expressions.Elements do begin if (expr.Kind = akVariableDeclaration) then begin diff --git a/KI/gemini.md b/KI/gemini.md index fb3c6b4..579a4f9 100644 --- a/KI/gemini.md +++ b/KI/gemini.md @@ -2,7 +2,8 @@ * Unterstütze mich bei der Entwicklung unter Embarcadero Delphi. * Ich bin ein sehr erfahrener Softwareentwickler. Fasse dich kurz und nutze Fachsprache. -* Wir nutzen immer die neueste Delphi-Version, aktuell ist das Delphi 12.3 Athens. +* Du brauchst mich nicht zu loben. +* Wir nutzen immer die neueste Delphi-Version, aktuell ist das Delphi 13. diff --git a/Src/AST/Myc.Ast.Analysis.Purity.pas b/Src/AST/Myc.Ast.Analysis.Purity.pas index c3eb116..46676c3 100644 --- a/Src/AST/Myc.Ast.Analysis.Purity.pas +++ b/Src/AST/Myc.Ast.Analysis.Purity.pas @@ -135,14 +135,14 @@ end; function TPurityAnalyzer.VisitTuple(const Node: IAstNode): Boolean; var - i: Integer; T: ITupleNode; begin T := Node.AsTuple; // A tuple is pure if ALL its elements are pure. - for i := 0 to T.Count - 1 do + // Optimization: Use Elements array directly + for var item in T.Elements do begin - if not IsNodePure(T.Items[i]) then + if not IsNodePure(item) then exit(False); end; Result := True; diff --git a/Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas b/Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas index 6be3ae4..3c0eed9 100644 --- a/Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas +++ b/Src/AST/Myc.Ast.Compiler.Binder.Upvalues.pas @@ -195,7 +195,6 @@ function TUpvalueAnalyzer.VisitLambdaExpression(const Node: IAstNode): IAstNode; var L: ILambdaExpressionNode; i: Integer; - paramTuple: ITupleNode; begin L := Node.AsLambdaExpression; @@ -205,11 +204,13 @@ begin // 2. Register parameters (they mask outer variables) // We pass 'nil' as the node because we currently don't box parameters, // but we must ensure Resolve() finds them so we don't accidentally box a shadowed variable. - paramTuple := L.Parameters; - for i := 0 to paramTuple.Count - 1 do + + // Optimized: Access Elements array directly + var paramElements := L.Parameters.Elements; + for i := 0 to High(paramElements) do begin - if paramTuple.Items[i].Kind = akIdentifier then - FCurrentScope.Define(paramTuple.Items[i].AsIdentifier.Name, nil); + if paramElements[i].Kind = akIdentifier then + FCurrentScope.Define(paramElements[i].AsIdentifier.Name, nil); end; // 3. Visit Body diff --git a/Src/AST/Myc.Ast.Compiler.Binder.pas b/Src/AST/Myc.Ast.Compiler.Binder.pas index 6140515..4787660 100644 --- a/Src/AST/Myc.Ast.Compiler.Binder.pas +++ b/Src/AST/Myc.Ast.Compiler.Binder.pas @@ -343,7 +343,6 @@ var startCount: Integer; hasNested: Boolean; paramIdentity: INamedIdentity; - paramsTuple: ITupleNode; paramNode: IAstNode; begin L := Node.AsLambdaExpression; @@ -359,12 +358,12 @@ begin try FCurrentBuilder.Define(''); - paramsTuple := L.Parameters; - SetLength(newParams, paramsTuple.Count); + var paramElements := L.Parameters.Elements; + SetLength(newParams, Length(paramElements)); - for i := 0 to paramsTuple.Count - 1 do + for i := 0 to High(paramElements) do begin - paramNode := paramsTuple.Items[i]; + paramNode := paramElements[i]; if paramNode.Kind <> akIdentifier then begin @@ -454,14 +453,15 @@ begin // 1. Keyword as Function Logic if C.Callee.Kind = akKeyword then begin - if C.Arguments.Count <> 1 then + var args := C.Arguments.Elements; + if Length(args) <> 1 then begin if Assigned(FLog) then FLog.AddError(Format('Keyword access :%s requires exactly one argument.', [C.Callee.AsKeyword.Value.Name]), Node); end else begin - var memberAccess := TAst.MemberAccess(Node.Identity, Accept(C.Arguments.Items[0]), C.Callee.AsKeyword); + var memberAccess := TAst.MemberAccess(Node.Identity, Accept(args[0]), C.Callee.AsKeyword); Result := memberAccess; exit; end; diff --git a/Src/AST/Myc.Ast.Compiler.Macros.pas b/Src/AST/Myc.Ast.Compiler.Macros.pas index 3793c71..ff2eae0 100644 --- a/Src/AST/Myc.Ast.Compiler.Macros.pas +++ b/Src/AST/Myc.Ast.Compiler.Macros.pas @@ -196,9 +196,11 @@ var begin newList := TList.Create; try - for i := 0 to ANodes.Count - 1 do + // Optimization: Access Elements array directly + var elements := ANodes.Elements; + for i := 0 to High(elements) do begin - node := ANodes.Items[i]; + node := elements[i]; if node.Kind = akUnquoteSplicing then begin var spliceExpr := node.AsUnquoteSplicing.Expression; @@ -218,15 +220,15 @@ begin // Unification: Lists are now Tuples. if nodeToSplice.Kind = akTuple then begin - var tuple := nodeToSplice.AsTuple; - for var k := 0 to tuple.Count - 1 do - newList.Add(tuple.Items[k]); + var tupleElements := nodeToSplice.AsTuple.Elements; + for var k := 0 to High(tupleElements) do + newList.Add(tupleElements[k]); end else if nodeToSplice.Kind = akBlockExpression then begin - var blkTuple := nodeToSplice.AsBlockExpression.Expressions; - for var k := 0 to blkTuple.Count - 1 do - newList.Add(blkTuple.Items[k]); + var blkElements := nodeToSplice.AsBlockExpression.Expressions.Elements; + for var k := 0 to High(blkElements) do + newList.Add(blkElements[k]); end else begin @@ -290,15 +292,14 @@ var newBody: IAstNode; newName: string; i: Integer; - paramsTuple: ITupleNode; begin L := Node.AsLambdaExpression; - paramsTuple := L.Parameters; - SetLength(newParams, paramsTuple.Count); + var paramsElements := L.Parameters.Elements; + SetLength(newParams, Length(paramsElements)); - for i := 0 to paramsTuple.Count - 1 do + for i := 0 to High(paramsElements) do begin - var param := paramsTuple.Items[i].AsIdentifier; // Binder ensures params are Identifiers + var param := paramsElements[i].AsIdentifier; // Binder ensures params are Identifiers newName := Gensym(param.Name); newParams[i] := TAst.Identifier(newName, param.Identity.Location); end; @@ -497,16 +498,18 @@ begin if macroDef <> nil then begin var expansionScope := TScope.CreateScope(FInitialScope, nil, nil); - var paramsTuple := macroDef.Parameters; - var argsTuple := C.Arguments; - if argsTuple.Count <> paramsTuple.Count then - raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, paramsTuple.Count]); + // Optimization: Access elements directly + var paramsElements := macroDef.Parameters.Elements; + var argsElements := C.Arguments.Elements; - for i := 0 to paramsTuple.Count - 1 do + if Length(argsElements) <> Length(paramsElements) then + raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, Length(paramsElements)]); + + for i := 0 to High(paramsElements) do begin - var paramName := paramsTuple.Items[i].AsIdentifier.Name; - expansionScope.Define(paramName, TDataValue.FromIntf(argsTuple.Items[i])); + var paramName := paramsElements[i].AsIdentifier.Name; + expansionScope.Define(paramName, TDataValue.FromIntf(argsElements[i])); end; var expandedBody := TExpansionVisitor.Expand(expansionScope, macroDef.Body.AsQuasiquote.Expression, FMacroEvaluator); diff --git a/Src/AST/Myc.Ast.Compiler.Specializer.pas b/Src/AST/Myc.Ast.Compiler.Specializer.pas index 81d33ab..2a3faa5 100644 --- a/Src/AST/Myc.Ast.Compiler.Specializer.pas +++ b/Src/AST/Myc.Ast.Compiler.Specializer.pas @@ -162,11 +162,14 @@ begin funcName := calleeIdent.Name; // 3. Check if all argument types are statically known + // Optimization: Use Elements array directly + var argsElements := newArgs.Elements; allTypesKnown := True; - SetLength(argTypes, newArgs.Count); - for i := 0 to newArgs.Count - 1 do + SetLength(argTypes, Length(argsElements)); + + for i := 0 to High(argsElements) do begin - argTypes[i] := newArgs.Items[i].AsTypedNode.StaticType; + argTypes[i] := argsElements[i].AsTypedNode.StaticType; if argTypes[i].Kind = stUnknown then begin allTypesKnown := False; diff --git a/Src/AST/Myc.Ast.Compiler.TCO.pas b/Src/AST/Myc.Ast.Compiler.TCO.pas index b6c5972..15bae21 100644 --- a/Src/AST/Myc.Ast.Compiler.TCO.pas +++ b/Src/AST/Myc.Ast.Compiler.TCO.pas @@ -123,19 +123,22 @@ var i: Integer; item, newItem: IAstNode; hasChanged: Boolean; + elements: TArray; begin B := Node.AsBlockExpression; isContextTail := FIsTailStack.Peek; exprsTuple := B.Expressions; + elements := exprsTuple.Elements; - SetLength(newExprs, exprsTuple.Count); + var count := Length(elements); + SetLength(newExprs, count); hasChanged := False; - var nTail := exprsTuple.Count - 1; + var nTail := count - 1; for i := 0 to nTail do begin - item := exprsTuple.Items[i]; + item := elements[i]; // Only the last expression in the block inherits the tail position status FNextIsTail := isContextTail and (i = nTail); @@ -150,8 +153,10 @@ begin if not hasChanged then Result := Node else + begin // Create new Tuple for expressions Result := TAst.Block(Node.Identity, TAst.Tuple(B.Expressions.Identity, newExprs), B.StaticType); + end; end; function TAstTCO.VisitIfExpression(const Node: IAstNode): IAstNode; @@ -324,6 +329,7 @@ var i: Integer; hasChanged: Boolean; savedNextIsTail: Boolean; + elements: TArray; begin T := Node.AsTuple; savedNextIsTail := FNextIsTail; // Zustand sichern @@ -332,13 +338,15 @@ begin FNextIsTail := False; hasChanged := False; - SetLength(newElements, T.Count); + elements := T.Elements; + var count := Length(elements); + SetLength(newElements, count); try - for i := 0 to T.Count - 1 do + for i := 0 to count - 1 do begin - newElements[i] := Accept(T.Items[i]); - if newElements[i] <> T.Items[i] then + newElements[i] := Accept(elements[i]); + if newElements[i] <> elements[i] then hasChanged := True; end; finally diff --git a/Src/AST/Myc.Ast.Compiler.TypeChecker.pas b/Src/AST/Myc.Ast.Compiler.TypeChecker.pas index 96ca832..4e6a353 100644 --- a/Src/AST/Myc.Ast.Compiler.TypeChecker.pas +++ b/Src/AST/Myc.Ast.Compiler.TypeChecker.pas @@ -314,9 +314,11 @@ var newDim: TArray; finalType: IStaticType; + elementsArray: TArray; begin T := Node.AsTuple; - var count := T.Count; + elementsArray := T.Elements; + var count := Length(elementsArray); SetLength(newElements, count); SetLength(elementTypes, count); @@ -325,7 +327,7 @@ begin // Recursively type-check all elements first to determine their static types. for i := 0 to count - 1 do begin - newElements[i] := Accept(T.Items[i]); + newElements[i] := Accept(elementsArray[i]); if newElements[i].IsTyped then elementTypes[i] := newElements[i].AsTypedNode.StaticType else @@ -503,8 +505,7 @@ function TTypeChecker.VisitBlockExpression(const Node: IAstNode): IAstNode; var newBlock: IBlockExpressionNode; blockType: IStaticType; - exprs: ITupleNode; - i: Integer; + exprs: TArray; begin var transformedNode := inherited VisitBlockExpression(Node); @@ -512,30 +513,25 @@ begin raise ECompilationFailed.Create([TCompilerError.Create(elError, 'Internal Error: Block transformation returned nil.', Node)]); newBlock := transformedNode.AsBlockExpression; - exprs := newBlock.Expressions; + exprs := newBlock.Expressions.Elements; - if exprs.Count > 0 then + if Length(exprs) > 0 then begin - for i := 0 to exprs.Count - 1 do - begin - if exprs.Items[i] = nil then - raise ECompilationFailed.Create( - [TCompilerError.Create(elError, Format('Internal Error: Block expression #%d transformed to nil.', [i]), Node)]); - end; - blockType := exprs.Items[exprs.Count - 1].AsTypedNode.StaticType; + // The type of the block is the type of the last expression + blockType := exprs[High(exprs)].AsTypedNode.StaticType; end else begin blockType := TTypes.Void; end; - Result := TAst.Block(Node.Identity, exprs, blockType); + Result := TAst.Block(Node.Identity, newBlock.Expressions, blockType); end; function TTypeChecker.VisitLambdaExpression(const Node: IAstNode): IAstNode; var L: ILambdaExpressionNode; - newParams: TArray; + newParams: TArray; newBody: IAstNode; bodyType, methodType: IStaticType; paramTypes: TArray; @@ -545,7 +541,7 @@ var paramIdent: IIdentifierNode; injectedType: IStaticType; paramsTuple: ITupleNode; - paramsAsNodes: TArray; + paramsElements: TArray; begin L := Node.AsLambdaExpression; @@ -566,12 +562,13 @@ begin FCurrentContext := TTypeContext.Create(FCurrentContext, L.Layout, upvalueTypes, nil); try paramsTuple := L.Parameters; - SetLength(newParams, paramsTuple.Count); - SetLength(paramTypes, paramsTuple.Count); + paramsElements := paramsTuple.Elements; + SetLength(newParams, Length(paramsElements)); + SetLength(paramTypes, Length(paramsElements)); - for i := 0 to paramsTuple.Count - 1 do + for i := 0 to High(paramsElements) do begin - paramIdent := paramsTuple.Items[i].AsIdentifier; + paramIdent := paramsElements[i].AsIdentifier; injectedType := paramIdent.AsTypedNode.StaticType; if injectedType.Kind = stUnknown then @@ -597,11 +594,7 @@ begin temp.Free; end; - SetLength(paramsAsNodes, Length(newParams)); - for i := 0 to High(newParams) do - paramsAsNodes[i] := newParams[i]; - - var paramList := TAst.Tuple(L.Parameters.Identity, paramsAsNodes); + var paramList := TAst.Tuple(L.Parameters.Identity, newParams); Result := TAst.LambdaExpr(Node.Identity, paramList, newBody, L.Layout, finalDescriptor, L.Upvalues, L.HasNestedLambdas, L.IsPure, methodType); @@ -617,17 +610,19 @@ var bestSig: IMethodSignature; match: Boolean; newArgs: ITupleNode; + argsElements: TArray; begin newCall := inherited VisitFunctionCall(Node).AsFunctionCall; var newCallee := newCall.Callee; newArgs := newCall.Arguments; + argsElements := newArgs.Elements; - SetLength(argTypes, newArgs.Count); + SetLength(argTypes, Length(argsElements)); hasUnknownArgs := False; - for i := 0 to newArgs.Count - 1 do + for i := 0 to High(argsElements) do begin - argTypes[i] := newArgs.Items[i].AsTypedNode.StaticType; + argTypes[i] := argsElements[i].AsTypedNode.StaticType; if argTypes[i].Kind = stUnknown then hasUnknownArgs := True; end; @@ -761,18 +756,20 @@ var valType: IStaticType; key: IKeyword; newFields: ITupleNode; + fieldsElements: TArray; begin R := Node.AsRecordLiteral; newFields := Visit(R.Fields).AsTuple; + fieldsElements := newFields.Elements; - var count := newFields.Count; + var count := Length(fieldsElements); SetLength(fieldTypes, count); SetLength(scalarFieldTypes, count); isScalar := True; for i := 0 to count - 1 do begin - var field := newFields.Items[i].AsRecordField; + var field := fieldsElements[i].AsRecordField; key := field.Key.Value; valType := field.Value.AsTypedNode.StaticType; @@ -865,17 +862,22 @@ var selectorsNode: ITupleNode; newSelectors: TList; streamType: IStaticType; + rawInputsElements: TArray; + rawTupleElements: TArray; + selectorsElements: TArray; + lambdaParamsElements: TArray; begin P := Node.AsPipe; rawInputs := P.Inputs; // This is the Tuple of Tuples [[id [sel]] ...] + rawInputsElements := rawInputs.Elements; newInputs := TList.Create; paramTypes := TList.Create; try // 1. Iterate over input definitions - for i := 0 to rawInputs.Count - 1 do + for i := 0 to High(rawInputsElements) do begin - inputPair := rawInputs.Items[i]; + inputPair := rawInputsElements[i]; // Validate Structure: [StreamSource, [Selectors]] if inputPair.Kind <> akTuple then @@ -886,7 +888,9 @@ begin end; rawTuple := inputPair.AsTuple; - if rawTuple.Count <> 2 then + rawTupleElements := rawTuple.Elements; + + if Length(rawTupleElements) <> 2 then begin if Assigned(FLog) then FLog.AddError('Pipe input vector must have exactly 2 elements.', inputPair); @@ -894,15 +898,15 @@ begin end; // Element 0: Stream Identifier - if rawTuple.Items[0].Kind <> akIdentifier then + if rawTupleElements[0].Kind <> akIdentifier then begin if Assigned(FLog) then - FLog.AddError('Pipe source must be an identifier.', rawTuple.Items[0]); + FLog.AddError('Pipe source must be an identifier.', rawTupleElements[0]); continue; end; // Resolve Stream Identifier (Type Binding) - streamNode := Accept(rawTuple.Items[0]).AsIdentifier; + streamNode := Accept(rawTupleElements[0]).AsIdentifier; streamType := streamNode.StaticType; // Validate Stream Type @@ -919,19 +923,21 @@ begin end; // Element 1: Selector Vector - if rawTuple.Items[1].Kind <> akTuple then + if rawTupleElements[1].Kind <> akTuple then begin if Assigned(FLog) then - FLog.AddError('Pipe selectors must be a vector of keywords.', rawTuple.Items[1]); + FLog.AddError('Pipe selectors must be a vector of keywords.', rawTupleElements[1]); continue; end; - selectorsNode := rawTuple.Items[1].AsTuple; + selectorsNode := rawTupleElements[1].AsTuple; + selectorsElements := selectorsNode.Elements; + newSelectors := TList.Create; try // Iterate Selectors - for k := 0 to selectorsNode.Count - 1 do + for k := 0 to High(selectorsElements) do begin - var selItem := selectorsNode.Items[k]; + var selItem := selectorsElements[k]; if selItem.Kind <> akKeyword then begin if Assigned(FLog) then @@ -974,23 +980,24 @@ begin // 2. Process Transformation Lambda lambda := P.Transformation; + lambdaParamsElements := lambda.Parameters.Elements; - if lambda.Parameters.Count <> paramTypes.Count then + if Length(lambdaParamsElements) <> paramTypes.Count then begin if Assigned(FLog) then FLog.AddError( Format( 'Pipe lambda expects %d parameters (one per selector), but got %d.', - [paramTypes.Count, lambda.Parameters.Count] + [paramTypes.Count, Length(lambdaParamsElements)] ), lambda ); end; - SetLength(newParams, lambda.Parameters.Count); - for k := 0 to lambda.Parameters.Count - 1 do + SetLength(newParams, Length(lambdaParamsElements)); + for k := 0 to High(lambdaParamsElements) do begin - var oldP := lambda.Parameters.Items[k].AsIdentifier; + var oldP := lambdaParamsElements[k].AsIdentifier; var typeToInject := if k < paramTypes.Count then paramTypes[k] else TTypes.Unknown; diff --git a/Src/AST/Myc.Ast.Dumper.pas b/Src/AST/Myc.Ast.Dumper.pas index b8754a7..bae304b 100644 --- a/Src/AST/Myc.Ast.Dumper.pas +++ b/Src/AST/Myc.Ast.Dumper.pas @@ -300,9 +300,11 @@ var argTypes: TArray; i: Integer; args: ITupleNode; + argsElements: TArray; begin C := Node.AsFunctionCall; args := C.Arguments; + argsElements := args.Elements; LogFmt( 'FunctionCall (IsTailCall: %s, StaticTarget: %s, IsTargetPure: %s)', @@ -317,11 +319,11 @@ begin if Assigned(C.StaticTarget) then begin Indent; - SetLength(argTypes, args.Count); - for i := 0 to args.Count - 1 do + SetLength(argTypes, Length(argsElements)); + for i := 0 to High(argsElements) do begin - if args.Items[i].IsTyped then - argTypes[i] := args.Items[i].AsTypedNode.StaticType.ToString + if argsElements[i].IsTyped then + argTypes[i] := argsElements[i].AsTypedNode.StaticType.ToString else argTypes[i] := 'Untyped'; end; @@ -332,7 +334,7 @@ begin Indent; Log('Callee:'); Visit(C.Callee); - LogFmt('Arguments (%d):', [args.Count]); + LogFmt('Arguments (%d):', [Length(argsElements)]); Visit(args); Unindent; end; @@ -469,7 +471,7 @@ var R: IRecordLiteralNode; begin R := Node.AsRecordLiteral; - LogFmt('RecordLiteral (%d fields)', [R.Fields.Count], Node); + LogFmt('RecordLiteral (%d fields)', [Length(R.Fields.Elements)], Node); Indent; Visit(R.Fields); Unindent; @@ -527,15 +529,17 @@ function TAstDumper.VisitTuple(const Node: IAstNode): TVoid; var T: ITupleNode; i: Integer; + elements: TArray; begin T := Node.AsTuple; - LogFmt('Tuple (%d elements)', [T.Count], Node); + elements := T.Elements; + LogFmt('Tuple (%d elements)', [Length(elements)], Node); Indent; - for i := 0 to T.Count - 1 do + for i := 0 to High(elements) do begin LogFmt('Item %d:', [i]); Indent; - Visit(T.Items[i]); + Visit(elements[i]); Unindent; end; Unindent; diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index 99559ee..c248f7b 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -179,10 +179,12 @@ function TEvaluatorVisitor.VisitTuple(const N: ITupleNode): TDataValue; var elements: TArray; i: Integer; + astElements: TArray; begin - SetLength(elements, N.Count); - for i := 0 to N.Count - 1 do - elements[i] := Visit(N.Items[i]); + astElements := N.Elements; + SetLength(elements, Length(astElements)); + for i := 0 to High(astElements) do + elements[i] := Visit(astElements[i]); // Uses the implicit operator: TArray -> TDataValue (vkTuple) Result := elements; @@ -194,6 +196,7 @@ var i: Integer; closureScope: IExecutionScope; visitorFactory: TEvaluatorFactory; + paramsElements: TArray; begin if Length(N.Upvalues) > 0 then begin @@ -210,7 +213,7 @@ begin visitorFactory := CreateVisitorFactory(); var descriptor := N.Descriptor; - var params := N.Parameters; + paramsElements := N.Parameters.Elements; var [unsafe] closure: TDataValue.TFunc; closure := @@ -220,17 +223,17 @@ begin bodyVisitor: IAstVisitor; k: Integer; begin - if (Length(ArgValues) <> params.Count) then + if (Length(ArgValues) <> Length(paramsElements)) then raise EEvaluatorException.Create('Arg mismatch'); lambdaScope := TScope.CreateScope(closureScope, descriptor, capturedCells); // Self-reference for recursion (Slot 0) lambdaScope.SetValues(TResolvedAddress.Create(akLocalOrParent, 0, 0), TDataValue(closure)); - for k := 0 to params.Count - 1 do + for k := 0 to High(paramsElements) do begin // Parameters are guaranteed to be Identifiers by the Binder - var paramNode := params.Items[k].AsIdentifier; + var paramNode := paramsElements[k].AsIdentifier; lambdaScope[paramNode.Address] := ArgValues[k]; end; @@ -245,12 +248,12 @@ var calleeValue: TDataValue; argValues: TArray; i: Integer; - argsTuple: ITupleNode; + argsElements: TArray; begin - argsTuple := N.Arguments; - SetLength(argValues, argsTuple.Count); - for i := 0 to argsTuple.Count - 1 do - argValues[i] := Visit(argsTuple.Items[i]); + argsElements := N.Arguments.Elements; + SetLength(argValues, Length(argsElements)); + for i := 0 to High(argsElements) do + argValues[i] := Visit(argsElements[i]); if Assigned(N.StaticTarget) then begin @@ -278,12 +281,12 @@ function TEvaluatorVisitor.VisitRecurNode(const N: IRecurNode): TDataValue; var argValues: TArray; i: Integer; - argsTuple: ITupleNode; + argsElements: TArray; begin - argsTuple := N.Arguments; - SetLength(argValues, argsTuple.Count); - for i := 0 to argsTuple.Count - 1 do - argValues[i] := Visit(argsTuple.Items[i]); + argsElements := N.Arguments.Elements; + SetLength(argValues, Length(argsElements)); + for i := 0 to High(argsElements) do + argValues[i] := Visit(argsElements[i]); // The "self" function is always at Slot 0 var callee := FScope[TResolvedAddress.Create(akLocalOrParent, 0, 0)]; @@ -292,13 +295,13 @@ end; function TEvaluatorVisitor.VisitBlockExpression(const N: IBlockExpressionNode): TDataValue; var - exprs: ITupleNode; + exprs: TArray; i: Integer; begin - exprs := N.Expressions; + exprs := N.Expressions.Elements; Result := TDataValue.Void; - for i := 0 to exprs.Count - 1 do - Result := Visit(exprs.Items[i]); + for i := 0 to High(exprs) do + Result := Visit(exprs[i]); end; function TEvaluatorVisitor.VisitIfExpression(const N: IIfExpressionNode): TDataValue; @@ -397,16 +400,16 @@ end; function TEvaluatorVisitor.VisitRecordLiteral(const N: IRecordLiteralNode): TDataValue; var i: Integer; - fieldsTuple: ITupleNode; + fieldsElements: TArray; begin + fieldsElements := N.Fields.Elements; if Assigned(N.ScalarDefinition) then begin var vals: TArray; - fieldsTuple := N.Fields; - SetLength(vals, fieldsTuple.Count); - for i := 0 to fieldsTuple.Count - 1 do + SetLength(vals, Length(fieldsElements)); + for i := 0 to High(fieldsElements) do begin - var field := fieldsTuple.Items[i].AsRecordField; + var field := fieldsElements[i].AsRecordField; vals[i] := Visit(field.Value).AsScalar.Value; end; Result := TScalarRecord.Create(N.ScalarDefinition, vals); @@ -414,11 +417,10 @@ begin else begin var fields: TArray>; - fieldsTuple := N.Fields; - SetLength(fields, fieldsTuple.Count); - for i := 0 to fieldsTuple.Count - 1 do + SetLength(fields, Length(fieldsElements)); + for i := 0 to High(fieldsElements) do begin - var field := fieldsTuple.Items[i].AsRecordField; + var field := fieldsElements[i].AsRecordField; fields[i] := TPair.Create(field.Key.Value, Visit(field.Value)); end; Result := TGenericRecord.Create(fields); @@ -460,24 +462,26 @@ var sourceSeries: IScalarRecordSeries; lambdaFunc: TDataValue.TFunc; - inputsTuple: ITupleNode; + inputsElements: TArray; entryTuple: ITupleNode; + entryElements: TArray; sourceId: IIdentifierNode; - selectorsTuple: ITupleNode; + selectorsElements: TArray; begin - inputsTuple := N.Inputs; + inputsElements := N.Inputs.Elements; - SetLength(sources, inputsTuple.Count); - SetLength(config, inputsTuple.Count); + SetLength(sources, Length(inputsElements)); + SetLength(config, Length(inputsElements)); - for i := 0 to inputsTuple.Count - 1 do + for i := 0 to High(inputsElements) do begin // Unpack: [Source, [Selectors]] // Note: Structure is guaranteed by TypeChecker - entryTuple := inputsTuple.Items[i].AsTuple; + entryTuple := inputsElements[i].AsTuple; + entryElements := entryTuple.Elements; - sourceId := entryTuple.Items[0].AsIdentifier; - selectorsTuple := entryTuple.Items[1].AsTuple; + sourceId := entryElements[0].AsIdentifier; + selectorsElements := entryElements[1].AsTuple.Elements; // Resolve Source Stream from Scope // The Binder has already linked sourceId to its variable address @@ -491,10 +495,10 @@ begin var srcDef := sourceSeries.Def; // Build Selectors Config - SetLength(config[i], selectorsTuple.Count); - for var k := 0 to selectorsTuple.Count - 1 do + SetLength(config[i], Length(selectorsElements)); + for var k := 0 to High(selectorsElements) do begin - var key := selectorsTuple.Items[k].AsKeyword.Value; + var key := selectorsElements[k].AsKeyword.Value; var idx := srcDef.IndexOf(key); // Index check already done in TypeChecker, but good for safety if idx < 0 then diff --git a/Src/AST/Myc.Ast.JSON.pas b/Src/AST/Myc.Ast.JSON.pas index 1465fe0..da1ce07 100644 --- a/Src/AST/Myc.Ast.JSON.pas +++ b/Src/AST/Myc.Ast.JSON.pas @@ -202,13 +202,18 @@ var T: ITupleNode; elemsArray: TJSONArray; i: Integer; + elements: TArray; begin T := Node.AsTuple; Result := TJSONObject.Create; Result.AddPair('NodeType', TJSONString.Create('Tuple')); elemsArray := TJSONArray.Create; - for i := 0 to T.Count - 1 do - elemsArray.Add(Visit(T.Items[i])); + + // Updated: Use Elements array directly + elements := T.Elements; + for i := 0 to High(elements) do + elemsArray.Add(Visit(elements[i])); + Result.AddPair('Elements', elemsArray); end; @@ -611,7 +616,8 @@ var begin identNodes := TList.Create; try - for node in JsonToNode(AObj.GetValue('Parameters')).AsTuple.ToArray do + // Updated: Use Elements instead of ToArray (or iterate if ToArray is missing) + for node in JsonToNode(AObj.GetValue('Parameters')).AsTuple.Elements do identNodes.Add(node.AsIdentifier); Result := TAst.LambdaExpr(identNodes.ToArray, JsonToNode(AObj.GetValue('Body'))); finally @@ -626,7 +632,8 @@ var begin identNodes := TList.Create; try - for node in JsonToNode(AObj.GetValue('Parameters')).AsTuple.ToArray do + // Updated: Use Elements + for node in JsonToNode(AObj.GetValue('Parameters')).AsTuple.Elements do identNodes.Add(node.AsIdentifier); Result := TAst.MacroDef( @@ -656,7 +663,8 @@ end; function TJsonAstConverter.JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode; begin - Result := TAst.FunctionCall(JsonToNode(AObj.GetValue('Callee')), JsonToNode(AObj.GetValue('Arguments')).AsTuple.ToArray); + // Updated: Use Elements + Result := TAst.FunctionCall(JsonToNode(AObj.GetValue('Callee')), JsonToNode(AObj.GetValue('Arguments')).AsTuple.Elements); end; function TJsonAstConverter.JsonToMacroExpansionNode(const AObj: TJSONObject): IMacroExpansionNode; @@ -667,17 +675,20 @@ begin callee := JsonToNode(AObj.GetValue('Callee')); expandedBody := JsonToNode(AObj.GetValue('ExpandedBody')); argsTuple := JsonToNode(AObj.GetValue('Arguments')).AsTuple; - Result := TAst.MacroExpansionNode(TAst.FunctionCall(callee, argsTuple.ToArray), expandedBody); + // Updated: Use Elements + Result := TAst.MacroExpansionNode(TAst.FunctionCall(callee, argsTuple.Elements), expandedBody); end; function TJsonAstConverter.JsonToRecurNode(const AObj: TJSONObject): IRecurNode; begin - Result := TAst.Recur(JsonToNode(AObj.GetValue('Arguments')).AsTuple.ToArray); + // Updated: Use Elements + Result := TAst.Recur(JsonToNode(AObj.GetValue('Arguments')).AsTuple.Elements); end; function TJsonAstConverter.JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode; begin - Result := TAst.Block(JsonToNode(AObj.GetValue('Expressions')).AsTuple.ToArray); + // Updated: Use Elements + Result := TAst.Block(JsonToNode(AObj.GetValue('Expressions')).AsTuple.Elements); end; function TJsonAstConverter.JsonToVarDeclNode(const AObj: TJSONObject): IVariableDeclarationNode; diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index cf49d28..9555ac8 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -263,12 +263,9 @@ type ITupleNode = interface(IAstTypedNode) {$region 'private'} - function GetCount: Integer; - function GetItems(Idx: Integer): IAstNode; + function GetElements: TArray; {$endregion} - function ToArray: TArray; - property Count: Integer read GetCount; - property Items[Idx: Integer]: IAstNode read GetItems; default; + property Elements: TArray read GetElements; end; IIfExpressionNode = interface(IAstTypedNode) @@ -582,11 +579,9 @@ type TTupleNode = class(TAstTypedNode, ITupleNode) private FElements: TArray; - function GetCount: Integer; - function GetItems(Idx: Integer): IAstNode; + function GetElements: TArray; protected function GetKind: TAstNodeKind; override; - function ToArray: TArray; public constructor Create(const AElements: TArray; const AIdentity: IAstIdentity; const AStaticType: IStaticType); function AsTuple: ITupleNode; override; @@ -1372,14 +1367,9 @@ begin Result := Self; end; -function TTupleNode.GetCount: Integer; +function TTupleNode.GetElements: TArray; begin - Result := Length(FElements); -end; - -function TTupleNode.GetItems(Idx: Integer): IAstNode; -begin - Result := FElements[Idx]; + Result := FElements; end; function TTupleNode.GetKind: TAstNodeKind; @@ -1387,11 +1377,6 @@ begin Result := akTuple; end; -function TTupleNode.ToArray: TArray; -begin - Result := FElements; -end; - { TCreateSeriesNode } constructor TCreateSeriesNode.Create(const AIdentity: IDefinitionIdentity; const AStaticType: IStaticType); diff --git a/Src/AST/Myc.Ast.Refactoring.Remove.pas b/Src/AST/Myc.Ast.Refactoring.Remove.pas index c8d2117..193f688 100644 --- a/Src/AST/Myc.Ast.Refactoring.Remove.pas +++ b/Src/AST/Myc.Ast.Refactoring.Remove.pas @@ -127,12 +127,14 @@ var i: Integer; item, transformed: IAstNode; list: TList; + elements: TArray; begin list := TList.Create; try - for i := 0 to Source.Count - 1 do + elements := Source.Elements; + for i := 0 to High(elements) do begin - item := Source.Items[i]; + item := elements[i]; transformed := Accept(item); if Assigned(transformed) then list.Add(transformed); @@ -335,14 +337,16 @@ var item, transformed: IAstNode; i: Integer; hasChanged: Boolean; + elements: TArray; begin T := Node.AsTuple; list := TList.Create; hasChanged := False; try - for i := 0 to T.Count - 1 do + elements := T.Elements; + for i := 0 to High(elements) do begin - item := T.Items[i]; + item := elements[i]; transformed := Accept(item); if transformed <> item then diff --git a/Src/AST/Myc.Ast.Script.Print.pas b/Src/AST/Myc.Ast.Script.Print.pas index 9def21a..d342fac 100644 --- a/Src/AST/Myc.Ast.Script.Print.pas +++ b/Src/AST/Myc.Ast.Script.Print.pas @@ -198,12 +198,13 @@ var i: Integer; begin T := N.AsTuple; + var elements := T.Elements; Append('['); - for i := 0 to T.Count - 1 do + for i := 0 to High(elements) do begin if i > 0 then Append(' '); - Visit(T.Items[i]); + Visit(elements[i]); end; Append(']'); end; @@ -315,26 +316,25 @@ function TPrettyPrintVisitor.VisitFunctionCall(const N: IAstNode): TVoid; var C: IFunctionCallNode; i: Integer; - args: ITupleNode; begin C := N.AsFunctionCall; - args := C.Arguments; + var args := C.Arguments.Elements; // Special case for 'quote' - if (C.Callee.Kind = akIdentifier) and (C.Callee.AsIdentifier.Name = 'quote') and (args.Count = 1) then + if (C.Callee.Kind = akIdentifier) and (C.Callee.AsIdentifier.Name = 'quote') and (Length(args) = 1) then begin Append(''''); - Visit(args.Items[0]); + Visit(args[0]); exit; end; Append('('); Visit(C.Callee); // Manually iterate arguments tuple to avoid printing [...] inside () - for i := 0 to args.Count - 1 do + for i := 0 to High(args) do begin Append(' '); - Visit(args.Items[i]); + Visit(args[i]); end; Append(')'); end; @@ -348,16 +348,15 @@ function TPrettyPrintVisitor.VisitRecurNode(const N: IAstNode): TVoid; var R: IRecurNode; i: Integer; - args: ITupleNode; begin R := N.AsRecur; - args := R.Arguments; + var args := R.Arguments.Elements; Append('(recur'); // Manually iterate arguments - for i := 0 to args.Count - 1 do + for i := 0 to High(args) do begin Append(' '); - Visit(args.Items[i]); + Visit(args[i]); end; Append(')'); end; @@ -365,18 +364,17 @@ end; function TPrettyPrintVisitor.VisitBlockExpression(const N: IAstNode): TVoid; var B: IBlockExpressionNode; - exprs: ITupleNode; i: Integer; begin B := N.AsBlockExpression; - exprs := B.Expressions; + var exprs := B.Expressions.Elements; Append('(do'); Indent; // Manually iterate expressions tuple for newlines - for i := 0 to exprs.Count - 1 do + for i := 0 to High(exprs) do begin NewLine; - Visit(exprs.Items[i]); + Visit(exprs[i]); end; Unindent; NewLine; @@ -437,22 +435,21 @@ end; function TPrettyPrintVisitor.VisitRecordLiteral(const N: IAstNode): TVoid; var R: IRecordLiteralNode; - fields: ITupleNode; i: Integer; begin R := N.AsRecordLiteral; - fields := R.Fields; - if fields.Count = 0 then + var fields := R.Fields.Elements; + if Length(fields) = 0 then begin Append('{}'); exit; end; Append('{'); Indent; - for i := 0 to fields.Count - 1 do + for i := 0 to High(fields) do begin NewLine; - Visit(fields.Items[i]); + Visit(fields[i]); end; Unindent; NewLine; diff --git a/Src/AST/Myc.Ast.Script.pas b/Src/AST/Myc.Ast.Script.pas index a8eed4a..ca4a692 100644 --- a/Src/AST/Myc.Ast.Script.pas +++ b/Src/AST/Myc.Ast.Script.pas @@ -453,12 +453,14 @@ begin ErrorFmt('%s expects a vector [...]', [Context]); tuple := Node.AsTuple; - SetLength(Result, tuple.Count); - for i := 0 to tuple.Count - 1 do + // Updated to use Elements property + var elements := tuple.Elements; + SetLength(Result, Length(elements)); + for i := 0 to High(elements) do begin - if tuple.Items[i].Kind <> akIdentifier then + if elements[i].Kind <> akIdentifier then ErrorFmt('%s vector must contain only identifiers.', [Context]); - Result[i] := tuple.Items[i].AsIdentifier; + Result[i] := elements[i].AsIdentifier; end; end; diff --git a/Src/AST/Myc.Ast.Visitor.pas b/Src/AST/Myc.Ast.Visitor.pas index 8a8e8d0..4a456c2 100644 --- a/Src/AST/Myc.Ast.Visitor.pas +++ b/Src/AST/Myc.Ast.Visitor.pas @@ -539,11 +539,14 @@ var begin T := N.AsTuple; hasChanged := False; - SetLength(newElements, T.Count); - for i := 0 to T.Count - 1 do + // Updated: Use Elements directly + var oldElements := T.Elements; + SetLength(newElements, Length(oldElements)); + + for i := 0 to High(oldElements) do begin - item := T.Items[i]; + item := oldElements[i]; newItem := Visit(item); newElements[i] := newItem; if item <> newItem then @@ -553,7 +556,7 @@ begin if not hasChanged then Result := N else - Result := TAst.Tuple(N.Identity, newElements, N.AsTypedNode.StaticType); + Result := TAst.Tuple(N.Identity, newElements, T.AsTypedNode.StaticType); end; { TAstVisitor (Walker) } @@ -731,12 +734,11 @@ begin end; function TAstVisitor.WalkTuple(const N: IAstNode): TVoid; -var - i: Integer; begin + // Updated: Iterate directly via Elements var t := N.AsTuple; - for i := 0 to t.Count - 1 do - Visit(t.Items[i]); + for var item in t.Elements do + Visit(item); end; end. diff --git a/Src/AST/Myc.Fmx.AstEditor.Handlers.Control.pas b/Src/AST/Myc.Fmx.AstEditor.Handlers.Control.pas index a8f9a98..dd94877 100644 --- a/Src/AST/Myc.Fmx.AstEditor.Handlers.Control.pas +++ b/Src/AST/Myc.Fmx.AstEditor.Handlers.Control.pas @@ -335,6 +335,7 @@ var i: Integer; argView: TAstViewNode; opLabel: TTextNode; + argsElements: TArray; begin visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth + 1); @@ -342,19 +343,23 @@ begin OwnerNode.Orientation := loHorizontal; OwnerNode.Alignment := laCenter; - if FNode.Arguments.Count = 1 then + // Optimization: Access Elements directly + argsElements := FNode.Arguments.Elements; + var argCount := Length(argsElements); + + if argCount = 1 then begin opLabel := OwnerNode.AddLabel(OwnerNode, FCalleeName); opLabel.FontSettings.Font.Style := [TFontStyle.fsBold]; opLabel.Color := TAlphaColors.Darkorange; opLabel.Margins := TMarginRect.Create(2, 0, 2, 0); - argView := visu.CallAccept(FNode.Arguments.Items[0]); + argView := visu.CallAccept(argsElements[0]); FArgumentNodes.Add(argView); end else begin - for i := 0 to FNode.Arguments.Count - 1 do + for i := 0 to argCount - 1 do begin if i > 0 then begin @@ -365,12 +370,12 @@ begin opLabel.Margins := TMarginRect.Create(0, 0, 0, 0); end; - argView := visu.CallAccept(FNode.Arguments.Items[i]); + argView := visu.CallAccept(argsElements[i]); FArgumentNodes.Add(argView); end; end; - if FNode.Arguments.Count = 0 then + if argCount = 0 then begin opLabel := OwnerNode.AddLabel(OwnerNode, FCalleeName); opLabel.FontSettings.Font.Style := [TFontStyle.fsBold]; diff --git a/Src/AST/Myc.Fmx.AstEditor.Handlers.Lists.pas b/Src/AST/Myc.Fmx.AstEditor.Handlers.Lists.pas index 15fa35d..e74a64f 100644 --- a/Src/AST/Myc.Fmx.AstEditor.Handlers.Lists.pas +++ b/Src/AST/Myc.Fmx.AstEditor.Handlers.Lists.pas @@ -47,6 +47,7 @@ var i: Integer; childView: TAstViewNode; lbl: TTextNode; + elements: TArray; begin // Tuples act as lists, using horizontal flow (e.g. [1 2 3] or [[src [:sel]]]) OwnerNode.Frameless := True; @@ -60,18 +61,21 @@ begin visu := OwnerNode.Visualizer.Clone(OwnerNode, OwnerNode.Visualizer.ExprDepth); - for i := 0 to FNode.Count - 1 do + // Optimization: Access Elements array directly + elements := FNode.Elements; + + for i := 0 to High(elements) do begin // Standard spacing between elements if i > 0 then OwnerNode.AddLabel(OwnerNode, ' '); - childView := visu.CallAccept(FNode.Items[i]); + childView := visu.CallAccept(elements[i]); FChildNodes.Add(childView); end; // Visual hint for empty tuples - if FNode.Count = 0 then + if Length(elements) = 0 then begin OwnerNode.AddLabel(OwnerNode, ' '); // spacer end; diff --git a/Src/AST/Myc.Fmx.AstEditor.Handlers.pas b/Src/AST/Myc.Fmx.AstEditor.Handlers.pas index 3198253..cc528a0 100644 --- a/Src/AST/Myc.Fmx.AstEditor.Handlers.pas +++ b/Src/AST/Myc.Fmx.AstEditor.Handlers.pas @@ -13,14 +13,15 @@ uses type // --- Abstract List Base --- - // Implements IReorderable to support drag & drop reordering within the virtual tree - TNodeListHandler = class(TBaseNodeHandler>, IReorderable) + // Implements IReorderable to support drag & drop reordering within the virtual tree. + // Now operates on the untyped ITupleNode. + TNodeListHandler = class(TBaseNodeHandler, IReorderable) protected FChildNodes: TList; function GetOrientation: TLayoutOrientation; virtual; abstract; function GetAlignment: TLayoutAlignment; virtual; public - constructor Create(const ANode: INodeList); + constructor Create(const ANode: ITupleNode); destructor Destroy; override; procedure BuildUI(OwnerNode: TAstViewNode); override; function ReconstructAst(OwnerNode: TAstViewNode): IAstNode; override; abstract; @@ -32,32 +33,33 @@ type implementation -{ TNodeListHandler } +{ TNodeListHandler } -constructor TNodeListHandler.Create(const ANode: INodeList); +constructor TNodeListHandler.Create(const ANode: ITupleNode); begin inherited Create(ANode); FChildNodes := TList.Create; end; -destructor TNodeListHandler.Destroy; +destructor TNodeListHandler.Destroy; begin FChildNodes.Free; inherited; end; -function TNodeListHandler.GetAlignment: TLayoutAlignment; +function TNodeListHandler.GetAlignment: TLayoutAlignment; begin Result := TLayoutAlignment.laCenter; end; -procedure TNodeListHandler.BuildUI(OwnerNode: TAstViewNode); +procedure TNodeListHandler.BuildUI(OwnerNode: TAstViewNode); var visu: IAstVisualizer; i: Integer; childView: TAstViewNode; openTok, closeTok, sep: string; listId: IListIdentity; + elements: TArray; begin if (FNode.Identity <> nil) and (FNode.Identity.Kind = ikList) then begin @@ -83,19 +85,22 @@ begin if openTok <> '' then OwnerNode.AddLabel(OwnerNode, openTok); - for i := 0 to FNode.Count - 1 do + // Optimization: Access Elements array directly from ITupleNode + elements := FNode.Elements; + + for i := 0 to High(elements) do begin - childView := visu.CallAccept(FNode[i]); + childView := visu.CallAccept(elements[i]); FChildNodes.Add(childView); - if (i < FNode.Count - 1) and (sep <> '') and (sep <> ' ') then + if (i < High(elements)) and (sep <> '') and (sep <> ' ') then OwnerNode.AddLabel(OwnerNode, sep); end; if closeTok <> '' then OwnerNode.AddLabel(OwnerNode, closeTok); - if FNode.Count = 0 then + if Length(elements) = 0 then begin if (openTok = '') and (closeTok = '') then begin @@ -107,7 +112,7 @@ end; // IReorderable Implementation -function TNodeListHandler.CanDrag(Child: TVisualNode): Boolean; +function TNodeListHandler.CanDrag(Child: TVisualNode): Boolean; begin // Only allow dragging if the child is one of our managed ViewNodes if Child is TAstViewNode then @@ -116,7 +121,7 @@ begin Result := False; end; -procedure TNodeListHandler.MoveChild(SourceIndex, TargetIndex: Integer); +procedure TNodeListHandler.MoveChild(SourceIndex, TargetIndex: Integer); var item: TAstViewNode; begin diff --git a/Test/AST/Test.Myc.Ast.Compiler.Macros.pas b/Test/AST/Test.Myc.Ast.Compiler.Macros.pas index 1f2b6cc..736ea66 100644 --- a/Test/AST/Test.Myc.Ast.Compiler.Macros.pas +++ b/Test/AST/Test.Myc.Ast.Compiler.Macros.pas @@ -119,7 +119,8 @@ begin if Node.Kind = akBlockExpression then begin block := Node.AsBlockExpression; - for child in block.Expressions.ToArray do + // Updated: Use Elements instead of ToArray + for child in block.Expressions.Elements do begin Result := FindFirstVarName(child); if Result <> '' then diff --git a/Test/AST/Test.Myc.Ast.Script.pas b/Test/AST/Test.Myc.Ast.Script.pas index 3bbef9d..ac72b5b 100644 --- a/Test/AST/Test.Myc.Ast.Script.pas +++ b/Test/AST/Test.Myc.Ast.Script.pas @@ -23,6 +23,7 @@ type [TestCase('Integer', '42,42')] [TestCase('Negative', '-10,-10')] [TestCase('Zero', '0,0')] + [TestCase('Nop', '...')] procedure Parser_Number_Integer(const Source: string; Expected: Int64); [Test] @@ -129,6 +130,12 @@ var node: IAstNode; begin node := Parse(Source); + if Source = '...' then + begin + Assert.AreEqual(TAstNodeKind.akNop, node.Kind); + Exit; + end; + Assert.AreEqual(TAstNodeKind.akConstant, node.Kind); Assert.AreEqual(TScalar.TKind.Ordinal, node.AsConstant.Value.AsScalar.Kind); Assert.AreEqual(Expected, node.AsConstant.Value.AsScalar.Value.AsInt64); @@ -209,11 +216,11 @@ begin call := node.AsFunctionCall; Assert.AreEqual('add', call.Callee.AsIdentifier.Name); - // Use Count instead of Length - Assert.AreEqual(2, call.Arguments.Count); + // Use Length(Elements) instead of Count + Assert.AreEqual(2, Length(call.Arguments.Elements)); - Assert.AreEqual(1, call.Arguments[0].AsConstant.Value.AsScalar.Value.AsInt64); - Assert.AreEqual(2, call.Arguments[1].AsConstant.Value.AsScalar.Value.AsInt64); + Assert.AreEqual(1, call.Arguments.Elements[0].AsConstant.Value.AsScalar.Value.AsInt64); + Assert.AreEqual(2, call.Arguments.Elements[1].AsConstant.Value.AsScalar.Value.AsInt64); end; procedure TTestMycAstScript.Parser_RecordLiteral; @@ -226,14 +233,15 @@ begin Assert.AreEqual(TAstNodeKind.akRecordLiteral, node.Kind); rec := node.AsRecordLiteral; - Assert.AreEqual(2, rec.Fields.Count); + // Use Length(Elements) instead of Count + Assert.AreEqual(2, Length(rec.Fields.Elements)); // FIX: Cast elements to RecordField to access Key/Value - Assert.AreEqual('a', rec.Fields[0].AsRecordField.Key.Value.Name); - Assert.AreEqual(1, rec.Fields[0].AsRecordField.Value.AsConstant.Value.AsScalar.Value.AsInt64); + Assert.AreEqual('a', rec.Fields.Elements[0].AsRecordField.Key.Value.Name); + Assert.AreEqual(1, rec.Fields.Elements[0].AsRecordField.Value.AsConstant.Value.AsScalar.Value.AsInt64); - Assert.AreEqual('b', rec.Fields[1].AsRecordField.Key.Value.Name); - Assert.AreEqual(2, rec.Fields[1].AsRecordField.Value.AsConstant.Value.AsScalar.Value.AsInt64); + Assert.AreEqual('b', rec.Fields.Elements[1].AsRecordField.Key.Value.Name); + Assert.AreEqual(2, rec.Fields.Elements[1].AsRecordField.Value.AsConstant.Value.AsScalar.Value.AsInt64); end; procedure TTestMycAstScript.Parser_RecordLiteral_Empty; @@ -242,7 +250,8 @@ var begin node := Parse('{}'); Assert.AreEqual(TAstNodeKind.akRecordLiteral, node.Kind); - Assert.AreEqual(0, node.AsRecordLiteral.Fields.Count); + // Use Length(Elements) instead of Count + Assert.AreEqual(0, Length(node.AsRecordLiteral.Fields.Elements)); end; // --- Special Forms --- @@ -271,10 +280,11 @@ begin Assert.AreEqual(TAstNodeKind.akLambdaExpression, node.Kind); lam := node.AsLambdaExpression; - Assert.AreEqual(2, lam.Parameters.Count); + // Use Length(Elements) instead of Count + Assert.AreEqual(2, Length(lam.Parameters.Elements)); // FIX: Cast tuple items to Identifier to access Name - Assert.AreEqual('a', lam.Parameters[0].AsIdentifier.Name); - Assert.AreEqual('b', lam.Parameters[1].AsIdentifier.Name); + Assert.AreEqual('a', lam.Parameters.Elements[0].AsIdentifier.Name); + Assert.AreEqual('b', lam.Parameters.Elements[1].AsIdentifier.Name); Assert.AreEqual(TAstNodeKind.akFunctionCall, lam.Body.Kind); end; @@ -322,8 +332,9 @@ begin call := node.AsFunctionCall; Assert.AreEqual('quote', call.Callee.AsIdentifier.Name); - Assert.AreEqual(1, call.Arguments.Count); - Assert.AreEqual('foo', call.Arguments[0].AsIdentifier.Name); + // Use Length(Elements) instead of Count + Assert.AreEqual(1, Length(call.Arguments.Elements)); + Assert.AreEqual('foo', call.Arguments.Elements[0].AsIdentifier.Name); end; procedure TTestMycAstScript.Parser_Quasiquote_CreatesNode; @@ -360,27 +371,27 @@ end; procedure TTestMycAstScript.Parser_Error_UnbalancedParens_MissingRight; begin - Assert.WillRaise(procedure begin Parse('(a b'); end); + Assert.WillRaise(procedure begin Parse('(a b'); end, EParserException); end; procedure TTestMycAstScript.Parser_Error_UnbalancedParens_ExtraRight; begin - Assert.WillRaise(procedure begin Parse('a )'); end); + Assert.WillRaise(procedure begin Parse('a )'); end, EParserException); end; procedure TTestMycAstScript.Parser_Error_UnterminatedString; begin - Assert.WillRaise(procedure begin Parse('"hello'); end); + Assert.WillRaise(procedure begin Parse('"hello'); end, EParserException); end; procedure TTestMycAstScript.Parser_Error_EmptyList; begin - Assert.WillRaise(procedure begin Parse('()'); end); + Assert.WillRaise(procedure begin Parse('()'); end, EParserException); end; procedure TTestMycAstScript.Parser_Error_Record_MissingValue; begin - Assert.WillRaise(procedure begin Parse('{:a 1 :b}'); end); + Assert.WillRaise(procedure begin Parse('{:a 1 :b}'); end, EParserException); end; end. diff --git a/Test/Test.Myc.Ast.Compiler.Binder.pas b/Test/Test.Myc.Ast.Compiler.Binder.pas index b195897..a629534 100644 --- a/Test/Test.Myc.Ast.Compiler.Binder.pas +++ b/Test/Test.Myc.Ast.Compiler.Binder.pas @@ -104,8 +104,8 @@ end; function TTestAstBinder.Unwrap(const Node: IAstNode): IAstNode; begin - if (Node.Kind = akBlockExpression) and (Node.AsBlockExpression.Expressions.Count = 1) then - Result := Node.AsBlockExpression.Expressions[0] + if (Node.Kind = akBlockExpression) and (Length(Node.AsBlockExpression.Expressions.Elements) = 1) then + Result := Node.AsBlockExpression.Expressions.Elements[0] else Result := Node; end; @@ -132,7 +132,7 @@ begin block := bound.AsBlockExpression; // Check definition of 'b' - decl := block.Expressions[1].AsVariableDeclaration; + decl := block.Expressions.Elements[1].AsVariableDeclaration; initIdent := decl.Initializer.AsIdentifier; // 'a' is Slot 0. 'b' is Slot 1. @@ -177,7 +177,7 @@ begin Assert.IsFalse(log.HasErrors); block := bound.AsBlockExpression; - ident := block.Expressions[1].AsIdentifier; + ident := block.Expressions.Elements[1].AsIdentifier; Assert.AreEqual(akLocalOrParent, ident.Address.Kind); Assert.AreEqual(0, ident.Address.ScopeDepth); Assert.AreEqual(0, ident.Address.SlotIndex); @@ -192,14 +192,15 @@ var innerUsage: IIdentifierNode; log: ICompilerLog; begin - root := - TAst.Block([TAst.VarDecl(TAst.Identifier('x'), TAst.Constant(1)), TAst.LambdaExpr([TAst.Identifier('x')], TAst.Identifier('x'))]); + // Convert array to tuple for lambda params + var paramTuple := TAst.Tuple([TAst.Identifier('x')]); + root := TAst.Block([TAst.VarDecl(TAst.Identifier('x'), TAst.Constant(1)), TAst.LambdaExpr(nil, paramTuple, TAst.Identifier('x'))]); bound := Bind(root, layout, log); Assert.IsFalse(log.HasErrors); block := bound.AsBlockExpression; - innerLambda := block.Expressions[1].AsLambdaExpression; + innerLambda := block.Expressions.Elements[1].AsLambdaExpression; innerUsage := innerLambda.Body.AsIdentifier; Assert.AreEqual(1, innerUsage.Address.SlotIndex); // Parameter x (Slot 1 because is Slot 0) end; @@ -211,7 +212,8 @@ var log: ICompilerLog; begin // (do (fn [] (def a 1)) a) -> 'a' is inside lambda, not visible outside - root := TAst.Block([TAst.LambdaExpr([], TAst.VarDecl(TAst.Identifier('a'), TAst.Constant(1))), TAst.Identifier('a')]); + var emptyParams := TAst.Tuple([]); + root := TAst.Block([TAst.LambdaExpr(nil, emptyParams, TAst.VarDecl(TAst.Identifier('a'), TAst.Constant(1))), TAst.Identifier('a')]); Bind(root, layout, log); @@ -230,14 +232,16 @@ var lambda: ILambdaExpressionNode; log: ICompilerLog; begin - root := TAst.LambdaExpr([TAst.Identifier('p1')], TAst.Nop); + // Use Tuple factory for params + var params := TAst.Tuple([TAst.Identifier('p1')]); + root := TAst.LambdaExpr(nil, params, TAst.Nop); bound := Unwrap(Bind(root, layout, log)); Assert.IsFalse(log.HasErrors); lambda := bound.AsLambdaExpression; // FIX: Cast element to Identifier to access Address - Assert.AreEqual(1, lambda.Parameters[0].AsIdentifier.Address.SlotIndex); // Slot 0 is reserved for + Assert.AreEqual(1, lambda.Parameters.Elements[0].AsIdentifier.Address.SlotIndex); // Slot 0 is reserved for end; procedure TTestAstBinder.Test_MultipleParameters_AreBoundCorrectly; @@ -247,15 +251,17 @@ var lambda: ILambdaExpressionNode; log: ICompilerLog; begin - root := TAst.LambdaExpr([TAst.Identifier('a'), TAst.Identifier('b')], TAst.Nop); + // Use Tuple factory for params + var params := TAst.Tuple([TAst.Identifier('a'), TAst.Identifier('b')]); + root := TAst.LambdaExpr(nil, params, TAst.Nop); bound := Unwrap(Bind(root, layout, log)); Assert.IsFalse(log.HasErrors); lambda := bound.AsLambdaExpression; // FIX: Cast elements to Identifier - Assert.AreEqual(1, lambda.Parameters[0].AsIdentifier.Address.SlotIndex); - Assert.AreEqual(2, lambda.Parameters[1].AsIdentifier.Address.SlotIndex); + Assert.AreEqual(1, lambda.Parameters.Elements[0].AsIdentifier.Address.SlotIndex); + Assert.AreEqual(2, lambda.Parameters.Elements[1].AsIdentifier.Address.SlotIndex); end; // ------------------------------------------------------------------------------------------------ @@ -271,13 +277,14 @@ var bodyIdent: IIdentifierNode; log: ICompilerLog; begin - root := TAst.Block([TAst.VarDecl(TAst.Identifier('x'), TAst.Constant(99)), TAst.LambdaExpr([], TAst.Identifier('x'))]); + var emptyParams := TAst.Tuple([]); + root := TAst.Block([TAst.VarDecl(TAst.Identifier('x'), TAst.Constant(99)), TAst.LambdaExpr(nil, emptyParams, TAst.Identifier('x'))]); bound := Bind(root, layout, log); Assert.IsFalse(log.HasErrors); block := bound.AsBlockExpression; - lambda := block.Expressions[1].AsLambdaExpression; + lambda := block.Expressions.Elements[1].AsLambdaExpression; bodyIdent := lambda.Body.AsIdentifier; // Inside the lambda, 'x' is accessed via an Upvalue @@ -293,16 +300,20 @@ var midLambda, innerLambda: ILambdaExpressionNode; log: ICompilerLog; begin + var emptyParams := TAst.Tuple([]); root := TAst.Block( - [TAst.VarDecl(TAst.Identifier('top'), TAst.Constant(100)), TAst.LambdaExpr([], TAst.LambdaExpr([], TAst.Identifier('top')))] + [ + TAst.VarDecl(TAst.Identifier('top'), TAst.Constant(100)), + TAst.LambdaExpr(nil, emptyParams, TAst.LambdaExpr(nil, emptyParams, TAst.Identifier('top'))) + ] ); bound := Bind(root, layout, log); Assert.IsFalse(log.HasErrors); outerBlock := bound.AsBlockExpression; - midLambda := outerBlock.Expressions[1].AsLambdaExpression; + midLambda := outerBlock.Expressions.Elements[1].AsLambdaExpression; innerLambda := midLambda.Body.AsLambdaExpression; // Inner lambda accesses 'top' via upvalue @@ -316,7 +327,8 @@ var lambda: ILambdaExpressionNode; log: ICompilerLog; begin - root := TAst.LambdaExpr([], TAst.Identifier('')); + var emptyParams := TAst.Tuple([]); + root := TAst.LambdaExpr(nil, emptyParams, TAst.Identifier('')); bound := Unwrap(Bind(root, layout, log)); Assert.IsFalse(log.HasErrors);