| 306 | } |
| 307 | |
| 308 | FieldGeneratorPtr ExpressionParser::parseIdentifierExpr(LexInfo& lex) const { |
| 309 | // Make a nice error message if we couldn't find the identifier |
| 310 | const auto generatorNotFoundErrorMessage = [&](const std::string& name) -> std::string { |
| 311 | const std::string message_template = _( |
| 312 | R"(Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so you |
| 313 | may need to change your input file. |
| 314 | {})"); |
| 315 | |
| 316 | // Start position of the current identifier: by this point, we've either |
| 317 | // moved one character past the token, or we're still at the start |
| 318 | const auto start = |
| 319 | std::max(std::stringstream::off_type{0}, |
| 320 | lex.ss.tellg() - std::stringstream::pos_type(lex.curident.length() + 1)); |
| 321 | |
| 322 | // Try to put a nice little '^~~~~' underlining the problem |
| 323 | // identifier. NOTE: this will be longer than required for multibyte UTF-8 |
| 324 | // characters, but will still point to the start of the identifier |
| 325 | const std::string problem_bit = |
| 326 | fmt::format(" {0}\n {1: >{2}}{3:~^{4}}", lex.ss.str(), "^", start, "", |
| 327 | lex.curident.length() - 1); |
| 328 | |
| 329 | auto possible_matches = fuzzyFind(name); |
| 330 | // Remove exact matches -- not helpful |
| 331 | bout::utils::erase_if(possible_matches, |
| 332 | [](const auto& match) -> bool { return match.distance == 0; }); |
| 333 | |
| 334 | // No matches, just point out the error |
| 335 | if (possible_matches.empty()) { |
| 336 | return fmt::format(message_template, name, problem_bit); |
| 337 | } |
| 338 | |
| 339 | // Give the first suggestion as a possible alternative |
| 340 | std::string error_message = fmt::format(message_template, name, problem_bit); |
| 341 | error_message += fmt::format(_("\n {1: ^{2}}{0}\n Did you mean '{0}'?"), |
| 342 | possible_matches.begin()->name, "", start); |
| 343 | return error_message; |
| 344 | }; |
| 345 | |
| 346 | string name = lex.curident; |
| 347 | lex.nextToken(); |
| 348 | |
| 349 | // sum(symbol, count, expr) |
| 350 | // e.g. sum(i, 10, {i}^2) -> 0 + 1^2 + 2^2 + 3^2 + ... + 9^2 |
| 351 | if (name == "sum") { |
| 352 | if (lex.curtok != '(') { |
| 353 | throw ParseException("Expecting '(' after 'sum' in 'sum(symbol, count, expr)'"); |
| 354 | } |
| 355 | lex.nextToken(); |
| 356 | |
| 357 | if ((lex.curtok != -2) && (lex.curtok != -3)) { |
| 358 | throw ParseException("Expecting symbol in 'sum(symbol, count, expr)'"); |
| 359 | } |
| 360 | string sym = lex.curident; |
| 361 | lex.nextToken(); |
| 362 | |
| 363 | if (lex.curtok != ',') { |
| 364 | throw ParseException("Expecting , after symbol {:s} in 'sum(symbol, count, expr)'", |
| 365 | sym); |
nothing calls this directly
no test coverage detected