Skip to content

Commit ee71eed

Browse files
committed
Start of some Grift notes.
1 parent e987dc1 commit ee71eed

4 files changed

Lines changed: 130 additions & 12 deletions

File tree

www/notes/fraud/compile-refactor.rkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
(match e
1919
[(? integer? i) (compile-integer i)]
2020
[(? boolean? b) (compile-boolean b)]
21+
[(? symbol? x) (compile-variable x c)]
2122
[`(add1 ,e0) (compile-add1 e0 c)]
2223
[`(sub1 ,e0) (compile-sub1 e0 c)]
2324
[`(zero? ,e0) (compile-zero? e0 c)]
2425
[`(if ,e0 ,e1 ,e2) (compile-if e0 e1 e2 c)]
25-
[(? symbol? x) (compile-variable x c)]
2626
[`(let ((,x ,e0)) ,e1) (compile-let x e0 e1 c)]))
2727

2828
;; Integer -> Asm

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.)

www/notes/grift/semantics.rkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252

5353
;; Primitive application
5454
[(𝑮-𝒆𝒏𝒗 e_0 r a_0) ...
55-
-----------
55+
----------- prim
5656
(𝑮-𝒆𝒏𝒗 (p e_0 ...) r (𝑮-𝒑𝒓𝒊𝒎 (p a_0 ...)))])
5757

5858
(define-metafunction G

0 commit comments

Comments
 (0)