diff --git a/lib/ocaml/parser.sx b/lib/ocaml/parser.sx index cf71cad7..0ff699d7 100644 --- a/lib/ocaml/parser.sx +++ b/lib/ocaml/parser.sx @@ -167,7 +167,7 @@ ((= tt "ctor") true) ((and (= tt "keyword") (or (= tv "true") (= tv "false"))) true) - ((and (= tt "op") (or (= tv "(") (= tv "[") (= tv "{"))) true) + ((and (= tt "op") (or (= tv "(") (= tv "[") (= tv "{") (= tv "!"))) true) (else false))))) (set! parse-pattern-atom @@ -532,7 +532,7 @@ ((= tt "ctor") true) ((and (= tt "keyword") (or (= tv "true") (= tv "false") (= tv "begin"))) true) - ((and (= tt "op") (or (= tv "(") (= tv "[") (= tv "{"))) true) + ((and (= tt "op") (or (= tv "(") (= tv "[") (= tv "{") (= tv "!"))) true) (else false))))) (define parse-atom-postfix (fn () @@ -568,7 +568,12 @@ (when (at-app-start?) (let - ((arg (parse-atom-postfix))) + ((arg + (cond + ((at-op? "!") + (begin (advance-tok!) + (list :deref (parse-atom-postfix)))) + (else (parse-atom-postfix))))) (begin (set! head (list :app head arg)) (loop)))))) (loop) head)))) diff --git a/lib/ocaml/runtime.sx b/lib/ocaml/runtime.sx index 97d1835a..142eb3f2 100644 --- a/lib/ocaml/runtime.sx +++ b/lib/ocaml/runtime.sx @@ -366,6 +366,16 @@ let printf fmt = print_string fmt end ;; + module Buffer = struct + let create _ = ref [] + let add_string b s = b := s :: !b + let add_char b c = b := c :: !b + let contents b = String.concat \"\" (List.rev !b) + let length b = String.length (String.concat \"\" (List.rev !b)) + let clear b = b := [] + let reset = clear + end ;; + module Sys = struct let os_type = \"SX\" let word_size = 64 diff --git a/lib/ocaml/test.sh b/lib/ocaml/test.sh index 0a0e75ba..23206812 100755 --- a/lib/ocaml/test.sh +++ b/lib/ocaml/test.sh @@ -1044,6 +1044,14 @@ cat > "$TMPFILE" << 'EPOCHS' (epoch 3403) (eval "(ocaml-run-program \"module IntOrd = struct let compare a b = compare a b end ;; module IntSet = Set.Make(IntOrd) ;; let a = IntSet.add 1 (IntSet.add 2 (IntSet.add 3 IntSet.empty)) ;; let b = IntSet.add 2 (IntSet.add 3 (IntSet.add 4 IntSet.empty)) ;; IntSet.elements (IntSet.inter a b)\")") +;; ── Buffer module ────────────────────────────────────────────── +(epoch 3500) +(eval "(ocaml-run-program \"let b = Buffer.create 16 ;; Buffer.add_string b \\\"Hello\\\" ;; Buffer.add_string b \\\", \\\" ;; Buffer.add_string b \\\"World\\\" ;; Buffer.contents b\")") +(epoch 3501) +(eval "(ocaml-run-program \"let b = Buffer.create 16 ;; Buffer.add_string b \\\"abc\\\" ;; Buffer.length b\")") +(epoch 3502) +(eval "(ocaml-run-program \"let b = Buffer.create 16 ;; Buffer.add_string b \\\"x\\\" ;; Buffer.clear b ;; Buffer.contents b\")") + EPOCHS OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) @@ -1652,6 +1660,11 @@ check 3401 "Map.is_empty false" 'false' check 3402 "Set.union" '(1 2 3)' check 3403 "Set.inter" '(2 3)' +# ── Buffer module ────────────────────────────────────────────── +check 3500 "Buffer concat 'Hello, World'" '"Hello, World"' +check 3501 "Buffer.length 3" '3' +check 3502 "Buffer.clear empties" '""' + 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 b44bcece..c494ee9c 100644 --- a/plans/ocaml-on-sx.md +++ b/plans/ocaml-on-sx.md @@ -264,6 +264,9 @@ SX CEK evaluator (both JS and OCaml hosts) - [~] `Hashtbl`: `create`, `add`, `find`, `find_opt`, `replace`, `mem`, `length`. Backed by a one-element list cell holding a SX dict; keys coerced to strings via `str` for polymorphic-key support. +- [~] `Buffer`: `create`, `add_string`, `add_char`, `contents`, `length`, + `clear`, `reset`. Backed by a ref holding a list of strings; reverse + + `String.concat` on `contents`. Mostly-OCaml impl. - [~] `Sys`: `os_type` (`"SX"`), `word_size`, `max_array_length`, `max_string_length`, `executable_name`, `big_endian`, `unix`, `win32`, `cygwin`. Constants only; `argv`/`getenv_opt`/`command` @@ -386,6 +389,11 @@ the "mother tongue" closure: OCaml → SX → OCaml. This means: _Newest first._ +- 2026-05-08 Phase 1+6 — Buffer module + parser fix for `f !x` (+3 + tests, 425 total). Parser: at-app-start? and parse-app's loop now + recognise `!` as the prefix-deref of an application argument, so + `String.concat "" (List.rev !b)` parses as `(... (deref b))`. Buffer + uses a ref holding a string list; contents reverses and concats. - 2026-05-08 Phase 5.1 — word_count.ml baseline (10/10 pass). Uses Map.Make(StrOrd) + List.fold_left to count word frequencies; tests the full functor pipeline with a real OCaml idiom.