diff --git a/ASTPlayground/Myc.Fmx.AstEditor.Text.pas b/ASTPlayground/Myc.Fmx.AstEditor.Text.pas index 92f3abe..e093905 100644 --- a/ASTPlayground/Myc.Fmx.AstEditor.Text.pas +++ b/ASTPlayground/Myc.Fmx.AstEditor.Text.pas @@ -20,6 +20,7 @@ type function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; + function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; @@ -164,6 +165,33 @@ begin end; end; +function TAstToTextVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; +var + i: Integer; + sb: TStringBuilder; +begin + // Added text representation for macro definitions. + sb := TStringBuilder.Create; + try + sb.Append('defmacro '); + sb.Append(Node.Name.Name); + sb.Append('('); + if Length(Node.Parameters) > 0 then + begin + for i := 0 to High(Node.Parameters) do + begin + sb.Append(Node.Parameters[i].Name); + if i < High(Node.Parameters) then + sb.Append(', '); + end; + end; + sb.Append(') => {...}'); + Result := sb.ToString; + finally + sb.Free; + end; +end; + function TAstToTextVisitor.VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; var baseStr: string; diff --git a/ASTPlayground/Myc.Fmx.AstEditor.pas b/ASTPlayground/Myc.Fmx.AstEditor.pas index dc5768d..0e8f66d 100644 --- a/ASTPlayground/Myc.Fmx.AstEditor.pas +++ b/ASTPlayground/Myc.Fmx.AstEditor.pas @@ -114,6 +114,7 @@ type function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; + function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; function VisitIndexer(const Node: IIndexerNode): TDataValue; override; function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; @@ -1147,6 +1148,103 @@ begin Result := TDataValue.Void; end; +function TAstToAuraNodeVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; +var + paramStr: String; + macroNode: TAuraNode; + i: Integer; + childVisitor: TAstToAuraNodeVisitor; + childStartPos: TPointF; + maxRight, maxBottom: Single; + control: TControl; + childLastResult: TAuraNodeResult; + entryNode, exitNode: TAuraNode; + paramDescriptor: IScopeDescriptor; +begin + // Create a string representation of the parameters for the node title. + paramStr := '('; + if Length(Node.Parameters) > 0 then + begin + paramStr := paramStr + Node.Parameters[0].Name; + for i := 1 to High(Node.Parameters) do + paramStr := paramStr + ', ' + Node.Parameters[i].Name; + end; + paramStr := paramStr + ')'; + + // 1. Create the main container node for the macro definition. + macroNode := BuildNodeControl('defmacro: ' + Node.Name.Name, paramStr); + macroNode.Position.Point := FCurrentPos; + + // A macro definition is a statement, so it has execution pins. + CreateEntry(macroNode); + + // 2. Create a temporary scope descriptor for the macro's parameters. + // This allows the child visitor to correctly visualize parameter nodes. + paramDescriptor := TScope.CreateDescriptor(nil); + for i := 0 to High(Node.Parameters) do + paramDescriptor.Define(Node.Parameters[i].Name); + + // 3. Create a child visitor for the new scope within the macro's body. + const pinNodeHeight = cPinSize + cVerticalPadding; + childStartPos := TPointF.Create(FSpacing.X, FSpacing.Y + pinNodeHeight); + childVisitor := TAstToAuraNodeVisitor.Create(FWorkspace, macroNode, childStartPos, FConnections, [], FMode, Self, nil, paramDescriptor); + + // 4. Create the visual nodes for the parameters inside the macro's scope. + for i := 0 to High(Node.Parameters) do + begin + var param := Node.Parameters[i]; + var paramNode := childVisitor.CreateNodeControl('Parameter', param.Name); + var paramResult: TAuraNodeResult; + paramResult.LayoutNode := paramNode; + paramResult.OutputPin := childVisitor.CreateOutput(paramNode); + childVisitor.FinalizeNodeLayout(paramNode); + // Cache the parameter node by its slot index for lookups within the body. + if i < Length(childVisitor.FSlotCache) then + childVisitor.FSlotCache[i] := paramResult; + end; + + // 5. Let the child visitor render the macro's body. + Node.Body.Accept(childVisitor); + childLastResult := childVisitor.FLastResult; + + // 6. Resize the container to fit all internally generated nodes. + maxRight := 0; + maxBottom := 0; + for control in macroNode.Controls do + begin + if control is TAuraNode then + begin + maxRight := Max(maxRight, control.Position.X + control.Width); + maxBottom := Max(maxBottom, control.Position.Y + control.Height); + end; + end; + macroNode.Width := Max(macroNode.Width, maxRight + FSpacing.X); + macroNode.Height := Max(macroNode.Height, maxBottom + FSpacing.Y); + + // 7. Create an internal 'expansion' exit point, showing what the macro produces. + exitNode := BuildNodeControl('Expansion', ''); + exitNode.Parent := macroNode; + exitNode.Height := pinNodeHeight; + // Connect the data flow (the result of the last expression in the body) to the expansion node. + if Assigned(childLastResult.OutputPin) then + begin + var dataResultPin := childVisitor.CreateInput(exitNode, 'AST'); + FConnections.Add(TPinConnection.Create(childLastResult.OutputPin, dataResultPin)); + end; + childVisitor.FinalizeNodeLayout(exitNode); + macroNode.Height := Max(macroNode.Height, maxBottom + FSpacing.Y + exitNode.Height); + exitNode.Position.Point := TPointF.Create(FSpacing.X, macroNode.Height - exitNode.Height - FSpacing.Y); + + // 8. Finalize the main visitor state. + FCurrentPos.Y := macroNode.Position.Y + macroNode.Height + FSpacing.Y; + FLastResult.LayoutNode := macroNode; + FLastResult.OutputPin := nil; // A macro definition itself does not return a value. + CreateExit(macroNode, ''); // Execution flow continues after the definition. + FinalizeNodeLayout(macroNode); + + Result := TDataValue.Void; +end; + function TAstToAuraNodeVisitor.VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; var details: string; diff --git a/Src/AST/Myc.Ast.Binding.pas b/Src/AST/Myc.Ast.Binding.pas index 6cafe7c..3a3d599 100644 --- a/Src/AST/Myc.Ast.Binding.pas +++ b/Src/AST/Myc.Ast.Binding.pas @@ -60,6 +60,7 @@ type function TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode; override; function TransformAssignment(const Node: IAssignmentNode): IAssignmentNode; override; function TransformLambdaExpression(const Node: ILambdaExpressionNode): ILambdaExpressionNode; override; + function TransformMacroDefinition(const Node: IMacroDefinitionNode): IMacroDefinitionNode; override; function TransformFunctionCall(const Node: IFunctionCallNode): IFunctionCallNode; override; function TransformRecur(const Node: IRecurNode): IRecurNode; override; function TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode; override; @@ -478,6 +479,12 @@ begin Result := Node; end; +function TAstBinder.TransformMacroDefinition(const Node: IMacroDefinitionNode): IMacroDefinitionNode; +begin + // The binder runs after the macro expander. It should never see a macro definition. + raise Exception.Create('IMacroDefinitionNode found in AST after macro expansion phase.'); +end; + function TAstBinder.TransformTernaryExpression(const Node: ITernaryExpressionNode): ITernaryExpressionNode; var isContextTail: Boolean; diff --git a/Src/AST/Myc.Ast.Dumper.pas b/Src/AST/Myc.Ast.Dumper.pas index 4e94a7f..6c989f2 100644 --- a/Src/AST/Myc.Ast.Dumper.pas +++ b/Src/AST/Myc.Ast.Dumper.pas @@ -41,6 +41,7 @@ type function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; + function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; function VisitIndexer(const Node: IIndexerNode): TDataValue; override; function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; @@ -322,6 +323,33 @@ begin Result := TDataValue.Void; end; +function TAstDumper.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; +var + param: IIdentifierNode; +begin + Log('MacroDefinition'); + Indent; + + Log('Name:'); + Indent; + Node.Name.Accept(Self); + Unindent; + + Log('Parameters:'); + Indent; + for param in Node.Parameters do + param.Accept(Self); + Unindent; + + Log('Body:'); + Indent; + Node.Body.Accept(Self); + Unindent; + + Unindent; + Result := TDataValue.Void; +end; + function TAstDumper.VisitIndexer(const Node: IIndexerNode): TDataValue; begin Log('Indexer'); diff --git a/Src/AST/Myc.Ast.Evaluator.pas b/Src/AST/Myc.Ast.Evaluator.pas index ccba005..22db272 100644 --- a/Src/AST/Myc.Ast.Evaluator.pas +++ b/Src/AST/Myc.Ast.Evaluator.pas @@ -35,6 +35,7 @@ type function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; + function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; @@ -240,6 +241,14 @@ begin Result := closure; end; +function TEvaluatorVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; +begin + // Macro definitions are compile-time constructs and should have been + // processed and removed from the AST by the TMacroExpander. + // If we encounter one here, it's a compiler pipeline error. + raise Exception.Create('Macro definitions cannot be evaluated at runtime.'); +end; + function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; var calleeValue: TDataValue; diff --git a/Src/AST/Myc.Ast.JSON.pas b/Src/AST/Myc.Ast.JSON.pas index 0f96b9b..477258f 100644 --- a/Src/AST/Myc.Ast.JSON.pas +++ b/Src/AST/Myc.Ast.JSON.pas @@ -34,6 +34,7 @@ type function JsonToIfExprNode(const AObj: TJSONObject): IIfExpressionNode; function JsonToTernaryExprNode(const AObj: TJSONObject): ITernaryExpressionNode; function JsonToLambdaExprNode(const AObj: TJSONObject): ILambdaExpressionNode; + function JsonToMacroDefNode(const AObj: TJSONObject): IMacroDefinitionNode; function JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode; function JsonToRecurNode(const AObj: TJSONObject): IRecurNode; function JsonToBlockNode(const AObj: TJSONObject): IBlockExpressionNode; @@ -53,6 +54,7 @@ type function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override; function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; override; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; override; + function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; override; function VisitRecurNode(const Node: IRecurNode): TDataValue; override; function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; @@ -258,6 +260,41 @@ begin Result := TDataValue.Void; end; +function TJsonAstConverter.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; +var + obj, nameObj, bodyObj: TJSONObject; + paramsArray: TJSONArray; + tempParams: TArray; + param: IIdentifierNode; + i: Integer; +begin + // Added serialization for macro definitions. + Node.Name.Accept(Self); + for param in Node.Parameters do + param.Accept(Self); + Node.Body.Accept(Self); + + bodyObj := FJsonObjectStack.Pop; + + SetLength(tempParams, Length(Node.Parameters)); + for i := High(tempParams) downto 0 do + tempParams[i] := FJsonObjectStack.Pop; + + nameObj := FJsonObjectStack.Pop; + + paramsArray := TJSONArray.Create; + for i := 0 to High(tempParams) do + paramsArray.Add(tempParams[i]); + + obj := TJSONObject.Create; + obj.AddPair('NodeType', TJSONString.Create('MacroDef')); + obj.AddPair('Name', nameObj); + obj.AddPair('Parameters', paramsArray); + obj.AddPair('Body', bodyObj); + FJsonObjectStack.Push(obj); + Result := TDataValue.Void; +end; + function TJsonAstConverter.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; var obj, calleeObj: TJSONObject; @@ -589,6 +626,26 @@ begin Result := TAst.LambdaExpr(params, body); end; +function TJsonAstConverter.JsonToMacroDefNode(const AObj: TJSONObject): IMacroDefinitionNode; +var + name: IIdentifierNode; + params: TArray; + body: IAstNode; + paramArray: TJSONArray; + i: Integer; +begin + // Added deserialization for macro definitions. + name := JsonToIdentifierNode(AObj.GetValue('Name') as TJSONObject); + + paramArray := AObj.GetValue('Parameters'); + SetLength(params, paramArray.Count); + for i := 0 to paramArray.Count - 1 do + params[i] := JsonToIdentifierNode(paramArray.Items[i] as TJSONObject); + + body := JsonToNode(AObj.GetValue('Body')); + Result := TAst.MacroDef(name, params, body); +end; + function TJsonAstConverter.JsonToFunctionCallNode(const AObj: TJSONObject): IFunctionCallNode; var callee: IAstNode; @@ -715,6 +772,8 @@ begin Result := JsonToTernaryExprNode(obj) else if nodeType = 'LambdaExpr' then Result := JsonToLambdaExprNode(obj) + else if nodeType = 'MacroDef' then + Result := JsonToMacroDefNode(obj) else if nodeType = 'FunctionCall' then Result := JsonToFunctionCallNode(obj) else if nodeType = 'Recur' then diff --git a/Src/AST/Myc.Ast.Nodes.pas b/Src/AST/Myc.Ast.Nodes.pas index c4a2098..11c2326 100644 --- a/Src/AST/Myc.Ast.Nodes.pas +++ b/Src/AST/Myc.Ast.Nodes.pas @@ -23,6 +23,7 @@ type IBlockExpressionNode = interface; IVariableDeclarationNode = interface; IAssignmentNode = interface; + IMacroDefinitionNode = interface; IIndexerNode = interface; IMemberAccessNode = interface; ICreateSeriesNode = interface; @@ -57,6 +58,7 @@ type function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; function VisitAssignment(const Node: IAssignmentNode): TDataValue; + function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; function VisitIndexer(const Node: IIndexerNode): TDataValue; function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; @@ -177,6 +179,17 @@ type property Value: IAstNode read GetValue; end; + IMacroDefinitionNode = interface(IAstNode) + {$region 'private'} + function GetName: IIdentifierNode; + function GetParameters: TArray; + function GetBody: IAstNode; + {$endregion} + property Name: IIdentifierNode read GetName; + property Parameters: TArray read GetParameters; + property Body: IAstNode read GetBody; + end; + IIndexerNode = interface(IAstNode) {$region 'private'} function GetBase: IAstNode; diff --git a/Src/AST/Myc.Ast.Script.pas b/Src/AST/Myc.Ast.Script.pas index b159b1c..0165a0b 100644 --- a/Src/AST/Myc.Ast.Script.pas +++ b/Src/AST/Myc.Ast.Script.pas @@ -108,6 +108,7 @@ type function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue; function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue; + function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; @@ -359,6 +360,20 @@ begin raise Exception.Create('Syntax Error: Expected an identifier for def statement.'); Result := TAst.VarDecl(IIdentifierNode(tailNodes[0]), IfThen(Length(tailNodes) > 1, tailNodes[1], nil)); end + else if SameText(head.Token.Text, 'defmacro') then + begin + // (defmacro name [params] body) + 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[1].Node <> nil then + raise Exception.Create('Syntax Error: Expected a parameter list [...] after macro name.'); + + var macroName := IIdentifierNode(tailNodes[0]); + var macroParams := elements[1].Params; + var macroBody := tailNodes[2]; + + Result := TAst.MacroDef(macroName, macroParams, macroBody); + end else if SameText(head.Token.Text, 'assign') then begin if tailTokens[0].Kind <> tkIdentifier then @@ -424,6 +439,19 @@ var i64: Int64; dbl: Double; begin + // TODO: Implement reader macros for quasiquoting here. + // The current lexer will tokenize `, ~, and ~@ as identifiers. + // Check for them here and wrap the subsequent expression accordingly. + // Example: + // if FCurrentToken.Text = '`' then + // begin + // NextToken; + // var exprToQuote := ParseExpression; + // Result.Node := TAst.Quasiquote(exprToQuote.Node); + // exit; + // end; + // (This requires IQuasiquoteNode and TAst.Quasiquote to be defined first). + Result.Token := FCurrentToken; case FCurrentToken.Kind of tkNumber: @@ -606,6 +634,33 @@ begin Result := TDataValue.Void; end; +function TPrettyPrintVisitor.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; +var + param: IIdentifierNode; + sb: TStringBuilder; +begin + // Added visitor for pretty printing macro definitions. + sb := TStringBuilder.Create; + try + for param in Node.Parameters do + sb.Append(param.Name + ' '); + if sb.Length > 0 then + sb.Remove(sb.Length - 1, 1); + + Append('(defmacro ' + Node.Name.Name + ' [' + sb.ToString + ']'); + finally + sb.Free; + end; + + Indent; + NewLine; + Node.Body.Accept(Self); + Unindent; + NewLine; + Append(')'); + Result := TDataValue.Void; +end; + function TPrettyPrintVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue; var arg: IAstNode; diff --git a/Src/AST/Myc.Ast.Visitor.pas b/Src/AST/Myc.Ast.Visitor.pas index a8bf43d..47ee9b6 100644 --- a/Src/AST/Myc.Ast.Visitor.pas +++ b/Src/AST/Myc.Ast.Visitor.pas @@ -24,6 +24,7 @@ type function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; virtual; abstract; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; virtual; abstract; function VisitAssignment(const Node: IAssignmentNode): TDataValue; virtual; abstract; + function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; virtual; abstract; function VisitIndexer(const Node: IIndexerNode): TDataValue; virtual; abstract; function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; virtual; abstract; function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; virtual; abstract; @@ -57,6 +58,7 @@ type function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue; override; final; function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue; override; final; function VisitAssignment(const Node: IAssignmentNode): TDataValue; override; final; + function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; override; final; function VisitIndexer(const Node: IIndexerNode): TDataValue; override; final; function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override; final; function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override; final; @@ -76,6 +78,7 @@ type function TransformBlockExpression(const Node: IBlockExpressionNode): IBlockExpressionNode; virtual; function TransformVariableDeclaration(const Node: IVariableDeclarationNode): IVariableDeclarationNode; virtual; function TransformAssignment(const Node: IAssignmentNode): IAssignmentNode; virtual; + function TransformMacroDefinition(const Node: IMacroDefinitionNode): IMacroDefinitionNode; virtual; function TransformIndexer(const Node: IIndexerNode): IIndexerNode; virtual; function TransformMemberAccess(const Node: IMemberAccessNode): IMemberAccessNode; virtual; function TransformCreateSeries(const Node: ICreateSeriesNode): ICreateSeriesNode; virtual; @@ -234,6 +237,18 @@ begin Result := TAst.Assign(identifier, value); end; +function TAstTransformer.TransformMacroDefinition(const Node: IMacroDefinitionNode): IMacroDefinitionNode; +begin + // Added identity transform for macro definitions. + var name := Accept(Node.Name).AsIntf; + var parameters := TransformNodes(Node.Parameters); + var body := Accept(Node.Body).AsIntf; + if (name = Node.Name) and (parameters = Node.Parameters) and (body = Node.Body) then + Result := Node + else + Result := TAst.MacroDef(name, parameters, body); +end; + function TAstTransformer.TransformIndexer(const Node: IIndexerNode): IIndexerNode; begin var base := Accept(Node.Base).AsIntf; @@ -360,6 +375,12 @@ begin Result := TDataValue.FromIntf(TransformAssignment(Node)); end; +function TAstTransformer.VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue; +begin + // Added visit method for macro definitions. + Result := TDataValue.FromIntf(TransformMacroDefinition(Node)); +end; + function TAstTransformer.VisitIndexer(const Node: IIndexerNode): TDataValue; begin Result := TDataValue.FromIntf(TransformIndexer(Node)); diff --git a/Src/AST/Myc.Ast.pas b/Src/AST/Myc.Ast.pas index 78f6a7b..62b7d67 100644 --- a/Src/AST/Myc.Ast.pas +++ b/Src/AST/Myc.Ast.pas @@ -36,6 +36,11 @@ type class function IfExpr(const ACondition: IAstNode; const AThenBranch, AElseBranch: IAstNode): IIfExpressionNode; static; class function TernaryExpr(const ACondition: IAstNode; const AThenBranch, AElseBranch: IAstNode): ITernaryExpressionNode; static; class function LambdaExpr(const AParameters: TArray; const ABody: IAstNode): ILambdaExpressionNode; static; + class function MacroDef( + const AName: IIdentifierNode; + const AParameters: TArray; + const ABody: IAstNode + ): IMacroDefinitionNode; static; class function FunctionCall(const ACallee: IAstNode; const AArguments: TArray): IFunctionCallNode; static; class function Recur(const AArguments: array of IAstNode): IRecurNode; static; class function Block(const AExpressions: array of IAstNode): IBlockExpressionNode; static; @@ -141,6 +146,19 @@ type property Parameters: TArray read FParameters; end; + TMacroDefinitionNode = class(TAstNode, IMacroDefinitionNode) + private + FName: IIdentifierNode; + FParameters: TArray; + FBody: IAstNode; + function GetName: IIdentifierNode; + function GetParameters: TArray; + function GetBody: IAstNode; + public + constructor Create(const AName: IIdentifierNode; const AParameters: TArray; const ABody: IAstNode); + function Accept(const Visitor: IAstVisitor): TDataValue; override; + end; + TFunctionCallNode = class(TAstNode, IFunctionCallNode) private FCallee: IAstNode; @@ -428,6 +446,37 @@ begin Result := FParameters; end; +{ TMacroDefinitionNode } + +constructor TMacroDefinitionNode.Create(const AName: IIdentifierNode; const AParameters: TArray; const ABody: IAstNode); +begin + // Added concrete class for macro definitions. + inherited Create; + FName := AName; + FParameters := AParameters; + FBody := ABody; +end; + +function TMacroDefinitionNode.Accept(const Visitor: IAstVisitor): TDataValue; +begin + Result := Visitor.VisitMacroDefinition(Self); +end; + +function TMacroDefinitionNode.GetBody: IAstNode; +begin + Result := FBody; +end; + +function TMacroDefinitionNode.GetName: IIdentifierNode; +begin + Result := FName; +end; + +function TMacroDefinitionNode.GetParameters: TArray; +begin + Result := FParameters; +end; + { TFunctionCallNode } constructor TFunctionCallNode.Create(const ACallee: IAstNode; const AArguments: TArray); @@ -751,6 +800,21 @@ begin Result := TIndexerNode.Create(ABase, AIndex); end; +class function TAst.LambdaExpr(const AParameters: TArray; const ABody: IAstNode): ILambdaExpressionNode; +begin + Result := TLambdaExpressionNode.Create(AParameters, ABody); +end; + +class function TAst.MacroDef( + const AName: IIdentifierNode; + const AParameters: TArray; + const ABody: IAstNode +): IMacroDefinitionNode; +begin + // Added factory for macro definitions. + Result := TMacroDefinitionNode.Create(AName, AParameters, ABody); +end; + class function TAst.MemberAccess(const ABase: IAstNode; const AMember: IIdentifierNode): IMemberAccessNode; begin Result := TMemberAccessNode.Create(ABase, AMember); @@ -777,11 +841,6 @@ begin Result := TTernaryExpressionNode.Create(ACondition, AThenBranch, AElseBranch); end; -class function TAst.LambdaExpr(const AParameters: TArray; const ABody: IAstNode): ILambdaExpressionNode; -begin - Result := TLambdaExpressionNode.Create(AParameters, ABody); -end; - class function TAst.UnaryExpr(const AOperator: TScalar.TUnaryOp; const ARight: IAstNode): IUnaryExpressionNode; begin Result := TUnaryExpressionNode.Create(AOperator, ARight);