Skip to content

Commit

Permalink
Consolidate number parsing to one function
Browse files Browse the repository at this point in the history
  • Loading branch information
CrazyInfin8 committed May 1, 2021
1 parent 7805d3c commit 3f5a16a
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 293 deletions.
157 changes: 10 additions & 147 deletions src/vm/wren_compiler.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#include <float.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
Expand Down Expand Up @@ -743,137 +742,21 @@ static int readHexDigit(Parser* parser)
}

// Parses a number token.
static void readNumber(Parser* parser, int base)
static void readNumber(Parser* parser)
{
bool hasDigits = false;
int e = 0, mantDigits = 0;
long long maxMant = 16, num = 0;
switch (base)
wrenParseNumResults results;
if (wrenParseNum(parser->currentChar, 0, &results))
{
case 16:
maxMant = 14;
break;
case 8:
maxMant = 18;
break;
case 2:
maxMant = 53;
break;
}
char c;
for (;;) {
c = peekChar(parser);
if (isDigit(c))
{
c -= '0';
}
else if (c >= 'a' && c <= 'z')
{
c -= 'a' - 10;
}
else if (c >= 'A' && c <= 'Z')
{
c -= 'A' - 10;
}
else if (c == '_')
{
nextChar(parser);
continue;
}
else break;
if (c >= base) break;
hasDigits = true;
if (mantDigits < maxMant)
{
num = num * base + c;
if (num > 0)
{
mantDigits++;
}
}
else e++;
nextChar(parser);
}
if (base == 10)
{
if (peekChar(parser) == '.' && isDigit(peekNextChar(parser)))
{
nextChar(parser);
for (;;)
{
c = peekChar(parser);
if (isDigit(c))
{
c -= '0';
}
else if (c == '_')
{
nextChar(parser);
continue;
}
else break;
if (mantDigits < maxMant)
{
num = num * 10 + c;
if (num > 0) mantDigits++;
e--;
}
nextChar(parser);
}
}
c = peekChar(parser);
if (c == 'e' || c == 'E')
{
nextChar(parser);
int expNum = 0;
bool expHasDigits = false, expNeg = false;
c = peekChar(parser);
if (c == '-') {
expNeg = true;
nextChar(parser);
}
else if (c == '+') nextChar(parser);
for (;;)
{
c = peekChar(parser);
if (isDigit(c))
{
expNum = expNum * 10 + (c - '0');
expHasDigits = true;
nextChar(parser);
}
else if (c == '_')
{
nextChar(parser);
continue;
}
else break;
}
if (!expHasDigits)
{
lexError(parser, "Unterminated scientific notation.");
parser->next.value = NUM_VAL(0);
return;
}
else if (expNeg) e -= expNum;
else e += expNum;
}
}
else if (!hasDigits)
{
lexError(parser, "Literal does not have digits.");
parser->next.value = NUM_VAL(0);
return;
parser->currentChar += results.consumed;
parser->next.value = NUM_VAL(results.value.dbl);
makeToken(parser, TOKEN_NUMBER);
}
double f = (double)(num) *
(double)(powl)((long double) base, (long double) e);
if (f > DBL_MAX || (f < DBL_MIN && num > 0))
else
{
lexError(parser, "Number literal was too large (%d).", sizeof(double));
parser->currentChar += results.consumed;
parser->next.value = NUM_VAL(0);
lexError(parser, results.value.err);
}
parser->next.value = NUM_VAL(f);
makeToken(parser, TOKEN_NUMBER);
}

