File indexing completed on 2024-06-16 05:12:09

0001 /***************************************************************************
0002  *   Copyright (C) 2016 by Renaud Guezennec                                *
0003  *   https://rolisteam.org/contact                                      *
0004  *                                                                         *
0005  *   rolisteam is free software; you can redistribute it and/or modify     *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
0019  ***************************************************************************/
0020 #include "charactersheet_formula/parsingtoolformula.h"
0021 #include "charactersheet_formula/nodes/operator.h"
0022 
0023 #include "charactersheet_formula/nodes/parenthesesfnode.h"
0024 #include "charactersheet_formula/nodes/valuefnode.h"
0025 #include <QDebug>
0026 
0027 namespace Formula
0028 {
0029 
0030 ParsingToolFormula::ParsingToolFormula()
0031 {
0032     // ABS,MIN,MAX,IF,FLOOR,CEIL,AVG
0033     m_hashOp.insert({QStringLiteral("abs"), ABS});
0034     m_hashOp.insert({QStringLiteral("min"), MIN});
0035     m_hashOp.insert({QStringLiteral("max"), MAX});
0036     m_hashOp.insert({QStringLiteral("concat"), CONCAT});
0037     m_hashOp.insert({QStringLiteral("floor"), FLOOR});
0038     m_hashOp.insert({QStringLiteral("ceil"), CEIL});
0039     m_hashOp.insert({QStringLiteral("avg"), AVG});
0040 
0041     m_arithmeticOperation.insert({QStringLiteral("+"), ScalarOperatorFNode::PLUS});
0042     m_arithmeticOperation.insert({QStringLiteral("-"), ScalarOperatorFNode::MINUS});
0043     m_arithmeticOperation.insert({QStringLiteral("*"), ScalarOperatorFNode::MULTIPLICATION});
0044     m_arithmeticOperation.insert({QStringLiteral("x"), ScalarOperatorFNode::MULTIPLICATION});
0045     m_arithmeticOperation.insert({QStringLiteral("/"), ScalarOperatorFNode::DIVIDE});
0046     m_arithmeticOperation.insert({QStringLiteral("รท"), ScalarOperatorFNode::DIVIDE});
0047 }
0048 ParsingToolFormula::~ParsingToolFormula() {}
0049 
0050 FormulaNode* ParsingToolFormula::getLatestNode(FormulaNode* node)
0051 {
0052     if(nullptr == node)
0053         return nullptr;
0054     FormulaNode* next= node;
0055     while(nullptr != next->next())
0056     {
0057         next= next->next();
0058     }
0059     return next;
0060 }
0061 
0062 const QHash<QString, QString> ParsingToolFormula::getVariableHash() const
0063 {
0064     return m_variableHash;
0065 }
0066 
0067 void ParsingToolFormula::setVariableHash(const QHash<QString, QString>& variableHash)
0068 {
0069     m_variableHash= variableHash;
0070 }
0071 bool ParsingToolFormula::readFormula(QString& str, FormulaNode*& previous)
0072 {
0073     if(str.startsWith('='))
0074     {
0075         str= str.remove(0, 1);
0076     }
0077     FormulaNode* operandNode= nullptr;
0078     bool found= false;
0079     if(readParenthese(str, operandNode))
0080     {
0081         previous= operandNode;
0082         found= true;
0083     }
0084     else if(readOperand(str, operandNode))
0085     {
0086         previous= operandNode;
0087         found= true;
0088     }
0089     if(found)
0090     {
0091         operandNode= getLatestNode(operandNode);
0092         while(readScalarOperator(str, operandNode))
0093             ;
0094     }
0095 
0096     return found;
0097 }
0098 bool ParsingToolFormula::readParenthese(QString& str, FormulaNode*& previous)
0099 {
0100     if(str.startsWith("("))
0101     {
0102         str= str.remove(0, 1);
0103         FormulaNode* internalNode= nullptr;
0104         if(readFormula(str, internalNode))
0105         {
0106             ParenthesesFNode* node= new ParenthesesFNode();
0107             node->setInternalNode(internalNode);
0108             previous= node;
0109 
0110             if(str.startsWith(")"))
0111             {
0112                 str= str.remove(0, 1);
0113                 return true;
0114             }
0115         }
0116     }
0117     return false;
0118 }
0119 
0120 bool ParsingToolFormula::readScalarOperator(QString& str, FormulaNode* previous)
0121 {
0122     ScalarOperatorFNode::ArithmeticOperator ope;
0123     auto it= std::find_if(m_arithmeticOperation.begin(), m_arithmeticOperation.end(),
0124                           [str](const std::pair<QString, ScalarOperatorFNode::ArithmeticOperator>& pair) {
0125                               return str.startsWith(pair.first);
0126                           });
0127 
0128     if(it == m_arithmeticOperation.end())
0129         return false;
0130 
0131     ope= (*it).second;
0132     str= str.remove(0, (*it).first.size());
0133 
0134     ScalarOperatorFNode* node= new ScalarOperatorFNode();
0135     node->setArithmeticOperator(ope);
0136 
0137     FormulaNode* internal= nullptr;
0138     readFormula(str, internal);
0139 
0140     node->setInternalNode(internal);
0141 
0142     if(nullptr == internal)
0143     {
0144         delete node;
0145         return false;
0146     }
0147     if(node->getPriority() >= internal->getPriority())
0148     {
0149         node->setNext(internal->next());
0150         internal->setNext(nullptr);
0151     }
0152     previous->setNext(node);
0153     return true;
0154 }
0155 
0156 bool ParsingToolFormula::readOperand(QString& str, FormulaNode*& previous)
0157 {
0158     if(readNumber(str, previous))
0159     {
0160         return true;
0161     }
0162     else if(readFieldRef(str, previous))
0163     {
0164         return true;
0165     }
0166     else if(readOperator(str, previous))
0167     {
0168         return true;
0169     }
0170     else if(readStringValue(str, previous))
0171     {
0172         return true;
0173     }
0174     return false;
0175 }
0176 bool ParsingToolFormula::readStringValue(QString& str, FormulaNode*& previous)
0177 {
0178     if(str.isEmpty())
0179         return false;
0180 
0181     QString strResult;
0182     if(str.startsWith("\""))
0183     {
0184         int i= 0;
0185         str= str.remove(0, 1);
0186         while(i < str.length()
0187               && str[i] != '"') //&& (str[i].isLetterOrNumber() || str[i].isPunct() || str[i].isSpace())
0188         {
0189             strResult+= str[i];
0190             ++i;
0191         }
0192 
0193         str= str.remove(0, strResult.size() + 1);
0194         ValueFNode* nodeV= new ValueFNode();
0195         nodeV->setValue(strResult);
0196         previous= nodeV;
0197         return true;
0198     }
0199 
0200     return false;
0201 }
0202 
0203 bool ParsingToolFormula::readOperator(QString& str, FormulaNode*& previous)
0204 {
0205     auto it= std::find_if(m_hashOp.begin(), m_hashOp.end(),
0206                           [str](const std::pair<QString, ParsingToolFormula::FormulaOperator>& pair) {
0207                               return str.startsWith(pair.first);
0208                           });
0209 
0210     if(it == m_hashOp.end())
0211         return false;
0212 
0213     QString key= it->first;
0214     str= str.remove(0, key.size());
0215     OperatorFNode* node= new OperatorFNode();
0216     previous= node;
0217     node->setOperator(it->second);
0218     FormulaNode* nextNode= nullptr;
0219     if(str.startsWith("("))
0220     {
0221         str= str.remove(0, 1);
0222         while(readFormula(str, nextNode)) //&& !str.startsWith(")")
0223         {                                 // reading parameter loop
0224             node->addParameter(nextNode);
0225             nextNode= nullptr;
0226             if(str.startsWith(","))
0227             {
0228                 str= str.remove(0, 1);
0229             }
0230         }
0231         if(str.startsWith(")"))
0232         {
0233             str= str.remove(0, 1);
0234         }
0235     }
0236     return true;
0237 }
0238 
0239 bool ParsingToolFormula::readFieldRef(QString& str, FormulaNode*& previous)
0240 {
0241     if(str.isEmpty())
0242         return false;
0243     if(str.startsWith("${"))
0244     {
0245         str= str.remove(0, 2);
0246     }
0247     QString key;
0248     int post= str.indexOf('}');
0249     key= str.left(post);
0250 
0251     if(m_variableHash.contains(key))
0252     {
0253         QString value= m_variableHash.value(key);
0254         bool ok;
0255         qreal valueR= value.toDouble(&ok);
0256         if(ok)
0257         {
0258             str= str.remove(0, post + 1);
0259             ValueFNode* nodeV= new ValueFNode();
0260             nodeV->setValue(valueR);
0261             previous= nodeV;
0262             return true;
0263         }
0264     }
0265 
0266     return false;
0267 }
0268 
0269 bool ParsingToolFormula::readNumber(QString& str, FormulaNode*& previous)
0270 {
0271     if(str.isEmpty())
0272         return false;
0273 
0274     QString number;
0275     int i= 0;
0276     while(i < str.length() && ((str[i].isNumber()) || (str[i] == '.') || ((i == 0) && (str[i] == '-'))))
0277     {
0278         number+= str[i];
0279         ++i;
0280     }
0281 
0282     bool ok;
0283     qreal r= number.toDouble(&ok);
0284     if(ok)
0285     {
0286         str= str.remove(0, number.size());
0287         ValueFNode* nodeV= new ValueFNode();
0288         nodeV->setValue(r);
0289         previous= nodeV;
0290         return true;
0291     }
0292 
0293     return false;
0294 }
0295 } // namespace Formula