The SECD machine is a virtual machine designed to be a target m platform for functional programming languages, in particular Lispkit Lisp. the SECD name comes from the four basic registers of the virtual machine: stack, environment, control, and dump. Three of the registers typically act as stacks: stack, control, and dump. The environment register usually contains an associative array.
The SECD machine was originally described by Peter Landin in "The Mechanical Evaluation of Expressions" in 1964. The version I am considering here is described in Peter Henderson's book Functional Programming Application and Implementation [1980]. It's out of print. You can find used copies for sale online.
Each cycle of the machine updates one or more of the registers. An SECD program is an encoding of a functional program into machine code/ In the SECD representation, each operation is represented as a number.
S-expressions
The SECD machine operates on three different types of objects: symbols, numbers, and lists, In this version of the SECD virtual machine, numbers will be integers. For purposes of input and output, SECD and Lispkit Lisp programs are formatted as s-expressions.
The grammar of s-expressions is
s-expression ::= atom | ( s-expression-list )s-expression-list ::= s-expression | s-expression . s-expression | s-expression s-expression-listatom ::= integer | string
There is a special symbol atom named "NIL" which represents the empty list. Examples of valid s-expressions are "cat", 42, ( A . B ), and ( a b c d ).
The latter example could be written (a . (b . (c . (d . NIL)))).
Each s-expression type is represented by a class.
/* * SExp.java * * author: Bill Thompson * license: GPL 3 * copyright: 2026-01-27 * * A class for S expressions. * * Cons, SymbolAtom, and NumberAtom are subclasses. * */ package LispKit; public class SExp { protected static final int EMPTY = -1; protected static final int CONS = 0; protected static final int INT = 1; protected static final int SYMBOL = 2; protected int _type; public SExp() { _type = EMPTY; } public boolean isNumber() { return (_type == INT); } public boolean isSymbol() { return (_type == SYMBOL ); } public boolean isCons() { return (_type == CONS); } public boolean isEmpty() { return (_type == EMPTY); } // NIL is a SymbolAtom // NIL should be made a singleton. public boolean isNIL() { if( _type == SYMBOL ) return ((SymbolAtom) this).GetSymbol().equals( "NIL"); else return false; } // added car and cdr functions, so that calling them on a non-cons, returs NIL public SExp car() { return new SymbolAtom(); } public SExp cdr() { return new SymbolAtom(); } }
/* * SymbolAtopm.java * author: Bill Thompson * license: GPL 3 * copyright: 2026-01-27 * * A class for holding symbols. * */ package LispKit; public class SymbolAtom extends SExp { private final String _string; public SymbolAtom( String s ) { _type = SYMBOL; _string = s; } // NIL public SymbolAtom() { _type = SYMBOL; _string = "NIL"; } public String GetSymbol() { return _string; } }
/* * NumberAtom.java * author: Bill Thompson * license: GPL 3 * copyright: 2026-01-27 * * A class for holding integers. * This class could be changed to hold floats of BigNums. * */ package LispKit; public class NumberAtom extends SExp { private final int _number; public NumberAtom( int n ) { _type = INT; _number = n; } public int GetInt() { return _number; } }
/* * Cons.java * author: Bill Thompson * license: GPL 3 * copyright: 2026-01-27 * * A class for cons cells. * Cons are the glue holding Lisp functions together. * */ package LispKit; public class Cons extends SExp { private SExp _car; private SExp _cdr; public Cons( SExp aCar, SExp aCdr ) { _type = CONS; _car = aCar; _cdr = aCdr; } @Override public SExp car() { return _car; } @Override public SExp cdr() { return _cdr; } public void SetCar( SExp aCar ) { _car = aCar; } public void SetCdr( SExp aCdr ) { _cdr = aCdr; } }
The Cons class provided three functions that are important for manipulating s-expressions:
cons - construct a new s-expressioncar - copy the first element of the cons.cdr - return the rest of the cons.
The code provides classes for reading and writing s-expression. I won't list them here, they are long. The code is modeled on the methods described in Henderson's book. See GitHub for details.
The SECD Machine
Below are the SECD numeric opcode and the operations described as transitions of the registers. The the descriptions are based on Peter Henderson's book. The transition table was generated by ChatGPT, mostly based on https://pqnelson.github.io/org-notes/comp-sci/abstract-machines/secd.html.
|
Opcode |
Mnemonic |
Meaning
(short) |
|
0 |
NIL |
push NIL /
empty list |
|
1 |
LD |
load from
environment |
|
2 |
LDC |
load constant
|
|
3 |
LDF |
load function
(closure) |
|
4 |
AP |
apply |
|
5 |
RTN |
return |
|
6 |
DUM |
push dummy
env (for recursion) |
|
7 |
RAP |
recursive
apply |
|
8 |
SEL |
conditional
branch |
|
9 |
JOIN |
join after
SEL |
|
10 |
CAR |
head |
|
11 |
CDR |
tail |
|
12 |
ATOM |
atom? |
|
13 |
CONS |
cons pair |
|
14 |
EQ |
equality |
|
15 |
ADD |
add |
|
16 |
SUB |
subtract |
|
17 |
MUL |
multiply |
|
18 |
DIV |
divide |
|
19 |
REM |
remainder |
|
20 |
LEQ |
≤ test |
|
21 |
STOP |
stop / (in
LispKit) special behavior described in manual |
|
|
==========================================================================
Below are the standard SECD small-step transition rules
(Henderson-style) for a machine state ⟨s, e, c, d⟩, using Lisp list notation:
- (a .
s) means push a on stack s (top at the left).
- (OP .
c) means instruction OP followed by remaining control c.
- A
closure is the pair (c' . e') (code + environment).
- T / F are
the booleans (often represented as non-NIL / NIL in implementations).
These rules follow the common “Henderson SECD” presentation.
(pqnelson.github.io)
S-expression operations
CONS
⟨(a b . s), e, (CONS . c), d⟩ → ⟨((a . b) . s), e, c, d⟩ (pqnelson.github.io)
CAR
⟨((a . b) . s), e, (CAR . c), d⟩ → ⟨(a . s), e, c, d⟩ (pqnelson.github.io)
CDR
⟨((a . b) . s), e, (CDR . c), d⟩ → ⟨(b . s), e, c, d⟩ (pqnelson.github.io)
ATOM
⟨(a . s), e, (ATOM . c), d⟩ → ⟨(t . s), e, c, d⟩
where t = T if a is an atom, else t = F. (pqnelson.github.io)
Arithmetic and predicates (all binary; pop a then b)
Each of these leaves e, c, d unchanged; it rewrites the
stack top:
- ADD:
(a b . s) → ((b + a) . s)
- SUB:
(a b . s) → ((b − a) . s)
- MUL:
(a b . s) → ((b * a) . s)
- DIV:
(a b . s) → ((b / a) . s)
- REM:
(a b . s) → ((b % a) . s)
- LEQ:
(a b . s) → ((b <= a) . s) (boolean result)
- EQ:
(a b . s) → ((b = a) . s) (boolean result)
So, e.g.
⟨(a b . s), e, (ADD . c), d⟩ → ⟨((b + a) . s), e, c, d⟩, etc. (pqnelson.github.io)
Loading / closures
LD i
⟨s, e, (LD i . c), d⟩ → ⟨(x . s), e, c, d⟩
where x = locate(i, e). (pqnelson.github.io)
LDC n
⟨s, e, (LDC n . c), d⟩ → ⟨(n . s), e, c, d⟩ (pqnelson.github.io)
LDF c' (make closure)
⟨s, e, (LDF c' . c), d⟩ → ⟨((c' . e) . s), e, c, d⟩ (pqnelson.github.io)
Meta-level lookup (as typically defined)
If i = (b . n) is a pair of indices (frame, offset), then:
locate(b, n, e) = index(n, index(b, e)) where index is 0-based list indexing. (pqnelson.github.io)
Conditionals
SEL cT cF
⟨(x . s), e, (SEL cT cF . c), d⟩ → ⟨s, e, cX, (c . d)⟩
where cX = cT if x = T, else cX = cF. (pqnelson.github.io)
JOIN
⟨s, e, (JOIN), (c . d)⟩ → ⟨s, e, c, d⟩ (pqnelson.github.io)
Function application / return / recursion
Let a closure be (c' . e') and let v be the argument value
list.
AP
⟨((c' . e') v . s), e, (AP . c), d⟩ → ⟨NIL, (v . e'), c', (s e c . d)⟩ (pqnelson.github.io)
(Interpretation: clear stack to NIL, extend closure env with
argument list v, start executing c', and push caller context onto dump.)
RTN
⟨(x), e', (RTN), (s e c . d)⟩ → ⟨(x . s), e, c, d⟩ (pqnelson.github.io)
DUM (dummy environment for recursion setup)
⟨s, e, (DUM . c), d⟩ → ⟨s, (PENDING . e), c, d⟩ (pqnelson.github.io)
RAP (recursive apply; patch dummy frame)
⟨((c' . e') v . s), (PENDING . e), (RAP . c), d⟩ → ⟨NIL, rplaca(e', v), c', (s
e c . d)⟩ (pqnelson.github.io)
Here rplaca is the meta-operation “replace the car of e'
with v” (i.e., patch the dummy frame so the function can refer to itself). (pqnelson.github.io)
STOP
STOP halts execution. (Often “final state when C and D are empty”; presentations vary.) (pqnelson.github.io)
case 2 -> { // LDC Cons c2 = (Cons) c; Cons c3 = (Cons) c2.cdr(); // cdr(c) SExp c4 = c3.car(); //car(cdr(c)) s = new Cons( c4, s); // cons(car(cdr(c)), s) c = c3.cdr(); // c = cdr(cdr(c)) }
Lispkit Lisp
( LETREC COMPILE ( COMPILE LAMBDA ( E ) ( COMP E ( QUOTE NIL ) ( QUOTE ( 4 21 ) ) ) ) ( COMP LAMBDA ( E N C ) ( IF ( ATOM E ) ( CONS ( QUOTE 1 ) ( CONS ( LOCATION E N ) C ) ) ( IF ( EQ ( CAR E ) ( QUOTE QUOTE ) ) ( CONS ( QUOTE 2 ) ( CONS ( CAR ( CDR E ) ) C ) ) ( IF ( EQ ( CAR E ) ( QUOTE ADD ) ) ( COMP ( CAR ( CDR E ) ) N ( COMP ( CAR ( CDR ( CDR E ) ) ) N ( CONS ( QUOTE 15 ) C ) ) ) ( IF ( EQ ( CAR E ) ( QUOTE SUB ) ) ( COMP ( CAR ( CDR E ) ) N ( COMP ( CAR ( CDR ( CDR E ) ) ) N ( CONS ( QUOTE 16 ) C ) ) ) ( IF ( EQ ( CAR E ) ( QUOTE MUL ) ) ( COMP ( CAR ( CDR E ) ) N ( COMP ( CAR ( CDR ( CDR E ) ) ) N ( CONS ( QUOTE 17 ) C ) ) ) ( IF ( EQ ( CAR E ) ( QUOTE DIV ) ) ( COMP ( CAR ( CDR E ) ) N ( COMP ( CAR ( CDR ( CDR E ) ) ) N ( CONS ( QUOTE 18 ) C ) ) ) ( IF ( EQ ( CAR E ) ( QUOTE REM ) ) ( COMP ( CAR ( CDR E ) ) N ( COMP ( CAR ( CDR ( CDR E ) ) ) N ( CONS ( QUOTE 19 ) C ) ) ) ( IF ( EQ ( CAR E ) ( QUOTE LEQ ) ) ( COMP ( CAR ( CDR E ) ) N ( COMP ( CAR ( CDR ( CDR E ) ) ) N ( CONS ( QUOTE 20 ) C ) ) ) ( IF ( EQ ( CAR E ) ( QUOTE EQ ) ) ( COMP ( CAR ( CDR E ) ) N ( COMP ( CAR ( CDR ( CDR E ) ) ) N ( CONS ( QUOTE 14 ) C ) ) ) ( IF ( EQ ( CAR E ) ( QUOTE CAR ) ) ( COMP ( CAR ( CDR E ) ) N ( CONS ( QUOTE 10 ) C ) ) ( IF ( EQ ( CAR E ) ( QUOTE CDR ) ) ( COMP ( CAR ( CDR E ) ) N ( CONS ( QUOTE 11 ) C ) ) ( IF ( EQ ( CAR E ) ( QUOTE ATOM ) ) ( COMP ( CAR ( CDR E ) ) N ( CONS ( QUOTE 12 ) C ) ) ( IF ( EQ ( CAR E ) ( QUOTE CONS ) ) ( COMP ( CAR ( CDR ( CDR E ) ) ) N ( COMP ( CAR ( CDR E ) ) N ( CONS ( QUOTE 13 ) C ) ) ) ( IF ( EQ ( CAR E ) ( QUOTE IF ) ) ( LET ( COMP ( CAR ( CDR E ) ) N ( CONS ( QUOTE 8 ) ( CONS THENPT ( CONS ELSEPT C ) ) ) ) ( THENPT COMP ( CAR ( CDR ( CDR E ) ) ) N ( QUOTE ( 9 ) ) ) ( ELSEPT COMP ( CAR ( CDR ( CDR ( CDR E ) ) ) ) N ( QUOTE ( 9 ) ) ) ) ( IF ( EQ ( CAR E ) ( QUOTE LAMBDA ) ) ( LET ( CONS ( QUOTE 3 ) ( CONS BODY C ) ) ( BODY COMP ( CAR ( CDR ( CDR E ) ) ) ( CONS ( CAR ( CDR E ) ) N ) ( QUOTE ( 5 ) ) ) ) ( IF ( EQ ( CAR E ) ( QUOTE LET ) ) ( LET ( LET ( COMPLIS ARGS N ( CONS ( QUOTE 3 ) ( CONS BODY ( CONS ( QUOTE 4 ) C ) ) ) ) ( BODY COMP ( CAR ( CDR E ) ) M ( QUOTE ( 5 ) ) ) ) ( M CONS ( VARS ( CDR ( CDR E ) ) ) N ) ( ARGS EXPRS ( CDR ( CDR E ) ) ) ) ( IF ( EQ ( CAR E ) ( QUOTE LETREC ) ) ( LET ( LET ( CONS ( QUOTE 6 ) ( COMPLIS ARGS M ( CONS ( QUOTE 3 ) ( CONS BODY ( CONS ( QUOTE 7 ) C ) ) ) ) ) ( BODY COMP ( CAR ( CDR E ) ) M ( QUOTE ( 5 ) ) ) ) ( M CONS ( VARS ( CDR ( CDR E ) ) ) N ) ( ARGS EXPRS ( CDR ( CDR E ) ) ) ) ( COMPLIS ( CDR E ) N ( COMP ( CAR E ) N ( CONS ( QUOTE 4 ) C ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ( COMPLIS LAMBDA ( E N C ) ( IF ( EQ E ( QUOTE NIL ) ) ( CONS ( QUOTE 2 ) ( CONS ( QUOTE NIL ) C ) ) ( COMPLIS ( CDR E ) N ( COMP ( CAR E ) N ( CONS ( QUOTE 13 ) C ) ) ) ) ) ( LOCATION LAMBDA ( E N ) ( LETREC ( IF ( MEMBER E ( CAR N ) ) ( CONS ( QUOTE 0 ) ( POSN E ( CAR N ) ) ) ( INCAR ( LOCATION E ( CDR N ) ) ) ) ( MEMBER LAMBDA ( E N ) ( IF ( EQ N ( QUOTE NIL ) ) ( QUOTE F ) ( IF ( EQ E ( CAR N ) ) ( QUOTE T ) ( MEMBER E ( CDR N ) ) ) ) ) ( POSN LAMBDA ( E N ) ( IF ( EQ E ( CAR N ) ) ( QUOTE 0 ) ( ADD ( QUOTE 1 ) ( POSN E ( CDR N ) ) ) ) ) ( INCAR LAMBDA ( L ) ( CONS ( ADD ( QUOTE 1 ) ( CAR L ) ) ( CDR L ) ) ) ) ) ( VARS LAMBDA ( D ) ( IF ( EQ D ( QUOTE NIL ) ) ( QUOTE NIL ) ( CONS ( CAR ( CAR D ) ) ( VARS ( CDR D ) ) ) ) ) ( EXPRS LAMBDA ( D ) ( IF ( EQ D ( QUOTE NIL ) ) ( QUOTE NIL ) ( CONS ( CDR ( CAR D ) ) ( EXPRS ( CDR D ) ) ) ) ) )
( 6 2 NIL 3 ( 1 ( 0 . 0 ) 2 NIL 14 8 ( 2 NIL 9 ) ( 2 NIL 1 ( 0 . 0 ) 11 13 1 ( 1 . 5 ) 4 1 ( 0 . 0 ) 10 11 13 9 ) 5 ) 13 3 ( 1 ( 0 . 0 ) 2 NIL 14 8 ( 2 NIL 9 ) ( 2 NIL 1 ( 0 . 0 ) 11 13 1 ( 1 . 4 ) 4 1 ( 0 . 0 ) 10 10 13 9 ) 5 ) 13 3 ( 6 2 NIL 3 ( 1 ( 0 . 0 ) 11 2 1 1 ( 0 . 0 ) 10 15 13 5 ) 13 3 ( 1 ( 0 . 0 ) 1 ( 0 . 1 ) 10 14 8 ( 2 0 9 ) ( 2 1 2 NIL 1 ( 0 . 1 ) 11 13 1 ( 0 . 0 ) 13 1 ( 1 . 1 ) 4 15 9 ) 5 ) 13 3 ( 1 ( 0 . 1 ) 2 NIL 14 8 ( 2 F 9 ) ( 1 ( 0 . 0 ) 1 ( 0 . 1 ) 10 14 8 ( 2 T 9 ) ( 2 NIL 1 ( 0 . 1 ) 11 13 1 ( 0 . 0 ) 13 1 ( 1 . 0 ) 4 9 ) 9 ) 5 ) 13 3 ( 2 NIL 1 ( 1 . 1 ) 10 13 1 ( 1 . 0 ) 13 1 ( 0 . 0 ) 4 8 ( 2 NIL 1 ( 1 . 1 ) 10 13 1 ( 1 . 0 ) 13 1 ( 0 . 1 ) 4 2 0 13 9 ) ( 2 NIL 2 NIL 1 ( 1 . 1 ) 11 13 1 ( 1 . 0 ) 13 1 ( 2 . 3 ) 4 13 1 ( 0 . 2 ) 4 9 ) 5 ) 7 5 ) 13 3 ( 1 ( 0 . 0 ) 2 NIL 14 8 ( 1 ( 0 . 2 ) 2 NIL 13 2 2 13 9 ) ( 2 NIL 2 NIL 1 ( 0 . 2 ) 2 13 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 10 13 1 ( 1 . 1 ) 4 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 13 1 ( 1 . 2 ) 4 9 ) 5 ) 13 3 ( 1 ( 0 . 0 ) 12 8 ( 1 ( 0 . 2 ) 2 NIL 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 13 1 ( 1 . 3 ) 4 13 2 1 13 9 ) ( 1 ( 0 . 0 ) 10 2 QUOTE 14 8 ( 1 ( 0 . 2 ) 1 ( 0 . 0 ) 11 10 13 2 2 13 9 ) ( 1 ( 0 . 0 ) 10 2 ADD 14 8 ( 2 NIL 2 NIL 1 ( 0 . 2 ) 2 15 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 11 10 13 1 ( 1 . 1 ) 4 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 10 13 1 ( 1 . 1 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 SUB 14 8 ( 2 NIL 2 NIL 1 ( 0 . 2 ) 2 16 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 11 10 13 1 ( 1 . 1 ) 4 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 10 13 1 ( 1 . 1 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 MUL 14 8 ( 2 NIL 2 NIL 1 ( 0 . 2 ) 2 17 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 11 10 13 1 ( 1 . 1 ) 4 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 10 13 1 ( 1 . 1 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 DIV 14 8 ( 2 NIL 2 NIL 1 ( 0 . 2 ) 2 18 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 11 10 13 1 ( 1 . 1 ) 4 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 10 13 1 ( 1 . 1 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 REM 14 8 ( 2 NIL 2 NIL 1 ( 0 . 2 ) 2 19 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 11 10 13 1 ( 1 . 1 ) 4 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 10 13 1 ( 1 . 1 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 LEQ 14 8 ( 2 NIL 2 NIL 1 ( 0 . 2 ) 2 20 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 11 10 13 1 ( 1 . 1 ) 4 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 10 13 1 ( 1 . 1 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 EQ 14 8 ( 2 NIL 2 NIL 1 ( 0 . 2 ) 2 14 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 11 10 13 1 ( 1 . 1 ) 4 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 10 13 1 ( 1 . 1 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 CAR 14 8 ( 2 NIL 1 ( 0 . 2 ) 2 10 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 10 13 1 ( 1 . 1 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 CDR 14 8 ( 2 NIL 1 ( 0 . 2 ) 2 11 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 10 13 1 ( 1 . 1 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 ATOM 14 8 ( 2 NIL 1 ( 0 . 2 ) 2 12 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 10 13 1 ( 1 . 1 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 CONS 14 8 ( 2 NIL 2 NIL 1 ( 0 . 2 ) 2 13 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 10 13 1 ( 1 . 1 ) 4 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 11 10 13 1 ( 1 . 1 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 IF 14 8 ( 2 NIL 2 NIL 2 ( 9 ) 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 11 11 10 13 1 ( 1 . 1 ) 4 13 2 NIL 2 ( 9 ) 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 11 10 13 1 ( 1 . 1 ) 4 13 3 ( 2 NIL 1 ( 1 . 2 ) 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 13 2 8 13 13 1 ( 1 . 1 ) 13 1 ( 1 . 0 ) 11 10 13 1 ( 2 . 1 ) 4 5 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 LAMBDA 14 8 ( 2 NIL 2 NIL 2 ( 5 ) 13 1 ( 0 . 1 ) 1 ( 0 . 0 ) 11 10 13 13 1 ( 0 . 0 ) 11 11 10 13 1 ( 1 . 1 ) 4 13 3 ( 1 ( 1 . 2 ) 1 ( 0 . 0 ) 13 2 3 13 5 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 LET 14 8 ( 2 NIL 2 NIL 1 ( 0 . 0 ) 11 11 13 1 ( 1 . 5 ) 4 13 1 ( 0 . 1 ) 2 NIL 1 ( 0 . 0 ) 11 11 13 1 ( 1 . 4 ) 4 13 13 3 ( 2 NIL 2 NIL 2 ( 5 ) 13 1 ( 0 . 0 ) 13 1 ( 1 . 0 ) 11 10 13 1 ( 2 . 1 ) 4 13 3 ( 2 NIL 1 ( 2 . 2 ) 2 4 13 1 ( 0 . 0 ) 13 2 3 13 13 1 ( 2 . 1 ) 13 1 ( 1 . 1 ) 13 1 ( 3 . 2 ) 4 5 ) 4 5 ) 4 9 ) ( 1 ( 0 . 0 ) 10 2 LETREC 14 8 ( 2 NIL 2 NIL 1 ( 0 . 0 ) 11 11 13 1 ( 1 . 5 ) 4 13 1 ( 0 . 1 ) 2 NIL 1 ( 0 . 0 ) 11 11 13 1 ( 1 . 4 ) 4 13 13 3 ( 2 NIL 2 NIL 2 ( 5 ) 13 1 ( 0 . 0 ) 13 1 ( 1 . 0 ) 11 10 13 1 ( 2 . 1 ) 4 13 3 ( 2 NIL 1 ( 2 . 2 ) 2 7 13 1 ( 0 . 0 ) 13 2 3 13 13 1 ( 1 . 0 ) 13 1 ( 1 . 1 ) 13 1 ( 3 . 2 ) 4 2 6 13 5 ) 4 5 ) 4 9 ) ( 2 NIL 2 NIL 1 ( 0 . 2 ) 2 4 13 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 10 13 1 ( 1 . 1 ) 4 13 1 ( 0 . 1 ) 13 1 ( 0 . 0 ) 11 13 1 ( 1 . 2 ) 4 9 ) 9 ) 9 ) 9 ) 9 ) 9 ) 9 ) 9 ) 9 ) 9 ) 9 ) 9 ) 9 ) 9 ) 9 ) 9 ) 9 ) 5 ) 13 3 ( 2 NIL 2 ( 4 21 ) 13 2 NIL 13 1 ( 0 . 0 ) 13 1 ( 1 . 1 ) 4 5 ) 13 3 ( 1 ( 0 . 0 ) 5 ) 7 4 21 )
/* * Lispkit.java * * author: Bill Thompson * license: GPL 3 * copyright: 2026-01-27 * * An SECD virtual machine for a functional version of Lisp * Based on Peter Henerson's book * Functional Programming Application and Implementation * ISBN 0-13-331579-7 * * I don't implement garbage collection. Java takes care of that. */ package LispKit; import java.io.*; public class LispKit { /** * Main class for the Lispkit System */ // compiler.secd is a compiled Lisp compiler for SECD from Henderson's book // the compiler.secd file is from https://github.com/carld/lispkit private static final String COMPILER_FILE = "compiler.secd"; public static void main(String[] args) throws FileNotFoundException, IOException { String cwd = GetCwd(); /** * Load the compiler * * Compiler file should be in same directory Lispkit class is run from */ SExp comp; try (FileInputStream f = new FileInputStream( cwd + COMPILER_FILE )) { SExpReader compReader = new SExpReader( f ); comp = compReader.GetExp(); } /** * Read the Lisp function * * If an arg is passed on commandline, it contains the Lisp function, * otherwise read the function from the terminal. */ SExpReader s; if( args.length > 0 ) { try (FileInputStream f2 = new FileInputStream( args[0] )) { s = new SExpReader( f2 ); } } else { s = new SExpReader(); } SExp fn = s.GetExp(); // display the function SExpWriter w = new SExpWriter(); w.PutSExp( fn ); w.ForceLineOut(); // compile the Lisp function SECD secd = new SECD(); SExp fn2 = new Cons( fn, new SymbolAtom() ); SExp compFn = secd.exec( comp, fn2 ); // print the compled function w.PutSExp( compFn ); w.ForceLineOut(); /** * Read the function arguments * * If a second arg is passed on commandline, it contains the arguments for the Lisp function, * otherwise read the arguments from the terminal. */ SExpReader s2; if( args.length > 1 ) { try (FileInputStream f3 = new FileInputStream( args[1] )) { s2 = new SExpReader( f3 ); } } else { s2 = new SExpReader(); } // SExp fnArgs = s2.GetExpList(); SExp fnArgs = s2.GetExp(); // display the function arguments w.PutSExp( fnArgs ); w.ForceLineOut(); // execute the function secd = new SECD(); SExp result = secd.exec( compFn, fnArgs ); // disply results w.PutSExp( result ); w.ForceLineOut(); } private static String GetCwd() { String cwd = System.getProperty( "user.dir" ); String dir; if( ! cwd.endsWith( "/" ) ) dir = cwd + "/"; else dir = cwd; return dir; } }
( LETREC ACKERMANN ( ACKERMANN LAMBDA ( X Y ) ( IF ( EQ X ( QUOTE 0 ) ) ( ADD Y ( QUOTE 1 ) ) ( IF ( EQ Y ( QUOTE 0 ) ) ( ACKERMANN ( SUB X ( QUOTE 1 ) ) ( QUOTE 1 ) ) ( ACKERMANN ( SUB X ( QUOTE 1 ) ) ( ACKERMANN X ( SUB Y ( QUOTE 1 ) ) ) ) ) ) ) )
// The input function ( LETREC ACKERMANN ( ACKERMANN LAMBDA ( X Y ) ( IF ( EQ X ( Q UOTE 0 ) ) ( ADD Y ( QUOTE 1 ) ) ( IF ( EQ Y ( QUOTE 0 ) ) ( ACKERMANN ( SUB X ( QUOTE 1 ) ) ( QUOTE 1 ) ) ( ACKERMANN ( S UB X ( QUOTE 1 ) ) ( ACKERMANN X ( SUB Y ( QUOTE 1 ) ) ) ) ) ) ) ) // compiled output ( 6 2 NIL 3 ( 1 ( 0 . 0 ) 2 0 14 8 ( 1 ( 0 . 1 ) 2 1 15 9 ) ( 1 ( 0 . 1 ) 2 0 14 8 ( 2 NIL 2 1 13 1 ( 0 . 0 ) 2 1 16 13 1 ( 1 . 0 ) 4 9 ) ( 2 NIL 2 NIL 1 ( 0 . 1 ) 2 1 16 13 1 ( 0 . 0 ) 13 1 ( 1 . 0 ) 4 13 1 ( 0 . 0 ) 2 1 16 13 1 ( 1 . 0 ) 4 9 ) 9 ) 5 ) 13 3 ( 1 ( 0 . 0 ) 5 ) 7 4 21 ) // arguments ( 3 3 ) // result 61