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