From aa461c85741f0c1a1768ac20a90b1b8cbb00de45 Mon Sep 17 00:00:00 2001 From: Jesse McDonald Date: Tue, 17 Nov 2009 00:53:36 -0600 Subject: [PATCH] Add basic support for reading floating-point values. No guarantees are made as to the precision. --- reader.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/reader.c b/reader.c index e3a49be..26b7a99 100644 --- a/reader.c +++ b/reader.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "gc.h" #include "builtin.h" @@ -309,9 +310,114 @@ static value_t read_fixnum(reader_state_t *state, int radix) static value_t read_number(reader_state_t *state) { - value_t v = read_fixnum(state, 0); + bool negative = false; + fixnum_t num = 0; + native_float_t flt; + int radix; + + if (state->ch == '-') + { + negative = true; + next_char(state); + } + else if (state->ch == '+') + { + next_char(state); + } + + release_assert(isdigit(state->ch)); + + if (state->ch == '0') + { + next_char(state); + + switch (state->ch) + { + case 'X': + case 'x': + next_char(state); + radix = 16; + break; + case 'B': + case 'b': + next_char(state); + radix = 2; + break; + case '0' ... '9': + ungetc(state->ch, state->file); + state->ch = '0'; + radix = 8; + break; + default: + ungetc(state->ch, state->file); + state->ch = '0'; + radix = 10; + break; + } + + if (radix != 8) + { + /* Make sure we have at least one digit; if octal then the '0' counts instead */ + release_assert(isalnum(state->ch)); + release_assert(char_to_digit(state->ch) < radix); + } + } + else + { + radix = 10; + } + + while (isalnum(state->ch)) + { + fixnum_t digit = char_to_digit(state->ch); + + if (digit >= radix) + break; + + release_assert(num <= (FIXNUM_MAX/radix)); + + num *= radix; + num += digit; + + next_char(state); + } + + if (negative) + num = -num; + + if ((radix != 10) || ((state->ch != '.') && (state->ch != 'E') && (state->ch != 'e'))) + { + release_assert(!issymbol(state->ch)); + release_assert((FIXNUM_MIN <= num) && (num <= FIXNUM_MAX)); + return fixnum_value(num); + } + + /* + * Floating-point. No guarantees as to precision... really should use binary/hex. + */ + + flt = num; + + if (state->ch == '.') + { + next_char(state); + + for (native_float_t pv = negative ? -0.1 : 0.1; isdigit(state->ch); pv /= 10) + { + flt += (state->ch - '0') * pv; + next_char(state); + } + } + + if ((state->ch == 'E') || (state->ch == 'e')) + { + next_char(state); + num = read_fixnum(state, 10); + flt *= pow(10, _get_fixnum(num)); + } + release_assert(!issymbol(state->ch)); - return v; + return make_float(flt); } static value_t read_string(reader_state_t *state)