329 lines
8.5 KiB
C
329 lines
8.5 KiB
C
#include "calculator.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
|
|
#define MAX_STACK_SIZE 256
|
|
#define MAX_TOKEN_LENGTH 100
|
|
|
|
typedef struct {
|
|
double data[MAX_STACK_SIZE];
|
|
int top;
|
|
} Stack;
|
|
|
|
typedef struct {
|
|
char data[MAX_STACK_SIZE][MAX_TOKEN_LENGTH];
|
|
int top;
|
|
} StringStack;
|
|
|
|
static void init_stack(Stack *s) {
|
|
s->top = -1;
|
|
}
|
|
|
|
static bool is_stack_empty(Stack *s) {
|
|
return s->top == -1;
|
|
}
|
|
|
|
static bool is_stack_full(Stack *s) {
|
|
return s->top == MAX_STACK_SIZE - 1;
|
|
}
|
|
|
|
static void push(Stack *s, double value) {
|
|
if (!is_stack_full(s)) {
|
|
s->data[++s->top] = value;
|
|
}
|
|
}
|
|
|
|
static double pop(Stack *s) {
|
|
if (!is_stack_empty(s)) {
|
|
return s->data[s->top--];
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
static void init_string_stack(StringStack *s) {
|
|
s->top = -1;
|
|
}
|
|
|
|
static bool is_string_stack_empty(StringStack *s) {
|
|
return s->top == -1;
|
|
}
|
|
|
|
static bool is_string_stack_full(StringStack *s) {
|
|
return s->top == MAX_STACK_SIZE - 1;
|
|
}
|
|
|
|
static void push_string(StringStack *s, const char *str) {
|
|
if (!is_string_stack_full(s) && strlen(str) < MAX_TOKEN_LENGTH) {
|
|
strcpy(s->data[++s->top], str);
|
|
}
|
|
}
|
|
|
|
static char* pop_string(StringStack *s) {
|
|
if (!is_string_stack_empty(s)) {
|
|
return s->data[s->top--];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static char* peek_string(StringStack *s) {
|
|
if (!is_string_stack_empty(s)) {
|
|
return s->data[s->top];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool is_operator(char c) {
|
|
return c == '+' || c == '-' || c == '*' || c == '/' || c == '^';
|
|
}
|
|
|
|
int get_priority(char op) {
|
|
switch (op) {
|
|
case '+':
|
|
case '-':
|
|
return 1;
|
|
case '*':
|
|
case '/':
|
|
return 2;
|
|
case '^':
|
|
return 3;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bool is_function(const char *str, int *length) {
|
|
if (strncmp(str, "sin", 3) == 0) {
|
|
*length = 3;
|
|
return true;
|
|
}
|
|
if (strncmp(str, "cos", 3) == 0) {
|
|
*length = 3;
|
|
return true;
|
|
}
|
|
if (strncmp(str, "sqrt", 4) == 0) {
|
|
*length = 4;
|
|
return true;
|
|
}
|
|
if (strncmp(str, "log", 3) == 0) {
|
|
*length = 3;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
double apply_operator(double a, double b, char op) {
|
|
switch (op) {
|
|
case '+': return a + b;
|
|
case '-': return a - b;
|
|
case '*': return a * b;
|
|
case '/':
|
|
if (b == 0) {
|
|
return NAN;
|
|
}
|
|
return a / b;
|
|
case '^': return pow(a, b);
|
|
default: return NAN;
|
|
}
|
|
}
|
|
|
|
double apply_function(const char *func, double value, bool *error) {
|
|
if (strcmp(func, "sin") == 0) {
|
|
return sin(value);
|
|
}
|
|
if (strcmp(func, "cos") == 0) {
|
|
return cos(value);
|
|
}
|
|
if (strcmp(func, "sqrt") == 0) {
|
|
if (value < 0) {
|
|
*error = true;
|
|
return NAN;
|
|
}
|
|
return sqrt(value);
|
|
}
|
|
if (strcmp(func, "log") == 0) {
|
|
if (value <= 0) {
|
|
*error = true;
|
|
return NAN;
|
|
}
|
|
return log10(value);
|
|
}
|
|
*error = true;
|
|
return NAN;
|
|
}
|
|
|
|
static char** infix_to_postfix(const char *expression, int *token_count, bool *error) {
|
|
StringStack operator_stack;
|
|
init_string_stack(&operator_stack);
|
|
char **output = malloc(MAX_STACK_SIZE * sizeof(char*));
|
|
for (int i = 0; i < MAX_STACK_SIZE; i++) {
|
|
output[i] = malloc(MAX_TOKEN_LENGTH * sizeof(char));
|
|
}
|
|
*token_count = 0;
|
|
*error = false;
|
|
int i = 0;
|
|
int len = strlen(expression);
|
|
while (i < len) {
|
|
if (isspace(expression[i])) {
|
|
i++;
|
|
continue;
|
|
}
|
|
if (isdigit(expression[i]) || expression[i] == '.') {
|
|
int j = 0;
|
|
char num[MAX_TOKEN_LENGTH] = {0};
|
|
while (i < len && (isdigit(expression[i]) || expression[i] == '.')) {
|
|
num[j++] = expression[i++];
|
|
}
|
|
num[j] = '\0';
|
|
strcpy(output[(*token_count)++], num);
|
|
continue;
|
|
}
|
|
int func_len;
|
|
if (is_function(expression + i, &func_len)) {
|
|
char func[10] = {0};
|
|
strncpy(func, expression + i, func_len);
|
|
func[func_len] = '\0';
|
|
i += func_len;
|
|
push_string(&operator_stack, func);
|
|
continue;
|
|
}
|
|
if (is_operator(expression[i])) {
|
|
while (!is_string_stack_empty(&operator_stack)) {
|
|
char *top = peek_string(&operator_stack);
|
|
if (strcmp(top, "(") != 0 &&
|
|
get_priority(top[0]) >= get_priority(expression[i]) &&
|
|
strlen(top) == 1) {
|
|
strcpy(output[(*token_count)++], pop_string(&operator_stack));
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
char op[2] = {expression[i++], '\0'};
|
|
push_string(&operator_stack, op);
|
|
continue;
|
|
}
|
|
if (expression[i] == '(') {
|
|
char bracket[2] = {expression[i++], '\0'};
|
|
push_string(&operator_stack, bracket);
|
|
continue;
|
|
}
|
|
if (expression[i] == ')') {
|
|
i++;
|
|
while (!is_string_stack_empty(&operator_stack) &&
|
|
strcmp(peek_string(&operator_stack), "(") != 0) {
|
|
strcpy(output[(*token_count)++], pop_string(&operator_stack));
|
|
}
|
|
if (is_string_stack_empty(&operator_stack)) {
|
|
*error = true;
|
|
break;
|
|
}
|
|
pop_string(&operator_stack);
|
|
if (!is_string_stack_empty(&operator_stack)) {
|
|
char *top = peek_string(&operator_stack);
|
|
if (is_function(top, &func_len)) {
|
|
strcpy(output[(*token_count)++], pop_string(&operator_stack));
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
*error = true;
|
|
break;
|
|
}
|
|
while (!is_string_stack_empty(&operator_stack)) {
|
|
char *top = peek_string(&operator_stack);
|
|
if (strcmp(top, "(") == 0) {
|
|
*error = true;
|
|
break;
|
|
}
|
|
strcpy(output[(*token_count)++], pop_string(&operator_stack));
|
|
}
|
|
if (*error) {
|
|
for (int j = 0; j < MAX_STACK_SIZE; j++) {
|
|
free(output[j]);
|
|
}
|
|
free(output);
|
|
return NULL;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
static double evaluate_postfix(char **tokens, int token_count, bool *error) {
|
|
Stack value_stack;
|
|
init_stack(&value_stack);
|
|
for (int i = 0; i < token_count; i++) {
|
|
char *token = tokens[i];
|
|
if (isdigit(token[0]) || (token[0] == '.' && isdigit(token[1]))) {
|
|
push(&value_stack, atof(token));
|
|
continue;
|
|
}
|
|
int func_len;
|
|
if (is_function(token, &func_len)) {
|
|
if (is_stack_empty(&value_stack)) {
|
|
*error = true;
|
|
return NAN;
|
|
}
|
|
double value = pop(&value_stack);
|
|
double result = apply_function(token, value, error);
|
|
if (*error) {
|
|
return NAN;
|
|
}
|
|
push(&value_stack, result);
|
|
continue;
|
|
}
|
|
if (is_operator(token[0]) && strlen(token) == 1) {
|
|
if (is_stack_empty(&value_stack)) {
|
|
*error = true;
|
|
return NAN;
|
|
}
|
|
double b = pop(&value_stack);
|
|
if (is_stack_empty(&value_stack)) {
|
|
*error = true;
|
|
return NAN;
|
|
}
|
|
double a = pop(&value_stack);
|
|
double result = apply_operator(a, b, token[0]);
|
|
if (isnan(result)) {
|
|
*error = true;
|
|
return NAN;
|
|
}
|
|
push(&value_stack, result);
|
|
continue;
|
|
}
|
|
*error = true;
|
|
return NAN;
|
|
}
|
|
if (is_stack_empty(&value_stack)) {
|
|
*error = true;
|
|
return NAN;
|
|
}
|
|
double result = pop(&value_stack);
|
|
if (!is_stack_empty(&value_stack)) {
|
|
*error = true;
|
|
return NAN;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
double evaluate_expression(const char *expression, bool *error) {
|
|
*error = false;
|
|
if (expression == NULL || strlen(expression) == 0) {
|
|
*error = true;
|
|
return NAN;
|
|
}
|
|
int token_count;
|
|
char **postfix_tokens = infix_to_postfix(expression, &token_count, error);
|
|
if (*error || postfix_tokens == NULL) {
|
|
*error = true;
|
|
return NAN;
|
|
}
|
|
double result = evaluate_postfix(postfix_tokens, token_count, error);
|
|
for (int i = 0; i < MAX_STACK_SIZE; i++) {
|
|
free(postfix_tokens[i]);
|
|
}
|
|
free(postfix_tokens);
|
|
|
|
return result;
|
|
}
|