1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "base/utils/string_expression.h"
17 
18 #include <regex>
19 #include "base/utils/string_utils.h"
20 
21 namespace OHOS::Ace::StringExpression {
InitMapping(std::map<std::string,int> & mapping)22 void InitMapping(std::map<std::string, int>& mapping)
23 {
24     mapping["+"] = 0;
25     mapping["-"] = 0;
26     mapping["*"] = 1;
27     mapping["/"] = 1;
28     mapping["("] = 2;
29     mapping[")"] = 2;
30 }
31 
CheckCalcIsValid(const std::string & formula)32 bool CheckCalcIsValid(const std::string& formula)
33 {
34     std::regex space(" ");
35     std::string formulaNoSpace = regex_replace(formula, space, "");
36 
37     std::smatch result;
38     std::string substr;
39     std::regex pattern("(\\-|\\+|\\/|\\*)(\\({0,})(calc)");
40     while (std::regex_search(formulaNoSpace, result, pattern)) {
41         size_t leftBracketCount = 0;
42         std::smatch leftBracket;
43         std::regex leftBracketPattern("\\(");
44         substr = result.suffix().str();
45 
46         while (std::regex_search(substr, leftBracket, leftBracketPattern)) {
47             ++leftBracketCount;
48             substr = leftBracket.suffix().str();
49         }
50 
51         size_t rightBracketCount = 0;
52         std::smatch rightBracket;
53         std::regex rightBracketPattern("\\)");
54         substr = result.suffix().str();
55 
56         while (std::regex_search(substr, rightBracket, rightBracketPattern)) {
57             ++rightBracketCount;
58             substr = rightBracket.suffix().str();
59         }
60 
61         if (leftBracketCount == rightBracketCount) {
62             return false;
63         }
64         formulaNoSpace = result.suffix().str();
65     }
66     return true;
67 }
68 
ReplaceSignNumber(std::string & formula)69 void ReplaceSignNumber(std::string& formula)
70 {
71     std::regex pattern("(\\-|\\+)\\d+(\\.\\d+)?");
72     std::smatch result;
73     std::string matchStr;
74     std::string catStr;
75     std::string mergeStr;
76     std::string leftstr = formula;
77     while (std::regex_search(leftstr, result, pattern)) {
78         if (result.size() == 0) {
79             break;
80         }
81         matchStr = result[0];
82         if (matchStr.empty()) {
83             break;
84         }
85         catStr = matchStr[0];
86         catStr = " (0 " + catStr;
87         catStr = catStr + " " + matchStr.substr(1) + ")";
88         mergeStr += result.prefix().str() + catStr;
89         leftstr = result.suffix().str();
90     }
91     mergeStr += leftstr;
92     if (!mergeStr.empty()) {
93         formula = mergeStr;
94     }
95 }
96 
ReplaceSignNumberWithUnit(std::string & formula)97 void ReplaceSignNumberWithUnit(std::string& formula)
98 {
99     std::regex pattern("(\\-|\\+)\\d+(\\.\\d+)?(px|vp|%|fp|lpx)");
100     std::smatch result;
101     std::string matchStr;
102     std::string catStr;
103     std::string mergeStr;
104     std::string leftstr = formula;
105     while (std::regex_search(leftstr, result, pattern)) {
106         if (result.size() == 0) {
107             break;
108         }
109         matchStr = result[0];
110         if (matchStr.empty()) {
111             break;
112         }
113         catStr = matchStr[0];
114         catStr = " (0px " + catStr;
115         catStr = catStr + " " + matchStr.substr(1) + ")";
116         mergeStr += result.prefix().str() + catStr;
117         leftstr = result.suffix().str();
118     }
119     mergeStr += leftstr;
120     if (!mergeStr.empty()) {
121         formula = mergeStr;
122     }
123 }
124 
PushOpStack(const std::string & formula,std::string & curNum,std::vector<std::string> & result,std::vector<std::string> & opStack)125 bool PushOpStack(const std::string& formula, std::string& curNum, std::vector<std::string>& result,
126     std::vector<std::string>& opStack)
127 {
128     std::string ops = "+-*/()";
129     std::map<std::string, int> opMapping;
130     InitMapping(opMapping);
131     std::string curOp;
132     for (char i : formula) {
133         if (ops.find(i) == ops.npos) {
134             curNum += i;
135         } else {
136             if (!curNum.empty()) {
137                 result.emplace_back(curNum);
138                 curNum.clear();
139             }
140             curOp = i;
141             if (opStack.empty()) {
142                 opStack.emplace_back(curOp);
143             } else if (curOp == "(") {
144                 opStack.emplace_back(curOp);
145             } else if (curOp == ")") {
146                 while (opStack.back() != "(") {
147                     result.emplace_back(opStack.back());
148                     opStack.pop_back();
149                     if (opStack.empty()) {
150                         LOGE("ExpressionError, opStack is empty");
151                         result.emplace_back("0");
152                         return false;
153                     }
154                 }
155                 opStack.pop_back();
156             } else if (opStack.back() == "(") {
157                 opStack.emplace_back(curOp);
158             } else if (opMapping[curOp] > opMapping[opStack.back()] && (!opStack.empty())) {
159                 opStack.emplace_back(curOp);
160             } else {
161                 while ((opStack.back() != "(") && (opMapping[opStack.back()] >= opMapping[curOp])) {
162                     result.emplace_back(opStack.back());
163                     opStack.pop_back();
164                     if (opStack.empty())
165                         break;
166                 }
167                 opStack.emplace_back(curOp);
168             }
169         }
170     }
171     return true;
172 }
173 
FilterCalcSpecialString(const std::string & formula)174 bool FilterCalcSpecialString(const std::string& formula)
175 {
176     if (formula.empty()) {
177         return false;
178     }
179     // check calc(100%)/2
180     size_t startPos = formula.find("calc");
181     size_t endPos = formula.rfind(")");
182     bool isSingleCalc = (startPos == 0) && (endPos == formula.size() - 1);
183     // check calc(100%())
184     size_t emptyBracketPos = formula.find("()");
185     bool isNotIncludeEmptyBracket = (emptyBracketPos == std::string::npos);
186 
187     return (isSingleCalc && isNotIncludeEmptyBracket);
188 }
189 
ConvertDal2Rpn(std::string formula)190 std::vector<std::string> ConvertDal2Rpn(std::string formula)
191 {
192     std::vector<std::string> result;
193     std::vector<std::string> opStack;
194     std::string curNum;
195     std::regex calc("calc");
196     std::regex space(" ");
197     bool isValid = CheckCalcIsValid(formula);
198     if (!isValid) {
199         return result;
200     }
201     ReplaceSignNumberWithUnit(formula);
202     ReplaceSignNumber(formula);
203     formula = regex_replace(formula, space, "");
204     isValid = FilterCalcSpecialString(formula);
205     if (!isValid) {
206         return result;
207     }
208     formula = regex_replace(formula, calc, "");
209     bool ret = PushOpStack(formula, curNum, result, opStack);
210     if (!ret) {
211         return result;
212     }
213     if (!curNum.empty()) {
214         result.emplace_back(curNum);
215         curNum.clear();
216     }
217     while (!opStack.empty()) {
218         result.emplace_back(opStack.back());
219         opStack.pop_back();
220     }
221     return result;
222 }
223 
CalculateFourOperationsExp(const std::string & exp,const Dimension & num1,const Dimension & num2,const std::function<double (const Dimension &)> & calcFunc,double & opRes)224 bool CalculateFourOperationsExp(const std::string& exp, const Dimension& num1, const Dimension& num2,
225     const std::function<double(const Dimension&)>& calcFunc, double& opRes)
226 {
227     if (exp == "+") {
228         if ((num1.Unit() == DimensionUnit::NONE && num2.Unit() != DimensionUnit::NONE) ||
229             (num1.Unit() != DimensionUnit::NONE && num2.Unit() == DimensionUnit::NONE)) {
230             return false;
231         }
232         opRes = calcFunc(num2) + calcFunc(num1);
233     } else if (exp == "-") {
234         if ((num1.Unit() == DimensionUnit::NONE && num2.Unit() != DimensionUnit::NONE) ||
235             (num1.Unit() != DimensionUnit::NONE && num2.Unit() == DimensionUnit::NONE)) {
236             return false;
237         }
238         opRes = calcFunc(num2) - calcFunc(num1);
239     } else if (exp == "*") {
240         if (num1.Unit() != DimensionUnit::NONE && num2.Unit() != DimensionUnit::NONE) {
241             return false;
242         }
243         opRes = calcFunc(num2) * calcFunc(num1);
244     } else if (exp == "/") {
245         if (NearZero(calcFunc(num1))) {
246             return false;
247         }
248         if ((num1.Unit() != DimensionUnit::NONE)) {
249             return false;
250         }
251         opRes = calcFunc(num2) / calcFunc(num1);
252     }
253     return true;
254 }
255 
CalculateExpImpl(const std::vector<std::string> & rpnexp,const std::function<double (const Dimension &)> & calcFunc,std::vector<Dimension> & result,double & opRes)256 bool CalculateExpImpl(const std::vector<std::string>& rpnexp, const std::function<double(const Dimension&)>& calcFunc,
257     std::vector<Dimension>& result, double& opRes)
258 {
259     std::string ops = "+-*/()";
260     for (auto& i : rpnexp) {
261         if (ops.find(i) == ops.npos) {
262             std::string value = i;
263             Dimension dim = StringUtils::StringToDimensionWithUnit(value, DimensionUnit::PX, 0.0f, true);
264             if (dim.Unit() == DimensionUnit::INVALID) {
265                 return false;
266             }
267             result.emplace_back(dim);
268         } else {
269             if (result.size() <= 1) {
270                 return false;
271             }
272             Dimension num1 = result.back();
273             result.pop_back();
274             Dimension num2 = result.back();
275             result.pop_back();
276             auto ret = CalculateFourOperationsExp(i, num1, num2, calcFunc, opRes);
277             if (!ret) {
278                 return ret;
279             }
280             if (num1.Unit() == DimensionUnit::NONE && num2.Unit() == DimensionUnit::NONE) {
281                 result.emplace_back(Dimension(opRes, DimensionUnit::NONE));
282                 continue;
283             }
284             result.emplace_back(Dimension(opRes, DimensionUnit::PX));
285         }
286     }
287     return true;
288 }
289 
CalculateExp(const std::string & expression,const std::function<double (const Dimension &)> & calcFunc)290 double CalculateExp(const std::string& expression, const std::function<double(const Dimension&)>& calcFunc)
291 {
292     std::vector<std::string> rpnexp = ConvertDal2Rpn(expression);
293     std::vector<Dimension> result;
294     double opRes = 0.0;
295     auto ret = CalculateExpImpl(rpnexp, calcFunc, result, opRes);
296     if (!ret) {
297         return 0.0;
298     }
299     if (result.size() == 1 && result.back().Unit() != DimensionUnit::NONE) {
300         return calcFunc(result.back());
301     }
302     return 0.0;
303 }
304 } // namespace OHOS::Ace::StringExpression
305