Skip to content

Commit b377795

Browse files
authored
Merge pull request #25 from dvanhorn/next
Next
2 parents 45bbc63 + 18d7307 commit b377795

13 files changed

Lines changed: 813 additions & 82 deletions

File tree

www/notes.scrbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
@include-section{notes/dupe.scrbl}
1212
@include-section{notes/extort.scrbl}
1313
@include-section{notes/fraud.scrbl}
14+
@include-section{notes/interlude1.scrbl}
1415
@include-section{notes/grift.scrbl}
1516
@include-section{notes/hustle.scrbl}
1617
@;{

www/notes/fraud/asm/interp.rkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#:exists 'truncate
1414
(λ ()
1515
(asm-display a)))
16-
(system (format "(cd ~a && make -s ~a)" dir t.run))
16+
(system (format "(cd ~a && make -s ~a) 2>&1 >/dev/null" dir t.run))
1717
(delete-file t.s)
1818
(with-input-from-string
1919
(with-output-to-string
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#lang racket
2+
(provide (all-defined-out))
3+
4+
;; type CEnv = [Listof Variable]
5+
6+
;; Expr -> Asm
7+
(define (compile e)
8+
`(entry
9+
,@(compile-e e '())
10+
ret
11+
err
12+
(push rbp)
13+
(call error)
14+
ret))
15+
16+
;; Expr CEnv -> Asm
17+
(define (compile-e e c)
18+
(match e
19+
[(? integer? i) (compile-integer i)]
20+
[(? boolean? b) (compile-boolean b)]
21+
[(? symbol? x) (compile-variable x c)]
22+
[`(add1 ,e0) (compile-add1 e0 c)]
23+
[`(sub1 ,e0) (compile-sub1 e0 c)]
24+
[`(zero? ,e0) (compile-zero? e0 c)]
25+
[`(if ,e0 ,e1 ,e2) (compile-if e0 e1 e2 c)]
26+
[`(let ((,x ,e0)) ,e1) (compile-let x e0 e1 c)]))
27+
28+
;; Integer -> Asm
29+
(define (compile-integer i)
30+
`((mov rax ,(* i 2))))
31+
32+
;; Boolean -> Asm
33+
(define (compile-boolean b)
34+
`((mov rax ,(if b #b11 #b01))))
35+
36+
;; Expr CEnv -> Asm
37+
(define (compile-add1 e0 c)
38+
(let ((c0 (compile-e e0 c)))
39+
`(,@c0
40+
,@assert-integer
41+
(add rax 2))))
42+
43+
;; Expr CEnv -> Asm
44+
(define (compile-sub1 e0 c)
45+
(let ((c0 (compile-e e0 c)))
46+
`(,@c0
47+
,@assert-integer
48+
(sub rax 2))))
49+
50+
;; Expr CEnv -> Asm
51+
(define (compile-zero? e0 c)
52+
(let ((c0 (compile-e e0 c))
53+
(l0 (gensym))
54+
(l1 (gensym)))
55+
`(,@c0
56+
,@assert-integer
57+
(cmp rax 0)
58+
(mov rax #b01) ; #f
59+
(jne ,l0)
60+
(mov rax #b11) ; #t
61+
,l0)))
62+
63+
;; Expr Expr Expr CEnv -> Asm
64+
(define (compile-if e0 e1 e2 c)
65+
(let ((c0 (compile-e e0 c))
66+
(c1 (compile-e e1 c))
67+
(c2 (compile-e e2 c))
68+
(l0 (gensym))
69+
(l1 (gensym)))
70+
`(,@c0
71+
(cmp rax #b01) ; compare to #f
72+
(je ,l0) ; jump to c2 if #f
73+
,@c1
74+
(jmp ,l1) ; jump past c2
75+
,l0
76+
,@c2
77+
,l1)))
78+
79+
;; Variable CEnv -> Asm
80+
(define (compile-variable x c)
81+
(let ((i (lookup x c)))
82+
`((mov rax (offset rsp ,(- (add1 i)))))))
83+
84+
;; Variable Expr Expr CEnv -> Asm
85+
(define (compile-let x e0 e1 c)
86+
(let ((c0 (compile-e e0 c))
87+
(c1 (compile-e e1 (cons x c))))
88+
`(,@c0
89+
(mov (offset rsp ,(- (add1 (length c)))) rax)
90+
,@c1)))
91+
92+
;; Variable CEnv -> Natural
93+
(define (lookup x cenv)
94+
(match cenv
95+
['() (error "undefined variable:" x)]
96+
[(cons y cenv)
97+
(match (symbol=? x y)
98+
[#t (length cenv)]
99+
[#f (lookup x cenv)])]))
100+
101+
(define assert-integer
102+
`((mov rbx rax)
103+
(and rbx 1)
104+
(cmp rbx 0)
105+
(jne err)))

www/notes/grift.scrbl

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,135 @@
1616

1717
@title[#:tag "Grift"]{Grift: binary operations}
1818

19-
@codeblock-include["grift/ast.rkt"]
19+
@emph{If you have to eat two frogs, eat the ugliest one first.}
20+
21+
You may have noticed that up until this point, evaluating compound
22+
expressions in our language always depend upon the result of a single
23+
subexpression. For example, @racket[(add1 _e)] depends upon the
24+
result of @racket[_e], @racket[(zero? _e)] depends upon @racket[_e],
25+
and so on. Even expressions that involve multiple subexpressions such
26+
as @racket[(if _e0 _e1 _e2)] really only depende on @racket[_e0] to
27+
determine which of @racket[_e1] or @racket[_e2] to evaluate.
28+
29+
Let's now consider what happens when we have @bold{multiple
30+
subexpressions} whose results must be combined in order to evaluate an
31+
expression. As an example, consider @racket[(+ _e0 _e1)]. We must
32+
evaluate @emph{both} @racket[_e0] and @racket[_e1] and sum their
33+
results.
34+
35+
We'll call this language @bold{Grift}.
36+
37+
What's new are the following @emph{binary} operations:
38+
39+
@racketblock[
40+
(+ _e0 _e1)
41+
(- _e0 _e1)
42+
]
43+
44+
This leads to the following grammar for Grift:
2045

2146
@centered[(render-language G)]
2247

23-
@(judgment-form-cases #f)
48+
We can model it as a datatype as usual:
49+
50+
@codeblock-include["grift/ast.rkt"]
51+
52+
@section{Meaning of Grift programs}
53+
54+
The meaning of Grift programs is pretty straightfoward. For
55+
@racket[(+ _e0 _e1)], the meaning is the sum of the meanings of
56+
@racket[_e0] and @racket[_e1], when they mean integers, otherwise the
57+
meaning is an error.
58+
59+
60+
61+
@(define ((rewrite s) lws)
62+
(define lhs (list-ref lws 2))
63+
(define rhs (list-ref lws 3))
64+
(list "" lhs (string-append " " (symbol->string s) " ") rhs ""))
65+
66+
@(require (only-in racket add-between))
67+
@(define-syntax-rule (show-judgment name cases)
68+
(with-unquote-rewriter
69+
(lambda (lw)
70+
(build-lw (lw-e lw) (lw-line lw) (lw-line-span lw) (lw-column lw) (lw-column-span lw)))
71+
(with-compound-rewriters (['+ (rewrite '+)]
72+
['- (rewrite '–)])
73+
(apply centered
74+
(add-between
75+
(map (λ (c) (parameterize ([judgment-form-cases (list c)]
76+
[judgment-form-show-rule-names #f])
77+
(render-judgment-form name)))
78+
cases)
79+
(hspace 4))))))
80+
81+
The handling of primitives occurs in the following rule:
2482

25-
@centered[(render-judgment-form 𝑮-𝒆𝒏𝒗)]
83+
@(show-judgment 𝑮-𝒆𝒏𝒗 '("prim"))
2684

27-
@centered[(render-metafunction 𝑮-𝒑𝒓𝒊𝒎 #:contract? #t)]
85+
It makes use of an auxiliary judgment for interpreting primitives:
2886

87+
@centered[
88+
(with-unquote-rewriter
89+
(lambda (lw)
90+
(build-lw (lw-e lw) (lw-line lw) (lw-line-span lw) (lw-column lw) (lw-column-span lw)))
91+
(render-metafunction 𝑮-𝒑𝒓𝒊𝒎 #:contract? #t))]
92+
93+
94+
The interpreter is likewise straightforward:
2995

3096
@codeblock-include["grift/interp.rkt"]
3197

98+
We can see that it works as expected:
99+
100+
@ex[
101+
(interp '(+ 3 4))
102+
(interp '(+ 3 (+ 2 2)))
103+
(interp '(+ #f 8))
104+
]
105+
106+
@section{A Compile for Grift}
107+
108+
Binary expressions are easy to deal with at the level of the semantics
109+
and interpreter. However things are more complicated at the level of
110+
the compiler.
111+
112+
To see the problem consider blindly following the pattern we used:
113+
114+
@#reader scribble/comment-reader
115+
(racketblock
116+
;; Expr Expr CEnv -> Asm
117+
(define (compile-+ e0 e1 c)
118+
(let ((c0 (compile-e e0 c))
119+
(c1 (compile-e e1 c)))
120+
`(,@c0 ; result in rax
121+
,@c1 ; result in rax
122+
(add rax _???))))
123+
)
124+
125+
The problem here is that executing @racket[c0] places its result in
126+
register @racket['rax], but then executing @racket[c1] places its
127+
result in @racket['rax], overwriting the value of @racket[e0].
128+
129+
It may be tempting to use another register to stash away the result of
130+
the first subexpression:
131+
132+
@#reader scribble/comment-reader
133+
(racketblock
134+
;; Expr Expr CEnv -> Asm
135+
(define (compile-+ e0 e1 c)
136+
(let ((c0 (compile-e e0 c))
137+
(c1 (compile-e e1 c)))
138+
`(,@c0 ; result in rax
139+
(mov rbx rax)
140+
,@c1 ; result in rax
141+
(add rax rbx))))
142+
)
143+
144+
Can you think of how this could go wrong?
145+
146+
147+
148+
149+
32150
@codeblock-include["grift/compile.rkt"]

www/notes/grift/ast.rkt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
;; | Integer
44
;; | Boolean
55
;; | Variable
6-
;; | `(add1 ,Expr)
7-
;; | `(sub1 ,Expr)
8-
;; | `(zero? ,Expr)
6+
;; | (list Prim1 Expr)
7+
;; | (list Prim2 Expr Expr)
98
;; | `(if ,Expr ,Expr ,Expr)
109
;; | `(let ((,Variable ,Expr)) ,Expr)
11-
;; | `(+ ,Expr ,Expr)
12-
;; | `(- ,Expr ,Expr)
1310

14-
;; type Variable = Symbol (except 'add1 'sub1 'if 'let)
11+
;; type Prim1 = 'add1 | 'sub1 | 'zero?
12+
;; type Prim2 = '+ | '-
13+
14+
;; type Variable = Symbol (except 'add1 'sub1 'if, etc.)

0 commit comments

Comments
 (0)