Scheme '97: A Scheme Evaluator written in Java

In order to get an overdesigned IG-tool working, I implemented overnight a scheme evaluator. The evaluator itself is written in Java and has been used to do research in ontologies, webdesign, distributed systems. As such there were 11 versions wandering around at the time I created this page. I took a couple of gours and some serious source-archeology to  find out what exactly has happened over the different versions.

Basically there are 4 branches: One for IG, one Ontology version (called the context version), one CORBA branch and one Distri branch. The latter was used to teach with.

Version 2.6: one of the first times I decided I should archive the different versions.
Version 2.8: branched from 2.6 and 2.5 together with new code written at IG.
Version 2.9-ig: inherits from 2.8, has full database support
Version 2.9: inherits from 2.9-ig, but without the databse support
Version 2.9-eval: only the evaluator, the runtime has been removed. Has been used in my java based agent system.
Version 2.6-corba1: branched from version 2.6. Started added distribution using corba
Version 2.6-corba2: corba experiment, inherits from 2.6-corba1
Version 2.6-context1: the ontology version of the scheme evaluator. Offers high-level dictionary primitives
Version 2.6-context2: inherits from Version 2.6-context1
Version 2.7-distri1: branched from 2.6-contex2, used to teach the course distributed systems in '01
Version 2.7-distri2: inherits from 2.7-distri1, prepared to be used in the distributed system course in '02. The versions 2.7-distri1 and 2.7-distri2 uses interfaces for applicabel functions. This makes it more easy to plugin skeletons and stubs.
Database support is available in version 2.6, 2.8 and 2.9-ig.
Portmailer support is not available in version 2.7-distri1, 2.7-distri2 and 2.9-eval
ContextDictionaries are available in version 2.6-context1 and 2.6-context2
Manuals are available in version 2.6-context1, 2.6-context2, 2.7-distri1 and 2.7-distri2

The inheritance tree is pictured below. Probably the best one to use is version 2.9, but don't forget to take the documentation from another version.




Manual

Manual by Dirk Van Deun

I: De Scheme interpreter

Scheme evalueren met deze evaluator doet men met 'java Scheme <file>.scm';

De Scheme interpreter die we gebruiken ondersteunt slechts drie elementaire datatypes: symbols, strings en 4 byte integers. Verder kan men cons-celletjes, lijsten en vectoren vormen. De lege lijst doet dienst als Booleaanse False; aangezien alles wat niet False is, True is, wordt meestal de integerwaarde 1 als True geretourneerd.

NOOT: In dit Scheme zijn door luiheid van de Scheme-implementator dingen hard gecodeerd, zoals
scheme/runtime/PortMailer.java bevat de positie van de sendmailport van de VUB

Overzicht van de ondersteunde syntax: (als er geen commentaar bij gegeven wordt, werd de R4RS standaard gevolgd; let ook steeds op de ondersteunde vormen en aantallen parameters):

(QUOTE expr)

(LAMBDA (parms) body)
(LAMBDA (parms . parm) body)

(LET ((name_1 value1) ... (name_n value_n)) body)
(LET* ((name_1 value1) ... (name_n value_n)) body)

(IF pred truebody)
(IF pred truebody falsebody)

