2013/01/29
New REPL
Upcoming 0.9.4 will have a slightly improved REPL---eval result history
is available via variables *1
etc, and it also shows current module in the prompt if you switch the module from the default user
.
(See Better REPL? for the history feature. Other features listed there haven't been ported.)
I admit it's not much. The point is that now we have a scaffold that can be easily extended later to try out new ideas. The new REPL is written in Scheme (in gauche.interactive
module) and loaded automatically when gosh
starts in the interactive mode.
Here I'd like to write about the implementation rather than the features.
Writing REPL in Scheme is trivial. There's a catch, though.
We use gosh
as a script interpreter as well as an interactive shell. If we use it as a script interpreter, we don't need REPL and we don't want to carry the extra baggage. (Currently the extended REPL is pretty simple, but it'll get bigger as we throw in more features).
This leads to an idea that we implement REPL in a separate module that can only be loaded when gosh
is in interactive mode. We already have such module: gauche.interactive
.
However, during development of Gauche itself, it is often a case that gosh
fails to load gauche.interactive
due to a bug. Writing REPL is like fixing a roof while you're on it; you can be stuck or fall if you make mistakes. Even in such a state we need some sort of basic REPL to investigate and fix the problem.
And we don't want to duplicate similar loop code. The difference between the basic REPL and the extended one is in evaluator, printer, etc., but the core loop construct should be shared.
So here's what I did.
- The core
libgauche
implements a built-in REPL, with the defaultreader
,evaluator
,printer
andprompter
as the minimal default. This is what we have inlibeval.scm
now, which is compatible to the one we had until 0.9.3, except it was written in C.(define-in-module gauche (read-eval-print-loop :optional (reader #f) (evaluator #f) (printer #f) (prompter #f)) (let ([reader (or reader read)] [evaluator (or evaluator eval)] [printer (or printer %repl-print)] [prompter (or prompter %repl-prompt)]) (let loop1 () (and (with-error-handler (^e (report-error e) #t) (^[] (let loop2 () (prompter) (let1 exp (reader) (and (not (eof-object? exp)) (receive results (evaluator exp (vm-current-module)) (apply printer results) (loop2))))))) (loop1)))))
- The
gauche.interactive
module implements extended evaluator (%evaluator
) and prompter (%prompter
). In future, we'll also extend reader and printer. Then it defines its ownread-eval-print-loop
, wrapping the built-in REPL with the extended handlers:(define (read-eval-print-loop :optional (reader #f) (evaluator #f) (printer #f) (prompter #f)) (let ([evaluator (or evaluator %evaluator)] [prompter (or prompter %prompter)]) ((with-module gauche read-eval-print-loop) reader evaluator printer prompter)))
- The
main.c
ofgosh
importsgauche.interactive
into theuser
module when it is invoked in interactive mode, then it evaluates(read-eval-print-loop)
. Ifgauche.interactive
is loaded successfully, the extended version is called because it shadows the built-in version. Ifgauche.interactive
can't be loaded, however, the built-in version is called.
Tags: 0.9.4, repl, gauche.interactive
Post a comment