ocaml: phase 2 references ref/!/:= (+6 tests, 189 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 55s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 55s
ref is a builtin boxing its arg in a one-element list. Prefix ! parses to (:deref ...) and reads via (nth cell 0). := joins the binop precedence table at level 1 right-assoc and mutates via set-nth!. Closures share the underlying cell.
This commit is contained in:
@@ -38,7 +38,10 @@
|
||||
(list "min" (fn (a) (fn (b) (if (< a b) a b))))
|
||||
(list "fst" (fn (p) (nth p 1)))
|
||||
(list "snd" (fn (p) (nth p 2)))
|
||||
(list "ignore" (fn (x) nil)))))
|
||||
(list "ignore" (fn (x) nil))
|
||||
;; References. A ref cell is a one-element list; ! reads it and
|
||||
;; := mutates it via set-nth!.
|
||||
(list "ref" (fn (x) (list x))))))
|
||||
|
||||
(define ocaml-env-lookup
|
||||
(fn (env name)
|
||||
@@ -235,11 +238,22 @@
|
||||
(else (error (str "ocaml-eval: unbound variable " name))))))
|
||||
((= tag "neg") (- 0 (ocaml-eval (nth ast 1) env)))
|
||||
((= tag "not") (not (ocaml-eval (nth ast 1) env)))
|
||||
((= tag "deref")
|
||||
(let ((cell (ocaml-eval (nth ast 1) env)))
|
||||
(nth cell 0)))
|
||||
((= tag "op")
|
||||
(ocaml-eval-op
|
||||
(nth ast 1)
|
||||
(ocaml-eval (nth ast 2) env)
|
||||
(ocaml-eval (nth ast 3) env)))
|
||||
(let ((op (nth ast 1)))
|
||||
(cond
|
||||
;; := mutates the lhs cell — short-circuit before generic
|
||||
;; eval-op so we still evaluate lhs (to obtain the cell).
|
||||
((= op ":=")
|
||||
(let ((cell (ocaml-eval (nth ast 2) env))
|
||||
(new-val (ocaml-eval (nth ast 3) env)))
|
||||
(begin (set-nth! cell 0 new-val) nil)))
|
||||
(else
|
||||
(ocaml-eval-op op
|
||||
(ocaml-eval (nth ast 2) env)
|
||||
(ocaml-eval (nth ast 3) env))))))
|
||||
((= tag "if")
|
||||
(if (ocaml-eval (nth ast 1) env)
|
||||
(ocaml-eval (nth ast 2) env)
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
(define
|
||||
ocaml-op-table
|
||||
(list
|
||||
(list ":=" 1 :right)
|
||||
(list "||" 2 :right)
|
||||
(list "or" 2 :right)
|
||||
(list "&&" 3 :right)
|
||||
@@ -401,6 +402,8 @@
|
||||
(cond
|
||||
((at-op? "-")
|
||||
(begin (advance-tok!) (list :neg (parse-prefix))))
|
||||
((at-op? "!")
|
||||
(begin (advance-tok!) (list :deref (parse-prefix))))
|
||||
((at-kw? "not")
|
||||
(begin (advance-tok!) (list :not (parse-prefix))))
|
||||
(else (parse-app)))))
|
||||
|
||||
@@ -481,6 +481,20 @@ cat > "$TMPFILE" << 'EPOCHS'
|
||||
(epoch 560)
|
||||
(eval "(ocaml-run \"match Pair (1, 2) with | Pair (a, b) -> a * b\")")
|
||||
|
||||
;; ── References (ref / ! / :=) ──────────────────────────────────
|
||||
(epoch 600)
|
||||
(eval "(ocaml-run \"let r = ref 5 in !r\")")
|
||||
(epoch 601)
|
||||
(eval "(ocaml-run \"let r = ref 5 in r := 10; !r\")")
|
||||
(epoch 602)
|
||||
(eval "(ocaml-run \"let r = ref 0 in r := !r + 1; r := !r + 1; !r\")")
|
||||
(epoch 603)
|
||||
(eval "(ocaml-run \"let r = ref 100 in let f x = r := !r + x in f 5; f 10; !r\")")
|
||||
(epoch 604)
|
||||
(eval "(ocaml-run \"let r = ref \\\"a\\\" in r := \\\"b\\\"; !r\")")
|
||||
(epoch 605)
|
||||
(eval "(ocaml-run \"let count = ref 0 in let rec loop n = if n = 0 then !count else (count := !count + n; loop (n - 1)) in loop 5\")")
|
||||
|
||||
EPOCHS
|
||||
|
||||
OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
||||
@@ -767,6 +781,14 @@ check 551 "match x -> x+1" '100'
|
||||
# ctor with tuple arg
|
||||
check 560 "Pair(a,b) → a*b" '2'
|
||||
|
||||
# ── References ──────────────────────────────────────────────────
|
||||
check 600 "deref new ref" '5'
|
||||
check 601 ":= then deref" '10'
|
||||
check 602 "increment cell twice" '2'
|
||||
check 603 "ref captured by closure" '115'
|
||||
check 604 "ref of string" '"b"'
|
||||
check 605 "ref + recursion" '15'
|
||||
|
||||
TOTAL=$((PASS + FAIL))
|
||||
if [ $FAIL -eq 0 ]; then
|
||||
echo "ok $PASS/$TOTAL OCaml-on-SX tests passed"
|
||||
|
||||
Reference in New Issue
Block a user