aboutsummaryrefslogtreecommitdiff
path: root/pseudocode-lib
diff options
context:
space:
mode:
Diffstat (limited to 'pseudocode-lib')
-rw-r--r--pseudocode-lib/README.md4
-rw-r--r--pseudocode-lib/string.cc35
2 files changed, 28 insertions, 11 deletions
diff --git a/pseudocode-lib/README.md b/pseudocode-lib/README.md
index e0819bf..1e360a4 100644
--- a/pseudocode-lib/README.md
+++ b/pseudocode-lib/README.md
@@ -35,7 +35,7 @@ The library provides `&`, six case-sensitive content comparisons, and these ordi
- `ASC(CHAR)` and `ASC(STRING)` return the unsigned character or first-byte code; an empty string returns zero.
- `CHR(Code)` returns a one-byte STRING containing the low eight bits. `LENGTH(CHR(0))` is 1.
- `NUM_TO_STR(INTEGER)` and `NUM_TO_STR(REAL)` use C++17 `std::to_string`, with six fractional digits for REAL.
-- `STR_TO_NUM(S)` returns REAL using `std::stod` and requires full consumption. Invalid text, trailing junk, and out-of-range values throw.
-- `IS_NUM(S)` tests full consumption by `strtod` without throwing for invalid text. Leading whitespace is accepted; trailing whitespace, trailing junk, empty input, and embedded NUL bytes are rejected.
+- `STR_TO_NUM(S)` returns REAL through the same `parseNumber` helper as IS_NUM (`strtod`, full consumption). Invalid text and trailing junk throw `STR_TO_NUM: '<text>' is not a number`; overflow throws `... is out of the REAL range`; underflow is accepted.
+- `IS_NUM(S)` agrees with STR_TO_NUM: full consumption by `strtod` and no overflow, without throwing. Leading whitespace is accepted; trailing whitespace, trailing junk, empty input, embedded NUL bytes, and overflowing values (`1e999`) are rejected.
Library C++ exceptions print the user call line and can be caught with TRY/CATCH. Library functions follow the guide where specified; LEFT, STRING case conversion, byte-code and numeric-conversion functions remain compatibility extensions.
diff --git a/pseudocode-lib/string.cc b/pseudocode-lib/string.cc
index f2f03f6..34f1ec5 100644
--- a/pseudocode-lib/string.cc
+++ b/pseudocode-lib/string.cc
@@ -1,6 +1,8 @@
#include <algorithm>
#include <cctype>
#include <cstdio>
+#include <cerrno>
+#include <cmath>
#include <cstdlib>
#include <exception>
#include <stdexcept>
@@ -20,6 +22,20 @@ decltype(auto) checked(Operation &&Op) {
}
}
std::string &text(void *Handle) { return *static_cast<std::string *>(Handle); }
+
+enum class NumberStatus { Ok, Invalid, OutOfRange };
+
+// Shared by IS_NUM and STR_TO_NUM so that they always agree.
+NumberStatus parseNumber(const std::string &Source, double &Result) {
+ if (Source.empty()) return NumberStatus::Invalid;
+ char *End = nullptr;
+ errno = 0;
+ double Number = std::strtod(Source.c_str(), &End);
+ if (End != Source.c_str() + Source.size()) return NumberStatus::Invalid;
+ if (errno == ERANGE && std::isinf(Number)) return NumberStatus::OutOfRange;
+ Result = Number;
+ return NumberStatus::Ok;
+}
}
extern "C" {
@@ -58,11 +74,14 @@ void _string_impl_from_real(void *Target, double Number) {
}
double _string_impl_to_real(void *Handle) {
return checked([&] {
- std::size_t Consumed = 0;
- double Number = std::stod(text(Handle), &Consumed);
- if (Consumed != text(Handle).size())
- throw std::invalid_argument("unconsumed characters in string conversion");
- return Number;
+ double Number = 0;
+ switch (parseNumber(text(Handle), Number)) {
+ case NumberStatus::Ok: return Number;
+ case NumberStatus::OutOfRange:
+ throw std::out_of_range("STR_TO_NUM: '" + text(Handle) + "' is out of the REAL range");
+ case NumberStatus::Invalid: break;
+ }
+ throw std::invalid_argument("STR_TO_NUM: '" + text(Handle) + "' is not a number");
});
}
long long _string_impl_length(void *Handle) {
@@ -130,10 +149,8 @@ void _string_impl_chr(void *Target, long long Code) {
}
int _string_impl_is_num(void *Handle) {
return checked([&]() -> int {
- const auto &Source = text(Handle);
- char *End = nullptr;
- std::strtod(Source.c_str(), &End);
- return !Source.empty() && End == Source.c_str() + Source.size();
+ double Number = 0;
+ return parseNumber(text(Handle), Number) == NumberStatus::Ok;
});
}
const char *_string_impl_c_str(void *Handle) {