/little-scheme-in-java

A Scheme interpreter with first-class continuations in circa 900 lines of Java 8/11

Primary LanguageJavaMIT LicenseMIT

A Little Scheme in Java

This is a small interpreter of a subset of Scheme in circa 900 lines of Java 8 and 11 (including a small arithmetic package little_arith in circa 100 lines). It implements almost the same language as

and their meta-circular interpreter, little-scheme.

As a Scheme implementation, it optimizes tail calls and handles first-class continuations properly.

How to run

$ make
rm -f little_arith/*.class little_scheme/*.class
javac -encoding utf-8 little_scheme/Main.java
jar cfm little-scheme.jar Manifest LICENSE little_arith/*.class little_scheme/*.
class
$ java -jar little-scheme.jar
> (+ 5 6)
11
> (cons 'a (cons 'b 'c))
(a b . c)
> (list
| 1
| 2
| 3
| )
(1 2 3)
> 

Press EOF (e.g. Control-D) to exit the session.

> Goodbye
$ 

You can run it with a Scheme script. Examples are found in little-scheme; download it at .. and you can try the following:

$ java -jar little-scheme.jar ../little-scheme/examples/yin-yang-puzzle.scm |
> head

*
**
***
****
*****
******
*******
********
*********
^C$
$ java -jar little-scheme.jar ../little-scheme/examples/amb.scm
((1 A) (1 B) (1 C) (2 A) (2 B) (2 C) (3 A) (3 B) (3 C))
$ java -jar little-scheme.jar ../little-scheme/examples/nqueens.scm
((5 3 1 6 4 2) (4 1 5 2 6 3) (3 6 2 5 1 4) (2 4 6 1 3 5))
$ java -jar little-scheme.jar ../little-scheme/scm.scm \
> < ../little-scheme/examples/nqueens.scm 
((5 3 1 6 4 2) (4 1 5 2 6 3) (3 6 2 5 1 4) (2 4 6 1 3 5))
$ 

Press INTR (e.g. Control-C) to terminate the yin-yang-puzzle.

Put a "-" after the script in the command line to begin a session after running the script.

$ java -jar little-scheme.jar ../little-scheme/examples/fib90.scm -
2880067194370816120
> (globals)
(globals error number? = < * - + apply call/cc symbol? eof-object? read newline
display list not null? pair? eq? cons cdr car fibonacci)
> (fibonacci 16)
987
> (fibonacci 1000)
43466557686937456435688527675040625802564660517371780402481729089536555417949051
89040387984007925516929592259308032263477520968962323987332247116164299644090653
3187938298969649928516003704476137795166849228875
> 

The implemented language

Scheme Expression Internal Representation
numbers 1, 2.3 java.lang.Number
#t java.lang.Boolean.TRUE
#f java.lang.Boolean.FALSE
strings "hello, world" java.lang.String
symbols a, + little_scheme.Sym
() null
pairs (1 . 2), (x y z) little_scheme.Cell
closures (lambda (x) (+ x 1)) little_scheme.Closure
built-in procedures car, cdr little_scheme.Intrinsic
continuations little_scheme.Continuation

The implementation is similar to those of little-scheme-in-dart and little-scheme-in-cs.

Expression types

  • v [variable reference]

  • (e0 e1...) [procedure call]

  • (quote e)
    'e [transformed into (quote e) when read]

  • (if e1 e2 e3)
    (if e1 e2)

  • (begin e...)

  • (lambda (v...) e...)

  • (set! v e)

  • (define v e)

For simplicity, this Scheme treats (define v e) as an expression type.

Built-in procedures

(car lst) (display x) (+ n1 n2)
(cdr lst) (newline) (- n1 n2)
(cons x y) (read) (* n1 n2)
(eq? x y) (eof-object? x) (< n1 n2)
(pair? x) (symbol? x) (= n1 n2)
(null? x) (call/cc fun) (number? x)
(not x) (apply fun arg) (globals)
(list x ...) (error reason arg)
  • (error reason arg) throws an exception with the message "Error: reason: arg". It is based on SRFI-23.

  • (globals) returns a list of keys of the global environment. It is not in the standard.

See GLOBAL_ENV in little_scheme.LS for the implementation of the procedures except call/cc and apply.
call/cc and apply are implemented particularly at applyFunction in little_scheme.Eval.

I hope this serves as a model of how to write a Scheme interpreter in Java 8 and later.