Skip to content

Commit 222b9d6

Browse files
committed
about to publish
1 parent 36dc44e commit 222b9d6

14 files changed

+1408
-2
lines changed

Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "lambdascript"
3+
version = "0.1.0"
4+
authors = ["Chuck Liang"]
5+
edition = "2021"
6+
license = "MIT"
7+
description = "Educational tool illustrating beta reduction of untyped lambda terms, parser generation"
8+
repository = "https://github.com/chuckcscccl/lambdascript/"
9+
keywords = ["lambda-calculus", "lambda", "beta-reduction", "education"]
10+
categories = ["command-line-utilities", "parsing", "mathematics"]
11+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12+
13+
[dependencies]
14+
rustlr = "0.2"
15+
fixedstr = "0.1"
16+
chrono = "0.4"

README.md

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,91 @@
1-
# lambdascript
2-
educational tool illustrating beta reduction of untyped lamba terms, also uses rustlr parser generator
1+
## Lambdascript
2+
3+
**Lambdascript** executes beta-reduction steps on untyped lambda
4+
terms. It is not a high-performance implementation of lambda
5+
calculus. Rather, the tool serves three primary purposes, all of which
6+
are illustrational or educational in nature:
7+
8+
1. It demonstrates the usage of the **[rustlr](https://docs.rs/rustlr/latest/rustlr/index.html)** *parser generator*. The LALR(1) grammar for lambdascript in rustlr format
9+
is given [here](https://cs.hofstra.edu/~cscccl/rustlr_project/lambdascript/untyped.grammar).
10+
11+
2. For introductory level students in a programming languages class, the
12+
tools shows every step of beta reduction, including alpha-conversions where
13+
necessary, in reducing a term to normal form. It includes both full
14+
beta-normalization using the normal order (call-by-name) strategy as well as
15+
weak-head normalization using call-by-value. Definitions can be given
16+
for terms such as S, K, I.
17+
18+
3. For more advanced students, the source code of the program demonstrates
19+
how lambda terms can be represented in abstract syntax and how
20+
reductions can be implemented.
21+
22+
### Usage
23+
The program was written in Rust and should be installed as an executable: **`cargo install lambdascript`**. You must have Rust installed (from <https://rust-lang.org>) to execute the cargo command.
24+
25+
The program can read from a script or interactively read from stdin. Expressions and defintions are separated by ; (semicolon). Here's an example of reading and evaluating from stdin, which can be initiated by running the executable.
26+
27+
```
28+
<<< (lambda x.x (lambda y.x y)) y;
29+
(λx.x (λy.x y)) y
30+
=> y (λy1.y y1)
31+
```
32+
Lambdascript uses standard syntax for lambda terms: application associates to
33+
the left and application binds tighter than abstraction, meaning that the
34+
scope of a λ extends to the right as far as possible unless bounded by
35+
parentheses. Lambda expressions inside applications must always by bound
36+
by parentheses: so `x lambda y.y` should be replaced with `x (lambda y.y)`.
37+
38+
Given a file [simple.ls](https://cs.hofstra.edu/~cscccl/rustlr_project/lambdascript/simple.ls) with the following contents:
39+
```
40+
define I = lambda x.x;
41+
define K = lambda x.lambda y.x;
42+
define lazy INFINITY = (lambda x.x x) (lambda x.x x);
43+
44+
K I INFINITY x;
45+
```
46+
**`lambdascript simple.ls`** produces the following output:
47+
```
48+
K I INFINITY x
49+
= (λxλy.x) I INFINITY x
50+
=> (λy.I) INFINITY x
51+
= (λyλx.x) INFINITY x
52+
=> (λx.x) x
53+
=> x
54+
```
55+
The reduction terminated because normal-order (call-by-name)
56+
evaluation is applied by default. If the
57+
the last line of the file was replaced with `weak (K I INFINITY x)`, then
58+
weak-head reduction using call-by-value will take place,
59+
resulting in an infinite loop. There will likewise be an infinite loop if
60+
`lazy` was missing from the definition of `INFINITY`. Full, normal-order
61+
evaluation and weak-head call-by-value are the only reduction strategies
62+
implemented in lambdascript.
63+
64+
After a script is executed, the interpreter automatically enters interactive
65+
mode with the definitions from the script still available.
66+
67+
The file **[pure.ls](https://cs.hofstra.edu/~cscccl/rustlr_project/lambdascript/pure.ls)** contains a full list of definitions of well-known lambda-calculus
68+
combinators.
69+
70+
#### Interactive Interpreter Directives
71+
72+
At the `<<<` prompt the following special directives can be given:
73+
74+
* `exit` or `quit`: exists the program
75+
* `use lambda` or `use lam` or `use Lam` or `use \`: On some systems,
76+
the Greek character λ (unicode 0x03BB) may fail to display properly.
77+
To change the symbol displayed for lambda, you can choose between one
78+
of four alternatives (the choices are limited to these four because the
79+
symbol must be a statically allocated string).
80+
* `use greek` or `use unicode`: reverts to displaying λ, which is the default
81+
* `trace off`: turns off the displaying of intermediate reduction steps: only the initial term and the final normal form are shown
82+
* `trace medium`: Beta-reduction steps are shown, but not the expansion
83+
of defintions nor alpha-conversion
84+
* `trace on` or `trace max`: restore displaying of all steps: this is the
85+
default
86+
87+
-----------------------------
88+
89+
As this tool is used actively in the classroom, each release will have
90+
a **limited lifetime**: after a certain period it will cease to work until
91+
a new version is released.

combinators.ls

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Programming with pure lambda terms, with full beta and weak head reduction
2+
// default define means reduce defined term to weak-head normal form.
3+
4+
define I = lambda x.x;
5+
define K = lambda x.lambda y.x;
6+
define S = lambda x.lambda y.lambda z.(x z (y z));
7+
define True = K;
8+
define False = (K I);
9+
define IF = lambda b.lambda x.lambda y.(b x y);
10+
define NOT = lambda b.(IF b False True);
11+
define OR = lambda a.lambda b.(IF a True b);
12+
define AND = lambda a.lambda b.(IF a b False);
13+
define CONS = lambda a.lambda b.lambda c.(IF c a b);
14+
define CAR = lambda c.(c True);
15+
define CDR = lambda c.(c False);
16+
define lazy FIX = lambda M.((lambda x.(M (x x))) (lambda y.(M (y y))));
17+
define Zero = False;
18+
define One = lambda f.lambda x.(f x);
19+
define PLUS = lambda m.lambda n.lambda f.lambda x.(m f (n f x));
20+
define TIMES = lambda m.lambda n.lambda f.lambda x.(m (n f) x);
21+
//define lazy INFINITY = (lambda x.(x x)) (lambda x.(x x));
22+
23+
define NULL = False;
24+
define M = (CONS 2 (CONS 3 (CONS 5 (CONS 7 NULL))));
25+
26+
(K 1); // lambda y.1
27+
(K 1 2); // 1
28+
(K I); // lambda x.lambda y.y
29+
(K I 2); // lambda y.y
30+
(K I 2 3); // 3
31+
//weak (S K I); // weak head normal form
32+
(S K I); // full normal form
33+
(S K I I); // lambda x.x
34+
(S K I 0); // 0
35+
36+
// booleans and if-else
37+
(IF (AND (NOT False) True) 1 2); // 1
38+
39+
// linked lists:
40+
(CAR (CDR (CDR M))); // 5
41+
42+
// arithmetic
43+
(PLUS One One); // 2 in church numeral
44+
45+
(TIMES (PLUS One One) Zero 1 0); // 0: 2*0=0!
46+
47+
// static scoping:
48+
let x = 1 in
49+
(let f = lambda y.x in
50+
(let x = 2 in (f 0))); // 1, since lambda calc implies static scoping.
51+
// if 2 was printed, then it's dynamically scoped
52+
// and something went horribly wrong.

hw1.ls

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Sample Solutions to Lambda Calculus Homework in Lambdascript.
2+
3+
// 2.
4+
2; // 2 is evaluated into 2, which is already a normal form
5+
(lambda x.lambda y. (y x)) u (lambda x.x);
6+
7+
8+
// 3. Basic combinators:
9+
3;
10+
define I = lambda x.x;
11+
define K = lambda x.lambda y.x;
12+
define S = lambda x.lambda y.lambda z.x z (y z);
13+
14+
K I I;
15+
S I K;
16+
S K (K I);
17+
S (K I);
18+
19+
// note that the lambdascript program does call-by-name by default.
20+
weak (S (K I)); // this does "weak head reduction using call-by-value"
21+
22+
// 4. Arithmetic
23+
4;
24+
define ZERO = K I;
25+
define ONE = lambda f.lambda x.f x;
26+
define PLUS = lambda m.lambda n.lambda f.lambda x.m f (n f x);
27+
define TIMES = lambda m.lambda n.lambda f.lambda x.m (n f) x;
28+
define TWO = PLUS ONE ONE;
29+
30+
PLUS TWO ONE;
31+
TIMES ZERO TWO;
32+
33+
// 4b.
34+
4 b; // a space is needed otherwise there's parser error
35+
define EXPT = lambda m.lambda n.n m;
36+
define TREE = PLUS ONE TWO;
37+
38+
EXPT TREE TWO;
39+
40+
41+
// 5. Booleans and if-else
42+
5;
43+
define TRUE = K; // K A B = A
44+
define FALSE = K I; // (K I) A B = B
45+
define IF = lambda b.lambda x.lambda y.b x y;
46+
define NOT = lambda b.IF b FALSE TRUE;
47+
define OR = lambda a.lambda b.IF a TRUE b;
48+
define AND = lambda a.lambda b.IF a b FALSE;
49+
OR;
50+
OR FALSE FALSE;
51+
OR FALSE TRUE;
52+
OR TRUE FALSE;
53+
OR TRUE TRUE;
54+
55+
56+
6;
57+
define NOT = lambda n.n FALSE TRUE;
58+
NOT;
59+
NOT FALSE;
60+
NOT TRUE;
61+
NOT (NOT TRUE); // "no, no it can't be true!" but it is.
62+
63+
7;
64+
IF TRUE 1 2;
65+
IF FALSE 1 2;
66+
67+
68+
// Data structure (pairs, linked-list = nested pairs)
69+
define PAIR = lambda a.lambda b.lambda c.IF c a b; // called "CONS"
70+
define FST = lambda c.c TRUE; // first element of cons pair, "CAR"
71+
define SND = lambda c.c FALSE; // second element of cons pair, "CDR"
72+
define NIL = FALSE; // represents empty list
73+
define ISNIL = lambda p.p (lambda a.lambda b.lambda z.FALSE) TRUE;
74+
75+
8;
76+
define M = PAIR a (PAIR b c);
77+
M;
78+
FST M;
79+
SND M;
80+
SND (SND M);
81+
82+
9;
83+
define ISZERO = lambda n.n (lambda z.FALSE) TRUE;
84+
ISZERO;
85+
ISZERO (TIMES ONE ZERO);
86+
ISZERO (PLUS ONE ZERO);
87+
88+
10;
89+
still working on it;
90+
define lazy INFINITY = (lambda x.x x) (lambda x.x x);
91+
// without "lazy" it will go into an infinite loop
92+
93+
// weak (INFINITY); // uncomment at your own risk...

pure.ls

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Programming with pure lambda terms, with full beta-normalization via CBN.
2+
3+
// Baisc combinators:
4+
define I = lambda x.x;
5+
define K = lambda x.lambda y.x;
6+
define S = lambda x.lambda y.lambda z.x z (y z);
7+
8+
// Booleans and if-else
9+
define TRUE = lambda x.lambda y.x; // K A B = A
10+
define FALSE = lambda x.lambda y.y; // (K I) A B = B
11+
define IF = lambda b.lambda x.lambda y.b x y;
12+
define NOT = lambda b.IF b FALSE TRUE;
13+
define OR = lambda a.lambda b.IF a TRUE b;
14+
define AND = lambda a.lambda b.IF a b FALSE;
15+
16+
// Arithmetic
17+
define ZERO = lambda f.lambda x.x;
18+
define ISZERO = lambda n.n (lambda z.FALSE) TRUE;
19+
define ONE = lambda f.lambda x.f x;
20+
define PLUS = lambda m.lambda n.lambda f.lambda x.m f (n f x);
21+
define TIMES = lambda m.lambda n.lambda f.lambda x.m (n f) x;
22+
define PRED = lambda n.lambda f.lambda x.n (lambda g.lambda h.h (g f)) (lambda u.x) (lambda u.u);
23+
define SUBTRACT = lambda m.lambda n. n PRED m;
24+
define LEQ = lambda m.lambda n. ISZERO (SUBTRACT m n); // <=
25+
define EQUALS = lambda m.lambda n.AND (LEQ m n) (LEQ n m);
26+
LEQ ONE (PLUS ONE ONE); //true
27+
LEQ (PLUS ONE ONE) ONE; // false
28+
EQUALS ONE (TIMES ONE ONE); // true
29+
30+
// Data structure (pairs, linked-list = nested pairs)
31+
define CONS = lambda a.lambda b.lambda c.IF c a b; //pair constructor
32+
define CAR = lambda c.c TRUE; // first element of cons pair
33+
define CDR = lambda c.c FALSE; // second element of cons pair
34+
define NIL = FALSE; // represents empty list
35+
define ISNIL = lambda p.p (lambda a.lambda b.lambda z.FALSE) TRUE;
36+
37+
// Recursion (loops) and divergence.
38+
define lazy INFINITY = (lambda x.(x x)) (lambda x.(x x));
39+
define lazy FIX = lambda m.(lambda x.m (x x)) (lambda y.m (y y));
40+
41+
// A sample linked list
42+
define TWO = PLUS ONE ONE;
43+
define THREE = PLUS TWO ONE;
44+
define FIVE = PLUS TWO THREE;
45+
define P = (CONS TWO (CONS THREE (CONS FIVE NIL)));
46+
47+
SUBTRACT FIVE THREE;
48+
49+
I 1;
50+
K 1; // lambda y.1
51+
K 1 2; // 1
52+
K I; // lambda x.lambda y.y
53+
K I 2; // lambda y.y
54+
K I 2 3; // 3
55+
S K I;
56+
S I I;
57+
58+
IF (AND (NOT FALSE) TRUE) 1 2; // 1
59+
60+
// linked lists:
61+
CAR (CDR (CDR P)); // 5 // in oop languages, M.cdr().cdr().car()
62+
ISNIL NIL;
63+
ISNIL P;
64+
65+
// recursive function to sum all numbers in list L:
66+
define lazy SUM = FIX (lambda f.lambda L. IF (ISNIL L) ZERO (PLUS (CAR L) (f (CDR L))));
67+
68+
//SUM P; // reduces to church numeral 10 (do in interactive mode)
69+
70+
TIMES (PLUS ONE ONE) ZERO; // 2*0=0, 0 in church numeral is lambda f.lambda x.x
71+
72+
let x = ONE in (PLUS x x); // 2 in church numeral
73+
// the above is expanded by the parser to ((lambda x.PLUS x x) ONE)
74+
75+
// static scoping:
76+
let x = 1 in
77+
(let f = lambda y.x in
78+
(let x = 2 in (f 0))); // 1, since lambda calc implies static scoping.
79+
// if 2 was printed, then it's dynamically scoped
80+
// and something went horribly wrong.
81+
/* the above let-expression expands to:
82+
(λx.(λf.(λx.f 0) 2) (λy.x)) 1
83+
=> (λf.(λx.f 0) 2) (λy.1)
84+
=> (λx.(λy.1) 0) 2
85+
=> (λy.1) 0
86+
=> 1
87+
88+
lambda calculus version of C program:
89+
int x = 1;
90+
int f(int y) { return x; }
91+
int main() {
92+
int x = 2;
93+
return f(0);
94+
} // main returns 1 under static scoping, 2 under dynamic scoping.
95+
96+
C and virtually all languages are statically scoped.
97+
*/

simple.ls

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
define I = lambda x.x;
2+
define K = lambda x.lambda y.x;
3+
define lazy INFINITY = (lambda x.x x) (lambda x.x x);
4+
5+
K I INFINITY x;

0 commit comments

Comments
 (0)