(COND ((pred_1 body1) ... (pred_n body_n))
(COND ((pred_1 body1) ... (pred_n body_n) (else body))

(DEFINE name expr)
(DEFINE (name parms) body)
(DEFINE (name . parm) body)
(DEFINE (name parms . parm) body)

(SET! name expr)

(AND expr_1 ... expr_n)
(OR expr_1 ... expr_n)
zoals standaard Scheme maar niet te vertrouwen met nul argumenten

(BEGIN expr_1 ... expr_n)

(DO ((var1 init1 increment1) ... (var_n init_n increment_n))
(pred result) body)
er is een goede kans dat dit overeenkomt met de standaard DO; de implementator is er zelf niet zo zeker van...

(WHILE pred body)
body wordt uitgevoerd totdat pred naar '() evalueert; functieresultaat ongedefinieerd

(DEFINED var)
geeft de inhoud van de variabele indien die gedefinieerd is, anders '(). Trek uw plan als de variabele de inhoud '() heeft.

Overzicht van ondersteunde functies:

(! expr) = (NOT expr)

(CONS expr expr)

(CAR list)
(CDR list)
(CADR list)

(MEMBER object list) = (MEMQ object list)
= de memq van standaard Scheme

(ADD numeric_expr_1 ... numeric_expr_n), (+ numeric_expr_1 ... numeric_expr_n)
(MUL numeric_expr_1 ... numeric_expr_n), (* numeric_expr_1 ... numeric_expr_n)

(SUB numeric_expr_1 numeric_expr_2), (- numeric_expr_1 numeric_expr_2)
(DIV numeric_expr_1 numeric_expr_2), (/ numeric_expr_1 numeric_expr_2)

(= numeric_expr_1 numeric_expr_2)
(< numeric_expr_1 numeric_expr_2), (> numeric_expr_1 numeric_expr_2)
(<= numeric_expr_1 numeric_expr_2), (>= numeric_expr_1 numeric_expr_2)
(<> numeric_expr_1 numeric_expr_2), (!= numeric_expr_1 numeric_expr_2)

(EQ? expr expr)

(= string_expr_1 string_expr_2), (string=? string_expr_1 string_expr_2)
(< string_expr_1 string_expr_2), (> string_expr_1 string_expr_2)
(<= string_expr_1 string_expr_2), (>= string_expr_1 string_expr_2)
(<> string_expr_1 string_expr_2), (!= string_expr_1 string_expr_2)
case sensitive stringvergelijkingen

(STRING? expr)
(VECTOR? expr)

(NULL? obj)
controleert of object '() is; is dus eigenlijk equivalent met NOT in deze versie omdat '() gebruikt wordt als False

(LIST? obj)
de PAIR? van standaard Scheme

(LENGTH list)

(LIST-REF list el)

(STRING-REF str pos)
retourneert een enkelkarakterige string bij gebrek aan chars

(STRING-SET! str1 pos str2)
als str2 enkelkarakterig is, komt dit overeen met de klassieke string-set!; maar str2 kan ook een andere lengte hebben, en dan wordt 1 karakter op de geviseerde positie weggenomen, en de hele tweede string ingevoegd. Motivatie: Werner is nu eenmaal zo.

(STRING-NULL? string-expr)
controleert of een string leeg is

(STRING->SYMBOL string)
(SYMBOL->STRING symbol)
wijken af van de standaard doordat symbol->string altijd een
upper case resultaat teruggeeft

(STRING->LIST string)
zet string om in een lijst van *strings* van lengte 1
(er zijn geen chars in dit dialect) (zeer onperformante functie)

(STRING->NUMBER string)
(NUMBER->STRING integer)
conversie tussen strings en integers

(LIST->VECTOR list)
(VECTOR->LIST vector)

(LIST expr_1 ... expr_n)
(VECTOR expr_1 ... expr_n)

(MAKE-VECTOR lengte)
(MAKE-VECTOR lengte init)
als geen init wordt opgegeven wordt '() gebruikt

(VECTOR-LENGTH vector)
(STRING-LENGTH string)

(VECTOR-SET! vector pos expr)
(VECTOR-REF vector pos)

(APPLY function parmlist)

(EVAL expr)
evalueert de expressie in de huidige environment

(STRING-APPEND str1 str2 ...)

(SUBSTRING beginpos eindpos string)
evident

(GETCHAR pos string)
geeft het karakter op de aangegeven positie in de string weer

(REPLACE string substring1 substring2)
geeft de string terug na vervanging van substring1 door substring2
gebruikt om HTML-templates in te vullen

(INDEX-OF string substring)
(INDEX-OF string substring startpos)
zoekt een substring in een string, eventueel vanaf een bepaalde
startpositie, en geeft zijn positie weer (een integer vanaf nul)

(OPEN-INPUT-FILE string)
(OPEN-OUTPUT-FILE string)
evident; functieresultaat is een port of '() (bij fout)

(CLOSE port)
evident; functieresultaat is '()

(READSTRING port)
leest alle beschikbare tekst van een beschikbare poort
en geeft dit weer als een enkele lange string

(WRITESTRING string port)
(WRITESTRING string)
evident; functieresultaat is weer de string

(DISPLAY expr)
(NEWLINE)

(LOAD string)

II: Het toevoegen van natives aan de Scheme interpreter

Men voegt Scheme-natives toe in scheme.java door middel van een instructie volgens het volgende patroon:

installNative(d,"/","native_divide",false,2,false)
  •     d: rootdictionary (gewoon kopieren)
  •     "/": naam binnen Scheme-environment
  •     "native_divide": methodenaam in de Runtime-klasse
  •     false: zegt dat de parameters voor deze functie op voorhand geevalueerd moeten worden (dus geen delayed evaluation). Voor de if functie zijn deze bijvoorbeeld wel delayed
  •     2: parameterpassing-conventie voor Java. Het getal 2 wil zeggen dat er 2 parameters verwacht worden. Voor een variabel aantal parameters zijn er Native.LIST en Native.ARRAY, te gebruiken naar smaak van de implementator van de primitieve functie
  •     false: geeft aan of de caller-environment interessant is

Om het voorbeeld af te maken is hier de functie native_divide:

public Object native_divide(Object a, Object b, Environment e)
{ return new Integer(((Integer)a).intValue()/((Integer)b).intValue()); }

Als we zouden werken met de conventie Native.ARRAY krijgen we:

public Object native_divide(Object p[], Environment e)
{ return new
Integer(((Integer)p[0]).intValue()/((Integer)p[1]).intValue()); }

En als we met de lijstrepresentatie zouden werken geeft dat:

public Object native_divide(Object o, Environment e)
{ Cons l=(Cons)o;
return new
Integer(((Integer)l.car()).intValue()/((Integer)l.cadr()).intValue()); }

Noot: mapping van datatypes:

Scheme Java-klasse
-------------------------------------------
string String
symbol Identifier
number Integer
cons-cel Cons
vector Object[]
#f / lege lijst null

http://werner.yellowcouch.org/
mailto:werner@yellowcouch.org