The piece between an editor and everything else. One long-lived Session, the program it belongs to launched and owned by the same process, and a socket that takes forms and installs them. What it adds over flan reload is that the session persists - a defvar added by one evaluation is part of what the next is checked against - and that it owns the build, which is what makes its layout rules describe the process actually running rather than a guess about it. The protocol is s-expressions rather than bencode, and I changed my mind about that. The case for nREPL was reusing a designed op set and not re-litigating session identity, but with the client ours too there is no CIDER to be compatible with, its eval is string-in/string-out with no slot for which form from which file, and Emacs already has read and prin1. So: one sexp per message, length framed because the payload contains newlines. No parsing code on the editor side, and on this side the parser is the language's own reader, where :op is already a keyword and Flan source is already a string literal. An nREPL front end can sit on the same Session later; it should not gate the editor. Two silent failures the daemon refuses to have. The agent socket is chosen by the daemon and forced through FLAN_AGENT_SOCKET before spawning, because a program's source has to name some path and a daemon that guessed would compile, build and deliver a module to nobody. And delivery is checked: agent/start returning 0 means a socket was bound, not that anyone connected, so a failed connect or a reply that is not ok becomes an error the editor sees. It waits for the program to bind before accepting an evaluation, since one arriving first fails for a reason that reads like a compiler bug, and it accepts with a timeout so a program that has exited takes the daemon with it instead of leaving an editor waiting on a socket nobody serves.
20 lines
561 B
Plaintext
20 lines
561 B
Plaintext
;;;; What [flan dev] launches: a program with a loop, a function to redefine,
|
|
;;;; and a way to stop. The daemon overrides the socket path through the
|
|
;;;; environment, so the one written here is only what it falls back to.
|
|
(import agent "vendor:agent")
|
|
|
|
(defvar ticks i64)
|
|
|
|
(defn step [] i64
|
|
(set ticks (+ ticks 1))
|
|
ticks)
|
|
|
|
(defn main [] i32
|
|
(agent/start "/tmp/flan-dev-fallback.sock")
|
|
(print-i64 (step)) (newline)
|
|
(while (= (agent/wait 100) 0) 0)
|
|
(print-i64 (step)) (newline)
|
|
(while (= (agent/wait 100) 0) 0)
|
|
(print-i64 (step)) (newline)
|
|
0)
|