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,std::vector<std::string> & result)190 void ConvertDal2Rpn(std::string formula, std::vector<std::string> &result)
191 {
192 result.clear();
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;
200 }
201 ReplaceSignNumberWithUnit(formula);
202 ReplaceSignNumber(formula);
203 formula = regex_replace(formula, space, "");
204 isValid = FilterCalcSpecialString(formula);
205 if (!isValid) {
206 return;
207 }
208 formula = regex_replace(formula, calc, "");
209 bool ret = PushOpStack(formula, curNum, result, opStack);
210 if (!ret) {
211 return;
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 }
222
CalculateFourOperationsExp(const std::string & exp,const Dimension & num1,const Dimension & num2,const std::function<double (const Dimension &)> & calcFunc,double & opRes)223 bool CalculateFourOperationsExp(const std::string& exp, const Dimension& num1, const Dimension& num2,
224 const std::function<double(const Dimension&)>& calcFunc, double& opRes)
225 {
226 if (exp == "+") {
227 if ((num1.Unit() == DimensionUnit::NONE && num2.Unit() != DimensionUnit::NONE) ||
228 (num1.Unit() != DimensionUnit::NONE && num2.Unit() == DimensionUnit::NONE)) {
229 return false;
230 }
231 opRes = calcFunc(num2) + calcFunc(num1);
232 } else if (exp == "-") {
233 if ((num1.Unit() == DimensionUnit::NONE && num2.Unit() != DimensionUnit::NONE) ||
234 (num1.Unit() != DimensionUnit::NONE && num2.Unit() == DimensionUnit::NONE)) {
235 return false;
236 }
237 opRes = calcFunc(num2) - calcFunc(num1);
238 } else if (exp == "*") {
239 if (num1.Unit() != DimensionUnit::NONE && num2.Unit() != DimensionUnit::NONE) {
240 return false;
241 }
242 opRes = calcFunc(num2) * calcFunc(num1);
243 } else if (exp == "/") {
244 if (NearZero(calcFunc(num1))) {
245 return false;
246 }
247 if ((num1.Unit() != DimensionUnit::NONE)) {
248 return false;
249 }
250 opRes = calcFunc(num2) / calcFunc(num1);
251 }
252 return true;
253 }
254
CalculateExpImpl(const std::vector<std::string> & rpnexp,const std::function<double (const Dimension &)> & calcFunc,std::vector<Dimension> & result,double & opRes)255 bool CalculateExpImpl(const std::vector<std::string>& rpnexp, const std::function<double(const Dimension&)>& calcFunc,
256 std::vector<Dimension>& result, double& opRes)
257 {
258 std::string ops = "+-*/()";
259 for (auto& i : rpnexp) {
260 if (ops.find(i) == ops.npos) {
261 std::string value = i;
262 Dimension dim = StringUtils::StringToDimensionWithUnit(value, DimensionUnit::PX, 0.0f, true);
263 if (dim.Unit() == DimensionUnit::INVALID) {
264 return false;
265 }
266 result.emplace_back(dim);
267 } else {
268 if (result.size() <= 1) {
269 return false;
270 }
271 Dimension num1 = result.back();
272 result.pop_back();
273 Dimension num2 = result.back();
274 result.pop_back();
275 auto ret = CalculateFourOperationsExp(i, num1, num2, calcFunc, opRes);
276 if (!ret) {
277 return ret;
278 }
279 if (num1.Unit() == DimensionUnit::NONE && num2.Unit() == DimensionUnit::NONE) {
280 result.emplace_back(Dimension(opRes, DimensionUnit::NONE));
281 continue;
282 }
283 result.emplace_back(Dimension(opRes, DimensionUnit::PX));
284 }
285 }
286 return true;
287 }
288
CalculateExp(const std::string & expression,const std::function<double (const Dimension &)> & calcFunc,const std::vector<std::string> & lengthString)289 double CalculateExp(const std::string& expression, const std::function<double(const Dimension&)>& calcFunc,
290 const std::vector<std::string>& lengthString)
291 {
292 std::vector<std::string> rpnexp;
293 if (!lengthString.empty()) {
294 rpnexp = lengthString;
295 } else {
296 ConvertDal2Rpn(expression, rpnexp);
297 }
298 std::vector<Dimension> result;
299 double opRes = 0.0;
300 auto ret = CalculateExpImpl(rpnexp, calcFunc, result, opRes);
301 if (!ret) {
302 return 0.0;
303 }
304 if (result.size() == 1 && result.back().Unit() != DimensionUnit::NONE) {
305 return calcFunc(result.back());
306 }
307 return 0.0;
308 }
309 } // namespace OHOS::Ace::StringExpression
310