vextoly/cryn

0.1.2

Commit 68a4b86 · Vextoly · 2026-10-04T20:08:00+02:00

Changeset
68a4b8667ea290f6897cc475959b92f0b95cb0ff
Parents
ee91cb1c5258c2c3ad11f4e4a941fe636845c6c2

View source at this commit

Comments

No comments yet.

Log in to comment

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