From 6894dc69374efcc23619e93b040a4224588054cd Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 11 Jan 2026 23:03:00 -0500 Subject: Refactor some AST logic to keep Ints/Reals as "Integer" and "Number" AST nodes and use improved parsing logic from Int/Real --- src/ast.h | 4 +- src/compile/functions.c | 13 ++-- src/compile/integers.c | 38 ++++++++--- src/compile/lists.c | 12 ++-- src/compile/loops.c | 6 +- src/compile/promotions.c | 6 +- src/formatter/formatter.c | 5 +- src/parse/functions.c | 4 +- src/parse/numbers.c | 24 +++++-- src/stdlib/bigint.h | 3 +- src/stdlib/reals.c | 170 ++++++++++++++++------------------------------ src/typecheck.c | 4 +- 12 files changed, 130 insertions(+), 159 deletions(-) diff --git a/src/ast.h b/src/ast.h index 023b390b..8a10e877 100644 --- a/src/ast.h +++ b/src/ast.h @@ -300,10 +300,10 @@ struct ast_s { const char *name; } Var; struct { - const char *str; + Int_t i; } Integer; struct { - double n; + Real_t n; } Number; struct { Text_t text; diff --git a/src/compile/functions.c b/src/compile/functions.c index 2784bedd..2f52cf18 100644 --- a/src/compile/functions.c +++ b/src/compile/functions.c @@ -7,7 +7,6 @@ #include "../stdlib/datatypes.h" #include "../stdlib/floats.h" #include "../stdlib/integers.h" -#include "../stdlib/optionals.h" #include "../stdlib/tables.h" #include "../stdlib/text.h" #include "../stdlib/util.h" @@ -82,8 +81,7 @@ Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_ if (spec_arg->type->tag == IntType && call_arg->value->tag == Integer) { value = compile_int_to_type(env, call_arg->value, spec_arg->type); } else if (spec_arg->type->tag == FloatType && call_arg->value->tag == Integer) { - OptionalInt_t int_val = Int$from_str(Match(call_arg->value, Integer)->str); - if (int_val.small == 0) code_err(call_arg->value, "Failed to parse this integer"); + Int_t int_val = Match(call_arg->value, Integer)->i; if (Match(spec_arg->type, FloatType)->bits == TYPE_NBITS64) value = Text$from_str(String(hex_double(Float64$from_int(int_val, false)))); else value = Text$from_str(String(hex_double((double)Float32$from_int(int_val, false)), "f")); @@ -106,8 +104,7 @@ Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_ if (spec_arg->type->tag == IntType && call_arg->value->tag == Integer) { value = compile_int_to_type(env, call_arg->value, spec_arg->type); } else if (spec_arg->type->tag == FloatType && call_arg->value->tag == Integer) { - OptionalInt_t int_val = Int$from_str(Match(call_arg->value, Integer)->str); - if (int_val.small == 0) code_err(call_arg->value, "Failed to parse this integer"); + Int_t int_val = Match(call_arg->value, Integer)->i; if (Match(spec_arg->type, FloatType)->bits == TYPE_NBITS64) value = Text$from_str(String(hex_double(Float64$from_int(int_val, false)))); else value = Text$from_str(String(hex_double((double)Float32$from_int(int_val, false)), "f")); @@ -829,13 +826,13 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static definition = Texts(definition, wrapper); } else if (cache && cache->tag == Integer) { assert(args); - OptionalInt64_t cache_size = Int64$parse(Text$from_str(Match(cache, Integer)->str), NONE_INT, NULL); + int64_t cache_size = Int64$from_int(Match(cache, Integer)->i, false); Text_t pop_code = EMPTY_TEXT; - if (cache->tag == Integer && cache_size.has_value && cache_size.value > 0) { + if (cache->tag == Integer && cache_size > 0) { // FIXME: this currently just deletes the first entry, but this // should be more like a least-recently-used cache eviction policy // or least-frequently-used - pop_code = Texts("if (cache.entries.length > ", cache_size.value, + pop_code = Texts("if (cache.entries.length > ", cache_size, ") Table$remove(&cache, cache.entries.data + " "cache.entries.stride*0, table_type);\n"); } diff --git a/src/compile/integers.c b/src/compile/integers.c index ea316218..ace98aa8 100644 --- a/src/compile/integers.c +++ b/src/compile/integers.c @@ -30,17 +30,17 @@ Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) { ", .has_value=true})"); } - const char *literal = Match(ast, Integer)->str; - OptionalInt_t int_val = Int$from_str(literal); - if (int_val.small == 0) code_err(ast, "Failed to parse this integer"); + OptionalInt_t int_val = Match(ast, Integer)->i; + + Text_t source = ast_source(ast); mpz_t i; mpz_init_set_int(i, int_val); char *c_literal; - if (strncmp(literal, "0x", 2) == 0 || strncmp(literal, "0X", 2) == 0 || strncmp(literal, "0b", 2) == 0) { + if (Text$has(source, Text("0x")) || Text$has(source, Text("0X")) || Text$has(source, Text("0b"))) { gmp_asprintf(&c_literal, "0x%ZX", i); - } else if (strncmp(literal, "0o", 2) == 0) { + } else if (Text$has(source, Text("0o"))) { gmp_asprintf(&c_literal, "%#Zo", i); } else { gmp_asprintf(&c_literal, "%#Zd", i); @@ -82,16 +82,32 @@ Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) { public Text_t compile_int(ast_t *ast) { - const char *str = Match(ast, Integer)->str; - OptionalInt_t int_val = Int$from_str(str); - if (int_val.small == 0) code_err(ast, "Failed to parse this integer"); + Int_t int_val = Match(ast, Integer)->i; mpz_t i; mpz_init_set_int(i, int_val); + + OptionalText_t source = ast_source(ast); + char *c_literal; + if (Text$has(source, Text("0x")) || Text$has(source, Text("0X")) || Text$has(source, Text("0b"))) { + gmp_asprintf(&c_literal, "0x%ZX", i); + } else if (Text$has(source, Text("0o"))) { + gmp_asprintf(&c_literal, "%#Zo", i); + } else { + gmp_asprintf(&c_literal, "%#Zd", i); + } + + // TODO: preserve base if (mpz_cmpabs_ui(i, BIGGEST_SMALL_INT) <= 0) { - return Texts("I_small(", str, ")"); + return Texts("I_small(", c_literal, ")"); } else if (mpz_cmp_si(i, INT64_MAX) <= 0 && mpz_cmp_si(i, INT64_MIN) >= 0) { - return Texts("Int$from_int64(", str, ")"); + return Texts("Int$from_int64(", c_literal, ")"); + } else if (source.tag == TEXT_NONE) { + return Texts("Int$from_str(\"", int_val, "\")"); } else { - return Texts("Int$from_str(\"", str, "\")"); + source = Text$replace(source, Text("("), Text("")); + source = Text$replace(source, Text(")"), Text("")); + source = Text$replace(source, Text(" "), Text("")); + source = Text$replace(source, Text("_"), Text("")); + return Texts("Int$from_str(\"", source, "\")"); } } diff --git a/src/compile/lists.c b/src/compile/lists.c index 9c969d2c..da125549 100644 --- a/src/compile/lists.c +++ b/src/compile/lists.c @@ -83,28 +83,28 @@ Text_t compile_list_method_call(env_t *env, ast_t *ast) { EXPECT_POINTER(); arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t, - .next = new (arg_t, .name = "at", .type = INT_TYPE, .default_val = FakeAST(Integer, .str = "0"))); + .next = new (arg_t, .name = "at", .type = INT_TYPE, .default_val = FakeAST(Integer, .i = I(0)))); return Texts("List$insert_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "insert_all")) { EXPECT_POINTER(); arg_t *arg_spec = new (arg_t, .name = "items", .type = self_value_t, - .next = new (arg_t, .name = "at", .type = INT_TYPE, .default_val = FakeAST(Integer, .str = "0"))); + .next = new (arg_t, .name = "at", .type = INT_TYPE, .default_val = FakeAST(Integer, .i = I(0)))); return Texts("List$insert_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "remove_at")) { EXPECT_POINTER(); arg_t *arg_spec = - new (arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Integer, .str = "-1"), - .next = new (arg_t, .name = "count", .type = INT_TYPE, .default_val = FakeAST(Integer, .str = "1"))); + new (arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Integer, .i = I(-1)), + .next = new (arg_t, .name = "count", .type = INT_TYPE, .default_val = FakeAST(Integer, .i = I(1)))); return Texts("List$remove_at(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "remove_item")) { EXPECT_POINTER(); arg_t *arg_spec = new ( arg_t, .name = "item", .type = item_t, - .next = new (arg_t, .name = "max_count", .type = INT_TYPE, .default_val = FakeAST(Integer, .str = "-1"))); + .next = new (arg_t, .name = "max_count", .type = INT_TYPE, .default_val = FakeAST(Integer, .i = I(-1)))); return Texts("List$remove_item_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "has")) { @@ -257,7 +257,7 @@ Text_t compile_list_method_call(env_t *env, ast_t *ast) { return Texts("Table$from_entries(", self, ", Table$info(", compile_type_info(item_t), ", &Present$$info))"); } else if (streq(call->name, "pop")) { EXPECT_POINTER(); - arg_t *arg_spec = new (arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Integer, "-1")); + arg_t *arg_spec = new (arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Integer, .i = I(-1))); Text_t index = compile_arguments(env, ast, arg_spec, call->args); return Texts("List$pop(", self, ", ", index, ", ", compile_type(item_t), ", _, ", promote_to_optional(item_t, Text("_")), ", ", compile_none(item_t), ")"); diff --git a/src/compile/loops.c b/src/compile/loops.c index f3ae9b59..8dd8eca3 100644 --- a/src/compile/loops.c +++ b/src/compile/loops.c @@ -125,7 +125,7 @@ Text_t compile_for_loop(env_t *env, ast_t *ast) { // Special case for Int.onward() arg_ast_t *args = Match(for_->iter, MethodCall)->args; arg_t *arg_spec = - new (arg_t, .name = "step", .type = INT_TYPE, .default_val = FakeAST(Integer, .str = "1"), .next = NULL); + new (arg_t, .name = "step", .type = INT_TYPE, .default_val = FakeAST(Integer, .i = I(1)), .next = NULL); Text_t step = compile_arguments(env, for_->iter, arg_spec, args); Text_t value = for_->vars ? compile(body_scope, for_->vars->ast) : Text("i"); return Texts("for (Int_t ", value, " = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", @@ -232,9 +232,7 @@ Text_t compile_for_loop(env_t *env, ast_t *ast) { case BigIntType: { Text_t n; if (for_->iter->tag == Integer) { - const char *str = Match(for_->iter, Integer)->str; - Int_t int_val = Int$from_str(str); - if (int_val.small == 0) code_err(for_->iter, "Failed to parse this integer"); + Int_t int_val = Match(for_->iter, Integer)->i; mpz_t i; mpz_init_set_int(i, int_val); if (mpz_cmpabs_ui(i, BIGGEST_SMALL_INT) <= 0) n = Text$from_str(mpz_get_str(NULL, 10, i)); diff --git a/src/compile/promotions.c b/src/compile/promotions.c index a13108e5..a4d6b9b3 100644 --- a/src/compile/promotions.c +++ b/src/compile/promotions.c @@ -3,6 +3,7 @@ #include "../ast.h" #include "../environment.h" #include "../stdlib/datatypes.h" +#include "../stdlib/reals.h" #include "../stdlib/text.h" #include "../typecheck.h" #include "../types.h" @@ -139,8 +140,11 @@ Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) { if (ast->tag == Integer && is_numeric_type(non_optional(t))) { return compile_int_to_type(env, ast, t); + } else if (ast->tag == Number && t->tag == RealType) { + return compile(env, ast); } else if (ast->tag == Number && t->tag == FloatType) { - double n = Match(ast, Number)->n; + Real_t real = Match(ast, Number)->n; + double n = Real$as_float64(real, true); switch (Match(t, FloatType)->bits) { case TYPE_NBITS64: return Text$from_str(String(hex_double(n))); case TYPE_NBITS32: return Text$from_str(String(hex_double(n), "f")); diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c index 3a6001b6..0441f9c0 100644 --- a/src/formatter/formatter.c +++ b/src/formatter/formatter.c @@ -13,6 +13,7 @@ #include "../stdlib/datatypes.h" #include "../stdlib/integers.h" #include "../stdlib/optionals.h" +#include "../stdlib/reals.h" #include "../stdlib/stdlib.h" #include "../stdlib/text.h" #include "args.h" @@ -329,11 +330,11 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) { return Match(ast, Bool)->b ? Text("yes") : Text("no"); /*inline*/ case Integer: { OptionalText_t source = ast_source(ast); - return source.length > 0 ? source : Text$from_str(Match(ast, Integer)->str); + return source.length > 0 ? source : Int$value_as_text(Match(ast, Integer)->i); } /*inline*/ case Number: { OptionalText_t source = ast_source(ast); - return source.length > 0 ? source : Text$from_str(String(Match(ast, Number)->n)); + return source.length > 0 ? source : Real$value_as_text(Match(ast, Number)->n); } /*inline*/ case Var: return Text$from_str(Match(ast, Var)->name); diff --git a/src/parse/functions.c b/src/parse/functions.c index e4eb1317..6dfa2509 100644 --- a/src/parse/functions.c +++ b/src/parse/functions.c @@ -119,7 +119,7 @@ ast_t *parse_func_def(parse_ctx_t *ctx, const char *pos) { if (match_word(&pos, "inline")) { is_inline = true; } else if (match_word(&pos, "cached")) { - if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Integer, .str = "-1"); + if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Integer, .i = I(-1)); } else if (match_word(&pos, "cache_size")) { whitespace(ctx, &pos); if (!match(&pos, "=")) parser_err(ctx, flag_start, pos, "I expected a value for 'cache_size'"); @@ -153,7 +153,7 @@ ast_t *parse_convert_def(parse_ctx_t *ctx, const char *pos) { if (match_word(&pos, "inline")) { is_inline = true; } else if (match_word(&pos, "cached")) { - if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Integer, .str = "-1"); + if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Integer, .i = I(-1)); } else if (match_word(&pos, "cache_size")) { whitespace(ctx, &pos); if (!match(&pos, "=")) parser_err(ctx, flag_start, pos, "I expected a value for 'cache_size'"); diff --git a/src/parse/numbers.c b/src/parse/numbers.c index 1ab774b4..1c9c6aa1 100644 --- a/src/parse/numbers.c +++ b/src/parse/numbers.c @@ -10,8 +10,14 @@ #include #include "../ast.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/integers.h" +#include "../stdlib/optionals.h" +#include "../stdlib/reals.h" +#include "../stdlib/text.h" #include "context.h" #include "errors.h" +#include "numbers.h" #include "utils.h" static const double RADIANS_PER_DEGREE = 0.0174532925199432957692369076848861271344287188854172545609719144; @@ -39,14 +45,16 @@ ast_t *parse_int(parse_ctx_t *ctx, const char *pos) { return NULL; if (match(&pos, "%")) { - double n = strtod(str, NULL) / 100.; - return NewAST(ctx->file, start, pos, Number, .n = n); + return parse_num(ctx, start); } else if (match(&pos, "deg")) { - double n = strtod(str, NULL) * RADIANS_PER_DEGREE; - return NewAST(ctx->file, start, pos, Number, .n = n); + return parse_num(ctx, start); } - return NewAST(ctx->file, start, pos, Integer, .str = str); + Text_t text = Text$from_strn(start, (size_t)(pos - start)); + Text_t remainder; + OptionalInt_t i = Int$parse(text, NONE_INT, &remainder); + assert(i.small != 0); + return NewAST(ctx->file, start, pos, Integer, .i = i); } ast_t *parse_num(parse_ctx_t *ctx, const char *pos) { @@ -77,5 +85,9 @@ ast_t *parse_num(parse_ctx_t *ctx, const char *pos) { if (match(&pos, "%")) d /= 100.; else if (match(&pos, "deg")) d *= RADIANS_PER_DEGREE; - return NewAST(ctx->file, start, pos, Number, .n = d); + Text_t text = Text$from_strn(start, (size_t)(pos - start)); + Text_t remainder; + OptionalReal_t real = Real$parse(text, &remainder); + if (Real$is_none(&real, &Real$info)) return NULL; + return NewAST(ctx->file, start, pos, Number, .n = real); } diff --git a/src/stdlib/bigint.h b/src/stdlib/bigint.h index ae95b224..2c93b5ec 100644 --- a/src/stdlib/bigint.h +++ b/src/stdlib/bigint.h @@ -47,7 +47,8 @@ bool Int$get_bit(Int_t x, Int_t bit_index); #define I_small(i) ((Int_t){.small = (int64_t)((uint64_t)(i) << 2L) | 1L}) #define I(i) _Generic(i, int8_t: I_small(i), int16_t: I_small(i), default: Int$from_int64(i)) -#define I_is_zero(i) ((i).small == 1L) +#define Int$is_zero(i) ((i).small == 1L) +#define Int$is_small(i) (((i).small & 1L) == 1L) Int_t Int$slow_plus(Int_t x, Int_t y); Int_t Int$slow_minus(Int_t x, Int_t y); diff --git a/src/stdlib/reals.c b/src/stdlib/reals.c index 6a6d7dc7..e35795c2 100644 --- a/src/stdlib/reals.c +++ b/src/stdlib/reals.c @@ -10,7 +10,6 @@ #include "bigint.h" #include "datatypes.h" #include "floats.h" -#include "optionals.h" #include "reals.h" #include "text.h" #include "types.h" @@ -207,6 +206,9 @@ double Real$as_float64(Real_t n, bool truncate) { public Real_t Real$plus(Real_t a, Real_t b) { + if (a.u64 == 0) return b; + else if (b.u64 == 0) return a; + if (!Real$is_boxed(a) && !Real$is_boxed(b)) { feclearexcept(FE_INEXACT); volatile double result = a.d + b.d; @@ -238,6 +240,9 @@ Real_t Real$plus(Real_t a, Real_t b) { public Real_t Real$minus(Real_t a, Real_t b) { + if (b.u64 == 0) return a; + else if (a.u64 == 0) return Real$negative(b); + if (!Real$is_boxed(a) && !Real$is_boxed(b)) { feclearexcept(FE_INEXACT); volatile double result = a.d - b.d; @@ -267,6 +272,9 @@ Real_t Real$minus(Real_t a, Real_t b) { public Real_t Real$times(Real_t a, Real_t b) { + if (a.d == 1.0) return b; + if (b.d == 1.0) return a; + if (!Real$is_boxed(a) && !Real$is_boxed(b)) { feclearexcept(FE_INEXACT); volatile double result = a.d * b.d; @@ -312,6 +320,8 @@ Real_t Real$times(Real_t a, Real_t b) { public Real_t Real$divided_by(Real_t a, Real_t b) { + if (b.d == 1.0) return a; + if (!Real$is_boxed(a) && !Real$is_boxed(b)) { feclearexcept(FE_INEXACT); volatile double result = a.d / b.d; @@ -684,133 +694,67 @@ public Text_t Real$value_as_text(Real_t n) { return Real$as_text(&n, false, &Real$info); } + public OptionalReal_t Real$parse(Text_t text, Text_t *remainder) { - const char *str = Text$as_c_string(text); - // Handle empty or null - if (!str || !*str) return NONE_REAL; - - const char *p = str; - const char *start = p; - - // Skip leading whitespace - while (*p == ' ' || *p == '\t') - p++; - - // Skip optional sign - if (*p == '-' || *p == '+') p++; - - // Must have at least one digit - if (!(*p >= '0' && *p <= '9')) return NONE_REAL; - - // Scan digits before decimal point - while (*p >= '0' && *p <= '9') - p++; - - // Check for decimal point - bool has_dot = (*p == '.'); - if (has_dot) { - p++; - // Scan digits after decimal point - while (*p >= '0' && *p <= '9') - p++; - } - - // Check for exponent (not supported yet, but detect it) - bool has_exp = (*p == 'e' || *p == 'E'); - if (has_exp) return NONE_REAL; // Don't support scientific notation yet - - // Now p points to first non-digit character - // Extract the valid number portion - ptrdiff_t num_len = p - start; - char buf[256]; - if (num_len >= 256) return NONE_REAL; - strncpy(buf, start, (size_t)num_len); - buf[num_len] = '\0'; - - // If there's remaining text and no remainder pointer, fail - if (*p != '\0' && remainder == NULL) return NONE_REAL; - - // Set remainder if provided - if (remainder) { - *remainder = Text$from_str(p); - } - - // Now parse buf as number - if (!has_dot) { - // Integer - char *endptr; - long long val = strtoll(buf, &endptr, 10); - if (endptr == buf + num_len) { - return Real$from_int64(val); + OptionalInt_t int_part = Int$parse(text, I(10), &text); + if (int_part.small == 0) { + if (Text$starts_with(text, Text("."), NULL)) { + int_part = I(0); + } else { + if (remainder) *remainder = text; + return NONE_REAL; } - - // Too large for int64, use GMP - OptionalInt_t b = Int$parse(Text$from_str(buf), NONE_INT, NULL); - if (b.small == 0) return NONE_REAL; - Int_t *bi = GC_MALLOC(sizeof(Int_t)); - *bi = b; - return box_ptr(bi, REAL_TAG_BIGINT); } - // Decimal - convert to rational - rational_t *r = GC_MALLOC(sizeof(rational_t)); - mpq_init(&r->value); + Real_t ret = Real$from_int(int_part); + + Text_t after_decimal; + if (Text$starts_with(text, Text("."), &after_decimal)) { + text = after_decimal; + // Count zeroes: + TextIter_t state = NEW_TEXT_ITER_STATE(text); + int64_t i = 0, digits = 0; + for (; i < text.length; i++) { + int32_t g = Text$get_grapheme_fast(&state, i); + if ('0' <= g && g <= '9') digits += 1; + else if (g == '_') continue; + else break; + } - // Count decimal places - const char *dot_pos = strchr(buf, '.'); - int decimal_places = 0; - if (dot_pos) { - const char *d = dot_pos + 1; - while (*d >= '0' && *d <= '9') { - decimal_places++; - d++; + if (digits > 0) { + // n = int_part + 10^digits * fractional_part + OptionalInt_t fractional_part = Int$parse(text, I(10), &after_decimal); + if (fractional_part.small != 0 && !Int$is_zero(fractional_part)) { + ret = Real$plus( + ret, Real$divided_by(Real$from_int(fractional_part), Real$from_float64(pow(10., (double)digits)))); + } } + text = after_decimal; } - // Remove decimal point - char int_buf[256]; - int j = 0; - for (int i = 0; buf[i] && j < 255; i++) { - if (buf[i] != '.') { - int_buf[j++] = buf[i]; + Text_t after_exp; + if (Text$starts_with(text, Text("e"), &after_exp) || Text$starts_with(text, Text("E"), &after_exp)) { + OptionalInt_t exponent = Int$parse(after_exp, I(10), &after_exp); + if (exponent.small != 0) { + // n *= 10^exp + if (!Int$is_zero(exponent)) { + if (Int$is_negative(exponent)) { + ret = Real$divided_by(ret, Real$from_float64(pow(10., -Float64$from_int(exponent, true)))); + } else { + ret = Real$times(ret, Real$from_float64(pow(10., Float64$from_int(exponent, true)))); + } + } + text = after_exp; } } - int_buf[j] = '\0'; - // Set numerator - if (mpz_set_str(mpq_numref(&r->value), int_buf, 10) != 0) { - mpq_clear(&r->value); + if (remainder) *remainder = text; + else if (text.length > 0) { return NONE_REAL; } - // Set denominator = 10^decimal_places - mpz_set_ui(mpq_denref(&r->value), 1); - for (int i = 0; i < decimal_places; i++) { - mpz_mul_ui(mpq_denref(&r->value), mpq_denref(&r->value), 10); - } - - mpq_canonicalize(&r->value); - - // Check if this can be represented exactly as a double - double d = mpq_get_d(&r->value); - if (isfinite(d)) { - // Convert back to rational to check for exact representation - rational_t temp; - mpq_init(&temp.value); - mpq_set_d(&temp.value, d); - - if (mpq_equal(&r->value, &temp.value)) { - // Can be represented exactly as double - mpq_clear(&temp.value); - mpq_clear(&r->value); - return make_double(d); - } - - mpq_clear(&temp.value); - } - - return box_ptr(r, REAL_TAG_RATIONAL); + return ret; } public diff --git a/src/typecheck.c b/src/typecheck.c index 166ae2e1..8b74e865 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -14,7 +14,6 @@ #include "naming.h" #include "parse/files.h" #include "parse/types.h" -#include "stdlib/optionals.h" #include "stdlib/paths.h" #include "stdlib/tables.h" #include "stdlib/text.h" @@ -1652,8 +1651,7 @@ PUREFUNC bool is_constant(env_t *env, ast_t *ast) { case None: return true; case Integer: { DeclareMatch(info, ast, Integer); - Int_t int_val = Int$parse(Text$from_str(info->str), NONE_INT, NULL); - if (int_val.small == 0) return false; // Failed to parse + Int_t int_val = info->i; return (Int$compare_value(int_val, I(BIGGEST_SMALL_INT)) <= 0); } case Number: { -- cgit v1.2.3