|
| 1 | +#lang racket |
| 2 | +(provide (all-defined-out)) |
| 3 | + |
| 4 | +;; type Prog = (Prog (Listof Defn) Expr) |
| 5 | +(struct Prog (ds e) #:prefab) |
| 6 | + |
| 7 | +;; type Defn = (Defn Id (Listof Id) Expr) |
| 8 | +(struct Defn (f xs e) #:prefab) |
| 9 | + |
| 10 | +;; Differences from Knock |
| 11 | +;; |
| 12 | +;; * We _remove_: |
| 13 | +;; - `Fun` |
| 14 | +;; - `Call` |
| 15 | +;; |
| 16 | +;; * We add |
| 17 | +;; - `Lam` |
| 18 | +;; |
| 19 | +;; * We change: |
| 20 | +;; - `App` |
| 21 | +;; |
| 22 | +;; type Expr = (Eof) |
| 23 | +;; | (Empty) |
| 24 | +;; | (Int Integer) |
| 25 | +;; | (Bool Boolean) |
| 26 | +;; | (Char Character) |
| 27 | +;; | (Prim0 Op0) |
| 28 | +;; | (Prim1 Op1 Expr) |
| 29 | +;; | (Prim1 Op2 Op2 Expr) |
| 30 | +;; | (If Expr Expr Expr) |
| 31 | +;; | (Begin Expr Expr) |
| 32 | +;; | (Let Id Expr Expr) |
| 33 | +;; | LetRec (Binding list) Expr <--- New for Loot (See the lecture notes!) |
| 34 | +;; | Lam Name [Variable] Expr <--- New for Loot |
| 35 | +;; | (Var Id) |
| 36 | +;; | (App Expr (Listof Expr)) <--- Changed from Knock |
| 37 | +;; type Id = Symbol |
| 38 | +;; type Op0 = 'read-byte | 'void | 'collect-garbage |
| 39 | +;; type Op1 = 'add1 | 'sub1 | 'zero? |
| 40 | +;; | 'char? | 'integer->char | 'char->integer |
| 41 | +;; | 'write-byte | 'eof-object? |
| 42 | +;; | 'box | 'car | 'cdr | 'unbox |
| 43 | +;; | 'empty? |
| 44 | +;; type Op2 = '+ | '- | 'eq? |
| 45 | +;; | 'cons |
| 46 | +(struct Eof () #:prefab) |
| 47 | +(struct Empty () #:prefab) |
| 48 | +(struct Int (i) #:prefab) |
| 49 | +(struct Bool (b) #:prefab) |
| 50 | +(struct Char (c) #:prefab) |
| 51 | +(struct Prim0 (p) #:prefab) |
| 52 | +(struct Prim1 (p e) #:prefab) |
| 53 | +(struct Prim2 (p e1 e2) #:prefab) |
| 54 | +(struct If (e1 e2 e3) #:prefab) |
| 55 | +(struct Begin (e1 e2) #:prefab) |
| 56 | +(struct Let (x e1 e2) #:prefab) |
| 57 | +(struct LetRec (bs e1) #:prefab) |
| 58 | +(struct Lam (n xs e) #:prefab) |
| 59 | +(struct Var (x) #:prefab) |
| 60 | +(struct App (f es) #:prefab) |
| 61 | + |
| 62 | + |
| 63 | +;; Helper functions |
| 64 | + |
| 65 | +;; Does an Expr represent an immediate (i.e. flat) value? |
| 66 | +;; Expr -> Bool |
| 67 | +(define (imm? e) |
| 68 | + (match e |
| 69 | + [(Int i) #t] |
| 70 | + [(Bool b) #t] |
| 71 | + [(Char c) #t] |
| 72 | + [(Eof) #t] |
| 73 | + [(Empty) #t] |
| 74 | + [_ #f])) |
| 75 | + |
| 76 | +;; Get the 'actual' value out of an immediate. |
| 77 | +;; Expr -> Imm |
| 78 | +(define (get-imm e) |
| 79 | + (match e |
| 80 | + [(Int i) i] |
| 81 | + [(Bool b) b] |
| 82 | + [(Char c) c] |
| 83 | + [(Eof) eof] |
| 84 | + [(Empty) '()] |
| 85 | + [_ (error (~a "get-imm: " e " is not an immedate!"))])) |
| 86 | + |
| 87 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 88 | +;; Free Variables |
| 89 | +;; |
| 90 | +;; Expr -> [Var] |
| 91 | +(define (fvs e) |
| 92 | + (define (fvs e) |
| 93 | + (match e |
| 94 | + [(Prim1 p e) (fvs e)] |
| 95 | + [(Prim2 p e1 e2) (append (fvs e1) (fvs e2))] |
| 96 | + [(If e1 e2 e3) (append (fvs e1) (fvs e2) (fvs e3))] |
| 97 | + [(Begin e1 e2) (append (fvs e1) (fvs e2))] |
| 98 | + [(Let x e1 e2) (append (fvs e1) (remq* (list x) (fvs e2)))] |
| 99 | + [(LetRec bs e1) (let ((bound (map car bs)) |
| 100 | + (def-fvs (append-map fvs-bind bs))) |
| 101 | + (remq* bound (append def-fvs (fvs e1))))] |
| 102 | + [(Lam n xs e1) (remq* xs (fvs e1))] |
| 103 | + [(Var x) (list x)] |
| 104 | + [(App f es) (append (fvs f) (append-map fvs es))] |
| 105 | + [_ '()])) |
| 106 | + (remove-duplicates (fvs e))) |
| 107 | + |
| 108 | + |
| 109 | + |
| 110 | + |
| 111 | + |
| 112 | +(define (fvs-bind d) |
| 113 | + (match d |
| 114 | + [(list x e1) (fvs e1)])) |
| 115 | + |
| 116 | + |
| 117 | + |
| 118 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 119 | +;; Desugaring Definitions |
| 120 | +;; |
| 121 | +;; Now that we have lambdas, we can actually treat user-defined functions |
| 122 | +;; as syntactic sugar for lambdas. For example: |
| 123 | +;; |
| 124 | +;; (begin |
| 125 | +;; (define (f x) (+ x x)) |
| 126 | +;; (f 42)) |
| 127 | +;; |
| 128 | +;; Can be transformed to: |
| 129 | +;; |
| 130 | +;; (let ((f (lambda (x) (+ x x)))) |
| 131 | +;; (f 42)) |
| 132 | +;; |
| 133 | +;; That's not _quite_ enough, as top-level functions can refer to each other: |
| 134 | +;; |
| 135 | +;; (begin |
| 136 | +;; (define (f x) (+ x x)) |
| 137 | +;; (define (g y) (+ (f y) y)) |
| 138 | +;; (g 42)) |
| 139 | +;; |
| 140 | +;; Becomes: |
| 141 | +;; |
| 142 | +;; (letrec ((f (lambda (x) (+ x x))) |
| 143 | +;; (g (lambda (y) (+ (f y) y)))) |
| 144 | +;; (g 42)) |
| 145 | +;; |
| 146 | +;; Since we can represent our programs using this 'more fundamental' feature |
| 147 | +;; we can always _desugar_ from the nice-to-write version to the more |
| 148 | +;; fundamental version. |
| 149 | +;; |
| 150 | +;; Prog -> Prog |
| 151 | +(define (desugar e+) |
| 152 | + (match e+ |
| 153 | + [(Prog ds e) (let ((defs (map desugar ds))) |
| 154 | + (Prog '() (LetRec defs e)))] |
| 155 | + [(Defn f xs e) (list f (Lam f xs e))] |
| 156 | + [(Prim1 p e) (Prim1 p (desugar e))] |
| 157 | + [(Prim2 p e1 e2) (Prim2 p (desugar e1) (desugar e2))] |
| 158 | + [(If e1 e2 e3) (If (desugar e1) (desugar e2) (desugar e3))] |
| 159 | + [(Begin e1 e2) (Begin (desugar e1) (desugar e2))] |
| 160 | + [(Let x e1 e2) (Let x (desugar e1) (desugar e2))] |
| 161 | + [(LetRec bs e1) (LetRec (map (lambda (xs) (map desugar xs)) bs) (desugar e1))] |
| 162 | + [(Lam n xs e) (Lam (gensym 'lam) xs (desugar e))] |
| 163 | + [(App f es) (App (desugar f) (map desugar es))] |
| 164 | + [_ e+])) |
| 165 | + |
| 166 | + |
| 167 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 168 | +;; Labelling Lambdas |
| 169 | +;; |
| 170 | +;; Each lambda in a program needs to have a unique name so that we know what |
| 171 | +;; code we need to jump to when that lambda is 'called'. |
| 172 | +;; Luckily, `gensym` provides all the functionality that we need here. |
| 173 | +;; |
| 174 | +;; The flat values are easy: no possibility of there being a lambda, so |
| 175 | +;; we just return the unaltered expression. For everything else we traverse |
| 176 | +;; down the structure, the only case that actually 'does' anything is |
| 177 | +;; for `Lam` |
| 178 | +;; |
| 179 | +;; Prog -> Prog |
| 180 | +(define (label-λ e) |
| 181 | + (match e |
| 182 | + [(Prog ds e) (Prog (map label-λ ds) (label-λ e))] |
| 183 | + [(Defn f xs e) (Defn f xs (label-λ e))] |
| 184 | + [(Prim1 p e) (Prim1 p (label-λ e))] |
| 185 | + [(Prim2 p e1 e2) (Prim2 p (label-λ e1) (label-λ e2))] |
| 186 | + [(If e1 e2 e3) (If (label-λ e1) (label-λ e2) (label-λ e3))] |
| 187 | + [(Begin e1 e2) (Begin (label-λ e1) (label-λ e2))] |
| 188 | + [(Let x e1 e2) (Let x (label-λ e1) (label-λ e2))] |
| 189 | + [(LetRec bs e1) (LetRec (map (lambda (xs) (map label-λ xs)) bs) (label-λ e1))] |
| 190 | + [(Lam '() xs e) (Lam (gensym 'lam) xs (label-λ e))] |
| 191 | + [(Lam n xs e) (Lam (gensym n) xs (label-λ e))] |
| 192 | + [(App f es) (App (label-λ f) (map label-λ es))] |
| 193 | + [_ e])) |
| 194 | + |
| 195 | +;; For those that struggle with typing unicode |
| 196 | +(define label-lambda label-λ) |
| 197 | + |
| 198 | +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
| 199 | +;; Collecting all Lambdas |
| 200 | +;; |
| 201 | +;; While the lambdas could be _written_ anywhere in the source code, we do need |
| 202 | +;; to write the generated target code somewhere reliable. There are a few ways |
| 203 | +;; to do this, but we've decided to take the most straightforward route: collect |
| 204 | +;; the lambdas and treat them as 'additional' function definitions. |
| 205 | +;; |
| 206 | +;; In order to do this we'll need a list of all the lambdas in a program. |
| 207 | +;; This function traverses our program and collects all the lambdas. |
| 208 | +;; |
| 209 | +;; Prog -> [Expr] |
| 210 | +(define (λs e) |
| 211 | + (match e |
| 212 | + [(Prog ds e) (append (append-map λs ds) (λs e))] |
| 213 | + [(Defn f xs e) (λs e)] |
| 214 | + [(Prim1 p e) (λs e)] |
| 215 | + [(Prim2 p e1 e2) (append (λs e1) (λs e2))] |
| 216 | + [(If e1 e2 e3) (append (λs e1) (λs e2) (λs e3))] |
| 217 | + [(Begin e1 e2) (append (λs e1) (λs e2))] |
| 218 | + [(Let x e1 e2) (append (λs e1) (λs e2))] |
| 219 | + [(LetRec bs e1) (append (append-map lambda-defs bs) (λs e1))] |
| 220 | + [(Lam n xs e1) (cons e (λs e1))] |
| 221 | + [(App f es) (append (λs f) (append-map λs es))] |
| 222 | + [_ '()])) |
| 223 | + |
| 224 | +(define (lambda-defs d) |
| 225 | + (match d |
| 226 | + [(list x e) (λs e)])) |
| 227 | + |
| 228 | +;; For those that struggle with typing unicode |
| 229 | +(define lambdas λs) |
0 commit comments