#include <iostream>
#include <string>
#include <limits>
#include <cctype>
#include <stdexcept>
#define err_msg(s) throw(std::domain_error(s))
//стек на базе динамического массива
template<typename T>
class mstack {
private:
T* arr;
size_t cnt;
size_t mem;
public:
mstack(void):arr(NULL),
cnt(0),
mem(8){}
~mstack(){
this->clear();
}
mstack(const mstack&);
mstack& operator = (const mstack&);
public:
void push(const T& val){
if(__alloc())
arr[cnt++] = val;
else
err_msg("Ошибка при выделение памяти");
}
void pop(void){
if(cnt > 0)
--cnt;
else
this->clear();
}
void removeAt(size_t index){
if(index < cnt){
T* e = arr + (cnt - 1);
for(T* p = arr + index; p < e; ++p)
*p = *(p + 1);
--cnt;
}
}
void clear(void){
if(arr != NULL)
delete[] arr;
arr = NULL;
cnt = 0;
mem = 8;
}
T& top(void) const { return arr[cnt - 1]; }
T& top(void){ return arr[cnt - 1]; }
T& operator [] (int index) const {
return arr[index];
}
T& operator [] (int index){
return arr[index];
}
size_t size(void) const {
return cnt;
}
bool empty(void) const {
return (! cnt);
}
private:
bool __alloc(void) {
if(arr == NULL){
arr = new (std::nothrow) T[mem];
if(arr == NULL)
return false;
} else if(cnt >= mem){
size_t tmem = cnt + mem / 3;
T* tmp = new (std::nothrow) T[tmem];
if(tmp == NULL)
return false;
T* e = arr + cnt;
T* d = tmp;
for(T* p = arr; p != e; ++p)
*d++ = *p;
delete[] arr;
arr = tmp;
mem = tmem;
}
return true;
}
};
//калькулятор только для целых чисел
template<typename T>
class expr {
public:
static T calc(const char* s){
const char* p = NULL;
size_t cnt = 0;
int scnt = 0;
std::string sb;
for(p = s; *p; ++p){
if(! isspace(*p))
sb += *p;
if(*p == '(')
++scnt;
else if(*p == ')')
--scnt;
}
if(scnt != 0)
err_msg("Ошибка скобочных выражений");
if(! sb.length())
err_msg("Строка пуста");
p = NULL;
return sub_calc(sb.c_str(), &p);
}
private:
static T sub_calc(const char* s, const char** p){
const char* t = s;
T num, val;
mstack<char> sops;
mstack<T> sval;
int neg = 0;
while(*s){
switch(*s){
case '-':
case '+':
case '*':
case '/':
if((s == t) || ((s > t) && is_start(*(s - 1)))){
neg = 1;
++s;
break;
}
sops.push(*s++);
break;
case '(':
val = sub_calc(s + 1, p);
if(neg){
val = 0 - val;
neg = 0;
}
s = *p;
sval.push(val);
calc_muldiv(sval, sops);
break;
case ')':
*p = s + 1;
goto end;
default:
if(! isdigit(*s))
err_msg("Неизвестный символ");
s = to_number(s, val);
if(neg){
val = 0 - val;
neg = 0;
}
sval.push(val);
calc_muldiv(sval, sops);
break;
}
}
end:
if(! sval.size())
err_msg("Неожиданный конец");
num = sval[0];
sval.removeAt(0);
for(size_t i = 0; (i < sops.size()) && (i < sval.size()); ++i){
switch(sops[i]){
case '+':
num += sval[i];
break;
case '-':
num -= sval[i];
break;
}
}
sops.clear();
sval.clear();
return num;
}
//приоритетные операции: умножение, деление
static void calc_muldiv(mstack<T>& sval, mstack<char>& sops){
T val;
char ch;
while(! sops.empty()){
ch = sops.top();
if((ch == '+') || (ch == '-'))
break;
val = sval.top();
sval.pop();
if(! sval.size())
err_msg("Неожиданный конец");
if(ch == '*')
sval.top() *= val;
else if(ch == '/'){
if(val == 0)
err_msg("Деление на нуль!");
sval.top() /= val;
}
sops.pop();
}
}
//конвертирование целых чисел из строки
static const char* to_number(const char* s, T& n){
static const T n_maxd = std::numeric_limits<T>::max() / 10;
for(n = 0; *s && isdigit(*s); ++s){
if(n >= n_maxd)
err_msg("Переполнение числа!");
n = n*10 + (T)(*s - '0');
}
return s;
}
static bool is_start(char c){
if(c == '+' || c == '-' || c == '*' || c == '/')
err_msg("Лишний арифметический символ");
return (c == '(');
}
};
int main(void){
try {
short n = -(7)-4*10-(-(2*(((-(-(-(-(-8))/2))))+5*4))+5*2)+(-(20/10*20)/(-1*20-10/2*2+1)*1-2*3+4*5/6*7/1);
char s[] = "-(7)-4*10-(-(2*(((-(-(-(-(-8))/2))))+5*4))+5*2)+(-(20/10*20)/(-1*20-10/2*2+1)*1-2*3+4*5/6*7/1)";
short res = expr<short>::calc(s);
std::cout << "my: " << res << std::endl;
std::cout << "cpp: " << n << std::endl << std::endl;
} catch(const std::domain_error& e){
std::cout << e.what() << std::endl;
}
//...
try {
long n = -(10 + 20) / 4 + 201200 - 77 * 4*4/(-7) * (-(-7)-7) - 1 + 70 - 5*2/9 - (-1 + 2 * 6 - 1);
char s[] = "-(10 + 20) / 4 + 201200 - 77 * 4*4/(-7) * (-(-7)-7) - 1 + 70 - 5*2/9 - (-1 + 2 * 6 - 1)";
long res = expr<long>::calc(s);
std::cout << "my: " << res << std::endl;
std::cout << "cpp: " << n << std::endl << std::endl;
} catch(const std::domain_error& e){
std::cout << e.what() << std::endl;
}
return 0;
}