summaryrefslogtreecommitdiff
path: root/prec/scope.scm
blob: e57ed64b9500dde81fbfee838862a82be98c6044 (about) (plain)
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
;;; scope.scm -- variable scoping and precedence parsing phase
;;; 
;;; author :  John & Sandra
;;; date   :  11 Feb 1992
;;;
;;;


;;;===================================================================
;;; Basic support
;;;===================================================================

(define (scope-modules modules)
  (walk-modules modules
    (lambda ()
      (setf (module-decls *module*) (scope-ast-decls (module-decls *module*)))
      (dolist (a (module-annotations *module*))
;;; This is currently bogus since it assumes only vars are annotated.	      
	 (when (annotation-decl? a)
	   (dolist (n (annotation-decl-names a))
	      (let ((v (table-entry *symbol-table* n)))
		(when (or (eq? v '#f) (not (var? v)))
		  (fatal-error 'unknown-annotation-name
			       "~A: not a var in annotation decl~%" n))
		(when (not (eq? (def-module v) *module-name*))
		  (fatal-error 'non-local-name-in-annotation
			       "~A: not a local var in annotation decl~%" n))
		(setf (var-annotations v)
		      (append (var-annotations v)
			      (annotation-decl-annotations a))))))))))

;;; Define the basic walker and some helper functions.

(define-walker scope ast-td-scope-walker)

(define (scope-ast-1 x env)
;  (call-walker scope x env))
  (remember-context x
    (call-walker scope x env)))


(define (scope-ast/list l env)
  (scope-ast/list-aux l env)
  l)

(define (scope-ast/list-aux l env)
  (when (not (null? l))
    (setf (car l) (scope-ast-1 (car l) env))
    (scope-ast/list-aux (cdr l) env)))

;;; This filters out signdecls from decl lists.  These declarations are moved
;;; into the var definitions.

(define (scope-ast/decl-list l env)
  (if (null? l)
      '()
      (let ((x (scope-ast-1 (car l) env))
	    (rest (scope-ast/decl-list (cdr l) env)))
	(if (or (annotation-decls? x)
		(and (signdecl? x)
		     (not (eq? (module-type *module*) 'interface))))
	    rest
	    (begin
	      (setf (car l) x)
	      (setf (cdr l) rest)
	      l)))))

;;; This is the main entry point.  It is called by the driver
;;; on each top-level decl in the module.

(define (scope-ast-decls x)
  (let ((result  (scope-ast/decl-list x '())))
;    (pprint result)   ;*** debug
    result))


;;; All top-level names are entered in the *symbol-table* hash table.
;;; This is done by the import/export phase of the compiler before
;;; we get here.
;;; The env is a list of a-lists that associates locally-defined names with
;;; their definitions.  Each nested a-list corresponds to a "level" or
;;; scope.
;;; *** If many variables are being added in each scope, it might be
;;; *** better to use a table instead of an alist to represent each contour.

(define (lookup-name name env)
  (if (null? env)
      (lookup-toplevel-name name)
      (let ((info  (assq name (car env))))
	(if info
	    (cdr info)
	    (lookup-name name (cdr env))))))


;;; Some kinds of names (e.g. type definitions) appear only at top-level,
;;; so use this to look for them directly.

(define (lookup-toplevel-name name)
  (or (resolve-toplevel-name name)
      (begin
        (signal-undefined-symbol name)
	*undefined-def*)))


;;; Some kinds of lookups (e.g., matching a signature declaration)
;;; require that the name be defined in the current scope and not
;;; an outer one.  Use this function.

(define (lookup-local-name name env)
  (if (null? env)
      (lookup-toplevel-name name)
      (let ((info  (assq name (car env))))
	(if info
	    (cdr info)
	    (begin
	      (signal-undefined-local-symbol name)
	      *undefined-def*)))))


;;; Add local declarations to the environment, returning a new env.
;;; Do not actually walk the local declarations here.

(define *scope-info* '())

(define (add-local-declarations decls env)
  (if (null? decls)
      env
      (let ((contour   '()))
	(dolist (d decls)
	  (if (is-type? 'valdef d)
	      (setf contour
		    (add-bindings (collect-pattern-vars (valdef-lhs d))
				  contour))))
	(cons contour env))))


;;; Similar, but for adding lambda and function argument bindings to the
;;; environment.

(define (add-pattern-variables patterns env)
  (if (null? patterns)
      env
      (let ((contour   '()))
	(dolist (p patterns)
	  (setf contour (add-bindings (collect-pattern-vars p) contour)))
	(cons contour env))))


;;; Given a list of var-refs, create defs for them and add them to
;;; the local environment.
;;; Also check to see that there are no duplicates.

(define (add-bindings var-refs contour)
  (dolist (v var-refs)
   (when (eq? (var-ref-var v) *undefined-def*)
    (let* ((name     (var-ref-name v))
	   (def      (create-local-definition name)))
      (setf (var-ref-var v) def)
      (if (assq name contour)
	  (signal-multiple-bindings name)
	  (push (cons name def) contour)))))
  contour)


;;; Error signalling utilities.

(define (signal-undefined-local-symbol name)
  (phase-error 'undefined-local-symbol
    "The name ~a has no definition in the current scope."
    name))

(define (signal-multiple-signatures name)
  (phase-error 'multiple-signatures
    "There are multiple signatures for the name ~a."
    name))

(define (signal-multiple-bindings name)
  (phase-error 'multiple-bindings
    "The name ~a appears more than once in a function or pattern binding."
    name))
  


;;;===================================================================
;;; Default traversal methods
;;;===================================================================


(define-local-syntax (make-scope-code slot type)
  (let ((stype  (sd-type slot))
	(sname  (sd-name slot)))
    (cond ((and (symbol? stype)
		(or (eq? stype 'exp)
		    (subtype? stype 'exp)))
	   `(setf (struct-slot ',type ',sname object)
		  (scope-ast-1 (struct-slot ',type ',sname object) env)))
	  ((and (pair? stype)
		(eq? (car stype) 'list)
		(symbol? (cadr stype))
		(or (eq? (cadr stype) 'exp)
		    (subtype? (cadr stype) 'exp)))
	   `(setf (struct-slot ',type ',sname object)
		  (scope-ast/list (struct-slot ',type ',sname object) env)))
	  (else
;	   (format '#t "Scope: skipping slot ~A in ~A~%"
;		   (sd-name slot)
;		   type)
	   '#f))))


(define-modify-walker-methods scope
  (guarded-rhs  ; exp slots
   if           ; exp slots
   app          ; exp slots
   integer-const float-const char-const string-const  ; no slots
   list-exp     ; (list exp) slot
   sequence sequence-to sequence-then sequence-then-to ; exp slots
   section-l section-r ; exp slots
   omitted-guard overloaded-var-ref ; no slots
   negate ; no slots
   sel
   prim-definition
   con-number cast
   )
  (object env)
  make-scope-code)


;;;===================================================================
;;; valdef-structs
;;;===================================================================


;;; Signature declarations must appear at the same level as the names
;;; they apply to.  There must not be more than one signature declaration
;;; applying to a given name.

(define-walker-method scope signdecl (object env)
  (let ((signature  (signdecl-signature object)))
    (resolve-signature signature)
    (let ((gtype (ast->gtype (signature-context signature)
			     (signature-type signature))))
      (dolist (v (signdecl-vars object))
	(when (eq? (var-ref-var v) *undefined-def*)
	      (setf (var-ref-var v)
		    (lookup-local-name (var-ref-name v) env)))
	(let ((def  (var-ref-var v)))
	  (when (not (eq? def *undefined-def*))
	    ;; The lookup-local-name may fail if there is a program error.
	    ;; In that case, skip this.
	    (if (var-signature def)
		(signal-multiple-signatures (var-ref-name v))
		(setf (var-signature def) gtype))))))
    object))

;;; This attaches annotations to locally defined vars in the same
;;; manner as signdecl annotations.

(define-walker-method scope annotation-decls (object env)
  (let ((anns (annotation-decls-annotations object)))
    (dolist (a anns)
      (cond ((annotation-value? a)
	     (recoverable-error 'unknown-annotation "Unknown annotation: ~A" a))
	    ((annotation-decl? a)
	     (dolist (v (annotation-decl-names a))
	       (let ((name (lookup-local-name v env)))
		 (when (not (eq? name *undefined-def*))
		   (setf (var-annotations name)
			 (append (var-annotations name)
			      (annotation-decl-annotations a))))))))))
  object)

(define-walker-method scope exp-sign (object env)
  (resolve-signature (exp-sign-signature object))
  (setf (exp-sign-exp object) (scope-ast-1 (exp-sign-exp object) env))
  object)

;;; By the time we get to walking a valdef, all the variables it
;;; declares have been entered into the environment.  All we need to
;;; do is massage the pattern and recursively walk the definitions.

(define-walker-method scope valdef (object env)
  (setf (valdef-module object) *module-name*)
  (setf (valdef-lhs object) (massage-pattern (valdef-lhs object)))
  (setf (valdef-definitions object)
	(scope-ast/list (valdef-definitions object) env))
  object)


;;; For a single-fun-def, do the where-decls first, and then walk the
;;; rhs in an env that includes both the where-decls and the args.

(define-walker-method scope single-fun-def (object env)
  (setf env (add-pattern-variables (single-fun-def-args object) env))
  (setf env (add-local-declarations (single-fun-def-where-decls object) env))
  (setf (single-fun-def-where-decls object)
	(scope-ast/decl-list (single-fun-def-where-decls object) env))
  (setf (single-fun-def-args object)
	(massage-pattern-list (single-fun-def-args object)))
  (setf (single-fun-def-rhs-list object)
	(scope-ast/list (single-fun-def-rhs-list object) env))
  object)


;;;===================================================================
;;; exp-structs
;;;===================================================================

(define-walker-method scope lambda (object env)
  (setf env (add-pattern-variables (lambda-pats object) env))
  (setf (lambda-pats object) (massage-pattern-list (lambda-pats object)))
  (setf (lambda-body object) (scope-ast-1 (lambda-body object) env))
  object)

(define-walker-method scope let (object env)
  (setf env (add-local-declarations (let-decls object) env))
  (setf (let-decls object) (scope-ast/decl-list (let-decls object) env))
  (setf (let-body object) (scope-ast-1 (let-body object) env))
  object)


;;; Case alts are treated very much like single-fun-defs.

(define-walker-method scope case (object env)
  (setf (case-exp object) (scope-ast-1 (case-exp object) env))
  (dolist (a (case-alts object))
    (let ((env  (add-pattern-variables (list (alt-pat a)) env)))
      (setf env (add-local-declarations (alt-where-decls a) env))
      (setf (alt-where-decls a)
	    (scope-ast/decl-list (alt-where-decls a) env))
      (setf (alt-pat a) (massage-pattern (alt-pat a)))
      (setf (alt-rhs-list a)
	    (scope-ast/list (alt-rhs-list a) env))))
  object)


(define-walker-method scope var-ref (object env)
  (when (eq? (var-ref-var object) *undefined-def*)
	(setf (var-ref-var object)
	      (lookup-name (var-ref-name object) env)))
  object)

(define-walker-method scope con-ref (object env)
  (declare (ignore env))
  (when (eq? (con-ref-con object) *undefined-def*)
	(setf (con-ref-con object)
	      (lookup-toplevel-name (con-ref-name object))))
  object)

(define-walker-method scope list-comp (object env)
  (dolist (q (list-comp-quals object))
    (cond ((is-type? 'qual-generator q)
	   (setf (qual-generator-exp q)
		 (scope-ast-1 (qual-generator-exp q) env))
	   (setf env
		 (add-pattern-variables (list (qual-generator-pat q)) env))
	   (setf (qual-generator-pat q)
		 (massage-pattern (qual-generator-pat q))))
	  ((is-type? 'qual-filter q)
	   (setf (qual-filter-exp q)
		 (scope-ast-1 (qual-filter-exp q) env)))))
  (setf (list-comp-exp object) (scope-ast-1 (list-comp-exp object) env))
  object)

(define-walker-method scope pp-exp-list (object env)
  (massage-pp-exp-list (scope-ast/list (pp-exp-list-exps object) env)))