unit Myc.Ast; interface uses System.SysUtils, System.Generics.Collections, Myc.Data.Types; type // Operators are now type-safe enums TBinaryOperator = (boAdd, boSubtract, boMultiply, boDivide); TUnaryOperator = (uoNegate, uoNot); // Forward declarations for interfaces IAstVisitor = interface; IAstNode = interface; IExpressionNode = interface; IStatementNode = interface; IConstantNode = interface; IIdentifierNode = interface; IBinaryExpressionNode = interface; IUnaryExpressionNode = interface; IIfExpressionNode = interface; ILambdaExpressionNode = interface; IFunctionCallNode = interface; IBlockStatementNode = interface; IVariableDeclarationStatementNode = interface; IExpressionStatementNode = interface; // --- Abstract Node Interfaces --- // Base interface for all AST nodes IAstNode = interface(IInterface) // Accept is a function that returns the result of the visit. function Accept(const Visitor: IAstVisitor): IDataValue; end; // Abstract interface for all nodes that evaluate to a value. IExpressionNode = interface(IAstNode) end; // Abstract interface for all nodes that perform an action. IStatementNode = interface(IAstNode) end; // --- Concrete Expression Node Interfaces --- IConstantNode = interface(IExpressionNode) {$region 'private'} function GetValue: IDataValue; {$endregion} property Value: IDataValue read GetValue; end; IIdentifierNode = interface(IExpressionNode) {$region 'private'} function GetName: string; {$endregion} property Name: string read GetName; end; IBinaryExpressionNode = interface(IExpressionNode) {$region 'private'} function GetLeft: IExpressionNode; function GetOperator: TBinaryOperator; function GetRight: IExpressionNode; {$endregion} property Left: IExpressionNode read GetLeft; property Operator: TBinaryOperator read GetOperator; property Right: IExpressionNode read GetRight; end; IUnaryExpressionNode = interface(IExpressionNode) {$region 'private'} function GetOperator: TUnaryOperator; function GetRight: IExpressionNode; {$endregion} property Operator: TUnaryOperator read GetOperator; property Right: IExpressionNode read GetRight; end; IIfExpressionNode = interface(IExpressionNode) {$region 'private'} function GetCondition: IExpressionNode; function GetThenBranch: IExpressionNode; function GetElseBranch: IExpressionNode; {$endregion} property Condition: IExpressionNode read GetCondition; property ThenBranch: IExpressionNode read GetThenBranch; property ElseBranch: IExpressionNode read GetElseBranch; end; ILambdaExpressionNode = interface(IExpressionNode) {$region 'private'} function GetParameters: TList; function GetBody: IAstNode; // Can be an expression or a block statement {$endregion} property Parameters: TList read GetParameters; property Body: IAstNode read GetBody; end; IFunctionCallNode = interface(IExpressionNode) {$region 'private'} function GetCallee: IExpressionNode; function GetArguments: TList; {$endregion} property Callee: IExpressionNode read GetCallee; property Arguments: TList read GetArguments; end; // --- Concrete Statement Node Interfaces --- IBlockStatementNode = interface(IStatementNode) {$region 'private'} function GetStatements: TList; {$endregion} property Statements: TList read GetStatements; end; IVariableDeclarationStatementNode = interface(IStatementNode) {$region 'private'} function GetIdentifier: IIdentifierNode; function GetInitializer: IExpressionNode; // Can be nil {$endregion} property Identifier: IIdentifierNode read GetIdentifier; property Initializer: IExpressionNode read GetInitializer; end; IExpressionStatementNode = interface(IStatementNode) {$region 'private'} function GetExpression: IExpressionNode; {$endregion} property Expression: IExpressionNode read GetExpression; end; // All visitor methods are functions returning a value. IAstVisitor = interface ['{5F4110E9-0158-41E9-A512-E57A843E8A5A}'] // Expression visitors function VisitConstant(const Node: IConstantNode): IDataValue; function VisitIdentifier(const Node: IIdentifierNode): IDataValue; function VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue; function VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue; function VisitIfExpression(const Node: IIfExpressionNode): IDataValue; function VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue; function VisitFunctionCall(const Node: IFunctionCallNode): IDataValue; // Statement visitors function VisitBlockStatement(const Node: IBlockStatementNode): IDataValue; function VisitVariableDeclarationStatement(const Node: IVariableDeclarationStatementNode): IDataValue; function VisitExpressionStatement(const Node: IExpressionStatementNode): IDataValue; end; // Record acting as a namespace for the factory functions. TAst = record // Expressions class function Constant(AValue: IDataValue): IConstantNode; static; class function Identifier(AName: string): IIdentifierNode; static; class function BinaryExpr( ALeft: IExpressionNode; AOperator: TBinaryOperator; ARight: IExpressionNode ): IBinaryExpressionNode; static; class function UnaryExpr(AOperator: TUnaryOperator; ARight: IExpressionNode): IUnaryExpressionNode; static; class function IfExpr(ACondition: IExpressionNode; AThenBranch, AElseBranch: IExpressionNode): IIfExpressionNode; static; class function LambdaExpr(const AParameters: array of IIdentifierNode; ABody: IAstNode): ILambdaExpressionNode; static; class function FunctionCall(ACallee: IExpressionNode; const AArguments: array of IExpressionNode): IFunctionCallNode; static; // Statements class function Block(const AStatements: array of IStatementNode): IBlockStatementNode; static; class function VarDecl(AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationStatementNode; static; class function ExprStmt(AExpression: IExpressionNode): IExpressionStatementNode; static; end; // Manages the scope of execution, holding variables and their values. TExecutionScope = class private FParent: TExecutionScope; FVariables: TDictionary; public constructor Create(AParent: TExecutionScope = nil); destructor Destroy; override; function FindValue(const Name: string; out Value: IDataValue): Boolean; procedure SetValue(const Name: string; const Value: IDataValue); end; // TEvaluatorVisitor is stateless and thread-safe. TEvaluatorVisitor = class(TInterfacedObject, IAstVisitor) private FScope: TExecutionScope; function IsTruthy(AValue: IDataValue): Boolean; public constructor Create(AScope: TExecutionScope); // Expression visitors function VisitConstant(const Node: IConstantNode): IDataValue; function VisitIdentifier(const Node: IIdentifierNode): IDataValue; function VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue; function VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue; function VisitIfExpression(const Node: IIfExpressionNode): IDataValue; function VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue; function VisitFunctionCall(const Node: IFunctionCallNode): IDataValue; // Statement visitors function VisitBlockStatement(const Node: IBlockStatementNode): IDataValue; function VisitVariableDeclarationStatement(const Node: IVariableDeclarationStatementNode): IDataValue; function VisitExpressionStatement(const Node: IExpressionStatementNode): IDataValue; end; implementation type TConstantNodeImpl = class(TInterfacedObject, IConstantNode) private FValue: IDataValue; function GetValue: IDataValue; public constructor Create(AValue: IDataValue); function Accept(const Visitor: IAstVisitor): IDataValue; end; TIdentifierNodeImpl = class(TInterfacedObject, IIdentifierNode) private FName: string; function GetName: string; public constructor Create(AName: string); function Accept(const Visitor: IAstVisitor): IDataValue; end; TBinaryExpressionNodeImpl = class(TInterfacedObject, IBinaryExpressionNode) private FLeft: IExpressionNode; FOperator: TBinaryOperator; FRight: IExpressionNode; function GetLeft: IExpressionNode; function GetOperator: TBinaryOperator; function GetRight: IExpressionNode; public constructor Create(ALeft: IExpressionNode; AOperator: TBinaryOperator; ARight: IExpressionNode); function Accept(const Visitor: IAstVisitor): IDataValue; end; TUnaryExpressionNodeImpl = class(TInterfacedObject, IUnaryExpressionNode) private FOperator: TUnaryOperator; FRight: IExpressionNode; function GetOperator: TUnaryOperator; function GetRight: IExpressionNode; public constructor Create(AOperator: TUnaryOperator; ARight: IExpressionNode); function Accept(const Visitor: IAstVisitor): IDataValue; end; TIfExpressionNodeImpl = class(TInterfacedObject, IIfExpressionNode) private FCondition: IExpressionNode; FThenBranch: IExpressionNode; FElseBranch: IExpressionNode; function GetCondition: IExpressionNode; function GetThenBranch: IExpressionNode; function GetElseBranch: IExpressionNode; public constructor Create(ACondition, AThenBranch, AElseBranch: IExpressionNode); function Accept(const Visitor: IAstVisitor): IDataValue; end; TLambdaExpressionNodeImpl = class(TInterfacedObject, ILambdaExpressionNode) private FParameters: TList; FBody: IAstNode; function GetParameters: TList; function GetBody: IAstNode; public constructor Create(AParameters: TList; ABody: IAstNode); destructor Destroy; override; function Accept(const Visitor: IAstVisitor): IDataValue; end; TFunctionCallNodeImpl = class(TInterfacedObject, IFunctionCallNode) private FCallee: IExpressionNode; FArguments: TList; function GetCallee: IExpressionNode; function GetArguments: TList; public constructor Create(ACallee: IExpressionNode; AArguments: TList); destructor Destroy; override; function Accept(const Visitor: IAstVisitor): IDataValue; end; TBlockStatementNodeImpl = class(TInterfacedObject, IBlockStatementNode) private FStatements: TList; function GetStatements: TList; public constructor Create(AStatements: TList); destructor Destroy; override; function Accept(const Visitor: IAstVisitor): IDataValue; end; TVariableDeclarationStatementNodeImpl = class(TInterfacedObject, IVariableDeclarationStatementNode) private FIdentifier: IIdentifierNode; FInitializer: IExpressionNode; function GetIdentifier: IIdentifierNode; function GetInitializer: IExpressionNode; public constructor Create(AIdentifier: IIdentifierNode; AInitializer: IExpressionNode); function Accept(const Visitor: IAstVisitor): IDataValue; end; TExpressionStatementNodeImpl = class(TInterfacedObject, IExpressionStatementNode) private FExpression: IExpressionNode; function GetExpression: IExpressionNode; public constructor Create(AExpression: IExpressionNode); function Accept(const Visitor: IAstVisitor): IDataValue; end; { TAst - Factory Function Implementations } class function TAst.Constant(AValue: IDataValue): IConstantNode; begin Result := TConstantNodeImpl.Create(AValue); end; class function TAst.Identifier(AName: string): IIdentifierNode; begin Result := TIdentifierNodeImpl.Create(AName); end; class function TAst.BinaryExpr(ALeft: IExpressionNode; AOperator: TBinaryOperator; ARight: IExpressionNode): IBinaryExpressionNode; begin Result := TBinaryExpressionNodeImpl.Create(ALeft, AOperator, ARight); end; class function TAst.UnaryExpr(AOperator: TUnaryOperator; ARight: IExpressionNode): IUnaryExpressionNode; begin Result := TUnaryExpressionNodeImpl.Create(AOperator, ARight); end; class function TAst.IfExpr(ACondition: IExpressionNode; AThenBranch, AElseBranch: IExpressionNode): IIfExpressionNode; begin Result := TIfExpressionNodeImpl.Create(ACondition, AThenBranch, AElseBranch); end; class function TAst.LambdaExpr(const AParameters: array of IIdentifierNode; ABody: IAstNode): ILambdaExpressionNode; var paramList: TList; param: IIdentifierNode; begin paramList := TList.Create; for param in AParameters do paramList.Add(param); Result := TLambdaExpressionNodeImpl.Create(paramList, ABody); end; class function TAst.FunctionCall(ACallee: IExpressionNode; const AArguments: array of IExpressionNode): IFunctionCallNode; var argList: TList; arg: IExpressionNode; begin argList := TList.Create; for arg in AArguments do argList.Add(arg); Result := TFunctionCallNodeImpl.Create(ACallee, argList); end; class function TAst.Block(const AStatements: array of IStatementNode): IBlockStatementNode; var stmtList: TList; stmt: IStatementNode; begin stmtList := TList.Create; for stmt in AStatements do stmtList.Add(stmt); Result := TBlockStatementNodeImpl.Create(stmtList); end; class function TAst.VarDecl(AIdentifier: IIdentifierNode; AInitializer: IExpressionNode): IVariableDeclarationStatementNode; begin Result := TVariableDeclarationStatementNodeImpl.Create(AIdentifier, AInitializer); end; class function TAst.ExprStmt(AExpression: IExpressionNode): IExpressionStatementNode; begin Result := TExpressionStatementNodeImpl.Create(AExpression); end; { TConstantNodeImpl } constructor TConstantNodeImpl.Create(AValue: IDataValue); begin inherited Create; FValue := AValue; end; function TConstantNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; begin Result := Visitor.VisitConstant(Self); end; function TConstantNodeImpl.GetValue: IDataValue; begin Result := FValue; end; { TIdentifierNodeImpl } constructor TIdentifierNodeImpl.Create(AName: string); begin inherited Create; FName := AName; end; function TIdentifierNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; begin Result := Visitor.VisitIdentifier(Self); end; function TIdentifierNodeImpl.GetName: string; begin Result := FName; end; { TBinaryExpressionNodeImpl } constructor TBinaryExpressionNodeImpl.Create(ALeft: IExpressionNode; AOperator: TBinaryOperator; ARight: IExpressionNode); begin inherited Create; FLeft := ALeft; FOperator := AOperator; FRight := ARight; end; function TBinaryExpressionNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; begin Result := Visitor.VisitBinaryExpression(Self); end; function TBinaryExpressionNodeImpl.GetLeft: IExpressionNode; begin Result := FLeft; end; function TBinaryExpressionNodeImpl.GetOperator: TBinaryOperator; begin Result := FOperator; end; function TBinaryExpressionNodeImpl.GetRight: IExpressionNode; begin Result := FRight; end; { TUnaryExpressionNodeImpl } constructor TUnaryExpressionNodeImpl.Create(AOperator: TUnaryOperator; ARight: IExpressionNode); begin inherited Create; FOperator := AOperator; FRight := ARight; end; function TUnaryExpressionNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; begin Result := Visitor.VisitUnaryExpression(Self); end; function TUnaryExpressionNodeImpl.GetOperator: TUnaryOperator; begin Result := FOperator; end; function TUnaryExpressionNodeImpl.GetRight: IExpressionNode; begin Result := FRight; end; { TIfExpressionNodeImpl } constructor TIfExpressionNodeImpl.Create(ACondition, AThenBranch, AElseBranch: IExpressionNode); begin inherited Create; FCondition := ACondition; FThenBranch := AThenBranch; FElseBranch := AElseBranch; end; function TIfExpressionNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; begin Result := Visitor.VisitIfExpression(Self); end; function TIfExpressionNodeImpl.GetCondition: IExpressionNode; begin Result := FCondition; end; function TIfExpressionNodeImpl.GetElseBranch: IExpressionNode; begin Result := FElseBranch; end; function TIfExpressionNodeImpl.GetThenBranch: IExpressionNode; begin Result := FThenBranch; end; { TLambdaExpressionNodeImpl } constructor TLambdaExpressionNodeImpl.Create(AParameters: TList; ABody: IAstNode); begin inherited Create; FParameters := AParameters; FBody := ABody; end; destructor TLambdaExpressionNodeImpl.Destroy; begin FParameters.Free; inherited Destroy; end; function TLambdaExpressionNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; begin Result := Visitor.VisitLambdaExpression(Self); end; function TLambdaExpressionNodeImpl.GetBody: IAstNode; begin Result := FBody; end; function TLambdaExpressionNodeImpl.GetParameters: TList; begin Result := FParameters; end; { TFunctionCallNodeImpl } constructor TFunctionCallNodeImpl.Create(ACallee: IExpressionNode; AArguments: TList); begin inherited Create; FCallee := ACallee; FArguments := AArguments; end; destructor TFunctionCallNodeImpl.Destroy; begin FArguments.Free; inherited Destroy; end; function TFunctionCallNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; begin Result := Visitor.VisitFunctionCall(Self); end; function TFunctionCallNodeImpl.GetArguments: TList; begin Result := FArguments; end; function TFunctionCallNodeImpl.GetCallee: IExpressionNode; begin Result := FCallee; end; { TBlockStatementNodeImpl } constructor TBlockStatementNodeImpl.Create(AStatements: TList); begin inherited Create; FStatements := AStatements; end; destructor TBlockStatementNodeImpl.Destroy; begin FStatements.Free; inherited Destroy; end; function TBlockStatementNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; begin Result := Visitor.VisitBlockStatement(Self); end; function TBlockStatementNodeImpl.GetStatements: TList; begin Result := FStatements; end; { TVariableDeclarationStatementNodeImpl } constructor TVariableDeclarationStatementNodeImpl.Create(AIdentifier: IIdentifierNode; AInitializer: IExpressionNode); begin inherited Create; FIdentifier := AIdentifier; FInitializer := AInitializer; end; function TVariableDeclarationStatementNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; begin Result := Visitor.VisitVariableDeclarationStatement(Self); end; function TVariableDeclarationStatementNodeImpl.GetIdentifier: IIdentifierNode; begin Result := FIdentifier; end; function TVariableDeclarationStatementNodeImpl.GetInitializer: IExpressionNode; begin Result := FInitializer; end; { TExpressionStatementNodeImpl } constructor TExpressionStatementNodeImpl.Create(AExpression: IExpressionNode); begin inherited Create; FExpression := AExpression; end; function TExpressionStatementNodeImpl.Accept(const Visitor: IAstVisitor): IDataValue; begin Result := Visitor.VisitExpressionStatement(Self); end; function TExpressionStatementNodeImpl.GetExpression: IExpressionNode; begin Result := FExpression; end; { TExecutionScope } constructor TExecutionScope.Create(AParent: TExecutionScope = nil); begin inherited Create; FParent := AParent; FVariables := TDictionary.Create; end; destructor TExecutionScope.Destroy; begin FVariables.Free; inherited Destroy; end; function TExecutionScope.FindValue(const Name: string; out Value: IDataValue): Boolean; begin Result := FVariables.TryGetValue(Name, Value); if not Result and Assigned(FParent) then begin Result := FParent.FindValue(Name, Value); end; end; procedure TExecutionScope.SetValue(const Name: string; const Value: IDataValue); begin // This defines a variable in the current scope. It can shadow a parent variable. FVariables.AddOrSetValue(Name, Value); end; { TEvaluatorVisitor } constructor TEvaluatorVisitor.Create(AScope: TExecutionScope); begin inherited Create; Assert(Assigned(AScope)); FScope := AScope; end; function TEvaluatorVisitor.IsTruthy(AValue: IDataValue): Boolean; begin // Defines the language's concept of "truthiness". // For now, only ordinals can be conditions. 0 is false, everything else is true. if not Assigned(AValue) then Exit(False); case AValue.DataType.Kind of dkOrdinal: Result := (TDataType.TValue(AValue).AsOrdinal.Value <> 0); else Result := False; end; end; function TEvaluatorVisitor.VisitConstant(const Node: IConstantNode): IDataValue; begin Result := Node.Value; end; function TEvaluatorVisitor.VisitIdentifier(const Node: IIdentifierNode): IDataValue; var val: IDataValue; begin if FScope.FindValue(Node.Name, val) then Result := val else raise EArgumentException.CreateFmt('Identifier not found: "%s"', [Node.Name]); end; function TEvaluatorVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): IDataValue; var leftValue, rightValue: IDataValue; begin leftValue := Node.Left.Accept(Self); rightValue := Node.Right.Accept(Self); if (leftValue.DataType.Kind <> rightValue.DataType.Kind) then raise ENotSupportedException.CreateFmt( 'Binary operations on different types (%s and %s) are not supported', [leftValue.DataType.Name, rightValue.DataType.Name]); case leftValue.DataType.Kind of dkOrdinal: begin var leftOrdinal := TDataType.TValue(leftValue).AsOrdinal; var rightOrdinal := TDataType.TValue(rightValue).AsOrdinal; var resultVal: Int64; case Node.Operator of boAdd: resultVal := leftOrdinal.Value + rightOrdinal.Value; boSubtract: resultVal := leftOrdinal.Value - rightOrdinal.Value; boMultiply: resultVal := leftOrdinal.Value * rightOrdinal.Value; boDivide: resultVal := leftOrdinal.Value div rightOrdinal.Value; else raise ENotSupportedException.Create('Operator not supported for Ordinal type'); end; Result := TDataType.Ordinal.CreateValue(resultVal); end; dkText: begin if (Node.Operator = boAdd) then begin var leftText := TDataType.TValue(leftValue).AsText; var rightText := TDataType.TValue(rightValue).AsText; Result := TDataType.Text.CreateValue(leftText.Value + rightText.Value); end else raise ENotSupportedException.Create('Operator not supported for Text type'); end; else raise ENotSupportedException.CreateFmt('Binary operation not supported for type %s', [leftValue.DataType.Name]); end; end; function TEvaluatorVisitor.VisitUnaryExpression(const Node: IUnaryExpressionNode): IDataValue; var rightValue: IDataValue; ordinalVal: IDataOrdinalValue; begin rightValue := Node.Right.Accept(Self); case Node.Operator of uoNegate: begin if (rightValue.DataType.Kind = dkOrdinal) then begin ordinalVal := TDataType.TValue(rightValue).AsOrdinal; Result := TDataType.Ordinal.CreateValue(-ordinalVal.Value); end else raise ENotSupportedException.CreateFmt('Unary "-" not supported for type %s', [rightValue.DataType.Name]); end; uoNot: raise ENotImplemented.Create('Unary "not" operator is not yet implemented'); else raise ENotSupportedException.Create('Unary operator not supported'); end; end; function TEvaluatorVisitor.VisitIfExpression(const Node: IIfExpressionNode): IDataValue; var conditionValue: IDataValue; begin conditionValue := Node.Condition.Accept(Self); if IsTruthy(conditionValue) then Result := Node.ThenBranch.Accept(Self) else Result := Node.ElseBranch.Accept(Self); end; function TEvaluatorVisitor.VisitLambdaExpression(const Node: ILambdaExpressionNode): IDataValue; var lambdaBody: IAstNode; lambdaParams: TList; closureScope: TExecutionScope; methodType: IDataMethodType; begin closureScope := FScope; lambdaBody := Node.Body; lambdaParams := Node.Parameters; methodType := TDataType.MethodOf(TDataType.Ordinal, TDataType.Ordinal); Result := methodType.CreateValue( function(const AValue: IDataValue): IDataValue var callScope: TExecutionScope; innerVisitor: IAstVisitor; begin callScope := TExecutionScope.Create(closureScope); try if (lambdaParams.Count <> 1) then raise EArgumentException.Create('This simple implementation only supports single-parameter lambdas.'); callScope.SetValue(lambdaParams[0].Name, AValue); innerVisitor := TEvaluatorVisitor.Create(callScope); Result := lambdaBody.Accept(innerVisitor); finally callScope.Free; end; end ); end; function TEvaluatorVisitor.VisitFunctionCall(const Node: IFunctionCallNode): IDataValue; var calleeValue, argValue: IDataValue; methodProc: TDataMethodProc; arguments: TList; begin calleeValue := Node.Callee.Accept(Self); if (calleeValue.DataType.Kind <> dkMethod) then raise EArgumentException.Create('Expression is not callable.'); arguments := Node.Arguments; if (arguments.Count <> 1) then raise EArgumentException.Create('This simple implementation only supports single-argument calls.'); argValue := arguments[0].Accept(Self); methodProc := TDataType.TValue(calleeValue).AsMethod.Value; Result := methodProc(argValue); end; // --- Statement Visitor Implementations --- function TEvaluatorVisitor.VisitBlockStatement(const Node: IBlockStatementNode): IDataValue; var statement: IStatementNode; begin // Execute all statements in the block sequentially. for statement in Node.Statements do begin statement.Accept(Self); // The result is ignored. end; Result := TDataType.Void.Value; end; function TEvaluatorVisitor.VisitVariableDeclarationStatement(const Node: IVariableDeclarationStatementNode): IDataValue; var varName: string; initValue: IDataValue; begin varName := Node.Identifier.Name; // Evaluate the initializer expression, if it exists. if Assigned(Node.Initializer) then initValue := Node.Initializer.Accept(Self) else initValue := TDataType.Void.Value; // Default value if no initializer is provided. // Define the variable in the current scope. FScope.SetValue(varName, initValue); Result := TDataType.Void.Value; end; function TEvaluatorVisitor.VisitExpressionStatement(const Node: IExpressionStatementNode): IDataValue; begin // Evaluate the expression for its side-effects and discard the result. Node.Expression.Accept(Self); Result := TDataType.Void.Value; end; end.