From cb14a074133543f2d80f80695d6ce150a31b6205 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 9 May 2026 03:12:28 +0000 Subject: [PATCH] ocaml: phase 6 Printf %i/%u/%x/%X/%o + int_to_hex/octal host primitives (+5 tests, 533 total) Three new host primitives in eval.sx: _int_to_hex_lower n -> string of hex digits (lowercase) _int_to_hex_upper n -> string of hex digits (uppercase) _int_to_octal n -> string of octal digits Each builds the digit string by repeated floor(n / base) + mod, prepending the digit at each step. Negative numbers prefix '-' so the output round-trips through int_of_string with a sign. Printf walker now fans out: %d, %i, %u -> _string_of_int %f -> _string_of_float %x -> _int_to_hex_lower %X -> _int_to_hex_upper %o -> _int_to_octal %s, %c, %b -> existing handling Printf.sprintf '%x' 255 = 'ff' Printf.sprintf '%X' 4096 = '1000' Printf.sprintf '%o' 8 = '10' Printf.sprintf '%x %X %o' 255 4096 8 = 'ff 1000 10' --- lib/ocaml/eval.sx | 55 ++++++++++++++++++++++++++++++++++++++++++++ lib/ocaml/runtime.sx | 12 +++++++--- lib/ocaml/test.sh | 19 +++++++++++++++ plans/ocaml-on-sx.md | 7 ++++++ 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/lib/ocaml/eval.sx b/lib/ocaml/eval.sx index f14a8ef3..3b3afb9e 100644 --- a/lib/ocaml/eval.sx +++ b/lib/ocaml/eval.sx @@ -70,6 +70,61 @@ (list "_int_of_string" (fn (s) (parse-number s))) (list "_string_of_int" (fn (i) (str i))) (list "_string_of_float" (fn (f) (str f))) + ;; Integer formatting helpers used by Printf %x/%X/%o. + (list "_int_to_hex_lower" + (fn (n) + (cond + ((= n 0) "0") + (else + (let ((digits "0123456789abcdef") + (m (if (< n 0) (- 0 n) n)) + (out "")) + (begin + (define loop + (fn () + (when (> m 0) + (begin + (set! out (str (nth digits (mod m 16)) out)) + (set! m (floor (/ m 16))) + (loop))))) + (loop) + (if (< n 0) (str "-" out) out))))))) + (list "_int_to_hex_upper" + (fn (n) + (cond + ((= n 0) "0") + (else + (let ((digits "0123456789ABCDEF") + (m (if (< n 0) (- 0 n) n)) + (out "")) + (begin + (define loop + (fn () + (when (> m 0) + (begin + (set! out (str (nth digits (mod m 16)) out)) + (set! m (floor (/ m 16))) + (loop))))) + (loop) + (if (< n 0) (str "-" out) out))))))) + (list "_int_to_octal" + (fn (n) + (cond + ((= n 0) "0") + (else + (let ((digits "01234567") + (m (if (< n 0) (- 0 n) n)) + (out "")) + (begin + (define loop + (fn () + (when (> m 0) + (begin + (set! out (str (nth digits (mod m 8)) out)) + (set! m (floor (/ m 8))) + (loop))))) + (loop) + (if (< n 0) (str "-" out) out))))))) (list "_char_code" (fn (c) (char-code c))) (list "_char_chr" (fn (n) (char-from-code n))) ;; Print: route to host SX `display` (no automatic newline). diff --git a/lib/ocaml/runtime.sx b/lib/ocaml/runtime.sx index b1b9bf04..280a9fc6 100644 --- a/lib/ocaml/runtime.sx +++ b/lib/ocaml/runtime.sx @@ -524,12 +524,18 @@ else if pos + 1 < n && _string_get fmt pos = \"%\" then let spec = _string_get fmt (pos + 1) in if spec = \"%\" then walk (pos + 2) (prefix ^ \"%\") - else if spec = \"d\" || spec = \"s\" || spec = \"f\" - || spec = \"c\" || spec = \"b\" then + else if spec = \"d\" || spec = \"i\" || spec = \"s\" + || spec = \"f\" || spec = \"c\" || spec = \"b\" + || spec = \"x\" || spec = \"X\" || spec = \"o\" + || spec = \"u\" then (fun arg -> let s = - if spec = \"d\" then _string_of_int arg + if spec = \"d\" || spec = \"i\" || spec = \"u\" + then _string_of_int arg else if spec = \"f\" then _string_of_float arg + else if spec = \"x\" then _int_to_hex_lower arg + else if spec = \"X\" then _int_to_hex_upper arg + else if spec = \"o\" then _int_to_octal arg else if spec = \"b\" then (if arg then \"true\" else \"false\") else arg diff --git a/lib/ocaml/test.sh b/lib/ocaml/test.sh index ecd7e10e..6501e0bd 100755 --- a/lib/ocaml/test.sh +++ b/lib/ocaml/test.sh @@ -1318,6 +1318,18 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 5062) (eval "(ocaml-run \"List.length (List.sort compare [9;8;7;6;5;4;3;2;1;0])\")") +;; ── Printf %i %x %X %o ───────────────────────────────────────── +(epoch 5070) +(eval "(ocaml-run \"Printf.sprintf \\\"%i\\\" 42\")") +(epoch 5071) +(eval "(ocaml-run \"Printf.sprintf \\\"%x\\\" 255\")") +(epoch 5072) +(eval "(ocaml-run \"Printf.sprintf \\\"%X\\\" 4096\")") +(epoch 5073) +(eval "(ocaml-run \"Printf.sprintf \\\"%o\\\" 8\")") +(epoch 5074) +(eval "(ocaml-run \"Printf.sprintf \\\"%x %X %o\\\" 255 4096 8\")") + EPOCHS OUTPUT=$(timeout 360 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -2094,6 +2106,13 @@ check 5060 "sort 9-element list" '(1 2 3 4 5 6 7 8 9)' check 5061 "sort with reverse cmp" '(5 4 3 1 1)' check 5062 "sort 10 reversed -> length" '10' +# ── Printf %i %x %X %o ────────────────────────────────────────── +check 5070 "%i 42" '"42"' +check 5071 "%x 255" '"ff"' +check 5072 "%X 4096" '"1000"' +check 5073 "%o 8" '"10"' +check 5074 "%x %X %o multi" '"ff 1000 10"' + TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL OCaml-on-SX tests passed" diff --git a/plans/ocaml-on-sx.md b/plans/ocaml-on-sx.md index 7811c7d3..07348aca 100644 --- a/plans/ocaml-on-sx.md +++ b/plans/ocaml-on-sx.md @@ -407,6 +407,13 @@ _Newest first._ binary search tree (`type 'a tree = Leaf | Node of 'a * 'a tree * 'a tree`) with insert + in-order traversal. Tests parametric ADT, recursive match, List.append, List.fold_left. +- 2026-05-09 Phase 6 — Printf.sprintf adds %i, %u (aliases of %d), + %x (lowercase hex), %X (uppercase hex), %o (octal) (+5 tests, 533 + total). New host primitives `_int_to_hex_lower`, `_int_to_hex_upper`, + `_int_to_octal` build the digit string by repeated host + `floor (/ n base)` + `mod`. The Printf walker fans out specs to the + right host helper. Examples: `%x` 255 = "ff", `%X` 4096 = "1000", + `%o` 8 = "10", multi: `%x %X %o` 255 4096 8 = "ff 1000 10". - 2026-05-09 Phase 6 — List.sort upgraded from O(n²) insertion sort to O(n log n) mergesort (+3 tests, 528 total). split + merge are inner functions of sort; tuple destructuring on the split result is