diff --git a/simplecpp.cpp b/simplecpp.cpp index 7afc17ab..dd73d6c7 100644 --- a/simplecpp.cpp +++ b/simplecpp.cpp @@ -67,6 +67,11 @@ static bool isOct(const std::string &s) return s.size()>1 && (s[0]=='0') && (s[1] >= '0') && (s[1] < '8'); } +static bool isBin(const std::string &s) +{ + return s.size()>2 && s.compare(0,2,"0b")==0; +} + static bool isStringLiteral(const std::string &s) { return s.size() > 1 && (s[0]=='\"') && (*s.rbegin()=='\"'); @@ -117,32 +122,51 @@ static std::string locstring(const simplecpp::Location &loc) } #endif -static long long stringToLL(const std::string &s) -{ - long long ret; - const bool hex = isHex(s); - const bool oct = isOct(s); - std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s); - if (hex) - istr >> std::hex; - else if (oct) - istr >> std::oct; - istr >> ret; - return ret; +static unsigned long long stringToULL(const std::string &s, std::size_t offset = 0) +{ + unsigned long long base = 10; + if (isHex(s.substr(offset))) { + offset += 2; + base = 16; + } else if (isOct(s.substr(offset))) { + offset += 1; + base = 8; + } else if (isBin(s.substr(offset))) { + offset += 2; + base = 2; + } + + unsigned long long result = 0; + for (std::size_t i = offset; i < s.size(); i++) { + const char c = s[i]; + result *= base; + + unsigned long long d; + if ('0' <= c && c <= '9') { + d = c - '0'; + } else if ('a' <= c && c <= 'f') { + d = 10 + c - 'a'; + } else if ('A' <= c && c <= 'F') { + d = 10 + c - 'A'; + } else { + throw std::runtime_error("invalid integer literal"); + } + + if (d >= base) { + throw std::runtime_error("invalid integer literal"); + } + + result += d; + } + + return result; } -static unsigned long long stringToULL(const std::string &s) +static long long stringToLL(const std::string &s) { - unsigned long long ret; - const bool hex = isHex(s); - const bool oct = isOct(s); - std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s); - if (hex) - istr >> std::hex; - else if (oct) - istr >> std::oct; - istr >> ret; - return ret; + if (s.size() > 0 && s[0] == '-') + return -stringToULL(s, 1); + return stringToULL(s); } static bool endsWith(const std::string &s, const std::string &e) diff --git a/test.cpp b/test.cpp index fa94a266..bf9ee987 100644 --- a/test.cpp +++ b/test.cpp @@ -2046,6 +2046,52 @@ static void ifUndefFuncStyleMacro() ASSERT_EQUALS("file0,1,syntax_error,failed to evaluate #if condition, undefined function-like macro invocation: A( ... )\n", toString(outputList)); } +static void testIfIntegerLiteral(const std::string &value, const std::string &literal, bool expectError) +{ + const std::string code = "#define A " + value + "\n" + "#if A == " + literal + "\n" + "int x = " + literal + ";\n" + "#endif\n"; + + simplecpp::OutputList outputList; + + std::string literalPreprocessed = literal; + if (literalPreprocessed[0] == '-') + { + literalPreprocessed = "- " + literalPreprocessed.substr(1); + } + + if (expectError) { + ASSERT_EQUALS("", preprocess(code.c_str(), &outputList)); + ASSERT_EQUALS("file0,2,syntax_error,failed to evaluate #if condition, invalid integer literal\n", toString(outputList)); + } else { + ASSERT_EQUALS("\n\nint x = " + literalPreprocessed + " ;", preprocess(code.c_str(), &outputList)); + ASSERT_EQUALS("", toString(outputList)); + } +} + +static void ifIntegerLiteral() +{ + testIfIntegerLiteral("123", "123", false); + testIfIntegerLiteral("-123", "-123", false); + + testIfIntegerLiteral("123", "0x7b", false); + testIfIntegerLiteral("-123", "-0x7b", false); + + testIfIntegerLiteral("123", "0x7B", false); + testIfIntegerLiteral("-123", "-0x7B", false); + + testIfIntegerLiteral("123", "0173", false); + testIfIntegerLiteral("-123", "-0173", false); + + testIfIntegerLiteral("123", "0b1111011", false); + testIfIntegerLiteral("-123", "-0b1111011", false); + + testIfIntegerLiteral("123", "0xGH", true); + testIfIntegerLiteral("123", "019", true); + testIfIntegerLiteral("123", "0b30", true); +} + static void location1() { const char *code; @@ -3688,6 +3734,7 @@ int main(int argc, char **argv) TEST_CASE(ifalt); // using "and", "or", etc TEST_CASE(ifexpr); TEST_CASE(ifUndefFuncStyleMacro); + TEST_CASE(ifIntegerLiteral); TEST_CASE(location1); TEST_CASE(location2);