diff --git a/src/Calculator.cpp b/src/Calculator.cpp deleted file mode 100644 index 3968740..0000000 --- a/src/Calculator.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Calculator.cpp - -#include "Calculator.h" - -namespace sci_calc { - -Calculator::Calculator() : parser_(), evaluator_() { -} - -double Calculator::calculate(const std::string &expression) { - if (expression.empty()) { - throw InvalidInputException("Empty expression"); - } - - std::vector postfixTokens = parser_.parseToPostfix(expression); - return evaluator_.evaluate(postfixTokens); -} - -} // namespace sci_calc - - diff --git a/src/Calculator.h b/src/Calculator.h deleted file mode 100644 index a5c652e..0000000 --- a/src/Calculator.h +++ /dev/null @@ -1,27 +0,0 @@ -// Calculator.h -// Main calculator class that coordinates parsing and evaluation - -#pragma once - -#include "ExpressionParser.h" -#include "ExpressionEvaluator.h" -#include "Exceptions.h" - -#include - -namespace sci_calc { - -class Calculator { -public: - Calculator(); - - double calculate(const std::string &expression); - -private: - ExpressionParser parser_; - ExpressionEvaluator evaluator_; -}; - -} // namespace sci_calc - - diff --git a/src/Exceptions.h b/src/Exceptions.h deleted file mode 100644 index 4775d7c..0000000 --- a/src/Exceptions.h +++ /dev/null @@ -1,31 +0,0 @@ -// Exceptions.h -// Scientific Calculator custom exception hierarchy - -#pragma once - -#include -#include - -namespace sci_calc { - -class CalculatorException : public std::runtime_error { -public: - explicit CalculatorException(const std::string &message) - : std::runtime_error(message) {} -}; - -class InvalidInputException : public CalculatorException { -public: - explicit InvalidInputException(const std::string &message) - : CalculatorException(message) {} -}; - -class MathDomainException : public CalculatorException { -public: - explicit MathDomainException(const std::string &message) - : CalculatorException(message) {} -}; - -} // namespace sci_calc - - diff --git a/src/ExpressionEvaluator.cpp b/src/ExpressionEvaluator.cpp deleted file mode 100644 index 7c378e1..0000000 --- a/src/ExpressionEvaluator.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// ExpressionEvaluator.cpp - -#include "ExpressionEvaluator.h" - -#include - -namespace sci_calc { - -double ExpressionEvaluator::evaluate(const std::vector &postfixTokens) const { - std::stack stack; - - for (const auto &token : postfixTokens) { - switch (token.type) { - case TokenType::Number: - case TokenType::Constant: - stack.push(token.value); - break; - - case TokenType::Operator: { - if (stack.size() < 1) { - throw InvalidInputException("Insufficient operands for operator: " + token.text); - } - - if (token.text == "neg" || token.text == "pos") { - double a = stack.top(); - stack.pop(); - stack.push(applyUnaryOperator(token.text, a)); - } else { - if (stack.size() < 2) { - throw InvalidInputException("Insufficient operands for binary operator: " + token.text); - } - double b = stack.top(); - stack.pop(); - double a = stack.top(); - stack.pop(); - stack.push(applyOperator(token.text, a, b)); - } - break; - } - - case TokenType::Function: { - if (stack.empty()) { - throw InvalidInputException("Insufficient operands for function: " + token.text); - } - double arg = stack.top(); - stack.pop(); - stack.push(applyFunction(token.text, arg)); - break; - } - - default: - throw InvalidInputException("Unexpected token type in postfix expression"); - } - } - - if (stack.size() != 1) { - throw InvalidInputException("Invalid expression: stack contains " + - std::to_string(stack.size()) + " values"); - } - - return stack.top(); -} - -double ExpressionEvaluator::applyOperator(const std::string &op, double a, double b) const { - if (op == "+") { - return a + b; - } else if (op == "-") { - return a - b; - } else if (op == "*") { - return a * b; - } else if (op == "/") { - if (std::abs(b) < 1e-10) { - throw MathDomainException("Division by zero"); - } - return a / b; - } else if (op == "%") { - if (std::abs(b) < 1e-10) { - throw MathDomainException("Modulo by zero"); - } - return std::fmod(a, b); - } else if (op == "^") { - if (a < 0 && std::fmod(b, 1.0) != 0.0) { - throw MathDomainException("Cannot raise negative number to non-integer power"); - } - double result = std::pow(a, b); - if (std::isnan(result) || std::isinf(result)) { - throw MathDomainException("Power operation resulted in invalid value"); - } - return result; - } else { - throw InvalidInputException("Unknown operator: " + op); - } -} - -double ExpressionEvaluator::applyUnaryOperator(const std::string &op, double a) const { - if (op == "neg") { - return -a; - } else if (op == "pos") { - return a; - } else { - throw InvalidInputException("Unknown unary operator: " + op); - } -} - -double ExpressionEvaluator::applyFunction(const std::string &func, double arg) const { - if (func == "sin") { - return std::sin(arg); - } else if (func == "cos") { - return std::cos(arg); - } else if (func == "tan") { - double result = std::tan(arg); - if (std::isinf(result) || std::isnan(result)) { - throw MathDomainException("Tangent is undefined for the given angle"); - } - return result; - } else if (func == "asin") { - if (arg < -1.0 || arg > 1.0) { - throw MathDomainException("Arcsin argument must be in range [-1, 1]"); - } - return std::asin(arg); - } else if (func == "acos") { - if (arg < -1.0 || arg > 1.0) { - throw MathDomainException("Arccos argument must be in range [-1, 1]"); - } - return std::acos(arg); - } else if (func == "atan") { - return std::atan(arg); - } else if (func == "log") { - if (arg <= 0.0) { - throw MathDomainException("Logarithm argument must be positive"); - } - return std::log10(arg); - } else if (func == "ln") { - if (arg <= 0.0) { - throw MathDomainException("Natural logarithm argument must be positive"); - } - return std::log(arg); - } else if (func == "sqrt") { - if (arg < 0.0) { - throw MathDomainException("Square root of negative number is not a real number"); - } - return std::sqrt(arg); - } else if (func == "exp") { - double result = std::exp(arg); - if (std::isinf(result)) { - throw MathDomainException("Exponential overflow"); - } - return result; - } else if (func == "abs") { - return std::abs(arg); - } else { - throw InvalidInputException("Unknown function: " + func); - } -} - -} // namespace sci_calc - - diff --git a/src/ExpressionEvaluator.h b/src/ExpressionEvaluator.h deleted file mode 100644 index ea8ea6b..0000000 --- a/src/ExpressionEvaluator.h +++ /dev/null @@ -1,27 +0,0 @@ -// ExpressionEvaluator.h -// Evaluates postfix expression tokens and performs mathematical operations - -#pragma once - -#include "ExpressionParser.h" -#include "Exceptions.h" - -#include -#include -#include - -namespace sci_calc { - -class ExpressionEvaluator { -public: - double evaluate(const std::vector &postfixTokens) const; - -private: - double applyOperator(const std::string &op, double a, double b) const; - double applyUnaryOperator(const std::string &op, double a) const; - double applyFunction(const std::string &func, double arg) const; -}; - -} // namespace sci_calc - - diff --git a/src/ExpressionParser.cpp b/src/ExpressionParser.cpp deleted file mode 100644 index 134331b..0000000 --- a/src/ExpressionParser.cpp +++ /dev/null @@ -1,270 +0,0 @@ -// ExpressionParser.cpp - -#include "ExpressionParser.h" - -#include -#include -#include - -namespace sci_calc { - -namespace { - -std::string toLower(std::string text) { - std::transform(text.begin(), text.end(), text.begin(), [](unsigned char ch) { - return static_cast(std::tolower(ch)); - }); - return text; -} - -} // namespace - -ExpressionParser::ExpressionParser() { - operators_.emplace("+", OperatorInfo{2, false, 2}); - operators_.emplace("-", OperatorInfo{2, false, 2}); - operators_.emplace("*", OperatorInfo{3, false, 2}); - operators_.emplace("/", OperatorInfo{3, false, 2}); - operators_.emplace("%", OperatorInfo{3, false, 2}); - operators_.emplace("^", OperatorInfo{4, true, 2}); - operators_.emplace("neg", OperatorInfo{5, true, 1}); - - constants_.emplace("pi", 3.14159265358979323846); - constants_.emplace("e", 2.71828182845904523536); -} - -std::vector ExpressionParser::parseToPostfix(const std::string &expression) const { - std::vector output; - std::stack stack; - - std::size_t i = 0; - while (i < expression.size()) { - char ch = expression[i]; - - if (std::isspace(static_cast(ch))) { - ++i; - continue; - } - - if (std::isdigit(static_cast(ch)) || - (ch == '.' && i + 1 < expression.size() && - std::isdigit(static_cast(expression[i + 1])))) { - output.push_back(parseNumber(expression, i)); - continue; - } - - if (isIdentifierChar(ch)) { - Token identifier = parseIdentifier(expression, i); - if (identifier.type == TokenType::Constant) { - output.push_back(identifier); - } else { - stack.push(identifier); - } - continue; - } - - if (ch == ',') { - // Function argument separator is unsupported (all functions are unary) - throw InvalidInputException("Unexpected ',' in expression: multiple arguments not supported"); - } - - if (ch == '(') { - stack.push(Token{TokenType::OpenParen, "(", 0.0}); - ++i; - continue; - } - - if (ch == ')') { - ++i; - bool foundOpenParen = false; - while (!stack.empty()) { - Token top = stack.top(); - stack.pop(); - if (top.type == TokenType::OpenParen) { - foundOpenParen = true; - break; - } - output.push_back(top); - } - - if (!foundOpenParen) { - throw InvalidInputException("Unmatched ')' in expression"); - } - - if (!stack.empty() && stack.top().type == TokenType::Function) { - output.push_back(stack.top()); - stack.pop(); - } - continue; - } - - // Operators - Token opToken = parseOperator(expression, i, output); - - while (!stack.empty()) { - const Token &top = stack.top(); - if (top.type != TokenType::Operator) { - break; - } - - const auto ¤tInfo = operators_.at(opToken.text); - const auto &topInfo = operators_.at(top.text); - - bool shouldPop = (!currentInfo.rightAssociative && currentInfo.precedence <= topInfo.precedence) || - (currentInfo.rightAssociative && currentInfo.precedence < topInfo.precedence); - - if (!shouldPop) { - break; - } - - output.push_back(top); - stack.pop(); - } - - stack.push(opToken); - } - - while (!stack.empty()) { - Token top = stack.top(); - stack.pop(); - if (top.type == TokenType::OpenParen || top.type == TokenType::CloseParen) { - throw InvalidInputException("Unmatched '(' in expression"); - } - output.push_back(top); - } - - return output; -} - -bool ExpressionParser::isIdentifierChar(char ch) { - return std::isalpha(static_cast(ch)) || ch == '_'; -} - -Token ExpressionParser::parseNumber(const std::string &expression, std::size_t &index) const { - std::size_t start = index; - bool hasDecimalPoint = false; - - while (index < expression.size()) { - char ch = expression[index]; - if (std::isdigit(static_cast(ch))) { - ++index; - continue; - } - if (ch == '.' && !hasDecimalPoint) { - hasDecimalPoint = true; - ++index; - continue; - } - if ((ch == 'e' || ch == 'E') && index + 1 < expression.size()) { - std::size_t expIndex = index + 1; - if (expression[expIndex] == '+' || expression[expIndex] == '-') { - ++expIndex; - } - if (expIndex >= expression.size() || - !std::isdigit(static_cast(expression[expIndex]))) { - throw InvalidInputException("Malformed exponent in number literal"); - } - index = expIndex + 1; - while (index < expression.size() && - std::isdigit(static_cast(expression[index]))) { - ++index; - } - break; - } - break; - } - - const std::string literal = expression.substr(start, index - start); - try { - double value = std::stod(literal); - return Token{TokenType::Number, literal, value}; - } catch (const std::exception &) { - throw InvalidInputException("Unable to parse number literal: " + literal); - } -} - -Token ExpressionParser::parseIdentifier(const std::string &expression, std::size_t &index) const { - std::size_t start = index; - while (index < expression.size() && - (isIdentifierChar(expression[index]) || - std::isdigit(static_cast(expression[index])))) { - ++index; - } - - std::string identifier = expression.substr(start, index - start); - std::string lower = toLower(identifier); - - auto constantIt = constants_.find(lower); - if (constantIt != constants_.end()) { - return Token{TokenType::Constant, lower, constantIt->second}; - } - - static const std::unordered_map functions = { - {"sin", 1}, {"cos", 1}, {"tan", 1}, {"asin", 1}, {"acos", 1}, {"atan", 1}, - {"log", 1}, {"ln", 1}, {"sqrt", 1}, {"exp", 1}, {"abs", 1}}; - - if (functions.find(lower) != functions.end()) { - return Token{TokenType::Function, lower, 0.0}; - } - - throw InvalidInputException("Unknown identifier: " + identifier); -} - -bool ExpressionParser::isUnaryOperator(const std::string &expression, std::size_t index, - const std::vector &output) const { - if (index == 0) { - return true; - } - - std::size_t prevIndex = index; - while (prevIndex > 0) { - char prevChar = expression[prevIndex - 1]; - if (std::isspace(static_cast(prevChar))) { - --prevIndex; - continue; - } - break; - } - - if (prevIndex == 0) { - return true; - } - - char prevChar = expression[prevIndex - 1]; - - if (prevChar == '(' || prevChar == ',' || operators_.count(std::string(1, prevChar)) > 0) { - return true; - } - - if (!output.empty()) { - Token lastToken = output.back(); - if (lastToken.type == TokenType::Operator) { - return true; - } - } - - return false; -} - -Token ExpressionParser::parseOperator(const std::string &expression, std::size_t &index, - const std::vector &output) const { - char ch = expression[index]; - std::string op(1, ch); - - if (ch == '+' || ch == '-') { - if (isUnaryOperator(expression, index, output)) { - ++index; - return Token{TokenType::Operator, ch == '-' ? "neg" : "+", 0.0}; - } - } - - if (operators_.find(op) == operators_.end()) { - throw InvalidInputException(std::string("Unsupported operator: ") + ch); - } - - ++index; - return Token{TokenType::Operator, op, 0.0}; -} - -} // namespace sci_calc - - diff --git a/src/ExpressionParser.h b/src/ExpressionParser.h deleted file mode 100644 index bf253ca..0000000 --- a/src/ExpressionParser.h +++ /dev/null @@ -1,58 +0,0 @@ -// ExpressionParser.h -// Responsible for tokenizing and converting infix expressions to postfix notation - -#pragma once - -#include "Exceptions.h" - -#include -#include -#include -#include - -namespace sci_calc { - -enum class TokenType { - Number, - Operator, - Function, - OpenParen, - CloseParen, - Constant -}; - -struct Token { - TokenType type; - std::string text; - double value = 0.0; -}; - -struct OperatorInfo { - int precedence; - bool rightAssociative; - int arity; -}; - -class ExpressionParser { -public: - ExpressionParser(); - - std::vector parseToPostfix(const std::string &expression) const; - -private: - std::unordered_map operators_; - std::unordered_map constants_; - - static bool isIdentifierChar(char ch); - Token parseNumber(const std::string &expression, std::size_t &index) const; - Token parseIdentifier(const std::string &expression, std::size_t &index) const; - Token parseOperator(const std::string &expression, std::size_t &index, - const std::vector &output) const; - - bool isUnaryOperator(const std::string &expression, std::size_t index, - const std::vector &output) const; -}; - -} // namespace sci_calc - - diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 67a9dcf..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// main.cpp -// Command-line interface for the Scientific Calculator - -#include "Calculator.h" - -#include -#include -#include - -void printUsage(const char *programName) { - std::cout << "Scientific Calculator\n"; - std::cout << "Usage: " << programName << " [expression]\n\n"; - std::cout << "If no expression is provided, the calculator runs in interactive mode.\n\n"; - std::cout << "Supported operations:\n"; - std::cout << " Basic: +, -, *, /, %, ^ (power)\n"; - std::cout << " Functions: sin, cos, tan, asin, acos, atan\n"; - std::cout << " log (base 10), ln (natural), sqrt, exp, abs\n"; - std::cout << " Constants: pi, e\n"; - std::cout << "\nExamples:\n"; - std::cout << " " << programName << " \"2 + 3 * 4\"\n"; - std::cout << " " << programName << " \"sin(pi/2)\"\n"; - std::cout << " " << programName << " \"sqrt(16) + log(100)\"\n"; - std::cout << " " << programName << " \"2^3 + 5\"\n"; -} - -int main(int argc, char *argv[]) { - sci_calc::Calculator calculator; - - // If expression provided as command-line argument - if (argc > 1) { - std::string expression = argv[1]; - if (expression == "-h" || expression == "--help") { - printUsage(argv[0]); - return 0; - } - - try { - double result = calculator.calculate(expression); - std::cout << std::fixed << std::setprecision(10) << result << std::endl; - return 0; - } catch (const sci_calc::CalculatorException &e) { - std::cerr << "Error: " << e.what() << std::endl; - return 1; - } catch (const std::exception &e) { - std::cerr << "Unexpected error: " << e.what() << std::endl; - return 1; - } - } - - // Interactive mode - std::cout << "Scientific Calculator - Interactive Mode\n"; - std::cout << "Enter expressions to evaluate. Type 'quit' or 'exit' to exit.\n"; - std::cout << "Type 'help' for usage information.\n\n"; - - std::string line; - while (true) { - std::cout << "> "; - if (!std::getline(std::cin, line)) { - break; - } - - // Trim whitespace - line.erase(0, line.find_first_not_of(" \t\n\r")); - line.erase(line.find_last_not_of(" \t\n\r") + 1); - - if (line.empty()) { - continue; - } - - if (line == "quit" || line == "exit" || line == "q") { - std::cout << "Goodbye!\n"; - break; - } - - if (line == "help" || line == "h") { - printUsage(argv[0]); - continue; - } - - try { - double result = calculator.calculate(line); - std::cout << std::fixed << std::setprecision(10) << result << std::endl; - } catch (const sci_calc::CalculatorException &e) { - std::cerr << "Error: " << e.what() << std::endl; - } catch (const std::exception &e) { - std::cerr << "Unexpected error: " << e.what() << std::endl; - } - } - - return 0; -} - -