2013-11-15 13:41:26 +01:00
|
|
|
|
/************************************************************************
|
2013-09-18 21:16:19 +02:00
|
|
|
|
**
|
2013-11-15 13:50:05 +01:00
|
|
|
|
** @file calculator.cpp
|
2013-11-15 13:41:26 +01:00
|
|
|
|
** @author Roman Telezhinsky <dismine@gmail.com>
|
2013-11-15 13:50:05 +01:00
|
|
|
|
** @date November 15, 2013
|
2013-09-18 21:16:19 +02:00
|
|
|
|
**
|
2013-11-15 13:41:26 +01:00
|
|
|
|
** @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.
|
2013-09-18 21:16:19 +02:00
|
|
|
|
**
|
2013-11-15 13:41:26 +01:00
|
|
|
|
** Valentina is free software: you can redistribute it and/or modify
|
2013-09-18 21:16:19 +02:00
|
|
|
|
** 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.
|
|
|
|
|
**
|
2013-10-27 13:36:29 +01:00
|
|
|
|
** Valentina is distributed in the hope that it will be useful,
|
2013-09-18 21:16:19 +02:00
|
|
|
|
** 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-11-15 13:41:26 +01:00
|
|
|
|
*************************************************************************/
|
2013-09-18 21:16:19 +02:00
|
|
|
|
|
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
|
|
|
|
|
|
2013-11-04 21:35:15 +01:00
|
|
|
|
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;
|
2013-09-10 14:29:06 +02:00
|
|
|
|
//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);
|
2013-09-10 14:29:06 +02:00
|
|
|
|
//qDebug()<<"Результат:"<<debugFormula;
|
2013-07-17 13:38:11 +02:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-04 21:35:15 +01:00
|
|
|
|
qreal Calculator::get_exp()
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
qreal result = 0;
|
|
|
|
|
get_token();
|
2013-11-04 21:35:15 +01:00
|
|
|
|
if (token.isEmpty())
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
serror(2);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
level2(&result);
|
|
|
|
|
putback(); /* возвращает последнюю считаную
|
|
|
|
|
лексему обратно во входной поток */
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Сложение или вычитание двух термов */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
void Calculator::level2(qreal *result)
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
QChar op;
|
|
|
|
|
qreal hold;
|
|
|
|
|
|
|
|
|
|
level3(result);
|
2013-11-04 21:35:15 +01:00
|
|
|
|
while ((op=token[0]) == '+' || op == '-')
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
get_token();
|
|
|
|
|
level3(&hold);
|
2013-11-04 21:35:15 +01:00
|
|
|
|
arith(op, result, &hold);
|
2013-07-17 13:38:11 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Вычисление произведения или частного двух фвкторов */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
void Calculator::level3(qreal *result)
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
QChar op;
|
|
|
|
|
qreal hold;
|
|
|
|
|
|
|
|
|
|
level4(result);
|
|
|
|
|
|
2013-11-04 21:35:15 +01:00
|
|
|
|
while ((op = token[0]) == '*' || op == '/' || op == '%')
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
get_token();
|
|
|
|
|
level4(&hold);
|
2013-11-04 21:35:15 +01:00
|
|
|
|
arith(op, result, &hold);
|
2013-07-17 13:38:11 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Обработка степени числа (целочисленной) */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
void Calculator::level4(qreal *result)
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
qreal hold;
|
|
|
|
|
|
|
|
|
|
level5(result);
|
2013-11-04 21:35:15 +01:00
|
|
|
|
if (token[0] == '^')
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
get_token();
|
|
|
|
|
level4(&hold);
|
|
|
|
|
arith('^', result, &hold);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Унарный + или - */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
void Calculator::level5(qreal *result)
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
QChar op;
|
|
|
|
|
|
|
|
|
|
op = '\0';
|
2013-11-04 21:35:15 +01:00
|
|
|
|
if ((token_type==DELIMITER) && (token[0]=='+' || token[0]=='-'))
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
op = token[0];
|
|
|
|
|
get_token();
|
|
|
|
|
}
|
|
|
|
|
level6(result);
|
2013-11-04 21:35:15 +01:00
|
|
|
|
if (op != '\0')
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
unary(op, result);
|
2013-11-04 21:35:15 +01:00
|
|
|
|
}
|
2013-07-17 13:38:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Обработка выражения в круглых скобках */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
void Calculator::level6(qreal *result)
|
|
|
|
|
{
|
|
|
|
|
if ((token[0] == '(') && (token_type == DELIMITER))
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
get_token();
|
|
|
|
|
level2(result);
|
2013-11-04 21:35:15 +01:00
|
|
|
|
if (token[0] != ')')
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
serror(1);
|
2013-11-04 21:35:15 +01:00
|
|
|
|
}
|
2013-07-17 13:38:11 +02:00
|
|
|
|
get_token();
|
|
|
|
|
} else
|
|
|
|
|
primitive(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Определение значения переменной по ее имени */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
void Calculator::primitive(qreal *result)
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
QString str;
|
2013-11-04 21:35:15 +01:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Выполнение специфицированной арифметики */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
void Calculator::arith(QChar o, qreal *r, qreal *h)
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
qreal t;//, ex;
|
|
|
|
|
|
2013-11-04 21:35:15 +01:00
|
|
|
|
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;
|
2013-11-04 21:35:15 +01:00
|
|
|
|
break;
|
2013-11-12 19:29:03 +01:00
|
|
|
|
default:
|
|
|
|
|
break;
|
2013-11-04 21:35:15 +01:00
|
|
|
|
}
|
2013-07-17 13:38:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Изменение знака */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
void Calculator::unary(QChar o, qreal *r)
|
|
|
|
|
{
|
|
|
|
|
if (o=='-')
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
*r = -(*r);
|
2013-11-04 21:35:15 +01:00
|
|
|
|
}
|
2013-07-17 13:38:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Поиск значения переменной */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
qreal Calculator::find_var(QString s)
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
bool ok = false;
|
|
|
|
|
qreal value = data->FindVar(s, &ok);
|
2013-11-04 21:35:15 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* выдать сообщение об ошибке */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
void Calculator::serror(qint32 error)
|
|
|
|
|
{
|
|
|
|
|
QString e[]=
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
"Синтаксическая ошибка",
|
|
|
|
|
"Непарные круглые скобки",
|
|
|
|
|
"Это не выражение",
|
|
|
|
|
"Предполагается символ равенства",
|
2013-07-25 14:00:51 +02:00
|
|
|
|
"Не переменная"
|
2013-11-04 21:35:15 +01:00
|
|
|
|
};
|
2013-07-25 14:00:51 +02:00
|
|
|
|
errorMsg->clear();
|
|
|
|
|
*errorMsg = e[error];
|
2013-11-04 21:35:15 +01:00
|
|
|
|
qDebug()<<e[error];
|
2013-07-17 13:38:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Поиск соответствия внутреннего формата для
|
|
|
|
|
текущей лексемы в таблице лексем.
|
|
|
|
|
*/
|
2013-11-04 21:35:15 +01: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" разделитель */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
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-11-04 21:35:15 +01:00
|
|
|
|
}
|
2013-07-17 13:38:11 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Возвращает 1, если "с" пробел или табуляция */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
bool Calculator::iswhite(QChar c)
|
|
|
|
|
{
|
|
|
|
|
if (c==' ' || c=='\t')
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
return true;
|
2013-11-04 21:35:15 +01:00
|
|
|
|
}
|
2013-07-17 13:38:11 +02:00
|
|
|
|
else
|
2013-11-04 21:35:15 +01:00
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
return false;
|
2013-11-04 21:35:15 +01:00
|
|
|
|
}
|
2013-07-17 13:38:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-04 21:35:15 +01:00
|
|
|
|
void Calculator::get_token()
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
QString *temp;
|
|
|
|
|
|
|
|
|
|
token_type=0; tok=0;
|
|
|
|
|
token.clear();
|
|
|
|
|
temp=&token;
|
|
|
|
|
|
2013-11-04 21:35:15 +01:00
|
|
|
|
if (prog[index]=='\0')
|
|
|
|
|
{ /* Конец файла */
|
2013-07-17 13:38:11 +02:00
|
|
|
|
token="\0";
|
|
|
|
|
tok=FINISHED;
|
|
|
|
|
token_type=DELIMITER;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-04 21:35:15 +01:00
|
|
|
|
while (iswhite(prog[index]))
|
|
|
|
|
{
|
|
|
|
|
++index; /* пропуск пробелов */
|
|
|
|
|
}
|
2013-07-17 13:38:11 +02:00
|
|
|
|
|
2013-11-04 21:35:15 +01: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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-04 21:35:15 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
2013-11-04 21:35:15 +01:00
|
|
|
|
if (prog[index]=='"')
|
|
|
|
|
{ /* строка в кавычках */
|
2013-07-17 13:38:11 +02:00
|
|
|
|
index++;
|
2013-11-04 21:35:15 +01:00
|
|
|
|
while (prog[index] != '"' && prog[index] != '\r')
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
temp->append(prog[index]);
|
|
|
|
|
index++;
|
|
|
|
|
}
|
2013-11-04 21:35:15 +01:00
|
|
|
|
if (prog[index]=='\r')
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
serror(1);
|
2013-11-04 21:35:15 +01:00
|
|
|
|
}
|
2013-07-17 13:38:11 +02:00
|
|
|
|
index++;temp->append("\0");
|
|
|
|
|
token_type=QUOTE;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-11-04 21:35:15 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-04 21:35:15 +01:00
|
|
|
|
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");
|
|
|
|
|
|
|
|
|
|
/* Просматривается, если строка есть команда или переменная */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
if (token_type==STRING)
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
tok=look_up(token); /* преобразование во внутренний
|
|
|
|
|
формат */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
if (tok == false)
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
token_type = VARIABLE;
|
2013-11-04 21:35:15 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
token_type = COMMAND; /* это команда */
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-07-17 13:38:11 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-04 21:35:15 +01:00
|
|
|
|
bool Calculator::StrChr(QString string, QChar c)
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
return string.contains(c, Qt::CaseInsensitive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Возвращает лексему обратно во входной поток */
|
2013-11-04 21:35:15 +01:00
|
|
|
|
void Calculator::putback()
|
|
|
|
|
{
|
2013-07-17 13:38:11 +02:00
|
|
|
|
QString t;
|
|
|
|
|
t = token;
|
|
|
|
|
index = index - t.size();
|
|
|
|
|
}
|