λ-calculus, but slow explicit usage instructions --------------------------- 1. build $ make $ make install # <- this is optional, the binary will show up in $PWD, it'll be called ./eru 2. run examples (see "included examples" part of readme) $ ./eru t/bazinga.l $ ./eru t/fac.l $ ./eru t/iota.l syntax ------ - a λ abstraction: λarg.body or \arg.body - a function application (func arg) - a symbolic value x - environment binding of a λ-abstraction or a value to a name name ← value or name <- value A program is a mixture of all of those. The value bound to the name `output' is reduced and printed to the standard output. You can use the -v (verbose) flag to show the number of passes used. You can use the -vv (very verbose) flag to show every single step of reduction. Files in lib/ are compiled into the `eru' binary, and can be later included in your program with .include basename.l So, lib/cons.l would be included with .include cons.l The .include directive will try to include a file from your computer, when a given filename is not compiled in. included examples (see t/) -------------------------- 1. bazinga bazinga ← λx.λy.x output ← ((bazinga mrow) boo) 1.1 usage $ ./eru -v t/bazinga.l Reduced in 5 passes, 4 substitutions. 144 bytes allocated. 7 symbols used. mrow 2. self-referencing factorial using Y combinator .include comb.l succ ← λn.λf.λx.(f ((n f) x)) pred ← λn.λf.λx.(((n λg.λh.(h (g f))) λ_.x) I) 0 ← λf.λx.x 1 ← (succ 0) 4 ← (succ (succ (succ 1))) zero? ← λn.((n λx.false) true) * ← λm.λn.((m (+ n)) 0) + ← λm.λn.((m succ) n) fac ← (Y λf.λn.(((zero? n) 1) ((* n) (f (pred n))))) output ← (fac 4) 2.1 usage $ ./eru -v t/fac.l Reduced in 298 passes, 10264 substitutions. 83394720 bytes allocated. 32 symbols used. λf.λx.(f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f x)))))))))))))))))))))))) 3. almost a iota w/ a stupid hack to overcome the naivity of the reducer .include eru.l rev ← (Y λrev.λl.(((null? (cdr l)) ((cons (car l)) nil)) ((append' (rev (cdr l))) (car l)))) append' ← (Y λappend'.λl.λel.(((null? l) ((cons el) nil)) ((cons (car l)) ((append' (cdr l)) el)))) riota ← (Y λriota.λx.(((zero? x) nil) ((cons x) (riota (pred x))))) output ← (car (rev #(riota 3))) ;; ^- here's the hack ;; it evaluates the riota in-place there when parsing the source ;; because (rev)-ing at the same time makes the reducer go boom 3.1 usage $ ./eru t/iota.l λf.λx.(f x) obligatory self-deprecation & warnings -------------------------------------- - the parser sucks. i'll fix it one day. a ← b ; this is also okay a <- b ; this is also also okay a arrow b ; but this is not a ← b ; ^- this whitespace is the reason ; can you spot the difference? the parser can (this is not a curried application over `this'. it's a syntax error. please beware) ((((while this) is) perfectly) okay) ((i'm so) sorry)