mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-06 12:46:53 +08:00
Merge pull request #225 from ThakeeNathees/number-from-string
Number constructor from string
This commit is contained in:
commit
78b7004de6
@ -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,16 +884,19 @@ 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 (c != '.') { // Number starts with a decimal point.
|
||||||
if (peekChar(parser) == '.' && utilIsDigit(peekNextChar(parser))) {
|
if (peekChar(parser) == '.' && utilIsDigit(peekNextChar(parser))) {
|
||||||
matchChar(parser, '.');
|
matchChar(parser, '.');
|
||||||
while (utilIsDigit(peekChar(parser))) {
|
while (utilIsDigit(peekChar(parser))) {
|
||||||
eatChar(parser);
|
eatChar(parser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parse if in scientific notation format (MeN == M * 10 ** N).
|
// Parse if in scientific notation format (MeN == M * 10 ** N).
|
||||||
if (matchChar(parser, 'e') || matchChar(parser, 'E')) {
|
if (matchChar(parser, 'e') || matchChar(parser, 'E')) {
|
||||||
|
@ -769,10 +769,22 @@ 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) {
|
||||||
if (!pkCheckArgcRange(vm, ARGC, 0, 1)) return;
|
if (!pkCheckArgcRange(vm, ARGC, 0, 1)) return;
|
||||||
if (ARGC == 0) {
|
if (ARGC == 0) {
|
||||||
|
121
src/pk_utils.c
121
src/pk_utils.c
@ -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 *
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
@ -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 *
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
@ -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
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
Loading…
Reference in New Issue
Block a user