This is the code for lc
, a lambda calculus interpreter.
lc
does normal order (leftmost-outermost first) beta and eta reductions.
lc
will rename bound variables to prevent variable capture.
I did not do GNU-style autoconf
scripts. I did write reasonably strict
ANSI C (C89/C90 version, I hope) that compiles under a number of compilers
and operating systems.
To build the lc
executable:
make gnu # should work on most linuxes that have devel environment
make cc # should work on most *BSDs, uses traditionally named tools
make pcc # Uses the "pcc" C compiler
make lcc # Uses the "lcc" C compiler
make tcc # Uses the "tcc" C compiler
Once that finishes (and it only takes a few seconds), you can do:
./runtests # to ensure that all the tests pass.
lc
is a command line, interactive program. It does not have any implicit
startup files, or location or directory dependencies. You can cp or mv
the lc
executable anwhere in your PATH
, or leave it somewhere and call it
explicitly:
% $HOME/src/lc-1.0/lc
LC>
lc
uses LC>
as its prompt for interactive input.
You have to use Control-D (end-of-file) to get it to exit cleanly. It does not interpreter a special "exit" or "quit" command.
Long-running or patience-exhausting reductions can be terminated with
Control-C. The LC>
prompt should return. Control-C'ing lc
at the LC>
prompt will cause it to exit.
Variables, bound or free, look like C or Java identifiers: start with a letter, contain letters, digits or underscores.
The lambda-character prints as '%', but the user can use '%', '$', '^' or '\'. The program reads and writes ASCII, so a Unicode lambda won't work.
Binding sites ("\x") get seperated from an abstraction body by "." or "->".
\x.x x x
is the same as $x -> x x x
.
Hopefully, this allows some compatibility with other lambda calculators floating around the net.
Alpha equivalence: term1 = term2
Total lexical equivalence: term1 == term2
define identifier lambdaterm
def identifier lambdaterm
$$
Using a previously-defined identifier in a new lambda term causes the interpreter to put a copy of the term's definition in the new term. Be careful. Abstractions can "capture" identifiers in the abbreviation that lexically match the bound variable.
The special token $$
always represents the last normal form calculated.
It changes every time something redues to a normal form. The user can
put $$
in the next term input as many times as desired. Copies of the
last normal form get inserted.
define identifier *term
define identifier term1 *term2
define identifier *term1 *term2
Marked subterms of the term denoted by identifier can get expanded.
When the interpreter sees a use of an abbreviation like identifier{N}
(where N is greater than zero) it expands the marked terms N times.
This feature can be used to make Church numerals more convenient.
LC> def C0 \f n.n
LC> def C \f n.*f n
LC> print C{2}
%f.%n.f (f n)
Print elapsed time of any reduction to normal form, default off:
timer on
timer off
Perform eta reductions, default on:
eta on
eta off
Print information about each redex found and performed:
trace on
trace off
Require user to hit return after each reduction:
step on
step off
Read in and evaluate a file full of lc
input:
load "some/filename"