Documentation
¶
Overview ¶
Package repl is the REPL driver of design/03-analyzer-eval.md §7b: a loop of Read (pkg/reader) → Analyze+Eval (pkg/eval) → bind *1 *2 *3 (and *e on error) → print via pr-str, over an injected reader/writer pair. The terminal frontend (cmd/cljgo) and the future nREPL server are both thin frontends of this one driver.
REPL ergonomics (ADR 0018): graceful exit words, help, and did-you-mean suggestions. All affordances are fallback-only — a user-defined var always wins (the precedence principle) — and the exit/help words additionally gate on interactive use (d.Prompts), so piped scripts see exactly the historical semantics.
Session is the shared eval-session helper of ADR 0031: the binding frame every interactive session pushes (*ns* plus the *1 *2 *3 *e result/error vars) and the per-eval bookkeeping on it. The terminal Driver and pkg/nrepl are both thin frontends of this one helper — the smallest export that keeps their session semantics identical (spike S15 found Driver itself is not reusable: Run is wired to line-oriented stdin and EvalString deliberately skips *1).
Session journals (ADR 0016): every successful top-level form is appended — with its namespace context and a timestamp — to ~/.config/cljgo/sessions/<id>.journal, plain readable Clojure. Failed forms are journaled as comments (visible history, never replayed). :resume <id> replays a journal through the normal read→analyze→eval path and continues journaling to that id. Journaling is off when stdin is not a tty unless CLJGO_SESSION=1, so scripts, pipes and tests stay clean.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Driver ¶
type Driver struct {
// Prompts controls whether Run writes a prompt to Out before each
// line of input (on for a terminal, off for piped input).
Prompts bool
// Interactive gates the ADR 0018 exit/quit/help affordances: they
// only fire at an interactive prompt, so piped scripts keep the
// historical unresolved-symbol semantics. Frontends set it for a tty;
// tests inject it directly (no real tty needed).
Interactive bool
// contains filtered or unexported fields
}
Driver owns one evaluator session. *1 *2 *3 *e are proper dynamic vars in clojure.core (design/03 §7b): Run pushes a session frame binding them (plus *ns*) and set!s them after each eval; they revert to their nil roots when the session ends, as on JVM Clojure.
func New ¶
New returns a driver with a fresh evaluator. in may be nil when only EvalReader/EvalString will be used (e.g. `cljgo run`).
func (*Driver) EvalReader ¶
EvalReader reads and evaluates every form from r (e.g. a .clj file), returning the value of the last form. No REPL affordances: results are not printed and *1/*e are not bound; errors return immediately with position. *ns* and *file* are bound for the load, as Clojure's load does (design/03 §7a) — an in-ns inside the file is undone when the load finishes. This is the `cljgo run` and conformance path.
func (*Driver) EvalString ¶
EvalString is EvalReader over a string (tests, future nREPL eval op).
func (*Driver) Interrupt ¶
func (d *Driver) Interrupt()
Interrupt aborts the input continuation in progress: the pending unfinished form is discarded and a fresh primary prompt is drawn. The session itself keeps running — at an empty prompt an interrupt is just a newline + new prompt. Safe to call from any goroutine (Run's SIGINT listener, a future nREPL interrupt op).
func (*Driver) Run ¶
Run is the interactive loop. Input is accumulated line by line; when the buffer ends mid-form (reader.ErrIncomplete) more input is read before the unfinished form is evaluated, so a form may span lines and one line may hold many forms. Each form is evaluated and printed AS IT COMPLETES: a syntax error later in the buffer never discards the result (or error) of a form that already closed. Reader syntax errors and eval errors are printed (with position when available) and the loop continues; only input exhaustion or an I/O error ends it.
Interrupts: Run listens for SIGINT for its whole lifetime. Ctrl-C discards the pending unfinished input (the " #_=> " continuation) and redraws a fresh prompt; it NEVER exits the session — like JVM Clojure's REPL under rlwrap (`clj`). The session ends on Ctrl-D (EOF at the prompt), exactly as clojure.main does.
type Session ¶ added in v0.2.0
type Session struct {
// contains filtered or unexported fields
}
Session holds the vars of one eval session's binding frame. It is NOT goroutine-safe: dynamic bindings are goroutine-keyed in pkg/lang, so a session lives on exactly one goroutine — push Bindings() there and call RecordResult/RecordError only from it.
func NewSession ¶ added in v0.2.0
NewSession returns a session over ev. The *1 *2 *3 *e vars are the interned clojure.core ones; their nil roots are untouched — per-session values live only in the pushed frame.
func (*Session) Bindings ¶ added in v0.2.0
func (s *Session) Bindings() lang.IPersistentMap
Bindings is the session frame (design/03 §7b): *ns* seeded from the current namespace, *1 *2 *3 *e starting nil. Push it with lang.PushThreadBindings on the session's goroutine (and pop on exit); callers may Assoc extra vars (pkg/nrepl adds *out*) before pushing.
func (*Session) RecordError ¶ added in v0.2.0
RecordError binds *e to the eval error. Only valid under the session frame.
func (*Session) RecordResult ¶ added in v0.2.0
RecordResult shifts the result history after a successful eval: *3 ← *2 ← *1 ← res. Only valid under the session frame.