valentina/container/calculator.cpp

401 lines
9.7 KiB
C++
Raw Normal View History

/************************************************************************
**
** @file calculator.cpp
** @author Roman Telezhinsky <dismine@gmail.com>
** @date November 15, 2013
**
** @brief
** @copyright
** This source code is part of the Valentine project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2013 Valentina project
** <https://bitbucket.org/dismine/valentina> All Rights Reserved.
**
** Valentina 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.
**
** Valentina 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 Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
2013-07-17 13:38:11 +02:00
#include "calculator.h"
#define DELIMITER 1
#define VARIABLE 2
#define NUMBER 3
#define COMMAND 4
#define STRING 5
#define QUOTE 6
#define FINISHED 10
#define EOL 9
qreal Calculator::eval(QString prog, QString *errorMsg)
{
2013-07-25 14:00:51 +02:00
this->errorMsg = errorMsg;
this->errorMsg->clear();
2013-07-17 13:38:11 +02:00
debugFormula.clear();
this->prog = prog;
//qDebug()<<"Формула: "<<prog;
2013-07-17 13:38:11 +02:00
index = 0;
qreal result = get_exp();
QString str = QString(" = %1").arg(result, 0, 'f', 3);
debugFormula.append(str);
//qDebug()<<"Результат:"<<debugFormula;
2013-07-17 13:38:11 +02:00
return result;
}
qreal Calculator::get_exp()
{
2013-07-17 13:38:11 +02:00
qreal result = 0;
get_token();
if (token.isEmpty())
{
2013-07-17 13:38:11 +02:00
serror(2);
return 0;
}
level2(&result);
putback(); /* возвращает последнюю считаную
лексему обратно во входной поток */
return result;
}
/* Сложение или вычитание двух термов */
void Calculator::level2(qreal *result)
{
2013-07-17 13:38:11 +02:00
QChar op;
qreal hold;
level3(result);
while ((op=token[0]) == '+' || op == '-')
{
2013-07-17 13:38:11 +02:00
get_token();
level3(&hold);
arith(op, result, &hold);
2013-07-17 13:38:11 +02:00
}
}
/* Вычисление произведения или частного двух фвкторов */
void Calculator::level3(qreal *result)
{
2013-07-17 13:38:11 +02:00
QChar op;
qreal hold;
level4(result);
while ((op = token[0]) == '*' || op == '/' || op == '%')
{
2013-07-17 13:38:11 +02:00
get_token();
level4(&hold);
arith(op, result, &hold);
2013-07-17 13:38:11 +02:00
}
}
/* Обработка степени числа (целочисленной) */
void Calculator::level4(qreal *result)
{
2013-07-17 13:38:11 +02:00
qreal hold;
level5(result);
if (token[0] == '^')
{
2013-07-17 13:38:11 +02:00
get_token();
level4(&hold);
arith('^', result, &hold);
}
}
/* Унарный + или - */
void Calculator::level5(qreal *result)
{
2013-07-17 13:38:11 +02:00
QChar op;
op = '\0';
if ((token_type==DELIMITER) && (token[0]=='+' || token[0]=='-'))
{
2013-07-17 13:38:11 +02:00
op = token[0];
get_token();
}
level6(result);
if (op != '\0')
{
2013-07-17 13:38:11 +02:00
unary(op, result);
}
2013-07-17 13:38:11 +02:00
}
/* Обработка выражения в круглых скобках */
void Calculator::level6(qreal *result)
{
if ((token[0] == '(') && (token_type == DELIMITER))
{
2013-07-17 13:38:11 +02:00
get_token();
level2(result);
if (token[0] != ')')
{
2013-07-17 13:38:11 +02:00
serror(1);
}
2013-07-17 13:38:11 +02:00
get_token();
} else
primitive(result);
}
/* Определение значения переменной по ее имени */
void Calculator::primitive(qreal *result)
{
2013-07-17 13:38:11 +02:00
QString str;
switch (token_type)
{
2013-07-17 13:38:11 +02:00
case VARIABLE:
*result = find_var(token);
str = QString("%1").arg(*result, 0, 'f', 3);
debugFormula.append(str);
get_token();
return;
case NUMBER:
*result = token.toDouble();
str = QString("%1").arg(*result, 0, 'f', 3);
debugFormula.append(str);
get_token();
return;
default:
serror(0);
}
}
/* Выполнение специфицированной арифметики */
void Calculator::arith(QChar o, qreal *r, qreal *h)
{
2013-07-17 13:38:11 +02:00
qreal t;//, ex;
switch (o.toLatin1())
{
2013-07-17 13:38:11 +02:00
case '-':
*r = *r-*h;
break;
case '+':
*r = *r+*h;
break;
case '*':
*r = *r * *h;
break;
case '/':
*r = (*r)/(*h);
break;
case '%':
t = (*r)/(*h);
*r = *r-(t*(*h));
break;
case '^':
*r = pow(*r, *h);
// ex =*r;
// if(*h==0) {
// *r = 1;
// break;
// }
// for(t=*h-1; t>0; --t)
// *r = (*r) * ex;
break;
default:
break;
}
2013-07-17 13:38:11 +02:00
}
/* Изменение знака */
void Calculator::unary(QChar o, qreal *r)
{
if (o=='-')
{
2013-07-17 13:38:11 +02:00
*r = -(*r);
}
2013-07-17 13:38:11 +02:00
}
/* Поиск значения переменной */
qreal Calculator::find_var(QString s)
{
2013-07-17 13:38:11 +02:00
bool ok = false;
qreal value = data->FindVar(s, &ok);
if (ok == false)
{
2013-07-30 15:09:34 +02:00
qDebug()<<s;
2013-07-17 13:38:11 +02:00
serror(4); /* не переменная */
return 0;
}
return value;
}
/* выдать сообщение об ошибке */
void Calculator::serror(qint32 error)
{
QString e[]=
{
2013-07-17 13:38:11 +02:00
"Синтаксическая ошибка",
"Непарные круглые скобки",
"Это не выражение",
"Предполагается символ равенства",
2013-07-25 14:00:51 +02:00
"Не переменная"
};
2013-07-25 14:00:51 +02:00
errorMsg->clear();
*errorMsg = e[error];
qDebug()<<e[error];
2013-07-17 13:38:11 +02:00
}
/* Поиск соответствия внутреннего формата для
текущей лексемы в таблице лексем.
*/
char Calculator::look_up(QString s)
{
2013-07-17 13:38:11 +02:00
QString p;
/* преобразование к нижнему регистру */
p = s;
p = p.toLower();
/* просматривается, если лексема обнаружена в
таблице */
/*
*у нас більше немає команд що потрібно опрацьовувати
*/
// if(commands.contains(p)){
// return commands[p];
// }
return 0; /* нераспознанная команда */
}
/* Возвращает "истину", если "c" разделитель */
bool Calculator::isdelim(QChar c)
{
if (StrChr(" ;,+-<>/*%^=()", c) || c=='\n' || c=='\r' || c=='\0')
{
2013-07-17 13:38:11 +02:00
return true;
}
2013-07-17 13:38:11 +02:00
return false;
}
/* Возвращает 1, если "с" пробел или табуляция */
bool Calculator::iswhite(QChar c)
{
if (c==' ' || c=='\t')
{
2013-07-17 13:38:11 +02:00
return true;
}
2013-07-17 13:38:11 +02:00
else
{
2013-07-17 13:38:11 +02:00
return false;
}
2013-07-17 13:38:11 +02:00
}
void Calculator::get_token()
{
2013-07-17 13:38:11 +02:00
QString *temp;
token_type=0; tok=0;
token.clear();
temp=&token;
if (prog[index]=='\0')
{ /* Конец файла */
2013-07-17 13:38:11 +02:00
token="\0";
tok=FINISHED;
token_type=DELIMITER;
return;
}
while (iswhite(prog[index]))
{
++index; /* пропуск пробелов */
}
2013-07-17 13:38:11 +02:00
if (prog[index]=='\r')
{ /* crtl */
2013-07-17 13:38:11 +02:00
++index; ++index;
tok= EOL; token='\r';
token.append('\n');token.append("\0");
token_type = DELIMITER;
return;
}
if (StrChr("+-*^/%=;(),><", prog[index]))
{ /* разделитель */
2013-07-17 13:38:11 +02:00
*temp=prog[index];
index++; /* переход на следующую позицию */
temp->append("\0");
token_type=DELIMITER;
debugFormula.append(token);
return;
}
if (prog[index]=='"')
{ /* строка в кавычках */
2013-07-17 13:38:11 +02:00
index++;
while (prog[index] != '"' && prog[index] != '\r')
{
2013-07-17 13:38:11 +02:00
temp->append(prog[index]);
index++;
}
if (prog[index]=='\r')
{
2013-07-17 13:38:11 +02:00
serror(1);
}
2013-07-17 13:38:11 +02:00
index++;temp->append("\0");
token_type=QUOTE;
return;
}
if (prog[index].isDigit())
{ /* число */
while (isdelim(prog[index]) == false)
{
2013-07-17 13:38:11 +02:00
temp->append(prog[index]);
index++;
}
temp->append('\0');
token_type = NUMBER;
return;
}
if (prog[index].isPrint())
{ /* переменная или команда */
while (isdelim(prog[index]) == false)
{
2013-07-17 13:38:11 +02:00
temp->append(prog[index]);
index++;
}
token_type=STRING;
}
temp->append("\0");
/* Просматривается, если строка есть команда или переменная */
if (token_type==STRING)
{
2013-07-17 13:38:11 +02:00
tok=look_up(token); /* преобразование во внутренний
формат */
if (tok == false)
{
2013-07-17 13:38:11 +02:00
token_type = VARIABLE;
}
else
{
token_type = COMMAND; /* это команда */
}
}
2013-07-17 13:38:11 +02:00
return;
}
bool Calculator::StrChr(QString string, QChar c)
{
2013-07-17 13:38:11 +02:00
return string.contains(c, Qt::CaseInsensitive);
}
/* Возвращает лексему обратно во входной поток */
void Calculator::putback()
{
2013-07-17 13:38:11 +02:00
QString t;
t = token;
index = index - t.size();
}