ocaml: phase 6 Printf.sprintf %d/%s/%f/%c/%b/%% + global string_of_* (+5 tests, 492 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 21s

Replaces the stub sprintf in runtime.sx with a real implementation:
walk fmt char-by-char accumulating a prefix; on recognised %X return a
one-arg fn that formats the arg and recurses on the rest of fmt. The
function self-curries to the spec count — there's no separate arity
machinery, just a closure chain.

Specs: %d (int), %s (string), %f (float), %c (char/string in our model),
%b (bool), %% (literal). Unknown specs pass through.

Same expression returns a string (no specs) or a function (>=1 spec) —
OCaml proper would reject this; works fine in OCaml-on-SX's dynamic
runtime.

Also adds top-level aliases:
  string_of_int   = _string_of_int
  string_of_float = _string_of_float
  string_of_bool  = if b then "true" else "false"
  int_of_string   = _int_of_string

  Printf.sprintf "x=%d" 42              = "x=42"
  Printf.sprintf "%s = %d" "answer" 42  = "answer = 42"
  Printf.sprintf "%d%%" 50               = "50%"
This commit is contained in:
2026-05-09 00:42:35 +00:00
parent 14b52cfaa7
commit 1b38f89055
3 changed files with 67 additions and 3 deletions

View File

@@ -424,8 +424,35 @@
end ;;
module Printf = struct
let sprintf fmt = fmt
let printf fmt = print_string fmt
(* sprintf walks fmt, accumulating prefix. When it sees a %X
spec, it returns a function of one arg that substitutes the
arg and recurses on the rest of fmt. With no specs, returns
the bare format string. Specs supported: %d %s %f %c %b
(and %% as a literal). Unknown specs are passed through. *)
let sprintf fmt =
let n = _string_length fmt in
let rec walk pos prefix =
if pos >= n then prefix
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
(fun arg ->
let s =
if spec = \"d\" then _string_of_int arg
else if spec = \"f\" then _string_of_float arg
else if spec = \"b\" then
(if arg then \"true\" else \"false\")
else arg
in
walk (pos + 2) (prefix ^ s))
else walk (pos + 1) (prefix ^ _string_get fmt pos)
else walk (pos + 1) (prefix ^ _string_get fmt pos)
in
walk 0 \"\"
let printf fmt = sprintf fmt
end ;;
module Stack = struct
@@ -643,7 +670,13 @@
| [] -> []
| h :: t -> if mem h b then h :: inter t b else inter t b
end
end")
end ;;
let string_of_int n = _string_of_int n
let string_of_float f = _string_of_float f
let string_of_bool b = if b then \"true\" else \"false\"
let int_of_string s = _int_of_string s
")
(define ocaml-stdlib-loaded false)
(define ocaml-stdlib-env nil)