let-nodes.scm 11 KB

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