├── .github └── workflows │ └── test.yml ├── LICENSE ├── Makefile ├── README.md ├── bin └── lambdavm-diagram.png ├── details.md ├── examples ├── fizzbuzz.cl ├── rot13.cl └── yes.cl ├── lambdavm.pdf ├── src ├── blc-clamb-wrapper.cl ├── blc-numbers.cl ├── blc-to-ski.cl ├── elvm-lazy.cl ├── lambda-asm-header.cl ├── lambdacraft.cl ├── lambdavm.cl ├── library.cl ├── main-blc.cl ├── main-lazy.cl ├── main-ulamb.lisp └── main.cl ├── test ├── fizzbuzz.cl.out ├── rot13.cl.in ├── rot13.cl.out └── yes.cl.out └── tools ├── main.tex └── make-latex.sh /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | test: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Run test 17 | run: | 18 | sudo apt install -y sbcl 19 | make testrot13 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Hikaru Ikuta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Binary lambda calculus 2 | BLC=./bin/Blc 3 | UNI=./bin/uni 4 | UNIPP=./bin/uni++ 5 | 6 | # Universal lambda 7 | CLAMB=./bin/clamb 8 | 9 | # Lazy K 10 | LAZYK=./bin/lazyk 11 | 12 | # Tools 13 | ASC2BIN=./bin/asc2bin 14 | LAM2BIN=./bin/lam2bin 15 | BLCAIT=./bin/blc-ait 16 | 17 | # Toolkit 18 | LAMBDATOOLS=./build/lambda-calculus-devkit 19 | 20 | # Other 21 | SBCL=sbcl 22 | LATEX=latex 23 | DVIPDFMX=dvipdfmx 24 | target_latex=out/lambdavm.tex 25 | target_pdf=lambdavm.pdf 26 | 27 | LAMBDAVM_SRCS=./src/lambdavm.cl ./src/lambdacraft.cl ./src/blc-numbers.cl ./src/blc-clamb-wrapper.cl 28 | 29 | 30 | 31 | all: $(addsuffix .blc, $(addprefix out/, $(notdir $(wildcard examples/*.cl)))) \ 32 | $(addsuffix .lazy, $(addprefix out/, $(notdir $(wildcard examples/*.cl)))) \ 33 | $(addsuffix .ulamb, $(addprefix out/, $(notdir $(wildcard examples/*.cl)))) 34 | 35 | test: test-blc test-ulamb test-lazyk test-blc-unipp 36 | testrot13: testrot13-blc testrot13-ulamb testrot13-lazyk testrot13-blc-unipp 37 | 38 | pdf: $(target_pdf) 39 | 40 | lambdavm.lam: $(BLCAIT) src/main.cl src/lambdavm.cl 41 | sbcl --script src/main.cl > out/lambdavm.lam.tmp 42 | $(BLCAIT) blc out/lambdavm.lam.tmp > out/lambdavm.lam.tmp.opt 43 | cat out/lambdavm.lam.tmp.opt | sbcl --script notes/blc-to-ski.cl -iblc -olambda > out/lambdavm.lam.tmp 44 | mv out/lambdavm.lam.tmp lambdavm.lam 45 | rm out/lambdavm.lam.tmp.opt 46 | 47 | lambdavm.blc: $(BLCAIT) src/main.cl src/lambdavm.cl 48 | sbcl --script src/main.cl > out/lambdavm.lam.tmp 49 | $(BLCAIT) blc out/lambdavm.lam.tmp > lambdavm.blc.tmp 50 | mv lambdavm.blc.tmp lambdavm.blc 51 | rm out/lambdavm.lam.tmp 52 | 53 | lambdavm.lazy: $(BLCAIT) src/main-lazy.cl src/lambdavm.cl src/blc-clamb-wrapper.cl 54 | sbcl --script src/main-lazy.cl > out/lambdavm.lazy.lam.tmp 55 | $(BLCAIT) blc out/lambdavm.lazy.lam.tmp > out/lambdavm.lazy.lam.tmp.opt 56 | cat out/lambdavm.lazy.lam.tmp.opt | sbcl --script notes/blc-to-ski.cl -iblc -oski > out/lambdavm.lazy.tmp 57 | cat out/lambdavm.lazy.tmp | sed -e 's/``s`kki/k/g' > lambdavm.lazy 58 | rm out/lambdavm.lazy.tmp out/lambdavm.lazy.lam.tmp 59 | 60 | #================================================================ 61 | # Build the PDF 62 | #================================================================ 63 | .PRECIOUS: $(target_latex) 64 | $(target_latex): $(LAMBDAVM_SRCS) ./src/main.cl ./tools/main.tex ./tools/make-latex.sh 65 | mkdir -p ./out 66 | ./tools/make-latex.sh 67 | mv lambdavm.tex out 68 | 69 | .PHONY: pdf 70 | $(target_pdf): $(target_latex) lambdavm.lam 71 | cp ./tools/main.tex out 72 | cd out; $(LATEX) main.tex 73 | cd out; $(DVIPDFMX) main.dvi -o lambdavm.pdf 74 | mv out/lambdavm.pdf . 75 | 76 | 77 | #================================================================ 78 | # Tests 79 | #================================================================ 80 | .PHONY: testrot13-% 81 | testrot13-%: out/rot13.cl.%-out.expected-diff 82 | @echo "\n All tests have passed for $(interpreter-name-$*).\n" 83 | 84 | .PHONY: test-% 85 | test-%: $(addsuffix .%-out.expected-diff, $(addprefix out/, $(notdir $(wildcard examples/*.cl)))) 86 | @echo "\n All tests have passed for $(interpreter-name-$*).\n" 87 | interpreter-name-blc="BLC with the interpreter 'uni'" 88 | interpreter-name-blc-blc="BLC with the interpreter 'Blc'" 89 | interpreter-name-blc-unipp="BLC with the interpreter 'uni++'" 90 | interpreter-name-ulamb="Universal Lambda" 91 | interpreter-name-lazyk="Lazy K" 92 | 93 | 94 | .PRECIOUS: out/%.blc 95 | out/%.blc: examples/% $(LAMBDAVM_SRCS) 96 | mkdir -p out 97 | $(SBCL) --script $< > $@ 98 | 99 | .PRECIOUS: out/%.ulamb 100 | out/%.ulamb: examples/% $(LAMBDAVM_SRCS) 101 | mkdir -p out 102 | ( printf '(defparameter **compile-ulamb** t)'; cat $< ) > $@.cl 103 | $(SBCL) --script $@.cl > $@ 104 | 105 | .PRECIOUS: out/%.lazy 106 | out/%.lazy: examples/% $(LAMBDAVM_SRCS) 107 | mkdir -p out 108 | ( printf '(defparameter **compile-lazy** t)'; cat $< ) > $@.cl 109 | $(SBCL) --script $@.cl > $@ 110 | 111 | .PRECIOUS: out/%.blc-blc-out 112 | out/%.blc-blc-out: out/%.blc $(BLC) $(ASC2BIN) 113 | if [ -f "test/$*.in" ]; then \ 114 | ( cat $< | $(ASC2BIN); cat test/$*.in ) | $(BLC) | head -n 20 > $@.tmp; else \ 115 | cat $< | $(ASC2BIN) | $(BLC) | head -n 20 > $@.tmp; fi 116 | mv $@.tmp $@ 117 | 118 | .PRECIOUS: out/%.blc-out 119 | out/%.blc-out: out/%.blc $(UNI) $(ASC2BIN) 120 | if [ -f "test/$*.in" ]; then \ 121 | ( cat $< | $(ASC2BIN); cat test/$*.in ) | $(UNI) | head -n 20 > $@.tmp; else \ 122 | cat $< | $(ASC2BIN) | $(UNI) | head -n 20 > $@.tmp; fi 123 | mv $@.tmp $@ 124 | 125 | .PRECIOUS: out/%.blc-unipp-out 126 | out/%.blc-unipp-out: out/%.blc $(UNIPP) $(ASC2BIN) 127 | if [ -f "test/$*.in" ]; then \ 128 | ( cat $< | $(ASC2BIN); cat test/$*.in ) | $(UNIPP) -o | head -n 20 > $@.tmp; else \ 129 | cat $< | $(ASC2BIN) | $(UNIPP) -o | head -n 20 > $@.tmp; fi 130 | mv $@.tmp $@ 131 | 132 | .PRECIOUS: out/%.ulamb-out 133 | out/%.ulamb-out: out/%.ulamb $(CLAMB) $(ASC2BIN) 134 | if [ -f "test/$*.in" ]; then \ 135 | ( cat $< | $(ASC2BIN); cat test/$*.in ) | $(CLAMB) -u | head -n 20 > $@.tmp; else \ 136 | cat $< | $(ASC2BIN) | $(CLAMB) -u | head -n 20 > $@.tmp; fi 137 | mv $@.tmp $@ 138 | 139 | .PRECIOUS: out/%.lazyk-out 140 | out/%.lazyk-out: out/%.lazy $(LAZYK) 141 | if [ -f "test/$*.in" ]; then \ 142 | cat test/$*.in | $(LAZYK) $< -u | head -n 20 > $@.tmp; else \ 143 | $(LAZYK) $< -u | head -n 20 > $@.tmp; fi 144 | mv $@.tmp $@ 145 | 146 | out/%.blc-out.expected-diff: ./out/%.blc-out ./test/%.out 147 | diff $^ || exit 1 148 | 149 | out/%.blc-blc-out.expected-diff: ./out/%.blc-blc-out ./test/%.out 150 | diff $^ || exit 1 151 | 152 | out/%.blc-unipp-out.expected-diff: ./out/%.blc-unipp-out ./test/%.out 153 | diff $^ || exit 1 154 | 155 | out/%.ulamb-out.expected-diff: ./out/%.ulamb-out ./test/%.out 156 | diff $^ || exit 1 157 | 158 | out/%.lazyk-out.expected-diff: ./out/%.lazyk-out ./test/%.out 159 | diff $^ || exit 1 160 | 161 | 162 | #================================================================ 163 | # Build the interpreters and tools 164 | #================================================================ 165 | $(LAMBDATOOLS): 166 | mkdir -p build 167 | cd build; git clone https://github.com/woodrush/lambda-calculus-devkit 168 | 169 | .PHONY: blc 170 | blc: $(BLC) 171 | $(BLC): $(LAMBDATOOLS) 172 | mkdir -p bin 173 | cd $(LAMBDATOOLS) && $(MAKE) blc && mv bin/Blc ../../bin 174 | 175 | .PHONY: uni 176 | uni: $(UNI) 177 | $(UNI): $(LAMBDATOOLS) 178 | mkdir -p bin 179 | cd $(LAMBDATOOLS) && $(MAKE) uni && cp bin/uni ../../bin 180 | 181 | .PHONY: uni++ 182 | uni++: $(UNIPP) 183 | $(UNIPP): $(LAMBDATOOLS) 184 | mkdir -p bin 185 | cd $(LAMBDATOOLS) && make uni++ && mv bin/uni++ ../../bin 186 | 187 | .PHONY: clamb 188 | clamb: $(CLAMB) 189 | $(CLAMB): $(LAMBDATOOLS) 190 | mkdir -p bin 191 | cd $(LAMBDATOOLS) && make clamb && mv bin/clamb ../../bin 192 | 193 | .PHONY: lazyk 194 | lazyk: $(LAZYK) 195 | $(LAZYK): $(LAMBDATOOLS) 196 | mkdir -p bin 197 | cd $(LAMBDATOOLS) && make lazyk && mv bin/lazyk ../../bin 198 | 199 | .PHONY: asc2bin 200 | asc2bin: $(ASC2BIN) 201 | $(ASC2BIN): $(LAMBDATOOLS) 202 | mkdir -p bin 203 | cd $(LAMBDATOOLS) && make asc2bin && mv bin/asc2bin ../../bin 204 | 205 | .PHONY: lam2bin 206 | lam2bin: $(LAM2BIN) 207 | $(LAM2BIN): $(LAMBDATOOLS) 208 | mkdir -p bin 209 | cd $(LAMBDATOOLS) && make lam2bin && mv bin/lam2bin ../../bin 210 | 211 | .PHONY: blc-ait 212 | blc-ait: $(BLCAIT) 213 | $(BLCAIT): $(LAMBDATOOLS) 214 | mkdir -p bin 215 | cd $(LAMBDATOOLS) && make blc-ait && mv bin/blc-ait ../../bin 216 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LambdaVM - A Programmable Virtual CPU Written as an Untyped Lambda Calculus Term 2 | 3 | [![test](https://github.com/woodrush/lambdavm/actions/workflows/test.yml/badge.svg)](https://github.com/woodrush/lambdavm/actions/workflows/test.yml) 4 | 5 | ![Lambda calculus diagram for LambdaVM.](bin/lambdavm-diagram.png) 6 | 7 | LambdaVM is a programmable virtual CPU written as a closed untyped lambda calculus term. 8 | It supports an extended version of the [ELVM](https://github.com/shinh/elvm) instruction set and architecture written by [Shinichiro Hamaji](https://github.com/shinh). 9 | Using LambdaVM, you can enjoy assembly programming to write programs in untyped lambda calculus. 10 | 11 | 12 | LambdaVM supports 8 instructions including standard I/O and virtual memory operations, and has an arbitrarily configurable ROM/RAM address size and word size, and an arbitrarily configurable number of registers. 13 | 14 | Despite its rather rich capability, LambdaVM's lambda term is quite small. Here is its entire lambda term written in plaintext: 15 | 16 | ```text 17 | LambdaVM = \x.\y.\z.\a.\b.((\c.((\d.((\e.((\f.((\g.((\h.(a ((\i.(i (d (\j.\k.(k 18 | (\l.\m.\n.\o.(o k (j m))) k)) a) (\j.(i z (d (\k.\l.\m.\n.\o.\p.((\q.((\r.((\s.( 19 | n (\t.\u.\v.\w.v) (\t.t) (\t.\u.\v.u) (\t.\u.u) (o (\t.\u.\v.(o (k l m) p)) o) ( 20 | n (\t.\u.((\v.(t (\w.\A.\B.((\C.(A (C B) (s B C))) (\C.\D.(w (D ((\E.(m (\F.\G.\ 21 | H.(E (y (\I.\J.(J (\K.\L.K) I)) F) G)) (E c m))) (\E.\F.(r B E (k l F u o)))) (\ 22 | E.(E (y (\F.(F (\G.\H.H))) C) (v p))) A) (D (\E.\F.\G.\H.((\I.(F (I G) (s G I))) 23 | (s H (\I.\J.(E (e I C) (q J) (v p))))))) (D (\E.\F.((\G.(f (\H.\I.I) (E (s F e 24 | C)) G G (\H.(r F)))) c)) v) (q C) (h l C (r D) v) (s D (g l C) k m u o p) (D (\E 25 | .\F.(s E (f F F) C (\G.(r E)))) v) (r D C v))))))) (k l m u o)))))) (h p))) (g p 26 | ))) (\q.(h j q (\r.(r (k l m) p))))))))))) (\i.\j.(d (\k.\l.\m.\n.(l (\o.\p.\q.( 27 | m (\r.\s.\t.(k l s (\u.\v.(k v s (\w.(n (\A.(A u w)))))))) (l n))) (n l l))) i c 28 | (\k.\l.(j k)))) b) (\i.\j.j))) (d (\h.\i.\j.\k.(i (\l.\m.\n.(j (\o.\p.\q.(o (h 29 | l) (h m) p k)) (k i))) (k c)))))) (d (\g.\h.\i.\j.\k.(i (\l.\m.\n.((\o.(h (\p.\q 30 | .\r.(l (h o) (o q p))) (o (\p.\q.q) (\p.\q.q)))) (\o.(g o m j (\p.\q.(l (k (\r.( 31 | r p q))) (k (\r.(r q p))))))))) (k j)))))) (d (\f.\g.\h.\i.\j.\k.(i (\l.\m.\n.(j 32 | (\o.\p.(f g h m p (\q.\r.((\s.((\t.((\u.((\v.(t s q (v (\w.\A.w)) (v (\w.\A.A)) 33 | )) (t q (q (\v.\w.w) (\v.\w.v)) (u (\v.\w.v)) (u (\v.\w.w))))) (\u.\v.(k v (\w.( 34 | w u r)))))) (\t.\u.(l (s t u) (s u t))))) (h o (o (\s.\t.t) (\s.\t.s))))))))) (k 35 | g i)))))) (d (\e.\f.\g.(f (\h.\i.\j.(g (\k.\l.((\m.(h (k m (\n.\o.\p.o)) (k (\n 36 | .\o.\p.p) m))) (e i l))))) (\h.\i.\j.h)))))) (\d.((\e.(d (e e))) (\e.(d (e e)))) 37 | ))) ((\c.(y c (x c (\d.\e.e)))) (\c.\d.(d (\e.\f.e) c)))) 38 | ``` 39 | 40 | Shown here is a lambda calculus term featuring a RAM unit with 8 instructions including I/O and memory operations. 41 | With LambdaVM, you can write an assembly program that runs on this virtual machine. 42 | 43 | The image on the top of this repo is LambdaVM's [lambda calculus diagram](http://tromp.github.io/cl/diagrams.html). 44 | 45 | Various designs for LambdaVM are borrowed from [Kunihiko Sakamoto](https://github.com/irori)'s [UnlambdaVM](https://irori.hatenablog.com/entry/elvm-unlambda-part2) (in Japanese), with many modifications. Details are described later. 46 | 47 | LambdaVM is built using [LambdaCraft](https://github.com/woodrush/lambdacraft), a Common Lisp DSL that I wrote for building large lambda calculus programs, also used to build [LambdaLisp](https://github.com/woodrush/lambdalisp), a Lisp interpreter implemented in untyped lambda calculus. 48 | 49 | 50 | ## Overview 51 | ### Lambda Calculus as a Programming Language 52 | Using LambdaVM, you can write lambda calculus programs in assembly style. 53 | But what does it mean to write programs in lambda calculus? 54 | 55 | Lambda calculus terms can be interpreted as programs, by interpreting it as a program that takes an input string and returns an output string. 56 | Characters and bytes are encoded as a list of bits with $0 = \lambda x. \lambda y.x$, $1 = \lambda x. \lambda y.y$, 57 | and lists are encoded in the [Scott encoding](https://en.wikipedia.org/wiki/Mogensen%E2%80%93Scott_encoding) with ${\rm cons} = \lambda x.\lambda y.\lambda f.(f x y)$, ${\rm nil} = \lambda x.\lambda y.y$. 58 | This way, _everything_ in the computation process, even including integers, is expressed as pure lambda terms, 59 | without the need of introducing any non-lambda type object whatsoever. 60 | 61 | Various lambda calculus interpreters automatically handle this I/O format so that it runs on the terminal - standard input is encoded into lambda terms, and the output lambda term is decoded and shown on the terminal. 62 | Using these interpreters, lambda calculus programs can be run on the terminal just like any other terminal utility with I/O. 63 | 64 | A thorough explanation of programming in lambda calculus is described in [my blog post](https://woodrush.github.io/blog/lambdalisp.html) about [LambdaLisp](https://github.com/woodrush/lambdalisp), a Lisp interpreter written as an untyped lambda calculus term. 65 | 66 | 67 | ### Programming Languages Based on Lambda Calculus 68 | There are various programming languages that use this I/O strategy. Examples are: 69 | 70 | - [Binary lambda calculus](https://tromp.github.io/cl/Binary_lambda_calculus.html) 71 | - [Universal Lambda](https://esolangs.org/wiki/Universal_Lambda) 72 | - [Lazy K](https://tromp.github.io/cl/lazy-k.html) 73 | 74 | Seen as a programming language, these languages are purely functional languages with lazy evaluation. 75 | The "purely" part is emphasized even more than languages such as Haskell, since no primitive data types such as integers exist except for lambdas exist in these languages. 76 | Each of these languages have a different I/O encoding and a different notation for expressing lambda calculus terms. 77 | The I/O encoding can be absorbed by using a [wrapper](https://github.com/woodrush/lambdavm/blob/main/src/blc-clamb-wrapper.cl), 78 | and the notation can be adapted by using a [compiler](https://github.com/woodrush/lambdacraft) to write lambdas in the expected format. 79 | 80 | Using LambdaVM, you can write programs for these languages. 81 | A FizzBuzz program written in LambdaVM assembly looks like [./examples/fizzbuzz.cl](https://github.com/woodrush/lambdavm/blob/main/examples/fizzbuzz.cl). 82 | 83 | Compiled programs can be run on the terminal using various interpreters, including: 84 | 85 | - SectorLambda, the [521-byte lambda calculus interpreter](https://justine.lol/lambda/) written by Justine Tunney 86 | - The [IOCCC](https://www.ioccc.org/) 2012 ["Most functional"](https://www.ioccc.org/2012/tromp/hint.html) interpreter written by John Tromp 87 | (the [source](https://www.ioccc.org/2012/tromp/tromp.c) is in the shape of a λ) 88 | - Universal Lambda interpreter [clamb](https://github.com/irori/clamb) and Lazy K interpreter [lazyk](https://github.com/irori/lazyk) written by Kunihiko Sakamoto 89 | 90 | 91 | ### Compiling C to Lambda Calculus 92 | I have integrated LambdaVM into ELVM to implement ELVM's lambda calculus backend. 93 | Using this backend, you can even compile interactive C code to LambdaVM's assembly. 94 | Since ELVM implements its [own libc](https://github.com/shinh/elvm/tree/master/libc), you can even `#include ` and use library functions such as `printf` and `scanf`. 95 | 96 | This is entirely contained in the realm of the ELVM repository. Please see the documentation for ELVM for details. 97 | Instead of compiling C to assembly, you can enjoy hand-assembling programs for lambda calculus using the examples in this repository. 98 | 99 | 100 | 101 | ## Features 102 | - Instruction set: 103 | - `mov` `load` `store` `addsub` `cmp` `jmpcmp` `jmp` `putchar` `getchar` `exit` 104 | - The behavior of each instruction is mostly the same as ELVM. Please see [elvm.md](https://github.com/shinh/elvm/blob/master/ELVM.md) from the ELVM repo for details. 105 | - The lambda calculus data structure for each instruction is described later. 106 | - ROM/RAM address size and word size: 107 | - Pre-configurable to an arbitrary integer 108 | - I/O bit size: 109 | - Pre-configurable to an arbitrary integer 110 | - Registers: 111 | - Word size is pre-configurable to an arbitrary integer 112 | - Number of registers is arbitrarily pre-configurable 113 | 114 | 115 | ## Usage 116 | ### Hand-Assembling Your Own LambdaVM Programs 117 | You can hand-assemble your own LambdaVM programs using [LambdaCraft](https://github.com/woodrush/lambdacraft), 118 | a Common Lisp DSL I wrote for building lambda calculus programs, also used to build [LambdaLisp](https://github.com/woodrush/lambdalisp). 119 | 120 | The [examples](https://github.com/woodrush/lambdavm/tree/main/examples) directory in this repo contains 3 example LambdaVM assembly programs: 121 | 122 | - [fizzbuzz.cl](https://github.com/woodrush/lambdavm/blob/main/examples/fizzbuzz.cl): Prints the FizzBuzz sequence in unary. 123 | - [rot13.cl](https://github.com/woodrush/lambdavm/blob/main/examples/rot13.cl): Encodes/decodes standard input to/from the [ROT13](https://en.wikipedia.org/wiki/ROT13) cipher. 124 | - [yes.cl](https://github.com/woodrush/lambdavm/blob/main/examples/yes.cl): The Unix `yes` command, printing infinite lines of `y`. 125 | 126 | Here is what the beginning of the assembly listing for rot13.cl looks like: 127 | 128 | ```lisp 129 | (def-lazy asm (list 130 | ;; Initialization (PC == 0) 131 | (list 132 | ;; Store 26/2 = 13 at reg-B 133 | (mov reg-B "N") 134 | (sub reg-B "A") 135 | ) 136 | ;; tag-main (PC == 1) 137 | (list 138 | (getc reg-A) 139 | 140 | ;; Exit at EOF 141 | (jmpcmp reg-A == EOF -> tag-exit) 142 | 143 | ;; "a" <= reg-A < "n" : add 13 144 | (mov reg-C reg-A) 145 | (cmp reg-C >= "a") 146 | (mov reg-D reg-A) 147 | (cmp reg-D < "n") 148 | (add reg-C reg-D) 149 | (jmpcmp reg-C == int-2 -> tag-plus13) 150 | ... 151 | ``` 152 | 153 | As shown here, the assembly is written as Common Lisp macros. 154 | These listings can be compiled by running *.cl on a Common Lisp interpreter such as SBCL. 155 | 156 | Since these programs are based on LambdaCraft and LambdaCraft runs on LambdaLisp, 157 | it is expected that these programs run on LambdaLisp as well, although it takes a lot of time compared to fast interpreters such as SBCL. 158 | 159 | Please use these example programs as a template for hand-assembling your own LambdaVM programs. 160 | 161 | 162 | 163 | ## Specifications 164 | LambdaVM is written as the following lambda calculus term: 165 | 166 | `LambdaVM = \iobitsize. \suppbitsize. \proglist. \memlist. \stdin. ...` 167 | 168 | - The first 2 arguments ${\rm iobitsize}$ and ${\rm suppbitsize}$ are configuration parameters specifying the CPU's I/O word size and RAM word size. 169 | - ${\rm proglist}$ represents the assembly listing to be executed. 170 | - ${\rm memlist}$ represents the memory initialization state. Unspecified memory regions are initialized to 0. 171 | - ${\rm stdin}$ is the input string provided by the interpreter. 172 | 173 | By applying the first 4 arguments except ${\rm stdin}$ to ${\rm LambdaVM}$, the combined lambda term 174 | `(LambdaVM iobitsize suppbitsize proglist memlist)` behaves as a lambda calculus program that accepts a string 175 | ${\rm stdin}$, processes it, and returns some string. 176 | 177 | 178 | ### Implementation Design 179 | Various designs for LambdaVM are borrowed from [Kunihiko Sakamoto](https://github.com/irori)'s [UnlambdaVM](https://irori.hatenablog.com/entry/elvm-unlambda-part2) (in Japanese): 180 | 181 | - Using a binary tree structure to represent the RAM 182 | - Using a list of lists of instructions to represent the program 183 | 184 | LambdaVM has the following differences: 185 | - While Unlambda is a strictly evaluated language, LambdaVM assumes a lazily evaluated language. 186 | While UnlambdaVM is written in direct style using function applications to mutate the VM's global state, 187 | LambdaVM is written using continuation-passing style to handle monadic I/O. 188 | - The binary tree structure is modified so that an empty tree can be initialized with `nil = \x.\y.y`. 189 | 190 | 191 | ### Standard Input and Output 192 | By loading the program and initialization configurations to LambdaVM, the resulting lambda calculus term `(LambdaVM iobitsize suppbitsize proglist memlist)` behaves as a function that accepts a string as an input and outputs a string. 193 | 194 | Here, characters and bytes are encoded as a list of bits with $0 = \lambda x. \lambda y.x$, $1 = \lambda x. \lambda y.y$, 195 | and lists are encoded in the [Scott encoding](https://en.wikipedia.org/wiki/Mogensen%E2%80%93Scott_encoding) with ${\rm cons} = \lambda x.\lambda y.\lambda f.(f x y)$, ${\rm nil} = \lambda x.\lambda y.y$. 196 | 197 | The variable ${\rm stdin}$, the last argument of ${\rm LambdaVM}$, is expected to be provided by the interpreter in this format. 198 | 199 | Various lambda calculus languages use a slightly different I/O encoding, such as using [Church numerals](https://en.wikipedia.org/wiki/Church_encoding) to express each character. 200 | Such differences can be absorbed by using a [wrapper](https://github.com/woodrush/lambdavm/blob/main/src/blc-clamb-wrapper.cl) that adapts the program to its surrounding environment. 201 | This is discussed later in the [Adaptation to Different Languages](#adaptation-to-different-languages) section. 202 | 203 | 204 | ### iobitsize and suppbitsize 205 | `iobitsize` and `suppbitsize` are integers encoded as lambda terms in the [Church encoding](https://en.wikipedia.org/wiki/Church_encoding). 206 | 207 | `iobitsize` specifies the number of bits used for the input string. 208 | In the [IOCCC](https://www.ioccc.org/) 2012 ["Most functional"](https://www.ioccc.org/2012/tromp/hint.html) interpreter, 209 | `iobitsize == 8` since it uses 8 bits for encoding the I/O. 210 | 211 | `suppbitsize` represents the additional number of bits added to `iobitsize` to make the machine's RAM and register word size. 212 | The word size becomes `iobitsize + suppbitsize`. 213 | 214 | In ELVM, the machine word size is 24 and the I/O bit size is 8, so `iobitsize` and `suppbitsize` are set to 8 and 16, respectively. 215 | 216 | ### proglist 217 | `proglist` is represented as a list of lists, where each sublist is a _tag_ containing a list of instructions. 218 | The instruction format is described later. 219 | The beginning of each list represents a tag that can be jumped to using the `jmp` or `jmpcmp` instructions. 220 | When the `jmp` or `jmpcmp` instruction is run, the program proceeds to the beginning of the specified tag. 221 | 222 | ### memlist 223 | `memlist` is represented as a list of N-bit unsigned integers with the machine's word size, where each integer is represented as a list of bits with 224 | $0 = \lambda x. \lambda y.x$ and $1 = \lambda x. \lambda y.y$. 225 | The elements of each list are assigned to contiguous RAM addresses startting from the address zero. 226 | The rest of the memory is initiliazed with the integer zero. 227 | 228 | ## Instruction Structure 229 | Each instruction ${\rm inst}$ is a 4-tuple containing 4 arguments: 230 | 231 | $$ 232 | {\rm inst} = {\rm cons4} ~ {\rm insttag} ~ {\rm srcisimm} ~ {\rm src} ~ {\rm opt} 233 | $$ 234 | 235 | - ${\rm cons4}$ is a 4-tuple constructor, ${\rm cons4} = \lambda x. \lambda y. \lambda z. \lambda w. \lambda f. (f x y z w)$. 236 | - ${\rm insttag}$ is an 8-tuple specifying the instruction. 237 | - ${\rm srcisimm}$ is a boolean of either ${\rm t} = \lambda x. \lambda y. y$ or ${\rm nil} = \lambda x. \lambda y. y$. 238 | It specifies whether if the following ${\rm src}$ is an immediate value or a register. 239 | - ${\rm src}$ is either an N-bit integer or a register address. 240 | - ${\rm opt}$ is an instruction-specific option with an instruction-specific data structure. 241 | 242 | ### insttag and opt 243 | ${\rm insttag}$ is used to specify the instruction type: 244 | 245 | | Instruction | ${\rm insttag}$ | 246 | |-------------|---------------------------------------------------------------------------------------------| 247 | | `mov` | $\lambda a. \lambda b. \lambda c. \lambda d. \lambda e. \lambda f. \lambda g. \lambda h. a$ | 248 | | `load` | $\lambda a. \lambda b. \lambda c. \lambda d. \lambda e. \lambda f. \lambda g. \lambda h. b$ | 249 | | `store` | $\lambda a. \lambda b. \lambda c. \lambda d. \lambda e. \lambda f. \lambda g. \lambda h. c$ | 250 | | `addsub` | $\lambda a. \lambda b. \lambda c. \lambda d. \lambda e. \lambda f. \lambda g. \lambda h. d$ | 251 | | `cmp` | $\lambda a. \lambda b. \lambda c. \lambda d. \lambda e. \lambda f. \lambda g. \lambda h. e$ | 252 | | `jmpcmp` | $\lambda a. \lambda b. \lambda c. \lambda d. \lambda e. \lambda f. \lambda g. \lambda h. f$ | 253 | | `jmp` | $\lambda a. \lambda b. \lambda c. \lambda d. \lambda e. \lambda f. \lambda g. \lambda h. g$ | 254 | | `io` | $\lambda a. \lambda b. \lambda c. \lambda d. \lambda e. \lambda f. \lambda g. \lambda h. h$ | 255 | 256 | | Instruction | ${\rm opt}$ | 257 | |-------------|------------------------------------------------------------------------| 258 | | `mov` | ${\rm dst}$ | 259 | | `load` | ${\rm dst}$ | 260 | | `store` | ${\rm dst}$ | 261 | | `addsub` | ${\rm cons} ~ {\rm dst} ~ {\rm isadd}$ | 262 | | `cmp` | ${\rm cons} ~ {\rm enumcmp} ~ {\rm dst}$ | 263 | | `jmpcmp` | ${\rm cons4} ~ {\rm enumcmp} ~ {\rm jmpisimm} ~ {\rm jmp} ~ {\rm dst}$ | 264 | | `jmp` | (unused) | 265 | | `io` | ${\rm enumio}$ | 266 | 267 | 268 | ### Implementation in LambdaCraft 269 | The examples under [./examples](https://github.com/woodrush/lambdavm/tree/main/examples) wrap this instruction structure as Lisp macros 270 | so that they can be written in an assembly-like notation. 271 | Macros are defined in [./src/lambda-asm-header.cl](https://github.com/woodrush/lambdavm/blob/main/src/lambda-asm-header.cl). 272 | For further details on the instruction structure, please see this source. 273 | 274 | 275 | 276 | ## Adaptation to Different Languages 277 | As mentioned earlier, various lambda calculus languages use a slightly different I/O encoding, such as using [Church numerals](https://en.wikipedia.org/wiki/Church_encoding) to express each character. 278 | Such differences can be absorbed by using a [wrapper](https://github.com/woodrush/lambdavm/blob/main/src/blc-clamb-wrapper.cl) that adapts the program to its surrounding environment. 279 | 280 | To run a lambda program $F = \lambda s. {\rm code}$ that uses an I/O encoding of method A in a different environment that uses an I/O encoding B, 281 | you can wrap $F$ by writing a wrapper ${\rm AtoB}$ and ${\rm BtoA}$: 282 | 283 | $$ 284 | F' = \lambda s. ({\rm AtoB} ~ (F ~ ({\rm BtoA} ~ s))) 285 | $$ 286 | 287 | When $F'$ is run in the environment B, the input string $s$ is first encoded in the method of B. 288 | However, $F$ expects strings to be encoded in method A. 289 | This difference in the input is first absorbed by the wrapper ${\rm BtoA}$ which translates $s$ to a format recognizable by $F$. 290 | 291 | After calculuations, $F$ outputs a string in the method A. 292 | However, this string is not recognizable by the surrounding environment B. 293 | This difference in the output is then absorbed by the wrapper ${\rm AtoB}$ so that the interpreter recognizes the output by $F$. 294 | 295 | Using this strategy, any program $F$ compiled in a lambda-like language A can easily be transplied to $F'$ that runs on a different language B. 296 | The same holds for programs written using LambdaVM, making LambdaVM run in various lambda-based languages 297 | such as Binary Lambda Calculus, Universal Lambda, and Lazy K. 298 | The [wrapper](https://github.com/woodrush/lambdavm/blob/main/src/blc-clamb-wrapper.cl) used in LambdaVM is implemented exactly this way. 299 | 300 | ## Implementation Details 301 | Please see [details.md](details.md). 302 | 303 | 304 | ## Credits 305 | LambdaVM was written by Hikaru Ikuta, inspired by [Kunihiko Sakamoto](https://github.com/irori)'s [UnlambdaVM](https://irori.hatenablog.com/entry/elvm-unlambda-part2) (in Japanese). 306 | The instruction set for LambdaVM is based on and is extended from the [ELVM](https://github.com/shinh/elvm) architecture written by [Shinichiro Hamaji](https://github.com/shinh). 307 | LambdaVM is written using [LambdaCraft](https://github.com/woodrush/lambdacraft) written by Hikaru Ikuta. 308 | -------------------------------------------------------------------------------- /bin/lambdavm-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woodrush/lambdavm/8e469b4e60a1033842a061b2b3a1e675b4f7d23c/bin/lambdavm-diagram.png -------------------------------------------------------------------------------- /details.md: -------------------------------------------------------------------------------- 1 | ## Implementation Details 2 | Here I will explain further implementation details of LambdaVM. 3 | 4 | ### Binary Tree Structure for the RAM and the Registers 5 | The binary tree structure is the same as my other project [LambdaLisp](https://github.com/woodrush/lambdalisp), a Lisp interpreter written as an untyped lambda calculus term. 6 | Please see my [blog post for LambdaLisp](https://woodrush.github.io/blog/lambdalisp.html#virtual-heap-memory-ram) for details on the RAM data structure. 7 | 8 | As the same with LambdaLisp, the registers share the same binary tree structure as the RAM, except registers can have a variable-length address, while the RAM has a fixed-length address. 9 | 10 | 11 | ### Eliminating the Need for a Program Counter 12 | The core `eval` function keeps track of the current list of instructions in the current tag, and the current list of tags. 13 | When the current tag list becomes empty, it fetches a new tag from the current list of tags, 14 | and starts executing the first instruction from the fetched tag. 15 | Remember that each tag is a list of instructions, and the list of tags represent the rest of the program. 16 | LambdaVM stops its execution when no more tags are left in the current list of tags. 17 | By this design, LambdaVM does not need to keep track of an explicit program counter, since the current instruction and the next tag can always be fetched from the current tag and current list. 18 | 19 | 20 | ### Shared Data Structures for the ROM and the RAM 21 | The ROM and the RAM share the same binary tree structure. 22 | When an uninitialized ROM/RAM address is referenced, the lookup function returns a list filled with bits of 0s, `zero = (list 0 0 0 ... 0)`. 23 | 24 | Interpreted as a RAM lookup call, `zero` is naturally interpreted as the integer zero. 25 | Interpreted as a ROM lookup call, `zero` is interpreted as a list of N empty tags with `N = iobitsize + suppbitsize`. 26 | Here, a tag is a list of instructions representing each jumpable tag, 27 | and in this case every jumpable tag is empty. 28 | These empty tags are skipped, and eventually the list of tags becomes empty, exiting the execution of LambdaVM. 29 | This way, when an uninitialized ROM address is referenced, LambdaVM exits the program, which is the expected behavior since the VM was commanded to jump to a nonexistent program counter. 30 | 31 | By this design, the ROM and RAM are both able to share the same tree lookup function, where an empty reference returns the value `zero = (list 0 0 0 ... 0)`. 32 | This design helps making the lambda calculus term size of LambdaVM to be small. 33 | -------------------------------------------------------------------------------- /examples/fizzbuzz.cl: -------------------------------------------------------------------------------- 1 | (load "./src/lambda-asm-header.cl") 2 | 3 | 4 | ;; Define your own register addresses 5 | (def-lazy reg-A (list t t)) 6 | (def-lazy reg-B (list t nil)) 7 | (def-lazy reg-C (list nil t)) 8 | (def-lazy reg-D (list nil nil)) 9 | ;; Used for the macros in ./lambda-asm-header.cl, to determine if a value is an immediate or a register 10 | (defparameter regnames '(reg-A reg-B reg-C reg-D)) 11 | 12 | ;; Numbers are in big-endian, with t == 0, nil == 1 13 | ;; (Raw integer literals `0`, `1`, etc. are bound to different lambda forms in ./lambdacraft.cl) 14 | (def-lazy int-0 (list t t t t t t t t)) 15 | (def-lazy int-1 (list t t t t t t t nil)) 16 | (def-lazy int-2 (list t t t t t t nil t)) 17 | (def-lazy int-3 (list t t t t t t nil nil)) 18 | (def-lazy int-4 (list t t t t t nil t t)) 19 | (def-lazy int-5 (list t t t t t nil t nil)) 20 | (def-lazy int-6 (list t t t t t nil nil t)) 21 | (def-lazy int-7 (list t t t t t nil nil nil)) 22 | (def-lazy int-8 (list t t t t nil t t t)) 23 | (def-lazy int-11 (list t t t t nil t nil nil)) 24 | (def-lazy int-12 (list t t t t nil nil t t)) 25 | (def-lazy int-13 (list t t t t nil nil t nil)) 26 | (def-lazy int-14 (list t t t t nil nil nil t)) 27 | (def-lazy int-15 (list t t t t nil nil nil nil)) 28 | (def-lazy int-16 (list t t t nil t t t t)) 29 | 30 | ;; The memory is allocated contiguously, starting from address 0 (int-0). 31 | (def-lazy initial-memory (list "F" "i" "z" "z" int-0 "B" "u" "z" "z" int-0)) 32 | 33 | ;; Define constants 34 | (def-lazy *Fizz int-0) 35 | (def-lazy *Buzz int-5) 36 | (def-lazy n int-11) 37 | (def-lazy nmod3 int-12) 38 | (def-lazy nmod5 int-13) 39 | (def-lazy i int-14) 40 | (def-lazy j int-15) 41 | (def-lazy return-pc int-16) 42 | 43 | ;; Define tags for jump instructions 44 | (def-lazy tag-main int-1) 45 | (def-lazy tag-print-n-times int-2) 46 | (def-lazy tag-print-space int-3) 47 | (def-lazy tag-print-fizz int-4) 48 | (def-lazy tag-print-buzz int-5) 49 | (def-lazy tag-print-fizzbuzz int-6) 50 | (def-lazy tag-print-string int-7) 51 | (def-lazy tag-prepare-next-iter int-8) 52 | 53 | 54 | 55 | ;; The assembly is a list of lists of instructions. 56 | ;; Each sublist defines a chunk bound with a program counter, starting from PC == 0. 57 | ;; When a jump instruction is executed, the instruction pointer transitions to the first instruction of the given program counter. 58 | (def-lazy asm (list 59 | ;; Initialization (PC == 0) 60 | (list 61 | (mov reg-A int-1) 62 | (store n reg-A) ;; Set n = 1 63 | (store nmod3 reg-A) ;; Set nmod3 = 1 64 | (store nmod5 reg-A) ;; Set nmod5 = 1 65 | (store i reg-A) ;; Set i = 1 66 | ) 67 | ;; tag-main (PC == 1) 68 | (list 69 | ;; Check nmod3 and nmod5 70 | (load reg-A nmod3) 71 | (cmp reg-A == int-0) 72 | (load reg-B nmod5) 73 | (cmp reg-B == int-0) 74 | (mov reg-C reg-A) 75 | (add reg-C reg-B) 76 | 77 | ;; Print FizzBuzz if n === 0 mod 3 && n === 0 mod 5 78 | (jmpcmp reg-C == int-2 -> tag-print-fizzbuzz) 79 | 80 | ;; Print Fizz if n === 0 mod 3 81 | (jmpcmp reg-A == int-1 -> tag-print-fizz) 82 | 83 | ;; Print Buzz if n === 0 mod 5 84 | (jmpcmp reg-B == int-1 -> tag-print-buzz) 85 | 86 | ;; If none of the above, fall through to print-n-times 87 | ) 88 | ;; tag-print-n-times 89 | (list 90 | (load reg-A i) 91 | (jmpcmp reg-A == int-0 -> tag-prepare-next-iter) 92 | (putc "*") 93 | (sub reg-A int-1) 94 | (store i reg-A) 95 | (load reg-A j) 96 | (add reg-A int-1) 97 | (store j reg-A) ;; Increment j 98 | (jmpcmp reg-A == int-5 -> tag-print-space) 99 | (jmp tag-print-n-times) 100 | ) 101 | ;; tag-print-space 102 | (list 103 | (putc " ") 104 | (mov reg-A int-0) 105 | (store j reg-A) 106 | (jmp tag-print-n-times) 107 | ) 108 | ;; tag-print-fizz 109 | (list 110 | (mov reg-A *Fizz) 111 | (store i reg-A) 112 | (mov reg-A tag-prepare-next-iter) 113 | (store return-pc reg-A) 114 | (jmp tag-print-string) 115 | ) 116 | ;; tag-print-buzz 117 | (list 118 | (mov reg-A *Buzz) 119 | (store i reg-A) 120 | (mov reg-A tag-prepare-next-iter) 121 | (store return-pc reg-A) 122 | (jmp tag-print-string) 123 | ) 124 | ;; tag-print-fizzbuzz 125 | (list 126 | (mov reg-A *Fizz) 127 | (store i reg-A) 128 | (mov reg-A tag-print-buzz) 129 | (store return-pc reg-A) 130 | (jmp tag-print-string) 131 | ) 132 | ;; tag-print-string 133 | (list 134 | (load reg-A i) 135 | (load reg-B reg-A) ;; Load the current character at &i 136 | (load reg-C return-pc) 137 | (jmpcmp reg-B == int-0 -> reg-C) ;; If the current character is null, jump to return-pc 138 | (putc reg-B) ;; Otherwise, print the current character 139 | (add reg-A int-1) 140 | (store i reg-A) ;; Increment i 141 | (jmp tag-print-string) 142 | ) 143 | ;; tag-prepare-next-iter 144 | (list 145 | (putc "\\n") ;; Print newline 146 | 147 | ;; Update n and i 148 | (load reg-A n) 149 | (add reg-A int-1) 150 | (store n reg-A) 151 | (store i reg-A) ;; i = ++n 152 | 153 | ;; Update nmod3 154 | (load reg-A nmod3) 155 | (add reg-A int-1) 156 | (mov reg-B reg-A) 157 | (cmp reg-B == int-3) ;; Evaluates to 1 if true 158 | (mov reg-C reg-B) 159 | (add reg-C reg-B) 160 | (add reg-C reg-B) 161 | (sub reg-A reg-C) ;; Subtract 3 from reg-A if nmod3 == 3 162 | (store nmod3 reg-A) 163 | 164 | ;; Update nmod5 165 | (load reg-A nmod5) 166 | (add reg-A int-1) 167 | (mov reg-B reg-A) 168 | (cmp reg-B == int-5) ;; Evaluates to 1 if true 169 | (mov reg-C reg-B) 170 | (add reg-C reg-B) 171 | (add reg-C reg-C) 172 | (add reg-C reg-B) 173 | (sub reg-A reg-C) ;; Subtract 5 from reg-A if nmod5 == 5 174 | (store nmod5 reg-A) 175 | 176 | ;; Update j 177 | (mov reg-A int-0) 178 | (store j reg-A) 179 | 180 | (jmp tag-main) 181 | ) 182 | )) 183 | 184 | 185 | 186 | ;; The number of bits used for I/O. 187 | ;; Binary lambda calculus supplies a list of lists of 8-bit-encoded characters. 188 | (def-lazy SYS-IO-BITS 8) 189 | ;; The number of supplementary bits to prepend to the I/O bits, to be used for the machine's word size. 190 | ;; Machine word size = SYS-IO-BITS + SYS-SUPPLEMENTARY-BITS 191 | (def-lazy SYS-SUPPLEMENTARY-BITS 0) 192 | 193 | (def-lazy standalone 194 | ;; All binary lambda calculus programs are functions that accept a string (stdin) and return a string. 195 | (lambda (stdin) 196 | (lambdaVM SYS-IO-BITS SYS-SUPPLEMENTARY-BITS initial-memory asm stdin))) 197 | 198 | 199 | ;; The code is compilable to 3 languages, Binary Lambda Calculus, Universal Lambda, and Lazy K. 200 | ;; To compile to Lazy K, write: 201 | ;; (defparameter **compile-lazy** t) 202 | ;; To compile to Universal Lambda, write: 203 | ;; (defparameter **compile-ulamb** t) 204 | ;; By default, the code gets compiled to Binary Lambda Calculus. 205 | (cond 206 | ((boundp '**compile-lazy**) 207 | (load "./src/blc-clamb-wrapper.cl") 208 | (format t (compile-to-ski-lazy (blc-to-lazyk standalone)))) 209 | ((boundp '**compile-ulamb**) 210 | (load "./src/blc-clamb-wrapper.cl") 211 | (format t (compile-to-blc-lazy (blc-to-ulamb standalone)))) 212 | (t 213 | (format t (compile-to-blc-lazy standalone)))) 214 | -------------------------------------------------------------------------------- /examples/rot13.cl: -------------------------------------------------------------------------------- 1 | (load "./src/lambda-asm-header.cl") 2 | 3 | 4 | ;; Define your own register addresses 5 | (def-lazy reg-A (list t t)) 6 | (def-lazy reg-B (list t nil)) 7 | (def-lazy reg-C (list nil t)) 8 | (def-lazy reg-D (list nil nil)) 9 | ;; Used for the macros in ./lambda-asm-header.cl, to determine if a value is an immediate or a register 10 | (defparameter regnames '(reg-A reg-B reg-C reg-D)) 11 | 12 | ;; Numbers are in big-endian, with t == 0, nil == 1 13 | ;; (Raw integer literals `0`, `1`, etc. are bound to different lambda forms in ./lambdacraft.cl) 14 | (def-lazy int-0 (list t t t t t t t t)) 15 | (def-lazy int-1 (list t t t t t t t nil)) 16 | (def-lazy int-2 (list t t t t t t nil t)) 17 | (def-lazy int-3 (list t t t t t t nil nil)) 18 | (def-lazy int-4 (list t t t t t nil t t)) 19 | (def-lazy int-5 (list t t t t t nil t nil)) 20 | 21 | ;; Define constants 22 | (def-lazy EOF int-0) 23 | 24 | ;; Define tags for jump instructions 25 | (def-lazy tag-main int-1) 26 | (def-lazy tag-print int-2) 27 | (def-lazy tag-plus13 int-3) 28 | (def-lazy tag-minus13 int-4) 29 | (def-lazy tag-exit int-5) 30 | 31 | 32 | 33 | ;; The assembly is a list of lists of instructions. 34 | ;; Each sublist defines a chunk bound with a program counter, starting from PC == 0. 35 | ;; When a jump instruction is executed, the instruction pointer transitions to the first instruction of the given program counter. 36 | (def-lazy asm (list 37 | ;; Initialization (PC == 0) 38 | (list 39 | ;; Store 26/2 = 13 at reg-B 40 | (mov reg-B "N") 41 | (sub reg-B "A") 42 | ) 43 | ;; tag-main (PC == 1) 44 | (list 45 | (getc reg-A) 46 | 47 | ;; Exit at EOF 48 | (jmpcmp reg-A == EOF -> tag-exit) 49 | 50 | ;; "a" <= reg-A < "n" : add 13 51 | (mov reg-C reg-A) 52 | (cmp reg-C >= "a") 53 | (mov reg-D reg-A) 54 | (cmp reg-D < "n") 55 | (add reg-C reg-D) 56 | (jmpcmp reg-C == int-2 -> tag-plus13) 57 | 58 | ;; "n" <= reg-A <= "z" : sub 13 59 | (mov reg-C reg-A) 60 | (cmp reg-C >= "n") 61 | (mov reg-D reg-A) 62 | (cmp reg-D <= "z") 63 | (add reg-C reg-D) 64 | (jmpcmp reg-C == int-2 -> tag-minus13) 65 | 66 | ;; "A" <= reg-A < "N" : add 13 67 | (mov reg-C reg-A) 68 | (cmp reg-C >= "A") 69 | (mov reg-D reg-A) 70 | (cmp reg-D < "N") 71 | (add reg-C reg-D) 72 | (jmpcmp reg-C == int-2 -> tag-plus13) 73 | 74 | ;; "N" <= reg-A <= "Z" : sub 13 75 | (mov reg-C reg-A) 76 | (cmp reg-C >= "N") 77 | (mov reg-D reg-A) 78 | (cmp reg-D <= "Z") 79 | (add reg-C reg-D) 80 | (jmpcmp reg-C == int-2 -> tag-minus13) 81 | 82 | ;; If the character is not an alphabet, fall through to tag-print and print it as is 83 | ) 84 | ;; tag-print 85 | (list 86 | (putc reg-A) 87 | (jmp tag-main) 88 | ) 89 | ;; tag-plus13 90 | (list 91 | (add reg-A reg-B) 92 | (jmp tag-print) 93 | ) 94 | ;; tag-minus13 95 | (list 96 | (sub reg-A reg-B) 97 | (jmp tag-print) 98 | ) 99 | ;; tag-exit 100 | (list 101 | ) 102 | )) 103 | 104 | 105 | 106 | ;; The number of bits used for I/O. 107 | ;; Binary lambda calculus supplies a list of lists of 8-bit-encoded characters. 108 | (def-lazy SYS-IO-BITS 8) 109 | ;; The number of supplementary bits to prepend to the I/O bits, to be used for the machine's word size. 110 | ;; Machine word size = SYS-IO-BITS + SYS-SUPPLEMENTARY-BITS 111 | (def-lazy SYS-SUPPLEMENTARY-BITS 0) 112 | (def-lazy initial-memory nil) 113 | 114 | (def-lazy standalone 115 | ;; All binary lambda calculus programs are functions that accept a string (stdin) and return a string. 116 | (lambda (stdin) 117 | (lambdaVM SYS-IO-BITS SYS-SUPPLEMENTARY-BITS initial-memory asm stdin))) 118 | 119 | 120 | ;; The code is compilable to 3 languages, Binary Lambda Calculus, Universal Lambda, and Lazy K. 121 | ;; To compile to Lazy K, write: 122 | ;; (defparameter **compile-lazy** t) 123 | ;; To compile to Universal Lambda, write: 124 | ;; (defparameter **compile-ulamb** t) 125 | ;; By default, the code gets compiled to Binary Lambda Calculus. 126 | (cond 127 | ((boundp '**compile-lazy**) 128 | (load "./src/blc-clamb-wrapper.cl") 129 | (format t (compile-to-ski-lazy (blc-to-lazyk standalone)))) 130 | ((boundp '**compile-ulamb**) 131 | (load "./src/blc-clamb-wrapper.cl") 132 | (format t (compile-to-blc-lazy (blc-to-ulamb standalone)))) 133 | (t 134 | (format t (compile-to-blc-lazy standalone)))) 135 | -------------------------------------------------------------------------------- /examples/yes.cl: -------------------------------------------------------------------------------- 1 | (load "./src/lambda-asm-header.cl") 2 | 3 | 4 | ;; Numbers are in big-endian, with t == 0, nil == 1 5 | ;; (Raw integer literals `0`, `1`, etc. are bound to different lambda forms in ./lambdacraft.cl) 6 | (def-lazy int-0 (list t t t t t t t t)) 7 | 8 | 9 | ;; The assembly is a list of lists of instructions. 10 | ;; Each sublist defines a chunk bound with a program counter, starting from PC == 0. 11 | ;; When a jump instruction is executed, the instruction pointer transitions to the first instruction of the given program counter. 12 | (def-lazy asm (list 13 | ;; Initialization (PC == 0) 14 | (list 15 | (putc "y") 16 | (putc "\\n") 17 | (jmp int-0) 18 | ) 19 | )) 20 | 21 | 22 | 23 | ;; The number of bits used for I/O. 24 | ;; Binary lambda calculus supplies a list of lists of 8-bit-encoded characters. 25 | (def-lazy SYS-IO-BITS 8) 26 | ;; The number of supplementary bits to prepend to the I/O bits, to be used for the machine's word size. 27 | ;; Machine word size = SYS-IO-BITS + SYS-SUPPLEMENTARY-BITS 28 | (def-lazy SYS-SUPPLEMENTARY-BITS 0) 29 | (def-lazy initial-memory nil) 30 | 31 | (def-lazy standalone 32 | ;; All binary lambda calculus programs are functions that accept a string (stdin) and return a string. 33 | (lambda (stdin) 34 | (lambdaVM SYS-IO-BITS SYS-SUPPLEMENTARY-BITS initial-memory asm stdin))) 35 | 36 | 37 | ;; The code is compilable to 3 languages, Binary Lambda Calculus, Universal Lambda, and Lazy K. 38 | ;; To compile to Lazy K, write: 39 | ;; (defparameter **compile-lazy** t) 40 | ;; To compile to Universal Lambda, write: 41 | ;; (defparameter **compile-ulamb** t) 42 | ;; By default, the code gets compiled to Binary Lambda Calculus. 43 | (cond 44 | ((boundp '**compile-lazy**) 45 | (load "./src/blc-clamb-wrapper.cl") 46 | (format t (compile-to-ski-lazy (blc-to-lazyk standalone)))) 47 | ((boundp '**compile-ulamb**) 48 | (load "./src/blc-clamb-wrapper.cl") 49 | (format t (compile-to-blc-lazy (blc-to-ulamb standalone)))) 50 | (t 51 | (format t (compile-to-blc-lazy standalone)))) 52 | -------------------------------------------------------------------------------- /lambdavm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woodrush/lambdavm/8e469b4e60a1033842a061b2b3a1e675b4f7d23c/lambdavm.pdf -------------------------------------------------------------------------------- /src/blc-clamb-wrapper.cl: -------------------------------------------------------------------------------- 1 | (def-lazy powerlist (list 128 64 32 16 8 4 2 1)) 2 | 3 | (defrec-lazy int2bitlist (n powerlist cont) 4 | (if (isnil powerlist) 5 | (cont nil) 6 | (do 7 | (<- (car-pow cdr-pow) (powerlist)) 8 | (if-then-return (<= car-pow n) 9 | (do 10 | (<- (nextlist) (int2bitlist (- n car-pow) cdr-pow)) 11 | (cont (cons nil nextlist)))) 12 | (<- (nextlist) (int2bitlist n cdr-pow)) 13 | (cont (cons t nextlist))))) 14 | 15 | (defrec-lazy ulambstr-to-blcstr (s) 16 | (cond 17 | ((isnil s) 18 | nil) 19 | (t 20 | (do 21 | (<- (c-ulamb s-cdr) (s)) 22 | (<- (c-blc) (int2bitlist c-ulamb powerlist)) 23 | (cons c-blc (ulambstr-to-blcstr s-cdr)))))) 24 | 25 | 26 | ;; In SKI combinator calculus, using isnil led to shorter code than using typematch-nil-cons here. 27 | (defrec-lazy bitlist2int (n powerlist cont) 28 | (if (isnil powerlist) 29 | (cont 0) 30 | (do 31 | (<- (car-pow cdr-pow) (powerlist)) 32 | (<- (car-n cdr-n) (n)) 33 | (<- (n-ret) (bitlist2int cdr-n cdr-pow)) 34 | (if car-n 35 | (cont n-ret) 36 | (cont (+ car-pow n-ret)))))) 37 | 38 | ;; In SKI combinator calculus, using isnil led to shorter code than using typematch-nil-cons here. 39 | (defun-lazy blcchar-to-ulambchar (c cont) 40 | (cond 41 | ((isnil c) 42 | (cont nil)) 43 | (t 44 | (bitlist2int c powerlist cont)))) 45 | 46 | (defrec-lazy blcstr-to-ulambstr (s) 47 | (cond 48 | ((isnil s) 49 | nil) 50 | (t 51 | (do 52 | (<- (c-blc s-cdr) (s)) 53 | (<- (c-ulamb) (blcchar-to-ulambchar c-blc)) 54 | (cons c-ulamb (blcstr-to-ulambstr s-cdr)))))) 55 | 56 | 57 | ;; Lazy K 58 | (defrec-lazy blcstr-to-lazykstr (s) 59 | (typematch-nil-cons s (c-blc s-cdr) 60 | ;; nil case 61 | (inflist 256) 62 | ;; cons case 63 | (do 64 | (<- (c-ulamb) (blcchar-to-ulambchar c-blc)) 65 | (cons c-ulamb (blcstr-to-lazykstr s-cdr))))) 66 | 67 | (defrec-lazy lazykstr-to-blcstr (s) 68 | (do 69 | (<- (c-ulamb s-cdr) (s)) 70 | (if-then-return (= 256 c-ulamb) 71 | nil) 72 | (<- (c-blc) (int2bitlist c-ulamb powerlist)) 73 | (cons c-blc (lazykstr-to-blcstr s-cdr)))) 74 | 75 | (defun-lazy blc-to-lazyk (program stdin) 76 | (blcstr-to-lazykstr (program (lazykstr-to-blcstr stdin)))) 77 | 78 | (defun-lazy blc-to-ulamb (program stdin) 79 | (blcstr-to-ulambstr (program (ulambstr-to-blcstr stdin)))) 80 | -------------------------------------------------------------------------------- /src/blc-numbers.cl: -------------------------------------------------------------------------------- 1 | (defun-lazy p-t-nil (x) (cons t (cons nil x))) 2 | (defun-lazy p-t-t (x) (cons t (cons t x))) 3 | (defun-lazy p-nil-nil (x) (cons nil (cons nil x))) 4 | (defun-lazy p-nil-t (x) (cons nil (cons t x))) 5 | (defun-lazy alphabet-prefix-t (x) (p-t-nil (p-t-t x))) 6 | (defun-lazy alphabet-prefix-nil (x) (2 p-t-nil x)) 7 | (def-lazy "A" (alphabet-prefix-t "A-tail")) 8 | (def-lazy "B" (alphabet-prefix-t "B-tail")) 9 | (def-lazy "C" (alphabet-prefix-t "C-tail")) 10 | (def-lazy "D" (alphabet-prefix-t "D-tail")) 11 | (def-lazy "E" (alphabet-prefix-t "E-tail")) 12 | (def-lazy "F" (alphabet-prefix-t "F-tail")) 13 | (def-lazy "G" (alphabet-prefix-t "G-tail")) 14 | (def-lazy "H" (alphabet-prefix-t "H-tail")) 15 | (def-lazy "I" (alphabet-prefix-t "I-tail")) 16 | (def-lazy "J" (alphabet-prefix-t "J-tail")) 17 | (def-lazy "K" (alphabet-prefix-t "K-tail")) 18 | (def-lazy "L" (alphabet-prefix-t "L-tail")) 19 | (def-lazy "M" (alphabet-prefix-t "M-tail")) 20 | (def-lazy "N" (alphabet-prefix-t "N-tail")) 21 | (def-lazy "O" (alphabet-prefix-t "O-tail")) 22 | (def-lazy "P" (alphabet-prefix-nil "P-tail")) 23 | (def-lazy "Q" (alphabet-prefix-nil "Q-tail")) 24 | (def-lazy "R" (alphabet-prefix-nil "R-tail")) 25 | (def-lazy "S" (alphabet-prefix-nil "S-tail")) 26 | (def-lazy "T" (alphabet-prefix-nil "T-tail")) 27 | (def-lazy "U" (alphabet-prefix-nil "U-tail")) 28 | (def-lazy "V" (alphabet-prefix-nil "V-tail")) 29 | (def-lazy "W" (alphabet-prefix-nil "W-tail")) 30 | (def-lazy "X" (alphabet-prefix-nil "X-tail")) 31 | (def-lazy "Y" (alphabet-prefix-nil "Y-tail")) 32 | (def-lazy "Z" (alphabet-prefix-nil "Z-tail")) 33 | 34 | (defun-lazy alphabet-lower-prefix-t (x) (p-t-nil (p-nil-t x))) 35 | (defun-lazy alphabet-lower-prefix-nil (x) (p-t-nil (p-nil-nil x))) 36 | (def-lazy "a" (alphabet-lower-prefix-t "A-tail")) 37 | (def-lazy "b" (alphabet-lower-prefix-t "B-tail")) 38 | (def-lazy "c" (alphabet-lower-prefix-t "C-tail")) 39 | (def-lazy "d" (alphabet-lower-prefix-t "D-tail")) 40 | (def-lazy "e" (alphabet-lower-prefix-t "E-tail")) 41 | (def-lazy "f" (alphabet-lower-prefix-t "F-tail")) 42 | (def-lazy "g" (alphabet-lower-prefix-t "G-tail")) 43 | (def-lazy "h" (alphabet-lower-prefix-t "H-tail")) 44 | (def-lazy "i" (alphabet-lower-prefix-t "I-tail")) 45 | (def-lazy "j" (alphabet-lower-prefix-t "J-tail")) 46 | (def-lazy "k" (alphabet-lower-prefix-t "K-tail")) 47 | (def-lazy "l" (alphabet-lower-prefix-t "L-tail")) 48 | (def-lazy "m" (alphabet-lower-prefix-t "M-tail")) 49 | (def-lazy "n" (alphabet-lower-prefix-t "N-tail")) 50 | (def-lazy "o" (alphabet-lower-prefix-t "O-tail")) 51 | (def-lazy "p" (alphabet-lower-prefix-nil "P-tail")) 52 | (def-lazy "q" (alphabet-lower-prefix-nil "Q-tail")) 53 | (def-lazy "r" (alphabet-lower-prefix-nil "R-tail")) 54 | (def-lazy "s" (alphabet-lower-prefix-nil "S-tail")) 55 | (def-lazy "t" (alphabet-lower-prefix-nil "T-tail")) 56 | (def-lazy "u" (alphabet-lower-prefix-nil "U-tail")) 57 | (def-lazy "v" (alphabet-lower-prefix-nil "V-tail")) 58 | (def-lazy "w" (alphabet-lower-prefix-nil "W-tail")) 59 | (def-lazy "x" (alphabet-lower-prefix-nil "X-tail")) 60 | (def-lazy "y" (alphabet-lower-prefix-nil "Y-tail")) 61 | (def-lazy "z" (alphabet-lower-prefix-nil "Z-tail")) 62 | 63 | 64 | (def-lazy "A-tail" (p-t-t (p-t-nil nil))) 65 | (def-lazy "B-tail" (p-t-t (p-nil-t nil))) 66 | (def-lazy "C-tail" (p-t-t (p-nil-nil nil))) 67 | (def-lazy "D-tail" (p-t-nil (p-t-t nil))) 68 | (def-lazy "E-tail" (p-t-nil (p-t-nil nil))) 69 | (def-lazy "F-tail" (p-t-nil (p-nil-t nil))) 70 | (def-lazy "G-tail" (p-t-nil (p-nil-nil nil))) 71 | (def-lazy "H-tail" (p-nil-t (p-t-t nil))) 72 | (def-lazy "I-tail" (p-nil-t (p-t-nil nil))) 73 | (def-lazy "J-tail" (p-nil-t (p-nil-t nil))) 74 | (def-lazy "K-tail" (p-nil-t (p-nil-nil nil))) 75 | (def-lazy "L-tail" (p-nil-nil (p-t-t nil))) 76 | (def-lazy "M-tail" (p-nil-nil (p-t-nil nil))) 77 | (def-lazy "N-tail" (p-nil-nil (p-nil-t nil))) 78 | (def-lazy "O-tail" (p-nil-nil(p-nil-nil nil))) 79 | (def-lazy "P-tail" (2 p-t-t nil)) 80 | (def-lazy "Q-tail" (p-t-t (p-t-nil nil))) 81 | (def-lazy "R-tail" (p-t-t (p-nil-t nil))) 82 | (def-lazy "S-tail" (p-t-t (p-nil-nil nil))) 83 | (def-lazy "T-tail" (p-t-nil (p-t-t nil))) 84 | (def-lazy "U-tail" (2 p-t-nil nil)) 85 | (def-lazy "V-tail" (cons t (cons nil (cons nil (cons t nil))))) 86 | (def-lazy "W-tail" (cons t (cons nil (cons nil (cons nil nil))))) 87 | (def-lazy "X-tail" (cons nil (cons t (cons t (cons t nil))))) 88 | (def-lazy "Y-tail" (cons nil (cons t (cons t (cons nil nil))))) 89 | (def-lazy "Z-tail" (cons nil (cons t (cons nil (cons t nil))))) 90 | 91 | (defun-lazy symbol-prefix (x) (p-t-t (p-nil-t x))) 92 | (def-lazy "(" (symbol-prefix (p-nil-t (p-t-t nil)))) 93 | (def-lazy ")" (symbol-prefix (p-nil-t (p-t-nil nil)))) 94 | (def-lazy " " (symbol-prefix (p-t-t (p-t-t nil)))) 95 | (def-lazy "." (symbol-prefix (p-nil-nil (p-nil-t nil)))) 96 | (def-lazy "\\n" (p-t-t (symbol-prefix (p-nil-t nil)))) 97 | (def-lazy "*" (p-t-t (p-nil-t (p-nil-t (p-nil-t nil))))) 98 | 99 | -------------------------------------------------------------------------------- /src/blc-to-ski.cl: -------------------------------------------------------------------------------- 1 | ;;=============================================================================== 2 | ;; MIT License 3 | ;; 4 | ;; Copyright (c) 2022 Hikaru Ikuta 5 | ;; 6 | ;; Permission is hereby granted, free of charge, to any person obtaining a copy 7 | ;; of this software and associated documentation files (the "Software"), to deal 8 | ;; in the Software without restriction, including without limitation the rights 9 | ;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | ;; copies of the Software, and to permit persons to whom the Software is 11 | ;; furnished to do so, subject to the following conditions: 12 | ;; 13 | ;; The above copyright notice and this permission notice shall be included in all 14 | ;; copies or substantial portions of the Software. 15 | ;; 16 | ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | ;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | ;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | ;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | ;; SOFTWARE. 23 | ;;=============================================================================== 24 | (load "./src/lambdacraft.cl") 25 | 26 | 27 | (defun show-help () 28 | (format *error-output* "Converts binary lambda calculus terms (0010, etc.) to a target format. 29 | 30 | Usage: 31 | sbcl --script blc-to-ski.cl [input option] [output option] 32 | clisp blc-to-ski.cl [input option] [output option] 33 | 34 | Input options: 35 | -iblc : From binary lambda calculus notation 36 | -iski : From SKI combinator calculus notation (Unlambda style) 37 | 38 | Output options: 39 | -oblc : To binary lambda calculus notation 40 | -oski : To ski (Unlambda style) 41 | -ojs : To js 42 | -ojs-arrow : To js arrow style 43 | -opython : To Python lambda 44 | -olambda : To simple lambda 45 | -odb-unl : To De Bruijn index notation (Unlambda style) 46 | -olisp : To Lisp S-expression (no pretty printing) 47 | -olisp-pp : To Lisp S-expression (with pretty printing) 48 | 49 | Other options: 50 | -h, -help : Show this help message 51 | 52 | Notes: 53 | - Runs on SBCL or CLISP (where command line arguments are bound to either *posix-argv* or *args*) 54 | - Running on CLISP prints a trailing newline. Remove it if it affects utilities such as asc2bin: 55 | clisp blc-to-ski.cl -oski | tr -d '\n' 56 | ")) 57 | 58 | (defun lex-var (s) 59 | (let ((c (car s))) 60 | (cond 61 | ((not s) 62 | (error "Lexing error: Unexpected EOF")) 63 | ((= c 0) 64 | (cons 0 (cdr s))) 65 | (t 66 | (let ((ret (lex-var (cdr s)))) 67 | (cons (+ 1 (car ret)) (cdr ret))))))) 68 | 69 | (defun lex-blc (s) 70 | (let ((curexpr nil) (ret nil)) 71 | (loop 72 | (cond 73 | ((not s) 74 | (return (reverse curexpr))) 75 | ((= (car s) 0) 76 | (let ((c2 (car (cdr s)))) 77 | (cond 78 | ((= c2 0) 79 | (setq ret 'ABS)) 80 | (t 81 | (setq ret 'APP)))) 82 | (setq curexpr (cons ret curexpr)) 83 | (setq s (cdr (cdr s)))) 84 | (t 85 | (let ((ret (lex-var (cdr s)))) 86 | (setq curexpr (cons (car ret) curexpr)) 87 | (setq s (cdr ret)))))))) 88 | 89 | (defun decorate-var (i) 90 | (intern (format nil "X~a" i))) 91 | 92 | (defun parse-de-bruijn (l) 93 | (let ((stack nil) 94 | (envdepth -1) 95 | (curexpr nil)) 96 | (loop :for token :in l :do 97 | (cond 98 | ((equal 'APP token) 99 | (setq stack (cons 'APP stack))) 100 | ((equal 'ABS token) 101 | (setq stack (cons 'ABS stack)) 102 | (setq envdepth (+ 1 envdepth))) 103 | (t 104 | (setq curexpr (decorate-var (- envdepth token))) 105 | (loop 106 | (if (not stack) 107 | (return)) 108 | (let ((stacktop (car stack))) 109 | (cond 110 | ((equal 'APP stacktop) 111 | (setq stack (cons curexpr stack)) 112 | (return)) 113 | ((equal 'ABS stacktop) 114 | (setq curexpr `(lambda (,(decorate-var envdepth)) ,curexpr)) 115 | (setq envdepth (- envdepth 1)) 116 | (setq stack (cdr stack))) 117 | ((equal 'APP (car (cdr stack))) 118 | (setq curexpr `(,stacktop ,curexpr)) 119 | (setq stack (cdr (cdr stack))))))) 120 | (if (not stack) 121 | (return curexpr))))))) 122 | 123 | (defun parse-argv () 124 | (cond 125 | ;; SBCL 126 | ((boundp '*posix-argv*) 127 | (cdr (eval '*posix-argv*))) 128 | ;; CLISP 129 | ((boundp '*args*) 130 | (eval '*args*)) 131 | (t 132 | nil))) 133 | 134 | (defun ski-to-blc (s) 135 | (setq s (coerce s 'list)) 136 | (setq s (subst "01" (car (coerce "`" 'list)) s)) 137 | (setq s (subst "00000001011110100111010" #\s s)) 138 | (setq s (subst "0000110" #\k s)) 139 | (setq s (subst "0010" #\i s)) 140 | (setq s (apply #'concatenate (cons 'string s))) 141 | s) 142 | 143 | (defun parse-input (argv) 144 | (cond 145 | ((equal (car argv) "-iblc") 146 | (let* ((s (read-line)) 147 | (s (coerce s 'list)) 148 | (s (mapcar (lambda (x) (if (equal x #\0) 0 1)) s)) 149 | (parsed (parse-de-bruijn (lex-blc s)))) 150 | parsed)) 151 | ((equal (car argv) "-iski") 152 | (let* ((s (read-line)) 153 | (s (ski-to-blc s)) 154 | (s (coerce s 'list)) 155 | (s (mapcar (lambda (x) (if (equal x #\0) 0 1)) s)) 156 | (parsed (parse-de-bruijn (lex-blc s)))) 157 | parsed)) 158 | (t 159 | (show-help)))) 160 | 161 | (defun compile-parsed (argv parsed) 162 | (cond 163 | ((equal (car argv) "-oblc") 164 | (format t (compile-to-blc parsed))) 165 | ((equal (car argv) "-oski") 166 | (format t (compile-to-ski parsed))) 167 | ((equal (car argv) "-ojs") 168 | (format t (compile-to-js parsed))) 169 | ((equal (car argv) "-ojs-arrow") 170 | (format t (compile-to-js-arrow parsed))) 171 | ((equal (car argv) "-opython") 172 | (format t (compile-to-python parsed))) 173 | ((equal (car argv) "-olambda") 174 | (format t (compile-to-lam parsed))) 175 | ((equal (car argv) "-db-unl") 176 | (format t (compile-to-de-bruijn-unl parsed))) 177 | ((equal (car argv) "-olisp") 178 | (setq *print-pretty* 'nil) 179 | (format t "~a" parsed)) 180 | ((equal (car argv) "-olisp-pp") 181 | (setf *print-right-margin* 800) 182 | (format t "~a" parsed)) 183 | (t 184 | (show-help)))) 185 | 186 | (defun main () 187 | (let ((argv (parse-argv))) 188 | (cond 189 | ((or (not argv) 190 | (not (= 2 (length argv))) 191 | (equal (car argv) "-h") 192 | (equal (car argv) "-help")) 193 | (show-help)) 194 | (t 195 | (compile-parsed (cdr argv) (parse-input argv)))))) 196 | 197 | (main) 198 | -------------------------------------------------------------------------------- /src/elvm-lazy.cl: -------------------------------------------------------------------------------- 1 | (load "./src/lambdacraft.cl") 2 | 3 | 4 | (defmacro-lazy typematch-nil-cons (expr cons-args nil-case cons-case) 5 | `(,expr 6 | (lambda ,cons-args 7 | (lambda (_) ,cons-case)) 8 | ,nil-case)) 9 | 10 | 11 | (def-lazy SYS-N-BITS (+ 16 8)) 12 | (def-lazy int-zero (take SYS-N-BITS (inflist nil))) 13 | 14 | ;;================================================================ 15 | ;; Memory and program 16 | ;;================================================================ 17 | (defrec-lazy lookup-tree (memory address) 18 | (typematch-nil-cons address (car-address cdr-address) 19 | ;; nil case 20 | memory 21 | ;; cons case 22 | (typematch-nil-cons memory (car-memory cdr-memory) 23 | ;; nil case 24 | int-zero 25 | ;; cons case 26 | ((if car-address 27 | (lookup-tree car-memory) 28 | (lookup-tree cdr-memory)) 29 | cdr-address) 30 | 31 | ) 32 | ) 33 | 34 | ;; (cond 35 | ;; ((isnil progtree) 36 | ;; int-zero) 37 | ;; ((isnil address) 38 | ;; progtree) 39 | ;; (t 40 | ;; (lookup-tree (progtree (car address)) (cdr address)))) 41 | 42 | ) 43 | 44 | (defrec-lazy memory-write (memory address value) 45 | (let ((next (lambda (x) (memory-write x (cdr address) value)))) 46 | (cond 47 | ((isnil address) 48 | value) 49 | ((isnil memory) 50 | ((car address) 51 | (cons (next nil) nil) 52 | (cons nil (next nil)))) 53 | (t 54 | ((car address) 55 | (cons (next (car memory)) (cdr memory)) 56 | (cons (car memory) (next (cdr memory)))))))) 57 | 58 | (defrec-lazy list2tree (memlist depth decorator) 59 | (cond 60 | ((isnil memlist) 61 | (cons nil nil)) 62 | ((isnil depth) 63 | (cons (decorator memlist) (cdr memlist))) 64 | (t 65 | (let ((rightstate (list2tree memlist (cdr depth) decorator)) 66 | (righttree (car rightstate)) 67 | (right-restmemlist (cdr rightstate)) 68 | (leftstate (list2tree right-restmemlist (cdr depth) decorator)) 69 | (lefttree (car leftstate)) 70 | (left-restmemlist (cdr leftstate))) 71 | (cons (cons lefttree righttree) left-restmemlist))))) 72 | 73 | 74 | ;;================================================================ 75 | ;; Registers 76 | ;;================================================================ 77 | (defun-lazy reg-A (r1 r2 r3 r4 r5 r6) r6) 78 | (defun-lazy reg-B (r1 r2 r3 r4 r5 r6) r5) 79 | (defun-lazy reg-C (r1 r2 r3 r4 r5 r6) r4) 80 | (defun-lazy reg-D (r1 r2 r3 r4 r5 r6) r3) 81 | (defun-lazy reg-SP (r1 r2 r3 r4 r5 r6) r2) 82 | (defun-lazy reg-BP (r1 r2 r3 r4 r5 r6) r1) 83 | (defmacro-lazy cons6 (r1 r2 r3 r4 r5 r6) 84 | `(lambda (f) (f ,r1 ,r2 ,r3 ,r4 ,r5 ,r6))) 85 | 86 | ;; (defun-lazy regptr2regaddr (regptr) 87 | ;; (regptr 88 | ;; (list nil nil nil) 89 | ;; (list t nil nil) 90 | ;; (list nil t nil) 91 | ;; (list t t nil) 92 | ;; (list nil nil t) 93 | ;; (list t nil t))) 94 | 95 | (defun-lazy reg-read (reg regptr) 96 | (lookup-tree reg 97 | regptr 98 | ;; (regptr2regaddr regptr) 99 | )) 100 | 101 | (defun-lazy reg-write (reg value regptr) 102 | (memory-write reg 103 | regptr 104 | ;; (regptr2regaddr regptr) 105 | value)) 106 | 107 | 108 | ;;================================================================ 109 | ;; Instructions 110 | ;;================================================================ 111 | (defun-lazy inst-add (i1 i2 i3 i4 i5 i6 i7 i8 i9) i9) 112 | (defun-lazy inst-store (i1 i2 i3 i4 i5 i6 i7 i8 i9) i8) 113 | (defun-lazy inst-mov (i1 i2 i3 i4 i5 i6 i7 i8 i9) i7) 114 | (defun-lazy inst-jmp (i1 i2 i3 i4 i5 i6 i7 i8 i9) i6) 115 | (defun-lazy inst-jumpcmp (i1 i2 i3 i4 i5 i6 i7 i8 i9) i5) 116 | (defun-lazy inst-load (i1 i2 i3 i4 i5 i6 i7 i8 i9) i4) 117 | (defun-lazy inst-cmp (i1 i2 i3 i4 i5 i6 i7 i8 i9) i3) 118 | (defun-lazy inst-sub (i1 i2 i3 i4 i5 i6 i7 i8 i9) i2) 119 | (defun-lazy inst-io-int (i1 i2 i3 i4 i5 i6 i7 i8 i9) i1) 120 | 121 | (defun-lazy io-int-putc (x1 x2 x3) x3) 122 | (defun-lazy io-int-getc (x1 x2 x3) x2) 123 | (defun-lazy io-int-exit (x1 x2 x3) x1) 124 | 125 | (defmacro-lazy car4-1 (f) `(,f (lambda (x1 x2 x3 x4) x1))) 126 | (defmacro-lazy car4-2 (f) `(,f (lambda (x1 x2 x3 x4) x2))) 127 | (defmacro-lazy car4-3 (f) `(,f (lambda (x1 x2 x3 x4) x3))) 128 | (defmacro-lazy car4-4 (f) `(,f (lambda (x1 x2 x3 x4) x4))) 129 | (defmacro-lazy cons4 (x1 x2 x3 x4) 130 | `(lambda (f) (f ,x1 ,x2 ,x3 ,x4))) 131 | 132 | 133 | ;;================================================================ 134 | ;; Arithmetic 135 | ;;================================================================ 136 | (defrec-lazy add-carry (n m carry invert) 137 | (cond ((isnil n) 138 | nil) 139 | (t 140 | (let ((next (lambda (x y) (cons x (add-carry (cdr n) (cdr m) y invert)))) 141 | (diff (next (not carry) carry))) 142 | (if (xor invert (car m)) 143 | (if (car n) 144 | (next carry t) 145 | diff) 146 | (if (car n) 147 | diff 148 | (next carry nil))))))) 149 | 150 | (defmacro-lazy add (n m) 151 | `(add-carry ,n ,m nil nil)) 152 | 153 | (defmacro-lazy sub (n m) 154 | `(add-carry ,n ,m t t)) 155 | 156 | (defrec-lazy iszero-bit (n) 157 | (cond ((isnil n) 158 | t) 159 | ((car n) 160 | nil) 161 | (t 162 | (iszero-bit (cdr n))))) 163 | 164 | (defun-lazy cmpret-eq (r1 r2 r3) r1) 165 | (defun-lazy cmpret-lt (r1 r2 r3) r2) 166 | (defun-lazy cmpret-gt (r1 r2 r3) r3) 167 | 168 | (defrec-lazy cmp* (n m) 169 | (cond ((isnil n) 170 | cmpret-eq) 171 | (t 172 | (let ((ncar (car n)) 173 | (mcar (car m))) 174 | (cond ((and (not ncar) mcar) 175 | cmpret-lt) 176 | ((and ncar (not mcar)) 177 | cmpret-gt) 178 | (t 179 | (cmp* (cdr n) (cdr m)))))))) 180 | 181 | (defun-lazy cmp-gt (x1 x2 x3 x4 x5 x6) x6) 182 | (defun-lazy cmp-lt (x1 x2 x3 x4 x5 x6) x5) 183 | (defun-lazy cmp-eq (x1 x2 x3 x4 x5 x6) x4) 184 | (defun-lazy cmp-le (x1 x2 x3 x4 x5 x6) x3) 185 | (defun-lazy cmp-ge (x1 x2 x3 x4 x5 x6) x2) 186 | (defun-lazy cmp-ne (x1 x2 x3 x4 x5 x6) x1) 187 | 188 | (defun-lazy cmp (n m enum-cmp) 189 | ((cmp* (reverse n) (reverse m)) 190 | (enum-cmp nil t t t nil nil) 191 | (enum-cmp t nil t nil t nil) 192 | (enum-cmp t t nil nil nil t ))) 193 | 194 | 195 | ;;================================================================ 196 | ;; I/O 197 | ;;================================================================ 198 | (def-lazy powerlist 199 | ((letrec-lazy powerlist (n bits) 200 | (cond ((isnil bits) 201 | nil) 202 | (t 203 | (cons n (powerlist (+ n n) (cdr bits)))))) 204 | 1 (take 8 (inflist t)))) 205 | 206 | (def-lazy revpowerlist 207 | (reverse powerlist)) 208 | 209 | (defrec-lazy bit2int* (n powerlist) 210 | (let ((next (bit2int* (cdr n) (cdr powerlist)))) 211 | (cond ((isnil powerlist) 212 | 0) 213 | ((car n) 214 | (+ (car powerlist) next)) 215 | (t 216 | next)))) 217 | 218 | (defmacro-lazy bit2int (n) 219 | `(bit2int* ,n powerlist)) 220 | 221 | (defrec-lazy int2bit* (n revpowerlist) 222 | (let ((next (lambda (x) (int2bit* x (cdr revpowerlist))))) 223 | (cond ((isnil revpowerlist) 224 | nil) 225 | ((<= (car revpowerlist) n) 226 | (cons t (next (- n (car revpowerlist))))) 227 | (t 228 | (cons nil (next n)))))) 229 | 230 | (defmacro-lazy int2bit (n) 231 | `(reverse-helper (int2bit* ,n revpowerlist) (take 16 (inflist nil)))) 232 | 233 | 234 | ;;================================================================ 235 | ;; Evaluation 236 | ;;================================================================ 237 | (defmacro-lazy await (stdin-top body) 238 | ;; The key ingredient to managing the I/O control flow. 239 | ;; By inspecting the value of the top character of the standard input and branching depending on its value, 240 | ;; `await` is able to halt the further execution of `body` until the input is actually provided. 241 | ;; Since elements of `stdin` are always a number, this form is guaranteed to evaluate to `body`. 242 | ;; However, since most interpreters do not use that fact during beta reduction 243 | ;; and expect `stdin` to be an arbitrary lambda form, 244 | ;; such interpreters cannot deduce that this form always reduces to `body`, 245 | ;; effectively making this form a method for halting evaluation until the standard input is provided. 246 | `(if (iszero (succ ,stdin-top)) 247 | nil 248 | ,body)) 249 | 250 | (defrec-lazy flatten (curlist listlist) 251 | (cond ((isnil curlist) 252 | (if (isnil listlist) 253 | nil 254 | (flatten (car listlist) (cdr listlist)))) 255 | (t 256 | (cons (car curlist) (flatten (cdr curlist) listlist))))) 257 | 258 | (defrec-lazy eval (reg memory progtree stdin curblock) 259 | (cond ((isnil curblock) 260 | SYS-STRING-TERM) 261 | (t 262 | ;; Prevent frequently used functions from being inlined every time 263 | (let ( 264 | ;; (lookup-tree lookup-tree) 265 | ;; (memory-write memory-write) 266 | ;; (reverse-helper reverse-helper) 267 | (expand-prog-at (lambda (pc) (flatten nil (lookup-tree progtree (reverse-helper pc nil))))) 268 | ;; (powerlist powerlist) 269 | ;; (add-carry add-carry) 270 | ;; (cmp cmp) 271 | (reg-read reg-read) 272 | (curinst (car curblock)) 273 | (*src (car4-3 curinst)) 274 | (src (if (car4-2 curinst) *src (reg-read reg *src))) 275 | (*dst (car4-4 curinst)) 276 | (nextblock (cdr curblock)) 277 | (eval-reg-write 278 | (lambda (src dst) 279 | (eval (reg-write reg src dst) memory progtree stdin nextblock)))) 280 | ;; Typematch on the current instruction's tag 281 | ((car4-1 curinst) 282 | ;; ==== inst-io-int ==== 283 | ;; Instruction structure: 284 | ;; exit: (cons4 inst-io-int nil nil io-int-exit) 285 | ;; getc: (cons4 inst-io-int nil [dst] io-int-getc) 286 | ;; putc: (cons4 inst-io-int [src-isimm] [src] io-int-putc) 287 | ;; Typematch over the inst. type 288 | (*dst 289 | ;; exit 290 | SYS-STRING-TERM 291 | ;; getc 292 | (cond ((isnil stdin) 293 | (eval-reg-write int-zero *src)) 294 | (t 295 | (eval 296 | (reg-write reg (int2bit (car stdin)) *src) 297 | memory progtree (cdr stdin) nextblock))) 298 | ;; putc 299 | (cons (bit2int src) (eval reg memory progtree stdin nextblock))) 300 | 301 | ;; ==== inst-sub ==== 302 | ;; Instruction structure: (cons4 inst-store [src-isimm] [src] [*dst]) 303 | (eval-reg-write 304 | (sub (reg-read reg *dst) src) 305 | *dst) 306 | 307 | ;; ==== inst-cmp ==== 308 | ;; Instruction structure: (cons4 inst-cmp [src-isimm] [src] (cons [emum-cmp] [dst])) 309 | (let ((*dst-cmp (cdr *dst)) 310 | (cmp-result (cmp (reg-read reg *dst-cmp) src (car *dst)))) 311 | (eval-reg-write 312 | (if cmp-result (cons t (cdr int-zero)) int-zero) 313 | *dst-cmp)) 314 | 315 | ;; ==== inst-load ==== 316 | ;; Instruction structure:: (cons4 inst-load [src-isimm] [src] [*dst]) 317 | (eval-reg-write 318 | (lookup-tree memory (reverse-helper src nil)) 319 | *dst) 320 | 321 | ;; ==== inst-jumpcmp ==== 322 | ;; Instruction structure: (cons4 inst-jumpcmp [src-isimm] [src] (cons4 [enum-cmp] [*dst] [jmp-isimm] [jmp])) 323 | (let ((*jmp (car4-4 *dst)) 324 | (jmp (if (car4-3 *dst) *jmp (reg-read reg *jmp)))) 325 | (eval reg memory progtree stdin 326 | (if (cmp (reg-read reg (car4-2 *dst)) src (car4-1 *dst)) 327 | (expand-prog-at jmp) 328 | nextblock))) 329 | 330 | ;; ==== inst-jmp ==== 331 | ;; Instruction structure:: (cons4 inst-jmp [jmp-isimm] [jmp] _) 332 | (eval reg memory progtree stdin (expand-prog-at src)) 333 | 334 | ;; ==== inst-mov ==== 335 | ;; Instruction structure:: (cons4 inst-mov [src-isimm] [src] [dst-memory]) 336 | (eval-reg-write src *dst) 337 | 338 | ;; ==== inst-store ==== 339 | ;; Instruction structure: (cons4 inst-store [dst-isimm] [dst-memory] [source]) 340 | ;; Note that the destination is stored in the variable *src 341 | (eval reg (memory-write memory (reverse-helper src nil) (reg-read reg *dst)) progtree stdin nextblock) 342 | 343 | ;; ==== inst-add ==== 344 | ;; Instruction structure: (cons4 inst-store [src-isimm] [src] [*dst]) 345 | (eval-reg-write (add src (reg-read reg *dst)) *dst)))))) 346 | 347 | 348 | (defun-lazy main (memlist proglist stdin) 349 | (let ((list2tree list2tree) 350 | (take take) 351 | (int-zero int-zero) 352 | ) 353 | (eval 354 | nil 355 | (car (list2tree memlist int-zero car*)) 356 | (car (list2tree proglist int-zero (lambda (x) x))) 357 | stdin 358 | (list 359 | (cons4 inst-jmp t int-zero nil))))) 360 | 361 | ;; In Lazy K, strings are terminated by an infinite list of `256`s 362 | (def-lazy SYS-STRING-TERM (inflist 256)) 363 | 364 | 365 | (defrec-lazy eof2nil (stdin) 366 | (let ((c (car stdin))) 367 | (if (<= 256 c) 368 | nil 369 | (cons c (eof2nil (cdr stdin)))))) 370 | 371 | (defun-lazy main-lazy (memlist proglist stdin) 372 | (main memlist proglist (eof2nil stdin))) 373 | 374 | ;;================================================================ 375 | ;; Code output 376 | ;;================================================================ 377 | ;; (format t (compile-to-ski-lazy main)) 378 | ;; (format t (compile-to-blc-lazy main)) 379 | 380 | (format t (compile-to-ski-lazy main-lazy)) 381 | ;; (format t (compile-to-blc-lazy main-lazy)) 382 | 383 | ;; ;; Print lambda term 384 | ;; (setf *print-right-margin* 800) 385 | ;; (format t (write-to-string (curry (macroexpand-lazy main)))) 386 | 387 | ;; ;; Print in curried De Bruijn notation 388 | ;; (format t (write-to-string (to-de-bruijn (curry (macroexpand-lazy main))))) 389 | -------------------------------------------------------------------------------- /src/lambda-asm-header.cl: -------------------------------------------------------------------------------- 1 | (load "./src/lambdavm.cl") 2 | 3 | 4 | ;; Used to determine if a value is an immediate or a register 5 | (defparameter regnames ()) 6 | 7 | (defmacro-lazy mov (dst src) 8 | (let ((is-imm (if (position src regnames) nil t))) 9 | `(cons4 inst-mov ,is-imm ,src ,dst))) 10 | 11 | (defmacro-lazy add (dst src) 12 | (let ((is-imm (if (position src regnames) nil t))) 13 | `(cons4 inst-addsub ,is-imm ,src (cons ,dst t)))) 14 | 15 | (defmacro-lazy sub (dst src) 16 | (let ((is-imm (if (position src regnames) nil t))) 17 | `(cons4 inst-addsub ,is-imm ,src (cons ,dst nil)))) 18 | 19 | (defmacro-lazy load (dst src) 20 | (let ((is-imm (if (position src regnames) nil t))) 21 | `(cons4 inst-load ,is-imm ,src ,dst))) 22 | 23 | (defmacro-lazy store (dst src) 24 | (let ((is-imm (if (position dst regnames) nil t))) 25 | `(cons4 inst-store ,is-imm ,dst ,src))) 26 | 27 | (defmacro-lazy jmp (jmp) 28 | (let ((is-imm (if (position jmp regnames) nil t))) 29 | `(cons4 inst-jmp ,is-imm ,jmp nil))) 30 | 31 | (defmacro-lazy jmpcmp (dst enum-cmp src -> jmp) 32 | (let ((src-is-imm (if (position src regnames) nil t)) 33 | (jmp-is-imm (if (position jmp regnames) nil t)) 34 | (enum-cmp (nth (position enum-cmp '(== <= >= != < >)) '(cmp-eq cmp-le cmp-ge cmp-ne cmp-lt cmp-gt)))) 35 | `(cons4 inst-jmpcmp ,src-is-imm ,src (cons4 ,enum-cmp ,jmp-is-imm ,jmp ,dst)))) 36 | 37 | (defmacro-lazy cmp (dst enum-cmp src) 38 | (let ((is-imm (if (position src regnames) nil t)) 39 | (enum-cmp (nth (position enum-cmp '(== <= >= != < >)) '(cmp-eq cmp-le cmp-ge cmp-ne cmp-lt cmp-gt)))) 40 | `(cons4 inst-cmp ,is-imm ,src (cons ,enum-cmp ,dst)))) 41 | 42 | (defmacro-lazy getc (reg) 43 | `(cons4 inst-io nil ,reg io-getc)) 44 | 45 | (defmacro-lazy putc (src) 46 | (let ((is-imm (if (position src regnames) nil t))) 47 | `(cons4 inst-io ,is-imm ,src io-putc))) 48 | 49 | (defmacro-lazy exit () 50 | `(cons4 inst-io nil nil io-exit)) 51 | -------------------------------------------------------------------------------- /src/lambdacraft.cl: -------------------------------------------------------------------------------- 1 | ;;=============================================================================== 2 | ;; MIT License 3 | ;; 4 | ;; Copyright (c) 2022 Hikaru Ikuta 5 | ;; 6 | ;; Permission is hereby granted, free of charge, to any person obtaining a copy 7 | ;; of this software and associated documentation files (the "Software"), to deal 8 | ;; in the Software without restriction, including without limitation the rights 9 | ;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | ;; copies of the Software, and to permit persons to whom the Software is 11 | ;; furnished to do so, subject to the following conditions: 12 | ;; 13 | ;; The above copyright notice and this permission notice shall be included in all 14 | ;; copies or substantial portions of the Software. 15 | ;; 16 | ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | ;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | ;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | ;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | ;; SOFTWARE. 23 | ;;=============================================================================== 24 | (defparameter profile-index-depth nil) 25 | (defparameter lambdacraft-loaded t) 26 | 27 | (defun islambda (expr) 28 | (and (not (atom expr)) (atom (car expr)) (eq 'lambda (car expr)))) 29 | 30 | (defun lambdaargs (expr) 31 | (car (cdr expr))) 32 | 33 | (defun lambdaarg-top (expr) 34 | (car (car (cdr expr)))) 35 | 36 | (defun lambdabody (expr) 37 | (car (cdr (cdr expr)))) 38 | 39 | (defun decorate-varname (var) 40 | (concatenate 'string "[" (write-to-string var) "]")) 41 | 42 | (defun curry (expr) 43 | (labels 44 | ((normalize-app (ret l) 45 | (if (not l) ret (normalize-app (list ret (curry (car l))) (cdr l)))) 46 | (curry-lambda (args body) 47 | `(lambda (,(car args)) 48 | ,(if (= 1 (length args)) 49 | (curry body) 50 | (curry-lambda (cdr args) body))))) 51 | (cond ((atom expr) 52 | expr) 53 | ((islambda expr) 54 | (curry-lambda (lambdaargs expr) (lambdabody expr))) 55 | ((= 1 (length expr)) 56 | (curry (car expr))) 57 | (t 58 | (normalize-app (curry (car expr)) (cdr expr)))))) 59 | 60 | (defun to-de-bruijn (body env) 61 | (labels 62 | ((lookup (env var) 63 | (let ((i (position var env :test #'equal))) 64 | (if profile-index-depth 65 | (if i (format nil "~%~d:~a~%" (+ 1 i) (write-to-string var)) 66 | (format nil "~%?:~a~%" (write-to-string var))) 67 | (if i (+ 1 i) (decorate-varname var))) 68 | ))) 69 | (if (atom body) 70 | (list (lookup env body)) 71 | (if (not (islambda body)) 72 | `(app ,@(to-de-bruijn (car body) env) ,@(to-de-bruijn (car (cdr body)) env)) 73 | `(abs ,@(to-de-bruijn (lambdabody body) (cons (lambdaarg-top body) env))))))) 74 | 75 | (defun to-blc-string (body) 76 | (labels 77 | ((int2varname (n) 78 | (if (> n 0) (concatenate 'string "1" (int2varname (- n 1))) "0")) 79 | (token2string (token) 80 | (cond ((not token) "") 81 | ((eq token 'abs) "00") 82 | ((eq token 'app) "01") 83 | ((stringp token) token) 84 | (t (int2varname token))))) 85 | (let ((curstring "")) 86 | (loop 87 | (cond 88 | ((not body) 89 | (return curstring)) 90 | (t 91 | (setq curstring (concatenate 'string curstring (token2string (car body)))) 92 | (setq body (cdr body)))))))) 93 | 94 | (defun compile-to-blc (expr) 95 | (to-blc-string (to-de-bruijn (curry expr) nil))) 96 | 97 | 98 | ;;================================================================ 99 | ;; The macro system 100 | ;;================================================================ 101 | (defparameter lazy-env (make-hash-table :test #'equal)) 102 | (defparameter lazy-var-list ()) 103 | (defparameter lazy-macro-list ()) 104 | 105 | (defmacro lazy-error (&rest message) 106 | `(error (concatenate 'string "Lazy K CL Error: " ,@message))) 107 | 108 | (defun mangle-varname (name) 109 | (cond ((stringp name) 110 | (intern (concatenate `string (write-to-string name) "-**LAZY-VAR-STR**"))) 111 | (t 112 | (intern (concatenate `string (write-to-string name) "-**LAZY-VAR**"))))) 113 | 114 | (defun mangle-macroname (name) 115 | (cond ((stringp name) 116 | (intern (concatenate `string (write-to-string name) "-**LAZY-MACRO-STR**"))) 117 | (t 118 | (intern (concatenate `string (write-to-string name) "-**LAZY-MACRO**"))))) 119 | 120 | (defmacro def-lazy (name expr) 121 | `(progn 122 | (setf lazy-var-list (cons ',name lazy-var-list)) 123 | (setf (gethash (mangle-varname ',name) lazy-env) ',expr) 124 | ',expr)) 125 | 126 | (defmacro defun-lazy (name args expr) 127 | `(progn 128 | (def-lazy ,name (lambda ,args ,expr)) 129 | '(lambda ,args ,expr))) 130 | 131 | (defmacro defmacro-lazy (name args &rest expr) 132 | (setf lazy-macro-list (cons name lazy-macro-list)) 133 | `(progn 134 | (defun ,(mangle-macroname name) ,args ,@expr) 135 | '(,name ,args))) 136 | 137 | (defun eval-lazy-var (name) 138 | (gethash (mangle-varname name) lazy-env)) 139 | 140 | (defun eval-lazy-macro (name argvalues) 141 | (apply (mangle-macroname name) argvalues)) 142 | 143 | (defun macroexpand-lazy-raw (expr env history) 144 | (cond ((atom expr) 145 | (cond ((position expr env :test #'equal) 146 | expr) 147 | ((position expr history :test #'equal) 148 | (lazy-error (format nil "Recursive expansion of macro/variable ~a. Expansion stack: ~a~%When writing recursive functions, please use anonymous recursion." expr (reverse (cons expr history))))) 149 | ((position expr lazy-var-list :test #'equal) 150 | (macroexpand-lazy-raw (eval-lazy-var expr) env (cons expr history))) 151 | (t 152 | expr))) 153 | ((islambda expr) 154 | `(lambda ,(lambdaargs expr) 155 | ,(macroexpand-lazy-raw (lambdabody expr) (append env (lambdaargs expr)) history))) 156 | ((position (car expr) lazy-macro-list :test #'equal) 157 | (macroexpand-lazy-raw (eval-lazy-macro (car expr) (cdr expr)) env history)) 158 | (t 159 | (mapcar (lambda (expr) (macroexpand-lazy-raw expr env history)) expr)))) 160 | 161 | (defmacro macroexpand-lazy (expr) 162 | `(macroexpand-lazy-raw ',expr nil nil)) 163 | 164 | 165 | (defun-lazy t (x y) x) 166 | (defun-lazy nil (x y) y) 167 | (defun-lazy cons* (x y f) (f x y)) 168 | (defun-lazy car* (l) (l t)) 169 | (defun-lazy cdr* (l) (l nil)) 170 | (defmacro-lazy car (l) `(,l t)) 171 | (defmacro-lazy cdr (l) `(,l nil)) 172 | (defmacro-lazy cons (x y) `(lambda (f) (f ,x ,y))) 173 | 174 | (defun-lazy isnil (l) ((lambda (a) (a (lambda (v n x) nil) t)) l)) 175 | (defmacro-lazy inflist (item) 176 | `((lambda (x) (x x)) 177 | (lambda (self) 178 | (cons ,item (self self))))) 179 | 180 | (defmacro-lazy not (x) `(,x nil t)) 181 | (defmacro-lazy and (x y) `(,x ,y nil)) 182 | (defmacro-lazy or (x &rest r) 183 | (if (not r) x `(,x t (or ,@r)))) 184 | 185 | (defmacro-lazy xor (x y) `(if ,x (not ,y) ,y)) 186 | (defun-lazy xnor (x y) (if x y (not y))) 187 | 188 | (defmacro-lazy succ (n) `(lambda (f x) (f (,n f x)))) 189 | (defun-lazy pred (n f x) (n ((lambda (g h) (h (g f)))) (lambda (u) x) (lambda (u) u))) 190 | (defmacro-lazy + (m n) `(lambda (f x) (,m f (,n f x)))) 191 | (defmacro-lazy - (m n) `(,n pred ,m)) 192 | (defmacro-lazy * (m n) `(lambda (f x) (,m (,n f) x))) 193 | (defmacro-lazy iszero (n) `(,n (lambda (x) nil) t)) 194 | 195 | (defmacro-lazy <= (m n) `(iszero (- ,m ,n))) 196 | (defmacro-lazy < (m n) `(<= (succ ,m) ,n)) 197 | (defmacro-lazy >= (m n) `(<= ,n ,m)) 198 | (defmacro-lazy = (m n) `(and (<= ,m ,n) (<= ,n ,m))) 199 | (defun-lazy 0 (f x) x) 200 | (defun-lazy 1 (f x) (f x)) 201 | (defun-lazy 2 (f x) (f (f x))) 202 | (def-lazy 4 ((lambda (x) (x x)) 2)) 203 | (def-lazy 8 (* 2 4)) 204 | (def-lazy 16 ((lambda (x) (x x x)) 2)) 205 | (def-lazy 32 (* 2 16)) 206 | (def-lazy 64 (* 2 32)) 207 | (def-lazy 128 (* 2 64)) 208 | (def-lazy 256 ((lambda (x) (x x)) 4)) 209 | 210 | (defmacro-lazy if (x y z) `(,x ,y ,z)) 211 | (defmacro-lazy let (argpairs body) 212 | ;; Syntax: (let ((x1 v1) (x2 v2) ...) body) 213 | (labels 214 | ((let-helper (argpairs) 215 | (cond ((not argpairs) body) 216 | (t `((lambda (,(car (car argpairs))) ,(let-helper (cdr argpairs))) 217 | ,(car (cdr (car argpairs)))))))) 218 | (let-helper argpairs))) 219 | 220 | (defmacro-lazy cond (&rest clauses) 221 | (cond ((not (cdr clauses)) 222 | (cond ((not (eq (car (car clauses)) t)) 223 | (lazy-error "No default case provided for cond")) 224 | (t (car (cdr (car clauses)))))) 225 | (t `(if ,(car (car clauses)) 226 | ,(car (cdr (car clauses))) 227 | (cond ,@(cdr clauses)))))) 228 | 229 | (defmacro-lazy list (&rest items) 230 | (if items 231 | `(cons ,(car items) (list ,@(cdr items))) 232 | `nil)) 233 | 234 | (defmacro-lazy list-tail (item &rest items) 235 | (if items 236 | `(cons ,item (list-tail ,@items)) 237 | item)) 238 | 239 | (defun-lazy nth (n list) 240 | (-> list (n cdr*) car*)) 241 | 242 | (defmacro-lazy nth (n list) 243 | `(-> ,list (,n cdr*) car*)) 244 | 245 | 246 | (defmacro-lazy letrec-lazy (name args body) 247 | `(Y-comb (lambda (,name) (lambda ,args ,body)))) 248 | 249 | (defmacro defrec-lazy (name args body) 250 | `(def-lazy ,name (letrec-lazy ,name ,args ,body))) 251 | 252 | (def-lazy Y-comb 253 | (lambda (f) 254 | ((lambda (x) (f (x x))) 255 | (lambda (x) (f (x x)))))) 256 | 257 | (defmacro-lazy -> (target &rest args) 258 | (if (not args) 259 | target 260 | `(-> (,(car args) ,target) ,@(cdr args)))) 261 | 262 | (defun-lazy take (n l) 263 | ((letrec-lazy take (n l ret) 264 | (cond 265 | ((iszero n) 266 | (reverse ret)) 267 | (t 268 | (take (pred n) (cdr l) (cons (car l) ret))))) 269 | n l nil)) 270 | 271 | (defrec-lazy length (l) 272 | ((letrec-lazy length (l n) 273 | (if (isnil l) 274 | n 275 | (length (cdr l) (succ n)))) 276 | l 0)) 277 | 278 | (defrec-lazy reverse* (l curlist) 279 | (if (isnil l) curlist (reverse* (cdr l) (cons (car l) curlist)))) 280 | (defun-lazy reverse (l) 281 | (reverse* l nil)) 282 | 283 | (defmacro-lazy if-then-return (condition then else) 284 | `(if ,condition ,then ,else)) 285 | 286 | (defmacro-lazy let* (name value body) 287 | `(let ((,name ,value)) ,body)) 288 | 289 | (defmacro-lazy do* (top &rest proc) 290 | (cond ((not proc) 291 | top) 292 | ((eq '<- (car (car proc))) 293 | (let* ((topproc (car proc)) 294 | (arglist (car (cdr topproc))) 295 | (body (car (cdr (cdr topproc))))) 296 | `(do* 297 | ,(append body `((lambda ,arglist ,top))) 298 | ,@(cdr proc)))) 299 | (t 300 | `(do* 301 | ,(append (car proc) `(,top)) 302 | ,@(cdr proc))))) 303 | 304 | (defmacro-lazy do (&rest proc) 305 | `(do* ,@(reverse proc))) 306 | 307 | (defmacro-lazy typematch-nil-cons (expr cons-args nil-case cons-case) 308 | `(,expr 309 | (lambda ,cons-args 310 | (lambda (_) ,cons-case)) 311 | ,nil-case)) 312 | 313 | 314 | ;;================================================================ 315 | ;; Lazy K support (compilation to SKI combinator calculus) 316 | ;;================================================================ 317 | (defun count-occurrences-in (expr var) 318 | (cond ((atom expr) (if (equal var expr) 1 0)) 319 | ((islambda expr) 320 | (if (equal (lambdaarg-top expr) var) 321 | 0 322 | (count-occurrences-in (cdr (cdr expr)) var))) 323 | (t (reduce '+ (mapcar (lambda (x) (count-occurrences-in x var)) expr))))) 324 | 325 | (defun occurs-freely-in (expr var) 326 | (cond ((atom expr) (equal var expr)) 327 | ((islambda expr) 328 | (if (equal (lambdaarg-top expr) var) 329 | nil 330 | (occurs-freely-in (cdr (cdr expr)) var))) 331 | (t (or (occurs-freely-in (car expr) var) 332 | (occurs-freely-in (cdr expr) var))))) 333 | 334 | (defun t-rewrite (expr) 335 | (cond ((atom expr) expr) 336 | ((equal 'lambda (car expr)) 337 | (let ((arg (lambdaarg-top expr)) 338 | (body (lambdabody expr))) 339 | (cond ((equal arg body) 'I-comb**) 340 | ((not (occurs-freely-in body arg)) 341 | `(K-comb** ,(t-rewrite body))) 342 | ((islambda body) 343 | (t-rewrite `(lambda (,arg) ,(t-rewrite body)))) 344 | (t `((S-comb** ,(t-rewrite `(lambda (,arg) ,(car body)))) 345 | ,(t-rewrite `(lambda (,arg) ,(car (cdr body))))))))) 346 | (t (mapcar #'t-rewrite expr)))) 347 | 348 | (defun flatten-ski (expr) 349 | (if (atom expr) 350 | (cond 351 | ((eq expr 'S-comb**) "s") 352 | ((eq expr 'K-comb**) "k") 353 | ((eq expr 'I-comb**) "i") 354 | (t (decorate-varname expr))) 355 | (concatenate `string "`" (flatten-ski (car expr)) (flatten-ski (car (cdr expr)))))) 356 | 357 | (defun compile-to-ski (expr) 358 | (flatten-ski (t-rewrite (curry expr)))) 359 | 360 | 361 | (defun rewrite-ski (expr) 362 | (if (atom expr) 363 | (cond 364 | ((eq expr 'S-comb**) "S") 365 | ((eq expr 'K-comb**) "K") 366 | ((eq expr 'I-comb**) "I") 367 | (t (decorate-varname expr))) 368 | (concatenate `string "(" (rewrite-ski (car expr)) (rewrite-ski (car (cdr expr))) ")"))) 369 | 370 | (defun compile-to-ski-parens (expr) 371 | (rewrite-ski (t-rewrite (curry expr)))) 372 | 373 | 374 | ;;================================================================ 375 | ;; Additional compilers 376 | ;;================================================================ 377 | (defparameter plaintext-lambda-env-vars 378 | (list 379 | "x" "y" "z" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" 380 | "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" 381 | "α" "β" "γ" "δ" "ε" "ζ" "η" "θ" "κ" "μ" "ν" "ξ" "π" "ρ" "σ" "τ" "υ" "φ" "χ" "ψ" "ω")) 382 | 383 | (defun int-to-alphabet (i) 384 | (if (< i (length plaintext-lambda-env-vars)) 385 | (nth i plaintext-lambda-env-vars) 386 | (format nil "~a_~a" 387 | (nth (mod i (length plaintext-lambda-env-vars)) plaintext-lambda-env-vars) 388 | (floor i (length plaintext-lambda-env-vars))))) 389 | 390 | (defun lambda-compiler-builder (app-format-var app-format-app abs-format) 391 | (let ((compiler ())) 392 | (setq compiler 393 | (lambda (body &optional (env ())) 394 | (labels 395 | ((lookup (env var) 396 | (let ((i (position var (reverse env) :test #'equal))) 397 | (if i 398 | (int-to-alphabet i) 399 | (decorate-varname var))))) 400 | (cond 401 | ((atom body) 402 | (lookup env body)) 403 | ((not (islambda body)) 404 | (format nil 405 | "(~a ~a)" 406 | (let ((s (funcall compiler (car body) env))) 407 | (if (and (not (atom (car body))) (not (islambda (car body)))) 408 | (subseq s 1 (- (length s) 1)) 409 | s)) 410 | (funcall compiler (car (cdr body)) env))) 411 | (t 412 | (format nil abs-format 413 | (lookup (cons (lambdaarg-top body) env) (lambdaarg-top body)) 414 | (let ((s (funcall compiler (lambdabody body) (cons (lambdaarg-top body) env)))) 415 | (if (islambda (lambdabody body)) 416 | (subseq s 1 (- (length s) 1)) 417 | s)))))))))) 418 | 419 | ;; (defparameter to-plaintext-lambda* (lambda-compiler-builder "(~a ~a)" "((~a) ~a)" "\\~a.~a")) 420 | ;; (defun to-plaintext-lambda (&rest args) 421 | ;; (apply to-plaintext-lambda* args)) 422 | 423 | (defparameter to-lam* (lambda-compiler-builder "(~a ~a)" "(~a ~a)" "(\\~a.~a)")) 424 | (defun to-lam (&rest args) 425 | (apply to-lam* args)) 426 | 427 | ;; (defparameter to-js-arrow* (lambda-compiler-builder "~a(~a)" "(~a)(~a)" "(~a) => ~a")) 428 | ;; (defun to-js-arrow (&rest args) 429 | ;; (apply to-js-arrow* args)) 430 | 431 | ;; (defparameter to-js* (lambda-compiler-builder "~a(~a)" "(~a)(~a)" "function (~a) { return ~a; }")) 432 | ;; (defun to-js (&rest args) 433 | ;; (apply to-js* args)) 434 | 435 | ;; (defparameter to-python* (lambda-compiler-builder "~a(~a)" "(~a)(~a)" "lambda ~a: ~a")) 436 | ;; (defun to-python (&rest args) 437 | ;; (apply to-python* args)) 438 | 439 | ;; (defun compile-to-js (expr) 440 | ;; (to-js (curry expr))) 441 | 442 | ;; (defun compile-to-js-arrow (expr) 443 | ;; (to-js-arrow (curry expr))) 444 | 445 | ;; (defun compile-to-python (expr) 446 | ;; (to-python (curry expr))) 447 | 448 | ;; (defun compile-to-plaintext-lambda (expr) 449 | ;; (to-plaintext-lambda (curry expr))) 450 | 451 | (defun compile-to-lam (expr) 452 | (to-lam (curry expr))) 453 | 454 | 455 | ;;================================================================ 456 | ;; Utilities 457 | ;;================================================================ 458 | ;; (defmacro compile-to-plaintext-lambda-lazy (expr-lazy) 459 | ;; `(compile-to-plaintext-lambda (macroexpand-lazy ,expr-lazy))) 460 | 461 | (defmacro compile-to-lam-lazy (expr-lazy) 462 | `(compile-to-lam (macroexpand-lazy ,expr-lazy))) 463 | 464 | (defmacro compile-to-blc-lazy (expr-lazy) 465 | `(compile-to-blc (macroexpand-lazy ,expr-lazy))) 466 | 467 | (defmacro compile-to-ski-lazy (expr-lazy) 468 | `(compile-to-ski (macroexpand-lazy ,expr-lazy))) 469 | 470 | (defmacro compile-to-ski-parens-lazy (expr-lazy) 471 | `(compile-to-ski-parens (macroexpand-lazy ,expr-lazy))) 472 | 473 | ;; (defmacro compile-to-js-lazy (expr-lazy) 474 | ;; `(compile-to-js (macroexpand-lazy ,expr-lazy))) 475 | 476 | ;; (defmacro compile-to-js-arrow-lazy (expr-lazy) 477 | ;; `(compile-to-js-arrow (macroexpand-lazy ,expr-lazy))) 478 | 479 | ;; (defmacro compile-to-python-lazy (expr-lazy) 480 | ;; `(compile-to-python (macroexpand-lazy ,expr-lazy))) 481 | 482 | (defmacro compile-to-lisp-lazy (expr-lazy) 483 | `(progn 484 | (setq *print-pretty* nil) 485 | (write-to-string (macroexpand-lazy ,expr-lazy)))) 486 | 487 | (defmacro compile-to-lisp-pretty-lazy (expr-lazy) 488 | `(progn 489 | (setq *print-pretty* t) 490 | (write-to-string (macroexpand-lazy ,expr-lazy)))) 491 | -------------------------------------------------------------------------------- /src/lambdavm.cl: -------------------------------------------------------------------------------- 1 | ;;=============================================================================== 2 | ;; MIT License 3 | ;; 4 | ;; Copyright (c) 2022 Hikaru Ikuta 5 | ;; 6 | ;; Permission is hereby granted, free of charge, to any person obtaining a copy 7 | ;; of this software and associated documentation files (the "Software"), to deal 8 | ;; in the Software without restriction, including without limitation the rights 9 | ;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | ;; copies of the Software, and to permit persons to whom the Software is 11 | ;; furnished to do so, subject to the following conditions: 12 | ;; 13 | ;; The above copyright notice and this permission notice shall be included in all 14 | ;; copies or substantial portions of the Software. 15 | ;; 16 | ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | ;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | ;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | ;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | ;; SOFTWARE. 23 | ;;=============================================================================== 24 | (load "./src/lambdacraft.cl") 25 | (load "./src/blc-numbers.cl") 26 | 27 | 28 | 29 | ;;================================================================ 30 | ;; Memory and program 31 | ;;================================================================ 32 | (defrec-lazy lookup-tree* (memory address cont) 33 | (typematch-nil-cons memory (car-memory cdr-memory) 34 | ;; nil case 35 | (cont int-zero) 36 | ;; cons case 37 | (typematch-nil-cons address (car-address cdr-address) 38 | ;; nil case 39 | (cont memory) 40 | ;; cons case 41 | ((if car-address 42 | (lookup-tree* car-memory) 43 | (lookup-tree* cdr-memory)) 44 | cdr-address 45 | cont)))) 46 | 47 | (defrec-lazy memory-write* (memory address value cont) 48 | (typematch-nil-cons address (car-address cdr-address) 49 | ;; nil case 50 | (cont value) 51 | ;; cons case 52 | (do 53 | (<- (memory-rewritten memory-orig) 54 | (do 55 | (<- (memory-target) 56 | ((lambda (cont) 57 | (typematch-nil-cons memory (car-memory cdr-memory) 58 | ;; nil case 59 | (cont nil nil) 60 | ;; cons case 61 | (cond 62 | (car-address 63 | (memory cont)) 64 | (t 65 | (cont cdr-memory car-memory) ;; Implicit parameter passing: memory-orig ? 66 | )))))) 67 | (memory-write* memory-target cdr-address value))) 68 | (if car-address 69 | (cont (cons memory-rewritten memory-orig)) 70 | (cont (cons memory-orig memory-rewritten)))))) 71 | 72 | (defmacro-lazy eval-bool (expr) 73 | `(lambda (cont) 74 | (if ,expr 75 | (cont t) 76 | (cont nil)))) 77 | 78 | (defrec-lazy add* (initcarry is-add n m cont) 79 | (typematch-nil-cons n (car-n cdr-n) 80 | ;; nil case 81 | (cont initcarry n) 82 | ;; cons case 83 | (do 84 | (<- (car-m cdr-m) (m)) 85 | (<- (carry curlist) (add* initcarry is-add cdr-n cdr-m)) 86 | (let* not-carry (not carry)) 87 | (let* car-m (if is-add car-m (not car-m))) 88 | (let* f (lambda (a b) 89 | (if car-n 90 | (if car-m a b) 91 | (if car-m b a)))) 92 | (<- (curbit nextcarry) 93 | ((lambda (cont) 94 | (do 95 | ((eval-bool (f car-m carry))) 96 | (if (f carry not-carry) 97 | (cont t) 98 | (cont nil)))))) 99 | (cont nextcarry (cons curbit curlist))))) 100 | 101 | 102 | 103 | ;;================================================================ 104 | ;; Arithmetic 105 | ;;================================================================ 106 | (defun-lazy cmpret-eq (r1 r2 r3) r1) 107 | (defun-lazy cmpret-lt (r1 r2 r3) r2) 108 | (defun-lazy cmpret-gt (r1 r2 r3) r3) 109 | 110 | (defrec-lazy cmp* (n m) 111 | (typematch-nil-cons n (car-n cdr-n) 112 | ;; nil case 113 | cmpret-eq 114 | ;; cons case 115 | (do 116 | (<- (car-m cdr-m) (m)) 117 | (let* next (cmp* cdr-n cdr-m)) 118 | (if car-n 119 | (if car-m 120 | next 121 | cmpret-lt) 122 | (if car-m 123 | cmpret-gt 124 | next))))) 125 | 126 | (defun-lazy cmp-gt (f) (f nil nil t)) 127 | (defun-lazy cmp-lt (f) (f nil t nil)) 128 | (defun-lazy cmp-eq (f) (f t nil nil)) 129 | (defun-lazy cmp-le (f) (f t t nil)) 130 | (defun-lazy cmp-ge (f) (f t nil t)) 131 | (defun-lazy cmp-ne (f) (f nil t t)) 132 | 133 | (defmacro-lazy compare (n m enum-cmp) 134 | `(,enum-cmp (cmp* ,n ,m))) 135 | 136 | 137 | ;;================================================================ 138 | ;; I/O 139 | ;;================================================================ 140 | (defmacro-lazy io-bitlength-to-wordsize (n) 141 | `(supp-bitlength (lambda (x f) (f t x)) ,n)) 142 | 143 | (defmacro-lazy wordsize-to-io-bitlength (n) 144 | `(supp-bitlength cdr* ,n)) 145 | 146 | 147 | ;;================================================================ 148 | ;; Evaluation 149 | ;;================================================================ 150 | (defun-lazy lookup-src-if-imm* (src-is-imm *src cont) 151 | (if src-is-imm 152 | (cont *src) 153 | (regread *src cont))) ;; regread is defined in eval 154 | 155 | ;; Checks if curblock is { t, nil } (returns t) or a cons cell (returns nil). 156 | (defmacro-lazy is-t-or-nil (expr) 157 | `(,expr (lambda (a b) t) (lambda (a) a) (lambda (a) t) nil)) 158 | 159 | 160 | (defrec-lazy eval (memory stdin curblock curproglist reg) 161 | (do 162 | (let* jumpto 163 | (lambda (jmp) 164 | (do 165 | (<- (proglist) (lookup-tree* progtree jmp)) 166 | ((proglist (eval memory stdin)) reg)))) 167 | (let* regwrite (memory-write* reg)) 168 | (let* regread (lookup-tree* reg)) 169 | (cond 170 | ((is-t-or-nil curblock) 171 | (typematch-nil-cons curproglist (car-curproglist cdr-curproglist) 172 | ;; nil case 173 | curproglist 174 | ;; cons case 175 | ((curproglist (eval memory stdin)) reg))) 176 | (t 177 | (do 178 | (<- (curinst nextblock) (curblock)) 179 | (let* eval-reg (eval memory stdin nextblock curproglist)) 180 | (<- (inst-type src-is-imm *src) (curinst)) ;; Delayed destruction: *dst 181 | (<- (src) (lookup-src-if-imm* src-is-imm *src)) 182 | (lambda (*dst) 183 | **instruction-typematch**)))))) 184 | 185 | 186 | ;;================================================================ 187 | ;; Instructions 188 | ;;================================================================ 189 | (defun-lazy inst-io (i1 i2 i3 i4 i5 i6 i7 i8) i1) 190 | (defun-lazy inst-jmpcmp (i1 i2 i3 i4 i5 i6 i7 i8) i2) 191 | (defun-lazy inst-cmp (i1 i2 i3 i4 i5 i6 i7 i8) i3) 192 | (defun-lazy inst-jmp (i1 i2 i3 i4 i5 i6 i7 i8) i4) 193 | (defun-lazy inst-load (i1 i2 i3 i4 i5 i6 i7 i8) i5) 194 | (defun-lazy inst-store (i1 i2 i3 i4 i5 i6 i7 i8) i6) 195 | (defun-lazy inst-addsub (i1 i2 i3 i4 i5 i6 i7 i8) i7) 196 | (defun-lazy inst-mov (i1 i2 i3 i4 i5 i6 i7 i8) i8) 197 | 198 | (def-lazy **instruction-typematch** 199 | (inst-type 200 | io-case 201 | jmpcmp-case 202 | cmp-case 203 | jmp-case 204 | load-case 205 | store-case 206 | addsub-case 207 | mov-case)) 208 | 209 | (defun-lazy io-getc (x1 x2 x3) x1) 210 | (defun-lazy io-putc (x1 x2 x3) x2) 211 | (defun-lazy io-exit (x1 x2 x3) x3) 212 | 213 | (defmacro-lazy cons4 (x1 x2 x3 x4) 214 | `(lambda (f) (f ,x1 ,x2 ,x3 ,x4))) 215 | 216 | 217 | (def-lazy addsub-case 218 | ;; Instruction structure: (cons4 inst-add [src-isimm] [src] (cons [*dst] is-add)) 219 | ((do 220 | (<- (*dst is-add) (*dst)) 221 | (<- (carry) ;; Implicit parameter passing: sum 222 | ((do 223 | (regread *dst) ;; Implicit parameter passing: dst 224 | (add* is-add is-add)) 225 | src)) ;; Applies src to the preceding add* 226 | (regwrite *dst)) eval-reg)) 227 | 228 | (def-lazy store-case 229 | ;; Instruction structure: (cons4 inst-store [dst-isimm] [dst-memory] [source]) 230 | ;; Note that the destination is stored in the variable *src 231 | (((regread *dst 232 | (memory-write* memory src)) 233 | eval) 234 | stdin nextblock curproglist reg)) 235 | 236 | (def-lazy mov-case 237 | ;; Instruction structure:: (cons4 inst-mov [src-isimm] [src] [dst]) 238 | (regwrite *dst src eval-reg)) 239 | 240 | (def-lazy jmp-case 241 | ;; Instruction structure:: (cons4 inst-jmp [jmp-isimm] [jmp] _) 242 | (jumpto src)) 243 | 244 | (def-lazy jmpcmp-case 245 | ;; Instruction structure: (cons4 inst-jmpcmp [src-isimm] [src] (cons4 [enum-cmp] [jmp-isimm] [jmp] [*dst])) 246 | (do 247 | (<- (enum-cmp jmp-is-imm *jmp *cmp-dst) (*dst)) 248 | (lookup-src-if-imm* jmp-is-imm *jmp) ;; Implicit parameter passing: jmp 249 | (regread *cmp-dst) ;; Implicit parameter passing: dst-value 250 | (lambda (dst-value jmp) 251 | (if (compare dst-value src enum-cmp) 252 | (jumpto jmp) 253 | (eval-reg reg))))) 254 | 255 | (def-lazy load-case 256 | ;; Instruction structure: (cons4 inst-load [src-isimm] [src] [*dst]) 257 | (do 258 | (((lookup-tree* memory src) 259 | (regwrite *dst)) 260 | eval-reg))) 261 | 262 | (def-lazy cmp-case 263 | ;; Instruction structure: (cons4 inst-cmp [src-isimm] [src] (cons [emum-cmp] [dst])) 264 | ((do 265 | (<- (enum-cmp dst) (*dst)) 266 | (let* int-zero int-zero) ;; Share references to save space 267 | (<- (carry) (add* nil (enum-cmp ((regread dst cmp*) src)) int-zero int-zero)) ;; Implicit parameter passing: sum 268 | (regwrite dst)) eval-reg)) 269 | 270 | (def-lazy io-case 271 | ;; Instruction structure: 272 | ;; getc: (cons4 inst-io nil [dst] io-getc) 273 | ;; putc: (cons4 inst-io [src-isimm] [src] io-putc) 274 | ;; exit: (cons4 inst-io nil nil io-exit) 275 | ;; For `exit`, the control flow depends on the second term, so it must be set to `nil`. 276 | ;; Typematch over the inst. type 277 | (*dst 278 | ;; getc 279 | (do 280 | (<- (c stdin) 281 | ((lambda (return) 282 | (typematch-nil-cons stdin (car-stdin cdr-stdin) 283 | ;; nil case 284 | (return int-zero stdin) 285 | ;; cons case 286 | (return (io-bitlength-to-wordsize car-stdin) cdr-stdin))))) 287 | (regwrite *src c) ;; Implicit parameter passing: reg 288 | (eval memory stdin nextblock curproglist)) 289 | ;; putc 290 | (do 291 | (cons (wordsize-to-io-bitlength src) (eval-reg reg))) 292 | ;; exit 293 | src-is-imm)) ;; always evaluates to nil 294 | 295 | (defrec-lazy list2tree** (l depth cont) 296 | (typematch-nil-cons l (_ _) 297 | ;; nil case 298 | (cont l l) 299 | ;; cons case 300 | (typematch-nil-cons depth (_ cdr-depth) 301 | ;; nil case 302 | (l cont) 303 | ;; cons case 304 | (do 305 | (<- (right-tree l) (list2tree** l cdr-depth)) 306 | (<- (left-tree) (list2tree** l cdr-depth)) ;; Implicit parameter passing: l 307 | (cont (cons right-tree left-tree)))))) 308 | 309 | (defrec-lazy cdr-generator (l) 310 | (typematch-nil-cons l (_ cdr-l) 311 | ;; nil case 312 | l 313 | ;; cons case 314 | (cons l (cdr-generator cdr-l)))) 315 | 316 | (def-lazy initreg nil) 317 | 318 | (defmacro def-lambdaVM () 319 | `(defun-lazy lambdaVM (io-bitlength supp-bitlength memlist proglist stdin) 320 | (do 321 | ;; Share references to functions to prevent them from being inlined multiple times 322 | (let* int-zero 323 | (let ((cons-t (lambda (x f) (f t x)))) 324 | (supp-bitlength cons-t (io-bitlength cons-t nil)))) 325 | ;; Remove reference sharing when compiling to Lazy K, which leads to a shorter code 326 | ,@(cond 327 | ((boundp 'compile-ski) 328 | nil) 329 | (t 330 | `((let* Y-comb Y-comb) 331 | (let* cmp* cmp*)))) 332 | (let* add* add*) 333 | (let* memory-write* memory-write*) 334 | (let* lookup-tree* lookup-tree*) 335 | ;; Implicit parameter passing of memtree and progtree: 336 | ;; ((proglist (eval memtree stdin)) initreg) 337 | ((proglist 338 | (((do 339 | (let* list2tree* 340 | (lambda (l cont) 341 | (do 342 | (<- (tree _) (list2tree** l int-zero)) 343 | (cont tree)))) 344 | (<- (progtree) (list2tree* (cdr-generator proglist))) 345 | (list2tree* memlist) ;; Implicit argument passing: memtree 346 | (eval))) 347 | stdin)) 348 | initreg)))) 349 | 350 | (def-lambdaVM) 351 | -------------------------------------------------------------------------------- /src/library.cl: -------------------------------------------------------------------------------- 1 | (load "./src/lambdavm.cl") 2 | 3 | 4 | (def-lazy reg-A (list t)) 5 | (def-lazy reg-B (list nil t t)) 6 | (def-lazy reg-SP (list nil t nil)) 7 | (def-lazy reg-D (list nil nil t)) 8 | (def-lazy reg-BP (list nil nil nil t)) 9 | (def-lazy reg-C (list nil nil nil nil)) 10 | 11 | (defmacro-lazy cons (x y) `(lambda (f) (f ,x ,y))) 12 | 13 | (defun-lazy return-tree (x cont) 14 | (if x 15 | (cont a) 16 | (cont b))) 17 | 18 | ;; (defrec-lazy string-concatenator (curstr x) 19 | ;; (cond 20 | ;; ((isnil x) 21 | ;; curstr) 22 | ;; (t 23 | ;; (string-concatenator (cons x curstr))))) 24 | 25 | (defrec-lazy string-concatenator (curstr x) 26 | (x 27 | (lambda (a b _) 28 | (string-concatenator (cons x curstr))) 29 | curstr)) 30 | 31 | (defrec-lazy inst-concatenator (curstr x) 32 | (x 33 | (lambda (a b c d _) 34 | (inst-concatenator (cons x curstr))) 35 | curstr)) 36 | 37 | ;; (defrec-lazy num-concatenator (curstr x) 38 | ;; (x 39 | ;; (lambda (bit _) 40 | ;; (num-concatenator (cons bit curstr))) 41 | ;; curstr)) 42 | 43 | (defun-lazy list24 (b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14 b15 b16 b17 b18 b19 b20 b21 b22 b23 b24) 44 | (list b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12 b13 b14 b15 b16 b17 b18 b19 b20 b21 b22 b23 b24)) 45 | 46 | (def-lazy text-header 47 | (do 48 | (let* string-concatenator string-concatenator) 49 | (let* reverse reverse) 50 | expr)) 51 | 52 | (def-lazy data-header 53 | (do 54 | (let* cons-nil (lambda (x f) (f nil x))) 55 | (let* cons-t (lambda (x f) (f t x))) 56 | expr)) 57 | 58 | (defparameter formlist `( 59 | (cons x y) 60 | (cons4 x1 x2 x3 x4) 61 | t 62 | nil 63 | 64 | ;; (cons (lambda (x) x) return-tree) 65 | ;; (lambda (f) (f f return-tree)) 66 | ;; (new-bintree-node a b) 67 | 68 | string-concatenator 69 | inst-concatenator 70 | reverse 71 | data-header 72 | text-header 73 | 74 | reg-A 75 | reg-B 76 | reg-C 77 | reg-D 78 | reg-SP 79 | reg-BP 80 | 81 | ;; (list nil nil nil) 82 | ;; (list t nil nil) 83 | ;; (list nil t nil) 84 | ;; (list t t nil) 85 | ;; (list nil nil t) 86 | ;; (list t nil t) 87 | 88 | inst-exit 89 | inst-io 90 | inst-jmpcmp 91 | inst-cmp 92 | inst-jmp 93 | inst-load 94 | inst-store 95 | inst-addsub 96 | inst-mov 97 | ;; inst-nand 98 | ;; inst-rshift 99 | 100 | cmp-gt 101 | cmp-lt 102 | cmp-eq 103 | cmp-le 104 | cmp-ge 105 | cmp-ne 106 | 107 | io-getc 108 | io-putc 109 | io-exit 110 | 111 | ((lambda (cons-t cons-nil) A) (lambda (x f) (f t x)) (lambda (x f) (f nil x))) 112 | ;; (lambda (x) (x (lambda (a b c d) t) (lambda (b c d) nil) x x x x)) 113 | ;; (lambda (x) (x (lambda (a b) t) (lambda (a) a) x nil)) 114 | 115 | 16 116 | 8 117 | ;; (lambda (f x) (f (f (f (f (f (f (f (f x))))))))) 118 | ;; (+ 4 4) 119 | ;; (lambda (f x) ((lambda (f x) (f (f x))) (lambda (f x) (f (f (f x)))) f x)) 120 | 121 | ;; (lambda (x) (x (lambda (a b) t) (lambda (b) nil) x x)) 122 | 123 | ;; (lambda (x) (x (lambda (a b) t) i x nil)) 124 | ;; (x (lambda (a b) t) i z nil) 125 | ;; SYS-STRING-TERM 126 | 127 | ((letrec-lazy loop (x) (loop nil)) nil) 128 | )) 129 | 130 | 131 | 132 | ;; 8 : 000001010000011100111010010100011010000001110011101011010 133 | ;; (LAMBDA (F X) (F (F (F (F (F (F (F (F X))))))))): 0000011100111001110011100111001110011100111010 134 | ;; (+ 4 4) : 000001010100011010000001110011101011001010100011010000001110011101011010 135 | ;; 0000010101000001110011101000000111001110011101011010 136 | 137 | 138 | ;; (LAMBDA (X) (X (LAMBDA (A B C D) T) (LAMBDA (B C D) NIL) X X X X)): 000101010101011000000000000011000000000001010101010 139 | ;; (LAMBDA (X) (X (LAMBDA (A B) T) (LAMBDA (A) A) X NIL)) : 00010101011000000000110001010000010 140 | 141 | ;; (LAMBDA (X) (X (LAMBDA (A B C D) T) (LAMBDA (B C D) NIL) X X X X)): 000101010101011000000000000011000000000001010101010 142 | ;; (LAMBDA (X) (X (LAMBDA (A B) T) (LAMBDA (B) NIL) X X)) : 00010101011000000000110000000101010 143 | ;; (LAMBDA (X) (X (LAMBDA (A B) T) I X NIL)) : 00010101011000000000110001010000010 144 | ;; (X (LAMBDA (A B) T) I Z NIL) : 01010101[X]000000001100010[Z]000010 145 | 146 | 147 | ;; item (lambda (a b) z) e a b 148 | 149 | ;; z e a b = nil 150 | ;; z = t 151 | 152 | ;; t e a b = nil 153 | 154 | ;; e b = nil 155 | 156 | ;; e = i, b = nil 157 | 158 | ;; (lambda (x) (x (lambda (a b) t) i x nil)) 159 | 160 | 161 | 162 | ;; (defun print-expression (expr) 163 | ;; (format t (concatenate 'string (write-to-string expr)": " (eval `(compile-to-ski-lazy ,expr)))) 164 | ;; (terpri)) 165 | 166 | ;; (defun print-expression (expr) 167 | ;; (format t (concatenate 'string (write-to-string expr)": " (eval `(compile-to-blc-lazy ,expr)))) 168 | ;; (terpri)) 169 | 170 | (defun print-expression (expr) 171 | (format t (concatenate 'string (write-to-string expr)": " (eval `(compile-to-lam-lazy ,expr)))) 172 | (terpri)) 173 | 174 | (mapcar #'print-expression formlist) 175 | -------------------------------------------------------------------------------- /src/main-blc.cl: -------------------------------------------------------------------------------- 1 | (load "./src/lambdavm.cl") 2 | 3 | 4 | (format t (compile-to-blc-lazy lambdaVM)) 5 | -------------------------------------------------------------------------------- /src/main-lazy.cl: -------------------------------------------------------------------------------- 1 | (defparameter compile-ski t) 2 | 3 | (load "./src/lambdavm.cl") 4 | (load "./src/blc-clamb-wrapper.cl") 5 | 6 | 7 | (defun-lazy main (io-bitlength supp-bitlength memlist proglist stdin) 8 | (blcstr-to-lazykstr (lambdaVM io-bitlength supp-bitlength memlist proglist (lazykstr-to-blcstr stdin)))) 9 | 10 | (format t (compile-to-lam-lazy main)) 11 | -------------------------------------------------------------------------------- /src/main-ulamb.lisp: -------------------------------------------------------------------------------- 1 | (load "./src/lambdavm.cl") 2 | 3 | 4 | (defun-lazy main (a b memlist proglist stdin) 5 | (blcstr-to-ulambstr (lambdaVM 6 | a b 7 | memlist proglist (ulambstr-to-blcstr stdin)))) 8 | 9 | (format t (compile-to-blc-lazy main)) 10 | -------------------------------------------------------------------------------- /src/main.cl: -------------------------------------------------------------------------------- 1 | (load "./src/lambdavm.cl") 2 | 3 | 4 | (format t (compile-to-lam-lazy lambdaVM)) 5 | -------------------------------------------------------------------------------- /test/fizzbuzz.cl.out: -------------------------------------------------------------------------------- 1 | * 2 | ** 3 | Fizz 4 | **** 5 | Buzz 6 | Fizz 7 | ***** ** 8 | ***** *** 9 | Fizz 10 | Buzz 11 | ***** ***** * 12 | Fizz 13 | ***** ***** *** 14 | ***** ***** **** 15 | FizzBuzz 16 | ***** ***** ***** * 17 | ***** ***** ***** ** 18 | Fizz 19 | ***** ***** ***** **** 20 | Buzz 21 | -------------------------------------------------------------------------------- /test/rot13.cl.in: -------------------------------------------------------------------------------- 1 | Hello, world! 2 | This is the test input for rot13.cl. 3 | -------------------------------------------------------------------------------- /test/rot13.cl.out: -------------------------------------------------------------------------------- 1 | Uryyb, jbeyq! 2 | Guvf vf gur grfg vachg sbe ebg13.py. 3 | -------------------------------------------------------------------------------- /test/yes.cl.out: -------------------------------------------------------------------------------- 1 | y 2 | y 3 | y 4 | y 5 | y 6 | y 7 | y 8 | y 9 | y 10 | y 11 | y 12 | y 13 | y 14 | y 15 | y 16 | y 17 | y 18 | y 19 | y 20 | y 21 | -------------------------------------------------------------------------------- /tools/main.tex: -------------------------------------------------------------------------------- 1 | \documentclass[article, 12pt]{article} 2 | \usepackage[letterpaper]{geometry} 3 | \renewcommand{\baselinestretch}{1.5} 4 | \addtolength{\textwidth}{0.2in} 5 | \addtolength{\textheight}{1.5in} 6 | \setlength{\hoffset}{-0.1in} 7 | \setlength{\voffset}{-0.4in} 8 | \setlength{\topmargin}{0pt} 9 | \setlength{\headheight}{0pt} 10 | \setlength{\headsep}{0pt} 11 | \title{LambdaVM - A Virtual CPU Written as an \\ Untyped Lambda Calculus Term} 12 | \author{Hikaru Ikuta} 13 | \date{https://woodrush.github.io/} 14 | \begin{document} 15 | \maketitle 16 | ${\rm LambdaVM} =$ 17 | \input{lambdavm.tex} 18 | \begin{thebibliography}{A99} 19 | \bibitem[1]{1} GitHub repository for LambdaVM: https://github.com/woodrush/lambdavm 20 | \end{thebibliography} 21 | \end{document} 22 | -------------------------------------------------------------------------------- /tools/make-latex.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | cat lambdavm.lam \ 5 | | LC_ALL=C sed -e "s/^/$/g" \ 6 | | LC_ALL=C sed -e "s/$/$ @/g" \ 7 | | LC_ALL=C sed -e "s/\\\\/@\\\\allowbreak \\\\lambda /g" \ 8 | | LC_ALL=C sed -e "s/(/@\\\\allowbreak (/g" \ 9 | | LC_ALL=C sed -e "s/)/@) \\\\allowbreak /g" \ 10 | | LC_ALL=C sed -e "s/\./.\\\\allowbreak /g" \ 11 | | LC_ALL=C sed -e "s/α/@\\\\allowbreak \\\\alpha /g" \ 12 | | LC_ALL=C sed -e "s/β/@\\\\allowbreak \\\\beta /g" \ 13 | | LC_ALL=C sed -e "s/γ/@\\\\allowbreak \\\\gamma /g" \ 14 | | LC_ALL=C sed -e "s/δ/@\\\\allowbreak \\\\delta /g" \ 15 | | LC_ALL=C sed -e "s/ε/@\\\\allowbreak \\\\varepsilon /g" \ 16 | | LC_ALL=C sed -e "s/ζ/@\\\\allowbreak \\\\zeta /g" \ 17 | | LC_ALL=C sed -e "s/η/@\\\\allowbreak \\\\eta /g" \ 18 | | LC_ALL=C sed -e "s/θ/@\\\\allowbreak \\\\theta /g" \ 19 | | LC_ALL=C sed -e "s/κ/@\\\\allowbreak \\\\kappa /g" \ 20 | | LC_ALL=C sed -e "s/μ/@\\\\allowbreak \\\\mu /g" \ 21 | | LC_ALL=C sed -e "s/ν/@\\\\allowbreak \\\\nu /g" \ 22 | | LC_ALL=C sed -e "s/ξ/@\\\\allowbreak \\\\xi /g" \ 23 | | LC_ALL=C sed -e "s/π/@\\\\allowbreak \\\\pi /g" \ 24 | | LC_ALL=C sed -e "s/ρ/@\\\\allowbreak \\\\rho /g" \ 25 | | LC_ALL=C sed -e "s/σ/@\\\\allowbreak \\\\sigma /g" \ 26 | | LC_ALL=C sed -e "s/τ/@\\\\allowbreak \\\\tau /g" \ 27 | | LC_ALL=C sed -e "s/υ/@\\\\allowbreak \\\\upsilon /g" \ 28 | | LC_ALL=C sed -e "s/φ/@\\\\allowbreak \\\\varphi /g" \ 29 | | LC_ALL=C sed -e "s/χ/@\\\\allowbreak \\\\chi /g" \ 30 | | LC_ALL=C sed -e "s/ψ/@\\\\allowbreak \\\\psi /g" \ 31 | | LC_ALL=C sed -e "s/ω/@\\\\allowbreak \\\\omega /g" \ 32 | | LC_ALL=C tr "@" "\n" \ 33 | > lambdavm.tex 34 | --------------------------------------------------------------------------------