Skip to content

Commit 2461dac

Browse files
committed
WIP on Loot
1 parent 0809867 commit 2461dac

27 files changed

+2066
-0
lines changed

langs/loot-new/Makefile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
UNAME := $(shell uname)
2+
.PHONY: test
3+
4+
ifeq ($(UNAME), Darwin)
5+
format=macho64
6+
else
7+
format=elf64
8+
endif
9+
10+
%.run: %.o runtime.o
11+
gcc runtime.o $< -o $@
12+
13+
runtime.o: main.o char.o io.o
14+
ld -r main.o char.o io.o -o runtime.o
15+
16+
main.o: main.c types.h runtime.h
17+
gcc -fPIC -c main.c -o main.o
18+
19+
char.o: char.c types.h
20+
gcc -fPIC -c char.c -o char.o
21+
22+
io.o: io.c runtime.h
23+
gcc -fPIC -c io.c -o io.o
24+
25+
%.o: %.s
26+
nasm -f $(format) -o $@ $<
27+
28+
%.s: %.rkt
29+
racket -t compile-file.rkt -m $< > $@
30+
31+
clean:
32+
rm *.o *.s *.run

langs/loot-new/ast.rkt

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
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)

langs/loot-new/char.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#include <stdio.h>
2+
#include <inttypes.h>
3+
#include "types.h"
4+
5+
void print_codepoint(int64_t);
6+
7+
void print_char (int64_t v) {
8+
int64_t codepoint = v >> char_shift;
9+
printf("#\\");
10+
switch (codepoint) {
11+
case 0:
12+
printf("nul"); break;
13+
case 8:
14+
printf("backspace"); break;
15+
case 9:
16+
printf("tab"); break;
17+
case 10:
18+
printf("newline"); break;
19+
case 11:
20+
printf("vtab"); break;
21+
case 12:
22+
printf("page"); break;
23+
case 13:
24+
printf("return"); break;
25+
case 32:
26+
printf("space"); break;
27+
case 127:
28+
printf("rubout"); break;
29+
default:
30+
print_codepoint(v);
31+
}
32+
}
33+
34+
void print_codepoint(int64_t v) {
35+
int64_t codepoint = v >> char_shift;
36+
// Print using UTF-8 encoding of codepoint
37+
// https://en.wikipedia.org/wiki/UTF-8
38+
if (codepoint < 128) {
39+
printf("%c", (char) codepoint);
40+
} else if (codepoint < 2048) {
41+
printf("%c%c",
42+
(char)(codepoint >> 6) | 192,
43+
((char)codepoint & 63) | 128);
44+
} else if (codepoint < 65536) {
45+
printf("%c%c%c",
46+
(char)(codepoint >> 12) | 224,
47+
((char)(codepoint >> 6) & 63) | 128,
48+
((char)codepoint & 63) | 128);
49+
} else {
50+
printf("%c%c%c%c",
51+
(char)(codepoint >> 18) | 240,
52+
((char)(codepoint >> 12) & 63) | 128,
53+
((char)(codepoint >> 6) & 63) | 128,
54+
((char)codepoint & 63) | 128);
55+
}
56+
}
57+

langs/loot-new/compile-file.rkt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#lang racket
2+
(provide main)
3+
(require "parse.rkt" "compile.rkt" a86/printer)
4+
5+
;; String -> Void
6+
;; Compile contents of given file name,
7+
;; emit asm code on stdout
8+
(define (main fn)
9+
(let ((p (open-input-file fn)))
10+
(begin
11+
(read-line p) ; ignore #lang racket line
12+
(displayln (asm-string (compile (parse (read p)))))
13+
(close-input-port p))))

0 commit comments

Comments
 (0)