File: node-check.scm

package info (click to toggle)
scheme48 1.9.2-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 18,332 kB
  • sloc: lisp: 88,907; ansic: 87,519; sh: 3,224; makefile: 771
file content (181 lines) | stat: -rw-r--r-- 6,167 bytes parent folder | download | duplicates (4)
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
; Part of Scheme 48 1.9.  See file COPYING for notices and license.

; Authors: Mike Sperber

; Check that a node is well-formed

(define (check-node node)
  (cond
   ((lambda-node? node)
    (check-lambda node))
   ((call-node? node)
    (check-call node))
   ((literal-node? node)
    (check-literal node))
   ((reference-node? node)
    (check-reference node))
   (else
    (assertion-violation 'check-node "unknown node type" node))))

(define (check-lambda node)
  (if (not (memq (lambda-type node) '(cont proc jump)))
      (assertion-violation 'check-node "invalid lambda type" node))
  (if (and (eq? 'jump (lambda-type node))
	   (not (memq (call-primop-id (node-parent node)) '(let letrec2))))
      (assertion-violation 'check-node "jump lambda must be bound by let or letrec2" node))
  (for-each (lambda (var)
	      (set-variable-flag! var #t))
	    (lambda-variables node))
  (let ((body (lambda-body node)))
    (if (not (call-node? body))
	(assertion-violation 'check-node "lambda body is not a call" node))
    (if (trivial-primop-call? body)
	(assertion-violation 'check-node "body call of a lambda must have non-trivial primop" node))
    (check-nontrivial-primop-call body))
  (for-each (lambda (var)
	      (set-variable-flag! var #f))
	    (lambda-variables node)))

(define (trivial-primop-call? node)
  (primop-trivial? (call-primop node)))

(define (check-call node)
  (if (> (call-exits node) (call-arg-count node))
      (assertion-violation 'check-node "call node has more exits than arguments"))
  (if (trivial-primop-call? node)
      (check-trivial-primop-call node)
      (check-nontrivial-primop-call node)))

(define (check-trivial-primop-call node)
  (walk-vector (lambda (arg)
		 (if (not (yields-value? node))
		     (assertion-violation 'check-node "argument to trivial-primop call must yield value" arg))
		 (check-node arg))
	       (call-args node)))

(define (cont-lambda? node)
  (and (lambda-node? node)
       (eq? 'cont (lambda-type node))))

(define (call-primop-id node)
  (primop-id (call-primop node)))

(define (call-primop-name node)
  (symbol->string (primop-id (call-primop node))))

; check that first argument is a continuation variable
(define (check-cont-var node)
  (if (positive? (call-exits node))
      (assertion-violation 'check-node
			   (string-append (call-primop-name node)
					  " node has non-zero exit count")
			   node))
  (if (not (and (positive? (call-arg-count node))
		(reference-node? (call-arg node 0))))
      (assertion-violation 'check-node
			   (string-append (call-primop-name node)
					  " node must have cont var as first argument"
					  (call-arg node 0)))))

; check that the call has single continuation
(define (check-cont node)
  (if (not (= 1 (call-exits node)))
      (assertion-violation 'check-node
			   (string-append (call-primop-name node)
					  " node must have single continuation")
			   node))
  (if (not (and (positive? (call-arg-count node))
		(cont-lambda? (call-arg node 0))))
      (assertion-violation 'check-node
			   (string-append (symbol->string primop-id)
					  " node must have cont lambda as first argument" (call-arg node 0)))))
  

(define (check-nontrivial-primop-call node)
  (let ((exit-count (call-exits node))
	(arg-count (call-arg-count node))
	(primop-id (call-primop-id node)))
    
    (do ((i 0 (+ 1 i)))
	((= i arg-count))
      (let ((arg (call-arg node i)))
	(cond
	 ((< i exit-count)
	  (if (not (cont-lambda? arg))
	      (assertion-violation 'check-node "exit argument must be cont lambda" arg)))
	 ((not (yields-value? arg))
	  (assertion-violation 'check-node "regular call argument must yield value" arg)))
	(check-node arg)))

    (let ((check-proc-arg
	   (lambda ()
	     (if (< arg-count 2)
		 (assertion-violation 'check-node "call node must have >=2 arguments" node)))))

      (case primop-id
	((let)
	 (check-cont node)
	 (if (not (= (length (lambda-variables (call-arg node 0)))
		     (- arg-count 1)))
	     (assertion-violation 'check-node
				  "variable and value count don't match up in let node" node)))
	((letrec1)
	 (check-cont node)
	 (if (not (= 1 arg-count))
	     (assertion-violation 'check-node
				  "letrec1 node must have exactly 1 arg" node))
	 (let* ((cont (call-arg node 0))
		(cont-args (lambda-variables cont))
		(cont-arg-count (length cont-args))
		(next (lambda-body cont)))
	   (check-cont next)
	   (if (not (eq? 'letrec2 (call-primop-id next)))
	       (assertion-violation 'check-node
				    "letrec1 node must be followed by letrec2 node" node))
	   (if (zero? cont-arg-count)
	       (assertion-violation 'check-node
				    "letrec1 cont lambda must have at least one variable" node))
	   (if (not (= cont-arg-count
		       (- (call-arg-count next) 1)))
	       (assertion-violation 'check-node
				    "letrec1 and letrec2 nodes must have matching arity" node))
	   (let ((var (car cont-args)))
	     (if (not (= 1 (length (variable-refs var))))
		 (assertion-violation 'check-node
				      "letrec id variable must have exactly one reference" node))
	     (if (or (not (eq? next (node-parent (car (variable-refs var)))))
		     (not (= 1 (node-index (car (variable-refs var))))))
		 (assertion-violation 'check-node
				      "letrec id binding invalid" node)))))
	((call unknown-call)
	 (check-proc-arg)
	 (check-cont node))
	((tail-call unknown-tail-call)
	 (check-proc-arg)
	 (check-cont-var node))
	((return unknown-return)
	 (check-cont-var node))
	((jump)
	 (check-cont-var node)		; sort of
	 (let ((jump-target (get-lambda-value (call-arg node 0))))
	   (if (not (eq? 'jump (lambda-type jump-target)))
	       (assertion-violation 'check-node
				    "jump must go to jump lambda"
				    node jump-target))))))))

(define (check-reference ref)
  (let ((var (reference-variable ref)))
    (if (and (variable-binder var)
	     (not (variable-flag var)))
	(assertion-violation 'check-node
			     "unbound variable reference" ref))))

(define (check-literal node)
  (values)) ; nothing to check

(define (yields-value? node)
  (or (lambda-node? node)
      (and (call-node? node)
	   (trivial-primop-call? node))
      (literal-node? node)
      (reference-node? node)))