vextoly/cryn
0.1.2
Commit 68a4b86 · Vextoly · 2026-10-04T20:08:00+02:00
Comments
No comments yet.
Diff
diff --git a/README.md b/README.md
index d6caf2c..580ecf8 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,8 @@ Cryn source -> lexer -> parser -> AST -> semantic analysis -> Cryn IR
```
The bootstrap compiler (`cryn`) is written in portable ISO C11 and has no
-dependencies beyond a C compiler and pthreads.
+dependencies beyond a C compiler, pthreads, and libm (`fmod`, used by
+float `remainder`).
---
diff --git a/src/be_c.c b/src/be_c.c
index eed7925..41d038e 100644
--- a/src/be_c.c
+++ b/src/be_c.c
@@ -387,12 +387,13 @@ static void emit_fn(Buf* o, ProgramIR* p, int fi) {
if (in->op == I_DIV)
buf_puts(o, "wv.d = wv.d / b; }\n");
else if (in->op == I_MOD) {
+ /* IEEE remainder (C fmod): x - b*(x/b) is NOT the
+ remainder (the quotient must truncate toward zero);
+ fmod/fmodf also give the right NaN/infinity cases */
if (t->kind == TY_FLOAT32)
- /* compute and round in single precision like the
- other float32 operations */
- buf_puts(o, "wv.d = (double)((float)wv.d - (float)b * ((float)wv.d / (float)b)); }\n");
+ buf_puts(o, "wv.d = (double)fmodf((float)wv.d, (float)b); }\n");
else
- buf_puts(o, "wv.d = wv.d - b * (wv.d / b); }\n");
+ buf_puts(o, "wv.d = fmod(wv.d, b); }\n");
}
else if (t->kind == TY_FLOAT32)
buf_printf(o, "wv.d = (double)((float)wv.d %c (float)b); }\n", opstr);
@@ -535,7 +536,7 @@ static void emit_fn(Buf* o, ProgramIR* p, int fi) {
break;
}
case I_CONV: {
- const Type* from = cur;
+ const Type* from = in->u ? in->u : cur; /* explicit source when given */
Type* to = in->t;
const char* ct = ctype(to);
if (ty_is_float(from)) {
diff --git a/src/check.c b/src/check.c
index ec667f5..92d91de 100644
--- a/src/check.c
+++ b/src/check.c
@@ -309,6 +309,30 @@ static int label_merge(Chk* c, int li, Loc loc) {
/* numeric type pairing */
/* ------------------------------------------------------------------ */
+/* Materialize integer-literal bits into float representation before an
+ operation whose resolved type is float: an intlit value holds integer
+ bits, a float operand holds double bits, and the pair may be adapted
+ (LANGUAGE.md 4.5) — the checker must emit the conversion. The float
+ representation is always double in wv; float32 operations narrow it. */
+static void coerce_intlit_float(Chk* c, Type** ty, Loc loc, int on_stack) {
+ if ((*ty)->kind != TY_INTLIT) return;
+ Type* f = ty_float64();
+ if (on_stack) {
+ /* bring the literal into the working value and back without
+ losing the current working value: swap, convert, swap. The
+ conversion carries its source type explicitly (the wv type at
+ this point is the other operand's). */
+ emit(c, I_SWAP, loc);
+ Insn* cv = emit(c, I_CONV, loc);
+ cv->u = *ty;
+ cv->t = f;
+ emit(c, I_SWAP, loc);
+ } else {
+ emit(c, I_CONV, loc)->t = f;
+ }
+ *ty = f;
+}
+
static Type* numeric_pair_type(Type* a, Type* b) {
if (a->kind == TY_INTLIT && b->kind == TY_INTLIT) return ty_int(64);
if (a->kind == TY_INTLIT) {
@@ -859,6 +883,14 @@ static void check_insn(Chk* c, Node* n) {
}
if (!lit_fits_rt(wv, rt, n->loc, "arithmetic")) return;
if (!lit_fits_rt(popped, rt, n->loc, "arithmetic")) return;
+ if (ty_is_float(rt)) {
+ /* materialize intlit bits into float representation (4.5) */
+ if (wv->kind == TY_INTLIT) coerce_intlit_float(c, &wv, n->loc, 0);
+ if (popped->kind == TY_INTLIT) {
+ coerce_intlit_float(c, &popped, n->loc, 1);
+ st->stk[st->nstk - 1] = popped;
+ }
+ }
Insn* in = emit(c, op, n->loc);
in->b = 0;
in->t = rt;
@@ -961,6 +993,8 @@ static void check_insn(Chk* c, Node* n) {
return;
}
if (!lit_fits_rt(lit, rt, n->loc, "comparison")) return;
+ if (ty_is_float(rt) && wv->kind == TY_INTLIT)
+ coerce_intlit_float(c, &wv, n->loc, 0);
}
Insn* in = emit(c, I_CMP, n->loc);
in->b = 1;
@@ -999,6 +1033,13 @@ static void check_insn(Chk* c, Node* n) {
}
if (!lit_fits_rt(wv, rt, n->loc, "comparison")) return;
if (!lit_fits_rt(popped, rt, n->loc, "comparison")) return;
+ if (ty_is_float(rt)) {
+ if (wv->kind == TY_INTLIT) coerce_intlit_float(c, &wv, n->loc, 0);
+ if (popped->kind == TY_INTLIT) {
+ coerce_intlit_float(c, &popped, n->loc, 1);
+ st->stk[st->nstk - 1] = popped;
+ }
+ }
}
Insn* in = emit(c, I_CMP, n->loc);
in->b = 0;
diff --git a/src/ir.h b/src/ir.h
index 6893e81..4c81b77 100644
--- a/src/ir.h
+++ b/src/ir.h
@@ -85,6 +85,8 @@ typedef struct Insn {
int a, b;
ImmV imm;
Type* t;
+ Type* u; /* secondary type: I_CONV explicit source type (NULL = wv's
+ current static type), reserved elsewhere */
Loc loc;
} Insn;
diff --git a/src/main.c b/src/main.c
index e9560f2..370753f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
#include <time.h>
#include <unistd.h>
-#define CRYN_VERSION "0.1.0"
+#define CRYN_VERSION "0.1.2"
static void usage(void) {
printf(
@@ -44,9 +44,9 @@ static char* find_host_cc(void) {
static int run_host_cc(const char* cc, const char* cfile, const char* out) {
char* cmd;
- size_t n = snprintf(NULL, 0, "%s -O2 -std=c11 -pthread -o %s %s", cc, out, cfile);
+ size_t n = snprintf(NULL, 0, "%s -O2 -std=c11 -pthread -o %s %s -lm", cc, out, cfile);
cmd = xmalloc(n + 1);
- snprintf(cmd, n + 1, "%s -O2 -std=c11 -pthread -o %s %s", cc, out, cfile);
+ snprintf(cmd, n + 1, "%s -O2 -std=c11 -pthread -o %s %s -lm", cc, out, cfile);
int rc = system(cmd);
free(cmd);
if (rc == -1) return -1;
diff --git a/src/rt/cryn_rt.c b/src/rt/cryn_rt.c
index a3c6150..1bd98b0 100644
--- a/src/rt/cryn_rt.c
+++ b/src/rt/cryn_rt.c
@@ -203,6 +203,10 @@ void crn_out_u64(uint64_t v) { fprintf(cr_ctx.out, "%llu", (unsigned long long)v
static void crn_out_f(double v, int maxprec) {
char buf[64];
+ if (v != v) { /* NaN: the sign is meaningless; print one spelling */
+ crn_out_str("nan");
+ return;
+ }
for (int prec = 1; prec <= maxprec; prec++) {
snprintf(buf, sizeof buf, "%.*g", prec, v);
if (strtod(buf, NULL) == v) break;
@@ -213,6 +217,10 @@ static void crn_out_f(double v, int maxprec) {
void crn_out_f64(double v) { crn_out_f(v, 17); }
void crn_out_f32(float v) {
char buf[64];
+ if (v != v) {
+ crn_out_str("nan");
+ return;
+ }
for (int prec = 1; prec <= 9; prec++) {
snprintf(buf, sizeof buf, "%.*g", prec, v);
if ((float)strtod(buf, NULL) == v) break;
diff --git a/tests/27_floatmod.cryn b/tests/27_floatmod.cryn
new file mode 100644
index 0000000..8511500
--- /dev/null
+++ b/tests/27_floatmod.cryn
@@ -0,0 +1,45 @@
+[# float remainder is the truncated IEEE remainder (fmod), not zero;
+ integer literals adapt to float operands at their point of use #]
+make string "\n" nl
+
+define newline result nothing
+ address nl
+ output text
+ set nothing
+ return
+end
+
+define main
+ set 2
+ convert float32
+ push
+ set 7.5
+ convert float32
+ remainder
+ output
+ call newline
+
+ set 7.5
+ push
+ set 2
+ remainder
+ output
+ call newline
+
+ set 2.5
+ push
+ set 5.25
+ remainder
+ output
+ call newline
+
+ set 0
+ convert float64
+ push
+ set 5
+ convert float64
+ remainder
+ output
+ call newline
+ return
+end
diff --git a/tests/expected/27_floatmod.txt b/tests/expected/27_floatmod.txt
new file mode 100644
index 0000000..b32f8c8
--- /dev/null
+++ b/tests/expected/27_floatmod.txt
@@ -0,0 +1,4 @@
+1.5
+2
+0.25
+nan