Merge pull request #225 from ThakeeNathees/number-from-string

Number constructor from string
This commit is contained in:
Thakee Nathees 2022-05-09 16:04:16 +05:30 committed by GitHub
commit 78b7004de6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 155 additions and 9 deletions

View File

@ -814,7 +814,7 @@ static void eatNumber(Compiler* compiler) {
char c = *parser->token_start; char c = *parser->token_start;
// Binary literal. // Binary literal.
if (c == '0' && peekChar(parser) == 'b') { if (c == '0' && ((peekChar(parser) == 'b') || (peekChar(parser) == 'B'))) {
eatChar(parser); // Consume '0b' eatChar(parser); // Consume '0b'
uint64_t bin = 0; uint64_t bin = 0;
@ -845,7 +845,8 @@ static void eatNumber(Compiler* compiler) {
} }
value = VAR_NUM((double)bin); value = VAR_NUM((double)bin);
} else if (c == '0' && peekChar(parser) == 'x') { } else if (c == '0' &&
((peekChar(parser) == 'x') || (peekChar(parser) == 'X'))) {
eatChar(parser); // Consume '0x' eatChar(parser); // Consume '0x'
uint64_t hex = 0; uint64_t hex = 0;
@ -883,14 +884,17 @@ static void eatNumber(Compiler* compiler) {
} }
} else { // Regular number literal. } else { // Regular number literal.
while (utilIsDigit(peekChar(parser))) { while (utilIsDigit(peekChar(parser))) {
eatChar(parser); eatChar(parser);
} }
if (peekChar(parser) == '.' && utilIsDigit(peekNextChar(parser))) { if (c != '.') { // Number starts with a decimal point.
matchChar(parser, '.'); if (peekChar(parser) == '.' && utilIsDigit(peekNextChar(parser))) {
while (utilIsDigit(peekChar(parser))) { matchChar(parser, '.');
eatChar(parser); while (utilIsDigit(peekChar(parser))) {
eatChar(parser);
}
} }
} }

View File

@ -769,8 +769,20 @@ static void _ctorBool(PKVM* vm) {
static void _ctorNumber(PKVM* vm) { static void _ctorNumber(PKVM* vm) {
double value; double value;
if (!validateNumeric(vm, ARG(1), &value, "Argument 1")) return; if (isNumeric(ARG(1), &value)) {
RET(VAR_NUM(value)); RET(VAR_NUM(value));
}
if (IS_OBJ_TYPE(ARG(1), OBJ_STRING)) {
String* str = (String*) AS_OBJ(ARG(1));
double value;
const char* err = utilToNumber(str->data, &value);
if (err == NULL) RET(VAR_NUM(value));
VM_SET_ERROR(vm, newString(vm, err));
RET(VAR_NULL);
}
VM_SET_ERROR(vm, newString(vm, "Argument must be numeric or string."));
} }
static void _ctorString(PKVM* vm) { static void _ctorString(PKVM* vm) {

View File

@ -6,6 +6,11 @@
#include "pk_utils.h" #include "pk_utils.h"
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
// Function implementation, see utils.h for description. // Function implementation, see utils.h for description.
int utilPowerOf2Ceil(int n) { int utilPowerOf2Ceil(int n) {
n--; n--;
@ -94,6 +99,122 @@ uint32_t utilHashString(const char* string) {
#undef FNV_offset_basis_32_bit #undef FNV_offset_basis_32_bit
} }
const char* utilToNumber(const char* str, double* num) {
#define IS_HEX_CHAR(c) \
(('0' <= (c) && (c) <= '9') || \
('a' <= (c) && (c) <= 'f'))
#define IS_BIN_CHAR(c) (((c) == '0') || ((c) == '1'))
#define IS_DIGIT(c) (('0' <= (c)) && ((c) <= '9'))
#define INVALID_NUMERIC_STRING "Invalid numeric string."
assert((str != NULL) && (num != NULL));
uint32_t length = (uint32_t) strlen(str);
// Consume the sign.
double sign = +1;
if (*str == '-') {
sign = -1;
str++;
} else if (*str == '+') {
str++;
}
// Binary String.
if (length >= 3 &&
((strncmp(str, "0b", 2) == 0) || (strncmp(str, "0B", 2) == 0))) {
uint64_t bin = 0;
const char* c = str + 2;
if (*c == '\0') return INVALID_NUMERIC_STRING;
do {
if (*c == '\0') {
*num = sign * bin;
return NULL;
};
if (!IS_BIN_CHAR(*c)) return INVALID_NUMERIC_STRING;
if ((c - str) > (68 /*STR_BIN_BUFF_SIZE*/ - 2)) { // -2 for '-, \0'.
return "Binary literal is too long.";
}
bin = (bin << 1) | (*c - '0');
c++;
} while (true);
}
// Hex String.
if (length >= 3 &&
((strncmp(str, "0x", 2) == 0) || (strncmp(str, "0X", 2) == 0))) {
uint64_t hex = 0;
const char* c = str + 2;
if (*c == '\0') return INVALID_NUMERIC_STRING;
do {
if (*c == '\0') {
*num = sign * hex;
return NULL;
};
if (!IS_HEX_CHAR(*c)) return INVALID_NUMERIC_STRING;
if ((c - str) > (20 /*STR_HEX_BUFF_SIZE*/ - 2)) { // -2 for '-, \0'.
return "Hex literal is too long.";
}
uint8_t digit = ('0' <= *c && *c <= '9')
? (uint8_t)(*c - '0')
: (uint8_t)((*c - 'a') + 10);
hex = (hex << 4) | digit;
c++;
} while (true);
}
// Regular number.
if (*str == '\0') return INVALID_NUMERIC_STRING;
const char* c = str;
do {
while (IS_DIGIT(*c)) c++;
if (*c == '.') { // TODO: allowing "1." as a valid float.?
c++;
while (IS_DIGIT(*c)) c++;
}
if (*c == '\0') break; // Done.
if (*c == 'e' || *c == 'E') {
c++;
if (*c == '+' || *c == '-') c++;
if (!IS_DIGIT(*c)) return INVALID_NUMERIC_STRING;
while (IS_DIGIT(*c)) c++;
if (*c != '\0') return INVALID_NUMERIC_STRING;
}
if (*c != '\0') return INVALID_NUMERIC_STRING;
} while (false);
errno = 0;
*num = atof(str) * sign;
if (errno == ERANGE) return "Numeric string is too long.";
return NULL;
#undef INVALID_NUMERIC_STRING
#undef IS_DIGIT
#undef IS_HEX_CHAR
#undef IS_BIN_CHAR
}
/**************************************************************************** /****************************************************************************
* UTF8 * * UTF8 *
****************************************************************************/ ****************************************************************************/

View File

@ -38,6 +38,11 @@ uint32_t utilHashNumber(double num);
// Generate a has code for [string]. // Generate a has code for [string].
uint32_t utilHashString(const char* string); uint32_t utilHashString(const char* string);
// Convert the string to number. On success it'll return NULL and set the
// [num] value. Otherwise it'll return a C literal string containing the error
// message.
const char* utilToNumber(const char* str, double* num);
/**************************************************************************** /****************************************************************************
* UTF8 * * UTF8 *
****************************************************************************/ ****************************************************************************/

View File

@ -2,7 +2,7 @@
## Builtin types attributes and methods tests. ## Builtin types attributes and methods tests.
############################################################################### ###############################################################################
## INTEGER ## Number
############################################################################### ###############################################################################
sum = 0 sum = 0
@ -11,6 +11,10 @@ sum = 0
end end
assert(sum == 0 + 1 + 2 + 3 + 4) assert(sum == 0 + 1 + 2 + 3 + 4)
assert(Number("-.23e+6") == -.23e6)
assert(Number("0b10100101") == 0b10100101)
assert(Number("-0Xabcdef123") == -0xabcdef123)
############################################################################### ###############################################################################
## STRING ## STRING
############################################################################### ###############################################################################