Ast list nodes
This commit is contained in:
@@ -34,15 +34,20 @@ type
|
||||
class var
|
||||
FGensymCounter: Int64;
|
||||
|
||||
function TransformAndSpliceNodes(const ANodes: TArray<IAstNode>): TArray<IAstNode>;
|
||||
// Helper to handle unquote-splicing (~@) within lists.
|
||||
// Takes a source list (e.g., Arguments) and returns a flattened array for the new node.
|
||||
function TransformAndSpliceNodes(const ANodes: INodeList<IAstNode>): TArray<IAstNode>;
|
||||
function Gensym(const ABaseName: string): string;
|
||||
protected
|
||||
function VisitUnquote(const Node: IUnquoteNode): IAstNode; override;
|
||||
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): IAstNode; override;
|
||||
|
||||
// We override these container visits to handle Splicing manually,
|
||||
// bypassing the standard 1:1 list visitor.
|
||||
function VisitFunctionCall(const Node: IFunctionCallNode): IAstNode; override;
|
||||
function VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode; override;
|
||||
function VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; override;
|
||||
|
||||
function VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode; override;
|
||||
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): IAstNode; override;
|
||||
function VisitLambdaExpression(const Node: ILambdaExpressionNode): IAstNode; override;
|
||||
function VisitIdentifier(const Node: IIdentifierNode): IAstNode; override;
|
||||
@@ -169,15 +174,16 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TExpansionVisitor.TransformAndSpliceNodes(const ANodes: TArray<IAstNode>): TArray<IAstNode>;
|
||||
function TExpansionVisitor.TransformAndSpliceNodes(const ANodes: INodeList<IAstNode>): TArray<IAstNode>;
|
||||
var
|
||||
newList: TList<IAstNode>;
|
||||
nodeToSplice: IAstNode;
|
||||
transformedNode: IAstNode;
|
||||
node: IAstNode;
|
||||
begin
|
||||
newList := TList<IAstNode>.Create;
|
||||
try
|
||||
for var node in ANodes do
|
||||
for node in ANodes do
|
||||
begin
|
||||
if node.Kind = akUnquoteSplicing then
|
||||
begin
|
||||
@@ -196,10 +202,30 @@ begin
|
||||
if (not evaluatedSpliceValue.IsVoid) and (evaluatedSpliceValue.Kind = vkInterface) then
|
||||
begin
|
||||
nodeToSplice := evaluatedSpliceValue.AsIntf<IAstNode>;
|
||||
if nodeToSplice.Kind = akBlockExpression then
|
||||
newList.AddRange(nodeToSplice.AsBlockExpression.Expressions)
|
||||
|
||||
// Splicing implies the value must be a list-like AST structure (Block or just a List Node)
|
||||
// If the evaluated value is an IArgumentList, IExpressionList etc., we flatten it.
|
||||
if nodeToSplice.Kind = akExpressionList then
|
||||
begin
|
||||
for var subItem in nodeToSplice.AsExpressionList do
|
||||
newList.Add(subItem);
|
||||
end
|
||||
else if nodeToSplice.Kind = akArgumentList then
|
||||
begin
|
||||
for var subItem in nodeToSplice.AsArgumentList do
|
||||
newList.Add(subItem);
|
||||
end
|
||||
else if nodeToSplice.Kind = akBlockExpression then
|
||||
begin
|
||||
// Splice content of block
|
||||
for var subItem in nodeToSplice.AsBlockExpression.Expressions do
|
||||
newList.Add(subItem);
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Treat as single item
|
||||
newList.Add(nodeToSplice);
|
||||
end;
|
||||
end
|
||||
else if (not evaluatedSpliceValue.IsVoid) then
|
||||
begin
|
||||
@@ -261,19 +287,24 @@ var
|
||||
newBody: IAstNode;
|
||||
newName: string;
|
||||
i: Integer;
|
||||
paramList: IParameterList;
|
||||
begin
|
||||
SetLength(newParams, Length(Node.Parameters));
|
||||
for i := 0 to High(Node.Parameters) do
|
||||
SetLength(newParams, Node.Parameters.Count);
|
||||
for i := 0 to Node.Parameters.Count - 1 do
|
||||
begin
|
||||
newName := Gensym(Node.Parameters[i].Name);
|
||||
var param := Node.Parameters[i];
|
||||
newName := Gensym(param.Name);
|
||||
// Use location from original param
|
||||
newParams[i] := TAst.Identifier(newName, Node.Parameters[i].Identity.Location);
|
||||
newParams[i] := TAst.Identifier(newName, param.Identity.Location);
|
||||
end;
|
||||
|
||||
newBody := Accept(Node.Body);
|
||||
|
||||
// Rebuild structural lambda
|
||||
Result := TAst.LambdaExpr(Node.Identity, newParams, newBody);
|
||||
// Rebuild: Wrap array in TParameterList with original Identity
|
||||
paramList := TParameterList.Create(newParams, Node.Parameters.Identity);
|
||||
|
||||
// Use transformation overload
|
||||
Result := TAst.LambdaExpr(Node.Identity, paramList, newBody);
|
||||
end;
|
||||
|
||||
function TExpansionVisitor.VisitUnquote(const Node: IUnquoteNode): IAstNode;
|
||||
@@ -284,6 +315,7 @@ var
|
||||
begin
|
||||
expr := Node.Expression;
|
||||
|
||||
// Optimization: If unquote refers to a local macro variable directly, try to get it.
|
||||
if expr.Kind = akIdentifier then
|
||||
begin
|
||||
addr := FMacroScope.Resolve(expr.AsIdentifier.Name);
|
||||
@@ -329,22 +361,36 @@ function TExpansionVisitor.VisitFunctionCall(const Node: IFunctionCallNode): IAs
|
||||
var
|
||||
newArgs: TArray<IAstNode>;
|
||||
transformedCallee: IAstNode;
|
||||
argList: IArgumentList;
|
||||
begin
|
||||
transformedCallee := Self.Accept(Node.Callee);
|
||||
|
||||
// Use splicing aware transformation for arguments (returns TArray)
|
||||
newArgs := TransformAndSpliceNodes(Node.Arguments);
|
||||
Result := TAst.FunctionCall(Node.Identity, transformedCallee, newArgs);
|
||||
|
||||
// Wrap in List container to use Transformation overload
|
||||
argList := TArgumentList.Create(newArgs, Node.Arguments.Identity);
|
||||
|
||||
Result := TAst.FunctionCall(Node.Identity, transformedCallee, argList);
|
||||
end;
|
||||
|
||||
function TExpansionVisitor.VisitBlockExpression(const Node: IBlockExpressionNode): IAstNode;
|
||||
var
|
||||
newExprs: TArray<IAstNode>;
|
||||
exprList: IExpressionList;
|
||||
begin
|
||||
// Use splicing aware transformation for block expressions
|
||||
newExprs := TransformAndSpliceNodes(Node.Expressions);
|
||||
Result := TAst.Block(Node.Identity, newExprs);
|
||||
|
||||
// Wrap in List
|
||||
exprList := TExpressionList.Create(newExprs, Node.Expressions.Identity);
|
||||
|
||||
Result := TAst.Block(Node.Identity, exprList);
|
||||
end;
|
||||
|
||||
function TExpansionVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): IAstNode;
|
||||
begin
|
||||
// For now, standard transformation for records (no splicing key-values yet)
|
||||
Result := inherited VisitRecordLiteral(Node);
|
||||
end;
|
||||
|
||||
@@ -438,10 +484,10 @@ begin
|
||||
var expansionScope := TScope.CreateScope(FInitialScope, nil, nil);
|
||||
|
||||
var params := macroDef.Parameters;
|
||||
if Length(Node.Arguments) <> Length(params) then
|
||||
raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, Length(params)]);
|
||||
if Node.Arguments.Count <> params.Count then
|
||||
raise EMacroException.CreateFmt('Macro %s expects %d arguments.', [calleeIdentifier.Name, params.Count]);
|
||||
|
||||
for i := 0 to High(params) do
|
||||
for i := 0 to params.Count - 1 do
|
||||
expansionScope.Define(params[i].Name, TDataValue.FromIntf<IAstNode>(Node.Arguments[i]));
|
||||
|
||||
var expandedBody := TExpansionVisitor.Expand(expansionScope, macroDef.Body.AsQuasiquote.Expression, FMacroEvaluator);
|
||||
|
||||
Reference in New Issue
Block a user