From 745d87c6d2f67f227c66ad033dc1547f958be481 Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 22 Jun 2026 22:54:25 +0200 Subject: [PATCH] fixes https://github.com/emsesp/EMS-ESP32/discussions/3127 --- src/core/shuntingYard.cpp | 26 +++++++++++++++++++++----- test/test_api/test_shuntingYard.h | 16 ++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/core/shuntingYard.cpp b/src/core/shuntingYard.cpp index 7041ca49f..c952dc9d6 100644 --- a/src/core/shuntingYard.cpp +++ b/src/core/shuntingYard.cpp @@ -334,9 +334,12 @@ std::deque shuntingYard(const std::deque & tokens) { return queue; } -// check if string is a number +// check if string is a number. Must contain at least one digit bool isnum(const std::string & s) { - if (!s.empty() && (s.find_first_not_of("0123456789.") == std::string::npos || (s[0] == '-' && s.find_first_not_of("0123456789.", 1) == std::string::npos))) { + if (s.empty() || s.find_first_of("0123456789") == std::string::npos) { + return false; + } + if (s.find_first_not_of("0123456789.") == std::string::npos || (s[0] == '-' && s.find_first_not_of("0123456789.", 1) == std::string::npos)) { return true; } return false; @@ -499,6 +502,18 @@ std::string calculate(const std::string & expr) { } break; } + if (token.str[0] == 'h') { + // hex string to number + if (rhs.empty() || rhs.find_first_not_of("0123456789abcdefABCDEF") != std::string::npos) { + return ""; + } + stack.push_back(to_string(std::stoi(rhs, 0, 16))); + break; + } + // bail out on non-numeric operand + if (!isnum(rhs)) { + return ""; + } auto rhd = std::stod(rhs); switch (token.str[0]) { default: @@ -531,9 +546,6 @@ std::string calculate(const std::string & expr) { case 'p': stack.push_back(to_string(std::pow(rhd, 2))); break; - case 'h': - stack.push_back(to_string(std::stoi(rhs, 0, 16))); - break; case 'x': stack.push_back(to_hex(static_cast(rhd))); break; @@ -641,6 +653,10 @@ std::string calculate(const std::string & expr) { stack.push_back(lhs + rhs); break; } + // bail out on non-numeric operands + if (!isnum(lhs) || !isnum(rhs)) { + return ""; + } auto lhd = std::stod(lhs); auto rhd = std::stod(rhs); switch (token.str[0]) { diff --git a/test/test_api/test_shuntingYard.h b/test/test_api/test_shuntingYard.h index eabf68974..cce84bc6f 100644 --- a/test/test_api/test_shuntingYard.h +++ b/test/test_api/test_shuntingYard.h @@ -107,6 +107,19 @@ void shuntingYard_test25() { run_shuntingYard_test("hello world!", "'hello world!'"); } +// non-numeric operands must not crash. should return empty string +void shuntingYard_test26() { + run_shuntingYard_test("", "info/x"); +} + +void shuntingYard_test27() { + run_shuntingYard_test("", "info-x"); +} + +void shuntingYard_test28() { + run_shuntingYard_test("", "-x"); +} + void run_shuntingYard_tests() { RUN_TEST(shuntingYard_test1); RUN_TEST(shuntingYard_test2); @@ -133,4 +146,7 @@ void run_shuntingYard_tests() { RUN_TEST(shuntingYard_test23); RUN_TEST(shuntingYard_test24); RUN_TEST(shuntingYard_test25); + RUN_TEST(shuntingYard_test26); + RUN_TEST(shuntingYard_test27); + RUN_TEST(shuntingYard_test28); }