From 6b3d80109c2076a271a2f668ab2536814ca72d83 Mon Sep 17 00:00:00 2001 From: gumi Date: Sat, 28 Mar 2020 16:56:20 -0400 Subject: add support for binary and octal number literals --- src/map/script.c | 89 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 23 deletions(-) (limited to 'src/map') diff --git a/src/map/script.c b/src/map/script.c index b8a7979a7..d5e73ad65 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -1161,6 +1161,41 @@ static const char *parse_variable(const char *p) return p; } +/** + * converts a number expression literal to an actual integer +*/ +static long long parse_number(const char *p, char **np) { + long long lli = 0; + unsigned char radix = 10; + + nullpo_retr(lli, p); + + if (*p == '0' && p[1] == 'x') { + p += 2; + radix = 16; // hexadecimal + } else if (*p == '0' && p[1] == 'o') { + p += 2; + radix = 8; // octal + } else if (*p == '0' && p[1] == 'b') { + p += 2; + radix = 2; // binary + } + + // actual parsing happens here + lli = strtoll(p, np, radix); + + // make sure we can't underflow/overflow + 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; + script->disp_warning_message("parse_number: overflow detected, capping value to INT_MAX", p); + } + + return lli; +} + /* * Checks whether the gives string is a number literal * @@ -1177,24 +1212,44 @@ static const char *parse_variable(const char *p) static bool is_number(const char *p) { const char *np; - if (!p) - return false; - if (*p == '-' || *p == '+') + nullpo_retr(false, p); + + if (*p == '-' || *p == '+') { p++; + } + np = p; + if (*p == '0' && p[1] == 'x') { - p+=2; - np = p; - // Hexadecimal - while (ISXDIGIT(*np)) + // Hexadecimal: 0xFFFF + np = p += 2; + while (ISXDIGIT(*np)) { np++; + } + } else if (*p == '0' && p[1] == 'b') { + // Binary: 0b0001 + np = p += 2; + while (ISBDIGIT(*np)) { + np++; + } + } else if (*p == '0' && p[1] == 'o') { + // Octal: 0o1500 + np = p += 2; + while (ISODIGIT(*np)) { + np++; + } } else { - // Decimal - while (ISDIGIT(*np)) + // Decimal: 1234 + while (ISDIGIT(*np)) { np++; + } } - if (p != np && *np != '_' && !ISALPHA(*np)) // At least one digit, and next isn't a letter or _ + + if (p != np && *np != '_' && !ISALPHA(*np)) { + // At least one digit, and next isn't a letter or _ return true; + } + return false; } @@ -1276,20 +1331,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; + long long lli = parse_number(p, &np); - nullpo_retr(NULL, p); - while (*p == '0' && ISDIGIT(p[1])) - p++; // Skip leading zeros, we don't support octal literals - - lli = strtoll(p, &np, 0); - if (lli < INT_MIN) { - lli = INT_MIN; - script->disp_warning_message("parse_simpleexpr: underflow detected, capping value to INT_MIN", p); - } else if (lli > INT_MAX) { - lli = INT_MAX; - script->disp_warning_message("parse_simpleexpr: overflow detected, capping value to INT_MAX", p); - } script->addi((int)lli); // Cast is safe, as it's already been checked for overflows return np; -- cgit v1.2.3-70-g09d2 From ac8d0f6ade21721e7946bdda5b9d712fc176e0f2 Mon Sep 17 00:00:00 2001 From: gumi Date: Sun, 3 May 2020 20:02:27 -0400 Subject: add support for number separators --- src/common/cbasetypes.h | 1 + src/map/script.c | 95 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 68 insertions(+), 28 deletions(-) (limited to 'src/map') diff --git a/src/common/cbasetypes.h b/src/common/cbasetypes.h index 3ca0c5f67..83c7c8d60 100644 --- a/src/common/cbasetypes.h +++ b/src/common/cbasetypes.h @@ -413,6 +413,7 @@ typedef char bool; #define ISXDIGIT(c) (isxdigit((unsigned char)(c))) #define ISBDIGIT(c) ((unsigned char)(c) == '0' || (unsigned char)(c) == '1') #define ISODIGIT(c) ((unsigned char)(c) >= '0' && (unsigned char)(c) <= '7') +#define ISNSEPARATOR(c) ((unsigned char)(c) == '_') #define ISGRAPH(c) (isgraph((unsigned char)(c))) #define ISLOWER(c) (islower((unsigned char)(c))) #define ISPRINT(c) (isprint((unsigned char)(c))) 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 -- cgit v1.2.3-70-g09d2