-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathanalyze.ss
More file actions
437 lines (381 loc) · 15.9 KB
/
Copy pathanalyze.ss
File metadata and controls
437 lines (381 loc) · 15.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
(load "common.ss")
(load "parse.ss")
(define make-environment
(lambda () (list 'env (make-stack) (make-stack))))
;it's a stack of contexts, which hold variable lists to be pushed or popped off upon ctx switch
(define (environment-ctx-vars env) (cadr env))
(define (environment-local-vars env) (car ((environment-ctx-vars env) 'list)))
(define (environment-functions env) (caddr env))
(define (environment-local-functions env) (car ((environment-functions env) 'list)))
(define (current-function env)
(car (reverse (cadr ((environment-functions env) 'list)))))
(define (get-local-var-count env) (cdddr (current-function env)))
(define (analyze-type tokens)
(let ((x ((tokens 'has-next) 'token-symbol)))
(if (null? x) '()
(if (type? (cadr x)) (cadr x) '() ))))
(define (analyze-name tokens)
(let ((x ((tokens 'has-next) 'token-symbol)))
(if (null? x) '() (cadr x))))
(define (create-context env)
(((environment-ctx-vars env) 'push!) (list '())))
(define (delete-context env)
(((environment-ctx-vars env) 'pop!)))
(define (var-type var) (cadr var))
(define (var-name var) (car var))
(define (func-return-type func) (cadr func))
(define (func-name func) (car func))
(define (func-params func) (caddr func))
(define (look-up-context name ctx)
(define (loop c)
(if (null? c)
'()
(let [(match (assoc name (car c)))]
(if match match (loop (cdr c))))))
(loop ctx))
(define (look-up-variable name env)
(look-up-context name ((environment-ctx-vars env) 'list)))
(define (look-up-function name env)
(look-up-context name ((environment-functions env) 'list)))
(define (declare-variable var env)
(if (eq? (var-type var) 'void)
(error "variable declaration" "variable cannot be void" var)
(if (null? (look-up-variable (var-name var) env))
(begin
(if (null? (environment-local-vars env))
(set-car! ((environment-ctx-vars env) 'list) (list var))
(append! (environment-local-vars env) (list var)))
(set-car! (get-local-var-count env) (+ (car (get-local-var-count env)) 1)))
(error "variable declaration" "variable already declared" (var-name var)))))
(define (declare-function func env)
(if (null? (look-up-function (func-name func) env))
(if (null? (environment-local-functions env))
(set-car! ((environment-functions env) 'list) (list func))
(append! (environment-local-functions env) (list func)))
(error "function declaration" "function already declared" (func-name func))))
(define (analyze-variable tokens env)
(let ((name (analyze-name tokens)))
(if (null? name)
'()
(look-up-variable name env))))
(define infix '((token-plus 10 add)
(token-minus 10 substract)
(token-star 20 multiply)
(token-divide 20 divide)
(token-percent 20 modulo)
(token-greater 5 greater)
(token-greater-equals 5 greater-or-equals)
(token-less 5 less)
(token-less-equals 5 less-or-equals)
(token-equals 5 equals)
(token-not-equals 5 not-equals)
(token-logical-and 2 and)
(token-logical-or 2 or)))
(define prefix '((token-minus 100 neg)
(token-bang 100 not)))
(define (analyze-unary t tokens env)
(let [(pre (assq (car t) prefix))]
(cond
(pre (list (caddr pre) (analyze-expression (cadr pre) tokens env)))
((tagged? t 'token-left-bracket) (analyze-brackets t tokens env))
((tagged? t 'token-plus) (analyze-expression 100 tokens env)) ;unary plus
((tagged? t 'token-number) (list 'const-value (cadr t) 'int))
((tagged? t 'token-float-number) (list 'const-value (cadr t) 'double))
((tagged? t 'token-string) (list 'const-value (cadr t) 'string))
((tagged? t 'token-symbol)
(let [(var (look-up-variable (cadr t) env))]
(if (null? var)
(let [(func (look-up-function (cadr t) env))]
(if (null? func)
(error "primary expression" "unknown named identifier" (cadr t))
(analyze-func-value func tokens env)))
(list 'var-value (var-name var) (var-type var)))))
(else (error "primary expression" "unknown identifier" t)))))
(define (analyze-brackets t tokens env)
(let [(expr (analyze-expression 0 tokens env))]
(if (null? ((tokens 'has-next) 'token-right-bracket))
(error "expression" "unbalanced parentheses in expression" ((tokens 'peek)))
expr)))
(define (analyze-func-value func tokens env)
(let [(arg-list '())]
(define (arg-loop)
(let [(value (analyze-expression 0 tokens env))]
(if (null? arg-list)
(set! arg-list (list value))
(append! arg-list (list value)))
(if (eq? (car ((tokens 'peek))) 'token-comma)
(begin
((tokens 'advance))
(arg-loop))
arg-list)))
(if (null? ((tokens 'has-next) 'token-left-bracket))
(error "function call" "missing parentheses in function call" (func-name func))
(begin
(if (eq? (car ((tokens 'peek))) 'token-right-bracket)
(set! arg-list '(void))
(arg-loop))
(if (null? ((tokens 'has-next) 'token-right-bracket))
(error "function call" "mismatched parentheses in function call" (func-name func))
(if (check-signature (if (eq? (car (func-params func)) 'void) '(void) (map cadr (func-params func))) (map get-type arg-list))
(list 'function-call (func-name func) (func-return-type func) arg-list)
(error "function call" "wrong argument types for function call")))))))
(define (analyze-cast left tokens env)
(let [(type (analyze-type tokens))]
(if (null? type)
(error "analyze-cast" "bad cast to unknown type" type)
(if (or (eq? 'void (get-type left)) (eq? 'void type))
(error "analyze-cast" "cannot cast void value or to void type")
(list 'cast type (get-type left) left)))))
(define (analyze-binary t left tokens env)
(let [(in (assq (car t) infix))]
(cond
(in (let [(right (analyze-expression (cadr in) tokens env))]
(if (check-types left right)
(list (caddr in) left right)
(error "analyze-binary" "type mismatch" (get-type left) (get-type right)))))
((tagged? t 'token-pointer) (analyze-cast left tokens env))
(else (error "analyze-binary" "unknown binary operator" (car t))))))
(define (get-binding-power t)
(cond
((assq (car t) infix) => cadr)
((assq (car t) prefix) => cadr)
((tagged? t 'token-pointer) 200)
(else 0)))
(define (analyze-value tokens env)
(analyze-expression 0 tokens env))
(define (analyze-expression rbp tokens env)
(let [(t ((tokens 'get-next)))
(left '())]
(define (loop)
(if (< rbp (get-binding-power ((tokens 'peek)) ))
(begin
(set! t ((tokens 'get-next)))
(set! left (analyze-binary t left tokens env))
(loop)
)))
(set! left (analyze-unary t tokens env))
(loop)
left))
(define (analyze-declaration choices tokens env)
((tokens 'save))
(let [(type (analyze-type tokens))
(name (analyze-name tokens))
(semicolon ((tokens 'has-next) 'token-semicolon))]
(if (or (null? type) (null? name) (null? semicolon))
(fail choices tokens env)
(begin
(declare-variable (list name type) env)
(success tokens (list 'declaration name type))))))
(define (analyze-assignment choices tokens env)
((tokens 'save))
(let [(var (analyze-variable tokens env))]
(if (null? var)
(fail choices tokens env)
(let [(eqv ((tokens 'has-next) 'token-equals))
(value (analyze-value tokens env))]
(if (or (null? eqv) (null? value) (not ((tokens 'has-next) 'token-semicolon)))
(fail choices tokens env)
(if (check-types (var-type var) value)
(success tokens (list 'assignment (var-name var) value))
(error "assignment statement" "mismatched variable and assigned value types")))))))
(define (does-return? block)
(let loop [(exp block)]
(cond
((null? exp) #f)
((return? (car exp)) #t)
((conditional? (car exp))
(if (and (does-return? (caddr (car exp))) (does-return? (cadddr (car exp))))
#t (loop (cdr exp))))
((loop? (car exp))
(if (does-return? (caddr (car exp))) #t (loop (cdr exp))))
(else (loop (cdr exp))))))
(define (analyze-return choices tokens env)
((tokens 'save))
(let [(ret (analyze-name tokens))]
(if (eq? ret 'return)
(let [(value '())]
(if (not (eq? (car ((tokens 'peek))) 'token-semicolon))
(set! value (analyze-value tokens env)))
(if ((tokens 'has-next) 'token-semicolon)
(if (check-types value (func-return-type (current-function env)))
(success tokens (list 'return value))
(error "return statement" "mismatched function and returned types"))
(error "return statement" "missing semicolon")))
(fail choices tokens env))))
(define (analyze-func-call choices tokens env)
((tokens 'save))
(let [(func (look-up-function (analyze-name tokens) env))]
(if (null? func)
(fail choices tokens env)
(let [(value (analyze-func-value func tokens env))]
(if ((tokens 'has-next) 'token-semicolon)
(success tokens value)
(fail choices tokens env))))))
(define (analyze-conditional choices tokens env)
((tokens 'save))
(let [(if-keyword (analyze-name tokens))]
(if (and (eq? if-keyword 'if) ((tokens 'has-next) 'token-left-bracket))
(let [(value (analyze-value tokens env))]
(if ((tokens 'has-next) 'token-right-bracket)
(let [(main-clause (analyze-block tokens env))]
(if (eq? main-clause 'block-fail)
(error "if conditional" "main clause block fail")
(let [(else-keyword ((tokens 'peek)))
(else-clause '())]
(if (and (eq? (car else-keyword) 'token-symbol) (eq? (cadr else-keyword) 'else))
(begin
((tokens 'advance))
(set! else-clause (analyze-block tokens env))))
(if (eq? else-clause 'block-fail) (error "if conditional" "else clause block fail"))
(success tokens (list 'conditional value main-clause else-clause)))))
(error "if conditional" "mismatched parentheses at if condition"))) ; if we matched both if keyword and a left-bracket, we're assured
(fail choices tokens env))))
(define (analyze-while-loop choices tokens env)
((tokens 'save))
(let [(while-keyword (analyze-name tokens))]
(if (and (eq? while-keyword 'while) ((tokens 'has-next) 'token-left-bracket))
(let [(value (analyze-value tokens env))]
(if ((tokens 'has-next) 'token-right-bracket)
(let [(body (analyze-block tokens env))]
(if (eq? body 'block-fail)
(error "while body block fail")
(success tokens (list 'while-loop value body))))
(error "while loop" "mismatched parentheses at while loop condition")))
(fail choices tokens env))))
(define (analyze-for-loop choices tokens env)
((tokens 'save))
(let [(for-keyword (analyze-name tokens))]
(if (and (eq? for-keyword 'for) ((tokens 'has-next) 'token-left-bracket))
(let [(initial (analyze-statement (list analyze-assignment analyze-func-call analyze-end-of-block) tokens env))
(condition (analyze-value tokens env))]
(if ((tokens 'has-next) 'token-semicolon)
(let [(post (analyze-statement (list analyze-assignment analyze-func-call analyze-end-of-block) tokens env))]
(if ((tokens 'has-next) 'token-right-bracket)
(let [(body (analyze-block tokens env))]
(if (eq? body 'block-fail)
(error "for body block fail")
(success tokens (list 'for-loop condition body initial post))))
(error "for loop" "mismatched parentheses at for loop condition")))
(error "for loop" "missing semicolon at for loop")))
(fail choices tokens env))))
(define (analyze-break choices tokens env)
((tokens 'save))
(let [(keyword (analyze-name tokens))]
(if (and (eq? keyword 'break) ((tokens 'has-next) 'token-semicolon))
(success tokens (list 'break))
(fail choices tokens env))))
(define (analyze-continue choices tokens env)
((tokens 'save))
(let [(keyword (analyze-name tokens))]
(if (and (eq? keyword 'continue) ((tokens 'has-next) 'token-semicolon))
(success tokens (list 'continue))
(fail choices tokens env))))
(define (analyze-local-function choices tokens env)
((tokens 'save))
(let [(ast (analyze-function tokens env))]
(if (null? ast)
(fail choices tokens env)
(success tokens ast))))
(define (analyze-end-of-block choices tokens env)
(if (eq? (car ((tokens 'peek))) 'token-right-curly-bracket)
'() ;valid statement
(error "analyze" "unknown statement type starting with token" ((tokens 'peek)))))
(define statement-choices (list analyze-declaration
analyze-local-function
analyze-assignment
analyze-return
analyze-break
analyze-continue
analyze-func-call
analyze-conditional
analyze-while-loop
analyze-for-loop
analyze-end-of-block))
(define (fail choices tokens env)
((tokens 'restore))
((car choices) (cdr choices) tokens env))
(define (success tokens tree-node)
((tokens 'accept))
tree-node)
(define (analyze-statement choices tokens env)
((car choices) (cdr choices) tokens env))
(define (analyze-statements tokens env)
(let rec ((statement (analyze-statement statement-choices tokens env)))
;(display statement) (newline)
(if (null? statement)
'()
(cons statement (rec (analyze-statement statement-choices tokens env))))))
(define (analyze-block tokens env)
(create-context env) ; various blocks have their own scope
(let [(left-bracket ((tokens 'has-next) 'token-left-curly-bracket))
(statements (analyze-statements tokens env))
(right-bracket ((tokens 'has-next) 'token-right-curly-bracket))]
(delete-context env)
(if (or (null? left-bracket) (null? right-bracket))
'block-fail
statements)))
(define (make-function-ast return_type name params block env)
(let [(count (car (get-local-var-count env)))]
(((environment-functions env) 'pop!))
(list 'function name return_type params block count)))
(define (analyze-function-param-list tokens env)
((tokens 'save))
(let [(type (analyze-type tokens))
(name (analyze-name tokens))]
(if (or (null? type) (null? name))
(begin
((tokens 'restore))
'())
(begin
((tokens 'accept))
(if (null? ((tokens 'has-next) 'token-comma))
(if (null? ((tokens 'has-next) 'token-right-bracket))
(error "analyze-function-param-list" "bad token" ((tokens 'peek)))
(cons (list name type) '()))
(cons (list name type) (analyze-function-param-list tokens env)))))))
(define (analyze-function-params tokens env)
(let ((param-list (analyze-function-param-list tokens env)))
(if (null? param-list)
(if (null? ((tokens 'has-next) 'token-right-bracket))
(error "analyze-function-params" "bad token" ((tokens 'peek)))
(list 'void))
param-list)))
(define (analyze-function tokens env)
(let [(return-type (analyze-type tokens))
(name (analyze-name tokens))
(left-bracket ((tokens 'has-next) 'token-left-bracket))]
(if (or (null? return-type) (null? name) (null? left-bracket))
'()
(let [(params (analyze-function-params tokens env))]
(declare-function (list name return-type params 0) env)
(((environment-functions env) 'push!) (list '()))
(create-context env)
;function declaration form
(if (null? ((tokens 'has-next) 'token-semicolon))
(begin
(if (not (eq? (car params) 'void))
(for-each
(lambda (param) (declare-variable (list (var-name param) (var-type param)) env))
params))
(let [(block (analyze-block tokens env))]
(delete-context env)
(if (eq? block 'block-fail) ;block actually can be empty
(error "analyze-function" "bad function block" name)
(if (does-return? block)
(make-function-ast return-type name params block env)
(error "analyze-function" "function does not return in all cases" name)))))
(begin
(((environment-functions env) 'pop!))
(delete-context env)
(analyze-function tokens env)))))))
(define (declare-builtin env)
(for-each (lambda (f) (declare-function f env)) builtin-funcs))
(define (analyze token-list)
(let ((env (make-environment))
(token-provider (make-token-provider token-list)))
(((environment-functions env) 'push!) (list '()))
(declare-builtin env)
(let rec ((func (analyze-function token-provider env)))
(if (null? func)
'()
(cons func (rec (analyze-function token-provider env)))))))