From 9c197965b30042df591c4d3b14d7ee2f0ff1f321 Mon Sep 17 00:00:00 2001 From: Justin Ethier Date: Tue, 14 Nov 2023 19:48:02 -0800 Subject: [PATCH 1/9] Cleanup, add TODO --- runtime.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/runtime.c b/runtime.c index 1c20a0f9..2d90ffe3 100644 --- a/runtime.c +++ b/runtime.c @@ -2557,7 +2557,8 @@ typedef enum { STR2INT_SUCCESS, STR2INT_OVERFLOW, STR2INT_UNDERFLOW, - STR2INT_INCONVERTIBLE + STR2INT_INCONVERTIBLE, + STR2INT_RATIONAL } str2int_errno; /* @@ -2588,12 +2589,18 @@ static str2int_errno str2int(int *out, char *s, int base) errno = 0; long l = strtol(s, &end, base); /* Both checks are needed because INT_MAX == LONG_MAX is possible. */ - if (l > CYC_FIXNUM_MAX /*INT_MAX*/ || (errno == ERANGE && l == LONG_MAX)) + if (l > CYC_FIXNUM_MAX /*INT_MAX*/ || (errno == ERANGE && l == LONG_MAX)) { return STR2INT_OVERFLOW; - if (l < CYC_FIXNUM_MIN /*INT_MIN*/ || (errno == ERANGE && l == LONG_MIN)) + } + if (l < CYC_FIXNUM_MIN /*INT_MIN*/ || (errno == ERANGE && l == LONG_MIN)) { return STR2INT_UNDERFLOW; - if (*end != '\0') + } + if (*end == '/') { + return STR2INT_RATIONAL; + } + if (*end != '\0') { return STR2INT_INCONVERTIBLE; + } *out = l; return STR2INT_SUCCESS; } @@ -2622,6 +2629,11 @@ object Cyc_string2number_(void *data, object cont, object str) rv = str2int(&result, s, 10); if (rv == STR2INT_SUCCESS) { _return_closcall1(data, cont, obj_int2obj(result)); + } else if (rv == STR2INT_RATIONAL) { + // TODO: function to handle this: + // - find / symbol + // - parse int on either side (could be bignums!) + // - for now, perform division and return float. longer-term, return rational } else if (str_is_bignum(rv, s)) { alloc_bignum(data, bn); if (MP_OKAY != mp_read_radix(&(bignum_value(bn)), s, 10)) { From c599dbb62a226ee2607bf22b7974b138716c40df Mon Sep 17 00:00:00 2001 From: Justin Ethier Date: Wed, 15 Nov 2023 19:43:25 -0800 Subject: [PATCH 2/9] WIP --- runtime.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/runtime.c b/runtime.c index 2d90ffe3..11fcf9db 100644 --- a/runtime.c +++ b/runtime.c @@ -2617,6 +2617,16 @@ int str_is_bignum(str2int_errno errnum, char *c) return 1; } +float string2rational(char *s){ +{ + // TODO: this is terrible, needs work: + char *nom = _strdup(s); + char *denom = strchr(s, '\'); + denom[0] = '\0'; + denom++; + return strtol(nom, NULL, 10) / strtol(denom, NULL, 10); +} + object Cyc_string2number_(void *data, object cont, object str) { int result, rv; From bb4e176e42f5079ee81fa682f482f37de153d154 Mon Sep 17 00:00:00 2001 From: Justin Ethier Date: Thu, 16 Nov 2023 19:37:36 -0800 Subject: [PATCH 3/9] WIP, read rationals as inexact nums --- CHANGELOG.md | 1 + runtime.c | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64fc54c0..5be088ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Features - Arthur Maciel added `make-opaque` to `(cyclone foreign)`. - Add `memory-streams` to the list of symbols that `(features)` can return, indicating that the current installation supports in-memory streams. +- Enhanced the reader to load rationals as inexact numbers. Bug Fixes diff --git a/runtime.c b/runtime.c index 11fcf9db..716e8075 100644 --- a/runtime.c +++ b/runtime.c @@ -2617,14 +2617,20 @@ int str_is_bignum(str2int_errno errnum, char *c) return 1; } -float string2rational(char *s){ +double string2rational(char *s) { - // TODO: this is terrible, needs work: char *nom = _strdup(s); - char *denom = strchr(s, '\'); + char *denom = strchr(nom, '/'); + if (denom == NULL) { + // Should never happen since we check for '/' elsewhere + return 0.0; + } denom[0] = '\0'; denom++; - return strtol(nom, NULL, 10) / strtol(denom, NULL, 10); +// TODO: check return values from strtol + double x = strtol(nom, NULL, 10); + double y = strtol(denom, NULL, 10); + return x / y; } object Cyc_string2number_(void *data, object cont, object str) @@ -2640,10 +2646,9 @@ object Cyc_string2number_(void *data, object cont, object str) if (rv == STR2INT_SUCCESS) { _return_closcall1(data, cont, obj_int2obj(result)); } else if (rv == STR2INT_RATIONAL) { - // TODO: function to handle this: - // - find / symbol - // - parse int on either side (could be bignums!) - // - for now, perform division and return float. longer-term, return rational + double d = string2rational(s); + make_double(result, d); + _return_closcall1(data, cont, &result); } else if (str_is_bignum(rv, s)) { alloc_bignum(data, bn); if (MP_OKAY != mp_read_radix(&(bignum_value(bn)), s, 10)) { From 705e70d12a7c458e7e45a1681cc1f6dcdf8a9424 Mon Sep 17 00:00:00 2001 From: Justin Ethier Date: Mon, 20 Nov 2023 18:37:34 -0800 Subject: [PATCH 4/9] Issue #513 - Add test case --- tests/base.scm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/base.scm b/tests/base.scm index 63e6dd91..4fd5496d 100644 --- a/tests/base.scm +++ b/tests/base.scm @@ -71,6 +71,11 @@ ;(test +inf.0 (exact +inf.0)) ) +(test-group + "rationals" + (test 3.0 (expt 81 1/4)) +) + (test-group "records" (define-record-type employee From cf5b273625695856fa4809c3367f53dcda19675d Mon Sep 17 00:00:00 2001 From: Justin Ethier Date: Mon, 20 Nov 2023 18:49:40 -0800 Subject: [PATCH 5/9] Issue #513 --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5be088ad..7f270f22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 0.36.0 - TBD +Features + +- Enhanced the reader to parse rationals and store them as inexact numbers. + Bug Fixes - Fix `exact` to properly handle complex numbers, including raising an error when passed `nan` or `inf` double values. @@ -20,7 +24,6 @@ Features - Arthur Maciel added `make-opaque` to `(cyclone foreign)`. - Add `memory-streams` to the list of symbols that `(features)` can return, indicating that the current installation supports in-memory streams. -- Enhanced the reader to load rationals as inexact numbers. Bug Fixes From 4d902f9a77e312477fb2e6ac9fa3182806f3b73f Mon Sep 17 00:00:00 2001 From: Justin Ethier Date: Mon, 20 Nov 2023 19:44:17 -0800 Subject: [PATCH 6/9] WIP - support for bignums in rational parsing --- runtime.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/runtime.c b/runtime.c index 716e8075..adde13fd 100644 --- a/runtime.c +++ b/runtime.c @@ -2617,7 +2617,7 @@ int str_is_bignum(str2int_errno errnum, char *c) return 1; } -double string2rational(char *s) +double string2rational(void *data, char *s) { char *nom = _strdup(s); char *denom = strchr(nom, '/'); @@ -2627,9 +2627,20 @@ double string2rational(char *s) } denom[0] = '\0'; denom++; +// TODO: clean this up // TODO: check return values from strtol - double x = strtol(nom, NULL, 10); - double y = strtol(denom, NULL, 10); +// double x = strtol(nom, NULL, 10); +// double y = strtol(denom, NULL, 10); + alloc_bignum(data, bn_nom); + if (MP_OKAY != mp_read_radix(&(bignum_value(bn_nom)), nom, 10)) { + Cyc_rt_raise2(data, "Error converting string to bignum", nom); + } + double x = mp_get_double(&bignum_value(bn_nom)); + alloc_bignum(data, bn_denom); + if (MP_OKAY != mp_read_radix(&(bignum_value(bn_denom)), denom, 10)) { + Cyc_rt_raise2(data, "Error converting string to bignum", denom); + } + double y = mp_get_double(&bignum_value(bn_denom)); return x / y; } @@ -2645,8 +2656,11 @@ object Cyc_string2number_(void *data, object cont, object str) rv = str2int(&result, s, 10); if (rv == STR2INT_SUCCESS) { _return_closcall1(data, cont, obj_int2obj(result)); - } else if (rv == STR2INT_RATIONAL) { - double d = string2rational(s); + } else if (rv == STR2INT_RATIONAL || + // TODO: is there a more efficient way? + ((rv == STR2INT_OVERFLOW || rv == STR2INT_UNDERFLOW) && + strchr(s, '/') != NULL)) { + double d = string2rational(data, s); make_double(result, d); _return_closcall1(data, cont, &result); } else if (str_is_bignum(rv, s)) { From 08bd3337013ced273ff0b66e24658c0970b22484 Mon Sep 17 00:00:00 2001 From: Justin Ethier Date: Mon, 27 Nov 2023 18:59:42 -0800 Subject: [PATCH 7/9] Cleanup --- runtime.c | 3 ++- tests/base.scm | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/runtime.c b/runtime.c index adde13fd..98576602 100644 --- a/runtime.c +++ b/runtime.c @@ -2657,7 +2657,8 @@ object Cyc_string2number_(void *data, object cont, object str) if (rv == STR2INT_SUCCESS) { _return_closcall1(data, cont, obj_int2obj(result)); } else if (rv == STR2INT_RATIONAL || - // TODO: is there a more efficient way? + // Could still be a rational if numerator is + // bignum, so in that case do one more scan ((rv == STR2INT_OVERFLOW || rv == STR2INT_UNDERFLOW) && strchr(s, '/') != NULL)) { double d = string2rational(data, s); diff --git a/tests/base.scm b/tests/base.scm index 4fd5496d..506a46b8 100644 --- a/tests/base.scm +++ b/tests/base.scm @@ -48,6 +48,11 @@ "rationals" (test 3.0 (numerator (/ 6 4))) (test 2.0 (denominator (/ 6 4))) + (test 3.0 (expt 81 1/4)) + (test #t + (< 1.0e+40 + (/ 33333333333333333333333333333333333333333 3.0) + 1.2e+40)) ) (test-group @@ -71,11 +76,6 @@ ;(test +inf.0 (exact +inf.0)) ) -(test-group - "rationals" - (test 3.0 (expt 81 1/4)) -) - (test-group "records" (define-record-type employee From 132c745330ef9614b7b0c85ab230fce5f89b7883 Mon Sep 17 00:00:00 2001 From: Justin Ethier Date: Mon, 27 Nov 2023 19:33:19 -0800 Subject: [PATCH 8/9] Cleanup --- runtime.c | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/runtime.c b/runtime.c index 98576602..0182cd3d 100644 --- a/runtime.c +++ b/runtime.c @@ -2617,9 +2617,22 @@ int str_is_bignum(str2int_errno errnum, char *c) return 1; } +/** + * @brief Read a rational number from given string. + * @param data Thread data object for the caller. + * @param char* String to read + * @return double Return number as double, since cyclone does + * not support a rational number type at this time + */ double string2rational(void *data, char *s) { + // Duplicate string so we can safely create separate strings + // for numerator and denominator char *nom = _strdup(s); + if (nom == NULL) { + return 0.0; + } + char *denom = strchr(nom, '/'); if (denom == NULL) { // Should never happen since we check for '/' elsewhere @@ -2627,20 +2640,22 @@ double string2rational(void *data, char *s) } denom[0] = '\0'; denom++; -// TODO: clean this up -// TODO: check return values from strtol -// double x = strtol(nom, NULL, 10); -// double y = strtol(denom, NULL, 10); - alloc_bignum(data, bn_nom); - if (MP_OKAY != mp_read_radix(&(bignum_value(bn_nom)), nom, 10)) { - Cyc_rt_raise2(data, "Error converting string to bignum", nom); - } - double x = mp_get_double(&bignum_value(bn_nom)); - alloc_bignum(data, bn_denom); - if (MP_OKAY != mp_read_radix(&(bignum_value(bn_denom)), denom, 10)) { - Cyc_rt_raise2(data, "Error converting string to bignum", denom); - } - double y = mp_get_double(&bignum_value(bn_denom)); + + // Parse both rational components as bignums since + // that code handles any integer + alloc_bignum(data, bn_nom); + if (MP_OKAY != mp_read_radix(&(bignum_value(bn_nom)), nom, 10)) { + Cyc_rt_raise2(data, "Error converting string to bignum", nom); + } + + alloc_bignum(data, bn_denom); + if (MP_OKAY != mp_read_radix(&(bignum_value(bn_denom)), denom, 10)) { + Cyc_rt_raise2(data, "Error converting string to bignum", denom); + } + + // Compute final result as double + double x = mp_get_double(&bignum_value(bn_nom)); + double y = mp_get_double(&bignum_value(bn_denom)); return x / y; } From 8bf60e9239c1c666ed42f53c73d5019460442658 Mon Sep 17 00:00:00 2001 From: Justin Ethier Date: Mon, 27 Nov 2023 19:43:38 -0800 Subject: [PATCH 9/9] Free memory --- runtime.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runtime.c b/runtime.c index 0182cd3d..9532a263 100644 --- a/runtime.c +++ b/runtime.c @@ -2653,6 +2653,9 @@ double string2rational(void *data, char *s) Cyc_rt_raise2(data, "Error converting string to bignum", denom); } + // Prevent memory leak + free(nom); + // Compute final result as double double x = mp_get_double(&bignum_value(bn_nom)); double y = mp_get_double(&bignum_value(bn_denom));