├── LICENSE ├── README.org └── src ├── main.s └── ski.sh /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2020, Yul3n 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: SKI in asm 2 | An interpreter for extended SKI combinators (including C, B, S', C', B* plus for 3 | Z (zero) and U (succ) for church numerals). The comportment of each combinator 4 | and an algorithm to translate lambda calculus yo those combinators can be found 5 | [[https://www.microsoft.com/en-us/research/wp-content/uploads/1987/01/slpj-book-1987-small.pdf][here]]. This repository is inspired by [[https://crypto.stanford.edu/~blynn/lambda/sk.html][this]] blog post by Ben Lynn. 6 | * How to use 7 | Each combinator is represented by a number from 0 to 9, any other number is an application. 8 | | Number | Combinator | 9 | |--------+------------| 10 | | 0 | S | 11 | | 1 | K | 12 | | 2 | U | 13 | | 3 | Z | 14 | | 4 | I | 15 | | 5 | B | 16 | | 6 | C | 17 | | 7 | S' | 18 | | 8 | B* | 19 | | 9 | C' | 20 | To create a program you need to lists, the stack which represents the program 21 | and the nodelist which is list of internal nodes (application). The nodelist 22 | consist of a list of pair of two values, they can be combinators or other 23 | internal nodes. An internal node is described by a number n superior to ten. It 24 | is the application of nodelist[n - 10] and nodelist[n - 9]. For instance, UZ can 25 | be compiled as: stack = [3, 2] (note, the stack list has to be in reverse order) 26 | and nodelist = [] or stack = [10] and nodelist = [2, 3]. To create a file with 27 | that does that you can use the script src/ski.sh. You can invoke it by giving it 28 | as an argument the name of a file which contains 2 lines, the stack and the 29 | nodelist. It will produce a binary called a.out that when executed is going to 30 | return a church numeral in the exit status (you can see it by using ~echo $?~ 31 | just after executing the program). 32 | -------------------------------------------------------------------------------- /src/main.s: -------------------------------------------------------------------------------- 1 | run: 2 | /* Save the callers's address */ 3 | pop %r8 4 | movq nadds, %r9 5 | movq %r8, adds(, %r9, 8) 6 | incq nadds 7 | pop %rbx /* Get the root node */ 8 | cmp $0, %rbx /* Check if it's an S node */ 9 | je snode 10 | cmp $1, %rbx /* Check if it's a K node */ 11 | je knode 12 | cmp $2, %rbx /* Check if it's a U node (successor) */ 13 | je unode 14 | cmp $3, %rbx /* Check if it's a Z node */ 15 | je znode 16 | cmp $4, %rbx /* Check if it's a I node */ 17 | je inode 18 | cmp $5, %rbx /* Check if it's a B node */ 19 | je bnode 20 | cmp $6, %rbx /* Check if it's a C node */ 21 | je cnode 22 | cmp $7, %rbx /* Check if it's a S' node */ 23 | je spnode 24 | cmp $8, %rbx /* Check if it's a B* node */ 25 | je bsnode 26 | cmp $9, %rbx /* Check if it's a C' node */ 27 | je cnode 28 | /* Otherwise it's an internal node and we push its two leaves */ 29 | sub $9, %rbx 30 | push nodes(, %rbx, 8) 31 | dec %rbx 32 | push nodes(, %rbx, 8) 33 | call run 34 | jmp end 35 | snode: 36 | /* Get the 3 arguments */ 37 | pop %rbx 38 | pop %rcx 39 | pop %rdx 40 | mov nodelen(, 1), %rsi /* Create an internal node (y z) */ 41 | add $10, %rsi 42 | push %rsi 43 | sub $10, %rsi 44 | mov %rcx, nodes(, %rsi, 8) 45 | inc %rsi 46 | mov %rdx, nodes(, %rsi, 8) 47 | inc %rsi 48 | mov %rsi, nodelen(, 1) 49 | push %rdx /* Push z and x on the stack */ 50 | push %rbx 51 | /* S x y z became x z (y z) on the stack */ 52 | call run 53 | jmp end 54 | knode: 55 | /* Get the 2 arguments */ 56 | pop %rbx /* x */ 57 | pop %rcx /* y */ 58 | /* Push x on the stack */ 59 | push %rbx 60 | /* K x y became x on the stack */ 61 | call run 62 | jmp end 63 | unode: 64 | /* Evaluate the rest of the stack and increment the result */ 65 | call run 66 | inc %rax 67 | jmp end 68 | znode: 69 | mov $0, %rax /* Evaluates to 0 */ 70 | jmp end 71 | inode: 72 | /* I x -> x so we evaluate the rest of the stack */ 73 | call run 74 | jmp end 75 | bnode: 76 | /* Get the 3 arguments */ 77 | pop %rbx 78 | pop %rcx 79 | pop %rdx 80 | mov nodelen, %rsi /* Create an internal node (y z) */ 81 | add $10, %rsi 82 | push %rsi 83 | sub $10, %rsi 84 | mov %rcx, nodes(, %rsi, 8) 85 | inc %rsi 86 | mov %rdx, nodes(, %rsi, 8) 87 | inc %rsi 88 | mov %rsi, nodelen 89 | push %rbx /* Push x on the stack */ 90 | /* B x y z became x (y z) on the stack */ 91 | call run 92 | jmp end 93 | cnode: 94 | /* Get the 3 arguments */ 95 | pop %rbx 96 | pop %rcx 97 | pop %rdx 98 | /* Push the arguments an flip the third and the second */ 99 | push %rcx 100 | push %rdx 101 | push %rbx 102 | /* C x y z became x z y on the stack */ 103 | call run 104 | jmp end 105 | spnode: 106 | /* Get the 4 arguments */ 107 | pop %rdi /* c */ 108 | pop %rbx /* f */ 109 | pop %rcx /* g */ 110 | pop %rdx /* x */ 111 | mov nodelen, %rsi 112 | add $12, %rsi 113 | push %rsi 114 | sub $2, %rsi 115 | push %rsi 116 | sub $10, %rsi 117 | mov %rbx, nodes(, %rsi, 8) /* Creates an internal node (f x) */ 118 | inc %rsi 119 | mov %rdx, nodes(, %rsi, 8) 120 | inc %rsi 121 | mov %rcx, nodes(, %rsi, 8) /* Creates an internal node (g x) */ 122 | inc %rsi 123 | mov %rdx, nodes(, %rsi, 8) 124 | inc %rsi 125 | mov %rsi, nodelen 126 | push %rdi 127 | /* S' c f g x became c (f x) (g x) on the stack */ 128 | call run 129 | jmp end 130 | bsnode: 131 | /* Get the 4 arguments */ 132 | pop %rdi /* c */ 133 | pop %rbx /* f */ 134 | pop %rcx /* g */ 135 | pop %rdx /* x */ 136 | mov nodelen, %rsi 137 | /* Creates an internal node f (g x) */ 138 | add $10, %rsi 139 | push %rsi 140 | sub $10, %rsi 141 | mov %rbx, nodes(, %rsi, 8) 142 | inc %rsi 143 | mov %rsi, %r10 144 | add $11, %r10 145 | mov %r10, nodes(, %rsi, 8) 146 | inc %rsi 147 | mov %rcx, nodes(, %rsi, 8) 148 | inc %rsi 149 | mov %rdx, nodes(, %rsi, 8) 150 | inc %rsi 151 | mov %rsi, nodelen 152 | push %rdi 153 | /* B* c f g x became c (f (g x)) on the stack */ 154 | call run 155 | jmp end 156 | cpnode: 157 | /* Get the 4 arguments */ 158 | pop %rdi /* c */ 159 | pop %rbx /* f */ 160 | pop %rcx /* g */ 161 | pop %rdx /* x */ 162 | mov nodelen, %rsi 163 | push %rdx 164 | add $10, %rsi 165 | push %rsi 166 | sub $10, %rsi 167 | mov %rbx, nodes(, %rsi, 8) /* Creates an internal node (f x) */ 168 | inc %rsi 169 | mov %rdx, nodes(, %rsi, 8) 170 | inc %rsi 171 | mov %rsi, nodelen 172 | push %rdi 173 | /* C' c f g x became c (f x) g on the stack */ 174 | call run 175 | jmp end 176 | end: 177 | decq nadds 178 | movq nadds, %r8 179 | push adds(, %r8, 8) 180 | ret 181 | -------------------------------------------------------------------------------- /src/ski.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | prog=".global _start 4 | .lcomm nodelen, 8 5 | .lcomm nodes, 10000 6 | .lcomm adds, 100000 7 | .lcomm nadds, 8 8 | _start:" 9 | IFS= read -r stack < "$1" 10 | prog="$prog 11 | push \$$(echo "$stack" | sed 's/ /\npush \$/g')" 12 | nodelist=$(sed -n 2p "$1") 13 | set -f 14 | IFS=" " 15 | set -- $nodelist 16 | nodelist=$(printf '%s\n' "$@") 17 | loop=0 18 | for node in "$@"; do 19 | prog="$prog 20 | movq \$$loop, %r8 21 | movq \$$node, nodes(, %r8, 8)" 22 | loop=$((loop+1)) 23 | done 24 | set +f 25 | prog="$prog 26 | movq \$0, nadds 27 | movq \$$loop, nodelen 28 | call run 29 | mov %rax, %rdi 30 | mov \$60, %rax 31 | syscall" 32 | echo "$prog" | 33 | cat - main.s | 34 | as - -o a.o 35 | ld a.o 36 | --------------------------------------------------------------------------------