Initial commit: Scientific Calculator with build + CLI
This commit is contained in:
parent
3cb779d873
commit
5dabd51653
10 changed files with 738 additions and 0 deletions
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Build artifacts
|
||||
obj/
|
||||
calculator
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Editor
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
|
||||
41
Makefile
Normal file
41
Makefile
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Makefile for Scientific Calculator
|
||||
|
||||
CXX = g++
|
||||
CXXFLAGS = -std=c++17 -Wall -Wextra -O2
|
||||
TARGET = calculator
|
||||
SRCDIR = src
|
||||
OBJDIR = obj
|
||||
|
||||
# Source files
|
||||
SOURCES = $(wildcard $(SRCDIR)/*.cpp)
|
||||
OBJECTS = $(SOURCES:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
|
||||
|
||||
# Default target
|
||||
all: $(TARGET)
|
||||
|
||||
# Create object directory if it doesn't exist
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
# Link object files to create executable
|
||||
$(TARGET): $(OBJECTS) | $(OBJDIR)
|
||||
$(CXX) $(OBJECTS) -o $(TARGET) $(LDFLAGS)
|
||||
@echo "Build complete: $(TARGET)"
|
||||
|
||||
# Compile source files to object files
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp | $(OBJDIR)
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
# Clean build artifacts
|
||||
clean:
|
||||
rm -rf $(OBJDIR) $(TARGET)
|
||||
@echo "Clean complete"
|
||||
|
||||
# Run the calculator in interactive mode
|
||||
run: $(TARGET)
|
||||
./$(TARGET)
|
||||
|
||||
# Phony targets
|
||||
.PHONY: all clean run
|
||||
|
||||
|
||||
21
src/Calculator.cpp
Normal file
21
src/Calculator.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// 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<Token> postfixTokens = parser_.parseToPostfix(expression);
|
||||
return evaluator_.evaluate(postfixTokens);
|
||||
}
|
||||
|
||||
} // namespace sci_calc
|
||||
|
||||
|
||||
27
src/Calculator.h
Normal file
27
src/Calculator.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Calculator.h
|
||||
// Main calculator class that coordinates parsing and evaluation
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ExpressionParser.h"
|
||||
#include "ExpressionEvaluator.h"
|
||||
#include "Exceptions.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace sci_calc {
|
||||
|
||||
class Calculator {
|
||||
public:
|
||||
Calculator();
|
||||
|
||||
double calculate(const std::string &expression);
|
||||
|
||||
private:
|
||||
ExpressionParser parser_;
|
||||
ExpressionEvaluator evaluator_;
|
||||
};
|
||||
|
||||
} // namespace sci_calc
|
||||
|
||||
|
||||
31
src/Exceptions.h
Normal file
31
src/Exceptions.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Exceptions.h
|
||||
// Scientific Calculator custom exception hierarchy
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
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
|
||||
|
||||
|
||||
158
src/ExpressionEvaluator.cpp
Normal file
158
src/ExpressionEvaluator.cpp
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
// ExpressionEvaluator.cpp
|
||||
|
||||
#include "ExpressionEvaluator.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace sci_calc {
|
||||
|
||||
double ExpressionEvaluator::evaluate(const std::vector<Token> &postfixTokens) const {
|
||||
std::stack<double> 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
|
||||
|
||||
|
||||
27
src/ExpressionEvaluator.h
Normal file
27
src/ExpressionEvaluator.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// ExpressionEvaluator.h
|
||||
// Evaluates postfix expression tokens and performs mathematical operations
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ExpressionParser.h"
|
||||
#include "Exceptions.h"
|
||||
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
namespace sci_calc {
|
||||
|
||||
class ExpressionEvaluator {
|
||||
public:
|
||||
double evaluate(const std::vector<Token> &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
|
||||
|
||||
|
||||
270
src/ExpressionParser.cpp
Normal file
270
src/ExpressionParser.cpp
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
// ExpressionParser.cpp
|
||||
|
||||
#include "ExpressionParser.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <stack>
|
||||
|
||||
namespace sci_calc {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string toLower(std::string text) {
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char ch) {
|
||||
return static_cast<char>(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<Token> ExpressionParser::parseToPostfix(const std::string &expression) const {
|
||||
std::vector<Token> output;
|
||||
std::stack<Token> stack;
|
||||
|
||||
std::size_t i = 0;
|
||||
while (i < expression.size()) {
|
||||
char ch = expression[i];
|
||||
|
||||
if (std::isspace(static_cast<unsigned char>(ch))) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::isdigit(static_cast<unsigned char>(ch)) ||
|
||||
(ch == '.' && i + 1 < expression.size() &&
|
||||
std::isdigit(static_cast<unsigned char>(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<unsigned char>(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<unsigned char>(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<unsigned char>(expression[expIndex]))) {
|
||||
throw InvalidInputException("Malformed exponent in number literal");
|
||||
}
|
||||
index = expIndex + 1;
|
||||
while (index < expression.size() &&
|
||||
std::isdigit(static_cast<unsigned char>(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<unsigned char>(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<std::string, int> 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<Token> &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<unsigned char>(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<Token> &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
|
||||
|
||||
|
||||
58
src/ExpressionParser.h
Normal file
58
src/ExpressionParser.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// ExpressionParser.h
|
||||
// Responsible for tokenizing and converting infix expressions to postfix notation
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Exceptions.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
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<Token> parseToPostfix(const std::string &expression) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, OperatorInfo> operators_;
|
||||
std::unordered_map<std::string, double> 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<Token> &output) const;
|
||||
|
||||
bool isUnaryOperator(const std::string &expression, std::size_t index,
|
||||
const std::vector<Token> &output) const;
|
||||
};
|
||||
|
||||
} // namespace sci_calc
|
||||
|
||||
|
||||
93
src/main.cpp
Normal file
93
src/main.cpp
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// main.cpp
|
||||
// Command-line interface for the Scientific Calculator
|
||||
|
||||
#include "Calculator.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Add table
Reference in a new issue