diff --git a/rail_native b/rail_native index bb8c71b..eef1bdd 100755 Binary files a/rail_native and b/rail_native differ diff --git a/tools/compile.rail b/tools/compile.rail index 1b5c245..102a1b4 100644 --- a/tools/compile.rail +++ b/tools/compile.rail @@ -975,7 +975,7 @@ is_float node env = let tg = head node if tg == "FL" then true else if tg == "V" then - efind (cat ["__float_", head (tail node)]) env != (0 - 1) + (efind (cat ["__float_", head (tail node)]) env != (0 - 1)) || (efind (cat ["__float_ret_", head (tail node)]) env != (0 - 1)) else if tg == "O" then let op = head (tail node) if op == "+." || op == "-." || op == "*." || op == "/." then true @@ -1144,10 +1144,16 @@ cg_bi2 fname args env ar lc sl tp fs = else if fname == "char_from_int" then let (aa, lc1, sl1) = cg (head args) env ar lc sl false fs -- Inverse of char_to_int: take a tagged int (ASCII code), return a - -- single-char Rail string. Allocates 2 bytes (char + null terminator), - -- writes the byte, wraps via _rail_wrap_str. Needed for buffer→string - -- reconstruction in pure-Rail socket I/O. - (cat [aa, " asr x0, x0, #1\n stp x29, x30, [sp, #-32]!\n mov x29, sp\n str x0, [x29, #16]\n mov x0, #2\n bl _rail_chained_malloc\n ldr x1, [x29, #16]\n strb w1, [x0]\n mov w1, #0\n strb w1, [x0, #1]\n bl _rail_wrap_str\n ldp x29, x30, [sp], #32\n"], lc1, sl1) + -- single-char Rail string. Builds a tagged string [tag=9, len=1, byte, + -- NUL] DIRECTLY with len hardcoded to 1, instead of going through + -- _rail_wrap_str (which recomputes length via _strlen and so truncates + -- to len=0 for a NUL byte). With this, `char_from_int 0` yields a true + -- 1-byte NUL string (length 1), and char_from_int 7/13/65/etc. are + -- byte-identical in behavior to before. NOTE: this fixes a SINGLE + -- char_from_int; embedded NULs still truncate through _strlen-based + -- concat (cat/join) — out of scope. Needed for buffer→string + -- reconstruction in pure-Rail socket I/O (CRLF uses 13/10, never 0). + (cat [aa, " asr x0, x0, #1\n stp x29, x30, [sp, #-32]!\n mov x29, sp\n str x0, [x29, #16]\n mov x0, #18\n bl _rail_alloc\n mov x1, #9\n str x1, [x0]\n mov x1, #1\n str x1, [x0, #8]\n ldr x1, [x29, #16]\n strb w1, [x0, #16]\n mov w1, #0\n strb w1, [x0, #17]\n ldp x29, x30, [sp], #32\n"], lc1, sl1) else if fname == "bit_and" then -- (int, int) -> int. ARM64 and on raw 63-bit values. Strip both tags, -- AND, re-tag. Operates on the full 63-bit int width; if the caller @@ -6243,9 +6249,15 @@ run_tests _ = let t168 = run_test "value_big_literal" "f x = if x == 0 then 16777216 else x\nmain =\n let _ = print (show (f 0))\n 0" "16777216" let t169 = run_test "float_modulo" "main =\n let a = 5.5\n let b = 2.0\n let _ = print (show_float (a % b))\n 0" "1.5" let s12 = (if t165 then 1 else 0) + (if t166 then 1 else 0) + (if t167 then 1 else 0) + (if t168 then 1 else 0) + (if t169 then 1 else 0) - let total = s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s_inf - let _ = print (cat [show total, "/175 tests passed"]) - if total == 175 then 0 else 1 + -- t171 (B1): char_from_int 0 must yield a 1-byte NUL string (length 1), not "". + let t171 = run_test "char_from_int_nul" "main =\n let s = char_from_int 0\n let _ = print (show (length s))\n 0" "1" + -- t172 (B2): a bare zero-arg top-level float constant in a binary op must be + -- classified as float by is_float (via __float_ret_) instead of segfaulting. + let t172 = run_test "top_float_const_binop" "l0 = 50.0\nl2 = 100.0\nmain =\n let _ = print (show_float (l0 + l2))\n 0" "150" + let s_bx = (if t171 then 1 else 0) + (if t172 then 1 else 0) + let total = s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s_inf + s_bx + let _ = print (cat [show total, "/177 tests passed"]) + if total == 177 then 0 else 1 self_compile _ = let _ = print "\n=== Self-compilation ==="