mirror of
https://github.com/arkorty/ExprEvalplusplus.git
synced 2026-03-18 00:57:18 +00:00
Refactor AST Node Hierarchy and Enable Test Execution
Commit details: - Refactor the Abstract Syntax Tree (AST) node hierarchy in `main.cxx` for improved memory management and ownership semantics. - Introduce smart pointers (`std::unique_ptr`) for managing ownership of AST nodes, eliminating manual memory management. - Add getter functions to access operands in Unary and Binary nodes. - Implemente functions to release ownership of operands in Unary and Binary nodes. - Replace manual deletion of operands in the destructor with default virtual destructors for Unary and Binary nodes. - Add a conditional compilation block (`#ifdef ENABLE_TESTS`) to enable testing functionality. - Create a test expression in the `runTest` function to evaluate a complex mathematical expression. - Enable test execution using the `--run-test` command-line option. - Provide a help message for the main program, outlining available options.
This commit is contained in:
110
main.cxx
110
main.cxx
@@ -1,8 +1,16 @@
|
|||||||
|
// Include necessary C++ standard library headers.
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
// Conditional compilation based on ENABLE_TESTS macro.
|
||||||
|
#ifdef ENABLE_TESTS
|
||||||
|
#include <cassert>
|
||||||
|
#endif
|
||||||
|
|
||||||
// Abstract Syntax Tree (AST) Node base class
|
// Abstract Syntax Tree (AST) Node base class
|
||||||
class ASTNode {
|
class ASTNode {
|
||||||
public:
|
public:
|
||||||
@@ -24,7 +32,7 @@ class ASTNode {
|
|||||||
// Virtual functions for evaluation and type retrieval.
|
// Virtual functions for evaluation and type retrieval.
|
||||||
virtual double evaluate() const = 0;
|
virtual double evaluate() const = 0;
|
||||||
virtual ASTNode::Type getType() const = 0;
|
virtual ASTNode::Type getType() const = 0;
|
||||||
virtual ~ASTNode() {}
|
virtual ~ASTNode() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constant Node class
|
// Constant Node class
|
||||||
@@ -41,6 +49,9 @@ class Constant : public ASTNode {
|
|||||||
|
|
||||||
// Implementation of evaluate for Constant node.
|
// Implementation of evaluate for Constant node.
|
||||||
double evaluate() const override { return value; }
|
double evaluate() const override { return value; }
|
||||||
|
|
||||||
|
// Getter function for the constant value.
|
||||||
|
double getValue() const { return value; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Identifier Node class
|
// Identifier Node class
|
||||||
@@ -76,17 +87,23 @@ class Identifier : public ASTNode {
|
|||||||
// Unary Node base class
|
// Unary Node base class
|
||||||
class Unary : public ASTNode {
|
class Unary : public ASTNode {
|
||||||
protected:
|
protected:
|
||||||
const ASTNode *operand;
|
std::unique_ptr<const ASTNode> operand;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor for Unary node.
|
// Constructor for Unary node.
|
||||||
explicit Unary(const ASTNode *operand) : operand(operand) {}
|
explicit Unary(std::unique_ptr<const ASTNode> operand) : operand(std::move(operand)) {}
|
||||||
|
|
||||||
// Implementation of getType for Unary node.
|
// Implementation of getType for Unary node.
|
||||||
ASTNode::Type getType() const override { return ASTNode::Type::Unary; }
|
ASTNode::Type getType() const = 0;
|
||||||
|
|
||||||
|
// Getter function to access the operand.
|
||||||
|
const ASTNode &getInput() { return *operand; }
|
||||||
|
|
||||||
|
// Function to release ownership of the operand.
|
||||||
|
std::unique_ptr<const ASTNode> releaseInput() { return std::move(operand); }
|
||||||
|
|
||||||
// Virtual destructor for Unary node.
|
// Virtual destructor for Unary node.
|
||||||
virtual ~Unary() { delete operand; }
|
virtual ~Unary() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
// UnaryPlus Node class
|
// UnaryPlus Node class
|
||||||
@@ -116,21 +133,27 @@ class UnaryMinus : public Unary {
|
|||||||
// Binary Node base class
|
// Binary Node base class
|
||||||
class Binary : public ASTNode {
|
class Binary : public ASTNode {
|
||||||
protected:
|
protected:
|
||||||
const ASTNode *left;
|
std::unique_ptr<const ASTNode> left;
|
||||||
const ASTNode *right;
|
std::unique_ptr<const ASTNode> right;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor for Binary node.
|
// Constructor for Binary node.
|
||||||
Binary(const ASTNode *left, const ASTNode *right) : left(left), right(right) {}
|
Binary(std::unique_ptr<const ASTNode> left, std::unique_ptr<const ASTNode> right)
|
||||||
|
: left(std::move(left)), right(std::move(right)) {}
|
||||||
|
|
||||||
|
// Getter functions to access left and right operands.
|
||||||
|
const ASTNode &getLeft() { return *left; }
|
||||||
|
const ASTNode &getRight() { return *right; }
|
||||||
|
|
||||||
|
// Functions to release ownership of left and right operands.
|
||||||
|
std::unique_ptr<const ASTNode> releaseLeft() { return std::move(left); }
|
||||||
|
std::unique_ptr<const ASTNode> releaseRight() { return std::move(right); }
|
||||||
|
|
||||||
// Implementation of getType for Binary node.
|
// Implementation of getType for Binary node.
|
||||||
ASTNode::Type getType() const override { return ASTNode::Type::Binary; }
|
ASTNode::Type getType() const override { return ASTNode::Type::Binary; }
|
||||||
|
|
||||||
// Virtual destructor for Binary node.
|
// Virtual destructor for Binary node.
|
||||||
virtual ~Binary() {
|
virtual ~Binary() = default;
|
||||||
delete left;
|
|
||||||
delete right;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add Node class
|
// Add Node class
|
||||||
@@ -202,23 +225,62 @@ class Power : public Binary {
|
|||||||
// Static initialization of variableTable in Identifier class.
|
// Static initialization of variableTable in Identifier class.
|
||||||
std::unordered_map<std::string, double> Identifier::variableTable;
|
std::unordered_map<std::string, double> Identifier::variableTable;
|
||||||
|
|
||||||
// Main function
|
#ifdef ENABLE_TESTS
|
||||||
int main() {
|
// Function to run a test expression evaluation.
|
||||||
// Setting variables in the variableTable.
|
void runTest() {
|
||||||
Identifier::setVariable("Num1", 3.0);
|
// Test with a complex expression: (2 * (a + b)) / (c - 1) ^ (d + 1)
|
||||||
Identifier::setVariable("Num2", 7.0);
|
Identifier::setVariable("a", 3.0);
|
||||||
|
Identifier::setVariable("b", 1.0);
|
||||||
|
Identifier::setVariable("c", 5.0);
|
||||||
|
Identifier::setVariable("d", 2.0);
|
||||||
|
|
||||||
// Creating an expression tree.
|
std::unique_ptr<const ASTNode> variableA = std::make_unique<Identifier>("a");
|
||||||
ASTNode *expression = new Add(new UnaryMinus(new Identifier("Num1")),
|
std::unique_ptr<const ASTNode> variableB = std::make_unique<Identifier>("b");
|
||||||
new Multiply(new Constant(2), new Subtract(new Constant(4), new Identifier("Num2"))));
|
std::unique_ptr<const ASTNode> variableC = std::make_unique<Identifier>("c");
|
||||||
|
std::unique_ptr<const ASTNode> variableD = std::make_unique<Identifier>("d");
|
||||||
|
|
||||||
// Evaluating the expression and printing the result.
|
std::unique_ptr<const ASTNode> expression = std::make_unique<Divide>(
|
||||||
|
std::make_unique<Multiply>(std::make_unique<Constant>(2.0),
|
||||||
|
std::make_unique<Add>(std::move(variableA), std::move(variableB))),
|
||||||
|
std::make_unique<Power>(std::make_unique<Subtract>(std::move(variableC), std::make_unique<Constant>(1.0)),
|
||||||
|
std::make_unique<Add>(std::move(variableD), std::make_unique<Constant>(1.0))));
|
||||||
|
|
||||||
|
// Evaluate the expression
|
||||||
double result = expression->evaluate();
|
double result = expression->evaluate();
|
||||||
std::cout << "Result: " << result << std::endl;
|
|
||||||
|
|
||||||
// Deleting the expression and clearing variables.
|
// Expected result: (2 * (3 + 1)) / (5 - 1) ^ (2 + 1) = 8 / 4 ^ 3 = 8 / 64 =
|
||||||
delete expression;
|
// 0.125
|
||||||
|
assert(result == 0.125);
|
||||||
|
|
||||||
|
std::cout << "Test passed successfully. Result: " << result << std::endl;
|
||||||
|
|
||||||
|
// Clear variables for the next test
|
||||||
Identifier::clearVariables();
|
Identifier::clearVariables();
|
||||||
|
}
|
||||||
|
#endif // ENABLE_TESTS
|
||||||
|
|
||||||
|
// Function to print the help message.
|
||||||
|
void printHelpMessage(const char *programName) {
|
||||||
|
std::cout << "Usage: " << programName << " [--run-test]\n"
|
||||||
|
<< "Options:\n"
|
||||||
|
<< " --run-test Run the test for the expression evaluation code.\n"
|
||||||
|
<< " This option should be used without any additional "
|
||||||
|
"arguments.\n"
|
||||||
|
<< " Example: " << programName << " --run-test\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main function
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// Check if the "--run-test" argument is provided
|
||||||
|
if (argc == 2 && std::strcmp(argv[1], "--run-test") == 0) {
|
||||||
|
#ifdef ENABLE_TESTS
|
||||||
|
// Run the test if ENABLE_TESTS is defined
|
||||||
|
runTest();
|
||||||
|
#endif // ENABLE_TESTS
|
||||||
|
} else {
|
||||||
|
// Print help message if no valid arguments are provided
|
||||||
|
printHelpMessage(argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user