let-nodes.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Richard Kelsey,
  3. ; This is a backquote-like macro for building nodes.
  4. ;
  5. ; One goal is to produce code that is as efficient as possible.
  6. ; We aren't quite there yet.
  7. ;
  8. ; (LET-NODES (<spec1> ... <specN>) . <body>)
  9. ; (NEW-CALL <primop-id> <exits> . <arg-list>)
  10. ; These all create cont lambdas:
  11. ; (NEW-LAMBDA (<var1> ... <varN>) <call-exp>)
  12. ; (NEW-LAMBDA (<var1> ... <varN> . <last-vars>) <call-exp>)
  13. ; (NEW-LAMBDA <vars> <call-exp>)
  14. ; (NEW-LAMBDA (<var1> ... <varN>))
  15. ; (NEW-LAMBDA (<var1> ... <varN> . <last-vars>))
  16. ; (NEW-LAMBDA <vars>)
  17. ;
  18. ; <spec> ::= (<ident> <real-call>) | ; call node
  19. ; (<ident> (<var1> ... <varN>) <call>) | ; cont lambda node
  20. ; (<ident> (<var1> ... <varN> . <last-vars>) <call>) ; cont lambda node
  21. ; (<ident> <vars> <call>) | ; cont lambda node
  22. ;
  23. ; <var> ::= #f | Ignored variable position
  24. ; <ident> | Evaluate <ident> and copy it, rebinding <ident>
  25. ; '<ident> | Evaluate <ident> to get the variable
  26. ; (<ident> <rep>) (MAKE-VARIABLE <ident> <rep>)
  27. ;
  28. ; <last-vars> ::= <ident>
  29. ;
  30. ; <call> ::= <ident> | <real-call>
  31. ;
  32. ; <real-call> ::= (<primop-id> <exits> . <arg-list>)
  33. ;
  34. ; <arg-list> ::= (<arg1> ... <argN>) | (<arg1> ... <argN> . <last-args>)
  35. ;
  36. ; <last-args> ::= <ident>
  37. ;
  38. ; <arg> ::= 'foo literal node containing the value of foo, no rep
  39. ; '(foo rep) " " " " " " " , using rep
  40. ; (* foo) reference to foo (which evaluates to a variable)
  41. ; (! foo) foo evaluates to a node
  42. ; foo short for (! foo) when foo is an atom
  43. ; #f put nothing here
  44. ; (<primop-id> . <arg-list>) a nested (simple) call
  45. ;--------------------------------------
  46. ; Example:
  47. ;
  48. ; (let-nodes ((call (let 1 l1 . vals))
  49. ; (l1 vars lr1))
  50. ; call)
  51. ; ====>
  52. ; (let ((call (make-call-node (get-primop (enum primop let) (+ 1 (length vals)) 1)))
  53. ; (l1 (make-lambda-node 'c 'cont (append (list) vars))))
  54. ; (attach-call-args call (append (list l1) vals))
  55. ; (attach-body l1 lr1)
  56. ; call)
  57. (define (expand-let-nodes form rename compare)
  58. (destructure (((#f specs . body) form))
  59. (receive (vars nodes code)
  60. (parse-node-specs specs rename compare)
  61. `(,(rename 'let) ,vars
  62. (,(rename 'let) ,nodes
  63. ,@code
  64. ,@body)))))
  65. ; (NEW-LAMBDA (<var1> ... <varN>) <call-exp>)
  66. ; (NEW-LAMBDA (<var1> ... <varN> . <last-vars>) <call-exp>)
  67. ; (NEW-LAMBDA <vars> <call-exp>)
  68. ; (NEW-LAMBDA (<var1> ... <varN>))
  69. ; (NEW-LAMBDA (<var1> ... <varN> . <last-vars>))
  70. ; (NEW-LAMBDA <vars>)
  71. (define (expand-new-lambda form rename compare)
  72. (destructure (((#f vars . maybe-call) form))
  73. (if (not (or (null? maybe-call)
  74. (null? (cdr maybe-call))))
  75. form
  76. (let ((lambda-name (rename 'the-lambda))
  77. (call-name (rename 'the-call))
  78. (%let (rename 'let))
  79. (%attach-body (rename 'attach-body)))
  80. (receive (vars node)
  81. (construct-vars vars rename compare)
  82. (if (null? maybe-call)
  83. `(,%let ,vars ,node)
  84. `(,%let ,vars
  85. (,%let ((,lambda-name ,node)
  86. (,call-name ,(car maybe-call)))
  87. (,%attach-body ,lambda-name ,call-name)
  88. ,lambda-name))))))))
  89. (define (expand-new-call form rename compare)
  90. (let ((call-name (rename 'the-call))
  91. (%let (rename 'let)))
  92. (receive (node code)
  93. (construct-call call-name (cdr form) rename compare)
  94. `(,%let ((,call-name ,node))
  95. ,@code
  96. ,call-name))))
  97. (define (test form)
  98. (destructure (((#f specs . body) form))
  99. (receive (vars nodes code)
  100. (parse-node-specs specs identity eq?)
  101. `(let ,vars
  102. (let ,nodes
  103. ,@code
  104. ,@body)))))
  105. ; Parse the specs, returning a list of variable specs, a list of node specs,
  106. ; and a list of construction forms. An input spec is either a call or a
  107. ; lambda, each is parsed by an appropriate procedure.
  108. (define (parse-node-specs specs r c)
  109. (let loop ((specs (reverse specs)) (vars '()) (nodes '()) (codes '()))
  110. (if (null? specs)
  111. (values vars nodes codes)
  112. (destructure ((((name . spec) . rest) specs))
  113. (cond ((null? (cdr spec))
  114. (receive (node code)
  115. (construct-call name (car spec) r c)
  116. (loop rest vars
  117. `((,name ,node) . ,nodes) (append code codes))))
  118. ((= 2 (length spec))
  119. (receive (vs node new-spec call)
  120. (construct-lambda (car spec) (cadr spec) r c)
  121. (loop (if new-spec (cons new-spec rest) rest)
  122. (append vs vars)
  123. `((,name ,node) . ,nodes)
  124. (if call
  125. `((,(r 'attach-body) ,name ,call) . ,codes)
  126. codes))))
  127. (else
  128. (error "illegal spec in LET-NODES ~S" (cons name spec))))))))
  129. ; The names of the call-arg relation procedures, indexed by the number of
  130. ; arguments handled.
  131. (define call-attach-names
  132. '#(#f
  133. #f
  134. attach-two-call-args
  135. attach-three-call-args
  136. attach-four-call-args
  137. attach-five-call-args))
  138. ; Return the node spec and construction forms for a call. This dispatches
  139. ; on whether the argument list is proper or not.
  140. ;
  141. ; <real-call> ::= (<arg0> <exits> <arg1> ... <argN>) |
  142. ; (<arg0> <exits> <arg1> ... <argN> . <last-args>))
  143. (define (construct-call name specs r c)
  144. (destructure (((proc . args) specs))
  145. (really-construct-call name proc (car args) '() (cdr args) r c)))
  146. (define (construct-nested-call specs r c)
  147. (destructure (((primop-id . args) specs))
  148. (let ((name (r 'call)))
  149. (receive (node code)
  150. (really-construct-call name primop-id 0 '() args r c)
  151. `(,(r 'let) ((,name ,node)) ,@code ,name)))))
  152. (define (really-construct-call name primop-id exits extra args r c)
  153. (receive (arg-count arg-code)
  154. (parse-call-args name extra args r c)
  155. (let ((primop-code (get-primop-code primop-id r)))
  156. (values `(,(r 'make-call-node) ,primop-code ,arg-count ,exits)
  157. arg-code))))
  158. (define (get-primop-code id r)
  159. (cond ((name->enumerand id primop)
  160. => (lambda (n)
  161. `(,(r 'get-primop) ,n)))
  162. (else
  163. `(,(r 'lookup-primop) ',id))))
  164. ; NAME = the call node which gets the arguments
  165. ; EXTRA = initial, already expanded arguments
  166. ; ARGS = unexpanded arguments
  167. ; LAST-ARG = an atom whose value is added to the end of the arguments
  168. ; Returns ARG-COUNT-CODE and ARG-CODE
  169. (define (parse-call-args name extra args r c)
  170. (receive (args last-arg)
  171. (decouple-improper-list args)
  172. (let* ((args (append extra (map (lambda (a) (construct-node a r c)) args)))
  173. (count (length args)))
  174. (if (not (null? last-arg))
  175. (values `(,(r '+) ,count (,(r 'length) ,last-arg))
  176. `((,(r 'attach-call-args)
  177. ,name
  178. ,(if (null? args)
  179. last-arg
  180. `(,(r 'append) (,(r 'list) . ,args) ,last-arg)))))
  181. (values count
  182. (cond ((= count 0)
  183. '())
  184. ((and (= count 1) (car args))
  185. `((,(r 'attach) ,name 0 ,(car args))))
  186. ((and (< count 6)
  187. (every? identity args))
  188. `((,(r (vector-ref call-attach-names count))
  189. ,name
  190. ,@args)))
  191. (else
  192. `((,(r 'attach-call-args) ,name (list . ,args))))))))))
  193. ; Return proper part of the list and its last-cdr separately.
  194. (define (decouple-improper-list list)
  195. (do ((list list (cdr list))
  196. (res '() (cons (car list) res)))
  197. ((atom? list)
  198. (values (reverse! res) list))))
  199. ; Dispatch on the type of the SPEC and return the appropriate code.
  200. ;
  201. ; <arg> ::= 'foo literal node containing the value of foo, no rep
  202. ; '(foo rep) literal node containing the value of foo
  203. ; (* foo) reference to foo (which evaluates to a variable)
  204. ; (! foo) foo evaluates to a node
  205. ; name short for (! name) when foo is an atom
  206. ; #f put nothing here
  207. ; (<primop-id> . <arg-list>) a nested (simple) call
  208. (define (construct-node spec r c)
  209. (cond ((atom? spec) spec)
  210. (else
  211. (destructure (((key data) spec))
  212. (case key
  213. ((*) `(,(r 'make-reference-node) ,data))
  214. ((quote) (if (pair? data)
  215. `(,(r 'make-literal-node) . ,data)
  216. `(,(r 'make-literal-node) ,data ,(r 'type/unknown))))
  217. ((!) data)
  218. (else
  219. (construct-nested-call spec r c)))))))
  220. ; Parse a lambda spec. This returns a list of variable specs, code to
  221. ; construct the lambda node, a spec for the body if necessary, and
  222. ; the code needed to put it all together.
  223. (define (construct-lambda vars call r c)
  224. (receive (vars node)
  225. (construct-vars vars r c)
  226. (cond ((null? call)
  227. (values vars node #f #f))
  228. ((atom? call)
  229. (values vars node #f call))
  230. (else
  231. (let ((sym (r (generate-symbol 'c))))
  232. (values vars node `(,sym ,call) sym))))))
  233. ; Returns the code needed to construct the variables and the code to make
  234. ; the lambda node that binds the variables.
  235. ;
  236. ; <var> ::= #f | Ignored variable position
  237. ; <ident> | Evaluate <ident> and copy it, rebinding <ident>
  238. ; '<ident> | Evaluate <ident> to get the variable
  239. ; (<ident> <rep>) (MAKE-VARIABLE <ident> <rep>)
  240. (define (construct-vars vars r c)
  241. (let loop ((vs vars) (vlist '()) (code '()))
  242. (cond ((atom? vs)
  243. (let ((vars (if (null? vs)
  244. `(,(r 'list) . ,(reverse! vlist))
  245. `(,(r 'append) (,(r 'list) . ,(reverse! vlist))
  246. ,vs))))
  247. (values code
  248. `(,(r 'make-lambda-node) 'c 'cont ,vars))))
  249. (else
  250. (let ((spec (car vs))
  251. (rest (cdr vs)))
  252. (cond ((null? spec)
  253. (loop rest
  254. (cons #f vlist)
  255. code))
  256. ((atom? spec)
  257. (loop rest
  258. (cons spec vlist)
  259. `((,spec (,(r 'copy-variable) ,spec)) . ,code)))
  260. ((c (car spec) 'quote)
  261. (loop rest
  262. (cons (cadr spec) vlist)
  263. code))
  264. (else
  265. (loop rest
  266. (cons (car spec) vlist)
  267. `((,(car spec)
  268. (,(r 'make-variable) ',(car spec) ,(cadr spec)))
  269. . ,code)))))))))
  270. ;------------------------------------------------------------------------------
  271. ; GENSYM utility
  272. (define *generate-symbol-index* 0)
  273. (define (generate-symbol sym)
  274. (let ((i *generate-symbol-index*))
  275. (set! *generate-symbol-index* (+ i 1))
  276. (concatenate-symbol sym "." i)))