better radix handling in float parsing. base 10 still doesn't work.

master
Josh Wolfe 2015-12-15 12:52:10 -07:00
parent fe94ca8d10
commit 2f15babbd3
1 changed files with 59 additions and 29 deletions

View File

@ -585,10 +585,13 @@ static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool
}
static unsigned long long parse_int_digits(ParseContext *pc, int digits_start, int digits_end, int radix,
unsigned long long initial_value, bool *overflow) {
unsigned long long x = initial_value;
int skip_index, bool *overflow)
{
unsigned long long x = 0;
for (int i = digits_start; i < digits_end; i++) {
if (i == skip_index)
continue;
uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i);
unsigned long long digit = get_digit_value(c);
@ -625,7 +628,7 @@ static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLi
if (token->decimal_point_pos == token->end_pos) {
// integer
unsigned long long whole_number = parse_int_digits(pc, whole_number_start, whole_number_end,
token->radix, 0, &num_lit->overflow);
token->radix, -1, &num_lit->overflow);
if (num_lit->overflow) return;
num_lit->data.x_uint = whole_number;
@ -641,12 +644,6 @@ static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLi
}
} else {
// float
// TODO: trim leading and trailing zeros in the significand digit sequence
unsigned long long significand_as_int = parse_int_digits(pc, whole_number_start, whole_number_end,
token->radix, 0, &num_lit->overflow);
if (num_lit->overflow) return;
int exponent = 0;
if (token->decimal_point_pos < token->exponent_marker_pos) {
// fraction
int fraction_start = token->decimal_point_pos + 1;
@ -655,15 +652,44 @@ static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLi
// TODO: error for empty fraction part
return;
}
}
// TODO: check for where the fraction got too precise instead of just saying overflow
significand_as_int = parse_int_digits(pc, fraction_start, fraction_end,
token->radix, significand_as_int, &num_lit->overflow);
if (num_lit->overflow) return;
// trim leading and trailing zeros in the significand digit sequence
int significand_start = whole_number_start;
for (; significand_start < token->exponent_marker_pos; significand_start++) {
if (significand_start == token->decimal_point_pos)
continue;
uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + significand_start);
if (c != '0')
break;
}
int significand_end = token->exponent_marker_pos;
for (; significand_end - 1 > significand_start; significand_end--) {
if (significand_end - 1 <= token->decimal_point_pos) {
significand_end = token->decimal_point_pos;
break;
}
uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + significand_end - 1);
if (c != '0')
break;
}
// adjust the exponent to compensate for us effectively moving
// the decimal point all the way to the right
exponent = -(fraction_end - fraction_start);
unsigned long long significand_as_int = parse_int_digits(pc, significand_start, significand_end,
token->radix, token->decimal_point_pos, &num_lit->overflow);
if (num_lit->overflow) return;
int exponent_in_bin_or_dec = 0;
if (significand_end > token->decimal_point_pos) {
exponent_in_bin_or_dec = token->decimal_point_pos + 1 - significand_end;
if (token->radix == 2) {
// already good
} else if (token->radix == 8) {
exponent_in_bin_or_dec *= 3;
} else if (token->radix == 10) {
// already good
} else if (token->radix == 16) {
exponent_in_bin_or_dec *= 4;
} else zig_unreachable();
}
if (token->exponent_marker_pos < token->end_pos) {
@ -674,7 +700,6 @@ static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLi
// TODO: error for empty exponent part
return;
}
bool is_exponent_negative = false;
uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + exponent_start);
if (c == '+') {
@ -690,16 +715,17 @@ static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLi
}
unsigned long long specified_exponent = parse_int_digits(pc, exponent_start, exponent_end,
10, 0, &num_lit->overflow);
10, -1, &num_lit->overflow);
// TODO: this check is a little silly
if (specified_exponent >= LONG_LONG_MAX) {
num_lit->overflow = true;
return;
}
if (is_exponent_negative) {
exponent -= specified_exponent;
exponent_in_bin_or_dec -= specified_exponent;
} else {
exponent += specified_exponent;
exponent_in_bin_or_dec += specified_exponent;
}
}
@ -707,16 +733,20 @@ static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLi
uint64_t exponent_bits;
if (significand_as_int != 0) {
// normalize the significand
int significand_magnitude = __builtin_clzll(1) - __builtin_clzll(significand_as_int);
exponent += significand_magnitude;
if (!(-1023 <= exponent && exponent < 1023)) {
num_lit->overflow = true;
return;
}
if (token->radix == 10) {
zig_panic("TODO: decimal floats");
} else {
int significand_magnitude_in_bin = __builtin_clzll(1) - __builtin_clzll(significand_as_int);
exponent_in_bin_or_dec += significand_magnitude_in_bin;
if (!(-1023 <= exponent_in_bin_or_dec && exponent_in_bin_or_dec < 1023)) {
num_lit->overflow = true;
return;
}
// this should chop off exactly one 1 bit from the top.
significand_bits = ((uint64_t)significand_as_int << (52 - significand_magnitude)) & 0xfffffffffffffULL;
exponent_bits = exponent + 1023;
// this should chop off exactly one 1 bit from the top.
significand_bits = ((uint64_t)significand_as_int << (52 - significand_magnitude_in_bin)) & 0xfffffffffffffULL;
exponent_bits = exponent_in_bin_or_dec + 1023;
}
} else {
// 0 is all 0's
significand_bits = 0;