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
21namespace OHOS::Ace::StringExpression {
22void 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
32bool 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
69void 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
97void 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
125bool 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
174bool 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
190std::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
224bool 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
256bool 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
290double 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