Custom REPL in Guile Scheme

Written by Amar Singh

Somewhere in the grey area between writing your own programming language and programming in a given environment.

What is a REPL?

img

A REPL is an acronym for Read Eval and Print Loop it’s often called the ‘interpreter’ or ‘prompt’. Any shell is a REPL. In Scheme, Read Eval and Print are just regular procedures.

img

read :: port -> S-expression: is a procedure, function, or command that can read scheme source code, also called S-expressions.

eval :: exp -> env -> value: is a procedure that can compute any S-expression. It is a universal function.

display :: exp -> port: is a procedure that can print or display any scheme value to some output device.

A custom REPL, where one or more of these behaviours of the REPL are modified or altered.

  • Repl-language
  • Meta-command

What’s the use?

You could have a repl that saves or ‘prints’ everything to a file, or perform any arbitrary task.

A REPL for testing

img

(define shift-left
  (lambda* (lst lst1 ##:optional (res '()))
    (if (or (null? lst) (null? lst1))
        (reverse res)
        (if (>= (length res) (car lst1))
            (if (zero? (car lst1))
                (shift-left (cdr lst) (cdr lst1) (cons (car lst) res))
                (shift-left (cdr lst) (cdr lst1)
                            (append (take res (car lst1))
                                    (cons (car lst) (drop res (car lst1))))))
            (error (format ##f "In procedure shift-left: invalid argument"))))))

(define display-language
  (let ((scheme (lookup-language 'scheme)))
    (make-language ##:name "shift"
                   ##:title "shift" ##:reader (language-reader scheme)
                   ##:compilers (language-compilers scheme)
                   ##:decompilers (language-decompilers scheme)
                   ##:evaluator (lambda (exp env)
                                 (let ((mvalue (compile exp ##:to 'value ##:env  env)))
                                   (shift-left (map 1+ (iota (length mvalue))) mvalue)))
                   ##:printer (language-printer scheme)
                   ##:make-default-environment
                   (language-make-default-environment scheme))))

(define-meta-command ((shift-repl exp) repl)
  "run-in-store EXP
Run EXP through the store monad."
  (let ((new (make-repl display-language)))
    (repl-option-set! new 'interp #t)
    (run-repl new)))

A REPL for rendering

img A Prompt that will ‘render’ the output of an expression into a Browser window.

(define render-language
  (let ((scheme (lookup-language 'scheme)))
    (make-language ##:name "display"
                   ##:title "display" ##:reader (language-reader scheme)
                   ##:compilers (language-compilers scheme)
                   ##:decompilers (language-decompilers scheme)
                   ##:evaluator (lambda (exp env)
                                 (let ((mvalue (compile exp ##:to 'value ##:env  env)))
                                   (render mvalue)))
                   ##:printer (language-printer scheme)
                   ##:make-default-environment
                   (language-make-default-environment scheme))))

(define-meta-command ((render-repl exp) repl)
  "run-in-store EXP
Run EXP through the store monad."
  (let ((new (make-repl render-language)))
    (repl-option-set! new 'interp #t)
    (run-repl new)))

You could define your own custom reader that will say read d3:msg5:helloe into an S-expression. This form is called bencode.