diff --git a/README.md b/README.md index 201e438..31f8c4c 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,12 @@ described below. Here's some places that have seen recent work + * Fix NaN comparisons and make dicts containing NaN keys work. + [Jake Edge's article, “Revisiting NaNs in Python”](https://lwn.net/Articles/869231/) + led to the discovery that Snek had several bugs in NaN + comparisons. Snek now computes the right value for comparisons with + Nan, as well as permitting NaN to be used as a key in dictionaries. + * Support explicit serial synchronization using ENQ/ACK so that applications sending lots of data do not require OS flow control support. With many devices connecting via USB/serial adapters that diff --git a/build-snek b/build-snek index 0d9dd74..1d6345c 100755 --- a/build-snek +++ b/build-snek @@ -2,6 +2,7 @@ export SNEKMAC=imac.keithp.com:build/snek export SNEK_RISCV_TEST=1 export SNEK_ESP32=1 +export SNEK_NANO_EVERY=1 export SNEK_OTHEROS=1 export ESP_IDF=$HOME/src/esp/esp-idf wakeonlan 58:40:4e:ee:39:1a diff --git a/chips/atmega/snek-atmega.defs b/chips/atmega/snek-atmega.defs index 8672086..19525a1 100644 --- a/chips/atmega/snek-atmega.defs +++ b/chips/atmega/snek-atmega.defs @@ -32,7 +32,7 @@ SNEK_ATMEGA_MATH_SRC = \ SNEK_ATMEGA_SRC = \ snek-atmega-eeprom.c \ snek-io.c \ - snek-strtof.c \ + snek-atof.c \ snek-atmega-serial.c \ snek-atmega-time.c diff --git a/chips/atmega/snek-atmega.h b/chips/atmega/snek-atmega.h index ca77816..eed440f 100644 --- a/chips/atmega/snek-atmega.h +++ b/chips/atmega/snek-atmega.h @@ -21,7 +21,8 @@ #include #define SNEK_DEBUG 0 -#define strtof(a,b) strtod(a,b) +float atoff(const char *); +#define strtof(s, n) atoff(s) #define PARSE_TABLE_DECLARATION(t) PROGMEM t #define PARSE_TABLE_FETCH_TOKEN(a) ((token_key_t) pgm_read_byte(a)) #define PARSE_TABLE_FETCH_INDEX(a) ((uint8_t) pgm_read_byte(a)) @@ -51,7 +52,8 @@ static const char PROGMEM __fmt__[] = (fmt); \ sprintf_P(dst, __fmt__, ##args); \ }) -#define strfromf(dst, len, fmt, val) sprintf_const(dst, fmt, val) +#define strfromf_const(dst, len, fmt, val) sprintf_const(dst, fmt, val) +#define strfromf(dst, len, fmt, val) sprintf(dst, fmt, val) #define SNEK_BUILTIN_NAMES_DECLARE(n) PROGMEM n #define SNEK_BUILTIN_NAMES(a) ((uint8_t) pgm_read_byte(&snek_builtin_names[a])) diff --git a/chips/avr/snek-avr.defs b/chips/avr/snek-avr.defs index a03e117..2a896d2 100644 --- a/chips/avr/snek-avr.defs +++ b/chips/avr/snek-avr.defs @@ -26,7 +26,7 @@ SNEK_AVR_SRC = \ ao-usb-avr.c \ ao-notask.c \ ao-product.c \ - snek-strtof.c + snek-atof.c SNEK_AVR_INC = \ ao.h \ diff --git a/chips/avr/snek-avr.h b/chips/avr/snek-avr.h index 329046c..05f5588 100644 --- a/chips/avr/snek-avr.h +++ b/chips/avr/snek-avr.h @@ -29,7 +29,8 @@ sqrtf(float x) { } #define SNEK_DEBUG 0 -#define strtof(a,b) strtod(a,b) +float atoff(const char *); +#define strtof(s, n) atoff(s) #define SNEK_POOL 1024 #define SNEK_MAX_TOKEN 63 #define VALUE_STACK_SIZE 16 @@ -64,7 +65,8 @@ sqrtf(float x) { static const char PROGMEM __fmt__[] = (fmt); \ sprintf_P(dst, __fmt__, ##args); \ }) -#define strfromf(dst, len, fmt, val) sprintf_const(dst, fmt, val) +#define strfromf_const(dst, len, fmt, val) sprintf_const(dst, fmt, val) +#define strfromf(dst, len, fmt, val) sprintf(dst, fmt, val) #define SNEK_BUILTIN_NAMES_DECLARE(n) PROGMEM n #define SNEK_BUILTIN_NAMES(a) ((uint8_t) pgm_read_byte(&snek_builtin_names[a])) diff --git a/chips/samd21/ao-stdio.c b/chips/samd21/ao-stdio.c index 22f4aef..de8ef96 100644 --- a/chips/samd21/ao-stdio.c +++ b/chips/samd21/ao-stdio.c @@ -22,4 +22,17 @@ static FILE __stdio = FDEV_SETUP_STREAM(ao_usb_putc, snek_io_getc, ao_usb_flush, _FDEV_SETUP_RW); +#ifdef PICOLIBC_STDIO_GLOBALS +#ifdef __strong_reference +#define STDIO_ALIAS(x) __strong_reference(stdin, x); +#else +#define STDIO_ALIAS(x) FILE *const x = &__stdio; +#endif + +FILE *const stdin = &__stdio; +STDIO_ALIAS(stdout); +STDIO_ALIAS(stderr); + +#else FILE *const __iob[3] = { &__stdio, &__stdio, &__stdio }; +#endif diff --git a/chips/samd21/snek-eeprom.c b/chips/samd21/snek-eeprom.c index fcebcb4..df33357 100644 --- a/chips/samd21/snek-eeprom.c +++ b/chips/samd21/snek-eeprom.c @@ -73,8 +73,8 @@ snek_eeprom_load(void) { snek_interactive = false; ao_flash_read_init(); - save_getc = __iob[0]->get; - __iob[0]->get = snek_eeprom_getchar; + save_getc = stdin->get; + stdin->get = snek_eeprom_getchar; } int @@ -87,6 +87,6 @@ snek_eeprom_getchar(FILE *stream) return c; } snek_interactive = true; - __iob[0]->get = save_getc; + stdin->get = save_getc; return EOF; } diff --git a/ports/duemilanove/Makefile b/ports/duemilanove/Makefile index d17895a..f53fa37 100644 --- a/ports/duemilanove/Makefile +++ b/ports/duemilanove/Makefile @@ -56,6 +56,7 @@ OPT=-Os -frename-registers -funsigned-char -fno-jump-tables -mcall-prologues CFLAGS=$(OPT) -DF_CPU=16000000UL -mmcu=atmega328p -I. -I$(SNEK_LOCAL_VPATH) -g $(SNEK_CFLAGS) -Waddr-space-convert LDFLAGS=$(SNEK_LDFLAGS) \ -Wl,-uvfprintf -lprintf_flt -lm \ + -Wl,--defsym -Wl,__TEXT_REGION_LENGTH__=0x7e00 \ -Wl,-Map=$(MAP) all:: $(HEX) snek-$(BOARD)-install snek-$(BOARD)-install.1 diff --git a/ports/ev3/snekserver.c b/ports/ev3/snekserver.c index 86b01c1..e12c935 100644 --- a/ports/ev3/snekserver.c +++ b/ports/ev3/snekserver.c @@ -12,8 +12,7 @@ * General Public License for more details. */ -#define _GNU_SOURCE - +#include #include "utils.h" #include #include diff --git a/ports/grove/Makefile b/ports/grove/Makefile index 413b809..608eae1 100644 --- a/ports/grove/Makefile +++ b/ports/grove/Makefile @@ -54,6 +54,7 @@ OPT=-Os -frename-registers -funsigned-char -fno-jump-tables -mcall-prologues CFLAGS=$(OPT) -DF_CPU=16000000UL -mmcu=atmega328p -I. -I$(SNEK_LOCAL_VPATH) -g $(SNEK_CFLAGS) -Waddr-space-convert LDFLAGS=$(SNEK_LDFLAGS) \ -Wl,-uvfprintf -lprintf_flt -lm \ + -Wl,--defsym -Wl,__TEXT_REGION_LENGTH__=0x7e00 \ -Wl,-Map=$(MAP) all: $(HEX) snek-grove-install diff --git a/ports/nano-every/Makefile b/ports/nano-every/Makefile index 7577359..46c2cb9 100644 --- a/ports/nano-every/Makefile +++ b/ports/nano-every/Makefile @@ -26,13 +26,18 @@ SNEK_LOCAL_VPATH = $(SNEK_ATMEGA) SNEK_LOCAL_SRC = \ snek-pow.c \ snek-nano-every.c \ + snek-input.c \ + $(SNEK_ATMEGA_MATH_SRC) \ $(SNEK_ATMEGA_SRC) SNEK_LOCAL_INC = \ - $(SNEK_ATMEGA_INC) + $(SNEK_ATMEGA_INC) \ + $(SNEK_ATMEGA_MATH_INC) SNEK_LOCAL_BUILTINS = \ $(SNEK_ATMEGA_BUILTINS) \ + snek-input.builtin \ + snek-math.builtin \ snek-nano-every.builtin include $(SNEK_ROOT)/snek-install.defs diff --git a/ports/nano-every/snek-nano-every.builtin b/ports/nano-every/snek-nano-every.builtin index 9c8b3ba..b7b929c 100644 --- a/ports/nano-every/snek-nano-every.builtin +++ b/ports/nano-every/snek-nano-every.builtin @@ -34,5 +34,6 @@ A5, -2, 19 A6, -2, 20 A7, -2, 21 #include +#include #define SNEK_POOL 3584 #define SNEK_MAX_TOKEN 63 diff --git a/ports/xiao/Makefile b/ports/xiao/Makefile index 32b7167..1dc4d84 100644 --- a/ports/xiao/Makefile +++ b/ports/xiao/Makefile @@ -18,8 +18,8 @@ SNEK_SAMD21 = $(SNEK_ROOT)/chips/samd21 PROGNAME=snek-xiao PRODUCT_NAME=SnekXiao -IDPRODUCT=0x8057 -IDVENDOR=0x2341 +IDPRODUCT=0x002e +IDVENDOR=0x2886 include $(SNEK_SAMD21)/snek-samd21.defs diff --git a/snek-strtof.c b/snek-atof.c similarity index 79% rename from snek-strtof.c rename to snek-atof.c index 12fd776..d9dc8f0 100644 --- a/snek-strtof.c +++ b/snek-atof.c @@ -28,8 +28,6 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* $Id: strtod.c 2191 2010-11-05 13:45:57Z arcanum $ */ - #include #include @@ -38,27 +36,27 @@ #include #ifdef AVR -#define strtof strtod #define float double #include #else -#define PROGMEM +#define __flash #define pgm_read_dword(x) (*x) #endif +#include /* Only GCC 4.2 calls the library function to convert an unsigned long to float. Other GCC-es (including 4.3) use a signed long to float conversion along with a large inline code to correct the result. */ extern float __floatunsisf (unsigned long); -PROGMEM static const float pwr_p10 [6] = { - 1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32 +static const __flash float pwr_p10 [6] = { + 1e+32, 1e+16, 1e+8, 1e+4, 1e+2, 1e+1, }; -PROGMEM static const float pwr_m10 [6] = { - 1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32 +static const __flash float pwr_m10 [6] = { + 1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1, }; -/** The strtof() function converts the initial portion of the string pointed +/** The atof() function converts the initial portion of the string pointed to by \a nptr to float representation. The expected form of the string is an optional plus ( \c '+' ) or minus @@ -69,11 +67,7 @@ PROGMEM static const float pwr_m10 [6] = { Leading white-space characters in the string are not skipped. - The strtod() function returns the converted value, if any. - - If \a endptr is not \c NULL, a pointer to the character after the last - character used in the conversion is stored in the location referenced by - \a endptr. + The atof() function returns the converted value, if any. If no conversion is performed, zero is returned and the value of \a nptr is stored in the location referenced by \a endptr. @@ -83,8 +77,12 @@ PROGMEM static const float pwr_m10 [6] = { in \c errno. If the correct value would cause underflow, zero is returned and \c ERANGE is stored in \c errno. */ + +#define CASE_CONVERT ('a' - 'A') +#define TOLOWER(c) ((c) | CASE_CONVERT) + float -strtof (const char * nptr, char ** endptr) +atoff (const char * nptr) { union { unsigned long u32; @@ -99,11 +97,15 @@ strtof (const char * nptr, char ** endptr) #define FL_DOT 0x04 /* decimal '.' was */ #define FL_MEXP 0x08 /* exponent 'e' is neg. */ - if (endptr) - *endptr = (char *)nptr; - c = *nptr++; +#if defined(SNEK_BUILTIN_float) + if (TOLOWER(c) == 'n') + return NAN; + if (TOLOWER(c) == 'i') + return INFINITY; +#endif + flag = 0; x.u32 = 0; @@ -120,8 +122,7 @@ strtof (const char * nptr, char ** endptr) } else { if (flag & FL_DOT) exp -= 1; - /* x.u32 = x.u32 * 10 + c */ - x.u32 = (((x.u32 << 2) + x.u32) << 1) + c; + x.u32 = x.u32 * 10 + c; if (x.u32 >= (ULONG_MAX - 9) / 10) flag |= FL_OVFL; } @@ -134,7 +135,7 @@ strtof (const char * nptr, char ** endptr) c = *nptr++; } - if (c == (('e'-'0') & 0xff) || c == (('E'-'0') & 0xff)) + if (TOLOWER(c) == 'e' - '0') { int i; c = *nptr++; @@ -163,29 +164,22 @@ strtof (const char * nptr, char ** endptr) } } - if ((flag & FL_ANY) && endptr) - *endptr = (char *)nptr - 1; - x.flt = __floatunsisf (x.u32); /* manually */ if (x.flt != 0) { int pwr; + const __flash float *fptr; + if (exp < 0) { - nptr = (void *)(pwr_m10 + 5); + fptr = pwr_m10; exp = -exp; } else { - nptr = (void *)(pwr_p10 + 5); + fptr = pwr_p10; } for (pwr = 32; pwr; pwr >>= 1) { - for (; exp >= pwr; exp -= pwr) { - union { - unsigned long u32; - float flt; - } y; - y.u32 = pgm_read_dword ((float *)nptr); - x.flt *= y.flt; - } - nptr -= sizeof(float); + for (; exp >= pwr; exp -= pwr) + x.flt *= *fptr; + fptr++; } } diff --git a/snek-exec.c b/snek-exec.c index 5352d4d..069bab4 100644 --- a/snek-exec.c +++ b/snek-exec.c @@ -290,30 +290,35 @@ snek_binary(snek_poly_t a, snek_op_t op, snek_poly_t b, bool inplace) * in snek, so no type checking needed here. */ if (op <= snek_op_is_not) { - int8_t cmp = snek_poly_cmp(a, b, op >= snek_op_is); bool v; - switch (op) { - case snek_op_eq: - case snek_op_is: - v = cmp == 0; - break; - case snek_op_ne: - case snek_op_is_not: - v = cmp != 0; - break; - case snek_op_gt: - v = cmp > 0; - break; - case snek_op_lt: - v = cmp < 0; - break; - case snek_op_ge: - v = cmp >= 0; - break; - case snek_op_le: - default: - v = cmp <= 0; - break; + if (op < snek_op_is && (snek_is_nan(a) || snek_is_nan(b))) + v = (op == snek_op_ne); + else + { + int8_t cmp = snek_poly_cmp(a, b, op >= snek_op_is); + switch (op) { + case snek_op_eq: + case snek_op_is: + v = cmp == 0; + break; + case snek_op_ne: + case snek_op_is_not: + v = cmp != 0; + break; + case snek_op_gt: + v = cmp > 0; + break; + case snek_op_lt: + v = cmp < 0; + break; + case snek_op_ge: + v = cmp >= 0; + break; + case snek_op_le: + default: + v = cmp <= 0; + break; + } } return snek_bool_to_poly(v); } diff --git a/snek-gram.ll b/snek-gram.ll index c31ae18..b32a516 100644 --- a/snek-gram.ll +++ b/snek-gram.ll @@ -38,7 +38,7 @@ command : @{ snek_print_val = snek_interactive; }@ stat }@ OP opt-formals CP COLON suite @{ - if (snek_compile[snek_compile_prev] == snek_op_return) + if (snek_compile[snek_code_prev_insn()] == snek_op_return) snek_code_delete_prev(); else snek_code_add_op(snek_op_null); @@ -215,7 +215,7 @@ if-expr : expr COLON snek_code_add_op_offset(snek_op_branch_false, 0); /* push 0 - if_expr_off */ - value_push_offset(snek_compile_prev); + value_push_offset(snek_code_prev_insn()); }@ ; while-stat : @@ -227,13 +227,13 @@ while-stat : @{ snek_code_add_op_offset(snek_op_branch_false, 0); /* push 1 - while_off */ - value_push_offset(snek_compile_prev); + value_push_offset(snek_code_prev_insn()); }@ suite @{ /* push 2 - loop_end_off */ snek_code_add_op_offset(snek_op_branch, 0); - value_push_offset(snek_compile_prev); + value_push_offset(snek_code_prev_insn()); /* push 3 - while_else_stat_off */ value_push_offset(snek_code_current()); }@ @@ -251,9 +251,10 @@ for-stat : FOR NAME @{ snek_code_add_op_offset(snek_op_branch, 0); /* push 2 - loop_end_off */ - value_push_offset(snek_compile_prev); + value_push_offset(snek_code_prev_insn()); /* push 3 - while_else_stat_off */ value_push_offset(snek_code_current()); + }@ @{ for_depth--; }@ while-else-stat @@ -268,9 +269,9 @@ for-params : RANGE OP opt-actuals CP COLON snek_code_add_in_range(id, num, for_depth); for_push_prevs: /* push 0 - for_off */ - value_push_offset(snek_compile_prev); + value_push_offset(snek_code_prev_insn()); /* push 1 - top_off */ - value_push_offset(snek_compile_prev); + value_push_offset(snek_code_prev_insn()); for_depth++; }@ | expr COLON @@ -300,10 +301,7 @@ suite : simple-stat expr : expr-and expr-or-p ; expr-or-p : OR - @{ - snek_code_add_op_offset(snek_op_branch_true, 0); - value_push_offset(snek_compile_prev); - }@ + @ bool_branch(snek_op_branch_true); @ expr-and @ short_second(); @ expr-or-p @@ -312,10 +310,7 @@ expr-or-p : OR expr-and : expr-not expr-and-p ; expr-and-p : AND - @{ - snek_code_add_op_offset(snek_op_branch_false, 0); - value_push_offset(snek_compile_prev); - }@ + @ bool_branch(snek_op_branch_false); @ expr-not @ short_second(); @ expr-and-p @@ -495,10 +490,10 @@ expr-prim : OP opt-tuple CP }@ | OS opt-actuals CS @{ - snek_offset_t num = value_pop().offset; - if (num >= 256) + snek_offset_t offset = value_pop().offset; + if (offset >= 256) return parse_return_syntax; - snek_code_add_op_offset(snek_op_list, num); + snek_code_add_op_offset(snek_op_list, offset); }@ {SNEK_DICT | OC @@ -509,8 +504,8 @@ expr-prim : OP opt-tuple CP opt-dict-ents CC @{ /* Fetch the number of entries compiled */ - snek_offset_t num = value_pop().offset; - snek_code_add_op_offset(snek_op_dict, num); + snek_offset_t offset = value_pop().offset; + snek_code_add_op_offset(snek_op_dict, offset); }@ } | NAME diff --git a/snek-install.defs b/snek-install.defs index 49c5a9d..784d5b3 100644 --- a/snek-install.defs +++ b/snek-install.defs @@ -20,6 +20,10 @@ SNEK_ESP32 ?= 0 ifneq "$(SNEK_ESP32)" "0" SNEK_PORTS_ESP32=$(SNEK_PORTS)/esp32/snek-esp32-$(SNEK_VERSION).bin endif +SNEK_NANO_EVERY ?= 0 +ifneq "$(SNEK_NANO_EVERY)" "0" +SNEK_PORTS_NANO_EVERY=$(SNEK_PORTS)/nano-every/snek-nano-every-$(SNEK_VERSION).hex +endif #SNEKMAC ?= imac-remote:build/snek FIRMWARE ?= \ $(SNEK_PORTS)/crickit/snek-crickit-$(SNEK_VERSION).uf2 \ @@ -41,7 +45,8 @@ FIRMWARE ?= \ $(SNEK_PORTS)/uduino/snek-uduino-$(SNEK_VERSION).hex \ $(SNEK_PORTS)/uno/snek-uno-$(SNEK_VERSION).hex \ $(SNEK_PORTS)/xiao/snek-xiao-$(SNEK_VERSION).uf2 \ - $(SNEK_PORTS_HIFIVE1REVB) $(SNEK_PORTS_ESP32) + $(SNEK_PORTS_HIFIVE1REVB) $(SNEK_PORTS_ESP32) \ + $(SNEK_PORTS_NANO_EVERY) USBFIRMWARE ?= \ $(SNEK_PORTS)/nano33iot/update-bootloader-nano33iot.ino \ diff --git a/snek-parse.c b/snek-parse.c index fcd7a1f..e06b420 100644 --- a/snek-parse.c +++ b/snek-parse.c @@ -17,9 +17,6 @@ bool snek_interactive; bool snek_parse_middle; -static uint8_t for_depth; - -static bool snek_print_val; uint8_t snek_parse_nformal; uint8_t snek_parse_nnamed; @@ -39,6 +36,10 @@ snek_token_val_t snek_token_val; #define snek_no_dict 0 #endif +#define PARSE_TOP \ + uint8_t for_depth = 0; \ + bool snek_print_val = 0; + #define GRAMMAR_TABLE #ifdef PARSE_DEBUG #define TOKEN_NAMES @@ -208,6 +209,12 @@ static inline void unop_second(void) snek_code_add_op(value_pop().op); } +static inline void bool_branch(snek_op_t op) +{ + snek_code_add_op_offset(op, 0); + value_push_offset(snek_compile_prev); +} + #define PARSE_CODE #include "snek-gram.h" @@ -225,7 +232,6 @@ snek_parse(void) /* Reset parse state */ snek_parse_middle = false; value_stack_p = 0; - for_depth = 0; /* Reset codegen state */ snek_code_reset(); diff --git a/snek-poly.c b/snek-poly.c index cc3e8fb..89ffc52 100644 --- a/snek-poly.c +++ b/snek-poly.c @@ -87,6 +87,9 @@ snek_poly_cmp(snek_poly_t a, snek_poly_t b, bool is) int sdiff; switch (atype) { case snek_float: + tdiff = snek_is_nan(a) - snek_is_nan(b); + if (tdiff) + return tdiff; return (b.f < a.f) - (a.f < b.f); case snek_string: sdiff = strcmp(snek_poly_to_string(a), snek_poly_to_string(b)); @@ -159,3 +162,9 @@ snek_poly_get_soffset(snek_poly_t a) { return (snek_soffset_t) snek_poly_get_float(a); } + +bool +snek_is_nan(snek_poly_t p) +{ + return p.u == SNEK_NAN_U; +} diff --git a/snek-print.c b/snek-print.c index 01d1adb..cfc28bd 100644 --- a/snek-print.c +++ b/snek-print.c @@ -87,7 +87,7 @@ snek_poly_format(snek_buf_t *buf, snek_poly_t a, char format) case 'G': if (atype != snek_float) break; - sprintf(tmp, format_string, printf_float(snek_poly_to_float(a))); + strfromf(tmp, sizeof(tmp), format_string, snek_poly_to_float(a)); buf->put_s(tmp, closure); return; case 'c': @@ -114,7 +114,7 @@ snek_poly_format(snek_buf_t *buf, snek_poly_t a, char format) buf->put_s("None", closure); else switch (atype) { case snek_float: - strfromf(tmp, sizeof(tmp), "%.9g", snek_poly_to_float(a)); + strfromf_const(tmp, sizeof(tmp), "%.9g", snek_poly_to_float(a)); buf->put_s(tmp, closure); break; case snek_string: diff --git a/snek.defs b/snek.defs index b9f2583..53a4a9e 100644 --- a/snek.defs +++ b/snek.defs @@ -7,8 +7,8 @@ vpath %.ll $(SNEK_LOCAL_VPATH):$(SNEK_ROOT) vpath %.py $(SNEK_LOCAL_VPATH):$(SNEK_ROOT) vpath %.builtin $(SNEK_LOCAL_VPATH):$(SNEK_ROOT) -SNEK_VERSION = 1.6 -SNEK_DATE = 2021-08-10 +SNEK_VERSION = 1.7 +SNEK_DATE = 2021-09-25 SNEK_VERSION_DASH = $(shell echo $(SNEK_VERSION) | sed 's/\./-/g') diff --git a/snek.h b/snek.h index 6743f8c..3e58367 100644 --- a/snek.h +++ b/snek.h @@ -337,11 +337,8 @@ extern snek_offset_t snek_stackp; extern snek_poly_t snek_a; extern snek_code_t *snek_code; -static inline bool -snek_is_nan(snek_poly_t p) -{ - return p.u == SNEK_NAN_U; -} +bool +snek_is_nan(snek_poly_t p); static inline bool snek_is_null(snek_poly_t p) @@ -574,6 +571,10 @@ snek_panic(const char *message); #define sprintf_const sprintf #endif +#ifndef strfromf_const +#define strfromf_const strfromf +#endif + extern bool snek_abort; /* snek-frame.c */ diff --git a/test/Makefile b/test/Makefile index b0fcbdc..99592fb 100644 --- a/test/Makefile +++ b/test/Makefile @@ -42,6 +42,7 @@ SUCCESS_TESTS = \ pass-if.py \ pass-int.py \ pass-math.py \ + pass-nan.py \ pass-op.py \ pass-random.py \ pass-range.py \ diff --git a/test/pass-nan.py b/test/pass-nan.py new file mode 100644 index 0000000..3f398a8 --- /dev/null +++ b/test/pass-nan.py @@ -0,0 +1,41 @@ +#!/usr/bin/python3 +# +# Copyright © 2021 Keith Packard +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# + +# +# Make sure that when nan is compared with any number +# the result is false for any op other than != +# + +nan = float("nan") +inf = float("inf") + +for x in (nan, -nan, -inf, inf, -1, 0, 1): + assert not (nan == x) + assert not (x == nan) + + assert nan != x + assert x != nan + + assert not (nan > x) + assert not (x > nan) + + assert not (nan < x) + assert not (x < nan) + + assert not (nan >= x) + assert not (x >= nan) + + assert not (nan <= x) + assert not (x <= nan) diff --git a/test/pass-op.py b/test/pass-op.py index 0de2476..fd52d54 100644 --- a/test/pass-op.py +++ b/test/pass-op.py @@ -51,3 +51,23 @@ exit(1) if not math.isclose(-7.8 % 2.2, 1.0): exit(1) +inf = float("inf") + +vals = (-inf, -2, -1, 0, 1, 2, inf) + +for first in range(0, len(vals)): + assert vals[first] == vals[first] + 0 + + for second in range(first, len(vals)): + assert vals[first] <= vals[second] + 0 + + assert vals[second] >= vals[first] + 0 + + for second in range(first + 1, len(vals)): + assert vals[first] < vals[second] + 0 + + assert vals[second] > vals[first] + 0 + + for second in range(0, len(vals)): + if second != first: + assert vals[first] != vals[second] + 0