mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-06 04:37:47 +08:00
Number constructor from string
This commit is contained in:
parent
6e66721b2e
commit
b96b18a4d7
@ -814,7 +814,7 @@ static void eatNumber(Compiler* compiler) {
|
||||
char c = *parser->token_start;
|
||||
|
||||
// Binary literal.
|
||||
if (c == '0' && peekChar(parser) == 'b') {
|
||||
if (c == '0' && ((peekChar(parser) == 'b') || (peekChar(parser) == 'B'))) {
|
||||
eatChar(parser); // Consume '0b'
|
||||
|
||||
uint64_t bin = 0;
|
||||
@ -845,7 +845,8 @@ static void eatNumber(Compiler* compiler) {
|
||||
}
|
||||
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'
|
||||
|
||||
uint64_t hex = 0;
|
||||
@ -883,16 +884,19 @@ static void eatNumber(Compiler* compiler) {
|
||||
}
|
||||
|
||||
} else { // Regular number literal.
|
||||
|
||||
while (utilIsDigit(peekChar(parser))) {
|
||||
eatChar(parser);
|
||||
}
|
||||
|
||||
if (c != '.') { // Number starts with a decimal point.
|
||||
if (peekChar(parser) == '.' && utilIsDigit(peekNextChar(parser))) {
|
||||
matchChar(parser, '.');
|
||||
while (utilIsDigit(peekChar(parser))) {
|
||||
eatChar(parser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse if in scientific notation format (MeN == M * 10 ** N).
|
||||
if (matchChar(parser, 'e') || matchChar(parser, 'E')) {
|
||||
|
@ -769,10 +769,22 @@ static void _ctorBool(PKVM* vm) {
|
||||
|
||||
static void _ctorNumber(PKVM* vm) {
|
||||
double value;
|
||||
if (!validateNumeric(vm, ARG(1), &value, "Argument 1")) return;
|
||||
if (isNumeric(ARG(1), &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) {
|
||||
if (!pkCheckArgcRange(vm, ARGC, 0, 1)) return;
|
||||
if (ARGC == 0) {
|
||||
|
121
src/pk_utils.c
121
src/pk_utils.c
@ -6,6 +6,11 @@
|
||||
|
||||
#include "pk_utils.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
int utilPowerOf2Ceil(int n) {
|
||||
n--;
|
||||
@ -94,6 +99,122 @@ uint32_t utilHashString(const char* string) {
|
||||
#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 *
|
||||
****************************************************************************/
|
||||
|
@ -38,6 +38,11 @@ uint32_t utilHashNumber(double num);
|
||||
// Generate a has code for [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 *
|
||||
****************************************************************************/
|
||||
|
@ -2,7 +2,7 @@
|
||||
## Builtin types attributes and methods tests.
|
||||
|
||||
###############################################################################
|
||||
## INTEGER
|
||||
## Number
|
||||
###############################################################################
|
||||
|
||||
sum = 0
|
||||
@ -11,6 +11,10 @@ sum = 0
|
||||
end
|
||||
assert(sum == 0 + 1 + 2 + 3 + 4)
|
||||
|
||||
assert(Number("-.23e+6") == -.23e6)
|
||||
assert(Number("0b10100101") == 0b10100101)
|
||||
assert(Number("-0Xabcdef123") == -0xabcdef123)
|
||||
|
||||
###############################################################################
|
||||
## STRING
|
||||
###############################################################################
|
||||
|
Loading…
Reference in New Issue
Block a user