usaa25/sk2/calculator.c
2026-02-12 00:11:50 +01:00

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;
}