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