[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.7 gauche.listener - Listener

Module: gauche.listener
This module provides a convenient way to enable multiple read-eval-print loop (repl) concurrently.

An obvious way to run multiple repls is to use threads; creating as many threads as sessions and calling read-eval-print-loop (See section 6.17 Eval and repl) from each thread. Nevertheless, sometimes single threaded implementation is preferred. For instance, you're using a library which is not MT-safe, or your application already uses select/poll-based dispatching mechanism.

To implement repl in the single-threaded selection-base application, usually you register a handler that is called when data is available in the listening port. The handler reads the data and add them into a buffer. Then it examines if the data in the buffer consists a complete expression, and if so, it reads the expression from the buffer, evaluates it, then prints the result to the reporting port. The <listener> class in this module provides this handler mechanism, so all you need to do is to register the handler to your dispatching mechanism.

Note: it may also be desirable to buffer the output sometimes, but the current version doesn't implement it.

Class: <listener>
An object that maintains the state of a repl session. It has many external slots to customize its behavior. Those slot values can be set at construction time by using the keyword of the same name as the slot, or can be set by slot-set! afterwards. However, most of them should be set before calling listener-read-hander.

Instance Variable: <listener> input-port
Specifies the input port from which the listener get the input. The default value is the current input port when the object is constructed.

Instance Variable: <listener> output-port
Specifies the output port to which the listener output will go. The default value is the current output port when the object is constructed.

Instance Variable: <listener> error-port
Specifies the output port to which the listener's error messages will go. The default value is the current error port when the object is constructed.

Instance Variable: <listener> reader
A procedure with no arguments. It should read a Scheme expression from the current input port when called. The default value is system's read procedure.

Instance Variable: <listener> evaluator
A procedure that takes two arguments, a Scheme expression and an environment specifier. It should evaluate the expression in the given environment and returns zero or more value(s). The default value is system's eval procedure.

Instance Variable: <listener> printer
A procedure that takes zero or more argument(s) and prints them out to the current output port. The default value is a procedure that prints each value by write, followed by a newline.

Instance Variable: <listener> prompter
A procedure with no arguments. It should prints a prompt to the current output port. The output is flushed by the listener object so this procedure doesn't need to care about it. The default procedure prints "listener> ".

Instance Variable: <listener> environment
An environment specifier where the expressions will be evaluated. The default value is the value returned by (interaction-environment).

Instance Variable: <listener> finalizer
A thunk that will be called when EOF is read from input-port. It can be #f if no such procedure is needed. The default value is #f.

Instance Variable: <listener> error-handler
A procedure that takes one argument, an error exception. It is called when an error occurs during read-eval-print stage, with the same dynamic environment as the error is signalled. The default value is a procedure that simply prints the error exception by report-error.

Method: listener-read-handler (listener <listener>)
Returns a thunk that is to be called when a data is available from input-port of the listener.

The returned thunk (read handler) does the following steps. Note that the first prompt is not printed by this procedure. See listener-show-prompt below.

  1. Reads available data from input-port and appends it to the listener's internal buffer.
  2. Scans the buffer to see if it has a complete S-expression. If not, returns.
  3. Reads the S-expression from the buffer. The read data is removed from the buffer.
  4. Evaluates the S-expression, then prints the result to output-port.
  5. Prints the prompt by prompter procedure to output-port, then flush output-port.
  6. Repeats from 2.

Method: listener-show-prompt (listener <listener>)
Shows a prompt to the listener's output port, by using listener's prompter procedure. Usually you want to use this procedure to print the first prompt, for instance, when the client is connected to the listener socket.

Function: complete-sexp? str
Returns #t if str contains a complete S-expression. This utility procedure is exported as well, since it might be useful for other purposes.

Note that this procedure only checks syntax of the expressions, and doesn't rule out erroneous expressions (such as containing invalid character name, unregistered SRFI-10 tag, etc.). This procedure may raise an error if the input contains '#<' character sequence.

The following code snippet opens a server socket, and opens a Scheme interactive session when a client is connected. (Note: this code is just for demonstration. Do not run this program on the machine accessible from outside network!)

(use gauche.net)
(use gauche.selector)
(use gauche.listener)

(define (scheme-server port)
  (let ((selector (make <selector>))
        (server   (make-server-socket 'inet port :reuse-addr? #t))
        (cid      0))

    (define (accept-handler sock flag)
      (let* ((client (socket-accept server))
             (id     cid)
             (input  (socket-input-port client :buffering :none))
             (output (socket-output-port client))
             (finalize (lambda ()
                         (selector-delete! selector input #f #f)
                         (socket-close client)
                         (format #t "client #~a disconnected\n" id)))
             (listener (make <listener>
                         :input-port input
                         :output-port output
                         :error-port output
                         :prompter (lambda () (format #t "client[~a]> " id))
                         :finalizer finalize))
             (handler (listener-read-handler listener))
        (format #t "client #~a from ~a\n" cid (socket-address client))
        (inc! cid)
        (listener-show-prompt listener)
        (selector-add! selector input (lambda _ (handler)) '(r))))

    (selector-add! selector
                   (socket-fd server)
    (format #t "scheme server started on port ~s\n" port)
    (do () (#f) (selector-select selector))))

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated by Ken Dickey on November, 28 2002 using texi2html