R7RS support in Gerbil
Gerbil supports R7RS as a custom language with the
As such you can write and import modules written in R7RS Scheme and mix them
freely with other Gerbil modules.
R7RS in the REPL
In pure Gerbil, you can also evaluate R7RS expression by using the
eval procedure provided by
You can also start the Gerbil interpreter in an r7rs repl, by passing
$ gxi --lang r7rs R7RS Scheme in Gerbil v0.12-DEV on Gambit v4.8.8 > _
The top context uses R7RS scheme syntax and includes bindings from
Writing (or porting) an R7RS module is simple: you just specify
:scheme/r7rs as the prelude. The prelude exports only the
necessities to support library definitions and imports.
Note that there can be only a single library defined within a module, and its name must match the module id constructed by the package and name of the file. This implementation restriction has to do with library module resolution: Gerbil constructs a top module per file and resolves by interpreting the library name as a load-path relative path.
Gerbil modules can also have nested modules, but nested libraries are unspecified by the R7RS report and so they are disallowed for R7RS modules.
With this in mind, we can reproduce the Life example from the R7RS report.
First, let's define the
(example grid) library:
$ cat > gerbil.pkg (prelude: :scheme/r7rs) $ mkdir example $ cat > example/grid.ss (define-library (example grid) (export make rows cols ref each (rename put! set!)) (import (scheme base)) (begin ;; Create an NxM grid. (define (make n m) (let ((grid (make-vector n))) (do ((i 0 (+ i 1))) ((= i n) grid) (let ((v (make-vector m #f))) (vector-set! grid i v))))) (define (rows grid) (vector-length grid)) (define (cols grid) (vector-length (vector-ref grid 0))) ;; Return #false if out of range. (define (ref grid n m) (and (< -1 n (rows grid)) (< -1 m (cols grid)) (vector-ref (vector-ref grid n) m))) (define (put! grid n m v) (vector-set! (vector-ref grid n) m v)) (define (each grid proc) (do ((j 0 (+ j 1))) ((= j (rows grid))) (do ((k 0 (+ k 1))) ((= k (cols grid))) (proc j k (ref grid j k)))))))
And then we define the
(example life) library:
$ cat > example/life.ss (define-library (example life) (export life) (import (except (scheme base) set!) (scheme write) (example grid)) (begin (define (life-count grid i j) (define (count i j) (if (ref grid i j) 1 0)) (+ (count (- i 1) (- j 1)) (count (- i 1) j) (count (- i 1) (+ j 1)) (count i (- j 1)) (count i (+ j 1)) (count (+ i 1) (- j 1)) (count (+ i 1) j) (count (+ i 1) (+ j 1)))) (define (life-alive? grid i j) (case (life-count grid i j) ((3) #t) ((2) (ref grid i j)) (else #f))) (define (life-print grid) (display "\x1B;[1H\x1B;[J") ; clear vt100 (each grid (lambda (i j v) (display (if v "*" " ")) (when (= j (- (cols grid) 1)) (newline))))) (define (life grid iterations) (do ((i 0 (+ i 1)) (grid0 grid grid1) (grid1 (make (rows grid) (cols grid)) grid0)) ((= i iterations)) (each grid0 (lambda (j k v) (let ((a (life-alive? grid0 j k))) (set! grid1 j k a)))) (life-print grid1)))))
We compile our library modules with
gxc as usual:
$ gxc example/grid.ss example/life.ss
And then we can run a little game of Life in the interpreter:
$ gxi > (import (only-in :example/life life) (rename-in (prefix-in :example/grid grid-) (grid-make make-grid))) ;; Initialize a grid with a glider. > (define grid (make-grid 24 24)) > (grid-set! grid 1 1 #t) > (grid-set! grid 2 2 #t) > (grid-set! grid 3 0 #t) > (grid-set! grid 3 1 #t) > (grid-set! grid 3 2 #t) ;; Run for 80 iterations > (life grid 80) ...
Evaluating R7RS Expressions
This is just a matter of using the R7RS eval. Continuing the example above, we can do the same evaluations with pure R7RS syntax:
$ gxi > (import :scheme/eval :scheme/repl) > (eval '(import (scheme base) (only (example life) life) (rename (prefix (example grid) grid-) (grid-make make-grid))) (interaction-environment)) > (eval '(begin (define grid (make-grid 24 24)) (grid-set! grid 1 1 #t) (grid-set! grid 2 2 #t) (grid-set! grid 3 0 #t) (grid-set! grid 3 1 #t) (grid-set! grid 3 2 #t)) (interaction-environment)) > (eval '(life grid 80) (interaction-environment)) ...
define-librarycan appear at most once per library module and the library id must match the id of the enclosing module.
(scheme char)are not implemented.
#falsedatums are not recognized by the reader.
#\nullnamed character datums are not recognized by the reader.
The reader doesn't accept shared structures by default, as cycles are not currently supported in syntax objects. You can enable it on a per-port basis with a custom readtable, but be advised that the repl will loop when given a cyclic structure as syntax input.
equal?is the default
equal?from Gerbil/Gambit, which doesn't do sharing check and may not terminate in cyclic structures. There is a separate implementation
(std misc shared)that terminates in cyclic structures albeit at the cost of performance degradation.