summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
authorgumi <git@gumi.ca>2020-05-03 20:02:27 -0400
committergumi <git@gumi.ca>2020-05-07 16:03:40 -0400
commitac8d0f6ade21721e7946bdda5b9d712fc176e0f2 (patch)
treed77cb7baaf6a0612b5233ec3bc64b39731583fb1 /src/map
parent6b3d80109c2076a271a2f668ab2536814ca72d83 (diff)
downloadhercules-ac8d0f6ade21721e7946bdda5b9d712fc176e0f2.tar.gz
hercules-ac8d0f6ade21721e7946bdda5b9d712fc176e0f2.tar.bz2
hercules-ac8d0f6ade21721e7946bdda5b9d712fc176e0f2.tar.xz
hercules-ac8d0f6ade21721e7946bdda5b9d712fc176e0f2.zip
add support for number separators
Diffstat (limited to 'src/map')
-rw-r--r--src/map/script.c95
1 files changed, 67 insertions, 28 deletions
diff --git a/src/map/script.c b/src/map/script.c
index d5e73ad65..d85f064b7 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -1162,38 +1162,77 @@ static const char *parse_variable(const char *p)
}
/**
- * converts a number expression literal to an actual integer
+ * Converts a number expression literal to an actual integer.
+ * Number separators are skipped.
+ *
+ * expects these formats:
+ * 1337
+ * 0x1337
+ * 0b1001
+ * 0o1337
+ *
+ * example with separating nibbles of a binary literal:
+ * 0b1101_0111_1001_1111
+ *
+ * @param p - a pointer to the first char of the number literal
+ * @param lli - a pointer to the resulting long long integer
+ * @returns a pointer to the first char after the parsed number
*/
-static long long parse_number(const char *p, char **np) {
- long long lli = 0;
- unsigned char radix = 10;
+static const char *parse_number(const char *p, long long *lli) {
+ nullpo_retr(NULL, p);
- nullpo_retr(lli, p);
+ const bool unary_plus = (*p == '+');
+ const bool unary_minus = (*p == '-');
+
+ if (unary_plus || unary_minus) {
+ p++;
+ }
+
+ if (ISNSEPARATOR(*p)) {
+ disp_error_message("parse_number: number literals cannot begin with a separator", p);
+ }
+
+#define PARSENUMBER(skip, func, radix) \
+ for (p += skip; func(*p) || (ISNSEPARATOR(*p) && (func(p[1]) || ISNSEPARATOR(p[1]))); ++p) { \
+ if (func(*p)) { \
+ *lli *= radix; \
+ *lli += (*p < 'A') ? (*p & 0xF) : (9 + (*p & 0x7)); \
+ } else if (ISNSEPARATOR(p[1])) { \
+ disp_error_message("parse_number: number literals cannot contain two separators in a row", p + 1); \
+ } \
+ }
if (*p == '0' && p[1] == 'x') {
- p += 2;
- radix = 16; // hexadecimal
+ PARSENUMBER(2, ISXDIGIT, 16);
} else if (*p == '0' && p[1] == 'o') {
- p += 2;
- radix = 8; // octal
+ PARSENUMBER(2, ISODIGIT, 8);
} else if (*p == '0' && p[1] == 'b') {
- p += 2;
- radix = 2; // binary
+ PARSENUMBER(2, ISBDIGIT, 2);
+ } else {
+ PARSENUMBER(0, ISDIGIT, 10);
+ }
+
+#undef PARSENUMBER
+
+ if (ISNSEPARATOR(*p)) {
+ disp_error_message("parse_number: number literals cannot end with a separator", p);
}
- // actual parsing happens here
- lli = strtoll(p, np, radix);
+ if (unary_minus) {
+ // reverse the sign
+ *lli = -(*lli);
+ }
// make sure we can't underflow/overflow
- if (lli < INT_MIN) {
- lli = INT_MIN;
+ if (*lli < INT_MIN) {
+ *lli = INT_MIN;
script->disp_warning_message("parse_number: underflow detected, capping value to INT_MIN", p);
- } else if (lli > INT_MAX) {
- lli = INT_MAX;
+ } else if (*lli > INT_MAX) {
+ *lli = INT_MAX;
script->disp_warning_message("parse_number: overflow detected, capping value to INT_MAX", p);
}
- return lli;
+ return p;
}
/*
@@ -1222,25 +1261,25 @@ static bool is_number(const char *p)
if (*p == '0' && p[1] == 'x') {
// Hexadecimal: 0xFFFF
- np = p += 2;
- while (ISXDIGIT(*np)) {
+ np = (p += 2);
+ while (ISXDIGIT(*np) || ISNSEPARATOR(*np)) {
np++;
}
} else if (*p == '0' && p[1] == 'b') {
// Binary: 0b0001
- np = p += 2;
- while (ISBDIGIT(*np)) {
+ np = (p += 2);
+ while (ISBDIGIT(*np) || ISNSEPARATOR(*np)) {
np++;
}
} else if (*p == '0' && p[1] == 'o') {
// Octal: 0o1500
- np = p += 2;
- while (ISODIGIT(*np)) {
+ np = (p += 2);
+ while (ISODIGIT(*np) || ISNSEPARATOR(*np)) {
np++;
}
- } else {
+ } else if (ISDIGIT(*p)) {
// Decimal: 1234
- while (ISDIGIT(*np)) {
+ while (ISDIGIT(*np) || ISNSEPARATOR(*np)) {
np++;
}
}
@@ -1330,8 +1369,8 @@ static const char *parse_simpleexpr_paren(const char *p)
static const char *parse_simpleexpr_number(const char *p)
{
- char *np = NULL;
- long long lli = parse_number(p, &np);
+ long long lli = 0;
+ const char *np = parse_number(p, &lli);
script->addi((int)lli); // Cast is safe, as it's already been checked for overflows