for88.red 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. module for88; % Definition of Rlisp88 FOR statement.
  2. % Author: Anthony C. Hearn.
  3. fluid '(!*fastfor binops!* loopdelimslist!*);
  4. global '(forkeywords!*);
  5. flag('(fastfor),'switch); % Since switch may not yet be defined.
  6. Comment The FOR statement defined here has a very rich syntax with many
  7. different options. The parsing and macro expansion are under the control
  8. of keywords that are activated during parsing once FOR has been read.
  9. The keywords are deactivated at the end of the FOR statement, enabling
  10. them to be used as regular ID's in other parts of the program.
  11. The next ID after FOR may define a different type of FOR loop. Such
  12. different loops are indicated by the presence of the ID in the list
  13. forloops!*;
  14. deflist('((all forallstat)),'forloops!*);
  15. Comment
  16. Keywords are defined by their presence in the global list FORKEYWORDS!*.
  17. For each keyword, a parsing construct is also defined under the
  18. indicator FOR-KEYWORD.
  19. The parsing phase of the analysis returns a form:
  20. (FOR (<keyword> . <expression>) ... (<keyword> . <expression>));
  21. forkeywords!* := '(collect count do each every finally in initially
  22. join on product returns some step sum unless until
  23. when with maximize minimize);
  24. % Note: append used to be on the above list, but was removed since it
  25. % couldn't be distinguished from the function "append".
  26. remflag(forkeywords!*,'delim); % For bootstrapping purposes.
  27. Comment some of the keywords denote actions (e.g., PRODUCT, SUM) with
  28. which a binary function is associated. To associate such a function with
  29. an action, one says;
  30. forbinops!* := '((append append) (collect cons) (count plus2)
  31. (join nconc) (maximize max2!*) (minimize min2!*)
  32. (product times2) (sum plus2));
  33. % NB: We need to reset FOR and LET delims if an error occurs. It's
  34. % probably best to do this in the begin1 loop.
  35. symbolic procedure forstat88;
  36. begin scalar !*blockp,x;
  37. if x := get(scan(),'forloops!*) then return lispapply(x,nil);
  38. loopdelimslist!* := forkeywords!* . loopdelimslist!*;
  39. flag(forkeywords!*,'delim);
  40. return 'for . if cursym!* neq 'each
  41. then progn(x := forfrag(), x . fortail())
  42. else fortail()
  43. end;
  44. symbolic procedure forfrag;
  45. begin scalar incr,var,x;
  46. x := erroreval '(xread1 'for);
  47. if not eqcar(x,'setq) or not idp(var := cadr x)
  48. then symerr('for,t);
  49. x := caddr x;
  50. if cursym!* eq 'step
  51. then <<incr := erroreval '(xread t);
  52. if not(cursym!* eq 'until) then symerr('for,t)>>
  53. else if cursym!* eq '!*colon!* then incr := 1
  54. else symerr('for,t);
  55. return list('incr,var,x,erroreval '(xread t),incr)
  56. % if numberp incr and incr>0
  57. % then incr := list('from,var,x,erroreval '(xread t),incr)
  58. % else if eqcar(incr,'minus) and numberp cadr incr and cadr incr>0
  59. % then incr := list('down,var,x,erroreval '(xread t),cadr incr)
  60. % else rederr list("Increment",incr,"not supported");
  61. % return incr
  62. end;
  63. symbolic procedure erroreval u;
  64. begin scalar x;
  65. x := errorset!*(u,t);
  66. if errorp x then error1() else return car x
  67. end;
  68. symbolic procedure eachfrag;
  69. begin scalar x,y;
  70. if not idp(x := scan()) or not((y := scan()) memq '(in on))
  71. then symerr("For each",t);
  72. return list(y,x,erroreval '(xread t));
  73. end;
  74. symbolic procedure fortail;
  75. begin scalar x,y,z,z1;
  76. a: z1 := cursym!*;
  77. if z1 eq 'each
  78. then if not idp(x := scan())
  79. or not((y := scan()) memq '(in on))
  80. then symerr("FOR EACH",t)
  81. else <<z := list(y,x,erroreval '(xread t)) . z;
  82. go to a>>
  83. else if z1 eq 'with
  84. then z := (z1 . erroreval '(xread 'lambda)) . z
  85. else if z1 eq '!*semicol!* then symerr("FOR EACH",t)
  86. else z := (z1 . erroreval '(xread t)) . z;
  87. if cursym!* memq forkeywords!* then go to a;
  88. remflag(car loopdelimslist!*,'delim);
  89. loopdelimslist!* := cdr loopdelimslist!*;
  90. if loopdelimslist!* then flag(car loopdelimslist!*,'delim);
  91. return reversip z
  92. end;
  93. symbolic procedure formfor88(u,vars,mode);
  94. begin scalar x,y,z;
  95. u := z := cdr u;
  96. % First check for local vars.
  97. a: if null z then go to b;
  98. x := car z;
  99. if car x memq '(down from incr in on)
  100. then vars := (cadr x . 'scalar) . vars;
  101. if null(car x eq 'with) then progn(z := cdr z,go to a);
  102. x := remcomma cdr x;
  103. a0: if x then progn(y := (car x . 'scalar) . y, x := cdr x, go to a0);
  104. vars := nconc(reversip!* y,vars);
  105. z := cdr z;
  106. go to a;
  107. % Now do actual analysis.
  108. b: if null u then return 'for . reversip z;
  109. x := car u;
  110. if car x memq '(down from incr)
  111. % We could optimize this by recognizing integers.
  112. then z := (car x . cadr x . formclis(cddr x,vars,mode)) . z
  113. else if car x eq 'with then z := (car x . remcomma cdr x) . z
  114. else if car x memq '(in on)
  115. then z := (car x . list(cadr x,formc(caddr x,vars,mode))) . z
  116. else z := (car x . formc(cdr x,vars,mode)) . z;
  117. u := cdr u;
  118. go to b
  119. end;
  120. symbolic macro procedure for88 x;
  121. begin scalar lvars,init,init2,final,body,!$cond,rets,cur,!$when,
  122. !*maxminflag,next,!$label2,!$while,cx,iv,action,curvar,
  123. valuevar,y;
  124. x := cdr x;
  125. action := caar x;
  126. !$label2 := gensym();
  127. loop:
  128. if null x
  129. then <<final := mkfn(final,'progn);
  130. next := mkfn(next,'progn);
  131. !$cond := mkfn(!$cond,'or);
  132. cur := mkfn(cur,'progn);
  133. body := mkfn(body,'progn);
  134. if !$while
  135. then !$while := forcond
  136. sublis(pair('(!$while final rets),
  137. list(mkfn(!$while,'or),
  138. final,rets)),
  139. '(!$while final
  140. (return rets)));
  141. if !$when
  142. then body := forcond list(!$when,body);
  143. if !*maxminflag then rets := list('null2zero,rets);
  144. return forprog(lvars .
  145. nconc(init,
  146. nconc(init2,
  147. sublis(pair('(final body !$cond rets cur next
  148. !$label !$label2 !$while),
  149. list(final,body,!$cond,rets,cur,next,
  150. gensym(),!$label2,!$while)),
  151. if final then
  152. '(!$label
  153. (cond (!$cond
  154. (progn final (return rets))))
  155. cur
  156. !$while
  157. body
  158. !$label2
  159. next
  160. (go !$label))
  161. else
  162. '(!$label
  163. (cond (!$cond (return rets)))
  164. cur
  165. !$while
  166. body
  167. !$label2
  168. next
  169. (go !$label))))))>>;
  170. cx := car x;
  171. if atom cx then rederr list(cx,"invalid in FOR form")
  172. % WITH tacks its variables onto the !$LVARS list
  173. else if car cx eq 'with
  174. then lvars := append(lvars,cdr cx)
  175. % INITIALLY takes its expressions and tacks them onto the list of
  176. % INIT. This will later be built into a PROGN.
  177. else if car cx eq 'initially
  178. then init := aconc(init,cdr cx)
  179. % FINALLY puts its expressions on the list of FINAL.
  180. % This becomes a PROGN that is created just before the RETURN.
  181. else if car cx eq 'finally
  182. then final := aconc(final,cdr cx)
  183. % ON
  184. else if car cx eq 'on
  185. then <<valuevar := cadr cx;
  186. lvars := valuevar . lvars;
  187. !$cond := list('null,valuevar) . !$cond;
  188. init := list('setq,valuevar,caddr cx) . init;
  189. if cdddr cx
  190. then next := list('setq,valuevar,cadddr x) . next
  191. else next := list('setq, valuevar,list('cdr,valuevar))
  192. . next>>
  193. % IN
  194. else if car cx eq 'in
  195. then <<valuevar := gensym();
  196. iv := cadr cx;
  197. lvars := valuevar . iv . lvars;
  198. init := list('setq,valuevar,caddr cx) . init;
  199. !$cond := list('null,valuevar) . !$cond;
  200. cur := list('setq,iv,list('car,valuevar)) . cur;
  201. if cdddr cx
  202. then next := list('setq,valuevar,list cadddr cx) . next
  203. else next := list('setq,valuevar,list('cdr,valuevar))
  204. . next>>
  205. % INCR
  206. else if car cx eq 'incr
  207. then begin scalar incr,incrvar;
  208. valuevar := cadr cx;
  209. cx := cddr cx;
  210. lvars := valuevar . lvars;
  211. init := list('setq,valuevar,car cx) . init;
  212. incr := caddr cx;
  213. if numberp incr then nil % Assume positive?
  214. else if eqcar(incr,'minus) and numberp cadr incr
  215. then incr := - cadr incr
  216. else <<incrvar := gensym();
  217. lvars := incrvar . lvars;
  218. init := list('setq,incrvar,incr) . init;
  219. incr := incrvar>>;
  220. !$cond :=
  221. (if incrvar
  222. then list('cond,list(list('minusp,incr),
  223. list('lessp,valuevar,cadr cx)),
  224. list('t,list('greaterp,valuevar,
  225. cadr cx)))
  226. else if minusp incr
  227. then if !*fastfor
  228. then list('ilessp,valuevar,cadr cx)
  229. else list('lessp,valuevar,cadr cx)
  230. else if !*fastfor
  231. then list('igreaterp,valuevar,cadr cx)
  232. else list('greaterp,valuevar,cadr cx))
  233. . !$cond;
  234. next := list('setq,valuevar,
  235. list(if incrvar or not !*fastfor
  236. then 'plus2
  237. else 'iplus2,
  238. valuevar,incr)) . next
  239. end
  240. % SUM, PRODUCT etc.
  241. else if car cx memq '(sum product append join count collect
  242. maximize minimize)
  243. then <<curvar := gensym();
  244. lvars := curvar . lvars;
  245. % Set up initial value for loop.
  246. if car cx eq 'product
  247. then init := aconc!*(init,list('setq,curvar,1))
  248. else if car cx memq '(count sum)
  249. then init := aconc!*(init,list('setq,curvar,0))
  250. else if car cx memq '(maximize minimize)
  251. then <<!*maxminflag := t;
  252. %y := list(list('setq,curvar,cdr cx),
  253. % list('go,!$label2));
  254. if action eq 'in
  255. then y :=
  256. list('setq,iv,list('car,valuevar)); % . y;
  257. if action memq '(in on)
  258. then y :=
  259. list('cond,list(list('null,valuevar),
  260. '(return 0)))
  261. . y;
  262. nconc!*(init,y)>>;
  263. if car cx eq 'collect
  264. then rets := list('reversip,curvar)
  265. else rets := curvar;
  266. body := list('setq,curvar,
  267. list(get(car cx,'bin),
  268. if car cx memq '(append count join) then curvar
  269. else cdr cx,
  270. if car cx memq '(append join) then cdr cx
  271. else if car cx eq 'count
  272. then list('cond,list(cdr cx,1),'(t 0))
  273. else curvar))
  274. . body>>
  275. % RETURNS
  276. else if car cx eq 'returns then rets := cdr cx
  277. % DO
  278. else if car cx eq 'do then body := aconc(body,cdr cx)
  279. % WHEN
  280. else if car cx eq 'when
  281. then if !$when
  282. then symerr("Redundant WHEN or UNLESS in FOR statement",
  283. nil)
  284. else !$when := cdr cx
  285. % UNLESS
  286. else if car cx eq 'unless
  287. then if !$when
  288. then symerr("Redundant WHEN or UNLESS in FOR statement",
  289. nil)
  290. else !$when := list('not,cdr cx)
  291. % WHILE
  292. % else if car cx eq 'while
  293. % then !$while := append(!$while,list list('not,cdr cx))
  294. % UNTIL
  295. else if car cx eq 'until
  296. then !$while := append(!$while,list cdr cx)
  297. % SOME
  298. else if car cx eq 'some
  299. then cur := append(cur,
  300. list list('cond,list(cdr cx,list('return,t))))
  301. % EVERY
  302. else if car cx eq 'every
  303. then <<if not rets then rets := t;
  304. cur := append(cur,
  305. list list('cond,list(list('null,cdr cx),
  306. list('return,nil))))>>
  307. else rederr list(car cx,"invalid in FOR form");
  308. x := cdr x;
  309. go to loop
  310. end;
  311. symbolic procedure forcond u;
  312. list('cond,list(car u,if cddr u then 'progn . cdr u else cadr u));
  313. symbolic procedure forprog u;
  314. 'prog . fornilchk u;
  315. symbolic procedure fornilchk u;
  316. if null u then nil
  317. else if null car u then fornilchk cdr u
  318. else car u . fornilchk cdr u;
  319. symbolic procedure max2!*(u,v); if null v then u else max2(u,v);
  320. symbolic procedure min2!*(u,v); if null v then u else min2(u,v);
  321. symbolic procedure null2zero u; if null u then 0 else u;
  322. symbolic procedure mkfn(x,fn);
  323. if atom x then x else if length x>1 then fn . x else car x;
  324. endmodule;
  325. end;