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