From 51265ce9450797ca9aebae5663511f3febbb8b98 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Tue, 7 Oct 2025 10:42:53 +0200 Subject: [PATCH] Fixed weak self method pointer bug Better formatting of function calls in pretty printer --- Src/AST/Myc.Ast.Analyzer.pas | 2 +- Src/AST/Myc.Ast.Evaluator.pas | 67 +++++++++++++++++------------------ Src/AST/Myc.Ast.Script.pas | 17 +++------ 3 files changed, 38 insertions(+), 48 deletions(-) diff --git a/Src/AST/Myc.Ast.Analyzer.pas b/Src/AST/Myc.Ast.Analyzer.pas index 9357871..532ad69 100644 --- a/Src/AST/Myc.Ast.Analyzer.pas +++ b/Src/AST/Myc.Ast.Analyzer.pas @@ -45,10 +45,10 @@ end; destructor TUpvalueAnalyzer.Destroy; begin - FBoxedDeclarations.Free; for var dict in FDeclarationMap.Values do dict.Free; FDeclarationMap.Free; + FBoxedDeclarations.Free; inherited Destroy; end; diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index 5541f1e..3b320c7 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -193,46 +193,43 @@ begin var scopeDescriptor := boundNode.ScopeDescriptor; var params := boundNode.Parameters; - - // [weak] prevents a reference cycle since the closure captures itself for 'recur'. - var [weak] closure: TDataValue.TFunc; var cNode: ILambdaExpressionNode := Node; + // [unsafe] prevents a reference cycle since the closure captures itself for 'recur'. + var [unsafe] closure: TDataValue.TFunc; closure := - TDataValue.TFunc( - function(const ArgValues: TArray): TDataValue - var - lambdaScope: IExecutionScope; - bodyVisitor: IAstVisitor; - i: Integer; - adr: TResolvedAddress; + function(const ArgValues: TArray): TDataValue + var + lambdaScope: IExecutionScope; + bodyVisitor: IAstVisitor; + i: Integer; + adr: TResolvedAddress; + begin + if (Length(ArgValues) <> Length(params)) then + raise EArgumentException.CreateFmt('Argument count mismatch: expected %d, got %d', [Length(params), Length(ArgValues)]); + + // Create the new execution scope for this function call. + lambdaScope := TScope.CreateScope(closureScope, scopeDescriptor, capturedCells); + + adr.Kind := akLocalOrParent; + adr.ScopeDepth := 0; + + // Capture the closure itself in slot 0 for 'recur' to find it. + adr.SlotIndex := 0; + lambdaScope[adr] := closure; + + // Populate the scope with the actual parameters passed to the function. + for i := 0 to High(ArgValues) do begin - if (Length(ArgValues) <> Length(params)) then - raise EArgumentException.CreateFmt('Argument count mismatch: expected %d, got %d', [Length(params), Length(ArgValues)]); + // Parameters in a bound lambda must be bound identifiers. + adr.SlotIndex := (params[i] as TBoundIdentifierNode).Address.SlotIndex; + lambdaScope[adr] := ArgValues[i]; + end; - // Create the new execution scope for this function call. - lambdaScope := TScope.CreateScope(closureScope, scopeDescriptor, capturedCells); - - adr.Kind := akLocalOrParent; - adr.ScopeDepth := 0; - - // Capture the closure itself in slot 0 for 'recur' to find it. - adr.SlotIndex := 0; - lambdaScope[adr] := TDataValue(closure); - - // Populate the scope with the actual parameters passed to the function. - for i := 0 to High(ArgValues) do - begin - // Parameters in a bound lambda must be bound identifiers. - adr.SlotIndex := (params[i] as TBoundIdentifierNode).Address.SlotIndex; - lambdaScope[adr] := ArgValues[i]; - end; - - // Create a visitor with the new scope and execute the lambda's body. - bodyVisitor := visitorFactory(lambdaScope); - Result := cNode.Body.Accept(bodyVisitor); - end - ); + // Create a visitor with the new scope and execute the lambda's body. + bodyVisitor := visitorFactory(lambdaScope); + Result := cNode.Body.Accept(bodyVisitor); + end; // The result of visiting a lambda node is the callable closure itself. Result := closure; diff --git a/Src/AST/Myc.Ast.Script.pas b/Src/AST/Myc.Ast.Script.pas index 4471f3a..5f9acc9 100644 --- a/Src/AST/Myc.Ast.Script.pas +++ b/Src/AST/Myc.Ast.Script.pas @@ -398,13 +398,11 @@ begin begin if (Length(tailNodes) <> 3) or (tailTokens[0].Kind <> tkIdentifier) then raise Exception.Create('Syntax Error: ''defmacro'' requires a name, a parameter list, and a body.'); - if elements[2].Params = nil then - raise Exception.Create('Syntax Error: Expected a parameter list [...] after macro name.'); var macroName := IIdentifierNode(tailNodes[0]); var macroParams := elements[2].Params; - if tailTokens[2].Kind <> tkQuote then + if tailTokens[2].Kind <> tkBacktick then raise Exception.Create('Syntax Error: Expected a quasiquote as macro body.'); var macroBody := IQuasiQuoteNode(tailNodes[2]); @@ -421,8 +419,6 @@ begin begin if Length(tailNodes) <> 2 then raise Exception.Create('Syntax Error: ''fn'' requires a parameter list and a body.'); - if elements[1].Params = nil then - raise Exception.Create('Syntax Error: Expected a parameter list [...] after ''fn''.'); Result := TAst.LambdaExpr(elements[1].Params, tailNodes[1]); end else if SameText(head.Token.Text, 'do') then @@ -453,6 +449,7 @@ var expr: TExpr; begin // Handle reader macros. + Result.Token := FCurrentToken; case FCurrentToken.Kind of tkBacktick: begin @@ -735,14 +732,11 @@ begin Indent; for arg in Node.Arguments do begin - NewLine; + Append(' '); arg.Accept(Self); end; Unindent; - if Length(Node.Arguments) > 0 then - NewLine; - Append(')'); Result := TDataValue.Void; end; @@ -762,12 +756,11 @@ begin Indent; for arg in Node.Arguments do begin - NewLine; + Append(' '); arg.Accept(Self); end; Unindent; - if Length(Node.Arguments) > 0 then - NewLine; + Append(')'); Result := TDataValue.Void; end;