;; Ping-pong program — two processes exchange N messages, then signal ;; main via separate `ping_done` / `pong_done` notifications. (define er-pp-test-count 0) (define er-pp-test-pass 0) (define er-pp-test-fails (list)) (define er-pp-test (fn (name actual expected) (set! er-pp-test-count (+ er-pp-test-count 1)) (if (= actual expected) (set! er-pp-test-pass (+ er-pp-test-pass 1)) (append! er-pp-test-fails {:actual actual :expected expected :name name})))) (define pp-ev erlang-eval-ast) ;; Three rounds of ping-pong, then stop. Main receives ping_done and ;; pong_done in arrival order (Ping finishes first because Pong exits ;; only after receiving stop). (define er-pp-program "Me = self(), Pong = spawn(fun () -> Loop = fun () -> receive {ping, From} -> From ! pong, Loop(); stop -> Me ! pong_done end end, Loop() end), Ping = fun (Target, K) -> if K =:= 0 -> Target ! stop, Me ! ping_done; true -> Target ! {ping, self()}, receive pong -> Ping(Target, K - 1) end end end, spawn(fun () -> Ping(Pong, 3) end), receive ping_done -> ok end, receive pong_done -> both_done end") (er-pp-test "ping-pong 3 rounds" (get (pp-ev er-pp-program) :name) "both_done") ;; Count exchanges via io-buffer — each pong trip prints "p". (er-pp-test "ping-pong 5 rounds trace" (do (er-io-flush!) (pp-ev "Me = self(), Pong = spawn(fun () -> Loop = fun () -> receive {ping, From} -> io:format(\"p\"), From ! pong, Loop(); stop -> Me ! pong_done end end, Loop() end), Ping = fun (Target, K) -> if K =:= 0 -> Target ! stop, Me ! ping_done; true -> Target ! {ping, self()}, receive pong -> Ping(Target, K - 1) end end end, spawn(fun () -> Ping(Pong, 5) end), receive ping_done -> ok end, receive pong_done -> ok end") (er-io-buffer-content)) "ppppp") ;; Main → Pong directly (no Ping process). Main plays the ping role. (er-pp-test "main-as-pinger 4 rounds" (pp-ev "Me = self(), Pong = spawn(fun () -> Loop = fun () -> receive {ping, From} -> From ! pong, Loop(); stop -> ok end end, Loop() end), Go = fun (K) -> if K =:= 0 -> Pong ! stop, K; true -> Pong ! {ping, Me}, receive pong -> Go(K - 1) end end end, Go(4)") 0) ;; Ensure the processes really interleave — inject an id into each ;; ping and check we get them all back via trace (the order is ;; deterministic under our sync scheduler). (er-pp-test "ids round-trip" (do (er-io-flush!) (pp-ev "Me = self(), Pong = spawn(fun () -> Loop = fun () -> receive {ping, From, Id} -> From ! {pong, Id}, Loop(); stop -> ok end end, Loop() end), Go = fun (K) -> if K =:= 0 -> Pong ! stop, done; true -> Pong ! {ping, Me, K}, receive {pong, RId} -> io:format(\"~p \", [RId]), Go(K - 1) end end end, Go(4)") (er-io-buffer-content)) "4 3 2 1 ") (define er-pp-test-summary (str "ping-pong " er-pp-test-pass "/" er-pp-test-count))