// Finishes lexing an identifier. Handles reserved words.
Expand Down Expand Up @@ -1258,28 +1141,8 @@ static void nextToken(Parser* parser)
}
else if (isDigit(c))
{
if (c == '0')
{
c = peekChar(parser);
switch (c)
{
case 'x':
nextChar(parser);
readNumber(parser, 16);
return;
case 'o':
nextChar(parser);
readNumber(parser, 8);
return;
case 'b':
nextChar(parser);
readNumber(parser, 2);
return;
}
}
parser->currentChar--;
readNumber(parser, 10);
return;
readNumber(parser);
}
else
{
Expand Down
157 changes: 12 additions & 145 deletions src/vm/wren_core.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#include <ctype.h>
#include <errno.h>
#include <float.h>
#include <math.h>
#include <string.h>
#include <time.h>

Expand Down Expand Up @@ -614,150 +612,19 @@ DEF_PRIMITIVE(num_fromString)
// Corner case: Can't parse an empty string.
if (string->length == 0) RETURN_NULL;

char* str = string->value;
int base = 10, i = 0;
bool hasDigits = false, neg = false;
char c = str[i];
while(i < string->length && isspace(str[i])) i++;
if (c == '-')
{
neg = true;
i++;
}
else if (c == '+') i++;
if (i >= string->length) goto end;
long long maxMant = 16, num = 0;
if (c == '0')
{
i++;
if (i >= string->length) goto end;
switch (str[i])
{
case 'x':
base = 16;
i++;
maxMant = 14;
break;
case 'o':
base = 8;
i++;
maxMant = 18;
break;
case 'b':
base = 2;
i++;
maxMant = 53;
break;
}
}
int e = 0, mantDigits = 0;
for (;i < string->length;)
{
c = str[i];
if (isdigit(c))
{
c -= '0';
}
else if (c >= 'a' && c <= 'z')
{
c -= 'a' - 10;
}
else if (c >= 'A' && c <= 'Z')
{
c -= 'A' - 10;
}
else if (c == '_')
{
i++;
continue;
}
else
{
break;
}
if (c >= base) break;
hasDigits = true;
if (mantDigits < maxMant)
{
num = num * base + c;
if (num > 0) mantDigits++;
}
else e++;
i++;
}
if (i >= string->length) goto end;
if (base == 10)
{
if (str[i] == '.' && isdigit(str[i+1]))
{
i++;
for(;i < string->length;)
{
c = str[i];
if (isdigit(c)) c -= '0';
else if(c == '_')
{
i++;
continue;
}
else break;
if (mantDigits < maxMant)
{
num = num * 10 + c;
if (num > 0) mantDigits++;
e--;
}
i++;
}
}
if (i >= string->length) goto end;
if(str[i] == 'e' || str[i] == 'E')
{
i++;
if (i >= string->length) goto eEnd;
int expNum = 0;
bool expHasDigits = false, expNeg = false;
if (str[i] == '-')
{
expNeg = true;
i++;
}
else if (str[i] == '+') i++;
for(;i < string->length;)
{
c = str[i];
if (isdigit(c))
{
expNum = expNum * 10 + (c - '0');
expHasDigits = true;
i++;
}
else if (c == '_')
{
i++;
continue;
}
else break;
}
eEnd:
if (!expHasDigits)
{
RETURN_NULL;
}
else if (expNeg) e -= expNum;
else e += expNum;
}
char* end = string->value;

// Skip leading whitespace.
while(isspace(*end)) end++;

wrenParseNumResults results;
if (wrenParseNum(end, 0, &results)) {
end += results.consumed;
while(isspace(*end)) end++;
if( end < string->value + string->length) RETURN_NULL;
RETURN_NUM(results.value.dbl);
}
while(i < string->length && isspace(str[i])) i++;
end:
if (base != 10 && !hasDigits) RETURN_NULL;
// We must have consumed the entire string. Otherwise, it contains non-number
// characters and we can't parse it.
if (i < string->length) RETURN_NULL;
double f = (double)(num) *
(double)(powl)((long double) base, (long double) e);
if (f > DBL_MAX || (f < DBL_MIN && num > 0)) RETURN_ERROR("Number literal is too large.");
RETURN_NUM(neg ? f * -1 : f);
RETURN_NULL;
}

// Defines a primitive on Num that calls infix [op] and returns [type].
Expand Down
Loading

0 comments on commit 3f5a16a

Please sign in to comment.