recurse.lisp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. (import test ())
  2. (import tests/compiler/codegen/codegen-helpers ())
  3. (describe "The codegen"
  4. (will-not "fail on empty 'recursive' lambdas"
  5. ;; Historically this would crash the categoriser as it thinks this should recurse,
  6. ;; but it never does.
  7. (affirm-codegen
  8. '(((lambda (recur)
  9. (set! recur (lambda ()))
  10. (recur))))
  11. "local recur
  12. recur = function()
  13. return nil
  14. end
  15. return recur()"))
  16. (will "correctly propagate breaks"
  17. (affirm-codegen
  18. '(((lambda (recur)
  19. (set! recur (lambda ()
  20. ((lambda (x)
  21. (cond
  22. [x (recur)]
  23. [true]))
  24. foo)))
  25. (recur)))
  26. nil)
  27. "while true do
  28. local x = foo
  29. if x then
  30. else
  31. break
  32. end
  33. end
  34. return nil"))
  35. (will "handle binding variables to themselves"
  36. (affirm-codegen
  37. '(((lambda (recur)
  38. (set! recur (lambda (x y)
  39. (recur x (+ y 1))))
  40. (recur 1 1))))
  41. "local x, y = 1, 1
  42. while true do
  43. y = y + 1
  44. end"))
  45. (section "will generate the correct bindings when"
  46. (it "no variables are changed"
  47. (affirm-codegen
  48. '(((lambda (recur)
  49. (set! recur (lambda (x) (recur x)))
  50. (recur 1))))
  51. "local x = 1
  52. while true do
  53. end"))
  54. (it "no variables are bound"
  55. (affirm-codegen
  56. '(((lambda (recur)
  57. (set! recur (lambda (x) (recur)))
  58. (recur 1))))
  59. "local x = 1
  60. while true do
  61. x = nil
  62. end"))
  63. (it "the correct number of args are used"
  64. (affirm-codegen
  65. '(((lambda (recur)
  66. (set! recur (lambda (x y) (recur 1 2)))
  67. (recur))))
  68. "local x, y
  69. while true do
  70. x, y = 1, 2
  71. end")
  72. (affirm-codegen
  73. '(((lambda (recur)
  74. (set! recur (lambda (x &y) (recur 1 2 3)))
  75. (recur))))
  76. "local x
  77. local y = {tag=\"list\", n=0}
  78. while true do
  79. x, y = 1, _pack(2, 3)
  80. y.tag = \"list\"
  81. end"))
  82. (it "with extra arguments"
  83. (affirm-codegen
  84. '(((lambda (recur)
  85. (set! recur (lambda (a b) (recur 1 2 3)))
  86. (recur))))
  87. "local a, b
  88. while true do
  89. a, b = 1, 2, 3
  90. end"))
  91. (it "with unknown values"
  92. (affirm-codegen
  93. '(((lambda (recur)
  94. (set! recur (lambda (a &b c) (recur (foo))))
  95. (recur))))
  96. "local a
  97. local b = {tag=\"list\", n=0}
  98. local c
  99. while true do
  100. local _p = _pack(foo())
  101. a = _p[1]
  102. local _n = _p.n
  103. if _n > 2 then
  104. b = {tag=\"list\", n=_n-2}
  105. for i=2, _n-1 do b[i-1]=_p[i] end
  106. c = _p[_n-0]
  107. else
  108. b = {tag=\"list\", n=0}
  109. c = _p[2]
  110. end
  111. end")))
  112. (section "handle variables being captured"
  113. (it "in set! bindings"
  114. (affirm-codegen
  115. '(((lambda (recur)
  116. (set! recur (lambda (x)
  117. (foo (lambda () x))
  118. (recur (+ x 1))))
  119. (recur 1))))
  120. "local x = 1
  121. while true do
  122. local x1 = x
  123. foo(function()
  124. return x1
  125. end)
  126. x = x1 + 1
  127. end"))
  128. (it "with loop termination"
  129. (affirm-codegen
  130. '(((lambda (recur)
  131. (set! recur (lambda (x)
  132. (cond
  133. [(bar x)
  134. (foo (lambda () x))
  135. (recur (+ x 1))]
  136. [true (foo x)])))
  137. (recur 1))))
  138. "local x = 1
  139. while bar(x) do
  140. local x1 = x
  141. foo(function()
  142. return x1
  143. end)
  144. x = x1 + 1
  145. end
  146. return foo(x)"))
  147. (it "in define bindings"
  148. (affirm-codegen
  149. '((define recur (lambda (x)
  150. (foo (lambda () x))
  151. (recur (+ x 1)))))
  152. "recur = function(x)
  153. while true do
  154. local x1 = x
  155. foo(function()
  156. return x1
  157. end)
  158. x = x1 + 1
  159. end
  160. end"))
  161. (it "unless they are not captured in a binding"
  162. (affirm-codegen
  163. '(((lambda (f)
  164. (set! f (lambda (x)
  165. ((lambda (a)
  166. (cond
  167. [a a]
  168. [true (f (+ x 1))]))
  169. (foo x))))
  170. (f 1))))
  171. "local x = 1
  172. while true do
  173. local a = foo(x)
  174. if a then
  175. return a
  176. else
  177. x = x + 1
  178. end
  179. end"))
  180. (it "unless they are captured in a nested loop"
  181. (affirm-codegen
  182. '(((lambda (f)
  183. (set! f (lambda (x)
  184. ((lambda (g)
  185. (set! g (lambda ()
  186. (foo x)
  187. (g)))
  188. (g)))
  189. (f 1)))
  190. (f 1))))
  191. "local x = 1
  192. while true do
  193. while true do
  194. foo(x)
  195. end
  196. x = 1
  197. end")))
  198. (section "will generate common patterns"
  199. (section "such as 'while x do'"
  200. (it "in return contexts"
  201. (affirm-codegen
  202. '(((lambda (recur)
  203. (set! recur (lambda (x)
  204. (cond
  205. [x (recur 0)]
  206. [true (foo)])))
  207. (recur 1))))
  208. "local x = 1
  209. while x do
  210. x = 0
  211. end
  212. return foo()"))
  213. (it "in non-return contexts"
  214. (affirm-codegen
  215. '(((lambda (recur)
  216. (set! recur (lambda (x)
  217. (cond
  218. [x (recur 0)]
  219. [true (foo)])))
  220. (recur 1)))
  221. nil)
  222. "local x = 1
  223. while x do
  224. x = 0
  225. end
  226. foo()
  227. return nil")))
  228. (section "such as 'while not x do'"
  229. ;; TODO: Remove additional parens
  230. (it "in return contexts"
  231. (affirm-codegen
  232. '(((lambda (recur)
  233. (set! recur (lambda (x)
  234. (cond
  235. [x (foo)]
  236. [true (recur 0)])))
  237. (recur 1))))
  238. "local x = 1
  239. while not (x) do
  240. x = 0
  241. end
  242. return foo()"))
  243. (it "in non-return contexts"
  244. (affirm-codegen
  245. '(((lambda (recur)
  246. (set! recur (lambda (x)
  247. (cond
  248. [x (foo)]
  249. [true (recur 0)])))
  250. (recur 1)))
  251. nil)
  252. "local x = 1
  253. while not (x) do
  254. x = 0
  255. end
  256. foo()
  257. return nil"))))
  258. (section "will not generate common patterns"
  259. (it "when there are too many branches"
  260. (affirm-codegen
  261. '(((lambda (recur)
  262. (set! recur (lambda (x)
  263. (cond
  264. [x (recur 0)]
  265. [foo (foo)]
  266. [true (bar)])))
  267. (recur 1))))
  268. "local x = 1
  269. while true do
  270. if x then
  271. x = 0
  272. elseif foo then
  273. return foo()
  274. else
  275. return bar()
  276. end
  277. end")))
  278. )