Skip to content

Commit 36a1b23

Browse files
committed
New AST for knock
1 parent 4610dc7 commit 36a1b23

6 files changed

Lines changed: 323 additions & 64 deletions

File tree

www/notes/knock/ast.rkt

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#lang racket
2+
(provide (all-defined-out))
3+
4+
;; type Prog = [FunDef] Expr
5+
6+
;; type FunDef = Variable [Variable] Expr
7+
8+
;; type Expr =
9+
;; | Integer
10+
;; | Boolean
11+
;; | Character
12+
;; | Variable
13+
;; | Fun <--- New for Knock
14+
;; | Prim1 Expr
15+
;; | Prim2 Expr Expr
16+
;; | Call Expr Expr <--- New for Knock
17+
;; | App Variable [Expr]
18+
;; | If Expr Expr Expr
19+
;; | Let (Binding list) Expr
20+
;; | Nil
21+
22+
;; type Prim1 = 'add1 | 'sub1 | 'zero? | box | unbox | car | cdr
23+
;; type Prim2 = '+ | '- | cons
24+
25+
;; type Binding = Variable Expr
26+
27+
;; type Variable = Symbol (except 'add1 'sub1 'if, etc.)
28+
29+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
30+
;;;;;; The represenation of top-level programs
31+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
32+
33+
(struct prog (ds e) #:transparent)
34+
35+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
36+
;;;;;; The represenation of a function definition
37+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
38+
39+
;; A FunDef has a symbol for the function's name,
40+
;; a list of symbols representing the names of the function's
41+
;; arguments, and one expression that forms the body of the function.
42+
(struct fundef (name args body) #:transparent)
43+
44+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
45+
;;;;;; The Expr data structure
46+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
47+
48+
;; An Expr can be viewed as having 'kinds' of nodes.
49+
;;
50+
;; * The nodes that represnt an expression themselves
51+
;;
52+
;; * The nodes that are part of an expression, but no an expression themselves
53+
54+
;; The below are the former:
55+
56+
(struct int-e (i) #:transparent)
57+
(struct bool-e (b) #:transparent)
58+
(struct char-e (c) #:transparent)
59+
(struct fun-e (f) #:transparent) ; <- new for Knock
60+
(struct var-e (v) #:transparent)
61+
(struct prim-e (p es) #:transparent)
62+
(struct call-e (f es) #:transparent) ; <- new for Knock
63+
(struct app-e (f es) #:transparent)
64+
(struct if-e (e t f) #:transparent)
65+
(struct let-e (bs b) #:transparent)
66+
(struct nil-e () #:transparent)
67+
68+
;; The next is the latter:
69+
70+
;; A binding holds a symbol representing the bound variable and
71+
;; Expr that represents the value that will be bound to that variable
72+
(struct binding (v e) #:transparent)
73+
74+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
75+
;;;;;; AST utility functions (predicates)
76+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
77+
78+
(define unops '(add1 sub1 zero? box unbox empty? car cdr))
79+
(define biops '(+ - cons))
80+
81+
;; Any -> Boolean
82+
(define (prim? x)
83+
(and (symbol? x)
84+
(memq x (append unops biops))))
85+
86+
;; Any -> Boolean
87+
(define (biop? x)
88+
(and (symbol? x)
89+
(memq x biops)))
90+
91+
;; Any -> Boolean
92+
(define (unop? x)
93+
(and (symbol? x)
94+
(memq x unops)))
95+
96+
(define (value? v)
97+
(or (int-e? v)
98+
(bool-e? v)))
99+
100+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
101+
;;;;;; AST utility functions (getters)
102+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
103+
104+
;; It will sometimes be useful to get the list of all the variables that are
105+
;; introduced by a `let`
106+
;; [Binding] -> [Symbol]
107+
(define (get-vars bs)
108+
(match bs
109+
['() '()]
110+
[(cons (binding v _) bs) (cons v (get-vars bs))]))
111+
112+
;; Get all of the _definitions_ from a list of bindings
113+
;; [Binding] -> [Expr]
114+
(define (get-defs bs)
115+
(match bs
116+
['() '()]
117+
[(cons (binding _ def) bs) (cons def (get-defs bs))]))
118+
119+
120+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
121+
;;;;;; AST utility functions (printers)
122+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
123+
124+
;; We have switched to using `#:transparent` above, so this should only be
125+
;; necessary if you're desperate when debugging :'(
126+
127+
;; Given a Program, construct an sexpr that has the same shape
128+
(define (prog-debug p)
129+
(match p
130+
[(prog ds e) `(prog ,(map fundef-debug ds) ,(ast-debug e))]))
131+
132+
;; Given a FunDef, construct an sexpr that has the same shape
133+
(define (fundef-debug def)
134+
(match def
135+
[(fundef name args body) `(fundef ,name ,args ,(ast-debug body))]))
136+
137+
;; Given an AST, construct an sexpr that has the same shape
138+
(define (ast-debug a)
139+
(match a
140+
[(int-e i) `(int-e ,i)]
141+
[(bool-e b) `(bool-e ,b)]
142+
[(char-e c) `(char-e ,c)]
143+
[(var-e v) `(var-e ,v)]
144+
[(fun-e f) `(fun-e ,(ast-debug f))]
145+
[(nil-e) ''()]
146+
[(prim-e p es) `(prim-e ,p ,@(map ast-debug es))]
147+
[(call-e f es) `(call-e ,(ast-debug f) ,@(map ast-debug es))]
148+
[(app-e f es) `(app-e ,f ,@(map ast-debug es))]
149+
[(if-e e t f) `(if-e ,(ast-debug e)
150+
,(ast-debug t)
151+
,(ast-debug f))]
152+
[(let-e bs b) `(let-e ,(binding-debug bs) ,(ast-debug b))]))
153+
154+
(define (binding-debug bnds)
155+
(match bnds
156+
['() '()]
157+
[(cons (binding v e) bnds) `((,v ,(ast-debug e)) ,@(binding-debug bnds))]))

www/notes/knock/compile-file.rkt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#lang racket
2+
(provide (all-defined-out))
3+
(require "compile.rkt" "syntax.rkt" "asm/printer.rkt")
4+
5+
;; String -> Void
6+
;; Compile contents of given file name,
7+
;; emit asm code on stdout
8+
(define (main fn)
9+
(with-input-from-file fn
10+
(λ ()
11+
(let ((c (read-line))
12+
(p (read)))
13+
(unless (expr? p) (error "syntax error" p))
14+
(asm-display (compile (sexpr->prog p)))))))

www/notes/knock/compile.rkt

Lines changed: 64 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#lang racket
22
(provide (all-defined-out))
33

4+
(require "ast.rkt")
5+
46
;; An immediate is anything ending in #b000
57
;; All other tags in mask #b111 are pointers
68

@@ -26,30 +28,22 @@
2628
;; end in #b000 and we tag with #b001 for boxes, etc.
2729

2830
;; type CEnv = (Listof (Maybe Variable))
29-
;; type Imm = Integer | Boolean | Char | ''()
30-
31-
;; type Prog =
32-
;; | Expr
33-
;; | `(begin ,@(Listof (define (,Variable ,@(Listof Variable)) ,Expr))
34-
;; ,Expr)
3531

3632
;; Prog -> Asm
3733
(define (compile p)
3834
(match p
39-
[(list 'begin `(define (,fs . ,xss) ,es) ... e0)
40-
(let ((ds (compile-defines fs xss es))
41-
(c0 (compile-entry e0)))
35+
[(prog defs e)
36+
(let ((ds (compile-defines defs))
37+
(c0 (compile-entry e)))
4238
`(,@c0
43-
,@ds))]
44-
[e (compile-entry e)]))
39+
,@ds))]))
4540

4641
;; Expr -> Asm
4742
;; Compile e as the entry point
4843
(define (compile-entry e)
4944
`(entry
5045
,@(compile-tail-e e '())
5146
ret
52-
5347
err
5448
(push rbp)
5549
(call error)
@@ -59,30 +53,32 @@
5953
;; Compile an expression in tail position
6054
(define (compile-tail-e e c)
6155
(match e
62-
[(? symbol? x) (compile-variable x c)]
63-
[(? imm? i) (compile-imm i)]
64-
[`(box ,e0) (compile-box e0 c)]
65-
[`(unbox ,e0) (compile-unbox e0 c)]
66-
[`(cons ,e0 ,e1) (compile-cons e0 e1 c)]
67-
[`(car ,e0) (compile-car e0 c)]
68-
[`(cdr ,e0) (compile-cdr e0 c)]
69-
[`(add1 ,e0) (compile-add1 e0 c)]
70-
[`(sub1 ,e0) (compile-sub1 e0 c)]
71-
[`(zero? ,e0) (compile-zero? e0 c)]
72-
[`(empty? ,e0) (compile-empty? e0 c)]
73-
[`(if ,e0 ,e1 ,e2) (compile-tail-if e0 e1 e2 c)]
74-
[`(+ ,e0 ,e1) (compile-+ e0 e1 c)]
75-
[`(let ((,x ,e0)) ,e1) (compile-tail-let x e0 e1 c)]
76-
[`(fun ,f) (compile-fun f)]
77-
[`(call ,e0 . ,es) (compile-fun-tail-call e0 es c)]
78-
[`(,f . ,es) (compile-tail-call f es c)]))
56+
[(var-e v) (compile-variable v c)]
57+
[(? imm? i) (compile-imm i)]
58+
[(prim-e (? prim? p) es) (compile-prim p es c)]
59+
[(if-e p t f) (compile-tail-if p t f c)]
60+
[(let-e (list b) body) (compile-tail-let b body c)]
61+
[(fun-e f) (compile-fun f)]
62+
[(call-e f es) (compile-fun-tail-call f es c)]
63+
[(app-e f es) (compile-tail-call f es c)]))
7964

8065
;; Expr CEnv -> Asm
8166
;; Compile an expression in non-tail position
8267
(define (compile-e e c)
8368
(match e
84-
[(? symbol? x) (compile-variable x c)]
85-
[(? imm? i) (compile-imm i)]
69+
[(var-e v) (compile-variable v c)]
70+
[(? imm? i) (compile-imm i)]
71+
[(prim-e (? prim? p) es) (compile-prim p es c)]
72+
[(if-e p t f) (compile-if p t f c)]
73+
[(let-e (list b) body) (compile-let b body c)]
74+
[(fun-e f) (compile-fun f)]
75+
[(call-e f es) (compile-fun-call f es c)]
76+
[(app-e f es) (compile-call f es c)]))
77+
78+
;; Our current set of primitive operations require no function calls,
79+
;; so there's no difference between tail and non-tail call positions
80+
(define (compile-prim p es c)
81+
(match (cons p es)
8682
[`(box ,e0) (compile-box e0 c)]
8783
[`(unbox ,e0) (compile-unbox e0 c)]
8884
[`(cons ,e0 ,e1) (compile-cons e0 e1 c)]
@@ -92,12 +88,9 @@
9288
[`(sub1 ,e0) (compile-sub1 e0 c)]
9389
[`(zero? ,e0) (compile-zero? e0 c)]
9490
[`(empty? ,e0) (compile-empty? e0 c)]
95-
[`(if ,e0 ,e1 ,e2) (compile-if e0 e1 e2 c)]
9691
[`(+ ,e0 ,e1) (compile-+ e0 e1 c)]
97-
[`(let ((,x ,e0)) ,e1) (compile-let x e0 e1 c)]
98-
[`(fun ,f) (compile-fun f)]
99-
[`(call ,e0 . ,es) (compile-fun-call e0 es c)]
100-
[`(,f . ,es) (compile-call f es c)]))
92+
[_ (error
93+
(format "prim applied to wrong number of args: ~a ~a" p es))]))
10194

10295
;; Variable (Listof Expr) CEnv -> Asm
10396
;; Statically know the function we're calling
@@ -182,22 +175,24 @@
182175
,@cs))]))
183176

184177
;; Variable (Listof Variable) Expr -> Asm
185-
(define (compile-define f xs e0)
186-
(let ((c0 (compile-tail-e e0 (reverse xs))))
187-
`(,(symbol->label f)
188-
,@c0
189-
ret)))
178+
(define (compile-define def)
179+
(match def
180+
[(fundef name args body)
181+
(let ((c0 (compile-e body (reverse args))))
182+
`(,(symbol->label name)
183+
,@c0
184+
ret))]))
190185

191186
;; (Listof Variable) (Listof (Listof Variable)) (Listof Expr) -> Asm
192-
(define (compile-defines fs xss es)
193-
(append-map compile-define fs xss es))
187+
(define (compile-defines defs)
188+
(append-map compile-define defs))
194189

195190
;; Any -> Boolean
196191
(define (imm? x)
197-
(or (integer? x)
198-
(boolean? x)
199-
(char? x)
200-
(equal? ''() x)))
192+
(or (int-e? x)
193+
(bool-e? x)
194+
(char-e? x)
195+
(nil-e? x)))
201196

202197
;; Imm -> Asm
203198
(define (compile-imm i)
@@ -206,10 +201,10 @@
206201
;; Imm -> Integer
207202
(define (imm->bits i)
208203
(match i
209-
[(? integer? i) (arithmetic-shift i imm-shift)]
210-
[(? char? c) (+ (arithmetic-shift (char->integer c) imm-shift) imm-type-char)]
211-
[(? boolean? b) (if b imm-val-true imm-val-false)]
212-
[''() imm-type-empty]))
204+
[(int-e i) (arithmetic-shift i imm-shift)]
205+
[(char-e c) (+ (arithmetic-shift (char->integer c) imm-shift) imm-type-char)]
206+
[(bool-e b) (if b imm-val-true imm-val-false)]
207+
[(nil-e) imm-type-empty]))
213208

214209
;; Variable CEnv -> Asm
215210
(define (compile-variable x c)
@@ -335,20 +330,26 @@
335330
,l1)))
336331

337332
;; Variable Expr Expr CEnv -> Asm
338-
(define (compile-tail-let x e0 e1 c)
339-
(let ((c0 (compile-e e0 c))
340-
(c1 (compile-tail-e e1 (cons x c))))
341-
`(,@c0
342-
(mov (offset rsp ,(- (add1 (length c)))) rax)
343-
,@c1)))
333+
(define (compile-tail-let b e1 c)
334+
(match b
335+
[(binding v def)
336+
(let ((c0 (compile-e def c))
337+
(c1 (compile-tail-e e1 (cons v c))))
338+
`(,@c0
339+
(mov (offset rsp ,(- (add1 (length c)))) rax)
340+
,@c1))]
341+
[_ (error "Compile-let can only handle bindings")]))
344342

345343
;; Variable Expr Expr CEnv -> Asm
346-
(define (compile-let x e0 e1 c)
347-
(let ((c0 (compile-e e0 c))
348-
(c1 (compile-e e1 (cons x c))))
349-
`(,@c0
350-
(mov (offset rsp ,(- (add1 (length c)))) rax)
351-
,@c1)))
344+
(define (compile-let b e1 c)
345+
(match b
346+
[(binding v def)
347+
(let ((c0 (compile-e def c))
348+
(c1 (compile-e e1 (cons v c))))
349+
`(,@c0
350+
(mov (offset rsp ,(- (add1 (length c)))) rax)
351+
,@c1))]
352+
[_ (error "Compile-let can only handle bindings")]))
352353

353354
;; Expr Expr CEnv -> Asm
354355
(define (compile-+ e0 e1 c)

www/notes/knock/interp-file.rkt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#lang racket
2+
(provide (all-defined-out))
3+
(require "interp.rkt" "syntax.rkt")
4+
5+
;; String -> Void
6+
;; Parse and interpret contents of given filename,
7+
;; print result on stdout
8+
(define (main fn)
9+
(with-input-from-file fn
10+
(λ ()
11+
(let ((c (read-line)))
12+
(let ((p (read)))
13+
(unless (expr? p) (error "syntax error" p))
14+
(writeln (interp (sexpr->prog p))))))))

0 commit comments

Comments
 (0)