Phase 8: REPL, printing, source maps, and nREPL server
- IPrintWithWriter protocol + CljElixir.Printer module with pr-str, print-str, pr, prn for all BEAM types (EDN-like output) - Source-mapped error messages: line/col metadata from reader now propagated through transformer into Elixir AST for accurate error locations in .clje files - Interactive REPL (mix clje.repl) with multi-line input detection, history, bindings persistence, and pr-str formatted output - nREPL server (mix clje.nrepl) with TCP transport, Bencode wire protocol, session management, and core operations (clone, close, eval, describe, ls-sessions, load-file, interrupt, completions). Writes .nrepl-port for editor auto-discovery. 92 new tests (699 total, 0 failures). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
defmodule CljElixir.REPLTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias CljElixir.REPL
|
||||
|
||||
describe "REPL.new" do
|
||||
test "creates initial state" do
|
||||
state = REPL.new()
|
||||
assert state.bindings == []
|
||||
assert state.history == []
|
||||
assert state.counter == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "REPL.eval" do
|
||||
test "evaluates simple expression" do
|
||||
state = REPL.new()
|
||||
assert {:ok, "3", _} = REPL.eval("(+ 1 2)", state)
|
||||
end
|
||||
|
||||
test "pr-str formats output" do
|
||||
state = REPL.new()
|
||||
{:ok, result, _} = REPL.eval("\"hello\"", state)
|
||||
assert result == "\"hello\""
|
||||
end
|
||||
|
||||
test "nil result" do
|
||||
state = REPL.new()
|
||||
{:ok, result, _} = REPL.eval("nil", state)
|
||||
assert result == "nil"
|
||||
end
|
||||
|
||||
test "map result" do
|
||||
state = REPL.new()
|
||||
{:ok, result, _} = REPL.eval("{:a 1 :b 2}", state)
|
||||
assert result =~ ":a"
|
||||
assert result =~ "1"
|
||||
end
|
||||
|
||||
test "increments counter" do
|
||||
state = REPL.new()
|
||||
{:ok, _, state2} = REPL.eval("1", state)
|
||||
assert state2.counter == 2
|
||||
{:ok, _, state3} = REPL.eval("2", state2)
|
||||
assert state3.counter == 3
|
||||
end
|
||||
|
||||
test "stores history" do
|
||||
state = REPL.new()
|
||||
{:ok, _, state2} = REPL.eval("(+ 1 2)", state)
|
||||
assert state2.history == ["(+ 1 2)"]
|
||||
{:ok, _, state3} = REPL.eval("(+ 3 4)", state2)
|
||||
assert state3.history == ["(+ 3 4)", "(+ 1 2)"]
|
||||
end
|
||||
|
||||
test "error returns error tuple" do
|
||||
state = REPL.new()
|
||||
{:error, msg, _} = REPL.eval("(defmodule REPLErrTest (invalid-syntax", state)
|
||||
assert is_binary(msg)
|
||||
assert msg =~ "Error" or msg =~ "error"
|
||||
end
|
||||
|
||||
test "defmodule persists across evals" do
|
||||
state = REPL.new()
|
||||
|
||||
{:ok, _, state2} =
|
||||
REPL.eval("""
|
||||
(defmodule REPLTestMod
|
||||
(defn hello [] :hi))
|
||||
""", state)
|
||||
|
||||
{:ok, result, _} = REPL.eval("(REPLTestMod/hello)", state2)
|
||||
assert result == ":hi"
|
||||
end
|
||||
|
||||
test "tuple result" do
|
||||
state = REPL.new()
|
||||
{:ok, result, _} = REPL.eval("#el[:ok 42]", state)
|
||||
assert result == "#el[:ok 42]"
|
||||
end
|
||||
|
||||
test "list result" do
|
||||
state = REPL.new()
|
||||
{:ok, result, _} = REPL.eval("(list 1 2 3)", state)
|
||||
assert result == "(1 2 3)"
|
||||
end
|
||||
end
|
||||
|
||||
describe "REPL.balanced?" do
|
||||
test "balanced parens" do
|
||||
assert REPL.balanced?("(+ 1 2)")
|
||||
assert REPL.balanced?("(defn foo [x] (+ x 1))")
|
||||
assert REPL.balanced?("42")
|
||||
assert REPL.balanced?("")
|
||||
end
|
||||
|
||||
test "unbalanced parens" do
|
||||
refute REPL.balanced?("(+ 1 2")
|
||||
refute REPL.balanced?("(defn foo [x]")
|
||||
refute REPL.balanced?("(let [x 1")
|
||||
end
|
||||
|
||||
test "balanced with nested" do
|
||||
assert REPL.balanced?("(let [x (+ 1 2)] (* x x))")
|
||||
end
|
||||
|
||||
test "string contents not counted" do
|
||||
assert REPL.balanced?("(str \"(hello)\")")
|
||||
assert REPL.balanced?("(str \"[not real]\")")
|
||||
end
|
||||
|
||||
test "comment contents not counted" do
|
||||
assert REPL.balanced?("(+ 1 2) ; this has unbalanced (")
|
||||
end
|
||||
|
||||
test "mixed delimiters" do
|
||||
assert REPL.balanced?("(let [{:keys [a b]} {:a 1 :b 2}] (+ a b))")
|
||||
refute REPL.balanced?("(let [{:keys [a b]} {:a 1 :b 2}] (+ a b)")
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user