AST list refactoring

This commit is contained in:
Michael Schimmel
2026-01-04 22:41:44 +01:00
parent 2a7e6626ad
commit 242ec9a56e
27 changed files with 815 additions and 1076 deletions
+53 -25
View File
@@ -32,7 +32,7 @@ type
class var
FGensymCounter: Int64;
function TransformAndSpliceNodes(const ANodes: INodeList<IAstNode>): TArray<IAstNode>;
function TransformAndSpliceNodes(const ANodes: ITupleNode): TArray<IAstNode>;
function Gensym(const ABaseName: string): string;
// Custom Logic Handlers (IAstNode signature)
@@ -186,17 +186,19 @@ begin
end;
end;
function TExpansionVisitor.TransformAndSpliceNodes(const ANodes: INodeList<IAstNode>): TArray<IAstNode>;
function TExpansionVisitor.TransformAndSpliceNodes(const ANodes: ITupleNode): TArray<IAstNode>;
var
newList: TList<IAstNode>;
nodeToSplice: IAstNode;
transformedNode: IAstNode;
node: IAstNode;
i: Integer;
begin
newList := TList<IAstNode>.Create;
try
for node in ANodes do
for i := 0 to ANodes.Count - 1 do
begin
node := ANodes.Items[i];
if node.Kind = akUnquoteSplicing then
begin
var spliceExpr := node.AsUnquoteSplicing.Expression;
@@ -213,17 +215,24 @@ begin
if (not evaluatedSpliceValue.IsVoid) and (evaluatedSpliceValue.Kind = vkInterface) then
begin
nodeToSplice := evaluatedSpliceValue.AsIntf<IAstNode>;
if nodeToSplice.Kind = akExpressionList then
for var subItem in nodeToSplice.AsExpressionList do
newList.Add(subItem)
else if nodeToSplice.Kind = akArgumentList then
for var subItem in nodeToSplice.AsArgumentList do
newList.Add(subItem)
// 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]);
end
else if nodeToSplice.Kind = akBlockExpression then
for var subItem in nodeToSplice.AsBlockExpression.Expressions do
newList.Add(subItem)
begin
var blkTuple := nodeToSplice.AsBlockExpression.Expressions;
for var k := 0 to blkTuple.Count - 1 do
newList.Add(blkTuple.Items[k]);
end
else
begin
// Single node splicing
newList.Add(nodeToSplice);
end;
end
else if (not evaluatedSpliceValue.IsVoid) then
newList.Add(TAst.Constant(evaluatedSpliceValue, node.Identity.Location));
@@ -277,22 +286,26 @@ end;
function TExpansionVisitor.VisitLambdaExpression(const Node: IAstNode): IAstNode;
var
L: ILambdaExpressionNode;
newParams: TArray<IIdentifierNode>;
newParams: TArray<IAstNode>;
newBody: IAstNode;
newName: string;
i: Integer;
paramsTuple: ITupleNode;
begin
L := Node.AsLambdaExpression;
SetLength(newParams, L.Parameters.Count);
for i := 0 to L.Parameters.Count - 1 do
paramsTuple := L.Parameters;
SetLength(newParams, paramsTuple.Count);
for i := 0 to paramsTuple.Count - 1 do
begin
var param := L.Parameters[i];
var param := paramsTuple.Items[i].AsIdentifier; // Binder ensures params are Identifiers
newName := Gensym(param.Name);
newParams[i] := TAst.Identifier(newName, param.Identity.Location);
end;
newBody := Accept(L.Body);
Result := TAst.LambdaExpr(Node.Identity, TParameterList.Create(newParams, L.Parameters.Identity), newBody);
Result := TAst.LambdaExpr(Node.Identity, TAst.Tuple(L.Parameters.Identity, newParams), newBody);
end;
function TExpansionVisitor.VisitUnquote(const Node: IAstNode): IAstNode;
@@ -335,7 +348,7 @@ end;
function TExpansionVisitor.VisitUnquoteSplicing(const Node: IAstNode): IAstNode;
begin
raise EMacroException.Create('Unquote-splicing (`~@`) can only appear inside a list form.');
raise EMacroException.Create('Unquote-splicing (`~@`) can only appear inside a list/tuple form.');
end;
function TExpansionVisitor.VisitFunctionCall(const Node: IAstNode): IAstNode;
@@ -343,27 +356,38 @@ var
C: IFunctionCallNode;
newArgs: TArray<IAstNode>;
transformedCallee: IAstNode;
argsTuple: ITupleNode;
begin
C := Node.AsFunctionCall;
transformedCallee := Self.Accept(C.Callee);
// Arguments is a Tuple, so we can splice into it
newArgs := TransformAndSpliceNodes(C.Arguments);
Result := TAst.FunctionCall(Node.Identity, transformedCallee, TArgumentList.Create(newArgs, C.Arguments.Identity));
// Wrap result array in a TupleNode before creating FunctionCall
argsTuple := TAst.Tuple(C.Arguments.Identity, newArgs);
Result := TAst.FunctionCall(Node.Identity, transformedCallee, argsTuple);
end;
function TExpansionVisitor.VisitBlockExpression(const Node: IAstNode): IAstNode;
var
B: IBlockExpressionNode;
newExprs: TArray<IAstNode>;
exprsTuple: ITupleNode;
begin
B := Node.AsBlockExpression;
// Expressions is a Tuple
newExprs := TransformAndSpliceNodes(B.Expressions);
Result := TAst.Block(Node.Identity, TExpressionList.Create(newExprs, B.Expressions.Identity));
exprsTuple := TAst.Tuple(B.Expressions.Identity, newExprs);
Result := TAst.Block(Node.Identity, exprsTuple);
end;
function TExpansionVisitor.VisitRecordLiteral(const Node: IAstNode): IAstNode;
begin
// Delegate to standard transformer to process fields recursively
// This calls inherited VisitRecordLiteral(IAstNode)
// This calls inherited VisitRecordLiteral(IAstNode) which visits the Fields Tuple
Result := inherited VisitRecordLiteral(Node);
end;
@@ -473,13 +497,17 @@ begin
if macroDef <> nil then
begin
var expansionScope := TScope.CreateScope(FInitialScope, nil, nil);
var params := macroDef.Parameters;
var paramsTuple := macroDef.Parameters;
var argsTuple := C.Arguments;
if C.Arguments.Count <> params.Count then
raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, params.Count]);
if argsTuple.Count <> paramsTuple.Count then
raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, paramsTuple.Count]);
for i := 0 to params.Count - 1 do
expansionScope.Define(params[i].Name, TDataValue.FromIntf<IAstNode>(C.Arguments[i]));
for i := 0 to paramsTuple.Count - 1 do
begin
var paramName := paramsTuple.Items[i].AsIdentifier.Name;
expansionScope.Define(paramName, TDataValue.FromIntf<IAstNode>(argsTuple.Items[i]));
end;
var expandedBody := TExpansionVisitor.Expand(expansionScope, macroDef.Body.AsQuasiquote.Expression, FMacroEvaluator);
var macroNode := TAst.MacroExpansionNode(Node.Identity, C, expandedBody);