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