476 lines
12 KiB
C
476 lines
12 KiB
C
#include "calculator.h"
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* ---------------------------- */
|
|
/* - globalna chyba */
|
|
/* ---------------------------- */
|
|
static char g_err[128] = "OK";
|
|
|
|
/* - helper: set error */
|
|
static void set_err(const char *msg) {
|
|
strncpy(g_err, msg, sizeof(g_err)-1);
|
|
}
|
|
|
|
/* - vrat error text */
|
|
const char *calc_error(void) {
|
|
return g_err;
|
|
}
|
|
|
|
/* ---------------------------- */
|
|
/* - token typy */
|
|
/* ---------------------------- */
|
|
typedef enum {
|
|
T_NUM, /* - cislo */
|
|
T_OP, /* - operator */
|
|
T_FUNC, /* - funkcia */
|
|
T_LP, /* - ( */
|
|
T_RP /* - ) */
|
|
} TType;
|
|
|
|
typedef struct {
|
|
TType type;
|
|
double num; /* - hodnota */
|
|
char op; /* - + - * / ^ ~ */
|
|
char func[8]; /* - sin cos sqrt log ln */
|
|
} Token;
|
|
|
|
/* ---------------------------- */
|
|
/* - jednoduche pole tokenov */
|
|
/* ---------------------------- */
|
|
typedef struct {
|
|
Token *a;
|
|
int n;
|
|
int cap;
|
|
} TVec;
|
|
|
|
/* - init */
|
|
static void tvec_init(TVec *v) { v->a=NULL; v->n=0; v->cap=0; }
|
|
|
|
/* - free */
|
|
static void tvec_free(TVec *v) { free(v->a); tvec_init(v); }
|
|
|
|
/* - push */
|
|
static int tvec_push(TVec *v, Token t) {
|
|
if (v->n >= v->cap) {
|
|
int nc = v->cap ? v->cap*2 : 32;
|
|
Token *p = realloc(v->a, nc*sizeof(Token));
|
|
if (!p) return 0;
|
|
v->a = p;
|
|
v->cap = nc;
|
|
}
|
|
v->a[v->n++] = t;
|
|
return 1;
|
|
}
|
|
|
|
/* ---------------------------- */
|
|
/* - priorita operatorov */
|
|
/* ---------------------------- */
|
|
static int prec(char op) {
|
|
if (op=='~') return 4; /* - unarne minus */
|
|
if (op=='^') return 3; /* - mocnina */
|
|
if (op=='*' || op=='/') return 2; /* - nasob/del */
|
|
if (op=='+' || op=='-') return 1; /* - plus/minus */
|
|
return -1;
|
|
}
|
|
|
|
/* - prava asociativita */
|
|
static int right_assoc(char op) {
|
|
return (op=='^' || op=='~'); /* - pow, unary */
|
|
}
|
|
|
|
/* ---------------------------- */
|
|
/* - tokenize: string -> tokeny */
|
|
/* ---------------------------- */
|
|
static int tokenize(const char *s, TVec *out) {
|
|
/* - reset error */
|
|
set_err("OK");
|
|
|
|
tvec_init(out);
|
|
|
|
/* - prev token */
|
|
TType prev = T_OP; /* - na zaciatku */
|
|
|
|
while (*s) {
|
|
/* - skip spaces */
|
|
while (isspace((unsigned char)*s)) s++;
|
|
|
|
if (!*s) break;
|
|
|
|
/* ---------------------------- */
|
|
/* - cislo */
|
|
/* ---------------------------- */
|
|
if (isdigit((unsigned char)*s) || *s=='.') {
|
|
char *end = NULL;
|
|
double x = strtod(s, &end);
|
|
if (end==s) { set_err("Zle cislo"); return 0; }
|
|
|
|
Token t = {0};
|
|
t.type = T_NUM;
|
|
t.num = x;
|
|
|
|
if (!tvec_push(out, t)) { set_err("No memory"); return 0; }
|
|
|
|
s = end;
|
|
prev = T_NUM;
|
|
continue;
|
|
}
|
|
|
|
/* ---------------------------- */
|
|
/* - ident: funkcia/konstanta */
|
|
/* ---------------------------- */
|
|
if (isalpha((unsigned char)*s)) {
|
|
char buf[16]={0};
|
|
int i=0;
|
|
|
|
while (isalpha((unsigned char)*s) && i<15) {
|
|
buf[i++] = (char)tolower((unsigned char)*s);
|
|
s++;
|
|
}
|
|
buf[i]=0;
|
|
|
|
/* - konstanta pi */
|
|
if (!strcmp(buf,"pi")) {
|
|
Token t={0};
|
|
t.type=T_NUM;
|
|
t.num=M_PI;
|
|
if (!tvec_push(out,t)) { set_err("No memory"); return 0; }
|
|
prev=T_NUM;
|
|
continue;
|
|
}
|
|
|
|
/* - konstanta e */
|
|
if (!strcmp(buf,"e")) {
|
|
Token t={0};
|
|
t.type=T_NUM;
|
|
t.num=M_E;
|
|
if (!tvec_push(out,t)) { set_err("No memory"); return 0; }
|
|
prev=T_NUM;
|
|
continue;
|
|
}
|
|
|
|
/* - funkcie */
|
|
if (!strcmp(buf,"sin") || !strcmp(buf,"cos") ||
|
|
!strcmp(buf,"sqrt") || !strcmp(buf,"log") ||
|
|
!strcmp(buf,"ln")) {
|
|
|
|
Token t={0};
|
|
t.type=T_FUNC;
|
|
strncpy(t.func, buf, sizeof(t.func)-1);
|
|
|
|
if (!tvec_push(out,t)) { set_err("No memory"); return 0; }
|
|
prev=T_FUNC;
|
|
continue;
|
|
}
|
|
|
|
set_err("Neznama funkcia");
|
|
return 0;
|
|
}
|
|
|
|
/* ---------------------------- */
|
|
/* - zatvorky */
|
|
/* ---------------------------- */
|
|
if (*s=='(') {
|
|
Token t={0};
|
|
t.type=T_LP;
|
|
if (!tvec_push(out,t)) { set_err("No memory"); return 0; }
|
|
s++;
|
|
prev=T_LP;
|
|
continue;
|
|
}
|
|
|
|
if (*s==')') {
|
|
Token t={0};
|
|
t.type=T_RP;
|
|
if (!tvec_push(out,t)) { set_err("No memory"); return 0; }
|
|
s++;
|
|
prev=T_RP;
|
|
continue;
|
|
}
|
|
|
|
/* ---------------------------- */
|
|
/* - operatory */
|
|
/* ---------------------------- */
|
|
if (strchr("+-*/^", *s)) {
|
|
char op = *s;
|
|
|
|
/* - unary minus */
|
|
if (op=='-' && !(prev==T_NUM || prev==T_RP)) {
|
|
op='~';
|
|
}
|
|
|
|
Token t={0};
|
|
t.type=T_OP;
|
|
t.op=op;
|
|
|
|
if (!tvec_push(out,t)) { set_err("No memory"); return 0; }
|
|
|
|
s++;
|
|
prev=T_OP;
|
|
continue;
|
|
}
|
|
|
|
set_err("Neplatny znak");
|
|
return 0;
|
|
}
|
|
|
|
if (out->n==0) { set_err("Prazdny vyraz"); return 0; }
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ---------------------------- */
|
|
/* - infix -> postfix */
|
|
/* ---------------------------- */
|
|
static int to_postfix(const TVec *in, TVec *out) {
|
|
tvec_init(out);
|
|
|
|
TVec st; /* - operator stack */
|
|
tvec_init(&st);
|
|
|
|
for (int i=0;i<in->n;i++) {
|
|
Token t = in->a[i];
|
|
|
|
/* - cislo -> output */
|
|
if (t.type==T_NUM) {
|
|
if (!tvec_push(out,t)) { set_err("No memory"); goto fail; }
|
|
}
|
|
|
|
/* - funkcia -> stack */
|
|
else if (t.type==T_FUNC) {
|
|
if (!tvec_push(&st,t)) { set_err("No memory"); goto fail; }
|
|
}
|
|
|
|
/* - operator */
|
|
else if (t.type==T_OP) {
|
|
while (st.n>0) {
|
|
Token top = st.a[st.n-1];
|
|
|
|
/* - funkcia ma prioritu */
|
|
if (top.type==T_FUNC) {
|
|
if (!tvec_push(out,top)) { set_err("No memory"); goto fail; }
|
|
st.n--;
|
|
continue;
|
|
}
|
|
|
|
/* - operator priorita */
|
|
if (top.type==T_OP) {
|
|
int p1 = prec(t.op);
|
|
int p2 = prec(top.op);
|
|
|
|
/* - pop pri vacsej priorite */
|
|
if (p2>p1 || (p2==p1 && !right_assoc(t.op))) {
|
|
if (!tvec_push(out,top)) { set_err("No memory"); goto fail; }
|
|
st.n--;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (!tvec_push(&st,t)) { set_err("No memory"); goto fail; }
|
|
}
|
|
|
|
/* - ( -> stack */
|
|
else if (t.type==T_LP) {
|
|
if (!tvec_push(&st,t)) { set_err("No memory"); goto fail; }
|
|
}
|
|
|
|
/* - ) -> pop do ( */
|
|
else if (t.type==T_RP) {
|
|
int ok=0;
|
|
|
|
while (st.n>0) {
|
|
Token top = st.a[--st.n];
|
|
|
|
if (top.type==T_LP) { ok=1; break; }
|
|
|
|
if (!tvec_push(out,top)) { set_err("No memory"); goto fail; }
|
|
}
|
|
|
|
if (!ok) { set_err("Zle zatvorky"); goto fail; }
|
|
|
|
/* - po zatvorke funkcia */
|
|
if (st.n>0 && st.a[st.n-1].type==T_FUNC) {
|
|
Token f = st.a[--st.n];
|
|
if (!tvec_push(out,f)) { set_err("No memory"); goto fail; }
|
|
}
|
|
}
|
|
}
|
|
|
|
/* - pop zvysok */
|
|
while (st.n>0) {
|
|
Token top = st.a[--st.n];
|
|
if (top.type==T_LP) { set_err("Zle zatvorky"); goto fail; }
|
|
if (!tvec_push(out,top)) { set_err("No memory"); goto fail; }
|
|
}
|
|
|
|
tvec_free(&st);
|
|
return 1;
|
|
|
|
fail:
|
|
tvec_free(&st);
|
|
tvec_free(out);
|
|
return 0;
|
|
}
|
|
|
|
/* ---------------------------- */
|
|
/* - stack double */
|
|
/* ---------------------------- */
|
|
typedef struct {
|
|
double *a;
|
|
int n;
|
|
int cap;
|
|
} DStack;
|
|
|
|
static void ds_init(DStack *s){ s->a=NULL; s->n=0; s->cap=0; }
|
|
static void ds_free(DStack *s){ free(s->a); ds_init(s); }
|
|
|
|
static int ds_push(DStack *s, double x){
|
|
if (s->n>=s->cap){
|
|
int nc=s->cap? s->cap*2:32;
|
|
double *p=realloc(s->a,nc*sizeof(double));
|
|
if(!p) return 0;
|
|
s->a=p; s->cap=nc;
|
|
}
|
|
s->a[s->n++]=x;
|
|
return 1;
|
|
}
|
|
|
|
static int ds_pop(DStack *s, double *x){
|
|
if(s->n==0) return 0;
|
|
*x=s->a[--s->n];
|
|
return 1;
|
|
}
|
|
|
|
/* ---------------------------- */
|
|
/* - apply function */
|
|
/* ---------------------------- */
|
|
static int apply_func(const char *f, double x, double *out){
|
|
if(!strcmp(f,"sin")) { *out=sin(x); return 1; }
|
|
if(!strcmp(f,"cos")) { *out=cos(x); return 1; }
|
|
|
|
if(!strcmp(f,"sqrt")){
|
|
if(x<0) { set_err("sqrt domena"); return 0; }
|
|
*out=sqrt(x);
|
|
return 1;
|
|
}
|
|
|
|
if(!strcmp(f,"log")){
|
|
if(x<=0) { set_err("log domena"); return 0; }
|
|
*out=log10(x);
|
|
return 1;
|
|
}
|
|
|
|
if(!strcmp(f,"ln")){
|
|
if(x<=0) { set_err("ln domena"); return 0; }
|
|
*out=log(x);
|
|
return 1;
|
|
}
|
|
|
|
set_err("Zla funkcia");
|
|
return 0;
|
|
}
|
|
|
|
/* ---------------------------- */
|
|
/* - eval postfix */
|
|
/* ---------------------------- */
|
|
static int eval_postfix(const TVec *post, double *out){
|
|
DStack st;
|
|
ds_init(&st);
|
|
|
|
for(int i=0;i<post->n;i++){
|
|
Token t=post->a[i];
|
|
|
|
/* - cislo -> push */
|
|
if(t.type==T_NUM){
|
|
if(!ds_push(&st,t.num)){ set_err("No memory"); goto fail; }
|
|
}
|
|
|
|
/* - funkcia */
|
|
else if(t.type==T_FUNC){
|
|
double a,r;
|
|
if(!ds_pop(&st,&a)){ set_err("Syntax"); goto fail; }
|
|
if(!apply_func(t.func,a,&r)) goto fail;
|
|
if(!ds_push(&st,r)){ set_err("No memory"); goto fail; }
|
|
}
|
|
|
|
/* - operator */
|
|
else if(t.type==T_OP){
|
|
|
|
/* - unary minus */
|
|
if(t.op=='~'){
|
|
double a;
|
|
if(!ds_pop(&st,&a)){ set_err("Syntax"); goto fail; }
|
|
if(!ds_push(&st,-a)){ set_err("No memory"); goto fail; }
|
|
continue;
|
|
}
|
|
|
|
/* - binarne */
|
|
double b,a,r;
|
|
if(!ds_pop(&st,&b) || !ds_pop(&st,&a)){ set_err("Syntax"); goto fail; }
|
|
|
|
if(t.op=='+') r=a+b;
|
|
else if(t.op=='-') r=a-b;
|
|
else if(t.op=='*') r=a*b;
|
|
else if(t.op=='/'){
|
|
if(b==0){ set_err("Delenie nulou"); goto fail; }
|
|
r=a/b;
|
|
}
|
|
else if(t.op=='^'){
|
|
r=pow(a,b);
|
|
if(isnan(r) || isinf(r)){ set_err("Pow domena"); goto fail; }
|
|
}
|
|
else { set_err("Zly operator"); goto fail; }
|
|
|
|
if(!ds_push(&st,r)){ set_err("No memory"); goto fail; }
|
|
}
|
|
else{
|
|
set_err("Syntax");
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
/* - musi ostat 1 */
|
|
if(st.n!=1){ set_err("Syntax"); goto fail; }
|
|
|
|
*out=st.a[0];
|
|
ds_free(&st);
|
|
return 1;
|
|
|
|
fail:
|
|
ds_free(&st);
|
|
return 0;
|
|
}
|
|
|
|
/* ---------------------------- */
|
|
/* - hlavna funkcia */
|
|
/* ---------------------------- */
|
|
calc_status_t calc_evaluate(const char *expr, double *out){
|
|
if(!expr || !out){ set_err("NULL"); return CALC_ERR; }
|
|
|
|
TVec toks, post;
|
|
tvec_init(&toks);
|
|
tvec_init(&post);
|
|
|
|
/* - tokenize */
|
|
if(!tokenize(expr,&toks)){ tvec_free(&toks); return CALC_ERR; }
|
|
|
|
/* - postfix */
|
|
if(!to_postfix(&toks,&post)){ tvec_free(&toks); return CALC_ERR; }
|
|
|
|
/* - eval */
|
|
if(!eval_postfix(&post,out)){
|
|
tvec_free(&toks);
|
|
tvec_free(&post);
|
|
return CALC_ERR;
|
|
}
|
|
|
|
tvec_free(&toks);
|
|
tvec_free(&post);
|
|
return CALC_OK;
|
|
}
|
|
|