rlisp88.tst 80 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699
  1. % Test of Rlisp88 version of Rlisp. Many of these functions are taken
  2. % from the solved exercises in the book "RLISP '88: An Evolutionary
  3. % Approach to Program Design and Reuse".
  4. % Author: Jed B. Marti.
  5. on rlisp88;
  6. % Confidence test tries to do a little of everything. This doesn't really
  7. % test itself so you need to compare to the log file. Syntax errors on
  8. % the other hand should be cause for alarm.
  9. % ARRAYS
  10. % 1. Single dimension array.
  11. global '(v1);
  12. v1 := mkarray 5;
  13. for i:=0:5 do v1[i] := 3**i;
  14. v1;
  15. % 2. 2D array.
  16. global '(v3x3);
  17. v3x3 := mkarray(2, 2);
  18. for row := 0:2 do
  19. for col := 0:2 do
  20. v3x3[row, col] := if row = col then 1.0 else 0.0;
  21. v3x3;
  22. % 3. Triangular array.
  23. global '(tri);
  24. tri := mkarray 3;
  25. for row := 0:3 do tri[row] := mkarray row;
  26. for row := 0:3 do
  27. for col := 0:row do
  28. tri[row,col] := row * col;
  29. tri;
  30. % 4. ARRAY test.
  31. expr procedure rotate theta;
  32. /* Generates rotation array for angle theta (in radians) */
  33. array(array(cosd theta, - sind theta, 0.0),
  34. array(sind theta, cosd theta, 0.0),
  35. array(0.0, 0.0, 1.0));
  36. rotate 45.0;
  37. % 5. Random elements.
  38. % Now create a vector with random elements.
  39. M3 := ARRAY('A, 3 + 4, ARRAY("String", 'ID), '(a b));
  40. M3[2, 1];
  41. M4 := ARRAY(ARRAY('a, 'b), ARRAY('c, 'd));
  42. M4[1];
  43. % 6. Array addition.
  44. expr procedure ArrayAdd(a, b);
  45. if vectorp a then
  46. for i:=0:uc
  47. with c, uc
  48. initially c := mkarray(uc := upbv a)
  49. do c[i] := ArrayAdd(a[i], b[i])
  50. returns c
  51. else a + b;
  52. ArrayAdd(array(array(array(1, 2), array(3, 4)),
  53. array(array(5, 6), array(7, 8))),
  54. array(array(array(1, 1), array(2, 2)),
  55. array(array(3, 3), array(4, 4))));
  56. % RECORDS
  57. % 1: Declaration.
  58. RECORD MAPF /* A MAPF record defines
  59. the contents of a MAPF file. */
  60. WITH
  61. MAPF!:NAME := "" /* Name of MAPF (a string) */,
  62. MAPF!:NUMBER := 0 /* MAPF number (integer) */,
  63. MAPF!:ROAD-COUNT := 0 /* Number of roads */,
  64. MAPF!:NODE-COUNT := 0 /* Number of nodes */,
  65. MAPF!:LLAT := 0.0 /* Lower left hand corner map latitude */,
  66. MAPF!:LLONG := 0.0 /* Lower left hand corner map longitude */,
  67. MAPF!:ULAT := 0.0 /* Upper right hand corner map latitude */,
  68. MAPF!:ULONG := 0.0 /* Upper right hand corner map longitude */;
  69. % 2: Creation.
  70. global '(r1 r2 r3);
  71. r1 := mapf();
  72. r2 := mapf(mapf!:name := "foobar", mapf!:road-count := 34);
  73. r3 := list('a . r1, 'b . r2);
  74. % 3: Accessing.
  75. mapf!:number r1;
  76. mapf!:road-count cdr assoc('b, r3);
  77. % 4: Assignment.
  78. mapf!:number r1 := 7622;
  79. mapf!:road-count cdr assoc('b, r3) := 376;
  80. mapf!:node-count(mapf!:name r2 := mapf()) := 34;
  81. r2;
  82. % 5. Options.
  83. RECORD complex /* Stores complex reals */
  84. WITH
  85. R := 0.0 /* Real part */,
  86. I := 0.0 /* Imaginary part */
  87. HAS CONSTRUCTOR;
  88. Make-Complex(I := 34.0, R := 12.0);
  89. RECORD Rational /* Representation of rational numbers */
  90. WITH
  91. Num := 0 /* Numerator */,
  92. Den := 1 /* Denominator */
  93. HAS CONSTRUCTOR = rat;
  94. expr procedure gcd(p, q);
  95. if q > p then gcd(q, p)
  96. else (if r = 0 then q else gcd(q, r)) where r = remainder(p,q);
  97. expr procedure Rational(a, b);
  98. /* Build a rational number in lowest terms */
  99. Rat(Num := a / g, Den := b / g) where g := gcd(a, b);
  100. Rational(34, 12);
  101. RECORD Timing /* Timing Record for RLISP test */
  102. WITH
  103. Machine := "" /* Machine name */,
  104. Storage := 0 /* Main storage in bits */,
  105. TimeMS = 0 /* Test time in milliseconds */
  106. HAS NO CONSTRUCTOR;
  107. % PREDICATE option.
  108. RECORD History /* Record of an event */
  109. WITH
  110. EventTime := 0.0 /* Time of event (units) */,
  111. EventData := NIL /* List with (type ...) */
  112. HAS PREDICATE = History!?;
  113. History!? History(EventData := '(MOVE 34.5 52.5));
  114. % FOR LOOP
  115. % 1) Basic test.
  116. EXPR PROCEDURE LPRINT lst;
  117. /* LPRINT displays each element of its argument separated by blanks.
  118. After the last element has been displayed, the print line is
  119. terminated. */
  120. FOR EACH element IN lst
  121. DO << PRIN2 element; PRINC " " >>
  122. FINALLY TERPRI()
  123. RETURNS lst;
  124. LPRINT '(Now is the time to use RLISP);
  125. % 2) Basic iteration in both directions.
  126. FOR i:=5 STEP -2 UNTIL 0 DO PRINT i;
  127. FOR i:=1:3 DO PRINT i;
  128. % 3) COLLECT option.
  129. FOR EACH leftpart IN '(A B C)
  130. EACH rightpart IN '(1 2 "string")
  131. COLLECT leftpart . rightpart;
  132. % 4) IN/ON iterators.
  133. FOR EACH X IN '(a b c) DO PRINT x;
  134. FOR EACH x ON '(a b c) DO PRINT x;
  135. % 5) EVERY option.
  136. FOR EACH x IN '(A B C) EVERY IDP x
  137. RETURNS "They are all id's";
  138. FOR EACH x IN '(A B 12) EVERY IDP x
  139. RETURNS "They are all id's";
  140. % 6) INITIALLY/FINALLY option.
  141. EXPR PROCEDURE ListPrint x;
  142. /* ListPrint(x) displays each element of x separated by blanks. The
  143. first element is prefixed with "*** ". The last element is suffixed
  144. with a period and a new line. */
  145. FOR EACH element ON x
  146. INITIALLY PRIN2 "*** "
  147. DO << PRIN2 CAR element;
  148. IF CDR element THEN PRIN2 " " >>
  149. FINALLY << PRIN2 "."; TERPRI() >>;
  150. ListPrint '(The quick brown bert died);
  151. % 7) MAXIMIZE/MINIMIZE options.
  152. FOR EACH x IN '(A B 12 -34 2.3)
  153. WHEN NUMBERP x
  154. MAXIMIZE x;
  155. FOR EACH x IN '(A B 12 -34 2.3)
  156. WHEN NUMBERP x
  157. MINIMIZE x;
  158. % 8) RETURNS option.
  159. EXPR PROCEDURE ListFiddle(f, x);
  160. /* ListFiddle displays every element of its second argument and returns
  161. a list of those for which the first argument returns non-NIL. */
  162. FOR EACH element IN x
  163. WITH clist
  164. DO << PRINT element;
  165. IF APPLY(f, LIST element) THEN clist := element . clist >>
  166. RETURNS REVERSIP clist;
  167. ListFiddle(FUNCTION ATOM, '(a (BANG 12) "OOPS!"));
  168. % 9) SOME option.
  169. FOR EACH x IN '(a b 12) SOME NUMBERP x
  170. DO PRINT x;
  171. % 10) UNTIL/WHILE options.
  172. EXPR PROCEDURE CollectUpTo l;
  173. /* CollectUpTo collect all the elements of the list l up to the
  174. first number. */
  175. FOR EACH x IN l UNTIL NUMBERP x COLLECT x;
  176. CollectUpTo '(a b c 1 2 3);
  177. % 11) WHEN/UNLESS options.
  178. FOR EACH x IN '(A 12 "A String" 32)
  179. WHEN NUMBERP x
  180. COLLECT x;
  181. % ##### Basic Tests #####
  182. % Tests some very basic things that seem to go wrong frequently.
  183. % Numbers.
  184. if +1 neq 1 then error(0, "+1 doesn't parse");
  185. if -1 neq - 1 then error(0, "-1 doesn't parse");
  186. expr procedure factorial n;
  187. if n < 2 then 1 else n * factorial(n - 1);
  188. if +2432902008176640000 neq factorial 20 then
  189. error(0, "bignum + doesn't work");
  190. if -2432902008176640000 neq - factorial 20 then
  191. error(0, "bignum - doesn't work");
  192. % This actually blew up at one time.
  193. if -3.14159 neq - 3.14159 then error(0, "negative floats don't work");
  194. if +3.14159 neq 3.14159 then error(0, "positive floats don't work");
  195. % ##### Safe Functions #####
  196. % Description: A set of CAR/CDR alternatives that
  197. % return NIL when CAR/CDR of an atom is tried.
  198. expr procedure SafeCar x;
  199. /* Returns CAR of a list or NIL. */
  200. if atom x then nil else car x;
  201. expr procedure SafeCdr x;
  202. /* Returns CDR of a list or NIL. */
  203. if atom x then nil else cdr x;
  204. expr procedure SafeFirst x; SafeCar x;
  205. expr procedure SafeSecond x; SafeCar SafeCdr x;
  206. expr procedure SafeThird x; SafeSecond SafeCdr x;
  207. % ##### Test of Procedures #####
  208. %------------------------- Exercise #1 -------------------------
  209. expr procedure delassoc(x, a);
  210. /* Delete the element from x from the alist a non-destructively. Returns
  211. the reconstructed list. */
  212. if null a then nil
  213. else if atom a then a . delassoc(x, cdr a)
  214. else if caar a = x then cdr a
  215. else car a . delassoc(x, cdr a);
  216. if delassoc('a, '((a b) (c d))) = '((c d))
  217. then "Test 1 delassoc OK"
  218. else error(0, "Test 1 delassoc failed");
  219. if delassoc('b, '((a b) (b c) (c d))) = '((a b) (c d))
  220. then "Test 2 delassoc OK"
  221. else error(0, "Test 2 delassoc failed");
  222. if delassoc('c, '((a b) (b c) (c d))) = '((a b) (b c))
  223. then "Test 3 delassoc OK"
  224. else error(0, "Test 3 delassoc failed");
  225. if delassoc('d, '((a b) (b c) (c d))) = '((a b) (b c) (c d))
  226. then "Test 4 delassoc OK"
  227. else error(0, "Test 4 delassoc failed");
  228. %------------------------- Exercise #2 -------------------------
  229. expr procedure gcd(u, v);
  230. if v = 0 then u else gcd(v, remainder(u, v));
  231. if gcd(2, 4) = 2 then "Test 1 GCD OK" else error(0, "Test 1 GCD fails");
  232. if gcd(13, 7) = 1
  233. then "Test 2 GCD OK" else error(0, "Test 2 GCD fails");
  234. if gcd(15, 10) = 5
  235. then "Test 3 GCD OK" else error(0, "Test 3 GCD fails");
  236. if gcd(-15, 10) = -5
  237. then "Test 4 GCD OK" else error(0, "Test 4 GCD fails");
  238. if gcd(-15, 0) = -15
  239. then "Test 5 GCD OK" else error(0, "Test 5 GCD fails");
  240. %-------------------- Exercise #3 --------------------
  241. expr procedure properintersection(a, b);
  242. /* Returns the proper intersection of proper sets a and b.
  243. The set representation is a list of elements with the
  244. EQUAL relation. */
  245. if null a then nil
  246. else if car a member b then car a . properintersection(cdr a, b)
  247. else properintersection(cdr a, b);
  248. % Test an EQ intersection.
  249. properintersection('(a b), '(b c));
  250. if properintersection('(a b), '(b c)) = '(b)
  251. then "Test 1 properintersection OK"
  252. else error(0, "Test 1 properintersection fails");
  253. % Test an EQUAL intersection.
  254. properintersection('((a) b (c)), '((a) b (c)));
  255. if properintersection('((a) b (c)), '((a) b (c))) = '((a) b (c))
  256. then "Test 2 properintersection OK"
  257. else error(0, "Test 2 properintersection fails");
  258. % Test an EQUAL intersection, out of order.
  259. properintersection('((a) b (c)), '(b (c) (a)));
  260. if properintersection('((a) b (c)), '(b (c) (a))) = '((a) b (c))
  261. then "Test 3 properintersection OK"
  262. else error(0, "Test 3 properintersection fails");
  263. % Test an empty intersection.
  264. properintersection('((a) b (c)), '(a (b) c));
  265. if properintersection('((a) b (c)), '(a (b) c)) = nil
  266. then "Test 4 properintersection OK"
  267. else error(0, "Test 4 properintersection fails");
  268. %-------------------- Exercise #4 -------------------------
  269. expr procedure TreeVisit(a, tree, c);
  270. /* Preorder visit of tree to find a. Returns path from root. c
  271. contains path to root of tree so far. */
  272. if null tree then nil
  273. else if a = car tree then append(c, {a})
  274. else TreeVisit(a, cadr tree, append(c, {car tree})) or
  275. TreeVisit(a, caddr tree, append(c, {car tree}));
  276. TreeVisit('c, '(a (b (d nil nil) (c nil nil)) (e nil nil)), nil);
  277. if TreeVisit('c, '(a (b (d nil nil) (c nil nil)) (e nil nil)), nil)
  278. = '(a b c)
  279. then "Test 1 TreeVisit OK"
  280. else error(0, "Test 1 TreeVisit fails");
  281. TreeVisit('h, '(a (b (d nil nil) (c nil nil))
  282. (e (f nil nil) (g (h nil nil) nil)) ), nil);
  283. if TreeVisit('h, '(a (b (d nil nil) (c nil nil))
  284. (e (f nil nil) (g (h nil nil) nil))),nil) = '(a e g h)
  285. then "Test 2 TreeVisit OK"
  286. else error(0, "Test 2 TreeVisit fails");
  287. if TreeVisit('i, '(a (b (d nil nil) (c nil nil))
  288. (e (f nil nil) (g (h nil nil) nil)) ), nil) = nil
  289. then "Test 3 TreeVisit OK"
  290. else error(0, "Test 3 TreeVisit fails");
  291. if TreeVisit('a, '(a (b (d nil nil) (c nil nil))
  292. (e (f nil nil) (g (h nil nil) nil)) ), nil) = '(a)
  293. then "Test 4 TreeVisit OK"
  294. else error(0, "Test 4 TreeVisit fails");
  295. if TreeVisit('e, '(a (b (d nil nil) (c nil nil))
  296. (e (f nil nil) (g (h nil nil) nil)) ), nil) = '(a e)
  297. then "Test 5 TreeVisit OK"
  298. else error(0, "Test 5 TreeVisit fails");
  299. %-------------------- Exercise #5 -------------------------
  300. expr procedure lookfor(str, l);
  301. /* Search for the list str (using =) in the top level
  302. of list l. Returns str and remaining part of l if
  303. found. */
  304. if null l then nil
  305. else if lookfor1(str, l) then l
  306. else lookfor(str, cdr l);
  307. expr procedure lookfor1(str, l);
  308. if null str then t
  309. else if null l then nil
  310. else if car str = car l then lookfor1(cdr str, cdr l);
  311. if lookfor('(n o w),'(h e l l o a n d n o w i t i s)) = '(n o w i t i s)
  312. then "Test 1 lookfor OK"
  313. else error(0, "Test 1 lookfor fails");
  314. if lookfor('(now is), '(now we have nothing is)) = NIL
  315. then "Test 2 lookfor OK"
  316. else error(0, "Test 2 lookfor fails");
  317. if lookfor('(now is), '(well hello!, now)) = NIL
  318. then "Test 3 lookfor OK"
  319. else error(0, "Test 3 lookfor fails");
  320. %-------------------- Exercise #6 -------------------------
  321. expr procedure add(a, b, carry, modulus);
  322. /* Add two numbers stored as lists with digits of
  323. modulus. Carry passes the carry around. Tries to
  324. suppress leading 0's but fails with negatives. */
  325. if null a then
  326. if null b then if zerop carry then nil
  327. else {carry}
  328. else remainder(carry + car b, modulus) .
  329. add(nil, cdr b, (carry + car b) / modulus, modulus)
  330. else if null b then add(b, a, carry, modulus)
  331. else remainder(car a + car b + carry, modulus) .
  332. add(cdr a, cdr b, (car a + car b + carry) / modulus,
  333. modulus);
  334. if add('(9 9), '(9 9), 0, 10) = '(8 9 1)
  335. then "Test 1 add OK"
  336. else error(0, "Test 1 add fails");
  337. if add('(-9 -9), '(9 9), 0, 10) = '(0 0)
  338. then "Test 2 add OK"
  339. else error(0, "Test 2 add fails");
  340. if add('(9 9 9), '(9 9 9 9), 0, 10) = '(8 9 9 0 1)
  341. then "Test 3 add OK"
  342. else error(0, "Test 3 add fails");
  343. if add('(99 99 99), '(99 99 99 99), 0, 100) = '(98 99 99 0 1)
  344. then "Test 4 add OK"
  345. else error(0, "Test 4 add fails");
  346. if add('(13 12), '(15 1), 0, 16) = '(12 14)
  347. then "Test 5 add OK"
  348. else error(0, "Test 5 add fails");
  349. %-------------------- Exercise #7 -------------------------
  350. expr procedure clength(l, tmp);
  351. /* Compute the length of the (possibly circular) list l.
  352. tmp is used to pass values looked at down the list. */
  353. if null l or l memq tmp then 0
  354. else 1 + clength(cdr l, l . tmp);
  355. if clength('(a b c), nil) = 3
  356. then "Test 1 clength OK"
  357. else error(0, "Test 1 clength fails");
  358. << xxx := '(a b c); cdr lastpair xxx := xxx; nil >>;
  359. if clength(xxx, nil) = 3
  360. then "Test 2 clength OK"
  361. else error(0, "Test 1 clength fails");
  362. if clength(append('(a b c), xxx), nil) = 6
  363. then "Test 3 clength OK"
  364. else error(0, "Test 1 clength fails");
  365. %------------------------- Exercise #8 -------------------------
  366. expr procedure fringe x;
  367. /* FRINGE(X) -- returns the fringe of X (the atoms at the
  368. end of the tree structure of X). */
  369. if atom x then {x}
  370. else if cdr x then append(fringe car x, fringe cdr x)
  371. else fringe car x;
  372. if fringe nil = '(NIL)
  373. then "Test 1 fringe OK"
  374. else error(0, "Test 1 fringe fails");
  375. if fringe '(a b . c) = '(a b c)
  376. then "Test 2 fringe OK"
  377. else error(0, "Test 2 fringe fails");
  378. if fringe '((((a) . b) (c . d)) . e) = '(a b c d e)
  379. then "Test 3 fringe OK"
  380. else error(0, "Test 3 fringe fails");
  381. %------------------------- Exercise #9 -------------------------
  382. expr procedure delall(x, l);
  383. /* DELALL(X, L) -- Delete all X's from the list L using EQUAL
  384. test. The list is reconstructed. */
  385. if null l then nil
  386. else if x = car l then delall(x, cdr l)
  387. else car l . delall(x, cdr l);
  388. if delall('X, nil) = NIL
  389. then "Test 1 delall OK"
  390. else error(0, "Test 1 delall fails");
  391. if delall('X, '(X)) = NIL
  392. then "Test 2 delall OK"
  393. else error(0, "Test 2 delall fails");
  394. if delall('X, '(A)) = '(A)
  395. then "Test 3 delall OK"
  396. else error(0, "Test 3 delall fails");
  397. if delall('(X B), '(A (B) (X B))) = '(A (B))
  398. then "Test 4 delall OK"
  399. else error(0, "Test 4 delall fails");
  400. if delall('(X B), '((X B) (X B))) = NIL
  401. then "Test 5 delall OK"
  402. else error(0, "Test 5 delall fails");
  403. if delall('(X B), '((X B) X B (X B))) = '(X B)
  404. then "Test 6 delall OK"
  405. else error(0, "Test 6 delall fails");
  406. % ------------------------- Exercise #10 -------------------------
  407. expr procedure startswith(prefix, word);
  408. /* STARTSWITH(PREFIX, WORD) -- Returns T if the list of
  409. characters WORD begins with the list of characters PREFIX. */
  410. if null prefix then T
  411. else if word then
  412. if car prefix eq car word then
  413. startswith(cdr prefix, cdr word);
  414. if startswith('(P R E), '(P R E S I D E N T)) = T
  415. then "Test 1 startswith OK!"
  416. else error(0, "Test 1 startswith fails");
  417. if startswith('(P R E), '(P O S T F I X)) = NIL
  418. then "Test 2 startswith OK!"
  419. else error(0, "Test 2 startswith fails");
  420. if startswith('(P R E), '(P R E)) = T
  421. then "Test 3 startswith OK!"
  422. else error(0, "Test 3 startswith fails");
  423. if startswith('(P R E), '(P R)) = NIL
  424. then "Test 4 startswith OK!"
  425. else error(0, "Test 4 startswith fails");
  426. if startswith('(P R E), NIL) = NIL
  427. then "Test 5 startswith OK!"
  428. else error(0, "Test 5 startswith fails");
  429. if startswith('(P R E), '(P P R E)) = NIL
  430. then "Test 6 startswith OK!"
  431. else error(0, "Test 6 startswith fails");
  432. % ##### Test of Definitions #####
  433. %------------------------- Exercise #1 -------------------------
  434. expr procedure goodlist l;
  435. /* GOODLIST(L) - returns T if L is a proper list. */
  436. if null l then T
  437. else if pairp l then goodlist cdr l;
  438. if goodlist '(a b c) = T
  439. then "Test 1 goodlist OK"
  440. else error(0, "Test 1 goodlist fails");
  441. if goodlist nil = T
  442. then "Test 2 goodlist OK"
  443. else error(0, "Test 2 goodlist fails");
  444. if goodlist '(a . b) = NIL
  445. then "Test 3 goodlist OK"
  446. else error(0, "Test 3 goodlist fails");
  447. %------------------------- Exercise #2 -------------------------
  448. expr procedure fmember(a, b, fn);
  449. /* FMEMBER(A, B, FN) - Returns rest of B is A is a member
  450. of B using the FN of two arguments as an equality check. */
  451. if null b then nil
  452. else if apply(fn, {a, car b}) then b
  453. else fmember(a, cdr b, fn);
  454. if fmember('a, '(b c a d), function EQ) = '(a d)
  455. then "Test 1 fmember is OK"
  456. else error(0, "Test 1 fmember fails");
  457. if fmember('(a), '((b c) (a) d), function EQ) = NIL
  458. then "Test 2 fmember is OK"
  459. else error(0, "Test 2 fmember fails");
  460. if fmember('(a), '((b c) (a) d), function EQUAL) = '((a) d)
  461. then "Test 3 fmember is OK"
  462. else error(0, "Test 3 fmember fails");
  463. if fmember(34, '(1 2 56 12), function LESSP) = '(56 12)
  464. then "Test 4 fmember is OK"
  465. else error(0, "Test 4 fmember fails");
  466. %------------------------- Exercise #3-4 -------------------------
  467. expr procedure findem(l, fn);
  468. /* FINDEM(L, FN) - returns a list of elements in L that satisfy
  469. the single argument function FN. */
  470. if null l then nil
  471. else if apply(fn, {car l}) then car l . findem(cdr l, fn)
  472. else findem(cdr l, fn);
  473. if findem('(a 1 23 b "foo"), function idp) = '(a b)
  474. then "Test 1 findem OK!"
  475. else error(0, "Test 1 findem fails");
  476. if findem('(1 3 a (44) 12 9),
  477. function (lambda x; numberp x and x < 10)) = '(1 3 9)
  478. then "Test 2 findem OK!"
  479. else error(0, "Test 2 findem fails");
  480. %------------------------- Exercise #5 -------------------------
  481. expr procedure insert(a, l, f);
  482. /* Insert the value a into list l based on the partial ordering function
  483. f(x,y). Non-destructive insertion. */
  484. if null l then {a}
  485. else if apply(f, {car l, a}) then a . l
  486. else car l . insert(a, cdr l, f);
  487. % Basic ascending order sort.
  488. insert(6, '(1 5 10), function geq);
  489. if insert(6, '(1 5 10), function geq) = '(1 5 6 10)
  490. then "Test 1 insert (>=) OK"
  491. else error(0, "Test 1 insert (>=) fails");
  492. % Try inserting element at end of list.
  493. insert(11, '(1 5 10), function geq);
  494. if insert(11, '(1 5 10), function geq) = '(1 5 10 11)
  495. then "Test 2 insert (>=) OK"
  496. else error(0, "Test 2 insert (>=) fails");
  497. % Tru inserting something at the list beginning.
  498. insert(-1, '(1 5 10), function geq);
  499. if insert(-1, '(1 5 10), function geq) = '(-1 1 5 10)
  500. then "Test 3 insert (>=) OK"
  501. else error(0, "Test 3 insert (>=) fails");
  502. % Insert into an empty list.
  503. insert('34, nil, function leq);
  504. if insert(34, nil, function leq) = '(34)
  505. then "Test 4 insert (<=) OK"
  506. else error(0, "Test 4 insert (<=) fails");
  507. % Use a funny insertion function for (order . any);
  508. expr procedure cargeq(a, b); car a >= car b;
  509. insert('(34 . any), '((5 . now) (20 . and) (30 . then) (40 . but)),
  510. function cargeq);
  511. if insert('(34 . any), '((5 . now) (20 . and) (30 . then) (40 . but)),
  512. function cargeq) = '((5 . now) (20 . and) (30 . then) (34 . any)
  513. (40 . but))
  514. then "Test 5 insert (>=) OK"
  515. else error(0, "Test 5 insert (>=) fails");
  516. % ###### FOR Loop Exercises #####
  517. %------------------------- Exercise #1 -------------------------
  518. expr procedure floatlist l;
  519. /* FLOATLIST(L) returns a list of all floating point
  520. numbers in list L. */
  521. for each x in l
  522. when floatp x
  523. collect x;
  524. if floatlist '(3 3.4 a nil) = '(3.4)
  525. then "Test 1 floatlist OK"
  526. else error(0, "Test 1 floatlist fails");
  527. if floatlist '(3.4 1.222 1.0e22) = '(3.4 1.222 1.0e22)
  528. then "Test 2 floatlist OK"
  529. else error(0, "Test 2 floatlist fails");
  530. if floatlist '(a b c) = NIL
  531. then "Test 3 floatlist OK"
  532. else error(0, "Test 3 floatlist fails");
  533. %------------------------- Exercise #2 -------------------------
  534. expr procedure revpairnum l;
  535. /* REVPAIRNUM(L) returns elements of L in a pair with
  536. the CAR a number starting at length of L and working
  537. backwards.*/
  538. for i:=length l step -1 until 0
  539. each x in l
  540. collect i . x;
  541. if revpairnum '(a b c) = '((3 . a) (2 . b) (1 . c))
  542. then "Test 1 revpairnum OK"
  543. else error(0, "Test 1 revpairnum fails");
  544. if revpairnum nil = nil
  545. then "Test 2 revpairnum OK"
  546. else error(0, "Test 2 revpairnum fails");
  547. if revpairnum '(a) = '((1 . a))
  548. then "Test 3 revpairnum OK"
  549. else error(0, "Test 3 revpairnum fails");
  550. %------------------------- Exercise #3 -------------------------
  551. expr procedure lflatten l;
  552. /* LFLATTEN(L) destructively flattens the list L
  553. to all levels. */
  554. if listp l then for each x in l conc lflatten x
  555. else {l};
  556. if lflatten '(a (b) c (e (e))) = '(a b c e e)
  557. then "Test 1 lflatten OK"
  558. else error(0, "Test 1 lflatten fails");
  559. if lflatten '(a b c) = '(a b c)
  560. then "Test 2 lflatten OK"
  561. else error(0, "Test 2 lflatten fails");
  562. if lflatten nil = nil
  563. then "Test 3 lflatten OK"
  564. else error(0, "Test 3 lflatten fails");
  565. if lflatten '(a (b (c (d)))) = '(a b c d)
  566. then "Test 4 lflatten OK"
  567. else error(0, "Test 4 lflatten fails");
  568. %------------------------- Exercise #4 -------------------------
  569. expr procedure realstuff l;
  570. /* REALSTUFF(L) returns the number of non-nil items in l. */
  571. for each x in l count x;
  572. if realstuff '(a b nil c) = 3
  573. then "Test 1 realstuff OK"
  574. else error(0, "Test 1 realstuff fails");
  575. if realstuff '(nil nil nil) = 0
  576. then "Test 2 realstuff OK"
  577. else error(0, "Test 2 realstuff fails");
  578. if realstuff '(a b c d) = 4
  579. then "Test 3 realstuff OK"
  580. else error(0, "Test 3 realstuff fails");
  581. %------------------------- Exercise #5 -------------------------
  582. expr procedure psentence s;
  583. /* PSENTENCE(S) prints the list of "words" S with
  584. separating blanks and a period at the end. */
  585. for each w on s
  586. do << prin2 car w;
  587. if cdr w then prin2 " " else prin2t "." >>;
  588. psentence '(The man in the field is happy);
  589. %------------------------- Exercise #6 -------------------------
  590. expr procedure bsort v;
  591. /* BSORT(V) sorts the vector V into ascending order using
  592. bubble sort. */
  593. for i:=0:sub1 upbv v
  594. returns v
  595. do for j:=add1 i:upbv v
  596. when i neq j and v[i] > v[j]
  597. with tmp
  598. do << tmp := v[j]; v[j] := v[i]; v[i] := tmp >>;
  599. xxx := [4,3,2,1, 5];
  600. if bsort xxx = [1,2,3,4,5]
  601. then "Test 1 bsort OK"
  602. else error(0, "Test 1 bsort fails");
  603. xxx := [1];
  604. if bsort xxx = [1]
  605. then "Test 2 bsort OK"
  606. else error(0, "Test 2 bsort fails");
  607. %------------------------- Exercise #7 -------------------------
  608. expr procedure bsortt v;
  609. /* BSORTT(V) sorts the vector V into ascending order using
  610. bubble sort. It verifies that all elements are numbers. */
  611. << for i:=0:upbv v
  612. when not numberp v[i]
  613. do error(0, {v[i], "is not a number for BSORTT"});
  614. for i:=0:sub1 upbv v
  615. returns v
  616. do for j:=add1 i:upbv v
  617. when i neq j and v[i] > v[j]
  618. with tmp
  619. do << tmp := v[j]; v[j] := v[i]; v[i] := tmp >> >>;
  620. xxx := [1,2,'a];
  621. if atom errorset(quote bsortt xxx, nil, nil)
  622. then "Test 1 bsortt OK"
  623. else error(0, "Test 1 bsortt fails");
  624. xxx := [1, 4, 3, 1];
  625. if car errorset(quote bsortt xxx, nil, nil) = [1,1,3,4]
  626. then "Test 2 bsortt OK"
  627. else error(0, "Test 2 bsortt fails");
  628. % ------------------------- Exercise #8 -------------------------
  629. expr procedure average l;
  630. /* AVERAGE(L) compute the average of the numbers
  631. in list L. Returns 0 if there are none. */
  632. for each x in l
  633. with sm, cnt
  634. initially sm := cnt := 0
  635. when numberp x
  636. do << sm := sm + x; cnt := cnt + 1 >>
  637. returns if cnt > 0 then sm / cnt else 0;
  638. if average '(a 12 34) = 23 then
  639. "Test 1 average OK"
  640. else error(0, "Test 1 average fails");
  641. if average '(a b c) = 0 then
  642. "Test 2 average OK"
  643. else error(0, "Test 2 average fails");
  644. if average '(a b c 5 6) = 5 then
  645. "Test 3 average OK"
  646. else error(0, "Test 3 average fails");
  647. if average '(a b c 5 6.0) = 5.5 then
  648. "Test 4 average OK"
  649. else error(0, "Test 4 average fails");
  650. %------------------------- Exercise #9 -------------------------
  651. expr procedure boundingbox L;
  652. /* BOUNDINGBOX(L) returns a list of
  653. (min X, max X, min Y, max Y)
  654. for the list L of dotted-pairs (x . y). */
  655. { for each x in L minimize car x,
  656. for each x in L maximize car x,
  657. for each y in L minimize cdr y,
  658. for each y in L maximize cdr y};
  659. if boundingbox '((0 . 1) (4 . 5)) = '(0 4 1 5)
  660. then "Test 1 boundingbox OK"
  661. else error(0, "Test 1 boundingbox fails");
  662. if boundingbox nil = '(0 0 0 0)
  663. then "Test 2 boundingbox OK"
  664. else error(0, "Test 2 boundingbox fails");
  665. if boundingbox '((-5 . 3.4) (3.3 . 2.3) (1.2 . 33)
  666. (-5 . -8) (22.11 . 3.14) (2 . 3)) = '(-5 22.11 -8 33)
  667. then "Test 3 boundingbox OK"
  668. else error(0, "Test 3 boundingbox fails");
  669. %------------------------- Exercise #10 -------------------------
  670. expr procedure maxlists(a, b);
  671. /* MAXLISTS(A, B) -- Build a list such that for each pair
  672. of elements in lists A and B the new list has the largest
  673. element. */
  674. for each ae in a
  675. each be in b
  676. collect max(ae, be);
  677. if maxlists('(3 1.2), '(44.22 0.9 1.3)) = '(44.22 1.2)
  678. then "Test 1 maxlists OK"
  679. else error(0, "Test 1 maxlists fails");
  680. if maxlists(nil, '(44.22 0.9 1.3)) = nil
  681. then "Test 2 maxlists OK"
  682. else error(0, "Test 2 maxlists fails");
  683. if maxlists('(44.22 0.9 1.3), nil) = nil
  684. then "Test 3 maxlists OK"
  685. else error(0, "Test 3 maxlists fails");
  686. if maxlists('(1.0 1.2 3.4), '(1 1)) = '(1.0 1.2)
  687. then "Test 4 maxlists OK"
  688. else error(0, "Test 4 maxlists fails");
  689. %------------------------- Exercise #11 -------------------------
  690. expr procedure numberedlist l;
  691. /* NUMBEREDLIST(L) -- returns an a-list with the CAR being
  692. elements of L and CDR, the position in the list of the
  693. element starting with 0. */
  694. for i:=0:length l
  695. each e in l
  696. collect e . i;
  697. if numberedlist nil = nil
  698. then "Test 1 numberedlist is OK"
  699. else error(0, "Test 1 numberedlist fails");
  700. if numberedlist '(a) = '((a . 0))
  701. then "Test 2 numberedlist is OK"
  702. else error(0, "Test 2 numberedlist fails");
  703. if numberedlist '(a b c) = '((a . 0) (b . 1) (c . 2))
  704. then "Test 2 numberedlist is OK"
  705. else error(0, "Test 2 numberedlist fails");
  706. %------------------------- Exercise #12 -------------------------
  707. expr procedure reduce x;
  708. /* REDUCE(X) -- X is a list of things some of which are
  709. encapsulated as (!! . y) and returns x. Destructively
  710. replace these elements with just y. */
  711. for each v on x
  712. when eqcar(car v, '!!)
  713. do car v := cdar v
  714. returns x;
  715. global '(x11);
  716. x11 := '((!! . a) (b c) (d (!! . 34)));
  717. if reduce x11 = '(a (b c) (d (!! . 34)))
  718. then "Test 1 reduce OK"
  719. else error(0, "Test 1 reduce fails");
  720. if x11 = '(a (b c) (d (!! . 34)))
  721. then "Test 2 reduce OK"
  722. else error(0, "Test 2 reduce fails");
  723. % ##### Further Procedure Tests #####
  724. %------------------------- Exercise #1 -------------------------
  725. expr procedure removeflags x;
  726. /* REMOVEFLAGS(X) -- Scan list x replacing each top level
  727. occurrence of (!! . x) with x (whatever x is) and return
  728. the list. Replacement is destructive. */
  729. while x and eqcar(car x, '!!)
  730. with v
  731. initially v := x
  732. do << print x; car x := cdar x; print x; x := cdr x >>
  733. returns v;
  734. xxx := '((!!. a) (!! . b) c (!! . d));
  735. if removeflags xxx = '(a b c (!! . d))
  736. then "Test 1 removeflags OK"
  737. else error(0, "Test 1 removeflags fails");
  738. if xxx = '(a b c (!! . d))
  739. then "Test 2 removeflags OK"
  740. else error(0, "Test 2 removeflags fails");
  741. %------------------------- Exercise #2 -------------------------
  742. expr procedure read2char c;
  743. /* READ2CHAR(C) -- Read characters to C and return the
  744. list including C. Terminates at end of file. */
  745. repeat l := (ch := readch()) . l
  746. with ch, l
  747. until ch eq c or ch eq !$EOF!$
  748. returns reversip l;
  749. if read2char '!* = {!$EOL!$, 'a, 'b, 'c, '!*}
  750. then "Test 1 read2char OK"
  751. else error(0, "Test 1 read2char fails");
  752. abc*
  753. %------------------------- Exercise #3 -------------------------
  754. expr procedure skipblanks l;
  755. /* SKIPBLANKS(L) - Returns L with leading blanks
  756. removed. */
  757. while l and eqcar(l, '! )
  758. do l := cdr l
  759. returns l;
  760. if skipblanks '(! ! ! a b) neq '(a b)
  761. then error(0, "Skipblanks fails test #1");
  762. if skipblanks nil
  763. then error(0, "Skipblanks fails test #2");
  764. if skipblanks '(! ! ! )
  765. then error(0, "Skipblanks fails test #3");
  766. if skipblanks '(! ! a b ! ) neq '(a b ! )
  767. then error(0, "Skipblanks fails test #4");
  768. %------------------------- Exercise #4 -------------------------
  769. expr procedure ntoken l;
  770. /* NTOKEN(L) - Scan over blanks in l. Then collect
  771. and return all characters up to the next blank
  772. returning a dotted-pair of (token . rest of L) or
  773. NIL if none is found. */
  774. while l and eqcar(l, '! ) do l := cdr l
  775. returns
  776. if l then
  777. while l and not eqcar(l, '! )
  778. with tok
  779. do << tok := car l . tok;
  780. l := cdr l >>
  781. returns (reversip tok . l);
  782. if ntoken '(! ! a b ! ) neq '((a b) . (! ))
  783. then error(0, "ntoken fails test #1");
  784. if ntoken nil then error(0, "ntoken fails test #2");
  785. if ntoken '(! ! ! ) then error(0, "ntoken fails test #3");
  786. if ntoken '(! ! a b) neq '((a b) . nil)
  787. then error(0, "ntoken fails test #4");
  788. % ##### Block Statement Exercises #####
  789. %------------------------- Exercise #1 -------------------------
  790. expr procedure r2nums;
  791. /* R2NUMS() -- Read 2 numbers and return as a list. */
  792. begin scalar n1;
  793. n1 := read();
  794. return {n1, read()}
  795. end;
  796. if r2nums() = '(2 3)
  797. then "Test 1 r2nums OK"
  798. else error(0, "Test 1 r2nums failed");
  799. 2 3
  800. %------------------------- Exercise #2 -------------------------
  801. expr procedure readcoordinate;
  802. /* READCOORDINATE() -- Read a coordinate and return
  803. it in radians. If prefixed with @, convert from
  804. degrees. If a list convert from degrees minutes
  805. seconds. */
  806. begin scalar x;
  807. return
  808. (if (x := read()) eq '!@ then read() / 57.2957795130823208767981
  809. else if pairp x then
  810. (car x + cadr x / 60.0 + caddr x / 3600.0)
  811. / 57.2957795130823208767981
  812. else x)
  813. end;
  814. fluid '(val);
  815. val := readcoordinate();
  816. @ 57.29577
  817. if val < 1.000001 AND val > 0.999999
  818. then "Test 1 readcoordinate OK"
  819. else error(0, "Test 1 readcoordinate failed");
  820. % This fails with poor arithmetic.
  821. val := readcoordinate();
  822. (57 17 44.772)
  823. if val < 1.000001 AND val > 0.999999
  824. then "Test 2 readcoordinate OK"
  825. else error(0, "Test 2 readcoordinate failed");
  826. unfluid '(val);
  827. if readcoordinate() = 1.0
  828. then "Test 3 readcoordinate OK"
  829. else error(0, "Test 3 readcoordinate failed");
  830. 1.0
  831. %------------------------- Exercise #3 -------------------------
  832. expr procedure delallnils l;
  833. /* DELALLNILS(L) - destructively remove all NIL's from
  834. list L. The resulting value is always EQ to L. */
  835. begin scalar p, prev;
  836. p := l;
  837. loop: if null p then return l;
  838. if null car p then
  839. if null cdr p then
  840. if null prev then return nil
  841. else << cdr prev := nil;
  842. return l >>
  843. else << car p := cadr p;
  844. cdr p := cddr p;
  845. go to loop >>;
  846. prev := p;
  847. p := cdr p;
  848. go to loop
  849. end;
  850. fluid '(xxx yyy); % New - added to aid CSL.
  851. xxx := '(a b c nil d);
  852. yyy := delallnils xxx;
  853. if yyy = '(a b c d) and yyy eq xxx
  854. then "Test 1 dellallnils OK"
  855. else error(0, "Test 1 delallnils Fails!");
  856. xxx := '(a nil b nil c nil d);
  857. yyy := delallnils xxx;
  858. if yyy = '(a b c d) and yyy eq xxx
  859. then "Test 2 dellallnils OK"
  860. else error(0, "Test 2 delallnils Fails!");
  861. xxx := '(a nil b nil c nil d nil);
  862. yyy := delallnils xxx;
  863. if yyy = '(a b c d) and yyy eq xxx
  864. then "Test 3 dellallnils OK"
  865. else error(0, "Test 3 delallnils Fails!");
  866. xxx := '(a nil nil nil nil b c d);
  867. yyy := delallnils xxx;
  868. if yyy = '(a b c d) and yyy eq xxx
  869. then "Test 4 dellallnils OK"
  870. else error(0, "Test 4 delallnils Fails!");
  871. xxx := '(nil a b c d);
  872. yyy := delallnils xxx;
  873. if yyy = '(a b c d) and yyy eq xxx
  874. then "Test 5 dellallnils OK"
  875. else error(0, "Test 5 delallnils Fails!");
  876. xxx := '(nil nil nil a b c d);
  877. yyy := delallnils xxx;
  878. if yyy = '(a b c d) and yyy eq xxx
  879. then "Test 6 dellallnils OK"
  880. else error(0, "Test 6 delallnils Fails!");
  881. xxx := '(a b c d nil nil nil);
  882. yyy := delallnils xxx;
  883. if yyy = '(a b c d) and yyy eq xxx
  884. then "Test 7 dellallnils OK"
  885. else error(0, "Test 7 delallnils Fails!");
  886. %------------------------- Exercise 4 -------------------------
  887. expr procedure dprin1 x;
  888. /* DPRIN1(X) - Print X in dotted-pair notation (to
  889. all levels). Returns X as its value. */
  890. if vectorp x then
  891. << prin2 "[";
  892. for i:=0:upbv x
  893. do << dprin1 x[i];
  894. if i < upbv x then prin2 " " >>;
  895. prin2 "]";
  896. x >>
  897. else if atom x then prin1 x
  898. else << prin2 "(";
  899. dprin1 car x;
  900. prin2 " . ";
  901. dprin1 cdr x;
  902. prin2 ")";
  903. x >>;
  904. % The test is hard to make because we're doing output.
  905. % Verify the results by hand and make sure it returns the
  906. % argument.
  907. dprin1 nil;
  908. dprin1 '(a . b);
  909. dprin1 '(a 1 "foo");
  910. dprin1 '(((a)));
  911. << x := mkvect 2; x[0] := 'a; x[1] := '(b c); x[2] := 34; >>;
  912. dprin1 {'(b c), x, 34};
  913. % ##### Property List Exercises #####
  914. %---------------------------- Exercise #1 ------------------------------
  915. global '(stack!*);
  916. expr procedure pexecute l;
  917. /* PEXECUTE(L) - L is a stack language. Constants are
  918. placed on the global stack!*, id's mean a function
  919. call to a function under the STACKFN property of the
  920. function name. Other values are placed on the stack
  921. without evaluation. */
  922. if null l then nil
  923. else if constantp car l then
  924. << stack!* := car l . stack!*;
  925. pexecute cdr l >>
  926. else if idp car l then
  927. if get(car l, 'STACKFN) then
  928. << apply(get(car l, 'STACKFN), nil);
  929. pexecute cdr l >>
  930. else error(0, {car l, "undefined function"})
  931. else << stack!* := car l . stack!*;
  932. pexecute cdr l >>;
  933. expr procedure pdiff;
  934. /* PADD1() - Subtract the 2nd stack elt from the
  935. first and replace top two entries with result. */
  936. stack!* := (cadr stack!* - car stack!*) . cddr stack!*;
  937. put('!-, 'STACKFN, 'pdiff);
  938. expr procedure pplus2;
  939. /* PPLUS2() - Pop and add the top two numbers
  940. on the stack and push the result. */
  941. stack!* := (car stack!* + cadr stack!*) . cddr stack!*;
  942. put('!+, 'STACKFN, 'pplus2);
  943. expr procedure pprint;
  944. /* PPRINT() - Print the top stack element. */
  945. print car stack!*;
  946. put('PRINT, 'STACKFN, 'pprint);
  947. pexecute '(3 4 !+);
  948. if stack!* neq '(7) then error(0, "PEXECUTE test #1 fails");
  949. stack!* := nil;
  950. pexecute '(5 3 !- 2 4 !+ !+);
  951. if stack!* neq '(8) then error(0, "PEXECUTE test #2 fails");
  952. %---------------------------- Exercise #2 ------------------------------
  953. expr procedure pexecute l;
  954. /* PEXECUTE(L) - L is a stack language. Constants are
  955. placed on the global stack!*, id's mean a function
  956. call to a function under the STACKFN property of the
  957. function name. Other values are placed on the stack
  958. without evaluation. */
  959. if null l then nil
  960. else if constantp car l then
  961. << stack!* := car l . stack!*;
  962. pexecute cdr l >>
  963. else if idp car l then
  964. if eqcar(l, 'QUOTE) then
  965. << stack!* := cadr l . stack!*;
  966. pexecute cddr l >>
  967. else if flagp(car l, 'STACKVAR) then
  968. << stack!* := get(car l, 'STACKVAL) . stack!*;
  969. pexecute cdr l >>
  970. else if get(car l, 'STACKFN) then
  971. << apply(get(car l, 'STACKFN), nil);
  972. pexecute cdr l >>
  973. else error(0, {car l, "undefined function"})
  974. else << stack!* := car l . stack!*;
  975. pexecute cdr l >>;
  976. expr procedure pset;
  977. /* PSET() - Put the second value on the stack under
  978. the STACKVAL attribute of the first. Flag the id as
  979. a STACKVAR for later use. Pop the top stack
  980. element. */
  981. << put(car stack!*, 'STACKVAL, cadr stack!*);
  982. flag({car stack!*}, 'STACKVAR);
  983. stack!* := cdr stack!* >>;
  984. put('SET, 'STACKFN, 'pset);
  985. stack!* := nil;
  986. pexecute '(4.5 quote x set 4 !+ x !+ PRINT);
  987. if stack!* neq '(13.0) then error(0, "Test 3 PEXECUTE fails");
  988. % ##### Records Exercises #####
  989. %------------------------- Exercise #1 -------------------------
  990. record qtree /* QTREE is a quad tree node element. */
  991. with
  992. node := NIL /* Node name */,
  993. q1 := NIL /* Child #1 */,
  994. q2 := NIL /* Child #2 */,
  995. q3 := NIL /* Child #3 */,
  996. q4 := NIL /* Child #4 */;
  997. expr procedure qvisit q;
  998. /* QVISIT(Q) -- Q is a QTREE data structure or NIL as are
  999. each of its children. Return a preorder visit of each
  1000. node. */
  1001. if null q then nil
  1002. else append({node q},
  1003. append(qvisit q1 q,
  1004. append(qvisit q2 q,
  1005. append(qvisit q3 q, qvisit q4 q))));
  1006. /* A simple quad tree. */
  1007. global '(qdemo);
  1008. qdemo := qtree(node := 'A,
  1009. q1 := qtree(node := 'B),
  1010. q2 := qtree(node := 'C),
  1011. q3 := qtree(node := 'D,
  1012. q1 := qtree(node := 'E)),
  1013. q4 := qtree(node := 'F));
  1014. if qvisit qdemo = '(A B C D E F)
  1015. then "Test 1 qvisit OK!"
  1016. else error(0, "Test 1 qvisit Fails!");
  1017. /* The quadtree in the book. */
  1018. global '(qdemo2);
  1019. qdemo2 := qtree(node := 'A,
  1020. q1 := qtree(node := 'B),
  1021. q2 := qtree(node := 'C),
  1022. q3 := qtree(node := 'D,
  1023. q1 := qtree(node := 'E,
  1024. q2 := qtree(node := 'F)),
  1025. q2 := qtree(node := 'G),
  1026. q3 := qtree(node := 'H),
  1027. q4 := qtree(node := 'I)));
  1028. if qvisit qdemo2 = '(A B C D E F G H I)
  1029. then "Test 2 qvisit OK!"
  1030. else error(0, "Test 2 qvisit Fails!");
  1031. if qvisit nil = NIL
  1032. then "Test 3 qvisit OK!"
  1033. else error(0, "Test 3 qvisit Fails!");
  1034. %------------------------- Exercise #2 -------------------------
  1035. expr procedure qsearch(q, val, fn);
  1036. /* QSEARCH(Q, VAL, FN) -- Returns the node path from the
  1037. root of the quadtree Q to VAL using FN as an equality
  1038. function whose first argument is from the tree and
  1039. second VAL. */
  1040. if null q then nil
  1041. else if apply(fn, {val, node q}) then {node q}
  1042. else begin scalar v;
  1043. if v := qsearch(q1 q, val, fn) then return node q . v;
  1044. if v := qsearch(q2 q, val, fn) then return node q . v;
  1045. if v := qsearch(q3 q, val, fn) then return node q . v;
  1046. if v := qsearch(q4 q, val, fn) then return node q . v
  1047. end;
  1048. if qsearch(qdemo, 'E, function EQ) = '(A D E)
  1049. then "Test 1 qsearch OK!"
  1050. else error(0, "Test 1 qsearch fails");
  1051. if qsearch(qdemo, 'XXX, function EQ) = nil
  1052. then "Test 2 qsearch OK!"
  1053. else error(0, "Test 2 qsearch fails");
  1054. if qsearch(qdemo2, 'F, function EQ) = '(A D E F)
  1055. then "Test 3 qsearch OK!"
  1056. else error(0, "Test 3 qsearch fails");
  1057. %------------------------- Exercise #3 -------------------------
  1058. record commchain
  1059. /* A COMMCHAIN is an n-ary tree with superior and
  1060. subordinate links. */
  1061. with
  1062. name := NIL /* Name of this node. */,
  1063. superior := NIL /* Pointer to superior node. */,
  1064. subordinates := NIL /* List of subordinates. */;
  1065. expr procedure backchain(l, sup);
  1066. /* BACKCHAIN(L, SUP) -- Fill in the SUPERIOR fields of
  1067. each record in the n-ary tree (links in the SUBORDINATES
  1068. field) to the lowest level. SUP is the current
  1069. superior. */
  1070. if null l then nil
  1071. else << superior l := sup;
  1072. for each sb in subordinates l
  1073. do backchain(sb, l) >>;
  1074. /* Demo the back chain. */
  1075. global '(cch);
  1076. cch :=
  1077. commchain(
  1078. name := 'TOP,
  1079. subordinates :=
  1080. {commchain(name := 'LEV1-A),
  1081. commchain(
  1082. name := 'LEV1-B,
  1083. subordinates :=
  1084. {commchain(name := 'LEV2-A),
  1085. commchain(name := 'LEV2-B)}),
  1086. commchain(name := 'LEV1-C)});
  1087. % Wrap this up to avoid printing problems.
  1088. << backchain(cch, 'COMMANDER); NIL >>;
  1089. if superior cch EQ 'COMMANDER
  1090. then "Test 1 backchain OK!"
  1091. else error(0, "Test 1 backchain Fails!");
  1092. if name superior car subordinates cch EQ 'TOP
  1093. then "Test 2 backchain OK!"
  1094. else error(0, "Test 2 backchain Fails!");
  1095. if name superior car subordinates cadr subordinates cch
  1096. eq 'LEV1-B
  1097. then "Test 3 backchain OK!"
  1098. else error(0, "Test 3 backchain Fails!");
  1099. % ##### Local Variable Exercises #####
  1100. %------------------------- Exercise #1 -------------------------
  1101. expr procedure lookup(v, a);
  1102. /* LOOKUP(V, A) -> Look for V in A and signal an error if not present.*/
  1103. (if rv then cdr rv else error(0, {v, "not in association list"}))
  1104. where rv := assoc(v, a);
  1105. if lookup('a, '((a . b) (c . d))) = 'b
  1106. then "Test 1 lookup success"
  1107. else error(0, "Test 1 lookup fails");
  1108. if errorset(quote lookup('f, '((a . b) (c . d))), nil, nil) = 0
  1109. then "Test 2 lookup success"
  1110. else error(0, "Test 2 lookup fails");
  1111. %------------------------- Exercise #2 -------------------------
  1112. expr procedure quadratic(a, b, c);
  1113. /* QUADRATIC(A, B, C) -- Returns both solutions of the
  1114. quadratic equation A*X^2 + B*X + C */
  1115. {(-B + U) / V, (-B - U) / V}
  1116. where U := SQRT(B^2 - 4*A*C),
  1117. V := 2.0 * A;
  1118. if quadratic(1.0, 2.0, 1.0) = '(-1.0 -1.0)
  1119. then "Test 1 quadratic OK!"
  1120. else error(0, "Test 1 quadratic Fails!");
  1121. if quadratic(1.0, 0.0, -1.0) = '(1.0 -1.0)
  1122. then "Test 2 quadratic OK!"
  1123. else error(0, "Test 2 quadratic Fails!");
  1124. %------------------------- Exercise #3 -------------------------
  1125. expr procedure lineintersection(x1, y1,
  1126. x2, y2,
  1127. x3, y3,
  1128. x4, y4);
  1129. /* LINEINTERSECTION(X1,Y1,X2,Y2,X3,Y3,X4,Y4) -
  1130. Computes the intersection of line X1,Y1 ->
  1131. X2,Y2 with X3,Y3 -> X4,Y4 if any. Returns NIL
  1132. if no such intersection. */
  1133. (if zerop denom or zerop d1 or zerop d2 then nil
  1134. else
  1135. ((if p1 < 0 or p1 > d1 or p2 < 0 or p2 > d2
  1136. then nil
  1137. else (x1 + (x2 - x1) * p1 / d1) .
  1138. (y1 + (y2 - y1) * p1 / d1))
  1139. where p1 := num1 / denom,
  1140. p2 := num2 / denom)
  1141. where
  1142. num1 := d1*(x1*y3 - x1*y4 - x3*y1 + x3*y4
  1143. + x4*y1 - x4*y3),
  1144. num2 := d2*(- x1*y2 + x1*y3 + x2*y1 - x2*y3
  1145. - x3*y1 + x3*y2))
  1146. where d1 :=sqrt((x2 - x1)^2 + (y2 - y1)^2),
  1147. d2 := sqrt((x4 - x3)^2 + (y4 - y3)^2),
  1148. denom := x1*y3 - x1*y4 - x2*y3 + x2*y4
  1149. - x3*y1 + x3*y2 + x4*y1 - x4*y2;
  1150. if lineintersection(1, 1, 3, 3, 1, 2, 5, 2) = '(2.0 . 2.0)
  1151. then "Test 1 LINEINTERSECTION success!"
  1152. else error(0, "Test 1 LINEINTERSECTION fails intersect test");
  1153. % intersection at start and end points.
  1154. if lineintersection(1, 1, 2, 2, 1, 1, 1, 0) = '(1.0 . 1.0)
  1155. then "Test 2 LINEINTERSECTION success!"
  1156. else error(0, "Test 2LINEINTERSECTION fails intersect at start test");
  1157. if lineintersection(1, 1, 2, 2, 0, 1, 2, 2) = '(2.0 . 2.0)
  1158. then "Test 3 LINEINTERSECTION success!"
  1159. else error(0,
  1160. "Test 3 LINEINTERSECTION fails intersect at endpoint test");
  1161. if lineintersection(1, 1, 2, 2, 2, 2, 3, 4) = '(2.0 . 2.0)
  1162. then "Test 4 LINEINTERSECTION success!"
  1163. else error(0,
  1164. "Test 4 LINEINTERSECTION fails intersect end - begin point test");
  1165. % Now try no intersection test.
  1166. if null lineintersection(1, 1, 2, 3, 2, 4, 4, 5)
  1167. then "Test 5 LINEINTERSECTION success!"
  1168. else error(0,
  1169. "Test 5 LINEINTERSECTION fails quadrant 1 no intersection");
  1170. if null lineintersection(1, 1, 2, 2, 1.75, 1.5, 5, 1.75)
  1171. then "Test 6 LINEINTERSECTION success!"
  1172. else error(0,
  1173. "Test 6 LINEINTERSECTION fails quadrant 2 no intersection");
  1174. %------------------------- Exercise #4 -------------------------
  1175. expr procedure stdev x;
  1176. /* STDEV(X) - compute the standard deviation of the
  1177. numbers in list X. */
  1178. if null x then 0
  1179. else (sqrt((for each v in x sum (v - avg)^2) / n)
  1180. where avg := (for each v in x sum v) / n)
  1181. where n := length x;
  1182. if stdev '(3.0 3.0 3.0) neq 0.0 then
  1183. error(0, "Test 1 STDEV fails");
  1184. % ##### Array Exercises #####
  1185. %------------------------- Exercise #1 -------------------------
  1186. expr procedure vaverage v;
  1187. /* VAVERAGE(V) -- compute the average of all numeric
  1188. elements of the vector v. */
  1189. (if cnt > 0 then
  1190. ((for i:=0:upbv v when numberp v[i] sum v[i]) / float cnt)
  1191. else 0.0)
  1192. where cnt := for i:=0:upbv v count numberp v[i];
  1193. if vaverage array(1,2,3) = 2.0
  1194. then "Test 1 vaverage is OK"
  1195. else error(0, "Test 1 vaverage fails");
  1196. if vaverage array(3, 'a, 3, 6.0, 'f) = 4.0
  1197. then "Test 2 vaverage is OK"
  1198. else error(0, "Test 2 vaverage fails");
  1199. if vaverage array('a, 'b) = 0.0
  1200. then "Test 3 vaverage is OK"
  1201. else error(0, "Test 3 vaverage fails");
  1202. %------------------------- Exercise #2 -------------------------
  1203. expr procedure MAPPEND(a, b);
  1204. /* MAPPEND(A, B) -- Appends array B to array A and
  1205. returns a new array with both. */
  1206. begin scalar c, ua;
  1207. c := mkvect((ua := 1 + upbv a) + upbv b);
  1208. for i:=0:upbv a do c[i] := a[i];
  1209. for i:=0:upbv b do c[i + ua] := b[i];
  1210. return c
  1211. end;
  1212. global '(a1 a2);
  1213. a1 := array(1, 2, 3);
  1214. a2 := array(3, 4, 5, 6);
  1215. if mappend(a1, a2) = array(1,2,3,3,4,5,6)
  1216. then "Test 1 MAPPEND is OK"
  1217. else error(0, "Test 1 MAPPEND fails");
  1218. if mappend(mkvect 0, mkvect 0) = mkvect 1
  1219. then "Test 2 MAPPEND is OK"
  1220. else error(0, "Test 2 MAPPEND fails");
  1221. %------------------------- Exercise #3 -------------------------
  1222. expr procedure indx(a, v);
  1223. /* INDX(A, V) -- returns index of A in V using EQ test,
  1224. otherwise NIL. */
  1225. for i:=0:upbv v
  1226. until a eq v[i]
  1227. returns if i <= upbv v then i
  1228. if indx('a, array(1, 2, 'a, 34)) = 2
  1229. then "Test 1 indx OK"
  1230. else error(0, "Test 1 indx fails");
  1231. if null indx('a, array(1, 2, 3, 4))
  1232. then "Test 2 indx OK"
  1233. else error(0, "Test 2 indx fails");
  1234. %------------------------- Exercise #4 -------------------------
  1235. expr procedure mpy4x4(a, b);
  1236. /* MPY4X4(A, B) -- Create a new 4x4 matrix and return with
  1237. the product of A and B in it. */
  1238. for row:=0:3
  1239. with c, s
  1240. initially c := mkarray(3,3)
  1241. do << for col := 0:3 do
  1242. do c[row,col] :=
  1243. for p := 0:3 sum a[row,p] * b[p,col] >>
  1244. returns c;
  1245. expr procedure translate4x4(x, y, z);
  1246. /* TRANSLATE4X4(X, Y, Z) -- Generate and return a
  1247. 4x4 matrix to translate X, Y, Z. */
  1248. array(array(1.0, 0.0, 0.0, 0.0),
  1249. array(0.0, 1.0, 0.0, 0.0),
  1250. array(0.0, 0.0, 1.0, 0.0),
  1251. array(x, y, z, 1.0));
  1252. expr procedure rotatex4x4 th;
  1253. /* ROTATEX4X4(TH) -- Generate a 4x4 rotation matrix about
  1254. the X axis, TH radians. */
  1255. array(array(1.0, 0.0, 0.0, 0.0),
  1256. array(0.0, cos th, -sin th, 0.0),
  1257. array(0.0, sin th, cos th, 0.0),
  1258. array(0.0, 0.0, 0.0, 1.0));
  1259. expr procedure mappoint(x, y, z, m);
  1260. /* MAPPOINT(X, Y, Z, M) -- Returns the transformed point
  1261. X, Y, Z by the 4x4 matrix M. */
  1262. {x*m[0,0] + y*m[1,0] + z*m[2,0] + m[3,0],
  1263. x*m[0,1] + y*m[1,1] + z*m[2,1] + m[3,1],
  1264. x*m[0,2] + y*m[1,2] + z*m[2,2] + m[3,2]};
  1265. /* tmat is test matrix to rotate about x. In our tests we
  1266. have to construct the resulting numbers on the fly
  1267. because when input, they aren't the same for EQUAL. */
  1268. global '(tmat);
  1269. tmat := rotatex4x4(45.0 / 57.29577);
  1270. if mappoint(0.0, 0.0, 0.0, tmat) = '(0.0 0.0 0.0)
  1271. then "Test 1 4x4 OK"
  1272. else error(0, "Test 1 4x4 failed");
  1273. if mappoint(1.0, 0.0, 0.0, tmat) = '(1.0 0.0 0.0)
  1274. then "Test 2 4x4 OK"
  1275. else error(0, "Test 2 4x4 failed");
  1276. if mappoint(0.0, 1.0, 0.0, tmat) =
  1277. {0.0, cos(45.0 / 57.29577), - sin(45.0 / 57.29577)}
  1278. then "Test 3 4x4 OK"
  1279. else error(0, "Test 3 4x4 failed");
  1280. if mappoint(1.0, 1.0, 0.0, tmat) =
  1281. {1.0, cos(45.0 / 57.29577), - sin(45.0 / 57.29577)}
  1282. then "Test 4 4x4 OK"
  1283. else error(0, "Test 4 4x4 failed");
  1284. if mappoint(0.0, 0.0, 1.0, tmat) =
  1285. {0.0, sin(45.0 / 57.29577), cos(45.0 / 57.29577)}
  1286. then "Test 5 4x4 OK"
  1287. else error(0, "Test 5 4x4 failed");
  1288. if mappoint(1.0, 0.0, 1.0, tmat) =
  1289. {1.0, sin(45.0 / 57.29577), cos(45.0 / 57.29577)}
  1290. then "Test 6 4x4 OK"
  1291. else error(0, "Test 6 4x4 failed");
  1292. if mappoint(0.0, 1.0, 1.0, tmat) =
  1293. {0.0, cos(45.0 / 57.29577) + sin(45.0 / 57.29577),
  1294. cos(45.0 / 57.29577) - sin(45.0 / 57.29577)}
  1295. then "Test 7 4x4 OK"
  1296. else error(0, "Test 7 4x4 failed");
  1297. if mappoint(1.0, 1.0, 1.0, tmat) =
  1298. {1.0, cos(45.0 / 57.29577) + sin(45.0 / 57.29577),
  1299. cos(45.0 / 57.29577) - sin(45.0 / 57.29577)}
  1300. then "Test 8 4x4 OK"
  1301. else error(0, "Test 8 4x4 failed");
  1302. /* Now try the multiplication routine. */
  1303. tmat := mpy4x4(rotatex4x4(45.0 / 57.29577),
  1304. translate4x4(1.0, 2.0, 3.0));
  1305. if mappoint(0.0, 0.0, 0.0, tmat) = '(1.0 2.0 3.0)
  1306. then "Test 9 4x4 OK"
  1307. else error(0, "Test 9 4x4 failed");
  1308. if mappoint(0.0, 0.0, 1.0, tmat) =
  1309. {1.0, 2.0 + sin(45.0 / 57.29577),
  1310. 3.0 + cos(45.0 / 57.29577)}
  1311. then "Test 10 4x4 OK"
  1312. else error(0, "Test 10 4x4 failed");
  1313. %------------------------- Exercise 4 -------------------------
  1314. expr procedure ltident n;
  1315. /* LTIDENT(N) -- Create and return a lower triangular,
  1316. square, identity matrix with N+1 rows. */
  1317. for i:=0:n
  1318. with a
  1319. initially a := mkvect n
  1320. do << a[i] := mkvect i;
  1321. for j:=0:i - 1 do a[i,j] := 0.0;
  1322. a[i,i] := 1.0 >>
  1323. returns a;
  1324. expr procedure ltmpy(a, b);
  1325. /* LTMPY(A, B) -- Compute the product of two square,
  1326. lower triangular matrices of the same size and return.
  1327. Note that the product is also lower triangular. */
  1328. (for i:=0:rows
  1329. with c
  1330. initially c := mkvect rows
  1331. do << c[i] := mkvect i;
  1332. for j:=0:i do
  1333. c[i,j] := for k:=j:i sum a[i,k] * b[k,j] >>
  1334. returns c)
  1335. where rows := upbv a;
  1336. if ltident 2 = array(array(1.0),
  1337. array(0.0, 1.0),
  1338. array(0.0, 0.0, 1.0))
  1339. then "Test 1 ltident OK"
  1340. else "Test 1 ltident fails";
  1341. if ltident 0 = array(array(1.0))
  1342. then "Test 2 ltident OK"
  1343. else "Test 2 ltident fails";
  1344. if ltmpy(ltident 2, ltident 2) = ltident 2
  1345. then "Test 3 ltident OK"
  1346. else "Test 3 ltident fails";
  1347. if ltmpy(array(array(1.0),
  1348. array(1.0, 2.0),
  1349. array(1.0, 2.0, 3.0)),
  1350. array(array(1.0),
  1351. array(1.0, 2.0),
  1352. array(1.0, 2.0, 3.0))) =
  1353. array(array(1.0),
  1354. array(3.0, 4.0),
  1355. array(6.0, 10.0, 9.0))
  1356. then "Test 4 ltmpy OK"
  1357. else error(0, "Test 4 ltmpy fails");
  1358. if ltmpy(array(array(1.2),
  1359. array(3.4, 5.0),
  1360. array(1.0,-2.3,-1.3)), ltident 2)
  1361. = array(array(1.2),
  1362. array(3.4, 5.0),
  1363. array(1.0, -2.3, -1.3))
  1364. then "Test 5 ltmpy OK"
  1365. else error(0, "Test 5 ltmpy fails");
  1366. %------------------------- Exercise #5 -------------------------
  1367. expr procedure coerce(a, b, pth, cmat);
  1368. /* COERCE(A,B,PTH,CMAT) -- return a list of functions
  1369. to coerce type A (an index into CMAT) into type B. PTH
  1370. is NIL to start and CMAT the coercion table arranged
  1371. with "from" type as rows, "to" type as columns. */
  1372. if cmat[a,b] then cmat[a,b] . pth
  1373. else
  1374. for j:=0:upbv cmat[a]
  1375. with cp
  1376. until j neq a and cmat[a,j] and
  1377. not (cmat[a,j] memq pth) and
  1378. not(cmat[j,a] memq pth) and
  1379. (cp := coerce(j, b, cmat[a,j] . pth, cmat))
  1380. returns cp;
  1381. /* Create the coercion array. Here int=0, string=1,
  1382. float=2, complex=3, and gaussian=4 */
  1383. global '(cpath);
  1384. cpath :=
  1385. array(array('ident, 'int2str, 'float, nil, nil),
  1386. array('str2int, 'ident, 'str2flt, nil, nil),
  1387. array('fix, 'flt2str, 'ident, 'flt2cplx,nil),
  1388. array(nil, nil, nil, 'ident, 'cfix),
  1389. array(nil, nil, nil, 'cfloat, 'ident));
  1390. % Coerce int to complex.
  1391. if coerce(0, 3, nil, cpath) = '(FLT2CPLX STR2FLT INT2STR)
  1392. then "Test 1 coerce OK"
  1393. else error(0, "Test 1 coerce fails");
  1394. % Coerce Complex into int.
  1395. if coerce(3, 0, nil, cpath) = NIL
  1396. then "Test 2 coerce OK"
  1397. else error(0, "Test 2 coerce fails");
  1398. % Coerce int into gaussian.
  1399. if coerce(0, 4, nil, cpath) =
  1400. '(CFIX FLT2CPLX STR2FLT INT2STR)
  1401. then "Test 3 coerce OK"
  1402. else error(0, "Test 3 coerce fails");
  1403. %------------------------- Exercise #6 -------------------------
  1404. expr procedure cellvon(a, b, fn);
  1405. /* CELLVON(A, B, FN) -- Compute the next generation of the
  1406. cellular matrix A and place it into B. Use the VonNeumann
  1407. neighborhood and the function FN to compute the next
  1408. generation. The space edges are wrapped into a torus*/
  1409. for r:=0:rows
  1410. with rows, cols
  1411. initially << rows := upbv a; cols := upbv a[1] >>
  1412. do for c:=0:cols
  1413. do b[r,c] := apply(fn,
  1414. {a[r,c],
  1415. a[torus(r + 1, rows), torus(c - 1, cols)],
  1416. a[torus(r + 1, rows), c],
  1417. a[torus(r + 1, rows), torus(c + 1, cols)],
  1418. a[r, torus(c + 1, cols)],
  1419. a[torus(r - 1, rows), torus(c + 1, cols)],
  1420. a[torus(r - 1, rows), c],
  1421. a[torus(r - 1, rows), torus(c - 1, cols)],
  1422. a[r, torus(c - 1, cols)]});
  1423. expr procedure torus(i, v);
  1424. /* TORUS(I, V) -- A positive modulus: if I is less than
  1425. 0, wrap to V, or if it exceeds V, wrap to I. */
  1426. if i < 0 then v
  1427. else if i > v then 0
  1428. else i;
  1429. expr procedure life(c, n1, n2, n3, n4, n5, n6, n7, n8);
  1430. /* LIFE(C, N1 ... N8) -- Game of life rules. Here C is
  1431. the cell being examined and N1-N8 are the VonNeumann
  1432. neighbor states. */
  1433. (if c = 1 then if cnt = 2 or cnt = 3 then 1 else 0
  1434. else if cnt = 3 then 1 else 0)
  1435. where cnt = n1 + n2 + n3 + n4 + n5 + n6 + n7 + n8;
  1436. /* LIFESTATES contains a vector of states and what
  1437. character to print. */
  1438. global '(LIFESTATES);
  1439. LIFESTATES := array(" ", "*");
  1440. expr procedure pcell(gen, a, pr);
  1441. /* PCELL(GEN, A) -- Display the state of the GEN generation
  1442. of the cellular matrix A. Display a * for state=1, and
  1443. a blank for state 0. */
  1444. for r:=0:rows
  1445. with rows, cols
  1446. initially << rows := upbv a; cols := upbv a[1];
  1447. terpri(); prin2 "Generation: "; print gen >>
  1448. do << terpri();
  1449. for c:=0:cols do prin2 pr[a[r,c]] >>;
  1450. expr procedure rungame(a, n, fn, pr);
  1451. /* RUNGAME(A, N, FN, PR) -- Run through N generations
  1452. starting with the cellular matrix A and using the
  1453. function FNto compute the new generation. Use the array
  1454. PR to display the state. */
  1455. for i:=1:n
  1456. with tmp, b
  1457. initially b := mkarray(upbv a, upbv a[1])
  1458. do << pcell(i, a, pr);
  1459. cellvon(a, b, function life);
  1460. tmp := a; a := b; b := tmp >>;
  1461. /* SEED is the seed array with 1's for on state, 0 for
  1462. off. */
  1463. global '(seed);
  1464. seed := array(
  1465. array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  1466. array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  1467. array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  1468. array(0, 0, 0, 0, 0, 1, 0, 0, 0, 0),
  1469. array(0, 0, 0, 0, 0, 0, 1, 0, 0, 0),
  1470. array(0, 0, 0, 0, 1, 1, 1, 0, 0, 0),
  1471. array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  1472. array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  1473. array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  1474. array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
  1475. rungame(seed, 10, function life, LIFESTATES);
  1476. %------------------------- Exercise #7 -------------------------
  1477. expr procedure compact heep;
  1478. /* compact(HEEP) -- HEEP is an array of blocks of
  1479. sequentially allocated items. The first entry in each
  1480. block is INUSE, the second the total number of entries
  1481. + 2 (for the header). The remainder are random values.
  1482. Free blocks are the same but instead have the header
  1483. FREE. Returns a compacted structure with a single FREE
  1484. entry at the end with entries changed to *. Returns the
  1485. number of free entries. */
  1486. begin scalar dest, src, last, u;
  1487. last := dest := src := 0;
  1488. loop: if src > upbv heep then
  1489. if src = dest then return 0
  1490. else << heep[dest] := 'FREE;
  1491. heep[dest+1] := src - dest;
  1492. for i:=dest+2:upbv heep do heep[i] := '!*;
  1493. return heep[dest+1] >>;
  1494. if heep[src] eq 'FREE then
  1495. src := heep[src+1] + src
  1496. else << u := heep[src+1] + src - 1;
  1497. for i:=src:u do << heep[dest] := heep[i];
  1498. dest := dest + 1 >>;
  1499. src := u + 1 >>;
  1500. go to loop
  1501. end;
  1502. /* A simple array to test. */
  1503. global '(H);
  1504. H := array('INUSE, 3, 0,
  1505. 'FREE, 4, '!*, '!*,
  1506. 'INUSE, 4, 0, 1,
  1507. 'FREE, 3, '!*,
  1508. 'FREE, 5, '!*, '!*, '!*,
  1509. 'INUSE, 5, 0, 1, 2,
  1510. 'INUSE, 5, 3, 4, 5);
  1511. if compact H = 12
  1512. then "Test 1 compact OK!"
  1513. else error(0, "Test 1 compact fails!");
  1514. if H = array('INUSE, 3, 0, 'INUSE, 4, 0, 1, 'INUSE,
  1515. 5, 0, 1, 2, 'INUSE, 5, 3, 4, 5,
  1516. 'FREE, 12, '!*, '!*, '!*, '!*, '!*, '!*,
  1517. '!*, '!*, '!*, '!*)
  1518. then "Test 2 compact OK!"
  1519. else error(0, "Test 2 compact fails!");
  1520. /* Test a completely full one. */
  1521. H := array('INUSE, 3, 0, 'INUSE, 5, 1, 2, 3);
  1522. if compact H = 0
  1523. then "Test 3 compact OK!"
  1524. else error(0, "Test 3 compact fails!");
  1525. if H = array('INUSE, 3, 0, 'INUSE, 5, 1, 2, 3)
  1526. then "Test 4 compact OK!"
  1527. else error(0, "Test 4 compact fails!");
  1528. /* Test a completely empty one. */
  1529. H := array('FREE, 3, '!*, 'FREE, 5, '!*, '!*, '!*);
  1530. if compact H = 8
  1531. then "Test 5 compact OK!"
  1532. else error(0, "Test 5 compact fails!");
  1533. if H = array('FREE, 8, '!*, '!*, '!*, '!*, '!*, '!*)
  1534. then "Test 6 compact OK!"
  1535. else error(0, "Test 6 compact fails!");
  1536. %------------------------- Exercise #8 -------------------------
  1537. expr procedure HISTOGRAM(v, n);
  1538. /* HISTOGRAM(V,N) -- V is an arbitrarily size vector of
  1539. numbers. Compute its an N element histogram over its
  1540. range and return it. */
  1541. begin scalar minv, maxv, h, range;
  1542. minv := maxv := v[0];
  1543. for i:=1:upbv v
  1544. do << if v[i] < minv then minv := v[i];
  1545. if v[i] > maxv then maxv := v[i] >>;
  1546. range := maxv - minv;
  1547. h := mkvect(n - 1);
  1548. for i:=0:n - 1 do h[i] := 0;
  1549. for i:=0:upbv v
  1550. with hn
  1551. do << hn := fix(n * (v[i] - minv) / range);
  1552. if hn = n then hn := hn - 1;
  1553. h[hn] := h[hn] + 1 >>;
  1554. return h
  1555. end;
  1556. global '(v1);
  1557. << v1 := mkvect 100;
  1558. for i:=0:100 do v1[i] := float i >>;
  1559. if HISTOGRAM(v1, 1) = array(101)
  1560. then "Test 1 HISTOGRAM OK!"
  1561. else error(0, "Test 1 HISTOGRAM Fails!");
  1562. if HISTOGRAM(v1, 2) = array(50, 51)
  1563. then "Test 2 HISTOGRAM OK!"
  1564. else error(0, "Test 2 HISTOGRAM Fails!");
  1565. if HISTOGRAM(v1, 7) = array(15, 14, 14, 15, 14, 14, 15)
  1566. then "Test 3 HISTOGRAM OK!"
  1567. else error(0, "Test 3 HISTOGRAM Fails!");
  1568. %------------------------- Exercise #9 -------------------------
  1569. expr procedure rarray n;
  1570. /* RARRAY(N) - generate an NxN matrix with uniform
  1571. distribution random numbers in the range 0.0 -> 1.0. */
  1572. for x:=0:n
  1573. with a
  1574. initially a := mkarray(n,n)
  1575. returns a
  1576. do for y:=0:n do a[x,y] := random(1000) / 1000.0;
  1577. if upbv rarray 4 = 4
  1578. then "Test 1 rarray OK"
  1579. else error(0, "Test 1 rarray fails");
  1580. expr procedure addcircle(a, r, xc, yc, v);
  1581. /* ADDCIRCLE(A, R, XC, YC, V) -- Add V to each cell within
  1582. distance R from center point XC, YC and return a new
  1583. matrix with these values. Values always remain in the
  1584. range 0.0 -> 1.0. */
  1585. begin scalar uax, uay, b;
  1586. b := mkarray(uax := upbv a, uay := upbv a[0]);
  1587. for x:=0:uax do
  1588. for y:=0:uay do
  1589. b[x,y] := if sqrt((x - xc)^2 + (y - yc)^2) <= r
  1590. then min(1.0, v + a[x,y]) else a[x,y];
  1591. return b
  1592. end;
  1593. global '(xxx);
  1594. xxx := array(array(0, 0, 0, 0, 0),
  1595. array(0, 0, 0, 0, 0),
  1596. array(0, 0, 0, 0, 0),
  1597. array(0, 0, 0, 0, 0),
  1598. array(0, 0, 0, 0, 0));
  1599. % This will fail if sqrt isn't very accurate.
  1600. if addcircle(xxx, 2.0, 2, 2, 0.75) =
  1601. array(array(0, 0, 0.75, 0, 0),
  1602. array(0, 0.75, 0.75, 0.75, 0),
  1603. array(0.75, 0.75, 0.75, 0.75, 0.75),
  1604. array(0, 0.75, 0.75, 0.75, 0),
  1605. array(0, 0, 0.75, 0, 0))
  1606. then "Test 1 addcircle OK!"
  1607. else error(0, "Test 1 addcircle fails!");
  1608. if addcircle(xxx, 10.0, 2, 2, 0.75) =
  1609. array(array(0.75, 0.75, 0.75, 0.75, 0.75),
  1610. array(0.75, 0.75, 0.75, 0.75, 0.75),
  1611. array(0.75, 0.75, 0.75, 0.75, 0.75),
  1612. array(0.75, 0.75, 0.75, 0.75, 0.75),
  1613. array(0.75, 0.75, 0.75, 0.75, 0.75))
  1614. then "Test 2 addcircle OK!"
  1615. else error(0, "Test 2 addcircle fails!");
  1616. %------------------------- Exercise #10 -------------------------
  1617. expr procedure areaaverage(a, n);
  1618. /* AREAAVERAGE(A, N) -- Compute the average of the NxN
  1619. neighborhood of each cell in the matrix A and return a
  1620. new matrix with these values. */
  1621. begin scalar uax, uay, sm, cnt, b, n2;
  1622. n2 := n / 2;
  1623. b := mkarray(uax := upbv a, uay := upbv a[1]);
  1624. for x := 0:uax do
  1625. for y := 0:uay do
  1626. << sm := 0.0;
  1627. cnt := 0;
  1628. for xp := max(0, x - n2):min(uax, x + n2) do
  1629. for yp := max(0, y - n2):min(uay, y + n2) do
  1630. << sm := sm + a[xp,yp];
  1631. cnt := cnt + 1 >>;
  1632. b[x,y] := sm / cnt >>;
  1633. return b
  1634. end;
  1635. global '(ninth);
  1636. xxx[2,2] := 1.0;
  1637. ninth := 1.0 / 9.0;
  1638. if areaaverage(xxx, 3) =
  1639. array(array(0.0, 0.0, 0.0, 0.0, 0.0),
  1640. array(0.0, ninth, ninth, ninth, 0.0),
  1641. array(0.0, ninth, ninth, ninth, 0.0),
  1642. array(0.0, ninth, ninth, ninth, 0.0),
  1643. array(0.0, 0.0, 0.0, 0.0, 0.0))
  1644. then "Test 1 areaaverage OK!"
  1645. else error(0, "Test 1 areaaverage Fails!");
  1646. %------------------------- Exercise #11 -------------------------
  1647. expr procedure laplace a;
  1648. /* LAPLACE(A) -- Compute the Laplacian on A but assuming
  1649. 0.0 at the borders. Returns a new array the same size
  1650. as A. */
  1651. begin scalar uax, uay, b, sm;
  1652. b := mkarray(uax := upbv a, uay := upbv a[0]);
  1653. for x := 0:uax do
  1654. for y := 0:uay do
  1655. << sm := 0.0;
  1656. for xp := max(0, x - 1):min(uax, x + 1)
  1657. when xp neq x do
  1658. for yp := max(0, y - 1):min(uay, y + 1)
  1659. when yp neq y
  1660. do sm := sm + a[xp,yp];
  1661. b[x,y] := max(0.0, min(5.0 * a[x,y] - sm, 1.0)) >>;
  1662. return b
  1663. end;
  1664. xxx := array(array(0,0,0,0,0),
  1665. array(0,1,1,1,0),
  1666. array(0,1,1,1,0),
  1667. array(0,1,1,1,0),
  1668. array(0,0,0,0,0));
  1669. if laplace xxx = array(array(0.0, 0.0, 0.0, 0.0, 0.0),
  1670. array(0.0, 1.0, 1.0, 1.0, 0.0),
  1671. array(0.0, 1.0, 1.0, 1.0, 0.0),
  1672. array(0.0, 1.0, 1.0, 1.0, 0.0),
  1673. array(0.0, 0.0, 0.0, 0.0, 0.0))
  1674. then "Test 1 laplace OK!"
  1675. else error(0, "Test 1 laplace fails!");
  1676. %------------------------- Exercise #12 -------------------------
  1677. expr procedure threshold(a, vl, vh);
  1678. /* THRESHOLD(A, VL, VH) -- Returns a new matrix of the same
  1679. size as A with each cell set to 1.0 that is
  1680. VL <= A(i,j) <= VH. Others are set to 0.0. */
  1681. for x := 0:uax
  1682. with uax, uay, b
  1683. initially b := mkarray(uax := upbv a,
  1684. uay := upbv a[0])
  1685. returns b
  1686. do for y := 0:uay
  1687. do b[x,y] :=
  1688. if a[x,y] >= vl and a[x,y] <= vh then 1.0
  1689. else 0.0;
  1690. xxx := mkarray(4,4);
  1691. for i:=0:4 do for j:=0:4 do xxx[i,j] := i * j;
  1692. if threshold(xxx, 8, 10) = array(
  1693. array(0.0, 0.0, 0.0, 0.0, 0.0),
  1694. array(0.0, 0.0, 0.0, 0.0, 0.0),
  1695. array(0.0, 0.0, 0.0, 0.0, 1.0),
  1696. array(0.0, 0.0, 0.0, 1.0, 0.0),
  1697. array(0.0, 0.0, 1.0, 0.0, 0.0))
  1698. then "Test 1 threshold OK!"
  1699. else error(0, "Test 1 threshold Fails!");
  1700. expr procedure dump(a, f);
  1701. /* DUMP(A,F) -- Dump an array A into a PicTex format
  1702. file for document processing. */
  1703. begin scalar fh;
  1704. fh := wrs open(f, 'output);
  1705. for x:=0:upbv a do
  1706. for y:=0:upbv a[0] do
  1707. printf("\setshadegrid span <%wpt>%n\vshade %d %d %d %d %d %d /%n",
  1708. max(0.5, 5.5 - a[x,y]*5.0),
  1709. x, y, y+1, x+1, y, y+1);
  1710. close wrs fh;
  1711. end;
  1712. % ##### Macro Exercises #####
  1713. %------------------------- Exercise -----------------------
  1714. macro procedure appendl x;
  1715. /* APPENDL( ...) - append all the lists together. */
  1716. expand(cdr x, 'append);
  1717. if appendl('(a b), '(c d), '(e f)) = '(a b c d e f)
  1718. then "Test 1 appendl OK!"
  1719. else error(0, "Test 1 appendl fails!");
  1720. if appendl '(a b c) = '(a b c)
  1721. then "Test 2 appendl OK!"
  1722. else error(0, "Test 2 appendl fails!");
  1723. if appendl nil = nil
  1724. then "Test 3 appendl OK!"
  1725. else error(0, "Test 3 appendl fails!");
  1726. %------------------------- Exercise ------------------------
  1727. macro procedure nconcl x;
  1728. /* NCONCL(...) - destructive concatenation of all the
  1729. lists. */
  1730. expand(cdr x, 'nconc);
  1731. global '(b1 b2 b3);
  1732. b1 := '(a b);
  1733. b2 := '(c d);
  1734. b3 := '(e f);
  1735. if nconcl(b1, b2, b3) = '(a b c d e f)
  1736. then "Test 1 nconcl OK!"
  1737. else error(0, "Test 1 nconcl fails!");
  1738. if b1 = '(a b c d e f)
  1739. then "Test 2 nconcl OK!"
  1740. else error(0, "Test 2 nconcl fails!");
  1741. if b2 = '(c d e f)
  1742. then "Test 3 nconcl OK!"
  1743. else error(0, "Test 3 nconcl fails!");
  1744. if b3 = '(e f)
  1745. then "Test 4 nconcl OK!"
  1746. else error(0, "Test 4 nconcl fails!");
  1747. %------------------------- Exercise ------------------------
  1748. smacro procedure d(x1, y1, x2, y2);
  1749. /* D(X1, Y1, X2, Y2) - Euclidean distance between points
  1750. (X1,Y1) -> (X2,Y2) */
  1751. sqrt((x1 - x2)^2 + (y1 - y2)^2);
  1752. % This fails with poor sqrt.
  1753. if d(0, 0, 3, 4) = 5.0
  1754. then "Test 1 d OK!"
  1755. else error(0, "Test 1 d Fails!");
  1756. if d(0, 0, 1, 1) = sqrt 2
  1757. then "Test 2 d OK!"
  1758. else error(0, "Test 2 d Fails!");
  1759. %------------------------- Exercise -------------------------
  1760. macro procedure pop x;
  1761. /* POP(X) - Assuming X is an identifier, pop the stack
  1762. and return the popped value. */
  1763. (`(prog (!$V!$)
  1764. (setq !$V!$ (car #v))
  1765. (setq #v (cdr #v))
  1766. (return !$V!$))) where v := cadr x;
  1767. xxx := '(A B);
  1768. if pop xxx eq 'A
  1769. then "Test 1 POP ok!"
  1770. else error(0, "Test 1 POP fails!");
  1771. if xxx = '(B)
  1772. then "Test 1 POP ok!"
  1773. else error(0, "Test 1 POP fails!");
  1774. if pop xxx eq 'B
  1775. then "Test 2 POP ok!"
  1776. else error(0, "Test 2 POP fails!");
  1777. if xxx eq NIL
  1778. then "Test 2 POP ok!"
  1779. else error(0, "Test 2 POP fails!");
  1780. %------------------------- Exercise -------------------------
  1781. macro procedure push x;
  1782. /* PUSH(ST, V) - push V onto ST (an identifier) and
  1783. return V. */
  1784. `(progn (setq #st (cons #v #st))
  1785. #v)
  1786. where st := cadr x,
  1787. v := caddr x;
  1788. if push(xxx, 'A) = 'A
  1789. then "Test 1 push OK!"
  1790. else error(0, "Test 1 push fails");
  1791. if xxx = '(A)
  1792. then "Test 1 push OK!"
  1793. else error(0, "Test 1 push fails");
  1794. if push(xxx, 'B) = 'B
  1795. then "Test 2 push OK!"
  1796. else error(0, "Test 2 push fails");
  1797. if xxx = '(B A)
  1798. then "Test 2 push OK!"
  1799. else error(0, "Test 2 push fails");
  1800. %------------------------- Exercise -------------------------
  1801. macro procedure format x;
  1802. /* FORMAT("str", ...) - A formatted print utility. It
  1803. looks for %x things in str, printing everything else.
  1804. A property of printf!-format will cause a call on
  1805. the named function with the corresponding argument.
  1806. This should return a print form to use. A property
  1807. printf!-expand calls a function without an argument.
  1808. Common controls are:
  1809. %n new line
  1810. %p prin2 call.
  1811. %w prin1 call.
  1812. */
  1813. begin scalar str, localstr, m;
  1814. str := explode2 cadr x;
  1815. x := cddr x;
  1816. loop: if null str then
  1817. << if localstr then
  1818. m := {'prin2, makestring reversip localstr} . m;
  1819. return 'progn . reverse m >>;
  1820. if eqcar(str, '!%) then
  1821. if cdr str then
  1822. if fn := get(cadr str, 'printf!-format) then
  1823. << if localstr then
  1824. << m := {'prin2, makestring reversip localstr} . m;
  1825. localstr := nil >>;
  1826. m := apply(fn, {car x}) . m;
  1827. x := cdr x;
  1828. str := cddr str;
  1829. go to loop >>
  1830. else if fn := get(cadr str, 'printf!-expand) then
  1831. << if localstr then
  1832. << m := {'prin2, makestring reverse localstr} . m;
  1833. localstr := nil >>;
  1834. m := apply(fn, nil) . m;
  1835. str := cddr str;
  1836. go to loop >>;
  1837. localstr := car str . localstr;
  1838. str := cdr str;
  1839. go to loop
  1840. end;
  1841. expr procedure makestring l;
  1842. /* MAKESTRING(L) - convert the list of character L into
  1843. a string. */
  1844. compress('!" . append(l, '(!")));
  1845. expr procedure printf!-terpri;
  1846. /* PRINTF!-TERPRI() - Generates a TERPRI call for %n */
  1847. '(terpri);
  1848. put('!n, 'printf!-expand, 'printf!-terpri);
  1849. put('!N, 'printf!-expand, 'printf!-terpri);
  1850. expr procedure printf!-prin1 x;
  1851. /* PRINTF!-PRIN1(X) - Generates a PRIN1 call for %w */
  1852. {'prin1, x};
  1853. put('!w, 'printf!-format, 'printf!-prin1);
  1854. put('!W, 'printf!-format, 'printf!-prin1);
  1855. expr procedure printf!-prin2 x;
  1856. /* PRINTF!-PRIN2(X) - Generates a PRIN2 call for %p */
  1857. {'prin2, x};
  1858. put('!p, 'printf!-format, 'printf!-prin2);
  1859. put('!P, 'printf!-format, 'printf!-prin2);
  1860. %------------------------- Exercise -------------------------
  1861. macro procedure rmsg x;
  1862. /* RMSG("str", ...) - A formatted string utility. It
  1863. looks for %x things in str, copying everything else.
  1864. A property of rmsg!-format will cause a call on
  1865. the named function with the corresponding argument.
  1866. This should return a explode form to use. A property
  1867. rmsg!-expand calls a function without an argument.
  1868. Common controls are:
  1869. %n new line
  1870. %p explode2 call.
  1871. %w explode call.
  1872. */
  1873. begin scalar str, localstr, m;
  1874. str := explode2 cadr x;
  1875. x := cddr x;
  1876. loop: if null str then
  1877. << if localstr then
  1878. m := mkquote reversip localstr . m;
  1879. return `(makestring (nconcl #@(reversip m))) >>;
  1880. if eqcar(str, '!%) then
  1881. if cdr str then
  1882. if fn := get(cadr str, 'rmsg!-format) then
  1883. << if localstr then
  1884. << m := mkquote reversip localstr . m;
  1885. localstr := nil >>;
  1886. m := apply(fn, {car x}) . m;
  1887. x := cdr x;
  1888. str := cddr str;
  1889. go to loop >>
  1890. else if fn := get(cadr str, 'rmsg!-expand) then
  1891. << if localstr then
  1892. << m := mkquote reversip localstr . m;
  1893. localstr := nil >>;
  1894. m := apply(fn, nil) . m;
  1895. str := cddr str;
  1896. go to loop >>;
  1897. localstr := car str . localstr;
  1898. str := cdr str;
  1899. go to loop
  1900. end;
  1901. expr procedure makestring l;
  1902. /* MAKESTRING(L) - convert the list of character L into
  1903. a string. */
  1904. compress('!" . append(l, '(!")));
  1905. expr procedure rmsg!-terpri;
  1906. /* RMSG!-TERPRI() - Generates an EOL. */
  1907. mkquote {!$eol!$};
  1908. put('!n, 'rmsg!-expand, 'rmsg!-terpri);
  1909. put('!N, 'rmsg!-expand, 'rmsg!-terpri);
  1910. expr procedure rmsg!-prin1 x;
  1911. /* RMSG!-PRIN1(X) - Generates an EXPLODE call */
  1912. `(fixstr (explode #x));
  1913. put('!w, 'rmsg!-format, 'rmsg!-prin1);
  1914. put('!W, 'rmsg!-format, 'rmsg!-prin1);
  1915. expr procedure rmsg!-prin2 x;
  1916. /* RMSG!-PRIN2(X) - Generates an EXPLODE2 call for x. */
  1917. `(explode2 #x);
  1918. put('!p, 'rmsg!-format, 'rmsg!-prin2);
  1919. put('!P, 'rmsg!-format, 'rmsg!-prin2);
  1920. expr procedure fixstr x;
  1921. /* FIXSTR(X) - Double up "'s in x. */
  1922. if null x then nil
  1923. else if eqcar(x, '!") then '!" . '!" . fixstr cdr x
  1924. else car x . fixstr cdr x;
  1925. if rmsg "abc" = "abc"
  1926. then "Test 1 rmsg OK!"
  1927. else error(0, "Test 1 rmsg fails!");
  1928. if rmsg("Test %w test", 12) = "Test 12 test"
  1929. then "Test 2 rmsg OK!"
  1930. else error(0, "Test 2 rmsg fails!");
  1931. if rmsg("Test %w string", "foo") = "Test ""foo"" string"
  1932. then "Test 3 rmsg OK!"
  1933. else error(0, "Test 3 rmsg fails!");
  1934. if rmsg("Test %w now %p", "foo", "foo") = "Test ""foo"" now foo"
  1935. then "Test 4 rmsg OK!"
  1936. else error(0, "Test 4 rmsg fails!");
  1937. %------------------------- Exercise -------------------------
  1938. define CFLAG = T;
  1939. macro procedure ifcflag x;
  1940. /* IFCLFAG(X) - generate the code for X if CFLAG is non-NIL,
  1941. otherwise generate NIL (this can't be used everywhere). */
  1942. if CFLAG then cadr x else nil;
  1943. ifCFLAG expr procedure pslfoo x; car x;
  1944. if getd 'pslfoo
  1945. then "Test 1 ifCFLAG OK!"
  1946. else error(0, "Test 1 ifCFLAG fails!");
  1947. % ##### Interactive Exercises #####
  1948. %------------------------- Exercise #2 -------------------------
  1949. /* Lists functions that have been embedded with count code. */
  1950. global '(EMBEDDED!*);
  1951. EMBEDDED!* := NIL;
  1952. expr procedure embed f;
  1953. /* EMBED(F) - wrap function F with counter code. Error if F is
  1954. not interpreted. Put the information under property COUNT and
  1955. add to the global list EMBEDDED!*. */
  1956. begin scalar def, args, nfn;
  1957. if not(def := getd f) then error(0, {f, "is undefined"});
  1958. if codep cdr def then error(0, {f, "is not interpreted"});
  1959. put(f, 'COUNT, 0);
  1960. if f memq EMBEDDED!* then return NIL;
  1961. EMBEDDED!* := f . EMBEDDED!*;
  1962. putd(nfn := intern gensym(), car def, cdr def);
  1963. putd(f, car def,
  1964. {'lambda, caddr def,
  1965. {'progn,
  1966. {'put, mkquote f, mkquote 'COUNT,
  1967. {'add1, {'get, mkquote f, mkquote 'COUNT}}},
  1968. nfn . caddr def}});
  1969. return f
  1970. end;
  1971. expr procedure stats;
  1972. /* STATS() - list all the embedded functions and their
  1973. counts. */
  1974. for each f in EMBEDDED!*
  1975. do << prin1 f; prin2 " "; print get(f, 'COUNT) >>;
  1976. expr procedure pcnt x;
  1977. /* PCNT(X) - returns the number of dotted-pairs in X (vectors
  1978. can hide dotted-pairs). */
  1979. if atom x then 0
  1980. else 1 + pcnt car x + pcnt cdr x;
  1981. if embed 'pcnt eq 'pcnt
  1982. then "Test 1 embed OK!"
  1983. else error(0, "Test 1 embed Fails!");
  1984. if get('pcnt, 'count) = 0
  1985. then "Test 2 embed OK!"
  1986. else error(0, "Test 2 embed Fails!");
  1987. if pcnt '(a . (b . c)) = 2
  1988. then "Test 3 embed OK!"
  1989. else error(0, "Test 3 embed Fails!");
  1990. if get('pcnt, 'COUNT) = 5
  1991. then "Test 4 embed OK!"
  1992. else error(0, "Test 4 embed Fails!");
  1993. if EMBEDDED!* = '(PCNT)
  1994. then "Test 5 embed OK!"
  1995. else error(0, "Test 5 embed Fails!");
  1996. % Just a visual check.
  1997. stats();
  1998. % ##### Test the inspector module #####
  1999. %
  2000. % We set LINELENGTH to various values to check how good we do on output.
  2001. % Don't let the default screw up the test:
  2002. LINELENGTH 80;
  2003. % Describe some of the basic data types.
  2004. % Dotted-pairs.
  2005. describe '(a . b);
  2006. % Vectors;
  2007. global '(xvar);
  2008. xvar := mkvect 3;
  2009. describe xvar;
  2010. % Records.
  2011. record insprec /* A record for testing. */
  2012. with
  2013. field1 := 'a;
  2014. xvar := insprec();
  2015. describe xvar;
  2016. describe 'insprec;
  2017. % A code pointer (usually).
  2018. describe cdr getd 'car;
  2019. % Numbers.
  2020. describe 1;
  2021. describe 3.14159;
  2022. % Strings
  2023. describe "This is a string";
  2024. % identifiers of various sourts.
  2025. describe 'car;
  2026. describe 'a!-plain!-jane!-identifier;
  2027. describe nil; % This message is sort of funny in odd ways.
  2028. % Now let's get serious. Here's a global with no active comment. The
  2029. % remprop is something you shouldn't know about but allows us to run
  2030. % the test file multiple times and get the same results.
  2031. remprop('TheCow, 'NEWNAM);
  2032. DEFINE TheCow = "How now brown cow";
  2033. describe 'TheCow;
  2034. off saveactives;
  2035. /* I never saw a purple cow, I never hope to see one now. */
  2036. global '(PurpleCow);
  2037. describe 'PurpleCow;
  2038. on saveactives;
  2039. /* But I'd rather see one than be one! */
  2040. global '(Pcow);
  2041. describe 'Pcow;
  2042. % Now we march on to procedures.
  2043. % Here's one with no comment and we don't save it.
  2044. off saveactives;
  2045. remd 'comtest1;
  2046. expr procedure comtest1 x;
  2047. print x;
  2048. describe 'comtest1;
  2049. % Here's one with no comment and we do save it.
  2050. on saveactives;
  2051. remd 'comtest2;
  2052. expr procedure comtest2(x, y);
  2053. print x;
  2054. describe 'comtest2;
  2055. % Here's one with a comment but we don't save it.
  2056. off saveactives;
  2057. remd 'comtest3;
  2058. expr procedure comtest3(x, y, z);
  2059. /* You should never see this comment. */
  2060. print x;
  2061. describe 'comtest3;
  2062. % Here's one with a comment and we should see it.
  2063. on saveactives;
  2064. remd 'comtest4;
  2065. expr procedure comtest4(x, y, z, xx);
  2066. /* COMTEST4(X, Y, Z, XX) - A well commented routine. This routine
  2067. does almost nothing, but a good article thereof. */
  2068. print x;
  2069. describe 'comtest4;
  2070. % Now try MACROS.
  2071. remd 'comtest5;
  2072. macro procedure comtest5 x;
  2073. /* COMTEST5(X) - A macro that doesn't really do much of anything. */
  2074. {'car, cadr x};
  2075. describe 'comtest5;
  2076. smacro procedure comtest6 x;
  2077. /* COMTEST6(X) - a SMACRO with an active comment. This smacro expands
  2078. to take CAR of its argument. */
  2079. car x;
  2080. describe 'comtest6;
  2081. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2082. % Module testing.
  2083. /* This is a test module which occurs at the top level just to make
  2084. sure that the module type works. */
  2085. module testmodule;
  2086. endmodule;
  2087. describe 'testmodule;
  2088. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2089. % Format testing. Put a big comment out there and look at it with
  2090. % various line lengths.
  2091. /* ********************
  2092. This is a test comment. We'll try do different things with it in
  2093. different contexts. Does it work?
  2094. expr procedure fact n;
  2095. if n < 2 then 1 else n * fact(n - 1);
  2096. Well hoop de doo! Is there anything else funny?
  2097. +------------+----------+
  2098. | Column 1 | Col. 2 |
  2099. +------------+----------+
  2100. | Aardvarks | 345 |
  2101. +------------+----------+
  2102. | Zarfs | 3 |
  2103. +------------+----------+
  2104. /// */
  2105. global '(testvariable);
  2106. describe 'testvariable;
  2107. LINELENGTH 60;
  2108. describe 'testvariable;
  2109. LINELENGTH 50;
  2110. describe 'testvariable;
  2111. LINELENGTH 40;
  2112. describe 'testvariable;
  2113. LINELENGTH 30;
  2114. describe 'testvariable;
  2115. LINELENGTH 20;
  2116. describe 'testvariable;
  2117. LINELENGTH 10;
  2118. describe 'testvariable;
  2119. % ##### Records Package #####
  2120. global '(rec1 rec2);
  2121. % Simple test.
  2122. record rtest1;
  2123. rec1 := rtest1();
  2124. if rec1 neq array 'rtest1 then
  2125. error(0, "Test 1 RECORD fails creation test!");
  2126. if null rtest1p rec1 then
  2127. error(0, "Test 1 RECORD fails predicate test!");
  2128. % A record with two fields.
  2129. record rtest2 with field1 := 0, field2 := 1;
  2130. % Test default creation.
  2131. rec2 := rtest2();
  2132. if rec2 neq array('rtest2, 0, 1) then
  2133. error(0, "Test 2 RECORD fails to create a record");
  2134. if null rtest2p rec2 then
  2135. error(0, "Test 2 RECORD fails predicate test");
  2136. if rtest2p rec1 then
  2137. error(0, "Test 2 RECORD fails to test record differences");
  2138. % Build a record with a predicate. Remove any old occurrence.
  2139. remd 'rtest3!?;
  2140. record rtest3 with field1 := 0, field2 := 1 has predicate = rtest3!?;
  2141. if not getd 'rtest3!? then
  2142. error(0, "Test 3 RECORD fails - no predicate built");
  2143. if rtest3!? rec2 then
  2144. error(0, "Test 3 RECORD fails - predicate returns T on non RTEST3 record");
  2145. for each x in {'identifier, 12, 12.3, "a string", cdr getd 'car,
  2146. '(a list), array("an", "array")}
  2147. when rtest3!? x
  2148. do error(0, {"Test 3 RECORD fails - predicate returns T on", x});
  2149. rec2 := rtest3();
  2150. if not rtest3!? rec2 then
  2151. error(0, "Test 3 RECORD fails - predicate returns NIL on record");
  2152. % Check that the no-predicate option works.
  2153. remd 'rtest4p; % Just to make sure.
  2154. record rtest4 with a := 34, b := 56 has no predicate;
  2155. if getd 'rtest4p then
  2156. error(0, "Test 4 RECORD fails - NO PREDICATE option generates a predicate");
  2157. % Verify that the CONSTRUCTOR option works.
  2158. remd 'rtest5;
  2159. remd 'make-rtest5;
  2160. record rtest5 with r5a := 0, r5b := 1 has constructor;
  2161. if getd 'rtest5 then
  2162. error(0, "Test 5 RECORD fails - CONSTRUCTOR generates simple constructor");
  2163. if not getd 'make-rtest5 then
  2164. error(0, "Test 5 RECORD fails - CONSTRUCTOR doesn't generate constructor");
  2165. if not rtest5p make-rtest5() then
  2166. error(0, "Test 5 RECORD fails - CONSTRUCTOR doesn't generate record");
  2167. % Verify that the named constructor works.
  2168. remd 'rtest6; remd 'please-make-rtest6;
  2169. record rtest6 with r6a := 0 has constructor = please!-make!-arecord;
  2170. if getd 'rtest6 then
  2171. error(0, "Test 6 RECORD fails - CONSTRUCTOR generates simple constructor");
  2172. if getd 'make-rtest6 then
  2173. error(0, "Test 6 RECORD fails - CONSTRUCTOR generates make- constructor");
  2174. if not getd 'please-make-arecord then
  2175. error(0, "Test 6 RECORD fails - CONSTRUCTOR doesn't generate constructor");
  2176. if not rtest6p please-make-arecord() then
  2177. error(0, "Test 6 RECORD fails - CONSTRUCTOR doesn't generate record");
  2178. end;