#!/usr/bin/env bash # Fast OCaml-on-SX test runner — epoch protocol direct to sx_server.exe. # Mirrors lib/lua/test.sh. # # Usage: # bash lib/ocaml/test.sh # run all tests # bash lib/ocaml/test.sh -v # verbose set -uo pipefail cd "$(git rev-parse --show-toplevel)" SX_SERVER="${SX_SERVER:-hosts/ocaml/_build/default/bin/sx_server.exe}" if [ ! -x "$SX_SERVER" ]; then SX_SERVER="/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe" fi if [ ! -x "$SX_SERVER" ]; then echo "ERROR: sx_server.exe not found. Run: cd hosts/ocaml && dune build" exit 1 fi VERBOSE="${1:-}" PASS=0 FAIL=0 ERRORS="" TMPFILE=$(mktemp) trap "rm -f $TMPFILE" EXIT cat > "$TMPFILE" << 'EPOCHS' (epoch 1) (load "lib/guest/lex.sx") (load "lib/guest/prefix.sx") (load "lib/ocaml/tokenizer.sx") (load "lib/ocaml/tests/tokenize.sx") ;; ── empty / eof ──────────────────────────────────────────────── (epoch 100) (eval "(ocaml-test-tok-count \"\")") (epoch 101) (eval "(ocaml-test-tok-type \"\" 0)") ;; ── numbers ──────────────────────────────────────────────────── (epoch 110) (eval "(ocaml-test-tok-type \"42\" 0)") (epoch 111) (eval "(ocaml-test-tok-value \"42\" 0)") (epoch 112) (eval "(ocaml-test-tok-value \"3.14\" 0)") (epoch 113) (eval "(ocaml-test-tok-value \"0xff\" 0)") (epoch 114) (eval "(ocaml-test-tok-value \"1e3\" 0)") (epoch 115) (eval "(ocaml-test-tok-value \"1_000_000\" 0)") (epoch 116) (eval "(ocaml-test-tok-value \"3.14e-2\" 0)") ;; ── identifiers / constructors / keywords ───────────────────── (epoch 120) (eval "(ocaml-test-tok-type \"foo\" 0)") (epoch 121) (eval "(ocaml-test-tok-value \"foo_bar1\" 0)") (epoch 122) (eval "(ocaml-test-tok-type \"Some\" 0)") (epoch 123) (eval "(ocaml-test-tok-value \"Some\" 0)") (epoch 124) (eval "(ocaml-test-tok-type \"let\" 0)") (epoch 125) (eval "(ocaml-test-tok-value \"match\" 0)") (epoch 126) (eval "(ocaml-test-tok-type \"true\" 0)") (epoch 127) (eval "(ocaml-test-tok-value \"false\" 0)") (epoch 128) (eval "(ocaml-test-tok-value \"name'\" 0)") ;; ── strings ──────────────────────────────────────────────────── (epoch 130) (eval "(ocaml-test-tok-type \"\\\"hi\\\"\" 0)") (epoch 131) (eval "(ocaml-test-tok-value \"\\\"hi\\\"\" 0)") (epoch 132) (eval "(ocaml-test-tok-value \"\\\"a\\\\nb\\\"\" 0)") ;; ── chars ────────────────────────────────────────────────────── (epoch 140) (eval "(ocaml-test-tok-type \"'a'\" 0)") (epoch 141) (eval "(ocaml-test-tok-value \"'a'\" 0)") (epoch 142) (eval "(ocaml-test-tok-value \"'\\\\n'\" 0)") ;; ── type variables ───────────────────────────────────────────── (epoch 145) (eval "(ocaml-test-tok-type \"'a\" 0)") (epoch 146) (eval "(ocaml-test-tok-value \"'a\" 0)") ;; ── multi-char operators ─────────────────────────────────────── (epoch 150) (eval "(ocaml-test-tok-value \"->\" 0)") (epoch 151) (eval "(ocaml-test-tok-value \"|>\" 0)") (epoch 152) (eval "(ocaml-test-tok-value \"<-\" 0)") (epoch 153) (eval "(ocaml-test-tok-value \":=\" 0)") (epoch 154) (eval "(ocaml-test-tok-value \"::\" 0)") (epoch 155) (eval "(ocaml-test-tok-value \";;\" 0)") (epoch 156) (eval "(ocaml-test-tok-value \"@@\" 0)") (epoch 157) (eval "(ocaml-test-tok-value \"<>\" 0)") (epoch 158) (eval "(ocaml-test-tok-value \"&&\" 0)") (epoch 159) (eval "(ocaml-test-tok-value \"||\" 0)") ;; ── single-char punctuation ──────────────────────────────────── (epoch 160) (eval "(ocaml-test-tok-value \"+\" 0)") (epoch 161) (eval "(ocaml-test-tok-value \"|\" 0)") (epoch 162) (eval "(ocaml-test-tok-value \";\" 0)") (epoch 163) (eval "(ocaml-test-tok-value \"(\" 0)") (epoch 164) (eval "(ocaml-test-tok-value \"!\" 0)") (epoch 165) (eval "(ocaml-test-tok-value \"@\" 0)") ;; ── comments ─────────────────────────────────────────────────── (epoch 170) (eval "(ocaml-test-tok-count \"(* hi *)\")") (epoch 171) (eval "(ocaml-test-tok-value \"(* c *) 42\" 0)") (epoch 172) (eval "(ocaml-test-tok-count \"(* outer (* inner *) end *) 1\")") (epoch 173) (eval "(ocaml-test-tok-value \"(* outer (* inner *) end *) 1\" 0)") ;; ── compound expressions ─────────────────────────────────────── (epoch 180) (eval "(ocaml-test-tok-count \"let x = 1\")") (epoch 181) (eval "(ocaml-test-tok-type \"let x = 1\" 0)") (epoch 182) (eval "(ocaml-test-tok-value \"let x = 1\" 0)") (epoch 183) (eval "(ocaml-test-tok-type \"let x = 1\" 1)") (epoch 184) (eval "(ocaml-test-tok-value \"let x = 1\" 2)") (epoch 185) (eval "(ocaml-test-tok-value \"let x = 1\" 3)") (epoch 190) (eval "(ocaml-test-tok-count \"match x with | None -> 0 | Some y -> y\")") (epoch 191) (eval "(ocaml-test-tok-value \"fun x -> x + 1\" 2)") (epoch 192) (eval "(ocaml-test-tok-type \"fun x -> x + 1\" 2)") (epoch 193) (eval "(ocaml-test-tok-type \"Some 42\" 0)") (epoch 194) (eval "(ocaml-test-tok-value \"a |> f |> g\" 1)") (epoch 195) (eval "(ocaml-test-tok-value \"x := !y\" 1)") EPOCHS OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null) check() { local epoch="$1" desc="$2" expected="$3" local actual actual=$(echo "$OUTPUT" | grep -A1 "^(ok-len $epoch " | tail -1) if [ -z "$actual" ]; then actual=$(echo "$OUTPUT" | grep "^(ok $epoch " || true) fi if [ -z "$actual" ]; then actual=$(echo "$OUTPUT" | grep "^(error $epoch " || true) fi if [ -z "$actual" ]; then actual="" fi if echo "$actual" | grep -qF -- "$expected"; then PASS=$((PASS + 1)) [ "$VERBOSE" = "-v" ] && echo " ok $desc" else FAIL=$((FAIL + 1)) ERRORS+=" FAIL $desc (epoch $epoch) expected: $expected actual: $actual " fi } # empty / eof check 100 "empty tokens length" '1' check 101 "empty first is eof" '"eof"' # numbers check 110 "int type" '"number"' check 111 "int value" '42' check 112 "float value" '3.14' check 113 "hex value" '255' check 114 "exponent" '1000' check 115 "underscored int" '1000000' check 116 "neg exponent" '0.0314' # idents / ctors / keywords check 120 "ident type" '"ident"' check 121 "ident value" '"foo_bar1"' check 122 "ctor type" '"ctor"' check 123 "ctor value" '"Some"' check 124 "let keyword type" '"keyword"' check 125 "match keyword value" '"match"' check 126 "true is keyword" '"keyword"' check 127 "false value" '"false"' check 128 "primed ident" "\"name'\"" # strings check 130 "string type" '"string"' check 131 "string value" '"hi"' check 132 "escape sequence" '"a' # chars check 140 "char type" '"char"' check 141 "char value" '"a"' check 142 "char escape" '"' # tyvars check 145 "tyvar type" '"tyvar"' check 146 "tyvar value" '"a"' # multi-char ops check 150 "->" '"->"' check 151 "|>" '"|>"' check 152 "<-" '"<-"' check 153 ":=" '":="' check 154 "::" '"::"' check 155 ";;" '";;"' check 156 "@@" '"@@"' check 157 "<>" '"<>"' check 158 "&&" '"&&"' check 159 "||" '"||"' # single ops check 160 "+" '"+"' check 161 "|" '"|"' check 162 ";" '";"' check 163 "(" '"("' check 164 "!" '"!"' check 165 "@" '"@"' # comments check 170 "block comment alone -> eof" '1' check 171 "num after block comment" '42' check 172 "nested comment count" '2' check 173 "nested comment value" '1' # compound check 180 "let x = 1 count" '5' check 181 "let is keyword" '"keyword"' check 182 "let value" '"let"' check 183 "x is ident" '"ident"' check 184 "= value" '"="' check 185 "1 value" '1' check 190 "match expr count" '13' check 191 "fun -> arrow value" '"->"' check 192 "fun -> arrow type" '"op"' check 193 "Some is ctor" '"ctor"' check 194 "first |> value" '"|>"' check 195 "ref assign :=" '":="' TOTAL=$((PASS + FAIL)) if [ $FAIL -eq 0 ]; then echo "ok $PASS/$TOTAL OCaml-on-SX tokenizer tests passed" else echo "FAIL $PASS/$TOTAL passed, $FAIL failed:" echo "" echo "$ERRORS" fi [ $FAIL -eq 0 ]