198 lines
5.8 KiB
C
198 lines
5.8 KiB
C
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include "calculator.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#define MAX_TOKEN_LEN 256
|
|
|
|
typedef enum{
|
|
TOKEN_NUMBER,
|
|
TOKEN_FUNCTION,
|
|
TOKEN_OPERATOR,
|
|
TOKEN_LPAREN,
|
|
TOKEN_RPAREN,
|
|
TOKEN_UNKNOWN
|
|
} TokenType;
|
|
|
|
|
|
|
|
typedef struct{
|
|
TokenType type;
|
|
char value[MAX_TOKEN_LEN];
|
|
} Token;
|
|
|
|
static int get_precedence(const char *op){
|
|
if (strcmp(op,"+")==0||strcmp(op,"-")==0) return 1;
|
|
if (strcmp(op,"*")==0||strcmp(op,"/")==0) return 2;
|
|
if (strcmp(op,"^2")==0) return 3;
|
|
if (strcmp(op,"sin")==0||strcmp(op,"cos")==0||strcmp(op,"sqrt")==0||strcmp(op,"log")==0) return 4;
|
|
return 0;
|
|
|
|
}
|
|
|
|
static bool is_right_associative(const char *op){
|
|
return strcmp(op,"^2")==0;
|
|
}
|
|
|
|
static int tokenize(const char *expr, Token *tokens, int max_tokens){
|
|
int count= 0;
|
|
const char *p = expr;
|
|
while(*p){
|
|
if(isspace(*p)){p++; continue;}
|
|
|
|
if(isdigit(*p)||*p=='.'||(*p=='-'&&(isdigit(p[1])||p[1]=='.'))){
|
|
char *end;
|
|
strtod(p,&end);
|
|
strncpy(tokens[count].value, p ,end-p);
|
|
tokens[count].value[end-p] = '\0';
|
|
tokens[count].type = TOKEN_NUMBER;
|
|
p = end;
|
|
// p++;
|
|
}
|
|
|
|
else if(*p=='('){
|
|
strcpy(tokens[count].value, "(");
|
|
tokens[count].type = TOKEN_LPAREN;
|
|
p++;
|
|
}
|
|
else if(*p==')'){
|
|
strcpy(tokens[count].value, ")");
|
|
tokens[count].type = TOKEN_RPAREN;
|
|
p++;
|
|
}
|
|
else if(isalpha(*p)){
|
|
char func[5];
|
|
int i =0;
|
|
while(isalpha(*p)&&i<4){func[i++]=*p++;};
|
|
func[i] = '\0';
|
|
if (strcmp(func,"cos")==0||strcmp(func,"sin")==0||strcmp(func,"sqrt")==0||strcmp(func,"log")==0){
|
|
strcpy(tokens[count].value, func);
|
|
tokens[count].type = TOKEN_FUNCTION;
|
|
}
|
|
else{ return -1;}
|
|
}
|
|
else{
|
|
char op[3];
|
|
op[0] = *p;
|
|
op[1] = '\0';
|
|
if (*p == '^' &&p[1]=='2'){
|
|
strcpy(op,"^2");
|
|
p+=2;
|
|
}
|
|
p++;
|
|
strcpy(tokens[count].value, op);
|
|
tokens[count].type = TOKEN_OPERATOR;
|
|
}
|
|
count++;
|
|
if (count>=max_tokens) return -1;
|
|
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int infix_to_postfix(Token *infix, int infix_count, Token *postfix){
|
|
Token op_stack[MAX_TOKEN_LEN];
|
|
int on_top = -1;
|
|
int postfix_count = 0;
|
|
for (int i = 0; i<infix_count;i++){
|
|
Token t = infix[i];
|
|
switch(t.type){
|
|
case TOKEN_NUMBER:
|
|
postfix[postfix_count++] = t;
|
|
break;
|
|
|
|
case TOKEN_FUNCTION:
|
|
op_stack[++on_top] = t;
|
|
break;
|
|
case TOKEN_OPERATOR:
|
|
while(on_top>=0){
|
|
Token top = op_stack[on_top];
|
|
if(top.type!=TOKEN_OPERATOR&&top.type!=TOKEN_FUNCTION) break;
|
|
int prec_t = get_precedence(t.value);
|
|
int prec_top = get_precedence(top.value);
|
|
if(prec_top<prec_t||(prec_t==prec_top&&!is_right_associative(t.value))) break;
|
|
postfix[postfix_count++] = op_stack[on_top--];
|
|
}
|
|
op_stack[++on_top] = t;
|
|
break;
|
|
case TOKEN_LPAREN:
|
|
op_stack[++on_top] = t;
|
|
break;
|
|
case TOKEN_RPAREN:
|
|
while (on_top>=0&&strcmp(op_stack[on_top].value,"(")!=0){
|
|
postfix[postfix_count++] = op_stack[on_top--];
|
|
}
|
|
if (on_top<0) return -1;
|
|
on_top--;
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
while(on_top>=0){
|
|
if(strcmp(op_stack[on_top].value,"(")==0) return -1;
|
|
postfix[postfix_count++] = op_stack[on_top--];
|
|
}
|
|
return postfix_count;
|
|
}
|
|
|
|
static double evaluate_postfix(Token *postfix, int count){
|
|
double stack[MAX_TOKEN_LEN];
|
|
int top= -1;
|
|
|
|
for (int i = 0; i<count; i++){
|
|
Token t =postfix[i];
|
|
if (t.type == TOKEN_NUMBER){
|
|
stack[++top] = atof(t.value);
|
|
}
|
|
else{
|
|
if (t.type == TOKEN_FUNCTION|| (strcmp(t.value,"^2")==0)){
|
|
if (top<0) return NAN;
|
|
double a= stack[top--];
|
|
if(strcmp(t.value, "cos")==0) stack[++top] = cos(a);
|
|
else if(strcmp(t.value, "sin")==0) stack[++top] = sin(a);
|
|
else if(strcmp(t.value, "sqrt")==0){
|
|
if (a<0) return NAN;
|
|
stack[++top] = sqrt(a);
|
|
}
|
|
else if(strcmp(t.value, "log")==0){
|
|
if (a<=0) return NAN;
|
|
stack[++top] = log(a);
|
|
}
|
|
else if(strcmp(t.value,"^2")==0) stack[++top] = a*a;
|
|
else return NAN;
|
|
}
|
|
else{
|
|
if (top<1) return NAN;
|
|
double b = stack[top--];
|
|
double a = stack[top--];
|
|
if (strcmp(t.value,"+")==0) stack[++top] = a+b;
|
|
else if (strcmp(t.value,"-")==0) stack[++top] = a-b;
|
|
else if (strcmp(t.value,"*")==0) stack[++top] = a*b;
|
|
else if (strcmp(t.value,"/")==0){
|
|
if (b==0) return NAN;
|
|
stack[++top] = a/b;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (top!=0) return NAN;
|
|
return stack[0];
|
|
}
|
|
|
|
double evaluate(const char *expression){
|
|
Token infix[MAX_TOKEN_LEN];
|
|
int infix_count = tokenize(expression, infix,MAX_TOKEN_LEN);
|
|
if (infix_count<0) return NAN;
|
|
|
|
Token postfix[MAX_TOKEN_LEN];
|
|
int postfix_count = infix_to_postfix(infix,infix_count,postfix);
|
|
if (postfix_count<0) return NAN;
|
|
|
|
return evaluate_postfix(postfix, postfix_count);
|
|
}
|