aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2026-01-11 23:03:00 -0500
committerBruce Hill <bruce@bruce-hill.com>2026-01-11 23:03:00 -0500
commit6894dc69374efcc23619e93b040a4224588054cd (patch)
tree045adf796e3291349b02d695b24e390929c8446e
parentb97ea6b6ac3498b21321e1f93ccc1a2dd145e9d7 (diff)
Refactor some AST logic to keep Ints/Reals as "Integer" and "Number" AST
nodes and use improved parsing logic from Int/Real
-rw-r--r--src/ast.h4
-rw-r--r--src/compile/functions.c13
-rw-r--r--src/compile/integers.c38
-rw-r--r--src/compile/lists.c12
-rw-r--r--src/compile/loops.c6
-rw-r--r--src/compile/promotions.c6
-rw-r--r--src/formatter/formatter.c5
-rw-r--r--src/parse/functions.c4
-rw-r--r--src/parse/numbers.c24
-rw-r--r--src/stdlib/bigint.h3
-rw-r--r--src/stdlib/reals.c170
-rw-r--r--src/typecheck.c4
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 <uniname.h>
#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: {