spec: string-buffer primitive — make-string-buffer/append!/->string/length

OCaml: StringBuffer of Buffer.t in sx_types.ml; 5 primitives in
sx_primitives.ml (make-string-buffer, string-buffer?, string-buffer-append!,
string-buffer->string, string-buffer-length); inspect case added.

JS: SxStringBuffer with array+join backend; _string_buffer marker for
typeOf dispatch and dict? exclusion (also excludes _vector from dict?).

spec/primitives.sx: 5 define-primitive entries.
17/17 tests pass on both OCaml and JS.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 17:05:05 +00:00
parent cc0af51921
commit d98b5fa223
7 changed files with 190 additions and 4 deletions

View File

@@ -1368,6 +1368,23 @@ let () =
if len <= 0 then Vector [||] else Vector (Array.sub arr start len)
| _ -> raise (Eval_error "vector-copy: expected (vector) or (vector start) or (vector start end)"));
(* String buffers — O(1) amortised append for string building in loops *)
register "make-string-buffer" (fun _ -> StringBuffer (Buffer.create 64));
register "string-buffer?" (fun args ->
match args with [StringBuffer _] -> Bool true | [_] -> Bool false
| _ -> raise (Eval_error "string-buffer?: expected 1 arg"));
register "string-buffer-append!" (fun args ->
match args with
| [StringBuffer buf; String s] -> Buffer.add_string buf s; Nil
| [StringBuffer _; v] -> raise (Eval_error ("string-buffer-append!: expected string, got " ^ type_of v))
| _ -> raise (Eval_error "string-buffer-append!: expected (buffer string)"));
register "string-buffer->string" (fun args ->
match args with [StringBuffer buf] -> String (Buffer.contents buf)
| _ -> raise (Eval_error "string-buffer->string: expected (buffer)"));
register "string-buffer-length" (fun args ->
match args with [StringBuffer buf] -> Integer (Buffer.length buf)
| _ -> raise (Eval_error "string-buffer-length: expected (buffer)"));
(* Capability-based sandboxing — gate IO operations *)
let cap_stack : string list ref = ref [] in
register "with-capabilities" (fun args ->

View File

@@ -73,6 +73,7 @@ and value =
| Record of record (** R7RS record — opaque, generative, field-indexed. *)
| Parameter of parameter (** R7RS parameter — dynamic binding via kont-stack provide frames. *)
| Vector of value array (** R7RS vector — mutable fixed-size array. *)
| StringBuffer of Buffer.t (** Mutable string buffer — O(1) amortized append. *)
(** CEK machine state — record instead of Dict for performance.
5 fields × 55K steps/sec = 275K Hashtbl allocations/sec eliminated. *)
@@ -491,6 +492,7 @@ let type_of = function
| Record r -> r.r_type.rt_name
| Parameter _ -> "parameter"
| Vector _ -> "vector"
| StringBuffer _ -> "string-buffer"
let is_nil = function Nil -> true | _ -> false
let is_lambda = function Lambda _ -> true | _ -> false
@@ -836,3 +838,4 @@ let rec inspect = function
Printf.sprintf "#(%s)" (String.concat " " elts)
| VmFrame f -> Printf.sprintf "<vm-frame:ip=%d base=%d>" f.vf_ip f.vf_base
| VmMachine m -> Printf.sprintf "<vm-machine:sp=%d frames=%d>" m.vm_sp (List.length m.vm_frames)
| StringBuffer buf -> Printf.sprintf "<string-buffer:%d>" (Buffer.length buf)