/* * * ProLinga-Calc * * Copyright (C) 2002-2008 Xobas Software * All rights reserved. * * This file is part of ProLinga-Calc. * * ProLinga-Calc is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ProLinga-Calc is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ProLinga-Calc. If not, see . * * More information is available at the following addresses: * * Website : http://www.prolinga.org * * Email : prolinga-list@prolinga.org * */ #include "CalcCommon.h" #include "CalcLib.hpp" #include char exprRes[1024]; int to_int(int val) { return val; } double to_float(double val) { return val; } int prime(int n) { if (n <= 3) { return n; } n |= 1; while (true) { int m = (int)sqrt((double)n) + 1; int k = 3; while (true) { if (k > m) return n; if (n % k == 0) break; k += 2; } n += 2; } } PlCalc::PlCalc() { result = NULL; } PlCalc::~PlCalc() { if (result != NULL) delete result; } char *PlCalc::getCalc(char *expression) { calculator ccalc; symbol* sp; symbol::add(FFUNC1, "abs", (void*)fabs); symbol::add(FFUNC1, "acos", (void*)acos); symbol::add(FFUNC1, "asin", (void*)asin); symbol::add(FFUNC1, "atan", (void*)atan); symbol::add(FFUNC2, "atan2", (void*)atan2); symbol::add(FFUNC1, "cos", (void*)cos); symbol::add(FFUNC1, "cosh", (void*)cosh); symbol::add(FFUNC1, "exp", (void*)exp); symbol::add(FFUNC1, "log", (void*)log); symbol::add(FFUNC1, "log10", (void*)log10); symbol::add(FFUNC1, "sin", (void*)sin); symbol::add(FFUNC1, "sinh", (void*)sinh); symbol::add(FFUNC1, "tan", (void*)tan); symbol::add(FFUNC1, "tanh", (void*)tanh); symbol::add(FFUNC1, "sqrt", (void*)sqrt); symbol::add(FFUNC1, "float", (void*)to_float); symbol::add(IFUNC1, "int", (void*)to_int); symbol::add(IFUNC1, "prime", (void*)prime); sp = symbol::add(VARIABLE, "pi"); sp->val.tag = FLOAT; sp->val.fval = 3.1415926535897932385E0; sp = symbol::add(VARIABLE, "e"); sp->val.tag = FLOAT; sp->val.fval = 2.7182818284590452354E0; /* Ignore empty expressions */ if ( strcmp(expression, "") != 0) ccalc.evaluate(expression); else return NULL; /* Free previous result */ if (result != NULL) delete result; /* Store result */ result = new char[strlen(exprRes)+1]; strcpy(result,exprRes); /* Return */ return result; } inline unsigned string_hash_function(char* p) { unsigned h = 0, g; while(*p) { h = (h << 4) + *p++; if ((g = h & 0xF0000000) != 0) h ^= g >> 24; h &= ~g; } return h; } symbol* symbol::hash_table[CALC_HASH_TABLE_SIZE]; symbol* symbol::add(t_symbol tag, const char* name, void* func) { unsigned h = string_hash_function((char *)name) % CALC_HASH_TABLE_SIZE; symbol* sp; for (sp = hash_table[h]; sp != NULL; sp = sp->next) { if (strcmp(sp->name, name) == 0) return sp; } sp = new symbol; sp->tag = tag; sp->func = func; /*sp->name = strdup(name); */ sp->name = new char[strlen(name)]; strcpy(sp->name, name); sp->val.tag = INT; sp->val.ival = 0; sp->next = hash_table[h]; hash_table[h] = sp; return sp; } void calculator::calcerror(int pos, const char* msg) { // pos += strlen(prompt); if (pos < 80) { while (--pos >= 0) putc('.', stderr); fprintf(stderr, "^\n"); } fprintf(stderr, "Error: %s\n\n", msg); } t_operator calculator::scan(bool operand) { char name[CALC_MAX_EXP_LEN], *np; while (isspace(buf[pos])) pos += 1; switch (buf[pos++]) { case '\0': return END; case '(': return LPAR; case ')': return RPAR; case '+': if (buf[pos] == '+') { pos += 1; return operand ? PREINC : POSTINC; } else if (buf[pos] == '=') { pos += 1; return SETADD; } return operand ? PLUS : ADD; case '-': if (buf[pos] == '-') { pos += 1; return operand ? PREDEC : POSTDEC; } else if (buf[pos] == '=') { pos += 1; return SETSUB; } return operand ? MINUS : SUB; case '!': if (buf[pos] == '=') { pos += 1; return NE; } return NOT; case '~': return COM; case '*': if (buf[pos] == '*') { if (buf[pos+1] == '=') { pos += 2; return SETPOW; } pos += 1; return POW; } else if (buf[pos] == '=') { pos += 1; return SETMUL; } return MUL; case '/': if (buf[pos] == '=') { pos += 1; return SETDIV; } return DIV; case '%': if (buf[pos] == '=') { pos += 1; return SETMOD; } return MOD; case '<': if (buf[pos] == '<') { if (buf[pos+1] == '=') { pos += 2; return SETASL; } else { pos += 1; return ASL; } } else if (buf[pos] == '=') { pos += 1; return LE; } return LT; case '>': if (buf[pos] == '>') { if (buf[pos+1] == '>') { if (buf[pos+2] == '=') { pos += 3; return SETLSR; } pos += 2; return LSR; } else if (buf[pos+1] == '=') { pos += 2; return SETASR; } else { pos += 1; return ASR; } } else if (buf[pos] == '=') { pos += 1; return GE; } return GT; case '=': if (buf[pos] == '=') { pos += 1; return EQ; } return SET; case '&': if (buf[pos] == '&') { pos += 1; return AND; } else if (buf[pos] == '=') { pos += 1; return SETAND; } return AND; case '|': if (buf[pos] == '|') { pos += 1; return OR; } else if (buf[pos] == '=') { pos += 1; return SETOR; } return OR; case '^': if (buf[pos] == '=') { pos += 1; return SETXOR; } return XOR; case ',': return COMMA; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { int ival, ipos = 0, fpos = 0; double fval; sscanf(buf+pos-1, "%i%n", &ival, &ipos); sscanf(buf+pos-1, "%lf%n", &fval, &fpos); if (ipos == 0 && fpos == 0) { calcerror("bad numeric constant"); return ERROR; } if (v_sp == CALC_MAX_STACK_SIZE) { calcerror("stack overflow"); return ERROR; } if (ipos >= fpos) { v_stack[v_sp].tag = INT; v_stack[v_sp].ival = ival; pos += ipos - 1; } else { v_stack[v_sp].tag = FLOAT; v_stack[v_sp].fval = fval; pos += fpos - 1; } v_stack[v_sp].pos = pos; v_stack[v_sp++].var = NULL; return OPERAND; } default: pos -= 1; np = name; while (isalnum(buf[pos]) || buf[pos] == '$' || buf[pos] == '_') { *np++ = buf[pos++]; } if (np == buf) { calcerror("Bad character"); return ERROR; } *np = '\0'; symbol* sym = symbol::add(VARIABLE, name); if (v_sp == CALC_MAX_STACK_SIZE) { calcerror("stack overflow"); return ERROR; } v_stack[v_sp] = sym->val; v_stack[v_sp].pos = pos; v_stack[v_sp++].var = sym; return (sym->tag == VARIABLE) ? OPERAND : FUNC; } } static int lpr[TERMINALS] = { 2, 0, 0, 0, // BEGIN, OPERAND, ERROR, END, 4, 4, // LPAR, RPAR 5, 98, 98, // FUNC, POSTINC, POSTDEC, 98, 98, 98, 98, 98, 98, // PREINC, PREDEC, PLUS, MINUS, NOT, COM, 90, // POW, 80, 80, 80, // MUL, DIV, MOD, 70, 70, // ADD, SUB, 60, 60, 60, // ASL, ASR, LSR, 50, 50, 50, 50, // GT, GE, LT, LE, 40, 40, // EQ, NE, 38, // AND, 36, // XOR, 34, // OR, 20, 20, 20, 20, 20, 20, 20, //SET, SETADD, SETSUB, SETMUL, SETDIV, SETMOD, 20, 20, 20, 20, 20, 20, // SETASL, SETASR, SETLSR, SETAND, SETXOR, SETOR, 10 // COMMA }; static int rpr[TERMINALS] = { 0, 0, 0, 1, // BEGIN, OPERAND, ERROR, END, 110, 3, // LPAR, RPAR 120, 99, 99, // FUNC, POSTINC, POSTDEC 99, 99, 99, 99, 99, 99, // PREINC, PREDEC, PLUS, MINUS, NOT, COM, 95, // POW, 80, 80, 80, // MUL, DIV, MOD, 70, 70, // ADD, SUB, 60, 60, 60, // ASL, ASR, LSR, 50, 50, 50, 50, // GT, GE, LT, LE, 40, 40, // EQ, NE, 38, // AND, 36, // XOR, 34, // OR, 25, 25, 25, 25, 25, 25, 25, //SET, SETADD, SETSUB, SETMUL, SETDIV, SETMOD, 25, 25, 25, 25, 25, 25, // SETASL, SETASR, SETLSR, SETAND, SETXOR, SETOR, 15 // COMMA }; bool calculator::assign() { value& v = v_stack[v_sp-1]; if (v.var == NULL) { calcerror(v.pos, "variable expected"); return false; } else { v.var->val = v; return true; } } void calculator::evaluate(char* expression) { char var_name[16]; buf = expression; v_sp = 0; o_sp = 0; pos = 0; o_stack[o_sp++] = BEGIN; bool operand = true; int n_args = 0; while (true) { next_token: int oper = scan(operand); if (oper == ERROR) return; if (!operand) { if (!BINARY(oper) && oper != END && oper != POSTINC && oper != POSTDEC && oper != RPAR) { calcerror("operator expected"); return; } if (oper != POSTINC && oper != POSTDEC && oper != RPAR) operand = true; } else { if (oper == OPERAND) { operand = false; n_args += 1; continue; } if (BINARY(oper) || oper == RPAR) { calcerror("operand expected"); return; } } int n_args = 1; while (lpr[o_stack[o_sp-1]] >= rpr[oper]) { int cop = o_stack[--o_sp]; switch (cop) { case BEGIN: if (oper == RPAR) { calcerror("Unmatched ')'"); return; } if (oper != END) calcerror("Unexpected end of input"); /* RESULTS */ if (v_sp == 1) { sprintf(var_name, "$%d", ++tmp_var_count); //printf("%s = ", var_name); symbol::add(VARIABLE, var_name)->val = v_stack[0]; if (v_stack[0].tag == INT) { //printf("%d [%#x %#o]\n", v_stack[0].ival, //v_stack[0].ival, v_stack[0].ival); sprintf(exprRes,"%d", v_stack[0].ival); } else { //printf("%.10g\n", v_stack[0].fval); sprintf(exprRes,"%.10g", v_stack[0].fval); } } else if (v_sp != 0) calcerror("Unexpected end of expression"); return; case COMMA: n_args += 1; continue; case ADD: case SETADD: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival += v_stack[v_sp-1].ival; else { v_stack[v_sp-2].fval = v_stack[v_sp-2].get() + v_stack[v_sp-1].get(); v_stack[v_sp-2].tag = FLOAT; } v_sp -= 1; if (cop == SETADD) { if (!assign()) return; } v_stack[v_sp-1].var = NULL; break; case SUB: case SETSUB: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival -= v_stack[v_sp-1].ival; else { v_stack[v_sp-2].fval = v_stack[v_sp-2].get() - v_stack[v_sp-1].get(); v_stack[v_sp-2].tag = FLOAT; } v_sp -= 1; if (cop == SETSUB) { if (!assign()) return; } v_stack[v_sp-1].var = NULL; break; case MUL: case SETMUL: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival *= v_stack[v_sp-1].ival; else { v_stack[v_sp-2].fval = v_stack[v_sp-2].get() * v_stack[v_sp-1].get(); v_stack[v_sp-2].tag = FLOAT; } v_sp -= 1; if (cop == SETMUL) { if (!assign()) return; } v_stack[v_sp-1].var = NULL; break; case DIV: case SETDIV: if (v_stack[v_sp-1].get() == 0.0) { calcerror(v_stack[v_sp-2].pos, "Division by zero"); return; } if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival /= v_stack[v_sp-1].ival; else { v_stack[v_sp-2].fval = v_stack[v_sp-2].get() / v_stack[v_sp-1].get(); v_stack[v_sp-2].tag = FLOAT; } v_sp -= 1; if (cop == SETDIV) { if (!assign()) return; } v_stack[v_sp-1].var = NULL; break; case MOD: case SETMOD: if (v_stack[v_sp-1].get() == 0.0) { calcerror(v_stack[v_sp-2].pos, "Division by zero"); return; } if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival %= v_stack[v_sp-1].ival; else { v_stack[v_sp-2].fval = fmod(v_stack[v_sp-2].get(), v_stack[v_sp-1].get()); v_stack[v_sp-2].tag = FLOAT; } v_sp -= 1; if (cop == SETMOD) { if (!assign()) return; } v_stack[v_sp-1].var = NULL; break; case POW: case SETPOW: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival = (int)pow((double)v_stack[v_sp-2].ival, (double)v_stack[v_sp-1].ival); else { v_stack[v_sp-2].fval = pow(v_stack[v_sp-2].get(), v_stack[v_sp-1].get()); v_stack[v_sp-2].tag = FLOAT; } v_sp -= 1; if (cop == SETPOW) { if (!assign()) return; } v_stack[v_sp-1].var = NULL; break; case AND: case SETAND: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival &= v_stack[v_sp-1].ival; else { v_stack[v_sp-2].ival = v_stack[v_sp-2].get_int() & v_stack[v_sp-1].get_int(); v_stack[v_sp-2].tag = INT; } v_sp -= 1; if (cop == SETAND) { if (!assign()) return; } v_stack[v_sp-1].var = NULL; break; case OR: case SETOR: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival |= v_stack[v_sp-1].ival; else { v_stack[v_sp-2].ival = v_stack[v_sp-2].get_int() | v_stack[v_sp-1].get_int(); v_stack[v_sp-2].tag = INT; } v_sp -= 1; if (cop == SETOR) { if (!assign()) return; } v_stack[v_sp-1].var = NULL; break; case XOR: case SETXOR: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival ^= v_stack[v_sp-1].ival; else { v_stack[v_sp-2].ival = v_stack[v_sp-2].get_int() ^ v_stack[v_sp-1].get_int(); v_stack[v_sp-2].tag = INT; } v_sp -= 1; if (cop == SETXOR) { if (!assign()) return; } v_stack[v_sp-1].var = NULL; break; case ASL: case SETASL: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival <<= v_stack[v_sp-1].ival; else { v_stack[v_sp-2].ival = v_stack[v_sp-2].get_int() << v_stack[v_sp-1].get_int(); v_stack[v_sp-2].tag = INT; } v_sp -= 1; if (cop == SETASL) { if (!assign()) return; } v_stack[v_sp-1].var = NULL; break; case ASR: case SETASR: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival >>= v_stack[v_sp-1].ival; else { v_stack[v_sp-2].ival = v_stack[v_sp-2].get_int() >> v_stack[v_sp-1].get_int(); v_stack[v_sp-2].tag = INT; } v_sp -= 1; if (cop == SETASR) { if (!assign()) return; } v_stack[v_sp-1].var = NULL; break; case LSR: case SETLSR: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival = (unsigned)v_stack[v_sp-2].ival >> v_stack[v_sp-1].ival; else { v_stack[v_sp-2].ival = (unsigned)v_stack[v_sp-2].get_int() >> v_stack[v_sp-1].get_int(); v_stack[v_sp-2].tag = INT; } v_sp -= 1; if (cop == SETLSR) { if (!assign()) return; } v_stack[v_sp-1].var = NULL; break; case EQ: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival = v_stack[v_sp-2].ival == v_stack[v_sp-1].ival; else { v_stack[v_sp-2].ival = v_stack[v_sp-2].get() == v_stack[v_sp-1].get(); v_stack[v_sp-2].tag = INT; } v_sp -= 1; v_stack[v_sp-1].var = NULL; break; case NE: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival = v_stack[v_sp-2].ival != v_stack[v_sp-1].ival; else { v_stack[v_sp-2].ival = v_stack[v_sp-2].get() != v_stack[v_sp-1].get(); v_stack[v_sp-2].tag = INT; } v_sp -= 1; v_stack[v_sp-1].var = NULL; break; case GT: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival = v_stack[v_sp-2].ival > v_stack[v_sp-1].ival; else { v_stack[v_sp-2].ival = v_stack[v_sp-2].get() > v_stack[v_sp-1].get(); v_stack[v_sp-2].tag = INT; } v_sp -= 1; v_stack[v_sp-1].var = NULL; break; case GE: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival = v_stack[v_sp-2].ival >= v_stack[v_sp-1].ival; else { v_stack[v_sp-2].ival = v_stack[v_sp-2].get() >= v_stack[v_sp-1].get(); v_stack[v_sp-2].tag = INT; } v_sp -= 1; v_stack[v_sp-1].var = NULL; break; case LT: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival = v_stack[v_sp-2].ival < v_stack[v_sp-1].ival; else { v_stack[v_sp-2].ival = v_stack[v_sp-2].get() < v_stack[v_sp-1].get(); v_stack[v_sp-2].tag = INT; } v_sp -= 1; v_stack[v_sp-1].var = NULL; break; case LE: if (v_stack[v_sp-1].tag == INT && v_stack[v_sp-2].tag == INT) v_stack[v_sp-2].ival = v_stack[v_sp-2].ival <= v_stack[v_sp-1].ival; else { v_stack[v_sp-2].ival = v_stack[v_sp-2].get() <= v_stack[v_sp-1].get(); v_stack[v_sp-2].tag = INT; } v_sp -= 1; v_stack[v_sp-1].var = NULL; break; case PREINC: if (v_stack[v_sp-1].tag == INT) v_stack[v_sp-1].ival += 1; else v_stack[v_sp-1].fval += 1; if (!assign()) return; v_stack[v_sp-1].var = NULL; break; case PREDEC: if (v_stack[v_sp-1].tag == INT) v_stack[v_sp-1].ival -= 1; else { v_stack[v_sp-1].fval -= 1; } if (!assign()) return; v_stack[v_sp-1].var = NULL; break; case POSTINC: if (v_stack[v_sp-1].var == NULL) { calcerror(v_stack[v_sp-1].pos, "Variable expected"); return; } if (v_stack[v_sp-1].var->val.tag == INT) v_stack[v_sp-1].var->val.ival += 1; else v_stack[v_sp-1].var->val.fval += 1; v_stack[v_sp-1].var = NULL; break; case POSTDEC: if (v_stack[v_sp-1].var == NULL) { calcerror(v_stack[v_sp-1].pos, "Variable expected"); return; } if (v_stack[v_sp-1].var->val.tag == INT) v_stack[v_sp-1].var->val.ival -= 1; else v_stack[v_sp-1].var->val.fval -= 1; v_stack[v_sp-1].var = NULL; break; case SET: if (v_stack[v_sp-2].var == NULL) { calcerror(v_stack[v_sp-2].pos, "Variable expected"); return; } else v_stack[v_sp-2]=v_stack[v_sp-2].var->val=v_stack[v_sp-1]; v_sp -= 1; v_stack[v_sp-1].var = NULL; break; case NOT: if (v_stack[v_sp-1].tag == INT) v_stack[v_sp-1].ival = !v_stack[v_sp-1].ival; else { v_stack[v_sp-1].ival = !v_stack[v_sp-1].fval; v_stack[v_sp-1].tag = INT; } v_stack[v_sp-1].var = NULL; break; case MINUS: if (v_stack[v_sp-1].tag == INT) v_stack[v_sp-1].ival = -v_stack[v_sp-1].ival; else v_stack[v_sp-1].fval = -v_stack[v_sp-1].fval; // no break case PLUS: v_stack[v_sp-1].var = NULL; break; case COM: if (v_stack[v_sp-1].tag == INT) v_stack[v_sp-1].ival = ~v_stack[v_sp-1].ival; else { v_stack[v_sp-1].ival = ~(int)v_stack[v_sp-1].fval; v_stack[v_sp-1].tag = INT; } v_stack[v_sp-1].var = NULL; break; case RPAR: calcerror("mismatch ')'"); return; case FUNC: calcerror("'(' expected"); return; case LPAR: if (oper != RPAR) { calcerror("')' expected"); return; } if (o_stack[o_sp-1] == FUNC) { symbol* sym = v_stack[v_sp-n_args-1].var; if (sym->tag == IFUNC1) { if (n_args != 1) { calcerror(v_stack[v_sp-n_args-1].pos, "Function should take one argument"); return; } v_stack[v_sp-2].ival = (*(int(*)(int))sym->func)(v_stack[v_sp-1].get_int()); v_stack[v_sp-2].tag = INT; v_sp -= 1; } else if (sym->tag == FFUNC1) { if (n_args != 1) { calcerror(v_stack[v_sp-n_args-1].pos, "Function should take one argument"); return; } v_stack[v_sp-2].fval = (*(double(*)(double))sym->func)(v_stack[v_sp-1].get()); v_stack[v_sp-2].tag = FLOAT; v_sp -= 1; } else if (sym->tag == FFUNC2) { if (n_args != 2) { calcerror(v_stack[v_sp-n_args-1].pos, "Function should take two arguments"); return; } v_stack[v_sp-3].fval = (*(double(*)(double, double))sym->func) (v_stack[v_sp-2].get(), v_stack[v_sp-1].get()); v_stack[v_sp-3].tag = FLOAT; v_sp -= 2; } else calcerror("Invalid expression"); v_stack[v_sp-1].var = NULL; o_sp -= 1; n_args = 1; } else if (n_args != 1) { calcerror("Function call expected"); return; } goto next_token; default: calcerror("syntax error"); } } if (o_sp == CALC_MAX_STACK_SIZE) { calcerror("operator stack overflow"); return; } o_stack[o_sp++] = oper; } }